From 4b47a1810cd2d2e0fcf933c7d04f22a155be9cb8 Mon Sep 17 00:00:00 2001 From: ngala Date: Tue, 21 Nov 2023 22:53:47 +0530 Subject: [PATCH] Add experimental namespace_in_path pages url in project deploy Related to: https://gitlab.com/gitlab-org/gitlab/-/issues/17584 Changelog: added --- .../artifacts/external_file.html.haml | 5 +- app/views/projects/pages/_access.html.haml | 5 +- config/gitlab.yml.example | 1 + config/initializers/1_settings.rb | 1 + lib/gitlab/pages/url_builder.rb | 13 +- spec/lib/gitlab/pages/url_builder_spec.rb | 151 ++++++++++++++---- 6 files changed, 139 insertions(+), 37 deletions(-) diff --git a/app/views/projects/artifacts/external_file.html.haml b/app/views/projects/artifacts/external_file.html.haml index 67f6ccd5695dfc..37faea3a86f697 100644 --- a/app/views/projects/artifacts/external_file.html.haml +++ b/app/views/projects/artifacts/external_file.html.haml @@ -1,4 +1,7 @@ - external_url = @blob.external_url(@build) +- external_url_text = external_url +- if Gitlab.config.pages.namespace_in_path + - external_url_text = "#{external_url} (Experimental)" - page_title @path, _('Artifacts'), "#{@build.name} (##{@build.id})", _('Jobs') = render "projects/jobs/header" @@ -9,7 +12,7 @@ %h2= _("You are being redirected away from GitLab") %p= _("This page is hosted on GitLab pages but contains user-generated content and may contain malicious code. Do not accept unless you trust the author and source.") - = link_to external_url, + = link_to external_url_text, external_url, target: '_blank', title: _('Opens in a new window'), diff --git a/app/views/projects/pages/_access.html.haml b/app/views/projects/pages/_access.html.haml index 1e18e528665775..53a4fb4389c76a 100644 --- a/app/views/projects/pages/_access.html.haml +++ b/app/views/projects/pages/_access.html.haml @@ -1,12 +1,15 @@ - if @project.pages_deployed? - pages_url = build_pages_url(@project, with_unique_domain: true) + - pages_url_text = pages_url + - if Gitlab.config.pages.namespace_in_path + - pages_url_text = "#{pages_url} (Experimental)" = render Pajamas::CardComponent.new(card_options: { class: 'gl-mb-5', data: { testid: 'access-page-container' } }, footer_options: { class: 'gl-alert-warning' }) do |c| - c.with_header do = s_('GitLabPages|Access pages') - c.with_body do %p - = external_link(pages_url, pages_url) + = external_link(pages_url_text, pages_url) - @project.pages_domains.each do |domain| %p diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 5002e9e24bf311..5b26a090234608 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -485,6 +485,7 @@ production: &base enabled: true # The location where pages are stored (default: shared/pages). # path: shared/pages + # namespace_in_path: false # Set to true if you want to enable namespace in the URL path. It requires pages nginx to be enabled. ## Mattermost ## For enabling Add to Mattermost button diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 2b8c30250f00fa..2b1238a5695ca2 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -349,6 +349,7 @@ Settings.pages['local_store'] ||= {} Settings.pages['local_store']['path'] = Settings.absolute(Settings.pages['local_store']['path'] || File.join(Settings.shared['path'], "pages")) Settings.pages['local_store']['enabled'] = true if Settings.pages['local_store']['enabled'].nil? +Settings.pages['namespace_in_path'] = false if Settings.pages['namespace_in_path'].nil? # # GitLab documentation diff --git a/lib/gitlab/pages/url_builder.rb b/lib/gitlab/pages/url_builder.rb index 5a28a5ffd2371c..7fe9c611f5b010 100644 --- a/lib/gitlab/pages/url_builder.rb +++ b/lib/gitlab/pages/url_builder.rb @@ -16,6 +16,8 @@ def initialize(project) def pages_url(with_unique_domain: false) return unique_url if with_unique_domain && unique_domain_enabled? + return "#{pages_base_url}/#{project_namespace}/#{project_path}".downcase if config.namespace_in_path + project_path_url = "#{config.protocol}://#{project_path}".downcase # If the project path is the same as host, we serve it as group page @@ -40,9 +42,11 @@ def namespace_pages? def artifact_url(artifact, job) return unless artifact_url_available?(artifact, job) + host_url = config.namespace_in_path ? "#{pages_base_url}/#{project_namespace}" : namespace_url + format( ARTIFACT_URL, - host: namespace_url, + host: host_url, project_path: project_path, job_id: job.id, artifact_path: artifact.path) @@ -67,6 +71,13 @@ def unique_url @unique_url ||= url_for(project.project_setting.pages_unique_domain) end + def pages_base_url + @pages_url ||= URI(config.url) + .tap { |url| url.port = config.port } + .to_s + .downcase + end + def url_for(subdomain) URI(config.url) .tap { |url| url.port = config.port } diff --git a/spec/lib/gitlab/pages/url_builder_spec.rb b/spec/lib/gitlab/pages/url_builder_spec.rb index ae94bbadffe618..9b6ecffa42668a 100644 --- a/spec/lib/gitlab/pages/url_builder_spec.rb +++ b/spec/lib/gitlab/pages/url_builder_spec.rb @@ -14,6 +14,7 @@ let(:project_public) { true } let(:unique_domain) { 'unique-domain' } let(:unique_domain_enabled) { false } + let(:namespace_in_path) { false } let(:project_setting) do instance_double( @@ -43,7 +44,8 @@ protocol: 'http', artifacts_server: artifacts_server, access_control: access_control, - port: port + port: port, + namespace_in_path: namespace_in_path ) end @@ -52,63 +54,131 @@ it { is_expected.to eq('http://group.example.com/project') } - context 'when namespace is upper cased' do - let(:full_path) { 'Group/project' } + context 'when namespace_in_path is false' do + let(:namespace_in_path) { false } - it { is_expected.to eq('http://group.example.com/project') } - end + context 'when namespace is upper cased' do + let(:full_path) { 'Group/project' } - context 'when project is in a nested group page' do - let(:full_path) { 'group/subgroup/project' } + it { is_expected.to eq('http://group.example.com/project') } + end - it { is_expected.to eq('http://group.example.com/subgroup/project') } - end + context 'when project is in a nested group page' do + let(:full_path) { 'group/subgroup/project' } + + it { is_expected.to eq('http://group.example.com/subgroup/project') } + end + + context 'when using domain pages' do + let(:full_path) { 'group/group.example.com' } + + it { is_expected.to eq('http://group.example.com') } + + context 'in development mode' do + let(:port) { 3010 } + + before do + stub_rails_env('development') + end + + it { is_expected.to eq('http://group.example.com:3010') } + end + end - context 'when using domain pages' do - let(:full_path) { 'group/group.example.com' } + context 'when not using pages_unique_domain' do + subject(:pages_url) { builder.pages_url(with_unique_domain: false) } - it { is_expected.to eq('http://group.example.com') } + context 'when pages_unique_domain_enabled is false' do + let(:unique_domain_enabled) { false } - context 'in development mode' do - let(:port) { 3010 } + it { is_expected.to eq('http://group.example.com/project') } + end + + context 'when pages_unique_domain_enabled is true' do + let(:unique_domain_enabled) { true } + + it { is_expected.to eq('http://group.example.com/project') } + end + end + + context 'when using pages_unique_domain' do + subject(:pages_url) { builder.pages_url(with_unique_domain: true) } + + context 'when pages_unique_domain_enabled is false' do + let(:unique_domain_enabled) { false } - before do - stub_rails_env('development') + it { is_expected.to eq('http://group.example.com/project') } end - it { is_expected.to eq('http://group.example.com:3010') } + context 'when pages_unique_domain_enabled is true' do + let(:unique_domain_enabled) { true } + + it { is_expected.to eq('http://unique-domain.example.com') } + end end end - context 'when not using pages_unique_domain' do - subject(:pages_url) { builder.pages_url(with_unique_domain: false) } + context 'when namespace_in_path is true' do + let(:namespace_in_path) { true } - context 'when pages_unique_domain_enabled is false' do - let(:unique_domain_enabled) { false } + context 'when namespace is upper cased' do + let(:full_path) { 'Group/project' } - it { is_expected.to eq('http://group.example.com/project') } + it { is_expected.to eq('http://example.com/group/project') } end - context 'when pages_unique_domain_enabled is true' do - let(:unique_domain_enabled) { true } + context 'when project is in a nested group page' do + let(:full_path) { 'group/subgroup/project' } - it { is_expected.to eq('http://group.example.com/project') } + it { is_expected.to eq('http://example.com/group/subgroup/project') } end - end - context 'when using pages_unique_domain' do - subject(:pages_url) { builder.pages_url(with_unique_domain: true) } + context 'when using domain pages' do + let(:full_path) { 'group/group.example.com' } - context 'when pages_unique_domain_enabled is false' do - let(:unique_domain_enabled) { false } + it { is_expected.to eq('http://example.com/group/group.example.com') } - it { is_expected.to eq('http://group.example.com/project') } + context 'in development mode' do + let(:port) { 3010 } + + before do + stub_rails_env('development') + end + + it { is_expected.to eq('http://example.com:3010/group/group.example.com') } + end end - context 'when pages_unique_domain_enabled is true' do - let(:unique_domain_enabled) { true } + context 'when not using pages_unique_domain' do + subject(:pages_url) { builder.pages_url(with_unique_domain: false) } + + context 'when pages_unique_domain_enabled is false' do + let(:unique_domain_enabled) { false } + + it { is_expected.to eq('http://example.com/group/project') } + end + + context 'when pages_unique_domain_enabled is true' do + let(:unique_domain_enabled) { true } - it { is_expected.to eq('http://unique-domain.example.com') } + it { is_expected.to eq('http://example.com/group/project') } + end + end + + context 'when using pages_unique_domain' do + subject(:pages_url) { builder.pages_url(with_unique_domain: true) } + + context 'when pages_unique_domain_enabled is false' do + let(:unique_domain_enabled) { false } + + it { is_expected.to eq('http://example.com/group/project') } + end + + context 'when pages_unique_domain_enabled is true' do + let(:unique_domain_enabled) { true } + + it { is_expected.to eq('http://unique-domain.example.com') } + end end end end @@ -157,6 +227,19 @@ it { is_expected.to eq("http://group.example.com:1234/-/project/-/jobs/1/artifacts/path/file.txt") } end end + + context 'with namespace_in_path enabled and allowed extension' do + let(:artifact_name) { 'file.txt' } + let(:namespace_in_path) { true } + + it { is_expected.to eq("http://example.com/group/-/project/-/jobs/1/artifacts/path/file.txt") } + + context 'when port is configured' do + let(:port) { 1234 } + + it { is_expected.to eq("http://example.com:1234/group/-/project/-/jobs/1/artifacts/path/file.txt") } + end + end end describe '#artifact_url_available?' do -- GitLab