diff --git a/app/assets/javascripts/observability/client.js b/app/assets/javascripts/observability/client.js
index 9510efb5953..71d323108e9 100644
--- a/app/assets/javascripts/observability/client.js
+++ b/app/assets/javascripts/observability/client.js
@@ -315,13 +315,13 @@ async function fetchMetrics(metricsUrl, { filters = {}, limit } = {}) {
const params = new URLSearchParams();
if (Array.isArray(filters.search)) {
- const searchPrefix = filters.search
+ const search = filters.search
.map((f) => f.value)
.join(' ')
.trim();
- if (searchPrefix) {
- params.append('starts_with', searchPrefix);
+ if (search) {
+ params.append('search', search);
if (limit) {
params.append('limit', limit);
}
diff --git a/app/assets/javascripts/organizations/index/components/app.vue b/app/assets/javascripts/organizations/index/components/app.vue
index 9a4abdaefa5..0f7badaf2da 100644
--- a/app/assets/javascripts/organizations/index/components/app.vue
+++ b/app/assets/javascripts/organizations/index/components/app.vue
@@ -49,6 +49,9 @@ export default {
showHeader() {
return this.loading || this.organizations.nodes?.length;
},
+ showNewOrganizationButton() {
+ return gon.features?.allowOrganizationCreation;
+ },
loading() {
return this.$apollo.queries.organizations.loading;
},
@@ -79,7 +82,7 @@ export default {
{{ $options.i18n.organizations }}
- {{
+ {{
$options.i18n.newOrganization
}}
diff --git a/app/controllers/jira_connect/app_descriptor_controller.rb b/app/controllers/jira_connect/app_descriptor_controller.rb
index 4659b51d5ae..2e0949a584e 100644
--- a/app/controllers/jira_connect/app_descriptor_controller.rb
+++ b/app/controllers/jira_connect/app_descriptor_controller.rb
@@ -131,15 +131,10 @@ class JiraConnect::AppDescriptorController < JiraConnect::ApplicationController
end
def actions
- actions = {
+ {
createBranch: {
templateUrl: "#{new_jira_connect_branch_url}?issue_key={issue.key}&issue_summary={issue.summary}"
- }
- }
-
- return actions unless Feature.enabled?(:atlassian_new_app_based_auth_model)
-
- actions.merge(
+ },
searchConnectedWorkspaces: {
templateUrl: search_jira_connect_workspaces_url
},
@@ -149,6 +144,6 @@ class JiraConnect::AppDescriptorController < JiraConnect::ApplicationController
associateRepository: {
templateUrl: associate_jira_connect_repositories_url
}
- )
+ }
end
end
diff --git a/app/controllers/jira_connect/repositories_controller.rb b/app/controllers/jira_connect/repositories_controller.rb
index 12fa80d5881..eeaa459f015 100644
--- a/app/controllers/jira_connect/repositories_controller.rb
+++ b/app/controllers/jira_connect/repositories_controller.rb
@@ -2,10 +2,6 @@
module JiraConnect
class RepositoriesController < JiraConnect::ApplicationController
- before_action do
- render_404 if Feature.disabled?(:atlassian_new_app_based_auth_model)
- end
-
feature_category :integrations
def search
diff --git a/app/controllers/jira_connect/workspaces_controller.rb b/app/controllers/jira_connect/workspaces_controller.rb
index 47adb9ba506..4aaf752fe7d 100644
--- a/app/controllers/jira_connect/workspaces_controller.rb
+++ b/app/controllers/jira_connect/workspaces_controller.rb
@@ -2,10 +2,6 @@
module JiraConnect
class WorkspacesController < JiraConnect::ApplicationController
- before_action do
- render_404 if Feature.disabled?(:atlassian_new_app_based_auth_model)
- end
-
feature_category :integrations
def search
diff --git a/app/controllers/organizations/organizations_controller.rb b/app/controllers/organizations/organizations_controller.rb
index 11988da8104..48e9d785721 100644
--- a/app/controllers/organizations/organizations_controller.rb
+++ b/app/controllers/organizations/organizations_controller.rb
@@ -11,6 +11,9 @@ module Organizations
before_action :event_filter, only: [:activity]
before_action :authorize_read_organization!, only: [:activity, :show, :groups_and_projects]
+ before_action only: [:index] do
+ push_frontend_feature_flag(:allow_organization_creation, current_user)
+ end
skip_before_action :authenticate_user!, only: [:activity, :show, :groups_and_projects]
diff --git a/app/helpers/environments_helper.rb b/app/helpers/environments_helper.rb
index 66f29e84f66..018109b2d4a 100644
--- a/app/helpers/environments_helper.rb
+++ b/app/helpers/environments_helper.rb
@@ -79,12 +79,7 @@ module EnvironmentsHelper
def static_metrics_data
{
'documentation_path' => help_page_path('administration/monitoring/prometheus/index'),
- 'add_dashboard_documentation_path' => help_page_path('operations/metrics/dashboards/index', anchor: 'add-a-new-dashboard-to-your-project'),
- 'empty_getting_started_svg_path' => image_path('illustrations/monitoring/getting_started.svg'),
- 'empty_loading_svg_path' => image_path('illustrations/monitoring/loading.svg'),
- 'empty_no_data_svg_path' => image_path('illustrations/monitoring/no_data.svg'),
- 'empty_no_data_small_svg_path' => image_path('illustrations/chart-empty-state-small.svg'),
- 'empty_unable_to_connect_svg_path' => image_path('illustrations/monitoring/unable_to_connect.svg')
+ 'add_dashboard_documentation_path' => help_page_path('operations/metrics/dashboards/index', anchor: 'add-a-new-dashboard-to-your-project')
}
end
end
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index 78377537ebd..3c444aee644 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -161,7 +161,7 @@ module SearchHelper
end
def search_service
- @search_service ||= ::SearchService.new(current_user, sanitized_search_params)
+ @search_service ||= ::SearchService.new(current_user, params)
end
def search_sort_options
@@ -588,20 +588,6 @@ module SearchHelper
issuable.target_branch unless issuable.target_branch == issuable.project.default_branch
end
- def sanitized_search_params
- sanitized_params = params.dup
-
- if sanitized_params.key?(:confidential)
- sanitized_params[:confidential] = Gitlab::Utils.to_boolean(sanitized_params[:confidential])
- end
-
- if sanitized_params.key?(:include_archived)
- sanitized_params[:include_archived] = Gitlab::Utils.to_boolean(sanitized_params[:include_archived])
- end
-
- sanitized_params
- end
-
def wiki_blob_link(wiki_blob)
project_wiki_path(wiki_blob.project, wiki_blob.basename)
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 2ff713fbfae..a5b92571bf8 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -815,6 +815,10 @@ class Project < ApplicationRecord
scope :in_organization, -> (organization) { where(organization: organization) }
+ scope :not_a_fork, -> {
+ left_outer_joins(:fork_network_member).where(fork_network_member: { forked_from_project_id: nil })
+ }
+
enum auto_cancel_pending_pipelines: { disabled: 0, enabled: 1 }
chronic_duration_attr :build_timeout_human_readable, :build_timeout,
diff --git a/app/presenters/clusters/cluster_presenter.rb b/app/presenters/clusters/cluster_presenter.rb
index 5765d08dfb3..06ca392851f 100644
--- a/app/presenters/clusters/cluster_presenter.rb
+++ b/app/presenters/clusters/cluster_presenter.rb
@@ -62,11 +62,6 @@ module Clusters
'dashboard-endpoint': clusterable.metrics_dashboard_path(cluster),
'documentation-path': help_page_path('user/infrastructure/clusters/manage/clusters_health'),
'add-dashboard-documentation-path': help_page_path('operations/metrics/dashboards/index', anchor: 'add-a-new-dashboard-to-your-project'),
- 'empty-getting-started-svg-path': image_path('illustrations/monitoring/getting_started.svg'),
- 'empty-loading-svg-path': image_path('illustrations/monitoring/loading.svg'),
- 'empty-no-data-svg-path': image_path('illustrations/monitoring/no_data.svg'),
- 'empty-no-data-small-svg-path': image_path('illustrations/chart-empty-state-small.svg'),
- 'empty-unable-to-connect-svg-path': image_path('illustrations/monitoring/unable_to_connect.svg'),
'settings-path': '',
'project-path': '',
'tags-path': ''
diff --git a/app/services/organizations/create_service.rb b/app/services/organizations/create_service.rb
index d58ea875310..59f88196559 100644
--- a/app/services/organizations/create_service.rb
+++ b/app/services/organizations/create_service.rb
@@ -4,6 +4,7 @@ module Organizations
class CreateService < ::Organizations::BaseService
def execute
return error_no_permissions unless can?(current_user, :create_organization)
+ return error_feature_flag unless Feature.enabled?(:allow_organization_creation, current_user)
add_organization_owner_attributes
organization = Gitlab::Database::QueryAnalyzers::PreventCrossDatabaseModification
@@ -35,5 +36,10 @@ module Organizations
ServiceResponse.error(message: Array(message))
end
+
+ def error_feature_flag
+ # Don't translate feature flag error because it's temporary.
+ ServiceResponse.error(message: ['Feature flag `allow_organization_creation` is not enabled for this user.'])
+ end
end
end
diff --git a/config/feature_flags/development/atlassian_new_app_based_auth_model.yml b/config/feature_flags/development/allow_organization_creation.yml
similarity index 59%
rename from config/feature_flags/development/atlassian_new_app_based_auth_model.yml
rename to config/feature_flags/development/allow_organization_creation.yml
index 6b0e270d4c6..b528338b818 100644
--- a/config/feature_flags/development/atlassian_new_app_based_auth_model.yml
+++ b/config/feature_flags/development/allow_organization_creation.yml
@@ -1,8 +1,8 @@
---
-name: atlassian_new_app_based_auth_model
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/142316
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/442334
-milestone: '16.10'
-group: group::import and integrate
+name: allow_organization_creation
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147930
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/452062
+milestone: '16.11'
type: development
+group: group::tenant scale
default_enabled: false
diff --git a/db/docs/audit_events_streaming_event_type_filters.yml b/db/docs/audit_events_streaming_event_type_filters.yml
index fe36fbf5a5a..dd4e805c950 100644
--- a/db/docs/audit_events_streaming_event_type_filters.yml
+++ b/db/docs/audit_events_streaming_event_type_filters.yml
@@ -1,10 +1,25 @@
---
table_name: audit_events_streaming_event_type_filters
classes:
- - AuditEvents::Streaming::EventTypeFilter
+- AuditEvents::Streaming::EventTypeFilter
feature_categories:
- - audit_events
+- audit_events
description: Represents a event type filter for audit event streaming
introduced_by_url:
milestone: '15.6'
-gitlab_schema: gitlab_main
+gitlab_schema: gitlab_main_cell
+allow_cross_joins:
+- gitlab_main_clusterwide
+allow_cross_transactions:
+- gitlab_main_clusterwide
+allow_cross_foreign_keys:
+- gitlab_main_clusterwide
+desired_sharding_key:
+ group_id:
+ references: namespaces
+ backfill_via:
+ parent:
+ foreign_key: external_audit_event_destination_id
+ table: audit_events_external_audit_event_destinations
+ sharding_key: namespace_id
+ belongs_to: external_audit_event_destination
diff --git a/db/docs/audit_events_streaming_headers.yml b/db/docs/audit_events_streaming_headers.yml
index 4f0ef9f20b5..f33c48caa73 100644
--- a/db/docs/audit_events_streaming_headers.yml
+++ b/db/docs/audit_events_streaming_headers.yml
@@ -1,10 +1,25 @@
---
table_name: audit_events_streaming_headers
classes:
- - AuditEvents::Streaming::Header
+- AuditEvents::Streaming::Header
feature_categories:
- - audit_events
+- audit_events
description: Represents a HTTP header sent with streaming audit events
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/88063
milestone: '15.1'
-gitlab_schema: gitlab_main
+gitlab_schema: gitlab_main_cell
+allow_cross_joins:
+- gitlab_main_clusterwide
+allow_cross_transactions:
+- gitlab_main_clusterwide
+allow_cross_foreign_keys:
+- gitlab_main_clusterwide
+desired_sharding_key:
+ group_id:
+ references: namespaces
+ backfill_via:
+ parent:
+ foreign_key: external_audit_event_destination_id
+ table: audit_events_external_audit_event_destinations
+ sharding_key: namespace_id
+ belongs_to: external_audit_event_destination
diff --git a/doc/administration/dedicated/create_instance.md b/doc/administration/dedicated/create_instance.md
index b89d4beca87..d13b5981174 100644
--- a/doc/administration/dedicated/create_instance.md
+++ b/doc/administration/dedicated/create_instance.md
@@ -237,7 +237,7 @@ Where **T** is the date of a [minor GitLab release](../../policy/maintenance.md)
1. At T+6 calendar days: Tenant instances in the `APAC` maintenance window are upgraded.
1. At T+10 calendar days: Tenant instances in the `AMER Option 2` maintenance window are upgraded.
-For example, GitLab 16.9 released on 2024-02-15. Therefore, tenant instances in the `EMEA` and `AMER Option 1` maintenance window are upgraded on 2024-04-20.
+For example, GitLab 16.9 released on 2024-02-15. Therefore, tenant instances in the `EMEA` and `AMER Option 1` maintenance window are upgraded to 16.8 on 2024-02-20.
#### Emergency maintenance
diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md
index c1558525861..3f813d824fe 100644
--- a/doc/ci/yaml/index.md
+++ b/doc/ci/yaml/index.md
@@ -1295,7 +1295,7 @@ Use `expire_in` to specify how long [job artifacts](../jobs/job_artifacts.md) ar
they expire and are deleted. The `expire_in` setting does not affect:
- Artifacts from the latest job, unless keeping the latest job artifacts is disabled
- [at the project level](../jobs/job_artifacts.md#keep-artifacts-from-most-recent-successful-jobs).
+ [at the project level](../jobs/job_artifacts.md#keep-artifacts-from-most-recent-successful-jobs)
or [instance-wide](../../administration/settings/continuous_integration.md#keep-the-latest-artifacts-for-all-jobs-in-the-latest-successful-pipelines).
After their expiry, artifacts are deleted hourly by default (using a cron job), and are not
diff --git a/doc/development/documentation/styleguide/index.md b/doc/development/documentation/styleguide/index.md
index af6a445d465..0dc404224a0 100644
--- a/doc/development/documentation/styleguide/index.md
+++ b/doc/development/documentation/styleguide/index.md
@@ -1740,6 +1740,31 @@ For status, choose one:
Generally available features should not have a status.
+##### Duplicating tier, offering, or status on subheadings
+
+If a subheading has the same tier, offering, or status as its parent
+topic, you don't need to repeat the information in the subheading's
+badge.
+
+For example, if the heading 1 is:
+
+```markdown
+# My title
+
+DETAILS:
+**Offering:** GitLab.com
+**Tier:** Premium, Ultimate
+```
+
+Any lower-level heading that applies to a different tier but same offering would be:
+
+```markdown
+## My title
+
+DETAILS:
+**Tier:** Ultimate
+```
+
##### Inline tier badges
Do not add tier badges inline with other text.
diff --git a/doc/user/ai_features.md b/doc/user/ai_features.md
index a3bf4a99fa5..91ffbe28fc1 100644
--- a/doc/user/ai_features.md
+++ b/doc/user/ai_features.md
@@ -21,7 +21,7 @@ Some features are still in development. View details about [support for each sta
| Helps you write code more efficiently by showing code suggestions as you type.
[Watch overview](https://www.youtube.com/watch?v=hCAyCTacdAQ) | [Code Suggestions](project/repository/code_suggestions/index.md) | **Tier:** Premium or Ultimate with [GitLab Duo Pro](../subscriptions/subscription-add-ons.md)
**Offering:** GitLab.com, Self-managed, GitLab Dedicated |
| Processes and generates text and code in a conversational manner. Helps you quickly identify useful information in large volumes of text in issues, epics, code, and GitLab documentation. | [Chat](gitlab_duo_chat.md) | **Tier:** Premium, Ultimate
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
**Status:** Beta (Subject to the [Testing Agreement](https://handbook.gitlab.com/handbook/legal/testing-agreement/)) |
| Helps you discover or recall Git commands when and where you need them. | [Git suggestions](../editor_extensions/gitlab_cli/index.md#gitlab-duo-commands) | **Tier:** Ultimate
**Offering:** GitLab.com
**Status:** Experiment |
-| Assists with quickly getting everyone up to speed on lengthy conversations to help ensure you are all on the same page. | [Discussion summary](#summarize-issue-discussions-with-discussion-summary) | **Tier:** Ultimate
**Offering:** GitLab.com
**Status:** Experiment |
+| Assists with quickly getting everyone up to speed on lengthy conversations to help ensure you are all on the same page.
[Watch overview](https://www.youtube.com/watch?v=IcdxLfTIUgc) | [Discussion summary](#summarize-issue-discussions-with-discussion-summary) | **Tier:** Ultimate
**Offering:** GitLab.com
**Status:** Experiment |
| Generates issue descriptions. | [Issue description generation](#summarize-an-issue-with-issue-description-generation) | **Tier:** Ultimate
**Offering:** GitLab.com
**Status:** Experiment |
| Automates repetitive tasks and helps catch bugs early. | [Test generation](gitlab_duo_chat.md#write-tests-in-the-ide) | **Tier:** Ultimate
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
**Status:** Beta |
| Generates a description for the merge request based on the contents of the template. | [Merge request template population](project/merge_requests/ai_in_merge_requests.md#fill-in-merge-request-templates) | **Tier:** Ultimate
**Offering:** GitLab.com
**Status:** Experiment |
diff --git a/doc/user/free_user_limit.md b/doc/user/free_user_limit.md
index f0b3d93d55c..63c8d947623 100644
--- a/doc/user/free_user_limit.md
+++ b/doc/user/free_user_limit.md
@@ -7,7 +7,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Free user limit
DETAILS:
-**Tier:** Free, Premium, Ultimate
+**Tier:** Free
**Offering:** GitLab.com
A five-user limit applies to newly created top-level namespaces with
diff --git a/lib/container_registry/client.rb b/lib/container_registry/client.rb
index 580ba2bdc0d..ff16b79e6f0 100644
--- a/lib/container_registry/client.rb
+++ b/lib/container_registry/client.rb
@@ -58,6 +58,10 @@ module ContainerRegistry
}
end
+ def connected?
+ !registry_info.empty?
+ end
+
def repository_tags(name, page_size: DEFAULT_TAGS_PAGE_SIZE)
response = faraday.get("/v2/#{name}/tags/list") do |req|
req.params['n'] = page_size
diff --git a/lib/gitlab/search/params.rb b/lib/gitlab/search/params.rb
index a7896b7d80d..d61e7997aff 100644
--- a/lib/gitlab/search/params.rb
+++ b/lib/gitlab/search/params.rb
@@ -18,7 +18,7 @@ module Gitlab
alias_method :term, :query_string
def initialize(params, detect_abuse: true)
- @raw_params = params.is_a?(Hash) ? params.with_indifferent_access : params.dup
+ @raw_params = convert_all_boolean_params(params)
@query_string = strip_surrounding_whitespace(@raw_params[:search] || @raw_params[:term])
@detect_abuse = detect_abuse
@abuse_detection = AbuseDetection.new(self) if @detect_abuse
@@ -93,6 +93,24 @@ module Gitlab
def strip_surrounding_whitespace(obj)
obj.to_s.strip
end
+
+ def convert_all_boolean_params(params)
+ converted_params = params.is_a?(Hash) ? params.with_indifferent_access : params.dup
+
+ if converted_params.key?(:confidential)
+ converted_params[:confidential] = Gitlab::Utils.to_boolean(converted_params[:confidential])
+ end
+
+ if converted_params.key?(:include_archived)
+ converted_params[:include_archived] = Gitlab::Utils.to_boolean(converted_params[:include_archived])
+ end
+
+ if converted_params.key?(:include_forked)
+ converted_params[:include_forked] = Gitlab::Utils.to_boolean(converted_params[:include_forked])
+ end
+
+ converted_params
+ end
end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 8e5d97f247b..9735df9edd5 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -34635,7 +34635,7 @@ msgstr ""
msgid "ObservabilityMetrics|Search"
msgstr ""
-msgid "ObservabilityMetrics|Search metrics starting with..."
+msgid "ObservabilityMetrics|Search metrics..."
msgstr ""
msgid "ObservabilityMetrics|Select attributes"
@@ -45826,6 +45826,9 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load images."
msgstr ""
+msgid "SecurityOrchestration|Fetching"
+msgstr ""
+
msgid "SecurityOrchestration|Fetching the scope information."
msgstr ""
diff --git a/qa/gdk/Dockerfile.gdk b/qa/gdk/Dockerfile.gdk
index de8f106a5d1..7b5f315e2b6 100644
--- a/qa/gdk/Dockerfile.gdk
+++ b/qa/gdk/Dockerfile.gdk
@@ -1,4 +1,4 @@
-ARG GDK_SHA=5c935ca5fabd2f0de85aee3e8799aee95b371123
+ARG GDK_SHA=a2f71be9f31d963372199010b9682bdfbab11b10
# Use tag prefix when running on 'stable' branch to make sure 'protected' image is used which is not deleted by registry cleanup
ARG GDK_BASE_TAG_PREFIX
diff --git a/qa/qa/page/project/pipeline/show.rb b/qa/qa/page/project/pipeline/show.rb
index 151df85af3d..9961082e5f7 100644
--- a/qa/qa/page/project/pipeline/show.rb
+++ b/qa/qa/page/project/pipeline/show.rb
@@ -32,6 +32,7 @@ module QA
view 'app/assets/javascripts/ci/pipeline_details/graph/components/stage_column_component.vue' do
element 'job-item-container', required: true
+ element 'stage-column-title'
end
def running?(wait: 0)
@@ -134,6 +135,10 @@ module QA
end
end
end
+
+ def has_stage?(name)
+ has_element?('stage-column-title', text: name)
+ end
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/import/import_github_repo_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/import/import_github_repo_spec.rb
index 2fe6e0ca714..2e648f8d7d5 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/import/import_github_repo_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/import/import_github_repo_spec.rb
@@ -2,7 +2,11 @@
module QA
RSpec.describe 'Manage', :github, :requires_admin, product_group: :import_and_integrate do
- describe 'GitHub import' do
+ describe 'GitHub import',
+ quarantine: {
+ type: :investigating,
+ issue: "https://gitlab.com/gitlab-org/gitlab/-/issues/452419"
+ } do
include_context 'with github import'
context 'when imported via UI' do
diff --git a/qa/qa/specs/features/browser_ui/4_verify/ci_components_catalog/run_component_in_project_pipeline_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/ci_components_catalog/run_component_in_project_pipeline_spec.rb
new file mode 100644
index 00000000000..7f7addf482d
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/4_verify/ci_components_catalog/run_component_in_project_pipeline_spec.rb
@@ -0,0 +1,115 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Verify', :runner, :skip_live_env, product_group: :pipeline_authoring do
+ describe 'CI component' do
+ let(:executor) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(number: 8)}" }
+ let(:tag) { '1.0.0' }
+ let(:domain_name) { Runtime::Scenario.gitlab_address.split("/").last }
+ let(:test_stage) { 'test' }
+ let(:test_phrase) { 'this is NOT secret!!!!!!!' }
+
+ let(:component_project) do
+ create(:project, :with_readme, name: 'component-project', description: 'This is a project with CI component.')
+ end
+
+ let(:test_project) do
+ create(:project, :with_readme, name: 'project-to-test-component')
+ end
+
+ let!(:runner) do
+ Resource::ProjectRunner.fabricate! do |runner|
+ runner.project = test_project
+ runner.name = executor
+ runner.tags = [executor]
+ end
+ end
+
+ let(:component_content) do
+ <<~YAML
+ spec:
+ inputs:
+ secret-phrase:
+ default: 'this is secret'
+ stage:
+ default: "#{test_stage}"
+ ---
+ my-component:
+ script: echo $[[ inputs.secret-phrase ]]
+ YAML
+ end
+
+ let(:ci_yml_content) do
+ <<~YAML
+ default:
+ tags: ["#{executor}"]
+
+ include:
+ - component: "#{domain_name}/#{component_project.full_path}/new-component@#{tag}"
+ inputs:
+ secret-phrase: #{test_phrase}
+
+ cat:
+ stage: deploy
+ script: echo 'Meow'
+ YAML
+ end
+
+ let(:pipeline) do
+ create(:pipeline, project: test_project, id: test_project.latest_pipeline[:id])
+ end
+
+ before do
+ Flow::Login.sign_in
+
+ enable_catalog_resource_feature
+ add_ci_file(component_project, 'templates/new-component.yml', component_content)
+ component_project.create_release(tag)
+
+ test_project.visit!
+ add_ci_file(test_project, '.gitlab-ci.yml', ci_yml_content)
+ end
+
+ after do
+ runner.remove_via_api!
+ end
+
+ it 'runs in project pipeline with correct inputs', :aggregate_failures,
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/451582' do
+ Flow::Pipeline.visit_latest_pipeline(status: 'Passed')
+
+ Page::Project::Pipeline::Show.perform do |show|
+ expect(show).to have_stage(test_stage), "Expected pipeline to have stage #{test_stage} but not found."
+ end
+
+ Flow::Pipeline.visit_pipeline_job_page(job_name: 'my-component', pipeline: pipeline)
+
+ Page::Project::Job::Show.perform do |show|
+ expect(show.output).to have_content(test_phrase),
+ "Component job failed to use custom phrase #{test_phrase}."
+ end
+ end
+
+ private
+
+ def enable_catalog_resource_feature
+ component_project.visit!
+
+ Page::Project::Menu.perform(&:go_to_general_settings)
+ Page::Project::Settings::Main.perform do |settings|
+ settings.expand_visibility_project_features_permissions(&:enable_ci_cd_catalog_resource)
+ end
+ end
+
+ def add_ci_file(project, file_path, content)
+ create(:commit, project: project, commit_message: 'Add CI yml file', actions: [
+ {
+ action: 'create',
+ file_path: file_path,
+ content: content
+ }
+ ])
+ end
+ end
+ end
+end
diff --git a/qa/qa/support/matchers/have_matcher.rb b/qa/qa/support/matchers/have_matcher.rb
index b8c63166068..a2b9c740f97 100644
--- a/qa/qa/support/matchers/have_matcher.rb
+++ b/qa/qa/support/matchers/have_matcher.rb
@@ -30,6 +30,7 @@ module QA
security_configuration_history_link
skipped_job_in_group
snippet_description
+ stage
system_note
tag
variable
diff --git a/spec/controllers/jira_connect/app_descriptor_controller_spec.rb b/spec/controllers/jira_connect/app_descriptor_controller_spec.rb
index 0d438cd4dce..e23b76146af 100644
--- a/spec/controllers/jira_connect/app_descriptor_controller_spec.rb
+++ b/spec/controllers/jira_connect/app_descriptor_controller_spec.rb
@@ -100,23 +100,6 @@ RSpec.describe JiraConnect::AppDescriptorController, feature_category: :integrat
)
)
end
-
- context 'when feature flag is disabled' do
- before do
- stub_feature_flags(atlassian_new_app_based_auth_model: false)
- end
-
- it 'returns JSON app descriptior with createBranch action' do
- get :show
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(descriptor[:modules][:jiraDevelopmentTool][:actions]).to include(
- createBranch: {
- templateUrl: 'http://test.host/-/jira_connect/branches/new?issue_key={issue.key}&issue_summary={issue.summary}'
- }
- )
- end
- end
end
end
end
diff --git a/spec/frontend/observability/client_spec.js b/spec/frontend/observability/client_spec.js
index 9e35735d155..6aca976c3a9 100644
--- a/spec/frontend/observability/client_spec.js
+++ b/spec/frontend/observability/client_spec.js
@@ -732,7 +732,7 @@ describe('buildClient', () => {
await client.fetchMetrics({
filters: { search: [{ value: 'foo' }, { value: 'bar' }, { value: ' ' }] },
});
- expect(getQueryParam()).toBe('starts_with=foo+bar');
+ expect(getQueryParam()).toBe('search=foo+bar');
});
it('ignores empty search', async () => {
@@ -769,7 +769,7 @@ describe('buildClient', () => {
filters: { search: [{ value: 'foo' }] },
limit: 50,
});
- expect(getQueryParam()).toBe('starts_with=foo&limit=50');
+ expect(getQueryParam()).toBe('search=foo&limit=50');
});
it('does not add the search limit param if the search filter is missing', async () => {
diff --git a/spec/frontend/organizations/index/components/app_spec.js b/spec/frontend/organizations/index/components/app_spec.js
index 5d86ff36349..0b607cc5902 100644
--- a/spec/frontend/organizations/index/components/app_spec.js
+++ b/spec/frontend/organizations/index/components/app_spec.js
@@ -90,34 +90,76 @@ describe('OrganizationsIndexApp', () => {
});
};
- describe('when API call is loading', () => {
+ describe('`allowOrganizationCreation` is enabled', () => {
beforeEach(() => {
- createComponent(jest.fn().mockReturnValue(new Promise(() => {})));
+ gon.features = { allowOrganizationCreation: true };
});
- itRendersHeaderText();
- itRendersNewOrganizationButton();
- itDoesNotRenderErrorMessage();
+ describe('when API call is loading', () => {
+ beforeEach(() => {
+ createComponent(jest.fn().mockResolvedValue({}));
+ });
- it('renders the organizations view with loading prop set to true', () => {
- expect(findOrganizationsView().props('loading')).toBe(true);
+ itRendersHeaderText();
+ itRendersNewOrganizationButton();
+ itDoesNotRenderErrorMessage();
+
+ it('renders the organizations view with loading prop set to true', () => {
+ expect(findOrganizationsView().props('loading')).toBe(true);
+ });
+ });
+ describe('when API call is successful', () => {
+ beforeEach(async () => {
+ createComponent();
+ await waitForPromises();
+ });
+
+ itRendersHeaderText();
+ itRendersNewOrganizationButton();
+ itDoesNotRenderErrorMessage();
+
+ it('passes organizations to view component', () => {
+ expect(findOrganizationsView().props()).toMatchObject({
+ loading: false,
+ organizations,
+ });
+ });
});
});
- describe('when API call is successful', () => {
- beforeEach(async () => {
- createComponent();
- await waitForPromises();
+ describe('`allowOrganizationCreation` is disabled', () => {
+ beforeEach(() => {
+ gon.features = { allowOrganizationCreation: false };
});
- itRendersHeaderText();
- itRendersNewOrganizationButton();
- itDoesNotRenderErrorMessage();
+ describe('when API call is loading', () => {
+ beforeEach(() => {
+ createComponent(jest.fn().mockResolvedValue({}));
+ });
- it('passes organizations to view component', () => {
- expect(findOrganizationsView().props()).toMatchObject({
- loading: false,
- organizations,
+ itRendersHeaderText();
+ itDoesNotRenderNewOrganizationButton();
+ itDoesNotRenderErrorMessage();
+
+ it('renders the organizations view with loading prop set to true', () => {
+ expect(findOrganizationsView().props('loading')).toBe(true);
+ });
+ });
+ describe('when API call is successful', () => {
+ beforeEach(() => {
+ createComponent();
+ return waitForPromises();
+ });
+
+ itRendersHeaderText();
+ itDoesNotRenderNewOrganizationButton();
+ itDoesNotRenderErrorMessage();
+
+ it('passes organizations to view component', () => {
+ expect(findOrganizationsView().props()).toMatchObject({
+ loading: false,
+ organizations,
+ });
});
});
});
diff --git a/spec/helpers/environments_helper_spec.rb b/spec/helpers/environments_helper_spec.rb
index 2aae7b61bd1..512b5c8b316 100644
--- a/spec/helpers/environments_helper_spec.rb
+++ b/spec/helpers/environments_helper_spec.rb
@@ -28,10 +28,6 @@ RSpec.describe EnvironmentsHelper, feature_category: :environment_management do
'current_environment_name' => environment.name,
'documentation_path' => help_page_path('administration/monitoring/prometheus/index'),
'add_dashboard_documentation_path' => help_page_path('operations/metrics/dashboards/index', anchor: 'add-a-new-dashboard-to-your-project'),
- 'empty_getting_started_svg_path' => match_asset_path('/assets/illustrations/monitoring/getting_started.svg'),
- 'empty_loading_svg_path' => match_asset_path('/assets/illustrations/monitoring/loading.svg'),
- 'empty_no_data_svg_path' => match_asset_path('/assets/illustrations/monitoring/no_data.svg'),
- 'empty_unable_to_connect_svg_path' => match_asset_path('/assets/illustrations/monitoring/unable_to_connect.svg'),
'deployments_endpoint' => project_environment_deployments_path(project, environment, format: :json),
'default_branch' => 'master',
'project_path' => project_path(project),
diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb
index e8c412cc892..5c6b646d5d5 100644
--- a/spec/helpers/search_helper_spec.rb
+++ b/spec/helpers/search_helper_spec.rb
@@ -789,53 +789,16 @@ RSpec.describe SearchHelper, feature_category: :global_search do
end
describe '#search_service' do
- using RSpec::Parameterized::TableSyntax
-
- subject { search_service }
+ let(:params) { { include_archived: true } }
before do
allow(self).to receive(:current_user).and_return(:the_current_user)
end
- shared_context 'with inputs' do
- where(:input, :expected) do
- '0' | false
- '1' | true
- 'yes' | true
- 'no' | false
- 'true' | true
- 'false' | false
- true | true
- false | false
- end
- end
+ it 'instantiates a new SearchService with current_user and params' do
+ expect(::SearchService).to receive(:new).with(:the_current_user, { include_archived: true })
- describe 'for confidential' do
- let(:params) { { confidential: input } }
-
- include_context 'with inputs'
-
- with_them do
- it 'transforms param' do
- expect(::SearchService).to receive(:new).with(:the_current_user, { confidential: expected })
-
- subject
- end
- end
- end
-
- describe 'for include_archived' do
- let(:params) { { include_archived: input } }
-
- include_context 'with inputs'
-
- with_them do
- it 'transforms param' do
- expect(::SearchService).to receive(:new).with(:the_current_user, { include_archived: expected })
-
- subject
- end
- end
+ search_service
end
end
diff --git a/spec/lib/container_registry/client_spec.rb b/spec/lib/container_registry/client_spec.rb
index 37161119744..c8f835b9704 100644
--- a/spec/lib/container_registry/client_spec.rb
+++ b/spec/lib/container_registry/client_spec.rb
@@ -468,6 +468,32 @@ RSpec.describe ContainerRegistry::Client, feature_category: :container_registry
it_behaves_like 'handling registry info'
end
+ describe '#connected?' do
+ subject { client.connected? }
+
+ context 'with a valid connection' do
+ before do
+ stub_container_registry_config(enabled: true, api_url: registry_api_url, key: 'spec/fixtures/x509_certificate_pk.key')
+ stub_registry_info
+ end
+
+ it 'returns true' do
+ expect(subject).to be true
+ end
+ end
+
+ context 'with an invalid connection' do
+ before do
+ stub_container_registry_config(enabled: true, api_url: registry_api_url, key: 'spec/fixtures/x509_certificate_pk.key')
+ stub_registry_info(status: 500)
+ end
+
+ it 'returns false' do
+ expect(subject).to be false
+ end
+ end
+ end
+
def stub_upload(path, content, digest, status = 200)
stub_request(:post, "#{registry_api_url}/v2/#{path}/blobs/uploads/")
.with(headers: headers_with_accept_types)
diff --git a/spec/lib/gitlab/search/params_spec.rb b/spec/lib/gitlab/search/params_spec.rb
index 3c64082aeeb..3f2ec8b178d 100644
--- a/spec/lib/gitlab/search/params_spec.rb
+++ b/spec/lib/gitlab/search/params_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Gitlab::Search::Params, feature_category: :global_search do
- subject { described_class.new(params, detect_abuse: detect_abuse) }
+ subject(:search_params) { described_class.new(params, detect_abuse: detect_abuse) }
let(:search) { 'search' }
let(:group_id) { 123 }
@@ -18,13 +18,14 @@ RSpec.describe Gitlab::Search::Params, feature_category: :global_search do
it 'uses AbuseDetection by default' do
expect(Gitlab::Search::AbuseDetection).to receive(:new).at_least(:once).and_call_original
- described_class.new(params)
+
+ search_params
end
end
describe '#[]' do
it 'feels like regular params' do
- expect(subject[:group_id]).to eq(params[:group_id])
+ expect(search_params[:group_id]).to eq(params[:group_id])
end
it 'has indifferent access' do
@@ -34,7 +35,7 @@ RSpec.describe Gitlab::Search::Params, feature_category: :global_search do
end
it 'also works on attr_reader attributes' do
- expect(subject[:query_string]).to eq(subject.query_string)
+ expect(search_params[:query_string]).to eq(search_params.query_string)
end
end
@@ -57,7 +58,7 @@ RSpec.describe Gitlab::Search::Params, feature_category: :global_search do
end
it 'strips surrounding whitespace from query string' do
- params = described_class.new({ search: ' ' + search + ' ' })
+ params = described_class.new({ search: " #{search} " })
expect(params.query_string).to eq(search)
end
end
@@ -68,13 +69,13 @@ RSpec.describe Gitlab::Search::Params, feature_category: :global_search do
it 'does NOT validate AbuseDetector' do
expect(Gitlab::Search::AbuseDetection).not_to receive(:new)
- subject.validate
+ search_params.validate
end
end
it 'validates AbuseDetector on validation' do
expect(Gitlab::Search::AbuseDetection).to receive(:new).at_least(:once).and_call_original
- subject.validate
+ search_params.validate
end
context 'when query has too many terms' do
@@ -96,13 +97,13 @@ RSpec.describe Gitlab::Search::Params, feature_category: :global_search do
it 'does NOT validate AbuseDetector' do
expect(Gitlab::Search::AbuseDetection).not_to receive(:new)
- subject.valid?
+ search_params.valid?
end
end
it 'validates AbuseDetector on validation' do
expect(Gitlab::Search::AbuseDetection).to receive(:new).at_least(:once).and_call_original
- subject.valid?
+ search_params.valid?
end
end
@@ -110,7 +111,7 @@ RSpec.describe Gitlab::Search::Params, feature_category: :global_search do
let(:abuse_detection) { instance_double(Gitlab::Search::AbuseDetection) }
before do
- allow(subject).to receive(:abuse_detection).and_return abuse_detection
+ allow(search_params).to receive(:abuse_detection).and_return abuse_detection
allow(abuse_detection).to receive(:errors).and_return abuse_errors
end
@@ -118,7 +119,7 @@ RSpec.describe Gitlab::Search::Params, feature_category: :global_search do
let(:abuse_errors) { { foo: ['bar'] } }
it 'is considered abusive' do
- expect(subject).to be_abusive
+ expect(search_params).to be_abusive
end
end
@@ -127,20 +128,20 @@ RSpec.describe Gitlab::Search::Params, feature_category: :global_search do
context 'and there are other validation errors' do
it 'is NOT considered abusive' do
- allow(subject).to receive(:valid?) do
- subject.errors.add :project_id, 'validation error unrelated to abuse'
+ allow(search_params).to receive(:valid?) do
+ search_params.errors.add :project_id, 'validation error unrelated to abuse'
false
end
- expect(subject).not_to be_abusive
+ expect(search_params).not_to be_abusive
end
end
context 'and there are NO other validation errors' do
it 'is NOT considered abusive' do
- allow(subject).to receive(:valid?).and_return(true)
+ allow(search_params).to receive(:valid?).and_return(true)
- expect(subject).not_to be_abusive
+ expect(search_params).not_to be_abusive
end
end
end
@@ -153,4 +154,57 @@ RSpec.describe Gitlab::Search::Params, feature_category: :global_search do
expect(described_class.new({ search: 'foo bar' })).not_to be_email_lookup
end
end
+
+ describe 'converts boolean params' do
+ using RSpec::Parameterized::TableSyntax
+
+ shared_context 'with inputs' do
+ where(:input, :expected) do
+ '0' | false
+ '1' | true
+ 'yes' | true
+ 'no' | false
+ 'true' | true
+ 'false' | false
+ true | true
+ false | false
+ end
+ end
+
+ describe 'for confidential' do
+ let(:params) { { group_id: 123, search: search, confidential: input } }
+
+ include_context 'with inputs'
+
+ with_them do
+ it 'transforms param' do
+ expect(search_params[:confidential]).to eq(expected)
+ end
+ end
+ end
+
+ describe 'for include_archived' do
+ let(:params) { { group_id: 123, search: search, include_archived: input } }
+
+ include_context 'with inputs'
+
+ with_them do
+ it 'transforms param' do
+ expect(search_params[:include_archived]).to eq(expected)
+ end
+ end
+ end
+
+ describe 'for include_forked' do
+ let(:params) { { group_id: 123, search: search, include_forked: input } }
+
+ include_context 'with inputs'
+
+ with_them do
+ it 'transforms param' do
+ expect(search_params[:include_forked]).to eq(expected)
+ end
+ end
+ end
+ end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 0d4609a89ed..858d4f5512a 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -7038,6 +7038,18 @@ RSpec.describe Project, factory_default: :keep, feature_category: :groups_and_pr
end
end
+ describe '.not_a_fork' do
+ let_it_be(:project) { create(:project, :public) }
+
+ subject(:not_a_fork) { described_class.not_a_fork }
+
+ it 'returns projects which are not forks' do
+ fork_project(project)
+
+ expect(not_a_fork).to contain_exactly(project)
+ end
+ end
+
describe '.deployments' do
subject { project.deployments }
diff --git a/spec/presenters/clusters/cluster_presenter_spec.rb b/spec/presenters/clusters/cluster_presenter_spec.rb
index aacb696a88e..6027d9d3f31 100644
--- a/spec/presenters/clusters/cluster_presenter_spec.rb
+++ b/spec/presenters/clusters/cluster_presenter_spec.rb
@@ -124,11 +124,6 @@ RSpec.describe Clusters::ClusterPresenter do
'dashboard-endpoint': clusterable_presenter.metrics_dashboard_path(cluster),
'documentation-path': help_page_path('user/infrastructure/clusters/manage/clusters_health'),
'add-dashboard-documentation-path': help_page_path('operations/metrics/dashboards/index', anchor: 'add-a-new-dashboard-to-your-project'),
- 'empty-getting-started-svg-path': match_asset_path('/assets/illustrations/monitoring/getting_started.svg'),
- 'empty-loading-svg-path': match_asset_path('/assets/illustrations/monitoring/loading.svg'),
- 'empty-no-data-svg-path': match_asset_path('/assets/illustrations/monitoring/no_data.svg'),
- 'empty-no-data-small-svg-path': match_asset_path('illustrations/chart-empty-state-small.svg'),
- 'empty-unable-to-connect-svg-path': match_asset_path('/assets/illustrations/monitoring/unable_to_connect.svg'),
'settings-path': '',
'project-path': '',
'tags-path': ''
diff --git a/spec/requests/jira_connect/repositories_controller_spec.rb b/spec/requests/jira_connect/repositories_controller_spec.rb
index 7397f3ba60d..61ffa95241e 100644
--- a/spec/requests/jira_connect/repositories_controller_spec.rb
+++ b/spec/requests/jira_connect/repositories_controller_spec.rb
@@ -65,17 +65,6 @@ RSpec.describe JiraConnect::RepositoriesController, feature_category: :integrati
expect(json_response).to include('containers' => [expected_response])
end
end
-
- context 'when feature flag is disabled' do
- before do
- stub_feature_flags(atlassian_new_app_based_auth_model: false)
- get '/-/jira_connect/repositories/search', params: { jwt: jwt, searchQuery: search_query }
- end
-
- it 'returns 404' do
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
end
end
@@ -115,17 +104,6 @@ RSpec.describe JiraConnect::RepositoriesController, feature_category: :integrati
expect(json_response).to include(expected_response)
end
end
-
- context 'when feature flag is disabled' do
- before do
- stub_feature_flags(atlassian_new_app_based_auth_model: false)
- post '/-/jira_connect/repositories/associate', params: { jwt: jwt, id: id }
- end
-
- it 'returns 404' do
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
end
end
end
diff --git a/spec/requests/jira_connect/workspaces_controller_spec.rb b/spec/requests/jira_connect/workspaces_controller_spec.rb
index b07dd769bbc..487816a610b 100644
--- a/spec/requests/jira_connect/workspaces_controller_spec.rb
+++ b/spec/requests/jira_connect/workspaces_controller_spec.rb
@@ -60,17 +60,6 @@ RSpec.describe JiraConnect::WorkspacesController, feature_category: :integration
expect(json_response).to include(expected_response)
end
end
-
- context 'when feature flag is disabled' do
- before do
- stub_feature_flags(atlassian_new_app_based_auth_model: false)
- get '/-/jira_connect/workspaces/search', params: { jwt: jwt, searchQuery: search_query }
- end
-
- it 'returns 404' do
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
end
end
end
diff --git a/spec/services/organizations/create_service_spec.rb b/spec/services/organizations/create_service_spec.rb
index bbc0f3d7515..3ebce5fe8f4 100644
--- a/spec/services/organizations/create_service_spec.rb
+++ b/spec/services/organizations/create_service_spec.rb
@@ -65,5 +65,18 @@ RSpec.describe Organizations::CreateService, feature_category: :cell do
end
end
end
+
+ context 'when `allow_organization_creation` FF is disabled' do
+ before do
+ stub_feature_flags(allow_organization_creation: false)
+ end
+
+ it 'returns an error' do
+ expect(response).to be_error
+
+ expect(response.message)
+ .to match_array(['Feature flag `allow_organization_creation` is not enabled for this user.'])
+ end
+ end
end
end