diff --git a/app/views/projects/artifacts/external_file.html.haml b/app/views/projects/artifacts/external_file.html.haml index 67f6ccd5695dfc1248cf968aaf8f532c3ef75bcb..37faea3a86f697b7d0988f0a414fdce7d1e1eb34 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 1e18e52866577551dfb04b266151ae258c2d0877..53a4fb4389c76ab3a8148952e42bef29580c4a5f 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 5002e9e24bf311e537ebce0af767da9582c428b7..5b26a090234608b4bf34480a6c73f3b208b63b18 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 2b8c30250f00fa9b013f0a2a4aaa4b3b16bfc989..2b1238a5695ca2a72a113460d85f48f8b18d0237 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 5a28a5ffd2371c3c257c7099744d35b257c813c6..7fe9c611f5b0105da774cbc0a6576c9a1e66aaed 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 ae94bbadffe618dbb837599117000a22cc341eb9..9b6ecffa42668a8bc9010ba160754656e91ac7c0 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