summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-07-23 12:09:05 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-07-23 12:09:05 +0000
commitab8eecd62cc11a31568b25304f5fd31c8b7f437f (patch)
treeb73b765c3cea414112840fd8041c62f886d8ce53 /app
parent00a889ea7a115ebbda95a071dd630f93b79261e3 (diff)
downloadgitlab-ce-ab8eecd62cc11a31568b25304f5fd31c8b7f437f.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/jira_connect/branches/components/project_dropdown.vue2
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/additional_metadata.vue94
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/composer_installation.vue65
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/conan_installation.vue59
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/dependency_row.vue35
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/file_sha.vue41
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/installation_commands.vue55
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/installation_title.vue38
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/maven_installation.vue152
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/npm_installation.vue103
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/nuget_installation.vue58
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/package_files.vue165
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/package_history.vue167
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/pypi_installation.vue71
-rw-r--r--app/assets/javascripts/pages/jira_connect/branches/new/index.js3
-rw-r--r--app/assets/javascripts/security_configuration/components/auto_dev_ops_enabled_alert.vue30
-rw-r--r--app/assets/javascripts/security_configuration/components/constants.js3
-rw-r--r--app/assets/javascripts/security_configuration/components/redesigned_app.vue47
-rw-r--r--app/assets/javascripts/security_configuration/components/section_layout.vue2
-rw-r--r--app/controllers/groups/dependency_proxy_for_containers_controller.rb12
-rw-r--r--app/controllers/jira_connect/app_descriptor_controller.rb44
-rw-r--r--app/controllers/jira_connect/branches_controller.rb28
-rw-r--r--app/graphql/mutations/packages/destroy_file.rb35
-rw-r--r--app/graphql/types/mutation_type.rb1
-rw-r--r--app/graphql/types/query_complexity_type.rb4
-rw-r--r--app/graphql/types/release_asset_link_shared_input_arguments.rb6
-rw-r--r--app/graphql/types/release_asset_link_type.rb12
-rw-r--r--app/graphql/types/release_assets_type.rb2
-rw-r--r--app/graphql/types/release_links_type.rb14
-rw-r--r--app/graphql/types/release_source_type.rb4
-rw-r--r--app/graphql/types/release_type.rb10
-rw-r--r--app/graphql/types/repository/blob_type.rb44
-rw-r--r--app/graphql/types/repository_type.rb10
-rw-r--r--app/graphql/types/resolvable_interface.rb4
-rw-r--r--app/graphql/types/snippet_type.rb14
-rw-r--r--app/graphql/types/snippets/blob_action_input_type.rb6
-rw-r--r--app/graphql/types/snippets/blob_type.rb22
-rw-r--r--app/graphql/types/task_completion_status.rb4
-rw-r--r--app/graphql/types/terraform/state_type.rb4
-rw-r--r--app/graphql/types/terraform/state_version_type.rb6
-rw-r--r--app/graphql/types/timelog_type.rb2
-rw-r--r--app/graphql/types/todo_type.rb4
-rw-r--r--app/graphql/types/tree/blob_type.rb8
-rw-r--r--app/graphql/types/tree/entry_type.rb10
-rw-r--r--app/graphql/types/tree/submodule_type.rb4
-rw-r--r--app/graphql/types/tree/tree_entry_type.rb4
-rw-r--r--app/graphql/types/user_interface.rb20
-rw-r--r--app/graphql/types/user_merge_request_interaction_type.rb8
-rw-r--r--app/graphql/types/user_status_type.rb4
-rw-r--r--app/mailers/previews/notify_preview.rb2
-rw-r--r--app/models/application_setting.rb2
-rw-r--r--app/models/internal_id.rb4
-rw-r--r--app/models/issue.rb26
-rw-r--r--app/models/packages/event.rb8
-rw-r--r--app/services/auto_merge/base_service.rb6
-rw-r--r--app/services/ci/delete_unit_tests_service.rb2
-rw-r--r--app/services/ci/unlock_artifacts_service.rb2
-rw-r--r--app/services/dependency_proxy/find_or_create_blob_service.rb4
-rw-r--r--app/services/dependency_proxy/find_or_create_manifest_service.rb8
-rw-r--r--app/services/deployments/update_environment_service.rb2
-rw-r--r--app/services/design_management/copy_design_collection/copy_service.rb2
-rw-r--r--app/services/feature_flags/create_service.rb2
-rw-r--r--app/services/feature_flags/destroy_service.rb2
-rw-r--r--app/services/feature_flags/update_service.rb2
-rw-r--r--app/services/issuable/clone/base_service.rb2
-rw-r--r--app/services/issuable/common_system_notes_service.rb2
-rw-r--r--app/services/issuable/destroy_label_links_service.rb2
-rw-r--r--app/services/packages/create_dependency_service.rb2
-rw-r--r--app/services/packages/go/create_package_service.rb2
-rw-r--r--app/services/packages/npm/create_package_service.rb2
-rw-r--r--app/services/packages/terraform_module/create_package_service.rb2
-rw-r--r--app/services/projects/cleanup_service.rb2
-rw-r--r--app/services/projects/fetch_statistics_increment_service.rb2
-rw-r--r--app/services/releases/update_service.rb2
-rw-r--r--app/services/todos/destroy/destroyed_issuable_service.rb2
-rw-r--r--app/views/admin/application_settings/_email.html.haml10
-rw-r--r--app/views/jira_connect/branches/new.html.haml5
77 files changed, 1464 insertions, 187 deletions
diff --git a/app/assets/javascripts/jira_connect/branches/components/project_dropdown.vue b/app/assets/javascripts/jira_connect/branches/components/project_dropdown.vue
index 2accb46db60..751f3e9639d 100644
--- a/app/assets/javascripts/jira_connect/branches/components/project_dropdown.vue
+++ b/app/assets/javascripts/jira_connect/branches/components/project_dropdown.vue
@@ -41,7 +41,7 @@ export default {
};
},
update(data) {
- return data?.projects?.nodes.filter((project) => !project.repository.empty) ?? [];
+ return data?.projects?.nodes.filter((project) => !project.repository?.empty) ?? [];
},
result() {
this.initialProjectsLoading = false;
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/additional_metadata.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/additional_metadata.vue
new file mode 100644
index 00000000000..89add0a0d31
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/additional_metadata.vue
@@ -0,0 +1,94 @@
+<script>
+import { GlLink, GlSprintf } from '@gitlab/ui';
+import { s__ } from '~/locale';
+import { PackageType } from '~/packages/shared/constants';
+import DetailsRow from '~/vue_shared/components/registry/details_row.vue';
+
+export default {
+ i18n: {
+ sourceText: s__('PackageRegistry|Source project located at %{link}'),
+ licenseText: s__('PackageRegistry|License information located at %{link}'),
+ recipeText: s__('PackageRegistry|Recipe: %{recipe}'),
+ appGroup: s__('PackageRegistry|App group: %{group}'),
+ appName: s__('PackageRegistry|App name: %{name}'),
+ },
+ components: {
+ DetailsRow,
+ GlLink,
+ GlSprintf,
+ },
+ props: {
+ packageEntity: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ showMetadata() {
+ const visibilityConditions = {
+ [PackageType.NUGET]: this.packageEntity.nuget_metadatum,
+ [PackageType.CONAN]: this.packageEntity.conan_metadatum,
+ [PackageType.MAVEN]: this.packageEntity.maven_metadatum,
+ };
+ return visibilityConditions[this.packageEntity.package_type];
+ },
+ },
+};
+</script>
+
+<template>
+ <div v-if="showMetadata">
+ <h3 class="gl-font-lg" data-testid="title">{{ __('Additional Metadata') }}</h3>
+
+ <div class="gl-bg-gray-50 gl-inset-border-1-gray-100 gl-rounded-base" data-testid="main">
+ <template v-if="packageEntity.nuget_metadatum">
+ <details-row icon="project" padding="gl-p-4" dashed data-testid="nuget-source">
+ <gl-sprintf :message="$options.i18n.sourceText">
+ <template #link>
+ <gl-link :href="packageEntity.nuget_metadatum.project_url" target="_blank">{{
+ packageEntity.nuget_metadatum.project_url
+ }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </details-row>
+ <details-row icon="license" padding="gl-p-4" data-testid="nuget-license">
+ <gl-sprintf :message="$options.i18n.licenseText">
+ <template #link>
+ <gl-link :href="packageEntity.nuget_metadatum.license_url" target="_blank">{{
+ packageEntity.nuget_metadatum.license_url
+ }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </details-row>
+ </template>
+
+ <details-row
+ v-else-if="packageEntity.conan_metadatum"
+ icon="information-o"
+ padding="gl-p-4"
+ data-testid="conan-recipe"
+ >
+ <gl-sprintf :message="$options.i18n.recipeText">
+ <template #recipe>{{ packageEntity.name }}</template>
+ </gl-sprintf>
+ </details-row>
+
+ <template v-else-if="packageEntity.maven_metadatum">
+ <details-row icon="information-o" padding="gl-p-4" dashed data-testid="maven-app">
+ <gl-sprintf :message="$options.i18n.appName">
+ <template #name>
+ <strong>{{ packageEntity.maven_metadatum.app_name }}</strong>
+ </template>
+ </gl-sprintf>
+ </details-row>
+ <details-row icon="information-o" padding="gl-p-4" data-testid="maven-group">
+ <gl-sprintf :message="$options.i18n.appGroup">
+ <template #group>
+ <strong>{{ packageEntity.maven_metadatum.app_group }}</strong>
+ </template>
+ </gl-sprintf>
+ </details-row>
+ </template>
+ </div>
+ </div>
+</template>
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
new file mode 100644
index 00000000000..b3979a620f0
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/composer_installation.vue
@@ -0,0 +1,65 @@
+<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 CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue';
+
+export default {
+ name: 'ComposerInstallation',
+ components: {
+ InstallationTitle,
+ CodeInstruction,
+ GlLink,
+ GlSprintf,
+ },
+ computed: {
+ ...mapState(['composerHelpPath']),
+ ...mapGetters(['composerRegistryInclude', 'composerPackageInclude', 'groupExists']),
+ },
+ i18n: {
+ registryInclude: s__('PackageRegistry|Add composer registry'),
+ copyRegistryInclude: s__('PackageRegistry|Copy registry include'),
+ packageInclude: s__('PackageRegistry|Install package version'),
+ copyPackageInclude: s__('PackageRegistry|Copy require package include'),
+ infoLine: s__(
+ 'PackageRegistry|For more information on Composer packages in GitLab, %{linkStart}see the documentation.%{linkEnd}',
+ ),
+ },
+ trackingActions: { ...TrackingActions },
+ TrackingLabels,
+ installOptions: [{ value: 'composer', label: s__('PackageRegistry|Show Composer commands') }],
+};
+</script>
+
+<template>
+ <div v-if="groupExists" data-testid="root-node">
+ <installation-title package-type="composer" :options="$options.installOptions" />
+
+ <code-instruction
+ :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"
+ data-testid="registry-include"
+ />
+
+ <code-instruction
+ :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"
+ data-testid="package-include"
+ />
+ <span data-testid="help-text">
+ <gl-sprintf :message="$options.i18n.infoLine">
+ <template #link="{ content }">
+ <gl-link :href="composerHelpPath" target="_blank">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </span>
+ </div>
+</template>
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
new file mode 100644
index 00000000000..59b446e46b5
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/conan_installation.vue
@@ -0,0 +1,59 @@
+<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 CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue';
+
+export default {
+ name: 'ConanInstallation',
+ components: {
+ InstallationTitle,
+ CodeInstruction,
+ GlLink,
+ GlSprintf,
+ },
+ computed: {
+ ...mapState(['conanHelpPath']),
+ ...mapGetters(['conanInstallationCommand', 'conanSetupCommand']),
+ },
+ i18n: {
+ helpText: s__(
+ 'PackageRegistry|For more information on the Conan registry, %{linkStart}see the documentation%{linkEnd}.',
+ ),
+ },
+ trackingActions: { ...TrackingActions },
+ TrackingLabels,
+ installOptions: [{ value: 'conan', label: s__('PackageRegistry|Show Conan commands') }],
+};
+</script>
+
+<template>
+ <div>
+ <installation-title package-type="conan" :options="$options.installOptions" />
+
+ <code-instruction
+ :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"
+ />
+
+ <h3 class="gl-font-lg">{{ __('Registry setup') }}</h3>
+
+ <code-instruction
+ :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"
+ />
+ <gl-sprintf :message="$options.i18n.helpText">
+ <template #link="{ content }">
+ <gl-link :href="conanHelpPath" target="_blank">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </div>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/dependency_row.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/dependency_row.vue
new file mode 100644
index 00000000000..1a2202b23c8
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/dependency_row.vue
@@ -0,0 +1,35 @@
+<script>
+export default {
+ name: 'DependencyRow',
+ props: {
+ dependency: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ showVersion() {
+ return Boolean(this.dependency.version_pattern);
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="gl-responsive-table-row">
+ <div class="table-section section-50">
+ <strong class="gl-text-body">{{ dependency.name }}</strong>
+ <span v-if="dependency.target_framework" data-testid="target-framework"
+ >({{ dependency.target_framework }})</span
+ >
+ </div>
+
+ <div
+ v-if="showVersion"
+ class="table-section section-50 gl-display-flex gl-md-justify-content-end"
+ data-testid="version-pattern"
+ >
+ <span class="gl-text-body">{{ dependency.version_pattern }}</span>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/file_sha.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/file_sha.vue
new file mode 100644
index 00000000000..a25839be7e1
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/file_sha.vue
@@ -0,0 +1,41 @@
+<script>
+import { s__ } from '~/locale';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+import DetailsRow from '~/vue_shared/components/registry/details_row.vue';
+
+export default {
+ name: 'FileSha',
+ components: {
+ DetailsRow,
+ ClipboardButton,
+ },
+ props: {
+ sha: {
+ type: String,
+ required: true,
+ },
+ title: {
+ type: String,
+ required: true,
+ },
+ },
+ i18n: {
+ copyButtonTitle: s__('PackageRegistry|Copy SHA'),
+ },
+};
+</script>
+
+<template>
+ <details-row dashed>
+ <div class="gl-px-4">
+ {{ title }}:
+ {{ sha }}
+ <clipboard-button
+ :text="sha"
+ :title="$options.i18n.copyButtonTitle"
+ category="tertiary"
+ size="small"
+ />
+ </div>
+ </details-row>
+</template>
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
new file mode 100644
index 00000000000..9ebfbbbf9e5
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/installation_commands.vue
@@ -0,0 +1,55 @@
+<script>
+import { PackageType, TERRAFORM_PACKAGE_TYPE } from '~/packages/shared/constants';
+import TerraformInstallation from '~/packages_and_registries/infrastructure_registry/components/terraform_installation.vue';
+import ComposerInstallation from './composer_installation.vue';
+import ConanInstallation from './conan_installation.vue';
+import MavenInstallation from './maven_installation.vue';
+import NpmInstallation from './npm_installation.vue';
+import NugetInstallation from './nuget_installation.vue';
+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,
+ },
+ 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];
+ },
+ },
+};
+</script>
+
+<template>
+ <div v-if="installationComponent">
+ <component
+ :is="installationComponent"
+ :name="packageEntity.name"
+ :registry-url="npmPath"
+ :help-url="npmHelpPath"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/installation_title.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/installation_title.vue
new file mode 100644
index 00000000000..43133bf7825
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/installation_title.vue
@@ -0,0 +1,38 @@
+<script>
+import PersistedDropdownSelection from '~/vue_shared/components/registry/persisted_dropdown_selection.vue';
+
+export default {
+ name: 'InstallationTitle',
+ components: {
+ PersistedDropdownSelection,
+ },
+ props: {
+ packageType: {
+ type: String,
+ required: true,
+ },
+ options: {
+ type: Array,
+ required: true,
+ },
+ },
+ computed: {
+ storageKey() {
+ return `package_${this.packageType}_installation_instructions`;
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="gl-display-flex gl-justify-content-space-between gl-align-items-center">
+ <h3 class="gl-font-lg">{{ __('Installation') }}</h3>
+ <div>
+ <persisted-dropdown-selection
+ :storage-key="storageKey"
+ :options="options"
+ @change="$emit('change', $event)"
+ />
+ </div>
+ </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
new file mode 100644
index 00000000000..b035e557d21
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/maven_installation.vue
@@ -0,0 +1,152 @@
+<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 CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue';
+
+export default {
+ name: 'MavenInstallation',
+ components: {
+ InstallationTitle,
+ CodeInstruction,
+ GlLink,
+ GlSprintf,
+ },
+ data() {
+ return {
+ instructionType: 'maven',
+ };
+ },
+ computed: {
+ ...mapState(['mavenHelpPath']),
+ ...mapGetters([
+ 'mavenInstallationXml',
+ 'mavenInstallationCommand',
+ 'mavenSetupXml',
+ 'gradleGroovyInstalCommand',
+ 'gradleGroovyAddSourceCommand',
+ 'gradleKotlinInstalCommand',
+ 'gradleKotlinAddSourceCommand',
+ ]),
+ showMaven() {
+ return this.instructionType === 'maven';
+ },
+ showGroovy() {
+ return this.instructionType === 'groovy';
+ },
+ },
+ i18n: {
+ xmlText: s__(
+ `PackageRegistry|Copy and paste this inside your %{codeStart}pom.xml%{codeEnd} %{codeStart}dependencies%{codeEnd} block.`,
+ ),
+ setupText: s__(
+ `PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}pom.xml%{codeEnd} file.`,
+ ),
+ helpText: s__(
+ 'PackageRegistry|For more information on the Maven registry, %{linkStart}see the documentation%{linkEnd}.',
+ ),
+ },
+ trackingActions: { ...TrackingActions },
+ TrackingLabels,
+ installOptions: [
+ { value: 'maven', label: s__('PackageRegistry|Maven XML') },
+ { value: 'groovy', label: s__('PackageRegistry|Gradle Groovy DSL') },
+ { value: 'kotlin', label: s__('PackageRegistry|Gradle Kotlin DSL') },
+ ],
+};
+</script>
+
+<template>
+ <div>
+ <installation-title
+ package-type="maven"
+ :options="$options.installOptions"
+ @change="instructionType = $event"
+ />
+
+ <template v-if="showMaven">
+ <p>
+ <gl-sprintf :message="$options.i18n.xmlText">
+ <template #code="{ content }">
+ <code>{{ content }}</code>
+ </template>
+ </gl-sprintf>
+ </p>
+
+ <code-instruction
+ :instruction="mavenInstallationXml"
+ :copy-text="s__('PackageRegistry|Copy Maven XML')"
+ :tracking-action="$options.trackingActions.COPY_MAVEN_XML"
+ :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ multiline
+ />
+
+ <code-instruction
+ :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"
+ />
+
+ <h3 class="gl-font-lg">{{ s__('PackageRegistry|Registry setup') }}</h3>
+ <p>
+ <gl-sprintf :message="$options.i18n.setupText">
+ <template #code="{ content }">
+ <code>{{ content }}</code>
+ </template>
+ </gl-sprintf>
+ </p>
+ <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"
+ multiline
+ />
+ <gl-sprintf :message="$options.i18n.helpText">
+ <template #link="{ content }">
+ <gl-link :href="mavenHelpPath" target="_blank">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </template>
+ <template v-else-if="showGroovy">
+ <code-instruction
+ class="gl-mb-5"
+ :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"
+ />
+ <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"
+ multiline
+ />
+ </template>
+ <template v-else>
+ <code-instruction
+ class="gl-mb-5"
+ :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"
+ />
+ <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"
+ multiline
+ />
+ </template>
+ </div>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/npm_installation.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/npm_installation.vue
new file mode 100644
index 00000000000..c178d3e97e9
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/npm_installation.vue
@@ -0,0 +1,103 @@
+<script>
+import { GlLink, GlSprintf } from '@gitlab/ui';
+import { mapGetters, mapState } from 'vuex';
+import { s__ } from '~/locale';
+import { NpmManager, TrackingActions, TrackingLabels } from '~/packages/details/constants';
+import InstallationTitle from '~/packages_and_registries/package_registry/components/details/installation_title.vue';
+import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue';
+
+export default {
+ name: 'NpmInstallation',
+ components: {
+ InstallationTitle,
+ CodeInstruction,
+ GlLink,
+ GlSprintf,
+ },
+ data() {
+ return {
+ instructionType: 'npm',
+ };
+ },
+ computed: {
+ ...mapState(['npmHelpPath']),
+ ...mapGetters(['npmInstallationCommand', 'npmSetupCommand']),
+ npmCommand() {
+ return this.npmInstallationCommand(NpmManager.NPM);
+ },
+ npmSetup() {
+ return this.npmSetupCommand(NpmManager.NPM);
+ },
+ yarnCommand() {
+ return this.npmInstallationCommand(NpmManager.YARN);
+ },
+ yarnSetupCommand() {
+ return this.npmSetupCommand(NpmManager.YARN);
+ },
+ showNpm() {
+ return this.instructionType === 'npm';
+ },
+ },
+ i18n: {
+ helpText: s__(
+ 'PackageRegistry|You may also need to setup authentication using an auth token. %{linkStart}See the documentation%{linkEnd} to find out more.',
+ ),
+ },
+ trackingActions: { ...TrackingActions },
+ TrackingLabels,
+ installOptions: [
+ { value: 'npm', label: s__('PackageRegistry|Show NPM commands') },
+ { value: 'yarn', label: s__('PackageRegistry|Show Yarn commands') },
+ ],
+};
+</script>
+
+<template>
+ <div>
+ <installation-title
+ package-type="npm"
+ :options="$options.installOptions"
+ @change="instructionType = $event"
+ />
+
+ <code-instruction
+ v-if="showNpm"
+ :instruction="npmCommand"
+ :copy-text="s__('PackageRegistry|Copy npm command')"
+ :tracking-action="$options.trackingActions.COPY_NPM_INSTALL_COMMAND"
+ :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ />
+
+ <code-instruction
+ v-else
+ :instruction="yarnCommand"
+ :copy-text="s__('PackageRegistry|Copy yarn command')"
+ :tracking-action="$options.trackingActions.COPY_YARN_INSTALL_COMMAND"
+ :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ />
+
+ <h3 class="gl-font-lg">{{ __('Registry setup') }}</h3>
+
+ <code-instruction
+ v-if="showNpm"
+ :instruction="npmSetup"
+ :copy-text="s__('PackageRegistry|Copy npm setup command')"
+ :tracking-action="$options.trackingActions.COPY_NPM_SETUP_COMMAND"
+ :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ />
+
+ <code-instruction
+ v-else
+ :instruction="yarnSetupCommand"
+ :copy-text="s__('PackageRegistry|Copy yarn setup command')"
+ :tracking-action="$options.trackingActions.COPY_YARN_SETUP_COMMAND"
+ :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ />
+
+ <gl-sprintf :message="$options.i18n.helpText">
+ <template #link="{ content }">
+ <gl-link :href="npmHelpPath" target="_blank">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </div>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/nuget_installation.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/nuget_installation.vue
new file mode 100644
index 00000000000..84493790b4d
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/nuget_installation.vue
@@ -0,0 +1,58 @@
+<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 CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue';
+
+export default {
+ name: 'NugetInstallation',
+ components: {
+ InstallationTitle,
+ CodeInstruction,
+ GlLink,
+ GlSprintf,
+ },
+ computed: {
+ ...mapState(['nugetHelpPath']),
+ ...mapGetters(['nugetInstallationCommand', 'nugetSetupCommand']),
+ },
+ i18n: {
+ helpText: s__(
+ 'PackageRegistry|For more information on the NuGet registry, %{linkStart}see the documentation%{linkEnd}.',
+ ),
+ },
+ trackingActions: { ...TrackingActions },
+ TrackingLabels,
+ installOptions: [{ value: 'nuget', label: s__('PackageRegistry|Show Nuget commands') }],
+};
+</script>
+
+<template>
+ <div>
+ <installation-title package-type="nuget" :options="$options.installOptions" />
+
+ <code-instruction
+ :label="s__('PackageRegistry|NuGet Command')"
+ :instruction="nugetInstallationCommand"
+ :copy-text="s__('PackageRegistry|Copy NuGet Command')"
+ :tracking-action="$options.trackingActions.COPY_NUGET_INSTALL_COMMAND"
+ :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ />
+ <h3 class="gl-font-lg">{{ __('Registry setup') }}</h3>
+
+ <code-instruction
+ :label="s__('PackageRegistry|Add NuGet Source')"
+ :instruction="nugetSetupCommand"
+ :copy-text="s__('PackageRegistry|Copy NuGet Setup Command')"
+ :tracking-action="$options.trackingActions.COPY_NUGET_SETUP_COMMAND"
+ :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ />
+ <gl-sprintf :message="$options.i18n.helpText">
+ <template #link="{ content }">
+ <gl-link :href="nugetHelpPath" target="_blank">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </div>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_files.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_files.vue
new file mode 100644
index 00000000000..3c2876e902b
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_files.vue
@@ -0,0 +1,165 @@
+<script>
+import { GlLink, GlTable, GlDropdownItem, GlDropdown, GlIcon, GlButton } from '@gitlab/ui';
+import { last } from 'lodash';
+import { numberToHumanSize } from '~/lib/utils/number_utils';
+import { __ } from '~/locale';
+import FileSha from '~/packages_and_registries/package_registry/components/details/file_sha.vue';
+import Tracking from '~/tracking';
+import FileIcon from '~/vue_shared/components/file_icon.vue';
+import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+
+export default {
+ name: 'PackageFiles',
+ components: {
+ GlLink,
+ GlTable,
+ GlIcon,
+ GlDropdown,
+ GlDropdownItem,
+ GlButton,
+ FileIcon,
+ TimeAgoTooltip,
+ FileSha,
+ },
+ mixins: [Tracking.mixin()],
+ props: {
+ packageFiles: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ canDelete: {
+ type: Boolean,
+ default: false,
+ required: false,
+ },
+ },
+ computed: {
+ filesTableRows() {
+ return this.packageFiles.map((pf) => ({
+ ...pf,
+ size: this.formatSize(pf.size),
+ pipeline: last(pf.pipelines),
+ }));
+ },
+ showCommitColumn() {
+ return this.filesTableRows.some((row) => Boolean(row.pipeline?.id));
+ },
+ filesTableHeaderFields() {
+ return [
+ {
+ key: 'name',
+ label: __('Name'),
+ },
+ {
+ key: 'commit',
+ label: __('Commit'),
+ hide: !this.showCommitColumn,
+ },
+ {
+ key: 'size',
+ label: __('Size'),
+ },
+ {
+ key: 'created',
+ label: __('Created'),
+ class: 'gl-text-right',
+ },
+ {
+ key: 'actions',
+ label: '',
+ hide: !this.canDelete,
+ class: 'gl-text-right',
+ tdClass: 'gl-w-4',
+ },
+ ].filter((c) => !c.hide);
+ },
+ },
+ methods: {
+ formatSize(size) {
+ return numberToHumanSize(size);
+ },
+ hasDetails(item) {
+ return item.file_sha256 || item.file_md5 || item.file_sha1;
+ },
+ },
+ i18n: {
+ deleteFile: __('Delete file'),
+ },
+};
+</script>
+
+<template>
+ <div>
+ <h3 class="gl-font-lg gl-mt-5">{{ __('Files') }}</h3>
+ <gl-table
+ :fields="filesTableHeaderFields"
+ :items="filesTableRows"
+ :tbody-tr-attr="{ 'data-testid': 'file-row' }"
+ >
+ <template #cell(name)="{ item, toggleDetails, detailsShowing }">
+ <gl-button
+ v-if="hasDetails(item)"
+ :icon="detailsShowing ? 'angle-up' : 'angle-down'"
+ :aria-label="detailsShowing ? __('Collapse') : __('Expand')"
+ category="tertiary"
+ size="small"
+ @click="toggleDetails"
+ />
+ <gl-link
+ :href="item.download_path"
+ class="gl-text-gray-500"
+ data-testid="download-link"
+ @click="$emit('download-file')"
+ >
+ <file-icon
+ :file-name="item.file_name"
+ css-classes="gl-relative file-icon"
+ class="gl-mr-1 gl-relative"
+ />
+ <span>{{ item.file_name }}</span>
+ </gl-link>
+ </template>
+
+ <template #cell(commit)="{ item }">
+ <gl-link
+ v-if="item.pipeline && item.pipeline.project"
+ :href="item.pipeline.project.commit_url"
+ class="gl-text-gray-500"
+ data-testid="commit-link"
+ >{{ item.pipeline.git_commit_message }}</gl-link
+ >
+ </template>
+
+ <template #cell(created)="{ item }">
+ <time-ago-tooltip :time="item.created_at" />
+ </template>
+
+ <template #cell(actions)="{ item }">
+ <gl-dropdown category="tertiary" right>
+ <template #button-content>
+ <gl-icon name="ellipsis_v" />
+ </template>
+ <gl-dropdown-item data-testid="delete-file" @click="$emit('delete-file', item)">
+ {{ $options.i18n.deleteFile }}
+ </gl-dropdown-item>
+ </gl-dropdown>
+ </template>
+
+ <template #row-details="{ item }">
+ <div
+ class="gl-display-flex gl-flex-direction-column gl-flex-grow-1 gl-bg-gray-10 gl-rounded-base gl-inset-border-1-gray-100"
+ >
+ <file-sha
+ v-if="item.file_sha256"
+ data-testid="sha-256"
+ title="SHA-256"
+ :sha="item.file_sha256"
+ />
+ <file-sha v-if="item.file_md5" data-testid="md5" title="MD5" :sha="item.file_md5" />
+ <file-sha v-if="item.file_sha1" data-testid="sha-1" title="SHA-1" :sha="item.file_sha1" />
+ </div>
+ </template>
+ </gl-table>
+ </div>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_history.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_history.vue
new file mode 100644
index 00000000000..0d7a73c12f1
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_history.vue
@@ -0,0 +1,167 @@
+<script>
+import { GlLink, GlSprintf } from '@gitlab/ui';
+import { first } from 'lodash';
+import { truncateSha } from '~/lib/utils/text_utility';
+import { s__, n__ } from '~/locale';
+import { HISTORY_PIPELINES_LIMIT } from '~/packages/details/constants';
+import HistoryItem from '~/vue_shared/components/registry/history_item.vue';
+import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+
+export default {
+ name: 'PackageHistory',
+ i18n: {
+ createdOn: s__('PackageRegistry|%{name} version %{version} was first created %{datetime}'),
+ createdByCommitText: s__('PackageRegistry|Created by commit %{link} on branch %{branch}'),
+ createdByPipelineText: s__(
+ 'PackageRegistry|Built by pipeline %{link} triggered %{datetime} by %{author}',
+ ),
+ publishText: s__('PackageRegistry|Published to the %{project} Package Registry %{datetime}'),
+ combinedUpdateText: s__(
+ 'PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}',
+ ),
+ archivedPipelineMessageSingular: s__('PackageRegistry|Package has %{number} archived update'),
+ archivedPipelineMessagePlural: s__('PackageRegistry|Package has %{number} archived updates'),
+ },
+ components: {
+ GlLink,
+ GlSprintf,
+ HistoryItem,
+ TimeAgoTooltip,
+ },
+ props: {
+ packageEntity: {
+ type: Object,
+ required: true,
+ },
+ projectName: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ showDescription: false,
+ };
+ },
+ computed: {
+ pipelines() {
+ return this.packageEntity.pipelines || [];
+ },
+ firstPipeline() {
+ return first(this.pipelines);
+ },
+ lastPipelines() {
+ return this.pipelines.slice(1).slice(-HISTORY_PIPELINES_LIMIT);
+ },
+ showPipelinesInfo() {
+ return Boolean(this.firstPipeline?.id);
+ },
+ archiviedLines() {
+ return Math.max(this.pipelines.length - HISTORY_PIPELINES_LIMIT - 1, 0);
+ },
+ archivedPipelineMessage() {
+ return n__(
+ this.$options.i18n.archivedPipelineMessageSingular,
+ this.$options.i18n.archivedPipelineMessagePlural,
+ this.archiviedLines,
+ );
+ },
+ },
+ methods: {
+ truncate(value) {
+ return truncateSha(value);
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="issuable-discussion">
+ <h3 class="gl-font-lg" data-testid="title">{{ __('History') }}</h3>
+ <ul class="timeline main-notes-list notes gl-mb-4" data-testid="timeline">
+ <history-item icon="clock" data-testid="created-on">
+ <gl-sprintf :message="$options.i18n.createdOn">
+ <template #name>
+ <strong>{{ packageEntity.name }}</strong>
+ </template>
+ <template #version>
+ <strong>{{ packageEntity.version }}</strong>
+ </template>
+ <template #datetime>
+ <time-ago-tooltip :time="packageEntity.created_at" />
+ </template>
+ </gl-sprintf>
+ </history-item>
+
+ <template v-if="showPipelinesInfo">
+ <!-- FIRST PIPELINE BLOCK -->
+ <history-item icon="commit" data-testid="first-pipeline-commit">
+ <gl-sprintf :message="$options.i18n.createdByCommitText">
+ <template #link>
+ <gl-link :href="firstPipeline.project.commit_url"
+ >#{{ truncate(firstPipeline.sha) }}</gl-link
+ >
+ </template>
+ <template #branch>
+ <strong>{{ firstPipeline.ref }}</strong>
+ </template>
+ </gl-sprintf>
+ </history-item>
+ <history-item icon="pipeline" data-testid="first-pipeline-pipeline">
+ <gl-sprintf :message="$options.i18n.createdByPipelineText">
+ <template #link>
+ <gl-link :href="firstPipeline.project.pipeline_url">#{{ firstPipeline.id }}</gl-link>
+ </template>
+ <template #datetime>
+ <time-ago-tooltip :time="firstPipeline.created_at" />
+ </template>
+ <template #author>{{ firstPipeline.user.name }}</template>
+ </gl-sprintf>
+ </history-item>
+ </template>
+
+ <!-- PUBLISHED LINE -->
+ <history-item icon="package" data-testid="published">
+ <gl-sprintf :message="$options.i18n.publishText">
+ <template #project>
+ <strong>{{ projectName }}</strong>
+ </template>
+ <template #datetime>
+ <time-ago-tooltip :time="packageEntity.created_at" />
+ </template>
+ </gl-sprintf>
+ </history-item>
+
+ <history-item v-if="archiviedLines" icon="history" data-testid="archived">
+ <gl-sprintf :message="archivedPipelineMessage">
+ <template #number>
+ <strong>{{ archiviedLines }}</strong>
+ </template>
+ </gl-sprintf>
+ </history-item>
+
+ <!-- PIPELINES LIST ENTRIES -->
+ <history-item
+ v-for="pipeline in lastPipelines"
+ :key="pipeline.id"
+ icon="pencil"
+ data-testid="pipeline-entry"
+ >
+ <gl-sprintf :message="$options.i18n.combinedUpdateText">
+ <template #link>
+ <gl-link :href="pipeline.project.commit_url">#{{ truncate(pipeline.sha) }}</gl-link>
+ </template>
+ <template #branch>
+ <strong>{{ pipeline.ref }}</strong>
+ </template>
+ <template #pipeline>
+ <gl-link :href="pipeline.project.pipeline_url">#{{ pipeline.id }}</gl-link>
+ </template>
+ <template #datetime>
+ <time-ago-tooltip :time="pipeline.created_at" />
+ </template>
+ </gl-sprintf>
+ </history-item>
+ </ul>
+ </div>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/pypi_installation.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/pypi_installation.vue
new file mode 100644
index 00000000000..21eba44a720
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/pypi_installation.vue
@@ -0,0 +1,71 @@
+<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 CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue';
+
+export default {
+ name: 'PyPiInstallation',
+ components: {
+ InstallationTitle,
+ CodeInstruction,
+ GlLink,
+ GlSprintf,
+ },
+ computed: {
+ ...mapState(['pypiHelpPath']),
+ ...mapGetters(['pypiPipCommand', 'pypiSetupCommand']),
+ },
+ i18n: {
+ setupText: s__(
+ `PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file.`,
+ ),
+ helpText: s__(
+ 'PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}.',
+ ),
+ },
+ trackingActions: { ...TrackingActions },
+ TrackingLabels,
+ installOptions: [{ value: 'pypi', label: s__('PackageRegistry|Show PyPi commands') }],
+};
+</script>
+
+<template>
+ <div>
+ <installation-title package-type="pypi" :options="$options.installOptions" />
+
+ <code-instruction
+ :label="s__('PackageRegistry|Pip Command')"
+ :instruction="pypiPipCommand"
+ :copy-text="s__('PackageRegistry|Copy Pip command')"
+ data-testid="pip-command"
+ :tracking-action="$options.trackingActions.COPY_PIP_INSTALL_COMMAND"
+ :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ />
+
+ <h3 class="gl-font-lg">{{ __('Registry setup') }}</h3>
+ <p>
+ <gl-sprintf :message="$options.i18n.setupText">
+ <template #code="{ content }">
+ <code>{{ content }}</code>
+ </template>
+ </gl-sprintf>
+ </p>
+
+ <code-instruction
+ :instruction="pypiSetupCommand"
+ :copy-text="s__('PackageRegistry|Copy .pypirc content')"
+ data-testid="pypi-setup-content"
+ multiline
+ :tracking-action="$options.trackingActions.COPY_PYPI_SETUP_COMMAND"
+ :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ />
+ <gl-sprintf :message="$options.i18n.helpText">
+ <template #link="{ content }">
+ <gl-link :href="pypiHelpPath" target="_blank">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </div>
+</template>
diff --git a/app/assets/javascripts/pages/jira_connect/branches/new/index.js b/app/assets/javascripts/pages/jira_connect/branches/new/index.js
new file mode 100644
index 00000000000..f8c3ec63f1f
--- /dev/null
+++ b/app/assets/javascripts/pages/jira_connect/branches/new/index.js
@@ -0,0 +1,3 @@
+import initJiraConnectBranches from '~/jira_connect/branches';
+
+initJiraConnectBranches();
diff --git a/app/assets/javascripts/security_configuration/components/auto_dev_ops_enabled_alert.vue b/app/assets/javascripts/security_configuration/components/auto_dev_ops_enabled_alert.vue
new file mode 100644
index 00000000000..7192108f7c5
--- /dev/null
+++ b/app/assets/javascripts/security_configuration/components/auto_dev_ops_enabled_alert.vue
@@ -0,0 +1,30 @@
+<script>
+import { GlAlert, GlLink, GlSprintf } from '@gitlab/ui';
+import { s__ } from '~/locale';
+
+export default {
+ components: {
+ GlAlert,
+ GlLink,
+ GlSprintf,
+ },
+ inject: ['autoDevopsHelpPagePath'],
+ i18n: {
+ body: s__(
+ 'AutoDevopsAlert|Security testing tools enabled with %{linkStart}Auto DevOps%{linkEnd}',
+ ),
+ },
+};
+</script>
+
+<template>
+ <gl-alert variant="success" @dismiss="$emit('dismiss')">
+ <gl-sprintf :message="$options.i18n.body">
+ <template #link="{ content }">
+ <gl-link :href="autoDevopsHelpPagePath">
+ {{ content }}
+ </gl-link>
+ </template>
+ </gl-sprintf>
+ </gl-alert>
+</template>
diff --git a/app/assets/javascripts/security_configuration/components/constants.js b/app/assets/javascripts/security_configuration/components/constants.js
index d80c67da8b1..fda18fb8009 100644
--- a/app/assets/javascripts/security_configuration/components/constants.js
+++ b/app/assets/javascripts/security_configuration/components/constants.js
@@ -305,3 +305,6 @@ export const featureToMutationMap = {
}),
},
};
+
+export const AUTO_DEVOPS_ENABLED_ALERT_DISMISSED_STORAGE_KEY =
+ 'security_configuration_auto_devops_enabled_dismissed_projects';
diff --git a/app/assets/javascripts/security_configuration/components/redesigned_app.vue b/app/assets/javascripts/security_configuration/components/redesigned_app.vue
index 915da378a4f..6c70a8c33db 100644
--- a/app/assets/javascripts/security_configuration/components/redesigned_app.vue
+++ b/app/assets/javascripts/security_configuration/components/redesigned_app.vue
@@ -1,8 +1,11 @@
<script>
import { GlTab, GlTabs, GlSprintf, GlLink } from '@gitlab/ui';
import { __, s__ } from '~/locale';
+import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import UserCalloutDismisser from '~/vue_shared/components/user_callout_dismisser.vue';
import AutoDevOpsAlert from './auto_dev_ops_alert.vue';
+import AutoDevOpsEnabledAlert from './auto_dev_ops_enabled_alert.vue';
+import { AUTO_DEVOPS_ENABLED_ALERT_DISMISSED_STORAGE_KEY } from './constants';
import FeatureCard from './feature_card.vue';
import SectionLayout from './section_layout.vue';
import UpgradeBanner from './upgrade_banner.vue';
@@ -25,16 +28,19 @@ export const i18n = {
export default {
i18n,
components: {
- GlTab,
+ AutoDevOpsAlert,
+ AutoDevOpsEnabledAlert,
+ FeatureCard,
GlLink,
- GlTabs,
GlSprintf,
- FeatureCard,
+ GlTab,
+ GlTabs,
+ LocalStorageSync,
SectionLayout,
UpgradeBanner,
- AutoDevOpsAlert,
UserCalloutDismisser,
},
+ inject: ['projectPath'],
props: {
augmentedSecurityFeatures: {
type: Array,
@@ -70,6 +76,11 @@ export default {
default: '',
},
},
+ data() {
+ return {
+ autoDevopsEnabledAlertDismissedProjects: [],
+ };
+ },
computed: {
canUpgrade() {
return [...this.augmentedSecurityFeatures, ...this.augmentedComplianceFeatures].some(
@@ -82,12 +93,32 @@ export default {
shouldShowDevopsAlert() {
return !this.autoDevopsEnabled && !this.gitlabCiPresent && this.canEnableAutoDevops;
},
+ shouldShowAutoDevopsEnabledAlert() {
+ return (
+ this.autoDevopsEnabled &&
+ !this.autoDevopsEnabledAlertDismissedProjects.includes(this.projectPath)
+ );
+ },
},
+ methods: {
+ dismissAutoDevopsEnabledAlert() {
+ const dismissedProjects = new Set(this.autoDevopsEnabledAlertDismissedProjects);
+ dismissedProjects.add(this.projectPath);
+ this.autoDevopsEnabledAlertDismissedProjects = Array.from(dismissedProjects);
+ },
+ },
+ autoDevopsEnabledAlertStorageKey: AUTO_DEVOPS_ENABLED_ALERT_DISMISSED_STORAGE_KEY,
};
</script>
<template>
<article>
+ <local-storage-sync
+ v-model="autoDevopsEnabledAlertDismissedProjects"
+ :storage-key="$options.autoDevopsEnabledAlertStorageKey"
+ as-json
+ />
+
<user-callout-dismisser
v-if="shouldShowDevopsAlert"
feature-name="security_configuration_devops_alert"
@@ -105,8 +136,14 @@ export default {
</template>
</user-callout-dismisser>
- <gl-tabs content-class="gl-pt-6">
+ <gl-tabs content-class="gl-pt-0">
<gl-tab data-testid="security-testing-tab" :title="$options.i18n.securityTesting">
+ <auto-dev-ops-enabled-alert
+ v-if="shouldShowAutoDevopsEnabledAlert"
+ class="gl-mt-3"
+ @dismiss="dismissAutoDevopsEnabledAlert"
+ />
+
<section-layout :heading="$options.i18n.securityTesting">
<template #description>
<p>
diff --git a/app/assets/javascripts/security_configuration/components/section_layout.vue b/app/assets/javascripts/security_configuration/components/section_layout.vue
index e351f9b9d8d..1fe8dd862a0 100644
--- a/app/assets/javascripts/security_configuration/components/section_layout.vue
+++ b/app/assets/javascripts/security_configuration/components/section_layout.vue
@@ -11,7 +11,7 @@ export default {
</script>
<template>
- <div class="row gl-line-height-20">
+ <div class="row gl-line-height-20 gl-pt-6">
<div class="col-lg-4">
<h2 class="gl-font-size-h2 gl-mt-0">{{ heading }}</h2>
<slot name="description"></slot>
diff --git a/app/controllers/groups/dependency_proxy_for_containers_controller.rb b/app/controllers/groups/dependency_proxy_for_containers_controller.rb
index e2c104f88a4..e341b9d75e0 100644
--- a/app/controllers/groups/dependency_proxy_for_containers_controller.rb
+++ b/app/controllers/groups/dependency_proxy_for_containers_controller.rb
@@ -4,6 +4,7 @@ class Groups::DependencyProxyForContainersController < Groups::ApplicationContro
include DependencyProxy::Auth
include DependencyProxy::GroupAccess
include SendFileUpload
+ include ::PackagesHelper # for event tracking
before_action :ensure_token_granted!
before_action :ensure_feature_enabled!
@@ -22,6 +23,8 @@ class Groups::DependencyProxyForContainersController < Groups::ApplicationContro
response.headers['Etag'] = "\"#{result[:manifest].digest}\""
content_type = result[:manifest].content_type
+ event_name = tracking_event_name(object_type: :manifest, from_cache: result[:from_cache])
+ track_package_event(event_name, :dependency_proxy, namespace: group, user: current_user)
send_upload(
result[:manifest].file,
proxy: true,
@@ -38,6 +41,8 @@ class Groups::DependencyProxyForContainersController < Groups::ApplicationContro
.new(group, image, token, params[:sha]).execute
if result[:status] == :success
+ event_name = tracking_event_name(object_type: :blob, from_cache: result[:from_cache])
+ track_package_event(event_name, :dependency_proxy, namespace: group, user: current_user)
send_upload(result[:blob].file)
else
head result[:http_status]
@@ -54,6 +59,13 @@ class Groups::DependencyProxyForContainersController < Groups::ApplicationContro
params[:tag]
end
+ def tracking_event_name(object_type:, from_cache:)
+ event_name = "pull_#{object_type}"
+ event_name = "#{event_name}_from_cache" if from_cache
+
+ event_name
+ end
+
def dependency_proxy
@dependency_proxy ||=
group.dependency_proxy_setting || group.create_dependency_proxy_setting
diff --git a/app/controllers/jira_connect/app_descriptor_controller.rb b/app/controllers/jira_connect/app_descriptor_controller.rb
index 0de42ad2452..ec3327cc20e 100644
--- a/app/controllers/jira_connect/app_descriptor_controller.rb
+++ b/app/controllers/jira_connect/app_descriptor_controller.rb
@@ -44,27 +44,14 @@ class JiraConnect::AppDescriptorController < JiraConnect::ApplicationController
def modules
modules = {
- jiraDevelopmentTool: {
- key: 'gitlab-development-tool',
- application: {
- value: 'GitLab'
- },
- name: {
- value: 'GitLab'
- },
- url: HOME_URL,
- logoUrl: logo_url,
- capabilities: %w(branch commit pull_request)
- },
postInstallPage: {
key: 'gitlab-configuration',
- name: {
- value: 'GitLab Configuration'
- },
+ name: { value: 'GitLab Configuration' },
url: relative_to_base_path(jira_connect_subscriptions_path)
}
}
+ modules.merge!(development_tool_module)
modules.merge!(build_information_module)
modules.merge!(deployment_information_module)
modules.merge!(feature_flag_module)
@@ -76,6 +63,29 @@ class JiraConnect::AppDescriptorController < JiraConnect::ApplicationController
view_context.image_url('gitlab_logo.png')
end
+ # See https://developer.atlassian.com/cloud/jira/software/modules/development-tool/
+ def development_tool_module
+ actions = {}
+
+ if JiraConnect::BranchesController.feature_enabled?(current_user)
+ actions[:createBranch] = {
+ templateUrl: new_jira_connect_branch_url + '?issue_key={issue.key}&issue_summary={issue.summary}'
+ }
+ end
+
+ {
+ jiraDevelopmentTool: {
+ actions: actions,
+ key: 'gitlab-development-tool',
+ application: { value: 'GitLab' },
+ name: { value: 'GitLab' },
+ url: HOME_URL,
+ logoUrl: logo_url,
+ capabilities: %w(branch commit pull_request)
+ }
+ }
+ end
+
# See: https://developer.atlassian.com/cloud/jira/software/modules/deployment/
def deployment_information_module
{
@@ -92,9 +102,7 @@ class JiraConnect::AppDescriptorController < JiraConnect::ApplicationController
{
jiraFeatureFlagInfoProvider: common_module_properties.merge(
actions: {}, # TODO: create, link and list feature flags https://gitlab.com/gitlab-org/gitlab/-/issues/297386
- name: {
- value: 'GitLab Feature Flags'
- },
+ name: { value: 'GitLab Feature Flags' },
key: 'gitlab-feature-flags'
)
}
diff --git a/app/controllers/jira_connect/branches_controller.rb b/app/controllers/jira_connect/branches_controller.rb
new file mode 100644
index 00000000000..7d7faae62a5
--- /dev/null
+++ b/app/controllers/jira_connect/branches_controller.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+# NOTE: This controller does not inherit from JiraConnect::ApplicationController
+# because we don't receive a JWT for this action, so we rely on standard GitLab authentication.
+class JiraConnect::BranchesController < ApplicationController
+ before_action :feature_enabled!
+
+ feature_category :integrations
+
+ def new
+ return unless params[:issue_key].present?
+
+ @branch_name = Issue.to_branch_name(
+ params[:issue_key],
+ params[:issue_summary]
+ )
+ end
+
+ def self.feature_enabled?(user)
+ Feature.enabled?(:jira_connect_create_branch, user, default_enabled: :yaml)
+ end
+
+ private
+
+ def feature_enabled!
+ render_404 unless self.class.feature_enabled?(current_user)
+ end
+end
diff --git a/app/graphql/mutations/packages/destroy_file.rb b/app/graphql/mutations/packages/destroy_file.rb
new file mode 100644
index 00000000000..35a486666d5
--- /dev/null
+++ b/app/graphql/mutations/packages/destroy_file.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Packages
+ class DestroyFile < ::Mutations::BaseMutation
+ graphql_name 'DestroyPackageFile'
+
+ authorize :destroy_package
+
+ argument :id,
+ ::Types::GlobalIDType[::Packages::PackageFile],
+ required: true,
+ description: 'ID of the Package file.'
+
+ def resolve(id:)
+ package_file = authorized_find!(id: id)
+
+ if package_file.destroy
+ return { errors: [] }
+ end
+
+ { errors: package_file.errors.full_messages }
+ end
+
+ private
+
+ def find_object(id:)
+ # TODO: remove this line when the compatibility layer is removed
+ # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
+ id = ::Types::GlobalIDType[::Packages::PackageFile].coerce_isolated_input(id)
+ GitlabSchema.find_by_gid(id)
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb
index df693fafbb9..812943e0a1e 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -107,6 +107,7 @@ module Types
mount_mutation Mutations::Namespace::PackageSettings::Update
mount_mutation Mutations::UserCallouts::Create
mount_mutation Mutations::Packages::Destroy
+ mount_mutation Mutations::Packages::DestroyFile
mount_mutation Mutations::Echo
end
end
diff --git a/app/graphql/types/query_complexity_type.rb b/app/graphql/types/query_complexity_type.rb
index 82809fac22f..3f58a15aef7 100644
--- a/app/graphql/types/query_complexity_type.rb
+++ b/app/graphql/types/query_complexity_type.rb
@@ -9,7 +9,7 @@ module Types
alias_method :query, :object
- field :limit, GraphQL::INT_TYPE,
+ field :limit, GraphQL::Types::Int,
null: true,
method: :max_complexity,
see: {
@@ -18,7 +18,7 @@ module Types
},
description: 'GraphQL query complexity limit.'
- field :score, GraphQL::INT_TYPE,
+ field :score, GraphQL::Types::Int,
null: true,
description: 'GraphQL query complexity score.'
diff --git a/app/graphql/types/release_asset_link_shared_input_arguments.rb b/app/graphql/types/release_asset_link_shared_input_arguments.rb
index 4aa247e47cc..37a6cdd55c9 100644
--- a/app/graphql/types/release_asset_link_shared_input_arguments.rb
+++ b/app/graphql/types/release_asset_link_shared_input_arguments.rb
@@ -5,15 +5,15 @@ module Types
extend ActiveSupport::Concern
included do
- argument :name, GraphQL::STRING_TYPE,
+ argument :name, GraphQL::Types::String,
required: true,
description: 'Name of the asset link.'
- argument :url, GraphQL::STRING_TYPE,
+ argument :url, GraphQL::Types::String,
required: true,
description: 'URL of the asset link.'
- argument :direct_asset_path, GraphQL::STRING_TYPE,
+ argument :direct_asset_path, GraphQL::Types::String,
required: false, as: :filepath,
description: 'Relative path for a direct asset link.'
diff --git a/app/graphql/types/release_asset_link_type.rb b/app/graphql/types/release_asset_link_type.rb
index 829e7e246db..02961f2f73f 100644
--- a/app/graphql/types/release_asset_link_type.rb
+++ b/app/graphql/types/release_asset_link_type.rb
@@ -7,20 +7,20 @@ module Types
authorize :read_release
- field :id, GraphQL::ID_TYPE, null: false,
+ field :id, GraphQL::Types::ID, null: false,
description: 'ID of the link.'
- field :name, GraphQL::STRING_TYPE, null: true,
+ field :name, GraphQL::Types::String, null: true,
description: 'Name of the link.'
- field :url, GraphQL::STRING_TYPE, null: true,
+ field :url, GraphQL::Types::String, null: true,
description: 'URL of the link.'
field :link_type, Types::ReleaseAssetLinkTypeEnum, null: true,
description: 'Type of the link: `other`, `runbook`, `image`, `package`; defaults to `other`.'
- field :external, GraphQL::BOOLEAN_TYPE, null: true, method: :external?,
+ field :external, GraphQL::Types::Boolean, null: true, method: :external?,
description: 'Indicates the link points to an external resource.'
- field :direct_asset_url, GraphQL::STRING_TYPE, null: true,
+ field :direct_asset_url, GraphQL::Types::String, null: true,
description: 'Direct asset URL of the link.'
- field :direct_asset_path, GraphQL::STRING_TYPE, null: true, method: :filepath,
+ field :direct_asset_path, GraphQL::Types::String, null: true, method: :filepath,
description: 'Relative path for the direct asset link.'
def direct_asset_url
diff --git a/app/graphql/types/release_assets_type.rb b/app/graphql/types/release_assets_type.rb
index d847d9842d5..ea6ee0b5fd9 100644
--- a/app/graphql/types/release_assets_type.rb
+++ b/app/graphql/types/release_assets_type.rb
@@ -11,7 +11,7 @@ module Types
present_using ReleasePresenter
- field :count, GraphQL::INT_TYPE, null: true, method: :assets_count,
+ field :count, GraphQL::Types::Int, null: true, method: :assets_count,
description: 'Number of assets of the release.'
field :links, Types::ReleaseAssetLinkType.connection_type, null: true, method: :sorted_links,
description: 'Asset links of the release.'
diff --git a/app/graphql/types/release_links_type.rb b/app/graphql/types/release_links_type.rb
index a51b80e1e13..7830e29f3cd 100644
--- a/app/graphql/types/release_links_type.rb
+++ b/app/graphql/types/release_links_type.rb
@@ -10,20 +10,20 @@ module Types
present_using ReleasePresenter
- field :self_url, GraphQL::STRING_TYPE, null: true,
+ field :self_url, GraphQL::Types::String, null: true,
description: 'HTTP URL of the release.'
- field :edit_url, GraphQL::STRING_TYPE, null: true,
+ field :edit_url, GraphQL::Types::String, null: true,
description: "HTTP URL of the release's edit page.",
authorize: :update_release
- field :opened_merge_requests_url, GraphQL::STRING_TYPE, null: true,
+ field :opened_merge_requests_url, GraphQL::Types::String, null: true,
description: 'HTTP URL of the merge request page, filtered by this release and `state=open`.'
- field :merged_merge_requests_url, GraphQL::STRING_TYPE, null: true,
+ field :merged_merge_requests_url, GraphQL::Types::String, null: true,
description: 'HTTP URL of the merge request page , filtered by this release and `state=merged`.'
- field :closed_merge_requests_url, GraphQL::STRING_TYPE, null: true,
+ field :closed_merge_requests_url, GraphQL::Types::String, null: true,
description: 'HTTP URL of the merge request page , filtered by this release and `state=closed`.'
- field :opened_issues_url, GraphQL::STRING_TYPE, null: true,
+ field :opened_issues_url, GraphQL::Types::String, null: true,
description: 'HTTP URL of the issues page, filtered by this release and `state=open`.'
- field :closed_issues_url, GraphQL::STRING_TYPE, null: true,
+ field :closed_issues_url, GraphQL::Types::String, null: true,
description: 'HTTP URL of the issues page, filtered by this release and `state=closed`.'
end
end
diff --git a/app/graphql/types/release_source_type.rb b/app/graphql/types/release_source_type.rb
index 10fc202514d..fd29a69d72a 100644
--- a/app/graphql/types/release_source_type.rb
+++ b/app/graphql/types/release_source_type.rb
@@ -7,9 +7,9 @@ module Types
authorize :download_code
- field :format, GraphQL::STRING_TYPE, null: true,
+ field :format, GraphQL::Types::String, null: true,
description: 'Format of the source.'
- field :url, GraphQL::STRING_TYPE, null: true,
+ field :url, GraphQL::Types::String, null: true,
description: 'Download URL of the source.'
end
end
diff --git a/app/graphql/types/release_type.rb b/app/graphql/types/release_type.rb
index 81813a10a3e..5e8f00b2b0a 100644
--- a/app/graphql/types/release_type.rb
+++ b/app/graphql/types/release_type.rb
@@ -13,22 +13,22 @@ module Types
present_using ReleasePresenter
- field :tag_name, GraphQL::STRING_TYPE, null: true, method: :tag,
+ field :tag_name, GraphQL::Types::String, null: true, method: :tag,
description: 'Name of the tag associated with the release.',
authorize: :download_code
- field :tag_path, GraphQL::STRING_TYPE, null: true,
+ field :tag_path, GraphQL::Types::String, null: true,
description: 'Relative web path to the tag associated with the release.',
authorize: :download_code
- field :description, GraphQL::STRING_TYPE, null: true,
+ field :description, GraphQL::Types::String, null: true,
description: 'Description (also known as "release notes") of the release.'
markdown_field :description_html, null: true
- field :name, GraphQL::STRING_TYPE, null: true,
+ field :name, GraphQL::Types::String, null: true,
description: 'Name of the release.'
field :created_at, Types::TimeType, null: true,
description: 'Timestamp of when the release was created.'
field :released_at, Types::TimeType, null: true,
description: 'Timestamp of when the release was released.'
- field :upcoming_release, GraphQL::BOOLEAN_TYPE, null: true, method: :upcoming_release?,
+ field :upcoming_release, GraphQL::Types::Boolean, null: true, method: :upcoming_release?,
description: 'Indicates the release is an upcoming release.'
field :assets, Types::ReleaseAssetsType, null: true, method: :itself,
description: 'Assets of the release.'
diff --git a/app/graphql/types/repository/blob_type.rb b/app/graphql/types/repository/blob_type.rb
index 8ed97d7e663..b6a1a91fd7a 100644
--- a/app/graphql/types/repository/blob_type.rb
+++ b/app/graphql/types/repository/blob_type.rb
@@ -8,67 +8,67 @@ module Types
graphql_name 'RepositoryBlob'
- field :id, GraphQL::ID_TYPE, null: false,
+ field :id, GraphQL::Types::ID, null: false,
description: 'ID of the blob.'
- field :oid, GraphQL::STRING_TYPE, null: false, method: :id,
+ field :oid, GraphQL::Types::String, null: false, method: :id,
description: 'OID of the blob.'
- field :path, GraphQL::STRING_TYPE, null: false,
+ field :path, GraphQL::Types::String, null: false,
description: 'Path of the blob.'
- field :name, GraphQL::STRING_TYPE,
+ field :name, GraphQL::Types::String,
description: 'Blob name.',
null: true
- field :mode, type: GraphQL::STRING_TYPE,
+ field :mode, type: GraphQL::Types::String,
description: 'Blob mode.',
null: true
- field :lfs_oid, GraphQL::STRING_TYPE, null: true,
+ field :lfs_oid, GraphQL::Types::String, null: true,
calls_gitaly: true,
description: 'LFS OID of the blob.'
- field :web_path, GraphQL::STRING_TYPE, null: true,
+ field :web_path, GraphQL::Types::String, null: true,
description: 'Web path of the blob.'
- field :ide_edit_path, GraphQL::STRING_TYPE, null: true,
+ field :ide_edit_path, GraphQL::Types::String, null: true,
description: 'Web path to edit this blob in the Web IDE.'
- field :fork_and_edit_path, GraphQL::STRING_TYPE, null: true,
+ field :fork_and_edit_path, GraphQL::Types::String, null: true,
description: 'Web path to edit this blob using a forked project.'
- field :ide_fork_and_edit_path, GraphQL::STRING_TYPE, null: true,
+ field :ide_fork_and_edit_path, GraphQL::Types::String, null: true,
description: 'Web path to edit this blob in the Web IDE using a forked project.'
- field :size, GraphQL::INT_TYPE, null: true,
+ field :size, GraphQL::Types::Int, null: true,
description: 'Size (in bytes) of the blob.'
- field :raw_size, GraphQL::INT_TYPE, null: true,
+ field :raw_size, GraphQL::Types::Int, null: true,
description: 'Size (in bytes) of the blob, or the blob target if stored externally.'
- field :raw_blob, GraphQL::STRING_TYPE, null: true, method: :data,
+ field :raw_blob, GraphQL::Types::String, null: true, method: :data,
description: 'The raw content of the blob.'
- field :raw_text_blob, GraphQL::STRING_TYPE, null: true, method: :text_only_data,
+ field :raw_text_blob, GraphQL::Types::String, null: true, method: :text_only_data,
description: 'The raw content of the blob, if the blob is text data.'
- field :stored_externally, GraphQL::BOOLEAN_TYPE, null: true, method: :stored_externally?,
+ field :stored_externally, GraphQL::Types::Boolean, null: true, method: :stored_externally?,
description: "Whether the blob's content is stored externally (for instance, in LFS)."
- field :edit_blob_path, GraphQL::STRING_TYPE, null: true,
+ field :edit_blob_path, GraphQL::Types::String, null: true,
description: 'Web path to edit the blob in the old-style editor.'
- field :raw_path, GraphQL::STRING_TYPE, null: true,
+ field :raw_path, GraphQL::Types::String, null: true,
description: 'Web path to download the raw blob.'
- field :external_storage_url, GraphQL::STRING_TYPE, null: true,
+ field :external_storage_url, GraphQL::Types::String, null: true,
description: 'Web path to download the raw blob via external storage, if enabled.'
- field :replace_path, GraphQL::STRING_TYPE, null: true,
+ field :replace_path, GraphQL::Types::String, null: true,
description: 'Web path to replace the blob content.'
- field :file_type, GraphQL::STRING_TYPE, null: true,
+ field :file_type, GraphQL::Types::String, null: true,
description: 'The expected format of the blob based on the extension.'
field :simple_viewer, type: Types::BlobViewerType,
@@ -79,12 +79,12 @@ module Types
description: 'Blob content rich viewer.',
null: true
- field :plain_data, GraphQL::STRING_TYPE,
+ field :plain_data, GraphQL::Types::String,
description: 'Blob plain highlighted data.',
null: true,
calls_gitaly: true
- field :can_modify_blob, GraphQL::BOOLEAN_TYPE, null: true, method: :can_modify_blob?,
+ field :can_modify_blob, GraphQL::Types::Boolean, null: true, method: :can_modify_blob?,
calls_gitaly: true,
description: 'Whether the current user can modify the blob.'
diff --git a/app/graphql/types/repository_type.rb b/app/graphql/types/repository_type.rb
index 9d896888fa7..466e3e45cb9 100644
--- a/app/graphql/types/repository_type.rb
+++ b/app/graphql/types/repository_type.rb
@@ -6,20 +6,20 @@ module Types
authorize :download_code
- field :root_ref, GraphQL::STRING_TYPE, null: true, calls_gitaly: true,
+ field :root_ref, GraphQL::Types::String, null: true, calls_gitaly: true,
description: 'Default branch of the repository.'
- field :empty, GraphQL::BOOLEAN_TYPE, null: false, method: :empty?, calls_gitaly: true,
+ field :empty, GraphQL::Types::Boolean, null: false, method: :empty?, calls_gitaly: true,
description: 'Indicates repository has no visible content.'
- field :exists, GraphQL::BOOLEAN_TYPE, null: false, method: :exists?, calls_gitaly: true,
+ field :exists, GraphQL::Types::Boolean, null: false, method: :exists?, calls_gitaly: true,
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 :blobs, Types::Repository::BlobType.connection_type, null: true, resolver: Resolvers::BlobsResolver, calls_gitaly: true,
description: 'Blobs contained within the repository'
- field :branch_names, [GraphQL::STRING_TYPE], null: true, calls_gitaly: true,
+ field :branch_names, [GraphQL::Types::String], null: true, calls_gitaly: true,
complexity: 170, description: 'Names of branches available in this repository that match the search pattern.',
resolver: Resolvers::RepositoryBranchNamesResolver
- field :disk_path, GraphQL::STRING_TYPE,
+ field :disk_path, GraphQL::Types::String,
description: 'Shows a disk path of the repository.',
null: true,
authorize: :read_storage_disk_path
diff --git a/app/graphql/types/resolvable_interface.rb b/app/graphql/types/resolvable_interface.rb
index a9d745c2bc1..42784aa5e00 100644
--- a/app/graphql/types/resolvable_interface.rb
+++ b/app/graphql/types/resolvable_interface.rb
@@ -16,10 +16,10 @@ module Types
Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.resolved_by_id).find
end
- field :resolved, GraphQL::BOOLEAN_TYPE, null: false,
+ field :resolved, GraphQL::Types::Boolean, null: false,
description: 'Indicates if the object is resolved.',
method: :resolved?
- field :resolvable, GraphQL::BOOLEAN_TYPE, null: false,
+ field :resolvable, GraphQL::Types::Boolean, null: false,
description: 'Indicates if the object can be resolved.',
method: :resolvable?
field :resolved_at, Types::TimeType, null: true,
diff --git a/app/graphql/types/snippet_type.rb b/app/graphql/types/snippet_type.rb
index 7606bdbc46d..c345aea08bd 100644
--- a/app/graphql/types/snippet_type.rb
+++ b/app/graphql/types/snippet_type.rb
@@ -17,7 +17,7 @@ module Types
description: 'ID of the snippet.',
null: false
- field :title, GraphQL::STRING_TYPE,
+ field :title, GraphQL::Types::String,
description: 'Title of the snippet.',
null: false
@@ -33,11 +33,11 @@ module Types
description: 'The owner of the snippet.',
null: true
- field :file_name, GraphQL::STRING_TYPE,
+ field :file_name, GraphQL::Types::String,
description: 'File Name of the snippet.',
null: true
- field :description, GraphQL::STRING_TYPE,
+ field :description, GraphQL::Types::String,
description: 'Description of the snippet.',
null: true
@@ -53,11 +53,11 @@ module Types
description: 'Timestamp this snippet was updated.',
null: false
- field :web_url, type: GraphQL::STRING_TYPE,
+ field :web_url, type: GraphQL::Types::String,
description: 'Web URL of the snippet.',
null: false
- field :raw_url, type: GraphQL::STRING_TYPE,
+ field :raw_url, type: GraphQL::Types::String,
description: 'Raw URL of the snippet.',
null: false
@@ -67,12 +67,12 @@ module Types
null: true,
resolver: Resolvers::Snippets::BlobsResolver
- field :ssh_url_to_repo, type: GraphQL::STRING_TYPE,
+ field :ssh_url_to_repo, type: GraphQL::Types::String,
description: 'SSH URL to the snippet repository.',
calls_gitaly: true,
null: true
- field :http_url_to_repo, type: GraphQL::STRING_TYPE,
+ field :http_url_to_repo, type: GraphQL::Types::String,
description: 'HTTP URL to the snippet repository.',
calls_gitaly: true,
null: true
diff --git a/app/graphql/types/snippets/blob_action_input_type.rb b/app/graphql/types/snippets/blob_action_input_type.rb
index 13eade3dcc4..45dc4be8451 100644
--- a/app/graphql/types/snippets/blob_action_input_type.rb
+++ b/app/graphql/types/snippets/blob_action_input_type.rb
@@ -10,15 +10,15 @@ module Types
description: 'Type of input action.',
required: true
- argument :previous_path, GraphQL::STRING_TYPE,
+ argument :previous_path, GraphQL::Types::String,
description: 'Previous path of the snippet file.',
required: false
- argument :file_path, GraphQL::STRING_TYPE,
+ argument :file_path, GraphQL::Types::String,
description: 'Path of the snippet file.',
required: true
- argument :content, GraphQL::STRING_TYPE,
+ argument :content, GraphQL::Types::String,
description: 'Snippet file content.',
required: false
end
diff --git a/app/graphql/types/snippets/blob_type.rb b/app/graphql/types/snippets/blob_type.rb
index 1335838935e..d5da271d936 100644
--- a/app/graphql/types/snippets/blob_type.rb
+++ b/app/graphql/types/snippets/blob_type.rb
@@ -8,36 +8,36 @@ module Types
description 'Represents the snippet blob'
present_using SnippetBlobPresenter
- field :rich_data, GraphQL::STRING_TYPE,
+ field :rich_data, GraphQL::Types::String,
description: 'Blob highlighted data.',
null: true
- field :plain_data, GraphQL::STRING_TYPE,
+ field :plain_data, GraphQL::Types::String,
description: 'Blob plain highlighted data.',
null: true
- field :raw_plain_data, GraphQL::STRING_TYPE,
+ field :raw_plain_data, GraphQL::Types::String,
description: 'The raw content of the blob, if the blob is text data.',
null: true
- field :raw_path, GraphQL::STRING_TYPE,
+ field :raw_path, GraphQL::Types::String,
description: 'Blob raw content endpoint path.',
null: false
- field :size, GraphQL::INT_TYPE,
+ field :size, GraphQL::Types::Int,
description: 'Blob size.',
null: false
- field :binary, GraphQL::BOOLEAN_TYPE,
+ field :binary, GraphQL::Types::Boolean,
description: 'Shows whether the blob is binary.',
method: :binary?,
null: false
- field :name, GraphQL::STRING_TYPE,
+ field :name, GraphQL::Types::String,
description: 'Blob name.',
null: true
- field :path, GraphQL::STRING_TYPE,
+ field :path, GraphQL::Types::String,
description: 'Blob path.',
null: true
@@ -49,15 +49,15 @@ module Types
description: 'Blob content rich viewer.',
null: true
- field :mode, type: GraphQL::STRING_TYPE,
+ field :mode, type: GraphQL::Types::String,
description: 'Blob mode.',
null: true
- field :external_storage, type: GraphQL::STRING_TYPE,
+ field :external_storage, type: GraphQL::Types::String,
description: 'Blob external storage.',
null: true
- field :rendered_as_text, type: GraphQL::BOOLEAN_TYPE,
+ field :rendered_as_text, type: GraphQL::Types::Boolean,
description: 'Shows whether the blob is rendered as text.',
method: :rendered_as_text?,
null: false
diff --git a/app/graphql/types/task_completion_status.rb b/app/graphql/types/task_completion_status.rb
index 6837256f202..3aa19ff9413 100644
--- a/app/graphql/types/task_completion_status.rb
+++ b/app/graphql/types/task_completion_status.rb
@@ -8,9 +8,9 @@ module Types
graphql_name 'TaskCompletionStatus'
description 'Completion status of tasks'
- field :count, GraphQL::INT_TYPE, null: false,
+ field :count, GraphQL::Types::Int, null: false,
description: 'Number of total tasks.'
- field :completed_count, GraphQL::INT_TYPE, null: false,
+ field :completed_count, GraphQL::Types::Int, null: false,
description: 'Number of completed tasks.'
end
# rubocop: enable Graphql/AuthorizeTypes
diff --git a/app/graphql/types/terraform/state_type.rb b/app/graphql/types/terraform/state_type.rb
index 9e2c47a9ece..cbd5aeaeef9 100644
--- a/app/graphql/types/terraform/state_type.rb
+++ b/app/graphql/types/terraform/state_type.rb
@@ -9,11 +9,11 @@ module Types
connection_type_class(Types::CountableConnectionType)
- field :id, GraphQL::ID_TYPE,
+ field :id, GraphQL::Types::ID,
null: false,
description: 'ID of the Terraform state.'
- field :name, GraphQL::STRING_TYPE,
+ field :name, GraphQL::Types::String,
null: false,
description: 'Name of the Terraform state.'
diff --git a/app/graphql/types/terraform/state_version_type.rb b/app/graphql/types/terraform/state_version_type.rb
index 2cd2ec8dcda..545b3c0044d 100644
--- a/app/graphql/types/terraform/state_version_type.rb
+++ b/app/graphql/types/terraform/state_version_type.rb
@@ -9,7 +9,7 @@ module Types
authorize :read_terraform_state
- field :id, GraphQL::ID_TYPE,
+ field :id, GraphQL::Types::ID,
null: false,
description: 'ID of the Terraform state version.'
@@ -17,7 +17,7 @@ module Types
null: true,
description: 'The user that created this version.'
- field :download_path, GraphQL::STRING_TYPE,
+ field :download_path, GraphQL::Types::String,
null: true,
description: "URL for downloading the version's JSON file."
@@ -25,7 +25,7 @@ module Types
null: true,
description: 'The job that created this version.'
- field :serial, GraphQL::INT_TYPE,
+ field :serial, GraphQL::Types::Int,
null: true,
description: 'Serial number of the version.',
method: :version
diff --git a/app/graphql/types/timelog_type.rb b/app/graphql/types/timelog_type.rb
index 925a522629e..fb5d4ca2dc0 100644
--- a/app/graphql/types/timelog_type.rb
+++ b/app/graphql/types/timelog_type.rb
@@ -12,7 +12,7 @@ module Types
description: 'Timestamp of when the time tracked was spent at.'
field :time_spent,
- GraphQL::INT_TYPE,
+ GraphQL::Types::Int,
null: false,
description: 'The time spent displayed in seconds.'
diff --git a/app/graphql/types/todo_type.rb b/app/graphql/types/todo_type.rb
index 3b983060de2..24c110ce09b 100644
--- a/app/graphql/types/todo_type.rb
+++ b/app/graphql/types/todo_type.rb
@@ -9,7 +9,7 @@ module Types
authorize :read_todo
- field :id, GraphQL::ID_TYPE,
+ field :id, GraphQL::Types::ID,
description: 'ID of the to-do item.',
null: false
@@ -35,7 +35,7 @@ module Types
description: 'Target type of the to-do item.',
null: false
- field :body, GraphQL::STRING_TYPE,
+ field :body, GraphQL::Types::String,
description: 'Body of the to-do item.',
null: false,
calls_gitaly: true # TODO This is only true when `target_type` is `Commit`. See https://gitlab.com/gitlab-org/gitlab/issues/34757#note_234752665
diff --git a/app/graphql/types/tree/blob_type.rb b/app/graphql/types/tree/blob_type.rb
index d192c8d3c57..bb15d91a62f 100644
--- a/app/graphql/types/tree/blob_type.rb
+++ b/app/graphql/types/tree/blob_type.rb
@@ -10,14 +10,14 @@ module Types
graphql_name 'Blob'
- field :web_url, GraphQL::STRING_TYPE, null: true,
+ field :web_url, GraphQL::Types::String, null: true,
description: 'Web URL of the blob.'
- field :web_path, GraphQL::STRING_TYPE, null: true,
+ field :web_path, GraphQL::Types::String, null: true,
description: 'Web path of the blob.'
- field :lfs_oid, GraphQL::STRING_TYPE, null: true,
+ field :lfs_oid, GraphQL::Types::String, null: true,
calls_gitaly: true,
description: 'LFS ID of the blob.'
- field :mode, GraphQL::STRING_TYPE, null: true,
+ field :mode, GraphQL::Types::String, null: true,
description: 'Blob mode in numeric format.'
def lfs_oid
diff --git a/app/graphql/types/tree/entry_type.rb b/app/graphql/types/tree/entry_type.rb
index c0150b77c55..1c612f91a5b 100644
--- a/app/graphql/types/tree/entry_type.rb
+++ b/app/graphql/types/tree/entry_type.rb
@@ -4,17 +4,17 @@ module Types
module EntryType
include Types::BaseInterface
- field :id, GraphQL::ID_TYPE, null: false,
+ field :id, GraphQL::Types::ID, null: false,
description: 'ID of the entry.'
- field :sha, GraphQL::STRING_TYPE, null: false,
+ field :sha, GraphQL::Types::String, null: false,
description: 'Last commit SHA for the entry.', method: :id
- field :name, GraphQL::STRING_TYPE, null: false,
+ field :name, GraphQL::Types::String, null: false,
description: 'Name of the entry.'
field :type, Tree::TypeEnum, null: false,
description: 'Type of tree entry.'
- field :path, GraphQL::STRING_TYPE, null: false,
+ field :path, GraphQL::Types::String, null: false,
description: 'Path of the entry.'
- field :flat_path, GraphQL::STRING_TYPE, null: false,
+ field :flat_path, GraphQL::Types::String, null: false,
description: 'Flat path of the entry.'
end
end
diff --git a/app/graphql/types/tree/submodule_type.rb b/app/graphql/types/tree/submodule_type.rb
index 519e866ebb0..05d8c1a951a 100644
--- a/app/graphql/types/tree/submodule_type.rb
+++ b/app/graphql/types/tree/submodule_type.rb
@@ -8,9 +8,9 @@ module Types
graphql_name 'Submodule'
- field :web_url, type: GraphQL::STRING_TYPE, null: true,
+ field :web_url, type: GraphQL::Types::String, null: true,
description: 'Web URL for the sub-module.'
- field :tree_url, type: GraphQL::STRING_TYPE, null: true,
+ field :tree_url, type: GraphQL::Types::String, null: true,
description: 'Tree URL for the sub-module.'
end
# rubocop: enable Graphql/AuthorizeTypes
diff --git a/app/graphql/types/tree/tree_entry_type.rb b/app/graphql/types/tree/tree_entry_type.rb
index daf4b5421fb..998b3617574 100644
--- a/app/graphql/types/tree/tree_entry_type.rb
+++ b/app/graphql/types/tree/tree_entry_type.rb
@@ -11,9 +11,9 @@ module Types
graphql_name 'TreeEntry'
description 'Represents a directory'
- field :web_url, GraphQL::STRING_TYPE, null: true,
+ field :web_url, GraphQL::Types::String, null: true,
description: 'Web URL for the tree entry (directory).'
- field :web_path, GraphQL::STRING_TYPE, null: true,
+ field :web_path, GraphQL::Types::String, null: true,
description: 'Web path for the tree entry (directory).'
end
# rubocop: enable Graphql/AuthorizeTypes
diff --git a/app/graphql/types/user_interface.rb b/app/graphql/types/user_interface.rb
index e5abc033155..045f612691b 100644
--- a/app/graphql/types/user_interface.rb
+++ b/app/graphql/types/user_interface.rb
@@ -14,20 +14,20 @@ module Types
method: :itself
field :id,
- type: GraphQL::ID_TYPE,
+ type: GraphQL::Types::ID,
null: false,
description: 'ID of the user.'
field :bot,
- type: GraphQL::BOOLEAN_TYPE,
+ type: GraphQL::Types::Boolean,
null: false,
description: 'Indicates if the user is a bot.',
method: :bot?
field :username,
- type: GraphQL::STRING_TYPE,
+ type: GraphQL::Types::String,
null: false,
description: 'Username of the user. Unique within this instance of GitLab.'
field :name,
- type: GraphQL::STRING_TYPE,
+ type: GraphQL::Types::String,
null: false,
description: 'Human-readable name of the user.'
field :state,
@@ -35,24 +35,24 @@ module Types
null: false,
description: 'State of the user.'
field :email,
- type: GraphQL::STRING_TYPE,
+ type: GraphQL::Types::String,
null: true,
description: 'User email.', method: :public_email,
deprecated: { reason: :renamed, replacement: 'User.publicEmail', milestone: '13.7' }
field :public_email,
- type: GraphQL::STRING_TYPE,
+ type: GraphQL::Types::String,
null: true,
description: "User's public email."
field :avatar_url,
- type: GraphQL::STRING_TYPE,
+ type: GraphQL::Types::String,
null: true,
description: "URL of the user's avatar."
field :web_url,
- type: GraphQL::STRING_TYPE,
+ type: GraphQL::Types::String,
null: false,
description: 'Web URL of the user.'
field :web_path,
- type: GraphQL::STRING_TYPE,
+ type: GraphQL::Types::String,
null: false,
description: 'Web path of the user.'
field :todos,
@@ -70,7 +70,7 @@ module Types
null: true,
description: 'User status.'
field :location,
- type: ::GraphQL::STRING_TYPE,
+ type: ::GraphQL::Types::String,
null: true,
description: 'The location of the user.'
field :project_memberships,
diff --git a/app/graphql/types/user_merge_request_interaction_type.rb b/app/graphql/types/user_merge_request_interaction_type.rb
index b9ff489e0d6..ff6e83efbb2 100644
--- a/app/graphql/types/user_merge_request_interaction_type.rb
+++ b/app/graphql/types/user_merge_request_interaction_type.rb
@@ -13,14 +13,14 @@ module Types
authorize :read_merge_request
field :can_merge,
- type: ::GraphQL::BOOLEAN_TYPE,
+ type: ::GraphQL::Types::Boolean,
null: false,
calls_gitaly: true,
method: :can_merge?,
description: 'Whether this user can merge this merge request.'
field :can_update,
- type: ::GraphQL::BOOLEAN_TYPE,
+ type: ::GraphQL::Types::Boolean,
null: false,
method: :can_update?,
description: 'Whether this user can update this merge request.'
@@ -31,13 +31,13 @@ module Types
description: 'The state of the review by this user.'
field :reviewed,
- type: ::GraphQL::BOOLEAN_TYPE,
+ type: ::GraphQL::Types::Boolean,
null: false,
method: :reviewed?,
description: 'Whether this user has provided a review for this merge request.'
field :approved,
- type: ::GraphQL::BOOLEAN_TYPE,
+ type: ::GraphQL::Types::Boolean,
null: false,
method: :approved?,
description: 'Whether this user has approved this merge request.'
diff --git a/app/graphql/types/user_status_type.rb b/app/graphql/types/user_status_type.rb
index c1a736a3722..61abec0ba96 100644
--- a/app/graphql/types/user_status_type.rb
+++ b/app/graphql/types/user_status_type.rb
@@ -7,9 +7,9 @@ module Types
markdown_field :message_html, null: true,
description: 'HTML of the user status message'
- field :message, GraphQL::STRING_TYPE, null: true,
+ field :message, GraphQL::Types::String, null: true,
description: 'User status message.'
- field :emoji, GraphQL::STRING_TYPE, null: true,
+ field :emoji, GraphQL::Types::String, null: true,
description: 'String representation of emoji.'
field :availability, Types::AvailabilityEnum, null: false,
description: 'User availability status.'
diff --git a/app/mailers/previews/notify_preview.rb b/app/mailers/previews/notify_preview.rb
index ceeb178e9c2..13b24e099c9 100644
--- a/app/mailers/previews/notify_preview.rb
+++ b/app/mailers/previews/notify_preview.rb
@@ -246,7 +246,7 @@ class NotifyPreview < ActionMailer::Preview
def cleanup
email = nil
- ActiveRecord::Base.transaction do
+ ActiveRecord::Base.transaction do # rubocop: disable Database/MultipleDatabases
email = yield
raise ActiveRecord::Rollback
end
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 02ebcfa7c0a..7d34198653a 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -627,7 +627,7 @@ class ApplicationSetting < ApplicationRecord
# prevent this from happening, we do a sanity check that the
# primary key constraint is present before inserting a new entry.
def self.check_schema!
- return if ActiveRecord::Base.connection.primary_key(self.table_name).present?
+ return if connection.primary_key(self.table_name).present?
raise "The `#{self.table_name}` table is missing a primary key constraint in the database schema"
end
diff --git a/app/models/internal_id.rb b/app/models/internal_id.rb
index f114094d69c..a54de3c82d1 100644
--- a/app/models/internal_id.rb
+++ b/app/models/internal_id.rb
@@ -83,7 +83,7 @@ class InternalId < ApplicationRecord
self.internal_id_transactions_total.increment(
operation: operation,
usage: usage.to_s,
- in_transaction: ActiveRecord::Base.connection.transaction_open?.to_s
+ in_transaction: ActiveRecord::Base.connection.transaction_open?.to_s # rubocop: disable Database/MultipleDatabases
)
end
@@ -317,7 +317,7 @@ class InternalId < ApplicationRecord
stmt.set(arel_table[:last_value] => new_value)
stmt.wheres = InternalId.filter_by(scope, usage).arel.constraints
- ActiveRecord::Base.connection.insert(stmt, 'Update InternalId', 'last_value')
+ ActiveRecord::Base.connection.insert(stmt, 'Update InternalId', 'last_value') # rubocop: disable Database/MultipleDatabases
end
def create_record!(subject, scope, usage, init)
diff --git a/app/models/issue.rb b/app/models/issue.rb
index d1ef84a8a97..f72e8fdb138 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -317,6 +317,21 @@ class Issue < ApplicationRecord
)
end
+ def self.to_branch_name(*args)
+ branch_name = args.map(&:to_s).each_with_index.map do |arg, i|
+ arg.parameterize(preserve_case: i == 0).presence
+ end.compact.join('-')
+
+ if branch_name.length > 100
+ truncated_string = branch_name[0, 100]
+ # Delete everything dangling after the last hyphen so as not to risk
+ # existence of unintended words in the branch name due to mid-word split.
+ branch_name = truncated_string.sub(/-[^-]*\Z/, '')
+ end
+
+ branch_name
+ end
+
# Temporary disable moving null elements because of performance problems
# For more information check https://gitlab.com/gitlab-com/gl-infra/production/-/issues/4321
def check_repositioning_allowed!
@@ -384,16 +399,7 @@ class Issue < ApplicationRecord
if self.confidential?
"#{iid}-confidential-issue"
else
- branch_name = "#{iid}-#{title.parameterize}"
-
- if branch_name.length > 100
- truncated_string = branch_name[0, 100]
- # Delete everything dangling after the last hyphen so as not to risk
- # existence of unintended words in the branch name due to mid-word split.
- branch_name = truncated_string[0, truncated_string.rindex("-")]
- end
-
- branch_name
+ self.class.to_branch_name(iid, title)
end
end
diff --git a/app/models/packages/event.rb b/app/models/packages/event.rb
index a1eb7120117..bb2c33594e5 100644
--- a/app/models/packages/event.rb
+++ b/app/models/packages/event.rb
@@ -4,7 +4,7 @@ class Packages::Event < ApplicationRecord
belongs_to :package, optional: true
UNIQUE_EVENTS_ALLOWED = %i[push_package delete_package pull_package pull_symbol_package push_symbol_package].freeze
- EVENT_SCOPES = ::Packages::Package.package_types.merge(container: 1000, tag: 1001).freeze
+ EVENT_SCOPES = ::Packages::Package.package_types.merge(container: 1000, tag: 1001, dependency_proxy: 1002).freeze
EVENT_PREFIX = "i_package"
@@ -23,7 +23,11 @@ class Packages::Event < ApplicationRecord
list_tags: 9,
cli_metadata: 10,
pull_symbol_package: 11,
- push_symbol_package: 12
+ push_symbol_package: 12,
+ pull_manifest: 13,
+ pull_manifest_from_cache: 14,
+ pull_blob: 15,
+ pull_blob_from_cache: 16
}
enum originator_type: { user: 0, deploy_token: 1, guest: 2 }
diff --git a/app/services/auto_merge/base_service.rb b/app/services/auto_merge/base_service.rb
index 142eebca2e3..e756e8c14d8 100644
--- a/app/services/auto_merge/base_service.rb
+++ b/app/services/auto_merge/base_service.rb
@@ -6,7 +6,7 @@ module AutoMerge
include MergeRequests::AssignsMergeParams
def execute(merge_request)
- ActiveRecord::Base.transaction do
+ ActiveRecord::Base.transaction do # rubocop: disable Database/MultipleDatabases
register_auto_merge_parameters!(merge_request)
yield if block_given?
end
@@ -29,7 +29,7 @@ module AutoMerge
end
def cancel(merge_request)
- ActiveRecord::Base.transaction do
+ ActiveRecord::Base.transaction do # rubocop: disable Database/MultipleDatabases
clear_auto_merge_parameters!(merge_request)
yield if block_given?
end
@@ -41,7 +41,7 @@ module AutoMerge
end
def abort(merge_request, reason)
- ActiveRecord::Base.transaction do
+ ActiveRecord::Base.transaction do # rubocop: disable Database/MultipleDatabases
clear_auto_merge_parameters!(merge_request)
yield if block_given?
end
diff --git a/app/services/ci/delete_unit_tests_service.rb b/app/services/ci/delete_unit_tests_service.rb
index 28f96351175..230661a107d 100644
--- a/app/services/ci/delete_unit_tests_service.rb
+++ b/app/services/ci/delete_unit_tests_service.rb
@@ -23,7 +23,7 @@ module Ci
def delete_batch!(klass)
deleted = 0
- ActiveRecord::Base.transaction do
+ klass.transaction do
ids = klass.deletable.lock('FOR UPDATE SKIP LOCKED').limit(BATCH_SIZE).pluck(:id)
break if ids.empty?
diff --git a/app/services/ci/unlock_artifacts_service.rb b/app/services/ci/unlock_artifacts_service.rb
index 07faf90dd6d..7c169cb8395 100644
--- a/app/services/ci/unlock_artifacts_service.rb
+++ b/app/services/ci/unlock_artifacts_service.rb
@@ -17,7 +17,7 @@ module Ci
SQL
loop do
- break if ActiveRecord::Base.connection.exec_query(query).empty?
+ break if Ci::Pipeline.connection.exec_query(query).empty?
end
end
diff --git a/app/services/dependency_proxy/find_or_create_blob_service.rb b/app/services/dependency_proxy/find_or_create_blob_service.rb
index bd06f9d7628..f3dbf31dcdb 100644
--- a/app/services/dependency_proxy/find_or_create_blob_service.rb
+++ b/app/services/dependency_proxy/find_or_create_blob_service.rb
@@ -10,10 +10,12 @@ module DependencyProxy
end
def execute
+ from_cache = true
file_name = @blob_sha.sub('sha256:', '') + '.gz'
blob = @group.dependency_proxy_blobs.find_or_build(file_name)
unless blob.persisted?
+ from_cache = false
result = DependencyProxy::DownloadBlobService
.new(@image, @blob_sha, @token).execute
@@ -28,7 +30,7 @@ module DependencyProxy
blob.save!
end
- success(blob: blob)
+ success(blob: blob, from_cache: from_cache)
end
private
diff --git a/app/services/dependency_proxy/find_or_create_manifest_service.rb b/app/services/dependency_proxy/find_or_create_manifest_service.rb
index ee608d715aa..0eb990ab7f8 100644
--- a/app/services/dependency_proxy/find_or_create_manifest_service.rb
+++ b/app/services/dependency_proxy/find_or_create_manifest_service.rb
@@ -17,10 +17,10 @@ module DependencyProxy
head_result = DependencyProxy::HeadManifestService.new(@image, @tag, @token).execute
- return success(manifest: @manifest) if cached_manifest_matches?(head_result)
+ return success(manifest: @manifest, from_cache: true) if cached_manifest_matches?(head_result)
pull_new_manifest
- respond
+ respond(from_cache: false)
rescue Timeout::Error, *Gitlab::HTTP::HTTP_ERRORS
respond
end
@@ -44,9 +44,9 @@ module DependencyProxy
@manifest && @manifest.digest == head_result[:digest] && @manifest.content_type == head_result[:content_type]
end
- def respond
+ def respond(from_cache: true)
if @manifest.persisted?
- success(manifest: @manifest)
+ success(manifest: @manifest, from_cache: from_cache)
else
error('Failed to download the manifest from the external registry', 503)
end
diff --git a/app/services/deployments/update_environment_service.rb b/app/services/deployments/update_environment_service.rb
index 6f85779c285..83c37257297 100644
--- a/app/services/deployments/update_environment_service.rb
+++ b/app/services/deployments/update_environment_service.rb
@@ -26,7 +26,7 @@ module Deployments
end
def update_environment(deployment)
- ActiveRecord::Base.transaction do
+ ActiveRecord::Base.transaction do # rubocop: disable Database/MultipleDatabases
# Renew attributes at update
renew_external_url
renew_auto_stop_in
diff --git a/app/services/design_management/copy_design_collection/copy_service.rb b/app/services/design_management/copy_design_collection/copy_service.rb
index b40f6a81174..0bec6de1dee 100644
--- a/app/services/design_management/copy_design_collection/copy_service.rb
+++ b/app/services/design_management/copy_design_collection/copy_service.rb
@@ -36,7 +36,7 @@ module DesignManagement
with_temporary_branch do
copy_commits!
- ActiveRecord::Base.transaction do
+ ApplicationRecord.transaction do
design_ids = copy_designs!
version_ids = copy_versions!
copy_actions!(design_ids, version_ids)
diff --git a/app/services/feature_flags/create_service.rb b/app/services/feature_flags/create_service.rb
index 5c87af561d5..5111f914447 100644
--- a/app/services/feature_flags/create_service.rb
+++ b/app/services/feature_flags/create_service.rb
@@ -6,7 +6,7 @@ module FeatureFlags
return error('Access Denied', 403) unless can_create?
return error('Version is invalid', :bad_request) unless valid_version?
- ActiveRecord::Base.transaction do
+ ApplicationRecord.transaction do
feature_flag = project.operations_feature_flags.new(params)
if feature_flag.save
diff --git a/app/services/feature_flags/destroy_service.rb b/app/services/feature_flags/destroy_service.rb
index b131a349fc7..986fe004db6 100644
--- a/app/services/feature_flags/destroy_service.rb
+++ b/app/services/feature_flags/destroy_service.rb
@@ -11,7 +11,7 @@ module FeatureFlags
def destroy_feature_flag(feature_flag)
return error('Access Denied', 403) unless can_destroy?(feature_flag)
- ActiveRecord::Base.transaction do
+ ApplicationRecord.transaction do
if feature_flag.destroy
save_audit_event(audit_event(feature_flag))
diff --git a/app/services/feature_flags/update_service.rb b/app/services/feature_flags/update_service.rb
index f5ab6f4005b..01e4f661d75 100644
--- a/app/services/feature_flags/update_service.rb
+++ b/app/services/feature_flags/update_service.rb
@@ -12,7 +12,7 @@ module FeatureFlags
return error('Access Denied', 403) unless can_update?(feature_flag)
return error('Not Found', 404) unless valid_user_list_ids?(feature_flag, user_list_ids(params))
- ActiveRecord::Base.transaction do
+ ApplicationRecord.transaction do
feature_flag.assign_attributes(params)
feature_flag.strategies.each do |strategy|
diff --git a/app/services/issuable/clone/base_service.rb b/app/services/issuable/clone/base_service.rb
index 574fe85b466..1d2c5c06d1b 100644
--- a/app/services/issuable/clone/base_service.rb
+++ b/app/services/issuable/clone/base_service.rb
@@ -14,7 +14,7 @@ module Issuable
# Using transaction because of a high resources footprint
# on rewriting notes (unfolding references)
#
- ActiveRecord::Base.transaction do
+ ApplicationRecord.transaction do
@new_entity = create_new_entity
update_new_entity
diff --git a/app/services/issuable/common_system_notes_service.rb b/app/services/issuable/common_system_notes_service.rb
index aedd0c377c6..38050708fc5 100644
--- a/app/services/issuable/common_system_notes_service.rb
+++ b/app/services/issuable/common_system_notes_service.rb
@@ -9,7 +9,7 @@ module Issuable
# We disable touch so that created system notes do not update
# the noteable's updated_at field
- ActiveRecord::Base.no_touching do
+ ApplicationRecord.no_touching do
if is_update
if issuable.previous_changes.include?('title')
create_title_change_note(issuable.previous_changes['title'].first)
diff --git a/app/services/issuable/destroy_label_links_service.rb b/app/services/issuable/destroy_label_links_service.rb
index 6fff9b5e8d2..49000d90c1f 100644
--- a/app/services/issuable/destroy_label_links_service.rb
+++ b/app/services/issuable/destroy_label_links_service.rb
@@ -22,7 +22,7 @@ module Issuable
SQL
loop do
- result = ActiveRecord::Base.connection.execute(delete_query)
+ result = LabelLink.connection.execute(delete_query)
break if result.cmd_tuples == 0
end
diff --git a/app/services/packages/create_dependency_service.rb b/app/services/packages/create_dependency_service.rb
index 2999885d55d..f2b30c9d816 100644
--- a/app/services/packages/create_dependency_service.rb
+++ b/app/services/packages/create_dependency_service.rb
@@ -27,7 +27,7 @@ module Packages
dependencies_to_insert = names_and_version_patterns.reject { |k, _| k.in?(existing_names) }
end
- ActiveRecord::Base.transaction do
+ ApplicationRecord.transaction do
inserted_ids = bulk_insert_package_dependencies(dependencies_to_insert)
bulk_insert_package_dependency_links(type, (existing_ids + inserted_ids))
end
diff --git a/app/services/packages/go/create_package_service.rb b/app/services/packages/go/create_package_service.rb
index 4e8b8ef8d6b..2a6eeff402e 100644
--- a/app/services/packages/go/create_package_service.rb
+++ b/app/services/packages/go/create_package_service.rb
@@ -23,7 +23,7 @@ module Packages
files[:mod] = prepare_file(version, :mod, version.gomod)
files[:zip] = prepare_file(version, :zip, version.archive.string)
- ActiveRecord::Base.transaction do
+ ApplicationRecord.transaction do
# create new package and files
package = create_package
files.each { |type, (file, digests)| create_file(package, type, file, digests) }
diff --git a/app/services/packages/npm/create_package_service.rb b/app/services/packages/npm/create_package_service.rb
index 22396eb7687..1d5d9c38432 100644
--- a/app/services/packages/npm/create_package_service.rb
+++ b/app/services/packages/npm/create_package_service.rb
@@ -9,7 +9,7 @@ module Packages
return error('Package already exists.', 403) if current_package_exists?
return error('File is too large.', 400) if file_size_exceeded?
- ActiveRecord::Base.transaction { create_npm_package! }
+ ApplicationRecord.transaction { create_npm_package! }
end
private
diff --git a/app/services/packages/terraform_module/create_package_service.rb b/app/services/packages/terraform_module/create_package_service.rb
index fc376c70b00..03f749edfa8 100644
--- a/app/services/packages/terraform_module/create_package_service.rb
+++ b/app/services/packages/terraform_module/create_package_service.rb
@@ -11,7 +11,7 @@ module Packages
return error('Package version already exists.', 403) if current_package_version_exists?
return error('File is too large.', 400) if file_size_exceeded?
- ActiveRecord::Base.transaction { create_terraform_module_package! }
+ ApplicationRecord.transaction { create_terraform_module_package! }
end
private
diff --git a/app/services/projects/cleanup_service.rb b/app/services/projects/cleanup_service.rb
index 5eafa5f9b29..75be3425029 100644
--- a/app/services/projects/cleanup_service.rb
+++ b/app/services/projects/cleanup_service.rb
@@ -65,7 +65,7 @@ module Projects
def cleanup_diffs(response)
old_commit_shas = extract_old_commit_shas(response.entries)
- ActiveRecord::Base.transaction do
+ ApplicationRecord.transaction do
cleanup_merge_request_diffs(old_commit_shas)
cleanup_note_diff_files(old_commit_shas)
end
diff --git a/app/services/projects/fetch_statistics_increment_service.rb b/app/services/projects/fetch_statistics_increment_service.rb
index b150fd2d9f1..3354a074d1e 100644
--- a/app/services/projects/fetch_statistics_increment_service.rb
+++ b/app/services/projects/fetch_statistics_increment_service.rb
@@ -15,7 +15,7 @@ module Projects
ON CONFLICT (project_id, date) DO UPDATE SET fetch_count = #{table_name}.fetch_count + 1
SQL
- ActiveRecord::Base.connection.execute(increment_fetch_count_sql)
+ ProjectDailyStatistic.connection.execute(increment_fetch_count_sql)
end
private
diff --git a/app/services/releases/update_service.rb b/app/services/releases/update_service.rb
index eda4b7102c0..011cb2e481d 100644
--- a/app/services/releases/update_service.rb
+++ b/app/services/releases/update_service.rb
@@ -18,7 +18,7 @@ module Releases
# when it does assign_attributes instead of actual saving
# this leads to the validation error being raised
# see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/43385
- ActiveRecord::Base.transaction do
+ ApplicationRecord.transaction do
if release.update(params)
execute_hooks(release, 'update')
success(tag: existing_tag, release: release, milestones_updated: milestones_updated?(previous_milestones))
diff --git a/app/services/todos/destroy/destroyed_issuable_service.rb b/app/services/todos/destroy/destroyed_issuable_service.rb
index db12965224b..7a85b59eeea 100644
--- a/app/services/todos/destroy/destroyed_issuable_service.rb
+++ b/app/services/todos/destroy/destroyed_issuable_service.rb
@@ -20,7 +20,7 @@ module Todos
SQL
loop do
- result = ActiveRecord::Base.connection.execute(delete_query)
+ result = Todo.connection.execute(delete_query)
break if result.cmd_tuples == 0
diff --git a/app/views/admin/application_settings/_email.html.haml b/app/views/admin/application_settings/_email.html.haml
index b22aaabe41a..1c35250644d 100644
--- a/app/views/admin/application_settings/_email.html.haml
+++ b/app/views/admin/application_settings/_email.html.haml
@@ -8,20 +8,20 @@
= f.label :email_author_in_body, class: 'form-check-label' do
= _('Include author name in notification email body')
.form-text.text-muted
- = _('Some email servers do not support overriding the email sender name. Enable this option to include the name of the author of the issue, merge request or comment in the email body instead.')
+ = _("Include the name of the author of the issue, merge request or comment in the email body. By default, GitLab overrides the email sender's name. Some email servers don't support that option.")
.form-group
.form-check
= f.check_box :html_emails_enabled, class: 'form-check-input'
= f.label :html_emails_enabled, class: 'form-check-label' do
- = _('Enable HTML emails')
+ = _('Enable multipart emails')
.form-text.text-muted
- = _('By default GitLab sends emails in HTML and plain text formats so mail clients can choose what format to use. Disable this option if you only want to send emails in plain text format.')
+ = _('Send email in multipart format (HTML and plain text). Uncheck to send email messages in plain text only.')
.form-group
= f.label :commit_email_hostname, _('Custom hostname (for private commit emails)'), class: 'label-bold'
= f.text_field :commit_email_hostname, class: 'form-control gl-form-input'
.form-text.text-muted
- commit_email_hostname_docs_link = link_to _('Learn more'), help_page_path('user/admin_area/settings/email.md', anchor: 'custom-hostname-for-private-commit-emails'), target: '_blank'
- = _("This setting will update the hostname that is used to generate private commit emails. %{learn_more}").html_safe % { learn_more: commit_email_hostname_docs_link }
+ = _("Hostname used in private commit emails. %{learn_more}").html_safe % { learn_more: commit_email_hostname_docs_link }
= render_if_exists 'admin/application_settings/email_additional_text_setting', form: f
@@ -31,6 +31,6 @@
= f.label :in_product_marketing_emails_enabled, class: 'form-check-label' do
= _('Enable in-product marketing emails')
.form-text.text-muted
- = _('By default, GitLab sends emails to help guide users through the onboarding process.')
+ = _('Send emails to help guide new users through the onboarding process.')
= f.submit _('Save changes'), class: "gl-button btn btn-confirm", data: { qa_selector: 'save_changes_button' }
diff --git a/app/views/jira_connect/branches/new.html.haml b/app/views/jira_connect/branches/new.html.haml
new file mode 100644
index 00000000000..ec2b7be47ca
--- /dev/null
+++ b/app/views/jira_connect/branches/new.html.haml
@@ -0,0 +1,5 @@
+- @hide_breadcrumbs = true
+- @hide_top_links = true
+- page_title _('New branch')
+
+.js-jira-connect-create-branch{ data: { initial_branch_name: @branch_name } }