Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
0e13b2c715
commit
592223823c
|
@ -296,6 +296,21 @@ gitlab:setup:
|
|||
paths:
|
||||
- log/*.log
|
||||
|
||||
db:backup_and_restore:
|
||||
extends: .db-job-base
|
||||
variables:
|
||||
SETUP_DB: "false"
|
||||
GITLAB_ASSUME_YES: "1"
|
||||
script:
|
||||
- . scripts/prepare_build.sh
|
||||
- bundle exec rake db:drop db:create db:structure:load db:seed_fu
|
||||
- mkdir -p tmp/tests/public/uploads tmp/tests/{artifacts,pages,lfs-objects,registry}
|
||||
- bundle exec rake gitlab:backup:create
|
||||
- date
|
||||
- bundle exec rake gitlab:backup:restore
|
||||
rules:
|
||||
- changes: ["lib/backup/**/*"]
|
||||
|
||||
rspec:coverage:
|
||||
extends:
|
||||
- .rails-job-base
|
||||
|
|
|
@ -638,13 +638,6 @@ Style/RedundantInterpolation:
|
|||
Style/RedundantSelf:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 2
|
||||
# Cop supports --auto-correct.
|
||||
Style/RedundantSort:
|
||||
Exclude:
|
||||
- 'app/presenters/packages/nuget/search_results_presenter.rb'
|
||||
- 'spec/presenters/packages/nuget/search_results_presenter_spec.rb'
|
||||
|
||||
# Offense count: 120
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, AllowInnerSlashes.
|
||||
|
@ -697,11 +690,6 @@ Rails/SaveBang:
|
|||
- 'ee/spec/initializers/fog_google_https_private_urls_spec.rb'
|
||||
- 'ee/spec/lib/analytics/merge_request_metrics_calculator_spec.rb'
|
||||
- 'ee/spec/lib/ee/gitlab/auth/ldap/sync/group_spec.rb'
|
||||
- 'ee/spec/lib/ee/gitlab/background_migration/migrate_approver_to_approval_rules_spec.rb'
|
||||
- 'ee/spec/lib/ee/gitlab/background_migration/move_epic_issues_after_epics_spec.rb'
|
||||
- 'ee/spec/lib/ee/gitlab/background_migration/populate_any_approval_rule_for_merge_requests_spec.rb'
|
||||
- 'ee/spec/lib/ee/gitlab/background_migration/populate_any_approval_rule_for_projects_spec.rb'
|
||||
- 'ee/spec/lib/ee/gitlab/background_migration/prune_orphaned_geo_events_spec.rb'
|
||||
- 'ee/spec/lib/ee/gitlab/checks/push_rules/commit_check_spec.rb'
|
||||
- 'ee/spec/lib/ee/gitlab/ci/pipeline/quota/activity_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/auth/ldap/access_spec.rb'
|
||||
|
@ -829,10 +817,6 @@ Rails/SaveBang:
|
|||
- 'ee/spec/services/merge_requests/remove_approval_service_spec.rb'
|
||||
- 'ee/spec/services/merge_requests/update_blocks_service_spec.rb'
|
||||
- 'ee/spec/services/merge_trains/refresh_merge_request_service_spec.rb'
|
||||
- 'ee/spec/services/projects/after_rename_service_spec.rb'
|
||||
- 'ee/spec/services/projects/import_export/export_service_spec.rb'
|
||||
- 'ee/spec/services/projects/update_mirror_service_spec.rb'
|
||||
- 'ee/spec/services/projects/update_service_spec.rb'
|
||||
- 'ee/spec/services/quick_actions/interpret_service_spec.rb'
|
||||
- 'ee/spec/services/slash_commands/global_slack_handler_spec.rb'
|
||||
- 'ee/spec/services/start_pull_mirroring_service_spec.rb'
|
||||
|
@ -1111,9 +1095,6 @@ Rails/SaveBang:
|
|||
- 'spec/models/concerns/subscribable_spec.rb'
|
||||
- 'spec/models/concerns/token_authenticatable_spec.rb'
|
||||
- 'spec/models/container_repository_spec.rb'
|
||||
- 'spec/models/cycle_analytics/issue_spec.rb'
|
||||
- 'spec/models/cycle_analytics/plan_spec.rb'
|
||||
- 'spec/models/cycle_analytics/production_spec.rb'
|
||||
- 'spec/models/deploy_keys_project_spec.rb'
|
||||
- 'spec/models/deploy_token_spec.rb'
|
||||
- 'spec/models/deployment_spec.rb'
|
||||
|
@ -1172,9 +1153,6 @@ Rails/SaveBang:
|
|||
- 'spec/presenters/ci/build_runner_presenter_spec.rb'
|
||||
- 'spec/presenters/ci/trigger_presenter_spec.rb'
|
||||
- 'spec/presenters/packages/conan/package_presenter_spec.rb'
|
||||
- 'spec/requests/api/access_requests_spec.rb'
|
||||
- 'spec/requests/api/boards_spec.rb'
|
||||
- 'spec/requests/api/branches_spec.rb'
|
||||
- 'spec/requests/api/ci/runner_spec.rb'
|
||||
- 'spec/requests/api/commit_statuses_spec.rb'
|
||||
- 'spec/requests/api/conan_packages_spec.rb'
|
||||
|
@ -1196,9 +1174,6 @@ Rails/SaveBang:
|
|||
- 'spec/requests/api/merge_request_diffs_spec.rb'
|
||||
- 'spec/requests/api/merge_requests_spec.rb'
|
||||
- 'spec/requests/api/notes_spec.rb'
|
||||
- 'spec/requests/api/pages/internal_access_spec.rb'
|
||||
- 'spec/requests/api/pages/private_access_spec.rb'
|
||||
- 'spec/requests/api/pages/public_access_spec.rb'
|
||||
- 'spec/requests/api/pipeline_schedules_spec.rb'
|
||||
- 'spec/requests/api/project_import_spec.rb'
|
||||
- 'spec/requests/api/project_milestones_spec.rb'
|
||||
|
@ -1208,11 +1183,6 @@ Rails/SaveBang:
|
|||
- 'spec/requests/lfs_http_spec.rb'
|
||||
- 'spec/requests/profiles/notifications_controller_spec.rb'
|
||||
- 'spec/requests/projects/cycle_analytics_events_spec.rb'
|
||||
- 'spec/serializers/environment_status_entity_spec.rb'
|
||||
- 'spec/serializers/issue_entity_spec.rb'
|
||||
- 'spec/serializers/job_entity_spec.rb'
|
||||
- 'spec/serializers/merge_request_poll_widget_entity_spec.rb'
|
||||
- 'spec/serializers/merge_request_widget_entity_spec.rb'
|
||||
- 'spec/services/auth/container_registry_authentication_service_spec.rb'
|
||||
- 'spec/services/auto_merge/base_service_spec.rb'
|
||||
- 'spec/services/auto_merge_service_spec.rb'
|
||||
|
@ -1232,11 +1202,6 @@ Rails/SaveBang:
|
|||
- 'spec/services/issuable/bulk_update_service_spec.rb'
|
||||
- 'spec/services/issuable/clone/attributes_rewriter_spec.rb'
|
||||
- 'spec/services/issuable/common_system_notes_service_spec.rb'
|
||||
- 'spec/services/issues/close_service_spec.rb'
|
||||
- 'spec/services/issues/create_service_spec.rb'
|
||||
- 'spec/services/issues/export_csv_service_spec.rb'
|
||||
- 'spec/services/issues/reopen_service_spec.rb'
|
||||
- 'spec/services/issues/update_service_spec.rb'
|
||||
- 'spec/services/labels/promote_service_spec.rb'
|
||||
- 'spec/services/members/destroy_service_spec.rb'
|
||||
- 'spec/services/merge_requests/build_service_spec.rb'
|
||||
|
|
|
@ -1 +1 @@
|
|||
8.44.0
|
||||
8.45.0
|
||||
|
|
|
@ -28,7 +28,8 @@ export default class AjaxLoadingSpinner {
|
|||
static toggleLoadingIcon(iconElement) {
|
||||
const { classList } = iconElement;
|
||||
classList.toggle(iconElement.dataset.icon);
|
||||
classList.toggle('fa-spinner');
|
||||
classList.toggle('fa-spin');
|
||||
classList.toggle('gl-spinner');
|
||||
classList.toggle('gl-spinner-orange');
|
||||
classList.toggle('gl-spinner-sm');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,10 @@ export default {
|
|||
type: String,
|
||||
required: true,
|
||||
},
|
||||
labelsWebUrl: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
scopedIssueBoardFeatureEnabled: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
|
@ -198,6 +202,7 @@ export default {
|
|||
:board="board"
|
||||
:can-admin-board="canAdminBoard"
|
||||
:labels-path="labelsPath"
|
||||
:labels-web-url="labelsWebUrl"
|
||||
:enable-scoped-labels="enableScopedLabels"
|
||||
:project-id="projectId"
|
||||
:group-id="groupId"
|
||||
|
|
|
@ -61,6 +61,10 @@ export default {
|
|||
type: String,
|
||||
required: true,
|
||||
},
|
||||
labelsWebUrl: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
projectId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
|
@ -332,6 +336,7 @@ export default {
|
|||
<board-form
|
||||
v-if="currentPage"
|
||||
:labels-path="labelsPath"
|
||||
:labels-web-url="labelsWebUrl"
|
||||
:project-id="projectId"
|
||||
:group-id="groupId"
|
||||
:can-admin-board="canAdminBoard"
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<script>
|
||||
/* eslint-disable vue/no-v-html */
|
||||
import { mapActions, mapGetters, mapState } from 'vuex';
|
||||
import { escape } from 'lodash';
|
||||
import { GlLoadingIcon, GlIcon } from '@gitlab/ui';
|
||||
import { GlLoadingIcon, GlIcon, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
|
||||
import { sprintf, __ } from '../../../locale';
|
||||
import CiIcon from '../../../vue_shared/components/ci_icon.vue';
|
||||
import Tabs from '../../../vue_shared/components/tabs/tabs';
|
||||
|
@ -22,6 +21,9 @@ export default {
|
|||
EmptyState,
|
||||
GlLoadingIcon,
|
||||
},
|
||||
directives: {
|
||||
SafeHtml,
|
||||
},
|
||||
computed: {
|
||||
...mapState(['pipelinesEmptyStateSvgPath', 'links']),
|
||||
...mapGetters(['currentProject']),
|
||||
|
@ -84,7 +86,7 @@ export default {
|
|||
<div v-else-if="latestPipeline.yamlError" class="bs-callout bs-callout-danger">
|
||||
<p class="gl-mb-0">{{ __('Found errors in your .gitlab-ci.yml:') }}</p>
|
||||
<p class="gl-mb-0 break-word">{{ latestPipeline.yamlError }}</p>
|
||||
<p class="gl-mb-0" v-html="ciLintText"></p>
|
||||
<p v-safe-html="ciLintText" class="gl-mb-0"></p>
|
||||
</div>
|
||||
<tabs v-else class="ide-pipeline-list">
|
||||
<tab :active="!pipelineFailed">
|
||||
|
|
|
@ -20,7 +20,7 @@ export default {
|
|||
return h(
|
||||
'span',
|
||||
{
|
||||
class: ['ws-pre-wrap', content.style],
|
||||
class: ['gl-white-space-pre-wrap', content.style],
|
||||
},
|
||||
content.text,
|
||||
);
|
||||
|
|
|
@ -52,7 +52,7 @@ export default {
|
|||
<span
|
||||
v-for="(content, i) in line.content"
|
||||
:key="i"
|
||||
class="line-text w-100 ws-pre-wrap"
|
||||
class="line-text w-100 gl-white-space-pre-wrap"
|
||||
:class="content.style"
|
||||
>{{ content.text }}</span
|
||||
>
|
||||
|
|
|
@ -13,6 +13,8 @@ import { __, sprintf } from '~/locale';
|
|||
import Api from '~/api';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
|
||||
const SEARCH_DEBOUNCE_MS = 250;
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlNewDropdown,
|
||||
|
@ -95,12 +97,15 @@ export default {
|
|||
// lodash attaches to the function, which is
|
||||
// made inaccessible by Vue. More info:
|
||||
// https://stackoverflow.com/a/52988020/1063392
|
||||
this.debouncedSearchMilestones = debounce(this.searchMilestones, 100);
|
||||
this.debouncedSearchMilestones = debounce(this.searchMilestones, SEARCH_DEBOUNCE_MS);
|
||||
},
|
||||
mounted() {
|
||||
this.fetchMilestones();
|
||||
},
|
||||
methods: {
|
||||
focusSearchBox() {
|
||||
this.$refs.searchBox.$el.querySelector('input').focus();
|
||||
},
|
||||
fetchMilestones() {
|
||||
this.requestCount += 1;
|
||||
|
||||
|
@ -183,7 +188,7 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<gl-new-dropdown>
|
||||
<gl-new-dropdown v-bind="$attrs" class="project-milestone-combobox" @shown="focusSearchBox">
|
||||
<template slot="button-content">
|
||||
<span ref="buttonText" class="flex-grow-1 ml-1 text-muted">{{
|
||||
selectedMilestonesLabel
|
||||
|
@ -198,6 +203,7 @@ export default {
|
|||
<gl-new-dropdown-divider />
|
||||
|
||||
<gl-search-box-by-type
|
||||
ref="searchBox"
|
||||
v-model.trim="searchQuery"
|
||||
class="gl-m-3"
|
||||
:placeholder="this.$options.translations.searchMilestones"
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
<script>
|
||||
/* eslint-disable vue/no-v-html */
|
||||
import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { __, sprintf } from '~/locale';
|
||||
|
||||
export default {
|
||||
directives: {
|
||||
SafeHtml,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getNotesDataByProp']),
|
||||
registerLink() {
|
||||
|
@ -31,5 +34,5 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="disabled-comment text-center" v-html="signedOutText"></div>
|
||||
<div v-safe-html="signedOutText" class="disabled-comment text-center"></div>
|
||||
</template>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
/* eslint-disable vue/no-v-html */
|
||||
import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
|
||||
import { escape } from 'lodash';
|
||||
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
|
||||
import { s__, sprintf } from '~/locale';
|
||||
|
@ -8,6 +8,9 @@ export default {
|
|||
components: {
|
||||
DeprecatedModal,
|
||||
},
|
||||
directives: {
|
||||
SafeHtml,
|
||||
},
|
||||
props: {
|
||||
deleteProjectUrl: {
|
||||
type: String,
|
||||
|
@ -94,8 +97,8 @@ export default {
|
|||
@cancel="onCancel"
|
||||
>
|
||||
<template #body="props">
|
||||
<p v-html="props.text"></p>
|
||||
<p v-html="confirmationTextLabel"></p>
|
||||
<p v-safe-html="props.text"></p>
|
||||
<p v-safe-html="confirmationTextLabel"></p>
|
||||
<form ref="form" :action="deleteProjectUrl" method="post">
|
||||
<input ref="method" type="hidden" name="_method" value="delete" />
|
||||
<input :value="csrfToken" type="hidden" name="authenticity_token" />
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
<script>
|
||||
import { GlButton } from '@gitlab/ui';
|
||||
import LoadingButton from '~/vue_shared/components/loading_button.vue';
|
||||
|
||||
export default {
|
||||
name: 'PipelineNavControls',
|
||||
components: {
|
||||
LoadingButton,
|
||||
GlButton,
|
||||
},
|
||||
props: {
|
||||
|
@ -52,13 +50,14 @@ export default {
|
|||
{{ s__('Pipelines|Run Pipeline') }}
|
||||
</gl-button>
|
||||
|
||||
<loading-button
|
||||
<gl-button
|
||||
v-if="resetCachePath"
|
||||
:loading="isResetCacheButtonLoading"
|
||||
:label="s__('Pipelines|Clear Runner Caches')"
|
||||
class="js-clear-cache"
|
||||
@click="onClickResetCache"
|
||||
/>
|
||||
>
|
||||
{{ s__('Pipelines|Clear Runner Caches') }}
|
||||
</gl-button>
|
||||
|
||||
<gl-button v-if="ciLintPath" :href="ciLintPath" class="js-ci-lint">
|
||||
{{ s__('Pipelines|CI Lint') }}
|
||||
|
|
|
@ -86,7 +86,7 @@ export default {
|
|||
<slot name="modal-body"></slot>
|
||||
<p class="gl-mb-1">{{ $options.strings.confirmText }}</p>
|
||||
<p>
|
||||
<code class="ws-pre-wrap">{{ confirmPhrase }}</code>
|
||||
<code class="gl-white-space-pre-wrap">{{ confirmPhrase }}</code>
|
||||
</p>
|
||||
<gl-form-input
|
||||
id="confirm_name_input"
|
||||
|
|
|
@ -6,19 +6,21 @@ import { __, sprintf } from '~/locale';
|
|||
import TitleField from '~/vue_shared/components/form/title.vue';
|
||||
import { redirectTo, joinPaths } from '~/lib/utils/url_utility';
|
||||
import FormFooterActions from '~/vue_shared/components/form/form_footer_actions.vue';
|
||||
import { SNIPPET_MARK_EDIT_APP_START } from '~/performance_constants';
|
||||
|
||||
import UpdateSnippetMutation from '../mutations/updateSnippet.mutation.graphql';
|
||||
import CreateSnippetMutation from '../mutations/createSnippet.mutation.graphql';
|
||||
import { getSnippetMixin } from '../mixins/snippets';
|
||||
import {
|
||||
SNIPPET_VISIBILITY_PRIVATE,
|
||||
SNIPPET_CREATE_MUTATION_ERROR,
|
||||
SNIPPET_UPDATE_MUTATION_ERROR,
|
||||
SNIPPET_VISIBILITY_PRIVATE,
|
||||
} from '../constants';
|
||||
import defaultVisibilityQuery from '../queries/snippet_visibility.query.graphql';
|
||||
|
||||
import SnippetBlobActionsEdit from './snippet_blob_actions_edit.vue';
|
||||
import SnippetVisibilityEdit from './snippet_visibility_edit.vue';
|
||||
import SnippetDescriptionEdit from './snippet_description_edit.vue';
|
||||
import { SNIPPET_MARK_EDIT_APP_START } from '~/performance_constants';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -31,6 +33,15 @@ export default {
|
|||
GlLoadingIcon,
|
||||
},
|
||||
mixins: [getSnippetMixin],
|
||||
apollo: {
|
||||
defaultVisibility: {
|
||||
query: defaultVisibilityQuery,
|
||||
manual: true,
|
||||
result({ data: { selectedLevel } }) {
|
||||
this.selectedLevelDefault = selectedLevel;
|
||||
},
|
||||
},
|
||||
},
|
||||
props: {
|
||||
markdownPreviewPath: {
|
||||
type: String,
|
||||
|
@ -56,6 +67,7 @@ export default {
|
|||
isUpdating: false,
|
||||
newSnippet: false,
|
||||
actions: [],
|
||||
selectedLevelDefault: SNIPPET_VISIBILITY_PRIVATE,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@ -98,6 +110,13 @@ export default {
|
|||
descriptionFieldId() {
|
||||
return `${this.isProjectSnippet ? 'project' : 'personal'}_snippet_description`;
|
||||
},
|
||||
newSnippetSchema() {
|
||||
return {
|
||||
title: '',
|
||||
description: '',
|
||||
visibilityLevel: this.selectedLevelDefault,
|
||||
};
|
||||
},
|
||||
},
|
||||
beforeCreate() {
|
||||
performance.mark(SNIPPET_MARK_EDIT_APP_START);
|
||||
|
@ -126,7 +145,7 @@ export default {
|
|||
},
|
||||
onNewSnippetFetched() {
|
||||
this.newSnippet = true;
|
||||
this.snippet = this.$options.newSnippetSchema;
|
||||
this.snippet = this.newSnippetSchema;
|
||||
},
|
||||
onExistingSnippetFetched() {
|
||||
this.newSnippet = false;
|
||||
|
@ -184,11 +203,6 @@ export default {
|
|||
this.actions = actions;
|
||||
},
|
||||
},
|
||||
newSnippetSchema: {
|
||||
title: '',
|
||||
description: '',
|
||||
visibilityLevel: SNIPPET_VISIBILITY_PRIVATE,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
<script>
|
||||
import { GlIcon, GlFormGroup, GlFormRadio, GlFormRadioGroup, GlLink } from '@gitlab/ui';
|
||||
import {
|
||||
SNIPPET_VISIBILITY,
|
||||
SNIPPET_VISIBILITY_PRIVATE,
|
||||
SNIPPET_VISIBILITY_INTERNAL,
|
||||
SNIPPET_VISIBILITY_PUBLIC,
|
||||
} from '~/snippets/constants';
|
||||
import defaultVisibilityQuery from '../queries/snippet_visibility.query.graphql';
|
||||
import { defaultSnippetVisibilityLevels } from '../utils/blob';
|
||||
import { SNIPPET_LEVELS_RESTRICTED, SNIPPET_LEVELS_DISABLED } from '~/snippets/constants';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -15,6 +12,16 @@ export default {
|
|||
GlFormRadioGroup,
|
||||
GlLink,
|
||||
},
|
||||
apollo: {
|
||||
defaultVisibility: {
|
||||
query: defaultVisibilityQuery,
|
||||
manual: true,
|
||||
result({ data: { visibilityLevels, multipleLevelsRestricted } }) {
|
||||
this.visibilityLevels = defaultSnippetVisibilityLevels(visibilityLevels);
|
||||
this.multipleLevelsRestricted = multipleLevelsRestricted;
|
||||
},
|
||||
},
|
||||
},
|
||||
props: {
|
||||
helpLink: {
|
||||
type: String,
|
||||
|
@ -28,19 +35,17 @@ export default {
|
|||
},
|
||||
value: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: SNIPPET_VISIBILITY_PRIVATE,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
visibilityOptions() {
|
||||
return [
|
||||
SNIPPET_VISIBILITY_PRIVATE,
|
||||
SNIPPET_VISIBILITY_INTERNAL,
|
||||
SNIPPET_VISIBILITY_PUBLIC,
|
||||
].map(key => ({ value: key, ...SNIPPET_VISIBILITY[key] }));
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visibilityLevels: [],
|
||||
multipleLevelsRestricted: false,
|
||||
};
|
||||
},
|
||||
SNIPPET_LEVELS_DISABLED,
|
||||
SNIPPET_LEVELS_RESTRICTED,
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
|
@ -51,10 +56,10 @@ export default {
|
|||
><gl-icon :size="12" name="question"
|
||||
/></gl-link>
|
||||
</label>
|
||||
<gl-form-group id="visibility-level-setting">
|
||||
<gl-form-radio-group v-bind="$attrs" :checked="value" stacked v-on="$listeners">
|
||||
<gl-form-group id="visibility-level-setting" class="gl-mb-0">
|
||||
<gl-form-radio-group :checked="value" stacked v-bind="$attrs" v-on="$listeners">
|
||||
<gl-form-radio
|
||||
v-for="option in visibilityOptions"
|
||||
v-for="option in visibilityLevels"
|
||||
:key="option.value"
|
||||
:value="option.value"
|
||||
class="mb-3"
|
||||
|
@ -71,5 +76,12 @@ export default {
|
|||
</gl-form-radio>
|
||||
</gl-form-radio-group>
|
||||
</gl-form-group>
|
||||
|
||||
<div class="text-muted" data-testid="restricted-levels-info">
|
||||
<template v-if="!visibilityLevels.length">{{ $options.SNIPPET_LEVELS_DISABLED }}</template>
|
||||
<template v-else-if="multipleLevelsRestricted">{{
|
||||
$options.SNIPPET_LEVELS_RESTRICTED
|
||||
}}</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -33,3 +33,15 @@ export const SNIPPET_BLOB_ACTION_MOVE = 'move';
|
|||
export const SNIPPET_BLOB_ACTION_DELETE = 'delete';
|
||||
|
||||
export const SNIPPET_MAX_BLOBS = 10;
|
||||
|
||||
export const SNIPPET_LEVELS_MAP = {
|
||||
0: SNIPPET_VISIBILITY_PRIVATE,
|
||||
10: SNIPPET_VISIBILITY_INTERNAL,
|
||||
20: SNIPPET_VISIBILITY_PUBLIC,
|
||||
};
|
||||
export const SNIPPET_LEVELS_RESTRICTED = __(
|
||||
'Other visibility settings have been disabled by the administrator.',
|
||||
);
|
||||
export const SNIPPET_LEVELS_DISABLED = __(
|
||||
'Visibility settings have been disabled by the administrator.',
|
||||
);
|
||||
|
|
|
@ -5,6 +5,7 @@ import createDefaultClient from '~/lib/graphql';
|
|||
|
||||
import SnippetsShow from './components/show.vue';
|
||||
import SnippetsEdit from './components/edit.vue';
|
||||
import { SNIPPET_LEVELS_MAP, SNIPPET_VISIBILITY_PRIVATE } from '~/snippets/constants';
|
||||
|
||||
Vue.use(VueApollo);
|
||||
Vue.use(Translate);
|
||||
|
@ -18,13 +19,23 @@ function appFactory(el, Component) {
|
|||
defaultClient: createDefaultClient(),
|
||||
});
|
||||
|
||||
const { visibilityLevels, selectedLevel, multipleLevelsRestricted, ...restDataset } = el.dataset;
|
||||
|
||||
apolloProvider.clients.defaultClient.cache.writeData({
|
||||
data: {
|
||||
visibilityLevels: JSON.parse(visibilityLevels),
|
||||
selectedLevel: SNIPPET_LEVELS_MAP[selectedLevel] ?? SNIPPET_VISIBILITY_PRIVATE,
|
||||
multipleLevelsRestricted: 'multipleLevelsRestricted' in el.dataset,
|
||||
},
|
||||
});
|
||||
|
||||
return new Vue({
|
||||
el,
|
||||
apolloProvider,
|
||||
render(createElement) {
|
||||
return createElement(Component, {
|
||||
props: {
|
||||
...el.dataset,
|
||||
...restDataset,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
query defaultSnippetVisibility {
|
||||
visibilityLevels @client
|
||||
selectedLevel @client
|
||||
multipleLevelsRestricted @client
|
||||
}
|
|
@ -4,6 +4,8 @@ import {
|
|||
SNIPPET_BLOB_ACTION_UPDATE,
|
||||
SNIPPET_BLOB_ACTION_MOVE,
|
||||
SNIPPET_BLOB_ACTION_DELETE,
|
||||
SNIPPET_LEVELS_MAP,
|
||||
SNIPPET_VISIBILITY,
|
||||
} from '../constants';
|
||||
|
||||
const createLocalId = () => uniqueId('blob_local_');
|
||||
|
@ -64,3 +66,16 @@ export const diffAll = (blobs, origBlobs) => {
|
|||
|
||||
return [...deletedEntries, ...newEntries];
|
||||
};
|
||||
|
||||
export const defaultSnippetVisibilityLevels = arr => {
|
||||
if (Array.isArray(arr)) {
|
||||
return arr.map(l => {
|
||||
const translatedLevel = SNIPPET_LEVELS_MAP[l];
|
||||
return {
|
||||
value: translatedLevel,
|
||||
...SNIPPET_VISIBILITY[translatedLevel],
|
||||
};
|
||||
});
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
<script>
|
||||
/* eslint-disable vue/no-v-html */
|
||||
import Vue from 'vue';
|
||||
import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
|
||||
import { __ } from '~/locale';
|
||||
import SuggestionDiff from './suggestion_diff.vue';
|
||||
import { deprecatedCreateFlash as Flash } from '~/flash';
|
||||
|
||||
export default {
|
||||
directives: {
|
||||
SafeHtml,
|
||||
},
|
||||
props: {
|
||||
lineType: {
|
||||
type: String,
|
||||
|
@ -116,6 +119,6 @@ export default {
|
|||
<template>
|
||||
<div>
|
||||
<div class="flash-container js-suggestions-flash"></div>
|
||||
<div v-show="isRendered" ref="container" class="md" v-html="noteHtml"></div>
|
||||
<div v-show="isRendered" ref="container" v-safe-html="noteHtml" class="md"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -14,7 +14,10 @@ import DropdownSearchInput from './dropdown_search_input.vue';
|
|||
import DropdownFooter from './dropdown_footer.vue';
|
||||
import DropdownCreateLabel from './dropdown_create_label.vue';
|
||||
|
||||
import { DropdownVariant } from '../labels_select_vue/constants';
|
||||
|
||||
export default {
|
||||
DropdownVariant,
|
||||
components: {
|
||||
DropdownTitle,
|
||||
DropdownValue,
|
||||
|
@ -80,6 +83,11 @@ export default {
|
|||
required: false,
|
||||
default: false,
|
||||
},
|
||||
variant: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: DropdownVariant.Sidebar,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
hiddenInputName() {
|
||||
|
@ -123,7 +131,7 @@ export default {
|
|||
<template>
|
||||
<div class="block labels js-labels-block">
|
||||
<dropdown-value-collapsed
|
||||
v-if="showCreate"
|
||||
v-if="showCreate && variant === $options.DropdownVariant.Sidebar"
|
||||
:labels="context.labels"
|
||||
@onValueClick="handleCollapsedValueClick"
|
||||
/>
|
||||
|
@ -150,18 +158,21 @@ export default {
|
|||
:labels-path="labelsPath"
|
||||
:namespace="namespace"
|
||||
:labels="context.labels"
|
||||
:show-extra-options="!showCreate"
|
||||
:show-extra-options="!showCreate || variant !== $options.DropdownVariant.Sidebar"
|
||||
:enable-scoped-labels="enableScopedLabels"
|
||||
/>
|
||||
<div
|
||||
class="dropdown-menu dropdown-select dropdown-menu-paging
|
||||
dropdown-menu-labels dropdown-menu-selectable"
|
||||
class="dropdown-menu dropdown-select dropdown-menu-paging dropdown-menu-labels dropdown-menu-selectable"
|
||||
>
|
||||
<div class="dropdown-page-one">
|
||||
<dropdown-header v-if="showCreate" />
|
||||
<dropdown-header v-if="showCreate && variant === $options.DropdownVariant.Sidebar" />
|
||||
<dropdown-search-input />
|
||||
<div class="dropdown-content" data-qa-selector="labels_dropdown_content"></div>
|
||||
<div class="dropdown-loading"><gl-loading-icon /></div>
|
||||
<div class="dropdown-loading">
|
||||
<gl-loading-icon
|
||||
class="gl-display-flex gl-justify-content-center gl-align-items-center gl-h-full"
|
||||
/>
|
||||
</div>
|
||||
<dropdown-footer
|
||||
v-if="showCreate"
|
||||
:labels-web-url="labelsWebUrl"
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
<script>
|
||||
import { GlTooltipDirective } from '@gitlab/ui';
|
||||
import { __ } from '~/locale';
|
||||
|
||||
export default {
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
props: {
|
||||
headerTitle: {
|
||||
type: String,
|
||||
|
@ -10,7 +14,11 @@ export default {
|
|||
},
|
||||
},
|
||||
created() {
|
||||
this.suggestedColors = gon.suggested_label_colors;
|
||||
const rawLabelsColors = gon.suggested_label_colors;
|
||||
this.suggestedColors = Object.keys(rawLabelsColors).map(colorCode => ({
|
||||
colorCode,
|
||||
title: rawLabelsColors[colorCode],
|
||||
}));
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -46,10 +54,12 @@ export default {
|
|||
<a
|
||||
v-for="(color, index) in suggestedColors"
|
||||
:key="index"
|
||||
:data-color="color"
|
||||
v-gl-tooltip
|
||||
:data-color="color.colorCode"
|
||||
:style="{
|
||||
backgroundColor: color,
|
||||
backgroundColor: color.colorCode,
|
||||
}"
|
||||
:title="color.title"
|
||||
href="#"
|
||||
>
|
||||
|
||||
|
|
|
@ -416,7 +416,6 @@ img.emoji {
|
|||
.flex-no-shrink { flex-shrink: 0; }
|
||||
.ws-initial { white-space: initial; }
|
||||
.ws-normal { white-space: normal; }
|
||||
.ws-pre-wrap { white-space: pre-wrap; }
|
||||
.overflow-auto { overflow: auto; }
|
||||
.overflow-visible { overflow: visible; }
|
||||
|
||||
|
|
|
@ -171,6 +171,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
.labels {
|
||||
// Prevent double scroll-bars for labels dropdown.
|
||||
.dropdown-menu-toggle.wide + .dropdown-select {
|
||||
max-height: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.gl-dropdown .dropdown-menu-toggle {
|
||||
padding-right: $gl-padding-8;
|
||||
|
||||
|
|
|
@ -878,7 +878,11 @@ module Ci
|
|||
end
|
||||
|
||||
def has_coverage_reports?
|
||||
self.has_reports?(Ci::JobArtifact.coverage_reports)
|
||||
pipeline_artifacts&.has_code_coverage?
|
||||
end
|
||||
|
||||
def can_generate_coverage_reports?
|
||||
has_reports?(Ci::JobArtifact.coverage_reports)
|
||||
end
|
||||
|
||||
def test_report_summary
|
||||
|
|
|
@ -1372,7 +1372,7 @@ class MergeRequest < ApplicationRecord
|
|||
def has_coverage_reports?
|
||||
return false unless Feature.enabled?(:coverage_report_view, project)
|
||||
|
||||
actual_head_pipeline&.pipeline_artifacts&.has_code_coverage?
|
||||
actual_head_pipeline&.has_coverage_reports?
|
||||
end
|
||||
|
||||
def has_terraform_reports?
|
||||
|
|
|
@ -6,6 +6,7 @@ class Packages::Pypi::Metadatum < ApplicationRecord
|
|||
belongs_to :package, -> { where(package_type: :pypi) }, inverse_of: :pypi_metadatum
|
||||
|
||||
validates :package, presence: true
|
||||
validates :required_python, length: { maximum: 50 }, allow_blank: true
|
||||
|
||||
validate :pypi_package_type
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ module Packages
|
|||
|
||||
def latest_version(packages)
|
||||
versions = packages.map(&:version).compact
|
||||
VersionSorter.sort(versions).last # rubocop: disable Style/UnneededSort
|
||||
VersionSorter.sort(versions).last # rubocop: disable Style/RedundantSort
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,8 +4,8 @@ module Ci
|
|||
class CreateArtifactService
|
||||
def execute(pipeline)
|
||||
return unless ::Gitlab::Ci::Features.coverage_report_view?(pipeline.project)
|
||||
return unless pipeline.has_coverage_reports?
|
||||
return if pipeline.pipeline_artifacts.has_code_coverage?
|
||||
return unless pipeline.can_generate_coverage_reports?
|
||||
return if pipeline.has_coverage_reports?
|
||||
|
||||
file = build_carrierwave_file(pipeline)
|
||||
|
||||
|
|
|
@ -7,11 +7,17 @@ module Packages
|
|||
|
||||
def execute
|
||||
::Packages::Package.transaction do
|
||||
Packages::Pypi::Metadatum.upsert(
|
||||
package_id: created_package.id,
|
||||
meta = Packages::Pypi::Metadatum.new(
|
||||
package: created_package,
|
||||
required_python: params[:requires_python]
|
||||
)
|
||||
|
||||
unless meta.valid?
|
||||
raise ActiveRecord::RecordInvalid.new(meta)
|
||||
end
|
||||
|
||||
Packages::Pypi::Metadatum.upsert(meta.attributes)
|
||||
|
||||
::Packages::CreatePackageFileService.new(created_package, file_params).execute
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
- page_title _("Packages")
|
||||
- page_title _("Package Registry")
|
||||
|
||||
.row
|
||||
.col-12
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
- page_title _("Packages")
|
||||
- page_title _("Package Registry")
|
||||
|
||||
.row
|
||||
.col-12
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
- add_to_breadcrumbs _("Packages"), project_packages_path(@project)
|
||||
- add_to_breadcrumbs _("Package Registry"), project_packages_path(@project)
|
||||
- add_to_breadcrumbs @package.name, project_packages_path(@project)
|
||||
- breadcrumb_title @package.version
|
||||
- page_title _("Packages")
|
||||
- page_title _("Package Registry")
|
||||
|
||||
.row
|
||||
.col-12
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
can_admin_board: can?(current_user, :admin_board, parent).to_s,
|
||||
multiple_issue_boards_available: parent.multiple_issue_boards_available?.to_s,
|
||||
labels_path: labels_filter_path_with_defaults(only_group_labels: true, include_descendant_groups: true),
|
||||
labels_web_url: parent.is_a?(Project) ? project_labels_path(@project) : group_labels_path(@group),
|
||||
project_id: @project&.id,
|
||||
group_id: @group&.id,
|
||||
scoped_issue_board_feature_enabled: Gitlab.ee? && parent.feature_available?(:scoped_issue_board) ? 'true' : 'false',
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
- if Feature.enabled?(:snippets_edit_vue)
|
||||
#js-snippet-edit.snippet-form{ data: {'project_path': @snippet.project&.full_path, 'snippet-gid': @snippet.new_record? ? '' : @snippet.to_global_id, 'markdown-preview-path': preview_markdown_path(parent), 'markdown-docs-path': help_page_path('user/markdown'), 'visibility-help-link': help_page_path("public_access/public_access") } }
|
||||
- available_visibility_levels = available_visibility_levels(@snippet)
|
||||
#js-snippet-edit.snippet-form{ data: {'project_path': @snippet.project&.full_path, 'snippet-gid': @snippet.new_record? ? '' : @snippet.to_global_id, 'markdown-preview-path': preview_markdown_path(parent), 'markdown-docs-path': help_page_path('user/markdown'), 'visibility-help-link': help_page_path("public_access/public_access"), 'visibility_levels': available_visibility_levels, 'selected_level': snippets_selected_visibility_level(available_visibility_levels, @snippet.visibility_level), 'multiple_levels_restricted': multiple_visibility_levels_restricted? } }
|
||||
- else
|
||||
.snippet-form-holder
|
||||
= form_for @snippet, url: url,
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Migrate '.fa-spinner' to '.spinner' for 'app/assets/javascripts/ajax_loading_spinner.js'
|
||||
merge_request: 41147
|
||||
author: Gilang Gumilar
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Adjust the Package Registry breadcrumb to match navigation
|
||||
merge_request: 41264
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Validates pypi required_python size to avoid 500 error
|
||||
merge_request: 40803
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Replace v-html with v-safe-html in note_signed_out_widget_spec.js
|
||||
merge_request: 41219
|
||||
author: Kev @KevSlashNull
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Replace v-html with v-safe-html in delete_project_modal.vue
|
||||
merge_request: 41130
|
||||
author: Kev @KevSlashNull
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Replace v-html with v-safe-html in list.vue
|
||||
merge_request: 41145
|
||||
author: Kev @KevSlashNull
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Replace v-html with v-safe-html in suggestions.vue
|
||||
merge_request: 41200
|
||||
author: Kev @KevSlashNull
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update Workhorse to v8.45.0
|
||||
merge_request: 41293
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix create & manage label actions in Labels dropdown
|
||||
merge_request: 40511
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix Rails/SaveBang offenses for spec/serializers/*
|
||||
merge_request: 41309
|
||||
author: Rajendra Kadam
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix Rails/SaveBang offenses for spec/services/issues/*
|
||||
merge_request: 41312
|
||||
author: Rajendra Kadam
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix Rails/SaveBang offenses for spec/requests/api/pages/*
|
||||
merge_request: 41324
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix Rails/SaveBang offenses for spec/models/cycle_analytics/*
|
||||
merge_request: 41326
|
||||
author: Rajendra Kadam
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix Rails/SaveBang offenses for ee/spec/services/projects/*
|
||||
merge_request: 41332
|
||||
author: Rajendra Kadam
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix Rails/SaveBang offenses for ee/spec/lib/ee/gitlab/background_migration/*
|
||||
merge_request: 41357
|
||||
author: Rajendra Kadam
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix Rails/SaveBang offenses for spec/requests/api/*
|
||||
merge_request: 41362
|
||||
author: Rajendra Kadam
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix Style/RedundantSort cop
|
||||
merge_request: 41108
|
||||
author: Rajendra Kadam
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Flag errors from psql when restoring from backups
|
||||
merge_request: 40911
|
||||
author:
|
||||
type: fixed
|
|
@ -59,11 +59,9 @@ if gitlab.mr_labels.include?('database') || db_paths_to_review.any?
|
|||
markdown(DB_MESSAGE)
|
||||
markdown(DB_FILES_MESSAGE + helper.markdown_list(db_paths_to_review)) if db_paths_to_review.any?
|
||||
|
||||
database_labels = helper.missing_database_labels(gitlab.mr_labels)
|
||||
|
||||
if database_labels.any?
|
||||
unless has_database_scoped_labels?(gitlab.mr_labels)
|
||||
gitlab.api.update_merge_request(gitlab.mr_json['project_id'],
|
||||
gitlab.mr_json['iid'],
|
||||
labels: (gitlab.mr_labels + database_labels).join(','))
|
||||
gitlab.mr_json['iid'],
|
||||
add_labels: 'database::review pending')
|
||||
end
|
||||
end
|
||||
|
|
|
@ -25,9 +25,3 @@ markdown(<<~MARKDOWN)
|
|||
- [Technical Writers assignments](https://about.gitlab.com/handbook/engineering/technical-writing/#designated-technical-writers) for the appropriate technical writer for this review.
|
||||
- [Documentation workflows](https://docs.gitlab.com/ee/development/documentation/workflow.html) for information on when to assign a merge request for review.
|
||||
MARKDOWN
|
||||
|
||||
unless gitlab.mr_labels.include?('documentation')
|
||||
gitlab.api.update_merge_request(gitlab.mr_json['project_id'],
|
||||
gitlab.mr_json['iid'],
|
||||
labels: (gitlab.mr_labels + ['documentation']).join(','))
|
||||
end
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
gitlab_danger = GitlabDanger.new(helper.gitlab_helper)
|
||||
|
||||
return unless gitlab_danger.ci?
|
||||
|
||||
SPECIALIZATIONS = {
|
||||
database: 'database',
|
||||
backend: 'backend',
|
||||
frontend: 'frontend',
|
||||
docs: 'documentation',
|
||||
qa: 'QA',
|
||||
engineering_productivity: 'Engineering Productivity'
|
||||
}.freeze
|
||||
|
||||
labels_to_add = helper.changes_by_category.each_with_object([]) do |(category, _changes), memo|
|
||||
label = SPECIALIZATIONS.fetch(category, category.to_s)
|
||||
memo << label unless gitlab.mr_labels.include?(label)
|
||||
end
|
||||
|
||||
if labels_to_add.any?
|
||||
gitlab.api.update_merge_request(gitlab.mr_json['project_id'],
|
||||
gitlab.mr_json['iid'],
|
||||
add_labels: labels_to_add.join(','))
|
||||
end
|
|
@ -123,7 +123,7 @@ Read more about [GitLab as an OAuth2 provider](oauth2.md).
|
|||
### Personal/project access tokens
|
||||
|
||||
Access tokens can be used to authenticate with the API by passing it in either the `private_token` parameter
|
||||
or the `Private-Token` header.
|
||||
or the `PRIVATE-TOKEN` header.
|
||||
|
||||
Example of using the personal/project access token in a parameter:
|
||||
|
||||
|
@ -134,7 +134,7 @@ curl "https://gitlab.example.com/api/v4/projects?private_token=<your_access_toke
|
|||
Example of using the personal/project access token in a header:
|
||||
|
||||
```shell
|
||||
curl --header "Private-Token: <your_access_token>" "https://gitlab.example.com/api/v4/projects"
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects"
|
||||
```
|
||||
|
||||
You can also use personal/project access tokens with OAuth-compliant headers:
|
||||
|
@ -178,7 +178,7 @@ For more information, refer to the
|
|||
[users API](users.md#create-an-impersonation-token) docs.
|
||||
|
||||
Impersonation tokens are used exactly like regular personal access tokens, and can be passed in either the
|
||||
`private_token` parameter or the `Private-Token` header.
|
||||
`private_token` parameter or the `PRIVATE-TOKEN` header.
|
||||
|
||||
#### Disable impersonation
|
||||
|
||||
|
@ -266,7 +266,7 @@ GET /projects?private_token=<your_access_token>&sudo=username
|
|||
```
|
||||
|
||||
```shell
|
||||
curl --header "Private-Token: <your_access_token>" --header "Sudo: username" "https://gitlab.example.com/api/v4/projects"
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" --header "Sudo: username" "https://gitlab.example.com/api/v4/projects"
|
||||
```
|
||||
|
||||
Example of a valid API call and a request using cURL with sudo request,
|
||||
|
@ -277,7 +277,7 @@ GET /projects?private_token=<your_access_token>&sudo=23
|
|||
```
|
||||
|
||||
```shell
|
||||
curl --header "Private-Token: <your_access_token>" --header "Sudo: 23" "https://gitlab.example.com/api/v4/projects"
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" --header "Sudo: 23" "https://gitlab.example.com/api/v4/projects"
|
||||
```
|
||||
|
||||
## Status codes
|
||||
|
|
|
@ -31,7 +31,7 @@ GET /projects/:id/freeze_periods
|
|||
Example request:
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: gVWYVHDRzXiRpN1rUC8T" "https://gitlab.example.com/api/v4/projects/19/freeze_periods"
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/19/freeze_periods"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
@ -65,7 +65,7 @@ GET /projects/:id/freeze_periods/:freeze_period_id
|
|||
Example request:
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: gVWYVHDRzXiRpN1rUC8T" "https://gitlab.example.com/api/v4/projects/19/freeze_periods/1"
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/19/freeze_periods/1"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
@ -99,7 +99,7 @@ POST /projects/:id/freeze_periods
|
|||
Example request:
|
||||
|
||||
```shell
|
||||
curl --header 'Content-Type: application/json' --header "PRIVATE-TOKEN: gVWYVHDRzXiRpN1rUC8T" \
|
||||
curl --header 'Content-Type: application/json' --header "PRIVATE-TOKEN: <your_access_token>" \
|
||||
--data '{ "freeze_start": "0 23 * * 5", "freeze_end": "0 7 * * 1", "cron_timezone": "UTC" }' \
|
||||
--request POST https://gitlab.example.com/api/v4/projects/19/freeze_periods
|
||||
```
|
||||
|
@ -136,7 +136,7 @@ PUT /projects/:id/freeze_periods/:tag_name
|
|||
Example request:
|
||||
|
||||
```shell
|
||||
curl --header 'Content-Type: application/json' --header "PRIVATE-TOKEN: gVWYVHDRzXiRpN1rUC8T" \
|
||||
curl --header 'Content-Type: application/json' --header "PRIVATE-TOKEN: <your_access_token>" \
|
||||
--data '{ "freeze_end": "0 8 * * 1" }' \
|
||||
--request PUT https://gitlab.example.com/api/v4/projects/19/freeze_periods/1
|
||||
```
|
||||
|
@ -170,6 +170,6 @@ DELETE /projects/:id/freeze_periods/:freeze_period_id
|
|||
Example request:
|
||||
|
||||
```shell
|
||||
curl --request DELETE --header "PRIVATE-TOKEN: gVWYVHDRzXiRpN1rUC8T" "https://gitlab.example.com/api/v4/projects/19/freeze_periods/1"
|
||||
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/19/freeze_periods/1"
|
||||
|
||||
```
|
||||
|
|
|
@ -1802,6 +1802,36 @@ type ClusterAgentTokenCreatePayload {
|
|||
token: ClusterAgentToken
|
||||
}
|
||||
|
||||
"""
|
||||
Autogenerated input type of ClusterAgentTokenDelete
|
||||
"""
|
||||
input ClusterAgentTokenDeleteInput {
|
||||
"""
|
||||
A unique identifier for the client performing the mutation.
|
||||
"""
|
||||
clientMutationId: String
|
||||
|
||||
"""
|
||||
Global ID of the cluster agent token that will be deleted
|
||||
"""
|
||||
id: ClustersAgentTokenID!
|
||||
}
|
||||
|
||||
"""
|
||||
Autogenerated return type of ClusterAgentTokenDelete
|
||||
"""
|
||||
type ClusterAgentTokenDeletePayload {
|
||||
"""
|
||||
A unique identifier for the client performing the mutation.
|
||||
"""
|
||||
clientMutationId: String
|
||||
|
||||
"""
|
||||
Errors encountered during execution of the mutation.
|
||||
"""
|
||||
errors: [String!]!
|
||||
}
|
||||
|
||||
"""
|
||||
Identifier of Clusters::Agent
|
||||
"""
|
||||
|
@ -10030,6 +10060,7 @@ type Mutation {
|
|||
boardListUpdateLimitMetrics(input: BoardListUpdateLimitMetricsInput!): BoardListUpdateLimitMetricsPayload
|
||||
clusterAgentDelete(input: ClusterAgentDeleteInput!): ClusterAgentDeletePayload
|
||||
clusterAgentTokenCreate(input: ClusterAgentTokenCreateInput!): ClusterAgentTokenCreatePayload
|
||||
clusterAgentTokenDelete(input: ClusterAgentTokenDeleteInput!): ClusterAgentTokenDeletePayload
|
||||
commitCreate(input: CommitCreateInput!): CommitCreatePayload
|
||||
configureSast(input: ConfigureSastInput!): ConfigureSastPayload
|
||||
createAlertIssue(input: CreateAlertIssueInput!): CreateAlertIssuePayload
|
||||
|
|
|
@ -4944,6 +4944,94 @@
|
|||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "INPUT_OBJECT",
|
||||
"name": "ClusterAgentTokenDeleteInput",
|
||||
"description": "Autogenerated input type of ClusterAgentTokenDelete",
|
||||
"fields": null,
|
||||
"inputFields": [
|
||||
{
|
||||
"name": "id",
|
||||
"description": "Global ID of the cluster agent token that will be deleted",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "ClustersAgentTokenID",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "clientMutationId",
|
||||
"description": "A unique identifier for the client performing the mutation.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
}
|
||||
],
|
||||
"interfaces": null,
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "ClusterAgentTokenDeletePayload",
|
||||
"description": "Autogenerated return type of ClusterAgentTokenDelete",
|
||||
"fields": [
|
||||
{
|
||||
"name": "clientMutationId",
|
||||
"description": "A unique identifier for the client performing the mutation.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "errors",
|
||||
"description": "Errors encountered during execution of the mutation.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [
|
||||
|
||||
],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "SCALAR",
|
||||
"name": "ClustersAgentID",
|
||||
|
@ -28343,6 +28431,33 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "clusterAgentTokenDelete",
|
||||
"description": null,
|
||||
"args": [
|
||||
{
|
||||
"name": "input",
|
||||
"description": null,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "INPUT_OBJECT",
|
||||
"name": "ClusterAgentTokenDeleteInput",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"kind": "OBJECT",
|
||||
"name": "ClusterAgentTokenDeletePayload",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "commitCreate",
|
||||
"description": null,
|
||||
|
|
|
@ -314,6 +314,15 @@ Autogenerated return type of ClusterAgentTokenCreate
|
|||
| `secret` | String | Token secret value. Make sure you save it - you won't be able to access it again |
|
||||
| `token` | ClusterAgentToken | Token created after mutation |
|
||||
|
||||
## ClusterAgentTokenDeletePayload
|
||||
|
||||
Autogenerated return type of ClusterAgentTokenDelete
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | ---- | ---------- |
|
||||
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
|
||||
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
|
||||
|
||||
## Commit
|
||||
|
||||
| Name | Type | Description |
|
||||
|
|
|
@ -47,6 +47,37 @@ To protect an environment:
|
|||
|
||||
The protected environment will now appear in the list of protected environments.
|
||||
|
||||
## Environment access by group membership
|
||||
|
||||
A user may be granted access to protected environments as part of
|
||||
[group membership](../../user/group/index.md). Users with
|
||||
[Reporter permissions](../../user/permissions.md), can only be granted access to
|
||||
protected environments with this method.
|
||||
|
||||
## Deployment branch access
|
||||
|
||||
Users with [Developer permissions](../../user/permissions.md) can be granted
|
||||
access to a protected environment through any of these methods:
|
||||
|
||||
- As an individual contributor, through a role.
|
||||
- Through a group membership.
|
||||
|
||||
If the user also has push or merge access to the branch deployed on production,
|
||||
they have the following privileges:
|
||||
|
||||
- [Stopping an environment](index.md#stopping-an-environment).
|
||||
- [Delete a stopped environment](index.md#delete-a-stopped-environment).
|
||||
- [Create an environment terminal](index.md#web-terminals).
|
||||
|
||||
## Deployment-only access to protected environments
|
||||
|
||||
Users granted access to a protected environment, but not push or merge access
|
||||
to the branch deployed to it, are only granted access to deploy the environment.
|
||||
|
||||
NOTE: **Note:**
|
||||
Deployment-only access is the only possible access level for users with
|
||||
[Reporter permissions](../../user/permissions.md).
|
||||
|
||||
## Modifying and unprotecting environments
|
||||
|
||||
Maintainers can:
|
||||
|
@ -55,6 +86,10 @@ Maintainers can:
|
|||
**Allowed to Deploy** dropdown menu.
|
||||
- Unprotect a protected environment by clicking the **Unprotect** button for that environment.
|
||||
|
||||
NOTE: **Note:**
|
||||
After an environment is unprotected, all access entries are deleted and must
|
||||
be re-entered if the environment is re-protected.
|
||||
|
||||
For more information, see [Deployment safety](deployment_safety.md).
|
||||
|
||||
<!-- ## Troubleshooting
|
||||
|
|
|
@ -2448,7 +2448,7 @@ You can set the period with `start_in` key. The value of `start_in` key is an el
|
|||
provided. `start_in` key must be less than or equal to one week. Examples of valid values include:
|
||||
|
||||
- `'5'`
|
||||
- `10 seconds`
|
||||
- `5 seconds`
|
||||
- `30 minutes`
|
||||
- `1 day`
|
||||
- `1 week`
|
||||
|
@ -3269,7 +3269,8 @@ and are not accessible anymore.
|
|||
The value of `expire_in` is an elapsed time in seconds, unless a unit is
|
||||
provided. Examples of valid values:
|
||||
|
||||
- `42`
|
||||
- `'42'`
|
||||
- `42 seconds`
|
||||
- `3 mins 4 sec`
|
||||
- `2 hrs 20 min`
|
||||
- `2h20min`
|
||||
|
|
|
@ -305,7 +305,7 @@ Do not include the same information in multiple places.
|
|||
|
||||
### References across documents
|
||||
|
||||
- Give each folder an index.md page that introduces the topic, introduces the
|
||||
- Give each folder an `index.md` page that introduces the topic, introduces the
|
||||
pages within, and links to the pages within (including to the index pages of
|
||||
any next-level subpaths).
|
||||
- To ensure discoverability, ensure each new or renamed doc is linked from its
|
||||
|
@ -642,8 +642,8 @@ Additional examples are available in the [Pajamas guide for punctuation](https:/
|
|||
|
||||
### Placeholder text
|
||||
|
||||
Often in examples, a writer will provide a command or configuration that is
|
||||
complete apart from a value specific to the reader.
|
||||
Often in examples, a writer will provide a command or configuration that
|
||||
uses values specific to the reader.
|
||||
|
||||
In these cases, use [`<` and `>`](https://en.wikipedia.org/wiki/Usage_message#Pattern)
|
||||
to call out where a reader must replace text with their own value.
|
||||
|
@ -1943,7 +1943,7 @@ METHOD /endpoint
|
|||
Example request:
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" 'https://gitlab.example.com/api/v4/endpoint?parameters'
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/endpoint?parameters"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
@ -2038,7 +2038,7 @@ you can use in the API documentation.
|
|||
Get the details of a group:
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/gitlab-org
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/gitlab-org"
|
||||
```
|
||||
|
||||
#### cURL example with parameters passed in the URL
|
||||
|
@ -2066,7 +2066,7 @@ In this example we create a new group. Watch carefully the single and double
|
|||
quotes.
|
||||
|
||||
```shell
|
||||
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --header "Content-Type: application/json" --data '{"path": "my-group", "name": "My group"}' https://gitlab.example.com/api/v4/groups
|
||||
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --header "Content-Type: application/json" --data '{"path": "my-group", "name": "My group"}' "https://gitlab.example.com/api/v4/groups"
|
||||
```
|
||||
|
||||
#### Post data using form-data
|
||||
|
@ -2075,7 +2075,7 @@ Instead of using JSON or urlencode you can use multipart/form-data which
|
|||
properly handles data encoding:
|
||||
|
||||
```shell
|
||||
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --form "title=ssh-key" --form "key=ssh-rsa AAAAB3NzaC1yc2EA..." https://gitlab.example.com/api/v4/users/25/keys
|
||||
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --form "title=ssh-key" --form "key=ssh-rsa AAAAB3NzaC1yc2EA..." "https://gitlab.example.com/api/v4/users/25/keys"
|
||||
```
|
||||
|
||||
The above example is run by and administrator and will add an SSH public key
|
||||
|
@ -2101,7 +2101,7 @@ exclude specific users when requesting a list of users for a project, you would
|
|||
do something like this:
|
||||
|
||||
```shell
|
||||
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" --data "skip_users[]=<user_id>" --data "skip_users[]=<user_id>" https://gitlab.example.com/api/v4/projects/<project_id>/users
|
||||
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" --data "skip_users[]=<user_id>" --data "skip_users[]=<user_id>" "https://gitlab.example.com/api/v4/projects/<project_id>/users"
|
||||
```
|
||||
|
||||
## GraphQL API
|
||||
|
|
|
@ -524,6 +524,48 @@ for modifications. If you have no other choice, an `around` block similar to the
|
|||
example for global variables, above, can be used, but this should be avoided if
|
||||
at all possible.
|
||||
|
||||
#### Test Snowplow events
|
||||
|
||||
CAUTION: **Warning:**
|
||||
Snowplow performs **runtime type checks** by using the [contracts gem](https://rubygems.org/gems/contracts).
|
||||
Since Snowplow is **by default disabled in tests and development**, it can be hard to
|
||||
**catch exceptions** when mocking `Gitlab::Tracking`.
|
||||
|
||||
To catch runtime errors due to type checks, you can enable Snowplow in tests by marking the spec with
|
||||
`:snowplow` and use the `expect_snowplow_event` helper which will check for
|
||||
calls to `Gitlab::Tracking#event`.
|
||||
|
||||
```ruby
|
||||
describe '#show', :snowplow do
|
||||
it 'tracks snowplow events' do
|
||||
get :show
|
||||
|
||||
expect_snowplow_event(
|
||||
category: 'Experiment',
|
||||
action: 'start',
|
||||
)
|
||||
expect_snowplow_event(
|
||||
category: 'Experiment',
|
||||
action: 'sent',
|
||||
property: 'property',
|
||||
label: 'label'
|
||||
)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
When you want to ensure that no event got called, you can use `expect_no_snowplow_event`.
|
||||
|
||||
```ruby
|
||||
describe '#show', :snowplow do
|
||||
it 'does not track any snowplow events' do
|
||||
get :show
|
||||
|
||||
expect_no_snowplow_event
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### Table-based / Parameterized tests
|
||||
|
||||
This style of testing is used to exercise one piece of code with a comprehensive
|
||||
|
|
|
@ -11,6 +11,12 @@ Navigate to the Alert details view by visiting the
|
|||
list. You need least Developer [permissions](../../user/permissions.md) to access
|
||||
alerts.
|
||||
|
||||
TIP: **Tip:**
|
||||
To review live examples of GitLab alerts, visit the
|
||||
[alert list](https://gitlab.com/gitlab-examples/ops/incident-setup/everyone/tanuki-inc/-/alert_management)
|
||||
for this demo project. Click any alert in the list to examine its alert details
|
||||
page.
|
||||
|
||||
Alerts provide **Overview** and **Alert details** tabs to give you the right
|
||||
amount of information you need.
|
||||
|
||||
|
|
|
@ -31,6 +31,10 @@ The alert list displays the following information:
|
|||
- **Triggered**: No one has begun investigation.
|
||||
- **Acknowledged**: Someone is actively investigating the problem.
|
||||
- **Resolved**: No further work is required.
|
||||
|
||||
TIP: **Tip:**
|
||||
Check out a [live example](https://gitlab.com/gitlab-examples/ops/incident-setup/everyone/tanuki-inc/-/alert_management)
|
||||
in GitLab to examine alerts in action.
|
||||
|
||||
## Enable Alerts
|
||||
|
||||
|
|
|
@ -33,6 +33,10 @@ The Incident list displays incidents sorted by incident created date.
|
|||
To see if a column is sortable, point your mouse at the header. Sortable columns
|
||||
display an arrow next to the column name.
|
||||
|
||||
TIP: **Tip:**
|
||||
For a live example of the incident list in action, visit this
|
||||
[demo project](https://gitlab.com/gitlab-examples/ops/incident-setup/everyone/tanuki-inc/-/incidents).
|
||||
|
||||
NOTE: **Note:**
|
||||
Incidents share the [Issues API](../../user/project/issues/index.md).
|
||||
|
||||
|
|
|
@ -295,6 +295,24 @@ For installations from source:
|
|||
sudo -u git -H bundle exec rake gitlab:backup:create SKIP=tar RAILS_ENV=production
|
||||
```
|
||||
|
||||
#### Disabling prompts during restore
|
||||
|
||||
During a restore from backup, the restore script may ask for confirmation before
|
||||
proceeding. If you wish to disable these prompts, you can set the `GITLAB_ASSUME_YES`
|
||||
environment variable to `1`.
|
||||
|
||||
For Omnibus GitLab packages:
|
||||
|
||||
```shell
|
||||
sudo GITLAB_ASSUME_YES=1 gitlab-backup restore
|
||||
```
|
||||
|
||||
For installations from source:
|
||||
|
||||
```shell
|
||||
sudo -u git -H GITLAB_ASSUME_YES=1 bundle exec rake gitlab:backup:restore RAILS_ENV=production
|
||||
```
|
||||
|
||||
#### Back up Git repositories concurrently
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37158) in GitLab 13.3.
|
||||
|
|
|
@ -68,7 +68,7 @@ Below are the settings for [GitLab Pages](https://about.gitlab.com/stages-devops
|
|||
| IP address | `35.185.44.232` | - |
|
||||
| Custom domains support | yes | no |
|
||||
| TLS certificates support | yes | no |
|
||||
| Maximum size (uncompressed) | 1G | 100M |
|
||||
| Maximum size (compressed) | 1G | 100M |
|
||||
|
||||
NOTE: **Note:**
|
||||
The maximum size of your Pages site is regulated by the artifacts maximum size
|
||||
|
@ -80,7 +80,7 @@ Below are the current settings regarding [GitLab CI/CD](../../ci/README.md).
|
|||
|
||||
| Setting | GitLab.com | Default |
|
||||
| ----------- | ----------------- | ------------- |
|
||||
| Artifacts maximum size (uncompressed) | 1G | 100M |
|
||||
| Artifacts maximum size (compressed) | 1G | 100M |
|
||||
| Artifacts [expiry time](../../ci/yaml/README.md#artifactsexpire_in) | From June 22, 2020, deleted after 30 days unless otherwise specified (artifacts created before that date have no expiry). | deleted after 30 days unless otherwise specified |
|
||||
| Scheduled Pipeline Cron | `*/5 * * * *` | `19 * * * *` |
|
||||
| [Max jobs in active pipelines](../../administration/instance_limits.md#number-of-jobs-in-active-pipelines) | `500` for Free tier, unlimited otherwise | Unlimited
|
||||
|
|
|
@ -6,16 +6,6 @@ module API
|
|||
extend Grape::API::Helpers
|
||||
include ::API::Helpers::PackagesHelpers
|
||||
|
||||
params :workhorse_upload_params do
|
||||
optional 'file.path', type: String, desc: 'Path to locally stored body (generated by Workhorse)'
|
||||
optional 'file.name', type: String, desc: 'Real filename as send in Content-Disposition (generated by Workhorse)'
|
||||
optional 'file.type', type: String, desc: 'Real content type as send in Content-Type (generated by Workhorse)'
|
||||
optional 'file.size', type: Integer, desc: 'Real size of file (generated by Workhorse)'
|
||||
optional 'file.md5', type: String, desc: 'MD5 checksum of the file (generated by Workhorse)'
|
||||
optional 'file.sha1', type: String, desc: 'SHA1 checksum of the file (generated by Workhorse)'
|
||||
optional 'file.sha256', type: String, desc: 'SHA256 checksum of the file (generated by Workhorse)'
|
||||
end
|
||||
|
||||
def find_job_from_http_basic_auth
|
||||
return unless request.headers
|
||||
|
||||
|
@ -36,12 +26,6 @@ module API
|
|||
DeployToken.active.find_by_token(token)
|
||||
end
|
||||
|
||||
def uploaded_package_file(param_name = :file)
|
||||
uploaded_file = UploadedFile.from_params(params, param_name, ::Packages::PackageFileUploader.workhorse_local_upload_path)
|
||||
bad_request!('Missing package file!') unless uploaded_file
|
||||
uploaded_file
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def decode_token
|
||||
|
|
|
@ -8,10 +8,10 @@ module Backup
|
|||
attr_reader :progress
|
||||
attr_reader :config, :db_file_name
|
||||
|
||||
def initialize(progress)
|
||||
def initialize(progress, filename: nil)
|
||||
@progress = progress
|
||||
@config = YAML.load_file(File.join(Rails.root, 'config', 'database.yml'))[Rails.env]
|
||||
@db_file_name = File.join(Gitlab.config.backup.path, 'db', 'database.sql.gz')
|
||||
@db_file_name = filename || File.join(Gitlab.config.backup.path, 'db', 'database.sql.gz')
|
||||
end
|
||||
|
||||
def dump
|
||||
|
@ -57,26 +57,63 @@ module Backup
|
|||
decompress_pid = spawn(*%w(gzip -cd), out: decompress_wr, in: db_file_name)
|
||||
decompress_wr.close
|
||||
|
||||
restore_pid =
|
||||
status, errors =
|
||||
case config["adapter"]
|
||||
when "postgresql" then
|
||||
progress.print "Restoring PostgreSQL database #{config['database']} ... "
|
||||
pg_env
|
||||
spawn('psql', config['database'], in: decompress_rd)
|
||||
execute_and_track_errors(pg_restore_cmd, decompress_rd)
|
||||
end
|
||||
decompress_rd.close
|
||||
|
||||
success = [decompress_pid, restore_pid].all? do |pid|
|
||||
Process.waitpid(pid)
|
||||
$?.success?
|
||||
Process.waitpid(decompress_pid)
|
||||
success = $?.success? && status.success?
|
||||
|
||||
if errors.present?
|
||||
progress.print "------ BEGIN ERRORS -----".color(:yellow)
|
||||
progress.print errors.join.color(:yellow)
|
||||
progress.print "------ END ERRORS -------".color(:yellow)
|
||||
end
|
||||
|
||||
report_success(success)
|
||||
abort 'Restore failed' unless success
|
||||
raise Backup::Error, 'Restore failed' unless success
|
||||
|
||||
errors
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def execute_and_track_errors(cmd, decompress_rd)
|
||||
errors = []
|
||||
|
||||
Open3.popen3(ENV, *cmd) do |stdin, stdout, stderr, thread|
|
||||
stdin.binmode
|
||||
|
||||
Thread.new do
|
||||
data = stdout.read
|
||||
$stdout.write(data)
|
||||
end
|
||||
|
||||
Thread.new do
|
||||
until (raw_line = stderr.gets).nil?
|
||||
warn(raw_line)
|
||||
# Recent database dumps will use --if-exists with pg_dump
|
||||
errors << raw_line unless raw_line =~ /does not exist$/
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
IO.copy_stream(decompress_rd, stdin)
|
||||
rescue Errno::EPIPE
|
||||
end
|
||||
|
||||
stdin.close
|
||||
|
||||
thread.join
|
||||
[thread.value, errors]
|
||||
end
|
||||
end
|
||||
|
||||
def pg_env
|
||||
args = {
|
||||
'username' => 'PGUSER',
|
||||
|
@ -101,5 +138,11 @@ module Backup
|
|||
progress.puts '[FAILED]'.color(:red)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def pg_restore_cmd
|
||||
['psql', config['database']]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -206,16 +206,6 @@ module Gitlab
|
|||
usernames.map { |u| Gitlab::Danger::Teammate.new('username' => u) }
|
||||
end
|
||||
|
||||
def missing_database_labels(current_mr_labels)
|
||||
labels = if has_database_scoped_labels?(current_mr_labels)
|
||||
['database']
|
||||
else
|
||||
['database', 'database::review pending']
|
||||
end
|
||||
|
||||
labels - current_mr_labels
|
||||
end
|
||||
|
||||
def sanitize_mr_title(title)
|
||||
title.gsub(DRAFT_REGEX, '').gsub(/`/, '\\\`')
|
||||
end
|
||||
|
@ -259,8 +249,6 @@ module Gitlab
|
|||
all_changed_files.grep(regex)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def has_database_scoped_labels?(current_mr_labels)
|
||||
current_mr_labels.any? { |label| label.start_with?('database::') }
|
||||
end
|
||||
|
|
|
@ -24,6 +24,8 @@ module Gitlab
|
|||
# Returns "yes" the user chose to continue
|
||||
# Raises Gitlab::TaskAbortedByUserError if the user chose *not* to continue
|
||||
def ask_to_continue
|
||||
return if Gitlab::Utils.to_boolean(ENV['GITLAB_ASSUME_YES'])
|
||||
|
||||
answer = prompt("Do you want to continue (yes/no)? ".color(:blue), %w{yes no})
|
||||
raise Gitlab::TaskAbortedByUserError unless answer == "yes"
|
||||
end
|
||||
|
|
|
@ -56,12 +56,19 @@ module Gitlab
|
|||
|
||||
def snowplow
|
||||
@snowplow ||= SnowplowTracker::Tracker.new(
|
||||
SnowplowTracker::AsyncEmitter.new(Gitlab::CurrentSettings.snowplow_collector_hostname, protocol: 'https'),
|
||||
emitter,
|
||||
SnowplowTracker::Subject.new,
|
||||
SNOWPLOW_NAMESPACE,
|
||||
Gitlab::CurrentSettings.snowplow_app_id
|
||||
)
|
||||
end
|
||||
|
||||
def emitter
|
||||
SnowplowTracker::AsyncEmitter.new(
|
||||
Gitlab::CurrentSettings.snowplow_collector_hostname,
|
||||
protocol: 'https'
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -22,6 +22,7 @@ class GitlabDanger
|
|||
roulette
|
||||
ce_ee_vue_templates
|
||||
sidekiq_queues
|
||||
specialization_labels
|
||||
].freeze
|
||||
|
||||
MESSAGE_PREFIX = '==>'.freeze
|
||||
|
|
|
@ -136,7 +136,21 @@ namespace :gitlab do
|
|||
|
||||
task restore: :gitlab_environment do
|
||||
puts_time "Restoring database ... ".color(:blue)
|
||||
Backup::Database.new(progress).restore
|
||||
errors = Backup::Database.new(progress).restore
|
||||
|
||||
if errors.present?
|
||||
warning = <<~MSG
|
||||
There were errors in restoring the schema. This may cause
|
||||
issues if this results in missing indexes, constraints, or
|
||||
columns. Please record the errors above and contact GitLab
|
||||
Support if you have questions:
|
||||
https://about.gitlab.com/support/
|
||||
MSG
|
||||
|
||||
warn warning.color(:red)
|
||||
ask_to_continue
|
||||
end
|
||||
|
||||
puts_time "done".color(:green)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7734,12 +7734,21 @@ msgstr ""
|
|||
msgid "DastProfiles|Could not create the site profile. Please try again."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Could not delete scanner profile. Please refresh the page, or try again later."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Could not delete scanner profiles:"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Could not delete site profile. Please refresh the page, or try again later."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Could not delete site profiles:"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Could not fetch scanner profiles. Please refresh the page, or try again later."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
|
||||
msgstr ""
|
||||
|
||||
|
@ -7752,6 +7761,9 @@ msgstr ""
|
|||
msgid "DastProfiles|Do you want to discard your changes?"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Edit feature will come soon. Please create a new profile if changes needed"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Edit site profile"
|
||||
msgstr ""
|
||||
|
||||
|
@ -7794,6 +7806,9 @@ msgstr ""
|
|||
msgid "DastProfiles|Scanner Profile"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Scanner Profiles"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Site Profile"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ FactoryBot.define do
|
|||
forward_deployment_enabled { nil }
|
||||
end
|
||||
|
||||
after(:create) do |project, evaluator|
|
||||
before(:create) do |project, evaluator|
|
||||
# Builds and MRs can't have higher visibility level than repository access level.
|
||||
builds_access_level = [evaluator.builds_access_level, evaluator.repository_access_level].min
|
||||
merge_requests_access_level = [evaluator.merge_requests_access_level, evaluator.repository_access_level].min
|
||||
|
@ -56,8 +56,10 @@ FactoryBot.define do
|
|||
pages_access_level: evaluator.pages_access_level
|
||||
}
|
||||
|
||||
project.project_feature.update!(hash)
|
||||
project.build_project_feature(hash)
|
||||
end
|
||||
|
||||
after(:create) do |project, evaluator|
|
||||
# Normally the class Projects::CreateService is used for creating
|
||||
# projects, and this class takes care of making sure the owner and current
|
||||
# user have access to the project. Our specs don't use said service class,
|
||||
|
|
|
@ -19,8 +19,9 @@ describe('Ajax Loading Spinner', () => {
|
|||
req.beforeSend(xhr, { dataType: 'text/html' });
|
||||
|
||||
expect(icon).not.toHaveClass('fa-trash-o');
|
||||
expect(icon).toHaveClass('fa-spinner');
|
||||
expect(icon).toHaveClass('fa-spin');
|
||||
expect(icon).toHaveClass('gl-spinner');
|
||||
expect(icon).toHaveClass('gl-spinner-orange');
|
||||
expect(icon).toHaveClass('gl-spinner-sm');
|
||||
expect(icon.dataset.icon).toEqual('fa-trash-o');
|
||||
expect(ajaxLoadingSpinner.getAttribute('disabled')).toEqual('');
|
||||
|
||||
|
@ -44,8 +45,9 @@ describe('Ajax Loading Spinner', () => {
|
|||
const icon = ajaxLoadingSpinner.querySelector('i');
|
||||
|
||||
expect(icon).toHaveClass('fa-trash-o');
|
||||
expect(icon).not.toHaveClass('fa-spinner');
|
||||
expect(icon).not.toHaveClass('fa-spin');
|
||||
expect(icon).not.toHaveClass('gl-spinner');
|
||||
expect(icon).not.toHaveClass('gl-spinner-orange');
|
||||
expect(icon).not.toHaveClass('gl-spinner-sm');
|
||||
expect(ajaxLoadingSpinner.getAttribute('disabled')).toEqual(null);
|
||||
|
||||
done();
|
||||
|
|
|
@ -11,6 +11,7 @@ describe('board_form.vue', () => {
|
|||
const propsData = {
|
||||
canAdminBoard: false,
|
||||
labelsPath: `${TEST_HOST}/labels/path`,
|
||||
labelsWebUrl: `${TEST_HOST}/-/labels`,
|
||||
};
|
||||
|
||||
const findModal = () => wrapper.find(DeprecatedModal);
|
||||
|
|
|
@ -86,6 +86,7 @@ describe('BoardsSelector', () => {
|
|||
canAdminBoard: true,
|
||||
multipleIssueBoardsAvailable: true,
|
||||
labelsPath: `${TEST_HOST}/labels/path`,
|
||||
labelsWebUrl: `${TEST_HOST}/labels`,
|
||||
projectId: 42,
|
||||
groupId: 19,
|
||||
scopedIssueBoardFeatureEnabled: true,
|
||||
|
|
|
@ -67,7 +67,7 @@ exports[`Project remove modal initialized matches the snapshot 1`] = `
|
|||
|
||||
<p>
|
||||
<code
|
||||
class="ws-pre-wrap"
|
||||
class="gl-white-space-pre-wrap"
|
||||
>
|
||||
foo
|
||||
</code>
|
||||
|
|
|
@ -56,7 +56,7 @@ exports[`Project remove modal intialized matches the snapshot 1`] = `
|
|||
|
||||
<p>
|
||||
<code
|
||||
class="ws-pre-wrap"
|
||||
class="gl-white-space-pre-wrap"
|
||||
>
|
||||
foo
|
||||
</code>
|
||||
|
|
|
@ -20,6 +20,7 @@ exports[`Snippet Visibility Edit component rendering matches the snapshot 1`] =
|
|||
</label>
|
||||
|
||||
<gl-form-group-stub
|
||||
class="gl-mb-0"
|
||||
id="visibility-level-setting"
|
||||
>
|
||||
<gl-form-radio-group-stub
|
||||
|
@ -90,5 +91,12 @@ exports[`Snippet Visibility Edit component rendering matches the snapshot 1`] =
|
|||
</gl-form-radio-stub>
|
||||
</gl-form-radio-group-stub>
|
||||
</gl-form-group-stub>
|
||||
|
||||
<div
|
||||
class="text-muted"
|
||||
data-testid="restricted-levels-info"
|
||||
>
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
@ -102,6 +102,13 @@ describe('Snippet Edit app', () => {
|
|||
markdownDocsPath: 'http://docs.foo.bar',
|
||||
...props,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
snippet: {
|
||||
visibilityLevel: SNIPPET_VISIBILITY_PRIVATE,
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,31 +1,55 @@
|
|||
import { GlFormRadio, GlIcon, GlFormRadioGroup, GlLink } from '@gitlab/ui';
|
||||
import { mount, shallowMount } from '@vue/test-utils';
|
||||
import SnippetVisibilityEdit from '~/snippets/components/snippet_visibility_edit.vue';
|
||||
import { defaultSnippetVisibilityLevels } from '~/snippets/utils/blob';
|
||||
import {
|
||||
SNIPPET_VISIBILITY,
|
||||
SNIPPET_VISIBILITY_PRIVATE,
|
||||
SNIPPET_VISIBILITY_INTERNAL,
|
||||
SNIPPET_VISIBILITY_PUBLIC,
|
||||
SNIPPET_LEVELS_RESTRICTED,
|
||||
SNIPPET_LEVELS_DISABLED,
|
||||
} from '~/snippets/constants';
|
||||
|
||||
describe('Snippet Visibility Edit component', () => {
|
||||
let wrapper;
|
||||
const defaultHelpLink = '/foo/bar';
|
||||
const defaultVisibilityLevel = 'private';
|
||||
const defaultVisibility = defaultSnippetVisibilityLevels([0, 10, 20]);
|
||||
|
||||
function createComponent(propsData = {}, deep = false) {
|
||||
function createComponent({
|
||||
propsData = {},
|
||||
visibilityLevels = defaultVisibility,
|
||||
multipleLevelsRestricted = false,
|
||||
deep = false,
|
||||
} = {}) {
|
||||
const method = deep ? mount : shallowMount;
|
||||
const $apollo = {
|
||||
queries: {
|
||||
defaultVisibility: {
|
||||
loading: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
wrapper = method.call(this, SnippetVisibilityEdit, {
|
||||
mock: { $apollo },
|
||||
propsData: {
|
||||
helpLink: defaultHelpLink,
|
||||
isProjectSnippet: false,
|
||||
value: defaultVisibilityLevel,
|
||||
...propsData,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visibilityLevels,
|
||||
multipleLevelsRestricted,
|
||||
};
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const findLabel = () => wrapper.find('label');
|
||||
const findLink = () => wrapper.find('label').find(GlLink);
|
||||
const findRadios = () => wrapper.find(GlFormRadioGroup).findAll(GlFormRadio);
|
||||
const findRadiosData = () =>
|
||||
findRadios().wrappers.map(x => {
|
||||
|
@ -47,60 +71,84 @@ describe('Snippet Visibility Edit component', () => {
|
|||
expect(wrapper.element).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders visibility options', () => {
|
||||
createComponent({}, true);
|
||||
it('renders label help link', () => {
|
||||
createComponent();
|
||||
|
||||
expect(findRadiosData()).toEqual([
|
||||
{
|
||||
expect(findLink().attributes('href')).toBe(defaultHelpLink);
|
||||
});
|
||||
|
||||
it('when helpLink is not defined, does not render label help link', () => {
|
||||
createComponent({ propsData: { helpLink: null } });
|
||||
|
||||
expect(findLink().exists()).toBe(false);
|
||||
});
|
||||
|
||||
describe('Visibility options', () => {
|
||||
const findRestrictedInfo = () => wrapper.find('[data-testid="restricted-levels-info"]');
|
||||
const RESULTING_OPTIONS = {
|
||||
0: {
|
||||
value: SNIPPET_VISIBILITY_PRIVATE,
|
||||
icon: SNIPPET_VISIBILITY.private.icon,
|
||||
text: SNIPPET_VISIBILITY.private.label,
|
||||
description: SNIPPET_VISIBILITY.private.description,
|
||||
},
|
||||
{
|
||||
10: {
|
||||
value: SNIPPET_VISIBILITY_INTERNAL,
|
||||
icon: SNIPPET_VISIBILITY.internal.icon,
|
||||
text: SNIPPET_VISIBILITY.internal.label,
|
||||
description: SNIPPET_VISIBILITY.internal.description,
|
||||
},
|
||||
{
|
||||
20: {
|
||||
value: SNIPPET_VISIBILITY_PUBLIC,
|
||||
icon: SNIPPET_VISIBILITY.public.icon,
|
||||
text: SNIPPET_VISIBILITY.public.label,
|
||||
description: SNIPPET_VISIBILITY.public.description,
|
||||
},
|
||||
]);
|
||||
});
|
||||
};
|
||||
|
||||
it('when project snippet, renders special private description', () => {
|
||||
createComponent({ isProjectSnippet: true }, true);
|
||||
|
||||
expect(findRadiosData()[0]).toEqual({
|
||||
value: SNIPPET_VISIBILITY_PRIVATE,
|
||||
icon: SNIPPET_VISIBILITY.private.icon,
|
||||
text: SNIPPET_VISIBILITY.private.label,
|
||||
description: SNIPPET_VISIBILITY.private.description_project,
|
||||
it.each`
|
||||
levels | resultOptions
|
||||
${undefined} | ${[]}
|
||||
${''} | ${[]}
|
||||
${[]} | ${[]}
|
||||
${[0]} | ${[RESULTING_OPTIONS[0]]}
|
||||
${[0, 10]} | ${[RESULTING_OPTIONS[0], RESULTING_OPTIONS[10]]}
|
||||
${[0, 10, 20]} | ${[RESULTING_OPTIONS[0], RESULTING_OPTIONS[10], RESULTING_OPTIONS[20]]}
|
||||
${[0, 20]} | ${[RESULTING_OPTIONS[0], RESULTING_OPTIONS[20]]}
|
||||
${[10, 20]} | ${[RESULTING_OPTIONS[10], RESULTING_OPTIONS[20]]}
|
||||
`('renders correct visibility options for $levels', ({ levels, resultOptions }) => {
|
||||
createComponent({ visibilityLevels: defaultSnippetVisibilityLevels(levels), deep: true });
|
||||
expect(findRadiosData()).toEqual(resultOptions);
|
||||
});
|
||||
});
|
||||
|
||||
it('renders label help link', () => {
|
||||
createComponent();
|
||||
it.each`
|
||||
levels | levelsRestricted | resultText
|
||||
${[]} | ${false} | ${SNIPPET_LEVELS_DISABLED}
|
||||
${[]} | ${true} | ${SNIPPET_LEVELS_DISABLED}
|
||||
${[0]} | ${true} | ${SNIPPET_LEVELS_RESTRICTED}
|
||||
${[0]} | ${false} | ${''}
|
||||
${[0, 10, 20]} | ${false} | ${''}
|
||||
`(
|
||||
'renders correct information about restricted visibility levels for $levels',
|
||||
({ levels, levelsRestricted, resultText }) => {
|
||||
createComponent({
|
||||
visibilityLevels: defaultSnippetVisibilityLevels(levels),
|
||||
multipleLevelsRestricted: levelsRestricted,
|
||||
});
|
||||
expect(findRestrictedInfo().text()).toBe(resultText);
|
||||
},
|
||||
);
|
||||
|
||||
expect(
|
||||
findLabel()
|
||||
.find(GlLink)
|
||||
.attributes('href'),
|
||||
).toBe(defaultHelpLink);
|
||||
});
|
||||
it('when project snippet, renders special private description', () => {
|
||||
createComponent({ propsData: { isProjectSnippet: true }, deep: true });
|
||||
|
||||
it('when helpLink is not defined, does not render label help link', () => {
|
||||
createComponent({ helpLink: null });
|
||||
|
||||
expect(
|
||||
findLabel()
|
||||
.find(GlLink)
|
||||
.exists(),
|
||||
).toBe(false);
|
||||
expect(findRadiosData()[0]).toEqual({
|
||||
value: SNIPPET_VISIBILITY_PRIVATE,
|
||||
icon: SNIPPET_VISIBILITY.private.icon,
|
||||
text: SNIPPET_VISIBILITY.private.label,
|
||||
description: SNIPPET_VISIBILITY.private.description_project,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -108,7 +156,7 @@ describe('Snippet Visibility Edit component', () => {
|
|||
it('pre-selects correct option in the list', () => {
|
||||
const value = SNIPPET_VISIBILITY_INTERNAL;
|
||||
|
||||
createComponent({ value });
|
||||
createComponent({ propsData: { value } });
|
||||
|
||||
expect(wrapper.find(GlFormRadioGroup).attributes('checked')).toBe(value);
|
||||
});
|
||||
|
|
|
@ -14,6 +14,7 @@ const createComponent = headerTitle => {
|
|||
};
|
||||
|
||||
describe('DropdownCreateLabelComponent', () => {
|
||||
const colorsCount = Object.keys(mockSuggestedColors).length;
|
||||
let vm;
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -27,7 +28,7 @@ describe('DropdownCreateLabelComponent', () => {
|
|||
|
||||
describe('created', () => {
|
||||
it('initializes `suggestedColors` prop on component from `gon.suggested_color_labels` object', () => {
|
||||
expect(vm.suggestedColors.length).toBe(mockSuggestedColors.length);
|
||||
expect(vm.suggestedColors.length).toBe(colorsCount);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -78,11 +79,11 @@ describe('DropdownCreateLabelComponent', () => {
|
|||
const colorsListContainerEl = vm.$el.querySelector('.suggest-colors.suggest-colors-dropdown');
|
||||
|
||||
expect(colorsListContainerEl).not.toBe(null);
|
||||
expect(colorsListContainerEl.querySelectorAll('a').length).toBe(mockSuggestedColors.length);
|
||||
expect(colorsListContainerEl.querySelectorAll('a').length).toBe(colorsCount);
|
||||
|
||||
const colorItemEl = colorsListContainerEl.querySelectorAll('a')[0];
|
||||
|
||||
expect(colorItemEl.dataset.color).toBe(vm.suggestedColors[0]);
|
||||
expect(colorItemEl.dataset.color).toBe(vm.suggestedColors[0].colorCode);
|
||||
expect(colorItemEl.getAttribute('style')).toBe('background-color: rgb(0, 51, 204);');
|
||||
});
|
||||
|
||||
|
|
|
@ -15,29 +15,29 @@ export const mockLabels = [
|
|||
},
|
||||
];
|
||||
|
||||
export const mockSuggestedColors = [
|
||||
'#0033CC',
|
||||
'#428BCA',
|
||||
'#44AD8E',
|
||||
'#A8D695',
|
||||
'#5CB85C',
|
||||
'#69D100',
|
||||
'#004E00',
|
||||
'#34495E',
|
||||
'#7F8C8D',
|
||||
'#A295D6',
|
||||
'#5843AD',
|
||||
'#8E44AD',
|
||||
'#FFECDB',
|
||||
'#AD4363',
|
||||
'#D10069',
|
||||
'#CC0033',
|
||||
'#FF0000',
|
||||
'#D9534F',
|
||||
'#D1D100',
|
||||
'#F0AD4E',
|
||||
'#AD8D43',
|
||||
];
|
||||
export const mockSuggestedColors = {
|
||||
'#0033CC': 'UA blue',
|
||||
'#428BCA': 'Moderate blue',
|
||||
'#44AD8E': 'Lime green',
|
||||
'#A8D695': 'Feijoa',
|
||||
'#5CB85C': 'Slightly desaturated green',
|
||||
'#69D100': 'Bright green',
|
||||
'#004E00': 'Very dark lime green',
|
||||
'#34495E': 'Very dark desaturated blue',
|
||||
'#7F8C8D': 'Dark grayish cyan',
|
||||
'#A295D6': 'Slightly desaturated blue',
|
||||
'#5843AD': 'Dark moderate blue',
|
||||
'#8E44AD': 'Dark moderate violet',
|
||||
'#FFECDB': 'Very pale orange',
|
||||
'#AD4363': 'Dark moderate pink',
|
||||
'#D10069': 'Strong pink',
|
||||
'#CC0033': 'Strong red',
|
||||
'#FF0000': 'Pure red',
|
||||
'#D9534F': 'Soft red',
|
||||
'#D1D100': 'Strong yellow',
|
||||
'#F0AD4E': 'Soft orange',
|
||||
'#AD8D43': 'Dark moderate orange',
|
||||
};
|
||||
|
||||
export const mockConfig = {
|
||||
showCreate: true,
|
||||
|
|
|
@ -86,36 +86,4 @@ RSpec.describe API::Helpers::PackagesManagerClientsHelpers do
|
|||
|
||||
it_behaves_like 'invalid auth header'
|
||||
end
|
||||
|
||||
describe '#uploaded_package_file' do
|
||||
let_it_be(:params) { {} }
|
||||
|
||||
subject { helper.uploaded_package_file }
|
||||
|
||||
before do
|
||||
allow(helper).to receive(:params).and_return(params)
|
||||
end
|
||||
|
||||
context 'with valid uploaded package file' do
|
||||
let_it_be(:uploaded_file) { Object.new }
|
||||
|
||||
before do
|
||||
allow(UploadedFile).to receive(:from_params).and_return(uploaded_file)
|
||||
end
|
||||
|
||||
it { is_expected.to be uploaded_file }
|
||||
end
|
||||
|
||||
context 'with invalid uploaded package file' do
|
||||
before do
|
||||
allow(UploadedFile).to receive(:from_params).and_return(nil)
|
||||
end
|
||||
|
||||
it 'fails with bad_request!' do
|
||||
expect(helper).to receive(:bad_request!)
|
||||
|
||||
expect(subject).to be nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Backup::Database do
|
||||
let(:progress) { StringIO.new }
|
||||
let(:output) { progress.string }
|
||||
|
||||
describe '#restore' do
|
||||
let(:cmd) { %W[#{Gem.ruby} -e $stdout.puts(1)] }
|
||||
let(:data) { Rails.root.join("spec/fixtures/pages_empty.tar.gz").to_s }
|
||||
|
||||
subject { described_class.new(progress, filename: data) }
|
||||
|
||||
before do
|
||||
allow(subject).to receive(:pg_restore_cmd).and_return(cmd)
|
||||
end
|
||||
|
||||
context 'with an empty .gz file' do
|
||||
let(:data) { Rails.root.join("spec/fixtures/pages_empty.tar.gz").to_s }
|
||||
|
||||
it 'returns successfully' do
|
||||
expect(subject.restore).to eq([])
|
||||
|
||||
expect(output).to include("Restoring PostgreSQL database")
|
||||
expect(output).to include("[DONE]")
|
||||
expect(output).not_to include("ERRORS")
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a corrupted .gz file' do
|
||||
let(:data) { Rails.root.join("spec/fixtures/big-image.png").to_s }
|
||||
|
||||
it 'raises a backup error' do
|
||||
expect { subject.restore }.to raise_error(Backup::Error)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the restore command prints errors' do
|
||||
let(:visible_error) { "This is a test error\n" }
|
||||
let(:noise) { "Table projects does not exist\n" }
|
||||
let(:cmd) { %W[#{Gem.ruby} -e $stderr.write("#{noise}#{visible_error}")] }
|
||||
|
||||
it 'filters out noise from errors' do
|
||||
expect(subject.restore).to eq([visible_error])
|
||||
expect(output).to include("ERRORS")
|
||||
expect(output).not_to include(noise)
|
||||
expect(output).to include(visible_error)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -371,22 +371,6 @@ RSpec.describe Gitlab::Danger::Helper do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#missing_database_labels' do
|
||||
subject { helper.missing_database_labels(current_mr_labels) }
|
||||
|
||||
context 'when current merge request has ~database::review pending' do
|
||||
let(:current_mr_labels) { ['database::review pending', 'feature'] }
|
||||
|
||||
it { is_expected.to match_array(['database']) }
|
||||
end
|
||||
|
||||
context 'when current merge request does not have ~database::review pending' do
|
||||
let(:current_mr_labels) { ['feature'] }
|
||||
|
||||
it { is_expected.to match_array(['database', 'database::review pending']) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#sanitize_mr_title' do
|
||||
where(:mr_title, :expected_mr_title) do
|
||||
'My MR title' | 'My MR title'
|
||||
|
|
|
@ -2945,6 +2945,22 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
|
|||
describe '#has_coverage_reports?' do
|
||||
subject { pipeline.has_coverage_reports? }
|
||||
|
||||
context 'when pipeline has a code coverage artifact' do
|
||||
let(:pipeline) { create(:ci_pipeline, :with_coverage_report_artifact, :running, project: project) }
|
||||
|
||||
it { expect(subject).to be_truthy }
|
||||
end
|
||||
|
||||
context 'when pipeline does not have a code coverage artifact' do
|
||||
let(:pipeline) { create(:ci_pipeline, :success, project: project) }
|
||||
|
||||
it { expect(subject).to be_falsey }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#can_generate_coverage_reports?' do
|
||||
subject { pipeline.can_generate_coverage_reports? }
|
||||
|
||||
context 'when pipeline has builds with coverage reports' do
|
||||
before do
|
||||
create(:ci_build, :coverage_reports, pipeline: pipeline, project: project)
|
||||
|
|
|
@ -15,17 +15,17 @@ RSpec.describe 'CycleAnalytics#issue' do
|
|||
generate_cycle_analytics_spec(
|
||||
phase: :issue,
|
||||
data_fn: -> (context) { { issue: context.build(:issue, project: context.project) } },
|
||||
start_time_conditions: [["issue created", -> (context, data) { data[:issue].save }]],
|
||||
start_time_conditions: [["issue created", -> (context, data) { data[:issue].save! }]],
|
||||
end_time_conditions: [["issue associated with a milestone",
|
||||
-> (context, data) do
|
||||
if data[:issue].persisted?
|
||||
data[:issue].update(milestone: context.create(:milestone, project: context.project))
|
||||
data[:issue].update!(milestone: context.create(:milestone, project: context.project))
|
||||
end
|
||||
end],
|
||||
["list label added to issue",
|
||||
-> (context, data) do
|
||||
if data[:issue].persisted?
|
||||
data[:issue].update(label_ids: [context.create(:list).label_id])
|
||||
data[:issue].update!(label_ids: [context.create(:list).label_id])
|
||||
end
|
||||
end]],
|
||||
post_fn: -> (context, data) do
|
||||
|
@ -35,7 +35,7 @@ RSpec.describe 'CycleAnalytics#issue' do
|
|||
it "returns nil" do
|
||||
regular_label = create(:label)
|
||||
issue = create(:issue, project: project)
|
||||
issue.update(label_ids: [regular_label.id])
|
||||
issue.update!(label_ids: [regular_label.id])
|
||||
|
||||
create_merge_request_closing_issue(user, project, issue)
|
||||
merge_merge_requests_closing_issue(user, project, issue)
|
||||
|
|
|
@ -22,11 +22,11 @@ RSpec.describe 'CycleAnalytics#plan' do
|
|||
end,
|
||||
start_time_conditions: [["issue associated with a milestone",
|
||||
-> (context, data) do
|
||||
data[:issue].update(milestone: context.create(:milestone, project: context.project))
|
||||
data[:issue].update!(milestone: context.create(:milestone, project: context.project))
|
||||
end],
|
||||
["list label added to issue",
|
||||
-> (context, data) do
|
||||
data[:issue].update(label_ids: [context.create(:list).label_id])
|
||||
data[:issue].update!(label_ids: [context.create(:list).label_id])
|
||||
end]],
|
||||
end_time_conditions: [["issue mentioned in a commit",
|
||||
-> (context, data) do
|
||||
|
@ -40,7 +40,7 @@ RSpec.describe 'CycleAnalytics#plan' do
|
|||
branch_name = generate(:branch)
|
||||
label = create(:label)
|
||||
issue = create(:issue, project: project)
|
||||
issue.update(label_ids: [label.id])
|
||||
issue.update!(label_ids: [label.id])
|
||||
create_commit_referencing_issue(issue, branch_name: branch_name)
|
||||
|
||||
create_merge_request_closing_issue(user, project, issue, source_branch: branch_name)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue