Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
7b5ff5df50
commit
eddf359962
41 changed files with 507 additions and 300 deletions
|
@ -2,5 +2,4 @@
|
|||
Gitlab/DelegatePredicateMethods:
|
||||
Exclude:
|
||||
- app/models/clusters/cluster.rb
|
||||
- app/models/project.rb
|
||||
- ee/app/models/concerns/ee/ci/metadatable.rb
|
||||
|
|
|
@ -12,11 +12,11 @@ export default () => {
|
|||
phrase,
|
||||
buttonText,
|
||||
buttonClass = '',
|
||||
buttonTestid = null,
|
||||
buttonVariant = null,
|
||||
buttonTestid,
|
||||
buttonVariant,
|
||||
confirmDangerMessage,
|
||||
confirmButtonText = null,
|
||||
disabled = false,
|
||||
disabled,
|
||||
additionalInformation,
|
||||
htmlConfirmationMessage,
|
||||
} = el.dataset;
|
||||
|
|
|
@ -38,7 +38,7 @@ initMembersApp(document.querySelector('.js-project-members-list-app'), {
|
|||
},
|
||||
},
|
||||
[MEMBER_TYPES.group]: {
|
||||
tableFields: SHARED_FIELDS.concat('granted'),
|
||||
tableFields: SHARED_FIELDS.concat(['source', 'granted']),
|
||||
tableAttrs: {
|
||||
table: { 'data-qa-selector': 'groups_list' },
|
||||
tr: { 'data-qa-selector': 'group_row' },
|
||||
|
@ -46,7 +46,7 @@ initMembersApp(document.querySelector('.js-project-members-list-app'), {
|
|||
requestFormatter: groupLinkRequestFormatter,
|
||||
filteredSearchBar: {
|
||||
show: true,
|
||||
tokens: [],
|
||||
tokens: ['groups_with_inherited_permissions'],
|
||||
searchParam: 'search_groups',
|
||||
placeholder: s__('Members|Search groups'),
|
||||
recentSearchesStorageKey: 'project_group_links',
|
||||
|
|
|
@ -121,27 +121,21 @@ export default {
|
|||
@selectRevision="onSelectRevision"
|
||||
/>
|
||||
</div>
|
||||
<div class="gl-mt-6">
|
||||
<div class="gl-display-flex gl-mt-6 gl-gap-3">
|
||||
<gl-button category="primary" variant="confirm" @click="onSubmit">
|
||||
{{ s__('CompareRevisions|Compare') }}
|
||||
</gl-button>
|
||||
<gl-button data-testid="swapRevisionsButton" class="btn btn-default" @click="onSwapRevision">
|
||||
<gl-button data-testid="swapRevisionsButton" @click="onSwapRevision">
|
||||
{{ s__('CompareRevisions|Swap revisions') }}
|
||||
</gl-button>
|
||||
<gl-button
|
||||
v-if="projectMergeRequestPath"
|
||||
:href="projectMergeRequestPath"
|
||||
data-testid="projectMrButton"
|
||||
class="btn btn-default gl-button"
|
||||
>
|
||||
{{ s__('CompareRevisions|View open merge request') }}
|
||||
</gl-button>
|
||||
<gl-button
|
||||
v-else-if="createMrPath"
|
||||
:href="createMrPath"
|
||||
data-testid="createMrButton"
|
||||
class="btn btn-default gl-button"
|
||||
>
|
||||
<gl-button v-else-if="createMrPath" :href="createMrPath" data-testid="createMrButton">
|
||||
{{ s__('CompareRevisions|Create merge request') }}
|
||||
</gl-button>
|
||||
</div>
|
||||
|
|
|
@ -276,28 +276,33 @@ const bindEvents = () => {
|
|||
);
|
||||
|
||||
let isProjectImportUrlDirty = false;
|
||||
$projectImportUrl.addEventListener('blur', () => {
|
||||
isProjectImportUrlDirty = true;
|
||||
debouncedUpdateUrlPathWarningVisibility();
|
||||
});
|
||||
$projectImportUrl.addEventListener('keyup', () => {
|
||||
deriveProjectPathFromUrl($projectImportUrl);
|
||||
});
|
||||
|
||||
if ($projectImportUrl) {
|
||||
$projectImportUrl.addEventListener('blur', () => {
|
||||
isProjectImportUrlDirty = true;
|
||||
debouncedUpdateUrlPathWarningVisibility();
|
||||
});
|
||||
$projectImportUrl.addEventListener('keyup', () => {
|
||||
deriveProjectPathFromUrl($projectImportUrl);
|
||||
});
|
||||
}
|
||||
|
||||
[$projectImportUrl, $projectImportUrlUser, $projectImportUrlPassword].forEach(($f) => {
|
||||
if ($f?.on) {
|
||||
$f.on('input', () => {
|
||||
if (isProjectImportUrlDirty) {
|
||||
debouncedUpdateUrlPathWarningVisibility();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$f.addEventListener('input', () => {
|
||||
if (!$f) return false;
|
||||
|
||||
if ($f.on) {
|
||||
return $f.on('input', () => {
|
||||
if (isProjectImportUrlDirty) {
|
||||
debouncedUpdateUrlPathWarningVisibility();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return $f.addEventListener('input', () => {
|
||||
if (isProjectImportUrlDirty) {
|
||||
debouncedUpdateUrlPathWarningVisibility();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$projectImportForm.on('submit', async (e) => {
|
||||
|
|
|
@ -13,9 +13,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController
|
|||
|
||||
def index
|
||||
@sort = params[:sort].presence || sort_value_name
|
||||
|
||||
@group_links = @project.project_group_links
|
||||
@group_links = @group_links.search(params[:search_groups]) if params[:search_groups].present?
|
||||
@include_relations ||= requested_relations(:groups_with_inherited_permissions)
|
||||
|
||||
if can?(current_user, :admin_project_member, @project)
|
||||
@invited_members = present_members(invited_members)
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Projects::ProjectMembersHelper
|
||||
def project_members_app_data_json(project, members:, group_links:, invited:, access_requests:)
|
||||
def project_members_app_data_json(project, members:, invited:, access_requests:, include_relations:, search:)
|
||||
{
|
||||
user: project_members_list_data(project, members, { param_name: :page, params: { search_groups: nil } }),
|
||||
group: project_group_links_list_data(project, group_links),
|
||||
group: project_group_links_list_data(project, include_relations, search),
|
||||
invite: project_members_list_data(project, invited.nil? ? [] : invited),
|
||||
access_request: project_members_list_data(project, access_requests.nil? ? [] : access_requests),
|
||||
source_id: project.id,
|
||||
|
@ -57,10 +57,29 @@ module Projects::ProjectMembersHelper
|
|||
}
|
||||
end
|
||||
|
||||
def project_group_links_list_data(project, group_links)
|
||||
def project_group_links_list_data(project, include_relations, search)
|
||||
members = []
|
||||
|
||||
if include_relations.include?(:direct)
|
||||
project_group_links = project.project_group_links
|
||||
project_group_links = project_group_links.search(search) if search
|
||||
members += project_group_links_serialized(project, project_group_links)
|
||||
end
|
||||
|
||||
if include_relations.include?(:inherited)
|
||||
group_group_links = project.group_group_links.distinct_on_shared_with_group_id_with_group_access
|
||||
group_group_links = group_group_links.search(search) if search
|
||||
members += group_group_links_serialized(project, group_group_links)
|
||||
end
|
||||
|
||||
if project_group_links.present? && group_group_links.present?
|
||||
members = members.sort_by { |m| -m.dig(:access_level, :integer_value).to_i }
|
||||
.uniq { |m| m.dig(:shared_with_group, :id) }
|
||||
end
|
||||
|
||||
{
|
||||
members: project_group_links_serialized(project, group_links),
|
||||
pagination: members_pagination_data(group_links),
|
||||
members: members,
|
||||
pagination: members_pagination_data(members),
|
||||
member_path: project_group_link_path(project, ':id')
|
||||
}
|
||||
end
|
||||
|
|
|
@ -441,33 +441,29 @@ class Project < ApplicationRecord
|
|||
accepts_nested_attributes_for :prometheus_integration, update_only: true
|
||||
accepts_nested_attributes_for :alerting_setting, update_only: true
|
||||
|
||||
delegate :feature_available?, :builds_enabled?, :wiki_enabled?,
|
||||
:merge_requests_enabled?, :forking_enabled?, :issues_enabled?,
|
||||
:pages_enabled?, :analytics_enabled?, :snippets_enabled?, :public_pages?, :private_pages?,
|
||||
:merge_requests_access_level, :forking_access_level, :issues_access_level,
|
||||
:wiki_access_level, :snippets_access_level, :builds_access_level,
|
||||
:repository_access_level, :package_registry_access_level, :pages_access_level, :metrics_dashboard_access_level, :analytics_access_level,
|
||||
:operations_enabled?, :operations_access_level, :security_and_compliance_access_level,
|
||||
:container_registry_access_level, :container_registry_enabled?,
|
||||
to: :project_feature, allow_nil: true
|
||||
alias_method :container_registry_enabled, :container_registry_enabled?
|
||||
delegate :show_default_award_emojis, :show_default_award_emojis=, :show_default_award_emojis?,
|
||||
:enforce_auth_checks_on_uploads, :enforce_auth_checks_on_uploads=, :enforce_auth_checks_on_uploads?,
|
||||
:warn_about_potentially_unwanted_characters, :warn_about_potentially_unwanted_characters=, :warn_about_potentially_unwanted_characters?,
|
||||
to: :project_setting, allow_nil: true
|
||||
delegate :scheduled?, :started?, :in_progress?, :failed?, :finished?,
|
||||
prefix: :import, to: :import_state, allow_nil: true
|
||||
delegate :merge_requests_access_level, :forking_access_level, :issues_access_level,
|
||||
:wiki_access_level, :snippets_access_level, :builds_access_level,
|
||||
:repository_access_level, :package_registry_access_level, :pages_access_level,
|
||||
:metrics_dashboard_access_level, :analytics_access_level,
|
||||
:operations_access_level, :security_and_compliance_access_level,
|
||||
:container_registry_access_level,
|
||||
to: :project_feature, allow_nil: true
|
||||
|
||||
delegate :show_default_award_emojis, :show_default_award_emojis=,
|
||||
:enforce_auth_checks_on_uploads, :enforce_auth_checks_on_uploads=,
|
||||
:warn_about_potentially_unwanted_characters, :warn_about_potentially_unwanted_characters=,
|
||||
to: :project_setting, allow_nil: true
|
||||
|
||||
delegate :squash_always?, :squash_never?, :squash_enabled_by_default?, :squash_readonly?, to: :project_setting
|
||||
delegate :squash_option, :squash_option=, to: :project_setting
|
||||
delegate :mr_default_target_self, :mr_default_target_self=, to: :project_setting
|
||||
delegate :previous_default_branch, :previous_default_branch=, to: :project_setting
|
||||
delegate :no_import?, to: :import_state, allow_nil: true
|
||||
delegate :name, to: :owner, allow_nil: true, prefix: true
|
||||
delegate :members, to: :team, prefix: true
|
||||
delegate :add_user, :add_users, to: :team
|
||||
delegate :add_guest, :add_reporter, :add_developer, :add_maintainer, :add_owner, :add_role, to: :team
|
||||
delegate :group_runners_enabled, :group_runners_enabled=, to: :ci_cd_settings, allow_nil: true
|
||||
delegate :root_ancestor, :certificate_based_clusters_enabled?, to: :namespace, allow_nil: true
|
||||
delegate :root_ancestor, to: :namespace, allow_nil: true
|
||||
delegate :last_pipeline, to: :commit, allow_nil: true
|
||||
delegate :external_dashboard_url, to: :metrics_setting, allow_nil: true, prefix: true
|
||||
delegate :dashboard_timezone, to: :metrics_setting, allow_nil: true, prefix: true
|
||||
|
@ -483,7 +479,6 @@ class Project < ApplicationRecord
|
|||
delegate :allow_merge_on_skipped_pipeline, :allow_merge_on_skipped_pipeline?,
|
||||
:allow_merge_on_skipped_pipeline=, :has_confluence?, :has_shimo?,
|
||||
to: :project_setting
|
||||
delegate :active?, to: :prometheus_integration, allow_nil: true, prefix: true
|
||||
delegate :merge_commit_template, :merge_commit_template=, to: :project_setting, allow_nil: true
|
||||
delegate :squash_commit_template, :squash_commit_template=, to: :project_setting, allow_nil: true
|
||||
|
||||
|
@ -916,6 +911,14 @@ class Project < ApplicationRecord
|
|||
association(:namespace).loaded?
|
||||
end
|
||||
|
||||
def certificate_based_clusters_enabled?
|
||||
!!namespace&.certificate_based_clusters_enabled?
|
||||
end
|
||||
|
||||
def prometheus_integration_active?
|
||||
!!prometheus_integration&.active?
|
||||
end
|
||||
|
||||
def personal_namespace_holder?(user)
|
||||
return false unless personal?
|
||||
return false unless user
|
||||
|
@ -932,6 +935,42 @@ class Project < ApplicationRecord
|
|||
super.presence || build_project_setting
|
||||
end
|
||||
|
||||
def show_default_award_emojis?
|
||||
!!project_setting&.show_default_award_emojis?
|
||||
end
|
||||
|
||||
def enforce_auth_checks_on_uploads?
|
||||
!!project_setting&.enforce_auth_checks_on_uploads?
|
||||
end
|
||||
|
||||
def warn_about_potentially_unwanted_characters?
|
||||
!!project_setting&.warn_about_potentially_unwanted_characters?
|
||||
end
|
||||
|
||||
def no_import?
|
||||
!!import_state&.no_import?
|
||||
end
|
||||
|
||||
def import_scheduled?
|
||||
!!import_state&.scheduled?
|
||||
end
|
||||
|
||||
def import_started?
|
||||
!!import_state&.started?
|
||||
end
|
||||
|
||||
def import_in_progress?
|
||||
!!import_state&.in_progress?
|
||||
end
|
||||
|
||||
def import_failed?
|
||||
!!import_state&.failed?
|
||||
end
|
||||
|
||||
def import_finished?
|
||||
!!import_state&.finished?
|
||||
end
|
||||
|
||||
def all_pipelines
|
||||
if builds_enabled?
|
||||
super
|
||||
|
@ -1839,6 +1878,59 @@ class Project < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
def feature_available?(feature, user = nil)
|
||||
!!project_feature&.feature_available?(feature, user)
|
||||
end
|
||||
|
||||
def builds_enabled?
|
||||
!!project_feature&.builds_enabled?
|
||||
end
|
||||
|
||||
def wiki_enabled?
|
||||
!!project_feature&.wiki_enabled?
|
||||
end
|
||||
|
||||
def merge_requests_enabled?
|
||||
!!project_feature&.merge_requests_enabled?
|
||||
end
|
||||
|
||||
def forking_enabled?
|
||||
!!project_feature&.forking_enabled?
|
||||
end
|
||||
|
||||
def issues_enabled?
|
||||
!!project_feature&.issues_enabled?
|
||||
end
|
||||
|
||||
def pages_enabled?
|
||||
!!project_feature&.pages_enabled?
|
||||
end
|
||||
|
||||
def analytics_enabled?
|
||||
!!project_feature&.analytics_enabled?
|
||||
end
|
||||
|
||||
def snippets_enabled?
|
||||
!!project_feature&.snippets_enabled?
|
||||
end
|
||||
|
||||
def public_pages?
|
||||
!!project_feature&.public_pages?
|
||||
end
|
||||
|
||||
def private_pages?
|
||||
!!project_feature&.private_pages?
|
||||
end
|
||||
|
||||
def operations_enabled?
|
||||
!!project_feature&.operations_enabled?
|
||||
end
|
||||
|
||||
def container_registry_enabled?
|
||||
!!project_feature&.container_registry_enabled?
|
||||
end
|
||||
alias_method :container_registry_enabled, :container_registry_enabled?
|
||||
|
||||
def enable_ci
|
||||
project_feature.update_attribute(:builds_access_level, ProjectFeature::ENABLED)
|
||||
end
|
||||
|
@ -2902,6 +2994,10 @@ class Project < ApplicationRecord
|
|||
build_artifacts_size_refresh&.started?
|
||||
end
|
||||
|
||||
def group_group_links
|
||||
group&.shared_with_group_links&.of_ancestors_and_self || GroupGroupLink.none
|
||||
end
|
||||
|
||||
def security_training_available?
|
||||
licensed_feature_available?(:security_training)
|
||||
end
|
||||
|
|
|
@ -16,6 +16,8 @@ module Packages
|
|||
raise ActiveRecord::RecordInvalid, meta
|
||||
end
|
||||
|
||||
params.delete(:md5_digest) if Gitlab::FIPS.enabled?
|
||||
|
||||
Packages::Pypi::Metadatum.upsert(meta.attributes)
|
||||
|
||||
::Packages::CreatePackageFileService.new(created_package, file_params).execute
|
||||
|
|
|
@ -39,7 +39,8 @@
|
|||
|
||||
.js-project-members-list-app{ data: { members_data: project_members_app_data_json(@project,
|
||||
members: @project_members,
|
||||
group_links: @group_links,
|
||||
invited: @invited_members,
|
||||
access_requests: @requesters) } }
|
||||
access_requests: @requesters,
|
||||
include_relations: @include_relations,
|
||||
search: params[:search_groups]) } }
|
||||
= gl_loading_icon(css_class: 'gl-my-5', size: 'md')
|
||||
|
|
|
@ -20,6 +20,8 @@ module Gitlab
|
|||
|
||||
config.view_component.preview_route = "/-/view_component/previews"
|
||||
|
||||
config.active_support.hash_digest_class = ::OpenSSL::Digest::SHA256
|
||||
|
||||
# This section contains configuration from Rails upgrades to override the new defaults so that we
|
||||
# keep existing behavior.
|
||||
#
|
||||
|
@ -38,7 +40,6 @@ module Gitlab
|
|||
# Rails 5.2
|
||||
config.action_dispatch.use_authenticated_cookie_encryption = false
|
||||
config.active_support.use_authenticated_message_encryption = false
|
||||
config.active_support.hash_digest_class = ::Digest::MD5 # New default is ::Digest::SHA1
|
||||
config.action_controller.default_protect_from_forgery = false
|
||||
config.action_view.form_with_generates_ids = false
|
||||
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: active_support_hash_digest_sha256
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90098
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/365314
|
||||
milestone: '15.1'
|
||||
type: development
|
||||
group: group::sharding
|
||||
default_enabled: false
|
|
@ -8,7 +8,8 @@
|
|||
# and it's used only as the last resort. In such case this termination is
|
||||
# logged and we should fix the potential timeout issue in the code itself.
|
||||
|
||||
if Gitlab::Runtime.puma? && !Rails.env.test?
|
||||
if Gitlab::Runtime.puma? && !Rails.env.test? &&
|
||||
Gitlab::Utils.to_boolean(ENV['GITLAB_RAILS_RACK_TIMEOUT_ENABLE'], default: true)
|
||||
Rack::Timeout::Logger.level = Logger::ERROR
|
||||
|
||||
Gitlab::Application.configure do |config|
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
Rails.application.configure do
|
||||
# We set ActiveSupport::Digest.hash_digest_class directly copying
|
||||
# See https://github.com/rails/rails/blob/6-1-stable/activesupport/lib/active_support/railtie.rb#L96-L98
|
||||
#
|
||||
# Note that is the only usage of config.active_support.hash_digest_class
|
||||
config.after_initialize do
|
||||
ActiveSupport::Digest.hash_digest_class = Gitlab::HashDigest::Facade
|
||||
end
|
||||
end
|
|
@ -169,6 +169,20 @@ mutation {
|
|||
|
||||
The header is created if the returned `errors` object is empty.
|
||||
|
||||
### Update with the API
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/361964) in GitLab 15.2.
|
||||
|
||||
Group owners can update a HTTP header using the GraphQL `auditEventsStreamingHeadersCreate` mutation.
|
||||
|
||||
```graphql
|
||||
mutation {
|
||||
auditEventsStreamingHeadersUpdate(input: { headerId: "gid://gitlab/AuditEvents::Streaming::Header/24255", key: "new-foo", value: "new-bar" }) {
|
||||
errors
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Delete with the API
|
||||
|
||||
Group owners can remove a HTTP header using the GraphQL `auditEventsStreamingHeadersDestroy` mutation. You can retrieve the header ID
|
||||
|
|
|
@ -42,11 +42,6 @@ The process also updates the following user information:
|
|||
- SSH public keys (if `sync_ssh_keys` is set)
|
||||
- Kerberos identity (if Kerberos is enabled)
|
||||
|
||||
The LDAP sync process:
|
||||
|
||||
- Updates existing users.
|
||||
- Creates new users on first sign in.
|
||||
|
||||
### Adjust LDAP user sync schedule
|
||||
|
||||
By default, GitLab runs a worker once per day at 01:30 a.m. server time to
|
||||
|
|
|
@ -778,6 +778,27 @@ Input type: `AuditEventsStreamingHeadersDestroyInput`
|
|||
| <a id="mutationauditeventsstreamingheadersdestroyclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||
| <a id="mutationauditeventsstreamingheadersdestroyerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
|
||||
|
||||
### `Mutation.auditEventsStreamingHeadersUpdate`
|
||||
|
||||
Input type: `AuditEventsStreamingHeadersUpdateInput`
|
||||
|
||||
#### Arguments
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="mutationauditeventsstreamingheadersupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||
| <a id="mutationauditeventsstreamingheadersupdateheaderid"></a>`headerId` | [`AuditEventsStreamingHeaderID!`](#auditeventsstreamingheaderid) | Header to update. |
|
||||
| <a id="mutationauditeventsstreamingheadersupdatekey"></a>`key` | [`String!`](#string) | Header key. |
|
||||
| <a id="mutationauditeventsstreamingheadersupdatevalue"></a>`value` | [`String!`](#string) | Header value. |
|
||||
|
||||
#### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="mutationauditeventsstreamingheadersupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||
| <a id="mutationauditeventsstreamingheadersupdateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
|
||||
| <a id="mutationauditeventsstreamingheadersupdateheader"></a>`header` | [`AuditEventStreamingHeader`](#auditeventstreamingheader) | Updates header. |
|
||||
|
||||
### `Mutation.awardEmojiAdd`
|
||||
|
||||
Input type: `AwardEmojiAddInput`
|
||||
|
|
|
@ -692,7 +692,7 @@ Example of response
|
|||
"finished_at": null,
|
||||
"duration": 8,
|
||||
"queued_duration": 0.010,
|
||||
"id": 42,
|
||||
"id": 1,
|
||||
"name": "rubocop",
|
||||
"ref": "main",
|
||||
"artifacts": [],
|
||||
|
@ -742,7 +742,7 @@ Example of response
|
|||
"finished_at": null,
|
||||
"duration": null,
|
||||
"queued_duration": 0.010,
|
||||
"id": 42,
|
||||
"id": 1,
|
||||
"name": "rubocop",
|
||||
"ref": "main",
|
||||
"artifacts": [],
|
||||
|
@ -792,7 +792,7 @@ Example of response
|
|||
"coverage": null,
|
||||
"allow_failure": false,
|
||||
"download_url": null,
|
||||
"id": 42,
|
||||
"id": 1,
|
||||
"name": "rubocop",
|
||||
"ref": "main",
|
||||
"artifacts": [],
|
||||
|
@ -873,7 +873,7 @@ Example response:
|
|||
"finished_at": null,
|
||||
"duration": null,
|
||||
"queued_duration": 0.010,
|
||||
"id": 42,
|
||||
"id": 1,
|
||||
"name": "rubocop",
|
||||
"ref": "main",
|
||||
"artifacts": [],
|
||||
|
|
|
@ -20,6 +20,10 @@ These endpoints do not adhere to the standard API authentication methods.
|
|||
See the [PyPI package registry documentation](../../user/packages/pypi_repository/index.md)
|
||||
for details on which headers and token types are supported.
|
||||
|
||||
NOTE:
|
||||
[Twine 3.4.2](https://twine.readthedocs.io/en/stable/changelog.html?highlight=FIPS#id28) or greater
|
||||
is recommended when [FIPS mode](../../development/fips_compliance.md) is enabled.
|
||||
|
||||
## Download a package file from a group
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/225545) in GitLab 13.12.
|
||||
|
|
|
@ -393,7 +393,7 @@ is updated in a major version GitLab release.
|
|||
|
||||
### Add metrics
|
||||
|
||||
Every CI/CD template must also have metrics defined to track their use.
|
||||
Every CI/CD template must also have metrics defined to track their use. The CI/CD template monthly usage report can be found in [Sisense (GitLab team members only)](https://app.periscopedata.com/app/gitlab/785953/Pipeline-Authoring-Dashboard?widget=14910475&udv=0).
|
||||
|
||||
To add a metric definition for a new template:
|
||||
|
||||
|
|
|
@ -217,6 +217,8 @@ module API
|
|||
|
||||
track_package_event('push_package', :pypi, project: authorized_user_project, user: current_user, namespace: authorized_user_project.namespace)
|
||||
|
||||
unprocessable_entity! if Gitlab::FIPS.enabled? && declared_params[:md5_digest].present?
|
||||
|
||||
::Packages::Pypi::CreatePackageService
|
||||
.new(authorized_user_project, current_user, declared_params.merge(build: current_authenticated_job))
|
||||
.execute
|
||||
|
|
|
@ -7,6 +7,8 @@ module Gitlab
|
|||
form_action frame_ancestors frame_src img_src manifest_src
|
||||
media_src object_src report_uri script_src style_src worker_src).freeze
|
||||
|
||||
DEFAULT_FALLBACK_VALUE = '<default_value>'
|
||||
|
||||
def self.default_enabled
|
||||
Rails.env.development? || Rails.env.test?
|
||||
end
|
||||
|
@ -62,8 +64,10 @@ module Gitlab
|
|||
end
|
||||
|
||||
def initialize(csp_directives)
|
||||
# Using <default_value> falls back to the default values.
|
||||
directives = csp_directives.reject { |_, value| value == DEFAULT_FALLBACK_VALUE }
|
||||
@merged_csp_directives =
|
||||
HashWithIndifferentAccess.new(csp_directives)
|
||||
HashWithIndifferentAccess.new(directives)
|
||||
.reverse_merge(::Gitlab::ContentSecurityPolicy::ConfigLoader.default_directives)
|
||||
end
|
||||
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module HashDigest
|
||||
# Used for rolling out to use OpenSSL::Digest::SHA256
|
||||
# for ActiveSupport::Digest
|
||||
class Facade
|
||||
class << self
|
||||
def hexdigest(...)
|
||||
hash_digest_class.hexdigest(...)
|
||||
end
|
||||
|
||||
def hash_digest_class
|
||||
if use_sha256?
|
||||
::OpenSSL::Digest::SHA256
|
||||
else
|
||||
::Digest::MD5 # rubocop:disable Fips/MD5
|
||||
end
|
||||
end
|
||||
|
||||
def use_sha256?
|
||||
return false unless Feature.feature_flags_available?
|
||||
|
||||
Feature.enabled?(:active_support_hash_digest_sha256)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -79,7 +79,10 @@ module QA
|
|||
|
||||
def delete_issue
|
||||
click_element(:issue_actions_ellipsis_dropdown)
|
||||
click_element(:delete_issue_button, Page::Modal::DeleteIssue)
|
||||
|
||||
click_element(:delete_issue_button,
|
||||
Page::Modal::DeleteIssue,
|
||||
wait: Support::Repeater::DEFAULT_MAX_WAIT_TIME)
|
||||
|
||||
Page::Modal::DeleteIssue.perform(&:confirm_delete_issue)
|
||||
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Manage' do
|
||||
describe 'Personal project permissions', :reliable do
|
||||
let!(:owner) { Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1) }
|
||||
|
||||
let!(:owner_api_client) { Runtime::API::Client.new(:gitlab, user: owner) }
|
||||
|
||||
let!(:project) do
|
||||
Resource::Project.fabricate_via_api! do |project|
|
||||
project.api_client = owner_api_client
|
||||
project.name = 'qa-owner-personal-project'
|
||||
project.personal_namespace = owner.username
|
||||
end
|
||||
end
|
||||
|
||||
after do
|
||||
project&.remove_via_api!
|
||||
end
|
||||
|
||||
context 'when user is added as Owner' do
|
||||
let(:issue) do
|
||||
Resource::Issue.fabricate_via_api! do |issue|
|
||||
issue.api_client = owner_api_client
|
||||
issue.project = project
|
||||
issue.title = 'Test Owner deletes issue'
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
Flow::Login.sign_in(as: owner)
|
||||
end
|
||||
|
||||
it "has Owner role with Owner permissions", testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/352542' do
|
||||
Page::Dashboard::Projects.perform do |projects|
|
||||
projects.filter_by_name(project.name)
|
||||
|
||||
expect(projects).to have_project_with_access_role(project.name, 'Owner')
|
||||
end
|
||||
|
||||
expect_owner_permissions_allow_delete_issue
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user is added as Maintainer' do
|
||||
let(:maintainer) { Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_2, Runtime::Env.gitlab_qa_password_2) }
|
||||
|
||||
let(:issue) do
|
||||
Resource::Issue.fabricate_via_api! do |issue|
|
||||
issue.api_client = owner_api_client
|
||||
issue.project = project
|
||||
issue.title = 'Test Maintainer deletes issue'
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
project.add_member(maintainer, Resource::Members::AccessLevel::MAINTAINER)
|
||||
Flow::Login.sign_in(as: maintainer)
|
||||
end
|
||||
|
||||
it "has Maintainer role without Owner permissions", testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/352607' do
|
||||
Page::Dashboard::Projects.perform do |projects|
|
||||
projects.filter_by_name(project.name)
|
||||
|
||||
expect(projects).to have_project_with_access_role(project.name, 'Maintainer')
|
||||
end
|
||||
|
||||
expect_maintainer_permissions_do_not_allow_delete_issue
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def expect_owner_permissions_allow_delete_issue
|
||||
issue.visit!
|
||||
|
||||
Page::Project::Issue::Show.perform(&:delete_issue)
|
||||
|
||||
Page::Project::Issue::Index.perform do |index|
|
||||
expect(index).not_to have_issue(issue)
|
||||
end
|
||||
end
|
||||
|
||||
def expect_maintainer_permissions_do_not_allow_delete_issue
|
||||
issue.visit!
|
||||
|
||||
Page::Project::Issue::Show.perform do |issue|
|
||||
expect(issue).not_to have_delete_issue_button
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,104 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Manage' do
|
||||
describe 'Project owner permissions' do
|
||||
let!(:owner) do
|
||||
Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1)
|
||||
end
|
||||
|
||||
let!(:owner_api_client) { Runtime::API::Client.new(:gitlab, user: owner) }
|
||||
|
||||
let!(:maintainer) do
|
||||
Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_2, Runtime::Env.gitlab_qa_password_2)
|
||||
end
|
||||
|
||||
shared_examples 'when user is added as owner' do |project_type, testcase|
|
||||
let!(:issue) do
|
||||
Resource::Issue.fabricate_via_api! do |issue|
|
||||
issue.api_client = owner_api_client
|
||||
issue.project = project
|
||||
issue.title = 'Test Owner Deletes Issue'
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
project.add_member(owner, Resource::Members::AccessLevel::OWNER) if project_type == :group_project
|
||||
Flow::Login.sign_in(as: owner)
|
||||
end
|
||||
|
||||
it "has owner role with owner permissions", testcase: testcase do
|
||||
Page::Dashboard::Projects.perform do |projects|
|
||||
projects.filter_by_name(project.name)
|
||||
|
||||
expect(projects).to have_project_with_access_role(project.name, 'Owner')
|
||||
end
|
||||
|
||||
issue.visit!
|
||||
|
||||
Page::Project::Issue::Show.perform(&:delete_issue)
|
||||
|
||||
Page::Project::Issue::Index.perform do |index|
|
||||
expect(index).not_to have_issue(issue)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'when user is added as maintainer' do |testcase|
|
||||
let!(:issue) do
|
||||
Resource::Issue.fabricate_via_api! do |issue|
|
||||
issue.api_client = owner_api_client
|
||||
issue.project = project
|
||||
issue.title = 'Test Maintainer Deletes Issue'
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
project.add_member(maintainer, Resource::Members::AccessLevel::MAINTAINER)
|
||||
Flow::Login.sign_in(as: maintainer)
|
||||
end
|
||||
|
||||
it "has maintainer role without owner permissions", testcase: testcase do
|
||||
Page::Dashboard::Projects.perform do |projects|
|
||||
projects.filter_by_name(project.name)
|
||||
|
||||
expect(projects).to have_project_with_access_role(project.name, 'Maintainer')
|
||||
end
|
||||
|
||||
issue.visit!
|
||||
|
||||
Page::Project::Issue::Show.perform do |issue|
|
||||
expect(issue).not_to have_delete_issue_button
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'for personal projects' do
|
||||
let!(:project) do
|
||||
Resource::Project.fabricate_via_api! do |project|
|
||||
project.api_client = owner_api_client
|
||||
project.name = 'qa-owner-personal-project'
|
||||
project.personal_namespace = owner.username
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'when user is added as owner', :personal_project, 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/352542'
|
||||
it_behaves_like 'when user is added as maintainer', 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/352607'
|
||||
end
|
||||
|
||||
context 'for group projects' do
|
||||
let!(:group) { Resource::Group.fabricate_via_api! }
|
||||
|
||||
let!(:project) do
|
||||
Resource::Project.fabricate_via_api! do |project|
|
||||
project.group = group
|
||||
project.name = 'qa-owner-group-project'
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'when user is added as owner', :group_project, 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/366436'
|
||||
it_behaves_like 'when user is added as maintainer', 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/366435'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -41,7 +41,6 @@ module Glfm
|
|||
|
||||
h1_regex = /\A# / # new logic compared to original Python code
|
||||
h2_regex = /\A## / # new logic compared to original Python code
|
||||
h3_regex = /\A### / # new logic compared to original Python code
|
||||
header_regex = /\A#+ / # Added beginning of line anchor to original Python code
|
||||
|
||||
spec_txt_lines.each do |line|
|
||||
|
@ -103,19 +102,12 @@ module Glfm
|
|||
# reset the headers array if we found a new H1
|
||||
headers = [] if line =~ h1_regex
|
||||
|
||||
if headers.length == 2 && line =~ h2_regex
|
||||
# pop the last entry from the headers array if we are in an H2 and found a new H2
|
||||
headers.pop
|
||||
elsif headers.length == 3 && line =~ h3_regex
|
||||
# pop the last entry from the headers array if we are in an H3 and found a new H3
|
||||
headers.pop
|
||||
elsif headers.length == 3 && line =~ h2_regex
|
||||
# pop the last two entries from the headers array if we are in an H3 and found a new H2
|
||||
headers.pop(2)
|
||||
end
|
||||
# headers should be size 2 or less [<H1_headertext>, <H2_headertext>]
|
||||
# pop the last entry from the headers array if we are in an H2 and found a new H2
|
||||
headers.pop if headers.length == 2 && line =~ h2_regex
|
||||
|
||||
# push the new header text to the headers array
|
||||
headers << headertext # New logic compared to original Python code
|
||||
headers << headertext if line =~ h1_regex || line =~ h2_regex
|
||||
else
|
||||
# Else if we are in regular text...
|
||||
|
||||
|
|
|
@ -68,27 +68,6 @@ RSpec.describe Projects::ProjectMembersController do
|
|||
end
|
||||
end
|
||||
|
||||
context 'group links' do
|
||||
let_it_be(:project_group_link) { create(:project_group_link, project: project, group: group) }
|
||||
|
||||
it 'lists group links' do
|
||||
get :index, params: { namespace_id: project.namespace, project_id: project }
|
||||
|
||||
expect(assigns(:group_links).map(&:id)).to contain_exactly(project_group_link.id)
|
||||
end
|
||||
|
||||
context 'when `search_groups` param is present' do
|
||||
let(:group_2) { create(:group, :public, name: 'group_2') }
|
||||
let!(:project_group_link_2) { create(:project_group_link, project: project, group: group_2) }
|
||||
|
||||
it 'lists group links that match search' do
|
||||
get :index, params: { namespace_id: project.namespace, project_id: project, search_groups: 'group_2' }
|
||||
|
||||
expect(assigns(:group_links).map(&:id)).to contain_exactly(project_group_link_2.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'invited members' do
|
||||
let_it_be(:invited_member) { create(:project_member, :invited, project: project) }
|
||||
|
||||
|
|
|
@ -298,6 +298,20 @@ RSpec.describe 'New project', :js do
|
|||
end
|
||||
end
|
||||
|
||||
context 'Import project options without any sources', :js do
|
||||
before do
|
||||
stub_application_setting(import_sources: [])
|
||||
|
||||
visit new_project_path
|
||||
click_link 'Import project'
|
||||
end
|
||||
|
||||
it 'displays the no import options message' do
|
||||
expect(page).to have_text s_('ProjectsNew|No import options available')
|
||||
expect(page).to have_text s_('ProjectsNew|Contact an administrator to enable options for importing your project.')
|
||||
end
|
||||
end
|
||||
|
||||
context 'Import project options', :js do
|
||||
before do
|
||||
visit new_project_path
|
||||
|
|
|
@ -14,7 +14,6 @@ RSpec.describe Projects::ProjectMembersHelper do
|
|||
|
||||
describe 'project members' do
|
||||
let_it_be(:members) { create_list(:project_member, 2, project: project) }
|
||||
let_it_be(:group_links) { create_list(:project_group_link, 1, project: project) }
|
||||
let_it_be(:invited) { create_list(:project_member, 2, :invited, project: project) }
|
||||
let_it_be(:access_requests) { create_list(:project_member, 2, :access_request, project: project) }
|
||||
|
||||
|
@ -26,9 +25,10 @@ RSpec.describe Projects::ProjectMembersHelper do
|
|||
helper.project_members_app_data_json(
|
||||
project,
|
||||
members: present_members(members_collection),
|
||||
group_links: group_links,
|
||||
invited: present_members(invited),
|
||||
access_requests: present_members(access_requests)
|
||||
access_requests: present_members(access_requests),
|
||||
include_relations: [:inherited, :direct],
|
||||
search: nil
|
||||
)
|
||||
)
|
||||
end
|
||||
|
@ -84,6 +84,70 @@ RSpec.describe Projects::ProjectMembersHelper do
|
|||
expect(subject['user']['pagination']).to match(expected)
|
||||
end
|
||||
end
|
||||
|
||||
context 'group links' do
|
||||
let_it_be(:shared_with_group) { create(:group) }
|
||||
let_it_be(:group_link) { create(:project_group_link, project: project, group: shared_with_group) }
|
||||
|
||||
before do
|
||||
allow(helper).to receive(:project_group_link_path).with(project, ':id').and_return('/foo-group/foo-project/-/group_links/:id')
|
||||
end
|
||||
|
||||
it 'sets `group.members` property that matches json schema' do
|
||||
expect(subject['group']['members'].to_json).to match_schema('group_link/project_group_links')
|
||||
end
|
||||
|
||||
it 'sets `member_path` property' do
|
||||
expect(subject['group']['member_path']).to eq('/foo-group/foo-project/-/group_links/:id')
|
||||
end
|
||||
|
||||
context 'inherited' do
|
||||
let_it_be(:shared_with_group_1) { create(:group) }
|
||||
let_it_be(:shared_with_group_2) { create(:group) }
|
||||
let_it_be(:shared_with_group_3) { create(:group) }
|
||||
let_it_be(:shared_with_group_4) { create(:group) }
|
||||
let_it_be(:shared_with_group_5) { create(:group) }
|
||||
let_it_be(:top_group) { create(:group) }
|
||||
let_it_be(:sub_group) { create(:group, parent: top_group) }
|
||||
let_it_be(:project) { create(:project, group: sub_group) }
|
||||
let_it_be(:group_link_1) { create(:group_group_link, shared_group: top_group, shared_with_group: shared_with_group_1, group_access: Gitlab::Access::GUEST) }
|
||||
let_it_be(:group_link_2) { create(:group_group_link, shared_group: top_group, shared_with_group: shared_with_group_4, group_access: Gitlab::Access::GUEST) }
|
||||
let_it_be(:group_link_3) { create(:group_group_link, shared_group: top_group, shared_with_group: shared_with_group_5, group_access: Gitlab::Access::DEVELOPER) }
|
||||
let_it_be(:group_link_4) { create(:group_group_link, shared_group: sub_group, shared_with_group: shared_with_group_2, group_access: Gitlab::Access::DEVELOPER) }
|
||||
let_it_be(:group_link_5) { create(:group_group_link, shared_group: sub_group, shared_with_group: shared_with_group_4, group_access: Gitlab::Access::DEVELOPER) }
|
||||
let_it_be(:group_link_6) { create(:group_group_link, shared_group: sub_group, shared_with_group: shared_with_group_5, group_access: Gitlab::Access::GUEST) }
|
||||
let_it_be(:group_link_7) { create(:project_group_link, project: project, group: shared_with_group_1, group_access: Gitlab::Access::DEVELOPER) }
|
||||
let_it_be(:group_link_8) { create(:project_group_link, project: project, group: shared_with_group_2, group_access: Gitlab::Access::GUEST) }
|
||||
let_it_be(:group_link_9) { create(:project_group_link, project: project, group: shared_with_group_3, group_access: Gitlab::Access::REPORTER) }
|
||||
|
||||
subject do
|
||||
Gitlab::Json.parse(
|
||||
helper.project_members_app_data_json(
|
||||
project,
|
||||
members: present_members(members_collection),
|
||||
invited: present_members(invited),
|
||||
access_requests: present_members(access_requests),
|
||||
include_relations: include_relations,
|
||||
search: nil
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
where(:include_relations, :result) do
|
||||
[:inherited, :direct] | lazy { [group_link_7, group_link_4, group_link_9, group_link_5, group_link_3].map(&:id) }
|
||||
[:inherited] | lazy { [group_link_1, group_link_4, group_link_5, group_link_3].map(&:id) }
|
||||
[:direct] | lazy { [group_link_7, group_link_8, group_link_9].map(&:id) }
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'returns correct group links' do
|
||||
expect(subject['group']['members'].map { |link| link['id'] }).to match_array(result)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'setting ActiveSupport::Digest.hash_digest_class' do
|
||||
it 'sets overrides config.active_support.hash_digest_class' do
|
||||
expect(ActiveSupport::Digest.hash_digest_class).to eq(Gitlab::HashDigest::Facade)
|
||||
end
|
||||
end
|
|
@ -71,6 +71,13 @@ RSpec.describe Gitlab::Ci::Tags::BulkInsert do
|
|||
expect(Ci::Build.tagged_with('tag3')).to include(other_job)
|
||||
end
|
||||
|
||||
it 'strips tags' do
|
||||
job.tag_list = [' taga', 'tagb ', ' tagc ']
|
||||
|
||||
service.insert!
|
||||
expect(job.tags.map(&:name)).to match_array(%w[taga tagb tagc])
|
||||
end
|
||||
|
||||
context 'when batching inserts for tags' do
|
||||
before do
|
||||
stub_const("#{described_class}::TAGS_BATCH_SIZE", 2)
|
||||
|
|
|
@ -220,10 +220,11 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do
|
|||
expect(policy.directives['base-uri']).to be_nil
|
||||
end
|
||||
|
||||
it 'returns default values for directives not defined by the user' do
|
||||
it 'returns default values for directives not defined by the user or with <default_value> and disables directives set to false' do
|
||||
# Explicitly disabling script_src and setting report_uri
|
||||
csp_config[:directives] = {
|
||||
script_src: false,
|
||||
style_src: '<default_value>',
|
||||
report_uri: 'https://example.org'
|
||||
}
|
||||
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::HashDigest::Facade do
|
||||
describe '.hexdigest' do
|
||||
let(:plaintext) { 'something that is plaintext' }
|
||||
|
||||
let(:sha256_hash) { OpenSSL::Digest::SHA256.hexdigest(plaintext) }
|
||||
let(:md5_hash) { Digest::MD5.hexdigest(plaintext) } # rubocop:disable Fips/MD5
|
||||
|
||||
it 'uses SHA256' do
|
||||
expect(described_class.hexdigest(plaintext)).to eq(sha256_hash)
|
||||
end
|
||||
|
||||
context 'when feature flags is not available' do
|
||||
before do
|
||||
allow(Feature).to receive(:feature_flags_available?).and_return(false)
|
||||
end
|
||||
|
||||
it 'uses MD5' do
|
||||
expect(described_class.hexdigest(plaintext)).to eq(md5_hash)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when active_support_hash_digest_sha256 FF is disabled' do
|
||||
before do
|
||||
stub_feature_flags(active_support_hash_digest_sha256: false)
|
||||
end
|
||||
|
||||
it 'uses MD5' do
|
||||
expect(described_class.hexdigest(plaintext)).to eq(md5_hash)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2073,6 +2073,13 @@ RSpec.describe Ci::Build do
|
|||
expect(build.tags.first.name).to eq('tag')
|
||||
end
|
||||
|
||||
it 'strips tags' do
|
||||
build.tag_list = [' taga', 'tagb ', ' tagc ']
|
||||
|
||||
build.save!
|
||||
expect(build.tags.map(&:name)).to match_array(%w[taga tagb tagc])
|
||||
end
|
||||
|
||||
context 'with BulkInsertableTags.with_bulk_insert_tags' do
|
||||
it 'does not save_tags' do
|
||||
Ci::BulkInsertableTags.with_bulk_insert_tags do
|
||||
|
|
|
@ -1190,6 +1190,13 @@ RSpec.describe Ci::Runner do
|
|||
expect(runner.tags.first.name).to eq('tag')
|
||||
end
|
||||
|
||||
it 'strips tags' do
|
||||
runner.tag_list = [' taga', 'tagb ', ' tagc ']
|
||||
|
||||
runner.save!
|
||||
expect(runner.tags.map(&:name)).to match_array(%w[taga tagb tagc])
|
||||
end
|
||||
|
||||
context 'with BulkInsertableTags.with_bulk_insert_tags' do
|
||||
it 'does not save_tags' do
|
||||
Ci::BulkInsertableTags.with_bulk_insert_tags do
|
||||
|
|
|
@ -8390,6 +8390,27 @@ RSpec.describe Project, factory_default: :keep do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#group_group_links' do
|
||||
context 'with group project' do
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:project) { create(:project, group: group) }
|
||||
|
||||
it 'returns group links of group' do
|
||||
expect(group).to receive_message_chain(:shared_with_group_links, :of_ancestors_and_self)
|
||||
|
||||
project.group_group_links
|
||||
end
|
||||
end
|
||||
|
||||
context 'with personal project' do
|
||||
let_it_be(:project) { create(:project) }
|
||||
|
||||
it 'returns none' do
|
||||
expect(project.group_group_links).to eq(GroupGroupLink.none)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#security_training_available?' do
|
||||
subject { build(:project) }
|
||||
|
||||
|
|
|
@ -197,7 +197,7 @@ RSpec.describe API::PypiPackages do
|
|||
let(:url) { "/projects/#{project.id}/packages/pypi" }
|
||||
let(:headers) { {} }
|
||||
let(:requires_python) { '>=3.7' }
|
||||
let(:base_params) { { requires_python: requires_python, version: '1.0.0', name: 'sample-project', sha256_digest: '1' * 64 } }
|
||||
let(:base_params) { { requires_python: requires_python, version: '1.0.0', name: 'sample-project', sha256_digest: '1' * 64, md5_digest: '1' * 32 } }
|
||||
let(:params) { base_params.merge(content: temp_file(file_name)) }
|
||||
let(:send_rewritten_field) { true }
|
||||
let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user } }
|
||||
|
@ -254,6 +254,19 @@ RSpec.describe API::PypiPackages do
|
|||
|
||||
it_behaves_like 'PyPI package creation', :developer, :created, true
|
||||
end
|
||||
|
||||
context 'without md5_digest' do
|
||||
let(:token) { personal_access_token.token }
|
||||
let(:user_headers) { basic_auth_header(user.username, token) }
|
||||
let(:headers) { user_headers.merge(workhorse_headers) }
|
||||
let(:params) { base_params.merge(content: temp_file(file_name)) }
|
||||
|
||||
before do
|
||||
params.delete(:md5_digest)
|
||||
end
|
||||
|
||||
it_behaves_like 'PyPI package creation', :developer, :created, true, false
|
||||
end
|
||||
end
|
||||
|
||||
context 'with required_python too big' do
|
||||
|
|
|
@ -42,6 +42,21 @@ RSpec.describe Packages::Pypi::CreatePackageService, :aggregate_failures do
|
|||
end
|
||||
end
|
||||
|
||||
context 'with FIPS mode', :fips_mode do
|
||||
it 'does not generate file_md5' do
|
||||
expect { subject }.to change { Packages::Package.pypi.count }.by(1)
|
||||
|
||||
expect(created_package.name).to eq 'foo'
|
||||
expect(created_package.version).to eq '1.0'
|
||||
|
||||
expect(created_package.pypi_metadatum.required_python).to eq '>=2.7'
|
||||
expect(created_package.package_files.size).to eq 1
|
||||
expect(created_package.package_files.first.file_name).to eq 'foo.tgz'
|
||||
expect(created_package.package_files.first.file_sha256).to eq sha256
|
||||
expect(created_package.package_files.first.file_md5).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'without required_python' do
|
||||
before do
|
||||
params.delete(:requires_python)
|
||||
|
|
|
@ -49,7 +49,7 @@ RSpec.describe Projects::CreateFromTemplateService do
|
|||
end
|
||||
|
||||
it 'is not scheduled' do
|
||||
expect(project.import_scheduled?).to be_nil
|
||||
expect(project.import_scheduled?).to be(false)
|
||||
end
|
||||
|
||||
it 'repository is empty' do
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_examples 'PyPI package creation' do |user_type, status, add_member = true|
|
||||
RSpec.shared_examples 'PyPI package creation' do |user_type, status, add_member = true, md5_digest = true|
|
||||
RSpec.shared_examples 'creating pypi package files' do
|
||||
it 'creates package files' do
|
||||
expect { subject }
|
||||
|
@ -14,6 +14,17 @@ RSpec.shared_examples 'PyPI package creation' do |user_type, status, add_member
|
|||
expect(package.name).to eq params[:name]
|
||||
expect(package.version).to eq params[:version]
|
||||
expect(package.pypi_metadatum.required_python).to eq params[:requires_python]
|
||||
|
||||
if md5_digest
|
||||
expect(package.package_files.first.file_md5).not_to be_nil
|
||||
else
|
||||
expect(package.package_files.first.file_md5).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'with FIPS mode', :fips_mode do
|
||||
it_behaves_like 'returning response status', :unprocessable_entity if md5_digest
|
||||
it_behaves_like 'returning response status', status unless md5_digest
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue