diff --git a/app/assets/javascripts/groups/components/group_item.vue b/app/assets/javascripts/groups/components/group_item.vue index 830385941d8..ede74d18ed4 100644 --- a/app/assets/javascripts/groups/components/group_item.vue +++ b/app/assets/javascripts/groups/components/group_item.vue @@ -104,11 +104,11 @@ export default { />
- - + +
diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb index 1baaa33c819..71a65dc6713 100644 --- a/app/graphql/types/merge_request_type.rb +++ b/app/graphql/types/merge_request_type.rb @@ -55,12 +55,27 @@ module Types field :web_url, GraphQL::STRING_TYPE, null: true # rubocop:disable Graphql/Descriptions field :upvotes, GraphQL::INT_TYPE, null: false # rubocop:disable Graphql/Descriptions field :downvotes, GraphQL::INT_TYPE, null: false # rubocop:disable Graphql/Descriptions - field :subscribed, GraphQL::BOOLEAN_TYPE, method: :subscribed?, null: false # rubocop:disable Graphql/Descriptions field :head_pipeline, Types::Ci::PipelineType, null: true, method: :actual_head_pipeline # rubocop:disable Graphql/Descriptions field :pipelines, Types::Ci::PipelineType.connection_type, # rubocop:disable Graphql/Descriptions resolver: Resolvers::MergeRequestPipelinesResolver + field :milestone, Types::MilestoneType, description: 'The milestone this merge request is linked to', + null: true, + resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Milestone, obj.milestone_id).find } + field :assignees, Types::UserType.connection_type, null: true, complexity: 5, description: 'The list of assignees for the merge request' + field :participants, Types::UserType.connection_type, null: true, complexity: 5, description: 'The list of participants on the merge request' + field :subscribed, GraphQL::BOOLEAN_TYPE, method: :subscribed?, null: false, complexity: 5, + description: 'Boolean flag for whether the currently logged in user is subscribed to this MR' + field :labels, Types::LabelType.connection_type, null: true, complexity: 5, description: 'The list of labels on the merge request' + field :discussion_locked, GraphQL::BOOLEAN_TYPE, description: 'Boolean flag determining if comments on the merge request are locked to members only', + null: false, + resolve: -> (obj, _args, _ctx) { !!obj.discussion_locked } + field :time_estimate, GraphQL::INT_TYPE, null: false, description: 'The time estimate for the merge request' + field :total_time_spent, GraphQL::INT_TYPE, null: false, description: 'Total time reported as spent on the merge request' + field :reference, GraphQL::STRING_TYPE, null: false, method: :to_reference, description: 'Internal merge request reference. Returned in shortened format by default' do + argument :full, GraphQL::BOOLEAN_TYPE, required: false, default_value: false, description: 'Boolean option specifying whether the reference should be returned in full' + end field :task_completion_status, Types::TaskCompletionStatus, null: false # rubocop:disable Graphql/Descriptions end end diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index 115d1031a8a..df17b82412f 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -291,7 +291,8 @@ module ApplicationSettingsHelper :snowplow_enabled, :snowplow_site_id, :push_event_hooks_limit, - :push_event_activities_limit + :push_event_activities_limit, + :custom_http_clone_url_root ] end diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb index b341cf04403..0c0ffb67c9a 100644 --- a/app/models/application_setting_implementation.rb +++ b/app/models/application_setting_implementation.rb @@ -128,7 +128,8 @@ module ApplicationSettingImplementation snowplow_collector_hostname: nil, snowplow_cookie_domain: nil, snowplow_enabled: false, - snowplow_site_id: nil + snowplow_site_id: nil, + custom_http_clone_url_root: nil } end diff --git a/app/models/project.rb b/app/models/project.rb index f1e232e95f8..3525f37f8d5 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1036,8 +1036,8 @@ class Project < ApplicationRecord end end - def web_url - Gitlab::Routing.url_helpers.project_url(self) + def web_url(only_path: nil) + Gitlab::Routing.url_helpers.project_url(self, only_path: only_path) end def readme_url @@ -1316,7 +1316,18 @@ class Project < ApplicationRecord end def http_url_to_repo - "#{web_url}.git" + custom_root = Gitlab::CurrentSettings.custom_http_clone_url_root + + project_url = if custom_root.present? + Gitlab::Utils.append_path( + custom_root, + web_url(only_path: true) + ) + else + web_url + end + + "#{project_url}.git" end # Is overridden in EE diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb index 218be974218..bb222ac7629 100644 --- a/app/models/project_wiki.rb +++ b/app/models/project_wiki.rb @@ -54,7 +54,7 @@ class ProjectWiki end def http_url_to_repo - "#{Gitlab.config.gitlab.url}/#{full_path}.git" + @project.http_url_to_repo.sub(%r{git\z}, 'wiki.git') end def wiki_base_path diff --git a/app/views/admin/application_settings/_visibility_and_access.html.haml b/app/views/admin/application_settings/_visibility_and_access.html.haml index e57ef1ea18f..be5f1f4f9a8 100644 --- a/app/views/admin/application_settings/_visibility_and_access.html.haml +++ b/app/views/admin/application_settings/_visibility_and_access.html.haml @@ -53,6 +53,11 @@ = select(:application_setting, :enabled_git_access_protocol, [['Both SSH and HTTP(S)', nil], ['Only SSH', 'ssh'], ['Only HTTP(S)', 'http']], {}, class: 'form-control') %span.form-text.text-muted#clone-protocol-help = _('Allow only the selected protocols to be used for Git access.') + .form-group + = f.label :custom_http_clone_url_root, _('Custom Git clone URL for HTTP(S)'), class: 'label-bold' + = f.text_field :custom_http_clone_url_root, class: 'form-control', placeholder: 'https://git.example.com', :'aria-describedby' => 'custom_http_clone_url_root_help_block' + %span.form-text.text-muted#custom_http_clone_url_root_help_block + = _('Replaces the clone URL root.') - ApplicationSetting::SUPPORTED_KEY_TYPES.each do |type| - field_name = :"#{type}_key_restriction" diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml index 094cbf755e1..4eec81c9125 100644 --- a/app/views/projects/pipelines/_info.html.haml +++ b/app/views/projects/pipelines/_info.html.haml @@ -59,3 +59,9 @@ %span.js-details-content.hide = link_to @pipeline.sha, project_commit_path(@project, @pipeline.sha), class: "commit-sha commit-hash-full" = clipboard_button(text: @pipeline.sha, title: _("Copy commit SHA")) + + .well-segment.related-merge-request-info + .icon-container + = sprite_icon("git-merge") + %span.related-merge-requests + = @pipeline.all_related_merge_request_text diff --git a/changelogs/unreleased/13360-fix-epics-api.yml b/changelogs/unreleased/13360-fix-epics-api.yml new file mode 100644 index 00000000000..ab89f2a8b5c --- /dev/null +++ b/changelogs/unreleased/13360-fix-epics-api.yml @@ -0,0 +1,5 @@ +--- +title: Fix creating epics with dates from api +merge_request: 18393 +author: +type: fixed diff --git a/changelogs/unreleased/20829-extend-mr-attributes-returned-by-graphql.yml b/changelogs/unreleased/20829-extend-mr-attributes-returned-by-graphql.yml new file mode 100644 index 00000000000..86e34bcdab1 --- /dev/null +++ b/changelogs/unreleased/20829-extend-mr-attributes-returned-by-graphql.yml @@ -0,0 +1,5 @@ +--- +title: Extend graphql query endpoint for merge requests to return more attributes to support sidebar implementation +merge_request: 17813 +author: +type: other diff --git a/changelogs/unreleased/dz-improve-groups-list-ui.yml b/changelogs/unreleased/dz-improve-groups-list-ui.yml new file mode 100644 index 00000000000..36460eb911a --- /dev/null +++ b/changelogs/unreleased/dz-improve-groups-list-ui.yml @@ -0,0 +1,5 @@ +--- +title: Increase group avatar size to 40px +merge_request: 18654 +author: +type: changed diff --git a/changelogs/unreleased/geo-mk-add-custom-http-clone-url-root.yml b/changelogs/unreleased/geo-mk-add-custom-http-clone-url-root.yml new file mode 100644 index 00000000000..5bd7cc1761d --- /dev/null +++ b/changelogs/unreleased/geo-mk-add-custom-http-clone-url-root.yml @@ -0,0 +1,5 @@ +--- +title: Add "Custom HTTP Git clone URL root" setting +merge_request: 18422 +author: +type: added diff --git a/changelogs/unreleased/mfluharty-add-mr-links-to-pipeline-view.yml b/changelogs/unreleased/mfluharty-add-mr-links-to-pipeline-view.yml new file mode 100644 index 00000000000..e3bb00bc5bd --- /dev/null +++ b/changelogs/unreleased/mfluharty-add-mr-links-to-pipeline-view.yml @@ -0,0 +1,5 @@ +--- +title: Show related merge requests in pipeline view +merge_request: 18697 +author: +type: added diff --git a/db/migrate/20191009222222_add_custom_http_clone_url_root_to_application_settings.rb b/db/migrate/20191009222222_add_custom_http_clone_url_root_to_application_settings.rb new file mode 100644 index 00000000000..0fa8ff449f7 --- /dev/null +++ b/db/migrate/20191009222222_add_custom_http_clone_url_root_to_application_settings.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class AddCustomHttpCloneUrlRootToApplicationSettings < ActiveRecord::Migration[5.2] + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + def change + add_column :application_settings, :custom_http_clone_url_root, :string, limit: 511 + end +end diff --git a/db/schema.rb b/db/schema.rb index 8bb2329a0b9..e43eaef911d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -340,6 +340,7 @@ ActiveRecord::Schema.define(version: 2019_10_16_220135) do t.integer "throttle_incident_management_notification_per_period", default: 3600 t.integer "push_event_hooks_limit", default: 3, null: false t.integer "push_event_activities_limit", default: 3, null: false + t.string "custom_http_clone_url_root", limit: 511 t.index ["custom_project_templates_group_id"], name: "index_application_settings_on_custom_project_templates_group_id" t.index ["file_template_project_id"], name: "index_application_settings_on_file_template_project_id" t.index ["instance_administration_project_id"], name: "index_applicationsettings_on_instance_administration_project_id" diff --git a/doc/administration/geo/replication/location_aware_git_url.md b/doc/administration/geo/replication/location_aware_git_url.md index b8ed3d07bdd..bb47031a544 100644 --- a/doc/administration/geo/replication/location_aware_git_url.md +++ b/doc/administration/geo/replication/location_aware_git_url.md @@ -95,13 +95,12 @@ on the external URL of the current host. For example: ![Clone panel](img/single_git_clone_panel.png) -However, you can customize the SSH remote URL to use the location-aware +You can customize the SSH remote URL to use the location-aware `git.example.com`. To do so, change the SSH remote URL's host by setting `gitlab_rails['gitlab_ssh_host']` in `gitlab.rb` of web nodes. -Unfortunately the means to specify a custom HTTP clone URL is not yet -implemented. The feature request can be found at -[Customizable Git HTTP clone root URL](https://gitlab.com/gitlab-org/gitlab/issues/31949). +You can customize the HTTP remote URL as shown in +[Custom Git clone URL for HTTP(S)](../../../user/admin_area/settings/visibility_and_access_controls.md#custom-git-clone-url-for-https). ## Example Git request handling behavior diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index d60916851a8..839289cf677 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -435,8 +435,13 @@ The API can be explored interactively using the [GraphiQL IDE](../index.md#graph | `webUrl` | String | | | `upvotes` | Int! | | | `downvotes` | Int! | | -| `subscribed` | Boolean! | | | `headPipeline` | Pipeline | | +| `milestone` | Milestone | The milestone this merge request is linked to | +| `subscribed` | Boolean! | Boolean flag for whether the currently logged in user is subscribed to this MR | +| `discussionLocked` | Boolean! | Boolean flag determining if comments on the merge request are locked to members only | +| `timeEstimate` | Int! | The time estimate for the merge request | +| `totalTimeSpent` | Int! | Total time reported as spent on the merge request | +| `reference` | String! | Internal merge request reference. Returned in shortened format by default | | `taskCompletionStatus` | TaskCompletionStatus! | | ### MergeRequestPermissions diff --git a/doc/user/admin_area/settings/img/clone_panel.png b/doc/user/admin_area/settings/img/clone_panel.png new file mode 100644 index 00000000000..8aa0bd2f7d8 Binary files /dev/null and b/doc/user/admin_area/settings/img/clone_panel.png differ diff --git a/doc/user/admin_area/settings/img/custom_git_clone_url_for_https.png b/doc/user/admin_area/settings/img/custom_git_clone_url_for_https.png new file mode 100644 index 00000000000..22cdd15cc0c Binary files /dev/null and b/doc/user/admin_area/settings/img/custom_git_clone_url_for_https.png differ diff --git a/doc/user/admin_area/settings/visibility_and_access_controls.md b/doc/user/admin_area/settings/visibility_and_access_controls.md index 81e09e4bb8a..63d3dd9ffe3 100644 --- a/doc/user/admin_area/settings/visibility_and_access_controls.md +++ b/doc/user/admin_area/settings/visibility_and_access_controls.md @@ -135,6 +135,30 @@ Starting with [GitLab 10.7](https://gitlab.com/gitlab-org/gitlab-ce/merge_reques HTTP(S) protocol will be allowed for Git clone or fetch requests done by GitLab Runner from CI/CD jobs, even if _Only SSH_ was selected. +## Custom Git clone URL for HTTP(S) + +You can customize project Git clone URLs for HTTP(S). This will affect the clone +panel: + +![Clone panel](img/clone_panel.png) + +For example, if your +GitLab instance is at `https://example.com`, then project clone URLs look like +`https://example.com/foo/bar.git`. If you'd to provide clone URLs that look like +`https://git.example.com/gitlab/foo/bar.git` instead, then you can set this +setting to `https://git.example.com/gitlab/`. + +![Custom Git clone URL for HTTP](img/custom_git_clone_url_for_https.png) + +To specify a custom Git clone URL for HTTP(S): + +1. Enter a root URL for **Custom Git clone URL for HTTP(S)**. +1. Click on **Save changes**. + +NOTE: **Note:** +SSH clone URLs can be customized in `gitlab.rb` by setting +`gitlab_rails["gitlab_ssh_host"]` and other related settings. + ## RSA, DSA, ECDSA, ED25519 SSH keys These options specify the permitted types and lengths for SSH keys. diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 8e381b31e9f..d760d993762 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -4828,6 +4828,9 @@ msgstr "" msgid "Custom CI configuration path" msgstr "" +msgid "Custom Git clone URL for HTTP(S)" +msgstr "" + msgid "Custom hostname (for private commit emails)" msgstr "" @@ -13738,6 +13741,9 @@ msgstr "" msgid "Replaced all labels with %{label_references} %{label_text}." msgstr "" +msgid "Replaces the clone URL root." +msgstr "" + msgid "Reply by email" msgstr "" diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb index 9759fd04ad2..04adb1ec6af 100644 --- a/spec/features/projects/pipelines/pipeline_spec.rb +++ b/spec/features/projects/pipelines/pipeline_spec.rb @@ -98,6 +98,16 @@ describe 'Pipeline', :js do end end + it 'shows links to the related merge requests' do + visit_pipeline + + within '.related-merge-request-info' do + pipeline.all_merge_requests.map do |merge_request| + expect(page).to have_link(project_merge_request_path(project, merge_request)) + end + end + end + it_behaves_like 'showing user status' do let(:user_with_status) { pipeline.user } diff --git a/spec/graphql/types/merge_request_type_spec.rb b/spec/graphql/types/merge_request_type_spec.rb index 6904c2dcd07..04e9bb6861f 100644 --- a/spec/graphql/types/merge_request_type_spec.rb +++ b/spec/graphql/types/merge_request_type_spec.rb @@ -20,7 +20,9 @@ describe GitlabSchema.types['MergeRequest'] do merge_error allow_collaboration should_be_rebased rebase_commit_sha rebase_in_progress merge_commit_message default_merge_commit_message merge_ongoing source_branch_exists mergeable_discussions_state web_url - upvotes downvotes subscribed head_pipeline pipelines task_completion_status + upvotes downvotes head_pipeline pipelines task_completion_status + milestone assignees participants subscribed labels discussion_locked time_estimate + total_time_spent reference ] is_expected.to have_graphql_fields(*expected_fields) diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb index f632b904e78..cfd0f8ec7a7 100644 --- a/spec/graphql/types/project_type_spec.rb +++ b/spec/graphql/types/project_type_spec.rb @@ -45,4 +45,22 @@ describe GitlabSchema.types['Project'] do is_expected.to have_graphql_resolver(Resolvers::IssuesResolver) end end + + describe 'merge_requests field' do + subject { described_class.fields['mergeRequest'] } + + it 'returns merge requests' do + is_expected.to have_graphql_type(Types::MergeRequestType) + is_expected.to have_graphql_resolver(Resolvers::MergeRequestsResolver.single) + end + end + + describe 'merge_request field' do + subject { described_class.fields['mergeRequests'] } + + it 'returns merge request' do + is_expected.to have_graphql_type(Types::MergeRequestType.connection_type) + is_expected.to have_graphql_resolver(Resolvers::MergeRequestsResolver) + end + end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 866003c9ffd..a5048d78363 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -631,8 +631,38 @@ describe Project do describe "#web_url" do let(:project) { create(:project, path: "somewhere") } - it 'returns the full web URL for this repo' do - expect(project.web_url).to eq("#{Gitlab.config.gitlab.url}/#{project.namespace.full_path}/somewhere") + context 'when given the only_path option' do + subject { project.web_url(only_path: only_path) } + + context 'when only_path is false' do + let(:only_path) { false } + + it 'returns the full web URL for this repo' do + expect(subject).to eq("#{Gitlab.config.gitlab.url}/#{project.namespace.full_path}/somewhere") + end + end + + context 'when only_path is true' do + let(:only_path) { true } + + it 'returns the relative web URL for this repo' do + expect(subject).to eq("/#{project.namespace.full_path}/somewhere") + end + end + + context 'when only_path is nil' do + let(:only_path) { nil } + + it 'returns the full web URL for this repo' do + expect(subject).to eq("#{Gitlab.config.gitlab.url}/#{project.namespace.full_path}/somewhere") + end + end + end + + context 'when not given the only_path option' do + it 'returns the full web URL for this repo' do + expect(project.web_url).to eq("#{Gitlab.config.gitlab.url}/#{project.namespace.full_path}/somewhere") + end end end @@ -3226,20 +3256,78 @@ describe Project do describe '#http_url_to_repo' do let(:project) { create(:project) } - it 'returns the url to the repo without a username' do - expect(project.http_url_to_repo).to eq("#{project.web_url}.git") - expect(project.http_url_to_repo).not_to include('@') + context 'when a custom HTTP clone URL root is not set' do + it 'returns the url to the repo without a username' do + expect(project.http_url_to_repo).to eq("#{project.web_url}.git") + expect(project.http_url_to_repo).not_to include('@') + end + end + + context 'when a custom HTTP clone URL root is set' do + before do + stub_application_setting(custom_http_clone_url_root: custom_http_clone_url_root) + end + + context 'when custom HTTP clone URL root has a relative URL root' do + context 'when custom HTTP clone URL root ends with a slash' do + let(:custom_http_clone_url_root) { 'https://git.example.com:51234/mygitlab/' } + + it 'returns the url to the repo, with the root replaced with the custom one' do + expect(project.http_url_to_repo).to eq("https://git.example.com:51234/mygitlab/#{project.full_path}.git") + end + end + + context 'when custom HTTP clone URL root does not end with a slash' do + let(:custom_http_clone_url_root) { 'https://git.example.com:51234/mygitlab' } + + it 'returns the url to the repo, with the root replaced with the custom one' do + expect(project.http_url_to_repo).to eq("https://git.example.com:51234/mygitlab/#{project.full_path}.git") + end + end + end + + context 'when custom HTTP clone URL root does not have a relative URL root' do + context 'when custom HTTP clone URL root ends with a slash' do + let(:custom_http_clone_url_root) { 'https://git.example.com:51234/' } + + it 'returns the url to the repo, with the root replaced with the custom one' do + expect(project.http_url_to_repo).to eq("https://git.example.com:51234/#{project.full_path}.git") + end + end + + context 'when custom HTTP clone URL root does not end with a slash' do + let(:custom_http_clone_url_root) { 'https://git.example.com:51234' } + + it 'returns the url to the repo, with the root replaced with the custom one' do + expect(project.http_url_to_repo).to eq("https://git.example.com:51234/#{project.full_path}.git") + end + end + end end end describe '#lfs_http_url_to_repo' do let(:project) { create(:project) } - it 'returns the url to the repo without a username' do - lfs_http_url_to_repo = project.lfs_http_url_to_repo('operation_that_doesnt_matter') + context 'when a custom HTTP clone URL root is not set' do + it 'returns the url to the repo without a username' do + lfs_http_url_to_repo = project.lfs_http_url_to_repo('operation_that_doesnt_matter') - expect(lfs_http_url_to_repo).to eq("#{project.web_url}.git") - expect(lfs_http_url_to_repo).not_to include('@') + expect(lfs_http_url_to_repo).to eq("#{project.web_url}.git") + expect(lfs_http_url_to_repo).not_to include('@') + end + end + + context 'when a custom HTTP clone URL root is set' do + before do + stub_application_setting(custom_http_clone_url_root: 'https://git.example.com:51234') + end + + it 'returns the url to the repo, with the root replaced with the custom one' do + lfs_http_url_to_repo = project.lfs_http_url_to_repo('operation_that_doesnt_matter') + + expect(lfs_http_url_to_repo).to eq("https://git.example.com:51234/#{project.full_path}.git") + end end end diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb index d12dd97bb9e..b089544c810 100644 --- a/spec/models/project_wiki_spec.rb +++ b/spec/models/project_wiki_spec.rb @@ -47,11 +47,25 @@ describe ProjectWiki do describe "#http_url_to_repo" do let(:project) { create :project } - it 'returns the full http url to the repo' do - expected_url = "#{Gitlab.config.gitlab.url}/#{subject.full_path}.git" + context 'when a custom HTTP clone URL root is not set' do + it 'returns the full http url to the repo' do + expected_url = "#{Gitlab.config.gitlab.url}/#{subject.full_path}.git" - expect(project_wiki.http_url_to_repo).to eq(expected_url) - expect(project_wiki.http_url_to_repo).not_to include('@') + expect(project_wiki.http_url_to_repo).to eq(expected_url) + expect(project_wiki.http_url_to_repo).not_to include('@') + end + end + + context 'when a custom HTTP clone URL root is set' do + before do + stub_application_setting(custom_http_clone_url_root: 'https://git.example.com:51234') + end + + it 'returns the full http url to the repo, with the root replaced with the custom one' do + expected_url = "https://git.example.com:51234/#{subject.full_path}.git" + + expect(project_wiki.http_url_to_repo).to eq(expected_url) + end end end