summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml3
-rw-r--r--.gitlab/ci/releases.gitlab-ci.yml26
-rw-r--r--changelogs/unreleased/mk-fix-node-name-vs-url.yml5
-rw-r--r--db/post_migrate/20200212052620_readd_template_column_to_services.rb23
-rw-r--r--db/schema.rb2
-rw-r--r--doc/administration/geo/disaster_recovery/index.md19
-rw-r--r--doc/development/gitaly.md17
-rw-r--r--lib/banzai/filter/inline_metrics_filter.rb2
-rw-r--r--lib/banzai/filter/inline_metrics_redactor_filter.rb2
-rw-r--r--lib/feature.rb2
-rw-r--r--lib/feature/gitaly.rb25
-rw-r--r--lib/gitlab/gitaly_client/operation_service.rb30
-rw-r--r--lib/gitlab/metrics/dashboard/url.rb58
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb2
-rw-r--r--spec/lib/feature/gitaly_spec.rb18
-rw-r--r--spec/lib/gitlab/metrics/dashboard/url_spec.rb42
-rw-r--r--spec/models/repository_spec.rb1
-rw-r--r--spec/requests/api/internal/base_spec.rb8
-rw-r--r--spec/support/shared_examples/metrics/url_shared_examples.rb31
-rw-r--r--spec/views/shared/projects/_list.html.haml_spec.rb78
20 files changed, 233 insertions, 161 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 295216a6fea..fec78218958 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -24,9 +24,6 @@ variables:
ES_JAVA_OPTS: "-Xms256m -Xmx256m"
ELASTIC_URL: "http://elastic:changeme@elasticsearch:9200"
-after_script:
- - date
-
include:
- local: .gitlab/ci/cache-repo.gitlab-ci.yml
- local: .gitlab/ci/cng.gitlab-ci.yml
diff --git a/.gitlab/ci/releases.gitlab-ci.yml b/.gitlab/ci/releases.gitlab-ci.yml
index 8ca4041e6be..0f6753aa274 100644
--- a/.gitlab/ci/releases.gitlab-ci.yml
+++ b/.gitlab/ci/releases.gitlab-ci.yml
@@ -1,4 +1,10 @@
----
+.releases:rules:canonical-dot-com-gitlab-stable-branch-only:
+ rules:
+ - if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAME == "gitlab-org/gitlab" && $CI_COMMIT_REF_NAME =~ /^[\d-]+-stable-ee$/'
+
+.releases:rules:canonical-dot-com-security-gitlab-stable-branch-only:
+ rules:
+ - if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAME == "gitlab-org/security/gitlab" && $CI_COMMIT_REF_NAME =~ /^[\d-]+-stable-ee$/'
# Syncs any changes pushed to a stable branch to the corresponding
# gitlab-foss/CE stable branch. We run this prior to any tests so that random
@@ -10,27 +16,21 @@
stage: sync
before_script:
- apk add --no-cache --update curl bash jq
- after_script: []
script:
- bash scripts/sync-stable-branch.sh
- only:
- variables:
- - $CI_SERVER_HOST == "gitlab.com"
sync-stable-branch:
- extends: .merge-train-sync
+ extends:
+ - .releases:rules:canonical-dot-com-gitlab-stable-branch-only
+ - .merge-train-sync
variables:
SOURCE_PROJECT: gitlab-org/gitlab
TARGET_PROJECT: gitlab-org/gitlab-foss
- only:
- refs:
- - /^[\d-]+-stable-ee$/@gitlab-org/gitlab
sync-security-branch:
- extends: .merge-train-sync
+ extends:
+ - .releases:rules:canonical-dot-com-security-gitlab-stable-branch-only
+ - .merge-train-sync
variables:
SOURCE_PROJECT: gitlab-org/security/gitlab
TARGET_PROJECT: gitlab-org/security/gitlab-foss
- only:
- refs:
- - /^[\d-]+-stable-ee$/@gitlab-org/security/gitlab
diff --git a/changelogs/unreleased/mk-fix-node-name-vs-url.yml b/changelogs/unreleased/mk-fix-node-name-vs-url.yml
new file mode 100644
index 00000000000..ea204a2f943
--- /dev/null
+++ b/changelogs/unreleased/mk-fix-node-name-vs-url.yml
@@ -0,0 +1,5 @@
+---
+title: 'Geo: Fix GeoNode name in geo:update_primary_node_url rake task'
+merge_request: 24649
+author:
+type: fixed
diff --git a/db/post_migrate/20200212052620_readd_template_column_to_services.rb b/db/post_migrate/20200212052620_readd_template_column_to_services.rb
new file mode 100644
index 00000000000..e54b9e39277
--- /dev/null
+++ b/db/post_migrate/20200212052620_readd_template_column_to_services.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+class ReaddTemplateColumnToServices < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ return if column_exists? :services, :template
+
+ # The migration to drop the template column never actually shipped
+ # to production, so we should be okay to re-add it without worrying
+ # about doing a data migration. If we needed to restore the value
+ # of `template`, we would look for entries with `project_id IS NULL`.
+ add_column_with_default :services, :template, :boolean, default: false, allow_null: true
+ end
+
+ def down
+ # NOP since the column is expected to exist
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 07ff6ff0828..90f2bb5cf0f 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2020_02_11_152410) do
+ActiveRecord::Schema.define(version: 2020_02_12_052620) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_trgm"
diff --git a/doc/administration/geo/disaster_recovery/index.md b/doc/administration/geo/disaster_recovery/index.md
index 2999e0b6f1d..5455e5914e1 100644
--- a/doc/administration/geo/disaster_recovery/index.md
+++ b/doc/administration/geo/disaster_recovery/index.md
@@ -205,6 +205,25 @@ secondary domain, like changing Git remotes and API URLs.
This command will use the changed `external_url` configuration defined
in `/etc/gitlab/gitlab.rb`.
+1. For GitLab 11.11 through 12.7 only, you may need to update the primary
+ node's name in the database. This bug has been fixed in GitLab 12.8.
+
+ To determine if you need to do this, search for the
+ `gitlab_rails["geo_node_name"]` setting in your `/etc/gitlab/gitlab.rb`
+ file. If it is commented out with `#` or not found at all, then you will
+ need to update the primary node's name in the database. You can search for it
+ like so:
+
+ ```shell
+ grep "geo_node_name" /etc/gitlab/gitlab.rb
+ ```
+
+ To update the primary node's name in the database:
+
+ ```shell
+ gitlab-rails runner 'Gitlab::Geo.primary_node.update!(name: GeoNode.current_node_name)'
+ ```
+
1. Verify you can connect to the newly promoted **primary** using its URL.
If you updated the DNS records for the primary domain, these changes may
not have yet propagated depending on the previous DNS records TTL.
diff --git a/doc/development/gitaly.md b/doc/development/gitaly.md
index 10da59ee9e0..32017a284d5 100644
--- a/doc/development/gitaly.md
+++ b/doc/development/gitaly.md
@@ -298,27 +298,12 @@ Here are the steps to gate a new feature in Gitaly behind a feature flag.
### GitLab Rails
-1. In GitLab Rails:
-
- 1. Add the feature flag to `SERVER_FEATURE_FLAGS` in `lib/feature/gitaly.rb`:
-
- ```ruby
- SERVER_FEATURE_FLAGS = %w[go-find-all-tags].freeze
- ```
-
- 1. Search for `["gitaly"]["features"]` (currently in `spec/requests/api/internal/base_spec.rb`)
- and fix the expected results for the tests by adding the new feature flag into it:
-
- ```ruby
- expect(json_response["gitaly"]["features"]).to eq('gitaly-feature-get-all-lfs-pointers-go' => 'true', 'gitaly-feature-go-find-all-tags' => 'true')
- ```
-
1. Test in a Rails console by setting the feature flag:
NOTE: **Note:**
Pay attention to the name of the flag and the one used in the Rails console.
There is a difference between them (dashes replaced by underscores and name
- prefix is changed).
+ prefix is changed). Make sure to prefix all flags with `gitaly_`.
```ruby
Feature.enable('gitaly_go_find_all_tags')
diff --git a/lib/banzai/filter/inline_metrics_filter.rb b/lib/banzai/filter/inline_metrics_filter.rb
index c5a328c21b2..c1f4bf1f97f 100644
--- a/lib/banzai/filter/inline_metrics_filter.rb
+++ b/lib/banzai/filter/inline_metrics_filter.rb
@@ -25,7 +25,7 @@ module Banzai
# Regular expression matching metrics urls
def link_pattern
- Gitlab::Metrics::Dashboard::Url.regex
+ Gitlab::Metrics::Dashboard::Url.metrics_regex
end
private
diff --git a/lib/banzai/filter/inline_metrics_redactor_filter.rb b/lib/banzai/filter/inline_metrics_redactor_filter.rb
index c70897fccbf..ae830831a27 100644
--- a/lib/banzai/filter/inline_metrics_redactor_filter.rb
+++ b/lib/banzai/filter/inline_metrics_redactor_filter.rb
@@ -59,7 +59,7 @@ module Banzai
embed = Embed.new
url = node.attribute('data-dashboard-url').to_s
- set_path_and_permission(embed, url, URL.regex, :read_environment)
+ set_path_and_permission(embed, url, URL.metrics_regex, :read_environment)
set_path_and_permission(embed, url, URL.grafana_regex, :read_project) unless embed.permission
embeds[node] = embed if embed.permission
diff --git a/lib/feature.rb b/lib/feature.rb
index 543512b1598..aadc2c64957 100644
--- a/lib/feature.rb
+++ b/lib/feature.rb
@@ -32,6 +32,8 @@ class Feature
end
def persisted_names
+ return [] unless Gitlab::Database.exists?
+
Gitlab::SafeRequestStore[:flipper_persisted_names] ||=
begin
# We saw on GitLab.com, this database request was called 2300
diff --git a/lib/feature/gitaly.rb b/lib/feature/gitaly.rb
index 7e3b9378d10..d327162b34e 100644
--- a/lib/feature/gitaly.rb
+++ b/lib/feature/gitaly.rb
@@ -1,34 +1,25 @@
# frozen_string_literal: true
-require 'set'
-
class Feature
class Gitaly
- # Server feature flags should use '_' to separate words.
- SERVER_FEATURE_FLAGS =
- %w[
- cache_invalidator
- inforef_uploadpack_cache
- commit_without_batch_check
- use_core_delta_islands
- use_git_protocol_v2
- ].freeze
-
- DEFAULT_ON_FLAGS = Set.new([]).freeze
+ PREFIX = "gitaly_"
class << self
def enabled?(feature_flag)
return false unless Feature::FlipperFeature.table_exists?
- default_on = DEFAULT_ON_FLAGS.include?(feature_flag)
- Feature.enabled?("gitaly_#{feature_flag}", default_enabled: default_on)
+ Feature.enabled?("#{PREFIX}#{feature_flag}")
rescue ActiveRecord::NoDatabaseError, PG::ConnectionBad
false
end
def server_feature_flags
- SERVER_FEATURE_FLAGS.map do |f|
- ["gitaly-feature-#{f.tr('_', '-')}", enabled?(f).to_s]
+ Feature.persisted_names
+ .select { |f| f.start_with?(PREFIX) }
+ .map do |f|
+ flag = f.delete_prefix(PREFIX)
+
+ ["gitaly-feature-#{flag.tr('_', '-')}", enabled?(flag).to_s]
end.to_h
end
end
diff --git a/lib/gitlab/gitaly_client/operation_service.rb b/lib/gitlab/gitaly_client/operation_service.rb
index 27522f89a5b..67fb0ab9608 100644
--- a/lib/gitlab/gitaly_client/operation_service.rb
+++ b/lib/gitlab/gitaly_client/operation_service.rb
@@ -203,36 +203,6 @@ module Gitlab
start_repository: start_repository)
end
- # DEPRECATED: https://gitlab.com/gitlab-org/gitaly/issues/1628
- def user_rebase(user, rebase_id, branch:, branch_sha:, remote_repository:, remote_branch:)
- request = Gitaly::UserRebaseRequest.new(
- repository: @gitaly_repo,
- user: Gitlab::Git::User.from_gitlab(user).to_gitaly,
- rebase_id: rebase_id.to_s,
- branch: encode_binary(branch),
- branch_sha: branch_sha,
- remote_repository: remote_repository.gitaly_repository,
- remote_branch: encode_binary(remote_branch)
- )
-
- response = GitalyClient.call(
- @repository.storage,
- :operation_service,
- :user_rebase,
- request,
- timeout: GitalyClient.long_timeout,
- remote_storage: remote_repository.storage
- )
-
- if response.pre_receive_error.presence
- raise Gitlab::Git::PreReceiveError, response.pre_receive_error
- elsif response.git_error.presence
- raise Gitlab::Git::Repository::GitError, response.git_error
- else
- response.rebase_sha
- end
- end
-
def rebase(user, rebase_id, branch:, branch_sha:, remote_repository:, remote_branch:, push_options: [])
request_enum = QueueEnumerator.new
rebase_sha = nil
diff --git a/lib/gitlab/metrics/dashboard/url.rb b/lib/gitlab/metrics/dashboard/url.rb
index 712f769bbeb..b3cbfde828c 100644
--- a/lib/gitlab/metrics/dashboard/url.rb
+++ b/lib/gitlab/metrics/dashboard/url.rb
@@ -6,41 +6,36 @@ module Gitlab
module Dashboard
class Url
class << self
+ include Gitlab::Utils::StrongMemoize
# Matches urls for a metrics dashboard. This could be
# either the /metrics endpoint or the /metrics_dashboard
# endpoint.
#
# EX - https://<host>/<namespace>/<project>/environments/<env_id>/metrics
- def regex
- %r{
- (?<url>
- #{gitlab_pattern}
- #{project_pattern}
- (?:\/\-)?
- \/environments
- \/(?<environment>\d+)
- \/metrics
- #{query_pattern}
- #{anchor_pattern}
+ def metrics_regex
+ strong_memoize(:metrics_regex) do
+ regex_for_project_metrics(
+ %r{
+ /environments
+ /(?<environment>\d+)
+ /metrics
+ }x
)
- }x
+ end
end
# Matches dashboard urls for a Grafana embed.
#
# EX - https://<host>/<namespace>/<project>/grafana/metrics_dashboard
def grafana_regex
- %r{
- (?<url>
- #{gitlab_pattern}
- #{project_pattern}
- (?:\/\-)?
- \/grafana
- \/metrics_dashboard
- #{query_pattern}
- #{anchor_pattern}
+ strong_memoize(:grafana_regex) do
+ regex_for_project_metrics(
+ %r{
+ /grafana
+ /metrics_dashboard
+ }x
)
- }x
+ end
end
# Parses query params out from full url string into hash.
@@ -62,11 +57,24 @@ module Gitlab
private
- def gitlab_pattern
+ def regex_for_project_metrics(path_suffix_pattern)
+ %r{
+ (?<url>
+ #{gitlab_host_pattern}
+ #{project_path_pattern}
+ (?:/-)?
+ #{path_suffix_pattern}
+ #{query_pattern}
+ #{anchor_pattern}
+ )
+ }x
+ end
+
+ def gitlab_host_pattern
Regexp.escape(Gitlab.config.gitlab.url)
end
- def project_pattern
+ def project_path_pattern
"\/#{Project.reference_pattern}"
end
@@ -82,3 +90,5 @@ module Gitlab
end
end
end
+
+Gitlab::Metrics::Dashboard::Url.extend_if_ee('::EE::Gitlab::Metrics::Dashboard::Url')
diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb
index 58caacc1ad1..c036f188ea2 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- context 'Verify', :docker, quarantine: 'https://gitlab.com/gitlab-org/gitlab/issues/202149' do
+ context 'Verify', :docker, quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/issues/202149', type: :flaky } do
describe 'Pipeline creation and processing' do
let(:executor) { "qa-runner-#{Time.now.to_i}" }
let(:max_wait) { 30 }
diff --git a/spec/lib/feature/gitaly_spec.rb b/spec/lib/feature/gitaly_spec.rb
index 4e07acf9c1a..afb522d05e1 100644
--- a/spec/lib/feature/gitaly_spec.rb
+++ b/spec/lib/feature/gitaly_spec.rb
@@ -5,10 +5,6 @@ require 'spec_helper'
describe Feature::Gitaly do
let(:feature_flag) { "mep_mep" }
- before do
- stub_const("#{described_class}::SERVER_FEATURE_FLAGS", [feature_flag])
- end
-
describe ".enabled?" do
context 'when the gate is closed' do
before do
@@ -28,15 +24,13 @@ describe Feature::Gitaly do
end
describe ".server_feature_flags" do
- context 'when one flag is disabled' do
- before do
- stub_feature_flags(gitaly_mep_mep: false)
- end
+ before do
+ allow(Feature).to receive(:persisted_names).and_return(%w[gitaly_mep_mep foo])
+ end
- subject { described_class.server_feature_flags }
+ subject { described_class.server_feature_flags }
- it { is_expected.to be_a(Hash) }
- it { is_expected.to eq("gitaly-feature-mep-mep" => "false") }
- end
+ it { is_expected.to be_a(Hash) }
+ it { is_expected.to eq("gitaly-feature-mep-mep" => "true") }
end
end
diff --git a/spec/lib/gitlab/metrics/dashboard/url_spec.rb b/spec/lib/gitlab/metrics/dashboard/url_spec.rb
index daaf66cba46..d98aa5e3697 100644
--- a/spec/lib/gitlab/metrics/dashboard/url_spec.rb
+++ b/spec/lib/gitlab/metrics/dashboard/url_spec.rb
@@ -3,38 +3,6 @@
require 'spec_helper'
describe Gitlab::Metrics::Dashboard::Url do
- shared_examples_for 'a regex which matches the expected url' do
- it { is_expected.to be_a Regexp }
-
- it 'matches a metrics dashboard link with named params' do
- expect(subject).to match url
-
- subject.match(url) do |m|
- expect(m.named_captures).to eq expected_params
- end
- end
- end
-
- shared_examples_for 'does not match non-matching urls' do
- it 'does not match other gitlab urls that contain the term metrics' do
- url = Gitlab::Routing.url_helpers.active_common_namespace_project_prometheus_metrics_url('foo', 'bar', :json)
-
- expect(subject).not_to match url
- end
-
- it 'does not match other gitlab urls' do
- url = Gitlab.config.gitlab.url
-
- expect(subject).not_to match url
- end
-
- it 'does not match non-gitlab urls' do
- url = 'https://www.super_awesome_site.com/'
-
- expect(subject).not_to match url
- end
- end
-
describe '#regex' do
let(:url) do
Gitlab::Routing.url_helpers.metrics_namespace_project_environment_url(
@@ -59,10 +27,9 @@ describe Gitlab::Metrics::Dashboard::Url do
}
end
- subject { described_class.regex }
+ subject { described_class.metrics_regex }
- it_behaves_like 'a regex which matches the expected url'
- it_behaves_like 'does not match non-matching urls'
+ it_behaves_like 'regex which matches url when expected'
end
describe '#grafana_regex' do
@@ -89,15 +56,14 @@ describe Gitlab::Metrics::Dashboard::Url do
subject { described_class.grafana_regex }
- it_behaves_like 'a regex which matches the expected url'
- it_behaves_like 'does not match non-matching urls'
+ it_behaves_like 'regex which matches url when expected'
end
describe '#build_dashboard_url' do
it 'builds the url for the dashboard endpoint' do
url = described_class.build_dashboard_url('foo', 'bar', 1)
- expect(url).to match described_class.regex
+ expect(url).to match described_class.metrics_regex
end
end
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 3d817065963..1e558131dc6 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -1622,7 +1622,6 @@ describe Repository do
it 'executes the new Gitaly RPC' do
expect_any_instance_of(Gitlab::GitalyClient::OperationService).to receive(:rebase)
- expect_any_instance_of(Gitlab::GitalyClient::OperationService).not_to receive(:user_rebase)
repository.rebase(user, merge_request)
end
diff --git a/spec/requests/api/internal/base_spec.rb b/spec/requests/api/internal/base_spec.rb
index 677ea071012..733f0446cf4 100644
--- a/spec/requests/api/internal/base_spec.rb
+++ b/spec/requests/api/internal/base_spec.rb
@@ -313,6 +313,10 @@ describe API::Internal::Base do
end
context "git pull" do
+ before do
+ allow(Feature).to receive(:persisted_names).and_return(%w[gitaly_mep_mep])
+ end
+
it "has the correct payload" do
pull(key, project)
@@ -326,7 +330,7 @@ describe API::Internal::Base do
expect(json_response["gitaly"]["repository"]["relative_path"]).to eq(project.repository.gitaly_repository.relative_path)
expect(json_response["gitaly"]["address"]).to eq(Gitlab::GitalyClient.address(project.repository_storage))
expect(json_response["gitaly"]["token"]).to eq(Gitlab::GitalyClient.token(project.repository_storage))
- expect(json_response["gitaly"]["features"]).to eq('gitaly-feature-inforef-uploadpack-cache' => 'true', 'gitaly-feature-cache-invalidator' => 'true', 'gitaly-feature-commit-without-batch-check' => 'true', 'gitaly-feature-use-core-delta-islands' => 'true', 'gitaly-feature-use-git-protocol-v2' => 'true')
+ expect(json_response["gitaly"]["features"]).to eq('gitaly-feature-mep-mep' => 'true')
expect(user.reload.last_activity_on).to eql(Date.today)
end
end
@@ -346,7 +350,6 @@ describe API::Internal::Base do
expect(json_response["gitaly"]["repository"]["relative_path"]).to eq(project.repository.gitaly_repository.relative_path)
expect(json_response["gitaly"]["address"]).to eq(Gitlab::GitalyClient.address(project.repository_storage))
expect(json_response["gitaly"]["token"]).to eq(Gitlab::GitalyClient.token(project.repository_storage))
- expect(json_response["gitaly"]["features"]).to eq('gitaly-feature-inforef-uploadpack-cache' => 'true', 'gitaly-feature-cache-invalidator' => 'true', 'gitaly-feature-commit-without-batch-check' => 'true', 'gitaly-feature-use-core-delta-islands' => 'true', 'gitaly-feature-use-git-protocol-v2' => 'true')
expect(user.reload.last_activity_on).to be_nil
end
end
@@ -594,7 +597,6 @@ describe API::Internal::Base do
expect(json_response["gitaly"]["repository"]["relative_path"]).to eq(project.repository.gitaly_repository.relative_path)
expect(json_response["gitaly"]["address"]).to eq(Gitlab::GitalyClient.address(project.repository_storage))
expect(json_response["gitaly"]["token"]).to eq(Gitlab::GitalyClient.token(project.repository_storage))
- expect(json_response["gitaly"]["features"]).to eq('gitaly-feature-inforef-uploadpack-cache' => 'true', 'gitaly-feature-cache-invalidator' => 'true', 'gitaly-feature-commit-without-batch-check' => 'true', 'gitaly-feature-use-core-delta-islands' => 'true', 'gitaly-feature-use-git-protocol-v2' => 'true')
end
end
diff --git a/spec/support/shared_examples/metrics/url_shared_examples.rb b/spec/support/shared_examples/metrics/url_shared_examples.rb
new file mode 100644
index 00000000000..67742aecb87
--- /dev/null
+++ b/spec/support/shared_examples/metrics/url_shared_examples.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'regex which matches url when expected' do
+ it { is_expected.to be_a Regexp }
+
+ it 'matches a metrics dashboard link with named params' do
+ expect(subject).to match url
+
+ subject.match(url) do |m|
+ expect(m.named_captures).to eq expected_params
+ end
+ end
+
+ it 'does not match other gitlab urls that contain the term metrics' do
+ url = Gitlab::Routing.url_helpers.active_common_namespace_project_prometheus_metrics_url('foo', 'bar', :json)
+
+ expect(subject).not_to match url
+ end
+
+ it 'does not match other gitlab urls' do
+ url = Gitlab.config.gitlab.url
+
+ expect(subject).not_to match url
+ end
+
+ it 'does not match non-gitlab urls' do
+ url = 'https://www.super_awesome_site.com/'
+
+ expect(subject).not_to match url
+ end
+end
diff --git a/spec/views/shared/projects/_list.html.haml_spec.rb b/spec/views/shared/projects/_list.html.haml_spec.rb
new file mode 100644
index 00000000000..d6043921fc8
--- /dev/null
+++ b/spec/views/shared/projects/_list.html.haml_spec.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'shared/projects/_list' do
+ let(:group) { create(:group) }
+
+ before do
+ allow(view).to receive(:projects).and_return(projects)
+ allow(view).to receive(:project_list_cache_key).and_return('fake_cache_key')
+ end
+
+ context 'with projects' do
+ let(:projects) { build_stubbed_list(:project, 1) }
+
+ it 'renders the list of projects' do
+ render
+
+ projects.each do |project|
+ expect(rendered).to have_content(project.name)
+ end
+ end
+ end
+
+ context 'without projects' do
+ let(:projects) { [] }
+
+ context 'when @contributed_projects is set' do
+ context 'and is empty' do
+ before do
+ @contributed_projects = []
+ end
+
+ it 'renders a no-content message' do
+ render
+
+ expect(rendered).to have_content(s_('UserProfile|This user hasn\'t contributed to any projects'))
+ end
+ end
+ end
+
+ context 'when @starred_projects is set' do
+ context 'and is empty' do
+ before do
+ @starred_projects = []
+ end
+
+ it 'renders a no-content message' do
+ render
+
+ expect(rendered).to have_content(s_('UserProfile|This user hasn\'t starred any projects'))
+ end
+ end
+ end
+
+ context 'and without a special instance variable' do
+ context 'for an explore_page' do
+ before do
+ allow(view).to receive(:explore_page).and_return(true)
+ end
+
+ it 'renders a no-content message' do
+ render
+
+ expect(rendered).to have_content(s_('UserProfile|Explore public groups to find projects to contribute to.'))
+ end
+ end
+
+ context 'for a non-explore page' do
+ it 'renders a no-content message' do
+ render
+
+ expect(rendered).to have_content(s_('UserProfile|This user doesn\'t have any personal projects'))
+ end
+ end
+ end
+ end
+end