diff options
148 files changed, 2634 insertions, 1533 deletions
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS index 2dfd9aa71ed..ab3b6348676 100644 --- a/.gitlab/CODEOWNERS +++ b/.gitlab/CODEOWNERS @@ -4,14 +4,15 @@ * @gitlab-org/maintainers/rails-backend @gitlab-org/maintainers/frontend @gitlab-org/maintainers/database @gl-quality/qe-maintainers @gl-quality/tooling-maintainers @gitlab-org/delivery @gitlab-org/maintainers/cicd-templates @nolith @jacobvosmaer-gitlab @gitlab-org/tw-leadership -CODEOWNERS @gitlab-org/development-leaders @gitlab-org/tw-leadership -docs/CODEOWNERS @gitlab-org/development-leaders @gitlab-org/tw-leadership .gitlab/CODEOWNERS @gitlab-org/development-leaders @gitlab-org/tw-leadership ## Allows release tooling to update the Gitaly Version GITALY_SERVER_VERSION @project_278964_bot6 @gitlab-org/maintainers/rails-backend @gitlab-org/delivery ## Files that are excluded from required approval +## These rules override the * rule above, so that changes to docs and templates +## can be merged by any user who has maintainer rights, but is not included in +## the * rule (usually technical writers). /.gitlab/issue_templates/*.md /.gitlab/merge_request_templates/*.md /doc/*.md @@ -21,9 +22,10 @@ GITALY_SERVER_VERSION @project_278964_bot6 @gitlab-org/maintainers/rails-backend /data/deprecations/*.yml /data/removals/**/*.yml -## Technical writing files that do need approval -/data/deprecations/templates/ @marcel.amirault @gitlab-org/tw-leadership @sarahgerman -/data/removals/templates/ @marcel.amirault @gitlab-org/tw-leadership @sarahgerman +## Technical writing files that do not need `*` rule approval, +## but still require an approval from a TW team DRI for each file. +/data/deprecations/templates/ @marcel.amirault @sarahgerman @gitlab-org/tw-leadership +/data/removals/templates/ @marcel.amirault @sarahgerman @gitlab-org/tw-leadership .markdownlint.yml @marcel.amirault @eread @aqualls @gitlab-org/tw-leadership /doc/.markdownlint/ @marcel.amirault @eread @aqualls @gitlab-org/tw-leadership /doc/.vale/ @marcel.amirault @eread @aqualls @gitlab-org/tw-leadership @@ -32,238 +34,241 @@ GITALY_SERVER_VERSION @project_278964_bot6 @gitlab-org/maintainers/rails-backend ^[Source code editing] .solargraph.yml.example @igor.drozdov -^[Backend] -*.rb @gitlab-org/maintainers/rails-backend -*.rake @gitlab-org/maintainers/rails-backend +^[Backend] @gitlab-org/maintainers/rails-backend +*.rb +*.rake -^[Frontend] -*.scss @gitlab-org/maintainers/frontend -*.js @gitlab-org/maintainers/frontend -/app/assets/ @gitlab-org/maintainers/frontend -/ee/app/assets/ @gitlab-org/maintainers/frontend -/spec/frontend/ @gitlab-org/maintainers/frontend -/ee/spec/frontend/ @gitlab-org/maintainers/frontend -/spec/frontend_integration/ @gitlab-org/maintainers/frontend -/ee/spec/frontend_integration/ @gitlab-org/maintainers/frontend +^[Frontend] @gitlab-org/maintainers/frontend +*.scss +*.js +/app/assets/ +/ee/app/assets/ +/spec/frontend/ +/ee/spec/frontend/ +/spec/frontend_integration/ +/ee/spec/frontend_integration/ -^[Database] -/db/ @gitlab-org/maintainers/database -/ee/db/ @gitlab-org/maintainers/database -/lib/gitlab/background_migration/ @gitlab-org/maintainers/database -/ee/lib/ee/gitlab/background_migration/ @gitlab-org/maintainers/database -/lib/gitlab/database/ @gitlab-org/maintainers/database -/lib/gitlab/sql/ @gitlab-org/maintainers/database -/app/finders/ @gitlab-org/maintainers/database -/ee/app/finders/ @gitlab-org/maintainers/database -/rubocop/rubocop-migrations.yml @gitlab-org/maintainers/database +^[Database] @gitlab-org/maintainers/database +/db/ +/ee/db/ +/lib/gitlab/background_migration/ +/ee/lib/ee/gitlab/background_migration/ +/lib/gitlab/database/ +/lib/gitlab/sql/ +/app/finders/ +/ee/app/finders/ +/rubocop/rubocop-migrations.yml -[Engineering Productivity] -/.gitlab-ci.yml @gl-quality/eng-prod -/.gitlab/ci/ @gl-quality/eng-prod -/.gitlab/ci/docs.gitlab-ci.yml @gl-quality/eng-prod @gl-docsteam -/.gitlab/ci/package-and-test/ @gl-quality/eng-prod @gl-quality/qe-maintainers -/.gitlab/ci/qa.gitlab-ci.yml @gl-quality/eng-prod @gl-quality/qe-maintainers -/.gitlab/ci/releases.gitlab-ci.yml @gl-quality/eng-prod @gitlab-org/delivery -/.gitlab/ci/reports.gitlab-ci.yml @gitlab-com/gl-security/appsec @gl-quality/eng-prod -/.gitlab/ci/review-apps/qa.gitlab-ci.yml @gl-quality/eng-prod @gl-quality/qe-maintainers -/.gitlab/ci/test-on-gdk/ @gl-quality/eng-prod @gl-quality/qe-maintainers -Dangerfile @gl-quality/eng-prod -/danger/ @gl-quality/eng-prod -/tooling/danger/ @gl-quality/eng-prod -/scripts/ @gl-quality/eng-prod -/scripts/frontend/ @gl-quality/eng-prod @gitlab-org/maintainers/frontend -/scripts/review_apps/seed-dast-test-data.sh @dappelt @ngeorge1 @gl-quality/eng-prod -.editorconfig @gl-quality/eng-prod +[Engineering Productivity] @gl-quality/eng-prod +/.gitlab-ci.yml +/.gitlab/ci/ +/.gitlab/ci/docs.gitlab-ci.yml @gl-docsteam +/.gitlab/ci/package-and-test/ @gl-quality/qe-maintainers +/.gitlab/ci/qa.gitlab-ci.yml @gl-quality/qe-maintainers +/.gitlab/ci/releases.gitlab-ci.yml @gitlab-org/delivery +/.gitlab/ci/reports.gitlab-ci.yml @gitlab-com/gl-security/appsec +/.gitlab/ci/review-apps/qa.gitlab-ci.yml @gl-quality/qe-maintainers +/.gitlab/ci/test-on-gdk/ @gl-quality/qe-maintainers +Dangerfile +/danger/ +/tooling/danger/ +/scripts/ +/scripts/frontend/ @gitlab-org/maintainers/frontend +/scripts/review_apps/seed-dast-test-data.sh @dappelt @ngeorge1 +.editorconfig -^[Backend Static Code Analysis] -.rubocop*.yml @dstull @splattael @gl-quality/eng-prod -.rubocop_todo.yml @dstull @splattael @gl-quality/eng-prod -.rubocop_todo/ @dstull @splattael @gl-quality/eng-prod -/rubocop/ @dstull @splattael @gl-quality/eng-prod -/spec/rubocop/ @dstull @splattael @gl-quality/eng-prod +^[Backend Static Code Analysis] @gl-quality/eng-prod @dstull @splattael +.rubocop*.yml +.rubocop_todo/ +/rubocop/ +/spec/rubocop/ -^[End-to-end] -/qa/ @gl-quality +^[End-to-end] @gl-quality +/qa/ -^[LDAP] -/ee/lib/ee/gitlab/auth/ldap/ @dblessing @mkozono -/lib/gitlab/auth/ldap/ @dblessing @mkozono +^[LDAP] @dblessing @mkozono +/ee/lib/ee/gitlab/auth/ldap/ +/lib/gitlab/auth/ldap/ -^[Project Alias] -/ee/app/models/project_alias.rb @patrickbajao -/ee/lib/api/project_aliases.rb @patrickbajao +^[Project Alias] @patrickbajao +/ee/app/models/project_alias.rb +/ee/lib/api/project_aliases.rb -^[Distribution] -/lib/support/ @gitlab-org/distribution +^[Distribution] @gitlab-org/distribution +/lib/support/ # Secure & Threat Management ownership delineation # https://about.gitlab.com/handbook/engineering/development/threat-management/delineate-secure-threat-management.html#technical-boundaries -^[Threat Insights] -/app/finders/security/ @gitlab-org/govern/threat-insights-backend-team -/app/models/vulnerability.rb @gitlab-org/govern/threat-insights-backend-team -/app/presenters/projects/security/ @gitlab-org/govern/threat-insights-backend-team -/spec/presenters/projects/security/ @gitlab-org/govern/threat-insights-backend-team -/ee/app/finders/security/ @gitlab-org/govern/threat-insights-backend-team -/ee/app/models/security/ @gitlab-org/govern/threat-insights-backend-team -/ee/app/models/vulnerabilities/ @gitlab-org/govern/threat-insights-backend-team -/ee/app/policies/vulnerabilities/ @gitlab-org/govern/threat-insights-backend-team -/ee/app/policies/vulnerability*.rb @gitlab-org/govern/threat-insights-backend-team -/ee/lib/api/vulnerabilit*.rb @gitlab-org/govern/threat-insights-backend-team -/ee/spec/policies/vulnerabilities/ @gitlab-org/govern/threat-insights-backend-team -/ee/spec/policies/vulnerability*.rb @gitlab-org/govern/threat-insights-backend-team -/ee/app/assets/javascripts/license_compliance/components/detected_licenses_table.vue @gitlab-org/govern/threat-insights-frontend-team -/ee/spec/frontend/license_compliance/components/detected_licenses_table_spec.js @gitlab-org/govern/threat-insights-frontend-team +^[Threat Insights backend] @gitlab-org/govern/threat-insights-backend-team +/app/finders/security/ +/app/models/vulnerability.rb +/app/presenters/projects/security/ +/spec/presenters/projects/security/ +/ee/app/finders/security/ +/ee/app/models/security/ +/ee/app/models/vulnerabilities/ +/ee/app/policies/vulnerabilities/ +/ee/app/policies/vulnerability*.rb +/ee/lib/api/vulnerabilit*.rb +/ee/spec/policies/vulnerabilities/ +/ee/spec/policies/vulnerability*.rb -^[Secure] -/ee/app/services/app_sec/dast/ @gitlab-org/secure/dynamic-analysis-be +^[Threat Insights frontend] @gitlab-org/govern/threat-insights-frontend-team +/ee/app/assets/javascripts/license_compliance/components/detected_licenses_table.vue +/ee/spec/frontend/license_compliance/components/detected_licenses_table_spec.js -^[Security Policies] -/ee/app/assets/javascripts/approvals/components/license_compliance/** @gitlab-org/govern/security-policies-frontend -/ee/app/assets/javascripts/approvals/stores/modules/license_compliance/** @gitlab-org/govern/security-policies-frontend -/ee/app/assets/javascripts/license_compliance/** @gitlab-org/govern/security-policies-frontend -/ee/app/assets/javascripts/pages/projects/licenses/** @gitlab-org/govern/security-policies-frontend -/ee/app/assets/javascripts/pages/projects/pipelines/licenses/** @gitlab-org/govern/security-policies-frontend +^[Secure] @gitlab-org/secure/dynamic-analysis-be +/ee/app/services/app_sec/dast/ + +^[Security Policies frontend] @gitlab-org/govern/security-policies-frontend +/ee/app/assets/javascripts/approvals/components/license_compliance/** +/ee/app/assets/javascripts/approvals/stores/modules/license_compliance/** +/ee/app/assets/javascripts/license_compliance/** +/ee/app/assets/javascripts/pages/projects/licenses/** +/ee/app/assets/javascripts/pages/projects/pipelines/licenses/** /ee/app/assets/javascripts/pages/projects/pipelines/show/license_report.js -/ee/app/assets/javascripts/vue_merge_request_widget/extensions/license_compliance/** @gitlab-org/govern/security-policies-frontend -/ee/app/assets/javascripts/vue_shared/license_compliance/** @gitlab-org/govern/security-policies-frontend -/ee/app/views/projects/licenses/** @gitlab-org/govern/security-policies-frontend -/ee/spec/frontend/approvals/components/license_compliance/** @gitlab-org/govern/security-policies-frontend -/ee/spec/frontend/approvals/stores/modules/license_compliance/** @gitlab-org/govern/security-policies-frontend -/ee/spec/frontend/license_compliance/** @gitlab-org/govern/security-policies-frontend -/ee/spec/frontend/vue_merge_request_widget/extensions/license_compliance/** @gitlab-org/govern/security-policies-frontend -/ee/spec/frontend/vue_shared/license_compliance/** @gitlab-org/govern/security-policies-frontend +/ee/app/assets/javascripts/vue_merge_request_widget/extensions/license_compliance/** +/ee/app/assets/javascripts/vue_shared/license_compliance/** +/ee/app/views/projects/licenses/** +/ee/spec/frontend/approvals/components/license_compliance/** +/ee/spec/frontend/approvals/stores/modules/license_compliance/** +/ee/spec/frontend/license_compliance/** +/ee/spec/frontend/vue_merge_request_widget/extensions/license_compliance/** +/ee/spec/frontend/vue_shared/license_compliance/** -/ee/app/assets/javascripts/approvals/components/security_orchestration/** @gitlab-org/govern/security-policies-frontend -/ee/app/assets/javascripts/approvals/stores/modules/security_orchestration/** @gitlab-org/govern/security-policies-frontend -/ee/app/assets/javascripts/pages/groups/security/policies/** @gitlab-org/govern/security-policies-frontend -/ee/app/assets/javascripts/pages/projects/security/policies/** @gitlab-org/govern/security-policies-frontend -/ee/app/assets/javascripts/security_orchestration/** @gitlab-org/govern/security-policies-frontend -/ee/app/views/groups/security/policies @gitlab-org/govern/security-policies-frontend -/ee/app/views/projects/security/policies/** @gitlab-org/govern/security-policies-frontend -/ee/spec/frontend/approvals/components/security_orchestration/** @gitlab-org/govern/security-policies-frontend -/ee/spec/frontend/approvals/stores/modules/security_orchestration/** @gitlab-org/govern/security-policies-frontend -/ee/spec/frontend/security_orchestration/** @gitlab-org/govern/security-policies-frontend -/ee/spec/views/projects/security/policies/** @gitlab-org/govern/security-policies-frontend +/ee/app/assets/javascripts/approvals/components/security_orchestration/** +/ee/app/assets/javascripts/approvals/stores/modules/security_orchestration/** +/ee/app/assets/javascripts/pages/groups/security/policies/** +/ee/app/assets/javascripts/pages/projects/security/policies/** +/ee/app/assets/javascripts/security_orchestration/** +/ee/app/views/groups/security/policies +/ee/app/views/projects/security/policies/** +/ee/spec/frontend/approvals/components/security_orchestration/** +/ee/spec/frontend/approvals/stores/modules/security_orchestration/** +/ee/spec/frontend/security_orchestration/** +/ee/spec/views/projects/security/policies/** -/app/models/clusters/applications/cilium.rb @gitlab-org/govern/security-policies-backend -/ee/app/controllers/groups/security/policies_controller.rb @gitlab-org/govern/security-policies-backend -/ee/app/controllers/projects/security/policies_controller.rb @gitlab-org/govern/security-policies-backend -/ee/app/graphql/mutations/concerns/mutations/finds_project_or_group_for_security_policies.rb @gitlab-org/govern/security-policies-backend -/ee/app/graphql/mutations/security_policy/** @gitlab-org/govern/security-policies-backend -/ee/app/graphql/resolvers/concerns/resolves_orchestration_policy.rb @gitlab-org/govern/security-policies-backend -/ee/app/graphql/resolvers/security_orchestration/** @gitlab-org/govern/security-policies-backend -/ee/app/graphql/types/security_orchestration/** @gitlab-org/govern/security-policies-backend -/ee/app/helpers/ee/security_orchestration_helper.rb @gitlab-org/govern/security-policies-backend -/ee/app/models/security/orchestration_policy_configuration.rb @gitlab-org/govern/security-policies-backend -/ee/app/models/security/orchestration_policy_rule_schedule.rb @gitlab-org/govern/security-policies-backend -/ee/app/services/security/orchestration/** @gitlab-org/govern/security-policies-backend -/ee/app/services/security/security_orchestration_policies/** @gitlab-org/govern/security-policies-backend -/ee/app/validators/json_schemas/security_orchestration_policy.json @gitlab-org/govern/security-policies-backend -/ee/app/workers/concerns/update_orchestration_policy_configuration.rb @gitlab-org/govern/security-policies-backend -/ee/app/workers/security/create_orchestration_policy_worker.rb @gitlab-org/govern/security-policies-backend -/ee/app/workers/security/orchestration_policy_rule_schedule_namespace_worker.rb @gitlab-org/govern/security-policies-backend -/ee/app/workers/security/orchestration_policy_rule_schedule_worker.rb @gitlab-org/govern/security-policies-backend -/ee/lib/ee/gitlab/ci/pipeline/chain/validate/security_orchestration_policy.rb @gitlab-org/govern/security-policies-backend -/ee/lib/gitlab/ci/config/security_orchestration_policies/** @gitlab-org/govern/security-policies-backend -/ee/lib/gitlab/graphql/aggregations/security_orchestration_policies/** @gitlab-org/govern/security-policies-backend -/ee/spec/controllers/groups/security/policies_controller_spec.rb @gitlab-org/govern/security-policies-backend -/ee/spec/factories/security_orchestration_policy_configurations.rb @gitlab-org/govern/security-policies-backend -/ee/spec/factories/security_orchestration_policy_rule_schedules.rb @gitlab-org/govern/security-policies-backend -/ee/spec/factories/security/policies.rb @gitlab-org/govern/security-policies-backend -/ee/spec/graphql/mutations/security_policy/** @gitlab-org/govern/security-policies-backend -/ee/spec/graphql/resolvers/security_orchestration/** @gitlab-org/govern/security-policies-backend -/ee/spec/graphql/types/security_orchestration/** @gitlab-org/govern/security-policies-backend -/ee/spec/helpers/ee/security_orchestration_helper_spec.rb @gitlab-org/govern/security-policies-backend -/ee/spec/lib/ee/gitlab/ci/pipeline/chain/validate/security_orchestration_policy_spec.rb @gitlab-org/govern/security-policies-backend -/ee/spec/lib/gitlab/ci/config/security_orchestration_policies/** @gitlab-org/govern/security-policies-backend -/ee/spec/lib/gitlab/graphql/aggregations/security_orchestration_policies/** @gitlab-org/govern/security-policies-backend -/ee/spec/models/security/orchestration_policy_configuration_spec.rb @gitlab-org/govern/security-policies-backend -/ee/spec/models/security/orchestration_policy_rule_schedule_spec.rb @gitlab-org/govern/security-policies-backend -/ee/spec/requests/api/graphql/mutations/security_policy/** @gitlab-org/govern/security-policies-backend -/ee/spec/requests/api/graphql/project/security_orchestration/** @gitlab-org/govern/security-policies-backend -/ee/spec/requests/projects/security/policies_controller_spec.rb @gitlab-org/govern/security-policies-backend -/ee/spec/services/security/orchestration/** @gitlab-org/govern/security-policies-backend -/ee/spec/services/security/security_orchestration_policies/** @gitlab-org/govern/security-policies-backend -/ee/spec/support/shared_contexts/graphql/resolvers/security_orchestration/** @gitlab-org/govern/security-policies-backend -/ee/spec/views/projects/security/policies/index.html.haml_spec.rb @gitlab-org/govern/security-policies-backend -/ee/spec/workers/concerns/update_orchestration_policy_configuration_spec.rb @gitlab-org/govern/security-policies-backend -/ee/spec/workers/security/create_orchestration_policy_worker_spec.rb @gitlab-org/govern/security-policies-backend -/ee/spec/workers/security/orchestration_policy_rule_schedule_namespace_worker_spec.rb @gitlab-org/govern/security-policies-backend -/ee/spec/workers/security/orchestration_policy_rule_schedule_worker_spec.rb @gitlab-org/govern/security-policies-backend -/lib/gitlab/ci/pipeline/chain/validate/security_orchestration_policy.rb @gitlab-org/govern/security-policies-backend -/spec/models/clusters/applications/cilium_spec.rb @gitlab-org/govern/security-policies-backend +^[Security Policies backend] @gitlab-org/govern/security-policies-backend +/app/models/clusters/applications/cilium.rb +/ee/app/controllers/groups/security/policies_controller.rb +/ee/app/controllers/projects/security/policies_controller.rb +/ee/app/graphql/mutations/concerns/mutations/finds_project_or_group_for_security_policies.rb +/ee/app/graphql/mutations/security_policy/** +/ee/app/graphql/resolvers/concerns/resolves_orchestration_policy.rb +/ee/app/graphql/resolvers/security_orchestration/** +/ee/app/graphql/types/security_orchestration/** +/ee/app/helpers/ee/security_orchestration_helper.rb +/ee/app/models/security/orchestration_policy_configuration.rb +/ee/app/models/security/orchestration_policy_rule_schedule.rb +/ee/app/services/security/orchestration/** +/ee/app/services/security/security_orchestration_policies/** +/ee/app/validators/json_schemas/security_orchestration_policy.json +/ee/app/workers/concerns/update_orchestration_policy_configuration.rb +/ee/app/workers/security/create_orchestration_policy_worker.rb +/ee/app/workers/security/orchestration_policy_rule_schedule_namespace_worker.rb +/ee/app/workers/security/orchestration_policy_rule_schedule_worker.rb +/ee/lib/ee/gitlab/ci/pipeline/chain/validate/security_orchestration_policy.rb +/ee/lib/gitlab/ci/config/security_orchestration_policies/** +/ee/lib/gitlab/graphql/aggregations/security_orchestration_policies/** +/ee/spec/controllers/groups/security/policies_controller_spec.rb +/ee/spec/factories/security_orchestration_policy_configurations.rb +/ee/spec/factories/security_orchestration_policy_rule_schedules.rb +/ee/spec/factories/security/policies.rb +/ee/spec/graphql/mutations/security_policy/** +/ee/spec/graphql/resolvers/security_orchestration/** +/ee/spec/graphql/types/security_orchestration/** +/ee/spec/helpers/ee/security_orchestration_helper_spec.rb +/ee/spec/lib/ee/gitlab/ci/pipeline/chain/validate/security_orchestration_policy_spec.rb +/ee/spec/lib/gitlab/ci/config/security_orchestration_policies/** +/ee/spec/lib/gitlab/graphql/aggregations/security_orchestration_policies/** +/ee/spec/models/security/orchestration_policy_configuration_spec.rb +/ee/spec/models/security/orchestration_policy_rule_schedule_spec.rb +/ee/spec/requests/api/graphql/mutations/security_policy/** +/ee/spec/requests/api/graphql/project/security_orchestration/** +/ee/spec/requests/projects/security/policies_controller_spec.rb +/ee/spec/services/security/orchestration/** +/ee/spec/services/security/security_orchestration_policies/** +/ee/spec/support/shared_contexts/graphql/resolvers/security_orchestration/** +/ee/spec/views/projects/security/policies/index.html.haml_spec.rb +/ee/spec/workers/concerns/update_orchestration_policy_configuration_spec.rb +/ee/spec/workers/security/create_orchestration_policy_worker_spec.rb +/ee/spec/workers/security/orchestration_policy_rule_schedule_namespace_worker_spec.rb +/ee/spec/workers/security/orchestration_policy_rule_schedule_worker_spec.rb +/lib/gitlab/ci/pipeline/chain/validate/security_orchestration_policy.rb +/spec/models/clusters/applications/cilium_spec.rb -/app/finders/security/license_compliance_jobs_finder.rb @gitlab-org/govern/security-policies-backend -/ee/app/controllers/projects/licenses_controller.rb @gitlab-org/govern/security-policies-backend -/ee/app/finders/software_license_policies_finder.rb @gitlab-org/govern/security-policies-backend -/ee/app/models/sca/license_compliance.rb @gitlab-org/govern/security-policies-backend @gitlab-org/secure/composition-analysis-be -/ee/app/models/sca/license_policy.rb @gitlab-org/govern/security-policies-backend -/ee/app/models/software_license_policy.rb @gitlab-org/govern/security-policies-backend -/ee/app/models/software_license.rb @gitlab-org/govern/security-policies-backend -/ee/app/serializers/license_compliance/** @gitlab-org/govern/security-policies-backend -/ee/app/serializers/license_entity.rb @gitlab-org/govern/security-policies-backend -/ee/app/serializers/licenses_list_entity.rb @gitlab-org/govern/security-policies-backend -/ee/app/serializers/licenses_list_serializer.rb @gitlab-org/govern/security-policies-backend -/ee/app/serializers/security/license_policy_entity.rb @gitlab-org/govern/security-policies-backend -/ee/app/services/ci/compare_license_scanning_reports_collapsed_service.rb @gitlab-org/govern/security-policies-backend -/ee/app/services/ci/compare_license_scanning_reports_service.rb @gitlab-org/govern/security-policies-backend -/ee/app/services/software_license_policies/** @gitlab-org/govern/security-policies-backend -/ee/app/services/software_license_policies/update_service.rb @gitlab-org/govern/security-policies-backend -/ee/app/workers/refresh_license_compliance_checks_worker.rb @gitlab-org/govern/security-policies-backend -/ee/lib/api/managed_licenses.rb @gitlab-org/govern/security-policies-backend -/ee/lib/ee/api/entities/managed_license.rb @gitlab-org/govern/security-policies-backend -/ee/lib/gitlab/spdx/license.rb @gitlab-org/govern/security-policies-backend -/ee/spec/factories/software_license_policy.rb @gitlab-org/govern/security-policies-backend -/ee/spec/factories/software_license.rb @gitlab-org/govern/security-policies-backend -/ee/spec/factories/spdx_license.rb @gitlab-org/govern/security-policies-backend -/ee/spec/finders/software_license_policies_finder_spec.rb @gitlab-org/govern/security-policies-backend -/ee/spec/lib/gitlab/ci/parsers/license_compliance/** @gitlab-org/govern/security-policies-backend -/ee/spec/models/sca/license_compliance_spec.rb @gitlab-org/govern/security-policies-backend @gitlab-org/secure/composition-analysis-be -/ee/spec/models/sca/license_policy_spec.rb @gitlab-org/govern/security-policies-backend -/ee/spec/models/software_license_policy_spec.rb @gitlab-org/govern/security-policies-backend -/ee/spec/models/software_license_spec.rb @gitlab-org/govern/security-policies-backend -/ee/spec/requests/api/managed_licenses_spec.rb @gitlab-org/govern/security-policies-backend -/ee/spec/serializers/license_compliance/** @gitlab-org/govern/security-policies-backend -/ee/spec/services/ci/compare_license_scanning_reports_collapsed_service_spec.rb @gitlab-org/govern/security-policies-backend -/ee/spec/services/ci/compare_license_scanning_reports_service_spec.rb @gitlab-org/govern/security-policies-backend -/ee/spec/services/software_license_policies/** @gitlab-org/govern/security-policies-backend -/spec/finders/security/license_compliance_jobs_finder_spec.rb @gitlab-org/govern/security-policies-backend +/app/finders/security/license_compliance_jobs_finder.rb +/ee/app/controllers/projects/licenses_controller.rb +/ee/app/finders/software_license_policies_finder.rb +/ee/app/models/sca/license_compliance.rb @gitlab-org/secure/composition-analysis-be +/ee/app/models/sca/license_policy.rb +/ee/app/models/software_license_policy.rb +/ee/app/models/software_license.rb +/ee/app/serializers/license_compliance/** +/ee/app/serializers/license_entity.rb +/ee/app/serializers/licenses_list_entity.rb +/ee/app/serializers/licenses_list_serializer.rb +/ee/app/serializers/security/license_policy_entity.rb +/ee/app/services/ci/compare_license_scanning_reports_collapsed_service.rb +/ee/app/services/ci/compare_license_scanning_reports_service.rb +/ee/app/services/software_license_policies/** +/ee/app/services/software_license_policies/update_service.rb +/ee/app/workers/refresh_license_compliance_checks_worker.rb +/ee/lib/api/managed_licenses.rb +/ee/lib/ee/api/entities/managed_license.rb +/ee/lib/gitlab/spdx/license.rb +/ee/spec/factories/software_license_policy.rb +/ee/spec/factories/software_license.rb +/ee/spec/factories/spdx_license.rb +/ee/spec/finders/software_license_policies_finder_spec.rb +/ee/spec/lib/gitlab/ci/parsers/license_compliance/** +/ee/spec/models/sca/license_compliance_spec.rb @gitlab-org/secure/composition-analysis-be +/ee/spec/models/sca/license_policy_spec.rb +/ee/spec/models/software_license_policy_spec.rb +/ee/spec/models/software_license_spec.rb +/ee/spec/requests/api/managed_licenses_spec.rb +/ee/spec/serializers/license_compliance/** +/ee/spec/services/ci/compare_license_scanning_reports_collapsed_service_spec.rb +/ee/spec/services/ci/compare_license_scanning_reports_service_spec.rb +/ee/spec/services/software_license_policies/** +/spec/finders/security/license_compliance_jobs_finder_spec.rb -^[Code Owners] -/ee/lib/gitlab/code_owners.rb @reprazent @kerrizor @garyh -/ee/lib/gitlab/code_owners/ @reprazent @kerrizor @garyh -/ee/spec/lib/gitlab/code_owners/ @reprazent @kerrizor @garyh -/doc/user/project/code_owners.md @reprazent @kerrizor @garyh +^[Code Owners] @reprazent @kerrizor @garyh +/ee/lib/gitlab/code_owners.rb +/ee/lib/gitlab/code_owners/ +/ee/spec/lib/gitlab/code_owners/ +/doc/user/project/code_owners.md -^[Merge Requests] -/app/controllers/projects/merge_requests/ @garyh @patrickbajao @marc_shaw @kerrizor -/app/models/merge_request.rb @dskim_gitlab @garyh @patrickbajao @marc_shaw @kerrizor -/app/services/merge_requests/ @dskim_gitlab @garyh @patrickbajao @marc_shaw @kerrizor -/app/workers/merge_requests/ @dskim_gitlab @garyh @patrickbajao @marc_shaw @kerrizor -/app/workers/merge_request_mergeability_check_worker.rb @dskim_gitlab @garyh @patrickbajao @marc_shaw @kerrizor -/lib/gitlab/diff/ @dskim_gitlab @garyh @patrickbajao @marc_shaw @kerrizor -/lib/gitlab/discussions_diff/ @dskim_gitlab @garyh @patrickbajao @marc_shaw @kerrizor -/lib/gitlab/quick_actions/ @dskim_gitlab @garyh @patrickbajao @marc_shaw @kerrizor +^[Merge Requests backend] @garyh @patrickbajao @marc_shaw @kerrizor +/app/controllers/projects/merge_requests/ +/app/models/merge_request.rb @dskim_gitlab +/app/services/merge_requests/ @dskim_gitlab +/app/workers/merge_requests/ @dskim_gitlab +/app/workers/merge_request_mergeability_check_worker.rb @dskim_gitlab +/lib/gitlab/diff/ @dskim_gitlab +/lib/gitlab/discussions_diff/ @dskim_gitlab +/lib/gitlab/quick_actions/ @dskim_gitlab -/ee/app/models/ee/merge_request.rb @dskim_gitlab @garyh @patrickbajao @marc_shaw @kerrizor -/ee/app/services/merge_requests/ @dskim_gitlab @garyh @patrickbajao @marc_shaw @kerrizor -/ee/app/workers/merge_requests/ @dskim_gitlab @garyh @patrickbajao @marc_shaw @kerrizor -/ee/app/workers/merge_request_reset_approvals_worker.rb @dskim_gitlab @garyh @patrickbajao @marc_shaw @kerrizor +/ee/app/models/ee/merge_request.rb @dskim_gitlab +/ee/app/services/merge_requests/ @dskim_gitlab +/ee/app/workers/merge_requests/ @dskim_gitlab +/ee/app/workers/merge_request_reset_approvals_worker.rb @dskim_gitlab -/app/assets/javascripts/diffs @viktomas @jboyson @iamphill @thomasrandolph -/app/assets/javascripts/batch_comments/ @viktomas @jboyson @iamphill @thomasrandolph -/app/assets/javascripts/notes @viktomas @jboyson @iamphill @thomasrandolph -/app/assets/javascripts/merge_conflicts @viktomas @jboyson @iamphill @thomasrandolph -/app/assets/javascripts/mr_notes @viktomas @jboyson @iamphill @thomasrandolph -/app/assets/javascripts/issuable/popover @viktomas @jboyson @iamphill @thomasrandolph -/app/assets/javascripts/vue_merge_request_widget @viktomas @jboyson @iamphill @thomasrandolph -/app/assets/javascripts/merge_request.js @viktomas @jboyson @iamphill @thomasrandolph -/app/assets/javascripts/merge_request_tabs.js @viktomas @jboyson @iamphill @thomasrandolph -/app/assets/stylesheets/framework/diffs.scss @viktomas @jboyson @iamphill @thomasrandolph -/ee/app/assets/javascripts/diffs/ @viktomas @jboyson @iamphill @thomasrandolph -/ee/app/assets/javascripts/vue_merge_request_widget @viktomas @jboyson @iamphill @thomasrandolph -/spec/frontend/diffs/ @viktomas @jboyson @iamphill @thomasrandolph -/spec/frontend/batch_comments/ @viktomas @jboyson @iamphill @thomasrandolph +^[Merge Requests frontend] @viktomas @jboyson @iamphill @thomasrandolph +/app/assets/javascripts/diffs +/app/assets/javascripts/batch_comments/ +/app/assets/javascripts/notes +/app/assets/javascripts/merge_conflicts +/app/assets/javascripts/mr_notes +/app/assets/javascripts/issuable/popover +/app/assets/javascripts/vue_merge_request_widget +/app/assets/javascripts/merge_request.js +/app/assets/javascripts/merge_request_tabs.js +/app/assets/stylesheets/framework/diffs.scss +/ee/app/assets/javascripts/diffs/ +/ee/app/assets/javascripts/vue_merge_request_widget +/spec/frontend/diffs/ +/spec/frontend/batch_comments/ ^[Product Analytics] @gitlab-org/analytics-section/product-analytics/engineers/frontend /ee/app/assets/javascripts/analytics/analytics_dashboards/components/analytics_dashboard.vue @@ -273,118 +278,118 @@ Dangerfile @gl-quality/eng-prod /ee/app/assets/javascripts/vue_shared/components/customizable_dashboard/panels_base.vue /ee/app/assets/javascripts/product_analytics/ -^[Product Intelligence] -/ee/lib/gitlab/usage_data_counters/ @gitlab-org/analytics-section/product-intelligence/engineers -/ee/lib/ee/gitlab/usage_data.rb @gitlab-org/analytics-section/product-intelligence/engineers -/lib/gitlab/usage_data.rb @gitlab-org/analytics-section/product-intelligence/engineers -/lib/gitlab/usage_data_counters/ @gitlab-org/analytics-section/product-intelligence/engineers -/lib/gitlab/usage/ @gitlab-org/analytics-section/product-intelligence/engineers -/ee/lib/ee/gitlab/usage_data_counters/ @gitlab-org/analytics-section/product-intelligence/engineers -/ee/lib/ee/gitlab/usage/ @gitlab-org/analytics-section/product-intelligence/engineers -/config/metrics/ @gitlab-org/analytics-section/product-intelligence/engineers -/ee/config/metrics/ @gitlab-org/analytics-section/product-intelligence/engineers -/app/workers/gitlab_service_ping_worker.rb @gitlab-org/analytics-section/product-intelligence/engineers -/spec/workers/gitlab_service_ping_worker_spec.rb @gitlab-org/analytics-section/product-intelligence/engineers -/ee/spec/lib/gitlab/usage_data_counters/ @gitlab-org/analytics-section/product-intelligence/engineers -/ee/spec/lib/ee/gitlab/usage_data_spec.rb @gitlab-org/analytics-section/product-intelligence/engineers -/spec/lib/gitlab/usage_data_spec.rb @gitlab-org/analytics-section/product-intelligence/engineers -/spec/lib/gitlab/usage_data_counters/ @gitlab-org/analytics-section/product-intelligence/engineers -/spec/lib/gitlab/usage/ @gitlab-org/analytics-section/product-intelligence/engineers -/ee/spec/lib/ee/gitlab/usage_data_counters/ @gitlab-org/analytics-section/product-intelligence/engineers -/ee/spec/lib/ee/gitlab/usage/ @gitlab-org/analytics-section/product-intelligence/engineers -/ee/spec/config/metrics/ @gitlab-org/analytics-section/product-intelligence/engineers +^[Product Intelligence] @gitlab-org/analytics-section/product-intelligence/engineers +/ee/lib/gitlab/usage_data_counters/ +/ee/lib/ee/gitlab/usage_data.rb +/lib/gitlab/usage_data.rb +/lib/gitlab/usage_data_counters/ +/lib/gitlab/usage/ +/ee/lib/ee/gitlab/usage_data_counters/ +/ee/lib/ee/gitlab/usage/ +/config/metrics/ +/ee/config/metrics/ +/app/workers/gitlab_service_ping_worker.rb +/spec/workers/gitlab_service_ping_worker_spec.rb +/ee/spec/lib/gitlab/usage_data_counters/ +/ee/spec/lib/ee/gitlab/usage_data_spec.rb +/spec/lib/gitlab/usage_data_spec.rb +/spec/lib/gitlab/usage_data_counters/ +/spec/lib/gitlab/usage/ +/ee/spec/lib/ee/gitlab/usage_data_counters/ +/ee/spec/lib/ee/gitlab/usage/ +/ee/spec/config/metrics/ -^[Growth Experiments] -/app/experiments/ @gitlab-org/growth/experiment-devs -/spec/experiments/ @gitlab-org/growth/experiment-devs -/config/initializers/gitlab_experiment.rb @gitlab-org/growth/experiment-devs -/config/feature_flags/experiment/ @gitlab-org/growth/experiment-devs -/ee/config/feature_flags/experiment/ @gitlab-org/growth/experiment-devs -/ee/lib/api/experiments.rb @gitlab-org/growth/experiment-devs -/ee/spec/requests/api/experiments_spec.rb @gitlab-org/growth/experiment-devs -/ee/lib/ee/api/entities/experiment.rb @gitlab-org/growth/experiment-devs -/ee/spec/lib/ee/api/entities/experiment_spec.rb @gitlab-org/growth/experiment-devs +^[Growth Experiments] @gitlab-org/growth/experiment-devs +/app/experiments/ +/spec/experiments/ +/config/initializers/gitlab_experiment.rb +/config/feature_flags/experiment/ +/ee/config/feature_flags/experiment/ +/ee/lib/api/experiments.rb +/ee/spec/requests/api/experiments_spec.rb +/ee/lib/ee/api/entities/experiment.rb +/ee/spec/lib/ee/api/entities/experiment_spec.rb -^[Growth] -/ee/app/workers/onboarding/ @gitlab-org/growth/engineers -/ee/spec/workers/onboarding/ @gitlab-org/growth/engineers -/app/models/onboarding/ @gitlab-org/growth/engineers -/spec/models/onboarding/ @gitlab-org/growth/engineers -/app/services/onboarding/ @gitlab-org/growth/engineers -/spec/services/onboarding/ @gitlab-org/growth/engineers -/ee/app/components/namespaces/free_user_cap/ @gitlab-org/growth/engineers -/ee/spec/components/namespaces/free_user_cap/ @gitlab-org/growth/engineers -/ee/app/models/namespaces/free_user_cap/ @gitlab-org/growth/engineers -/ee/spec/models/namespaces/free_user_cap/ @gitlab-org/growth/engineers -/app/controllers/registrations_controller.rb @gitlab-org/growth/engineers -/spec/controllers/registrations_controller_spec.rb @gitlab-org/growth/engineers -/app/controllers/registrations/ @gitlab-org/growth/engineers -/spec/controllers/registrations/ @gitlab-org/growth/engineers -/app/controllers/confirmations_controller.rb @gitlab-org/growth/engineers -/spec/controllers/confirmations_controller_spec.rb @gitlab-org/growth/engineers -/ee/app/controllers/trial_registrations_controller.rb @gitlab-org/growth/engineers -/ee/spec/controllers/trial_registrations_controller_spec.rb @gitlab-org/growth/engineers -/ee/spec/requests/trial_registrations_controller_spec.rb @gitlab-org/growth/engineers -/ee/app/controllers/registrations/ @gitlab-org/growth/engineers -/ee/spec/controllers/registrations/ @gitlab-org/growth/engineers -/ee/spec/requests/registrations/ @gitlab-org/growth/engineers -/ee/app/controllers/ee/registrations_controller.rb @gitlab-org/growth/engineers -/ee/spec/controllers/ee/registrations_controller_spec.rb @gitlab-org/growth/engineers -/ee/app/controllers/ee/registrations/ @gitlab-org/growth/engineers -/ee/app/controllers/ee/confirmations_controller.rb @gitlab-org/growth/engineers -/ee/app/controllers/subscriptions_controller.rb @gitlab-org/growth/engineers -/ee/spec/controllers/subscriptions_controller_spec.rb @gitlab-org/growth/engineers -/ee/app/controllers/subscriptions/ @gitlab-org/growth/engineers -/ee/spec/controllers/subscriptions/ @gitlab-org/growth/engineers -/app/services/users/in_product_marketing_email_records.rb @gitlab-org/growth/engineers -/spec/services/users/in_product_marketing_email_records_spec.rb @gitlab-org/growth/engineers -/app/workers/namespaces/in_product_marketing_emails_worker.rb @gitlab-org/growth/engineers -/spec/workers/namespaces/in_product_marketing_emails_worker_spec.rb @gitlab-org/growth/engineers -/ee/app/workers/ee/namespaces/in_product_marketing_emails_worker.rb @gitlab-org/growth/engineers -/ee/spec/workers/ee/namespaces/in_product_marketing_emails_worker_spec.rb @gitlab-org/growth/engineers -/app/models/users/in_product_marketing_email.rb @gitlab-org/growth/engineers -/spec/models/users/in_product_marketing_email_spec.rb @gitlab-org/growth/engineers -/app/services/namespaces/in_product_marketing_emails_service.rb @gitlab-org/growth/engineers -/spec/services/namespaces/in_product_marketing_emails_service_spec.rb @gitlab-org/growth/engineers -/ee/app/services/ee/namespaces/in_product_marketing_emails_service.rb @gitlab-org/growth/engineers -/ee/spec/services/namespaces/in_product_marketing_emails_service_spec.rb @gitlab-org/growth/engineers -/app/workers/projects/record_target_platforms_worker.rb @gitlab-org/growth/engineers -/spec/workers/projects/record_target_platforms_worker_spec.rb @gitlab-org/growth/engineers +^[Growth] @gitlab-org/growth/engineers +/ee/app/workers/onboarding/ +/ee/spec/workers/onboarding/ +/app/models/onboarding/ +/spec/models/onboarding/ +/app/services/onboarding/ +/spec/services/onboarding/ +/ee/app/components/namespaces/free_user_cap/ +/ee/spec/components/namespaces/free_user_cap/ +/ee/app/models/namespaces/free_user_cap/ +/ee/spec/models/namespaces/free_user_cap/ +/app/controllers/registrations_controller.rb +/spec/controllers/registrations_controller_spec.rb +/app/controllers/registrations/ +/spec/controllers/registrations/ +/app/controllers/confirmations_controller.rb +/spec/controllers/confirmations_controller_spec.rb +/ee/app/controllers/trial_registrations_controller.rb +/ee/spec/controllers/trial_registrations_controller_spec.rb +/ee/spec/requests/trial_registrations_controller_spec.rb +/ee/app/controllers/registrations/ +/ee/spec/controllers/registrations/ +/ee/spec/requests/registrations/ +/ee/app/controllers/ee/registrations_controller.rb +/ee/spec/controllers/ee/registrations_controller_spec.rb +/ee/app/controllers/ee/registrations/ +/ee/app/controllers/ee/confirmations_controller.rb +/ee/app/controllers/subscriptions_controller.rb +/ee/spec/controllers/subscriptions_controller_spec.rb +/ee/app/controllers/subscriptions/ +/ee/spec/controllers/subscriptions/ +/app/services/users/in_product_marketing_email_records.rb +/spec/services/users/in_product_marketing_email_records_spec.rb +/app/workers/namespaces/in_product_marketing_emails_worker.rb +/spec/workers/namespaces/in_product_marketing_emails_worker_spec.rb +/ee/app/workers/ee/namespaces/in_product_marketing_emails_worker.rb +/ee/spec/workers/ee/namespaces/in_product_marketing_emails_worker_spec.rb +/app/models/users/in_product_marketing_email.rb +/spec/models/users/in_product_marketing_email_spec.rb +/app/services/namespaces/in_product_marketing_emails_service.rb +/spec/services/namespaces/in_product_marketing_emails_service_spec.rb +/ee/app/services/ee/namespaces/in_product_marketing_emails_service.rb +/ee/spec/services/namespaces/in_product_marketing_emails_service_spec.rb +/app/workers/projects/record_target_platforms_worker.rb +/spec/workers/projects/record_target_platforms_worker_spec.rb -^[Legal] -/config/dependency_decisions.yml @gitlab-org/legal-reviewers +^[Legal] @gitlab-org/legal-reviewers +/config/dependency_decisions.yml -^[Workhorse] -/workhorse/ @jacobvosmaer-gitlab @nolith @patrickbajao @igor.drozdov +^[Workhorse] @jacobvosmaer-gitlab @nolith @patrickbajao @igor.drozdov +/workhorse/ -[Application Security] -/app/assets/javascripts/lib/dompurify.js @gitlab-com/gl-security/appsec -/app/assets/javascripts/gfm_auto_complete.js @gitlab-com/gl-security/appsec -/ee/app/assets/javascripts/gfm_auto_complete.js @gitlab-com/gl-security/appsec -/app/validators/addressable_url_validator.rb @gitlab-com/gl-security/appsec -/app/validators/public_url_validator.rb @gitlab-com/gl-security/appsec -/config/initializers/content_security_policy.rb @gitlab-com/gl-security/appsec -/lib/gitlab/content_security_policy/ @gitlab-com/gl-security/appsec -/lib/gitlab/http.rb @gitlab-com/gl-security/appsec -/lib/gitlab/http_connection_adapter.rb @gitlab-com/gl-security/appsec -/lib/gitlab/sanitizers @gitlab-com/gl-security/appsec -/lib/gitlab/untrusted_regexp.rb @gitlab-com/gl-security/appsec -/lib/gitlab/url_blocker.rb @gitlab-com/gl-security/appsec -/lib/gitlab/url_blockers/ @gitlab-com/gl-security/appsec -/lib/gitlab/utils.rb @gitlab-com/gl-security/appsec +[Application Security] @gitlab-com/gl-security/appsec +/app/assets/javascripts/lib/dompurify.js +/app/assets/javascripts/gfm_auto_complete.js +/ee/app/assets/javascripts/gfm_auto_complete.js +/app/validators/addressable_url_validator.rb +/app/validators/public_url_validator.rb +/config/initializers/content_security_policy.rb +/lib/gitlab/content_security_policy/ +/lib/gitlab/http.rb +/lib/gitlab/http_connection_adapter.rb +/lib/gitlab/sanitizers +/lib/gitlab/untrusted_regexp.rb +/lib/gitlab/url_blocker.rb +/lib/gitlab/url_blockers/ +/lib/gitlab/utils.rb -^[Gitaly] -lib/gitlab/git_access.rb @proglottis @toon -lib/gitlab/git_access_*.rb @proglottis @toon -ee/lib/ee/gitlab/git_access.rb @proglottis @toon -ee/lib/ee/gitlab/git_access_*.rb @proglottis @toon -ee/lib/ee/gitlab/checks/** @proglottis @toon -lib/gitlab/checks/** @proglottis @toon +^[Gitaly] @proglottis @toon +lib/gitlab/git_access.rb +lib/gitlab/git_access_*.rb +ee/lib/ee/gitlab/git_access.rb +ee/lib/ee/gitlab/git_access_*.rb +ee/lib/ee/gitlab/checks/** +lib/gitlab/checks/** -^[Documentation Directories] -/doc/ @gl-docsteam -/data/deprecations/ @gl-docsteam -/data/removals/ @gl-docsteam +^[Documentation Directories] @gl-docsteam +/doc/ +/data/deprecations/ +/data/removals/ ^[Documentation Pages] # This block is managed by the rake script at lib/tasks/gitlab/tw/codeowners.rake, manual updates will be overwritten! @@ -978,253 +983,252 @@ lib/gitlab/checks/** @proglottis @toon /doc/user/workspace/quick_start/ @ashrafkhamis # End rake-managed-docs-block -[Authentication and Authorization] -/app/assets/javascripts/access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/assets/javascripts/alerts_settings/graphql/mutations/reset_http_token.mutation.graphql @gitlab-org/manage/authentication-and-authorization/approvers -/app/assets/javascripts/authentication/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/assets/javascripts/ide/components/shared/tokened_input.vue @gitlab-org/manage/authentication-and-authorization/approvers -/app/assets/javascripts/packages_and_registries/package_registry/components/list/tokens/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/assets/javascripts/pages/admin/impersonation_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/assets/javascripts/pages/groups/settings/access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/assets/javascripts/pages/ldap/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/assets/javascripts/pages/oauth/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/assets/javascripts/pages/omniauth_callbacks/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/assets/javascripts/pages/profiles/password_prompt/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/assets/javascripts/pages/profiles/personal_access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/assets/javascripts/pages/profiles/two_factor_auths/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/assets/javascripts/pages/projects/settings/access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/assets/javascripts/pages/sessions/new/oauth_remember_me.js @gitlab-org/manage/authentication-and-authorization/approvers -/app/assets/javascripts/projects/settings/topics/components/ @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/stylesheets/page_bundles/profile_two_factor_auth.scss @gitlab-org/manage/authentication-and-authorization/approvers -/app/controllers/admin/impersonation_tokens_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/controllers/concerns/access_tokens_actions.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/controllers/concerns/authenticates_with_two_factor.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/controllers/concerns/authenticates_with_two_factor_for_admin_mode.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/controllers/concerns/enforces_admin_authentication.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/controllers/concerns/enforces_two_factor_authentication.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/controllers/concerns/oauth_applications.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/controllers/concerns/project_unauthorized.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/models/concerns/require_email_verification.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/controllers/concerns/sessionless_authentication.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/controllers/concerns/snippet_authorizations.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/controllers/concerns/verifies_with_email.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/controllers/concerns/workhorse_authorization.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/controllers/groups/settings/access_tokens_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/controllers/ldap/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/controllers/oauth/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/controllers/omniauth_callbacks_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/controllers/passwords_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/controllers/profiles/passwords_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/controllers/profiles/personal_access_tokens_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/controllers/profiles/two_factor_auths_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/controllers/profiles/webauthn_registrations_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/controllers/projects/settings/access_tokens_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/controllers/sessions_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/finders/groups/projects_requiring_authorizations_refresh/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/finders/personal_access_tokens_finder.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/helpers/access_tokens_helper.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/helpers/auth_helper.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/models/authentication_event.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/models/concerns/admin_changed_password_notifier.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/models/concerns/mirror_authentication.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/models/concerns/select_for_project_authorization.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/models/concerns/token_authenticatable.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/models/concerns/token_authenticatable_strategies/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/models/oauth_access_grant.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/models/oauth_access_token.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/models/personal_access_token.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/models/project_authorization.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/models/token_with_iv.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/models/webauthn_registration.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/policies/personal_access_token_policy.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/serializers/access_token_entity_base.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/serializers/group_access_token_entity.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/serializers/group_access_token_serializer.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/serializers/impersonation_access_token_entity.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/serializers/impersonation_access_token_serializer.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/serializers/personal_access_token_entity.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/serializers/personal_access_token_serializer.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/serializers/project_access_token_entity.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/serializers/project_access_token_serializer.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/services/access_token_validation_service.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/services/auth/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/services/authorized_project_update/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/services/chat_names/authorize_user_service.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/services/personal_access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/services/projects/move_project_authorizations_service.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/services/resource_access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/services/todos/destroy/unauthorized_features_service.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/services/users/authorized_build_service.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/services/users/authorized_create_service.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/services/users/email_verification/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/services/users/refresh_authorized_projects_service.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/services/webauthn/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/validators/json_schemas/cluster_agent_authorization_configuration.json @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/admin/application_settings/_external_authorization_service_form.html.haml @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/admin/impersonation_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/authentication/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/dashboard/projects/_zero_authorized_projects.html.haml @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/devise/mailer/password_change.html.haml @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/devise/mailer/password_change.text.erb @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/devise/mailer/password_change_by_admin.html.haml @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/devise/mailer/password_change_by_admin.text.erb @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/devise/mailer/reset_password_instructions.html.haml @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/devise/mailer/reset_password_instructions.text.erb @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/devise/**/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/doorkeeper/authorizations/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/doorkeeper/authorized_applications/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/errors/omniauth_error.html.haml @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/groups/settings/_resource_access_token_creation.html.haml @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/groups/settings/_two_factor_auth.html.haml @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/groups/settings/access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/layouts/oauth_error.html.haml @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/notify/access_token_about_to_expire_email.html.haml @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/notify/access_token_about_to_expire_email.text.erb @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/notify/access_token_created_email.html.haml @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/notify/access_token_created_email.text.erb @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/notify/access_token_expired_email.html.haml @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/notify/access_token_expired_email.text.erb @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/notify/access_token_revoked_email.html.haml @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/notify/access_token_revoked_email.text.erb @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/profiles/passwords/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/profiles/personal_access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/profiles/two_factor_auths/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/projects/mirrors/_authentication_method.html.haml @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/projects/settings/access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/shared/_no_password.html.haml @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/shared/_two_factor_auth_recovery_settings_check.html.haml @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/shared/access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/shared/members/_two_factor_auth_badge.html.haml @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/shared/tokens/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/workers/authorized_keys_worker.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/workers/authorized_project_update/ @gitlab-org/manage/authentication-and-authorization/approvers -/app/workers/authorized_projects_worker.rb @gitlab-org/manage/authentication-and-authorization/approvers -/app/workers/personal_access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers -/config/initializers/01_secret_token.rb @gitlab-org/manage/authentication-and-authorization/approvers -/config/initializers/devise_dynamic_password_length_validation.rb @gitlab-org/manage/authentication-and-authorization/approvers -/config/initializers/devise_password_length.rb.example @gitlab-org/manage/authentication-and-authorization/approvers -/config/initializers/gitlab_shell_secret_token.rb @gitlab-org/manage/authentication-and-authorization/approvers -/config/initializers/omniauth.rb @gitlab-org/manage/authentication-and-authorization/approvers -/config/initializers/rails_host_authorization.rb @gitlab-org/manage/authentication-and-authorization/approvers -/config/initializers/rails_host_authorization_gitpod.rb @gitlab-org/manage/authentication-and-authorization/approvers -/config/initializers/webauthn.rb @gitlab-org/manage/authentication-and-authorization/approvers -/config/initializers_before_autoloader/100_patch_omniauth_oauth2.rb @gitlab-org/manage/authentication-and-authorization/approvers -/config/initializers_before_autoloader/100_patch_omniauth_saml.rb @gitlab-org/manage/authentication-and-authorization/approvers -/config/weak_password_digests.yml @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/assets/javascripts/access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/assets/javascripts/audit_events/components/tokens/ @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/assets/javascripts/audit_events/token_utils.js @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/assets/javascripts/batch_comments/ @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/assets/javascripts/groups/settings/components/ @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/assets/javascripts/pages/admin/application_settings/general/components/ @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/assets/javascripts/pages/groups/omniauth_callbacks/ @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/assets/javascripts/pages/passwords/ @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/assets/javascripts/pages/profiles/passwords/ @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/assets/javascripts/password/ @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/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/vue_merge_request_widget/components/approvals/approvals_auth.vue @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/controllers/concerns/ee/authenticates_with_two_factor.rb @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/controllers/concerns/ee/enforces_two_factor_authentication.rb @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/controllers/concerns/saml_authorization.rb @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/controllers/ee/ldap/ @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/controllers/ee/omniauth_callbacks_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/controllers/ee/passwords_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/controllers/ee/sessions_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/controllers/groups/omniauth_callbacks_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/controllers/groups/scim_oauth_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/controllers/oauth/ @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/controllers/omniauth_kerberos_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/controllers/users/identity_verification_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/finders/auth/ @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/helpers/ee/access_tokens_helper.rb @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/helpers/ee/auth_helper.rb @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/helpers/ee/personal_access_tokens_helper.rb @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/models/concerns/identity_verifiable.rb @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/models/concerns/password_complexity.rb @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/models/ee/personal_access_token.rb @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/models/ee/project_authorization.rb @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/models/scim_oauth_access_token.rb @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/serializers/scim_oauth_access_token_entity.rb @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/services/arkose/token_verification_service.rb @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/services/ee/auth/ @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/services/ee/personal_access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/services/ee/resource_access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/services/ee/users/authorized_build_service.rb @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/services/personal_access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/services/security/token_revocation_service.rb @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/services/users/email_verification/ @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/validators/password/ @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/views/admin/application_settings/_personal_access_token_expiration_policy.html.haml @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/views/credentials_inventory_mailer/personal_access_token_revoked_email.html.haml @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/views/credentials_inventory_mailer/personal_access_token_revoked_email.text.haml @gitlab-org/manage/authentication-and-authorization/approvers -/app/views/devise/**/ @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/views/groups/_personal_access_token_expiration_policy.html.haml @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/views/groups/sso/_authorize_pane.html.haml @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/views/notify/policy_revoked_personal_access_tokens_email.html.haml @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/views/notify/policy_revoked_personal_access_tokens_email.text.erb @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/views/oauth/ @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/views/shared/_password_requirements_list.html.haml @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/views/shared/credentials_inventory/_personal_access_tokens.html.haml @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/views/shared/credentials_inventory/personal_access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/workers/auth/ @gitlab-org/manage/authentication-and-authorization/approvers -/ee/app/workers/personal_access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers -/ee/config/routes/oauth.rb @gitlab-org/manage/authentication-and-authorization/approvers -/ee/lib/ee/gitlab/auth/ @gitlab-org/manage/authentication-and-authorization/approvers -/ee/lib/ee/gitlab/omniauth_initializer.rb @gitlab-org/manage/authentication-and-authorization/approvers -/ee/lib/gitlab/auth/ @gitlab-org/manage/authentication-and-authorization/approvers -/ee/lib/gitlab/authority_analyzer.rb @gitlab-org/manage/authentication-and-authorization/approvers -/ee/lib/gitlab/geo/oauth/ @gitlab-org/manage/authentication-and-authorization/approvers -/ee/lib/gitlab/kerberos/ @gitlab-org/manage/authentication-and-authorization/approvers -/ee/lib/omni_auth/ @gitlab-org/manage/authentication-and-authorization/approvers -/ee/lib/system_check/geo/authorized_keys_check.rb @gitlab-org/manage/authentication-and-authorization/approvers -/ee/lib/system_check/geo/authorized_keys_flag_check.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/api/entities/impersonation_token.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/api/entities/impersonation_token_with_token.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/api/entities/personal_access_token.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/api/entities/personal_access_token_with_token.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/api/entities/resource_access_token.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/api/entities/resource_access_token_with_token.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/api/helpers/authentication.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/api/helpers/packages/basic_auth_helpers.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/api/helpers/personal_access_tokens_helpers.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/api/personal_access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers -/lib/api/personal_access_tokens.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/api/resource_access_tokens.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/api/support/token_with_expiration.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/gitlab/api_authentication/ @gitlab-org/manage/authentication-and-authorization/approvers -/lib/gitlab/auth/ @gitlab-org/manage/authentication-and-authorization/approvers -/lib/gitlab/auth.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/gitlab/auth_logger.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/gitlab/authorized_keys.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/gitlab/background_migration/encrypt_static_object_token.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/gitlab/background_migration/expire_o_auth_tokens.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/gitlab/background_migration/migrate_u2f_webauthn.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/gitlab/background_migration/update_users_where_two_factor_auth_required_from_group.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/gitlab/chat_name_token.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/gitlab/cleanup/personal_access_tokens.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/gitlab/external_authorization/ @gitlab-org/manage/authentication-and-authorization/approvers -/lib/gitlab/external_authorization.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/gitlab/grape_logging/loggers/token_logger.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/gitlab/graphql/authorize/ @gitlab-org/manage/authentication-and-authorization/approvers -/lib/gitlab/jwt_authenticatable.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/gitlab/jwt_token.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/gitlab/lfs_token.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/gitlab/mail_room/ @gitlab-org/manage/authentication-and-authorization/approvers -/lib/gitlab/omniauth_initializer.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/gitlab/project_authorizations.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/json_web_token/ @gitlab-org/manage/authentication-and-authorization/approvers -/lib/omni_auth/ @gitlab-org/manage/authentication-and-authorization/approvers -/lib/security/weak_passwords.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/system_check/app/authorized_keys_permission_check.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/system_check/incoming_email/imap_authentication_check.rb @gitlab-org/manage/authentication-and-authorization/approvers -/lib/tasks/gitlab/password.rake @gitlab-org/manage/authentication-and-authorization/approvers -/lib/tasks/tokens.rake @gitlab-org/manage/authentication-and-authorization/approvers +[Authentication and Authorization] @gitlab-org/manage/authentication-and-authorization/approvers +/app/assets/javascripts/access_tokens/ +/app/assets/javascripts/alerts_settings/graphql/mutations/reset_http_token.mutation.graphql +/app/assets/javascripts/authentication/ +/app/assets/javascripts/ide/components/shared/tokened_input.vue +/app/assets/javascripts/packages_and_registries/package_registry/components/list/tokens/ +/app/assets/javascripts/pages/admin/impersonation_tokens/ +/app/assets/javascripts/pages/groups/settings/access_tokens/ +/app/assets/javascripts/pages/ldap/ +/app/assets/javascripts/pages/oauth/ +/app/assets/javascripts/pages/omniauth_callbacks/ +/app/assets/javascripts/pages/profiles/password_prompt/ +/app/assets/javascripts/pages/profiles/personal_access_tokens/ +/app/assets/javascripts/pages/profiles/two_factor_auths/ +/app/assets/javascripts/pages/projects/settings/access_tokens/ +/app/assets/javascripts/pages/sessions/new/oauth_remember_me.js +/app/assets/javascripts/projects/settings/topics/components/ +/app/assets/javascripts/related_issues/components/issue_token.vue +/app/assets/stylesheets/page_bundles/profile_two_factor_auth.scss +/app/controllers/admin/impersonation_tokens_controller.rb +/app/controllers/concerns/access_tokens_actions.rb +/app/controllers/concerns/authenticates_with_two_factor.rb +/app/controllers/concerns/authenticates_with_two_factor_for_admin_mode.rb +/app/controllers/concerns/enforces_admin_authentication.rb +/app/controllers/concerns/enforces_two_factor_authentication.rb +/app/controllers/concerns/oauth_applications.rb +/app/controllers/concerns/project_unauthorized.rb +/app/models/concerns/require_email_verification.rb +/app/controllers/concerns/sessionless_authentication.rb +/app/controllers/concerns/snippet_authorizations.rb +/app/controllers/concerns/verifies_with_email.rb +/app/controllers/concerns/workhorse_authorization.rb +/app/controllers/groups/settings/access_tokens_controller.rb +/app/controllers/ldap/ +/app/controllers/oauth/ +/app/controllers/omniauth_callbacks_controller.rb +/app/controllers/passwords_controller.rb +/app/controllers/profiles/passwords_controller.rb +/app/controllers/profiles/personal_access_tokens_controller.rb +/app/controllers/profiles/two_factor_auths_controller.rb +/app/controllers/profiles/webauthn_registrations_controller.rb +/app/controllers/projects/settings/access_tokens_controller.rb +/app/controllers/sessions_controller.rb +/app/finders/groups/projects_requiring_authorizations_refresh/ +/app/finders/personal_access_tokens_finder.rb +/app/helpers/access_tokens_helper.rb +/app/helpers/auth_helper.rb +/app/models/authentication_event.rb +/app/models/concerns/admin_changed_password_notifier.rb +/app/models/concerns/mirror_authentication.rb +/app/models/concerns/select_for_project_authorization.rb +/app/models/concerns/token_authenticatable.rb +/app/models/concerns/token_authenticatable_strategies/ +/app/models/oauth_access_grant.rb +/app/models/oauth_access_token.rb +/app/models/personal_access_token.rb +/app/models/project_authorization.rb +/app/models/token_with_iv.rb +/app/models/webauthn_registration.rb +/app/policies/personal_access_token_policy.rb +/app/serializers/access_token_entity_base.rb +/app/serializers/group_access_token_entity.rb +/app/serializers/group_access_token_serializer.rb +/app/serializers/impersonation_access_token_entity.rb +/app/serializers/impersonation_access_token_serializer.rb +/app/serializers/personal_access_token_entity.rb +/app/serializers/personal_access_token_serializer.rb +/app/serializers/project_access_token_entity.rb +/app/serializers/project_access_token_serializer.rb +/app/services/access_token_validation_service.rb +/app/services/auth/ +/app/services/authorized_project_update/ +/app/services/chat_names/authorize_user_service.rb +/app/services/personal_access_tokens/ +/app/services/projects/move_project_authorizations_service.rb +/app/services/resource_access_tokens/ +/app/services/todos/destroy/unauthorized_features_service.rb +/app/services/users/authorized_build_service.rb +/app/services/users/authorized_create_service.rb +/app/services/users/email_verification/ +/app/services/users/refresh_authorized_projects_service.rb +/app/services/webauthn/ +/app/validators/json_schemas/cluster_agent_authorization_configuration.json +/app/views/admin/application_settings/_external_authorization_service_form.html.haml +/app/views/admin/impersonation_tokens/ +/app/views/authentication/ +/app/views/dashboard/projects/_zero_authorized_projects.html.haml +/app/views/devise/mailer/password_change.html.haml +/app/views/devise/mailer/password_change.text.erb +/app/views/devise/mailer/password_change_by_admin.html.haml +/app/views/devise/mailer/password_change_by_admin.text.erb +/app/views/devise/mailer/reset_password_instructions.html.haml +/app/views/devise/mailer/reset_password_instructions.text.erb +/app/views/devise/**/ +/app/views/doorkeeper/authorizations/ +/app/views/doorkeeper/authorized_applications/ +/app/views/errors/omniauth_error.html.haml +/app/views/groups/settings/_resource_access_token_creation.html.haml +/app/views/groups/settings/_two_factor_auth.html.haml +/app/views/groups/settings/access_tokens/ +/app/views/layouts/oauth_error.html.haml +/app/views/notify/access_token_about_to_expire_email.html.haml +/app/views/notify/access_token_about_to_expire_email.text.erb +/app/views/notify/access_token_created_email.html.haml +/app/views/notify/access_token_created_email.text.erb +/app/views/notify/access_token_expired_email.html.haml +/app/views/notify/access_token_expired_email.text.erb +/app/views/notify/access_token_revoked_email.html.haml +/app/views/notify/access_token_revoked_email.text.erb +/app/views/profiles/passwords/ +/app/views/profiles/personal_access_tokens/ +/app/views/profiles/two_factor_auths/ +/app/views/projects/mirrors/_authentication_method.html.haml +/app/views/projects/settings/access_tokens/ +/app/views/shared/_no_password.html.haml +/app/views/shared/_two_factor_auth_recovery_settings_check.html.haml +/app/views/shared/access_tokens/ +/app/views/shared/members/_two_factor_auth_badge.html.haml +/app/views/shared/tokens/ +/app/workers/authorized_keys_worker.rb +/app/workers/authorized_project_update/ +/app/workers/authorized_projects_worker.rb +/app/workers/personal_access_tokens/ +/config/initializers/01_secret_token.rb +/config/initializers/devise_dynamic_password_length_validation.rb +/config/initializers/devise_password_length.rb.example +/config/initializers/gitlab_shell_secret_token.rb +/config/initializers/omniauth.rb +/config/initializers/rails_host_authorization.rb +/config/initializers/rails_host_authorization_gitpod.rb +/config/initializers/webauthn.rb +/config/initializers_before_autoloader/100_patch_omniauth_oauth2.rb +/config/initializers_before_autoloader/100_patch_omniauth_saml.rb +/config/weak_password_digests.yml +/ee/app/assets/javascripts/access_tokens/ +/ee/app/assets/javascripts/audit_events/components/tokens/ +/ee/app/assets/javascripts/audit_events/token_utils.js +/ee/app/assets/javascripts/batch_comments/ +/ee/app/assets/javascripts/groups/settings/components/ +/ee/app/assets/javascripts/pages/admin/application_settings/general/components/ +/ee/app/assets/javascripts/pages/groups/omniauth_callbacks/ +/ee/app/assets/javascripts/pages/passwords/ +/ee/app/assets/javascripts/pages/profiles/passwords/ +/ee/app/assets/javascripts/password/ +/ee/app/assets/javascripts/requirements/components/tokens/ +/ee/app/assets/javascripts/saml_providers/scim_token_service.js +/ee/app/assets/javascripts/saml_sso/components/ +/ee/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_auth.vue +/ee/app/controllers/concerns/ee/authenticates_with_two_factor.rb +/ee/app/controllers/concerns/ee/enforces_two_factor_authentication.rb +/ee/app/controllers/concerns/saml_authorization.rb +/ee/app/controllers/ee/ldap/ +/ee/app/controllers/ee/omniauth_callbacks_controller.rb +/ee/app/controllers/ee/passwords_controller.rb +/ee/app/controllers/ee/sessions_controller.rb +/ee/app/controllers/groups/omniauth_callbacks_controller.rb +/ee/app/controllers/groups/scim_oauth_controller.rb +/ee/app/controllers/oauth/ +/ee/app/controllers/omniauth_kerberos_controller.rb +/ee/app/controllers/users/identity_verification_controller.rb +/ee/app/finders/auth/ +/ee/app/helpers/ee/access_tokens_helper.rb +/ee/app/helpers/ee/auth_helper.rb +/ee/app/helpers/ee/personal_access_tokens_helper.rb +/ee/app/models/concerns/identity_verifiable.rb +/ee/app/models/concerns/password_complexity.rb +/ee/app/models/ee/personal_access_token.rb +/ee/app/models/ee/project_authorization.rb +/ee/app/models/scim_oauth_access_token.rb +/ee/app/serializers/scim_oauth_access_token_entity.rb +/ee/app/services/arkose/token_verification_service.rb +/ee/app/services/ee/auth/ +/ee/app/services/ee/personal_access_tokens/ +/ee/app/services/ee/resource_access_tokens/ +/ee/app/services/ee/users/authorized_build_service.rb +/ee/app/services/personal_access_tokens/ +/ee/app/services/security/token_revocation_service.rb +/ee/app/services/users/email_verification/ +/ee/app/validators/password/ +/ee/app/views/admin/application_settings/_personal_access_token_expiration_policy.html.haml +/ee/app/views/credentials_inventory_mailer/personal_access_token_revoked_email.html.haml +/ee/app/views/credentials_inventory_mailer/personal_access_token_revoked_email.text.haml +/app/views/devise/**/ +/ee/app/views/groups/_personal_access_token_expiration_policy.html.haml +/ee/app/views/groups/sso/_authorize_pane.html.haml +/ee/app/views/notify/policy_revoked_personal_access_tokens_email.html.haml +/ee/app/views/notify/policy_revoked_personal_access_tokens_email.text.erb +/ee/app/views/oauth/ +/ee/app/views/shared/_password_requirements_list.html.haml +/ee/app/views/shared/credentials_inventory/_personal_access_tokens.html.haml +/ee/app/views/shared/credentials_inventory/personal_access_tokens/ +/ee/app/workers/auth/ +/ee/app/workers/personal_access_tokens/ +/ee/config/routes/oauth.rb +/ee/lib/ee/gitlab/auth/ +/ee/lib/ee/gitlab/omniauth_initializer.rb +/ee/lib/gitlab/auth/ +/ee/lib/gitlab/authority_analyzer.rb +/ee/lib/gitlab/geo/oauth/ +/ee/lib/gitlab/kerberos/ +/ee/lib/omni_auth/ +/ee/lib/system_check/geo/authorized_keys_check.rb +/ee/lib/system_check/geo/authorized_keys_flag_check.rb +/lib/api/entities/impersonation_token.rb +/lib/api/entities/impersonation_token_with_token.rb +/lib/api/entities/personal_access_token.rb +/lib/api/entities/personal_access_token_with_token.rb +/lib/api/entities/resource_access_token.rb +/lib/api/entities/resource_access_token_with_token.rb +/lib/api/helpers/authentication.rb +/lib/api/helpers/packages/basic_auth_helpers.rb +/lib/api/helpers/personal_access_tokens_helpers.rb +/lib/api/personal_access_tokens/ +/lib/api/personal_access_tokens.rb +/lib/api/resource_access_tokens.rb +/lib/api/support/token_with_expiration.rb +/lib/gitlab/api_authentication/ +/lib/gitlab/auth/ +/lib/gitlab/auth.rb +/lib/gitlab/auth_logger.rb +/lib/gitlab/authorized_keys.rb +/lib/gitlab/background_migration/encrypt_static_object_token.rb +/lib/gitlab/background_migration/expire_o_auth_tokens.rb +/lib/gitlab/background_migration/migrate_u2f_webauthn.rb +/lib/gitlab/background_migration/update_users_where_two_factor_auth_required_from_group.rb +/lib/gitlab/chat_name_token.rb +/lib/gitlab/cleanup/personal_access_tokens.rb +/lib/gitlab/external_authorization/ +/lib/gitlab/external_authorization.rb +/lib/gitlab/grape_logging/loggers/token_logger.rb +/lib/gitlab/graphql/authorize/ +/lib/gitlab/jwt_authenticatable.rb +/lib/gitlab/jwt_token.rb +/lib/gitlab/lfs_token.rb +/lib/gitlab/mail_room/ +/lib/gitlab/omniauth_initializer.rb +/lib/gitlab/project_authorizations.rb +/lib/json_web_token/ +/lib/omni_auth/ +/lib/security/weak_passwords.rb +/lib/system_check/app/authorized_keys_permission_check.rb +/lib/system_check/incoming_email/imap_authentication_check.rb +/lib/tasks/gitlab/password.rake +/lib/tasks/tokens.rake -[Verify] @gitlab-org/maintainers/cicd-verify @shinya.maeda @stanhu @ayufan -# Verify Backend +[Verify backend] @gitlab-org/maintainers/cicd-verify @shinya.maeda @stanhu @ayufan /**/app/**/ci/ /**/lib/**/ci/ /**/spec/**/ci/ @@ -1336,17 +1340,26 @@ lib/gitlab/checks/** @proglottis @toon /ee/spec/controllers/projects/subscriptions_controller_spec.rb /ee/spec/helpers/ee/projects/pipeline_helper_spec.rb /ee/spec/workers/clear_shared_runners_minutes_worker_spec.rb +# Overrides for Verify. These files below require approval from teams outside Verify. +/**/ci/reports/**/ @gitlab-org/maintainers/rails-backend +/**/ci/parsers/**/ @gitlab-org/maintainers/rails-backend +/ee/lib/gitlab/ci/parsers/license_compliance/ @gitlab-org/secure/composition-analysis-be +/ee/lib/gitlab/ci/parsers/security/ @gitlab-org/govern/threat-insights-backend-team +/ee/lib/gitlab/ci/reports/coverage_fuzzing/ @gitlab-org/secure/fuzzing-be +/ee/lib/gitlab/ci/reports/dependency_list/ @gitlab-org/secure/composition-analysis-be +/ee/lib/gitlab/ci/reports/license_scanning/ @gitlab-org/secure/composition-analysis-be +/ee/lib/gitlab/ci/reports/security/ @gitlab-org/govern/threat-insights-backend-team -# Verify Frontend -/**/spec/frontend/**/ci/ @gitlab-org/ci-cd/verify/frontend -/**/javascripts/ci/ @gitlab-org/ci-cd/verify/frontend -/**/javascripts/pipelines/ @gitlab-org/ci-cd/verify/frontend -/**/javascripts/jobs/ @gitlab-org/ci-cd/verify/frontend -/**/javascripts/token_access/ @gitlab-org/ci-cd/verify/frontend -/**/javascripts/admin/application_settings/runner_token_expiration/ @gitlab-org/ci-cd/verify/frontend -/**/javascripts/usage_quotas/pipelines/ @gitlab-org/ci-cd/verify/frontend @sheldonled @aalakkad @kpalchyk +[Verify frontend] @gitlab-org/ci-cd/verify/frontend +/**/spec/frontend/**/ci/ +/**/javascripts/ci/ +/**/javascripts/pipelines/ +/**/javascripts/jobs/ +/**/javascripts/token_access/ +/**/javascripts/admin/application_settings/runner_token_expiration/ +/**/javascripts/usage_quotas/pipelines/ @sheldonled @aalakkad @kpalchyk -# CI/CD templates +[CI/CD templates] /lib/gitlab/ci/templates/ @gitlab-org/maintainers/cicd-templates /lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml @DylanGriffith @mayra-cabrera @tkuah /lib/gitlab/ci/templates/Security/ @gonzoyumo @twoodham @sethgitlab @thiagocsf @@ -1366,93 +1379,83 @@ lib/gitlab/checks/** @proglottis @toon /lib/gitlab/ci/templates/Jobs/SAST.*.yml @gitlab-org/secure/static-analysis /lib/gitlab/ci/templates/Jobs/Secret-Detection.*.yml @gitlab-org/secure/static-analysis -# Overrides for Verify. These files below require approval from teams outside Verify. -/**/ci/reports/**/ @gitlab-org/maintainers/rails-backend -/**/ci/parsers/**/ @gitlab-org/maintainers/rails-backend -/ee/lib/gitlab/ci/parsers/license_compliance/ @gitlab-org/secure/composition-analysis-be -/ee/lib/gitlab/ci/parsers/security/ @gitlab-org/govern/threat-insights-backend-team -/ee/lib/gitlab/ci/reports/coverage_fuzzing/ @gitlab-org/secure/fuzzing-be -/ee/lib/gitlab/ci/reports/dependency_list/ @gitlab-org/secure/composition-analysis-be -/ee/lib/gitlab/ci/reports/license_scanning/ @gitlab-org/secure/composition-analysis-be -/ee/lib/gitlab/ci/reports/security/ @gitlab-org/govern/threat-insights-backend-team - -[Manage::Workspace] -lib/api/entities/basic_project_details.rb @gitlab-org/manage/manage-workspace/backend-approvers -lib/api/entities/project_with_access.rb @gitlab-org/manage/manage-workspace/backend-approvers -lib/api/entities/project_identity.rb @gitlab-org/manage/manage-workspace/backend-approvers -lib/api/entities/project.rb @gitlab-org/manage/manage-workspace/backend-approvers -ee/lib/ee/api/entities/project.rb @gitlab-org/manage/manage-workspace/backend-approvers +[Manage::Workspace] @gitlab-org/manage/manage-workspace/backend-approvers +lib/api/entities/basic_project_details.rb +lib/api/entities/project_with_access.rb +lib/api/entities/project_identity.rb +lib/api/entities/project.rb +ee/lib/ee/api/entities/project.rb -[Compliance] -/app/services/audit_events/build_service.rb @gitlab-org/govern/compliance -/ee/spec/services/audit_events/custom_audit_event_service_spec.rb @gitlab-org/govern/compliance -/app/models/audit_event.rb @gitlab-org/govern/compliance -/app/services/audit_event_service.rb @gitlab-org/govern/compliance -/app/services/concerns/audit_event_save_type.rb @gitlab-org/govern/compliance -/app/views/profiles/audit_log.html.haml @gitlab-org/govern/compliance -/ee/app/assets/javascripts/audit_events/components/audit_events_app.vue @gitlab-org/govern/compliance -/ee/app/assets/javascripts/audit_events/components/audit_events_export_button.vue @gitlab-org/govern/compliance -/ee/app/assets/javascripts/audit_events/components/audit_events_filter.vue @gitlab-org/govern/compliance -/ee/app/assets/javascripts/audit_events/components/audit_events_log.vue @gitlab-org/govern/compliance -/ee/app/assets/javascripts/audit_events/components/audit_events_stream.vue @gitlab-org/govern/compliance -/ee/app/assets/javascripts/audit_events/components/audit_events_table.vue @gitlab-org/govern/compliance -/ee/app/assets/javascripts/audit_events/components/tokens/shared/ @gitlab-org/govern/compliance -/ee/app/assets/javascripts/audit_events/init_audit_events.js @gitlab-org/govern/compliance -/ee/app/controllers/admin/audit_log_reports_controller.rb @gitlab-org/govern/compliance -/ee/app/controllers/admin/audit_logs_controller.rb @gitlab-org/govern/compliance -/ee/app/controllers/concerns/audit_events/audit_events_params.rb @gitlab-org/govern/compliance -/ee/app/controllers/groups/audit_events_controller.rb @gitlab-org/govern/compliance -/ee/app/controllers/projects/audit_events_controller.rb @gitlab-org/govern/compliance -/ee/app/finders/audit_event_finder.rb @gitlab-org/govern/compliance -/ee/app/graphql/types/audit_events/external_audit_event_destination_type.rb @gitlab-org/govern/compliance -/ee/app/helpers/audit_events_helper.rb @gitlab-org/govern/compliance -/ee/app/helpers/auditor_user_helper.rb @gitlab-org/govern/compliance -/ee/app/models/audit_events/external_audit_event_destination.rb @gitlab-org/govern/compliance -/ee/app/models/concerns/auditable.rb @gitlab-org/govern/compliance -/ee/app/models/ee/audit_event.rb @gitlab-org/govern/compliance -/ee/app/policies/audit_events/external_audit_event_destination_policy.rb @gitlab-org/govern/compliance -/ee/app/presenters/audit_event_presenter.rb @gitlab-org/govern/compliance -/ee/app/serializers/audit_event_entity.rb @gitlab-org/govern/compliance -/ee/app/serializers/audit_event_serializer.rb @gitlab-org/govern/compliance -/ee/app/services/ci/audit_variable_change_service.rb @gitlab-org/govern/compliance -/ee/app/services/ee/audit_event_service.rb @gitlab-org/govern/compliance -/ee/app/views/admin/users/_auditor_access_level_radio.html.haml @gitlab-org/govern/compliance -/ee/app/views/admin/users/_auditor_user_badge.html.haml @gitlab-org/govern/compliance -/ee/app/views/shared/icons/_icon_audit_events_purple.svg @gitlab-org/govern/compliance -/ee/app/views/shared/promotions/_promote_audit_events.html.haml @gitlab-org/govern/compliance -/ee/app/workers/audit_events/audit_event_streaming_worker.rb @gitlab-org/govern/compliance -/ee/config/events/1652263097_groups__audit_events__index_click_streams_tab.yml @gitlab-org/govern/compliance -/ee/config/events/202108302307_admin_audit_logs_index_click_date_range_button.yml @gitlab-org/govern/compliance -/ee/config/events/202108302307_groups__audit_events_controller_search_audit_event.yml @gitlab-org/govern/compliance -/ee/config/events/202108302307_profiles_controller_search_audit_event.yml @gitlab-org/govern/compliance -/ee/config/events/202108302307_projects__audit_events_controller_search_audit_event.yml @gitlab-org/govern/compliance -/ee/config/events/202111041910_admin__audit_logs_controller_search_audit_event.yml @gitlab-org/govern/compliance -/ee/config/metrics/counts_28d/20210216183930_g_compliance_audit_events_monthly.yml @gitlab-org/govern/compliance -/ee/config/metrics/counts_28d/20210216183934_i_compliance_audit_events_monthly.yml @gitlab-org/govern/compliance -/ee/config/metrics/counts_28d/20210216183942_a_compliance_audit_events_api_monthly.yml @gitlab-org/govern/compliance -/ee/config/metrics/counts_28d/20211130085433_g_manage_compliance_audit_event_destinations.yml @gitlab-org/govern/compliance -/ee/config/metrics/counts_7d/20210216183906_g_compliance_audit_events.yml @gitlab-org/govern/compliance -/ee/config/metrics/counts_7d/20210216183908_i_compliance_audit_events.yml @gitlab-org/govern/compliance -/ee/config/metrics/counts_7d/20210216183912_a_compliance_audit_events_api.yml @gitlab-org/govern/compliance -/ee/config/metrics/counts_7d/20210216183928_g_compliance_audit_events_weekly.yml @gitlab-org/govern/compliance -/ee/config/metrics/counts_7d/20210216183932_i_compliance_audit_events_weekly.yml @gitlab-org/govern/compliance -/ee/config/metrics/counts_7d/20210216183940_a_compliance_audit_events_api_weekly.yml @gitlab-org/govern/compliance -/ee/config/metrics/counts_all/20211130085433_g_manage_compliance_audit_event_destinations.yml @gitlab-org/govern/compliance -/ee/lib/api/audit_events.rb @gitlab-org/govern/compliance -/ee/lib/audit/ @gitlab-org/govern/compliance -/ee/lib/ee/api/entities/audit_event.rb @gitlab-org/govern/compliance -/ee/lib/ee/gitlab/audit/ @gitlab-org/govern/compliance -/lib/gitlab/audit/auditor.rb @gitlab-org/govern/compliance -/lib/gitlab/audit_json_logger.rb @gitlab-org/govern/compliance +[Compliance] @gitlab-org/govern/compliance +/app/services/audit_events/build_service.rb +/ee/spec/services/audit_events/custom_audit_event_service_spec.rb +/app/models/audit_event.rb +/app/services/audit_event_service.rb +/app/services/concerns/audit_event_save_type.rb +/app/views/profiles/audit_log.html.haml +/ee/app/assets/javascripts/audit_events/components/audit_events_app.vue +/ee/app/assets/javascripts/audit_events/components/audit_events_export_button.vue +/ee/app/assets/javascripts/audit_events/components/audit_events_filter.vue +/ee/app/assets/javascripts/audit_events/components/audit_events_log.vue +/ee/app/assets/javascripts/audit_events/components/audit_events_stream.vue +/ee/app/assets/javascripts/audit_events/components/audit_events_table.vue +/ee/app/assets/javascripts/audit_events/components/tokens/shared/ +/ee/app/assets/javascripts/audit_events/init_audit_events.js +/ee/app/controllers/admin/audit_log_reports_controller.rb +/ee/app/controllers/admin/audit_logs_controller.rb +/ee/app/controllers/concerns/audit_events/audit_events_params.rb +/ee/app/controllers/groups/audit_events_controller.rb +/ee/app/controllers/projects/audit_events_controller.rb +/ee/app/finders/audit_event_finder.rb +/ee/app/graphql/types/audit_events/external_audit_event_destination_type.rb +/ee/app/helpers/audit_events_helper.rb +/ee/app/helpers/auditor_user_helper.rb +/ee/app/models/audit_events/external_audit_event_destination.rb +/ee/app/models/concerns/auditable.rb +/ee/app/models/ee/audit_event.rb +/ee/app/policies/audit_events/external_audit_event_destination_policy.rb +/ee/app/presenters/audit_event_presenter.rb +/ee/app/serializers/audit_event_entity.rb +/ee/app/serializers/audit_event_serializer.rb +/ee/app/services/ci/audit_variable_change_service.rb +/ee/app/services/ee/audit_event_service.rb +/ee/app/views/admin/users/_auditor_access_level_radio.html.haml +/ee/app/views/admin/users/_auditor_user_badge.html.haml +/ee/app/views/shared/icons/_icon_audit_events_purple.svg +/ee/app/views/shared/promotions/_promote_audit_events.html.haml +/ee/app/workers/audit_events/audit_event_streaming_worker.rb +/ee/config/events/1652263097_groups__audit_events__index_click_streams_tab.yml +/ee/config/events/202108302307_admin_audit_logs_index_click_date_range_button.yml +/ee/config/events/202108302307_groups__audit_events_controller_search_audit_event.yml +/ee/config/events/202108302307_profiles_controller_search_audit_event.yml +/ee/config/events/202108302307_projects__audit_events_controller_search_audit_event.yml +/ee/config/events/202111041910_admin__audit_logs_controller_search_audit_event.yml +/ee/config/metrics/counts_28d/20210216183930_g_compliance_audit_events_monthly.yml +/ee/config/metrics/counts_28d/20210216183934_i_compliance_audit_events_monthly.yml +/ee/config/metrics/counts_28d/20210216183942_a_compliance_audit_events_api_monthly.yml +/ee/config/metrics/counts_28d/20211130085433_g_manage_compliance_audit_event_destinations.yml +/ee/config/metrics/counts_7d/20210216183906_g_compliance_audit_events.yml +/ee/config/metrics/counts_7d/20210216183908_i_compliance_audit_events.yml +/ee/config/metrics/counts_7d/20210216183912_a_compliance_audit_events_api.yml +/ee/config/metrics/counts_7d/20210216183928_g_compliance_audit_events_weekly.yml +/ee/config/metrics/counts_7d/20210216183932_i_compliance_audit_events_weekly.yml +/ee/config/metrics/counts_7d/20210216183940_a_compliance_audit_events_api_weekly.yml +/ee/config/metrics/counts_all/20211130085433_g_manage_compliance_audit_event_destinations.yml +/ee/lib/api/audit_events.rb +/ee/lib/audit/ +/ee/lib/ee/api/entities/audit_event.rb +/ee/lib/ee/gitlab/audit/ +/lib/gitlab/audit/auditor.rb +/lib/gitlab/audit_json_logger.rb -[Fulfillment::Utilization] -/ee/app/assets/javascripts/usage_quotas/components/ @sheldonled @aalakkad @kpalchyk -/ee/app/assets/javascripts/usage_quotas/seats/ @sheldonled @aalakkad @kpalchyk -/ee/app/assets/javascripts/usage_quotas/storage/ @sheldonled @aalakkad @kpalchyk +[Fulfillment::Utilization] @sheldonled @aalakkad @kpalchyk +/ee/app/assets/javascripts/usage_quotas/components/ +/ee/app/assets/javascripts/usage_quotas/seats/ +/ee/app/assets/javascripts/usage_quotas/storage/ -[Manage::Foundations] -/lib/sidebars/ @gitlab-org/manage/foundations/engineering -/ee/lib/sidebars/ @gitlab-org/manage/foundations/engineering +[Manage::Foundations] @gitlab-org/manage/foundations/engineering +/lib/sidebars/ +/ee/lib/sidebars/ # JiHu GitLab rules. See https://gitlab.com/gitlab-jh/gitlab-jh-enablement/-/issues/213#note_1024367528 diff --git a/.rubocop_todo/layout/argument_alignment.yml b/.rubocop_todo/layout/argument_alignment.yml index 15e8912cffb..bb17d0371dc 100644 --- a/.rubocop_todo/layout/argument_alignment.yml +++ b/.rubocop_todo/layout/argument_alignment.yml @@ -571,11 +571,6 @@ Layout/ArgumentAlignment: - 'app/models/webauthn_registration.rb' - 'app/models/wiki_page.rb' - 'app/models/work_item.rb' - - 'app/services/ci/archive_trace_service.rb' - - 'app/services/ci/ensure_stage_service.rb' - - 'app/services/ci/list_config_variables_service.rb' - - 'app/services/ci/parse_dotenv_artifact_service.rb' - - 'app/services/ci/stuck_builds/drop_helpers.rb' - 'app/services/compare_service.rb' - 'app/services/concerns/rate_limited_service.rb' - 'app/services/design_management/copy_design_collection/copy_service.rb' @@ -1465,11 +1460,6 @@ Layout/ArgumentAlignment: - 'ee/spec/services/audit_events/streaming/event_type_filters/destroy_service_spec.rb' - 'ee/spec/services/auto_merge/merge_train_service_spec.rb' - 'ee/spec/services/boards/lists/update_service_spec.rb' - - 'ee/spec/services/ci/process_pipeline_service_spec.rb' - - 'ee/spec/services/ci/retry_pipeline_service_spec.rb' - - 'ee/spec/services/ci/sync_reports_to_approval_rules_service_spec.rb' - - 'ee/spec/services/ci_cd/github_integration_setup_service_spec.rb' - - 'ee/spec/services/ci_cd/github_setup_service_spec.rb' - 'ee/spec/services/ee/boards/issues/create_service_spec.rb' - 'ee/spec/services/ee/boards/issues/list_service_spec.rb' - 'ee/spec/services/ee/boards/issues/move_service_spec.rb' @@ -2458,24 +2448,6 @@ Layout/ArgumentAlignment: - 'spec/services/award_emojis/destroy_service_spec.rb' - 'spec/services/bulk_imports/create_service_spec.rb' - 'spec/services/bulk_imports/get_importable_data_service_spec.rb' - - 'spec/services/ci/archive_trace_service_spec.rb' - - 'spec/services/ci/create_downstream_pipeline_service_spec.rb' - - 'spec/services/ci/create_pipeline_service/include_spec.rb' - - 'spec/services/ci/create_pipeline_service/logger_spec.rb' - - 'spec/services/ci/create_pipeline_service/merge_requests_spec.rb' - - 'spec/services/ci/create_pipeline_service/partitioning_spec.rb' - - 'spec/services/ci/create_pipeline_service/rate_limit_spec.rb' - - 'spec/services/ci/create_pipeline_service_spec.rb' - - 'spec/services/ci/job_artifacts/destroy_all_expired_service_spec.rb' - - 'spec/services/ci/job_artifacts/track_artifact_report_service_spec.rb' - - 'spec/services/ci/list_config_variables_service_spec.rb' - - 'spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb' - - 'spec/services/ci/play_build_service_spec.rb' - - 'spec/services/ci/play_manual_stage_service_spec.rb' - - 'spec/services/ci/process_sync_events_service_spec.rb' - - 'spec/services/ci/retry_job_service_spec.rb' - - 'spec/services/ci/retry_pipeline_service_spec.rb' - - 'spec/services/ci/run_scheduled_build_service_spec.rb' - 'spec/services/clusters/kubernetes/create_or_update_namespace_service_spec.rb' - 'spec/services/clusters/kubernetes/create_or_update_service_account_service_spec.rb' - 'spec/services/cohorts_service_spec.rb' diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 426ac482800..0f4b55a20b9 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -c751c4f269b472d5c35d59d869402a1efc227719 +3ce8f3a93ef7a00dfe5d65d92aa917a24e74e499 diff --git a/app/assets/javascripts/behaviors/shortcuts/keybindings.js b/app/assets/javascripts/behaviors/shortcuts/keybindings.js index cc8a9baf69e..f4facc39f4a 100644 --- a/app/assets/javascripts/behaviors/shortcuts/keybindings.js +++ b/app/assets/javascripts/behaviors/shortcuts/keybindings.js @@ -2,8 +2,11 @@ import { memoize } from 'lodash'; import AccessorUtilities from '~/lib/utils/accessor'; import { __ } from '~/locale'; -const isCustomizable = (command) => - 'customizable' in command ? Boolean(command.customizable) : true; +/** + * @param {object} command + * @param {boolean} [command.customizable] + */ +const isCustomizable = ({ customizable }) => Boolean(customizable ?? true); export const LOCAL_STORAGE_KEY = 'gl-keyboard-shortcuts-customizations'; @@ -183,7 +186,10 @@ export const TOGGLE_MARKDOWN_PREVIEW = { defaultKeys: ['ctrl+shift+p', 'command+shift+p'], }; -export const EDIT_RECENT_COMMENT = { +/** + * @keydown.up event is handled here: https://gitlab.com/gitlab-org/gitlab/-/blob/f3e807cdff5cf25765894163b4e92f8b2bcf8a68/app/assets/javascripts/notes/components/comment_form.vue#L379 + */ +const EDIT_RECENT_COMMENT = { id: 'editing.editRecentComment', description: __('Edit your most recent comment in a thread (from an empty textarea)'), defaultKeys: ['up'], @@ -472,13 +478,22 @@ export const ISSUE_CLOSE_DESIGN = { defaultKeys: ['esc'], }; -export const WEB_IDE_GO_TO_FILE = { +/** + * Legacy Web IDE uses the same shortcuts as MR_GO_TO_FILE, from this shared component: + * https://gitlab.com/gitlab-org/gitlab/-/blob/f3e807cdff5cf25765894163b4e92f8b2bcf8a68/app/assets/javascripts/vue_shared/components/file_finder/index.vue#L6 + */ +const WEB_IDE_GO_TO_FILE = { id: 'webIDE.goToFile', description: __('Go to file'), - defaultKeys: ['mod+p'], + defaultKeys: ['mod+p', 't'], + customizable: false /* customize MR_GO_TO_FILE instead */, }; -export const WEB_IDE_COMMIT = { +/** + * Legacy Web IDE uses @keydown.ctrl.enter and @keydown.meta.enter events here: + * https://gitlab.com/gitlab-org/gitlab/-/blob/f3e807cdff5cf25765894163b4e92f8b2bcf8a68/app/assets/javascripts/ide/components/shared/commit_message_field.vue#L131-132 + */ +const WEB_IDE_COMMIT = { id: 'webIDE.commit', description: __('Commit (when editing commit message)'), defaultKeys: ['mod+enter'], @@ -489,39 +504,28 @@ export const METRICS_EXPAND_PANEL = { id: 'metrics.expandPanel', description: __('Expand panel'), defaultKeys: ['e'], - customizable: false, -}; - -export const METRICS_VIEW_LOGS = { - id: 'metrics.viewLogs', - description: __('View logs'), - defaultKeys: ['l'], - customizable: false, }; export const METRICS_DOWNLOAD_CSV = { id: 'metrics.downloadCSV', description: __('Download CSV'), defaultKeys: ['d'], - customizable: false, }; export const METRICS_COPY_LINK_TO_CHART = { id: 'metrics.copyLinkToChart', description: __('Copy link to chart'), defaultKeys: ['c'], - customizable: false, }; export const METRICS_SHOW_ALERTS = { id: 'metrics.showAlerts', description: __('Alerts'), defaultKeys: ['a'], - customizable: false, }; // All keybinding groups -export const GLOBAL_SHORTCUTS_GROUP = { +const GLOBAL_SHORTCUTS_GROUP = { id: 'globalShortcuts', name: __('Global Shortcuts'), keybindings: [ @@ -559,13 +563,13 @@ export const EDITING_SHORTCUTS_GROUP = { ], }; -export const WIKI_SHORTCUTS_GROUP = { +const WIKI_SHORTCUTS_GROUP = { id: 'wiki', name: __('Wiki'), keybindings: [EDIT_WIKI_PAGE], }; -export const REPOSITORY_GRAPH_SHORTCUTS_GROUP = { +const REPOSITORY_GRAPH_SHORTCUTS_GROUP = { id: 'repositoryGraph', name: __('Repository Graph'), keybindings: [ @@ -578,7 +582,7 @@ export const REPOSITORY_GRAPH_SHORTCUTS_GROUP = { ], }; -export const PROJECT_SHORTCUTS_GROUP = { +const PROJECT_SHORTCUTS_GROUP = { id: 'project', name: __('Project'), keybindings: [ @@ -604,7 +608,7 @@ export const PROJECT_SHORTCUTS_GROUP = { ], }; -export const PROJECT_FILES_SHORTCUTS_GROUP = { +const PROJECT_FILES_SHORTCUTS_GROUP = { id: 'projectFiles', name: __('Project Files'), keybindings: [ @@ -616,19 +620,19 @@ export const PROJECT_FILES_SHORTCUTS_GROUP = { ], }; -export const ISSUABLE_SHORTCUTS_GROUP = { +const ISSUABLE_SHORTCUTS_GROUP = { id: 'issuables', name: __('Epics, issues, and merge requests'), keybindings: [ISSUABLE_COMMENT_OR_REPLY, ISSUABLE_EDIT_DESCRIPTION, ISSUABLE_CHANGE_LABEL], }; -export const ISSUE_MR_SHORTCUTS_GROUP = { +const ISSUE_MR_SHORTCUTS_GROUP = { id: 'issuesMRs', name: __('Issues and merge requests'), keybindings: [ISSUE_MR_CHANGE_ASSIGNEE, ISSUE_MR_CHANGE_MILESTONE], }; -export const MR_SHORTCUTS_GROUP = { +const MR_SHORTCUTS_GROUP = { id: 'mergeRequests', name: __('Merge requests'), keybindings: [ @@ -641,30 +645,29 @@ export const MR_SHORTCUTS_GROUP = { ], }; -export const MR_COMMITS_SHORTCUTS_GROUP = { +const MR_COMMITS_SHORTCUTS_GROUP = { id: 'mergeRequestCommits', name: __('Merge request commits'), keybindings: [MR_COMMITS_NEXT_COMMIT, MR_COMMITS_PREVIOUS_COMMIT], }; -export const ISSUES_SHORTCUTS_GROUP = { +const ISSUES_SHORTCUTS_GROUP = { id: 'issues', name: __('Issues'), keybindings: [ISSUE_NEXT_DESIGN, ISSUE_PREVIOUS_DESIGN, ISSUE_CLOSE_DESIGN], }; -export const WEB_IDE_SHORTCUTS_GROUP = { +const WEB_IDE_SHORTCUTS_GROUP = { id: 'webIDE', - name: __('Web IDE'), + name: __('Legacy Web IDE'), keybindings: [WEB_IDE_GO_TO_FILE, WEB_IDE_COMMIT], }; -export const METRICS_SHORTCUTS_GROUP = { +const METRICS_SHORTCUTS_GROUP = { id: 'metrics', name: __('Metrics'), keybindings: [ METRICS_EXPAND_PANEL, - METRICS_VIEW_LOGS, METRICS_DOWNLOAD_CSV, METRICS_COPY_LINK_TO_CHART, METRICS_SHOW_ALERTS, @@ -700,6 +703,9 @@ export const keybindingGroups = [ * * @param {Object} command The command object. All command objects are * available as imports from this file. + * @param {string} command.id + * @param {string[]} command.defaultKeys + * @param {boolean} [command.customizable] * * @returns {string[]} An array of keyboard shortcut strings bound to the command * diff --git a/app/assets/javascripts/ci/runner/project_new_runner/project_new_runner_app.vue b/app/assets/javascripts/ci/runner/project_new_runner/project_new_runner_app.vue index abc2eca38f4..821e002581c 100644 --- a/app/assets/javascripts/ci/runner/project_new_runner/project_new_runner_app.vue +++ b/app/assets/javascripts/ci/runner/project_new_runner/project_new_runner_app.vue @@ -1,9 +1,11 @@ <script> import { createAlert, VARIANT_SUCCESS } from '~/alert'; +import { redirectTo, setUrlParams } from '~/lib/utils/url_utility'; import { s__ } from '~/locale'; import RunnerPlatformsRadioGroup from '~/ci/runner/components/runner_platforms_radio_group.vue'; import RunnerCreateForm from '~/ci/runner/components/runner_create_form.vue'; -import { DEFAULT_PLATFORM, PROJECT_TYPE } from '../constants'; +import { DEFAULT_PLATFORM, PARAM_KEY_PLATFORM, PROJECT_TYPE } from '../constants'; +import { saveAlertToLocalStorage } from '../local_storage_alert/save_alert_to_local_storage'; export default { name: 'ProjectNewRunnerApp', @@ -23,11 +25,15 @@ export default { }; }, methods: { - onSaved() { - createAlert({ + onSaved(runner) { + const params = { [PARAM_KEY_PLATFORM]: this.platform }; + const ephemeralRegisterUrl = setUrlParams(params, runner.ephemeralRegisterUrl); + + saveAlertToLocalStorage({ message: s__('Runners|Runner created.'), variant: VARIANT_SUCCESS, }); + redirectTo(ephemeralRegisterUrl); }, onError(error) { createAlert({ message: error.message }); diff --git a/app/assets/javascripts/ci/runner/project_register_runner/index.js b/app/assets/javascripts/ci/runner/project_register_runner/index.js new file mode 100644 index 00000000000..63e88359ee7 --- /dev/null +++ b/app/assets/javascripts/ci/runner/project_register_runner/index.js @@ -0,0 +1,36 @@ +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import createDefaultClient from '~/lib/graphql'; +import { showAlertFromLocalStorage } from '../local_storage_alert/show_alert_from_local_storage'; +import ProjectRegisterRunnerApp from './project_register_runner_app.vue'; + +Vue.use(VueApollo); + +export const initProjectRegisterRunner = (selector = '#js-project-register-runner') => { + showAlertFromLocalStorage(); + + const el = document.querySelector(selector); + + if (!el) { + return null; + } + + const { runnerId, runnersPath } = el.dataset; + + const apolloProvider = new VueApollo({ + defaultClient: createDefaultClient(), + }); + + return new Vue({ + el, + apolloProvider, + render(h) { + return h(ProjectRegisterRunnerApp, { + props: { + runnerId, + runnersPath, + }, + }); + }, + }); +}; diff --git a/app/assets/javascripts/ci/runner/project_register_runner/project_register_runner_app.vue b/app/assets/javascripts/ci/runner/project_register_runner/project_register_runner_app.vue new file mode 100644 index 00000000000..b3fad595c7e --- /dev/null +++ b/app/assets/javascripts/ci/runner/project_register_runner/project_register_runner_app.vue @@ -0,0 +1,69 @@ +<script> +import { GlButton } from '@gitlab/ui'; +import { getParameterByName, updateHistory, mergeUrlParams } from '~/lib/utils/url_utility'; +import { PARAM_KEY_PLATFORM, DEFAULT_PLATFORM } from '../constants'; +import RegistrationInstructions from '../components/registration/registration_instructions.vue'; +import PlatformsDrawer from '../components/registration/platforms_drawer.vue'; + +export default { + name: 'ProjectRegisterRunnerApp', + components: { + GlButton, + RegistrationInstructions, + PlatformsDrawer, + }, + props: { + runnerId: { + type: String, + required: true, + }, + runnersPath: { + type: String, + required: true, + }, + }, + data() { + return { + platform: getParameterByName(PARAM_KEY_PLATFORM) || DEFAULT_PLATFORM, + isDrawerOpen: false, + }; + }, + watch: { + platform(platform) { + updateHistory({ + url: mergeUrlParams({ [PARAM_KEY_PLATFORM]: platform }, window.location.href), + }); + }, + }, + methods: { + onSelectPlatform(platform) { + this.platform = platform; + }, + onToggleDrawer(val = !this.isDrawerOpen) { + this.isDrawerOpen = val; + }, + }, +}; +</script> +<template> + <div> + <registration-instructions + :runner-id="runnerId" + :platform="platform" + @toggleDrawer="onToggleDrawer" + > + <template #runner-list-name>{{ s__('Runners|Project › CI/CD Settings › Runners') }}</template> + </registration-instructions> + + <platforms-drawer + :platform="platform" + :open="isDrawerOpen" + @selectPlatform="onSelectPlatform" + @close="onToggleDrawer(false)" + /> + + <gl-button :href="runnersPath" variant="confirm">{{ + s__('Runners|Go to runners page') + }}</gl-button> + </div> +</template> diff --git a/app/assets/javascripts/environments/components/kubernetes_overview.vue b/app/assets/javascripts/environments/components/kubernetes_overview.vue index 736eaa7062d..41abfcf6dc8 100644 --- a/app/assets/javascripts/environments/components/kubernetes_overview.vue +++ b/app/assets/javascripts/environments/components/kubernetes_overview.vue @@ -5,6 +5,7 @@ import csrf from '~/lib/utils/csrf'; import { getIdFromGraphQLId, isGid } from '~/graphql_shared/utils'; import KubernetesAgentInfo from './kubernetes_agent_info.vue'; import KubernetesPods from './kubernetes_pods.vue'; +import KubernetesTabs from './kubernetes_tabs.vue'; export default { components: { @@ -13,6 +14,7 @@ export default { GlAlert, KubernetesAgentInfo, KubernetesPods, + KubernetesTabs, }, inject: ['kasTunnelUrl'], props: { @@ -103,6 +105,10 @@ export default { :configuration="k8sAccessConfiguration" :namespace="namespace" class="gl-mb-5" + @cluster-error="onClusterError" /> + <kubernetes-tabs + :configuration="k8sAccessConfiguration" + class="gl-mb-5" @cluster-error="onClusterError" /></template> </gl-collapse> diff --git a/app/assets/javascripts/environments/components/kubernetes_tabs.vue b/app/assets/javascripts/environments/components/kubernetes_tabs.vue new file mode 100644 index 00000000000..b1eb92a4049 --- /dev/null +++ b/app/assets/javascripts/environments/components/kubernetes_tabs.vue @@ -0,0 +1,158 @@ +<script> +import { GlTabs, GlTab, GlLoadingIcon, GlBadge, GlTable, GlPagination } from '@gitlab/ui'; +import { __, s__ } from '~/locale'; +import k8sServicesQuery from '../graphql/queries/k8s_services.query.graphql'; +import { generateServicePortsString, getServiceAge } from '../helpers/k8s_integration_helper'; +import { SERVICES_LIMIT_PER_PAGE } from '../constants'; + +const tableHeadingClasses = 'gl-bg-gray-50! gl-font-weight-bold gl-white-space-nowrap'; + +export default { + components: { + GlTabs, + GlTab, + GlBadge, + GlTable, + GlPagination, + GlLoadingIcon, + }, + apollo: { + k8sServices: { + query: k8sServicesQuery, + variables() { + return { + configuration: this.configuration, + }; + }, + update(data) { + return data?.k8sServices || []; + }, + error(error) { + this.$emit('cluster-error', error); + }, + }, + }, + props: { + configuration: { + required: true, + type: Object, + }, + }, + data() { + return { + currentPage: 1, + }; + }, + computed: { + servicesItems() { + if (!this.k8sServices?.length) return []; + + return this.k8sServices.map((service) => { + return { + name: service?.metadata?.name, + namespace: service?.metadata?.namespace, + type: service?.spec?.type, + clusterIP: service?.spec?.clusterIP, + externalIP: service?.spec?.externalIP, + ports: generateServicePortsString(service?.spec?.ports), + age: getServiceAge(service?.metadata?.creationTimestamp), + }; + }); + }, + servicesLoading() { + return this.$apollo.queries.k8sServices.loading; + }, + showPagination() { + return this.servicesItems.length > SERVICES_LIMIT_PER_PAGE; + }, + prevPage() { + return Math.max(this.currentPage - 1, 0); + }, + nextPage() { + const nextPage = this.currentPage + 1; + return nextPage > Math.ceil(this.servicesItems.length / SERVICES_LIMIT_PER_PAGE) + ? null + : nextPage; + }, + }, + i18n: { + servicesTitle: s__('Environment|Services'), + name: __('Name'), + namespace: __('Namespace'), + status: __('Status'), + type: __('Type'), + clusterIP: s__('Environment|Cluster IP'), + externalIP: s__('Environment|External IP'), + ports: s__('Environment|Ports'), + age: s__('Environment|Age'), + }, + servicesFields: [ + { + key: 'name', + label: __('Name'), + thClass: tableHeadingClasses, + }, + { + key: 'namespace', + label: __('Namespace'), + thClass: tableHeadingClasses, + }, + { + key: 'type', + label: __('Type'), + thClass: tableHeadingClasses, + }, + { + key: 'clusterIP', + label: s__('Environment|Cluster IP'), + thClass: tableHeadingClasses, + }, + { + key: 'externalIP', + label: s__('Environment|External IP'), + thClass: tableHeadingClasses, + }, + { + key: 'ports', + label: s__('Environment|Ports'), + thClass: tableHeadingClasses, + }, + { + key: 'age', + label: s__('Environment|Age'), + thClass: tableHeadingClasses, + }, + ], + SERVICES_LIMIT_PER_PAGE, +}; +</script> +<template> + <gl-tabs> + <gl-tab> + <template #title> + {{ $options.i18n.servicesTitle }} + <gl-badge size="sm" class="gl-tab-counter-badge">{{ servicesItems.length }}</gl-badge> + </template> + + <gl-loading-icon v-if="servicesLoading" /> + + <gl-table + v-else + :fields="$options.servicesFields" + :items="servicesItems" + :per-page="$options.SERVICES_LIMIT_PER_PAGE" + :current-page="currentPage" + stacked="lg" + class="gl-bg-white! gl-mt-3" + /> + <gl-pagination + v-if="showPagination" + v-model="currentPage" + :prev-page="prevPage" + :next-page="nextPage" + align="center" + class="gl-mt-6" + /> + </gl-tab> + </gl-tabs> +</template> diff --git a/app/assets/javascripts/environments/constants.js b/app/assets/javascripts/environments/constants.js index 28424322dd2..e675a73ba7d 100644 --- a/app/assets/javascripts/environments/constants.js +++ b/app/assets/javascripts/environments/constants.js @@ -87,3 +87,5 @@ export const ENVIRONMENT_NEW_HELP_TEXT = __( ); export const ENVIRONMENT_EDIT_HELP_TEXT = ENVIRONMENT_NEW_HELP_TEXT; + +export const SERVICES_LIMIT_PER_PAGE = 10; diff --git a/app/assets/javascripts/environments/graphql/client.js b/app/assets/javascripts/environments/graphql/client.js index 0482741979b..bb6f57e7e80 100644 --- a/app/assets/javascripts/environments/graphql/client.js +++ b/app/assets/javascripts/environments/graphql/client.js @@ -6,6 +6,7 @@ import environmentToDeleteQuery from './queries/environment_to_delete.query.grap import environmentToRollbackQuery from './queries/environment_to_rollback.query.graphql'; import environmentToStopQuery from './queries/environment_to_stop.query.graphql'; import k8sPodsQuery from './queries/k8s_pods.query.graphql'; +import k8sServicesQuery from './queries/k8s_services.query.graphql'; import { resolvers } from './resolvers'; import typeDefs from './typedefs.graphql'; @@ -87,7 +88,23 @@ export const apolloProvider = (endpoint) => { query: k8sPodsQuery, data: { status: { - phase: '', + phase: null, + }, + }, + }); + cache.writeQuery({ + query: k8sServicesQuery, + data: { + metadata: { + name: null, + namespace: null, + creationTimestamp: null, + }, + spec: { + type: null, + clusterIP: null, + externalIP: null, + ports: [], }, }, }); diff --git a/app/assets/javascripts/environments/graphql/queries/k8s_pods.query.graphql b/app/assets/javascripts/environments/graphql/queries/k8s_pods.query.graphql index 818bca24d51..2d57ede8c15 100644 --- a/app/assets/javascripts/environments/graphql/queries/k8s_pods.query.graphql +++ b/app/assets/javascripts/environments/graphql/queries/k8s_pods.query.graphql @@ -1,4 +1,4 @@ -query getK8sPods($configuration: Object, $namespace: String) { +query getK8sPods($configuration: LocalConfiguration, $namespace: String) { k8sPods(configuration: $configuration, namespace: $namespace) @client { status { phase diff --git a/app/assets/javascripts/environments/graphql/queries/k8s_services.query.graphql b/app/assets/javascripts/environments/graphql/queries/k8s_services.query.graphql new file mode 100644 index 00000000000..d97849eecc1 --- /dev/null +++ b/app/assets/javascripts/environments/graphql/queries/k8s_services.query.graphql @@ -0,0 +1,15 @@ +query getK8sServices($configuration: LocalConfiguration) { + k8sServices(configuration: $configuration) @client { + metadata { + name + namespace + creationTimestamp + } + spec { + type + clusterIP + externalIP + ports + } + } +} diff --git a/app/assets/javascripts/environments/graphql/resolvers.js b/app/assets/javascripts/environments/graphql/resolvers.js index 013467e34be..8ebeeb92a53 100644 --- a/app/assets/javascripts/environments/graphql/resolvers.js +++ b/app/assets/javascripts/environments/graphql/resolvers.js @@ -85,6 +85,30 @@ export const resolvers = (endpoint) => ({ throw error; }); }, + k8sServices(_, { configuration }) { + const coreV1Api = new CoreV1Api(new Configuration(configuration)); + return coreV1Api + .listCoreV1ServiceForAllNamespaces() + .then((res) => { + const items = res?.data?.items || []; + return items.map((item) => { + const { type, clusterIP, externalIP, ports } = item.spec; + return { + metadata: item.metadata, + spec: { + type, + clusterIP: clusterIP || '-', + externalIP: externalIP || '-', + ports, + }, + }; + }); + }) + .catch((err) => { + const error = err?.response?.data?.message ? new Error(err.response.data.message) : err; + throw error; + }); + }, }, Mutation: { stopEnvironmentREST(_, { environment }, { client }) { diff --git a/app/assets/javascripts/environments/graphql/typedefs.graphql b/app/assets/javascripts/environments/graphql/typedefs.graphql index 4de271935d6..b85bf1e10d3 100644 --- a/app/assets/javascripts/environments/graphql/typedefs.graphql +++ b/app/assets/javascripts/environments/graphql/typedefs.graphql @@ -75,6 +75,24 @@ input LocalConfiguration { baseOptions: JSON } +type k8sServiceMetadata { + name: String + namespace: String + creationTimestamp: String +} + +type k8sServiceSpec { + type: String + clusterIP: String + externalIP: String + ports: JSON +} + +type LocalK8sServices { + metadata: k8sServiceMetadata + spec: k8sServiceSpec +} + extend type Query { environmentApp(page: Int, scope: String): LocalEnvironmentApp folder(environment: NestedLocalEnvironmentInput): LocalEnvironmentFolder @@ -85,6 +103,7 @@ extend type Query { isEnvironmentStopping(environment: LocalEnvironmentInput): Boolean isLastDeployment(environment: LocalEnvironmentInput): Boolean k8sPods(configuration: LocalConfiguration, namespace: String): [LocalK8sPods] + k8sServices(configuration: LocalConfiguration): [LocalK8sServices] } extend type Mutation { diff --git a/app/assets/javascripts/environments/helpers/k8s_integration_helper.js b/app/assets/javascripts/environments/helpers/k8s_integration_helper.js new file mode 100644 index 00000000000..6a94ac31fa1 --- /dev/null +++ b/app/assets/javascripts/environments/helpers/k8s_integration_helper.js @@ -0,0 +1,36 @@ +import { differenceInSeconds } from '~/lib/utils/datetime_utility'; + +export function generateServicePortsString(ports) { + if (!ports?.length) return ''; + + return ports + .map((port) => { + const nodePort = port.nodePort ? `:${port.nodePort}` : ''; + return `${port.port}${nodePort}/${port.protocol}`; + }) + .join(', '); +} + +export function getServiceAge(creationTimestamp) { + if (!creationTimestamp) return ''; + + const timeDifference = differenceInSeconds(new Date(creationTimestamp), new Date()); + + const seconds = Math.floor(timeDifference); + const minutes = Math.floor(seconds / 60) % 60; + const hours = Math.floor(seconds / 60 / 60) % 24; + const days = Math.floor(seconds / 60 / 60 / 24); + + let ageString; + if (days > 0) { + ageString = `${days}d`; + } else if (hours > 0) { + ageString = `${hours}h`; + } else if (minutes > 0) { + ageString = `${minutes}m`; + } else { + ageString = `${seconds}s`; + } + + return ageString; +} diff --git a/app/assets/javascripts/invite_members/components/invite_members_trigger.vue b/app/assets/javascripts/invite_members/components/invite_members_trigger.vue index 6d1a3ceba16..fadce6457bc 100644 --- a/app/assets/javascripts/invite_members/components/invite_members_trigger.vue +++ b/app/assets/javascripts/invite_members/components/invite_members_trigger.vue @@ -1,5 +1,5 @@ <script> -import { GlButton, GlLink, GlDropdownItem } from '@gitlab/ui'; +import { GlButton, GlLink, GlDropdownItem, GlDisclosureDropdownItem } from '@gitlab/ui'; import { s__ } from '~/locale'; import eventHub from '../event_hub'; import { @@ -7,10 +7,11 @@ import { TRIGGER_DEFAULT_QA_SELECTOR, TRIGGER_ELEMENT_WITH_EMOJI, TRIGGER_ELEMENT_DROPDOWN_WITH_EMOJI, + TRIGGER_ELEMENT_DISCLOSURE_DROPDOWN, } from '../constants'; export default { - components: { GlButton, GlLink, GlDropdownItem }, + components: { GlButton, GlLink, GlDropdownItem, GlDisclosureDropdownItem }, props: { displayText: { type: String, @@ -55,6 +56,9 @@ export default { 'data-test-id': 'invite-members-button', }; }, + item() { + return { text: this.displayText }; + }, }, methods: { checkTrigger(targetTriggerElement) { @@ -63,10 +67,15 @@ export default { openModal() { eventHub.$emit('openModal', { source: this.triggerSource }); }, + handleDisclosureDropdownAction() { + this.openModal(); + this.$emit('modal-opened'); + }, }, TRIGGER_ELEMENT_BUTTON, TRIGGER_ELEMENT_WITH_EMOJI, TRIGGER_ELEMENT_DROPDOWN_WITH_EMOJI, + TRIGGER_ELEMENT_DISCLOSURE_DROPDOWN, }; </script> @@ -97,6 +106,12 @@ export default { {{ displayText }} <gl-emoji class="gl-vertical-align-baseline gl-reset-font-size gl-mr-1" :data-name="icon" /> </gl-dropdown-item> + <gl-disclosure-dropdown-item + v-else-if="checkTrigger($options.TRIGGER_ELEMENT_DISCLOSURE_DROPDOWN)" + v-bind="componentAttributes" + :item="item" + @action="handleDisclosureDropdownAction" + /> <gl-link v-else v-bind="componentAttributes" data-is-link="true" @click="openModal"> {{ displayText }} </gl-link> diff --git a/app/assets/javascripts/invite_members/constants.js b/app/assets/javascripts/invite_members/constants.js index d5e9e498c6b..58457c80524 100644 --- a/app/assets/javascripts/invite_members/constants.js +++ b/app/assets/javascripts/invite_members/constants.js @@ -21,6 +21,7 @@ export const TRIGGER_ELEMENT_BUTTON = 'button'; export const TOP_NAV_INVITE_MEMBERS_COMPONENT = 'invite_members'; export const TRIGGER_ELEMENT_WITH_EMOJI = 'text-emoji'; export const TRIGGER_ELEMENT_DROPDOWN_WITH_EMOJI = 'dropdown-text-emoji'; +export const TRIGGER_ELEMENT_DISCLOSURE_DROPDOWN = 'dropdown-text'; export const INVITE_MEMBER_MODAL_TRACKING_CATEGORY = 'invite_members_modal'; export const TRIGGER_DEFAULT_QA_SELECTOR = 'invite_members_button'; export const MEMBERS_MODAL_DEFAULT_TITLE = s__('InviteMembersModal|Invite members'); diff --git a/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue b/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue index 970ab12cc85..b4a9b37d487 100644 --- a/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue +++ b/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue @@ -53,7 +53,7 @@ import { TOKEN_TYPE_TYPE, } from '~/vue_shared/components/filtered_search_bar/constants'; import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue'; -import { DEFAULT_PAGE_SIZE, IssuableListTabs } from '~/vue_shared/issuable/list/constants'; +import { DEFAULT_PAGE_SIZE, issuableListTabs } from '~/vue_shared/issuable/list/constants'; import getIssuesCountsQuery from '../queries/get_issues_counts.query.graphql'; import { AutocompleteCache } from '../utils'; @@ -67,7 +67,7 @@ const MilestoneToken = () => export default { i18n, - IssuableListTabs, + issuableListTabs, components: { GlDisclosureDropdown, GlEmptyState, @@ -457,7 +457,7 @@ export default { show-work-item-type-icon :sort-options="sortOptions" :tab-counts="tabCounts" - :tabs="$options.IssuableListTabs" + :tabs="$options.issuableListTabs" truncate-counts :url-params="urlParams" use-keyset-pagination diff --git a/app/assets/javascripts/issues/list/components/issues_list_app.vue b/app/assets/javascripts/issues/list/components/issues_list_app.vue index 5c4bf8f19e4..525daab41d5 100644 --- a/app/assets/javascripts/issues/list/components/issues_list_app.vue +++ b/app/assets/javascripts/issues/list/components/issues_list_app.vue @@ -52,7 +52,7 @@ import { TOKEN_TYPE_TYPE, } from '~/vue_shared/components/filtered_search_bar/constants'; import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue'; -import { DEFAULT_PAGE_SIZE, IssuableListTabs } from '~/vue_shared/issuable/list/constants'; +import { DEFAULT_PAGE_SIZE, issuableListTabs } from '~/vue_shared/issuable/list/constants'; import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import NewResourceDropdown from '~/vue_shared/components/new_resource_dropdown/new_resource_dropdown.vue'; import { @@ -108,7 +108,7 @@ const CrmOrganizationToken = () => export default { i18n, - IssuableListTabs, + issuableListTabs, components: { CsvImportExportButtons, EmptyStateWithAnyIssues, @@ -774,7 +774,7 @@ export default { :issuables="issues" :error="issuesError" label-filter-param="label_name" - :tabs="$options.IssuableListTabs" + :tabs="$options.issuableListTabs" :current-tab="state" :tab-counts="tabCounts" :truncate-counts="!isProject" diff --git a/app/assets/javascripts/ml/experiment_tracking/components/delete_button.vue b/app/assets/javascripts/ml/experiment_tracking/components/delete_button.vue index 4c0f99cf62c..c10b0cb52b9 100644 --- a/app/assets/javascripts/ml/experiment_tracking/components/delete_button.vue +++ b/app/assets/javascripts/ml/experiment_tracking/components/delete_button.vue @@ -1,9 +1,9 @@ <script> import { GlModal, - GlDropdown, + GlDisclosureDropdown, GlTooltipDirective, - GlDropdownItem, + GlDisclosureDropdownItem, GlModalDirective, } from '@gitlab/ui'; import { __ } from '~/locale'; @@ -12,8 +12,8 @@ import csrf from '~/lib/utils/csrf'; export default { components: { GlModal, - GlDropdown, - GlDropdownItem, + GlDisclosureDropdown, + GlDisclosureDropdownItem, }, directives: { GlTooltip: GlTooltipDirective, @@ -63,36 +63,42 @@ export default { </script> <template> - <gl-dropdown - right - category="tertiary" - :aria-label="__('More actions')" - icon="ellipsis_v" - no-caret - > - <gl-dropdown-item - v-gl-modal-directive="modal.id" - :aria-label="actionPrimaryText" - variant="danger" + <div> + <gl-disclosure-dropdown + placement="right" + category="tertiary" + :aria-label="__('More actions')" + icon="ellipsis_v" + no-caret > - {{ actionPrimaryText }} + <gl-disclosure-dropdown-item + v-gl-modal-directive="modal.id" + :aria-label="actionPrimaryText" + variant="danger" + > + <template #list-item> + <span class="gl-text-red-500"> + {{ actionPrimaryText }} + </span> + </template> + </gl-disclosure-dropdown-item> + </gl-disclosure-dropdown> - <form ref="deleteForm" method="post" :action="deletePath"> - <input type="hidden" name="_method" value="delete" /> - <input type="hidden" name="authenticity_token" :value="$options.csrf.token" /> - </form> + <form ref="deleteForm" method="post" :action="deletePath"> + <input type="hidden" name="_method" value="delete" /> + <input type="hidden" name="authenticity_token" :value="$options.csrf.token" /> + </form> - <gl-modal - :modal-id="modal.id" - :title="modalTitle" - :action-primary="modal.actionPrimary" - :action-cancel="modal.actionCancel" - @primary="confirmDelete" - > - <p> - {{ deleteConfirmationText }} - </p> - </gl-modal> - </gl-dropdown-item> - </gl-dropdown> + <gl-modal + :modal-id="modal.id" + :title="modalTitle" + :action-primary="modal.actionPrimary" + :action-cancel="modal.actionCancel" + @primary="confirmDelete" + > + <p> + {{ deleteConfirmationText }} + </p> + </gl-modal> + </div> </template> diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue index 100ef11409f..752ba4241d8 100644 --- a/app/assets/javascripts/monitoring/components/dashboard.vue +++ b/app/assets/javascripts/monitoring/components/dashboard.vue @@ -11,6 +11,13 @@ import { import VueDraggable from 'vuedraggable'; import { mapActions, mapState, mapGetters } from 'vuex'; import { createAlert } from '~/alert'; +import { + keysFor, + METRICS_COPY_LINK_TO_CHART, + METRICS_DOWNLOAD_CSV, + METRICS_EXPAND_PANEL, + METRICS_SHOW_ALERTS, +} from '~/behaviors/shortcuts/keybindings'; import invalidUrl from '~/lib/utils/invalid_url'; import { ESC_KEY } from '~/lib/utils/keys'; import { Mousetrap } from '~/lib/mousetrap'; @@ -18,7 +25,7 @@ import { mergeUrlParams, updateHistory } from '~/lib/utils/url_utility'; import { s__ } from '~/locale'; import { defaultTimeRange } from '~/vue_shared/constants'; import TrackEventDirective from '~/vue_shared/directives/track_event'; -import { metricStates, keyboardShortcutKeys } from '../constants'; +import { metricStates } from '../constants'; import { timeRangeFromUrl, panelToUrl, @@ -214,12 +221,28 @@ export default { created() { window.addEventListener('keyup', this.onKeyup); - Mousetrap.bind(Object.values(keyboardShortcutKeys), this.runShortcut); + Mousetrap.bind(keysFor(METRICS_EXPAND_PANEL), () => + this.runShortcut('onExpandFromKeyboardShortcut'), + ); + Mousetrap.bind(keysFor(METRICS_SHOW_ALERTS), () => + this.runShortcut('showAlertModalFromKeyboardShortcut'), + ); + Mousetrap.bind(keysFor(METRICS_DOWNLOAD_CSV), () => + this.runShortcut('downloadCsvFromKeyboardShortcut'), + ); + Mousetrap.bind(keysFor(METRICS_COPY_LINK_TO_CHART), () => + this.runShortcut('copyChartLinkFromKeyboardShotcut'), + ); }, destroyed() { window.removeEventListener('keyup', this.onKeyup); - Mousetrap.unbind(Object.values(keyboardShortcutKeys)); + [ + METRICS_COPY_LINK_TO_CHART, + METRICS_DOWNLOAD_CSV, + METRICS_EXPAND_PANEL, + METRICS_SHOW_ALERTS, + ].forEach((command) => Mousetrap.unbind(keysFor(command))); }, mounted() { if (!this.hasMetrics) { @@ -339,42 +362,18 @@ export default { }, /** * TODO: Investigate this to utilize the eventBus from Vue - * The intentation behind this cleanup is to allow for better tests + * The intention behind this cleanup is to allow for better tests * as well as use the correct eventBus facilities that are compatible * with Vue 3 * https://gitlab.com/gitlab-org/gitlab/-/issues/225583 */ // - runShortcut(e) { + runShortcut(actionToRun) { const panel = this.$refs[this.hoveredPanel]; if (!panel) return; const [panelInstance] = panel; - let actionToRun = ''; - - switch (e.key) { - case keyboardShortcutKeys.EXPAND: - actionToRun = 'onExpandFromKeyboardShortcut'; - break; - - case keyboardShortcutKeys.SHOW_ALERT: - actionToRun = 'showAlertModalFromKeyboardShortcut'; - break; - - case keyboardShortcutKeys.DOWNLOAD_CSV: - actionToRun = 'downloadCsvFromKeyboardShortcut'; - break; - - case keyboardShortcutKeys.CHART_COPY: - actionToRun = 'copyChartLinkFromKeyboardShotcut'; - break; - - default: - actionToRun = 'onExpandFromKeyboardShortcut'; - break; - } - panelInstance[actionToRun](); }, setHoveredPanel(groupKey, graphIndex) { diff --git a/app/assets/javascripts/monitoring/constants.js b/app/assets/javascripts/monitoring/constants.js index faef4b01c27..e35dcc350f2 100644 --- a/app/assets/javascripts/monitoring/constants.js +++ b/app/assets/javascripts/monitoring/constants.js @@ -256,19 +256,6 @@ export const VARIABLE_TYPES = { */ export const VARIABLE_PREFIX = 'var-'; -/** - * All of the actions inside each panel dropdown can be accessed - * via keyboard shortcuts than can be activated via mouse hovers - * and or focus via tabs. - */ - -export const keyboardShortcutKeys = { - EXPAND: 'e', - SHOW_ALERT: 'a', - DOWNLOAD_CSV: 'd', - CHART_COPY: 'c', -}; - export const thresholdModeTypes = { ABSOLUTE: 'absolute', PERCENTAGE: 'percentage', diff --git a/app/assets/javascripts/pages/projects/runners/register/index.js b/app/assets/javascripts/pages/projects/runners/register/index.js new file mode 100644 index 00000000000..a55ff95f84d --- /dev/null +++ b/app/assets/javascripts/pages/projects/runners/register/index.js @@ -0,0 +1,3 @@ +import { initProjectRegisterRunner } from '~/ci/runner/project_register_runner'; + +initProjectRegisterRunner(); diff --git a/app/assets/javascripts/super_sidebar/components/create_menu.vue b/app/assets/javascripts/super_sidebar/components/create_menu.vue index 4cff4642cf7..fa6056aff5e 100644 --- a/app/assets/javascripts/super_sidebar/components/create_menu.vue +++ b/app/assets/javascripts/super_sidebar/components/create_menu.vue @@ -1,6 +1,16 @@ <script> -import { GlDisclosureDropdown, GlTooltip } from '@gitlab/ui'; +import { + GlDisclosureDropdown, + GlTooltip, + GlDisclosureDropdownGroup, + GlDisclosureDropdownItem, +} from '@gitlab/ui'; +import InviteMembersTrigger from '~/invite_members/components/invite_members_trigger.vue'; import { __ } from '~/locale'; +import { + TOP_NAV_INVITE_MEMBERS_COMPONENT, + TRIGGER_ELEMENT_DISCLOSURE_DROPDOWN, +} from '~/invite_members/constants'; import { DROPDOWN_Y_OFFSET } from '../constants'; // Left offset required for the dropdown to be aligned with the super sidebar @@ -9,7 +19,10 @@ const DROPDOWN_X_OFFSET = -147; export default { components: { GlDisclosureDropdown, + GlDisclosureDropdownGroup, + GlDisclosureDropdownItem, GlTooltip, + InviteMembersTrigger, }, i18n: { createNew: __('Create new...'), @@ -25,6 +38,14 @@ export default { dropdownOpen: false, }; }, + methods: { + isInvitedMembers(groupItem) { + return groupItem.component === TOP_NAV_INVITE_MEMBERS_COMPONENT; + }, + closeAndFocus() { + this.$refs.dropdown.closeAndFocus(); + }, + }, toggleId: 'create-menu-toggle', popperOptions: { modifiers: [ @@ -36,24 +57,44 @@ export default { }, ], }, + TRIGGER_ELEMENT_DISCLOSURE_DROPDOWN, }; </script> <template> <div> <gl-disclosure-dropdown + ref="dropdown" category="tertiary" icon="plus" - :items="groups" no-caret text-sr-only :toggle-text="$options.i18n.createNew" :toggle-id="$options.toggleId" :popper-options="$options.popperOptions" data-qa-selector="new_menu_toggle" + data-testid="new-menu-toggle" @shown="dropdownOpen = true" @hidden="dropdownOpen = false" - /> + > + <gl-disclosure-dropdown-group + v-for="(group, index) in groups" + :key="group.name" + :bordered="index !== 0" + :group="group" + > + <template v-for="groupItem in group.items"> + <invite-members-trigger + v-if="isInvitedMembers(groupItem)" + :key="`${groupItem.text}-trigger`" + trigger-source="top-nav" + :trigger-element="$options.TRIGGER_ELEMENT_DISCLOSURE_DROPDOWN" + @modal-opened="closeAndFocus" + /> + <gl-disclosure-dropdown-item v-else :key="groupItem.text" :item="groupItem" /> + </template> + </gl-disclosure-dropdown-group> + </gl-disclosure-dropdown> <gl-tooltip v-if="!dropdownOpen" :target="`#${$options.toggleId}`" diff --git a/app/assets/javascripts/super_sidebar/components/help_center.vue b/app/assets/javascripts/super_sidebar/components/help_center.vue index a75d1fd6bff..70e1780aae1 100644 --- a/app/assets/javascripts/super_sidebar/components/help_center.vue +++ b/app/assets/javascripts/super_sidebar/components/help_center.vue @@ -12,7 +12,7 @@ import { PROMO_URL } from 'jh_else_ce/lib/utils/url_utility'; import { __ } from '~/locale'; import { STORAGE_KEY } from '~/whats_new/utils/notification'; import Tracking from '~/tracking'; -import { DROPDOWN_Y_OFFSET, HELP_MENU_TRACKING_DEFAULTS } from '../constants'; +import { DROPDOWN_Y_OFFSET, HELP_MENU_TRACKING_DEFAULTS, helpCenterState } from '../constants'; // Left offset required for the dropdown to be aligned with the super sidebar const DROPDOWN_X_OFFSET = -4; @@ -49,6 +49,7 @@ export default { data() { return { showWhatsNewNotification: this.shouldShowWhatsNewNotification(), + helpCenterState, }; }, computed: { @@ -175,9 +176,10 @@ export default { this.$refs.dropdown.close(); }, - async showTanukiBotChat() { - // This will be implemented in the following MR: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/117930 - return true; + showTanukiBotChat() { + this.$refs.dropdown.close(); + + this.helpCenterState.showTanukiBotChatDrawer = true; }, async showWhatsNew() { diff --git a/app/assets/javascripts/super_sidebar/constants.js b/app/assets/javascripts/super_sidebar/constants.js index 4f5b027c138..f3f71feaa8a 100644 --- a/app/assets/javascripts/super_sidebar/constants.js +++ b/app/assets/javascripts/super_sidebar/constants.js @@ -20,6 +20,10 @@ export const sidebarState = Vue.observable({ closePeekTimer: null, }); +export const helpCenterState = Vue.observable({ + showTanukiBotChatDrawer: false, +}); + export const MAX_FREQUENT_PROJECTS_COUNT = 5; export const MAX_FREQUENT_GROUPS_COUNT = 3; diff --git a/app/assets/javascripts/vue_shared/issuable/list/constants.js b/app/assets/javascripts/vue_shared/issuable/list/constants.js index 1b71819bdc2..7ece3b60bd5 100644 --- a/app/assets/javascripts/vue_shared/issuable/list/constants.js +++ b/app/assets/javascripts/vue_shared/issuable/list/constants.js @@ -1,7 +1,7 @@ import { STATUS_ALL, STATUS_CLOSED, STATUS_OPEN } from '~/issues/constants'; import { __ } from '~/locale'; -export const IssuableListTabs = [ +export const issuableListTabs = [ { id: 'state-opened', name: STATUS_OPEN, @@ -22,7 +22,7 @@ export const IssuableListTabs = [ }, ]; -export const AvailableSortOptions = [ +export const availableSortOptions = [ { id: 1, title: __('Created date'), diff --git a/app/assets/javascripts/work_items/components/work_item_links/work_item_links.vue b/app/assets/javascripts/work_items/components/work_item_links/work_item_links.vue index 3c999d166dc..c7ac0b71510 100644 --- a/app/assets/javascripts/work_items/components/work_item_links/work_item_links.vue +++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_links.vue @@ -5,7 +5,6 @@ import { s__ } from '~/locale'; import { convertToGraphQLId, getIdFromGraphQLId } from '~/graphql_shared/utils'; import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants'; import { TYPENAME_ISSUE, TYPENAME_WORK_ITEM } from '~/graphql_shared/constants'; -import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import getIssueDetailsQuery from 'ee_else_ce/work_items/graphql/get_issue_details.query.graphql'; import { isMetaKey } from '~/lib/utils/common_utils'; import { getParameterByName, setUrlParams, updateHistory } from '~/lib/utils/url_utility'; @@ -21,7 +20,6 @@ import getWorkItemLinksQuery from '../../graphql/work_item_links.query.graphql'; import addHierarchyChildMutation from '../../graphql/add_hierarchy_child.mutation.graphql'; import removeHierarchyChildMutation from '../../graphql/remove_hierarchy_child.mutation.graphql'; import updateWorkItemMutation from '../../graphql/update_work_item.mutation.graphql'; -import workItemQuery from '../../graphql/work_item.query.graphql'; import workItemByIidQuery from '../../graphql/work_item_by_iid.query.graphql'; import WidgetWrapper from '../widget_wrapper.vue'; import WorkItemDetailModal from '../work_item_detail_modal.vue'; @@ -43,14 +41,8 @@ export default { directives: { GlTooltip: GlTooltipDirective, }, - mixins: [glFeatureFlagMixin()], inject: ['projectPath', 'reportAbusePath'], props: { - workItemId: { - type: String, - required: false, - default: null, - }, issuableId: { type: Number, required: false, @@ -75,10 +67,8 @@ export default { this.error = e.message || this.$options.i18n.fetchError; }, async result() { - const { id, iid } = this.childUrlParams(); - this.activeChild = this.fetchByIid - ? this.children.find((child) => child.iid === iid) ?? {} - : this.children.find((child) => child.id === id) ?? {}; + const iid = getParameterByName('work_item_iid'); + this.activeChild = this.children.find((child) => child.iid === iid) ?? {}; await this.$nextTick(); if (!isEmpty(this.activeChild)) { this.$refs.modal.show(); @@ -147,31 +137,11 @@ export default { childrenCountLabel() { return this.isLoading && this.children.length === 0 ? '...' : this.children.length; }, - fetchByIid() { - return true; - }, }, mounted() { - if (!isEmpty(this.childUrlParams())) { - this.addWorkItemQuery(this.childUrlParams()); - } + this.addWorkItemQuery(getParameterByName('work_item_iid')); }, methods: { - childUrlParams() { - const params = {}; - if (this.fetchByIid) { - const iid = getParameterByName('work_item_iid'); - if (iid) { - params.iid = iid; - } - } else { - const workItemId = getParameterByName('work_item_id'); - if (workItemId) { - params.id = convertToGraphQLId(TYPENAME_WORK_ITEM, workItemId); - } - } - return params; - }, showAddForm(formType) { this.$refs.wrapper.show(); this.isShownAddForm = true; @@ -200,11 +170,8 @@ export default { this.removeHierarchyChild(child); this.activeToast = this.$toast.show(s__('WorkItem|Task deleted')); }, - updateWorkItemIdUrlQuery({ id, iid } = {}) { - const params = this.fetchByIid - ? { work_item_iid: iid } - : { work_item_id: getIdFromGraphQLId(id) }; - updateHistory({ url: setUrlParams(params), replace: true }); + updateWorkItemIdUrlQuery({ iid } = {}) { + updateHistory({ url: setUrlParams({ work_item_iid: iid }), replace: true }); }, async addHierarchyChild(workItem) { return this.$apollo.mutate({ @@ -251,31 +218,28 @@ export default { }); } }, - addWorkItemQuery({ id, iid }) { - const variables = this.fetchByIid - ? { - fullPath: this.projectPath, - iid, - } - : { - id, - }; + addWorkItemQuery(iid) { + if (!iid) { + return; + } + this.$apollo.addSmartQuery('prefetchedWorkItem', { - query() { - return this.fetchByIid ? workItemByIidQuery : workItemQuery; + query: workItemByIidQuery, + variables: { + fullPath: this.projectPath, + iid, }, - variables, update(data) { - return this.fetchByIid ? data.workspace.workItems.nodes[0] : data.workItem; + return data.workspace.workItems.nodes[0]; }, context: { isSingleRequest: true, }, }); }, - prefetchWorkItem({ id, iid }) { + prefetchWorkItem({ iid }) { this.prefetch = setTimeout( - () => this.addWorkItemQuery({ id, iid }), + () => this.addWorkItemQuery(iid), DEFAULT_DEBOUNCE_AND_THROTTLE_MS, ); }, diff --git a/app/assets/javascripts/work_items/components/work_item_links/work_item_tree.vue b/app/assets/javascripts/work_items/components/work_item_links/work_item_tree.vue index dd0d50861e4..06d6d62e603 100644 --- a/app/assets/javascripts/work_items/components/work_item_links/work_item_tree.vue +++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_tree.vue @@ -1,7 +1,6 @@ <script> import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants'; -import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; - +import { getParameterByName } from '~/lib/utils/url_utility'; import { FORM_TYPES, WIDGET_TYPE_HIERARCHY, @@ -10,7 +9,6 @@ import { WORK_ITEM_TYPE_ENUM_KEY_RESULT, WORK_ITEM_TYPE_VALUE_OBJECTIVE, } from '../../constants'; -import workItemQuery from '../../graphql/work_item.query.graphql'; import workItemByIidQuery from '../../graphql/work_item_by_iid.query.graphql'; import WidgetWrapper from '../widget_wrapper.vue'; import OkrActionsSplitButton from './okr_actions_split_button.vue'; @@ -28,7 +26,6 @@ export default { WorkItemLinksForm, WorkItemLinkChild, }, - mixins: [glFeatureFlagMixin()], props: { workItemType: { type: String, @@ -72,9 +69,6 @@ export default { }; }, computed: { - fetchByIid() { - return true; - }, childrenIds() { return this.children.map((c) => c.id); }, @@ -86,6 +80,9 @@ export default { .some((hierarchy) => hierarchy.hasChildren); }, }, + mounted() { + this.addWorkItemQuery(getParameterByName('work_item_iid')); + }, methods: { showAddForm(formType, childType) { this.$refs.wrapper.show(); @@ -99,10 +96,10 @@ export default { hideAddForm() { this.isShownAddForm = false; }, - prefetchWorkItem({ id, iid }) { + prefetchWorkItem({ iid }) { if (this.workItemType !== WORK_ITEM_TYPE_VALUE_OBJECTIVE) { this.prefetch = setTimeout( - () => this.addWorkItemQuery({ id, iid }), + () => this.addWorkItemQuery(iid), DEFAULT_DEBOUNCE_AND_THROTTLE_MS, ); } @@ -112,22 +109,19 @@ export default { clearTimeout(this.prefetch); } }, - addWorkItemQuery({ id, iid }) { - const variables = this.fetchByIid - ? { - fullPath: this.projectPath, - iid, - } - : { - id, - }; + addWorkItemQuery(iid) { + if (!iid) { + return; + } + this.$apollo.addSmartQuery('prefetchedWorkItem', { - query() { - return this.fetchByIid ? workItemByIidQuery : workItemQuery; + query: workItemByIidQuery, + variables: { + fullPath: this.projectPath, + iid, }, - variables, update(data) { - return this.fetchByIid ? data.workspace.workItems.nodes[0] : data.workItem; + return data.workspace.workItems.nodes[0]; }, context: { isSingleRequest: true, diff --git a/app/assets/javascripts/work_items/pages/create_work_item.vue b/app/assets/javascripts/work_items/pages/create_work_item.vue index 9f653fc8f71..49ec12db4e1 100644 --- a/app/assets/javascripts/work_items/pages/create_work_item.vue +++ b/app/assets/javascripts/work_items/pages/create_work_item.vue @@ -1,13 +1,12 @@ <script> import { GlButton, GlAlert, GlLoadingIcon, GlFormSelect } from '@gitlab/ui'; +import { TYPENAME_PROJECT } from '~/graphql_shared/constants'; import { getPreferredLocales, s__ } from '~/locale'; -import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { capitalizeFirstCharacter } from '~/lib/utils/text_utility'; -import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { sprintfWorkItem, I18N_WORK_ITEM_ERROR_CREATING } from '../constants'; import createWorkItemMutation from '../graphql/create_work_item.mutation.graphql'; import projectWorkItemTypesQuery from '../graphql/project_work_item_types.query.graphql'; -import { getWorkItemQuery } from '../utils'; +import workItemByIidQuery from '../graphql/work_item_by_iid.query.graphql'; import ItemTitle from '../components/item_title.vue'; @@ -22,7 +21,6 @@ export default { ItemTitle, GlFormSelect, }, - mixins: [glFeatureFlagMixin()], inject: ['fullPath'], props: { initialTitle: { @@ -73,9 +71,6 @@ export default { return sprintfWorkItem(I18N_WORK_ITEM_ERROR_CREATING, workItemType); }, - fetchByIid() { - return true; - }, }, methods: { async createWorkItem() { @@ -96,45 +91,31 @@ export default { }, update: (store, { data: { workItemCreate } }) => { const { workItem } = workItemCreate; - const data = this.fetchByIid - ? { - workspace: { - // eslint-disable-next-line @gitlab/require-i18n-strings - __typename: 'Project', - id: workItem.project.id, - workItems: { - __typename: 'WorkItemConnection', - nodes: [workItem], - }, - }, - } - : { workItem }; store.writeQuery({ - query: getWorkItemQuery(this.fetchByIid), - variables: this.fetchByIid - ? { - fullPath: this.fullPath, - iid: workItem.iid, - } - : { - id: workItem.id, + query: workItemByIidQuery, + variables: { + fullPath: this.fullPath, + iid: workItem.iid, + }, + data: { + workspace: { + __typename: TYPENAME_PROJECT, + id: workItem.project.id, + workItems: { + __typename: 'WorkItemConnection', + nodes: [workItem], }, - data, + }, + }, }); }, }); - const { - data: { - workItemCreate: { - workItem: { id, iid }, - }, - }, - } = response; - const routerParams = this.fetchByIid - ? { name: 'workItem', params: { id: iid } } - : { name: 'workItem', params: { id: `${getIdFromGraphQLId(id)}` } }; - this.$router.push(routerParams); + + this.$router.push({ + name: 'workItem', + params: { id: response.data.workItemCreate.workItem.iid }, + }); } catch { this.error = this.createErrorText; } diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index dba9cafbd71..82da6e959d0 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -886,6 +886,11 @@ Multi file editor $border-color-settings: #e1e1e1; /* +Drawers +*/ +$wide-drawer: 500px; + +/* Modals */ $modal-body-height: 80px; diff --git a/app/assets/stylesheets/utilities.scss b/app/assets/stylesheets/utilities.scss index e3c901d787c..267bd3f9506 100644 --- a/app/assets/stylesheets/utilities.scss +++ b/app/assets/stylesheets/utilities.scss @@ -145,3 +145,7 @@ width: $gl-spacing-scale-30; } } + +.gl-fill-orange-500 { + fill: $orange-500; +} diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index 53c6676b62b..727a4e0251d 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -50,6 +50,7 @@ class Projects::BlobController < Projects::ApplicationController before_action do push_frontend_feature_flag(:highlight_js, @project) push_frontend_feature_flag(:synchronize_fork, @project&.fork_source) + push_frontend_feature_flag(:explain_code_chat, current_user) push_licensed_feature(:file_locks) if @project.licensed_feature_available?(:file_locks) end diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb index 6e9a1b575ce..2b2c2cef8e2 100644 --- a/app/controllers/projects/runners_controller.rb +++ b/app/controllers/projects/runners_controller.rb @@ -2,8 +2,8 @@ class Projects::RunnersController < Projects::ApplicationController before_action :authorize_admin_build! - before_action :authorize_create_runner!, only: [:new] - before_action :runner, only: [:edit, :update, :destroy, :pause, :resume, :show] + before_action :authorize_create_runner!, only: [:new, :register] + before_action :runner, only: [:edit, :update, :destroy, :pause, :resume, :show, :register] feature_category :runner urgency :low @@ -24,7 +24,11 @@ class Projects::RunnersController < Projects::ApplicationController end def new - render_404 unless Feature.enabled?(:create_runner_workflow_for_namespace, project.namespace) + render_404 unless create_runner_workflow_for_namespace_enabled? + end + + def register + render_404 unless create_runner_workflow_for_namespace_enabled? && runner.registration_available? end def destroy @@ -80,4 +84,8 @@ class Projects::RunnersController < Projects::ApplicationController def runner_params params.require(:runner).permit(Ci::Runner::FORM_EDITABLE) end + + def create_runner_workflow_for_namespace_enabled? + Feature.enabled?(:create_runner_workflow_for_namespace, project.namespace) + end end diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb index 0631c02355e..495241df912 100644 --- a/app/controllers/projects/tree_controller.rb +++ b/app/controllers/projects/tree_controller.rb @@ -19,6 +19,7 @@ class Projects::TreeController < Projects::ApplicationController before_action do push_frontend_feature_flag(:highlight_js, @project) push_frontend_feature_flag(:synchronize_fork, @project.fork_source) + push_frontend_feature_flag(:explain_code_chat, current_user) push_licensed_feature(:file_locks) if @project.licensed_feature_available?(:file_locks) end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 0a6ac2a25a4..d1a86083ac0 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -40,6 +40,7 @@ class ProjectsController < Projects::ApplicationController push_frontend_feature_flag(:highlight_js, @project) push_frontend_feature_flag(:synchronize_fork, @project&.fork_source) push_frontend_feature_flag(:remove_monitor_metrics, @project) + push_frontend_feature_flag(:explain_code_chat, current_user) push_licensed_feature(:file_locks) if @project.present? && @project.licensed_feature_available?(:file_locks) push_licensed_feature(:security_orchestration_policies) if @project.present? && @project.licensed_feature_available?(:security_orchestration_policies) push_force_frontend_feature_flag(:work_items, @project&.work_items_feature_flag_enabled?) diff --git a/app/finders/deployments_finder.rb b/app/finders/deployments_finder.rb index 3d40da78dbc..2a9eb3b9b22 100644 --- a/app/finders/deployments_finder.rb +++ b/app/finders/deployments_finder.rb @@ -59,8 +59,8 @@ class DeploymentsFinder # Currently, the inefficient parameters are allowed in order to avoid breaking changes in Deployment API. # We'll switch to a hard error in https://gitlab.com/gitlab-org/gitlab/-/issues/328500. - if (filter_by_updated_at? && !order_by_updated_at?) || (!filter_by_updated_at? && order_by_updated_at?) - error = InefficientQueryError.new('`updated_at` filter and `updated_at` sorting must be paired') + if filter_by_updated_at? && !order_by_updated_at? + error = InefficientQueryError.new('`updated_at` filter requires `updated_at` sort') Gitlab::ErrorTracking.log_exception(error) diff --git a/app/graphql/types/ci/runner_type.rb b/app/graphql/types/ci/runner_type.rb index 20e8b506a3f..6367847a5a5 100644 --- a/app/graphql/types/ci/runner_type.rb +++ b/app/graphql/types/ci/runner_type.rb @@ -158,6 +158,8 @@ module Types Gitlab::Routing.url_helpers.register_admin_runner_url(runner) when 'group_type' Gitlab::Routing.url_helpers.register_group_runner_url(runner.groups[0], runner) + when 'project_type' + Gitlab::Routing.url_helpers.register_project_runner_url(runner.projects[0], runner) end end @@ -212,6 +214,10 @@ module Types group = runner.groups[0] group && context[:current_user]&.can?(:register_group_runners, group) + when 'project_type' + project = runner.projects[0] + + project && context[:current_user]&.can?(:register_project_runners, project) end end end diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb index fb906759ba4..20dce54d740 100644 --- a/app/graphql/types/query_type.rb +++ b/app/graphql/types/query_type.rb @@ -14,6 +14,13 @@ module Types null: true, description: 'CI related settings that apply to the entire instance.' field :ci_config, resolver: Resolvers::Ci::ConfigResolver, complexity: 126 # AUTHENTICATED_MAX_COMPLEXITY / 2 + 1 + + field :ci_pipeline_stage, ::Types::Ci::StageType, + null: true, description: 'Stage belonging to a CI pipeline.' do + argument :id, type: ::Types::GlobalIDType[::Ci::Stage], + required: true, description: 'Global ID of the CI stage.' + end + field :ci_variables, Types::Ci::InstanceVariableType.connection_type, null: true, @@ -202,6 +209,15 @@ module Types def query_complexity context.query end + + def ci_pipeline_stage(id:) + stage = ::Gitlab::Graphql::Lazy.force(GitlabSchema.find_by_gid(id)) + authorized = Ability.allowed?(current_user, :read_build, stage.project) + + return unless authorized + + stage + end end end diff --git a/app/helpers/sidebars_helper.rb b/app/helpers/sidebars_helper.rb index 4dfc65dd849..d249364a4de 100644 --- a/app/helpers/sidebars_helper.rb +++ b/app/helpers/sidebars_helper.rb @@ -155,18 +155,19 @@ module SidebarsHelper { name: show_headers ? section[:title] : '', items: section[:menu_items].map do |item| - { - text: item[:title], - href: item[:href], - extraAttrs: { - 'data-track-label': item[:id], - 'data-track-action': 'click_link', - 'data-track-property': 'nav_create_menu', - 'data-qa-selector': 'create_menu_item', - 'data-qa-create-menu-item': item[:id] - } - } - end + { + text: item[:title], + href: item[:href].presence, + component: item[:component].presence, + extraAttrs: { + 'data-track-label': item[:id], + 'data-track-action': 'click_link', + 'data-track-property': 'nav_create_menu', + 'data-qa-selector': 'create_menu_item', + 'data-qa-create-menu-item': item[:id] + } + } + end } end end diff --git a/app/models/ci/build_need.rb b/app/models/ci/build_need.rb index 03d1bd14bfb..940221619b3 100644 --- a/app/models/ci/build_need.rb +++ b/app/models/ci/build_need.rb @@ -6,8 +6,6 @@ module Ci include BulkInsertSafe include IgnorableColumns - ignore_column :id_convert_to_bigint, remove_with: '16.0', remove_after: '2023-04-22' - belongs_to :build, class_name: "Ci::Processable", foreign_key: :build_id, inverse_of: :needs partitionable scope: :build diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index 80a3d8df632..dc285a4268a 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -506,6 +506,10 @@ module Ci !runner_managers.any? end + def gitlab_hosted? + Gitlab.com? && instance_type? + end + private scope :with_upgrade_status, ->(upgrade_status) do diff --git a/app/services/ci/archive_trace_service.rb b/app/services/ci/archive_trace_service.rb index f2ace1f1590..e370f85fa96 100644 --- a/app/services/ci/archive_trace_service.rb +++ b/app/services/ci/archive_trace_service.rb @@ -67,21 +67,23 @@ module Ci def failed_archive_counter @failed_archive_counter ||= - Gitlab::Metrics.counter(:job_trace_archive_failed_total, - "Counter of failed attempts of trace archiving") + Gitlab::Metrics.counter(:job_trace_archive_failed_total, "Counter of failed attempts of trace archiving") end def archive_error(error, job, worker_name) failed_archive_counter.increment - Sidekiq.logger.warn(class: worker_name, - message: "Failed to archive trace. message: #{error.message}.", - job_id: job.id) - - Gitlab::ErrorTracking - .track_and_raise_for_dev_exception(error, - issue_url: 'https://gitlab.com/gitlab-org/gitlab-foss/issues/51502', - job_id: job.id) + Sidekiq.logger.warn( + class: worker_name, + message: "Failed to archive trace. message: #{error.message}.", + job_id: job.id + ) + + Gitlab::ErrorTracking.track_and_raise_for_dev_exception( + error, + issue_url: 'https://gitlab.com/gitlab-org/gitlab-foss/issues/51502', + job_id: job.id + ) end end end diff --git a/app/services/ci/ensure_stage_service.rb b/app/services/ci/ensure_stage_service.rb index cbb3a2e4709..9d5ccecbe33 100644 --- a/app/services/ci/ensure_stage_service.rb +++ b/app/services/ci/ensure_stage_service.rb @@ -45,10 +45,12 @@ module Ci # rubocop: enable CodeReuse/ActiveRecord def create_stage - Ci::Stage.create!(name: @build.stage, - position: @build.stage_idx, - pipeline: @build.pipeline, - project: @build.project) + Ci::Stage.create!( + name: @build.stage, + position: @build.stage_idx, + pipeline: @build.pipeline, + project: @build.project + ) end end end diff --git a/app/services/ci/list_config_variables_service.rb b/app/services/ci/list_config_variables_service.rb index dbea270b7c6..1020e98f463 100644 --- a/app/services/ci/list_config_variables_service.rb +++ b/app/services/ci/list_config_variables_service.rb @@ -28,9 +28,12 @@ module Ci return {} unless config.exists? - result = Gitlab::Ci::YamlProcessor.new(config.content, project: project, - user: current_user, - sha: sha).execute + result = Gitlab::Ci::YamlProcessor.new( + config.content, + project: project, + user: current_user, + sha: sha + ).execute result.valid? ? result.root_variables_with_prefill_data : {} end diff --git a/app/services/ci/parse_dotenv_artifact_service.rb b/app/services/ci/parse_dotenv_artifact_service.rb index d4d5acef44e..89a3c7d9e03 100644 --- a/app/services/ci/parse_dotenv_artifact_service.rb +++ b/app/services/ci/parse_dotenv_artifact_service.rb @@ -44,8 +44,13 @@ module Ci blob.each_line do |line| key, value = scan_line!(line) - variables[key] = Ci::JobVariable.new(job_id: artifact.job_id, - source: :dotenv, key: key, value: value, raw: false) + variables[key] = Ci::JobVariable.new( + job_id: artifact.job_id, + source: :dotenv, + key: key, + value: value, + raw: false + ) end end diff --git a/app/services/ci/stuck_builds/drop_helpers.rb b/app/services/ci/stuck_builds/drop_helpers.rb index f56c9aaeb55..4ce30a6068c 100644 --- a/app/services/ci/stuck_builds/drop_helpers.rb +++ b/app/services/ci/stuck_builds/drop_helpers.rb @@ -45,23 +45,26 @@ module Ci end def track_exception_for_build(ex, build) - Gitlab::ErrorTracking.track_exception(ex, - build_id: build.id, - build_name: build.name, - build_stage: build.stage_name, - pipeline_id: build.pipeline_id, - project_id: build.project_id + Gitlab::ErrorTracking.track_exception( + ex, + build_id: build.id, + build_name: build.name, + build_stage: build.stage_name, + pipeline_id: build.pipeline_id, + project_id: build.project_id ) end def log_dropping_message(type, build, reason) - Gitlab::AppLogger.info(class: self.class.name, - message: "Dropping #{type} build", - build_stuck_type: type, - build_id: build.id, - runner_id: build.runner_id, - build_status: build.status, - build_failure_reason: reason) + Gitlab::AppLogger.info( + class: self.class.name, + message: "Dropping #{type} build", + build_stuck_type: type, + build_id: build.id, + runner_id: build.runner_id, + build_status: build.status, + build_failure_reason: reason + ) end end end diff --git a/app/views/groups/runners/register.html.haml b/app/views/groups/runners/register.html.haml index fdee1675475..7e12aa3ab3f 100644 --- a/app/views/groups/runners/register.html.haml +++ b/app/views/groups/runners/register.html.haml @@ -1,6 +1,6 @@ - runner_name = "##{@runner.id} (#{@runner.short_sha})" - breadcrumb_title s_('Runners|Register') -- page_title s_('Runners|Register'), "##{@runner.id} (#{@runner.short_sha})" +- page_title s_('Runners|Register'), runner_name - add_to_breadcrumbs _('Runners'), group_runners_path(@group) - add_to_breadcrumbs runner_name, register_group_runner_path(@runner) diff --git a/app/views/projects/runners/register.html.haml b/app/views/projects/runners/register.html.haml new file mode 100644 index 00000000000..561a919323d --- /dev/null +++ b/app/views/projects/runners/register.html.haml @@ -0,0 +1,7 @@ +- runner_name = "##{@runner.id} (#{@runner.short_sha})" + +- add_to_breadcrumbs _('CI/CD Settings'), project_settings_ci_cd_path(@project) +- breadcrumb_title s_('Runners|Register runner') +- page_title s_('Runners|Register'), runner_name + +#js-project-register-runner{ data: { runner_id: @runner.id, runners_path: project_runners_path(@project) } } diff --git a/config/feature_flags/development/allow_dots_on_tf_state_names.yml b/config/feature_flags/development/allow_dots_on_tf_state_names.yml deleted file mode 100644 index f7a981d11db..00000000000 --- a/config/feature_flags/development/allow_dots_on_tf_state_names.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: allow_dots_on_tf_state_names -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106861 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/385597 -milestone: '15.7' -type: development -group: group::configure -default_enabled: true diff --git a/config/feature_flags/development/explain_code_chat.yml b/config/feature_flags/development/explain_code_chat.yml new file mode 100644 index 00000000000..6599c3015b4 --- /dev/null +++ b/config/feature_flags/development/explain_code_chat.yml @@ -0,0 +1,8 @@ +--- +name: explain_code_chat +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/117674 +rollout_issue_url: +milestone: '16.0' +type: development +group: group::source code +default_enabled: false diff --git a/config/feature_flags/development/synchronize_fork.yml b/config/feature_flags/development/synchronize_fork.yml index 46307136c33..207739cf34e 100644 --- a/config/feature_flags/development/synchronize_fork.yml +++ b/config/feature_flags/development/synchronize_fork.yml @@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/395793 milestone: '15.10' type: development group: group::source code -default_enabled: false +default_enabled: true diff --git a/config/routes/project.rb b/config/routes/project.rb index bb79b4ee89d..ef5f95eee75 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -106,6 +106,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do resources :runners, only: [:index, :new, :edit, :update, :destroy, :show] do member do + get :register post :resume post :pause end diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 57868455803..78a9a8a9e67 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -108,6 +108,18 @@ four standard [pagination arguments](#connection-pagination-arguments): | <a id="queryciminutesusagedate"></a>`date` | [`Date`](#date) | Date for which to retrieve the usage data, should be the first day of a month. | | <a id="queryciminutesusagenamespaceid"></a>`namespaceId` | [`NamespaceID`](#namespaceid) | Global ID of the Namespace for the monthly CI/CD minutes usage. | +### `Query.ciPipelineStage` + +Stage belonging to a CI pipeline. + +Returns [`CiStage`](#cistage). + +#### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="querycipipelinestageid"></a>`id` | [`CiStageID!`](#cistageid) | Global ID of the CI stage. | + ### `Query.ciVariables` List of the instance's CI/CD variables. @@ -25349,6 +25361,12 @@ A `CiRunnerManagerID` is a global ID. It is encoded as a string. An example `CiRunnerManagerID` is: `"gid://gitlab/Ci::RunnerManager/1"`. +### `CiStageID` + +A `CiStageID` is a global ID. It is encoded as a string. + +An example `CiStageID` is: `"gid://gitlab/Ci::Stage/1"`. + ### `ClustersAgentID` A `ClustersAgentID` is a global ID. It is encoded as a string. diff --git a/doc/ci/resource_groups/index.md b/doc/ci/resource_groups/index.md index d00a92169f1..ee7256dad3f 100644 --- a/doc/ci/resource_groups/index.md +++ b/doc/ci/resource_groups/index.md @@ -194,13 +194,11 @@ You must define [`strategy: depend`](../yaml/index.md#triggerstrategy) with the `trigger` keyword. This ensures that the lock isn't released until the downstream pipeline finishes. -## API +## Related topics -See the [API documentation](../../api/resource_groups.md). - -## Related features - -Read more how you can use GitLab for [safe deployments](../environments/deployment_safety.md). +- [API documentation](../../api/resource_groups.md) +- [Log documentation](../../administration/logs/index.md#ci_resource_groups_jsonlog) +- [GitLab for safe deployments](../environments/deployment_safety.md) ## Troubleshooting diff --git a/doc/ci/secrets/id_token_authentication.md b/doc/ci/secrets/id_token_authentication.md index d6d1f62e47d..177398a6acc 100644 --- a/doc/ci/secrets/id_token_authentication.md +++ b/doc/ci/secrets/id_token_authentication.md @@ -67,6 +67,9 @@ The token also includes custom claims provided by GitLab: | `environment` | Job specifies an environment | Environment this job deploys to ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/294440) in GitLab 13.9). | | `environment_protected` | Job specifies an environment | `true` if deployed environment is protected, `false` otherwise ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/294440) in GitLab 13.9). | | `deployment_tier` | Job specifies an environment | [Deployment tier](../environments/index.md#deployment-tier-of-environments) of the environment the job specifies. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/363590) in GitLab 15.2. | +| `runner_id` | Always | ID of the runner executing the job. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/404722) in GitLab 16.0. | +| `runner_environment` | Always | The type of runner used by the job. Can be either `gitlab-hosted` or `self-hosted`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/404722) in GitLab 16.0. | +| `sha` | Always | The commit SHA for the job. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/404722) in GitLab 16.0. | ```json { @@ -86,6 +89,9 @@ The token also includes custom claims provided by GitLab: "environment": "test-environment2", "environment_protected": "false", "deployment_tier": "testing", + "runner_id": 1, + "runner_environment": "self-hosted", + "sha": "714a629c0b401fdce83e847fc9589983fc6f46bc", "jti": "235b3a54-b797-45c7-ae9a-f72d7bc6ef5b", "iss": "https://gitlab.example.com", "iat": 1681395193, diff --git a/doc/install/aws/eks_clusters_aws.md b/doc/install/aws/eks_clusters_aws.md index 191d0f93382..ccbc0752975 100644 --- a/doc/install/aws/eks_clusters_aws.md +++ b/doc/install/aws/eks_clusters_aws.md @@ -23,7 +23,7 @@ Using `eksctl` enables the following when building an EKS Cluster: - You have various cluster configuration options: - Selection of operating system: Amazon Linux 2, Windows, Bottlerocket - Selection of Hardware Architecture: x86, ARM, GPU - - Selection of Kubernetes version (the GitLab-managed clusters for your project's applications have [specific Kubernetes version requirements](../../user/clusters/agent/index.md#supported-cluster-versions)) + - Selection of Kubernetes version (the GitLab-managed clusters for your project's applications have [specific Kubernetes version requirements](../../user/clusters/agent/index.md#gitlab-agent-for-kubernetes-supported-cluster-versions)) - It can deploy high value-add items to the cluster, including: - A bastion host to keep the cluster endpoint private and possible perform performance testing. - Prometheus and Grafana for monitoring. diff --git a/doc/integration/advanced_search/elasticsearch.md b/doc/integration/advanced_search/elasticsearch.md index 2cffad0b0e0..dfef58ad830 100644 --- a/doc/integration/advanced_search/elasticsearch.md +++ b/doc/integration/advanced_search/elasticsearch.md @@ -31,9 +31,10 @@ before we remove them. ### OpenSearch version requirements -| GitLab version | OpenSearch version | -|-----------------------|--------------------------| -| GitLab 15.0 or later | OpenSearch 1.x or later | +| GitLab version | OpenSearch version | +|-------------------------|---------------------------| +| GitLab 15.0 to 15.5.2 | OpenSearch 1.x | +| GitLab 15.5.3 and later | OpenSearch 1.x and later | If your version of Elasticsearch or OpenSearch is incompatible, to prevent data loss, indexing pauses and a message is logged in the diff --git a/doc/operations/metrics/index.md b/doc/operations/metrics/index.md index e67e7b0f97e..d6a9190be1e 100644 --- a/doc/operations/metrics/index.md +++ b/doc/operations/metrics/index.md @@ -162,7 +162,6 @@ chart panel. To activate keyboard shortcuts, use keyboard tabs to highlight the with your mouse, then press the key corresponding to your desired action: - **Expand panel** - <kbd>e</kbd> -- **View logs** - <kbd>l</kbd> (lowercase 'L') - **Download CSV** - <kbd>d</kbd> - **Copy link to chart** - <kbd>c</kbd> - **Alerts** - <kbd>a</kbd> diff --git a/doc/policy/alpha-beta-support.md b/doc/policy/alpha-beta-support.md index 8976e0ed503..252b6a6fd1b 100644 --- a/doc/policy/alpha-beta-support.md +++ b/doc/policy/alpha-beta-support.md @@ -49,3 +49,11 @@ Generally Available features means that they passed the [Production Readiness Re - Ready for production use at any scale. - Fully documented and supported. - UX complete and in line with GitLab design standards. + +## Never internal + +Features are never internal (GitLab team-members) only. +Our [mission is "everyone can contribute"](https://about.gitlab.com/company/mission/), and that is only possible if people outside the company can try a feature. +We will get higher quality (more diverse) feedback if people from different organizations try something. +We've also learned that internal only as a state slows us down more than it speeds us up. +The experimental features are only shown when people/organizations opt-in to experiments, we are allowed to make mistakes here and literally experiment. diff --git a/doc/topics/autodevops/cicd_variables.md b/doc/topics/autodevops/cicd_variables.md index 4242958d962..f4c74fe3245 100644 --- a/doc/topics/autodevops/cicd_variables.md +++ b/doc/topics/autodevops/cicd_variables.md @@ -62,8 +62,7 @@ Use these variables to customize and deploy your build. ## Database variables WARNING: -The default value of `true` for `POSTGRES_ENABLED` was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/387766) -in GitLab 15.8. In [GitLab 16.0](https://gitlab.com/gitlab-org/gitlab/-/issues/343988), the default value will change to `false`. +From [GitLab 16.0](https://gitlab.com/gitlab-org/gitlab/-/issues/343988), `POSTGRES_ENABLED` is no longer set by default. Use these variables to integrate CI/CD with PostgreSQL databases. @@ -71,7 +70,7 @@ Use these variables to integrate CI/CD with PostgreSQL databases. |-----------------------------------------|------------------------------------| | `DB_INITIALIZE` | Used to specify the command to run to initialize the application's PostgreSQL database. Runs inside the application pod. | | `DB_MIGRATE` | Used to specify the command to run to migrate the application's PostgreSQL database. Runs inside the application pod. | -| `POSTGRES_ENABLED` | Whether PostgreSQL is enabled. Defaults to `true` until GitLab 16.0, when the default will change to `false`. Set to `false` to disable the automatic deployment of PostgreSQL. | +| `POSTGRES_ENABLED` | Whether PostgreSQL is enabled. Set to `true` to enable the automatic deployment of PostgreSQL. | | `POSTGRES_USER` | The PostgreSQL user. Defaults to `user`. Set it to use a custom username. | | `POSTGRES_PASSWORD` | The PostgreSQL password. Defaults to `testing-password`. Set it to use a custom password. | | `POSTGRES_DB` | The PostgreSQL database name. Defaults to the value of [`$CI_ENVIRONMENT_SLUG`](../../ci/variables/index.md#predefined-cicd-variables). Set it to use a custom database name. | diff --git a/doc/topics/autodevops/upgrading_postgresql.md b/doc/topics/autodevops/upgrading_postgresql.md index 2cf85dc07d7..77e98d7bcaa 100644 --- a/doc/topics/autodevops/upgrading_postgresql.md +++ b/doc/topics/autodevops/upgrading_postgresql.md @@ -6,8 +6,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w # Upgrading PostgreSQL for Auto DevOps **(FREE)** -Auto DevOps provides an [in-cluster PostgreSQL database](customize.md#postgresql-database-support) -for your application. +When `POSTGRES_ENABLED` is `true`, Auto DevOps provides an +[in-cluster PostgreSQL database](customize.md#postgresql-database-support) for your application. The version of the chart used to provision PostgreSQL: diff --git a/doc/user/clusters/agent/index.md b/doc/user/clusters/agent/index.md index 07149ccd8fc..ccb3a346903 100644 --- a/doc/user/clusters/agent/index.md +++ b/doc/user/clusters/agent/index.md @@ -52,9 +52,9 @@ Use this workflow: This workflow has a weaker security model and is not recommended for production deployments. -## Supported cluster versions +## GitLab agent for Kubernetes supported cluster versions -GitLab supports the following Kubernetes versions. You can upgrade your +The agent for Kubernetes supports the following Kubernetes versions. You can upgrade your Kubernetes version to a supported version at any time: - 1.26 (support ends on March 22, 2024 or when 1.29 becomes supported) diff --git a/doc/user/clusters/agent/troubleshooting.md b/doc/user/clusters/agent/troubleshooting.md index d058a583b19..531dac20fb6 100644 --- a/doc/user/clusters/agent/troubleshooting.md +++ b/doc/user/clusters/agent/troubleshooting.md @@ -239,4 +239,4 @@ When you install the agent, you might encounter an error that states: Error: parse error at (gitlab-agent/templates/observability-secret.yaml:1): unclosed action ``` -This error is typically caused by an incompatible version of Helm. To resolve the issue, ensure that you are using a version of Helm [compatible with your version of Kubernetes](index.md#supported-cluster-versions). +This error is typically caused by an incompatible version of Helm. To resolve the issue, ensure that you are using a version of Helm [compatible with your version of Kubernetes](index.md#gitlab-agent-for-kubernetes-supported-cluster-versions). diff --git a/doc/user/compliance/license_check_rules.md b/doc/user/compliance/license_check_rules.md new file mode 100644 index 00000000000..3007d04a8c1 --- /dev/null +++ b/doc/user/compliance/license_check_rules.md @@ -0,0 +1,15 @@ +--- +redirect_to: 'license_approval_policies.md' +remove_date: '2023-07-25' +--- + +# License Check Policies (removed) **(ULTIMATE)** + +This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/390417) in GitLab 15.9 +and [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/397067) in 16.0. +Use [License Approval Policies](license_approval_policies.md) instead. + +<!-- This redirect file can be deleted after <2023-07-25>. --> +<!-- Redirects that point to other docs in the same project expire in three months. --> +<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. --> +<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> diff --git a/doc/user/infrastructure/iac/terraform_state.md b/doc/user/infrastructure/iac/terraform_state.md index 9bddbfdb1dd..1b0065fd165 100644 --- a/doc/user/infrastructure/iac/terraform_state.md +++ b/doc/user/infrastructure/iac/terraform_state.md @@ -7,10 +7,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w # GitLab-managed Terraform state **(FREE)** > - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2673) in GitLab 13.0. -> - Support for state names that contain periods introduced in GitLab 15.7 [with a flag](../../../administration/feature_flags.md) named `allow_dots_on_tf_state_names`. Disabled by default. [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106861) in GitLab 15.7. - -FLAG: -On self-managed GitLab, by default support for state names that contain periods is not available. To make it available, ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named `allow_dots_on_tf_state_names`. On GitLab.com, support for state names that contain periods is available. Requests for state files might generate HTTP 404 errors after enabling this feature. For more information, see [Troubleshooting the Terraform integration with GitLab](troubleshooting.md#state-not-found-if-the-state-name-contains-a-period). +> - Support for state names that contain periods introduced in GitLab 15.7 [with a flag](../../../administration/feature_flags.md) named `allow_dots_on_tf_state_names`. Disabled by default. +> - Support for state names that contain periods [generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/385597) in GitLab 16.0. Feature flag `allow_dots_on_tf_state_names` removed. Terraform uses state files to store details about your infrastructure configuration. With Terraform remote [backends](https://www.terraform.io/language/settings/backends/configuration), diff --git a/doc/user/infrastructure/iac/troubleshooting.md b/doc/user/infrastructure/iac/troubleshooting.md index 624bb5ff276..d770c0111d0 100644 --- a/doc/user/infrastructure/iac/troubleshooting.md +++ b/doc/user/infrastructure/iac/troubleshooting.md @@ -160,12 +160,3 @@ If your `TF_HTTP_ADDRESS`, `TF_HTTP_LOCK_ADDRESS` and `TF_HTTP_UNLOCK_ADDRESS` a to update the state names there. Alternatively, you can [migrate your terraform state](terraform_state.md#migrate-to-a-gitlab-managed-terraform-state). - -#### Self-managed GitLab instances - -By default, support for state names with periods is not enabled on self-managed GitLab. -You can enable it from the Rails console: - -```ruby -Feature.enable(:allow_dots_on_tf_state_names) -``` diff --git a/doc/user/profile/preferences.md b/doc/user/profile/preferences.md index 0428a66cd37..da4d2da70fe 100644 --- a/doc/user/profile/preferences.md +++ b/doc/user/profile/preferences.md @@ -110,12 +110,9 @@ between the fixed (max. `1280px`) and the fluid (`100%`) application layout. NOTE: While `1280px` is the standard max width when using fixed layout, some pages still use 100% width, depending on the content. -### Dashboard +### Homepage -For users who have access to a large number of projects but only keep up with a -select few, the amount of activity on the your dashboard can be -overwhelming. From the **Dashboard** dropdown list, select what you'd like displayed on your -personal dashboard. +This setting changes the behavior of the tanuki icon in the upper-left corner of GitLab. ### Group overview content diff --git a/doc/user/project/clusters/add_eks_clusters.md b/doc/user/project/clusters/add_eks_clusters.md index f4b806c3047..6c8edb8c3e5 100644 --- a/doc/user/project/clusters/add_eks_clusters.md +++ b/doc/user/project/clusters/add_eks_clusters.md @@ -174,7 +174,7 @@ When you create a new cluster, you have the following settings: | Kubernetes cluster name | Your cluster's name. | | Environment scope | The [associated environment](multiple_kubernetes_clusters.md#setting-the-environment-scope). | | Service role | The **EKS IAM role** (**role A**). | -| Kubernetes version | The [Kubernetes version](../../clusters/agent/index.md#supported-cluster-versions) for your cluster. | +| Kubernetes version | The [Kubernetes version](../../clusters/agent/index.md#gitlab-agent-for-kubernetes-supported-cluster-versions) for your cluster. | | Key pair name | The [key pair](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html) that you can use to connect to your worker nodes. | | VPC | The [VPC](https://docs.aws.amazon.com/vpc/latest/userguide/what-is-amazon-vpc.html) to use for your EKS Cluster resources. | | Subnets | The [subnets](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Subnets.html) in your VPC where your worker nodes run. Two are required. | diff --git a/doc/user/project/merge_requests/reviews/data_usage.md b/doc/user/project/merge_requests/reviews/data_usage.md index dd07f0b4a6e..f0eb3c015b6 100644 --- a/doc/user/project/merge_requests/reviews/data_usage.md +++ b/doc/user/project/merge_requests/reviews/data_usage.md @@ -5,7 +5,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w type: index, reference --- -# Suggested Reviewers Data Usage +# Suggested Reviewers Data Usage **(ULTIMATE SAAS)** ## How it works diff --git a/lib/api/terraform/state.rb b/lib/api/terraform/state.rb index 184690f9979..8017a195f28 100644 --- a/lib/api/terraform/state.rb +++ b/lib/api/terraform/state.rb @@ -55,17 +55,6 @@ module API def remote_state_handler ::Terraform::RemoteStateHandler.new(user_project, current_user, name: params[:name], lock_id: params[:ID]) end - - def not_found_for_dots? - Feature.disabled?(:allow_dots_on_tf_state_names) && params[:name].include?(".") - end - - # Change the state name to behave like before, https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105674 - # has been introduced. This behavior can be controlled via `allow_dots_on_tf_state_names` FF. - # See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106861 - def legacy_state_name! - params[:name] = params[:name].split('.').first - end end desc 'Get a Terraform state by its name' do @@ -83,8 +72,6 @@ module API end route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth get do - legacy_state_name! if not_found_for_dots? - remote_state_handler.find_with_lock do |state| no_content! unless state.latest_file && state.latest_file.exists? @@ -109,7 +96,6 @@ module API route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth post do authorize! :admin_terraform_state, user_project - legacy_state_name! if not_found_for_dots? data = request.body.read no_content! if data.empty? @@ -138,7 +124,6 @@ module API route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth delete do authorize! :admin_terraform_state, user_project - legacy_state_name! if not_found_for_dots? remote_state_handler.find_with_lock do |state| ::Terraform::States::TriggerDestroyService.new(state, current_user: current_user).execute @@ -170,8 +155,6 @@ module API requires :Path, type: String, desc: 'Terraform path' end post '/lock' do - not_found! if not_found_for_dots? - authorize! :admin_terraform_state, user_project status_code = :ok @@ -215,8 +198,6 @@ module API optional :ID, type: String, limit: 255, desc: 'Terraform state lock ID' end delete '/lock' do - not_found! if not_found_for_dots? - authorize! :admin_terraform_state, user_project remote_state_handler.unlock! diff --git a/lib/gitlab/ci/jwt_v2.rb b/lib/gitlab/ci/jwt_v2.rb index fdff5035d37..bb49c18f784 100644 --- a/lib/gitlab/ci/jwt_v2.rb +++ b/lib/gitlab/ci/jwt_v2.rb @@ -4,6 +4,8 @@ module Gitlab module Ci class JwtV2 < Jwt DEFAULT_AUD = Settings.gitlab.base_url + GITLAB_HOSTED_RUNNER = 'gitlab-hosted' + SELF_HOSTED_RUNNER = 'self-hosted' def self.for_build(build, aud: DEFAULT_AUD) new(build, ttl: build.metadata_timeout, aud: aud).encoded @@ -38,6 +40,14 @@ module Gitlab } end end + + def custom_claims + super.merge( + runner_id: build.runner.id, + runner_environment: build.runner.gitlab_hosted? ? GITLAB_HOSTED_RUNNER : SELF_HOSTED_RUNNER, + sha: build.pipeline.sha + ) + end end end end diff --git a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml index 11420b05dfb..4f12f0cd3b8 100644 --- a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml @@ -58,7 +58,6 @@ variables: POSTGRES_USER: user POSTGRES_PASSWORD: testing-password - POSTGRES_ENABLED: "true" POSTGRES_DB: $CI_ENVIRONMENT_SLUG DOCKER_DRIVER: overlay2 diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 62e14eebc9a..2936d4d21b9 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -1860,6 +1860,9 @@ msgstr "" msgid "AI|The container element wasn't found, stopping AI Genie." msgstr "" +msgid "AI|There is too much text in the chat. Please try again with a shorter text." +msgstr "" + msgid "AI|Unhelpful" msgstr "" @@ -1872,6 +1875,9 @@ msgstr "" msgid "AI|You are not allowed to copy any part of this output into issues, comments, GitLab source code, commit messages, merge requests or any other user interface in the %{gitlabOrg} or %{gitlabCom} groups." msgstr "" +msgid "AI|You can ask AI for more information." +msgstr "" + msgid "API" msgstr "" @@ -11048,6 +11054,9 @@ msgstr "" msgid "ComplianceReport|Framework successfully removed" msgstr "" +msgid "ComplianceReport|Full target branch name" +msgstr "" + msgid "ComplianceReport|Less than 2 approvers" msgstr "" @@ -11063,6 +11072,9 @@ msgstr "" msgid "ComplianceReport|Retrieving the compliance framework report failed. Refresh the page and try again." msgstr "" +msgid "ComplianceReport|Search target branch" +msgstr "" + msgid "ComplianceReport|Select at least one project to apply the bulk action" msgstr "" @@ -16690,12 +16702,21 @@ msgstr "" msgid "Environments|protected" msgstr "" +msgid "Environment|Age" +msgstr "" + msgid "Environment|Auto stop %{time}" msgstr "" +msgid "Environment|Cluster IP" +msgstr "" + msgid "Environment|Deployment tier" msgstr "" +msgid "Environment|External IP" +msgstr "" + msgid "Environment|Failed" msgstr "" @@ -16708,9 +16729,15 @@ msgstr "" msgid "Environment|Pods" msgstr "" +msgid "Environment|Ports" +msgstr "" + msgid "Environment|Running" msgstr "" +msgid "Environment|Services" +msgstr "" + msgid "Environment|Succeeded" msgstr "" @@ -25906,6 +25933,9 @@ msgstr "" msgid "Leave zen mode" msgstr "" +msgid "Legacy Web IDE" +msgstr "" + msgid "Legacy burndown chart" msgstr "" @@ -38373,6 +38403,9 @@ msgstr "" msgid "Runners|Project runners" msgstr "" +msgid "Runners|Project › CI/CD Settings › Runners" +msgstr "" + msgid "Runners|Property Name" msgstr "" @@ -43617,6 +43650,9 @@ msgstr "" msgid "Take a look at the documentation to discover all of GitLab’s capabilities." msgstr "" +msgid "TanukiBot|Tanuki Bot" +msgstr "" + msgid "Target" msgstr "" @@ -48633,9 +48669,6 @@ msgstr "" msgid "View log" msgstr "" -msgid "View logs" -msgstr "" - msgid "View milestones" msgstr "" @@ -50308,9 +50341,18 @@ msgstr "" msgid "Workspaces|Create workspace" msgstr "" +msgid "Workspaces|Creating" +msgstr "" + msgid "Workspaces|Develop anywhere" msgstr "" +msgid "Workspaces|Error" +msgstr "" + +msgid "Workspaces|Failed" +msgstr "" + msgid "Workspaces|Failed to create workspace" msgstr "" @@ -50320,6 +50362,9 @@ msgstr "" msgid "Workspaces|New workspace" msgstr "" +msgid "Workspaces|Running" +msgstr "" + msgid "Workspaces|Select cluster agent" msgstr "" @@ -50329,12 +50374,24 @@ msgstr "" msgid "Workspaces|Select project" msgstr "" +msgid "Workspaces|Starting" +msgstr "" + +msgid "Workspaces|Stopped" +msgstr "" + +msgid "Workspaces|Stopping" +msgstr "" + msgid "Workspaces|To create a workspace for this project, an administrator must configure an agent for the project's group." msgstr "" msgid "Workspaces|To create a workspace, add a devfile to this project. A devfile is a configuration file for your workspace." msgstr "" +msgid "Workspaces|Unknown state" +msgstr "" + msgid "Workspaces|Workspaces" msgstr "" diff --git a/package.json b/package.json index 67389cf31a9..b884cd07239 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "@gitlab/favicon-overlay": "2.0.0", "@gitlab/fonts": "^1.2.0", "@gitlab/svgs": "3.40.0", - "@gitlab/ui": "61.3.0", + "@gitlab/ui": "62.4.0", "@gitlab/visual-review-tools": "1.7.3", "@gitlab/web-ide": "0.0.1-dev-20230425040132", "@mattiasbuelens/web-streams-adapter": "^0.1.0", diff --git a/scripts/review_apps/base-config.yaml b/scripts/review_apps/base-config.yaml index 18efa445b26..42552a5e9b1 100644 --- a/scripts/review_apps/base-config.yaml +++ b/scripts/review_apps/base-config.yaml @@ -83,7 +83,7 @@ gitlab: cpu: 400m memory: 920Mi limits: - cpu: 800m + cpu: 1000m memory: 1380Mi sidekiq: diff --git a/spec/controllers/projects/runners_controller_spec.rb b/spec/controllers/projects/runners_controller_spec.rb index 2edb41b7119..e0e4d0f7bc5 100644 --- a/spec/controllers/projects/runners_controller_spec.rb +++ b/spec/controllers/projects/runners_controller_spec.rb @@ -3,9 +3,9 @@ require 'spec_helper' RSpec.describe Projects::RunnersController, feature_category: :runner_fleet do - let(:user) { create(:user) } - let(:project) { create(:project) } - let(:runner) { create(:ci_runner, :project, projects: [project]) } + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project) } + let_it_be(:runner) { create(:ci_runner, :project, projects: [project]) } let(:params) do { @@ -78,6 +78,79 @@ RSpec.describe Projects::RunnersController, feature_category: :runner_fleet do end end + describe '#register' do + subject(:register) { get :register, params: { namespace_id: project.namespace, project_id: project, id: new_runner } } + + context 'when create_runner_workflow_for_namespace is enabled' do + before do + stub_feature_flags(create_runner_workflow_for_namespace: [project.namespace]) + end + + context 'when user is maintainer' do + before do + project.add_maintainer(user) + end + + context 'when runner can be registered after creation' do + let_it_be(:new_runner) { create(:ci_runner, :project, projects: [project], registration_type: :authenticated_user) } + + it 'renders a :register template' do + register + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to render_template(:register) + end + end + + context 'when runner cannot be registered after creation' do + let_it_be(:new_runner) { runner } + + it 'returns :not_found' do + register + + expect(response).to have_gitlab_http_status(:not_found) + end + end + end + + context 'when user is not maintainer' do + before do + project.add_developer(user) + end + + context 'when runner can be registered after creation' do + let_it_be(:new_runner) { create(:ci_runner, :project, projects: [project], registration_type: :authenticated_user) } + + it 'returns :not_found' do + register + + expect(response).to have_gitlab_http_status(:not_found) + end + end + end + end + + context 'when create_runner_workflow_for_namespace is disabled' do + let_it_be(:new_runner) { create(:ci_runner, :project, projects: [project], registration_type: :authenticated_user) } + + before do + stub_feature_flags(create_runner_workflow_for_namespace: false) + end + + context 'when user is maintainer' do + before do + project.add_maintainer(user) + end + + it 'returns :not_found' do + register + + expect(response).to have_gitlab_http_status(:not_found) + end + end + end + end + describe '#update' do it 'updates the runner and ticks the queue' do new_desc = runner.description.swapcase diff --git a/spec/factories/customer_relations/contacts.rb b/spec/factories/customer_relations/contacts.rb index 1896510d362..6410e298bc3 100644 --- a/spec/factories/customer_relations/contacts.rb +++ b/spec/factories/customer_relations/contacts.rb @@ -8,10 +8,6 @@ FactoryBot.define do last_name { generate(:name) } email { generate(:email) } - trait :with_organization do - organization - end - trait :inactive do state { :inactive } end diff --git a/spec/factories/customer_relations/organizations.rb b/spec/factories/customer_relations/organizations.rb index b6efd46f1a4..789099190ac 100644 --- a/spec/factories/customer_relations/organizations.rb +++ b/spec/factories/customer_relations/organizations.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true FactoryBot.define do - factory :organization, class: 'CustomerRelations::Organization' do + factory :crm_organization, class: 'CustomerRelations::Organization' do group name { generate(:name) } diff --git a/spec/features/groups/crm/contacts/create_spec.rb b/spec/features/groups/crm/contacts/create_spec.rb index 860cadd322d..aa05ef82a8b 100644 --- a/spec/features/groups/crm/contacts/create_spec.rb +++ b/spec/features/groups/crm/contacts/create_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe 'Create a CRM contact', :js, feature_category: :service_desk do let(:user) { create(:user) } let(:group) { create(:group, :crm_enabled) } - let!(:organization) { create(:organization, group: group, name: 'GitLab') } + let!(:crm_organization) { create(:crm_organization, group: group, name: 'GitLab') } before do group.add_owner(user) diff --git a/spec/features/nav/new_nav_invite_members_spec.rb b/spec/features/nav/new_nav_invite_members_spec.rb new file mode 100644 index 00000000000..4c37d6b4760 --- /dev/null +++ b/spec/features/nav/new_nav_invite_members_spec.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'new navigation toggle', :js, feature_category: :navigation do + include Features::InviteMembersModalHelpers + + let_it_be(:user) { create(:user, use_new_navigation: true) } + + before do + sign_in(user) + end + + context 'when inside a group' do + let_it_be(:group) { create(:group).tap { |record| record.add_owner(user) } } + + before do + visit group_path(group) + end + + it 'the add menu contains invite members dropdown option and opens invite modal' do + invite_members_from_menu + + page.within invite_modal_selector do + expect(page).to have_content("You're inviting members to the #{group.name} group") + end + end + end + + context 'when inside a project' do + let_it_be(:project) { create(:project, :repository).tap { |record| record.add_owner(user) } } + + before do + visit project_path(project) + end + + it 'the add menu contains invite members dropdown option and opens invite modal' do + invite_members_from_menu + + page.within invite_modal_selector do + expect(page).to have_content("You're inviting members to the #{project.name} project") + end + end + end + + def invite_members_from_menu + page.find('[data-testid="new-menu-toggle"] button').click + click_button('Invite team members') + end +end diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb index cab6aac42e6..54482d141c7 100644 --- a/spec/features/runners_spec.rb +++ b/spec/features/runners_spec.rb @@ -31,6 +31,16 @@ RSpec.describe 'Runners', feature_category: :runner_fleet do expect(page).to have_link(s_('Runners|New project runner'), href: new_project_runner_path(project)) end + + describe 'runner registration', :js do + before do + visit new_project_runner_path(project) + end + + it_behaves_like 'creates runner and shows register page' do + let(:register_path_pattern) { register_project_runner_path(project, '.*') } + end + end end context 'when create_runner_workflow_for_namespace is disabled' do diff --git a/spec/finders/crm/contacts_finder_spec.rb b/spec/finders/crm/contacts_finder_spec.rb index 43dcced53fd..d0339ce2b18 100644 --- a/spec/finders/crm/contacts_finder_spec.rb +++ b/spec/finders/crm/contacts_finder_spec.rb @@ -148,7 +148,7 @@ RSpec.describe Crm::ContactsFinder do :contact, group: search_test_group, email: "a@test.com", - organization: create(:organization, name: "Company Z") + organization: create(:crm_organization, name: "Company Z") ) end @@ -157,7 +157,7 @@ RSpec.describe Crm::ContactsFinder do :contact, group: search_test_group, email: "b@test.com", - organization: create(:organization, name: "Company A") + organization: create(:crm_organization, name: "Company A") ) end diff --git a/spec/finders/crm/organizations_finder_spec.rb b/spec/finders/crm/organizations_finder_spec.rb index c89ac3b1cb5..bc174f927a7 100644 --- a/spec/finders/crm/organizations_finder_spec.rb +++ b/spec/finders/crm/organizations_finder_spec.rb @@ -12,8 +12,8 @@ RSpec.describe Crm::OrganizationsFinder do let_it_be(:root_group) { create(:group, :crm_enabled) } let_it_be(:group) { create(:group, parent: root_group) } - let_it_be(:organization_1) { create(:organization, group: root_group) } - let_it_be(:organization_2) { create(:organization, group: root_group) } + let_it_be(:crm_organization_1) { create(:crm_organization, group: root_group) } + let_it_be(:crm_organization_2) { create(:crm_organization, group: root_group) } context 'when user does not have permissions to see organizations in the group' do it 'returns an empty array' do @@ -28,7 +28,7 @@ RSpec.describe Crm::OrganizationsFinder do context 'when feature flag is enabled' do it 'returns all group organizations' do - expect(subject).to match_array([organization_1, organization_2]) + expect(subject).to match_array([crm_organization_1, crm_organization_2]) end end end @@ -46,7 +46,7 @@ RSpec.describe Crm::OrganizationsFinder do context 'when customer relations feature is disabled for the group' do let_it_be(:group) { create(:group) } - let_it_be(:organization) { create(:organization, group: group) } + let_it_be(:crm_organization) { create(:crm_organization, group: group) } before do group.add_developer(user) @@ -62,7 +62,7 @@ RSpec.describe Crm::OrganizationsFinder do let_it_be(:search_test_a) do create( - :organization, + :crm_organization, group: search_test_group, name: "DEF", description: "ghi_st", @@ -72,7 +72,7 @@ RSpec.describe Crm::OrganizationsFinder do let_it_be(:search_test_b) do create( - :organization, + :crm_organization, group: search_test_group, name: "ABC_st", description: "JKL", @@ -134,7 +134,7 @@ RSpec.describe Crm::OrganizationsFinder do let_it_be(:sort_test_a) do create( - :organization, + :crm_organization, group: group, name: "ABC", description: "1" @@ -143,7 +143,7 @@ RSpec.describe Crm::OrganizationsFinder do let_it_be(:sort_test_b) do create( - :organization, + :crm_organization, group: group, name: "DEF", description: "2", @@ -153,7 +153,7 @@ RSpec.describe Crm::OrganizationsFinder do let_it_be(:sort_test_c) do create( - :organization, + :crm_organization, group: group, name: "GHI", default_rate: 20 @@ -186,8 +186,8 @@ RSpec.describe Crm::OrganizationsFinder do describe '.counts_by_state' do let_it_be(:group) { create(:group, :crm_enabled) } - let_it_be(:active_organizations) { create_list(:organization, 3, group: group, state: :active) } - let_it_be(:inactive_organizations) { create_list(:organization, 2, group: group, state: :inactive) } + let_it_be(:active_crm_organizations) { create_list(:crm_organization, 3, group: group, state: :active) } + let_it_be(:inactive_crm_organizations) { create_list(:crm_organization, 2, group: group, state: :inactive) } before do group.add_developer(user) diff --git a/spec/finders/deployments_finder_spec.rb b/spec/finders/deployments_finder_spec.rb index 90cd6283130..be7e9a84991 100644 --- a/spec/finders/deployments_finder_spec.rb +++ b/spec/finders/deployments_finder_spec.rb @@ -22,7 +22,7 @@ RSpec.describe DeploymentsFinder do it 'raises an error' do expect { subject }.to raise_error( described_class::InefficientQueryError, - '`updated_at` filter and `updated_at` sorting must be paired') + '`updated_at` filter requires `updated_at` sort') end end @@ -178,8 +178,8 @@ RSpec.describe DeploymentsFinder do 'iid' | 'desc' | [:deployment_3, :deployment_2, :deployment_1] 'ref' | 'asc' | [:deployment_2, :deployment_1, :deployment_3] 'ref' | 'desc' | [:deployment_3, :deployment_1, :deployment_2] - 'updated_at' | 'asc' | described_class::InefficientQueryError - 'updated_at' | 'desc' | described_class::InefficientQueryError + 'updated_at' | 'asc' | [:deployment_2, :deployment_3, :deployment_1] + 'updated_at' | 'desc' | [:deployment_1, :deployment_3, :deployment_2] 'finished_at' | 'asc' | described_class::InefficientQueryError 'finished_at' | 'desc' | described_class::InefficientQueryError 'invalid' | 'asc' | [:deployment_1, :deployment_2, :deployment_3] diff --git a/spec/finders/issuables/crm_organization_filter_spec.rb b/spec/finders/issuables/crm_organization_filter_spec.rb index 2a521dcf721..9a910091fd2 100644 --- a/spec/finders/issuables/crm_organization_filter_spec.rb +++ b/spec/finders/issuables/crm_organization_filter_spec.rb @@ -6,11 +6,11 @@ RSpec.describe Issuables::CrmOrganizationFilter do let_it_be(:group) { create(:group) } let_it_be(:project) { create(:project, group: group) } - let_it_be(:organization1) { create(:organization, group: group) } - let_it_be(:organization2) { create(:organization, group: group) } - let_it_be(:contact1) { create(:contact, group: group, organization: organization1) } - let_it_be(:contact2) { create(:contact, group: group, organization: organization1) } - let_it_be(:contact3) { create(:contact, group: group, organization: organization2) } + let_it_be(:crm_organization1) { create(:crm_organization, group: group) } + let_it_be(:crm_organization2) { create(:crm_organization, group: group) } + let_it_be(:contact1) { create(:contact, group: group, organization: crm_organization1) } + let_it_be(:contact2) { create(:contact, group: group, organization: crm_organization1) } + let_it_be(:contact3) { create(:contact, group: group, organization: crm_organization2) } let_it_be(:contact1_issue) { create(:issue, project: project) } let_it_be(:contact2_issue) { create(:issue, project: project) } @@ -24,14 +24,14 @@ RSpec.describe Issuables::CrmOrganizationFilter do end describe 'when an organization has issues' do - it 'returns all organization1 issues' do - params = { crm_organization_id: organization1.id } + it 'returns all crm_organization1 issues' do + params = { crm_organization_id: crm_organization1.id } expect(described_class.new(params: params).filter(issues)).to contain_exactly(contact1_issue, contact2_issue) end - it 'returns all organization2 issues' do - params = { crm_organization_id: organization2.id } + it 'returns all crm_organization2 issues' do + params = { crm_organization_id: crm_organization2.id } expect(described_class.new(params: params).filter(issues)).to contain_exactly(contact3_issue) end @@ -39,8 +39,8 @@ RSpec.describe Issuables::CrmOrganizationFilter do describe 'when an organization has no issues' do it 'returns no issues' do - organization3 = create(:organization, group: group) - params = { crm_organization_id: organization3.id } + crm_organization3 = create(:crm_organization, group: group) + params = { crm_organization_id: crm_organization3.id } expect(described_class.new(params: params).filter(issues)).to be_empty end diff --git a/spec/frontend/behaviors/shortcuts/keybindings_spec.js b/spec/frontend/behaviors/shortcuts/keybindings_spec.js index 1f7e1b24e78..65ef6a18864 100644 --- a/spec/frontend/behaviors/shortcuts/keybindings_spec.js +++ b/spec/frontend/behaviors/shortcuts/keybindings_spec.js @@ -7,7 +7,7 @@ import { TOGGLE_PERFORMANCE_BAR, HIDE_APPEARING_CONTENT, LOCAL_STORAGE_KEY, - WEB_IDE_COMMIT, + BOLD_TEXT, } from '~/behaviors/shortcuts/keybindings'; describe('~/behaviors/shortcuts/keybindings', () => { @@ -67,11 +67,11 @@ describe('~/behaviors/shortcuts/keybindings', () => { const customization = ['mod+shift+c']; beforeEach(() => { - setupCustomizations(JSON.stringify({ [WEB_IDE_COMMIT.id]: customization })); + setupCustomizations(JSON.stringify({ [BOLD_TEXT.id]: customization })); }); it('returns the default keybinding for the command', () => { - expect(keysFor(WEB_IDE_COMMIT)).toEqual(['mod+enter']); + expect(keysFor(BOLD_TEXT)).toEqual(['mod+b']); }); }); diff --git a/spec/frontend/ci/runner/project_new_runner_app/project_new_runner_app_spec.js b/spec/frontend/ci/runner/project_new_runner_app/project_new_runner_app_spec.js index 6949108fb1f..14e36e74549 100644 --- a/spec/frontend/ci/runner/project_new_runner_app/project_new_runner_app_spec.js +++ b/spec/frontend/ci/runner/project_new_runner_app/project_new_runner_app_spec.js @@ -5,9 +5,16 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { createAlert, VARIANT_SUCCESS } from '~/alert'; import ProjectRunnerRunnerApp from '~/ci/runner/project_new_runner/project_new_runner_app.vue'; +import { saveAlertToLocalStorage } from '~/ci/runner/local_storage_alert/save_alert_to_local_storage'; import RunnerPlatformsRadioGroup from '~/ci/runner/components/runner_platforms_radio_group.vue'; -import { PROJECT_TYPE, DEFAULT_PLATFORM } from '~/ci/runner/constants'; +import { + PARAM_KEY_PLATFORM, + PROJECT_TYPE, + DEFAULT_PLATFORM, + WINDOWS_PLATFORM, +} from '~/ci/runner/constants'; import RunnerCreateForm from '~/ci/runner/components/runner_create_form.vue'; +import { redirectTo } from '~/lib/utils/url_utility'; import { runnerCreateResult, mockRegistrationToken } from '../mock_data'; const mockProjectId = 'gid://gitlab/Project/72'; @@ -63,12 +70,31 @@ describe('ProjectRunnerRunnerApp', () => { findRunnerCreateForm().vm.$emit('saved', mockCreatedRunner); }); - it('shows an alert', () => { - expect(createAlert).toHaveBeenCalledWith({ + it('pushes an alert to be shown after redirection', () => { + expect(saveAlertToLocalStorage).toHaveBeenCalledWith({ message: s__('Runners|Runner created.'), variant: VARIANT_SUCCESS, }); }); + + it('redirects to the registration page', () => { + const url = `${mockCreatedRunner.ephemeralRegisterUrl}?${PARAM_KEY_PLATFORM}=${DEFAULT_PLATFORM}`; + + expect(redirectTo).toHaveBeenCalledWith(url); + }); + }); + + describe('When another platform is selected and a runner is saved', () => { + beforeEach(() => { + findRunnerPlatformsRadioGroup().vm.$emit('input', WINDOWS_PLATFORM); + findRunnerCreateForm().vm.$emit('saved', mockCreatedRunner); + }); + + it('redirects to the registration page with the platform', () => { + const url = `${mockCreatedRunner.ephemeralRegisterUrl}?${PARAM_KEY_PLATFORM}=${WINDOWS_PLATFORM}`; + + expect(redirectTo).toHaveBeenCalledWith(url); + }); }); describe('When runner fails to save', () => { diff --git a/spec/frontend/ci/runner/project_register_runner_app/project_register_runner_app_spec.js b/spec/frontend/ci/runner/project_register_runner_app/project_register_runner_app_spec.js new file mode 100644 index 00000000000..240fd82fb3b --- /dev/null +++ b/spec/frontend/ci/runner/project_register_runner_app/project_register_runner_app_spec.js @@ -0,0 +1,120 @@ +import { nextTick } from 'vue'; +import { GlButton } from '@gitlab/ui'; + +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import setWindowLocation from 'helpers/set_window_location_helper'; +import { TEST_HOST } from 'helpers/test_constants'; + +import { updateHistory } from '~/lib/utils/url_utility'; +import { PARAM_KEY_PLATFORM, DEFAULT_PLATFORM, WINDOWS_PLATFORM } from '~/ci/runner/constants'; +import ProjectRegisterRunnerApp from '~/ci/runner/project_register_runner/project_register_runner_app.vue'; +import RegistrationInstructions from '~/ci/runner/components/registration/registration_instructions.vue'; +import PlatformsDrawer from '~/ci/runner/components/registration/platforms_drawer.vue'; +import { runnerForRegistration } from '../mock_data'; + +const mockRunnerId = runnerForRegistration.data.runner.id; +const mockRunnersPath = '/group1/project1/-/settings/ci_cd'; + +jest.mock('~/lib/utils/url_utility', () => ({ + ...jest.requireActual('~/lib/utils/url_utility'), + updateHistory: jest.fn(), +})); + +describe('ProjectRegisterRunnerApp', () => { + let wrapper; + + const findRegistrationInstructions = () => wrapper.findComponent(RegistrationInstructions); + const findPlatformsDrawer = () => wrapper.findComponent(PlatformsDrawer); + const findBtn = () => wrapper.findComponent(GlButton); + + const createComponent = () => { + wrapper = shallowMountExtended(ProjectRegisterRunnerApp, { + propsData: { + runnerId: mockRunnerId, + runnersPath: mockRunnersPath, + }, + }); + }; + + describe('When showing runner details', () => { + beforeEach(() => { + createComponent(); + }); + + describe('when runner token is available', () => { + it('shows registration instructions', () => { + expect(findRegistrationInstructions().props()).toEqual({ + platform: DEFAULT_PLATFORM, + runnerId: mockRunnerId, + }); + }); + + it('configures platform drawer', () => { + expect(findPlatformsDrawer().props()).toEqual({ + open: false, + platform: DEFAULT_PLATFORM, + }); + }); + + it('shows runner list button', () => { + expect(findBtn().attributes('href')).toBe(mockRunnersPath); + expect(findBtn().props('variant')).toBe('confirm'); + }); + }); + }); + + describe('When another platform has been selected', () => { + beforeEach(() => { + setWindowLocation(`?${PARAM_KEY_PLATFORM}=${WINDOWS_PLATFORM}`); + + createComponent(); + }); + + it('shows registration instructions for the platform', () => { + expect(findRegistrationInstructions().props('platform')).toBe(WINDOWS_PLATFORM); + }); + }); + + describe('When opening install instructions', () => { + beforeEach(() => { + createComponent(); + + findRegistrationInstructions().vm.$emit('toggleDrawer'); + }); + + it('opens platform drawer', () => { + expect(findPlatformsDrawer().props('open')).toBe(true); + }); + + it('closes platform drawer', async () => { + findRegistrationInstructions().vm.$emit('toggleDrawer'); + await nextTick(); + + expect(findPlatformsDrawer().props('open')).toBe(false); + }); + + it('closes platform drawer from drawer', async () => { + findPlatformsDrawer().vm.$emit('close'); + await nextTick(); + + expect(findPlatformsDrawer().props('open')).toBe(false); + }); + + describe('when selecting a platform', () => { + beforeEach(() => { + findPlatformsDrawer().vm.$emit('selectPlatform', WINDOWS_PLATFORM); + }); + + it('updates the url', () => { + expect(updateHistory).toHaveBeenCalledTimes(1); + expect(updateHistory).toHaveBeenCalledWith({ + url: `${TEST_HOST}/?${PARAM_KEY_PLATFORM}=${WINDOWS_PLATFORM}`, + }); + }); + + it('updates the registration instructions', () => { + expect(findRegistrationInstructions().props('platform')).toBe(WINDOWS_PLATFORM); + }); + }); + }); +}); diff --git a/spec/frontend/comment_templates/components/__snapshots__/list_item_spec.js.snap b/spec/frontend/comment_templates/components/__snapshots__/list_item_spec.js.snap index 788e80de3f6..9c58090344d 100644 --- a/spec/frontend/comment_templates/components/__snapshots__/list_item_spec.js.snap +++ b/spec/frontend/comment_templates/components/__snapshots__/list_item_spec.js.snap @@ -58,7 +58,7 @@ exports[`Comment templates list item component renders list item 1`] = ` </button> <div - class="gl-new-dropdown-panel" + class="gl-new-dropdown-panel gl-w-31" data-testid="base-dropdown-menu" id="base-dropdown-7" > diff --git a/spec/frontend/environments/graphql/mock_data.js b/spec/frontend/environments/graphql/mock_data.js index 8d91ffe5ffc..22d1fefd657 100644 --- a/spec/frontend/environments/graphql/mock_data.js +++ b/spec/frontend/environments/graphql/mock_data.js @@ -812,3 +812,56 @@ const succeededPod = { status: { phase: 'Succeeded' } }; const failedPod = { status: { phase: 'Failed' } }; export const k8sPodsMock = [runningPod, runningPod, pendingPod, succeededPod, failedPod, failedPod]; + +export const k8sServicesMock = [ + { + metadata: { + name: 'my-first-service', + namespace: 'default', + creationTimestamp: new Date(), + }, + spec: { + ports: [ + { + name: 'https', + protocol: 'TCP', + port: 443, + targetPort: 8443, + }, + ], + clusterIP: '10.96.0.1', + externalIP: '-', + type: 'ClusterIP', + }, + }, + { + metadata: { + name: 'my-second-service', + namespace: 'default', + creationTimestamp: '2020-07-03T14:06:04Z', + }, + spec: { + ports: [ + { + name: 'http', + protocol: 'TCP', + appProtocol: 'http', + port: 80, + targetPort: 'http', + nodePort: 31989, + }, + { + name: 'https', + protocol: 'TCP', + appProtocol: 'https', + port: 443, + targetPort: 'https', + nodePort: 32679, + }, + ], + clusterIP: '10.105.219.238', + externalIP: '-', + type: 'NodePort', + }, + }, +]; diff --git a/spec/frontend/environments/graphql/resolvers_spec.js b/spec/frontend/environments/graphql/resolvers_spec.js index 94bed8d93ab..6300ecd62a7 100644 --- a/spec/frontend/environments/graphql/resolvers_spec.js +++ b/spec/frontend/environments/graphql/resolvers_spec.js @@ -19,6 +19,7 @@ import { folder, resolvedFolder, k8sPodsMock, + k8sServicesMock, } from './mock_data'; const ENDPOINT = `${TEST_HOST}/environments`; @@ -29,6 +30,13 @@ describe('~/frontend/environments/graphql/resolvers', () => { let mockApollo; let localState; + const configuration = { + basePath: 'kas-proxy/', + baseOptions: { + headers: { 'GitLab-Agent-Id': '1' }, + }, + }; + beforeEach(() => { mockResolvers = resolvers(ENDPOINT); mock = new MockAdapter(axios); @@ -147,12 +155,6 @@ describe('~/frontend/environments/graphql/resolvers', () => { }); describe('k8sPods', () => { const namespace = 'default'; - const configuration = { - basePath: 'kas-proxy/', - baseOptions: { - headers: { 'GitLab-Agent-Id': '1' }, - }, - }; const mockPodsListFn = jest.fn().mockImplementation(() => { return Promise.resolve({ @@ -200,6 +202,38 @@ describe('~/frontend/environments/graphql/resolvers', () => { ); }); }); + describe('k8sServices', () => { + const mockServicesListFn = jest.fn().mockImplementation(() => { + return Promise.resolve({ + data: { + items: k8sServicesMock, + }, + }); + }); + + beforeEach(() => { + jest + .spyOn(CoreV1Api.prototype, 'listCoreV1ServiceForAllNamespaces') + .mockImplementation(mockServicesListFn); + }); + + it('should request services from the cluster_client library', async () => { + const services = await mockResolvers.Query.k8sServices(null, { configuration }); + + expect(mockServicesListFn).toHaveBeenCalled(); + + expect(services).toEqual(k8sServicesMock); + }); + it('should throw an error if the API call fails', async () => { + jest + .spyOn(CoreV1Api.prototype, 'listCoreV1ServiceForAllNamespaces') + .mockRejectedValue(new Error('API error')); + + await expect(mockResolvers.Query.k8sServices(null, { configuration })).rejects.toThrow( + 'API error', + ); + }); + }); describe('stopEnvironmentREST', () => { it('should post to the stop environment path', async () => { mock.onPost(ENDPOINT).reply(HTTP_STATUS_OK); diff --git a/spec/frontend/environments/kubernetes_overview_spec.js b/spec/frontend/environments/kubernetes_overview_spec.js index 1912fd4a82b..6942285261a 100644 --- a/spec/frontend/environments/kubernetes_overview_spec.js +++ b/spec/frontend/environments/kubernetes_overview_spec.js @@ -4,6 +4,7 @@ import { GlCollapse, GlButton, GlAlert } from '@gitlab/ui'; import KubernetesOverview from '~/environments/components/kubernetes_overview.vue'; import KubernetesAgentInfo from '~/environments/components/kubernetes_agent_info.vue'; import KubernetesPods from '~/environments/components/kubernetes_pods.vue'; +import KubernetesTabs from '~/environments/components/kubernetes_tabs.vue'; import { agent } from './graphql/mock_data'; import { mockKasTunnelUrl } from './mock_data'; @@ -32,6 +33,7 @@ describe('~/environments/components/kubernetes_overview.vue', () => { const findCollapseButton = () => wrapper.findComponent(GlButton); const findAgentInfo = () => wrapper.findComponent(KubernetesAgentInfo); const findKubernetesPods = () => wrapper.findComponent(KubernetesPods); + const findKubernetesTabs = () => wrapper.findComponent(KubernetesTabs); const findAlert = () => wrapper.findComponent(GlAlert); const createWrapper = () => { @@ -102,6 +104,12 @@ describe('~/environments/components/kubernetes_overview.vue', () => { configuration, }); }); + + it('renders kubernetes tabs', () => { + expect(findKubernetesTabs().props()).toEqual({ + configuration, + }); + }); }); describe('on cluster error', () => { diff --git a/spec/frontend/environments/kubernetes_tabs_spec.js b/spec/frontend/environments/kubernetes_tabs_spec.js new file mode 100644 index 00000000000..550c7e8b953 --- /dev/null +++ b/spec/frontend/environments/kubernetes_tabs_spec.js @@ -0,0 +1,158 @@ +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import { shallowMount } from '@vue/test-utils'; +import { GlLoadingIcon, GlTabs, GlTab, GlTable, GlPagination } from '@gitlab/ui'; +import { stubComponent } from 'helpers/stub_component'; +import { useFakeDate } from 'helpers/fake_date'; +import waitForPromises from 'helpers/wait_for_promises'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import KubernetesTabs from '~/environments/components/kubernetes_tabs.vue'; +import { SERVICES_LIMIT_PER_PAGE } from '~/environments/constants'; +import { mockKasTunnelUrl } from './mock_data'; +import { k8sServicesMock } from './graphql/mock_data'; + +Vue.use(VueApollo); + +describe('~/environments/components/kubernetes_tabs.vue', () => { + let wrapper; + + const configuration = { + basePath: mockKasTunnelUrl, + baseOptions: { + headers: { 'GitLab-Agent-Id': '1' }, + }, + }; + + const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); + const findTabs = () => wrapper.findComponent(GlTabs); + const findTab = (at) => wrapper.findAllComponents(GlTab).at(at); + const findTable = () => wrapper.findComponent(GlTable); + const findPagination = () => wrapper.findComponent(GlPagination); + + const createApolloProvider = () => { + const mockResolvers = { + Query: { + k8sServices: jest.fn().mockReturnValue(k8sServicesMock), + }, + }; + + return createMockApollo([], mockResolvers); + }; + + const createWrapper = (apolloProvider = createApolloProvider()) => { + wrapper = shallowMount(KubernetesTabs, { + propsData: { configuration }, + apolloProvider, + stubs: { + GlTab, + GlTable: stubComponent(GlTable, { + props: ['items', 'per-page'], + }), + }, + }); + }; + + describe('mounted', () => { + it('shows tabs', () => { + createWrapper(); + + expect(findTabs().exists()).toBe(true); + }); + + it('renders services tab', () => { + createWrapper(); + + expect(findTab(0).text()).toMatchInterpolatedText(`${KubernetesTabs.i18n.servicesTitle} 0`); + }); + }); + + describe('services tab', () => { + useFakeDate(2020, 6, 6); + it('shows the loading icon', () => { + createWrapper(); + + expect(findLoadingIcon().exists()).toBe(true); + }); + + describe('when services data is loaded', () => { + beforeEach(async () => { + createWrapper(); + await waitForPromises(); + }); + + it('hides the loading icon when the list of services loaded', () => { + expect(findLoadingIcon().exists()).toBe(false); + }); + + it('renders services table when gets services data', () => { + expect(findTable().props('perPage')).toBe(SERVICES_LIMIT_PER_PAGE); + expect(findTable().props('items')).toMatchObject([ + { + name: 'my-first-service', + namespace: 'default', + type: 'ClusterIP', + clusterIP: '10.96.0.1', + externalIP: '-', + ports: '443/TCP', + age: '0s', + }, + { + name: 'my-second-service', + namespace: 'default', + type: 'NodePort', + clusterIP: '10.105.219.238', + externalIP: '-', + ports: '80:31989/TCP, 443:32679/TCP', + age: '2d', + }, + ]); + }); + + it("doesn't render pagination when services are less then SERVICES_LIMIT_PER_PAGE", async () => { + createWrapper(); + await waitForPromises(); + + expect(findPagination().exists()).toBe(false); + }); + }); + + it('shows pagination when services are more then SERVICES_LIMIT_PER_PAGE', async () => { + const createApolloProviderWithPagination = () => { + const mockResolvers = { + Query: { + k8sServices: jest + .fn() + .mockReturnValue( + Array.from({ length: 6 }, () => k8sServicesMock).flatMap((array) => array), + ), + }, + }; + + return createMockApollo([], mockResolvers); + }; + + createWrapper(createApolloProviderWithPagination()); + await waitForPromises(); + + expect(findPagination().exists()).toBe(true); + }); + + it('emits an error message when gets an error from the cluster_client API', async () => { + const error = new Error('Error from the cluster_client API'); + const createErroredApolloProvider = () => { + const mockResolvers = { + Query: { + k8sServices: jest.fn().mockRejectedValueOnce(error), + }, + }; + + return createMockApollo([], mockResolvers); + }; + + createWrapper(createErroredApolloProvider()); + await waitForPromises(); + + expect(wrapper.emitted('cluster-error')).toEqual([[error]]); + }); + }); +}); diff --git a/spec/frontend/invite_members/components/invite_members_trigger_spec.js b/spec/frontend/invite_members/components/invite_members_trigger_spec.js index cdb6182e2ae..58c40a49b3c 100644 --- a/spec/frontend/invite_members/components/invite_members_trigger_spec.js +++ b/spec/frontend/invite_members/components/invite_members_trigger_spec.js @@ -1,4 +1,4 @@ -import { GlButton, GlLink, GlDropdownItem } from '@gitlab/ui'; +import { GlButton, GlLink, GlDropdownItem, GlDisclosureDropdownItem } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import InviteMembersTrigger from '~/invite_members/components/invite_members_trigger.vue'; import eventHub from '~/invite_members/event_hub'; @@ -7,6 +7,7 @@ import { TRIGGER_DEFAULT_QA_SELECTOR, TRIGGER_ELEMENT_WITH_EMOJI, TRIGGER_ELEMENT_DROPDOWN_WITH_EMOJI, + TRIGGER_ELEMENT_DISCLOSURE_DROPDOWN, } from '~/invite_members/constants'; import { GlEmoji } from '../mock_data/member_modal'; @@ -23,6 +24,7 @@ const triggerComponent = { anchor: GlLink, 'text-emoji': GlLink, 'dropdown-text-emoji': GlDropdownItem, + 'dropdown-text': GlButton, }; const createComponent = (props = {}) => { @@ -34,6 +36,8 @@ const createComponent = (props = {}) => { }, stubs: { GlEmoji, + GlDisclosureDropdownItem, + GlButton, }, }); }; @@ -116,3 +120,22 @@ describe('dropdown item with emoji', () => { expect(findEmoji().attributes('data-name')).toBe('shaking_hands'); }); }); + +describe('disclosure dropdown item', () => { + const findTrigger = () => wrapper.findComponent(GlDisclosureDropdownItem); + + beforeEach(() => { + createComponent({ triggerElement: TRIGGER_ELEMENT_DISCLOSURE_DROPDOWN }); + }); + + it('renders a trigger button', () => { + expect(findTrigger().exists()).toBe(true); + expect(findTrigger().text()).toBe(displayText); + }); + + it('emits modalOpened which clicked', () => { + findTrigger().vm.$emit('action'); + + expect(wrapper.emitted('modal-opened')).toHaveLength(1); + }); +}); diff --git a/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js b/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js index 0187970efdc..c152a5ef9a8 100644 --- a/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js +++ b/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js @@ -145,7 +145,7 @@ describe('IssuesDashboardApp component', () => { closed: 2, all: 3, }, - tabs: IssuesDashboardApp.IssuableListTabs, + tabs: IssuesDashboardApp.issuableListTabs, urlParams: { sort: urlSortParams[CREATED_DESC], state: STATUS_OPEN, diff --git a/spec/frontend/issues/list/components/issues_list_app_spec.js b/spec/frontend/issues/list/components/issues_list_app_spec.js index 15dde76f49b..61dbc9afeeb 100644 --- a/spec/frontend/issues/list/components/issues_list_app_spec.js +++ b/spec/frontend/issues/list/components/issues_list_app_spec.js @@ -29,7 +29,7 @@ import { STATUS_ALL, STATUS_CLOSED, STATUS_OPEN } from '~/issues/constants'; import CsvImportExportButtons from '~/issuable/components/csv_import_export_buttons.vue'; import IssuableByEmail from '~/issuable/components/issuable_by_email.vue'; import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue'; -import { IssuableListTabs } from '~/vue_shared/issuable/list/constants'; +import { issuableListTabs } from '~/vue_shared/issuable/list/constants'; import EmptyStateWithAnyIssues from '~/issues/list/components/empty_state_with_any_issues.vue'; import EmptyStateWithoutAnyIssues from '~/issues/list/components/empty_state_without_any_issues.vue'; import IssuesListApp from '~/issues/list/components/issues_list_app.vue'; @@ -213,7 +213,7 @@ describe('CE IssuesListApp component', () => { }), initialSortBy: CREATED_DESC, issuables: getIssuesQueryResponse.data.project.issues.nodes, - tabs: IssuableListTabs, + tabs: issuableListTabs, currentTab: STATUS_OPEN, tabCounts: { opened: 1, diff --git a/spec/frontend/ml/experiment_tracking/components/delete_button_spec.js b/spec/frontend/ml/experiment_tracking/components/delete_button_spec.js index 0243cbeb7bf..f2a9e3ad9ee 100644 --- a/spec/frontend/ml/experiment_tracking/components/delete_button_spec.js +++ b/spec/frontend/ml/experiment_tracking/components/delete_button_spec.js @@ -1,4 +1,4 @@ -import { GlModal, GlDropdown, GlDropdownItem } from '@gitlab/ui'; +import { GlModal, GlDisclosureDropdown, GlDisclosureDropdownItem } from '@gitlab/ui'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import DeleteButton from '~/ml/experiment_tracking/components/delete_button.vue'; @@ -12,8 +12,8 @@ describe('DeleteButton', () => { let wrapper; const findModal = () => wrapper.findComponent(GlModal); - const findDropdown = () => wrapper.findComponent(GlDropdown); - const findDeleteButton = () => wrapper.findComponent(GlDropdownItem); + const findDropdown = () => wrapper.findComponent(GlDisclosureDropdown); + const findDeleteButton = () => wrapper.findComponent(GlDisclosureDropdownItem); const findForm = () => wrapper.find('form'); const findModalText = () => wrapper.findByText(MODAL_BODY); diff --git a/spec/frontend/super_sidebar/components/create_menu_spec.js b/spec/frontend/super_sidebar/components/create_menu_spec.js index e05b5d30e69..456085e23da 100644 --- a/spec/frontend/super_sidebar/components/create_menu_spec.js +++ b/spec/frontend/super_sidebar/components/create_menu_spec.js @@ -1,6 +1,13 @@ import { nextTick } from 'vue'; -import { GlDisclosureDropdown, GlTooltip } from '@gitlab/ui'; +import { + GlDisclosureDropdown, + GlTooltip, + GlDisclosureDropdownGroup, + GlDisclosureDropdownItem, +} from '@gitlab/ui'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import { stubComponent } from 'helpers/stub_component'; +import InviteMembersTrigger from '~/invite_members/components/invite_members_trigger.vue'; import { __ } from '~/locale'; import CreateMenu from '~/super_sidebar/components/create_menu.vue'; import { createNewMenuGroups } from '../mock_data'; @@ -9,13 +16,24 @@ describe('CreateMenu component', () => { let wrapper; const findGlDisclosureDropdown = () => wrapper.findComponent(GlDisclosureDropdown); + const findGlDisclosureDropdownGroups = () => wrapper.findAllComponents(GlDisclosureDropdownGroup); + const findGlDisclosureDropdownItems = () => wrapper.findAllComponents(GlDisclosureDropdownItem); + const findInviteMembersTrigger = () => wrapper.findComponent(InviteMembersTrigger); const findGlTooltip = () => wrapper.findComponent(GlTooltip); + const closeAndFocusMock = jest.fn(); + const createWrapper = () => { wrapper = shallowMountExtended(CreateMenu, { propsData: { groups: createNewMenuGroups, }, + stubs: { + InviteMembersTrigger, + GlDisclosureDropdown: stubComponent(GlDisclosureDropdown, { + methods: { closeAndFocus: closeAndFocusMock }, + }), + }, }); }; @@ -35,9 +53,25 @@ describe('CreateMenu component', () => { it("sets the toggle's label", () => { expect(findGlDisclosureDropdown().props('toggleText')).toBe(__('Create new...')); }); + it('has correct amount of dropdown groups', () => { + const items = findGlDisclosureDropdownGroups(); + + expect(items.exists()).toBe(true); + expect(items).toHaveLength(createNewMenuGroups.length); + }); + + it('has correct amount of dropdown items', () => { + const items = findGlDisclosureDropdownItems(); + const numberOfMenuItems = createNewMenuGroups + .map((group) => group.items.length) + .reduce((a, b) => a + b); - it('passes the groups to the disclosure dropdown', () => { - expect(findGlDisclosureDropdown().props('items')).toBe(createNewMenuGroups); + expect(items.exists()).toBe(true); + expect(items).toHaveLength(numberOfMenuItems); + }); + + it('renders the invite member trigger', () => { + expect(findInviteMembersTrigger().exists()).toBe(true); }); it("sets the toggle ID and tooltip's target", () => { @@ -59,5 +93,10 @@ describe('CreateMenu component', () => { expect(findGlTooltip().exists()).toBe(true); }); + + it('closes the dropdown when invite members modal is opened', () => { + findInviteMembersTrigger().vm.$emit('modal-opened'); + expect(closeAndFocusMock).toHaveBeenCalled(); + }); }); }); diff --git a/spec/frontend/super_sidebar/components/help_center_spec.js b/spec/frontend/super_sidebar/components/help_center_spec.js index aa94ca301db..b441c5f531d 100644 --- a/spec/frontend/super_sidebar/components/help_center_spec.js +++ b/spec/frontend/super_sidebar/components/help_center_spec.js @@ -7,6 +7,7 @@ import { helpPagePath } from '~/helpers/help_page_helper'; import { PROMO_URL } from 'jh_else_ce/lib/utils/url_utility'; import { useLocalStorageSpy } from 'helpers/local_storage_helper'; import { STORAGE_KEY } from '~/whats_new/utils/notification'; +import { helpCenterState } from '~/super_sidebar/constants'; import { mockTracking } from 'helpers/tracking_helper'; import { sidebarData } from '../mock_data'; @@ -99,9 +100,10 @@ describe('HelpCenter component', () => { describe('with show_tanuki_bot true', () => { beforeEach(() => { createWrapper({ ...sidebarData, show_tanuki_bot: true }); + jest.spyOn(wrapper.vm.$refs.dropdown, 'close'); }); - it('shows Ask the Tanuki Bot with the help items', () => { + it('shows Ask the Tanuki Bot with the help items via Portal', () => { expect(findDropdownGroup(0).props('group').items).toEqual([ expect.objectContaining({ icon: 'tanuki', @@ -111,6 +113,20 @@ describe('HelpCenter component', () => { ...DEFAULT_HELP_ITEMS, ]); }); + + describe('when Ask the Tanuki Bot button is clicked', () => { + beforeEach(() => { + findButton('Ask the Tanuki Bot').click(); + }); + + it('closes the dropdown', () => { + expect(wrapper.vm.$refs.dropdown.close).toHaveBeenCalled(); + }); + + it('sets helpCenterState.showTanukiBotChatDrawer to true', () => { + expect(helpCenterState.showTanukiBotChatDrawer).toBe(true); + }); + }); }); describe('with Gitlab version check feature enabled', () => { diff --git a/spec/frontend/super_sidebar/mock_data.js b/spec/frontend/super_sidebar/mock_data.js index 0c9449d98a9..234eb7c315c 100644 --- a/spec/frontend/super_sidebar/mock_data.js +++ b/spec/frontend/super_sidebar/mock_data.js @@ -18,7 +18,7 @@ export const createNewMenuGroups = [ }, { text: 'Invite members', - href: '/groups/gitlab-org/-/group_members', + component: 'invite_members', }, ], }, diff --git a/spec/frontend/work_items/components/work_item_links/work_item_links_spec.js b/spec/frontend/work_items/components/work_item_links/work_item_links_spec.js index 4bf7d0c57a3..1d78f35615a 100644 --- a/spec/frontend/work_items/components/work_item_links/work_item_links_spec.js +++ b/spec/frontend/work_items/components/work_item_links/work_item_links_spec.js @@ -14,7 +14,6 @@ import WorkItemLinkChild from '~/work_items/components/work_item_links/work_item import WorkItemDetailModal from '~/work_items/components/work_item_detail_modal.vue'; import AbuseCategorySelector from '~/abuse_reports/components/abuse_category_selector.vue'; import { FORM_TYPES } from '~/work_items/constants'; -import workItemQuery from '~/work_items/graphql/work_item.query.graphql'; import changeWorkItemParentMutation from '~/work_items/graphql/update_work_item.mutation.graphql'; import getWorkItemLinksQuery from '~/work_items/graphql/work_item_links.query.graphql'; import workItemByIidQuery from '~/work_items/graphql/work_item_by_iid.query.graphql'; @@ -24,8 +23,8 @@ import { workItemHierarchyEmptyResponse, workItemHierarchyNoUpdatePermissionResponse, changeWorkItemParentMutationResponse, + workItemByIidResponseFactory, workItemQueryResponse, - projectWorkItemResponse, mockWorkItemCommentNote, } from '../../mock_data'; @@ -46,9 +45,7 @@ describe('WorkItemLinks', () => { const mutationChangeParentHandler = jest .fn() .mockResolvedValue(changeWorkItemParentMutationResponse); - - const childWorkItemQueryHandler = jest.fn().mockResolvedValue(workItemQueryResponse); - const childWorkItemByIidHandler = jest.fn().mockResolvedValue(projectWorkItemResponse); + const childWorkItemByIidHandler = jest.fn().mockResolvedValue(workItemByIidResponseFactory()); const createComponent = async ({ data = {}, @@ -61,7 +58,6 @@ describe('WorkItemLinks', () => { [ [getWorkItemLinksQuery, fetchHandler], [changeWorkItemParentMutation, mutationHandler], - [workItemQuery, childWorkItemQueryHandler], [issueDetailsQuery, issueDetailsQueryHandler], [workItemByIidQuery, childWorkItemByIidHandler], ], @@ -308,18 +304,10 @@ describe('WorkItemLinks', () => { expect(childWorkItemByIidHandler).not.toHaveBeenCalled(); }); - - it('does not fetch work item by id if link is hovered for 250+ ms', async () => { - firstChild.vm.$emit('mouseover', firstChild.vm.childItem.id); - jest.advanceTimersByTime(DEFAULT_DEBOUNCE_AND_THROTTLE_MS); - await waitForPromises(); - - expect(childWorkItemQueryHandler).not.toHaveBeenCalled(); - }); }); - it('starts prefetching work item by iid if URL contains work item id', async () => { - setWindowLocation('?work_item_iid=5&iid_path=true'); + it('starts prefetching work item by iid if URL contains work_item_iid query parameter', async () => { + setWindowLocation('?work_item_iid=5'); await createComponent(); expect(childWorkItemByIidHandler).toHaveBeenCalledWith({ @@ -329,7 +317,7 @@ describe('WorkItemLinks', () => { }); it('does not open the modal if work item iid URL parameter is not found in child items', async () => { - setWindowLocation('?work_item_iid=555&iid_path=true'); + setWindowLocation('?work_item_iid=555'); await createComponent(); expect(showModal).not.toHaveBeenCalled(); @@ -337,7 +325,7 @@ describe('WorkItemLinks', () => { }); it('opens the modal if work item iid URL parameter is found in child items', async () => { - setWindowLocation('?work_item_iid=2&iid_path=true'); + setWindowLocation('?work_item_iid=2'); await createComponent(); expect(showModal).toHaveBeenCalled(); diff --git a/spec/frontend/work_items/components/work_item_links/work_item_tree_spec.js b/spec/frontend/work_items/components/work_item_links/work_item_tree_spec.js index ab69ba7ee1b..f6b70d0161a 100644 --- a/spec/frontend/work_items/components/work_item_links/work_item_tree_spec.js +++ b/spec/frontend/work_items/components/work_item_links/work_item_tree_spec.js @@ -4,11 +4,11 @@ import VueApollo from 'vue-apollo'; import createMockApollo from 'helpers/mock_apollo_helper'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import waitForPromises from 'helpers/wait_for_promises'; -import workItemByIidQuery from '~/work_items/graphql/work_item_by_iid.query.graphql'; import WorkItemTree from '~/work_items/components/work_item_links/work_item_tree.vue'; import WorkItemLinksForm from '~/work_items/components/work_item_links/work_item_links_form.vue'; import WorkItemLinkChild from '~/work_items/components/work_item_links/work_item_link_child.vue'; import OkrActionsSplitButton from '~/work_items/components/work_item_links/okr_actions_split_button.vue'; +import workItemByIidQuery from '~/work_items/graphql/work_item_by_iid.query.graphql'; import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants'; @@ -17,12 +17,13 @@ import { WORK_ITEM_TYPE_ENUM_OBJECTIVE, WORK_ITEM_TYPE_ENUM_KEY_RESULT, } from '~/work_items/constants'; -import { childrenWorkItems, projectWorkItemResponse } from '../../mock_data'; +import { childrenWorkItems, workItemByIidResponseFactory } from '../../mock_data'; describe('WorkItemTree', () => { - let getWorkItemQueryHandler; let wrapper; + const getWorkItemQueryHandler = jest.fn().mockResolvedValue(workItemByIidResponseFactory()); + const findEmptyState = () => wrapper.findByTestId('tree-empty'); const findToggleFormSplitButton = () => wrapper.findComponent(OkrActionsSplitButton); const findForm = () => wrapper.findComponent(WorkItemLinksForm); @@ -35,13 +36,9 @@ describe('WorkItemTree', () => { parentWorkItemType = 'Objective', confidential = false, children = childrenWorkItems, - apolloProvider = null, } = {}) => { - getWorkItemQueryHandler = jest.fn().mockResolvedValue(projectWorkItemResponse); - wrapper = shallowMountExtended(WorkItemTree, { - apolloProvider: - apolloProvider || createMockApollo([[workItemByIidQuery, getWorkItemQueryHandler]]), + apolloProvider: createMockApollo([[workItemByIidQuery, getWorkItemQueryHandler]]), propsData: { workItemType, parentWorkItemType, diff --git a/spec/graphql/mutations/customer_relations/contacts/create_spec.rb b/spec/graphql/mutations/customer_relations/contacts/create_spec.rb index f2bbf0949fb..3ee898c2079 100644 --- a/spec/graphql/mutations/customer_relations/contacts/create_spec.rb +++ b/spec/graphql/mutations/customer_relations/contacts/create_spec.rb @@ -57,10 +57,10 @@ RSpec.describe Mutations::CustomerRelations::Contacts::Create do end end - context 'when attaching to an organization' do + context 'when attaching to an crm_organization' do context 'when all ok' do before do - organization = create(:organization, group: group) + organization = create(:crm_organization, group: group) valid_params[:organization_id] = organization.to_global_id end @@ -69,7 +69,7 @@ RSpec.describe Mutations::CustomerRelations::Contacts::Create do end end - context 'when organization does not exist' do + context 'when crm_organization does not exist' do before do valid_params[:organization_id] = global_id_of(model_name: 'CustomerRelations::Organization', id: non_existing_record_id) end @@ -79,10 +79,10 @@ RSpec.describe Mutations::CustomerRelations::Contacts::Create do end end - context 'when organzation belongs to a different group' do + context 'when crm_organzation belongs to a different group' do before do - organization = create(:organization) - valid_params[:organization_id] = organization.to_global_id + crm_organization = create(:crm_organization) + valid_params[:organization_id] = crm_organization.to_global_id end it 'returns the relevant error' do diff --git a/spec/graphql/mutations/customer_relations/organizations/create_spec.rb b/spec/graphql/mutations/customer_relations/organizations/create_spec.rb index ffc9632350a..cf1ff2d5653 100644 --- a/spec/graphql/mutations/customer_relations/organizations/create_spec.rb +++ b/spec/graphql/mutations/customer_relations/organizations/create_spec.rb @@ -7,7 +7,7 @@ RSpec.describe Mutations::CustomerRelations::Organizations::Create do let_it_be(:group) { create(:group, :crm_enabled) } let(:valid_params) do - attributes_for(:organization, + attributes_for(:crm_organization, group: group, description: 'This company is super important!', default_rate: 1_000 diff --git a/spec/graphql/mutations/customer_relations/organizations/update_spec.rb b/spec/graphql/mutations/customer_relations/organizations/update_spec.rb index f0f37ee9c47..2fad320b497 100644 --- a/spec/graphql/mutations/customer_relations/organizations/update_spec.rb +++ b/spec/graphql/mutations/customer_relations/organizations/update_spec.rb @@ -10,10 +10,10 @@ RSpec.describe Mutations::CustomerRelations::Organizations::Update do let(:default_rate) { 1000.to_f } let(:description) { 'VIP' } let(:does_not_exist_or_no_permission) { Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR } - let(:organization) { create(:organization, group: group) } + let(:crm_organization) { create(:crm_organization, group: group) } let(:attributes) do { - id: organization.to_global_id, + id: crm_organization.to_global_id, name: name, default_rate: default_rate, description: description @@ -27,7 +27,7 @@ RSpec.describe Mutations::CustomerRelations::Organizations::Update do ) end - context 'when the user does not have permission to update an organization' do + context 'when the user does not have permission to update an crm_organization' do before do group.add_reporter(user) end @@ -38,7 +38,7 @@ RSpec.describe Mutations::CustomerRelations::Organizations::Update do end end - context 'when the organization does not exist' do + context 'when the crm_organization does not exist' do it 'raises an error' do attributes[:id] = "gid://gitlab/CustomerRelations::Organization/#{non_existing_record_id}" @@ -47,12 +47,12 @@ RSpec.describe Mutations::CustomerRelations::Organizations::Update do end end - context 'when the user has permission to update an organization' do + context 'when the user has permission to update an crm_organization' do before_all do group.add_developer(user) end - it 'updates the organization with correct values' do + it 'updates the crm_organization with correct values' do expect(resolve_mutation[:organization]).to have_attributes(attributes) end diff --git a/spec/graphql/resolvers/crm/contacts_resolver_spec.rb b/spec/graphql/resolvers/crm/contacts_resolver_spec.rb index c7c2d11e114..1a53f42633f 100644 --- a/spec/graphql/resolvers/crm/contacts_resolver_spec.rb +++ b/spec/graphql/resolvers/crm/contacts_resolver_spec.rb @@ -16,7 +16,7 @@ RSpec.describe Resolvers::Crm::ContactsResolver do last_name: "DEF", email: "ghi@test.com", description: "LMNO", - organization: create(:organization, group: group), + organization: create(:crm_organization, group: group), state: "inactive" ) end diff --git a/spec/graphql/resolvers/crm/organization_state_counts_resolver_spec.rb b/spec/graphql/resolvers/crm/organization_state_counts_resolver_spec.rb index c6ad4beeee0..f6c2040b7d0 100644 --- a/spec/graphql/resolvers/crm/organization_state_counts_resolver_spec.rb +++ b/spec/graphql/resolvers/crm/organization_state_counts_resolver_spec.rb @@ -9,10 +9,10 @@ RSpec.describe Resolvers::Crm::OrganizationStateCountsResolver do let_it_be(:group) { create(:group, :crm_enabled) } before_all do - create(:organization, group: group, name: "ABC Corp") - create(:organization, group: group, name: "123 Corp", state: 'inactive') - create_list(:organization, 3, group: group) - create_list(:organization, 2, group: group, state: 'inactive') + create(:crm_organization, group: group, name: "ABC Corp") + create(:crm_organization, group: group, name: "123 Corp", state: 'inactive') + create_list(:crm_organization, 3, group: group) + create_list(:crm_organization, 2, group: group, state: 'inactive') end describe '#resolve' do @@ -36,7 +36,7 @@ RSpec.describe Resolvers::Crm::OrganizationStateCountsResolver do context 'with a group' do context 'when no filter is provided' do - it 'returns the count of all organizations' do + it 'returns the count of all crm_organizations' do counts = resolve_counts(group) expect(counts['active']).to eq(4) expect(counts['inactive']).to eq(3) diff --git a/spec/graphql/resolvers/crm/organizations_resolver_spec.rb b/spec/graphql/resolvers/crm/organizations_resolver_spec.rb index d5980bf3c41..edc1986799a 100644 --- a/spec/graphql/resolvers/crm/organizations_resolver_spec.rb +++ b/spec/graphql/resolvers/crm/organizations_resolver_spec.rb @@ -8,18 +8,18 @@ RSpec.describe Resolvers::Crm::OrganizationsResolver do let_it_be(:user) { create(:user) } let_it_be(:group) { create(:group, :crm_enabled) } - let_it_be(:organization_a) do + let_it_be(:crm_organization_a) do create( - :organization, + :crm_organization, group: group, name: "ABC", state: "inactive" ) end - let_it_be(:organization_b) do + let_it_be(:crm_organization_b) do create( - :organization, + :crm_organization, group: group, name: "DEF", state: "active" @@ -28,23 +28,23 @@ RSpec.describe Resolvers::Crm::OrganizationsResolver do describe '#resolve' do context 'with unauthorized user' do - it 'does not rise an error and returns no organizations' do + it 'does not rise an error and returns no crm_organizations' do expect { resolve_organizations(group) }.not_to raise_error expect(resolve_organizations(group)).to be_empty end end context 'with authorized user' do - it 'does not rise an error and returns all organizations in the correct order' do + it 'does not rise an error and returns all crm_organizations in the correct order' do group.add_reporter(user) expect { resolve_organizations(group) }.not_to raise_error - expect(resolve_organizations(group)).to eq([organization_a, organization_b]) + expect(resolve_organizations(group)).to eq([crm_organization_a, crm_organization_b]) end end context 'without parent' do - it 'returns no organizations' do + it 'returns no crm_organizations' do expect(resolve_organizations(nil)).to be_empty end end @@ -55,40 +55,42 @@ RSpec.describe Resolvers::Crm::OrganizationsResolver do end context 'when no filter is provided' do - it 'returns all the organizations in the default order' do - expect(resolve_organizations(group)).to eq([organization_a, organization_b]) + it 'returns all the crm_organizations in the default order' do + expect(resolve_organizations(group)).to eq([crm_organization_a, crm_organization_b]) end end context 'when a sort is provided' do - it 'returns all the organizations in the correct order' do - expect(resolve_organizations(group, { sort: 'NAME_DESC' })).to eq([organization_b, organization_a]) + it 'returns all the crm_organizations in the correct order' do + expect(resolve_organizations(group, { sort: 'NAME_DESC' })).to eq([crm_organization_b, crm_organization_a]) end end context 'when filtering for all states' do - it 'returns all the organizations' do - expect(resolve_organizations(group, { state: 'all' })).to contain_exactly(organization_a, organization_b) + it 'returns all the crm_organizations' do + expect(resolve_organizations(group, { state: 'all' })).to contain_exactly( + crm_organization_a, crm_organization_b + ) end end context 'when search term is provided' do - it 'returns the correct organizations' do - expect(resolve_organizations(group, { search: "def" })).to contain_exactly(organization_b) + it 'returns the correct crm_organizations' do + expect(resolve_organizations(group, { search: "def" })).to contain_exactly(crm_organization_b) end end context 'when state is provided' do - it 'returns the correct organizations' do - expect(resolve_organizations(group, { state: :inactive })).to contain_exactly(organization_a) + it 'returns the correct crm_organizations' do + expect(resolve_organizations(group, { state: :inactive })).to contain_exactly(crm_organization_a) end end context 'when ids are provided' do - it 'returns the correct organizations' do + it 'returns the correct crm_organizations' do expect(resolve_organizations(group, { - ids: [organization_b.to_global_id] - })).to contain_exactly(organization_b) + ids: [crm_organization_b.to_global_id] + })).to contain_exactly(crm_organization_b) end end end diff --git a/spec/graphql/resolvers/project_issues_resolver_spec.rb b/spec/graphql/resolvers/project_issues_resolver_spec.rb index b2796ad9b18..a510baab5a9 100644 --- a/spec/graphql/resolvers/project_issues_resolver_spec.rb +++ b/spec/graphql/resolvers/project_issues_resolver_spec.rb @@ -359,9 +359,9 @@ RSpec.describe Resolvers::ProjectIssuesResolver do end describe 'filtering by crm' do - let_it_be(:organization) { create(:organization, group: group) } - let_it_be(:contact1) { create(:contact, group: group, organization: organization) } - let_it_be(:contact2) { create(:contact, group: group, organization: organization) } + let_it_be(:crm_organization) { create(:crm_organization, group: group) } + let_it_be(:contact1) { create(:contact, group: group, organization: crm_organization) } + let_it_be(:contact2) { create(:contact, group: group, organization: crm_organization) } let_it_be(:contact3) { create(:contact, group: group) } let_it_be(:crm_issue1) { create(:issue, project: project) } let_it_be(:crm_issue2) { create(:issue, project: project) } @@ -381,9 +381,9 @@ RSpec.describe Resolvers::ProjectIssuesResolver do end end - context 'when filtering by organization' do + context 'when filtering by crm_organization' do it 'returns only the issues for the contact' do - expect(resolve_issues({ crm_organization_id: organization.id })).to contain_exactly(crm_issue1, crm_issue2) + expect(resolve_issues({ crm_organization_id: crm_organization.id })).to contain_exactly(crm_issue1, crm_issue2) end end end diff --git a/spec/helpers/sidebars_helper_spec.rb b/spec/helpers/sidebars_helper_spec.rb index 5fbda3d77b0..24dc93922a8 100644 --- a/spec/helpers/sidebars_helper_spec.rb +++ b/spec/helpers/sidebars_helper_spec.rb @@ -250,10 +250,13 @@ RSpec.describe SidebarsHelper, feature_category: :navigation do name: "", items: [ { href: "/projects/new", text: "New project/repository", + component: nil, extraAttrs: extra_attrs.call("general_new_project") }, { href: "/groups/new", text: "New group", + component: nil, extraAttrs: extra_attrs.call("general_new_group") }, { href: "/-/snippets/new", text: "New snippet", + component: nil, extraAttrs: extra_attrs.call("general_new_snippet") } ] } @@ -279,10 +282,13 @@ RSpec.describe SidebarsHelper, feature_category: :navigation do name: "In this group", items: array_including( { href: "/projects/new", text: "New project/repository", + component: nil, extraAttrs: extra_attrs.call("new_project") }, { href: "/groups/new#create-group-pane", text: "New subgroup", + component: nil, extraAttrs: extra_attrs.call("new_subgroup") }, - { href: "", text: "Invite members", + { href: nil, text: "Invite members", + component: 'invite_members', extraAttrs: extra_attrs.call("invite") } ) ), @@ -290,10 +296,13 @@ RSpec.describe SidebarsHelper, feature_category: :navigation do name: "In GitLab", items: array_including( { href: "/projects/new", text: "New project/repository", + component: nil, extraAttrs: extra_attrs.call("general_new_project") }, { href: "/groups/new", text: "New group", + component: nil, extraAttrs: extra_attrs.call("general_new_group") }, { href: "/-/snippets/new", text: "New snippet", + component: nil, extraAttrs: extra_attrs.call("general_new_snippet") } ) ) diff --git a/spec/lib/gitlab/ci/jwt_v2_spec.rb b/spec/lib/gitlab/ci/jwt_v2_spec.rb index 21fd7e3adcf..5103e92806a 100644 --- a/spec/lib/gitlab/ci/jwt_v2_spec.rb +++ b/spec/lib/gitlab/ci/jwt_v2_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Gitlab::Ci::JwtV2 do +RSpec.describe Gitlab::Ci::JwtV2, feature_category: :continuous_integration do let(:namespace) { build_stubbed(:namespace) } let(:project) { build_stubbed(:project, namespace: namespace) } let(:user) do @@ -13,6 +13,7 @@ RSpec.describe Gitlab::Ci::JwtV2 do end let(:pipeline) { build_stubbed(:ci_pipeline, ref: 'auto-deploy-2020-03-19') } + let(:runner) { build_stubbed(:ci_runner) } let(:aud) { described_class::DEFAULT_AUD } let(:build) do @@ -20,7 +21,8 @@ RSpec.describe Gitlab::Ci::JwtV2 do :ci_build, project: project, user: user, - pipeline: pipeline + pipeline: pipeline, + runner: runner ) end @@ -58,5 +60,41 @@ RSpec.describe Gitlab::Ci::JwtV2 do expect(payload[:aud]).to eq('AWS') end end + + describe 'custom claims' do + describe 'runner_id' do + it 'is the ID of the runner executing the job' do + expect(payload[:runner_id]).to eq(runner.id) + end + end + + describe 'runner_environment' do + context 'when runner is gitlab-hosted' do + before do + allow(runner).to receive(:gitlab_hosted?).and_return(true) + end + + it "is #{described_class::GITLAB_HOSTED_RUNNER}" do + expect(payload[:runner_environment]).to eq(described_class::GITLAB_HOSTED_RUNNER) + end + end + + context 'when runner is self-hosted' do + before do + allow(runner).to receive(:gitlab_hosted?).and_return(false) + end + + it "is #{described_class::SELF_HOSTED_RUNNER}" do + expect(payload[:runner_environment]).to eq(described_class::SELF_HOSTED_RUNNER) + end + end + end + + describe 'sha' do + it 'is the commit revision the project is built for' do + expect(payload[:sha]).to eq(pipeline.sha) + end + end + end end end diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 99326dfd02d..970717b1212 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -3842,6 +3842,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def 'ID_TOKEN_1' => { aud: 'developers' }, 'ID_TOKEN_2' => { aud: 'maintainers' } }) + build.runner = build_stubbed(:ci_runner) end subject(:runner_vars) { build.variables.to_runner_variables } diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index 53a6a0e6a88..65c4174c1ea 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -2093,4 +2093,30 @@ RSpec.describe Ci::Runner, type: :model, feature_category: :runner do end end end + + describe '#gitlab_hosted?' do + using RSpec::Parameterized::TableSyntax + + subject(:runner) { build_stubbed(:ci_runner) } + + where(:saas, :runner_type, :expected_value) do + true | :instance_type | true + true | :group_type | false + true | :project_type | false + false | :instance_type | false + false | :group_type | false + false | :project_type | false + end + + with_them do + before do + allow(Gitlab).to receive(:com?).and_return(saas) + runner.runner_type = runner_type + end + + it 'returns the correct value based on saas and runner type' do + expect(runner.gitlab_hosted?).to eq(expected_value) + end + end + end end diff --git a/spec/models/customer_relations/contact_spec.rb b/spec/models/customer_relations/contact_spec.rb index 487af404a7c..6beb5323f60 100644 --- a/spec/models/customer_relations/contact_spec.rb +++ b/spec/models/customer_relations/contact_spec.rb @@ -241,8 +241,8 @@ RSpec.describe CustomerRelations::Contact, type: :model do end describe 'sorting' do - let_it_be(:organization_a) { create(:organization, name: 'a') } - let_it_be(:organization_b) { create(:organization, name: 'b') } + let_it_be(:crm_organization_a) { create(:crm_organization, name: 'a') } + let_it_be(:crm_organization_b) { create(:crm_organization, name: 'b') } let_it_be(:contact_a) { create(:contact, group: group, first_name: "c", last_name: "d") } let_it_be(:contact_b) do create(:contact, @@ -250,7 +250,7 @@ RSpec.describe CustomerRelations::Contact, type: :model do first_name: "a", last_name: "b", phone: "123", - organization: organization_a) + organization: crm_organization_a) end let_it_be(:contact_c) do @@ -259,7 +259,7 @@ RSpec.describe CustomerRelations::Contact, type: :model do first_name: "e", last_name: "d", phone: "456", - organization: organization_b) + organization: crm_organization_b) end describe '.sort_by_name' do diff --git a/spec/models/customer_relations/organization_spec.rb b/spec/models/customer_relations/organization_spec.rb index d19a0bdf6c7..7fab9fd0e80 100644 --- a/spec/models/customer_relations/organization_spec.rb +++ b/spec/models/customer_relations/organization_spec.rb @@ -10,7 +10,7 @@ RSpec.describe CustomerRelations::Organization, type: :model do end describe 'validations' do - subject { build(:organization) } + subject { build(:crm_organization) } it { is_expected.to validate_presence_of(:group) } it { is_expected.to validate_presence_of(:name) } @@ -21,13 +21,13 @@ RSpec.describe CustomerRelations::Organization, type: :model do describe '#root_group' do context 'when root group' do - subject { build(:organization, group: group) } + subject { build(:crm_organization, group: group) } it { is_expected.to be_valid } end context 'when subgroup' do - subject { build(:organization, group: create(:group, parent: group)) } + subject { build(:crm_organization, group: create(:group, parent: group)) } it { is_expected.to be_invalid } end @@ -43,23 +43,25 @@ RSpec.describe CustomerRelations::Organization, type: :model do end describe '#find_by_name' do - let!(:organiztion1) { create(:organization, group: group, name: 'Test') } - let!(:organiztion2) { create(:organization, group: create(:group), name: 'Test') } + let!(:crm_organiztion1) { create(:crm_organization, group: group, name: 'Test') } + let!(:crm_organiztion2) { create(:crm_organization, group: create(:group), name: 'Test') } it 'strips name' do - expect(described_class.find_by_name(group.id, 'TEST')).to eq([organiztion1]) + expect(described_class.find_by_name(group.id, 'TEST')).to eq([crm_organiztion1]) end end describe '#self.move_to_root_group' do let!(:old_root_group) { create(:group) } - let!(:organizations) { create_list(:organization, 4, group: old_root_group) } + let!(:crm_organizations) { create_list(:crm_organization, 4, group: old_root_group) } let!(:new_root_group) { create(:group) } - let!(:contact1) { create(:contact, group: new_root_group, organization: organizations[0]) } - let!(:contact2) { create(:contact, group: new_root_group, organization: organizations[1]) } + let!(:contact1) { create(:contact, group: new_root_group, organization: crm_organizations[0]) } + let!(:contact2) { create(:contact, group: new_root_group, organization: crm_organizations[1]) } - let!(:dupe_organization1) { create(:organization, group: new_root_group, name: organizations[1].name) } - let!(:dupe_organization2) { create(:organization, group: new_root_group, name: organizations[3].name.upcase) } + let!(:dupe_crm_organization1) { create(:crm_organization, group: new_root_group, name: crm_organizations[1].name) } + let!(:dupe_crm_organization2) do + create(:crm_organization, group: new_root_group, name: crm_organizations[3].name.upcase) + end before do old_root_group.update!(parent: new_root_group) @@ -67,22 +69,22 @@ RSpec.describe CustomerRelations::Organization, type: :model do end it 'moves organizations with unique names and deletes the rest' do - expect(organizations[0].reload.group_id).to eq(new_root_group.id) - expect(organizations[2].reload.group_id).to eq(new_root_group.id) - expect { organizations[1].reload }.to raise_error(ActiveRecord::RecordNotFound) - expect { organizations[3].reload }.to raise_error(ActiveRecord::RecordNotFound) + expect(crm_organizations[0].reload.group_id).to eq(new_root_group.id) + expect(crm_organizations[2].reload.group_id).to eq(new_root_group.id) + expect { crm_organizations[1].reload }.to raise_error(ActiveRecord::RecordNotFound) + expect { crm_organizations[3].reload }.to raise_error(ActiveRecord::RecordNotFound) end it 'updates contact.organization_id for dupes and leaves the rest untouched' do - expect(contact1.reload.organization_id).to eq(organizations[0].id) - expect(contact2.reload.organization_id).to eq(dupe_organization1.id) + expect(contact1.reload.organization_id).to eq(crm_organizations[0].id) + expect(contact2.reload.organization_id).to eq(dupe_crm_organization1.id) end end describe '.search' do - let_it_be(:organization_a) do + let_it_be(:crm_organization_a) do create( - :organization, + :crm_organization, group: group, name: "DEF", description: "ghi_st", @@ -90,9 +92,9 @@ RSpec.describe CustomerRelations::Organization, type: :model do ) end - let_it_be(:organization_b) do + let_it_be(:crm_organization_b) do create( - :organization, + :crm_organization, group: group, name: "ABC_st", description: "JKL", @@ -106,7 +108,7 @@ RSpec.describe CustomerRelations::Organization, type: :model do let(:search_term) { "" } it 'returns all group organizations' do - expect(found_organizations).to contain_exactly(organization_a, organization_b) + expect(found_organizations).to contain_exactly(crm_organization_a, crm_organization_b) end end @@ -114,42 +116,42 @@ RSpec.describe CustomerRelations::Organization, type: :model do context 'when searching for name' do let(:search_term) { "aBc" } - it { is_expected.to contain_exactly(organization_b) } + it { is_expected.to contain_exactly(crm_organization_b) } end context 'when searching for description' do let(:search_term) { "ghI" } - it { is_expected.to contain_exactly(organization_a) } + it { is_expected.to contain_exactly(crm_organization_a) } end context 'when searching for name and description' do let(:search_term) { "_st" } - it { is_expected.to contain_exactly(organization_a, organization_b) } + it { is_expected.to contain_exactly(crm_organization_a, crm_organization_b) } end end end describe '.search_by_state' do - let_it_be(:organization_a) { create(:organization, group: group, state: "inactive") } - let_it_be(:organization_b) { create(:organization, group: group, state: "active") } + let_it_be(:crm_organization_a) { create(:crm_organization, group: group, state: "inactive") } + let_it_be(:crm_organization_b) { create(:crm_organization, group: group, state: "active") } context 'when searching for organizations state' do it 'returns only inactive organizations' do - expect(group.organizations.search_by_state(:inactive)).to contain_exactly(organization_a) + expect(group.organizations.search_by_state(:inactive)).to contain_exactly(crm_organization_a) end it 'returns only active organizations' do - expect(group.organizations.search_by_state(:active)).to contain_exactly(organization_b) + expect(group.organizations.search_by_state(:active)).to contain_exactly(crm_organization_b) end end end describe '.counts_by_state' do before do - create_list(:organization, 3, group: group) - create_list(:organization, 2, group: group, state: 'inactive') + create_list(:crm_organization, 3, group: group) + create_list(:crm_organization, 2, group: group, state: 'inactive') end it 'returns correct organization counts' do @@ -168,20 +170,20 @@ RSpec.describe CustomerRelations::Organization, type: :model do end describe 'sorting' do - let_it_be(:organization_a) { create(:organization, group: group, name: "c", description: "1") } - let_it_be(:organization_b) { create(:organization, group: group, name: "a") } - let_it_be(:organization_c) { create(:organization, group: group, name: "b", description: "2") } + let_it_be(:crm_organization_a) { create(:crm_organization, group: group, name: "c", description: "1") } + let_it_be(:crm_organization_b) { create(:crm_organization, group: group, name: "a") } + let_it_be(:crm_organization_c) { create(:crm_organization, group: group, name: "b", description: "2") } describe '.sort_by_name' do it 'sorts them by name in ascendent order' do - expect(group.organizations.sort_by_name).to eq([organization_b, organization_c, organization_a]) + expect(group.organizations.sort_by_name).to eq([crm_organization_b, crm_organization_c, crm_organization_a]) end end describe '.sort_by_field' do it 'sorts them by description in descending order' do expect(group.organizations.sort_by_field('description', :desc)) - .to eq([organization_c, organization_a, organization_b]) + .to eq([crm_organization_c, crm_organization_a, crm_organization_b]) end end end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 0bf4540f535..f7f9ec8e7a7 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -3162,11 +3162,11 @@ RSpec.describe Group, feature_category: :subgroups do describe '.organizations' do it 'returns organizations belonging to the group' do - organization1 = create(:organization, group: group) - create(:organization) - organization3 = create(:organization, group: group) + crm_organization1 = create(:crm_organization, group: group) + create(:crm_organization) + crm_organization3 = create(:crm_organization, group: group) - expect(group.organizations).to contain_exactly(organization1, organization3) + expect(group.organizations).to contain_exactly(crm_organization1, crm_organization3) end end diff --git a/spec/requests/api/deployments_spec.rb b/spec/requests/api/deployments_spec.rb index efe76c9cfda..692b4c76ea3 100644 --- a/spec/requests/api/deployments_spec.rb +++ b/spec/requests/api/deployments_spec.rb @@ -51,7 +51,7 @@ RSpec.describe API::Deployments, feature_category: :continuous_delivery do perform_request({ updated_before: 30.minutes.ago, updated_after: 90.minutes.ago, order_by: :id }) expect(response).to have_gitlab_http_status(:bad_request) - expect(json_response['message']).to include('`updated_at` filter and `updated_at` sorting must be paired') + expect(json_response['message']).to include('`updated_at` filter requires `updated_at` sort') end end end diff --git a/spec/requests/api/graphql/ci/runner_spec.rb b/spec/requests/api/graphql/ci/runner_spec.rb index 68c722c5e9d..65afeb2cb56 100644 --- a/spec/requests/api/graphql/ci/runner_spec.rb +++ b/spec/requests/api/graphql/ci/runner_spec.rb @@ -456,6 +456,44 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner_fleet do it_behaves_like 'has no register url' end end + + context 'with a project runner' do + context 'with registration available' do + let_it_be(:runner) { create(:ci_runner, :project, projects: [project1], registration_type: :authenticated_user) } + + it_behaves_like 'has register url' do + let(:expected_url) { "http://localhost/#{project1.full_path}/-/runners/#{runner.id}/register" } + end + end + + context 'with no project' do + let(:destroyed_project) { create(:project) } + let(:runner) { create(:ci_runner, :project, projects: [destroyed_project], registration_type: :authenticated_user) } + + before do + destroyed_project.destroy! + end + + it_behaves_like 'has no register url' + end + + context 'with no registration available' do + let_it_be(:runner) { create(:ci_runner, :project, projects: [project1]) } + + it_behaves_like 'has no register url' + end + + context 'with no access' do + let_it_be(:user) { create(:user) } + let_it_be(:runner) { create(:ci_runner, :project, projects: [project1], registration_type: :authenticated_user) } + + before do + group.add_maintainer(user) + end + + it_behaves_like 'has no register url' + end + end end describe 'for runner with status' do diff --git a/spec/requests/api/graphql/query_spec.rb b/spec/requests/api/graphql/query_spec.rb index d93077b1c70..0602cfec149 100644 --- a/spec/requests/api/graphql/query_spec.rb +++ b/spec/requests/api/graphql/query_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe 'Query', feature_category: :shared do include GraphqlHelpers - let_it_be(:project) { create(:project) } + let_it_be(:project) { create(:project, public_builds: false) } let_it_be(:issue) { create(:issue, project: project) } let_it_be(:developer) { create(:user) } @@ -116,4 +116,36 @@ RSpec.describe 'Query', feature_category: :shared do end end end + + describe '.ciPipelineStage' do + let_it_be(:ci_stage) { create(:ci_stage, name: 'graphql test stage', project: project) } + + let(:query) do + <<~GRAPHQL + { + ciPipelineStage(id: "#{ci_stage.to_global_id}") { + name + } + } + GRAPHQL + end + + context 'when the current user has access to the stage' do + it 'fetches the stage for the given ID' do + project.add_developer(developer) + + post_graphql(query, current_user: developer) + + expect(graphql_data.dig('ciPipelineStage', 'name')).to eq('graphql test stage') + end + end + + context 'when the current user does not have access to the stage' do + it 'returns nil' do + post_graphql(query, current_user: developer) + + expect(graphql_data['ciPipelineStage']).to be_nil + end + end + end end diff --git a/spec/requests/api/terraform/state_spec.rb b/spec/requests/api/terraform/state_spec.rb index bce004dcd48..4c9f930df2f 100644 --- a/spec/requests/api/terraform/state_spec.rb +++ b/spec/requests/api/terraform/state_spec.rb @@ -114,17 +114,6 @@ RSpec.describe API::Terraform::State, :snowplow, feature_category: :infrastructu end end - context 'allow_dots_on_tf_state_names is disabled, and the state name contains a dot' do - let(:state_name) { 'state-name-with-dot' } - let(:state_path) { "/projects/#{project_id}/terraform/state/#{state_name}.tfstate" } - - before do - stub_feature_flags(allow_dots_on_tf_state_names: false) - end - - it_behaves_like 'can access terraform state' - end - context 'for a project that does not exist' do let(:project_id) { '0000' } @@ -277,21 +266,6 @@ RSpec.describe API::Terraform::State, :snowplow, feature_category: :infrastructu expect(Gitlab::Json.parse(response.body)).to be_empty end end - - context 'allow_dots_on_tf_state_names is disabled, and the state name contains a dot' do - let(:non_existing_state_name) { 'state-name-with-dot.tfstate' } - - before do - stub_feature_flags(allow_dots_on_tf_state_names: false) - end - - it 'strips characters after the dot' do - expect { request }.to change { Terraform::State.count }.by(1) - - expect(response).to have_gitlab_http_status(:ok) - expect(Terraform::State.last.name).to eq('state-name-with-dot') - end - end end context 'without body' do @@ -399,18 +373,6 @@ RSpec.describe API::Terraform::State, :snowplow, feature_category: :infrastructu it_behaves_like 'schedules the state for deletion' end - context 'allow_dots_on_tf_state_names is disabled, and the state name contains a dot' do - let(:state_name) { 'state-name-with-dot' } - let(:state_name_with_dot) { "#{state_name}.tfstate" } - let(:state_path) { "/projects/#{project_id}/terraform/state/#{state_name_with_dot}" } - - before do - stub_feature_flags(allow_dots_on_tf_state_names: false) - end - - it_behaves_like 'schedules the state for deletion' - end - context 'with invalid state name' do let(:state_name) { 'foo/bar' } @@ -500,30 +462,10 @@ RSpec.describe API::Terraform::State, :snowplow, feature_category: :infrastructu context 'with a dot in the state name' do let(:state_name) { 'test.state' } - context 'with allow_dots_on_tf_state_names ff enabled' do - before do - stub_feature_flags(allow_dots_on_tf_state_names: true) - end - - let(:state_name) { 'test.state' } - - it 'locks the terraform state' do - request - - expect(response).to have_gitlab_http_status(:ok) - end - end - - context 'with allow_dots_on_tf_state_names ff disabled' do - before do - stub_feature_flags(allow_dots_on_tf_state_names: false) - end - - it 'returns 404' do - request + it 'locks the terraform state' do + request - expect(response).to have_gitlab_http_status(:not_found) - end + expect(response).to have_gitlab_http_status(:ok) end end end @@ -544,7 +486,6 @@ RSpec.describe API::Terraform::State, :snowplow, feature_category: :infrastructu before do state.lock_xid = '123.456' state.save! - stub_feature_flags(allow_dots_on_tf_state_names: true) end subject(:request) { delete api("#{state_path}/lock"), headers: auth_header, params: params } @@ -575,23 +516,6 @@ RSpec.describe API::Terraform::State, :snowplow, feature_category: :infrastructu end end - context 'with allow_dots_on_tf_state_names ff disabled' do - before do - stub_feature_flags(allow_dots_on_tf_state_names: false) - end - - context 'with dots in the state name' do - let(:lock_id) { '123.456' } - let(:state_name) { 'test.state' } - - it 'returns 404' do - request - - expect(response).to have_gitlab_http_status(:not_found) - end - end - end - context 'with no lock id (force-unlock)' do let(:params) { {} } diff --git a/spec/services/ci/create_downstream_pipeline_service_spec.rb b/spec/services/ci/create_downstream_pipeline_service_spec.rb index 7b576339c61..6da6ec3379a 100644 --- a/spec/services/ci/create_downstream_pipeline_service_spec.rb +++ b/spec/services/ci/create_downstream_pipeline_service_spec.rb @@ -26,10 +26,13 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute', feature_category end let(:bridge) do - create(:ci_bridge, status: :pending, - user: user, - options: trigger, - pipeline: upstream_pipeline) + create( + :ci_bridge, + status: :pending, + user: user, + options: trigger, + pipeline: upstream_pipeline + ) end let(:service) { described_class.new(upstream_project, user) } @@ -704,8 +707,7 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute', feature_category context 'when user does not have access to push protected branch of downstream project' do before do - create(:protected_branch, :maintainers_can_push, - project: downstream_project, name: 'feature') + create(:protected_branch, :maintainers_can_push, project: downstream_project, name: 'feature') end it 'changes status of the bridge build' do diff --git a/spec/services/ci/create_pipeline_service/include_spec.rb b/spec/services/ci/create_pipeline_service/include_spec.rb index 86f71be5971..1280ab4b7bd 100644 --- a/spec/services/ci/create_pipeline_service/include_spec.rb +++ b/spec/services/ci/create_pipeline_service/include_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Ci::CreatePipelineService, -:yaml_processor_feature_flag_corectness, feature_category: :pipeline_composition do + :yaml_processor_feature_flag_corectness, feature_category: :pipeline_composition do include RepoHelpers context 'include:' do diff --git a/spec/services/ci/create_pipeline_service/logger_spec.rb b/spec/services/ci/create_pipeline_service/logger_spec.rb index 082a09db69e..6a1987fcc7c 100644 --- a/spec/services/ci/create_pipeline_service/logger_spec.rb +++ b/spec/services/ci/create_pipeline_service/logger_spec.rb @@ -3,8 +3,8 @@ require 'spec_helper' RSpec.describe Ci::CreatePipelineService, # rubocop: disable RSpec/FilePath - :yaml_processor_feature_flag_corectness, - feature_category: :continuous_integration do + :yaml_processor_feature_flag_corectness, + feature_category: :continuous_integration do describe 'pipeline logger' do let_it_be(:project) { create(:project, :repository) } let_it_be(:user) { project.first_owner } diff --git a/spec/services/ci/create_pipeline_service/merge_requests_spec.rb b/spec/services/ci/create_pipeline_service/merge_requests_spec.rb index 8d04b3d020f..25f6e43d600 100644 --- a/spec/services/ci/create_pipeline_service/merge_requests_spec.rb +++ b/spec/services/ci/create_pipeline_service/merge_requests_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness, -feature_category: :continuous_integration do + feature_category: :continuous_integration do context 'merge requests handling' do let_it_be(:project) { create(:project, :repository) } let_it_be(:user) { project.first_owner } @@ -31,11 +31,13 @@ feature_category: :continuous_integration do context 'when pushing a change' do context 'when a merge request already exists' do let!(:merge_request) do - create(:merge_request, - source_project: project, - source_branch: 'feature', - target_project: project, - target_branch: 'master') + create( + :merge_request, + source_project: project, + source_branch: 'feature', + target_project: project, + target_branch: 'master' + ) end it 'does not create a pipeline' do diff --git a/spec/services/ci/create_pipeline_service/partitioning_spec.rb b/spec/services/ci/create_pipeline_service/partitioning_spec.rb index a87135cefdd..70c4eb49698 100644 --- a/spec/services/ci/create_pipeline_service/partitioning_spec.rb +++ b/spec/services/ci/create_pipeline_service/partitioning_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness, :aggregate_failures, -:ci_partitionable, feature_category: :continuous_integration do + :ci_partitionable, feature_category: :continuous_integration do let_it_be(:project) { create(:project, :repository) } let_it_be(:user) { project.first_owner } diff --git a/spec/services/ci/create_pipeline_service/rate_limit_spec.rb b/spec/services/ci/create_pipeline_service/rate_limit_spec.rb index 31bea10f062..26a9484dbc4 100644 --- a/spec/services/ci/create_pipeline_service/rate_limit_spec.rb +++ b/spec/services/ci/create_pipeline_service/rate_limit_spec.rb @@ -2,9 +2,9 @@ require 'spec_helper' RSpec.describe Ci::CreatePipelineService, :freeze_time, - :clean_gitlab_redis_rate_limiting, - :yaml_processor_feature_flag_corectness, - feature_category: :continuous_integration do + :clean_gitlab_redis_rate_limiting, + :yaml_processor_feature_flag_corectness, + feature_category: :continuous_integration do describe 'rate limiting' do let_it_be(:project) { create(:project, :repository) } let_it_be(:user) { project.first_owner } diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb index acdbeee40a9..b08dda72a69 100644 --- a/spec/services/ci/create_pipeline_service_spec.rb +++ b/spec/services/ci/create_pipeline_service_spec.rb @@ -1193,8 +1193,10 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes context 'when pipeline is running for a tag' do before do - config = YAML.dump(test: { script: 'test', only: ['branches'] }, - deploy: { script: 'deploy', only: ['tags'] }) + config = YAML.dump( + test: { script: 'test', only: ['branches'] }, + deploy: { script: 'deploy', only: ['tags'] } + ) stub_ci_pipeline_yaml_file(config) end @@ -1369,11 +1371,13 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes describe 'Pipeline for external pull requests' do let(:response) do - execute_service(source: source, - external_pull_request: pull_request, - ref: ref_name, - source_sha: source_sha, - target_sha: target_sha) + execute_service( + source: source, + external_pull_request: pull_request, + ref: ref_name, + source_sha: source_sha, + target_sha: target_sha + ) end let(:pipeline) { response.payload } @@ -1525,11 +1529,13 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes describe 'Pipelines for merge requests' do let(:response) do - execute_service(source: source, - merge_request: merge_request, - ref: ref_name, - source_sha: source_sha, - target_sha: target_sha) + execute_service( + source: source, + merge_request: merge_request, + ref: ref_name, + source_sha: source_sha, + target_sha: target_sha + ) end let(:pipeline) { response.payload } diff --git a/spec/services/ci/job_artifacts/destroy_all_expired_service_spec.rb b/spec/services/ci/job_artifacts/destroy_all_expired_service_spec.rb index d1ec2a1d3a6..cdbb0c0f8ce 100644 --- a/spec/services/ci/job_artifacts/destroy_all_expired_service_spec.rb +++ b/spec/services/ci/job_artifacts/destroy_all_expired_service_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Ci::JobArtifacts::DestroyAllExpiredService, :clean_gitlab_redis_shared_state, -feature_category: :build_artifacts do + feature_category: :build_artifacts do include ExclusiveLeaseHelpers let(:service) { described_class.new } diff --git a/spec/services/ci/job_artifacts/track_artifact_report_service_spec.rb b/spec/services/ci/job_artifacts/track_artifact_report_service_spec.rb index 5355bec2d6c..af0c9b0833d 100644 --- a/spec/services/ci/job_artifacts/track_artifact_report_service_spec.rb +++ b/spec/services/ci/job_artifacts/track_artifact_report_service_spec.rb @@ -33,13 +33,9 @@ RSpec.describe Ci::JobArtifacts::TrackArtifactReportService, feature_category: : .with(test_event_name_1, values: user1.id) .and_call_original - expect { track_artifact_report } - .to change { - counter.unique_events(event_names: test_event_name_1, - start_date: start_time, - end_date: end_time) - } - .by 1 + expect { track_artifact_report }.to change { + counter.unique_events(event_names: test_event_name_1, start_date: start_time, end_date: end_time) + }.by 1 end end @@ -81,13 +77,9 @@ RSpec.describe Ci::JobArtifacts::TrackArtifactReportService, feature_category: : expect do described_class.new.execute(pipeline1) described_class.new.execute(pipeline2) - end - .to change { - counter.unique_events(event_names: test_event_name_1, - start_date: start_time, - end_date: end_time) - } - .by 1 + end.to change { + counter.unique_events(event_names: test_event_name_1, start_date: start_time, end_date: end_time) + }.by 1 end end @@ -109,13 +101,9 @@ RSpec.describe Ci::JobArtifacts::TrackArtifactReportService, feature_category: : expect do described_class.new.execute(pipeline1) described_class.new.execute(pipeline2) - end - .to change { - counter.unique_events(event_names: test_event_name_1, - start_date: start_time, - end_date: end_time) - } - .by 2 + end.to change { + counter.unique_events(event_names: test_event_name_1, start_date: start_time, end_date: end_time) + }.by 2 end end @@ -134,13 +122,9 @@ RSpec.describe Ci::JobArtifacts::TrackArtifactReportService, feature_category: : .with(test_event_name_2, values: user1.id) .and_call_original - expect { track_artifact_report } - .to change { - counter.unique_events(event_names: test_event_name_2, - start_date: start_time, - end_date: end_time) - } - .by 1 + expect { track_artifact_report }.to change { + counter.unique_events(event_names: test_event_name_2, start_date: start_time, end_date: end_time) + }.by 1 end end @@ -158,13 +142,9 @@ RSpec.describe Ci::JobArtifacts::TrackArtifactReportService, feature_category: : expect do described_class.new.execute(pipeline1) described_class.new.execute(pipeline2) - end - .to change { - counter.unique_events(event_names: test_event_name_2, - start_date: start_time, - end_date: end_time) - } - .by 1 + end.to change { + counter.unique_events(event_names: test_event_name_2, start_date: start_time, end_date: end_time) + }.by 1 end end @@ -186,13 +166,9 @@ RSpec.describe Ci::JobArtifacts::TrackArtifactReportService, feature_category: : expect do described_class.new.execute(pipeline1) described_class.new.execute(pipeline2) - end - .to change { - counter.unique_events(event_names: test_event_name_2, - start_date: start_time, - end_date: end_time) - } - .by 2 + end.to change { + counter.unique_events(event_names: test_event_name_2, start_date: start_time, end_date: end_time) + }.by 2 end end end diff --git a/spec/services/ci/list_config_variables_service_spec.rb b/spec/services/ci/list_config_variables_service_spec.rb index febb1533b0f..07c9085b83a 100644 --- a/spec/services/ci/list_config_variables_service_spec.rb +++ b/spec/services/ci/list_config_variables_service_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Ci::ListConfigVariablesService, -:use_clean_rails_memory_store_caching, feature_category: :secrets_management do + :use_clean_rails_memory_store_caching, feature_category: :secrets_management do include ReactiveCachingHelpers let(:ci_config) { {} } diff --git a/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb b/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb index d0496acc6fe..8c52603e769 100644 --- a/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb +++ b/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb @@ -646,8 +646,7 @@ RSpec.describe Ci::PipelineProcessing::AtomicProcessingService, feature_category # Users need ability to merge into a branch in order to trigger # protected manual actions. # - create(:protected_branch, :developers_can_merge, - name: 'master', project: project) + create(:protected_branch, :developers_can_merge, name: 'master', project: project) end it 'properly processes entire pipeline' do diff --git a/spec/services/ci/play_build_service_spec.rb b/spec/services/ci/play_build_service_spec.rb index f439538b4cc..46b6622d6ec 100644 --- a/spec/services/ci/play_build_service_spec.rb +++ b/spec/services/ci/play_build_service_spec.rb @@ -16,8 +16,7 @@ RSpec.describe Ci::PlayBuildService, '#execute', feature_category: :continuous_i let(:project) { create(:project) } it 'allows user to play build if protected branch rules are met' do - create(:protected_branch, :developers_can_merge, - name: build.ref, project: project) + create(:protected_branch, :developers_can_merge, name: build.ref, project: project) service.execute(build) diff --git a/spec/services/ci/play_manual_stage_service_spec.rb b/spec/services/ci/play_manual_stage_service_spec.rb index 1aca4ffec2d..dd8e037b129 100644 --- a/spec/services/ci/play_manual_stage_service_spec.rb +++ b/spec/services/ci/play_manual_stage_service_spec.rb @@ -11,10 +11,7 @@ RSpec.describe Ci::PlayManualStageService, '#execute', feature_category: :contin let(:stage_status) { 'manual' } let(:stage) do - create(:ci_stage, - pipeline: pipeline, - project: project, - name: 'test') + create(:ci_stage, pipeline: pipeline, project: project, name: 'test') end before do diff --git a/spec/services/ci/process_sync_events_service_spec.rb b/spec/services/ci/process_sync_events_service_spec.rb index c042a9bd46e..84b6d7d96f6 100644 --- a/spec/services/ci/process_sync_events_service_spec.rb +++ b/spec/services/ci/process_sync_events_service_spec.rb @@ -147,8 +147,7 @@ RSpec.describe Ci::ProcessSyncEventsService, feature_category: :continuous_integ context 'when the FFs use_traversal_ids and use_traversal_ids_for_ancestors are disabled' do before do - stub_feature_flags(use_traversal_ids: false, - use_traversal_ids_for_ancestors: false) + stub_feature_flags(use_traversal_ids: false, use_traversal_ids_for_ancestors: false) end it_behaves_like 'event consuming' diff --git a/spec/services/ci/retry_job_service_spec.rb b/spec/services/ci/retry_job_service_spec.rb index fed66bc535d..f15f4a16d4f 100644 --- a/spec/services/ci/retry_job_service_spec.rb +++ b/spec/services/ci/retry_job_service_spec.rb @@ -51,11 +51,13 @@ RSpec.describe Ci::RetryJobService, feature_category: :continuous_integration do let_it_be(:another_pipeline) { create(:ci_empty_pipeline, project: project) } let_it_be(:job_to_clone) do - create(:ci_build, :failed, :picked, :expired, :erased, :queued, :coverage, :tags, - :allowed_to_fail, :on_tag, :triggered, :teardown_environment, :resource_group, - description: 'my-job', ci_stage: stage, - pipeline: pipeline, auto_canceled_by: another_pipeline, - scheduled_at: 10.seconds.since) + create( + :ci_build, :failed, :picked, :expired, :erased, :queued, :coverage, :tags, + :allowed_to_fail, :on_tag, :triggered, :teardown_environment, :resource_group, + description: 'my-job', ci_stage: stage, + pipeline: pipeline, auto_canceled_by: another_pipeline, + scheduled_at: 10.seconds.since + ) end before do @@ -236,8 +238,7 @@ RSpec.describe Ci::RetryJobService, feature_category: :continuous_integration do context 'when a build with a deployment is retried' do let!(:job) do - create(:ci_build, :with_deployment, :deploy_to_production, - pipeline: pipeline, ci_stage: stage) + create(:ci_build, :with_deployment, :deploy_to_production, pipeline: pipeline, ci_stage: stage) end it 'creates a new deployment' do diff --git a/spec/services/ci/retry_pipeline_service_spec.rb b/spec/services/ci/retry_pipeline_service_spec.rb index 51a894640ab..fc2c66e7f73 100644 --- a/spec/services/ci/retry_pipeline_service_spec.rb +++ b/spec/services/ci/retry_pipeline_service_spec.rb @@ -19,8 +19,7 @@ RSpec.describe Ci::RetryPipelineService, '#execute', feature_category: :continuo before do project.add_developer(user) - create(:protected_branch, :developers_can_merge, - name: pipeline.ref, project: project) + create(:protected_branch, :developers_can_merge, name: pipeline.ref, project: project) end context 'when there are already retried jobs present' do @@ -408,8 +407,7 @@ RSpec.describe Ci::RetryPipelineService, '#execute', feature_category: :continuo context 'when user is not allowed to trigger manual action' do before do project.add_developer(user) - create(:protected_branch, :maintainers_can_push, - name: pipeline.ref, project: project) + create(:protected_branch, :maintainers_can_push, name: pipeline.ref, project: project) end context 'when there is a failed manual action present' do @@ -490,11 +488,15 @@ RSpec.describe Ci::RetryPipelineService, '#execute', feature_category: :continuo end def create_processable(type, name, status, stage, **opts) - create(type, name: name, - status: status, - ci_stage: stage, - stage_idx: stage.position, - pipeline: pipeline, **opts) do |_job| + create( + type, + name: name, + status: status, + ci_stage: stage, + stage_idx: stage.position, + pipeline: pipeline, + **opts + ) do |_job| ::Ci::ProcessPipelineService.new(pipeline).execute end end diff --git a/spec/services/ci/run_scheduled_build_service_spec.rb b/spec/services/ci/run_scheduled_build_service_spec.rb index e3ae1d93e5e..33f9efcb89f 100644 --- a/spec/services/ci/run_scheduled_build_service_spec.rb +++ b/spec/services/ci/run_scheduled_build_service_spec.rb @@ -13,8 +13,7 @@ RSpec.describe Ci::RunScheduledBuildService, feature_category: :continuous_integ before do project.add_developer(user) - create(:protected_branch, :developers_can_merge, - name: pipeline.ref, project: project) + create(:protected_branch, :developers_can_merge, name: pipeline.ref, project: project) end context 'when build is scheduled' do diff --git a/spec/services/customer_relations/contacts/create_service_spec.rb b/spec/services/customer_relations/contacts/create_service_spec.rb index 610143586e3..91aa51385e7 100644 --- a/spec/services/customer_relations/contacts/create_service_spec.rb +++ b/spec/services/customer_relations/contacts/create_service_spec.rb @@ -50,8 +50,8 @@ RSpec.describe CustomerRelations::Contacts::CreateService, feature_category: :se end it 'returns an error when the organization belongs to a different group' do - organization = create(:organization) - params[:organization_id] = organization.id + crm_organization = create(:crm_organization) + params[:organization_id] = crm_organization.id expect(response).to be_error expect(response.message).to match_array([not_found_or_does_not_belong]) diff --git a/spec/services/customer_relations/organizations/create_service_spec.rb b/spec/services/customer_relations/organizations/create_service_spec.rb index 3b1ae5606b1..8748fe44763 100644 --- a/spec/services/customer_relations/organizations/create_service_spec.rb +++ b/spec/services/customer_relations/organizations/create_service_spec.rb @@ -7,11 +7,11 @@ RSpec.describe CustomerRelations::Organizations::CreateService, feature_category let_it_be(:user) { create(:user) } let(:group) { create(:group, :crm_enabled) } - let(:params) { attributes_for(:organization, group: group) } + let(:params) { attributes_for(:crm_organization, group: group) } subject(:response) { described_class.new(group: group, current_user: user, params: params).execute } - it 'creates an organization' do + it 'creates a crm_organization' do group.add_developer(user) expect(response).to be_success @@ -24,7 +24,7 @@ RSpec.describe CustomerRelations::Organizations::CreateService, feature_category expect(response.message).to match_array(['You have insufficient permissions to create an organization for this group']) end - it 'returns an error when the organization is not persisted' do + it 'returns an error when the crm_organization is not persisted' do group.add_developer(user) params[:name] = nil diff --git a/spec/services/customer_relations/organizations/update_service_spec.rb b/spec/services/customer_relations/organizations/update_service_spec.rb index ac71a211bf8..f11b99b101e 100644 --- a/spec/services/customer_relations/organizations/update_service_spec.rb +++ b/spec/services/customer_relations/organizations/update_service_spec.rb @@ -5,9 +5,9 @@ require 'spec_helper' RSpec.describe CustomerRelations::Organizations::UpdateService, feature_category: :service_desk do let_it_be(:user) { create(:user) } - let(:organization) { create(:organization, name: 'Test', group: group, state: 'active') } + let(:crm_organization) { create(:crm_organization, name: 'Test', group: group, state: 'active') } - subject(:update) { described_class.new(group: group, current_user: user, params: params).execute(organization) } + subject(:update) { described_class.new(group: group, current_user: user, params: params).execute(crm_organization) } describe '#execute' do context 'when the user has no permission' do @@ -33,7 +33,7 @@ RSpec.describe CustomerRelations::Organizations::UpdateService, feature_category context 'when name is changed' do let(:params) { { name: 'GitLab' } } - it 'updates the organization' do + it 'updates the crm_organization' do response = update expect(response).to be_success @@ -42,7 +42,7 @@ RSpec.describe CustomerRelations::Organizations::UpdateService, feature_category end context 'when activating' do - let(:organization) { create(:organization, state: 'inactive') } + let(:crm_organization) { create(:crm_organization, state: 'inactive') } let(:params) { { active: true } } it 'updates the contact' do @@ -56,7 +56,7 @@ RSpec.describe CustomerRelations::Organizations::UpdateService, feature_category context 'when deactivating' do let(:params) { { active: false } } - it 'updates the organization' do + it 'updates the crm_organization' do response = update expect(response).to be_success @@ -64,7 +64,7 @@ RSpec.describe CustomerRelations::Organizations::UpdateService, feature_category end end - context 'when the organization is invalid' do + context 'when the crm_organization is invalid' do let(:params) { { name: nil } } it 'returns an error' do diff --git a/spec/services/groups/transfer_service_spec.rb b/spec/services/groups/transfer_service_spec.rb index 27da948079e..475cd250e7c 100644 --- a/spec/services/groups/transfer_service_spec.rb +++ b/spec/services/groups/transfer_service_spec.rb @@ -890,7 +890,7 @@ RSpec.describe Groups::TransferService, :sidekiq_inline, feature_category: :subg let(:subsub_project) { create(:project, group: subsubgroup) } let!(:contacts) { create_list(:contact, 4, group: root_group) } - let!(:organizations) { create_list(:organization, 2, group: root_group) } + let!(:organizations) { create_list(:crm_organization, 2, group: root_group) } before do create(:issue_customer_relations_contact, contact: contacts[0], issue: create(:issue, project: root_project)) diff --git a/spec/support/protected_branch_helpers.rb b/spec/support/protected_branch_helpers.rb index b34b9ec4641..d983d03fd2e 100644 --- a/spec/support/protected_branch_helpers.rb +++ b/spec/support/protected_branch_helpers.rb @@ -2,18 +2,10 @@ module ProtectedBranchHelpers def set_allowed_to(operation, option = 'Maintainers', form: '.js-new-protected-branch') - within form do - select_elem = find(".js-allowed-to-#{operation}") - select_elem.click - - wait_for_requests - - within('.dropdown-content') do + within(form) do + within_select(".js-allowed-to-#{operation}") do Array(option).each { |opt| click_on(opt) } end - - # Enhanced select is used in EE, therefore an extra click is needed. - select_elem.click if select_elem['aria-expanded'] == 'true' end end @@ -32,4 +24,15 @@ module ProtectedBranchHelpers click_on "Protect" wait_for_requests end + + def within_select(selector, &block) + select_input = find(selector) + select_input.click + wait_for_requests + + within('.dropdown.show .dropdown-menu', &block) + + # Enhanced select is used in EE, therefore an extra click is needed. + select_input.click if select_input['aria-expanded'] == 'true' + end end diff --git a/spec/support/shared_contexts/graphql/types/query_type_shared_context.rb b/spec/support/shared_contexts/graphql/types/query_type_shared_context.rb index 1585ef0e7fc..095c8639d15 100644 --- a/spec/support/shared_contexts/graphql/types/query_type_shared_context.rb +++ b/spec/support/shared_contexts/graphql/types/query_type_shared_context.rb @@ -7,6 +7,7 @@ RSpec.shared_context 'with FOSS query type fields' do :board_list, :ci_application_settings, :ci_config, + :ci_pipeline_stage, :ci_variables, :container_repository, :current_user, diff --git a/spec/support/shared_examples/features/protected_branches_access_control_ce_shared_examples.rb b/spec/support/shared_examples/features/protected_branches_access_control_ce_shared_examples.rb index db63521d1ee..2d3f1949716 100644 --- a/spec/support/shared_examples/features/protected_branches_access_control_ce_shared_examples.rb +++ b/spec/support/shared_examples/features/protected_branches_access_control_ce_shared_examples.rb @@ -1,126 +1,67 @@ # frozen_string_literal: true RSpec.shared_examples "protected branches > access control > CE" do + let(:no_one) { ProtectedRef::AccessLevel.humanize(::Gitlab::Access::NO_ACCESS) } + ProtectedRef::AccessLevel.human_access_levels.each do |(access_type_id, access_type_name)| it "allows creating protected branches that #{access_type_name} can push to" do visit project_protected_branches_path(project) set_protected_branch_name('master') - - find(".js-allowed-to-merge").click - within('[data-testid="allowed-to-merge-dropdown"]') do - expect(first("li")).to have_content("Roles") - find(:link, 'No one').click - end - - within('.js-new-protected-branch') do - allowed_to_push_button = find(".js-allowed-to-push") - - unless allowed_to_push_button.text == access_type_name - allowed_to_push_button.click - within(".dropdown.show .dropdown-menu") { click_on access_type_name } - end - end - + set_allowed_to('merge', no_one) + set_allowed_to('push', access_type_name) click_on_protect - wait_for_requests expect(ProtectedBranch.count).to eq(1) expect(ProtectedBranch.last.push_access_levels.map(&:access_level)).to eq([access_type_id]) end - it "allows updating protected branches so that #{access_type_name} can push to them" do + it "allows creating protected branches that #{access_type_name} can merge to" do visit project_protected_branches_path(project) set_protected_branch_name('master') - - find(".js-allowed-to-merge").click - within('[data-testid="allowed-to-merge-dropdown"]') do - expect(first("li")).to have_content("Roles") - find(:link, 'No one').click - end - - find(".js-allowed-to-push").click - within('[data-testid="allowed-to-push-dropdown"]') do - expect(first("li")).to have_content("Roles") - find(:link, 'No one').click - end - + set_allowed_to('merge', access_type_name) + set_allowed_to('push', no_one) click_on_protect expect(ProtectedBranch.count).to eq(1) - - within(".protected-branches-list") do - find(".js-allowed-to-push").click - - within('.js-allowed-to-push-container') do - expect(first("li")).to have_content("Roles") - find(:link, access_type_name).click - end - - find(".js-allowed-to-push").click - end - - wait_for_requests - - expect(ProtectedBranch.last.push_access_levels.map(&:access_level)).to include(access_type_id) + expect(ProtectedBranch.last.merge_access_levels.map(&:access_level)).to eq([access_type_id]) end - end - ProtectedRef::AccessLevel.human_access_levels.each do |(access_type_id, access_type_name)| - it "allows creating protected branches that #{access_type_name} can merge to" do + it "allows updating protected branches so that #{access_type_name} can push to them" do visit project_protected_branches_path(project) set_protected_branch_name('master') + set_allowed_to('merge', no_one) + set_allowed_to('push', no_one) + click_on_protect - within('.js-new-protected-branch') do - allowed_to_merge_button = find(".js-allowed-to-merge") + expect(ProtectedBranch.count).to eq(1) - unless allowed_to_merge_button.text == access_type_name - allowed_to_merge_button.click - within(".dropdown.show .dropdown-menu") { click_on access_type_name } + within(".protected-branches-list") do + within_select(".js-allowed-to-push") do + click_on(access_type_name) end end - find(".js-allowed-to-push").click - within('[data-testid="allowed-to-push-dropdown"]') do - expect(first("li")).to have_content("Roles") - find(:link, 'No one').click - end - - click_on_protect + wait_for_requests - expect(ProtectedBranch.count).to eq(1) - expect(ProtectedBranch.last.merge_access_levels.map(&:access_level)).to eq([access_type_id]) + expect(ProtectedBranch.last.push_access_levels.map(&:access_level)).to include(access_type_id) end it "allows updating protected branches so that #{access_type_name} can merge to them" do visit project_protected_branches_path(project) set_protected_branch_name('master') - - find(".js-allowed-to-merge").click - within('[data-testid="allowed-to-merge-dropdown"]') do - expect(first("li")).to have_content("Roles") - find(:link, 'No one').click - end - - find(".js-allowed-to-push").click - within('[data-testid="allowed-to-push-dropdown"]') do - expect(first("li")).to have_content("Roles") - find(:link, 'No one').click - end - + set_allowed_to('merge', no_one) + set_allowed_to('push', no_one) click_on_protect expect(ProtectedBranch.count).to eq(1) within(".protected-branches-list") do - find(".js-allowed-to-merge").click - - within('.js-allowed-to-merge-container') do - expect(first("li")).to have_content("Roles") - find(:link, access_type_name).click + within_select(".js-allowed-to-merge") do + click_on(access_type_name) end end diff --git a/spec/support/shared_examples/finders/issues_finder_shared_examples.rb b/spec/support/shared_examples/finders/issues_finder_shared_examples.rb index c68d53db01e..67fed00b5ca 100644 --- a/spec/support/shared_examples/finders/issues_finder_shared_examples.rb +++ b/spec/support/shared_examples/finders/issues_finder_shared_examples.rb @@ -988,9 +988,9 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context let_it_be(:root_group) { create(:group) } let_it_be(:group) { create(:group, parent: root_group) } let_it_be(:project_crm) { create(:project, :public, group: group) } - let_it_be(:organization) { create(:organization, group: root_group) } - let_it_be(:contact1) { create(:contact, group: root_group, organization: organization) } - let_it_be(:contact2) { create(:contact, group: root_group, organization: organization) } + let_it_be(:crm_organization) { create(:crm_organization, group: root_group) } + let_it_be(:contact1) { create(:contact, group: root_group, organization: crm_organization) } + let_it_be(:contact2) { create(:contact, group: root_group, organization: crm_organization) } let_it_be(:contact1_item1) { create(factory, project: project_crm) } let_it_be(:contact1_item2) { create(factory, project: project_crm) } @@ -1028,10 +1028,10 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context end context 'filtering by crm organization' do - let(:params) { { project_id: project_crm.id, crm_organization_id: organization.id } } + let(:params) { { project_id: project_crm.id, crm_organization_id: crm_organization.id } } context 'when the user can read crm organization' do - it 'returns for that organization' do + it 'returns for that crm organization' do root_group.add_reporter(user) expect(items).to contain_exactly(contact1_item1, contact1_item2, contact2_item1) @@ -1039,7 +1039,7 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context end context 'when the user can not read crm organization' do - it 'does not filter by organization' do + it 'does not filter by crm organization' do expect(items).to match_array(all_project_issues) end end diff --git a/yarn.lock b/yarn.lock index ceb4171621d..fbf5b697039 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1115,10 +1115,10 @@ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.40.0.tgz#f1ebb2fcdbb1181550d53f0db827eca1f5060af0" integrity sha512-9CVkIbV0VnIFfVBjWcW8+nHzpMhHhC73C9mGPEktEPfpEbaaRws2UywgDEH+C2B8Ba1QdBo/aFr68RDu2VwvfA== -"@gitlab/ui@61.3.0": - version "61.3.0" - resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-61.3.0.tgz#5b1f9c7a01d7571150a796ca5f02fe44f9604fc4" - integrity sha512-jOaEGjvtrVT7IZhtu/VZNmlodGgrE/UllciNfJxzm4nbbWMHlALspFp8btt/zxGxiI33oQJLS2qTCEu0QVVZHA== +"@gitlab/ui@62.4.0": + version "62.4.0" + resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-62.4.0.tgz#827e2506a2880fb8d9899b9c18c1d5dfae79a9a7" + integrity sha512-FouJu0o3Za2Hz6WjnZSN+LTMSjpaBCE3SZ5HSBoz0be/WKjS3CX5WW3Qp2NMvNs59xe4V0xg3uWI2g/X0edjTw== dependencies: "@popperjs/core" "^2.11.2" bootstrap-vue "2.23.1" |