Merge branch 'api-protected-tags' into 'master'
API: Protected tags Closes #39144 See merge request gitlab-org/gitlab-ce!14986
This commit is contained in:
commit
0fc5bc3145
12 changed files with 531 additions and 4 deletions
|
@ -4,6 +4,8 @@ class ProtectedTag < ActiveRecord::Base
|
|||
include Gitlab::ShellAdapter
|
||||
include ProtectedRef
|
||||
|
||||
validates :name, uniqueness: { scope: :project_id }
|
||||
|
||||
protected_ref_access_levels :create
|
||||
|
||||
def self.protected?(project, ref_name)
|
||||
|
|
5
changelogs/unreleased/api-protected-tags.yml
Normal file
5
changelogs/unreleased/api-protected-tags.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: 'API: Protected tags'
|
||||
merge_request: 14986
|
||||
author: Robert Schilling
|
||||
type: added
|
45
db/migrate/20180711103851_drop_duplicate_protected_tags.rb
Normal file
45
db/migrate/20180711103851_drop_duplicate_protected_tags.rb
Normal file
|
@ -0,0 +1,45 @@
|
|||
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
|
||||
# for more information on how to write migrations for GitLab.
|
||||
|
||||
class DropDuplicateProtectedTags < ActiveRecord::Migration
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
BATCH_SIZE = 1000
|
||||
|
||||
class Project < ActiveRecord::Base
|
||||
self.table_name = 'projects'
|
||||
|
||||
include ::EachBatch
|
||||
end
|
||||
|
||||
class ProtectedTag < ActiveRecord::Base
|
||||
self.table_name = 'protected_tags'
|
||||
end
|
||||
|
||||
def up
|
||||
Project.each_batch(of: BATCH_SIZE) do |projects|
|
||||
ids = ProtectedTag
|
||||
.where(project_id: projects)
|
||||
.group(:name, :project_id)
|
||||
.select('max(id)')
|
||||
|
||||
tags = ProtectedTag
|
||||
.where(project_id: projects)
|
||||
.where.not(id: ids)
|
||||
|
||||
if Gitlab::Database.postgresql?
|
||||
tags.delete_all
|
||||
else
|
||||
# Workaround needed for MySQL
|
||||
sql = "SELECT id FROM (#{tags.to_sql}) protected_tags"
|
||||
|
||||
ProtectedTag.where("id IN (#{sql})").delete_all # rubocop:disable GitlabSecurity/SqlInjection
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
end
|
||||
end
|
18
db/migrate/20180711103922_add_protected_tags_index.rb
Normal file
18
db/migrate/20180711103922_add_protected_tags_index.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
|
||||
# for more information on how to write migrations for GitLab.
|
||||
|
||||
class AddProtectedTagsIndex < ActiveRecord::Migration
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_concurrent_index :protected_tags, [:project_id, :name], unique: true
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index :protected_tags, [:project_id, :name]
|
||||
end
|
||||
end
|
|
@ -1741,6 +1741,7 @@ ActiveRecord::Schema.define(version: 20180816193530) do
|
|||
t.datetime "updated_at", null: false
|
||||
end
|
||||
|
||||
add_index "protected_tags", ["project_id", "name"], name: "index_protected_tags_on_project_id_and_name", unique: true, using: :btree
|
||||
add_index "protected_tags", ["project_id"], name: "index_protected_tags_on_project_id", using: :btree
|
||||
|
||||
create_table "push_event_payloads", id: false, force: :cascade do |t|
|
||||
|
|
|
@ -53,6 +53,7 @@ following locations:
|
|||
- [Project Members](members.md)
|
||||
- [Project Snippets](project_snippets.md)
|
||||
- [Protected Branches](protected_branches.md)
|
||||
- [Protected Tags](protected_tags.md)
|
||||
- [Repositories](repositories.md)
|
||||
- [Repository Files](repository_files.md)
|
||||
- [Runners](runners.md)
|
||||
|
|
128
doc/api/protected_tags.md
Normal file
128
doc/api/protected_tags.md
Normal file
|
@ -0,0 +1,128 @@
|
|||
# Protected tags API
|
||||
|
||||
>**Note:** This feature was introduced in GitLab 11.3
|
||||
|
||||
**Valid access levels**
|
||||
|
||||
Currently, these levels are recognized:
|
||||
```
|
||||
0 => No access
|
||||
30 => Developer access
|
||||
40 => Maintainer access
|
||||
```
|
||||
|
||||
## List protected tags
|
||||
|
||||
Gets a list of protected tags from a project.
|
||||
This function takes pagination parameters `page` and `per_page` to restrict the list of protected tags.
|
||||
|
||||
```
|
||||
GET /projects/:id/protected_tags
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||
|
||||
```bash
|
||||
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v4/projects/5/protected_tags'
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"name": "release-1-0",
|
||||
"create_access_levels": [
|
||||
{
|
||||
"access_level": 40,
|
||||
"access_level_description": "Maintainers"
|
||||
}
|
||||
]
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
## Get a single protected tag or wildcard protected tag
|
||||
|
||||
Gets a single protected tag or wildcard protected tag.
|
||||
The pagination parameters `page` and `per_page` can be used to restrict the list of protected tags.
|
||||
|
||||
```
|
||||
GET /projects/:id/protected_tags/:name
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||
| `name` | string | yes | The name of the tag or wildcard |
|
||||
|
||||
```bash
|
||||
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v4/projects/5/protected_tags/release-1-0'
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "release-1-0",
|
||||
"create_access_levels": [
|
||||
{
|
||||
"access_level": 40,
|
||||
"access_level_description": "Maintainers"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Protect repository tags
|
||||
|
||||
Protects a single repository tag or several project repository
|
||||
tags using a wildcard protected tag.
|
||||
|
||||
```
|
||||
POST /projects/:id/protected_tags
|
||||
```
|
||||
|
||||
```bash
|
||||
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v4/projects/5/protected_tags?name=*-stable&create_access_level=30'
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||
| `name` | string | yes | The name of the tag or wildcard |
|
||||
| `create_access_level` | string | no | Access levels allowed to create (defaults: `40`, maintainer access level) |
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "*-stable",
|
||||
"create_access_levels": [
|
||||
{
|
||||
"access_level": 30,
|
||||
"access_level_description": "Developers + Maintainers"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Unprotect repository tags
|
||||
|
||||
Unprotects the given protected tag or wildcard protected tag.
|
||||
|
||||
```
|
||||
DELETE /projects/:id/protected_tags/:name
|
||||
```
|
||||
|
||||
```bash
|
||||
curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v4/projects/5/protected_tags/*-stable'
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||
| `name` | string | yes | The name of the tag |
|
|
@ -99,12 +99,13 @@ module API
|
|||
mount ::API::Features
|
||||
mount ::API::Files
|
||||
mount ::API::GroupBoards
|
||||
mount ::API::Groups
|
||||
mount ::API::GroupMilestones
|
||||
mount ::API::Groups
|
||||
mount ::API::GroupVariables
|
||||
mount ::API::Internal
|
||||
mount ::API::Issues
|
||||
mount ::API::Jobs
|
||||
mount ::API::JobArtifacts
|
||||
mount ::API::Jobs
|
||||
mount ::API::Keys
|
||||
mount ::API::Labels
|
||||
mount ::API::Lint
|
||||
|
@ -122,11 +123,12 @@ module API
|
|||
mount ::API::ProjectExport
|
||||
mount ::API::ProjectImport
|
||||
mount ::API::ProjectHooks
|
||||
mount ::API::Projects
|
||||
mount ::API::ProjectMilestones
|
||||
mount ::API::Projects
|
||||
mount ::API::ProjectSnapshots
|
||||
mount ::API::ProjectSnippets
|
||||
mount ::API::ProtectedBranches
|
||||
mount ::API::ProtectedTags
|
||||
mount ::API::Repositories
|
||||
mount ::API::Runner
|
||||
mount ::API::Runners
|
||||
|
@ -143,7 +145,6 @@ module API
|
|||
mount ::API::Triggers
|
||||
mount ::API::Users
|
||||
mount ::API::Variables
|
||||
mount ::API::GroupVariables
|
||||
mount ::API::Version
|
||||
mount ::API::Wikis
|
||||
|
||||
|
|
|
@ -429,6 +429,11 @@ module API
|
|||
expose :merge_access_levels, using: Entities::ProtectedRefAccess
|
||||
end
|
||||
|
||||
class ProtectedTag < Grape::Entity
|
||||
expose :name
|
||||
expose :create_access_levels, using: Entities::ProtectedRefAccess
|
||||
end
|
||||
|
||||
class Milestone < Grape::Entity
|
||||
expose :id, :iid
|
||||
expose :project_id, if: -> (entity, options) { entity&.project_id }
|
||||
|
|
79
lib/api/protected_tags.rb
Normal file
79
lib/api/protected_tags.rb
Normal file
|
@ -0,0 +1,79 @@
|
|||
module API
|
||||
class ProtectedTags < Grape::API
|
||||
include PaginationParams
|
||||
|
||||
TAG_ENDPOINT_REQUIREMENTS = API::PROJECT_ENDPOINT_REQUIREMENTS.merge(name: API::NO_SLASH_URL_PART_REGEX)
|
||||
|
||||
before { authorize_admin_project }
|
||||
|
||||
params do
|
||||
requires :id, type: String, desc: 'The ID of a project'
|
||||
end
|
||||
resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
|
||||
desc "Get a project's protected tags" do
|
||||
detail 'This feature was introduced in GitLab 11.3.'
|
||||
success Entities::ProtectedTag
|
||||
end
|
||||
params do
|
||||
use :pagination
|
||||
end
|
||||
get ':id/protected_tags' do
|
||||
protected_tags = user_project.protected_tags.preload(:create_access_levels)
|
||||
|
||||
present paginate(protected_tags), with: Entities::ProtectedTag, project: user_project
|
||||
end
|
||||
|
||||
desc 'Get a single protected tag' do
|
||||
detail 'This feature was introduced in GitLab 11.3.'
|
||||
success Entities::ProtectedTag
|
||||
end
|
||||
params do
|
||||
requires :name, type: String, desc: 'The name of the tag or wildcard'
|
||||
end
|
||||
get ':id/protected_tags/:name', requirements: TAG_ENDPOINT_REQUIREMENTS do
|
||||
protected_tag = user_project.protected_tags.find_by!(name: params[:name])
|
||||
|
||||
present protected_tag, with: Entities::ProtectedTag, project: user_project
|
||||
end
|
||||
|
||||
desc 'Protect a single tag or wildcard' do
|
||||
detail 'This feature was introduced in GitLab 11.3.'
|
||||
success Entities::ProtectedTag
|
||||
end
|
||||
params do
|
||||
requires :name, type: String, desc: 'The name of the protected tag'
|
||||
optional :create_access_level, type: Integer, default: Gitlab::Access::MAINTAINER,
|
||||
values: ProtectedRefAccess::ALLOWED_ACCESS_LEVELS,
|
||||
desc: 'Access levels allowed to create (defaults: `40`, maintainer access level)'
|
||||
end
|
||||
post ':id/protected_tags' do
|
||||
protected_tags_params = {
|
||||
name: params[:name],
|
||||
create_access_levels_attributes: [{ access_level: params[:create_access_level] }]
|
||||
}
|
||||
|
||||
protected_tag = ::ProtectedTags::CreateService.new(user_project,
|
||||
current_user,
|
||||
protected_tags_params).execute
|
||||
|
||||
if protected_tag.persisted?
|
||||
present protected_tag, with: Entities::ProtectedTag, project: user_project
|
||||
else
|
||||
render_api_error!(protected_tag.errors.full_messages, 422)
|
||||
end
|
||||
end
|
||||
|
||||
desc 'Unprotect a single tag' do
|
||||
detail 'This feature was introduced in GitLab 11.3.'
|
||||
end
|
||||
params do
|
||||
requires :name, type: String, desc: 'The name of the protected tag'
|
||||
end
|
||||
delete ':id/protected_tags/:name', requirements: TAG_ENDPOINT_REQUIREMENTS do
|
||||
protected_tag = user_project.protected_tags.find_by!(name: params[:name])
|
||||
|
||||
destroy_conditionally!(protected_tag)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
40
spec/migrations/drop_duplicate_protected_tags_spec.rb
Normal file
40
spec/migrations/drop_duplicate_protected_tags_spec.rb
Normal file
|
@ -0,0 +1,40 @@
|
|||
require 'spec_helper'
|
||||
require Rails.root.join('db', 'migrate', '20180711103851_drop_duplicate_protected_tags.rb')
|
||||
|
||||
describe DropDuplicateProtectedTags, :migration do
|
||||
let(:namespaces) { table(:namespaces) }
|
||||
let(:projects) { table(:projects) }
|
||||
let(:protected_tags) { table(:protected_tags) }
|
||||
|
||||
before do
|
||||
stub_const("#{described_class}::BATCH_SIZE", 2)
|
||||
|
||||
namespaces.create(id: 1, name: 'gitlab-org', path: 'gitlab-org')
|
||||
projects.create!(id: 1, namespace_id: 1, name: 'gitlab1', path: 'gitlab1')
|
||||
projects.create!(id: 2, namespace_id: 1, name: 'gitlab2', path: 'gitlab2')
|
||||
end
|
||||
|
||||
it 'removes duplicated protected tags' do
|
||||
protected_tags.create!(id: 1, project_id: 1, name: 'foo')
|
||||
tag2 = protected_tags.create!(id: 2, project_id: 1, name: 'foo1')
|
||||
protected_tags.create!(id: 3, project_id: 1, name: 'foo')
|
||||
tag4 = protected_tags.create!(id: 4, project_id: 1, name: 'foo')
|
||||
tag5 = protected_tags.create!(id: 5, project_id: 2, name: 'foo')
|
||||
|
||||
migrate!
|
||||
|
||||
expect(protected_tags.all.count).to eq 3
|
||||
expect(protected_tags.all.pluck(:id)).to contain_exactly(tag2.id, tag4.id, tag5.id)
|
||||
end
|
||||
|
||||
it 'does not remove unique protected tags' do
|
||||
tag1 = protected_tags.create!(id: 1, project_id: 1, name: 'foo1')
|
||||
tag2 = protected_tags.create!(id: 2, project_id: 1, name: 'foo2')
|
||||
tag3 = protected_tags.create!(id: 3, project_id: 1, name: 'foo3')
|
||||
|
||||
migrate!
|
||||
|
||||
expect(protected_tags.all.count).to eq 3
|
||||
expect(protected_tags.all.pluck(:id)).to contain_exactly(tag1.id, tag2.id, tag3.id)
|
||||
end
|
||||
end
|
202
spec/requests/api/protected_tags_spec.rb
Normal file
202
spec/requests/api/protected_tags_spec.rb
Normal file
|
@ -0,0 +1,202 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe API::ProtectedTags do
|
||||
let(:user) { create(:user) }
|
||||
let!(:project) { create(:project, :repository) }
|
||||
let(:project2) { create(:project, path: 'project2', namespace: user.namespace) }
|
||||
let(:protected_name) { 'feature' }
|
||||
let(:tag_name) { protected_name }
|
||||
let!(:protected_tag) do
|
||||
create(:protected_tag, project: project, name: protected_name)
|
||||
end
|
||||
|
||||
describe 'GET /projects/:id/protected_tags' do
|
||||
let(:route) { "/projects/#{project.id}/protected_tags" }
|
||||
|
||||
shared_examples_for 'protected tags' do
|
||||
it 'returns the protected tags' do
|
||||
get api(route, user), per_page: 100
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to be_an Array
|
||||
|
||||
protected_tag_names = json_response.map { |x| x['name'] }
|
||||
expected_tags_names = project.protected_tags.map { |x| x['name'] }
|
||||
expect(protected_tag_names).to match_array(expected_tags_names)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when authenticated as a maintainer' do
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
end
|
||||
|
||||
it_behaves_like 'protected tags'
|
||||
end
|
||||
|
||||
context 'when authenticated as a guest' do
|
||||
before do
|
||||
project.add_guest(user)
|
||||
end
|
||||
|
||||
it_behaves_like '403 response' do
|
||||
let(:request) { get api(route, user) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /projects/:id/protected_tags/:tag' do
|
||||
let(:route) { "/projects/#{project.id}/protected_tags/#{tag_name}" }
|
||||
|
||||
shared_examples_for 'protected tag' do
|
||||
it 'returns the protected tag' do
|
||||
get api(route, user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(json_response['name']).to eq(tag_name)
|
||||
expect(json_response['create_access_levels'][0]['access_level']).to eq(::Gitlab::Access::MAINTAINER)
|
||||
end
|
||||
|
||||
context 'when protected tag does not exist' do
|
||||
let(:tag_name) { 'unknown' }
|
||||
|
||||
it_behaves_like '404 response' do
|
||||
let(:request) { get api(route, user) }
|
||||
let(:message) { '404 Not found' }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when authenticated as a maintainer' do
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
end
|
||||
|
||||
it_behaves_like 'protected tag'
|
||||
|
||||
context 'when protected tag contains a wildcard' do
|
||||
let(:protected_name) { 'feature*' }
|
||||
|
||||
it_behaves_like 'protected tag'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when authenticated as a guest' do
|
||||
before do
|
||||
project.add_guest(user)
|
||||
end
|
||||
|
||||
it_behaves_like '403 response' do
|
||||
let(:request) { get api(route, user) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /projects/:id/protected_tags' do
|
||||
let(:tag_name) { 'new_tag' }
|
||||
|
||||
context 'when authenticated as a maintainer' do
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
end
|
||||
|
||||
it 'protects a single tag with maintainers can create tags' do
|
||||
post api("/projects/#{project.id}/protected_tags", user), name: tag_name
|
||||
|
||||
expect(response).to have_gitlab_http_status(201)
|
||||
expect(json_response['name']).to eq(tag_name)
|
||||
expect(json_response['create_access_levels'][0]['access_level']).to eq(Gitlab::Access::MAINTAINER)
|
||||
end
|
||||
|
||||
it 'protects a single tag with developers can create tags' do
|
||||
post api("/projects/#{project.id}/protected_tags", user),
|
||||
name: tag_name, create_access_level: 30
|
||||
|
||||
expect(response).to have_gitlab_http_status(201)
|
||||
expect(json_response['name']).to eq(tag_name)
|
||||
expect(json_response['create_access_levels'][0]['access_level']).to eq(Gitlab::Access::DEVELOPER)
|
||||
end
|
||||
|
||||
it 'protects a single tag with no one can create tags' do
|
||||
post api("/projects/#{project.id}/protected_tags", user),
|
||||
name: tag_name, create_access_level: 0
|
||||
|
||||
expect(response).to have_gitlab_http_status(201)
|
||||
expect(json_response['name']).to eq(tag_name)
|
||||
expect(json_response['create_access_levels'][0]['access_level']).to eq(Gitlab::Access::NO_ACCESS)
|
||||
end
|
||||
|
||||
it 'returns a 422 error if the same tag is protected twice' do
|
||||
post api("/projects/#{project.id}/protected_tags", user), name: protected_name
|
||||
|
||||
expect(response).to have_gitlab_http_status(422)
|
||||
expect(json_response['message'][0]).to eq('Name has already been taken')
|
||||
end
|
||||
|
||||
it 'returns 201 if the same tag is proteted on different projects' do
|
||||
post api("/projects/#{project.id}/protected_tags", user), name: protected_name
|
||||
post api("/projects/#{project2.id}/protected_tags", user), name: protected_name
|
||||
|
||||
expect(response).to have_gitlab_http_status(201)
|
||||
expect(json_response['name']).to eq(protected_name)
|
||||
end
|
||||
|
||||
context 'when tag has a wildcard in its name' do
|
||||
let(:tag_name) { 'feature/*' }
|
||||
|
||||
it 'protects multiple tags with a wildcard in the name' do
|
||||
post api("/projects/#{project.id}/protected_tags", user), name: tag_name
|
||||
|
||||
expect(response).to have_gitlab_http_status(201)
|
||||
expect(json_response['name']).to eq(tag_name)
|
||||
expect(json_response['create_access_levels'][0]['access_level']).to eq(Gitlab::Access::MAINTAINER)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when authenticated as a guest' do
|
||||
before do
|
||||
project.add_guest(user)
|
||||
end
|
||||
|
||||
it 'returns a 403 error if guest' do
|
||||
post api("/projects/#{project.id}/protected_tags/", user), name: tag_name
|
||||
|
||||
expect(response).to have_gitlab_http_status(403)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE /projects/:id/protected_tags/unprotect/:tag' do
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
end
|
||||
|
||||
it 'unprotects a single tag' do
|
||||
delete api("/projects/#{project.id}/protected_tags/#{tag_name}", user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(204)
|
||||
end
|
||||
|
||||
it_behaves_like '412 response' do
|
||||
let(:request) { api("/projects/#{project.id}/protected_tags/#{tag_name}", user) }
|
||||
end
|
||||
|
||||
it "returns 404 if tag does not exist" do
|
||||
delete api("/projects/#{project.id}/protected_tags/barfoo", user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(404)
|
||||
end
|
||||
|
||||
context 'when tag has a wildcard in its name' do
|
||||
let(:protected_name) { 'feature*' }
|
||||
|
||||
it 'unprotects a wildcard tag' do
|
||||
delete api("/projects/#{project.id}/protected_tags/#{tag_name}", user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(204)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue