Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-09-15 06:12:51 +00:00
parent 6eb95ccbe8
commit 9a0e0265e4
21 changed files with 368 additions and 226 deletions

View File

@ -4,7 +4,6 @@ variables:
RELEASE: "${REGISTRY_HOST}/${REGISTRY_GROUP}/build/omnibus-gitlab-mirror/gitlab-ee:${CI_COMMIT_SHA}" RELEASE: "${REGISTRY_HOST}/${REGISTRY_GROUP}/build/omnibus-gitlab-mirror/gitlab-ee:${CI_COMMIT_SHA}"
SKIP_REPORT_IN_ISSUES: "true" SKIP_REPORT_IN_ISSUES: "true"
OMNIBUS_GITLAB_CACHE_UPDATE: "false" OMNIBUS_GITLAB_CACHE_UPDATE: "false"
COLORIZED_LOGS: "true"
QA_LOG_LEVEL: "info" QA_LOG_LEVEL: "info"
QA_TESTS: "" QA_TESTS: ""
QA_FEATURE_FLAGS: "" QA_FEATURE_FLAGS: ""

View File

@ -11,6 +11,7 @@ stages:
include: include:
- local: .gitlab/ci/global.gitlab-ci.yml - local: .gitlab/ci/global.gitlab-ci.yml
- local: .gitlab/ci/rules.gitlab-ci.yml - local: .gitlab/ci/rules.gitlab-ci.yml
- local: .gitlab/ci/review-apps/rules.gitlab-ci.yml
- local: .gitlab/ci/review-apps/qa.gitlab-ci.yml - local: .gitlab/ci/review-apps/qa.gitlab-ci.yml
- local: .gitlab/ci/review-apps/dast.gitlab-ci.yml - local: .gitlab/ci/review-apps/dast.gitlab-ci.yml
@ -97,6 +98,7 @@ review-deploy:
- export GITALY_VERSION=$(<GITALY_SERVER_VERSION) - export GITALY_VERSION=$(<GITALY_SERVER_VERSION)
- export GITLAB_WORKHORSE_VERSION=$(<GITLAB_WORKHORSE_VERSION) - export GITLAB_WORKHORSE_VERSION=$(<GITLAB_WORKHORSE_VERSION)
- echo "${CI_ENVIRONMENT_URL}" > environment_url.txt - echo "${CI_ENVIRONMENT_URL}" > environment_url.txt
- echo "QA_GITLAB_URL=${CI_ENVIRONMENT_URL}" > environment.env
- *base-before_script - *base-before_script
script: script:
- check_kube_domain - check_kube_domain
@ -112,6 +114,8 @@ review-deploy:
artifacts: artifacts:
paths: paths:
- environment_url.txt - environment_url.txt
reports:
dotenv: environment.env
expire_in: 7 days expire_in: 7 days
when: always when: always

View File

@ -1,6 +1,6 @@
include: include:
- project: gitlab-org/quality/pipeline-common - project: gitlab-org/quality/pipeline-common
ref: 0.13.0 ref: 1.2.2
file: file:
- /ci/allure-report.yml - /ci/allure-report.yml
- /ci/knapsack-report.yml - /ci/knapsack-report.yml
@ -8,7 +8,7 @@ include:
.test-variables: .test-variables:
variables: variables:
QA_GENERATE_ALLURE_REPORT: "true" QA_GENERATE_ALLURE_REPORT: "true"
COLORIZED_LOGS: "true" QA_CAN_TEST_PRAEFECT: "false"
GITLAB_USERNAME: "root" GITLAB_USERNAME: "root"
GITLAB_PASSWORD: "${REVIEW_APPS_ROOT_PASSWORD}" GITLAB_PASSWORD: "${REVIEW_APPS_ROOT_PASSWORD}"
GITLAB_ADMIN_USERNAME: "root" GITLAB_ADMIN_USERNAME: "root"
@ -21,49 +21,31 @@ include:
- .qa-cache - .qa-cache
image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/debian-bullseye-ruby-${RUBY_VERSION}:bundler-2.3 image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/debian-bullseye-ruby-${RUBY_VERSION}:bundler-2.3
before_script: before_script:
- export QA_GITLAB_URL="$(cat environment_url.txt)"
- cd qa && bundle install - cd qa && bundle install
.review-qa-base: .review-qa-base:
image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/debian-bullseye-ruby-${RUBY_VERSION}:bundler-2.3-git-2.33-lfs-2.9-chrome-${CHROME_VERSION}-docker-${DOCKER_VERSION}-gcloud-383-kubectl-1.23
extends: extends:
- .use-docker-in-docker - .use-docker-in-docker
- .bundle-base - .bundle-base
- .test-variables - .test-variables
image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/debian-bullseye-ruby-${RUBY_VERSION}:bundler-2.3-git-2.33-lfs-2.9-chrome-${CHROME_VERSION}-docker-${DOCKER_VERSION}-gcloud-383-kubectl-1.23
stage: qa stage: qa
needs: needs:
- review-deploy - review-deploy
- download-knapsack-report - download-knapsack-report
variables: variables:
DOCKER_HOST: tcp://docker:2376
DOCKER_TLS_CERTDIR: /certs
DOCKER_CERT_PATH: /certs/client
DOCKER_TLS_VERIFY: 1
GIT_LFS_SKIP_SMUDGE: 1 GIT_LFS_SKIP_SMUDGE: 1
WD_INSTALL_DIR: /usr/local/bin WD_INSTALL_DIR: /usr/local/bin
before_script: RSPEC_REPORT_OPTS: --force-color --order random --format documentation --format RspecJunitFormatter --out tmp/rspec.xml
- !reference [.bundle-base, before_script]
script: script:
- export EE_LICENSE="$(cat $REVIEW_APPS_EE_LICENSE_FILE)" - export EE_LICENSE="$(cat $REVIEW_APPS_EE_LICENSE_FILE)"
- qa_run_status=0 - QA_COMMAND="bundle exec bin/qa ${QA_SCENARIO} ${QA_GITLAB_URL} -- ${QA_TESTS} ${RSPEC_REPORT_OPTS}"
- echo "Running - '${QA_COMMAND}'"
- eval "$QA_COMMAND"
after_script:
- | - |
bundle exec rake "knapsack:rspec[\ echo "Sentry errors for the current review-app test run can be found via following url:"
${RSPEC_TAGS} \ echo "https://sentry.gitlab.net/gitlab/gitlab-review-apps/releases/$(echo "${CI_COMMIT_SHA}" | cut -c1-11)/all-events/."
--tag ~orchestrated \
--tag ~transient \
--tag ~skip_signup_disabled \
--tag ~requires_git_protocol_v2 \
--tag ~requires_praefect \
--force-color \
--order random \
--format documentation \
--format RspecJunitFormatter --out tmp/rspec.xml \
]" || qa_run_status=$?
- if [ ${qa_run_status} -ne 0 ]; then
release_sha=$(echo "${CI_COMMIT_SHA}" | cut -c1-11);
echo "Errors can be found at https://sentry.gitlab.net/gitlab/gitlab-review-apps/releases/${release_sha}/all-events/.";
fi
- exit ${qa_run_status}
artifacts: artifacts:
paths: paths:
- qa/tmp - qa/tmp
@ -72,20 +54,11 @@ include:
expire_in: 7 days expire_in: 7 days
when: always when: always
.allure-report-base:
extends: .generate-allure-report-base
stage: post-qa
variables:
GITLAB_AUTH_TOKEN: $GITLAB_QA_MR_ALLURE_REPORT_TOKEN
ALLURE_PROJECT_PATH: $CI_PROJECT_PATH
ALLURE_MERGE_REQUEST_IID: $CI_MERGE_REQUEST_IID
ALLURE_RESULTS_GLOB: qa/tmp/allure-results/*
# Store knapsack report as artifact so the same report is reused across all jobs # Store knapsack report as artifact so the same report is reused across all jobs
download-knapsack-report: download-knapsack-report:
extends: extends:
- .bundle-base - .bundle-base
- .review:rules:review-qa-reliable - .rules:qa-framework-changes-or-review-scenarios
stage: prepare stage: prepare
script: script:
- bundle exec rake "knapsack:download[qa]" - bundle exec rake "knapsack:download[qa]"
@ -98,30 +71,39 @@ download-knapsack-report:
review-qa-smoke: review-qa-smoke:
extends: extends:
- .review-qa-base - .review-qa-base
- .review:rules:review-qa-smoke - .rules:qa-smoke
retry: 1
variables: variables:
QA_SCENARIO: Test::Instance::Smoke
QA_RUN_TYPE: review-qa-smoke QA_RUN_TYPE: review-qa-smoke
RSPEC_TAGS: --tag smoke
review-qa-reliable:
extends:
- .review-qa-base
- .review:rules:review-qa-reliable
retry: 1 retry: 1
parallel: 10
variables:
QA_RUN_TYPE: review-qa-reliable
RSPEC_TAGS: --tag reliable --tag sanity_feature_flags
review-qa-all: review-qa-blocking:
extends: extends:
- .review-qa-base - .review-qa-base
- .review:rules:review-qa-all - .rules:qa-blocking
parallel: 5
variables: variables:
QA_RUN_TYPE: review-qa-all QA_SCENARIO: Test::Instance::ReviewBlocking
RSPEC_TAGS: --tag ~reliable --tag ~smoke --tag ~sanity_feature_flags QA_RUN_TYPE: review-qa-blocking
retry: 1
review-qa-blocking-parallel:
extends:
- review-qa-blocking
- .rules:qa-blocking-parallel
parallel: 10
review-qa-non-blocking:
extends:
- .review-qa-base
- .rules:qa-non-blocking
variables:
QA_SCENARIO: Test::Instance::ReviewNonBlocking
QA_RUN_TYPE: review-qa-non-blocking
allow_failure: true
review-qa-non-blocking-parallel:
extends:
- review-qa-non-blocking
- .rules:qa-non-blocking-parallel
parallel: 5
review-performance: review-performance:
extends: extends:
@ -149,27 +131,20 @@ review-performance:
performance: performance.json performance: performance.json
expire_in: 31d expire_in: 31d
# Generate single report for both smoke and reliable test jobs e2e-test-report:
# Both job types are essentially the same:
# * always executed
# * always blocking
allure-report-qa-blocking:
extends: extends:
- .allure-report-base - .generate-allure-report-base
- .review:rules:review-qa-blocking-report - .rules:qa-framework-changes-or-review-scenarios
needs: stage: post-qa
- review-qa-smoke
- review-qa-reliable
variables: variables:
ALLURE_JOB_NAME: e2e-review-qa-blocking ALLURE_JOB_NAME: e2e-review-qa
ALLURE_PROJECT_PATH: $CI_PROJECT_PATH
allure-report-qa-all: ALLURE_RESULTS_GLOB: qa/tmp/allure-results/*
extends: ALLURE_MERGE_REQUEST_IID: $CI_MERGE_REQUEST_IID
- .allure-report-base GITLAB_AUTH_TOKEN: $GITLAB_QA_MR_ALLURE_REPORT_TOKEN
- .review:rules:review-qa-all-report GIT_STRATEGY: none
needs: ["review-qa-all"] allow_failure: true
variables: when: always
ALLURE_JOB_NAME: e2e-review-qa-all
upload-knapsack-report: upload-knapsack-report:
extends: extends:
@ -182,13 +157,13 @@ upload-knapsack-report:
delete-test-resources: delete-test-resources:
extends: extends:
- .bundle-base - .bundle-base
- .review:rules:review-qa-cleanup - .rules:qa-framework-changes-or-review-scenarios
stage: post-qa stage: post-qa
variables: variables:
QA_TEST_RESOURCES_FILE_PATTERN: $CI_PROJECT_DIR/qa/tmp/test-resources-*.json QA_TEST_RESOURCES_FILE_PATTERN: $CI_PROJECT_DIR/qa/tmp/test-resources-*.json
GITLAB_QA_ACCESS_TOKEN: $REVIEW_APPS_ROOT_TOKEN GITLAB_QA_ACCESS_TOKEN: $REVIEW_APPS_ROOT_TOKEN
COLORIZED_LOGS: "true"
script: script:
- export GITLAB_ADDRESS="$QA_GITLAB_URL" - export GITLAB_ADDRESS="$QA_GITLAB_URL"
- bundle exec rake "test_resources:delete[$QA_TEST_RESOURCES_FILE_PATTERN]" - bundle exec rake "test_resources:delete[$QA_TEST_RESOURCES_FILE_PATTERN]"
allow_failure: true allow_failure: true
when: always

View File

@ -0,0 +1,80 @@
# Specific specs passed
.specific-specs: &specific-specs
if: $QA_TESTS != ""
# No specific specs passed
.all-specs: &all-specs
if: $QA_TESTS == ""
# No specific specs in mr pipeline
.all-specs-mr: &all-specs-mr
if: $CI_MERGE_REQUEST_IID && $QA_TESTS == ""
when: manual
# Triggered by change pattern
.app-changes: &app-changes
if: $APP_CHANGE_TRIGGER == "true"
# QA framework changes present
.qa-framework-changes: &qa-framework-changes
if: $QA_FRAMEWORK_CHANGES == "true"
.never-when-qa-framework-changes-or-no-specific-specs:
- <<: *qa-framework-changes
when: never
- <<: *all-specs
when: never
.never-when-specific-specs-always-when-qa-framework-changes:
- <<: *specific-specs
when: never
- *qa-framework-changes
# ------------------------------------------
# Test
# ------------------------------------------
.rules:qa-smoke:
rules:
# always trigger smoke suite if review pipeline got triggered by specific changes in application code
- <<: *app-changes
variables:
QA_TESTS: "" # unset QA_TESTS even if specific tests were inferred from stage label
- *qa-framework-changes
- if: $QA_SUITES =~ /Test::Instance::Smoke/
.rules:qa-blocking:
rules:
- <<: *app-changes
when: never
- !reference [.never-when-qa-framework-changes-or-no-specific-specs]
- if: $QA_SUITES =~ /Test::Instance::ReviewBlocking/
.rules:qa-blocking-parallel:
rules:
# always trigger blocking suite if review pipeline got triggered by specific changes in application code
- <<: *app-changes
variables:
QA_TESTS: "" # unset QA_TESTS even if specific tests were inferred from stage label
- !reference [.never-when-specific-specs-always-when-qa-framework-changes]
- if: $QA_SUITES =~ /Test::Instance::ReviewBlocking/
.rules:qa-non-blocking:
rules:
- !reference [.never-when-qa-framework-changes-or-no-specific-specs]
- if: $QA_SUITES =~ /Test::Instance::ReviewNonBlocking/
.rules:qa-non-blocking-parallel:
rules:
- !reference [.never-when-specific-specs-always-when-qa-framework-changes]
- *all-specs-mr # set full suite to manual when no specific specs passed in mr
- if: $QA_SUITES =~ /Test::Instance::ReviewNonBlocking/
# ------------------------------------------
# Prepare/Report
# ------------------------------------------
# if no framework changes or QA_SUITES do not contain review scenarios, pipeline will not have e2e test jobs
# so we need to skip knapsack, allure and test resource deletion jobs as well
.rules:qa-framework-changes-or-review-scenarios:
rules:
- *qa-framework-changes
- if: $QA_SUITES =~ /Test::Instance::Smoke/
- if: $QA_SUITES =~ /Test::Instance::ReviewBlocking/
- if: $QA_SUITES =~ /Test::Instance::ReviewNonBlocking/

View File

@ -1667,6 +1667,9 @@
################ ################
# Review rules # # Review rules #
################ ################
.review-change-pattern: &review-change-pattern
APP_CHANGE_TRIGGER: "true"
.review:rules:start-review-app-pipeline: .review:rules:start-review-app-pipeline:
rules: rules:
- <<: *if-not-ee - <<: *if-not-ee
@ -1678,12 +1681,16 @@
changes: *ci-review-patterns changes: *ci-review-patterns
- <<: *if-dot-com-gitlab-org-merge-request - <<: *if-dot-com-gitlab-org-merge-request
changes: *frontend-build-patterns changes: *frontend-build-patterns
variables: *review-change-pattern
- <<: *if-dot-com-gitlab-org-merge-request - <<: *if-dot-com-gitlab-org-merge-request
changes: *controllers-patterns changes: *controllers-patterns
variables: *review-change-pattern
- <<: *if-dot-com-gitlab-org-merge-request - <<: *if-dot-com-gitlab-org-merge-request
changes: *models-patterns changes: *models-patterns
variables: *review-change-pattern
- <<: *if-dot-com-gitlab-org-merge-request - <<: *if-dot-com-gitlab-org-merge-request
changes: *lib-gitlab-patterns changes: *lib-gitlab-patterns
variables: *review-change-pattern
- <<: *if-dot-com-gitlab-org-merge-request - <<: *if-dot-com-gitlab-org-merge-request
changes: *qa-patterns changes: *qa-patterns
- <<: *if-dot-com-gitlab-org-merge-request - <<: *if-dot-com-gitlab-org-merge-request
@ -1717,49 +1724,6 @@
rules: rules:
- when: on_success - when: on_success
.review:rules:review-qa-smoke:
rules:
- when: on_success
# If the needed job isn't allowed to fail, we need to use `when: always` in
# order to keep the job always running after it.
#
# If the needed job is allowed to fail, we need to use both
# `when: on_success` and `when: on_failure` in order to keep
# the job always running after it.
# Not that if the needed job has `when: on_success` we can use `when: always`
# for the depending job.
#
# See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/76756
.review:rules:review-qa-reliable:
rules:
- when: on_success
# Since `review-qa-reliable` isn't allowed to fail, we need to use `when: always`for `review-qa-reliable-report`.
.review:rules:review-qa-blocking-report:
rules:
- when: always
.review:rules:review-qa-all:
rules:
- <<: *if-dot-com-gitlab-org-merge-request
changes: *code-patterns
when: manual
allow_failure: true # manual jobs needs to be allowed to fail, otherwise they block the pipeline
- when: on_success
allow_failure: true
# Since `review-qa-all` is allowed to fail (and potentially manual), we need to use `when: on_success` and `when: on_failure` for `review-qa-all-report`.
.review:rules:review-qa-all-report:
rules:
- when: on_success
- when: on_failure
.review:rules:review-qa-cleanup:
rules:
- when: always
.review:rules:review-cleanup: .review:rules:review-cleanup:
rules: rules:
- <<: *if-not-ee - <<: *if-not-ee

View File

@ -46,6 +46,7 @@ const populateUserInfo = (user) => {
pronouns: userData.pronouns, pronouns: userData.pronouns,
localTime: userData.local_time, localTime: userData.local_time,
isFollowed: userData.is_followed, isFollowed: userData.is_followed,
state: userData.state,
loaded: true, loaded: true,
}); });
} }

View File

@ -1 +1,14 @@
import { __ } from '~/locale';
export const USER_POPOVER_DELAY = 200; export const USER_POPOVER_DELAY = 200;
export const I18N_ERROR_FOLLOW = __(
'An error occurred while trying to follow this user, please try again.',
);
export const I18N_ERROR_UNFOLLOW = __(
'An error occurred while trying to unfollow this user, please try again.',
);
export const I18N_USER_BLOCKED = __('User is blocked');
export const I18N_USER_BUSY = __('Busy');
export const I18N_USER_LEARN = __('Learn more about %{name}');
export const I18N_USER_FOLLOW = __('Follow');
export const I18N_USER_UNFOLLOW = __('Unfollow');

View File

@ -9,23 +9,31 @@ import {
GlButton, GlButton,
GlAvatarLabeled, GlAvatarLabeled,
} from '@gitlab/ui'; } from '@gitlab/ui';
import { __ } from '~/locale';
import { glEmojiTag } from '~/emoji'; import { glEmojiTag } from '~/emoji';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { followUser, unfollowUser } from '~/rest_api'; import { followUser, unfollowUser } from '~/rest_api';
import { isUserBusy } from '~/set_status_modal/utils'; import { isUserBusy } from '~/set_status_modal/utils';
import Tracking from '~/tracking'; import Tracking from '~/tracking';
import { USER_POPOVER_DELAY } from './constants'; import {
I18N_ERROR_FOLLOW,
I18N_ERROR_UNFOLLOW,
I18N_USER_BLOCKED,
I18N_USER_BUSY,
I18N_USER_LEARN,
I18N_USER_FOLLOW,
I18N_USER_UNFOLLOW,
USER_POPOVER_DELAY,
} from './constants';
const MAX_SKELETON_LINES = 4; const MAX_SKELETON_LINES = 4;
export default { export default {
name: 'UserPopover', name: 'UserPopover',
maxSkeletonLines: MAX_SKELETON_LINES, maxSkeletonLines: MAX_SKELETON_LINES,
I18N_USER_BLOCKED,
I18N_USER_BUSY,
I18N_USER_LEARN,
USER_POPOVER_DELAY, USER_POPOVER_DELAY,
i18n: {
busy: __('Busy'),
},
components: { components: {
GlIcon, GlIcon,
GlLink, GlLink,
@ -94,7 +102,7 @@ export default {
toggleFollowButtonText() { toggleFollowButtonText() {
if (this.toggleFollowLoading) return null; if (this.toggleFollowLoading) return null;
return this.user?.isFollowed ? __('Unfollow') : __('Follow'); return this.user?.isFollowed ? I18N_USER_UNFOLLOW : I18N_USER_FOLLOW;
}, },
toggleFollowButtonVariant() { toggleFollowButtonVariant() {
return this.user?.isFollowed ? 'default' : 'confirm'; return this.user?.isFollowed ? 'default' : 'confirm';
@ -102,6 +110,9 @@ export default {
hasPronouns() { hasPronouns() {
return Boolean(this.user?.pronouns?.trim()); return Boolean(this.user?.pronouns?.trim());
}, },
isBlocked() {
return this.user?.state === 'blocked';
},
isBusy() { isBusy() {
return isUserBusy(this.availabilityStatus); return isUserBusy(this.availabilityStatus);
}, },
@ -129,7 +140,7 @@ export default {
this.$emit('follow'); this.$emit('follow');
} catch (error) { } catch (error) {
createFlash({ createFlash({
message: __('An error occurred while trying to follow this user, please try again.'), message: I18N_ERROR_FOLLOW,
error, error,
captureError: true, captureError: true,
}); });
@ -149,7 +160,7 @@ export default {
this.$emit('unfollow'); this.$emit('unfollow');
} catch (error) { } catch (error) {
createFlash({ createFlash({
message: __('An error occurred while trying to unfollow this user, please try again.'), message: I18N_ERROR_UNFOLLOW,
error, error,
captureError: true, captureError: true,
}); });
@ -189,16 +200,21 @@ export default {
:label="user.name" :label="user.name"
:sub-label="username" :sub-label="username"
> >
<gl-button <template v-if="isBlocked">
v-if="shouldRenderToggleFollowButton" <span class="gl-mt-4 gl-font-style-italic">{{ $options.I18N_USER_BLOCKED }}</span>
class="gl-mt-3 gl-align-self-start" </template>
:variant="toggleFollowButtonVariant" <template v-else>
:loading="toggleFollowLoading" <gl-button
size="small" v-if="shouldRenderToggleFollowButton"
data-testid="toggle-follow-button" class="gl-mt-3 gl-align-self-start"
@click="toggleFollow" :variant="toggleFollowButtonVariant"
>{{ toggleFollowButtonText }}</gl-button :loading="toggleFollowLoading"
> size="small"
data-testid="toggle-follow-button"
@click="toggleFollow"
>{{ toggleFollowButtonText }}</gl-button
>
</template>
<template #meta> <template #meta>
<span <span
@ -208,7 +224,7 @@ export default {
>({{ user.pronouns }})</span >({{ user.pronouns }})</span
> >
<span v-if="isBusy" class="gl-text-gray-500 gl-font-sm gl-font-weight-normal gl-p-1" <span v-if="isBusy" class="gl-text-gray-500 gl-font-sm gl-font-weight-normal gl-p-1"
>({{ $options.i18n.busy }})</span >({{ $options.I18N_USER_BUSY }})</span
> >
</template> </template>
</gl-avatar-labeled> </gl-avatar-labeled>
@ -223,39 +239,41 @@ export default {
/> />
</template> </template>
<template v-else> <template v-else>
<div class="gl-text-gray-500"> <template v-if="!isBlocked">
<div v-if="user.bio" class="gl-display-flex gl-mb-2"> <div class="gl-text-gray-500">
<gl-icon name="profile" class="gl-flex-shrink-0" /> <div v-if="user.bio" class="gl-display-flex gl-mb-2">
<span ref="bio" class="gl-ml-2">{{ user.bio }}</span> <gl-icon name="profile" class="gl-flex-shrink-0" />
<span ref="bio" class="gl-ml-2">{{ user.bio }}</span>
</div>
<div v-if="user.workInformation" class="gl-display-flex gl-mb-2">
<gl-icon name="work" class="gl-flex-shrink-0" />
<span ref="workInformation" class="gl-ml-2">{{ user.workInformation }}</span>
</div>
<div v-if="user.location" class="gl-display-flex gl-mb-2">
<gl-icon name="location" class="gl-flex-shrink-0" />
<span class="gl-ml-2">{{ user.location }}</span>
</div>
<div
v-if="user.localTime && !user.bot"
class="gl-display-flex gl-mb-2"
data-testid="user-popover-local-time"
>
<gl-icon name="clock" class="gl-flex-shrink-0" />
<span class="gl-ml-2">{{ user.localTime }}</span>
</div>
</div> </div>
<div v-if="user.workInformation" class="gl-display-flex gl-mb-2"> <div v-if="statusHtml" class="gl-mb-2" data-testid="user-popover-status">
<gl-icon name="work" class="gl-flex-shrink-0" /> <span v-safe-html:[$options.safeHtmlConfig]="statusHtml"></span>
<span ref="workInformation" class="gl-ml-2">{{ user.workInformation }}</span>
</div> </div>
<div v-if="user.location" class="gl-display-flex gl-mb-2"> <div v-if="user.bot && user.websiteUrl" class="gl-text-blue-500">
<gl-icon name="location" class="gl-flex-shrink-0" /> <gl-icon name="question" />
<span class="gl-ml-2">{{ user.location }}</span> <gl-link data-testid="user-popover-bot-docs-link" :href="user.websiteUrl">
<gl-sprintf :message="$options.I18N_USER_LEARN">
<template #name>{{ user.name }}</template>
</gl-sprintf>
</gl-link>
</div> </div>
<div </template>
v-if="user.localTime && !user.bot"
class="gl-display-flex gl-mb-2"
data-testid="user-popover-local-time"
>
<gl-icon name="clock" class="gl-flex-shrink-0" />
<span class="gl-ml-2">{{ user.localTime }}</span>
</div>
</div>
<div v-if="statusHtml" class="gl-mb-2" data-testid="user-popover-status">
<span v-safe-html:[$options.safeHtmlConfig]="statusHtml"></span>
</div>
<div v-if="user.bot && user.websiteUrl" class="gl-text-blue-500">
<gl-icon name="question" />
<gl-link data-testid="user-popover-bot-docs-link" :href="user.websiteUrl">
<gl-sprintf :message="__('Learn more about %{username}')">
<template #username>{{ user.name }}</template>
</gl-sprintf>
</gl-link>
</div>
</template> </template>
</div> </div>
</gl-popover> </gl-popover>

View File

@ -2,29 +2,30 @@
= stylesheet_link_tag 'mailers/highlighted_diff_email' = stylesheet_link_tag 'mailers/highlighted_diff_email'
%h3 %h3
#{@message.author_name} #{@message.action_name} #{@message.ref_type} #{@message.ref_name} = s_('Notify|%{author_name} %{action_name} %{ref_type} %{ref_name} at %{project_link}').html_safe % {author_name: @message.author_name, action_name: @message.action_name, ref_type: @message.ref_type, ref_name: @message.ref_name, project_link: link_to(@message.project_name_with_namespace, strip_tags(project_url(@message.project)))}
at #{link_to(@message.project_name_with_namespace, project_url(@message.project))}
- if @message.compare - if @message.compare
- if @message.reverse_compare? - if @message.reverse_compare?
%p %p
%strong WARNING: %strong
The push did not contain any new commits, but force pushed to delete the commits and changes below. = _('WARNING:')
= s_('Notify|The push did not contain any new commits, but force pushed to delete the commits and changes below.')
%h4 %h4
= @message.reverse_compare? ? "Deleted commits:" : "Commits:" = @message.reverse_compare? ? _("Deleted commits:") : _("Commits:")
%ul %ul
- @message.commits.each do |commit| - @message.commits.each do |commit|
%li %li
%strong= link_to(commit.short_id, project_commit_url(@message.project, commit)) %strong= link_to(commit.short_id, project_commit_url(@message.project, commit))
%div %div
%span by #{commit.author_name} = html_escape(s_('Notify|%{committed_by_start} by %{author_name} %{committed_by_end} %{committed_at_start} at %{committed_date} %{committed_at_end}')) % {committed_by_start: '<span>'.html_safe, author_name: commit.author_name, committed_by_end: '</span>'.html_safe, committed_at_start: '<i>'.html_safe, committed_date: commit.committed_date.to_s(:iso8601), committed_at_end: '</i>'.html_safe}
%i at #{commit.committed_date.to_s(:iso8601)}
%pre.commit-message %pre.commit-message
= commit.safe_message = commit.safe_message
%h4 #{pluralize @message.diffs_count, "changed file"}: %h4
- changed_files = n_('%d changed file', '%d changed files', @message.diffs_count) % @message.diffs_count
= s_('Notify|%{changed_files}:') % {changed_files: changed_files}
%ul %ul
- @message.diffs.each do |diff_file| - @message.diffs.each do |diff_file|
@ -47,9 +48,11 @@
- unless @message.disable_diffs? - unless @message.disable_diffs?
- if @message.compare_timeout - if @message.compare_timeout
%h5 The diff was not included because it is too large. %h5
= s_('Notify|The diff was not included because it is too large.')
- else - else
%h4 Changes: %h4
= _('Changes:')
- @message.diffs.each do |diff_file| - @message.diffs.each do |diff_file|
- file_hash = hexdigest(diff_file.file_path) - file_hash = hexdigest(diff_file.file_path)
%li{ id: file_hash } %li{ id: file_hash }
@ -57,7 +60,7 @@
- if diff_file.deleted_file? - if diff_file.deleted_file?
%strong< %strong<
= diff_file.old_path = diff_file.old_path
deleted = s_('deleted')
- elsif diff_file.renamed_file? - elsif diff_file.renamed_file?
%strong< %strong<
= diff_file.old_path = diff_file.old_path
@ -68,7 +71,7 @@
%strong< %strong<
= diff_file.new_path = diff_file.new_path
- if diff_file.too_large? - if diff_file.too_large?
The diff for this file was not included because it is too large. = s_('Notify|The diff for this file was not included because it is too large.')
- else - else
%hr %hr
- blob = diff_file.blob - blob = diff_file.blob
@ -76,5 +79,5 @@
%table.code.white %table.code.white
= render partial: "projects/diffs/email_line", collection: diff_file.highlighted_diff_lines, as: :line, locals: { diff_file: diff_file } = render partial: "projects/diffs/email_line", collection: diff_file.highlighted_diff_lines, as: :line, locals: { diff_file: diff_file }
- else - else
No preview for this file type = s_('Notify|No preview for this file type')
%br %br

View File

@ -79,11 +79,11 @@ module Gitlab
@action_name ||= @action_name ||=
case @action case @action
when :create when :create
'pushed new' s_('Notify|pushed new')
when :delete when :delete
'deleted' s_('Notify|deleted')
else else
'pushed to' s_('Notify|pushed to')
end end
end end

View File

@ -7723,6 +7723,9 @@ msgstr ""
msgid "Changes to the title have not been saved" msgid "Changes to the title have not been saved"
msgstr "" msgstr ""
msgid "Changes:"
msgstr ""
msgid "Changing any setting here requires an application restart" msgid "Changing any setting here requires an application restart"
msgstr "" msgstr ""
@ -9620,6 +9623,9 @@ msgstr ""
msgid "Commits you select appear here. Go to the first tab and select commits to add to this merge request." msgid "Commits you select appear here. Go to the first tab and select commits to add to this merge request."
msgstr "" msgstr ""
msgid "Commits:"
msgstr ""
msgid "Commits|An error occurred while fetching merge requests data." msgid "Commits|An error occurred while fetching merge requests data."
msgstr "" msgstr ""
@ -12717,6 +12723,9 @@ msgstr ""
msgid "Deleted chat nickname: %{chat_name}!" msgid "Deleted chat nickname: %{chat_name}!"
msgstr "" msgstr ""
msgid "Deleted commits:"
msgstr ""
msgid "Deleted projects cannot be restored!" msgid "Deleted projects cannot be restored!"
msgstr "" msgstr ""
@ -23423,7 +23432,7 @@ msgstr ""
msgid "Learn more" msgid "Learn more"
msgstr "" msgstr ""
msgid "Learn more about %{username}" msgid "Learn more about %{name}"
msgstr "" msgstr ""
msgid "Learn more about Auto DevOps" msgid "Learn more about Auto DevOps"
@ -27039,12 +27048,21 @@ msgstr ""
msgid "Notify|%{author_link}'s issue %{issue_reference_link} is due soon." msgid "Notify|%{author_link}'s issue %{issue_reference_link} is due soon."
msgstr "" msgstr ""
msgid "Notify|%{author_name} %{action_name} %{ref_type} %{ref_name} at %{project_link}"
msgstr ""
msgid "Notify|%{changed_files}:"
msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}" msgid "Notify|%{commit_link} in %{mr_link}"
msgstr "" msgstr ""
msgid "Notify|%{commits_text} from branch `%{target_branch}`" msgid "Notify|%{commits_text} from branch `%{target_branch}`"
msgstr "" msgstr ""
msgid "Notify|%{committed_by_start} by %{author_name} %{committed_by_end} %{committed_at_start} at %{committed_date} %{committed_at_end}"
msgstr ""
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}." msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr "" msgstr ""
@ -27171,6 +27189,9 @@ msgstr ""
msgid "Notify|New issue: %{project_issue_url}" msgid "Notify|New issue: %{project_issue_url}"
msgstr "" msgstr ""
msgid "Notify|No preview for this file type"
msgstr ""
msgid "Notify|Pipeline %{pipeline_link} triggered by" msgid "Notify|Pipeline %{pipeline_link} triggered by"
msgstr "" msgstr ""
@ -27180,6 +27201,15 @@ msgstr ""
msgid "Notify|The Auto DevOps pipeline failed for pipeline %{pipeline_link} and has been disabled for %{project_link}. In order to use the Auto DevOps pipeline with your project, please review the %{supported_langs_link}, adjust your project accordingly, and turn on the Auto DevOps pipeline within your %{settings_link}." msgid "Notify|The Auto DevOps pipeline failed for pipeline %{pipeline_link} and has been disabled for %{project_link}. In order to use the Auto DevOps pipeline with your project, please review the %{supported_langs_link}, adjust your project accordingly, and turn on the Auto DevOps pipeline within your %{settings_link}."
msgstr "" msgstr ""
msgid "Notify|The diff for this file was not included because it is too large."
msgstr ""
msgid "Notify|The diff was not included because it is too large."
msgstr ""
msgid "Notify|The push did not contain any new commits, but force pushed to delete the commits and changes below."
msgstr ""
msgid "Notify|This issue is due on: %{issue_due_date}" msgid "Notify|This issue is due on: %{issue_due_date}"
msgstr "" msgstr ""
@ -27204,6 +27234,15 @@ msgstr ""
msgid "Notify|currently supported languages" msgid "Notify|currently supported languages"
msgstr "" msgstr ""
msgid "Notify|deleted"
msgstr ""
msgid "Notify|pushed new"
msgstr ""
msgid "Notify|pushed to"
msgstr ""
msgid "Notify|successfully completed %{jobs} in %{stages}." msgid "Notify|successfully completed %{jobs} in %{stages}."
msgstr "" msgstr ""
@ -43051,6 +43090,9 @@ msgstr ""
msgid "User identity was successfully updated." msgid "User identity was successfully updated."
msgstr "" msgstr ""
msgid "User is blocked"
msgstr ""
msgid "User is not allowed to resolve thread" msgid "User is not allowed to resolve thread"
msgstr "" msgstr ""
@ -44194,6 +44236,9 @@ msgstr ""
msgid "Vulnerability|View training" msgid "Vulnerability|View training"
msgstr "" msgstr ""
msgid "WARNING:"
msgstr ""
msgid "WARNING: This snippet contains hidden files which might be used to mask malicious behavior. Exercise caution if cloning and executing code from this snippet." msgid "WARNING: This snippet contains hidden files which might be used to mask malicious behavior. Exercise caution if cloning and executing code from this snippet."
msgstr "" msgstr ""

View File

@ -1,16 +0,0 @@
# frozen_string_literal: true
module QA
module Scenario
module Test
module Instance
class Blocking < Template
include Bootable
include SharedAttributes
tags :reliable, :smoke
end
end
end
end
end

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
module QA
module Scenario
module Test
module Instance
class ReviewBlocking < All
tags :reliable,
:sanity_feature_flags,
:"~orchestrated",
:"~skip_signup_disabled"
end
end
end
end
end

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
module QA
module Scenario
module Test
module Instance
class ReviewNonBlocking < All
tags :"~reliable",
:"~smoke",
:"~skip_signup_disabled",
*Specs::Runner::DEFAULT_SKIPPED_TAGS.map { |tag| :"~#{tag}" }
end
end
end
end
end

View File

@ -64,7 +64,7 @@ module QA
it( it(
'comments on an issue with an attachment', 'comments on an issue with an attachment',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347946', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347946',
except: { job: 'review-qa-smoke' } except: { job: 'review-qa-*' }
) do ) do
Page::Project::Issue::Show.perform do |show| Page::Project::Issue::Show.perform do |show|
show.comment('See attached image for scale', attachment: file_to_attach) show.comment('See attached image for scale', attachment: file_to_attach)

View File

@ -15,7 +15,8 @@ module QA
{ klass: "Test::Instance::All" }, { klass: "Test::Instance::All" },
{ klass: "Test::Instance::Smoke" }, { klass: "Test::Instance::Smoke" },
{ klass: "Test::Instance::Reliable" }, { klass: "Test::Instance::Reliable" },
{ klass: "Test::Instance::Blocking" }, { klass: "Test::Instance::ReviewBlocking" },
{ klass: "Test::Instance::ReviewNonBlocking" },
{ klass: "Test::Instance::CloudActivation" }, { klass: "Test::Instance::CloudActivation" },
{ klass: "Test::Instance::Integrations" }, { klass: "Test::Instance::Integrations" },
{ klass: "Test::Instance::Jira" }, { klass: "Test::Instance::Jira" },

View File

@ -21,6 +21,7 @@ fi
variables=$(cat <<YML variables=$(cat <<YML
variables: variables:
GITLAB_VERSION: "$(cat VERSION)" GITLAB_VERSION: "$(cat VERSION)"
COLORIZED_LOGS: "true"
QA_TESTS: "$QA_TESTS" QA_TESTS: "$QA_TESTS"
QA_FEATURE_FLAGS: "${QA_FEATURE_FLAGS}" QA_FEATURE_FLAGS: "${QA_FEATURE_FLAGS}"
QA_FRAMEWORK_CHANGES: "${QA_FRAMEWORK_CHANGES:-false}" QA_FRAMEWORK_CHANGES: "${QA_FRAMEWORK_CHANGES:-false}"
@ -29,13 +30,10 @@ YML
) )
echo "Using .gitlab/ci/review-apps/main.gitlab-ci.yml and .gitlab/ci/package-and-test/main.gitlab-ci.yml" echo "Using .gitlab/ci/review-apps/main.gitlab-ci.yml and .gitlab/ci/package-and-test/main.gitlab-ci.yml"
cp .gitlab/ci/review-apps/main.gitlab-ci.yml "$REVIEW_PIPELINE_YML" cp .gitlab/ci/review-apps/main.gitlab-ci.yml "$REVIEW_PIPELINE_YML"
echo "$variables" >>"$REVIEW_PIPELINE_YML" echo "$variables" >>"$REVIEW_PIPELINE_YML"
echo "Successfully generated review-app pipeline with following variables section:"
echo "$variables"
cp .gitlab/ci/package-and-test/main.gitlab-ci.yml "$OMNIBUS_PIPELINE_YML" cp .gitlab/ci/package-and-test/main.gitlab-ci.yml "$OMNIBUS_PIPELINE_YML"
echo "$variables" >>"$OMNIBUS_PIPELINE_YML" echo "$variables" >>"$OMNIBUS_PIPELINE_YML"
echo "Successfully generated package-and-test pipeline with following variables section:"
echo "Successfully generated review-app and package-and-test pipeline with following variables section:"
echo "$variables" echo "$variables"

View File

@ -32,13 +32,6 @@ end
ActiveSupport::XmlMini.backend = 'Nokogiri' ActiveSupport::XmlMini.backend = 'Nokogiri'
RSpec.configure do |config| RSpec.configure do |config|
unless ENV['CI']
# Allow running `:focus` examples locally,
# falling back to all tests when there is no `:focus` example.
config.filter_run focus: true
config.run_all_when_everything_filtered = true
end
# Makes diffs show entire non-truncated values. # Makes diffs show entire non-truncated values.
config.before(:each, unlimited_max_formatted_output_length: true) do |_example| config.before(:each, unlimited_max_formatted_output_length: true) do |_example|
config.expect_with :rspec do |c| config.expect_with :rspec do |c|

View File

@ -1,8 +1,15 @@
import { GlSkeletonLoader, GlIcon } from '@gitlab/ui'; import { GlSkeletonLoader, GlIcon } from '@gitlab/ui';
import { loadHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; import { loadHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import { sprintf } from '~/locale';
import { mountExtended } from 'helpers/vue_test_utils_helper'; import { mountExtended } from 'helpers/vue_test_utils_helper';
import { AVAILABILITY_STATUS } from '~/set_status_modal/constants'; import { AVAILABILITY_STATUS } from '~/set_status_modal/constants';
import UserPopover from '~/vue_shared/components/user_popover/user_popover.vue'; import UserPopover from '~/vue_shared/components/user_popover/user_popover.vue';
import {
I18N_USER_BLOCKED,
I18N_USER_LEARN,
I18N_USER_FOLLOW,
I18N_USER_UNFOLLOW,
} from '~/vue_shared/components/user_popover/constants';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { followUser, unfollowUser } from '~/api/user_api'; import { followUser, unfollowUser } from '~/api/user_api';
@ -310,7 +317,9 @@ describe('User Popover Component', () => {
const securityBotDocsLink = findSecurityBotDocsLink(); const securityBotDocsLink = findSecurityBotDocsLink();
expect(securityBotDocsLink.exists()).toBe(true); expect(securityBotDocsLink.exists()).toBe(true);
expect(securityBotDocsLink.attributes('href')).toBe(SECURITY_BOT_USER.websiteUrl); expect(securityBotDocsLink.attributes('href')).toBe(SECURITY_BOT_USER.websiteUrl);
expect(securityBotDocsLink.text()).toBe('Learn more about GitLab Security Bot'); expect(securityBotDocsLink.text()).toBe(
sprintf(I18N_USER_LEARN, { name: SECURITY_BOT_USER.name }),
);
}); });
it("does not show a link to the bot's documentation if there is no website_url", () => { it("does not show a link to the bot's documentation if there is no website_url", () => {
@ -320,9 +329,10 @@ describe('User Popover Component', () => {
}); });
it("doesn't escape user's name", () => { it("doesn't escape user's name", () => {
createWrapper({ user: { ...SECURITY_BOT_USER, name: '%<>\';"' } }); const name = '%<>\';"';
createWrapper({ user: { ...SECURITY_BOT_USER, name } });
const securityBotDocsLink = findSecurityBotDocsLink(); const securityBotDocsLink = findSecurityBotDocsLink();
expect(securityBotDocsLink.text()).toBe('Learn more about %<>\';"'); expect(securityBotDocsLink.text()).toBe(sprintf(I18N_USER_LEARN, { name }, false));
}); });
it('does not display local time', () => { it('does not display local time', () => {
@ -336,7 +346,7 @@ describe('User Popover Component', () => {
beforeEach(() => createWrapper()); beforeEach(() => createWrapper());
it('renders the Follow button with the correct variant', () => { it('renders the Follow button with the correct variant', () => {
expect(findToggleFollowButton().text()).toBe('Follow'); expect(findToggleFollowButton().text()).toBe(I18N_USER_FOLLOW);
expect(findToggleFollowButton().props('variant')).toBe('confirm'); expect(findToggleFollowButton().props('variant')).toBe('confirm');
}); });
@ -387,7 +397,7 @@ describe('User Popover Component', () => {
beforeEach(() => createWrapper({ user: { ...DEFAULT_PROPS.user, isFollowed: true } })); beforeEach(() => createWrapper({ user: { ...DEFAULT_PROPS.user, isFollowed: true } }));
it('renders the Unfollow button with the correct variant', () => { it('renders the Unfollow button with the correct variant', () => {
expect(findToggleFollowButton().text()).toBe('Unfollow'); expect(findToggleFollowButton().text()).toBe(I18N_USER_UNFOLLOW);
expect(findToggleFollowButton().props('variant')).toBe('default'); expect(findToggleFollowButton().props('variant')).toBe('default');
}); });
@ -441,6 +451,25 @@ describe('User Popover Component', () => {
}); });
}); });
describe('when the user is blocked', () => {
const bio = 'My super interesting bio';
const status = 'My status';
beforeEach(() =>
createWrapper({
user: { ...DEFAULT_PROPS.user, state: 'blocked', bio, status: { message_html: status } },
}),
);
it('renders warning', () => {
expect(wrapper.text()).toContain(I18N_USER_BLOCKED);
});
it("doesn't show other information", () => {
expect(wrapper.text()).not.toContain(bio);
expect(wrapper.text()).not.toContain(status);
});
});
describe('when API does not support `isFollowed`', () => { describe('when API does not support `isFollowed`', () => {
beforeEach(() => { beforeEach(() => {
const user = { const user = {

View File

@ -36,6 +36,7 @@ require 'rspec-parameterized'
require 'shoulda/matchers' require 'shoulda/matchers'
require 'test_prof/recipes/rspec/let_it_be' require 'test_prof/recipes/rspec/let_it_be'
require 'test_prof/factory_default' require 'test_prof/factory_default'
require 'test_prof/factory_prof/nate_heckler'
require 'parslet/rig/rspec' require 'parslet/rig/rspec'
rspec_profiling_is_configured = rspec_profiling_is_configured =
@ -109,11 +110,6 @@ RSpec.configure do |config|
warn `curl -s -o log/goroutines.log http://localhost:9236/debug/pprof/goroutine?debug=2` warn `curl -s -o log/goroutines.log http://localhost:9236/debug/pprof/goroutine?debug=2`
end end
end end
else
# Allow running `:focus` examples locally,
# falling back to all tests when there is no `:focus` example.
config.filter_run focus: true
config.run_all_when_everything_filtered = true
end end
# Attempt to troubleshoot https://gitlab.com/gitlab-org/gitlab/-/issues/351531 # Attempt to troubleshoot https://gitlab.com/gitlab-org/gitlab/-/issues/351531

View File

@ -14,6 +14,13 @@ RSpec.configure do |config|
# Re-run failures locally with `--only-failures` # Re-run failures locally with `--only-failures`
config.example_status_persistence_file_path = ENV.fetch('RSPEC_LAST_RUN_RESULTS_FILE', './spec/examples.txt') config.example_status_persistence_file_path = ENV.fetch('RSPEC_LAST_RUN_RESULTS_FILE', './spec/examples.txt')
unless ENV['CI']
# Allow running `:focus` examples locally,
# falling back to all tests when there is no `:focus` example.
config.filter_run focus: true
config.run_all_when_everything_filtered = true
end
config.mock_with :rspec do |mocks| config.mock_with :rspec do |mocks|
mocks.verify_doubled_constant_names = true mocks.verify_doubled_constant_names = true
end end