summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlessio Caiazza <acaiazza@gitlab.com>2018-12-13 12:08:53 +0100
committerShinya Maeda <shinya@gitlab.com>2018-12-31 12:05:56 +0900
commit1ea2d9faa518f6b769b05a71a1f93b4ea0226bc1 (patch)
tree291cd8d7a6c6113e75219dc873e24fb414df119d
parentc07bf1abf2a5c3751e575ee5926e9a052fa0b341 (diff)
downloadgitlab-ce-1ea2d9faa518f6b769b05a71a1f93b4ea0226bc1.tar.gz
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
-rw-r--r--app/controllers/projects/releases_controller.rb6
-rw-r--r--app/controllers/projects/tags/releases_controller.rb2
-rw-r--r--app/controllers/projects/tags_controller.rb8
-rw-r--r--app/finders/releases_finder.rb16
-rw-r--r--app/models/release.rb8
-rw-r--r--app/policies/project_policy.rb7
-rw-r--r--app/services/commits/tag_service.rb3
-rw-r--r--app/services/create_release_service.rb59
-rw-r--r--app/services/tags/create_service.rb7
-rw-r--r--app/services/update_release_service.rb22
-rw-r--r--app/views/layouts/nav/sidebar/_project.html.haml2
-rw-r--r--changelogs/unreleased/ac-releases-api.yml5
-rw-r--r--lib/api/api.rb1
-rw-r--r--lib/api/entities.rb13
-rw-r--r--lib/api/helpers.rb12
-rw-r--r--lib/api/releases.rb90
-rw-r--r--lib/api/tags.rb33
-rw-r--r--spec/factories/releases.rb1
-rw-r--r--spec/finders/releases_finder_spec.rb43
-rw-r--r--spec/models/release_spec.rb4
-rw-r--r--spec/policies/project_policy_spec.rb11
-rw-r--r--spec/services/create_release_service_spec.rb52
-rw-r--r--spec/services/update_release_service_spec.rb51
23 files changed, 370 insertions, 86 deletions
diff --git a/app/controllers/projects/releases_controller.rb b/app/controllers/projects/releases_controller.rb
index 58d5ea4762f..62bdc84b41a 100644
--- a/app/controllers/projects/releases_controller.rb
+++ b/app/controllers/projects/releases_controller.rb
@@ -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
diff --git a/app/controllers/projects/tags/releases_controller.rb b/app/controllers/projects/tags/releases_controller.rb
index 334e1847cc8..3725df010b0 100644
--- a/app/controllers/projects/tags/releases_controller.rb
+++ b/app/controllers/projects/tags/releases_controller.rb
@@ -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
diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb
index a50a1475eb2..35d798c0fda 100644
--- a/app/controllers/projects/tags_controller.rb
+++ b/app/controllers/projects/tags_controller.rb
@@ -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)
diff --git a/app/finders/releases_finder.rb b/app/finders/releases_finder.rb
new file mode 100644
index 00000000000..c185f77afa2
--- /dev/null
+++ b/app/finders/releases_finder.rb
@@ -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
diff --git a/app/models/release.rb b/app/models/release.rb
index 7a09ee459a6..a4272dd63aa 100644
--- a/app/models/release.rb
+++ b/app/models/release.rb
@@ -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
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index bcbd9676f2e..88cab816fe4 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -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
diff --git a/app/services/commits/tag_service.rb b/app/services/commits/tag_service.rb
index 7961ba4d3c4..bb8cfb63f98 100644
--- a/app/services/commits/tag_service.rb
+++ b/app/services/commits/tag_service.rb
@@ -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]
diff --git a/app/services/create_release_service.rb b/app/services/create_release_service.rb
index ab2dc5337aa..223670a935d 100644
--- a/app/services/create_release_service.rb
+++ b/app/services/create_release_service.rb
@@ -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)
-
- # Only create a release if the tag exists
- if existing_tag
- release = project.releases.find_by(tag: tag_name)
-
- 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
- )
-
- success(release)
- end
+ tag = repository.find_tag(tag_name)
+
+ if tag.blank? && ref.present?
+ result = create_tag(tag_name, ref)
+ return result unless result[:status] == :success
+
+ tag = result[:tag]
+ 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
diff --git a/app/services/tags/create_service.rb b/app/services/tags/create_service.rb
index 6bb9bb3988e..4de6b2d2774 100644
--- a/app/services/tags/create_service.rb
+++ b/app/services/tags/create_service.rb
@@ -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
diff --git a/app/services/update_release_service.rb b/app/services/update_release_service.rb
index e2228ca026c..b51e72c7bfc 100644
--- a/app/services/update_release_service.rb
+++ b/app/services/update_release_service.rb
@@ -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
diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml
index 59557c70904..d8017742c90 100644
--- a/app/views/layouts/nav/sidebar/_project.html.haml
+++ b/app/views/layouts/nav/sidebar/_project.html.haml
@@ -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')
diff --git a/changelogs/unreleased/ac-releases-api.yml b/changelogs/unreleased/ac-releases-api.yml
new file mode 100644
index 00000000000..87217cce371
--- /dev/null
+++ b/changelogs/unreleased/ac-releases-api.yml
@@ -0,0 +1,5 @@
+---
+title: Releases API
+merge_request: 23795
+author:
+type: added
diff --git a/lib/api/api.rb b/lib/api/api.rb
index f1448da7403..19da0b2c434 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -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
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 22403664c21..7116ab2882b 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -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
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index c3eca713712..742d3760533 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -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')
diff --git a/lib/api/releases.rb b/lib/api/releases.rb
new file mode 100644
index 00000000000..c5c2e3d9594
--- /dev/null
+++ b/lib/api/releases.rb
@@ -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
diff --git a/lib/api/tags.rb b/lib/api/tags.rb
index b18eec7d796..50ad184ba99 100644
--- a/lib/api/tags.rb
+++ b/lib/api/tags.rb
@@ -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
diff --git a/spec/factories/releases.rb b/spec/factories/releases.rb
index 18047c74a5d..eaf3999e9ff 100644
--- a/spec/factories/releases.rb
+++ b/spec/factories/releases.rb
@@ -4,5 +4,6 @@ FactoryBot.define do
name { tag }
description "Awesome release"
project
+ author
end
end
diff --git a/spec/finders/releases_finder_spec.rb b/spec/finders/releases_finder_spec.rb
new file mode 100644
index 00000000000..aa7163021cb
--- /dev/null
+++ b/spec/finders/releases_finder_spec.rb
@@ -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
diff --git a/spec/models/release_spec.rb b/spec/models/release_spec.rb
index 51725eeacac..92ba2d82f58 100644
--- a/spec/models/release_spec.rb
+++ b/spec/models/release_spec.rb
@@ -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 }
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index fa47b95899a..ca339dd8ef5 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -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)
diff --git a/spec/services/create_release_service_spec.rb b/spec/services/create_release_service_spec.rb
index 1a2dd0b39ee..d7ec6d4815a 100644
--- a/spec/services/create_release_service_spec.rb
+++ b/spec/services/create_release_service_spec.rb
@@ -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)
diff --git a/spec/services/update_release_service_spec.rb b/spec/services/update_release_service_spec.rb
index dc2d0e2d47a..a24dcabfc2e 100644
--- a/spec/services/update_release_service_spec.rb
+++ b/spec/services/update_release_service_spec.rb
@@ -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
- before do
- create_service.execute(tag_name, description)
+ 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(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 '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
- it 'raises an error if the tag does not exist' do
- result = service.execute("foobar", description)
- expect(result[:status]).to eq(:error)
+ context 'when the release does not exist' do
+ before do
+ project.releases.delete_all
+ end
+
+ it_behaves_like 'a failed update'
end
- it 'raises an error if the release does not exist' do
- result = service.execute(tag_name, description)
- expect(result[:status]).to eq(:error)
+ context 'with an invalid update' do
+ let(:new_description) { '' }
+
+ it_behaves_like 'a failed update'
end
end