Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-07-23 12:09:05 +00:00
parent 00a889ea7a
commit ab8eecd62c
159 changed files with 4321 additions and 730 deletions

View File

@ -104,28 +104,6 @@ Graphql/OldTypes:
- 'app/graphql/types/container_repository_type.rb'
- 'app/graphql/types/countable_connection_type.rb'
- 'app/graphql/types/custom_emoji_type.rb'
- 'app/graphql/types/query_complexity_type.rb'
- 'app/graphql/types/release_asset_link_shared_input_arguments.rb'
- 'app/graphql/types/release_asset_link_type.rb'
- 'app/graphql/types/release_assets_type.rb'
- 'app/graphql/types/release_links_type.rb'
- 'app/graphql/types/release_source_type.rb'
- 'app/graphql/types/release_type.rb'
- 'app/graphql/types/repository/blob_type.rb'
- 'app/graphql/types/repository_type.rb'
- 'app/graphql/types/resolvable_interface.rb'
- 'app/graphql/types/snippet_type.rb'
- 'app/graphql/types/snippets/blob_action_input_type.rb'
- 'app/graphql/types/snippets/blob_type.rb'
- 'app/graphql/types/task_completion_status.rb'
- 'app/graphql/types/terraform/state_type.rb'
- 'app/graphql/types/terraform/state_version_type.rb'
- 'app/graphql/types/timelog_type.rb'
- 'app/graphql/types/todo_type.rb'
- 'app/graphql/types/tree/blob_type.rb'
- 'app/graphql/types/tree/entry_type.rb'
- 'app/graphql/types/tree/tree_entry_type.rb'
- 'app/graphql/types/user_status_type.rb'
- 'ee/app/graphql/ee/mutations/ci/ci_cd_settings_update.rb'
- 'ee/app/graphql/ee/resolvers/issues_resolver.rb'
- 'ee/app/graphql/ee/resolvers/namespace_projects_resolver.rb'
@ -2750,45 +2728,9 @@ Style/RegexpLiteralMixedPreserve:
- 'spec/support/helpers/require_migration.rb'
- 'spec/views/layouts/_head.html.haml_spec.rb'
# WIP see: https://gitlab.com/gitlab-org/gitlab/-/issues/335808
Database/MultipleDatabases:
Exclude:
- 'app/mailers/previews/notify_preview.rb'
- 'app/models/application_setting.rb'
- 'app/models/internal_id.rb'
- 'app/services/auto_merge/base_service.rb'
- 'app/services/ci/delete_unit_tests_service.rb'
- 'app/services/ci/unlock_artifacts_service.rb'
- 'app/services/deployments/update_environment_service.rb'
- 'app/services/design_management/copy_design_collection/copy_service.rb'
- 'app/services/feature_flags/create_service.rb'
- 'app/services/feature_flags/destroy_service.rb'
- 'app/services/feature_flags/update_service.rb'
- 'app/services/issuable/clone/base_service.rb'
- 'app/services/issuable/common_system_notes_service.rb'
- 'app/services/issuable/destroy_label_links_service.rb'
- 'app/services/packages/create_dependency_service.rb'
- 'app/services/packages/go/create_package_service.rb'
- 'app/services/packages/npm/create_package_service.rb'
- 'app/services/packages/terraform_module/create_package_service.rb'
- 'app/services/projects/cleanup_service.rb'
- 'app/services/projects/fetch_statistics_increment_service.rb'
- 'app/services/releases/update_service.rb'
- 'app/services/todos/destroy/destroyed_issuable_service.rb'
- 'ee/app/models/dora/daily_metrics.rb'
- 'ee/app/services/analytics/devops_adoption/enabled_namespaces/bulk_delete_service.rb'
- 'ee/app/services/approval_rules/finalize_service.rb'
- 'ee/app/services/approval_rules/project_rule_destroy_service.rb'
- 'ee/app/services/app_sec/dast/site_profiles/create_service.rb'
- 'ee/app/services/app_sec/dast/site_profiles/update_service.rb'
- 'ee/app/services/ci/minutes/update_build_minutes_service.rb'
- 'ee/app/services/ee/issuable/common_system_notes_service.rb'
- 'ee/app/services/group_saml/group_managed_accounts/transfer_membership_service.rb'
- 'ee/app/services/group_saml/sign_up_service.rb'
- 'ee/app/services/iterations/roll_over_issues_service.rb'
- 'ee/app/services/security/store_scan_service.rb'
- 'ee/app/services/timebox_report_service.rb'
- 'ee/app/services/vulnerability_feedback/create_service.rb'
- 'ee/lib/ee/gitlab/checks/push_rule_check.rb'
- 'ee/lib/ee/gitlab/database.rb'
- 'ee/lib/gitlab/geo/database_tasks.rb'
- 'ee/lib/gitlab/geo/geo_tasks.rb'
@ -2804,14 +2746,7 @@ Database/MultipleDatabases:
- 'lib/after_commit_queue.rb'
- 'lib/api/rubygem_packages.rb'
- 'lib/backup/manager.rb'
- 'lib/gitlab/analytics/cycle_analytics/stage_query_helpers.rb'
- 'lib/gitlab/chaos.rb'
- 'lib/gitlab/current_settings.rb'
- 'lib/gitlab/database/batch_count.rb'
- 'lib/gitlab/database/batch_counter.rb'
- 'lib/gitlab/database/count/reltuples_count_strategy.rb'
- 'lib/gitlab/database/count/tablesample_count_strategy.rb'
- 'lib/gitlab/database/grant.rb'
- 'lib/gitlab/database/load_balancing/load_balancer.rb'
- 'lib/gitlab/database/load_balancing.rb'
- 'lib/gitlab/database/load_balancing/sticking.rb'
@ -2833,7 +2768,6 @@ Database/MultipleDatabases:
- 'lib/gitlab/database/schema_cache_with_renamed_table.rb'
- 'lib/gitlab/database/schema_migrations/context.rb'
- 'lib/gitlab/database/schema_version_files.rb'
- 'lib/gitlab/database/similarity_score.rb'
- 'lib/gitlab/database/unidirectional_copy_trigger.rb'
- 'lib/gitlab/database/with_lock_retries.rb'
- 'lib/gitlab/gitlab_import/importer.rb'
@ -2842,13 +2776,8 @@ Database/MultipleDatabases:
- 'lib/gitlab/import_export/relation_tree_restorer.rb'
- 'lib/gitlab/legacy_github_import/importer.rb'
- 'lib/gitlab/metrics/samplers/database_sampler.rb'
- 'lib/gitlab/optimistic_locking.rb'
- 'lib/gitlab/otp_key_rotator.rb'
- 'lib/gitlab/profiler.rb'
- 'lib/gitlab/seeder.rb'
- 'lib/gitlab/sherlock/query.rb'
- 'lib/gitlab/sql/glob.rb'
- 'lib/gitlab/sql/set_operator.rb'
- 'lib/system_check/orphans/repository_check.rb'
- 'spec/db/schema_spec.rb'
- 'spec/features/admin/dashboard_spec.rb'
@ -2864,9 +2793,6 @@ Database/MultipleDatabases:
- 'spec/lib/gitlab/profiler_spec.rb'
- 'spec/lib/gitlab/query_limiting/active_support_subscriber_spec.rb'
- 'spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb'
- 'spec/lib/gitlab/sql/cte_spec.rb'
- 'spec/lib/gitlab/sql/glob_spec.rb'
- 'spec/lib/gitlab/sql/recursive_cte_spec.rb'
- 'spec/lib/gitlab/usage_data_metrics_spec.rb'
- 'spec/lib/gitlab/usage_data_queries_spec.rb'
- 'spec/lib/gitlab/usage/metrics/names_suggestions/relation_parsers/constraints_spec.rb'
@ -2874,11 +2800,6 @@ Database/MultipleDatabases:
- 'spec/lib/gitlab/usage/metrics/instrumentations/database_metric_spec.rb'
- 'spec/lib/gitlab/utils/usage_data_spec.rb'
- 'spec/models/application_setting_spec.rb'
- 'spec/models/concerns/case_sensitivity_spec.rb'
- 'spec/models/concerns/sortable_spec.rb'
- 'spec/models/concerns/where_composite_spec.rb'
- 'spec/models/experiment_spec.rb'
- 'spec/models/internal_id_spec.rb'
- 'spec/models/project_feature_usage_spec.rb'
- 'spec/models/users_statistics_spec.rb'
- 'spec/requests/api/statistics_spec.rb'

View File

@ -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;

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -0,0 +1,3 @@
import initJiraConnectBranches from '~/jira_connect/branches';
initJiraConnectBranches();

View File

@ -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>

View File

@ -305,3 +305,6 @@ export const featureToMutationMap = {
}),
},
};
export const AUTO_DEVOPS_ENABLED_ALERT_DISMISSED_STORAGE_KEY =
'security_configuration_auto_devops_enabled_dismissed_projects';

View File

@ -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,
GlLink,
GlTabs,
GlSprintf,
AutoDevOpsAlert,
AutoDevOpsEnabledAlert,
FeatureCard,
GlLink,
GlSprintf,
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>

View File

@ -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>

View File

@ -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

View File

@ -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'
)
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.'

View File

@ -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.'

View File

@ -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

View File

@ -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.'

View File

@ -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

View File

@ -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

View File

@ -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.'

View File

@ -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.'

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.'

View File

@ -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

View File

@ -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.'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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.'

View File

@ -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.'

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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 }

View File

@ -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

View File

@ -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?

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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))

View File

@ -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|

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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) }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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))

View File

@ -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

View File

@ -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' }

View File

@ -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 } }

View File

@ -0,0 +1,8 @@
---
name: jira_connect_create_branch
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66032
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/336191
milestone: '14.2'
type: development
group: group::ecosystem
default_enabled: false

View File

@ -13,4 +13,5 @@ namespace :jira_connect do
end
resources :subscriptions, only: [:index, :create, :destroy]
resources :branches, only: [:new]
end

View File

@ -1923,6 +1923,24 @@ Input type: `DestroyPackageInput`
| <a id="mutationdestroypackageclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationdestroypackageerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
### `Mutation.destroyPackageFile`
Input type: `DestroyPackageFileInput`
#### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationdestroypackagefileclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationdestroypackagefileid"></a>`id` | [`PackagesPackageFileID!`](#packagespackagefileid) | ID of the Package file. |
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationdestroypackagefileclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationdestroypackagefileerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
### `Mutation.destroySnippet`
Input type: `DestroySnippetInput`

View File

@ -133,20 +133,24 @@ For example, `app/graphql/types/issue_type.rb`:
```ruby
graphql_name 'Issue'
field :iid, GraphQL::ID_TYPE, null: true
field :title, GraphQL::STRING_TYPE, null: true
field :iid, GraphQL::Types::ID, null: true
field :title, GraphQL::Types::String, null: true
# we also have a method here that we've defined, that extends `field`
markdown_field :title_html, null: true
field :description, GraphQL::STRING_TYPE, null: true
field :description, GraphQL::Types::String, null: true
markdown_field :description_html, null: true
```
We give each type a name (in this case `Issue`).
The `iid`, `title` and `description` are _scalar_ GraphQL types.
`iid` is a `GraphQL::ID_TYPE`, a special string type that signifies a unique ID.
`title` and `description` are regular `GraphQL::STRING_TYPE` types.
`iid` is a `GraphQL::Types::ID`, a special string type that signifies a unique ID.
`title` and `description` are regular `GraphQL::Types::String` types.
Note that the old scalar types `GraphQL:ID`, `GraphQL::INT_TYPE`, `GraphQL::STRING_TYPE`,
and `GraphQL:BOOLEAN_TYPE` are no longer allowed. Please use `GraphQL::Types::ID`,
`GraphQL::Types::Int`, `GraphQL::Types::String`, and `GraphQL::Types::Boolean`.
When exposing a model through the GraphQL API, we do so by creating a
new type in `app/graphql/types`. You can also declare custom GraphQL data types
@ -225,7 +229,7 @@ Using an example from
[`Types::Notes::DiscussionType`](https://gitlab.com/gitlab-org/gitlab/-/blob/3c95bd9/app/graphql/types/notes/discussion_type.rb#L24-26):
```ruby
field :reply_id, GraphQL::ID_TYPE
field :reply_id, GraphQL::Types::ID
def reply_id
::Gitlab::GlobalId.build(object, id: object.reply_id)
@ -402,7 +406,7 @@ class BranchResolver < BaseResolver
type ::Types::BranchType, null: true
calls_gitaly!
argument name: ::GraphQL::STRING_TYPE, required: true
argument name: ::GraphQL::Types::String, required: true
def resolve(name:)
object.branch(name)
@ -499,7 +503,7 @@ everyone.
Example:
```ruby
field :test_field, type: GraphQL::STRING_TYPE,
field :test_field, type: GraphQL::Types::String,
null: true,
description: 'Some test field.',
feature_flag: :my_feature_flag
@ -523,7 +527,7 @@ When applying a feature flag to toggle the value of a field, the
Example:
```ruby
field :foo, GraphQL::STRING_TYPE,
field :foo, GraphQL::Types::String,
null: true,
description: 'Some test field. Will always return `null`' \
'if `my_feature_flag` feature flag is disabled.'
@ -553,7 +557,7 @@ The value of the property is a `Hash` of:
Example:
```ruby
field :token, GraphQL::STRING_TYPE, null: true,
field :token, GraphQL::Types::String, null: true,
deprecated: { reason: 'Login via token has been removed', milestone: '10.0' },
description: 'Token for login.'
```
@ -795,15 +799,15 @@ We can use GraphQL types like this:
```ruby
module Types
class ChartType < BaseObject
field :title, GraphQL::STRING_TYPE, null: true, description: 'Title of the chart.'
field :title, GraphQL::Types::String, null: true, description: 'Title of the chart.'
field :data, [Types::ChartDatumType], null: true, description: 'Data of the chart.'
end
end
module Types
class ChartDatumType < BaseObject
field :x, GraphQL::INT_TYPE, null: true, description: 'X-axis value of the chart datum.'
field :y, GraphQL::INT_TYPE, null: true, description: 'Y-axis value of the chart datum.'
field :x, GraphQL::Types::Int, null: true, description: 'X-axis value of the chart datum.'
field :y, GraphQL::Types::Int, null: true, description: 'Y-axis value of the chart datum.'
end
end
```
@ -817,7 +821,7 @@ A description of a field or argument is given using the `description:`
keyword. For example:
```ruby
field :id, GraphQL::ID_TYPE, description: 'ID of the resource.'
field :id, GraphQL::Types::ID, description: 'ID of the resource.'
```
Descriptions of fields and arguments are viewable to users through:
@ -834,7 +838,7 @@ descriptions:
`'Labels of the issue'` (issue being the resource).
- Use `"{x} of the {y}"` where possible. Example: `'Title of the issue'`.
Do not start descriptions with `The`.
- Descriptions of `GraphQL::BOOLEAN_TYPE` fields should answer the question: "What does
- Descriptions of `GraphQL::Types::Boolean` fields should answer the question: "What does
this field do?". Example: `'Indicates project has a Git repository'`.
- Always include the word `"timestamp"` when describing an argument or
field of type `Types::TimeType`. This lets the reader know that the
@ -844,8 +848,8 @@ descriptions:
Example:
```ruby
field :id, GraphQL::ID_TYPE, description: 'ID of the issue.'
field :confidential, GraphQL::BOOLEAN_TYPE, description: 'Indicates the issue is confidential.'
field :id, GraphQL::Types::ID, description: 'ID of the issue.'
field :confidential, GraphQL::Types::Boolean, description: 'Indicates the issue is confidential.'
field :closed_at, Types::TimeType, description: 'Timestamp of when the issue was closed.'
```
@ -861,7 +865,7 @@ passing it the type, and field name to copy the description of.
Example:
```ruby
argument :title, GraphQL::STRING_TYPE,
argument :title, GraphQL::Types::String,
required: false,
description: copy_field_description(Types::MergeRequestType, :title)
```
@ -874,7 +878,7 @@ provide a `see` property on fields. For example:
```ruby
field :genus,
type: GraphQL::STRING_TYPE,
type: GraphQL::Types::String,
null: true,
description: 'A taxonomic genus.'
see: { 'Wikipedia page on genera' => 'https://wikipedia.org/wiki/Genus' }
@ -923,7 +927,7 @@ class PostResolver < BaseResolver
authorize :read_blog
description 'Blog posts, optionally filtered by name'
argument :name, [::GraphQL::STRING_TYPE], required: false, as: :slug
argument :name, [::GraphQL::Types::String], required: false, as: :slug
alias_method :blog, :object
@ -1015,10 +1019,10 @@ class JobsResolver < BaseResolver
type JobType.connection_type, null: true
authorize :read_pipeline
argument :name, [::GraphQL::STRING_TYPE], required: false
argument :name, [::GraphQL::Types::String], required: false
when_single do
argument :name, ::GraphQL::STRING_TYPE, required: true
argument :name, ::GraphQL::Types::String, required: true
end
def resolve(**args)
@ -1039,13 +1043,13 @@ class JobsResolver < BaseResolver
type JobType.connection_type, null: true
authorize :read_pipeline
argument :name, [::GraphQL::STRING_TYPE], required: false
argument :name, [::GraphQL::Types::String], required: false
argument :id, [::Types::GlobalIDType[::Job]],
required: false,
prepare: ->(ids, ctx) { ids.map(&:model_id) }
when_single do
argument :name, ::GraphQL::STRING_TYPE, required: false
argument :name, ::GraphQL::Types::String, required: false
argument :id, ::Types::GlobalIDType[::Job],
required: false
prepare: ->(id, ctx) { id.model_id }
@ -1357,7 +1361,7 @@ Arguments for a mutation are defined using `argument`.
Example:
```ruby
argument :my_arg, GraphQL::STRING_TYPE,
argument :my_arg, GraphQL::Types::String,
required: true,
description: "A description of the argument."
```
@ -1382,16 +1386,16 @@ defines these arguments (some
[through inheritance](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/graphql/mutations/merge_requests/base.rb)):
```ruby
argument :project_path, GraphQL::ID_TYPE,
argument :project_path, GraphQL::Types::ID,
required: true,
description: "The project the merge request to mutate is in."
argument :iid, GraphQL::STRING_TYPE,
argument :iid, GraphQL::Types::String,
required: true,
description: "The IID of the merge request to mutate."
argument :draft,
GraphQL::BOOLEAN_TYPE,
GraphQL::Types::Boolean,
required: false,
description: <<~DESC
Whether or not to set the merge request as a draft.

View File

@ -480,7 +480,7 @@ module EE
prepended do
argument :name,
GraphQL::STRING_TYPE,
GraphQL::Types::String,
required: false,
description: 'Tanuki name'
end

View File

@ -142,7 +142,7 @@ to view the `secretName` field:
```ruby
module Types
class ProjectType < BaseObject
field :secret_name, ::GraphQL::STRING_TYPE, null: true, authorize: :owner_access
field :secret_name, ::GraphQL::Types::String, null: true, authorize: :owner_access
end
end
```
@ -179,7 +179,7 @@ as an array instead of as a single value:
```ruby
module Types
class MyType < BaseObject
field :hidden_field, ::GraphQL::INT_TYPE,
field :hidden_field, ::GraphQL::Types::Int,
null: true,
authorize: [:owner_access, :another_ability]
end

View File

@ -29,7 +29,7 @@ For example, to load a `User` by `username`, we can add batching as follows:
```ruby
class UserResolver < BaseResolver
type UserType, null: true
argument :username, ::GraphQL::STRING_TYPE, required: true
argument :username, ::GraphQL::Types::String, required: true
def resolve(**args)
BatchLoader::GraphQL.for(username).batch do |usernames, loader|

View File

@ -13,27 +13,40 @@ You can customize some of the content in emails sent from your GitLab instance.
The logo in the header of some emails can be customized, see the [logo customization section](../appearance.md#navigation-bar).
## Custom additional text **(PREMIUM SELF)**
## Include author name in email notification email body **(PREMIUM SELF)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/5031) in [GitLab Premium](https://about.gitlab.com/pricing/) 10.7.
By default, GitLab overrides the email address in notification emails with the email address
of the issue, merge request, or comment author. Enable this setting to include the author's email
address in the body of the email instead.
The additional text appears at the bottom of any email and can be used for
legal/auditing/compliance reasons.
To include the author's email address in the email body:
1. On the top bar, select **Menu >** **{admin}** **Admin**.
1. On the left sidebar, select **Settings > Preferences** (`/admin/application_settings/preferences`).
1. Expand **Email**.
1. Enter your text in the **Additional text** field.
1. Click **Save**.
1. Select the **Include author name in email notification email body** checkbox.
1. Select **Save changes**.
## Custom hostname for private commit emails
## Enable multipart email **(PREMIUM SELF)**
GitLab can send email in multipart format (HTML and plain text) or plain text only.
To enable multipart email:
1. On the top bar, select **Menu >** **{admin}** **Admin**.
1. On the left sidebar, select **Settings > Preferences** (`/admin/application_settings/preferences`).
1. Expand **Email**.
1. Select **Enable multipart email**.
1. Select **Save changes**.
## Custom hostname for private commit emails **(PREMIUM SELF)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/22560) in GitLab 11.5.
This configuration option sets the email hostname for [private commit emails](../../profile/index.md#use-an-automatically-generated-private-commit-email).
By default it is set to `users.noreply.YOUR_CONFIGURED_HOSTNAME`.
In order to change this option:
To change the hostname used in private commit emails:
1. On the top bar, select **Menu >** **{admin}** **Admin**.
1. On the left sidebar, select **Settings > Preferences** (`/admin/application_settings/preferences`).
@ -42,10 +55,23 @@ In order to change this option:
1. Select **Save changes**.
NOTE:
After the hostname gets configured, every private commit email using the previous hostname is not
After the hostname is configured, every private commit email using the previous hostname is not
recognized by GitLab. This can directly conflict with certain [Push rules](../../../push_rules/push_rules.md) such as
`Check whether author is a GitLab user` and `Check whether committer is the current authenticated user`.
## Custom additional text **(PREMIUM SELF)**
You can add additional text at the bottom of any email that GitLab sends. This additional text
can be used for legal, auditing, or compliance reasons, for example.
To add additional text to emails:
1. On the top bar, select **Menu >** **{admin}** **Admin**.
1. On the left sidebar, select **Settings > Preferences** (`/admin/application_settings/preferences`).
1. Expand **Email**.
1. Enter your text in the **Additional text** field.
1. Select **Save changes**.
<!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues

View File

@ -47,7 +47,7 @@ module API
path = params[:path]
before = params[:until]
after = params[:since]
ref = params[:ref_name].presence || user_project.try(:default_branch) || 'master' unless params[:all]
ref = params[:ref_name].presence || user_project.default_branch unless params[:all]
offset = (params[:page] - 1) * params[:per_page]
all = params[:all]
with_stats = params[:with_stats]

View File

@ -23,7 +23,7 @@ module API
optional :mentions_disabled, type: Boolean, desc: 'Disable a group from getting mentioned'
optional :lfs_enabled, type: Boolean, desc: 'Enable/disable LFS for the projects in this group'
optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access'
optional :default_branch_protection, type: Integer, values: ::Gitlab::Access.protection_values, desc: 'Determine if developers can push to master'
optional :default_branch_protection, type: Integer, values: ::Gitlab::Access.protection_values, desc: 'Determine if developers can push to default branch'
optional :shared_runners_setting, type: String, values: ::Namespace::SHARED_RUNNERS_SETTINGS, desc: 'Enable/disable shared runners for the group and its subgroups and projects'
end

View File

@ -55,7 +55,7 @@ module API
use :pagination
end
get ':id/repository/tree' do
ref = params[:ref] || user_project.try(:default_branch) || 'master'
ref = params[:ref] || user_project.default_branch
path = params[:path] || nil
commit = user_project.commit(ref)

View File

@ -48,7 +48,7 @@ module API
optional :default_artifacts_expire_in, type: String, desc: "Set the default expiration time for each job's artifacts"
optional :default_ci_config_path, type: String, desc: 'The instance default CI/CD configuration file and path for new projects'
optional :default_project_creation, type: Integer, values: ::Gitlab::Access.project_creation_values, desc: 'Determine if developers can create projects in the group'
optional :default_branch_protection, type: Integer, values: ::Gitlab::Access.protection_values, desc: 'Determine if developers can push to master'
optional :default_branch_protection, type: Integer, values: ::Gitlab::Access.protection_values, desc: 'Determine if developers can push to default branch'
optional :default_group_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default group visibility'
optional :default_project_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default project visibility'
optional :default_projects_limit, type: Integer, desc: 'The maximum number of personal projects'

View File

@ -81,7 +81,7 @@ module Atlassian
end
def store_dev_info(project:, commits: nil, branches: nil, merge_requests: nil, update_sequence_id: nil)
repo = Serializers::RepositoryEntity.represent(
repo = ::Atlassian::JiraConnect::Serializers::RepositoryEntity.represent(
project,
commits: commits,
branches: branches,

View File

@ -5,7 +5,7 @@ module Gitlab
module CycleAnalytics
module StageQueryHelpers
def execute_query(query)
ActiveRecord::Base.connection.execute(query.to_sql)
ApplicationRecord.connection.execute(query.to_sql)
end
def zero_interval

View File

@ -31,7 +31,7 @@ module Gitlab
expected_end_time = Time.now + duration_s
while Time.now < expected_end_time
ActiveRecord::Base.connection.execute("SELECT 1")
ApplicationRecord.connection.execute("SELECT 1")
end_interval_time = Time.now + [duration_s, interval_s].min
rand while Time.now < end_interval_time

View File

@ -122,7 +122,7 @@ module Gitlab
def empty_project_push_message
<<~MESSAGE
A default branch (e.g. master) does not yet exist for #{project.full_path}
A default branch (e.g. main) does not yet exist for #{project.full_path}
Ask a project Owner or Maintainer to create a default branch:
#{project_members_url}

View File

@ -5,7 +5,7 @@
# This template is on early stage of development.
# Use it with caution. For usage instruction please read
# https://gitlab.com/gitlab-org/5-minute-production-app/deploy-template/-/blob/v2.3.0/README.md
# https://gitlab.com/gitlab-org/5-minute-production-app/deploy-template/-/blob/v3.0.0/README.md
include:
# workflow rules to prevent duplicate detached pipelines

View File

@ -31,7 +31,7 @@ module Gitlab
end
def count(batch_size: nil, mode: :itself, start: nil, finish: nil)
raise 'BatchCount can not be run inside a transaction' if ActiveRecord::Base.connection.transaction_open?
raise 'BatchCount can not be run inside a transaction' if @relation.connection.transaction_open?
check_mode!(mode)

View File

@ -54,7 +54,7 @@ module Gitlab
# Querying tuple stats only works on the primary. Due to load balancing, the
# easiest way to do this is to start a transaction.
ActiveRecord::Base.transaction do
ActiveRecord::Base.transaction do # rubocop: disable Database/MultipleDatabases
get_statistics(non_sti_table_names, check_statistics: check_statistics).each_with_object({}) do |row, data|
model = table_to_model[row.table_name]
data[model] = row.estimate

View File

@ -61,7 +61,7 @@ module Gitlab
#{where_clause(model)}
SQL
rows = ActiveRecord::Base.connection.select_all(query)
rows = ActiveRecord::Base.connection.select_all(query) # rubocop: disable Database/MultipleDatabases
Integer(rows.first['count'])
end

View File

@ -10,7 +10,7 @@ module Gitlab
# We _must not_ use quote_table_name as this will produce double
# quotes on PostgreSQL and for "has_table_privilege" we need single
# quotes.
connection = ActiveRecord::Base.connection
connection = ActiveRecord::Base.connection # rubocop: disable Database/MultipleDatabases
quoted_table = connection.quote(table)
begin

View File

@ -57,7 +57,7 @@ module Gitlab
# @param finish final pkey range
# @return [Gitlab::Database::PostgresHll::Buckets] HyperLogLog data structure instance that can estimate number of unique elements
def execute(batch_size: nil, start: nil, finish: nil)
raise 'BatchCount can not be run inside a transaction' if ActiveRecord::Base.connection.transaction_open?
raise 'BatchCount can not be run inside a transaction' if ActiveRecord::Base.connection.transaction_open? # rubocop: disable Database/MultipleDatabases
batch_size ||= DEFAULT_BATCH_SIZE
start = actual_start(start)

Some files were not shown because too many files have changed in this diff Show More