+ On-demand scans +
+ ++ On-demand scans run outside of DevOps cycle and find vulnerabilities in your projects. + + Learn more + + . +
+ +diff --git a/.eslintrc.yml b/.eslintrc.yml
index 6b9a1ce62c07e771768dcdae2439f7c498d6e621..cd3cd82d4e7a29d6887d17029d13f9c94c57b571 100644
--- a/.eslintrc.yml
+++ b/.eslintrc.yml
@@ -88,7 +88,7 @@ rules:
- pattern: test_fixtures/**
group: internal
alphabetize:
- order: asc
+ order: ignore
overrides:
- files:
- '**/spec/**/*'
@@ -100,6 +100,8 @@ overrides:
- 'scripts/**/*'
- '*.config.js'
- '*.config.*.js'
+ - 'jest_resolver.js'
+ - storybook/config/*.js
rules:
'@gitlab/require-i18n-strings': off
import/no-extraneous-dependencies: off
diff --git a/.gitignore b/.gitignore
index f753a24756386951a1b8bc4367335273b9025b71..5152ef20575338b533df52cc79d897e75e56418f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,10 +40,7 @@ eslint-report.html
/config/initializers/smtp_settings.rb
/config/initializers/relative_url.rb
/config/resque.yml
-/config/redis.cache.yml
-/config/redis.queues.yml
-/config/redis.shared_state.yml
-/config/redis.trace_chunks.yml
+/config/redis.*.yml
/config/unicorn.rb
/config/puma.rb
/config/secrets.yml
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 3aa901463d19235152bbdc72d9331dc14b4c9def..d0a0c7b241404ba20e24077bca19bfc12906dfe1 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -17,7 +17,7 @@ stages:
# in cases where jobs require Docker-in-Docker, the job
# definition must be extended with `.use-docker-in-docker`
default:
- image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.7.patched-golang-1.14-git-2.31-lfs-2.9-chrome-89-node-14.15-yarn-1.22-postgresql-11-graphicsmagick-1.3.36"
+ image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.7.patched-golang-1.16-git-2.31-lfs-2.9-chrome-89-node-14.15-yarn-1.22-postgresql-11-graphicsmagick-1.3.36"
tags:
- gitlab-org
# All jobs are interruptible by default
@@ -89,11 +89,12 @@ variables:
REVIEW_APPS_GCP_REGION: "us-central1"
BUILD_ASSETS_IMAGE: "true" # Set it to "false" to disable assets image building, used in `build-assets-image`
- RSPEC_FAIL_FAST_ENABLED: "true" # Set it to "false" to disable RSpec fail-fast
SIMPLECOV: "true"
# For the default QA image, we use $CI_COMMIT_SHA as tag since it's always available and we override it for specific workflow.rules (see above)
QA_IMAGE: "${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab-ee-qa:${CI_COMMIT_SHA}"
+ # Default latest tag for particular branch
+ QA_IMAGE_BRANCH: "${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab-ee-qa:${CI_COMMIT_REF_SLUG}"
# Preparing custom clone path to reduce space used by all random forks
# on GitLab.com's Shared Runners. Our main forks - especially the security
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS
index af1cf88b1767bfc2b8be6ec5eff01d649fbee585..40170d2ed190ecbbe2bdf77aa246cc2db0574ccc 100644
--- a/.gitlab/CODEOWNERS
+++ b/.gitlab/CODEOWNERS
@@ -11,39 +11,40 @@
/doc/.markdownlint @marcel.amirault @eread @aqualls @cnorris
/doc/ @gl-docsteam
/doc/.vale/ @marcel.amirault @eread @aqualls @cnorris
-/doc/administration/geo/ @axil
+/doc/administration/geo/ @marcel.amirault
/doc/administration/gitaly/ @eread
-/doc/administration/integration/ @aqualls
/doc/administration/lfs/ @aqualls
/doc/administration/monitoring/ @ngaskill
-/doc/administration/operations/ @axil @eread @marcia
+/doc/administration/operations/ @marcel.amirault @eread @marcia
/doc/administration/packages/ @ngaskill
-/doc/administration/pages/ @axil @kpaizee
+/doc/administration/pages/ @rdickenson @kpaizee
/doc/administration/postgresql/ @marcia
-/doc/administration/raketasks/ @axil @eread
-/doc/administration/redis/ @axil
-/doc/administration/reference_architectures/ @axil
+/doc/administration/raketasks/ @marcel.amirault @eread
+/doc/administration/redis/ @marcel.amirault
+/doc/administration/reference_architectures/ @marcel.amirault
/doc/administration/snippets/ @aqualls
-/doc/administration/troubleshooting @axil @marcia @eread
+/doc/administration/troubleshooting @marcel.amirault @marcia @eread
+/doc/api/graphql/ @msedlakjakubowski @kpaizee
+/doc/api/graphql/reference/ @kpaizee
/doc/api/group_activity_analytics.md @msedlakjakubowski
/doc/ci/ @marcel.amirault @sselhorn
-/doc/ci/environments/ @axil
+/doc/ci/environments/ @rdickenson
/doc/ci/services/ @sselhorn
/doc/ci/test_cases/ @msedlakjakubowski
/doc/development/ @marcia
-/doc/development/documentation/ @cnorris
+/doc/development/documentation/ @cnorris @dianalogan
/doc/development/i18n/ @ngaskill
/doc/development/value_stream_analytics.md @msedlakjakubowski
/doc/gitlab-basics/ @aqualls
-/doc/install/ @axil
-/doc/integration/ @aqualls @eread
-/doc/operations/ @ngaskill @axil
+/doc/install/ @marcel.amirault
+/doc/operations/ @ngaskill @rdickenson
/doc/push_rules/ @aqualls
+/doc/security/ @eread
/doc/ssh/ @eread
/doc/subscriptions/ @sselhorn
/doc/topics/autodevops/ @marcia
/doc/topics/git/ @aqualls
-/doc/update/ @axil @marcia
+/doc/update/ @marcel.amirault @marcia
/doc/user/analytics/ @msedlakjakubowski @ngaskill
/doc/user/application_security/ @rdickenson
/doc/user/application_security/container_scanning/ @ngaskill
@@ -62,15 +63,13 @@
/doc/user/packages/infrastructure_registry/ @marcia
/doc/user/packages/terraform_module_registry/ @marcia
/doc/user/profile/ @msedlakjakubowski @eread
-/doc/user/project/ @aqualls @axil @eread @msedlakjakubowski @ngaskill
+/doc/user/project/ @aqualls @rdickenson @eread @msedlakjakubowski @ngaskill
/doc/user/project/clusters/ @marcia
/doc/user/project/import/ @ngaskill @msedlakjakubowski
-/doc/user/project/integrations/ @aqualls
-/doc/user/project/integrations/prometheus_library/ @ngaskill
/doc/user/project/issues/ @msedlakjakubowski
/doc/user/project/merge_requests/ @aqualls @eread
/doc/user/project/milestones/ @msedlakjakubowski
-/doc/user/project/pages/ @axil
+/doc/user/project/pages/ @rdickenson
/doc/user/project/repository/ @aqualls
/doc/user/project/settings/ @aqualls @eread
/doc/user/project/static_site_editor/index.md @aqualls
@@ -142,6 +141,12 @@
/doc/user/project/settings/import_export.md @aqualls
/doc/user/snippets.md @aqualls
+[Docs Ecosystem]
+/doc/administration/integration/ @kpaizee
+/doc/integration/ @kpaizee
+/doc/user/project/integrations/ @kpaizee
+/doc/user/project/integrations/prometheus_library/ @ngaskill
+
[Docs Growth]
/doc/administration/instance_review.md @kpaizee
/doc/api/invitations.md @kpaizee
@@ -237,7 +242,7 @@ Dangerfile @gl-quality/eng-prod
/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/secure/composition-analysis-be @gitlab-org/secure/dynamic-analysis-be @gitlab-org/secure/static-analysis-be @gitlab-org/secure/fuzzing-be
-/ee/app/services/ci/run_dast_scan_service.rb @gitlab-org/secure/dynamic-analysis-be
+/ee/app/services/app_sec/dast/ @gitlab-org/secure/dynamic-analysis-be
[Container Security]
/ee/app/views/projects/threat_monitoring/** @gitlab-org/protect/container-security-frontend
diff --git a/.gitlab/changelog_config.yml b/.gitlab/changelog_config.yml
index 6069cd17a084063960976731517f3ac460d5968c..f6a041cced971e262a7ac654c1ee81531df862a0 100644
--- a/.gitlab/changelog_config.yml
+++ b/.gitlab/changelog_config.yml
@@ -11,6 +11,8 @@ categories:
security: Security
performance: Performance
other: Other
+include_groups:
+ - gitlab-org/gitlab-core-team/community-members
template: |
{% if categories %}
{% each categories %}
@@ -18,7 +20,7 @@ template: |
{% each entries %}
- [{{ title }}]({{ commit.reference }})\
- {% if author.contributor %} by {{ author.reference }}{% end %}\
+ {% if author.credit %} by {{ author.reference }}{% end %}\
{% if commit.trailers.MR %}\
([merge request]({{ commit.trailers.MR }}))\
{% else %}\
diff --git a/.gitlab/ci/build-images.gitlab-ci.yml b/.gitlab/ci/build-images.gitlab-ci.yml
index 91cf7c9f2d5d63eab649437a08868b0afbdd91f8..6a222d8937f000eb8d3c905c2955709da123ef7f 100644
--- a/.gitlab/ci/build-images.gitlab-ci.yml
+++ b/.gitlab/ci/build-images.gitlab-ci.yml
@@ -24,46 +24,12 @@ build-qa-image:
- .base-image-build
- .build-images:rules:build-qa-image
stage: build-images
- tags:
- - high-cpu
needs: []
script:
- !reference [.base-image-build, script]
- echo $QA_IMAGE
- - /kaniko/executor --context=${CI_PROJECT_DIR} --dockerfile=${CI_PROJECT_DIR}/qa/Dockerfile --destination=${QA_IMAGE} --cache=true
-
-# This image is used by:
-# - The `review-qa-*` jobs
-# - The downstream `omnibus-gitlab-mirror` pipeline triggered by `package-and-qa` so that it doesn't have to rebuild it again.
-# The downstream `omnibus-gitlab-mirror` pipeline itself passes the image name to the `gitlab-qa-mirror` pipeline so that
-# it can use it instead of inferring an end-to-end image from the GitLab image built by the downstream `omnibus-gitlab-mirror` pipeline.
-# See https://docs.gitlab.com/ee/development/testing_guide/end_to_end/index.html#testing-code-in-merge-requests for more details.
-build-qa-image-buildkit:
- extends:
- - .base-image-build
- - .use-buildkit
- - .build-images:rules:build-qa-image
- stage: build-images
- tags:
- - docker
- - high-cpu
- needs: []
- variables:
- QA_IMAGE_BASE: ${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab-ee-qa
- allow_failure: true
- script:
- - !reference [.base-image-build, script]
- # Build image and push current sha tag and latest branch tag
- # Fetch cache from latest branch image or latest main branch image
- - |
- buildctl-daemonless.sh build \
- --frontend=dockerfile.v0 \
- --local context="." \
- --local dockerfile="qa" \
- --export-cache type=inline \
- --import-cache type=registry,ref="${QA_IMAGE_BASE}:${CI_COMMIT_REF_SLUG}-buildkit" \
- --import-cache type=registry,ref="${QA_IMAGE_BASE}:${CI_DEFAULT_BRANCH}" \
- --output type=image,\"name="${QA_IMAGE}-buildkit","${QA_IMAGE_BASE}:${CI_COMMIT_REF_SLUG}-buildkit"\",push=true
+ - echo $QA_IMAGE_BRANCH
+ - /kaniko/executor --context=${CI_PROJECT_DIR} --dockerfile=${CI_PROJECT_DIR}/qa/Dockerfile --destination=${QA_IMAGE} --destination=${QA_IMAGE_BRANCH} --cache=true
# This image is used by:
# - The `CNG` pipelines (via the `review-build-cng` job): https://gitlab.com/gitlab-org/build/CNG/-/blob/cfc67136d711e1c8c409bf8e57427a644393da2f/.gitlab-ci.yml#L335
diff --git a/.gitlab/ci/dast.gitlab-ci.yml b/.gitlab/ci/dast.gitlab-ci.yml
index 309714f8739e054475ca6925b5306c213eeec3bc..512c850b7da3f9d9ec8ca66c23aac98b613daee8 100644
--- a/.gitlab/ci/dast.gitlab-ci.yml
+++ b/.gitlab/ci/dast.gitlab-ci.yml
@@ -10,29 +10,21 @@
variables:
DAST_USERNAME_FIELD: "user[login]"
DAST_PASSWORD_FIELD: "user[password]"
+ DAST_SUBMIT_FIELD: "commit"
DAST_FULL_SCAN_ENABLED: "true"
- DAST_SPIDER_MINS: 0
- # TBD pin to a version
- DAST_VERSION: 1.22.1
+ DAST_VERSION: 2
+ GIT_STRATEGY: none
# -Xmx is used to set the JVM memory to 6GB to prevent DAST OutOfMemoryError.
DAST_ZAP_CLI_OPTIONS: "-Xmx6144m"
- DAST_RULES: "41,42,43,10027,10032,10041,10042,10045,10047,10052,10053,10057,10061,10096,10097,10104,10106,20012,20014,20015,20016,20017,20018,40019,40020,40021,40024,40025,40027,40029,40032,90001,90019,10109,10026,10028,10029,10030,10031,10033,10034,10035,10036,10038,10039,10043,10044,10048,10050,10051,10058,10062,10095,10107,10108,30003,40013,40022,40023,40028,90021,90023,90024,90025,90027,90028,10003,50003,0,2,3,6,7,10010,10011,10015,10017,10019,10020,10021,10023,10024,10025,10037,10040,10054,10055,10056,10098,10105,10202,20019,30001,30002,40003,40008,40009,40012,40014,40016,40017,40018,50000,50001,90011,90020,90022,90033"
before_script:
- 'export DAST_WEBSITE="${DAST_WEBSITE:-$(cat environment_url.txt)}"'
- 'export DAST_AUTH_URL="${DAST_WEBSITE}/users/sign_in"'
- 'export DAST_PASSWORD="${REVIEW_APPS_ROOT_PASSWORD}"'
- # Below three lines can be removed once https://gitlab.com/gitlab-org/gitlab/-/issues/230687 is fixed
- - mkdir -p /zap/xml
- - 'sed -i "84 s/true/false/" /zap/xml/config.xml'
- - cat /zap/xml/config.xml
# Help pages are excluded from scan as they are static pages.
# profile/two_factor_auth is excluded from scan to prevent 2FA from being turned on from user profile, which will reduce coverage.
- - 'export DAST_AUTH_EXCLUDE_URLS="${DAST_WEBSITE}/help/.*,${DAST_WEBSITE}/profile/two_factor_auth,${DAST_WEBSITE}/users/sign_out"'
+ - 'DAST_EXCLUDE_URLS="${DAST_WEBSITE}/help/.*,${DAST_WEBSITE}/-/profile/two_factor_auth,${DAST_WEBSITE}/users/sign_out"'
# Exclude the automatically generated monitoring project from being tested due to https://gitlab.com/gitlab-org/gitlab/-/issues/260362
- - 'DAST_AUTH_EXCLUDE_URLS="${DAST_AUTH_EXCLUDE_URLS},https://.*\.gitlab-review\.app/gitlab-instance-(administrators-)?[a-zA-Z0-9]{8}/.*"'
- - enable_rule () { read all_rules; rule=$1; echo $all_rules | sed -r "s/(,)?$rule(,)?/\1-1\2/" ; }
- # Sort ids in DAST_RULES ascendingly, which is required when using DAST_RULES as argument to enable_rule
- - 'DAST_RULES=$(echo $DAST_RULES | tr "," "\n" | sort -n | paste -sd ",")'
+ - 'export DAST_EXCLUDE_URLS="${DAST_EXCLUDE_URLS},${DAST_WEBSITE}/gitlab-instance-.*"'
needs: ["review-deploy"]
stage: dast
# Default job timeout set to 90m and dast rules needs 2h to so that it won't timeout.
@@ -45,161 +37,155 @@
reports:
dast: gl-dast-report.json
expire_in: 1 week # GitLab-specific
+ allow_failure: true
# DAST scan with a subset of Release scan rules.
-DAST-fullscan-ruleset1:
+# ZAP rule details can be found at https://www.zaproxy.org/docs/alerts/
+
+# 10019, 10021 Missing security headers
+# 10023, 10024, 10025, 10037 Information Disclosure
+# 10040 Secure Pages Include Mixed Content
+# 10055 CSP
+# 10056 X-Debug-Token Information Leak
+# Duration: 14 minutes 20 seconds
+
+dast:secureHeaders-csp-infoLeak:
extends:
- .dast_conf
variables:
DAST_USERNAME: "user1"
+ DAST_ONLY_INCLUDE_RULES: "10019,10021,10023,10024,10025,10037,10040,10055,10056"
script:
- - export DAST_EXCLUDE_RULES=$(echo $DAST_RULES | enable_rule 10019 | enable_rule 10020 | enable_rule 10021 | enable_rule 10023 | enable_rule 10024 | enable_rule 10025 | enable_rule 10037 | enable_rule 10040 | enable_rule 10054 | enable_rule 10055 | enable_rule 10056)
- - echo $DAST_EXCLUDE_RULES
- - /analyze -t $DAST_WEBSITE -d
+ - /analyze
-# DAST scan with a subset of Release scan rules.
-DAST-fullscan-ruleset2:
+# 90023 XML External Entity Attack
+# Duration: 41 minutes 20 seconds
+# 90019 Server Side Code Injection
+# Duration: 34 minutes 31 seconds
+dast:XXE-SrvSideInj:
extends:
- .dast_conf
variables:
DAST_USERNAME: "user2"
+ DAST_ONLY_INCLUDE_RULES: "90023,90019"
script:
- - export DAST_EXCLUDE_RULES=$(echo $DAST_RULES | enable_rule 90011 | enable_rule 90020 | enable_rule 90022 | enable_rule 90033)
- - echo $DAST_EXCLUDE_RULES
- - /analyze -t $DAST_WEBSITE -d
+ - /analyze
-# DAST scan with a subset of Release scan rules.
-DAST-fullscan-ruleset3:
+# 0 Directory Browsing
+# 2 Private IP Disclosure
+# 3 Session ID in URL Rewrite
+# 7 Remote File Inclusion
+# Duration: 63 minutes 43 seconds
+# 90034 Cloud Metadata Potentially Exposed
+# Duration: 13 minutes 48 seconds
+# 90022 Application Error Disclosure
+# Duration: 12 minutes 7 seconds
+dast:infoLeak-fileInc-DirBrowsing:
extends:
- .dast_conf
variables:
DAST_USERNAME: "user3"
+ DAST_ONLY_INCLUDE_RULES: "0,2,3,7,90034,90022"
script:
- - export DAST_EXCLUDE_RULES=$(echo $DAST_RULES | enable_rule 40016 | enable_rule 40017 | enable_rule 50000 | enable_rule 50001)
- - echo $DAST_EXCLUDE_RULES
- - /analyze -t $DAST_WEBSITE -d
+ - /analyze
-# DAST scan with a subset of Release scan rules.
-DAST-fullscan-ruleset4:
+# 10010 Cookie No HttpOnly Flag
+# 10011 Cookie Without Secure Flag
+# 10017 Cross-Domain JavaScript Source File Inclusion
+# 10029 Cookie Poisoning
+# 90033 Loosely Scoped Cookie
+# 10054 Cookie Without SameSite Attribute
+# Duration: 13 minutes 23 seconds
+dast:insecureCookie:
extends:
- .dast_conf
variables:
DAST_USERNAME: "user4"
+ DAST_ONLY_INCLUDE_RULES: "10010,10011,10017,10029,90033,10054"
script:
- - export DAST_EXCLUDE_RULES=$(echo $DAST_RULES | enable_rule 0 | enable_rule 2 | enable_rule 3 | enable_rule 7 )
- - echo $DAST_EXCLUDE_RULES
- - /analyze -t $DAST_WEBSITE -d
+ - /analyze
-# DAST scan with a subset of Release scan rules.
-DAST-fullscan-ruleset5:
- extends:
- - .dast_conf
- variables:
- DAST_USERNAME: "user5"
- script:
- - export DAST_EXCLUDE_RULES=$(echo $DAST_RULES | enable_rule 10010 | enable_rule 10011 | enable_rule 10017 | enable_rule 10019)
- - echo $DAST_EXCLUDE_RULES
- - /analyze -t $DAST_WEBSITE -d
-
-# DAST scan with a subset of Release scan rules.
-DAST-fullscan-ruleset6:
- extends:
- - .dast_conf
- variables:
- DAST_USERNAME: "user6"
- script:
- - export DAST_EXCLUDE_RULES=$(echo $DAST_RULES | enable_rule 30001 | enable_rule 40009)
- - echo $DAST_EXCLUDE_RULES
- - /analyze -t $DAST_WEBSITE -d
-# Enable when https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39749 is fixed
-# DAST scan with a subset of Beta scan rules.
-# DAST-fullscan-ruleset7:
-# extends:
-# - .dast_conf
-# variables:
-# DAST_USERNAME: "user7"
-# script:
-# - export DAST_EXCLUDE_RULES=$(echo $DAST_RULES | enable_rule 10098 | enable_rule 10105 | enable_rule 10202 | enable_rule 30002 | enable_rule 40003 | enable_rule 40008 | enable_rule 40009)
-# - echo $DAST_EXCLUDE_RULES
-# - /analyze -t $DAST_WEBSITE -d
+# 20012 Anti-CSRF Tokens Check
+# 10202 Absence of Anti-CSRF Tokens
+# https://gitlab.com/gitlab-com/gl-security/appsec/appsec-team/-/issues/192
-# Enable when https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39749 is fixed
-# Below jobs runs DAST scans with one time consuming scan rule. These scan rules are disabled in above jobs so that those jobs won't timeout.
-# DAST scan with rule - 20019 External Redirect
-# DAST-fullscan-rule-20019:
+# Commented because of lot of FP's
+# dast:csrfTokenCheck:
# extends:
# - .dast_conf
# variables:
-# DAST_USERNAME: "user8"
+# DAST_USERNAME: "user6"
+# DAST_ONLY_INCLUDE_RULES: "20012,10202"
# script:
-# - export DAST_EXCLUDE_RULES=$(echo $DAST_RULES | enable_rule 20019)
-# - echo $DAST_EXCLUDE_RULES
-# - /analyze -t $DAST_WEBSITE -d
+# - /analyze
-# Enable when https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39749 is fixed
-# DAST scan with rule - 10107 Httpoxy - Proxy Header Misuse - Active/beta
-# DAST-fullscan-rule-10107:
-# extends:
-# - .dast_conf
-# variables:
-# DAST_USERNAME: "user9"
-# script:
-# - export DAST_EXCLUDE_RULES=$(echo $DAST_RULES | enable_rule 10107)
-# - echo $DAST_EXCLUDE_RULES
-# - /analyze -t $DAST_WEBSITE -d
+# 10098 Cross-Domain Misconfiguration
+# 10105 Weak Authentication Method
+# 40003 CRLF Injection
+# 40008 Parameter Tampering
+# Duration: 71 minutes 15 seconds
+dast:corsMisconfig-weakauth-crlfInj:
+ extends:
+ - .dast_conf
+ variables:
+ DAST_USERNAME: "user5"
+ DAST_ONLY_INCLUDE_RULES: "10098,10105,40003,40008"
+ script:
+ - /analyze
-# DAST scan with rule - 90020 Remote OS Command Injection
-DAST-fullscan-rule-90020:
+# 20019 External Redirect
+# 20014 HTTP Parameter Pollution
+# Duration: 46 minutes 12 seconds
+dast:extRedirect-paramPollution:
extends:
- .dast_conf
variables:
- DAST_USERNAME: "user10"
+ DAST_USERNAME: "user6"
+ DAST_ONLY_INCLUDE_RULES: "20019,20014"
script:
- - export DAST_EXCLUDE_RULES=$(echo $DAST_RULES | enable_rule 90020)
- - echo $DAST_EXCLUDE_RULES
- - /analyze -t $DAST_WEBSITE -d
+ - /analyze
-# DAST scan with rule - 40018 SQL Injection - Active/release
-DAST-fullscan-rule-40018:
+# 40022 SQL Injection - PostgreSQL
+# Duration: 53 minutes 59 seconds
+dast:sqlInjection:
extends:
- .dast_conf
variables:
- DAST_USERNAME: "user11"
+ DAST_USERNAME: "user7"
+ DAST_ONLY_INCLUDE_RULES: "40022"
script:
- - export DAST_EXCLUDE_RULES=$(echo $DAST_RULES | enable_rule 40018)
- - echo $DAST_EXCLUDE_RULES
- - /analyze -t $DAST_WEBSITE -d
+ - /analyze
-# DAST scan with rule - 40014 Cross Site Scripting (Persistent) - Active/release
-DAST-fullscan-rule-40014:
+# 40014 Cross Site Scripting (Persistent)
+# Duration: 21 minutes 50 seconds
+dast:xss-persistent:
extends:
- .dast_conf
variables:
- DAST_USERNAME: "user12"
+ DAST_USERNAME: "user8"
+ DAST_ONLY_INCLUDE_RULES: "40014"
script:
- - export DAST_EXCLUDE_RULES=$(echo $DAST_RULES | enable_rule 40014)
- - echo $DAST_EXCLUDE_RULES
- - /analyze -t $DAST_WEBSITE -d
+ - /analyze
-# DAST scan with rule - 6 Path travesal
-DAST-fullscan-rule-6:
+# 40012 Cross Site Scripting (Reflected)
+# Duration: 73 minutes 15 seconds
+dast:xss-reflected:
extends:
- .dast_conf
variables:
- DAST_USERNAME: "user13"
+ DAST_USERNAME: "user9"
+ DAST_ONLY_INCLUDE_RULES: "40012"
script:
- - export DAST_EXCLUDE_RULES=$(echo $DAST_RULES | enable_rule 6)
- - echo $DAST_EXCLUDE_RULES
- - /analyze -t $DAST_WEBSITE -d
+ - /analyze
-# DAST scan with rule - 40012 Cross Site Scripting (Reflected)
-DAST-fullscan-rule-40012:
+# 40013 Session Fixation
+# Duration: 44 minutes 25 seconds
+dast:sessionFixation:
extends:
- .dast_conf
variables:
- DAST_USERNAME: "user14"
+ DAST_USERNAME: "user10"
+ DAST_ONLY_INCLUDE_RULES: "40013"
script:
- - export DAST_EXCLUDE_RULES=$(echo $DAST_RULES | enable_rule 40012)
- - echo $DAST_EXCLUDE_RULES
- - /analyze -t $DAST_WEBSITE -d
+ - /analyze
diff --git a/.gitlab/ci/docs.gitlab-ci.yml b/.gitlab/ci/docs.gitlab-ci.yml
index 3e3d994c70b2035f53b11616a63550725b33e5af..f4d8698f22da4616d6eaae2f89d9e3212b20f3ea 100644
--- a/.gitlab/ci/docs.gitlab-ci.yml
+++ b/.gitlab/ci/docs.gitlab-ci.yml
@@ -75,16 +75,3 @@ ui-docs-links lint:
needs: []
script:
- bundle exec haml-lint -i DocumentationLinks
-
-deprecations-doc check:
- variables:
- SETUP_DB: "false"
- extends:
- - .default-retry
- - .rails-cache
- - .default-before_script
- - .docs:rules:deprecations
- stage: test
- needs: []
- script:
- - bundle exec rake gitlab:docs:check_deprecations
diff --git a/.gitlab/ci/frontend.gitlab-ci.yml b/.gitlab/ci/frontend.gitlab-ci.yml
index 48f85219ff4e7f2a3723a92ad6ed1f9ecbcb028f..6974d63a49c0c13694312ec80632fcb58c6f8ee1 100644
--- a/.gitlab/ci/frontend.gitlab-ci.yml
+++ b/.gitlab/ci/frontend.gitlab-ci.yml
@@ -71,6 +71,12 @@ compile-test-assets as-if-foss:
- .frontend:rules:compile-test-assets-as-if-foss
- .as-if-foss
+compile-test-assets as-if-jh:
+ extends:
+ - compile-test-assets
+ - .frontend:rules:compile-test-assets-as-if-jh
+ needs: ["add-jh-folder"]
+
update-assets-compile-production-cache:
extends:
- compile-production-assets
@@ -112,7 +118,7 @@ update-storybook-yarn-cache:
- .rails-cache
- .use-pg12
stage: fixtures
- needs: ["setup-test-env", "retrieve-tests-metadata", "compile-test-assets"]
+ needs: ["setup-test-env", "retrieve-tests-metadata"]
variables:
WEBPACK_VENDOR_DLL: "true"
script:
@@ -128,23 +134,38 @@ update-storybook-yarn-cache:
- tmp/tests/frontend/
- knapsack/
-rspec frontend_fixture:
+# Builds FOSS, and EE fixtures in the EE project.
+# Builds FOSS fixtures in the FOSS project.
+rspec-all frontend_fixture:
extends:
- .frontend-fixtures-base
- .frontend:rules:default-frontend-jobs
- parallel: 2
+ needs:
+ - !reference [.frontend-fixtures-base, needs]
+ - "compile-test-assets"
+ parallel: 5
-rspec frontend_fixture as-if-foss:
+# Builds FOSS fixtures in the EE project, with the `ee/` folder removed (due to `as-if-foss`).
+rspec-all frontend_fixture as-if-foss:
extends:
- .frontend-fixtures-base
- .frontend:rules:default-frontend-jobs-as-if-foss
- .as-if-foss
+ needs:
+ - !reference [.frontend-fixtures-base, needs]
+ - "compile-test-assets as-if-foss"
-rspec-ee frontend_fixture:
+# Builds FOSS, EE, and JH fixtures in the EE project, with the `jh/` folder added (due to `as-if-jh`).
+rspec-all frontend_fixture as-if-jh:
extends:
- .frontend-fixtures-base
- - .frontend:rules:default-frontend-jobs-ee
- parallel: 3
+ - .frontend:rules:default-frontend-jobs-as-if-jh
+ needs:
+ - !reference [.frontend-fixtures-base, needs]
+ - "compile-test-assets as-if-jh"
+ - "add-jh-folder"
+ script:
+ - echo "This job is currently doing nothing since there's no specific JH fixtures yet. To enable this job, remove this line."
graphql-schema-dump:
variables:
@@ -172,7 +193,9 @@ graphql-schema-dump:
# Disable warnings in browserslist which can break on backports
# https://github.com/browserslist/browserslist/blob/a287ec6/node.js#L367-L384
BROWSERSLIST_IGNORE_OLD_DATA: "true"
+ SETUP_DB: "false"
before_script:
+ - !reference [.default-before_script, before_script]
- *yarn-install
stage: test
@@ -194,11 +217,7 @@ jest:
extends:
- .jest-base
- .frontend:rules:jest
- needs:
- - job: "detect-tests"
- - job: "rspec frontend_fixture"
- - job: "rspec-ee frontend_fixture"
- optional: true
+ needs: ["rspec-all frontend_fixture"]
artifacts:
name: coverage-frontend
expire_in: 31d
@@ -215,6 +234,9 @@ jest minimal:
extends:
- jest
- .frontend:rules:jest:minimal
+ needs:
+ - !reference [jest, needs]
+ - "detect-tests"
script:
- run_timed_command "yarn jest:ci:minimal"
@@ -225,9 +247,7 @@ jest-integration:
script:
- run_timed_command "yarn jest:integration --ci"
needs:
- - job: "rspec frontend_fixture"
- - job: "rspec-ee frontend_fixture"
- optional: true
+ - job: "rspec-all frontend_fixture"
- job: "graphql-schema-dump"
jest-as-if-foss:
@@ -235,9 +255,17 @@ jest-as-if-foss:
- .jest-base
- .frontend:rules:default-frontend-jobs-as-if-foss
- .as-if-foss
- needs: ["rspec frontend_fixture as-if-foss"]
+ needs: ["rspec-all frontend_fixture as-if-foss"]
parallel: 2
+jest-as-if-jh:
+ extends:
+ - .jest-base
+ - .frontend:rules:default-frontend-jobs-as-if-jh
+ needs: ["rspec-all frontend_fixture as-if-jh", "add-jh-folder"]
+ script:
+ - echo "This job is currently doing nothing since there's no specific JH Jest tests yet. To enable this job, remove this line."
+
coverage-frontend:
extends:
- .default-retry
@@ -341,9 +369,7 @@ startup-css-check:
- .frontend:rules:default-frontend-jobs
needs:
- job: "compile-test-assets"
- - job: "rspec frontend_fixture"
- - job: "rspec-ee frontend_fixture"
- optional: true
+ - job: "rspec-all frontend_fixture"
startup-css-check as-if-foss:
extends:
@@ -352,7 +378,7 @@ startup-css-check as-if-foss:
- .frontend:rules:default-frontend-jobs-as-if-foss
needs:
- job: "compile-test-assets as-if-foss"
- - job: "rspec frontend_fixture as-if-foss"
+ - job: "rspec-all frontend_fixture as-if-foss"
.compile-storybook-base:
extends:
@@ -361,11 +387,15 @@ startup-css-check as-if-foss:
script:
- *storybook-yarn-install
- yarn run storybook:build
+ needs: ["graphql-schema-dump"]
compile-storybook:
extends:
- .compile-storybook-base
- .frontend:rules:default-frontend-jobs
+ needs:
+ - !reference [.compile-storybook-base, needs]
+ - job: "rspec-all frontend_fixture"
artifacts:
name: storybook
expire_in: 31d
@@ -378,3 +408,6 @@ compile-storybook as-if-foss:
- .compile-storybook-base
- .as-if-foss
- .frontend:rules:default-frontend-jobs-as-if-foss
+ needs:
+ - !reference [.compile-storybook-base, needs]
+ - job: "rspec-all frontend_fixture as-if-foss"
diff --git a/.gitlab/ci/global.gitlab-ci.yml b/.gitlab/ci/global.gitlab-ci.yml
index b065d578b9dec13277da25a56ac9405bb2e583bd..d0c26d60066d8efd656cab47e5beb0bb01ddb59e 100644
--- a/.gitlab/ci/global.gitlab-ci.yml
+++ b/.gitlab/ci/global.gitlab-ci.yml
@@ -10,6 +10,7 @@
.default-before_script:
before_script:
+ - echo $FOSS_ONLY
- '[ "$FOSS_ONLY" = "1" ] && rm -rf ee/ qa/spec/ee/ qa/qa/specs/features/ee/ qa/qa/ee/ qa/qa/ee.rb'
- export GOPATH=$CI_PROJECT_DIR/.go
- mkdir -p $GOPATH
@@ -193,10 +194,12 @@
.storybook-yarn-cache:
cache:
+ - *node-modules-cache
- *storybook-node-modules-cache
.storybook-yarn-cache-push:
cache:
+ - *node-modules-cache # We don't push this cache as it's already rebuilt by `update-yarn-cache`
- *storybook-node-modules-cache-push
.use-pg11:
@@ -268,24 +271,6 @@
- mkdir -p /kaniko/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
-.use-buildkit:
- image:
- name: ${GITLAB_DEPENDENCY_PROXY}moby/buildkit:v0.9.0
- entrypoint: [""]
- before_script:
- - source scripts/utils.sh
- - mkdir -p $HOME/.docker
- - |
- cat <<- EOF > $HOME/.docker/config.json
- {
- "auths": {
- "$CI_REGISTRY": {
- "auth": "$(echo -n $CI_REGISTRY_USER:$CI_REGISTRY_PASSWORD | base64)"
- }
- }
- }
- EOF
-
.as-if-foss:
variables:
FOSS_ONLY: '1'
diff --git a/.gitlab/ci/memory.gitlab-ci.yml b/.gitlab/ci/memory.gitlab-ci.yml
index f3ad8f81da5d1652589d43ba6cea07bfa571a730..9234b116ff87b7bbc49b385b0799a83191857a05 100644
--- a/.gitlab/ci/memory.gitlab-ci.yml
+++ b/.gitlab/ci/memory.gitlab-ci.yml
@@ -4,6 +4,12 @@
- .rails-cache
- .default-before_script
- .memory:rules
+ variables:
+ METRICS_FILE: "metrics.txt"
+ artifacts:
+ reports:
+ metrics: "${METRICS_FILE}"
+ expire_in: 31d
memory-static:
extends: .only-code-memory-job-base
@@ -11,24 +17,25 @@ memory-static:
needs: ["setup-test-env"]
variables:
SETUP_DB: "false"
+ MEMORY_BUNDLE_MEM_FILE: "tmp/memory_bundle_mem.txt"
+ MEMORY_BUNDLE_OBJECTS_FILE: "tmp/memory_bundle_objects.txt"
script:
# Uses two different reports from the 'derailed_benchmars' gem.
# Loads each of gems in the Gemfile and checks how much memory they consume when they are required.
# 'derailed_benchmarks' internally uses 'get_process_mem'
- - bundle exec derailed bundle:mem > tmp/memory_bundle_mem.txt
- - scripts/generate-gems-size-metrics-static tmp/memory_bundle_mem.txt >> 'tmp/memory_metrics.txt'
+ - bundle exec derailed bundle:mem > "${MEMORY_BUNDLE_MEM_FILE}"
+ - scripts/generate-gems-size-metrics-static "${MEMORY_BUNDLE_MEM_FILE}" >> "${METRICS_FILE}"
# Outputs detailed information about objects created while gems are loaded.
# 'derailed_benchmarks' internally uses 'memory_profiler'
- - bundle exec derailed bundle:objects > tmp/memory_bundle_objects.txt
- - scripts/generate-gems-memory-metrics-static tmp/memory_bundle_objects.txt >> 'tmp/memory_metrics.txt'
+ - bundle exec derailed bundle:objects > "${MEMORY_BUNDLE_OBJECTS_FILE}"
+ - scripts/generate-gems-memory-metrics-static "${MEMORY_BUNDLE_OBJECTS_FILE}" >> "${METRICS_FILE}"
artifacts:
paths:
- - tmp/memory_*.txt
- reports:
- metrics: tmp/memory_metrics.txt
- expire_in: 31d
+ - "${METRICS_FILE}"
+ - "${MEMORY_BUNDLE_MEM_FILE}"
+ - "${MEMORY_BUNDLE_OBJECTS_FILE}"
# Show memory usage caused by invoking require per gem.
# Unlike `memory-static`, it hits the app with one request to ensure that any last minute require-s have been called.
@@ -44,12 +51,11 @@ memory-on-boot:
NODE_ENV: "production"
RAILS_ENV: "production"
SETUP_DB: "true"
+ MEMORY_ON_BOOT_FILE: "tmp/memory_on_boot.txt"
script:
- - PATH_TO_HIT="/users/sign_in" CUT_OFF=0.3 bundle exec derailed exec perf:mem >> 'tmp/memory_on_boot.txt'
- - scripts/generate-memory-metrics-on-boot tmp/memory_on_boot.txt >> 'tmp/memory_on_boot_metrics.txt'
+ - PATH_TO_HIT="/users/sign_in" CUT_OFF=0.3 bundle exec derailed exec perf:mem >> "${MEMORY_ON_BOOT_FILE}"
+ - scripts/generate-memory-metrics-on-boot "${MEMORY_ON_BOOT_FILE}" >> "${METRICS_FILE}"
artifacts:
paths:
- - tmp/memory_*.txt
- reports:
- metrics: tmp/memory_on_boot_metrics.txt
- expire_in: 31d
+ - "${METRICS_FILE}"
+ - "${MEMORY_ON_BOOT_FILE}"
diff --git a/.gitlab/ci/reports.gitlab-ci.yml b/.gitlab/ci/reports.gitlab-ci.yml
index a5403073e1b0489687d24adfc6182505c73b6bf7..82453ccda4fcfcda26868c331d09de62f753c2fb 100644
--- a/.gitlab/ci/reports.gitlab-ci.yml
+++ b/.gitlab/ci/reports.gitlab-ci.yml
@@ -1,7 +1,7 @@
include:
- template: Jobs/Code-Quality.gitlab-ci.yml
- - template: Security/SAST.gitlab-ci.yml
- - template: Security/Secret-Detection.gitlab-ci.yml
+ - template: Jobs/SAST.gitlab-ci.yml
+ - template: Jobs/Secret-Detection.gitlab-ci.yml
- template: Security/Dependency-Scanning.gitlab-ci.yml
- template: Security/License-Scanning.gitlab-ci.yml
@@ -13,6 +13,7 @@ code_quality:
paths:
- gl-code-quality-report.json # GitLab-specific
rules: !reference [".reports:rules:code_quality", rules]
+ allow_failure: true
.sast-analyzer:
# We need to re-`extends` from `sast` as the `extends` here overrides the one from the template.
@@ -27,16 +28,15 @@ code_quality:
variables:
SAST_BRAKEMAN_LEVEL: 2 # GitLab-specific
SAST_EXCLUDED_PATHS: "qa, spec, doc, ee/spec, config/gitlab.yml.example, tmp" # GitLab-specific
- SAST_EXCLUDED_ANALYZERS: bandit, flawfinder, phpcs-security-audit, pmd-apex, security-code-scan, spotbugs, eslint
+ SAST_EXCLUDED_ANALYZERS: bandit, flawfinder, phpcs-security-audit, pmd-apex, security-code-scan, spotbugs, eslint, nodejs-scan
brakeman-sast:
- rules: !reference [".reports:rules:sast", rules]
-
-nodejs-scan-sast:
- rules: !reference [".reports:rules:sast", rules]
+ rules: !reference [".reports:rules:brakeman-sast", rules]
+ allow_failure: true
semgrep-sast:
- rules: !reference [".reports:rules:sast", rules]
+ rules: !reference [".reports:rules:semgrep-sast", rules]
+ allow_failure: true
gosec-sast:
variables:
@@ -52,7 +52,8 @@ gosec-sast:
cache:
paths:
- vendor/go
- rules: !reference [".reports:rules:sast", rules]
+ rules: !reference [".reports:rules:gosec-sast", rules]
+ allow_failure: true
.secret-analyzer:
extends: .default-retry
@@ -64,6 +65,7 @@ gosec-sast:
secret_detection:
rules: !reference [".reports:rules:secret_detection", rules]
+ allow_failure: true
.ds-analyzer:
# We need to re-`extends` from `dependency_scanning` as the `extends` here overrides the one from the template.
@@ -88,21 +90,24 @@ gemnasium-dependency_scanning:
# Lower execa severity based on https://gitlab.com/gitlab-org/gitlab/-/issues/223859#note_452922390
- jq '(.vulnerabilities[] | select (.cve == "yarn.lock:execa:gemnasium:05cfa2e8-2d0c-42c1-8894-638e2f12ff3d")).severity = "Medium"' gl-dependency-scanning-report.json > temp.json && mv temp.json gl-dependency-scanning-report.json
rules: !reference [".reports:rules:gemnasium-dependency_scanning", rules]
+ allow_failure: true
bundler-audit-dependency_scanning:
rules: !reference [".reports:rules:bundler-audit-dependency_scanning", rules]
+ allow_failure: true
retire-js-dependency_scanning:
rules: !reference [".reports:rules:retire-js-dependency_scanning", rules]
+ allow_failure: true
gemnasium-python-dependency_scanning:
rules: !reference [".reports:rules:gemnasium-python-dependency_scanning", rules]
+ allow_failure: true
# Analyze dependencies for malicious behavior
# See https://gitlab.com/gitlab-com/gl-security/security-research/package-hunter
.package_hunter-base:
- extends:
- - .default-retry
+ extends: .default-retry
stage: test
image:
name: registry.gitlab.com/gitlab-com/gl-security/security-research/package-hunter-cli:1.1.0
@@ -116,6 +121,8 @@ gemnasium-python-dependency_scanning:
before_script:
- rm -r spec locale .git app/assets/images doc/
- cd .. && tar -I "gzip --best" -cf gitlab.tgz gitlab/
+ script:
+ - node /usr/src/app/cli.js analyze --format gitlab --manager ${PACKAGE_MANAGER} gitlab.tgz | tee ${CI_PROJECT_DIR}/gl-dependency-scanning-report.json
artifacts:
paths:
- gl-dependency-scanning-report.json
@@ -127,15 +134,15 @@ package_hunter-yarn:
extends:
- .package_hunter-base
- .reports:rules:package_hunter-yarn
- script:
- - node /usr/src/app/cli.js analyze --format gitlab --manager yarn gitlab.tgz | tee $CI_PROJECT_DIR/gl-dependency-scanning-report.json
+ variables:
+ PACKAGE_MANAGER: yarn
package_hunter-bundler:
extends:
- .package_hunter-base
- .reports:rules:package_hunter-bundler
- script:
- - node /usr/src/app/cli.js analyze --format gitlab --manager bundler gitlab.tgz | tee $CI_PROJECT_DIR/gl-dependency-scanning-report.json
+ variables:
+ PACKAGE_MANAGER: bundler
license_scanning:
extends: .default-retry
@@ -143,3 +150,4 @@ license_scanning:
artifacts:
expire_in: 1 week # GitLab-specific
rules: !reference [".reports:rules:license_scanning", rules]
+ allow_failure: true
diff --git a/.gitlab/ci/review.gitlab-ci.yml b/.gitlab/ci/review.gitlab-ci.yml
index f20f3276867491d77020d07ff7eb39209ab60f18..2fa8c2519f7986a502a6394a86cb1af07ac13eb3 100644
--- a/.gitlab/ci/review.gitlab-ci.yml
+++ b/.gitlab/ci/review.gitlab-ci.yml
@@ -172,6 +172,8 @@ review-qa-smoke:
- .review-qa-base
- .review:rules:review-qa-smoke
retry: 1 # This is confusing but this means "2 runs at max".
+ variables:
+ QA_RUN_TYPE: review-qa-smoke
script:
- bin/test Test::Instance::Smoke "${CI_ENVIRONMENT_URL}"
@@ -180,6 +182,8 @@ review-qa-all:
- .review-qa-base
- .review:rules:review-qa-all
parallel: 5
+ variables:
+ QA_RUN_TYPE: review-qa-all
script:
- export KNAPSACK_REPORT_PATH=knapsack/master_report.json
- export KNAPSACK_TEST_FILE_PATTERN=qa/specs/features/**/*_spec.rb
diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml
index 016be4af5003ded1e435df6fea1d62e9fb9c4eac..5e04db6701c4123fced7fa8ce3b4b0c3afc0b2e1 100644
--- a/.gitlab/ci/rules.gitlab-ci.yml
+++ b/.gitlab/ci/rules.gitlab-ci.yml
@@ -37,19 +37,22 @@
.if-automated-merge-request: &if-automated-merge-request
if: '$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME == "release-tools/update-gitaly" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME =~ /stable-ee$/'
-.if-merge-request-title-as-if-foss: &if-merge-request-title-as-if-foss
+.if-merge-request-labels-as-if-foss: &if-merge-request-labels-as-if-foss
if: '$CI_MERGE_REQUEST_LABELS =~ /pipeline:run-as-if-foss/'
-.if-merge-request-title-update-caches: &if-merge-request-title-update-caches
+.if-merge-request-labels-as-if-jh: &if-merge-request-labels-as-if-jh
+ if: '$CI_MERGE_REQUEST_LABELS =~ /pipeline:run-as-if-jh/'
+
+.if-merge-request-labels-update-caches: &if-merge-request-labels-update-caches
if: '$CI_MERGE_REQUEST_LABELS =~ /pipeline:update-cache/'
-.if-merge-request-title-run-all-rspec: &if-merge-request-title-run-all-rspec
+.if-merge-request-labels-run-all-rspec: &if-merge-request-labels-run-all-rspec
if: '$CI_MERGE_REQUEST_LABELS =~ /pipeline:run-all-rspec/'
-.if-merge-request-title-run-all-jest: &if-merge-request-title-run-all-jest
+.if-merge-request-labels-run-all-jest: &if-merge-request-labels-run-all-jest
if: '$CI_MERGE_REQUEST_LABELS =~ /pipeline:run-all-jest/'
-.if-merge-request-run-decomposed: &if-merge-request-run-decomposed
+.if-merge-request-labels-run-decomposed: &if-merge-request-labels-run-decomposed
if: '$CI_MERGE_REQUEST_LABELS =~ /pipeline:run-decomposed/'
.if-security-merge-request: &if-security-merge-request
@@ -91,13 +94,6 @@
.if-dot-com-gitlab-org-and-security-tag: &if-dot-com-gitlab-org-and-security-tag
if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE =~ /^gitlab-org($|\/security$)/ && $CI_COMMIT_TAG'
-
-.if-rspec-fail-fast-disabled: &if-rspec-fail-fast-disabled
- if: '$RSPEC_FAIL_FAST_ENABLED != "true"'
-
-.if-rspec-fail-fast-skipped: &if-rspec-fail-fast-skipped
- if: '$CI_MERGE_REQUEST_LABELS =~ /pipeline:skip-rspec-fail-fast/'
-
# For Security merge requests, the gitlab-release-tools-bot triggers a new
# pipeline for the "Pipelines for merged results" feature. If the pipeline
# fails, we notify release managers.
@@ -150,13 +146,6 @@
- ".markdownlint.yml"
- "scripts/lint-doc.sh"
-.docs-deprecations-patterns: &docs-deprecations-patterns
- - "doc/deprecations/index.md"
- - "data/deprecations/*.yml"
- - "data/deprecations/templates/_deprecation_template.md.erb"
- - "lib/tasks/gitlab/docs/compile_deprecations.rake"
- - "tooling/deprecations/docs.rb"
-
.bundler-patterns: &bundler-patterns
- '{Gemfile.lock,*/Gemfile.lock,*/*/Gemfile.lock}'
@@ -368,13 +357,16 @@
- "danger/**/*"
- "tooling/danger/**/*"
+.core-backend-patterns: &core-backend-patterns
+ - "{,jh/}Gemfile{,.lock}"
+ - "{,ee/,jh/}config/**/*.rb"
+
.core-frontend-patterns: &core-frontend-patterns
- "{package.json,yarn.lock}"
- "babel.config.js"
- "jest.config.{base,integration,unit}.js"
- "config/helpers/**/*.js"
- "vendor/assets/javascripts/**/*"
- - "{,ee}/app/assets/**/*.graphql"
################
# Shared rules #
@@ -383,11 +375,11 @@
rules:
- <<: *if-default-branch-schedule-2-hourly
- <<: *if-security-schedule
- - <<: *if-merge-request-title-update-caches
+ - <<: *if-merge-request-labels-update-caches
.shared:rules:update-gitaly-binaries-cache:
rules:
- - <<: *if-merge-request-title-update-caches
+ - <<: *if-merge-request-labels-update-caches
- changes: *gitaly-patterns
######################
@@ -471,12 +463,6 @@
changes: *docs-patterns
when: on_success
-.docs:rules:deprecations:
- rules:
- - <<: *if-default-refs
- changes: *docs-deprecations-patterns
- when: on_success
-
##################
# GraphQL rules #
##################
@@ -502,35 +488,52 @@
.frontend:rules:compile-test-assets:
rules:
- changes: *code-backstage-qa-patterns
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
.frontend:rules:compile-test-assets-as-if-foss:
rules:
- <<: *if-not-ee
when: never
+ - <<: *if-merge-request-labels-as-if-foss
+ - <<: *if-merge-request-labels-run-all-rspec
+ - changes: *code-backstage-qa-patterns
+ - changes: *startup-css-patterns
+
+.frontend:rules:compile-test-assets-as-if-jh:
+ rules:
+ - <<: *if-not-ee
+ when: never
+ - <<: *if-merge-request-labels-as-if-jh
+ - <<: *if-merge-request-labels-run-all-rspec
- changes: *code-backstage-qa-patterns
- - <<: *if-merge-request-title-run-all-rspec
+ - changes: *startup-css-patterns
.frontend:rules:default-frontend-jobs:
rules:
- <<: *if-default-refs
changes: *code-backstage-patterns
-.frontend:rules:default-frontend-jobs-ee:
+.frontend:rules:default-frontend-jobs-as-if-foss:
rules:
- <<: *if-not-ee
when: never
- - <<: *if-default-refs
+ - <<: *if-security-merge-request
changes: *code-backstage-patterns
+ - <<: *if-merge-request-labels-as-if-foss
+ - <<: *if-merge-request-labels-run-all-rspec
+ - <<: *if-merge-request
+ changes: *startup-css-patterns
+ - <<: *if-merge-request
+ changes: *ci-patterns
-.frontend:rules:default-frontend-jobs-as-if-foss:
+.frontend:rules:default-frontend-jobs-as-if-jh:
rules:
- <<: *if-not-ee
when: never
- <<: *if-security-merge-request
changes: *code-backstage-patterns
- - <<: *if-merge-request-title-as-if-foss
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-as-if-jh
+ - <<: *if-merge-request-labels-run-all-rspec
- <<: *if-merge-request
changes: *startup-css-patterns
- <<: *if-merge-request
@@ -538,7 +541,7 @@
.frontend:rules:jest:
rules:
- - <<: *if-merge-request-title-run-all-jest
+ - <<: *if-merge-request-labels-run-all-jest
- <<: *if-default-refs
changes: *core-frontend-patterns
- <<: *if-merge-request
@@ -558,7 +561,7 @@
when: never
- <<: *if-automated-merge-request
when: never
- - <<: *if-merge-request-title-run-all-jest
+ - <<: *if-merge-request-labels-run-all-jest
when: never
- <<: *if-default-refs
changes: *core-frontend-patterns
@@ -576,7 +579,8 @@
rules:
- <<: *if-not-ee
when: never
- - <<: *if-merge-request-title-as-if-foss
+ # We already have `static-analysis as-if-foss` which already runs `lint:eslint:all` if the `pipeline:run-as-if-foss` label is set.
+ - <<: *if-merge-request-labels-as-if-foss
when: never
- <<: *if-merge-request
changes: *frontend-patterns
@@ -646,8 +650,8 @@
when: never
- <<: *if-security-merge-request
changes: *code-qa-patterns
- - <<: *if-merge-request-title-as-if-foss
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-as-if-foss
+ - <<: *if-merge-request-labels-run-all-rspec
- <<: *if-merge-request
changes: *ci-patterns
@@ -673,12 +677,13 @@
###############
.rails:rules:decomposed-databases:
rules:
- - <<: *if-merge-request-run-decomposed
- allow_failure: true
+ - <<: *if-merge-request-labels-run-decomposed
.rails:rules:ee-and-foss-migration:
rules:
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
+ - <<: *if-merge-request
+ changes: *core-backend-patterns
- <<: *if-merge-request
changes: *ci-patterns
- <<: *if-merge-request
@@ -695,7 +700,10 @@
when: never
- <<: *if-automated-merge-request
when: never
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
+ when: never
+ - <<: *if-merge-request
+ changes: *core-backend-patterns
when: never
- <<: *if-merge-request
changes: *ci-patterns
@@ -708,7 +716,7 @@
rules:
- <<: *if-merge-request
changes: *db-patterns
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
.rails:rules:db:gitlabcom-database-testing:
rules:
@@ -720,7 +728,9 @@
.rails:rules:ee-and-foss-unit:
rules:
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
+ - <<: *if-merge-request
+ changes: *core-backend-patterns
- <<: *if-merge-request
changes: *ci-patterns
- <<: *if-automated-merge-request
@@ -735,7 +745,10 @@
when: never
- <<: *if-automated-merge-request
when: never
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
+ when: never
+ - <<: *if-merge-request
+ changes: *core-backend-patterns
when: never
- <<: *if-merge-request
changes: *ci-patterns
@@ -745,7 +758,9 @@
.rails:rules:ee-and-foss-integration:
rules:
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
+ - <<: *if-merge-request
+ changes: *core-backend-patterns
- <<: *if-merge-request
changes: *ci-patterns
- <<: *if-automated-merge-request
@@ -760,7 +775,10 @@
when: never
- <<: *if-automated-merge-request
when: never
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
+ when: never
+ - <<: *if-merge-request
+ changes: *core-backend-patterns
when: never
- <<: *if-merge-request
changes: *ci-patterns
@@ -770,7 +788,9 @@
.rails:rules:ee-and-foss-system:
rules:
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
+ - <<: *if-merge-request
+ changes: *core-backend-patterns
- <<: *if-merge-request
changes: *ci-patterns
- <<: *if-automated-merge-request
@@ -785,7 +805,10 @@
when: never
- <<: *if-automated-merge-request
when: never
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
+ when: never
+ - <<: *if-merge-request
+ changes: *core-backend-patterns
when: never
- <<: *if-merge-request
changes: *ci-patterns
@@ -795,7 +818,9 @@
.rails:rules:ee-and-foss-fast_spec_helper:
rules:
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
+ - <<: *if-merge-request
+ changes: *core-backend-patterns
- <<: *if-merge-request
changes: *ci-patterns
- <<: *if-automated-merge-request
@@ -810,7 +835,10 @@
when: never
- <<: *if-automated-merge-request
when: never
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
+ when: never
+ - <<: *if-merge-request
+ changes: *core-backend-patterns
when: never
- <<: *if-merge-request
changes: *ci-patterns
@@ -821,13 +849,15 @@
.rails:rules:code-backstage-qa:
rules:
- changes: *code-backstage-qa-patterns
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
.rails:rules:ee-only-migration:
rules:
- <<: *if-not-ee
when: never
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
+ - <<: *if-merge-request
+ changes: *core-backend-patterns
- <<: *if-merge-request
changes: *ci-patterns
- <<: *if-merge-request
@@ -846,7 +876,10 @@
when: never
- <<: *if-automated-merge-request
when: never
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
+ when: never
+ - <<: *if-merge-request
+ changes: *core-backend-patterns
when: never
- <<: *if-merge-request
changes: *ci-patterns
@@ -859,7 +892,9 @@
rules:
- <<: *if-not-ee
when: never
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
+ - <<: *if-merge-request
+ changes: *core-backend-patterns
- <<: *if-merge-request
changes: *ci-patterns
- <<: *if-automated-merge-request
@@ -876,7 +911,10 @@
when: never
- <<: *if-automated-merge-request
when: never
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
+ when: never
+ - <<: *if-merge-request
+ changes: *core-backend-patterns
when: never
- <<: *if-merge-request
changes: *ci-patterns
@@ -888,7 +926,9 @@
rules:
- <<: *if-not-ee
when: never
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
+ - <<: *if-merge-request
+ changes: *core-backend-patterns
- <<: *if-merge-request
changes: *ci-patterns
- <<: *if-automated-merge-request
@@ -905,7 +945,10 @@
when: never
- <<: *if-automated-merge-request
when: never
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
+ when: never
+ - <<: *if-merge-request
+ changes: *core-backend-patterns
when: never
- <<: *if-merge-request
changes: *ci-patterns
@@ -917,7 +960,9 @@
rules:
- <<: *if-not-ee
when: never
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
+ - <<: *if-merge-request
+ changes: *core-backend-patterns
- <<: *if-merge-request
changes: *ci-patterns
- <<: *if-automated-merge-request
@@ -934,7 +979,10 @@
when: never
- <<: *if-automated-merge-request
when: never
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
+ when: never
+ - <<: *if-merge-request
+ changes: *core-backend-patterns
when: never
- <<: *if-merge-request
changes: *ci-patterns
@@ -946,12 +994,14 @@
rules:
- <<: *if-not-ee
when: never
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
+ - <<: *if-merge-request
+ changes: *core-backend-patterns
- <<: *if-merge-request
changes: *ci-patterns
- <<: *if-security-merge-request
changes: *db-patterns
- - <<: *if-merge-request-title-as-if-foss
+ - <<: *if-merge-request-labels-as-if-foss
changes: *db-patterns
- <<: *if-automated-merge-request
changes: *db-patterns
@@ -966,13 +1016,16 @@
when: never
- <<: *if-automated-merge-request
when: never
+ - <<: *if-merge-request
+ changes: *core-backend-patterns
+ when: never
- <<: *if-merge-request
changes: *ci-patterns
when: never
- <<: *if-security-merge-request
changes: *db-patterns
when: never
- - <<: *if-merge-request-title-as-if-foss
+ - <<: *if-merge-request-labels-as-if-foss
changes: *db-patterns
when: never
@@ -980,7 +1033,9 @@
rules:
- <<: *if-not-ee
when: never
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
+ - <<: *if-merge-request
+ changes: *core-backend-patterns
- <<: *if-merge-request
changes: *ci-patterns
- <<: *if-automated-merge-request
@@ -989,7 +1044,7 @@
when: never
- <<: *if-security-merge-request
changes: *backend-patterns
- - <<: *if-merge-request-title-as-if-foss
+ - <<: *if-merge-request-labels-as-if-foss
changes: *backend-patterns
.rails:rules:as-if-foss-unit:minimal:
@@ -1000,19 +1055,24 @@
when: never
- <<: *if-automated-merge-request
when: never
+ - <<: *if-merge-request
+ changes: *core-backend-patterns
+ when: never
- <<: *if-merge-request
changes: *ci-patterns
when: never
- <<: *if-security-merge-request
changes: *backend-patterns
- - <<: *if-merge-request-title-as-if-foss
+ - <<: *if-merge-request-labels-as-if-foss
changes: *backend-patterns
.rails:rules:as-if-foss-integration:
rules:
- <<: *if-not-ee
when: never
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
+ - <<: *if-merge-request
+ changes: *core-backend-patterns
- <<: *if-merge-request
changes: *ci-patterns
- <<: *if-automated-merge-request
@@ -1021,7 +1081,7 @@
when: never
- <<: *if-security-merge-request
changes: *backend-patterns
- - <<: *if-merge-request-title-as-if-foss
+ - <<: *if-merge-request-labels-as-if-foss
changes: *backend-patterns
.rails:rules:as-if-foss-integration:minimal:
@@ -1032,19 +1092,24 @@
when: never
- <<: *if-automated-merge-request
when: never
+ - <<: *if-merge-request
+ changes: *core-backend-patterns
+ when: never
- <<: *if-merge-request
changes: *ci-patterns
when: never
- <<: *if-security-merge-request
changes: *backend-patterns
- - <<: *if-merge-request-title-as-if-foss
+ - <<: *if-merge-request-labels-as-if-foss
changes: *backend-patterns
.rails:rules:as-if-foss-system:
rules:
- <<: *if-not-ee
when: never
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
+ - <<: *if-merge-request
+ changes: *core-backend-patterns
- <<: *if-merge-request
changes: *ci-patterns
- <<: *if-automated-merge-request
@@ -1053,7 +1118,7 @@
when: never
- <<: *if-security-merge-request
changes: *code-backstage-patterns
- - <<: *if-merge-request-title-as-if-foss
+ - <<: *if-merge-request-labels-as-if-foss
changes: *code-backstage-patterns
.rails:rules:as-if-foss-system:minimal:
@@ -1064,24 +1129,27 @@
when: never
- <<: *if-automated-merge-request
when: never
+ - <<: *if-merge-request
+ changes: *core-backend-patterns
+ when: never
- <<: *if-merge-request
changes: *ci-patterns
when: never
- <<: *if-security-merge-request
changes: *code-backstage-patterns
- - <<: *if-merge-request-title-as-if-foss
+ - <<: *if-merge-request-labels-as-if-foss
changes: *code-backstage-patterns
.rails:rules:ee-and-foss-db-library-code:
rules:
- changes: *db-library-patterns
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
.rails:rules:ee-mr-and-default-branch-only:
rules:
- <<: *if-not-ee
when: never
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
- <<: *if-merge-request
changes: *code-backstage-patterns
- <<: *if-default-branch-refs
@@ -1090,13 +1158,13 @@
.rails:rules:detect-tests:
rules:
- changes: *code-backstage-patterns
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
.rails:rules:rspec-foss-impact:
rules:
- <<: *if-not-ee
when: never
- - <<: *if-merge-request-title-as-if-foss
+ - <<: *if-merge-request-labels-as-if-foss
when: never
- <<: *if-security-merge-request
changes: *code-backstage-patterns
@@ -1105,10 +1173,6 @@
.rails:rules:rspec fail-fast:
rules:
- - <<: *if-rspec-fail-fast-disabled
- when: never
- - <<: *if-rspec-fail-fast-skipped
- when: never
- <<: *if-not-ee
when: never
- <<: *if-security-merge-request
@@ -1118,10 +1182,6 @@
.rails:rules:fail-pipeline-early:
rules:
- - <<: *if-rspec-fail-fast-disabled
- when: never
- - <<: *if-rspec-fail-fast-skipped
- when: never
- <<: *if-not-ee
when: never
- <<: *if-security-merge-request
@@ -1136,7 +1196,7 @@
- <<: *if-not-ee
when: never
- <<: *if-default-branch-schedule-nightly
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
.rails:rules:rspec-coverage:
rules:
@@ -1146,7 +1206,7 @@
changes: *code-backstage-patterns
when: always
- <<: *if-default-branch-schedule-2-hourly
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
when: always
.rails:rules:default-branch-schedule-nightly--code-backstage:
@@ -1181,7 +1241,7 @@
rules:
- <<: *if-not-ee
when: never
- - <<: *if-merge-request-title-as-if-foss
+ - <<: *if-merge-request-labels-as-if-foss
changes: *code-backstage-qa-patterns
- <<: *if-security-merge-request
changes: *code-backstage-qa-patterns
@@ -1196,7 +1256,7 @@
rules:
- <<: *if-merge-request
changes: ["vendor/gems/mail-smtp_pool/**/*"]
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
##################
# Releases rules #
@@ -1222,75 +1282,76 @@
when: never
- <<: *if-default-refs
changes: *code-backstage-patterns
- allow_failure: true
-.reports:rules:sast:
+.reports:rules:brakeman-sast:
rules:
- - if: '$SAST_DISABLED || $GITLAB_FEATURES !~ /\bsast\b/'
+ - if: $SAST_DISABLED
when: never
- - <<: *if-default-refs
- changes: *code-backstage-qa-patterns
- allow_failure: true
+ - if: $SAST_EXCLUDED_ANALYZERS =~ /brakeman/
+ when: never
+ - changes:
+ - '**/*.rb'
+ - '**/Gemfile'
+
+.reports:rules:gosec-sast:
+ rules:
+ - if: $SAST_DISABLED
+ when: never
+ - if: $SAST_EXCLUDED_ANALYZERS =~ /gosec/
+ when: never
+ - changes:
+ - '**/*.go'
+
+.reports:rules:semgrep-sast:
+ rules:
+ - if: $SAST_DISABLED
+ when: never
+ - if: $SAST_EXCLUDED_ANALYZERS =~ /semgrep/
+ when: never
+ - changes:
+ - '**/*.py'
+ - '**/*.js'
+ - '**/*.jsx'
+ - '**/*.ts'
+ - '**/*.tsx'
+ - '**/*.c'
+ - '**/*.go'
.reports:rules:secret_detection:
rules:
- if: '$SECRET_DETECTION_DISABLED'
when: never
- - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' # The Secret-Detection template already has a `secret_detection_default_branch` job
- when: never
- changes: *code-backstage-qa-patterns
- allow_failure: true
.reports:rules:gemnasium-dependency_scanning:
rules:
- - if: '$DEPENDENCY_SCANNING_DISABLED || $GITLAB_FEATURES !~ /\bdependency_scanning\b/ || $DS_EXCLUDED_ANALYZERS =~ /gemnasium([^-]|$)/'
+ - if: '$DEPENDENCY_SCANNING_DISABLED || $GITLAB_FEATURES !~ /\bdependency_scanning\b/ || $DS_EXCLUDED_ANALYZERS =~ /gemnasium([^-]|$)/ || $DS_DEFAULT_ANALYZERS !~ /gemnasium([^-]|$)/'
when: never
- - <<: *if-default-refs
- changes: *dependency-patterns
- allow_failure: true
+ - changes: *dependency-patterns
.reports:rules:bundler-audit-dependency_scanning:
rules:
- - if: '$DEPENDENCY_SCANNING_DISABLED || $GITLAB_FEATURES !~ /\bdependency_scanning\b/ || $DS_EXCLUDED_ANALYZERS =~ /bundler-audit/'
+ - if: '$DEPENDENCY_SCANNING_DISABLED || $GITLAB_FEATURES !~ /\bdependency_scanning\b/ || $DS_EXCLUDED_ANALYZERS =~ /bundler-audit/ || $DS_DEFAULT_ANALYZERS !~ /bundler-audit/'
when: never
- - <<: *if-default-refs
- changes: *bundler-patterns
- allow_failure: true
+ - changes: *bundler-patterns
.reports:rules:retire-js-dependency_scanning:
rules:
- - if: '$DEPENDENCY_SCANNING_DISABLED || $GITLAB_FEATURES !~ /\bdependency_scanning\b/ || $DS_EXCLUDED_ANALYZERS =~ /retire.js/'
+ - if: '$DEPENDENCY_SCANNING_DISABLED || $GITLAB_FEATURES !~ /\bdependency_scanning\b/ || $DS_EXCLUDED_ANALYZERS =~ /retire.js/ || $DS_DEFAULT_ANALYZERS !~ /retire.js/'
when: never
- - <<: *if-default-refs
- changes: *nodejs-patterns
- allow_failure: true
+ - changes: *nodejs-patterns
.reports:rules:gemnasium-python-dependency_scanning:
rules:
- - if: '$DEPENDENCY_SCANNING_DISABLED || $GITLAB_FEATURES !~ /\bdependency_scanning\b/ || $DS_EXCLUDED_ANALYZERS =~ /gemnasium-python/'
+ - if: '$DEPENDENCY_SCANNING_DISABLED || $GITLAB_FEATURES !~ /\bdependency_scanning\b/ || $DS_EXCLUDED_ANALYZERS =~ /gemnasium-python/ || $DS_DEFAULT_ANALYZERS !~ /gemnasium-python/'
when: never
- - <<: *if-default-refs
- changes: *python-patterns
- allow_failure: true
-
-.reports:rules:dast:
- rules:
- - if: '$DAST_DISABLED || $GITLAB_FEATURES !~ /\bdast\b/'
- when: never
- - <<: *if-dot-com-gitlab-org-merge-request
- changes: *frontend-patterns
- allow_failure: true
- - <<: *if-dot-com-gitlab-org-merge-request
- changes: *code-qa-patterns
- when: manual
- allow_failure: true
+ - changes: *python-patterns
.reports:rules:schedule-dast:
rules:
- if: '$DAST_DISABLED || $GITLAB_FEATURES !~ /\bdast\b/'
when: never
- - <<: *if-default-branch-schedule-nightly
- allow_failure: true
+ - <<: *if-dot-com-ee-nightly-schedule
.reports:rules:package_hunter-yarn:
rules:
@@ -1310,11 +1371,9 @@
.reports:rules:license_scanning:
rules:
- - if: '$LICENSE_SCANNING_DISABLED || $GITLAB_FEATURES !~ /\blicense_scanning\b/'
+ - if: '$LICENSE_MANAGEMENT_DISABLED || $GITLAB_FEATURES !~ /\blicense_scanning\b/'
when: never
- - <<: *if-default-refs
- changes: *code-backstage-qa-patterns
- allow_failure: true
+ - changes: *code-backstage-qa-patterns
################
# Review rules #
@@ -1534,6 +1593,15 @@
changes: *code-backstage-patterns
when: on_success
+.setup:rules:add-jh-folder:
+ rules:
+ - <<: *if-not-ee
+ when: never
+ - <<: *if-merge-request-labels-as-if-jh
+ - <<: *if-merge-request-labels-run-all-rspec
+ - changes: *code-backstage-qa-patterns
+ - changes: *startup-css-patterns
+
#######################
# Test metadata rules #
#######################
@@ -1541,7 +1609,7 @@
rules:
- changes: *code-backstage-patterns
when: on_success
- - <<: *if-merge-request-title-run-all-rspec
+ - <<: *if-merge-request-labels-run-all-rspec
.test-metadata:rules:update-tests-metadata:
rules:
diff --git a/.gitlab/ci/setup.gitlab-ci.yml b/.gitlab/ci/setup.gitlab-ci.yml
index 60a1ad54cffe4fb70d381c8e3189aa8838ff9282..eb7a5afad3d7316261f9c619424b02811aeca1a8 100644
--- a/.gitlab/ci/setup.gitlab-ci.yml
+++ b/.gitlab/ci/setup.gitlab-ci.yml
@@ -101,3 +101,19 @@ detect-tests as-if-foss:
MATCHED_TESTS_FILE: tmp/matching_foss_tests.txt
before_script:
- '[ "$FOSS_ONLY" = "1" ] && rm -rf ee/ qa/spec/ee/ qa/qa/specs/features/ee/ qa/qa/ee/ qa/qa/ee.rb'
+
+add-jh-folder:
+ extends: .setup:rules:add-jh-folder
+ image: ${GITLAB_DEPENDENCY_PROXY}alpine:edge
+ stage: prepare
+ before_script:
+ - apk add --no-cache --update curl bash
+ script:
+ - curl --location -o "jh-folder.tar.gz" "https://gitlab.com/gitlab-jh/gitlab/-/archive/main-jh/gitlab-main-jh.tar.gz?path=jh"
+ - tar -xf "jh-folder.tar.gz"
+ - mv gitlab-main-jh-jh/jh/ ./
+ - ls -l jh/
+ artifacts:
+ expire_in: 2d
+ paths:
+ - jh/
diff --git a/.gitlab/ci/static-analysis.gitlab-ci.yml b/.gitlab/ci/static-analysis.gitlab-ci.yml
index 1394085b6e49f4a59a579db383dc4d9470443c42..85df68e903065e2f3096b789187dc636fc45d27f 100644
--- a/.gitlab/ci/static-analysis.gitlab-ci.yml
+++ b/.gitlab/ci/static-analysis.gitlab-ci.yml
@@ -35,6 +35,17 @@ static-analysis:
paths:
- tmp/feature_flags/
+static-analysis-with-database:
+ extends:
+ - .static-analysis-base
+ - .static-analysis:rules:ee-and-foss
+ - .use-pg12
+ stage: test
+ script:
+ - bundle exec rake lint:static_verification_with_database
+ variables:
+ SETUP_DB: "true"
+
static-analysis as-if-foss:
extends:
- static-analysis
diff --git a/.gitlab/ci/test-metadata.gitlab-ci.yml b/.gitlab/ci/test-metadata.gitlab-ci.yml
index ac719977975b2c43b514c61836e33913a3c7abed..2d96fb6d4b07c61d149c663aefb439a72e3f0fcd 100644
--- a/.gitlab/ci/test-metadata.gitlab-ci.yml
+++ b/.gitlab/ci/test-metadata.gitlab-ci.yml
@@ -29,8 +29,7 @@ update-tests-metadata:
- retrieve-tests-metadata
- setup-test-env
- rspec migration pg12
- - rspec frontend_fixture
- - rspec-ee frontend_fixture
+ - rspec-all frontend_fixture
- rspec unit pg12
- rspec integration pg12
- rspec system pg12
diff --git a/.gitlab/issue_templates/Feature Flag Roll Out.md b/.gitlab/issue_templates/Feature Flag Roll Out.md
index 1576f6e8f537b9a5e6495ed188d9cb9d84c47710..00b396bac4e4eea7d894718cfc2da7a89601af60 100644
--- a/.gitlab/issue_templates/Feature Flag Roll Out.md
+++ b/.gitlab/issue_templates/Feature Flag Roll Out.md
@@ -24,26 +24,6 @@ Are there any other stages or teams involved that need to be kept in the loop?
- The Delivery Team
-->
-## The Rollout Plan
-
-- Partial Rollout on GitLab.com with testing groups
-- Rollout on GitLab.com for a certain period (How long)
-- Percentage Rollout on GitLab.com
-- Rollout Feature for everyone as soon as it's ready
-
-
-
-## Testing Groups/Projects/Users
-
-
-
-- `gitlab-org/gitlab` project
-- `gitlab-org/gitlab-foss` project
-- `gitlab-com/www-gitlab-com` project
-- `gitlab-org`/`gitlab-com` groups
-- ...
-
-
## Expectations
### What are we expecting to happen?
@@ -62,17 +42,30 @@ Are there any other stages or teams involved that need to be kept in the loop?
### Rollout on non-production environments
-- [ ] Ensure that the feature MRs have been deployed to non-production environments.
+- Ensure that the feature MRs have been deployed to non-production environments.
- [ ] `/chatops run auto_deploy status Expand for Details
+
+- [ ] Title:
+ - Length limit: 7 words (not including articles or prepositions).
+ - Capitalization: ensure the title is [sentence cased](https://design.gitlab.com/content/punctuation#case).
+ - No Markdown `` `code` `` formatting in the title, as it doesn't render correctly in the release post.
+- [ ] Consistency:
+ - Ensure that all resources (docs, deprecation, etc.) refer to the feature with the same term / feature name.
+- [ ] Content:
+ - Make sure the deprecation is accurate based on your understanding. Look for typos or grammar mistakes. Work with PM and PMM to ensure a consistent GitLab style and tone for messaging, based on other features and deprecations.
+ - Review use of whitespace and bullet lists. Will the deprecation item be easily scannable when published? Consider adding line breaks or breaking content into bullets if you have more than a few sentences.
+ - Make sure there aren't acronyms readers may not understand per
{{ username }}
+ {{ username }}
+
+ {{ s__('ClusterAgents|For more troubleshooting information go to') }}
+
- {{
- __(
- "To protect this issue's confidentiality, a private fork of this project was selected.",
- )
- }}
+ {{ $options.i18n.privateForkSelected }}
- {{ __('No forks are available to you.') }}
-
+
- {{ - __(`Value Stream Analytics gives an overview -of how much time it takes to go from idea to production in your project.`) - }} -
- -
-
+
-
+
-
+
diff --git a/app/assets/javascripts/jobs/components/table/cells/actions_cell.vue b/app/assets/javascripts/jobs/components/table/cells/actions_cell.vue
index cfbd92c0fc0745aec5be02f7d83bb630a336f6f8..51251c0cacc1cbc4ce9cf30e35c72771c0f8117c 100644
--- a/app/assets/javascripts/jobs/components/table/cells/actions_cell.vue
+++ b/app/assets/javascripts/jobs/components/table/cells/actions_cell.vue
@@ -18,6 +18,7 @@ import cancelJobMutation from '../graphql/mutations/job_cancel.mutation.graphql'
import playJobMutation from '../graphql/mutations/job_play.mutation.graphql';
import retryJobMutation from '../graphql/mutations/job_retry.mutation.graphql';
import unscheduleJobMutation from '../graphql/mutations/job_unschedule.mutation.graphql';
+import { reportMessageToSentry } from '../../../utils';
export default {
ACTIONS_DOWNLOAD_ARTIFACTS,
@@ -34,6 +35,7 @@ export default {
jobPlay: 'jobPlay',
jobUnschedule: 'jobUnschedule',
playJobModalId: 'play-job-modal',
+ name: 'JobActionsCell',
components: {
GlButton,
GlButtonGroup,
@@ -99,15 +101,17 @@ export default {
variables: { id: this.job.id },
});
if (errors.length > 0) {
- this.reportFailure();
+ reportMessageToSentry(this.$options.name, errors.join(', '), {});
+ this.showToastMessage();
} else {
eventHub.$emit('jobActionPerformed');
}
- } catch {
- this.reportFailure();
+ } catch (failure) {
+ reportMessageToSentry(this.$options.name, failure, {});
+ this.showToastMessage();
}
},
- reportFailure() {
+ showToastMessage() {
const toastProps = {
text: this.$options.GENERIC_ERROR,
variant: 'danger',
@@ -135,17 +139,14 @@ export default {
-
-
+
@@ -191,5 +192,14 @@ export default {
/>
+
diff --git a/app/assets/javascripts/jobs/components/table/jobs_table.vue b/app/assets/javascripts/jobs/components/table/jobs_table.vue
index 076c0e78b11b5f77efc6fdaf9420fc5a4c1ab4eb..298c99c416207482442d31abdf4e826b6d05c51b 100644
--- a/app/assets/javascripts/jobs/components/table/jobs_table.vue
+++ b/app/assets/javascripts/jobs/components/table/jobs_table.vue
@@ -141,7 +141,7 @@ export default {
-
+
diff --git a/app/assets/javascripts/jobs/store/actions.js b/app/assets/javascripts/jobs/store/actions.js
index 53e3dbbad0dcc36feedca6e72510fe4d3d122a2f..927ba7c7e1e85bb99971986015131e593bdd8a3c 100644
--- a/app/assets/javascripts/jobs/store/actions.js
+++ b/app/assets/javascripts/jobs/store/actions.js
@@ -18,16 +18,16 @@ import * as types from './mutation_types';
export const init = ({ dispatch }, { endpoint, logState, pagePath }) => {
dispatch('setJobEndpoint', endpoint);
- dispatch('setTraceOptions', {
+ dispatch('setJobLogOptions', {
logState,
pagePath,
});
- return Promise.all([dispatch('fetchJob'), dispatch('fetchTrace')]);
+ return Promise.all([dispatch('fetchJob'), dispatch('fetchJobLog')]);
};
export const setJobEndpoint = ({ commit }, endpoint) => commit(types.SET_JOB_ENDPOINT, endpoint);
-export const setTraceOptions = ({ commit }, options) => commit(types.SET_TRACE_OPTIONS, options);
+export const setJobLogOptions = ({ commit }, options) => commit(types.SET_JOB_LOG_OPTIONS, options);
export const hideSidebar = ({ commit }) => commit(types.HIDE_SIDEBAR);
export const showSidebar = ({ commit }) => commit(types.SHOW_SIDEBAR);
@@ -107,7 +107,7 @@ export const receiveJobError = ({ commit }) => {
};
/**
- * Job's Trace
+ * Job Log
*/
export const scrollTop = ({ dispatch }) => {
scrollUp();
@@ -156,59 +156,62 @@ export const toggleScrollAnimation = ({ commit }, toggle) =>
* Responsible to handle automatic scroll
*/
export const toggleScrollisInBottom = ({ commit }, toggle) => {
- commit(types.TOGGLE_IS_SCROLL_IN_BOTTOM_BEFORE_UPDATING_TRACE, toggle);
+ commit(types.TOGGLE_IS_SCROLL_IN_BOTTOM_BEFORE_UPDATING_JOB_LOG, toggle);
};
-export const requestTrace = ({ commit }) => commit(types.REQUEST_TRACE);
+export const requestJobLog = ({ commit }) => commit(types.REQUEST_JOB_LOG);
-export const fetchTrace = ({ dispatch, state }) =>
+export const fetchJobLog = ({ dispatch, state }) =>
+ // update trace endpoint once BE compeletes trace re-naming in #340626
axios
- .get(`${state.traceEndpoint}/trace.json`, {
- params: { state: state.traceState },
+ .get(`${state.jobLogEndpoint}/trace.json`, {
+ params: { state: state.jobLogState },
})
.then(({ data }) => {
dispatch('toggleScrollisInBottom', isScrolledToBottom());
- dispatch('receiveTraceSuccess', data);
+ dispatch('receiveJobLogSuccess', data);
if (data.complete) {
- dispatch('stopPollingTrace');
- } else if (!state.traceTimeout) {
- dispatch('startPollingTrace');
+ dispatch('stopPollingJobLog');
+ } else if (!state.jobLogTimeout) {
+ dispatch('startPollingJobLog');
}
})
.catch((e) => {
if (e.response.status === httpStatusCodes.FORBIDDEN) {
- dispatch('receiveTraceUnauthorizedError');
+ dispatch('receiveJobLogUnauthorizedError');
} else {
reportToSentry('job_actions', e);
- dispatch('receiveTraceError');
+ dispatch('receiveJobLogError');
}
});
-export const startPollingTrace = ({ dispatch, commit }) => {
- const traceTimeout = setTimeout(() => {
- commit(types.SET_TRACE_TIMEOUT, 0);
- dispatch('fetchTrace');
+export const startPollingJobLog = ({ dispatch, commit }) => {
+ const jobLogTimeout = setTimeout(() => {
+ commit(types.SET_JOB_LOG_TIMEOUT, 0);
+ dispatch('fetchJobLog');
}, 4000);
- commit(types.SET_TRACE_TIMEOUT, traceTimeout);
+ commit(types.SET_JOB_LOG_TIMEOUT, jobLogTimeout);
};
-export const stopPollingTrace = ({ state, commit }) => {
- clearTimeout(state.traceTimeout);
- commit(types.SET_TRACE_TIMEOUT, 0);
- commit(types.STOP_POLLING_TRACE);
+export const stopPollingJobLog = ({ state, commit }) => {
+ clearTimeout(state.jobLogTimeout);
+ commit(types.SET_JOB_LOG_TIMEOUT, 0);
+ commit(types.STOP_POLLING_JOB_LOG);
};
-export const receiveTraceSuccess = ({ commit }, log) => commit(types.RECEIVE_TRACE_SUCCESS, log);
-export const receiveTraceError = ({ dispatch }) => {
- dispatch('stopPollingTrace');
+export const receiveJobLogSuccess = ({ commit }, log) => commit(types.RECEIVE_JOB_LOG_SUCCESS, log);
+
+export const receiveJobLogError = ({ dispatch }) => {
+ dispatch('stopPollingJobLog');
createFlash({
message: __('An error occurred while fetching the job log.'),
});
};
-export const receiveTraceUnauthorizedError = ({ dispatch }) => {
- dispatch('stopPollingTrace');
+
+export const receiveJobLogUnauthorizedError = ({ dispatch }) => {
+ dispatch('stopPollingJobLog');
createFlash({
message: __('The current user is not authorized to access the job log.'),
});
@@ -248,6 +251,7 @@ export const fetchJobsForStage = ({ dispatch }, stage = {}) => {
};
export const receiveJobsForStageSuccess = ({ commit }, data) =>
commit(types.RECEIVE_JOBS_FOR_STAGE_SUCCESS, data);
+
export const receiveJobsForStageError = ({ commit }) => {
commit(types.RECEIVE_JOBS_FOR_STAGE_ERROR);
createFlash({
diff --git a/app/assets/javascripts/jobs/store/getters.js b/app/assets/javascripts/jobs/store/getters.js
index 6cb96bee07daf8b0fa5b2640abea6ee47f0f6478..9d2558222501d15e27ef9c272c31dc1ba40815bd 100644
--- a/app/assets/javascripts/jobs/store/getters.js
+++ b/app/assets/javascripts/jobs/store/getters.js
@@ -21,11 +21,12 @@ export const shouldRenderTriggeredLabel = (state) => isString(state.job.started)
export const hasEnvironment = (state) => !isEmpty(state.job.deployment_status);
/**
- * Checks if it the job has trace.
+ * Checks if it the job has a log.
* Used to check if it should render the job log or the empty state
* @returns {Boolean}
*/
-export const hasTrace = (state) =>
+export const hasJobLog = (state) =>
+ // update has_trace once BE compeletes trace re-naming in #340626
state.job.has_trace || (!isEmpty(state.job.status) && state.job.status.group === 'running');
export const emptyStateIllustration = (state) => state?.job?.status?.illustration || {};
@@ -43,7 +44,7 @@ export const shouldRenderSharedRunnerLimitWarning = (state) =>
!isEmpty(state.job.runners.quota) &&
state.job.runners.quota.used >= state.job.runners.quota.limit;
-export const isScrollingDown = (state) => isScrolledToBottom() && !state.isTraceComplete;
+export const isScrollingDown = (state) => isScrolledToBottom() && !state.isJobLogComplete;
export const hasRunnersForProject = (state) =>
state?.job?.runners?.available && !state?.job?.runners?.online;
diff --git a/app/assets/javascripts/jobs/store/mutation_types.js b/app/assets/javascripts/jobs/store/mutation_types.js
index 6c4f1b5a19182306031b95d727aa0742551282c0..4915a826b84d30873ad0a2d3c906f6b5b0c6ab8e 100644
--- a/app/assets/javascripts/jobs/store/mutation_types.js
+++ b/app/assets/javascripts/jobs/store/mutation_types.js
@@ -1,5 +1,5 @@
export const SET_JOB_ENDPOINT = 'SET_JOB_ENDPOINT';
-export const SET_TRACE_OPTIONS = 'SET_TRACE_OPTIONS';
+export const SET_JOB_LOG_OPTIONS = 'SET_JOB_LOG_OPTIONS';
export const HIDE_SIDEBAR = 'HIDE_SIDEBAR';
export const SHOW_SIDEBAR = 'SHOW_SIDEBAR';
@@ -12,17 +12,17 @@ export const ENABLE_SCROLL_BOTTOM = 'ENABLE_SCROLL_BOTTOM';
export const ENABLE_SCROLL_TOP = 'ENABLE_SCROLL_TOP';
export const TOGGLE_SCROLL_ANIMATION = 'TOGGLE_SCROLL_ANIMATION';
-export const TOGGLE_IS_SCROLL_IN_BOTTOM_BEFORE_UPDATING_TRACE = 'TOGGLE_IS_SCROLL_IN_BOTTOM';
+export const TOGGLE_IS_SCROLL_IN_BOTTOM_BEFORE_UPDATING_JOB_LOG = 'TOGGLE_IS_SCROLL_IN_BOTTOM';
export const REQUEST_JOB = 'REQUEST_JOB';
export const RECEIVE_JOB_SUCCESS = 'RECEIVE_JOB_SUCCESS';
export const RECEIVE_JOB_ERROR = 'RECEIVE_JOB_ERROR';
-export const REQUEST_TRACE = 'REQUEST_TRACE';
-export const SET_TRACE_TIMEOUT = 'SET_TRACE_TIMEOUT';
-export const STOP_POLLING_TRACE = 'STOP_POLLING_TRACE';
-export const RECEIVE_TRACE_SUCCESS = 'RECEIVE_TRACE_SUCCESS';
-export const RECEIVE_TRACE_ERROR = 'RECEIVE_TRACE_ERROR';
+export const REQUEST_JOB_LOG = 'REQUEST_JOB_LOG';
+export const SET_JOB_LOG_TIMEOUT = 'SET_JOB_LOG_TIMEOUT';
+export const STOP_POLLING_JOB_LOG = 'STOP_POLLING_JOB_LOG';
+export const RECEIVE_JOB_LOG_SUCCESS = 'RECEIVE_JOB_LOG_SUCCESS';
+export const RECEIVE_JOB_LOG_ERROR = 'RECEIVE_JOB_LOG_ERROR';
export const TOGGLE_COLLAPSIBLE_LINE = 'TOGGLE_COLLAPSIBLE_LINE';
export const SET_SELECTED_STAGE = 'SET_SELECTED_STAGE';
diff --git a/app/assets/javascripts/jobs/store/mutations.js b/app/assets/javascripts/jobs/store/mutations.js
index 4045d8a0c16fbcca528eb73426bc76829f62bf42..eda2ee0349a14d3fe0e74ee210d94365f7245389 100644
--- a/app/assets/javascripts/jobs/store/mutations.js
+++ b/app/assets/javascripts/jobs/store/mutations.js
@@ -1,16 +1,16 @@
import Vue from 'vue';
import { INFINITELY_NESTED_COLLAPSIBLE_SECTIONS_FF } from '../constants';
import * as types from './mutation_types';
-import { logLinesParser, logLinesParserLegacy, updateIncrementalTrace } from './utils';
+import { logLinesParser, logLinesParserLegacy, updateIncrementalJobLog } from './utils';
export default {
[types.SET_JOB_ENDPOINT](state, endpoint) {
state.jobEndpoint = endpoint;
},
- [types.SET_TRACE_OPTIONS](state, options = {}) {
- state.traceEndpoint = options.pagePath;
- state.traceState = options.logState;
+ [types.SET_JOB_LOG_OPTIONS](state, options = {}) {
+ state.jobLogEndpoint = options.pagePath;
+ state.jobLogState = options.logState;
},
[types.HIDE_SIDEBAR](state) {
@@ -20,11 +20,11 @@ export default {
state.isSidebarOpen = true;
},
- [types.RECEIVE_TRACE_SUCCESS](state, log = {}) {
+ [types.RECEIVE_JOB_LOG_SUCCESS](state, log = {}) {
const infinitelyCollapsibleSectionsFlag =
gon.features?.[INFINITELY_NESTED_COLLAPSIBLE_SECTIONS_FF];
if (log.state) {
- state.traceState = log.state;
+ state.jobLogState = log.state;
}
if (log.append) {
@@ -32,52 +32,52 @@ export default {
if (log.lines) {
const parsedResult = logLinesParser(
log.lines,
- state.auxiliaryPartialTraceHelpers,
- state.trace,
+ state.auxiliaryPartialJobLogHelpers,
+ state.jobLog,
);
- state.trace = parsedResult.parsedLines;
- state.auxiliaryPartialTraceHelpers = parsedResult.auxiliaryPartialTraceHelpers;
+ state.jobLog = parsedResult.parsedLines;
+ state.auxiliaryPartialJobLogHelpers = parsedResult.auxiliaryPartialJobLogHelpers;
}
} else {
- state.trace = log.lines ? updateIncrementalTrace(log.lines, state.trace) : state.trace;
+ state.jobLog = log.lines ? updateIncrementalJobLog(log.lines, state.jobLog) : state.jobLog;
}
- state.traceSize += log.size;
+ state.jobLogSize += log.size;
} else {
- // When the job still does not have a trace
- // the trace response will not have a defined
+ // When the job still does not have a log
+ // the job log response will not have a defined
// html or size. We keep the old value otherwise these
// will be set to `null`
if (infinitelyCollapsibleSectionsFlag) {
const parsedResult = logLinesParser(log.lines);
- state.trace = parsedResult.parsedLines;
- state.auxiliaryPartialTraceHelpers = parsedResult.auxiliaryPartialTraceHelpers;
+ state.jobLog = parsedResult.parsedLines;
+ state.auxiliaryPartialJobLogHelpers = parsedResult.auxiliaryPartialJobLogHelpers;
} else {
- state.trace = log.lines ? logLinesParserLegacy(log.lines) : state.trace;
+ state.jobLog = log.lines ? logLinesParserLegacy(log.lines) : state.jobLog;
}
- state.traceSize = log.size || state.traceSize;
+ state.jobLogSize = log.size || state.jobLogSize;
}
- if (state.traceSize < log.total) {
- state.isTraceSizeVisible = true;
+ if (state.jobLogSize < log.total) {
+ state.isJobLogSizeVisible = true;
} else {
- state.isTraceSizeVisible = false;
+ state.isJobLogSizeVisible = false;
}
- state.isTraceComplete = log.complete || state.isTraceComplete;
+ state.isJobLogComplete = log.complete || state.isJobLogComplete;
},
- [types.SET_TRACE_TIMEOUT](state, id) {
- state.traceTimeout = id;
+ [types.SET_JOB_LOG_TIMEOUT](state, id) {
+ state.jobLogTimeout = id;
},
/**
* Will remove loading animation
*/
- [types.STOP_POLLING_TRACE](state) {
- state.isTraceComplete = true;
+ [types.STOP_POLLING_JOB_LOG](state) {
+ state.isJobLogComplete = true;
},
/**
@@ -137,8 +137,8 @@ export default {
state.isScrollingDown = toggle;
},
- [types.TOGGLE_IS_SCROLL_IN_BOTTOM_BEFORE_UPDATING_TRACE](state, toggle) {
- state.isScrolledToBottomBeforeReceivingTrace = toggle;
+ [types.TOGGLE_IS_SCROLL_IN_BOTTOM_BEFORE_UPDATING_JOB_LOG](state, toggle) {
+ state.isScrolledToBottomBeforeReceivingJobLog = toggle;
},
[types.REQUEST_JOBS_FOR_STAGE](state, stage = {}) {
diff --git a/app/assets/javascripts/jobs/store/state.js b/app/assets/javascripts/jobs/store/state.js
index 718324c8bad9517ad701ea220c58b1daac774df7..a1ba64aa71ea8d57d216df344a68e35622001f46 100644
--- a/app/assets/javascripts/jobs/store/state.js
+++ b/app/assets/javascripts/jobs/store/state.js
@@ -1,6 +1,6 @@
export default () => ({
jobEndpoint: null,
- traceEndpoint: null,
+ jobLogEndpoint: null,
// sidebar
isSidebarOpen: true,
@@ -14,16 +14,16 @@ export default () => ({
isScrollTopDisabled: true,
// Used to check if we should keep the automatic scroll
- isScrolledToBottomBeforeReceivingTrace: true,
+ isScrolledToBottomBeforeReceivingJobLog: true,
- trace: [],
- isTraceComplete: false,
- traceSize: 0,
- isTraceSizeVisible: false,
- traceTimeout: 0,
+ jobLog: [],
+ isJobLogComplete: false,
+ jobLogSize: 0,
+ isJobLogSizeVisible: false,
+ jobLogTimeout: 0,
- // used as a query parameter to fetch the trace
- traceState: null,
+ // used as a query parameter to fetch the job log
+ jobLogState: null,
// sidebar dropdown & list of jobs
isLoadingJobs: false,
@@ -32,5 +32,5 @@ export default () => ({
jobs: [],
// to parse partial logs
- auxiliaryPartialTraceHelpers: {},
+ auxiliaryPartialJobLogHelpers: {},
});
diff --git a/app/assets/javascripts/jobs/store/utils.js b/app/assets/javascripts/jobs/store/utils.js
index b64734e29f666aaa215e54950f82cc50ba0a73a0..8bca448ee110895bea5710f47394edfe2b5ca643 100644
--- a/app/assets/javascripts/jobs/store/utils.js
+++ b/app/assets/javascripts/jobs/store/utils.js
@@ -131,17 +131,17 @@ export const logLinesParserLegacy = (lines = [], accumulator = []) =>
[...accumulator],
);
-export const logLinesParser = (lines = [], previousTraceState = {}, prevParsedLines = []) => {
- let currentLineCount = previousTraceState?.prevLineCount ?? 0;
- let currentHeader = previousTraceState?.currentHeader;
- let isPreviousLineHeader = previousTraceState?.isPreviousLineHeader ?? false;
+export const logLinesParser = (lines = [], previousJobLogState = {}, prevParsedLines = []) => {
+ let currentLineCount = previousJobLogState?.prevLineCount ?? 0;
+ let currentHeader = previousJobLogState?.currentHeader;
+ let isPreviousLineHeader = previousJobLogState?.isPreviousLineHeader ?? false;
const parsedLines = prevParsedLines.length > 0 ? prevParsedLines : [];
- const sectionsQueue = previousTraceState?.sectionsQueue ?? [];
+ const sectionsQueue = previousJobLogState?.sectionsQueue ?? [];
for (let i = 0; i < lines.length; i += 1) {
const line = lines[i];
// First run we can use the current index, later runs we have to retrieve the last number of lines
- currentLineCount = previousTraceState?.prevLineCount ? currentLineCount + 1 : i + 1;
+ currentLineCount = previousJobLogState?.prevLineCount ? currentLineCount + 1 : i + 1;
if (line.section_header && !isPreviousLineHeader) {
// If there's no previous line header that means we're at the root of the log
@@ -198,7 +198,7 @@ export const logLinesParser = (lines = [], previousTraceState = {}, prevParsedLi
return {
parsedLines,
- auxiliaryPartialTraceHelpers: {
+ auxiliaryPartialJobLogHelpers: {
isPreviousLineHeader,
currentHeader,
sectionsQueue,
@@ -241,7 +241,7 @@ export const findOffsetAndRemove = (newLog = [], oldParsed = []) => {
};
/**
- * When the trace is not complete, backend may send the last received line
+ * When the job log is not complete, backend may send the last received line
* in the new response.
*
* We need to check if that is the case by looking for the offset property
@@ -250,7 +250,7 @@ export const findOffsetAndRemove = (newLog = [], oldParsed = []) => {
* @param array oldLog
* @param array newLog
*/
-export const updateIncrementalTrace = (newLog = [], oldParsed = []) => {
+export const updateIncrementalJobLog = (newLog = [], oldParsed = []) => {
const parsedLog = findOffsetAndRemove(newLog, oldParsed);
return logLinesParserLegacy(newLog, parsedLog);
diff --git a/app/assets/javascripts/jobs/utils.js b/app/assets/javascripts/jobs/utils.js
index bb27658369ff18e2a47ce4566dd05c61546fc682..a4e695518f15269d269edb6e2d6bbfb5ef0e9ed9 100644
--- a/app/assets/javascripts/jobs/utils.js
+++ b/app/assets/javascripts/jobs/utils.js
@@ -19,3 +19,12 @@ export const reportToSentry = (component, failureType) => {
Sentry.captureException(failureType);
});
};
+
+export const reportMessageToSentry = (component, message, context) => {
+ Sentry.withScope((scope) => {
+ // eslint-disable-next-line @gitlab/require-i18n-strings
+ scope.setContext('Vue data', context);
+ scope.setTag('component', component);
+ Sentry.captureMessage(message);
+ });
+};
diff --git a/app/assets/javascripts/lib/apollo/suppress_network_errors_during_navigation_link.js b/app/assets/javascripts/lib/apollo/suppress_network_errors_during_navigation_link.js
new file mode 100644
index 0000000000000000000000000000000000000000..ad92bd4de42490a51d3026f82c89c65a83654c34
--- /dev/null
+++ b/app/assets/javascripts/lib/apollo/suppress_network_errors_during_navigation_link.js
@@ -0,0 +1,36 @@
+import { Observable } from 'apollo-link';
+import { onError } from 'apollo-link-error';
+import { isNavigatingAway } from '~/lib/utils/is_navigating_away';
+
+/**
+ * Returns an ApolloLink (or null if not enabled) which supresses network
+ * errors when the browser is navigating away.
+ *
+ * @returns {ApolloLink|null}
+ */
+export const getSuppressNetworkErrorsDuringNavigationLink = () => {
+ if (!gon.features?.suppressApolloErrorsDuringNavigation) {
+ return null;
+ }
+
+ return onError(({ networkError }) => {
+ if (networkError && isNavigatingAway()) {
+ // Return an observable that will never notify any subscribers with any
+ // values, errors, or completions. This ensures that requests aborted due
+ // to navigating away do not trigger any failure behaviour.
+ //
+ // See '../utils/suppress_ajax_errors_during_navigation.js' for an axios
+ // interceptor that performs a similar role.
+ return new Observable(() => {});
+ }
+
+ // We aren't suppressing anything here, so simply do nothing.
+ // The onError helper will forward all values/errors/completions from the
+ // underlying request observable to the next link if you return a falsey
+ // value.
+ //
+ // Note that this return statement is technically redundant, but is kept
+ // for explicitness.
+ return undefined;
+ });
+};
diff --git a/app/assets/javascripts/lib/graphql.js b/app/assets/javascripts/lib/graphql.js
index b96a55fe1166b879dc684cfe434af76b510ed88c..39bf804b54e78e42afcdc20808fd7a56508341c2 100644
--- a/app/assets/javascripts/lib/graphql.js
+++ b/app/assets/javascripts/lib/graphql.js
@@ -11,6 +11,7 @@ import csrf from '~/lib/utils/csrf';
import { objectToQuery, queryToObject } from '~/lib/utils/url_utility';
import PerformanceBarService from '~/performance_bar/services/performance_bar_service';
import { getInstrumentationLink } from './apollo/instrumentation_link';
+import { getSuppressNetworkErrorsDuringNavigationLink } from './apollo/suppress_network_errors_during_navigation_link';
export const fetchPolicies = {
CACHE_FIRST: 'cache-first',
@@ -143,6 +144,7 @@ export default (resolvers = {}, config = {}) => {
new ActionCableLink(),
ApolloLink.from(
[
+ getSuppressNetworkErrorsDuringNavigationLink(),
getInstrumentationLink(),
requestCounterLink,
performanceBarLink,
diff --git a/app/assets/javascripts/lib/logger/hello.js b/app/assets/javascripts/lib/logger/hello.js
new file mode 100644
index 0000000000000000000000000000000000000000..ccfdfe91e60f23c76899af7c04f66485d10a68af
--- /dev/null
+++ b/app/assets/javascripts/lib/logger/hello.js
@@ -0,0 +1,37 @@
+import { s__, sprintf } from '~/locale';
+
+const HANDSHAKE = String.fromCodePoint(0x1f91d);
+const MAG = String.fromCodePoint(0x1f50e);
+const ROCKET = String.fromCodePoint(0x1f680);
+
+export const logHello = () => {
+ // eslint-disable-next-line no-console
+ console.log(
+ `%c${s__('HelloMessage|Welcome to GitLab!')}%c
+
+${s__(
+ 'HelloMessage|Does this page need fixes or improvements? Open an issue or contribute a merge request to help make GitLab more lovable. At GitLab, everyone can contribute!',
+)}
+
+${sprintf(s__('HelloMessage|%{handshake_emoji} Contribute to GitLab: %{contribute_link}'), {
+ handshake_emoji: `${HANDSHAKE}`,
+ contribute_link: 'https://about.gitlab.com/community/contribute/',
+})}
+${sprintf(s__('HelloMessage|%{magnifier_emoji} Create a new GitLab issue: %{new_issue_link}'), {
+ magnifier_emoji: `${MAG}`,
+ new_issue_link: 'https://gitlab.com/gitlab-org/gitlab/-/issues/new',
+})}
+${
+ window.gon?.dot_com
+ ? `${sprintf(
+ s__(
+ 'HelloMessage|%{rocket_emoji} We like your curiosity! Help us improve GitLab by joining the team: %{jobs_page_link}',
+ ),
+ { rocket_emoji: `${ROCKET}`, jobs_page_link: 'https://about.gitlab.com/jobs/' },
+ )}`
+ : ''
+}`,
+ `padding-top: 0.5em; font-size: 2em;`,
+ 'padding-bottom: 0.5em;',
+ );
+};
diff --git a/app/assets/javascripts/lib/logger/hello_deferred.js b/app/assets/javascripts/lib/logger/hello_deferred.js
new file mode 100644
index 0000000000000000000000000000000000000000..ce1dd91cb376161a6c4d4ce931aa8b9620ac0cba
--- /dev/null
+++ b/app/assets/javascripts/lib/logger/hello_deferred.js
@@ -0,0 +1,5 @@
+export const logHelloDeferred = async () => {
+ const { logHello } = await import(/* webpackChunkName: 'hello' */ './hello');
+
+ logHello();
+};
diff --git a/app/assets/javascripts/lib/utils/accessor.js b/app/assets/javascripts/lib/utils/accessor.js
index 39cffedcac6d278f1c821d276e5de79829e61df5..d4a6d70c62c149fcad054db04a1c7e3c3d0cee64 100644
--- a/app/assets/javascripts/lib/utils/accessor.js
+++ b/app/assets/javascripts/lib/utils/accessor.js
@@ -1,4 +1,4 @@
-function isPropertyAccessSafe(base, property) {
+function canAccessProperty(base, property) {
let safe;
try {
@@ -10,7 +10,7 @@ function isPropertyAccessSafe(base, property) {
return safe;
}
-function isFunctionCallSafe(base, functionName, ...args) {
+function canCallFunction(base, functionName, ...args) {
let safe = true;
try {
@@ -22,16 +22,28 @@ function isFunctionCallSafe(base, functionName, ...args) {
return safe;
}
-function isLocalStorageAccessSafe() {
+/**
+ * Determines if `window.localStorage` is available and
+ * can be written to and read from.
+ *
+ * Important: This is not a guarantee that
+ * `localStorage.setItem` will work in all cases.
+ *
+ * `setItem` can still throw exceptions and should be
+ * surrounded with a try/catch where used.
+ *
+ * See: https://developer.mozilla.org/en-US/docs/Web/API/Storage/setItem#exceptions
+ */
+function canUseLocalStorage() {
let safe;
- const TEST_KEY = 'isLocalStorageAccessSafe';
+ const TEST_KEY = 'canUseLocalStorage';
const TEST_VALUE = 'true';
- safe = isPropertyAccessSafe(window, 'localStorage');
+ safe = canAccessProperty(window, 'localStorage');
if (!safe) return safe;
- safe = isFunctionCallSafe(window.localStorage, 'setItem', TEST_KEY, TEST_VALUE);
+ safe = canCallFunction(window.localStorage, 'setItem', TEST_KEY, TEST_VALUE);
if (safe) window.localStorage.removeItem(TEST_KEY);
@@ -39,9 +51,7 @@ function isLocalStorageAccessSafe() {
}
const AccessorUtilities = {
- isPropertyAccessSafe,
- isFunctionCallSafe,
- isLocalStorageAccessSafe,
+ canUseLocalStorage,
};
export default AccessorUtilities;
diff --git a/app/assets/javascripts/lib/utils/axios_utils.js b/app/assets/javascripts/lib/utils/axios_utils.js
index 0a26f78e2538adecaa0ba534abbe0200a581e147..de6d85b8a189f4282cf0102f505491a038051526 100644
--- a/app/assets/javascripts/lib/utils/axios_utils.js
+++ b/app/assets/javascripts/lib/utils/axios_utils.js
@@ -2,6 +2,7 @@ import axios from 'axios';
import { registerCaptchaModalInterceptor } from '~/captcha/captcha_modal_axios_interceptor';
import setupAxiosStartupCalls from './axios_startup_calls';
import csrf from './csrf';
+import { isNavigatingAway } from './is_navigating_away';
import suppressAjaxErrorsDuringNavigation from './suppress_ajax_errors_during_navigation';
axios.defaults.headers.common[csrf.headerKey] = csrf.token;
@@ -30,16 +31,11 @@ axios.interceptors.response.use(
},
);
-let isUserNavigating = false;
-window.addEventListener('beforeunload', () => {
- isUserNavigating = true;
-});
-
// Ignore AJAX errors caused by requests
// being cancelled due to browser navigation
axios.interceptors.response.use(
(response) => response,
- (err) => suppressAjaxErrorsDuringNavigation(err, isUserNavigating),
+ (err) => suppressAjaxErrorsDuringNavigation(err, isNavigatingAway()),
);
registerCaptchaModalInterceptor(axios);
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index db6834c40848cb92afeaa3fb9f7b22a5c966eff5..813fd3dbb1ecd0fe20fa2832bec814328872feb2 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -6,6 +6,7 @@ import { GlBreakpointInstance as breakpointInstance } from '@gitlab/ui/dist/util
import $ from 'jquery';
import Cookies from 'js-cookie';
import { isFunction, defer } from 'lodash';
+import { SCOPED_LABEL_DELIMITER } from '~/vue_shared/components/sidebar/labels_select_widget/constants';
import { convertToCamelCase, convertToSnakeCase } from './text_utility';
import { isObject } from './type_utility';
import { getLocationHash } from './url_utility';
@@ -117,7 +118,6 @@ export const handleLocationHash = () => {
};
// Check if element scrolled into viewport from above or below
-// Courtesy http://stackoverflow.com/a/7557433/414749
export const isInViewport = (el, offset = {}) => {
const rect = el.getBoundingClientRect();
const { top, left } = offset;
@@ -560,8 +560,6 @@ export const addSelectOnFocusBehaviour = (selector = '.js-select-on-focus') => {
* Method to round of values with decimal places
* with provided precision.
*
- * Taken from https://stackoverflow.com/a/7343013/414749
- *
* Eg; roundOffFloat(3.141592, 3) = 3.142
*
* Refer to spec/frontend/lib/utils/common_utils_spec.js for
@@ -688,7 +686,7 @@ export const searchBy = (query = '', searchSpace = {}) => {
* @param {Object} label
* @returns Boolean
*/
-export const isScopedLabel = ({ title = '' } = {}) => title.indexOf('::') !== -1;
+export const isScopedLabel = ({ title = '' } = {}) => title.includes(SCOPED_LABEL_DELIMITER);
/**
* Returns the base value of the scoped label
@@ -699,7 +697,8 @@ export const isScopedLabel = ({ title = '' } = {}) => title.indexOf('::') !== -1
* @param {Object} label
* @returns String
*/
-export const scopedLabelKey = ({ title = '' }) => isScopedLabel({ title }) && title.split('::')[0];
+export const scopedLabelKey = ({ title = '' }) =>
+ isScopedLabel({ title }) && title.split(SCOPED_LABEL_DELIMITER)[0];
// Methods to set and get Cookie
export const setCookie = (name, value) => Cookies.set(name, value, { expires: 365 });
diff --git a/app/assets/javascripts/lib/utils/constants.js b/app/assets/javascripts/lib/utils/constants.js
index e41de72ded4cc9a704b99f5ec3941901cd2fd5c9..0e5a23a5cbbcb3bf4b1aaa211a943a89c8377a26 100644
--- a/app/assets/javascripts/lib/utils/constants.js
+++ b/app/assets/javascripts/lib/utils/constants.js
@@ -20,3 +20,7 @@ export const BV_DROPDOWN_HIDE = 'bv::dropdown::hide';
export const DEFAULT_TH_CLASSES =
'gl-bg-transparent! gl-border-b-solid! gl-border-b-gray-100! gl-p-5! gl-border-b-1!';
+
+// We set the drawer's z-index to 252 to clear flash messages that might
+// be displayed in the page and that have a z-index of 251.
+export const DRAWER_Z_INDEX = 252;
diff --git a/app/assets/javascripts/lib/utils/datetime/date_format_utility.js b/app/assets/javascripts/lib/utils/datetime/date_format_utility.js
index 0a35efb0ac8ef13ac8827f4e581efec471b10afe..3c446c2186599f2f59321f9c8e80f65d6a49d9e6 100644
--- a/app/assets/javascripts/lib/utils/datetime/date_format_utility.js
+++ b/app/assets/javascripts/lib/utils/datetime/date_format_utility.js
@@ -1,6 +1,8 @@
import dateFormat from 'dateformat';
-import { isString, mapValues, reduce, isDate } from 'lodash';
-import { s__, n__, __ } from '../../../locale';
+import { isString, mapValues, reduce, isDate, unescape } from 'lodash';
+import { roundToNearestHalf } from '~/lib/utils/common_utils';
+import { sanitize } from '~/lib/dompurify';
+import { s__, n__, __, sprintf } from '../../../locale';
/**
* Returns i18n month names array.
@@ -361,3 +363,26 @@ export const dateToTimeInputValue = (date) => {
hour12: false,
});
};
+
+export const formatTimeAsSummary = ({ seconds, hours, days, minutes, weeks, months }) => {
+ if (months) {
+ return sprintf(s__('ValueStreamAnalytics|%{value}M'), {
+ value: roundToNearestHalf(months),
+ });
+ } else if (weeks) {
+ return sprintf(s__('ValueStreamAnalytics|%{value}w'), {
+ value: roundToNearestHalf(weeks),
+ });
+ } else if (days) {
+ return sprintf(s__('ValueStreamAnalytics|%{value}d'), {
+ value: roundToNearestHalf(days),
+ });
+ } else if (hours) {
+ return sprintf(s__('ValueStreamAnalytics|%{value}h'), { value: hours });
+ } else if (minutes) {
+ return sprintf(s__('ValueStreamAnalytics|%{value}m'), { value: minutes });
+ } else if (seconds) {
+ return unescape(sanitize(s__('ValueStreamAnalytics|<1m'), { ALLOWED_TAGS: [] }));
+ }
+ return '-';
+};
diff --git a/app/assets/javascripts/lib/utils/datetime_range.js b/app/assets/javascripts/lib/utils/datetime_range.js
index a2b161d1446ff084706fb3b271290b0fd77df9f3..840cc4600fe1e3d89d10d9c97c8f3264be822b3c 100644
--- a/app/assets/javascripts/lib/utils/datetime_range.js
+++ b/app/assets/javascripts/lib/utils/datetime_range.js
@@ -26,7 +26,17 @@ const isValidDateString = (dateString) => {
return false;
}
- return !Number.isNaN(Date.parse(dateformat(dateString, 'isoUtcDateTime')));
+ let isoFormatted;
+ try {
+ isoFormatted = dateformat(dateString, 'isoUtcDateTime');
+ } catch (e) {
+ if (e instanceof TypeError) {
+ // not a valid date string
+ return false;
+ }
+ throw e;
+ }
+ return !Number.isNaN(Date.parse(isoFormatted));
};
const handleRangeDirection = ({ direction = DEFAULT_DIRECTION, anchorDate, minDate, maxDate }) => {
diff --git a/app/assets/javascripts/lib/utils/is_navigating_away.js b/app/assets/javascripts/lib/utils/is_navigating_away.js
new file mode 100644
index 0000000000000000000000000000000000000000..7df00b4537994007a76f00e62f750dca303d1de6
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/is_navigating_away.js
@@ -0,0 +1,23 @@
+let navigating = false;
+
+window.addEventListener('beforeunload', () => {
+ navigating = true;
+});
+
+/**
+ * To only be used for testing purposes. Allows the navigating state to be set
+ * to a given value.
+ *
+ * @param {boolean} value The value to set the navigating flag to.
+ */
+export const setNavigatingForTestsOnly = (value) => {
+ navigating = value;
+};
+
+/**
+ * Returns a boolean indicating whether the browser is in the process of
+ * navigating away from the current page.
+ *
+ * @returns {boolean}
+ */
+export const isNavigatingAway = () => navigating;
diff --git a/app/assets/javascripts/lib/utils/regexp.js b/app/assets/javascripts/lib/utils/regexp.js
index 25b60dcd14aee32b97ebb6e9af04fc5197dad354..f212bf80bd79f5b2c51ee6627f534623ef63f29f 100644
--- a/app/assets/javascripts/lib/utils/regexp.js
+++ b/app/assets/javascripts/lib/utils/regexp.js
@@ -1,6 +1,5 @@
/**
* Regexp utility for the convenience of working with regular expressions.
- *
*/
// Inspired by https://github.com/mishoo/UglifyJS/blob/2bc1d02363db3798d5df41fb5059a19edca9b7eb/lib/parse-js.js#L203
@@ -8,4 +7,9 @@
const unicodeLetters =
'\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u0527\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0620-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u08A0\\u08A2-\\u08AC\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0977\\u0979-\\u097F\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C33\\u0C35-\\u0C39\\u0C3D\\u0C58\\u0C59\\u0C60\\u0C61\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\u0CF1\\u0CF2\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D60\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32\\u0E33\\u0E40-\\u0E46\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB0\\u0EB2\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F4\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16EE-\\u16F0\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191C\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19C1-\\u19C7\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4B\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1CE9-\\u1CEC\\u1CEE-\\u1CF1\\u1CF5\\u1CF6\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2160-\\u2188\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005-\\u3007\\u3021-\\u3029\\u3031-\\u3035\\u3038-\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31BA\\u31F0-\\u31FF\\u3400-\\u4DB5\\u4E00-\\u9FCC\\uA000-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA66E\\uA67F-\\uA697\\uA6A0-\\uA6EF\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA78E\\uA790-\\uA793\\uA7A0-\\uA7AA\\uA7F8-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA80-\\uAAAF\\uAAB1\\uAAB5\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uABC0-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC';
-export default { unicodeLetters };
+/**
+ * A regex that matches all single quotes in a string
+ */
+export const allSingleQuotes = /'/g;
+
+export default { unicodeLetters, allSingleQuotes };
diff --git a/app/assets/javascripts/lib/utils/text_utility.js b/app/assets/javascripts/lib/utils/text_utility.js
index 5ee00464a8bd9f20f6d0f428183480ade4d4f971..419afa0a0a91a6b6b8e67d6b60810bb838cf2c50 100644
--- a/app/assets/javascripts/lib/utils/text_utility.js
+++ b/app/assets/javascripts/lib/utils/text_utility.js
@@ -4,6 +4,7 @@ import {
TRUNCATE_WIDTH_DEFAULT_WIDTH,
TRUNCATE_WIDTH_DEFAULT_FONT_SIZE,
} from '~/lib/utils/constants';
+import { allSingleQuotes } from '~/lib/utils/regexp';
/**
* Adds a , to a string composed by numbers, at every 3 chars.
@@ -479,3 +480,17 @@ export const markdownConfig = {
ALLOWED_ATTR: ['class', 'style', 'href', 'src'],
ALLOW_DATA_ATTR: false,
};
+
+/**
+ * Escapes a string into a shell string, for example
+ * when you want to give a user the command to checkout
+ * a branch.
+ *
+ * It replaces all single-quotes with an escaped "'\''"
+ * that is interpreted by shell as a single-quote. It also
+ * encapsulates the string in single-quotes.
+ *
+ * If the branch is `fix-'bug-behavior'`, that should be
+ * escaped to `'fix-'\''bug-behavior'\'''`.
+ */
+export const escapeShellString = (str) => `'${str.replace(allSingleQuotes, () => "'\\''")}'`;
diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js
index bca0e45d98dc9ce83f4ecd3a2404e2c5c92e2a10..1c22d21a313bd1b9eca9eb0e938affb96a980efb 100644
--- a/app/assets/javascripts/lib/utils/url_utility.js
+++ b/app/assets/javascripts/lib/utils/url_utility.js
@@ -1,3 +1,5 @@
+export const DASH_SCOPE = '-';
+
const PATH_SEPARATOR = '/';
const PATH_SEPARATOR_LEADING_REGEX = new RegExp(`^${PATH_SEPARATOR}+`);
const PATH_SEPARATOR_ENDING_REGEX = new RegExp(`${PATH_SEPARATOR}+$`);
@@ -588,3 +590,30 @@ export function isSameOriginUrl(url) {
return false;
}
}
+
+/**
+ * Returns a URL to WebIDE considering the current user's position in
+ * repository's tree. If not MR `iid` has been passed, the URL is fetched
+ * from the global `gl.webIDEPath`.
+ *
+ * @param sourceProjectFullPath Source project's full path. Used in MRs
+ * @param targetProjectFullPath Target project's full path. Used in MRs
+ * @param iid MR iid
+ * @returns {string}
+ */
+
+export function constructWebIDEPath({
+ sourceProjectFullPath,
+ targetProjectFullPath = '',
+ iid,
+} = {}) {
+ if (!iid || !sourceProjectFullPath) {
+ return window.gl?.webIDEPath;
+ }
+ return mergeUrlParams(
+ {
+ target_project: sourceProjectFullPath !== targetProjectFullPath ? targetProjectFullPath : '',
+ },
+ webIDEUrl(`/${sourceProjectFullPath}/merge_requests/${iid}`),
+ );
+}
diff --git a/app/assets/javascripts/logs/components/environment_logs.vue b/app/assets/javascripts/logs/components/environment_logs.vue
index 3db9fa01629340f3f06a98e53645a6efd0d9a88f..2a60825a42707e7eece312b41e5df4d4cd4982b8 100644
--- a/app/assets/javascripts/logs/components/environment_logs.vue
+++ b/app/assets/javascripts/logs/components/environment_logs.vue
@@ -214,7 +214,7 @@ export default {
diff --git a/app/assets/javascripts/logs/stores/state.js b/app/assets/javascripts/logs/stores/state.js
index 83080589362b5e582a50908a624e0fdbc2a0d9bb..ee17e8ecef24bd5414cea882756157b1004ad7c3 100644
--- a/app/assets/javascripts/logs/stores/state.js
+++ b/app/assets/javascripts/logs/stores/state.js
@@ -31,7 +31,7 @@ export default () => ({
},
/**
- * Logs including trace
+ * Jobs with logs
*/
logs: {
lines: [],
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index 15c483485f1b1a038209aad0f1e82af53edbc1c0..e422d9b1a32cf8839d52d87e3325ac3140bcc662 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -1,5 +1,4 @@
/* global $ */
-/* eslint-disable import/order */
import jQuery from 'jquery';
import Cookies from 'js-cookie';
@@ -15,10 +14,12 @@ import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
import { initRails } from '~/lib/utils/rails_ujs';
import * as popovers from '~/popovers';
import * as tooltips from '~/tooltips';
+import { initHeaderSearchApp } from '~/header_search';
import initAlertHandler from './alert_handler';
import { removeFlashClickListener } from './flash';
import initTodoToggle from './header';
import initLayoutNav from './layout_nav';
+import { logHelloDeferred } from './lib/logger/hello_deferred';
import { handleLocationHash, addSelectOnFocusBehaviour } from './lib/utils/common_utils';
import { localTimeAgo } from './lib/utils/datetime/timeago_utility';
import { getLocationHash, visitUrl } from './lib/utils/url_utility';
@@ -35,11 +36,12 @@ import GlFieldErrors from './gl_field_errors';
import initUserPopovers from './user_popovers';
import initBroadcastNotifications from './broadcast_notification';
import { initTopNav } from './nav';
-import { initHeaderSearchApp } from '~/header_search';
import 'ee_else_ce/main_ee';
import 'jh_else_ce/main_jh';
+logHelloDeferred();
+
applyGitLabUIConfig();
// expose jQuery as global (TODO: remove these)
diff --git a/app/assets/javascripts/members/components/action_buttons/remove_member_button.vue b/app/assets/javascripts/members/components/action_buttons/remove_member_button.vue
index 665e8ee69f759de824494c61befff7957d9c79a9..69137ce615baf0804b85c666fec53b47a9b72699 100644
--- a/app/assets/javascripts/members/components/action_buttons/remove_member_button.vue
+++ b/app/assets/javascripts/members/components/action_buttons/remove_member_button.vue
@@ -42,7 +42,7 @@ export default {
required: false,
default: false,
},
- oncallSchedules: {
+ userDeletionObstacles: {
type: Object,
required: false,
default: () => ({}),
@@ -61,7 +61,7 @@ export default {
memberPath: this.memberPath.replace(':id', this.memberId),
memberType: this.memberType,
message: this.message,
- oncallSchedules: this.oncallSchedules,
+ userDeletionObstacles: this.userDeletionObstacles,
};
},
},
diff --git a/app/assets/javascripts/members/components/action_buttons/user_action_buttons.vue b/app/assets/javascripts/members/components/action_buttons/user_action_buttons.vue
index 0c20f935d506f89055624b42e5b74b158c80a891..44d658c90a0c9cbfaff8b10d7b89585e77cf72e3 100644
--- a/app/assets/javascripts/members/components/action_buttons/user_action_buttons.vue
+++ b/app/assets/javascripts/members/components/action_buttons/user_action_buttons.vue
@@ -1,5 +1,6 @@
-
-
- {{ s__('Members|No expiration set') }}
-
- {{ s__('Members|Expired') }}
-
-
- {{ inWords }}
-
-
-
-
diff --git a/app/assets/javascripts/members/components/table/members_table.vue b/app/assets/javascripts/members/components/table/members_table.vue
index debc3fc31f64ac87bdf900cf239ba73fcdcbb7f7..202f3aa89e14db412f904d1405b15c7b73ae1e78 100644
--- a/app/assets/javascripts/members/components/table/members_table.vue
+++ b/app/assets/javascripts/members/components/table/members_table.vue
@@ -5,12 +5,17 @@ import MembersTableCell from 'ee_else_ce/members/components/table/members_table_
import { canOverride, canRemove, canResend, canUpdate } from 'ee_else_ce/members/utils';
import { mergeUrlParams } from '~/lib/utils/url_utility';
import initUserPopovers from '~/user_popovers';
-import { FIELDS, ACTIVE_TAB_QUERY_PARAM_NAME } from '../../constants';
+import {
+ FIELDS,
+ ACTIVE_TAB_QUERY_PARAM_NAME,
+ MEMBER_STATE_AWAITING,
+ USER_STATE_BLOCKED_PENDING_APPROVAL,
+ BADGE_LABELS_PENDING_OWNER_APPROVAL,
+} from '../../constants';
import RemoveGroupLinkModal from '../modals/remove_group_link_modal.vue';
import RemoveMemberModal from '../modals/remove_member_modal.vue';
import CreatedAt from './created_at.vue';
import ExpirationDatepicker from './expiration_datepicker.vue';
-import ExpiresAt from './expires_at.vue';
import MemberActionButtons from './member_action_buttons.vue';
import MemberAvatar from './member_avatar.vue';
import MemberSource from './member_source.vue';
@@ -24,7 +29,6 @@ export default {
GlPagination,
MemberAvatar,
CreatedAt,
- ExpiresAt,
MembersTableCell,
MemberSource,
MemberActionButtons,
@@ -131,6 +135,74 @@ export default {
window.location.href,
);
},
+ /**
+ * Returns whether it's a new or existing user
+ *
+ * If memberInviteMetadata doesn't exist, it means we're adding an existing user
+ * to the Group/Project, so `isNewUser` should be false.
+ * If memberInviteMetadata exists but `userState` has content,
+ * the user has registered but is awaiting root approval
+ *
+ * @param {object} memberInviteMetadata - MemberEntity.invite
+ * @see {@link ~/app/serializers/member_entity.rb}
+ * @returns {boolean}
+ */
+ isNewUser(memberInviteMetadata) {
+ return memberInviteMetadata && !memberInviteMetadata.userState;
+ },
+ /**
+ * Returns whether the user is awaiting root approval
+ *
+ * This checks User.state exposed via MemberEntity
+ *
+ * @param {object} memberInviteMetadata - MemberEntity.invite
+ * @see {@link ~/app/serializers/member_entity.rb}
+ * @returns {boolean}
+ */
+ isUserPendingRootApproval(memberInviteMetadata) {
+ return memberInviteMetadata?.userState === USER_STATE_BLOCKED_PENDING_APPROVAL;
+ },
+ /**
+ * Returns whether the member is awaiting owner approval
+ *
+ * This checks Member.state exposed via MemberEntity
+ *
+ * @param {Number} memberState - Member.state exposed via MemberEntity.state
+ * @see {@link ~/ee/app/models/ee/member.rb}
+ * @see {@link ~/app/serializers/member_entity.rb}
+ * @returns {boolean}
+ */
+ isMemberPendingOwnerApproval(memberState) {
+ return memberState === MEMBER_STATE_AWAITING;
+ },
+ isUserAwaiting(memberInviteMetadata, memberState) {
+ return (
+ this.isUserPendingRootApproval(memberInviteMetadata) ||
+ this.isMemberPendingOwnerApproval(memberState)
+ );
+ },
+ shouldAddPendingOwnerApprovalBadge(memberInviteMetadata, memberState) {
+ return (
+ this.isUserAwaiting(memberInviteMetadata, memberState) &&
+ !this.isNewUser(memberInviteMetadata)
+ );
+ },
+ /**
+ * Returns the string to be used in the invite badge
+ *
+ * @param {object} memberInviteMetadata - MemberEntity.invite
+ * @see {@link ~/app/serializers/member_entity.rb}
+ * @param {Number} memberState - Member.state exposed via MemberEntity.state
+ * @see {@link ~/ee/app/models/ee/member.rb}
+ * @returns {string}
+ */
+ inviteBadge(memberInviteMetadata, memberState) {
+ if (this.shouldAddPendingOwnerApprovalBadge(memberInviteMetadata, memberState)) {
+ return BADGE_LABELS_PENDING_OWNER_APPROVAL;
+ }
+
+ return '';
+ },
},
};
@@ -174,18 +246,17 @@ export default {
-
+
+ {{
+ inviteBadge(invite, state)
+ }}
-
-
-
-
diff --git a/app/assets/javascripts/members/constants.js b/app/assets/javascripts/members/constants.js
index 6f465245d20790f606fa3309782670f1909f3ea8..f5ca881ab0dd19c5d6da281ddc13bfa5b9cc521d 100644
--- a/app/assets/javascripts/members/constants.js
+++ b/app/assets/javascripts/members/constants.js
@@ -37,12 +37,6 @@ export const FIELDS = [
thClass: 'col-meta',
tdClass: 'col-meta',
},
- {
- key: 'expires',
- label: __('Access expires'),
- thClass: 'col-meta',
- tdClass: 'col-meta',
- },
{
key: 'maxRole',
label: __('Max role'),
@@ -95,6 +89,22 @@ export const TAB_QUERY_PARAM_VALUES = {
accessRequest: 'access_requests',
};
+/**
+ * This user state value comes from the User model
+ * see the state machine in app/models/user.rb
+ */
+export const USER_STATE_BLOCKED_PENDING_APPROVAL = 'blocked_pending_approval';
+
+/**
+ * This and following member state constants' values
+ * come from ee/app/models/ee/member.rb
+ */
+export const MEMBER_STATE_CREATED = 0;
+export const MEMBER_STATE_AWAITING = 1;
+export const MEMBER_STATE_ACTIVE = 2;
+
+export const BADGE_LABELS_PENDING_OWNER_APPROVAL = __('Pending owner approval');
+
export const DAYS_TO_EXPIRE_SOON = 7;
export const LEAVE_MODAL_ID = 'member-leave-modal';
diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js
index e89b886df835941b4ad9be297b0eab3d44b5a35a..244cf1e150a88339a31279f1cf021cd34f1f47e4 100644
--- a/app/assets/javascripts/merge_request.js
+++ b/app/assets/javascripts/merge_request.js
@@ -2,6 +2,7 @@
import $ from 'jquery';
import createFlash from '~/flash';
+import toast from '~/vue_shared/plugins/global_toast';
import { __ } from '~/locale';
import eventHub from '~/vue_merge_request_widget/event_hub';
import axios from './lib/utils/axios_utils';
@@ -136,10 +137,9 @@ MergeRequest.hideCloseButton = function () {
MergeRequest.toggleDraftStatus = function (title, isReady) {
if (isReady) {
- createFlash({
- message: __('The merge request can now be merged.'),
- type: 'notice',
- });
+ toast(__('Marked as ready. Merging is now allowed.'));
+ } else {
+ toast(__('Marked as draft. Can only be merged when marked as ready.'));
}
const titleEl = document.querySelector('.merge-request .detail-page-description .title');
diff --git a/app/assets/javascripts/milestones/components/milestone_combobox.vue b/app/assets/javascripts/milestones/components/milestone_combobox.vue
index e84990152100d9f340113bc2f325bdc2c3b71e45..a840e696386ebaafb080591a101cf5c0b02a8f38 100644
--- a/app/assets/javascripts/milestones/components/milestone_combobox.vue
+++ b/app/assets/javascripts/milestones/components/milestone_combobox.vue
@@ -125,8 +125,7 @@ export default {
// This method is defined here instead of in `methods`
// because we need to access the .cancel() method
// lodash attaches to the function, which is
- // made inaccessible by Vue. More info:
- // https://stackoverflow.com/a/52988020/1063392
+ // made inaccessible by Vue.
this.debouncedSearch = debounce(function search() {
this.search(this.searchQuery);
}, SEARCH_DEBOUNCE_MS);
diff --git a/app/assets/javascripts/mr_popover/index.js b/app/assets/javascripts/mr_popover/index.js
index 714cf67e0bd5549bca1183254359e9ee0e156f23..6e46c5d3c1f85c4de90f332f2e6f1e72670b5efc 100644
--- a/app/assets/javascripts/mr_popover/index.js
+++ b/app/assets/javascripts/mr_popover/index.js
@@ -48,7 +48,12 @@ export default (elements) => {
Vue.use(VueApollo);
const apolloProvider = new VueApollo({
- defaultClient: createDefaultClient(),
+ defaultClient: createDefaultClient(
+ {},
+ {
+ assumeImmutableResults: true,
+ },
+ ),
});
const listenerAddedAttr = 'data-mr-listener-added';
diff --git a/app/assets/javascripts/namespace_select.js b/app/assets/javascripts/namespace_select.js
deleted file mode 100644
index af7a600d1ad212e4c808a865e187da791f68f9df..0000000000000000000000000000000000000000
--- a/app/assets/javascripts/namespace_select.js
+++ /dev/null
@@ -1,58 +0,0 @@
-import $ from 'jquery';
-import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
-import { parseBoolean } from '~/lib/utils/common_utils';
-import Api from './api';
-import { mergeUrlParams } from './lib/utils/url_utility';
-import { __ } from './locale';
-
-export default class NamespaceSelect {
- constructor(opts) {
- const isFilter = parseBoolean(opts.dropdown.dataset.isFilter);
- const fieldName = opts.dropdown.dataset.fieldName || 'namespace_id';
-
- initDeprecatedJQueryDropdown($(opts.dropdown), {
- filterable: true,
- selectable: true,
- filterRemote: true,
- search: {
- fields: ['path'],
- },
- fieldName,
- toggleLabel(selected) {
- if (selected.id == null) {
- return selected.text;
- }
- return `${selected.kind}: ${selected.full_path}`;
- },
- data(term, dataCallback) {
- return Api.namespaces(term, (namespaces) => {
- if (isFilter) {
- const anyNamespace = {
- text: __('Any namespace'),
- id: null,
- };
- namespaces.unshift(anyNamespace);
- namespaces.splice(1, 0, { type: 'divider' });
- }
- return dataCallback(namespaces);
- });
- },
- text(namespace) {
- if (namespace.id == null) {
- return namespace.text;
- }
- return `${namespace.kind}: ${namespace.full_path}`;
- },
- renderRow: this.renderRow,
- clicked(options) {
- if (!isFilter) {
- const { e } = options;
- e.preventDefault();
- }
- },
- url(namespace) {
- return mergeUrlParams({ [fieldName]: namespace.id }, window.location.href);
- },
- });
- }
-}
diff --git a/app/assets/javascripts/notebook/cells/markdown.vue b/app/assets/javascripts/notebook/cells/markdown.vue
index 1384c9c40b311f5e3790c14168d8a416f5b238ac..073b27605bb898b832eef8f3902ee4c712bf248d 100644
--- a/app/assets/javascripts/notebook/cells/markdown.vue
+++ b/app/assets/javascripts/notebook/cells/markdown.vue
@@ -1,6 +1,7 @@
-
+
diff --git a/app/assets/javascripts/notes/components/comment_type_dropdown.vue b/app/assets/javascripts/notes/components/comment_type_dropdown.vue
index 9c4bab2099cd773ba92f8146d0f4dae12ab4e44b..30ea5d3532edaf8bd78701ea8a259844055a5d1f 100644
--- a/app/assets/javascripts/notes/components/comment_type_dropdown.vue
+++ b/app/assets/javascripts/notes/components/comment_type_dropdown.vue
@@ -93,10 +93,14 @@ export default {
data-testid="comment-button"
data-qa-selector="comment_button"
:data-track-label="trackingLabel"
- data-track-event="click_button"
+ data-track-action="click_button"
@click="$emit('click')"
>
-
+
{{ $options.i18n.submitButton.comment }}
{{ commentDescription }}
@@ -105,7 +109,7 @@ export default {
is-check-item
:is-checked="isNoteTypeDiscussion"
data-qa-selector="discussion_menu_item"
- @click="setNoteTypeToDiscussion"
+ @click.stop.prevent="setNoteTypeToDiscussion"
>
{{ $options.i18n.submitButton.startThread }}
{{ startDiscussionDescription }}
diff --git a/app/assets/javascripts/notes/components/discussion_counter.vue b/app/assets/javascripts/notes/components/discussion_counter.vue
index 55cf75132a99e3c6cfafa6bcfa5c933435f9d8eb..831e6dd8f92ba04812e9142fc0228abab9a68e86 100644
--- a/app/assets/javascripts/notes/components/discussion_counter.vue
+++ b/app/assets/javascripts/notes/components/discussion_counter.vue
@@ -78,8 +78,8 @@ export default {
v-if="resolveAllDiscussionsIssuePath && !allResolved"
v-gl-tooltip
:href="resolveAllDiscussionsIssuePath"
- :title="s__('Resolve all threads in new issue')"
- :aria-label="s__('Resolve all threads in new issue')"
+ :title="s__('Create issue to resolve all threads')"
+ :aria-label="s__('Create issue to resolve all threads')"
class="new-issue-for-discussion discussion-create-issue-btn"
icon="issue-new"
/>
@@ -89,7 +89,7 @@ export default {
:title="__('Jump to next unresolved thread')"
:aria-label="__('Jump to next unresolved thread')"
class="discussion-next-btn"
- data-track-event="click_button"
+ data-track-action="click_button"
data-track-label="mr_next_unresolved_thread"
data-track-property="click_next_unresolved_thread_top"
icon="comment-next"
diff --git a/app/assets/javascripts/notes/components/discussion_notes.vue b/app/assets/javascripts/notes/components/discussion_notes.vue
index 0892276ff3b06af00d93eec0d040b54915bd586b..6fcfa66ea49413acd2c39197e14387b9c06f6d3c 100644
--- a/app/assets/javascripts/notes/components/discussion_notes.vue
+++ b/app/assets/javascripts/notes/components/discussion_notes.vue
@@ -47,6 +47,11 @@ export default {
required: false,
default: '',
},
+ isOverviewTab: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
computed: {
...mapGetters(['userCanReply']),
@@ -127,6 +132,7 @@ export default {
:show-reply-button="userCanReply"
:discussion-root="true"
:discussion-resolve-path="discussion.resolve_path"
+ :is-overview-tab="isOverviewTab"
@handleDeleteNote="$emit('deleteNote')"
@startReplying="$emit('startReplying')"
>
@@ -176,6 +182,7 @@ export default {
:line="diffLine"
:discussion-root="index === 0"
:discussion-resolve-path="discussion.resolve_path"
+ :is-overview-tab="isOverviewTab"
@handleDeleteNote="$emit('deleteNote')"
>
diff --git a/app/assets/javascripts/notes/components/discussion_resolve_with_issue_button.vue b/app/assets/javascripts/notes/components/discussion_resolve_with_issue_button.vue
index 9119d319d7299e02bc8cac6e6691712524f4fd3c..4ccba011014bec0ebff0a46264c9e1fcaaa128c9 100644
--- a/app/assets/javascripts/notes/components/discussion_resolve_with_issue_button.vue
+++ b/app/assets/javascripts/notes/components/discussion_resolve_with_issue_button.vue
@@ -4,7 +4,7 @@ import { s__ } from '~/locale';
export default {
i18n: {
- buttonLabel: s__('MergeRequests|Resolve this thread in a new issue'),
+ buttonLabel: s__('MergeRequests|Create issue to resolve thread'),
},
name: 'ResolveWithIssueButton',
components: {
diff --git a/app/assets/javascripts/notes/components/note_actions.vue b/app/assets/javascripts/notes/components/note_actions.vue
index 44d0c741d5a2f5fb610442f418fe817879cfe7c0..e2a2edd73445100d0b000cb45780a79d109114ab 100644
--- a/app/assets/javascripts/notes/components/note_actions.vue
+++ b/app/assets/javascripts/notes/components/note_actions.vue
@@ -257,7 +257,7 @@ export default {
{{ __('Author') }}
@@ -265,7 +265,7 @@ export default {
{{ accessLevel }}
@@ -273,7 +273,7 @@ export default {
{{ __('Contributor') }}
diff --git a/app/assets/javascripts/notes/components/note_actions/reply_button.vue b/app/assets/javascripts/notes/components/note_actions/reply_button.vue
index 0cd2afcf8a0d8047b1bd66efd4ccb849cc5c59db..8c8cc7984b14023308ad298b55f8ba7d99e22060 100644
--- a/app/assets/javascripts/notes/components/note_actions/reply_button.vue
+++ b/app/assets/javascripts/notes/components/note_actions/reply_button.vue
@@ -19,7 +19,7 @@ export default {
-import { GlIcon, GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui';
+import {
+ GlIcon,
+ GlLoadingIcon,
+ GlTooltipDirective,
+ GlSafeHtmlDirective as SafeHtml,
+} from '@gitlab/ui';
import { mapActions } from 'vuex';
import timeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import UserNameWithStatus from '../../sidebar/components/assignees/user_name_with_status.vue';
export default {
+ safeHtmlConfig: { ADD_TAGS: ['gl-emoji'] },
components: {
timeAgoTooltip,
GitlabTeamMemberBadge: () =>
@@ -14,6 +20,7 @@ export default {
UserNameWithStatus,
},
directives: {
+ SafeHtml,
GlTooltip: GlTooltipDirective,
},
props: {
@@ -165,10 +172,10 @@ export default {