Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
d81023e4e9
commit
26dfad7651
65 changed files with 1180 additions and 626 deletions
|
@ -7,20 +7,6 @@ Layout/SpaceInsideParens:
|
|||
Exclude:
|
||||
- 'config/initializers/wikicloth_redos_patch.rb'
|
||||
- 'db/post_migrate/20210722042939_update_issuable_slas_where_issue_closed.rb'
|
||||
- 'ee/app/graphql/resolvers/external_issue_resolver.rb'
|
||||
- 'ee/app/helpers/billing_plans_helper.rb'
|
||||
- 'ee/app/helpers/ee/boards_helper.rb'
|
||||
- 'ee/app/models/ee/lfs_object.rb'
|
||||
- 'ee/app/models/ee/merge_request_diff.rb'
|
||||
- 'ee/app/models/ee/pages_deployment.rb'
|
||||
- 'ee/app/models/ee/upload.rb'
|
||||
- 'ee/app/models/requirements_management/requirement.rb'
|
||||
- 'ee/app/models/resource_iteration_event.rb'
|
||||
- 'ee/app/services/compliance_management/frameworks/create_service.rb'
|
||||
- 'ee/app/services/compliance_management/frameworks/destroy_service.rb'
|
||||
- 'ee/app/services/compliance_management/frameworks/update_service.rb'
|
||||
- 'ee/app/services/elastic/cluster_reindexing_service.rb'
|
||||
- 'ee/app/services/sitemap/create_service.rb'
|
||||
- 'ee/lib/ee/gitlab/auth/ldap/access.rb'
|
||||
- 'ee/lib/gitlab/auth/smartcard/session.rb'
|
||||
- 'ee/spec/finders/ee/alert_management/http_integrations_finder_spec.rb'
|
||||
|
|
|
@ -1 +1 @@
|
|||
15.6.0-rc1
|
||||
15.6.0-rc2
|
||||
|
|
2
Gemfile
2
Gemfile
|
@ -362,7 +362,7 @@ gem 'prometheus-client-mmap', '~> 0.16', require: 'prometheus/client'
|
|||
gem 'warning', '~> 1.3.0'
|
||||
|
||||
group :development do
|
||||
gem 'lefthook', '~> 1.1.3', require: false
|
||||
gem 'lefthook', '~> 1.1.4', require: false
|
||||
gem 'rubocop'
|
||||
gem 'solargraph', '~> 0.47.2', require: false
|
||||
|
||||
|
|
|
@ -302,7 +302,7 @@
|
|||
{"name":"kramdown-parser-gfm","version":"1.1.0","platform":"ruby","checksum":"fb39745516427d2988543bf01fc4cf0ab1149476382393e0e9c48592f6581729"},
|
||||
{"name":"kubeclient","version":"4.9.3","platform":"ruby","checksum":"d5d38e719fbac44f396851aa57cd1b9f4f7dab4410ab680ccd21c9b741230046"},
|
||||
{"name":"launchy","version":"2.5.0","platform":"ruby","checksum":"954243c4255920982ce682f89a42e76372dba94770bf09c23a523e204bdebef5"},
|
||||
{"name":"lefthook","version":"1.1.3","platform":"ruby","checksum":"3f8337b2176f49e6d4ab8f0f4494c8d1be0548d79bca898fbf2184d717092b75"},
|
||||
{"name":"lefthook","version":"1.1.4","platform":"ruby","checksum":"251fbc6681a7d0f05e594b5091206998fd21060285f9752ac40b92441d5eb93c"},
|
||||
{"name":"letter_opener","version":"1.7.0","platform":"ruby","checksum":"095bc0d58e006e5b43ea7d219e64ecf2de8d1f7d9dafc432040a845cf59b4725"},
|
||||
{"name":"letter_opener_web","version":"2.0.0","platform":"ruby","checksum":"33860ad41e1785d75456500e8ca8bba8ed71ee6eaf08a98d06bbab67c5577b6f"},
|
||||
{"name":"libyajl2","version":"1.2.0","platform":"ruby","checksum":"1117cd1e48db013b626e36269bbf1cef210538ca6d2e62d3fa3db9ded005b258"},
|
||||
|
|
|
@ -802,7 +802,7 @@ GEM
|
|||
rest-client (~> 2.0)
|
||||
launchy (2.5.0)
|
||||
addressable (~> 2.7)
|
||||
lefthook (1.1.3)
|
||||
lefthook (1.1.4)
|
||||
letter_opener (1.7.0)
|
||||
launchy (~> 2.2)
|
||||
letter_opener_web (2.0.0)
|
||||
|
@ -1680,7 +1680,7 @@ DEPENDENCIES
|
|||
knapsack (~> 1.21.1)
|
||||
kramdown (~> 2.3.1)
|
||||
kubeclient (~> 4.9.3)
|
||||
lefthook (~> 1.1.3)
|
||||
lefthook (~> 1.1.4)
|
||||
letter_opener_web (~> 2.0.0)
|
||||
license_finder (~> 7.0)
|
||||
licensee (~> 9.15)
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
<script>
|
||||
import { GlButton, GlEmptyState } from '@gitlab/ui';
|
||||
import { __ } from '~/locale';
|
||||
import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue';
|
||||
import { IssuableListTabs, IssuableStates } from '~/vue_shared/issuable/list/constants';
|
||||
|
||||
export default {
|
||||
i18n: {
|
||||
calendarButtonText: __('Subscribe to calendar'),
|
||||
emptyStateTitle: __('Please select at least one filter to see results'),
|
||||
rssButtonText: __('Subscribe to RSS feed'),
|
||||
searchInputPlaceholder: __('Search or filter results...'),
|
||||
},
|
||||
IssuableListTabs,
|
||||
components: {
|
||||
GlButton,
|
||||
GlEmptyState,
|
||||
IssuableList,
|
||||
},
|
||||
inject: ['calendarPath', 'emptyStateSvgPath', 'isSignedIn', 'rssPath'],
|
||||
data() {
|
||||
return {
|
||||
issues: [],
|
||||
searchTokens: [],
|
||||
sortOptions: [],
|
||||
state: IssuableStates.Opened,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<issuable-list
|
||||
namespace="dashboard"
|
||||
recent-searches-storage-key="issues"
|
||||
:search-input-placeholder="$options.i18n.searchInputPlaceholder"
|
||||
:search-tokens="searchTokens"
|
||||
:sort-options="sortOptions"
|
||||
:issuables="issues"
|
||||
:tabs="$options.IssuableListTabs"
|
||||
:current-tab="state"
|
||||
>
|
||||
<template #nav-actions>
|
||||
<gl-button :href="rssPath" icon="rss">
|
||||
{{ $options.i18n.rssButtonText }}
|
||||
</gl-button>
|
||||
<gl-button :href="calendarPath" icon="calendar">
|
||||
{{ $options.i18n.calendarButtonText }}
|
||||
</gl-button>
|
||||
</template>
|
||||
|
||||
<template #empty-state>
|
||||
<gl-empty-state :svg-path="emptyStateSvgPath" :title="$options.i18n.emptyStateTitle" />
|
||||
</template>
|
||||
</issuable-list>
|
||||
</template>
|
25
app/assets/javascripts/issues/dashboard/index.js
Normal file
25
app/assets/javascripts/issues/dashboard/index.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
import Vue from 'vue';
|
||||
import { parseBoolean } from '~/lib/utils/common_utils';
|
||||
import IssuesDashboardApp from './components/issues_dashboard_app.vue';
|
||||
|
||||
export function mountIssuesDashboardApp() {
|
||||
const el = document.querySelector('.js-issues-dashboard');
|
||||
|
||||
if (!el) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { calendarPath, emptyStateSvgPath, isSignedIn, rssPath } = el.dataset;
|
||||
|
||||
return new Vue({
|
||||
el,
|
||||
name: 'IssuesDashboardRoot',
|
||||
provide: {
|
||||
calendarPath,
|
||||
emptyStateSvgPath,
|
||||
isSignedIn: parseBoolean(isSignedIn),
|
||||
rssPath,
|
||||
},
|
||||
render: (createComponent) => createComponent(IssuesDashboardApp),
|
||||
});
|
||||
}
|
|
@ -3,7 +3,6 @@ import { GlTooltipDirective, GlIcon, GlSprintf, GlSkeletonLoader, GlButton } fro
|
|||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
import { n__ } from '~/locale';
|
||||
import Tracking from '~/tracking';
|
||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
|
||||
import ListItem from '~/vue_shared/components/registry/list_item.vue';
|
||||
import { joinPaths } from '~/lib/utils/url_utility';
|
||||
|
@ -38,7 +37,7 @@ export default {
|
|||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
mixins: [Tracking.mixin(), glFeatureFlagsMixin()],
|
||||
mixins: [Tracking.mixin()],
|
||||
inject: ['config'],
|
||||
props: {
|
||||
item: {
|
||||
|
@ -91,17 +90,14 @@ export default {
|
|||
);
|
||||
},
|
||||
imageName() {
|
||||
if (this.glFeatures.containerRegistryShowShortenedPath) {
|
||||
if (this.showFullPath) {
|
||||
return this.item.path;
|
||||
}
|
||||
const projectPath = this.item?.project?.path?.toLowerCase() ?? '';
|
||||
if (this.item.name) {
|
||||
return joinPaths(projectPath, this.item.name);
|
||||
}
|
||||
return projectPath;
|
||||
if (this.showFullPath) {
|
||||
return this.item.path;
|
||||
}
|
||||
return this.item.path;
|
||||
const projectPath = this.item?.project?.path?.toLowerCase() ?? '';
|
||||
if (this.item.name) {
|
||||
return joinPaths(projectPath, this.item.name);
|
||||
}
|
||||
return projectPath;
|
||||
},
|
||||
routerLinkEvent() {
|
||||
return this.deleting ? '' : 'click';
|
||||
|
@ -136,7 +132,7 @@ export default {
|
|||
>
|
||||
<template #left-primary>
|
||||
<gl-button
|
||||
v-if="glFeatures.containerRegistryShowShortenedPath && !showFullPath"
|
||||
v-if="!showFullPath"
|
||||
v-gl-tooltip="{
|
||||
placement: 'top',
|
||||
title: $options.i18n.IMAGE_FULL_PATH_LABEL,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
|
||||
import { mountIssuesDashboardApp } from '~/issues/dashboard';
|
||||
import initManualOrdering from '~/issues/manual_ordering';
|
||||
import { FILTERED_SEARCH } from '~/filtered_search/constants';
|
||||
import initFilteredSearch from '~/pages/search/init_filtered_search';
|
||||
|
@ -12,3 +13,5 @@ initFilteredSearch({
|
|||
|
||||
projectSelect();
|
||||
initManualOrdering();
|
||||
|
||||
mountIssuesDashboardApp();
|
||||
|
|
|
@ -319,7 +319,7 @@ export default {
|
|||
(sortBy) =>
|
||||
sortBy.sortDirection.ascending === sort || sortBy.sortDirection.descending === sort,
|
||||
);
|
||||
this.selectedSortDirection = Object.keys(this.selectedSortOption.sortDirection).find(
|
||||
this.selectedSortDirection = Object.keys(this.selectedSortOption?.sortDirection || {}).find(
|
||||
(key) => this.selectedSortOption.sortDirection[key] === sort,
|
||||
);
|
||||
},
|
||||
|
|
|
@ -6,10 +6,6 @@ import WorkItemLinks from './work_item_links.vue';
|
|||
Vue.use(GlToast);
|
||||
|
||||
export default function initWorkItemLinks() {
|
||||
if (!window.gon.features.workItemsHierarchy) {
|
||||
return;
|
||||
}
|
||||
|
||||
const workItemLinksRoot = document.querySelector('.js-work-item-links-root');
|
||||
|
||||
if (!workItemLinksRoot) {
|
||||
|
|
5
app/assets/stylesheets/page_bundles/dashboard.scss
Normal file
5
app/assets/stylesheets/page_bundles/dashboard.scss
Normal file
|
@ -0,0 +1,5 @@
|
|||
@import 'mixins_and_variables_and_functions';
|
||||
|
||||
.empty-state .svg-250 img {
|
||||
max-width: $gl-spacing-scale-20;
|
||||
}
|
|
@ -8,10 +8,6 @@ module Groups
|
|||
before_action :verify_container_registry_enabled!
|
||||
before_action :authorize_read_container_image!
|
||||
|
||||
before_action do
|
||||
push_frontend_feature_flag(:container_registry_show_shortened_path, group)
|
||||
end
|
||||
|
||||
feature_category :container_registry
|
||||
urgency :low
|
||||
|
||||
|
|
|
@ -83,7 +83,8 @@ class JiraConnect::SubscriptionsController < JiraConnect::ApplicationController
|
|||
def allowed_instance_connect_src
|
||||
[
|
||||
Gitlab::Utils.append_path(current_jira_installation.instance_url, '/-/jira_connect/'),
|
||||
Gitlab::Utils.append_path(current_jira_installation.instance_url, '/api/')
|
||||
Gitlab::Utils.append_path(current_jira_installation.instance_url, '/api/'),
|
||||
Gitlab::Utils.append_path(current_jira_installation.instance_url, '/oauth/token')
|
||||
]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -38,9 +38,9 @@ class Projects::ApplicationController < ApplicationController
|
|||
if build.debug_mode?
|
||||
access_denied!(
|
||||
_('You must have developer or higher permissions in the associated project to view job logs when debug trace ' \
|
||||
"is enabled. To disable debug trace, set the 'CI_DEBUG_TRACE' variable to 'false' in your pipeline " \
|
||||
'configuration or CI/CD settings. If you need to view this job log, a project maintainer or owner must add ' \
|
||||
'you to the project with developer permissions or higher.')
|
||||
"is enabled. To disable debug trace, set the 'CI_DEBUG_TRACE' and 'CI_DEBUG_SERVICES' variables to 'false' " \
|
||||
'in your pipeline configuration or CI/CD settings. If you must view this job log, a project maintainer ' \
|
||||
'or owner must add you to the project with developer permissions or higher.')
|
||||
)
|
||||
else
|
||||
access_denied!(_('The current user is not authorized to access the job log.'))
|
||||
|
|
|
@ -9,7 +9,6 @@ class Projects::IncidentsController < Projects::ApplicationController
|
|||
before_action do
|
||||
push_force_frontend_feature_flag(:work_items, @project&.work_items_feature_flag_enabled?)
|
||||
push_force_frontend_feature_flag(:work_items_mvc_2, @project&.work_items_mvc_2_feature_flag_enabled?)
|
||||
push_frontend_feature_flag(:work_items_hierarchy, @project)
|
||||
end
|
||||
|
||||
feature_category :incident_management
|
||||
|
|
|
@ -54,7 +54,6 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
push_frontend_feature_flag(:realtime_labels, project)
|
||||
push_frontend_feature_flag(:work_items_mvc, project&.group)
|
||||
push_force_frontend_feature_flag(:work_items_mvc_2, project&.work_items_mvc_2_feature_flag_enabled?)
|
||||
push_frontend_feature_flag(:work_items_hierarchy, project)
|
||||
push_frontend_feature_flag(:epic_widget_edit_confirmation, project)
|
||||
push_force_frontend_feature_flag(:work_items_create_from_markdown, project&.work_items_create_from_markdown_feature_flag_enabled?)
|
||||
end
|
||||
|
|
|
@ -8,10 +8,6 @@ module Projects
|
|||
|
||||
before_action :authorize_update_container_image!, only: [:destroy]
|
||||
|
||||
before_action do
|
||||
push_frontend_feature_flag(:container_registry_show_shortened_path, project)
|
||||
end
|
||||
|
||||
def index
|
||||
respond_to do |format|
|
||||
format.html { ensure_root_container_repository! }
|
||||
|
|
|
@ -4,7 +4,6 @@ class Projects::WorkItemsController < Projects::ApplicationController
|
|||
before_action do
|
||||
push_force_frontend_feature_flag(:work_items, project&.work_items_feature_flag_enabled?)
|
||||
push_force_frontend_feature_flag(:work_items_mvc_2, project&.work_items_mvc_2_feature_flag_enabled?)
|
||||
push_frontend_feature_flag(:work_items_hierarchy, project)
|
||||
end
|
||||
|
||||
feature_category :team_planning
|
||||
|
|
|
@ -46,7 +46,6 @@ class ProjectsController < Projects::ApplicationController
|
|||
push_force_frontend_feature_flag(:work_items, @project&.work_items_feature_flag_enabled?)
|
||||
push_force_frontend_feature_flag(:work_items_mvc_2, @project&.work_items_mvc_2_feature_flag_enabled?)
|
||||
push_frontend_feature_flag(:package_registry_access_level)
|
||||
push_frontend_feature_flag(:work_items_hierarchy, @project)
|
||||
end
|
||||
|
||||
before_action only: :edit do
|
||||
|
|
|
@ -10,7 +10,7 @@ module AppearancesHelper
|
|||
|
||||
def default_brand_title
|
||||
# This resides in a separate method so that EE can easily redefine it.
|
||||
'GitLab Community Edition'
|
||||
_('GitLab Community Edition')
|
||||
end
|
||||
|
||||
def brand_image
|
||||
|
|
|
@ -1040,7 +1040,8 @@ module Ci
|
|||
# TODO: Have `debug_mode?` check against data on sent back from runner
|
||||
# to capture all the ways that variables can be set.
|
||||
# See (https://gitlab.com/gitlab-org/gitlab/-/issues/290955)
|
||||
variables['CI_DEBUG_TRACE']&.value&.casecmp('true') == 0
|
||||
variables['CI_DEBUG_TRACE']&.value&.casecmp('true') == 0 ||
|
||||
variables['CI_DEBUG_SERVICES']&.value&.casecmp('true') == 0
|
||||
end
|
||||
|
||||
def drop_with_exit_code!(failure_reason, exit_code)
|
||||
|
|
|
@ -209,7 +209,27 @@ class Wiki
|
|||
# empty Array if this Wiki has no pages.
|
||||
def list_pages(direction: DIRECTION_ASC, load_content: false, limit: 0, offset: 0)
|
||||
create_wiki_repository unless repository_exists?
|
||||
list_pages_with_repository_rpcs(direction: direction, load_content: load_content, limit: limit, offset: offset)
|
||||
|
||||
paths = list_page_paths(limit: limit, offset: offset)
|
||||
return [] if paths.empty?
|
||||
|
||||
pages = paths.map do |path|
|
||||
page = Gitlab::Git::WikiPage.new(
|
||||
url_path: sluggified_title(strip_extension(path)),
|
||||
title: canonicalize_filename(path),
|
||||
format: find_page_format(path),
|
||||
path: sluggified_title(path),
|
||||
raw_data: '',
|
||||
name: canonicalize_filename(path),
|
||||
historical: false
|
||||
)
|
||||
WikiPage.new(self, page)
|
||||
end
|
||||
sort_pages!(pages, direction)
|
||||
pages = pages.take(limit) if limit > 0
|
||||
fetch_pages_content!(pages) if load_content
|
||||
|
||||
pages
|
||||
end
|
||||
|
||||
def sidebar_entries(limit: Gitlab::WikiPages::MAX_SIDEBAR_PAGES, **options)
|
||||
|
@ -229,7 +249,27 @@ class Wiki
|
|||
# Returns an initialized WikiPage instance or nil
|
||||
def find_page(title, version = nil, load_content: true)
|
||||
create_wiki_repository unless repository_exists?
|
||||
find_page_with_repository_rpcs(title, version, load_content: load_content)
|
||||
|
||||
version = version.presence || default_branch
|
||||
path = find_matched_file(title, version)
|
||||
return if path.blank?
|
||||
|
||||
blob_options = load_content ? {} : { limit: 0 }
|
||||
blob = repository.blob_at(version, path, **blob_options)
|
||||
commit = repository.commit(blob.commit_id)
|
||||
format = find_page_format(path)
|
||||
|
||||
page = Gitlab::Git::WikiPage.new(
|
||||
url_path: sluggified_title(strip_extension(path)),
|
||||
title: canonicalize_filename(path),
|
||||
format: format,
|
||||
path: sluggified_title(path),
|
||||
raw_data: blob.data,
|
||||
name: canonicalize_filename(path),
|
||||
historical: version == default_branch ? false : check_page_historical(path, commit),
|
||||
version: Gitlab::Git::WikiPageVersion.new(commit, format)
|
||||
)
|
||||
WikiPage.new(self, page)
|
||||
end
|
||||
|
||||
def find_sidebar(version = nil)
|
||||
|
@ -315,12 +355,6 @@ class Wiki
|
|||
[title, title_array.join("/")]
|
||||
end
|
||||
|
||||
# TODO: This method is redundant. Should be replaced by create_wiki_repository
|
||||
def ensure_repository
|
||||
create_wiki_repository
|
||||
raise CouldNotCreateWikiError unless repository_exists?
|
||||
end
|
||||
|
||||
def hook_attrs
|
||||
{
|
||||
web_url: web_url,
|
||||
|
@ -472,29 +506,6 @@ class Wiki
|
|||
repository.last_commit_for_path(default_branch, path)&.id != commit&.id
|
||||
end
|
||||
|
||||
def find_page_with_repository_rpcs(title, version, load_content: true)
|
||||
version = version.presence || default_branch
|
||||
path = find_matched_file(title, version)
|
||||
return if path.blank?
|
||||
|
||||
blob_options = load_content ? {} : { limit: 0 }
|
||||
blob = repository.blob_at(version, path, **blob_options)
|
||||
commit = repository.commit(blob.commit_id)
|
||||
format = find_page_format(path)
|
||||
|
||||
page = Gitlab::Git::WikiPage.new(
|
||||
url_path: sluggified_title(strip_extension(path)),
|
||||
title: canonicalize_filename(path),
|
||||
format: format,
|
||||
path: sluggified_title(path),
|
||||
raw_data: blob.data,
|
||||
name: canonicalize_filename(path),
|
||||
historical: version == default_branch ? false : check_page_historical(path, commit),
|
||||
version: Gitlab::Git::WikiPageVersion.new(commit, format)
|
||||
)
|
||||
WikiPage.new(self, page)
|
||||
end
|
||||
|
||||
def file_extension_regexp
|
||||
# We could not use ALLOWED_EXTENSIONS_REGEX constant or similar regexp with
|
||||
# Regexp.union. The result combination complicated modifiers:
|
||||
|
@ -516,29 +527,6 @@ class Wiki
|
|||
repository.search_files_by_regexp(path_regexp, default_branch, limit: limit, offset: offset)
|
||||
end
|
||||
|
||||
def list_pages_with_repository_rpcs(direction:, load_content:, limit:, offset:)
|
||||
paths = list_page_paths(limit: limit, offset: offset)
|
||||
return [] if paths.empty?
|
||||
|
||||
pages = paths.map do |path|
|
||||
page = Gitlab::Git::WikiPage.new(
|
||||
url_path: sluggified_title(strip_extension(path)),
|
||||
title: canonicalize_filename(path),
|
||||
format: find_page_format(path),
|
||||
path: sluggified_title(path),
|
||||
raw_data: '',
|
||||
name: canonicalize_filename(path),
|
||||
historical: false
|
||||
)
|
||||
WikiPage.new(self, page)
|
||||
end
|
||||
sort_pages!(pages, direction)
|
||||
pages = pages.take(limit) if limit > 0
|
||||
fetch_pages_content!(pages) if load_content
|
||||
|
||||
pages
|
||||
end
|
||||
|
||||
# After migrating to normal repository RPCs, it's very expensive to sort the
|
||||
# pages by created_at. We have to either ListLastCommitsForTree RPC call or
|
||||
# N+1 LastCommitForPath. Either are efficient for a large repository.
|
||||
|
|
|
@ -7,7 +7,6 @@ module WorkItems
|
|||
private
|
||||
|
||||
def handle_hierarchy_changes(params)
|
||||
return feature_flag_error unless feature_flag_enabled?
|
||||
return incompatible_args_error if incompatible_args?(params)
|
||||
|
||||
if params.key?(:parent)
|
||||
|
@ -48,18 +47,10 @@ module WorkItems
|
|||
.execute
|
||||
end
|
||||
|
||||
def feature_flag_enabled?
|
||||
Feature.enabled?(:work_items_hierarchy, work_item&.project)
|
||||
end
|
||||
|
||||
def incompatible_args?(params)
|
||||
params[:children] && params[:parent]
|
||||
end
|
||||
|
||||
def feature_flag_error
|
||||
error(_('`work_items_hierarchy` feature flag disabled for this project'))
|
||||
end
|
||||
|
||||
def incompatible_args_error
|
||||
error(_('A Work Item can be a parent or a child, but not both.'))
|
||||
end
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
- @hide_top_links = true
|
||||
- page_title _("Issues")
|
||||
- @breadcrumb_link = issues_dashboard_path(assignee_username: current_user.username)
|
||||
- add_page_specific_style 'page_bundles/dashboard'
|
||||
= content_for :meta_tags do
|
||||
= auto_discovery_link_tag(:atom, safe_params.merge(rss_url_options).to_h, title: "#{current_user.name} issues")
|
||||
|
||||
|
@ -13,14 +14,20 @@
|
|||
.page-title-controls
|
||||
= render 'shared/new_project_item_select', path: 'issues/new', label: _("issue"), with_feature_enabled: 'issues', type: :issues
|
||||
|
||||
.top-area
|
||||
= render 'shared/issuable/nav', type: :issues, display_count: !@no_filters_set
|
||||
.nav-controls
|
||||
= render 'shared/issuable/feed_buttons'
|
||||
|
||||
= render 'shared/issuable/search_bar', type: :issues
|
||||
|
||||
- if current_user && @no_filters_set
|
||||
= render 'shared/dashboard/no_filter_selected'
|
||||
- if ::Feature.enabled?(:vue_issues_dashboard)
|
||||
.js-issues-dashboard{ data: { calendar_path: url_for(safe_params.merge(calendar_url_options)),
|
||||
empty_state_svg_path: image_path('illustrations/issue-dashboard_results-without-filter.svg'),
|
||||
is_signed_in: current_user.present?.to_s,
|
||||
rss_path: url_for(safe_params.merge(rss_url_options)) } }
|
||||
- else
|
||||
= render 'shared/issues'
|
||||
.top-area
|
||||
= render 'shared/issuable/nav', type: :issues, display_count: !@no_filters_set
|
||||
.nav-controls
|
||||
= render 'shared/issuable/feed_buttons'
|
||||
|
||||
= render 'shared/issuable/search_bar', type: :issues
|
||||
|
||||
- if current_user && @no_filters_set
|
||||
= render 'shared/dashboard/no_filter_selected'
|
||||
- else
|
||||
= render 'shared/issues'
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
- if Feature.enabled?(:work_items_hierarchy, @project)
|
||||
.js-work-item-links-root{ data: { issuable_id: @issue.id, iid: @issue.iid, project_namespace: @project.namespace.path, project_path: @project.full_path, wi: work_items_index_data(@project) } }
|
||||
.js-work-item-links-root{ data: { issuable_id: @issue.id, iid: @issue.iid, project_namespace: @project.namespace.path, project_path: @project.full_path, wi: work_items_index_data(@project) } }
|
||||
|
|
|
@ -268,6 +268,7 @@ module Gitlab
|
|||
config.assets.precompile << "page_bundles/cluster_agents.css"
|
||||
config.assets.precompile << "page_bundles/clusters.css"
|
||||
config.assets.precompile << "page_bundles/cycle_analytics.css"
|
||||
config.assets.precompile << "page_bundles/dashboard.css"
|
||||
config.assets.precompile << "page_bundles/dashboard_projects.css"
|
||||
config.assets.precompile << "page_bundles/design_management.css"
|
||||
config.assets.precompile << "page_bundles/dev_ops_reports.css"
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
---
|
||||
name: container_registry_show_shortened_path
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91548
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/366808
|
||||
milestone: '15.2'
|
||||
name: vue_issues_dashboard
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102197
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/379025
|
||||
milestone: '15.6'
|
||||
type: development
|
||||
group: group::package
|
||||
group: group::project management
|
||||
default_enabled: false
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: work_items_hierarchy
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/88504
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/363447
|
||||
milestone: '15.1'
|
||||
type: development
|
||||
group: group::product planning
|
||||
default_enabled: true
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddCommitCommitterNameCheckToPushRules < Gitlab::Database::Migration[2.0]
|
||||
def change
|
||||
add_column :push_rules, :commit_committer_name_check, :boolean, default: false, null: false
|
||||
end
|
||||
end
|
|
@ -0,0 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddFindingDataColumnToSecurityFindings < Gitlab::Database::Migration[2.0]
|
||||
enable_lock_retries!
|
||||
|
||||
def up
|
||||
add_column :security_findings, :finding_data, :jsonb, default: {}, null: false
|
||||
end
|
||||
|
||||
def down
|
||||
remove_column :security_findings, :finding_data
|
||||
end
|
||||
end
|
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class PrepareForVulnerabilityOccurrencesUuidTypeTransition < Gitlab::Database::Migration[2.0]
|
||||
enable_lock_retries!
|
||||
|
||||
TABLE = :vulnerability_occurrences
|
||||
MAPPINGS = {
|
||||
uuid: {
|
||||
from_type: :string,
|
||||
to_type: :uuid,
|
||||
default_value: '00000000-0000-0000-0000-000000000000'
|
||||
}
|
||||
}
|
||||
|
||||
def up
|
||||
create_temporary_columns_and_triggers(TABLE, MAPPINGS)
|
||||
end
|
||||
|
||||
def down
|
||||
columns = MAPPINGS.keys
|
||||
temporary_columns = columns.map { |column| convert_to_type_column(column, :string, :uuid) }
|
||||
trigger_name = rename_trigger_name(TABLE, columns, temporary_columns)
|
||||
remove_rename_triggers(TABLE, trigger_name)
|
||||
temporary_columns.each { |column| remove_column(TABLE, column) }
|
||||
end
|
||||
end
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CheckVulnerabilitiesStateTransitionFromStateNotEqualToState < Gitlab::Database::Migration[2.0]
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_check_constraint(:vulnerability_state_transitions, '(from_state != to_state)', constraint_name)
|
||||
end
|
||||
|
||||
def down
|
||||
remove_check_constraint(:vulnerability_state_transitions, constraint_name)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def constraint_name
|
||||
check_constraint_name('vulnerability_state_transitions', 'fully_qualified_table_name', 'state_not_equal')
|
||||
end
|
||||
end
|
1
db/schema_migrations/20220927171740
Normal file
1
db/schema_migrations/20220927171740
Normal file
|
@ -0,0 +1 @@
|
|||
5ba49d525d6238975f990c94972ee4f3587a2446a4873e6e8a7f4791cf015b7e
|
1
db/schema_migrations/20221025115006
Normal file
1
db/schema_migrations/20221025115006
Normal file
|
@ -0,0 +1 @@
|
|||
1529e1b436b65ff7b787f43fc5b8de7515aebe427719d2e4e62e9a7f923e877b
|
1
db/schema_migrations/20221028015347
Normal file
1
db/schema_migrations/20221028015347
Normal file
|
@ -0,0 +1 @@
|
|||
7b86ae0739c4c381b050539261c67dbf3d4716edf0f0bde9b281cbdc5143a4d2
|
1
db/schema_migrations/20221028152422
Normal file
1
db/schema_migrations/20221028152422
Normal file
|
@ -0,0 +1 @@
|
|||
c9322bdc7e862bd20ec548fbcd3ec6a9ef4da6abc0a688d503e1792acc262472
|
|
@ -225,6 +225,15 @@ RETURN NULL;
|
|||
END
|
||||
$$;
|
||||
|
||||
CREATE FUNCTION trigger_1a857e8db6cd() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
NEW."uuid_convert_string_to_uuid" := NEW."uuid";
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
CREATE FUNCTION unset_has_issues_on_vulnerability_reads() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
|
@ -434,6 +443,7 @@ CREATE TABLE security_findings (
|
|||
uuid uuid,
|
||||
overridden_uuid uuid,
|
||||
partition_number integer DEFAULT 1 NOT NULL,
|
||||
finding_data jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
CONSTRAINT check_6c2851a8c9 CHECK ((uuid IS NOT NULL)),
|
||||
CONSTRAINT check_b9508c6df8 CHECK ((char_length(project_fingerprint) <= 40))
|
||||
)
|
||||
|
@ -20652,7 +20662,8 @@ CREATE TABLE push_rules (
|
|||
commit_committer_check boolean,
|
||||
regexp_uses_re2 boolean DEFAULT true,
|
||||
commit_message_negative_regex character varying,
|
||||
reject_non_dco_commits boolean
|
||||
reject_non_dco_commits boolean,
|
||||
commit_committer_name_check boolean DEFAULT false NOT NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE push_rules_id_seq
|
||||
|
@ -22945,6 +22956,7 @@ CREATE TABLE vulnerability_occurrences (
|
|||
cve text,
|
||||
location jsonb,
|
||||
detection_method smallint DEFAULT 0 NOT NULL,
|
||||
uuid_convert_string_to_uuid uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL,
|
||||
CONSTRAINT check_4a3a60f2ba CHECK ((char_length(solution) <= 7000)),
|
||||
CONSTRAINT check_ade261da6b CHECK ((char_length(description) <= 15000)),
|
||||
CONSTRAINT check_df6dd20219 CHECK ((char_length(message) <= 3000)),
|
||||
|
@ -23041,7 +23053,8 @@ CREATE TABLE vulnerability_state_transitions (
|
|||
author_id bigint,
|
||||
comment text,
|
||||
dismissal_reason smallint,
|
||||
CONSTRAINT check_fca4a7ca39 CHECK ((char_length(comment) <= 255))
|
||||
CONSTRAINT check_fca4a7ca39 CHECK ((char_length(comment) <= 255)),
|
||||
CONSTRAINT state_not_equal CHECK ((from_state <> to_state))
|
||||
);
|
||||
|
||||
CREATE SEQUENCE vulnerability_state_transitions_id_seq
|
||||
|
@ -32516,6 +32529,8 @@ CREATE TRIGGER nullify_merge_request_metrics_build_data_on_update BEFORE UPDATE
|
|||
|
||||
CREATE TRIGGER projects_loose_fk_trigger AFTER DELETE ON projects REFERENCING OLD TABLE AS old_table FOR EACH STATEMENT EXECUTE FUNCTION insert_into_loose_foreign_keys_deleted_records();
|
||||
|
||||
CREATE TRIGGER trigger_1a857e8db6cd BEFORE INSERT OR UPDATE ON vulnerability_occurrences FOR EACH ROW EXECUTE FUNCTION trigger_1a857e8db6cd();
|
||||
|
||||
CREATE TRIGGER trigger_delete_project_namespace_on_project_delete AFTER DELETE ON projects FOR EACH ROW WHEN ((old.project_namespace_id IS NOT NULL)) EXECUTE FUNCTION delete_associated_project_namespace();
|
||||
|
||||
CREATE TRIGGER trigger_has_external_issue_tracker_on_delete AFTER DELETE ON integrations FOR EACH ROW WHEN ((((old.category)::text = 'issue_tracker'::text) AND (old.active = true) AND (old.project_id IS NOT NULL))) EXECUTE FUNCTION set_has_external_issue_tracker();
|
||||
|
|
|
@ -14,3 +14,4 @@ swap:
|
|||
note that: "Be concise: rewrite the sentence to not use"
|
||||
please: "Remove this word from the sentence: "
|
||||
respectively: "Rewrite the sentence to be more precise, instead of using "
|
||||
and so on: "Be more specific, rewrite the sentence to not use"
|
||||
|
|
|
@ -46,11 +46,11 @@ GET /runners?tag_list=tag1,tag2
|
|||
|
||||
| Attribute | Type | Required | Description |
|
||||
|------------|--------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of specific runners to show, one of: `active`, `paused`, `online` and `offline`; showing all runners if none provided |
|
||||
| `type` | string | no | The type of runners to show, one of: `instance_type`, `group_type`, `project_type` |
|
||||
| `status` | string | no | The status of runners to show, one of: `online`, `offline`, `stale`, and `never_contacted`. `active` and `paused` are also possible values which were deprecated in GitLab 14.8 and will be removed in GitLab 16.0 |
|
||||
| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of specific runners to return, one of: `active`, `paused`, `online` and `offline`; showing all runners if none provided |
|
||||
| `type` | string | no | The type of runners to return, one of: `instance_type`, `group_type`, `project_type` |
|
||||
| `status` | string | no | The status of runners to return, one of: `online`, `offline`, `stale`, and `never_contacted`. `active` and `paused` are also possible values which were deprecated in GitLab 14.8 and will be removed in GitLab 16.0 |
|
||||
| `paused` | boolean | no | Whether to include only runners that are accepting or ignoring new jobs |
|
||||
| `tag_list` | string array | no | List of the runner's tags |
|
||||
| `tag_list` | string array | no | A list of runner tags |
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/runners"
|
||||
|
@ -111,11 +111,11 @@ GET /runners/all?tag_list=tag1,tag2
|
|||
|
||||
| Attribute | Type | Required | Description |
|
||||
|------------|--------------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of runners to show, one of: `specific`, `shared`, `active`, `paused`, `online` and `offline`; showing all runners if none provided |
|
||||
| `type` | string | no | The type of runners to show, one of: `instance_type`, `group_type`, `project_type` |
|
||||
| `status` | string | no | The status of runners to show, one of: `online`, `offline`, `stale`, and `never_contacted`. `active` and `paused` are also possible values which were deprecated in GitLab 14.8 and will be removed in GitLab 16.0 |
|
||||
| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of runners to return, one of: `specific`, `shared`, `active`, `paused`, `online` and `offline`; showing all runners if none provided |
|
||||
| `type` | string | no | The type of runners to return, one of: `instance_type`, `group_type`, `project_type` |
|
||||
| `status` | string | no | The status of runners to return, one of: `online`, `offline`, `stale`, and `never_contacted`. `active` and `paused` are also possible values which were deprecated in GitLab 14.8 and will be removed in GitLab 16.0 |
|
||||
| `paused` | boolean | no | Whether to include only runners that are accepting or ignoring new jobs |
|
||||
| `tag_list` | string array | no | List of the runner's tags |
|
||||
| `tag_list` | string array | no | A list of runner tags |
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/runners/all"
|
||||
|
@ -260,17 +260,17 @@ Update details of a runner.
|
|||
PUT /runners/:id
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|-------------------|---------|----------|--------------------------------------------------------------------------------------------------|
|
||||
| `id` | integer | yes | The ID of a runner |
|
||||
| `description` | string | no | The description of a runner |
|
||||
| `active` | boolean | no | Deprecated: Use `:paused` instead. Flag indicating whether the runner is allowed to receive jobs |
|
||||
| `paused` | boolean | no | Flag indicating whether the runner should ignore new jobs |
|
||||
| `tag_list` | array | no | The list of tags for a runner; put array of tags, that should be finally assigned to a runner |
|
||||
| `run_untagged` | boolean | no | Flag indicating the runner can execute untagged jobs |
|
||||
| `locked` | boolean | no | Flag indicating the runner is locked |
|
||||
| `access_level` | string | no | The access_level of the runner; `not_protected` or `ref_protected` |
|
||||
| `maximum_timeout` | integer | no | Maximum timeout set when this runner handles the job |
|
||||
| Attribute | Type | Required | Description |
|
||||
|-------------------|---------|----------|-------------------------------------------------------------------------------------------------|
|
||||
| `id` | integer | yes | The ID of a runner |
|
||||
| `description` | string | no | The description of the runner |
|
||||
| `active` | boolean | no | Deprecated: Use `paused` instead. Flag indicating whether the runner is allowed to receive jobs |
|
||||
| `paused` | boolean | no | Specifies whether the runner should ignore new jobs |
|
||||
| `tag_list` | array | no | The list of tags for the runner |
|
||||
| `run_untagged` | boolean | no | Specifies whether the runner can execute untagged jobs |
|
||||
| `locked` | boolean | no | Specifies whether the runner is locked |
|
||||
| `access_level` | string | no | The access level of the runner; `not_protected` or `ref_protected` |
|
||||
| `maximum_timeout` | integer | no | Maximum timeout that limits the amount of time (in seconds) that runners can run jobs |
|
||||
|
||||
```shell
|
||||
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/runners/6" \
|
||||
|
@ -370,7 +370,7 @@ GET /runners/:id/jobs
|
|||
|-----------|---------|----------|---------------------|
|
||||
| `id` | integer | yes | The ID of a runner |
|
||||
| `status` | string | no | Status of the job; one of: `running`, `success`, `failed`, `canceled` |
|
||||
| `order_by`| string | no | Order jobs by `id`. |
|
||||
| `order_by`| string | no | Order jobs by `id` |
|
||||
| `sort` | string | no | Sort jobs in `asc` or `desc` order (default: `desc`). Specify `order_by` as well, including for `id`. |
|
||||
|
||||
```shell
|
||||
|
@ -463,11 +463,11 @@ GET /projects/:id/runners?tag_list=tag1,tag2
|
|||
| Attribute | Type | Required | Description |
|
||||
|------------|----------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
|
||||
| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of specific runners to show, one of: `active`, `paused`, `online` and `offline`; showing all runners if none provided |
|
||||
| `type` | string | no | The type of runners to show, one of: `instance_type`, `group_type`, `project_type` |
|
||||
| `status` | string | no | The status of runners to show, one of: `online`, `offline`, `stale`, and `never_contacted`. `active` and `paused` are also possible values which were deprecated in GitLab 14.8 and will be removed in GitLab 16.0 |
|
||||
| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of specific runners to return, one of: `active`, `paused`, `online` and `offline`; showing all runners if none provided |
|
||||
| `type` | string | no | The type of runners to return, one of: `instance_type`, `group_type`, `project_type` |
|
||||
| `status` | string | no | The status of runners to return, one of: `online`, `offline`, `stale`, and `never_contacted`. `active` and `paused` are also possible values which were deprecated in GitLab 14.8 and will be removed in GitLab 16.0 |
|
||||
| `paused` | boolean | no | Whether to include only runners that are accepting or ignoring new jobs |
|
||||
| `tag_list` | string array | no | List of the runner's tags |
|
||||
| `tag_list` | string array | no | A list of runner tags |
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/9/runners"
|
||||
|
@ -580,10 +580,10 @@ GET /groups/:id/runners?tag_list=tag1,tag2
|
|||
| Attribute | Type | Required | Description |
|
||||
|------------|----------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | integer | yes | The ID of the group owned by the authenticated user |
|
||||
| `type` | string | no | The type of runners to show, one of: `instance_type`, `group_type`, `project_type`. The `project_type` value is [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/351466) and will be removed in GitLab 15.0 |
|
||||
| `status` | string | no | The status of runners to show, one of: `online`, `offline`, `stale`, and `never_contacted`. `active` and `paused` are also possible values which were deprecated in GitLab 14.8 and will be removed in GitLab 16.0 |
|
||||
| `type` | string | no | The type of runners to return, one of: `instance_type`, `group_type`, `project_type`. The `project_type` value is [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/351466) and will be removed in GitLab 15.0 |
|
||||
| `status` | string | no | The status of runners to return, one of: `online`, `offline`, `stale`, and `never_contacted`. `active` and `paused` are also possible values which were deprecated in GitLab 14.8 and will be removed in GitLab 16.0 |
|
||||
| `paused` | boolean | no | Whether to include only runners that are accepting or ignoring new jobs |
|
||||
| `tag_list` | string array | no | List of the runner's tags |
|
||||
| `tag_list` | string array | no | A list of runner tags |
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/9/runners"
|
||||
|
@ -774,7 +774,7 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \
|
|||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30942) in GitLab 14.3.
|
||||
|
||||
Resets the runner registration token for a project.
|
||||
Reset the runner registration token for a project.
|
||||
|
||||
```plaintext
|
||||
POST /projects/:id/runners/reset_registration_token
|
||||
|
@ -789,7 +789,7 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \
|
|||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30942) in GitLab 14.3.
|
||||
|
||||
Resets the runner registration token for a group.
|
||||
Reset the runner registration token for a group.
|
||||
|
||||
```plaintext
|
||||
POST /groups/:id/runners/reset_registration_token
|
||||
|
@ -802,7 +802,7 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \
|
|||
|
||||
## Reset runner's authentication token by using the runner ID
|
||||
|
||||
Resets the runner's authentication token by using its runner ID.
|
||||
Reset the runner's authentication token by using its runner ID.
|
||||
|
||||
```plaintext
|
||||
POST /runners/:id/reset_authentication_token
|
||||
|
|
|
@ -356,6 +356,8 @@ Advanced vulnerability tracking is available in a subset of the [supported langu
|
|||
|
||||
Support for more languages and analyzers is tracked in [this epic](https://gitlab.com/groups/gitlab-org/-/epics/5144).
|
||||
|
||||
For more information, see the confidential project `https://gitlab.com/gitlab-org/security-products/post-analyzers/tracking-calculator`. The content of this project is available only to GitLab team members.
|
||||
|
||||
### Using CI/CD variables to pass credentials for private repositories
|
||||
|
||||
Some analyzers require downloading the project's dependencies to
|
||||
|
|
|
@ -72,6 +72,12 @@ To purge files from a GitLab repository:
|
|||
cd project.git
|
||||
```
|
||||
|
||||
1. Because cloning from a bundle file sets the `origin` remote to the local bundle file, change it to the URL of your repository:
|
||||
|
||||
```shell
|
||||
git remote set-url origin https://gitlab.example.com/<namespace>/<project_name>.git
|
||||
```
|
||||
|
||||
1. Using either `git filter-repo` or `git-sizer`, analyze your repository
|
||||
and review the results to determine which items you want to purge:
|
||||
|
||||
|
@ -84,38 +90,39 @@ To purge files from a GitLab repository:
|
|||
git-sizer
|
||||
```
|
||||
|
||||
1. Proceed to purging any files from the history of your repository. Because we are
|
||||
trying to remove internal refs, we rely on the `commit-map` produced by each run to tell us
|
||||
which internal refs to remove.
|
||||
1. Purge the history of your repository using relevant `git filter-repo` options.
|
||||
Two common options are:
|
||||
|
||||
NOTE:
|
||||
`git filter-repo` creates a new `commit-map` file every run, and overwrites the `commit-map` from
|
||||
the previous run. You need this file from **every** run. Do the next step every time you run
|
||||
`git filter-repo`.
|
||||
- `--path` and `--invert-paths` to purge specific files:
|
||||
|
||||
To purge specific files, the `--path` and `--invert-paths` options can be combined:
|
||||
```shell
|
||||
git filter-repo --path path/to/file.ext --invert-paths
|
||||
```
|
||||
|
||||
```shell
|
||||
git filter-repo --path path/to/file.ext --invert-paths
|
||||
```
|
||||
- `--strip-blobs-bigger-than` to purge all files larger than for example 10M:
|
||||
|
||||
To generally purge all files larger than 10M, the `--strip-blobs-bigger-than` option can be used:
|
||||
|
||||
```shell
|
||||
git filter-repo --strip-blobs-bigger-than 10M
|
||||
```
|
||||
```shell
|
||||
git filter-repo --strip-blobs-bigger-than 10M
|
||||
```
|
||||
|
||||
See the
|
||||
[`git filter-repo` documentation](https://htmlpreview.github.io/?https://github.com/newren/git-filter-repo/blob/docs/html/git-filter-repo.html#EXAMPLES)
|
||||
for more examples and the complete documentation.
|
||||
|
||||
1. Because cloning from a bundle file sets the `origin` remote to the local bundle file, delete this `origin` remote, and set it to the URL to your repository:
|
||||
1. Because you are trying to remove internal refs,
|
||||
you'll later rely on `commit-map` files produced by each run
|
||||
to tell you which internal refs to remove.
|
||||
Every `git filter-repo` run creates a new `commit-map`,
|
||||
and overwrites the `commit-map` from the previous run.
|
||||
You can use the following command to back up each `commit-map` file:
|
||||
|
||||
```shell
|
||||
git remote remove origin
|
||||
git remote add origin https://gitlab.example.com/<namespace>/<project_name>.git
|
||||
cp .git/filter-repo/commit-map ./_filter_repo_commit_map_$(date +%s)
|
||||
```
|
||||
|
||||
Repeat this step and all following steps (including the [repository cleanup](#repository-cleanup) step)
|
||||
every time you run any `git filter-repo` command.
|
||||
|
||||
1. Force push your changes to overwrite all branches on GitLab:
|
||||
|
||||
```shell
|
||||
|
|
|
@ -18,7 +18,7 @@ For the latest updates, check the [Tasks Roadmap](https://gitlab.com/groups/gitl
|
|||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default this feature is available. To hide the feature,
|
||||
ask an administrator to [disable the feature flags](../administration/feature_flags.md) named `work_items` and `work_items_hierarchy`.
|
||||
ask an administrator to [disable the feature flags](../administration/feature_flags.md) named `work_items`.
|
||||
On GitLab.com, this feature is available.
|
||||
|
||||
Use tasks to track steps needed for the [issue](project/issues/index.md) to be closed.
|
||||
|
|
|
@ -174,6 +174,7 @@ module API
|
|||
mount ::API::Appearance
|
||||
mount ::API::BulkImports
|
||||
mount ::API::Ci::Runner
|
||||
mount ::API::Ci::Runners
|
||||
mount ::API::Clusters::Agents
|
||||
mount ::API::Clusters::AgentTokens
|
||||
mount ::API::DeployKeys
|
||||
|
@ -218,7 +219,6 @@ module API
|
|||
mount ::API::Ci::PipelineSchedules
|
||||
mount ::API::Ci::Pipelines
|
||||
mount ::API::Ci::ResourceGroups
|
||||
mount ::API::Ci::Runners
|
||||
mount ::API::Ci::SecureFiles
|
||||
mount ::API::Ci::Triggers
|
||||
mount ::API::Ci::Variables
|
||||
|
|
|
@ -10,296 +10,23 @@ module API
|
|||
feature_category :runner
|
||||
urgency :low
|
||||
|
||||
resource :runners do
|
||||
desc 'Get runners available for user' do
|
||||
success Entities::Ci::Runner
|
||||
end
|
||||
params do
|
||||
optional :scope, type: String, values: ::Ci::Runner::AVAILABLE_STATUSES,
|
||||
desc: 'The scope of specific runners to show'
|
||||
optional :type, type: String, values: ::Ci::Runner::AVAILABLE_TYPES,
|
||||
desc: 'The type of the runners to show'
|
||||
optional :paused, type: Boolean, desc: 'Whether to include only runners that are accepting or ignoring new jobs'
|
||||
optional :status, type: String, values: ::Ci::Runner::AVAILABLE_STATUSES,
|
||||
desc: 'The status of the runners to show'
|
||||
optional :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'The tags of the runners to show'
|
||||
use :pagination
|
||||
end
|
||||
get do
|
||||
runners = current_user.ci_owned_runners
|
||||
runners = filter_runners(runners, params[:scope], allowed_scopes: ::Ci::Runner::AVAILABLE_STATUSES)
|
||||
runners = apply_filter(runners, params)
|
||||
|
||||
present paginate(runners), with: Entities::Ci::Runner
|
||||
end
|
||||
|
||||
desc 'Get all runners - shared and specific' do
|
||||
success Entities::Ci::Runner
|
||||
end
|
||||
params do
|
||||
optional :scope, type: String, values: ::Ci::Runner::AVAILABLE_SCOPES,
|
||||
desc: 'The scope of specific runners to show'
|
||||
optional :type, type: String, values: ::Ci::Runner::AVAILABLE_TYPES,
|
||||
desc: 'The type of the runners to show'
|
||||
optional :paused, type: Boolean, desc: 'Whether to include only runners that are accepting or ignoring new jobs'
|
||||
optional :status, type: String, values: ::Ci::Runner::AVAILABLE_STATUSES,
|
||||
desc: 'The status of the runners to show'
|
||||
optional :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'The tags of the runners to show'
|
||||
use :pagination
|
||||
end
|
||||
get 'all' do
|
||||
authenticated_as_admin!
|
||||
|
||||
runners = ::Ci::Runner.all
|
||||
runners = filter_runners(runners, params[:scope])
|
||||
runners = apply_filter(runners, params)
|
||||
|
||||
present paginate(runners), with: Entities::Ci::Runner
|
||||
end
|
||||
|
||||
desc "Get runner's details" do
|
||||
success Entities::Ci::RunnerDetails
|
||||
end
|
||||
params do
|
||||
requires :id, type: Integer, desc: 'The ID of the runner'
|
||||
end
|
||||
get ':id' do
|
||||
runner = get_runner(params[:id])
|
||||
authenticate_show_runner!(runner)
|
||||
|
||||
present runner, with: Entities::Ci::RunnerDetails, current_user: current_user
|
||||
end
|
||||
|
||||
desc "Update runner's details" do
|
||||
success Entities::Ci::RunnerDetails
|
||||
end
|
||||
params do
|
||||
requires :id, type: Integer, desc: 'The ID of the runner'
|
||||
optional :description, type: String, desc: 'The description of the runner'
|
||||
optional :active, type: Boolean, desc: 'Deprecated: Use `:paused` instead. Flag indicating whether the runner is allowed to receive jobs'
|
||||
optional :paused, type: Boolean, desc: 'Flag indicating whether the runner should ignore new jobs'
|
||||
optional :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'The list of tags for a runner'
|
||||
optional :run_untagged, type: Boolean, desc: 'Flag indicating whether the runner can execute untagged jobs'
|
||||
optional :locked, type: Boolean, desc: 'Flag indicating the runner is locked'
|
||||
optional :access_level, type: String, values: ::Ci::Runner.access_levels.keys,
|
||||
desc: 'The access_level of the runner'
|
||||
optional :maximum_timeout, type: Integer, desc: 'Maximum timeout set when this Runner will handle the job'
|
||||
at_least_one_of :description, :active, :paused, :tag_list, :run_untagged, :locked, :access_level, :maximum_timeout
|
||||
mutually_exclusive :active, :paused
|
||||
end
|
||||
put ':id' do
|
||||
runner = get_runner(params.delete(:id))
|
||||
authenticate_update_runner!(runner)
|
||||
params[:active] = !params.delete(:paused) if params.include?(:paused)
|
||||
update_service = ::Ci::Runners::UpdateRunnerService.new(runner)
|
||||
|
||||
if update_service.execute(declared_params(include_missing: false)).success?
|
||||
present runner, with: Entities::Ci::RunnerDetails, current_user: current_user
|
||||
else
|
||||
render_validation_error!(runner)
|
||||
end
|
||||
end
|
||||
|
||||
desc 'Remove a runner' do
|
||||
success Entities::Ci::Runner
|
||||
end
|
||||
params do
|
||||
requires :id, type: Integer, desc: 'The ID of the runner'
|
||||
end
|
||||
delete ':id' do
|
||||
runner = get_runner(params[:id])
|
||||
|
||||
authenticate_delete_runner!(runner)
|
||||
|
||||
destroy_conditionally!(runner) { ::Ci::Runners::UnregisterRunnerService.new(runner, current_user).execute }
|
||||
end
|
||||
|
||||
desc 'List jobs running on a runner' do
|
||||
success Entities::Ci::JobBasicWithProject
|
||||
end
|
||||
params do
|
||||
requires :id, type: Integer, desc: 'The ID of the runner'
|
||||
optional :status, type: String, desc: 'Status of the job', values: ::Ci::Build::AVAILABLE_STATUSES
|
||||
optional :order_by, type: String, desc: 'Order by `id` or not', values: ::Ci::RunnerJobsFinder::ALLOWED_INDEXED_COLUMNS
|
||||
optional :sort, type: String, values: %w[asc desc], default: 'desc', desc: 'Sort by asc (ascending) or desc (descending)'
|
||||
use :pagination
|
||||
end
|
||||
get ':id/jobs' do
|
||||
runner = get_runner(params[:id])
|
||||
authenticate_list_runners_jobs!(runner)
|
||||
|
||||
jobs = ::Ci::RunnerJobsFinder.new(runner, current_user, params).execute
|
||||
jobs = jobs.preload( # rubocop: disable CodeReuse/ActiveRecord
|
||||
[
|
||||
:user,
|
||||
{ pipeline: { project: [:route, { namespace: :route }] } },
|
||||
{ project: [:route, { namespace: :route }] }
|
||||
]
|
||||
)
|
||||
jobs = paginate(jobs)
|
||||
jobs.each(&:commit) # batch loads all commits in the page
|
||||
|
||||
present jobs, with: Entities::Ci::JobBasicWithProject
|
||||
end
|
||||
|
||||
desc 'Reset runner authentication token' do
|
||||
success Entities::Ci::ResetTokenResult
|
||||
end
|
||||
params do
|
||||
requires :id, type: Integer, desc: 'The ID of the runner'
|
||||
end
|
||||
post ':id/reset_authentication_token' do
|
||||
runner = get_runner(params[:id])
|
||||
authenticate_update_runner!(runner)
|
||||
|
||||
runner.reset_token!
|
||||
present runner.token_with_expiration, with: Entities::Ci::ResetTokenResult
|
||||
end
|
||||
end
|
||||
|
||||
params do
|
||||
requires :id, type: String, desc: 'The ID of a project'
|
||||
end
|
||||
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
|
||||
before { authorize_admin_project }
|
||||
|
||||
desc 'Get runners available for project' do
|
||||
success Entities::Ci::Runner
|
||||
end
|
||||
params do
|
||||
optional :scope, type: String, values: ::Ci::Runner::AVAILABLE_SCOPES,
|
||||
desc: 'The scope of specific runners to show'
|
||||
optional :type, type: String, values: ::Ci::Runner::AVAILABLE_TYPES,
|
||||
desc: 'The type of the runners to show'
|
||||
optional :paused, type: Boolean, desc: 'Whether to include only runners that are accepting or ignoring new jobs'
|
||||
optional :status, type: String, values: ::Ci::Runner::AVAILABLE_STATUSES,
|
||||
desc: 'The status of the runners to show'
|
||||
optional :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'The tags of the runners to show'
|
||||
use :pagination
|
||||
end
|
||||
get ':id/runners' do
|
||||
runners = ::Ci::Runner.owned_or_instance_wide(user_project.id)
|
||||
# scope is deprecated (for project runners), however api documentation still supports it.
|
||||
# Not including them in `apply_filter` method as it's not supported for group runners
|
||||
runners = filter_runners(runners, params[:scope])
|
||||
runners = apply_filter(runners, params)
|
||||
|
||||
present paginate(runners), with: Entities::Ci::Runner
|
||||
end
|
||||
|
||||
desc 'Enable a runner for a project' do
|
||||
success Entities::Ci::Runner
|
||||
end
|
||||
params do
|
||||
requires :runner_id, type: Integer, desc: 'The ID of the runner'
|
||||
end
|
||||
post ':id/runners' do
|
||||
runner = get_runner(params[:runner_id])
|
||||
authenticate_enable_runner!(runner)
|
||||
|
||||
if ::Ci::Runners::AssignRunnerService.new(runner, user_project, current_user).execute.success?
|
||||
present runner, with: Entities::Ci::Runner
|
||||
else
|
||||
render_validation_error!(runner)
|
||||
end
|
||||
end
|
||||
|
||||
desc "Disable project's runner" do
|
||||
success Entities::Ci::Runner
|
||||
end
|
||||
params do
|
||||
requires :runner_id, type: Integer, desc: 'The ID of the runner'
|
||||
end
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
delete ':id/runners/:runner_id' do
|
||||
runner_project = user_project.runner_projects.find_by(runner_id: params[:runner_id])
|
||||
not_found!('Runner') unless runner_project
|
||||
|
||||
runner = runner_project.runner
|
||||
forbidden!("Only one project associated with the runner. Please remove the runner instead") if runner.runner_projects.count == 1
|
||||
|
||||
destroy_conditionally!(runner_project)
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
||||
|
||||
params do
|
||||
requires :id, type: String, desc: 'The ID of a group'
|
||||
end
|
||||
resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
|
||||
before { authorize_admin_group }
|
||||
|
||||
desc 'Get runners available for group' do
|
||||
success Entities::Ci::Runner
|
||||
end
|
||||
params do
|
||||
optional :type, type: String, values: ::Ci::Runner::AVAILABLE_TYPES,
|
||||
desc: 'The type of the runners to show'
|
||||
optional :paused, type: Boolean, desc: 'Whether to include only runners that are accepting or ignoring new jobs'
|
||||
optional :status, type: String, values: ::Ci::Runner::AVAILABLE_STATUSES,
|
||||
desc: 'The status of the runners to show'
|
||||
optional :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'The tags of the runners to show'
|
||||
use :pagination
|
||||
end
|
||||
get ':id/runners' do
|
||||
runners = ::Ci::Runner.group_or_instance_wide(user_group)
|
||||
runners = apply_filter(runners, params)
|
||||
|
||||
present paginate(runners), with: Entities::Ci::Runner
|
||||
end
|
||||
end
|
||||
|
||||
resource :runners do
|
||||
before { authenticate_non_get! }
|
||||
|
||||
desc 'Resets runner registration token' do
|
||||
success Entities::Ci::ResetTokenResult
|
||||
end
|
||||
post 'reset_registration_token' do
|
||||
authorize! :update_runners_registration_token, ApplicationSetting.current
|
||||
|
||||
::Ci::Runners::ResetRegistrationTokenService.new(ApplicationSetting.current, current_user).execute
|
||||
present ApplicationSetting.current_without_cache.runners_registration_token_with_expiration, with: Entities::Ci::ResetTokenResult
|
||||
end
|
||||
end
|
||||
|
||||
params do
|
||||
requires :id, type: String, desc: 'The ID of a project'
|
||||
end
|
||||
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
|
||||
before { authenticate_non_get! }
|
||||
|
||||
desc 'Resets runner registration token' do
|
||||
success Entities::Ci::ResetTokenResult
|
||||
end
|
||||
post ':id/runners/reset_registration_token' do
|
||||
project = find_project! user_project.id
|
||||
authorize! :update_runners_registration_token, project
|
||||
|
||||
project.reset_runners_token!
|
||||
present project.runners_token_with_expiration, with: Entities::Ci::ResetTokenResult
|
||||
end
|
||||
end
|
||||
|
||||
params do
|
||||
requires :id, type: String, desc: 'The ID of a group'
|
||||
end
|
||||
resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
|
||||
before { authenticate_non_get! }
|
||||
|
||||
desc 'Resets runner registration token' do
|
||||
success Entities::Ci::ResetTokenResult
|
||||
end
|
||||
post ':id/runners/reset_registration_token' do
|
||||
group = find_group! user_group.id
|
||||
authorize! :update_runners_registration_token, group
|
||||
|
||||
group.reset_runners_token!
|
||||
present group.runners_token_with_expiration, with: Entities::Ci::ResetTokenResult
|
||||
end
|
||||
end
|
||||
|
||||
helpers do
|
||||
params :deprecated_filter_params do
|
||||
optional :scope, type: String, values: ::Ci::Runner::AVAILABLE_SCOPES,
|
||||
desc: 'Deprecated: Use `type` or `status` instead. The scope of specific runners to return'
|
||||
end
|
||||
|
||||
params :filter_params do
|
||||
optional :type, type: String, values: ::Ci::Runner::AVAILABLE_TYPES, desc: 'The type of runners to return'
|
||||
optional :paused, type: Boolean,
|
||||
desc: 'Whether to include only runners that are accepting or ignoring new jobs'
|
||||
optional :status, type: String, values: ::Ci::Runner::AVAILABLE_STATUSES,
|
||||
desc: 'The status of runners to return'
|
||||
optional :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
|
||||
desc: 'A list of runner tags', documentation: { example: "['macos', 'shell']" }
|
||||
use :pagination
|
||||
end
|
||||
|
||||
def filter_runners(runners, scope, allowed_scopes: ::Ci::Runner::AVAILABLE_SCOPES)
|
||||
return runners unless scope.present?
|
||||
|
||||
|
@ -364,6 +91,329 @@ module API
|
|||
forbidden!("No access granted") unless can?(current_user, :read_builds, runner)
|
||||
end
|
||||
end
|
||||
|
||||
resource :runners do
|
||||
desc 'Get runners available for user' do
|
||||
summary 'List owned runners'
|
||||
success Entities::Ci::Runner
|
||||
failure [[400, 'Scope contains invalid value'], [401, 'Unauthorized']]
|
||||
tags %w[runners]
|
||||
end
|
||||
params do
|
||||
use :deprecated_filter_params
|
||||
use :filter_params
|
||||
end
|
||||
get do
|
||||
runners = current_user.ci_owned_runners
|
||||
runners = filter_runners(runners, params[:scope], allowed_scopes: ::Ci::Runner::AVAILABLE_STATUSES)
|
||||
runners = apply_filter(runners, params)
|
||||
|
||||
present paginate(runners), with: Entities::Ci::Runner
|
||||
end
|
||||
|
||||
desc 'Get all runners - shared and specific' do
|
||||
summary 'List all runners'
|
||||
detail 'Get a list of all runners in the GitLab instance (specific and shared). ' \
|
||||
'Access is restricted to users with administrator access.'
|
||||
success Entities::Ci::Runner
|
||||
failure [[400, 'Scope contains invalid value'], [401, 'Unauthorized']]
|
||||
tags %w[runners]
|
||||
end
|
||||
params do
|
||||
use :deprecated_filter_params
|
||||
use :filter_params
|
||||
end
|
||||
get 'all' do
|
||||
authenticated_as_admin!
|
||||
|
||||
runners = ::Ci::Runner.all
|
||||
runners = filter_runners(runners, params[:scope])
|
||||
runners = apply_filter(runners, params)
|
||||
|
||||
present paginate(runners), with: Entities::Ci::Runner
|
||||
end
|
||||
|
||||
desc "Get runner's details" do
|
||||
detail 'At least the Maintainer role is required to get runner details at the project and group level. ' \
|
||||
'Instance-level runner details via this endpoint are available to all signed in users.'
|
||||
success Entities::Ci::RunnerDetails
|
||||
failure [[401, 'Unauthorized'], [403, 'No access granted'], [404, 'Runner not found']]
|
||||
end
|
||||
params do
|
||||
requires :id, type: Integer, desc: 'The ID of a runner'
|
||||
end
|
||||
get ':id' do
|
||||
runner = get_runner(params[:id])
|
||||
authenticate_show_runner!(runner)
|
||||
|
||||
present runner, with: Entities::Ci::RunnerDetails, current_user: current_user
|
||||
end
|
||||
|
||||
desc "Update runner's details" do
|
||||
summary "Update details of a runner"
|
||||
success Entities::Ci::RunnerDetails
|
||||
failure [[400, 'Bad Request'], [401, 'Unauthorized'], [403, 'No access granted'], [404, 'Runner not found']]
|
||||
end
|
||||
params do
|
||||
requires :id, type: Integer, desc: 'The ID of a runner'
|
||||
optional :description, type: String, desc: 'The description of the runner'
|
||||
optional :active, type: Boolean, desc: 'Deprecated: Use `paused` instead. Flag indicating whether the runner is allowed to receive jobs'
|
||||
optional :paused, type: Boolean, desc: 'Specifies whether the runner should ignore new jobs'
|
||||
optional :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
|
||||
desc: 'The list of tags for a runner', documentation: { example: "['macos', 'shell']" }
|
||||
optional :run_untagged, type: Boolean, desc: 'Specifies whether the runner can execute untagged jobs'
|
||||
optional :locked, type: Boolean, desc: 'Specifies whether the runner is locked'
|
||||
optional :access_level, type: String, values: ::Ci::Runner.access_levels.keys,
|
||||
desc: 'The access level of the runner'
|
||||
optional :maximum_timeout, type: Integer,
|
||||
desc: 'Maximum timeout that limits the amount of time (in seconds) ' \
|
||||
'that runners can run jobs'
|
||||
at_least_one_of :description, :active, :paused, :tag_list, :run_untagged, :locked, :access_level, :maximum_timeout
|
||||
mutually_exclusive :active, :paused
|
||||
end
|
||||
put ':id' do
|
||||
runner = get_runner(params.delete(:id))
|
||||
authenticate_update_runner!(runner)
|
||||
params[:active] = !params.delete(:paused) if params.include?(:paused)
|
||||
update_service = ::Ci::Runners::UpdateRunnerService.new(runner)
|
||||
|
||||
if update_service.execute(declared_params(include_missing: false)).success?
|
||||
present runner, with: Entities::Ci::RunnerDetails, current_user: current_user
|
||||
else
|
||||
render_validation_error!(runner)
|
||||
end
|
||||
end
|
||||
|
||||
desc 'Remove a runner' do
|
||||
summary 'Delete a runner'
|
||||
success Entities::Ci::Runner
|
||||
failure [[401, 'Unauthorized'], [403, 'No access granted'],
|
||||
[403, 'Runner associated with more than one project'], [404, 'Runner not found'],
|
||||
[412, 'Precondition Failed']]
|
||||
tags %w[runners]
|
||||
end
|
||||
params do
|
||||
requires :id, type: Integer, desc: 'The ID of a runner'
|
||||
end
|
||||
delete ':id' do
|
||||
runner = get_runner(params[:id])
|
||||
|
||||
authenticate_delete_runner!(runner)
|
||||
|
||||
destroy_conditionally!(runner) { ::Ci::Runners::UnregisterRunnerService.new(runner, current_user).execute }
|
||||
end
|
||||
|
||||
desc 'List jobs running on a runner' do
|
||||
summary "List runner's jobs"
|
||||
detail 'List jobs that are being processed or were processed by the specified runner. ' \
|
||||
'The list of jobs is limited to projects where the user has at least the Reporter role.'
|
||||
success Entities::Ci::JobBasicWithProject
|
||||
failure [[401, 'Unauthorized'], [403, 'No access granted'], [404, 'Runner not found']]
|
||||
tags %w[runners jobs]
|
||||
end
|
||||
params do
|
||||
requires :id, type: Integer, desc: 'The ID of a runner'
|
||||
optional :status, type: String, desc: 'Status of the job', values: ::Ci::Build::AVAILABLE_STATUSES
|
||||
optional :order_by, type: String, desc: 'Order by `id`', values: ::Ci::RunnerJobsFinder::ALLOWED_INDEXED_COLUMNS
|
||||
optional :sort, type: String, values: %w[asc desc], default: 'desc', desc: 'Sort by `asc` or `desc` order. ' \
|
||||
'Specify `order_by` as well, including for `id`'
|
||||
use :pagination
|
||||
end
|
||||
get ':id/jobs' do
|
||||
runner = get_runner(params[:id])
|
||||
authenticate_list_runners_jobs!(runner)
|
||||
|
||||
jobs = ::Ci::RunnerJobsFinder.new(runner, current_user, params).execute
|
||||
jobs = jobs.preload( # rubocop: disable CodeReuse/ActiveRecord
|
||||
[
|
||||
:user,
|
||||
{ pipeline: { project: [:route, { namespace: :route }] } },
|
||||
{ project: [:route, { namespace: :route }] }
|
||||
]
|
||||
)
|
||||
jobs = paginate(jobs)
|
||||
jobs.each(&:commit) # batch loads all commits in the page
|
||||
|
||||
present jobs, with: Entities::Ci::JobBasicWithProject
|
||||
end
|
||||
|
||||
desc 'Reset runner authentication token' do
|
||||
summary "Reset runner's authentication token"
|
||||
success Entities::Ci::ResetTokenResult
|
||||
failure [[403, 'No access granted'], [404, 'Runner not found']]
|
||||
tags %w[runners]
|
||||
end
|
||||
params do
|
||||
requires :id, type: Integer, desc: 'The ID of the runner'
|
||||
end
|
||||
post ':id/reset_authentication_token' do
|
||||
runner = get_runner(params[:id])
|
||||
authenticate_update_runner!(runner)
|
||||
|
||||
runner.reset_token!
|
||||
present runner.token_with_expiration, with: Entities::Ci::ResetTokenResult
|
||||
end
|
||||
end
|
||||
|
||||
params do
|
||||
requires :id, type: String, desc: 'The ID or URL-encoded path of the project owned by the authenticated user'
|
||||
end
|
||||
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
|
||||
before { authorize_admin_project }
|
||||
|
||||
desc 'Get runners available for project' do
|
||||
summary "List project's runners"
|
||||
detail 'List all runners available in the project, including from ancestor groups ' \
|
||||
'and any allowed shared runners.'
|
||||
success Entities::Ci::Runner
|
||||
failure [[400, 'Scope contains invalid value'], [403, 'No access granted']]
|
||||
tags %w[runners projects]
|
||||
end
|
||||
params do
|
||||
use :deprecated_filter_params
|
||||
use :filter_params
|
||||
end
|
||||
get ':id/runners' do
|
||||
runners = ::Ci::Runner.owned_or_instance_wide(user_project.id)
|
||||
# scope is deprecated (for project runners), however api documentation still supports it.
|
||||
# Not including them in `apply_filter` method as it's not supported for group runners
|
||||
runners = filter_runners(runners, params[:scope])
|
||||
runners = apply_filter(runners, params)
|
||||
|
||||
present paginate(runners), with: Entities::Ci::Runner
|
||||
end
|
||||
|
||||
desc 'Enable a runner in project' do
|
||||
detail "Enable an available specific runner in the project."
|
||||
success Entities::Ci::Runner
|
||||
failure [[400, 'Bad Request'],
|
||||
[403, 'No access granted'], [403, 'Runner is a group runner'], [403, 'Runner is locked'],
|
||||
[404, 'Runner not found']]
|
||||
tags %w[runners projects]
|
||||
end
|
||||
params do
|
||||
requires :runner_id, type: Integer, desc: 'The ID of a runner'
|
||||
end
|
||||
post ':id/runners' do
|
||||
runner = get_runner(params[:runner_id])
|
||||
authenticate_enable_runner!(runner)
|
||||
|
||||
if ::Ci::Runners::AssignRunnerService.new(runner, user_project, current_user).execute.success?
|
||||
present runner, with: Entities::Ci::Runner
|
||||
else
|
||||
render_validation_error!(runner)
|
||||
end
|
||||
end
|
||||
|
||||
desc "Disable project's runner" do
|
||||
summary "Disable a specific runner from the project"
|
||||
detail "It works only if the project isn't the only project associated with the specified runner. " \
|
||||
"If so, an error is returned. Use the call to delete a runner instead."
|
||||
success Entities::Ci::Runner
|
||||
failure [[400, 'Bad Request'],
|
||||
[403, 'Only one project associated with the runner. Please remove the runner instead'],
|
||||
[404, 'Runner not found'], [412, 'Precondition Failed']]
|
||||
tags %w[runners projects]
|
||||
end
|
||||
params do
|
||||
requires :runner_id, type: Integer, desc: 'The ID of a runner'
|
||||
end
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
delete ':id/runners/:runner_id' do
|
||||
runner_project = user_project.runner_projects.find_by(runner_id: params[:runner_id])
|
||||
not_found!('Runner') unless runner_project
|
||||
|
||||
runner = runner_project.runner
|
||||
forbidden!("Only one project associated with the runner. Please remove the runner instead") if runner.runner_projects.count == 1
|
||||
|
||||
destroy_conditionally!(runner_project)
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
||||
|
||||
params do
|
||||
requires :id, type: String, desc: 'The ID of a group'
|
||||
end
|
||||
resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
|
||||
before { authorize_admin_group }
|
||||
|
||||
desc 'Get runners available for group' do
|
||||
summary "List group's runners"
|
||||
detail 'List all runners available in the group as well as its ancestor groups, ' \
|
||||
'including any allowed shared runners.'
|
||||
success Entities::Ci::Runner
|
||||
failure [[400, 'Scope contains invalid value'], [403, 'Forbidden']]
|
||||
tags %w[runners groups]
|
||||
end
|
||||
params do
|
||||
use :filter_params
|
||||
end
|
||||
get ':id/runners' do
|
||||
runners = ::Ci::Runner.group_or_instance_wide(user_group)
|
||||
runners = apply_filter(runners, params)
|
||||
|
||||
present paginate(runners), with: Entities::Ci::Runner
|
||||
end
|
||||
end
|
||||
|
||||
resource :runners do
|
||||
before { authenticate_non_get! }
|
||||
|
||||
desc 'Reset runner registration token' do
|
||||
summary "Reset instance's runner registration token"
|
||||
success Entities::Ci::ResetTokenResult
|
||||
failure [[403, 'Forbidden']]
|
||||
tags %w[runners groups]
|
||||
end
|
||||
post 'reset_registration_token' do
|
||||
authorize! :update_runners_registration_token, ApplicationSetting.current
|
||||
|
||||
::Ci::Runners::ResetRegistrationTokenService.new(ApplicationSetting.current, current_user).execute
|
||||
present ApplicationSetting.current_without_cache.runners_registration_token_with_expiration, with: Entities::Ci::ResetTokenResult
|
||||
end
|
||||
end
|
||||
|
||||
params do
|
||||
requires :id, type: String, desc: 'The ID of a project'
|
||||
end
|
||||
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
|
||||
before { authenticate_non_get! }
|
||||
|
||||
desc 'Reset runner registration token' do
|
||||
summary "Reset the runner registration token for a project"
|
||||
success Entities::Ci::ResetTokenResult
|
||||
failure [[401, 'Unauthorized'], [403, 'Forbidden'], [404, 'Project Not Found']]
|
||||
tags %w[runners projects]
|
||||
end
|
||||
post ':id/runners/reset_registration_token' do
|
||||
project = find_project! user_project.id
|
||||
authorize! :update_runners_registration_token, project
|
||||
|
||||
project.reset_runners_token!
|
||||
present project.runners_token_with_expiration, with: Entities::Ci::ResetTokenResult
|
||||
end
|
||||
end
|
||||
|
||||
params do
|
||||
requires :id, type: String, desc: 'The ID of a group'
|
||||
end
|
||||
resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
|
||||
before { authenticate_non_get! }
|
||||
|
||||
desc 'Reset runner registration token' do
|
||||
summary "Reset the runner registration token for a group"
|
||||
success Entities::Ci::ResetTokenResult
|
||||
failure [[401, 'Unauthorized'], [403, 'Forbidden'], [404, 'Group Not Found']]
|
||||
tags %w[runners groups]
|
||||
end
|
||||
post ':id/runners/reset_registration_token' do
|
||||
group = find_group! user_group.id
|
||||
authorize! :update_runners_registration_token, group
|
||||
|
||||
group.reset_runners_token!
|
||||
present group.runners_token_with_expiration, with: Entities::Ci::ResetTokenResult
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -24,7 +24,7 @@ module BulkImports
|
|||
|
||||
Gitlab::UrlBlocker.validate!(url, schemes: %w[http https], allow_local_network: allow_local_requests?, allow_localhost: allow_local_requests?)
|
||||
|
||||
wiki.ensure_repository
|
||||
wiki.create_wiki_repository
|
||||
wiki.repository.fetch_as_mirror(url)
|
||||
end
|
||||
|
||||
|
|
|
@ -706,6 +706,10 @@ module Gitlab
|
|||
install_rename_triggers(table, old, new)
|
||||
end
|
||||
|
||||
def convert_to_type_column(column, from_type, to_type)
|
||||
"#{column}_convert_#{from_type}_to_#{to_type}"
|
||||
end
|
||||
|
||||
def convert_to_bigint_column(column)
|
||||
"#{column}_convert_to_bigint"
|
||||
end
|
||||
|
@ -736,7 +740,22 @@ module Gitlab
|
|||
# columns - The name, or array of names, of the column(s) that we want to convert to bigint.
|
||||
# primary_key - The name of the primary key column (most often :id)
|
||||
def initialize_conversion_of_integer_to_bigint(table, columns, primary_key: :id)
|
||||
create_temporary_columns_and_triggers(table, columns, primary_key: primary_key, data_type: :bigint)
|
||||
mappings = Array(columns).map do |c|
|
||||
{
|
||||
c => {
|
||||
from_type: :int,
|
||||
to_type: :bigint,
|
||||
default_value: 0
|
||||
}
|
||||
}
|
||||
end.reduce(&:merge)
|
||||
|
||||
create_temporary_columns_and_triggers(
|
||||
table,
|
||||
mappings,
|
||||
primary_key: primary_key,
|
||||
old_bigint_column_naming: true
|
||||
)
|
||||
end
|
||||
|
||||
# Reverts `initialize_conversion_of_integer_to_bigint`
|
||||
|
@ -759,9 +778,23 @@ module Gitlab
|
|||
# table - The name of the database table containing the columns
|
||||
# columns - The name, or array of names, of the column(s) that we have converted to bigint.
|
||||
# primary_key - The name of the primary key column (most often :id)
|
||||
|
||||
def restore_conversion_of_integer_to_bigint(table, columns, primary_key: :id)
|
||||
create_temporary_columns_and_triggers(table, columns, primary_key: primary_key, data_type: :int)
|
||||
mappings = Array(columns).map do |c|
|
||||
{
|
||||
c => {
|
||||
from_type: :bigint,
|
||||
to_type: :int,
|
||||
default_value: 0
|
||||
}
|
||||
}
|
||||
end.reduce(&:merge)
|
||||
|
||||
create_temporary_columns_and_triggers(
|
||||
table,
|
||||
mappings,
|
||||
primary_key: primary_key,
|
||||
old_bigint_column_naming: true
|
||||
)
|
||||
end
|
||||
|
||||
# Backfills the new columns used in an integer-to-bigint conversion using background migrations.
|
||||
|
@ -1170,13 +1203,20 @@ into similar problems in the future (e.g. when new tables are created).
|
|||
SQL
|
||||
end
|
||||
|
||||
private
|
||||
# rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
|
||||
def create_temporary_columns_and_triggers(table, mappings, primary_key: :id, old_bigint_column_naming: false)
|
||||
raise ArgumentError, "No mappings for column conversion provided" if mappings.blank?
|
||||
|
||||
def multiple_columns(columns, separator: ', ')
|
||||
Array.wrap(columns).join(separator)
|
||||
end
|
||||
unless mappings.values.all? { |values| mapping_has_required_columns?(values) }
|
||||
raise ArgumentError, "Some mappings don't have required keys provided"
|
||||
end
|
||||
|
||||
neutral_values_for_type = {
|
||||
int: 0,
|
||||
bigint: 0,
|
||||
uuid: '00000000-0000-0000-0000-000000000000'
|
||||
}
|
||||
|
||||
def create_temporary_columns_and_triggers(table, columns, primary_key: :id, data_type: :bigint)
|
||||
unless table_exists?(table)
|
||||
raise "Table #{table} does not exist"
|
||||
end
|
||||
|
@ -1185,7 +1225,7 @@ into similar problems in the future (e.g. when new tables are created).
|
|||
raise "Column #{primary_key} does not exist on #{table}"
|
||||
end
|
||||
|
||||
columns = Array.wrap(columns)
|
||||
columns = mappings.keys
|
||||
columns.each do |column|
|
||||
next if column_exists?(table, column)
|
||||
|
||||
|
@ -1194,26 +1234,81 @@ into similar problems in the future (e.g. when new tables are created).
|
|||
|
||||
check_trigger_permissions!(table)
|
||||
|
||||
conversions = columns.to_h { |column| [column, convert_to_bigint_column(column)] }
|
||||
if old_bigint_column_naming
|
||||
mappings.each do |column, params|
|
||||
params.merge!(
|
||||
temporary_column_name: convert_to_bigint_column(column)
|
||||
)
|
||||
end
|
||||
else
|
||||
mappings.each do |column, params|
|
||||
params.merge!(
|
||||
temporary_column_name: convert_to_type_column(column, params[:from_type], params[:to_type])
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
with_lock_retries do
|
||||
conversions.each do |(source_column, temporary_name)|
|
||||
column = column_for(table, source_column)
|
||||
mappings.each do |(column_name, params)|
|
||||
column = column_for(table, column_name)
|
||||
temporary_name = params[:temporary_column_name]
|
||||
data_type = params[:to_type]
|
||||
default_value = params[:default_value]
|
||||
|
||||
if (column.name.to_s == primary_key.to_s) || !column.null
|
||||
# If the column to be converted is either a PK or is defined as NOT NULL,
|
||||
# set it to `NOT NULL DEFAULT 0` and we'll copy paste the correct values bellow
|
||||
# That way, we skip the expensive validation step required to add
|
||||
# a NOT NULL constraint at the end of the process
|
||||
add_column(table, temporary_name, data_type, default: column.default || 0, null: false)
|
||||
add_column(
|
||||
table,
|
||||
temporary_name,
|
||||
data_type,
|
||||
default: column.default || default_value || neutral_values_for_type.fetch(data_type),
|
||||
null: false
|
||||
)
|
||||
else
|
||||
add_column(table, temporary_name, data_type, default: column.default)
|
||||
add_column(
|
||||
table,
|
||||
temporary_name,
|
||||
data_type,
|
||||
default: column.default
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
install_rename_triggers(table, conversions.keys, conversions.values)
|
||||
old_column_names = mappings.keys
|
||||
temporary_column_names = mappings.values.map { |v| v[:temporary_column_name] }
|
||||
install_rename_triggers(table, old_column_names, temporary_column_names)
|
||||
end
|
||||
end
|
||||
# rubocop:enable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
|
||||
|
||||
private
|
||||
|
||||
def multiple_columns(columns, separator: ', ')
|
||||
Array.wrap(columns).join(separator)
|
||||
end
|
||||
|
||||
def cascade_statement(cascade)
|
||||
cascade ? 'CASCADE' : ''
|
||||
end
|
||||
|
||||
def validate_check_constraint_name!(constraint_name)
|
||||
if constraint_name.to_s.length > MAX_IDENTIFIER_NAME_LENGTH
|
||||
raise "The maximum allowed constraint name is #{MAX_IDENTIFIER_NAME_LENGTH} characters"
|
||||
end
|
||||
end
|
||||
|
||||
# mappings => {} where keys are column names and values are hashes with the following keys:
|
||||
# from_type - from which type we're migrating
|
||||
# to_type - to which type we're migrating
|
||||
# default_value - custom default value, if not provided will be taken from neutral_values_for_type
|
||||
def mapping_has_required_columns?(mapping)
|
||||
%i[from_type to_type].map do |required_key|
|
||||
mapping.has_key?(required_key)
|
||||
end.all?
|
||||
end
|
||||
|
||||
def column_is_nullable?(table, column)
|
||||
# Check if table.column has not been defined with NOT NULL
|
||||
|
|
|
@ -18137,6 +18137,12 @@ msgstr ""
|
|||
msgid "GitLab Billing Team."
|
||||
msgstr ""
|
||||
|
||||
msgid "GitLab Community Edition"
|
||||
msgstr ""
|
||||
|
||||
msgid "GitLab Enterprise Edition"
|
||||
msgstr ""
|
||||
|
||||
msgid "GitLab Error Tracking"
|
||||
msgstr ""
|
||||
|
||||
|
@ -46738,7 +46744,7 @@ msgstr ""
|
|||
msgid "You must be logged in to search across all of GitLab"
|
||||
msgstr ""
|
||||
|
||||
msgid "You must have developer or higher permissions in the associated project to view job logs when debug trace is enabled. To disable debug trace, set the 'CI_DEBUG_TRACE' variable to 'false' in your pipeline configuration or CI/CD settings. If you need to view this job log, a project maintainer or owner must add you to the project with developer permissions or higher."
|
||||
msgid "You must have developer or higher permissions in the associated project to view job logs when debug trace is enabled. To disable debug trace, set the 'CI_DEBUG_TRACE' and 'CI_DEBUG_SERVICES' variables to 'false' in your pipeline configuration or CI/CD settings. If you must view this job log, a project maintainer or owner must add you to the project with developer permissions or higher."
|
||||
msgstr ""
|
||||
|
||||
msgid "You must have maintainer access to force delete a lock"
|
||||
|
@ -47302,9 +47308,6 @@ msgstr ""
|
|||
msgid "`start_time` should precede `end_time`"
|
||||
msgstr ""
|
||||
|
||||
msgid "`work_items_hierarchy` feature flag disabled for this project"
|
||||
msgstr ""
|
||||
|
||||
msgid "a deleted user"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -228,8 +228,9 @@ RSpec.describe Projects::ArtifactsController do
|
|||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
expect(response.body).to include(
|
||||
'You must have developer or higher permissions in the associated project to view job logs when debug trace is enabled. ' \
|
||||
'To disable debug trace, set the 'CI_DEBUG_TRACE' variable to 'false' in your pipeline configuration or CI/CD settings. ' \
|
||||
'If you need to view this job log, a project maintainer or owner must add you to the project with developer permissions or higher.'
|
||||
'To disable debug trace, set the 'CI_DEBUG_TRACE' and 'CI_DEBUG_SERVICES' variables to 'false' ' \
|
||||
'in your pipeline configuration or CI/CD settings. If you must view this job log, a project maintainer or owner must ' \
|
||||
'add you to the project with developer permissions or higher.'
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -660,6 +660,38 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when CI_DEBUG_SERVICES enabled' do
|
||||
let!(:variable) { create(:ci_instance_variable, key: 'CI_DEBUG_SERVICES', value: 'true') }
|
||||
|
||||
context 'with proper permissions on a project' do
|
||||
let(:user) { developer }
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
it 'returns response ok' do
|
||||
get_trace
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
end
|
||||
end
|
||||
|
||||
context 'without proper permissions for debug logging' do
|
||||
let(:user) { guest }
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
it 'returns response forbidden' do
|
||||
get_trace
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when job has a live trace' do
|
||||
|
@ -1184,36 +1216,51 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
|
|||
expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
|
||||
end
|
||||
|
||||
context 'when CI_DEBUG_TRACE enabled' do
|
||||
before do
|
||||
create(:ci_instance_variable, key: 'CI_DEBUG_TRACE', value: 'true')
|
||||
context 'when CI_DEBUG_TRACE and/or CI_DEBUG_SERVICES are enabled' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
where(:ci_debug_trace, :ci_debug_services) do
|
||||
'true' | 'true'
|
||||
'true' | 'false'
|
||||
'false' | 'true'
|
||||
'false' | 'false'
|
||||
end
|
||||
|
||||
context 'with proper permissions for debug logging on a project' do
|
||||
let(:user) { developer }
|
||||
|
||||
with_them do
|
||||
before do
|
||||
sign_in(user)
|
||||
create(:ci_instance_variable, key: 'CI_DEBUG_TRACE', value: ci_debug_trace)
|
||||
create(:ci_instance_variable, key: 'CI_DEBUG_SERVICES', value: ci_debug_services)
|
||||
end
|
||||
|
||||
it 'returns response ok' do
|
||||
response = subject
|
||||
context 'with proper permissions for debug logging on a project' do
|
||||
let(:user) { developer }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
end
|
||||
end
|
||||
before do
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
context 'without proper permissions for debug logging on a project' do
|
||||
let(:user) { reporter }
|
||||
it 'returns response ok' do
|
||||
response = subject
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns response forbidden' do
|
||||
response = subject
|
||||
context 'without proper permissions for debug logging on a project' do
|
||||
let(:user) { reporter }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
before do
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
it 'returns response forbidden if dev mode enabled' do
|
||||
response = subject
|
||||
|
||||
if ci_debug_trace == 'true' || ci_debug_services == 'true'
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
else
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -211,4 +211,48 @@ RSpec.describe 'Project Jobs Permissions' do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with CI_DEBUG_SERVICES' do
|
||||
let_it_be(:ci_instance_variable) { create(:ci_instance_variable, key: 'CI_DEBUG_SERVICES') }
|
||||
|
||||
describe 'trace endpoint and raw page' do
|
||||
let_it_be(:job) { create(:ci_build, :running, :coverage, :trace_artifact, pipeline: pipeline) }
|
||||
|
||||
where(:public_builds, :user_project_role, :ci_debug_services, :expected_status_code, :expected_msg) do
|
||||
true | 'developer' | true | 200 | nil
|
||||
true | 'guest' | true | 403 | 'You must have developer or higher permissions'
|
||||
true | nil | true | 404 | 'Page Not Found Make sure the address is correct'
|
||||
true | 'developer' | false | 200 | nil
|
||||
true | 'guest' | false | 200 | nil
|
||||
true | nil | false | 404 | 'Page Not Found Make sure the address is correct'
|
||||
false | 'developer' | true | 200 | nil
|
||||
false | 'guest' | true | 403 | 'You must have developer or higher permissions'
|
||||
false | nil | true | 404 | 'Page Not Found Make sure the address is correct'
|
||||
false | 'developer' | false | 200 | nil
|
||||
false | 'guest' | false | 403 | 'The current user is not authorized to access the job log'
|
||||
false | nil | false | 404 | 'Page Not Found Make sure the address is correct'
|
||||
end
|
||||
|
||||
with_them do
|
||||
before do
|
||||
ci_instance_variable.update!(value: ci_debug_services)
|
||||
project.update!(public_builds: public_builds)
|
||||
user_project_role && project.add_role(user, user_project_role)
|
||||
end
|
||||
|
||||
it 'renders trace to authorized users' do
|
||||
visit trace_project_job_path(project, job)
|
||||
|
||||
expect(status_code).to eq(expected_status_code)
|
||||
end
|
||||
|
||||
it 'renders raw trace to authorized users' do
|
||||
visit raw_project_job_path(project, job)
|
||||
|
||||
expect(status_code).to eq(expected_status_code)
|
||||
expect(page).to have_content(expected_msg)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,7 +15,6 @@ RSpec.describe 'Work item children', :js do
|
|||
sign_in(user)
|
||||
|
||||
stub_feature_flags(work_items: true)
|
||||
stub_feature_flags(work_items_hierarchy: true)
|
||||
|
||||
visit project_issue_path(project, issue)
|
||||
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
import { GlEmptyState } from '@gitlab/ui';
|
||||
import { mountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import IssuesDashboardApp from '~/issues/dashboard/components/issues_dashboard_app.vue';
|
||||
import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue';
|
||||
import { IssuableStates } from '~/vue_shared/issuable/list/constants';
|
||||
|
||||
describe('IssuesDashboardApp component', () => {
|
||||
let wrapper;
|
||||
|
||||
const defaultProvide = {
|
||||
calendarPath: 'calendar/path',
|
||||
emptyStateSvgPath: 'empty-state.svg',
|
||||
isSignedIn: true,
|
||||
rssPath: 'rss/path',
|
||||
};
|
||||
|
||||
const findCalendarButton = () =>
|
||||
wrapper.findByRole('link', { name: IssuesDashboardApp.i18n.calendarButtonText });
|
||||
const findEmptyState = () => wrapper.findComponent(GlEmptyState);
|
||||
const findIssuableList = () => wrapper.findComponent(IssuableList);
|
||||
const findRssButton = () =>
|
||||
wrapper.findByRole('link', { name: IssuesDashboardApp.i18n.rssButtonText });
|
||||
|
||||
const mountComponent = () => {
|
||||
wrapper = mountExtended(IssuesDashboardApp, { provide: defaultProvide });
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mountComponent();
|
||||
});
|
||||
|
||||
it('renders IssuableList component', () => {
|
||||
expect(findIssuableList().props()).toMatchObject({
|
||||
currentTab: IssuableStates.Opened,
|
||||
namespace: 'dashboard',
|
||||
recentSearchesStorageKey: 'issues',
|
||||
searchInputPlaceholder: IssuesDashboardApp.i18n.searchInputPlaceholder,
|
||||
tabs: IssuesDashboardApp.IssuableListTabs,
|
||||
});
|
||||
});
|
||||
|
||||
it('renders RSS button link', () => {
|
||||
expect(findRssButton().attributes('href')).toBe(defaultProvide.rssPath);
|
||||
expect(findRssButton().props('icon')).toBe('rss');
|
||||
});
|
||||
|
||||
it('renders calendar button link', () => {
|
||||
expect(findCalendarButton().attributes('href')).toBe(defaultProvide.calendarPath);
|
||||
expect(findCalendarButton().props('icon')).toBe('calendar');
|
||||
});
|
||||
|
||||
it('renders empty state', () => {
|
||||
expect(findEmptyState().props()).toMatchObject({
|
||||
svgPath: defaultProvide.emptyStateSvgPath,
|
||||
title: IssuesDashboardApp.i18n.emptyStateTitle,
|
||||
});
|
||||
});
|
||||
});
|
|
@ -33,7 +33,7 @@ describe('Image List Row', () => {
|
|||
const findListItemComponent = () => wrapper.findComponent(ListItem);
|
||||
const findShowFullPathButton = () => wrapper.findComponent(GlButton);
|
||||
|
||||
const mountComponent = (props, features = {}) => {
|
||||
const mountComponent = (props) => {
|
||||
wrapper = shallowMount(Component, {
|
||||
stubs: {
|
||||
RouterLink,
|
||||
|
@ -47,9 +47,6 @@ describe('Image List Row', () => {
|
|||
},
|
||||
provide: {
|
||||
config: {},
|
||||
glFeatures: {
|
||||
...features,
|
||||
},
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: createMockDirective(),
|
||||
|
@ -88,23 +85,43 @@ describe('Image List Row', () => {
|
|||
});
|
||||
|
||||
describe('image title and path', () => {
|
||||
it('contains a link to the details page', () => {
|
||||
it('renders shortened name of image and contains a link to the details page', () => {
|
||||
mountComponent();
|
||||
|
||||
const link = findDetailsLink();
|
||||
expect(link.text()).toBe(item.path);
|
||||
expect(findDetailsLink().props('to')).toMatchObject({
|
||||
expect(link.text()).toBe('gitlab-test/rails-12009');
|
||||
|
||||
expect(link.props('to')).toMatchObject({
|
||||
name: 'details',
|
||||
params: {
|
||||
id: getIdFromGraphQLId(item.id),
|
||||
},
|
||||
});
|
||||
|
||||
expect(findShowFullPathButton().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('when the image has no name lists the path', () => {
|
||||
mountComponent({ item: { ...item, name: '' } });
|
||||
|
||||
expect(findDetailsLink().text()).toBe('gitlab-test');
|
||||
});
|
||||
|
||||
it('clicking on shortened name of image hides the button & shows full path', async () => {
|
||||
mountComponent();
|
||||
|
||||
const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
|
||||
const mockFocusFn = jest.fn();
|
||||
wrapper.vm.$refs.imageName.$el.focus = mockFocusFn;
|
||||
|
||||
await findShowFullPathButton().trigger('click');
|
||||
|
||||
expect(findShowFullPathButton().exists()).toBe(false);
|
||||
expect(findDetailsLink().text()).toBe(item.path);
|
||||
expect(mockFocusFn).toHaveBeenCalled();
|
||||
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_show_full_path', {
|
||||
label: 'registry_image_list',
|
||||
});
|
||||
});
|
||||
|
||||
it('contains a clipboard button', () => {
|
||||
|
@ -149,35 +166,6 @@ describe('Image List Row', () => {
|
|||
expect(findClipboardButton().attributes('disabled')).toBe('true');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when containerRegistryShowShortenedPath feature enabled', () => {
|
||||
let trackingSpy;
|
||||
|
||||
beforeEach(() => {
|
||||
mountComponent({}, { containerRegistryShowShortenedPath: true });
|
||||
trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
|
||||
});
|
||||
|
||||
it('renders shortened name of image', () => {
|
||||
expect(findShowFullPathButton().exists()).toBe(true);
|
||||
expect(findDetailsLink().text()).toBe('gitlab-test/rails-12009');
|
||||
});
|
||||
|
||||
it('clicking on shortened name of image hides the button & shows full path', async () => {
|
||||
const btn = findShowFullPathButton();
|
||||
const mockFocusFn = jest.fn();
|
||||
wrapper.vm.$refs.imageName.$el.focus = mockFocusFn;
|
||||
|
||||
await btn.trigger('click');
|
||||
|
||||
expect(findShowFullPathButton().exists()).toBe(false);
|
||||
expect(findDetailsLink().text()).toBe(item.path);
|
||||
expect(mockFocusFn).toHaveBeenCalled();
|
||||
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_show_full_path', {
|
||||
label: 'registry_image_list',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('delete button', () => {
|
||||
|
|
|
@ -169,4 +169,13 @@ RSpec.describe AppearancesHelper do
|
|||
expect(helper.brand_title).to eq(helper.default_brand_title)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#default_brand_title' do
|
||||
it 'returns the default title' do
|
||||
edition = Gitlab.ee? ? 'Enterprise' : 'Community'
|
||||
expected_default_brand_title = "GitLab #{edition} Edition"
|
||||
|
||||
expect(helper.default_brand_title).to eq _(expected_default_brand_title)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2097,6 +2097,110 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#convert_to_type_column' do
|
||||
it 'returns the name of the temporary column used to convert to bigint' do
|
||||
expect(model.convert_to_type_column(:id, :int, :bigint)).to eq('id_convert_int_to_bigint')
|
||||
end
|
||||
|
||||
it 'returns the name of the temporary column used to convert to uuid' do
|
||||
expect(model.convert_to_type_column(:uuid, :string, :uuid)).to eq('uuid_convert_string_to_uuid')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#create_temporary_columns_and_triggers' do
|
||||
let(:table) { :test_table }
|
||||
let(:column) { :id }
|
||||
let(:mappings) do
|
||||
{
|
||||
id: {
|
||||
from_type: :int,
|
||||
to_type: :bigint
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
let(:old_bigint_column_naming) { false }
|
||||
|
||||
subject do
|
||||
model.create_temporary_columns_and_triggers(
|
||||
table,
|
||||
mappings,
|
||||
old_bigint_column_naming: old_bigint_column_naming
|
||||
)
|
||||
end
|
||||
|
||||
before do
|
||||
model.create_table table, id: false do |t|
|
||||
t.integer :id, primary_key: true
|
||||
t.integer :non_nullable_column, null: false
|
||||
t.integer :nullable_column
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
|
||||
context 'when no mappings are provided' do
|
||||
let(:mappings) { nil }
|
||||
|
||||
it 'raises an error' do
|
||||
expect { subject }.to raise_error("No mappings for column conversion provided")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when any of the mappings does not have the required keys' do
|
||||
let(:mappings) do
|
||||
{
|
||||
id: {
|
||||
from_type: :int
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
it 'raises an error' do
|
||||
expect { subject }.to raise_error("Some mappings don't have required keys provided")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the target table does not exist' do
|
||||
it 'raises an error' do
|
||||
expect { model.create_temporary_columns_and_triggers(:non_existent_table, mappings) }.to raise_error("Table non_existent_table does not exist")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the column to migrate does not exist' do
|
||||
let(:missing_column) { :test }
|
||||
let(:mappings) do
|
||||
{
|
||||
missing_column => {
|
||||
from_type: :int,
|
||||
to_type: :bigint
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
it 'raises an error' do
|
||||
expect { subject }.to raise_error("Column #{missing_column} does not exist on #{table}")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when old_bigint_column_naming is true' do
|
||||
let(:old_bigint_column_naming) { true }
|
||||
|
||||
it 'calls convert_to_bigint_column' do
|
||||
expect(model).to receive(:convert_to_bigint_column).with(:id).and_return("id_convert_to_bigint")
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context 'when old_bigint_column_naming is false' do
|
||||
it 'calls convert_to_type_column' do
|
||||
expect(model).to receive(:convert_to_type_column).with(:id, :int, :bigint).and_return("id_convert_to_bigint")
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#initialize_conversion_of_integer_to_bigint' do
|
||||
let(:table) { :test_table }
|
||||
let(:column) { :id }
|
||||
|
@ -2253,7 +2357,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
|
|||
let(:columns) { :id }
|
||||
|
||||
it 'removes column, trigger, and function' do
|
||||
temporary_column = model.convert_to_bigint_column(:id)
|
||||
temporary_column = model.convert_to_bigint_column(columns)
|
||||
trigger_name = model.rename_trigger_name(table, :id, temporary_column)
|
||||
|
||||
model.revert_initialize_conversion_of_integer_to_bigint(table, columns)
|
||||
|
|
|
@ -5095,6 +5095,60 @@ RSpec.describe Ci::Build do
|
|||
context 'when CI_DEBUG_TRACE is not in variables' do
|
||||
it { is_expected.to eq false }
|
||||
end
|
||||
|
||||
context 'when CI_DEBUG_SERVICES=true is in variables' do
|
||||
context 'when in instance variables' do
|
||||
before do
|
||||
create(:ci_instance_variable, key: 'CI_DEBUG_SERVICES', value: 'true')
|
||||
end
|
||||
|
||||
it { is_expected.to eq true }
|
||||
end
|
||||
|
||||
context 'when in group variables' do
|
||||
before do
|
||||
create(:ci_group_variable, key: 'CI_DEBUG_SERVICES', value: 'true', group: project.group)
|
||||
end
|
||||
|
||||
it { is_expected.to eq true }
|
||||
end
|
||||
|
||||
context 'when in pipeline variables' do
|
||||
before do
|
||||
create(:ci_pipeline_variable, key: 'CI_DEBUG_SERVICES', value: 'true', pipeline: pipeline)
|
||||
end
|
||||
|
||||
it { is_expected.to eq true }
|
||||
end
|
||||
|
||||
context 'when in project variables' do
|
||||
before do
|
||||
create(:ci_variable, key: 'CI_DEBUG_SERVICES', value: 'true', project: project)
|
||||
end
|
||||
|
||||
it { is_expected.to eq true }
|
||||
end
|
||||
|
||||
context 'when in job variables' do
|
||||
before do
|
||||
create(:ci_job_variable, key: 'CI_DEBUG_SERVICES', value: 'true', job: build)
|
||||
end
|
||||
|
||||
it { is_expected.to eq true }
|
||||
end
|
||||
|
||||
context 'when in yaml variables' do
|
||||
before do
|
||||
build.update!(yaml_variables: [{ key: :CI_DEBUG_SERVICES, value: 'true' }])
|
||||
end
|
||||
|
||||
it { is_expected.to eq true }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when CI_DEBUG_SERVICES is not in variables' do
|
||||
it { is_expected.to eq false }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#drop_with_exit_code!' do
|
||||
|
|
|
@ -606,6 +606,32 @@ RSpec.describe API::Ci::Jobs do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when ci_debug_services is set to true' do
|
||||
before_all do
|
||||
create(:ci_instance_variable, key: 'CI_DEBUG_SERVICES', value: true)
|
||||
end
|
||||
|
||||
where(:public_builds, :user_project_role, :expected_status) do
|
||||
true | 'developer' | :ok
|
||||
true | 'guest' | :forbidden
|
||||
false | 'developer' | :ok
|
||||
false | 'guest' | :forbidden
|
||||
end
|
||||
|
||||
with_them do
|
||||
before do
|
||||
project.update!(public_builds: public_builds)
|
||||
project.add_role(user, user_project_role)
|
||||
|
||||
get api("/projects/#{project.id}/jobs/#{job.id}/trace", api_user)
|
||||
end
|
||||
|
||||
it 'renders successfully to authorized users' do
|
||||
expect(response).to have_gitlab_http_status(expected_status)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /projects/:id/jobs/:job_id/cancel' do
|
||||
|
|
|
@ -5,7 +5,6 @@ require 'spec_helper'
|
|||
RSpec.describe JiraConnect::SubscriptionsController do
|
||||
describe 'GET /-/jira_connect/subscriptions' do
|
||||
let_it_be(:installation) { create(:jira_connect_installation, instance_url: 'http://self-managed-gitlab.com') }
|
||||
|
||||
let(:qsh) do
|
||||
Atlassian::Jwt.create_query_string_hash('https://gitlab.test/subscriptions', 'GET', 'https://gitlab.test')
|
||||
end
|
||||
|
@ -20,12 +19,14 @@ RSpec.describe JiraConnect::SubscriptionsController do
|
|||
|
||||
it { is_expected.to include('http://self-managed-gitlab.com/-/jira_connect/') }
|
||||
it { is_expected.to include('http://self-managed-gitlab.com/api/') }
|
||||
it { is_expected.to include('http://self-managed-gitlab.com/oauth/') }
|
||||
|
||||
context 'with no self-managed instance configured' do
|
||||
let_it_be(:installation) { create(:jira_connect_installation, instance_url: '') }
|
||||
|
||||
it { is_expected.not_to include('http://self-managed-gitlab.com/-/jira_connect/') }
|
||||
it { is_expected.not_to include('http://self-managed-gitlab.com/api/') }
|
||||
it { is_expected.not_to include('http://self-managed-gitlab.com/oauth/') }
|
||||
end
|
||||
|
||||
context 'with jira_connect_oauth_self_managed_setting feature disabled' do
|
||||
|
@ -35,6 +36,7 @@ RSpec.describe JiraConnect::SubscriptionsController do
|
|||
|
||||
it { is_expected.not_to include('http://self-managed-gitlab.com/-/jira_connect/') }
|
||||
it { is_expected.not_to include('http://self-managed-gitlab.com/api/') }
|
||||
it { is_expected.not_to include('http://self-managed-gitlab.com/oauth/') }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -34,10 +34,6 @@ RSpec.describe Issues::RelativePositionRebalancingService, :clean_gitlab_redis_s
|
|||
end
|
||||
end
|
||||
|
||||
before do
|
||||
stub_feature_flags(issue_rebalancing_with_retry: false)
|
||||
end
|
||||
|
||||
def issues_in_position_order
|
||||
project.reload.issues.order_by_relative_position.to_a
|
||||
end
|
||||
|
|
|
@ -179,16 +179,6 @@ RSpec.describe WorkItems::CreateService do
|
|||
let(:error_message) { 'only Issue and Incident can be parent of Task.' }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when hierarchy feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(work_items_hierarchy: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'fails creating work item and returns errors' do
|
||||
let(:error_message) { '`work_items_hierarchy` feature flag disabled for this project' }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user cannot admin parent link' do
|
||||
|
|
|
@ -42,18 +42,6 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService do
|
|||
let_it_be(:child_work_item3) { create(:work_item, :task, project: project) }
|
||||
let_it_be(:child_work_item4) { create(:work_item, :task, project: project) }
|
||||
|
||||
context 'when work_items_hierarchy feature flag is disabled' do
|
||||
let(:params) { { children: [child_work_item4] } }
|
||||
|
||||
before do
|
||||
stub_feature_flags(work_items_hierarchy: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'raises a WidgetError' do
|
||||
let(:message) { '`work_items_hierarchy` feature flag disabled for this project' }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user has insufficient permissions to link work items' do
|
||||
let(:params) { { children: [child_work_item4] } }
|
||||
|
||||
|
@ -105,16 +93,6 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService do
|
|||
|
||||
let(:params) { { parent: parent_work_item } }
|
||||
|
||||
context 'when work_items_hierarchy feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(work_items_hierarchy: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'raises a WidgetError' do
|
||||
let(:message) { '`work_items_hierarchy` feature flag disabled for this project' }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user has insufficient permissions to link work items' do
|
||||
it_behaves_like 'raises a WidgetError' do
|
||||
let(:message) { not_found_error }
|
||||
|
|
|
@ -310,6 +310,10 @@ RSpec.configure do |config|
|
|||
# See https://docs.gitlab.com/ee/development/feature_flags/#selectively-disable-by-actor
|
||||
stub_feature_flags(legacy_merge_request_state_check_for_merged_result_pipelines: false)
|
||||
|
||||
# Disable the `vue_issues_dashboard` feature flag in specs as we migrate the issues
|
||||
# dashboard page to Vue. https://gitlab.com/gitlab-org/gitlab/-/issues/379025
|
||||
stub_feature_flags(vue_issues_dashboard: false)
|
||||
|
||||
allow(Gitlab::GitalyClient).to receive(:can_use_disk?).and_return(enable_rugged)
|
||||
else
|
||||
unstub_all_feature_flags
|
||||
|
|
|
@ -35,7 +35,7 @@ RSpec.shared_examples 'wiki pipeline imports a wiki for an entity' do
|
|||
it 'does not import wiki' do
|
||||
expect(subject).to receive(:source_wiki_exists?).and_return(false)
|
||||
|
||||
expect(parent.wiki).not_to receive(:ensure_repository)
|
||||
expect(parent.wiki).not_to receive(:create_wiki_repository)
|
||||
expect(parent.wiki.repository).not_to receive(:ensure_repository)
|
||||
|
||||
expect { subject.run }.not_to raise_error
|
||||
|
@ -75,7 +75,7 @@ RSpec.shared_examples 'wiki pipeline imports a wiki for an entity' do
|
|||
describe 'unsuccessful response' do
|
||||
shared_examples 'does not raise an error' do
|
||||
it 'does not raise an error' do
|
||||
expect(parent.wiki).not_to receive(:ensure_repository)
|
||||
expect(parent.wiki).not_to receive(:create_wiki_repository)
|
||||
expect(parent.wiki.repository).not_to receive(:ensure_repository)
|
||||
|
||||
expect { subject.run }.not_to raise_error
|
||||
|
|
|
@ -846,29 +846,6 @@ RSpec.shared_examples 'wiki model' do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#ensure_repository' do
|
||||
context 'if the repository exists' do
|
||||
it 'does not create the repository' do
|
||||
expect(subject.repository.exists?).to eq(true)
|
||||
expect(subject.repository.raw).not_to receive(:create_repository)
|
||||
|
||||
subject.ensure_repository
|
||||
end
|
||||
end
|
||||
|
||||
context 'if the repository does not exist' do
|
||||
let(:wiki_container) { wiki_container_without_repo }
|
||||
|
||||
it 'creates the repository' do
|
||||
expect(subject.repository.exists?).to eq(false)
|
||||
|
||||
subject.ensure_repository
|
||||
|
||||
expect(subject.repository.exists?).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#hook_attrs' do
|
||||
it 'returns a hash with values' do
|
||||
expect(subject.hook_attrs).to be_a Hash
|
||||
|
|
Loading…
Reference in a new issue