Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
6c68583a42
commit
2994a84f01
|
|
@ -4564,7 +4564,6 @@ Layout/LineLength:
|
|||
- 'spec/services/packages/nuget/search_service_spec.rb'
|
||||
- 'spec/services/packages/nuget/update_package_from_metadata_service_spec.rb'
|
||||
- 'spec/services/packages/rubygems/process_gem_service_spec.rb'
|
||||
- 'spec/services/packages/terraform_module/create_package_service_spec.rb'
|
||||
- 'spec/services/personal_access_tokens/create_service_spec.rb'
|
||||
- 'spec/services/post_receive_service_spec.rb'
|
||||
- 'spec/services/projects/apple_target_platform_detector_service_spec.rb'
|
||||
|
|
|
|||
|
|
@ -3690,15 +3690,11 @@ RSpec/FeatureCategory:
|
|||
- 'spec/lib/gitlab/lazy_spec.rb'
|
||||
- 'spec/lib/gitlab/legacy_github_import/branch_formatter_spec.rb'
|
||||
- 'spec/lib/gitlab/legacy_github_import/client_spec.rb'
|
||||
- 'spec/lib/gitlab/legacy_github_import/comment_formatter_spec.rb'
|
||||
- 'spec/lib/gitlab/legacy_github_import/issuable_formatter_spec.rb'
|
||||
- 'spec/lib/gitlab/legacy_github_import/issue_formatter_spec.rb'
|
||||
- 'spec/lib/gitlab/legacy_github_import/label_formatter_spec.rb'
|
||||
- 'spec/lib/gitlab/legacy_github_import/milestone_formatter_spec.rb'
|
||||
- 'spec/lib/gitlab/legacy_github_import/project_creator_spec.rb'
|
||||
- 'spec/lib/gitlab/legacy_github_import/pull_request_formatter_spec.rb'
|
||||
- 'spec/lib/gitlab/legacy_github_import/release_formatter_spec.rb'
|
||||
- 'spec/lib/gitlab/legacy_github_import/user_formatter_spec.rb'
|
||||
- 'spec/lib/gitlab/legacy_github_import/wiki_formatter_spec.rb'
|
||||
- 'spec/lib/gitlab/lets_encrypt/challenge_spec.rb'
|
||||
- 'spec/lib/gitlab/lets_encrypt/client_spec.rb'
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ export default () => {
|
|||
gl.mrWidgetData.can_create_pipeline_in_target_project,
|
||||
),
|
||||
commitPathTemplate: gl.mrWidgetData.commit_path_template,
|
||||
canAdminVulnerability: gl.mrWidgetData.can_admin_vulnerability,
|
||||
dismissalDescriptions,
|
||||
},
|
||||
...MrWidgetOptions,
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ export default {
|
|||
:key="opt.value"
|
||||
:disabled="!!opt.disabled"
|
||||
:selected="value === opt.value"
|
||||
v-bind="opt.props"
|
||||
@click="$emit('input', opt.value)"
|
||||
>
|
||||
<slot name="button-content" v-bind="opt">{{ opt.text }}</slot>
|
||||
|
|
|
|||
|
|
@ -51,6 +51,16 @@ module Mutations
|
|||
required: false,
|
||||
description: copy_field_description(Types::Namespace::PackageSettingsType, :nuget_duplicate_exception_regex)
|
||||
|
||||
argument :terraform_module_duplicates_allowed,
|
||||
GraphQL::Types::Boolean,
|
||||
required: false,
|
||||
description: copy_field_description(Types::Namespace::PackageSettingsType, :terraform_module_duplicates_allowed)
|
||||
|
||||
argument :terraform_module_duplicate_exception_regex,
|
||||
Types::UntrustedRegexp,
|
||||
required: false,
|
||||
description: copy_field_description(Types::Namespace::PackageSettingsType, :terraform_module_duplicate_exception_regex)
|
||||
|
||||
argument :maven_package_requests_forwarding,
|
||||
GraphQL::Types::Boolean,
|
||||
required: false,
|
||||
|
|
|
|||
|
|
@ -35,6 +35,12 @@ module Types
|
|||
field :pypi_package_requests_forwarding, GraphQL::Types::Boolean,
|
||||
null: true,
|
||||
description: 'Indicates whether PyPI package forwarding is allowed for this namespace.'
|
||||
field :terraform_module_duplicate_exception_regex, Types::UntrustedRegexp,
|
||||
null: true,
|
||||
description: 'When terraform_module_duplicates_allowed is false, you can publish duplicate packages with names that match this regex. Otherwise, this setting has no effect.'
|
||||
field :terraform_module_duplicates_allowed, GraphQL::Types::Boolean,
|
||||
null: false,
|
||||
description: 'Indicates whether duplicate Terraform packages are allowed for this namespace.'
|
||||
|
||||
field :lock_maven_package_requests_forwarding, GraphQL::Types::Boolean,
|
||||
null: false,
|
||||
|
|
|
|||
|
|
@ -1,14 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module EnvironmentHelper
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def environment_for_build(project, build)
|
||||
return unless build.environment
|
||||
|
||||
project.environments.find_by(name: build.expanded_environment_name)
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
def deployment_path(deployment)
|
||||
[deployment.project, deployment.deployable]
|
||||
end
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ class Namespace::PackageSetting < ApplicationRecord
|
|||
|
||||
PackageSettingNotImplemented = Class.new(StandardError)
|
||||
|
||||
PACKAGES_WITH_SETTINGS = %w[maven generic nuget].freeze
|
||||
PACKAGES_WITH_SETTINGS = %w[maven generic nuget terraform_module].freeze
|
||||
|
||||
belongs_to :namespace, inverse_of: :package_setting_relation
|
||||
|
||||
|
|
@ -24,6 +24,14 @@ class Namespace::PackageSetting < ApplicationRecord
|
|||
validates :nuget_duplicates_allowed, inclusion: { in: [true, false] }
|
||||
validates :nuget_duplicate_exception_regex, untrusted_regexp: true, length: { maximum: 255 }
|
||||
validates :nuget_symbol_server_enabled, inclusion: { in: [true, false] }
|
||||
validates :terraform_module_duplicates_allowed, inclusion: { in: [true, false] }
|
||||
validates :terraform_module_duplicate_exception_regex, untrusted_regexp: true, length: { maximum: 255 }
|
||||
|
||||
scope :namespace_id_in, ->(namespace_ids) { where(namespace_id: namespace_ids) }
|
||||
scope :with_terraform_module_duplicates_allowed_or_exception_regex, -> do
|
||||
where(terraform_module_duplicates_allowed: true)
|
||||
.or(where.not(terraform_module_duplicate_exception_regex: ''))
|
||||
end
|
||||
|
||||
class << self
|
||||
def duplicates_allowed?(package)
|
||||
|
|
|
|||
|
|
@ -13,12 +13,14 @@ module Organizations
|
|||
|
||||
rule { admin }.policy do
|
||||
enable :admin_organization
|
||||
enable :create_group
|
||||
enable :read_organization
|
||||
enable :read_organization_user
|
||||
end
|
||||
|
||||
rule { organization_user }.policy do
|
||||
enable :admin_organization
|
||||
enable :create_group
|
||||
enable :read_organization
|
||||
enable :read_organization_user
|
||||
end
|
||||
|
|
|
|||
|
|
@ -92,9 +92,32 @@ module Groups
|
|||
end
|
||||
end
|
||||
|
||||
unless organization_setting_valid?
|
||||
# We are unsetting this here to match behavior of invalid parent_id above and protect against possible
|
||||
# committing to the database of a value that isn't allowed.
|
||||
@group.organization = nil
|
||||
message = s_("CreateGroup|You don't have permission to create a group in the provided organization.")
|
||||
@group.errors.add(:organization_id, message)
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def organization_setting_valid?
|
||||
# we check for the params presence explicitly since:
|
||||
# 1. We have a default organization_id at db level set and organization exists and may not have the entry
|
||||
# in organization_users table to allow authorization. This shouldn't be the case longterm as we
|
||||
# plan on populating organization_users correctly.
|
||||
# 2. We shouldn't need to check if this is allowed if the user didn't try to set it themselves. i.e.
|
||||
# provided in the params
|
||||
return true if params[:organization_id].blank?
|
||||
return true if @group.organization.blank?
|
||||
|
||||
can?(current_user, :create_group, @group.organization)
|
||||
end
|
||||
|
||||
def can_use_visibility_level?
|
||||
unless Gitlab::VisibilityLevel.allowed_for?(current_user, visibility_level)
|
||||
deny_visibility_level(@group)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ module Namespaces
|
|||
maven_package_requests_forwarding
|
||||
nuget_duplicates_allowed
|
||||
nuget_duplicate_exception_regex
|
||||
terraform_module_duplicates_allowed
|
||||
terraform_module_duplicate_exception_regex
|
||||
npm_package_requests_forwarding
|
||||
pypi_package_requests_forwarding
|
||||
lock_maven_package_requests_forwarding
|
||||
|
|
|
|||
|
|
@ -6,10 +6,20 @@ module Packages
|
|||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
def execute
|
||||
return error('Version is empty.', 400) if params[:module_version].blank?
|
||||
return error('Access Denied', 403) if current_package_exists_elsewhere?
|
||||
return error('Package version already exists.', 403) if current_package_version_exists?
|
||||
return error('File is too large.', 400) if file_size_exceeded?
|
||||
if params[:module_version].blank?
|
||||
return ServiceResponse.error(message: 'Version is empty.', reason: :bad_request)
|
||||
end
|
||||
|
||||
if duplicates_not_allowed? && current_package_exists_elsewhere?
|
||||
return ServiceResponse.error(
|
||||
message: 'A package with the same name already exists in the namespace',
|
||||
reason: :forbidden
|
||||
)
|
||||
end
|
||||
|
||||
if current_package_version_exists?
|
||||
return ServiceResponse.error(message: 'Package version already exists.', reason: :forbidden)
|
||||
end
|
||||
|
||||
ApplicationRecord.transaction { create_terraform_module_package! }
|
||||
end
|
||||
|
|
@ -24,6 +34,15 @@ module Packages
|
|||
package
|
||||
end
|
||||
|
||||
def duplicates_not_allowed?
|
||||
return true if package_settings_with_duplicates_allowed.blank?
|
||||
|
||||
package_settings_with_duplicates_allowed.none? do |setting|
|
||||
setting.terraform_module_duplicates_allowed ||
|
||||
::Gitlab::UntrustedRegexp.new("\\A#{setting.terraform_module_duplicate_exception_regex}\\z").match?(name)
|
||||
end
|
||||
end
|
||||
|
||||
def current_package_exists_elsewhere?
|
||||
::Packages::Package
|
||||
.for_projects(project.root_namespace.all_projects.id_not_in(project.id))
|
||||
|
|
@ -62,9 +81,13 @@ module Packages
|
|||
}
|
||||
end
|
||||
|
||||
def file_size_exceeded?
|
||||
project.actual_limits.exceeded?(:generic_packages_max_file_size, params[:file].size)
|
||||
def package_settings_with_duplicates_allowed
|
||||
::Namespace::PackageSetting
|
||||
.select(:terraform_module_duplicates_allowed, :terraform_module_duplicate_exception_regex)
|
||||
.namespace_id_in(project.namespace.self_and_ancestor_ids)
|
||||
.with_terraform_module_duplicates_allowed_or_exception_regex
|
||||
end
|
||||
strong_memoize_attr :package_settings_with_duplicates_allowed
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddTerraformModuleDuplicatesAllowedToNamespacePackageSettings < Gitlab::Database::Migration[2.2]
|
||||
milestone '16.8'
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
with_lock_retries do
|
||||
add_column(:namespace_package_settings,
|
||||
:terraform_module_duplicates_allowed,
|
||||
:boolean,
|
||||
null: false,
|
||||
default: false,
|
||||
if_not_exists: true
|
||||
)
|
||||
|
||||
add_column(:namespace_package_settings,
|
||||
:terraform_module_duplicate_exception_regex,
|
||||
:text,
|
||||
null: false,
|
||||
default: '',
|
||||
if_not_exists: true
|
||||
)
|
||||
end
|
||||
|
||||
add_text_limit(:namespace_package_settings, :terraform_module_duplicate_exception_regex, 255)
|
||||
end
|
||||
|
||||
def down
|
||||
with_lock_retries do
|
||||
remove_column(:namespace_package_settings, :terraform_module_duplicates_allowed, if_exists: true)
|
||||
remove_column(:namespace_package_settings, :terraform_module_duplicate_exception_regex, if_exists: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -3,24 +3,10 @@
|
|||
class ChangeICodeReviewCreateMrKeysFromRedisHllToRedis < Gitlab::Database::Migration[2.2]
|
||||
milestone '16.8'
|
||||
|
||||
disable_ddl_transaction!
|
||||
restrict_gitlab_migration gitlab_schema: :gitlab_main
|
||||
|
||||
REDIS_HLL_PREFIX = '{hll_counters}_i_code_review_create_mr'
|
||||
REDIS_PREFIX = '{event_counters}_i_code_review_user_create_mr'
|
||||
|
||||
def up
|
||||
# For each old (redis_hll) counter we find the corresponding target (redis) counter and add
|
||||
# old value to migrate a metric. If the Redis counter does not exist, it will get created.
|
||||
# Since the RedisHLL keys expire after 6 weeks, we will migrate 6 keys at the most.
|
||||
Gitlab::Redis::SharedState.with do |redis|
|
||||
redis.scan_each(match: "#{REDIS_HLL_PREFIX}-*") do |key|
|
||||
redis_key = key.sub(REDIS_HLL_PREFIX, REDIS_PREFIX)
|
||||
redis_hll_value = redis.pfcount(key)
|
||||
|
||||
redis.incrby(redis_key, redis_hll_value)
|
||||
end
|
||||
end
|
||||
# no-op
|
||||
#
|
||||
# Removed due to https://gitlab.com/gitlab-com/gl-infra/production/-/issues/17321
|
||||
end
|
||||
|
||||
def down
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
a18e718e99c23ae6db929929a905af0db72e3a3734d3c33e12ec2cdb44467f6d
|
||||
|
|
@ -19505,9 +19505,12 @@ CREATE TABLE namespace_package_settings (
|
|||
nuget_duplicates_allowed boolean DEFAULT true NOT NULL,
|
||||
nuget_duplicate_exception_regex text DEFAULT ''::text NOT NULL,
|
||||
nuget_symbol_server_enabled boolean DEFAULT false NOT NULL,
|
||||
terraform_module_duplicates_allowed boolean DEFAULT false NOT NULL,
|
||||
terraform_module_duplicate_exception_regex text DEFAULT ''::text NOT NULL,
|
||||
CONSTRAINT check_31340211b1 CHECK ((char_length(generic_duplicate_exception_regex) <= 255)),
|
||||
CONSTRAINT check_d63274b2b6 CHECK ((char_length(maven_duplicate_exception_regex) <= 255)),
|
||||
CONSTRAINT check_eedcf85c48 CHECK ((char_length(nuget_duplicate_exception_regex) <= 255))
|
||||
CONSTRAINT check_eedcf85c48 CHECK ((char_length(nuget_duplicate_exception_regex) <= 255)),
|
||||
CONSTRAINT check_f10503f1ad CHECK ((char_length(terraform_module_duplicate_exception_regex) <= 255))
|
||||
);
|
||||
|
||||
CREATE TABLE namespace_root_storage_statistics (
|
||||
|
|
|
|||
|
|
@ -7788,6 +7788,8 @@ Input type: `UpdateNamespacePackageSettingsInput`
|
|||
| <a id="mutationupdatenamespacepackagesettingsnugetduplicatesallowed"></a>`nugetDuplicatesAllowed` | [`Boolean`](#boolean) | Indicates whether duplicate NuGet packages are allowed for this namespace. |
|
||||
| <a id="mutationupdatenamespacepackagesettingsnugetsymbolserverenabled"></a>`nugetSymbolServerEnabled` | [`Boolean`](#boolean) | Indicates wheather the NuGet symbol server is enabled for this namespace. |
|
||||
| <a id="mutationupdatenamespacepackagesettingspypipackagerequestsforwarding"></a>`pypiPackageRequestsForwarding` | [`Boolean`](#boolean) | Indicates whether PyPI package forwarding is allowed for this namespace. |
|
||||
| <a id="mutationupdatenamespacepackagesettingsterraformmoduleduplicateexceptionregex"></a>`terraformModuleDuplicateExceptionRegex` | [`UntrustedRegexp`](#untrustedregexp) | When terraform_module_duplicates_allowed is false, you can publish duplicate packages with names that match this regex. Otherwise, this setting has no effect. |
|
||||
| <a id="mutationupdatenamespacepackagesettingsterraformmoduleduplicatesallowed"></a>`terraformModuleDuplicatesAllowed` | [`Boolean`](#boolean) | Indicates whether duplicate Terraform packages are allowed for this namespace. |
|
||||
|
||||
#### Fields
|
||||
|
||||
|
|
@ -23730,6 +23732,8 @@ Namespace-level Package Registry settings.
|
|||
| <a id="packagesettingsnugetsymbolserverenabled"></a>`nugetSymbolServerEnabled` | [`Boolean!`](#boolean) | Indicates wheather the NuGet symbol server is enabled for this namespace. |
|
||||
| <a id="packagesettingspypipackagerequestsforwarding"></a>`pypiPackageRequestsForwarding` | [`Boolean`](#boolean) | Indicates whether PyPI package forwarding is allowed for this namespace. |
|
||||
| <a id="packagesettingspypipackagerequestsforwardinglocked"></a>`pypiPackageRequestsForwardingLocked` | [`Boolean!`](#boolean) | Indicates whether PyPI package forwarding settings are locked by a parent namespace. |
|
||||
| <a id="packagesettingsterraformmoduleduplicateexceptionregex"></a>`terraformModuleDuplicateExceptionRegex` | [`UntrustedRegexp`](#untrustedregexp) | When terraform_module_duplicates_allowed is false, you can publish duplicate packages with names that match this regex. Otherwise, this setting has no effect. |
|
||||
| <a id="packagesettingsterraformmoduleduplicatesallowed"></a>`terraformModuleDuplicatesAllowed` | [`Boolean!`](#boolean) | Indicates whether duplicate Terraform packages are allowed for this namespace. |
|
||||
|
||||
### `PackageTag`
|
||||
|
||||
|
|
|
|||
|
|
@ -817,7 +817,7 @@ POST /groups
|
|||
Parameters:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| ------------------------------------------------------- | ------- | -------- | ----------- |
|
||||
| ------------------------------------------------------- | ------- | -------- |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `name` | string | yes | The name of the group. |
|
||||
| `path` | string | yes | The path of the group. |
|
||||
| `auto_devops_enabled` | boolean | no | Default to Auto DevOps pipeline for all projects within this group. |
|
||||
|
|
@ -829,6 +829,7 @@ Parameters:
|
|||
| `emails_enabled` | boolean | no | Enable email notifications. |
|
||||
| `lfs_enabled` | boolean | no | Enable/disable Large File Storage (LFS) for the projects in this group. |
|
||||
| `mentions_disabled` | boolean | no | Disable the capability of a group from getting mentioned. |
|
||||
| `organization_id` | integer | no | The organization ID for the group. |
|
||||
| `parent_id` | integer | no | The parent group ID for creating nested group. |
|
||||
| `project_creation_level` | string | no | Determine if developers can create projects in the group. Can be `noone` (No one), `maintainer` (users with the Maintainer role), or `developer` (users with the Developer or Maintainer role). |
|
||||
| `request_access_enabled` | boolean | no | Allow users to request member access. |
|
||||
|
|
|
|||
|
|
@ -70,6 +70,8 @@ Setting | Table | Description
|
|||
`nuget_duplicates_allowed` | `namespace_package_settings` | Allow or prevent duplicate NuGet packages.
|
||||
`nuget_duplicate_exception_regex` | `namespace_package_settings` | Regex defining NuGet packages that are allowed to be duplicate when duplicates are not allowed.
|
||||
`nuget_symbol_server_enabled` | `namespace_package_settings` | Enable or disable the NuGet symbol server.
|
||||
`terraform_module_duplicates_allowed` | `namespace_package_settings` | Allow or prevent duplicate Terraform module packages.
|
||||
`terraform_module_duplicate_exception_regex` | `namespace_package_settings` | Regex defining Terraform module packages that are allowed to be duplicate when duplicates are not allowed.
|
||||
Dependency Proxy Cleanup Policies - `ttl` | `dependency_proxy_image_ttl_group_policies` | Number of days to retain an unused Dependency Proxy file before it is removed.
|
||||
Dependency Proxy - `enabled` | `dependency_proxy_image_ttl_group_policies` | Enable or disable the Dependency Proxy cleanup policy.
|
||||
|
||||
|
|
|
|||
|
|
@ -48,10 +48,10 @@ You can publish Terraform modules by using the [Terraform Module Registry API](.
|
|||
|
||||
Prerequisites:
|
||||
|
||||
- The package name and version [must be unique in the top-level namespace](#how-module-resolution-works).
|
||||
- Unless [duplicates are allowed](#allow-duplicate-terraform-modules), the package name and version [must be unique in the top-level namespace](#how-module-resolution-works).
|
||||
- Your project and group names must not include a dot (`.`). For example, `source = "gitlab.example.com/my.group/project.name"`.
|
||||
- You must [authenticate with the API](../../../api/rest/index.md#authentication). If authenticating with a deploy token, it must be configured with the `write_package_registry` scope.
|
||||
- The name of a module [must be unique in the scope of its group](#how-module-resolution-works), otherwise an
|
||||
- Unless [duplicates are allowed](#allow-duplicate-terraform-modules), the name of a module [must be unique in the scope of its group](#how-module-resolution-works), otherwise an
|
||||
[error occurs](#troubleshooting).
|
||||
|
||||
```plaintext
|
||||
|
|
@ -157,6 +157,22 @@ upload:
|
|||
To trigger this upload job, add a Git tag to your commit. Ensure the tag follows the [Semantic versioning specification](https://semver.org/) that Terraform requires. The `rules:if: $CI_COMMIT_TAG` ensures that only tagged commits to your repository trigger the module upload job.
|
||||
For other ways to control jobs in your CI/CD pipeline, refer to the [`.gitlab-ci.yml`](../../../ci/yaml/index.md) keyword reference.
|
||||
|
||||
### Allow duplicate Terraform modules
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/368040) in GitLab 16.8.
|
||||
|
||||
By default, the Terraform Module Registry enforces uniqueness for module names in the same namespace. To allow publishing duplicate module names:
|
||||
|
||||
- Enable `terraform_module_duplicates_allowed` for the namespace with the [GraphQl API](../../../api/graphql/reference/index.md#packagesettings).
|
||||
|
||||
To allow duplicates with specific names:
|
||||
|
||||
1. Ensure `terraform_module_duplicates_allowed` is disabled.
|
||||
1. Use `terraform_module_duplicate_exception_regex` to define a regex pattern for the module names you want to allow duplicates for.
|
||||
|
||||
The top-level namespace setting takes precedence over the child namespace settings.
|
||||
For example, if you enable `terraform_module_duplicates_allowed` for a group, and disable it for a subgroup, duplicates are allowed for all projects in the group and its subgroups.
|
||||
|
||||
## Reference a Terraform module
|
||||
|
||||
Prerequisites:
|
||||
|
|
@ -209,6 +225,8 @@ module "<module>" {
|
|||
|
||||
If you need to reference the latest version of a module, you can omit the `<module-version>` from the source URL. To prevent future issues, you should reference a specific version if possible.
|
||||
|
||||
If there are [duplicate module names](#allow-duplicate-terraform-modules) in the same namespace, referencing the module from the namespace level installs the recently published module. To reference a specific version of a duplicate module, use the [project-level](#from-a-project) source type.
|
||||
|
||||
## Download a Terraform module
|
||||
|
||||
To download a Terraform module:
|
||||
|
|
@ -275,4 +293,4 @@ For examples of the Terraform Module Registry, check the projects below:
|
|||
|
||||
## Troubleshooting
|
||||
|
||||
- Publishing a module with a duplicate name results in a `{"message":"Access Denied"}` error. There's an ongoing discussion about allowing duplicate module names [in this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/368040).
|
||||
- Publishing a module with a duplicate name results in a `{"message":"A package with the same name already exists in the namespace"}` error.
|
||||
|
|
|
|||
|
|
@ -395,6 +395,6 @@ third-party Git clients.
|
|||
|
||||
### Branch names are case-sensitive
|
||||
|
||||
Branch names in `git` are case-sensitive. When configuring your protected branch
|
||||
or [target branch rule](repository/branches/index.md#configure-rules-for-target-branches),
|
||||
Branch names in `git` are case-sensitive. When configuring your protected branch,
|
||||
or your [target branch workflow](repository/branches/index.md#configure-workflows-for-target-branches),
|
||||
`dev` is not the same `DEV` or `Dev`.
|
||||
|
|
|
|||
|
|
@ -287,58 +287,58 @@ To do this:
|
|||
1. Select **Delete merged branches**.
|
||||
1. In the dialog, enter the word `delete` to confirm, then select **Delete merged branches**.
|
||||
|
||||
## Configure rules for target branches **(PREMIUM ALL)**
|
||||
## Configure workflows for target branches **(PREMIUM ALL)**
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/127115) in GitLab 16.4 [with a flag](../../../../administration/feature_flags.md) named `target_branch_rules_flag`. Enabled by default.
|
||||
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/136431) in GitLab 16.7.
|
||||
|
||||
Some projects use multiple long-term branches for development, like `develop` and `qa`.
|
||||
In these projects, you might want to keep `main` as the default branch, but expect
|
||||
merge requests to target `develop` or `qa` instead. Target branch rules help ensure
|
||||
merge requests to target `develop` or `qa` instead. Target branch workflows help ensure
|
||||
merge requests target the appropriate development branch for your project.
|
||||
|
||||
When you create a merge request, the rule checks the name of the branch. If the
|
||||
branch name matches the rule, the merge request targets the branch you specify
|
||||
in the rule. If the branch name does not match, the merge request targets the
|
||||
When you create a merge request, the workflow checks the name of the branch. If the
|
||||
branch name matches the workflow, the merge request targets the branch you specify. If the branch name does not match, the merge request targets the
|
||||
default branch of the project.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have at least the Maintainer role.
|
||||
|
||||
To create a target branch rule:
|
||||
To create a target branch workflow:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Select **Settings > Merge requests**.
|
||||
1. Select **Add target branch rule**.
|
||||
1. For **Rule name**, provide a string or wild card to compare against branch names.
|
||||
1. Select the **Target branch** to use when the branch name matches the **Rule name**.
|
||||
1. Scroll down to **Merge request branch workflow**
|
||||
1. Select **Add branch target**.
|
||||
1. For **Branch name pattern**, provide a string or wild card to compare against branch names.
|
||||
1. Select the **Target branch** to use when the branch name matches the **Branch name pattern**.
|
||||
1. Select **Save**.
|
||||
|
||||
### Example
|
||||
|
||||
You could configure your project to have the following target branch rules:
|
||||
You could configure your project to have the following target branch workflows:
|
||||
|
||||
| Rule name | Target branch |
|
||||
| Branch name pattern | Target branch |
|
||||
|-------------|---------------|
|
||||
| `feature/*` | `develop` |
|
||||
| `bug/*` | `develop` |
|
||||
| `release/*` | `main` |
|
||||
|
||||
These rules simplify the process of creating merge requests for a project that:
|
||||
These target branches simplify the process of creating merge requests for a project that:
|
||||
|
||||
- Uses `main` to represent the deployed state of your application.
|
||||
- Tracks current, unreleased development work in another long-running branch, like `develop`.
|
||||
|
||||
If your workflow initially places new features in `develop` instead of `main`, these rules
|
||||
If your workflow initially places new features in `develop` instead of `main`, these target branches
|
||||
ensure all branches matching either `feature/*` or `bug/*` do not target `main` by mistake.
|
||||
|
||||
When you're ready to release to `main`, create a branch named `release/*`, and the rules
|
||||
When you're ready to release to `main`, create a branch named `release/*`, and
|
||||
ensure this branch targets `main`.
|
||||
|
||||
## Delete a target branch rule
|
||||
## Delete a target branch workflow
|
||||
|
||||
When you remove a target branch rule, existing merge requests remain unchanged.
|
||||
When you remove a target branch workflow, existing merge requests remain unchanged.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
|
|
@ -348,7 +348,7 @@ To do this:
|
|||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Select **Settings > Merge requests**.
|
||||
1. Select **Delete** on the rule you want to delete.
|
||||
1. Select **Delete** on the branch target you want to delete.
|
||||
|
||||
## Related topics
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ module API
|
|||
expose :full_name, :full_path
|
||||
expose :created_at
|
||||
expose :parent_id
|
||||
expose :organization_id
|
||||
expose :shared_runners_setting
|
||||
|
||||
expose :custom_attributes, using: 'API::Entities::CustomAttribute', if: :with_custom_attributes
|
||||
|
|
|
|||
|
|
@ -213,11 +213,15 @@ module API
|
|||
requires :name, type: String, desc: 'The name of the group'
|
||||
requires :path, type: String, desc: 'The path of the group'
|
||||
optional :parent_id, type: Integer, desc: 'The parent group id for creating nested group'
|
||||
optional :organization_id, type: Integer, desc: 'The organization id for the group'
|
||||
|
||||
use :optional_params
|
||||
end
|
||||
post feature_category: :groups_and_projects, urgency: :low do
|
||||
parent_group = find_group!(params[:parent_id]) if params[:parent_id].present?
|
||||
organization = find_organization!(params[:organization_id]) if params[:organization_id].present?
|
||||
authorize! :create_group, organization if organization
|
||||
|
||||
parent_group = find_group!(params[:parent_id], organization: organization) if params[:parent_id].present?
|
||||
if parent_group
|
||||
authorize! :create_subgroup, parent_group
|
||||
else
|
||||
|
|
|
|||
|
|
@ -211,18 +211,25 @@ module API
|
|||
not_found!('Pipeline')
|
||||
end
|
||||
|
||||
def find_organization!(id)
|
||||
organization = Organizations::Organization.find_by_id(id)
|
||||
check_organization_access(organization)
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def find_group(id)
|
||||
def find_group(id, organization: nil)
|
||||
collection = organization.present? ? Group.in_organization(organization) : Group.all
|
||||
|
||||
if id.to_s =~ INTEGER_ID_REGEX
|
||||
Group.find_by(id: id)
|
||||
collection.find_by(id: id)
|
||||
else
|
||||
Group.find_by_full_path(id)
|
||||
collection.find_by_full_path(id)
|
||||
end
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
def find_group!(id)
|
||||
group = find_group(id)
|
||||
def find_group!(id, organization: nil)
|
||||
group = find_group(id, organization: organization)
|
||||
check_group_access(group)
|
||||
end
|
||||
|
||||
|
|
@ -835,6 +842,12 @@ module API
|
|||
@sudo_identifier ||= params[SUDO_PARAM] || env[SUDO_HEADER]
|
||||
end
|
||||
|
||||
def check_organization_access(organization)
|
||||
return organization if can?(current_user, :read_organization, organization)
|
||||
|
||||
not_found!('Organization')
|
||||
end
|
||||
|
||||
def secret_token
|
||||
Gitlab::Shell.secret_token
|
||||
end
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ module API
|
|||
.new(authorized_user_project, current_user, create_package_file_params)
|
||||
.execute
|
||||
|
||||
render_api_error!(result[:message], result[:http_status]) if result[:status] == :error
|
||||
render_api_error!(result.message, result.reason) if result.error?
|
||||
|
||||
track_package_event('push_package', :terraform_module, project: authorized_user_project,
|
||||
namespace: authorized_user_project.namespace)
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ module Gitlab
|
|||
def gitlab_id
|
||||
return @gitlab_id if defined?(@gitlab_id)
|
||||
|
||||
@gitlab_id = find_by_external_uid || find_by_email
|
||||
@gitlab_id = find_by_email
|
||||
end
|
||||
|
||||
private
|
||||
|
|
@ -45,14 +45,6 @@ module Gitlab
|
|||
User.find_by_any_email(email)
|
||||
.try(:id)
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def find_by_external_uid
|
||||
return unless id
|
||||
|
||||
User.by_provider_and_extern_uid(:github, id).select(:id).first&.id
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -14578,6 +14578,9 @@ msgstr ""
|
|||
msgid "CreateGitTag|Set tag message"
|
||||
msgstr ""
|
||||
|
||||
msgid "CreateGroup|You don't have permission to create a group in the provided organization."
|
||||
msgstr ""
|
||||
|
||||
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ FactoryBot.define do
|
|||
|
||||
nuget_symbol_server_enabled { false }
|
||||
|
||||
terraform_module_duplicates_allowed { false }
|
||||
terraform_module_duplicate_exception_regex { 'foo' }
|
||||
|
||||
trait :group do
|
||||
namespace { association(:group) }
|
||||
end
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@ describe('~/vue_shared/components/segmented_control_button_group.vue', () => {
|
|||
[[{ value: '1' }]],
|
||||
[[{ value: 1, disabled: true }]],
|
||||
[[{ value: true, disabled: false }]],
|
||||
[[{ value: true, props: { 'data-testid': 'test' } }]],
|
||||
])('with options=%j, passes validation', (options) => {
|
||||
createComponent({ options });
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,9 @@ RSpec.describe Mutations::Namespace::PackageSettings::Update, feature_category:
|
|||
lock_npm_package_requests_forwarding: false,
|
||||
pypi_package_requests_forwarding: nil,
|
||||
lock_pypi_package_requests_forwarding: false,
|
||||
nuget_symbol_server_enabled: false
|
||||
nuget_symbol_server_enabled: false,
|
||||
terraform_module_duplicates_allowed: false,
|
||||
terraform_module_duplicate_exception_regex: 'foo'
|
||||
}, to: {
|
||||
maven_duplicates_allowed: false,
|
||||
maven_duplicate_exception_regex: 'RELEASE',
|
||||
|
|
@ -53,7 +55,9 @@ RSpec.describe Mutations::Namespace::PackageSettings::Update, feature_category:
|
|||
lock_npm_package_requests_forwarding: true,
|
||||
pypi_package_requests_forwarding: true,
|
||||
lock_pypi_package_requests_forwarding: true,
|
||||
nuget_symbol_server_enabled: true
|
||||
nuget_symbol_server_enabled: true,
|
||||
terraform_module_duplicates_allowed: true,
|
||||
terraform_module_duplicate_exception_regex: 'bar'
|
||||
}
|
||||
|
||||
it_behaves_like 'returning a success'
|
||||
|
|
@ -109,7 +113,9 @@ RSpec.describe Mutations::Namespace::PackageSettings::Update, feature_category:
|
|||
lock_npm_package_requests_forwarding: true,
|
||||
pypi_package_requests_forwarding: true,
|
||||
lock_pypi_package_requests_forwarding: true,
|
||||
nuget_symbol_server_enabled: true
|
||||
nuget_symbol_server_enabled: true,
|
||||
terraform_module_duplicates_allowed: true,
|
||||
terraform_module_duplicate_exception_regex: 'bar'
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ RSpec.describe GitlabSchema.types['PackageSettings'], feature_category: :package
|
|||
npm_package_requests_forwarding_locked
|
||||
pypi_package_requests_forwarding_locked
|
||||
nuget_symbol_server_enabled
|
||||
terraform_module_duplicates_allowed
|
||||
terraform_module_duplicate_exception_regex
|
||||
]
|
||||
|
||||
expect(described_class).to include_graphql_fields(*expected_fields)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe API::Entities::Group, feature_category: :groups_and_projects do
|
||||
let_it_be(:group) do
|
||||
base_group = create(:group) { |g| create(:project_statistics, namespace_id: g.id) }
|
||||
Group.with_statistics.find(base_group.id)
|
||||
end
|
||||
|
||||
subject(:json) { described_class.new(group, { with_custom_attributes: true, statistics: true }).as_json }
|
||||
|
||||
it 'returns expected data' do
|
||||
expect(json.keys).to(
|
||||
include(
|
||||
:organization_id, :path, :description, :visibility, :share_with_group_lock, :require_two_factor_authentication,
|
||||
:two_factor_grace_period, :project_creation_level, :auto_devops_enabled,
|
||||
:subgroup_creation_level, :emails_disabled, :emails_enabled, :lfs_enabled, :default_branch_protection,
|
||||
:default_branch_protection_defaults, :avatar_url, :request_access_enabled, :full_name, :full_path, :created_at,
|
||||
:parent_id, :organization_id, :shared_runners_setting, :custom_attributes, :statistics
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
@ -406,6 +406,37 @@ RSpec.describe API::Helpers, feature_category: :shared do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#find_organization!' do
|
||||
let_it_be(:organization) { create(:organization) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
allow(helper).to receive(:current_user).and_return(user)
|
||||
allow(helper).to receive(:initial_current_user).and_return(user)
|
||||
end
|
||||
|
||||
context 'when user is authenticated' do
|
||||
it 'returns requested organization' do
|
||||
expect(helper.find_organization!(organization.id)).to eq(organization)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user is not authenticated' do
|
||||
let(:user) { nil }
|
||||
|
||||
it 'returns requested organization' do
|
||||
expect(helper.find_organization!(organization.id)).to eq(organization)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when organization does not exist' do
|
||||
it 'returns nil' do
|
||||
expect(helper).to receive(:render_api_error!).with('404 Organization Not Found', 404)
|
||||
expect(helper.find_organization!(non_existing_record_id)).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#find_group!' do
|
||||
let_it_be(:group) { create(:group, :public) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
|
@ -457,7 +488,7 @@ RSpec.describe API::Helpers, feature_category: :shared do
|
|||
end
|
||||
end
|
||||
|
||||
context 'support for IDs and paths as arguments' do
|
||||
context 'with support for IDs and paths as arguments' do
|
||||
let_it_be(:group) { create(:group) }
|
||||
|
||||
let(:user) { group.first_owner }
|
||||
|
|
@ -505,6 +536,34 @@ RSpec.describe API::Helpers, feature_category: :shared do
|
|||
end
|
||||
end
|
||||
|
||||
context 'with support for organization as an argument' do
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:organization) { create(:organization) }
|
||||
|
||||
before do
|
||||
allow(helper).to receive(:current_user).and_return(group.first_owner)
|
||||
allow(helper).to receive(:job_token_authentication?).and_return(false)
|
||||
allow(helper).to receive(:authenticate_non_public?).and_return(false)
|
||||
end
|
||||
|
||||
subject { helper.find_group!(group.id, organization: organization) }
|
||||
|
||||
context 'when group exists in the organization' do
|
||||
before do
|
||||
group.update!(organization: organization)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(group) }
|
||||
end
|
||||
|
||||
context 'when group does not exist in the organization' do
|
||||
it 'returns nil' do
|
||||
expect(helper).to receive(:render_api_error!).with('404 Group Not Found', 404)
|
||||
is_expected.to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#find_group_by_full_path!' do
|
||||
let_it_be(:group) { create(:group, :public) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::LegacyGithubImport::CommentFormatter do
|
||||
RSpec.describe Gitlab::LegacyGithubImport::CommentFormatter, feature_category: :importers do
|
||||
let_it_be(:project) { create(:project) }
|
||||
let(:client) { double }
|
||||
let(:octocat) { { id: 123456, login: 'octocat', email: 'octocat@example.com' } }
|
||||
|
|
@ -76,12 +76,6 @@ RSpec.describe Gitlab::LegacyGithubImport::CommentFormatter do
|
|||
context 'when author is a GitLab user' do
|
||||
let(:raw) { base.merge(user: octocat) }
|
||||
|
||||
it 'returns GitLab user id associated with GitHub id as author_id' do
|
||||
gl_user = create(:omniauth_user, extern_uid: octocat[:id], provider: 'github')
|
||||
|
||||
expect(comment.attributes.fetch(:author_id)).to eq gl_user.id
|
||||
end
|
||||
|
||||
it 'returns GitLab user id associated with GitHub email as author_id' do
|
||||
gl_user = create(:user, email: octocat[:email])
|
||||
|
||||
|
|
@ -89,7 +83,7 @@ RSpec.describe Gitlab::LegacyGithubImport::CommentFormatter do
|
|||
end
|
||||
|
||||
it 'returns note without created at tag line' do
|
||||
create(:omniauth_user, extern_uid: octocat[:id], provider: 'github')
|
||||
create(:user, email: octocat[:email])
|
||||
|
||||
expect(comment.attributes.fetch(:note)).to eq("I'm having a problem with this.")
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::LegacyGithubImport::IssueFormatter do
|
||||
RSpec.describe Gitlab::LegacyGithubImport::IssueFormatter, feature_category: :importers do
|
||||
let_it_be(:project) { create(:project, namespace: create(:namespace, path: 'octocat')) }
|
||||
let(:client) { double }
|
||||
let(:octocat) { { id: 123456, login: 'octocat', email: 'octocat@example.com' } }
|
||||
|
|
@ -82,12 +82,6 @@ RSpec.describe Gitlab::LegacyGithubImport::IssueFormatter do
|
|||
expect(issue.attributes.fetch(:assignee_ids)).to be_empty
|
||||
end
|
||||
|
||||
it 'returns GitLab user id associated with GitHub id as assignee_id' do
|
||||
gl_user = create(:omniauth_user, extern_uid: octocat[:id], provider: 'github')
|
||||
|
||||
expect(issue.attributes.fetch(:assignee_ids)).to eq [gl_user.id]
|
||||
end
|
||||
|
||||
it 'returns GitLab user id associated with GitHub email as assignee_id' do
|
||||
gl_user = create(:user, email: octocat[:email])
|
||||
|
||||
|
|
@ -117,12 +111,6 @@ RSpec.describe Gitlab::LegacyGithubImport::IssueFormatter do
|
|||
expect(issue.attributes.fetch(:author_id)).to eq project.creator_id
|
||||
end
|
||||
|
||||
it 'returns GitLab user id associated with GitHub id as author_id' do
|
||||
gl_user = create(:omniauth_user, extern_uid: octocat[:id], provider: 'github')
|
||||
|
||||
expect(issue.attributes.fetch(:author_id)).to eq gl_user.id
|
||||
end
|
||||
|
||||
it 'returns GitLab user id associated with GitHub email as author_id' do
|
||||
gl_user = create(:user, email: octocat[:email])
|
||||
|
||||
|
|
@ -130,7 +118,7 @@ RSpec.describe Gitlab::LegacyGithubImport::IssueFormatter do
|
|||
end
|
||||
|
||||
it 'returns description without created at tag line' do
|
||||
create(:omniauth_user, extern_uid: octocat[:id], provider: 'github')
|
||||
create(:user, email: octocat[:email])
|
||||
|
||||
expect(issue.attributes.fetch(:description)).to eq("I'm having a problem with this.")
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::LegacyGithubImport::PullRequestFormatter do
|
||||
RSpec.describe Gitlab::LegacyGithubImport::PullRequestFormatter, feature_category: :importers do
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
let(:client) { double }
|
||||
let(:source_sha) { create(:commit, project: project).id }
|
||||
|
|
@ -136,12 +136,6 @@ RSpec.describe Gitlab::LegacyGithubImport::PullRequestFormatter do
|
|||
expect(pull_request.attributes.fetch(:assignee_id)).to be_nil
|
||||
end
|
||||
|
||||
it 'returns GitLab user id associated with GitHub id as assignee_id' do
|
||||
gl_user = create(:omniauth_user, extern_uid: octocat[:id], provider: 'github')
|
||||
|
||||
expect(pull_request.attributes.fetch(:assignee_id)).to eq gl_user.id
|
||||
end
|
||||
|
||||
it 'returns GitLab user id associated with GitHub email as assignee_id' do
|
||||
gl_user = create(:user, email: octocat[:email])
|
||||
|
||||
|
|
@ -156,12 +150,6 @@ RSpec.describe Gitlab::LegacyGithubImport::PullRequestFormatter do
|
|||
expect(pull_request.attributes.fetch(:author_id)).to eq project.creator_id
|
||||
end
|
||||
|
||||
it 'returns GitLab user id associated with GitHub id as author_id' do
|
||||
gl_user = create(:omniauth_user, extern_uid: octocat[:id], provider: 'github')
|
||||
|
||||
expect(pull_request.attributes.fetch(:author_id)).to eq gl_user.id
|
||||
end
|
||||
|
||||
it 'returns GitLab user id associated with GitHub email as author_id' do
|
||||
gl_user = create(:user, email: octocat[:email])
|
||||
|
||||
|
|
@ -169,7 +157,7 @@ RSpec.describe Gitlab::LegacyGithubImport::PullRequestFormatter do
|
|||
end
|
||||
|
||||
it 'returns description without created at tag line' do
|
||||
create(:omniauth_user, extern_uid: octocat[:id], provider: 'github')
|
||||
create(:user, email: octocat[:email])
|
||||
|
||||
expect(pull_request.attributes.fetch(:description)).to eq('Please pull these awesome changes')
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::LegacyGithubImport::UserFormatter do
|
||||
RSpec.describe Gitlab::LegacyGithubImport::UserFormatter, feature_category: :importers do
|
||||
let(:client) { double }
|
||||
let(:octocat) { { id: 123456, login: 'octocat', email: 'octocat@example.com' } }
|
||||
let(:gitea_ghost) { { id: -1, login: 'Ghost', email: '' } }
|
||||
|
|
@ -15,12 +15,6 @@ RSpec.describe Gitlab::LegacyGithubImport::UserFormatter do
|
|||
end
|
||||
|
||||
context 'when GitHub user is a GitLab user' do
|
||||
it 'return GitLab user id when user associated their account with GitHub' do
|
||||
gl_user = create(:omniauth_user, extern_uid: octocat[:id], provider: 'github')
|
||||
|
||||
expect(user.gitlab_id).to eq gl_user.id
|
||||
end
|
||||
|
||||
it 'returns GitLab user id when user confirmed primary email matches GitHub email' do
|
||||
gl_user = create(:user, email: octocat[:email])
|
||||
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require_migration!
|
||||
|
||||
RSpec.describe ChangeICodeReviewCreateMrKeysFromRedisHllToRedis, :migration, :clean_gitlab_redis_cache, feature_category: :service_ping do
|
||||
def set_redis_hll(key, value)
|
||||
Gitlab::Redis::HLL.add(key: key, value: value, expiry: 6.weeks)
|
||||
end
|
||||
|
||||
def get_int_from_redis(key)
|
||||
Gitlab::Redis::SharedState.with { |redis| redis.get(key)&.to_i }
|
||||
end
|
||||
|
||||
describe "#up" do
|
||||
before do
|
||||
set_redis_hll('{hll_counters}_i_code_review_create_mr-2023-16', 1)
|
||||
set_redis_hll('{hll_counters}_i_code_review_create_mr-2023-16', 2)
|
||||
set_redis_hll('{hll_counters}_i_code_review_create_mr-2023-47', 3)
|
||||
set_redis_hll('{hll_counters}_i_code_review_create_mr-2023-48', 1)
|
||||
set_redis_hll('{hll_counters}_i_code_review_create_mr-2023-49', 2)
|
||||
set_redis_hll('{hll_counters}_i_code_review_create_mr-2023-49', 4)
|
||||
set_redis_hll('{hll_counters}_some_other_event-2023-49', 7)
|
||||
end
|
||||
|
||||
it 'migrates all RedisHLL keys for i_code_review_create_mr', :aggregate_failures do
|
||||
migrate!
|
||||
|
||||
expect(get_int_from_redis('{event_counters}_i_code_review_user_create_mr-2023-16')).to eq(2)
|
||||
expect(get_int_from_redis('{event_counters}_i_code_review_user_create_mr-2023-47')).to eq(1)
|
||||
expect(get_int_from_redis('{event_counters}_i_code_review_user_create_mr-2023-48')).to eq(1)
|
||||
expect(get_int_from_redis('{event_counters}_i_code_review_user_create_mr-2023-49')).to eq(2)
|
||||
end
|
||||
|
||||
it 'does not not migrate other RedisHLL keys' do
|
||||
migrate!
|
||||
|
||||
expect(get_int_from_redis('{event_counters}_some_other_event-2023-16')).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -12,13 +12,21 @@ RSpec.describe Namespace::PackageSetting, feature_category: :package_registry do
|
|||
|
||||
describe '#maven_duplicates_allowed' do
|
||||
it { is_expected.to validate_inclusion_of(:maven_duplicates_allowed).in_array([true, false]) }
|
||||
it { is_expected.to validate_inclusion_of(:generic_duplicates_allowed).in_array([true, false]) }
|
||||
it { is_expected.to validate_inclusion_of(:nuget_duplicates_allowed).in_array([true, false]) }
|
||||
it { is_expected.to validate_length_of(:maven_duplicate_exception_regex).is_at_most(255) }
|
||||
end
|
||||
|
||||
it { is_expected.to allow_value(true, false).for(:nuget_symbol_server_enabled) }
|
||||
it { is_expected.not_to allow_value(nil).for(:nuget_symbol_server_enabled) }
|
||||
|
||||
it { is_expected.to validate_inclusion_of(:generic_duplicates_allowed).in_array([true, false]) }
|
||||
it { is_expected.to validate_length_of(:generic_duplicate_exception_regex).is_at_most(255) }
|
||||
it { is_expected.to validate_inclusion_of(:nuget_duplicates_allowed).in_array([true, false]) }
|
||||
it { is_expected.to validate_length_of(:nuget_duplicate_exception_regex).is_at_most(255) }
|
||||
|
||||
it { is_expected.to allow_value(true, false).for(:terraform_module_duplicates_allowed) }
|
||||
it { is_expected.not_to allow_value(nil).for(:terraform_module_duplicates_allowed) }
|
||||
it { is_expected.to validate_length_of(:terraform_module_duplicate_exception_regex).is_at_most(255) }
|
||||
|
||||
describe 'regex values' do
|
||||
let_it_be(:package_settings) { create(:namespace_package_setting) }
|
||||
|
||||
|
|
@ -39,6 +47,50 @@ RSpec.describe Namespace::PackageSetting, feature_category: :package_registry do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'scopes' do
|
||||
describe '.namespace_id_in' do
|
||||
let_it_be(:package_settings) { create(:namespace_package_setting) }
|
||||
let_it_be(:other_package_settings) { create(:namespace_package_setting) }
|
||||
|
||||
subject { described_class.namespace_id_in([package_settings.namespace_id]) }
|
||||
|
||||
it { is_expected.to eq([package_settings]) }
|
||||
end
|
||||
|
||||
describe '.with_terraform_module_duplicates_allowed_or_exception_regex' do
|
||||
let_it_be(:package_settings) { create(:namespace_package_setting) }
|
||||
|
||||
subject { described_class.with_terraform_module_duplicates_allowed_or_exception_regex }
|
||||
|
||||
context 'when terraform_module_duplicates_allowed is true' do
|
||||
before do
|
||||
package_settings.update_column(:terraform_module_duplicates_allowed, true)
|
||||
end
|
||||
|
||||
it { is_expected.to eq([package_settings]) }
|
||||
end
|
||||
|
||||
context 'when terraform_module_duplicate_exception_regex is present' do
|
||||
before do
|
||||
package_settings.update_column(:terraform_module_duplicate_exception_regex, 'foo')
|
||||
end
|
||||
|
||||
it { is_expected.to eq([package_settings]) }
|
||||
end
|
||||
|
||||
context 'when terraform_module_duplicates_allowed is false and terraform_module_duplicate_exception_regex is empty' do
|
||||
before do
|
||||
package_settings.update_columns(
|
||||
terraform_module_duplicates_allowed: false,
|
||||
terraform_module_duplicate_exception_regex: ''
|
||||
)
|
||||
end
|
||||
|
||||
it { is_expected.to be_empty }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#duplicates_allowed?' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
|
|
@ -46,9 +98,14 @@ RSpec.describe Namespace::PackageSetting, feature_category: :package_registry do
|
|||
|
||||
context 'package types with package_settings' do
|
||||
# As more package types gain settings they will be added to this list
|
||||
%i[maven_package generic_package nuget_package].each do |format|
|
||||
context "with package_type:#{format}" do
|
||||
let_it_be(:package) { create(format, name: 'foo', version: '1.0.0-beta') }
|
||||
[
|
||||
{ format: :maven_package, package_name: 'foo' },
|
||||
{ format: :generic_package, package_name: 'foo' },
|
||||
{ format: :nuget_package, package_name: 'foo' },
|
||||
{ format: :terraform_module_package, package_name: 'foo/bar' }
|
||||
].each do |type|
|
||||
context "with package_type: #{type[:format]}" do
|
||||
let_it_be(:package) { create(type[:format], name: type[:package_name], version: '1.0.0-beta') }
|
||||
let_it_be(:package_type) { package.package_type }
|
||||
let_it_be(:package_setting) { package.project.namespace.package_settings }
|
||||
|
||||
|
|
@ -61,7 +118,7 @@ RSpec.describe Namespace::PackageSetting, feature_category: :package_registry do
|
|||
end
|
||||
|
||||
with_them do
|
||||
context "for #{format}" do
|
||||
context "for #{type[:format]}" do
|
||||
before do
|
||||
package_setting.update!(
|
||||
"#{package_type}_duplicates_allowed" => duplicates_allowed,
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ RSpec.describe Organizations::OrganizationPolicy, feature_category: :cell do
|
|||
|
||||
context 'when admin mode is enabled', :enable_admin_mode do
|
||||
it { is_expected.to be_allowed(:admin_organization) }
|
||||
it { is_expected.to be_allowed(:create_group) }
|
||||
it { is_expected.to be_allowed(:read_organization) }
|
||||
it { is_expected.to be_allowed(:read_organization_user) }
|
||||
end
|
||||
|
|
@ -36,12 +37,14 @@ RSpec.describe Organizations::OrganizationPolicy, feature_category: :cell do
|
|||
end
|
||||
|
||||
it { is_expected.to be_allowed(:admin_organization) }
|
||||
it { is_expected.to be_allowed(:create_group) }
|
||||
it { is_expected.to be_allowed(:read_organization) }
|
||||
it { is_expected.to be_allowed(:read_organization_user) }
|
||||
end
|
||||
|
||||
context 'when the user is not part of the organization' do
|
||||
it { is_expected.to be_disallowed(:admin_organization) }
|
||||
it { is_expected.to be_disallowed(:create_group) }
|
||||
it { is_expected.to be_disallowed(:read_organization_user) }
|
||||
# All organizations are currently public, and hence they are allowed to be read
|
||||
# even if the user is not a part of the organization.
|
||||
|
|
|
|||
|
|
@ -23,7 +23,9 @@ RSpec.describe 'Updating the package settings', feature_category: :package_regis
|
|||
lock_npm_package_requests_forwarding: true,
|
||||
pypi_package_requests_forwarding: true,
|
||||
lock_pypi_package_requests_forwarding: true,
|
||||
nuget_symbol_server_enabled: true
|
||||
nuget_symbol_server_enabled: true,
|
||||
terraform_module_duplicates_allowed: true,
|
||||
terraform_module_duplicate_exception_regex: 'foo-.*'
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -44,6 +46,8 @@ RSpec.describe 'Updating the package settings', feature_category: :package_regis
|
|||
pypiPackageRequestsForwarding
|
||||
lockPypiPackageRequestsForwarding
|
||||
nugetSymbolServerEnabled
|
||||
terraformModuleDuplicatesAllowed
|
||||
terraformModuleDuplicateExceptionRegex
|
||||
}
|
||||
errors
|
||||
QL
|
||||
|
|
@ -73,6 +77,8 @@ RSpec.describe 'Updating the package settings', feature_category: :package_regis
|
|||
expect(package_settings_response['npmPackageRequestsForwarding']).to eq(params[:npm_package_requests_forwarding])
|
||||
expect(package_settings_response['lockNpmPackageRequestsForwarding']).to eq(params[:lock_npm_package_requests_forwarding])
|
||||
expect(package_settings_response['nugetSymbolServerEnabled']).to eq(params[:nuget_symbol_server_enabled])
|
||||
expect(package_settings_response['terraformModuleDuplicatesAllowed']).to eq(params[:terraform_module_duplicates_allowed])
|
||||
expect(package_settings_response['terraformModuleDuplicateExceptionRegex']).to eq(params[:terraform_module_duplicate_exception_regex])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -115,7 +121,9 @@ RSpec.describe 'Updating the package settings', feature_category: :package_regis
|
|||
lock_npm_package_requests_forwarding: false,
|
||||
pypi_package_requests_forwarding: nil,
|
||||
lock_pypi_package_requests_forwarding: false,
|
||||
nuget_symbol_server_enabled: false
|
||||
nuget_symbol_server_enabled: false,
|
||||
terraform_module_duplicates_allowed: false,
|
||||
terraform_module_duplicate_exception_regex: 'foo'
|
||||
}, to: {
|
||||
maven_duplicates_allowed: false,
|
||||
maven_duplicate_exception_regex: 'foo-.*',
|
||||
|
|
@ -129,7 +137,9 @@ RSpec.describe 'Updating the package settings', feature_category: :package_regis
|
|||
lock_npm_package_requests_forwarding: true,
|
||||
pypi_package_requests_forwarding: true,
|
||||
lock_pypi_package_requests_forwarding: true,
|
||||
nuget_symbol_server_enabled: true
|
||||
nuget_symbol_server_enabled: true,
|
||||
terraform_module_duplicates_allowed: true,
|
||||
terraform_module_duplicate_exception_regex: 'foo-.*'
|
||||
}
|
||||
|
||||
it_behaves_like 'returning a success'
|
||||
|
|
|
|||
|
|
@ -1937,6 +1937,59 @@ RSpec.describe API::Groups, feature_category: :groups_and_projects do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when group is within a provided organization' do
|
||||
let_it_be(:organization) { create(:organization) }
|
||||
|
||||
context 'when user is an organization user' do
|
||||
before_all do
|
||||
create(:organization_user, user: user3, organization: organization)
|
||||
end
|
||||
|
||||
it 'creates group within organization' do
|
||||
post api('/groups', user3), params: attributes_for_group_api(organization_id: organization.id)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:created)
|
||||
expect(json_response['organization_id']).to eq(organization.id)
|
||||
end
|
||||
|
||||
context 'when parent_group is not part of the organization' do
|
||||
it 'does not create the group with not_found' do
|
||||
post(
|
||||
api('/groups', user3),
|
||||
params: attributes_for_group_api(parent_id: group2.id, organization_id: organization.id)
|
||||
)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when organization does not exist' do
|
||||
it 'does not create the group with not_found' do
|
||||
post api('/groups', user3), params: attributes_for_group_api(organization_id: non_existing_record_id)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user is not an organization user' do
|
||||
it 'does not create the group' do
|
||||
post api('/groups', user3), params: attributes_for_group_api(organization_id: organization.id)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user is an admin' do
|
||||
it 'creates group within organization' do
|
||||
post api('/groups', admin, admin_mode: true), params: attributes_for_group_api(organization_id: organization.id)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:created)
|
||||
expect(json_response['organization_id']).to eq(organization.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when authenticated as user with group permissions" do
|
||||
it "creates group", :aggregate_failures do
|
||||
group = attributes_for_group_api request_access_enabled: false
|
||||
|
|
|
|||
|
|
@ -103,7 +103,28 @@ RSpec.describe API::Terraform::Modules::V1::ProjectPackages, feature_category: :
|
|||
)
|
||||
end
|
||||
|
||||
shared_examples 'creating a package' do
|
||||
it 'creates a package' do
|
||||
expect { api_request }
|
||||
.to change { project.packages.count }.by(1)
|
||||
.and change { Packages::PackageFile.count }.by(1)
|
||||
expect(response).to have_gitlab_http_status(:created)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'not creating a package' do |expected_status|
|
||||
it 'does not create a package' do
|
||||
expect { api_request }
|
||||
.to change { project.packages.count }.by(0)
|
||||
.and change { Packages::PackageFile.count }.by(0)
|
||||
expect(response).to have_gitlab_http_status(expected_status)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with valid project' do
|
||||
let(:user_headers) { { 'PRIVATE-TOKEN' => personal_access_token.token } }
|
||||
let(:headers) { user_headers.merge(workhorse_headers) }
|
||||
|
||||
where(:visibility, :user_role, :member, :token_header, :token_type, :shared_examples_name, :expected_status) do
|
||||
:public | :developer | true | 'PRIVATE-TOKEN' | :personal_access_token | 'process terraform module upload' | :created
|
||||
:public | :guest | true | 'PRIVATE-TOKEN' | :personal_access_token | 'rejects terraform module packages access' | :forbidden
|
||||
|
|
@ -147,7 +168,6 @@ RSpec.describe API::Terraform::Modules::V1::ProjectPackages, feature_category: :
|
|||
|
||||
with_them do
|
||||
let(:user_headers) { user_role == :anonymous ? {} : { token_header => token } }
|
||||
let(:headers) { user_headers.merge(workhorse_headers) }
|
||||
let(:snowplow_gitlab_standard_context) do
|
||||
{ project: project, namespace: project.namespace, user: snowplow_user,
|
||||
property: 'i_package_terraform_module_user' }
|
||||
|
|
@ -172,43 +192,73 @@ RSpec.describe API::Terraform::Modules::V1::ProjectPackages, feature_category: :
|
|||
end
|
||||
|
||||
context 'when failed package file save' do
|
||||
let(:user_headers) { { 'PRIVATE-TOKEN' => personal_access_token.token } }
|
||||
let(:headers) { user_headers.merge(workhorse_headers) }
|
||||
before do
|
||||
project.add_developer(user)
|
||||
allow(Packages::CreatePackageFileService).to receive(:new).and_raise(StandardError)
|
||||
end
|
||||
|
||||
it_behaves_like 'not creating a package', :error
|
||||
end
|
||||
|
||||
context 'with an existing package in the same project' do
|
||||
let_it_be_with_reload(:existing_package) do
|
||||
create(:terraform_module_package, name: 'mymodule/mysystem', version: '1.0.0', project: project)
|
||||
end
|
||||
|
||||
before do
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
it 'does not create package record', :aggregate_failures do
|
||||
allow(Packages::CreatePackageFileService).to receive(:new).and_raise(StandardError)
|
||||
|
||||
expect { api_request }
|
||||
.to change { project.packages.count }.by(0)
|
||||
.and change { Packages::PackageFile.count }.by(0)
|
||||
expect(response).to have_gitlab_http_status(:error)
|
||||
end
|
||||
|
||||
context 'with an existing package' do
|
||||
let_it_be_with_reload(:existing_package) do
|
||||
create(:terraform_module_package, name: 'mymodule/mysystem', version: '1.0.0', project: project)
|
||||
end
|
||||
|
||||
it 'does not create a new package' do
|
||||
expect { api_request }
|
||||
.to change { project.packages.count }.by(0)
|
||||
.and change { Packages::PackageFile.count }.by(0)
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
it_behaves_like 'not creating a package', :forbidden
|
||||
|
||||
context 'when marked as pending_destruction' do
|
||||
it 'does create a new package' do
|
||||
before do
|
||||
existing_package.pending_destruction!
|
||||
|
||||
expect { api_request }
|
||||
.to change { project.packages.count }.by(1)
|
||||
.and change { Packages::PackageFile.count }.by(1)
|
||||
expect(response).to have_gitlab_http_status(:created)
|
||||
end
|
||||
|
||||
it_behaves_like 'creating a package'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with existing package in another project' do
|
||||
let_it_be(:package_settings) { create(:namespace_package_setting, namespace: group) }
|
||||
let_it_be(:project2) { create(:project, namespace: group) }
|
||||
let!(:existing_package) { create(:terraform_module_package, name: 'mymodule/mysystem', project: project2) }
|
||||
|
||||
before do
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
context 'when duplicates not allowed' do
|
||||
it_behaves_like 'not creating a package', :forbidden
|
||||
end
|
||||
|
||||
context 'when duplicates allowed' do
|
||||
before do
|
||||
package_settings.update_column(:terraform_module_duplicates_allowed, true)
|
||||
end
|
||||
|
||||
it_behaves_like 'creating a package'
|
||||
end
|
||||
|
||||
context 'with duplicate regex exception' do
|
||||
before do
|
||||
package_settings.update_columns(
|
||||
terraform_module_duplicates_allowed: false,
|
||||
terraform_module_duplicate_exception_regex: regex
|
||||
)
|
||||
end
|
||||
|
||||
context 'when regex matches' do
|
||||
let(:regex) { ".*#{existing_package.name.last(3)}.*" }
|
||||
|
||||
it_behaves_like 'creating a package'
|
||||
end
|
||||
|
||||
context 'when regex does not match' do
|
||||
let(:regex) { '.*non-matching-regex.*' }
|
||||
|
||||
it_behaves_like 'not creating a package', :forbidden
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ RSpec.describe Groups::CreateService, '#execute', feature_category: :groups_and_
|
|||
let!(:user) { create(:user) }
|
||||
let!(:group_params) { { path: "group_path", visibility_level: Gitlab::VisibilityLevel::PUBLIC } }
|
||||
|
||||
subject { service.execute }
|
||||
subject(:execute) { service.execute }
|
||||
|
||||
shared_examples 'has sync-ed traversal_ids' do
|
||||
specify { expect(subject.reload.traversal_ids).to eq([subject.parent&.traversal_ids, subject.id].flatten.compact) }
|
||||
|
|
@ -119,6 +119,49 @@ RSpec.describe Groups::CreateService, '#execute', feature_category: :groups_and_
|
|||
end
|
||||
end
|
||||
|
||||
describe 'creating a group within an organization' do
|
||||
let(:current_user) { user }
|
||||
let(:service) { described_class.new(current_user, params) }
|
||||
|
||||
context 'when organization is provided' do
|
||||
let_it_be(:organization) { create(:organization) }
|
||||
let(:params) { group_params.merge(organization_id: organization.id) }
|
||||
|
||||
context 'when user can create the group' do
|
||||
before do
|
||||
create(:organization_user, user: user, organization: organization)
|
||||
end
|
||||
|
||||
it { is_expected.to be_persisted }
|
||||
end
|
||||
|
||||
context 'when user is an admin', :enable_admin_mode do
|
||||
let(:current_user) { create(:admin) }
|
||||
|
||||
it { is_expected.to be_persisted }
|
||||
end
|
||||
|
||||
context 'when user can not create the group' do
|
||||
it 'does not save group and returns an error' do
|
||||
expect(execute).not_to be_persisted
|
||||
expect(execute.errors[:organization_id].first)
|
||||
.to eq(s_("CreateGroup|You don't have permission to create a group in the provided organization."))
|
||||
expect(execute.organization_id).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when organization is the default organization and not set by params' do
|
||||
let(:params) { group_params }
|
||||
|
||||
before do
|
||||
create(:organization, :default)
|
||||
end
|
||||
|
||||
it { is_expected.to be_persisted }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'creating subgroup' do
|
||||
let!(:group) { create(:group) }
|
||||
let!(:service) { described_class.new(user, group_params.merge(parent_id: group.id)) }
|
||||
|
|
|
|||
|
|
@ -46,7 +46,9 @@ RSpec.describe ::Namespaces::PackageSettings::UpdateService, feature_category: :
|
|||
lock_npm_package_requests_forwarding: false,
|
||||
pypi_package_requests_forwarding: nil,
|
||||
lock_pypi_package_requests_forwarding: false,
|
||||
nuget_symbol_server_enabled: false
|
||||
nuget_symbol_server_enabled: false,
|
||||
terraform_module_duplicates_allowed: false,
|
||||
terraform_module_duplicate_exception_regex: 'foo'
|
||||
}, to: {
|
||||
maven_duplicates_allowed: false,
|
||||
maven_duplicate_exception_regex: 'RELEASE',
|
||||
|
|
@ -60,7 +62,9 @@ RSpec.describe ::Namespaces::PackageSettings::UpdateService, feature_category: :
|
|||
lock_npm_package_requests_forwarding: true,
|
||||
pypi_package_requests_forwarding: true,
|
||||
lock_pypi_package_requests_forwarding: true,
|
||||
nuget_symbol_server_enabled: true
|
||||
nuget_symbol_server_enabled: true,
|
||||
terraform_module_duplicates_allowed: true,
|
||||
terraform_module_duplicate_exception_regex: 'bar'
|
||||
}
|
||||
|
||||
it_behaves_like 'returning a success'
|
||||
|
|
@ -112,7 +116,9 @@ RSpec.describe ::Namespaces::PackageSettings::UpdateService, feature_category: :
|
|||
lock_npm_package_requests_forwarding: true,
|
||||
pypi_package_requests_forwarding: true,
|
||||
lock_pypi_package_requests_forwarding: true,
|
||||
nuget_symbol_server_enabled: true
|
||||
nuget_symbol_server_enabled: true,
|
||||
terraform_module_duplicates_allowed: true,
|
||||
terraform_module_duplicate_exception_regex: 'bar'
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Packages::TerraformModule::CreatePackageService, feature_category: :package_registry do
|
||||
let_it_be(:namespace) { create(:namespace) }
|
||||
let_it_be(:namespace) { create(:group) }
|
||||
let_it_be(:project) { create(:project, namespace: namespace) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:sha256) { '440e5e148a25331bbd7991575f7d54933c0ebf6cc735a18ee5066ac1381bb590' }
|
||||
let_it_be(:package_settings) { create(:namespace_package_setting, namespace: namespace) }
|
||||
|
||||
let(:overrides) { {} }
|
||||
|
||||
|
|
@ -36,10 +37,72 @@ RSpec.describe Packages::TerraformModule::CreatePackageService, feature_category
|
|||
|
||||
context 'package already exists elsewhere' do
|
||||
let(:project2) { create(:project, namespace: namespace) }
|
||||
let!(:existing_package) { create(:terraform_module_package, project: project2, name: 'foo/bar', version: '1.0.0') }
|
||||
let!(:existing_package) do
|
||||
create(:terraform_module_package, project: project2, name: 'foo/bar', version: '1.0.0')
|
||||
end
|
||||
|
||||
it { expect(subject[:http_status]).to eq 403 }
|
||||
it { expect(subject[:message]).to be 'Access Denied' }
|
||||
context 'when duplicates not allowed' do
|
||||
it { expect(subject.reason).to eq :forbidden }
|
||||
it { expect(subject.message).to be 'A package with the same name already exists in the namespace' }
|
||||
end
|
||||
|
||||
context 'when duplicates allowed' do
|
||||
before do
|
||||
package_settings.update_column(:terraform_module_duplicates_allowed, true)
|
||||
end
|
||||
|
||||
it_behaves_like 'creating a package'
|
||||
end
|
||||
|
||||
context 'with duplicate regex exception' do
|
||||
before do
|
||||
package_settings.update_columns(
|
||||
terraform_module_duplicates_allowed: false,
|
||||
terraform_module_duplicate_exception_regex: regex
|
||||
)
|
||||
end
|
||||
|
||||
context 'when regex matches' do
|
||||
let(:regex) { ".*#{existing_package.name.last(3)}.*" }
|
||||
|
||||
it_behaves_like 'creating a package'
|
||||
end
|
||||
|
||||
context 'when regex does not match' do
|
||||
let(:regex) { '.*not-a-match.*' }
|
||||
|
||||
it { expect(subject.reason).to eq :forbidden }
|
||||
it { expect(subject.message).to be 'A package with the same name already exists in the namespace' }
|
||||
end
|
||||
end
|
||||
|
||||
context 'for ancestor namespace' do
|
||||
let_it_be(:package_settings) { create(:namespace_package_setting, :group) }
|
||||
let_it_be(:parent_namespace) { package_settings.namespace }
|
||||
|
||||
before do
|
||||
namespace.update!(parent: parent_namespace)
|
||||
end
|
||||
|
||||
context 'when duplicates allowed in an ancestor' do
|
||||
before do
|
||||
package_settings.update_column(:terraform_module_duplicates_allowed, true)
|
||||
end
|
||||
|
||||
it_behaves_like 'creating a package'
|
||||
end
|
||||
|
||||
context 'when duplicates allowed in an ancestor with exception' do
|
||||
before do
|
||||
package_settings.update_columns(
|
||||
terraform_module_duplicates_allowed: false,
|
||||
terraform_module_duplicate_exception_regex: ".*#{existing_package.name.last(3)}.*"
|
||||
)
|
||||
end
|
||||
|
||||
it_behaves_like 'creating a package'
|
||||
end
|
||||
end
|
||||
|
||||
context 'marked as pending_destruction' do
|
||||
before do
|
||||
|
|
@ -53,7 +116,7 @@ RSpec.describe Packages::TerraformModule::CreatePackageService, feature_category
|
|||
context 'version already exists' do
|
||||
let!(:existing_version) { create(:terraform_module_package, project: project, name: 'foo/bar', version: '1.0.1') }
|
||||
|
||||
it { expect(subject[:http_status]).to eq 403 }
|
||||
it { expect(subject[:reason]).to eq :forbidden }
|
||||
it { expect(subject[:message]).to be 'Package version already exists.' }
|
||||
|
||||
context 'marked as pending_destruction' do
|
||||
|
|
@ -68,7 +131,7 @@ RSpec.describe Packages::TerraformModule::CreatePackageService, feature_category
|
|||
context 'with empty version' do
|
||||
let(:overrides) { { module_version: '' } }
|
||||
|
||||
it { expect(subject[:http_status]).to eq 400 }
|
||||
it { expect(subject[:reason]).to eq :bad_request }
|
||||
it { expect(subject[:message]).to eq 'Version is empty.' }
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ RSpec.shared_examples 'updating the namespace package setting attributes' do |to
|
|||
.and change { namespace.package_settings.reload.nuget_duplicates_allowed }.from(from[:nuget_duplicates_allowed]).to(to[:nuget_duplicates_allowed])
|
||||
.and change { namespace.package_settings.reload.nuget_duplicate_exception_regex }.from(from[:nuget_duplicate_exception_regex]).to(to[:nuget_duplicate_exception_regex])
|
||||
.and change { namespace.package_settings.reload.nuget_symbol_server_enabled }.from(from[:nuget_symbol_server_enabled]).to(to[:nuget_symbol_server_enabled])
|
||||
.and change { namespace.package_settings.reload.terraform_module_duplicates_allowed }.from(from[:terraform_module_duplicates_allowed]).to(to[:terraform_module_duplicates_allowed])
|
||||
.and change { namespace.package_settings.reload.terraform_module_duplicate_exception_regex }.from(from[:terraform_module_duplicate_exception_regex]).to(to[:terraform_module_duplicate_exception_regex])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -36,6 +38,8 @@ RSpec.shared_examples 'creating the namespace package setting' do
|
|||
expect(namespace.package_setting_relation.nuget_duplicates_allowed).to eq(package_settings[:nuget_duplicates_allowed])
|
||||
expect(namespace.package_setting_relation.nuget_duplicate_exception_regex).to eq(package_settings[:nuget_duplicate_exception_regex])
|
||||
expect(namespace.package_setting_relation.nuget_symbol_server_enabled).to eq(package_settings[:nuget_symbol_server_enabled])
|
||||
expect(namespace.package_setting_relation.terraform_module_duplicates_allowed).to eq(package_settings[:terraform_module_duplicates_allowed])
|
||||
expect(namespace.package_setting_relation.terraform_module_duplicate_exception_regex).to eq(package_settings[:terraform_module_duplicate_exception_regex])
|
||||
end
|
||||
|
||||
it_behaves_like 'returning a success'
|
||||
|
|
|
|||
Loading…
Reference in New Issue