+
+
{{ __('Installation') }}
+
+
+
+ {{ __('Registry setup') }}
+
+
+
+
+ {{ content }}
+
+
+
+
diff --git a/app/assets/javascripts/packages/details/components/installation_commands.vue b/app/assets/javascripts/packages/details/components/installation_commands.vue
index ed55d7fe782a56e013abfc61ca38cef3fe593eb7..48da63130e7c9987dadab42672344979b80234b5 100644
--- a/app/assets/javascripts/packages/details/components/installation_commands.vue
+++ b/app/assets/javascripts/packages/details/components/installation_commands.vue
@@ -3,6 +3,7 @@ import TerraformInstallation from '~/packages_and_registries/infrastructure_regi
import { PackageType, TERRAFORM_PACKAGE_TYPE } from '../../shared/constants';
import ComposerInstallation from './composer_installation.vue';
import ConanInstallation from './conan_installation.vue';
+import DebianInstallation from './debian_installation.vue';
import MavenInstallation from './maven_installation.vue';
import NpmInstallation from './npm_installation.vue';
import NugetInstallation from './nuget_installation.vue';
@@ -12,6 +13,7 @@ export default {
name: 'InstallationCommands',
components: {
[PackageType.CONAN]: ConanInstallation,
+ [PackageType.DEBIAN]: DebianInstallation,
[PackageType.MAVEN]: MavenInstallation,
[PackageType.NPM]: NpmInstallation,
[PackageType.NUGET]: NugetInstallation,
diff --git a/app/assets/javascripts/packages/details/constants.js b/app/assets/javascripts/packages/details/constants.js
index cd34b1ad45ac476a18e6466db80f0c1dc5570974..9cf0ff5148d5f66124a59c29a3fb33e773da09aa 100644
--- a/app/assets/javascripts/packages/details/constants.js
+++ b/app/assets/javascripts/packages/details/constants.js
@@ -3,6 +3,7 @@ import { s__ } from '~/locale';
export const TrackingLabels = {
CODE_INSTRUCTION: 'code_instruction',
CONAN_INSTALLATION: 'conan_installation',
+ DEBIAN_INSTALLATION: 'debian_installation',
MAVEN_INSTALLATION: 'maven_installation',
NPM_INSTALLATION: 'npm_installation',
NUGET_INSTALLATION: 'nuget_installation',
@@ -17,6 +18,9 @@ export const TrackingActions = {
COPY_CONAN_COMMAND: 'copy_conan_command',
COPY_CONAN_SETUP_COMMAND: 'copy_conan_setup_command',
+ COPY_DEBIAN_COMMAND: 'copy_debian_command',
+ COPY_DEBIAN_SETUP_COMMAND: 'copy_debian_setup_command',
+
COPY_MAVEN_XML: 'copy_maven_xml',
COPY_MAVEN_COMMAND: 'copy_maven_command',
COPY_MAVEN_SETUP: 'copy_maven_setup_xml',
diff --git a/app/assets/javascripts/packages/details/store/getters.js b/app/assets/javascripts/packages/details/store/getters.js
index ae273e26d6af7aa70baed804683e8f85febe5b04..19f40eac696bc2a2efd608f5a08ceac15916327a 100644
--- a/app/assets/javascripts/packages/details/store/getters.js
+++ b/app/assets/javascripts/packages/details/store/getters.js
@@ -27,6 +27,25 @@ export const conanSetupCommand = ({ conanPath }) =>
// eslint-disable-next-line @gitlab/require-i18n-strings
`conan remote add gitlab ${conanPath}`;
+export const debianInstallationCommand = ({ packageEntity }) => {
+ const distributionCodename = packageEntity.debian_distributions[0].codename;
+ const debNames = packageEntity.package_files
+ .filter((x) => x.file_name.endsWith('.deb'))
+ .map((x) => {
+ return x.file_name.split('_')[0];
+ })
+ .join(' ');
+ return `apt-get install -t ${distributionCodename} ${debNames}`;
+};
+
+export const debianSetupCommand = ({ debianPath, packageEntity }) => {
+ const distributionCodename = packageEntity.debian_distributions[0].codename;
+ const distributionComponents = packageEntity.debian_distributions[0].components.join(' ');
+ return `echo 'deb ${debianPath} ${distributionCodename} ${distributionComponents}' \\
+ > /etc/apt/sources.list.d/${distributionCodename}.list
+apt-get update`;
+};
+
export const mavenInstallationXml = ({ packageEntity = {} }) => {
const {
app_group: appGroup = '',
diff --git a/app/assets/javascripts/packages/list/constants.js b/app/assets/javascripts/packages/list/constants.js
index d871c2e4d2494389423644f4d48244f13a39e5dd..2c0e03199748441fab885ee3776c6e1175027738 100644
--- a/app/assets/javascripts/packages/list/constants.js
+++ b/app/assets/javascripts/packages/list/constants.js
@@ -61,11 +61,14 @@ export const PACKAGE_TYPES = [
title: s__('PackageRegistry|Conan'),
type: PackageType.CONAN,
},
+ {
+ title: s__('PackageRegistry|Debian'),
+ type: PackageType.DEBIAN,
+ },
{
title: s__('PackageRegistry|Generic'),
type: PackageType.GENERIC,
},
-
{
title: s__('PackageRegistry|Maven'),
type: PackageType.MAVEN,
diff --git a/app/assets/javascripts/packages/shared/constants.js b/app/assets/javascripts/packages/shared/constants.js
index 0ef6a3d0d128b0db1a6be71601847e5ae5a2c0e1..db9a2d6bf42efe2378f6e789a517d80fd74e6d3e 100644
--- a/app/assets/javascripts/packages/shared/constants.js
+++ b/app/assets/javascripts/packages/shared/constants.js
@@ -2,6 +2,7 @@ import { __, s__ } from '~/locale';
export const PackageType = {
CONAN: 'conan',
+ DEBIAN: 'debian',
MAVEN: 'maven',
NPM: 'npm',
NUGET: 'nuget',
@@ -28,6 +29,7 @@ export const TrackingCategories = {
[PackageType.MAVEN]: 'MavenPackages',
[PackageType.NPM]: 'NpmPackages',
[PackageType.CONAN]: 'ConanPackages',
+ [PackageType.DEBIAN]: 'DebianPackages',
};
export const SHOW_DELETE_SUCCESS_ALERT = 'showSuccessDeleteAlert';
diff --git a/app/assets/javascripts/packages/shared/utils.js b/app/assets/javascripts/packages/shared/utils.js
index bd35a47ca4dead62a4f4ee8e9549eab90122b547..4ea136ac82fb9f0cf8894465ab030724e43305e1 100644
--- a/app/assets/javascripts/packages/shared/utils.js
+++ b/app/assets/javascripts/packages/shared/utils.js
@@ -11,6 +11,8 @@ export const getPackageTypeLabel = (packageType) => {
switch (packageType) {
case PackageType.CONAN:
return s__('PackageRegistry|Conan');
+ case PackageType.DEBIAN:
+ return s__('PackageRegistry|Debian');
case PackageType.MAVEN:
return s__('PackageRegistry|Maven');
case PackageType.NPM:
diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb
index ba06b98e9066005716bbd2fe463e3a120ac9366e..5f8e9dcd14ec67dcd99b6c91c430542724daa422 100644
--- a/app/policies/group_policy.rb
+++ b/app/policies/group_policy.rb
@@ -117,6 +117,7 @@ class GroupPolicy < BasePolicy
enable :delete_metrics_dashboard_annotation
enable :update_metrics_dashboard_annotation
enable :create_custom_emoji
+ enable :create_package
enable :create_package_settings
end
@@ -134,6 +135,7 @@ class GroupPolicy < BasePolicy
end
rule { maintainer }.policy do
+ enable :destroy_package
enable :create_projects
enable :admin_pipeline
enable :admin_build
diff --git a/app/presenters/packages/detail/package_presenter.rb b/app/presenters/packages/detail/package_presenter.rb
index 59e50b96ab22f9c0f8fce884d1ca1a86244354f4..2c0b1fef3adb4ef530146b215d252e0e8dee51ac 100644
--- a/app/presenters/packages/detail/package_presenter.rb
+++ b/app/presenters/packages/detail/package_presenter.rb
@@ -8,10 +8,35 @@ def initialize(package)
end
def detail_view
+ package_detail = build_basic_package_detail
+
+ package_detail[:conan_package_name] = @package.name if @package.conan?
+ package_detail[:maven_metadatum] = @package.maven_metadatum if @package.maven_metadatum
+ package_detail[:nuget_metadatum] = @package.nuget_metadatum if @package.nuget_metadatum
+ package_detail[:composer_metadatum] = @package.composer_metadatum if @package.composer_metadatum
+ package_detail[:conan_metadatum] = @package.conan_metadatum if @package.conan_metadatum
+
+ if @package.debian_package?
+ debian_distribution = @package&.debian_publication&.distribution
+ if debian_distribution
+ package_detail[:debian_distributions] = [build_debian_distribution(debian_distribution)]
+ end
+ end
+
+ package_detail[:dependency_links] = @package.dependency_links.map(&method(:build_dependency_links))
+ package_detail[:pipeline] = build_pipeline_info(@package.pipeline) if @package.pipeline
+ package_detail[:pipelines] = build_pipeline_infos(@package.pipelines) if @package.pipelines.present?
+
+ package_detail
+ end
+
+ private
+
+ def build_basic_package_detail
name = @package.name
name = @package.conan_recipe if @package.conan?
- package_detail = {
+ {
id: @package.id,
created_at: @package.created_at,
name: name,
@@ -23,21 +48,8 @@ def detail_view
updated_at: @package.updated_at,
version: @package.version
}
-
- package_detail[:conan_package_name] = @package.name if @package.conan?
- package_detail[:maven_metadatum] = @package.maven_metadatum if @package.maven_metadatum
- package_detail[:nuget_metadatum] = @package.nuget_metadatum if @package.nuget_metadatum
- package_detail[:composer_metadatum] = @package.composer_metadatum if @package.composer_metadatum
- package_detail[:conan_metadatum] = @package.conan_metadatum if @package.conan_metadatum
- package_detail[:dependency_links] = @package.dependency_links.map(&method(:build_dependency_links))
- package_detail[:pipeline] = build_pipeline_info(@package.pipeline) if @package.pipeline
- package_detail[:pipelines] = build_pipeline_infos(@package.pipelines) if @package.pipelines.present?
-
- package_detail
end
- private
-
def build_package_file_view(package_file)
file_view = {
created_at: package_file.created_at,
@@ -91,6 +103,22 @@ def build_dependency_links(link)
target_framework: link.nuget_metadatum&.target_framework
}.compact
end
+
+ def build_debian_distribution(distribution)
+ {
+ id: distribution.id,
+ web_url: distribution.id,
+ codename: distribution.codename,
+ suite: distribution.suite,
+ origin: distribution.origin,
+ label: distribution.label,
+ version: distribution.version,
+ description: distribution.description,
+ valid_time_duration_seconds: distribution.valid_time_duration_seconds,
+ components: distribution.components.map { |component| component.name },
+ architectures: distribution.architectures.map { |architecture| architecture.name }
+ }
+ end
end
end
end
diff --git a/app/services/packages/debian/find_or_create_distribution_key_service.rb b/app/services/packages/debian/find_or_create_distribution_key_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b1f4f4298c16a89efb5ded930f836a28c4312040
--- /dev/null
+++ b/app/services/packages/debian/find_or_create_distribution_key_service.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Packages
+ module Debian
+ class FindOrCreateDistributionKeyService
+ include Gitlab::Utils::StrongMemoize
+
+ def initialize(distribution, params: {})
+ @distribution = distribution
+ @params = params
+ end
+
+ def execute
+ return @distribution.key if @distribution.key
+
+ key_params = GenerateDistributionKeyService.new(params: @params).execute
+
+ @distribution.create_key(key_params)
+ end
+ end
+ end
+end
diff --git a/app/services/packages/debian/generate_distribution_key_service.rb b/app/services/packages/debian/generate_distribution_key_service.rb
index 28c97c7681e410d3fc9fde07520179c76d500782..917965da58ee3ba2bc4b3a984d04ff81b1f0d537 100644
--- a/app/services/packages/debian/generate_distribution_key_service.rb
+++ b/app/services/packages/debian/generate_distribution_key_service.rb
@@ -5,20 +5,42 @@ module Debian
class GenerateDistributionKeyService
include Gitlab::Utils::StrongMemoize
- def initialize(current_user:, params: {})
- @current_user = current_user
+ def initialize(params: {})
@params = params
end
def execute
- raise ArgumentError, 'Please provide a user' unless current_user.is_a?(User)
+ using_pinentry do |ctx|
+ # Generate key
+ ctx.generate_key generate_key_params
+
+ key = ctx.keys.first # rubocop:disable Gitlab/KeysFirstAndValuesFirst
+ fingerprint = key.fingerprint
+
+ # Export private key
+ data = GPGME::Data.new
+ ctx.export_keys fingerprint, data, GPGME::EXPORT_MODE_SECRET
+ data.seek 0
+ private_key = data.read
- generate_key
+ # Export public key
+ data = GPGME::Data.new
+ ctx.export_keys fingerprint, data
+ data.seek 0
+ public_key = data.read
+
+ {
+ private_key: private_key,
+ public_key: public_key,
+ passphrase: passphrase,
+ fingerprint: fingerprint
+ }
+ end
end
private
- attr_reader :current_user, :params
+ attr_reader :params
def passphrase
strong_memoize(:passphrase) do
@@ -72,35 +94,6 @@ def generate_key_params
}.map { |k, v| "#{k}: #{v}\n" }.join +
''
end
-
- def generate_key
- using_pinentry do |ctx|
- # Generate key
- ctx.generate_key generate_key_params
-
- key = ctx.keys.first # rubocop:disable Gitlab/KeysFirstAndValuesFirst
- fingerprint = key.fingerprint
-
- # Export private key
- data = GPGME::Data.new
- ctx.export_keys fingerprint, data, GPGME::EXPORT_MODE_SECRET
- data.seek 0
- private_key = data.read
-
- # Export public key
- data = GPGME::Data.new
- ctx.export_keys fingerprint, data
- data.seek 0
- public_key = data.read
-
- {
- private_key: private_key,
- public_key: public_key,
- passphrase: passphrase,
- fingerprint: fingerprint
- }
- end
- end
end
end
end
diff --git a/app/views/projects/packages/packages/show.html.haml b/app/views/projects/packages/packages/show.html.haml
index aeca3f5b3e3a1f7215d74cee13e3efe08b61edd0..18462e71ac7cb896cf9433e866d387f5e3760df3 100644
--- a/app/views/projects/packages/packages/show.html.haml
+++ b/app/views/projects/packages/packages/show.html.haml
@@ -15,6 +15,8 @@
maven_help_path: help_page_path('user/packages/maven_repository/index'),
conan_path: package_registry_project_url(@project.id, :conan),
conan_help_path: help_page_path('user/packages/conan_repository/index'),
+ debian_path: package_registry_project_url(@project.id, :debian),
+ debian_help_path: help_page_path('user/packages/debian_repository/index'),
nuget_path: nuget_package_registry_url(@project.id),
nuget_help_path: help_page_path('user/packages/nuget_repository/index'),
pypi_path: pypi_registry_url(@project.id),
diff --git a/doc/api/api_resources.md b/doc/api/api_resources.md
index 9200d47effe2ce3dd08210f8ad5648051c07e94c..b1d64d098480f256f1f2f29d9ce315573967adc3 100644
--- a/doc/api/api_resources.md
+++ b/doc/api/api_resources.md
@@ -34,6 +34,7 @@ The following API resources are available in the project context:
| [Dependencies](dependencies.md) **(ULTIMATE)** | `/projects/:id/dependencies` |
| [Deploy keys](deploy_keys.md) | `/projects/:id/deploy_keys` (also available standalone) |
| [Freeze Periods](freeze_periods.md) | `/projects/:id/freeze_periods` |
+| [Debian distributions](debian_project_distributions.md) | `/projects/:id/debian_distributions` (also available for groups) |
| [Deployments](deployments.md) | `/projects/:id/deployments` |
| [Discussions](discussions.md) (threaded comments) | `/projects/:id/issues/.../discussions`, `/projects/:id/snippets/.../discussions`, `/projects/:id/merge_requests/.../discussions`, `/projects/:id/commits/.../discussions` (also available for groups) |
| [Environments](environments.md) | `/projects/:id/environments` |
@@ -99,6 +100,7 @@ The following API resources are available in the group context:
|:-----------------------------------------------------------------|:---------------------------------------------------------------------------------|
| [Access requests](access_requests.md) | `/groups/:id/access_requests/` (also available for projects) |
| [Custom attributes](custom_attributes.md) | `/groups/:id/custom_attributes` (also available for projects and users) |
+| [Debian distributions](debian_group_distributions.md) | `/groups/:id/debian_distributions` (also available for projects) |
| [Discussions](discussions.md) (threaded comments) **(ULTIMATE)** | `/groups/:id/epics/.../discussions` (also available for projects) |
| [Epic issues](epic_issues.md) **(ULTIMATE)** | `/groups/:id/epics/.../issues` |
| [Epic links](epic_links.md) **(ULTIMATE)** | `/groups/:id/epics/.../epics` |
diff --git a/doc/api/debian_group_distributions.md b/doc/api/debian_group_distributions.md
new file mode 100644
index 0000000000000000000000000000000000000000..9756b0afe66a53079cecac51c7e8910711e52eb1
--- /dev/null
+++ b/doc/api/debian_group_distributions.md
@@ -0,0 +1,217 @@
+---
+stage: Package
+group: Package
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+---
+
+# Debian group distributions API
+
+## Enable Debian repository feature
+
+Debian repository support is gated behind a feature flag that is **disabled by default**.
+[GitLab administrators with access to the GitLab Rails console](../administration/feature_flags.md)
+can opt to disable it.
+
+To enable it:
+
+```ruby
+Feature.enable(:debian_packages)
+```
+
+To disable it:
+
+```ruby
+Feature.disable(:debian_packages)
+```
+
+## List all Debian distributions in a group
+
+Lists Debian distributions in the given group.
+
+```plaintext
+GET /groups/:id/debian_distributions
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) |
+| `codename` | string | no | Filter with specific `codename` |
+| `suite` | string | no | Filter with specific `suite` |
+
+```shell
+curl --header "PRIVATE-TOKEN: