diff options
-rw-r--r-- | GITALY_SERVER_VERSION | 2 | ||||
-rw-r--r-- | Gemfile | 2 | ||||
-rw-r--r-- | Gemfile.lock | 4 | ||||
-rw-r--r-- | app/models/repository.rb | 1 | ||||
-rw-r--r-- | changelogs/unreleased/24035-api-create-application.yml | 4 | ||||
-rw-r--r-- | changelogs/unreleased/osw-updates-merge-status-on-api-actions.yml | 5 | ||||
-rw-r--r-- | doc/api/applications.md | 37 | ||||
-rw-r--r-- | doc/ci/variables/README.md | 2 | ||||
-rw-r--r-- | lib/api/api.rb | 1 | ||||
-rw-r--r-- | lib/api/applications.rb | 27 | ||||
-rw-r--r-- | lib/api/entities.rb | 21 | ||||
-rw-r--r-- | lib/gitlab/git/repository.rb | 12 | ||||
-rw-r--r-- | lib/gitlab/gitaly_client/repository_service.rb | 17 | ||||
-rw-r--r-- | lib/gitlab/import_export/command_line_util.rb | 4 | ||||
-rw-r--r-- | lib/gitlab/import_export/repo_saver.rb | 2 | ||||
-rw-r--r-- | lib/gitlab/import_export/wiki_repo_saver.rb | 2 | ||||
-rw-r--r-- | spec/lib/gitlab/git/repository_spec.rb | 28 | ||||
-rw-r--r-- | spec/requests/api/applications_spec.rb | 86 |
18 files changed, 245 insertions, 12 deletions
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 7375dee5f49..31b648bd6fa 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -0.72.0 +0.73.0 @@ -406,7 +406,7 @@ group :ed25519 do end # Gitaly GRPC client -gem 'gitaly-proto', '~> 0.74.0', require: 'gitaly' +gem 'gitaly-proto', '~> 0.76.0', require: 'gitaly' gem 'toml-rb', '~> 0.3.15', require: false diff --git a/Gemfile.lock b/Gemfile.lock index d69c532b309..1cbeab8d6b5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -285,7 +285,7 @@ GEM po_to_json (>= 1.0.0) rails (>= 3.2.0) gherkin-ruby (0.3.2) - gitaly-proto (0.74.0) + gitaly-proto (0.76.0) google-protobuf (~> 3.1) grpc (~> 1.0) github-linguist (4.7.6) @@ -1056,7 +1056,7 @@ DEPENDENCIES gettext (~> 3.2.2) gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails_js (~> 1.2.0) - gitaly-proto (~> 0.74.0) + gitaly-proto (~> 0.76.0) github-linguist (~> 4.7.0) gitlab-flowdock-git-hook (~> 1.0.1) gitlab-markup (~> 1.6.2) diff --git a/app/models/repository.rb b/app/models/repository.rb index 73c4899cb9b..824e18bec78 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -20,6 +20,7 @@ class Repository attr_accessor :full_path, :disk_path, :project, :is_wiki delegate :ref_name_for_sha, to: :raw_repository + delegate :bundle_to_disk, to: :raw_repository CreateTreeError = Class.new(StandardError) diff --git a/changelogs/unreleased/24035-api-create-application.yml b/changelogs/unreleased/24035-api-create-application.yml new file mode 100644 index 00000000000..c583a020d9d --- /dev/null +++ b/changelogs/unreleased/24035-api-create-application.yml @@ -0,0 +1,4 @@ +--- +title: Add application create API +merge_request: 8160 +author: Nicolas Merelli @PNSalocin diff --git a/changelogs/unreleased/osw-updates-merge-status-on-api-actions.yml b/changelogs/unreleased/osw-updates-merge-status-on-api-actions.yml new file mode 100644 index 00000000000..3854985e576 --- /dev/null +++ b/changelogs/unreleased/osw-updates-merge-status-on-api-actions.yml @@ -0,0 +1,5 @@ +--- +title: Return more consistent values for merge_status on MR APIs +merge_request: +author: +type: fixed diff --git a/doc/api/applications.md b/doc/api/applications.md new file mode 100644 index 00000000000..933867ed0bb --- /dev/null +++ b/doc/api/applications.md @@ -0,0 +1,37 @@ +# Applications API + +> [Introduced][ce-8160] in GitLab 10.5 + +[ce-8160]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8160 + +## Create a application + +Create a application by posting a JSON payload. + +User must be admin to do that. + +Returns `200` if the request succeeds. + +``` +POST /applications +``` + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `name` | string | yes | The name of the application | +| `redirect_uri` | string | yes | The redirect URI of the application | +| `scopes` | string | yes | The scopes of the application | + +```bash +curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --data "name=MyApplication&redirect_uri=http://redirect.uri&scopes=" https://gitlab.example.com/api/v3/applications +``` + +Example response: + +```json +{ + "application_id": "5832fc6e14300a0d962240a8144466eef4ee93ef0d218477e55f11cf12fc3737", + "secret": "ee1dd64b6adc89cf7e2c23099301ccc2c61b441064e9324d963c46902a85ec34", + "callback_url": "http://redirect.uri" +} +``` diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md index 06f69938aae..598a7515b01 100644 --- a/doc/ci/variables/README.md +++ b/doc/ci/variables/README.md @@ -110,7 +110,7 @@ future GitLab releases.** | `CI_BUILD_MANUAL` | `CI_JOB_MANUAL` | | `CI_BUILD_TOKEN` | `CI_JOB_TOKEN` | -## `.gitlab-ci.yaml` defined variables +## `.gitlab-ci.yml` defined variables >**Note:** This feature requires GitLab Runner 0.5.0 or higher and GitLab CI 7.14 or higher. diff --git a/lib/api/api.rb b/lib/api/api.rb index ae161efb358..f3f64244589 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -106,6 +106,7 @@ module API # Keep in alphabetical order mount ::API::AccessRequests + mount ::API::Applications mount ::API::AwardEmoji mount ::API::Boards mount ::API::Branches diff --git a/lib/api/applications.rb b/lib/api/applications.rb new file mode 100644 index 00000000000..b122cdefe4e --- /dev/null +++ b/lib/api/applications.rb @@ -0,0 +1,27 @@ +module API + # External applications API + class Applications < Grape::API + before { authenticated_as_admin! } + + resource :applications do + desc 'Create a new application' do + detail 'This feature was introduced in GitLab 10.5' + success Entities::ApplicationWithSecret + end + params do + requires :name, type: String, desc: 'Application name' + requires :redirect_uri, type: String, desc: 'Application redirect URI' + requires :scopes, type: String, desc: 'Application scopes' + end + post do + application = Doorkeeper::Application.new(declared_params) + + if application.save + present application, with: Entities::ApplicationWithSecret + else + render_validation_error! application + end + end + end + end +end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 3f4b62dc1b2..5b470bd3479 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -507,7 +507,16 @@ module API expose :work_in_progress?, as: :work_in_progress expose :milestone, using: Entities::Milestone expose :merge_when_pipeline_succeeds - expose :merge_status + + # Ideally we should deprecate `MergeRequest#merge_status` exposure and + # use `MergeRequest#mergeable?` instead (boolean). + # See https://gitlab.com/gitlab-org/gitlab-ce/issues/42344 for more + # information. + expose :merge_status do |merge_request| + # In order to avoid having a breaking change for users, we keep returning the + # expected values from MergeRequest#merge_status state machine. + merge_request.mergeable? ? 'can_be_merged' : 'cannot_be_merged' + end expose :diff_head_sha, as: :sha expose :merge_commit_sha expose :user_notes_count @@ -1157,5 +1166,15 @@ module API pages_domain end end + + class Application < Grape::Entity + expose :uid, as: :application_id + expose :redirect_uri, as: :callback_url + end + + # Use with care, this exposes the secret + class ApplicationWithSecret < Application + expose :secret + end end end diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index d666362de0d..9864e4251f1 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -1268,6 +1268,18 @@ module Gitlab success || gitlab_projects_error end + def bundle_to_disk(save_path) + gitaly_migrate(:bundle_to_disk) do |is_enabled| + if is_enabled + gitaly_repository_client.create_bundle(save_path) + else + run_git!(%W(bundle create #{save_path} --all)) + end + end + + true + end + # rubocop:disable Metrics/ParameterLists def multi_action( user, branch_name:, message:, actions:, diff --git a/lib/gitlab/gitaly_client/repository_service.rb b/lib/gitlab/gitaly_client/repository_service.rb index 12016aee2a6..654a3c314f1 100644 --- a/lib/gitlab/gitaly_client/repository_service.rb +++ b/lib/gitlab/gitaly_client/repository_service.rb @@ -161,6 +161,23 @@ module Gitlab return response.error.b, 1 end end + + def create_bundle(save_path) + request = Gitaly::CreateBundleRequest.new(repository: @gitaly_repo) + response = GitalyClient.call( + @storage, + :repository_service, + :create_bundle, + request, + timeout: GitalyClient.default_timeout + ) + + File.open(save_path, 'wb') do |f| + response.each do |message| + f.write(message.data) + end + end + end end end end diff --git a/lib/gitlab/import_export/command_line_util.rb b/lib/gitlab/import_export/command_line_util.rb index dd5d35feab9..25399f307f2 100644 --- a/lib/gitlab/import_export/command_line_util.rb +++ b/lib/gitlab/import_export/command_line_util.rb @@ -11,10 +11,6 @@ module Gitlab untar_with_options(archive: archive, dir: dir, options: 'zxf') end - def git_bundle(repo_path:, bundle_path:) - execute(%W(#{git_bin_path} --git-dir=#{repo_path} bundle create #{bundle_path} --all)) - end - def git_clone_bundle(repo_path:, bundle_path:) execute(%W(#{git_bin_path} clone --bare -- #{bundle_path} #{repo_path})) Gitlab::Git::Repository.create_hooks(repo_path, File.expand_path(Gitlab.config.gitlab_shell.hooks_path)) diff --git a/lib/gitlab/import_export/repo_saver.rb b/lib/gitlab/import_export/repo_saver.rb index a7028a32570..695462c7dd2 100644 --- a/lib/gitlab/import_export/repo_saver.rb +++ b/lib/gitlab/import_export/repo_saver.rb @@ -21,7 +21,7 @@ module Gitlab def bundle_to_disk mkdir_p(@shared.export_path) - git_bundle(repo_path: path_to_repo, bundle_path: @full_path) + @project.repository.bundle_to_disk(@full_path) rescue => e @shared.error(e) false diff --git a/lib/gitlab/import_export/wiki_repo_saver.rb b/lib/gitlab/import_export/wiki_repo_saver.rb index 1e6722a7bba..5fa2e101e29 100644 --- a/lib/gitlab/import_export/wiki_repo_saver.rb +++ b/lib/gitlab/import_export/wiki_repo_saver.rb @@ -10,7 +10,7 @@ module Gitlab def bundle_to_disk(full_path) mkdir_p(@shared.export_path) - git_bundle(repo_path: path_to_repo, bundle_path: full_path) + @wiki.repository.bundle_to_disk(full_path) rescue => e @shared.error(e) false diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index aec7cde6df8..36ca3980de9 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -1926,6 +1926,34 @@ describe Gitlab::Git::Repository, seed_helper: true do it { expect(subject.repository_relative_path).to eq(repository.relative_path) } end + describe '#bundle_to_disk' do + shared_examples 'bundling to disk' do + let(:save_path) { File.join(Dir.tmpdir, "repo-#{SecureRandom.hex}.bundle") } + + after do + FileUtils.rm_rf(save_path) + end + + it 'saves a bundle to disk' do + repository.bundle_to_disk(save_path) + + success = system( + *%W(#{Gitlab.config.git.bin_path} -C #{repository.path} bundle verify #{save_path}), + [:out, :err] => '/dev/null' + ) + expect(success).to be true + end + end + + context 'when Gitaly bundle_to_disk feature is enabled' do + it_behaves_like 'bundling to disk' + end + + context 'when Gitaly bundle_to_disk feature is disabled', :disable_gitaly do + it_behaves_like 'bundling to disk' + end + end + context 'gitlab_projects commands' do let(:gitlab_projects) { repository.gitlab_projects } let(:timeout) { Gitlab.config.gitlab_shell.git_timeout } diff --git a/spec/requests/api/applications_spec.rb b/spec/requests/api/applications_spec.rb new file mode 100644 index 00000000000..f56bc932f40 --- /dev/null +++ b/spec/requests/api/applications_spec.rb @@ -0,0 +1,86 @@ +require 'spec_helper' + +describe API::Applications, :api do + include ApiHelpers + + let(:admin_user) { create(:user, admin: true) } + let(:user) { create(:user, admin: false) } + + describe 'POST /applications' do + context 'authenticated and authorized user' do + it 'creates and returns an OAuth application' do + expect do + post api('/applications', admin_user), name: 'application_name', redirect_uri: 'http://application.url', scopes: '' + end.to change { Doorkeeper::Application.count }.by 1 + + application = Doorkeeper::Application.find_by(name: 'application_name', redirect_uri: 'http://application.url') + + expect(response).to have_http_status 201 + expect(json_response).to be_a Hash + expect(json_response['application_id']).to eq application.uid + expect(json_response['secret']).to eq application.secret + expect(json_response['callback_url']).to eq application.redirect_uri + end + + it 'does not allow creating an application with the wrong redirect_uri format' do + expect do + post api('/applications', admin_user), name: 'application_name', redirect_uri: 'wrong_url_format', scopes: '' + end.not_to change { Doorkeeper::Application.count } + + expect(response).to have_http_status 400 + expect(json_response).to be_a Hash + expect(json_response['message']['redirect_uri'][0]).to eq('must be an absolute URI.') + end + + it 'does not allow creating an application without a name' do + expect do + post api('/applications', admin_user), redirect_uri: 'http://application.url', scopes: '' + end.not_to change { Doorkeeper::Application.count } + + expect(response).to have_http_status 400 + expect(json_response).to be_a Hash + expect(json_response['error']).to eq('name is missing') + end + + it 'does not allow creating an application without a redirect_uri' do + expect do + post api('/applications', admin_user), name: 'application_name', scopes: '' + end.not_to change { Doorkeeper::Application.count } + + expect(response).to have_http_status 400 + expect(json_response).to be_a Hash + expect(json_response['error']).to eq('redirect_uri is missing') + end + + it 'does not allow creating an application without scopes' do + expect do + post api('/applications', admin_user), name: 'application_name', redirect_uri: 'http://application.url' + end.not_to change { Doorkeeper::Application.count } + + expect(response).to have_http_status 400 + expect(json_response).to be_a Hash + expect(json_response['error']).to eq('scopes is missing') + end + end + + context 'authorized user without authorization' do + it 'does not create application' do + expect do + post api('/applications', user), name: 'application_name', redirect_uri: 'http://application.url', scopes: '' + end.not_to change { Doorkeeper::Application.count } + + expect(response).to have_http_status 403 + end + end + + context 'non-authenticated user' do + it 'does not create application' do + expect do + post api('/applications'), name: 'application_name', redirect_uri: 'http://application.url' + end.not_to change { Doorkeeper::Application.count } + + expect(response).to have_http_status 401 + end + end + end +end |