summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/packages_and_registries
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/packages_and_registries')
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/additional_metadata.vue94
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/composer_installation.vue65
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/conan_installation.vue59
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/dependency_row.vue35
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/file_sha.vue41
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/installation_commands.vue55
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/installation_title.vue38
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/maven_installation.vue152
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/npm_installation.vue103
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/nuget_installation.vue58
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/package_files.vue165
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/package_history.vue167
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/pypi_installation.vue71
13 files changed, 1103 insertions, 0 deletions
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/additional_metadata.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/additional_metadata.vue
new file mode 100644
index 00000000000..89add0a0d31
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/additional_metadata.vue
@@ -0,0 +1,94 @@
+<script>
+import { GlLink, GlSprintf } from '@gitlab/ui';
+import { s__ } from '~/locale';
+import { PackageType } from '~/packages/shared/constants';
+import DetailsRow from '~/vue_shared/components/registry/details_row.vue';
+
+export default {
+ i18n: {
+ sourceText: s__('PackageRegistry|Source project located at %{link}'),
+ licenseText: s__('PackageRegistry|License information located at %{link}'),
+ recipeText: s__('PackageRegistry|Recipe: %{recipe}'),
+ appGroup: s__('PackageRegistry|App group: %{group}'),
+ appName: s__('PackageRegistry|App name: %{name}'),
+ },
+ components: {
+ DetailsRow,
+ GlLink,
+ GlSprintf,
+ },
+ props: {
+ packageEntity: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ showMetadata() {
+ const visibilityConditions = {
+ [PackageType.NUGET]: this.packageEntity.nuget_metadatum,
+ [PackageType.CONAN]: this.packageEntity.conan_metadatum,
+ [PackageType.MAVEN]: this.packageEntity.maven_metadatum,
+ };
+ return visibilityConditions[this.packageEntity.package_type];
+ },
+ },
+};
+</script>
+
+<template>
+ <div v-if="showMetadata">
+ <h3 class="gl-font-lg" data-testid="title">{{ __('Additional Metadata') }}</h3>
+
+ <div class="gl-bg-gray-50 gl-inset-border-1-gray-100 gl-rounded-base" data-testid="main">
+ <template v-if="packageEntity.nuget_metadatum">
+ <details-row icon="project" padding="gl-p-4" dashed data-testid="nuget-source">
+ <gl-sprintf :message="$options.i18n.sourceText">
+ <template #link>
+ <gl-link :href="packageEntity.nuget_metadatum.project_url" target="_blank">{{
+ packageEntity.nuget_metadatum.project_url
+ }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </details-row>
+ <details-row icon="license" padding="gl-p-4" data-testid="nuget-license">
+ <gl-sprintf :message="$options.i18n.licenseText">
+ <template #link>
+ <gl-link :href="packageEntity.nuget_metadatum.license_url" target="_blank">{{
+ packageEntity.nuget_metadatum.license_url
+ }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </details-row>
+ </template>
+
+ <details-row
+ v-else-if="packageEntity.conan_metadatum"
+ icon="information-o"
+ padding="gl-p-4"
+ data-testid="conan-recipe"
+ >
+ <gl-sprintf :message="$options.i18n.recipeText">
+ <template #recipe>{{ packageEntity.name }}</template>
+ </gl-sprintf>
+ </details-row>
+
+ <template v-else-if="packageEntity.maven_metadatum">
+ <details-row icon="information-o" padding="gl-p-4" dashed data-testid="maven-app">
+ <gl-sprintf :message="$options.i18n.appName">
+ <template #name>
+ <strong>{{ packageEntity.maven_metadatum.app_name }}</strong>
+ </template>
+ </gl-sprintf>
+ </details-row>
+ <details-row icon="information-o" padding="gl-p-4" data-testid="maven-group">
+ <gl-sprintf :message="$options.i18n.appGroup">
+ <template #group>
+ <strong>{{ packageEntity.maven_metadatum.app_group }}</strong>
+ </template>
+ </gl-sprintf>
+ </details-row>
+ </template>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/composer_installation.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/composer_installation.vue
new file mode 100644
index 00000000000..b3979a620f0
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/composer_installation.vue
@@ -0,0 +1,65 @@
+<script>
+import { GlLink, GlSprintf } from '@gitlab/ui';
+import { mapGetters, mapState } from 'vuex';
+import { s__ } from '~/locale';
+import { TrackingActions, TrackingLabels } from '~/packages/details/constants';
+import InstallationTitle from '~/packages_and_registries/package_registry/components/details/installation_title.vue';
+import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue';
+
+export default {
+ name: 'ComposerInstallation',
+ components: {
+ InstallationTitle,
+ CodeInstruction,
+ GlLink,
+ GlSprintf,
+ },
+ computed: {
+ ...mapState(['composerHelpPath']),
+ ...mapGetters(['composerRegistryInclude', 'composerPackageInclude', 'groupExists']),
+ },
+ i18n: {
+ registryInclude: s__('PackageRegistry|Add composer registry'),
+ copyRegistryInclude: s__('PackageRegistry|Copy registry include'),
+ packageInclude: s__('PackageRegistry|Install package version'),
+ copyPackageInclude: s__('PackageRegistry|Copy require package include'),
+ infoLine: s__(
+ 'PackageRegistry|For more information on Composer packages in GitLab, %{linkStart}see the documentation.%{linkEnd}',
+ ),
+ },
+ trackingActions: { ...TrackingActions },
+ TrackingLabels,
+ installOptions: [{ value: 'composer', label: s__('PackageRegistry|Show Composer commands') }],
+};
+</script>
+
+<template>
+ <div v-if="groupExists" data-testid="root-node">
+ <installation-title package-type="composer" :options="$options.installOptions" />
+
+ <code-instruction
+ :label="$options.i18n.registryInclude"
+ :instruction="composerRegistryInclude"
+ :copy-text="$options.i18n.copyRegistryInclude"
+ :tracking-action="$options.trackingActions.COPY_COMPOSER_REGISTRY_INCLUDE_COMMAND"
+ :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ data-testid="registry-include"
+ />
+
+ <code-instruction
+ :label="$options.i18n.packageInclude"
+ :instruction="composerPackageInclude"
+ :copy-text="$options.i18n.copyPackageInclude"
+ :tracking-action="$options.trackingActions.COPY_COMPOSER_PACKAGE_INCLUDE_COMMAND"
+ :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ data-testid="package-include"
+ />
+ <span data-testid="help-text">
+ <gl-sprintf :message="$options.i18n.infoLine">
+ <template #link="{ content }">
+ <gl-link :href="composerHelpPath" target="_blank">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </span>
+ </div>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/conan_installation.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/conan_installation.vue
new file mode 100644
index 00000000000..59b446e46b5
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/conan_installation.vue
@@ -0,0 +1,59 @@
+<script>
+import { GlLink, GlSprintf } from '@gitlab/ui';
+import { mapGetters, mapState } from 'vuex';
+import { s__ } from '~/locale';
+import { TrackingActions, TrackingLabels } from '~/packages/details/constants';
+import InstallationTitle from '~/packages_and_registries/package_registry/components/details/installation_title.vue';
+import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue';
+
+export default {
+ name: 'ConanInstallation',
+ components: {
+ InstallationTitle,
+ CodeInstruction,
+ GlLink,
+ GlSprintf,
+ },
+ computed: {
+ ...mapState(['conanHelpPath']),
+ ...mapGetters(['conanInstallationCommand', 'conanSetupCommand']),
+ },
+ i18n: {
+ helpText: s__(
+ 'PackageRegistry|For more information on the Conan registry, %{linkStart}see the documentation%{linkEnd}.',
+ ),
+ },
+ trackingActions: { ...TrackingActions },
+ TrackingLabels,
+ installOptions: [{ value: 'conan', label: s__('PackageRegistry|Show Conan commands') }],
+};
+</script>
+
+<template>
+ <div>
+ <installation-title package-type="conan" :options="$options.installOptions" />
+
+ <code-instruction
+ :label="s__('PackageRegistry|Conan Command')"
+ :instruction="conanInstallationCommand"
+ :copy-text="s__('PackageRegistry|Copy Conan Command')"
+ :tracking-action="$options.trackingActions.COPY_CONAN_COMMAND"
+ :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ />
+
+ <h3 class="gl-font-lg">{{ __('Registry setup') }}</h3>
+
+ <code-instruction
+ :label="s__('PackageRegistry|Add Conan Remote')"
+ :instruction="conanSetupCommand"
+ :copy-text="s__('PackageRegistry|Copy Conan Setup Command')"
+ :tracking-action="$options.trackingActions.COPY_CONAN_SETUP_COMMAND"
+ :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ />
+ <gl-sprintf :message="$options.i18n.helpText">
+ <template #link="{ content }">
+ <gl-link :href="conanHelpPath" target="_blank">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </div>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/dependency_row.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/dependency_row.vue
new file mode 100644
index 00000000000..1a2202b23c8
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/dependency_row.vue
@@ -0,0 +1,35 @@
+<script>
+export default {
+ name: 'DependencyRow',
+ props: {
+ dependency: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ showVersion() {
+ return Boolean(this.dependency.version_pattern);
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="gl-responsive-table-row">
+ <div class="table-section section-50">
+ <strong class="gl-text-body">{{ dependency.name }}</strong>
+ <span v-if="dependency.target_framework" data-testid="target-framework"
+ >({{ dependency.target_framework }})</span
+ >
+ </div>
+
+ <div
+ v-if="showVersion"
+ class="table-section section-50 gl-display-flex gl-md-justify-content-end"
+ data-testid="version-pattern"
+ >
+ <span class="gl-text-body">{{ dependency.version_pattern }}</span>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/file_sha.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/file_sha.vue
new file mode 100644
index 00000000000..a25839be7e1
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/file_sha.vue
@@ -0,0 +1,41 @@
+<script>
+import { s__ } from '~/locale';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+import DetailsRow from '~/vue_shared/components/registry/details_row.vue';
+
+export default {
+ name: 'FileSha',
+ components: {
+ DetailsRow,
+ ClipboardButton,
+ },
+ props: {
+ sha: {
+ type: String,
+ required: true,
+ },
+ title: {
+ type: String,
+ required: true,
+ },
+ },
+ i18n: {
+ copyButtonTitle: s__('PackageRegistry|Copy SHA'),
+ },
+};
+</script>
+
+<template>
+ <details-row dashed>
+ <div class="gl-px-4">
+ {{ title }}:
+ {{ sha }}
+ <clipboard-button
+ :text="sha"
+ :title="$options.i18n.copyButtonTitle"
+ category="tertiary"
+ size="small"
+ />
+ </div>
+ </details-row>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/installation_commands.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/installation_commands.vue
new file mode 100644
index 00000000000..9ebfbbbf9e5
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/installation_commands.vue
@@ -0,0 +1,55 @@
+<script>
+import { PackageType, TERRAFORM_PACKAGE_TYPE } from '~/packages/shared/constants';
+import TerraformInstallation from '~/packages_and_registries/infrastructure_registry/components/terraform_installation.vue';
+import ComposerInstallation from './composer_installation.vue';
+import ConanInstallation from './conan_installation.vue';
+import MavenInstallation from './maven_installation.vue';
+import NpmInstallation from './npm_installation.vue';
+import NugetInstallation from './nuget_installation.vue';
+import PypiInstallation from './pypi_installation.vue';
+
+export default {
+ name: 'InstallationCommands',
+ components: {
+ [PackageType.CONAN]: ConanInstallation,
+ [PackageType.MAVEN]: MavenInstallation,
+ [PackageType.NPM]: NpmInstallation,
+ [PackageType.NUGET]: NugetInstallation,
+ [PackageType.PYPI]: PypiInstallation,
+ [PackageType.COMPOSER]: ComposerInstallation,
+ [TERRAFORM_PACKAGE_TYPE]: TerraformInstallation,
+ },
+ props: {
+ packageEntity: {
+ type: Object,
+ required: true,
+ },
+ npmPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ npmHelpPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+ computed: {
+ installationComponent() {
+ return this.$options.components[this.packageEntity.package_type];
+ },
+ },
+};
+</script>
+
+<template>
+ <div v-if="installationComponent">
+ <component
+ :is="installationComponent"
+ :name="packageEntity.name"
+ :registry-url="npmPath"
+ :help-url="npmHelpPath"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/installation_title.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/installation_title.vue
new file mode 100644
index 00000000000..43133bf7825
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/installation_title.vue
@@ -0,0 +1,38 @@
+<script>
+import PersistedDropdownSelection from '~/vue_shared/components/registry/persisted_dropdown_selection.vue';
+
+export default {
+ name: 'InstallationTitle',
+ components: {
+ PersistedDropdownSelection,
+ },
+ props: {
+ packageType: {
+ type: String,
+ required: true,
+ },
+ options: {
+ type: Array,
+ required: true,
+ },
+ },
+ computed: {
+ storageKey() {
+ return `package_${this.packageType}_installation_instructions`;
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="gl-display-flex gl-justify-content-space-between gl-align-items-center">
+ <h3 class="gl-font-lg">{{ __('Installation') }}</h3>
+ <div>
+ <persisted-dropdown-selection
+ :storage-key="storageKey"
+ :options="options"
+ @change="$emit('change', $event)"
+ />
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/maven_installation.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/maven_installation.vue
new file mode 100644
index 00000000000..b035e557d21
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/maven_installation.vue
@@ -0,0 +1,152 @@
+<script>
+import { GlLink, GlSprintf } from '@gitlab/ui';
+import { mapGetters, mapState } from 'vuex';
+import { s__ } from '~/locale';
+import { TrackingActions, TrackingLabels } from '~/packages/details/constants';
+import InstallationTitle from '~/packages_and_registries/package_registry/components/details/installation_title.vue';
+import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue';
+
+export default {
+ name: 'MavenInstallation',
+ components: {
+ InstallationTitle,
+ CodeInstruction,
+ GlLink,
+ GlSprintf,
+ },
+ data() {
+ return {
+ instructionType: 'maven',
+ };
+ },
+ computed: {
+ ...mapState(['mavenHelpPath']),
+ ...mapGetters([
+ 'mavenInstallationXml',
+ 'mavenInstallationCommand',
+ 'mavenSetupXml',
+ 'gradleGroovyInstalCommand',
+ 'gradleGroovyAddSourceCommand',
+ 'gradleKotlinInstalCommand',
+ 'gradleKotlinAddSourceCommand',
+ ]),
+ showMaven() {
+ return this.instructionType === 'maven';
+ },
+ showGroovy() {
+ return this.instructionType === 'groovy';
+ },
+ },
+ i18n: {
+ xmlText: s__(
+ `PackageRegistry|Copy and paste this inside your %{codeStart}pom.xml%{codeEnd} %{codeStart}dependencies%{codeEnd} block.`,
+ ),
+ setupText: s__(
+ `PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}pom.xml%{codeEnd} file.`,
+ ),
+ helpText: s__(
+ 'PackageRegistry|For more information on the Maven registry, %{linkStart}see the documentation%{linkEnd}.',
+ ),
+ },
+ trackingActions: { ...TrackingActions },
+ TrackingLabels,
+ installOptions: [
+ { value: 'maven', label: s__('PackageRegistry|Maven XML') },
+ { value: 'groovy', label: s__('PackageRegistry|Gradle Groovy DSL') },
+ { value: 'kotlin', label: s__('PackageRegistry|Gradle Kotlin DSL') },
+ ],
+};
+</script>
+
+<template>
+ <div>
+ <installation-title
+ package-type="maven"
+ :options="$options.installOptions"
+ @change="instructionType = $event"
+ />
+
+ <template v-if="showMaven">
+ <p>
+ <gl-sprintf :message="$options.i18n.xmlText">
+ <template #code="{ content }">
+ <code>{{ content }}</code>
+ </template>
+ </gl-sprintf>
+ </p>
+
+ <code-instruction
+ :instruction="mavenInstallationXml"
+ :copy-text="s__('PackageRegistry|Copy Maven XML')"
+ :tracking-action="$options.trackingActions.COPY_MAVEN_XML"
+ :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ multiline
+ />
+
+ <code-instruction
+ :label="s__('PackageRegistry|Maven Command')"
+ :instruction="mavenInstallationCommand"
+ :copy-text="s__('PackageRegistry|Copy Maven command')"
+ :tracking-action="$options.trackingActions.COPY_MAVEN_COMMAND"
+ :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ />
+
+ <h3 class="gl-font-lg">{{ s__('PackageRegistry|Registry setup') }}</h3>
+ <p>
+ <gl-sprintf :message="$options.i18n.setupText">
+ <template #code="{ content }">
+ <code>{{ content }}</code>
+ </template>
+ </gl-sprintf>
+ </p>
+ <code-instruction
+ :instruction="mavenSetupXml"
+ :copy-text="s__('PackageRegistry|Copy Maven registry XML')"
+ :tracking-action="$options.trackingActions.COPY_MAVEN_SETUP"
+ :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ multiline
+ />
+ <gl-sprintf :message="$options.i18n.helpText">
+ <template #link="{ content }">
+ <gl-link :href="mavenHelpPath" target="_blank">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </template>
+ <template v-else-if="showGroovy">
+ <code-instruction
+ class="gl-mb-5"
+ :label="s__('PackageRegistry|Gradle Groovy DSL install command')"
+ :instruction="gradleGroovyInstalCommand"
+ :copy-text="s__('PackageRegistry|Copy Gradle Groovy DSL install command')"
+ :tracking-action="$options.trackingActions.COPY_GRADLE_INSTALL_COMMAND"
+ :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ />
+ <code-instruction
+ :label="s__('PackageRegistry|Add Gradle Groovy DSL repository command')"
+ :instruction="gradleGroovyAddSourceCommand"
+ :copy-text="s__('PackageRegistry|Copy add Gradle Groovy DSL repository command')"
+ :tracking-action="$options.trackingActions.COPY_GRADLE_ADD_TO_SOURCE_COMMAND"
+ :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ multiline
+ />
+ </template>
+ <template v-else>
+ <code-instruction
+ class="gl-mb-5"
+ :label="s__('PackageRegistry|Gradle Kotlin DSL install command')"
+ :instruction="gradleKotlinInstalCommand"
+ :copy-text="s__('PackageRegistry|Copy Gradle Kotlin DSL install command')"
+ :tracking-action="$options.trackingActions.COPY_KOTLIN_INSTALL_COMMAND"
+ :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ />
+ <code-instruction
+ :label="s__('PackageRegistry|Add Gradle Kotlin DSL repository command')"
+ :instruction="gradleKotlinAddSourceCommand"
+ :copy-text="s__('PackageRegistry|Copy add Gradle Kotlin DSL repository command')"
+ :tracking-action="$options.trackingActions.COPY_KOTLIN_ADD_TO_SOURCE_COMMAND"
+ :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ multiline
+ />
+ </template>
+ </div>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/npm_installation.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/npm_installation.vue
new file mode 100644
index 00000000000..c178d3e97e9
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/npm_installation.vue
@@ -0,0 +1,103 @@
+<script>
+import { GlLink, GlSprintf } from '@gitlab/ui';
+import { mapGetters, mapState } from 'vuex';
+import { s__ } from '~/locale';
+import { NpmManager, TrackingActions, TrackingLabels } from '~/packages/details/constants';
+import InstallationTitle from '~/packages_and_registries/package_registry/components/details/installation_title.vue';
+import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue';
+
+export default {
+ name: 'NpmInstallation',
+ components: {
+ InstallationTitle,
+ CodeInstruction,
+ GlLink,
+ GlSprintf,
+ },
+ data() {
+ return {
+ instructionType: 'npm',
+ };
+ },
+ computed: {
+ ...mapState(['npmHelpPath']),
+ ...mapGetters(['npmInstallationCommand', 'npmSetupCommand']),
+ npmCommand() {
+ return this.npmInstallationCommand(NpmManager.NPM);
+ },
+ npmSetup() {
+ return this.npmSetupCommand(NpmManager.NPM);
+ },
+ yarnCommand() {
+ return this.npmInstallationCommand(NpmManager.YARN);
+ },
+ yarnSetupCommand() {
+ return this.npmSetupCommand(NpmManager.YARN);
+ },
+ showNpm() {
+ return this.instructionType === 'npm';
+ },
+ },
+ i18n: {
+ helpText: s__(
+ 'PackageRegistry|You may also need to setup authentication using an auth token. %{linkStart}See the documentation%{linkEnd} to find out more.',
+ ),
+ },
+ trackingActions: { ...TrackingActions },
+ TrackingLabels,
+ installOptions: [
+ { value: 'npm', label: s__('PackageRegistry|Show NPM commands') },
+ { value: 'yarn', label: s__('PackageRegistry|Show Yarn commands') },
+ ],
+};
+</script>
+
+<template>
+ <div>
+ <installation-title
+ package-type="npm"
+ :options="$options.installOptions"
+ @change="instructionType = $event"
+ />
+
+ <code-instruction
+ v-if="showNpm"
+ :instruction="npmCommand"
+ :copy-text="s__('PackageRegistry|Copy npm command')"
+ :tracking-action="$options.trackingActions.COPY_NPM_INSTALL_COMMAND"
+ :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ />
+
+ <code-instruction
+ v-else
+ :instruction="yarnCommand"
+ :copy-text="s__('PackageRegistry|Copy yarn command')"
+ :tracking-action="$options.trackingActions.COPY_YARN_INSTALL_COMMAND"
+ :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ />
+
+ <h3 class="gl-font-lg">{{ __('Registry setup') }}</h3>
+
+ <code-instruction
+ v-if="showNpm"
+ :instruction="npmSetup"
+ :copy-text="s__('PackageRegistry|Copy npm setup command')"
+ :tracking-action="$options.trackingActions.COPY_NPM_SETUP_COMMAND"
+ :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ />
+
+ <code-instruction
+ v-else
+ :instruction="yarnSetupCommand"
+ :copy-text="s__('PackageRegistry|Copy yarn setup command')"
+ :tracking-action="$options.trackingActions.COPY_YARN_SETUP_COMMAND"
+ :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ />
+
+ <gl-sprintf :message="$options.i18n.helpText">
+ <template #link="{ content }">
+ <gl-link :href="npmHelpPath" target="_blank">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </div>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/nuget_installation.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/nuget_installation.vue
new file mode 100644
index 00000000000..84493790b4d
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/nuget_installation.vue
@@ -0,0 +1,58 @@
+<script>
+import { GlLink, GlSprintf } from '@gitlab/ui';
+import { mapGetters, mapState } from 'vuex';
+import { s__ } from '~/locale';
+import { TrackingActions, TrackingLabels } from '~/packages/details/constants';
+import InstallationTitle from '~/packages_and_registries/package_registry/components/details/installation_title.vue';
+import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue';
+
+export default {
+ name: 'NugetInstallation',
+ components: {
+ InstallationTitle,
+ CodeInstruction,
+ GlLink,
+ GlSprintf,
+ },
+ computed: {
+ ...mapState(['nugetHelpPath']),
+ ...mapGetters(['nugetInstallationCommand', 'nugetSetupCommand']),
+ },
+ i18n: {
+ helpText: s__(
+ 'PackageRegistry|For more information on the NuGet registry, %{linkStart}see the documentation%{linkEnd}.',
+ ),
+ },
+ trackingActions: { ...TrackingActions },
+ TrackingLabels,
+ installOptions: [{ value: 'nuget', label: s__('PackageRegistry|Show Nuget commands') }],
+};
+</script>
+
+<template>
+ <div>
+ <installation-title package-type="nuget" :options="$options.installOptions" />
+
+ <code-instruction
+ :label="s__('PackageRegistry|NuGet Command')"
+ :instruction="nugetInstallationCommand"
+ :copy-text="s__('PackageRegistry|Copy NuGet Command')"
+ :tracking-action="$options.trackingActions.COPY_NUGET_INSTALL_COMMAND"
+ :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ />
+ <h3 class="gl-font-lg">{{ __('Registry setup') }}</h3>
+
+ <code-instruction
+ :label="s__('PackageRegistry|Add NuGet Source')"
+ :instruction="nugetSetupCommand"
+ :copy-text="s__('PackageRegistry|Copy NuGet Setup Command')"
+ :tracking-action="$options.trackingActions.COPY_NUGET_SETUP_COMMAND"
+ :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ />
+ <gl-sprintf :message="$options.i18n.helpText">
+ <template #link="{ content }">
+ <gl-link :href="nugetHelpPath" target="_blank">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </div>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_files.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_files.vue
new file mode 100644
index 00000000000..3c2876e902b
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_files.vue
@@ -0,0 +1,165 @@
+<script>
+import { GlLink, GlTable, GlDropdownItem, GlDropdown, GlIcon, GlButton } from '@gitlab/ui';
+import { last } from 'lodash';
+import { numberToHumanSize } from '~/lib/utils/number_utils';
+import { __ } from '~/locale';
+import FileSha from '~/packages_and_registries/package_registry/components/details/file_sha.vue';
+import Tracking from '~/tracking';
+import FileIcon from '~/vue_shared/components/file_icon.vue';
+import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+
+export default {
+ name: 'PackageFiles',
+ components: {
+ GlLink,
+ GlTable,
+ GlIcon,
+ GlDropdown,
+ GlDropdownItem,
+ GlButton,
+ FileIcon,
+ TimeAgoTooltip,
+ FileSha,
+ },
+ mixins: [Tracking.mixin()],
+ props: {
+ packageFiles: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ canDelete: {
+ type: Boolean,
+ default: false,
+ required: false,
+ },
+ },
+ computed: {
+ filesTableRows() {
+ return this.packageFiles.map((pf) => ({
+ ...pf,
+ size: this.formatSize(pf.size),
+ pipeline: last(pf.pipelines),
+ }));
+ },
+ showCommitColumn() {
+ return this.filesTableRows.some((row) => Boolean(row.pipeline?.id));
+ },
+ filesTableHeaderFields() {
+ return [
+ {
+ key: 'name',
+ label: __('Name'),
+ },
+ {
+ key: 'commit',
+ label: __('Commit'),
+ hide: !this.showCommitColumn,
+ },
+ {
+ key: 'size',
+ label: __('Size'),
+ },
+ {
+ key: 'created',
+ label: __('Created'),
+ class: 'gl-text-right',
+ },
+ {
+ key: 'actions',
+ label: '',
+ hide: !this.canDelete,
+ class: 'gl-text-right',
+ tdClass: 'gl-w-4',
+ },
+ ].filter((c) => !c.hide);
+ },
+ },
+ methods: {
+ formatSize(size) {
+ return numberToHumanSize(size);
+ },
+ hasDetails(item) {
+ return item.file_sha256 || item.file_md5 || item.file_sha1;
+ },
+ },
+ i18n: {
+ deleteFile: __('Delete file'),
+ },
+};
+</script>
+
+<template>
+ <div>
+ <h3 class="gl-font-lg gl-mt-5">{{ __('Files') }}</h3>
+ <gl-table
+ :fields="filesTableHeaderFields"
+ :items="filesTableRows"
+ :tbody-tr-attr="{ 'data-testid': 'file-row' }"
+ >
+ <template #cell(name)="{ item, toggleDetails, detailsShowing }">
+ <gl-button
+ v-if="hasDetails(item)"
+ :icon="detailsShowing ? 'angle-up' : 'angle-down'"
+ :aria-label="detailsShowing ? __('Collapse') : __('Expand')"
+ category="tertiary"
+ size="small"
+ @click="toggleDetails"
+ />
+ <gl-link
+ :href="item.download_path"
+ class="gl-text-gray-500"
+ data-testid="download-link"
+ @click="$emit('download-file')"
+ >
+ <file-icon
+ :file-name="item.file_name"
+ css-classes="gl-relative file-icon"
+ class="gl-mr-1 gl-relative"
+ />
+ <span>{{ item.file_name }}</span>
+ </gl-link>
+ </template>
+
+ <template #cell(commit)="{ item }">
+ <gl-link
+ v-if="item.pipeline && item.pipeline.project"
+ :href="item.pipeline.project.commit_url"
+ class="gl-text-gray-500"
+ data-testid="commit-link"
+ >{{ item.pipeline.git_commit_message }}</gl-link
+ >
+ </template>
+
+ <template #cell(created)="{ item }">
+ <time-ago-tooltip :time="item.created_at" />
+ </template>
+
+ <template #cell(actions)="{ item }">
+ <gl-dropdown category="tertiary" right>
+ <template #button-content>
+ <gl-icon name="ellipsis_v" />
+ </template>
+ <gl-dropdown-item data-testid="delete-file" @click="$emit('delete-file', item)">
+ {{ $options.i18n.deleteFile }}
+ </gl-dropdown-item>
+ </gl-dropdown>
+ </template>
+
+ <template #row-details="{ item }">
+ <div
+ class="gl-display-flex gl-flex-direction-column gl-flex-grow-1 gl-bg-gray-10 gl-rounded-base gl-inset-border-1-gray-100"
+ >
+ <file-sha
+ v-if="item.file_sha256"
+ data-testid="sha-256"
+ title="SHA-256"
+ :sha="item.file_sha256"
+ />
+ <file-sha v-if="item.file_md5" data-testid="md5" title="MD5" :sha="item.file_md5" />
+ <file-sha v-if="item.file_sha1" data-testid="sha-1" title="SHA-1" :sha="item.file_sha1" />
+ </div>
+ </template>
+ </gl-table>
+ </div>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_history.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_history.vue
new file mode 100644
index 00000000000..0d7a73c12f1
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_history.vue
@@ -0,0 +1,167 @@
+<script>
+import { GlLink, GlSprintf } from '@gitlab/ui';
+import { first } from 'lodash';
+import { truncateSha } from '~/lib/utils/text_utility';
+import { s__, n__ } from '~/locale';
+import { HISTORY_PIPELINES_LIMIT } from '~/packages/details/constants';
+import HistoryItem from '~/vue_shared/components/registry/history_item.vue';
+import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+
+export default {
+ name: 'PackageHistory',
+ i18n: {
+ createdOn: s__('PackageRegistry|%{name} version %{version} was first created %{datetime}'),
+ createdByCommitText: s__('PackageRegistry|Created by commit %{link} on branch %{branch}'),
+ createdByPipelineText: s__(
+ 'PackageRegistry|Built by pipeline %{link} triggered %{datetime} by %{author}',
+ ),
+ publishText: s__('PackageRegistry|Published to the %{project} Package Registry %{datetime}'),
+ combinedUpdateText: s__(
+ 'PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}',
+ ),
+ archivedPipelineMessageSingular: s__('PackageRegistry|Package has %{number} archived update'),
+ archivedPipelineMessagePlural: s__('PackageRegistry|Package has %{number} archived updates'),
+ },
+ components: {
+ GlLink,
+ GlSprintf,
+ HistoryItem,
+ TimeAgoTooltip,
+ },
+ props: {
+ packageEntity: {
+ type: Object,
+ required: true,
+ },
+ projectName: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ showDescription: false,
+ };
+ },
+ computed: {
+ pipelines() {
+ return this.packageEntity.pipelines || [];
+ },
+ firstPipeline() {
+ return first(this.pipelines);
+ },
+ lastPipelines() {
+ return this.pipelines.slice(1).slice(-HISTORY_PIPELINES_LIMIT);
+ },
+ showPipelinesInfo() {
+ return Boolean(this.firstPipeline?.id);
+ },
+ archiviedLines() {
+ return Math.max(this.pipelines.length - HISTORY_PIPELINES_LIMIT - 1, 0);
+ },
+ archivedPipelineMessage() {
+ return n__(
+ this.$options.i18n.archivedPipelineMessageSingular,
+ this.$options.i18n.archivedPipelineMessagePlural,
+ this.archiviedLines,
+ );
+ },
+ },
+ methods: {
+ truncate(value) {
+ return truncateSha(value);
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="issuable-discussion">
+ <h3 class="gl-font-lg" data-testid="title">{{ __('History') }}</h3>
+ <ul class="timeline main-notes-list notes gl-mb-4" data-testid="timeline">
+ <history-item icon="clock" data-testid="created-on">
+ <gl-sprintf :message="$options.i18n.createdOn">
+ <template #name>
+ <strong>{{ packageEntity.name }}</strong>
+ </template>
+ <template #version>
+ <strong>{{ packageEntity.version }}</strong>
+ </template>
+ <template #datetime>
+ <time-ago-tooltip :time="packageEntity.created_at" />
+ </template>
+ </gl-sprintf>
+ </history-item>
+
+ <template v-if="showPipelinesInfo">
+ <!-- FIRST PIPELINE BLOCK -->
+ <history-item icon="commit" data-testid="first-pipeline-commit">
+ <gl-sprintf :message="$options.i18n.createdByCommitText">
+ <template #link>
+ <gl-link :href="firstPipeline.project.commit_url"
+ >#{{ truncate(firstPipeline.sha) }}</gl-link
+ >
+ </template>
+ <template #branch>
+ <strong>{{ firstPipeline.ref }}</strong>
+ </template>
+ </gl-sprintf>
+ </history-item>
+ <history-item icon="pipeline" data-testid="first-pipeline-pipeline">
+ <gl-sprintf :message="$options.i18n.createdByPipelineText">
+ <template #link>
+ <gl-link :href="firstPipeline.project.pipeline_url">#{{ firstPipeline.id }}</gl-link>
+ </template>
+ <template #datetime>
+ <time-ago-tooltip :time="firstPipeline.created_at" />
+ </template>
+ <template #author>{{ firstPipeline.user.name }}</template>
+ </gl-sprintf>
+ </history-item>
+ </template>
+
+ <!-- PUBLISHED LINE -->
+ <history-item icon="package" data-testid="published">
+ <gl-sprintf :message="$options.i18n.publishText">
+ <template #project>
+ <strong>{{ projectName }}</strong>
+ </template>
+ <template #datetime>
+ <time-ago-tooltip :time="packageEntity.created_at" />
+ </template>
+ </gl-sprintf>
+ </history-item>
+
+ <history-item v-if="archiviedLines" icon="history" data-testid="archived">
+ <gl-sprintf :message="archivedPipelineMessage">
+ <template #number>
+ <strong>{{ archiviedLines }}</strong>
+ </template>
+ </gl-sprintf>
+ </history-item>
+
+ <!-- PIPELINES LIST ENTRIES -->
+ <history-item
+ v-for="pipeline in lastPipelines"
+ :key="pipeline.id"
+ icon="pencil"
+ data-testid="pipeline-entry"
+ >
+ <gl-sprintf :message="$options.i18n.combinedUpdateText">
+ <template #link>
+ <gl-link :href="pipeline.project.commit_url">#{{ truncate(pipeline.sha) }}</gl-link>
+ </template>
+ <template #branch>
+ <strong>{{ pipeline.ref }}</strong>
+ </template>
+ <template #pipeline>
+ <gl-link :href="pipeline.project.pipeline_url">#{{ pipeline.id }}</gl-link>
+ </template>
+ <template #datetime>
+ <time-ago-tooltip :time="pipeline.created_at" />
+ </template>
+ </gl-sprintf>
+ </history-item>
+ </ul>
+ </div>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/pypi_installation.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/pypi_installation.vue
new file mode 100644
index 00000000000..21eba44a720
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/pypi_installation.vue
@@ -0,0 +1,71 @@
+<script>
+import { GlLink, GlSprintf } from '@gitlab/ui';
+import { mapGetters, mapState } from 'vuex';
+import { s__ } from '~/locale';
+import { TrackingActions, TrackingLabels } from '~/packages/details/constants';
+import InstallationTitle from '~/packages_and_registries/package_registry/components/details/installation_title.vue';
+import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue';
+
+export default {
+ name: 'PyPiInstallation',
+ components: {
+ InstallationTitle,
+ CodeInstruction,
+ GlLink,
+ GlSprintf,
+ },
+ computed: {
+ ...mapState(['pypiHelpPath']),
+ ...mapGetters(['pypiPipCommand', 'pypiSetupCommand']),
+ },
+ i18n: {
+ setupText: s__(
+ `PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file.`,
+ ),
+ helpText: s__(
+ 'PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}.',
+ ),
+ },
+ trackingActions: { ...TrackingActions },
+ TrackingLabels,
+ installOptions: [{ value: 'pypi', label: s__('PackageRegistry|Show PyPi commands') }],
+};
+</script>
+
+<template>
+ <div>
+ <installation-title package-type="pypi" :options="$options.installOptions" />
+
+ <code-instruction
+ :label="s__('PackageRegistry|Pip Command')"
+ :instruction="pypiPipCommand"
+ :copy-text="s__('PackageRegistry|Copy Pip command')"
+ data-testid="pip-command"
+ :tracking-action="$options.trackingActions.COPY_PIP_INSTALL_COMMAND"
+ :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ />
+
+ <h3 class="gl-font-lg">{{ __('Registry setup') }}</h3>
+ <p>
+ <gl-sprintf :message="$options.i18n.setupText">
+ <template #code="{ content }">
+ <code>{{ content }}</code>
+ </template>
+ </gl-sprintf>
+ </p>
+
+ <code-instruction
+ :instruction="pypiSetupCommand"
+ :copy-text="s__('PackageRegistry|Copy .pypirc content')"
+ data-testid="pypi-setup-content"
+ multiline
+ :tracking-action="$options.trackingActions.COPY_PYPI_SETUP_COMMAND"
+ :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
+ />
+ <gl-sprintf :message="$options.i18n.helpText">
+ <template #link="{ content }">
+ <gl-link :href="pypiHelpPath" target="_blank">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </div>
+</template>