Commit 2b5aa3ef authored by Rémy Coutable's avatar Rémy Coutable

Merge branch '43832-adds-chdmod-to-commits-actions-api' into 'master'

Allows to work with execute permissions in GitLab API

Closes #43832

See merge request gitlab-org/gitlab-ce!21866
parents d5bce06d 271776d4
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
module Files module Files
class MultiService < Files::BaseService class MultiService < Files::BaseService
UPDATE_FILE_ACTIONS = %w(update move delete).freeze UPDATE_FILE_ACTIONS = %w(update move delete chmod).freeze
def create_commit! def create_commit!
transformer = Lfs::FileTransformer.new(project, @branch_name) transformer = Lfs::FileTransformer.new(project, @branch_name)
......
---
title: Allows to chmod file with commits API
merge_request: 21866
author: Jacopo Beschi @jacopo-beschi
type: added
...@@ -83,12 +83,13 @@ POST /projects/:id/repository/commits ...@@ -83,12 +83,13 @@ POST /projects/:id/repository/commits
| `actions[]` Attribute | Type | Required | Description | | `actions[]` Attribute | Type | Required | Description |
| --------------------- | ---- | -------- | ----------- | | --------------------- | ---- | -------- | ----------- |
| `action` | string | yes | The action to perform, `create`, `delete`, `move`, `update` | | `action` | string | yes | The action to perform, `create`, `delete`, `move`, `update`, `chmod`|
| `file_path` | string | yes | Full path to the file. Ex. `lib/class.rb` | | `file_path` | string | yes | Full path to the file. Ex. `lib/class.rb` |
| `previous_path` | string | no | Original full path to the file being moved. Ex. `lib/class1.rb` | | `previous_path` | string | no | Original full path to the file being moved. Ex. `lib/class1.rb`. Only considered for `move` action. |
| `content` | string | no | File content, required for all except `delete`. Optional for `move` | | `content` | string | no | File content, required for all except `delete` and `chmod`. Optional for `move` |
| `encoding` | string | no | `text` or `base64`. `text` is default. | | `encoding` | string | no | `text` or `base64`. `text` is default. |
| `last_commit_id` | string | no | Last known file commit id. Will be only considered in update, move and delete actions. | | `last_commit_id` | string | no | Last known file commit id. Will be only considered in update, move and delete actions. |
| `execute_filemode` | boolean | no | When `true/false` enables/disables the execute flag on the file. Only considered for `chmod` action. |
```bash ```bash
PAYLOAD=$(cat << 'JSON' PAYLOAD=$(cat << 'JSON'
...@@ -115,6 +116,11 @@ PAYLOAD=$(cat << 'JSON' ...@@ -115,6 +116,11 @@ PAYLOAD=$(cat << 'JSON'
"action": "update", "action": "update",
"file_path": "foo/bar5", "file_path": "foo/bar5",
"content": "new content" "content": "new content"
},
{
"action": "chmod",
"file_path": "foo/bar5",
"execute_filemode": true
} }
] ]
} }
......
...@@ -73,7 +73,26 @@ module API ...@@ -73,7 +73,26 @@ module API
params do params do
requires :branch, type: String, desc: 'Name of the branch to commit into. To create a new branch, also provide `start_branch`.', allow_blank: false requires :branch, type: String, desc: 'Name of the branch to commit into. To create a new branch, also provide `start_branch`.', allow_blank: false
requires :commit_message, type: String, desc: 'Commit message' requires :commit_message, type: String, desc: 'Commit message'
requires :actions, type: Array[Hash], desc: 'Actions to perform in commit' requires :actions, type: Array, desc: 'Actions to perform in commit' do
requires :action, type: String, desc: 'The action to perform, `create`, `delete`, `move`, `update`, `chmod`', values: %w[create update move delete chmod].freeze
requires :file_path, type: String, desc: 'Full path to the file. Ex. `lib/class.rb`'
given action: ->(action) { action == 'move' } do
requires :previous_path, type: String, desc: 'Original full path to the file being moved. Ex. `lib/class1.rb`'
end
given action: ->(action) { %w[create move].include? action } do
optional :content, type: String, desc: 'File content'
end
given action: ->(action) { action == 'update' } do
requires :content, type: String, desc: 'File content'
end
optional :encoding, type: String, desc: '`text` or `base64`', default: 'text', values: %w[text base64]
given action: ->(action) { %w[update move delete].include? action } do
optional :last_commit_id, type: String, desc: 'Last known file commit id'
end
given action: ->(action) { action == 'chmod' } do
requires :execute_filemode, type: Boolean, desc: 'When `true/false` enables/disables the execute flag on the file.'
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 :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'
......
...@@ -333,7 +333,8 @@ module Gitlab ...@@ -333,7 +333,8 @@ module Gitlab
action: action[:action].upcase.to_sym, action: action[:action].upcase.to_sym,
file_path: encode_binary(action[:file_path]), file_path: encode_binary(action[:file_path]),
previous_path: encode_binary(action[:previous_path]), previous_path: encode_binary(action[:previous_path]),
base64_content: action[:encoding] == 'base64' base64_content: action[:encoding] == 'base64',
execute_filemode: !!action[:execute_filemode]
) )
rescue RangeError rescue RangeError
raise ArgumentError, "Unknown action '#{action[:action]}'" raise ArgumentError, "Unknown action '#{action[:action]}'"
......
...@@ -238,7 +238,7 @@ describe API::Commits do ...@@ -238,7 +238,7 @@ describe API::Commits do
describe 'create' do describe 'create' do
let(:message) { 'Created file' } let(:message) { 'Created file' }
let!(:invalid_c_params) do let(:invalid_c_params) do
{ {
branch: 'master', branch: 'master',
commit_message: message, commit_message: message,
...@@ -251,7 +251,7 @@ describe API::Commits do ...@@ -251,7 +251,7 @@ describe API::Commits do
] ]
} }
end end
let!(:valid_c_params) do let(:valid_c_params) do
{ {
branch: 'master', branch: 'master',
commit_message: message, commit_message: message,
...@@ -264,7 +264,7 @@ describe API::Commits do ...@@ -264,7 +264,7 @@ describe API::Commits do
] ]
} }
end end
let!(:valid_utf8_c_params) do let(:valid_utf8_c_params) do
{ {
branch: 'master', branch: 'master',
commit_message: message, commit_message: message,
...@@ -315,7 +315,7 @@ describe API::Commits do ...@@ -315,7 +315,7 @@ describe API::Commits do
describe 'delete' do describe 'delete' do
let(:message) { 'Deleted file' } let(:message) { 'Deleted file' }
let!(:invalid_d_params) do let(:invalid_d_params) do
{ {
branch: 'markdown', branch: 'markdown',
commit_message: message, commit_message: message,
...@@ -327,7 +327,7 @@ describe API::Commits do ...@@ -327,7 +327,7 @@ describe API::Commits do
] ]
} }
end end
let!(:valid_d_params) do let(:valid_d_params) do
{ {
branch: 'markdown', branch: 'markdown',
commit_message: message, commit_message: message,
...@@ -356,7 +356,7 @@ describe API::Commits do ...@@ -356,7 +356,7 @@ describe API::Commits do
describe 'move' do describe 'move' do
let(:message) { 'Moved file' } let(:message) { 'Moved file' }
let!(:invalid_m_params) do let(:invalid_m_params) do
{ {
branch: 'feature', branch: 'feature',
commit_message: message, commit_message: message,
...@@ -370,7 +370,7 @@ describe API::Commits do ...@@ -370,7 +370,7 @@ describe API::Commits do
] ]
} }
end end
let!(:valid_m_params) do let(:valid_m_params) do
{ {
branch: 'feature', branch: 'feature',
commit_message: message, commit_message: message,
...@@ -401,7 +401,7 @@ describe API::Commits do ...@@ -401,7 +401,7 @@ describe API::Commits do
describe 'update' do describe 'update' do
let(:message) { 'Updated file' } let(:message) { 'Updated file' }
let!(:invalid_u_params) do let(:invalid_u_params) do
{ {
branch: 'master', branch: 'master',
commit_message: message, commit_message: message,
...@@ -414,7 +414,7 @@ describe API::Commits do ...@@ -414,7 +414,7 @@ describe API::Commits do
] ]
} }
end end
let!(:valid_u_params) do let(:valid_u_params) do
{ {
branch: 'master', branch: 'master',
commit_message: message, commit_message: message,
...@@ -442,9 +442,57 @@ describe API::Commits do ...@@ -442,9 +442,57 @@ describe API::Commits do
end end
end end
describe 'chmod' do
let(:message) { 'Chmod +x file' }
let(:file_path) { 'files/ruby/popen.rb' }
let(:execute_filemode) { true }
let(:params) do
{
branch: 'master',
commit_message: message,
actions: [
{
action: 'chmod',
file_path: file_path,
execute_filemode: execute_filemode
}
]
}
end
it 'responds with success' do
post api(url, user), params
expect(response).to have_gitlab_http_status(201)
expect(json_response['title']).to eq(message)
end
context 'when execute_filemode is false' do
let(:execute_filemode) { false }
it 'responds with success' do
post api(url, user), params
expect(response).to have_gitlab_http_status(201)
expect(json_response['title']).to eq(message)
end
end
context "when the file doesn't exists" do
let(:file_path) { 'foo/bar.baz' }
it "responds with 400" do
post api(url, user), params
expect(response).to have_gitlab_http_status(400)
expect(json_response['message']).to eq("A file with this name doesn't exist")
end
end
end
describe 'multiple operations' do describe 'multiple operations' do
let(:message) { 'Multiple actions' } let(:message) { 'Multiple actions' }
let!(:invalid_mo_params) do let(:invalid_mo_params) do
{ {
branch: 'master', branch: 'master',
commit_message: message, commit_message: message,
...@@ -468,11 +516,16 @@ describe API::Commits do ...@@ -468,11 +516,16 @@ describe API::Commits do
action: 'update', action: 'update',
file_path: 'foo/bar.baz', file_path: 'foo/bar.baz',
content: 'puts 8' content: 'puts 8'
},
{
action: 'chmod',
file_path: 'files/ruby/popen.rb',
execute_filemode: true
} }
] ]
} }
end end
let!(:valid_mo_params) do let(:valid_mo_params) do
{ {
branch: 'master', branch: 'master',
commit_message: message, commit_message: message,
...@@ -496,6 +549,11 @@ describe API::Commits do ...@@ -496,6 +549,11 @@ describe API::Commits do
action: 'update', action: 'update',
file_path: 'files/ruby/popen.rb', file_path: 'files/ruby/popen.rb',
content: 'puts 8' content: 'puts 8'
},
{
action: 'chmod',
file_path: 'files/ruby/popen.rb',
execute_filemode: true
} }
] ]
} }
......
...@@ -11,6 +11,7 @@ describe Files::MultiService do ...@@ -11,6 +11,7 @@ describe Files::MultiService do
let(:new_file_path) { 'files/ruby/popen.rb' } let(:new_file_path) { 'files/ruby/popen.rb' }
let(:file_content) { 'New content' } let(:file_content) { 'New content' }
let(:action) { 'update' } let(:action) { 'update' }
let(:commit_message) { 'Update File' }
let!(:original_commit_id) do let!(:original_commit_id) do
Gitlab::Git::Commit.last_for_path(project.repository, branch_name, original_file_path).sha Gitlab::Git::Commit.last_for_path(project.repository, branch_name, original_file_path).sha
...@@ -30,7 +31,7 @@ describe Files::MultiService do ...@@ -30,7 +31,7 @@ describe Files::MultiService do
let(:commit_params) do let(:commit_params) do
{ {
commit_message: "Update File", commit_message: commit_message,
branch_name: branch_name, branch_name: branch_name,
start_branch: branch_name, start_branch: branch_name,
actions: actions actions: actions
...@@ -84,6 +85,39 @@ describe Files::MultiService do ...@@ -84,6 +85,39 @@ describe Files::MultiService do
end end
end end
describe 'changing execute_filemode of a file' do
let(:commit_message) { 'Chmod +x file' }
let(:file_path) { original_file_path }
let(:default_action) do
{
action: 'chmod',
file_path: file_path,
execute_filemode: true
}
end
it 'accepts the commit' do
results = subject.execute
expect(results[:status]).to eq(:success)
end
it 'updates the execute_filemode of the file' do
expect { subject.execute }.to change { repository.blob_at_branch(branch_name, file_path).mode }.from('100644').to('100755')
end
context "when the file doesn't exists" do
let(:file_path) { 'files/wrong_path.rb' }
it 'rejects the commit' do
results = subject.execute
expect(results[:status]).to eq(:error)
expect(results[:message]).to eq("A file with this name doesn't exist")
end
end
end
context 'when moving a file' do context 'when moving a file' do
let(:action) { 'move' } let(:action) { 'move' }
let(:new_file_path) { 'files/ruby/new_popen.rb' } let(:new_file_path) { 'files/ruby/new_popen.rb' }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment