Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-05-24 21:10:28 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-05-24 21:10:28 +0300
commit6ef0d7a8fee1a4d3de286a4a004517ce6d1cb692 (patch)
treef4df19e1e32d30c1389ba51b5fd2ccac3834a301
parent474d90a88dfd96f135655d07e9530da3cde6fd64 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab/merge_request_templates/Security Release.md4
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock104
-rw-r--r--app/assets/javascripts/issuable/components/csv_import_export_buttons.vue2
-rw-r--r--app/assets/javascripts/issues_list/components/issues_list_app.vue59
-rw-r--r--app/assets/javascripts/issues_list/constants.js28
-rw-r--r--app/assets/javascripts/issues_list/index.js8
-rw-r--r--app/assets/javascripts/issues_list/utils.js3
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/author_token.vue21
-rw-r--r--app/assets/javascripts/vue_shared/components/issue/issue_assignees.vue2
-rw-r--r--app/controllers/concerns/membership_actions.rb2
-rw-r--r--app/graphql/mutations/commits/create.rb6
-rw-r--r--app/helpers/issues_helper.rb2
-rw-r--r--app/models/ci/build.rb8
-rw-r--r--app/views/import/_githubish_status.html.haml2
-rw-r--r--app/views/shared/milestones/_issuable.html.haml2
-rw-r--r--app/workers/build_hooks_worker.rb10
-rw-r--r--config/feature_flags/development/prevent_retry_of_retried_jobs.yml (renamed from config/feature_flags/development/delayed_perform_for_build_hooks_worker.yml)10
-rw-r--r--config/gitlab.yml.example2
-rw-r--r--doc/api/graphql/reference/index.md1
-rw-r--r--doc/development/database/strings_and_the_text_data_type.md8
-rw-r--r--doc/development/graphql_guide/pagination.md20
-rw-r--r--doc/development/jh_features_review.md6
-rw-r--r--doc/development/migration_style_guide.md31
-rw-r--r--doc/development/stage_group_dashboards.md20
-rw-r--r--doc/user/markdown.md58
-rw-r--r--doc/user/packages/composer_repository/index.md2
-rw-r--r--doc/user/packages/conan_repository/index.md2
-rw-r--r--doc/user/packages/maven_repository/index.md8
-rw-r--r--doc/user/packages/npm_registry/index.md2
-rw-r--r--doc/user/packages/nuget_repository/index.md2
-rw-r--r--doc/user/packages/pypi_repository/index.md2
-rw-r--r--doc/user/packages/rubygems_registry/index.md4
-rw-r--r--doc/user/project/clusters/add_eks_clusters.md6
-rw-r--r--doc/user/project/clusters/add_gke_clusters.md2
-rw-r--r--doc/user/project/clusters/add_remove_clusters.md2
-rw-r--r--doc/user/project/issues/managing_issues.md11
-rw-r--r--lib/gitlab/database/background_migration/batched_migration.rb4
-rw-r--r--lib/gitlab/database/migrations/background_migration_helpers.rb12
-rw-r--r--lib/gitlab/experimentation.rb6
-rw-r--r--lib/gitlab/git/remote_repository.rb17
-rw-r--r--lib/gitlab/setup_helper.rb2
-rw-r--r--package.json2
-rw-r--r--spec/deprecation_toolkit_env.rb6
-rw-r--r--spec/frontend/issues_list/components/issues_list_app_spec.js115
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js62
-rw-r--r--spec/frontend/vue_shared/components/issue/issue_assignees_spec.js4
-rw-r--r--spec/graphql/mutations/commits/create_spec.rb5
-rw-r--r--spec/helpers/issues_helper_spec.rb2
-rw-r--r--spec/lib/gitlab/database/background_migration/batched_migration_spec.rb25
-rw-r--r--spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb32
-rw-r--r--spec/lib/gitlab/git/remote_repository_spec.rb41
-rw-r--r--spec/models/ci/build_spec.rb20
-rw-r--r--spec/workers/build_hooks_worker_spec.rb16
-rw-r--r--yarn.lock8
55 files changed, 573 insertions, 270 deletions
diff --git a/.gitlab/merge_request_templates/Security Release.md b/.gitlab/merge_request_templates/Security Release.md
index fccfad18ef0..77e8718c34f 100644
--- a/.gitlab/merge_request_templates/Security Release.md
+++ b/.gitlab/merge_request_templates/Security Release.md
@@ -16,7 +16,7 @@ See [the general developer security release guidelines](https://gitlab.com/gitla
- [ ] Merge request targets `master`, or a versioned stable branch (`X-Y-stable-ee`).
- [ ] Milestone is set for the version this merge request applies to. A closed milestone can be assigned via [quick actions].
- [ ] Title of this merge request is the same as for all backports.
-- [ ] A [CHANGELOG entry] is added without a `merge_request` value, with `type` set to `security`
+- [ ] A [CHANGELOG entry] has been included, with `Changelog` trailer set to `security`.
- [ ] For the MR targeting `master`:
- [ ] Assign to a reviewer and maintainer, per our [Code Review process].
- [ ] Ensure it's approved according to our [Approval Guidelines].
@@ -37,7 +37,7 @@ See [the general developer security release guidelines](https://gitlab.com/gitla
[GitLab Security]: https://gitlab.com/gitlab-org/security/gitlab
[quick actions]: https://docs.gitlab.com/ee/user/project/quick_actions.html#quick-actions-for-issues-merge-requests-and-epics
-[CHANGELOG entry]: https://docs.gitlab.com/ee/development/changelog.html
+[CHANGELOG entry]: https://docs.gitlab.com/ee/development/changelog.html#overview
[Code Review process]: https://docs.gitlab.com/ee/development/code_review.html
[Approval Guidelines]: https://docs.gitlab.com/ee/development/code_review.html#approval-guidelines
[Canonical repository]: https://gitlab.com/gitlab-org/gitlab
diff --git a/Gemfile b/Gemfile
index 724c7143119..018bf5fbcbe 100644
--- a/Gemfile
+++ b/Gemfile
@@ -2,7 +2,7 @@
source 'https://rubygems.org'
-gem 'rails', '~> 6.0.3.6'
+gem 'rails', '~> 6.0.3.7'
gem 'bootsnap', '~> 1.4.6'
diff --git a/Gemfile.lock b/Gemfile.lock
index 2d13bfb68c1..c70f1710d50 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -12,59 +12,59 @@ GEM
abstract_type (0.0.7)
acme-client (2.0.6)
faraday (>= 0.17, < 2.0.0)
- actioncable (6.0.3.6)
- actionpack (= 6.0.3.6)
+ actioncable (6.0.3.7)
+ actionpack (= 6.0.3.7)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
- actionmailbox (6.0.3.6)
- actionpack (= 6.0.3.6)
- activejob (= 6.0.3.6)
- activerecord (= 6.0.3.6)
- activestorage (= 6.0.3.6)
- activesupport (= 6.0.3.6)
+ actionmailbox (6.0.3.7)
+ actionpack (= 6.0.3.7)
+ activejob (= 6.0.3.7)
+ activerecord (= 6.0.3.7)
+ activestorage (= 6.0.3.7)
+ activesupport (= 6.0.3.7)
mail (>= 2.7.1)
- actionmailer (6.0.3.6)
- actionpack (= 6.0.3.6)
- actionview (= 6.0.3.6)
- activejob (= 6.0.3.6)
+ actionmailer (6.0.3.7)
+ actionpack (= 6.0.3.7)
+ actionview (= 6.0.3.7)
+ activejob (= 6.0.3.7)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
- actionpack (6.0.3.6)
- actionview (= 6.0.3.6)
- activesupport (= 6.0.3.6)
+ actionpack (6.0.3.7)
+ actionview (= 6.0.3.7)
+ activesupport (= 6.0.3.7)
rack (~> 2.0, >= 2.0.8)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
- actiontext (6.0.3.6)
- actionpack (= 6.0.3.6)
- activerecord (= 6.0.3.6)
- activestorage (= 6.0.3.6)
- activesupport (= 6.0.3.6)
+ actiontext (6.0.3.7)
+ actionpack (= 6.0.3.7)
+ activerecord (= 6.0.3.7)
+ activestorage (= 6.0.3.7)
+ activesupport (= 6.0.3.7)
nokogiri (>= 1.8.5)
- actionview (6.0.3.6)
- activesupport (= 6.0.3.6)
+ actionview (6.0.3.7)
+ activesupport (= 6.0.3.7)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
- activejob (6.0.3.6)
- activesupport (= 6.0.3.6)
+ activejob (6.0.3.7)
+ activesupport (= 6.0.3.7)
globalid (>= 0.3.6)
- activemodel (6.0.3.6)
- activesupport (= 6.0.3.6)
- activerecord (6.0.3.6)
- activemodel (= 6.0.3.6)
- activesupport (= 6.0.3.6)
+ activemodel (6.0.3.7)
+ activesupport (= 6.0.3.7)
+ activerecord (6.0.3.7)
+ activemodel (= 6.0.3.7)
+ activesupport (= 6.0.3.7)
activerecord-explain-analyze (0.1.0)
activerecord (>= 4)
pg
- activestorage (6.0.3.6)
- actionpack (= 6.0.3.6)
- activejob (= 6.0.3.6)
- activerecord (= 6.0.3.6)
+ activestorage (6.0.3.7)
+ actionpack (= 6.0.3.7)
+ activejob (= 6.0.3.7)
+ activerecord (= 6.0.3.7)
marcel (~> 1.0.0)
- activesupport (6.0.3.6)
+ activesupport (6.0.3.7)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
@@ -366,7 +366,7 @@ GEM
fast_blank (1.0.0)
fast_gettext (1.6.0)
ffaker (2.10.0)
- ffi (1.13.1)
+ ffi (1.15.1)
ffi-compiler (1.0.1)
ffi (>= 1.0.0)
rake
@@ -963,20 +963,20 @@ GEM
rack-test (1.1.0)
rack (>= 1.0, < 3)
rack-timeout (0.5.2)
- rails (6.0.3.6)
- actioncable (= 6.0.3.6)
- actionmailbox (= 6.0.3.6)
- actionmailer (= 6.0.3.6)
- actionpack (= 6.0.3.6)
- actiontext (= 6.0.3.6)
- actionview (= 6.0.3.6)
- activejob (= 6.0.3.6)
- activemodel (= 6.0.3.6)
- activerecord (= 6.0.3.6)
- activestorage (= 6.0.3.6)
- activesupport (= 6.0.3.6)
+ rails (6.0.3.7)
+ actioncable (= 6.0.3.7)
+ actionmailbox (= 6.0.3.7)
+ actionmailer (= 6.0.3.7)
+ actionpack (= 6.0.3.7)
+ actiontext (= 6.0.3.7)
+ actionview (= 6.0.3.7)
+ activejob (= 6.0.3.7)
+ activemodel (= 6.0.3.7)
+ activerecord (= 6.0.3.7)
+ activestorage (= 6.0.3.7)
+ activesupport (= 6.0.3.7)
bundler (>= 1.3.0)
- railties (= 6.0.3.6)
+ railties (= 6.0.3.7)
sprockets-rails (>= 2.0.0)
rails-controller-testing (1.0.5)
actionpack (>= 5.0.1.rc1)
@@ -990,9 +990,9 @@ GEM
rails-i18n (6.0.0)
i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 7)
- railties (6.0.3.6)
- actionpack (= 6.0.3.6)
- activesupport (= 6.0.3.6)
+ railties (6.0.3.7)
+ actionpack (= 6.0.3.7)
+ activesupport (= 6.0.3.7)
method_source
rake (>= 0.8.7)
thor (>= 0.20.3, < 2.0)
@@ -1588,7 +1588,7 @@ DEPENDENCIES
rack-oauth2 (~> 1.16.0)
rack-proxy (~> 0.6.0)
rack-timeout (~> 0.5.1)
- rails (~> 6.0.3.6)
+ rails (~> 6.0.3.7)
rails-controller-testing
rails-i18n (~> 6.0)
rainbow (~> 3.0)
diff --git a/app/assets/javascripts/issuable/components/csv_import_export_buttons.vue b/app/assets/javascripts/issuable/components/csv_import_export_buttons.vue
index 5d75b87fe76..4fdd094072c 100644
--- a/app/assets/javascripts/issuable/components/csv_import_export_buttons.vue
+++ b/app/assets/javascripts/issuable/components/csv_import_export_buttons.vue
@@ -84,7 +84,7 @@ export default {
<template>
<div :class="containerClass">
- <gl-button-group>
+ <gl-button-group class="gl-w-full">
<gl-button
v-if="showExportButton"
v-gl-tooltip="$options.i18n.exportAsCsvButtonText"
diff --git a/app/assets/javascripts/issues_list/components/issues_list_app.vue b/app/assets/javascripts/issues_list/components/issues_list_app.vue
index 93ba338a6b3..197eeb0a3b6 100644
--- a/app/assets/javascripts/issues_list/components/issues_list_app.vue
+++ b/app/assets/javascripts/issues_list/components/issues_list_app.vue
@@ -27,6 +27,15 @@ import {
PARAM_SORT,
PARAM_STATE,
RELATIVE_POSITION_DESC,
+ TOKEN_TYPE_ASSIGNEE,
+ TOKEN_TYPE_AUTHOR,
+ TOKEN_TYPE_CONFIDENTIAL,
+ TOKEN_TYPE_MY_REACTION,
+ TOKEN_TYPE_EPIC,
+ TOKEN_TYPE_ITERATION,
+ TOKEN_TYPE_LABEL,
+ TOKEN_TYPE_MILESTONE,
+ TOKEN_TYPE_WEIGHT,
UPDATED_DESC,
URL_PARAM,
urlSortParams,
@@ -110,15 +119,15 @@ export default {
hasBlockedIssuesFeature: {
default: false,
},
- hasIssues: {
- default: false,
- },
hasIssueWeightsFeature: {
default: false,
},
hasMultipleIssueAssigneesFeature: {
default: false,
},
+ hasProjectIssues: {
+ default: false,
+ },
initialEmail: {
default: '',
},
@@ -174,6 +183,9 @@ export default {
};
},
computed: {
+ hasSearch() {
+ return this.searchQuery || Object.keys(this.urlFilterParams).length;
+ },
isBulkEditButtonDisabled() {
return this.showBulkEditSidebar || !this.issues.length;
},
@@ -195,7 +207,7 @@ export default {
searchTokens() {
const tokens = [
{
- type: 'author_username',
+ type: TOKEN_TYPE_AUTHOR,
title: TOKEN_TITLE_AUTHOR,
icon: 'pencil',
token: AuthorToken,
@@ -205,7 +217,7 @@ export default {
fetchAuthors: this.fetchUsers,
},
{
- type: 'assignee_username',
+ type: TOKEN_TYPE_ASSIGNEE,
title: TOKEN_TITLE_ASSIGNEE,
icon: 'user',
token: AuthorToken,
@@ -215,7 +227,7 @@ export default {
fetchAuthors: this.fetchUsers,
},
{
- type: 'milestone',
+ type: TOKEN_TYPE_MILESTONE,
title: TOKEN_TITLE_MILESTONE,
icon: 'clock',
token: MilestoneToken,
@@ -224,24 +236,28 @@ export default {
fetchMilestones: this.fetchMilestones,
},
{
- type: 'labels',
+ type: TOKEN_TYPE_LABEL,
title: TOKEN_TITLE_LABEL,
icon: 'labels',
token: LabelToken,
defaultLabels: [],
fetchLabels: this.fetchLabels,
},
- {
- type: 'my_reaction_emoji',
+ ];
+
+ if (this.isSignedIn) {
+ tokens.push({
+ type: TOKEN_TYPE_MY_REACTION,
title: TOKEN_TITLE_MY_REACTION,
icon: 'thumb-up',
token: EmojiToken,
unique: true,
operators: OPERATOR_IS_ONLY,
fetchEmojis: this.fetchEmojis,
- },
- {
- type: 'confidential',
+ });
+
+ tokens.push({
+ type: TOKEN_TYPE_CONFIDENTIAL,
title: TOKEN_TITLE_CONFIDENTIAL,
icon: 'eye-slash',
token: GlFilteredSearchToken,
@@ -251,12 +267,12 @@ export default {
{ icon: 'eye-slash', value: 'yes', title: this.$options.i18n.confidentialYes },
{ icon: 'eye', value: 'no', title: this.$options.i18n.confidentialNo },
],
- },
- ];
+ });
+ }
if (this.projectIterationsPath) {
tokens.push({
- type: 'iteration',
+ type: TOKEN_TYPE_ITERATION,
title: TOKEN_TITLE_ITERATION,
icon: 'iteration',
token: IterationToken,
@@ -267,7 +283,7 @@ export default {
if (this.groupEpicsPath) {
tokens.push({
- type: 'epic_id',
+ type: TOKEN_TYPE_EPIC,
title: TOKEN_TITLE_EPIC,
icon: 'epic',
token: EpicToken,
@@ -278,7 +294,7 @@ export default {
if (this.hasIssueWeightsFeature) {
tokens.push({
- type: 'weight',
+ type: TOKEN_TYPE_WEIGHT,
title: TOKEN_TITLE_WEIGHT,
icon: 'weight',
token: WeightToken,
@@ -365,7 +381,7 @@ export default {
return axios.get(this.autocompleteUsersPath, { params: { search } });
},
fetchIssues() {
- if (!this.hasIssues) {
+ if (!this.hasProjectIssues) {
return undefined;
}
@@ -490,7 +506,7 @@ export default {
</script>
<template>
- <div v-if="hasIssues">
+ <div v-if="hasProjectIssues">
<issuable-list
:namespace="projectPath"
recent-searches-storage-key="issues"
@@ -500,6 +516,7 @@ export default {
:sort-options="sortOptions"
:initial-sort-by="sortKey"
:issuables="issues"
+ label-filter-param="label_name"
:tabs="$options.IssuableListTabs"
:current-tab="state"
:tab-counts="tabCounts"
@@ -536,7 +553,7 @@ export default {
/>
<csv-import-export-buttons
v-if="isSignedIn"
- class="gl-mr-3"
+ class="gl-md-mr-3"
:export-csv-path="exportCsvPathWithQuery"
:issuable-count="totalIssues"
/>
@@ -600,7 +617,7 @@ export default {
<template #empty-state>
<gl-empty-state
- v-if="searchQuery"
+ v-if="hasSearch"
:description="$options.i18n.noSearchResultsDescription"
:title="$options.i18n.noSearchResultsTitle"
:svg-path="emptyStateSvgPath"
diff --git a/app/assets/javascripts/issues_list/constants.js b/app/assets/javascripts/issues_list/constants.js
index 54e9668d300..06e140d6420 100644
--- a/app/assets/javascripts/issues_list/constants.js
+++ b/app/assets/javascripts/issues_list/constants.js
@@ -281,8 +281,18 @@ export const SPECIAL_FILTER = 'specialFilter';
export const ALTERNATIVE_FILTER = 'alternativeFilter';
export const SPECIAL_FILTER_VALUES = [FILTER_NONE, FILTER_ANY, FILTER_CURRENT];
+export const TOKEN_TYPE_AUTHOR = 'author_username';
+export const TOKEN_TYPE_ASSIGNEE = 'assignee_username';
+export const TOKEN_TYPE_MILESTONE = 'milestone';
+export const TOKEN_TYPE_LABEL = 'labels';
+export const TOKEN_TYPE_MY_REACTION = 'my_reaction_emoji';
+export const TOKEN_TYPE_CONFIDENTIAL = 'confidential';
+export const TOKEN_TYPE_ITERATION = 'iteration';
+export const TOKEN_TYPE_EPIC = 'epic_id';
+export const TOKEN_TYPE_WEIGHT = 'weight';
+
export const filters = {
- author_username: {
+ [TOKEN_TYPE_AUTHOR]: {
[API_PARAM]: {
[OPERATOR_IS]: {
[NORMAL_FILTER]: 'author_username',
@@ -300,7 +310,7 @@ export const filters = {
},
},
},
- assignee_username: {
+ [TOKEN_TYPE_ASSIGNEE]: {
[API_PARAM]: {
[OPERATOR_IS]: {
[NORMAL_FILTER]: 'assignee_username',
@@ -321,7 +331,7 @@ export const filters = {
},
},
},
- milestone: {
+ [TOKEN_TYPE_MILESTONE]: {
[API_PARAM]: {
[OPERATOR_IS]: {
[NORMAL_FILTER]: 'milestone',
@@ -339,7 +349,7 @@ export const filters = {
},
},
},
- labels: {
+ [TOKEN_TYPE_LABEL]: {
[API_PARAM]: {
[OPERATOR_IS]: {
[NORMAL_FILTER]: 'labels',
@@ -357,7 +367,7 @@ export const filters = {
},
},
},
- my_reaction_emoji: {
+ [TOKEN_TYPE_MY_REACTION]: {
[API_PARAM]: {
[OPERATOR_IS]: {
[NORMAL_FILTER]: 'my_reaction_emoji',
@@ -371,7 +381,7 @@ export const filters = {
},
},
},
- confidential: {
+ [TOKEN_TYPE_CONFIDENTIAL]: {
[API_PARAM]: {
[OPERATOR_IS]: {
[NORMAL_FILTER]: 'confidential',
@@ -383,7 +393,7 @@ export const filters = {
},
},
},
- iteration: {
+ [TOKEN_TYPE_ITERATION]: {
[API_PARAM]: {
[OPERATOR_IS]: {
[NORMAL_FILTER]: 'iteration_title',
@@ -403,7 +413,7 @@ export const filters = {
},
},
},
- epic_id: {
+ [TOKEN_TYPE_EPIC]: {
[API_PARAM]: {
[OPERATOR_IS]: {
[NORMAL_FILTER]: 'epic_id',
@@ -423,7 +433,7 @@ export const filters = {
},
},
},
- weight: {
+ [TOKEN_TYPE_WEIGHT]: {
[API_PARAM]: {
[OPERATOR_IS]: {
[NORMAL_FILTER]: 'weight',
diff --git a/app/assets/javascripts/issues_list/index.js b/app/assets/javascripts/issues_list/index.js
index 55719f6449b..d0c9462a3d7 100644
--- a/app/assets/javascripts/issues_list/index.js
+++ b/app/assets/javascripts/issues_list/index.js
@@ -88,9 +88,9 @@ export function mountIssuesListApp() {
groupEpicsPath,
hasBlockedIssuesFeature,
hasIssuableHealthStatusFeature,
- hasIssues,
hasIssueWeightsFeature,
hasMultipleIssueAssigneesFeature,
+ hasProjectIssues,
importCsvIssuesPath,
initialEmail,
isSignedIn,
@@ -126,9 +126,9 @@ export function mountIssuesListApp() {
groupEpicsPath,
hasBlockedIssuesFeature: parseBoolean(hasBlockedIssuesFeature),
hasIssuableHealthStatusFeature: parseBoolean(hasIssuableHealthStatusFeature),
- hasIssues: parseBoolean(hasIssues),
hasIssueWeightsFeature: parseBoolean(hasIssueWeightsFeature),
hasMultipleIssueAssigneesFeature: parseBoolean(hasMultipleIssueAssigneesFeature),
+ hasProjectIssues: parseBoolean(hasProjectIssues),
isSignedIn: parseBoolean(isSignedIn),
issuesPath,
jiraIntegrationPath,
@@ -147,9 +147,9 @@ export function mountIssuesListApp() {
importCsvIssuesPath,
maxAttachmentSize,
projectImportJiraPath,
- showExportButton: parseBoolean(hasIssues),
+ showExportButton: parseBoolean(hasProjectIssues),
showImportButton: parseBoolean(canImportIssues),
- showLabel: !parseBoolean(hasIssues),
+ showLabel: !parseBoolean(hasProjectIssues),
// For IssuableByEmail component
emailsHelpPagePath,
initialEmail,
diff --git a/app/assets/javascripts/issues_list/utils.js b/app/assets/javascripts/issues_list/utils.js
index 234fd59ca8d..b5ec44198da 100644
--- a/app/assets/javascripts/issues_list/utils.js
+++ b/app/assets/javascripts/issues_list/utils.js
@@ -16,6 +16,7 @@ import {
RELATIVE_POSITION_DESC,
SPECIAL_FILTER,
SPECIAL_FILTER_VALUES,
+ TOKEN_TYPE_ASSIGNEE,
UPDATED_ASC,
UPDATED_DESC,
urlSortParams,
@@ -173,7 +174,7 @@ export const getFilterTokens = (locationSearch) => {
const getFilterType = (data, tokenType = '') =>
SPECIAL_FILTER_VALUES.includes(data) ||
- (tokenType === 'assignee_username' && isPositiveInteger(data))
+ (tokenType === TOKEN_TYPE_ASSIGNEE && isPositiveInteger(data))
? SPECIAL_FILTER
: NORMAL_FILTER;
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/author_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/author_token.vue
index aeb698a3adb..3bea89c8670 100644
--- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/author_token.vue
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/author_token.vue
@@ -39,6 +39,14 @@ export default {
};
},
computed: {
+ currentUser() {
+ return {
+ id: gon.current_user_id,
+ name: gon.current_user_fullname,
+ username: gon.current_username,
+ avatar_url: gon.current_user_avatar_url,
+ };
+ },
currentValue() {
return this.value.data.toLowerCase();
},
@@ -113,7 +121,18 @@ export default {
{{ author.text }}
</gl-filtered-search-suggestion>
<gl-dropdown-divider v-if="defaultAuthors.length" />
- <gl-loading-icon v-if="loading" />
+ <template v-if="loading">
+ <gl-filtered-search-suggestion v-if="currentUser.id" :value="currentUser.username">
+ <div class="gl-display-flex">
+ <gl-avatar :size="32" :src="avatarUrl(currentUser)" />
+ <div>
+ <div>{{ currentUser.name }}</div>
+ <div>@{{ currentUser.username }}</div>
+ </div>
+ </div>
+ </gl-filtered-search-suggestion>
+ <gl-loading-icon class="gl-mt-3" />
+ </template>
<template v-else>
<gl-filtered-search-suggestion
v-for="author in authors"
diff --git a/app/assets/javascripts/vue_shared/components/issue/issue_assignees.vue b/app/assets/javascripts/vue_shared/components/issue/issue_assignees.vue
index 3006ba83f98..b2f077f5329 100644
--- a/app/assets/javascripts/vue_shared/components/issue/issue_assignees.vue
+++ b/app/assets/javascripts/vue_shared/components/issue/issue_assignees.vue
@@ -60,7 +60,7 @@ export default {
},
methods: {
avatarUrlTitle(assignee) {
- return sprintf(__('Avatar for %{assigneeName}'), {
+ return sprintf(__('Assigned to %{assigneeName}'), {
assigneeName: assignee.name,
});
},
diff --git a/app/controllers/concerns/membership_actions.rb b/app/controllers/concerns/membership_actions.rb
index 20861afbb88..a4b64a7848c 100644
--- a/app/controllers/concerns/membership_actions.rb
+++ b/app/controllers/concerns/membership_actions.rb
@@ -108,7 +108,7 @@ module MembershipActions
respond_to do |format|
format.html do
- redirect_path = member.request? ? member.source : [:dashboard, membershipable.class.to_s.tableize]
+ redirect_path = member.request? ? member.source : [:dashboard, membershipable.class.to_s.tableize.to_sym]
redirect_to redirect_path, notice: notice
end
diff --git a/app/graphql/mutations/commits/create.rb b/app/graphql/mutations/commits/create.rb
index 2e06e1ea0c4..f432f679909 100644
--- a/app/graphql/mutations/commits/create.rb
+++ b/app/graphql/mutations/commits/create.rb
@@ -44,6 +44,11 @@ module Mutations
null: true,
description: 'The commit after mutation.'
+ field :content,
+ [GraphQL::STRING_TYPE],
+ null: true,
+ description: 'Contents of the commit.'
+
authorize :push_code
def resolve(project_path:, branch:, message:, actions:, **args)
@@ -59,6 +64,7 @@ module Mutations
result = ::Files::MultiService.new(project, current_user, attributes).execute
{
+ content: actions.pluck(:content), # rubocop:disable CodeReuse/ActiveRecord because actions is an Array, not a Relation
commit: (project.repository.commit(result[:result]) if result[:status] == :success),
commit_pipeline_path: UrlHelpers.new.graphql_etag_pipeline_sha_path(result[:result]),
errors: Array.wrap(result[:message])
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index 80b8b5922e6..8331ba15cd5 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -192,7 +192,7 @@ module IssuesHelper
empty_state_svg_path: image_path('illustrations/issues.svg'),
endpoint: expose_path(api_v4_projects_issues_path(id: project.id)),
export_csv_path: export_csv_project_issues_path(project),
- has_issues: project_issues(project).exists?.to_s,
+ has_project_issues: project_issues(project).exists?.to_s,
import_csv_issues_path: import_csv_namespace_project_issues_path,
initial_email: project.new_issuable_address(current_user, 'issue'),
is_signed_in: current_user.present?.to_s,
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index a26f28c3006..3c78b9f53d5 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -469,7 +469,13 @@ module Ci
end
def retryable?
- !archived? && (success? || failed? || canceled?)
+ if Feature.enabled?(:prevent_retry_of_retried_jobs, project, default_enabled: :yaml)
+ return false if retried? || archived?
+
+ success? || failed? || canceled?
+ else
+ !archived? && (success? || failed? || canceled?)
+ end
end
def retries_count
diff --git a/app/views/import/_githubish_status.html.haml b/app/views/import/_githubish_status.html.haml
index 4cf08b1d2be..221529a048b 100644
--- a/app/views/import/_githubish_status.html.haml
+++ b/app/views/import/_githubish_status.html.haml
@@ -1,5 +1,5 @@
- add_page_specific_style 'page_bundles/import'
-- provider = local_assigns.fetch(:provider)
+- provider = local_assigns.fetch(:provider).to_sym
- extra_data = local_assigns.fetch(:extra_data, {})
- filterable = local_assigns.fetch(:filterable, true)
- paginatable = local_assigns.fetch(:paginatable, false)
diff --git a/app/views/shared/milestones/_issuable.html.haml b/app/views/shared/milestones/_issuable.html.haml
index a62ed009552..184904dd7ab 100644
--- a/app/views/shared/milestones/_issuable.html.haml
+++ b/app/views/shared/milestones/_issuable.html.haml
@@ -3,7 +3,7 @@
- labels = issuable.labels
- assignees = issuable.assignees
- base_url_args = [project]
-- issuable_type_args = base_url_args + [issuable.class.table_name]
+- issuable_type_args = base_url_args + [issuable.class.table_name.to_sym]
- issuable_url_args = base_url_args + [issuable]
%li.issuable-row
diff --git a/app/workers/build_hooks_worker.rb b/app/workers/build_hooks_worker.rb
index c3ba3e5b715..a0d1d9dca45 100644
--- a/app/workers/build_hooks_worker.rb
+++ b/app/workers/build_hooks_worker.rb
@@ -11,16 +11,6 @@ class BuildHooksWorker # rubocop:disable Scalability/IdempotentWorker
urgency :high
data_consistency :delayed
- DATA_CONSISTENCY_DELAY = 3
-
- def self.perform_async(*args)
- if Feature.enabled?(:delayed_perform_for_build_hooks_worker, default_enabled: :yaml)
- perform_in(DATA_CONSISTENCY_DELAY.seconds, *args)
- else
- super
- end
- end
-
# rubocop: disable CodeReuse/ActiveRecord
def perform(build_id)
Ci::Build.includes({ runner: :tags })
diff --git a/config/feature_flags/development/delayed_perform_for_build_hooks_worker.yml b/config/feature_flags/development/prevent_retry_of_retried_jobs.yml
index 45fcf7d21bd..0179f84f92a 100644
--- a/config/feature_flags/development/delayed_perform_for_build_hooks_worker.yml
+++ b/config/feature_flags/development/prevent_retry_of_retried_jobs.yml
@@ -1,8 +1,8 @@
---
-name: delayed_perform_for_build_hooks_worker
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61149
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/330132
-milestone: '13.12'
+name: prevent_retry_of_retried_jobs
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62349
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/331695
+milestone: '14.0'
type: development
-group: group::memory
+group: group::continuous integration
default_enabled: false
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 81eb9878376..57694da4dfa 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -1089,8 +1089,6 @@ production: &base
# Gitaly settings
gitaly:
- # Path to the directory containing Gitaly client executables.
- client_path: /home/git/gitaly
# Default Gitaly authentication token. Can be overridden per storage. Can
# be left blank when Gitaly is running locally on a Unix socket, which
# is the normal way to deploy Gitaly.
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index d238db2e4bd..6f9adb3d427 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -882,6 +882,7 @@ Input type: `CommitCreateInput`
| <a id="mutationcommitcreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationcommitcreatecommit"></a>`commit` | [`Commit`](#commit) | The commit after mutation. |
| <a id="mutationcommitcreatecommitpipelinepath"></a>`commitPipelinePath` | [`String`](#string) | ETag path for the commit's pipeline. |
+| <a id="mutationcommitcreatecontent"></a>`content` | [`[String!]`](#string) | Contents of the commit. |
| <a id="mutationcommitcreateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
### `Mutation.configureSast`
diff --git a/doc/development/database/strings_and_the_text_data_type.md b/doc/development/database/strings_and_the_text_data_type.md
index 6fbb95c4f60..688d811b897 100644
--- a/doc/development/database/strings_and_the_text_data_type.md
+++ b/doc/development/database/strings_and_the_text_data_type.md
@@ -34,11 +34,9 @@ but only for updating the declaration of the columns. We can then validate it at
`VALIDATE CONSTRAINT`, which requires only a `SHARE UPDATE EXCLUSIVE LOCK` (only conflicts with other
validations and index creation while it allows reads and writes).
-### Exceptions
-
-Text columns used by `attr_encrypted` are not required to have a limit, because the length of the
-text after encryption may be longer than the text itself. Instead, you can use an Active Record
-length validation on the attribute.
+NOTE:
+Don't use text columns for `attr_encrypted` attributes. Use a
+[`:binary` column](../migration_style_guide.md#encrypted-attributes) instead.
## Create a new table with text columns
diff --git a/doc/development/graphql_guide/pagination.md b/doc/development/graphql_guide/pagination.md
index 5db9238faed..99b27280670 100644
--- a/doc/development/graphql_guide/pagination.md
+++ b/doc/development/graphql_guide/pagination.md
@@ -223,6 +223,26 @@ the `order_due_date_and_labels_priority` method creates a very complex query.
These types of queries are not supported. In these instances, you can use offset pagination.
+#### Gotchas
+
+Do not define a collection's order using the string syntax:
+
+```ruby
+# Bad
+items.order('created_at DESC')
+```
+
+Instead, use the hash syntax:
+
+```ruby
+# Good
+items.order(created_at: :desc)
+```
+
+The first example won't correctly embed the sort information (`created_at`, in
+the example above) into the pagination cursors, which will result in an
+incorrect sort order.
+
### Offset pagination
There are times when the [complexity of sorting](#limitations-of-query-complexity)
diff --git a/doc/development/jh_features_review.md b/doc/development/jh_features_review.md
index 260da2d7ef2..b139a380344 100644
--- a/doc/development/jh_features_review.md
+++ b/doc/development/jh_features_review.md
@@ -23,6 +23,12 @@ We have two kinds of changes related to JH:
If needed, review the corresponding JH merge request located at [JH repository](https://gitlab.com/gitlab-jh/gitlab)
+## Process overview
+
+See the [merge request process](https://about.gitlab.com/handbook/ceo/chief-of-staff-team/jihu-support/#merge-request-process)
+on the JiHu Support handbook.
+This page is the single source of truth for JiHu-related processes.
+
## Act as EE when `jh/` does not exist
- In the case of EE repository, `jh/` does not exist so it should just act like EE (or CE when the license is absent)
diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md
index e1444f1a726..62a81e21da6 100644
--- a/doc/development/migration_style_guide.md
+++ b/doc/development/migration_style_guide.md
@@ -856,6 +856,37 @@ class BuildMetadata
end
```
+## Encrypted attributes
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/227779) in GitLab 14.0.
+
+Do not store `attr_encrypted` attributes as `:text` in the database; use
+`:binary` instead. This uses the `bytea` type in PostgreSQL and makes storage more
+efficient:
+
+```ruby
+class AddSecretToSomething < ActiveRecord::Migration[5.0]
+ def change
+ add_column :something, :encrypted_secret, :binary
+ add_column :something, :encrypted_secret_iv, :binary
+ end
+end
+```
+
+When storing encrypted attributes in a binary column, we need to provide the
+`encode: false` and `encode_iv: false` options to `attr_encrypted`:
+
+```ruby
+class Something < ApplicationRecord
+ attr_encrypted :secret,
+ mode: :per_attribute_iv,
+ key: Settings.attr_encrypted_db_key_base_32,
+ algorithm: 'aes-256-gcm',
+ encode: false,
+ encode_iv: false
+end
+```
+
## Testing
See the [Testing Rails migrations](testing_guide/testing_migrations_guide.md) style guide.
diff --git a/doc/development/stage_group_dashboards.md b/doc/development/stage_group_dashboards.md
index 44c738092ac..af729add1df 100644
--- a/doc/development/stage_group_dashboards.md
+++ b/doc/development/stage_group_dashboards.md
@@ -52,6 +52,26 @@ component has 2 indicators:
1. [Apdex](https://en.wikipedia.org/wiki/Apdex): The rate of
operations that performed adequately.
+
+ The threshold for 'performed adequately' is stored in our [metrics
+ catalog](https://gitlab.com/gitlab-com/runbooks/-/tree/master/metrics-catalog)
+ and depends on the service in question. For the Puma (Rails)
+ component of the
+ [API](https://gitlab.com/gitlab-com/runbooks/-/blob/f22f40b2c2eab37d85e23ccac45e658b2c914445/metrics-catalog/services/api.jsonnet#L127),
+ [Git](https://gitlab.com/gitlab-com/runbooks/-/blob/f22f40b2c2eab37d85e23ccac45e658b2c914445/metrics-catalog/services/git.jsonnet#L216),
+ and
+ [Web](https://gitlab.com/gitlab-com/runbooks/-/blob/f22f40b2c2eab37d85e23ccac45e658b2c914445/metrics-catalog/services/web.jsonnet#L154)
+ services, that threshold is **1 second**.
+
+ For Sidekiq job execution, the threshold depends on the [job
+ urgency](sidekiq_style_guide.md#job-urgency). It is
+ [currently](https://gitlab.com/gitlab-com/runbooks/-/blob/f22f40b2c2eab37d85e23ccac45e658b2c914445/metrics-catalog/services/lib/sidekiq-helpers.libsonnet#L25-38)
+ **10 seconds** for high-urgency jobs and **5 minutes** for other
+ jobs.
+
+ Some stage groups may have more services than these, and the
+ thresholds for those will be in the metrics catalog as well.
+
1. Error rate: The rate of operations that had errors.
The calculation to a ratio then happens as follows:
diff --git a/doc/user/markdown.md b/doc/user/markdown.md
index cbd5bf1553a..292403e0597 100644
--- a/doc/user/markdown.md
+++ b/doc/user/markdown.md
@@ -5,25 +5,36 @@ info: "To determine the technical writer assigned to the Stage/Group associated
type: reference, howto
---
-# GitLab Markdown **(FREE)**
+# GitLab Flavored Markdown **(FREE)**
-This Markdown guide is **valid only for the GitLab internal Markdown rendering system for entries and files**.
-It is **not** valid for the [GitLab documentation website](https://docs.gitlab.com)
-or the [GitLab main website](https://about.gitlab.com), as they both use
-[Kramdown](https://kramdown.gettalong.org) as their Markdown engine. The documentation
-website uses an extended Kramdown gem, [GitLab Kramdown](https://gitlab.com/gitlab-org/gitlab_kramdown).
-Consult the [GitLab Kramdown Guide](https://about.gitlab.com/handbook/markdown-guide/)
-for a complete Kramdown reference.
+GitLab automatically renders Markdown content. For example, when you add a comment to an issue,
+you type the text in the Markdown language. When you save the issue, the text is rendered
+with a set of styles. These styles are described on this page.
-NOTE:
-We encourage you to view this document as [rendered by GitLab itself](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/user/markdown.md).
+For example, in Markdown, an ordered list looks like this:
+
+```markdown
+- Cat
+- Dog
+- Turtle
+```
-## GitLab Flavored Markdown
+When this list is rendered, it looks like this:
-GitLab uses "GitLab Flavored Markdown". It extends the [CommonMark specification](https://spec.commonmark.org/current/)
-(which is based on standard Markdown) in several ways to add more features.
+- Cat
+- Dog
+- Turtle
+
+These styles are **valid for GitLab only**. The [GitLab documentation website](https://docs.gitlab.com)
+and the [main GitLab website](https://about.gitlab.com) use [Kramdown](https://kramdown.gettalong.org) instead.
+
+You should not view this page in the documentation, but instead [view these styles as they appear on GitLab](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/user/markdown.md).
+
+GitLab Flavored Markdown extends the [CommonMark specification](https://spec.commonmark.org/current/).
It was inspired by [GitHub Flavored Markdown](https://docs.github.com/en/github/writing-on-github/basic-writing-and-formatting-syntax).
+## Where you can use GitLab Flavored Markdown
+
You can use GitLab Flavored Markdown in the following areas:
- Comments
@@ -33,22 +44,21 @@ You can use GitLab Flavored Markdown in the following areas:
- Snippets (the snippet must be named with a `.md` extension)
- Wiki pages
- Markdown documents inside repositories
-- Epics **(ULTIMATE)**
+- Epics
You can also use other rich text files in GitLab. You might have to install a dependency
-to do so. Please see the [`gitlab-markup` gem project](https://gitlab.com/gitlab-org/gitlab-markup)
-for more information.
+to do so. For more information, see the [`gitlab-markup` gem project](https://gitlab.com/gitlab-org/gitlab-markup).
### Transition from Redcarpet to CommonMark
-- In GitLab version 11.8, the [Redcarpet Ruby library](https://github.com/vmg/redcarpet)
- was removed. All issues and comments, including those from pre-11.1, are now processed
- using the [CommonMark Ruby Library](https://github.com/gjtorikian/commonmarker).
-- GitLab versions 11.3 and greater use CommonMark to process wiki pages and Markdown
+- In GitLab 11.8, the [Redcarpet Ruby library](https://github.com/vmg/redcarpet)
+ was removed. All issues and comments, including those in 11.1 and earlier, are now processed
+ by using the [CommonMark Ruby Library](https://github.com/gjtorikian/commonmarker).
+- In GitLab 11.3 and later, CommonMark processes wiki pages and Markdown
files (`*.md`) in repositories.
-- GitLab versions 11.1 and greater use the [CommonMark Ruby Library](https://github.com/gjtorikian/commonmarker)
- for Markdown processing of all new issues, merge requests, comments, and other Markdown
- content in the GitLab system.
+- In GitLab 11.1 and later, the [CommonMark Ruby Library](https://github.com/gjtorikian/commonmarker)
+ for Markdown processes all new issues, merge requests, comments, and other Markdown
+ content.
The documentation website migrated its Markdown engine
[from Redcarpet to Kramdown](https://gitlab.com/gitlab-org/gitlab-docs/-/merge_requests/108)
@@ -68,7 +78,7 @@ render incorrectly:
- milk
```
-To correct their rendering, add a space to each nested item to align the `-` with the first
+To fix this issue, add a space to each nested item. The `-` must be aligned with the first
character of the top list item (`C` in this case):
```markdown
diff --git a/doc/user/packages/composer_repository/index.md b/doc/user/packages/composer_repository/index.md
index bc1e59e4ac2..4d3d3cc24a3 100644
--- a/doc/user/packages/composer_repository/index.md
+++ b/doc/user/packages/composer_repository/index.md
@@ -120,7 +120,7 @@ You can publish a Composer package to the Package Registry as part of your CI/CD
deploy:
stage: deploy
script:
- - 'curl --header "Job-Token: $CI_JOB_TOKEN" --data tag=<tag> "https://gitlab.example.com/api/v4/projects/$CI_PROJECT_ID/packages/composer"'
+ - 'curl --header "Job-Token: $CI_JOB_TOKEN" --data tag=<tag> "${CI_API_V4_URL}/projects/$CI_PROJECT_ID/packages/composer"'
```
1. Run the pipeline.
diff --git a/doc/user/packages/conan_repository/index.md b/doc/user/packages/conan_repository/index.md
index 53d191cbcfe..5643882b23c 100644
--- a/doc/user/packages/conan_repository/index.md
+++ b/doc/user/packages/conan_repository/index.md
@@ -281,7 +281,7 @@ image: conanio/gcc7
create_package:
stage: deploy
script:
- - conan remote add gitlab https://gitlab.example.com/api/v4/packages/conan
+ - ${CI_API_V4_URL}/projects/$CI_PROJECT_ID/packages/conan
- conan new <package-name>/0.1 -t
- conan create . <group-name>+<project-name>/stable
- CONAN_LOGIN_USERNAME=ci_user CONAN_PASSWORD=${CI_JOB_TOKEN} conan upload <package-name>/0.1@<group-name>+<project-name>/stable --all --remote=gitlab
diff --git a/doc/user/packages/maven_repository/index.md b/doc/user/packages/maven_repository/index.md
index ba7b55dc47d..1f2ef514536 100644
--- a/doc/user/packages/maven_repository/index.md
+++ b/doc/user/packages/maven_repository/index.md
@@ -341,7 +341,7 @@ file:
```groovy
repositories {
maven {
- url "https://gitlab.example.com/api/v4/groups/<group>/-/packages/maven"
+ url "${CI_API_V4_URL}/groups/<group>/-/packages/maven"
name "GitLab"
credentials(HttpHeaderCredentials) {
name = 'Job-Token'
@@ -742,17 +742,17 @@ You can create a new package each time the `master` branch is updated.
<repositories>
<repository>
<id>gitlab-maven</id>
- <url>${env.CI_SERVER_URL}/api/v4/projects/${env.CI_PROJECT_ID}/packages/maven</url>
+ <url>$env{CI_API_V4_URL}/projects/${env.CI_PROJECT_ID}/packages/maven</url>
</repository>
</repositories>
<distributionManagement>
<repository>
<id>gitlab-maven</id>
- <url>${env.CI_SERVER_URL}/api/v4/projects/${env.CI_PROJECT_ID}/packages/maven</url>
+ <url>${CI_API_V4_URL}/projects/${env.CI_PROJECT_ID}/packages/maven</url>
</repository>
<snapshotRepository>
<id>gitlab-maven</id>
- <url>${env.CI_SERVER_URL}/api/v4/projects/${env.CI_PROJECT_ID}/packages/maven</url>
+ <url>${CI_API_V4_URL}/projects/${env.CI_PROJECT_ID}/packages/maven</url>
</snapshotRepository>
</distributionManagement>
```
diff --git a/doc/user/packages/npm_registry/index.md b/doc/user/packages/npm_registry/index.md
index ace432b305f..54a899bb949 100644
--- a/doc/user/packages/npm_registry/index.md
+++ b/doc/user/packages/npm_registry/index.md
@@ -300,7 +300,7 @@ stages:
deploy:
stage: deploy
script:
- - echo "//gitlab.example.com/api/v4/projects/${CI_PROJECT_ID}/packages/npm/:_authToken=${CI_JOB_TOKEN}">.npmrc
+ - echo "//${CI_SERVER_HOST}/api/v4/projects/${CI_PROJECT_ID}/packages/npm/:_authToken=${CI_JOB_TOKEN}">.npmrc
- npm publish
```
diff --git a/doc/user/packages/nuget_repository/index.md b/doc/user/packages/nuget_repository/index.md
index 7e59b19076a..6d206739852 100644
--- a/doc/user/packages/nuget_repository/index.md
+++ b/doc/user/packages/nuget_repository/index.md
@@ -336,7 +336,7 @@ updated:
stage: deploy
script:
- dotnet pack -c Release
- - dotnet nuget add source "$CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/packages/nuget/index.json" --name gitlab --username gitlab-ci-token --password $CI_JOB_TOKEN --store-password-in-clear-text
+ - dotnet nuget add source "${CI_API_V4_URL}/$CI_PROJECT_ID/packages/nuget/index.json" --name gitlab --username gitlab-ci-token --password $CI_JOB_TOKEN --store-password-in-clear-text
- dotnet nuget push "bin/Release/*.nupkg" --source gitlab
only:
- master
diff --git a/doc/user/packages/pypi_repository/index.md b/doc/user/packages/pypi_repository/index.md
index 17b51e313fa..1708bfdf2e5 100644
--- a/doc/user/packages/pypi_repository/index.md
+++ b/doc/user/packages/pypi_repository/index.md
@@ -216,7 +216,7 @@ run:
script:
- pip install twine
- python setup.py sdist bdist_wheel
- - TWINE_PASSWORD=${CI_JOB_TOKEN} TWINE_USERNAME=gitlab-ci-token python -m twine upload --repository-url https://gitlab.example.com/api/v4/projects/${CI_PROJECT_ID}/packages/pypi dist/*
+ - TWINE_PASSWORD=${CI_JOB_TOKEN} TWINE_USERNAME=gitlab-ci-token python -m twine upload --repository-url ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/pypi dist/*
```
You can also use `CI_JOB_TOKEN` in a `~/.pypirc` file that you check in to
diff --git a/doc/user/packages/rubygems_registry/index.md b/doc/user/packages/rubygems_registry/index.md
index e4d297ac1d8..743bc229e11 100644
--- a/doc/user/packages/rubygems_registry/index.md
+++ b/doc/user/packages/rubygems_registry/index.md
@@ -88,11 +88,11 @@ run:
- mkdir ~/.gem
- echo "---" > ~/.gem/credentials
- |
- echo "https://gitlab.example.com/api/v4/projects/${CI_PROJECT_ID}/packages/rubygems: '${CI_JOB_TOKEN}'" >> ~/.gem/credentials
+ echo "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/rubygems: '${CI_JOB_TOKEN}'" >> ~/.gem/credentials
- chmod 0600 ~/.gem/credentials # rubygems requires 0600 permissions on the credentials file
script:
- gem build my_gem
- - gem push my_gem-0.0.1.gem --host https://gitlab.example.com/api/v4/projects/${CI_PROJECT_ID}/packages/rubygems
+ - gem push my_gem-0.0.1.gem --host ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/rubygems
```
You can also use `CI_JOB_TOKEN` in a `~/.gem/credentials` file that you check in to
diff --git a/doc/user/project/clusters/add_eks_clusters.md b/doc/user/project/clusters/add_eks_clusters.md
index c0fb8f5848f..eb43c85ef8a 100644
--- a/doc/user/project/clusters/add_eks_clusters.md
+++ b/doc/user/project/clusters/add_eks_clusters.md
@@ -77,7 +77,7 @@ To create and add a new Kubernetes cluster to your project, group, or instance:
- Project's **Operations > Kubernetes** page, for a project-level cluster.
- Group's **Kubernetes** page, for a group-level cluster.
- **Admin Area > Kubernetes**, for an instance-level cluster.
-1. Click **Add Kubernetes cluster**.
+1. Click **Integrate with a cluster certificate**.
1. Under the **Create new cluster** tab, click **Amazon EKS** to display an
`Account ID` and `External ID` needed for later steps.
1. In the [IAM Management Console](https://console.aws.amazon.com/iam/home), create an IAM policy:
@@ -188,9 +188,7 @@ After about 10 minutes, your cluster is ready to go. You can now proceed
to install some [pre-defined applications](index.md#installing-applications).
NOTE:
-You must add your AWS external ID to the
-[IAM Role in the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-role.html#cli-configure-role-xaccount)
-to manage your cluster using `kubectl`.
+If you have [installed and configured](https://docs.aws.amazon.com/eks/latest/userguide/getting-started.html#get-started-kubectl) `kubectl` and you would like to manage your cluster with it, you must add your AWS external ID in the AWS configuration. For more information on how to configure AWS CLI, see [using an IAM role in the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-role.html#cli-configure-role-xaccount).
### Cluster creation flow
diff --git a/doc/user/project/clusters/add_gke_clusters.md b/doc/user/project/clusters/add_gke_clusters.md
index af3a17dc60c..8a41a4d18e5 100644
--- a/doc/user/project/clusters/add_gke_clusters.md
+++ b/doc/user/project/clusters/add_gke_clusters.md
@@ -49,7 +49,7 @@ To create and add a new Kubernetes cluster to your project, group, or instance:
- Project's **{cloud-gear}** **Operations > Kubernetes** page, for a project-level cluster.
- Group's **{cloud-gear}** **Kubernetes** page, for a group-level cluster.
- **Admin Area >** **{cloud-gear}** **Kubernetes** page, for an instance-level cluster.
-1. Click **Add Kubernetes cluster**.
+1. Click **Integrate with a cluster certificate**.
1. Under the **Create new cluster** tab, click **Google GKE**.
1. Connect your Google account if you haven't done already by clicking the
**Sign in with Google** button.
diff --git a/doc/user/project/clusters/add_remove_clusters.md b/doc/user/project/clusters/add_remove_clusters.md
index 1b4b4f38f4b..f9c563a38b9 100644
--- a/doc/user/project/clusters/add_remove_clusters.md
+++ b/doc/user/project/clusters/add_remove_clusters.md
@@ -147,7 +147,7 @@ Amazon Elastic Kubernetes Service (EKS) at the project, group, or instance level
- Project's **{cloud-gear}** **Operations > Kubernetes** page, for a project-level cluster.
- Group's **{cloud-gear}** **Kubernetes** page, for a group-level cluster.
- **Admin Area >** **{cloud-gear}** **Kubernetes** page, for an instance-level cluster.
-1. Click **Add Kubernetes cluster**.
+1. Click **Integrate with a cluster certificate**.
1. Click the **Create new cluster** tab.
1. Click either **Amazon EKS** or **Google GKE**, and follow the instructions for your desired service:
- [Amazon EKS](add_eks_clusters.md#new-eks-cluster).
diff --git a/doc/user/project/issues/managing_issues.md b/doc/user/project/issues/managing_issues.md
index 09c7762d3eb..869f6678640 100644
--- a/doc/user/project/issues/managing_issues.md
+++ b/doc/user/project/issues/managing_issues.md
@@ -315,10 +315,15 @@ issues are still displayed, but are not closed automatically.
![disable issue auto close - settings](img/disable_issue_auto_close.png)
+The automatic issue closing is also disabled in a project if the project has the issue tracker
+disabled. If you want to enable automatic issue closing, make sure to
+[enable GitLab Issues](../settings/index.md#sharing-and-permissions).
+
This only applies to issues affected by new merge requests or commits. Already
-closed issues remain as-is. Disabling automatic issue closing only affects merge
-requests *in* the project and does not prevent other projects from closing it
-via cross-project issues.
+closed issues remain as-is.
+If issue tracking is enabled, disabling automatic issue closing only applies to merge requests
+attempting to automatically close issues within the same project.
+Merge requests in other projects can still close another project's issues.
#### Customizing the issue closing pattern **(FREE SELF)**
diff --git a/lib/gitlab/database/background_migration/batched_migration.rb b/lib/gitlab/database/background_migration/batched_migration.rb
index bf666a24a5b..53a5089a877 100644
--- a/lib/gitlab/database/background_migration/batched_migration.rb
+++ b/lib/gitlab/database/background_migration/batched_migration.rb
@@ -16,6 +16,10 @@ module Gitlab
scope :queue_order, -> { order(id: :asc) }
scope :queued, -> { where(status: [:active, :paused]) }
+ scope :for_configuration, ->(job_class_name, table_name, column_name, job_arguments) do
+ where(job_class_name: job_class_name, table_name: table_name, column_name: column_name)
+ .where("job_arguments = ?", job_arguments.to_json) # rubocop:disable Rails/WhereEquals
+ end
enum status: {
paused: 0,
diff --git a/lib/gitlab/database/migrations/background_migration_helpers.rb b/lib/gitlab/database/migrations/background_migration_helpers.rb
index 8d5ea652bfc..f56d375bb9e 100644
--- a/lib/gitlab/database/migrations/background_migration_helpers.rb
+++ b/lib/gitlab/database/migrations/background_migration_helpers.rb
@@ -137,6 +137,9 @@ module Gitlab
# class must be present in the Gitlab::BackgroundMigration module, and the batch class (if specified) must be
# present in the Gitlab::BackgroundMigration::BatchingStrategies module.
#
+ # If migration with same job_class_name, table_name, column_name, and job_aruments already exists, this helper
+ # will log an warning and not create a new one.
+ #
# job_class_name - The background migration job class as a string
# batch_table_name - The name of the table the migration will batch over
# batch_column_name - The name of the column the migration will batch over
@@ -180,6 +183,13 @@ module Gitlab
sub_batch_size: SUB_BATCH_SIZE
)
+ if Gitlab::Database::BackgroundMigration::BatchedMigration.for_configuration(job_class_name, batch_table_name, batch_column_name, job_arguments).exists?
+ Gitlab::AppLogger.warn "Batched background migration not enqueued because it already exists: " \
+ "job_class_name: #{job_class_name}, table_name: #{batch_table_name}, column_name: #{batch_column_name}, " \
+ "job_arguments: #{job_arguments.inspect}"
+ return
+ end
+
job_interval = BATCH_MIN_DELAY if job_interval < BATCH_MIN_DELAY
batch_max_value ||= connection.select_value(<<~SQL)
@@ -194,13 +204,13 @@ module Gitlab
job_class_name: job_class_name,
table_name: batch_table_name,
column_name: batch_column_name,
+ job_arguments: job_arguments,
interval: job_interval,
min_value: batch_min_value,
max_value: batch_max_value,
batch_class_name: batch_class_name,
batch_size: batch_size,
sub_batch_size: sub_batch_size,
- job_arguments: job_arguments,
status: migration_status)
# This guard is necessary since #total_tuple_count was only introduced schema-wise,
diff --git a/lib/gitlab/experimentation.rb b/lib/gitlab/experimentation.rb
index 87fe7bd857a..be96de85b87 100644
--- a/lib/gitlab/experimentation.rb
+++ b/lib/gitlab/experimentation.rb
@@ -59,10 +59,12 @@ module Gitlab
tracking_category: 'Growth::Conversion::Experiment::TrialOnboardingIssues'
},
learn_gitlab_a: {
- tracking_category: 'Growth::Conversion::Experiment::LearnGitLabA'
+ tracking_category: 'Growth::Conversion::Experiment::LearnGitLabA',
+ rollout_strategy: :user
},
learn_gitlab_b: {
- tracking_category: 'Growth::Activation::Experiment::LearnGitLabB'
+ tracking_category: 'Growth::Activation::Experiment::LearnGitLabB',
+ rollout_strategy: :user
},
in_product_marketing_emails: {
tracking_category: 'Growth::Activation::Experiment::InProductMarketingEmails'
diff --git a/lib/gitlab/git/remote_repository.rb b/lib/gitlab/git/remote_repository.rb
index 234541d8145..0ea009930b0 100644
--- a/lib/gitlab/git/remote_repository.rb
+++ b/lib/gitlab/git/remote_repository.rb
@@ -53,23 +53,6 @@ module Gitlab
gitaly_repository.relative_path == other_repository.relative_path
end
- def fetch_env
- gitaly_ssh = File.absolute_path(File.join(Gitlab.config.gitaly.client_path, 'gitaly-ssh'))
- gitaly_address = gitaly_client.address(storage)
- gitaly_token = gitaly_client.token(storage)
-
- request = Gitaly::SSHUploadPackRequest.new(repository: gitaly_repository)
- env = {
- 'GITALY_ADDRESS' => gitaly_address,
- 'GITALY_PAYLOAD' => request.to_json,
- 'GITALY_WD' => Dir.pwd,
- 'GIT_SSH_COMMAND' => "#{gitaly_ssh} upload-pack"
- }
- env['GITALY_TOKEN'] = gitaly_token if gitaly_token.present?
-
- env
- end
-
def path
@repository.path
end
diff --git a/lib/gitlab/setup_helper.rb b/lib/gitlab/setup_helper.rb
index 3ac20724403..5f4112ccf63 100644
--- a/lib/gitlab/setup_helper.rb
+++ b/lib/gitlab/setup_helper.rb
@@ -129,7 +129,7 @@ module Gitlab
config[:'gitaly-ruby'] = { dir: File.join(gitaly_dir, 'ruby') } if gitaly_ruby
config[:'gitlab-shell'] = { dir: Gitlab.config.gitlab_shell.path }
- config[:bin_dir] = Gitlab.config.gitaly.client_path
+ config[:bin_dir] = File.join(gitaly_dir, '_build', 'bin') # binaries by default are in `_build/bin`
config[:gitlab] = { url: Gitlab.config.gitlab.url }
config[:logging] = { dir: Rails.root.join('log').to_s }
diff --git a/package.json b/package.json
index 29ae2ec3d11..b4cd5589749 100644
--- a/package.json
+++ b/package.json
@@ -51,7 +51,7 @@
"@babel/preset-env": "^7.10.1",
"@gitlab/at.js": "1.5.7",
"@gitlab/favicon-overlay": "2.0.0",
- "@gitlab/svgs": "1.197.0",
+ "@gitlab/svgs": "1.198.0",
"@gitlab/tributejs": "1.0.0",
"@gitlab/ui": "29.29.2",
"@gitlab/visual-review-tools": "1.6.1",
diff --git a/spec/deprecation_toolkit_env.rb b/spec/deprecation_toolkit_env.rb
index 57d32b5423c..00d66ff3cdc 100644
--- a/spec/deprecation_toolkit_env.rb
+++ b/spec/deprecation_toolkit_env.rb
@@ -55,9 +55,9 @@ module DeprecationToolkitEnv
# one by one
def self.allowed_kwarg_warning_paths
%w[
- activerecord-6.0.3.6/lib/active_record/migration.rb
- activesupport-6.0.3.6/lib/active_support/cache.rb
- activerecord-6.0.3.6/lib/active_record/relation.rb
+ activerecord-6.0.3.7/lib/active_record/migration.rb
+ activesupport-6.0.3.7/lib/active_support/cache.rb
+ activerecord-6.0.3.7/lib/active_record/relation.rb
asciidoctor-2.0.12/lib/asciidoctor/extensions.rb
attr_encrypted-3.1.0/lib/attr_encrypted/adapters/active_record.rb
]
diff --git a/spec/frontend/issues_list/components/issues_list_app_spec.js b/spec/frontend/issues_list/components/issues_list_app_spec.js
index 5d83bf0142f..8a91dca51c2 100644
--- a/spec/frontend/issues_list/components/issues_list_app_spec.js
+++ b/spec/frontend/issues_list/components/issues_list_app_spec.js
@@ -18,6 +18,15 @@ import {
PAGE_SIZE_MANUAL,
PARAM_DUE_DATE,
RELATIVE_POSITION_DESC,
+ TOKEN_TYPE_ASSIGNEE,
+ TOKEN_TYPE_AUTHOR,
+ TOKEN_TYPE_CONFIDENTIAL,
+ TOKEN_TYPE_EPIC,
+ TOKEN_TYPE_ITERATION,
+ TOKEN_TYPE_LABEL,
+ TOKEN_TYPE_MILESTONE,
+ TOKEN_TYPE_MY_REACTION,
+ TOKEN_TYPE_WEIGHT,
urlSortParams,
} from '~/issues_list/constants';
import eventHub from '~/issues_list/eventhub';
@@ -39,8 +48,8 @@ describe('IssuesListApp component', () => {
endpoint: 'api/endpoint',
exportCsvPath: 'export/csv/path',
hasBlockedIssuesFeature: true,
- hasIssues: true,
hasIssueWeightsFeature: true,
+ hasProjectIssues: true,
isSignedIn: false,
issuesPath: 'path/to/issues',
jiraIntegrationPath: 'jira/integration/path',
@@ -320,7 +329,7 @@ describe('IssuesListApp component', () => {
beforeEach(async () => {
global.jsdom.reconfigure({ url: `${TEST_HOST}?search=no+results` });
- wrapper = mountComponent({ provide: { hasIssues: true }, mountFn: mount });
+ wrapper = mountComponent({ provide: { hasProjectIssues: true }, mountFn: mount });
await waitForPromises();
});
@@ -336,7 +345,7 @@ describe('IssuesListApp component', () => {
describe('when "Open" tab has no issues', () => {
beforeEach(async () => {
- wrapper = mountComponent({ provide: { hasIssues: true }, mountFn: mount });
+ wrapper = mountComponent({ provide: { hasProjectIssues: true }, mountFn: mount });
await waitForPromises();
});
@@ -356,7 +365,7 @@ describe('IssuesListApp component', () => {
url: setUrlParams({ state: IssuableStates.Closed }, TEST_HOST),
});
- wrapper = mountComponent({ provide: { hasIssues: true }, mountFn: mount });
+ wrapper = mountComponent({ provide: { hasProjectIssues: true }, mountFn: mount });
await waitForPromises();
});
@@ -374,7 +383,7 @@ describe('IssuesListApp component', () => {
describe('when user is logged in', () => {
beforeEach(() => {
wrapper = mountComponent({
- provide: { hasIssues: false, isSignedIn: true },
+ provide: { hasProjectIssues: false, isSignedIn: true },
mountFn: mount,
});
});
@@ -413,7 +422,7 @@ describe('IssuesListApp component', () => {
describe('when user is logged out', () => {
beforeEach(() => {
wrapper = mountComponent({
- provide: { hasIssues: false, isSignedIn: false },
+ provide: { hasProjectIssues: false, isSignedIn: false },
});
});
@@ -430,6 +439,100 @@ describe('IssuesListApp component', () => {
});
});
+ describe('tokens', () => {
+ describe('when user is signed out', () => {
+ beforeEach(() => {
+ wrapper = mountComponent({
+ provide: {
+ isSignedIn: false,
+ },
+ });
+ });
+
+ it('does not render My-Reaction or Confidential tokens', () => {
+ expect(findIssuableList().props('searchTokens')).not.toMatchObject([
+ { type: TOKEN_TYPE_MY_REACTION },
+ { type: TOKEN_TYPE_CONFIDENTIAL },
+ ]);
+ });
+ });
+
+ describe('when iterations are not available', () => {
+ beforeEach(() => {
+ wrapper = mountComponent({
+ provide: {
+ projectIterationsPath: '',
+ },
+ });
+ });
+
+ it('does not render Iteration token', () => {
+ expect(findIssuableList().props('searchTokens')).not.toMatchObject([
+ { type: TOKEN_TYPE_ITERATION },
+ ]);
+ });
+ });
+
+ describe('when epics are not available', () => {
+ beforeEach(() => {
+ wrapper = mountComponent({
+ provide: {
+ groupEpicsPath: '',
+ },
+ });
+ });
+
+ it('does not render Epic token', () => {
+ expect(findIssuableList().props('searchTokens')).not.toMatchObject([
+ { type: TOKEN_TYPE_EPIC },
+ ]);
+ });
+ });
+
+ describe('when weights are not available', () => {
+ beforeEach(() => {
+ wrapper = mountComponent({
+ provide: {
+ groupEpicsPath: '',
+ },
+ });
+ });
+
+ it('does not render Weight token', () => {
+ expect(findIssuableList().props('searchTokens')).not.toMatchObject([
+ { type: TOKEN_TYPE_WEIGHT },
+ ]);
+ });
+ });
+
+ describe('when all tokens are available', () => {
+ beforeEach(() => {
+ wrapper = mountComponent({
+ provide: {
+ isSignedIn: true,
+ projectIterationsPath: 'project/iterations/path',
+ groupEpicsPath: 'group/epics/path',
+ hasIssueWeightsFeature: true,
+ },
+ });
+ });
+
+ it('renders all tokens', () => {
+ expect(findIssuableList().props('searchTokens')).toMatchObject([
+ { type: TOKEN_TYPE_AUTHOR },
+ { type: TOKEN_TYPE_ASSIGNEE },
+ { type: TOKEN_TYPE_MILESTONE },
+ { type: TOKEN_TYPE_LABEL },
+ { type: TOKEN_TYPE_MY_REACTION },
+ { type: TOKEN_TYPE_CONFIDENTIAL },
+ { type: TOKEN_TYPE_ITERATION },
+ { type: TOKEN_TYPE_EPIC },
+ { type: TOKEN_TYPE_WEIGHT },
+ ]);
+ });
+ });
+ });
+
describe('events', () => {
describe('when "click-tab" event is emitted by IssuableList', () => {
beforeEach(() => {
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js
index 3b50927dcc6..e2f8dbd8ceb 100644
--- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js
@@ -3,6 +3,7 @@ import {
GlFilteredSearchTokenSegment,
GlFilteredSearchSuggestion,
GlDropdownDivider,
+ GlLoadingIcon,
} from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
@@ -35,6 +36,7 @@ function createComponent(options = {}) {
value = { data: '' },
active = false,
stubs = defaultStubs,
+ data = {},
} = options;
return mount(AuthorToken, {
propsData: {
@@ -47,20 +49,32 @@ function createComponent(options = {}) {
alignSuggestions: function fakeAlignSuggestions() {},
suggestionsListClass: 'custom-class',
},
+ data() {
+ return { ...data };
+ },
stubs,
});
}
describe('AuthorToken', () => {
+ const originalGon = window.gon;
+ const currentUserLength = 1;
let mock;
let wrapper;
beforeEach(() => {
+ window.gon = {
+ ...originalGon,
+ current_user_id: 13,
+ current_user_fullname: 'Administrator',
+ current_username: 'root',
+ current_user_avatar_url: 'avatar/url',
+ };
mock = new MockAdapter(axios);
- wrapper = createComponent();
});
afterEach(() => {
+ window.gon = originalGon;
mock.restore();
wrapper.destroy();
});
@@ -91,6 +105,8 @@ describe('AuthorToken', () => {
describe('fetchAuthorBySearchTerm', () => {
it('calls `config.fetchAuthors` with provided searchTerm param', () => {
+ wrapper = createComponent();
+
jest.spyOn(wrapper.vm.config, 'fetchAuthors');
wrapper.vm.fetchAuthorBySearchTerm(mockAuthors[0].username);
@@ -102,6 +118,8 @@ describe('AuthorToken', () => {
});
it('sets response to `authors` when request is succesful', () => {
+ wrapper = createComponent();
+
jest.spyOn(wrapper.vm.config, 'fetchAuthors').mockResolvedValue(mockAuthors);
wrapper.vm.fetchAuthorBySearchTerm('root');
@@ -112,6 +130,8 @@ describe('AuthorToken', () => {
});
it('calls `createFlash` with flash error message when request fails', () => {
+ wrapper = createComponent();
+
jest.spyOn(wrapper.vm.config, 'fetchAuthors').mockRejectedValue({});
wrapper.vm.fetchAuthorBySearchTerm('root');
@@ -122,6 +142,8 @@ describe('AuthorToken', () => {
});
it('sets `loading` to false when request completes', () => {
+ wrapper = createComponent();
+
jest.spyOn(wrapper.vm.config, 'fetchAuthors').mockRejectedValue({});
wrapper.vm.fetchAuthorBySearchTerm('root');
@@ -133,21 +155,16 @@ describe('AuthorToken', () => {
});
describe('template', () => {
- beforeEach(() => {
- wrapper.setData({
- authors: mockAuthors,
- });
-
- return wrapper.vm.$nextTick();
- });
-
it('renders gl-filtered-search-token component', () => {
+ wrapper = createComponent({ data: { authors: mockAuthors } });
+
expect(wrapper.find(GlFilteredSearchToken).exists()).toBe(true);
});
it('renders token item when value is selected', () => {
- wrapper.setProps({
+ wrapper = createComponent({
value: { data: mockAuthors[0].username },
+ data: { authors: mockAuthors },
});
return wrapper.vm.$nextTick(() => {
@@ -172,7 +189,7 @@ describe('AuthorToken', () => {
const suggestions = wrapper.findAll(GlFilteredSearchSuggestion);
- expect(suggestions).toHaveLength(defaultAuthors.length);
+ expect(suggestions).toHaveLength(defaultAuthors.length + currentUserLength);
defaultAuthors.forEach((label, index) => {
expect(suggestions.at(index).text()).toBe(label.text);
});
@@ -189,7 +206,6 @@ describe('AuthorToken', () => {
suggestionsSegment.vm.$emit('activate');
await wrapper.vm.$nextTick();
- expect(wrapper.find(GlFilteredSearchSuggestion).exists()).toBe(false);
expect(wrapper.find(GlDropdownDivider).exists()).toBe(false);
});
@@ -206,8 +222,28 @@ describe('AuthorToken', () => {
const suggestions = wrapper.findAll(GlFilteredSearchSuggestion);
- expect(suggestions).toHaveLength(1);
+ expect(suggestions).toHaveLength(1 + currentUserLength);
expect(suggestions.at(0).text()).toBe(DEFAULT_LABEL_ANY.text);
});
+
+ describe('when loading', () => {
+ beforeEach(() => {
+ wrapper = createComponent({
+ active: true,
+ config: { ...mockAuthorToken, defaultAuthors: [] },
+ stubs: { Portal: true },
+ });
+ });
+
+ it('shows loading icon', () => {
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
+ });
+
+ it('shows current user', () => {
+ const firstSuggestion = wrapper.findComponent(GlFilteredSearchSuggestion).text();
+ expect(firstSuggestion).toContain('Administrator');
+ expect(firstSuggestion).toContain('@root');
+ });
+ });
});
});
diff --git a/spec/frontend/vue_shared/components/issue/issue_assignees_spec.js b/spec/frontend/vue_shared/components/issue/issue_assignees_spec.js
index 5c29c267c99..2658fa4a706 100644
--- a/spec/frontend/vue_shared/components/issue/issue_assignees_spec.js
+++ b/spec/frontend/vue_shared/components/issue/issue_assignees_spec.js
@@ -91,7 +91,7 @@ describe('IssueAssigneesComponent', () => {
});
it('computes alt text for assignee avatar', () => {
- expect(vm.avatarUrlTitle(mockAssigneesList[0])).toBe('Avatar for Terrell Graham');
+ expect(vm.avatarUrlTitle(mockAssigneesList[0])).toBe('Assigned to Terrell Graham');
});
it('renders component root element with class `issue-assignees`', () => {
@@ -106,7 +106,7 @@ describe('IssueAssigneesComponent', () => {
const expected = mockAssigneesList.slice(0, TEST_MAX_VISIBLE - 1).map((x) =>
expect.objectContaining({
linkHref: x.web_url,
- imgAlt: `Avatar for ${x.name}`,
+ imgAlt: `Assigned to ${x.name}`,
imgCssClasses: TEST_CSS_CLASSES,
imgSrc: x.avatar_url,
imgSize: TEST_ICON_SIZE,
diff --git a/spec/graphql/mutations/commits/create_spec.rb b/spec/graphql/mutations/commits/create_spec.rb
index 152b5d87da0..097e70bada6 100644
--- a/spec/graphql/mutations/commits/create_spec.rb
+++ b/spec/graphql/mutations/commits/create_spec.rb
@@ -74,6 +74,10 @@ RSpec.describe Mutations::Commits::Create do
expect(commit_pipeline_path).to match(%r(pipelines/sha/\w+))
end
+ it 'returns the content of the commit' do
+ expect(subject[:content]).to eq(actions.pluck(:content))
+ end
+
it 'returns a new commit' do
expect(mutated_commit).to have_attributes(message: message, project: project)
expect(subject[:errors]).to be_empty
@@ -166,6 +170,7 @@ RSpec.describe Mutations::Commits::Create do
it 'returns a new commit' do
expect(mutated_commit).to have_attributes(message: message, project: project)
expect(subject[:errors]).to be_empty
+ expect(subject[:content]).to eq(actions.pluck(:content))
expect_to_contain_deltas([
a_hash_including(a_mode: '0', b_mode: '100644', new_file: true, new_path: 'ANOTHER_FILE.md')
diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb
index 17fe90c830e..ceca83d107b 100644
--- a/spec/helpers/issues_helper_spec.rb
+++ b/spec/helpers/issues_helper_spec.rb
@@ -304,7 +304,7 @@ RSpec.describe IssuesHelper do
empty_state_svg_path: '#',
endpoint: expose_path(api_v4_projects_issues_path(id: project.id)),
export_csv_path: export_csv_project_issues_path(project),
- has_issues: project_issues(project).exists?.to_s,
+ has_project_issues: project_issues(project).exists?.to_s,
import_csv_issues_path: '#',
initial_email: project.new_issuable_address(current_user, 'issue'),
is_signed_in: current_user.present?.to_s,
diff --git a/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb b/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb
index e3507df5736..47f9bcc08d2 100644
--- a/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb
+++ b/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb
@@ -356,4 +356,29 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
subject
end
end
+
+ describe '.for_configuration' do
+ let!(:migration) do
+ create(
+ :batched_background_migration,
+ job_class_name: 'MyJobClass',
+ table_name: :projects,
+ column_name: :id,
+ job_arguments: [[:id], [:id_convert_to_bigint]]
+ )
+ end
+
+ before do
+ create(:batched_background_migration, job_class_name: 'OtherClass')
+ create(:batched_background_migration, table_name: 'other_table')
+ create(:batched_background_migration, column_name: 'other_column')
+ create(:batched_background_migration, job_arguments: %w[other arguments])
+ end
+
+ it 'finds the migration matching the given configuration parameters' do
+ actual = described_class.for_configuration('MyJobClass', :projects, :id, [[:id], [:id_convert_to_bigint]])
+
+ expect(actual).to contain_exactly(migration)
+ end
+ end
end
diff --git a/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb b/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb
index c6d456964cf..85083bccaca 100644
--- a/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb
@@ -269,6 +269,38 @@ RSpec.describe Gitlab::Database::Migrations::BackgroundMigrationHelpers do
allow(Gitlab::Database::PgClass).to receive(:for_table).and_call_original
end
+ context 'when such migration already exists' do
+ it 'does not create duplicate migration' do
+ create(
+ :batched_background_migration,
+ job_class_name: 'MyJobClass',
+ table_name: :projects,
+ column_name: :id,
+ interval: 10.minutes,
+ min_value: 5,
+ max_value: 1005,
+ batch_class_name: 'MyBatchClass',
+ batch_size: 200,
+ sub_batch_size: 20,
+ job_arguments: [[:id], [:id_convert_to_bigint]]
+ )
+
+ expect do
+ model.queue_batched_background_migration(
+ 'MyJobClass',
+ :projects,
+ :id,
+ [:id], [:id_convert_to_bigint],
+ job_interval: 5.minutes,
+ batch_min_value: 5,
+ batch_max_value: 1000,
+ batch_class_name: 'MyBatchClass',
+ batch_size: 100,
+ sub_batch_size: 10)
+ end.not_to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }
+ end
+ end
+
it 'creates the database record for the migration' do
expect(Gitlab::Database::PgClass).to receive(:for_table).with(:projects).and_return(pgclass_info)
diff --git a/spec/lib/gitlab/git/remote_repository_spec.rb b/spec/lib/gitlab/git/remote_repository_spec.rb
index 84c17234ae4..c7bc81573a6 100644
--- a/spec/lib/gitlab/git/remote_repository_spec.rb
+++ b/spec/lib/gitlab/git/remote_repository_spec.rb
@@ -58,45 +58,4 @@ RSpec.describe Gitlab::Git::RemoteRepository, :seed_helper do
it { expect(subject.same_repository?(other_repository)).to eq(result) }
end
end
-
- describe '#fetch_env' do
- let(:remote_repository) { described_class.new(repository) }
-
- let(:gitaly_client) { double(:gitaly_client) }
- let(:address) { 'fake-address' }
- let(:token) { 'fake-token' }
-
- subject { remote_repository.fetch_env }
-
- before do
- allow(remote_repository).to receive(:gitaly_client).and_return(gitaly_client)
-
- expect(gitaly_client).to receive(:address).with(repository.storage).and_return(address)
- expect(gitaly_client).to receive(:token).with(repository.storage).and_return(token)
- end
-
- it { expect(subject).to be_a(Hash) }
- it { expect(subject['GITALY_ADDRESS']).to eq(address) }
- it { expect(subject['GITALY_TOKEN']).to eq(token) }
- it { expect(subject['GITALY_WD']).to eq(Dir.pwd) }
-
- it 'creates a plausible GIT_SSH_COMMAND' do
- git_ssh_command = subject['GIT_SSH_COMMAND']
-
- expect(git_ssh_command).to start_with('/')
- expect(git_ssh_command).to end_with('/gitaly-ssh upload-pack')
- end
-
- it 'creates a plausible GITALY_PAYLOAD' do
- req = Gitaly::SSHUploadPackRequest.decode_json(subject['GITALY_PAYLOAD'])
-
- expect(remote_repository.gitaly_repository).to eq(req.repository)
- end
-
- context 'when the token is blank' do
- let(:token) { '' }
-
- it { expect(subject.keys).not_to include('GITALY_TOKEN') }
- end
- end
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 33233171813..c2f49b10639 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -1880,6 +1880,26 @@ RSpec.describe Ci::Build do
it { is_expected.not_to be_retryable }
end
+
+ context 'when a canceled build has been retried already' do
+ before do
+ project.add_developer(user)
+ build.cancel!
+ described_class.retry(build, user)
+ end
+
+ context 'when prevent_retry_of_retried_jobs feature flag is enabled' do
+ it { is_expected.not_to be_retryable }
+ end
+
+ context 'when prevent_retry_of_retried_jobs feature flag is disabled' do
+ before do
+ stub_feature_flags(prevent_retry_of_retried_jobs: false)
+ end
+
+ it { is_expected.to be_retryable }
+ end
+ end
end
end
diff --git a/spec/workers/build_hooks_worker_spec.rb b/spec/workers/build_hooks_worker_spec.rb
index 62e1a4fd294..5f7e7e5fb00 100644
--- a/spec/workers/build_hooks_worker_spec.rb
+++ b/spec/workers/build_hooks_worker_spec.rb
@@ -24,20 +24,8 @@ RSpec.describe BuildHooksWorker do
end
describe '.perform_async' do
- context 'when delayed_perform_for_build_hooks_worker feature flag is disabled' do
- before do
- stub_feature_flags(delayed_perform_for_build_hooks_worker: false)
- end
-
- it 'delays scheduling a job by calling perform_in with default delay' do
- expect(described_class).to receive(:perform_in).with(ApplicationWorker::DEFAULT_DELAY_INTERVAL.second, 123)
-
- described_class.perform_async(123)
- end
- end
-
- it 'delays scheduling a job by calling perform_in' do
- expect(described_class).to receive(:perform_in).with(described_class::DATA_CONSISTENCY_DELAY.second, 123)
+ it 'delays scheduling a job by calling perform_in with default delay' do
+ expect(described_class).to receive(:perform_in).with(ApplicationWorker::DEFAULT_DELAY_INTERVAL.second, 123)
described_class.perform_async(123)
end
diff --git a/yarn.lock b/yarn.lock
index 31e2397323a..a8960245df5 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -898,10 +898,10 @@
stylelint-declaration-strict-value "1.7.7"
stylelint-scss "3.18.0"
-"@gitlab/svgs@1.197.0":
- version "1.197.0"
- resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.197.0.tgz#70be3217b4c0c84b494615d0672734f40e3695d4"
- integrity sha512-fW9EY3D1adWu4bGqo+74uElQ+U7qIxUM/I2np3133AZBeWK8bvW+92/SHpGQeINxcS/DBVOoepUSSUVvQEqroQ==
+"@gitlab/svgs@1.198.0":
+ version "1.198.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.198.0.tgz#0c90310a95ff4582f953645ed0158fbef56448e1"
+ integrity sha512-pdOQVeXOkc+4BqKFCXRXw4SPkeyrm/Ym+/B6oU7lzqlV/Wc+qKwSbmK8qr1AEAOzojwhGM17KEqZFyXx/bHwvg==
"@gitlab/tributejs@1.0.0":
version "1.0.0"