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:
-rw-r--r--.rubocop.yml1
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock4
-rw-r--r--app/assets/javascripts/issues_list/components/issuables_list_app.vue6
-rw-r--r--app/assets/javascripts/jobs/store/actions.js14
-rw-r--r--app/assets/javascripts/jobs/utils.js9
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/composer_installation.vue42
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/conan_installation.vue40
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/installation_commands.vue42
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/maven_installation.vue133
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/constants.js41
-rw-r--r--app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue26
-rw-r--r--app/assets/javascripts/pipelines/components/header_component.vue9
-rw-r--r--app/controllers/projects_controller.rb2
-rw-r--r--app/graphql/resolvers/paginated_tree_resolver.rb47
-rw-r--r--app/graphql/types/repository_type.rb4
-rw-r--r--app/helpers/nav/top_nav_helper.rb6
-rw-r--r--app/services/issuable_base_service.rb6
-rw-r--r--app/services/issues/close_service.rb8
-rw-r--r--app/services/issues/reopen_service.rb6
-rw-r--r--app/views/admin/application_settings/_plantuml.html.haml7
-rw-r--r--config/feature_flags/development/paginated_tree_graphql_query.yml8
-rw-r--r--config/initializers/check_decomposition_database_config.rb7
l---------db/ci_migrate1
-rw-r--r--db/ci_migrate/20210617101848_create_ci_instance_variables_on_ci.rb31
-rw-r--r--db/ci_schema_migrations/202106171018481
l---------[-rw-r--r--]db/ci_structure.sql46
-rw-r--r--db/migrate/20210712052519_add_label_applied_issuable_closed_to_issuable_sla.rb8
-rw-r--r--db/migrate/20210714043818_add_index_for_label_applied_to_issuable_sla.rb17
-rw-r--r--db/migrate/20210729123101_confirm_security_bot.rb18
-rw-r--r--db/post_migrate/20210722042939_update_issuable_slas_where_issue_closed.rb31
-rw-r--r--db/schema_migrations/202107120525191
-rw-r--r--db/schema_migrations/202107140438181
-rw-r--r--db/schema_migrations/202107220429391
-rw-r--r--db/schema_migrations/202107291231011
-rw-r--r--db/structure.sql6
-rw-r--r--doc/api/graphql/reference/index.md41
-rw-r--r--doc/development/database/multiple_databases.md2
-rw-r--r--doc/user/profile/index.md3
-rw-r--r--lib/gitlab/database/schema_migrations/context.rb13
-rw-r--r--lib/gitlab/usage/docs/renderer.rb2
-rw-r--r--locale/gitlab.pot9
-rw-r--r--package.json2
-rw-r--r--spec/controllers/projects_controller_spec.rb26
-rw-r--r--spec/features/projects/pipelines/pipeline_spec.rb45
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/conan_installation_spec.js.snap4
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/maven_installation_spec.js.snap37
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/details/composer_installation_spec.js70
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/details/conan_installation_spec.js45
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/details/installations_commands_spec.js30
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/details/maven_installation_spec.js115
-rw-r--r--spec/frontend/pipelines/graph/graph_component_wrapper_spec.js12
-rw-r--r--spec/frontend/pipelines/header_component_spec.js18
-rw-r--r--spec/frontend/pipelines/mock_data.js22
-rw-r--r--spec/graphql/resolvers/paginated_tree_resolver_spec.rb102
-rw-r--r--spec/graphql/types/repository_type_spec.rb2
-rw-r--r--spec/lib/gitlab/database/schema_migrations/context_spec.rb61
-rw-r--r--spec/migrations/20210722042939_update_issuable_slas_where_issue_closed_spec.rb31
-rw-r--r--spec/migrations/confirm_security_bot_spec.rb38
-rw-r--r--spec/requests/api/graphql/project/repository_spec.rb22
-rw-r--r--spec/services/issues/close_service_spec.rb2
-rw-r--r--yarn.lock8
63 files changed, 1021 insertions, 376 deletions
diff --git a/.rubocop.yml b/.rubocop.yml
index 0853b418a4c..7b2b8ca70f5 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -37,6 +37,7 @@ AllCops:
- 'file_hooks/**/*'
- 'workhorse/**/*'
- 'spec/support/*.git/**/*' # e.g. spec/support/gitlab-git-test.git
+ - 'db/ci_migrate/*.rb' # since the `db/ci_migrate` is a symlinked to `db/migrate`
CacheRootDirectory: tmp
MaxFilesInCache: 25000
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index df0556ead12..a7802efdf73 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-6430f0f4df82aecc0b282d8fb620d1d9219a6aee
+59dfc252c79b7f9d290a3ede54c9ba8a3b12d4bd
diff --git a/Gemfile b/Gemfile
index d89e8f6adec..a8e18170d88 100644
--- a/Gemfile
+++ b/Gemfile
@@ -339,7 +339,7 @@ gem 'warning', '~> 1.2.0'
group :development do
gem 'lefthook', '~> 0.7.0', require: false
- gem 'solargraph', '~> 0.42', require: false
+ gem 'solargraph', '~> 0.43', require: false
gem 'letter_opener_web', '~> 1.4.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index 02298bca3c8..4fc082bbe1b 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1206,7 +1206,7 @@ GEM
slack-messenger (2.3.4)
snowplow-tracker (0.6.1)
contracts (~> 0.7, <= 0.11)
- solargraph (0.42.3)
+ solargraph (0.43.0)
backport (~> 1.2)
benchmark
bundler (>= 1.17.2)
@@ -1631,7 +1631,7 @@ DEPENDENCIES
simplecov-cobertura (~> 1.3.1)
slack-messenger (~> 2.3.4)
snowplow-tracker (~> 0.6.1)
- solargraph (~> 0.42)
+ solargraph (~> 0.43)
spamcheck (~> 0.1.0)
spring (~> 2.1.0)
spring-commands-rspec (~> 1.0.4)
diff --git a/app/assets/javascripts/issues_list/components/issuables_list_app.vue b/app/assets/javascripts/issues_list/components/issuables_list_app.vue
index b13a389b963..62b52afdaca 100644
--- a/app/assets/javascripts/issues_list/components/issuables_list_app.vue
+++ b/app/assets/javascripts/issues_list/components/issuables_list_app.vue
@@ -9,8 +9,7 @@ import { toNumber, omit } from 'lodash';
import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { scrollToElement, historyPushState } from '~/lib/utils/common_utils';
-// eslint-disable-next-line import/no-deprecated
-import { setUrlParams, urlParamsToObject, getParameterByName } from '~/lib/utils/url_utility';
+import { setUrlParams, queryToObject, getParameterByName } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import initManualOrdering from '~/manual_ordering';
import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
@@ -264,8 +263,7 @@ export default {
});
},
getQueryObject() {
- // eslint-disable-next-line import/no-deprecated
- return urlParamsToObject(window.location.search);
+ return queryToObject(window.location.search, { gatherArrays: true });
},
onPaginate(newPage) {
if (newPage === this.page) return;
diff --git a/app/assets/javascripts/jobs/store/actions.js b/app/assets/javascripts/jobs/store/actions.js
index a8be5d8d039..53e3dbbad0d 100644
--- a/app/assets/javascripts/jobs/store/actions.js
+++ b/app/assets/javascripts/jobs/store/actions.js
@@ -13,6 +13,7 @@ import {
scrollUp,
} from '~/lib/utils/scroll_utils';
import { __ } from '~/locale';
+import { reportToSentry } from '../utils';
import * as types from './mutation_types';
export const init = ({ dispatch }, { endpoint, logState, pagePath }) => {
@@ -175,11 +176,14 @@ export const fetchTrace = ({ dispatch, state }) =>
dispatch('startPollingTrace');
}
})
- .catch((e) =>
- e.response.status === httpStatusCodes.FORBIDDEN
- ? dispatch('receiveTraceUnauthorizedError')
- : dispatch('receiveTraceError'),
- );
+ .catch((e) => {
+ if (e.response.status === httpStatusCodes.FORBIDDEN) {
+ dispatch('receiveTraceUnauthorizedError');
+ } else {
+ reportToSentry('job_actions', e);
+ dispatch('receiveTraceError');
+ }
+ });
export const startPollingTrace = ({ dispatch, commit }) => {
const traceTimeout = setTimeout(() => {
diff --git a/app/assets/javascripts/jobs/utils.js b/app/assets/javascripts/jobs/utils.js
index 1ccecf3eb53..bb27658369f 100644
--- a/app/assets/javascripts/jobs/utils.js
+++ b/app/assets/javascripts/jobs/utils.js
@@ -1,3 +1,5 @@
+import * as Sentry from '@sentry/browser';
+
/**
* capture anything starting with http:// or https://
* https?:\/\/
@@ -10,3 +12,10 @@
*/
export const linkRegex = /(https?:\/\/[^"<>()\\^`{|}\s]+[^"<>()\\^`{|}\s.,:;!?])/g;
export default { linkRegex };
+
+export const reportToSentry = (component, failureType) => {
+ Sentry.withScope((scope) => {
+ scope.setTag('component', component);
+ Sentry.captureException(failureType);
+ });
+};
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/composer_installation.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/composer_installation.vue
index b3979a620f0..cc629ae394c 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/components/details/composer_installation.vue
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/composer_installation.vue
@@ -1,9 +1,12 @@
<script>
import { GlLink, GlSprintf } from '@gitlab/ui';
-import { mapGetters, mapState } from 'vuex';
import { s__ } from '~/locale';
-import { TrackingActions, TrackingLabels } from '~/packages/details/constants';
import InstallationTitle from '~/packages_and_registries/package_registry/components/details/installation_title.vue';
+import {
+ TRACKING_ACTION_COPY_COMPOSER_REGISTRY_INCLUDE_COMMAND,
+ TRACKING_ACTION_COPY_COMPOSER_PACKAGE_INCLUDE_COMMAND,
+ TRACKING_LABEL_CODE_INSTRUCTION,
+} from '~/packages_and_registries/package_registry/constants';
import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue';
export default {
@@ -14,9 +17,25 @@ export default {
GlLink,
GlSprintf,
},
+ inject: ['composerHelpPath', 'composerConfigRepositoryName', 'composerPath', 'groupListUrl'],
+ props: {
+ packageEntity: {
+ type: Object,
+ required: true,
+ },
+ },
computed: {
- ...mapState(['composerHelpPath']),
- ...mapGetters(['composerRegistryInclude', 'composerPackageInclude', 'groupExists']),
+ composerRegistryInclude() {
+ // eslint-disable-next-line @gitlab/require-i18n-strings
+ return `composer config repositories.${this.composerConfigRepositoryName} '{"type": "composer", "url": "${this.composerPath}"}'`;
+ },
+ composerPackageInclude() {
+ // eslint-disable-next-line @gitlab/require-i18n-strings
+ return `composer req ${[this.packageEntity.name]}:${this.packageEntity.version}`;
+ },
+ groupExists() {
+ return this.groupListUrl?.length > 0;
+ },
},
i18n: {
registryInclude: s__('PackageRegistry|Add composer registry'),
@@ -27,8 +46,11 @@ export default {
'PackageRegistry|For more information on Composer packages in GitLab, %{linkStart}see the documentation.%{linkEnd}',
),
},
- trackingActions: { ...TrackingActions },
- TrackingLabels,
+ tracking: {
+ TRACKING_ACTION_COPY_COMPOSER_REGISTRY_INCLUDE_COMMAND,
+ TRACKING_ACTION_COPY_COMPOSER_PACKAGE_INCLUDE_COMMAND,
+ TRACKING_LABEL_CODE_INSTRUCTION,
+ },
installOptions: [{ value: 'composer', label: s__('PackageRegistry|Show Composer commands') }],
};
</script>
@@ -41,8 +63,8 @@ export default {
:label="$options.i18n.registryInclude"
:instruction="composerRegistryInclude"
:copy-text="$options.i18n.copyRegistryInclude"
- :tracking-action="$options.trackingActions.COPY_COMPOSER_REGISTRY_INCLUDE_COMMAND"
- :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ :tracking-action="$options.tracking.TRACKING_ACTION_COPY_COMPOSER_REGISTRY_INCLUDE_COMMAND"
+ :tracking-label="$options.tracking.TRACKING_LABEL_CODE_INSTRUCTION"
data-testid="registry-include"
/>
@@ -50,8 +72,8 @@ export default {
:label="$options.i18n.packageInclude"
:instruction="composerPackageInclude"
:copy-text="$options.i18n.copyPackageInclude"
- :tracking-action="$options.trackingActions.COPY_COMPOSER_PACKAGE_INCLUDE_COMMAND"
- :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ :tracking-action="$options.tracking.TRACKING_ACTION_COPY_COMPOSER_PACKAGE_INCLUDE_COMMAND"
+ :tracking-label="$options.tracking.TRACKING_LABEL_CODE_INSTRUCTION"
data-testid="package-include"
/>
<span data-testid="help-text">
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/conan_installation.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/conan_installation.vue
index 59b446e46b5..99e27c9d44a 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/components/details/conan_installation.vue
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/conan_installation.vue
@@ -1,9 +1,12 @@
<script>
import { GlLink, GlSprintf } from '@gitlab/ui';
-import { mapGetters, mapState } from 'vuex';
import { s__ } from '~/locale';
-import { TrackingActions, TrackingLabels } from '~/packages/details/constants';
import InstallationTitle from '~/packages_and_registries/package_registry/components/details/installation_title.vue';
+import {
+ TRACKING_ACTION_COPY_CONAN_COMMAND,
+ TRACKING_ACTION_COPY_CONAN_SETUP_COMMAND,
+ TRACKING_LABEL_CODE_INSTRUCTION,
+} from '~/packages_and_registries/package_registry/constants';
import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue';
export default {
@@ -14,17 +17,34 @@ export default {
GlLink,
GlSprintf,
},
+ inject: ['conanHelpPath', 'conanPath'],
+ props: {
+ packageEntity: {
+ type: Object,
+ required: true,
+ },
+ },
computed: {
- ...mapState(['conanHelpPath']),
- ...mapGetters(['conanInstallationCommand', 'conanSetupCommand']),
+ conanInstallationCommand() {
+ // eslint-disable-next-line @gitlab/require-i18n-strings
+ return `conan install ${this.packageEntity.name} --remote=gitlab`;
+ },
+ conanSetupCommand() {
+ // eslint-disable-next-line @gitlab/require-i18n-strings
+ return `conan remote add gitlab ${this.conanPath}`;
+ },
},
i18n: {
helpText: s__(
'PackageRegistry|For more information on the Conan registry, %{linkStart}see the documentation%{linkEnd}.',
),
},
- trackingActions: { ...TrackingActions },
- TrackingLabels,
+ tracking: {
+ TRACKING_ACTION_COPY_CONAN_COMMAND,
+ TRACKING_ACTION_COPY_CONAN_SETUP_COMMAND,
+ TRACKING_LABEL_CODE_INSTRUCTION,
+ },
+
installOptions: [{ value: 'conan', label: s__('PackageRegistry|Show Conan commands') }],
};
</script>
@@ -37,8 +57,8 @@ export default {
:label="s__('PackageRegistry|Conan Command')"
:instruction="conanInstallationCommand"
:copy-text="s__('PackageRegistry|Copy Conan Command')"
- :tracking-action="$options.trackingActions.COPY_CONAN_COMMAND"
- :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ :tracking-action="$options.tracking.TRACKING_ACTION_COPY_CONAN_COMMAND"
+ :tracking-label="$options.tracking.TRACKING_LABEL_CODE_INSTRUCTION"
/>
<h3 class="gl-font-lg">{{ __('Registry setup') }}</h3>
@@ -47,8 +67,8 @@ export default {
:label="s__('PackageRegistry|Add Conan Remote')"
:instruction="conanSetupCommand"
:copy-text="s__('PackageRegistry|Copy Conan Setup Command')"
- :tracking-action="$options.trackingActions.COPY_CONAN_SETUP_COMMAND"
- :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ :tracking-action="$options.tracking.TRACKING_ACTION_COPY_CONAN_SETUP_COMMAND"
+ :tracking-label="$options.tracking.TRACKING_LABEL_CODE_INSTRUCTION"
/>
<gl-sprintf :message="$options.i18n.helpText">
<template #link="{ content }">
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/installation_commands.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/installation_commands.vue
index 9ebfbbbf9e5..122d444e859 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/components/details/installation_commands.vue
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/installation_commands.vue
@@ -1,6 +1,12 @@
<script>
-import { PackageType, TERRAFORM_PACKAGE_TYPE } from '~/packages/shared/constants';
-import TerraformInstallation from '~/packages_and_registries/infrastructure_registry/components/terraform_installation.vue';
+import {
+ PACKAGE_TYPE_CONAN,
+ PACKAGE_TYPE_MAVEN,
+ PACKAGE_TYPE_NPM,
+ PACKAGE_TYPE_NUGET,
+ PACKAGE_TYPE_PYPI,
+ PACKAGE_TYPE_COMPOSER,
+} from '~/packages_and_registries/package_registry/constants';
import ComposerInstallation from './composer_installation.vue';
import ConanInstallation from './conan_installation.vue';
import MavenInstallation from './maven_installation.vue';
@@ -11,33 +17,22 @@ import PypiInstallation from './pypi_installation.vue';
export default {
name: 'InstallationCommands',
components: {
- [PackageType.CONAN]: ConanInstallation,
- [PackageType.MAVEN]: MavenInstallation,
- [PackageType.NPM]: NpmInstallation,
- [PackageType.NUGET]: NugetInstallation,
- [PackageType.PYPI]: PypiInstallation,
- [PackageType.COMPOSER]: ComposerInstallation,
- [TERRAFORM_PACKAGE_TYPE]: TerraformInstallation,
+ [PACKAGE_TYPE_CONAN]: ConanInstallation,
+ [PACKAGE_TYPE_MAVEN]: MavenInstallation,
+ [PACKAGE_TYPE_NPM]: NpmInstallation,
+ [PACKAGE_TYPE_NUGET]: NugetInstallation,
+ [PACKAGE_TYPE_PYPI]: PypiInstallation,
+ [PACKAGE_TYPE_COMPOSER]: ComposerInstallation,
},
props: {
packageEntity: {
type: Object,
required: true,
},
- npmPath: {
- type: String,
- required: false,
- default: '',
- },
- npmHelpPath: {
- type: String,
- required: false,
- default: '',
- },
},
computed: {
installationComponent() {
- return this.$options.components[this.packageEntity.package_type];
+ return this.$options.components[this.packageEntity.packageType];
},
},
};
@@ -45,11 +40,6 @@ export default {
<template>
<div v-if="installationComponent">
- <component
- :is="installationComponent"
- :name="packageEntity.name"
- :registry-url="npmPath"
- :help-url="npmHelpPath"
- />
+ <component :is="installationComponent" :package-entity="packageEntity" />
</div>
</template>
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/maven_installation.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/maven_installation.vue
index b035e557d21..2070f0bbca0 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/components/details/maven_installation.vue
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/maven_installation.vue
@@ -1,9 +1,18 @@
<script>
import { GlLink, GlSprintf } from '@gitlab/ui';
-import { mapGetters, mapState } from 'vuex';
import { s__ } from '~/locale';
-import { TrackingActions, TrackingLabels } from '~/packages/details/constants';
import InstallationTitle from '~/packages_and_registries/package_registry/components/details/installation_title.vue';
+import {
+ TRACKING_ACTION_COPY_MAVEN_XML,
+ TRACKING_ACTION_COPY_MAVEN_COMMAND,
+ TRACKING_ACTION_COPY_MAVEN_SETUP,
+ TRACKING_ACTION_COPY_GRADLE_INSTALL_COMMAND,
+ TRACKING_ACTION_COPY_GRADLE_ADD_TO_SOURCE_COMMAND,
+ TRACKING_ACTION_COPY_KOTLIN_INSTALL_COMMAND,
+ TRACKING_ACTION_COPY_KOTLIN_ADD_TO_SOURCE_COMMAND,
+ TRACKING_LABEL_CODE_INSTRUCTION,
+ TRACKING_LABEL_MAVEN_INSTALLATION,
+} from '~/packages_and_registries/package_registry/constants';
import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue';
export default {
@@ -14,22 +23,80 @@ export default {
GlLink,
GlSprintf,
},
+ inject: ['mavenHelpPath', 'mavenPath'],
+ props: {
+ packageEntity: {
+ type: Object,
+ required: true,
+ },
+ },
data() {
return {
instructionType: 'maven',
};
},
computed: {
- ...mapState(['mavenHelpPath']),
- ...mapGetters([
- 'mavenInstallationXml',
- 'mavenInstallationCommand',
- 'mavenSetupXml',
- 'gradleGroovyInstalCommand',
- 'gradleGroovyAddSourceCommand',
- 'gradleKotlinInstalCommand',
- 'gradleKotlinAddSourceCommand',
- ]),
+ appGroup() {
+ return this.packageEntity.metadata.appGroup;
+ },
+ appName() {
+ return this.packageEntity.metadata.appName;
+ },
+ appVersion() {
+ return this.packageEntity.metadata.appVersion;
+ },
+ mavenInstallationXml() {
+ return `<dependency>
+ <groupId>${this.appGroup}</groupId>
+ <artifactId>${this.appName}</artifactId>
+ <version>${this.appVersion}</version>
+</dependency>`;
+ },
+
+ mavenInstallationCommand() {
+ return `mvn dependency:get -Dartifact=${this.appGroup}:${this.appName}:${this.appVersion}`;
+ },
+
+ mavenSetupXml() {
+ return `<repositories>
+ <repository>
+ <id>gitlab-maven</id>
+ <url>${this.mavenPath}</url>
+ </repository>
+</repositories>
+
+<distributionManagement>
+ <repository>
+ <id>gitlab-maven</id>
+ <url>${this.mavenPath}</url>
+ </repository>
+
+ <snapshotRepository>
+ <id>gitlab-maven</id>
+ <url>${this.mavenPath}</url>
+ </snapshotRepository>
+</distributionManagement>`;
+ },
+
+ gradleGroovyInstalCommand() {
+ // eslint-disable-next-line @gitlab/require-i18n-strings
+ return `implementation '${this.appGroup}:${this.appName}:${this.appVersion}'`;
+ },
+
+ gradleGroovyAddSourceCommand() {
+ // eslint-disable-next-line @gitlab/require-i18n-strings
+ return `maven {
+ url '${this.mavenPath}'
+}`;
+ },
+
+ gradleKotlinInstalCommand() {
+ return `implementation("${this.appGroup}:${this.appName}:${this.appVersion}")`;
+ },
+
+ gradleKotlinAddSourceCommand() {
+ return `maven("${this.mavenPath}")`;
+ },
showMaven() {
return this.instructionType === 'maven';
},
@@ -48,8 +115,18 @@ export default {
'PackageRegistry|For more information on the Maven registry, %{linkStart}see the documentation%{linkEnd}.',
),
},
- trackingActions: { ...TrackingActions },
- TrackingLabels,
+ tracking: {
+ TRACKING_ACTION_COPY_MAVEN_XML,
+ TRACKING_ACTION_COPY_MAVEN_COMMAND,
+ TRACKING_ACTION_COPY_MAVEN_SETUP,
+ TRACKING_ACTION_COPY_GRADLE_INSTALL_COMMAND,
+ TRACKING_ACTION_COPY_GRADLE_ADD_TO_SOURCE_COMMAND,
+ TRACKING_ACTION_COPY_KOTLIN_INSTALL_COMMAND,
+ TRACKING_ACTION_COPY_KOTLIN_ADD_TO_SOURCE_COMMAND,
+ TRACKING_LABEL_CODE_INSTRUCTION,
+ TRACKING_LABEL_MAVEN_INSTALLATION,
+ },
+
installOptions: [
{ value: 'maven', label: s__('PackageRegistry|Maven XML') },
{ value: 'groovy', label: s__('PackageRegistry|Gradle Groovy DSL') },
@@ -78,8 +155,8 @@ export default {
<code-instruction
:instruction="mavenInstallationXml"
:copy-text="s__('PackageRegistry|Copy Maven XML')"
- :tracking-action="$options.trackingActions.COPY_MAVEN_XML"
- :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ :tracking-action="$options.tracking.TRACKING_ACTION_COPY_MAVEN_XML"
+ :tracking-label="$options.tracking.TRACKING_LABEL_CODE_INSTRUCTION"
multiline
/>
@@ -87,8 +164,8 @@ export default {
:label="s__('PackageRegistry|Maven Command')"
:instruction="mavenInstallationCommand"
:copy-text="s__('PackageRegistry|Copy Maven command')"
- :tracking-action="$options.trackingActions.COPY_MAVEN_COMMAND"
- :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ :tracking-action="$options.tracking.TRACKING_ACTION_COPY_MAVEN_COMMAND"
+ :tracking-label="$options.tracking.TRACKING_LABEL_CODE_INSTRUCTION"
/>
<h3 class="gl-font-lg">{{ s__('PackageRegistry|Registry setup') }}</h3>
@@ -102,8 +179,8 @@ export default {
<code-instruction
:instruction="mavenSetupXml"
:copy-text="s__('PackageRegistry|Copy Maven registry XML')"
- :tracking-action="$options.trackingActions.COPY_MAVEN_SETUP"
- :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ :tracking-action="$options.tracking.TRACKING_ACTION_COPY_MAVEN_SETUP"
+ :tracking-label="$options.tracking.TRACKING_LABEL_CODE_INSTRUCTION"
multiline
/>
<gl-sprintf :message="$options.i18n.helpText">
@@ -118,15 +195,15 @@ export default {
:label="s__('PackageRegistry|Gradle Groovy DSL install command')"
:instruction="gradleGroovyInstalCommand"
:copy-text="s__('PackageRegistry|Copy Gradle Groovy DSL install command')"
- :tracking-action="$options.trackingActions.COPY_GRADLE_INSTALL_COMMAND"
- :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ :tracking-action="$options.tracking.TRACKING_ACTION_COPY_GRADLE_INSTALL_COMMAND"
+ :tracking-label="$options.tracking.TRACKING_LABEL_CODE_INSTRUCTION"
/>
<code-instruction
:label="s__('PackageRegistry|Add Gradle Groovy DSL repository command')"
:instruction="gradleGroovyAddSourceCommand"
:copy-text="s__('PackageRegistry|Copy add Gradle Groovy DSL repository command')"
- :tracking-action="$options.trackingActions.COPY_GRADLE_ADD_TO_SOURCE_COMMAND"
- :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ :tracking-action="$options.tracking.TRACKING_ACTION_COPY_GRADLE_ADD_TO_SOURCE_COMMAND"
+ :tracking-label="$options.tracking.TRACKING_LABEL_CODE_INSTRUCTION"
multiline
/>
</template>
@@ -136,15 +213,15 @@ export default {
:label="s__('PackageRegistry|Gradle Kotlin DSL install command')"
:instruction="gradleKotlinInstalCommand"
:copy-text="s__('PackageRegistry|Copy Gradle Kotlin DSL install command')"
- :tracking-action="$options.trackingActions.COPY_KOTLIN_INSTALL_COMMAND"
- :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ :tracking-action="$options.tracking.TRACKING_ACTION_COPY_KOTLIN_INSTALL_COMMAND"
+ :tracking-label="$options.tracking.TRACKING_LABEL_CODE_INSTRUCTION"
/>
<code-instruction
:label="s__('PackageRegistry|Add Gradle Kotlin DSL repository command')"
:instruction="gradleKotlinAddSourceCommand"
:copy-text="s__('PackageRegistry|Copy add Gradle Kotlin DSL repository command')"
- :tracking-action="$options.trackingActions.COPY_KOTLIN_ADD_TO_SOURCE_COMMAND"
- :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ :tracking-action="$options.tracking.TRACKING_ACTION_COPY_KOTLIN_ADD_TO_SOURCE_COMMAND"
+ :tracking-label="$options.tracking.TRACKING_LABEL_CODE_INSTRUCTION"
multiline
/>
</template>
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/constants.js b/app/assets/javascripts/packages_and_registries/package_registry/constants.js
index 7a63543540e..0008e5f0c98 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/constants.js
+++ b/app/assets/javascripts/packages_and_registries/package_registry/constants.js
@@ -10,7 +10,6 @@ export const PACKAGE_TYPE_RUBYGEMS = 'RUBYGEMS';
export const PACKAGE_TYPE_GENERIC = 'GENERIC';
export const PACKAGE_TYPE_DEBIAN = 'DEBIAN';
export const PACKAGE_TYPE_HELM = 'HELM';
-export const PACKAGE_TYPE_TERRAFORM = 'terraform_module';
export const DELETE_PACKAGE_TRACKING_ACTION = 'delete_package';
export const REQUEST_DELETE_PACKAGE_TRACKING_ACTION = 'request_delete_package';
@@ -20,6 +19,46 @@ export const DELETE_PACKAGE_FILE_TRACKING_ACTION = 'delete_package_file';
export const REQUEST_DELETE_PACKAGE_FILE_TRACKING_ACTION = 'request_delete_package_file';
export const CANCEL_DELETE_PACKAGE_FILE_TRACKING_ACTION = 'cancel_delete_package_file';
+export const TRACKING_LABEL_CODE_INSTRUCTION = 'code_instruction';
+export const TRACKING_LABEL_CONAN_INSTALLATION = 'conan_installation';
+export const TRACKING_LABEL_MAVEN_INSTALLATION = 'maven_installation';
+export const TRACKING_LABEL_NPM_INSTALLATION = 'npm_installation';
+export const TRACKING_LABEL_NUGET_INSTALLATION = 'nuget_installation';
+export const TRACKING_LABEL_PYPI_INSTALLATION = 'pypi_installation';
+export const TRACKING_LABEL_COMPOSER_INSTALLATION = 'composer_installation';
+
+export const TRACKING_ACTION_INSTALLATION = 'installation';
+export const TRACKING_ACTION_REGISTRY_SETUP = 'registry_setup';
+
+export const TRACKING_ACTION_COPY_CONAN_COMMAND = 'copy_conan_command';
+export const TRACKING_ACTION_COPY_CONAN_SETUP_COMMAND = 'copy_conan_setup_command';
+
+export const TRACKING_ACTION_COPY_MAVEN_XML = 'copy_maven_xml';
+export const TRACKING_ACTION_COPY_MAVEN_COMMAND = 'copy_maven_command';
+export const TRACKING_ACTION_COPY_MAVEN_SETUP = 'copy_maven_setup_xml';
+export const TRACKING_ACTION_COPY_GRADLE_INSTALL_COMMAND = 'copy_gradle_install_command';
+export const TRACKING_ACTION_COPY_GRADLE_ADD_TO_SOURCE_COMMAND =
+ 'copy_gradle_add_to_source_command';
+export const TRACKING_ACTION_COPY_KOTLIN_INSTALL_COMMAND = 'copy_kotlin_install_command';
+export const TRACKING_ACTION_COPY_KOTLIN_ADD_TO_SOURCE_COMMAND =
+ 'copy_kotlin_add_to_source_command';
+
+export const TRACKING_ACTION_COPY_NPM_INSTALL_COMMAND = 'copy_npm_install_command';
+export const TRACKING_ACTION_COPY_NPM_SETUP_COMMAND = 'copy_npm_setup_command';
+export const TRACKING_ACTION_COPY_YARN_INSTALL_COMMAND = 'copy_yarn_install_command';
+export const TRACKING_ACTION_COPY_YARN_SETUP_COMMAND = 'copy_yarn_setup_command';
+
+export const TRACKING_ACTION_COPY_NUGET_INSTALL_COMMAND = 'copy_nuget_install_command';
+export const TRACKING_ACTION_COPY_NUGET_SETUP_COMMAND = 'copy_nuget_setup_command';
+
+export const TRACKING_ACTION_COPY_PIP_INSTALL_COMMAND = 'copy_pip_install_command';
+export const TRACKING_ACTION_COPY_PYPI_SETUP_COMMAND = 'copy_pypi_setup_command';
+
+export const TRACKING_ACTION_COPY_COMPOSER_REGISTRY_INCLUDE_COMMAND =
+ 'copy_composer_registry_include_command';
+export const TRACKING_ACTION_COPY_COMPOSER_PACKAGE_INCLUDE_COMMAND =
+ 'copy_composer_package_include_command';
+
export const TrackingCategories = {
[PACKAGE_TYPE_MAVEN]: 'MavenPackages',
[PACKAGE_TYPE_NPM]: 'NpmPackages',
diff --git a/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue b/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue
index a948a57c144..5d51d97eaee 100644
--- a/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue
+++ b/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue
@@ -7,6 +7,7 @@ import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { DEFAULT, DRAW_FAILURE, LOAD_FAILURE } from '../../constants';
import DismissPipelineGraphCallout from '../../graphql/mutations/dismiss_pipeline_notification.graphql';
+import getPipelineQuery from '../../graphql/queries/get_pipeline_header_data.query.graphql';
import { reportToSentry, reportMessageToSentry } from '../../utils';
import { listByLayers } from '../parsing_utils';
import { IID_FAILURE, LAYER_VIEW, STAGE_VIEW, VIEW_TYPE_KEY } from './constants';
@@ -51,6 +52,7 @@ export default {
alertType: null,
callouts: [],
currentViewType: STAGE_VIEW,
+ canRefetchHeaderPipeline: false,
pipeline: null,
pipelineLayers: null,
showAlert: false,
@@ -78,6 +80,26 @@ export default {
);
},
},
+ headerPipeline: {
+ query: getPipelineQuery,
+ // this query is already being called in header_component.vue, which shares the same cache as this component
+ // the skip here is to prevent sending double network requests on page load
+ skip() {
+ return !this.canRefetchHeaderPipeline;
+ },
+ variables() {
+ return {
+ fullPath: this.pipelineProjectPath,
+ iid: this.pipelineIid,
+ };
+ },
+ update(data) {
+ return data.project?.pipeline || {};
+ },
+ error() {
+ this.reportFailure({ type: LOAD_FAILURE, skipSentry: true });
+ },
+ },
pipeline: {
context() {
return getQueryHeaders(this.graphqlResourceEtag);
@@ -217,6 +239,10 @@ export default {
},
refreshPipelineGraph() {
this.$apollo.queries.pipeline.refetch();
+
+ // this will update the status in header_component since they share the same cache
+ this.canRefetchHeaderPipeline = true;
+ this.$apollo.queries.headerPipeline.refetch();
},
/* eslint-disable @gitlab/require-i18n-strings */
reportFailure({ type, err = 'No error string passed.', skipSentry = false }) {
diff --git a/app/assets/javascripts/pipelines/components/header_component.vue b/app/assets/javascripts/pipelines/components/header_component.vue
index b7500ef00b0..5db2b604956 100644
--- a/app/assets/javascripts/pipelines/components/header_component.vue
+++ b/app/assets/javascripts/pipelines/components/header_component.vue
@@ -143,13 +143,6 @@ export default {
return cancelable && userPermissions.updatePipeline;
},
},
- watch: {
- isFinished(finished) {
- if (finished) {
- this.$apollo.queries.pipeline.stopPolling();
- }
- },
- },
methods: {
reportFailure(errorType) {
this.failureType = errorType;
@@ -218,7 +211,7 @@ export default {
};
</script>
<template>
- <div class="pipeline-header-container">
+ <div class="js-pipeline-header-container">
<gl-alert v-if="hasError" :variant="failure.variant">{{ failure.text }}</gl-alert>
<ci-header
v-if="shouldRenderContent"
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index bdb645e1934..9aa1ed65fa5 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -54,6 +54,8 @@ class ProjectsController < Projects::ApplicationController
# rubocop: disable CodeReuse/ActiveRecord
def new
+ return access_denied! unless current_user.can_create_project?
+
@namespace = Namespace.find_by(id: params[:namespace_id]) if params[:namespace_id]
return access_denied! if @namespace && !can?(current_user, :create_projects, @namespace)
diff --git a/app/graphql/resolvers/paginated_tree_resolver.rb b/app/graphql/resolvers/paginated_tree_resolver.rb
new file mode 100644
index 00000000000..d1b4e75169c
--- /dev/null
+++ b/app/graphql/resolvers/paginated_tree_resolver.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class PaginatedTreeResolver < BaseResolver
+ type Types::Tree::TreeType.connection_type, null: true
+ extension Gitlab::Graphql::Extensions::ExternallyPaginatedArrayExtension
+
+ calls_gitaly!
+
+ argument :path, GraphQL::Types::String,
+ required: false,
+ default_value: '', # root of the repository
+ description: 'The path to get the tree for. Default value is the root of the repository.'
+ argument :ref, GraphQL::Types::String,
+ required: false,
+ default_value: :head,
+ description: 'The commit ref to get the tree for. Default value is HEAD.'
+ argument :recursive, GraphQL::Types::Boolean,
+ required: false,
+ default_value: false,
+ description: 'Used to get a recursive tree. Default is false.'
+
+ alias_method :repository, :object
+
+ def resolve(**args)
+ return unless repository.exists?
+
+ cursor = args.delete(:after)
+
+ pagination_params = {
+ limit: @field.max_page_size || 100,
+ page_token: cursor
+ }
+
+ tree = repository.tree(args[:ref], args[:path], recursive: args[:recursive], pagination_params: pagination_params)
+
+ next_cursor = tree.cursor&.next_cursor
+ Gitlab::Graphql::ExternallyPaginatedArray.new(cursor, next_cursor, *tree)
+ rescue Gitlab::Git::CommandError => e
+ raise Gitlab::Graphql::Errors::ArgumentError, e
+ end
+
+ def self.field_options
+ super.merge(connection: false) # we manage the pagination manually, so opt out of the connection field extension
+ end
+ end
+end
diff --git a/app/graphql/types/repository_type.rb b/app/graphql/types/repository_type.rb
index 466e3e45cb9..63d1eef5b59 100644
--- a/app/graphql/types/repository_type.rb
+++ b/app/graphql/types/repository_type.rb
@@ -14,6 +14,10 @@ module Types
description: 'Indicates a corresponding Git repository exists on disk.'
field :tree, Types::Tree::TreeType, null: true, resolver: Resolvers::TreeResolver, calls_gitaly: true,
description: 'Tree of the repository.'
+ field :paginated_tree, Types::Tree::TreeType.connection_type, null: true, resolver: Resolvers::PaginatedTreeResolver, calls_gitaly: true,
+ max_page_size: 100,
+ description: 'Paginated tree of the repository.',
+ feature_flag: :paginated_tree_graphql_query
field :blobs, Types::Repository::BlobType.connection_type, null: true, resolver: Resolvers::BlobsResolver, calls_gitaly: true,
description: 'Blobs contained within the repository'
field :branch_names, [GraphQL::Types::String], null: true, calls_gitaly: true,
diff --git a/app/helpers/nav/top_nav_helper.rb b/app/helpers/nav/top_nav_helper.rb
index 052b8339ebd..7e200ebc8a8 100644
--- a/app/helpers/nav/top_nav_helper.rb
+++ b/app/helpers/nav/top_nav_helper.rb
@@ -267,7 +267,11 @@ module Nav
builder.add_primary_menu_item(id: 'your', title: _('Your projects'), href: dashboard_projects_path)
builder.add_primary_menu_item(id: 'starred', title: _('Starred projects'), href: starred_dashboard_projects_path)
builder.add_primary_menu_item(id: 'explore', title: _('Explore projects'), href: explore_root_path)
- builder.add_secondary_menu_item(id: 'create', title: _('Create new project'), href: new_project_path)
+
+ if current_user.can_create_project?
+ builder.add_secondary_menu_item(id: 'create', title: _('Create new project'), href: new_project_path)
+ end
+
builder.build
end
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index 8d65865e7da..0984238517e 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -527,6 +527,12 @@ class IssuableBaseService < ::BaseProjectService
def allowed_update_params(params)
params
end
+
+ def update_issuable_sla(issuable)
+ return unless issuable_sla = issuable.issuable_sla
+
+ issuable_sla.update(issuable_closed: issuable.closed?)
+ end
end
IssuableBaseService.prepend_mod_with('IssuableBaseService')
diff --git a/app/services/issues/close_service.rb b/app/services/issues/close_service.rb
index cc4ad1a9c85..ea64239dd99 100644
--- a/app/services/issues/close_service.rb
+++ b/app/services/issues/close_service.rb
@@ -32,7 +32,7 @@ module Issues
notification_service.async.close_issue(issue, current_user, { closed_via: closed_via }) if notifications
todo_service.close_issue(issue, current_user)
- resolve_alert(issue)
+ perform_incident_management_actions(issue)
execute_hooks(issue, 'close')
invalidate_cache_counts(issue, users: issue.assignees)
issue.update_project_counter_caches
@@ -51,6 +51,10 @@ module Issues
private
+ def perform_incident_management_actions(issue)
+ resolve_alert(issue)
+ end
+
def close_external_issue(issue, closed_via)
return unless project.external_issue_tracker&.support_close_issue?
@@ -89,3 +93,5 @@ module Issues
end
end
end
+
+Issues::CloseService.prepend_mod_with('Issues::CloseService')
diff --git a/app/services/issues/reopen_service.rb b/app/services/issues/reopen_service.rb
index e2b1b5400c7..977b924ed72 100644
--- a/app/services/issues/reopen_service.rb
+++ b/app/services/issues/reopen_service.rb
@@ -9,6 +9,7 @@ module Issues
event_service.reopen_issue(issue, current_user)
create_note(issue, 'reopened')
notification_service.async.reopen_issue(issue, current_user)
+ perform_incident_management_actions(issue)
execute_hooks(issue, 'reopen')
invalidate_cache_counts(issue, users: issue.assignees)
issue.update_project_counter_caches
@@ -21,8 +22,13 @@ module Issues
private
+ def perform_incident_management_actions(issue)
+ end
+
def create_note(issue, state = issue.state)
SystemNoteService.change_status(issue, issue.project, current_user, state, nil)
end
end
end
+
+Issues::ReopenService.prepend_mod_with('Issues::ReopenService')
diff --git a/app/views/admin/application_settings/_plantuml.html.haml b/app/views/admin/application_settings/_plantuml.html.haml
index 632aeec6ce3..39de15dc38d 100644
--- a/app/views/admin/application_settings/_plantuml.html.haml
+++ b/app/views/admin/application_settings/_plantuml.html.haml
@@ -6,7 +6,8 @@
%button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
= expanded ? _('Collapse') : _('Expand')
%p
- = _('Allow rendering of PlantUML diagrams in Asciidoc documents.')
+ = _('Render diagrams in your documents using PlantUML.')
+ = link_to _('Learn more.'), help_page_path('administration/integration/plantuml.md'), target: '_blank', rel: 'noopener noreferrer'
.settings-content
= form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-plantuml-settings'), html: { class: 'fieldset-form', id: 'plantuml-settings' } do |f|
= form_errors(@application_setting) if expanded
@@ -20,8 +21,6 @@
= f.label :plantuml_url, _('PlantUML URL'), class: 'label-bold'
= f.text_field :plantuml_url, class: 'form-control gl-form-input', placeholder: 'http://your-plantuml-instance:8080'
.form-text.text-muted
- Allow rendering of
- = link_to "PlantUML", "http://plantuml.com"
- diagrams in Asciidoc documents using an external PlantUML service.
+ = _('The hostname of your PlantUML server.')
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"
diff --git a/config/feature_flags/development/paginated_tree_graphql_query.yml b/config/feature_flags/development/paginated_tree_graphql_query.yml
new file mode 100644
index 00000000000..13096412f25
--- /dev/null
+++ b/config/feature_flags/development/paginated_tree_graphql_query.yml
@@ -0,0 +1,8 @@
+---
+name: paginated_tree_graphql_query
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66751
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/337214
+milestone: '14.2'
+type: development
+group: group::source code
+default_enabled: false
diff --git a/config/initializers/check_decomposition_database_config.rb b/config/initializers/check_decomposition_database_config.rb
deleted file mode 100644
index f32c72efa75..00000000000
--- a/config/initializers/check_decomposition_database_config.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# frozen_string_literal: true
-
-ci_db_config = Gitlab::Application.config.database_configuration[Rails.env]["ci"]
-
-if ci_db_config.present?
- raise "migrations_paths setting for ci database must be `db/ci_migrate`" unless ci_db_config["migrations_paths"] == 'db/ci_migrate'
-end
diff --git a/db/ci_migrate b/db/ci_migrate
new file mode 120000
index 00000000000..1f0710ccbe7
--- /dev/null
+++ b/db/ci_migrate
@@ -0,0 +1 @@
+migrate \ No newline at end of file
diff --git a/db/ci_migrate/20210617101848_create_ci_instance_variables_on_ci.rb b/db/ci_migrate/20210617101848_create_ci_instance_variables_on_ci.rb
deleted file mode 100644
index 7274e6bcdf2..00000000000
--- a/db/ci_migrate/20210617101848_create_ci_instance_variables_on_ci.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-# frozen_string_literal: true
-
-class CreateCiInstanceVariablesOnCi < ActiveRecord::Migration[6.1]
- include Gitlab::Database::MigrationHelpers
-
- disable_ddl_transaction!
-
- def up
- unless table_exists?(:ci_instance_variables)
- create_table :ci_instance_variables do |t|
- t.integer :variable_type, null: false, limit: 2, default: 1
- t.boolean :masked, default: false, allow_null: false
- t.boolean :protected, default: false, allow_null: false
- t.text :key, null: false
- t.text :encrypted_value
- t.text :encrypted_value_iv
-
- t.index [:key], name: 'index_ci_instance_variables_on_key', unique: true, using: :btree
- end
- end
-
- add_text_limit(:ci_instance_variables, :key, 255)
- # Use constraint_name generated from db/migrate/20200625193358_increase_size_on_instance_level_variable_values.rb
- add_text_limit(:ci_instance_variables, :encrypted_value, 13_579, constraint_name: 'check_956afd70f1')
- add_text_limit(:ci_instance_variables, :encrypted_value_iv, 255)
- end
-
- def down
- drop_table :ci_instance_variables
- end
-end
diff --git a/db/ci_schema_migrations/20210617101848 b/db/ci_schema_migrations/20210617101848
deleted file mode 100644
index 2969c694fa2..00000000000
--- a/db/ci_schema_migrations/20210617101848
+++ /dev/null
@@ -1 +0,0 @@
-1b74312f59f6f8937cd0dd754d22dc72e9bdc7302e6254a2fda5762afebe303c \ No newline at end of file
diff --git a/db/ci_structure.sql b/db/ci_structure.sql
index 1b898012f46..b402facb598 100644..120000
--- a/db/ci_structure.sql
+++ b/db/ci_structure.sql
@@ -1,45 +1 @@
-CREATE TABLE ar_internal_metadata (
- key character varying NOT NULL,
- value character varying,
- created_at timestamp(6) without time zone NOT NULL,
- updated_at timestamp(6) without time zone NOT NULL
-);
-
-CREATE TABLE ci_instance_variables (
- id bigint NOT NULL,
- variable_type smallint DEFAULT 1 NOT NULL,
- masked boolean DEFAULT false,
- protected boolean DEFAULT false,
- key text NOT NULL,
- encrypted_value text,
- encrypted_value_iv text,
- CONSTRAINT check_07a45a5bcb CHECK ((char_length(encrypted_value_iv) <= 255)),
- CONSTRAINT check_5aede12208 CHECK ((char_length(key) <= 255)),
- CONSTRAINT check_956afd70f1 CHECK ((char_length(encrypted_value) <= 13579))
-);
-
-CREATE SEQUENCE ci_instance_variables_id_seq
- START WITH 1
- INCREMENT BY 1
- NO MINVALUE
- NO MAXVALUE
- CACHE 1;
-
-ALTER SEQUENCE ci_instance_variables_id_seq OWNED BY ci_instance_variables.id;
-
-CREATE TABLE schema_migrations (
- version character varying NOT NULL
-);
-
-ALTER TABLE ONLY ci_instance_variables ALTER COLUMN id SET DEFAULT nextval('ci_instance_variables_id_seq'::regclass);
-
-ALTER TABLE ONLY ar_internal_metadata
- ADD CONSTRAINT ar_internal_metadata_pkey PRIMARY KEY (key);
-
-ALTER TABLE ONLY ci_instance_variables
- ADD CONSTRAINT ci_instance_variables_pkey PRIMARY KEY (id);
-
-ALTER TABLE ONLY schema_migrations
- ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version);
-
-CREATE UNIQUE INDEX index_ci_instance_variables_on_key ON ci_instance_variables USING btree (key);
+structure.sql \ No newline at end of file
diff --git a/db/migrate/20210712052519_add_label_applied_issuable_closed_to_issuable_sla.rb b/db/migrate/20210712052519_add_label_applied_issuable_closed_to_issuable_sla.rb
new file mode 100644
index 00000000000..216d43dd62d
--- /dev/null
+++ b/db/migrate/20210712052519_add_label_applied_issuable_closed_to_issuable_sla.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+class AddLabelAppliedIssuableClosedToIssuableSla < ActiveRecord::Migration[6.1]
+ def change
+ add_column :issuable_slas, :label_applied, :boolean, default: false, null: false
+ add_column :issuable_slas, :issuable_closed, :boolean, default: false, null: false
+ end
+end
diff --git a/db/migrate/20210714043818_add_index_for_label_applied_to_issuable_sla.rb b/db/migrate/20210714043818_add_index_for_label_applied_to_issuable_sla.rb
new file mode 100644
index 00000000000..5931941a95e
--- /dev/null
+++ b/db/migrate/20210714043818_add_index_for_label_applied_to_issuable_sla.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddIndexForLabelAppliedToIssuableSla < ActiveRecord::Migration[6.1]
+ include Gitlab::Database::MigrationHelpers
+
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_issuable_slas_on_due_at_id_label_applied_issuable_closed'
+
+ def up
+ add_concurrent_index :issuable_slas, [:due_at, :id], name: INDEX_NAME, where: 'label_applied = FALSE AND issuable_closed = FALSE'
+ end
+
+ def down
+ remove_concurrent_index_by_name :issuable_slas, INDEX_NAME
+ end
+end
diff --git a/db/migrate/20210729123101_confirm_security_bot.rb b/db/migrate/20210729123101_confirm_security_bot.rb
new file mode 100644
index 00000000000..2184cc4e193
--- /dev/null
+++ b/db/migrate/20210729123101_confirm_security_bot.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class ConfirmSecurityBot < ActiveRecord::Migration[6.0]
+ class User < ActiveRecord::Base
+ self.table_name = 'users'
+ SECURITY_BOT_TYPE = 8
+ end
+
+ def up
+ User.where(user_type: User::SECURITY_BOT_TYPE, confirmed_at: nil)
+ .update_all(confirmed_at: Time.current)
+ end
+
+ # no-op
+ # Security Bot should be always confirmed
+ def down
+ end
+end
diff --git a/db/post_migrate/20210722042939_update_issuable_slas_where_issue_closed.rb b/db/post_migrate/20210722042939_update_issuable_slas_where_issue_closed.rb
new file mode 100644
index 00000000000..b611b51e3ff
--- /dev/null
+++ b/db/post_migrate/20210722042939_update_issuable_slas_where_issue_closed.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+class UpdateIssuableSlasWhereIssueClosed < ActiveRecord::Migration[6.1]
+ ISSUE_CLOSED_STATUS = 2
+
+ class IssuableSla < ActiveRecord::Base
+ include EachBatch
+
+ self.table_name = 'issuable_slas'
+
+ belongs_to :issue, class_name: 'Issue'
+ end
+
+ class Issue < ActiveRecord::Base
+ self.table_name = 'issues'
+
+ has_one :issuable_sla, class_name: 'IssuableSla'
+ end
+
+ def up
+ IssuableSla.each_batch(of: 50) do |relation|
+ relation.joins(:issue)
+ .where(issues: { state_id: ISSUE_CLOSED_STATUS } )
+ .update_all(issuable_closed: true)
+ end
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/schema_migrations/20210712052519 b/db/schema_migrations/20210712052519
new file mode 100644
index 00000000000..3c0874b338f
--- /dev/null
+++ b/db/schema_migrations/20210712052519
@@ -0,0 +1 @@
+f3959b7a6f7ac95019f2f85c6383ddd11294562e94936ef3b5704bd4de7c5910 \ No newline at end of file
diff --git a/db/schema_migrations/20210714043818 b/db/schema_migrations/20210714043818
new file mode 100644
index 00000000000..21c46a2608f
--- /dev/null
+++ b/db/schema_migrations/20210714043818
@@ -0,0 +1 @@
+344736284dc18b5f7516ec2062bef99b2444ae31720691e56b4e8687d5566b31 \ No newline at end of file
diff --git a/db/schema_migrations/20210722042939 b/db/schema_migrations/20210722042939
new file mode 100644
index 00000000000..fe5a3820bf9
--- /dev/null
+++ b/db/schema_migrations/20210722042939
@@ -0,0 +1 @@
+dd3b35b87c2f015895d807ede2521c9672fb41ec7a3b0b1a2f7abdc009950b6e \ No newline at end of file
diff --git a/db/schema_migrations/20210729123101 b/db/schema_migrations/20210729123101
new file mode 100644
index 00000000000..77f5bfba94e
--- /dev/null
+++ b/db/schema_migrations/20210729123101
@@ -0,0 +1 @@
+8522eaf951d87de04aea82fe8e1a9577e6665c8d08245282239476e49b02bc7d \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index c8130d5488f..1bd388b01af 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -14200,7 +14200,9 @@ ALTER SEQUENCE issuable_severities_id_seq OWNED BY issuable_severities.id;
CREATE TABLE issuable_slas (
id bigint NOT NULL,
issue_id bigint NOT NULL,
- due_at timestamp with time zone NOT NULL
+ due_at timestamp with time zone NOT NULL,
+ label_applied boolean DEFAULT false NOT NULL,
+ issuable_closed boolean DEFAULT false NOT NULL
);
CREATE SEQUENCE issuable_slas_id_seq
@@ -24047,6 +24049,8 @@ CREATE INDEX index_issuable_metric_images_on_issue_id ON issuable_metric_images
CREATE UNIQUE INDEX index_issuable_severities_on_issue_id ON issuable_severities USING btree (issue_id);
+CREATE INDEX index_issuable_slas_on_due_at_id_label_applied_issuable_closed ON issuable_slas USING btree (due_at, id) WHERE ((label_applied = false) AND (issuable_closed = false));
+
CREATE UNIQUE INDEX index_issuable_slas_on_issue_id ON issuable_slas USING btree (issue_id);
CREATE INDEX index_issue_assignees_on_user_id ON issue_assignees USING btree (user_id);
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 0090a732f4e..5e8e53f73c4 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -7039,6 +7039,29 @@ The edge type for [`Todo`](#todo).
| <a id="todoedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="todoedgenode"></a>`node` | [`Todo`](#todo) | The item at the end of the edge. |
+#### `TreeConnection`
+
+The connection type for [`Tree`](#tree).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="treeconnectionedges"></a>`edges` | [`[TreeEdge]`](#treeedge) | A list of edges. |
+| <a id="treeconnectionnodes"></a>`nodes` | [`[Tree]`](#tree) | A list of nodes. |
+| <a id="treeconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
+
+#### `TreeEdge`
+
+The edge type for [`Tree`](#tree).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="treeedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
+| <a id="treeedgenode"></a>`node` | [`Tree`](#tree) | The item at the end of the edge. |
+
#### `TreeEntryConnection`
The connection type for [`TreeEntry`](#treeentry).
@@ -12714,6 +12737,24 @@ Returns [`[String!]`](#string).
| <a id="repositorybranchnamesoffset"></a>`offset` | [`Int!`](#int) | The number of branch names to skip. |
| <a id="repositorybranchnamessearchpattern"></a>`searchPattern` | [`String!`](#string) | The pattern to search for branch names by. |
+##### `Repository.paginatedTree`
+
+Paginated tree of the repository. Available only when feature flag `paginated_tree_graphql_query` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice.
+
+Returns [`TreeConnection`](#treeconnection).
+
+This field returns a [connection](#connections). It accepts the
+four standard [pagination arguments](#connection-pagination-arguments):
+`before: String`, `after: String`, `first: Int`, `last: Int`.
+
+###### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="repositorypaginatedtreepath"></a>`path` | [`String`](#string) | The path to get the tree for. Default value is the root of the repository. |
+| <a id="repositorypaginatedtreerecursive"></a>`recursive` | [`Boolean`](#boolean) | Used to get a recursive tree. Default is false. |
+| <a id="repositorypaginatedtreeref"></a>`ref` | [`String`](#string) | The commit ref to get the tree for. Default value is HEAD. |
+
##### `Repository.tree`
Tree of the repository.
diff --git a/doc/development/database/multiple_databases.md b/doc/development/database/multiple_databases.md
index 515a58fcb89..a44807a1b05 100644
--- a/doc/development/database/multiple_databases.md
+++ b/doc/development/database/multiple_databases.md
@@ -61,7 +61,6 @@ development:
adapter: postgresql
encoding: unicode
database: gitlabhq_development_ci
- migrations_paths: db/ci_migrate
host: /path/to/gdk/postgresql
pool: 10
prepared_statements: false
@@ -82,7 +81,6 @@ test: &test
adapter: postgresql
encoding: unicode
database: gitlabhq_test_ci
- migrations_paths: db/ci_migrate
host: /path/to/gdk/postgresql
pool: 10
prepared_statements: false
diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md
index c7ff7faed57..feaa2df26f5 100644
--- a/doc/user/profile/index.md
+++ b/doc/user/profile/index.md
@@ -133,7 +133,8 @@ To specify your pronouns:
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/25742) in GitLab 14.2.
-You can add your name pronunciation to your GitLab account. This will be displayed in your profile, below your name.
+You can add your name pronunciation to your GitLab account. This is displayed in your profile, below
+your name.
To add your name pronunciation:
diff --git a/lib/gitlab/database/schema_migrations/context.rb b/lib/gitlab/database/schema_migrations/context.rb
index bd8b9bed2c1..35105121bbd 100644
--- a/lib/gitlab/database/schema_migrations/context.rb
+++ b/lib/gitlab/database/schema_migrations/context.rb
@@ -6,17 +6,14 @@ module Gitlab
class Context
attr_reader :connection
+ DEFAULT_SCHEMA_MIGRATIONS_PATH = "db/schema_migrations"
+
def initialize(connection)
@connection = connection
end
def schema_directory
- @schema_directory ||=
- if ActiveRecord::Base.configurations.primary?(database_name)
- File.join(db_dir, 'schema_migrations')
- else
- File.join(db_dir, "#{database_name}_schema_migrations")
- end
+ @schema_directory ||= Rails.root.join(database_schema_migrations_path).to_s
end
def versions_to_create
@@ -32,8 +29,8 @@ module Gitlab
@database_name ||= @connection.pool.db_config.name
end
- def db_dir
- @db_dir ||= Rails.application.config.paths["db"].first
+ def database_schema_migrations_path
+ @connection.pool.db_config.configuration_hash[:schema_migrations_path] || DEFAULT_SCHEMA_MIGRATIONS_PATH
end
end
end
diff --git a/lib/gitlab/usage/docs/renderer.rb b/lib/gitlab/usage/docs/renderer.rb
index 7a7c58005bb..fe00ab21bbb 100644
--- a/lib/gitlab/usage/docs/renderer.rb
+++ b/lib/gitlab/usage/docs/renderer.rb
@@ -5,7 +5,7 @@ module Gitlab
module Docs
class Renderer
include Gitlab::Usage::Docs::Helper
- DICTIONARY_PATH = Rails.root.join('doc', 'development', 'usage_ping')
+ DICTIONARY_PATH = Rails.root.join('doc', 'development', 'service_ping')
TEMPLATE_PATH = Rails.root.join('lib', 'gitlab', 'usage', 'docs', 'templates', 'default.md.haml')
def initialize(metrics_definitions)
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 3307529c3e3..0a5985925ea 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -3342,9 +3342,6 @@ msgstr ""
msgid "Allow public access to pipelines and job details, including output logs and artifacts."
msgstr ""
-msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
-msgstr ""
-
msgid "Allow requests to the local network from hooks and services."
msgstr ""
@@ -27724,6 +27721,9 @@ msgstr ""
msgid "Rename/Move"
msgstr ""
+msgid "Render diagrams in your documents using PlantUML."
+msgstr ""
+
msgid "Renew subscription"
msgstr ""
@@ -33016,6 +33016,9 @@ msgstr ""
msgid "The group_project_ids parameter is only allowed for a group"
msgstr ""
+msgid "The hostname of your PlantUML server."
+msgstr ""
+
msgid "The hostname of your Snowplow collector."
msgstr ""
diff --git a/package.json b/package.json
index e9119753584..dca2e2add59 100644
--- a/package.json
+++ b/package.json
@@ -57,7 +57,7 @@
"@babel/preset-env": "^7.10.1",
"@gitlab/at.js": "1.5.7",
"@gitlab/favicon-overlay": "2.0.0",
- "@gitlab/svgs": "1.208.0",
+ "@gitlab/svgs": "1.209.0",
"@gitlab/tributejs": "1.0.0",
"@gitlab/ui": "32.0.0",
"@gitlab/visual-review-tools": "1.6.1",
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index 8afb80d9cc5..92df35f9d10 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -42,6 +42,32 @@ RSpec.describe ProjectsController do
expect(response).not_to render_template('new')
end
end
+
+ context 'when user is an external user' do
+ let_it_be(:user) { create(:user, external: true) }
+
+ it 'responds with status 404' do
+ group.add_owner(user)
+
+ get :new, params: { namespace_id: group.id }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ expect(response).not_to render_template('new')
+ end
+ end
+
+ context 'when user is a group guest' do
+ let_it_be(:user) { create(:user) }
+
+ it 'responds with status 404' do
+ group.add_guest(user)
+
+ get :new, params: { namespace_id: group.id }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ expect(response).not_to render_template('new')
+ end
+ end
end
end
end
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index ee1ff550697..06da2a64be9 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -240,10 +240,14 @@ RSpec.describe 'Pipeline', :js do
end
end
- it 'is possible to retry the success job' do
+ it 'is possible to retry the success job', :sidekiq_might_not_need_inline do
find('#ci-badge-build .ci-action-icon-container').click
+ wait_for_requests
expect(page).not_to have_content('Retry job')
+ within('.js-pipeline-header-container') do
+ expect(page).to have_selector('.js-ci-status-icon-running')
+ end
end
end
@@ -282,10 +286,14 @@ RSpec.describe 'Pipeline', :js do
end
end
- it 'is possible to retry the failed build' do
+ it 'is possible to retry the failed build', :sidekiq_might_not_need_inline do
find('#ci-badge-test .ci-action-icon-container').click
+ wait_for_requests
expect(page).not_to have_content('Retry job')
+ within('.js-pipeline-header-container') do
+ expect(page).to have_selector('.js-ci-status-icon-running')
+ end
end
it 'includes the failure reason' do
@@ -308,10 +316,14 @@ RSpec.describe 'Pipeline', :js do
end
end
- it 'is possible to play the manual job' do
+ it 'is possible to play the manual job', :sidekiq_might_not_need_inline do
find('#ci-badge-manual-build .ci-action-icon-container').click
+ wait_for_requests
expect(page).not_to have_content('Play job')
+ within('.js-pipeline-header-container') do
+ expect(page).to have_selector('.js-ci-status-icon-running')
+ end
end
end
@@ -411,11 +423,18 @@ RSpec.describe 'Pipeline', :js do
context 'when retrying' do
before do
find('[data-testid="retryPipeline"]').click
+ wait_for_requests
end
it 'does not show a "Retry" button', :sidekiq_might_not_need_inline do
expect(page).not_to have_content('Retry')
end
+
+ it 'shows running status in pipeline header', :sidekiq_might_not_need_inline do
+ within('.js-pipeline-header-container') do
+ expect(page).to have_selector('.js-ci-status-icon-running')
+ end
+ end
end
end
@@ -770,7 +789,7 @@ RSpec.describe 'Pipeline', :js do
it 'shows deploy job as created' do
subject
- within('.pipeline-header-container') do
+ within('.js-pipeline-header-container') do
expect(page).to have_content('pending')
end
@@ -795,7 +814,7 @@ RSpec.describe 'Pipeline', :js do
it 'shows deploy job as pending' do
subject
- within('.pipeline-header-container') do
+ within('.js-pipeline-header-container') do
expect(page).to have_content('running')
end
@@ -817,7 +836,7 @@ RSpec.describe 'Pipeline', :js do
it 'shows deploy job as created' do
subject
- within('.pipeline-header-container') do
+ within('.js-pipeline-header-container') do
expect(page).to have_content('pending')
end
@@ -842,7 +861,7 @@ RSpec.describe 'Pipeline', :js do
it 'shows deploy job as pending' do
subject
- within('.pipeline-header-container') do
+ within('.js-pipeline-header-container') do
expect(page).to have_content('running')
end
@@ -871,7 +890,7 @@ RSpec.describe 'Pipeline', :js do
it 'shows deploy job as waiting for resource' do
subject
- within('.pipeline-header-container') do
+ within('.js-pipeline-header-container') do
expect(page).to have_content('waiting')
end
@@ -893,7 +912,7 @@ RSpec.describe 'Pipeline', :js do
it 'shows deploy job as waiting for resource' do
subject
- within('.pipeline-header-container') do
+ within('.js-pipeline-header-container') do
expect(page).to have_content('waiting')
end
@@ -914,7 +933,7 @@ RSpec.describe 'Pipeline', :js do
it 'shows deploy job as pending' do
subject
- within('.pipeline-header-container') do
+ within('.js-pipeline-header-container') do
expect(page).to have_content('running')
end
@@ -936,7 +955,7 @@ RSpec.describe 'Pipeline', :js do
it 'shows deploy job as pending' do
subject
- within('.pipeline-header-container') do
+ within('.js-pipeline-header-container') do
expect(page).to have_content('running')
end
@@ -959,7 +978,7 @@ RSpec.describe 'Pipeline', :js do
it 'shows deploy job as waiting for resource' do
subject
- within('.pipeline-header-container') do
+ within('.js-pipeline-header-container') do
expect(page).to have_content('waiting')
end
@@ -981,7 +1000,7 @@ RSpec.describe 'Pipeline', :js do
it 'shows deploy job as waiting for resource' do
subject
- within('.pipeline-header-container') do
+ within('.js-pipeline-header-container') do
expect(page).to have_content('waiting')
end
diff --git a/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/conan_installation_spec.js.snap b/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/conan_installation_spec.js.snap
index a3423e3f4d7..e9f80d5f512 100644
--- a/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/conan_installation_spec.js.snap
+++ b/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/conan_installation_spec.js.snap
@@ -9,7 +9,7 @@ exports[`ConanInstallation renders all the messages 1`] = `
<code-instruction-stub
copytext="Copy Conan Command"
- instruction="foo/command"
+ instruction="conan install @gitlab-org/package-15 --remote=gitlab"
label="Conan Command"
trackingaction="copy_conan_command"
trackinglabel="code_instruction"
@@ -23,7 +23,7 @@ exports[`ConanInstallation renders all the messages 1`] = `
<code-instruction-stub
copytext="Copy Conan Setup Command"
- instruction="foo/setup"
+ instruction="conan remote add gitlab conanPath"
label="Add Conan Remote"
trackingaction="copy_conan_setup_command"
trackinglabel="code_instruction"
diff --git a/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/maven_installation_spec.js.snap b/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/maven_installation_spec.js.snap
index 8a2793c0010..4865b8205ab 100644
--- a/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/maven_installation_spec.js.snap
+++ b/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/maven_installation_spec.js.snap
@@ -10,7 +10,7 @@ exports[`MavenInstallation groovy renders all the messages 1`] = `
<code-instruction-stub
class="gl-mb-5"
copytext="Copy Gradle Groovy DSL install command"
- instruction="foo/gradle/groovy/install"
+ instruction="implementation 'appGroup:appName:appVersion'"
label="Gradle Groovy DSL install command"
trackingaction="copy_gradle_install_command"
trackinglabel="code_instruction"
@@ -18,7 +18,9 @@ exports[`MavenInstallation groovy renders all the messages 1`] = `
<code-instruction-stub
copytext="Copy add Gradle Groovy DSL repository command"
- instruction="foo/gradle/groovy/add/source"
+ instruction="maven {
+ url 'mavenPath'
+}"
label="Add Gradle Groovy DSL repository command"
multiline="true"
trackingaction="copy_gradle_add_to_source_command"
@@ -37,7 +39,7 @@ exports[`MavenInstallation kotlin renders all the messages 1`] = `
<code-instruction-stub
class="gl-mb-5"
copytext="Copy Gradle Kotlin DSL install command"
- instruction="foo/gradle/kotlin/install"
+ instruction="implementation(\\"appGroup:appName:appVersion\\")"
label="Gradle Kotlin DSL install command"
trackingaction="copy_kotlin_install_command"
trackinglabel="code_instruction"
@@ -45,7 +47,7 @@ exports[`MavenInstallation kotlin renders all the messages 1`] = `
<code-instruction-stub
copytext="Copy add Gradle Kotlin DSL repository command"
- instruction="foo/gradle/kotlin/add/source"
+ instruction="maven(\\"mavenPath\\")"
label="Add Gradle Kotlin DSL repository command"
multiline="true"
trackingaction="copy_kotlin_add_to_source_command"
@@ -69,7 +71,11 @@ exports[`MavenInstallation maven renders all the messages 1`] = `
<code-instruction-stub
copytext="Copy Maven XML"
- instruction="foo/xml"
+ instruction="<dependency>
+ <groupId>appGroup</groupId>
+ <artifactId>appName</artifactId>
+ <version>appVersion</version>
+</dependency>"
label=""
multiline="true"
trackingaction="copy_maven_xml"
@@ -78,7 +84,7 @@ exports[`MavenInstallation maven renders all the messages 1`] = `
<code-instruction-stub
copytext="Copy Maven command"
- instruction="foo/command"
+ instruction="mvn dependency:get -Dartifact=appGroup:appName:appVersion"
label="Maven Command"
trackingaction="copy_maven_command"
trackinglabel="code_instruction"
@@ -98,7 +104,24 @@ exports[`MavenInstallation maven renders all the messages 1`] = `
<code-instruction-stub
copytext="Copy Maven registry XML"
- instruction="foo/setup"
+ instruction="<repositories>
+ <repository>
+ <id>gitlab-maven</id>
+ <url>mavenPath</url>
+ </repository>
+</repositories>
+
+<distributionManagement>
+ <repository>
+ <id>gitlab-maven</id>
+ <url>mavenPath</url>
+ </repository>
+
+ <snapshotRepository>
+ <id>gitlab-maven</id>
+ <url>mavenPath</url>
+ </snapshotRepository>
+</distributionManagement>"
label=""
multiline="true"
trackingaction="copy_maven_setup_xml"
diff --git a/spec/frontend/packages_and_registries/package_registry/components/details/composer_installation_spec.js b/spec/frontend/packages_and_registries/package_registry/components/details/composer_installation_spec.js
index 0afe69dd467..aedf20e873a 100644
--- a/spec/frontend/packages_and_registries/package_registry/components/details/composer_installation_spec.js
+++ b/spec/frontend/packages_and_registries/package_registry/components/details/composer_installation_spec.js
@@ -1,44 +1,35 @@
import { GlSprintf, GlLink } from '@gitlab/ui';
-import { shallowMount, createLocalVue } from '@vue/test-utils';
-import Vuex from 'vuex';
-import { registryUrl as composerHelpPath } from 'jest/packages/details/mock_data';
-import { composerPackage as packageEntity } from 'jest/packages/mock_data';
-import { TrackingActions } from '~/packages/details/constants';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import { packageData } from 'jest/packages_and_registries/package_registry/mock_data';
import ComposerInstallation from '~/packages_and_registries/package_registry/components/details/composer_installation.vue';
import InstallationTitle from '~/packages_and_registries/package_registry/components/details/installation_title.vue';
+import {
+ TRACKING_ACTION_COPY_COMPOSER_REGISTRY_INCLUDE_COMMAND,
+ TRACKING_ACTION_COPY_COMPOSER_PACKAGE_INCLUDE_COMMAND,
+ PACKAGE_TYPE_COMPOSER,
+} from '~/packages_and_registries/package_registry/constants';
-const localVue = createLocalVue();
-localVue.use(Vuex);
+const packageEntity = { ...packageData(), packageType: PACKAGE_TYPE_COMPOSER };
describe('ComposerInstallation', () => {
let wrapper;
- let store;
-
- const composerRegistryIncludeStr = 'foo/registry';
- const composerPackageIncludeStr = 'foo/package';
-
- const createStore = (groupExists = true) => {
- store = new Vuex.Store({
- state: { packageEntity, composerHelpPath },
- getters: {
- composerRegistryInclude: () => composerRegistryIncludeStr,
- composerPackageInclude: () => composerPackageIncludeStr,
- groupExists: () => groupExists,
- },
- });
- };
- const findRootNode = () => wrapper.find('[data-testid="root-node"]');
- const findRegistryInclude = () => wrapper.find('[data-testid="registry-include"]');
- const findPackageInclude = () => wrapper.find('[data-testid="package-include"]');
- const findHelpText = () => wrapper.find('[data-testid="help-text"]');
- const findHelpLink = () => wrapper.find(GlLink);
+ const findRootNode = () => wrapper.findByTestId('root-node');
+ const findRegistryInclude = () => wrapper.findByTestId('registry-include');
+ const findPackageInclude = () => wrapper.findByTestId('package-include');
+ const findHelpText = () => wrapper.findByTestId('help-text');
+ const findHelpLink = () => wrapper.findComponent(GlLink);
const findInstallationTitle = () => wrapper.findComponent(InstallationTitle);
- function createComponent() {
- wrapper = shallowMount(ComposerInstallation, {
- localVue,
- store,
+ function createComponent(groupListUrl = 'groupListUrl') {
+ wrapper = shallowMountExtended(ComposerInstallation, {
+ provide: {
+ composerHelpPath: 'composerHelpPath',
+ composerConfigRepositoryName: 'composerConfigRepositoryName',
+ composerPath: 'composerPath',
+ groupListUrl,
+ },
+ propsData: { packageEntity },
stubs: {
GlSprintf,
},
@@ -51,7 +42,6 @@ describe('ComposerInstallation', () => {
describe('install command switch', () => {
it('has the installation title component', () => {
- createStore();
createComponent();
expect(findInstallationTitle().exists()).toBe(true);
@@ -64,7 +54,6 @@ describe('ComposerInstallation', () => {
describe('registry include command', () => {
beforeEach(() => {
- createStore();
createComponent();
});
@@ -72,9 +61,9 @@ describe('ComposerInstallation', () => {
const registryIncludeCommand = findRegistryInclude();
expect(registryIncludeCommand.exists()).toBe(true);
expect(registryIncludeCommand.props()).toMatchObject({
- instruction: composerRegistryIncludeStr,
+ instruction: `composer config repositories.composerConfigRepositoryName '{"type": "composer", "url": "composerPath"}'`,
copyText: 'Copy registry include',
- trackingAction: TrackingActions.COPY_COMPOSER_REGISTRY_INCLUDE_COMMAND,
+ trackingAction: TRACKING_ACTION_COPY_COMPOSER_REGISTRY_INCLUDE_COMMAND,
});
});
@@ -85,7 +74,6 @@ describe('ComposerInstallation', () => {
describe('package include command', () => {
beforeEach(() => {
- createStore();
createComponent();
});
@@ -93,9 +81,9 @@ describe('ComposerInstallation', () => {
const registryIncludeCommand = findPackageInclude();
expect(registryIncludeCommand.exists()).toBe(true);
expect(registryIncludeCommand.props()).toMatchObject({
- instruction: composerPackageIncludeStr,
+ instruction: 'composer req @gitlab-org/package-15:1.0.0',
copyText: 'Copy require package include',
- trackingAction: TrackingActions.COPY_COMPOSER_PACKAGE_INCLUDE_COMMAND,
+ trackingAction: TRACKING_ACTION_COPY_COMPOSER_PACKAGE_INCLUDE_COMMAND,
});
});
@@ -108,7 +96,7 @@ describe('ComposerInstallation', () => {
'For more information on Composer packages in GitLab, see the documentation.',
);
expect(findHelpLink().attributes()).toMatchObject({
- href: composerHelpPath,
+ href: 'composerHelpPath',
target: '_blank',
});
});
@@ -116,15 +104,13 @@ describe('ComposerInstallation', () => {
describe('root node', () => {
it('is normally rendered', () => {
- createStore();
createComponent();
expect(findRootNode().exists()).toBe(true);
});
it('is not rendered when the group does not exist', () => {
- createStore(false);
- createComponent();
+ createComponent('');
expect(findRootNode().exists()).toBe(false);
});
diff --git a/spec/frontend/packages_and_registries/package_registry/components/details/conan_installation_spec.js b/spec/frontend/packages_and_registries/package_registry/components/details/conan_installation_spec.js
index 08dd21c4496..6b642cc21b7 100644
--- a/spec/frontend/packages_and_registries/package_registry/components/details/conan_installation_spec.js
+++ b/spec/frontend/packages_and_registries/package_registry/components/details/conan_installation_spec.js
@@ -1,38 +1,27 @@
-import { shallowMount, createLocalVue } from '@vue/test-utils';
-import Vuex from 'vuex';
-import { registryUrl as conanPath } from 'jest/packages/details/mock_data';
-import { conanPackage as packageEntity } from 'jest/packages/mock_data';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import { packageData } from 'jest/packages_and_registries/package_registry/mock_data';
import ConanInstallation from '~/packages_and_registries/package_registry/components/details/conan_installation.vue';
import InstallationTitle from '~/packages_and_registries/package_registry/components/details/installation_title.vue';
+import { PACKAGE_TYPE_CONAN } from '~/packages_and_registries/package_registry/constants';
import CodeInstructions from '~/vue_shared/components/registry/code_instruction.vue';
-const localVue = createLocalVue();
-localVue.use(Vuex);
+const packageEntity = { ...packageData(), packageType: PACKAGE_TYPE_CONAN };
describe('ConanInstallation', () => {
let wrapper;
- const conanInstallationCommandStr = 'foo/command';
- const conanSetupCommandStr = 'foo/setup';
-
- const store = new Vuex.Store({
- state: {
- packageEntity,
- conanPath,
- },
- getters: {
- conanInstallationCommand: () => conanInstallationCommandStr,
- conanSetupCommand: () => conanSetupCommandStr,
- },
- });
-
- const findCodeInstructions = () => wrapper.findAll(CodeInstructions);
+ const findCodeInstructions = () => wrapper.findAllComponents(CodeInstructions);
const findInstallationTitle = () => wrapper.findComponent(InstallationTitle);
function createComponent() {
- wrapper = shallowMount(ConanInstallation, {
- localVue,
- store,
+ wrapper = shallowMountExtended(ConanInstallation, {
+ provide: {
+ conanHelpPath: 'conanHelpPath',
+ conanPath: 'conanPath',
+ },
+ propsData: {
+ packageEntity,
+ },
});
}
@@ -60,13 +49,17 @@ describe('ConanInstallation', () => {
describe('installation commands', () => {
it('renders the correct command', () => {
- expect(findCodeInstructions().at(0).props('instruction')).toBe(conanInstallationCommandStr);
+ expect(findCodeInstructions().at(0).props('instruction')).toBe(
+ 'conan install @gitlab-org/package-15 --remote=gitlab',
+ );
});
});
describe('setup commands', () => {
it('renders the correct command', () => {
- expect(findCodeInstructions().at(1).props('instruction')).toBe(conanSetupCommandStr);
+ expect(findCodeInstructions().at(1).props('instruction')).toBe(
+ 'conan remote add gitlab conanPath',
+ );
});
});
});
diff --git a/spec/frontend/packages_and_registries/package_registry/components/details/installations_commands_spec.js b/spec/frontend/packages_and_registries/package_registry/components/details/installations_commands_spec.js
index b5b795ccc5a..b24946c8638 100644
--- a/spec/frontend/packages_and_registries/package_registry/components/details/installations_commands_spec.js
+++ b/spec/frontend/packages_and_registries/package_registry/components/details/installations_commands_spec.js
@@ -1,14 +1,5 @@
import { shallowMount } from '@vue/test-utils';
-import {
- conanPackage,
- mavenPackage,
- npmPackage,
- nugetPackage,
- pypiPackage,
- composerPackage,
- terraformModule,
-} from 'jest/packages/mock_data';
-import TerraformInstallation from '~/packages_and_registries/infrastructure_registry/components/terraform_installation.vue';
+import { packageData } from 'jest/packages_and_registries/package_registry/mock_data';
import ComposerInstallation from '~/packages_and_registries/package_registry/components/details/composer_installation.vue';
import ConanInstallation from '~/packages_and_registries/package_registry/components/details/conan_installation.vue';
import InstallationCommands from '~/packages_and_registries/package_registry/components/details/installation_commands.vue';
@@ -17,6 +8,21 @@ import MavenInstallation from '~/packages_and_registries/package_registry/compon
import NpmInstallation from '~/packages_and_registries/package_registry/components/details/npm_installation.vue';
import NugetInstallation from '~/packages_and_registries/package_registry/components/details/nuget_installation.vue';
import PypiInstallation from '~/packages_and_registries/package_registry/components/details/pypi_installation.vue';
+import {
+ PACKAGE_TYPE_CONAN,
+ PACKAGE_TYPE_MAVEN,
+ PACKAGE_TYPE_NPM,
+ PACKAGE_TYPE_NUGET,
+ PACKAGE_TYPE_PYPI,
+ PACKAGE_TYPE_COMPOSER,
+} from '~/packages_and_registries/package_registry/constants';
+
+const conanPackage = { ...packageData(), packageType: PACKAGE_TYPE_CONAN };
+const mavenPackage = { ...packageData(), packageType: PACKAGE_TYPE_MAVEN };
+const npmPackage = { ...packageData(), packageType: PACKAGE_TYPE_NPM };
+const nugetPackage = { ...packageData(), packageType: PACKAGE_TYPE_NUGET };
+const pypiPackage = { ...packageData(), packageType: PACKAGE_TYPE_PYPI };
+const composerPackage = { ...packageData(), packageType: PACKAGE_TYPE_COMPOSER };
describe('InstallationCommands', () => {
let wrapper;
@@ -33,7 +39,6 @@ describe('InstallationCommands', () => {
const nugetInstallation = () => wrapper.find(NugetInstallation);
const pypiInstallation = () => wrapper.find(PypiInstallation);
const composerInstallation = () => wrapper.find(ComposerInstallation);
- const terraformInstallation = () => wrapper.findComponent(TerraformInstallation);
afterEach(() => {
wrapper.destroy();
@@ -48,9 +53,8 @@ describe('InstallationCommands', () => {
${nugetPackage} | ${nugetInstallation}
${pypiPackage} | ${pypiInstallation}
${composerPackage} | ${composerInstallation}
- ${terraformModule} | ${terraformInstallation}
`('renders', ({ packageEntity, selector }) => {
- it(`${packageEntity.package_type} instructions exist`, () => {
+ it(`${packageEntity.packageType} instructions exist`, () => {
createComponent({ packageEntity });
expect(selector()).toExist();
diff --git a/spec/frontend/packages_and_registries/package_registry/components/details/maven_installation_spec.js b/spec/frontend/packages_and_registries/package_registry/components/details/maven_installation_spec.js
index d2ee0ea8bad..eed7e903833 100644
--- a/spec/frontend/packages_and_registries/package_registry/components/details/maven_installation_spec.js
+++ b/spec/frontend/packages_and_registries/package_registry/components/details/maven_installation_spec.js
@@ -1,50 +1,79 @@
-import { shallowMount, createLocalVue } from '@vue/test-utils';
import { nextTick } from 'vue';
-import Vuex from 'vuex';
-import { registryUrl as mavenPath } from 'jest/packages/details/mock_data';
-import { mavenPackage as packageEntity } from 'jest/packages/mock_data';
-import { TrackingActions } from '~/packages/details/constants';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+
+import {
+ packageData,
+ mavenMetadata,
+} from 'jest/packages_and_registries/package_registry/mock_data';
import InstallationTitle from '~/packages_and_registries/package_registry/components/details/installation_title.vue';
import MavenInstallation from '~/packages_and_registries/package_registry/components/details/maven_installation.vue';
+import {
+ TRACKING_ACTION_COPY_MAVEN_XML,
+ TRACKING_ACTION_COPY_MAVEN_COMMAND,
+ TRACKING_ACTION_COPY_MAVEN_SETUP,
+ TRACKING_ACTION_COPY_GRADLE_INSTALL_COMMAND,
+ TRACKING_ACTION_COPY_GRADLE_ADD_TO_SOURCE_COMMAND,
+ TRACKING_ACTION_COPY_KOTLIN_INSTALL_COMMAND,
+ TRACKING_ACTION_COPY_KOTLIN_ADD_TO_SOURCE_COMMAND,
+ PACKAGE_TYPE_MAVEN,
+} from '~/packages_and_registries/package_registry/constants';
import CodeInstructions from '~/vue_shared/components/registry/code_instruction.vue';
-const localVue = createLocalVue();
-localVue.use(Vuex);
-
describe('MavenInstallation', () => {
let wrapper;
- const xmlCodeBlock = 'foo/xml';
- const mavenCommandStr = 'foo/command';
- const mavenSetupXml = 'foo/setup';
- const gradleGroovyInstallCommandText = 'foo/gradle/groovy/install';
- const gradleGroovyAddSourceCommandText = 'foo/gradle/groovy/add/source';
- const gradleKotlinInstallCommandText = 'foo/gradle/kotlin/install';
- const gradleKotlinAddSourceCommandText = 'foo/gradle/kotlin/add/source';
-
- const store = new Vuex.Store({
- state: {
- packageEntity,
- mavenPath,
- },
- getters: {
- mavenInstallationXml: () => xmlCodeBlock,
- mavenInstallationCommand: () => mavenCommandStr,
- mavenSetupXml: () => mavenSetupXml,
- gradleGroovyInstalCommand: () => gradleGroovyInstallCommandText,
- gradleGroovyAddSourceCommand: () => gradleGroovyAddSourceCommandText,
- gradleKotlinInstalCommand: () => gradleKotlinInstallCommandText,
- gradleKotlinAddSourceCommand: () => gradleKotlinAddSourceCommandText,
- },
- });
-
- const findCodeInstructions = () => wrapper.findAll(CodeInstructions);
+ const packageEntity = {
+ ...packageData(),
+ packageType: PACKAGE_TYPE_MAVEN,
+ metadata: mavenMetadata(),
+ };
+
+ const mavenHelpPath = 'mavenHelpPath';
+ const mavenPath = 'mavenPath';
+
+ const xmlCodeBlock = `<dependency>
+ <groupId>appGroup</groupId>
+ <artifactId>appName</artifactId>
+ <version>appVersion</version>
+</dependency>`;
+ const mavenCommandStr = 'mvn dependency:get -Dartifact=appGroup:appName:appVersion';
+ const mavenSetupXml = `<repositories>
+ <repository>
+ <id>gitlab-maven</id>
+ <url>${mavenPath}</url>
+ </repository>
+</repositories>
+
+<distributionManagement>
+ <repository>
+ <id>gitlab-maven</id>
+ <url>${mavenPath}</url>
+ </repository>
+
+ <snapshotRepository>
+ <id>gitlab-maven</id>
+ <url>${mavenPath}</url>
+ </snapshotRepository>
+</distributionManagement>`;
+ const gradleGroovyInstallCommandText = `implementation 'appGroup:appName:appVersion'`;
+ const gradleGroovyAddSourceCommandText = `maven {
+ url '${mavenPath}'
+}`;
+ const gradleKotlinInstallCommandText = `implementation("appGroup:appName:appVersion")`;
+ const gradleKotlinAddSourceCommandText = `maven("${mavenPath}")`;
+
+ const findCodeInstructions = () => wrapper.findAllComponents(CodeInstructions);
const findInstallationTitle = () => wrapper.findComponent(InstallationTitle);
function createComponent({ data = {} } = {}) {
- wrapper = shallowMount(MavenInstallation, {
- localVue,
- store,
+ wrapper = shallowMountExtended(MavenInstallation, {
+ provide: {
+ mavenHelpPath,
+ mavenPath,
+ },
+ propsData: {
+ packageEntity,
+ },
data() {
return data;
},
@@ -98,7 +127,7 @@ describe('MavenInstallation', () => {
expect(findCodeInstructions().at(0).props()).toMatchObject({
instruction: xmlCodeBlock,
multiline: true,
- trackingAction: TrackingActions.COPY_MAVEN_XML,
+ trackingAction: TRACKING_ACTION_COPY_MAVEN_XML,
});
});
@@ -106,7 +135,7 @@ describe('MavenInstallation', () => {
expect(findCodeInstructions().at(1).props()).toMatchObject({
instruction: mavenCommandStr,
multiline: false,
- trackingAction: TrackingActions.COPY_MAVEN_COMMAND,
+ trackingAction: TRACKING_ACTION_COPY_MAVEN_COMMAND,
});
});
});
@@ -116,7 +145,7 @@ describe('MavenInstallation', () => {
expect(findCodeInstructions().at(2).props()).toMatchObject({
instruction: mavenSetupXml,
multiline: true,
- trackingAction: TrackingActions.COPY_MAVEN_SETUP,
+ trackingAction: TRACKING_ACTION_COPY_MAVEN_SETUP,
});
});
});
@@ -136,7 +165,7 @@ describe('MavenInstallation', () => {
expect(findCodeInstructions().at(0).props()).toMatchObject({
instruction: gradleGroovyInstallCommandText,
multiline: false,
- trackingAction: TrackingActions.COPY_GRADLE_INSTALL_COMMAND,
+ trackingAction: TRACKING_ACTION_COPY_GRADLE_INSTALL_COMMAND,
});
});
});
@@ -146,7 +175,7 @@ describe('MavenInstallation', () => {
expect(findCodeInstructions().at(1).props()).toMatchObject({
instruction: gradleGroovyAddSourceCommandText,
multiline: true,
- trackingAction: TrackingActions.COPY_GRADLE_ADD_TO_SOURCE_COMMAND,
+ trackingAction: TRACKING_ACTION_COPY_GRADLE_ADD_TO_SOURCE_COMMAND,
});
});
});
@@ -166,7 +195,7 @@ describe('MavenInstallation', () => {
expect(findCodeInstructions().at(0).props()).toMatchObject({
instruction: gradleKotlinInstallCommandText,
multiline: false,
- trackingAction: TrackingActions.COPY_KOTLIN_INSTALL_COMMAND,
+ trackingAction: TRACKING_ACTION_COPY_KOTLIN_INSTALL_COMMAND,
});
});
});
@@ -176,7 +205,7 @@ describe('MavenInstallation', () => {
expect(findCodeInstructions().at(1).props()).toMatchObject({
instruction: gradleKotlinAddSourceCommandText,
multiline: true,
- trackingAction: TrackingActions.COPY_KOTLIN_ADD_TO_SOURCE_COMMAND,
+ trackingAction: TRACKING_ACTION_COPY_KOTLIN_ADD_TO_SOURCE_COMMAND,
});
});
});
diff --git a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
index bb7e27b5ec2..ce507c2413e 100644
--- a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
+++ b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
@@ -18,6 +18,8 @@ import GraphViewSelector from '~/pipelines/components/graph/graph_view_selector.
import StageColumnComponent from '~/pipelines/components/graph/stage_column_component.vue';
import LinksLayer from '~/pipelines/components/graph_shared/links_layer.vue';
import * as parsingUtils from '~/pipelines/components/parsing_utils';
+import getPipelineHeaderData from '~/pipelines/graphql/queries/get_pipeline_header_data.query.graphql';
+import { mockRunningPipelineHeaderData } from '../mock_data';
import { mapCallouts, mockCalloutsResponse, mockPipelineResponse } from './mock_data';
const defaultProvide = {
@@ -72,8 +74,10 @@ describe('Pipeline graph wrapper', () => {
} = {}) => {
const callouts = mapCallouts(calloutsList);
const getUserCalloutsHandler = jest.fn().mockResolvedValue(mockCalloutsResponse(callouts));
+ const getPipelineHeaderDataHandler = jest.fn().mockResolvedValue(mockRunningPipelineHeaderData);
const requestHandlers = [
+ [getPipelineHeaderData, getPipelineHeaderDataHandler],
[getPipelineDetails, getPipelineDetailsHandler],
[getUserCallouts, getUserCalloutsHandler],
];
@@ -111,6 +115,11 @@ describe('Pipeline graph wrapper', () => {
createComponentWithApollo();
expect(getGraph().exists()).toBe(false);
});
+
+ it('skips querying headerPipeline', () => {
+ createComponentWithApollo();
+ expect(wrapper.vm.$apollo.queries.headerPipeline.skip).toBe(true);
+ });
});
describe('when data has loaded', () => {
@@ -190,12 +199,15 @@ describe('Pipeline graph wrapper', () => {
describe('when refresh action is emitted', () => {
beforeEach(async () => {
createComponentWithApollo();
+ jest.spyOn(wrapper.vm.$apollo.queries.headerPipeline, 'refetch');
jest.spyOn(wrapper.vm.$apollo.queries.pipeline, 'refetch');
await wrapper.vm.$nextTick();
getGraph().vm.$emit('refreshPipelineGraph');
});
it('calls refetch', () => {
+ expect(wrapper.vm.$apollo.queries.headerPipeline.skip).toBe(false);
+ expect(wrapper.vm.$apollo.queries.headerPipeline.refetch).toHaveBeenCalled();
expect(wrapper.vm.$apollo.queries.pipeline.refetch).toHaveBeenCalled();
});
});
diff --git a/spec/frontend/pipelines/header_component_spec.js b/spec/frontend/pipelines/header_component_spec.js
index 31f0e72c279..e531e26a858 100644
--- a/spec/frontend/pipelines/header_component_spec.js
+++ b/spec/frontend/pipelines/header_component_spec.js
@@ -99,24 +99,6 @@ describe('Pipeline details header', () => {
);
});
- describe('polling', () => {
- it('is stopped when pipeline is finished', async () => {
- wrapper = createComponent({ ...mockRunningPipelineHeader });
-
- await wrapper.setData({
- pipeline: { ...mockCancelledPipelineHeader },
- });
-
- expect(wrapper.vm.$apollo.queries.pipeline.stopPolling).toHaveBeenCalled();
- });
-
- it('is not stopped when pipeline is not finished', () => {
- wrapper = createComponent();
-
- expect(wrapper.vm.$apollo.queries.pipeline.stopPolling).not.toHaveBeenCalled();
- });
- });
-
describe('actions', () => {
describe('Retry action', () => {
beforeEach(() => {
diff --git a/spec/frontend/pipelines/mock_data.js b/spec/frontend/pipelines/mock_data.js
index 7e3c3727c9d..fdc78d48901 100644
--- a/spec/frontend/pipelines/mock_data.js
+++ b/spec/frontend/pipelines/mock_data.js
@@ -127,6 +127,28 @@ export const mockSuccessfulPipelineHeader = {
},
};
+export const mockRunningPipelineHeaderData = {
+ data: {
+ project: {
+ pipeline: {
+ ...mockRunningPipelineHeader,
+ iid: '28',
+ user: {
+ name: 'Foo',
+ username: 'foobar',
+ webPath: '/foo',
+ email: 'foo@bar.com',
+ avatarUrl: 'link',
+ status: null,
+ __typename: 'UserCore',
+ },
+ __typename: 'Pipeline',
+ },
+ __typename: 'Project',
+ },
+ },
+};
+
export const stageReply = {
name: 'deploy',
title: 'deploy: running',
diff --git a/spec/graphql/resolvers/paginated_tree_resolver_spec.rb b/spec/graphql/resolvers/paginated_tree_resolver_spec.rb
new file mode 100644
index 00000000000..82b05937aa3
--- /dev/null
+++ b/spec/graphql/resolvers/paginated_tree_resolver_spec.rb
@@ -0,0 +1,102 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::PaginatedTreeResolver do
+ include GraphqlHelpers
+
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:repository) { project.repository }
+
+ specify do
+ expect(described_class).to have_nullable_graphql_type(Types::Tree::TreeType.connection_type)
+ end
+
+ describe '#resolve', :aggregate_failures do
+ subject { resolve_repository(args, opts) }
+
+ let(:args) { { ref: 'master' } }
+ let(:opts) { {} }
+
+ let(:start_cursor) { subject.start_cursor }
+ let(:end_cursor) { subject.end_cursor }
+ let(:items) { subject.items }
+ let(:entries) { items.first.entries }
+
+ it 'resolves to a collection with a tree object' do
+ expect(items.first).to be_an_instance_of(Tree)
+
+ expect(start_cursor).to be_nil
+ expect(end_cursor).to be_blank
+ expect(entries.count).to eq(repository.tree.entries.count)
+ end
+
+ context 'with recursive option' do
+ let(:args) { super().merge(recursive: true) }
+
+ it 'resolve to a recursive tree' do
+ expect(entries[4].path).to eq('files/html')
+ end
+ end
+
+ context 'with limited max_page_size' do
+ let(:opts) { { max_page_size: 5 } }
+
+ it 'resolves to a pagination collection with a tree object' do
+ expect(items.first).to be_an_instance_of(Tree)
+
+ expect(start_cursor).to be_nil
+ expect(end_cursor).to be_present
+ expect(entries.count).to eq(5)
+ end
+ end
+
+ context 'when repository does not exist' do
+ before do
+ allow(repository).to receive(:exists?).and_return(false)
+ end
+
+ it 'returns nil' do
+ is_expected.to be(nil)
+ end
+ end
+
+ describe 'Cursor pagination' do
+ context 'when cursor is invalid' do
+ let(:args) { super().merge(after: 'invalid') }
+
+ it { expect { subject }.to raise_error(Gitlab::Graphql::Errors::ArgumentError) }
+ end
+
+ it 'returns all tree entries during cursor pagination' do
+ cursor = nil
+
+ expected_entries = repository.tree.entries.map(&:path)
+ collected_entries = []
+
+ loop do
+ result = resolve_repository(args.merge(after: cursor), max_page_size: 10)
+
+ collected_entries += result.items.first.entries.map(&:path)
+
+ expect(result.start_cursor).to eq(cursor)
+ cursor = result.end_cursor
+
+ break if cursor.blank?
+ end
+
+ expect(collected_entries).to match_array(expected_entries)
+ end
+ end
+ end
+
+ def resolve_repository(args, opts = {})
+ field_options = described_class.field_options.merge(
+ owner: resolver_parent,
+ name: 'field_value'
+ ).merge(opts)
+
+ field = ::Types::BaseField.new(**field_options)
+ resolve_field(field, repository, args: args, object_type: resolver_parent)
+ end
+end
diff --git a/spec/graphql/types/repository_type_spec.rb b/spec/graphql/types/repository_type_spec.rb
index ee0cc4361da..5488d78b720 100644
--- a/spec/graphql/types/repository_type_spec.rb
+++ b/spec/graphql/types/repository_type_spec.rb
@@ -11,6 +11,8 @@ RSpec.describe GitlabSchema.types['Repository'] do
specify { expect(described_class).to have_graphql_field(:tree) }
+ specify { expect(described_class).to have_graphql_field(:paginated_tree, calls_gitaly?: true, max_page_size: 100) }
+
specify { expect(described_class).to have_graphql_field(:exists, calls_gitaly?: true, complexity: 2) }
specify { expect(described_class).to have_graphql_field(:blobs) }
diff --git a/spec/lib/gitlab/database/schema_migrations/context_spec.rb b/spec/lib/gitlab/database/schema_migrations/context_spec.rb
index 31cba9290db..1f1943d00a3 100644
--- a/spec/lib/gitlab/database/schema_migrations/context_spec.rb
+++ b/spec/lib/gitlab/database/schema_migrations/context_spec.rb
@@ -3,7 +3,8 @@
require 'spec_helper'
RSpec.describe Gitlab::Database::SchemaMigrations::Context do
- let(:connection) { ActiveRecord::Base.connection }
+ let(:connection_class) { ActiveRecord::Base }
+ let(:connection) { connection_class.connection }
let(:context) { described_class.new(connection) }
@@ -12,13 +13,65 @@ RSpec.describe Gitlab::Database::SchemaMigrations::Context do
expect(context.schema_directory).to eq(File.join(Rails.root, 'db/schema_migrations'))
end
- context 'multiple databases' do
- let(:connection) { Ci::CiDatabaseRecord.connection }
+ context 'CI database' do
+ let(:connection_class) { Ci::CiDatabaseRecord }
it 'returns a directory path that is database specific' do
skip_if_multiple_databases_not_setup
- expect(context.schema_directory).to eq(File.join(Rails.root, 'db/ci_schema_migrations'))
+ expect(context.schema_directory).to eq(File.join(Rails.root, 'db/schema_migrations'))
+ end
+ end
+
+ context 'multiple databases' do
+ let(:connection_class) do
+ Class.new(::ApplicationRecord) do
+ self.abstract_class = true
+
+ def self.name
+ 'Gitlab::Database::SchemaMigrations::Context::TestConnection'
+ end
+ end
+ end
+
+ let(:configuration_overrides) { {} }
+
+ before do
+ connection_class.establish_connection(
+ ActiveRecord::Base
+ .connection_pool
+ .db_config
+ .configuration_hash
+ .merge(configuration_overrides)
+ )
+ end
+
+ after do
+ connection_class.remove_connection
+ end
+
+ context 'when `schema_migrations_path` is configured as string' do
+ let(:configuration_overrides) do
+ { "schema_migrations_path" => "db/ci_schema_migrations" }
+ end
+
+ it 'returns a configured directory path that' do
+ skip_if_multiple_databases_not_setup
+
+ expect(context.schema_directory).to eq(File.join(Rails.root, 'db/ci_schema_migrations'))
+ end
+ end
+
+ context 'when `schema_migrations_path` is configured as symbol' do
+ let(:configuration_overrides) do
+ { schema_migrations_path: "db/ci_schema_migrations" }
+ end
+
+ it 'returns a configured directory path that' do
+ skip_if_multiple_databases_not_setup
+
+ expect(context.schema_directory).to eq(File.join(Rails.root, 'db/ci_schema_migrations'))
+ end
end
end
end
diff --git a/spec/migrations/20210722042939_update_issuable_slas_where_issue_closed_spec.rb b/spec/migrations/20210722042939_update_issuable_slas_where_issue_closed_spec.rb
new file mode 100644
index 00000000000..a0aae00776d
--- /dev/null
+++ b/spec/migrations/20210722042939_update_issuable_slas_where_issue_closed_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!('update_issuable_slas_where_issue_closed')
+
+RSpec.describe UpdateIssuableSlasWhereIssueClosed, :migration do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:issues) { table(:issues) }
+ let(:issuable_slas) { table(:issuable_slas) }
+ let(:issue_params) { { title: 'title', project_id: project.id } }
+ let(:issue_closed_state) { 2 }
+
+ let!(:namespace) { namespaces.create!(name: 'foo', path: 'foo') }
+ let!(:project) { projects.create!(namespace_id: namespace.id) }
+ let!(:issue_open) { issues.create!(issue_params) }
+ let!(:issue_closed) { issues.create!(issue_params.merge(state_id: issue_closed_state)) }
+
+ let!(:issuable_sla_open_issue) { issuable_slas.create!(issue_id: issue_open.id, due_at: Time.now) }
+ let!(:issuable_sla_closed_issue) { issuable_slas.create!(issue_id: issue_closed.id, due_at: Time.now) }
+
+ it 'sets the issuable_closed attribute to false' do
+ expect(issuable_sla_open_issue.issuable_closed).to eq(false)
+ expect(issuable_sla_closed_issue.issuable_closed).to eq(false)
+
+ migrate!
+
+ expect(issuable_sla_open_issue.reload.issuable_closed).to eq(false)
+ expect(issuable_sla_closed_issue.reload.issuable_closed).to eq(true)
+ end
+end
diff --git a/spec/migrations/confirm_security_bot_spec.rb b/spec/migrations/confirm_security_bot_spec.rb
new file mode 100644
index 00000000000..19ca81f92f3
--- /dev/null
+++ b/spec/migrations/confirm_security_bot_spec.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe ConfirmSecurityBot, :migration do
+ let(:users) { table(:users) }
+
+ let(:user_type) { 8 }
+
+ context 'when bot is not created' do
+ it 'skips migration' do
+ migrate!
+
+ bot = users.find_by(user_type: user_type)
+
+ expect(bot).to be_nil
+ end
+ end
+
+ context 'when bot is confirmed' do
+ let(:bot) { table(:users).create!(user_type: user_type, confirmed_at: Time.current, projects_limit: 1) }
+
+ it 'skips migration' do
+ expect { migrate! }.not_to change { bot.reload.confirmed_at }
+ end
+ end
+
+ context 'when bot is not confirmed' do
+ let(:bot) { table(:users).create!(user_type: user_type, projects_limit: 1) }
+
+ it 'update confirmed_at' do
+ freeze_time do
+ expect { migrate! }.to change { bot.reload.confirmed_at }.from(nil).to(Time.current)
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/project/repository_spec.rb b/spec/requests/api/graphql/project/repository_spec.rb
index bddd300e27f..8810f2fa3d5 100644
--- a/spec/requests/api/graphql/project/repository_spec.rb
+++ b/spec/requests/api/graphql/project/repository_spec.rb
@@ -83,4 +83,26 @@ RSpec.describe 'getting a repository in a project' do
expect(graphql_data['project']['repository']).to be_nil
end
end
+
+ context 'when paginated tree requested' do
+ let(:fields) do
+ %(
+ paginatedTree {
+ nodes {
+ trees {
+ nodes {
+ path
+ }
+ }
+ }
+ }
+ )
+ end
+
+ it 'returns paginated tree' do
+ post_graphql(query, current_user: current_user)
+
+ expect(graphql_data['project']['repository']['paginatedTree']).to be_present
+ end
+ end
end
diff --git a/spec/services/issues/close_service_spec.rb b/spec/services/issues/close_service_spec.rb
index 9a70de80123..b1d4877e138 100644
--- a/spec/services/issues/close_service_spec.rb
+++ b/spec/services/issues/close_service_spec.rb
@@ -222,7 +222,7 @@ RSpec.describe Issues::CloseService do
it 'verifies the number of queries' do
recorded = ActiveRecord::QueryRecorder.new { close_issue }
- expected_queries = 24
+ expected_queries = 25
expect(recorded.count).to be <= expected_queries
expect(recorded.cached_count).to eq(0)
diff --git a/yarn.lock b/yarn.lock
index 7870aa23b58..158d46b1679 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.208.0":
- version "1.208.0"
- resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.208.0.tgz#e298b4d38ac38b7186000cc21bafebebe0766a83"
- integrity sha512-dK2fXLCPOg0bchIqiNpVPY8RmEIpmR9wo/BHeLpf8NTcEjtAok87hJ+cwySbDvKeu10bSePsgl8NcST61GlNaA==
+"@gitlab/svgs@1.209.0":
+ version "1.209.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.209.0.tgz#06020b74668df17b9bdaa33b561b4800ca34cc10"
+ integrity sha512-V6NaXDhu899tNCuU4+VepY7Rv2Ge3ViOctrUAS3NQtGcXV1THDhGAg6+OCFpgHr1fhmxYNwpxQ8OTh+HaPvtIA==
"@gitlab/tributejs@1.0.0":
version "1.0.0"