From da35510cdad8f8d3cb6c119682dc2735531983cd Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 17 Oct 2019 21:06:41 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../groups/components/group_item.vue | 6 +- app/graphql/types/merge_request_type.rb | 17 ++- app/helpers/application_settings_helper.rb | 3 +- .../application_setting_implementation.rb | 3 +- app/models/project.rb | 17 ++- app/models/project_wiki.rb | 2 +- .../_visibility_and_access.html.haml | 5 + app/views/projects/pipelines/_info.html.haml | 6 + changelogs/unreleased/13360-fix-epics-api.yml | 5 + ...tend-mr-attributes-returned-by-graphql.yml | 5 + .../unreleased/dz-improve-groups-list-ui.yml | 5 + .../geo-mk-add-custom-http-clone-url-root.yml | 5 + ...fluharty-add-mr-links-to-pipeline-view.yml | 5 + ..._clone_url_root_to_application_settings.rb | 13 +++ db/schema.rb | 1 + .../geo/replication/location_aware_git_url.md | 7 +- doc/api/graphql/reference/index.md | 7 +- .../admin_area/settings/img/clone_panel.png | Bin 0 -> 20007 bytes .../img/custom_git_clone_url_for_https.png | Bin 0 -> 11171 bytes .../visibility_and_access_controls.md | 24 ++++ locale/gitlab.pot | 6 + .../projects/pipelines/pipeline_spec.rb | 10 ++ spec/graphql/types/merge_request_type_spec.rb | 4 +- spec/graphql/types/project_type_spec.rb | 18 +++ spec/models/project_spec.rb | 106 ++++++++++++++++-- spec/models/project_wiki_spec.rb | 22 +++- 26 files changed, 273 insertions(+), 29 deletions(-) create mode 100644 changelogs/unreleased/13360-fix-epics-api.yml create mode 100644 changelogs/unreleased/20829-extend-mr-attributes-returned-by-graphql.yml create mode 100644 changelogs/unreleased/dz-improve-groups-list-ui.yml create mode 100644 changelogs/unreleased/geo-mk-add-custom-http-clone-url-root.yml create mode 100644 changelogs/unreleased/mfluharty-add-mr-links-to-pipeline-view.yml create mode 100644 db/migrate/20191009222222_add_custom_http_clone_url_root_to_application_settings.rb create mode 100644 doc/user/admin_area/settings/img/clone_panel.png create mode 100644 doc/user/admin_area/settings/img/custom_git_clone_url_for_https.png 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 0000000000000000000000000000000000000000..8aa0bd2f7d8869fd14739e0d66a47b2b27b1d02b GIT binary patch literal 20007 zcmcG0Wl$YmwE7LYtw+|*Cskz`j2A>NU|?V{e|uGUoQ?&Q_-CGU6~WywTC`^({%>;|e#sSWrG8rrSl>FWL&c!L&9Rf%sm-IwBQ27Seky zjef^oF>RQqWWD$8&t`#1rrT0prQ?zDW3FANW$2k*cj;()u5iea@rOG`Zkp{?wH}3U z3AxhuC~G!$zI7zO7g93gUy2~bEiPx2m%KcZ{P1G|`CD6VQCaP03cu|To-?L3=7`a^ z+d~?iv){iZ_GWdu_tbOio}LOIbbM;k%Q)$GzO&QFIo;me<<1!P_L986ZSj)5^mWeV zJJ|90a~6`qjC^05niTxHCpf}CUs~*Pd+o7uQ63wf(3)sd&YvZr5L1tJUEi+c&!_@A z{@W>X1-!l6K=r&Q$?8bwfN~wA?`l!eD)Y>ZyL&fky-<9gT%S;$?}7ep7}cnVZ_?Pp zTVE(?!@o3pBZ{1%|WN+g4c|$z2TlF0IdqbXUCbya?(oc2aD9$Wu_+z9z$S?&4WhGv+v@Lq{W%-zvTP(8i((`{}L{?p*2d{rh)2j!E>Snhr|5ZDKf4)DmV< zNj&ea8nn*xDU($;)vd5K1T=8IR!)gwg;$rj=h?yC-IM#TZMC@Oe93V#K56^VsuDj%F+zJrog)xL zU|K!VO@V19K;>3?+xeiNd!bai#)XJz&9@L=)aWN~n|U}fjy<6~vxVCCRo23jz? zc-gxedotU*Q2jH=e~cq(?qcd}<>+eVU=Mm8*Vx3t%~gn!@_C|v|M}-WU9G_XIg`E1 zza9&CK-TAfVP$7wWBvEoKv%)%tNiNbE)KSC&%?jBw{jKc5d7QtzhC~(+5YJz;b7$?H^KSlL1zwKu3qjU@zi8nXSW*m$FfgJpa*|^2JYf&AQCl>- ziTiVFmM+!juf*@+?TmSH2`Ks-xb@c3}<%rJ$3}tFhzZu~ih49gJ`^Q9lrp!Zm11BeHw4MKCc0KX5 zF^gIp-_w!Gt%20|^&=8}HmgL(0u=R6A=&2upecgb?;{+_+@J4EFyYihS&=v^7W}!4 zum{f<9NG~t-F!H}kT-MH7O5W^oe?oCgr2U~v2M0={WmrS6Q|{3*~2>cwzEuk1w9TI zjW_Q*0^v`uR{V$CpB{-dJuz^JQ9`Vd&vD^ki3!avK(Ee7zW$BuX8sJr^Mdi@a zqOX5az&CjkL&CS_d?74l8rCz($?i>sqxaKN(Q-s47Af5&o8{Gk_$R))-M5R2Vb;j@ zWnO2O`tlHp0P``EW8Lm6wMYkH!jt(E5uPzV~X!Ug2s*# z+g>2nJi8M|?UN`q$h!oVg=&Kkmtd!_WRfL3>ur%9d2pWa*Byfx=tf`GITWP(L z-rIf_zVL7F-{A(HM~P0wpXDqyn~&G83{{%;Pq?%e-BgXH3+YWio+nn?#6E7|76{_y}LTP zYLq$MNHz3}A1{=V;TS})s%%hxK-5bz*BS%kyFbQ{U0TuQ*A3sU;D6m)L#!IEAagUb zf9m7d`zn3v({Fu}U~qo_p5w9d&KV@<}KH zkqcSg0&crzc&v>_g|WgM3s1t=zv1aWG*na^>^OmzUeL^^R*1DQuSe z8xPZZ4#{$fwCV0qv`Bm*vKy z%P6;5IM{cmIF;y$2NPW0cKglIp#~KpS}r)f(@mKV8xKByBF4}pqzUa@k}8-pm>Wyu zHM8!!$E)_I!Oc`d`vP)4=c2D#yM$HEt6{{Dyg$mBorgQ6xhXPaUXnjsFLmp+s&WdA z@|M&~A;;KA^hhQ)%K@I_346c3Z+j0EOGV;^4cHOLW(T%7th7llNUxVDy`)1h=-n~= zN{^o`p!H%4%5WD;lA3eG%BiDMxueo}aB*aIUwDBocDH@Bn5ARBSePpJ^~^b<&}{g` z|0$7>(POApA~hp#FN`a73Hj0eaD+CU6C5W2t=`OKo~M22|H=MHe$gVyQm)!z`Nwj1 z<*`MJA46v?SMi;GY10<^ODb%a)aFBu9tih5=ZxkMUlFn5SdMO;ExiIMbF)G!OP4Et zGh_B)5E@Gyu%icEMujXcjSE9v59WVeV-c{4TJy~lWf$nyT7z#R!lE6YdOqPOwOAI! z$9XJ!$zYQ5o7GS~or6F_MxWB5_akptWCP(45lp1tc!y(>`qbMmHjM9wRhS)|MFipO z4~1rlftTiMPw%ToGgiPB;9z3EK?=*MU}P+wNiTsBi#UV5qEC5^)za*4yYEb2nX0;? zT??O&NlYwO2A5K^C(>SE-b{Z{lgRO0Lv1`AVpLin<^)H1jfJzdNC(2EmST+;q?@;1 z&f6p~>+wv+pmy(%Ezvp_Mo$_9&3!?|N3}Azn~JjRhDw|y-2H0&`FPA_b+Z`cdH4bS zNJnM}7edi|`NZrpy`O^fl#_&4`*}cLX*j(iokW~vXSQNSzzOnT6>Bz}p!Ty|WAXZ; zF~7i+g-->OH@I`^wZP`sIL<`gnwNT^qVPKTpYlJAbLN{JU}xVYjgwi&53B@^#T!E@ zbNs~v?7z4zxXkhD#-i8_8w85=aesVhtctA(&L69@)0;#M#~`L?`~?k<$r4(sl50=IoE;=B;&<;k`FjV1lKonV=2eyXFrJu(bT!(=EZ2?Llq*0P5(%5xmxy?){u0O{-OXTmk7t?=6tdELnlNB-(&Q+R*ZpALe4di_ zMTIe&5%_A$idZl>G8qFJRponkxs_0hFciK6$Mi?#u^J_bJcLVK?{jwMh?()P6nJBc z&7gwDjXxq!xBKH|d$@_69#ctmm}+fr*R^o4GCa><&qi*d=!l1ILRDI^zDSjsV6sq3 zwB7d3)~5>Se$3rt7=mYJjax9p`XNNl|8dcZn6iA?{WDr0>#}`B#$GUHXK77Q)0QHv zUM`Q;wMwg1z`!r)XTJvZc0b>04bu$M_7YQOgBISr*4HEOWraYvl;&)jqowFSFe8e&jD47QSAKH*>WhI|%V#$cKt1<0P|qZ^ z6Zc^3k-#T?i(J@8n~2NucO)j|cZ1d@qphkg{!kk5USA9Wm?G`TREBTy{-dSgpj`A@LQCs&r?(a9|F5hFYM?DRJ!2I?%Y}9kEJkaSgZ9`ywUkGPxr{M5X&m zGLkU(?M}X2xo+){;zMP(ZY9R+HE?A}vI9T2yZNCUhM1m&hD3KQ>B-$u`&$Aw9LxEd zucLTFQ@G5Z1x0Q)5_9Iv-E!|%KJGD_&Yz$sNs~};;|<=|N<|RnGdCdO1Z1|r3Hd=W z!^`yQUsN;J94Fx+!G&;RDNK>b5l=#vTfIk^BG{5(HC~HAywXH1hkth{C*Pc@SjB}% zHKaD|N1Y_u?B2&3wn#L+Wp>!_KO`{%6SzB&&Nb{+EFPb}wP!W$8_3;Dx{U2l{`G?J z?qrn>Y4hEWf%L$`s8pjQjPScVEy~_e#~OKLMB?4lTb|w3D}74h6>EA2_p$@> zsk8Z4=LHvc=xQ=SX@j!wETLY??QhedpGMM!hL~I{j`%(t5SGDUq&g;OrwsA!72ga!&8*lGwK%_JD-)6qGYVY$unx8PZ{vO> z_}JGayG(G#&`xQuxgA7qcOp<6x!(5zwb}gfC7q)6aXUO`8~%_q67ePDRCC%OE4b(+ zMDd!f7*c^S#h}zjqss#Q00DS1JBH$r`vlBj2JaKSdV6MFA?hOwCL8?~=h>2O=tM?h%b2^0WG;L~|a6{U(?$ zF>YkR;{I-g^TD?ypG&Vk?(aO zP7JHok*M2$7_Oh}#utmjLAlw|!be1nzc-***|trTbU}Mx8IrG7R5x~0=di+3tdM$1 zt+oFNW&ULsjA*|j>l?yY>c`54G?JrJ^KB&WqyvdJg07BNOCEFsuf0koB!u^4HwQWV zjfWt3-+NFv(;asO-=(Qq#kk{DuaQiw?{x6WptZ1|XEsOUa2!pR#u9$1AiYC#TWI(9 zuO`J1L5b-k%ECsf0(j2nUrbKH`yfvDN04QnU|iRlV?#CLt+@I;Oa=#5{%q?_(dd5o zZEa)^`dUqNuKy#U1C=e78!Lf{hK1TxhWj71w1@qlWZs6n{h|kXPe1&9>)Wq7!xQL_ z=&Pef*`pGjp#@7L7YaiecE}rO7M;vV*lmZQ+e7_lbs;Y@I&u#@XQo)Pio9_o-SIH# zE}vLDmgeU0yA!p{&$F9VCaC$E(b9;v2c@82<2^hw0{M#t6nTT|1X^bJifl&+El21p zEqeo^kOr5pWM$O{=eDB75IGSM$Rkwgv9R{pCeQ1?<6udSVuB!K(>ZqQDiC&60a;9E zWkul$`B}$7J_ZPp6{0J%%mj8o)RNwtL_CG#<}@0q&uPKi)Pja67hPzq=tKlc^VX9F zu<2^~?B9E6;kJ9gb=)(5S$~loLKme4-b{--;PdMcd7%BOHKZZdh(0pMS(7&U`CVcAzSx{c)W=m;H!{%kr0A!4orm~OWj6fO-Xq;} z=^d0L3wM@u3|44?ss|vIMMCo*;&ZQ@Ps>F}gM`!TL}mLX(y&?{YYkc6xPkl8$vrWs z3ElT+U%EC(n1-c!m57i84(LGgOx~N|z;c0i7oYNDM2lCmv2D*Zf-pl=ZJWZakthB6 z=oRX6@?&pYECyNi{1OvOJtTU;Uo}`}EFsv{52rlz?^!d%bhS>WZz48u#DJ7 zfKhJpnA-U1pZa=8=memmZoN$FsV@}IiX6bz=0IxRi>==ETvij3Rp1c@g%l?C8xkJd zw2h%;1^bp0X&1ZUkcF;Lv~C78x8w6km0>`c*+Coi7-;f1V$pAMWw&1qA$)6}b$+yB z__O(te`lsVmzCvo>W2=eaaMg z(~5(+KF0*5N}05+A1^opL6N{@!&!VTW;+wb6F>C@(s`WJlRG))Mz6Qs9VUuAUUzRh zs9YYcIIQhXeNAM2L?-u9{n@bgLI)7eoFoeAgcM(=5rIaL$vj1Q>=)kv2$0A4jS2vy z%7XgLY1v6_@7aO*~0 zCoeZ(1m3;phauw4V~KWj0HRB^=adc-n~&YKWXHiuv$>|YL_ht zhxeuJI*-TUhp+Fyr!X0`Xg}VcsnF{;)v!OYp@^m^stpvrhBmIeFzXdf#;jfx zvFJLf#1p|6B=>N8G176d4-noeeXq@A^=h!vS#@AHs%dw^a*mMqe(@kHnDI5RUn7JO zD8KPZ4Ud6YDNRCFFgHYdi10YS$M< z*tgO*ULb{5IJjo;?Hzrgub$m3=}TSaQ$Ug%+Wqx?gm0dq2px67o27Z5W8LBjWCMmn z2}YWC_^w&@qn4TTS_sT|JU}Bu+>UiYZl;)hNV$ha(Qq$38-T8WU=umBUOF> z*)((|;q5WXWXmh`yz6$Uu+^Nwo#@6*ygdMsyy1)4EY7rg-zsR%U3Z;60mEL#QW9`@J(cs>FcoLYhcE6XK?ax;B)j?ZVLnuA~;$@Uvs7pxT;NdjyX2H{Qm}f?j zeCGnjM+!lq7iG}|Y2%&eeB7naMeV%J?-ieoSPJTUxFi{2Pr{a&t1?$&i+BRWD4upQ zp^s_3wSWps9YF~=1hnPAEyd;f-44$Bvp0i|TFzp|CnVxI5tI!QDQAN|<%+@C5e~*v zg==%Zh)h`xk&~bQ82cRgh2dWQ%Ay$TC--069}|U_(vZ%~5%F&uSFRkdvz_CBWnj9; zAmyJ5Fm?uY{F5jDrPSS>2j|l2+Hc6 zH;1+YvG0L*znAV>qL?1l2=9F;bhWs?V7^Ak8$dPBHh&A)84h&>1*-)QrcjXG8U~$W zz@J|_K$9Y8U@l0xU)g9u$jzEN06vwVsN=M{psI*iEFt#YzCk4)EOKO1A6bL z{tRjm1H#(rtFB5BUJGbeUyOjywWDddIb+rg=F7DT9^+g%NHP4?_dLo+f{o<*X%hi_yLK#uswz={5g$a0*PBW zJo%@T=HE^oGSC*)2)c>uxm47X39>r^mvelnzhitBofqp6q!=(T$jMnxP8jG6MjlZ! zq_Z*_6Zv}&lraJ0SzgI>M{%nELLL#A2ckufG#EPoM6gdGBORe{{l z=OomtXSPj&vM#cyQR^A405F0V*<|YhtEHAX>k6&kFTNH5xJlrdMc~psHHcX+R^f{s z+HSy~H`6?b&qjoh;GQEL05V`C-_y z5Q~IGasj2mb1wVmRS-t-JQ;1g8I5dOy7~n`M}M^}ZXDnk_)# zE>_(CsXd;SoTeY}=j-#W2*FuG`CK;LgLasc^(NFt`a<5@#Ue?L8a-mS7b*Q;+gsI*2u|FDgX_voG*Ss6QNEgi>4RigM6Ac%4cOLMH&cTXd11E&sDa? zPQCpclgIKeW+iOb3^p6ZEM7HxV0}l2J||Hf_1FXPJ1(_n==x;6*lE3Q%j4I3Et*UB zGg+VE6lU}J8mmujZ&Ms*D~yA^=pg0oj~QP~RJDGavjeKPS-~OQFHDaD-~ zE;R#T%kL!Q3J99XMBLT`fR`xraFYkKaovNXB0HKR6gpE%dqD-iVLbCQ1OuMK^LHR} zky0j4j#tC=TI42Rr=;?`?G6AUkWe>*3-zPjLfv4S?_Ch0f{R~^+DntIKdgWW$9~b~ zxGL_?dgk~Gu&x+sd#K35 zn615iWk&ZAL##b-aMXe25wz8537{j@$K* z>eFwra<2?$ATGmpA73|Vku~H?etNZ452PjljeYFkouU$k`}OTT+I2iiWNq2gwZ~^J z+gUav-|KKCg&9M?o8tS2tCfjX@2i@3OggpBp^ywdi5Ncf>I7~UW&Y)PxflGBWRb6G zGtTlQ!WH4a0T3hIuMP0qCceFAETq@TD^i z$6*~_Wu!b_ug6Zbc)8$&2^J{PNxvDcFz$w|-xp~B7AGNxSp@5gnx7UqUVROTsemP? z=J$AGu|C?W-|Ws^Lb`Vam{O?QMtgWjfL*ZHbs!^X1hEYw-)@0U+p(+-3r+Ef%pjrO-hD(9JOA((9n)4;`F&b_N2vK+v-*0 zvOnEcfr&%jNuk&1YpD-$@r#pc=)%pu%0AzlQ8DmbMa03+2Aq4ElY(mif)oKI2G7PK zfgqDuFBX;xV5toeVLS4*(3yo}bn)2|uXT4UYYAuDZV{_EWf6 zjG&ROb0!QM)&625r&xA8b`gBv`|q0EW|F?5Z6Wnt3mEAqTz2zQ8>>Lrs4`FoxJdAk zUHuYxk&PzsEtqD($~Q1Li*3vpY+@Ann?lUsyG?ubU|9+TA|8yN4?rUn34lP>63;i!JlH!&o+lGkX|B{0k$&XD+{B~NOMQC>G6?Sx4W}D zJYO|i!&voF@_aGbT@&bu&g6sh1KCU@WKj@)mnWyN=<8lT0G{M2U@vDlUSd&h_J5b( z5$#E)0czRoh^VgSl`rNjatWhBVs>$5vx`fftx}*X>Ua ztOm0Hr_AI=MRJSegFjPXZ8dAt_P;x*C+4uokAqDQr8nr`qp6V?h`GR6GlJROXDa~?SE2I~h?jHh}UF|ghe+#Yu3$?`no{8D5 zIik{RZdRG0Ni5eCkO>H#SF=3FWykztNd>?q(LH?rk3N%LiyKID(jT=D=81@=0d~CR z=2^p69}xd~zcLA4;z|v)E z1QCh}ANdjF1eTZjg^Lgr=uGs6m8LxTm^C2| znpw0}XB}!L@|7*R;Bo1q<(*}kfeA93W3}B_SmE-b=bExln%8~r^e18}553w|D>}#* zkRM*#lF6j=cK7##hV{+GX}gAtUF9>|QFgtJ!AzAp`{g8~ek7-Am;oq;*;K@;^e_)T z`=y6G^XDqXYVW{OUp97a;}@B@l^8Q+Cu0?935A3pOBzL*amg{uANu3U;p`aNe3?Gi zHcdRr-W$I2D0=W^W0e(W>`T2o5wvnT_i{nUfCy4wKL}(;MPxVyMr?EqT|ZlLR|P;P z8QI*Qt6qY(x!43b9?9yoeylxVHE3yI{lH5XiO-q>kSN)eHeVcs(yT+S#mgClz+hg_ zU(H&M5VXfVgc`Jx+odKqPP7qx1J8UTm8(|}IL(gkHavV6bluvSSU1>^ibV%x9VFR_ z%dW|Ue5FX!hKG<|J1~ra0M`KBPBVM`_O;aa3VqDzIXlCc=@wdUL$$Ab8fnteM1vuy zI1tO&GBoc}fB0hf@rp5@mjc|S-A-8IG_Q~jaO`kAA|f!mD5>hi_Csf<(yR+_bnGHR zu~5zc@r7$4*7Eo4>f%j+W2MakrnJHSyYW|4tLa1HKC{&0ewE(~1{;d~9&TyL@bU++ z&=y~ z{4(p+@FcV9bb>}*jlB~IJXi5#0yG41Is?JDoiVj9z}Oq``#Q5dWJjz$v5lzF8BOM@ z{q`g=aXdU;Y-VuKokU5aFY=6(r?od@$ zcnIr#(A4+==z_`|ya!W5Ks8jG!m4Jp1LXk|E6Q2P*lJN?vRq=6$FE{RjGye-)L}`eA$2}CY2PUD@rU> zYQ`t;k0Lb1YgaUZk_+Hm$E(!$jmV2^ppH|`x<4HF@6!gLtoYs8angkKP>2t8#UM8G z$}BHshxt$@GivcnYC+|45kXg_owmI9OWT)ChV6bO`$?b3gJ>hM$zB<|8`sao_dVBC zIOYrA_hw7yxjcDmtD1pKL|EWq7yf|IY*;8r-FdVRV(t#sNb9WU(YiW%;Fvd{x^O!Z z9qt`mfmtF*U?jojvV>k$$DS-P{kc%(y4Ywkq4{v)lXyBLW}7yc?>&NF<>xGMHii#R zfBRh-SvXb8WIDt&gznP40dgwS;(XhBOz>sr8Z#$h^YKs+bHmGa0e$a7_j>z7UMk-h z!b0m`)>R)b7{~V{wlvEYdR!utBfmVodNH3vL&UYzSgLiF2ycWZ_XpVezc@pn zYf+hyMXNY0ek$r=lj5hLMgd#-?7~{pLsT<-T752)E_Tz3kd)c`JfRa&^z0;9h+!IO z-upshT5gOd-iLBR5QPbZD9vS7oWSzs_|x)66YawY)PyeNP|B8v^jO;Fs%t0=)!pm? z{(yYs*SvRd=+)`jE?v!QBvN;6wlT!W%&G)rB;w}BwPfgAQ@pgi@9*RXh)lMWX#Iv8 z+p&Mb*a7*Wm$eTn4Fk6YOd)$dX?}D?JB|y?UBR7ZpM@njOrV zG#gHZcF@vG3{>r7xt=Cx8N$)66KaU=SJ>cWu>LJgq zb_PITI$(&b@=24NAZq#4SszjHkgNHwBq%z&ZtsBAPm{G==!{6`rQe%N=@xN_Z;Saw z2MfQ7Te}fe5=0nm#gLsy?#@#v zHyIv1Wa6u8Mr3zx?Pyf=Mms{!6nFup!h%;5A)4>|*{dhpIhEE@!GxO_0=_eCE(b4- zf9Ve>(V{&NqWtOtioddhDgqP0qVZ6lY_&+9+Y9&}A_iUDF z+9wRCnl-qC^b-+-NR=q2f#ux;!29s{h06wj*3aFG;DTB8h5AxT&nEPM3 z7ZP}($FV|}X#u~`SX)4Vk#e@=a{OEDt6_WFC;!eMMCa{nkD-ZT1>eU*oaMrYXO;j6 z0k1d|0j9i>Ve`{zg{4L5crB7O1;{sYK!@lRpx|iZxm*!owYOaW6$#}4yc~M9f{;-q zmFF)^7_R-{G7~6rmU$-Agq&b5))TCA%BP4{jw!GcAVf@j*J5=67}kv3YFDT^P)`}g zJ6~%juF{BcMKPwE%v|mQQyW28Y+F zStCHTagwY7E-me?_lz^X1KNCJSODldQ1q(M@bs)-00PKd%WS*b-n28|uP6YDHbkiB z1R%bHu_BL_KPf8wVx&C}8@Kxu9G)H@x;g^!cujgxaGoTvgs2&{esbwrmg&|;Nw1Wu zkmJPMuQ(4gs{;}wj(;qfkfIMzQpZs=TdaVOgDG>$50rp;0YH9pAf8IPn)V3tDuDq{ zW^K8~D$xg+I!uxHxfrPmSk5g$?X-i1`b;+T7%buIJU)vp1ZB&?q+Aa6^=GuH>WA>d z!25uk^G)8bLc%Y=j_Q~LgmmY=K7foG7wT}^FV3dAaFzXItO&SWdhydOx^!c_B`9MC zG!iHLXtTtD}$$6aeR(&t$EW*w~nl++-?G^MvNakCW!-jRtbT;E4 z0Y36ZBdl;yP75fLWf)2Z#YT(giOCJHc@WER&#*QSPuQg>Vl{cU{TP!rfdWh(uMLtj zWXW*6U7DUxN`9j`oQAQ-RpZ`EnI!g#tpnuO>dQ@Tdl{DgPY)iP*c=PSc?hP@?^G5r zRhmGu$TA=!VZBYfpElD!=zBlEb`i*Q0lbw^gfy^3+-wm*HfRB7B?i9@D46S0w!vQp zL(*Qd#^5pQ5&*|xY7T+tM3FpVeNKJKmUEhV;a@=JD3i-BbLOeN&9AG-fFeYv?lwXD zMP934-;!C;;m7mDf)&XQnBX)d*}NWawl}|h|9SdAU`9!St_Zjzjhzwa9pq)RP}{Uk z*WIbX*`oA#Hr{W~!R+*}p@qm9^3|yU?&VLtdMJVeP-3NmwwaX;C-QV(&B_ol%3>+D zj6zf@fQh$1U+>^>uRYN10H`L?#5^%j@{z7ZS86v4bSVIjTY?u*C-f)iU%=p-skh5g0f#5<0IUZX;&6J)3#IK6%%oycva2U zq7rP8N7}i&O=D_y+MDP7S!}F!7^7eUW7G@r%|6JTz`u1L1)+ z)B?cb?dzt>^qZ%RHZi|%Kj%Ep=iSW2B^Ch)XPiV|^i_k5n%xhq_v`jI91}S=5R6UF z%+ZL5NalYo#b^TdMO-m$3=r2ccq1}xP)z+UTXnCoSK@}BEP>8zCXeAazNQd_&M#NHrdO;)Uj1V*=x z&#uxly6rdwtDu=rbG(838Yn7>k3bLF3VOTj*}ZQWU^GW=`#+m5>@W0KuQo?AXL?MP zUQLI<#(zXz1tbZ#!F0~jxz)f{+%n>VlX(hpl4)AS#b=2q#C8O=I6G8iP5eiC*9Ttz zjld-|bQnR&YM&eDz1K$sZ^}>LvSUV9n8#{I6e=3vLs8vhm4Na>>#ckOh@kD<-Jil( zNCu1mzrn{H8Vz@1&FAzS=Zc`di7J+f!$5T=jA$P>05+oi{vNFu02-As0G9YMlec9E zI2iv&iU0q-lW30t;ZSq*4H79U+&`5eAUy2f7%@8QReTQP5Edt$Q6Ds}S6;CMd=iir zFX%(%q%5xL_lr-BFMiZic2c^b=D-~8C;^3g!j>EQs`KKLLV3f}1@Ygy$mwe&L z#27_?u^s^m(+4)gI@@IuR@i zQ(FKuqW<$kqiE-=hF~wiX5RoLQw~4CDM`_DXyq1bFv0{bCIBYKu=i0b9gvOC@VIvG z#ed0Fpo?5By7Xo8WD?~61PU&!?FWAgs<3$;8+`?~%dDEJ#grQWC^mpI9cXuL+L#fI zcqY&U?(eQU^Gc^1LT6LU+ot^kx4`KkU^2O{>cu9FX|zXbHZCQr_VYFLZ#uZ3&$Sl- z{K8>dEY0;};I^KMTknrWM#quEI(2%k2LVook+RJJMKe6z65ye=I&@fMCmn$ix^yp23?>% zvc^1v2rKIJ;|qKiCIA670NG9sFcGl$KAwNk!WCPR^#H6XvF;+NQ9#7)rveW>n>5cJ zlT}a}y$U3gd+cxr6xbs?hr^_p=dIE80AwNDf$zYiIpdrfg#4~wD8>_L6h4NbZfTe4 za*Ls%Qv-(92@nL0yYsYf!&Mk(@^`7V`3R?k?~l6!#F(ZH4p;#P*%`3L1_5E(wq`3J z{)>z(f@awVifS-4&XlDWWb+}*wWT%@*EK>AkH(tYqC>LKxJmQ7CUYZjD#Q#xtin;_ zOqtv^v@1Sm(YEe*NI%{2;QksWv=HEY#0a;B6h9Iz!u^uQg>iS7+}-6tbc#HYWip^= zw6S6+Umh&bsR&&paaoRue$y&v(K7HVhU5qd7!WAp_9*%J0ltl=-ys_RuE0?XRjtiT z%IW5CbF0s`q3=a9iJ&JNnOEWya1!P{OWU={**b~fo5EzEmPQ^}$Puiw5WU28zpg}) zn(Ai-Ss1T(dDpR zz-}pJaK!_V{d!`q)+Uu58CxXn`Djp;|C8T(yHO_`4uGu7-|k=$a`bG3Rsn~b=JCUY zP~Xu(;0Q$J3V}&a`-mL~sd&Bg1 zM?Wot;mX{R%7&Af6WeH#rWbjl2fKcOTN@21@$s9=GJhIerDpdM-DN24>KEE=pr&>RSm3`XG-R_*L_*Zn$F^#= zgl}7hPLj>sQ7KruIUn9-2|&^023$kH@(!KvOl|^Jz}ra*{(Ksy2&9FBBDq9GIxoOM zR6;g?v5RNhBj9n!B?5&Wu&Z&UKiH^eCQD0>t{1We4zob>WTrH0Y|9T zjv`s>eP!PW7;Cu^sj$mFsBRe-=ep9o85J8TzqJDs;FPY+y437$ij+~C2_zX;-! z5AkNBesrAA6(Bd*o}zGYDzpVkS2*mvdm>(<*4QGE&QpWwL+VHrs@!JsD03}Kz zCWIJ~p`41aDM`$iX$aTjUrGRHQ~H5Pr%KylC7x?cdQ7?EA^(>+i>1v#2DeQSPRy77z*f}lYEtU{VQv(jb9Wd??)u`|CwPg0n4#g7i1y_!^ zOA^P>b4XS>Z^|3;?)9HMpDFY5yxl2PBD+6v4##jkBqKo$9;pGaK2o6qOJk3RUVjYf zX1oj;`;Hh91Zj+k)BMHKVw($4{OyG2ru|03MiOLULzX8Dl9L@58t?_)THmsg=Ps`J zFi>sN@^HD(*EcE(*Jcmwf(D6e2-EGwo`rp2Qv3B9+SNfxnQCgT@1>5Q2TPZoKdQYe zA1=c?*}hovBNwZwFY!JAg$uge#Tya;LkPCByXO=Iz$pl1QzJle*elEV%VogVOLxmQ z^1Nl;pXg(vMTem5;V{Wdi*xXPsvu~v+r!gowJr{bPi}zG`?FYrNz8*6lkcs-kWygM z`?+l<>Y1I>(xtJ1wl{G1Yc$8G*xx>kvsvuzqZ<8p62_F+)U1P`{pn)c@AYs=Ej5Rkq}{-$(ZBkBA=!;-{K_^IwPS$ zMQ;BTdLGPWJFHdfHVDLsdOEYxzV$yT_xFF=XzR0$r*jaVYx2?vA|)>Bg#!t^jZ>ov zBW7Os&@;P2??&CnaR6l@ji9#yRgyx=mTtly02bw=oONfjl_vP?4|5hvR=cTiH^zUZ z(s1-M&>O3;DYQeH&xchrI_Y z#QtzFU6xs6x1gCeXbT+VBkjr3NZA}ty@Rq>gESYqbGZ>9uxX6<#ZlE^q6AM;TEv)2 znEr3cE*Hx-=Z*toI(I|(!KCZq#S|)4pTNa#EB`5hH3Nyfs5fxv_GvHEYKf5hlE$#lzQ2+yj zT=M*X0ld@w`r6^04K!agKYuKJXCT+-^i%q-Nv4!UtvJoZn2xHk2dsM?AZ_V6toOgm z4yR-LAFZ6}KU?_%#%n1dK{fVih{ie%Vz{=Uh`l9LC?lFi=C+vNQ)`uWED7Oel%gZm zXr;Bxbd1vIlA;I+)ifwARg5UgAhk8xiE>Z)2kwje`h3ngzw_=q%l8RD9qeg5pQi{J zBYGoxYdD(|F102{$Z~ax>)kW-cRSwWF|IMW%XO=VokpeIwe{)yOm)-6z?*h2ku!+` zRgJ8uZ1~3%3lN!wN(u zA6Y`&oo9ZtJBh%50oP-iK4R#ot`_6fu11L6=|O+%tV7y3gkwaqj@-4XqP(B7dVDfv zqV=bkf|zc;zfO5<*m6kM?W*JWh;L(At$9^GbY7n+xK`+E=GZLkMpY&l?6bQDz~2?85#uNM;LxW#<0j91hCiRJ>~hTQ|{r#g={f=379+ zdU@r%vM)KBdO-Gz`wXb)R{d0ZNvEa)50>vTR<*}YoLfV?D#0ra}v_<6;qBK`Dg@&NkvDhPw4q!oH5?K?B z5k=*cFCyn57aobd;(nFdG(HhMiCqV^Ki>)`{}xV3p|#>CMGTLbxM+DRg^-^?qku+{ zu|C;MyCZEs$Z1_leLukF_D$YGzgF66X5~?(m=ii9Wh+qKu zYZx|xe(1`%pf+xOt~Ro!D?vjc**{ji=&FENo?a$A;Iuzs#qc@y9CY=WyUjvbn1?X- zBP-QAW5|SVeed+2^KsWBjluITiz-|S*r7{yYZJt|qvYXcs7~Tg_-FSB|BAuv?fQQT zE#Q?CZ+X|oY(4UNLvqsOe_(~(z{J$kMVTvNWQ#VbI9a0Tq!8bV#c;!%; zq6wLRt~z9TUL|g7*44b#GPhQ}zj*V8JL=P8&$(1JT*KfmN);i^Pdtt%d~dgQO>JDG zCMv*0SOdjWJ$cXkipB6TarI>Rp*V#3vI81>aRBsnxIR9}iXeO~*zT5`HPsB zMw@@i2@~5Gq-x1bmX2BnvJKYKB*ydG#aA**^E%92mF40K?&4xs2LeZa671*(ERsO* z5<=E&MY#KmPS4zY-QT){+++TsE6Xa?X|&uUHL`ha64}z}O4mxyRl};ZQU#CvbJjKaFszRmvy6pB{9YR3TvmxcF`<1d zQ1*d^4I{0*@%fD_O@4-xJ+{cLowN5PgZU_~JhXtWs6wAPT7pmudlJv>5`}eq22t)o z(RVF=@?Uk*)i{c7#6blB?Ww99f_5%4gXsTPnc8PPE`IY*mCsf^Vm0T$-If) zY6E_Q`UY+>_yKyafEXkn3fhV=fY+B!NF zKXcn6^J+n~BXO1wjt`csgsS_5K|}V=RS5FrH(cx4UX0(@>nEk$$GL|jrZmGDmvSFs zy;s}78{M8txsNlaTeTv9r{94AUl()V@THVksrkR(1UGWruyfpxS~PNI-QkfK6+glA zGT1)f0vyd6lwNAXPssA*kv29%Zx`l-cJC)Y1~LvK&szRkC!s9H%NUcVl9Jyv{4fWM zr6kStC_S#Y1z-SDV}g=%z(DV9M^;AisL z-Lq}|tlGh8-+JRvi8SJ@H)3w!p}s)>{U(KZvlU6@#I71&8BK~sh1fXcxP%fID{5GU z58{Pq+$;J>X3u9BfE5F_g9)II0^Cj%7Mi~4enoG_Mr{lL!me$`%zJIxcL@9^l|{90 zk6H*6M0Q`_`~dtqORH)ghf~~=>YNJrOVM8i&-!J1F94hepuA(AP8oARC%xwTufKqr zar>aZ{`=xchC`H5d^^KcS=L@iYI}K#) z9g`F8uMD}iNt}!vp<}dy_K6AgCH9`{vj~&1IxJ4w;4JP^5HA8xeYtyBI^E>wh4E}e H6LS9xeUuzF literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..22cdd15cc0c3b31abb5840edc30cd305f4845f46 GIT binary patch literal 11171 zcmaJ{1yod9zefQX1Y`!3ZUj_f=o~tgGy%y0DT$$HXn_$1X%Hy^#a5)G89J2)NvR

FKCbTx7UNKtMooQ$ytr0RdqE@C*lC0RFcQ zrrRMPIHzu>tgLraS(#1G6=7}XU`0T1E!NW9oa!duwPrZn+`PGs_xeRwbA)J#}c=^Iu?_O~3E}GbA&nc!Qk<=3ZSb%p_8g znWeo)WA)zV`8CP&%GdALh(HZ>)oYroJL3Y+U7i8=W+0x_#odk|WPcG57+B-W6_dxr z{vxTmI`>UdpyEAx_V;Da0|Spp3F}6ug6cC>N(kwy@-Ba%XH*;*{zXq`@ZsR-Na|%^ zphA-7gxxerENKbs3O^8>O+xsu`eUTlDQXfVSD}8v!jE!=8XJr+>!% zM5|`~Z+ga66o}6JVCTZ*{UB#vs<2UlW42oef4i>5ArhK9sY_ zdT--Hm#x-!tBwwG_L}4maXK$0W4ms+`bjk&&VR`%GdtStcWR1aF7=1m(Gwu7=*1pC z_c`2|z57VsZ6@MZEtpYC2dry&benPJs)`@}qBqomSE+D?`ADcU>Ss#=caxbykJ*ZG;Y z)LGULFHoAeV+yZ+w(MC zMoUYz{k|NtF`HB&%MjX<#i2sCLi_$rJEoqvrg_yB3I@^UXfnC|nh4g5B~6sq0(N^c zm?oC;npoBxBba6S$mHwSX)9NVkxRo;64tGBTT}76Wf!m9Knck@32M0?iG+?LY&=(jq(|uf?*LWa_aXh#7@CvTZ z&%d_6aOZ zxsG&MpW%*xLpN4A4W(3Z$9<+3P_xmojz1)h$%%1Za0d)`y_q+8&IdpJBpJHHwTz>6 zr3lt@=GsXv4bQGrBn25&`tESJwNpZ#*V%6Y)1ud7aY#(IO?~x3g)KU5HBIRWy_;~# z_$=%J3-%+O!%B!nYrwA}$Ae)`cl`O)I?^?*0L?^!#kHFAdrQcwg(d8oPC%4Rn)AT^ z&f1=RVr+kD=|} zT>Lc~k9rj_|LKEq!j1db@kw?XNPoVSrhb9#?PXGSOI+>K1F_kxDxZCPlhnrRuuw|Y zO^bLEXC=G=rz+z*an8kn;YnN?iG$J|(q7-qL7Rg*7%oNejhqppDcGYE>;+OLBJrGR zhK$j`R@}R29^{%UE|Y!>b;s3m+=5yDPtBZrYxKnjlr*=2aU* zE)Go-i*nEiDFo0%;b<<+7wbDK#=>euM3zfqRaIut5cAhL!vX7E>YNipv_B>&7c{*; zKQ$5!>5$!e>9afc!S6l_4u3)tu}m$`J@7I$SDi_iH7{Z#RS{|~-2Y`Yt@>^|T@SyCjr=1sI5+F>XXiYu)BwbcOi?;>MnnKul6$_^&2+25a)U(V4c8o$2Rj;x$~ z%COL~a%1AAkg{)UhWr@h^FXDa4>ag-HL+^D&=&RdW8zA0?)KzsCQDxoh#W!XksQmi z4vN#EV>3<7@tx+4B0&y;7)|d|Uqc-|EPOn@&=~x5>rxk^XzewKKbkPVF;4GcW3;pC zK2aS9Bo6rSZKzVEfY7y~aCfT=+P_zAIBT_d9tSr!Za# z*jU>KrE69w3GF`7bzC{0*r|?9bnp$vy=wrkquM|<)8*%tQK>ay_`Z*~>*6w5Aa#Rq zkc*P+qKtLAq5sS3po!>ZTKN#Reejcg$oU^fs-17_tg@p1&?OFCK5^bQ`=2L;1k5DE zzlF6j$XIa@;5Q?gq`E^nIp**9pwz+etzoxld8#|=beyAno+<@}As|7l{64-j!nRP` zKx;*nF*uSEhc>8dDYj_17`pYH98O0Q;0Z0{G?lKuwlE3yr-SFUyx}-&lpN*;{2@>V9+*o_IwoDAlvVk#v-!n9%Vd9wj<_WnOz* zUp#T$f`Ccca|@c4_3`T}1~?O+=>+|KR{J@SOSts|R{Mk2KQIG%b~OB(v$jmgvYX>% zol%Ihg(a;~Xo>HRYtutjqcNJVzK`vS;I|5#J8h>|Cg9?7?GRZ5=PY9qi9Nw{n!7t| zvr;JNds1SVl%5LbiKag68PO1RXQ7?j_xCg`((_8(pgziry>zveYAWrw!w1dTq?!A+?(;-s*ly5`^{;Z zt=e~hj=qVRp@y|`^Ng%JLA_VU2JB@RCk`y)X~+@koeq)gcHHd)SYqYpLBz3Jt$A}=o#TNL?c!f|>R1oK z*tAb>K8~P?CM8DF>qQ>P+>bd|}Tl-Y<`oc;4u};|F$U^@=H+DvSmUoa7>_ni3f;8%2$465y z(}WD~ihIo8Gm~Flv}|LYRHhpY5y6#x56zRg`Z{ZDyHSE8DB2w+g_429VxYCAkZjaX z2W`%sR?zo)nNR`+LAHDlqJgEN2(I}sB8}IO45NQV=Vz0*I?Okhl5te5Zy?W-XQ2h9 z&ujUUVA-~+QP zUnj{FXAyi;(hh5NPpdJwJDiSZpIRB4dUKzil=&av&W51Yz|2yeQ4uSk3I1Bk%}!-X)xIA5tLP{7cSGoEz={q5(I90I z`^y2b_{e)G+oDc|_BOt@D9?^;{tV_h>J8lV)q(_VX*$;PG*B)jkrW(GXql__*~Vk| zUJa%HxS59{>Z;euGhu7fNQn4us6x;=#sERn#>kJ47Fn$=b2;sRMK&iQ^O@s^7c;>@ z6Jv9T5SmR*WSeUKnNSt?KG+O1WDpXg{L&IdC$vDj|B+|ghW(S=BoAvJQYNl-3(b1j zypc|_wq@7Ck*I}eL2JRQVD$m=u}A$6YF=4mTKR|OMPGPlvEuc8g&f0Pi=^jc3WF3o z*i$#n|F7d|c_@VINOS$;U~7=P(mBU8vl4A02Kzk=S#s)`kdnL+vASvgDWQGm_C_wy zB_k4Afy68Q>we3l6{F>LGRci!X#cF!zouyc3;tx*)Cg(GBfK#|S3?tS39J={Ms)7` z1loy+wCnVg9C9U_g2qVUY3T`q+$g`{&cxg0EB-n7$!cx#MqmS88)M9X9 z!M?_mP+3V5^d(O4V0EKsO`)*#NhUYNfBq&Uz>DmY8t?fqb_D|wTpFsE)J-QGa`>I0 z)2`>es^@{0rqnd##6ePW2#`FJ0JqN&lP01SqRHzKSE3kMq|*_wXL+z0+Y^Up>UnR|u2?TK9ECo}_X)HDO3=B=TB#tcPR4oCz0_1w0>-4Q++pf%Jk1XmU z$26FiYZS9lmj4H?gKhj|dP|;VQ+J$+*O@t%pR5oCK4uPXM#WB%VG{rya{{pS(r~F; zER0dy!5LAp@nA~&HjjN@mP@(kqAS;}=NQpEhe2WF@6Ol9Rqa*;9QhY}Z#>T68j`d~ zXq%f;k7n60ri<4)JxSPRse$%GS>DU~Y?dtkDlU)b(N)WL8ZPN_94ff(n)WfC->9hM zc*K>SUfki##MP4Lo>1m5gN-{UGHhYIcY z_Sa_G+<>;bT;YANzO%!Ijoq_G`_2G7o#POs4=k6;a`(A7S`5%Fci<(mc+9^Xh+?wO z&UB*5WMgE7IeD~Ya}4_f$0>~}(ko)G!<+KVs{FI>eY)ec{<|w17|NwCb=HlYF~2R7 zV$a2g{S`h)d7W%X{cJ}DQuWzoKq~1CkML_v5;CplFtsBTI@TLxV5xhXi)$PAb4*8y z8;E!8;$3jlbwK>4j%jeyKAQhQRZ9XdZToxJ=(jN2Mle-%W%oY^*5E6mMK#;G zfb^RH440Wb{qB1${8k-n-Y#={li0*%7m}mT_rL7AaCjQ-g|RRFCb5XB#LFt^iLj8T zKeuuy%prBFXXQ(>DZw{AtZxXX-P_+iqbE8GG@%Z`w7{mf;TD9c2`_xydn1%*@lCYc zDogrimz&)+GiogDh~}_D-rj9y2~?0NyIkcIMp2M^gWJilu(UZr8^g1=X)qI zimL1H0vM^h=a3(uTvlK*{l0~=`b$m#t9;OgIY`n0)A)0@Z6J8FU1k}j*#BCYWK4Vu z+%*&U$N?q58nEwTM!r0G%n_xLcn|vvXZt$V?z?1fgy7G`SG@C=dkZuTejn`A~_j&=)vAr zapob0?z&l}@72hdTUb5N%~nr>ccvm(H~JAGV#rh6YaiTG+I^{7k~dJyH5GPKar7f# zi8H^Brkhk-sn{H{ACxq{4FVV@pEi4u7UI>bpS@li`N6cnt>B#q4^ zBmj7}ejz4OAY60l^#ZJz3fj4%#sf2MgAJt-4<2dr|hL z?OX4aeK|dDY`fsF#3~WLBu|ZdTKGw+T)~uU<ZC$2owW?E=#WwcE#BU-qa5!wXb!kc#q zDCOwqn99+=e%c@H@r;hKPf!46lx0`9(7X)}0C6apLT;gL@)dQg$vfx+xK;S4;$0@a zL^U<5_@V09sOPgD%rjn(hSA0Or4*C78(~v6SddF2-x)B`Oc!X(emLMvWEp7!5buK& zNf-(P8Qlm~5b2<+eLP&^;{QR@b3#Qjm;GX62-JbER$2mP3j(7rOoUj;Nd{vOPYik5n`$YFm3_7Z$wMPqTRnP+aYDH`cOKx8!2h0ZMe^5*n;r!Yi1 zrR&r8rzNa7Qcta(AroXjQdUSy0U|W*G7oV!*JhBB=cr$~#ojH5wL|5B_Eg*TFwO<0W}ns&C~_9lhqdi9xBBjGV4J~ zT|b=3qw9t!W`rON!*7Z4_0m$*rc*qTa;cqxBwPGH#SCn`+XpX)O0P(Ay#LfwZ~k~g zWkF(RCk0RYM`jKLt1te_bG40J0JGw(kv-CX(lt;Za+L{GvHM_a?5aBO!DF~DeNapt zd%KJ8Au4lP8FqpA?YmUHdg_K!Qu!s$Cqd2P;;1!e8tG5v}bpua}z@Qba zpEkvUZ^0fb`T(js$HGN^W}FA0dTx7oWi=>gvF!qY&(hF!G1kL($egQRCqr7`)F@=I z$9$=klkf52I)j-p3Ig0Qt+^e>c{ojhbWIPF(})k~J`sR&nS?0j(WfWx6VTNcPwLGq zc0PNa1;)(~0A#z?i+-d&1uFnF9b<5;0y->YJn1Llet~{WdlrRXrBV`{!jk}m=Z?!4p?`&k9SY5D!J&c8Zw9&if@kRGM!DvJyr$R^``*t9IRSvn;{?D^Qaj#< z??4u#%-sO{uq~!e5l1c=7sKl=NVxyj)6Y~1yX+XrAok;ka4&fa>#NB_Mi%TNT_#Eg8~oZ1?`;>-Qbv5@Q395qyBd!1edFu_vO3hz?D zeJL|?Ds8CRL(KpDbT|@VDE8!~0p*0v!S04so;~R@btd&U%Us3o+PQsjH4@EA3}l-e zqmXi$s5j4Zknd<&OI}>*JvqSw`8Z<)J6L7wTkhtV%8m`DI+cBw-$nrR#cvxInhjt} zr~Ygcr@kyhQ}UUPjIsOK#)BKnV`InmV)zHIwV6`DJShhVn-5OIFHHs+H8`0eu}vUBPbF4EsweG z26p3Gaf^D;m>jf}RnE6$IcGW}fFiTKc0nLx>s$Zs?Q*rR@9 zG=H41c(wjw#F+Vu=AG_$nj^sO?`t~n0N@H6b3IKsJLLf^;Axq@L<|*tpADo4ijdA* z-{kLhX3(Ll@>5?Kk7AT)@3_tI%%7V~o=@;7R~GPwz8K_XGD*j*O zN?CKDBG#?WK3hv362s2Hv|nf2)7(I%wjm!?&DmeAc;0Dr079J&l%5Un%u7PIB-v?l z{CE-SeP98=Ry3*b?iHe`|GhL)UO`s?^SnxQ*@aM3l9tb)b5`{lyr4~1h3!D;N&Jhh zO2cw`+1eL!*7d(7rXl{$YFdqq0oc7yZ8mDVAB9hCUNI&_!kf?28b{Nm+#17$^Ev;> z9d27fpWOm@*HhZeHvsj9=#KvGdNXo(uy^-o)RuMUD@8d+z>8l39B-H9%DwC@En<hBwcH^RBc3wFd5y%Q@GQr#d`n2sVz88o9*=kr7roXfQY!S5dJChf^u_Gk`RJ* zlu?9?bSlMJrk<4ro6ByxGNF|s`aMm~Z^&n}Irz5>aJ`~q6o8!?d5w{bGKYfm^%fh% zlq}^o94Vl?Sim-5RuZUCqx($Hn93yOT5r~9d9L^sPE2Jzd1Z1G&`juBsiZ==c0kEu zb>*Rn&%IlNUPk~f=BiSf&)i~aZ(<+ksQDV`?u9@WUSBbO4~}m9eoDjfnC?5Xo!V6c z7XlTO8d%wrC}r1Uw&o)MFDl1FdEFe%XHgSsFVn%6omIfQ7@Bof)|LmYelC}5KFPGH^ zCL-lW51jo@X_?@_VGSVa9Z%RmD=+e1gIGjO6`znqI^U|jJV<_llkc*NRc*K^EEGf} zqaKkbC+L96H`ucdiqO_-oop&A2X2vgb(q(MQ25=hCxvG)OS@x!(Zbx<`)hSSXeCc5 z^WALHq-oOdG zAdSu0FF+dT_w=vdpbo$okVM~e_6rbQOfkw2UPtRm`fdzEYeY!5{=(3R36e8q^CrPu z(n?Tw1!lTegMG;vf>+8{i*u{*2CmU?!n11j9SoG@n_Ij!R%^mKI|>ZE_d>>%Q9U2< zlTCXYu?63~RrrR64du6^1m*t_$e>otH9dVv3})>@oUH`3wjX*ISYq)_<+^FOoktZ{ zEy`8HX%3r$Z5MB7pIR`k41v0nVCrEJr3C|-&~YUuuv%E|I73Z{LJe6RS!M8pH)<4B z5K#H;xI~gAKwR_V8f;LH}V@P7Xi7&N+=S({W*HG<7z8N1f*{Gk86HskVrOUGY;hH}6lr-X1c zF_D}SGFJ57yP$9IO(%9E2qXTBi3Dep6Adb^6Wan^QNwcnYy-yu_k5!E*@y8|9~zD) zq~8#zgJ*(Xmu*x(`4keGqB*~l{glwZtXd$|JCY}IMac-NCIP;I#^fuK|JtOsNTxwt zsSPZ%2xDLwh9L54Dc@`~`bT3 z*T462dY+hq+>UimNR9e$?$jsEXM|MXPhoYoChtRG3BH7rHp1UR4~VKcFTKi~$Ky1# zJ}x>&3V?LUlOMWIof*doyvJA4p4r{uIV*p&xd9+qxJmcjzY_G1FTGvqd8_j;d^iIX zW)+oj>l8^i29DXlNs`F>{$|=KQ3Aj|fvtd-(ZVGEukHjID+02%zE-^S^-~}W=s+?j zP?(hejgyi6O9?Lx0uXI>61T^lQ*-;TumOd+?caNNHo=uR@Q6@R59EGh&BXKgBIr$3 L9hGb)^Pv9$EQJC` literal 0 HcmV?d00001 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