Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
f471d83619
commit
c59393a068
|
@ -150,12 +150,12 @@ Dangerfile @gl-quality/eng-prod
|
||||||
/ee/lib/ee/api/entities/merge_train.rb @gitlab-org/maintainers/cicd-verify
|
/ee/lib/ee/api/entities/merge_train.rb @gitlab-org/maintainers/cicd-verify
|
||||||
/**/javascripts/jobs/ @gitlab-org/ci-cd/verify/frontend
|
/**/javascripts/jobs/ @gitlab-org/ci-cd/verify/frontend
|
||||||
/**/javascripts/pipelines/ @gitlab-org/ci-cd/verify/frontend
|
/**/javascripts/pipelines/ @gitlab-org/ci-cd/verify/frontend
|
||||||
|
/app/assets/javascripts/ci/ @gitlab-org/ci-cd/verify/frontend
|
||||||
/app/assets/javascripts/pipeline_new/ @gitlab-org/ci-cd/verify/frontend
|
/app/assets/javascripts/pipeline_new/ @gitlab-org/ci-cd/verify/frontend
|
||||||
/app/assets/javascripts/ci_lint/ @gitlab-org/ci-cd/verify/frontend
|
/app/assets/javascripts/ci_lint/ @gitlab-org/ci-cd/verify/frontend
|
||||||
/app/assets/javascripts/ci_variable_list/ @gitlab-org/ci-cd/verify/frontend
|
/app/assets/javascripts/ci_variable_list/ @gitlab-org/ci-cd/verify/frontend
|
||||||
/app/assets/javascripts/pipeline_schedules/ @gitlab-org/ci-cd/verify/frontend
|
/app/assets/javascripts/pipeline_schedules/ @gitlab-org/ci-cd/verify/frontend
|
||||||
/app/assets/javascripts/pipeline_editor/ @gitlab-org/ci-cd/verify/frontend
|
/app/assets/javascripts/pipeline_editor/ @gitlab-org/ci-cd/verify/frontend
|
||||||
/app/assets/javascripts/ci/runner/ @gitlab-org/ci-cd/verify/frontend
|
|
||||||
/ee/app/assets/javascripts/ci_minutes_usage/ @gitlab-org/ci-cd/verify/frontend
|
/ee/app/assets/javascripts/ci_minutes_usage/ @gitlab-org/ci-cd/verify/frontend
|
||||||
/ee/app/assets/javascripts/usage_quotas/ci_minutes_usage/ @gitlab-org/ci-cd/verify/frontend
|
/ee/app/assets/javascripts/usage_quotas/ci_minutes_usage/ @gitlab-org/ci-cd/verify/frontend
|
||||||
/ee/app/assets/javascripts/usage_quotas/pipelines/ @gitlab-org/ci-cd/verify/frontend
|
/ee/app/assets/javascripts/usage_quotas/pipelines/ @gitlab-org/ci-cd/verify/frontend
|
||||||
|
@ -769,7 +769,6 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
||||||
/doc/operations/index.md @msedlakjakubowski
|
/doc/operations/index.md @msedlakjakubowski
|
||||||
/doc/operations/metrics/ @msedlakjakubowski
|
/doc/operations/metrics/ @msedlakjakubowski
|
||||||
/doc/operations/metrics/dashboards/ @msedlakjakubowski
|
/doc/operations/metrics/dashboards/ @msedlakjakubowski
|
||||||
/doc/operations/product_analytics.md @lciutacu
|
|
||||||
/doc/operations/tracing.md @msedlakjakubowski
|
/doc/operations/tracing.md @msedlakjakubowski
|
||||||
/doc/policy/ @axil
|
/doc/policy/ @axil
|
||||||
/doc/raketasks/ @axil
|
/doc/raketasks/ @axil
|
||||||
|
@ -1013,7 +1012,6 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
||||||
/app/assets/javascripts/related_issues/components/issue_token.vue @gitlab-org/manage/authentication-and-authorization/approvers
|
/app/assets/javascripts/related_issues/components/issue_token.vue @gitlab-org/manage/authentication-and-authorization/approvers
|
||||||
/app/assets/javascripts/ci/runner/components/registration/registration_token.vue @gitlab-org/manage/authentication-and-authorization/approvers
|
/app/assets/javascripts/ci/runner/components/registration/registration_token.vue @gitlab-org/manage/authentication-and-authorization/approvers
|
||||||
/app/assets/javascripts/ci/runner/components/registration/registration_token_reset_dropdown_item.vue @gitlab-org/manage/authentication-and-authorization/approvers
|
/app/assets/javascripts/ci/runner/components/registration/registration_token_reset_dropdown_item.vue @gitlab-org/manage/authentication-and-authorization/approvers
|
||||||
/app/assets/javascripts/ci/runner/components/search_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
|
|
||||||
/app/assets/javascripts/token_access/components/ @gitlab-org/manage/authentication-and-authorization/approvers
|
/app/assets/javascripts/token_access/components/ @gitlab-org/manage/authentication-and-authorization/approvers
|
||||||
/app/assets/javascripts/token_access/index.js @gitlab-org/manage/authentication-and-authorization/approvers
|
/app/assets/javascripts/token_access/index.js @gitlab-org/manage/authentication-and-authorization/approvers
|
||||||
/app/assets/stylesheets/page_bundles/profile_two_factor_auth.scss @gitlab-org/manage/authentication-and-authorization/approvers
|
/app/assets/stylesheets/page_bundles/profile_two_factor_auth.scss @gitlab-org/manage/authentication-and-authorization/approvers
|
||||||
|
@ -1148,7 +1146,6 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
||||||
/ee/app/assets/javascripts/password/ @gitlab-org/manage/authentication-and-authorization/approvers
|
/ee/app/assets/javascripts/password/ @gitlab-org/manage/authentication-and-authorization/approvers
|
||||||
/ee/app/assets/javascripts/pipelines/components/pipelines_list/ @gitlab-org/manage/authentication-and-authorization/approvers
|
/ee/app/assets/javascripts/pipelines/components/pipelines_list/ @gitlab-org/manage/authentication-and-authorization/approvers
|
||||||
/ee/app/assets/javascripts/requirements/components/tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
|
/ee/app/assets/javascripts/requirements/components/tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
|
||||||
/ee/app/assets/javascripts/ci/runner/components/search_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
|
|
||||||
/ee/app/assets/javascripts/saml_providers/scim_token_service.js @gitlab-org/manage/authentication-and-authorization/approvers
|
/ee/app/assets/javascripts/saml_providers/scim_token_service.js @gitlab-org/manage/authentication-and-authorization/approvers
|
||||||
/ee/app/assets/javascripts/saml_sso/components/ @gitlab-org/manage/authentication-and-authorization/approvers
|
/ee/app/assets/javascripts/saml_sso/components/ @gitlab-org/manage/authentication-and-authorization/approvers
|
||||||
/ee/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_auth.vue @gitlab-org/manage/authentication-and-authorization/approvers
|
/ee/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_auth.vue @gitlab-org/manage/authentication-and-authorization/approvers
|
||||||
|
|
|
@ -1618,8 +1618,10 @@
|
||||||
rules:
|
rules:
|
||||||
- if: '$CODE_QUALITY_DISABLED'
|
- if: '$CODE_QUALITY_DISABLED'
|
||||||
when: never
|
when: never
|
||||||
|
# Run code_quality on master until https://gitlab.com/gitlab-org/gitlab/-/issues/363747 is resolved
|
||||||
|
- <<: *if-default-branch-refs
|
||||||
- <<: *if-default-refs
|
- <<: *if-default-refs
|
||||||
changes: *code-backstage-patterns
|
changes: *code-backstage-qa-patterns
|
||||||
|
|
||||||
.reports:rules:brakeman-sast:
|
.reports:rules:brakeman-sast:
|
||||||
rules:
|
rules:
|
||||||
|
|
|
@ -471,7 +471,9 @@ RSpec/FactoriesInMigrationSpecs:
|
||||||
RSpec/FactoryBot/AvoidCreate:
|
RSpec/FactoryBot/AvoidCreate:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
Include:
|
Include:
|
||||||
|
- 'spec/presenters/**/*.rb'
|
||||||
- 'spec/serializers/**/*.rb'
|
- 'spec/serializers/**/*.rb'
|
||||||
|
- 'ee/spec/presenters/**/*.rb'
|
||||||
- 'ee/spec/serializers/**/*.rb'
|
- 'ee/spec/serializers/**/*.rb'
|
||||||
|
|
||||||
Cop/IncludeSidekiqWorker:
|
Cop/IncludeSidekiqWorker:
|
||||||
|
|
|
@ -2,6 +2,28 @@
|
||||||
RSpec/FactoryBot/AvoidCreate:
|
RSpec/FactoryBot/AvoidCreate:
|
||||||
Details: grace period
|
Details: grace period
|
||||||
Exclude:
|
Exclude:
|
||||||
|
- 'ee/spec/presenters/approval_rule_presenter_spec.rb'
|
||||||
|
- 'ee/spec/presenters/audit_event_presenter_spec.rb'
|
||||||
|
- 'ee/spec/presenters/ci/build_presenter_spec.rb'
|
||||||
|
- 'ee/spec/presenters/ci/build_runner_presenter_spec.rb'
|
||||||
|
- 'ee/spec/presenters/ci/minutes/usage_presenter_spec.rb'
|
||||||
|
- 'ee/spec/presenters/ci/pipeline_presenter_spec.rb'
|
||||||
|
- 'ee/spec/presenters/dast/site_profile_presenter_spec.rb'
|
||||||
|
- 'ee/spec/presenters/ee/blob_presenter_spec.rb'
|
||||||
|
- 'ee/spec/presenters/ee/clusters/cluster_presenter_spec.rb'
|
||||||
|
- 'ee/spec/presenters/ee/instance_clusterable_presenter_spec.rb'
|
||||||
|
- 'ee/spec/presenters/ee/issue_presenter_spec.rb'
|
||||||
|
- 'ee/spec/presenters/ee/projects/security/configuration_presenter_spec.rb'
|
||||||
|
- 'ee/spec/presenters/epic_issue_presenter_spec.rb'
|
||||||
|
- 'ee/spec/presenters/epic_presenter_spec.rb'
|
||||||
|
- 'ee/spec/presenters/group_clusterable_presenter_spec.rb'
|
||||||
|
- 'ee/spec/presenters/label_presenter_spec.rb'
|
||||||
|
- 'ee/spec/presenters/merge_request_approver_presenter_spec.rb'
|
||||||
|
- 'ee/spec/presenters/merge_request_presenter_spec.rb'
|
||||||
|
- 'ee/spec/presenters/project_clusterable_presenter_spec.rb'
|
||||||
|
- 'ee/spec/presenters/subscription_presenter_spec.rb'
|
||||||
|
- 'ee/spec/presenters/vulnerability_presenter_spec.rb'
|
||||||
|
- 'ee/spec/presenters/web_hooks/group/hook_presenter_spec.rb'
|
||||||
- 'ee/spec/serializers/analytics/cycle_analytics/stage_entity_spec.rb'
|
- 'ee/spec/serializers/analytics/cycle_analytics/stage_entity_spec.rb'
|
||||||
- 'ee/spec/serializers/analytics/cycle_analytics/value_stream_errors_serializer_spec.rb'
|
- 'ee/spec/serializers/analytics/cycle_analytics/value_stream_errors_serializer_spec.rb'
|
||||||
- 'ee/spec/serializers/audit_event_entity_spec.rb'
|
- 'ee/spec/serializers/audit_event_entity_spec.rb'
|
||||||
|
@ -11,7 +33,6 @@ RSpec/FactoryBot/AvoidCreate:
|
||||||
- 'ee/spec/serializers/clusters/environment_entity_spec.rb'
|
- 'ee/spec/serializers/clusters/environment_entity_spec.rb'
|
||||||
- 'ee/spec/serializers/clusters/environment_serializer_spec.rb'
|
- 'ee/spec/serializers/clusters/environment_serializer_spec.rb'
|
||||||
- 'ee/spec/serializers/dashboard_environment_entity_spec.rb'
|
- 'ee/spec/serializers/dashboard_environment_entity_spec.rb'
|
||||||
- 'ee/spec/serializers/dashboard_environments_project_entity_spec.rb'
|
|
||||||
- 'ee/spec/serializers/dashboard_environments_serializer_spec.rb'
|
- 'ee/spec/serializers/dashboard_environments_serializer_spec.rb'
|
||||||
- 'ee/spec/serializers/dashboard_operations_project_entity_spec.rb'
|
- 'ee/spec/serializers/dashboard_operations_project_entity_spec.rb'
|
||||||
- 'ee/spec/serializers/dependency_entity_spec.rb'
|
- 'ee/spec/serializers/dependency_entity_spec.rb'
|
||||||
|
@ -73,6 +94,62 @@ RSpec/FactoryBot/AvoidCreate:
|
||||||
- 'ee/spec/serializers/vulnerabilities/scanner_entity_spec.rb'
|
- 'ee/spec/serializers/vulnerabilities/scanner_entity_spec.rb'
|
||||||
- 'ee/spec/serializers/vulnerability_entity_spec.rb'
|
- 'ee/spec/serializers/vulnerability_entity_spec.rb'
|
||||||
- 'ee/spec/serializers/vulnerability_note_entity_spec.rb'
|
- 'ee/spec/serializers/vulnerability_note_entity_spec.rb'
|
||||||
|
- 'spec/presenters/alert_management/alert_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/blob_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/blobs/notebook_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/ci/bridge_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/ci/build_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/ci/build_runner_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/ci/group_variable_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/ci/pipeline_artifacts/code_coverage_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/ci/pipeline_artifacts/code_quality_mr_diff_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/ci/pipeline_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/ci/stage_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/ci/trigger_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/ci/variable_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/clusterable_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/clusters/cluster_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/commit_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/commit_status_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/deployments/deployment_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/event_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/gitlab/blame_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/group_clusterable_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/instance_clusterable_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/issue_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/label_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/merge_request_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/milestone_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/packages/composer/packages_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/packages/conan/package_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/packages/detail/package_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/packages/helm/index_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/packages/npm/package_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/packages/nuget/package_metadata_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/packages/nuget/packages_metadata_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/packages/nuget/packages_versions_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/packages/nuget/search_results_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/packages/nuget/service_index_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/packages/pypi/simple_index_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/packages/pypi/simple_package_versions_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/pages_domain_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/project_clusterable_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/project_hook_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/project_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/projects/import_export/project_export_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/projects/security/configuration_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/projects/settings/deploy_keys_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/prometheus_alert_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/release_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/releases/link_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/search_service_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/service_hook_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/snippet_blob_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/snippet_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/terraform/modules_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/tree_entry_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/user_presenter_spec.rb'
|
||||||
|
- 'spec/presenters/web_hook_log_presenter_spec.rb'
|
||||||
- 'spec/serializers/access_token_entity_base_spec.rb'
|
- 'spec/serializers/access_token_entity_base_spec.rb'
|
||||||
- 'spec/serializers/analytics_build_entity_spec.rb'
|
- 'spec/serializers/analytics_build_entity_spec.rb'
|
||||||
- 'spec/serializers/analytics_build_serializer_spec.rb'
|
- 'spec/serializers/analytics_build_serializer_spec.rb'
|
||||||
|
|
4
Gemfile
4
Gemfile
|
@ -31,7 +31,7 @@ gem 'responders', '~> 3.0'
|
||||||
|
|
||||||
gem 'sprockets', '~> 3.7.0'
|
gem 'sprockets', '~> 3.7.0'
|
||||||
|
|
||||||
gem 'view_component', '~> 2.71.0'
|
gem 'view_component', '~> 2.74.1'
|
||||||
|
|
||||||
# Default values for AR models
|
# Default values for AR models
|
||||||
gem 'default_value_for', '~> 3.4.0'
|
gem 'default_value_for', '~> 3.4.0'
|
||||||
|
@ -134,7 +134,7 @@ gem 'hashie', '~> 5.0.0'
|
||||||
gem 'hashie-forbidden_attributes'
|
gem 'hashie-forbidden_attributes'
|
||||||
|
|
||||||
# Pagination
|
# Pagination
|
||||||
gem 'kaminari', '~> 1.0'
|
gem 'kaminari', '~> 1.2.2'
|
||||||
|
|
||||||
# HAML
|
# HAML
|
||||||
gem 'hamlit', '~> 2.15.0'
|
gem 'hamlit', '~> 2.15.0'
|
||||||
|
|
|
@ -294,10 +294,10 @@
|
||||||
{"name":"json_schemer","version":"0.2.18","platform":"ruby","checksum":"3362c21efbefdd12ce994e541a1e7fdb86fd267a6541dd8715e8a580fe3b6be6"},
|
{"name":"json_schemer","version":"0.2.18","platform":"ruby","checksum":"3362c21efbefdd12ce994e541a1e7fdb86fd267a6541dd8715e8a580fe3b6be6"},
|
||||||
{"name":"jsonpath","version":"1.1.2","platform":"ruby","checksum":"6804124c244d04418218acb85b15c7caa79c592d7d6970195300428458946d3a"},
|
{"name":"jsonpath","version":"1.1.2","platform":"ruby","checksum":"6804124c244d04418218acb85b15c7caa79c592d7d6970195300428458946d3a"},
|
||||||
{"name":"jwt","version":"2.1.0","platform":"ruby","checksum":"7e7e7ffc1a5ebce628ac7da428341c50615a3a10ac47bb74c22c1cba325613f0"},
|
{"name":"jwt","version":"2.1.0","platform":"ruby","checksum":"7e7e7ffc1a5ebce628ac7da428341c50615a3a10ac47bb74c22c1cba325613f0"},
|
||||||
{"name":"kaminari","version":"1.2.1","platform":"ruby","checksum":"03b26388ebb732b708e40b4f1d858c4cbc58d7cb18d12eeb9364176f23c3b3ef"},
|
{"name":"kaminari","version":"1.2.2","platform":"ruby","checksum":"c4076ff9adccc6109408333f87b5c4abbda5e39dc464bd4c66d06d9f73442a3e"},
|
||||||
{"name":"kaminari-actionview","version":"1.2.1","platform":"ruby","checksum":"31a3dc6955e7dff95f6133e1f4c03b9dec36d714b632330034ee51b33d0c1770"},
|
{"name":"kaminari-actionview","version":"1.2.2","platform":"ruby","checksum":"1330f6fc8b59a4a4ef6a549ff8a224797289ebf7a3a503e8c1652535287cc909"},
|
||||||
{"name":"kaminari-activerecord","version":"1.2.1","platform":"ruby","checksum":"8327823ddf8a8e059ce10d9da0c784ef44d5323e9f6d53054e03d26876efc50a"},
|
{"name":"kaminari-activerecord","version":"1.2.2","platform":"ruby","checksum":"0dd3a67bab356a356f36b3b7236bcb81cef313095365befe8e98057dd2472430"},
|
||||||
{"name":"kaminari-core","version":"1.2.1","platform":"ruby","checksum":"9ff2f0fa5a454731943a847bbd9960f0b9f859dd6910df5f40d6c9e049660440"},
|
{"name":"kaminari-core","version":"1.2.2","platform":"ruby","checksum":"3bd26fec7370645af40ca73b9426a448d09b8a8ba7afa9ba3c3e0d39cdbb83ff"},
|
||||||
{"name":"kas-grpc","version":"0.0.2","platform":"ruby","checksum":"111ff7515952e939f491297ba4c69a218b72d9d0ef8e5bff80a5df6a56df9a16"},
|
{"name":"kas-grpc","version":"0.0.2","platform":"ruby","checksum":"111ff7515952e939f491297ba4c69a218b72d9d0ef8e5bff80a5df6a56df9a16"},
|
||||||
{"name":"knapsack","version":"1.21.1","platform":"ruby","checksum":"82f70422adebcacec1b514f6ebff65265fc85d836e3c320718a160d8ac41cf14"},
|
{"name":"knapsack","version":"1.21.1","platform":"ruby","checksum":"82f70422adebcacec1b514f6ebff65265fc85d836e3c320718a160d8ac41cf14"},
|
||||||
{"name":"kramdown","version":"2.3.2","platform":"ruby","checksum":"cb4530c2e9d16481591df2c9336723683c354e5416a5dd3e447fa48215a6a71c"},
|
{"name":"kramdown","version":"2.3.2","platform":"ruby","checksum":"cb4530c2e9d16481591df2c9336723683c354e5416a5dd3e447fa48215a6a71c"},
|
||||||
|
@ -620,7 +620,7 @@
|
||||||
{"name":"validates_hostname","version":"1.0.11","platform":"ruby","checksum":"d506bae0342ec14c920eb319e057fc1886c321a59b85b4b6e966ee4b88fab8c3"},
|
{"name":"validates_hostname","version":"1.0.11","platform":"ruby","checksum":"d506bae0342ec14c920eb319e057fc1886c321a59b85b4b6e966ee4b88fab8c3"},
|
||||||
{"name":"version_gem","version":"1.1.0","platform":"ruby","checksum":"6b009518020db57f51ec7b410213fae2bf692baea9f1b51770db97fbc93d9a80"},
|
{"name":"version_gem","version":"1.1.0","platform":"ruby","checksum":"6b009518020db57f51ec7b410213fae2bf692baea9f1b51770db97fbc93d9a80"},
|
||||||
{"name":"version_sorter","version":"2.2.4","platform":"ruby","checksum":"7ad071609edfaa3cf28c42d83b1a03096e43512244ae5a9e2fce1404f7e06d41"},
|
{"name":"version_sorter","version":"2.2.4","platform":"ruby","checksum":"7ad071609edfaa3cf28c42d83b1a03096e43512244ae5a9e2fce1404f7e06d41"},
|
||||||
{"name":"view_component","version":"2.71.0","platform":"ruby","checksum":"c1880647800d9cfb03ff4ba92313db624a4a4b3d5753e137effe86e5f2b3662b"},
|
{"name":"view_component","version":"2.74.1","platform":"ruby","checksum":"0bbd47a9c11455a45043dc01aa604db708654718a4d8755c911425482e8392c0"},
|
||||||
{"name":"vmstat","version":"2.3.0","platform":"ruby","checksum":"ab5446a3e3bd0a9cdb9d9ac69a0bbd119c4f161d945a0846a519dd7018af656d"},
|
{"name":"vmstat","version":"2.3.0","platform":"ruby","checksum":"ab5446a3e3bd0a9cdb9d9ac69a0bbd119c4f161d945a0846a519dd7018af656d"},
|
||||||
{"name":"warden","version":"1.2.9","platform":"ruby","checksum":"46684f885d35a69dbb883deabf85a222c8e427a957804719e143005df7a1efd0"},
|
{"name":"warden","version":"1.2.9","platform":"ruby","checksum":"46684f885d35a69dbb883deabf85a222c8e427a957804719e143005df7a1efd0"},
|
||||||
{"name":"warning","version":"1.3.0","platform":"ruby","checksum":"23695a5d8e50bd5c46068931b529bee0b28e4982cbcefbe77d867800dde8069e"},
|
{"name":"warning","version":"1.3.0","platform":"ruby","checksum":"23695a5d8e50bd5c46068931b529bee0b28e4982cbcefbe77d867800dde8069e"},
|
||||||
|
|
24
Gemfile.lock
24
Gemfile.lock
|
@ -778,18 +778,18 @@ GEM
|
||||||
jsonpath (1.1.2)
|
jsonpath (1.1.2)
|
||||||
multi_json
|
multi_json
|
||||||
jwt (2.1.0)
|
jwt (2.1.0)
|
||||||
kaminari (1.2.1)
|
kaminari (1.2.2)
|
||||||
activesupport (>= 4.1.0)
|
activesupport (>= 4.1.0)
|
||||||
kaminari-actionview (= 1.2.1)
|
kaminari-actionview (= 1.2.2)
|
||||||
kaminari-activerecord (= 1.2.1)
|
kaminari-activerecord (= 1.2.2)
|
||||||
kaminari-core (= 1.2.1)
|
kaminari-core (= 1.2.2)
|
||||||
kaminari-actionview (1.2.1)
|
kaminari-actionview (1.2.2)
|
||||||
actionview
|
actionview
|
||||||
kaminari-core (= 1.2.1)
|
kaminari-core (= 1.2.2)
|
||||||
kaminari-activerecord (1.2.1)
|
kaminari-activerecord (1.2.2)
|
||||||
activerecord
|
activerecord
|
||||||
kaminari-core (= 1.2.1)
|
kaminari-core (= 1.2.2)
|
||||||
kaminari-core (1.2.1)
|
kaminari-core (1.2.2)
|
||||||
kas-grpc (0.0.2)
|
kas-grpc (0.0.2)
|
||||||
grpc (~> 1.0)
|
grpc (~> 1.0)
|
||||||
knapsack (1.21.1)
|
knapsack (1.21.1)
|
||||||
|
@ -1488,7 +1488,7 @@ GEM
|
||||||
activesupport (>= 3.0)
|
activesupport (>= 3.0)
|
||||||
version_gem (1.1.0)
|
version_gem (1.1.0)
|
||||||
version_sorter (2.2.4)
|
version_sorter (2.2.4)
|
||||||
view_component (2.71.0)
|
view_component (2.74.1)
|
||||||
activesupport (>= 5.0.0, < 8.0)
|
activesupport (>= 5.0.0, < 8.0)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
method_source (~> 1.0)
|
method_source (~> 1.0)
|
||||||
|
@ -1677,7 +1677,7 @@ DEPENDENCIES
|
||||||
json (~> 2.5.1)
|
json (~> 2.5.1)
|
||||||
json_schemer (~> 0.2.18)
|
json_schemer (~> 0.2.18)
|
||||||
jwt (~> 2.1.0)
|
jwt (~> 2.1.0)
|
||||||
kaminari (~> 1.0)
|
kaminari (~> 1.2.2)
|
||||||
kas-grpc (~> 0.0.2)
|
kas-grpc (~> 0.0.2)
|
||||||
knapsack (~> 1.21.1)
|
knapsack (~> 1.21.1)
|
||||||
kramdown (~> 2.3.1)
|
kramdown (~> 2.3.1)
|
||||||
|
@ -1825,7 +1825,7 @@ DEPENDENCIES
|
||||||
valid_email (~> 0.1)
|
valid_email (~> 0.1)
|
||||||
validates_hostname (~> 1.0.11)
|
validates_hostname (~> 1.0.11)
|
||||||
version_sorter (~> 2.2.4)
|
version_sorter (~> 2.2.4)
|
||||||
view_component (~> 2.71.0)
|
view_component (~> 2.74.1)
|
||||||
vmstat (~> 2.3.0)
|
vmstat (~> 2.3.0)
|
||||||
warning (~> 1.3.0)
|
warning (~> 1.3.0)
|
||||||
webauthn (~> 2.3)
|
webauthn (~> 2.3)
|
||||||
|
|
|
@ -8,7 +8,7 @@ export const initTimezoneDropdown = () => {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { timezoneData, initialValue } = el.dataset;
|
const { timezoneData, initialValue, name } = el.dataset;
|
||||||
const timezones = JSON.parse(timezoneData);
|
const timezones = JSON.parse(timezoneData);
|
||||||
|
|
||||||
const timezoneDropdown = new Vue({
|
const timezoneDropdown = new Vue({
|
||||||
|
@ -23,7 +23,7 @@ export const initTimezoneDropdown = () => {
|
||||||
props: {
|
props: {
|
||||||
value: this.value,
|
value: this.value,
|
||||||
timezoneData: timezones,
|
timezoneData: timezones,
|
||||||
name: 'user[timezone]',
|
name,
|
||||||
},
|
},
|
||||||
class: 'gl-md-form-input-lg',
|
class: 'gl-md-form-input-lg',
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,8 +6,8 @@ import { REF_TYPE_BRANCHES, REF_TYPE_TAGS } from '~/ref/constants';
|
||||||
import setupNativeFormVariableList from '~/ci_variable_list/native_form_variable_list';
|
import setupNativeFormVariableList from '~/ci_variable_list/native_form_variable_list';
|
||||||
import GlFieldErrors from '~/gl_field_errors';
|
import GlFieldErrors from '~/gl_field_errors';
|
||||||
import Translate from '~/vue_shared/translate';
|
import Translate from '~/vue_shared/translate';
|
||||||
|
import { initTimezoneDropdown } from '../../../profiles/init_timezone_dropdown';
|
||||||
import IntervalPatternInput from './components/interval_pattern_input.vue';
|
import IntervalPatternInput from './components/interval_pattern_input.vue';
|
||||||
import TimezoneDropdown from './components/timezone_dropdown';
|
|
||||||
|
|
||||||
Vue.use(Translate);
|
Vue.use(Translate);
|
||||||
|
|
||||||
|
@ -81,13 +81,6 @@ export default () => {
|
||||||
|
|
||||||
const formElement = document.getElementById('new-pipeline-schedule-form');
|
const formElement = document.getElementById('new-pipeline-schedule-form');
|
||||||
|
|
||||||
gl.timezoneDropdown = new TimezoneDropdown({
|
|
||||||
$dropdownEl: $('.js-timezone-dropdown'),
|
|
||||||
$inputEl: $('#schedule_cron_timezone'),
|
|
||||||
onSelectTimezone: () => {
|
|
||||||
gl.pipelineScheduleFieldErrors.updateFormValidityState();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
gl.pipelineScheduleFieldErrors = new GlFieldErrors(formElement);
|
gl.pipelineScheduleFieldErrors = new GlFieldErrors(formElement);
|
||||||
|
|
||||||
initTargetRefDropdown();
|
initTargetRefDropdown();
|
||||||
|
@ -97,3 +90,5 @@ export default () => {
|
||||||
formField: 'schedule',
|
formField: 'schedule',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
initTimezoneDropdown();
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Projects::ProductAnalyticsController < Projects::ApplicationController
|
|
||||||
before_action :feature_enabled!, only: [:index, :setup, :test, :graphs]
|
|
||||||
before_action :authorize_read_product_analytics!
|
|
||||||
before_action :tracker_variables, only: [:setup, :test]
|
|
||||||
|
|
||||||
feature_category :product_analytics
|
|
||||||
|
|
||||||
def index
|
|
||||||
@events = product_analytics_events.order_by_time.page(params[:page])
|
|
||||||
end
|
|
||||||
|
|
||||||
def setup
|
|
||||||
end
|
|
||||||
|
|
||||||
def test
|
|
||||||
@event = product_analytics_events.try(:first)
|
|
||||||
end
|
|
||||||
|
|
||||||
def graphs
|
|
||||||
@graphs = []
|
|
||||||
@timerange = 30
|
|
||||||
|
|
||||||
requested_graphs = %w(platform os_timezone br_lang doc_charset)
|
|
||||||
|
|
||||||
requested_graphs.each do |graph|
|
|
||||||
@graphs << ProductAnalytics::BuildGraphService
|
|
||||||
.new(project, { graph: graph, timerange: @timerange })
|
|
||||||
.execute
|
|
||||||
end
|
|
||||||
|
|
||||||
@activity_graph = ProductAnalytics::BuildActivityGraphService
|
|
||||||
.new(project, { timerange: @timerange })
|
|
||||||
.execute
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def product_analytics_events
|
|
||||||
@project.product_analytics_events
|
|
||||||
end
|
|
||||||
|
|
||||||
def tracker_variables
|
|
||||||
# We use project id as Snowplow appId
|
|
||||||
@project_id = @project.id.to_s
|
|
||||||
|
|
||||||
# Snowplow remembers values like appId and platform between reloads.
|
|
||||||
# That is why we have to rename the tracker with a random integer.
|
|
||||||
@random = rand(999999)
|
|
||||||
|
|
||||||
# Generate random platform every time a tracker is rendered.
|
|
||||||
@platform = %w(web mob app)[(@random % 3)]
|
|
||||||
end
|
|
||||||
|
|
||||||
def feature_enabled!
|
|
||||||
render_404 unless Feature.enabled?(:product_analytics, @project)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Projects::ProductAnalyticsController.prepend_mod_with('Projects::ProductAnalyticsController')
|
|
|
@ -43,7 +43,7 @@ class EnvironmentSerializer < BaseSerializer
|
||||||
# immediately.
|
# immediately.
|
||||||
items = @paginator.paginate(items) if paginated?
|
items = @paginator.paginate(items) if paginated?
|
||||||
|
|
||||||
environments = batch_load(resource.where(id: items.map(&:last_id)))
|
environments = batch_load(Environment.where(id: items.map(&:last_id)))
|
||||||
environments_by_id = environments.index_by(&:id)
|
environments_by_id = environments.index_by(&:id)
|
||||||
|
|
||||||
items.map do |item|
|
items.map do |item|
|
||||||
|
|
|
@ -62,7 +62,7 @@
|
||||||
%p= s_("Profiles|Set your local time zone.")
|
%p= s_("Profiles|Set your local time zone.")
|
||||||
.col-lg-8
|
.col-lg-8
|
||||||
= f.label :user_timezone, _("Time zone")
|
= f.label :user_timezone, _("Time zone")
|
||||||
.js-timezone-dropdown{ data: { timezone_data: timezone_data.to_json, initial_value: @user.timezone } }
|
.js-timezone-dropdown{ data: { timezone_data: timezone_data.to_json, initial_value: @user.timezone, name: 'user[timezone]' } }
|
||||||
.col-lg-12
|
.col-lg-12
|
||||||
%hr
|
%hr
|
||||||
.row.js-search-settings-section
|
.row.js-search-settings-section
|
||||||
|
|
|
@ -9,10 +9,10 @@
|
||||||
= f.label :cron, _('Interval Pattern'), class: 'label-bold'
|
= f.label :cron, _('Interval Pattern'), class: 'label-bold'
|
||||||
#interval-pattern-input{ data: { initial_interval: @schedule.cron, daily_limit: @schedule.daily_limit } }
|
#interval-pattern-input{ data: { initial_interval: @schedule.cron, daily_limit: @schedule.daily_limit } }
|
||||||
.form-group.row
|
.form-group.row
|
||||||
.col-md-9
|
.col-md-9{ data: { testid: 'schedule-timezone' } }
|
||||||
= f.label :cron_timezone, _('Cron Timezone'), class: 'label-bold'
|
= f.label :cron_timezone, _("Cron Timezone")
|
||||||
= dropdown_tag(_("Select a timezone"), options: { toggle_class: 'gl-button btn btn-default js-timezone-dropdown w-100', dropdown_class: 'w-100', title: _("Select a timezone"), filter: true, placeholder: s_("OfSearchInADropdown|Filter"), data: { data: timezone_data } } )
|
.js-timezone-dropdown{ data: { timezone_data: timezone_data.to_json, initial_value: @schedule.cron_timezone, name: 'schedule[cron_timezone]' } }
|
||||||
= f.text_field :cron_timezone, value: @schedule.cron_timezone, id: 'schedule_cron_timezone', class: 'hidden', name: 'schedule[cron_timezone]', required: true
|
|
||||||
.form-group.row
|
.form-group.row
|
||||||
.col-md-9
|
.col-md-9
|
||||||
= f.label :ref, _('Target branch or tag'), class: 'label-bold'
|
= f.label :ref, _('Target branch or tag'), class: 'label-bold'
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
- graph = local_assigns.fetch(:graph)
|
|
||||||
|
|
||||||
%h3
|
|
||||||
= graph[:id]
|
|
||||||
|
|
||||||
.js-project-analytics-chart{ "data-chart-data": graph.to_json, "data-chart-id": graph[:id] }
|
|
|
@ -1,5 +0,0 @@
|
||||||
= gl_tabs_nav({ class: 'mb-3'}) do
|
|
||||||
= gl_tab_link_to _('Events'), project_product_analytics_path(@project)
|
|
||||||
= gl_tab_link_to _('Graphs'), graphs_project_product_analytics_path(@project)
|
|
||||||
= gl_tab_link_to _('Test'), test_project_product_analytics_path(@project)
|
|
||||||
= gl_tab_link_to _('Setup'), setup_project_product_analytics_path(@project)
|
|
|
@ -1,10 +0,0 @@
|
||||||
;(function(p,l,o,w,i,n,g){if(!p[i]){p.GlobalSnowplowNamespace=p.GlobalSnowplowNamespace||[];
|
|
||||||
p.GlobalSnowplowNamespace.push(i);p[i]=function(){(p[i].q=p[i].q||[]).push(arguments)
|
|
||||||
};p[i].q=p[i].q||[];n=l.createElement(o);g=l.getElementsByTagName(o)[0];n.async=1;
|
|
||||||
n.src=w;g.parentNode.insertBefore(n,g)}}(window,document,"script","<%= product_analytics_tracker_url -%>","snowplow<%= @random -%>"));
|
|
||||||
snowplow<%= @random -%>("newTracker", "sp", "<%= product_analytics_tracker_collector_url -%>", {
|
|
||||||
appId: "<%= @project_id -%>",
|
|
||||||
platform: "<%= @platform -%>",
|
|
||||||
eventMethod: "get"
|
|
||||||
});
|
|
||||||
snowplow<%= @random -%>('trackPageView');
|
|
|
@ -1,16 +0,0 @@
|
||||||
- page_title _('Product Analytics')
|
|
||||||
|
|
||||||
= render 'links'
|
|
||||||
|
|
||||||
%p
|
|
||||||
= _('Showing graphs based on events of the last %{timerange} days.') % { timerange: @timerange }
|
|
||||||
|
|
||||||
|
|
||||||
.gl-mb-3
|
|
||||||
= render 'graph', graph: @activity_graph
|
|
||||||
|
|
||||||
- @graphs.each_slice(2) do |pair|
|
|
||||||
.row.append-bottom-10
|
|
||||||
- pair.each do |graph|
|
|
||||||
.col-md-6{ id: graph[:id] }
|
|
||||||
= render 'graph', graph: graph
|
|
|
@ -1,16 +0,0 @@
|
||||||
- page_title _('Product Analytics')
|
|
||||||
|
|
||||||
= render 'links'
|
|
||||||
|
|
||||||
- if @events.any?
|
|
||||||
%p
|
|
||||||
- if @events.total_count > @events.size
|
|
||||||
= _('Number of events for this project: %{total_count}.') % { total_count: number_with_delimiter(@events.total_count) }
|
|
||||||
%ol
|
|
||||||
- @events.each do |event|
|
|
||||||
%li
|
|
||||||
%code= event.as_json_wo_empty
|
|
||||||
- else
|
|
||||||
.empty-state
|
|
||||||
.text-content
|
|
||||||
= _('There are currently no events.')
|
|
|
@ -1,12 +0,0 @@
|
||||||
- page_title _('Product Analytics')
|
|
||||||
|
|
||||||
= render 'links'
|
|
||||||
|
|
||||||
%p
|
|
||||||
= _('Copy the code below to implement tracking in your application:')
|
|
||||||
|
|
||||||
%pre
|
|
||||||
= render "tracker"
|
|
||||||
|
|
||||||
%p.hint
|
|
||||||
= _('A platform value can be web, mob or app.')
|
|
|
@ -1,17 +0,0 @@
|
||||||
- page_title _('Product Analytics')
|
|
||||||
|
|
||||||
= render 'links'
|
|
||||||
|
|
||||||
%p
|
|
||||||
= _('This page sends a payload. Go back to the events page to see a newly created event.')
|
|
||||||
|
|
||||||
- if @event
|
|
||||||
%p
|
|
||||||
= _('Last item before this page loaded in your browser:')
|
|
||||||
|
|
||||||
%code
|
|
||||||
= @event.as_json_wo_empty
|
|
||||||
|
|
||||||
-# haml-lint:disable InlineJavaScript
|
|
||||||
:javascript
|
|
||||||
#{render 'tracker'}
|
|
|
@ -1,8 +0,0 @@
|
||||||
---
|
|
||||||
name: product_analytics
|
|
||||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36443
|
|
||||||
rollout_issue_url:
|
|
||||||
milestone: '13.2'
|
|
||||||
type: development
|
|
||||||
group: group::product intelligence
|
|
||||||
default_enabled: false
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
require 'sidekiq/web'
|
require 'sidekiq/web'
|
||||||
require 'sidekiq/cron/web'
|
require 'sidekiq/cron/web'
|
||||||
require 'product_analytics/collector_app'
|
|
||||||
|
|
||||||
InitializerConnections.with_disabled_database_connections do
|
InitializerConnections.with_disabled_database_connections do
|
||||||
Rails.application.routes.draw do
|
Rails.application.routes.draw do
|
||||||
|
@ -216,9 +215,6 @@ InitializerConnections.with_disabled_database_connections do
|
||||||
# Deprecated route for permanent failures
|
# Deprecated route for permanent failures
|
||||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/362606
|
# https://gitlab.com/gitlab-org/gitlab/-/issues/362606
|
||||||
post '/members/mailgun/permanent_failures' => 'mailgun/webhooks#process_webhook'
|
post '/members/mailgun/permanent_failures' => 'mailgun/webhooks#process_webhook'
|
||||||
|
|
||||||
# Product analytics collector
|
|
||||||
match '/collector/i', to: ProductAnalytics::CollectorApp.new, via: :all
|
|
||||||
end
|
end
|
||||||
# End of the /-/ scope.
|
# End of the /-/ scope.
|
||||||
|
|
||||||
|
|
|
@ -380,14 +380,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
|
||||||
resources :projects, only: :index
|
resources :projects, only: :index
|
||||||
end
|
end
|
||||||
|
|
||||||
resources :product_analytics, only: [:index] do
|
|
||||||
collection do
|
|
||||||
get :setup
|
|
||||||
get :test
|
|
||||||
get :graphs
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
resources :error_tracking, only: [:index], controller: :error_tracking do
|
resources :error_tracking, only: [:index], controller: :error_tracking do
|
||||||
collection do
|
collection do
|
||||||
get ':issue_id/details',
|
get ':issue_id/details',
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddUniqueIndexOnCiRunnersToken < Gitlab::Database::Migration[2.0]
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
INDEX_NAME = 'index_uniq_ci_runners_on_token'
|
||||||
|
|
||||||
|
def up
|
||||||
|
finalize_background_migration 'ResetDuplicateCiRunnersTokenValues'
|
||||||
|
|
||||||
|
add_concurrent_index :ci_runners,
|
||||||
|
:token,
|
||||||
|
name: INDEX_NAME,
|
||||||
|
unique: true
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
remove_concurrent_index_by_name :ci_runners, INDEX_NAME
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,20 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddUniqueIndexOnCiRunnersTokenEncrypted < Gitlab::Database::Migration[2.0]
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
INDEX_NAME = 'index_uniq_ci_runners_on_token_encrypted'
|
||||||
|
|
||||||
|
def up
|
||||||
|
finalize_background_migration 'ResetDuplicateCiRunnersTokenEncryptedValues'
|
||||||
|
|
||||||
|
add_concurrent_index :ci_runners,
|
||||||
|
:token_encrypted,
|
||||||
|
name: INDEX_NAME,
|
||||||
|
unique: true
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
remove_concurrent_index_by_name :ci_runners, INDEX_NAME
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,17 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class DropIndexOnCiRunnersToken < Gitlab::Database::Migration[2.0]
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
INDEX_NAME = 'index_ci_runners_on_token'
|
||||||
|
|
||||||
|
def up
|
||||||
|
remove_concurrent_index_by_name :ci_runners, INDEX_NAME
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
add_concurrent_index :ci_runners,
|
||||||
|
:token,
|
||||||
|
name: INDEX_NAME
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,17 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class DropIndexOnCiRunnersTokenEncrypted < Gitlab::Database::Migration[2.0]
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
INDEX_NAME = 'index_ci_runners_on_token_encrypted'
|
||||||
|
|
||||||
|
def up
|
||||||
|
remove_concurrent_index_by_name :ci_runners, INDEX_NAME
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
add_concurrent_index :ci_runners,
|
||||||
|
:token_encrypted,
|
||||||
|
name: INDEX_NAME
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1 @@
|
||||||
|
10caa548bccc134775ed14f85eae2b2063e83afe4a932982c353ecf1549a557d
|
|
@ -0,0 +1 @@
|
||||||
|
86d979a179c504508fd2e9c1a62e935884297054b13b78a4c1460679d75f5b96
|
|
@ -0,0 +1 @@
|
||||||
|
a9122e3772587b85a889740ccc54d48b6ead91a3b472d712e1e8bf5946655cf4
|
|
@ -0,0 +1 @@
|
||||||
|
d7c109cba935e1f355789dffa1d64b29b787f44ced7b0d3090e19a2dd0b8e266
|
|
@ -28460,10 +28460,6 @@ CREATE INDEX index_ci_runners_on_locked ON ci_runners USING btree (locked);
|
||||||
|
|
||||||
CREATE INDEX index_ci_runners_on_runner_type ON ci_runners USING btree (runner_type);
|
CREATE INDEX index_ci_runners_on_runner_type ON ci_runners USING btree (runner_type);
|
||||||
|
|
||||||
CREATE INDEX index_ci_runners_on_token ON ci_runners USING btree (token);
|
|
||||||
|
|
||||||
CREATE INDEX index_ci_runners_on_token_encrypted ON ci_runners USING btree (token_encrypted);
|
|
||||||
|
|
||||||
CREATE INDEX index_ci_runners_on_token_expires_at_and_id_desc ON ci_runners USING btree (token_expires_at, id DESC);
|
CREATE INDEX index_ci_runners_on_token_expires_at_and_id_desc ON ci_runners USING btree (token_expires_at, id DESC);
|
||||||
|
|
||||||
CREATE INDEX index_ci_runners_on_token_expires_at_desc_and_id_desc ON ci_runners USING btree (token_expires_at DESC, id DESC);
|
CREATE INDEX index_ci_runners_on_token_expires_at_desc_and_id_desc ON ci_runners USING btree (token_expires_at DESC, id DESC);
|
||||||
|
@ -30668,6 +30664,10 @@ CREATE INDEX index_u2f_registrations_on_key_handle ON u2f_registrations USING bt
|
||||||
|
|
||||||
CREATE INDEX index_u2f_registrations_on_user_id ON u2f_registrations USING btree (user_id);
|
CREATE INDEX index_u2f_registrations_on_user_id ON u2f_registrations USING btree (user_id);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX index_uniq_ci_runners_on_token ON ci_runners USING btree (token);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX index_uniq_ci_runners_on_token_encrypted ON ci_runners USING btree (token_encrypted);
|
||||||
|
|
||||||
CREATE UNIQUE INDEX index_uniq_im_issuable_escalation_statuses_on_issue_id ON incident_management_issuable_escalation_statuses USING btree (issue_id);
|
CREATE UNIQUE INDEX index_uniq_im_issuable_escalation_statuses_on_issue_id ON incident_management_issuable_escalation_statuses USING btree (issue_id);
|
||||||
|
|
||||||
CREATE UNIQUE INDEX index_uniq_projects_on_runners_token ON projects USING btree (runners_token);
|
CREATE UNIQUE INDEX index_uniq_projects_on_runners_token ON projects USING btree (runners_token);
|
||||||
|
|
|
@ -118,10 +118,10 @@ Some feature flags can be enabled or disabled on a per project basis:
|
||||||
Feature.enable(:<feature flag>, Project.find(<project id>))
|
Feature.enable(:<feature flag>, Project.find(<project id>))
|
||||||
```
|
```
|
||||||
|
|
||||||
For example, to enable the [`:product_analytics`](../operations/product_analytics.md) feature flag for project `1234`:
|
For example, to enable the `:my_awesome_feature` feature flag for project `1234`:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
Feature.enable(:product_analytics, Project.find(1234))
|
Feature.enable(:my_awesome_feature, Project.find(1234))
|
||||||
```
|
```
|
||||||
|
|
||||||
`Feature.enable` and `Feature.disable` always return `true`, even if the application doesn't use the flag:
|
`Feature.enable` and `Feature.disable` always return `true`, even if the application doesn't use the flag:
|
||||||
|
|
|
@ -20,8 +20,10 @@ but there are [key differences](pipeline_architectures.md).
|
||||||
|
|
||||||
## Parent-child pipelines
|
## Parent-child pipelines
|
||||||
|
|
||||||
A parent pipeline is one that triggers a downstream pipeline in the same project.
|
A parent pipeline is a pipeline that triggers a downstream pipeline in the same project.
|
||||||
The downstream pipeline is called a child pipeline. Child pipelines:
|
The downstream pipeline is called a child pipeline.
|
||||||
|
|
||||||
|
Child pipelines:
|
||||||
|
|
||||||
- Run under the same project, ref, and commit SHA as the parent pipeline.
|
- Run under the same project, ref, and commit SHA as the parent pipeline.
|
||||||
- Do not directly affect the overall status of the ref the pipeline runs against. For example,
|
- Do not directly affect the overall status of the ref the pipeline runs against. For example,
|
||||||
|
@ -30,18 +32,18 @@ The downstream pipeline is called a child pipeline. Child pipelines:
|
||||||
pipeline is triggered with [`strategy:depend`](../yaml/index.md#triggerstrategy).
|
pipeline is triggered with [`strategy:depend`](../yaml/index.md#triggerstrategy).
|
||||||
- Are automatically canceled if the pipeline is configured with [`interruptible`](../yaml/index.md#interruptible)
|
- Are automatically canceled if the pipeline is configured with [`interruptible`](../yaml/index.md#interruptible)
|
||||||
when a new pipeline is created for the same ref.
|
when a new pipeline is created for the same ref.
|
||||||
- Are not displayed in the pipeline index page. You can only view child pipelines on
|
- Are not displayed in the project's pipeline list. You can only view child pipelines on
|
||||||
their parent pipeline's page.
|
their parent pipeline's details page.
|
||||||
|
|
||||||
### Nested child pipelines
|
### Nested child pipelines
|
||||||
|
|
||||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/29651) in GitLab 13.4.
|
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/29651) in GitLab 13.4.
|
||||||
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/243747) in GitLab 13.5.
|
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/243747) in GitLab 13.5.
|
||||||
|
|
||||||
Parent and child pipelines were introduced with a maximum depth of one level of child
|
Parent and child pipelines have a maximum depth of two levels of child pipelines.
|
||||||
pipelines, which was later increased to two. A parent pipeline can trigger many child
|
|
||||||
pipelines, and these child pipelines can trigger their own child pipelines. It's not
|
A parent pipeline can trigger many child pipelines, and these child pipelines can trigger
|
||||||
possible to trigger another level of child pipelines.
|
their own child pipelines. You cannot trigger another level of child pipelines.
|
||||||
|
|
||||||
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
|
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
|
||||||
For an overview, see [Nested Dynamic Pipelines](https://youtu.be/C5j3ju9je2M).
|
For an overview, see [Nested Dynamic Pipelines](https://youtu.be/C5j3ju9je2M).
|
||||||
|
@ -52,11 +54,6 @@ A pipeline in one project can trigger downstream pipelines in another project,
|
||||||
called multi-project pipelines. The user triggering the upstream pipeline must be able to
|
called multi-project pipelines. The user triggering the upstream pipeline must be able to
|
||||||
start pipelines in the downstream project, otherwise [the downstream pipeline fails to start](#trigger-job-fails-and-does-not-create-multi-project-pipeline).
|
start pipelines in the downstream project, otherwise [the downstream pipeline fails to start](#trigger-job-fails-and-does-not-create-multi-project-pipeline).
|
||||||
|
|
||||||
For example, you might deploy your web application from three different GitLab projects.
|
|
||||||
With multi-project pipelines you can trigger a pipeline in each project, where each
|
|
||||||
has its own build, test, and deploy process. You can visualize the connected pipelines
|
|
||||||
in one place, including all cross-project interdependencies.
|
|
||||||
|
|
||||||
Multi-project pipelines:
|
Multi-project pipelines:
|
||||||
|
|
||||||
- Are triggered from another project's pipeline, but the upstream (triggering) pipeline does
|
- Are triggered from another project's pipeline, but the upstream (triggering) pipeline does
|
||||||
|
@ -68,8 +65,7 @@ Multi-project pipelines:
|
||||||
- Are not automatically canceled in the downstream project when using [`interruptible`](../yaml/index.md#interruptible)
|
- Are not automatically canceled in the downstream project when using [`interruptible`](../yaml/index.md#interruptible)
|
||||||
if a new pipeline runs for the same ref in the upstream pipeline. They can be
|
if a new pipeline runs for the same ref in the upstream pipeline. They can be
|
||||||
automatically canceled if a new pipeline is triggered for the same ref on the downstream project.
|
automatically canceled if a new pipeline is triggered for the same ref on the downstream project.
|
||||||
- Multi-project pipelines are standalone pipelines because they are normal pipelines
|
- Are visible in the downstream project's pipeline list.
|
||||||
that happened to be triggered by an external project. They are all visible on the pipeline index page.
|
|
||||||
- Are independent, so there are no nesting limits.
|
- Are independent, so there are no nesting limits.
|
||||||
|
|
||||||
Learn more in the "Cross-project Pipeline Triggering and Visualization" demo at
|
Learn more in the "Cross-project Pipeline Triggering and Visualization" demo at
|
||||||
|
@ -87,24 +83,10 @@ always displays:
|
||||||
Use the [`trigger`](../yaml/index.md#trigger) keyword in your `.gitlab-ci.yml` file
|
Use the [`trigger`](../yaml/index.md#trigger) keyword in your `.gitlab-ci.yml` file
|
||||||
to create a job that triggers a downstream pipeline. This job is called a trigger job.
|
to create a job that triggers a downstream pipeline. This job is called a trigger job.
|
||||||
|
|
||||||
After the trigger job starts, the initial status of the job is `pending` while GitLab
|
|
||||||
attempts to create the downstream pipeline. If the downstream pipeline is created,
|
|
||||||
GitLab marks the job as passed, otherwise the job failed. Alternatively,
|
|
||||||
you can [set the trigger job to show the downstream pipeline's status](#mirror-the-status-of-a-downstream-pipeline-in-the-trigger-job)
|
|
||||||
instead.
|
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
::Tabs
|
::Tabs
|
||||||
|
|
||||||
:::TabTitle Multi-project pipeline
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
trigger_job:
|
|
||||||
trigger:
|
|
||||||
project: project-group/my-downstream-project
|
|
||||||
```
|
|
||||||
|
|
||||||
:::TabTitle Parent-child pipeline
|
:::TabTitle Parent-child pipeline
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -114,21 +96,36 @@ trigger_job:
|
||||||
- local: path/to/child-pipeline.yml
|
- local: path/to/child-pipeline.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
|
:::TabTitle Multi-project pipeline
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
trigger_job:
|
||||||
|
trigger:
|
||||||
|
project: project-group/my-downstream-project
|
||||||
|
```
|
||||||
|
|
||||||
::EndTabs
|
::EndTabs
|
||||||
|
|
||||||
|
After the trigger job starts, the initial status of the job is `pending` while GitLab
|
||||||
|
attempts to create the downstream pipeline. The trigger job shows `passed` if the
|
||||||
|
downstream pipeline is created successfully, otherwise it shows `failed`. Alternatively,
|
||||||
|
you can [set the trigger job to show the downstream pipeline's status](#mirror-the-status-of-a-downstream-pipeline-in-the-trigger-job)
|
||||||
|
instead.
|
||||||
|
|
||||||
### Use `rules` to control downstream pipeline jobs
|
### Use `rules` to control downstream pipeline jobs
|
||||||
|
|
||||||
You can use CI/CD variables or the [`rules`](../yaml/index.md#rulesif) keyword to
|
Use CI/CD variables or the [`rules`](../yaml/index.md#rulesif) keyword to
|
||||||
[control job behavior](../jobs/job_control.md) for downstream pipelines.
|
[control job behavior](../jobs/job_control.md) in downstream pipelines.
|
||||||
|
|
||||||
When a downstream pipeline is triggered with the [`trigger`](../yaml/index.md#trigger) keyword,
|
When you trigger a downstream pipeline with the [`trigger`](../yaml/index.md#trigger) keyword,
|
||||||
the value of the [`$CI_PIPELINE_SOURCE` predefined variable](../variables/predefined_variables.md)
|
the value of the [`$CI_PIPELINE_SOURCE` predefined variable](../variables/predefined_variables.md)
|
||||||
for all jobs is:
|
for all jobs is:
|
||||||
|
|
||||||
- `pipeline` for multi-project pipelines.
|
- `pipeline` for multi-project pipelines.
|
||||||
- `parent` for parent-child pipelines.
|
- `parent` for parent-child pipelines.
|
||||||
|
|
||||||
For example, with a multi-project pipeline:
|
For example, to control jobs in multi-project pipelines in a project that also runs
|
||||||
|
merge request pipelines:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
job1:
|
job1:
|
||||||
|
@ -148,35 +145,6 @@ job3:
|
||||||
script: echo "This job runs in both multi-project and merge request pipelines"
|
script: echo "This job runs in both multi-project and merge request pipelines"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Specify a branch for multi-project pipelines
|
|
||||||
|
|
||||||
You can specify a branch name for a multi-project pipeline to use. GitLab uses
|
|
||||||
the commit on the head of the branch to create the downstream pipeline:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
rspec:
|
|
||||||
stage: test
|
|
||||||
script: bundle exec rspec
|
|
||||||
|
|
||||||
staging:
|
|
||||||
stage: deploy
|
|
||||||
trigger:
|
|
||||||
project: my/deployment
|
|
||||||
branch: stable-11-2
|
|
||||||
```
|
|
||||||
|
|
||||||
Use:
|
|
||||||
|
|
||||||
- The `project` keyword to specify the full path to a downstream project.
|
|
||||||
In [GitLab 15.3 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/367660), variable expansion is
|
|
||||||
supported.
|
|
||||||
- The `branch` keyword to specify the name of a branch or [tag](../../topics/git/tags.md)
|
|
||||||
in the project specified by `project`. If you use a tag when a branch exists with the same
|
|
||||||
name, the downstream pipeline fails to create with the error: `downstream pipeline can not be created, Ref is ambiguous`.
|
|
||||||
|
|
||||||
In [GitLab 12.4 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/10126), variable expansion is
|
|
||||||
supported.
|
|
||||||
|
|
||||||
### Use a child pipeline configuration file in a different project
|
### Use a child pipeline configuration file in a different project
|
||||||
|
|
||||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/205157) in GitLab 13.5.
|
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/205157) in GitLab 13.5.
|
||||||
|
@ -211,8 +179,6 @@ microservice_a:
|
||||||
|
|
||||||
### Dynamic child pipelines
|
### Dynamic child pipelines
|
||||||
|
|
||||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/35632) in GitLab 12.9.
|
|
||||||
|
|
||||||
You can trigger a child pipeline from a YAML file generated in a job, instead of a
|
You can trigger a child pipeline from a YAML file generated in a job, instead of a
|
||||||
static file saved in your project. This technique can be very powerful for generating pipelines
|
static file saved in your project. This technique can be very powerful for generating pipelines
|
||||||
targeting content that changed or to build a matrix of targets and architectures.
|
targeting content that changed or to build a matrix of targets and architectures.
|
||||||
|
@ -234,43 +200,42 @@ To trigger a child pipeline from a dynamically generated configuration file:
|
||||||
|
|
||||||
1. Generate the configuration file in a job and save it as an [artifact](../yaml/index.md#artifactspaths):
|
1. Generate the configuration file in a job and save it as an [artifact](../yaml/index.md#artifactspaths):
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
generate-config:
|
generate-config:
|
||||||
stage: build
|
stage: build
|
||||||
script: generate-ci-config > generated-config.yml
|
script: generate-ci-config > generated-config.yml
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- generated-config.yml
|
- generated-config.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Configure the trigger job to run after the job that generated the configuration file,
|
1. Configure the trigger job to run after the job that generated the configuration file,
|
||||||
and set `include: artifact` to the generated artifact:
|
and set `include: artifact` to the generated artifact:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
child-pipeline:
|
child-pipeline:
|
||||||
stage: test
|
stage: test
|
||||||
trigger:
|
trigger:
|
||||||
include:
|
include:
|
||||||
- artifact: generated-config.yml
|
- artifact: generated-config.yml
|
||||||
job: generate-config
|
job: generate-config
|
||||||
```
|
```
|
||||||
|
|
||||||
In this example, `generated-config.yml` is extracted from the artifacts and used as the configuration
|
In this example, GitLab retrieves `generated-config.yml` and triggers a child pipeline
|
||||||
for triggering the child pipeline.
|
with the CI/CD configuration in that file.
|
||||||
|
|
||||||
The artifact path is parsed by GitLab, not the runner, so the path must match the
|
The artifact path is parsed by GitLab, not the runner, so the path must match the
|
||||||
syntax for the OS running GitLab. If GitLab is running on Linux but using a Windows
|
syntax for the OS running GitLab. If GitLab is running on Linux but using a Windows
|
||||||
runner for testing, the path separator for the trigger job is `/`. Other CI/CD
|
runner for testing, the path separator for the trigger job is `/`. Other CI/CD
|
||||||
configuration for jobs that use the Windows runner, like scripts, use `\`.
|
configuration for jobs that use the Windows runner, like scripts, use <code>\</code>.
|
||||||
|
|
||||||
### Run child pipelines with merge request pipelines
|
### Run child pipelines with merge request pipelines
|
||||||
|
|
||||||
To trigger a child pipeline as a [merge request pipeline](merge_request_pipelines.md):
|
To trigger a child pipeline as a [merge request pipeline](merge_request_pipelines.md):
|
||||||
|
|
||||||
1. Set the trigger job to run on merge requests:
|
1. Set the trigger job to run on merge requests in the parent pipeline's configuration file:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# parent .gitlab-ci.yml
|
|
||||||
microservice_a:
|
microservice_a:
|
||||||
trigger:
|
trigger:
|
||||||
include: path/to/microservice_a.yml
|
include: path/to/microservice_a.yml
|
||||||
|
@ -278,45 +243,50 @@ To trigger a child pipeline as a [merge request pipeline](merge_request_pipeline
|
||||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Configure the child pipeline jobs to run in merge request pipelines:
|
1. Configure the child pipeline jobs to run in merge request pipelines with [`rules`](../yaml/index.md#rules)
|
||||||
|
or [`workflow:rules`](../yaml/index.md#workflowrules). For example, with `rules`
|
||||||
|
in a child pipeline's configuration file:
|
||||||
|
|
||||||
- With [`workflow:rules`](../yaml/index.md#workflowrules):
|
```yaml
|
||||||
|
job1:
|
||||||
|
script: ...
|
||||||
|
rules:
|
||||||
|
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||||
|
|
||||||
```yaml
|
job2:
|
||||||
# child path/to/microservice_a.yml
|
script: ...
|
||||||
workflow:
|
rules:
|
||||||
rules:
|
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
```
|
||||||
|
|
||||||
job1:
|
### Specify a branch for multi-project pipelines
|
||||||
script: ...
|
|
||||||
|
|
||||||
job2:
|
You can specify the branch to use when triggering a multi-project pipeline. GitLab uses
|
||||||
script: ...
|
the commit on the head of the branch to create the downstream pipeline. For example:
|
||||||
```
|
|
||||||
|
|
||||||
- By configuring [rules](../yaml/index.md#rules) for each job:
|
```yaml
|
||||||
|
staging:
|
||||||
|
stage: deploy
|
||||||
|
trigger:
|
||||||
|
project: my/deployment
|
||||||
|
branch: stable-11-2
|
||||||
|
```
|
||||||
|
|
||||||
```yaml
|
Use:
|
||||||
# child path/to/microservice_a.yml
|
|
||||||
job1:
|
|
||||||
script: ...
|
|
||||||
rules:
|
|
||||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
|
||||||
|
|
||||||
job2:
|
- The `project` keyword to specify the full path to the downstream project.
|
||||||
script: ...
|
In [GitLab 15.3 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/367660),
|
||||||
rules:
|
you can use [variable expansion](../variables/where_variables_can_be_used.md#gitlab-ciyml-file).
|
||||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
- The `branch` keyword to specify the name of a branch or [tag](../../topics/git/tags.md)
|
||||||
```
|
in the project specified by `project`. You can use variable expansion.
|
||||||
|
|
||||||
## Trigger a multi-project pipeline by using the API
|
## Trigger a multi-project pipeline by using the API
|
||||||
|
|
||||||
You can use the [CI/CD job token (`CI_JOB_TOKEN`)](../jobs/ci_job_token.md) with the
|
You can use the [CI/CD job token (`CI_JOB_TOKEN`)](../jobs/ci_job_token.md) with the
|
||||||
[pipeline trigger API endpoint](../../api/pipeline_triggers.md#trigger-a-pipeline-with-a-token)
|
[pipeline trigger API endpoint](../../api/pipeline_triggers.md#trigger-a-pipeline-with-a-token)
|
||||||
to trigger multi-project pipelines from a CI/CD job. GitLab recognizes the source of the job token
|
to trigger multi-project pipelines from inside a CI/CD job. GitLab sets pipelines triggered
|
||||||
and marks the pipelines as related. In the pipeline graph, the relationships are displayed
|
with a job token as downstream pipelines of the pipeline that contains the job that
|
||||||
as inbound and outbound connections for upstream and downstream pipeline dependencies.
|
made the API call.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
|
@ -360,20 +330,11 @@ To cancel a downstream pipeline that is still running, select **Cancel** (**{can
|
||||||
|
|
||||||
### Mirror the status of a downstream pipeline in the trigger job
|
### Mirror the status of a downstream pipeline in the trigger job
|
||||||
|
|
||||||
You can mirror the pipeline status from the triggered pipeline to the source trigger job
|
You can mirror the status of the downstream pipeline in the trigger job
|
||||||
by using [`strategy: depend`](../yaml/index.md#triggerstrategy):
|
by using [`strategy: depend`](../yaml/index.md#triggerstrategy):
|
||||||
|
|
||||||
::Tabs
|
::Tabs
|
||||||
|
|
||||||
:::TabTitle Multi-project pipeline
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
trigger_job:
|
|
||||||
trigger:
|
|
||||||
project: my/project
|
|
||||||
strategy: depend
|
|
||||||
```
|
|
||||||
|
|
||||||
:::TabTitle Parent-child pipeline
|
:::TabTitle Parent-child pipeline
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -384,11 +345,20 @@ trigger_job:
|
||||||
strategy: depend
|
strategy: depend
|
||||||
```
|
```
|
||||||
|
|
||||||
|
:::TabTitle Multi-project pipeline
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
trigger_job:
|
||||||
|
trigger:
|
||||||
|
project: my/project
|
||||||
|
strategy: depend
|
||||||
|
```
|
||||||
|
|
||||||
::EndTabs
|
::EndTabs
|
||||||
|
|
||||||
### View multi-project pipelines in pipeline graphs **(PREMIUM)**
|
### View multi-project pipelines in pipeline graphs **(PREMIUM)**
|
||||||
|
|
||||||
When you trigger a multi-project pipeline, the downstream pipeline displays
|
After you trigger a multi-project pipeline, the downstream pipeline displays
|
||||||
to the right of the [pipeline graph](index.md#visualize-pipelines).
|
to the right of the [pipeline graph](index.md#visualize-pipelines).
|
||||||
|
|
||||||
![Multi-project pipeline graph](img/multi_project_pipeline_graph_v14_3.png)
|
![Multi-project pipeline graph](img/multi_project_pipeline_graph_v14_3.png)
|
||||||
|
@ -398,12 +368,13 @@ displays to the right of the mini graph.
|
||||||
|
|
||||||
![Multi-project pipeline mini graph](img/pipeline_mini_graph_v15_0.png)
|
![Multi-project pipeline mini graph](img/pipeline_mini_graph_v15_0.png)
|
||||||
|
|
||||||
## Pass artifacts to a downstream pipeline
|
## Fetch artifacts from an upstream pipeline
|
||||||
|
|
||||||
You can pass artifacts to a downstream pipeline by using [`needs:project`](../yaml/index.md#needsproject).
|
Use [`needs:project`](../yaml/index.md#needsproject) to fetch artifacts from an
|
||||||
|
upstream pipeline:
|
||||||
|
|
||||||
1. In a job in the upstream pipeline, save the artifacts using the [`artifacts`](../yaml/index.md#artifacts) keyword.
|
1. In the upstream pipeline, save the artifacts in a job with the [`artifacts`](../yaml/index.md#artifacts)
|
||||||
1. Trigger the downstream pipeline with a trigger job:
|
keyword, then trigger the downstream pipeline with a trigger job:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
build_artifacts:
|
build_artifacts:
|
||||||
|
@ -419,9 +390,7 @@ You can pass artifacts to a downstream pipeline by using [`needs:project`](../ya
|
||||||
trigger: my/downstream_project
|
trigger: my/downstream_project
|
||||||
```
|
```
|
||||||
|
|
||||||
1. In a job in the downstream pipeline, fetch the artifacts from the upstream pipeline
|
1. Use `needs:project` in a job in the downstream pipeline to fetch the artifacts.
|
||||||
by using `needs:project`. Set `job` to the job in the upstream pipeline to fetch artifacts from,
|
|
||||||
`ref` to the branch, and `artifacts: true`.
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
test:
|
test:
|
||||||
|
@ -435,22 +404,27 @@ You can pass artifacts to a downstream pipeline by using [`needs:project`](../ya
|
||||||
artifacts: true
|
artifacts: true
|
||||||
```
|
```
|
||||||
|
|
||||||
### Pass artifacts from a Merge Request pipeline
|
Set:
|
||||||
|
|
||||||
When you use `needs:project` to [pass artifacts to a downstream pipeline](#pass-artifacts-to-a-downstream-pipeline),
|
- `job` to the job in the upstream pipeline that created the artifacts.
|
||||||
|
- `ref` to the branch.
|
||||||
|
- `artifacts` to `true`.
|
||||||
|
|
||||||
|
### Fetch artifacts from an upstream merge request pipeline
|
||||||
|
|
||||||
|
When you use `needs:project` to [pass artifacts to a downstream pipeline](#fetch-artifacts-from-an-upstream-pipeline),
|
||||||
the `ref` value is usually a branch name, like `main` or `development`.
|
the `ref` value is usually a branch name, like `main` or `development`.
|
||||||
|
|
||||||
For merge request pipelines, the `ref` value is in the form of `refs/merge-requests/<id>/head`,
|
For [merge request pipelines](merge_request_pipelines.md), the `ref` value is in the form of `refs/merge-requests/<id>/head`,
|
||||||
where `id` is the merge request ID. You can retrieve this ref with the [`CI_MERGE_REQUEST_REF_PATH`](../variables/predefined_variables.md#predefined-variables-for-merge-request-pipelines)
|
where `id` is the merge request ID. You can retrieve this ref with the [`CI_MERGE_REQUEST_REF_PATH`](../variables/predefined_variables.md#predefined-variables-for-merge-request-pipelines)
|
||||||
CI/CD variable. Do not use a branch name as the `ref` with merge request pipelines,
|
CI/CD variable. Do not use a branch name as the `ref` with merge request pipelines,
|
||||||
because the downstream pipeline attempts to fetch artifacts from the latest branch pipeline.
|
because the downstream pipeline attempts to fetch artifacts from the latest branch pipeline.
|
||||||
|
|
||||||
To fetch the artifacts from the upstream `merge request` pipeline instead of the `branch` pipeline,
|
To fetch the artifacts from the upstream `merge request` pipeline instead of the `branch` pipeline,
|
||||||
pass this variable to the downstream pipeline using variable inheritance:
|
pass `CI_MERGE_REQUEST_REF_PATH` to the downstream pipeline using [variable inheritance](#pass-yaml-defined-cicd-variables):
|
||||||
|
|
||||||
1. In a job in the upstream pipeline, save the artifacts using the [`artifacts`](../yaml/index.md#artifacts) keyword.
|
1. In a job in the upstream pipeline, save the artifacts using the [`artifacts`](../yaml/index.md#artifacts) keyword.
|
||||||
1. In the job that triggers the downstream pipeline, pass the `$CI_MERGE_REQUEST_REF_PATH` variable by using
|
1. In the job that triggers the downstream pipeline, pass the `$CI_MERGE_REQUEST_REF_PATH` variable:
|
||||||
[variable inheritance](#pass-yaml-defined-cicd-variables):
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
build_artifacts:
|
build_artifacts:
|
||||||
|
@ -470,8 +444,7 @@ pass this variable to the downstream pipeline using variable inheritance:
|
||||||
```
|
```
|
||||||
|
|
||||||
1. In a job in the downstream pipeline, fetch the artifacts from the upstream pipeline
|
1. In a job in the downstream pipeline, fetch the artifacts from the upstream pipeline
|
||||||
by using `needs:project`. Set the `ref` to the `UPSTREAM_REF` variable, and `job`
|
by using `needs:project` and the passed variable as the `ref`:
|
||||||
to the job in the upstream pipeline to fetch artifacts from:
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
test:
|
test:
|
||||||
|
@ -485,86 +458,133 @@ pass this variable to the downstream pipeline using variable inheritance:
|
||||||
artifacts: true
|
artifacts: true
|
||||||
```
|
```
|
||||||
|
|
||||||
This method works for fetching artifacts from a regular merge request parent pipeline,
|
You can use this method to fetch artifacts from upstream merge request pipeline,
|
||||||
but fetching artifacts from [merge results](merged_results_pipelines.md) pipelines is not supported.
|
but not from [merge results pipelines](merged_results_pipelines.md).
|
||||||
|
|
||||||
## Pass CI/CD variables to a downstream pipeline
|
## Pass CI/CD variables to a downstream pipeline
|
||||||
|
|
||||||
You can pass CI/CD variables to a downstream pipeline with a few different methods,
|
You can pass [CI/CD variables](../variables/index.md) to a downstream pipeline with
|
||||||
based on where the variable is created or defined.
|
a few different methods, based on where the variable is created or defined.
|
||||||
|
|
||||||
### Pass YAML-defined CI/CD variables
|
### Pass YAML-defined CI/CD variables
|
||||||
|
|
||||||
You can use the `variables` keyword to pass CI/CD variables to a downstream pipeline,
|
You can use the `variables` keyword to pass CI/CD variables to a downstream pipeline.
|
||||||
just like you would for any other job.
|
|
||||||
|
|
||||||
For example, in a [multi-project pipeline](#multi-project-pipelines):
|
For example:
|
||||||
|
|
||||||
|
::Tabs
|
||||||
|
|
||||||
|
:::TabTitle Parent-child pipeline
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
rspec:
|
variables:
|
||||||
stage: test
|
VERSION: "1.0.0"
|
||||||
script: bundle exec rspec
|
|
||||||
|
|
||||||
staging:
|
staging:
|
||||||
variables:
|
variables:
|
||||||
ENVIRONMENT: staging
|
ENVIRONMENT: staging
|
||||||
stage: deploy
|
stage: deploy
|
||||||
trigger: my/deployment
|
trigger:
|
||||||
|
include:
|
||||||
|
- local: path/to/child-pipeline.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
The `ENVIRONMENT` variable is passed to every job defined in a downstream
|
:::TabTitle Multi-project pipeline
|
||||||
pipeline. It is available as a variable when GitLab Runner picks a job.
|
|
||||||
|
|
||||||
In the following configuration, the `MY_VARIABLE` variable is passed to the downstream pipeline
|
|
||||||
that is created when the `trigger-downstream` job is queued. This behavior is because `trigger-downstream`
|
|
||||||
job inherits variables declared in [global `variables`](../yaml/index.md#variables) blocks,
|
|
||||||
and then GitLab passes these variables to the downstream pipeline.
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
variables:
|
variables:
|
||||||
MY_VARIABLE: my-value
|
VERSION: "1.0.0"
|
||||||
|
|
||||||
trigger-downstream:
|
staging:
|
||||||
variables:
|
variables:
|
||||||
ENVIRONMENT: something
|
ENVIRONMENT: staging
|
||||||
trigger: my/project
|
stage: deploy
|
||||||
|
trigger: my-group/my-deployment-project
|
||||||
```
|
```
|
||||||
|
|
||||||
|
::EndTabs
|
||||||
|
|
||||||
|
The `ENVIRONMENT` variable is available in every job defined in the downstream pipeline.
|
||||||
|
|
||||||
|
The `VERSION` global variable is also available in the downstream pipeline, because
|
||||||
|
all jobs in a pipeline, including trigger jobs, inherit [global `variables`](../yaml/index.md#variables).
|
||||||
|
|
||||||
#### Prevent global variables from being passed
|
#### Prevent global variables from being passed
|
||||||
|
|
||||||
You can stop global variables from reaching the downstream pipeline by using the [`inherit:variables` keyword](../yaml/index.md#inheritvariables).
|
You can stop global CI/CD variables from reaching the downstream pipeline with
|
||||||
For example, in a [multi-project pipeline](#multi-project-pipelines):
|
[`inherit:variables:false`](../yaml/index.md#inheritvariables).
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
::Tabs
|
||||||
|
|
||||||
|
:::TabTitle Parent-child pipeline
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
variables:
|
variables:
|
||||||
MY_GLOBAL_VAR: value
|
GLOBAL_VAR: value
|
||||||
|
|
||||||
trigger-downstream:
|
trigger-job:
|
||||||
inherit:
|
inherit:
|
||||||
variables: false
|
variables: false
|
||||||
variables:
|
variables:
|
||||||
MY_LOCAL_VAR: value
|
JOB_VAR: value
|
||||||
trigger: my/project
|
trigger:
|
||||||
|
include:
|
||||||
|
- local: path/to/child-pipeline.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
In this example, the `MY_GLOBAL_VAR` variable is not available in the triggered pipeline.
|
:::TabTitle Multi-project pipeline
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
variables:
|
||||||
|
GLOBAL_VAR: value
|
||||||
|
|
||||||
|
trigger-job:
|
||||||
|
inherit:
|
||||||
|
variables: false
|
||||||
|
variables:
|
||||||
|
JOB_VAR: value
|
||||||
|
trigger: my-group/my-project
|
||||||
|
```
|
||||||
|
|
||||||
|
::EndTabs
|
||||||
|
|
||||||
|
The `GLOBAL_VAR` variable is not available in the triggered pipeline, but `JOB_VAR`
|
||||||
|
is available.
|
||||||
|
|
||||||
### Pass a predefined variable
|
### Pass a predefined variable
|
||||||
|
|
||||||
You might want to pass some information about the upstream pipeline using predefined variables.
|
To pass information about the upstream pipeline using [predefined CI/CD variables](../variables/predefined_variables.md).
|
||||||
To do that, you can use interpolation to pass any variable. For example,
|
use interpolation. Save the predefined variable as a new job variable in the trigger
|
||||||
in a [multi-project pipeline](#multi-project-pipelines):
|
job, which is passed to the downstream pipeline. For example:
|
||||||
|
|
||||||
|
::Tabs
|
||||||
|
|
||||||
|
:::TabTitle Parent-child pipeline
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
downstream-job:
|
trigger-job:
|
||||||
variables:
|
variables:
|
||||||
UPSTREAM_BRANCH: $CI_COMMIT_REF_NAME
|
PARENT_BRANCH: $CI_COMMIT_REF_NAME
|
||||||
trigger: my/project
|
trigger:
|
||||||
|
include:
|
||||||
|
- local: path/to/child-pipeline.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
In this scenario, the `UPSTREAM_BRANCH` variable with the value of the upstream pipeline's
|
:::TabTitle Multi-project pipeline
|
||||||
`$CI_COMMIT_REF_NAME` is passed to `downstream-job`. It is available in the
|
|
||||||
context of all downstream builds.
|
```yaml
|
||||||
|
trigger-job:
|
||||||
|
variables:
|
||||||
|
UPSTREAM_BRANCH: $CI_COMMIT_REF_NAME
|
||||||
|
trigger: my-group/my-project
|
||||||
|
```
|
||||||
|
|
||||||
|
::EndTabs
|
||||||
|
|
||||||
|
The `UPSTREAM_BRANCH` variable, which contains the value of the upstream pipeline's `$CI_COMMIT_REF_NAME`
|
||||||
|
predefined CI/CD variable, is available in the downstream pipeline.
|
||||||
|
|
||||||
You cannot use this method to forward [job-level persisted variables](../variables/where_variables_can_be_used.md#persisted-variables)
|
You cannot use this method to forward [job-level persisted variables](../variables/where_variables_can_be_used.md#persisted-variables)
|
||||||
to a downstream pipeline, as they are not available in trigger jobs.
|
to a downstream pipeline, as they are not available in trigger jobs.
|
||||||
|
@ -626,3 +646,10 @@ With multi-project pipelines, the trigger job fails and does not create the down
|
||||||
- The downstream pipeline targets a protected branch and the user does not have permission
|
- The downstream pipeline targets a protected branch and the user does not have permission
|
||||||
to run pipelines against the protected branch. See [pipeline security for protected branches](index.md#pipeline-security-on-protected-branches)
|
to run pipelines against the protected branch. See [pipeline security for protected branches](index.md#pipeline-security-on-protected-branches)
|
||||||
for more information.
|
for more information.
|
||||||
|
|
||||||
|
### `Ref is ambiguous`
|
||||||
|
|
||||||
|
You cannot trigger a multi-project pipeline with a tag when a branch exists with the same
|
||||||
|
name. The downstream pipeline fails to create with the error: `downstream pipeline can not be created, Ref is ambiguous`.
|
||||||
|
|
||||||
|
Only trigger multi-project pipelines with tag names that do not match branch names.
|
||||||
|
|
|
@ -23,6 +23,11 @@ own advantages. These methods can be mixed and matched if needed:
|
||||||
- [Multi-project pipelines](downstream_pipelines.md#multi-project-pipelines): Good for larger products that require cross-project interdependencies,
|
- [Multi-project pipelines](downstream_pipelines.md#multi-project-pipelines): Good for larger products that require cross-project interdependencies,
|
||||||
like those with a [microservices architecture](https://about.gitlab.com/blog/2016/08/16/trends-in-version-control-land-microservices/).
|
like those with a [microservices architecture](https://about.gitlab.com/blog/2016/08/16/trends-in-version-control-land-microservices/).
|
||||||
|
|
||||||
|
For example, you might deploy your web application from three different GitLab projects.
|
||||||
|
With multi-project pipelines you can trigger a pipeline in each project, where each
|
||||||
|
has its own build, test, and deploy process. You can visualize the connected pipelines
|
||||||
|
in one place, including all cross-project interdependencies.
|
||||||
|
|
||||||
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
|
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
|
||||||
For an overview, see the [Multi-project pipelines demo](https://www.youtube.com/watch?v=g_PIwBM1J84).
|
For an overview, see the [Multi-project pipelines demo](https://www.youtube.com/watch?v=g_PIwBM1J84).
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ There are two places defined variables can be used. On the:
|
||||||
| [`services:name`](../yaml/index.md#services) | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism). |
|
| [`services:name`](../yaml/index.md#services) | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism). |
|
||||||
| [`services`](../yaml/index.md#services) | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism). |
|
| [`services`](../yaml/index.md#services) | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism). |
|
||||||
| [`tags`](../yaml/index.md#tags) | yes | GitLab | The variable expansion is made by the [internal variable expansion mechanism](#gitlab-internal-variable-expansion-mechanism) in GitLab. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/35742) in GitLab 14.1. |
|
| [`tags`](../yaml/index.md#tags) | yes | GitLab | The variable expansion is made by the [internal variable expansion mechanism](#gitlab-internal-variable-expansion-mechanism) in GitLab. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/35742) in GitLab 14.1. |
|
||||||
| [`trigger` and `trigger:project`](../yaml/index.md#trigger) | yes | GitLab | The variable expansion is made by the [internal variable expansion mechanism](#gitlab-internal-variable-expansion-mechanism) in GitLab. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/367660) in GitLab 15.3. |
|
| [`trigger` and `trigger:project`](../yaml/index.md#trigger) | yes | GitLab | The variable expansion is made by the [internal variable expansion mechanism](#gitlab-internal-variable-expansion-mechanism) in GitLab. Variable expansion for `trigger:project` [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/367660) in GitLab 15.3. |
|
||||||
| [`variables`](../yaml/index.md#variables) | yes | GitLab/Runner | The variable expansion is first made by the [internal variable expansion mechanism](#gitlab-internal-variable-expansion-mechanism) in GitLab, and then any unrecognized or unavailable variables are expanded by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism). |
|
| [`variables`](../yaml/index.md#variables) | yes | GitLab/Runner | The variable expansion is first made by the [internal variable expansion mechanism](#gitlab-internal-variable-expansion-mechanism) in GitLab, and then any unrecognized or unavailable variables are expanded by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism). |
|
||||||
|
|
||||||
### `config.toml` file
|
### `config.toml` file
|
||||||
|
|
|
@ -54,10 +54,12 @@ This process adds the `gitlab` user you created to a new group named `gitlab-dev
|
||||||
1. To add the `gitlab` user to the `gitlab-developers` group, select **Edit members**.
|
1. To add the `gitlab` user to the `gitlab-developers` group, select **Edit members**.
|
||||||
The `gitlab-developers` group should be listed in the leftmost box as a
|
The `gitlab-developers` group should be listed in the leftmost box as a
|
||||||
selected group.
|
selected group.
|
||||||
|
<!-- vale gitlab.BadPlurals = NO -->
|
||||||
1. In the **Add members to selected group(s)** section, enter `gitlab`.
|
1. In the **Add members to selected group(s)** section, enter `gitlab`.
|
||||||
1. Select **Add selected users**.
|
1. Select **Add selected users**.
|
||||||
The `gitlab` user appears in the **Group member(s)**
|
The `gitlab` user appears in the **Group member(s)**
|
||||||
section.
|
section.
|
||||||
|
<!-- vale gitlab.BadPlurals = YES -->
|
||||||
|
|
||||||
![Jira added user to group](img/jira_added_user_to_group.png)
|
![Jira added user to group](img/jira_added_user_to_group.png)
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ stage: none
|
||||||
group: unassigned
|
group: unassigned
|
||||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||||
---
|
---
|
||||||
|
<!-- vale off -->
|
||||||
|
|
||||||
# Corporate contributor license agreement
|
# Corporate contributor license agreement
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ stage: none
|
||||||
group: unassigned
|
group: unassigned
|
||||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||||
---
|
---
|
||||||
|
<!-- vale off -->
|
||||||
|
|
||||||
# Developer Certificate of Origin Version 1.1
|
# Developer Certificate of Origin Version 1.1
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ stage: none
|
||||||
group: unassigned
|
group: unassigned
|
||||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||||
---
|
---
|
||||||
|
<!-- vale off -->
|
||||||
|
|
||||||
# Individual contributor license agreement
|
# Individual contributor license agreement
|
||||||
|
|
||||||
|
|
|
@ -96,5 +96,4 @@ an environment.
|
||||||
|
|
||||||
- Deploy to different [environments](../ci/environments/index.md).
|
- Deploy to different [environments](../ci/environments/index.md).
|
||||||
- Connect your project to a [Kubernetes cluster](../user/infrastructure/clusters/index.md).
|
- Connect your project to a [Kubernetes cluster](../user/infrastructure/clusters/index.md).
|
||||||
- See how your application is used and analyze events with [Product Analytics](product_analytics.md).
|
|
||||||
- Create, toggle, and remove [Feature Flags](feature_flags.md).
|
- Create, toggle, and remove [Feature Flags](feature_flags.md).
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
---
|
|
||||||
stage: Analytics
|
|
||||||
group: Product Analytics
|
|
||||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
|
||||||
---
|
|
||||||
|
|
||||||
# Product Analytics **(FREE)**
|
|
||||||
|
|
||||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/225167) in GitLab 13.3 [with a flag](../administration/feature_flags.md) named `product_analytics`. Disabled by default.
|
|
||||||
|
|
||||||
FLAG:
|
|
||||||
On self-managed GitLab, by default this feature is not available. To make it available per project or for your entire instance, ask an administrator to [enable the feature flag](../administration/feature_flags.md) named `product_analytics`. On GitLab.com, this feature is not available. The feature is not ready for production use.
|
|
||||||
|
|
||||||
GitLab enables you to go from planning an application to getting feedback. You can use
|
|
||||||
Product Analytics to receive and analyze events sent from your application. This analysis
|
|
||||||
provides observability information and feedback on how people use your product.
|
|
||||||
|
|
||||||
Events are collected by a [Rails collector](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36443) and
|
|
||||||
then processed with [Snowplow](https://github.com/snowplow/snowplow). Events are stored in a GitLab database.
|
|
||||||
|
|
||||||
## View Product Analytics
|
|
||||||
|
|
||||||
You can view the event data collected about your applications.
|
|
||||||
|
|
||||||
Prerequisite:
|
|
||||||
|
|
||||||
- You must have at least the Reporter role.
|
|
||||||
|
|
||||||
To access Product Analytics:
|
|
||||||
|
|
||||||
1. On the top bar, select **Main menu > Projects** and find your project.
|
|
||||||
1. On the left sidebar, select **Monitor > Product Analytics**.
|
|
||||||
|
|
||||||
The Product Analytics interface contains:
|
|
||||||
|
|
||||||
- An Events tab that shows the recent events and a total count.
|
|
||||||
- A Graph tab that shows graphs based on events of the last 30 days.
|
|
||||||
- A Test tab that sends a sample event payload.
|
|
||||||
- A Setup page containing the code to implement in your application.
|
|
||||||
|
|
||||||
## Rate limits
|
|
||||||
|
|
||||||
While Product Analytics is under development, it's rate-limited to
|
|
||||||
**100 events per minute** per project. This limit prevents the events table in the
|
|
||||||
database from growing too quickly.
|
|
|
@ -310,9 +310,7 @@ for the group's projects to meet your group's needs.
|
||||||
[Feature flag `invite_members_group_modal`](https://gitlab.com/gitlab-org/gitlab/-/issues/352526) removed.
|
[Feature flag `invite_members_group_modal`](https://gitlab.com/gitlab-org/gitlab/-/issues/352526) removed.
|
||||||
|
|
||||||
Similar to how you [share a project with a group](../project/members/share_project_with_groups.md),
|
Similar to how you [share a project with a group](../project/members/share_project_with_groups.md),
|
||||||
you can share a group with another group. To invite a group, you must be a member of it. Members get direct access
|
you can share a group with another group. To invite a group, you must be a member of it.
|
||||||
to the shared group. This includes members who inherited group membership from a parent group.
|
|
||||||
|
|
||||||
To share a given group, for example, `Frontend` with another group, for example,
|
To share a given group, for example, `Frontend` with another group, for example,
|
||||||
`Engineering`:
|
`Engineering`:
|
||||||
|
|
||||||
|
@ -327,7 +325,7 @@ After sharing the `Frontend` group with the `Engineering` group:
|
||||||
|
|
||||||
- The **Groups** tab lists the `Engineering` group.
|
- The **Groups** tab lists the `Engineering` group.
|
||||||
- The **Groups** tab lists a group regardless of whether it is a public or private group.
|
- The **Groups** tab lists a group regardless of whether it is a public or private group.
|
||||||
- All members of the `Engineering` group have access to the `Frontend` group. The same access levels of the members apply up to the maximum access level selected when sharing the group.
|
- All direct members of the `Engineering` group have access to the `Frontend` group. Direct members of `Engineering` that gain access to the `Frontend` group keep their same access level as in `Engineering`, but up to the maximum access level selected when sharing the group. Inherited members of the `Engineering` group do not gain access to the `Frontend` group.
|
||||||
|
|
||||||
## Transfer a group
|
## Transfer a group
|
||||||
|
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module ProductAnalytics
|
|
||||||
class CollectorApp
|
|
||||||
def call(env)
|
|
||||||
request = Rack::Request.new(env)
|
|
||||||
params = request.params
|
|
||||||
|
|
||||||
return not_found unless EventParams.has_required_params?(params)
|
|
||||||
|
|
||||||
# Product analytics feature is behind a flag and is disabled by default.
|
|
||||||
# We expect limited amount of projects with this feature enabled in first release.
|
|
||||||
# Since collector has no authentication we temporary prevent recording of events
|
|
||||||
# for project without the feature enabled. During increase of feature adoption, this
|
|
||||||
# check will be removed for better performance.
|
|
||||||
project = Project.find(params['aid'].to_i)
|
|
||||||
return not_found unless Feature.enabled?(:product_analytics, project)
|
|
||||||
|
|
||||||
# Snowplow tracker has own format of events.
|
|
||||||
# We need to convert them to match the schema of our database.
|
|
||||||
event_params = EventParams.parse_event_params(params)
|
|
||||||
|
|
||||||
if ProductAnalyticsEvent.create(event_params)
|
|
||||||
ok
|
|
||||||
else
|
|
||||||
not_found
|
|
||||||
end
|
|
||||||
rescue ActiveRecord::InvalidForeignKey, ActiveRecord::RecordNotFound
|
|
||||||
not_found
|
|
||||||
end
|
|
||||||
|
|
||||||
def ok
|
|
||||||
[200, {}, []]
|
|
||||||
end
|
|
||||||
|
|
||||||
def not_found
|
|
||||||
[404, {}, []]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -12,7 +12,6 @@ module Sidebars
|
||||||
add_item(error_tracking_menu_item)
|
add_item(error_tracking_menu_item)
|
||||||
add_item(alert_management_menu_item)
|
add_item(alert_management_menu_item)
|
||||||
add_item(incidents_menu_item)
|
add_item(incidents_menu_item)
|
||||||
add_item(product_analytics_menu_item)
|
|
||||||
|
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
@ -101,20 +100,6 @@ module Sidebars
|
||||||
item_id: :incidents
|
item_id: :incidents
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def product_analytics_menu_item
|
|
||||||
if Feature.disabled?(:product_analytics, context.project) ||
|
|
||||||
!can?(context.current_user, :read_product_analytics, context.project)
|
|
||||||
return ::Sidebars::NilMenuItem.new(item_id: :product_analytics)
|
|
||||||
end
|
|
||||||
|
|
||||||
::Sidebars::MenuItem.new(
|
|
||||||
title: _('Product Analytics'),
|
|
||||||
link: project_product_analytics_path(context.project),
|
|
||||||
active_routes: { controller: :product_analytics },
|
|
||||||
item_id: :product_analytics
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1729,9 +1729,6 @@ msgstr ""
|
||||||
msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features"
|
msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "A platform value can be web, mob or app."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "A project boilerplate for Salesforce App development with Salesforce Developer tools"
|
msgid "A project boilerplate for Salesforce App development with Salesforce Developer tools"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -10863,9 +10860,6 @@ msgstr ""
|
||||||
msgid "Copy source branch name"
|
msgid "Copy source branch name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Copy the code below to implement tracking in your application:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Copy this registration token."
|
msgid "Copy this registration token."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -18791,9 +18785,6 @@ msgstr ""
|
||||||
msgid "GraphViewType|Stage"
|
msgid "GraphViewType|Stage"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Graphs"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Gravatar"
|
msgid "Gravatar"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -23766,9 +23757,6 @@ msgstr ""
|
||||||
msgid "Last event"
|
msgid "Last event"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Last item before this page loaded in your browser:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Last modified"
|
msgid "Last modified"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -27841,9 +27829,6 @@ msgstr ""
|
||||||
msgid "Number of events"
|
msgid "Number of events"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Number of events for this project: %{total_count}."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Number of files touched"
|
msgid "Number of files touched"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -27868,9 +27853,6 @@ msgstr ""
|
||||||
msgid "October"
|
msgid "October"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "OfSearchInADropdown|Filter"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Off"
|
msgid "Off"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -30881,9 +30863,6 @@ msgstr ""
|
||||||
msgid "Proceed"
|
msgid "Proceed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Product Analytics"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "ProductAnalytics|Audience"
|
msgid "ProductAnalytics|Audience"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -36891,9 +36870,6 @@ msgstr ""
|
||||||
msgid "Select a template type"
|
msgid "Select a template type"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Select a timezone"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Select all"
|
msgid "Select all"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -37439,9 +37415,6 @@ msgstr ""
|
||||||
msgid "Settings|Unable to load the merge request options settings. Try reloading the page."
|
msgid "Settings|Unable to load the merge request options settings. Try reloading the page."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Setup"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Severity"
|
msgid "Severity"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -37699,9 +37672,6 @@ msgstr ""
|
||||||
msgid "Showing data for workflow items completed in this date range. Date range limited to %{maxDateRange} days."
|
msgid "Showing data for workflow items completed in this date range. Date range limited to %{maxDateRange} days."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Showing graphs based on events of the last %{timerange} days."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Showing last %{size} of log -"
|
msgid "Showing last %{size} of log -"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -40811,9 +40781,6 @@ msgstr ""
|
||||||
msgid "Theme"
|
msgid "Theme"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "There are currently no events."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "There are currently no mirrored repositories."
|
msgid "There are currently no mirrored repositories."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -41654,9 +41621,6 @@ msgstr ""
|
||||||
msgid "This page is unavailable because you are not allowed to read information across multiple projects."
|
msgid "This page is unavailable because you are not allowed to read information across multiple projects."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "This page sends a payload. Go back to the events page to see a newly created event."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
|
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module QA
|
|
||||||
module Page
|
|
||||||
module Component
|
|
||||||
module UsersSelect
|
|
||||||
def select_user(element, username)
|
|
||||||
find("#{element_selector_css(element)} input").set(username)
|
|
||||||
find('.ajax-users-dropdown .user-username', text: "@#{username}").click
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -5,7 +5,6 @@ module QA
|
||||||
module Group
|
module Group
|
||||||
class Members < Page::Base
|
class Members < Page::Base
|
||||||
include Page::Component::InviteMembersModal
|
include Page::Component::InviteMembersModal
|
||||||
include Page::Component::UsersSelect
|
|
||||||
include Page::Component::MembersFilter
|
include Page::Component::MembersFilter
|
||||||
|
|
||||||
view 'app/assets/javascripts/members/components/modals/remove_member_modal.vue' do
|
view 'app/assets/javascripts/members/components/modals/remove_member_modal.vue' do
|
||||||
|
|
|
@ -74,3 +74,5 @@ module QA
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
QA::Page::Project::SubMenus::Issues.prepend_mod_with('Page::Project::SubMenus::Issues', namespace: QA)
|
||||||
|
|
|
@ -90,6 +90,10 @@ module QA
|
||||||
# code_owner_approval_required: true
|
# code_owner_approval_required: true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# GitHub branch protection rule "Require signed commits" is mapped to the
|
||||||
|
# "Reject unsigned commits" push rule
|
||||||
|
expect(imported_project.push_rules[:reject_unsigned_commits]).to be_truthy
|
||||||
end
|
end
|
||||||
|
|
||||||
def verify_commits_import
|
def verify_commits_import
|
||||||
|
|
|
@ -1,95 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
RSpec.describe Projects::ProductAnalyticsController do
|
|
||||||
let_it_be(:project) { create(:project) }
|
|
||||||
let_it_be(:user) { create(:user) }
|
|
||||||
|
|
||||||
before(:all) do
|
|
||||||
project.add_maintainer(user)
|
|
||||||
end
|
|
||||||
|
|
||||||
before do
|
|
||||||
sign_in(user)
|
|
||||||
stub_feature_flags(product_analytics: true)
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'GET #index' do
|
|
||||||
it 'renders index with 200 status code' do
|
|
||||||
get :index, params: project_params
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
|
||||||
expect(response).to render_template(:index)
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with an anonymous user' do
|
|
||||||
before do
|
|
||||||
sign_out(user)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'redirects to sign-in page' do
|
|
||||||
get :index, params: project_params
|
|
||||||
|
|
||||||
expect(response).to redirect_to(new_user_session_path)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'feature flag disabled' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(product_analytics: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns not found' do
|
|
||||||
get :index, params: project_params
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:not_found)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'GET #test' do
|
|
||||||
it 'renders test with 200 status code' do
|
|
||||||
get :test, params: project_params
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
|
||||||
expect(response).to render_template(:test)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'GET #setup' do
|
|
||||||
it 'renders setup with 200 status code' do
|
|
||||||
get :setup, params: project_params
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
|
||||||
expect(response).to render_template(:setup)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'GET #graphs' do
|
|
||||||
it 'renders graphs with 200 status code' do
|
|
||||||
get :graphs, params: project_params
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
|
||||||
expect(response).to render_template(:graphs)
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'feature flag disabled' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(product_analytics: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns not found' do
|
|
||||||
get :graphs, params: project_params
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:not_found)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def project_params(opts = {})
|
|
||||||
opts.reverse_merge(namespace_id: project.namespace, project_id: project)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -15,7 +15,7 @@ RSpec.describe 'Database schema' do
|
||||||
}.with_indifferent_access.freeze
|
}.with_indifferent_access.freeze
|
||||||
|
|
||||||
# List of columns historically missing a FK, don't add more columns
|
# List of columns historically missing a FK, don't add more columns
|
||||||
# See: https://docs.gitlab.com/ee/development/foreign_keys.html#naming-foreign-keys
|
# See: https://docs.gitlab.com/ee/development/database/foreign_keys.html#naming-foreign-keys
|
||||||
IGNORED_FK_COLUMNS = {
|
IGNORED_FK_COLUMNS = {
|
||||||
abuse_reports: %w[reporter_id user_id],
|
abuse_reports: %w[reporter_id user_id],
|
||||||
application_settings: %w[performance_bar_allowed_group_id slack_app_id snowplow_app_id eks_account_id eks_access_key_id],
|
application_settings: %w[performance_bar_allowed_group_id slack_app_id snowplow_app_id eks_account_id eks_access_key_id],
|
||||||
|
|
|
@ -64,7 +64,6 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do
|
||||||
expect(page).not_to have_link('Metrics', href: project_metrics_dashboard_path(project))
|
expect(page).not_to have_link('Metrics', href: project_metrics_dashboard_path(project))
|
||||||
expect(page).not_to have_link('Alerts', href: project_alert_management_index_path(project))
|
expect(page).not_to have_link('Alerts', href: project_alert_management_index_path(project))
|
||||||
expect(page).not_to have_link('Error Tracking', href: project_error_tracking_index_path(project))
|
expect(page).not_to have_link('Error Tracking', href: project_error_tracking_index_path(project))
|
||||||
expect(page).not_to have_link('Product Analytics', href: project_product_analytics_path(project))
|
|
||||||
expect(page).not_to have_link('Kubernetes', href: project_clusters_path(project))
|
expect(page).not_to have_link('Kubernetes', href: project_clusters_path(project))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -119,7 +118,6 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do
|
||||||
expect(page).not_to have_link('Metrics', href: project_metrics_dashboard_path(project))
|
expect(page).not_to have_link('Metrics', href: project_metrics_dashboard_path(project))
|
||||||
expect(page).not_to have_link('Alerts', href: project_alert_management_index_path(project))
|
expect(page).not_to have_link('Alerts', href: project_alert_management_index_path(project))
|
||||||
expect(page).not_to have_link('Error Tracking', href: project_error_tracking_index_path(project))
|
expect(page).not_to have_link('Error Tracking', href: project_error_tracking_index_path(project))
|
||||||
expect(page).not_to have_link('Product Analytics', href: project_product_analytics_path(project))
|
|
||||||
expect(page).not_to have_link('Kubernetes', href: project_clusters_path(project))
|
expect(page).not_to have_link('Kubernetes', href: project_clusters_path(project))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -135,7 +133,6 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do
|
||||||
expect(page).to have_link('Incidents', href: project_incidents_path(project))
|
expect(page).to have_link('Incidents', href: project_incidents_path(project))
|
||||||
expect(page).to have_link('Environments', href: project_environments_path(project))
|
expect(page).to have_link('Environments', href: project_environments_path(project))
|
||||||
expect(page).to have_link('Error Tracking', href: project_error_tracking_index_path(project))
|
expect(page).to have_link('Error Tracking', href: project_error_tracking_index_path(project))
|
||||||
expect(page).to have_link('Product Analytics', href: project_product_analytics_path(project))
|
|
||||||
|
|
||||||
expect(page).not_to have_link('Alerts', href: project_alert_management_index_path(project))
|
expect(page).not_to have_link('Alerts', href: project_alert_management_index_path(project))
|
||||||
expect(page).not_to have_link('Kubernetes', href: project_clusters_path(project))
|
expect(page).not_to have_link('Kubernetes', href: project_clusters_path(project))
|
||||||
|
@ -154,7 +151,6 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do
|
||||||
expect(page).to have_link('Incidents', href: project_incidents_path(project))
|
expect(page).to have_link('Incidents', href: project_incidents_path(project))
|
||||||
expect(page).to have_link('Environments', href: project_environments_path(project))
|
expect(page).to have_link('Environments', href: project_environments_path(project))
|
||||||
expect(page).to have_link('Error Tracking', href: project_error_tracking_index_path(project))
|
expect(page).to have_link('Error Tracking', href: project_error_tracking_index_path(project))
|
||||||
expect(page).to have_link('Product Analytics', href: project_product_analytics_path(project))
|
|
||||||
expect(page).to have_link('Kubernetes', href: project_clusters_path(project))
|
expect(page).to have_link('Kubernetes', href: project_clusters_path(project))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -171,7 +167,6 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do
|
||||||
expect(page).to have_link('Incidents', href: project_incidents_path(project))
|
expect(page).to have_link('Incidents', href: project_incidents_path(project))
|
||||||
expect(page).to have_link('Environments', href: project_environments_path(project))
|
expect(page).to have_link('Environments', href: project_environments_path(project))
|
||||||
expect(page).to have_link('Error Tracking', href: project_error_tracking_index_path(project))
|
expect(page).to have_link('Error Tracking', href: project_error_tracking_index_path(project))
|
||||||
expect(page).to have_link('Product Analytics', href: project_product_analytics_path(project))
|
|
||||||
expect(page).to have_link('Kubernetes', href: project_clusters_path(project))
|
expect(page).to have_link('Kubernetes', href: project_clusters_path(project))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ RSpec.describe 'Pipeline Schedules', :js do
|
||||||
description = find_field('schedule_description').value
|
description = find_field('schedule_description').value
|
||||||
expect(description).to eq('pipeline schedule')
|
expect(description).to eq('pipeline schedule')
|
||||||
expect(page).to have_button('master')
|
expect(page).to have_button('master')
|
||||||
expect(page).to have_button('UTC')
|
expect(page).to have_button('Select timezone')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'edits the scheduled pipeline' do
|
it 'edits the scheduled pipeline' do
|
||||||
|
@ -164,7 +164,7 @@ RSpec.describe 'Pipeline Schedules', :js do
|
||||||
|
|
||||||
it 'sets defaults for timezone and target branch' do
|
it 'sets defaults for timezone and target branch' do
|
||||||
expect(page).to have_button('master')
|
expect(page).to have_button('master')
|
||||||
expect(page).to have_button('UTC')
|
expect(page).to have_button('Select timezone')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates a new scheduled pipeline' do
|
it 'creates a new scheduled pipeline' do
|
||||||
|
@ -314,8 +314,8 @@ RSpec.describe 'Pipeline Schedules', :js do
|
||||||
end
|
end
|
||||||
|
|
||||||
def select_timezone
|
def select_timezone
|
||||||
find('.js-timezone-dropdown').click
|
find('[data-testid="schedule-timezone"] .dropdown-toggle').click
|
||||||
click_link 'American Samoa'
|
find("button", text: "Arizona").click
|
||||||
end
|
end
|
||||||
|
|
||||||
def select_target_branch
|
def select_target_branch
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
RSpec.describe 'Product Analytics > Events' do
|
|
||||||
let_it_be(:project) { create(:project_empty_repo) }
|
|
||||||
let_it_be(:user) { create(:user) }
|
|
||||||
|
|
||||||
let(:event) { create(:product_analytics_event, project: project) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
project.add_maintainer(user)
|
|
||||||
sign_in(user)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'shows no events message' do
|
|
||||||
visit(project_product_analytics_path(project))
|
|
||||||
|
|
||||||
expect(page).to have_content('There are currently no events')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'shows events' do
|
|
||||||
event
|
|
||||||
|
|
||||||
visit(project_product_analytics_path(project))
|
|
||||||
|
|
||||||
expect(page).to have_content('dvce_created_tstamp')
|
|
||||||
expect(page).to have_content(event.event_id)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,25 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
RSpec.describe 'Product Analytics > Graphs' do
|
|
||||||
let_it_be(:project) { create(:project) }
|
|
||||||
let_it_be(:user) { create(:user) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
project.add_maintainer(user)
|
|
||||||
sign_in(user)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'shows graphs', :js do
|
|
||||||
create(:product_analytics_event, project: project)
|
|
||||||
|
|
||||||
visit(graphs_project_product_analytics_path(project))
|
|
||||||
|
|
||||||
expect(page).to have_content('Showing graphs based on events')
|
|
||||||
expect(page).to have_content('platform')
|
|
||||||
expect(page).to have_content('os_timezone')
|
|
||||||
expect(page).to have_content('br_lang')
|
|
||||||
expect(page).to have_content('doc_charset')
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,19 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
RSpec.describe 'Product Analytics > Setup' do
|
|
||||||
let_it_be(:project) { create(:project_empty_repo) }
|
|
||||||
let_it_be(:user) { create(:user) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
project.add_maintainer(user)
|
|
||||||
sign_in(user)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'shows the setup instructions' do
|
|
||||||
visit(setup_project_product_analytics_path(project))
|
|
||||||
|
|
||||||
expect(page).to have_content('Copy the code below to implement tracking in your application')
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,27 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
RSpec.describe 'Product Analytics > Test' do
|
|
||||||
let_it_be(:project) { create(:project_empty_repo) }
|
|
||||||
let_it_be(:user) { create(:user) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
project.add_maintainer(user)
|
|
||||||
sign_in(user)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'says it sends a payload' do
|
|
||||||
visit(test_project_product_analytics_path(project))
|
|
||||||
|
|
||||||
expect(page).to have_content('This page sends a payload.')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'shows the last event if there is one' do
|
|
||||||
event = create(:product_analytics_event, project: project)
|
|
||||||
|
|
||||||
visit(test_project_product_analytics_path(project))
|
|
||||||
|
|
||||||
expect(page).to have_content(event.event_id)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,70 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
RSpec.describe Gitlab::BackgroundMigration::ResetDuplicateCiRunnersTokenEncryptedValues,
|
|
||||||
:migration,
|
|
||||||
schema: 20220922143634 do
|
|
||||||
it { expect(described_class).to be < Gitlab::BackgroundMigration::BatchedMigrationJob }
|
|
||||||
|
|
||||||
describe '#perform' do
|
|
||||||
let(:ci_runners) { table(:ci_runners, database: :ci) }
|
|
||||||
|
|
||||||
let(:test_worker) do
|
|
||||||
described_class.new(
|
|
||||||
start_id: 1,
|
|
||||||
end_id: 4,
|
|
||||||
batch_table: :ci_runners,
|
|
||||||
batch_column: :id,
|
|
||||||
sub_batch_size: 2,
|
|
||||||
pause_ms: 0,
|
|
||||||
connection: Ci::ApplicationRecord.connection
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
subject(:perform) { test_worker.perform }
|
|
||||||
|
|
||||||
before do
|
|
||||||
ci_runners.create!(id: 1, runner_type: 1, token_encrypted: 'duplicate')
|
|
||||||
ci_runners.create!(id: 2, runner_type: 1, token_encrypted: 'a-token')
|
|
||||||
ci_runners.create!(id: 3, runner_type: 1, token_encrypted: 'duplicate-2')
|
|
||||||
ci_runners.create!(id: 4, runner_type: 1, token_encrypted: nil)
|
|
||||||
ci_runners.create!(id: 5, runner_type: 1, token_encrypted: 'duplicate-2')
|
|
||||||
ci_runners.create!(id: 6, runner_type: 1, token_encrypted: 'duplicate')
|
|
||||||
ci_runners.create!(id: 7, runner_type: 1, token_encrypted: 'another-token')
|
|
||||||
ci_runners.create!(id: 8, runner_type: 1, token_encrypted: 'another-token')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'nullifies duplicate encrypted tokens', :aggregate_failures do
|
|
||||||
expect { perform }.to change { ci_runners.all.order(:id).pluck(:id, :token_encrypted).to_h }
|
|
||||||
.from(
|
|
||||||
{
|
|
||||||
1 => 'duplicate',
|
|
||||||
2 => 'a-token',
|
|
||||||
3 => 'duplicate-2',
|
|
||||||
4 => nil,
|
|
||||||
5 => 'duplicate-2',
|
|
||||||
6 => 'duplicate',
|
|
||||||
7 => 'another-token',
|
|
||||||
8 => 'another-token'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.to(
|
|
||||||
{
|
|
||||||
1 => nil,
|
|
||||||
2 => 'a-token',
|
|
||||||
3 => nil,
|
|
||||||
4 => nil,
|
|
||||||
5 => nil,
|
|
||||||
6 => nil,
|
|
||||||
7 => 'another-token',
|
|
||||||
8 => 'another-token'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
expect(ci_runners.count).to eq(8)
|
|
||||||
expect(ci_runners.pluck(:token_encrypted).uniq).to match_array [
|
|
||||||
nil, 'a-token', 'another-token'
|
|
||||||
]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,70 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
RSpec.describe Gitlab::BackgroundMigration::ResetDuplicateCiRunnersTokenValues,
|
|
||||||
:migration,
|
|
||||||
schema: 20220922143143 do
|
|
||||||
it { expect(described_class).to be < Gitlab::BackgroundMigration::BatchedMigrationJob }
|
|
||||||
|
|
||||||
describe '#perform' do
|
|
||||||
let(:ci_runners) { table(:ci_runners, database: :ci) }
|
|
||||||
|
|
||||||
let(:test_worker) do
|
|
||||||
described_class.new(
|
|
||||||
start_id: 1,
|
|
||||||
end_id: 4,
|
|
||||||
batch_table: :ci_runners,
|
|
||||||
batch_column: :id,
|
|
||||||
sub_batch_size: 2,
|
|
||||||
pause_ms: 0,
|
|
||||||
connection: Ci::ApplicationRecord.connection
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
subject(:perform) { test_worker.perform }
|
|
||||||
|
|
||||||
before do
|
|
||||||
ci_runners.create!(id: 1, runner_type: 1, token: 'duplicate')
|
|
||||||
ci_runners.create!(id: 2, runner_type: 1, token: 'a-token')
|
|
||||||
ci_runners.create!(id: 3, runner_type: 1, token: 'duplicate-2')
|
|
||||||
ci_runners.create!(id: 4, runner_type: 1, token: nil)
|
|
||||||
ci_runners.create!(id: 5, runner_type: 1, token: 'duplicate-2')
|
|
||||||
ci_runners.create!(id: 6, runner_type: 1, token: 'duplicate')
|
|
||||||
ci_runners.create!(id: 7, runner_type: 1, token: 'another-token')
|
|
||||||
ci_runners.create!(id: 8, runner_type: 1, token: 'another-token')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'nullifies duplicate tokens', :aggregate_failures do
|
|
||||||
expect { perform }.to change { ci_runners.all.order(:id).pluck(:id, :token).to_h }
|
|
||||||
.from(
|
|
||||||
{
|
|
||||||
1 => 'duplicate',
|
|
||||||
2 => 'a-token',
|
|
||||||
3 => 'duplicate-2',
|
|
||||||
4 => nil,
|
|
||||||
5 => 'duplicate-2',
|
|
||||||
6 => 'duplicate',
|
|
||||||
7 => 'another-token',
|
|
||||||
8 => 'another-token'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.to(
|
|
||||||
{
|
|
||||||
1 => nil,
|
|
||||||
2 => 'a-token',
|
|
||||||
3 => nil,
|
|
||||||
4 => nil,
|
|
||||||
5 => nil,
|
|
||||||
6 => nil,
|
|
||||||
7 => 'another-token',
|
|
||||||
8 => 'another-token'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
expect(ci_runners.count).to eq(8)
|
|
||||||
expect(ci_runners.pluck(:token).uniq).to match_array [
|
|
||||||
nil, 'a-token', 'another-token'
|
|
||||||
]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -102,19 +102,5 @@ RSpec.describe Sidebars::Projects::Menus::MonitorMenu do
|
||||||
|
|
||||||
it_behaves_like 'access rights checks'
|
it_behaves_like 'access rights checks'
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'Product Analytics' do
|
|
||||||
let(:item_id) { :product_analytics }
|
|
||||||
|
|
||||||
specify { is_expected.not_to be_nil }
|
|
||||||
|
|
||||||
describe 'when feature flag :product_analytics is disabled' do
|
|
||||||
specify do
|
|
||||||
stub_feature_flags(product_analytics: false)
|
|
||||||
|
|
||||||
is_expected.to be_nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
RSpec.describe 'ProductAnalytics::CollectorApp throttle' do
|
|
||||||
include RackAttackSpecHelpers
|
|
||||||
|
|
||||||
include_context 'rack attack cache store'
|
|
||||||
|
|
||||||
let(:project1) { create(:project) }
|
|
||||||
let(:project2) { create(:project) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
allow(ProductAnalyticsEvent).to receive(:create).and_return(true)
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'per application id' do
|
|
||||||
let(:params) do
|
|
||||||
{
|
|
||||||
aid: project1.id,
|
|
||||||
eid: SecureRandom.uuid
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'throttles the endpoint' do
|
|
||||||
# Allow requests under the rate limit.
|
|
||||||
100.times do
|
|
||||||
expect_ok { get '/-/collector/i', params: params }
|
|
||||||
end
|
|
||||||
|
|
||||||
# Ensure its not related to ip address
|
|
||||||
random_next_ip
|
|
||||||
|
|
||||||
# Reject request over the limit
|
|
||||||
expect_rejection { get '/-/collector/i', params: params }
|
|
||||||
|
|
||||||
# But allows request for different aid
|
|
||||||
expect_ok { get '/-/collector/i', params: params.merge(aid: project2.id) }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,58 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
RSpec.describe 'ProductAnalytics::CollectorApp' do
|
|
||||||
let_it_be(:project) { create(:project) }
|
|
||||||
|
|
||||||
let(:params) { {} }
|
|
||||||
let(:raw_event) { Gitlab::Json.parse(fixture_file('product_analytics/event.json')) }
|
|
||||||
|
|
||||||
subject { get '/-/collector/i', params: params }
|
|
||||||
|
|
||||||
RSpec.shared_examples 'not found' do
|
|
||||||
it 'repond with 404' do
|
|
||||||
expect { subject }.not_to change { ProductAnalyticsEvent.count }
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:not_found)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'correct event params' do
|
|
||||||
let(:params) { raw_event.merge(aid: project.id) }
|
|
||||||
|
|
||||||
it 'repond with 200' do
|
|
||||||
expect { subject }.to change { ProductAnalyticsEvent.count }.by(1)
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'feature disabled' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(product_analytics: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it_behaves_like 'not found'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'empty event params' do
|
|
||||||
it_behaves_like 'not found'
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'invalid project id in params' do
|
|
||||||
let(:params) do
|
|
||||||
{
|
|
||||||
aid: '-1',
|
|
||||||
p: 'web',
|
|
||||||
tna: 'sp',
|
|
||||||
tv: 'js-2.14.0',
|
|
||||||
eid: SecureRandom.uuid,
|
|
||||||
duid: SecureRandom.uuid,
|
|
||||||
sid: SecureRandom.uuid
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
it_behaves_like 'not found'
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -17,6 +17,6 @@ RSpec.shared_context 'runners resolver setup' do
|
||||||
end
|
end
|
||||||
|
|
||||||
let_it_be(:group_runner) { create(:ci_runner, :group, groups: [group], token: 'mnopqr', description: 'group runner', contacted_at: 2.seconds.ago) }
|
let_it_be(:group_runner) { create(:ci_runner, :group, groups: [group], token: 'mnopqr', description: 'group runner', contacted_at: 2.seconds.ago) }
|
||||||
let_it_be(:subgroup_runner) { create(:ci_runner, :group, groups: [subgroup], token: 'mnopqr', description: 'subgroup runner', contacted_at: 1.second.ago) }
|
let_it_be(:subgroup_runner) { create(:ci_runner, :group, groups: [subgroup], token: '123456', description: 'subgroup runner', contacted_at: 1.second.ago) }
|
||||||
let_it_be(:instance_runner) { create(:ci_runner, :instance, description: 'shared runner', token: 'stuvxz', contacted_at: 2.minutes.ago, tag_list: %w(instance_runner active_runner)) }
|
let_it_be(:instance_runner) { create(:ci_runner, :instance, description: 'shared runner', token: 'stuvxz', contacted_at: 2.minutes.ago, tag_list: %w(instance_runner active_runner)) }
|
||||||
end
|
end
|
||||||
|
|
|
@ -85,8 +85,7 @@ RSpec.shared_context 'project navbar structure' do
|
||||||
_('Metrics'),
|
_('Metrics'),
|
||||||
_('Error Tracking'),
|
_('Error Tracking'),
|
||||||
_('Alerts'),
|
_('Alerts'),
|
||||||
_('Incidents'),
|
_('Incidents')
|
||||||
_('Product Analytics')
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -472,24 +472,6 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'Product Analytics' do
|
|
||||||
it 'has a link to the product analytics page' do
|
|
||||||
render
|
|
||||||
|
|
||||||
expect(rendered).to have_link('Product Analytics', href: project_product_analytics_path(project))
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'when feature flag :product_analytics is disabled' do
|
|
||||||
it 'does not have a link to the feature flags page' do
|
|
||||||
stub_feature_flags(product_analytics: false)
|
|
||||||
|
|
||||||
render
|
|
||||||
|
|
||||||
expect(rendered).not_to have_link('Product Analytics')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'Infrastructure' do
|
describe 'Infrastructure' do
|
||||||
|
|
Loading…
Reference in New Issue