- {{
- s__(
- 'Hierarchy|Is there a framework or type of work item you wish you had access to in GitLab? Give us your feedback and help us build the experiences valuable to you.',
- )
- }}
-
-
-
{{ s__('Hierarchy|Planning hierarchy') }}
-
- {{
- s__(
- 'Hierarchy|Deliver value more efficiently by breaking down necessary work into a hierarchical structure. This structure helps teams understand scope, priorities, and how work cascades up toward larger goals.',
- )
- }}
-
-
-
{{ s__('Hierarchy|Current structure') }}
-
{{ s__('Hierarchy|You can start using these items now.') }}
-
-
-
- {{ s__('Hierarchy|Unavailable structure') }}
-
-
- {{ s__('Hierarchy|These items are unavailable in the current structure.') }}
-
-
diff --git a/app/assets/javascripts/work_items_hierarchy/constants.js b/app/assets/javascripts/work_items_hierarchy/constants.js
deleted file mode 100644
index f001f556c0e..00000000000
--- a/app/assets/javascripts/work_items_hierarchy/constants.js
+++ /dev/null
@@ -1,12 +0,0 @@
-export const WORK_ITEMS_SURVEY_COOKIE_NAME = 'hide_work_items_hierarchy_survey';
-
-/**
- * Hard-coded strings since we're rendering hierarchy
- * items from mock responses. Remove this when we
- * have a real hierarchy endpoint.
- */
-export const LICENSE_PLAN = {
- FREE: 'free',
- PREMIUM: 'premium',
- ULTIMATE: 'ultimate',
-};
diff --git a/app/assets/javascripts/work_items_hierarchy/hierarchy_util.js b/app/assets/javascripts/work_items_hierarchy/hierarchy_util.js
deleted file mode 100644
index 61d93acdb91..00000000000
--- a/app/assets/javascripts/work_items_hierarchy/hierarchy_util.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import { LICENSE_PLAN } from './constants';
-
-export function inferLicensePlan({ hasSubEpics, hasEpics }) {
- if (hasSubEpics) {
- return LICENSE_PLAN.ULTIMATE;
- } else if (hasEpics) {
- return LICENSE_PLAN.PREMIUM;
- }
- return LICENSE_PLAN.FREE;
-}
diff --git a/app/assets/javascripts/work_items_hierarchy/static_response.js b/app/assets/javascripts/work_items_hierarchy/static_response.js
deleted file mode 100644
index d1e2e486082..00000000000
--- a/app/assets/javascripts/work_items_hierarchy/static_response.js
+++ /dev/null
@@ -1,142 +0,0 @@
-const FREE_TIER = 'free';
-const ULTIMATE_TIER = 'ultimate';
-const PREMIUM_TIER = 'premium';
-
-const RESPONSE = {
- [FREE_TIER]: [
- {
- id: '1',
- type: 'ISSUE',
- available: true,
- license: null,
- nestedTypes: null,
- },
- {
- id: '2',
- type: 'TASK',
- available: true,
- license: null,
- nestedTypes: null,
- },
- {
- id: '3',
- type: 'INCIDENT',
- available: true,
- license: null,
- nestedTypes: null,
- },
- {
- id: '4',
- type: 'EPIC',
- available: false,
- license: 'Premium', // eslint-disable-line @gitlab/require-i18n-strings
- nestedTypes: null,
- },
- {
- id: '5',
- type: 'SUB_EPIC',
- available: false,
- license: 'Ultimate', // eslint-disable-line @gitlab/require-i18n-strings
- nestedTypes: null,
- },
- {
- id: '6',
- type: 'REQUIREMENT',
- available: false,
- license: 'Ultimate', // eslint-disable-line @gitlab/require-i18n-strings
- nestedTypes: null,
- },
- {
- id: '7',
- type: 'TEST_CASE',
- available: false,
- license: 'Ultimate', // eslint-disable-line @gitlab/require-i18n-strings
- nestedTypes: null,
- },
- ],
-
- [PREMIUM_TIER]: [
- {
- id: '1',
- type: 'EPIC',
- available: true,
- license: null,
- nestedTypes: ['ISSUE'],
- },
- {
- id: '2',
- type: 'TASK',
- available: true,
- license: null,
- nestedTypes: null,
- },
- {
- id: '3',
- type: 'INCIDENT',
- available: true,
- license: null,
- nestedTypes: null,
- },
- {
- id: '5',
- type: 'SUB_EPIC',
- available: false,
- license: 'Ultimate', // eslint-disable-line @gitlab/require-i18n-strings
- nestedTypes: null,
- },
- {
- id: '6',
- type: 'REQUIREMENT',
- available: false,
- license: 'Ultimate', // eslint-disable-line @gitlab/require-i18n-strings
- nestedTypes: null,
- },
- {
- id: '7',
- type: 'TEST_CASE',
- available: false,
- license: 'Ultimate', // eslint-disable-line @gitlab/require-i18n-strings
- nestedTypes: null,
- },
- ],
-
- [ULTIMATE_TIER]: [
- {
- id: '1',
- type: 'EPIC',
- available: true,
- license: null,
- nestedTypes: ['SUB_EPIC', 'ISSUE'],
- },
- {
- id: '2',
- type: 'TASK',
- available: true,
- license: null,
- nestedTypes: null,
- },
- {
- id: '3',
- type: 'INCIDENT',
- available: true,
- license: null,
- nestedTypes: null,
- },
- {
- id: '6',
- type: 'REQUIREMENT',
- available: true,
- license: null,
- nestedTypes: null,
- },
- {
- id: '7',
- type: 'TEST_CASE',
- available: true,
- license: null,
- nestedTypes: null,
- },
- ],
-};
-
-export default RESPONSE;
diff --git a/app/assets/javascripts/work_items_hierarchy/work_items_hierarchy_bundle.js b/app/assets/javascripts/work_items_hierarchy/work_items_hierarchy_bundle.js
deleted file mode 100644
index 2258c725301..00000000000
--- a/app/assets/javascripts/work_items_hierarchy/work_items_hierarchy_bundle.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import Vue from 'vue';
-import { parseBoolean } from '~/lib/utils/common_utils';
-import App from './components/app.vue';
-import { inferLicensePlan } from './hierarchy_util';
-
-export const initWorkItemsHierarchy = () => {
- const el = document.querySelector('#js-work-items-hierarchy');
-
- const { illustrationPath, hasEpics, hasSubEpics } = el.dataset;
-
- const licensePlan = inferLicensePlan({
- hasEpics: parseBoolean(hasEpics),
- hasSubEpics: parseBoolean(hasSubEpics),
- });
-
- return new Vue({
- el,
- provide: {
- illustrationPath,
- licensePlan,
- },
- render(createElement) {
- return createElement(App);
- },
- });
-};
diff --git a/app/assets/stylesheets/_page_specific_files.scss b/app/assets/stylesheets/_page_specific_files.scss
index 072b78305a9..8f3b5b3b7cc 100644
--- a/app/assets/stylesheets/_page_specific_files.scss
+++ b/app/assets/stylesheets/_page_specific_files.scss
@@ -32,4 +32,3 @@
@import './pages/storage_quota';
@import './pages/tree';
@import './pages/users';
-@import './pages/hierarchy';
diff --git a/app/assets/stylesheets/highlight/white_base.scss b/app/assets/stylesheets/highlight/white_base.scss
index c0f8475323a..80052f4a4d5 100644
--- a/app/assets/stylesheets/highlight/white_base.scss
+++ b/app/assets/stylesheets/highlight/white_base.scss
@@ -255,18 +255,23 @@ span.highlight_word {
.hll { background-color: $white-hll-bg; }
-.c { color: $white-c;
+.c,
+.hljs-comment { color: $white-c;
font-style: italic; }
.err { color: $white-err;
background-color: $white-err-bg; }
-.k { font-weight: $gl-font-weight-bold; }
+
+.k,
+.hljs-variable.language_,
+.hljs-built_in { font-weight: $gl-font-weight-bold; }
.o { font-weight: $gl-font-weight-bold; }
.cm { color: $white-cm;
font-style: italic; }
-.cp { color: $white-cp;
+.cp,
+.hljs-meta { color: $white-cp;
font-weight: $gl-font-weight-bold; }
.c1 { color: $white-c1;
@@ -310,20 +315,34 @@ span.highlight_word {
font-weight: $gl-font-weight-bold; }
.gt { color: $white-gt; }
.kc { font-weight: $gl-font-weight-bold; }
-.kd { font-weight: $gl-font-weight-bold; }
+
+.kd,
+.hljs-keyword { font-weight: $gl-font-weight-bold; }
.kn { font-weight: $gl-font-weight-bold; }
.kp { font-weight: $gl-font-weight-bold; }
.kr { font-weight: $gl-font-weight-bold; }
-.kt { color: $white-kt;
+.kt,
+.hljs-type { color: $white-kt;
font-weight: $gl-font-weight-bold; }
.m { color: $white-m; }
.s { color: $white-s; }
-.n { color: $white-n; }
-.na { color: $white-na; }
-.nb { color: $white-nb; }
-.nc { color: $white-nc;
+.n,
+.hljs-built_in { color: $white-n; }
+
+.na,
+.hljs-attr,
+.hljs-property,
+.hljs-title.function_ { color: $white-na; }
+
+.nb,
+.hljs-title.class_,
+.hljs-literal { color: $white-nb; }
+
+.nc,
+.hljs-title.class_,
+.hljs-built_in { color: $white-nc;
font-weight: $gl-font-weight-bold; }
.no { color: $white-no; }
.ni { color: $white-ni; }
@@ -331,7 +350,9 @@ span.highlight_word {
.ne { color: $white-ne;
font-weight: $gl-font-weight-bold; }
-.nf { color: $white-nf;
+.nf,
+.hljs-title,
+.hljs-title.function_ { color: $white-nf;
font-weight: $gl-font-weight-bold; }
.nn { color: $white-nn; }
.nt { color: $white-nt; }
@@ -340,7 +361,9 @@ span.highlight_word {
.w { color: $white-w; }
.mf { color: $white-mf; }
.mh { color: $white-mh; }
-.mi { color: $white-mi; }
+
+.mi,
+.hljs-number { color: $white-mi; }
.mo { color: $white-mo; }
.sb { color: $white-sb; }
.sc { color: $white-sc; }
@@ -351,7 +374,9 @@ span.highlight_word {
.si { color: $white-si; }
.sx { color: $white-sx; }
.sr { color: $white-sr; }
-.s1 { color: $white-s1; }
+
+.s1,
+.hljs-string { color: $white-s1; }
.ss { color: $white-ss; }
.bp { color: $white-bp; }
.vc { color: $white-vc; }
diff --git a/app/assets/stylesheets/pages/hierarchy.scss b/app/assets/stylesheets/pages/hierarchy.scss
deleted file mode 100644
index 0812e4cc41e..00000000000
--- a/app/assets/stylesheets/pages/hierarchy.scss
+++ /dev/null
@@ -1,15 +0,0 @@
-.hierarchy-rounded-arrow-tail {
- position: absolute;
- top: 4px;
- left: 5px;
- height: calc(100% - 20px);
-}
-
-.hierarchy-icon-wrapper {
- height: $default-icon-size;
- width: $default-icon-size;
-}
-
-.hierarchy-rounded-arrow {
- transform: scale(1, -1) rotate(90deg);
-}
diff --git a/app/controllers/concerns/work_items_hierarchy.rb b/app/controllers/concerns/work_items_hierarchy.rb
deleted file mode 100644
index 6008256408c..00000000000
--- a/app/controllers/concerns/work_items_hierarchy.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: true
-
-module WorkItemsHierarchy
- extend ActiveSupport::Concern
-
- # rubocop:disable Gitlab/ModuleWithInstanceVariables
- def planning_hierarchy
- return render_404 unless Feature.enabled?(:work_items_hierarchy, @project, default_enabled: :yaml)
-
- render 'shared/planning_hierarchy'
- end
- # rubocop:enable Gitlab/ModuleWithInstanceVariables
-end
-
-WorkItemsHierarchy.prepend_mod_with('WorkItemsHierarchy')
diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb
index 482ae2c35f5..c48f3352530 100644
--- a/app/controllers/projects/tree_controller.rb
+++ b/app/controllers/projects/tree_controller.rb
@@ -6,6 +6,7 @@ class Projects::TreeController < Projects::ApplicationController
include CreatesCommit
include ActionView::Helpers::SanitizeHelper
include RedirectsForMissingPathOnTree
+ include SourcegraphDecorator
around_action :allow_gitaly_ref_name_caching, only: [:show]
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 27483d455b1..369fdbee089 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -9,7 +9,7 @@ class ProjectsController < Projects::ApplicationController
include RecordUserLastActivity
include ImportUrlParams
include FiltersEvents
- include WorkItemsHierarchy
+ include SourcegraphDecorator
prepend_before_action(only: [:show]) { authenticate_sessionless_user!(:rss) }
@@ -53,7 +53,6 @@ class ProjectsController < Projects::ApplicationController
feature_category :team_planning, [:preview_markdown, :new_issuable_address]
feature_category :importers, [:export, :remove_export, :generate_new_export, :download_export]
feature_category :code_review, [:unfoldered_environment_names]
- feature_category :portfolio_management, [:planning_hierarchy]
urgency :low, [:refs]
urgency :high, [:unfoldered_environment_names]
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 7ae301cdf73..52a4d2ca736 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -69,6 +69,7 @@ module Ci
has_many :builds, foreign_key: :commit_id, inverse_of: :pipeline
has_many :generic_commit_statuses, foreign_key: :commit_id, inverse_of: :pipeline, class_name: 'GenericCommitStatus'
has_many :job_artifacts, through: :builds
+ has_many :build_trace_chunks, class_name: 'Ci::BuildTraceChunk', through: :builds, source: :trace_chunks
has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id # rubocop:disable Cop/ActiveRecordDependent
has_many :variables, class_name: 'Ci::PipelineVariable'
has_many :deployments, through: :builds
@@ -130,6 +131,7 @@ module Ci
after_create :keep_around_commits, unless: :importing?
use_fast_destroy :job_artifacts
+ use_fast_destroy :build_trace_chunks
# We use `Enums::Ci::Pipeline.sources` here so that EE can more easily extend
# this `Hash` with new values.
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index d89a449bdd5..55f43cd9f7b 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -240,7 +240,6 @@ class ProjectPolicy < BasePolicy
enable :read_wiki
enable :read_issue
enable :read_label
- enable :read_work_items_hierarchy
enable :read_milestone
enable :read_snippet
enable :read_project_member
@@ -573,7 +572,6 @@ class ProjectPolicy < BasePolicy
enable :read_issue_board_list
enable :read_wiki
enable :read_label
- enable :read_work_items_hierarchy
enable :read_milestone
enable :read_snippet
enable :read_project_member
diff --git a/app/services/ci/after_requeue_job_service.rb b/app/services/ci/after_requeue_job_service.rb
index 9101ae91967..ee0ae6651ca 100644
--- a/app/services/ci/after_requeue_job_service.rb
+++ b/app/services/ci/after_requeue_job_service.rb
@@ -3,39 +3,50 @@
module Ci
class AfterRequeueJobService < ::BaseService
def execute(processable)
- process_subsequent_jobs(processable)
- reset_source_bridge(processable)
+ @processable = processable
+
+ process_subsequent_jobs
+ reset_source_bridge
end
private
- def process_subsequent_jobs(processable)
- (stage_dependent_jobs(processable) | needs_dependent_jobs(processable))
- .each do |processable|
- process(processable)
+ def process_subsequent_jobs
+ dependent_jobs.each do |job|
+ process(job)
end
end
- def reset_source_bridge(processable)
- processable.pipeline.reset_source_bridge!(current_user)
+ def reset_source_bridge
+ @processable.pipeline.reset_source_bridge!(current_user)
end
- def process(processable)
- Gitlab::OptimisticLocking.retry_lock(processable, name: 'ci_requeue_job') do |processable|
- processable.process(current_user)
+ def dependent_jobs
+ if ::Feature.enabled?(:ci_order_subsequent_jobs_by_stage, @processable.pipeline.project, default_enabled: :yaml)
+ stage_dependent_jobs
+ .or(needs_dependent_jobs.except(:preload))
+ .ordered_by_stage
+ else
+ stage_dependent_jobs | needs_dependent_jobs
end
end
- def skipped_jobs(processable)
- processable.pipeline.processables.skipped
+ def process(job)
+ Gitlab::OptimisticLocking.retry_lock(job, name: 'ci_requeue_job') do |job|
+ job.process(current_user)
+ end
end
- def stage_dependent_jobs(processable)
- skipped_jobs(processable).after_stage(processable.stage_idx)
+ def stage_dependent_jobs
+ skipped_jobs.after_stage(@processable.stage_idx)
end
- def needs_dependent_jobs(processable)
- skipped_jobs(processable).scheduling_type_dag.with_needs([processable.name])
+ def needs_dependent_jobs
+ skipped_jobs.scheduling_type_dag.with_needs([@processable.name])
+ end
+
+ def skipped_jobs
+ @skipped_jobs ||= @processable.pipeline.processables.skipped
end
end
end
diff --git a/app/services/ci/destroy_pipeline_service.rb b/app/services/ci/destroy_pipeline_service.rb
index 476c7523d60..d85e52e1312 100644
--- a/app/services/ci/destroy_pipeline_service.rb
+++ b/app/services/ci/destroy_pipeline_service.rb
@@ -9,9 +9,11 @@ module Ci
pipeline.cancel_running if pipeline.cancelable?
- # Ci::Pipeline#destroy triggers `use_fast_destroy :job_artifacts` and
- # ci_builds has ON DELETE CASCADE to ci_pipelines. The pipeline, the builds,
- # job and pipeline artifacts all get destroyed here.
+ # The pipeline, the builds, job and pipeline artifacts all get destroyed here.
+ # Ci::Pipeline#destroy triggers fast destroy on job_artifacts and
+ # build_trace_chunks to remove the records and data stored in object storage.
+ # ci_builds records are deleted using ON DELETE CASCADE from ci_pipelines
+ #
pipeline.reset.destroy!
ServiceResponse.success(message: 'Pipeline not found')
diff --git a/app/views/shared/planning_hierarchy.html.haml b/app/views/shared/planning_hierarchy.html.haml
deleted file mode 100644
index d67ecc6ee48..00000000000
--- a/app/views/shared/planning_hierarchy.html.haml
+++ /dev/null
@@ -1,5 +0,0 @@
-- page_title _("Planning hierarchy")
-- has_sub_epics = Gitlab.ee? && @project&.feature_available?(:subepics)
-- has_epics = Gitlab.ee? && @project&.feature_available?(:epics)
-
-#js-work-items-hierarchy{ data: { has_sub_epics: has_sub_epics.to_s, has_epics: has_epics.to_s, illustration_path: image_path('illustrations/rocket-launch-md.svg') } }
diff --git a/config/feature_flags/development/work_items_hierarchy.yml b/config/feature_flags/development/ci_order_subsequent_jobs_by_stage.yml
similarity index 66%
rename from config/feature_flags/development/work_items_hierarchy.yml
rename to config/feature_flags/development/ci_order_subsequent_jobs_by_stage.yml
index 8699511ea63..dfc4ab3bad3 100644
--- a/config/feature_flags/development/work_items_hierarchy.yml
+++ b/config/feature_flags/development/ci_order_subsequent_jobs_by_stage.yml
@@ -1,8 +1,8 @@
---
-name: work_items_hierarchy
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/76720
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/350451
+name: ci_order_subsequent_jobs_by_stage
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/77528
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/349977
milestone: '14.7'
type: development
-group: group::product planning
+group: group::pipeline authoring
default_enabled: false
diff --git a/config/routes/project.rb b/config/routes/project.rb
index 004206e0ae2..702ef64a2ca 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -641,7 +641,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
post :generate_new_export
get :download_export
get :activity
- get :planning_hierarchy
get :refs
put :new_issuable_address
get :unfoldered_environment_names
diff --git a/doc/development/snowplow/schemas.md b/doc/development/snowplow/schemas.md
index 6bda026b9a7..eb57e7d98a5 100644
--- a/doc/development/snowplow/schemas.md
+++ b/doc/development/snowplow/schemas.md
@@ -19,6 +19,7 @@ The [`StandardContext`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/g
| `project_id` | **{dotted-circle}** | integer | |
| `namespace_id` | **{dotted-circle}** | integer | |
| `user_id` | **{dotted-circle}** | integer | User database record ID attribute. This file undergoes a pseudonymization process at the collector level. |
+| `context_generated_at` | **{dotted-circle}** | string (date time format) | Timestamp indicating when context was generated. |
| `environment` | **{check-circle}** | string (max 32 chars) | Name of the source environment, such as `production` or `staging` |
| `source` | **{check-circle}** | string (max 32 chars) | Name of the source application, such as `gitlab-rails` or `gitlab-javascript` |
| `plan` | **{dotted-circle}** | string (max 32 chars) | Name of the plan for the namespace, such as `free`, `premium`, or `ultimate`. Automatically picked from the `namespace`. |
diff --git a/doc/user/admin_area/geo_nodes.md b/doc/user/admin_area/geo_nodes.md
index 87ac75bdd7e..b3b2c14adbd 100644
--- a/doc/user/admin_area/geo_nodes.md
+++ b/doc/user/admin_area/geo_nodes.md
@@ -55,14 +55,21 @@ you can increase the values to complete backfill in a shorter time. If it's
under heavy load and backfill reduces its availability for normal requests,
you can decrease them.
-## Using a different URL for synchronization
+## Set up the internal URLs
+
+> Setting up internal URLs in secondary sites was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/77179) in GitLab 14.7.
+
+You can set up a different URL for synchronization between the primary and secondary site.
The **primary** site's Internal URL is used by **secondary** sites to contact it
(to sync repositories, for example). The name Internal URL distinguishes it from
[External URL](https://docs.gitlab.com/omnibus/settings/configuration.html#configuring-the-external-url-for-gitlab),
which is used by users. Internal URL does not need to be a private address.
-Internal URL defaults to external URL, but you can also customize it:
+When [Geo secondary proxying](../../administration/geo/secondary_proxy/index.md) is enabled,
+the primary uses the secondary's internal URL to contact it directly.
+
+The internal URL defaults to external URL. To change it:
1. On the top bar, select **Menu > Admin**.
1. On the left sidebar, select **Geo > Nodes**.
@@ -70,6 +77,9 @@ Internal URL defaults to external URL, but you can also customize it:
1. Edit the internal URL.
1. Select **Save changes**.
+When enabled, the Admin Area for Geo shows replication details for each site directly
+from the primary site's UI, and through the Geo secondary proxy, if enabled.
+
WARNING:
We recommend using an HTTPS connection while configuring the Geo sites. To avoid
breaking communication between **primary** and **secondary** sites when using
diff --git a/doc/user/application_security/policies/index.md b/doc/user/application_security/policies/index.md
index c433ad67c6f..11f2b91177a 100644
--- a/doc/user/application_security/policies/index.md
+++ b/doc/user/application_security/policies/index.md
@@ -313,10 +313,10 @@ Use this schema to define `clusters` objects in the [`schedule` rule type](#sche
| Field | Type | Possible values | Description |
|--------------|---------------------|--------------------------|-------------|
-| `containers` | `array` of `string` | | The container name to be scanned (only the first value is currently supported). |
-| `resources` | `array` of `string` | | The resource name to be scanned (only the first value is currently supported). |
-| `namespaces` | `array` of `string` | | The namespace to be scanned (only the first value is currently supported). |
-| `kinds` | `array` of `string` | `deployment`/`daemonset` | The resource kind to be scanned (only the first value is currently supported). |
+| `containers` | `array` of `string` | | The container name that is scanned (only the first value is currently supported). |
+| `resources` | `array` of `string` | | The resource name that is scanned (only the first value is currently supported). |
+| `namespaces` | `array` of `string` | | The namespace that is scanned (only the first value is currently supported). |
+| `kinds` | `array` of `string` | `deployment`/`daemonset` | The resource kind that should be scanned (only the first value is currently supported). |
### `scan` action type
diff --git a/doc/user/group/index.md b/doc/user/group/index.md
index 4102b4ffbf1..56c4c82494c 100644
--- a/doc/user/group/index.md
+++ b/doc/user/group/index.md
@@ -100,6 +100,14 @@ You can give a user access to all projects in a group.
1. Fill in the fields.
- The role applies to all projects in the group. [Learn more about permissions](../permissions.md).
- On the **Access expiration date**, the user can no longer access projects in the group.
+1. Select **Invite**.
+
+Members that are not automatically added are displayed on the **Invited** tab.
+Users can be on this tab because they:
+
+- Have not yet accepted the invitation.
+- Are waiting for [approval from an administrator](../admin_area/moderate_users.md).
+- [Exceed the group user cap](#user-cap-for-groups).
## Request access to a group
@@ -508,6 +516,74 @@ To prevent a project from being shared with other groups:
This setting applies to all subgroups unless overridden by a group owner. Groups already
added to a project lose access when the setting is enabled.
+## User cap for groups
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/330027) in GitLab 14.7.
+
+FLAG:
+On self-managed GitLab, this feature is not available. On GitLab.com, this feature is available for some groups.
+This feature is not ready for production use.
+
+When the number of billable members reaches the user cap, new users can't be added to the group
+without being approved by the group owner.
+
+Groups with the user cap feature enabled have [group sharing](#share-a-group-with-another-group)
+disabled for the group and its subgroups.
+
+### Specify a user cap for a group
+
+Prerequisite:
+
+- You must be assigned the [Owner role](../permissions.md#group-members-permissions) for the group.
+
+To specify a user cap:
+
+1. On the top bar, select **Menu > Groups** and find your group.
+ You can set a cap on the top-level group only.
+1. On the left sidebar, select **Settings > General**.
+1. Expand **Permissions and group features**.
+1. In the **User cap** box, enter the desired number of users.
+1. Select **Save changes**.
+
+If you already have more users in the group than the user cap value, users
+are not removed. However, you can't add more without approval.
+
+Increasing the user cap does not approve pending members.
+
+### Remove the user cap for a group
+
+You can remove the user cap, so there is no limit on the number of members you can add to a group.
+
+Prerequisite:
+
+- You must be assigned the [Owner role](../permissions.md#group-members-permissions) for the group.
+
+To remove the user cap:
+
+1. On the top bar, select **Menu > Groups** and find your group.
+1. On the left sidebar, select **Settings > General**.
+1. Expand **Permissions and group features**.
+1. In the **User cap** box, delete the value.
+1. Select **Save changes**.
+
+Decreasing the user cap does not approve pending members.
+
+### Approve pending members for a group
+
+When the number of billable users reaches the user cap, any new member is put in a pending state
+and must be approved.
+
+Prerequisite:
+
+- You must be assigned the [Owner role](../permissions.md#group-members-permissions) for the group.
+
+To approve members that are pending because they've exceeded the user cap:
+
+1. On the top bar, select **Menu > Groups** and find your group.
+1. On the left sidebar, select **Settings > Usage Quotas**.
+1. On the **Seats** tab, under the alert, select **View pending approvals**.
+1. For each member you want to approve, select **Approve**.
+
## Prevent members from being added to projects in a group **(PREMIUM)**
As a group owner, you can prevent any new project membership for all
diff --git a/doc/user/group/planning_hierarchy/img/view-project-work-item-hierarchy_v14_7.png b/doc/user/group/planning_hierarchy/img/view-project-work-item-hierarchy_v14_7.png
deleted file mode 100644
index 2ddd551ee46..00000000000
Binary files a/doc/user/group/planning_hierarchy/img/view-project-work-item-hierarchy_v14_7.png and /dev/null differ
diff --git a/doc/user/group/planning_hierarchy/index.md b/doc/user/group/planning_hierarchy/index.md
index 0525246b812..5887328abe4 100644
--- a/doc/user/group/planning_hierarchy/index.md
+++ b/doc/user/group/planning_hierarchy/index.md
@@ -20,20 +20,6 @@ To learn about hierarchies in general, common frameworks, and using GitLab for
portfolio management, see
[How to use GitLab for Agile portfolio planning and project management](https://about.gitlab.com/blog/2020/11/11/gitlab-for-agile-portfolio-planning-project-management/).
-## View planning hierarchies
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/340844/) in GitLab 14.7 and is behind the feature flag `work_items_hierarchy`.
-
-To view the planning hierarchy in a project:
-
-1. On the top bar, select **Menu > Projects** and find your project.
-1. On the left sidebar, select **Project information > Planning hierarchy**.
-
-Under **Current structure**, you can see a hierarchy diagram that matches your current planning hierarchy.
-The work items outside your subscription plan show up below **Unavailable structure**.
-
-![Screenshot showing hierarchy page](img/view-project-work-item-hierarchy_v14_7.png)
-
## Hierarchies with epics
With epics, you can achieve the following hierarchy:
diff --git a/lib/gitlab/ci/trace/stream.rb b/lib/gitlab/ci/trace/stream.rb
index 2d31049a0c9..dd435ba05b7 100644
--- a/lib/gitlab/ci/trace/stream.rb
+++ b/lib/gitlab/ci/trace/stream.rb
@@ -11,10 +11,6 @@ module Gitlab
delegate :close, :tell, :seek, :size, :url, :truncate, to: :stream, allow_nil: true
- delegate :valid?, to: :stream, allow_nil: true
-
- alias_method :present?, :valid?
-
def initialize(metrics = Trace::Metrics.new)
@stream = yield
@stream&.binmode
@@ -24,6 +20,7 @@ module Gitlab
def valid?
self.stream.present?
end
+ alias_method :present?, :valid?
def file?
self.path.present?
diff --git a/lib/gitlab/tracking/standard_context.rb b/lib/gitlab/tracking/standard_context.rb
index 837390b91fb..542dc476526 100644
--- a/lib/gitlab/tracking/standard_context.rb
+++ b/lib/gitlab/tracking/standard_context.rb
@@ -3,7 +3,7 @@
module Gitlab
module Tracking
class StandardContext
- GITLAB_STANDARD_SCHEMA_URL = 'iglu:com.gitlab/gitlab_standard/jsonschema/1-0-7'
+ GITLAB_STANDARD_SCHEMA_URL = 'iglu:com.gitlab/gitlab_standard/jsonschema/1-0-8'
GITLAB_RAILS_SOURCE = 'gitlab-rails'
def initialize(namespace: nil, project: nil, user: nil, **extra)
@@ -46,7 +46,8 @@ module Gitlab
extra: extra,
user_id: user&.id,
namespace_id: namespace&.id,
- project_id: project_id
+ project_id: project_id,
+ context_generated_at: Time.current
}
end
diff --git a/lib/sidebars/concerns/work_item_hierarchy.rb b/lib/sidebars/concerns/work_item_hierarchy.rb
deleted file mode 100644
index f88e24b205d..00000000000
--- a/lib/sidebars/concerns/work_item_hierarchy.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-# frozen_string_literal: true
-
-# This module has the necessary methods to render
-# work items hierarchy menu
-module Sidebars
- module Concerns
- module WorkItemHierarchy
- def hierarchy_menu_item(container, url, path)
- unless show_hierarachy_menu_item?(container)
- return ::Sidebars::NilMenuItem.new(item_id: :hierarchy)
- end
-
- ::Sidebars::MenuItem.new(
- title: _('Planning hierarchy'),
- link: url,
- active_routes: { path: path },
- item_id: :hierarchy
- )
- end
-
- def show_hierarachy_menu_item?(container)
- Feature.enabled?(:work_items_hierarchy, container, default_enabled: :yaml) &&
- can?(context.current_user, :read_work_items_hierarchy, container)
- end
- end
- end
-end
diff --git a/lib/sidebars/projects/menus/project_information_menu.rb b/lib/sidebars/projects/menus/project_information_menu.rb
index ad11a383757..44b94ee3522 100644
--- a/lib/sidebars/projects/menus/project_information_menu.rb
+++ b/lib/sidebars/projects/menus/project_information_menu.rb
@@ -4,13 +4,10 @@ module Sidebars
module Projects
module Menus
class ProjectInformationMenu < ::Sidebars::Menu
- include ::Sidebars::Concerns::WorkItemHierarchy
-
override :configure_menu_items
def configure_menu_items
add_item(activity_menu_item)
add_item(labels_menu_item)
- add_item(hierarchy_menu_item(context.project, planning_hierarchy_project_path(context.project), 'projects#planning_hierarchy'))
add_item(members_menu_item)
true
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index d2fcecb165b..b02a7f1f805 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -722,6 +722,9 @@ msgstr ""
msgid "%{level_name} is not allowed since the fork source project has lower visibility."
msgstr ""
+msgid "%{linkStart}Learn more.%{linkEnd}"
+msgstr ""
+
msgid "%{link_start}Learn more%{link_end} about roles."
msgstr ""
@@ -2480,6 +2483,12 @@ msgstr ""
msgid "AdminDashboard|Error loading the statistics. Please try again"
msgstr ""
+msgid "AdminGeo|The URL of the primary site that is used internally by the secondary sites."
+msgstr ""
+
+msgid "AdminGeo|The URL of the secondary site that is used internally by the primary site."
+msgstr ""
+
msgid "AdminLabels|Define your default set of project labels"
msgstr ""
@@ -7071,9 +7080,6 @@ msgstr ""
msgid "Child"
msgstr ""
-msgid "Child epic"
-msgstr ""
-
msgid "Child epic does not exist."
msgstr ""
@@ -17619,33 +17625,6 @@ msgstr[1] ""
msgid "Hide values"
msgstr ""
-msgid "Hierarchy|Current structure"
-msgstr ""
-
-msgid "Hierarchy|Deliver value more efficiently by breaking down necessary work into a hierarchical structure. This structure helps teams understand scope, priorities, and how work cascades up toward larger goals."
-msgstr ""
-
-msgid "Hierarchy|Help us improve work items in GitLab!"
-msgstr ""
-
-msgid "Hierarchy|Is there a framework or type of work item you wish you had access to in GitLab? Give us your feedback and help us build the experiences valuable to you."
-msgstr ""
-
-msgid "Hierarchy|Planning hierarchy"
-msgstr ""
-
-msgid "Hierarchy|Take the work items survey"
-msgstr ""
-
-msgid "Hierarchy|These items are unavailable in the current structure."
-msgstr ""
-
-msgid "Hierarchy|Unavailable structure"
-msgstr ""
-
-msgid "Hierarchy|You can start using these items now."
-msgstr ""
-
msgid "High or unknown vulnerabilities present"
msgstr ""
@@ -26530,9 +26509,6 @@ msgstr ""
msgid "Plan:"
msgstr ""
-msgid "Planning hierarchy"
-msgstr ""
-
msgid "PlantUML"
msgstr ""
@@ -30256,9 +30232,6 @@ msgstr ""
msgid "Required only if you are not using role instance credentials."
msgstr ""
-msgid "Requirement"
-msgstr ""
-
msgid "Requirement %{reference} has been added"
msgstr ""
@@ -34942,9 +34915,6 @@ msgstr ""
msgid "Target-Branch"
msgstr ""
-msgid "Task"
-msgstr ""
-
msgid "Task ID: %{elastic_task}"
msgstr ""
@@ -35190,9 +35160,6 @@ msgstr ""
msgid "Test Cases"
msgstr ""
-msgid "Test case"
-msgstr ""
-
msgid "Test coverage parsing"
msgstr ""
@@ -35406,12 +35373,6 @@ msgstr ""
msgid "The Snowplow cookie domain."
msgstr ""
-msgid "The URL defined on the primary node that secondary nodes should use to contact it."
-msgstr ""
-
-msgid "The URL defined on the primary node that secondary nodes should use to contact it. %{linkStart}Learn more%{linkEnd}"
-msgstr ""
-
msgid "The URL of the Jenkins server."
msgstr ""
diff --git a/qa/Dockerfile b/qa/Dockerfile
index 54de6509518..7e199093c92 100644
--- a/qa/Dockerfile
+++ b/qa/Dockerfile
@@ -63,6 +63,7 @@ WORKDIR /home/gitlab/qa
# Install qa dependencies or fetch from cache if unchanged
COPY ./qa/Gemfile* /home/gitlab/qa/
+RUN gem install bundler --no-document --conservative --version 2.3.5
RUN bundle install --jobs=$(nproc) --retry=3 --without=development --quiet
##
diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock
index 3e85a33f2a2..1e0415f1ee4 100644
--- a/qa/Gemfile.lock
+++ b/qa/Gemfile.lock
@@ -355,4 +355,4 @@ DEPENDENCIES
zeitwerk (~> 2.4)
BUNDLED WITH
- 2.2.33
+ 2.3.5
diff --git a/scripts/utils.sh b/scripts/utils.sh
index 15047d35fc3..849e8f77fd5 100644
--- a/scripts/utils.sh
+++ b/scripts/utils.sh
@@ -36,6 +36,7 @@ function bundle_install_script() {
exit 1;
fi;
+ gem install bundler --no-document --conservative --version 2.3.5
bundle --version
bundle config set path "$(pwd)/vendor"
bundle config set clean 'true'
diff --git a/spec/commands/metrics_server/metrics_server_spec.rb b/spec/commands/metrics_server/metrics_server_spec.rb
index b755801bb65..0178017ee37 100644
--- a/spec/commands/metrics_server/metrics_server_spec.rb
+++ b/spec/commands/metrics_server/metrics_server_spec.rb
@@ -47,7 +47,7 @@ RSpec.describe 'bin/metrics-server', :aggregate_failures do
if @pid
pgrp = Process.getpgid(@pid)
- Timeout.timeout(5) do
+ Timeout.timeout(10) do
Process.kill('TERM', -pgrp)
Process.waitpid(@pid)
end
@@ -63,7 +63,7 @@ RSpec.describe 'bin/metrics-server', :aggregate_failures do
it 'serves /metrics endpoint' do
expect do
- Timeout.timeout(5) do
+ Timeout.timeout(10) do
http_ok = false
until http_ok
sleep 1
diff --git a/spec/controllers/concerns/work_items_hierarchy_spec.rb b/spec/controllers/concerns/work_items_hierarchy_spec.rb
deleted file mode 100644
index 270e30cb5f9..00000000000
--- a/spec/controllers/concerns/work_items_hierarchy_spec.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe WorkItemsHierarchy do
- controller(ApplicationController) do
- include WorkItemsHierarchy
- end
-
- let_it_be(:user) { create(:user) }
- let_it_be(:group) { create(:group) }
- let_it_be(:project) { create(:project, group: group) }
-
- render_views
-
- before do
- sign_in user
- routes.draw { get :planning_hierarchy, to: "anonymous#planning_hierarchy" }
- controller.instance_variable_set(:@project, project)
- end
-
- it 'renders hierarchy' do
- stub_feature_flags(work_items_hierarchy: true)
-
- get :planning_hierarchy
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.body).to match(/id="js-work-items-hierarchy"/)
- end
-
- it 'renders 404' do
- stub_feature_flags(work_items_hierarchy: false)
-
- get :planning_hierarchy
-
- expect(response).to have_gitlab_http_status(:not_found)
- expect(response.body).not_to match(/id="js-work-items-hierarchy"/)
- end
-end
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index 1d5df505743..011021f6320 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -5,6 +5,7 @@ FactoryBot.define do
name { 'test' }
add_attribute(:protected) { false }
created_at { 'Di 29. Okt 09:50:00 CET 2013' }
+ scheduling_type { 'stage' }
pending
options do
@@ -33,6 +34,8 @@ FactoryBot.define do
end
trait :dependent do
+ scheduling_type { 'dag' }
+
transient do
sequence(:needed_name) { |n| "dependency #{n}" }
needed { association(:ci_build, name: needed_name, pipeline: pipeline) }
diff --git a/spec/frontend/__helpers__/wait_using_real_timer.js b/spec/frontend/__helpers__/wait_using_real_timer.js
deleted file mode 100644
index 110d5f46c08..00000000000
--- a/spec/frontend/__helpers__/wait_using_real_timer.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/* useful for timing promises when jest fakeTimers are not reliable enough */
-export default (timeout) =>
- new Promise((resolve) => {
- jest.useRealTimers();
- setTimeout(resolve, timeout);
- jest.useFakeTimers();
- });
diff --git a/spec/frontend/blob/components/__snapshots__/blob_header_spec.js.snap b/spec/frontend/blob/components/__snapshots__/blob_header_spec.js.snap
index db9684239a1..ad2f8aca55c 100644
--- a/spec/frontend/blob/components/__snapshots__/blob_header_spec.js.snap
+++ b/spec/frontend/blob/components/__snapshots__/blob_header_spec.js.snap
@@ -17,7 +17,7 @@ exports[`Blob Header Default Actions rendering matches the snapshot 1`] = `
{
},
});
await vm.$nextTick();
- await vm.$nextTick();
expect(vm.initEditor).toHaveBeenCalled();
});
@@ -567,8 +565,8 @@ describe('RepoEditor', () => {
// switching from edit to diff mode usually triggers editor initialization
vm.$store.state.viewer = viewerTypes.diff;
- // we delay returning the file to make sure editor doesn't initialize before we fetch file content
- await waitUsingRealTimer(30);
+ jest.runOnlyPendingTimers();
+
return 'rawFileData123\n';
});
@@ -598,8 +596,9 @@ describe('RepoEditor', () => {
return aContent;
})
.mockImplementationOnce(async () => {
- // we delay returning fileB content to make sure the editor doesn't initialize prematurely
- await waitUsingRealTimer(30);
+ // we delay returning fileB content
+ // to make sure the editor doesn't initialize prematurely
+ jest.advanceTimersByTime(30);
return bContent;
});
diff --git a/spec/frontend/work_items_hierarchy/components/__snapshots__/app_spec.js.snap b/spec/frontend/work_items_hierarchy/components/__snapshots__/app_spec.js.snap
deleted file mode 100644
index f4979e4e707..00000000000
--- a/spec/frontend/work_items_hierarchy/components/__snapshots__/app_spec.js.snap
+++ /dev/null
@@ -1,1197 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`WorkItemsHierarchy App when licensePlan is free matches the snapshot 1`] = `
-
-
-
-
-
-
-
-
-
-
-
- Help us improve work items in GitLab!
-
-
-
-
- Is there a framework or type of work item you wish you had access to in GitLab? Give us your feedback and help us build the experiences valuable to you.
-
-
-
- Deliver value more efficiently by breaking down necessary work into a hierarchical structure. This structure helps teams understand scope, priorities, and how work cascades up toward larger goals.
-
-
-
-
- Current structure
-
-
-
- You can start using these items now.
-
-
-
-
-
-
-
-
-
-
- Issue
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Task
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Incident
-
-
-
-
-
-
-
-
-
-
-
- Unavailable structure
-
-
-
-
-
- These items are unavailable in the current structure.
-
-
-
-
-
-
-
-
- Test case
-
-
-
-
-
- Ultimate
-
-
-
-
-
-
-`;
-
-exports[`WorkItemsHierarchy App when licensePlan is premium matches the snapshot 1`] = `
-
-
-
-
-
-
-
-
-
-
-
- Help us improve work items in GitLab!
-
-
-
-
- Is there a framework or type of work item you wish you had access to in GitLab? Give us your feedback and help us build the experiences valuable to you.
-
-
-
- Deliver value more efficiently by breaking down necessary work into a hierarchical structure. This structure helps teams understand scope, priorities, and how work cascades up toward larger goals.
-
-
-
-
- Current structure
-
-
-
- You can start using these items now.
-
-
-
-
-
-
-
-
-
-
- Epic
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Issue
-
-
-
-
-
-
-
-
-
-
-
- Task
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Incident
-
-
-
-
-
-
-
-
-
-
-
- Unavailable structure
-
-
-
-
-
- These items are unavailable in the current structure.
-
-
-
-
-
-
-
-
- Test case
-
-
-
-
-
- Ultimate
-
-
-
-
-
-
-`;
-
-exports[`WorkItemsHierarchy App when licensePlan is ultimate matches the snapshot 1`] = `
-
-
-
-
-
-
-
-
-
-
-
- Help us improve work items in GitLab!
-
-
-
-
- Is there a framework or type of work item you wish you had access to in GitLab? Give us your feedback and help us build the experiences valuable to you.
-
-
-
- Deliver value more efficiently by breaking down necessary work into a hierarchical structure. This structure helps teams understand scope, priorities, and how work cascades up toward larger goals.
-
-
-
-
- Current structure
-
-
-
- You can start using these items now.
-
-
-
-
-
-
-
-
-
-
- Epic
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Child epic
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Issue
-
-
-
-
-
-
-
-
-
-
-
- Task
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Incident
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Requirement
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Test case
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-`;
diff --git a/spec/frontend/work_items_hierarchy/components/app_spec.js b/spec/frontend/work_items_hierarchy/components/app_spec.js
deleted file mode 100644
index c22c0fcb21c..00000000000
--- a/spec/frontend/work_items_hierarchy/components/app_spec.js
+++ /dev/null
@@ -1,78 +0,0 @@
-import { nextTick } from 'vue';
-import { createLocalVue, mount } from '@vue/test-utils';
-import VueApollo from 'vue-apollo';
-import { GlBanner } from '@gitlab/ui';
-import App from '~/work_items_hierarchy/components/app.vue';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-
-const localVue = createLocalVue();
-localVue.use(VueApollo);
-
-describe('WorkItemsHierarchy App', () => {
- let wrapper;
- const createComponent = (props = {}, data = {}) => {
- wrapper = extendedWrapper(
- mount(App, {
- localVue,
- provide: {
- illustrationPath: '/foo.svg',
- licensePlan: 'free',
- ...props,
- },
- data() {
- return data;
- },
- }),
- );
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe.each`
- licensePlan
- ${'free'}
- ${'premium'}
- ${'ultimate'}
- `('when licensePlan is $licensePlan', ({ licensePlan }) => {
- beforeEach(() => {
- createComponent({ licensePlan });
- });
-
- it('matches the snapshot', () => {
- expect(wrapper.element).toMatchSnapshot();
- });
- });
-
- describe('survey banner', () => {
- it('shows when the banner is visible', () => {
- createComponent({}, { bannerVisible: true });
-
- expect(wrapper.find(GlBanner).exists()).toBe(true);
- });
-
- it('hide when close is called', async () => {
- createComponent({}, { bannerVisible: true });
-
- wrapper.findByTestId('close-icon').trigger('click');
-
- await nextTick();
-
- expect(wrapper.find(GlBanner).exists()).toBe(false);
- });
- });
-
- describe('Unavailable structure', () => {
- it.each`
- licensePlan | visible
- ${'free'} | ${true}
- ${'premium'} | ${true}
- ${'ultimate'} | ${false}
- `('visibility is $visible when plan is $licensePlan', ({ licensePlan, visible }) => {
- createComponent({ licensePlan });
-
- expect(wrapper.findByTestId('unavailable-structure').exists()).toBe(visible);
- });
- });
-});
diff --git a/spec/frontend/work_items_hierarchy/components/hierarchy_spec.js b/spec/frontend/work_items_hierarchy/components/hierarchy_spec.js
deleted file mode 100644
index 14c15fb5cbe..00000000000
--- a/spec/frontend/work_items_hierarchy/components/hierarchy_spec.js
+++ /dev/null
@@ -1,118 +0,0 @@
-import { createLocalVue, mount } from '@vue/test-utils';
-import VueApollo from 'vue-apollo';
-import { GlBadge } from '@gitlab/ui';
-import Hierarchy from '~/work_items_hierarchy/components/hierarchy.vue';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import RESPONSE from '~/work_items_hierarchy/static_response';
-import { workItemTypes } from '~/work_items/constants';
-
-const localVue = createLocalVue();
-localVue.use(VueApollo);
-
-describe('WorkItemsHierarchy Hierarchy', () => {
- let wrapper;
-
- const workItemsFromResponse = (response) => {
- return response.reduce(
- (itemTypes, item) => {
- const key = item.available ? 'available' : 'unavailable';
- itemTypes[key].push({
- ...item,
- ...workItemTypes[item.type],
- nestedTypes: item.nestedTypes
- ? item.nestedTypes.map((type) => workItemTypes[type])
- : null,
- });
- return itemTypes;
- },
- { available: [], unavailable: [] },
- );
- };
-
- const createComponent = (props = {}) => {
- wrapper = extendedWrapper(
- mount(Hierarchy, {
- localVue,
- propsData: {
- workItemTypes: props.workItemTypes,
- ...props,
- },
- }),
- );
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('available structure', () => {
- let items = [];
-
- beforeEach(() => {
- items = workItemsFromResponse(RESPONSE.ultimate).available;
- createComponent({ workItemTypes: items });
- });
-
- it('renders all work items', () => {
- expect(wrapper.findAllByTestId('work-item-wrapper')).toHaveLength(items.length);
- });
-
- it('does not render badges', () => {
- expect(wrapper.find(GlBadge).exists()).toBe(false);
- });
- });
-
- describe('unavailable structure', () => {
- let items = [];
-
- beforeEach(() => {
- items = workItemsFromResponse(RESPONSE.premium).unavailable;
- createComponent({ workItemTypes: items });
- });
-
- it('renders all work items', () => {
- expect(wrapper.findAllByTestId('work-item-wrapper')).toHaveLength(items.length);
- });
-
- it('renders license badges for all work items', () => {
- expect(wrapper.findAll(GlBadge)).toHaveLength(items.length);
- });
-
- it('does not render svg icon for linking', () => {
- expect(wrapper.findByTestId('hierarchy-rounded-arrow-tail').exists()).toBe(false);
- expect(wrapper.findByTestId('level-up-icon').exists()).toBe(false);
- });
- });
-
- describe('nested work items', () => {
- describe.each`
- licensePlan | arrowTailVisible | levelUpIconVisible | arrowDownIconVisible
- ${'ultimate'} | ${true} | ${true} | ${true}
- ${'premium'} | ${false} | ${false} | ${true}
- ${'free'} | ${false} | ${false} | ${false}
- `(
- 'when $licensePlan license',
- ({ licensePlan, arrowTailVisible, levelUpIconVisible, arrowDownIconVisible }) => {
- let items = [];
- beforeEach(() => {
- items = workItemsFromResponse(RESPONSE[licensePlan]).available;
- createComponent({ workItemTypes: items });
- });
-
- it(`${arrowTailVisible ? 'render' : 'does not render'} arrow tail svg`, () => {
- expect(wrapper.findByTestId('hierarchy-rounded-arrow-tail').exists()).toBe(
- arrowTailVisible,
- );
- });
-
- it(`${levelUpIconVisible ? 'render' : 'does not render'} arrow tail svg`, () => {
- expect(wrapper.findByTestId('level-up-icon').exists()).toBe(levelUpIconVisible);
- });
-
- it(`${arrowDownIconVisible ? 'render' : 'does not render'} arrow tail svg`, () => {
- expect(wrapper.findByTestId('arrow-down-icon').exists()).toBe(arrowDownIconVisible);
- });
- },
- );
- });
-});
diff --git a/spec/frontend/work_items_hierarchy/hierarchy_util_spec.js b/spec/frontend/work_items_hierarchy/hierarchy_util_spec.js
deleted file mode 100644
index 9042fa27d16..00000000000
--- a/spec/frontend/work_items_hierarchy/hierarchy_util_spec.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { inferLicensePlan } from '~/work_items_hierarchy/hierarchy_util';
-import { LICENSE_PLAN } from '~/work_items_hierarchy/constants';
-
-describe('inferLicensePlan', () => {
- it.each`
- epics | subEpics | licensePlan
- ${true} | ${true} | ${LICENSE_PLAN.ULTIMATE}
- ${true} | ${false} | ${LICENSE_PLAN.PREMIUM}
- ${false} | ${false} | ${LICENSE_PLAN.FREE}
- `(
- 'returns $licensePlan when epic is $epics and sub-epic is $subEpics',
- ({ epics, subEpics, licensePlan }) => {
- expect(inferLicensePlan({ hasEpics: epics, hasSubEpics: subEpics })).toBe(licensePlan);
- },
- );
-});
diff --git a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
index f4875aa0ebc..7d4a3655be6 100644
--- a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
+++ b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
@@ -92,7 +92,7 @@ RSpec.describe Gitlab::Gfm::ReferenceRewriter do
let!(:group_label) { create(:group_label, id: 321, name: 'group label', group: old_group) }
before do
- old_project.update(namespace: old_group)
+ old_project.update!(namespace: old_group)
end
context 'label referenced by id' do
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index bf2e3c7f5f8..4bf7994f4dd 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -96,7 +96,7 @@ RSpec.describe Gitlab::GitAccess do
context 'when the DeployKey has access to the project' do
before do
- deploy_key.deploy_keys_projects.create(project: project, can_push: true)
+ deploy_key.deploy_keys_projects.create!(project: project, can_push: true)
end
it 'allows push and pull access' do
@@ -820,7 +820,7 @@ RSpec.describe Gitlab::GitAccess do
project.add_role(user, role)
end
- protected_branch.save
+ protected_branch.save!
aggregate_failures do
matrix.each do |action, allowed|
@@ -1090,7 +1090,7 @@ RSpec.describe Gitlab::GitAccess do
context 'when deploy_key can push' do
context 'when project is authorized' do
before do
- key.deploy_keys_projects.create(project: project, can_push: true)
+ key.deploy_keys_projects.create!(project: project, can_push: true)
end
it { expect { push_access_check }.not_to raise_error }
@@ -1120,7 +1120,7 @@ RSpec.describe Gitlab::GitAccess do
context 'when deploy_key cannot push' do
context 'when project is authorized' do
before do
- key.deploy_keys_projects.create(project: project, can_push: false)
+ key.deploy_keys_projects.create!(project: project, can_push: false)
end
it { expect { push_access_check }.to raise_forbidden(described_class::ERROR_MESSAGES[:deploy_key_upload]) }
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 625e94e3d77..e00af614cff 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -281,6 +281,7 @@ ci_pipelines:
- dast_site_profiles_pipeline
- package_build_infos
- package_file_build_infos
+- build_trace_chunks
ci_refs:
- project
- ci_pipelines
diff --git a/spec/lib/gitlab/import_export/avatar_saver_spec.rb b/spec/lib/gitlab/import_export/avatar_saver_spec.rb
index 334d930c47c..d897ce76da0 100644
--- a/spec/lib/gitlab/import_export/avatar_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/avatar_saver_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe Gitlab::ImportExport::AvatarSaver do
end
it 'saves a project avatar' do
- described_class.new(project: project_with_avatar, shared: shared).save
+ described_class.new(project: project_with_avatar, shared: shared).save # rubocop:disable Rails/SaveBang
expect(File).to exist(Dir["#{shared.export_path}/avatar/**/dk.png"].first)
end
diff --git a/spec/lib/gitlab/import_export/base/relation_factory_spec.rb b/spec/lib/gitlab/import_export/base/relation_factory_spec.rb
index bd8873fe20e..b8999f608b1 100644
--- a/spec/lib/gitlab/import_export/base/relation_factory_spec.rb
+++ b/spec/lib/gitlab/import_export/base/relation_factory_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe Gitlab::ImportExport::Base::RelationFactory do
let(:excluded_keys) { [] }
subject do
- described_class.create(relation_sym: relation_sym,
+ described_class.create(relation_sym: relation_sym, # rubocop:disable Rails/SaveBang
relation_hash: relation_hash,
relation_index: 1,
object_builder: Gitlab::ImportExport::Project::ObjectBuilder,
diff --git a/spec/lib/gitlab/import_export/design_repo_restorer_spec.rb b/spec/lib/gitlab/import_export/design_repo_restorer_spec.rb
index 6680f4e7a03..346f653acd4 100644
--- a/spec/lib/gitlab/import_export/design_repo_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/design_repo_restorer_spec.rb
@@ -24,7 +24,7 @@ RSpec.describe Gitlab::ImportExport::DesignRepoRestorer do
allow(instance).to receive(:storage_path).and_return(export_path)
end
- bundler.save
+ bundler.save # rubocop:disable Rails/SaveBang
end
after do
diff --git a/spec/lib/gitlab/tracking/standard_context_spec.rb b/spec/lib/gitlab/tracking/standard_context_spec.rb
index 7d678db5ec8..c88b0af30f6 100644
--- a/spec/lib/gitlab/tracking/standard_context_spec.rb
+++ b/spec/lib/gitlab/tracking/standard_context_spec.rb
@@ -58,6 +58,10 @@ RSpec.describe Gitlab::Tracking::StandardContext do
expect(snowplow_context.to_json.dig(:data, :source)).to eq(described_class::GITLAB_RAILS_SOURCE)
end
+ it 'contains context_generated_at timestamp', :freeze_time do
+ expect(snowplow_context.to_json.dig(:data, :context_generated_at)).to eq(Time.current)
+ end
+
context 'plan' do
context 'when namespace is not available' do
it 'is nil' do
diff --git a/spec/lib/sidebars/concerns/work_item_hierarchy_spec.rb b/spec/lib/sidebars/concerns/work_item_hierarchy_spec.rb
deleted file mode 100644
index f0a5e032764..00000000000
--- a/spec/lib/sidebars/concerns/work_item_hierarchy_spec.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Sidebars::Concerns::WorkItemHierarchy do
- shared_examples 'hierarchy menu' do
- let(:item_id) { :hierarchy }
-
- context 'when the feature is disabled does not render' do
- before do
- stub_feature_flags(work_items_hierarchy: false)
- end
-
- specify { is_expected.to be_nil }
- end
-
- context 'when the feature is enabled does render' do
- before do
- stub_feature_flags(work_items_hierarchy: true)
- end
-
- specify { is_expected.not_to be_nil }
- end
- end
-
- describe 'Project hierarchy menu item' do
- let_it_be_with_reload(:project) { create(:project, :repository) }
-
- let(:user) { project.owner }
- let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project) }
-
- subject { Sidebars::Projects::Menus::ProjectInformationMenu.new(context).renderable_items.index { |e| e.item_id == item_id } }
-
- it_behaves_like 'hierarchy menu'
- end
-end
diff --git a/spec/lib/sidebars/projects/menus/project_information_menu_spec.rb b/spec/lib/sidebars/projects/menus/project_information_menu_spec.rb
index 5cbf0a13b66..7e8d0ab0518 100644
--- a/spec/lib/sidebars/projects/menus/project_information_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/project_information_menu_spec.rb
@@ -59,25 +59,5 @@ RSpec.describe Sidebars::Projects::Menus::ProjectInformationMenu do
specify { is_expected.to be_nil }
end
end
-
- describe 'Hierarchy' do
- let(:item_id) { :hierarchy }
-
- context 'when the feature is disabled' do
- before do
- stub_feature_flags(work_items_hierarchy: false)
- end
-
- specify { is_expected.to be_nil }
- end
-
- context 'when the feature is enabled' do
- before do
- stub_feature_flags(work_items_hierarchy: true)
- end
-
- specify { is_expected.not_to be_nil }
- end
- end
end
end
diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb
index 5c76d43d753..31c7c7a44bc 100644
--- a/spec/models/ci/build_trace_chunk_spec.rb
+++ b/spec/models/ci/build_trace_chunk_spec.rb
@@ -49,9 +49,8 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state, :clean_git
end
context 'FastDestroyAll' do
- let(:project) { create(:project) }
- let(:pipeline) { create(:ci_pipeline, project: project) }
- let!(:build) { create(:ci_build, :running, :trace_live, pipeline: pipeline, project: project) }
+ let(:pipeline) { create(:ci_pipeline) }
+ let!(:build) { create(:ci_build, :running, :trace_live, pipeline: pipeline) }
let(:subjects) { build.trace_chunks }
describe 'Forbid #destroy and #destroy_all' do
@@ -84,13 +83,7 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state, :clean_git
expect(external_data_counter).to be > 0
expect(subjects.count).to be > 0
- ::Gitlab::Database::QueryAnalyzers::PreventCrossDatabaseModification.allow_cross_database_modification_within_transaction(url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/350185') do
- # This should use to prevent cross-DB modification
- # but due to https://gitlab.com/gitlab-org/gitlab/-/issues/350185
- # the build trace chunks are not destroyed by Projects::DestroyService
- # Change to: expect { Projects::DestroyService.new(project, project.owner).execute }.to eq(true)
- expect { project.destroy! }.not_to raise_error
- end
+ expect { pipeline.destroy! }.not_to raise_error
expect(subjects.count).to eq(0)
expect(external_data_counter).to eq(0)
@@ -858,13 +851,7 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state, :clean_git
context 'when project is destroyed' do
let(:subject) do
- ::Gitlab::Database::QueryAnalyzers::PreventCrossDatabaseModification.allow_cross_database_modification_within_transaction(url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/350185') do
- # This should use to prevent cross-DB modification
- # but due to https://gitlab.com/gitlab-org/gitlab/-/issues/350185
- # the build trace chunks are not destroyed by Projects::DestroyService
- # Change to: Projects::DestroyService.new(project, project.owner).execute
- project.destroy!
- end
+ Projects::DestroyService.new(project, project.owner).execute
end
it_behaves_like 'deletes all build_trace_chunk and data in redis'
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 04d12913828..e740b9d823c 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -31,6 +31,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
it { is_expected.to have_many(:statuses_order_id_desc) }
it { is_expected.to have_many(:bridges) }
it { is_expected.to have_many(:job_artifacts).through(:builds) }
+ it { is_expected.to have_many(:build_trace_chunks).through(:builds) }
it { is_expected.to have_many(:auto_canceled_pipelines) }
it { is_expected.to have_many(:auto_canceled_jobs) }
it { is_expected.to have_many(:sourced_pipelines) }
diff --git a/spec/services/ci/after_requeue_job_service_spec.rb b/spec/services/ci/after_requeue_job_service_spec.rb
index 2a5a971fdac..2465bac7d10 100644
--- a/spec/services/ci/after_requeue_job_service_spec.rb
+++ b/spec/services/ci/after_requeue_job_service_spec.rb
@@ -8,46 +8,56 @@ RSpec.describe Ci::AfterRequeueJobService do
let(:pipeline) { create(:ci_pipeline, project: project) }
- let!(:build) { create(:ci_build, pipeline: pipeline, stage_idx: 0, name: 'build') }
- let!(:test1) { create(:ci_build, :success, pipeline: pipeline, stage_idx: 1) }
- let!(:test2) { create(:ci_build, :skipped, pipeline: pipeline, stage_idx: 1) }
- let!(:test3) { create(:ci_build, :skipped, :dependent, pipeline: pipeline, stage_idx: 1, needed: build) }
- let!(:deploy) { create(:ci_build, :skipped, :dependent, pipeline: pipeline, stage_idx: 2, needed: test3) }
+ let!(:build1) { create(:ci_build, name: 'build1', pipeline: pipeline, stage_idx: 0) }
+ let!(:test1) { create(:ci_build, :success, name: 'test1', pipeline: pipeline, stage_idx: 1) }
+ let!(:test2) { create(:ci_build, :skipped, name: 'test2', pipeline: pipeline, stage_idx: 1) }
+ let!(:test3) { create(:ci_build, :skipped, :dependent, name: 'test3', pipeline: pipeline, stage_idx: 1, needed: build1) }
+ let!(:deploy) { create(:ci_build, :skipped, :dependent, name: 'deploy', pipeline: pipeline, stage_idx: 2, needed: test3) }
- subject(:execute_service) { described_class.new(project, user).execute(build) }
+ subject(:execute_service) { described_class.new(project, user).execute(build1) }
- it 'marks subsequent skipped jobs as processable' do
- expect(test1.reload).to be_success
- expect(test2.reload).to be_skipped
- expect(test3.reload).to be_skipped
- expect(deploy.reload).to be_skipped
+ shared_examples 'processing subsequent skipped jobs' do
+ it 'marks subsequent skipped jobs as processable' do
+ expect(test1.reload).to be_success
+ expect(test2.reload).to be_skipped
+ expect(test3.reload).to be_skipped
+ expect(deploy.reload).to be_skipped
- execute_service
+ execute_service
- expect(test1.reload).to be_success
- expect(test2.reload).to be_created
- expect(test3.reload).to be_created
- expect(deploy.reload).to be_created
+ expect(test1.reload).to be_success
+ expect(test2.reload).to be_created
+ expect(test3.reload).to be_created
+ expect(deploy.reload).to be_created
+ end
end
+ it_behaves_like 'processing subsequent skipped jobs'
+
context 'when there is a job need from the same stage' do
- let!(:test4) do
+ let!(:build2) do
create(:ci_build,
:skipped,
:dependent,
+ name: 'build2',
pipeline: pipeline,
stage_idx: 0,
scheduling_type: :dag,
- needed: build)
+ needed: build1)
end
- it 'marks subsequent skipped jobs as processable' do
- expect { execute_service }.to change { test4.reload.status }.from('skipped').to('created')
+ shared_examples 'processing the same stage job' do
+ it 'marks subsequent skipped jobs as processable' do
+ expect { execute_service }.to change { build2.reload.status }.from('skipped').to('created')
+ end
end
+
+ it_behaves_like 'processing subsequent skipped jobs'
+ it_behaves_like 'processing the same stage job'
end
context 'when the pipeline is a downstream pipeline and the bridge is depended' do
- let!(:trigger_job) { create(:ci_bridge, :strategy_depend, status: 'success') }
+ let!(:trigger_job) { create(:ci_bridge, :strategy_depend, name: 'trigger_job', status: 'success') }
before do
create(:ci_sources_pipeline, pipeline: pipeline, source_job: trigger_job)
diff --git a/spec/services/ci/destroy_pipeline_service_spec.rb b/spec/services/ci/destroy_pipeline_service_spec.rb
index 6c1c02b2875..166c4c4bd80 100644
--- a/spec/services/ci/destroy_pipeline_service_spec.rb
+++ b/spec/services/ci/destroy_pipeline_service_spec.rb
@@ -66,6 +66,28 @@ RSpec.describe ::Ci::DestroyPipelineService do
expect { subject }.to change { Ci::DeletedObject.count }
end
end
+
+ context 'when job has trace chunks' do
+ let(:connection_params) { Gitlab.config.artifacts.object_store.connection.symbolize_keys }
+ let(:connection) { ::Fog::Storage.new(connection_params) }
+
+ before do
+ stub_object_storage(connection_params: connection_params, remote_directory: 'artifacts')
+ stub_artifacts_object_storage
+ end
+
+ let!(:trace_chunk) { create(:ci_build_trace_chunk, :fog_with_data, build: build) }
+
+ it 'destroys associated trace chunks' do
+ subject
+
+ expect { trace_chunk.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+
+ it 'removes data from object store' do
+ expect { subject }.to change { Ci::BuildTraceChunks::Fog.new.data(trace_chunk) }
+ end
+ end
end
context 'when pipeline is in cancelable state' do
diff --git a/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb b/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb
index 02f8f2dd99f..31e1b0a896d 100644
--- a/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb
+++ b/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb
@@ -1004,6 +1004,63 @@ RSpec.describe Ci::PipelineProcessing::AtomicProcessingService do
end
end
+ context 'when the dependency is stage-independent', :sidekiq_inline do
+ let(:config) do
+ <<-EOY
+ stages: [A, B]
+
+ A1:
+ stage: A
+ script: exit 0
+ when: manual
+
+ A2:
+ stage: A
+ script: exit 0
+ needs: [A1]
+
+ B:
+ stage: B
+ needs: [A2]
+ script: exit 0
+ EOY
+ end
+
+ let(:pipeline) do
+ Ci::CreatePipelineService.new(project, user, { ref: 'master' }).execute(:push).payload
+ end
+
+ before do
+ stub_ci_pipeline_yaml_file(config)
+ end
+
+ it 'processes subsequent jobs in the correct order when playing first job' do
+ expect(all_builds_names).to eq(%w[A1 A2 B])
+ expect(all_builds_statuses).to eq(%w[manual skipped skipped])
+
+ play_manual_action('A1')
+
+ expect(all_builds_names).to eq(%w[A1 A2 B])
+ expect(all_builds_statuses).to eq(%w[pending created created])
+ end
+
+ context 'when the FF ci_order_subsequent_jobs_by_stage is disabled' do
+ before do
+ stub_feature_flags(ci_order_subsequent_jobs_by_stage: false)
+ end
+
+ it 'processes subsequent jobs in an incorrect order when playing first job' do
+ expect(all_builds_names).to eq(%w[A1 A2 B])
+ expect(all_builds_statuses).to eq(%w[manual skipped skipped])
+
+ play_manual_action('A1')
+
+ expect(all_builds_names).to eq(%w[A1 A2 B])
+ expect(all_builds_statuses).to eq(%w[pending created skipped])
+ end
+ end
+ end
+
private
def all_builds
diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb
index 0ea51785863..c571962f739 100644
--- a/spec/support/helpers/test_env.rb
+++ b/spec/support/helpers/test_env.rb
@@ -212,9 +212,7 @@ module TestEnv
spawn_script = Rails.root.join('scripts/gitaly-test-spawn').to_s
Bundler.with_original_env do
unless system(spawn_script)
- message = 'gitaly spawn failed'
- message += " (try `rm -rf #{gitaly_dir}` ?)" unless ci?
- raise message
+ raise gitaly_failure_message
end
end
@@ -616,6 +614,39 @@ module TestEnv
expected_version == sha.chomp
end
+
+ def gitaly_failure_message
+ message = "gitaly spawn failed\n\n"
+
+ message += "- The `gitaly` binary does not exist: #{gitaly_binary}\n" unless File.exist?(gitaly_binary)
+ message += "- The `praefect` binary does not exist: #{praefect_binary}\n" unless File.exist?(praefect_binary)
+ message += "- The `git` binary does not exist: #{git_binary}\n" unless File.exist?(git_binary)
+
+ message += "\nCheck log/gitaly-test.log for errors.\n"
+
+ unless ci?
+ message += "\nIf binaries are missing, try running `make -C tmp/tests/gitaly build git.`\n"
+ message += "\nOtherwise, try running `rm -rf #{gitaly_dir}`."
+ end
+
+ message
+ end
+
+ def git_binary
+ File.join(gitaly_dir, "_build", "deps", "git", "install", "bin", "git")
+ end
+
+ def gitaly_binary
+ File.join(gitaly_dir, "_build", "bin", "gitaly")
+ end
+
+ def praefect_binary
+ File.join(gitaly_dir, "_build", "bin", "praefect")
+ end
+
+ def git_binary_exists?
+ File.exist?(git_binary)
+ end
end
require_relative('../../../ee/spec/support/helpers/ee/test_env') if Gitlab.ee?
diff --git a/spec/support/shared_contexts/navbar_structure_context.rb b/spec/support/shared_contexts/navbar_structure_context.rb
index 576a8aa44fa..27967850389 100644
--- a/spec/support/shared_contexts/navbar_structure_context.rb
+++ b/spec/support/shared_contexts/navbar_structure_context.rb
@@ -22,7 +22,6 @@ RSpec.shared_context 'project navbar structure' do
nav_sub_items: [
_('Activity'),
_('Labels'),
- _('Planning hierarchy'),
_('Members')
]
},
diff --git a/spec/support/shared_contexts/policies/project_policy_shared_context.rb b/spec/support/shared_contexts/policies/project_policy_shared_context.rb
index aabd6454b88..c39252cef13 100644
--- a/spec/support/shared_contexts/policies/project_policy_shared_context.rb
+++ b/spec/support/shared_contexts/policies/project_policy_shared_context.rb
@@ -17,7 +17,7 @@ RSpec.shared_context 'ProjectPolicy context' do
%i[
award_emoji create_issue create_merge_request_in create_note
create_project read_issue_board read_issue read_issue_iid read_issue_link
- read_label read_work_items_hierarchy read_issue_board_list read_milestone read_note read_project
+ read_label read_issue_board_list read_milestone read_note read_project
read_project_for_iids read_project_member read_release read_snippet
read_wiki upload_file
]