summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-05-13 15:10:20 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-05-13 15:10:20 +0000
commite958867b2e341329243be8db0c262233ae1238c0 (patch)
treec5d57d56735b4cf750f14a49ca576920b0458a85
parent3748ae5cbbefd3de0111951e71e74b676c276d61 (diff)
downloadgitlab-ce-e958867b2e341329243be8db0c262233ae1238c0.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/awards_handler.js1
-rw-r--r--app/assets/javascripts/behaviors/date_picker.js33
-rw-r--r--app/assets/javascripts/due_date_select.js33
-rw-r--r--app/assets/javascripts/init_issuable_sidebar.js2
-rw-r--r--app/assets/javascripts/logs/stores/actions.js2
-rw-r--r--app/assets/javascripts/pages/groups/milestones/edit/index.js2
-rw-r--r--app/assets/javascripts/pages/groups/milestones/new/index.js2
-rw-r--r--app/assets/javascripts/pages/groups/settings/repository/show/index.js5
-rw-r--r--app/assets/javascripts/pages/projects/milestones/new/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/settings/repository/form.js4
-rw-r--r--app/assets/javascripts/shared/milestones/form.js5
-rw-r--r--app/graphql/resolvers/group_packages_resolver.rb28
-rw-r--r--app/graphql/resolvers/packages_base_resolver.rb53
-rw-r--r--app/graphql/resolvers/project_packages_resolver.rb32
-rw-r--r--app/helpers/environments_helper.rb2
-rw-r--r--app/models/ci/pipeline_schedule.rb28
-rw-r--r--app/models/concerns/cron_schedulable.rb41
-rw-r--r--app/services/web_hook_service.rb3
-rw-r--r--changelogs/unreleased/285467-package-registry-graphql-api-3.yml5
-rw-r--r--changelogs/unreleased/ph-enableImprovedEmojiPicker.yml5
-rw-r--r--config/feature_flags/development/improved_emoji_picker.yml2
-rw-r--r--config/initializers/1_settings.rb6
-rw-r--r--doc/api/graphql/reference/index.md8
-rw-r--r--doc/api/notes.md8
-rw-r--r--doc/subscriptions/gitlab_com/index.md5
-rw-r--r--doc/topics/git/bisect.md76
-rw-r--r--doc/topics/git/feature_branching.md31
-rw-r--r--doc/topics/git/getting_started.md86
-rw-r--r--doc/topics/git/git_add.md40
-rw-r--r--doc/topics/git/git_log.md60
-rw-r--r--doc/topics/git/index.md2
-rw-r--r--doc/topics/git/merge_conflicts.md69
-rw-r--r--doc/topics/git/merge_requests.md40
-rw-r--r--doc/topics/git/rollback_commits.md81
-rw-r--r--doc/topics/git/stash.md82
-rw-r--r--doc/topics/git/subtree.md52
-rw-r--r--doc/topics/git/unstage.md33
-rw-r--r--doc/university/training/topics/bisect.md76
-rw-r--r--doc/university/training/topics/feature_branching.md31
-rw-r--r--doc/university/training/topics/getting_started.md86
-rw-r--r--doc/university/training/topics/git_add.md40
-rw-r--r--doc/university/training/topics/git_log.md60
-rw-r--r--doc/university/training/topics/merge_conflicts.md69
-rw-r--r--doc/university/training/topics/merge_requests.md40
-rw-r--r--doc/university/training/topics/rollback_commits.md81
-rw-r--r--doc/university/training/topics/stash.md82
-rw-r--r--doc/university/training/topics/subtree.md52
-rw-r--r--doc/university/training/topics/unstage.md33
-rw-r--r--doc/user/group/value_stream_analytics/img/vsa_stage_table_v13_12.pngbin105403 -> 81442 bytes
-rw-r--r--doc/user/group/value_stream_analytics/index.md5
-rw-r--r--doc/user/project/repository/branches/index.md2
-rw-r--r--package.json4
-rw-r--r--spec/features/issues/user_interacts_with_awards_spec.rb53
-rw-r--r--spec/frontend/behaviors/date_picker_spec.js30
-rw-r--r--spec/frontend/logs/stores/actions_spec.js2
-rw-r--r--spec/graphql/resolvers/group_packages_resolver_spec.rb34
-rw-r--r--spec/graphql/resolvers/packages_base_resolver_spec.rb15
-rw-r--r--spec/graphql/resolvers/project_packages_resolver_spec.rb34
-rw-r--r--spec/helpers/environments_helper_spec.rb2
-rw-r--r--spec/models/ci/pipeline_schedule_spec.rb28
-rw-r--r--spec/models/concerns/cron_schedulable_spec.rb17
-rw-r--r--spec/requests/api/graphql/group/packages_spec.rb84
-rw-r--r--spec/requests/api/graphql/project/packages_spec.rb68
-rw-r--r--spec/services/web_hook_service_spec.rb19
-rw-r--r--spec/support/shared_examples/graphql/resolvers/packages_resolvers_shared_examples.rb63
-rw-r--r--spec/support/shared_examples/models/concerns/cron_schedulable_shared_examples.rb23
-rw-r--r--spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb129
-rw-r--r--yarn.lock16
68 files changed, 1237 insertions, 1010 deletions
diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js
index 3a2f2078e44..43f44370af8 100644
--- a/app/assets/javascripts/awards_handler.js
+++ b/app/assets/javascripts/awards_handler.js
@@ -12,7 +12,6 @@ import axios from './lib/utils/axios_utils';
import { isInVueNoteablePage } from './lib/utils/dom_utils';
import { __ } from './locale';
-window.axios = axios;
const animationEndEventString = 'animationend webkitAnimationEnd MSAnimationEnd oAnimationEnd';
const transitionEndEventString = 'transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd';
diff --git a/app/assets/javascripts/behaviors/date_picker.js b/app/assets/javascripts/behaviors/date_picker.js
new file mode 100644
index 00000000000..efd89ec4330
--- /dev/null
+++ b/app/assets/javascripts/behaviors/date_picker.js
@@ -0,0 +1,33 @@
+import $ from 'jquery';
+import Pikaday from 'pikaday';
+import { parsePikadayDate, pikadayToString } from '~/lib/utils/datetime_utility';
+
+export default function initDatePickers() {
+ $('.datepicker').each(function initPikaday() {
+ const $datePicker = $(this);
+ const datePickerVal = $datePicker.val();
+
+ const calendar = new Pikaday({
+ field: $datePicker.get(0),
+ theme: 'gitlab-theme animate-picker',
+ format: 'yyyy-mm-dd',
+ container: $datePicker.parent().get(0),
+ parse: (dateString) => parsePikadayDate(dateString),
+ toString: (date) => pikadayToString(date),
+ onSelect(dateText) {
+ $datePicker.val(calendar.toString(dateText));
+ },
+ firstDay: gon.first_day_of_week,
+ });
+
+ calendar.setDate(parsePikadayDate(datePickerVal));
+
+ $datePicker.data('pikaday', calendar);
+ });
+
+ $('.js-clear-due-date,.js-clear-start-date').on('click', (e) => {
+ e.preventDefault();
+ const calendar = $(e.target).siblings('.datepicker').data('pikaday');
+ calendar.setDate(null);
+ });
+}
diff --git a/app/assets/javascripts/due_date_select.js b/app/assets/javascripts/due_date_select.js
index 1f57d73d3d3..aa223270f2c 100644
--- a/app/assets/javascripts/due_date_select.js
+++ b/app/assets/javascripts/due_date_select.js
@@ -2,6 +2,7 @@
import dateFormat from 'dateformat';
import $ from 'jquery';
import Pikaday from 'pikaday';
+import initDatePicker from '~/behaviors/date_picker';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
import { __ } from '~/locale';
import boardsStore from './boards/stores/boards_store';
@@ -168,40 +169,10 @@ class DueDateSelect {
export default class DueDateSelectors {
constructor() {
- this.initMilestoneDatePicker();
+ initDatePicker();
this.initIssuableSelect();
}
// eslint-disable-next-line class-methods-use-this
- initMilestoneDatePicker() {
- $('.datepicker').each(function initPikadayMilestone() {
- const $datePicker = $(this);
- const datePickerVal = $datePicker.val();
-
- const calendar = new Pikaday({
- field: $datePicker.get(0),
- theme: 'gitlab-theme animate-picker',
- format: 'yyyy-mm-dd',
- container: $datePicker.parent().get(0),
- parse: (dateString) => parsePikadayDate(dateString),
- toString: (date) => pikadayToString(date),
- onSelect(dateText) {
- $datePicker.val(calendar.toString(dateText));
- },
- firstDay: gon.first_day_of_week,
- });
-
- calendar.setDate(parsePikadayDate(datePickerVal));
-
- $datePicker.data('pikaday', calendar);
- });
-
- $('.js-clear-due-date,.js-clear-start-date').on('click', (e) => {
- e.preventDefault();
- const calendar = $(e.target).siblings('.datepicker').data('pikaday');
- calendar.setDate(null);
- });
- }
- // eslint-disable-next-line class-methods-use-this
initIssuableSelect() {
const $loading = $('.js-issuable-update .due_date')
.find('.block-loading')
diff --git a/app/assets/javascripts/init_issuable_sidebar.js b/app/assets/javascripts/init_issuable_sidebar.js
index 59038b3d9fb..17c73fdf1c3 100644
--- a/app/assets/javascripts/init_issuable_sidebar.js
+++ b/app/assets/javascripts/init_issuable_sidebar.js
@@ -1,7 +1,6 @@
/* eslint-disable no-new */
import { mountSidebarLabels, getSidebarOptions } from '~/sidebar/mount_sidebar';
-import DueDateSelectors from './due_date_select';
import IssuableContext from './issuable_context';
import LabelsSelect from './labels_select';
import MilestoneSelect from './milestone_select';
@@ -19,7 +18,6 @@ export default () => {
});
new LabelsSelect();
new IssuableContext(sidebarOptions.currentUser);
- new DueDateSelectors();
Sidebar.initialize();
mountSidebarLabels();
diff --git a/app/assets/javascripts/logs/stores/actions.js b/app/assets/javascripts/logs/stores/actions.js
index e813f91d2fa..c3dc9f4bc12 100644
--- a/app/assets/javascripts/logs/stores/actions.js
+++ b/app/assets/javascripts/logs/stores/actions.js
@@ -127,7 +127,7 @@ export const fetchEnvironments = ({ commit, dispatch }, environmentsPath) => {
return axios
.get(environmentsPath)
.then(({ data }) => {
- commit(types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS, data.environments);
+ commit(types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS, data);
dispatch('fetchLogs', tracking.ENVIRONMENT_SELECTED);
})
.catch(() => {
diff --git a/app/assets/javascripts/pages/groups/milestones/edit/index.js b/app/assets/javascripts/pages/groups/milestones/edit/index.js
index 364b0d95d9c..4f8514a9a1d 100644
--- a/app/assets/javascripts/pages/groups/milestones/edit/index.js
+++ b/app/assets/javascripts/pages/groups/milestones/edit/index.js
@@ -1,3 +1,3 @@
-import initForm from '../../../../shared/milestones/form';
+import initForm from '~/shared/milestones/form';
initForm();
diff --git a/app/assets/javascripts/pages/groups/milestones/new/index.js b/app/assets/javascripts/pages/groups/milestones/new/index.js
index 364b0d95d9c..4f8514a9a1d 100644
--- a/app/assets/javascripts/pages/groups/milestones/new/index.js
+++ b/app/assets/javascripts/pages/groups/milestones/new/index.js
@@ -1,3 +1,3 @@
-import initForm from '../../../../shared/milestones/form';
+import initForm from '~/shared/milestones/form';
initForm();
diff --git a/app/assets/javascripts/pages/groups/settings/repository/show/index.js b/app/assets/javascripts/pages/groups/settings/repository/show/index.js
index 92405f205cb..f048955dadf 100644
--- a/app/assets/javascripts/pages/groups/settings/repository/show/index.js
+++ b/app/assets/javascripts/pages/groups/settings/repository/show/index.js
@@ -1,7 +1,8 @@
-import DueDateSelectors from '~/due_date_select';
+import initDatePicker from '~/behaviors/date_picker';
import initSettingsPanels from '~/settings_panels';
// Initialize expandable settings panels
initSettingsPanels();
-new DueDateSelectors(); // eslint-disable-line no-new
+// Used for deploy tokens "expires at" field
+initDatePicker();
diff --git a/app/assets/javascripts/pages/projects/milestones/new/index.js b/app/assets/javascripts/pages/projects/milestones/new/index.js
index 364b0d95d9c..4f8514a9a1d 100644
--- a/app/assets/javascripts/pages/projects/milestones/new/index.js
+++ b/app/assets/javascripts/pages/projects/milestones/new/index.js
@@ -1,3 +1,3 @@
-import initForm from '../../../../shared/milestones/form';
+import initForm from '~/shared/milestones/form';
initForm();
diff --git a/app/assets/javascripts/pages/projects/settings/repository/form.js b/app/assets/javascripts/pages/projects/settings/repository/form.js
index 8d390c8586b..380091a3501 100644
--- a/app/assets/javascripts/pages/projects/settings/repository/form.js
+++ b/app/assets/javascripts/pages/projects/settings/repository/form.js
@@ -1,7 +1,7 @@
/* eslint-disable no-new */
+import initDatePicker from '~/behaviors/date_picker';
import initDeployKeys from '~/deploy_keys';
-import DueDateSelectors from '~/due_date_select';
import fileUpload from '~/lib/utils/file_upload';
import ProtectedBranchCreate from '~/protected_branches/protected_branch_create';
import ProtectedBranchEditList from '~/protected_branches/protected_branch_edit_list';
@@ -16,6 +16,6 @@ export default () => {
initSettingsPanels();
new ProtectedBranchCreate({ hasLicense: false });
new ProtectedBranchEditList();
- new DueDateSelectors();
+ initDatePicker(); // Used for deploy token "expires at" field
fileUpload('.js-choose-file', '.js-object-map-input');
};
diff --git a/app/assets/javascripts/shared/milestones/form.js b/app/assets/javascripts/shared/milestones/form.js
index 467cd321fb8..3ca9288b156 100644
--- a/app/assets/javascripts/shared/milestones/form.js
+++ b/app/assets/javascripts/shared/milestones/form.js
@@ -1,11 +1,12 @@
import $ from 'jquery';
-import DueDateSelectors from '../../due_date_select';
+import initDatePicker from '~/behaviors/date_picker';
import GLForm from '../../gl_form';
import ZenMode from '../../zen_mode';
export default (initGFM = true) => {
new ZenMode(); // eslint-disable-line no-new
- new DueDateSelectors(); // eslint-disable-line no-new
+ initDatePicker();
+
// eslint-disable-next-line no-new
new GLForm($('.milestone-form'), {
emojis: true,
diff --git a/app/graphql/resolvers/group_packages_resolver.rb b/app/graphql/resolvers/group_packages_resolver.rb
index 3849b870b40..d91fe84317d 100644
--- a/app/graphql/resolvers/group_packages_resolver.rb
+++ b/app/graphql/resolvers/group_packages_resolver.rb
@@ -1,26 +1,19 @@
# frozen_string_literal: true
+# rubocop: disable Graphql/ResolverType
module Resolvers
- class GroupPackagesResolver < BaseResolver
- type Types::Packages::PackageType.connection_type, null: true
+ class GroupPackagesResolver < PackagesBaseResolver
+ # The GraphQL type is defined in the extended class
argument :sort, Types::Packages::PackageGroupSortEnum,
description: 'Sort packages by this criteria.',
required: false,
default_value: :created_desc
- SORT_TO_PARAMS_MAP = {
- created_desc: { order_by: 'created', sort: 'desc' },
- created_asc: { order_by: 'created', sort: 'asc' },
- name_desc: { order_by: 'name', sort: 'desc' },
- name_asc: { order_by: 'name', sort: 'asc' },
- version_desc: { order_by: 'version', sort: 'desc' },
- version_asc: { order_by: 'version', sort: 'asc' },
- type_desc: { order_by: 'type', sort: 'desc' },
- type_asc: { order_by: 'type', sort: 'asc' },
+ GROUP_SORT_TO_PARAMS_MAP = SORT_TO_PARAMS_MAP.merge({
project_path_desc: { order_by: 'project_path', sort: 'desc' },
project_path_asc: { order_by: 'project_path', sort: 'asc' }
- }.freeze
+ }).freeze
def ready?(**args)
context[self.class] ||= { executions: 0 }
@@ -30,16 +23,11 @@ module Resolvers
super
end
- def resolve(sort:)
+ def resolve(sort:, **filters)
return unless packages_available?
- ::Packages::GroupPackagesFinder.new(current_user, object, SORT_TO_PARAMS_MAP[sort]).execute
- end
-
- private
-
- def packages_available?
- ::Gitlab.config.packages.enabled
+ ::Packages::GroupPackagesFinder.new(current_user, object, filters.merge(GROUP_SORT_TO_PARAMS_MAP.fetch(sort))).execute
end
end
end
+# rubocop: enable Graphql/ResolverType
diff --git a/app/graphql/resolvers/packages_base_resolver.rb b/app/graphql/resolvers/packages_base_resolver.rb
new file mode 100644
index 00000000000..3378cc32c9f
--- /dev/null
+++ b/app/graphql/resolvers/packages_base_resolver.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class PackagesBaseResolver < BaseResolver
+ type Types::Packages::PackageType.connection_type, null: true
+
+ argument :sort, Types::Packages::PackageSortEnum,
+ description: 'Sort packages by this criteria.',
+ required: false,
+ default_value: :created_desc
+
+ argument :package_name, GraphQL::STRING_TYPE,
+ description: 'Search a package by name.',
+ required: false,
+ default_value: nil
+
+ argument :package_type, Types::Packages::PackageTypeEnum,
+ description: 'Filter a package by type.',
+ required: false,
+ default_value: nil
+
+ argument :status, Types::Packages::PackageStatusEnum,
+ description: 'Filter a package by status.',
+ required: false,
+ default_value: nil
+
+ argument :include_versionless, GraphQL::BOOLEAN_TYPE,
+ description: 'Include versionless packages.',
+ required: false,
+ default_value: false
+
+ SORT_TO_PARAMS_MAP = {
+ created_desc: { order_by: 'created', sort: 'desc' },
+ created_asc: { order_by: 'created', sort: 'asc' },
+ name_desc: { order_by: 'name', sort: 'desc' },
+ name_asc: { order_by: 'name', sort: 'asc' },
+ version_desc: { order_by: 'version', sort: 'desc' },
+ version_asc: { order_by: 'version', sort: 'asc' },
+ type_desc: { order_by: 'type', sort: 'desc' },
+ type_asc: { order_by: 'type', sort: 'asc' }
+ }.freeze
+
+ def resolve
+ raise NotImplementedError
+ end
+
+ private
+
+ def packages_available?
+ ::Gitlab.config.packages.enabled
+ end
+ end
+end
diff --git a/app/graphql/resolvers/project_packages_resolver.rb b/app/graphql/resolvers/project_packages_resolver.rb
index 2f90a2fda1f..6d66c2fe460 100644
--- a/app/graphql/resolvers/project_packages_resolver.rb
+++ b/app/graphql/resolvers/project_packages_resolver.rb
@@ -1,35 +1,15 @@
# frozen_string_literal: true
+# rubocop: disable Graphql/ResolverType
module Resolvers
- class ProjectPackagesResolver < BaseResolver
- type Types::Packages::PackageType.connection_type, null: true
+ class ProjectPackagesResolver < PackagesBaseResolver
+ # The GraphQL type is defined in the extended class
- argument :sort, Types::Packages::PackageSortEnum,
- description: 'Sort packages by this criteria.',
- required: false,
- default_value: :created_desc
-
- SORT_TO_PARAMS_MAP = {
- created_desc: { order_by: 'created', sort: 'desc' },
- created_asc: { order_by: 'created', sort: 'asc' },
- name_desc: { order_by: 'name', sort: 'desc' },
- name_asc: { order_by: 'name', sort: 'asc' },
- version_desc: { order_by: 'version', sort: 'desc' },
- version_asc: { order_by: 'version', sort: 'asc' },
- type_desc: { order_by: 'type', sort: 'desc' },
- type_asc: { order_by: 'type', sort: 'asc' }
- }.freeze
-
- def resolve(sort:)
+ def resolve(sort:, **filters)
return unless packages_available?
- ::Packages::PackagesFinder.new(object, SORT_TO_PARAMS_MAP.fetch(sort)).execute
- end
-
- private
-
- def packages_available?
- ::Gitlab.config.packages.enabled
+ ::Packages::PackagesFinder.new(object, filters.merge(SORT_TO_PARAMS_MAP.fetch(sort))).execute
end
end
end
+# rubocop: enable Graphql/ResolverType
diff --git a/app/helpers/environments_helper.rb b/app/helpers/environments_helper.rb
index 5add24500e2..594c6fedef1 100644
--- a/app/helpers/environments_helper.rb
+++ b/app/helpers/environments_helper.rb
@@ -34,7 +34,7 @@ module EnvironmentsHelper
def environment_logs_data(project, environment)
{
"environment_name": environment.name,
- "environments_path": project_environments_path(project, format: :json),
+ "environments_path": api_v4_projects_environments_path(id: project.id),
"environment_id": environment.id,
"cluster_applications_documentation_path" => help_page_path('user/clusters/applications.md', anchor: 'elastic-stack'),
"clusters_path": project_clusters_path(project, format: :json)
diff --git a/app/models/ci/pipeline_schedule.rb b/app/models/ci/pipeline_schedule.rb
index b76f1d48631..9e5d517c1fe 100644
--- a/app/models/ci/pipeline_schedule.rb
+++ b/app/models/ci/pipeline_schedule.rb
@@ -5,7 +5,7 @@ module Ci
extend Gitlab::Ci::Model
include Importable
include StripAttribute
- include Schedulable
+ include CronSchedulable
include Limitable
include EachBatch
@@ -51,36 +51,14 @@ module Ci
update_attribute(:active, false)
end
- ##
- # The `next_run_at` column is set to the actual execution date of `PipelineScheduleWorker`.
- # This way, a schedule like `*/1 * * * *` won't be triggered in a short interval
- # when PipelineScheduleWorker runs irregularly by Sidekiq Memory Killer.
- def set_next_run_at
- now = Time.zone.now
- ideal_next_run = ideal_next_run_from(now)
-
- self.next_run_at = if ideal_next_run == cron_worker_next_run_from(now)
- ideal_next_run
- else
- cron_worker_next_run_from(ideal_next_run)
- end
- end
-
def job_variables
variables&.map(&:to_runner_variable) || []
end
private
- def ideal_next_run_from(start_time)
- Gitlab::Ci::CronParser.new(cron, cron_timezone)
- .next_time_from(start_time)
- end
-
- def cron_worker_next_run_from(start_time)
- Gitlab::Ci::CronParser.new(Settings.cron_jobs['pipeline_schedule_worker']['cron'],
- Time.zone.name)
- .next_time_from(start_time)
+ def worker_cron_expression
+ Settings.cron_jobs['pipeline_schedule_worker']['cron']
end
end
end
diff --git a/app/models/concerns/cron_schedulable.rb b/app/models/concerns/cron_schedulable.rb
new file mode 100644
index 00000000000..beb3a09c119
--- /dev/null
+++ b/app/models/concerns/cron_schedulable.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module CronSchedulable
+ extend ActiveSupport::Concern
+ include Schedulable
+
+ ##
+ # The `next_run_at` column is set to the actual execution date of worker that
+ # triggers the schedule. This way, a schedule like `*/1 * * * *` won't be triggered
+ # in a short interval when the worker runs irregularly by Sidekiq Memory Killer.
+ def set_next_run_at
+ now = Time.zone.now
+ ideal_next_run = ideal_next_run_from(now)
+
+ self.next_run_at = if ideal_next_run == cron_worker_next_run_from(now)
+ ideal_next_run
+ else
+ cron_worker_next_run_from(ideal_next_run)
+ end
+ end
+
+ private
+
+ def ideal_next_run_from(start_time)
+ next_time_from(start_time, cron, cron_timezone)
+ end
+
+ def cron_worker_next_run_from(start_time)
+ next_time_from(start_time, worker_cron_expression, Time.zone.name)
+ end
+
+ def next_time_from(start_time, cron, cron_timezone)
+ Gitlab::Ci::CronParser
+ .new(cron, cron_timezone)
+ .next_time_from(start_time)
+ end
+
+ def worker_cron_expression
+ raise NotImplementedError
+ end
+end
diff --git a/app/services/web_hook_service.rb b/app/services/web_hook_service.rb
index 0535bc625ac..a3de5021076 100644
--- a/app/services/web_hook_service.rb
+++ b/app/services/web_hook_service.rb
@@ -27,6 +27,7 @@ class WebHookService
REQUEST_BODY_SIZE_LIMIT = 25.megabytes
GITLAB_EVENT_HEADER = 'X-Gitlab-Event'
+ MAX_FAILURES = 100
attr_accessor :hook, :data, :hook_name, :request_options
@@ -141,7 +142,7 @@ class WebHookService
next_backoff = hook.next_backoff
hook.update!(disabled_until: next_backoff.from_now, backoff_count: hook.backoff_count + 1)
else
- hook.update!(recent_failures: hook.recent_failures + 1)
+ hook.update!(recent_failures: hook.recent_failures + 1) if hook.recent_failures < MAX_FAILURES
end
end
diff --git a/changelogs/unreleased/285467-package-registry-graphql-api-3.yml b/changelogs/unreleased/285467-package-registry-graphql-api-3.yml
new file mode 100644
index 00000000000..6307cbc75af
--- /dev/null
+++ b/changelogs/unreleased/285467-package-registry-graphql-api-3.yml
@@ -0,0 +1,5 @@
+---
+title: 'Package: group and project graphql types - add search'
+merge_request: 61001
+author:
+type: added
diff --git a/changelogs/unreleased/ph-enableImprovedEmojiPicker.yml b/changelogs/unreleased/ph-enableImprovedEmojiPicker.yml
new file mode 100644
index 00000000000..24d71a00040
--- /dev/null
+++ b/changelogs/unreleased/ph-enableImprovedEmojiPicker.yml
@@ -0,0 +1,5 @@
+---
+title: Improve UX of the award emoji picker
+merge_request: 61384
+author:
+type: added
diff --git a/config/feature_flags/development/improved_emoji_picker.yml b/config/feature_flags/development/improved_emoji_picker.yml
index 2d72c38630a..211f0264446 100644
--- a/config/feature_flags/development/improved_emoji_picker.yml
+++ b/config/feature_flags/development/improved_emoji_picker.yml
@@ -5,4 +5,4 @@ rollout_issue_url:
milestone: '13.9'
type: development
group: group::code review
-default_enabled: false
+default_enabled: true
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 64ae868735a..65d6299bb89 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -691,6 +691,12 @@ Gitlab.ee do
Settings.cron_jobs['vulnerability_historical_statistics_deletion_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['vulnerability_historical_statistics_deletion_worker']['cron'] ||= '15 3 * * *'
Settings.cron_jobs['vulnerability_historical_statistics_deletion_worker']['job_class'] = 'Vulnerabilities::HistoricalStatistics::DeletionWorker'
+ Settings.cron_jobs['security_create_orchestration_policy_worker'] ||= Settingslogic.new({})
+ Settings.cron_jobs['security_create_orchestration_policy_worker']['cron'] ||= '*/10 * * * *'
+ Settings.cron_jobs['security_create_orchestration_policy_worker']['job_class'] = 'Security::CreateOrchestrationPolicyWorker'
+ Settings.cron_jobs['security_orchestration_policy_rule_schedule_worker'] ||= Settingslogic.new({})
+ Settings.cron_jobs['security_orchestration_policy_rule_schedule_worker']['cron'] ||= '*/15 * * * *'
+ Settings.cron_jobs['security_orchestration_policy_rule_schedule_worker']['job_class'] = 'Security::OrchestrationPolicyRuleScheduleWorker'
end
#
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index f0189971b56..3cff4218a32 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -9107,7 +9107,11 @@ four standard [pagination arguments](#connection-pagination-arguments):
| Name | Type | Description |
| ---- | ---- | ----------- |
+| <a id="grouppackagesincludeversionless"></a>`includeVersionless` | [`Boolean`](#boolean) | Include versionless packages. |
+| <a id="grouppackagespackagename"></a>`packageName` | [`String`](#string) | Search a package by name. |
+| <a id="grouppackagespackagetype"></a>`packageType` | [`PackageTypeEnum`](#packagetypeenum) | Filter a package by type. |
| <a id="grouppackagessort"></a>`sort` | [`PackageGroupSort`](#packagegroupsort) | Sort packages by this criteria. |
+| <a id="grouppackagesstatus"></a>`status` | [`PackageStatus`](#packagestatus) | Filter a package by status. |
##### `Group.projects`
@@ -11362,7 +11366,11 @@ four standard [pagination arguments](#connection-pagination-arguments):
| Name | Type | Description |
| ---- | ---- | ----------- |
+| <a id="projectpackagesincludeversionless"></a>`includeVersionless` | [`Boolean`](#boolean) | Include versionless packages. |
+| <a id="projectpackagespackagename"></a>`packageName` | [`String`](#string) | Search a package by name. |
+| <a id="projectpackagespackagetype"></a>`packageType` | [`PackageTypeEnum`](#packagetypeenum) | Filter a package by type. |
| <a id="projectpackagessort"></a>`sort` | [`PackageSort`](#packagesort) | Sort packages by this criteria. |
+| <a id="projectpackagesstatus"></a>`status` | [`PackageStatus`](#packagestatus) | Filter a package by status. |
##### `Project.pipeline`
diff --git a/doc/api/notes.md b/doc/api/notes.md
index edc479601bf..0fb13e56f78 100644
--- a/doc/api/notes.md
+++ b/doc/api/notes.md
@@ -4,7 +4,7 @@ group: Project Management
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
-# Notes API
+# Notes API **(FREE)**
Notes are comments on:
@@ -19,15 +19,15 @@ assignee changes, GitLab posts a system note).
## Resource events
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/38096) in GitLab 13.3 for state, milestone, and weight events.
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40850) in [GitLab Starter](https://about.gitlab.com/pricing/) 13.4 for iteration events.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40850) in GitLab 13.4 for iteration events.
Some system notes are not part of this API, but are recorded as separate events:
- [Resource label events](resource_label_events.md)
- [Resource state events](resource_state_events.md)
- [Resource milestone events](resource_milestone_events.md)
-- [Resource weight events](resource_weight_events.md) **(STARTER)**
-- [Resource iteration events](resource_iteration_events.md) **(STARTER)**
+- [Resource weight events](resource_weight_events.md)
+- [Resource iteration events](resource_iteration_events.md)
## Notes pagination
diff --git a/doc/subscriptions/gitlab_com/index.md b/doc/subscriptions/gitlab_com/index.md
index f2a33e821b8..f35f146f8b9 100644
--- a/doc/subscriptions/gitlab_com/index.md
+++ b/doc/subscriptions/gitlab_com/index.md
@@ -265,6 +265,11 @@ To remove a billable user:
1. In the row for the user you want to remove, on the right side, select the ellipsis and **Remove user**.
1. Re-type the username and select **Remove user**.
+If you add a member to a group by using the [share a group with another group](../../user/group/index.md#share-a-group-with-another-group) feature, you can't remove the member by using this method. Instead, you can either:
+
+- Remove the member from the shared group. You must be a group owner to do this.
+- From the group's membership page, remove access from the entire shared group.
+
## CI pipeline minutes
CI pipeline minutes are the execution time for your
diff --git a/doc/topics/git/bisect.md b/doc/topics/git/bisect.md
new file mode 100644
index 00000000000..8af77031c93
--- /dev/null
+++ b/doc/topics/git/bisect.md
@@ -0,0 +1,76 @@
+---
+stage: none
+group: unassigned
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+comments: false
+---
+
+# Bisect
+
+- Find a commit that introduced a bug
+- Works through a process of elimination
+- Specify a known good and bad revision to begin
+
+## Bisect sample workflow
+
+1. Start the bisect process
+1. Enter the bad revision (usually latest commit)
+1. Enter a known good revision (commit/branch)
+1. Run code to see if bug still exists
+1. Tell bisect the result
+1. Repeat the previous 2 items until you find the offending commit
+
+## Setup
+
+```shell
+ mkdir bisect-ex
+ cd bisect-ex
+ touch index.html
+ git add -A
+ git commit -m "starting out"
+ vi index.html
+ # Add all good
+ git add -A
+ git commit -m "second commit"
+ vi index.html
+ # Add all good 2
+ git add -A
+ git commit -m "third commit"
+ vi index.html
+```
+
+```shell
+ # Add all good 3
+ git add -A
+ git commit -m "fourth commit"
+ vi index.html
+ # This looks bad
+ git add -A
+ git commit -m "fifth commit"
+ vi index.html
+ # Really bad
+ git add -A
+ git commit -m "sixth commit"
+ vi index.html
+ # again just bad
+ git add -A
+ git commit -m "seventh commit"
+```
+
+## Commands
+
+```shell
+ git bisect start
+ # Test your code
+ git bisect bad
+ git bisect next
+ # Say yes to the warning
+ # Test
+ git bisect good
+ # Test
+ git bisect bad
+ # Test
+ git bisect good
+ # done
+ git bisect reset
+```
diff --git a/doc/topics/git/feature_branching.md b/doc/topics/git/feature_branching.md
new file mode 100644
index 00000000000..f6233bddb18
--- /dev/null
+++ b/doc/topics/git/feature_branching.md
@@ -0,0 +1,31 @@
+---
+stage: none
+group: unassigned
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+comments: false
+---
+
+# Feature branching
+
+- Efficient parallel workflow for teams
+- Develop each feature in a branch
+- Keeps changes isolated
+- Consider a 1-to-1 link to issues
+- Push branches to the server frequently
+ - Hint: This is a cheap backup for your work-in-progress code
+
+## Feature branching sample workflow
+
+1. Create a new feature branch called 'squash_some_bugs'
+1. Edit '`bugs.rb`' and remove all the bugs.
+1. Commit
+1. Push
+
+```shell
+git checkout -b squash_some_bugs
+# Edit `bugs.rb`
+git status
+git add bugs.rb
+git commit -m 'Fix some buggy code'
+git push origin squash_some_bugs
+```
diff --git a/doc/topics/git/getting_started.md b/doc/topics/git/getting_started.md
new file mode 100644
index 00000000000..2c3d5fe15de
--- /dev/null
+++ b/doc/topics/git/getting_started.md
@@ -0,0 +1,86 @@
+---
+stage: none
+group: unassigned
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+comments: false
+---
+
+# Getting Started
+
+## Instantiating Repositories
+
+- Create a new repository by instantiating it through:
+
+ ```shell
+ git init
+ ```
+
+- Copy an existing project by cloning the repository through:
+
+ ```shell
+ git clone <url>
+ ```
+
+## Central Repositories
+
+- To instantiate a central repository a `--bare` flag is required.
+- Bare repositories don't allow file editing or committing changes.
+- Create a bare repository with:
+
+ ```shell
+ git init --bare project-name.git
+ ```
+
+## Instantiate workflow with clone
+
+1. Create a project in your user namespace.
+ - Choose to import from **Any Repository by URL** and use <https://gitlab.com/gitlab-org/training-examples.git>.
+1. Create a '`Workspace`' directory in your home directory.
+1. Clone the '`training-examples`' project.
+
+```shell
+mkdir ~/workspace
+cd ~/workspace
+
+git clone git@gitlab.example.com:<username>/training-examples.git
+cd training-examples
+```
+
+## Git concepts
+
+**Untracked files**
+
+New files that Git has not been told to track previously.
+
+**Working area**
+
+Files that have been modified but are not committed.
+
+**Staging area**
+
+Modified files that have been marked to go in the next commit.
+
+## Committing Workflow
+
+1. Edit '`edit_this_file.rb`' in '`training-examples`'
+1. See it listed as a changed file (working area)
+1. View the differences
+1. Stage the file
+1. Commit
+1. Push the commit to the remote
+1. View the Git log
+
+```shell
+# Edit `edit_this_file.rb`
+git status
+git diff
+git add <file>
+git commit -m 'My change'
+git push origin master
+git log
+```
+
+## Note
+
+- `git fetch` vs `git pull`
+- Pull is `git fetch` + `git merge`
diff --git a/doc/topics/git/git_add.md b/doc/topics/git/git_add.md
new file mode 100644
index 00000000000..d136b9151bc
--- /dev/null
+++ b/doc/topics/git/git_add.md
@@ -0,0 +1,40 @@
+---
+stage: none
+group: unassigned
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+comments: false
+---
+
+# Git Add
+
+Adds content to the index or staging area.
+
+- Adds a list of file:
+
+ ```shell
+ git add <files>
+ ```
+
+- Adds all files including deleted ones:
+
+ ```shell
+ git add -A
+ ```
+
+- Add all text files in current dir:
+
+ ```shell
+ git add *.txt
+ ```
+
+- Add all text file in the project:
+
+ ```shell
+ git add "*.txt*"
+ ```
+
+- Adds all files in directory:
+
+ ```shell
+ git add views/layouts/
+ ```
diff --git a/doc/topics/git/git_log.md b/doc/topics/git/git_log.md
new file mode 100644
index 00000000000..ae4ae69ce76
--- /dev/null
+++ b/doc/topics/git/git_log.md
@@ -0,0 +1,60 @@
+---
+stage: none
+group: unassigned
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+comments: false
+---
+
+# Git Log
+
+Git log lists commit history. It allows searching and filtering.
+
+- Initiate log:
+
+ ```shell
+ git log
+ ```
+
+- Retrieve set number of records:
+
+ ```shell
+ git log -n 2
+ ```
+
+- Search commits by author. Allows user name or a regular expression.
+
+ ```shell
+ git log --author="user_name"
+ ```
+
+- Search by comment message:
+
+ ```shell
+ git log --grep="<pattern>"
+ ```
+
+- Search by date:
+
+ ```shell
+ git log --since=1.month.ago --until=3.weeks.ago
+ ```
+
+## Git Log Workflow
+
+1. Change to workspace directory
+1. Clone the multi runner projects
+1. Change to project dir
+1. Search by author
+1. Search by date
+1. Combine
+
+## Commands
+
+```shell
+cd ~/workspace
+git clone git@gitlab.com:gitlab-org/gitlab-runner.git
+cd gitlab-runner
+git log --author="Travis"
+git log --since=1.month.ago --until=3.weeks.ago
+git log --since=1.month.ago --until=1.day.ago --author="Travis"
+```
diff --git a/doc/topics/git/index.md b/doc/topics/git/index.md
index a4fc5f6fbf1..f676329e41b 100644
--- a/doc/topics/git/index.md
+++ b/doc/topics/git/index.md
@@ -39,7 +39,7 @@ The following resources can help you get started with Git:
- [Squashing commits](../gitlab_flow.md#squashing-commits-with-rebase)
- [Squash-and-merge](../../user/project/merge_requests/squash_and_merge.md)
- [Signing commits](../../user/project/repository/gpg_signed_commits/index.md)
-- [Git stash](../../university/training/topics/stash.md)
+- [Git stash](stash.md)
- [Git file blame](../../user/project/repository/git_blame.md)
- [Git file history](../../user/project/repository/git_history.md)
- [Git tags](tags.md)
diff --git a/doc/topics/git/merge_conflicts.md b/doc/topics/git/merge_conflicts.md
new file mode 100644
index 00000000000..66771559298
--- /dev/null
+++ b/doc/topics/git/merge_conflicts.md
@@ -0,0 +1,69 @@
+---
+stage: none
+group: unassigned
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+comments: false
+---
+
+# Merge conflicts
+
+- Happen often
+- Learning to fix conflicts is hard
+- Practice makes perfect
+- Force push after fixing conflicts. Be careful!
+
+## Merge conflicts sample workflow
+
+1. Checkout a new branch and edit `conflicts.rb`. Add 'Line4' and 'Line5'.
+1. Commit and push.
+1. Checkout master and edit `conflicts.rb`. Add 'Line6' and 'Line7' below 'Line3'.
+1. Commit and push to master.
+1. Create a merge request and watch it fail.
+1. Rebase our new branch with master.
+1. Fix conflicts on the `conflicts.rb` file.
+1. Stage the file and continue rebasing.
+1. Force push the changes.
+1. Finally continue with the Merge Request.
+
+```shell
+git checkout -b conflicts_branch
+
+# vi conflicts.rb
+# Add 'Line4' and 'Line5'
+
+git commit -am "add line4 and line5"
+git push origin conflicts_branch
+
+git checkout master
+
+# vi conflicts.rb
+# Add 'Line6' and 'Line7'
+git commit -am "add line6 and line7"
+git push origin master
+```
+
+Create a merge request on the GitLab web UI, and a conflict warning displays.
+
+```shell
+git checkout conflicts_branch
+git fetch
+git rebase master
+
+# Fix conflicts by editing the files.
+
+git add conflicts.rb
+# No need to commit this file
+
+git rebase --continue
+
+# Remember that we have rewritten our commit history so we
+# need to force push so that our remote branch is restructured
+git push origin conflicts_branch -f
+```
+
+## Note
+
+- When to use `git merge` and when to use `git rebase`
+- Rebase when updating your branch with master
+- Merge when bringing changes from feature to master
+- Reference: <https://www.atlassian.com/git/tutorials/merging-vs-rebasing>
diff --git a/doc/topics/git/merge_requests.md b/doc/topics/git/merge_requests.md
new file mode 100644
index 00000000000..a4a3108ebd1
--- /dev/null
+++ b/doc/topics/git/merge_requests.md
@@ -0,0 +1,40 @@
+---
+stage: none
+group: unassigned
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+comments: false
+---
+
+# Code review and collaboration with Merge Requests
+
+- When you want feedback create a merge request
+- Target is the default branch (usually master)
+- Assign or mention the person you would like to review
+- Add `[Draft]` to the title if it's a work in progress
+- When accepting, always delete the branch
+- Anyone can comment, not just the assignee
+- Push corrections to the same branch
+
+## Merge requests
+
+**Create your first merge request**
+
+1. Use the blue button in the activity feed
+1. View the diff (changes) and leave a comment
+1. Push a new commit to the same branch
+1. Review the changes again and notice the update
+
+## Feedback and Collaboration
+
+- Merge requests are a time for feedback and collaboration
+- Giving feedback is hard
+- Be as kind as possible
+- Receiving feedback is hard
+- Be as receptive as possible
+- Feedback is about the best code, not the person. You are not your code
+
+Review the Thoughtbot code-review guide for suggestions to follow when reviewing merge requests:
+[https://github.com/thoughtbot/guides/tree/master/code-review](https://github.com/thoughtbot/guides/tree/master/code-review)
+
+See GitLab merge requests for examples:
+[https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests)
diff --git a/doc/topics/git/rollback_commits.md b/doc/topics/git/rollback_commits.md
new file mode 100644
index 00000000000..34c2d9687bb
--- /dev/null
+++ b/doc/topics/git/rollback_commits.md
@@ -0,0 +1,81 @@
+---
+stage: none
+group: unassigned
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+comments: false
+---
+
+# Rollback Commits
+
+## Undo Commits
+
+- Undo last commit putting everything back into the staging area:
+
+ ```shell
+ git reset --soft HEAD^
+ ```
+
+- Add files and change message with:
+
+ ```shell
+ git commit --amend -m "New Message"
+ ```
+
+- Undo last and remove changes:
+
+ ```shell
+ git reset --hard HEAD^
+ ```
+
+- Same as last one but for two commits back:
+
+ ```shell
+ git reset --hard HEAD^^
+ ```
+
+**Don't reset after pushing**
+
+## Reset Workflow
+
+1. Edit file again 'edit_this_file.rb'
+1. Check status
+1. Add and commit with wrong message
+1. Check log
+1. Amend commit
+1. Check log
+1. Soft reset
+1. Check log
+1. Pull for updates
+1. Push changes
+
+## Commands
+
+```shell
+# Change file edit_this_file.rb
+git status
+git commit -am "kjkfjkg"
+git log
+git commit --amend -m "New comment added"
+git log
+git reset --soft HEAD^
+git log
+git pull origin master
+git push origin master
+```
+
+## Note
+
+- `git revert` vs `git reset`
+- Reset removes the commit while revert removes the changes but leaves the commit
+- Revert is safer considering we can revert a revert
+
+```shell
+# Changed file
+git commit -am "bug introduced"
+git revert HEAD
+# New commit created reverting changes
+# Now we want to re apply the reverted commit
+git log # take hash from the revert commit
+git revert <rev commit hash>
+# reverted commit is back (new commit created again)
+```
diff --git a/doc/topics/git/stash.md b/doc/topics/git/stash.md
new file mode 100644
index 00000000000..051103e5f4b
--- /dev/null
+++ b/doc/topics/git/stash.md
@@ -0,0 +1,82 @@
+---
+stage: none
+group: unassigned
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+comments: false
+---
+
+# Git Stash
+
+We use `git stash` to store our changes when they are not ready to be committed
+and we need to change to a different branch.
+
+- Stash:
+
+ ```shell
+ git stash save
+ # or
+ git stash
+ # or with a message
+ git stash save "this is a message to display on the list"
+ ```
+
+- Apply stash to keep working on it:
+
+ ```shell
+ git stash apply
+ # or apply a specific one from out stack
+ git stash apply stash@{3}
+ ```
+
+- Every time we save a stash it gets stacked so by using `list` we can see all our
+ stashes.
+
+ ```shell
+ git stash list
+ # or for more information (log methods)
+ git stash list --stat
+ ```
+
+- To clean our stack we need to manually remove them:
+
+ ```shell
+ # drop top stash
+ git stash drop
+ # or
+ git stash drop <name>
+ # to clear all history we can use
+ git stash clear
+ ```
+
+- Apply and drop on one command:
+
+ ```shell
+ git stash pop
+ ```
+
+- If we meet conflicts we need to either reset or commit our changes.
+- Conflicts through `pop` doesn't drop a stash afterwards.
+
+## Git Stash sample workflow
+
+1. Modify a file
+1. Stage file
+1. Stash it
+1. View our stash list
+1. Confirm no pending changes through status
+1. Apply with pop
+1. View list to confirm changes
+
+```shell
+# Modify edit_this_file.rb file
+git add .
+
+git stash save "Saving changes from edit this file"
+
+git stash list
+git status
+
+git stash pop
+git stash list
+git status
+```
diff --git a/doc/topics/git/subtree.md b/doc/topics/git/subtree.md
new file mode 100644
index 00000000000..54461915a05
--- /dev/null
+++ b/doc/topics/git/subtree.md
@@ -0,0 +1,52 @@
+---
+stage: none
+group: unassigned
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+comments: false
+---
+
+# Subtree
+
+- Used when there are nested repositories.
+- Not recommended when the amount of dependencies is too large.
+- For these cases we need a dependency control system.
+- Command are painfully long so aliases are necessary.
+
+## Subtree Aliases
+
+- Add: `git subtree add --prefix <target-folder> <url> <branch> --squash`
+- Pull: `git subtree pull --prefix <target-folder> <url> <branch> --squash`
+- Push: `git subtree add --prefix <target-folder> <url> <branch>`
+- Ex: `git config alias.sbp 'subtree pull --prefix st /
+ git@gitlab.com:balameb/subtree-nested-example.git master --squash'`
+
+```shell
+ # Add an alias
+ # Add
+ git config alias.sba 'subtree add --prefix st /
+ git@gitlab.com:balameb/subtree-nested-example.git master --squash'
+ # Pull
+ git config alias.sbpl 'subtree pull --prefix st /
+ git@gitlab.com:balameb/subtree-nested-example.git master --squash'
+ # Push
+ git config alias.sbph 'subtree push --prefix st /
+ git@gitlab.com:balameb/subtree-nested-example.git master'
+
+ # Adding this subtree adds a st dir with a readme
+ git sba
+ vi st/README.md
+ # Edit file
+ git status shows differences
+
+```
+
+```shell
+ # Adding, or committing won't change the sub repo at remote
+ # even if we push
+ git add -A
+ git commit -m "Adding to subtree readme"
+
+ # Push to subtree repo
+ git sbph
+ # now we can check our remote sub repo
+```
diff --git a/doc/topics/git/unstage.md b/doc/topics/git/unstage.md
new file mode 100644
index 00000000000..30d26854135
--- /dev/null
+++ b/doc/topics/git/unstage.md
@@ -0,0 +1,33 @@
+---
+stage: none
+group: unassigned
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+comments: false
+---
+
+# Unstage
+
+- To remove files from stage use reset HEAD where HEAD is the last commit of the current branch. This unstages the file but maintain the modifications.
+
+ ```shell
+ git reset HEAD <file>
+ ```
+
+- To revert the file back to the state it was in before the changes we can use:
+
+ ```shell
+ git checkout -- <file>
+ ```
+
+- To remove a file from disk and repository, use `git rm`. To remove a directory, use the `-r` flag:
+
+ ```shell
+ git rm '*.txt'
+ git rm -r <dirname>
+ ```
+
+- If we want to remove a file from the repository but keep it on disk, say we forgot to add it to our `.gitignore` file then use `--cache`:
+
+ ```shell
+ git rm <filename> --cache
+ ```
diff --git a/doc/university/training/topics/bisect.md b/doc/university/training/topics/bisect.md
index 8af77031c93..9c06f0b407d 100644
--- a/doc/university/training/topics/bisect.md
+++ b/doc/university/training/topics/bisect.md
@@ -1,76 +1,8 @@
---
-stage: none
-group: unassigned
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
-comments: false
+redirect_to: '../../../topics/git/bisect.md'
---
-# Bisect
+This document was moved to [another location](../../../topics/git/bisect.md).
-- Find a commit that introduced a bug
-- Works through a process of elimination
-- Specify a known good and bad revision to begin
-
-## Bisect sample workflow
-
-1. Start the bisect process
-1. Enter the bad revision (usually latest commit)
-1. Enter a known good revision (commit/branch)
-1. Run code to see if bug still exists
-1. Tell bisect the result
-1. Repeat the previous 2 items until you find the offending commit
-
-## Setup
-
-```shell
- mkdir bisect-ex
- cd bisect-ex
- touch index.html
- git add -A
- git commit -m "starting out"
- vi index.html
- # Add all good
- git add -A
- git commit -m "second commit"
- vi index.html
- # Add all good 2
- git add -A
- git commit -m "third commit"
- vi index.html
-```
-
-```shell
- # Add all good 3
- git add -A
- git commit -m "fourth commit"
- vi index.html
- # This looks bad
- git add -A
- git commit -m "fifth commit"
- vi index.html
- # Really bad
- git add -A
- git commit -m "sixth commit"
- vi index.html
- # again just bad
- git add -A
- git commit -m "seventh commit"
-```
-
-## Commands
-
-```shell
- git bisect start
- # Test your code
- git bisect bad
- git bisect next
- # Say yes to the warning
- # Test
- git bisect good
- # Test
- git bisect bad
- # Test
- git bisect good
- # done
- git bisect reset
-```
+<!-- This redirect file can be deleted after <2021-08-13>. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/university/training/topics/feature_branching.md b/doc/university/training/topics/feature_branching.md
index f6233bddb18..495462cdd00 100644
--- a/doc/university/training/topics/feature_branching.md
+++ b/doc/university/training/topics/feature_branching.md
@@ -1,31 +1,8 @@
---
-stage: none
-group: unassigned
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
-comments: false
+redirect_to: '../../../topics/git/feature_branching.md'
---
-# Feature branching
+This document was moved to [another location](../../../topics/git/feature_branching.md).
-- Efficient parallel workflow for teams
-- Develop each feature in a branch
-- Keeps changes isolated
-- Consider a 1-to-1 link to issues
-- Push branches to the server frequently
- - Hint: This is a cheap backup for your work-in-progress code
-
-## Feature branching sample workflow
-
-1. Create a new feature branch called 'squash_some_bugs'
-1. Edit '`bugs.rb`' and remove all the bugs.
-1. Commit
-1. Push
-
-```shell
-git checkout -b squash_some_bugs
-# Edit `bugs.rb`
-git status
-git add bugs.rb
-git commit -m 'Fix some buggy code'
-git push origin squash_some_bugs
-```
+<!-- This redirect file can be deleted after <2021-08-13>. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/university/training/topics/getting_started.md b/doc/university/training/topics/getting_started.md
index 2c3d5fe15de..3dc3902c2e3 100644
--- a/doc/university/training/topics/getting_started.md
+++ b/doc/university/training/topics/getting_started.md
@@ -1,86 +1,8 @@
---
-stage: none
-group: unassigned
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
-comments: false
+redirect_to: '../../../topics/git/getting_started.md'
---
-# Getting Started
+This document was moved to [another location](../../../topics/git/getting_started.md).
-## Instantiating Repositories
-
-- Create a new repository by instantiating it through:
-
- ```shell
- git init
- ```
-
-- Copy an existing project by cloning the repository through:
-
- ```shell
- git clone <url>
- ```
-
-## Central Repositories
-
-- To instantiate a central repository a `--bare` flag is required.
-- Bare repositories don't allow file editing or committing changes.
-- Create a bare repository with:
-
- ```shell
- git init --bare project-name.git
- ```
-
-## Instantiate workflow with clone
-
-1. Create a project in your user namespace.
- - Choose to import from **Any Repository by URL** and use <https://gitlab.com/gitlab-org/training-examples.git>.
-1. Create a '`Workspace`' directory in your home directory.
-1. Clone the '`training-examples`' project.
-
-```shell
-mkdir ~/workspace
-cd ~/workspace
-
-git clone git@gitlab.example.com:<username>/training-examples.git
-cd training-examples
-```
-
-## Git concepts
-
-**Untracked files**
-
-New files that Git has not been told to track previously.
-
-**Working area**
-
-Files that have been modified but are not committed.
-
-**Staging area**
-
-Modified files that have been marked to go in the next commit.
-
-## Committing Workflow
-
-1. Edit '`edit_this_file.rb`' in '`training-examples`'
-1. See it listed as a changed file (working area)
-1. View the differences
-1. Stage the file
-1. Commit
-1. Push the commit to the remote
-1. View the Git log
-
-```shell
-# Edit `edit_this_file.rb`
-git status
-git diff
-git add <file>
-git commit -m 'My change'
-git push origin master
-git log
-```
-
-## Note
-
-- `git fetch` vs `git pull`
-- Pull is `git fetch` + `git merge`
+<!-- This redirect file can be deleted after <2021-08-13>. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/university/training/topics/git_add.md b/doc/university/training/topics/git_add.md
index d136b9151bc..aa5e756995f 100644
--- a/doc/university/training/topics/git_add.md
+++ b/doc/university/training/topics/git_add.md
@@ -1,40 +1,8 @@
---
-stage: none
-group: unassigned
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
-comments: false
+redirect_to: '../../../topics/git/git_add.md'
---
-# Git Add
+This document was moved to [another location](../../../topics/git/git_add.md).
-Adds content to the index or staging area.
-
-- Adds a list of file:
-
- ```shell
- git add <files>
- ```
-
-- Adds all files including deleted ones:
-
- ```shell
- git add -A
- ```
-
-- Add all text files in current dir:
-
- ```shell
- git add *.txt
- ```
-
-- Add all text file in the project:
-
- ```shell
- git add "*.txt*"
- ```
-
-- Adds all files in directory:
-
- ```shell
- git add views/layouts/
- ```
+<!-- This redirect file can be deleted after <2021-08-13>. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/university/training/topics/git_log.md b/doc/university/training/topics/git_log.md
index ae4ae69ce76..1af8abb0782 100644
--- a/doc/university/training/topics/git_log.md
+++ b/doc/university/training/topics/git_log.md
@@ -1,60 +1,8 @@
---
-stage: none
-group: unassigned
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
-comments: false
+redirect_to: '../../../topics/git/git_log.md'
---
-# Git Log
+This document was moved to [another location](../../../topics/git/git_log.md).
-Git log lists commit history. It allows searching and filtering.
-
-- Initiate log:
-
- ```shell
- git log
- ```
-
-- Retrieve set number of records:
-
- ```shell
- git log -n 2
- ```
-
-- Search commits by author. Allows user name or a regular expression.
-
- ```shell
- git log --author="user_name"
- ```
-
-- Search by comment message:
-
- ```shell
- git log --grep="<pattern>"
- ```
-
-- Search by date:
-
- ```shell
- git log --since=1.month.ago --until=3.weeks.ago
- ```
-
-## Git Log Workflow
-
-1. Change to workspace directory
-1. Clone the multi runner projects
-1. Change to project dir
-1. Search by author
-1. Search by date
-1. Combine
-
-## Commands
-
-```shell
-cd ~/workspace
-git clone git@gitlab.com:gitlab-org/gitlab-runner.git
-cd gitlab-runner
-git log --author="Travis"
-git log --since=1.month.ago --until=3.weeks.ago
-git log --since=1.month.ago --until=1.day.ago --author="Travis"
-```
+<!-- This redirect file can be deleted after <2021-08-13>. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/university/training/topics/merge_conflicts.md b/doc/university/training/topics/merge_conflicts.md
index 66771559298..d76d297803f 100644
--- a/doc/university/training/topics/merge_conflicts.md
+++ b/doc/university/training/topics/merge_conflicts.md
@@ -1,69 +1,8 @@
---
-stage: none
-group: unassigned
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
-comments: false
+redirect_to: '../../../topics/git/merge_conflicts.md'
---
-# Merge conflicts
+This document was moved to [another location](../../../topics/git/merge_conflicts.md).
-- Happen often
-- Learning to fix conflicts is hard
-- Practice makes perfect
-- Force push after fixing conflicts. Be careful!
-
-## Merge conflicts sample workflow
-
-1. Checkout a new branch and edit `conflicts.rb`. Add 'Line4' and 'Line5'.
-1. Commit and push.
-1. Checkout master and edit `conflicts.rb`. Add 'Line6' and 'Line7' below 'Line3'.
-1. Commit and push to master.
-1. Create a merge request and watch it fail.
-1. Rebase our new branch with master.
-1. Fix conflicts on the `conflicts.rb` file.
-1. Stage the file and continue rebasing.
-1. Force push the changes.
-1. Finally continue with the Merge Request.
-
-```shell
-git checkout -b conflicts_branch
-
-# vi conflicts.rb
-# Add 'Line4' and 'Line5'
-
-git commit -am "add line4 and line5"
-git push origin conflicts_branch
-
-git checkout master
-
-# vi conflicts.rb
-# Add 'Line6' and 'Line7'
-git commit -am "add line6 and line7"
-git push origin master
-```
-
-Create a merge request on the GitLab web UI, and a conflict warning displays.
-
-```shell
-git checkout conflicts_branch
-git fetch
-git rebase master
-
-# Fix conflicts by editing the files.
-
-git add conflicts.rb
-# No need to commit this file
-
-git rebase --continue
-
-# Remember that we have rewritten our commit history so we
-# need to force push so that our remote branch is restructured
-git push origin conflicts_branch -f
-```
-
-## Note
-
-- When to use `git merge` and when to use `git rebase`
-- Rebase when updating your branch with master
-- Merge when bringing changes from feature to master
-- Reference: <https://www.atlassian.com/git/tutorials/merging-vs-rebasing>
+<!-- This redirect file can be deleted after <2021-08-13>. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/university/training/topics/merge_requests.md b/doc/university/training/topics/merge_requests.md
index a4a3108ebd1..c064bd78036 100644
--- a/doc/university/training/topics/merge_requests.md
+++ b/doc/university/training/topics/merge_requests.md
@@ -1,40 +1,8 @@
---
-stage: none
-group: unassigned
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
-comments: false
+redirect_to: '../../../topics/git/merge_requests.md'
---
-# Code review and collaboration with Merge Requests
+This document was moved to [another location](../../../topics/git/merge_requests.md).
-- When you want feedback create a merge request
-- Target is the default branch (usually master)
-- Assign or mention the person you would like to review
-- Add `[Draft]` to the title if it's a work in progress
-- When accepting, always delete the branch
-- Anyone can comment, not just the assignee
-- Push corrections to the same branch
-
-## Merge requests
-
-**Create your first merge request**
-
-1. Use the blue button in the activity feed
-1. View the diff (changes) and leave a comment
-1. Push a new commit to the same branch
-1. Review the changes again and notice the update
-
-## Feedback and Collaboration
-
-- Merge requests are a time for feedback and collaboration
-- Giving feedback is hard
-- Be as kind as possible
-- Receiving feedback is hard
-- Be as receptive as possible
-- Feedback is about the best code, not the person. You are not your code
-
-Review the Thoughtbot code-review guide for suggestions to follow when reviewing merge requests:
-[https://github.com/thoughtbot/guides/tree/master/code-review](https://github.com/thoughtbot/guides/tree/master/code-review)
-
-See GitLab merge requests for examples:
-[https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests)
+<!-- This redirect file can be deleted after <2021-08-13>. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/university/training/topics/rollback_commits.md b/doc/university/training/topics/rollback_commits.md
index 34c2d9687bb..b87aa12b834 100644
--- a/doc/university/training/topics/rollback_commits.md
+++ b/doc/university/training/topics/rollback_commits.md
@@ -1,81 +1,8 @@
---
-stage: none
-group: unassigned
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
-comments: false
+redirect_to: '../../../topics/git/rollback_commits.md'
---
-# Rollback Commits
+This document was moved to [another location](../../../topics/git/rollback_commits.md).
-## Undo Commits
-
-- Undo last commit putting everything back into the staging area:
-
- ```shell
- git reset --soft HEAD^
- ```
-
-- Add files and change message with:
-
- ```shell
- git commit --amend -m "New Message"
- ```
-
-- Undo last and remove changes:
-
- ```shell
- git reset --hard HEAD^
- ```
-
-- Same as last one but for two commits back:
-
- ```shell
- git reset --hard HEAD^^
- ```
-
-**Don't reset after pushing**
-
-## Reset Workflow
-
-1. Edit file again 'edit_this_file.rb'
-1. Check status
-1. Add and commit with wrong message
-1. Check log
-1. Amend commit
-1. Check log
-1. Soft reset
-1. Check log
-1. Pull for updates
-1. Push changes
-
-## Commands
-
-```shell
-# Change file edit_this_file.rb
-git status
-git commit -am "kjkfjkg"
-git log
-git commit --amend -m "New comment added"
-git log
-git reset --soft HEAD^
-git log
-git pull origin master
-git push origin master
-```
-
-## Note
-
-- `git revert` vs `git reset`
-- Reset removes the commit while revert removes the changes but leaves the commit
-- Revert is safer considering we can revert a revert
-
-```shell
-# Changed file
-git commit -am "bug introduced"
-git revert HEAD
-# New commit created reverting changes
-# Now we want to re apply the reverted commit
-git log # take hash from the revert commit
-git revert <rev commit hash>
-# reverted commit is back (new commit created again)
-```
+<!-- This redirect file can be deleted after <2021-08-13>. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/university/training/topics/stash.md b/doc/university/training/topics/stash.md
index 051103e5f4b..ea9ba6a7bcc 100644
--- a/doc/university/training/topics/stash.md
+++ b/doc/university/training/topics/stash.md
@@ -1,82 +1,8 @@
---
-stage: none
-group: unassigned
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
-comments: false
+redirect_to: '../../../topics/git/stash.md'
---
-# Git Stash
+This document was moved to [another location](../../../topics/git/stash.md).
-We use `git stash` to store our changes when they are not ready to be committed
-and we need to change to a different branch.
-
-- Stash:
-
- ```shell
- git stash save
- # or
- git stash
- # or with a message
- git stash save "this is a message to display on the list"
- ```
-
-- Apply stash to keep working on it:
-
- ```shell
- git stash apply
- # or apply a specific one from out stack
- git stash apply stash@{3}
- ```
-
-- Every time we save a stash it gets stacked so by using `list` we can see all our
- stashes.
-
- ```shell
- git stash list
- # or for more information (log methods)
- git stash list --stat
- ```
-
-- To clean our stack we need to manually remove them:
-
- ```shell
- # drop top stash
- git stash drop
- # or
- git stash drop <name>
- # to clear all history we can use
- git stash clear
- ```
-
-- Apply and drop on one command:
-
- ```shell
- git stash pop
- ```
-
-- If we meet conflicts we need to either reset or commit our changes.
-- Conflicts through `pop` doesn't drop a stash afterwards.
-
-## Git Stash sample workflow
-
-1. Modify a file
-1. Stage file
-1. Stash it
-1. View our stash list
-1. Confirm no pending changes through status
-1. Apply with pop
-1. View list to confirm changes
-
-```shell
-# Modify edit_this_file.rb file
-git add .
-
-git stash save "Saving changes from edit this file"
-
-git stash list
-git status
-
-git stash pop
-git stash list
-git status
-```
+<!-- This redirect file can be deleted after <2021-08-13>. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/university/training/topics/subtree.md b/doc/university/training/topics/subtree.md
index 54461915a05..5090ff8ca36 100644
--- a/doc/university/training/topics/subtree.md
+++ b/doc/university/training/topics/subtree.md
@@ -1,52 +1,8 @@
---
-stage: none
-group: unassigned
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
-comments: false
+redirect_to: '../../../topics/git/subtree.md'
---
-# Subtree
+This document was moved to [another location](../../../topics/git/subtree.md).
-- Used when there are nested repositories.
-- Not recommended when the amount of dependencies is too large.
-- For these cases we need a dependency control system.
-- Command are painfully long so aliases are necessary.
-
-## Subtree Aliases
-
-- Add: `git subtree add --prefix <target-folder> <url> <branch> --squash`
-- Pull: `git subtree pull --prefix <target-folder> <url> <branch> --squash`
-- Push: `git subtree add --prefix <target-folder> <url> <branch>`
-- Ex: `git config alias.sbp 'subtree pull --prefix st /
- git@gitlab.com:balameb/subtree-nested-example.git master --squash'`
-
-```shell
- # Add an alias
- # Add
- git config alias.sba 'subtree add --prefix st /
- git@gitlab.com:balameb/subtree-nested-example.git master --squash'
- # Pull
- git config alias.sbpl 'subtree pull --prefix st /
- git@gitlab.com:balameb/subtree-nested-example.git master --squash'
- # Push
- git config alias.sbph 'subtree push --prefix st /
- git@gitlab.com:balameb/subtree-nested-example.git master'
-
- # Adding this subtree adds a st dir with a readme
- git sba
- vi st/README.md
- # Edit file
- git status shows differences
-
-```
-
-```shell
- # Adding, or committing won't change the sub repo at remote
- # even if we push
- git add -A
- git commit -m "Adding to subtree readme"
-
- # Push to subtree repo
- git sbph
- # now we can check our remote sub repo
-```
+<!-- This redirect file can be deleted after <2021-08-13>. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/university/training/topics/unstage.md b/doc/university/training/topics/unstage.md
index 30d26854135..13c21f5cbb2 100644
--- a/doc/university/training/topics/unstage.md
+++ b/doc/university/training/topics/unstage.md
@@ -1,33 +1,8 @@
---
-stage: none
-group: unassigned
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
-comments: false
+redirect_to: '../../../topics/git/unstage.md'
---
-# Unstage
+This document was moved to [another location](../../../topics/git/unstage.md).
-- To remove files from stage use reset HEAD where HEAD is the last commit of the current branch. This unstages the file but maintain the modifications.
-
- ```shell
- git reset HEAD <file>
- ```
-
-- To revert the file back to the state it was in before the changes we can use:
-
- ```shell
- git checkout -- <file>
- ```
-
-- To remove a file from disk and repository, use `git rm`. To remove a directory, use the `-r` flag:
-
- ```shell
- git rm '*.txt'
- git rm -r <dirname>
- ```
-
-- If we want to remove a file from the repository but keep it on disk, say we forgot to add it to our `.gitignore` file then use `--cache`:
-
- ```shell
- git rm <filename> --cache
- ```
+<!-- This redirect file can be deleted after <2021-08-13>. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/group/value_stream_analytics/img/vsa_stage_table_v13_12.png b/doc/user/group/value_stream_analytics/img/vsa_stage_table_v13_12.png
index 4ad7d2e353e..24d485306be 100644
--- a/doc/user/group/value_stream_analytics/img/vsa_stage_table_v13_12.png
+++ b/doc/user/group/value_stream_analytics/img/vsa_stage_table_v13_12.png
Binary files differ
diff --git a/doc/user/group/value_stream_analytics/index.md b/doc/user/group/value_stream_analytics/index.md
index 023eddfddd5..a0ddbf22d2a 100644
--- a/doc/user/group/value_stream_analytics/index.md
+++ b/doc/user/group/value_stream_analytics/index.md
@@ -226,6 +226,8 @@ Hovering over a stage item displays a popover with the following information:
- Start event description for the given stage
- End event description
+- Median time items took to complete the stage
+- Number of items that completed the stage
### Stream overview
@@ -256,6 +258,9 @@ The stage table shows a list of related workflow items for the selected stage. T
- Merge requests
- Pipelines
+A little badge next to the workflow items table header shows the number of workflow items that
+completed the selected stage.
+
The stage table also includes the **Time** column, which shows how long it takes each item to pass
through the selected value stream stage.
diff --git a/doc/user/project/repository/branches/index.md b/doc/user/project/repository/branches/index.md
index 5239f2bbf32..1a21616e3a5 100644
--- a/doc/user/project/repository/branches/index.md
+++ b/doc/user/project/repository/branches/index.md
@@ -40,7 +40,7 @@ You can also manage branches using the
See also:
- [Branches API](../../../../api/branches.md), for information on operating on repository branches using the GitLab API.
-- [GitLab Flow](../../../../university/training/gitlab_flow.md) documentation.
+- [GitLab Flow](../../../../topics/gitlab_flow.md) documentation.
- [Getting started with Git](../../../../topics/git/index.md) and GitLab.
## Compare
diff --git a/package.json b/package.json
index b80d4013268..57d229cd8e9 100644
--- a/package.json
+++ b/package.json
@@ -51,9 +51,9 @@
"@babel/preset-env": "^7.10.1",
"@gitlab/at.js": "1.5.7",
"@gitlab/favicon-overlay": "2.0.0",
- "@gitlab/svgs": "1.195.0",
+ "@gitlab/svgs": "1.196.0",
"@gitlab/tributejs": "1.0.0",
- "@gitlab/ui": "29.23.0",
+ "@gitlab/ui": "29.25.0",
"@gitlab/visual-review-tools": "1.6.1",
"@rails/actioncable": "^6.0.3-4",
"@rails/ujs": "^6.0.3-4",
diff --git a/spec/features/issues/user_interacts_with_awards_spec.rb b/spec/features/issues/user_interacts_with_awards_spec.rb
index e862f7030c0..bbb7e8a028d 100644
--- a/spec/features/issues/user_interacts_with_awards_spec.rb
+++ b/spec/features/issues/user_interacts_with_awards_spec.rb
@@ -5,10 +5,6 @@ require 'spec_helper'
RSpec.describe 'User interacts with awards' do
let(:user) { create(:user) }
- before do
- stub_feature_flags(improved_emoji_picker: false)
- end
-
describe 'User interacts with awards in an issue', :js do
let(:issue) { create(:issue, project: project)}
let(:project) { create(:project) }
@@ -55,29 +51,24 @@ RSpec.describe 'User interacts with awards' do
it 'toggles a custom award emoji' do
page.within('.awards') do
- page.find('.js-add-award').click
+ page.find('.add-reaction-button').click
end
- page.find('.emoji-menu.is-visible')
-
- expect(page).to have_selector('.js-emoji-menu-search')
- expect(page.evaluate_script("document.activeElement.classList.contains('js-emoji-menu-search')")).to eq(true)
-
- page.within('.emoji-menu-content') do
- emoji_button = page.first('.js-emoji-btn')
+ page.within('.emoji-picker') do
+ emoji_button = page.first('gl-emoji[data-name="8ball"]')
emoji_button.hover
emoji_button.click
end
page.within('.awards') do
- expect(page).to have_selector('.js-emoji-btn')
- expect(page.find('.js-emoji-btn.active .js-counter')).to have_content('1')
- expect(page).to have_css(".js-emoji-btn.active[title='You']")
+ expect(page).to have_selector('[data-testid="award-button"]')
+ expect(page.find('[data-testid="award-button"].is-active .js-counter')).to have_content('1')
+ expect(page).to have_css('[data-testid="award-button"].is-active[title="You"]')
expect do
- page.find('.js-emoji-btn.active').click
+ page.find('[data-testid="award-button"].is-active').click
wait_for_requests
- end.to change { page.all('.award-control.js-emoji-btn').size }.from(3).to(2)
+ end.to change { page.all('[data-testid="award-button"]').size }.from(3).to(2)
end
end
@@ -212,31 +203,25 @@ RSpec.describe 'User interacts with awards' do
end
it 'adds award to issue' do
- first('.js-emoji-btn').click
+ first('[data-testid="award-button"]').click
- expect(page).to have_selector('.js-emoji-btn.active')
- expect(first('.js-emoji-btn')).to have_content '1'
+ expect(page).to have_selector('[data-testid="award-button"].is-active')
+ expect(first('[data-testid="award-button"]')).to have_content '1'
visit project_issue_path(project, issue)
- expect(first('.js-emoji-btn')).to have_content '1'
+ expect(first('[data-testid="award-button"]')).to have_content '1'
end
it 'removes award from issue' do
- first('.js-emoji-btn').click
- find('.js-emoji-btn.active').click
+ first('[data-testid="award-button"]').click
+ find('[data-testid="award-button"].is-active').click
- expect(first('.js-emoji-btn')).to have_content '0'
+ expect(first('[data-testid="award-button"]')).to have_content '0'
visit project_issue_path(project, issue)
- expect(first('.js-emoji-btn')).to have_content '0'
- end
-
- it 'only has one menu on the page' do
- first('.js-add-award').click
-
- expect(page).to have_selector('.emoji-menu', count: 1)
+ expect(first('[data-testid="award-button"]')).to have_content '0'
end
end
@@ -311,7 +296,7 @@ RSpec.describe 'User interacts with awards' do
end
context 'execute /award quick action' do
- it 'toggles the emoji award on noteable', :js do
+ xit 'toggles the emoji award on noteable', :js do
execute_quick_action('/award :100:')
expect(find(noteable_award_counter)).to have_text("1")
@@ -330,7 +315,7 @@ RSpec.describe 'User interacts with awards' do
end
it 'has disabled emoji button' do
- expect(first('.award-control')[:class]).to have_text('disabled')
+ expect(first('[data-testid="award-button"]')[:class]).to have_text('disabled')
end
end
@@ -356,7 +341,7 @@ RSpec.describe 'User interacts with awards' do
end
def noteable_award_counter
- ".awards .active"
+ ".awards .is-active"
end
def toggle_smiley_emoji(status)
diff --git a/spec/frontend/behaviors/date_picker_spec.js b/spec/frontend/behaviors/date_picker_spec.js
new file mode 100644
index 00000000000..9f7701a0366
--- /dev/null
+++ b/spec/frontend/behaviors/date_picker_spec.js
@@ -0,0 +1,30 @@
+import * as Pikaday from 'pikaday';
+import initDatePickers from '~/behaviors/date_picker';
+import * as utils from '~/lib/utils/datetime_utility';
+
+jest.mock('pikaday');
+jest.mock('~/lib/utils/datetime_utility');
+
+describe('date_picker behavior', () => {
+ let pikadayMock;
+ let parseMock;
+
+ beforeEach(() => {
+ pikadayMock = jest.spyOn(Pikaday, 'default');
+ parseMock = jest.spyOn(utils, 'parsePikadayDate');
+ setFixtures(`
+ <div>
+ <input class="datepicker" value="2020-10-01" />
+ </div>
+ <div>
+ <input class="datepicker" value="" />
+ </div>`);
+ });
+
+ it('Instantiates Pickaday for every instance of a .datepicker class', () => {
+ initDatePickers();
+
+ expect(pikadayMock.mock.calls.length).toEqual(2);
+ expect(parseMock.mock.calls).toEqual([['2020-10-01'], ['']]);
+ });
+});
diff --git a/spec/frontend/logs/stores/actions_spec.js b/spec/frontend/logs/stores/actions_spec.js
index 92c2f82af27..d5118bbde8c 100644
--- a/spec/frontend/logs/stores/actions_spec.js
+++ b/spec/frontend/logs/stores/actions_spec.js
@@ -191,7 +191,7 @@ describe('Logs Store actions', () => {
});
it('should commit RECEIVE_ENVIRONMENTS_DATA_SUCCESS mutation on correct data', () => {
- mock.onGet(mockEnvironmentsEndpoint).replyOnce(200, { environments: mockEnvironments });
+ mock.onGet(mockEnvironmentsEndpoint).replyOnce(200, mockEnvironments);
return testAction(
fetchEnvironments,
mockEnvironmentsEndpoint,
diff --git a/spec/graphql/resolvers/group_packages_resolver_spec.rb b/spec/graphql/resolvers/group_packages_resolver_spec.rb
index d2695a3c43a..48f4c8ec4ca 100644
--- a/spec/graphql/resolvers/group_packages_resolver_spec.rb
+++ b/spec/graphql/resolvers/group_packages_resolver_spec.rb
@@ -16,38 +16,6 @@ RSpec.describe Resolvers::GroupPackagesResolver do
describe '#resolve' do
subject { resolve(described_class, ctx: { current_user: user }, obj: group, args: args).to_a }
- context 'without sort' do
- let_it_be(:package) { create(:package, project: project) }
-
- it { is_expected.to contain_exactly(package) }
- end
-
- context 'with a sort argument' do
- let_it_be(:project2) { create(:project, :public, group: group) }
-
- let_it_be(:sort_repository) do
- create(:conan_package, name: 'bar', project: project, created_at: 1.day.ago, version: "1.0.0")
- end
-
- let_it_be(:sort_repository2) do
- create(:maven_package, name: 'foo', project: project2, created_at: 1.hour.ago, version: "2.0.0")
- end
-
- [:created_desc, :name_desc, :version_desc, :type_asc, :project_path_desc].each do |order|
- context "#{order}" do
- let(:args) { { sort: order } }
-
- it { is_expected.to eq([sort_repository2, sort_repository]) }
- end
- end
-
- [:created_asc, :name_asc, :version_asc, :type_desc, :project_path_asc].each do |order|
- context "#{order}" do
- let(:args) { { sort: order } }
-
- it { is_expected.to eq([sort_repository, sort_repository2]) }
- end
- end
- end
+ it_behaves_like 'group and projects packages resolver'
end
end
diff --git a/spec/graphql/resolvers/packages_base_resolver_spec.rb b/spec/graphql/resolvers/packages_base_resolver_spec.rb
new file mode 100644
index 00000000000..8f9865c3785
--- /dev/null
+++ b/spec/graphql/resolvers/packages_base_resolver_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::PackagesBaseResolver do
+ include GraphqlHelpers
+
+ describe '#resolve' do
+ subject { resolve(described_class) }
+
+ it 'throws an error' do
+ expect { subject }.to raise_error(NotImplementedError)
+ end
+ end
+end
diff --git a/spec/graphql/resolvers/project_packages_resolver_spec.rb b/spec/graphql/resolvers/project_packages_resolver_spec.rb
index 695db64743d..66a94bd42dd 100644
--- a/spec/graphql/resolvers/project_packages_resolver_spec.rb
+++ b/spec/graphql/resolvers/project_packages_resolver_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe Resolvers::ProjectPackagesResolver do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
- let_it_be(:project) { create(:project, :public) }
+ let_it_be_with_reload(:project) { create(:project, :public) }
let(:args) do
{ sort: :created_desc }
@@ -15,36 +15,6 @@ RSpec.describe Resolvers::ProjectPackagesResolver do
describe '#resolve' do
subject { resolve(described_class, ctx: { current_user: user }, obj: project, args: args).to_a }
- context 'without sort' do
- let_it_be(:package) { create(:package, project: project) }
-
- it { is_expected.to contain_exactly(package) }
- end
-
- context 'with a sort argument' do
- let_it_be(:sort_repository) do
- create(:conan_package, name: 'bar', project: project, created_at: 1.day.ago, version: "1.0.0")
- end
-
- let_it_be(:sort_repository2) do
- create(:maven_package, name: 'foo', project: project, created_at: 1.hour.ago, version: "2.0.0")
- end
-
- [:created_desc, :name_desc, :version_desc, :type_asc].each do |order|
- context "#{order}" do
- let(:args) { { sort: order } }
-
- it { is_expected.to eq([sort_repository2, sort_repository]) }
- end
- end
-
- [:created_asc, :name_asc, :version_asc, :type_desc].each do |order|
- context "#{order}" do
- let(:args) { { sort: order } }
-
- it { is_expected.to eq([sort_repository, sort_repository2]) }
- end
- end
- end
+ it_behaves_like 'group and projects packages resolver'
end
end
diff --git a/spec/helpers/environments_helper_spec.rb b/spec/helpers/environments_helper_spec.rb
index 8adb329e730..89cb0f72277 100644
--- a/spec/helpers/environments_helper_spec.rb
+++ b/spec/helpers/environments_helper_spec.rb
@@ -191,7 +191,7 @@ RSpec.describe EnvironmentsHelper do
it 'returns logs data' do
expected_data = {
"environment_name": environment.name,
- "environments_path": project_environments_path(project, format: :json),
+ "environments_path": api_v4_projects_environments_path(id: project.id),
"environment_id": environment.id,
"cluster_applications_documentation_path" => help_page_path('user/clusters/applications.md', anchor: 'elastic-stack'),
"clusters_path": project_clusters_path(project, format: :json)
diff --git a/spec/models/ci/pipeline_schedule_spec.rb b/spec/models/ci/pipeline_schedule_spec.rb
index 028208a56f1..d5560edbbfd 100644
--- a/spec/models/ci/pipeline_schedule_spec.rb
+++ b/spec/models/ci/pipeline_schedule_spec.rb
@@ -126,16 +126,6 @@ RSpec.describe Ci::PipelineSchedule do
end
end
- context 'when pipeline schedule runs every minute' do
- let(:pipeline_schedule) { create(:ci_pipeline_schedule, :every_minute) }
-
- it "updates next_run_at to the sidekiq worker's execution time" do
- travel_to(Time.zone.parse("2019-06-01 12:18:00+0000")) do
- expect(pipeline_schedule.next_run_at).to eq(cron_worker_next_run_at)
- end
- end
- end
-
context 'when there are two different pipeline schedules in different time zones' do
let(:pipeline_schedule_1) { create(:ci_pipeline_schedule, :weekly, cron_timezone: 'Eastern Time (US & Canada)') }
let(:pipeline_schedule_2) { create(:ci_pipeline_schedule, :weekly, cron_timezone: 'UTC') }
@@ -144,24 +134,6 @@ RSpec.describe Ci::PipelineSchedule do
expect(pipeline_schedule_1.next_run_at).not_to eq(pipeline_schedule_2.next_run_at)
end
end
-
- context 'when there are two different pipeline schedules in the same time zones' do
- let(:pipeline_schedule_1) { create(:ci_pipeline_schedule, :weekly, cron_timezone: 'UTC') }
- let(:pipeline_schedule_2) { create(:ci_pipeline_schedule, :weekly, cron_timezone: 'UTC') }
-
- it 'sets the sames next_run_at' do
- expect(pipeline_schedule_1.next_run_at).to eq(pipeline_schedule_2.next_run_at)
- end
- end
-
- context 'when updates cron of exsisted pipeline schedule' do
- let(:new_cron) { '0 0 1 1 *' }
-
- it 'updates next_run_at automatically' do
- expect { pipeline_schedule.update!(cron: new_cron) }
- .to change { pipeline_schedule.next_run_at }
- end
- end
end
describe '#schedule_next_run!' do
diff --git a/spec/models/concerns/cron_schedulable_spec.rb b/spec/models/concerns/cron_schedulable_spec.rb
new file mode 100644
index 00000000000..39c3d5e55d3
--- /dev/null
+++ b/spec/models/concerns/cron_schedulable_spec.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe CronSchedulable do
+ let(:ideal_next_run_at) { schedule.send(:ideal_next_run_from, Time.zone.now) }
+ let(:cron_worker_next_run_at) { schedule.send(:cron_worker_next_run_from, Time.zone.now) }
+
+ context 'for ci_pipeline_schedule' do
+ let(:schedule) { create(:ci_pipeline_schedule, :every_minute) }
+ let(:schedule_1) { create(:ci_pipeline_schedule, :weekly, cron_timezone: 'UTC') }
+ let(:schedule_2) { create(:ci_pipeline_schedule, :weekly, cron_timezone: 'UTC') }
+ let(:new_cron) { '0 0 1 1 *' }
+
+ it_behaves_like 'handles set_next_run_at'
+ end
+end
diff --git a/spec/requests/api/graphql/group/packages_spec.rb b/spec/requests/api/graphql/group/packages_spec.rb
index 409771b13ac..adee556db3a 100644
--- a/spec/requests/api/graphql/group/packages_spec.rb
+++ b/spec/requests/api/graphql/group/packages_spec.rb
@@ -7,82 +7,19 @@ RSpec.describe 'getting a package list for a group' do
let_it_be(:resource) { create(:group, :private) }
let_it_be(:group_two) { create(:group, :private) }
- let_it_be(:project) { create(:project, :repository, group: resource) }
- let_it_be(:another_project) { create(:project, :repository, group: resource) }
- let_it_be(:group_two_project) { create(:project, :repository, group: group_two) }
+ let_it_be(:project1) { create(:project, :repository, group: resource) }
+ let_it_be(:project2) { create(:project, :repository, group: resource) }
let_it_be(:current_user) { create(:user) }
- let_it_be(:npm_package) { create(:npm_package, project: group_two_project) }
- let_it_be(:maven_package) { create(:maven_package, project: project, name: 'tab', version: '4.0.0', created_at: 5.days.ago) }
- let_it_be(:package) { create(:npm_package, project: project, name: 'uab', version: '5.0.0', created_at: 4.days.ago) }
- let_it_be(:composer_package) { create(:composer_package, project: another_project, name: 'vab', version: '6.0.0', created_at: 3.days.ago) }
- let_it_be(:debian_package) { create(:debian_package, project: another_project, name: 'zab', version: '7.0.0', created_at: 2.days.ago) }
- let_it_be(:composer_metadatum) do
- create(:composer_metadatum, package: composer_package,
- target_sha: 'afdeh',
- composer_json: { name: 'x', type: 'y', license: 'z', version: 1 })
- end
-
- let(:package_names) { graphql_data_at(:group, :packages, :nodes, :name) }
- let(:target_shas) { graphql_data_at(:group, :packages, :nodes, :metadata, :target_sha) }
- let(:packages) { graphql_data_at(:group, :packages, :nodes) }
-
- let(:fields) do
- <<~QUERY
- nodes {
- #{all_graphql_fields_for('packages'.classify, excluded: ['project'])}
- metadata { #{query_graphql_fragment('ComposerMetadata')} }
- }
- QUERY
- end
-
- let(:query) do
- graphql_query_for(
- 'group',
- { 'fullPath' => resource.full_path },
- query_graphql_field('packages', {}, fields)
- )
- end
+ let(:resource_type) { :group }
it_behaves_like 'group and project packages query'
- describe 'sorting and pagination' do
- let_it_be(:ascending_packages) { [maven_package, package, composer_package, debian_package].map { |package| global_id_of(package)} }
-
- let(:data_path) { [:group, :packages] }
-
- before do
- resource.add_reporter(current_user)
- end
-
- [:CREATED_ASC, :NAME_ASC, :VERSION_ASC, :TYPE_ASC].each do |order|
- context "#{order}" do
- it_behaves_like 'sorted paginated query' do
- let(:sort_param) { order }
- let(:first_param) { 4 }
- let(:expected_results) { ascending_packages }
- end
- end
- end
-
- [:CREATED_DESC, :NAME_DESC, :VERSION_DESC, :TYPE_DESC].each do |order|
- context "#{order}" do
- it_behaves_like 'sorted paginated query' do
- let(:sort_param) { order }
- let(:first_param) { 4 }
- let(:expected_results) { ascending_packages.reverse }
- end
- end
- end
-
- def pagination_query(params)
- graphql_query_for(:group, { 'fullPath' => resource.full_path },
- query_nodes(:packages, :id, include_pagination_info: true, args: params)
- )
- end
- end
-
context 'with a batched query' do
+ let_it_be(:group_two_project) { create(:project, :repository, group: group_two) }
+ let_it_be(:group_one_package) { create(:npm_package, project: project1) }
+ let_it_be(:group_two_package) { create(:npm_package, project: group_two_project) }
+
let(:batch_query) do
<<~QUERY
{
@@ -101,12 +38,7 @@ RSpec.describe 'getting a package list for a group' do
end
it 'returns an error for the second group and data for the first' do
- expect(a_packages_names).to contain_exactly(
- package.name,
- maven_package.name,
- debian_package.name,
- composer_package.name
- )
+ expect(a_packages_names).to contain_exactly(group_one_package.name)
expect_graphql_errors_to_include [/Packages can be requested only for one group at a time/]
expect(graphql_data_at(:b, :packages)).to be(nil)
end
diff --git a/spec/requests/api/graphql/project/packages_spec.rb b/spec/requests/api/graphql/project/packages_spec.rb
index e7c6a4c0018..d9ee997eb02 100644
--- a/spec/requests/api/graphql/project/packages_spec.rb
+++ b/spec/requests/api/graphql/project/packages_spec.rb
@@ -7,72 +7,10 @@ RSpec.describe 'getting a package list for a project' do
let_it_be(:resource) { create(:project, :repository) }
let_it_be(:current_user) { create(:user) }
- let_it_be(:maven_package) { create(:maven_package, project: resource, name: 'tab', version: '4.0.0', created_at: 5.days.ago) }
- let_it_be(:package) { create(:npm_package, project: resource, name: 'uab', version: '5.0.0', created_at: 4.days.ago) }
- let_it_be(:composer_package) { create(:composer_package, project: resource, name: 'vab', version: '6.0.0', created_at: 3.days.ago) }
- let_it_be(:debian_package) { create(:debian_package, project: resource, name: 'zab', version: '7.0.0', created_at: 2.days.ago) }
- let_it_be(:composer_metadatum) do
- create(:composer_metadatum, package: composer_package,
- target_sha: 'afdeh',
- composer_json: { name: 'x', type: 'y', license: 'z', version: 1 })
- end
+ let_it_be(:project1) { resource }
+ let_it_be(:project2) { resource }
- let(:package_names) { graphql_data_at(:project, :packages, :nodes, :name) }
- let(:target_shas) { graphql_data_at(:project, :packages, :nodes, :metadata, :target_sha) }
- let(:packages) { graphql_data_at(:project, :packages, :nodes) }
-
- let(:fields) do
- <<~QUERY
- nodes {
- #{all_graphql_fields_for('packages'.classify, excluded: ['project'])}
- metadata { #{query_graphql_fragment('ComposerMetadata')} }
- }
- QUERY
- end
-
- let(:query) do
- graphql_query_for(
- 'project',
- { 'fullPath' => resource.full_path },
- query_graphql_field('packages', {}, fields)
- )
- end
+ let(:resource_type) { :project }
it_behaves_like 'group and project packages query'
-
- describe 'sorting and pagination' do
- let_it_be(:ascending_packages) { [maven_package, package, composer_package, debian_package].map { |package| global_id_of(package)} }
-
- let(:data_path) { [:project, :packages] }
-
- before do
- resource.add_reporter(current_user)
- end
-
- [:CREATED_ASC, :NAME_ASC, :VERSION_ASC, :TYPE_ASC].each do |order|
- context "#{order}" do
- it_behaves_like 'sorted paginated query' do
- let(:sort_param) { order }
- let(:first_param) { 4 }
- let(:expected_results) { ascending_packages }
- end
- end
- end
-
- [:CREATED_DESC, :NAME_DESC, :VERSION_DESC, :TYPE_DESC].each do |order|
- context "#{order}" do
- it_behaves_like 'sorted paginated query' do
- let(:sort_param) { order }
- let(:first_param) { 4 }
- let(:expected_results) { ascending_packages.reverse }
- end
- end
- end
-
- def pagination_query(params)
- graphql_query_for(:project, { 'fullPath' => resource.full_path },
- query_nodes(:packages, :id, include_pagination_info: true, args: params)
- )
- end
- end
end
diff --git a/spec/services/web_hook_service_spec.rb b/spec/services/web_hook_service_spec.rb
index 46dab4fa171..696b0bcaaf2 100644
--- a/spec/services/web_hook_service_spec.rb
+++ b/spec/services/web_hook_service_spec.rb
@@ -240,6 +240,25 @@ RSpec.describe WebHookService do
it 'does not change the disabled_until attribute' do
expect { service_instance.execute }.not_to change(project_hook, :disabled_until)
end
+
+ it 'does not allow the failure count to overflow' do
+ project_hook.update!(recent_failures: 32767)
+
+ expect { service_instance.execute }.not_to change(project_hook, :recent_failures)
+ end
+
+ context 'when the web_hooks_disable_failed FF is disabled' do
+ before do
+ # Hook will only be executed if the flag is disabled.
+ stub_feature_flags(web_hooks_disable_failed: false)
+ end
+
+ it 'does not allow the failure count to overflow' do
+ project_hook.update!(recent_failures: 32767)
+
+ expect { service_instance.execute }.not_to change(project_hook, :recent_failures)
+ end
+ end
end
context 'with exception' do
diff --git a/spec/support/shared_examples/graphql/resolvers/packages_resolvers_shared_examples.rb b/spec/support/shared_examples/graphql/resolvers/packages_resolvers_shared_examples.rb
new file mode 100644
index 00000000000..3d6fec85490
--- /dev/null
+++ b/spec/support/shared_examples/graphql/resolvers/packages_resolvers_shared_examples.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'group and projects packages resolver' do
+ context 'without sort' do
+ let_it_be(:npm_package) { create(:package, project: project) }
+
+ it { is_expected.to contain_exactly(npm_package) }
+ end
+
+ context 'with sorting and filtering' do
+ let_it_be(:conan_package) do
+ create(:conan_package, name: 'bar', project: project, created_at: 1.day.ago, version: "1.0.0", status: 'default')
+ end
+
+ let_it_be(:maven_package) do
+ create(:maven_package, name: 'foo', project: project, created_at: 1.hour.ago, version: "2.0.0", status: 'error')
+ end
+
+ let_it_be(:repository3) do
+ create(:maven_package, name: 'baz', project: project, created_at: 1.minute.ago, version: nil)
+ end
+
+ [:created_desc, :name_desc, :version_desc, :type_asc].each do |order|
+ context "#{order}" do
+ let(:args) { { sort: order } }
+
+ it { is_expected.to eq([maven_package, conan_package]) }
+ end
+ end
+
+ [:created_asc, :name_asc, :version_asc, :type_desc].each do |order|
+ context "#{order}" do
+ let(:args) { { sort: order } }
+
+ it { is_expected.to eq([conan_package, maven_package]) }
+ end
+ end
+
+ context 'filter by package_name' do
+ let(:args) { { package_name: 'bar', sort: :created_desc } }
+
+ it { is_expected.to eq([conan_package]) }
+ end
+
+ context 'filter by package_type' do
+ let(:args) { { package_type: 'conan', sort: :created_desc } }
+
+ it { is_expected.to eq([conan_package]) }
+ end
+
+ context 'filter by status' do
+ let(:args) { { status: 'error', sort: :created_desc } }
+
+ it { is_expected.to eq([maven_package]) }
+ end
+
+ context 'include_versionless' do
+ let(:args) { { include_versionless: true, sort: :created_desc } }
+
+ it { is_expected.to include(repository3) }
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/concerns/cron_schedulable_shared_examples.rb b/spec/support/shared_examples/models/concerns/cron_schedulable_shared_examples.rb
new file mode 100644
index 00000000000..47a02a8fa49
--- /dev/null
+++ b/spec/support/shared_examples/models/concerns/cron_schedulable_shared_examples.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'handles set_next_run_at' do
+ context 'when schedule runs every minute' do
+ it "updates next_run_at to the worker's execution time" do
+ travel_to(1.day.ago) do
+ expect(schedule.next_run_at).to eq(cron_worker_next_run_at)
+ end
+ end
+ end
+
+ context 'when there are two different schedules in the same time zones' do
+ it 'sets the sames next_run_at' do
+ expect(schedule_1.next_run_at).to eq(schedule_2.next_run_at)
+ end
+ end
+
+ context 'when cron is updated for existing schedules' do
+ it 'updates next_run_at automatically' do
+ expect { schedule.update!(cron: new_cron) }.to change { schedule.next_run_at }
+ end
+ end
+end
diff --git a/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb
index 66fbfa798b0..af4c9286e7c 100644
--- a/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb
@@ -3,6 +3,38 @@
RSpec.shared_examples 'group and project packages query' do
include GraphqlHelpers
+ let_it_be(:versionaless_package) { create(:maven_package, project: project1, version: nil) }
+ let_it_be(:maven_package) { create(:maven_package, project: project1, name: 'tab', version: '4.0.0', created_at: 5.days.ago) }
+ let_it_be(:package) { create(:npm_package, project: project1, name: 'uab', version: '5.0.0', created_at: 4.days.ago) }
+ let_it_be(:composer_package) { create(:composer_package, project: project2, name: 'vab', version: '6.0.0', created_at: 3.days.ago) }
+ let_it_be(:debian_package) { create(:debian_package, project: project2, name: 'zab', version: '7.0.0', created_at: 2.days.ago) }
+ let_it_be(:composer_metadatum) do
+ create(:composer_metadatum, package: composer_package,
+ target_sha: 'afdeh',
+ composer_json: { name: 'x', type: 'y', license: 'z', version: 1 })
+ end
+
+ let(:package_names) { graphql_data_at(resource_type, :packages, :nodes, :name) }
+ let(:target_shas) { graphql_data_at(resource_type, :packages, :nodes, :metadata, :target_sha) }
+ let(:packages) { graphql_data_at(resource_type, :packages, :nodes) }
+
+ let(:fields) do
+ <<~QUERY
+ nodes {
+ #{all_graphql_fields_for('packages'.classify, excluded: ['project'])}
+ metadata { #{query_graphql_fragment('ComposerMetadata')} }
+ }
+ QUERY
+ end
+
+ let(:query) do
+ graphql_query_for(
+ resource_type,
+ { 'fullPath' => resource.full_path },
+ query_graphql_field('packages', {}, fields)
+ )
+ end
+
context 'when user has access to the resource' do
before do
resource.add_reporter(current_user)
@@ -48,4 +80,101 @@ RSpec.shared_examples 'group and project packages query' do
expect(packages).to be_nil
end
end
+
+ describe 'sorting and pagination' do
+ let_it_be(:ascending_packages) { [maven_package, package, composer_package, debian_package].map { |package| global_id_of(package)} }
+
+ let(:data_path) { [resource_type, :packages] }
+
+ before do
+ resource.add_reporter(current_user)
+ end
+
+ [:CREATED_ASC, :NAME_ASC, :VERSION_ASC, :TYPE_ASC].each do |order|
+ context "#{order}" do
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { order }
+ let(:first_param) { 4 }
+ let(:expected_results) { ascending_packages }
+ end
+ end
+ end
+
+ [:CREATED_DESC, :NAME_DESC, :VERSION_DESC, :TYPE_DESC].each do |order|
+ context "#{order}" do
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { order }
+ let(:first_param) { 4 }
+ let(:expected_results) { ascending_packages.reverse }
+ end
+ end
+ end
+
+ context 'with an invalid sort' do
+ let(:query) do
+ graphql_query_for(
+ resource_type,
+ { 'fullPath' => resource.full_path },
+ query_nodes(:packages, :name, args: { sort: :WRONG_ORDER })
+ )
+ end
+
+ before do
+ post_graphql(query, current_user: current_user)
+ end
+
+ it 'throws an error' do
+ expect_graphql_errors_to_include(/Argument \'sort\' on Field \'packages\' has an invalid value/)
+ end
+ end
+
+ def pagination_query(params)
+ graphql_query_for(resource_type, { 'fullPath' => resource.full_path },
+ query_nodes(:packages, :id, include_pagination_info: true, args: params)
+ )
+ end
+ end
+
+ describe 'filtering' do
+ subject { packages }
+
+ let(:query) do
+ graphql_query_for(
+ resource_type,
+ { 'fullPath' => resource.full_path },
+ query_nodes(:packages, :name, args: params)
+ )
+ end
+
+ before do
+ resource.add_reporter(current_user)
+ post_graphql(query, current_user: current_user)
+ end
+
+ context 'package_name' do
+ let(:params) { { package_name: maven_package.name } }
+
+ it { is_expected.to contain_exactly({ "name" => maven_package.name }) }
+ end
+
+ context 'package_type' do
+ let(:params) { { package_type: :COMPOSER } }
+
+ it { is_expected.to contain_exactly({ "name" => composer_package.name }) }
+ end
+
+ context 'status' do
+ let_it_be(:errored_package) { create(:maven_package, project: project1, status: 'error') }
+
+ let(:params) { { status: :ERROR } }
+
+ it { is_expected.to contain_exactly({ "name" => errored_package.name }) }
+ end
+
+ context 'include_versionless' do
+ let(:params) { { include_versionless: true } }
+
+ it { is_expected.to include({ "name" => versionaless_package.name }) }
+ end
+ end
end
diff --git a/yarn.lock b/yarn.lock
index 99ab9c7780f..2353ff71ad5 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -897,20 +897,20 @@
stylelint-declaration-strict-value "1.7.7"
stylelint-scss "3.18.0"
-"@gitlab/svgs@1.195.0":
- version "1.195.0"
- resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.195.0.tgz#addf7a31fe8b49a9b91af7373877ec46e56603b1"
- integrity sha512-ZEVfFEONZSUf0SmUY1+BDCQPTXBKPamHp/qfTVPXvuXxkihsSBRGwqdu57fNQq/0/Jk95IY2uxm+I9fzb4uQUQ==
+"@gitlab/svgs@1.196.0":
+ version "1.196.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.196.0.tgz#7905bc66ef9c69ba7b5225326c0eaf4e55d11386"
+ integrity sha512-X/UGCBXIZXBcPqfRy8qaxQG/SYyrZajAazORZjmnVF+cyM9lQTuCiZFi7dHtMQ1XLew+m1NREVngCmBLuuK98g==
"@gitlab/tributejs@1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8"
integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw==
-"@gitlab/ui@29.23.0":
- version "29.23.0"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-29.23.0.tgz#cf0bf2ed85c679a618f0f78d7f5d4e4346663b55"
- integrity sha512-ELMHentJ9v5WXlAAuSr5s5kYu8KW710N4qCnzdt09g+McSsvXUhOx2ltM0d4rZe+TZwgwAUndwCUDP+dHqDtrA==
+"@gitlab/ui@29.25.0":
+ version "29.25.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-29.25.0.tgz#535f1e358176fb73d0e7df0315a8f50f7c8fc20b"
+ integrity sha512-Bt9wBj954Ev6UGbAJ0+ViT7ggOBXocyxru8NPDq060G7OAchtm6IQK0bwa4DugF5LxiP9IWXy34Oz55zjymwqw==
dependencies:
"@babel/standalone" "^7.0.0"
"@gitlab/vue-toasted" "^1.3.0"