diff --git a/app/assets/javascripts/ci/runner/components/runner_status_badge.vue b/app/assets/javascripts/ci/runner/components/runner_status_badge.vue
index c2c52bd756a..8b775197386 100644
--- a/app/assets/javascripts/ci/runner/components/runner_status_badge.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_status_badge.vue
@@ -2,29 +2,42 @@
import { GlBadge, GlTooltipDirective } from '@gitlab/ui';
import { __, sprintf } from '~/locale';
import { getTimeago } from '~/lib/utils/datetime_utility';
+import { duration } from '~/lib/utils/datetime/timeago_utility';
import {
I18N_STATUS_ONLINE,
I18N_STATUS_NEVER_CONTACTED,
I18N_STATUS_OFFLINE,
I18N_STATUS_STALE,
- I18N_ONLINE_TIMEAGO_TOOLTIP,
+ I18N_ONLINE_TOOLTIP,
I18N_NEVER_CONTACTED_TOOLTIP,
- I18N_OFFLINE_TIMEAGO_TOOLTIP,
- I18N_STALE_TIMEAGO_TOOLTIP,
- I18N_STALE_NEVER_CONTACTED_TOOLTIP,
+ I18N_NEVER_CONTACTED_STALE_TOOLTIP,
+ I18N_DISCONNECTED_TOOLTIP,
STATUS_ONLINE,
STATUS_NEVER_CONTACTED,
STATUS_OFFLINE,
STATUS_STALE,
+ ONLINE_CONTACT_TIMEOUT_SECS,
+ STALE_TIMEOUT_SECS,
} from '../constants';
export default {
+ name: 'RunnerStatusBadge',
components: {
GlBadge,
},
directives: {
GlTooltip: GlTooltipDirective,
},
+ inject: {
+ onlineContactTimeoutSecs: {
+ // Real value must be provided from ::Ci::Runner::ONLINE_CONTACT_TIMEOUT
+ default: ONLINE_CONTACT_TIMEOUT_SECS,
+ },
+ staleTimeoutSecs: {
+ // Real value must be provided from ::Ci::Runner::STALE_TIMEOUT
+ default: STALE_TIMEOUT_SECS,
+ },
+ },
props: {
contactedAt: {
type: String,
@@ -38,6 +51,12 @@ export default {
},
},
computed: {
+ onlineContactTimeoutDuration() {
+ return duration(this.onlineContactTimeoutSecs * 1000);
+ },
+ staleTimeoutDuration() {
+ return duration(this.staleTimeoutSecs * 1000);
+ },
contactedAtTimeAgo() {
if (this.contactedAt) {
return getTimeago().format(this.contactedAt);
@@ -52,7 +71,9 @@ export default {
icon: 'status-active',
variant: 'success',
label: I18N_STATUS_ONLINE,
- tooltip: this.timeAgoTooltip(I18N_ONLINE_TIMEAGO_TOOLTIP),
+ tooltip: sprintf(I18N_ONLINE_TOOLTIP, {
+ timeAgo: this.contactedAtTimeAgo,
+ }),
};
case STATUS_NEVER_CONTACTED:
return {
@@ -66,7 +87,10 @@ export default {
icon: 'time-out',
variant: 'muted',
label: I18N_STATUS_OFFLINE,
- tooltip: this.timeAgoTooltip(I18N_OFFLINE_TIMEAGO_TOOLTIP),
+ tooltip: sprintf(I18N_DISCONNECTED_TOOLTIP, {
+ elapsedTime: this.onlineContactTimeoutDuration,
+ timeAgo: this.contactedAtTimeAgo,
+ }),
};
case STATUS_STALE:
return {
@@ -75,19 +99,19 @@ export default {
label: I18N_STATUS_STALE,
// runner may have contacted (or not) and be stale: consider both cases.
tooltip: this.contactedAt
- ? this.timeAgoTooltip(I18N_STALE_TIMEAGO_TOOLTIP)
- : I18N_STALE_NEVER_CONTACTED_TOOLTIP,
+ ? sprintf(I18N_DISCONNECTED_TOOLTIP, {
+ elapsedTime: this.staleTimeoutDuration,
+ timeAgo: this.contactedAtTimeAgo,
+ })
+ : sprintf(I18N_NEVER_CONTACTED_STALE_TOOLTIP, {
+ elapsedTime: this.staleTimeoutDuration,
+ }),
};
default:
return null;
}
},
},
- methods: {
- timeAgoTooltip(text) {
- return sprintf(text, { timeAgo: this.contactedAtTimeAgo });
- },
- },
};
diff --git a/app/assets/javascripts/ci/runner/components/runner_status_popover.vue b/app/assets/javascripts/ci/runner/components/runner_status_popover.vue
deleted file mode 100644
index 06174d39a59..00000000000
--- a/app/assets/javascripts/ci/runner/components/runner_status_popover.vue
+++ /dev/null
@@ -1,75 +0,0 @@
-
-
-
-
- {{ $options.I18N_STATUS_POPOVER_TITLE }}
-
-
- {{ $options.I18N_STATUS_POPOVER_NEVER_CONTACTED }}
-
-
- {{ content }}
-
-
-
-
- {{ $options.I18N_STATUS_POPOVER_ONLINE }}
-
- {{ onlineContactTimeoutDuration }}
-
-
-
- {{ $options.I18N_STATUS_POPOVER_OFFLINE }}
-
- {{ onlineContactTimeoutDuration }}
-
-
-
- {{ $options.I18N_STATUS_POPOVER_STALE }}
-
- {{ staleTimeoutDuration }}
-
-
-
-
diff --git a/app/assets/javascripts/ci/runner/constants.js b/app/assets/javascripts/ci/runner/constants.js
index 571e7ecdf92..db8feda6dbd 100644
--- a/app/assets/javascripts/ci/runner/constants.js
+++ b/app/assets/javascripts/ci/runner/constants.js
@@ -39,41 +39,20 @@ export const I18N_STATUS_STALE = s__('Runners|Stale');
export const I18N_JOB_STATUS_RUNNING = s__('Runners|Running');
export const I18N_JOB_STATUS_IDLE = s__('Runners|Idle');
-// Status help popover
-export const I18N_STATUS_POPOVER_TITLE = s__('Runners|Runner statuses');
-
-export const I18N_STATUS_POPOVER_NEVER_CONTACTED = s__('Runners|Never contacted:');
-export const I18N_STATUS_POPOVER_NEVER_CONTACTED_DESCRIPTION = s__(
- 'Runners|Runner has never contacted GitLab (when you register a runner, use %{codeStart}gitlab-runner run%{codeEnd} to bring it online)',
-);
-export const I18N_STATUS_POPOVER_ONLINE = s__('Runners|Online:');
-export const I18N_STATUS_POPOVER_ONLINE_DESCRIPTION = s__(
- 'Runners|Runner has contacted GitLab within the last %{elapsedTime}',
-);
-export const I18N_STATUS_POPOVER_OFFLINE = s__('Runners|Offline:');
-export const I18N_STATUS_POPOVER_OFFLINE_DESCRIPTION = s__(
- 'Runners|Runner has not contacted GitLab in more than %{elapsedTime}',
-);
-export const I18N_STATUS_POPOVER_STALE = s__('Runners|Stale:');
-export const I18N_STATUS_POPOVER_STALE_DESCRIPTION = s__(
- 'Runners|Runner has not contacted GitLab in more than %{elapsedTime}',
-);
-
// Status tooltips
-export const I18N_ONLINE_TIMEAGO_TOOLTIP = s__(
- 'Runners|Runner is online; last contact was %{timeAgo}',
-);
+export const I18N_ONLINE_TOOLTIP = s__('Runners|Last contact was %{timeAgo}');
export const I18N_NEVER_CONTACTED_TOOLTIP = s__('Runners|Runner has never contacted this instance');
-export const I18N_OFFLINE_TIMEAGO_TOOLTIP = s__(
- 'Runners|Runner is offline; last contact was %{timeAgo}',
+export const I18N_NEVER_CONTACTED_STALE_TOOLTIP = s__(
+ 'Runners|Runner is older than %{elapsedTime} and has never contacted GitLab',
);
-export const I18N_STALE_TIMEAGO_TOOLTIP = s__(
- 'Runners|Runner is stale; last contact was %{timeAgo}',
-);
-export const I18N_STALE_NEVER_CONTACTED_TOOLTIP = s__(
- 'Runners|Runner is stale; it has never contacted this instance',
+export const I18N_DISCONNECTED_TOOLTIP = s__(
+ "Runners|Runner hasn't contacted GitLab in more than %{elapsedTime} and last contact was %{timeAgo}",
);
+// Default online/stale status timeouts, actual values
+export const ONLINE_CONTACT_TIMEOUT_SECS = 2 * 60 * 60; // 2 hours
+export const STALE_TIMEOUT_SECS = 7889238; // Ruby's `3.months`
+
// Registration dropdown
export const I18N_REGISTER_INSTANCE_TYPE = s__('Runners|Register an instance runner');
export const I18N_REGISTER_GROUP_TYPE = s__('Runners|Register a group runner');
diff --git a/app/assets/javascripts/ci/runner/group_new_runner/group_new_runner_app.vue b/app/assets/javascripts/ci/runner/group_new_runner/group_new_runner_app.vue
index 4ca205cfdce..df8d901b32f 100644
--- a/app/assets/javascripts/ci/runner/group_new_runner/group_new_runner_app.vue
+++ b/app/assets/javascripts/ci/runner/group_new_runner/group_new_runner_app.vue
@@ -44,21 +44,21 @@ export default {
};
},
computed: {
- gcpEnabled() {
- return this.glFeatures.gcpRunner;
+ googleCloudProvisioningEnabled() {
+ return this.glFeatures.googleCloudRunnerProvisioning;
},
showCloudForm() {
return (
this.platform === GOOGLE_CLOUD_PLATFORM &&
this.googleCloudStage === GOOGLE_CLOUD_SETUP_START &&
- this.gcpEnabled
+ this.googleCloudProvisioningEnabled
);
},
showCloudFormEnd() {
return (
this.platform === GOOGLE_CLOUD_PLATFORM &&
this.googleCloudStage === GOOGLE_CLOUD_SETUP_END &&
- this.gcpEnabled
+ this.googleCloudProvisioningEnabled
);
},
},
diff --git a/app/assets/javascripts/ci/runner/project_new_runner/project_new_runner_app.vue b/app/assets/javascripts/ci/runner/project_new_runner/project_new_runner_app.vue
index 5ed722fb95c..3577dfd66e5 100644
--- a/app/assets/javascripts/ci/runner/project_new_runner/project_new_runner_app.vue
+++ b/app/assets/javascripts/ci/runner/project_new_runner/project_new_runner_app.vue
@@ -45,21 +45,21 @@ export default {
};
},
computed: {
- gcpEnabled() {
- return this.glFeatures.gcpRunner;
+ googleCloudProvisioningEnabled() {
+ return this.glFeatures.googleCloudRunnerProvisioning;
},
showCloudForm() {
return (
this.platform === GOOGLE_CLOUD_PLATFORM &&
this.googleCloudStage === GOOGLE_CLOUD_SETUP_START &&
- this.gcpEnabled
+ this.googleCloudProvisioningEnabled
);
},
showCloudFormEnd() {
return (
this.platform === GOOGLE_CLOUD_PLATFORM &&
this.googleCloudStage === GOOGLE_CLOUD_SETUP_END &&
- this.gcpEnabled
+ this.googleCloudProvisioningEnabled
);
},
},
diff --git a/app/assets/stylesheets/page_bundles/trial_discover_page.scss b/app/assets/stylesheets/page_bundles/trial_discover_page.scss
new file mode 100644
index 00000000000..bfcbf2b12b1
--- /dev/null
+++ b/app/assets/stylesheets/page_bundles/trial_discover_page.scss
@@ -0,0 +1,16 @@
+@import 'mixins_and_variables_and_functions';
+
+.trial-discover-page-card {
+ @include media-breakpoint-down(md) {
+ .description-text {
+ height: 6em;
+ }
+ }
+}
+
+.trial-discover-page-card.gl-flex-basis-third,
+.trial-discover-page-card.gl-flex-basis-half {
+ @include media-breakpoint-down(md) {
+ flex-basis: 100%;
+ }
+}
diff --git a/app/controllers/groups/runners_controller.rb b/app/controllers/groups/runners_controller.rb
index cb6f837b8e3..b699ca559b1 100644
--- a/app/controllers/groups/runners_controller.rb
+++ b/app/controllers/groups/runners_controller.rb
@@ -7,7 +7,7 @@ class Groups::RunnersController < Groups::ApplicationController
before_action :runner, only: [:edit, :update, :destroy, :pause, :resume, :show, :register]
before_action do
- push_frontend_feature_flag(:gcp_runner, @project, type: :wip)
+ push_frontend_feature_flag(:google_cloud_runner_provisioning, @project, type: :beta)
end
feature_category :runner
diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb
index 01a2d7f04dc..fa5a3c5f23d 100644
--- a/app/controllers/projects/runners_controller.rb
+++ b/app/controllers/projects/runners_controller.rb
@@ -6,7 +6,7 @@ class Projects::RunnersController < Projects::ApplicationController
before_action :runner, only: [:edit, :update, :destroy, :pause, :resume, :show, :register]
before_action do
- push_frontend_feature_flag(:gcp_runner, @project, type: :wip)
+ push_frontend_feature_flag(:google_cloud_runner_provisioning, @project, type: :beta)
end
feature_category :runner
diff --git a/app/finders/personal_projects_finder.rb b/app/finders/personal_projects_finder.rb
index a01465a64d2..3ebc90432b0 100644
--- a/app/finders/personal_projects_finder.rb
+++ b/app/finders/personal_projects_finder.rb
@@ -22,7 +22,7 @@ class PersonalProjectsFinder < UnionFinder
segments = all_projects(current_user)
- find_union(segments, Project).with_namespace.order_updated_desc
+ find_union(segments, Project).with_namespace.sorted_by_activity
end
private
diff --git a/app/models/ci/pipeline_artifact.rb b/app/models/ci/pipeline_artifact.rb
index 05c9535cef1..e0e6906f211 100644
--- a/app/models/ci/pipeline_artifact.rb
+++ b/app/models/ci/pipeline_artifact.rb
@@ -10,9 +10,6 @@ module Ci
include FileStoreMounter
include Lockable
include Presentable
- include SafelyChangeColumnDefault
-
- columns_changing_default :partition_id
FILE_SIZE_LIMIT = 10.megabytes.freeze
EXPIRATION_DATE = 1.week.freeze
diff --git a/app/models/ci/pipeline_chat_data.rb b/app/models/ci/pipeline_chat_data.rb
index 1a2bc37d17d..d4f38b556b7 100644
--- a/app/models/ci/pipeline_chat_data.rb
+++ b/app/models/ci/pipeline_chat_data.rb
@@ -4,9 +4,6 @@ module Ci
class PipelineChatData < Ci::ApplicationRecord
include Ci::Partitionable
include Ci::NamespacedModelName
- include SafelyChangeColumnDefault
-
- columns_changing_default :partition_id
self.table_name = 'ci_pipeline_chat_data'
diff --git a/app/models/ci/pipeline_config.rb b/app/models/ci/pipeline_config.rb
index 8e992aae2c5..11decd3fc66 100644
--- a/app/models/ci/pipeline_config.rb
+++ b/app/models/ci/pipeline_config.rb
@@ -3,9 +3,6 @@
module Ci
class PipelineConfig < Ci::ApplicationRecord
include Ci::Partitionable
- include SafelyChangeColumnDefault
-
- columns_changing_default :partition_id
self.table_name = 'ci_pipelines_config'
self.primary_key = :pipeline_id
diff --git a/app/models/ci/pipeline_metadata.rb b/app/models/ci/pipeline_metadata.rb
index 39e2ef5cebb..21d102374f0 100644
--- a/app/models/ci/pipeline_metadata.rb
+++ b/app/models/ci/pipeline_metadata.rb
@@ -4,9 +4,6 @@ module Ci
class PipelineMetadata < Ci::ApplicationRecord
include Ci::Partitionable
include Importable
- include SafelyChangeColumnDefault
-
- columns_changing_default :partition_id
self.primary_key = :pipeline_id
diff --git a/config/feature_flags/wip/gcp_runner.yml b/config/feature_flags/beta/google_cloud_runner_provisioning.yml
similarity index 58%
rename from config/feature_flags/wip/gcp_runner.yml
rename to config/feature_flags/beta/google_cloud_runner_provisioning.yml
index e98b2508b1e..0f230e908c6 100644
--- a/config/feature_flags/wip/gcp_runner.yml
+++ b/config/feature_flags/beta/google_cloud_runner_provisioning.yml
@@ -1,9 +1,9 @@
---
-name: gcp_runner
+name: google_cloud_runner_provisioning
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/437901
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141521
-rollout_issue_url:
-milestone: '16.9'
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/441627
+milestone: '16.10'
group: group::runner
-type: wip
+type: beta
default_enabled: false
diff --git a/db/migrate/20240131123824_add_admin_cicd_variables_to_member_roles.rb b/db/migrate/20240131123824_add_admin_cicd_variables_to_member_roles.rb
new file mode 100644
index 00000000000..b4daa35e91a
--- /dev/null
+++ b/db/migrate/20240131123824_add_admin_cicd_variables_to_member_roles.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class AddAdminCicdVariablesToMemberRoles < Gitlab::Database::Migration[2.2]
+ milestone '16.9'
+
+ enable_lock_retries!
+
+ def up
+ add_column :member_roles, :admin_cicd_variables, :boolean, default: false, null: false
+ end
+
+ def down
+ remove_column :member_roles, :admin_cicd_variables
+ end
+end
diff --git a/db/post_migrate/20240212092520_ensure_id_uniqueness_for_p_ci_job_artifacts.rb b/db/post_migrate/20240212092520_ensure_id_uniqueness_for_p_ci_job_artifacts.rb
new file mode 100644
index 00000000000..bfa499f56da
--- /dev/null
+++ b/db/post_migrate/20240212092520_ensure_id_uniqueness_for_p_ci_job_artifacts.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+class EnsureIdUniquenessForPCiJobArtifacts < Gitlab::Database::Migration[2.2]
+ include Gitlab::Database::PartitioningMigrationHelpers::UniquenessHelpers
+
+ milestone '16.10'
+ enable_lock_retries!
+
+ TABLE_NAME = :p_ci_job_artifacts
+ FUNCTION_NAME = :assign_p_ci_job_artifacts_id_value
+
+ def up
+ ensure_unique_id(TABLE_NAME)
+ end
+
+ def down
+ execute(<<~SQL.squish)
+ ALTER TABLE #{TABLE_NAME}
+ ALTER COLUMN id SET DEFAULT nextval('ci_job_artifacts_id_seq'::regclass);
+
+ DROP FUNCTION IF EXISTS #{FUNCTION_NAME} CASCADE;
+ SQL
+ end
+end
diff --git a/db/schema_migrations/20240131123824 b/db/schema_migrations/20240131123824
new file mode 100644
index 00000000000..2f0ca9bd23f
--- /dev/null
+++ b/db/schema_migrations/20240131123824
@@ -0,0 +1 @@
+0720a5a4ff3738f1ce591ff7ada103b94ed584614757521d448b5cdb1fc983be
\ No newline at end of file
diff --git a/db/schema_migrations/20240212092520 b/db/schema_migrations/20240212092520
new file mode 100644
index 00000000000..3dbc7397800
--- /dev/null
+++ b/db/schema_migrations/20240212092520
@@ -0,0 +1 @@
+50bf364d065407160eff306427eec8a7d6dd15e116a7671ff811f81aef0fa624
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 300a7325362..c5b3fdabf18 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -23,6 +23,19 @@ RETURN NEW;
END
$$;
+CREATE FUNCTION assign_p_ci_job_artifacts_id_value() RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+IF NEW."id" IS NOT NULL THEN
+ RAISE WARNING 'Manually assigning ids is not allowed, the value will be ignored';
+END IF;
+NEW."id" := nextval('ci_job_artifacts_id_seq'::regclass);
+RETURN NEW;
+
+END
+$$;
+
CREATE FUNCTION assign_p_ci_pipeline_variables_id_value() RETURNS trigger
LANGUAGE plpgsql
AS $$
@@ -14526,15 +14539,6 @@ CREATE TABLE p_ci_job_artifacts (
)
PARTITION BY LIST (partition_id);
-CREATE SEQUENCE ci_job_artifacts_id_seq
- START WITH 1
- INCREMENT BY 1
- NO MINVALUE
- NO MAXVALUE
- CACHE 1;
-
-ALTER SEQUENCE ci_job_artifacts_id_seq OWNED BY p_ci_job_artifacts.id;
-
CREATE TABLE ci_job_artifacts (
project_id integer NOT NULL,
file_type integer NOT NULL,
@@ -14547,7 +14551,7 @@ CREATE TABLE ci_job_artifacts (
file_sha256 bytea,
file_format smallint,
file_location smallint,
- id bigint DEFAULT nextval('ci_job_artifacts_id_seq'::regclass) NOT NULL,
+ id bigint NOT NULL,
job_id bigint NOT NULL,
locked smallint DEFAULT 2,
partition_id bigint NOT NULL,
@@ -14557,6 +14561,15 @@ CREATE TABLE ci_job_artifacts (
CONSTRAINT check_9f04410cf4 CHECK ((char_length(file_final_path) <= 1024))
);
+CREATE SEQUENCE ci_job_artifacts_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE ci_job_artifacts_id_seq OWNED BY p_ci_job_artifacts.id;
+
CREATE TABLE ci_job_token_group_scope_links (
id bigint NOT NULL,
source_project_id bigint NOT NULL,
@@ -19181,6 +19194,7 @@ CREATE TABLE member_roles (
manage_group_access_tokens boolean DEFAULT false NOT NULL,
remove_project boolean DEFAULT false NOT NULL,
admin_terraform_state boolean DEFAULT false NOT NULL,
+ admin_cicd_variables boolean DEFAULT false NOT NULL,
CONSTRAINT check_4364846f58 CHECK ((char_length(description) <= 255)),
CONSTRAINT check_9907916995 CHECK ((char_length(name) <= 255))
);
@@ -27747,8 +27761,6 @@ ALTER TABLE ONLY p_ci_builds_metadata ALTER COLUMN id SET DEFAULT nextval('ci_bu
ALTER TABLE ONLY p_ci_job_annotations ALTER COLUMN id SET DEFAULT nextval('p_ci_job_annotations_id_seq'::regclass);
-ALTER TABLE ONLY p_ci_job_artifacts ALTER COLUMN id SET DEFAULT nextval('ci_job_artifacts_id_seq'::regclass);
-
ALTER TABLE ONLY packages_build_infos ALTER COLUMN id SET DEFAULT nextval('packages_build_infos_id_seq'::regclass);
ALTER TABLE ONLY packages_composer_cache_files ALTER COLUMN id SET DEFAULT nextval('packages_composer_cache_files_id_seq'::regclass);
@@ -38377,6 +38389,8 @@ ALTER INDEX p_ci_builds_token_encrypted_partition_id_idx ATTACH PARTITION unique
CREATE TRIGGER assign_p_ci_builds_id_trigger BEFORE INSERT ON p_ci_builds FOR EACH ROW EXECUTE FUNCTION assign_p_ci_builds_id_value();
+CREATE TRIGGER assign_p_ci_job_artifacts_id_trigger BEFORE INSERT ON p_ci_job_artifacts FOR EACH ROW EXECUTE FUNCTION assign_p_ci_job_artifacts_id_value();
+
CREATE TRIGGER assign_p_ci_pipeline_variables_id_trigger BEFORE INSERT ON p_ci_pipeline_variables FOR EACH ROW EXECUTE FUNCTION assign_p_ci_pipeline_variables_id_value();
CREATE TRIGGER chat_names_loose_fk_trigger AFTER DELETE ON chat_names REFERENCING OLD TABLE AS old_table FOR EACH STATEMENT EXECUTE FUNCTION insert_into_loose_foreign_keys_deleted_records();
diff --git a/doc/administration/settings/jira_cloud_app_troubleshooting.md b/doc/administration/settings/jira_cloud_app_troubleshooting.md
index 3ce9ad70adb..cccd6e9654a 100644
--- a/doc/administration/settings/jira_cloud_app_troubleshooting.md
+++ b/doc/administration/settings/jira_cloud_app_troubleshooting.md
@@ -61,7 +61,7 @@ If the issue persists, verify that your self-managed GitLab instance can connect
To test connectivity, run the following command:
```shell
-# A `404` status code is expected because you're not passing a token
+# A `404 Not Found` is expected because you're not passing a token
curl --head "https://connect-install-keys.atlassian.com"
```
@@ -77,7 +77,7 @@ To resolve this issue on your self-managed GitLab instance:
- GitLab.com (if you [installed the app from the official Atlassian Marketplace listing](jira_cloud_app.md#connect-the-gitlab-for-jira-cloud-app)).
- Jira Cloud (if you [installed the app manually](jira_cloud_app.md#install-the-gitlab-for-jira-cloud-app-manually)).
- Ensure the token request sent to the `/-/jira_connect/events/installed` endpoint when you install the app is accessible from Jira.
- The following `curl` command must return a `401` status code:
+ The following command should return a `401 Unauthorized`:
```shell
curl --include --request POST "https://gitlab.example.com/-/jira_connect/events/installed"
@@ -194,8 +194,8 @@ Each `GET` request to the Jira Connect Proxy URL `https://gitlab.com/-/jira_conn
For the first log:
-- `json.status` is `422`.
-- `json.params.value` should match the GitLab self-managed URL `[[FILTERED], {"instance_url"=>"https://gitlab.example.com"}]`.
+- `json.status` is `422 Unprocessable Entity`.
+- `json.params.value` should match the self-managed GitLab URL `[[FILTERED], {"instance_url"=>"https://gitlab.example.com"}]`.
For the second log, you might have one of the following scenarios:
@@ -203,8 +203,8 @@ For the second log, you might have one of the following scenarios:
- `json.message`, `json.jira_status_code`, and `json.jira_body` are present.
- `json.message` is `Proxy lifecycle event received error response` or similar.
- `json.jira_status_code` and `json.jira_body` might contain the response received from the self-managed instance or a proxy in front of the instance.
- - If `json.jira_status_code` is `401` and `json.jira_body` is empty, [**Jira Connect Proxy URL**](jira_cloud_app.md#set-up-your-instance) might not be set to
- `https://gitlab.com`.
+ - If `json.jira_status_code` is `401 Unauthorized` and `json.jira_body` is empty,
+ [**Jira Connect Proxy URL**](jira_cloud_app.md#set-up-your-instance) might not be set to `https://gitlab.com`.
- Scenario 2:
- `json.exception.class` and `json.exception.message` are present.
- `json.exception.class` and `json.exception.message` contain whether an issue occurred while contacting the self-managed instance.
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 7f8d2dc4277..4830e4850c2 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -26062,7 +26062,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
##### `Project.runnerCloudProvisioningOptions`
-Options for runner cloud provisioning by a specified cloud provider. Returns `null` if `:gcp_runner` feature flag is disabled, or the GitLab instance is not a SaaS instance.
+Options for runner cloud provisioning by a specified cloud provider. Returns `null` if `:google_cloud_runner_provisioning` feature flag is disabled, or the GitLab instance is not a SaaS instance.
NOTE:
**Introduced** in 16.9.
@@ -31518,6 +31518,7 @@ Member role permission.
| Value | Description |
| ----- | ----------- |
+| `ADMIN_CICD_VARIABLES` | Allows to admin CI/CD variables. |
| `ADMIN_GROUP_MEMBER` | Allows admin of group members. |
| `ADMIN_MERGE_REQUEST` | Allows approval of merge requests. |
| `ADMIN_TERRAFORM_STATE` | Allows to admin terraform state. |
diff --git a/doc/api/lint.md b/doc/api/lint.md
index e4115024de5..c3640cff1f0 100644
--- a/doc/api/lint.md
+++ b/doc/api/lint.md
@@ -61,9 +61,10 @@ Example responses:
## Validate a project's CI configuration
> - `sha` attribute [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/369212) in GitLab 16.5.
+> - `sha` and `ref` [renamed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/143098) to `content_ref` and `dry_run_ref` in GitLab 16.10.
-Checks if a project’s `.gitlab-ci.yml` configuration in a given ref (the
-`sha` parameter, by default `HEAD` of the project’s default branch) is valid.
+Checks if a project's `.gitlab-ci.yml` configuration in a given ref (the
+`content_ref` parameter, by default `HEAD` of the project's default branch) is valid.
This endpoint uses all namespace specific data available, including variables
and local includes.
@@ -73,10 +74,12 @@ GET /projects/:id/ci/lint
| Attribute | Type | Required | Description |
|----------------|---------|----------|-------------|
+| `content_ref` | string | No | The CI/CD configuration content is taken from this commit SHA, branch or tag. Defaults to the SHA of the head of the project's default branch when not set. |
+| `dry_run_ref` | string | No | When `dry_run` is `true`, sets the branch or tag context to use to validate the CI/CD YAML configuration. Defaults to the project's default branch when not set. |
| `dry_run` | boolean | No | Run pipeline creation simulation, or only do static check. |
| `include_jobs` | boolean | No | If the list of jobs that would exist in a static check or pipeline simulation should be included in the response. Default: `false`. |
-| `ref` | string | No | When `dry_run` is `true`, sets the branch or tag context to use to validate the CI/CD YAML configuration. Defaults to the project's default branch when not set. |
-| `sha` | string | No | The CI/CD configuration content is taken from this commit SHA, branch or tag. Defaults to the SHA of the head of the project's default branch when not set. |
+| `ref` | string | No | (Deprecated) When `dry_run` is `true`, sets the branch or tag context to use to validate the CI/CD YAML configuration. Defaults to the project's default branch when not set. Use `dry_run_ref` instead. |
+| `sha` | string | No | (Deprecated) The CI/CD configuration content is taken from this commit SHA, branch or tag. Defaults to the SHA of the head of the project's default branch when not set. Use `content_ref` instead. |
Example request:
diff --git a/doc/api/merge_request_approvals.md b/doc/api/merge_request_approvals.md
index 6463c428a2f..af472a1166d 100644
--- a/doc/api/merge_request_approvals.md
+++ b/doc/api/merge_request_approvals.md
@@ -33,7 +33,7 @@ Group approval rules apply to all protected branches of projects belonging to th
### Create group-level approval rules
-Users with at least the Maintainer role can create group level approval rules using the following endpoint:
+Group admins can create group level approval rules using the following endpoint:
```plaintext
POST /groups/:id/approval_rules
diff --git a/doc/integration/jira/connect-app.md b/doc/integration/jira/connect-app.md
index 8ddc0f5af9a..a1b259ed659 100644
--- a/doc/integration/jira/connect-app.md
+++ b/doc/integration/jira/connect-app.md
@@ -126,5 +126,5 @@ Failed to link group. Please try again.
A `403 Forbidden` is returned if the user information cannot be fetched from Jira because of insufficient permissions.
-To resolve this issue, ensure the Jira user that installs and configures the app meets certain
-[requirements](../../administration/settings/jira_cloud_app.md#jira-user-requirements).
+To resolve this issue, ensure you meet certain
+[Jira user requirements](../../administration/settings/jira_cloud_app.md#jira-user-requirements).
diff --git a/doc/solutions/cloud/aws/gitlab_aws_integration.md b/doc/solutions/cloud/aws/gitlab_aws_integration.md
index 90263385fc8..8a197c35c07 100644
--- a/doc/solutions/cloud/aws/gitlab_aws_integration.md
+++ b/doc/solutions/cloud/aws/gitlab_aws_integration.md
@@ -46,7 +46,6 @@ AWS Services that are supported directly by a CodeStar Connection in an AWS acco
- **Amazon CodeWhisperer Customization Capability** ([12/28/2023](https://aws.amazon.com/about-aws/whats-new/2023/12/codepipeline-gitlab-self-managed/)) [can connect to a GitLab repository](https://aws.amazon.com/blogs/aws/new-customization-capability-in-amazon-codewhisperer-generates-even-better-suggestions-preview/). `[AWS Built]`
- **AWS Service Catalog** directly inherits CodeStar Connections, there is not any specific documentation about GitLab because it just uses any GitLab CodeStar Connection that has been created in the account. ([12/28/2023](https://aws.amazon.com/about-aws/whats-new/2023/12/codepipeline-gitlab-self-managed/)) `[AWS Built]`
- **AWS Proton** directly inherits CodeStar Connections, there is not any specific documentation about GitLab since it just uses any GitLab CodeStar Connection that has been created in the account. ([12/28/2023](https://aws.amazon.com/about-aws/whats-new/2023/12/codepipeline-gitlab-self-managed/)) `[AWS Built]`
-- **AWS Glue Notebook Jobs** directly inherit CodeStar Connections, there is not any specific documentation about GitLab because it just uses any GitLab CodeStar Connection that has been created in the account. ([12/28/2023](https://aws.amazon.com/about-aws/whats-new/2023/12/codepipeline-gitlab-self-managed/)) `[AWS Built]`
Documentation and References:
@@ -77,6 +76,7 @@ Documentation and References:
- **Amazon SageMaker Notebooks** [allow Git repositories to be specified by the Git clone URL](https://docs.aws.amazon.com/sagemaker/latest/dg/nbi-git-resource.html) and configuration of a secret - so GitLab is configurable. ([12/28/2023](https://aws.amazon.com/about-aws/whats-new/2023/12/codepipeline-gitlab-self-managed/)) `[AWS Configuration]`
- **AWS Amplify** - [uses a Git integration mechanism designed by the AWS Amplify team](https://docs.aws.amazon.com/amplify/latest/userguide/getting-started.html). `[AWS Built]`
+- **AWS Glue Notebook Jobs** support for GitLab respository URL with Personal Access Token (PAT) authentication at the "job" level. ([10/03/2022](https://aws.amazon.com/about-aws/whats-new/2022/10/aws-glue-git-integration/)) [AWS Docs for GitLab Configuration](https://docs.aws.amazon.com/glue/latest/dg/edit-job-add-source-control-integration.html) `[AWS Built]`
#### Other SCM Integration Options
diff --git a/doc/solutions/cloud/aws/gitlab_single_box_on_aws.md b/doc/solutions/cloud/aws/gitlab_single_box_on_aws.md
index 867f038fc34..aedfdf8aa2c 100644
--- a/doc/solutions/cloud/aws/gitlab_single_box_on_aws.md
+++ b/doc/solutions/cloud/aws/gitlab_single_box_on_aws.md
@@ -42,8 +42,8 @@ Because any given GitLab upgrade might involve data disk updates or database sch
1. Log in to the AWS Web Console, so that selecting the links in the following step take you directly to the AMI list.
1. Pick the edition you want:
- - [GitLab Enterprise Edition](https://console.aws.amazon.com/ec2/v2/home?region=us-east-1#Images:visibility=public-images;ownerAlias=782774275127;search=GitLab%20EE;sort=desc:name): If you want to unlock the enterprise features, a license is needed.
- - [GitLab Community Edition](https://console.aws.amazon.com/ec2/v2/home?region=us-east-1#Images:visibility=public-images;ownerAlias=782774275127;search=GitLab%20CE;sort=desc:name): The open source version of GitLab.
+ - [GitLab Enterprise Edition](https://console.aws.amazon.com/ec2/v2/home?region=us-east-1#Images:visibility=public-images;owner=782774275127;search=GitLab%20EE;sort=desc:name): If you want to unlock the enterprise features, a license is needed.
+ - [GitLab Community Edition](https://console.aws.amazon.com/ec2/v2/home?region=us-east-1#Images:visibility=public-images;owner=782774275127;search=GitLab%20CE;sort=desc:name): The open source version of GitLab.
- [GitLab Premium or Ultimate Marketplace (pre-licensed)](https://console.aws.amazon.com/ec2/v2/home?region=us-east-1#Images:visibility=public-images;source=Marketplace;search=GitLab%20EE;sort=desc:name): 5 user license built into per-minute billing.
1. AMI IDs are unique per region. After you've loaded any of these editions, in the upper-right corner, select the desired target region of the console to see the appropriate AMIs.
diff --git a/doc/tutorials/configure_gitlab_runner_to_use_gke/index.md b/doc/tutorials/configure_gitlab_runner_to_use_gke/index.md
index 77679015f69..8b1cfaf2cbd 100644
--- a/doc/tutorials/configure_gitlab_runner_to_use_gke/index.md
+++ b/doc/tutorials/configure_gitlab_runner_to_use_gke/index.md
@@ -71,7 +71,7 @@ This step describes how to create a cluster and connect to it. After you connect
Now that you have a cluster, you're ready to install and configure the Kubernetes Operator.
-1. Install the prerequisites:
+1. Install `cert-manager`. Skip this step if you already have a certificate manager installed:
```shell
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.7.1/cert-manager.yaml
@@ -85,19 +85,92 @@ Now that you have a cluster, you're ready to install and configure the Kubernete
| bash -s v0.24.0
```
-1. Install the Kubernetes Operator Catalog:
-
- ```shell
- kubectl create -f https://raw.githubusercontent.com/operator-framework/operator-lifecycle-manager/master/deploy/upstream/quickstart/crds.yaml
- kubectl create -f https://raw.githubusercontent.com/operator-framework/operator-lifecycle-manager/master/deploy/upstream/quickstart/olm.yaml
- ```
-
1. Install the Kubernetes Operator:
```shell
kubectl create -f https://operatorhub.io/install/gitlab-runner-operator.yaml
```
+1. Operator Lifecycle Manager v0.25.0 and later only. Add your own certificate manager or use `cert-manager`.
+
+ - To add your own certificate provider:
+
+ 1. In the `gitlab-runner-operator.yaml`, define the certificate namespace and certificate name in the `env` setting:
+
+ ```shell
+ cat > gitlab-runner-operator.yaml << EOF
+ apiVersion: operators.coreos.com/v1alpha1
+ kind: Subscription
+ metadata:
+ name: gitlab-runner-operator
+ namespace: gitlab-ns
+ spec:
+ channel: stable
+ name: gitlab-runner-operator
+ source: operatorhubio-catalog
+ ca: webhook-server-cert
+ sourceNamespace: olm
+ config:
+ env:
+ - name: CERTIFICATE_NAMESPACE
+ value: cert_namespace_desired_value
+ - name: CERTIFICATE_NAME
+ value: cert_name_desired_value
+ EOF
+ ```
+
+ 1. Apply the `gitlab-runner-operator.yaml` to the Kubernetes cluster:
+
+ ```shell
+ kubectl apply -f gitlab-runner-operator.yaml
+ ```
+
+ - To use the `cert-manager`:
+
+ 1. Use the `certificate-issuer-install.yaml` to install a `Certificate` and `Issuer` in the default namespace, in addition
+ to the operator installation:
+
+ ```shell
+ cat > certificate-issuer-install.yaml << EOF
+ apiVersion: v1
+ kind: Namespace
+ metadata:
+ labels:
+ app.kubernetes.io/component: controller-manager
+ app.kubernetes.io/managed-by: olm
+ app.kubernetes.io/name: gitlab-runner-operator
+ name: gitlab-runner-system
+ ---
+ apiVersion: cert-manager.io/v1
+ kind: Certificate
+ metadata:
+ name: gitlab-runner-serving-cert
+ namespace: gitlab-runner-system
+ spec:
+ dnsNames:
+ - gitlab-runner-webhook-service.gitlab-runner-system.svc
+ - gitlab-runner-webhook-service.gitlab-runner-system.svc.cluster.local
+ issuerRef:
+ kind: Issuer
+ name: gitlab-runner-selfsigned-issuer
+ secretName: webhook-server-cert
+ ---
+ apiVersion: cert-manager.io/v1
+ kind: Issuer
+ metadata:
+ name: gitlab-runner-selfsigned-issuer
+ namespace: gitlab-runner-system
+ spec:
+ selfSigned: {}
+ EOF
+ ```
+
+ 1. Apply the `certificate-issuer-install.yaml` to the Kubernetes cluster:
+
+ ```shell
+ kubectl create -f certificate-issuer-install.yaml
+ ```
+
1. Create a secret that contains the `runner-registration-token` from your
GitLab project:
@@ -109,7 +182,7 @@ Now that you have a cluster, you're ready to install and configure the Kubernete
name: gitlab-runner-secret
type: Opaque
stringData:
- runner-registration-token: YOUR_RUNNER_REGISTRATION_TOKEN
+ runner-token: YOUR_RUNNER_AUTHENTICATION_TOKEN
EOF
```
diff --git a/doc/user/application_security/dependency_list/index.md b/doc/user/application_security/dependency_list/index.md
index 19c99f59c34..1ce69e9fa30 100644
--- a/doc/user/application_security/dependency_list/index.md
+++ b/doc/user/application_security/dependency_list/index.md
@@ -68,10 +68,11 @@ Details of each dependency are listed, sorted by decreasing severity of vulnerab
## Filter dependency list
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/422356) in GitLab 16.7 [with a flag](../../../administration/feature_flags.md) named `group_level_dependencies_filtering`. Disabled by default.
+> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/424727) in GitLab 16.10.
FLAG:
-On self-managed GitLab, by default this feature is not available. To make it available, an administrator can [enable the feature flag](../../../administration/feature_flags.md) named `group_level_dependencies_filtering`.
-On GitLab.com, this feature is available but can be configured by GitLab.com administrators only.
+On self-managed GitLab, by default this feature is available. To hide the feature, an administrator can [disable the feature flag](../../../administration/feature_flags.md) named `group_level_dependencies_filtering`.
+On GitLab.com, this feature is available.
In the group-level dependency list you can filter by:
diff --git a/lib/api/lint.rb b/lib/api/lint.rb
index b2f0f54e380..39016f38631 100644
--- a/lib/api/lint.rb
+++ b/lib/api/lint.rb
@@ -15,24 +15,32 @@ module API
]
end
params do
- optional :sha, type: String, desc: 'The commit hash or name of a repository branch or tag. Defaults to the HEAD of the project’s default branch'
+ optional :sha, type: String, desc: 'Deprecated: Use content_ref instead'
+ optional :content_ref, type: String, desc: "The CI/CD configuration content is taken from this commit SHA, branch or tag. Defaults to the HEAD of the project's default branch"
+ mutually_exclusive :sha, :content_ref
+
optional :dry_run, type: Boolean, default: false, desc: 'Run pipeline creation simulation, or only do static check. This is false by default'
optional :include_jobs, type: Boolean, desc: 'If the list of jobs that would exist in a static check or pipeline
simulation should be included in the response. This is false by default'
- optional :ref, type: String, desc: 'Branch or tag used to execute a dry run. Defaults to the default branch of the project. Only used when dry_run is true'
+
+ optional :ref, type: String, desc: 'Deprecated: Use dry_run_ref instead'
+ optional :dry_run_ref, type: String, desc: 'Branch or tag used as context when executing a dry run. Defaults to the default branch of the project. Only used when dry_run is true'
+ mutually_exclusive :ref, :dry_run_ref
end
get ':id/ci/lint', urgency: :low do
authorize_read_code!
- sha = params[:sha] || user_project.repository.root_ref_sha
+ content_ref = params[:content_ref] || params[:sha] || user_project.repository.root_ref_sha
+ dry_run_ref = params[:dry_run_ref] || params[:ref] || user_project.default_branch
- not_found! 'Commit' unless user_project.commit(sha).present?
+ commit = user_project.commit(content_ref)
+ not_found! 'Commit' unless commit.present?
- content = user_project.repository.blob_data_at(sha, user_project.ci_config_path_or_default)
+ content = user_project.repository.blob_data_at(commit.sha, user_project.ci_config_path_or_default)
result = Gitlab::Ci::Lint
- .new(project: user_project, current_user: current_user, sha: sha)
- .validate(content, dry_run: params[:dry_run], ref: params[:ref] || user_project.default_branch)
+ .new(project: user_project, current_user: current_user, sha: commit.sha)
+ .validate(content, dry_run: params[:dry_run], ref: dry_run_ref)
present result, with: Entities::Ci::Lint::Result, current_user: current_user, include_jobs: params[:include_jobs]
end
diff --git a/lib/gitlab/git/diff_tree.rb b/lib/gitlab/git/diff_tree.rb
index a8f01078be7..34a32b65b00 100644
--- a/lib/gitlab/git/diff_tree.rb
+++ b/lib/gitlab/git/diff_tree.rb
@@ -11,20 +11,6 @@ module Gitlab
@left_tree_id = left_tree_id
@right_tree_id = right_tree_id
end
-
- def self.from_commit(commit)
- return unless commit.tree_id
-
- parent_tree_id =
- if commit.parent_ids.blank?
- Gitlab::Git::SHA1_EMPTY_TREE_ID
- else
- parent_id = commit.parent_ids.first
- commit.repository.commit(parent_id).tree_id
- end
-
- new(parent_tree_id, commit.tree_id)
- end
end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 2c617b32eb9..339cbd2f670 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -42651,6 +42651,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact was %{timeAgo}"
+msgstr ""
+
msgid "Runners|Last contact: %{timeAgo}"
msgstr ""
@@ -42702,9 +42705,6 @@ msgstr ""
msgid "Runners|Never contacted"
msgstr ""
-msgid "Runners|Never contacted:"
-msgstr ""
-
msgid "Runners|Never expires"
msgstr ""
@@ -42759,15 +42759,9 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline:"
-msgstr ""
-
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online:"
-msgstr ""
-
msgid "Runners|Only administrators can view this."
msgstr ""
@@ -42901,16 +42895,10 @@ msgstr ""
msgid "Runners|Runner description"
msgstr ""
-msgid "Runners|Runner has contacted GitLab within the last %{elapsedTime}"
-msgstr ""
-
-msgid "Runners|Runner has never contacted GitLab (when you register a runner, use %{codeStart}gitlab-runner run%{codeEnd} to bring it online)"
-msgstr ""
-
msgid "Runners|Runner has never contacted this instance"
msgstr ""
-msgid "Runners|Runner has not contacted GitLab in more than %{elapsedTime}"
+msgid "Runners|Runner hasn't contacted GitLab in more than %{elapsedTime} and last contact was %{timeAgo}"
msgstr ""
msgid "Runners|Runner is locked and available for currently assigned projects only. Only administrators can change the assigned projects."
@@ -42919,33 +42907,24 @@ msgstr ""
msgid "Runners|Runner is offline; last contact was %{runner_contact} ago"
msgstr ""
-msgid "Runners|Runner is offline; last contact was %{timeAgo}"
+msgid "Runners|Runner is older than %{elapsedTime} and has never contacted GitLab"
msgstr ""
msgid "Runners|Runner is online; last contact was %{runner_contact} ago"
msgstr ""
-msgid "Runners|Runner is online; last contact was %{timeAgo}"
-msgstr ""
-
msgid "Runners|Runner is stale; it has never contacted this instance"
msgstr ""
msgid "Runners|Runner is stale; last contact was %{runner_contact} ago"
msgstr ""
-msgid "Runners|Runner is stale; last contact was %{timeAgo}"
-msgstr ""
-
msgid "Runners|Runner performance insights"
msgstr ""
msgid "Runners|Runner registration"
msgstr ""
-msgid "Runners|Runner statuses"
-msgstr ""
-
msgid "Runners|Runner unassigned from project."
msgstr ""
@@ -43024,9 +43003,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale:"
-msgstr ""
-
msgid "Runners|Status"
msgstr ""
@@ -52431,15 +52407,114 @@ msgstr ""
msgid "Trending"
msgstr ""
+msgid "TrialDiscoverPage|A single application eliminates complex integrations, data checkpoints, and toolchain maintenance, resulting in greater productivity and lower cost."
+msgstr ""
+
+msgid "TrialDiscoverPage|Access advanced features"
+msgstr ""
+
msgid "TrialDiscoverPage|Access advanced features, build more efficiently, strengthen security and compliance."
msgstr ""
+msgid "TrialDiscoverPage|Break down silos to coordinate seamlessly across development, operations, and security with a consistent experience across the development lifecycle."
+msgstr ""
+
+msgid "TrialDiscoverPage|Burn down charts"
+msgstr ""
+
+msgid "TrialDiscoverPage|Calculate seats"
+msgstr ""
+
+msgid "TrialDiscoverPage|Code owners"
+msgstr ""
+
+msgid "TrialDiscoverPage|Code review analytics"
+msgstr ""
+
+msgid "TrialDiscoverPage|Collaborate on high-level ideas that share a common theme. Use epics to group issues that cross milestones and projects."
+msgstr ""
+
+msgid "TrialDiscoverPage|Collaboration made easy"
+msgstr ""
+
+msgid "TrialDiscoverPage|Compare all plans"
+msgstr ""
+
+msgid "TrialDiscoverPage|Create a more advanced workflow for issues, merge requests, and epics by using scoped, mutually exclusive labels."
+msgstr ""
+
+msgid "TrialDiscoverPage|Dependency scanning"
+msgstr ""
+
msgid "TrialDiscoverPage|Discover"
msgstr ""
msgid "TrialDiscoverPage|Discover Premium & Ultimate"
msgstr ""
+msgid "TrialDiscoverPage|Documentation"
+msgstr ""
+
+msgid "TrialDiscoverPage|Dynamic application security testing (DAST)"
+msgstr ""
+
+msgid "TrialDiscoverPage|Epics"
+msgstr ""
+
+msgid "TrialDiscoverPage|Find and fix bottlenecks in your code review process by understanding how long open merge requests have been in review."
+msgstr ""
+
+msgid "TrialDiscoverPage|Free guest users"
+msgstr ""
+
+msgid "TrialDiscoverPage|GitLab is infrastructure agnostic. GitLab supports GCP, AWS, OpenShift, VMware, on-premises, bare metal, and more."
+msgstr ""
+
+msgid "TrialDiscoverPage|Keep your application secure by checking your deployed environments for vulnerabilities."
+msgstr ""
+
+msgid "TrialDiscoverPage|Keep your application secure by checking your libraries for vulnerabilities."
+msgstr ""
+
+msgid "TrialDiscoverPage|Let users view what GitLab has to offer without using a subscription seat."
+msgstr ""
+
+msgid "TrialDiscoverPage|Lower cost of development"
+msgstr ""
+
+msgid "TrialDiscoverPage|Maintain high quality code by requiring approval from specific users on your merge requests."
+msgstr ""
+
+msgid "TrialDiscoverPage|Merge request approval rule"
+msgstr ""
+
+msgid "TrialDiscoverPage|Premium"
+msgstr ""
+
+msgid "TrialDiscoverPage|Roadmaps"
+msgstr ""
+
+msgid "TrialDiscoverPage|Scoped Labels"
+msgstr ""
+
+msgid "TrialDiscoverPage|Speed. Efficiency. Trust."
+msgstr ""
+
+msgid "TrialDiscoverPage|Target the right approvers for your merge request by assigning owners to specific files."
+msgstr ""
+
+msgid "TrialDiscoverPage|Track your development progress by viewing issues in a burndown chart."
+msgstr ""
+
+msgid "TrialDiscoverPage|Ultimate"
+msgstr ""
+
+msgid "TrialDiscoverPage|Visualize your epics and milestones in a timeline."
+msgstr ""
+
+msgid "TrialDiscoverPage|Your software, deployed your way"
+msgstr ""
+
msgid "TrialRegistration|Start GitLab Ultimate free trial"
msgstr ""
diff --git a/package.json b/package.json
index 7c546ea74fe..d4998f53e06 100644
--- a/package.json
+++ b/package.json
@@ -63,7 +63,7 @@
"@gitlab/svgs": "3.83.0",
"@gitlab/ui": "^74.4.0",
"@gitlab/visual-review-tools": "1.7.3",
- "@gitlab/web-ide": "^0.0.1-dev-20240208022507",
+ "@gitlab/web-ide": "^0.0.1-dev-20240214084918",
"@mattiasbuelens/web-streams-adapter": "^0.1.0",
"@rails/actioncable": "7.0.8",
"@rails/ujs": "7.0.8",
diff --git a/qa/gdk/Dockerfile.gdk b/qa/gdk/Dockerfile.gdk
index 6cc2c6a3ed4..0177b649e7e 100644
--- a/qa/gdk/Dockerfile.gdk
+++ b/qa/gdk/Dockerfile.gdk
@@ -1,6 +1,8 @@
ARG GDK_SHA=fb96a94cf54d4b0c5db2a3bdf905874dbbc1cf67
+# 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
-FROM registry.gitlab.com/gitlab-org/gitlab-development-kit/asdf-bootstrapped-verify/main:${GDK_SHA} as base
+FROM registry.gitlab.com/gitlab-org/gitlab-development-kit/asdf-bootstrapped-verify/main:${GDK_BASE_TAG_PREFIX}${GDK_SHA} as base
ENV GITLAB_LICENSE_MODE=test \
GDK_KILL_CONFIRM=true
diff --git a/scripts/build_gdk_image b/scripts/build_gdk_image
index 3401c8df86c..58b1ef6fd05 100755
--- a/scripts/build_gdk_image
+++ b/scripts/build_gdk_image
@@ -7,6 +7,7 @@ source "$(dirname "$0")/utils.sh"
REGISTRY="${CI_REGISTRY}/${CI_PROJECT_PATH}"
SHA_TAG="${CI_COMMIT_SHA}"
BRANCH_TAG="${CI_COMMIT_REF_SLUG}"
+STABLE_BRANCH_PATTERN="^[\d-]+-stable-ee$"
IMAGE="${REGISTRY}/gitlab-qa-gdk"
@@ -16,6 +17,10 @@ else
OUTPUT_OPTION="--load"
fi
+if [[ "$BRANCH_TAG" =~ $STABLE_BRANCH_PATTERN || "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" =~ $STABLE_BRANCH_PATTERN ]]; then
+ GDK_BASE_TAG_PREFIX="stable-"
+fi
+
echoinfo "Building GDK image" "yes"
docker buildx build \
@@ -24,6 +29,7 @@ docker buildx build \
--cache-from="${IMAGE}/cache:master" \
--file="qa/gdk/Dockerfile.gdk" \
--platform=${ARCH:-amd64} \
+ --build-arg "GDK_BASE_TAG_PREFIX=${GDK_BASE_TAG_PREFIX}" \
--tag="${IMAGE}:${SHA_TAG}" \
--tag="${IMAGE}:${BRANCH_TAG}" \
--provenance=false \
diff --git a/spec/dot_gitlab_ci/rules_spec.rb b/spec/dot_gitlab_ci/rules_spec.rb
index fe82fdca403..6e30cbd2110 100644
--- a/spec/dot_gitlab_ci/rules_spec.rb
+++ b/spec/dot_gitlab_ci/rules_spec.rb
@@ -144,10 +144,12 @@ RSpec.describe '.gitlab/ci/rules.gitlab-ci.yml', feature_category: :tooling do
patterns_lists = config.filter_map do |name, patterns|
next unless name.start_with?('.')
next unless name.end_with?('patterns')
- # Ignore EE-only patterns list when in FOSS context
- next if foss_context && patterns.all? { |pattern| pattern =~ %r|{?ee/| }
- PatternsList.new(name, patterns)
+ # Ignore EE-only patterns list when in FOSS context
+ relevant_patterns = foss_context ? patterns.reject { |pattern| pattern =~ %r|^{?ee/| } : patterns
+ next if relevant_patterns.empty?
+
+ PatternsList.new(name, relevant_patterns)
end
# One loop to gather a { pattern => files } hash
diff --git a/spec/features/users/user_browses_projects_on_user_page_spec.rb b/spec/features/users/user_browses_projects_on_user_page_spec.rb
index 039b1bbe5b1..addf9bbdcb1 100644
--- a/spec/features/users/user_browses_projects_on_user_page_spec.rb
+++ b/spec/features/users/user_browses_projects_on_user_page_spec.rb
@@ -42,8 +42,8 @@ RSpec.describe 'Users > User browses projects on user page', :js, feature_catego
end
it 'paginates projects', :js do
- project = create(:project, namespace: user.namespace, updated_at: 2.minutes.since)
- project2 = create(:project, namespace: user.namespace, updated_at: 1.minute.since)
+ project = create(:project, namespace: user.namespace, last_activity_at: 2.minutes.since)
+ project2 = create(:project, namespace: user.namespace, last_activity_at: 1.minute.since)
allow(Project).to receive(:default_per_page).and_return(1)
sign_in(user)
diff --git a/spec/finders/personal_projects_finder_spec.rb b/spec/finders/personal_projects_finder_spec.rb
index af3b5bf894b..92539529e97 100644
--- a/spec/finders/personal_projects_finder_spec.rb
+++ b/spec/finders/personal_projects_finder_spec.rb
@@ -2,29 +2,29 @@
require 'spec_helper'
-RSpec.describe PersonalProjectsFinder do
+RSpec.describe PersonalProjectsFinder, feature_category: :groups_and_projects do
let_it_be(:source_user) { create(:user) }
let_it_be(:current_user) { create(:user) }
let_it_be(:admin) { create(:admin) }
let(:finder) { described_class.new(source_user) }
- let!(:public_project) do
- create(:project, :public, namespace: source_user.namespace, updated_at: 1.hour.ago, path: 'pblc')
+ let_it_be(:public_project) do
+ create(:project, :public, namespace: source_user.namespace, last_activity_at: 1.year.ago, path: 'pblc')
end
- let!(:private_project_shared) do
- create(:project, :private, namespace: source_user.namespace, updated_at: 3.hours.ago, path: 'mepmep')
+ let_it_be(:private_project_shared) do
+ create(:project, :private, namespace: source_user.namespace, last_activity_at: 2.hours.ago, path: 'mepmep')
end
- let!(:internal_project) do
- create(:project, :internal, namespace: source_user.namespace, updated_at: 2.hours.ago, path: 'C')
+ let_it_be(:internal_project) do
+ create(:project, :internal, namespace: source_user.namespace, last_activity_at: 3.hours.ago, path: 'C')
end
- let!(:private_project_self) do
- create(:project, :private, namespace: source_user.namespace, updated_at: 3.hours.ago, path: 'D')
+ let_it_be(:private_project_self) do
+ create(:project, :private, namespace: source_user.namespace, last_activity_at: 4.hours.ago, path: 'D')
end
- before do
+ before_all do
private_project_shared.add_developer(current_user)
end
@@ -38,7 +38,7 @@ RSpec.describe PersonalProjectsFinder do
context 'normal user' do
subject { finder.execute(current_user) }
- it { is_expected.to match_array([public_project, internal_project, private_project_shared]) }
+ it { is_expected.to eq([private_project_shared, internal_project, public_project]) }
end
context 'external' do
@@ -48,13 +48,13 @@ RSpec.describe PersonalProjectsFinder do
current_user.update!(external: true)
end
- it { is_expected.to match_array([public_project, private_project_shared]) }
+ it { is_expected.to eq([private_project_shared, public_project]) }
end
context 'and searching with an admin user', :enable_admin_mode do
subject { finder.execute(admin) }
- it { is_expected.to match_array([public_project, internal_project, private_project_self, private_project_shared]) }
+ it { is_expected.to eq([private_project_shared, internal_project, private_project_self, public_project]) }
end
end
end
diff --git a/spec/frontend/ci/runner/admin_runners/admin_runners_app_spec.js b/spec/frontend/ci/runner/admin_runners/admin_runners_app_spec.js
index 798cef252c9..542f3503d2f 100644
--- a/spec/frontend/ci/runner/admin_runners/admin_runners_app_spec.js
+++ b/spec/frontend/ci/runner/admin_runners/admin_runners_app_spec.js
@@ -58,8 +58,6 @@ import {
runnersCountData,
runnerJobCountData,
allRunnersDataPaginated,
- onlineContactTimeoutSecs,
- staleTimeoutSecs,
mockRegistrationToken,
newRunnerPath,
emptyPageInfo,
@@ -123,8 +121,6 @@ describe('AdminRunnersApp', () => {
},
provide: {
localMutations,
- onlineContactTimeoutSecs,
- staleTimeoutSecs,
...provide,
},
mocks: {
diff --git a/spec/frontend/ci/runner/admin_runners/provide_spec.js b/spec/frontend/ci/runner/admin_runners/provide_spec.js
index b24ddabbb66..ef68999acf9 100644
--- a/spec/frontend/ci/runner/admin_runners/provide_spec.js
+++ b/spec/frontend/ci/runner/admin_runners/provide_spec.js
@@ -1,23 +1,20 @@
import { provide } from '~/ci/runner/admin_runners/provide';
-import {
- onlineContactTimeoutSecs,
- staleTimeoutSecs,
- runnerInstallHelpPage,
-} from 'jest/ci/runner/mock_data';
+import { runnerInstallHelpPage } from 'jest/ci/runner/mock_data';
+import { ONLINE_CONTACT_TIMEOUT_SECS, STALE_TIMEOUT_SECS } from '~/ci/runner/constants';
const mockDataset = {
runnerInstallHelpPage,
- onlineContactTimeoutSecs,
- staleTimeoutSecs,
+ onlineContactTimeoutSecs: ONLINE_CONTACT_TIMEOUT_SECS,
+ staleTimeoutSecs: STALE_TIMEOUT_SECS,
};
describe('admin runners provide', () => {
it('returns provide values', () => {
expect(provide(mockDataset)).toMatchObject({
runnerInstallHelpPage,
- onlineContactTimeoutSecs,
- staleTimeoutSecs,
+ onlineContactTimeoutSecs: ONLINE_CONTACT_TIMEOUT_SECS,
+ staleTimeoutSecs: STALE_TIMEOUT_SECS,
});
});
diff --git a/spec/frontend/ci/runner/components/__snapshots__/runner_status_popover_spec.js.snap b/spec/frontend/ci/runner/components/__snapshots__/runner_status_popover_spec.js.snap
deleted file mode 100644
index b27a1adf01b..00000000000
--- a/spec/frontend/ci/runner/components/__snapshots__/runner_status_popover_spec.js.snap
+++ /dev/null
@@ -1,3 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`RunnerStatusPopover renders complete text 1`] = `"Never contacted: Runner has never contacted GitLab (when you register a runner, use gitlab-runner run to bring it online) Online: Runner has contacted GitLab within the last 2 hours Offline: Runner has not contacted GitLab in more than 2 hours Stale: Runner has not contacted GitLab in more than 3 months"`;
diff --git a/spec/frontend/ci/runner/components/runner_list_spec.js b/spec/frontend/ci/runner/components/runner_list_spec.js
index 7c00aa48d31..30177a1c0fa 100644
--- a/spec/frontend/ci/runner/components/runner_list_spec.js
+++ b/spec/frontend/ci/runner/components/runner_list_spec.js
@@ -12,7 +12,7 @@ import RunnerBulkDelete from '~/ci/runner/components/runner_bulk_delete.vue';
import RunnerBulkDeleteCheckbox from '~/ci/runner/components/runner_bulk_delete_checkbox.vue';
import { I18N_PROJECT_TYPE, I18N_STATUS_NEVER_CONTACTED } from '~/ci/runner/constants';
-import { allRunnersData, onlineContactTimeoutSecs, staleTimeoutSecs } from '../mock_data';
+import { allRunnersData } from '../mock_data';
const mockRunners = allRunnersData.data.runners.nodes;
@@ -41,8 +41,6 @@ describe('RunnerList', () => {
},
provide: {
localMutations,
- onlineContactTimeoutSecs,
- staleTimeoutSecs,
},
...options,
});
@@ -62,7 +60,6 @@ describe('RunnerList', () => {
const headerLabels = findHeaders().wrappers.map((w) => w.text());
- expect(findHeaders().at(0).findComponent(HelpPopover).exists()).toBe(true);
expect(findHeaders().at(2).findComponent(HelpPopover).exists()).toBe(true);
expect(headerLabels).toEqual([
diff --git a/spec/frontend/ci/runner/components/runner_platforms_radio_group_spec.js b/spec/frontend/ci/runner/components/runner_platforms_radio_group_spec.js
index 18aa722b94a..e567f238858 100644
--- a/spec/frontend/ci/runner/components/runner_platforms_radio_group_spec.js
+++ b/spec/frontend/ci/runner/components/runner_platforms_radio_group_spec.js
@@ -25,7 +25,7 @@ describe('RunnerPlatformsRadioGroup', () => {
const createComponent = ({
props = {},
mountFn = shallowMountExtended,
- gcpRunner = false,
+ googleCloudRunnerProvisioning = false,
...options
} = {}) => {
wrapper = mountFn(RunnerPlatformsRadioGroup, {
@@ -35,7 +35,7 @@ describe('RunnerPlatformsRadioGroup', () => {
},
provide: {
glFeatures: {
- gcpRunner,
+ googleCloudRunnerProvisioning,
},
},
...options,
@@ -96,9 +96,13 @@ describe('RunnerPlatformsRadioGroup', () => {
});
});
- describe('with gcpRunner flag enabled', () => {
+ describe('with googleCloudRunnerProvisioning flag enabled', () => {
it('contains expected options with images', () => {
- createComponent({ props: {}, mountFn: shallowMountExtended, gcpRunner: true });
+ createComponent({
+ props: {},
+ mountFn: shallowMountExtended,
+ googleCloudRunnerProvisioning: true,
+ });
const labels = findFormRadios().map((w) => [w.text(), w.props('image')]);
@@ -113,7 +117,11 @@ describe('RunnerPlatformsRadioGroup', () => {
});
it('does not contain cloud option when admin prop is passed', () => {
- createComponent({ props: { admin: true }, mountFn: shallowMountExtended, gcpRunner: true });
+ createComponent({
+ props: { admin: true },
+ mountFn: shallowMountExtended,
+ googleCloudRunnerProvisioning: true,
+ });
const labels = findFormRadios().map((w) => [w.text(), w.props('image')]);
diff --git a/spec/frontend/ci/runner/components/runner_status_badge_spec.js b/spec/frontend/ci/runner/components/runner_status_badge_spec.js
index 781193d8afa..05478c1769b 100644
--- a/spec/frontend/ci/runner/components/runner_status_badge_spec.js
+++ b/spec/frontend/ci/runner/components/runner_status_badge_spec.js
@@ -7,8 +7,6 @@ import {
I18N_STATUS_NEVER_CONTACTED,
I18N_STATUS_OFFLINE,
I18N_STATUS_STALE,
- I18N_NEVER_CONTACTED_TOOLTIP,
- I18N_STALE_NEVER_CONTACTED_TOOLTIP,
STATUS_ONLINE,
STATUS_OFFLINE,
STATUS_STALE,
@@ -21,7 +19,7 @@ describe('RunnerTypeBadge', () => {
const findBadge = () => wrapper.findComponent(GlBadge);
const getTooltip = () => getBinding(findBadge().element, 'gl-tooltip');
- const createComponent = ({ props = {} } = {}) => {
+ const createComponent = ({ props = {}, ...options } = {}) => {
wrapper = shallowMount(RunnerStatusBadge, {
propsData: {
contactedAt: '2020-12-31T23:59:00Z',
@@ -31,6 +29,7 @@ describe('RunnerTypeBadge', () => {
directives: {
GlTooltip: createMockDirective('gl-tooltip'),
},
+ ...options,
});
};
@@ -48,7 +47,7 @@ describe('RunnerTypeBadge', () => {
expect(wrapper.text()).toBe(I18N_STATUS_ONLINE);
expect(findBadge().props('variant')).toBe('success');
- expect(getTooltip().value).toBe('Runner is online; last contact was 1 minute ago');
+ expect(getTooltip().value).toBe('Last contact was 1 minute ago');
});
it('renders never contacted state', () => {
@@ -61,7 +60,7 @@ describe('RunnerTypeBadge', () => {
expect(wrapper.text()).toBe(I18N_STATUS_NEVER_CONTACTED);
expect(findBadge().props('variant')).toBe('muted');
- expect(getTooltip().value).toBe(I18N_NEVER_CONTACTED_TOOLTIP);
+ expect(getTooltip().value).toBe('Runner has never contacted this instance');
});
it('renders offline state', () => {
@@ -74,7 +73,9 @@ describe('RunnerTypeBadge', () => {
expect(wrapper.text()).toBe(I18N_STATUS_OFFLINE);
expect(findBadge().props('variant')).toBe('muted');
- expect(getTooltip().value).toBe('Runner is offline; last contact was 1 day ago');
+ expect(getTooltip().value).toBe(
+ "Runner hasn't contacted GitLab in more than 2 hours and last contact was 1 day ago",
+ );
});
it('renders stale state', () => {
@@ -87,7 +88,9 @@ describe('RunnerTypeBadge', () => {
expect(wrapper.text()).toBe(I18N_STATUS_STALE);
expect(findBadge().props('variant')).toBe('warning');
- expect(getTooltip().value).toBe('Runner is stale; last contact was 1 year ago');
+ expect(getTooltip().value).toBe(
+ "Runner hasn't contacted GitLab in more than 3 months and last contact was 1 year ago",
+ );
});
it('renders stale state with no contact time', () => {
@@ -100,7 +103,7 @@ describe('RunnerTypeBadge', () => {
expect(wrapper.text()).toBe(I18N_STATUS_STALE);
expect(findBadge().props('variant')).toBe('warning');
- expect(getTooltip().value).toBe(I18N_STALE_NEVER_CONTACTED_TOOLTIP);
+ expect(getTooltip().value).toBe('Runner is older than 3 months and has never contacted GitLab');
});
describe('does not fail when data is missing', () => {
@@ -113,7 +116,7 @@ describe('RunnerTypeBadge', () => {
});
expect(wrapper.text()).toBe(I18N_STATUS_ONLINE);
- expect(getTooltip().value).toBe('Runner is online; last contact was never');
+ expect(getTooltip().value).toBe('Last contact was never');
});
it('status is missing', () => {
@@ -126,4 +129,34 @@ describe('RunnerTypeBadge', () => {
expect(wrapper.text()).toBe('');
});
});
+
+ describe('default timeout values are overridden', () => {
+ it('shows a different offline timeout', () => {
+ createComponent({
+ props: {
+ contactedAt: '2020-12-31T00:00:00Z',
+ status: STATUS_OFFLINE,
+ },
+ provide: {
+ onlineContactTimeoutSecs: 60,
+ },
+ });
+
+ expect(getTooltip().value).toContain('1 minute');
+ });
+
+ it('shows a different stale timeout', () => {
+ createComponent({
+ props: {
+ contactedAt: '2020-01-01T00:00:00Z',
+ status: STATUS_STALE,
+ },
+ provide: {
+ staleTimeoutSecs: 20 * 60,
+ },
+ });
+
+ expect(getTooltip().value).toContain('20 minutes');
+ });
+ });
});
diff --git a/spec/frontend/ci/runner/components/runner_status_popover_spec.js b/spec/frontend/ci/runner/components/runner_status_popover_spec.js
deleted file mode 100644
index 89fb95f2da4..00000000000
--- a/spec/frontend/ci/runner/components/runner_status_popover_spec.js
+++ /dev/null
@@ -1,36 +0,0 @@
-import { GlSprintf } from '@gitlab/ui';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import RunnerStatusPopover from '~/ci/runner/components/runner_status_popover.vue';
-import HelpPopover from '~/vue_shared/components/help_popover.vue';
-import { onlineContactTimeoutSecs, staleTimeoutSecs } from '../mock_data';
-
-describe('RunnerStatusPopover', () => {
- let wrapper;
-
- const createComponent = ({ provide = {} } = {}) => {
- wrapper = shallowMountExtended(RunnerStatusPopover, {
- provide: {
- onlineContactTimeoutSecs,
- staleTimeoutSecs,
- ...provide,
- },
- stubs: {
- GlSprintf,
- },
- });
- };
-
- const findHelpPopover = () => wrapper.findComponent(HelpPopover);
-
- it('renders popoover', () => {
- createComponent();
-
- expect(findHelpPopover().exists()).toBe(true);
- });
-
- it('renders complete text', () => {
- createComponent();
-
- expect(findHelpPopover().text()).toMatchSnapshot();
- });
-});
diff --git a/spec/frontend/ci/runner/group_new_runner_app/group_new_runner_app_spec.js b/spec/frontend/ci/runner/group_new_runner_app/group_new_runner_app_spec.js
index 4e1e8c0adde..a934e2a1516 100644
--- a/spec/frontend/ci/runner/group_new_runner_app/group_new_runner_app_spec.js
+++ b/spec/frontend/ci/runner/group_new_runner_app/group_new_runner_app_spec.js
@@ -41,7 +41,7 @@ describe('GroupRunnerRunnerApp', () => {
const findRunnerCreateForm = () => wrapper.findComponent(RunnerCreateForm);
const findRunnerCloudForm = () => wrapper.findComponent(RunnerCloudConnectionForm);
- const createComponent = (gcpRunner = false) => {
+ const createComponent = (googleCloudRunnerProvisioning = false) => {
wrapper = shallowMountExtended(GroupRunnerRunnerApp, {
propsData: {
groupId: mockGroupId,
@@ -51,7 +51,7 @@ describe('GroupRunnerRunnerApp', () => {
},
provide: {
glFeatures: {
- gcpRunner,
+ googleCloudRunnerProvisioning,
},
},
});
diff --git a/spec/frontend/ci/runner/group_runners/group_runners_app_spec.js b/spec/frontend/ci/runner/group_runners/group_runners_app_spec.js
index 3e4cdecb07b..2af877f86c9 100644
--- a/spec/frontend/ci/runner/group_runners/group_runners_app_spec.js
+++ b/spec/frontend/ci/runner/group_runners/group_runners_app_spec.js
@@ -58,8 +58,6 @@ import {
groupRunnersDataPaginated,
groupRunnersCountData,
runnerJobCountData,
- onlineContactTimeoutSecs,
- staleTimeoutSecs,
mockRegistrationToken,
newRunnerPath,
emptyPageInfo,
@@ -124,8 +122,6 @@ describe('GroupRunnersApp', () => {
},
provide: {
localMutations,
- onlineContactTimeoutSecs,
- staleTimeoutSecs,
...provide,
},
mocks: {
diff --git a/spec/frontend/ci/runner/mock_data.js b/spec/frontend/ci/runner/mock_data.js
index 58d8e0ee74a..783abec9ade 100644
--- a/spec/frontend/ci/runner/mock_data.js
+++ b/spec/frontend/ci/runner/mock_data.js
@@ -338,9 +338,6 @@ export const mockSearchExamples = [
},
];
-export const onlineContactTimeoutSecs = 2 * 60 * 60;
-export const staleTimeoutSecs = 7889238; // Ruby's `3.months`
-
export const mockRegistrationToken = 'MOCK_REGISTRATION_TOKEN';
export const mockAuthenticationToken = 'MOCK_AUTHENTICATION_TOKEN';
diff --git a/spec/frontend/ci/runner/project_new_runner_app/project_new_runner_app_spec.js b/spec/frontend/ci/runner/project_new_runner_app/project_new_runner_app_spec.js
index 3eeff39976c..4061b2b7a5d 100644
--- a/spec/frontend/ci/runner/project_new_runner_app/project_new_runner_app_spec.js
+++ b/spec/frontend/ci/runner/project_new_runner_app/project_new_runner_app_spec.js
@@ -44,7 +44,7 @@ describe('ProjectRunnerRunnerApp', () => {
const findRunnerCloudExecutionEnvironment = () =>
wrapper.findComponent(RunnerCloudExecutionEnvironment);
- const createComponent = (gcpRunner = false) => {
+ const createComponent = (googleCloudRunnerProvisioning = false) => {
wrapper = shallowMountExtended(ProjectRunnerRunnerApp, {
propsData: {
projectId: mockProjectId,
@@ -55,7 +55,7 @@ describe('ProjectRunnerRunnerApp', () => {
},
provide: {
glFeatures: {
- gcpRunner,
+ googleCloudRunnerProvisioning,
},
},
});
diff --git a/spec/lib/gitlab/git/diff_tree_spec.rb b/spec/lib/gitlab/git/diff_tree_spec.rb
index e82978bd488..3b3297650bb 100644
--- a/spec/lib/gitlab/git/diff_tree_spec.rb
+++ b/spec/lib/gitlab/git/diff_tree_spec.rb
@@ -3,28 +3,20 @@
require "spec_helper"
RSpec.describe Gitlab::Git::DiffTree, feature_category: :source_code_management do
- let_it_be(:project) { create(:project, :repository) }
- let_it_be(:repository) { project.repository }
+ subject(:diff_tree) { described_class.new(left_tree_id, right_tree_id) }
- describe '.from_commit' do
- subject(:diff_tree) { described_class.from_commit(commit) }
+ let(:left_tree_id) { '1a0b36b3cdad1d2ee32457c102a8c0b7056fa863' }
+ let(:right_tree_id) { '60ecb67744cb56576c30214ff52294f8ce2def98' }
- context 'when commit is an initial commit' do
- let(:commit) { repository.commit('1a0b36b3cdad1d2ee32457c102a8c0b7056fa863') }
+ describe '#left_tree_id' do
+ subject { diff_tree.left_tree_id }
- it 'returns the expected diff tree object' do
- expect(diff_tree.left_tree_id).to eq(Gitlab::Git::SHA1_EMPTY_TREE_ID)
- expect(diff_tree.right_tree_id).to eq(commit.tree_id)
- end
- end
+ it { is_expected.to eq(left_tree_id) }
+ end
- context 'when commit is a regular commit' do
- let(:commit) { repository.commit('60ecb67744cb56576c30214ff52294f8ce2def98') }
+ describe '#right_tree_id' do
+ subject { diff_tree.right_tree_id }
- it 'returns the expected diff tree object' do
- expect(diff_tree.left_tree_id).to eq(commit.parent.tree_id)
- expect(diff_tree.right_tree_id).to eq(commit.tree_id)
- end
- end
+ it { is_expected.to eq(right_tree_id) }
end
end
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index 959074a79bf..ddcdf2d559a 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -1706,7 +1706,6 @@ RSpec.describe Gitlab::Git::Repository, feature_category: :source_code_managemen
let_it_be(:commit_3) { repository.commit('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') }
let_it_be(:initial_commit) { repository.commit('1a0b36b3cdad1d2ee32457c102a8c0b7056fa863') }
- let_it_be(:diff_tree) { Gitlab::Git::DiffTree.from_commit(initial_commit) }
let(:commit_1_files) do
[
@@ -1739,7 +1738,7 @@ RSpec.describe Gitlab::Git::Repository, feature_category: :source_code_managemen
]
end
- let(:diff_tree_files) do
+ let(:initial_commit_files) do
[
Gitlab::Git::ChangedPath.new(
status: :ADDED, path: ".gitignore", old_mode: "0", new_mode: "100644",
@@ -1757,10 +1756,10 @@ RSpec.describe Gitlab::Git::Repository, feature_category: :source_code_managemen
end
it 'returns a list of paths' do
- collection = repository.find_changed_paths([commit_1, commit_2, commit_3, diff_tree])
+ collection = repository.find_changed_paths([commit_1, commit_2, commit_3, initial_commit])
expect(collection).to be_a(Enumerable)
- expect(collection.as_json).to eq((commit_1_files + commit_2_files + commit_3_files + diff_tree_files).as_json)
+ expect(collection.as_json).to eq((commit_1_files + commit_2_files + commit_3_files + initial_commit_files).as_json)
end
it 'returns only paths with valid SHAs' do
diff --git a/spec/requests/api/lint_spec.rb b/spec/requests/api/lint_spec.rb
index 5842bd1c716..e10ea492d95 100644
--- a/spec/requests/api/lint_spec.rb
+++ b/spec/requests/api/lint_spec.rb
@@ -4,10 +4,10 @@ require 'spec_helper'
RSpec.describe API::Lint, feature_category: :pipeline_composition do
describe 'GET /projects/:id/ci/lint' do
- subject(:ci_lint) { get api("/projects/#{project.id}/ci/lint", api_user), params: { sha: sha, dry_run: dry_run, include_jobs: include_jobs } }
+ subject(:ci_lint) { get api("/projects/#{project.id}/ci/lint", api_user), params: { content_ref: content_ref, dry_run: dry_run, include_jobs: include_jobs } }
let(:project) { create(:project, :repository) }
- let(:sha) { nil }
+ let(:content_ref) { nil }
let(:dry_run) { nil }
let(:include_jobs) { nil }
@@ -323,6 +323,8 @@ RSpec.describe API::Lint, feature_category: :pipeline_composition do
branch_name: 'master'
)
+ project.repository.create_branch('invalid-content', 'master')
+
project.repository.update_file(
project.creator,
'.gitlab-ci.yml',
@@ -330,11 +332,13 @@ RSpec.describe API::Lint, feature_category: :pipeline_composition do
message: 'Automatically edited .gitlab-ci.yml again',
branch_name: 'master'
)
+
+ project.repository.create_branch('valid-content', 'master')
end
context 'when latest .gitlab-ci.yml is valid' do
- # check with explicit sha
- let(:sha) { project.repository.commit.sha }
+ # check with explicit content_ref
+ let(:content_ref) { project.repository.commit.sha }
it 'passes validation' do
ci_lint
@@ -349,7 +353,7 @@ RSpec.describe API::Lint, feature_category: :pipeline_composition do
end
context 'when previous .gitlab-ci.yml is invalid' do
- let(:sha) { project.repository.commit.parent.sha }
+ let(:content_ref) { project.repository.commit.parent.sha }
it 'fails validation' do
ci_lint
@@ -359,12 +363,12 @@ RSpec.describe API::Lint, feature_category: :pipeline_composition do
expect(json_response['merged_yaml']).to eq(first_edit)
expect(json_response['valid']).to eq(false)
expect(json_response['warnings']).to eq([])
- expect(json_response['errors']).to eq(["jobs config should contain at least one visible job"])
+ expect(json_response['errors']).to eq(['jobs config should contain at least one visible job'])
end
end
context 'when first .gitlab-ci.yml is valid' do
- let(:sha) { project.repository.commit.parent.parent.sha }
+ let(:content_ref) { project.repository.commit.parent.parent.sha }
it 'passes validation' do
ci_lint
@@ -378,8 +382,8 @@ RSpec.describe API::Lint, feature_category: :pipeline_composition do
end
end
- context 'when sha is not found' do
- let(:sha) { "unknown" }
+ context 'when content_ref is not found' do
+ let(:content_ref) { 'unknown' }
it 'returns 404 response' do
ci_lint
@@ -387,6 +391,73 @@ RSpec.describe API::Lint, feature_category: :pipeline_composition do
expect(response).to have_gitlab_http_status(:not_found)
end
end
+
+ context 'when sha (deprecated) is used with valid configuration' do
+ let(:sha) { project.repository.commit.sha }
+
+ it 'passes validation' do
+ get api("/projects/#{project.id}/ci/lint", api_user), params: { sha: sha, dry_run: dry_run, include_jobs: include_jobs }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response).to be_an Hash
+ expect(json_response['merged_yaml']).to eq(second_edit)
+ expect(json_response['valid']).to eq(true)
+ expect(json_response['warnings']).to eq([])
+ expect(json_response['errors']).to eq([])
+ end
+ end
+
+ context 'when sha (deprecated) and content_ref are used at the same time' do
+ let(:sha) { project.repository.commit.sha }
+
+ it 'returns bad request' do
+ get api("/projects/#{project.id}/ci/lint", api_user), params: { sha: sha, content_ref: sha }
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to eq('sha, content_ref are mutually exclusive')
+ end
+ end
+
+ context 'when ref (deprecated) and dry_run_ref are used at the same time' do
+ let(:sha) { project.repository.commit.sha }
+
+ it 'returns bad request' do
+ get api("/projects/#{project.id}/ci/lint", api_user), params: { ref: sha, dry_run_ref: sha }
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to eq('ref, dry_run_ref are mutually exclusive')
+ end
+ end
+
+ context 'when content_ref is a valid ref name with invalid config' do
+ let(:content_ref) { 'invalid-content' }
+
+ it 'fails validation' do
+ ci_lint
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response).to be_an Hash
+ expect(json_response['merged_yaml']).to eq(first_edit)
+ expect(json_response['valid']).to eq(false)
+ expect(json_response['warnings']).to eq([])
+ expect(json_response['errors']).to eq(["jobs config should contain at least one visible job"])
+ end
+ end
+
+ context 'when content_ref is a valid ref name with valid config' do
+ let(:content_ref) { 'valid-content' }
+
+ it 'passes validation' do
+ ci_lint
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response).to be_an Hash
+ expect(json_response['merged_yaml']).to eq(second_edit)
+ expect(json_response['valid']).to eq(true)
+ expect(json_response['warnings']).to eq([])
+ expect(json_response['errors']).to eq([])
+ end
+ end
end
end
end
diff --git a/yarn.lock b/yarn.lock
index bbdbfdcbdcc..b712342d766 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1341,10 +1341,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/visual-review-tools/-/visual-review-tools-1.7.3.tgz#9ea641146436da388ffbad25d7f2abe0df52c235"
integrity sha512-NMV++7Ew1FSBDN1xiZaauU9tfeSfgDHcOLpn+8bGpP+O5orUPm2Eu66R5eC5gkjBPaXosNAxNWtriee+aFk4+g==
-"@gitlab/web-ide@^0.0.1-dev-20240208022507":
- version "0.0.1-dev-20240208022507"
- resolved "https://registry.yarnpkg.com/@gitlab/web-ide/-/web-ide-0.0.1-dev-20240208022507.tgz#94e7a3aee893278e1d112124af54a34f3c79b098"
- integrity sha512-aC8wTxiJ9bO7ZddpinwMyE5NWegIdzxszH+LxsZ69E6zcw0m5ilbYfJq+ILAXgtTNwUD4queEjSBY74Vp8OVVA==
+"@gitlab/web-ide@^0.0.1-dev-20240214084918":
+ version "0.0.1-dev-20240214084918"
+ resolved "https://registry.yarnpkg.com/@gitlab/web-ide/-/web-ide-0.0.1-dev-20240214084918.tgz#f2e4b5ecbb42e8e19c1f50ff4bcbaab618b16fa9"
+ integrity sha512-ZRlfZv/Bbtth6MwWE3Twna0rQ5PV5Xb0Ux4+NPeZGgYOhCAOLGK8j8pzuX0+6YcWgwuooiK1LRzQ3mxfeqlmXg==
"@graphql-eslint/eslint-plugin@3.20.1":
version "3.20.1"