Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
b7b4bb751c
commit
b1016dffb4
52
CHANGELOG.md
52
CHANGELOG.md
|
|
@ -2,6 +2,25 @@
|
|||
documentation](doc/development/changelog.md) for instructions on adding your own
|
||||
entry.
|
||||
|
||||
## 17.1.2 (2024-07-09)
|
||||
|
||||
### Fixed (2 changes)
|
||||
|
||||
- [Update dependency slack-messenger to v2.3.5](gitlab-org/security/gitlab@e21a4599ab21cc6f6b89ca432d9557ed72169c85)
|
||||
- [Fix MailRoom not loading in Omnibus](gitlab-org/security/gitlab@5547c1164c494705bf29595062124ff8cb266cb3)
|
||||
|
||||
### Security (9 changes)
|
||||
|
||||
- [Disallow serving Pages over disabled custom domains with deployments](gitlab-org/security/gitlab@176442d616a111667481f22186560925d1175c67) ([merge request](gitlab-org/security/gitlab!4245))
|
||||
- [Check npm package name, version and scripts coherence](gitlab-org/security/gitlab@917d805ce57e5d0439b4a4c757967d494014a97d) ([merge request](gitlab-org/security/gitlab!4212))
|
||||
- [Check for create_deploy_token policy before creating deploy token](gitlab-org/security/gitlab@8ae4e9b0b25bec92561698da3c7d0495d6ba61bc) ([merge request](gitlab-org/security/gitlab!4209))
|
||||
- [Check if user has ban_group_member access before banning in namespace](gitlab-org/security/gitlab@eefb608987d64b9cf58411b8520f260d1fb9b1c3) ([merge request](gitlab-org/security/gitlab!4091))
|
||||
- [Prevent privilege escalation via custom role](gitlab-org/security/gitlab@a618e86dc4585b0fef049f75f13acf0eec00656d) ([merge request](gitlab-org/security/gitlab!4199))
|
||||
- [Prevent using quick actions for some bot users](gitlab-org/security/gitlab@5789cc333d04d76ffb4c79239e71be1910f12229) ([merge request](gitlab-org/security/gitlab!4231))
|
||||
- [Disable raw HTML for quick action pipeline](gitlab-org/security/gitlab@7db9b002b803cb6b53a3e6ce3f8d9b15107e7464) ([merge request](gitlab-org/security/gitlab!4235))
|
||||
- [Disable quick actions unless description changed](gitlab-org/security/gitlab@a1800c591b38df0e2d143df3ee56f76b4f3a914f) ([merge request](gitlab-org/security/gitlab!4234))
|
||||
- [Remove comment support from shrug and tableflip](gitlab-org/security/gitlab@fb6bcef1935dc3a7dd60def448a652769c86ee62) ([merge request](gitlab-org/security/gitlab!4229))
|
||||
|
||||
## 17.1.1 (2024-06-25)
|
||||
|
||||
### Fixed (1 change)
|
||||
|
|
@ -1125,6 +1144,24 @@ entry.
|
|||
|
||||
- [Update Web IDE dependency to receive duo fixes](gitlab-org/gitlab@47323c05565dd32ea4de9f999adbd9f7aa8748e3) ([merge request](gitlab-org/gitlab!154064))
|
||||
|
||||
## 17.0.4 (2024-07-09)
|
||||
|
||||
### Fixed (1 change)
|
||||
|
||||
- [Update dependency slack-messenger to v2.3.5](gitlab-org/security/gitlab@8800f576925de4fcf00d46f707a70e1e81e5f00a)
|
||||
|
||||
### Security (9 changes)
|
||||
|
||||
- [Disallow serving Pages over disabled custom domains with deployments](gitlab-org/security/gitlab@760d6115e963e744ee55230be45e9fc3c138a73d) ([merge request](gitlab-org/security/gitlab!4248))
|
||||
- [Check npm package name, version and scripts coherence](gitlab-org/security/gitlab@f3b322c5e20036e380ac15b3b2614d9bcc800e75) ([merge request](gitlab-org/security/gitlab!4158))
|
||||
- [Check for create_deploy_token policy before creating deploy token](gitlab-org/security/gitlab@87ab0448c48a0ac45540986f4f7429cbc0db3e04) ([merge request](gitlab-org/security/gitlab!4168))
|
||||
- [Check if user has ban_group_member access before banning in namespace](gitlab-org/security/gitlab@1b69ccb4a7e7e16372a926d7e2954aec76cdc0fd) ([merge request](gitlab-org/security/gitlab!4087))
|
||||
- [Prevent privilege escalation via custom role](gitlab-org/security/gitlab@288e1493a3c16689993d7cbbd7f60cd9bdeffcc0) ([merge request](gitlab-org/security/gitlab!4198))
|
||||
- [Prevent using quick actions for some bot users](gitlab-org/security/gitlab@5b8aef69fc74e5aba42d65591902a151a1654316) ([merge request](gitlab-org/security/gitlab!4232))
|
||||
- [Disable raw HTML for quick action pipeline](gitlab-org/security/gitlab@0ca2b123f1f627e68b73a1699fbefbb4a70c28d1) ([merge request](gitlab-org/security/gitlab!4236))
|
||||
- [Disable quick actions unless description changed](gitlab-org/security/gitlab@9e5397fbd82497083cd69f526a13caea1b5efd21) ([merge request](gitlab-org/security/gitlab!4239))
|
||||
- [Remove comment support from shrug and tableflip](gitlab-org/security/gitlab@e378c24c4a1a8dd323bd157bce29b21cca1e0701) ([merge request](gitlab-org/security/gitlab!4228))
|
||||
|
||||
## 17.0.3 (2024-06-25)
|
||||
|
||||
### Fixed (5 changes)
|
||||
|
|
@ -2051,6 +2088,21 @@ entry.
|
|||
- [Migrate self-managed custom roles to the instance-level roles](gitlab-org/gitlab@46ab664a1877f8b761c2b25e13e01561d56cf6fd) ([merge request](gitlab-org/gitlab!147829))
|
||||
- [Feature cleanup flag wiki_content_background_job](gitlab-org/gitlab@c39a37db4a6112456052c11bf5fd1afa9c23bd6d) by @ivantedja ([merge request](gitlab-org/gitlab!148820))
|
||||
|
||||
## 16.11.6 (2024-07-09)
|
||||
|
||||
### Fixed (1 change)
|
||||
|
||||
- [Update dependency slack-messenger to v2.3.5](gitlab-org/security/gitlab@4e78461cef64c7423d793de22deffbae7088e7d6)
|
||||
|
||||
### Security (6 changes)
|
||||
|
||||
- [Disallow serving Pages over disabled custom domains with deployments](gitlab-org/security/gitlab@ff23e03cab7495107b1342b4fa175db63a4acd61) ([merge request](gitlab-org/security/gitlab!4247))
|
||||
- [Check if user has ban_group_member access before banning in namespace](gitlab-org/security/gitlab@cadb2dba7f5fe825fab7fe761259e7c1721bebfe) ([merge request](gitlab-org/security/gitlab!4090))
|
||||
- [Prevent using quick actions for some bot users](gitlab-org/security/gitlab@951a656e6d530ba7692b03506b7c340bc5ac2788) ([merge request](gitlab-org/security/gitlab!4233))
|
||||
- [Disable raw HTML for quick action pipeline](gitlab-org/security/gitlab@1a7f336059af3223b4886e79060b8dc8a17f5482) ([merge request](gitlab-org/security/gitlab!4237))
|
||||
- [Disable quick actions unless description changed](gitlab-org/security/gitlab@267f4cf51303f70d5a834a3358fe62b6e981a873) ([merge request](gitlab-org/security/gitlab!4240))
|
||||
- [Remove comment support from shrug and tableflip](gitlab-org/security/gitlab@579a180538609ccad2d3930218a5410cb33d3920) ([merge request](gitlab-org/security/gitlab!4230))
|
||||
|
||||
## 16.11.5 (2024-06-25)
|
||||
|
||||
### Fixed (2 changes)
|
||||
|
|
|
|||
2
Gemfile
2
Gemfile
|
|
@ -388,6 +388,8 @@ gem 'gettext', '~> 3.4', '>= 3.4.9',
|
|||
|
||||
gem 'batch-loader', '~> 2.0.5' # rubocop:todo Gemfile/MissingFeatureCategory
|
||||
|
||||
gem 'tty-prompt', '~> 0.23', require: false, feature_category: :shared
|
||||
|
||||
# Perf bar
|
||||
gem 'peek', '~> 1.1' # rubocop:todo Gemfile/MissingFeatureCategory
|
||||
|
||||
|
|
|
|||
|
|
@ -2262,6 +2262,7 @@ DEPENDENCIES
|
|||
timfel-krb5-auth (~> 0.8)
|
||||
toml-rb (~> 2.2.0)
|
||||
truncato (~> 0.7.12)
|
||||
tty-prompt (~> 0.23)
|
||||
typhoeus (~> 1.4.0)
|
||||
undercover (~> 0.4.4)
|
||||
unleash (~> 3.2.2)
|
||||
|
|
|
|||
|
|
@ -185,7 +185,10 @@ export default {
|
|||
this.submitForm();
|
||||
},
|
||||
setPasswordComplexity({ name, value }) {
|
||||
this.$set(this.form, name, value);
|
||||
this.form = {
|
||||
...this.form,
|
||||
[name]: value,
|
||||
};
|
||||
},
|
||||
submitForm() {
|
||||
this.$refs.form.submit();
|
||||
|
|
|
|||
|
|
@ -15,6 +15,10 @@ class Packages::Npm::Metadatum < ApplicationRecord
|
|||
|
||||
scope :package_id_in, ->(package_ids) { where(package_id: package_ids) }
|
||||
|
||||
def package_json_scripts
|
||||
package_json.try(:[], 'scripts')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ensure_npm_package_type
|
||||
|
|
|
|||
|
|
@ -26,6 +26,23 @@ class GlobalPolicy < BasePolicy
|
|||
|
||||
condition(:service_account, scope: :user) { @user&.service_account? }
|
||||
|
||||
condition(:bot, scope: :user) { @user&.bot? }
|
||||
|
||||
# By default bots should not be allowed to use quick actions as they have too many permissions and it can lead to
|
||||
# surprises. We should scope down this allowlist over time as we confirm if these bots actually need to use quick
|
||||
# actions.
|
||||
condition(:bot_with_quick_actions_permitted) do
|
||||
@user.alert_bot? ||
|
||||
@user.project_bot? ||
|
||||
@user.support_bot? ||
|
||||
@user.admin_bot? ||
|
||||
@user.service_account?
|
||||
end
|
||||
|
||||
rule { bot & ~bot_with_quick_actions_permitted }.policy do
|
||||
prevent :use_quick_actions
|
||||
end
|
||||
|
||||
rule { anonymous }.policy do
|
||||
prevent :log_in
|
||||
prevent :receive_notifications
|
||||
|
|
|
|||
|
|
@ -60,18 +60,16 @@ module WorkItems
|
|||
def interpret_quick_actions!(work_item, widget_params, attributes = {})
|
||||
return unless work_item.has_widget?(:description)
|
||||
|
||||
description_param = widget_params[::WorkItems::Widgets::Description.api_symbol]
|
||||
return unless description_param
|
||||
widget_description_param = widget_params[::WorkItems::Widgets::Description.api_symbol]
|
||||
return unless widget_description_param
|
||||
|
||||
original_description = description_param.fetch(:description, work_item.description)
|
||||
merge_quick_actions_into_params!(work_item, params: widget_description_param)
|
||||
|
||||
description, command_params = QuickActions::InterpretService
|
||||
.new(container: work_item.resource_parent, current_user: current_user)
|
||||
.execute(original_description, work_item)
|
||||
# cleanup `description` param so that it is not passed into common params after transform_quick_action_params
|
||||
quick_action_params = widget_description_param.dup
|
||||
quick_action_params.delete(:description)
|
||||
|
||||
description_param[:description] = description if description && description != original_description
|
||||
|
||||
parsed_params = work_item.transform_quick_action_params(command_params)
|
||||
parsed_params = work_item.transform_quick_action_params(quick_action_params)
|
||||
|
||||
widget_params.merge!(parsed_params[:widgets])
|
||||
attributes.merge!(parsed_params[:common])
|
||||
|
|
|
|||
|
|
@ -209,28 +209,50 @@ class IssuableBaseService < ::BaseContainerService
|
|||
end
|
||||
|
||||
def handle_quick_actions(issuable)
|
||||
merge_quick_actions_into_params!(issuable)
|
||||
merge_quick_actions_into_params!(issuable, params: params)
|
||||
end
|
||||
|
||||
def merge_quick_actions_into_params!(issuable, only: nil)
|
||||
original_description = params.fetch(:description, issuable.description)
|
||||
# Notes: When the description has been edited, then we need to sanitize and compare with
|
||||
# the original description, removing any extra quick actions.
|
||||
# If the description has not been edited, then just remove any quick actions
|
||||
# in the current description.
|
||||
def merge_quick_actions_into_params!(issuable, params:, only: nil)
|
||||
interpret_params = quick_action_options
|
||||
unedited_description = issuable.description
|
||||
edited_description = params.fetch(:description, issuable.description)
|
||||
|
||||
description, command_params = QuickActions::InterpretService.new(
|
||||
container: container,
|
||||
current_user: current_user,
|
||||
params: quick_action_options
|
||||
).execute(original_description, issuable, only: only)
|
||||
target_text = issuable.new_record? || params[:description] ? edited_description : unedited_description
|
||||
|
||||
# only set the original_text if we're editing the issuable
|
||||
original_text = params[:description] && !issuable.new_record? ? unedited_description : nil
|
||||
|
||||
sanitized_description, sanitized_command_params = interpret_quick_actions(target_text, issuable, params: interpret_params, only: only, original_text: original_text)
|
||||
|
||||
unless issuable.new_record? || params[:description]
|
||||
edited_description = unedited_description
|
||||
sanitized_command_params = nil
|
||||
end
|
||||
|
||||
# Avoid a description already set on an issuable to be overwritten by a nil
|
||||
params[:description] = description if description && description != original_description
|
||||
params[:description] = sanitized_description if sanitized_description && sanitized_description != edited_description
|
||||
|
||||
params.merge!(command_params)
|
||||
params.merge!(sanitized_command_params) if sanitized_command_params
|
||||
end
|
||||
|
||||
def quick_action_options
|
||||
{}
|
||||
end
|
||||
|
||||
def interpret_quick_actions(new_text, issuable, params:, only:, original_text: nil)
|
||||
sanitized_new_text, new_command_params = QuickActions::InterpretService.new(
|
||||
container: container,
|
||||
current_user: current_user,
|
||||
params: params
|
||||
).execute_with_original_text(new_text, issuable, only: only, original_text: original_text)
|
||||
|
||||
[sanitized_new_text, new_command_params]
|
||||
end
|
||||
|
||||
def create(issuable, skip_system_notes: false)
|
||||
initialize_callbacks!(issuable)
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ module MergeRequests
|
|||
self.merge_request = MergeRequest.new
|
||||
# TODO: this should handle all quick actions that don't have side effects
|
||||
# https://gitlab.com/gitlab-org/gitlab-foss/issues/53658
|
||||
merge_quick_actions_into_params!(merge_request, only: [:target_branch])
|
||||
merge_quick_actions_into_params!(merge_request, params: params, only: [:target_branch])
|
||||
|
||||
# Assign the projects first so we can use policies for `filter_params`
|
||||
merge_request.author = current_user
|
||||
|
|
|
|||
|
|
@ -147,6 +147,7 @@ module MergeRequests
|
|||
draft: push_options[:draft],
|
||||
target_branch: push_options[:target],
|
||||
force_remove_source_branch: push_options[:remove_source_branch],
|
||||
squash: push_options[:squash],
|
||||
label: push_options[:label],
|
||||
unlabel: push_options[:unlabel],
|
||||
assign: push_options[:assign],
|
||||
|
|
|
|||
|
|
@ -40,7 +40,10 @@ module Notes
|
|||
params: options
|
||||
)
|
||||
|
||||
interpret_service.execute(note.note, note.noteable)
|
||||
# NOTE: old_note would be nil if the note hasn't changed or it is a new record
|
||||
old_note, _ = note.note_change
|
||||
|
||||
interpret_service.execute_with_original_text(note.note, note.noteable, original_text: old_note)
|
||||
end
|
||||
|
||||
# Applies updates extracted to note#noteable
|
||||
|
|
|
|||
|
|
@ -0,0 +1,94 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Packages
|
||||
module Npm
|
||||
class CheckManifestCoherenceService
|
||||
MismatchError = Class.new(StandardError)
|
||||
|
||||
delegate :npm_metadatum, to: :package, private: true
|
||||
delegate :package_json_scripts, to: :npm_metadatum, private: true, allow_nil: true
|
||||
|
||||
def initialize(package, package_json_entry)
|
||||
@package = package
|
||||
@package_json_entry = package_json_entry
|
||||
@handler = SajHandler.new
|
||||
end
|
||||
|
||||
def execute
|
||||
extract_manifest_data
|
||||
raise MismatchError, 'Package manifest is not coherent' unless coherent?
|
||||
|
||||
ServiceResponse.success
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :package, :package_json_entry, :handler
|
||||
|
||||
def extract_manifest_data
|
||||
::Oj.saj_parse(handler, package_json_entry)
|
||||
rescue SajHandler::ParsingDoneError
|
||||
# no-op
|
||||
# this simply signals that the handler has been stopped
|
||||
# and that's ok.
|
||||
end
|
||||
|
||||
def coherent?
|
||||
handler.name == package.name &&
|
||||
handler.version == package.version &&
|
||||
handler.scripts == (package_json_scripts || {})
|
||||
end
|
||||
|
||||
class SajHandler < ::Oj::Saj
|
||||
ParsingDoneError = Class.new(StandardError)
|
||||
|
||||
attr_reader :name, :version, :scripts
|
||||
|
||||
def initialize
|
||||
@in_scripts_block = false
|
||||
@scripts_processed = false
|
||||
|
||||
@scripts = {}
|
||||
@name = nil
|
||||
@version = nil
|
||||
end
|
||||
|
||||
def hash_start(key)
|
||||
return unless key == 'scripts'
|
||||
|
||||
self.in_scripts_block = true
|
||||
end
|
||||
|
||||
def hash_end(key)
|
||||
return unless key == 'scripts'
|
||||
|
||||
self.in_scripts_block = false
|
||||
self.scripts_processed = true
|
||||
|
||||
raise ParsingDoneError if complete?
|
||||
end
|
||||
|
||||
def add_value(value, key)
|
||||
if in_scripts_block
|
||||
scripts[key] = value
|
||||
elsif key == 'name'
|
||||
self.name = value
|
||||
elsif key == 'version'
|
||||
self.version = value
|
||||
end
|
||||
|
||||
raise ParsingDoneError if complete?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_writer :name, :version
|
||||
attr_accessor :in_scripts_block, :scripts_processed
|
||||
|
||||
def complete?
|
||||
scripts_processed && name && version
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -19,6 +19,8 @@ module Packages
|
|||
with_package_json_entry do |entry|
|
||||
raise ExtractionError, 'package.json not found' unless entry
|
||||
raise ExtractionError, 'package.json file too large' if entry.size > MAX_FILE_SIZE
|
||||
|
||||
::Packages::Npm::CheckManifestCoherenceService.new(package, entry).execute
|
||||
end
|
||||
|
||||
package.default!
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@ module QuickActions
|
|||
end.compact
|
||||
end
|
||||
|
||||
# IMPORTANT: unsafe! Use `execute_with_original_text` instead as it handles cleanup of any residual quick actions
|
||||
# left in the original description.
|
||||
#
|
||||
# Takes a text and interprets the commands that are extracted from it.
|
||||
# Returns the content without commands, a hash of changes to be applied to a record
|
||||
# and a string containing the execution_message to show to the user.
|
||||
|
|
@ -48,6 +51,26 @@ module QuickActions
|
|||
[content, @updates, execution_messages_for(commands), command_names(commands)]
|
||||
end
|
||||
|
||||
# Similar to `execute` except also tries to extract any quick actions from original_text,
|
||||
# and if found removes them from the main list of quick actions.
|
||||
def execute_with_original_text(new_text, quick_action_target, only: nil, original_text: nil)
|
||||
sanitized_new_text, new_command_params, execution_messages, command_names = execute(
|
||||
new_text, quick_action_target, only: only
|
||||
)
|
||||
|
||||
if original_text
|
||||
_, original_command_params = self.class.new(
|
||||
container: container,
|
||||
current_user: current_user,
|
||||
params: params
|
||||
).execute(original_text, quick_action_target, only: only)
|
||||
|
||||
new_command_params = (new_command_params.to_a - original_command_params.to_a).to_h if original_command_params
|
||||
end
|
||||
|
||||
[sanitized_new_text, new_command_params, execution_messages, command_names]
|
||||
end
|
||||
|
||||
# Takes a text and interprets the commands that are extracted from it.
|
||||
# Returns the content without commands, and array of changes explained.
|
||||
# `keep_actions: true` will keep the quick actions in the content.
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ module Packages
|
|||
::Packages::Nuget::UpdatePackageFromMetadataService::ZipError,
|
||||
::Packages::Rubygems::ProcessGemService::ExtractionError,
|
||||
::Packages::Rubygems::ProcessGemService::InvalidMetadataError,
|
||||
::Packages::Npm::ProcessPackageFileService::ExtractionError
|
||||
::Packages::Npm::ProcessPackageFileService::ExtractionError,
|
||||
::Packages::Npm::CheckManifestCoherenceService::MismatchError
|
||||
].freeze
|
||||
|
||||
def process_package_file_error(package_file:, exception:, extra_log_payload: {})
|
||||
|
|
|
|||
|
|
@ -355,3 +355,18 @@ these steps:
|
|||
- `deflate` is the default encoding type for PlantUML. To use a different encoding type, PlantUML integration
|
||||
[requires a header prefix in the URL](https://plantuml.com/text-encoding)
|
||||
to distinguish different encoding types.
|
||||
|
||||
## Troubleshooting PlantUML configuration
|
||||
|
||||
### Rendered diagram URL remains the same after update
|
||||
|
||||
Rendered diagrams are cached. To see the updates, try these steps:
|
||||
|
||||
- If the diagram is in a Markdown file, make a small change to the Markdown file, and commit it. This triggers a re-render.
|
||||
- [Clear your GitLab cache](../raketasks/maintenance.md#clear-redis-cache).
|
||||
|
||||
If you're still not seeing the updated URL, check the following:
|
||||
|
||||
- Ensure the PlantUML server is accessible from your GitLab instance.
|
||||
- Verify that the PlantUML integration is enabled in your GitLab settings.
|
||||
- Check the GitLab logs for errors related to PlantUML rendering.
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 18 KiB |
|
|
@ -26,7 +26,7 @@ To access the visibility and access control options:
|
|||
|
||||
## Define which roles can create projects
|
||||
|
||||
Instance-level protections for project creation define which roles can
|
||||
You can add project creation protections to your instance. These protections define which roles can
|
||||
[add projects to a group](../../user/group/index.md#specify-who-can-add-projects-to-a-group)
|
||||
on the instance. To alter which roles have permission to create projects:
|
||||
|
||||
|
|
@ -62,7 +62,7 @@ To restrict project deletion to only administrators:
|
|||
1. Expand **Visibility and access controls**.
|
||||
1. Scroll to:
|
||||
- (GitLab 15.1 and later) **Allowed to delete projects**, and select **Administrators**.
|
||||
- (GitLab 15.0 and earlier) **Default project deletion protection** and select **Only admins can delete project**.
|
||||
- (GitLab 15.0 and earlier) **Default project deletion protection**, and select **Only admins can delete project**.
|
||||
1. Select **Save changes**.
|
||||
|
||||
## Deletion protection
|
||||
|
|
@ -77,21 +77,21 @@ DETAILS:
|
|||
> - [Removed option to delete immediately](https://gitlab.com/gitlab-org/gitlab/-/issues/389557) in GitLab 15.11 [with a flag](../feature_flags.md) named `always_perform_delayed_deletion`. Disabled by default.
|
||||
> - Enabled delayed deletion by default and removed the option to delete immediately [on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/393622) and [on self-managed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/119606) in GitLab 16.0.
|
||||
|
||||
Instance-level protection against accidental deletion of groups and projects.
|
||||
These protections help guard against accidental deletion of groups and projects on your instance.
|
||||
|
||||
### Retention period
|
||||
|
||||
> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/352960) in GitLab 15.1.
|
||||
|
||||
Groups and projects remain restorable during the retention period you define. By default,
|
||||
this is 7 days, but you can change it. If you set the retention period to `0`, GitLab
|
||||
this is 7 days, but you can change it. If you set the retention period to `0` days, GitLab
|
||||
removes deleted groups and projects immediately. You can't restore them.
|
||||
|
||||
In GitLab 15.1 and later, the retention period must be between `1` and `90`.
|
||||
If the retention period was `0` before the 15.1 update, the next time you change
|
||||
In GitLab 15.1 and later, the retention period must be between `1` and `90` days.
|
||||
If, before the 15.1 update, you set the retention period to `0` days, the next time you change
|
||||
any application setting, GitLab:
|
||||
|
||||
- Changes the retention period to `1`.
|
||||
- Changes the retention period to `1` day.
|
||||
- Disables deletion protection.
|
||||
|
||||
### Delayed project deletion
|
||||
|
|
@ -102,23 +102,35 @@ any application setting, GitLab:
|
|||
Prerequisites:
|
||||
|
||||
- You must be an administrator.
|
||||
- You must enable delayed project deletion for groups before you can enable it for projects.
|
||||
Deletion protection is not available for projects only.
|
||||
- When disabled, GitLab 15.1 and later enforces this delayed-deletion setting, and you can't override it.
|
||||
|
||||
To configure delayed project deletion:
|
||||
|
||||
::Tabs
|
||||
|
||||
:::TabTitle GitLab 16.0 and later
|
||||
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Settings > General**.
|
||||
1. Expand **Visibility and access controls**.
|
||||
1. Scroll to **Deletion protection** and set the retention period to a value between `1` and `90` days.
|
||||
1. Select **Save changes**.
|
||||
|
||||
:::TabTitle GitLab 15.11 and earlier
|
||||
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Settings > General**.
|
||||
1. Expand **Visibility and access controls**.
|
||||
1. Scroll to:
|
||||
- In GitLab 16.0 and later: **Deletion protection** and set the retention period to a value between `1` and `90`.
|
||||
- In GitLab 15.11 with `always_perform_delayed_deletion` feature flag enabled: **Deletion protection** and set the retention period to a value between `1` and `90`.
|
||||
- In GitLab 15.11 with `always_perform_delayed_deletion` feature flag enabled: **Deletion protection** and set the retention period to a value between `1` and `90` days.
|
||||
- In GitLab 15.1 to 15.10: **Deletion protection** and select **Keep deleted groups and projects**, then set the retention period.
|
||||
- In GitLab 15.0 and earlier: **Default delayed project protection** and select **Enable delayed project deletion by
|
||||
default for newly-created groups**, then set the retention period.
|
||||
1. Select **Save changes**.
|
||||
|
||||
Deletion protection is not available for projects only (without being also being enabled for groups).
|
||||
|
||||
GitLab 15.1 and later enforces this setting on groups when disabled. You can't override this setting.
|
||||
::EndTabs
|
||||
|
||||
### Delayed group deletion
|
||||
|
||||
|
|
@ -127,11 +139,16 @@ GitLab 15.1 and later enforces this setting on groups when disabled. You can't o
|
|||
|
||||
Groups remain restorable if the retention period is `1` or more days.
|
||||
|
||||
In GitLab 15.1 and later, enable delayed group deletion by setting **Deletion projection** to **Keep deleted**.
|
||||
In GitLab 15.11 and later with the `always_perform_delayed_deletion` feature flag enabled, or in GitLab 16.0 and later:
|
||||
In GitLab 16.0 and later, the **Keep deleted** option is removed, and delayed group deletion is the default.
|
||||
|
||||
- The **Keep deleted** option is removed.
|
||||
- Delayed group deletion is the default.
|
||||
To enable delayed group deletion in GitLab 15:
|
||||
|
||||
1. GitLab 15.11 only: enable the `always_perform_delayed_deletion` feature flag.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Settings > General**.
|
||||
1. Expand **Visibility and access controls**.
|
||||
1. For **Deletion projection**, select **Keep deleted**.
|
||||
1. Select **Save changes**.
|
||||
|
||||
### Override defaults and delete immediately
|
||||
|
||||
|
|
@ -153,15 +170,12 @@ Prerequisites:
|
|||
1. Select **Settings > General**.
|
||||
1. Expand **Visibility and access controls**.
|
||||
1. Select the desired default project visibility:
|
||||
- **Private** - Project access must be granted explicitly to each user. If this
|
||||
project is part of a group, access is granted to members of the group.
|
||||
- **Internal** - The project can be accessed by any authenticated user except external users.
|
||||
- **Public** - The project can be accessed without any authentication.
|
||||
- **Private** - Grant project access explicitly to each user. If this
|
||||
project is part of a group, grants access to members of the group.
|
||||
- **Internal** - Any authenticated user, except external users, can access the project.
|
||||
- **Public** - Any user can access the project without any authentication.
|
||||
1. Select **Save changes**.
|
||||
|
||||
For more details on project visibility, see
|
||||
[Project visibility](../../user/public_access.md).
|
||||
|
||||
## Configure snippet visibility defaults
|
||||
|
||||
To set the default visibility levels for new [snippets](../../user/snippets.md):
|
||||
|
|
@ -173,12 +187,14 @@ Prerequisites:
|
|||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Settings > General**.
|
||||
1. Expand **Visibility and access controls**.
|
||||
1. Select the desired default snippet visibility.
|
||||
1. For **Default snippet visibility**, select your desired visibility level:
|
||||
- **Private**.
|
||||
- **Internal**. This setting is disabled for new projects, groups, and snippets on GitLab.com.
|
||||
Existing snippets using the `Internal` visibility setting keep this setting. To learn more
|
||||
about this change, see [issue 12388](https://gitlab.com/gitlab-org/gitlab/-/issues/12388).
|
||||
- **Public**.
|
||||
1. Select **Save changes**.
|
||||
|
||||
For more details on snippet visibility, read
|
||||
[Snippet visibility](../../user/snippets.md).
|
||||
|
||||
## Configure group visibility defaults
|
||||
|
||||
To set the default visibility levels for new groups:
|
||||
|
|
@ -190,9 +206,9 @@ Prerequisites:
|
|||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Settings > General**.
|
||||
1. Expand **Visibility and access controls**.
|
||||
1. Select the desired default group visibility:
|
||||
1. For **Default group visibility**, select your desired visibility level:
|
||||
- **Private** - Only members can view the group and its projects.
|
||||
- **Internal** - Any authenticated user (except external users) can view the group and any internal projects.
|
||||
- **Internal** - Any authenticated user, except external users, can view the group and any internal projects.
|
||||
- **Public** - Authentication is not required to view the group and any public projects.
|
||||
1. Select **Save changes**.
|
||||
|
||||
|
|
@ -218,7 +234,7 @@ Prerequisites:
|
|||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Settings > General**.
|
||||
1. Expand **Visibility and access controls**.
|
||||
1. In the **Restricted visibility levels** section, select the desired visibility levels to restrict.
|
||||
1. For **Restricted visibility levels**, select the desired visibility levels to restrict.
|
||||
- If you restrict the **Public** level:
|
||||
- Only administrators can create public groups, projects, and snippets.
|
||||
- User profiles are visible to only authenticated users through the Web interface.
|
||||
|
|
@ -241,7 +257,13 @@ communicate with GitLab. Disabling an access protocol does not block port access
|
|||
server itself. The ports used for the protocol, SSH or HTTP(S), are still accessible.
|
||||
The GitLab restrictions apply at the application level.
|
||||
|
||||
To specify the enabled Git access protocols:
|
||||
GitLab allows Git actions only for the protocols you select:
|
||||
|
||||
- If you enable both SSH and HTTP(S), users can choose either protocol.
|
||||
- If you enable only one protocol, project pages show only the allowed protocol's
|
||||
URL, with no option to change it.
|
||||
|
||||
To specify the enabled Git access protocols for all projects on your instance:
|
||||
|
||||
Prerequisites:
|
||||
|
||||
|
|
@ -250,24 +272,12 @@ Prerequisites:
|
|||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Settings > General**.
|
||||
1. Expand **Visibility and access controls**.
|
||||
1. Select the desired Git access protocols:
|
||||
- Both SSH and HTTP(S)
|
||||
- Only SSH
|
||||
- Only HTTP(S)
|
||||
1. For **Enabled Git access protocols**, select your desired protocols:
|
||||
- Both SSH and HTTP(S).
|
||||
- Only SSH.
|
||||
- Only HTTP(S).
|
||||
1. Select **Save changes**.
|
||||
|
||||
If you enable both SSH and HTTP(S), users can choose either protocol.
|
||||
If you enable only one protocol:
|
||||
|
||||
- The project page shows only the allowed protocol's URL, with no option to
|
||||
change it.
|
||||
- GitLab shows a tooltip when you hover over the protocol for the URL, if user action
|
||||
(such as adding a SSH key or setting a password) is required:
|
||||
|
||||

|
||||
|
||||
GitLab only allows Git actions for the protocols you select.
|
||||
|
||||
WARNING:
|
||||
GitLab [allows the HTTP(S) protocol](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/18021)
|
||||
for Git clone or fetch requests performed [with GitLab CI/CD job tokens](../../ci/jobs/ci_job_token.md).
|
||||
|
|
@ -276,7 +286,7 @@ This happens even if you select **Only SSH**, because GitLab Runner and CI/CD jo
|
|||
## Customize Git clone URL for HTTP(S)
|
||||
|
||||
You can customize project Git clone URLs for HTTP(S), which affects the clone
|
||||
panel. For example, if:
|
||||
panel shown to users on a project's page. For example, if:
|
||||
|
||||
- Your GitLab instance is at `https://example.com`, then project clone URLs are like
|
||||
`https://example.com/foo/bar.git`.
|
||||
|
|
@ -284,7 +294,7 @@ panel. For example, if:
|
|||
you can set this setting to `https://git.example.com/gitlab/`.
|
||||
|
||||
To specify a custom Git clone URL for HTTP(S) in `gitlab.rb`, set a new value for
|
||||
`gitlab_rails['gitlab_ssh_host']`. To specify it from the GitLab UI:
|
||||
`gitlab_rails['gitlab_ssh_host']`. To specify a new value from the GitLab UI:
|
||||
|
||||
Prerequisites:
|
||||
|
||||
|
|
@ -298,7 +308,7 @@ Prerequisites:
|
|||
|
||||
## Configure defaults for RSA, DSA, ECDSA, ED25519, ECDSA_SK, ED25519_SK SSH keys
|
||||
|
||||
These options specify the permitted types and lengths for SSH keys.
|
||||
These options specify the [permitted types and lengths](../../security/ssh_keys_restrictions.md) for SSH keys.
|
||||
|
||||
To specify a restriction for each key type:
|
||||
|
||||
|
|
@ -314,8 +324,6 @@ To specify a restriction for each key type:
|
|||
- At least 1024 bits.
|
||||
1. Select **Save changes**.
|
||||
|
||||
For more details, see [SSH key restrictions](../../security/ssh_keys_restrictions.md).
|
||||
|
||||
## Enable project mirroring
|
||||
|
||||
GitLab enables project mirroring by default. If you disable it, both
|
||||
|
|
@ -323,7 +331,7 @@ GitLab enables project mirroring by default. If you disable it, both
|
|||
[push mirroring](../../user/project/repository/mirror/push.md) no longer
|
||||
work in every repository. They can only be re-enabled by an administrator user on a per-project basis.
|
||||
|
||||
To allow project maintainers on your instance to configure mirroring at the project level:
|
||||
To allow project maintainers on your instance to configure mirroring per project:
|
||||
|
||||
Prerequisites:
|
||||
|
||||
|
|
@ -340,15 +348,16 @@ Prerequisites:
|
|||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87579) in GitLab 15.1 [with a flag](../feature_flags.md) named `group_ip_restrictions_allow_global`. Disabled by default.
|
||||
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/366445) in GitLab 15.4. [Feature flag `group_ip_restrictions_allow_global`](https://gitlab.com/gitlab-org/gitlab/-/issues/366445) removed.
|
||||
|
||||
Administrators can combine IP address ranges with [group-level IP restrictions](../../user/group/access_and_permissions.md#restrict-group-access-by-ip-address).
|
||||
Use globally-allowed IP addresses to allow aspects of the GitLab installation to work even when group-level IP address
|
||||
restrictions are set.
|
||||
Administrators can combine IP address ranges with
|
||||
[IP restrictions per group](../../user/group/access_and_permissions.md#restrict-group-access-by-ip-address).
|
||||
Use globally-allowed IP addresses to allow aspects of the GitLab installation to work even when IP address
|
||||
restrictions are set per group.
|
||||
|
||||
For example, if the GitLab Pages daemon runs on the `10.0.0.0/24` range, you can specify that range as globally-allowed.
|
||||
This means GitLab Pages can still fetch artifacts from pipelines even if group-level IP address restrictions don't
|
||||
For example, if the GitLab Pages daemon runs on the `10.0.0.0/24` range, you can specify that range as globally allowed.
|
||||
GitLab Pages can still fetch artifacts from pipelines, even if IP address restrictions for the group don't
|
||||
include the `10.0.0.0/24` range.
|
||||
|
||||
To add a IP address range to the group-level allowlist:
|
||||
To add a IP address range to the allowlist for a group:
|
||||
|
||||
Prerequisites:
|
||||
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ To resolve the issue:
|
|||
|
||||
You might get the error `Attempt_Exceed_Limitation - Attempt exceed the limitation, refresh page to try again.` when purchasing compute minutes.
|
||||
|
||||
This issue occurs when the credit card form is re-submitted too quickly within a specific timeframe (3 submissions in 1 minute or 6 submissions in 1 hour).
|
||||
This issue occurs when the credit card form is re-submitted too quickly within a specific time frame (three submissions within one minute or six submissions within one hour).
|
||||
|
||||
To resolve this issue, wait a few minutes and try the purchase process again.
|
||||
|
||||
|
|
|
|||
|
|
@ -61,15 +61,6 @@ You might have jobs or pipelines that run unexpectedly when using `rules:changes
|
|||
|
||||
With merged results pipelines, the internal commit that GitLab creates is used as a base to compare against. This commit likely contains more changes than the tip of the MR branch, which causes unexpected outcomes.
|
||||
|
||||
### Merged results pipelines are not created
|
||||
|
||||
In GitLab 13.7 and earlier, merged results pipelines might not be created due
|
||||
to a disabled [feature flag](../../user/feature_flags.md). This feature flag
|
||||
[was removed](https://gitlab.com/gitlab-org/gitlab/-/issues/299115) in GitLab 13.8.
|
||||
Upgrade to 13.8 or later, or make sure the `:merge_ref_auto_sync`
|
||||
[feature flag is enabled](../../administration/feature_flags.md#check-if-a-feature-flag-is-enabled)
|
||||
on your GitLab instance.
|
||||
|
||||
### Successful merged results pipeline overrides a failed branch pipeline
|
||||
|
||||
A failed branch pipeline is sometimes ignored when the
|
||||
|
|
|
|||
|
|
@ -255,6 +255,7 @@ Git push options can perform actions for merge requests while pushing changes:
|
|||
| `merge_request.target_project=<project>` | Set the target of the merge request to a particular upstream project, such as: `git push -o merge_request.target_project=path/to/project`. Introduced in [GitLab 16.6](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/132475). |
|
||||
| `merge_request.merge_when_pipeline_succeeds` | Set the merge request to [merge when its pipeline succeeds](../user/project/merge_requests/merge_when_pipeline_succeeds.md). |
|
||||
| `merge_request.remove_source_branch` | Set the merge request to remove the source branch when it's merged. |
|
||||
| `merge_request.squash` | Set the merge request to squash all commits into a single commit on merge Introduced in [GitLab 17.2](https://gitlab.com/gitlab-org/gitlab/-/issues/). |
|
||||
| `merge_request.title="<title>"` | Set the title of the merge request. For example: `git push -o merge_request.title="The title I want"`. |
|
||||
| `merge_request.description="<description>"` | Set the description of the merge request. For example: `git push -o merge_request.description="The description I want"`. |
|
||||
| `merge_request.draft` | Mark the merge request as a draft. For example: `git push -o merge_request.draft`. Introduced in [GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/296673). |
|
||||
|
|
|
|||
|
|
@ -523,7 +523,7 @@ apply, contact [GitLab Support](https://support.gitlab.com/hc/en-us/requests/new
|
|||
|
||||
You might get the error `Attempt_Exceed_Limitation - Attempt exceed the limitation, refresh page to try again.` when purchasing a GitLab subscription.
|
||||
|
||||
This issue occurs when the credit card form is re-submitted too quickly within a specific timeframe (3 submissions in 1 minute or 6 submissions in 1 hour).
|
||||
This issue occurs when the credit card form is re-submitted too quickly within a specific time frame (three submissions within one minute or six submissions within one hour).
|
||||
|
||||
To resolve this issue, wait a few minutes and try the purchase process again.
|
||||
|
||||
|
|
|
|||
|
|
@ -501,7 +501,7 @@ apply, contact [GitLab Support](https://support.gitlab.com/hc/en-us/requests/new
|
|||
|
||||
You might get the error `Attempt_Exceed_Limitation - Attempt exceed the limitation, refresh page to try again.` when purchasing a GitLab subscription.
|
||||
|
||||
This issue occurs when the credit card form is re-submitted too quickly within a specific timeframe (3 submissions in 1 minute or 6 submissions in 1 hour).
|
||||
This issue occurs when the credit card form is re-submitted too quickly within a specific time frame (three submissions within one minute or six submissions within one hour).
|
||||
|
||||
To resolve this issue, wait a few minutes and try the purchase process again.
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ Learn how to connect GitLab to Google Cloud and create a GitLab pipeline using r
|
|||
1. Create or select a Google Cloud project.
|
||||
|
||||
NOTE:
|
||||
If you don't plan to keep the resources that you create in this procedure, create a new Google Cloud project instead of selecting an existing project. After you finish these steps, you can delete the project, removing all resources associated with the project.
|
||||
If you don't plan to keep the resources that you create in this procedure, then create a new Google Cloud project instead of selecting an existing project. After you finish these steps, you can delete the project, removing all resources associated with the project.
|
||||
|
||||
To create a Google Cloud project, run the following command:
|
||||
|
||||
|
|
@ -70,7 +70,7 @@ Learn how to connect GitLab to Google Cloud and create a GitLab pipeline using r
|
|||
|
||||
```dockerfile
|
||||
# Dockerfile for test purposes. Generates a new random image in every build.
|
||||
FROM alpine:latest
|
||||
FROM alpine:3.15.11
|
||||
RUN dd if=/dev/urandom of=random bs=10 count=1
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ DETAILS:
|
|||
|
||||
- Helps you write code more efficiently by generating code and showing suggestions as you type.
|
||||
- Large language model (LLM) for code completion: Vertex AI Codey [`code-gecko`](https://console.cloud.google.com/vertex-ai/publishers/google/model-garden/code-gecko)
|
||||
- LLM for code generation: Anthropic [`claude-3-sonnet-20240229`](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-sonnet)
|
||||
- LLM for code generation: Anthropic [`claude-3-5-sonnet-20240620`](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet)
|
||||
- <i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Watch overview](https://youtu.be/ds7SG1wgcVM)
|
||||
- [View documentation](../project/repository/code_suggestions/index.md).
|
||||
|
||||
|
|
|
|||
|
|
@ -255,5 +255,4 @@ Use the following commands to quickly accomplish specific tasks.
|
|||
| /tests | [Write tests](#write-tests-in-the-ide) |
|
||||
| /explain | [Explain code](../gitlab_duo_chat/examples.md#explain-code-in-the-ide) |
|
||||
| /vulnerability_explain | [Explain current vulnerability](../gitlab_duo/index.md#vulnerability-explanation) |
|
||||
| | [Explain current vulnerability](../application_security/vulnerabilities/index.md#explaining-a-vulnerability) |
|
||||
| /refactor | [Refactor the code](../gitlab_duo_chat/examples.md#refactor-code-in-the-ide) |
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ Because this is an experimental feature,
|
|||
| `work_item_epics` | Consolidated flag that contains all the changes needed to get epic work items to work for a given group. | Group | **Required** |
|
||||
| `synced_epic_work_item_editable` | Allows editing epic work items when they have a legacy epic. | Group | **Required** |
|
||||
| `work_items_rolledup_dates` | Calculates the start and due dates in a hierarchy for work items. | Group | **Required** |
|
||||
| `epic_and_work_item_labels_unification` | Delegates labels between epics and epic work item. | Group | **Required** |
|
||||
| `epic_and_work_item_associations_unification` | Delegates other epic and work item associations. | Group | **Required** |
|
||||
| `epic_and_work_item_notes_unification` | Delegates notes between epics and work item. | Group | **Required** |
|
||||
|
||||
|
|
|
|||
|
|
@ -59,8 +59,8 @@ The following table lists project permissions available for each role:
|
|||
<!-- Keep these tables sorted: By category first, then by minimum role, then alphabetically by action. -->
|
||||
|
||||
| Action | Guest | Reporter | Developer | Maintainer | Owner | Notes |
|
||||
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------|----------|-----------|------------|-------|-------|
|
||||
| [Analytics](analytics/index.md):<br>View [issue analytics](group/issues_analytics/index.md) | ✓ | ✓ | ✓ | ✓ | ✓ | |
|
||||
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----:|:--------:|:---------:|:----------:|:-----:|-------|
|
||||
| [Analytics](analytics/index.md):<br>View [issue analytics](group/issues_analytics/index.md) | ✓ | ✓ | ✓ | ✓ | ✓ | |
|
||||
| [Analytics](analytics/index.md):<br>View [value stream analytics](group/value_stream_analytics/index.md) | ✓ | ✓ | ✓ | ✓ | ✓ | |
|
||||
| [Analytics](analytics/index.md):<br>View [DORA metrics](analytics/ci_cd_analytics.md) | | ✓ | ✓ | ✓ | ✓ | |
|
||||
| [Analytics](analytics/index.md):<br>View [CI/CD analytics](analytics/ci_cd_analytics.md) | | ✓ | ✓ | ✓ | ✓ | |
|
||||
|
|
@ -106,7 +106,7 @@ The following table lists project permissions available for each role:
|
|||
| [Issues](project/issues/index.md):<br>View [confidential issues](project/issues/confidential_issues.md) | | ✓ | ✓ | ✓ | ✓ | Guest users can only view the [confidential issues](project/issues/confidential_issues.md) they created themselves or are assigned to. |
|
||||
| [Issues](project/issues/index.md):<br>Close / reopen | | ✓ | ✓ | ✓ | ✓ | Authors and assignees can close and reopen issues even if they don't have the Reporter role. |
|
||||
| [Issues](project/issues/index.md):<br>Lock threads | | ✓ | ✓ | ✓ | ✓ | |
|
||||
| [Issues](project/issues/index.md):<br>Manage [related issues](project/issues/related_issues.md) | ✓ | ✓ | ✓ | ✓ | ✓ | |
|
||||
| [Issues](project/issues/index.md):<br>Manage [related issues](project/issues/related_issues.md) | ✓ | ✓ | ✓ | ✓ | ✓ | |
|
||||
| [Issues](project/issues/index.md):<br>Manage tracker | | ✓ | ✓ | ✓ | ✓ | |
|
||||
| [Issues](project/issues/index.md):<br>Move issues | | ✓ | ✓ | ✓ | ✓ | Attached design files are moved together with the issue. |
|
||||
| [Issues](project/issues/index.md):<br>Set issue [time tracking](project/time_tracking.md) estimate and time spent | | ✓ | ✓ | ✓ | ✓ | |
|
||||
|
|
@ -162,8 +162,8 @@ The following table lists project permissions available for each role:
|
|||
| [Projects](project/index.md):<br>Disable notification emails | | | | | ✓ | |
|
||||
| [Projects](project/index.md):<br>Transfer project to another namespace | | | | | ✓ | |
|
||||
| [Projects](project/index.md): View [Usage Quotas](usage_quotas.md) page | | | | ✓ | ✓ | |
|
||||
| [Requirements Management](project/requirements/index.md):<br>Archive / reopen | | ✓ | ✓ | ✓ | ✓ | Authors and assignees can archive and re-open even if they don’t have the Reporter role. |
|
||||
| [Requirements Management](project/requirements/index.md):<br>Create / edit | | ✓ | ✓ | ✓ | ✓ | Authors and assignees can modify the title and description even if they don’t have the Reporter role.|
|
||||
| [Requirements Management](project/requirements/index.md):<br>Archive / reopen | | ✓ | ✓ | ✓ | ✓ | Authors and assignees can archive and re-open even if they don't have the Reporter role. |
|
||||
| [Requirements Management](project/requirements/index.md):<br>Create / edit | | ✓ | ✓ | ✓ | ✓ | Authors and assignees can modify the title and description even if they don't have the Reporter role.|
|
||||
| [Requirements Management](project/requirements/index.md):<br>Import / export | | ✓ | ✓ | ✓ | ✓ | |
|
||||
| [Tasks](tasks.md):<br>Add a linked item | ✓ | ✓ | ✓ | ✓ | ✓ | |
|
||||
| [Tasks](tasks.md):<br>Create | | ✓ | ✓ | ✓ | ✓ | Authors and assignees can modify the title and description even if they don't have the Reporter role. |
|
||||
|
|
@ -181,32 +181,32 @@ The following table lists project permissions available for each role:
|
|||
|
||||
Project permissions for [Application Security](application_security/secure_your_application.md) features including dependency management, security analyzers, security policies, and vulnerability management.
|
||||
|
||||
| Action | Guest | Reporter | Developer | Maintainer | Owner | Notes |
|
||||
|--------|:-----:|:--------:|:---------:|:----------:|:-----:|-------|
|
||||
| View licenses in [dependency list](application_security/dependency_list/index.md) | | | ✓ | ✓ | ✓ | |
|
||||
| View [dependency list](application_security/dependency_list/index.md) | | | ✓ | ✓ | ✓ | |
|
||||
| View [vulnerability report](application_security/vulnerability_report/index.md) | | | ✓ | ✓ | ✓ | |
|
||||
| View [security dashboard](application_security/security_dashboard/index.md) | | | ✓ | ✓ | ✓ | |
|
||||
| [Create issue](application_security/vulnerabilities/index.md#create-a-gitlab-issue-for-a-vulnerability) from vulnerability finding | | | ✓ | ✓ | ✓ | |
|
||||
| Manually [create a vulnerability](application_security/vulnerability_report/index.md#manually-add-a-vulnerability) | | | ✓ | ✓ | ✓ | |
|
||||
| Create and run [on-demand DAST scans](application_security/dast/on-demand_scan.md) | | | ✓ | ✓ | ✓ | |
|
||||
| Create, edit, delete [individual security policies](application_security/policies/index.md) | | | ✓ | ✓ | ✓ | |
|
||||
| Create a [CVE ID Request](application_security/cve_id_request.md) | | | | ✓ | ✓ | |
|
||||
| Change vulnerability status | | | | ✓ | ✓ | The `admin_vulnerability` permission was [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/412693) from the Developer role in GitLab 17.0. |
|
||||
| Create or assign [security policy project](application_security/policies/index.md) | | | | | ✓ | |
|
||||
| Manage [security configurations](application_security/configuration/index.md) | | | | | ✓ | |
|
||||
| Action | Guest | Reporter | Developer | Maintainer | Owner | Notes |
|
||||
|------------------------------------------------------------------------------------------------------------------------------------|:-----:|:--------:|:---------:|:----------:|:-----:|-------|
|
||||
| View licenses in [dependency list](application_security/dependency_list/index.md) | | | ✓ | ✓ | ✓ | |
|
||||
| View [dependency list](application_security/dependency_list/index.md) | | | ✓ | ✓ | ✓ | |
|
||||
| View [vulnerability report](application_security/vulnerability_report/index.md) | | | ✓ | ✓ | ✓ | |
|
||||
| View [security dashboard](application_security/security_dashboard/index.md) | | | ✓ | ✓ | ✓ | |
|
||||
| [Create issue](application_security/vulnerabilities/index.md#create-a-gitlab-issue-for-a-vulnerability) from vulnerability finding | | | ✓ | ✓ | ✓ | |
|
||||
| Manually [create a vulnerability](application_security/vulnerability_report/index.md#manually-add-a-vulnerability) | | | ✓ | ✓ | ✓ | |
|
||||
| Create and run [on-demand DAST scans](application_security/dast/on-demand_scan.md) | | | ✓ | ✓ | ✓ | |
|
||||
| Create, edit, delete [individual security policies](application_security/policies/index.md) | | | ✓ | ✓ | ✓ | |
|
||||
| Create a [CVE ID Request](application_security/cve_id_request.md) | | | | ✓ | ✓ | |
|
||||
| Change vulnerability status | | | | ✓ | ✓ | The `admin_vulnerability` permission was [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/412693) from the Developer role in GitLab 17.0. |
|
||||
| Create or assign [security policy project](application_security/policies/index.md) | | | | | ✓ | |
|
||||
| Manage [security configurations](application_security/configuration/index.md) | | | | | ✓ | |
|
||||
|
||||
### Compliance
|
||||
|
||||
Project permissions for [compliance](compliance/index.md) features including compliance center, audit events, compliance frameworks, and licenses.
|
||||
|
||||
| Action | Guest | Reporter | Developer | Maintainer | Owner | Notes |
|
||||
|-----------------------------------------------------------------------------------------|:-------:|:----------:|:-----------:|:------------:|:-------:|-------|
|
||||
| View [allowed and denied licenses in MR](compliance/license_scanning_of_cyclonedx_files/index.md) | ✓ | ✓ | ✓ | ✓ | ✓ | On self-managed GitLab instances, users with the Guest role are able to perform this action only on public and internal projects (not on private projects). [External users](../administration/external_users.md) must be have at least the Reporter role, even if the project is internal. Users with the Guest role on GitLab.com are able to perform this action only on public projects because internal visibility is not available. |
|
||||
| View [audit events](../administration/audit_event_reports.md) | | | ✓ | ✓ | ✓ | Users can only view events based on their individual actions. |
|
||||
| View licenses in [dependency list](application_security/dependency_list/index.md) | | | ✓ | ✓ | ✓ | |
|
||||
| Create, edit, and delete license approval policies | | | ✓ | ✓ | ✓ | |
|
||||
| Manage [audit streams](compliance/audit_event_streaming.md) | | | | | ✓ | |
|
||||
| Action | Guest | Reporter | Developer | Maintainer | Owner | Notes |
|
||||
|---------------------------------------------------------------------------------------------------|:-----:|:--------:|:---------:|:----------:|:-----:|-------|
|
||||
| View [allowed and denied licenses in MR](compliance/license_scanning_of_cyclonedx_files/index.md) | ✓ | ✓ | ✓ | ✓ | ✓ | On self-managed GitLab instances, users with the Guest role are able to perform this action only on public and internal projects (not on private projects). [External users](../administration/external_users.md) must be have at least the Reporter role, even if the project is internal. Users with the Guest role on GitLab.com are able to perform this action only on public projects because internal visibility is not available. |
|
||||
| View [audit events](../administration/audit_event_reports.md) | | | ✓ | ✓ | ✓ | Users can only view events based on their individual actions. |
|
||||
| View licenses in [dependency list](application_security/dependency_list/index.md) | | | ✓ | ✓ | ✓ | |
|
||||
| Create, edit, and delete license approval policies | | | ✓ | ✓ | ✓ | |
|
||||
| Manage [audit streams](compliance/audit_event_streaming.md) | | | | | ✓ | |
|
||||
|
||||
### Repository
|
||||
|
||||
|
|
@ -242,7 +242,7 @@ Project permissions for [merge requests](project/merge_requests/index.md):
|
|||
| Manage [merge request approval rules](project/merge_requests/approvals/rules.md) | | | | ✓ | ✓ | |
|
||||
| Delete merge request | | | | | ✓ | |
|
||||
|
||||
## GitLab CI/CD permissions
|
||||
### GitLab CI/CD permissions
|
||||
|
||||
[GitLab CI/CD](../ci/index.md) permissions for some roles can be modified by these settings:
|
||||
|
||||
|
|
@ -251,56 +251,59 @@ Project permissions for [merge requests](project/merge_requests/index.md):
|
|||
- [Pipeline visibility](../ci/pipelines/settings.md#change-pipeline-visibility-for-non-project-members-in-public-projects):
|
||||
When set to **Everyone with Access**, gives access to certain CI/CD "view" features to *non-project* members.
|
||||
|
||||
| Action | Non-member | Guest | Reporter | Developer | Maintainer | Owner | Notes |
|
||||
|--------------------------------------------------------------------------------------------------------------------------------|------------|-------|----------|-----------|------------|-------|-------|
|
||||
| See that artifacts exist | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | Non-members and guests: Only if the project is public. |
|
||||
| View a list of jobs | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | Non-members: Only if the project is public and **Public pipelines** is enabled in **Project Settings > CI/CD**.<br>Guests: Only if **Public pipelines** is enabled in **Project Settings > CI/CD**. |
|
||||
| View and download artifacts | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | Non-members: Only if the project is public, **Public pipelines** is enabled in **Project Settings > CI/CD**, and [`artifacts:public: false`](../ci/yaml/index.md#artifactspublic) is not set on the job.<br>Guests: Only if **Public pipelines** is enabled in **Project Settings > CI/CD** and `artifacts:public: false` is not set on the job.<br>Reporters: Only if `artifacts:public: false` is not set on the job. |
|
||||
| View [environments](../ci/environments/index.md) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | Non-members and guests: Only if the project is public. |
|
||||
| View job logs and job details page | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | Non-members: Only if the project is public and **Public pipelines** is enabled in **Project Settings > CI/CD**.<br>Guests: Only if **Public pipelines** is enabled in **Project Settings > CI/CD**. |
|
||||
| View pipelines and pipeline details pages | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | Non-members: Only if the project is public and **Public pipelines** is enabled in **Project Settings > CI/CD**.<br>Guests: Only if **Public pipelines** is enabled in **Project Settings > CI/CD**. |
|
||||
| View pipelines tab in MR | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | Non-members and guests: Only if the project is public. |
|
||||
| [View vulnerabilities in a pipeline](application_security/vulnerability_report/pipeline.md#view-vulnerabilities-in-a-pipeline) | | ✓ | ✓ | ✓ | ✓ | ✓ | Guests: Only if **Public pipelines** is enabled in **Project Settings > CI/CD**. |
|
||||
| Run deployment job for a protected environment | | | ✓ | ✓ | ✓ | ✓ | Reporters: Only if the user is [part of a group with access to the protected environment](../ci/environments/protected_environments.md#deployment-only-access-to-protected-environments).<br>Developers and maintainers: Only if the user is [allowed to deploy to the protected branch](../ci/environments/protected_environments.md#protecting-environments). |
|
||||
| View and download project-level [Secure Files](../api/secure_files.md) | | | | ✓ | ✓ | ✓ | |
|
||||
| Retry jobs | | | | ✓ | ✓ | ✓ | |
|
||||
| Cancel jobs | | | | ✓ | ✓ | ✓ | Cancellation permissions can be [restricted in the pipeline settings](../ci/pipelines/settings.md#restrict-roles-that-can-cancel-pipelines-or-jobs). |
|
||||
| Create new [environments](../ci/environments/index.md) | | | | ✓ | ✓ | ✓ | |
|
||||
| Delete job logs or job artifacts | | | | ✓ | ✓ | ✓ | Developers: Only if the job was triggered by the user and runs for a non-protected branch. |
|
||||
| Run CI/CD pipeline | | | | ✓ | ✓ | ✓ | |
|
||||
| Run CI/CD pipeline for a protected branch | | | | ✓ | ✓ | ✓ | Developers and maintainers: Only if the user is [allowed to merge or push to the protected branch](../ci/pipelines/index.md#pipeline-security-on-protected-branches). |
|
||||
| Stop [environments](../ci/environments/index.md) | | | | ✓ | ✓ | ✓ | |
|
||||
| View a job with [debug logging](../ci/variables/index.md#enable-debug-logging) | | | | ✓ | ✓ | ✓ | |
|
||||
| Use pipeline editor | | | | ✓ | ✓ | ✓ | |
|
||||
| Run [interactive web terminals](../ci/interactive_web_terminal/index.md) | | | | ✓ | ✓ | ✓ | |
|
||||
| Add project runners to project | | | | | ✓ | ✓ | |
|
||||
| Clear runner caches manually | | | | | ✓ | ✓ | |
|
||||
| Enable instance runners in project | | | | | ✓ | ✓ | |
|
||||
| Manage CI/CD settings | | | | | ✓ | ✓ | |
|
||||
| Manage job triggers | | | | | ✓ | ✓ | |
|
||||
| Manage project-level CI/CD variables | | | | | ✓ | ✓ | |
|
||||
| Manage project-level [Secure Files](../api/secure_files.md) | | | | | ✓ | ✓ | |
|
||||
| Use [environment terminals](../ci/environments/index.md#web-terminals-deprecated) | | | | | ✓ | ✓ | |
|
||||
| Delete pipelines | | | | | | ✓ | |
|
||||
Project Owners can do any listed action, and also can delete pipelines:
|
||||
|
||||
### Job permissions
|
||||
| Action | Non-member | Guest | Reporter | Developer | Maintainer | Notes |
|
||||
|--------------------------------------------------------------------------------------------------------------------------------|:----------:|:-----:|:--------:|:---------:|:----------:|-------|
|
||||
| See that artifacts exist | ✓ | ✓ | ✓ | ✓ | ✓ | Non-members and guests: Only if the project is public. |
|
||||
| View a list of jobs | ✓ | ✓ | ✓ | ✓ | ✓ | Non-members: Only if the project is public and **Public pipelines** is enabled in **Project Settings > CI/CD**.<br>Guests: Only if **Public pipelines** is enabled in **Project Settings > CI/CD**. |
|
||||
| View and download artifacts | ✓ | ✓ | ✓ | ✓ | ✓ | Non-members: Only if the project is public, **Public pipelines** is enabled in **Project Settings > CI/CD**, and [`artifacts:public: false`](../ci/yaml/index.md#artifactspublic) is not set on the job.<br>Guests: Only if **Public pipelines** is enabled in **Project Settings > CI/CD** and `artifacts:public: false` is not set on the job.<br>Reporters: Only if `artifacts:public: false` is not set on the job. |
|
||||
| View [environments](../ci/environments/index.md) | ✓ | ✓ | ✓ | ✓ | ✓ | Non-members and guests: Only if the project is public. |
|
||||
| View job logs and job details page | ✓ | ✓ | ✓ | ✓ | ✓ | Non-members: Only if the project is public and **Public pipelines** is enabled in **Project Settings > CI/CD**.<br>Guests: Only if **Public pipelines** is enabled in **Project Settings > CI/CD**. |
|
||||
| View pipelines and pipeline details pages | ✓ | ✓ | ✓ | ✓ | ✓ | Non-members: Only if the project is public and **Public pipelines** is enabled in **Project Settings > CI/CD**.<br>Guests: Only if **Public pipelines** is enabled in **Project Settings > CI/CD**. |
|
||||
| View pipelines tab in MR | ✓ | ✓ | ✓ | ✓ | ✓ | Non-members and guests: Only if the project is public. |
|
||||
| [View vulnerabilities in a pipeline](application_security/vulnerability_report/pipeline.md#view-vulnerabilities-in-a-pipeline) | | ✓ | ✓ | ✓ | ✓ | Guests: Only if **Public pipelines** is enabled in **Project Settings > CI/CD**. |
|
||||
| Run deployment job for a protected environment | | | ✓ | ✓ | ✓ | Reporters: Only if the user is [part of a group with access to the protected environment](../ci/environments/protected_environments.md#deployment-only-access-to-protected-environments).<br>Developers and maintainers: Only if the user is [allowed to deploy to the protected branch](../ci/environments/protected_environments.md#protecting-environments). |
|
||||
| View and download project [Secure Files](../api/secure_files.md) | | | | ✓ | ✓ | |
|
||||
| Retry jobs | | | | ✓ | ✓ | |
|
||||
| Cancel jobs | | | | ✓ | ✓ | Cancellation permissions can be [restricted in the pipeline settings](../ci/pipelines/settings.md#restrict-roles-that-can-cancel-pipelines-or-jobs). |
|
||||
| Create new [environments](../ci/environments/index.md) | | | | ✓ | ✓ | |
|
||||
| Delete job logs or job artifacts | | | | ✓ | ✓ | Developers: Only if the job was triggered by the user and runs for a non-protected branch. |
|
||||
| Run CI/CD pipeline | | | | ✓ | ✓ | |
|
||||
| Run CI/CD job | | | | ✓ | ✓ | |
|
||||
| Run CI/CD pipeline for a protected branch | | | | ✓ | ✓ | Developers and maintainers: Only if the user is [allowed to merge or push to the protected branch](../ci/pipelines/index.md#pipeline-security-on-protected-branches). |
|
||||
| Stop [environments](../ci/environments/index.md) | | | | ✓ | ✓ | |
|
||||
| View a job with [debug logging](../ci/variables/index.md#enable-debug-logging) | | | | ✓ | ✓ | |
|
||||
| Use pipeline editor | | | | ✓ | ✓ | |
|
||||
| Run [interactive web terminals](../ci/interactive_web_terminal/index.md) | | | | ✓ | ✓ | |
|
||||
| Add project runners to project | | | | | ✓ | |
|
||||
| Clear runner caches manually | | | | | ✓ | |
|
||||
| Enable instance runners in project | | | | | ✓ | |
|
||||
| Manage CI/CD settings | | | | | ✓ | |
|
||||
| Manage job triggers | | | | | ✓ | |
|
||||
| Manage project CI/CD variables | | | | | ✓ | |
|
||||
| Manage project [Secure Files](../api/secure_files.md) | | | | | ✓ | |
|
||||
| Use [environment terminals](../ci/environments/index.md#web-terminals-deprecated) | | | | | ✓ | |
|
||||
|
||||
This table shows granted privileges for jobs triggered by specific types of users:
|
||||
#### Job permissions
|
||||
|
||||
| Action | Guest, Reporter | Developer | Maintainer | Administrator | Notes |
|
||||
|----------------------------------------------|-----------------|-----------|------------|---------------|-------|
|
||||
| Run CI job | | ✓ | ✓ | ✓ | |
|
||||
| Clone source and LFS from current project | | ✓ | ✓ | ✓ | |
|
||||
| Clone source and LFS from public projects | | ✓ | ✓ | ✓ | |
|
||||
| Clone source and LFS from internal projects | | ✓ | ✓ | ✓ | Developers and Maintainers: Only if the triggering user is not an external user. |
|
||||
| Clone source and LFS from private projects | | ✓ | ✓ | ✓ | Only if the triggering user is a member of the project. See also [Usage of private Docker images with `if-not-present` pull policy](https://docs.gitlab.com/runner/security/index.html#usage-of-private-docker-images-with-if-not-present-pull-policy). |
|
||||
| Pull container images from current project | | ✓ | ✓ | ✓ | |
|
||||
| Pull container images from public projects | | ✓ | ✓ | ✓ | |
|
||||
| Pull container images from internal projects | | ✓ | ✓ | ✓ | Developers and Maintainers: Only if the triggering user is not an external user. |
|
||||
| Pull container images from private projects | | ✓ | ✓ | ✓ | Only if the triggering user is a member of the project. See also [Usage of private Docker images with `if-not-present` pull policy](https://docs.gitlab.com/runner/security/index.html#usage-of-private-docker-images-with-if-not-present-pull-policy). |
|
||||
| Push container images to current project | | ✓ | ✓ | ✓ | |
|
||||
| Push container images to other projects | | | | | |
|
||||
| Push source and LFS | | | | | |
|
||||
This table shows granted privileges for jobs triggered by specific roles.
|
||||
|
||||
Project Owners can do any listed action, but no users can push source and LFS together.
|
||||
|
||||
| Action | Developer | Maintainer | Notes |
|
||||
|----------------------------------------------|:---------:|:----------:|-------|
|
||||
| Clone source and LFS from current project | ✓ | ✓ | |
|
||||
| Clone source and LFS from public projects | ✓ | ✓ | |
|
||||
| Clone source and LFS from internal projects | ✓ | ✓ | Developers and Maintainers: Only if the triggering user is not an external user. |
|
||||
| Clone source and LFS from private projects | ✓ | ✓ | Only if the triggering user is a member of the project. See also [Usage of private Docker images with `if-not-present` pull policy](https://docs.gitlab.com/runner/security/index.html#usage-of-private-docker-images-with-if-not-present-pull-policy). |
|
||||
| Pull container images from current project | ✓ | ✓ | |
|
||||
| Pull container images from public projects | ✓ | ✓ | |
|
||||
| Pull container images from internal projects | ✓ | ✓ | Developers and Maintainers: Only if the triggering user is not an external user. |
|
||||
| Pull container images from private projects | ✓ | ✓ | Only if the triggering user is a member of the project. See also [Usage of private Docker images with `if-not-present` pull policy](https://docs.gitlab.com/runner/security/index.html#usage-of-private-docker-images-with-if-not-present-pull-policy). |
|
||||
| Push container images to current project | ✓ | ✓ | You cannot push container images to other projects. |
|
||||
|
||||
Guest users and members with the Reporter role cannot do any of these actions.
|
||||
|
||||
## Group members permissions
|
||||
|
||||
|
|
@ -321,7 +324,7 @@ The following table lists group permissions available for each role:
|
|||
| View [group wiki](project/wiki/group.md) pages | ✓ | ✓ | ✓ | ✓ | ✓ | Guests: In addition, if your group is public or internal, all users who can see the group can also see group wiki pages. |
|
||||
| View [Insights](project/insights/index.md) | ✓ | ✓ | ✓ | ✓ | ✓ | |
|
||||
| View [Insights](project/insights/index.md) charts | ✓ | ✓ | ✓ | ✓ | ✓ | |
|
||||
| View [Issue analytics](group/issues_analytics/index.md) | ✓ | ✓ | ✓ | ✓ | ✓ | |
|
||||
| View [Issue analytics](group/issues_analytics/index.md) | ✓ | ✓ | ✓ | ✓ | ✓ | |
|
||||
| View Contribution analytics | ✓ | ✓ | ✓ | ✓ | ✓ | |
|
||||
| View group [epic](group/epics/index.md) | ✓ | ✓ | ✓ | ✓ | ✓ | |
|
||||
| View value stream analytics | ✓ | ✓ | ✓ | ✓ | ✓ | |
|
||||
|
|
@ -369,7 +372,7 @@ The following table lists group permissions available for each role:
|
|||
| [Migrate groups](group/import/index.md) | | | | | ✓ | |
|
||||
| Purge the dependency proxy for a group | | | | | ✓ | |
|
||||
| Share (invite) groups with groups | | | | | ✓ | |
|
||||
| View [Billing](../subscriptions/gitlab_com/index.md#view-your-gitlabcom-subscription) | | | | | ✓ | Does not apply to subgroups |
|
||||
| View [Billing](../subscriptions/gitlab_com/index.md#view-your-gitlabcom-subscription) | | | | | ✓ | Does not apply to subgroups |
|
||||
| View 2FA status of members | | | | | ✓ | |
|
||||
| View group [Usage Quotas](usage_quotas.md) page | | | | | ✓ | Does not apply to subgroups |
|
||||
| Map or unmap workspace cluster agents to and from a group | | | | | ✓ | |
|
||||
|
|
@ -390,14 +393,14 @@ Group permissions for [Application Security](application_security/secure_your_ap
|
|||
|
||||
Groups permissions for [compliance](compliance/index.md) features including compliance center, audit events, compliance frameworks, and licenses.
|
||||
|
||||
| Action | Guest | Reporter | Developer | Maintainer | Owner | Notes |
|
||||
|-----------------------------------------------------------------------------------------|:-------:|:----------:|:-----------:|:------------:|:-------:|-------|
|
||||
| View [audit events](../administration/audit_event_reports.md) | | | ✓ | ✓ | ✓ | Users can view only events based on their individual actions. |
|
||||
| View licenses in the [dependency list](application_security/dependency_list/index.md) | | | ✓ | ✓ | ✓ | |
|
||||
| Manage [audit streams](compliance/audit_event_streaming.md) | | | | | ✓ | |
|
||||
| View the [compliance center](compliance/compliance_center/index.md) | | | | | ✓ | |
|
||||
| Manage [compliance frameworks](group/compliance_frameworks.md) | | | | | ✓ | |
|
||||
| Assign [compliance frameworks](group/compliance_frameworks.md) to projects | | | | | ✓ | |
|
||||
| Action | Guest | Reporter | Developer | Maintainer | Owner | Notes |
|
||||
|---------------------------------------------------------------------------------------|:-----:|:--------:|:---------:|:----------:|:-----:|-------|
|
||||
| View [audit events](../administration/audit_event_reports.md) | | | ✓ | ✓ | ✓ | Users can view only events based on their individual actions. |
|
||||
| View licenses in the [dependency list](application_security/dependency_list/index.md) | | | ✓ | ✓ | ✓ | |
|
||||
| Manage [audit streams](compliance/audit_event_streaming.md) | | | | | ✓ | |
|
||||
| View the [compliance center](compliance/compliance_center/index.md) | | | | | ✓ | |
|
||||
| Manage [compliance frameworks](group/compliance_frameworks.md) | | | | | ✓ | |
|
||||
| Assign [compliance frameworks](group/compliance_frameworks.md) to projects | | | | | ✓ | |
|
||||
|
||||
### Repository
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ easier for unauthorized people to sign in to your account.
|
|||
|
||||
Some organizations require you to meet certain requirements when choosing a password.
|
||||
|
||||
Improve the security of your account with [two-factor authentication](account/two_factor_authentication.md)
|
||||
Improve the security of your account with [two-factor authentication](account/two_factor_authentication.md).
|
||||
|
||||
## Choose your password
|
||||
|
||||
|
|
|
|||
|
|
@ -112,11 +112,11 @@ To auto-format this table, use the VS Code Markdown Table formatter: `https://do
|
|||
| `/remove_zoom` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Remove Zoom meeting from this issue. |
|
||||
| `/reopen` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Reopen. |
|
||||
| `/severity <severity>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set the severity. Issue type must be `Incident`. Options for `<severity>` are `S1` ... `S4`, `critical`, `high`, `medium`, `low`, `unknown`. |
|
||||
| `/shrug <comment>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Append the comment with `¯\_(ツ)_/¯`. |
|
||||
| `/shrug` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Add `¯\_(ツ)_/¯`. |
|
||||
| `/spend <time> [<date>]` or `/spend_time <time> [<date>]` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Add or subtract spent time. Optionally, specify the date that time was spent on. For example, `/spend 1mo 2w 3d 4h 5m 2018-08-26` or `/spend -1h 30m`. For more information, see [Time tracking](time_tracking.md). Alias `/spend_time` [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/16501) in GitLab 15.6. |
|
||||
| `/submit_review` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Submit a pending review. |
|
||||
| `/subscribe` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Subscribe to notifications. |
|
||||
| `/tableflip <comment>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Append the comment with `(╯°□°)╯︵ ┻━┻`. |
|
||||
| `/tableflip` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Add `(╯°□°)╯︵ ┻━┻`. |
|
||||
| `/target_branch <local branch name>` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Set target branch. |
|
||||
| `/timeline <timeline comment> \| <date(YYYY-MM-DD)> <time(HH:MM)>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add a timeline event to this incident. For example, `/timeline DB load spiked \| 2022-09-07 09:30`. ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/368721) in GitLab 15.4). |
|
||||
| `/title <new title>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Change title. |
|
||||
|
|
@ -172,9 +172,9 @@ To auto-format this table, use the VS Code Markdown Table formatter: `https://do
|
|||
| `/remove_parent` | **{check-circle}** Yes | **{dotted-circle}** No | **{check-circle}** Yes | Removes the parent work item. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/434344) in GitLab 16.9. |
|
||||
| `/reopen` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Reopen. |
|
||||
| `/set_parent <work_item>` | **{check-circle}** Yes | **{dotted-circle}** No | **{check-circle}** Yes | Set parent work item to `<work_item>`. The `<work_item>` value should be in the format of `#iid`, `group/project#iid`, or a URL to a work item. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/420798) in GitLab 16.5. |
|
||||
| `/shrug <comment>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Append the comment with `¯\_(ツ)_/¯`. |
|
||||
| `/shrug` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Add `¯\_(ツ)_/¯`. |
|
||||
| `/subscribe` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Subscribe to notifications. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/420796) in GitLab 16.4 |
|
||||
| `/tableflip <comment>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Append the comment with `(╯°□°)╯︵ ┻━┻`. |
|
||||
| `/tableflip` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Add `(╯°□°)╯︵ ┻━┻`. |
|
||||
| `/title <new title>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Change title. |
|
||||
| `/todo` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Add a to-do item. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/412277) in GitLab 16.2. |
|
||||
| `/type` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Converts work item to specified type. Available options for `<type>` include `issue`, `task`, `objective` and `key result`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/385227) in GitLab 16.0. |
|
||||
|
|
|
|||
|
|
@ -182,8 +182,11 @@ For more information, see the [VS Code documentation](https://code.visualstudio.
|
|||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/385157) in GitLab 17.2.
|
||||
|
||||
The GitLab Workflow extension for VS Code is configured by default in workspaces.
|
||||
With this extension, you can view issues, create and review merge requests, and manage CI/CD pipelines.
|
||||
The extension also powers AI features like GitLab Duo Code Suggestions and GitLab Duo Chat.
|
||||
|
||||
With this extension, you can view issues, create merge requests, and manage CI/CD pipelines.
|
||||
This extension also powers AI features like [GitLab Duo Code Suggestions](../project/repository/code_suggestions/index.md)
|
||||
and [GitLab Duo Chat](../gitlab_duo_chat/index.md).
|
||||
|
||||
For more information, see [GitLab Workflow extension for VS Code](https://gitlab.com/gitlab-org/gitlab-vscode-extension).
|
||||
|
||||
## Extension marketplace
|
||||
|
|
@ -196,6 +199,7 @@ For more information, see the history.
|
|||
|
||||
When `allow_extensions_marketplace_in_workspace` is enabled, you can use the extension marketplace in workspaces.
|
||||
An administrator can enable or disable the flag for top-level groups only.
|
||||
|
||||
The extension marketplace connects to the [Open VSX Registry](https://open-vsx.org/).
|
||||
|
||||
## Personal access token
|
||||
|
|
@ -203,8 +207,10 @@ The extension marketplace connects to the [Open VSX Registry](https://open-vsx.o
|
|||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/129715) in GitLab 16.4.
|
||||
> - `api` permission [added](https://gitlab.com/gitlab-org/gitlab/-/issues/385157) in GitLab 17.2.
|
||||
|
||||
When you [create a workspace](configuration.md#create-a-workspace), you get a personal access token with `write_repository` and `api` permissions.
|
||||
This token is used to initially clone the project while starting the workspace and to configure the GitLab Workflow extension for VS Code.
|
||||
When you [create a workspace](configuration.md#create-a-workspace), you get a personal access token
|
||||
with `write_repository` and `api` permissions.
|
||||
This token is used to initially clone the project while starting the workspace
|
||||
and to configure the GitLab Workflow extension for VS Code.
|
||||
|
||||
Any Git operation you perform in the workspace uses this token for authentication and authorization.
|
||||
When you terminate the workspace, the token is revoked.
|
||||
|
|
|
|||
|
|
@ -38,13 +38,14 @@ module Banzai
|
|||
private
|
||||
|
||||
def render_options
|
||||
return OPTIONS unless sourcepos_disabled? || headers_disabled? || autolink_disabled?
|
||||
return OPTIONS unless sourcepos_disabled? || headers_disabled? || autolink_disabled? || raw_html_disabled?
|
||||
|
||||
OPTIONS.merge(
|
||||
sourcepos: !sourcepos_disabled?,
|
||||
header_ids: headers_disabled? ? nil : OPTIONS[:header_ids],
|
||||
autolink: !autolink_disabled?,
|
||||
relaxed_autolinks: !autolink_disabled?
|
||||
relaxed_autolinks: !autolink_disabled?,
|
||||
unsafe: !raw_html_disabled?
|
||||
)
|
||||
end
|
||||
|
||||
|
|
@ -55,6 +56,10 @@ module Banzai
|
|||
def autolink_disabled?
|
||||
context[:autolink] == false
|
||||
end
|
||||
|
||||
def raw_html_disabled?
|
||||
context[:disable_raw_html]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -15,6 +15,10 @@ module Banzai
|
|||
Filter::QuickActionFilter
|
||||
]
|
||||
end
|
||||
|
||||
def self.transform_context(context)
|
||||
context.merge(disable_raw_html: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ module Gitlab
|
|||
def by_custom_domain(host)
|
||||
domain = PagesDomain.find_by_domain_case_insensitive(host)
|
||||
|
||||
return unless domain&.enabled?
|
||||
return unless domain&.pages_deployed?
|
||||
|
||||
::Pages::VirtualDomain.new(projects: [domain.project], domain: domain)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ module Gitlab
|
|||
:merge_when_pipeline_succeeds,
|
||||
:milestone,
|
||||
:remove_source_branch,
|
||||
:squash,
|
||||
:target,
|
||||
:target_project,
|
||||
:title,
|
||||
|
|
|
|||
|
|
@ -94,8 +94,8 @@ module Gitlab
|
|||
# use a markdown based pipeline to grab possible paragraphs that might
|
||||
# contain quick actions. This ensures they are not in HTML blocks, quote blocks,
|
||||
# or code blocks.
|
||||
pipeline = Banzai::Pipeline::QuickActionPipeline.html_pipeline
|
||||
possible_paragraphs = pipeline.call(content, {}, {})[:quick_action_paragraphs]
|
||||
pipeline = Banzai::Pipeline::QuickActionPipeline
|
||||
possible_paragraphs = pipeline.call(content, {})[:quick_action_paragraphs]
|
||||
|
||||
if possible_paragraphs.present?
|
||||
content_lines = content.lines
|
||||
|
|
|
|||
|
|
@ -211,18 +211,16 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
desc { _("Append the comment with %{shrug}") % { shrug: SHRUG } }
|
||||
params '<Comment>'
|
||||
desc { _("Add %{shrug}") % { shrug: SHRUG } }
|
||||
types ::Issuable
|
||||
substitution :shrug do |comment|
|
||||
"#{comment} #{SHRUG}"
|
||||
substitution :shrug do
|
||||
SHRUG
|
||||
end
|
||||
|
||||
desc { _("Append the comment with %{tableflip}") % { tableflip: TABLEFLIP } }
|
||||
params '<Comment>'
|
||||
desc { _("Add %{tableflip}") % { tableflip: TABLEFLIP } }
|
||||
types ::Issuable
|
||||
substitution :tableflip do |comment|
|
||||
"#{comment} #{TABLEFLIP}"
|
||||
substitution :tableflip do
|
||||
TABLEFLIP
|
||||
end
|
||||
|
||||
desc { _('Set severity') }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'tokens/manage_expiry_task'
|
||||
|
||||
namespace :gitlab do
|
||||
namespace :tokens do
|
||||
desc 'GitLab | Tokens | Show information about tokens'
|
||||
task analyze: :environment do |_t, _args|
|
||||
Tasks::Gitlab::Tokens::ManageExpiryTask.new.analyze
|
||||
end
|
||||
|
||||
desc 'GitLab | Tokens | Edit expiration dates for tokens'
|
||||
task edit: :environment do |_t, _args|
|
||||
Tasks::Gitlab::Tokens::ManageExpiryTask.new.edit
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,203 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'tty-prompt'
|
||||
|
||||
module Tasks
|
||||
module Gitlab
|
||||
module Tokens
|
||||
class ManageExpiryTask
|
||||
TOTAL_WIDTH = 70
|
||||
|
||||
def analyze
|
||||
show_pat_expires_at_migration_status
|
||||
show_most_common_pat_expiration_dates
|
||||
end
|
||||
|
||||
def edit
|
||||
loop do
|
||||
analyze
|
||||
|
||||
break unless prompt_action
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def show_pat_expires_at_migration_status
|
||||
sql = <<~SQL
|
||||
SELECT * FROM batched_background_migrations
|
||||
WHERE job_class_name = 'CleanupPersonalAccessTokensWithNilExpiresAt'
|
||||
AND table_name = 'personal_access_tokens'
|
||||
AND column_name = 'id'
|
||||
SQL
|
||||
|
||||
print_header("Personal/Project/Group Access Token Expiration Migration")
|
||||
|
||||
base_model = ::Gitlab::Database.database_base_models[::Gitlab::Database::MAIN_DATABASE_NAME]
|
||||
record = base_model.connection.select_one(sql)
|
||||
|
||||
if record
|
||||
puts "Started at: #{record['started_at']}"
|
||||
puts "Finished : #{record['finished_at']}"
|
||||
else
|
||||
puts "Status: Not run"
|
||||
end
|
||||
end
|
||||
|
||||
def show_most_common_pat_expiration_dates
|
||||
print_header "Top 10 Personal/Project/Group Access Token Expiration Dates"
|
||||
|
||||
puts "| Expiration Date | Count |"
|
||||
puts "|-----------------|-------|"
|
||||
|
||||
with_most_common_pat_expiration_dates do |row|
|
||||
expiry_date = row[:expires_at] || "(none)"
|
||||
puts "| #{expiry_date.to_s.ljust(15)} | #{row[:count].to_s.ljust(5)} |"
|
||||
end
|
||||
|
||||
print_footer
|
||||
end
|
||||
|
||||
def with_most_common_pat_expiration_dates
|
||||
# rubocop:disable CodeReuse/ActiveRecord -- Rake task specifically for fixing an issue
|
||||
ApplicationRecord.with_fast_read_statement_timeout(0) do # rubocop: disable Performance/ActiveRecordSubtransactionMethods -- no subtransaction here
|
||||
PersonalAccessToken
|
||||
.select(:expires_at, Arel.sql('count(*)'))
|
||||
.group(:expires_at)
|
||||
.order(Arel.sql('count(*) DESC'))
|
||||
.order(expires_at: :desc)
|
||||
.limit(10)
|
||||
.each do |row|
|
||||
yield row
|
||||
end
|
||||
end
|
||||
# rubocop:enable CodeReuse/ActiveRecord
|
||||
end
|
||||
|
||||
def prompt_action
|
||||
prompt = TTY::Prompt.new
|
||||
|
||||
puts ""
|
||||
user_choice = prompt.select("What do you want to do?") do |menu|
|
||||
menu.enum "."
|
||||
|
||||
menu.choice "Extend expiration date", 1
|
||||
menu.choice "Remove expiration date", 2
|
||||
menu.choice "Quit", 3
|
||||
end
|
||||
|
||||
case user_choice
|
||||
when 1
|
||||
extend_expiration_date
|
||||
true
|
||||
when 2
|
||||
remove_expiration_date
|
||||
true
|
||||
when 3
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def extend_expiration_date
|
||||
old_date = prompt_expiration_date_selection
|
||||
|
||||
return unless old_date
|
||||
|
||||
prompt = TTY::Prompt.new
|
||||
|
||||
num_days = ::Gitlab::CurrentSettings.max_personal_access_token_lifetime || 365
|
||||
new_date = old_date + num_days.days
|
||||
new_date = prompt.ask("What would you like the new expiration date to be?", default: new_date)
|
||||
|
||||
new_date = Date.parse(new_date) unless new_date.is_a?(Date)
|
||||
|
||||
puts ""
|
||||
puts "Old expiration date: #{old_date}"
|
||||
puts "New expiration date: #{new_date}"
|
||||
confirmed = prompt.yes?(
|
||||
"WARNING: This will now update #{token_count(old_date)} token(s). Are you sure?",
|
||||
default: false
|
||||
)
|
||||
|
||||
if confirmed
|
||||
puts "Updating tokens..."
|
||||
update_tokens_with_expiration(old_date, new_date)
|
||||
else
|
||||
puts "Aborting!"
|
||||
end
|
||||
rescue Date::Error
|
||||
puts "Invalid date, aborting..."
|
||||
end
|
||||
|
||||
def remove_expiration_date
|
||||
old_date = prompt_expiration_date_selection
|
||||
|
||||
return unless old_date
|
||||
|
||||
prompt = TTY::Prompt.new
|
||||
|
||||
puts ""
|
||||
puts "WARNING: This will remove the expiration for tokens that expire on #{old_date}."
|
||||
confirmed = prompt.yes?("This will affect #{token_count(old_date)} tokens. Are you sure?", default: false)
|
||||
|
||||
if confirmed
|
||||
update_tokens_with_expiration(old_date, nil)
|
||||
else
|
||||
puts "Aborting!"
|
||||
end
|
||||
end
|
||||
|
||||
def update_tokens_with_expiration(old_date, new_date)
|
||||
total = 0
|
||||
# rubocop:disable CodeReuse/ActiveRecord -- Rake task specifically for fixing an issue
|
||||
PersonalAccessToken.where(expires_at: old_date).each_batch do |batch|
|
||||
puts "Updating personal access tokens from ID #{batch.minimum(:id)} to #{batch.maximum(:id)}..."
|
||||
total += batch.update_all(expires_at: new_date)
|
||||
end
|
||||
# rubocop:enable CodeReuse/ActiveRecord -- Rake task specifically for fixing an issue
|
||||
|
||||
puts "Updated #{total} tokens!"
|
||||
end
|
||||
|
||||
def prompt_expiration_date_selection
|
||||
prompt = TTY::Prompt.new
|
||||
|
||||
choices = []
|
||||
with_most_common_pat_expiration_dates do |row|
|
||||
choices << row[:expires_at] if row[:expires_at]
|
||||
end
|
||||
|
||||
abort_choice = "--> Abort"
|
||||
choices << abort_choice
|
||||
|
||||
selection = prompt.select("Select an expiration date", choices)
|
||||
selection == abort_choice ? nil : selection
|
||||
end
|
||||
|
||||
def token_count(expiration_date)
|
||||
# rubocop:disable CodeReuse/ActiveRecord -- Rake task specifically for fixing an issue
|
||||
ApplicationRecord.with_fast_read_statement_timeout(0) do # rubocop: disable Performance/ActiveRecordSubtransactionMethods -- no subtransaction here
|
||||
PersonalAccessToken.where(expires_at: expiration_date).count
|
||||
end
|
||||
# rubocop:enable CodeReuse/ActiveRecord
|
||||
end
|
||||
|
||||
def print_header(title, total_width = TOTAL_WIDTH)
|
||||
title_length = title.length
|
||||
side_length = (total_width - title_length) / 2
|
||||
|
||||
left_side = "=" * side_length
|
||||
right_side = "=" * (side_length + (total_width % 2))
|
||||
|
||||
header = "#{left_side} #{title} #{right_side}"
|
||||
puts header
|
||||
end
|
||||
|
||||
def print_footer
|
||||
# Account for the spaces between the "=" in the header
|
||||
puts "=" * (TOTAL_WIDTH + 2)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -3002,6 +3002,12 @@ msgstr ""
|
|||
msgid "Add %{linkStart}assets%{linkEnd} to your Release. GitLab automatically includes read-only assets, like source code and release evidence."
|
||||
msgstr ""
|
||||
|
||||
msgid "Add %{shrug}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Add %{tableflip}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Add CHANGELOG"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -6295,12 +6301,6 @@ msgstr ""
|
|||
msgid "AppearanceSettings|Preview last save:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Append the comment with %{shrug}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Append the comment with %{tableflip}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Append the hostname of your GitLab instance to the status check name."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@ FactoryBot.define do
|
|||
dist: {
|
||||
tarball: 'http://localhost/tarball.tgz',
|
||||
shasum: '1234567890'
|
||||
},
|
||||
scripts: {
|
||||
test: 'echo "Test"'
|
||||
}
|
||||
}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class OrderingTestOne < Gitlab::Database::Migration[2.1]
|
||||
def change
|
||||
# no op
|
||||
end
|
||||
end
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class OrderingTestTwo < Gitlab::Database::Migration[2.1]
|
||||
def change
|
||||
# no op
|
||||
end
|
||||
end
|
||||
|
|
@ -93,7 +93,7 @@ RSpec.describe LooksAhead do
|
|||
end
|
||||
|
||||
before_all do
|
||||
stub_feature_flags(epic_and_work_item_labels_unification: false)
|
||||
stub_feature_flags(epic_and_work_item_associations_unification: false)
|
||||
end
|
||||
|
||||
def run_query(gql_query)
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ RSpec.describe Banzai::Pipeline::QuickActionPipeline, feature_category: :team_pl
|
|||
.to match_array [{ start_line: 0, end_line: 2 }, { start_line: 8, end_line: 8 }]
|
||||
end
|
||||
|
||||
it 'does not a quick action in a code block' do
|
||||
it 'does not detect a quick action in a code block' do
|
||||
markdown = <<~MD.strip
|
||||
```
|
||||
Lorem ipsum
|
||||
|
|
@ -59,4 +59,15 @@ RSpec.describe Banzai::Pipeline::QuickActionPipeline, feature_category: :team_pl
|
|||
|
||||
expect(result[:quick_action_paragraphs]).to be_empty
|
||||
end
|
||||
|
||||
it 'does not detect a quick action in raw html with a sourcepos' do
|
||||
markdown = <<~MD.strip
|
||||
<p data-sourcepos="0:1-2:10">
|
||||
/quick
|
||||
</p>
|
||||
MD
|
||||
result = described_class.call(markdown, project: nil)
|
||||
|
||||
expect(result[:quick_action_paragraphs]).to be_empty
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -30,11 +30,19 @@ RSpec.describe Gitlab::Pages::VirtualHostFinder, feature_category: :pages do
|
|||
create(:pages_deployment, project: project)
|
||||
end
|
||||
|
||||
it 'returns the virual domain when there are pages deployed for the project' do
|
||||
it 'returns the virtual domain' do
|
||||
expect(virtual_domain).to be_an_instance_of(Pages::VirtualDomain)
|
||||
expect(virtual_domain.lookup_paths.length).to eq(1)
|
||||
expect(virtual_domain.lookup_paths.first.project_id).to eq(project.id)
|
||||
end
|
||||
|
||||
context 'when the domain is disabled' do
|
||||
let_it_be(:pages_domain) { create(:pages_domain, :disabled, project: project) }
|
||||
|
||||
it 'does not return the virtual domain' do
|
||||
expect(virtual_domain).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ RSpec.describe Gitlab::QuickActions::Extractor, feature_category: :team_planning
|
|||
command(:power) {}
|
||||
command(:noop_command)
|
||||
substitution(:substitution) { 'foo' }
|
||||
substitution :shrug do |comment|
|
||||
"#{comment} SHRUG"
|
||||
substitution :shrug do
|
||||
"SHRUG"
|
||||
end
|
||||
end.command_definitions
|
||||
end
|
||||
|
|
@ -241,15 +241,15 @@ RSpec.describe Gitlab::QuickActions::Extractor, feature_category: :team_planning
|
|||
msg, commands = extractor.extract_commands(msg)
|
||||
|
||||
expect(commands).to eq [['reopen'], ['shrug', 'this is great?']]
|
||||
expect(msg).to eq "hello\nworld\nthis is great? SHRUG"
|
||||
expect(msg).to eq "hello\nworld\nSHRUG"
|
||||
end
|
||||
|
||||
it 'extracts and performs multiple substitution commands' do
|
||||
msg = %(hello\nworld\n/reopen\n/shrug this is great?\n/shrug meh)
|
||||
msg = %(hello\nworld\n/reopen\n/shrug this is great?\n/shrug)
|
||||
msg, commands = extractor.extract_commands(msg)
|
||||
|
||||
expect(commands).to eq [['reopen'], ['shrug', 'this is great?'], %w[shrug meh]]
|
||||
expect(msg).to eq "hello\nworld\nthis is great? SHRUG\nmeh SHRUG"
|
||||
expect(commands).to eq [['reopen'], ['shrug', 'this is great?'], ['shrug']]
|
||||
expect(msg).to eq "hello\nworld\nSHRUG\nSHRUG"
|
||||
end
|
||||
|
||||
it 'does not extract substitution command in inline code' do
|
||||
|
|
@ -265,7 +265,7 @@ RSpec.describe Gitlab::QuickActions::Extractor, feature_category: :team_planning
|
|||
msg, commands = extractor.extract_commands(msg)
|
||||
|
||||
expect(commands).to eq [['reopen'], ['shrug', 'this is great?']]
|
||||
expect(msg).to eq "hello\nworld\nthis is great? SHRUG"
|
||||
expect(msg).to eq "hello\nworld\nSHRUG"
|
||||
end
|
||||
|
||||
it 'extracts and performs substitution commands with comments' do
|
||||
|
|
@ -336,6 +336,7 @@ RSpec.describe Gitlab::QuickActions::Extractor, feature_category: :team_planning
|
|||
context 'does not extract commands inside' do
|
||||
where(:description, :text) do
|
||||
'block HTML tags' | "Hello\r\n<div>\r\nText\r\n/close\r\n/assign @user\r\n</div>\r\n\r\nWorld"
|
||||
'raw HTML with sourcepos' | "<p data-sourcepos=\"0:1-2:10\">\r\n/close\r\n</p>"
|
||||
'inline html on seperated rows' | "Text\r\n<b>\r\n/close\r\n</b>"
|
||||
'HTML comments' | "<!--\n/assign @user\n-->"
|
||||
'blockquotes' | "> Text\r\n/reopen"
|
||||
|
|
|
|||
|
|
@ -61,4 +61,12 @@ RSpec.describe Packages::Npm::Metadatum, type: :model, feature_category: :packag
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#package_json_scripts' do
|
||||
let(:metadatum) { build_stubbed(:npm_metadatum) }
|
||||
|
||||
subject { metadatum.package_json_scripts }
|
||||
|
||||
it { is_expected.to eq({ 'test' => 'echo "Test"' }) }
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ require 'spec_helper'
|
|||
RSpec.describe GlobalPolicy, feature_category: :shared do
|
||||
include TermsHelper
|
||||
|
||||
HasUserType::BOT_USER_TYPES.each do |type| # rubocop:disable RSpec/UselessDynamicDefinition -- False positive
|
||||
type_sym = type.to_sym
|
||||
let_it_be(type_sym) { create(:user, type_sym) }
|
||||
end
|
||||
|
||||
let_it_be(:admin_user) { create(:admin) }
|
||||
let_it_be(:project_bot) { create(:user, :project_bot) }
|
||||
let_it_be(:service_account) { create(:user, :service_account) }
|
||||
let_it_be(:migration_bot) { create(:user, :migration_bot) }
|
||||
let_it_be(:security_bot) { create(:user, :security_bot) }
|
||||
let_it_be(:llm_bot) { create(:user, :llm_bot) }
|
||||
let_it_be_with_reload(:current_user) { create(:user) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
|
|
@ -705,6 +705,32 @@ RSpec.describe GlobalPolicy, feature_category: :shared do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'use_quick_actions' do
|
||||
HasUserType::BOT_USER_TYPES.each do |bot|
|
||||
context "with #{bot}" do
|
||||
let(:current_user) { public_send(bot) }
|
||||
|
||||
if bot.in?(%w[alert_bot project_bot support_bot admin_bot service_account])
|
||||
it { is_expected.to be_allowed(:use_quick_actions) }
|
||||
else
|
||||
it { is_expected.to be_disallowed(:use_quick_actions) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with regular user' do
|
||||
let(:current_user) { user }
|
||||
|
||||
it { is_expected.to be_allowed(:use_quick_actions) }
|
||||
end
|
||||
|
||||
context 'with anonymous' do
|
||||
let(:current_user) { nil }
|
||||
|
||||
it { is_expected.to be_disallowed(:use_quick_actions) }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'create_organization' do
|
||||
context 'with regular user' do
|
||||
let(:current_user) { user }
|
||||
|
|
|
|||
|
|
@ -1458,5 +1458,12 @@ RSpec.describe Issues::UpdateService, :mailer, feature_category: :team_planning
|
|||
let(:existing_issue) { create(:issue, project: project) }
|
||||
let(:issuable) { described_class.new(container: project, current_user: user, params: params).execute(existing_issue) }
|
||||
end
|
||||
|
||||
it_behaves_like 'issuable record does not run quick actions when not editing description' do
|
||||
let(:label) { create(:label, project: project) }
|
||||
let(:assignee) { create(:user, maintainer_of: project) }
|
||||
let(:existing_issue) { create(:issue, project: project, description: old_description) }
|
||||
let(:updated_issuable) { described_class.new(container: project, current_user: user, params: params).execute(existing_issue) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -391,7 +391,9 @@ RSpec.describe MergeRequests::CreateService, :clean_gitlab_redis_shared_state, f
|
|||
}
|
||||
end
|
||||
|
||||
let(:issuable) { described_class.new(project: project, current_user: user, params: params).execute }
|
||||
let(:issuable) do
|
||||
described_class.new(project: project, current_user: user, params: params.merge(default_params)).execute
|
||||
end
|
||||
end
|
||||
|
||||
context 'Quick actions' do
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ RSpec.describe MergeRequests::PushOptionsHandlerService, feature_category: :sour
|
|||
let(:title) { 'my title' }
|
||||
let(:draft_title) { 'Draft: my title' }
|
||||
let(:draft) { true }
|
||||
let(:squash) { true }
|
||||
let(:description) { 'my description' }
|
||||
let(:multiline_description) do
|
||||
<<~MD.chomp
|
||||
|
|
@ -105,6 +106,16 @@ RSpec.describe MergeRequests::PushOptionsHandlerService, feature_category: :sour
|
|||
end
|
||||
end
|
||||
|
||||
shared_examples_for 'a service that can set a merge request to be squashed' do
|
||||
subject(:last_mr) { MergeRequest.last }
|
||||
|
||||
it 'sets the squash property' do
|
||||
service.execute
|
||||
|
||||
expect(last_mr.squash).to eq(squash)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples_for 'a service that can set the milestone of a merge request' do
|
||||
subject(:last_mr) { MergeRequest.last }
|
||||
|
||||
|
|
@ -572,6 +583,71 @@ RSpec.describe MergeRequests::PushOptionsHandlerService, feature_category: :sour
|
|||
it_behaves_like 'with the project default branch'
|
||||
end
|
||||
|
||||
describe '`squash` push option' do
|
||||
let(:push_options) { { squash: squash } }
|
||||
|
||||
context 'with a new branch' do
|
||||
let(:changes) { new_branch_changes }
|
||||
|
||||
it_behaves_like 'a service that does not create a merge request'
|
||||
|
||||
it 'adds an error to the service' do
|
||||
service.execute
|
||||
|
||||
expect(service.errors).to include(error_mr_required)
|
||||
end
|
||||
|
||||
context 'when coupled with the `create` push option' do
|
||||
let(:push_options) { { create: true, squash: squash } }
|
||||
|
||||
it_behaves_like 'a service that can create a merge request'
|
||||
it_behaves_like 'a service that can set a merge request to be squashed'
|
||||
|
||||
context 'when squash is false' do
|
||||
let(:squash) { false }
|
||||
|
||||
it_behaves_like 'a service that can set a merge request to be squashed'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an existing branch but no open MR' do
|
||||
let(:changes) { existing_branch_changes }
|
||||
|
||||
it_behaves_like 'a service that does not create a merge request'
|
||||
|
||||
it 'adds an error to the service' do
|
||||
service.execute
|
||||
|
||||
expect(service.errors).to include(error_mr_required)
|
||||
end
|
||||
|
||||
context 'when coupled with the `create` push option' do
|
||||
let(:push_options) { { create: true, squash: squash } }
|
||||
|
||||
it_behaves_like 'a service that can create a merge request'
|
||||
it_behaves_like 'a service that can set a merge request to be squashed'
|
||||
|
||||
context 'when squash is false' do
|
||||
let(:squash) { false }
|
||||
|
||||
it_behaves_like 'a service that can set a merge request to be squashed'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an existing branch that has a merge request open' do
|
||||
let(:changes) { existing_branch_changes }
|
||||
let!(:merge_request) { create(:merge_request, source_project: project, source_branch: source_branch) }
|
||||
|
||||
it_behaves_like 'a service that does not create a merge request'
|
||||
it_behaves_like 'a service that can set a merge request to be squashed'
|
||||
end
|
||||
|
||||
it_behaves_like 'with a deleted branch'
|
||||
it_behaves_like 'with the project default branch'
|
||||
end
|
||||
|
||||
describe '`label` push option' do
|
||||
let(:push_options) { { label: { label1 => 1, label2 => 1 } } }
|
||||
|
||||
|
|
|
|||
|
|
@ -1383,6 +1383,13 @@ RSpec.describe MergeRequests::UpdateService, :mailer, feature_category: :code_re
|
|||
let(:issuable) { described_class.new(project: project, current_user: user, params: params).execute(existing_merge_request) }
|
||||
end
|
||||
|
||||
it_behaves_like 'issuable record does not run quick actions when not editing description' do
|
||||
let(:label) { create(:label, project: project) }
|
||||
let(:assignee) { create(:user, maintainer_of: project) }
|
||||
let(:existing_merge_request) { create(:merge_request, source_project: project, description: old_description) }
|
||||
let(:updated_issuable) { described_class.new(project: project, current_user: user, params: params).execute(existing_merge_request) }
|
||||
end
|
||||
|
||||
context 'updating labels' do
|
||||
context 'when merge request is not merged' do
|
||||
let(:label_a) { label }
|
||||
|
|
|
|||
|
|
@ -387,7 +387,7 @@ RSpec.describe Notes::QuickActionsService, feature_category: :team_planning do
|
|||
let_it_be(:child) { create(:work_item, :objective, project: project) }
|
||||
let_it_be(:second_child) { create(:work_item, :objective, project: project) }
|
||||
let_it_be(:note_text) { "/add_child #{child.to_reference}, #{second_child.to_reference}" }
|
||||
let_it_be(:note) { create(:note, noteable: noteable, project: project, note: note_text) }
|
||||
let_it_be(:note) { build(:note, noteable: noteable, project: project, note: note_text) }
|
||||
let_it_be(:children) { [child, second_child] }
|
||||
|
||||
shared_examples 'adds child work items' do
|
||||
|
|
@ -423,7 +423,7 @@ RSpec.describe Notes::QuickActionsService, feature_category: :team_planning do
|
|||
let_it_be_with_reload(:noteable) { create(:work_item, :objective, project: project) }
|
||||
let_it_be_with_reload(:child) { create(:work_item, :objective, project: project) }
|
||||
let_it_be(:note_text) { "/remove_child #{child.to_reference}" }
|
||||
let_it_be(:note) { create(:note, noteable: noteable, project: project, note: note_text) }
|
||||
let_it_be(:note) { build(:note, noteable: noteable, project: project, note: note_text) }
|
||||
|
||||
before do
|
||||
create(:parent_link, work_item_parent: noteable, work_item: child)
|
||||
|
|
@ -465,7 +465,7 @@ RSpec.describe Notes::QuickActionsService, feature_category: :team_planning do
|
|||
let_it_be_with_reload(:noteable) { create(:work_item, :objective, project: project) }
|
||||
let_it_be_with_reload(:parent) { create(:work_item, :objective, project: project) }
|
||||
let_it_be(:note_text) { "/set_parent #{parent.to_reference}" }
|
||||
let_it_be(:note) { create(:note, noteable: noteable, project: project, note: note_text) }
|
||||
let_it_be(:note) { build(:note, noteable: noteable, project: project, note: note_text) }
|
||||
|
||||
shared_examples 'sets work item parent' do
|
||||
it 'leaves the note empty' do
|
||||
|
|
@ -536,7 +536,7 @@ RSpec.describe Notes::QuickActionsService, feature_category: :team_planning do
|
|||
context 'when user is not allowed to promote work item' do
|
||||
let_it_be_with_reload(:noteable) { create(:work_item, :task, project: project) }
|
||||
let_it_be(:note_text) { '/promote_to issue' }
|
||||
let_it_be(:note) { create(:note, noteable: noteable, project: project, note: note_text) }
|
||||
let_it_be(:note) { build(:note, noteable: noteable, project: project, note: note_text) }
|
||||
|
||||
before do
|
||||
project.team.find_member(maintainer.id).destroy!
|
||||
|
|
@ -551,7 +551,7 @@ RSpec.describe Notes::QuickActionsService, feature_category: :team_planning do
|
|||
context 'on a task' do
|
||||
let_it_be_with_reload(:noteable) { create(:work_item, :task, project: project) }
|
||||
let_it_be(:note_text) { '/promote_to Issue' }
|
||||
let_it_be(:note) { create(:note, noteable: noteable, project: project, note: note_text) }
|
||||
let_it_be(:note) { build(:note, noteable: noteable, project: project, note: note_text) }
|
||||
|
||||
it_behaves_like 'promotes work item', from: 'task', to: 'issue'
|
||||
|
||||
|
|
@ -565,7 +565,7 @@ RSpec.describe Notes::QuickActionsService, feature_category: :team_planning do
|
|||
context 'on an issue' do
|
||||
let_it_be_with_reload(:noteable) { create(:work_item, :issue, project: project) }
|
||||
let_it_be(:note_text) { '/promote_to Incident' }
|
||||
let_it_be(:note) { create(:note, noteable: noteable, project: project, note: note_text) }
|
||||
let_it_be(:note) { build(:note, noteable: noteable, project: project, note: note_text) }
|
||||
|
||||
it_behaves_like 'promotes work item', from: 'issue', to: 'incident'
|
||||
|
||||
|
|
@ -576,6 +576,38 @@ RSpec.describe Notes::QuickActionsService, feature_category: :team_planning do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when existing note contains quick actions' do
|
||||
let(:note_text) { "foo\n/close\nbar" }
|
||||
|
||||
before do
|
||||
note.save!
|
||||
note.note = edit_note_text
|
||||
end
|
||||
|
||||
context 'when a quick action exists in original note' do
|
||||
let(:edit_note_text) { "foo\n/close\nbar\nbaz" }
|
||||
|
||||
it 'sanitizes/removes any quick actions and does not execute them' do
|
||||
content = execute(note)
|
||||
|
||||
expect(content).to eq "foo\nbar\nbaz"
|
||||
expect(note.noteable.open?).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a new quick action is used in new note' do
|
||||
let(:edit_note_text) { "bar\n/react :smile:\nfoo" }
|
||||
|
||||
it 'executes any quick actions not in unedited note' do
|
||||
content = execute(note)
|
||||
|
||||
expect(content).to eq "bar\nfoo"
|
||||
expect(note.noteable.award_emoji.first.name).to eq 'smile'
|
||||
expect(note.noteable.open?).to be_truthy
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.supported?' do
|
||||
|
|
|
|||
|
|
@ -135,6 +135,35 @@ RSpec.describe Notes::UpdateService, feature_category: :team_planning do
|
|||
])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when existing note contains quick actions' do
|
||||
let!(:note) { create(:note, project: project, noteable: issue, author: user2, note: "foo\n/close\nbar") }
|
||||
|
||||
before do
|
||||
update_note(edit_note_text)
|
||||
note.reload
|
||||
note.noteable.reload
|
||||
end
|
||||
|
||||
context 'when a quick action exists in original note' do
|
||||
let(:edit_note_text) { { note: "foo\n/close\nbar\nbaz" } }
|
||||
|
||||
it 'sanitizes/removes any quick actions and does not execute them' do
|
||||
expect(note.note).to eq "foo\nbar\nbaz"
|
||||
expect(note.noteable.open?).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a new quick action is used in new note' do
|
||||
let(:edit_note_text) { { note: "bar\n/react :smile:\nfoo" } }
|
||||
|
||||
it 'executes any quick actions not in unedited note' do
|
||||
expect(note.note).to eq "bar\nfoo"
|
||||
expect(note.noteable.award_emoji.first.name).to eq 'smile'
|
||||
expect(note.noteable.open?).to be_truthy
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when note text was not changed' do
|
||||
|
|
|
|||
|
|
@ -0,0 +1,124 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Packages::Npm::CheckManifestCoherenceService, :aggregate_failures, feature_category: :package_registry do
|
||||
let_it_be(:package) { build(:npm_package) }
|
||||
let_it_be(:package_metadata) { build(:npm_metadatum, package: package) }
|
||||
let(:tar_header) { Gem::Package::TarHeader.new(name: 'json', size: package_json.size, mode: 0o644, prefix: '') }
|
||||
let(:package_json_entry) { Gem::Package::TarReader::Entry.new(tar_header, StringIO.new(package_json)) }
|
||||
let(:service) { described_class.new(package, package_json_entry) }
|
||||
|
||||
subject(:execute_service) { service.execute }
|
||||
|
||||
describe '#execute' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
let(:package_name) { package.name }
|
||||
let(:package_version) { package.version }
|
||||
let(:package_scripts) { package_metadata.package_json_scripts }
|
||||
|
||||
where(:name, :version, :scripts, :coherent) do
|
||||
ref(:package_name) | ref(:package_version) | ref(:package_scripts) | true
|
||||
'foo' | ref(:package_version) | ref(:package_scripts) | false
|
||||
ref(:package_name) | '5.0.3' | ref(:package_scripts) | false
|
||||
ref(:package_name) | ref(:package_version) | { test: 'different script' } | false
|
||||
'foo' | '5.0.3' | { test: 'different script' } | false
|
||||
end
|
||||
|
||||
with_them do
|
||||
let(:package_json) do
|
||||
{
|
||||
name: name,
|
||||
version: version,
|
||||
scripts: scripts
|
||||
}.to_json
|
||||
end
|
||||
|
||||
if params[:coherent]
|
||||
it { is_expected.to be_success }
|
||||
else
|
||||
it 'raises a mismatch error' do
|
||||
expect { execute_service }
|
||||
.to raise_error(described_class::MismatchError, 'Package manifest is not coherent')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the package metadata is missing' do
|
||||
let(:package_json) { { name: package_name, version: package_version, scripts: {} }.to_json }
|
||||
|
||||
before do
|
||||
package.npm_metadatum = nil
|
||||
end
|
||||
|
||||
it { is_expected.to be_success }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'SajHandler' do
|
||||
let(:handler) { described_class::SajHandler.new }
|
||||
|
||||
describe 'parsing a json document' do
|
||||
let(:json) { { name: 'test', version: '1.2.3', scripts: { test: 'echo "test"' } }.to_json }
|
||||
|
||||
subject(:parse) { ::Oj.saj_parse(handler, json) }
|
||||
|
||||
it 'extracts the fields' do
|
||||
expect { parse }.to raise_error(described_class::SajHandler::ParsingDoneError)
|
||||
|
||||
expect(handler.name).to eq('test')
|
||||
expect(handler.version).to eq('1.2.3')
|
||||
expect(handler.scripts).to eq('test' => 'echo "test"')
|
||||
end
|
||||
|
||||
context 'with a different fields order' do
|
||||
let(:json) { { scripts: { test: 'echo "test"' }, name: 'test', version: '1.2.3' }.to_json }
|
||||
|
||||
it 'extracts the fields' do
|
||||
expect { parse }.to raise_error(described_class::SajHandler::ParsingDoneError)
|
||||
|
||||
expect(handler.name).to eq('test')
|
||||
expect(handler.version).to eq('1.2.3')
|
||||
expect(handler.scripts).to eq('test' => 'echo "test"')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with no scripts field' do
|
||||
let(:json) { { name: 'test', version: '1.2.3' }.to_json }
|
||||
|
||||
it 'extracts the name and version fields' do
|
||||
expect { parse }.not_to raise_error
|
||||
|
||||
expect(handler.name).to eq('test')
|
||||
expect(handler.version).to eq('1.2.3')
|
||||
expect(handler.scripts).to eq({})
|
||||
end
|
||||
end
|
||||
|
||||
context 'with very large fields' do
|
||||
let(:json) do
|
||||
{
|
||||
name: 'test',
|
||||
version: '1.2.3',
|
||||
scripts: { test: 'echo "test"' },
|
||||
extra: 'test' * 10000,
|
||||
another_hash: { large_field: 'aaaa' * 10000 }
|
||||
}.to_json
|
||||
end
|
||||
|
||||
it 'avoids extract large fields' do
|
||||
expect(handler).to receive(:hash_start).twice.and_call_original
|
||||
expect(handler).to receive(:hash_end).once.and_call_original
|
||||
expect(handler).to receive(:add_value).exactly(3).times.and_call_original
|
||||
|
||||
expect { parse }.to raise_error(described_class::SajHandler::ParsingDoneError)
|
||||
|
||||
expect(handler.name).to eq('test')
|
||||
expect(handler.version).to eq('1.2.3')
|
||||
expect(handler.scripts).to eq('test' => 'echo "test"')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -20,6 +20,10 @@ RSpec.describe ::Packages::Npm::ProcessPackageFileService, feature_category: :pa
|
|||
package.project_id,
|
||||
package.name
|
||||
)
|
||||
expect_next_instance_of(::Packages::Npm::CheckManifestCoherenceService, package,
|
||||
instance_of(Gem::Package::TarReader::Entry)) do |service|
|
||||
expect(service).to receive(:execute)
|
||||
end
|
||||
expect(package).to receive(:default!)
|
||||
|
||||
service.execute
|
||||
|
|
|
|||
|
|
@ -707,18 +707,18 @@ RSpec.describe QuickActions::InterpretService, feature_category: :team_planning
|
|||
end
|
||||
|
||||
shared_examples 'shrug command' do
|
||||
it 'appends ¯\_(ツ)_/¯ to the comment' do
|
||||
it 'adds ¯\_(ツ)_/¯' do
|
||||
new_content, _, _ = service.execute(content, issuable)
|
||||
|
||||
expect(new_content).to end_with(described_class::SHRUG)
|
||||
expect(new_content).to eq(described_class::SHRUG)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'tableflip command' do
|
||||
it 'appends (╯°□°)╯︵ ┻━┻ to the comment' do
|
||||
it 'adds (╯°□°)╯︵ ┻━┻' do
|
||||
new_content, _, _ = service.execute(content, issuable)
|
||||
|
||||
expect(new_content).to end_with(described_class::TABLEFLIP)
|
||||
expect(new_content).to eq(described_class::TABLEFLIP)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -2204,7 +2204,7 @@ RSpec.describe QuickActions::InterpretService, feature_category: :team_planning
|
|||
text, commands = service.execute(content, issue, only: [:shrug])
|
||||
|
||||
expect(commands).to be_empty
|
||||
expect(text).to eq("test #{described_class::SHRUG}\n/close")
|
||||
expect(text).to eq("#{described_class::SHRUG}\n/close")
|
||||
end
|
||||
|
||||
it 'preserves leading whitespace' do
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ RSpec.describe WorkItems::CreateService, feature_category: :team_planning do
|
|||
it 'saves the work item and applies the quick action' do
|
||||
expect(service_result).to be_success
|
||||
expect(work_item).to be_persisted
|
||||
expect(work_item.description).to eq(' ¯\_(ツ)_/¯')
|
||||
expect(work_item.description).to eq('¯\_(ツ)_/¯')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -87,7 +87,78 @@ RSpec.describe WorkItems::UpdateService, feature_category: :team_planning do
|
|||
it 'applies the quick action' do
|
||||
expect do
|
||||
update_work_item
|
||||
end.to change(work_item, :description).to(' ¯\_(ツ)_/¯')
|
||||
end.to change(work_item, :description).to('¯\_(ツ)_/¯')
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'issuable record that supports quick actions' do
|
||||
let(:opts) { params }
|
||||
let(:current_user) { user }
|
||||
let(:work_item) { create(:work_item, project: project, description: "some random description") }
|
||||
let(:issuable) { update_work_item[:work_item] }
|
||||
end
|
||||
|
||||
it_behaves_like 'issuable record that supports quick actions', with_widgets: true do
|
||||
let(:opts) { params }
|
||||
let(:current_user) { user }
|
||||
let(:work_item) { create(:work_item, project: project, description: "some random description") }
|
||||
let(:issuable) { update_work_item[:work_item] }
|
||||
end
|
||||
|
||||
it_behaves_like 'issuable record does not run quick actions when not editing description' do
|
||||
let(:label) { create(:label, project: project) }
|
||||
let(:assignee) { create(:user, maintainer_of: project) }
|
||||
let(:work_item) { create(:work_item, project: project, description: old_description) }
|
||||
let(:opts) { params }
|
||||
let(:updated_issuable) { update_work_item[:work_item] }
|
||||
end
|
||||
|
||||
it_behaves_like 'issuable record does not run quick actions when not editing description', with_widgets: true do
|
||||
let(:label) { create(:label, project: project) }
|
||||
let(:assignee) { create(:user, maintainer_of: project) }
|
||||
let(:work_item) { create(:work_item, project: project, description: old_description) }
|
||||
let(:opts) { params }
|
||||
let(:updated_issuable) { update_work_item[:work_item] }
|
||||
end
|
||||
|
||||
context 'when work item type is not default issue' do
|
||||
# when quick actions are passed in `description` param those are not interpreted and just saved into description
|
||||
# as plain text
|
||||
#
|
||||
# it_behaves_like 'issuable record that does not supports quick actions' do
|
||||
# let(:opts) { params }
|
||||
# let(:current_user) { user }
|
||||
# let(:work_item) { create(:work_item, :task, project: project, description: "some random description") }
|
||||
# let(:issuable) { update_work_item[:work_item] }
|
||||
# end
|
||||
|
||||
it_behaves_like 'issuable record that supports quick actions', with_widgets: true do
|
||||
let(:opts) { params }
|
||||
let(:current_user) { user }
|
||||
let(:work_item) { create(:work_item, :task, project: project, description: "some random description") }
|
||||
let(:issuable) { update_work_item[:work_item] }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when work item labels widget is disabled' do
|
||||
before do
|
||||
widget_definitions = WorkItems::Type.default_by_type(:issue).widget_definitions
|
||||
widget_definitions.find_by_widget_type(:labels).update!(disabled: true)
|
||||
widget_definitions.find_by_widget_type(:assignees).update!(disabled: true)
|
||||
end
|
||||
|
||||
it_behaves_like 'issuable record that does not supports quick actions' do
|
||||
let(:opts) { params }
|
||||
let(:current_user) { user }
|
||||
let(:work_item) { create(:work_item, project: project, description: "some random description") }
|
||||
let(:issuable) { update_work_item[:work_item] }
|
||||
end
|
||||
|
||||
it_behaves_like 'issuable record that does not supports quick actions', with_widgets: true do
|
||||
let(:opts) { params }
|
||||
let(:current_user) { user }
|
||||
let(:work_item) { create(:work_item, project: project, description: "some random description") }
|
||||
let(:issuable) { update_work_item[:work_item] }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -172,7 +243,7 @@ RSpec.describe WorkItems::UpdateService, feature_category: :team_planning do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when decription is changed' do
|
||||
context 'when description is changed' do
|
||||
let(:opts) { { description: 'description changed' } }
|
||||
|
||||
include_examples 'publish WorkItems::WorkItemUpdatedEvent event',
|
||||
|
|
@ -197,7 +268,7 @@ RSpec.describe WorkItems::UpdateService, feature_category: :team_planning do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when decription is not changed' do
|
||||
context 'when description is not changed' do
|
||||
let(:opts) { { title: 'title changed' } }
|
||||
|
||||
it 'does not trigger GraphQL description updated subscription' do
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ RSpec.shared_examples 'update work item description widget' do
|
|||
it_behaves_like 'quick action is applied' do
|
||||
let(:new_description) { "/tableflip updated description\n/shrug\n/cc @#{developer.username}" }
|
||||
# note: \cc performs no action since 15.0
|
||||
let(:filtered_description) { "updated description (╯°□°)╯︵ ┻━┻\n ¯\\_(ツ)_/¯\n/cc @#{developer.username}" }
|
||||
let(:filtered_description) { "(╯°□°)╯︵ ┻━┻\n¯\\_(ツ)_/¯\n/cc @#{developer.username}" }
|
||||
let(:expected_response) do
|
||||
{
|
||||
'widgets' => include({
|
||||
|
|
|
|||
|
|
@ -5,15 +5,33 @@ RSpec.shared_examples 'search results filtered by labels' do
|
|||
let_it_be(:labeled_issue) { create(:labeled_issue, labels: [project_label], project: project, title: 'foo project') }
|
||||
let_it_be(:unlabeled_issue) { create(:issue, project: project, title: 'foo unlabeled') }
|
||||
|
||||
let(:filters) { { labels: [project_label.id] } }
|
||||
|
||||
before do
|
||||
::Elastic::ProcessBookkeepingService.track!(labeled_issue)
|
||||
::Elastic::ProcessBookkeepingService.track!(unlabeled_issue)
|
||||
ensure_elasticsearch_index!
|
||||
end
|
||||
|
||||
it 'filters by labels', :sidekiq_inline do
|
||||
expect(results.objects(scope)).to contain_exactly(labeled_issue)
|
||||
context 'when labels filter is provided' do
|
||||
let(:filters) { { labels: [project_label.id] } }
|
||||
|
||||
it 'filters by labels', :sidekiq_inline do
|
||||
expect(results.objects(scope)).to contain_exactly(labeled_issue)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when label_name filter is provided' do
|
||||
let(:filters) { { label_name: [project_label.name] } }
|
||||
|
||||
it 'filters by labels', :sidekiq_inline do
|
||||
expect(results.objects(scope)).to contain_exactly(labeled_issue)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when both labels and label_name filters are provided' do
|
||||
let(:filters) { { labels: [0], label_name: [project_label.name] } }
|
||||
|
||||
it 'uses label_name filter and filters by labels', :sidekiq_inline do
|
||||
expect(results.objects(scope)).to contain_exactly(labeled_issue)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -218,13 +218,13 @@ RSpec.shared_examples 'issuable quick actions' do
|
|||
QuickAction.new(
|
||||
action_text: "/shrug oops",
|
||||
expectation: ->(noteable, can_use_quick_action) {
|
||||
expect(noteable.notes&.last&.note == "HELLO\noops ¯\\_(ツ)_/¯\nWORLD").to eq(can_use_quick_action)
|
||||
expect(noteable.notes&.last&.note == "HELLO\n¯\\_(ツ)_/¯\nWORLD").to eq(can_use_quick_action)
|
||||
}
|
||||
),
|
||||
QuickAction.new(
|
||||
action_text: "/tableflip oops",
|
||||
expectation: ->(noteable, can_use_quick_action) {
|
||||
expect(noteable.notes&.last&.note == "HELLO\noops (╯°□°)╯︵ ┻━┻\nWORLD").to eq(can_use_quick_action)
|
||||
expect(noteable.notes&.last&.note == "HELLO\n(╯°□°)╯︵ ┻━┻\nWORLD").to eq(can_use_quick_action)
|
||||
}
|
||||
),
|
||||
QuickAction.new(
|
||||
|
|
|
|||
|
|
@ -3,15 +3,19 @@
|
|||
# Specifications for behavior common to all objects with executable attributes.
|
||||
# It can take a `default_params`.
|
||||
|
||||
RSpec.shared_examples 'issuable record that supports quick actions' do
|
||||
RSpec.shared_examples 'issuable record that does not supports quick actions' do |with_widgets: false|
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
let_it_be(:user) { create(:user, maintainer_of: project) }
|
||||
let_it_be(:assignee) { create(:user, maintainer_of: project) }
|
||||
let_it_be(:milestone) { create(:milestone, project: project) }
|
||||
let_it_be(:labels) { create_list(:label, 3, project: project) }
|
||||
|
||||
let(:new_descr) { "some updated description" }
|
||||
let(:base_params) { { title: 'My issuable title' } }
|
||||
let(:params) { base_params.merge(defined?(default_params) ? default_params : {}).merge(example_params) }
|
||||
let(:params) { base_params.merge(with_widgets ? { label_ids: example_params.delete(:label_ids) } : example_params) }
|
||||
let(:widget_params) do
|
||||
with_widgets ? { description_widget: { description: example_params.delete(:description) } } : {}
|
||||
end
|
||||
|
||||
before do
|
||||
issuable.reload
|
||||
|
|
@ -20,12 +24,13 @@ RSpec.shared_examples 'issuable record that supports quick actions' do
|
|||
context 'with labels in command only' do
|
||||
let(:example_params) do
|
||||
{
|
||||
description: "/label ~#{labels.first.name} ~#{labels.second.name}\n/unlabel ~#{labels.third.name}"
|
||||
description: "#{new_descr}\n/label ~#{labels.first.name} ~#{labels.second.name}\n/unlabel ~#{labels.third.name}"
|
||||
}
|
||||
end
|
||||
|
||||
it 'attaches labels to issuable' do
|
||||
expect(issuable.label_ids).to match_array([labels.first.id, labels.second.id])
|
||||
expect(issuable.label_ids).to be_empty
|
||||
expect(issuable.description).to eq new_descr
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -33,25 +38,159 @@ RSpec.shared_examples 'issuable record that supports quick actions' do
|
|||
let(:example_params) do
|
||||
{
|
||||
label_ids: [labels.second.id],
|
||||
description: "/label ~#{labels.first.name}\n/unlabel ~#{labels.third.name}"
|
||||
description: "#{new_descr}\n/label ~#{labels.first.name}\n/unlabel ~#{labels.third.name}"
|
||||
}
|
||||
end
|
||||
|
||||
it 'attaches all labels to issuable' do
|
||||
expect(issuable.label_ids).to match_array([labels.first.id, labels.second.id])
|
||||
expect(issuable.label_ids).to match_array([labels.second.id])
|
||||
expect(issuable.description).to eq new_descr
|
||||
end
|
||||
end
|
||||
|
||||
context 'with assignee and milestone in command only' do
|
||||
let(:example_params) do
|
||||
{
|
||||
description: %(/assign @#{assignee.username}\n/milestone %"#{milestone.name}")
|
||||
description: %(#{new_descr}\n/assign @#{assignee.username}\n/milestone %"#{milestone.name}")
|
||||
}
|
||||
end
|
||||
|
||||
it 'assigns and sets milestone to issuable' do
|
||||
expect(issuable.assignees).to be_empty
|
||||
expect(issuable.milestone).to be_nil
|
||||
expect(issuable.description).to eq new_descr
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.shared_examples 'issuable record that supports quick actions' do |with_widgets: false|
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
let_it_be(:user) { create(:user, maintainer_of: project) }
|
||||
let_it_be(:assignee) { create(:user, maintainer_of: project) }
|
||||
let_it_be(:milestone) { create(:milestone, project: project) }
|
||||
let_it_be(:labels) { create_list(:label, 3, project: project) }
|
||||
|
||||
let(:new_descr) { "some updated description" }
|
||||
let(:base_params) { { title: 'My issuable title' } }
|
||||
let(:params) { base_params.merge(with_widgets ? { label_ids: example_params.delete(:label_ids) } : example_params) }
|
||||
let(:widget_params) do
|
||||
with_widgets ? { description_widget: { description: example_params.delete(:description) } } : {}
|
||||
end
|
||||
|
||||
before do
|
||||
issuable.reload
|
||||
end
|
||||
|
||||
context 'with labels in command only' do
|
||||
let(:example_params) do
|
||||
{
|
||||
description: "#{new_descr}\n/label ~#{labels.first.name} ~#{labels.second.name}\n/unlabel ~#{labels.third.name}"
|
||||
}
|
||||
end
|
||||
|
||||
it 'attaches labels to issuable' do
|
||||
expect(issuable.label_ids).to match_array([labels.first.id, labels.second.id])
|
||||
expect(issuable.description).to eq new_descr
|
||||
end
|
||||
end
|
||||
|
||||
context 'with labels in params and command' do
|
||||
let(:example_params) do
|
||||
{
|
||||
label_ids: [labels.second.id],
|
||||
description: "#{new_descr}\n/label ~#{labels.first.name}\n/unlabel ~#{labels.third.name}"
|
||||
}
|
||||
end
|
||||
|
||||
it 'attaches all labels to issuable' do
|
||||
expect(issuable.label_ids).to match_array([labels.first.id, labels.second.id])
|
||||
expect(issuable.description).to eq new_descr
|
||||
end
|
||||
end
|
||||
|
||||
context 'with assignee and milestone in command only' do
|
||||
let(:example_params) do
|
||||
{
|
||||
description: %(#{new_descr}\n/assign @#{assignee.username}\n/milestone %"#{milestone.name}")
|
||||
}
|
||||
end
|
||||
|
||||
it 'assigns and sets milestone to issuable' do
|
||||
expect(issuable.assignees).to eq([assignee])
|
||||
expect(issuable.milestone).to eq(milestone)
|
||||
expect(issuable.description).to eq new_descr
|
||||
# WorkItem milestone widget does not support quick the action yet
|
||||
expect(issuable.milestone).to eq(milestone) unless issuable.is_a?(WorkItem)
|
||||
expect(issuable.milestone).to be_nil if issuable.is_a?(WorkItem)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.shared_examples 'issuable record does not run quick actions when not editing description' do |with_widgets: false|
|
||||
let(:residual_quick_actions) { "/label ~#{label.name}\n/assign @#{assignee.username}" }
|
||||
let(:old_description) { "foo\n#{residual_quick_actions}\nbar" }
|
||||
let(:base_params) { { title: 'My issuable title' } }
|
||||
let(:params) { base_params.merge(with_widgets ? {} : description_param) }
|
||||
let(:widget_params) { with_widgets ? { description_widget: description_param } : {} }
|
||||
|
||||
before do
|
||||
updated_issuable.reload
|
||||
end
|
||||
|
||||
context 'when no description param is provided' do
|
||||
let(:description_param) { {} }
|
||||
|
||||
it 'sanitizes/removes any residual quick actions and does not execute them' do
|
||||
expect(updated_issuable.description).to eq "foo\nbar"
|
||||
expect(updated_issuable.labels).to be_empty
|
||||
expect(updated_issuable.assignees).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'when description param is provided' do
|
||||
let(:description_param) { { description: "bar\n/react :smile:\nfoo" } }
|
||||
|
||||
it 'executes only quick actions provided in the description param and skips residual quick actions' do
|
||||
expect(updated_issuable.description).to eq "bar\nfoo"
|
||||
expect(updated_issuable.award_emoji.first.name).to eq 'smile'
|
||||
expect(updated_issuable.labels).to be_empty
|
||||
expect(updated_issuable.assignees).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'when original description is replaced by description containing a residual quick action' do
|
||||
let(:description_param) do
|
||||
{ description: "bar\n/react :smile:\n#{residual_quick_actions}\nfoo" }
|
||||
end
|
||||
|
||||
# side-effect of not executing the residual quick actions resulting in a quick action not being executed
|
||||
# even if provided by the user when editing the description
|
||||
it 'executes only the non residual quick actions even though provided in description param' do
|
||||
expect(updated_issuable.description).to eq "bar\nfoo"
|
||||
expect(updated_issuable.award_emoji.first.name).to eq 'smile'
|
||||
expect(updated_issuable.labels).to be_empty
|
||||
expect(updated_issuable.assignees).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'when prepending description with new content' do
|
||||
let(:description_param) { { description: "bar\n/react :smile:\nfoo\n\n#{old_description}" } }
|
||||
|
||||
it 'executes only the non residual quick actions' do
|
||||
expect(updated_issuable.description).to eq "bar\nfoo\n\nfoo\nbar"
|
||||
expect(updated_issuable.award_emoji.first.name).to eq 'smile'
|
||||
expect(updated_issuable.labels).to be_empty
|
||||
expect(updated_issuable.assignees).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'when appending description with new content' do
|
||||
let(:description_param) { { description: "#{old_description}\n\nbar\n/react :smile:\nfoo" } }
|
||||
|
||||
it 'executes only the non residual quick actions' do
|
||||
expect(updated_issuable.description).to eq "foo\nbar\n\nbar\nfoo"
|
||||
expect(updated_issuable.award_emoji.first.name).to eq 'smile'
|
||||
expect(updated_issuable.labels).to be_empty
|
||||
expect(updated_issuable.assignees).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,91 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require_relative '../../../../lib/tasks/gitlab/tokens/manage_expiry_task'
|
||||
|
||||
RSpec.describe 'Tasks::Gitlab::Tokens::ManageExpiryTask', feature_category: :system_access do
|
||||
# rubocop:disable RSpec/AvoidTestProf -- this is not a migration spec
|
||||
let_it_be(:expires_at) { Date.today + 364.days }
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:migration_status) do
|
||||
create(:batched_background_migration, :finished,
|
||||
job_class_name: 'CleanupPersonalAccessTokensWithNilExpiresAt',
|
||||
table_name: 'personal_access_tokens',
|
||||
column_name: 'id')
|
||||
end
|
||||
# rubocop:enable RSpec/AvoidTestProf
|
||||
|
||||
let!(:personal_access_token1) { create(:personal_access_token, user: user, expires_at: expires_at) }
|
||||
let!(:personal_access_token2) { create(:personal_access_token, user: user, expires_at: expires_at) }
|
||||
let!(:personal_access_token3) { create(:personal_access_token, user: user, expires_at: expires_at + 1.day) }
|
||||
|
||||
subject(:task) { Tasks::Gitlab::Tokens::ManageExpiryTask.new }
|
||||
|
||||
describe '.analyze' do
|
||||
it 'calls the expected methods' do
|
||||
expect(task).to receive(:show_pat_expires_at_migration_status)
|
||||
expect(task).to receive(:show_most_common_pat_expiration_dates)
|
||||
|
||||
task.analyze
|
||||
end
|
||||
end
|
||||
|
||||
describe '.edit' do
|
||||
it 'calls analyze and prompts for action' do
|
||||
expect(task).to receive(:analyze).at_least(:once)
|
||||
expect(task).to receive(:prompt_action).at_least(:once).and_return(false)
|
||||
|
||||
task.edit
|
||||
end
|
||||
end
|
||||
|
||||
describe '.show_pat_expires_at_migration_status' do
|
||||
it 'prints the migration status' do
|
||||
expect { task.send(:show_pat_expires_at_migration_status) }.to output(
|
||||
/Started at: #{migration_status[:started_at]}\nFinished : #{migration_status[:finished_at]}/).to_stdout
|
||||
end
|
||||
end
|
||||
|
||||
describe '.show_most_common_pat_expiration_dates' do
|
||||
let(:second) { personal_access_token3.expires_at }
|
||||
|
||||
it 'shows the two groups of expiration dates' do
|
||||
expect { task.send(:show_most_common_pat_expiration_dates) }.to output(
|
||||
/#{expires_at}.*\|\s+2\s+\|\n\|\s+#{second}\s+\|\s+1\s+/).to_stdout
|
||||
end
|
||||
end
|
||||
|
||||
describe '.extend_expiration_date' do
|
||||
it 'extends the expiration date for selected tokens' do
|
||||
new_date = expires_at + 1.day
|
||||
prompt = instance_double(TTY::Prompt)
|
||||
|
||||
allow(task).to receive(:prompt_expiration_date_selection).and_return(expires_at)
|
||||
allow(TTY::Prompt).to receive(:new).and_return(prompt)
|
||||
allow(prompt).to receive(:ask).and_return(new_date.to_s)
|
||||
allow(prompt).to receive(:yes?).and_return(true)
|
||||
expect(task).to receive(:update_tokens_with_expiration).with(expires_at, new_date).and_call_original
|
||||
|
||||
expect { task.send(:extend_expiration_date) }.to output(/Updated 2 tokens!/).to_stdout
|
||||
|
||||
expect(personal_access_token1.reload.expires_at).to eq(new_date)
|
||||
expect(personal_access_token2.reload.expires_at).to eq(new_date)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.remove_expiration_date' do
|
||||
it 'removes the expiration date for selected tokens' do
|
||||
prompt = instance_double(TTY::Prompt)
|
||||
|
||||
allow(task).to receive(:prompt_expiration_date_selection).and_return(expires_at)
|
||||
allow(TTY::Prompt).to receive(:new).and_return(prompt)
|
||||
allow(prompt).to receive(:yes?).and_return(true)
|
||||
expect(task).to receive(:update_tokens_with_expiration).with(expires_at, nil).and_call_original
|
||||
|
||||
expect { task.send(:remove_expiration_date) }.to output(/Updated 2 tokens!/).to_stdout
|
||||
|
||||
expect(personal_access_token1.reload.expires_at).to be_nil
|
||||
expect(personal_access_token2.reload.expires_at).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -26,7 +26,7 @@ RSpec.describe Packages::Npm::ProcessPackageFileWorker, type: :worker, feature_c
|
|||
it 'does not call the service' do
|
||||
expect(Packages::Npm::ProcessPackageFileService).not_to receive(:new)
|
||||
|
||||
worker.perform(-1)
|
||||
worker.perform(non_existing_record_id)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -47,6 +47,30 @@ RSpec.describe Packages::Npm::ProcessPackageFileWorker, type: :worker, feature_c
|
|||
|
||||
worker.perform(package_file.id)
|
||||
end
|
||||
|
||||
context 'with manefist coherence mismatch error' do
|
||||
let(:exception) do
|
||||
Packages::Npm::CheckManifestCoherenceService::MismatchError.new('Package manifest is not coherent')
|
||||
end
|
||||
|
||||
before do
|
||||
allow_next_instance_of(Packages::Npm::ProcessPackageFileService, package_file) do |service|
|
||||
allow(service).to receive(:execute).and_raise(exception)
|
||||
end
|
||||
end
|
||||
|
||||
it 'calls the error handling service & sets the package status to error' do
|
||||
expect(worker).to receive(:process_package_file_error).with(
|
||||
package_file: package_file,
|
||||
exception: exception
|
||||
).and_call_original
|
||||
|
||||
worker.perform(package_file.id)
|
||||
|
||||
expect(package_file.package.reset).to be_error
|
||||
expect(package_file.package.status_message).to eq('Package manifest is not coherent')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ NUMBER_OF_WORKERS = ENV['NUMBER_OF_WORKERS'] || 10
|
|||
NUMBER_OF_JOBS = ENV['NUMBER_OF_JOBS'] || 1000
|
||||
NUMBER_OF_DUPLICATE_JOBS_ALLOWED = ENV['NUMBER_OF_DUPLICATE_JOBS_ALLOWED'] || 10
|
||||
JOB_FETCHER = (ENV['JOB_FETCHER'] || :semi).to_sym # :basic, :semi, :reliable
|
||||
NUMBER_OF_JOBS_LOST_ALLOWED = ENV['NUMBER_OF_JOBS_LOST_ALLOWED'] || (JOB_FETCHER == :reliable ? 0 : 3)
|
||||
TEST_CLEANUP_INTERVAL = 20
|
||||
TEST_LEASE_INTERVAL = 5
|
||||
WAIT_CLEANUP = TEST_CLEANUP_INTERVAL +
|
||||
|
|
|
|||
|
|
@ -105,10 +105,10 @@ Sidekiq.redis do |redis|
|
|||
end
|
||||
end
|
||||
|
||||
puts "Remaining unprocessed: #{jobs_lost}"
|
||||
puts "Remaining unprocessed: #{jobs_lost}. Max allowed: #{NUMBER_OF_JOBS_LOST_ALLOWED}"
|
||||
puts "Duplicates found: #{duplicates}. Max allowed: #{NUMBER_OF_DUPLICATE_JOBS_ALLOWED}"
|
||||
|
||||
if jobs_lost.zero? && duplicates <= NUMBER_OF_DUPLICATE_JOBS_ALLOWED
|
||||
if jobs_lost <= NUMBER_OF_JOBS_LOST_ALLOWED && duplicates <= NUMBER_OF_DUPLICATE_JOBS_ALLOWED
|
||||
exit 0
|
||||
else
|
||||
exit 1
|
||||
|
|
|
|||
Loading…
Reference in New Issue