summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.rubocop_todo/gitlab/strong_memoize_attr.yml1
-rw-r--r--.rubocop_todo/rspec/missing_feature_category.yml1
-rw-r--r--app/assets/images/learn_gitlab/section_code.svg4
-rw-r--r--app/assets/javascripts/ide/index.js1
-rw-r--r--app/assets/javascripts/ide/stores/modules/commit/actions.js5
-rw-r--r--app/assets/javascripts/ide/stores/state.js1
-rw-r--r--app/assets/javascripts/jobs/components/table/graphql/cache_config.js60
-rw-r--r--app/assets/javascripts/jobs/components/table/graphql/queries/get_jobs.query.graphql1
-rw-r--r--app/assets/javascripts/jobs/components/table/graphql/queries/get_jobs_count.query.graphql8
-rw-r--r--app/assets/javascripts/jobs/components/table/jobs_table_app.vue41
-rw-r--r--app/controllers/ide_controller.rb1
-rw-r--r--app/helpers/broadcast_messages_helper.rb17
-rw-r--r--app/helpers/ide_helper.rb5
-rw-r--r--app/models/onboarding/completion.rb38
-rw-r--r--app/views/admin/broadcast_messages/index.html.haml14
-rw-r--r--app/views/ide/_show.html.haml9
-rw-r--r--data/deprecations/15-7-deprecate-gitlab-runner-exec-cmd.yml10
-rw-r--r--data/deprecations/15-7-deprecate-kas-metrics-port-in-gitlab-chart.yml10
-rw-r--r--data/deprecations/15-7-deprecate-shimo-integration.yml2
-rw-r--r--data/deprecations/15-7-deprecate-zentao-integration.yml2
-rw-r--r--data/deprecations/15-8-deprecate-slack-notifications-integration.yml2
-rw-r--r--data/deprecations/15-8-deprecate-system-hook-test-endpoint.yml2
-rw-r--r--data/deprecations/15-8-deprecate-updated-at-error.yml2
-rw-r--r--data/deprecations/15-8-jira-connect-app-cookie-auth.yml2
-rw-r--r--data/deprecations/15-8-live-preview.yml2
-rw-r--r--data/deprecations/15-8-projects-api-ops-access-level.yml2
-rw-r--r--data/deprecations/15-8-visual-review-tool.yml2
-rw-r--r--data/deprecations/15-9-accessibility-testing-deprecation.yml2
-rw-r--r--data/deprecations/15-9-ci-builds-column-validations.yml2
-rw-r--r--data/deprecations/15-9-deprecate-ci-pre-clone-script.yml2
-rw-r--r--data/deprecations/15-9-insecure-ci-job-token.yml20
-rw-r--r--data/deprecations/15-9-legacy-praefect-configuration.yml2
-rw-r--r--data/deprecations/15-9-remove-offset-pagination-jobs-api.yml2
-rw-r--r--data/deprecations/15-9-secure-analyzers-bump.yml10
-rw-r--r--data/whats_new/20230222001_15_09.yml75
-rw-r--r--db/docs/bulk_import_batch_trackers.yml10
-rw-r--r--db/docs/bulk_import_export_batches.yml10
-rw-r--r--db/migrate/20230210152109_add_bulk_import_export_batches.rb21
-rw-r--r--db/migrate/20230210153420_add_batched_column_to_bulk_import_exports.rb9
-rw-r--r--db/migrate/20230210155715_add_batch_id_to_bulk_import_export_uploads.rb7
-rw-r--r--db/migrate/20230210160037_add_batch_foreign_key_to_bulk_import_export_uploads.rb15
-rw-r--r--db/migrate/20230210160351_add_bulk_import_batch_trackers.rb22
-rw-r--r--db/migrate/20230210161002_add_batched_column_to_bulk_import_trackers.rb7
-rw-r--r--db/migrate/20230210171012_add_batch_id_index_to_bulk_import_export_uploads.rb15
-rw-r--r--db/schema_migrations/202302101521091
-rw-r--r--db/schema_migrations/202302101534201
-rw-r--r--db/schema_migrations/202302101557151
-rw-r--r--db/schema_migrations/202302101600371
-rw-r--r--db/schema_migrations/202302101603511
-rw-r--r--db/schema_migrations/202302101610021
-rw-r--r--db/schema_migrations/202302101710121
-rw-r--r--db/structure.sql77
-rw-r--r--lib/bulk_imports/clients/http.rb7
-rw-r--r--locale/gitlab.pot18
-rw-r--r--qa/spec/specs/helpers/feature_flag_spec.rb2
-rw-r--r--spec/factories/projects.rb12
-rw-r--r--spec/frontend/fixtures/jobs.rb22
-rw-r--r--spec/frontend/ide/stores/modules/commit/actions_spec.js66
-rw-r--r--spec/frontend/jobs/components/table/graphql/cache_config_spec.js19
-rw-r--r--spec/frontend/jobs/components/table/job_table_app_spec.js45
-rw-r--r--spec/frontend/jobs/mock_data.js2
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/tokens/branch_token_spec.js93
-rw-r--r--spec/helpers/broadcast_messages_helper_spec.rb29
-rw-r--r--spec/helpers/ide_helper_spec.rb163
-rw-r--r--spec/lib/bulk_imports/clients/http_spec.rb31
-rw-r--r--spec/models/onboarding/completion_spec.rb65
-rw-r--r--spec/requests/ide_controller_spec.rb22
67 files changed, 943 insertions, 213 deletions
diff --git a/.rubocop_todo/gitlab/strong_memoize_attr.yml b/.rubocop_todo/gitlab/strong_memoize_attr.yml
index 04f76fbe260..6b403601d0d 100644
--- a/.rubocop_todo/gitlab/strong_memoize_attr.yml
+++ b/.rubocop_todo/gitlab/strong_memoize_attr.yml
@@ -127,7 +127,6 @@ Gitlab/StrongMemoizeAttr:
- 'app/models/namespaces/traversal/linear.rb'
- 'app/models/namespaces/traversal/recursive.rb'
- 'app/models/note.rb'
- - 'app/models/onboarding/completion.rb'
- 'app/models/packages/go/module.rb'
- 'app/models/packages/go/module_version.rb'
- 'app/models/packages/package.rb'
diff --git a/.rubocop_todo/rspec/missing_feature_category.yml b/.rubocop_todo/rspec/missing_feature_category.yml
index 5f4d4dda292..289eeab505a 100644
--- a/.rubocop_todo/rspec/missing_feature_category.yml
+++ b/.rubocop_todo/rspec/missing_feature_category.yml
@@ -6149,7 +6149,6 @@ RSpec/MissingFeatureCategory:
- 'spec/models/notification_setting_spec.rb'
- 'spec/models/oauth_access_grant_spec.rb'
- 'spec/models/oauth_access_token_spec.rb'
- - 'spec/models/onboarding/completion_spec.rb'
- 'spec/models/onboarding/progress_spec.rb'
- 'spec/models/operations/feature_flag_spec.rb'
- 'spec/models/operations/feature_flags/strategy_spec.rb'
diff --git a/app/assets/images/learn_gitlab/section_code.svg b/app/assets/images/learn_gitlab/section_code.svg
new file mode 100644
index 00000000000..da170c93be6
--- /dev/null
+++ b/app/assets/images/learn_gitlab/section_code.svg
@@ -0,0 +1,4 @@
+<svg width="25" height="30" viewBox="0 0 20 23" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M15 8L15 9.5C15 10.3284 15.6716 11 16.5 11C17.3284 11 18 10.3284 18 9.5V7.74264C18 6.94699 17.6839 6.18393 17.1213 5.62132L12.8787 1.37868C12.3161 0.816071 11.553 0.5 10.7574 0.5H1.5C0.671573 0.5 0 1.17157 0 2V20C0 20.8284 0.671573 21.5 1.5 21.5H6C6.82843 21.5 7.5 20.8284 7.5 20C7.5 19.1716 6.82843 18.5 6 18.5H3V3.5H10.5V6.5C10.5 7.32843 11.1716 8 12 8H15Z" fill="#6E49CB"/>
+<path d="M10.5 18.5C10.5 17.6716 11.1716 17 12 17H13.5V15.5C13.5 14.6716 14.1716 14 15 14C15.8284 14 16.5 14.6716 16.5 15.5V17H18C18.8284 17 19.5 17.6716 19.5 18.5C19.5 19.3284 18.8284 20 18 20H16.5V21.5C16.5 22.3284 15.8284 23 15 23C14.1716 23 13.5 22.3284 13.5 21.5V20H12C11.1716 20 10.5 19.3284 10.5 18.5Z" fill="#6E49CB"/>
+</svg>
diff --git a/app/assets/javascripts/ide/index.js b/app/assets/javascripts/ide/index.js
index 29c44d2f596..967c83b320f 100644
--- a/app/assets/javascripts/ide/index.js
+++ b/app/assets/javascripts/ide/index.js
@@ -72,6 +72,7 @@ export const initLegacyWebIDE = (el, options = {}) => {
environmentsGuidanceAlertDismissed: !parseBoolean(el.dataset.enableEnvironmentsGuidance),
previewMarkdownPath: el.dataset.previewMarkdownPath,
userPreferencesPath: el.dataset.userPreferencesPath,
+ learnGitlabSource: parseBoolean(el.dataset.learnGitlabSource),
});
},
beforeDestroy() {
diff --git a/app/assets/javascripts/ide/stores/modules/commit/actions.js b/app/assets/javascripts/ide/stores/modules/commit/actions.js
index d490b8c5dad..7f8e253ed37 100644
--- a/app/assets/javascripts/ide/stores/modules/commit/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/commit/actions.js
@@ -1,6 +1,7 @@
import { createAlert } from '~/flash';
import { addNumericSuffix } from '~/ide/utils';
import { sprintf, __ } from '~/locale';
+import Tracking from '~/tracking';
import { leftSidebarViews } from '../../../constants';
import eventHub from '../../../eventhub';
import { parseCommitError } from '../../../lib/errors';
@@ -162,6 +163,10 @@ export const commitChanges = ({ commit, state, getters, dispatch, rootState, roo
);
}
+ if (rootState.learnGitlabSource) {
+ Tracking.event(undefined, 'commit', { label: 'web_ide_learn_gitlab_source' });
+ }
+
dispatch('setLastCommitMessage', data);
dispatch('updateCommitMessage', '');
return dispatch('updateFilesAfterCommit', {
diff --git a/app/assets/javascripts/ide/stores/state.js b/app/assets/javascripts/ide/stores/state.js
index 356bbf28a48..013a0c3ce8f 100644
--- a/app/assets/javascripts/ide/stores/state.js
+++ b/app/assets/javascripts/ide/stores/state.js
@@ -32,4 +32,5 @@ export default () => ({
environmentsGuidanceAlertDetected: false,
previewMarkdownPath: '',
userPreferencesPath: '',
+ learnGitlabSource: false,
});
diff --git a/app/assets/javascripts/jobs/components/table/graphql/cache_config.js b/app/assets/javascripts/jobs/components/table/graphql/cache_config.js
index 8bcd7ffd10f..5390c023da4 100644
--- a/app/assets/javascripts/jobs/components/table/graphql/cache_config.js
+++ b/app/assets/javascripts/jobs/components/table/graphql/cache_config.js
@@ -11,42 +11,48 @@ export default {
},
CiJobConnection: {
merge(existing = {}, incoming, { args = {} }) {
- let nodes;
+ if (incoming.nodes) {
+ let nodes;
- const areNodesEqual = isEqual(existing.nodes, incoming.nodes);
- const statuses = Array.isArray(args.statuses) ? [...args.statuses] : args.statuses;
- const { pageInfo } = incoming;
+ const areNodesEqual = isEqual(existing.nodes, incoming.nodes);
+ const statuses = Array.isArray(args.statuses) ? [...args.statuses] : args.statuses;
+ const { pageInfo } = incoming;
- if (Object.keys(existing).length !== 0 && isEqual(existing?.statuses, args?.statuses)) {
- if (areNodesEqual) {
- if (incoming.pageInfo.hasNextPage) {
- nodes = [...existing.nodes, ...incoming.nodes];
+ if (Object.keys(existing).length !== 0 && isEqual(existing?.statuses, args?.statuses)) {
+ if (areNodesEqual) {
+ if (incoming.pageInfo.hasNextPage) {
+ nodes = [...existing.nodes, ...incoming.nodes];
+ } else {
+ nodes = [...incoming.nodes];
+ }
} else {
- nodes = [...incoming.nodes];
- }
- } else {
- if (!existing.pageInfo?.hasNextPage) {
- nodes = [...incoming.nodes];
+ if (!existing.pageInfo?.hasNextPage) {
+ nodes = [...incoming.nodes];
- return {
- nodes,
- statuses,
- pageInfo,
- count: incoming.count,
- };
- }
+ return {
+ nodes,
+ statuses,
+ pageInfo,
+ };
+ }
- nodes = [...existing.nodes, ...incoming.nodes];
+ nodes = [...existing.nodes, ...incoming.nodes];
+ }
+ } else {
+ nodes = [...incoming.nodes];
}
- } else {
- nodes = [...incoming.nodes];
+
+ return {
+ nodes,
+ statuses,
+ pageInfo,
+ };
}
return {
- nodes,
- statuses,
- pageInfo,
- count: incoming.count,
+ nodes: existing.nodes,
+ pageInfo: existing.pageInfo,
+ statuses: args.statuses,
};
},
},
diff --git a/app/assets/javascripts/jobs/components/table/graphql/queries/get_jobs.query.graphql b/app/assets/javascripts/jobs/components/table/graphql/queries/get_jobs.query.graphql
index 851be211b25..69719011079 100644
--- a/app/assets/javascripts/jobs/components/table/graphql/queries/get_jobs.query.graphql
+++ b/app/assets/javascripts/jobs/components/table/graphql/queries/get_jobs.query.graphql
@@ -2,7 +2,6 @@ query getJobs($fullPath: ID!, $after: String, $first: Int = 30, $statuses: [CiJo
project(fullPath: $fullPath) {
id
jobs(after: $after, first: $first, statuses: $statuses) {
- count
pageInfo {
endCursor
hasNextPage
diff --git a/app/assets/javascripts/jobs/components/table/graphql/queries/get_jobs_count.query.graphql b/app/assets/javascripts/jobs/components/table/graphql/queries/get_jobs_count.query.graphql
new file mode 100644
index 00000000000..a4e02ae721a
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/table/graphql/queries/get_jobs_count.query.graphql
@@ -0,0 +1,8 @@
+query getJobsCount($fullPath: ID!, $statuses: [CiJobStatus!]) {
+ project(fullPath: $fullPath) {
+ id
+ jobs(statuses: $statuses) {
+ count
+ }
+ }
+}
diff --git a/app/assets/javascripts/jobs/components/table/jobs_table_app.vue b/app/assets/javascripts/jobs/components/table/jobs_table_app.vue
index 3209fc4b90d..49960278208 100644
--- a/app/assets/javascripts/jobs/components/table/jobs_table_app.vue
+++ b/app/assets/javascripts/jobs/components/table/jobs_table_app.vue
@@ -6,6 +6,7 @@ import { setUrlParams, updateHistory, queryToObject } from '~/lib/utils/url_util
import JobsFilteredSearch from '../filtered_search/jobs_filtered_search.vue';
import { validateQueryString } from '../filtered_search/utils';
import GetJobs from './graphql/queries/get_jobs.query.graphql';
+import GetJobsCount from './graphql/queries/get_jobs_count.query.graphql';
import JobsTable from './jobs_table.vue';
import JobsTableEmptyState from './jobs_table_empty_state.vue';
import JobsTableTabs from './jobs_table_tabs.vue';
@@ -13,7 +14,8 @@ import { RAW_TEXT_WARNING } from './constants';
export default {
i18n: {
- errorMsg: __('There was an error fetching the jobs for your project.'),
+ jobsFetchErrorMsg: __('There was an error fetching the jobs for your project.'),
+ jobsCountErrorMsg: __('There was an error fetching the number of jobs for your project.'),
loadingAriaLabel: __('Loading'),
},
filterSearchBoxStyles:
@@ -43,15 +45,29 @@ export default {
};
},
update(data) {
- const { jobs: { nodes: list = [], pageInfo = {}, count } = {} } = data.project || {};
+ const { jobs: { nodes: list = [], pageInfo = {} } = {} } = data.project || {};
return {
list,
pageInfo,
- count,
};
},
error() {
- this.hasError = true;
+ this.error = this.$options.i18n.jobsFetchErrorMsg;
+ },
+ },
+ jobsCount: {
+ query: GetJobsCount,
+ variables() {
+ return {
+ fullPath: this.fullPath,
+ ...this.validatedQueryString,
+ };
+ },
+ update({ project }) {
+ return project?.jobs?.count || 0;
+ },
+ error() {
+ this.error = this.$options.i18n.jobsCountErrorMsg;
},
},
},
@@ -60,11 +76,11 @@ export default {
jobs: {
list: [],
},
- hasError: false,
- isAlertDismissed: false,
+ error: '',
scope: null,
infiniteScrollingTriggered: false,
filterSearchTriggered: false,
+ jobsCount: null,
count: 0,
};
},
@@ -72,9 +88,6 @@ export default {
loading() {
return this.$apollo.queries.jobs.loading;
},
- shouldShowAlert() {
- return this.hasError && !this.isAlertDismissed;
- },
// Show when on All tab with no jobs
// Show only when not loading and filtered search has not been triggered
// So we don't show empty state when results are empty on a filtered search
@@ -95,9 +108,6 @@ export default {
showFilteredSearch() {
return !this.scope;
},
- jobsCount() {
- return this.jobs.count;
- },
validatedQueryString() {
const queryStringObject = queryToObject(window.location.search);
@@ -146,6 +156,7 @@ export default {
});
this.$apollo.queries.jobs.refetch({ statuses: filter.value.data });
+ this.$apollo.queries.jobsCount.refetch({ statuses: filter.value.data });
}
});
},
@@ -168,14 +179,14 @@ export default {
<template>
<div>
<gl-alert
- v-if="shouldShowAlert"
+ v-if="error"
class="gl-mt-2"
variant="danger"
data-testid="jobs-table-error-alert"
dismissible
- @dismiss="isAlertDismissed = true"
+ @dismiss="error = ''"
>
- {{ $options.i18n.errorMsg }}
+ {{ error }}
</gl-alert>
<jobs-table-tabs
diff --git a/app/controllers/ide_controller.rb b/app/controllers/ide_controller.rb
index b61e2318cea..1b3d9223502 100644
--- a/app/controllers/ide_controller.rb
+++ b/app/controllers/ide_controller.rb
@@ -39,6 +39,7 @@ class IdeController < ApplicationController
@branch = params[:branch]
@path = params[:path]
@merge_request = params[:merge_request_id]
+ @learn_gitlab_source = params[:learn_gitlab_source]
@fork_info = fork_info(project, @branch)
end
diff --git a/app/helpers/broadcast_messages_helper.rb b/app/helpers/broadcast_messages_helper.rb
index 01d28ed3221..1c5a601de25 100644
--- a/app/helpers/broadcast_messages_helper.rb
+++ b/app/helpers/broadcast_messages_helper.rb
@@ -62,6 +62,23 @@ module BroadcastMessagesHelper
end.join(', ')
end
+ def admin_broadcast_messages_data(broadcast_messages)
+ broadcast_messages.map do |message|
+ {
+ id: message.id,
+ status: broadcast_message_status(message),
+ preview: broadcast_message(message, preview: true),
+ starts_at: message.starts_at.iso8601,
+ ends_at: message.ends_at.iso8601,
+ target_roles: target_access_levels_display(message.target_access_levels),
+ target_path: message.target_path,
+ type: message.broadcast_type.capitalize,
+ edit_path: edit_admin_broadcast_message_path(message),
+ delete_path: "#{admin_broadcast_message_path(message)}.js"
+ }
+ end.to_json
+ end
+
private
def current_user_access_level_for_project_or_group
diff --git a/app/helpers/ide_helper.rb b/app/helpers/ide_helper.rb
index c5be044a27b..296fe6856ac 100644
--- a/app/helpers/ide_helper.rb
+++ b/app/helpers/ide_helper.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module IdeHelper
- def ide_data(project:, branch:, path:, merge_request:, fork_info:)
+ def ide_data(project:, branch:, path:, merge_request:, fork_info:, learn_gitlab_source:)
{
'can-use-new-web-ide' => can_use_new_web_ide?.to_s,
'use-new-web-ide' => use_new_web_ide?.to_s,
@@ -13,7 +13,8 @@ module IdeHelper
'editor-font-src-url' => font_url('jetbrains-mono/JetBrainsMono.woff2'),
'editor-font-family' => 'JetBrains Mono',
'editor-font-format' => 'woff2',
- 'merge-request' => merge_request
+ 'merge-request' => merge_request,
+ 'learn-gitlab-source' => (!!learn_gitlab_source).to_s
}.merge(use_new_web_ide? ? new_ide_data(project: project) : legacy_ide_data(project: project))
end
diff --git a/app/models/onboarding/completion.rb b/app/models/onboarding/completion.rb
index 269283df826..2d1cfc4e876 100644
--- a/app/models/onboarding/completion.rb
+++ b/app/models/onboarding/completion.rb
@@ -19,35 +19,51 @@ module Onboarding
:user_added
].freeze
- def initialize(namespace, current_user = nil)
- @namespace = namespace
+ def initialize(project, current_user = nil)
+ @project = project
+ @namespace = project.namespace
@current_user = current_user
end
def percentage
return 0 unless onboarding_progress
- attributes = onboarding_progress.attributes.symbolize_keys
-
total_actions = action_columns.count
- completed_actions = action_columns.count { |column| attributes[column].present? }
+ completed_actions = action_columns.count { |column| completed?(column) }
(completed_actions.to_f / total_actions * 100).round
end
+ def completed?(column)
+ if column == :code_added
+ repository.commit_count > 1 || repository.branch_count > 1
+ else
+ attributes[column].present?
+ end
+ end
+
private
+ def repository
+ project.repository
+ end
+ strong_memoize_attr :repository
+
+ def attributes
+ onboarding_progress.attributes.symbolize_keys
+ end
+ strong_memoize_attr :attributes
+
def onboarding_progress
- strong_memoize(:onboarding_progress) do
- ::Onboarding::Progress.find_by(namespace: namespace)
- end
+ ::Onboarding::Progress.find_by(namespace: namespace)
end
+ strong_memoize_attr :onboarding_progress
def action_columns
- strong_memoize(:action_columns) do
+ [:code_added] +
tracked_actions.map { |action_key| ::Onboarding::Progress.column_name(action_key) }
- end
end
+ strong_memoize_attr :action_columns
def tracked_actions
ACTION_ISSUE_IDS.keys + ACTION_PATHS + deploy_section_tracked_actions
@@ -65,6 +81,6 @@ module Onboarding
end.run
end
- attr_reader :namespace, :current_user
+ attr_reader :project, :namespace, :current_user
end
end
diff --git a/app/views/admin/broadcast_messages/index.html.haml b/app/views/admin/broadcast_messages/index.html.haml
index 2fb59570231..010cc493ddf 100644
--- a/app/views/admin/broadcast_messages/index.html.haml
+++ b/app/views/admin/broadcast_messages/index.html.haml
@@ -10,16 +10,4 @@
page: params[:page] || 1,
target_access_level_options: target_access_level_options.to_json,
messages_count: @broadcast_messages.total_count,
- messages: @broadcast_messages.map { |message| {
- id: message.id,
- status: broadcast_message_status(message),
- preview: broadcast_message(message, preview: true),
- starts_at: message.starts_at.to_s,
- ends_at: message.ends_at.to_s,
- target_roles: target_access_levels_display(message.target_access_levels),
- target_path: message.target_path,
- type: message.broadcast_type.capitalize,
- edit_path: edit_admin_broadcast_message_path(message),
- delete_path: admin_broadcast_message_path(message) + '.js'
- } }.to_json
-} }
+ messages: admin_broadcast_messages_data(@broadcast_messages) } }
diff --git a/app/views/ide/_show.html.haml b/app/views/ide/_show.html.haml
index 5a6e93c3573..091d7e7a4f1 100644
--- a/app/views/ide/_show.html.haml
+++ b/app/views/ide/_show.html.haml
@@ -7,4 +7,11 @@
- content_for :prefetch_asset_tags do
- webpack_preload_asset_tag('monaco')
-= render partial: 'shared/ide_root', locals: { data: ide_data(project: @project, branch: @branch, path: @path, merge_request: @merge_request, fork_info: @fork_info), loading_text: _('Loading the GitLab IDE...') }
+- data = ide_data(project: @project,
+ branch: @branch,
+ path: @path,
+ merge_request: @merge_request,
+ fork_info: @fork_info,
+ learn_gitlab_source: @learn_gitlab_source)
+
+= render partial: 'shared/ide_root', locals: { data: data, loading_text: _('Loading the GitLab IDE...') }
diff --git a/data/deprecations/15-7-deprecate-gitlab-runner-exec-cmd.yml b/data/deprecations/15-7-deprecate-gitlab-runner-exec-cmd.yml
index cfc1362b0b6..0b52fa4d72d 100644
--- a/data/deprecations/15-7-deprecate-gitlab-runner-exec-cmd.yml
+++ b/data/deprecations/15-7-deprecate-gitlab-runner-exec-cmd.yml
@@ -3,16 +3,16 @@
removal_milestone: "17.0" # (required) The milestone when this feature is planned to be removed
breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
reporter: DarrenEastman # (required) GitLab username of the person reporting the deprecation
- stage: Verify # (required) String value of the stage that the feature was created in. e.g., Growth
- issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/385235 # (required) Link to the deprecation issue in GitLab
+ stage: Verify # (required) String value of the stage that the feature was created in. e.g., Growth
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/385235 # (required) Link to the deprecation issue in GitLab
body: | # (required) Do not modify this line, instead modify the lines below.
The [`gitlab-runner exec`](https://docs.gitlab.com/runner/commands/#gitlab-runner-exec) command is deprecated and will be fully removed from GitLab Runner in 16.0. The `gitlab-runner exec` feature was initially developed to provide the ability to validate a GitLab CI pipeline on a local system without needing to commit the updates to a GitLab instance. However, with the continued evolution of GitLab CI, replicating all GitLab CI features into `gitlab-runner exec` was no longer viable. Pipeline syntax and validation [simulation](https://docs.gitlab.com/ee/ci/pipeline_editor/#simulate-a-cicd-pipeline) are available in the GitLab pipeline editor.
end_of_support_milestone: "17.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
-# OTHER OPTIONAL FIELDS
-#
+ # OTHER OPTIONAL FIELDS
+ #
tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
- documentation_url: https://docs.gitlab.com/runner/commands/#gitlab-runner-exec # (optional) This is a link to the current documentation page
+ documentation_url: https://docs.gitlab.com/runner/commands/#gitlab-runner-exec # (optional) This is a link to the current documentation page
image_url: # (optional) This is a link to a thumbnail image depicting the feature
video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
diff --git a/data/deprecations/15-7-deprecate-kas-metrics-port-in-gitlab-chart.yml b/data/deprecations/15-7-deprecate-kas-metrics-port-in-gitlab-chart.yml
index ce26849879a..921bea87b38 100644
--- a/data/deprecations/15-7-deprecate-kas-metrics-port-in-gitlab-chart.yml
+++ b/data/deprecations/15-7-deprecate-kas-metrics-port-in-gitlab-chart.yml
@@ -2,9 +2,9 @@
announcement_milestone: "15.7" # (required) The milestone when this feature was first announced as deprecated.
removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
- reporter: timofurrer # (required) GitLab username of the person reporting the deprecation
- stage: Configure # (required) String value of the stage that the feature was created in. e.g., Growth
- issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/383039 # (required) Link to the deprecation issue in GitLab
+ reporter: timofurrer # (required) GitLab username of the person reporting the deprecation
+ stage: Configure # (required) String value of the stage that the feature was created in. e.g., Growth
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/383039 # (required) Link to the deprecation issue in GitLab
body: | # (required) Do not modify this line, instead modify the lines below.
The `gitlab.kas.metrics.port` has been deprecated in favor of the new `gitlab.kas.observability.port` configuration field for the [GitLab Helm Chart](https://gitlab.com/gitlab-org/charts/gitlab/-/merge_requests/2839).
This port is used for much more than just metrics, which warranted this change to avoid confusion in configuration.
@@ -13,7 +13,7 @@
# OTHER OPTIONAL FIELDS
#
- tiers: [Core, Premium, Ultimate] # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
- documentation_url: https://docs.gitlab.com/charts/charts/gitlab/kas/index.html # (optional) This is a link to the current documentation page
+ tiers: [Core, Premium, Ultimate] # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
+ documentation_url: https://docs.gitlab.com/charts/charts/gitlab/kas/index.html # (optional) This is a link to the current documentation page
image_url: # (optional) This is a link to a thumbnail image depicting the feature
video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
diff --git a/data/deprecations/15-7-deprecate-shimo-integration.yml b/data/deprecations/15-7-deprecate-shimo-integration.yml
index 95bb4ec141d..aa92bdf943a 100644
--- a/data/deprecations/15-7-deprecate-shimo-integration.yml
+++ b/data/deprecations/15-7-deprecate-shimo-integration.yml
@@ -2,7 +2,7 @@
announcement_milestone: "15.7" # (required) The milestone when this feature was first announced as deprecated.
removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
- reporter: arturoherrero # (required) GitLab username of the person reporting the deprecation
+ reporter: arturoherrero # (required) GitLab username of the person reporting the deprecation
stage: Manage # (required) String value of the stage that the feature was created in. e.g., Growth
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/377824 # (required) Link to the deprecation issue in GitLab
body: | # (required) Do not modify this line, instead modify the lines below.
diff --git a/data/deprecations/15-7-deprecate-zentao-integration.yml b/data/deprecations/15-7-deprecate-zentao-integration.yml
index 0925be8de60..332aaefdf04 100644
--- a/data/deprecations/15-7-deprecate-zentao-integration.yml
+++ b/data/deprecations/15-7-deprecate-zentao-integration.yml
@@ -2,7 +2,7 @@
announcement_milestone: "15.7" # (required) The milestone when this feature was first announced as deprecated.
removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
- reporter: arturoherrero # (required) GitLab username of the person reporting the deprecation
+ reporter: arturoherrero # (required) GitLab username of the person reporting the deprecation
stage: Manage # (required) String value of the stage that the feature was created in. e.g., Growth
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/377825 # (required) Link to the deprecation issue in GitLab
body: | # (required) Do not modify this line, instead modify the lines below.
diff --git a/data/deprecations/15-8-deprecate-slack-notifications-integration.yml b/data/deprecations/15-8-deprecate-slack-notifications-integration.yml
index 3cbcbc83925..8ac12d64a77 100644
--- a/data/deprecations/15-8-deprecate-slack-notifications-integration.yml
+++ b/data/deprecations/15-8-deprecate-slack-notifications-integration.yml
@@ -4,7 +4,7 @@
breaking_change: true # (required) Change to false if this is not a breaking change.
reporter: g.hickman # (required) GitLab username of the person reporting the change
stage: manage # (required) String value of the stage that the feature was created in. e.g., Growth
- issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/372411 # (required) Link to the deprecation issue in GitLab
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/372411 # (required) Link to the deprecation issue in GitLab
body: | # (required) Do not modify this line, instead modify the lines below.
As we're consolidating all Slack capabilities into the
GitLab for Slack app, we're [deprecating the Slack notifications
diff --git a/data/deprecations/15-8-deprecate-system-hook-test-endpoint.yml b/data/deprecations/15-8-deprecate-system-hook-test-endpoint.yml
index afde85f03d9..f3b2b11e63c 100644
--- a/data/deprecations/15-8-deprecate-system-hook-test-endpoint.yml
+++ b/data/deprecations/15-8-deprecate-system-hook-test-endpoint.yml
@@ -2,7 +2,7 @@
announcement_milestone: "15.8" # (required) The milestone when this feature was first announced as deprecated.
removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
- reporter: arturoherrero # (required) GitLab username of the person reporting the deprecation
+ reporter: arturoherrero # (required) GitLab username of the person reporting the deprecation
stage: Manage # (required) String value of the stage that the feature was created in. e.g., Growth
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/381572 # (required) Link to the deprecation issue in GitLab
body: | # (required) Do not modify this line, instead modify the lines below.
diff --git a/data/deprecations/15-8-deprecate-updated-at-error.yml b/data/deprecations/15-8-deprecate-updated-at-error.yml
index 9b4c5c3e007..6985803c12e 100644
--- a/data/deprecations/15-8-deprecate-updated-at-error.yml
+++ b/data/deprecations/15-8-deprecate-updated-at-error.yml
@@ -21,7 +21,7 @@
announcement_milestone: "15.8" # (required) The milestone when this feature was first announced as deprecated.
announcement_date: "2023-01-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
- removal_date: 2023-05-22 # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ removal_date: 2023-05-22 # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
breaking_change: true # (required) Change to false if this is not a breaking change.
reporter: cbalane # (required) GitLab username of the person reporting the change
stage: Release # (required) String value of the stage that the feature was created in. e.g., Growth
diff --git a/data/deprecations/15-8-jira-connect-app-cookie-auth.yml b/data/deprecations/15-8-jira-connect-app-cookie-auth.yml
index 324bcb83ae9..a9fe6e727ea 100644
--- a/data/deprecations/15-8-jira-connect-app-cookie-auth.yml
+++ b/data/deprecations/15-8-jira-connect-app-cookie-auth.yml
@@ -9,4 +9,4 @@
Cookie authentication in the GitLab for Jira Cloud app is now deprecated in favor of OAuth authentication.
You must [set up OAuth authentication](https://docs.gitlab.com/ee/integration/jira/connect-app.html#set-up-oauth-authentication)
to continue to use the GitLab for Jira Cloud app. Without OAuth, you will not be able to manage linked namespaces.
- tiers: [Core, Premium, Ultimate] # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
+ tiers: [Core, Premium, Ultimate] # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
diff --git a/data/deprecations/15-8-live-preview.yml b/data/deprecations/15-8-live-preview.yml
index 05ec5111e4a..a8fbb0e038e 100644
--- a/data/deprecations/15-8-live-preview.yml
+++ b/data/deprecations/15-8-live-preview.yml
@@ -2,7 +2,7 @@
announcement_milestone: "15.8" # (required) The milestone when this feature was first announced as deprecated.
announcement_date: "2023-01-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.9" # (required) The milestone when this feature is planned to be removed
- removal_date: "2023-02-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ removal_date: "2023-02-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
breaking_change: true # (required) Change to false if this is not a breaking change.
reporter: ericschurter # (required) GitLab username of the person reporting the change
stage: create # (required) String value of the stage that the feature was created in. e.g., Growth
diff --git a/data/deprecations/15-8-projects-api-ops-access-level.yml b/data/deprecations/15-8-projects-api-ops-access-level.yml
index 4e0fbbe63ab..06fb1b76232 100644
--- a/data/deprecations/15-8-projects-api-ops-access-level.yml
+++ b/data/deprecations/15-8-projects-api-ops-access-level.yml
@@ -21,7 +21,7 @@
announcement_milestone: "15.8" # (required) The milestone when this feature was first announced as deprecated.
announcement_date: "2023-01-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
- removal_date: 2023-05-22 # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ removal_date: 2023-05-22 # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
breaking_change: true # (required) Change to false if this is not a breaking change.
reporter: cbalane # (required) GitLab username of the person reporting the change
stage: Release # (required) String value of the stage that the feature was created in. e.g., Growth
diff --git a/data/deprecations/15-8-visual-review-tool.yml b/data/deprecations/15-8-visual-review-tool.yml
index 4833bc314d8..ced41ff3cbd 100644
--- a/data/deprecations/15-8-visual-review-tool.yml
+++ b/data/deprecations/15-8-visual-review-tool.yml
@@ -18,6 +18,6 @@
# OTHER OPTIONAL FIELDS
#
tiers: Premium # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
- documentation_url: https://docs.gitlab.com/ee/ci/review_apps/#visual-reviews # (optional) This is a link to the current documentation page
+ documentation_url: https://docs.gitlab.com/ee/ci/review_apps/#visual-reviews # (optional) This is a link to the current documentation page
image_url: # (optional) This is a link to a thumbnail image depicting the feature
video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
diff --git a/data/deprecations/15-9-accessibility-testing-deprecation.yml b/data/deprecations/15-9-accessibility-testing-deprecation.yml
index 17863cf78af..74fb61ed2fc 100644
--- a/data/deprecations/15-9-accessibility-testing-deprecation.yml
+++ b/data/deprecations/15-9-accessibility-testing-deprecation.yml
@@ -21,6 +21,6 @@
# OTHER OPTIONAL FIELDS
#
tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
- documentation_url: https://docs.gitlab.com/ee/ci/testing/accessibility_testing.html # (optional) This is a link to the current documentation page
+ documentation_url: https://docs.gitlab.com/ee/ci/testing/accessibility_testing.html # (optional) This is a link to the current documentation page
image_url: # (optional) This is a link to a thumbnail image depicting the feature
video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
diff --git a/data/deprecations/15-9-ci-builds-column-validations.yml b/data/deprecations/15-9-ci-builds-column-validations.yml
index 227b3145320..b94200dce8d 100644
--- a/data/deprecations/15-9-ci-builds-column-validations.yml
+++ b/data/deprecations/15-9-ci-builds-column-validations.yml
@@ -24,6 +24,6 @@
# OPTIONAL FIELDS
#
tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
- documentation_url: "https://docs.gitlab.com/ee/ci/yaml/#stages" # (optional) This is a link to the current documentation page
+ documentation_url: "https://docs.gitlab.com/ee/ci/yaml/#stages" # (optional) This is a link to the current documentation page
image_url: # (optional) This is a link to a thumbnail image depicting the feature
video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
diff --git a/data/deprecations/15-9-deprecate-ci-pre-clone-script.yml b/data/deprecations/15-9-deprecate-ci-pre-clone-script.yml
index 72c33915ae9..15e0802035a 100644
--- a/data/deprecations/15-9-deprecate-ci-pre-clone-script.yml
+++ b/data/deprecations/15-9-deprecate-ci-pre-clone-script.yml
@@ -13,7 +13,7 @@
# If an End of Support period applies, the announcement should be shared with GitLab Support
# in the `#spt_managers` channel in Slack, and mention `@gitlab-com/support` in this MR.
#
- end_of_support_milestone: 15.11 # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
+ end_of_support_milestone: 15.11 # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
#
# OTHER OPTIONAL FIELDS
#
diff --git a/data/deprecations/15-9-insecure-ci-job-token.yml b/data/deprecations/15-9-insecure-ci-job-token.yml
index eb40224335c..a5ccffe897b 100644
--- a/data/deprecations/15-9-insecure-ci-job-token.yml
+++ b/data/deprecations/15-9-insecure-ci-job-token.yml
@@ -14,18 +14,18 @@
In 15.9 we extended this functionality with a better solution, an "inbound" scope limit. You can prevent the job tokens from _other_ projects from being used to access your project. With this feature, you can optionally list specific projects that you want to allow to access your project with _their_ job token.
In 16.0, this inbound scope limit will be the only option available for all projects, and the outbound limit setting will be removed. To prepare for this change, you can enable the ["inbound" CI/CD job token limit](https://docs.gitlab.com/ee/ci/jobs/ci_job_token.html#configure-the-job-token-scope-limit) feature now, and list any projects that need to access your project.
-#
-# OPTIONAL END OF SUPPORT FIELDS
-#
-# If an End of Support period applies, the announcement should be shared with GitLab Support
-# in the `#spt_managers` channel in Slack, and mention `@gitlab-com/support` in this MR.
-#
+ #
+ # OPTIONAL END OF SUPPORT FIELDS
+ #
+ # If an End of Support period applies, the announcement should be shared with GitLab Support
+ # in the `#spt_managers` channel in Slack, and mention `@gitlab-com/support` in this MR.
+ #
end_of_support_milestone: # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
end_of_support_date: # (optional) The date of the milestone release when support for this feature will end.
-#
-# OTHER OPTIONAL FIELDS
-#
+ #
+ # OTHER OPTIONAL FIELDS
+ #
tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
- documentation_url: "https://docs.gitlab.com/ee/ci/jobs/ci_job_token.html#configure-the-job-token-scope-limit" # (optional) This is a link to the current documentation page
+ documentation_url: "https://docs.gitlab.com/ee/ci/jobs/ci_job_token.html#configure-the-job-token-scope-limit" # (optional) This is a link to the current documentation page
image_url: # (optional) This is a link to a thumbnail image depicting the feature
video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
diff --git a/data/deprecations/15-9-legacy-praefect-configuration.yml b/data/deprecations/15-9-legacy-praefect-configuration.yml
index 666a6d4f56a..a5ed314bb5a 100644
--- a/data/deprecations/15-9-legacy-praefect-configuration.yml
+++ b/data/deprecations/15-9-legacy-praefect-configuration.yml
@@ -29,6 +29,6 @@
# OTHER OPTIONAL FIELDS
#
tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
- documentation_url: https://docs.gitlab.com/ee/administration/gitaly/praefect.html # (optional) This is a link to the current documentation page
+ documentation_url: https://docs.gitlab.com/ee/administration/gitaly/praefect.html # (optional) This is a link to the current documentation page
image_url: # (optional) This is a link to a thumbnail image depicting the feature
video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
diff --git a/data/deprecations/15-9-remove-offset-pagination-jobs-api.yml b/data/deprecations/15-9-remove-offset-pagination-jobs-api.yml
index 8c3406a34e3..2e748f56695 100644
--- a/data/deprecations/15-9-remove-offset-pagination-jobs-api.yml
+++ b/data/deprecations/15-9-remove-offset-pagination-jobs-api.yml
@@ -18,6 +18,6 @@
# OPTIONAL FIELDS
#
tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
- documentation_url: "https://docs.gitlab.com/ee/api/jobs.html#list-project-jobs" # (optional) This is a link to the current documentation page
+ documentation_url: "https://docs.gitlab.com/ee/api/jobs.html#list-project-jobs" # (optional) This is a link to the current documentation page
image_url: # (optional) This is a link to a thumbnail image depicting the feature
video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
diff --git a/data/deprecations/15-9-secure-analyzers-bump.yml b/data/deprecations/15-9-secure-analyzers-bump.yml
index 339b0ecd78a..b144986da4a 100644
--- a/data/deprecations/15-9-secure-analyzers-bump.yml
+++ b/data/deprecations/15-9-secure-analyzers-bump.yml
@@ -45,11 +45,11 @@
# If an End of Support period applies, the announcement should be shared with GitLab Support
# in the `#spt_managers` channel in Slack, and mention `@gitlab-com/support` in this MR.
#
- end_of_support_milestone: # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
+ end_of_support_milestone: # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
#
# OTHER OPTIONAL FIELDS
#
- tiers: [Free, Silver, Gold, Core, Premium, Ultimate] # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
- documentation_url: # (optional) This is a link to the current documentation page
- image_url: # (optional) This is a link to a thumbnail image depicting the feature
- video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
+ tiers: [Free, Silver, Gold, Core, Premium, Ultimate] # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
+ documentation_url: # (optional) This is a link to the current documentation page
+ image_url: # (optional) This is a link to a thumbnail image depicting the feature
+ video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
diff --git a/data/whats_new/20230222001_15_09.yml b/data/whats_new/20230222001_15_09.yml
new file mode 100644
index 00000000000..7e7f9b808d6
--- /dev/null
+++ b/data/whats_new/20230222001_15_09.yml
@@ -0,0 +1,75 @@
+- name: Users with the Guest role can view private repositories
+ description: | # Do not modify this line, instead modify the lines below.
+ Users with the Guest role and an Ultimate license can now view private repository content if their administrator gives them permission. Administrators must create a new role through the API, and assign that role to users who the administrator wants to have view repository permissions. Previously, users with the Guest role could not view code in private projects, limiting their utility.
+ stage: manage
+ self-managed: true
+ gitlab-com: true
+ available_in: [Ultimate]
+ documentation_link: https://docs.gitlab.com/ee/user/permissions.html#custom-roles
+ image_url: https://img.youtube.com/vi/46cp_-Rtxps/hqdefault.jpg
+ published_at: 2023-02-02 # YYYY-MM-DD
+ release: 15.9
+- name: Manage license approval policies
+ description: | # Do not modify this line, instead modify the lines below.
+ GitLab now supports flexible license approval policies as the replacement for the [deprecated License-Check feature](https://docs.gitlab.com/ee/update/deprecations.html#license-check-and-the-policies-tab-on-the-license-compliance-page). License approval policies improve the experience over the License-check feature in several ways:
+
+ - Users can choose who is allowed to edit license approval policies.
+ - Multiple policy rules can be created and chained together.
+ - A two-step approval process can be enforced for any desired changes to license approval policies.
+ - A single set of license policies can be applied to multiple development projects, or can be applied at the group or subgroup level, to allow for ease in maintaining a single, centralized ruleset.
+ - Policies can be used to require approval for any license that is not specifically allowed.
+
+ License approval policies can be used alongside the existing License-Check feature, as the two policies are additive and don't conflict. To get started, verify that the `license_scanning_policies` feature flag is enabled for your instance and then navigate to **Security & Compliance > Policies**, create a new Scan Result type policy, and select **License scanning** for your policy rule.
+ stage: manage
+ self-managed: true
+ gitlab-com: true
+ available_in: [Ultimate]
+ documentation_link: https://docs.gitlab.com/ee/user/compliance/license_approval_policies.html
+ image_url: https://img.youtube.com/vi/34qBQ9t8qO8/hqdefault.jpg
+ published_at: 2023-02-02 # YYYY-MM-DD
+ release: 15.9
+- name: New License Compliance scanner
+ description: | # Do not modify this line, instead modify the lines below.
+ GitLab now supports a new method of detecting licenses that is capable of parsing and identifying over 500 different types of licenses and can extract license information from packages that are dual-licensed or have multiple different licenses that apply. In GitLab's development testing, this has empirically resulted in dramatically improved accuracy and completeness of results. Fewer CI pipeline minutes are consumed because the License Compliance job is no longer required. Additionally the new method has support for the same languages and versions as GitLab Dependency Scanning.
+
+ To use this new scanner, remove the inclusion of the `Jobs/License-Scanning.yml` template in your CI configuration and instead include the `Jobs/Dependency-Scanning.yml` template. After GitLab 16.0, the old method of scanning with the `Jobs/License-Scanning.yml` template will no longer be supported.
+
+ Currently this feature is available for GitLab SaaS users behind the `license_scanning_sbom_scanner` and `package_metadata_synchronization` feature flags. Users can follow along in GitLab to track the work to enable the [license_scanning_sbom_scanner](https://gitlab.com/gitlab-org/gitlab/-/issues/385173) and the [package_metadata_synchronization](https://gitlab.com/gitlab-org/gitlab/-/issues/390836) feature flags by default along with work to add support for [self-managed instances](https://gitlab.com/gitlab-org/gitlab/-/issues/391904) and [offline instances](https://gitlab.com/gitlab-org/gitlab/-/issues/384047).
+ stage: secure
+ self-managed: false
+ gitlab-com: true
+ available_in: [Ultimate]
+ documentation_link: https://docs.gitlab.com/ee/user/compliance/license_scanning_of_cyclonedx_files/
+ image_url: https://about.gitlab.com/images/15_9/new_license_scanner.png
+ published_at: 2023-02-02 # YYYY-MM-DD
+ release: 15.9
+- name: Notifications now available in the GitLab for Slack app
+ description: | # Do not modify this line, instead modify the lines below.
+ The GitLab for Slack app is the new home for managing notifications from GitLab to your Slack workspace. Not only can you use existing app features such as [slash commands](https://docs.gitlab.com/ee/user/project/integrations/slack_slash_commands.html), but you can now also specify which Slack channels you want to notify based on merge request changes, push events, issue changes, and many other GitLab events.
+
+ The [Slack notifications integration](https://docs.gitlab.com/ee/user/project/integrations/slack.html) is now deprecated for SaaS customers and will eventually [be removed](https://gitlab.com/groups/gitlab-org/-/epics/8673) as we continue to expand support for our GitLab for Slack app to better meet your needs.
+
+ To keep your teams in sync with what's happening in GitLab, get the [GitLab for Slack app](https://gitlab.slack.com/apps/A676ADMV5-gitlab) today!
+ stage: manage
+ self-managed: false
+ gitlab-com: true
+ available_in: [Free, Premium, Ultimate]
+ documentation_link: https://docs.gitlab.com/ee/user/project/integrations/gitlab_slack_application.html#slack-notifications
+ image_url: https://about.gitlab.com/images/15_9/slack_notifications_jan_31-optimized.png
+ published_at: 2023-02-02 # YYYY-MM-DD
+ release: 15.9
+- name: Code Suggestions available in closed beta
+ description: | # Do not modify this line, instead modify the lines below.
+ Every day millions of developers use GitLab to contribute code. We’re starting to empower developers to code more efficiently and effectively with a closed beta of Gitlab Code Suggestions.
+
+ Closed beta participants can use the GitLab Workflow VSCode extension to get code suggestions as they type. Depending on the prompt, the extension either provides entire code snippets like generating functions, or completes the current line. Simply pressing the tab key allows you to accept the suggestions.
+
+ GitLab Code Suggestions can improve developer productivity, focus, and innovation without context switching and within a single DevSecOps platform. While currently limited in closed beta, interested Ultimate customers can express interest by filling out this form to be considered for early access.
+ stage: modelops
+ self-managed: false
+ gitlab-com: true
+ available_in: [Ultimate]
+ documentation_link: https://release-15-9.about.gitlab-review.app/direction/modelops/ai_assisted/code_suggestions/
+ image_url: https://about.gitlab.com/images/15_9/DemoFastApi.gif
+ published_at: 2023-02-02 # YYYY-MM-DD
+ release: 15.9
diff --git a/db/docs/bulk_import_batch_trackers.yml b/db/docs/bulk_import_batch_trackers.yml
new file mode 100644
index 00000000000..d69888aaadd
--- /dev/null
+++ b/db/docs/bulk_import_batch_trackers.yml
@@ -0,0 +1,10 @@
+---
+table_name: bulk_import_batch_trackers
+classes: []
+feature_categories:
+- importers
+description: Used to store and track the import status of a batch of relations for the migration
+ of groups or projects
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/111708
+milestone: '15.10'
+gitlab_schema: gitlab_main
diff --git a/db/docs/bulk_import_export_batches.yml b/db/docs/bulk_import_export_batches.yml
new file mode 100644
index 00000000000..8d25e2e4a3d
--- /dev/null
+++ b/db/docs/bulk_import_export_batches.yml
@@ -0,0 +1,10 @@
+---
+table_name: bulk_import_export_batches
+classes: []
+feature_categories:
+- importers
+description: Used to track the generation status of export batch files for groups
+ or projects
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/111708
+milestone: '15.10'
+gitlab_schema: gitlab_main
diff --git a/db/migrate/20230210152109_add_bulk_import_export_batches.rb b/db/migrate/20230210152109_add_bulk_import_export_batches.rb
new file mode 100644
index 00000000000..380f8a43aae
--- /dev/null
+++ b/db/migrate/20230210152109_add_bulk_import_export_batches.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class AddBulkImportExportBatches < Gitlab::Database::Migration[2.1]
+ def up
+ create_table :bulk_import_export_batches do |t|
+ t.references :export, index: true, null: false, foreign_key: {
+ to_table: :bulk_import_exports, on_delete: :cascade
+ }
+ t.timestamps_with_timezone null: false
+ t.integer :status, limit: 2, null: false, default: 0
+ t.integer :batch_number, null: false, default: 0
+ t.integer :objects_count, null: false, default: 0
+ t.text :error, limit: 255
+ t.index [:export_id, :batch_number], unique: true, name: 'i_bulk_import_export_batches_id_batch_number'
+ end
+ end
+
+ def down
+ drop_table :bulk_import_export_batches
+ end
+end
diff --git a/db/migrate/20230210153420_add_batched_column_to_bulk_import_exports.rb b/db/migrate/20230210153420_add_batched_column_to_bulk_import_exports.rb
new file mode 100644
index 00000000000..cfe4a2059bb
--- /dev/null
+++ b/db/migrate/20230210153420_add_batched_column_to_bulk_import_exports.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddBatchedColumnToBulkImportExports < Gitlab::Database::Migration[2.1]
+ def change
+ add_column :bulk_import_exports, :batched, :boolean, null: false, default: false
+ add_column :bulk_import_exports, :batches_count, :integer, null: false, default: 0
+ add_column :bulk_import_exports, :total_objects_count, :integer, null: false, default: 0
+ end
+end
diff --git a/db/migrate/20230210155715_add_batch_id_to_bulk_import_export_uploads.rb b/db/migrate/20230210155715_add_batch_id_to_bulk_import_export_uploads.rb
new file mode 100644
index 00000000000..986d31a5839
--- /dev/null
+++ b/db/migrate/20230210155715_add_batch_id_to_bulk_import_export_uploads.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class AddBatchIdToBulkImportExportUploads < Gitlab::Database::Migration[2.1]
+ def change
+ add_column :bulk_import_export_uploads, :batch_id, :bigint
+ end
+end
diff --git a/db/migrate/20230210160037_add_batch_foreign_key_to_bulk_import_export_uploads.rb b/db/migrate/20230210160037_add_batch_foreign_key_to_bulk_import_export_uploads.rb
new file mode 100644
index 00000000000..de0286f27ed
--- /dev/null
+++ b/db/migrate/20230210160037_add_batch_foreign_key_to_bulk_import_export_uploads.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class AddBatchForeignKeyToBulkImportExportUploads < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key :bulk_import_export_uploads, :bulk_import_export_batches, column: :batch_id
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key_if_exists :bulk_import_export_uploads, column: :batch_id
+ end
+ end
+end
diff --git a/db/migrate/20230210160351_add_bulk_import_batch_trackers.rb b/db/migrate/20230210160351_add_bulk_import_batch_trackers.rb
new file mode 100644
index 00000000000..40f6341680f
--- /dev/null
+++ b/db/migrate/20230210160351_add_bulk_import_batch_trackers.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class AddBulkImportBatchTrackers < Gitlab::Database::Migration[2.1]
+ def up
+ create_table :bulk_import_batch_trackers do |t|
+ t.references :tracker, index: true, null: false, foreign_key: {
+ to_table: :bulk_import_trackers, on_delete: :cascade
+ }
+ t.timestamps_with_timezone null: false
+ t.integer :status, limit: 2, null: false, default: 0
+ t.integer :batch_number, null: false, default: 0
+ t.integer :fetched_objects_count, null: false, default: 0
+ t.integer :imported_objects_count, null: false, default: 0
+ t.text :error, limit: 255
+ t.index [:tracker_id, :batch_number], unique: true, name: 'i_bulk_import_trackers_id_batch_number'
+ end
+ end
+
+ def down
+ drop_table :bulk_import_batch_trackers
+ end
+end
diff --git a/db/migrate/20230210161002_add_batched_column_to_bulk_import_trackers.rb b/db/migrate/20230210161002_add_batched_column_to_bulk_import_trackers.rb
new file mode 100644
index 00000000000..5640164a117
--- /dev/null
+++ b/db/migrate/20230210161002_add_batched_column_to_bulk_import_trackers.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class AddBatchedColumnToBulkImportTrackers < Gitlab::Database::Migration[2.1]
+ def change
+ add_column :bulk_import_trackers, :batched, :boolean, default: false
+ end
+end
diff --git a/db/migrate/20230210171012_add_batch_id_index_to_bulk_import_export_uploads.rb b/db/migrate/20230210171012_add_batch_id_index_to_bulk_import_export_uploads.rb
new file mode 100644
index 00000000000..c2fd4198762
--- /dev/null
+++ b/db/migrate/20230210171012_add_batch_id_index_to_bulk_import_export_uploads.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class AddBatchIdIndexToBulkImportExportUploads < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'bulk_import_export_uploads_batch_id'
+
+ def up
+ add_concurrent_index :bulk_import_export_uploads, :batch_id, name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index :bulk_import_export_uploads, :batch_id, name: INDEX_NAME
+ end
+end
diff --git a/db/schema_migrations/20230210152109 b/db/schema_migrations/20230210152109
new file mode 100644
index 00000000000..cc158d38529
--- /dev/null
+++ b/db/schema_migrations/20230210152109
@@ -0,0 +1 @@
+ed74efe6b6c5428f5d1be55d1ea4d11dfb23623d092483d0d474e82312379335 \ No newline at end of file
diff --git a/db/schema_migrations/20230210153420 b/db/schema_migrations/20230210153420
new file mode 100644
index 00000000000..8ace16a1e67
--- /dev/null
+++ b/db/schema_migrations/20230210153420
@@ -0,0 +1 @@
+07d3ef18df7faefc3b86d14b37b7254ab3301392053bbe322622be8a74a56f94 \ No newline at end of file
diff --git a/db/schema_migrations/20230210155715 b/db/schema_migrations/20230210155715
new file mode 100644
index 00000000000..68ff0072b83
--- /dev/null
+++ b/db/schema_migrations/20230210155715
@@ -0,0 +1 @@
+7d0b2686ec505eb7b08df119cbb8a3c1cf033d708050de474d627df68e72c3b4 \ No newline at end of file
diff --git a/db/schema_migrations/20230210160037 b/db/schema_migrations/20230210160037
new file mode 100644
index 00000000000..f9a1697411b
--- /dev/null
+++ b/db/schema_migrations/20230210160037
@@ -0,0 +1 @@
+db0d359d329b7578c676ee137380b53d84c77c5699adb76243eb25eceda7e7e5 \ No newline at end of file
diff --git a/db/schema_migrations/20230210160351 b/db/schema_migrations/20230210160351
new file mode 100644
index 00000000000..534b07f4371
--- /dev/null
+++ b/db/schema_migrations/20230210160351
@@ -0,0 +1 @@
+0504365806c9692fff3e9aa32e371a3ddacaf8a26549929e45e271dac60992ac \ No newline at end of file
diff --git a/db/schema_migrations/20230210161002 b/db/schema_migrations/20230210161002
new file mode 100644
index 00000000000..4ba016dc519
--- /dev/null
+++ b/db/schema_migrations/20230210161002
@@ -0,0 +1 @@
+9d300a27b9c5f3e1b157d5b741c605d9a8d80a886a0a574a5946addfc0ef4998 \ No newline at end of file
diff --git a/db/schema_migrations/20230210171012 b/db/schema_migrations/20230210171012
new file mode 100644
index 00000000000..e026a7fffa9
--- /dev/null
+++ b/db/schema_migrations/20230210171012
@@ -0,0 +1 @@
+f769362c0836821687c46f824e13b30ef7c8686eebf62da8f3e8a7d3c66c0f01 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 3c182d2b80c..96c344f6fd9 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -12603,6 +12603,28 @@ CREATE SEQUENCE broadcast_messages_id_seq
ALTER SEQUENCE broadcast_messages_id_seq OWNED BY broadcast_messages.id;
+CREATE TABLE bulk_import_batch_trackers (
+ id bigint NOT NULL,
+ tracker_id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ status smallint DEFAULT 0 NOT NULL,
+ batch_number integer DEFAULT 0 NOT NULL,
+ fetched_objects_count integer DEFAULT 0 NOT NULL,
+ imported_objects_count integer DEFAULT 0 NOT NULL,
+ error text,
+ CONSTRAINT check_3d6963a51f CHECK ((char_length(error) <= 255))
+);
+
+CREATE SEQUENCE bulk_import_batch_trackers_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE bulk_import_batch_trackers_id_seq OWNED BY bulk_import_batch_trackers.id;
+
CREATE TABLE bulk_import_configurations (
id bigint NOT NULL,
bulk_import_id integer NOT NULL,
@@ -12655,11 +12677,33 @@ CREATE SEQUENCE bulk_import_entities_id_seq
ALTER SEQUENCE bulk_import_entities_id_seq OWNED BY bulk_import_entities.id;
+CREATE TABLE bulk_import_export_batches (
+ id bigint NOT NULL,
+ export_id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ status smallint DEFAULT 0 NOT NULL,
+ batch_number integer DEFAULT 0 NOT NULL,
+ objects_count integer DEFAULT 0 NOT NULL,
+ error text,
+ CONSTRAINT check_046dc60dfe CHECK ((char_length(error) <= 255))
+);
+
+CREATE SEQUENCE bulk_import_export_batches_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE bulk_import_export_batches_id_seq OWNED BY bulk_import_export_batches.id;
+
CREATE TABLE bulk_import_export_uploads (
id bigint NOT NULL,
export_id bigint NOT NULL,
updated_at timestamp with time zone NOT NULL,
export_file text,
+ batch_id bigint,
CONSTRAINT check_5add76239d CHECK ((char_length(export_file) <= 255))
);
@@ -12682,6 +12726,9 @@ CREATE TABLE bulk_import_exports (
relation text NOT NULL,
jid text,
error text,
+ batched boolean DEFAULT false NOT NULL,
+ batches_count integer DEFAULT 0 NOT NULL,
+ total_objects_count integer DEFAULT 0 NOT NULL,
CONSTRAINT check_24cb010672 CHECK ((char_length(relation) <= 255)),
CONSTRAINT check_8f0f357334 CHECK ((char_length(error) <= 255)),
CONSTRAINT check_9ee6d14d33 CHECK ((char_length(jid) <= 255))
@@ -12732,6 +12779,7 @@ CREATE TABLE bulk_import_trackers (
status smallint DEFAULT 0 NOT NULL,
created_at timestamp with time zone,
updated_at timestamp with time zone,
+ batched boolean DEFAULT false,
CONSTRAINT check_2d45cae629 CHECK ((char_length(relation) <= 255)),
CONSTRAINT check_40aeaa600b CHECK ((char_length(next_page) <= 255)),
CONSTRAINT check_603f91cb06 CHECK ((char_length(jid) <= 255)),
@@ -24334,10 +24382,14 @@ ALTER TABLE ONLY boards_epic_user_preferences ALTER COLUMN id SET DEFAULT nextva
ALTER TABLE ONLY broadcast_messages ALTER COLUMN id SET DEFAULT nextval('broadcast_messages_id_seq'::regclass);
+ALTER TABLE ONLY bulk_import_batch_trackers ALTER COLUMN id SET DEFAULT nextval('bulk_import_batch_trackers_id_seq'::regclass);
+
ALTER TABLE ONLY bulk_import_configurations ALTER COLUMN id SET DEFAULT nextval('bulk_import_configurations_id_seq'::regclass);
ALTER TABLE ONLY bulk_import_entities ALTER COLUMN id SET DEFAULT nextval('bulk_import_entities_id_seq'::regclass);
+ALTER TABLE ONLY bulk_import_export_batches ALTER COLUMN id SET DEFAULT nextval('bulk_import_export_batches_id_seq'::regclass);
+
ALTER TABLE ONLY bulk_import_export_uploads ALTER COLUMN id SET DEFAULT nextval('bulk_import_export_uploads_id_seq'::regclass);
ALTER TABLE ONLY bulk_import_exports ALTER COLUMN id SET DEFAULT nextval('bulk_import_exports_id_seq'::regclass);
@@ -26087,12 +26139,18 @@ ALTER TABLE ONLY boards
ALTER TABLE ONLY broadcast_messages
ADD CONSTRAINT broadcast_messages_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY bulk_import_batch_trackers
+ ADD CONSTRAINT bulk_import_batch_trackers_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY bulk_import_configurations
ADD CONSTRAINT bulk_import_configurations_pkey PRIMARY KEY (id);
ALTER TABLE ONLY bulk_import_entities
ADD CONSTRAINT bulk_import_entities_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY bulk_import_export_batches
+ ADD CONSTRAINT bulk_import_export_batches_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY bulk_import_export_uploads
ADD CONSTRAINT bulk_import_export_uploads_pkey PRIMARY KEY (id);
@@ -28721,6 +28779,8 @@ CREATE UNIQUE INDEX any_approver_project_rule_type_unique_index ON approval_proj
CREATE INDEX approval_mr_rule_index_merge_request_id ON approval_merge_request_rules USING btree (merge_request_id);
+CREATE INDEX bulk_import_export_uploads_batch_id ON bulk_import_export_uploads USING btree (batch_id);
+
CREATE UNIQUE INDEX bulk_import_trackers_uniq_relation_by_entity ON bulk_import_trackers USING btree (bulk_import_entity_id, relation);
CREATE INDEX ca_aggregations_last_consistency_check_updated_at ON analytics_cycle_analytics_aggregations USING btree (last_consistency_check_updated_at NULLS FIRST) WHERE (enabled IS TRUE);
@@ -28765,6 +28825,10 @@ CREATE INDEX finding_links_on_vulnerability_occurrence_id ON vulnerability_findi
CREATE INDEX i_batched_background_migration_job_transition_logs_on_job_id ON ONLY batched_background_migration_job_transition_logs USING btree (batched_background_migration_job_id);
+CREATE UNIQUE INDEX i_bulk_import_export_batches_id_batch_number ON bulk_import_export_batches USING btree (export_id, batch_number);
+
+CREATE UNIQUE INDEX i_bulk_import_trackers_id_batch_number ON bulk_import_batch_trackers USING btree (tracker_id, batch_number);
+
CREATE INDEX i_compliance_frameworks_on_id_and_created_at ON compliance_management_frameworks USING btree (id, created_at, pipeline_configuration_full_path);
CREATE INDEX i_dast_pre_scan_verification_steps_on_pre_scan_verification_id ON dast_pre_scan_verification_steps USING btree (dast_pre_scan_verification_id);
@@ -29257,6 +29321,8 @@ CREATE INDEX index_broadcast_messages_on_namespace_id ON broadcast_messages USIN
CREATE INDEX index_btree_namespaces_traversal_ids ON namespaces USING btree (traversal_ids);
+CREATE INDEX index_bulk_import_batch_trackers_on_tracker_id ON bulk_import_batch_trackers USING btree (tracker_id);
+
CREATE INDEX index_bulk_import_configurations_on_bulk_import_id ON bulk_import_configurations USING btree (bulk_import_id);
CREATE INDEX index_bulk_import_entities_on_bulk_import_id_and_status ON bulk_import_entities USING btree (bulk_import_id, status);
@@ -29267,6 +29333,8 @@ CREATE INDEX index_bulk_import_entities_on_parent_id ON bulk_import_entities USI
CREATE INDEX index_bulk_import_entities_on_project_id ON bulk_import_entities USING btree (project_id);
+CREATE INDEX index_bulk_import_export_batches_on_export_id ON bulk_import_export_batches USING btree (export_id);
+
CREATE INDEX index_bulk_import_export_uploads_on_export_id ON bulk_import_export_uploads USING btree (export_id);
CREATE INDEX index_bulk_import_failures_on_bulk_import_entity_id ON bulk_import_failures USING btree (bulk_import_entity_id);
@@ -34080,6 +34148,9 @@ ALTER TABLE ONLY zoekt_indexed_namespaces
ALTER TABLE ONLY epics
ADD CONSTRAINT fk_3c1fd1cccc FOREIGN KEY (due_date_sourcing_milestone_id) REFERENCES milestones(id) ON DELETE SET NULL;
+ALTER TABLE ONLY bulk_import_export_uploads
+ ADD CONSTRAINT fk_3cbf0b9a2e FOREIGN KEY (batch_id) REFERENCES bulk_import_export_batches(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY ci_pipelines
ADD CONSTRAINT fk_3d34ab2e06 FOREIGN KEY (pipeline_schedule_id) REFERENCES ci_pipeline_schedules(id) ON DELETE SET NULL;
@@ -35139,6 +35210,9 @@ ALTER TABLE ONLY issuable_severities
ALTER TABLE ONLY saml_providers
ADD CONSTRAINT fk_rails_306d459be7 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
+ALTER TABLE ONLY bulk_import_batch_trackers
+ ADD CONSTRAINT fk_rails_307efb9f32 FOREIGN KEY (tracker_id) REFERENCES bulk_import_trackers(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY pm_package_version_licenses
ADD CONSTRAINT fk_rails_30ddb7f837 FOREIGN KEY (pm_package_version_id) REFERENCES pm_package_versions(id) ON DELETE CASCADE;
@@ -36081,6 +36155,9 @@ ALTER TABLE ONLY elasticsearch_indexed_namespaces
ALTER TABLE ONLY vulnerability_occurrence_identifiers
ADD CONSTRAINT fk_rails_be2e49e1d0 FOREIGN KEY (identifier_id) REFERENCES vulnerability_identifiers(id) ON DELETE CASCADE;
+ALTER TABLE ONLY bulk_import_export_batches
+ ADD CONSTRAINT fk_rails_be479792f6 FOREIGN KEY (export_id) REFERENCES bulk_import_exports(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY alert_management_http_integrations
ADD CONSTRAINT fk_rails_bec49f52cc FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
diff --git a/lib/bulk_imports/clients/http.rb b/lib/bulk_imports/clients/http.rb
index 6c36875111b..61577ab0c4e 100644
--- a/lib/bulk_imports/clients/http.rb
+++ b/lib/bulk_imports/clients/http.rb
@@ -9,6 +9,7 @@ module BulkImports
DEFAULT_PAGE = 1
DEFAULT_PER_PAGE = 30
PAT_ENDPOINT_MIN_VERSION = '15.5.0'
+ SIDEKIQ_REQUEST_TIMEOUT = 60
def initialize(url:, token:, page: DEFAULT_PAGE, per_page: DEFAULT_PER_PAGE, api_version: API_VERSION)
@url = url
@@ -140,7 +141,7 @@ module BulkImports
follow_redirects: true,
resend_on_redirect: false,
limit: 2
- }
+ }.merge(request_timeout.to_h)
end
def request_query
@@ -151,6 +152,10 @@ module BulkImports
}
end
+ def request_timeout
+ { timeout: SIDEKIQ_REQUEST_TIMEOUT } if Gitlab::Runtime.sidekiq?
+ end
+
def with_error_handling
response = yield
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index d9f62121193..76fbb839683 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -25074,6 +25074,15 @@ msgstr ""
msgid "LearnGitLab|%{percentage}%{percentSymbol} completed"
msgstr ""
+msgid "LearnGitLab|1. Add code to your project"
+msgstr ""
+
+msgid "LearnGitLab|2. Build"
+msgstr ""
+
+msgid "LearnGitLab|Add code"
+msgstr ""
+
msgid "LearnGitLab|Add code owners"
msgstr ""
@@ -25155,6 +25164,9 @@ msgstr ""
msgid "LearnGitLab|Start a free trial of GitLab Ultimate"
msgstr ""
+msgid "LearnGitLab|Start with the WebIDE"
+msgstr ""
+
msgid "LearnGitLab|Submit a merge request (MR)"
msgstr ""
@@ -25164,6 +25176,9 @@ msgstr ""
msgid "LearnGitLab|Try all GitLab features for 30 days, no credit card required."
msgstr ""
+msgid "LearnGitLab|Use the built-in editor to create or upload files."
+msgstr ""
+
msgid "LearnGitLab|Use your new GitLab workflow to deploy your application, monitor its health, and keep it secure:"
msgstr ""
@@ -43575,6 +43590,9 @@ msgstr ""
msgid "There was an error fetching the jobs for your project."
msgstr ""
+msgid "There was an error fetching the number of jobs for your project."
+msgstr ""
+
msgid "There was an error fetching the top labels for the selected group"
msgstr ""
diff --git a/qa/spec/specs/helpers/feature_flag_spec.rb b/qa/spec/specs/helpers/feature_flag_spec.rb
index 5ec829161ad..9b6a52ba67c 100644
--- a/qa/spec/specs/helpers/feature_flag_spec.rb
+++ b/qa/spec/specs/helpers/feature_flag_spec.rb
@@ -151,7 +151,7 @@ RSpec.describe QA::Specs::Helpers::FeatureFlag do
it_behaves_like 'skips with given feature flag metadata', { name: 'global_ff', scope: :global }
end
- context 'when run on jh production' do
+ context 'when run on jh production', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/392832' do
before do
allow(GitlabEdition).to receive(:jh?).and_return(true)
end
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index f113ca2425f..dde2848c36c 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -354,6 +354,18 @@ FactoryBot.define do
end
end
+ trait :stubbed_commit_count do
+ after(:build) do |project|
+ stub_method(project.repository, :commit_count) { 2 }
+ end
+ end
+
+ trait :stubbed_branch_count do
+ after(:build) do |project|
+ stub_method(project.repository, :branch_count) { 2 }
+ end
+ end
+
trait :wiki_repo do
after(:create) do |project|
stub_feature_flags(main_branch_over_master: false)
diff --git a/spec/frontend/fixtures/jobs.rb b/spec/frontend/fixtures/jobs.rb
index 6d452bf1bff..3583beb83c2 100644
--- a/spec/frontend/fixtures/jobs.rb
+++ b/spec/frontend/fixtures/jobs.rb
@@ -93,4 +93,26 @@ RSpec.describe 'Jobs (JavaScript fixtures)' do
expect_graphql_errors_to_be_empty
end
end
+
+ describe 'get_jobs_count.query.graphql', type: :request do
+ let!(:build) { create(:ci_build, :success, name: 'build', pipeline: pipeline) }
+ let!(:cancelable) { create(:ci_build, :cancelable, name: 'cancelable', pipeline: pipeline) }
+ let!(:failed) { create(:ci_build, :failed, name: 'failed', pipeline: pipeline) }
+
+ fixtures_path = 'graphql/jobs/'
+ get_jobs_count_query = 'get_jobs_count.query.graphql'
+ full_path = 'frontend-fixtures/builds-project'
+
+ let_it_be(:query) do
+ get_graphql_query_as_string("jobs/components/table/graphql/queries/#{get_jobs_count_query}")
+ end
+
+ it "#{fixtures_path}#{get_jobs_count_query}.json" do
+ post_graphql(query, current_user: user, variables: {
+ fullPath: full_path
+ })
+
+ expect_graphql_errors_to_be_empty
+ end
+ end
end
diff --git a/spec/frontend/ide/stores/modules/commit/actions_spec.js b/spec/frontend/ide/stores/modules/commit/actions_spec.js
index 4068a9d0919..685935954bd 100644
--- a/spec/frontend/ide/stores/modules/commit/actions_spec.js
+++ b/spec/frontend/ide/stores/modules/commit/actions_spec.js
@@ -1,6 +1,7 @@
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
+import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import { file } from 'jest/ide/helpers';
import { commitActionTypes, PERMISSION_CREATE_MR } from '~/ide/constants';
import eventHub from '~/ide/eventhub';
@@ -39,12 +40,14 @@ describe('IDE commit module actions', () => {
let mock;
let store;
let router;
+ let trackingSpy;
beforeEach(() => {
store = createStore();
router = createRouter(store);
gon.api_version = 'v1';
mock = new MockAdapter(axios);
+ trackingSpy = mockTracking(undefined, undefined, jest.spyOn);
jest.spyOn(router, 'push').mockImplementation();
mock
@@ -53,6 +56,7 @@ describe('IDE commit module actions', () => {
});
afterEach(() => {
+ unmockTracking();
delete gon.api_version;
mock.restore();
});
@@ -430,6 +434,28 @@ describe('IDE commit module actions', () => {
});
});
});
+
+ describe('learnGitlabSource', () => {
+ describe('learnGitlabSource is true', () => {
+ it('tracks commit', async () => {
+ store.state.learnGitlabSource = true;
+
+ await store.dispatch('commit/commitChanges');
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'commit', {
+ label: 'web_ide_learn_gitlab_source',
+ });
+ });
+ });
+
+ describe('learnGitlabSource is false', () => {
+ it('does not track commit', async () => {
+ await store.dispatch('commit/commitChanges');
+
+ expect(trackingSpy).not.toHaveBeenCalled();
+ });
+ });
+ });
});
describe('success response with failed message', () => {
@@ -447,6 +473,26 @@ describe('IDE commit module actions', () => {
expect(alert.textContent.trim()).toBe('failed message');
});
+
+ describe('learnGitlabSource', () => {
+ describe('learnGitlabSource is true', () => {
+ it('does not track commit', async () => {
+ store.state.learnGitlabSource = true;
+
+ await store.dispatch('commit/commitChanges');
+
+ expect(trackingSpy).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('learnGitlabSource is false', () => {
+ it('does not track commit', async () => {
+ await store.dispatch('commit/commitChanges');
+
+ expect(trackingSpy).not.toHaveBeenCalled();
+ });
+ });
+ });
});
describe('failed response', () => {
@@ -466,6 +512,26 @@ describe('IDE commit module actions', () => {
['commit/SET_ERROR', createUnexpectedCommitError(), undefined],
]);
});
+
+ describe('learnGitlabSource', () => {
+ describe('learnGitlabSource is true', () => {
+ it('does not track commit', async () => {
+ store.state.learnGitlabSource = true;
+
+ await store.dispatch('commit/commitChanges').catch(() => {});
+
+ expect(trackingSpy).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('learnGitlabSource is false', () => {
+ it('does not track commit', async () => {
+ await store.dispatch('commit/commitChanges').catch(() => {});
+
+ expect(trackingSpy).not.toHaveBeenCalled();
+ });
+ });
+ });
});
describe('first commit of a branch', () => {
diff --git a/spec/frontend/jobs/components/table/graphql/cache_config_spec.js b/spec/frontend/jobs/components/table/graphql/cache_config_spec.js
index 88c97285b85..e3b1ca1cce3 100644
--- a/spec/frontend/jobs/components/table/graphql/cache_config_spec.js
+++ b/spec/frontend/jobs/components/table/graphql/cache_config_spec.js
@@ -84,4 +84,23 @@ describe('jobs/components/table/graphql/cache_config', () => {
expect(res.nodes).toHaveLength(CIJobConnectionIncomingCacheRunningStatus.nodes.length);
});
});
+
+ describe('when incoming data has no nodes', () => {
+ it('should return existing cache', () => {
+ const res = cacheConfig.typePolicies.CiJobConnection.merge(
+ CIJobConnectionExistingCache,
+ { __typename: 'CiJobConnection', count: 500 },
+ {
+ args: { statuses: 'SUCCESS' },
+ },
+ );
+
+ const expectedResponse = {
+ ...CIJobConnectionExistingCache,
+ statuses: 'SUCCESS',
+ };
+
+ expect(res).toEqual(expectedResponse);
+ });
+ });
});
diff --git a/spec/frontend/jobs/components/table/job_table_app_spec.js b/spec/frontend/jobs/components/table/job_table_app_spec.js
index 109cef6f817..b14a3bae54f 100644
--- a/spec/frontend/jobs/components/table/job_table_app_spec.js
+++ b/spec/frontend/jobs/components/table/job_table_app_spec.js
@@ -14,6 +14,7 @@ import waitForPromises from 'helpers/wait_for_promises';
import { TEST_HOST } from 'spec/test_constants';
import { createAlert } from '~/flash';
import getJobsQuery from '~/jobs/components/table/graphql/queries/get_jobs.query.graphql';
+import getJobsCountQuery from '~/jobs/components/table/graphql/queries/get_jobs_count.query.graphql';
import JobsTable from '~/jobs/components/table/jobs_table.vue';
import JobsTableApp from '~/jobs/components/table/jobs_table_app.vue';
import JobsTableTabs from '~/jobs/components/table/jobs_table_tabs.vue';
@@ -23,6 +24,7 @@ import {
mockJobsResponsePaginated,
mockJobsResponseEmpty,
mockFailedSearchToken,
+ mockJobsCountResponse,
} from '../../mock_data';
const projectPath = 'gitlab-org/gitlab';
@@ -37,6 +39,8 @@ describe('Job table app', () => {
const failedHandler = jest.fn().mockRejectedValue(new Error('GraphQL error'));
const emptyHandler = jest.fn().mockResolvedValue(mockJobsResponseEmpty);
+ const countSuccessHandler = jest.fn().mockResolvedValue(mockJobsCountResponse);
+
const findSkeletonLoader = () => wrapper.findComponent(GlSkeletonLoader);
const findLoadingSpinner = () => wrapper.findComponent(GlLoadingIcon);
const findTable = () => wrapper.findComponent(JobsTable);
@@ -48,14 +52,18 @@ describe('Job table app', () => {
const triggerInfiniteScroll = () =>
wrapper.findComponent(GlIntersectionObserver).vm.$emit('appear');
- const createMockApolloProvider = (handler) => {
- const requestHandlers = [[getJobsQuery, handler]];
+ const createMockApolloProvider = (handler, countHandler) => {
+ const requestHandlers = [
+ [getJobsQuery, handler],
+ [getJobsCountQuery, countHandler],
+ ];
return createMockApollo(requestHandlers);
};
const createComponent = ({
handler = successHandler,
+ countHandler = countSuccessHandler,
mountFn = shallowMount,
data = {},
} = {}) => {
@@ -68,7 +76,7 @@ describe('Job table app', () => {
provide: {
fullPath: projectPath,
},
- apolloProvider: createMockApolloProvider(handler),
+ apolloProvider: createMockApolloProvider(handler, countHandler),
});
};
@@ -148,12 +156,39 @@ describe('Job table app', () => {
});
describe('error state', () => {
- it('should show an alert if there is an error fetching the data', async () => {
+ it('should show an alert if there is an error fetching the jobs data', async () => {
createComponent({ handler: failedHandler });
await waitForPromises();
- expect(findAlert().exists()).toBe(true);
+ expect(findAlert().text()).toBe('There was an error fetching the jobs for your project.');
+ expect(findTable().exists()).toBe(false);
+ });
+
+ it('should show an alert if there is an error fetching the jobs count data', async () => {
+ createComponent({ handler: successHandler, countHandler: failedHandler });
+
+ await waitForPromises();
+
+ expect(findAlert().text()).toBe(
+ 'There was an error fetching the number of jobs for your project.',
+ );
+ });
+
+ it('jobs table should still load if count query fails', async () => {
+ createComponent({ handler: successHandler, countHandler: failedHandler });
+
+ await waitForPromises();
+
+ expect(findTable().exists()).toBe(true);
+ });
+
+ it('jobs count should be zero if count query fails', async () => {
+ createComponent({ handler: successHandler, countHandler: failedHandler });
+
+ await waitForPromises();
+
+ expect(findTabs().props('allJobsCount')).toBe(0);
});
});
diff --git a/spec/frontend/jobs/mock_data.js b/spec/frontend/jobs/mock_data.js
index 9abd610c26d..483b4ca711f 100644
--- a/spec/frontend/jobs/mock_data.js
+++ b/spec/frontend/jobs/mock_data.js
@@ -1,3 +1,4 @@
+import mockJobsCount from 'test_fixtures/graphql/jobs/get_jobs_count.query.graphql.json';
import mockJobsEmpty from 'test_fixtures/graphql/jobs/get_jobs.query.graphql.empty.json';
import mockJobsPaginated from 'test_fixtures/graphql/jobs/get_jobs.query.graphql.paginated.json';
import mockJobs from 'test_fixtures/graphql/jobs/get_jobs.query.graphql.json';
@@ -13,6 +14,7 @@ export const mockJobsResponsePaginated = mockJobsPaginated;
export const mockJobsResponseEmpty = mockJobsEmpty;
export const mockJobsNodes = mockJobs.data.project.jobs.nodes;
export const mockJobsNodesAsGuest = mockJobsAsGuest.data.project.jobs.nodes;
+export const mockJobsCountResponse = mockJobsCount;
export const stages = [
{
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/branch_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/branch_token_spec.js
index 311d5a13280..3b09cf581cc 100644
--- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/branch_token_spec.js
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/branch_token_spec.js
@@ -13,6 +13,7 @@ import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { OPTIONS_NONE_ANY } from '~/vue_shared/components/filtered_search_bar/constants';
import BranchToken from '~/vue_shared/components/filtered_search_bar/tokens/branch_token.vue';
+import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
import { mockBranches, mockBranchToken } from '../mock_data';
@@ -54,58 +55,83 @@ describe('BranchToken', () => {
let mock;
let wrapper;
+ const findBaseToken = () => wrapper.findComponent(BaseToken);
+ const triggerFetchBranches = (searchTerm = null) => {
+ findBaseToken().vm.$emit('fetch-suggestions', searchTerm);
+ return waitForPromises();
+ };
+
beforeEach(() => {
mock = new MockAdapter(axios);
});
afterEach(() => {
mock.restore();
- wrapper.destroy();
});
describe('methods', () => {
- beforeEach(() => {
- wrapper = createComponent();
- });
-
describe('fetchBranches', () => {
- it('calls `config.fetchBranches` with provided searchTerm param', () => {
- jest.spyOn(wrapper.vm.config, 'fetchBranches');
-
- wrapper.vm.fetchBranches('foo');
+ it('sets loading state', async () => {
+ wrapper = createComponent({
+ config: {
+ fetchBranches: jest.fn().mockResolvedValue(new Promise(() => {})),
+ },
+ });
+ await nextTick();
- expect(wrapper.vm.config.fetchBranches).toHaveBeenCalledWith('foo');
+ expect(findBaseToken().props('suggestionsLoading')).toBe(true);
});
- it('sets response to `branches` when request is succesful', () => {
- jest.spyOn(wrapper.vm.config, 'fetchBranches').mockResolvedValue({ data: mockBranches });
+ describe('when request is successful', () => {
+ beforeEach(() => {
+ wrapper = createComponent({
+ config: {
+ fetchBranches: jest.fn().mockResolvedValue({ data: mockBranches }),
+ },
+ });
+ });
+
+ it('calls `config.fetchBranches` with provided searchTerm param', async () => {
+ const searchTerm = 'foo';
+ await triggerFetchBranches(searchTerm);
- wrapper.vm.fetchBranches('foo');
+ expect(findBaseToken().props('config').fetchBranches).toHaveBeenCalledWith(searchTerm);
+ });
+
+ it('sets response to `branches`', async () => {
+ await triggerFetchBranches();
- return waitForPromises().then(() => {
- expect(wrapper.vm.branches).toEqual(mockBranches);
+ expect(findBaseToken().props('suggestions')).toEqual(mockBranches);
+ });
+
+ it('sets `loading` to false when request completes', async () => {
+ await triggerFetchBranches();
+
+ expect(findBaseToken().props('suggestionsLoading')).toBe(false);
});
});
- it('calls `createAlert` with flash error message when request fails', () => {
- jest.spyOn(wrapper.vm.config, 'fetchBranches').mockRejectedValue({});
+ describe('when request fails', () => {
+ beforeEach(() => {
+ wrapper = createComponent({
+ config: {
+ fetchBranches: jest.fn().mockRejectedValue({}),
+ },
+ });
+ });
- wrapper.vm.fetchBranches('foo');
+ it('calls `createAlert` with flash error message when request fails', async () => {
+ await triggerFetchBranches();
- return waitForPromises().then(() => {
expect(createAlert).toHaveBeenCalledWith({
message: 'There was a problem fetching branches.',
});
});
- });
-
- it('sets `loading` to false when request completes', () => {
- jest.spyOn(wrapper.vm.config, 'fetchBranches').mockRejectedValue({});
- wrapper.vm.fetchBranches('foo');
+ it('sets `loading` to false when request completes', async () => {
+ await triggerFetchBranches();
- return waitForPromises().then(() => {
- expect(wrapper.vm.loading).toBe(false);
+ expect(findBaseToken().props('suggestionsLoading')).toBe(false);
});
});
});
@@ -120,16 +146,13 @@ describe('BranchToken', () => {
await nextTick();
}
- beforeEach(async () => {
- wrapper = createComponent({ value: { data: mockBranches[0].name } });
-
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- branches: mockBranches,
+ beforeEach(() => {
+ wrapper = createComponent({
+ value: { data: mockBranches[0].name },
+ config: {
+ initialBranches: mockBranches,
+ },
});
-
- await nextTick();
});
it('renders gl-filtered-search-token component', () => {
diff --git a/spec/helpers/broadcast_messages_helper_spec.rb b/spec/helpers/broadcast_messages_helper_spec.rb
index d4021a2eb59..e0bdb09f257 100644
--- a/spec/helpers/broadcast_messages_helper_spec.rb
+++ b/spec/helpers/broadcast_messages_helper_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe BroadcastMessagesHelper do
+RSpec.describe BroadcastMessagesHelper, feature_category: :onboarding do
include Gitlab::Routing.url_helpers
let_it_be(:user) { create(:user) }
@@ -68,7 +68,7 @@ RSpec.describe BroadcastMessagesHelper do
end
end
- describe 'current_broadcast_notification_message' do
+ describe '#current_broadcast_notification_message' do
subject { helper.current_broadcast_notification_message }
context 'with available broadcast notification messages' do
@@ -97,7 +97,7 @@ RSpec.describe BroadcastMessagesHelper do
end
end
- describe 'current_broadcast_banner_messages' do
+ describe '#current_broadcast_banner_messages' do
describe 'user access level targeted messages' do
let_it_be(:message) { create(:broadcast_message, broadcast_type: 'banner', starts_at: Time.now, target_access_levels: [Gitlab::Access::DEVELOPER]) }
@@ -107,7 +107,7 @@ RSpec.describe BroadcastMessagesHelper do
end
end
- describe 'broadcast_message' do
+ describe '#broadcast_message' do
let(:current_broadcast_message) { BroadcastMessage.new(message: 'Current Message') }
it 'returns nil when no current message' do
@@ -119,7 +119,7 @@ RSpec.describe BroadcastMessagesHelper do
end
end
- describe 'broadcast_message_status' do
+ describe '#broadcast_message_status' do
it 'returns Active' do
message = build(:broadcast_message)
@@ -138,4 +138,23 @@ RSpec.describe BroadcastMessagesHelper do
expect(helper.broadcast_message_status(message)).to eq 'Pending'
end
end
+
+ describe '#admin_broadcast_messages_data' do
+ let(:starts_at) { 1.day.ago }
+ let(:ends_at) { 1.day.from_now }
+ let(:message) { build(:broadcast_message, id: non_existing_record_id, starts_at: starts_at, ends_at: ends_at) }
+
+ subject(:single_broadcast_message) { Gitlab::Json.parse(admin_broadcast_messages_data([message])).first }
+
+ it 'returns the expected messages data attributes' do
+ keys = %w[id status preview starts_at ends_at target_roles target_path type edit_path delete_path]
+
+ expect(single_broadcast_message.keys).to match(keys)
+ end
+
+ it 'has the correct iso formatted date', time_travel_to: '2020-01-01 00:00:00 +0000' do
+ expect(single_broadcast_message['starts_at']).to eq('2019-12-31T00:00:00Z')
+ expect(single_broadcast_message['ends_at']).to eq('2020-01-02T00:00:00Z')
+ end
+ end
end
diff --git a/spec/helpers/ide_helper_spec.rb b/spec/helpers/ide_helper_spec.rb
index e2ee4f33eee..811b7a3490c 100644
--- a/spec/helpers/ide_helper_spec.rb
+++ b/spec/helpers/ide_helper_spec.rb
@@ -18,32 +18,63 @@ RSpec.describe IdeHelper, feature_category: :web_ide do
end
it 'returns hash' do
- expect(helper.ide_data(project: project, branch: 'master', path: 'foo/README.md', merge_request: '7',
-fork_info: nil))
- .to match(
- 'can-use-new-web-ide' => 'true',
- 'use-new-web-ide' => 'true',
- 'user-preferences-path' => profile_preferences_path,
- 'new-web-ide-help-page-path' =>
- help_page_path('user/project/web_ide/index.md', anchor: 'vscode-reimplementation'),
- 'branch-name' => 'master',
- 'project-path' => project.path_with_namespace,
- 'csp-nonce' => 'test-csp-nonce',
- 'ide-remote-path' => ide_remote_path(remote_host: ':remote_host', remote_path: ':remote_path'),
- 'file-path' => 'foo/README.md',
- 'editor-font-family' => 'JetBrains Mono',
- 'editor-font-format' => 'woff2',
- 'editor-font-src-url' => a_string_matching(%r{jetbrains-mono/JetBrainsMono}),
- 'merge-request' => '7',
- 'fork-info' => nil
+ expect(
+ helper.ide_data(
+ project: project,
+ branch: 'master',
+ path: 'foo/README.md',
+ merge_request: '7',
+ fork_info: nil,
+ learn_gitlab_source: nil
)
+ ).to match(
+ 'can-use-new-web-ide' => 'true',
+ 'use-new-web-ide' => 'true',
+ 'user-preferences-path' => profile_preferences_path,
+ 'new-web-ide-help-page-path' =>
+ help_page_path('user/project/web_ide/index.md', anchor: 'vscode-reimplementation'),
+ 'branch-name' => 'master',
+ 'project-path' => project.path_with_namespace,
+ 'csp-nonce' => 'test-csp-nonce',
+ 'ide-remote-path' => ide_remote_path(remote_host: ':remote_host', remote_path: ':remote_path'),
+ 'file-path' => 'foo/README.md',
+ 'editor-font-family' => 'JetBrains Mono',
+ 'editor-font-format' => 'woff2',
+ 'editor-font-src-url' => a_string_matching(%r{jetbrains-mono/JetBrainsMono}),
+ 'merge-request' => '7',
+ 'fork-info' => nil,
+ 'learn-gitlab-source' => 'false'
+ )
end
it 'does not use new web ide if user.use_legacy_web_ide' do
allow(user).to receive(:use_legacy_web_ide).and_return(true)
- expect(helper.ide_data(project: project, branch: nil, path: nil, merge_request: nil,
-fork_info: nil)).to include('use-new-web-ide' => 'false')
+ expect(
+ helper.ide_data(
+ project: project,
+ branch: nil,
+ path: nil,
+ merge_request: nil,
+ fork_info: nil,
+ learn_gitlab_source: nil
+ )
+ ).to include('use-new-web-ide' => 'false')
+ end
+
+ it 'returns source data in the hash if learn gitlab source' do
+ allow(user).to receive(:use_legacy_web_ide).and_return(true)
+
+ expect(
+ helper.ide_data(
+ project: project,
+ branch: nil,
+ path: nil,
+ merge_request: nil,
+ fork_info: nil,
+ learn_gitlab_source: true
+ )
+ ).to include('learn-gitlab-source' => 'true')
end
end
@@ -54,18 +85,26 @@ fork_info: nil)).to include('use-new-web-ide' => 'false')
context 'when instance vars and parameters are not set' do
it 'returns instance data in the hash as nil' do
- expect(helper.ide_data(project: nil, branch: nil, path: nil, merge_request: nil, fork_info: nil))
- .to include(
- 'can-use-new-web-ide' => 'false',
- 'use-new-web-ide' => 'false',
- 'user-preferences-path' => profile_preferences_path,
- 'branch-name' => nil,
- 'file-path' => nil,
- 'merge-request' => nil,
- 'fork-info' => nil,
- 'project' => nil,
- 'preview-markdown-path' => nil
+ expect(
+ helper.ide_data(
+ project: nil,
+ branch: nil,
+ path: nil,
+ merge_request: nil,
+ fork_info: nil,
+ learn_gitlab_source: nil
)
+ ).to include(
+ 'can-use-new-web-ide' => 'false',
+ 'use-new-web-ide' => 'false',
+ 'user-preferences-path' => profile_preferences_path,
+ 'branch-name' => nil,
+ 'file-path' => nil,
+ 'merge-request' => nil,
+ 'fork-info' => nil,
+ 'project' => nil,
+ 'preview-markdown-path' => nil
+ )
end
end
@@ -75,16 +114,23 @@ fork_info: nil)).to include('use-new-web-ide' => 'false')
serialized_project = API::Entities::Project.represent(project, current_user: project.creator).to_json
- expect(helper.ide_data(project: project, branch: 'master', path: 'foo/bar', merge_request: '1',
-fork_info: fork_info))
- .to include(
- 'branch-name' => 'master',
- 'file-path' => 'foo/bar',
- 'merge-request' => '1',
- 'fork-info' => fork_info.to_json,
- 'project' => serialized_project,
- 'preview-markdown-path' => Gitlab::Routing.url_helpers.preview_markdown_project_path(project)
+ expect(
+ helper.ide_data(
+ project: project,
+ branch: 'master',
+ path: 'foo/bar',
+ merge_request: '1',
+ fork_info: fork_info,
+ learn_gitlab_source: nil
)
+ ).to include(
+ 'branch-name' => 'master',
+ 'file-path' => 'foo/bar',
+ 'merge-request' => '1',
+ 'fork-info' => fork_info.to_json,
+ 'project' => serialized_project,
+ 'preview-markdown-path' => Gitlab::Routing.url_helpers.preview_markdown_project_path(project)
+ )
end
end
@@ -95,8 +141,16 @@ fork_info: fork_info))
context 'when project has no enviornments' do
it 'enables environment guidance' do
- expect(helper.ide_data(project: project, branch: nil, path: nil, merge_request: nil,
-fork_info: nil)).to include('enable-environments-guidance' => 'true')
+ expect(
+ helper.ide_data(
+ project: project,
+ branch: nil,
+ path: nil,
+ merge_request: nil,
+ fork_info: nil,
+ learn_gitlab_source: nil
+ )
+ ).to include('enable-environments-guidance' => 'true')
end
context 'and the callout has been dismissed' do
@@ -104,8 +158,17 @@ fork_info: nil)).to include('enable-environments-guidance' => 'true')
callout = create(:callout, feature_name: :web_ide_ci_environments_guidance, user: project.creator)
callout.update!(dismissed_at: Time.now - 1.week)
allow(helper).to receive(:current_user).and_return(User.find(project.creator.id))
- expect(helper.ide_data(project: project, branch: nil, path: nil, merge_request: nil,
-fork_info: nil)).to include('enable-environments-guidance' => 'false')
+
+ expect(
+ helper.ide_data(
+ project: project,
+ branch: nil,
+ path: nil,
+ merge_request: nil,
+ fork_info: nil,
+ learn_gitlab_source: nil
+ )
+ ).to include('enable-environments-guidance' => 'false')
end
end
end
@@ -114,8 +177,16 @@ fork_info: nil)).to include('enable-environments-guidance' => 'false')
it 'disables environment guidance' do
create(:environment, project: project)
- expect(helper.ide_data(project: project, branch: nil, path: nil, merge_request: nil,
-fork_info: nil)).to include('enable-environments-guidance' => 'false')
+ expect(
+ helper.ide_data(
+ project: project,
+ branch: nil,
+ path: nil,
+ merge_request: nil,
+ fork_info: nil,
+ learn_gitlab_source: nil
+ )
+ ).to include('enable-environments-guidance' => 'false')
end
end
end
diff --git a/spec/lib/bulk_imports/clients/http_spec.rb b/spec/lib/bulk_imports/clients/http_spec.rb
index 780f61f8c61..09753b3c23d 100644
--- a/spec/lib/bulk_imports/clients/http_spec.rb
+++ b/spec/lib/bulk_imports/clients/http_spec.rb
@@ -10,6 +10,7 @@ RSpec.describe BulkImports::Clients::HTTP, feature_category: :importers do
let(:resource) { 'resource' }
let(:version) { "#{BulkImport::MIN_MAJOR_VERSION}.0.0" }
let(:enterprise) { false }
+ let(:sidekiq_request_timeout) { described_class::SIDEKIQ_REQUEST_TIMEOUT }
let(:response_double) { double(code: 200, success?: true, parsed_response: {}) }
let(:metadata_response) do
double(
@@ -123,6 +124,36 @@ RSpec.describe BulkImports::Clients::HTTP, feature_category: :importers do
allow(Gitlab::HTTP).to receive(:get).with(uri, params).and_return(response)
end
end
+
+ context 'when the request is asynchronous' do
+ let(:expected_args) do
+ [
+ 'http://gitlab.example/api/v4/resource',
+ hash_including(
+ query: {
+ page: described_class::DEFAULT_PAGE,
+ per_page: described_class::DEFAULT_PER_PAGE,
+ private_token: token
+ },
+ headers: {
+ 'Content-Type' => 'application/json'
+ },
+ follow_redirects: true,
+ resend_on_redirect: false,
+ limit: 2,
+ timeout: sidekiq_request_timeout
+ )
+ ]
+ end
+
+ it 'sets a timeout that is double the default read timeout' do
+ allow(Gitlab::Runtime).to receive(:sidekiq?).and_return(true)
+
+ expect(Gitlab::HTTP).to receive(method).with(*expected_args).and_return(response_double)
+
+ subject.public_send(method, resource)
+ end
+ end
end
describe '#post' do
diff --git a/spec/models/onboarding/completion_spec.rb b/spec/models/onboarding/completion_spec.rb
index e1fad4255bc..175f8d6ef68 100644
--- a/spec/models/onboarding/completion_spec.rb
+++ b/spec/models/onboarding/completion_spec.rb
@@ -2,10 +2,14 @@
require 'spec_helper'
-RSpec.describe Onboarding::Completion do
+RSpec.describe Onboarding::Completion, feature_category: :onboarding do
+ let(:completed_actions) { {} }
+ let(:project) { build(:project, namespace: namespace) }
+ let!(:onboarding_progress) { create(:onboarding_progress, namespace: namespace, **completed_actions) }
+
+ let_it_be(:namespace) { create(:namespace) }
+
describe '#percentage' do
- let(:completed_actions) { {} }
- let!(:onboarding_progress) { create(:onboarding_progress, namespace: namespace, **completed_actions) }
let(:tracked_action_columns) do
[
*described_class::ACTION_ISSUE_IDS.keys,
@@ -14,12 +18,10 @@ RSpec.describe Onboarding::Completion do
].map { |key| ::Onboarding::Progress.column_name(key) }
end
- let_it_be(:namespace) { create(:namespace) }
-
- subject { described_class.new(namespace).percentage }
+ subject(:percentage) { described_class.new(project).percentage }
context 'when no onboarding_progress exists' do
- subject { described_class.new(build(:namespace)).percentage }
+ subject(:percentage) { described_class.new(build(:project)).percentage }
it { is_expected.to eq(0) }
end
@@ -29,6 +31,8 @@ RSpec.describe Onboarding::Completion do
end
context 'when all tracked actions have been completed' do
+ let(:project) { build(:project, :stubbed_commit_count, namespace: namespace) }
+
let(:completed_actions) do
tracked_action_columns.index_with { Time.current }
end
@@ -44,7 +48,7 @@ RSpec.describe Onboarding::Completion do
stub_experiments(security_actions_continuous_onboarding: :control)
end
- it { is_expected.to eq(11) }
+ it { is_expected.to eq(10) }
end
context 'when candidate' do
@@ -52,7 +56,50 @@ RSpec.describe Onboarding::Completion do
stub_experiments(security_actions_continuous_onboarding: :candidate)
end
- it { is_expected.to eq(9) }
+ it { is_expected.to eq(8) }
+ end
+ end
+ end
+
+ describe '#completed?' do
+ subject(:completed?) { described_class.new(project).completed?(column) }
+
+ context 'when code_added' do
+ let(:column) { :code_added }
+
+ context 'when commit_count > 1' do
+ let(:project) { build(:project, :stubbed_commit_count, namespace: namespace) }
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'when branch_count > 1' do
+ let(:project) { build(:project, :stubbed_branch_count, namespace: namespace) }
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'when empty repository' do
+ let(:project) { build(:project, namespace: namespace) }
+
+ it { is_expected.to eq(false) }
+ end
+ end
+
+ context 'when security_scan_enabled' do
+ let(:column) { :security_scan_enabled_at }
+ let(:completed_actions) { { security_scan_enabled_at: security_scan_enabled_at } }
+
+ context 'when is completed' do
+ let(:security_scan_enabled_at) { Time.current }
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'when is not completed' do
+ let(:security_scan_enabled_at) { nil }
+
+ it { is_expected.to eq(false) }
end
end
end
diff --git a/spec/requests/ide_controller_spec.rb b/spec/requests/ide_controller_spec.rb
index 6e79943e83d..31a53949f2f 100644
--- a/spec/requests/ide_controller_spec.rb
+++ b/spec/requests/ide_controller_spec.rb
@@ -130,10 +130,27 @@ RSpec.describe IdeController, feature_category: :web_ide do
expect(assigns(:path)).to be_nil
expect(assigns(:merge_request)).to be_nil
expect(assigns(:fork_info)).to be_nil
+ expect(assigns(:learn_gitlab_source)).to be_nil
end
it_behaves_like 'user access rights check'
+ context "/-/ide/project/:project?learn_gitlab_source=true" do
+ let(:route) { "/-/ide/project/#{project.full_path}?learn_gitlab_source=true" }
+
+ it 'instantiates project instance var and returns 200' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(assigns(:project)).to eq project
+ expect(assigns(:branch)).to be_nil
+ expect(assigns(:path)).to be_nil
+ expect(assigns(:merge_request)).to be_nil
+ expect(assigns(:fork_info)).to be_nil
+ expect(assigns(:learn_gitlab_source)).to eq 'true'
+ end
+ end
+
%w(edit blob tree).each do |action|
context "/-/ide/project/:project/#{action}" do
let(:route) { "/-/ide/project/#{project.full_path}/#{action}" }
@@ -147,6 +164,7 @@ RSpec.describe IdeController, feature_category: :web_ide do
expect(assigns(:path)).to be_nil
expect(assigns(:merge_request)).to be_nil
expect(assigns(:fork_info)).to be_nil
+ expect(assigns(:learn_gitlab_source)).to be_nil
end
it_behaves_like 'user access rights check'
@@ -164,6 +182,7 @@ RSpec.describe IdeController, feature_category: :web_ide do
expect(assigns(:path)).to be_nil
expect(assigns(:merge_request)).to be_nil
expect(assigns(:fork_info)).to be_nil
+ expect(assigns(:learn_gitlab_source)).to be_nil
end
it_behaves_like 'user access rights check'
@@ -181,6 +200,7 @@ RSpec.describe IdeController, feature_category: :web_ide do
expect(assigns(:path)).to be_nil
expect(assigns(:merge_request)).to be_nil
expect(assigns(:fork_info)).to be_nil
+ expect(assigns(:learn_gitlab_source)).to be_nil
end
it_behaves_like 'user access rights check'
@@ -198,6 +218,7 @@ RSpec.describe IdeController, feature_category: :web_ide do
expect(assigns(:path)).to eq 'foo/.bar'
expect(assigns(:merge_request)).to be_nil
expect(assigns(:fork_info)).to be_nil
+ expect(assigns(:learn_gitlab_source)).to be_nil
end
it_behaves_like 'user access rights check'
@@ -221,6 +242,7 @@ RSpec.describe IdeController, feature_category: :web_ide do
expect(assigns(:path)).to be_nil
expect(assigns(:merge_request)).to eq merge_request.id.to_s
expect(assigns(:fork_info)).to be_nil
+ expect(assigns(:learn_gitlab_source)).to be_nil
end
it_behaves_like 'user access rights check'