summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab/ci/rules.gitlab-ci.yml156
-rw-r--r--CHANGELOG.md6
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.checksum2
-rw-r--r--Gemfile.lock4
-rw-r--r--app/controllers/explore/groups_controller.rb4
-rw-r--r--app/services/clusters/applications/check_ingress_ip_address_service.rb46
-rw-r--r--app/services/event_create_service.rb10
-rw-r--r--app/views/projects/commits/_commit.html.haml4
-rw-r--r--app/workers/cluster_wait_for_ingress_ip_address_worker.rb9
-rw-r--r--config/feature_flags/development/cube_api_proxy.yml8
-rw-r--r--config/feature_flags/experiment/generic_explore_groups.yml8
-rw-r--r--data/deprecations/15-6-deprecate-post-api-v4-runner.yml21
-rw-r--r--doc/api/runners.md9
-rw-r--r--doc/architecture/blueprints/runner_tokens/index.md4
-rw-r--r--doc/tutorials/agile_sprint.md6
-rw-r--r--doc/update/deprecations.md26
-rw-r--r--doc/user/admin_area/license.md17
-rw-r--r--doc/user/application_security/container_scanning/index.md40
-rw-r--r--doc/user/clusters/agent/troubleshooting.md21
-rw-r--r--doc/user/packages/container_registry/index.md2
-rw-r--r--doc/user/project/repository/gpg_signed_commits/index.md16
-rw-r--r--doc/user/project/repository/x509_signed_commits/index.md5
-rw-r--r--lib/gitlab/github_import/importer/protected_branches_importer.rb6
-rw-r--r--lib/sbom/package_url.rb123
-rw-r--r--lib/sbom/package_url/decoder.rb174
-rw-r--r--lib/sbom/package_url/encoder.rb137
-rw-r--r--lib/sbom/package_url/string_utils.rb75
-rw-r--r--qa/Gemfile2
-rw-r--r--qa/Gemfile.lock4
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/ci_variable/raw_variables_defined_in_yaml_spec.rb142
-rw-r--r--spec/controllers/explore/groups_controller_spec.rb46
-rw-r--r--spec/lib/gitlab/github_import/importer/protected_branches_importer_spec.rb8
-rw-r--r--spec/lib/sbom/package_url/decoder_spec.rb119
-rw-r--r--spec/lib/sbom/package_url/encoder_spec.rb29
-rw-r--r--spec/lib/sbom/package_url_spec.rb144
-rw-r--r--spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb44
-rw-r--r--spec/services/event_create_service_spec.rb55
-rw-r--r--spec/support/shared_contexts/lib/sbom/package_url_shared_contexts.rb98
-rw-r--r--spec/workers/cluster_wait_for_ingress_ip_address_worker_spec.rb32
40 files changed, 1368 insertions, 296 deletions
diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml
index 9b3e2941f93..41c61483bc0 100644
--- a/.gitlab/ci/rules.gitlab-ci.yml
+++ b/.gitlab/ci/rules.gitlab-ci.yml
@@ -664,7 +664,8 @@
.shared:rules:update-gitaly-binaries-cache:
rules:
- <<: *if-merge-request-labels-update-caches
- - changes: *gitaly-patterns
+ - <<: *if-default-refs
+ changes: *gitaly-patterns
######################
# Build images rules #
@@ -693,8 +694,10 @@
- <<: *if-merge-request-labels-run-review-app
- <<: *if-auto-deploy-branches
- <<: *if-ruby3-branch
- - changes: *ci-build-images-patterns
- - changes: *code-qa-patterns
+ - <<: *if-default-refs
+ changes: *ci-build-images-patterns
+ - <<: *if-default-refs
+ changes: *code-qa-patterns
#################
# Caching rules #
@@ -856,15 +859,20 @@
- <<: *if-merge-request-targeting-stable-branch
- <<: *if-merge-request-labels-run-review-app
- <<: *if-auto-deploy-branches
- - changes: *ci-build-images-patterns
- - changes: *code-qa-patterns
- - changes: *workhorse-patterns
+ - <<: *if-default-refs
+ changes: *ci-build-images-patterns
+ - <<: *if-default-refs
+ changes: *code-qa-patterns
+ - <<: *if-default-refs
+ changes: *workhorse-patterns
.frontend:rules:compile-test-assets:
rules:
- <<: *if-merge-request-labels-run-all-rspec
- - changes: *code-backstage-qa-patterns
- - changes: *workhorse-patterns
+ - <<: *if-default-refs
+ changes: *code-backstage-qa-patterns
+ - <<: *if-default-refs
+ changes: *workhorse-patterns
.frontend:rules:compile-test-assets-as-if-foss:
rules:
@@ -872,14 +880,18 @@
when: never
- <<: *if-merge-request-labels-as-if-foss
- <<: *if-merge-request-labels-run-all-rspec
- - changes: *code-backstage-qa-patterns
- - changes: *startup-css-patterns
- - changes: *workhorse-patterns
+ - <<: *if-default-refs
+ changes: *code-backstage-qa-patterns
+ - <<: *if-default-refs
+ changes: *startup-css-patterns
+ - <<: *if-default-refs
+ changes: *workhorse-patterns
.frontend:rules:default-frontend-jobs:
rules:
- <<: *if-merge-request-labels-run-all-rspec
- - changes: *code-backstage-patterns
+ - <<: *if-default-refs
+ changes: *code-backstage-patterns
.frontend:rules:default-frontend-jobs-as-if-foss:
rules:
@@ -1124,7 +1136,8 @@
###############
.rails:rules:setup-test-env:
rules:
- - changes: *setup-test-env-patterns
+ - <<: *if-default-refs
+ changes: *setup-test-env-patterns
- <<: *if-merge-request-labels-run-all-rspec
.rails:rules:single-db:
@@ -1156,7 +1169,8 @@
changes: *db-patterns
- <<: *if-merge-request-not-approved
when: never
- - changes: *db-patterns
+ - <<: *if-default-refs
+ changes: *db-patterns
.rails:rules:ee-and-foss-migration:minimal:
rules:
@@ -1189,7 +1203,8 @@
- <<: *if-fork-merge-request
when: never
- !reference [".rails:rules:ee-and-foss-default-rules", rules]
- - changes: *backend-patterns
+ - <<: *if-default-refs
+ changes: *backend-patterns
.rails:rules:ee-and-foss-unit:minimal:
rules:
@@ -1205,7 +1220,8 @@
- <<: *if-fork-merge-request
when: never
- !reference [".rails:rules:ee-and-foss-default-rules", rules]
- - changes: *backend-patterns
+ - <<: *if-default-refs
+ changes: *backend-patterns
.rails:rules:ee-and-foss-integration:minimal:
rules:
@@ -1221,7 +1237,8 @@
- <<: *if-fork-merge-request
when: never
- !reference [".rails:rules:system-default-rules", rules]
- - changes: *code-backstage-patterns
+ - <<: *if-default-refs
+ changes: *code-backstage-patterns
.rails:rules:ee-and-foss-system:minimal:
rules:
@@ -1235,11 +1252,13 @@
- <<: *if-merge-request-labels-run-all-rspec
- <<: *if-merge-request
changes: *backend-patterns
- - changes: *core-backend-patterns
+ - <<: *if-default-refs
+ changes: *core-backend-patterns
.rails:rules:code-backstage-qa:
rules:
- - changes: *code-backstage-qa-patterns
+ - <<: *if-default-refs
+ changes: *code-backstage-qa-patterns
- <<: *if-merge-request-labels-run-all-rspec
.rails:rules:ee-only-migration:
@@ -1259,7 +1278,8 @@
changes: *db-patterns
- <<: *if-merge-request-not-approved
when: never
- - changes: *db-patterns
+ - <<: *if-default-refs
+ changes: *db-patterns
.rails:rules:ee-only-migration:minimal:
rules:
@@ -1280,7 +1300,8 @@
- <<: *if-fork-merge-request
when: never
- !reference [".rails:rules:ee-and-foss-default-rules", rules]
- - changes: *backend-patterns
+ - <<: *if-default-refs
+ changes: *backend-patterns
.rails:rules:ee-only-unit:minimal:
rules:
@@ -1300,7 +1321,8 @@
- <<: *if-fork-merge-request
when: never
- !reference [".rails:rules:ee-and-foss-default-rules", rules]
- - changes: *backend-patterns
+ - <<: *if-default-refs
+ changes: *backend-patterns
.rails:rules:ee-only-integration:minimal:
rules:
@@ -1320,7 +1342,8 @@
- <<: *if-fork-merge-request
when: never
- !reference [".rails:rules:system-default-rules", rules]
- - changes: *code-backstage-patterns
+ - <<: *if-default-refs
+ changes: *code-backstage-patterns
.rails:rules:ee-only-system:minimal:
rules:
@@ -1434,7 +1457,8 @@
.rails:rules:ee-and-foss-db-library-code:
rules:
- - changes: *db-library-patterns
+ - <<: *if-default-refs
+ changes: *db-library-patterns
- <<: *if-merge-request-labels-run-all-rspec
.rails:rules:ee-mr-and-default-branch-only:
@@ -1450,8 +1474,10 @@
.rails:rules:detect-tests:
rules:
- <<: *if-merge-request-labels-run-all-rspec
- - changes: *code-backstage-qa-patterns
- - changes: *workhorse-patterns
+ - <<: *if-default-refs
+ changes: *code-backstage-qa-patterns
+ - <<: *if-default-refs
+ changes: *workhorse-patterns
.rails:rules:detect-previous-failed-tests:
rules:
@@ -1542,7 +1568,8 @@
rules:
- <<: *if-not-ee
when: never
- - changes: *code-backstage-patterns
+ - <<: *if-default-refs
+ changes: *code-backstage-patterns
.rails:rules:flaky-tests-report:
rules:
@@ -1560,38 +1587,51 @@
.static-analysis:rules:static-analysis:
rules:
- - changes: *code-backstage-qa-patterns
- - changes: *static-analysis-patterns
+ - <<: *if-default-refs
+ changes: *code-backstage-qa-patterns
+ - <<: *if-default-refs
+ changes: *static-analysis-patterns
.static-analysis:rules:static-verification-with-database:
rules:
- - changes: *code-backstage-qa-patterns
+ - <<: *if-default-refs
+ changes: *code-backstage-qa-patterns
.static-analysis:rules:rubocop:
rules:
- - changes: *rubocop-patterns
+ - <<: *if-default-refs
+ changes: *rubocop-patterns
variables:
RUN_ALL_RUBOCOP: "true"
- - changes: *code-backstage-qa-patterns
+ - <<: *if-default-refs
+ changes: *code-backstage-qa-patterns
.static-analysis:rules:qa:metadata-lint:
rules:
- - changes: *qa-patterns
- - changes: [".gitlab/ci/static-analysis.gitlab-ci.yml"]
+ - <<: *if-default-refs
+ changes: *qa-patterns
+ - <<: *if-default-refs
+ changes: [".gitlab/ci/static-analysis.gitlab-ci.yml"]
.static-analysis:rules:haml-lint:
rules:
- - changes: *rubocop-patterns
- - changes: *static-analysis-patterns
- - changes: *code-backstage-qa-patterns
+ - <<: *if-default-refs
+ changes: *rubocop-patterns
+ - <<: *if-default-refs
+ changes: *static-analysis-patterns
+ - <<: *if-default-refs
+ changes: *code-backstage-qa-patterns
.static-analysis:rules:haml-lint-ee:
rules:
- <<: *if-not-ee
when: never
- - changes: *rubocop-patterns
- - changes: *static-analysis-patterns
- - changes: *code-backstage-qa-patterns
+ - <<: *if-default-refs
+ changes: *rubocop-patterns
+ - <<: *if-default-refs
+ changes: *static-analysis-patterns
+ - <<: *if-default-refs
+ changes: *code-backstage-qa-patterns
.static-analysis:rules:static-analysis-as-if-foss:
rules:
@@ -1709,7 +1749,8 @@
when: never
- if: $SAST_EXCLUDED_ANALYZERS =~ /brakeman/
when: never
- - changes:
+ - <<: *if-default-refs
+ changes:
- '**/*.rb'
- '**/Gemfile'
@@ -1719,7 +1760,8 @@
when: never
- if: $SAST_EXCLUDED_ANALYZERS =~ /semgrep/
when: never
- - changes:
+ - <<: *if-default-refs
+ changes:
- '**/*.py'
- '**/*.js'
- '**/*.jsx'
@@ -1735,7 +1777,8 @@
when: never
# Scan each commit on master to feed the Vulnerability Reports with detected secrets
- <<: *if-default-branch-refs
- - changes: *code-backstage-qa-patterns
+ - <<: *if-default-refs
+ changes: *code-backstage-qa-patterns
.reports:rules:gemnasium-dependency_scanning:
rules:
@@ -1743,7 +1786,8 @@
when: never
# Run Dependency Scanning on master until https://gitlab.com/gitlab-org/gitlab/-/issues/361657 is resolved
- <<: *if-default-branch-refs
- - changes: *dependency-patterns
+ - <<: *if-default-refs
+ changes: *dependency-patterns
.reports:rules:gemnasium-python-dependency_scanning:
rules:
@@ -1751,7 +1795,8 @@
when: never
# Run Dependency Scanning on master until https://gitlab.com/gitlab-org/gitlab/-/issues/361657 is resolved
- <<: *if-default-branch-refs
- - changes: *python-patterns
+ - <<: *if-default-refs
+ changes: *python-patterns
.reports:rules:yarn-audit-dependency_scanning:
rules:
@@ -1759,7 +1804,8 @@
when: never
# Run Dependency Scanning on master until https://gitlab.com/gitlab-org/gitlab/-/issues/361657 is resolved
- <<: *if-default-branch-refs
- - changes: *nodejs-patterns
+ - <<: *if-default-refs
+ changes: *nodejs-patterns
.reports:rules:schedule-dast:
rules:
@@ -1793,7 +1839,8 @@
rules:
- if: '$LICENSE_MANAGEMENT_DISABLED || $GITLAB_FEATURES !~ /\blicense_scanning\b/'
when: never
- - changes: *dependency-patterns
+ - <<: *if-default-refs
+ changes: *dependency-patterns
################
# Review rules #
@@ -1980,7 +2027,8 @@
- <<: *if-not-ee
when: never
- <<: *if-dot-com-ee-schedule-maintenance
- - changes:
+ - <<: *if-default-refs
+ changes:
- ".gitlab/ci/setup.gitlab-ci.yml"
- ".gitlab/ci/test-metadata.gitlab-ci.yml"
- "scripts/rspec_helpers.sh"
@@ -1990,8 +2038,10 @@
#######################
.test-metadata:rules:retrieve-tests-metadata:
rules:
- - changes: *code-backstage-patterns
- - changes: *workhorse-patterns
+ - <<: *if-default-refs
+ changes: *code-backstage-patterns
+ - <<: *if-default-refs
+ changes: *workhorse-patterns
- <<: *if-merge-request-labels-run-all-rspec
.test-metadata:rules:update-tests-metadata:
@@ -1999,7 +2049,8 @@
- <<: *if-not-ee
when: never
- <<: *if-dot-com-ee-schedule-maintenance
- - changes:
+ - <<: *if-default-refs
+ changes:
- ".gitlab/ci/test-metadata.gitlab-ci.yml"
- "scripts/rspec_helpers.sh"
@@ -2008,7 +2059,8 @@
###################
.workhorse:rules:workhorse:
rules:
- - changes: *workhorse-patterns
+ - <<: *if-default-refs
+ changes: *workhorse-patterns
###################
# yaml-lint rules #
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 12dee8f01ba..fc59fa0e101 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,12 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 15.5.3 (2022-11-07)
+
+### Fixed (1 change)
+
+- [Fix Opensearch compatibility check](gitlab-org/gitlab@c7094017eb6fae71c0b8441a04f6927ed69025ea) ([merge request](gitlab-org/gitlab!103157)) **GitLab Enterprise Edition**
+
## 15.5.2 (2022-11-02)
### Security (11 changes)
diff --git a/Gemfile b/Gemfile
index 95f39e2bd3b..6c43eab3dee 100644
--- a/Gemfile
+++ b/Gemfile
@@ -151,7 +151,7 @@ gem 'fog-local', '~> 0.6'
gem 'fog-openstack', '~> 1.0'
gem 'fog-rackspace', '~> 0.1.1'
gem 'fog-aliyun', '~> 0.3'
-gem 'gitlab-fog-azure-rm', '~> 1.3.0', require: 'fog/azurerm'
+gem 'gitlab-fog-azure-rm', '~> 1.4.0', require: 'fog/azurerm'
# for Google storage
gem 'google-api-client', '~> 0.33'
diff --git a/Gemfile.checksum b/Gemfile.checksum
index 5fe80ce1593..bdc06979e7c 100644
--- a/Gemfile.checksum
+++ b/Gemfile.checksum
@@ -203,7 +203,7 @@
{"name":"gitlab-chronic","version":"0.10.5","platform":"ruby","checksum":"f80f18dc699b708870a80685243331290bc10cfeedb6b99c92219722f729c875"},
{"name":"gitlab-dangerfiles","version":"3.6.1","platform":"ruby","checksum":"f7b69b093d52acb89095d411cb7b8849f5f3b9e76f8baa4c99b5671f1564865f"},
{"name":"gitlab-experiment","version":"0.7.1","platform":"ruby","checksum":"166dddb3aa83428bcaa93c35684ed01dc4d61f321fd2ae40b020806dc54a7824"},
-{"name":"gitlab-fog-azure-rm","version":"1.3.0","platform":"ruby","checksum":"2fef5317d6515f95f803099afa860fe3019ce6e1907bf49f66b5e06468a617b5"},
+{"name":"gitlab-fog-azure-rm","version":"1.4.0","platform":"ruby","checksum":"af4163c32b028aa5208814a3f4765a5817d50527e6c61931f766bf18a2e0eb7e"},
{"name":"gitlab-labkit","version":"0.28.0","platform":"ruby","checksum":"a7ebf52336566f7607d280056acd64f390c9991f152fc3d6b1dd966a372d5654"},
{"name":"gitlab-license","version":"2.2.1","platform":"ruby","checksum":"39fcf6be8b2887df8afe01b5dcbae8d08b7c5d937ff56b0fb40484a8c4f02d30"},
{"name":"gitlab-mail_room","version":"0.0.9","platform":"ruby","checksum":"6700374b5c0aa9d9ad4e711aeb677f0b7d415a6d01d3baa699efab25349d851c"},
diff --git a/Gemfile.lock b/Gemfile.lock
index eb3f1002b73..8a3b5097747 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -561,7 +561,7 @@ GEM
gitlab-experiment (0.7.1)
activesupport (>= 3.0)
request_store (>= 1.0)
- gitlab-fog-azure-rm (1.3.0)
+ gitlab-fog-azure-rm (1.4.0)
azure-storage-blob (~> 2.0)
azure-storage-common (~> 2.0)
fog-core (= 2.1.0)
@@ -1628,7 +1628,7 @@ DEPENDENCIES
gitlab-chronic (~> 0.10.5)
gitlab-dangerfiles (~> 3.6.1)
gitlab-experiment (~> 0.7.1)
- gitlab-fog-azure-rm (~> 1.3.0)
+ gitlab-fog-azure-rm (~> 1.4.0)
gitlab-labkit (~> 0.28.0)
gitlab-license (~> 2.2.1)
gitlab-mail_room (~> 0.0.9)
diff --git a/app/controllers/explore/groups_controller.rb b/app/controllers/explore/groups_controller.rb
index 97791b43d41..ac355b861b3 100644
--- a/app/controllers/explore/groups_controller.rb
+++ b/app/controllers/explore/groups_controller.rb
@@ -7,6 +7,8 @@ class Explore::GroupsController < Explore::ApplicationController
urgency :low
def index
- render_group_tree GroupsFinder.new(current_user).execute
+ user = Feature.enabled?(:generic_explore_groups, current_user, type: :experiment) ? nil : current_user
+
+ render_group_tree GroupsFinder.new(user).execute
end
end
diff --git a/app/services/clusters/applications/check_ingress_ip_address_service.rb b/app/services/clusters/applications/check_ingress_ip_address_service.rb
deleted file mode 100644
index e254a0358a0..00000000000
--- a/app/services/clusters/applications/check_ingress_ip_address_service.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# frozen_string_literal: true
-
-module Clusters
- module Applications
- class CheckIngressIpAddressService < BaseHelmService
- include Gitlab::Utils::StrongMemoize
-
- Error = Class.new(StandardError)
-
- LEASE_TIMEOUT = 15.seconds.to_i
-
- def execute
- return if app.external_ip
- return if app.external_hostname
- return unless try_obtain_lease
-
- app.external_ip = ingress_ip if ingress_ip
- app.external_hostname = ingress_hostname if ingress_hostname
-
- app.save! if app.changed?
- end
-
- private
-
- def try_obtain_lease
- Gitlab::ExclusiveLease
- .new("check_ingress_ip_address_service:#{app.id}", timeout: LEASE_TIMEOUT)
- .try_obtain
- end
-
- def ingress_ip
- ingress_service&.ip
- end
-
- def ingress_hostname
- ingress_service&.hostname
- end
-
- def ingress_service
- strong_memoize(:ingress_service) do
- app.ingress_service.status.loadBalancer.ingress&.first
- end
- end
- end
- end
-end
diff --git a/app/services/event_create_service.rb b/app/services/event_create_service.rb
index 019246dfc9f..193b1b6f150 100644
--- a/app/services/event_create_service.rb
+++ b/app/services/event_create_service.rb
@@ -213,7 +213,15 @@ class EventCreateService
namespace = project.namespace
if Feature.enabled?(:route_hll_to_snowplow, namespace)
- Gitlab::Tracking.event(self.class.to_s, 'action_active_users_project_repo', namespace: namespace, user: current_user, project: project)
+ Gitlab::Tracking.event(
+ self.class.to_s,
+ :push,
+ label: 'usage_activity_by_stage_monthly.create.action_monthly_active_users_project_repo',
+ namespace: namespace,
+ user: current_user,
+ project: project,
+ context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: 'action_active_users_project_repo').to_context]
+ )
end
Users::LastPushEventService.new(current_user)
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index bf6b628dd36..b5481f19352 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -34,8 +34,8 @@
&middot;
= commit.short_id
- if commit.description? && collapsible
- %button.gl-button.btn.btn-default.button-ellipsis-horizontal.btn-sm.gl-ml-2.text-expander.js-toggle-button{ data: { toggle: 'tooltip', container: 'body' }, :title => _("Toggle commit description"), aria: { label: _("Toggle commit description") } }
- = sprite_icon('ellipsis_h', size: 12)
+ = render Pajamas::ButtonComponent.new(icon: 'ellipsis_h',
+ button_options: { class: 'button-ellipsis-horizontal text-expander js-toggle-button', data: { toggle: 'tooltip', container: 'body' }, :title => _("Toggle commit description"), aria: { label: _("Toggle commit description") }})
.committer
- commit_author_link = commit_author_link(commit, avatar: false, size: 24)
diff --git a/app/workers/cluster_wait_for_ingress_ip_address_worker.rb b/app/workers/cluster_wait_for_ingress_ip_address_worker.rb
index 561e72562e9..8983942c0fb 100644
--- a/app/workers/cluster_wait_for_ingress_ip_address_worker.rb
+++ b/app/workers/cluster_wait_for_ingress_ip_address_worker.rb
@@ -1,5 +1,8 @@
# frozen_string_literal: true
+# DEPRECATED
+#
+# To be removed by https://gitlab.com/gitlab-org/gitlab/-/issues/366573
class ClusterWaitForIngressIpAddressWorker # rubocop:disable Scalability/IdempotentWorker
include ApplicationWorker
@@ -12,9 +15,5 @@ class ClusterWaitForIngressIpAddressWorker # rubocop:disable Scalability/Idempot
worker_has_external_dependencies!
loggable_arguments 0
- def perform(app_name, app_id)
- find_application(app_name, app_id) do |app|
- Clusters::Applications::CheckIngressIpAddressService.new(app).execute
- end
- end
+ def perform(app_name, app_id); end
end
diff --git a/config/feature_flags/development/cube_api_proxy.yml b/config/feature_flags/development/cube_api_proxy.yml
new file mode 100644
index 00000000000..06dcefb1303
--- /dev/null
+++ b/config/feature_flags/development/cube_api_proxy.yml
@@ -0,0 +1,8 @@
+---
+name: cube_api_proxy
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96250
+rollout_issue_url:
+milestone: '15.4'
+type: development
+group: group::product_analytics
+default_enabled: false
diff --git a/config/feature_flags/experiment/generic_explore_groups.yml b/config/feature_flags/experiment/generic_explore_groups.yml
new file mode 100644
index 00000000000..635af65f000
--- /dev/null
+++ b/config/feature_flags/experiment/generic_explore_groups.yml
@@ -0,0 +1,8 @@
+---
+name: generic_explore_groups
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103019"
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/381564
+milestone: '15.6'
+type: experiment
+group: group::source code
+default_enabled: false
diff --git a/data/deprecations/15-6-deprecate-post-api-v4-runner.yml b/data/deprecations/15-6-deprecate-post-api-v4-runner.yml
new file mode 100644
index 00000000000..ca7d19fe353
--- /dev/null
+++ b/data/deprecations/15-6-deprecate-post-api-v4-runner.yml
@@ -0,0 +1,21 @@
+- name: "`POST /api/v4/runners` method to register runners" # (required) The name of the feature to be deprecated
+ announcement_milestone: "15.6" # (required) The milestone when this feature was first announced as deprecated.
+ announcement_date: "2022-11-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
+ removal_date: "2023-05-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
+ reporter: pedropombeiro # (required) GitLab username of the person reporting the deprecation
+ stage: Verify # (required) String value of the stage that the feature was created in. e.g., Growth
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/379743 # (required) Link to the deprecation issue in GitLab
+ body: | # (required) Do not modify this line, instead modify the lines below.
+ The `POST` method operation on the `/api/v4/runners` endpoint is deprecated.
+ This endpoint and method [registers](https://docs.gitlab.com/ee/api/runners.html#register-a-new-runner) a runner
+ with a GitLab instance at the instance, group, or project level through the API. We plan to remove this endpoint
+ and method in GitLab 16.0, and introduce a new
+ [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/).
+ This new architecture introduces a new method for registering runners and eliminates the legacy
+ [runner registration token](https://docs.gitlab.com/ee/security/token_overview.html#runner-registration-tokens).
+ end_of_support_milestone: "16.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
+ end_of_support_date: "2023-05-22" # (optional) The date of the milestone release when support for this feature will end.
+ tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
+ documentation_url: https://docs.gitlab.com/ee/api/runners.html#register-a-new-runner # (optional) This is a link to the current documentation page
diff --git a/doc/api/runners.md b/doc/api/runners.md
index b2377fda7d6..f690e0cb9c1 100644
--- a/doc/api/runners.md
+++ b/doc/api/runners.md
@@ -15,7 +15,7 @@ There are two tokens to take into account when connecting a runner with GitLab.
| Token | Description |
| ----- | ----------- |
| Registration token | Token used to [register the runner](https://docs.gitlab.com/runner/register/). It can be [obtained through GitLab](../ci/runners/index.md). |
-| Authentication token | Token used to authenticate the runner with the GitLab instance. It is obtained automatically when you [register a runner](https://docs.gitlab.com/runner/register/) or by the Runner API when you manually [register a runner](#register-a-new-runner) or [reset the authentication token](#reset-runners-authentication-token-by-using-the-runner-id). |
+| Authentication token | Token used to authenticate the runner with the GitLab instance. It is obtained automatically when you [register a runner](https://docs.gitlab.com/runner/register/) or by the Runner API when you manually [register a runner](#register-a-new-runner-deprecated) or [reset the authentication token](#reset-runners-authentication-token-by-using-the-runner-id). |
Here's an example of how the two tokens are used in runner registration:
@@ -640,7 +640,12 @@ Example response:
]
```
-## Register a new runner
+## Register a new runner (deprecated)
+
+> [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102579) in GitLab 15.6.
+
+WARNING:
+This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102579) in GitLab 15.6 and is planned for removal in 16.0. This change is a breaking change.
Register a new runner for the instance.
diff --git a/doc/architecture/blueprints/runner_tokens/index.md b/doc/architecture/blueprints/runner_tokens/index.md
index a95fab08833..6ae139344bb 100644
--- a/doc/architecture/blueprints/runner_tokens/index.md
+++ b/doc/architecture/blueprints/runner_tokens/index.md
@@ -11,7 +11,7 @@ description: 'Next Runner Token Architecture'
GitLab Runner is a core component of GitLab CI/CD that runs
CI/CD jobs in a reliable and concurrent environment. Ever since the beginnings
-of the service as a Ruby program, runners are registered in a GitLab instance with
+of the service as a Ruby program, runners are registered in a GitLab instance with
a registration token - a randomly generated string of text. The registration token is unique for its given scope
(instance, group, or project). The registration token proves that the party that registers the runner has
administrator access to the instance, group, or project to which the runner is registered.
@@ -197,7 +197,7 @@ using PAT tokens for example - such that every runner is associated with an owne
| GitLab Runner | `16.0` | Remove `register` command and support for `POST /runners` endpoint. |
| GitLab Rails app | `16.0` | Remove legacy UI showing registration with a registration token. |
| GitLab Rails app | `16.0` | Create database migrations to remove settings from `application_settings` and `namaspace_settings` tables. |
-| GitLab Rails app | `16.0` | Make [`POST /api/v4/runners` endpoint](../../../api/runners.md#register-a-new-runner) permanently return `410 Gone`. A future v5 version of the API would return `404 Not Found`. |
+| GitLab Rails app | `16.0` | Make [`POST /api/v4/runners` endpoint](../../../api/runners.md#register-a-new-runner-deprecated) permanently return `410 Gone`. A future v5 version of the API would return `404 Not Found`. |
| GitLab Rails app | `16.0` | Start refusing job requests that don't include a unique ID. |
## Status
diff --git a/doc/tutorials/agile_sprint.md b/doc/tutorials/agile_sprint.md
index 69fa9c8fb07..eb6aed92467 100644
--- a/doc/tutorials/agile_sprint.md
+++ b/doc/tutorials/agile_sprint.md
@@ -4,12 +4,12 @@ group: Tutorials
info: For assistance with this tutorial, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments-to-other-projects-and-subjects.
---
-# Use GitLab to run an agile iteration
+# Use GitLab to run an Agile iteration
-To run an agile development iteration in GitLab, you use multiple GitLab features
+To run an Agile development iteration in GitLab, you use multiple GitLab features
that work together.
-To run an agile iteration from GitLab:
+To run an Agile iteration from GitLab:
1. Create a group.
1. Create a project.
diff --git a/doc/update/deprecations.md b/doc/update/deprecations.md
index 8e4da6c6bb5..7320f3b0eb0 100644
--- a/doc/update/deprecations.md
+++ b/doc/update/deprecations.md
@@ -45,6 +45,32 @@ sole discretion of GitLab Inc.
<div class="announcement-milestone">
+## Announced in 15.6
+
+<div class="deprecation removal-160 breaking-change">
+
+### `POST /api/v4/runners` method to register runners
+
+End of Support: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)<br />
+Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)
+
+WARNING:
+This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
+Review the details carefully before upgrading.
+
+The `POST` method operation on the `/api/v4/runners` endpoint is deprecated.
+This endpoint and method [registers](https://docs.gitlab.com/ee/api/runners.html#register-a-new-runner) a runner
+with a GitLab instance at the instance, group, or project level through the API. We plan to remove this endpoint
+and method in GitLab 16.0, and introduce a new
+[GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/).
+This new architecture introduces a new method for registering runners and eliminates the legacy
+[runner registration token](https://docs.gitlab.com/ee/security/token_overview.html#runner-registration-tokens).
+
+</div>
+</div>
+
+<div class="announcement-milestone">
+
## Announced in 15.5
<div class="deprecation removal-157 breaking-change">
diff --git a/doc/user/admin_area/license.md b/doc/user/admin_area/license.md
index f2e2bd1a5f1..ec430ea0128 100644
--- a/doc/user/admin_area/license.md
+++ b/doc/user/admin_area/license.md
@@ -64,6 +64,17 @@ This error occurs when you use an activation code to activate your instance, but
You may have connectivity issues due to the following reasons:
-- **You have an offline environment**: Configure your setup to allow connection to GitLab servers. If connection to GitLab servers is not possible, contact your Sales Representative to request a license key. You can also contact [GitLab support](https://about.gitlab.com/support/#contact-support) if you need help finding out your Sales Representative.
-- **Firewall settings**: Enable an encrypted HTTPS connection from your GitLab instance to `customers.gitlab.com` (with IP addresses 104.18.26.123 and 104.18.27.123) on port 443.
-- **Customers Portal is not operational**: To check for performance or service disruptions, check the Customers Portal [status](https://status.gitlab.com/).
+- **You have an offline environment**:
+ - Configure your setup to allow connection to GitLab servers. If connection to GitLab servers is not possible, contact your Sales Representative to request a license key. You can also contact [GitLab support](https://about.gitlab.com/support/#contact-support) if you need help finding your Sales Representative.
+- **Customers Portal is not operational**:
+ - To check for performance or service disruptions, check the Customers Portal [status](https://status.gitlab.com/).
+- **Firewall settings**:
+ - Check if your GitLab instance has an encrypted connection to `customers.gitlab.com` (with IP addresses 104.18.26.123 and 104.18.27.123) on port 443:
+
+ ```shell
+ curl --verbose "telnet://customers.gitlab.com/"
+ ```
+
+ - If the curl command returns a failure, either:
+ - [Configure a proxy](https://docs.gitlab.com/omnibus/settings/environment-variables.html) in `gitlab.rb` to point to your server.
+ - Contact your network administrator to make changes to the proxy.
diff --git a/doc/user/application_security/container_scanning/index.md b/doc/user/application_security/container_scanning/index.md
index f6bd6157a28..ede945f5888 100644
--- a/doc/user/application_security/container_scanning/index.md
+++ b/doc/user/application_security/container_scanning/index.md
@@ -359,46 +359,12 @@ The container-scanning analyzer can use different scanners, depending on the val
The following options are available:
-| Scanner name | `CS_ANALYZER_IMAGE` |
-| ------------ | ------------------- |
-| Default ([Trivy](https://github.com/aquasecurity/trivy)) | `registry.gitlab.com/security-products/container-scanning:5` |
+| Scanner name | `CS_ANALYZER_IMAGE` |
+|----------------------------------------------------------|--------------------------------------------------------------------|
+| Default ([Trivy](https://github.com/aquasecurity/trivy)) | `registry.gitlab.com/security-products/container-scanning:5` |
| [Grype](https://github.com/anchore/grype) | `registry.gitlab.com/security-products/container-scanning/grype:5` |
| Trivy | `registry.gitlab.com/security-products/container-scanning/trivy:5` |
-If you're migrating from a GitLab 13.x release to a GitLab 14.x release and have customized the
-`container_scanning` job or its CI variables, you might need to perform these migration steps in
-your CI file:
-
-1. Remove these variables:
-
- - `CS_MAJOR_VERSION`
- - `CS_PROJECT`
- - `SECURE_ANALYZERS_PREFIX`
-
-1. Review the `CS_ANALYZER_IMAGE` variable. It no longer depends on the variables above and its new
- default value is `registry.gitlab.com/security-products/container-scanning:5`. If you have an
- offline environment, see
- [Running container scanning in an offline environment](#running-container-scanning-in-an-offline-environment).
-
-1. If present, remove the `.cs_common` and `container_scanning_new` configuration sections.
-
-1. If the `container_scanning` section is present, it's safer to create one from scratch based on
- the new version of the [`Container-Scanning.gitlab-ci.yml` template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml).
- Once finished, it should not have any variables that are only applicable to Klar or Clair. For a
- complete list of supported variables, see [available variables](#available-cicd-variables).
-
-1. Make any [necessary customizations](#customizing-the-container-scanning-settings)
- to the chosen scanner. We recommend that you minimize such customizations, as they might require
- changes in future GitLab major releases.
-
-1. Trigger a new run of a pipeline that includes the `container_scanning` job. Inspect the job
- output and ensure that the log messages do not mention Clair.
-
-NOTE:
-Prior to the GitLab 14.0 release, any variable defined under the scope `container_scanning` is not
-considered for scanners other than Clair. In GitLab 14.0 and later, all variables can be defined
-either as a global variable or under `container_scanning`.
-
### Setting the default branch image
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/338877) in GitLab 14.5.
diff --git a/doc/user/clusters/agent/troubleshooting.md b/doc/user/clusters/agent/troubleshooting.md
index 0c26c533cc3..2edb2ecbc8d 100644
--- a/doc/user/clusters/agent/troubleshooting.md
+++ b/doc/user/clusters/agent/troubleshooting.md
@@ -208,3 +208,24 @@ kubectl delete jobs -l app.kubernetes.io/managed-by=starboard -n gitlab-agent
```
[We're working on making the cleanup of these jobs more robust.](https://gitlab.com/gitlab-org/gitlab/-/issues/362016)
+
+## Inventory policy prevented actuation (strategy: Apply, status: Empty, policy: MustMatch)
+
+```json
+{
+ "error":"inventory policy prevented actuation (strategy: Apply, status: Empty, policy: MustMatch)",
+ "group":"networking.k8s.io",
+ "kind":"Deployment",
+ "name":"resource-name",
+ "namespace":"namespace",
+ "status":"Skipped",
+ "timestamp":"2022-10-29T15:34:21Z",
+ "type":"apply"
+}
+```
+
+This error occurs when the GitLab agent tries to update an object and the object doesn't have the required annotations. To fix this error, you can:
+
+- Add the required annotations manually.
+- Delete the object and let the agent recreate it.
+- Change your [`inventory_policy`](../../infrastructure/clusters/deploy/inventory_object.md#inventory_policy-options) setting.
diff --git a/doc/user/packages/container_registry/index.md b/doc/user/packages/container_registry/index.md
index 779af944674..65e7918d827 100644
--- a/doc/user/packages/container_registry/index.md
+++ b/doc/user/packages/container_registry/index.md
@@ -556,7 +556,7 @@ this setting. However, disabling the Container Registry disables all Container R
## Troubleshooting the GitLab Container Registry
-## Migrating OCI container images to GitLab Container Registry
+### Migrating OCI container images to GitLab Container Registry
Migrating built container images to the GitLab registry is not a current feature. However, an [epic](https://gitlab.com/groups/gitlab-org/-/epics/5210) is open to track the work on this feature.
diff --git a/doc/user/project/repository/gpg_signed_commits/index.md b/doc/user/project/repository/gpg_signed_commits/index.md
index 910b09449d8..a1f57f51f26 100644
--- a/doc/user/project/repository/gpg_signed_commits/index.md
+++ b/doc/user/project/repository/gpg_signed_commits/index.md
@@ -253,3 +253,19 @@ If you must unverify both future and past commits,
- [OpenPGP Best Practices](https://riseup.net/en/security/message-security/openpgp/best-practices)
- [Creating a new GPG key with subkeys](https://www.void.gr/kargig/blog/2013/12/02/creating-a-new-gpg-key-with-subkeys/) (advanced)
- [Review existing GPG keys in your instance](../../../admin_area/credentials_inventory.md#review-existing-gpg-keys)
+
+## Troubleshooting
+
+### Fix verification problems with signed commits
+
+Commits can be signed with [X.509 certificates](../x509_signed_commits/index.md)
+or a GPG key. The verification process for both methods can fail for multiple reasons:
+
+| Value | Description | Possible Fixes |
+|-----------------------------|-------------|----------------|
+| `UNVERIFIED` | The commit signature is not valid. | Sign the commit with a valid signature. |
+| `SAME_USER_DIFFERENT_EMAIL` | The GPG key used to sign the commit does not contain the committer email, but does contain a different valid email for the committer. | Amend the commit to use an email address that matches the GPG key, or update the GPG key [to include the email address](https://security.stackexchange.com/a/261468). |
+| `OTHER_USER` | The signature and GPG key are valid, but the key belongs to a different user than the committer. | Amend the commit to use the correct email address, or amend the commit to use a GPG key associated with your user. |
+| `UNVERIFIED_KEY` | The key associated with the GPG signature has no verified email address associated with the committer. | Add and verify the email to your GitLab profile, [update the GPG key to include the email address](https://security.stackexchange.com/a/261468), or amend the commit to use a different committer email address. |
+| `UNKNOWN_KEY` | The GPG key associated with the GPG signature for this commit is unknown to GitLab. | [Add the GPG key](#add-a-gpg-key-to-your-account) to your GitLab profile. |
+| `MULTIPLE_SIGNATURES` | Multiple GPG or X.509 signatures have been found for the commit. | Amend the commit to use only one GPG or X.509 signature. |
diff --git a/doc/user/project/repository/x509_signed_commits/index.md b/doc/user/project/repository/x509_signed_commits/index.md
index 4a17b2ab84c..e16f5e4defe 100644
--- a/doc/user/project/repository/x509_signed_commits/index.md
+++ b/doc/user/project/repository/x509_signed_commits/index.md
@@ -163,6 +163,11 @@ can start signing your tags:
## Troubleshooting
+For committers without administrator access, review the list of
+[verification problems with signed commits](../gpg_signed_commits/index.md#fix-verification-problems-with-signed-commits)
+for possible fixes. The other troubleshooting suggestions on this page require
+administrator access.
+
### Re-verify commits
GitLab stores the status of any checked commits in the database. You can use a
diff --git a/lib/gitlab/github_import/importer/protected_branches_importer.rb b/lib/gitlab/github_import/importer/protected_branches_importer.rb
index 4372477f55d..ff425528aec 100644
--- a/lib/gitlab/github_import/importer/protected_branches_importer.rb
+++ b/lib/gitlab/github_import/importer/protected_branches_importer.rb
@@ -13,13 +13,15 @@ module Gitlab
protected_branches = client.branches(repo).select { |branch| branch.dig(:protection, :enabled) }
protected_branches.each do |protected_branch|
+ next if already_imported?(protected_branch)
+
object = client.branch_protection(repo, protected_branch[:name])
- next if object.nil? || already_imported?(object)
+ next if object.nil?
yield object
Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :fetched)
- mark_as_imported(object)
+ mark_as_imported(protected_branch)
end
end
diff --git a/lib/sbom/package_url.rb b/lib/sbom/package_url.rb
new file mode 100644
index 00000000000..6afd9943992
--- /dev/null
+++ b/lib/sbom/package_url.rb
@@ -0,0 +1,123 @@
+# frozen_string_literal: true
+
+# MIT License
+#
+# Copyright (c) 2021 package-url
+# Portions Copyright 2022 Gitlab B.V.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+module Sbom
+ # A package URL, or _purl_, is a URL string used to
+ # identify and locate a software package in a mostly universal and uniform way
+ # across programing languages, package managers, packaging conventions, tools,
+ # APIs and databases.
+ #
+ # A purl is a URL composed of seven components:
+ #
+ # ```
+ # scheme:type/namespace/name@version?qualifiers#subpath
+ # ```
+ #
+ # For example,
+ # the package URL for this Ruby package at version 0.1.0 is
+ # `pkg:ruby/mattt/packageurl-ruby@0.1.0`.
+ #
+ # More details on the package URL format can be found in the purl specification:
+ # https://github.com/package-url/purl-spec/blob/0b1559f76b79829e789c4f20e6d832c7314762c5/PURL-SPECIFICATION.rst
+ class PackageUrl
+ # Raised when attempting to parse an invalid package URL string.
+ # @see #parse
+ InvalidPackageURL = Class.new(ArgumentError)
+
+ # The URL scheme, which has a constant value of `"pkg"`.
+ def scheme
+ 'pkg'
+ end
+
+ # The package type or protocol, such as `"gem"`, `"npm"`, and `"github"`.
+ attr_reader :type
+
+ # A name prefix, specific to the type of package.
+ # For example, an npm scope, a Docker image owner, or a GitHub user.
+ attr_reader :namespace
+
+ # The name of the package.
+ attr_reader :name
+
+ # The version of the package.
+ attr_reader :version
+
+ # Extra qualifying data for a package, specific to the type of package.
+ # For example, the operating system or architecture.
+ attr_reader :qualifiers
+
+ # An extra subpath within a package, relative to the package root.
+ attr_reader :subpath
+
+ # Constructs a package URL from its components
+ # @param type [String] The package type or protocol.
+ # @param namespace [String] A name prefix, specific to the type of package.
+ # @param name [String] The name of the package.
+ # @param version [String] The version of the package.
+ # @param qualifiers [Hash] Extra qualifying data for a package, specific to the type of package.
+ # @param subpath [String] An extra subpath within a package, relative to the package root.
+ def initialize(type:, name:, namespace: nil, version: nil, qualifiers: nil, subpath: nil)
+ raise ArgumentError, 'type is required' unless type.present?
+ raise ArgumentError, 'name is required' unless name.present?
+
+ @type = type.downcase
+ @namespace = namespace
+ @name = name
+ @version = version
+ @qualifiers = qualifiers
+ @subpath = subpath
+ end
+
+ # Creates a new PackageURL from a string.
+ # @param [String] string The package URL string.
+ # @raise [InvalidPackageURL] If the string is not a valid package URL.
+ # @return [PackageURL]
+ def self.parse(string)
+ Decoder.new(string).decode!
+ end
+
+ # Returns a hash containing the
+ # scheme, type, namespace, name, version, qualifiers, and subpath components
+ # of the package URL.
+ def to_h
+ {
+ scheme: scheme,
+ type: @type,
+ namespace: @namespace,
+ name: @name,
+ version: @version,
+ qualifiers: @qualifiers,
+ subpath: @subpath
+ }
+ end
+
+ # Returns a string representation of the package URL.
+ # Package URL representations are created according to the instructions from
+ # https://github.com/package-url/purl-spec/blob/0b1559f76b79829e789c4f20e6d832c7314762c5/PURL-SPECIFICATION.rst#how-to-build-purl-string-from-its-components.
+ def to_s
+ Encoder.new(self).encode
+ end
+ end
+end
diff --git a/lib/sbom/package_url/decoder.rb b/lib/sbom/package_url/decoder.rb
new file mode 100644
index 00000000000..a8032e021c5
--- /dev/null
+++ b/lib/sbom/package_url/decoder.rb
@@ -0,0 +1,174 @@
+# frozen_string_literal: true
+
+# MIT License
+#
+# Copyright (c) 2021 package-url
+# Portions Copyright 2022 Gitlab B.V.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+module Sbom
+ class PackageUrl
+ class Decoder
+ include StringUtils
+
+ def initialize(string)
+ @string = string
+ end
+
+ def decode!
+ raise ArgumentError, "expected String but given #{@string.class}" unless @string.is_a?(::String)
+
+ decode_subpath!
+ decode_qualifiers!
+ decode_scheme!
+ decode_type!
+ decode_version!
+ decode_name!
+ decode_namespace!
+
+ PackageUrl.new(
+ type: @type,
+ name: @name,
+ namespace: @namespace,
+ version: @version,
+ qualifiers: @qualifiers,
+ subpath: @subpath
+ )
+ end
+
+ private
+
+ def decode_subpath!
+ # Split the purl string once from right on '#'
+ # Given the string: `scheme:type/namespace/name@version?qualifiers#subpath`
+ # - The left side is the remainder: `scheme:type/namespace/name@version?qualifiers`
+ # - The right side will be parsed as the subpath: `subpath`
+ @subpath, @string = partition(@string, '#', from: :right) do |subpath|
+ decode_segments(subpath) do |segment|
+ # Discard segments which are blank, `.`, or `..`
+ segment.empty? || segment == '.' || segment == '..'
+ end
+ end
+ end
+
+ def decode_qualifiers!
+ # Split the remainder once from right on '?'
+ # Given string: `scheme:type/namespace/name@version?qualifiers`
+ # - The left side is the remainder: `scheme:type/namespace/name@version`
+ # - The right side is the qualifiers string: `qualifiers`
+ @qualifiers, @string = partition(@string, '?', from: :right) do |qualifiers|
+ parse_qualifiers(qualifiers)
+ end
+ end
+
+ def decode_scheme!
+ # Split the remainder once from left on ':'
+ # Given the string: `scheme:type/namespace/name@version`
+ # - The left side lowercased is the scheme: `scheme`
+ # - The right side is the remainder: `type/namespace/name@version`
+ @scheme, @string = partition(@string, ':', from: :left)
+ raise InvalidPackageURL, 'invalid or missing "pkg:" URL scheme' unless @scheme == 'pkg'
+ end
+
+ def decode_type!
+ # Strip the remainder from leading and trailing '/'
+ @string = strip(@string, '/')
+ # Split this once from left on '/'
+ # Given the string: `type/namespace/name@version`
+ # - The left side lowercased is the type: `type`
+ # - The right side is the remainder: `namespace/name@version`
+ @type, @string = partition(@string, '/', from: :left)
+ raise InvalidPackageURL, 'invalid or missing package type' if @type.empty?
+ end
+
+ def decode_version!
+ # Split the remainder once from right on '@'
+ # Given the string: `namespace/name@version`
+ # - The left side is the remainder: `namespace/name`
+ # - The right side is the version: `version`
+ # - The version must be URI decoded
+ @version, @string = partition(@string, '@', from: :right) do |version|
+ URI.decode_www_form_component(version)
+ end
+ end
+
+ def decode_name!
+ # Split the remainder once from right on '/'
+ # Given the string: `namespace/name`
+ # - The left side is the remainder: `namespace`
+ # - The right size is the name: `name`
+ # - The name must be URI decoded
+ @name, @string = partition(@string, '/', from: :right, require_separator: false) do |name|
+ URI.decode_www_form_component(name)
+ end
+ end
+
+ def decode_namespace!
+ # If there is anything remaining, this is the namespace.
+ # The namespace may contain multiple segments delimited by `/`.
+ @namespace = decode_segments(@string, &:empty?) unless @string.empty?
+ end
+
+ def decode_segment(segment)
+ decoded = URI.decode_www_form_component(segment)
+
+ raise InvalidPackageURL, 'slash-separated segments may not contain `/`' if decoded.include?('/')
+
+ decoded
+ end
+
+ def decode_segments(string)
+ string.split('/').filter_map do |segment|
+ next if block_given? && yield(segment)
+
+ decode_segment(segment)
+ end.join('/')
+ end
+
+ def parse_qualifiers(raw_qualifiers)
+ # - Split the qualifiers on '&'. Each part is a key=value pair
+ # - For each pair, split the key=value once from left on '=':
+ # - The key is the lowercase left side
+ # - The value is the percent-decoded right side
+ # - Discard any key/value pairs where the value is empty
+ # - If the key is checksums,
+ # split the value on ',' to create a list of checksums
+ # - This list of key/value is the qualifiers object
+ raw_qualifiers.split('&').each_with_object({}) do |pair, memo|
+ key, separator, value = pair.partition('=')
+
+ next if separator.empty?
+
+ key = key.downcase
+ value = URI.decode_www_form_component(value)
+
+ next if value.empty?
+
+ memo[key] = case key
+ when 'checksums'
+ value.split(',')
+ else
+ value
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/sbom/package_url/encoder.rb b/lib/sbom/package_url/encoder.rb
new file mode 100644
index 00000000000..1412824b76f
--- /dev/null
+++ b/lib/sbom/package_url/encoder.rb
@@ -0,0 +1,137 @@
+# frozen_string_literal: true
+
+# MIT License
+#
+# Copyright (c) 2021 package-url
+# Portions Copyright 2022 Gitlab B.V.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+module Sbom
+ class PackageUrl
+ class Encoder
+ include StringUtils
+
+ def initialize(package)
+ @type = package.type
+ @namespace = package.namespace
+ @name = package.name
+ @version = package.version
+ @qualifiers = package.qualifiers
+ @subpath = package.subpath
+ @io = StringIO.new
+ end
+
+ def encode
+ encode_scheme!
+ encode_type!
+ encode_name!
+ encode_version!
+ encode_qualifiers!
+ encode_subpath!
+
+ io.string
+ end
+
+ private
+
+ attr_reader :io
+
+ def encode_scheme!
+ io.write('pkg:')
+ end
+
+ def encode_type!
+ # Append the type string to the purl as a lowercase ASCII string
+ # Append '/' to the purl
+ io.write(@type)
+ io.write('/')
+ end
+
+ def encode_name!
+ # If the namespace is empty:
+ # - Apply type-specific normalization to the name if needed
+ # - UTF-8-encode the name if needed in your programming language
+ # - Append the percent-encoded name to the purl
+ #
+ # If the namespace is not empty:
+ # - Strip the namespace from leading and trailing '/'
+ # - Split on '/' as segments
+ # - Apply type-specific normalization to each segment if needed
+ # - UTF-8-encode each segment if needed in your programming language
+ # - Percent-encode each segment
+ # - Join the segments with '/'
+ # - Append this to the purl
+ # - Append '/' to the purl
+ # - Strip the name from leading and trailing '/'
+ # - Apply type-specific normalization to the name if needed
+ # - UTF-8-encode the name if needed in your programming language
+ # - Append the percent-encoded name to the purl
+ if @namespace.nil?
+ io.write(URI.encode_www_form_component(@name))
+ else
+ io.write(encode_segments(@namespace, &:empty?))
+ io.write('/')
+ io.write(URI.encode_www_form_component(strip(@name, '/')))
+ end
+ end
+
+ def encode_version!
+ return if @version.nil?
+
+ # - Append '@' to the purl
+ # - UTF-8-encode the version if needed in your programming language
+ # - Append the percent-encoded version to the purl
+ io.write('@')
+ io.write(URI.encode_www_form_component(@version))
+ end
+
+ def encode_qualifiers!
+ return if @qualifiers.nil? || encoded_qualifiers.empty?
+
+ io.write('?')
+ io.write(encoded_qualifiers)
+ end
+
+ def encoded_qualifiers
+ @encoded_qualifiers ||= @qualifiers.filter_map do |key, value|
+ next if value.empty?
+
+ next "#{key.downcase}=#{value.join(',')}" if key == 'checksums' && value.is_a?(::Array)
+
+ "#{key.downcase}=#{URI.encode_www_form_component(value)}"
+ end.sort.join('&')
+ end
+
+ def encode_subpath!
+ return if @subpath.nil? || encoded_subpath.empty?
+
+ io.write('#')
+ io.write(encoded_subpath)
+ end
+
+ def encoded_subpath
+ @encoded_subpath ||= encode_segments(@subpath) do |segment|
+ # Discard segments which are blank, `.`, or `..`
+ segment.empty? || segment == '.' || segment == '..'
+ end
+ end
+ end
+ end
+end
diff --git a/lib/sbom/package_url/string_utils.rb b/lib/sbom/package_url/string_utils.rb
new file mode 100644
index 00000000000..7b476292c72
--- /dev/null
+++ b/lib/sbom/package_url/string_utils.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+
+# MIT License
+#
+# Copyright (c) 2021 package-url
+# Portions Copyright 2022 Gitlab B.V.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+module Sbom
+ class PackageUrl
+ module StringUtils
+ private
+
+ def strip(string, char)
+ string.delete_prefix(char).delete_suffix(char)
+ end
+
+ def split_segments(string)
+ strip(string, '/').split('/')
+ end
+
+ def encode_segments(string)
+ return '' if string.nil?
+
+ split_segments(string).map do |segment|
+ next if block_given? && yield(segment)
+
+ URI.encode_www_form_component(segment)
+ end.join('/')
+ end
+
+ # Partition the given string on the separator.
+ # The side being partitioned from is returned as the value,
+ # with the opposing side being returned as the remainder.
+ #
+ # If a block is given, then the (value, remainder) are given
+ # to the block, and the return value of the block is used as the value.
+ #
+ # If `require_separator` is true, then a nil value will be returned
+ # if the separator is not present.
+ def partition(string, sep, from: :left, require_separator: true)
+ value, separator, remainder = if from == :left
+ left, separator, right = string.partition(sep)
+ [left, separator, right]
+ else
+ left, separator, right = string.rpartition(sep)
+ [right, separator, left]
+ end
+
+ return [nil, value] if separator.empty? && require_separator
+
+ value = yield(value, remainder) if block_given?
+
+ [value, remainder]
+ end
+ end
+ end
+end
diff --git a/qa/Gemfile b/qa/Gemfile
index c8cb1c096e3..1761f0b4eda 100644
--- a/qa/Gemfile
+++ b/qa/Gemfile
@@ -16,7 +16,7 @@ gem 'rspec-retry', '~> 0.6.2', require: 'rspec/retry'
gem 'rspec_junit_formatter', '~> 0.6.0'
gem 'faker', '~> 2.23'
gem 'knapsack', '~> 4.0'
-gem 'parallel_tests', '~> 3.13'
+gem 'parallel_tests', '~> 4.0'
gem 'rotp', '~> 6.2.0'
gem 'timecop', '~> 0.9.5'
gem 'parallel', '~> 1.22', '>= 1.22.1'
diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock
index 2bc85dc0158..c99a955da97 100644
--- a/qa/Gemfile.lock
+++ b/qa/Gemfile.lock
@@ -187,7 +187,7 @@ GEM
oj (3.13.21)
os (1.1.4)
parallel (1.22.1)
- parallel_tests (3.13.0)
+ parallel_tests (4.0.0)
parallel
parser (3.1.2.1)
ast (~> 2.4.1)
@@ -318,7 +318,7 @@ DEPENDENCIES
nokogiri (~> 1.13, >= 1.13.9)
octokit (~> 6.0.0)
parallel (~> 1.22, >= 1.22.1)
- parallel_tests (~> 3.13)
+ parallel_tests (~> 4.0)
pry-byebug (~> 3.10.1)
rainbow (~> 3.1.1)
rake (~> 13, >= 13.0.6)
diff --git a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/raw_variables_defined_in_yaml_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/raw_variables_defined_in_yaml_spec.rb
new file mode 100644
index 00000000000..ae2c9ae17a9
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/raw_variables_defined_in_yaml_spec.rb
@@ -0,0 +1,142 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Verify', :runner do
+ describe 'Pipeline with raw variables in YAML', product_group: :pipeline_authoring, feature_flag: {
+ name: 'ci_raw_variables_in_yaml_config',
+ scope: :project
+ } do
+ let(:executor) { "qa-runner-#{Time.now.to_i}" }
+ let(:pipeline_job_name) { 'rspec' }
+
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'project-with-raw-variable-pipeline'
+ end
+ end
+
+ let!(:runner) do
+ Resource::Runner.fabricate! do |runner|
+ runner.project = project
+ runner.name = executor
+ runner.tags = [executor]
+ end
+ end
+
+ let(:commit_ci_file) do
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.commit_message = 'Add .gitlab-ci.yml'
+ commit.add_files(
+ [
+ {
+ file_path: '.gitlab-ci.yml',
+ content: <<~YAML
+ variables:
+ VAR7:
+ value: "value 7 $CI_PIPELINE_ID"
+ expand: false
+ VAR8:
+ value: "value 8 $CI_PIPELINE_ID"
+ expand: false
+
+ #{pipeline_job_name}:
+ tags:
+ - #{executor}
+ script:
+ - echo "VAR1 is $VAR1"
+ - echo "VAR2 is $VAR2"
+ - echo "VAR3 is $VAR3"
+ - echo "VAR4 is $VAR4"
+ - echo "VAR5 is $VAR5"
+ - echo "VAR6 is $VAR6"
+ - echo "VAR7 is $VAR7"
+ - echo "VAR8 is $VAR8"
+ variables:
+ VAR1: "JOBID-$CI_JOB_ID"
+ VAR2: "PIPELINEID-$CI_PIPELINE_ID and $VAR1"
+ VAR3:
+ value: "PIPELINEID-$CI_PIPELINE_ID and $VAR1"
+ expand: false
+ VAR4:
+ value: "JOBID-$CI_JOB_ID"
+ expand: false
+ VAR5: "PIPELINEID-$CI_PIPELINE_ID and $VAR4"
+ VAR6:
+ value: "PIPELINEID-$CI_PIPELINE_ID and $VAR4"
+ expand: false
+ VAR7: "overridden value 7 $CI_PIPELINE_ID"
+ YAML
+ }
+ ]
+ )
+ end
+ end
+
+ let(:pipeline_id) { project.pipelines.first[:id] }
+ let(:job_id) { project.job_by_name(pipeline_job_name)[:id] }
+
+ def before_do
+ # TODO: Switch to use `let!` and remove this line when removing FF
+ commit_ci_file
+
+ Flow::Login.sign_in
+ project.visit!
+ Flow::Pipeline.visit_latest_pipeline(status: 'passed')
+ Page::Project::Pipeline::Show.perform do |show|
+ show.click_job(pipeline_job_name)
+ end
+ end
+
+ after do
+ runner&.remove_via_api!
+ end
+
+ context 'when FF is on', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/381487' do
+ before do
+ Runtime::Feature.enable(:ci_raw_variables_in_yaml_config, project: project)
+ sleep 60
+
+ before_do
+ end
+
+ it 'expands variables according to expand: true/false', :aggregate_failures do
+ Page::Project::Job::Show.perform do |show|
+ expect(show.output).to have_content("VAR1 is JOBID-#{job_id}")
+ expect(show.output).to have_content("VAR2 is PIPELINEID-#{pipeline_id} and JOBID-#{job_id}")
+ expect(show.output).to have_content("VAR3 is PIPELINEID-$CI_PIPELINE_ID and $VAR1")
+ expect(show.output).to have_content("VAR4 is JOBID-$CI_JOB_ID")
+ expect(show.output).to have_content("VAR5 is PIPELINEID-#{pipeline_id} and JOBID-$CI_JOB_ID")
+ expect(show.output).to have_content("VAR6 is PIPELINEID-$CI_PIPELINE_ID and $VAR4")
+ expect(show.output).to have_content("VAR7 is overridden value 7 #{pipeline_id}")
+ expect(show.output).to have_content("VAR8 is value 8 $CI_PIPELINE_ID")
+ end
+ end
+ end
+
+ # TODO: Remove this context when FF :ci_raw_variables_in_yaml_config is removed
+ # Also archive testcase and close related issue
+ context 'when FF is off', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/381486' do
+ before do
+ Runtime::Feature.disable(:ci_raw_variables_in_yaml_config, project: project)
+ sleep 60
+
+ before_do
+ end
+
+ it 'expands all variables', :aggregate_failures do
+ Page::Project::Job::Show.perform do |show|
+ expect(show.output).to have_content("VAR1 is JOBID-#{job_id}")
+ expect(show.output).to have_content("VAR2 is PIPELINEID-#{pipeline_id} and JOBID-#{job_id}")
+ expect(show.output).to have_content("VAR3 is PIPELINEID-#{pipeline_id} and JOBID-#{job_id}")
+ expect(show.output).to have_content("VAR4 is JOBID-#{job_id}")
+ expect(show.output).to have_content("VAR5 is PIPELINEID-#{pipeline_id} and JOBID-#{job_id}")
+ expect(show.output).to have_content("VAR6 is PIPELINEID-#{pipeline_id} and JOBID-#{job_id}")
+ expect(show.output).to have_content("VAR7 is overridden value 7 #{pipeline_id}")
+ expect(show.output).to have_content("VAR8 is value 8 #{pipeline_id}")
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/controllers/explore/groups_controller_spec.rb b/spec/controllers/explore/groups_controller_spec.rb
index 310fe609cf1..a3bd8102462 100644
--- a/spec/controllers/explore/groups_controller_spec.rb
+++ b/spec/controllers/explore/groups_controller_spec.rb
@@ -9,31 +9,43 @@ RSpec.describe Explore::GroupsController do
sign_in(user)
end
- it 'renders group trees' do
- expect(described_class).to include(GroupTree)
- end
+ shared_examples 'explore groups' do
+ it 'renders group trees' do
+ expect(described_class).to include(GroupTree)
+ end
- it 'includes public projects' do
- member_of_group = create(:group)
- member_of_group.add_developer(user)
- public_group = create(:group, :public)
+ it 'includes public projects' do
+ member_of_group = create(:group)
+ member_of_group.add_developer(user)
+ public_group = create(:group, :public)
- get :index
+ get :index
- expect(assigns(:groups)).to contain_exactly(member_of_group, public_group)
- end
+ expect(assigns(:groups)).to contain_exactly(member_of_group, public_group)
+ end
- context 'restricted visibility level is public' do
- before do
- sign_out(user)
+ context 'restricted visibility level is public' do
+ before do
+ sign_out(user)
- stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
+ stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
+ end
+
+ it 'redirects to login page' do
+ get :index
+
+ expect(response).to redirect_to new_user_session_path
+ end
end
+ end
- it 'redirects to login page' do
- get :index
+ it_behaves_like 'explore groups'
- expect(response).to redirect_to new_user_session_path
+ context 'generic_explore_groups flag is disabled' do
+ before do
+ stub_feature_flags(generic_explore_groups: false)
end
+
+ it_behaves_like 'explore groups'
end
end
diff --git a/spec/lib/gitlab/github_import/importer/protected_branches_importer_spec.rb b/spec/lib/gitlab/github_import/importer/protected_branches_importer_spec.rb
index f5e053da413..8809d58a252 100644
--- a/spec/lib/gitlab/github_import/importer/protected_branches_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/protected_branches_importer_spec.rb
@@ -164,6 +164,7 @@ RSpec.describe Gitlab::GithubImport::Importer::ProtectedBranchesImporter do
let(:branch_struct) { Struct.new(:protection, :name, :url, keyword_init: true) }
let(:protection_struct) { Struct.new(:enabled, keyword_init: true) }
let(:protected_branch) { branch_struct.new(name: 'main', protection: protection_struct.new(enabled: true)) }
+ let(:second_protected_branch) { branch_struct.new(name: 'fix', protection: protection_struct.new(enabled: true)) }
let(:unprotected_branch) { branch_struct.new(name: 'staging', protection: protection_struct.new(enabled: false)) }
# when user has no admin rights on repo
let(:unknown_protection_branch) { branch_struct.new(name: 'development', protection: nil) }
@@ -172,9 +173,9 @@ RSpec.describe Gitlab::GithubImport::Importer::ProtectedBranchesImporter do
before do
allow(client).to receive(:branches).with(project.import_source)
- .and_return([protected_branch, unprotected_branch, unknown_protection_branch])
+ .and_return([protected_branch, second_protected_branch, unprotected_branch, unknown_protection_branch])
allow(client).to receive(:branch_protection)
- .with(project.import_source, protected_branch.name).once
+ .with(project.import_source, anything)
.and_return(github_protection_rule)
allow(Gitlab::GithubImport::ObjectCounter).to receive(:increment)
.with(project, :protected_branch, :fetched)
@@ -184,12 +185,13 @@ RSpec.describe Gitlab::GithubImport::Importer::ProtectedBranchesImporter do
subject.each_object_to_import do |object|
expect(object).to eq github_protection_rule
end
- expect(Gitlab::GithubImport::ObjectCounter).to have_received(:increment).once
+ expect(Gitlab::GithubImport::ObjectCounter).to have_received(:increment).twice
end
context 'when protected branch is already processed' do
it "doesn't process this branch" do
subject.mark_as_imported(protected_branch)
+ subject.mark_as_imported(second_protected_branch)
subject.each_object_to_import {}
expect(Gitlab::GithubImport::ObjectCounter).not_to have_received(:increment)
diff --git a/spec/lib/sbom/package_url/decoder_spec.rb b/spec/lib/sbom/package_url/decoder_spec.rb
new file mode 100644
index 00000000000..6f709c93601
--- /dev/null
+++ b/spec/lib/sbom/package_url/decoder_spec.rb
@@ -0,0 +1,119 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+require 'rspec-parameterized'
+
+require_relative '../../../support/shared_contexts/lib/sbom/package_url_shared_contexts'
+
+RSpec.describe Sbom::PackageUrl::Decoder do
+ describe '#decode' do
+ subject(:decode) { described_class.new(url).decode! }
+
+ include_context 'with purl matrix'
+
+ with_them do
+ it do
+ is_expected.to have_attributes(
+ type: type,
+ namespace: namespace,
+ name: name,
+ version: version,
+ qualifiers: qualifiers,
+ subpath: subpath
+ )
+ end
+ end
+
+ context 'when no argument is passed' do
+ let(:url) { nil }
+
+ it 'raises an error' do
+ expect { decode }.to raise_error(ArgumentError)
+ end
+ end
+
+ context 'when an invalid package URL string is passed' do
+ let(:url) { 'invalid' }
+
+ it 'raises an error' do
+ expect { decode }.to raise_error(Sbom::PackageUrl::InvalidPackageURL)
+ end
+ end
+
+ context 'when namespace or subpath contains an encoded slash' do
+ where(:url) do
+ [
+ 'pkg:golang/google.org/golang/genproto#googleapis%2fapi%2fannotations',
+ 'pkg:golang/google.org%2fgolang/genproto#googleapis/api/annotations'
+ ]
+ end
+
+ with_them do
+ it { expect { decode }.to raise_error(Sbom::PackageUrl::InvalidPackageURL) }
+ end
+ end
+
+ context 'when name contains an encoded slash' do
+ let(:url) { 'pkg:golang/google.org/golang%2fgenproto#googleapis/api/annotations' }
+
+ it do
+ is_expected.to have_attributes(
+ type: 'golang',
+ namespace: 'google.org',
+ name: 'golang/genproto',
+ version: nil,
+ qualifiers: nil,
+ subpath: 'googleapis/api/annotations'
+ )
+ end
+ end
+
+ context 'with URL encoded segments' do
+ let(:url) do
+ 'pkg:golang/namespace%21/google.golang.org%20genproto@version%21?k=v%21#googleapis%20api%20annotations'
+ end
+
+ it 'decodes them' do
+ is_expected.to have_attributes(
+ type: 'golang',
+ namespace: 'namespace!',
+ name: 'google.golang.org genproto',
+ version: 'version!',
+ qualifiers: { 'k' => 'v!' },
+ subpath: 'googleapis api annotations'
+ )
+ end
+ end
+
+ context 'when segments contain empty values' do
+ let(:url) { 'pkg:golang/google.golang.org//.././genproto#googleapis/..//./api/annotations' }
+
+ it 'removes them from the segments' do
+ is_expected.to have_attributes(
+ type: 'golang',
+ namespace: 'google.golang.org/../.', # . and .. are allowed in the namespace, but not the subpath
+ name: 'genproto',
+ version: nil,
+ qualifiers: nil,
+ subpath: 'googleapis/api/annotations'
+ )
+ end
+ end
+
+ context 'when qualifiers have no value' do
+ let(:url) { 'pkg:rpm/fedora/curl@7.50.3-1.fc25?arch=i386&distro=fedora-25&foo=&bar=' }
+
+ it 'they are ignored' do
+ is_expected.to have_attributes(
+ type: 'rpm',
+ namespace: 'fedora',
+ name: 'curl',
+ version: '7.50.3-1.fc25',
+ qualifiers: { 'arch' => 'i386',
+ 'distro' => 'fedora-25' },
+ subpath: nil
+ )
+ end
+ end
+ end
+end
diff --git a/spec/lib/sbom/package_url/encoder_spec.rb b/spec/lib/sbom/package_url/encoder_spec.rb
new file mode 100644
index 00000000000..ff672170050
--- /dev/null
+++ b/spec/lib/sbom/package_url/encoder_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+require 'rspec-parameterized'
+
+require_relative '../../../support/shared_contexts/lib/sbom/package_url_shared_contexts'
+
+RSpec.describe Sbom::PackageUrl::Encoder do
+ describe '#encode' do
+ let(:package) do
+ ::Sbom::PackageUrl.new(
+ type: type,
+ namespace: namespace,
+ name: name,
+ version: version,
+ qualifiers: qualifiers,
+ subpath: subpath
+ )
+ end
+
+ subject(:encode) { described_class.new(package).encode }
+
+ include_context 'with purl matrix'
+
+ with_them do
+ it { is_expected.to eq(url) }
+ end
+ end
+end
diff --git a/spec/lib/sbom/package_url_spec.rb b/spec/lib/sbom/package_url_spec.rb
new file mode 100644
index 00000000000..72090c5bd29
--- /dev/null
+++ b/spec/lib/sbom/package_url_spec.rb
@@ -0,0 +1,144 @@
+# frozen_string_literal: true
+
+# MIT License
+#
+# Copyright (c) 2021 package-url
+# Portions Copyright 2022 Gitlab B.V.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+require 'fast_spec_helper'
+require 'rspec-parameterized'
+
+require_relative '../../support/helpers/next_instance_of'
+require_relative '../../support/shared_contexts/lib/sbom/package_url_shared_contexts'
+
+RSpec.describe Sbom::PackageUrl do
+ include NextInstanceOf
+
+ let(:args) do
+ {
+ type: 'example',
+ namespace: 'test',
+ name: 'test',
+ version: '1.0.0',
+ qualifiers: { 'arch' => 'x86_64' },
+ subpath: 'path/to/package'
+ }
+ end
+
+ describe '#initialize' do
+ subject { described_class.new(**args) }
+
+ context 'with well-formed arguments' do
+ it { is_expected.to have_attributes(**args) }
+ end
+
+ context 'when no arguments are given' do
+ it { expect { described_class.new }.to raise_error(ArgumentError) }
+ end
+
+ context 'when required parameters are missing' do
+ where(:param) { %i[type name] }
+
+ before do
+ args[param] = nil
+ end
+
+ with_them do
+ it { expect { subject }.to raise_error(ArgumentError) }
+ end
+ end
+
+ describe 'normalization' do
+ it 'downcases provided type component' do
+ purl = described_class.new(type: 'EXAMPLE', name: 'test')
+
+ expect(purl.type).to eq('example')
+ expect(purl.name).to eq('test')
+ end
+
+ it 'does not down provided name component' do
+ purl = described_class.new(type: 'example', name: 'TEST')
+
+ expect(purl.type).to eq('example')
+ expect(purl.name).to eq('TEST')
+ end
+ end
+ end
+
+ describe '#parse' do
+ let(:url) { 'pkg:gem/rails@6.1.6.1' }
+
+ subject(:parse) { described_class.parse(url) }
+
+ it 'delegates parsing to the decoder' do
+ expect_next_instance_of(described_class::Decoder, url) do |decoder|
+ expect(decoder).to receive(:decode!)
+ end
+
+ parse
+ end
+ end
+
+ describe '#to_h' do
+ let(:purl) do
+ described_class.new(
+ type: type,
+ namespace: namespace,
+ name: name,
+ version: version,
+ qualifiers: qualifiers,
+ subpath: subpath
+ )
+ end
+
+ subject(:to_h) { purl.to_h }
+
+ include_context 'with purl matrix'
+
+ with_them do
+ it do
+ is_expected.to eq(
+ {
+ scheme: 'pkg',
+ type: type,
+ namespace: namespace,
+ name: name,
+ version: version,
+ qualifiers: qualifiers,
+ subpath: subpath
+ }
+ )
+ end
+ end
+ end
+
+ describe '#to_s' do
+ let(:package) { described_class.new(**args) }
+
+ it 'delegates to_s to the encoder' do
+ expect_next_instance_of(described_class::Encoder, package) do |encoder|
+ expect(encoder).to receive(:encode)
+ end
+
+ package.to_s
+ end
+ end
+end
diff --git a/spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb b/spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb
deleted file mode 100644
index 605d9e67ab6..00000000000
--- a/spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Clusters::Applications::CheckIngressIpAddressService do
- include ExclusiveLeaseHelpers
-
- let(:application) { create(:clusters_applications_ingress, :installed) }
- let(:service) { described_class.new(application) }
- let(:kubeclient) { double(::Kubeclient::Client, get_service: kube_service) }
- let(:lease_key) { "check_ingress_ip_address_service:#{application.id}" }
-
- let(:ingress) do
- [
- {
- ip: '111.222.111.222',
- hostname: 'localhost.localdomain'
- }
- ]
- end
-
- let(:kube_service) do
- ::Kubeclient::Resource.new(
- {
- status: {
- loadBalancer: {
- ingress: ingress
- }
- }
- }
- )
- end
-
- subject { service.execute }
-
- before do
- stub_exclusive_lease(lease_key, timeout: 15.seconds.to_i)
- allow(application.cluster).to receive(:kubeclient).and_return(kubeclient)
- end
-
- include_examples 'check ingress ip executions', :clusters_applications_ingress
-
- include_examples 'check ingress ip executions', :clusters_applications_knative
-end
diff --git a/spec/services/event_create_service_spec.rb b/spec/services/event_create_service_spec.rb
index 06f0eb1efbc..e37fffc8995 100644
--- a/spec/services/event_create_service_spec.rb
+++ b/spec/services/event_create_service_spec.rb
@@ -20,33 +20,6 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
end
end
- shared_examples 'Snowplow event' do
- let(:label) { nil }
-
- it 'is not emitted if FF is disabled' do
- stub_feature_flags(feature_flag_name => false)
-
- subject
-
- expect_no_snowplow_event
- end
-
- it 'is emitted' do
- params = {
- category: category,
- action: action,
- namespace: namespace,
- user: user,
- project: project,
- label: label
- }.compact
-
- subject
-
- expect_snowplow_event(**params)
- end
- end
-
describe 'Issues' do
describe '#open_issue' do
let(:issue) { create(:issue) }
@@ -95,7 +68,7 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
let(:event_action) { Gitlab::UsageDataCounters::TrackUniqueEvents::MERGE_REQUEST_ACTION }
end
- it_behaves_like 'Snowplow event' do
+ it_behaves_like 'Snowplow event tracking' do
let(:category) { Gitlab::UsageDataCounters::TrackUniqueEvents::MERGE_REQUEST_ACTION.to_s }
let(:label) { 'merge_requests_users' }
let(:action) { 'create' }
@@ -121,7 +94,7 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
let(:event_action) { Gitlab::UsageDataCounters::TrackUniqueEvents::MERGE_REQUEST_ACTION }
end
- it_behaves_like 'Snowplow event' do
+ it_behaves_like 'Snowplow event tracking' do
let(:category) { Gitlab::UsageDataCounters::TrackUniqueEvents::MERGE_REQUEST_ACTION.to_s }
let(:label) { 'merge_requests_users' }
let(:action) { 'close' }
@@ -147,7 +120,7 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
let(:event_action) { Gitlab::UsageDataCounters::TrackUniqueEvents::MERGE_REQUEST_ACTION }
end
- it_behaves_like 'Snowplow event' do
+ it_behaves_like 'Snowplow event tracking' do
let(:category) { Gitlab::UsageDataCounters::TrackUniqueEvents::MERGE_REQUEST_ACTION.to_s }
let(:label) { 'merge_requests_users' }
let(:action) { 'merge' }
@@ -330,11 +303,16 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
let(:event_action) { Gitlab::UsageDataCounters::TrackUniqueEvents::PUSH_ACTION }
end
- it_behaves_like 'Snowplow event' do
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
let(:category) { described_class.to_s }
- let(:action) { 'action_active_users_project_repo' }
+ let(:action) { :push }
let(:namespace) { project.namespace }
let(:feature_flag_name) { :route_hll_to_snowplow }
+ let(:label) { 'usage_activity_by_stage_monthly.create.action_monthly_active_users_project_repo' }
+ let(:context) do
+ [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll,
+ event: 'action_active_users_project_repo').to_context]
+ end
end
end
@@ -355,11 +333,16 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
let(:event_action) { Gitlab::UsageDataCounters::TrackUniqueEvents::PUSH_ACTION }
end
- it_behaves_like 'Snowplow event' do
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
let(:category) { described_class.to_s }
- let(:action) { 'action_active_users_project_repo' }
+ let(:action) { :push }
let(:namespace) { project.namespace }
let(:feature_flag_name) { :route_hll_to_snowplow }
+ let(:label) { 'usage_activity_by_stage_monthly.create.action_monthly_active_users_project_repo' }
+ let(:context) do
+ [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll,
+ event: 'action_active_users_project_repo').to_context]
+ end
end
end
@@ -495,7 +478,7 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
stub_feature_flags(route_hll_to_snowplow_phase2: false)
end
- it 'doesnt emit snowwplow events', :snowplow do
+ it 'doesnt emit snowplow events', :snowplow do
subject
expect_no_snowplow_event
@@ -522,7 +505,7 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
let(:note) { create(:diff_note_on_merge_request) }
end
- it_behaves_like 'Snowplow event' do
+ it_behaves_like 'Snowplow event tracking' do
let(:note) { create(:diff_note_on_merge_request) }
let(:category) { Gitlab::UsageDataCounters::TrackUniqueEvents::MERGE_REQUEST_ACTION.to_s }
let(:label) { 'merge_requests_users' }
diff --git a/spec/support/shared_contexts/lib/sbom/package_url_shared_contexts.rb b/spec/support/shared_contexts/lib/sbom/package_url_shared_contexts.rb
new file mode 100644
index 00000000000..b5c9e9cc7b0
--- /dev/null
+++ b/spec/support/shared_contexts/lib/sbom/package_url_shared_contexts.rb
@@ -0,0 +1,98 @@
+# frozen_string_literal: true
+
+RSpec.shared_context 'with purl matrix' do
+ where do
+ {
+ 'valid RubyGems package URL' => {
+ url: 'pkg:gem/ruby-advisory-db-check@0.12.4',
+ type: 'gem',
+ namespace: nil,
+ name: 'ruby-advisory-db-check',
+ version: '0.12.4',
+ qualifiers: nil,
+ subpath: nil
+ },
+ 'valid BitBucket package URL' => {
+ url: 'pkg:bitbucket/birkenfeld/pygments-main@244fd47e07d1014f0aed9c',
+ type: 'bitbucket',
+ namespace: 'birkenfeld',
+ name: 'pygments-main',
+ version: '244fd47e07d1014f0aed9c',
+ qualifiers: nil,
+ subpath: nil
+ },
+ 'valid GitHub package URL' => {
+ url: 'pkg:github/package-url/purl-spec@244fd47e07d1004f0aed9c',
+ type: 'github',
+ namespace: 'package-url',
+ name: 'purl-spec',
+ version: '244fd47e07d1004f0aed9c',
+ qualifiers: nil,
+ subpath: nil
+ },
+ 'valid Go module URL' => {
+ url: 'pkg:golang/google.golang.org/genproto#googleapis/api/annotations',
+ type: 'golang',
+ namespace: 'google.golang.org',
+ name: 'genproto',
+ version: nil,
+ qualifiers: nil,
+ subpath: 'googleapis/api/annotations'
+ },
+ 'valid Maven package URL' => {
+ url: 'pkg:maven/org.apache.commons/io@1.3.4',
+ type: 'maven',
+ namespace: 'org.apache.commons',
+ name: 'io',
+ version: '1.3.4',
+ qualifiers: nil,
+ subpath: nil
+ },
+ 'valid NPM package URL' => {
+ url: 'pkg:npm/foobar@12.3.1',
+ type: 'npm',
+ namespace: nil,
+ name: 'foobar',
+ version: '12.3.1',
+ qualifiers: nil,
+ subpath: nil
+ },
+ 'valid NuGet package URL' => {
+ url: 'pkg:nuget/EnterpriseLibrary.Common@6.0.1304',
+ type: 'nuget',
+ namespace: nil,
+ name: 'EnterpriseLibrary.Common',
+ version: '6.0.1304',
+ qualifiers: nil,
+ subpath: nil
+ },
+ 'valid PyPI package URL' => {
+ url: 'pkg:pypi/django@1.11.1',
+ type: 'pypi',
+ namespace: nil,
+ name: 'django',
+ version: '1.11.1',
+ qualifiers: nil,
+ subpath: nil
+ },
+ 'valid RPM package URL' => {
+ url: 'pkg:rpm/fedora/curl@7.50.3-1.fc25?arch=i386&distro=fedora-25',
+ type: 'rpm',
+ namespace: 'fedora',
+ name: 'curl',
+ version: '7.50.3-1.fc25',
+ qualifiers: { 'arch' => 'i386', 'distro' => 'fedora-25' },
+ subpath: nil
+ },
+ 'package URL with checksums' => {
+ url: 'pkg:rpm/name?checksums=a,b,c',
+ type: 'rpm',
+ namespace: nil,
+ name: 'name',
+ version: nil,
+ qualifiers: { 'checksums' => %w[a b c] },
+ subpath: nil
+ }
+ }
+ end
+end
diff --git a/spec/workers/cluster_wait_for_ingress_ip_address_worker_spec.rb b/spec/workers/cluster_wait_for_ingress_ip_address_worker_spec.rb
deleted file mode 100644
index 7a42c988a92..00000000000
--- a/spec/workers/cluster_wait_for_ingress_ip_address_worker_spec.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe ClusterWaitForIngressIpAddressWorker do
- describe '#perform' do
- let(:service) { instance_double(Clusters::Applications::CheckIngressIpAddressService, execute: true) }
- let(:application) { instance_double(Clusters::Applications::Ingress) }
- let(:worker) { described_class.new }
-
- before do
- allow(worker)
- .to receive(:find_application)
- .with('ingress', 117)
- .and_yield(application)
-
- allow(Clusters::Applications::CheckIngressIpAddressService)
- .to receive(:new)
- .with(application)
- .and_return(service)
-
- allow(described_class)
- .to receive(:perform_in)
- end
-
- it 'finds the application and calls CheckIngressIpAddressService#execute' do
- worker.perform('ingress', 117)
-
- expect(service).to have_received(:execute)
- end
- end
-end