+
+
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 04c40826d13..6af46e22d8f 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -119,7 +119,10 @@ class ProjectsController < Projects::ApplicationController
if @project.errors[:new_namespace].present?
flash[:alert] = @project.errors[:new_namespace].first
+ return redirect_to edit_project_path(@project)
end
+
+ render_edit
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/controllers/repositories/lfs_api_controller.rb b/app/controllers/repositories/lfs_api_controller.rb
index 30cafb6747e..c8e44bc8399 100644
--- a/app/controllers/repositories/lfs_api_controller.rb
+++ b/app/controllers/repositories/lfs_api_controller.rb
@@ -76,7 +76,10 @@ module Repositories
existing_oids = project.lfs_objects_oids(oids: objects_oids)
objects.each do |object|
- object[:actions] = upload_actions(object) unless existing_oids.include?(object[:oid])
+ next if existing_oids.include?(object[:oid])
+ next if should_auto_link? && oids_from_fork.include?(object[:oid]) && link_to_project!(object)
+
+ object[:actions] = upload_actions(object)
end
objects
@@ -150,6 +153,26 @@ module Repositories
Gitlab::LfsToken.new(user).basic_encoding
end
+
+ def should_auto_link?
+ return false unless Feature.enabled?(:lfs_auto_link_fork_source, project)
+ return false unless project.forked?
+
+ # Sanity check in case for some reason the user doesn't have access to the parent
+ can?(user, :download_code, project.fork_source)
+ end
+
+ def oids_from_fork
+ @oids_from_fork ||= project.lfs_objects_oids_from_fork_source(oids: objects_oids)
+ end
+
+ def link_to_project!(object)
+ lfs_object = LfsObject.for_oid_and_size(object[:oid], object[:size])
+
+ return unless lfs_object
+
+ LfsObjectsProject.link_to_project!(lfs_object, project)
+ end
end
end
diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb
index 106df168080..6acec417a75 100644
--- a/app/helpers/namespaces_helper.rb
+++ b/app/helpers/namespaces_helper.rb
@@ -88,6 +88,13 @@ module NamespacesHelper
group.namespace_settings.public_send(method_name, **args) # rubocop:disable GitlabSecurity/PublicSend
end
+ def namespaces_as_json(selected = :current_user)
+ {
+ group: formatted_namespaces(current_user.manageable_groups_with_routes),
+ user: formatted_namespaces([current_user.namespace])
+ }.to_json
+ end
+
private
# Many importers create a temporary Group, so use the real
@@ -119,6 +126,17 @@ module NamespacesHelper
[group_label.camelize, elements]
end
+
+ def formatted_namespaces(namespaces)
+ namespaces.sort_by(&:human_name).map! do |n|
+ {
+ id: n.id,
+ display_path: n.full_path,
+ human_name: n.human_name,
+ name: n.name
+ }
+ end
+ end
end
NamespacesHelper.prepend_mod_with('NamespacesHelper')
diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb
index 9765ac6f2e9..caeffae7bda 100644
--- a/app/models/lfs_object.rb
+++ b/app/models/lfs_object.rb
@@ -13,6 +13,7 @@ class LfsObject < ApplicationRecord
scope :with_files_stored_locally, -> { where(file_store: LfsObjectUploader::Store::LOCAL) }
scope :with_files_stored_remotely, -> { where(file_store: LfsObjectUploader::Store::REMOTE) }
scope :for_oids, -> (oids) { where(oid: oids) }
+ scope :for_oid_and_size, -> (oid, size) { find_by(oid: oid, size: size) }
validates :oid, presence: true, uniqueness: true
diff --git a/app/models/lfs_objects_project.rb b/app/models/lfs_objects_project.rb
index e5632ff2842..bf6d1394569 100644
--- a/app/models/lfs_objects_project.rb
+++ b/app/models/lfs_objects_project.rb
@@ -21,9 +21,19 @@ class LfsObjectsProject < ApplicationRecord
scope :project_id_in, ->(ids) { where(project_id: ids) }
scope :lfs_object_in, -> (lfs_objects) { where(lfs_object: lfs_objects) }
+ def self.link_to_project!(lfs_object, project)
+ # We can't use an upsert here because there is no uniqueness constraint:
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/347466
+ self.safe_find_or_create_by!(lfs_object_id: lfs_object.id, project_id: project.id) # rubocop:disable Performance/ActiveRecordSubtransactionMethods
+ end
+
+ def self.update_statistics_for_project_id(project_id)
+ ProjectCacheWorker.perform_async(project_id, [], [:lfs_objects_size]) # rubocop:disable CodeReuse/Worker
+ end
+
private
def update_project_statistics
- ProjectCacheWorker.perform_async(project_id, [], [:lfs_objects_size])
+ self.class.update_statistics_for_project_id(project_id)
end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 6d35534bb6a..088a2f9ea27 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1569,6 +1569,12 @@ class Project < ApplicationRecord
oids(lfs_objects, oids: oids)
end
+ def lfs_objects_oids_from_fork_source(oids: [])
+ return [] unless forked?
+
+ oids(fork_source.lfs_objects, oids: oids)
+ end
+
def personal?
!group
end
diff --git a/app/services/groups/update_shared_runners_service.rb b/app/services/groups/update_shared_runners_service.rb
index eb6b46a5613..c09dce0761f 100644
--- a/app/services/groups/update_shared_runners_service.rb
+++ b/app/services/groups/update_shared_runners_service.rb
@@ -8,7 +8,7 @@ module Groups
validate_params
update_shared_runners
- update_pending_builds!
+ update_pending_builds_async
success
@@ -28,12 +28,18 @@ module Groups
group.update_shared_runners_setting!(params[:shared_runners_setting])
end
- def update_pending_builds!
- return unless group.previous_changes.include?('shared_runners_enabled')
+ def update_pending_builds?
+ group.previous_changes.include?('shared_runners_enabled')
+ end
- update_params = { instance_runners_enabled: group.shared_runners_enabled }
+ def update_pending_builds_async
+ return unless update_pending_builds?
- ::Ci::UpdatePendingBuildService.new(group, update_params).execute
+ group.run_after_commit_or_now do |group|
+ pending_builds_params = { instance_runners_enabled: group.shared_runners_enabled }
+
+ ::Ci::UpdatePendingBuildService.new(group, pending_builds_params).execute
+ end
end
end
end
diff --git a/app/views/projects/_transfer.html.haml b/app/views/projects/_transfer.html.haml
index e48008e1cc6..9f9daa7ec6f 100644
--- a/app/views/projects/_transfer.html.haml
+++ b/app/views/projects/_transfer.html.haml
@@ -1,8 +1,11 @@
- return unless can?(current_user, :change_namespace, @project)
+- form_id = "transfer-project-form"
+- hidden_input_id = "new_namespace_id"
+- initial_data = { namespaces: namespaces_as_json, button_text: s_('ProjectSettings|Transfer project'), confirm_danger_message: transfer_project_message(@project), phrase: @project.name, target_form_id: form_id, target_hidden_input_id: hidden_input_id }
.sub-section
%h4.danger-title= _('Transfer project')
- = form_for @project, url: transfer_project_path(@project), method: :put, remote: true, html: { class: 'js-project-transfer-form' } do |f|
+ = form_for @project, url: transfer_project_path(@project), method: :put, html: { class: 'js-project-transfer-form', id: form_id } do |f|
.form-group
- link_start = ''.html_safe % { url: help_page_path('user/project/settings/index', anchor: 'transferring-an-existing-project-into-another-namespace') }
%p= _("Transfer your project into another namespace. %{link_start}Learn more.%{link_end}").html_safe % { link_start: link_start, link_end: ''.html_safe }
@@ -11,7 +14,6 @@
%li= _('You can only transfer the project to namespaces you manage.')
%li= _('You will need to update your local repositories to point to the new location.')
%li= _('Project visibility level will be changed to match namespace rules when transferring to a group.')
+ = hidden_field_tag(hidden_input_id)
= label_tag :new_namespace_id, _('Select a new namespace'), class: 'gl-font-weight-bold'
- .form-group
- = select_tag :new_namespace_id, namespaces_options(nil), include_blank: true, class: 'select2'
- = f.submit 'Transfer project', class: "gl-button btn btn-danger js-legacy-confirm-danger qa-transfer-button", data: { "confirm-danger-message" => transfer_project_message(@project) }
+ .js-transfer-project-form{ data: initial_data }
diff --git a/app/views/projects/transfer.js.haml b/app/views/projects/transfer.js.haml
deleted file mode 100644
index 6d083c5c516..00000000000
--- a/app/views/projects/transfer.js.haml
+++ /dev/null
@@ -1,2 +0,0 @@
-:plain
- location.href = "#{edit_project_path(@project)}";
diff --git a/config/feature_flags/development/lfs_auto_link_fork_source.yml b/config/feature_flags/development/lfs_auto_link_fork_source.yml
new file mode 100644
index 00000000000..b094d56294c
--- /dev/null
+++ b/config/feature_flags/development/lfs_auto_link_fork_source.yml
@@ -0,0 +1,8 @@
+---
+name: lfs_auto_link_fork_source
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75972
+rollout_issue_url:
+milestone: '14.6'
+type: development
+group: group::source code
+default_enabled: false
diff --git a/config/metrics/counts_28d/20210216184559_ci_templates_total_unique_counts_monthly.yml b/config/metrics/counts_28d/20210216184559_ci_templates_total_unique_counts_monthly.yml
index c80a7dada11..637b02b93ed 100644
--- a/config/metrics/counts_28d/20210216184559_ci_templates_total_unique_counts_monthly.yml
+++ b/config/metrics/counts_28d/20210216184559_ci_templates_total_unique_counts_monthly.yml
@@ -49,6 +49,7 @@ options:
- p_ci_templates_security_api_fuzzing_latest
- p_ci_templates_security_secure_binaries
- p_ci_templates_security_dast_api
+ - p_ci_templates_security_dast_api_latest
- p_ci_templates_security_container_scanning
- p_ci_templates_security_dast_latest
- p_ci_templates_security_dependency_scanning
@@ -153,6 +154,7 @@ options:
- p_ci_templates_implicit_security_api_fuzzing_latest
- p_ci_templates_implicit_security_secure_binaries
- p_ci_templates_implicit_security_dast_api
+ - p_ci_templates_implicit_security_dast_api_latest
- p_ci_templates_implicit_security_container_scanning
- p_ci_templates_implicit_security_dast_latest
- p_ci_templates_implicit_security_dependency_scanning
diff --git a/config/metrics/counts_28d/20211122134101_p_ci_templates_implicit_security_dast_api_latest_monthly.yml b/config/metrics/counts_28d/20211122134101_p_ci_templates_implicit_security_dast_api_latest_monthly.yml
new file mode 100644
index 00000000000..4b9bf4120f5
--- /dev/null
+++ b/config/metrics/counts_28d/20211122134101_p_ci_templates_implicit_security_dast_api_latest_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_security_dast_api_latest_monthly
+description: Monthly counts for DAST API latest CI template
+product_section: sec
+product_stage: secure
+product_group: dynamic_analysis
+product_category: dynamic_application_security_testing
+value_type: number
+status: active
+milestone: '14.6'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73876
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+options:
+ events:
+ - p_ci_templates_implicit_security_dast_api_latest
diff --git a/config/metrics/counts_28d/20211201194407_p_ci_templates_security_dast_api_latest_monthly.yml b/config/metrics/counts_28d/20211201194407_p_ci_templates_security_dast_api_latest_monthly.yml
new file mode 100644
index 00000000000..cfcffe5caca
--- /dev/null
+++ b/config/metrics/counts_28d/20211201194407_p_ci_templates_security_dast_api_latest_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_security_dast_api_latest_monthly
+description: Monthly counts for DAST API latest CI template
+product_section: sec
+product_stage: secure
+product_group: dynamic_analysis
+product_category: dynamic_application_security_testing
+value_type: number
+status: active
+milestone: "14.6"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73876
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+options:
+ events:
+ - p_ci_templates_security_dast_api_latest
diff --git a/config/metrics/counts_7d/20210216184557_ci_templates_total_unique_counts_weekly.yml b/config/metrics/counts_7d/20210216184557_ci_templates_total_unique_counts_weekly.yml
index 31813735ea4..8d545b91d1f 100644
--- a/config/metrics/counts_7d/20210216184557_ci_templates_total_unique_counts_weekly.yml
+++ b/config/metrics/counts_7d/20210216184557_ci_templates_total_unique_counts_weekly.yml
@@ -49,6 +49,7 @@ options:
- p_ci_templates_security_api_fuzzing_latest
- p_ci_templates_security_secure_binaries
- p_ci_templates_security_dast_api
+ - p_ci_templates_security_dast_api_latest
- p_ci_templates_security_container_scanning
- p_ci_templates_security_dast_latest
- p_ci_templates_security_dependency_scanning
@@ -153,6 +154,7 @@ options:
- p_ci_templates_implicit_security_api_fuzzing_latest
- p_ci_templates_implicit_security_secure_binaries
- p_ci_templates_implicit_security_dast_api
+ - p_ci_templates_implicit_security_dast_api_latest
- p_ci_templates_implicit_security_container_scanning
- p_ci_templates_implicit_security_dast_latest
- p_ci_templates_implicit_security_dependency_scanning
diff --git a/config/metrics/counts_7d/20211122134101_p_ci_templates_implicit_security_dast_api_latest_weekly.yml b/config/metrics/counts_7d/20211122134101_p_ci_templates_implicit_security_dast_api_latest_weekly.yml
new file mode 100644
index 00000000000..8d0415dedd2
--- /dev/null
+++ b/config/metrics/counts_7d/20211122134101_p_ci_templates_implicit_security_dast_api_latest_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_security_dast_api_latest_weekly
+description: Weekly counts for DAST API latest CI template
+product_section: sec
+product_stage: secure
+product_group: dynamic_analysis
+product_category: dynamic_application_security_testing
+value_type: number
+status: active
+milestone: '14.6'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73876
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+options:
+ events:
+ - p_ci_templates_implicit_security_dast_api_latest
diff --git a/config/metrics/counts_7d/20211201194402_p_ci_templates_security_dast_api_latest_weekly.yml b/config/metrics/counts_7d/20211201194402_p_ci_templates_security_dast_api_latest_weekly.yml
new file mode 100644
index 00000000000..1d4f024b361
--- /dev/null
+++ b/config/metrics/counts_7d/20211201194402_p_ci_templates_security_dast_api_latest_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_security_dast_api_latest_weekly
+description: Weekly counts for DAST API latest CI template
+product_section: sec
+product_stage: secure
+product_group: dynamic_analysis
+product_category: dynamic_application_security_testing
+value_type: number
+status: active
+milestone: "14.6"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73876
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+options:
+ events:
+ - p_ci_templates_security_dast_api_latest
diff --git a/data/deprecations/14-6-deprecation-secure-dependency-scanning-bundler-audit.yml b/data/deprecations/14-6-deprecation-secure-dependency-scanning-bundler-audit.yml
new file mode 100644
index 00000000000..23e59da21e4
--- /dev/null
+++ b/data/deprecations/14-6-deprecation-secure-dependency-scanning-bundler-audit.yml
@@ -0,0 +1,16 @@
+- name: "Deprecation of bundler-audit Dependency Scanning tool" # The name of the feature to be deprecated
+ announcement_milestone: "14.6" # The milestone when this feature was first announced as deprecated.
+ announcement_date: "2021-12-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ removal_milestone: "15.0" # The milestone when this feature is planned to be removed
+ body: | # Do not modify this line, instead modify the lines below.
+ As of 14.6 bundler-audit is being deprecated from Dependency Scanning. It will continue to be in our CI/CD template while deprecated. We are removing bundler-audit from Dependency Scanning on May 22, 2022 in 15.0. After this removal Ruby scanning functionality will not be affected as it is still being covered by Gemnasium.
+
+ If you have explicitly excluded bundler-audit using DS_EXCLUDED_ANALYZERS you will need to clean up (remove the reference) in 15.0. If you have customized your pipeline's Dependency Scanning configuration, for example to edit the `bundler-audit-dependency_scanning` job, you will want to switch to gemnasium-dependency_scanning before removal in 15.0, to prevent your pipeline from failing. If you have not used the DS_EXCLUDED_ANALYZERS to reference bundler-audit, or customized your template specifically for bundler-audit, you will not need to take action.
+# The following items are not published on the docs page, but may be used in the future.
+ stage: secure # (optional - may be required in the future) String value of the stage that the feature was created in. e.g., Growth
+ tiers: ultimate # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/289832 # (optional) This is a link to the deprecation issue in GitLab
+ documentation_url: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/analyzers.html # (optional) This is a link to the current documentation page
+ image_url: # (optional) This is a link to a thumbnail image depicting the feature
+ video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
+ removal_date: 2022-05-22 # (optional - may be required in the future) YYYY-MM-DD format. This should almost always be the 22nd of a month (YYYY-MM-22), the date of the milestone release when this feature is planned to be removed
diff --git a/doc/administration/audit_events.md b/doc/administration/audit_events.md
index 71eee78c168..06ad16bbcba 100644
--- a/doc/administration/audit_events.md
+++ b/doc/administration/audit_events.md
@@ -9,8 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
GitLab offers a way to view the changes made within the GitLab server for owners and administrators
on a [paid plan](https://about.gitlab.com/pricing/).
-GitLab system administrators can also take advantage of the logs located on the
-file system. See [the logs system documentation](logs.md#audit_jsonlog) for more details.
+GitLab system administrators can also view all audit events by accessing the [`audit_json.log` file](logs.md#audit_jsonlog).
You can:
diff --git a/doc/administration/incoming_email.md b/doc/administration/incoming_email.md
index 6b390cfc77a..62fbf9e7166 100644
--- a/doc/administration/incoming_email.md
+++ b/doc/administration/incoming_email.md
@@ -10,7 +10,7 @@ GitLab has several features based on receiving incoming email messages:
- [Reply by Email](reply_by_email.md): allow GitLab users to comment on issues
and merge requests by replying to notification email.
-- [New issue by email](../user/project/issues/managing_issues.md#new-issue-via-email):
+- [New issue by email](../user/project/issues/managing_issues.md#by-sending-an-email):
allow GitLab users to create a new issue by sending an email to a
user-specific email address.
- [New merge request by email](../user/project/merge_requests/creating_merge_requests.md#by-sending-an-email):
diff --git a/doc/administration/index.md b/doc/administration/index.md
index ef9c56c1615..d78c9d80b5f 100644
--- a/doc/administration/index.md
+++ b/doc/administration/index.md
@@ -133,7 +133,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- Instances.
- [Auditor users](auditor_users.md): Users with read-only access to all projects, groups, and other resources on the GitLab instance.
- [Incoming email](incoming_email.md): Configure incoming emails to allow
- users to [reply by email](reply_by_email.md), create [issues by email](../user/project/issues/managing_issues.md#new-issue-via-email) and
+ users to [reply by email](reply_by_email.md), create [issues by email](../user/project/issues/managing_issues.md#by-sending-an-email) and
[merge requests by email](../user/project/merge_requests/creating_merge_requests.md#by-sending-an-email), and to enable [Service Desk](../user/project/service_desk.md).
- [Postfix for incoming email](reply_by_email_postfix_setup.md): Set up a
basic Postfix mail server with IMAP authentication on Ubuntu for incoming
diff --git a/doc/development/cicd/cicd_reference_documentation_guide.md b/doc/development/cicd/cicd_reference_documentation_guide.md
index aa3888cd866..892c9814c7f 100644
--- a/doc/development/cicd/cicd_reference_documentation_guide.md
+++ b/doc/development/cicd/cicd_reference_documentation_guide.md
@@ -9,14 +9,14 @@ info: To determine the technical writer assigned to the Stage/Group associated w
The CI/CD YAML reference uses a standard style to make it easier to use and update.
The reference information should be kept as simple as possible, and expanded details
-and examples documented in a separate page.
+and examples should be documented on other pages.
## YAML reference structure
Every YAML keyword must have its own section in the reference. The sections should
be nested so that the keywords follow a logical tree structure. For example:
-```plaintext
+```markdown
### `artifacts`
#### `artifacts:name`
#### `artifacts:paths`
@@ -27,128 +27,127 @@ be nested so that the keywords follow a logical tree structure. For example:
## YAML reference style
-Each keyword entry in the reference should use the following style:
+Each keyword entry in the reference:
-````markdown
-### `keyword-name`
+- Must have a simple introductory section. The introduction should give the fundamental
+ information needed to use the keyword. Advanced details and tasks should be in
+ feature pages, not the reference page.
-> Version information
+- Must use the keyword name as the title, for example:
-Keyword description and main details.
+ ```markdown
+ ### `artifacts`
+ ```
-**Keyword type**:
+- Should include the following sections:
+ - [Keyword type](#keyword-type)
+ - [Possible inputs](#possible-inputs)
+ - [Example of `keyword-name`](#example-of-keyword-name)
+- (Optional) Can also include the following sections when needed:
+ - [Additional details](#additional-details)
+ - [Related topics](#related-topics)
+The keyword name must always be in backticks without a final `:`, like `artifacts`, not `artifacts:`.
+If it is a subkey of another keyword, write out all the subkeys to the "parent" key the first time it
+is used, like `artifacts:reports:dast`. Afterwards, you can use just the subkey alone, like `dast`.
+
+## Keyword type
+
+The keyword can be either a job or global keyword. If it can be used in a `default`
+section, make not of that as well, for example:
+
+- `**Keyword type**: Global keyword.`
+- `**Keyword type**: Job keyword. You can use it only as part of a job.`
+- ``**Keyword type**: Job keyword. You can use it only as part of a job or in the [`default:` section](#default).``
+
+### Possible inputs
+
+List all the possible inputs, and any extra details about the inputs, such as defaults
+or changes due to different GitLab versions, for example:
+
+```markdown
**Possible inputs**:
-**Example of `keyword-name`**:
-
-(optional) In this example...
-
-(optional) **Additional details**:
-
-- List of extra details.
-
-(optional) **Related topics**:
-
-- List of links to topics related to the keyword.
-````
-
-- ``### `keyword-name` ``: The keyword name must always be in backticks.
- If it is a subkey of another keyword, write out all the keywords, with each separated
- by `:`, for example: `artifacts:reports:dast`.
-
-- ``> Version information``: The [version history details](../documentation/styleguide/index.md#version-text-in-the-version-history).
- If the keyword is feature flagged, see the [feature flag documentation guide](../documentation/feature_flags.md)
- as well.
-
-- `Keyword description and main details.`: A simple description of the keyword, and
- how to use it. Additional use cases and benefits should be added to a page outside
- the reference document. Link to that document in this section.
-
-- `**Keyword type**:`: Most keywords are defined at the job level, like `script`,
- or at the pipeline level, like `stages`. Add the appropriate line:
-
- - `**Keyword type**: Job keyword. You can use it only as part of a job.`
- - `**Keyword type**: Pipeline keyword. You cannot use it as part of a job.`
-
- If a keyword can be used at both the job and pipeline level, like `variables`,
- explain it in detail instead of using the pre-written lines above.
-
-- `**Possible inputs**:`: Explain in detail which inputs the keyword can accept.
- You can add the details in a sentence, paragraph, or list.
-
-- ``**Example of `keyword-name`**:``: An example configuration that uses the keyword.
- Do not add extra keywords that are not required to understand the behavior.
-
-- (optional) `In this example...`: If the example needs extra details,
- add the clarification text below the example.
-
-- (optional) `**Additional details**:` If there are any caveats or extra details you
- want to document along with the keyword, add each one as a list item here.
-
-- (optional) `**Related topics**:` If there are any other keywords or pages that
- relate to this keyword, add these links as list items here.
-
-### YAML reference style example
-
-See the [`only:changes` / `except:changes`](../../ci/yaml/index.md#onlychanges--exceptchanges)
-documentation for an example of the YAML reference style. The following example is a
-shortened version of that documentation's Markdown:
-
-````markdown
-#### `only:changes` / `except:changes`
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/19232) in GitLab 11.4.
-
-Use the `changes` keyword with `only` to run a job, or with `except` to skip a job,
-when a Git push event modifies a file.
-
-Use `changes` in pipelines with the following refs:
-
-- `branches`
-- `external_pull_requests`
-- `merge_requests` (see additional details about [using `only:changes` with pipelines for merge requests](../jobs/job_control.md#use-onlychanges-with-pipelines-for-merge-requests))
-
-**Keyword type**: Job keyword. You can use it only as part of a job.
-
-**Possible inputs**: An array including any number of:
-
-- Paths to files.
-- Wildcard paths for single directories, for example `path/to/directory/*`, or a directory
- and all its subdirectories, for example `path/to/directory/**/*`.
-
-**Example of `only:changes`**:
-
-```yaml
-docker build:
- script: docker build -t my-image:$CI_COMMIT_REF_SLUG .
- only:
- refs:
- - branches
- changes:
- - Dockerfile
- - docker/scripts/*
- - dockerfiles/**/*
+- `true` (default if not defined) or `false`.
```
-In this example, `docker build` only runs in branch pipelines, and only if at least one of
-these files changed:
+```markdown
+**Possible inputs**:
-- `Dockerfile`.
-- Any file in `docker/scripts`
-- Any file in `dockerfiles/` or any of its subdirectories.
+- A single exit code.
+- An array of exit codes.
+```
+```markdown
+**Possible inputs**:
+
+- A string with the long description.
+- The path to a file that contains the description. Introduced in [GitLab 13.7](https://gitlab.com/gitlab-org/release-cli/-/merge_requests/67).
+ - The file location must be relative to the project directory (`$CI_PROJECT_DIR`).
+ - If the file is a symbolic link, it must be in the `$CI_PROJECT_DIR`.
+ - The `./path/to/file` and filename can't contain spaces.
+```
+
+### Example of `keyword-name`
+
+An example of the keyword. Use the minimum number of other keywords necessary
+to make the example valid. If the example needs explanation, add it after the example,
+for example:
+
+````markdown
+**Example of `dast`**:
+
+```yaml
+stages:
+ - build
+ - dast
+
+include:
+ - template: DAST.gitlab-ci.yml
+
+dast:
+ dast_configuration:
+ site_profile: "Example Co"
+ scanner_profile: "Quick Passive Test"
+```
+
+In this example, the `dast` job extends the `dast` configuration added with the `include:` keyword
+to select a specific site profile and scanner profile.
+````
+
+### Additional details
+
+The additional details should be an unordered list of extra information that is
+useful to know, but not important enough to put in the introduction. This information
+can include changes introduced in different GitLab versions. For example:
+
+```markdown
**Additional details**:
-- If you use refs other than `branches`, `external_pull_requests`, or `merge_requests`,
- `changes` can't determine if a given file is new or old and always returns `true`.
-- If you use `only: changes` with other refs, jobs ignore the changes and always run.
-- If you use `except: changes` with other refs, jobs ignore the changes and never run.
+- The expiration time period begins when the artifact is uploaded and stored on GitLab.
+ If the expiry time is not defined, it defaults to the [instance wide setting](../../user/admin_area/settings/continuous_integration.md#default-artifacts-expiration).
+- To override the expiration date and protect artifacts from being automatically deleted:
+ - Select **Keep** on the job page.
+ - [In GitLab 13.3 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/22761), set the value of
+ `expire_in` to `never`.
+```
+### Related topics
+
+The related topics should be an unordered list of crosslinks to related pages, including:
+
+- Specific tasks that you can accomplish with the keyword.
+- Advanced examples of the keyword.
+- Other related keywords that can be used together with this keyword.
+
+For example:
+
+```markdown
**Related topics**:
-- [`only: changes` and `except: changes` examples](../jobs/job_control.md#onlychanges--exceptchanges-examples).
-- If you use `changes` with [only allow merge requests to be merged if the pipeline succeeds](../../user/project/merge_requests/merge_when_pipeline_succeeds.md#only-allow-merge-requests-to-be-merged-if-the-pipeline-succeeds),
- you should [also use `only:merge_requests`](../jobs/job_control.md#use-onlychanges-with-pipelines-for-merge-requests).
-- Use `changes` with [scheduled pipelines](../jobs/job_control.md#use-onlychanges-with-scheduled-pipelines).
-````
+- You can specify a [fallback cache key](../caching/index.md#use-a-fallback-cache-key)
+ to use if the specified `cache:key` is not found.
+- You can [use multiple cache keys](../caching/index.md#use-multiple-caches) in a single job.
+- See the [common `cache` use cases](../caching/index.md#common-use-cases-for-caches) for more
+ `cache:key` examples.
+```
diff --git a/doc/gitlab-basics/index.md b/doc/gitlab-basics/index.md
index 5e4ec0e8c51..5ba5366eafa 100644
--- a/doc/gitlab-basics/index.md
+++ b/doc/gitlab-basics/index.md
@@ -29,7 +29,7 @@ The following are guides to basic GitLab functionality:
- [Feature branch workflow](feature_branch_workflow.md).
- [Fork a project](../user/project/repository/forking_workflow.md#creating-a-fork), to duplicate projects so they can be worked on in parallel.
- [Add a file](add-file.md), to add new files to a project's repository.
-- [Create an issue](../user/project/issues/managing_issues.md#create-a-new-issue),
+- [Create an issue](../user/project/issues/managing_issues.md#create-an-issue),
to start collaborating within a project.
- [Create a merge request](../user/project/merge_requests/creating_merge_requests.md), to request changes made in a branch
be merged into a project's repository.
diff --git a/doc/update/deprecations.md b/doc/update/deprecations.md
index 066c91b7dd9..20c6d692cf6 100644
--- a/doc/update/deprecations.md
+++ b/doc/update/deprecations.md
@@ -116,6 +116,14 @@ Long term service and support (LTSS) for SUSE Linux Enterprise Server (SLES) 12
Announced: 2021-11-22
+### Deprecation of bundler-audit Dependency Scanning tool
+
+As of 14.6 bundler-audit is being deprecated from Dependency Scanning. It will continue to be in our CI/CD template while deprecated. We are removing bundler-audit from Dependency Scanning on May 22, 2022 in 15.0. After this removal Ruby scanning functionality will not be affected as it is still being covered by Gemnasium.
+
+If you have explicitly excluded bundler-audit using DS_EXCLUDED_ANALYZERS you will need to clean up (remove the reference) in 15.0. If you have customized your pipeline's Dependency Scanning configuration, for example to edit the `bundler-audit-dependency_scanning` job, you will want to switch to gemnasium-dependency_scanning before removal in 15.0, to prevent your pipeline from failing. If you have not used the DS_EXCLUDED_ANALYZERS to reference bundler-audit, or customized your template specifically for bundler-audit, you will not need to take action.
+
+Announced: 2021-12-22
+
### GitLab Serverless
[GitLab Serverless](https://docs.gitlab.com/ee/user/project/clusters/serverless/) is a feature set to support Knative-based serverless development with automatic deployments and monitoring.
diff --git a/doc/user/project/description_templates.md b/doc/user/project/description_templates.md
index 5b19a54bd91..66e5931fa4c 100644
--- a/doc/user/project/description_templates.md
+++ b/doc/user/project/description_templates.md
@@ -71,7 +71,7 @@ To create the `.gitlab/issue_templates` directory:
1. Select **New directory**.
1. Name your directory `issue_templates` and commit to your default branch.
-To check if this has worked correctly, [create a new issue](issues/managing_issues.md#create-a-new-issue)
+To check if this has worked correctly, [create a new issue](issues/managing_issues.md#create-an-issue)
and see if you can choose a description template.
## Create a merge request template
diff --git a/doc/user/project/issues/img/new_issue_from_email.png b/doc/user/project/issues/img/new_issue_from_email.png
deleted file mode 100644
index 6da899ea37c..00000000000
Binary files a/doc/user/project/issues/img/new_issue_from_email.png and /dev/null differ
diff --git a/doc/user/project/issues/img/new_issue_from_issue_board.png b/doc/user/project/issues/img/new_issue_from_issue_board.png
deleted file mode 100644
index 30a1ffb9011..00000000000
Binary files a/doc/user/project/issues/img/new_issue_from_issue_board.png and /dev/null differ
diff --git a/doc/user/project/issues/img/new_issue_from_open_issue_v13_6.png b/doc/user/project/issues/img/new_issue_from_open_issue_v13_6.png
deleted file mode 100644
index 902aa40614a..00000000000
Binary files a/doc/user/project/issues/img/new_issue_from_open_issue_v13_6.png and /dev/null differ
diff --git a/doc/user/project/issues/img/new_issue_from_projects_dashboard.png b/doc/user/project/issues/img/new_issue_from_projects_dashboard.png
deleted file mode 100644
index 474ca2b45c0..00000000000
Binary files a/doc/user/project/issues/img/new_issue_from_projects_dashboard.png and /dev/null differ
diff --git a/doc/user/project/issues/img/new_issue_from_tracker_list.png b/doc/user/project/issues/img/new_issue_from_tracker_list.png
deleted file mode 100644
index 66793cb44fa..00000000000
Binary files a/doc/user/project/issues/img/new_issue_from_tracker_list.png and /dev/null differ
diff --git a/doc/user/project/issues/img/new_issue_v13_1.png b/doc/user/project/issues/img/new_issue_v13_1.png
deleted file mode 100644
index a66846c234e..00000000000
Binary files a/doc/user/project/issues/img/new_issue_v13_1.png and /dev/null differ
diff --git a/doc/user/project/issues/img/select_project_from_group_level_issue_tracker_v13_11.png b/doc/user/project/issues/img/select_project_from_group_level_issue_tracker_v13_11.png
deleted file mode 100644
index 4612ae254d4..00000000000
Binary files a/doc/user/project/issues/img/select_project_from_group_level_issue_tracker_v13_11.png and /dev/null differ
diff --git a/doc/user/project/issues/index.md b/doc/user/project/issues/index.md
index c535d16bc0f..85c30fa4ae5 100644
--- a/doc/user/project/issues/index.md
+++ b/doc/user/project/issues/index.md
@@ -29,7 +29,7 @@ To learn how the GitLab Strategic Marketing department uses GitLab issues with [
## Related topics
-- [Create issues](managing_issues.md#create-a-new-issue)
+- [Create issues](managing_issues.md#create-an-issue)
- [Create an issue from a template](../../project/description_templates.md#use-the-templates)
- [Edit issues](managing_issues.md#edit-an-issue)
- [Move issues](managing_issues.md#moving-issues)
diff --git a/doc/user/project/issues/managing_issues.md b/doc/user/project/issues/managing_issues.md
index d357f45b1e7..0f02eec0ce9 100644
--- a/doc/user/project/issues/managing_issues.md
+++ b/doc/user/project/issues/managing_issues.md
@@ -4,148 +4,228 @@ group: Project Management
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
-# Managing issues **(FREE)**
+# Manage issues **(FREE)**
[GitLab Issues](index.md) are the fundamental medium for collaborating on ideas and
planning work in GitLab.
-Key actions for issues include:
+## Create an issue
-- [Creating issues](#create-a-new-issue)
-- [Moving issues](#moving-issues)
-- [Closing issues](#closing-issues)
-- [Deleting issues](#deleting-issues)
-- [Promoting issues](#promote-an-issue-to-an-epic) **(PREMIUM)**
-
-## Create a new issue
-
-When you create a new issue, you are prompted to enter the fields of the issue.
+When you create an issue, you are prompted to enter the fields of the issue.
If you know the values you want to assign to an issue, you can use
[quick actions](../quick_actions.md) to enter them.
-While creating an issue, you can associate it to an existing epic from current group by
-selecting it using **Epic** dropdown.
+You can create an issue in many ways in GitLab:
-### Accessing the New Issue form
+- [From a project](#from-a-project)
+- [From a group](#from-a-group)
+- [From another issue](#from-another-issue)
+- [From an issue board](#from-an-issue-board)
+- [By sending an email](#by-sending-an-email)
+- Using a URL with prefilled fields
+- [Using Service Desk](#using-service-desk)
-There are many ways to get to the New Issue form from a project's page:
+### From a project
-- Navigate to your **Project's Dashboard** > **Issues** > **New Issue**:
+Prerequisites:
- ![New issue from the issue list view](img/new_issue_from_tracker_list.png)
+- You must have at least the [Guest role](../../permissions.md) for the project.
-- From an **open issue** in your project, click the vertical ellipsis (**{ellipsis_v}**) button
- to open a dropdown menu, and then click **New Issue** to create a new issue in the same project:
+To create an issue:
- ![New issue from an open issue](img/new_issue_from_open_issue_v13_6.png)
+1. On the top bar, select **Menu > Projects** and find your project.
+1. Either:
-- From your **Project's Dashboard**, click the plus sign (**+**) to open a dropdown
- menu with a few options. Select **New Issue** to create an issue in that project:
+ - On the left sidebar, select **Issues**, and then, in the top right corner, select **New issue**.
+ - On the top bar, select the plus sign (**{plus-square}**) and then, under **This project**,
+ select **New issue**.
- ![New issue from a project's dashboard](img/new_issue_from_projects_dashboard.png)
+1. Complete the [fields](#fields-in-the-new-issue-form).
+1. Select **Create issue**.
-- From an **issue board**, create a new issue by clicking on the plus sign (**+**) at the top of a list.
- It opens a new issue for that project, pre-labeled with its respective list.
+The newly created issue opens.
- ![From the issue board](img/new_issue_from_issue_board.png)
+### From a group
-### Elements of the New Issue form
+Issues belong to projects, but when you're in a group, you can access and create issues that belong
+to the projects in the group.
-> Ability to add the new issue to an epic [was introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13847) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.1.
+Prerequisites:
-![New issue from the issues list](img/new_issue_v13_1.png)
+- You must have at least the [Guest role](../../permissions.md) for a project in the group.
-When you're creating a new issue, these are the fields you can fill in:
+To create an issue from a group:
-- Title
-- Description
-- Checkbox to make the issue [confidential](confidential_issues.md)
-- Assignee
-- Weight
-- [Epic](../../group/epics/index.md)
-- Due date
-- Milestone
-- Labels
-
-### New issue from the group-level issue tracker
-
-To visit the issue tracker for all projects in your group:
-
-1. Go to the group dashboard.
+1. On the top bar, select **Menu > Groups** and find your group.
1. On the left sidebar, select **Issues**.
-1. In the top-right, select the **Select project to create issue** button.
+1. In the top right corner, select **Select project to create issue**.
1. Select the project you'd like to create an issue for. The button now reflects the selected
project.
-1. Select the button to create an issue in the selected project.
+1. Select **New issue in ``**.
+1. Complete the [fields](#fields-in-the-new-issue-form).
+1. Select **Create issue**.
-![Select project to create issue](img/select_project_from_group_level_issue_tracker_v13_11.png)
+The newly created issue opens.
The project you selected most recently becomes the default for your next visit.
-This should save you a lot of time and clicks, if you mostly create issues for the same project.
+This can save you a lot of time and clicks, if you mostly create issues for the same project.
-### New issue via Service Desk
+### From another issue
-Enable [Service Desk](../service_desk.md) for your project and offer email support.
-Now, when your customer sends a new email, a new issue can be created in
-the appropriate project and followed up from there.
+> New issue becoming linked to the issue of origin [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68226) in GitLab 14.3.
-### New issue via email
+You can create a new issue from an existing one. The two issues can then be marked as related.
-A link to **Email a new issue to this project** is displayed at the bottom of a project's
-**Issues List** page. The link is shown only if your GitLab instance has [incoming email](../../../administration/incoming_email.md)
-configured and there is at least one issue in the issue list.
+Prerequisites:
-![Bottom of a project issues page](img/new_issue_from_email.png)
+- You must have at least the [Guest role](../../permissions.md) for the project.
-When you click this link, an email address is generated and displayed, which should be used
-by **you only**, to create issues in this project. You can save this address as a
-contact in your email client for quick access.
+To create an issue from another issue:
+
+1. In an existing issue, select the vertical ellipsis (**{ellipsis_v}**).
+1. Select **New issue**.
+1. Complete the [fields](#fields-in-the-new-issue-form).
+ The new issue's description is prefilled with `Related to #123`, where `123` is the ID of the
+ issue of origin. If you keep this mention in the description, the two issues become
+ [linked](related_issues.md).
+1. Select **Create issue**.
+
+The newly created issue opens.
+
+### From an issue board
+
+You can create a new issue from an [issue board](../issue_board.md).
+
+Prerequisites:
+
+- You must have at least the [Guest role](../../permissions.md) for the project.
+
+To create an issue from a project issue board:
+
+1. On the top bar, select **Menu > Projects** and find your project.
+1. Select **Issues > Boards**.
+1. At the top of a board list, select **New issue** (**{plus-square}**).
+1. Enter the issue's title.
+1. Select **Create issue**.
+
+To create an issue from a group issue board:
+
+1. On the top bar, select **Menu > Groups** and find your group.
+1. Select **Issues > Boards**.
+1. At the top of a board list, select **New issue** (**{plus-square}**).
+1. Enter the issue's title.
+1. Under **Projects**, select the project in the group that the issue should belong to.
+1. Select **Create issue**.
+
+The issue is created and shows up in the board list. It shares the list's characteristic, so, for
+example, if the list is scoped to a label `Frontend`, the new issue also has this label.
+
+### By sending an email
+
+> Generated email address format changed in GitLab 11.7.
+> The older format is still supported, so existing aliases and contacts still work.
+
+You can send an email to create an issue in a project on the project's
+**Issues List** page.
+
+Prerequisites:
+
+- Your GitLab instance must have [incoming email](../../../administration/incoming_email.md)
+ configured.
+- There must be at least one issue in the issue list.
+- You must have at least the [Guest role](../../permissions.md) for the project.
+
+To email an issue to a project:
+
+1. On the top bar, select **Menu > Projects** and find your project.
+1. Select **Issues**.
+1. At the bottom of the page, select **Email a new issue to this project**.
+1. To copy the email address, select **Copy** (**{copy-to-clipboard}**).
+1. From your email client, send an email to this address.
+ The subject is used as the title of the new issue, and the email body becomes the description.
+ You can use [Markdown](../../markdown.md) and [quick actions](../quick_actions.md).
+
+A new issue is created, with your user as the author.
+You can save this address as a contact in your email client to use it again.
WARNING:
-This is a private email address, generated just for you. **Keep it to yourself**,
-as anyone who knows it can create issues or merge requests as if they
-were you. If the address is compromised, or you want to regenerate it,
-click **Email a new issue to this project**, followed by **reset it**.
+The email address you see is a private email address, generated just for you.
+**Keep it to yourself**, because anyone who knows it can create issues or merge requests as if they
+were you.
-Sending an email to this address creates a new issue associated with your account for
-this project, where:
+To regenerate the email address:
-- The email subject becomes the issue title.
-- The email body becomes the issue description.
-- [Markdown](../../markdown.md) and [quick actions](../quick_actions.md) are supported.
+1. On the issues list, select **Email a new issue to this project**.
+1. Select **reset this token**.
-NOTE:
-In GitLab 11.7, we updated the format of the generated email address. However the
-older format is still supported, allowing existing aliases or contacts to continue working.
-
-### New issue via URL with prefilled fields
+### Using a URL with prefilled values
To link directly to the new issue page with prefilled fields, use query
string parameters in a URL. You can embed a URL in an external
-HTML page to create issues with certain
-fields prefilled.
+HTML page to create issues with certain fields prefilled.
-| Field | URL Parameter Name | Notes |
-|----------------------|-----------------------|-------------------------------------------------------|
-| title | `issue[title]` | |
-| description | `issue[description]` | Cannot be used at the same time as `issuable_template`. |
-| description template | `issuable_template` | Cannot be used at the same time as `issue[description]`. |
-| issue type | `issue[issue_type]` | Either `incident` or `issue`. |
-| confidential | `issue[confidential]` | Parameter value must be `true` to set to confidential. |
+| Field | URL parameter | Notes |
+| -------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
+| Title | `issue[title]` | Must be [URL-encoded](../../../api/index.md#namespaced-path-encoding). |
+| Issue type | `issue[issue_type]` | Either `incident` or `issue`. |
+| Description template | `issuable_template` | Cannot be used at the same time as `issue[description]`. Must be [URL-encoded](../../../api/index.md#namespaced-path-encoding). |
+| Description | `issue[description]` | Cannot be used at the same time as `issuable_template`. Must be [URL-encoded](../../../api/index.md#namespaced-path-encoding). |
+| Confidential | `issue[confidential]` | If `true`, the issue is marked as confidential. |
-Follow these examples to form your new issue URL with prefilled fields.
+Adapt these examples to form your new issue URL with prefilled fields.
+To create an issue in the GitLab project:
-- For a new issue in the GitLab Community Edition project with a pre-filled title
- and a pre-filled description, the URL would be `https://gitlab.com/gitlab-org/gitlab-foss/-/issues/new?issue[title]=Validate%20new%20concept&issue[description]=Research%20idea`
-- For a new issue in the GitLab Community Edition project with a pre-filled title
- and a pre-filled description template, the URL would be `https://gitlab.com/gitlab-org/gitlab-foss/-/issues/new?issue[title]=Validate%20new%20concept&issuable_template=Research%20proposal`
-- For a new issue in the GitLab Community Edition project with a pre-filled title,
- a pre-filled description, and the confidential flag set, the URL would be `https://gitlab.com/gitlab-org/gitlab-foss/-/issues/new?issue[title]=Validate%20new%20concept&issue[description]=Research%20idea&issue[confidential]=true`
+- With a prefilled title and description:
+
+ ```plaintext
+ https://gitlab.com/gitlab-org/gitlab/-/issues/new?issue[title]=Whoa%2C%20we%27re%20half-way%20there&issue[description]=Whoa%2C%20livin%27%20in%20a%20URL
+ ```
+
+- With a prefilled title and description template:
+
+ ```plaintext
+ https://gitlab.com/gitlab-org/gitlab/-/issues/new?issue[title]=Validate%20new%20concept&issuable_template=Feature%20Proposal%20-%20basic
+ ```
+
+- With a prefilled title, description, and marked as confidential:
+
+ ```plaintext
+ https://gitlab.com/gitlab-org/gitlab/-/issues/new?issue[title]=Validate%20new%20concept&issue[description]=Research%20idea&issue[confidential]=true
+ ```
+
+### Using Service Desk
+
+To offer email support, enable [Service Desk](../service_desk.md) for your project.
+
+Now, when your customer sends a new email, a new issue can be created in
+the appropriate project and followed up from there.
+
+### Fields in the new issue form
+
+> Adding the new issue to an epic [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13847) in GitLab 13.1.
+
+When you're creating a new issue, you can complete the following fields:
+
+- Title
+- Type: either issue (default) or incident
+- [Description template](../description_templates.md): overwrites anything in the Description text box
+- Description: you can use [Markdown](../../markdown.md) and [quick actions](../quick_actions.md)
+- Checkbox to make the issue [confidential](confidential_issues.md)
+- [Assignees](#assignee)
+- [Weight](issue_weight.md)
+- [Epic](../../group/epics/index.md)
+- [Due date](due_dates.md)
+- [Milestone](../milestones/index.md)
+- [Labels](../labels.md)
## Edit an issue
You can edit an issue's title and description.
+Prerequisites:
+
+- You must have at least the [Reporter role](../../permissions.md) for a project.
+
To edit an issue, select **Edit title and description** (**{pencil}**).
### Bulk edit issues at the project level
diff --git a/doc/user/project/working_with_projects.md b/doc/user/project/working_with_projects.md
index 9469dead0dc..2bcfad97c25 100644
--- a/doc/user/project/working_with_projects.md
+++ b/doc/user/project/working_with_projects.md
@@ -4,7 +4,7 @@ group: Workspace
info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments"
---
-# Working with projects **(FREE)**
+# Manage projects **(FREE)**
Most work in GitLab is done in a [project](../../user/project/index.md). Files and
code are saved in projects, and most features are in the scope of projects.
@@ -16,7 +16,7 @@ To explore projects:
1. On the top bar, select **Menu > Projects**.
1. Select **Explore projects**.
-GitLab displays a list of projects, sorted by last updated date.
+The **Projects** page shows a list of projects, sorted by last updated date.
- To view projects with the most [stars](#star-a-project), select **Most stars**.
- To view projects with the largest number of comments in the past month, select **Trending**.
@@ -26,14 +26,37 @@ The **Explore projects** tab is visible to unauthenticated users unless the
[**Public** visibility level](../admin_area/settings/visibility_and_access_controls.md#restrict-visibility-levels)
is restricted. Then the tab is visible only to signed-in users.
+### Who can view the **Projects** page
+
+When you select a project, the project landing page shows the project contents.
+
+For public projects, and members of internal and private projects
+with [permissions to view the project's code](../permissions.md#project-members-permissions),
+the project landing page shows:
+
+- A [`README` or index file](repository/index.md#readme-and-index-files).
+- A list of directories in the project's repository.
+
+For users without permission to view the project's code, the landing page shows:
+
+- The wiki homepage.
+- The list of issues in the project.
+
+### Access a project page with the project ID
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/53671) in GitLab 11.8.
+
+To access a project from the GitLab UI using the project ID,
+visit the `/projects/:id` URL in your browser or other tool accessing the project.
+
## Explore topics
-You can explore popular project topics available on GitLab. To explore project topics:
+To explore project topics:
1. On the top bar, select **Menu > Projects**.
1. Select **Explore topics**.
-GitLab displays a list of topics sorted by the number of associated projects.
+The **Projects** page shows list of topics sorted by the number of associated projects.
To view projects associated with a topic, select a topic from the list.
You can assign topics to a project on the [Project Settings page](settings/index.md#topics).
@@ -260,9 +283,8 @@ To add a star to a project:
## Delete a project
-After you delete a project, projects in personal namespaces are deleted immediately. You can
-[enable delayed project removal](../group/index.md#enable-delayed-project-deletion) to
-delay deletion of projects in a group.
+After you delete a project, projects in personal namespaces are deleted immediately. To delay deletion of projects in a group
+you can [enable delayed project removal](../group/index.md#enable-delayed-project-deletion).
To delete a project:
@@ -299,56 +321,27 @@ To leave a project:
on the project dashboard when a project is part of a group under a
[group namespace](../group/index.md#namespaces).
-## Use your project as a Go package
+## Use a project as a Go package
-Any project can be used as a Go package. GitLab responds correctly to `go get`
-and `godoc.org` discovery requests, including the
-[`go-import`](https://golang.org/cmd/go/#hdr-Remote_import_paths) and
-[`go-source`](https://github.com/golang/gddo/wiki/Source-Code-Links) meta tags.
+Prerequisites:
-Private projects, including projects in subgroups, can be used as a Go package.
-These projects may require configuration to work correctly. GitLab responds correctly
-to `go get` discovery requests for projects that *are not* in subgroups,
-regardless of authentication or authorization.
-[Authentication](#authenticate-go-requests) is required to use a private project
-in a subgroup as a Go package. Otherwise, GitLab truncates the path for
-private projects in subgroups to the first two segments, causing `go get` to
-fail.
+- Contact your administrator to enable the [GitLab Go Proxy](../packages/go_proxy/index.md).
+- To use a private project in a subgroup as a Go package, you must [authenticate Go requests](#authenticate-go-requests-to-private-projects). Go requests that are not authenticated cause
+`go get` to fail. You don't need to authenticate Go requests for projects that are not in subgroups.
-GitLab implements its own Go proxy. This feature must be enabled by an
-administrator and requires additional configuration. See [GitLab Go
-Proxy](../packages/go_proxy/index.md).
+To use a project as a Go package, use the `go get` and `godoc.org` discovery requests. You can use the meta tags:
-### Disable Go module features for private projects
+- [`go-import`](https://golang.org/cmd/go/#hdr-Remote_import_paths)
+- [`go-source`](https://github.com/golang/gddo/wiki/Source-Code-Links)
-In Go 1.12 and later, Go queries module proxies and checksum databases in the
-process of [fetching a
-module](../../development/go_guide/dependencies.md#fetching). This can be
-selectively disabled with `GOPRIVATE` (disable both),
-[`GONOPROXY`](../../development/go_guide/dependencies.md#proxies) (disable proxy
-queries), and [`GONOSUMDB`](../../development/go_guide/dependencies.md#fetching)
-(disable checksum queries).
+### Authenticate Go requests to private projects
-`GOPRIVATE`, `GONOPROXY`, and `GONOSUMDB` are comma-separated lists of Go
-modules and Go module prefixes. For example,
-`GOPRIVATE=gitlab.example.com/my/private/project` disables queries for that
-one project, but `GOPRIVATE=gitlab.example.com` disables queries for *all*
-projects on GitLab.com. Go does not query module proxies if the module name or a
-prefix of it appears in `GOPRIVATE` or `GONOPROXY`. Go does not query checksum
-databases if the module name or a prefix of it appears in `GONOPRIVATE` or
-`GONOSUMDB`.
+Prerequisites:
-### Authenticate Go requests
+- Your GitLab instance must be accessible with HTTPS.
+- You must have a [personal access token](../profile/personal_access_tokens.md).
-To authenticate requests to private projects made by Go, use a [`.netrc`
-file](https://everything.curl.dev/usingcurl/netrc) and a [personal access
-token](../profile/personal_access_tokens.md) in the password field. **This only
-works if your GitLab instance can be accessed with HTTPS.** The `go` command
-does not transmit credentials over insecure connections. This authenticates
-all HTTPS requests made directly by Go, but does not authenticate requests made
-through Git.
-
-For example:
+To authenticate Go requests, create a [`.netrc`](https://everything.curl.dev/usingcurl/netrc) file with the following information:
```plaintext
machine gitlab.example.com
@@ -356,98 +349,106 @@ login
password
```
-NOTE:
On Windows, Go reads `~/_netrc` instead of `~/.netrc`.
-### Authenticate Git fetches
+The `go` command does not transmit credentials over insecure connections. It authenticates
+HTTPS requests made by Go, but does not authenticate requests made
+through Git.
-If a module cannot be fetched from a proxy, Go falls back to using Git (for
-GitLab projects). Git uses `.netrc` to authenticate requests. You can also
-configure Git to either:
+### Authenticate Git requests
-- Embed specific credentials in the request URL.
-- Use SSH instead of HTTPS, as Go always uses HTTPS to fetch Git repositories.
+If Go cannot fetch a module from a proxy, it uses Git. Git uses a `.netrc` file to authenticate requests, but you can
+configure other authentication methods.
-```shell
-# Embed credentials in any request to GitLab.com:
-git config --global url."https://${user}:${personal_access_token}@gitlab.example.com".insteadOf "https://gitlab.example.com"
+Configure Git to either:
-# Use SSH instead of HTTPS:
-git config --global url."git@gitlab.example.com".insteadOf "https://gitlab.example.com"
-```
+- Embed credentials in the request URL:
+
+ ```shell
+ git config --global url."https://${user}:${personal_access_token}@gitlab.example.com".insteadOf "https://gitlab.example.com"
+ ```
+
+- Use SSH instead of HTTPS:
+
+ ```shell
+ git config --global url."git@gitlab.example.com".insteadOf "https://gitlab.example.com"
+ ```
+
+### Disable Go module fetching for private projects
+
+To [fetch modules or packages](../../development/go_guide/dependencies.md#fetching), Go uses
+the [environment variables](../../development/go_guide/dependencies.md#proxies):
+
+- `GOPRIVATE`
+- `GONOPROXY`
+- `GONOSUMDB`
+
+To disable fetching:
+
+1. Disable `GOPRIVATE`:
+ - To disable queries for one project, disable `GOPRIVATE=gitlab.example.com/my/private/project`.
+ - To disable queries for all projects on GitLab.com, disable `GOPRIVATE=gitlab.example.com`.
+1. Disable proxy queries in `GONOPROXY`.
+1. Disable checksum queries in `GONOSUMDB`.
+
+- If the module name or its prefix is in `GOPRIVATE` or `GONOPROXY`, Go does not query module
+proxies.
+- If the module name or its prefix is in `GONOPRIVATE` or `GONOSUMDB`, Go does not query
+Checksum databases.
### Fetch Go modules from Geo secondary sites
-As Go modules are stored in Git repositories, you can use the [Geo](../../administration/geo/index.md)
-feature that allows Git repositories to be accessed on the secondary Geo servers.
+Use [Geo](../../administration/geo/index.md) to access Git repositories that contain Go modules
+on secondary Geo servers.
-In the following examples, the primary's site domain name is `gitlab.example.com`,
-and the secondary's is `gitlab-secondary.example.com`.
+You can use SSH or HTTP to access the Geo secondary server.
-`go get` will initially generate some HTTP traffic to the primary, but when the module
-download commences, the `insteadOf` configuration sends the traffic to the secondary.
+#### Use SSH to access the Geo secondary server
-#### Use SSH to access the Geo secondary
-
-To fetch Go modules from the secondary using SSH:
+To access the Geo secondary server with SSH:
1. Reconfigure Git on the client to send traffic for the primary to the secondary:
- ```plaintext
+ ```shell
git config --global url."git@gitlab-secondary.example.com".insteadOf "https://gitlab.example.com"
git config --global url."git@gitlab-secondary.example.com".insteadOf "http://gitlab.example.com"
```
-1. Ensure the client is set up for SSH access to GitLab repositories. This can be tested on the primary,
- and GitLab will replicate the public key to the secondary.
+ - For `gitlab.example.com`, use the primary site domain name.
+ - For `gitlab-secondary.example.com`, use the secondary site domain name.
+
+1. Ensure the client is set up for SSH access to GitLab repositories. You can test this on the primary,
+ and GitLab replicates the public key to the secondary.
+
+The `go get` request generates HTTP traffic to the primary Geo server. When the module
+download starts, the `insteadOf` configuration sends the traffic to the secondary Geo server.
#### Use HTTP to access the Geo secondary
-Using HTTP to fetch Go modules does not work with CI/CD job tokens, only with
-persistent access tokens that are replicated to the secondary.
+You must use persistent access tokens that replicate to the secondary server. You cannot use
+CI/CD job tokens to fetch Go modules with HTTP.
-To fetch Go modules from the secondary using HTTP:
+To access the Geo secondary server with HTTP:
-1. Put in place a Git `insteadOf` redirect on the client:
+1. Add a Git `insteadOf` redirect on the client:
- ```plaintext
+ ```shell
git config --global url."https://gitlab-secondary.example.com".insteadOf "https://gitlab.example.com"
```
-1. Generate a [personal access token](../profile/personal_access_tokens.md) and
- provide those credentials in the client's `~/.netrc` file:
+ - For `gitlab.example.com`, use the primary site domain name.
+ - For `gitlab-secondary.example.com`, use the secondary site domain name.
- ```plaintext
+1. Generate a [personal access token](../profile/personal_access_tokens.md) and
+ add the credentials in the client's `~/.netrc` file:
+
+ ```shell
machine gitlab.example.com login USERNAME password TOKEN
machine gitlab-secondary.example.com login USERNAME password TOKEN
```
-## Access project page with project ID
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/53671) in GitLab 11.8.
-
-To quickly access a project from the GitLab UI using the project ID,
-visit the `/projects/:id` URL in your browser or other tool accessing the project.
-
-## Project's landing page
-
-The project's landing page shows different information depending on
-the project's visibility settings and user permissions.
-
-For public projects, and to members of internal and private projects
-with [permissions to view the project's code](../permissions.md#project-members-permissions):
-
-- The content of a
- [`README` or an index file](repository/index.md#readme-and-index-files)
- is displayed (if any), followed by the list of directories in the
- project's repository.
-- If the project doesn't contain either of these files, the
- visitor sees the list of files and directories of the repository.
-
-For users without permissions to view the project's code, GitLab displays:
-
-- The wiki homepage, if any.
-- The list of issues in the project.
+The `go get` request generates HTTP traffic to the primary Geo server. When the module
+download starts, the `insteadOf` configuration sends the traffic to the secondary Geo server.
## Related topics
diff --git a/lib/gitlab/ci/templates/Security/API-Fuzzing.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/API-Fuzzing.latest.gitlab-ci.yml
index 544774d3b06..01041f4f056 100644
--- a/lib/gitlab/ci/templates/Security/API-Fuzzing.latest.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/API-Fuzzing.latest.gitlab-ci.yml
@@ -11,11 +11,11 @@
variables:
FUZZAPI_VERSION: "1"
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers"
- FUZZAPI_IMAGE: ${SECURE_ANALYZERS_PREFIX}/api-fuzzing:${FUZZAPI_VERSION}
+ FUZZAPI_IMAGE: api-fuzzing
apifuzzer_fuzz:
stage: fuzz
- image: $FUZZAPI_IMAGE
+ image: $SECURE_ANALYZERS_PREFIX/$FUZZAPI_IMAGE:$FUZZAPI_VERSION
allow_failure: true
rules:
- if: $API_FUZZING_DISABLED
diff --git a/lib/gitlab/ci/templates/Security/DAST-API.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/DAST-API.latest.gitlab-ci.yml
new file mode 100644
index 00000000000..57f1993921d
--- /dev/null
+++ b/lib/gitlab/ci/templates/Security/DAST-API.latest.gitlab-ci.yml
@@ -0,0 +1,52 @@
+# To contribute improvements to CI/CD templates, please follow the Development guide at:
+# https://docs.gitlab.com/ee/development/cicd/templates.html
+# This specific template is located at:
+# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/Dast-API.gitlab-ci.yml
+
+# To use this template, add the following to your .gitlab-ci.yml file:
+#
+# include:
+# template: DAST-API.latest.gitlab-ci.yml
+#
+# You also need to add a `dast` stage to your `stages:` configuration. A sample configuration for DAST API:
+#
+# stages:
+# - build
+# - test
+# - deploy
+# - dast
+
+# Read more about this feature here: https://docs.gitlab.com/ee/user/application_security/dast_api/index.html
+
+# Configure DAST API scanning with CI/CD variables (https://docs.gitlab.com/ee/ci/variables/index.html).
+# List of available variables: https://docs.gitlab.com/ee/user/application_security/dast_api/index.html#available-cicd-variables
+
+variables:
+ # Setting this variable affects all Security templates
+ # (SAST, Dependency Scanning, ...)
+ SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers"
+ #
+ DAST_API_VERSION: "1"
+ DAST_API_IMAGE: api-fuzzing
+
+dast_api:
+ stage: dast
+ image: $SECURE_ANALYZERS_PREFIX/$DAST_API_IMAGE:$DAST_API_VERSION
+ allow_failure: true
+ rules:
+ - if: $DAST_API_DISABLED
+ when: never
+ - if: $DAST_API_DISABLED_FOR_DEFAULT_BRANCH &&
+ $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
+ when: never
+ - if: $CI_COMMIT_BRANCH
+ script:
+ - /peach/analyzer-dast-api
+ artifacts:
+ when: always
+ paths:
+ - gl-assets
+ - gl-dast-api-report.json
+ - gl-*.log
+ reports:
+ dast: gl-dast-api-report.json
diff --git a/lib/gitlab/graphql/tracers/logger_tracer.rb b/lib/gitlab/graphql/tracers/logger_tracer.rb
index c7ba56824db..3302b2bae3f 100644
--- a/lib/gitlab/graphql/tracers/logger_tracer.rb
+++ b/lib/gitlab/graphql/tracers/logger_tracer.rb
@@ -11,19 +11,20 @@ module Gitlab
end
def trace(key, data)
- result = yield
-
+ yield
+ rescue StandardError => e
+ data[:exception] = e
+ raise e
+ ensure
case key
when "execute_query"
log_execute_query(**data)
end
-
- result
end
private
- def log_execute_query(query: nil, duration_s: 0)
+ def log_execute_query(query: nil, duration_s: 0, exception: nil)
# execute_query should always have :query, but we're just being defensive
return unless query
@@ -39,6 +40,8 @@ module Gitlab
query_string: query.query_string
}
+ Gitlab::ExceptionLogFormatter.format!(exception, info)
+
info.merge!(::Gitlab::ApplicationContext.current)
info.merge!(analysis_info) if analysis_info
diff --git a/lib/gitlab/graphql/tracers/timer_tracer.rb b/lib/gitlab/graphql/tracers/timer_tracer.rb
index 326620a22bc..8e058621110 100644
--- a/lib/gitlab/graphql/tracers/timer_tracer.rb
+++ b/lib/gitlab/graphql/tracers/timer_tracer.rb
@@ -17,13 +17,9 @@ module Gitlab
def trace(key, data)
start_time = Gitlab::Metrics::System.monotonic_time
- result = yield
-
- duration_s = Gitlab::Metrics::System.monotonic_time - start_time
-
- data[:duration_s] = duration_s
-
- result
+ yield
+ ensure
+ data[:duration_s] = Gitlab::Metrics::System.monotonic_time - start_time
end
end
end
diff --git a/lib/gitlab/usage_data_counters/known_events/ci_templates.yml b/lib/gitlab/usage_data_counters/known_events/ci_templates.yml
index 40922433635..d90960b344c 100644
--- a/lib/gitlab/usage_data_counters/known_events/ci_templates.yml
+++ b/lib/gitlab/usage_data_counters/known_events/ci_templates.yml
@@ -119,6 +119,10 @@
category: ci_templates
redis_slot: ci_templates
aggregation: weekly
+- name: p_ci_templates_security_dast_api_latest
+ category: ci_templates
+ redis_slot: ci_templates
+ aggregation: weekly
- name: p_ci_templates_security_container_scanning
category: ci_templates
redis_slot: ci_templates
@@ -551,6 +555,10 @@
category: ci_templates
redis_slot: ci_templates
aggregation: weekly
+- name: p_ci_templates_implicit_security_dast_api_latest
+ category: ci_templates
+ redis_slot: ci_templates
+ aggregation: weekly
- name: p_ci_templates_implicit_security_container_scanning
category: ci_templates
redis_slot: ci_templates
diff --git a/qa/qa/page/project/settings/advanced.rb b/qa/qa/page/project/settings/advanced.rb
index 0ba856e8a6e..da1f16f4cfc 100644
--- a/qa/qa/page/project/settings/advanced.rb
+++ b/qa/qa/page/project/settings/advanced.rb
@@ -5,7 +5,6 @@ module QA
module Project
module Settings
class Advanced < Page::Base
- include Component::Select2
include Component::ConfirmModal
view 'app/views/projects/edit.html.haml' do
@@ -13,8 +12,10 @@ module QA
element :change_path_button
end
- view 'app/views/projects/_transfer.html.haml' do
- element :transfer_button
+ view "app/assets/javascripts/vue_shared/components/namespace_select/namespace_select.vue" do
+ element :namespaces_list
+ element :namespaces_list_groups
+ element :namespaces_list_item
end
view 'app/views/projects/settings/_archive.html.haml' do
@@ -42,16 +43,22 @@ module QA
click_element :change_path_button
end
+ def select_namespace(item)
+ click_element :namespaces_list
+
+ within_element(:namespaces_list) do
+ find_element(:namespaces_list_item, text: item).click
+ end
+ end
+
def transfer_project!(project_name, namespace)
QA::Runtime::Logger.info "Transferring project: #{project_name} to namespace: #{namespace}"
click_element_coordinates(:archive_project_content)
- expand_select_list
-
# Workaround for a failure to search when there are no spaces around the /
# https://gitlab.com/gitlab-org/gitlab/-/issues/218965
- search_and_select(namespace.gsub(%r{([^\s])/([^\s])}, '\1 / \2'))
+ select_namespace(namespace.gsub(%r{([^\s])/([^\s])}, '\1 / \2'))
click_element(:transfer_button)
fill_confirmation_text(project_name)
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index a0f0b388b9e..950be154096 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -899,10 +899,34 @@ RSpec.describe ProjectsController do
describe '#transfer', :enable_admin_mode do
render_views
- let_it_be(:project, reload: true) { create(:project) }
+ let(:project) { create(:project) }
+
let_it_be(:admin) { create(:admin) }
let_it_be(:new_namespace) { create(:namespace) }
+ shared_examples 'project namespace is not changed' do |flash_message|
+ it 'project namespace is not changed' do
+ controller.instance_variable_set(:@project, project)
+ sign_in(admin)
+
+ old_namespace = project.namespace
+
+ put :transfer,
+ params: {
+ namespace_id: old_namespace.path,
+ new_namespace_id: new_namespace_id,
+ id: project.path
+ },
+ format: :js
+
+ project.reload
+
+ expect(project.namespace).to eq(old_namespace)
+ expect(response).to redirect_to(edit_project_path(project))
+ expect(flash[:alert]).to eq flash_message
+ end
+ end
+
it 'updates namespace' do
sign_in(admin)
@@ -921,26 +945,15 @@ RSpec.describe ProjectsController do
end
context 'when new namespace is empty' do
- it 'project namespace is not changed' do
- controller.instance_variable_set(:@project, project)
- sign_in(admin)
+ let(:new_namespace_id) { nil }
- old_namespace = project.namespace
+ it_behaves_like 'project namespace is not changed', s_('TransferProject|Please select a new namespace for your project.')
+ end
- put :transfer,
- params: {
- namespace_id: old_namespace.path,
- new_namespace_id: nil,
- id: project.path
- },
- format: :js
+ context 'when new namespace is the same as the current namespace' do
+ let(:new_namespace_id) { project.namespace.id }
- project.reload
-
- expect(project.namespace).to eq(old_namespace)
- expect(response).to have_gitlab_http_status(:ok)
- expect(flash[:alert]).to eq s_('TransferProject|Please select a new namespace for your project.')
- end
+ it_behaves_like 'project namespace is not changed', s_('TransferProject|Project is already in this namespace.')
end
end
diff --git a/spec/features/issuables/shortcuts_issuable_spec.rb b/spec/features/issuables/shortcuts_issuable_spec.rb
index 78cd8d0bef3..7e8f39c47a7 100644
--- a/spec/features/issuables/shortcuts_issuable_spec.rb
+++ b/spec/features/issuables/shortcuts_issuable_spec.rb
@@ -44,4 +44,90 @@ RSpec.describe 'Blob shortcuts', :js do
include_examples 'quotes the selected text'
end
end
+
+ shared_examples "opens assignee dropdown for editing" do
+ it "opens assignee dropdown for editing" do
+ find('body').native.send_key('a')
+
+ expect(find('.block.assignee')).to have_selector('.js-sidebar-assignee-data')
+ end
+ end
+
+ describe 'pressing "a"' do
+ describe 'On an Issue' do
+ before do
+ stub_feature_flags(issue_assignees_widget: false)
+ visit project_issue_path(project, issue)
+ wait_for_requests
+ end
+
+ include_examples 'opens assignee dropdown for editing'
+ end
+
+ describe 'On a Merge Request' do
+ before do
+ stub_feature_flags(issue_assignees_widget: false)
+ visit project_merge_request_path(project, merge_request)
+ wait_for_requests
+ end
+
+ include_examples 'opens assignee dropdown for editing'
+ end
+ end
+
+ shared_examples "opens milestones dropdown for editing" do
+ it "opens milestones dropdown for editing" do
+ find('body').native.send_key('m')
+
+ expect(find('[data-testid="milestone-edit"]')).to have_selector('.gl-new-dropdown-inner')
+ end
+ end
+
+ describe 'pressing "m"' do
+ describe 'On an Issue' do
+ before do
+ visit project_issue_path(project, issue)
+ wait_for_requests
+ end
+
+ include_examples 'opens milestones dropdown for editing'
+ end
+
+ describe 'On a Merge Request' do
+ before do
+ visit project_merge_request_path(project, merge_request)
+ wait_for_requests
+ end
+
+ include_examples 'opens milestones dropdown for editing'
+ end
+ end
+
+ shared_examples "opens labels dropdown for editing" do
+ it "opens labels dropdown for editing" do
+ find('body').native.send_key('l')
+
+ expect(find('.js-labels-block')).to have_selector('[data-testid="labels-select-dropdown-contents"]')
+ end
+ end
+
+ describe 'pressing "l"' do
+ describe 'On an Issue' do
+ before do
+ visit project_issue_path(project, issue)
+ wait_for_requests
+ end
+
+ include_examples 'opens labels dropdown for editing'
+ end
+
+ describe 'On a Merge Request' do
+ before do
+ visit project_merge_request_path(project, merge_request)
+ wait_for_requests
+ end
+
+ include_examples 'opens labels dropdown for editing'
+ end
+ end
end
diff --git a/spec/features/projects/settings/user_transfers_a_project_spec.rb b/spec/features/projects/settings/user_transfers_a_project_spec.rb
index ba4c379ef0a..a88b9101869 100644
--- a/spec/features/projects/settings/user_transfers_a_project_spec.rb
+++ b/spec/features/projects/settings/user_transfers_a_project_spec.rb
@@ -8,6 +8,8 @@ RSpec.describe 'Projects > Settings > User transfers a project', :js do
let(:group) { create(:group) }
before do
+ stub_const('Gitlab::QueryLimiting::Transaction::THRESHOLD', 120)
+
group.add_owner(user)
sign_in(user)
end
@@ -16,10 +18,12 @@ RSpec.describe 'Projects > Settings > User transfers a project', :js do
visit edit_project_path(project)
page.within('.js-project-transfer-form') do
- page.find('.select2-container').click
+ page.find('[data-testid="transfer-project-namespace"]').click
end
- page.find("div[role='option']", text: group.full_name).click
+ page.within('[data-testid="transfer-project-namespace"]') do
+ page.find("li button", text: group.full_name).click
+ end
click_button('Transfer project')
diff --git a/spec/frontend/projects/settings/components/transfer_project_form_spec.js b/spec/frontend/projects/settings/components/transfer_project_form_spec.js
new file mode 100644
index 00000000000..f7ce7c6f840
--- /dev/null
+++ b/spec/frontend/projects/settings/components/transfer_project_form_spec.js
@@ -0,0 +1,68 @@
+import { namespaces } from 'jest/vue_shared/components/namespace_select/mock_data';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import TransferProjectForm from '~/projects/settings/components/transfer_project_form.vue';
+import NamespaceSelect from '~/vue_shared/components/namespace_select/namespace_select.vue';
+import ConfirmDanger from '~/vue_shared/components/confirm_danger/confirm_danger.vue';
+
+describe('Transfer project form', () => {
+ let wrapper;
+
+ const confirmButtonText = 'Confirm';
+ const confirmationPhrase = 'You must construct additional pylons!';
+
+ const createComponent = () =>
+ shallowMountExtended(TransferProjectForm, {
+ propsData: {
+ namespaces,
+ confirmButtonText,
+ confirmationPhrase,
+ },
+ });
+
+ const findNamespaceSelect = () => wrapper.findComponent(NamespaceSelect);
+ const findConfirmDanger = () => wrapper.findComponent(ConfirmDanger);
+
+ beforeEach(() => {
+ wrapper = createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders the namespace selector', () => {
+ expect(findNamespaceSelect().exists()).toBe(true);
+ });
+
+ it('renders the confirm button', () => {
+ expect(findConfirmDanger().exists()).toBe(true);
+ });
+
+ it('disables the confirm button by default', () => {
+ expect(findConfirmDanger().attributes('disabled')).toBe('true');
+ });
+
+ describe('with a selected namespace', () => {
+ const [selectedItem] = namespaces.group;
+
+ beforeEach(() => {
+ findNamespaceSelect().vm.$emit('select', selectedItem);
+ });
+
+ it('emits the `selectNamespace` event when a namespace is selected', () => {
+ const args = [selectedItem.id];
+
+ expect(wrapper.emitted('selectNamespace')).toEqual([args]);
+ });
+
+ it('enables the confirm button', () => {
+ expect(findConfirmDanger().attributes('disabled')).toBeUndefined();
+ });
+
+ it('clicking the confirm button emits the `confirm` event', () => {
+ findConfirmDanger().vm.$emit('confirm');
+
+ expect(wrapper.emitted('confirm')).toBeDefined();
+ });
+ });
+});
diff --git a/spec/frontend/transfer_edit_spec.js b/spec/frontend/transfer_edit_spec.js
index ad8c9c68f37..4091d753fe5 100644
--- a/spec/frontend/transfer_edit_spec.js
+++ b/spec/frontend/transfer_edit_spec.js
@@ -4,11 +4,11 @@ import { loadHTMLFixture } from 'helpers/fixtures';
import setupTransferEdit from '~/transfer_edit';
describe('setupTransferEdit', () => {
- const formSelector = '.js-project-transfer-form';
- const targetSelector = 'select.select2';
+ const formSelector = '.js-group-transfer-form';
+ const targetSelector = '#new_parent_group_id';
beforeEach(() => {
- loadHTMLFixture('projects/edit.html');
+ loadHTMLFixture('groups/edit.html');
setupTransferEdit(formSelector, targetSelector);
});
@@ -17,8 +17,8 @@ describe('setupTransferEdit', () => {
});
it('enables submit button when selection changes to non-empty value', () => {
- const nonEmptyValue = $(formSelector).find(targetSelector).find('option').not(':empty').val();
- $(formSelector).find(targetSelector).val(nonEmptyValue).trigger('change');
+ const lastValue = $(formSelector).find(targetSelector).find('.dropdown-content li').last();
+ $(formSelector).find(targetSelector).val(lastValue).trigger('change');
expect($(formSelector).find(':submit').prop('disabled')).toBeFalsy();
});
diff --git a/spec/frontend/vue_shared/components/namespace_select/mock_data.js b/spec/frontend/vue_shared/components/namespace_select/mock_data.js
new file mode 100644
index 00000000000..c9d96672e85
--- /dev/null
+++ b/spec/frontend/vue_shared/components/namespace_select/mock_data.js
@@ -0,0 +1,11 @@
+export const group = [
+ { id: 1, name: 'Group 1', humanName: 'Group 1' },
+ { id: 2, name: 'Subgroup 1', humanName: 'Group 1 / Subgroup 1' },
+];
+
+export const user = [{ id: 3, name: 'User namespace 1', humanName: 'User namespace 1' }];
+
+export const namespaces = {
+ group,
+ user,
+};
diff --git a/spec/frontend/vue_shared/components/namespace_select/namespace_select_spec.js b/spec/frontend/vue_shared/components/namespace_select/namespace_select_spec.js
new file mode 100644
index 00000000000..8f07f63993d
--- /dev/null
+++ b/spec/frontend/vue_shared/components/namespace_select/namespace_select_spec.js
@@ -0,0 +1,86 @@
+import { GlDropdown, GlDropdownItem, GlDropdownSectionHeader } from '@gitlab/ui';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import NamespaceSelect, {
+ i18n,
+} from '~/vue_shared/components/namespace_select/namespace_select.vue';
+import { user, group, namespaces } from './mock_data';
+
+describe('Namespace Select', () => {
+ let wrapper;
+
+ const createComponent = (props = {}) =>
+ shallowMountExtended(NamespaceSelect, {
+ propsData: {
+ data: namespaces,
+ ...props,
+ },
+ });
+
+ const wrappersText = (arr) => arr.wrappers.map((w) => w.text());
+ const flatNamespaces = () => [...group, ...user];
+ const findDropdown = () => wrapper.findComponent(GlDropdown);
+ const findDropdownAttributes = (attr) => findDropdown().attributes(attr);
+ const selectedDropdownItemText = () => findDropdownAttributes('text');
+ const findDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
+ const findSectionHeaders = () => wrapper.findAllComponents(GlDropdownSectionHeader);
+
+ beforeEach(() => {
+ wrapper = createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders the dropdown', () => {
+ expect(findDropdown().exists()).toBe(true);
+ });
+
+ it('renders each dropdown item', () => {
+ const items = findDropdownItems().wrappers;
+ expect(items).toHaveLength(flatNamespaces().length);
+ });
+
+ it('renders the human name for each item', () => {
+ const dropdownItems = wrappersText(findDropdownItems());
+ const flatNames = flatNamespaces().map(({ humanName }) => humanName);
+ expect(dropdownItems).toEqual(flatNames);
+ });
+
+ it('sets the initial dropdown text', () => {
+ expect(selectedDropdownItemText()).toBe(i18n.DEFAULT_TEXT);
+ });
+
+ it('splits group and user namespaces', () => {
+ const headers = findSectionHeaders();
+ expect(headers).toHaveLength(2);
+ expect(wrappersText(headers)).toEqual([i18n.GROUPS, i18n.USERS]);
+ });
+
+ it('sets the dropdown to full width', () => {
+ expect(findDropdownAttributes('block')).toBeUndefined();
+
+ wrapper = createComponent({ fullWidth: true });
+
+ expect(findDropdownAttributes('block')).not.toBeUndefined();
+ expect(findDropdownAttributes('block')).toBe('true');
+ });
+
+ describe('with a selected namespace', () => {
+ const selectedGroupIndex = 1;
+ const selectedItem = group[selectedGroupIndex];
+
+ beforeEach(() => {
+ findDropdownItems().at(selectedGroupIndex).vm.$emit('click');
+ });
+
+ it('sets the dropdown text', () => {
+ expect(selectedDropdownItemText()).toBe(selectedItem.humanName);
+ });
+
+ it('emits the `select` event when a namespace is selected', () => {
+ const args = [selectedItem];
+ expect(wrapper.emitted('select')).toEqual([args]);
+ });
+ });
+});
diff --git a/spec/helpers/namespaces_helper_spec.rb b/spec/helpers/namespaces_helper_spec.rb
index 68bc19cb429..6eb560e3f5c 100644
--- a/spec/helpers/namespaces_helper_spec.rb
+++ b/spec/helpers/namespaces_helper_spec.rb
@@ -45,6 +45,39 @@ RSpec.describe NamespacesHelper do
user_group.add_owner(user)
end
+ describe '#namespaces_as_json' do
+ let(:result) { helper.namespaces_as_json(user) }
+
+ before do
+ allow(helper).to receive(:current_user).and_return(user)
+ end
+
+ it 'returns the user\'s groups' do
+ json_data = Gitlab::Json.parse(result)
+
+ expect(result).to include('group')
+ expect(json_data['group']).to include(
+ "id" => user_group.id,
+ "name" => user_group.name,
+ "display_path" => user_group.full_path,
+ "human_name" => user_group.human_name
+ )
+ end
+
+ it 'returns the user\'s namespace' do
+ user_namespace = user.namespace
+ json_data = Gitlab::Json.parse(result)
+
+ expect(result).to include('user')
+ expect(json_data['user']).to include(
+ "id" => user_namespace.id,
+ "name" => user_namespace.name,
+ "display_path" => user_namespace.full_path,
+ "human_name" => user_namespace.human_name
+ )
+ end
+ end
+
describe '#namespaces_options' do
context 'when admin mode is enabled', :enable_admin_mode do
it 'returns groups without being a member for admin' do
diff --git a/spec/lib/gitlab/graphql/tracers/logger_tracer_spec.rb b/spec/lib/gitlab/graphql/tracers/logger_tracer_spec.rb
index d83ac4dabc5..5bc077a963e 100644
--- a/spec/lib/gitlab/graphql/tracers/logger_tracer_spec.rb
+++ b/spec/lib/gitlab/graphql/tracers/logger_tracer_spec.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: true
-require "fast_spec_helper"
-require "support/graphql/fake_query_type"
+require "spec_helper"
RSpec.describe Gitlab::Graphql::Tracers::LoggerTracer do
let(:dummy_schema) do
@@ -49,4 +48,15 @@ RSpec.describe Gitlab::Graphql::Tracers::LoggerTracer do
dummy_schema.execute(query_string, variables: variables)
end
+
+ it 'logs exceptions for breaking queries' do
+ query_string = "query fooOperation { breakingField }"
+
+ expect(::Gitlab::GraphqlLogger).to receive(:info).with(a_hash_including({
+ 'exception.message' => 'This field is supposed to break',
+ 'exception.class' => 'RuntimeError'
+ }))
+
+ expect { dummy_schema.execute(query_string) }.to raise_error(/This field is supposed to break/)
+ end
end
diff --git a/spec/lib/gitlab/graphql/tracers/metrics_tracer_spec.rb b/spec/lib/gitlab/graphql/tracers/metrics_tracer_spec.rb
index ff6a76aa319..168f5aa529e 100644
--- a/spec/lib/gitlab/graphql/tracers/metrics_tracer_spec.rb
+++ b/spec/lib/gitlab/graphql/tracers/metrics_tracer_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'spec_helper'
require 'rspec-parameterized'
require "support/graphql/fake_query_type"
@@ -36,7 +36,7 @@ RSpec.describe Gitlab::Graphql::Tracers::MetricsTracer do
end
with_them do
- it 'increments sli' do
+ it 'increments apdex sli' do
# Trigger initialization
fake_schema
@@ -56,5 +56,13 @@ RSpec.describe Gitlab::Graphql::Tracers::MetricsTracer do
fake_schema.execute("query lorem { helloWorld }")
end
end
+
+ it "does not record apdex for failing queries" do
+ query_string = "query fooOperation { breakingField }"
+
+ expect(Gitlab::Metrics::RailsSlis.graphql_query_apdex).not_to receive(:increment)
+
+ expect { fake_schema.execute(query_string) }.to raise_error(/This field is supposed to break/)
+ end
end
end
diff --git a/spec/lib/gitlab/graphql/tracers/timer_tracer_spec.rb b/spec/lib/gitlab/graphql/tracers/timer_tracer_spec.rb
index 7f837e28772..986120dcd95 100644
--- a/spec/lib/gitlab/graphql/tracers/timer_tracer_spec.rb
+++ b/spec/lib/gitlab/graphql/tracers/timer_tracer_spec.rb
@@ -20,6 +20,7 @@ RSpec.describe Gitlab::Graphql::Tracers::TimerTracer do
before do
current_time = 0
+ allow(tracer_spy).to receive(:trace)
allow(Gitlab::Metrics::System).to receive(:monotonic_time) do
current_time += expected_duration
end
@@ -30,6 +31,18 @@ RSpec.describe Gitlab::Graphql::Tracers::TimerTracer do
dummy_schema.execute(query_string)
+ expect_to_have_traced(tracer_spy, expected_duration, query_string)
+ end
+
+ it "adds a duration_s even if the query failed" do
+ query_string = "query fooOperation { breakingField }"
+
+ expect { dummy_schema.execute(query_string) }.to raise_error(/This field is supposed to break/)
+
+ expect_to_have_traced(tracer_spy, expected_duration, query_string)
+ end
+
+ def expect_to_have_traced(tracer_spy, expected_duration, query_string)
# "parse" and "execute_query" are just arbitrary trace events
expect(tracer_spy).to have_received(:trace).with("parse", {
duration_s: expected_duration,
diff --git a/spec/models/lfs_objects_project_spec.rb b/spec/models/lfs_objects_project_spec.rb
index df49b60c4fa..7378beeed06 100644
--- a/spec/models/lfs_objects_project_spec.rb
+++ b/spec/models/lfs_objects_project_spec.rb
@@ -25,6 +25,28 @@ RSpec.describe LfsObjectsProject do
end
end
+ describe '#link_to_project!' do
+ it 'does not throw error when duplicate exists' do
+ subject
+
+ expect do
+ result = described_class.link_to_project!(subject.lfs_object, subject.project)
+ expect(result).to be_a(LfsObjectsProject)
+ end.not_to change { described_class.count }
+ end
+
+ it 'upserts a new entry and updates the project cache' do
+ new_project = create(:project)
+
+ allow(ProjectCacheWorker).to receive(:perform_async).and_call_original
+ expect(ProjectCacheWorker).to receive(:perform_async).with(new_project.id, [], [:lfs_objects_size])
+ expect { described_class.link_to_project!(subject.lfs_object, new_project) }
+ .to change { described_class.count }
+
+ expect(described_class.find_by(lfs_object_id: subject.lfs_object.id, project_id: new_project.id)).to be_present
+ end
+ end
+
describe '#update_project_statistics' do
it 'updates project statistics when the object is added' do
expect(ProjectCacheWorker).to receive(:perform_async)
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 20fec9c21a2..5cdcffb98d1 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -3581,6 +3581,29 @@ RSpec.describe Project, factory_default: :keep do
expect(project.forks).to contain_exactly(forked_project)
end
end
+
+ describe '#lfs_object_oids_from_fork_source' do
+ let_it_be(:lfs_object) { create(:lfs_object) }
+ let_it_be(:another_lfs_object) { create(:lfs_object) }
+
+ let(:oids) { [lfs_object.oid, another_lfs_object.oid] }
+
+ context 'when fork has one of two LFS objects' do
+ before do
+ create(:lfs_objects_project, lfs_object: lfs_object, project: project)
+ create(:lfs_objects_project, lfs_object: another_lfs_object, project: forked_project)
+ end
+
+ it 'returns OIDs of owned LFS objects', :aggregate_failures do
+ expect(forked_project.lfs_objects_oids_from_fork_source(oids: oids)).to eq([lfs_object.oid])
+ expect(forked_project.lfs_objects_oids(oids: oids)).to eq([another_lfs_object.oid])
+ end
+
+ it 'returns empty when project is not a fork' do
+ expect(project.lfs_objects_oids_from_fork_source(oids: oids)).to eq([])
+ end
+ end
+ end
end
it_behaves_like 'can housekeep repository' do
diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb
index 656ae744ac1..47786e20ed6 100644
--- a/spec/requests/lfs_http_spec.rb
+++ b/spec/requests/lfs_http_spec.rb
@@ -518,13 +518,43 @@ RSpec.describe 'Git LFS API and storage' do
end
context 'in source of fork project' do
+ let(:other_project) { create(:project, :empty_repo) }
let(:project) { fork_project(other_project) }
before do
lfs_object.update!(projects: [other_project])
end
- it_behaves_like 'batch upload with existing LFS object'
+ context 'when user has access to both the parent and fork' do
+ before do
+ project.add_developer(user)
+ other_project.add_developer(user)
+ end
+
+ it 'links existing LFS objects to other project' do
+ expect(json_response['objects']).to be_kind_of(Array)
+ expect(json_response['objects'].first).to include(sample_object)
+ expect(json_response['objects'].first).not_to have_key('actions')
+
+ expect(lfs_object.reload.projects.pluck(:id)).to match_array([other_project.id, project.id])
+ end
+
+ context 'when feature flag is disabled' do
+ before do
+ stub_feature_flags(lfs_auto_link_fork_source: false)
+ end
+
+ it_behaves_like 'batch upload with existing LFS object'
+ end
+ end
+
+ context 'when user does not have access to parent' do
+ before do
+ project.add_developer(user)
+ end
+
+ it_behaves_like 'batch upload with existing LFS object'
+ end
end
end
diff --git a/spec/services/groups/update_shared_runners_service_spec.rb b/spec/services/groups/update_shared_runners_service_spec.rb
index 53870e810b1..6e938984052 100644
--- a/spec/services/groups/update_shared_runners_service_spec.rb
+++ b/spec/services/groups/update_shared_runners_service_spec.rb
@@ -63,6 +63,8 @@ RSpec.describe Groups::UpdateSharedRunnersService do
let_it_be(:pending_build_2) { create(:ci_pending_build, project: project, instance_runners_enabled: false) }
it 'updates pending builds for the group' do
+ expect(::Ci::UpdatePendingBuildService).to receive(:new).and_call_original
+
subject
expect(pending_build_1.reload.instance_runners_enabled).to be_truthy
@@ -73,6 +75,8 @@ RSpec.describe Groups::UpdateSharedRunnersService do
let(:params) { { shared_runners_setting: 'invalid_enabled' } }
it 'does not update pending builds for the group' do
+ expect(::Ci::UpdatePendingBuildService).not_to receive(:new).and_call_original
+
subject
expect(pending_build_1.reload.instance_runners_enabled).to be_falsey
@@ -99,6 +103,8 @@ RSpec.describe Groups::UpdateSharedRunnersService do
let_it_be(:pending_build_2) { create(:ci_pending_build, project: project, instance_runners_enabled: true) }
it 'updates pending builds for the group' do
+ expect(::Ci::UpdatePendingBuildService).to receive(:new).and_call_original
+
subject
expect(pending_build_1.reload.instance_runners_enabled).to be_falsey
diff --git a/spec/support/graphql/fake_query_type.rb b/spec/support/graphql/fake_query_type.rb
index ffd851a6e6a..18cf2cf3e82 100644
--- a/spec/support/graphql/fake_query_type.rb
+++ b/spec/support/graphql/fake_query_type.rb
@@ -1,15 +1,22 @@
# frozen_string_literal: true
+require 'graphql'
module Graphql
- class FakeQueryType < Types::BaseObject
+ class FakeQueryType < ::GraphQL::Schema::Object
graphql_name 'FakeQuery'
field :hello_world, String, null: true do
argument :message, String, required: false
end
+ field :breaking_field, String, null: true
+
def hello_world(message: "world")
"Hello #{message}!"
end
+
+ def breaking_field
+ raise "This field is supposed to break"
+ end
end
end