Add releases API
This commit introduces Releases API under /api/v4/projects/:id/releases * We are introducing release policies at project level. * We are deprecating releases changes from tags, both api and web interface. * Tags::CreateService no longer create a release This feature is controlled by :releases_page feature flag
This commit is contained in:
parent
c07bf1abf2
commit
1ea2d9faa5
|
@ -3,7 +3,7 @@
|
|||
class Projects::ReleasesController < Projects::ApplicationController
|
||||
# Authorize
|
||||
before_action :require_non_empty_project
|
||||
before_action :authorize_download_code!
|
||||
before_action :authorize_read_release!
|
||||
before_action :check_releases_page_feature_flag
|
||||
|
||||
def index
|
||||
|
@ -12,8 +12,8 @@ class Projects::ReleasesController < Projects::ApplicationController
|
|||
private
|
||||
|
||||
def check_releases_page_feature_flag
|
||||
return render_404 unless Feature.enabled?(:releases_page)
|
||||
return render_404 unless Feature.enabled?(:releases_page, @project)
|
||||
|
||||
push_frontend_feature_flag(:releases_page)
|
||||
push_frontend_feature_flag(:releases_page, @project)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,7 +4,7 @@ class Projects::Tags::ReleasesController < Projects::ApplicationController
|
|||
# Authorize
|
||||
before_action :require_non_empty_project
|
||||
before_action :authorize_download_code!
|
||||
before_action :authorize_push_code!
|
||||
before_action :authorize_update_release!
|
||||
before_action :tag
|
||||
before_action :release
|
||||
|
||||
|
|
|
@ -43,9 +43,15 @@ class Projects::TagsController < Projects::ApplicationController
|
|||
|
||||
def create
|
||||
result = ::Tags::CreateService.new(@project, current_user)
|
||||
.execute(params[:tag_name], params[:ref], params[:message], params[:release_description])
|
||||
.execute(params[:tag_name], params[:ref], params[:message])
|
||||
|
||||
if result[:status] == :success
|
||||
# Release creation with Tags was deprecated in GitLab 11.7
|
||||
if params[:release_description].present?
|
||||
CreateReleaseService.new(@project, current_user)
|
||||
.execute(params[:tag_name], params[:release_description])
|
||||
end
|
||||
|
||||
@tag = result[:tag]
|
||||
|
||||
redirect_to project_tag_path(@project, @tag.name)
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ReleasesFinder
|
||||
def initialize(project, current_user = nil)
|
||||
@project = project
|
||||
@current_user = current_user
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def execute
|
||||
return [] unless Ability.allowed?(@current_user, :read_release, @project)
|
||||
|
||||
@project.releases.order('created_at DESC')
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
|
@ -6,7 +6,15 @@ class Release < ActiveRecord::Base
|
|||
cache_markdown_field :description
|
||||
|
||||
belongs_to :project
|
||||
# releases prior to 11.7 have no author
|
||||
belongs_to :author, class_name: 'User'
|
||||
|
||||
validates :description, :project, :tag, presence: true
|
||||
|
||||
delegate :repository, to: :project
|
||||
|
||||
def commit
|
||||
git_tag = repository.find_tag(tag)
|
||||
repository.commit(git_tag.dereferenced_target)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -23,6 +23,7 @@ class ProjectPolicy < BasePolicy
|
|||
container_image
|
||||
pages
|
||||
cluster
|
||||
release
|
||||
].freeze
|
||||
|
||||
desc "User is a project owner"
|
||||
|
@ -173,6 +174,7 @@ class ProjectPolicy < BasePolicy
|
|||
enable :read_cycle_analytics
|
||||
enable :award_emoji
|
||||
enable :read_pages_content
|
||||
enable :read_release
|
||||
end
|
||||
|
||||
# These abilities are not allowed to admins that are not members of the project,
|
||||
|
@ -239,6 +241,8 @@ class ProjectPolicy < BasePolicy
|
|||
enable :update_container_image
|
||||
enable :create_environment
|
||||
enable :create_deployment
|
||||
enable :create_release
|
||||
enable :update_release
|
||||
end
|
||||
|
||||
rule { can?(:maintainer_access) }.policy do
|
||||
|
@ -266,6 +270,7 @@ class ProjectPolicy < BasePolicy
|
|||
enable :update_cluster
|
||||
enable :admin_cluster
|
||||
enable :create_environment_terminal
|
||||
enable :admin_release
|
||||
end
|
||||
|
||||
rule { (mirror_available & can?(:admin_project)) | admin }.enable :admin_remote_mirror
|
||||
|
@ -325,6 +330,7 @@ class ProjectPolicy < BasePolicy
|
|||
prevent :download_code
|
||||
prevent :fork_project
|
||||
prevent :read_commit_status
|
||||
prevent(*create_read_update_admin_destroy(:release))
|
||||
end
|
||||
|
||||
rule { container_registry_disabled }.policy do
|
||||
|
@ -354,6 +360,7 @@ class ProjectPolicy < BasePolicy
|
|||
enable :read_commit_status
|
||||
enable :read_container_image
|
||||
enable :download_code
|
||||
enable :read_release
|
||||
enable :download_wiki_code
|
||||
enable :read_cycle_analytics
|
||||
enable :read_pages_content
|
||||
|
|
|
@ -9,11 +9,10 @@ module Commits
|
|||
|
||||
tag_name = params[:tag_name]
|
||||
message = params[:tag_message]
|
||||
release_description = nil
|
||||
|
||||
result = Tags::CreateService
|
||||
.new(commit.project, current_user)
|
||||
.execute(tag_name, commit.sha, message, release_description)
|
||||
.execute(tag_name, commit.sha, message)
|
||||
|
||||
if result[:status] == :success
|
||||
tag = result[:tag]
|
||||
|
|
|
@ -1,35 +1,50 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CreateReleaseService < BaseService
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def execute(tag_name, release_description)
|
||||
def execute(tag_name, release_description, name: nil, ref: nil)
|
||||
repository = project.repository
|
||||
existing_tag = repository.find_tag(tag_name)
|
||||
tag = repository.find_tag(tag_name)
|
||||
|
||||
# Only create a release if the tag exists
|
||||
if existing_tag
|
||||
release = project.releases.find_by(tag: tag_name)
|
||||
if tag.blank? && ref.present?
|
||||
result = create_tag(tag_name, ref)
|
||||
return result unless result[:status] == :success
|
||||
|
||||
if release
|
||||
error('Release already exists', 409)
|
||||
else
|
||||
release = project.releases.create!(
|
||||
tag: tag_name,
|
||||
name: tag_name,
|
||||
sha: existing_tag.dereferenced_target.sha,
|
||||
author: current_user,
|
||||
description: release_description
|
||||
)
|
||||
tag = result[:tag]
|
||||
end
|
||||
|
||||
success(release)
|
||||
end
|
||||
if tag.present?
|
||||
create_release(tag, name, release_description)
|
||||
else
|
||||
error('Tag does not exist', 404)
|
||||
end
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
def success(release)
|
||||
super().merge(release: release)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_release(tag, name, description)
|
||||
release = project.releases.find_by(tag: tag.name) # rubocop: disable CodeReuse/ActiveRecord
|
||||
|
||||
if release
|
||||
error('Release already exists', 409)
|
||||
else
|
||||
release = project.releases.create!(
|
||||
tag: tag.name,
|
||||
name: name || tag.name,
|
||||
sha: tag.dereferenced_target.sha,
|
||||
author: current_user,
|
||||
description: description
|
||||
)
|
||||
|
||||
success(release)
|
||||
end
|
||||
end
|
||||
|
||||
def create_tag(tag_name, ref)
|
||||
Tags::CreateService.new(project, current_user)
|
||||
.execute(tag_name, ref, nil)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module Tags
|
||||
class CreateService < BaseService
|
||||
def execute(tag_name, target, message, release_description = nil)
|
||||
def execute(tag_name, target, message)
|
||||
valid_tag = Gitlab::GitRefValidator.validate(tag_name)
|
||||
return error('Tag name invalid') unless valid_tag
|
||||
|
||||
|
@ -20,10 +20,7 @@ module Tags
|
|||
end
|
||||
|
||||
if new_tag
|
||||
if release_description.present?
|
||||
CreateReleaseService.new(@project, @current_user)
|
||||
.execute(tag_name, release_description)
|
||||
end
|
||||
repository.expire_tags_cache
|
||||
|
||||
success.merge(tag: new_tag)
|
||||
else
|
||||
|
|
|
@ -1,18 +1,28 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class UpdateReleaseService < BaseService
|
||||
attr_accessor :tag_name
|
||||
|
||||
def initialize(project, user, tag_name, params)
|
||||
super(project, user, params)
|
||||
|
||||
@tag_name = tag_name
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def execute(tag_name, release_description)
|
||||
def execute
|
||||
repository = project.repository
|
||||
existing_tag = repository.find_tag(tag_name)
|
||||
existing_tag = repository.find_tag(@tag_name)
|
||||
|
||||
if existing_tag
|
||||
release = project.releases.find_by(tag: tag_name)
|
||||
release = project.releases.find_by(tag: @tag_name)
|
||||
|
||||
if release
|
||||
release.update(description: release_description)
|
||||
|
||||
success(release)
|
||||
if release.update(params)
|
||||
success(release)
|
||||
else
|
||||
error(release.errors.messages || '400 Bad request', 400)
|
||||
end
|
||||
else
|
||||
error('Release does not exist', 404)
|
||||
end
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
= link_to activity_project_path(@project), title: _('Activity'), class: 'shortcuts-project-activity' do
|
||||
%span= _('Activity')
|
||||
|
||||
- if project_nav_tab?(:releases) && Feature.enabled?(:releases_page)
|
||||
- if project_nav_tab?(:releases) && Feature.enabled?(:releases_page, @project)
|
||||
= nav_link(controller: :releases) do
|
||||
= link_to project_releases_path(@project), title: _('Releases'), class: 'shortcuts-project-releases' do
|
||||
%span= _('Releases')
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Releases API
|
||||
merge_request: 23795
|
||||
author:
|
||||
type: added
|
|
@ -139,6 +139,7 @@ module API
|
|||
mount ::API::ProjectTemplates
|
||||
mount ::API::ProtectedBranches
|
||||
mount ::API::ProtectedTags
|
||||
mount ::API::Releases
|
||||
mount ::API::Repositories
|
||||
mount ::API::Runner
|
||||
mount ::API::Runners
|
||||
|
|
|
@ -1087,11 +1087,20 @@ module API
|
|||
expose :password_authentication_enabled_for_web, as: :signin_enabled
|
||||
end
|
||||
|
||||
class Release < Grape::Entity
|
||||
# deprecated old Release representation
|
||||
class TagRelease < Grape::Entity
|
||||
expose :tag, as: :tag_name
|
||||
expose :description
|
||||
end
|
||||
|
||||
class Release < TagRelease
|
||||
expose :name
|
||||
expose :description_html
|
||||
expose :created_at
|
||||
expose :author, using: Entities::UserBasic, if: -> (release, _) { release.author.present? }
|
||||
expose :commit, using: Entities::Commit
|
||||
end
|
||||
|
||||
class Tag < Grape::Entity
|
||||
expose :name, :message, :target
|
||||
|
||||
|
@ -1100,7 +1109,7 @@ module API
|
|||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
expose :release, using: Entities::Release do |repo_tag, options|
|
||||
expose :release, using: Entities::TagRelease do |repo_tag, options|
|
||||
options[:project].releases.find_by(tag: repo_tag.name)
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
|
|
@ -255,6 +255,18 @@ module API
|
|||
authorize! :update_build, user_project
|
||||
end
|
||||
|
||||
def authorize_create_release!
|
||||
authorize! :create_release, user_project
|
||||
end
|
||||
|
||||
def authorize_read_release!
|
||||
authorize! :read_release, user_project
|
||||
end
|
||||
|
||||
def authorize_update_release!
|
||||
authorize! :update_release, user_project
|
||||
end
|
||||
|
||||
def require_gitlab_workhorse!
|
||||
unless env['HTTP_GITLAB_WORKHORSE'].present?
|
||||
forbidden!('Request should be executed via GitLab Workhorse')
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
class Releases < Grape::API
|
||||
include PaginationParams
|
||||
|
||||
RELEASE_ENDPOINT_REQUIREMETS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(tag_name: API::NO_SLASH_URL_PART_REGEX)
|
||||
|
||||
before { error!('404 Not Found', 404) unless Feature.enabled?(:releases_page, user_project) }
|
||||
before { authorize_read_release! }
|
||||
|
||||
params do
|
||||
requires :id, type: String, desc: 'The ID of a project'
|
||||
end
|
||||
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
|
||||
desc 'Get a project releases' do
|
||||
detail 'This feature was introduced in GitLab 11.7.'
|
||||
success Entities::Release
|
||||
end
|
||||
params do
|
||||
use :pagination
|
||||
end
|
||||
get ':id/releases' do
|
||||
releases = ::Kaminari.paginate_array(::ReleasesFinder.new(user_project, current_user).execute)
|
||||
|
||||
present paginate(releases), with: Entities::Release
|
||||
end
|
||||
|
||||
desc 'Get a single project release' do
|
||||
detail 'This feature was introduced in GitLab 11.7.'
|
||||
success Entities::Release
|
||||
end
|
||||
params do
|
||||
requires :tag_name, type: String, desc: 'The name of the tag'
|
||||
end
|
||||
get ':id/releases/:tag_name', requirements: RELEASE_ENDPOINT_REQUIREMETS do
|
||||
release = user_project.releases.find_by_tag(params[:tag_name])
|
||||
not_found!('Release') unless release
|
||||
|
||||
present release, with: Entities::Release
|
||||
end
|
||||
|
||||
desc 'Create a new release' do
|
||||
detail 'This feature was introduced in GitLab 11.7.'
|
||||
success Entities::Release
|
||||
end
|
||||
params do
|
||||
requires :name, type: String, desc: 'The name of the release'
|
||||
requires :tag_name, type: String, desc: 'The name of the tag'
|
||||
requires :description, type: String, desc: 'The release notes'
|
||||
optional :ref, type: String, desc: 'The commit sha or branch name'
|
||||
end
|
||||
post ':id/releases' do
|
||||
authorize_create_release!
|
||||
|
||||
result = ::CreateReleaseService.new(user_project, current_user)
|
||||
.execute(params[:tag_name], params[:description], params[:name], params[:ref])
|
||||
|
||||
if result[:status] == :success
|
||||
present result[:release], with: Entities::Release
|
||||
else
|
||||
render_api_error!(result[:message], 400)
|
||||
end
|
||||
end
|
||||
|
||||
desc 'Update a release' do
|
||||
detail 'This feature was introduced in GitLab 11.7.'
|
||||
success Entities::Release
|
||||
end
|
||||
params do
|
||||
requires :tag_name, type: String, desc: 'The name of the tag'
|
||||
requires :name, type: String, desc: 'The name of the release'
|
||||
requires :description, type: String, desc: 'Release notes with markdown support'
|
||||
end
|
||||
put ':id/releases/:tag_name', requirements: RELEASE_ENDPOINT_REQUIREMETS do
|
||||
authorize_update_release!
|
||||
|
||||
attributes = declared(params)
|
||||
tag = attributes.delete(:tag_name)
|
||||
result = UpdateReleaseService.new(user_project, current_user, tag, attributes).execute
|
||||
|
||||
if result[:status] == :success
|
||||
present result[:release], with: Entities::Release
|
||||
else
|
||||
render_api_error!(result[:message], result[:http_status])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -42,21 +42,28 @@ module API
|
|||
end
|
||||
|
||||
desc 'Create a new repository tag' do
|
||||
detail 'This optional release_description parameter was deprecated in GitLab 11.7.'
|
||||
success Entities::Tag
|
||||
end
|
||||
params do
|
||||
requires :tag_name, type: String, desc: 'The name of the tag'
|
||||
requires :ref, type: String, desc: 'The commit sha or branch name'
|
||||
optional :message, type: String, desc: 'Specifying a message creates an annotated tag'
|
||||
optional :release_description, type: String, desc: 'Specifying release notes stored in the GitLab database'
|
||||
optional :release_description, type: String, desc: 'Specifying release notes stored in the GitLab database (deprecated in GitLab 11.7)'
|
||||
end
|
||||
post ':id/repository/tags' do
|
||||
authorize_push_project
|
||||
|
||||
result = ::Tags::CreateService.new(user_project, current_user)
|
||||
.execute(params[:tag_name], params[:ref], params[:message], params[:release_description])
|
||||
.execute(params[:tag_name], params[:ref], params[:message])
|
||||
|
||||
if result[:status] == :success
|
||||
# Release creation with Tags API was deprecated in GitLab 11.7
|
||||
if params[:release_description].present?
|
||||
CreateReleaseService.new(user_project, current_user)
|
||||
.execute(params[:tag_name], params[:release_description])
|
||||
end
|
||||
|
||||
present result[:tag],
|
||||
with: Entities::Tag,
|
||||
project: user_project
|
||||
|
@ -88,40 +95,46 @@ module API
|
|||
end
|
||||
|
||||
desc 'Add a release note to a tag' do
|
||||
success Entities::Release
|
||||
detail 'This feature was deprecated in GitLab 11.7.'
|
||||
success Entities::TagRelease
|
||||
end
|
||||
params do
|
||||
requires :tag_name, type: String, desc: 'The name of the tag'
|
||||
requires :description, type: String, desc: 'Release notes with markdown support'
|
||||
end
|
||||
post ':id/repository/tags/:tag_name/release', requirements: TAG_ENDPOINT_REQUIREMENTS do
|
||||
authorize_push_project
|
||||
authorize_create_release!
|
||||
|
||||
result = CreateReleaseService.new(user_project, current_user)
|
||||
.execute(params[:tag_name], params[:description])
|
||||
|
||||
if result[:status] == :success
|
||||
present result[:release], with: Entities::Release
|
||||
present result[:release], with: Entities::TagRelease
|
||||
else
|
||||
render_api_error!(result[:message], result[:http_status])
|
||||
end
|
||||
end
|
||||
|
||||
desc "Update a tag's release note" do
|
||||
success Entities::Release
|
||||
detail 'This feature was deprecated in GitLab 11.7.'
|
||||
success Entities::TagRelease
|
||||
end
|
||||
params do
|
||||
requires :tag_name, type: String, desc: 'The name of the tag'
|
||||
requires :description, type: String, desc: 'Release notes with markdown support'
|
||||
end
|
||||
put ':id/repository/tags/:tag_name/release', requirements: TAG_ENDPOINT_REQUIREMENTS do
|
||||
authorize_push_project
|
||||
authorize_update_release!
|
||||
|
||||
result = UpdateReleaseService.new(user_project, current_user)
|
||||
.execute(params[:tag_name], params[:description])
|
||||
result = UpdateReleaseService.new(
|
||||
user_project,
|
||||
current_user,
|
||||
params[:tag_name],
|
||||
description: params[:description]
|
||||
).execute
|
||||
|
||||
if result[:status] == :success
|
||||
present result[:release], with: Entities::Release
|
||||
present result[:release], with: Entities::TagRelease
|
||||
else
|
||||
render_api_error!(result[:message], result[:http_status])
|
||||
end
|
||||
|
|
|
@ -4,5 +4,6 @@ FactoryBot.define do
|
|||
name { tag }
|
||||
description "Awesome release"
|
||||
project
|
||||
author
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe ReleasesFinder do
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:repository) { project.repository }
|
||||
let(:v1_0_0) { create(:release, project: project, tag: 'v1.0.0')}
|
||||
let(:v1_1_0) { create(:release, project: project, tag: 'v1.1.0')}
|
||||
|
||||
subject { described_class.new(project, user)}
|
||||
|
||||
before do
|
||||
now = Time.now
|
||||
v1_0_0.update_attribute(:created_at, now - 2.days)
|
||||
v1_1_0.update_attribute(:created_at, now - 1.day)
|
||||
end
|
||||
|
||||
describe '#execute' do
|
||||
context 'when the user is not part of the project' do
|
||||
it 'returns no releases' do
|
||||
releases = subject.execute
|
||||
|
||||
expect(releases).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the user is a project developer' do
|
||||
before do
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
it 'sorts by creation date' do
|
||||
releases = subject.execute
|
||||
|
||||
expect(releases).to be_present
|
||||
expect(releases.size).to eq(2)
|
||||
expect(releases).to eq([v1_1_0, v1_0_0])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,7 +1,9 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Release do
|
||||
let(:release) { create(:release) }
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project, :public, :repository) }
|
||||
let(:release) { create(:release, project: project, author: user) }
|
||||
|
||||
it { expect(release).to be_valid }
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ describe ProjectPolicy do
|
|||
read_project_for_iids read_issue_iid read_merge_request_iid read_label
|
||||
read_milestone read_project_snippet read_project_member read_note
|
||||
create_project create_issue create_note upload_file create_merge_request_in
|
||||
award_emoji
|
||||
award_emoji read_release
|
||||
]
|
||||
end
|
||||
|
||||
|
@ -38,7 +38,7 @@ describe ProjectPolicy do
|
|||
update_commit_status create_build update_build create_pipeline
|
||||
update_pipeline create_merge_request_from create_wiki push_code
|
||||
resolve_note create_container_image update_container_image
|
||||
create_environment create_deployment
|
||||
create_environment create_deployment create_release update_release
|
||||
]
|
||||
end
|
||||
|
||||
|
@ -48,7 +48,7 @@ describe ProjectPolicy do
|
|||
update_deployment admin_project_snippet
|
||||
admin_project_member admin_note admin_wiki admin_project
|
||||
admin_commit_status admin_build admin_container_image
|
||||
admin_pipeline admin_environment admin_deployment add_cluster
|
||||
admin_pipeline admin_environment admin_deployment admin_release add_cluster
|
||||
]
|
||||
end
|
||||
|
||||
|
@ -56,7 +56,7 @@ describe ProjectPolicy do
|
|||
%i[
|
||||
download_code fork_project read_commit_status read_pipeline
|
||||
read_container_image build_download_code build_read_container_image
|
||||
download_wiki_code
|
||||
download_wiki_code read_release
|
||||
]
|
||||
end
|
||||
|
||||
|
@ -183,7 +183,8 @@ describe ProjectPolicy do
|
|||
:create_pipeline_schedule, :read_pipeline_schedule, :update_pipeline_schedule, :admin_pipeline_schedule, :destroy_pipeline_schedule,
|
||||
:create_environment, :read_environment, :update_environment, :admin_environment, :destroy_environment,
|
||||
:create_cluster, :read_cluster, :update_cluster, :admin_cluster,
|
||||
:create_deployment, :read_deployment, :update_deployment, :admin_deployment, :destroy_deployment
|
||||
:create_deployment, :read_deployment, :update_deployment, :admin_deployment, :destroy_deployment,
|
||||
:admin_release
|
||||
]
|
||||
|
||||
expect_disallowed(*repository_permissions)
|
||||
|
|
|
@ -4,27 +4,57 @@ describe CreateReleaseService do
|
|||
let(:project) { create(:project, :repository) }
|
||||
let(:user) { create(:user) }
|
||||
let(:tag_name) { project.repository.tag_names.first }
|
||||
let(:name) { 'Bionic Beaver'}
|
||||
let(:description) { 'Awesome release!' }
|
||||
let(:service) { described_class.new(project, user) }
|
||||
let(:tag) { project.repository.find_tag(tag_name) }
|
||||
let(:sha) { tag.dereferenced_target.sha }
|
||||
let(:ref) { nil }
|
||||
|
||||
it 'creates a new release' do
|
||||
result = service.execute(tag_name, description)
|
||||
expect(result[:status]).to eq(:success)
|
||||
release = project.releases.find_by(tag: tag_name)
|
||||
expect(release).not_to be_nil
|
||||
expect(release.description).to eq(description)
|
||||
expect(release.name).to eq(tag_name)
|
||||
expect(release.sha).to eq(sha)
|
||||
expect(release.author).to eq(user)
|
||||
shared_examples 'a successful release creation' do
|
||||
it 'creates a new release' do
|
||||
result = service.execute(tag_name, description, name: name, ref: ref)
|
||||
expect(result[:status]).to eq(:success)
|
||||
release = project.releases.find_by(tag: tag_name)
|
||||
expect(release).not_to be_nil
|
||||
expect(release.description).to eq(description)
|
||||
expect(release.name).to eq(name)
|
||||
expect(release.author).to eq(user)
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'a successful release creation'
|
||||
|
||||
it 'raises an error if the tag does not exist' do
|
||||
result = service.execute("foobar", description)
|
||||
expect(result[:status]).to eq(:error)
|
||||
end
|
||||
|
||||
it 'keeps track of the commit sha' do
|
||||
tag = project.repository.find_tag(tag_name)
|
||||
sha = tag.dereferenced_target.sha
|
||||
result = service.execute(tag_name, description, name: name)
|
||||
|
||||
expect(result[:status]).to eq(:success)
|
||||
expect(project.releases.find_by(tag: tag_name).sha).to eq(sha)
|
||||
end
|
||||
|
||||
context 'when ref is provided' do
|
||||
let(:ref) { 'master' }
|
||||
let(:tag_name) { 'foobar' }
|
||||
|
||||
it_behaves_like 'a successful release creation'
|
||||
|
||||
it 'creates a tag if the tag does not exist' do
|
||||
expect(project.repository.ref_exists?("refs/tags/#{tag_name}")).to be_falsey
|
||||
|
||||
result = service.execute(tag_name, description, name: name, ref: ref)
|
||||
expect(result[:status]).to eq(:success)
|
||||
expect(project.repository.ref_exists?("refs/tags/#{tag_name}")).to be_truthy
|
||||
|
||||
release = project.releases.find_by(tag: tag_name)
|
||||
expect(release).not_to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'there already exists a release on a tag' do
|
||||
before do
|
||||
service.execute(tag_name, description)
|
||||
|
|
|
@ -5,30 +5,49 @@ describe UpdateReleaseService do
|
|||
let(:user) { create(:user) }
|
||||
let(:tag_name) { project.repository.tag_names.first }
|
||||
let(:description) { 'Awesome release!' }
|
||||
let(:new_name) { 'A new name' }
|
||||
let(:new_description) { 'The best release!' }
|
||||
let(:service) { described_class.new(project, user) }
|
||||
let(:params) { { name: new_name, description: new_description } }
|
||||
let(:service) { described_class.new(project, user, tag_name, params) }
|
||||
let(:create_service) { CreateReleaseService.new(project, user) }
|
||||
|
||||
context 'with an existing release' do
|
||||
let(:create_service) { CreateReleaseService.new(project, user) }
|
||||
before do
|
||||
create_service.execute(tag_name, description)
|
||||
end
|
||||
|
||||
shared_examples 'a failed update' do
|
||||
it 'raises an error' do
|
||||
result = service.execute
|
||||
expect(result[:status]).to eq(:error)
|
||||
end
|
||||
end
|
||||
|
||||
it 'successfully updates an existing release' do
|
||||
result = service.execute
|
||||
expect(result[:status]).to eq(:success)
|
||||
|
||||
release = project.releases.find_by(tag: tag_name)
|
||||
expect(release.name).to eq(new_name)
|
||||
expect(release.description).to eq(new_description)
|
||||
end
|
||||
|
||||
context 'when the tag does not exists' do
|
||||
let(:tag_name) { 'foobar' }
|
||||
|
||||
it_behaves_like 'a failed update'
|
||||
end
|
||||
|
||||
context 'when the release does not exist' do
|
||||
before do
|
||||
create_service.execute(tag_name, description)
|
||||
project.releases.delete_all
|
||||
end
|
||||
|
||||
it 'successfully updates an existing release' do
|
||||
result = service.execute(tag_name, new_description)
|
||||
expect(result[:status]).to eq(:success)
|
||||
expect(project.releases.find_by(tag: tag_name).description).to eq(new_description)
|
||||
end
|
||||
it_behaves_like 'a failed update'
|
||||
end
|
||||
|
||||
it 'raises an error if the tag does not exist' do
|
||||
result = service.execute("foobar", description)
|
||||
expect(result[:status]).to eq(:error)
|
||||
end
|
||||
context 'with an invalid update' do
|
||||
let(:new_description) { '' }
|
||||
|
||||
it 'raises an error if the release does not exist' do
|
||||
result = service.execute(tag_name, description)
|
||||
expect(result[:status]).to eq(:error)
|
||||
it_behaves_like 'a failed update'
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue