Add optional param :start_project to allow variable commit targets
This extends POST#:id/repository/commits to allow the optional parameter `:start_project`, which will allow targeting other projects besides the one derived from `:id`. Resolves https://gitlab.com/gitlab-org/gitlab-ce/issues/50850
This commit is contained in:
parent
9a8955d3c0
commit
35c37fb293
4 changed files with 108 additions and 0 deletions
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Add API support for committing changes to different projects in same fork network
|
||||||
|
merge_request: 27915
|
||||||
|
author:
|
||||||
|
type: added
|
|
@ -75,6 +75,7 @@ POST /projects/:id/repository/commits
|
||||||
| `branch` | string | yes | Name of the branch to commit into. To create a new branch, also provide `start_branch`. |
|
| `branch` | string | yes | Name of the branch to commit into. To create a new branch, also provide `start_branch`. |
|
||||||
| `commit_message` | string | yes | Commit message |
|
| `commit_message` | string | yes | Commit message |
|
||||||
| `start_branch` | string | no | Name of the branch to start the new commit from |
|
| `start_branch` | string | no | Name of the branch to start the new commit from |
|
||||||
|
| `start_project` | integer/string | no | The project ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) to start the commit from. Defaults to the value of `id`. |
|
||||||
| `actions[]` | array | yes | An array of action hashes to commit as a batch. See the next table for what attributes it can take. |
|
| `actions[]` | array | yes | An array of action hashes to commit as a batch. See the next table for what attributes it can take. |
|
||||||
| `author_email` | string | no | Specify the commit author's email address |
|
| `author_email` | string | no | Specify the commit author's email address |
|
||||||
| `author_name` | string | no | Specify the commit author's name |
|
| `author_name` | string | no | Specify the commit author's name |
|
||||||
|
|
|
@ -96,17 +96,27 @@ module API
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
optional :start_branch, type: String, desc: 'Name of the branch to start the new commit from'
|
optional :start_branch, type: String, desc: 'Name of the branch to start the new commit from'
|
||||||
|
optional :start_project, types: [Integer, String], desc: 'The ID or path of the project to start the commit from'
|
||||||
optional :author_email, type: String, desc: 'Author email for commit'
|
optional :author_email, type: String, desc: 'Author email for commit'
|
||||||
optional :author_name, type: String, desc: 'Author name for commit'
|
optional :author_name, type: String, desc: 'Author name for commit'
|
||||||
optional :stats, type: Boolean, default: true, desc: 'Include commit stats'
|
optional :stats, type: Boolean, default: true, desc: 'Include commit stats'
|
||||||
optional :force, type: Boolean, default: false, desc: 'When `true` overwrites the target branch with a new commit based on the `start_branch`'
|
optional :force, type: Boolean, default: false, desc: 'When `true` overwrites the target branch with a new commit based on the `start_branch`'
|
||||||
end
|
end
|
||||||
post ':id/repository/commits' do
|
post ':id/repository/commits' do
|
||||||
|
if params[:start_project]
|
||||||
|
start_project = find_project!(params[:start_project])
|
||||||
|
|
||||||
|
unless user_project.forked_from?(start_project)
|
||||||
|
forbidden!("Project is not included in the fork network for #{start_project.full_name}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
authorize_push_to_branch!(params[:branch])
|
authorize_push_to_branch!(params[:branch])
|
||||||
|
|
||||||
attrs = declared_params
|
attrs = declared_params
|
||||||
attrs[:branch_name] = attrs.delete(:branch)
|
attrs[:branch_name] = attrs.delete(:branch)
|
||||||
attrs[:start_branch] ||= attrs[:branch_name]
|
attrs[:start_branch] ||= attrs[:branch_name]
|
||||||
|
attrs[:start_project] = start_project if start_project
|
||||||
|
|
||||||
result = ::Files::MultiService.new(user_project, current_user, attrs).execute
|
result = ::Files::MultiService.new(user_project, current_user, attrs).execute
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,8 @@ require 'spec_helper'
|
||||||
require 'mime/types'
|
require 'mime/types'
|
||||||
|
|
||||||
describe API::Commits do
|
describe API::Commits do
|
||||||
|
include ProjectForksHelper
|
||||||
|
|
||||||
let(:user) { create(:user) }
|
let(:user) { create(:user) }
|
||||||
let(:guest) { create(:user).tap { |u| project.add_guest(u) } }
|
let(:guest) { create(:user).tap { |u| project.add_guest(u) } }
|
||||||
let(:project) { create(:project, :repository, creator: user, path: 'my.project') }
|
let(:project) { create(:project, :repository, creator: user, path: 'my.project') }
|
||||||
|
@ -317,6 +319,96 @@ describe API::Commits do
|
||||||
expect(response).to have_gitlab_http_status(201)
|
expect(response).to have_gitlab_http_status(201)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when the API user is a guest' do
|
||||||
|
def last_commit_id(project, branch_name)
|
||||||
|
project.repository.find_branch(branch_name)&.dereferenced_target&.id
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:public_project) { create(:project, :public, :repository) }
|
||||||
|
let!(:url) { "/projects/#{public_project.id}/repository/commits" }
|
||||||
|
let(:guest) { create(:user).tap { |u| public_project.add_guest(u) } }
|
||||||
|
|
||||||
|
it 'returns a 403' do
|
||||||
|
post api(url, guest), params: valid_c_params
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(403)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when start_project is provided' do
|
||||||
|
context 'when posting to a forked project the user owns' do
|
||||||
|
let!(:forked_project) { fork_project(public_project, guest, namespace: guest.namespace, repository: true) }
|
||||||
|
let!(:url) { "/projects/#{forked_project.id}/repository/commits" }
|
||||||
|
|
||||||
|
before do
|
||||||
|
valid_c_params[:start_branch] = "master"
|
||||||
|
valid_c_params[:branch] = "patch"
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'identified by Integer (id)' do
|
||||||
|
before do
|
||||||
|
valid_c_params[:start_project] = public_project.id
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'adds a new commit to forked_project and returns a 201' do
|
||||||
|
expect { post api(url, guest), params: valid_c_params }
|
||||||
|
.to change { last_commit_id(forked_project, valid_c_params[:branch]) }
|
||||||
|
.and not_change { last_commit_id(public_project, valid_c_params[:start_branch]) }
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(201)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'identified by String (full_path)' do
|
||||||
|
before do
|
||||||
|
valid_c_params[:start_project] = public_project.full_path
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'adds a new commit to forked_project and returns a 201' do
|
||||||
|
expect { post api(url, guest), params: valid_c_params }
|
||||||
|
.to change { last_commit_id(forked_project, valid_c_params[:branch]) }
|
||||||
|
.and not_change { last_commit_id(public_project, valid_c_params[:start_branch]) }
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(201)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the target project is not part of the fork network of start_project' do
|
||||||
|
let(:unrelated_project) { create(:project, :public, :repository, creator: guest) }
|
||||||
|
let!(:url) { "/projects/#{unrelated_project.id}/repository/commits" }
|
||||||
|
|
||||||
|
before do
|
||||||
|
valid_c_params[:start_branch] = "master"
|
||||||
|
valid_c_params[:branch] = "patch"
|
||||||
|
valid_c_params[:start_project] = public_project.id
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns a 403' do
|
||||||
|
post api(url, guest), params: valid_c_params
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(403)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when posting to a forked project the user does not have write access' do
|
||||||
|
let!(:forked_project) { fork_project(public_project, user, namespace: user.namespace, repository: true) }
|
||||||
|
let!(:url) { "/projects/#{forked_project.id}/repository/commits" }
|
||||||
|
|
||||||
|
before do
|
||||||
|
valid_c_params[:start_branch] = "master"
|
||||||
|
valid_c_params[:branch] = "patch"
|
||||||
|
valid_c_params[:start_project] = public_project.id
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns a 403' do
|
||||||
|
post api(url, guest), params: valid_c_params
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(403)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'delete' do
|
describe 'delete' do
|
||||||
|
|
Loading…
Reference in a new issue