Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
0629b10324
commit
491c773c72
40 changed files with 854 additions and 354 deletions
|
@ -1 +1 @@
|
|||
8e731456707441f9e22bfb3b668885f0f983c449
|
||||
a8e416c51179f80bb9fe213c657f14d950279c75
|
||||
|
|
6
Gemfile
6
Gemfile
|
@ -133,8 +133,10 @@ gem 'seed-fu', '~> 2.3.7'
|
|||
gem 'elasticsearch-model', '~> 6.1'
|
||||
gem 'elasticsearch-rails', '~> 6.1', require: 'elasticsearch/rails/instrumentation'
|
||||
gem 'elasticsearch-api', '~> 6.8'
|
||||
gem 'aws-sdk'
|
||||
gem 'faraday_middleware-aws-signers-v4'
|
||||
gem 'aws-sdk-core', '~> 3'
|
||||
gem 'aws-sdk-cloudformation', '~> 1'
|
||||
gem 'aws-sdk-s3', '~> 1'
|
||||
gem 'faraday_middleware-aws-sigv4', '~>0.3.0'
|
||||
|
||||
# Markdown and HTML processing
|
||||
gem 'html-pipeline', '~> 2.12'
|
||||
|
|
39
Gemfile.lock
39
Gemfile.lock
|
@ -93,16 +93,25 @@ GEM
|
|||
encryptor (~> 3.0.0)
|
||||
attr_required (1.0.1)
|
||||
awesome_print (1.8.0)
|
||||
aws-eventstream (1.0.3)
|
||||
aws-sdk (2.11.374)
|
||||
aws-sdk-resources (= 2.11.374)
|
||||
aws-sdk-core (2.11.374)
|
||||
aws-sigv4 (~> 1.0)
|
||||
aws-eventstream (1.1.0)
|
||||
aws-partitions (1.345.0)
|
||||
aws-sdk-cloudformation (1.41.0)
|
||||
aws-sdk-core (~> 3, >= 3.99.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-core (3.104.3)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.239.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
jmespath (~> 1.0)
|
||||
aws-sdk-resources (2.11.374)
|
||||
aws-sdk-core (= 2.11.374)
|
||||
aws-sigv4 (1.1.0)
|
||||
aws-eventstream (~> 1.0, >= 1.0.2)
|
||||
aws-sdk-kms (1.36.0)
|
||||
aws-sdk-core (~> 3, >= 3.99.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.75.0)
|
||||
aws-sdk-core (~> 3, >= 3.104.1)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sigv4 (1.2.1)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
babosa (1.0.2)
|
||||
base32 (0.3.2)
|
||||
batch-loader (1.4.0)
|
||||
|
@ -306,9 +315,9 @@ GEM
|
|||
faraday (~> 0.8)
|
||||
faraday_middleware (0.14.0)
|
||||
faraday (>= 0.7.4, < 1.0)
|
||||
faraday_middleware-aws-signers-v4 (0.1.7)
|
||||
aws-sdk-resources (~> 2)
|
||||
faraday (~> 0.9)
|
||||
faraday_middleware-aws-sigv4 (0.3.0)
|
||||
aws-sigv4 (~> 1.0)
|
||||
faraday (>= 0.15)
|
||||
faraday_middleware-multi_json (0.0.6)
|
||||
faraday_middleware
|
||||
multi_json
|
||||
|
@ -1183,7 +1192,9 @@ DEPENDENCIES
|
|||
atlassian-jwt (~> 0.2.0)
|
||||
attr_encrypted (~> 3.1.0)
|
||||
awesome_print
|
||||
aws-sdk
|
||||
aws-sdk-cloudformation (~> 1)
|
||||
aws-sdk-core (~> 3)
|
||||
aws-sdk-s3 (~> 1)
|
||||
babosa (~> 1.0.2)
|
||||
base32 (~> 0.3.0)
|
||||
batch-loader (~> 1.4.0)
|
||||
|
@ -1230,7 +1241,7 @@ DEPENDENCIES
|
|||
escape_utils (~> 1.1)
|
||||
factory_bot_rails (~> 5.1.0)
|
||||
faraday (~> 0.12)
|
||||
faraday_middleware-aws-signers-v4
|
||||
faraday_middleware-aws-sigv4 (~> 0.3.0)
|
||||
fast_blank
|
||||
ffaker (~> 2.10)
|
||||
flipper (~> 0.17.1)
|
||||
|
|
|
@ -138,7 +138,7 @@ export default {
|
|||
@input="updateCommitMessage"
|
||||
@submit="commit"
|
||||
/>
|
||||
<div class="clearfix prepend-top-15">
|
||||
<div class="clearfix gl-mt-5">
|
||||
<actions />
|
||||
<loading-button
|
||||
:loading="submitCommitLoading"
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
<script>
|
||||
import { mapActions, mapState } from 'vuex';
|
||||
import { GlCard, GlForm, GlFormGroup, GlFormTextarea, GlButton, GlAlert } from '@gitlab/ui';
|
||||
import DashboardPanel from './dashboard_panel.vue';
|
||||
|
||||
const initialYml = `title:
|
||||
y_label:
|
||||
type: area-chart
|
||||
metrics:
|
||||
- query_range:
|
||||
label:
|
||||
`;
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlCard,
|
||||
GlForm,
|
||||
GlFormGroup,
|
||||
GlFormTextarea,
|
||||
GlButton,
|
||||
GlAlert,
|
||||
DashboardPanel,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
yml: initialYml,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState('monitoringDashboard', [
|
||||
'panelPreviewIsLoading',
|
||||
'panelPreviewError',
|
||||
'panelPreviewGraphData',
|
||||
]),
|
||||
},
|
||||
methods: {
|
||||
...mapActions('monitoringDashboard', ['fetchPanelPreview']),
|
||||
onSubmit() {
|
||||
this.fetchPanelPreview(this.yml);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<gl-card>
|
||||
<template #header>
|
||||
<h2 class="gl-font-size-h2 gl-my-3">{{ s__('Metrics|Define and preview panel') }}</h2>
|
||||
</template>
|
||||
<template #default>
|
||||
<gl-form @submit.prevent="onSubmit">
|
||||
<gl-form-group
|
||||
:label="s__('Metrics|Panel YAML')"
|
||||
:description="s__('Metrics|Define panel YAML to preview panel.')"
|
||||
label-for="panel-yml-input"
|
||||
>
|
||||
<gl-form-textarea
|
||||
id="panel-yml-input"
|
||||
v-model="yml"
|
||||
class="gl-h-200! gl-font-monospace! gl-font-size-monospace!"
|
||||
/>
|
||||
</gl-form-group>
|
||||
<div class="gl-text-right">
|
||||
<gl-button
|
||||
ref="clipboardCopyBtn"
|
||||
variant="success"
|
||||
category="secondary"
|
||||
:data-clipboard-text="yml"
|
||||
@click="$toast.show(s__('Metrics|Panel YAML copied'))"
|
||||
>
|
||||
{{ s__('Metrics|Copy YAML') }}
|
||||
</gl-button>
|
||||
<gl-button
|
||||
type="submit"
|
||||
variant="success"
|
||||
:disabled="panelPreviewIsLoading"
|
||||
class="js-no-auto-disable"
|
||||
>
|
||||
{{ s__('Metrics|Preview panel') }}
|
||||
</gl-button>
|
||||
</div>
|
||||
</gl-form>
|
||||
</template>
|
||||
</gl-card>
|
||||
|
||||
<gl-alert v-if="panelPreviewError" variant="warning" :dismissible="false">
|
||||
{{ panelPreviewError }}
|
||||
</gl-alert>
|
||||
|
||||
<dashboard-panel :graph-data="panelPreviewGraphData" />
|
||||
</div>
|
||||
</template>
|
|
@ -1,33 +1,45 @@
|
|||
<script>
|
||||
import { mapState } from 'vuex';
|
||||
import { GlButton, GlTooltipDirective } from '@gitlab/ui';
|
||||
import { s__ } from '~/locale';
|
||||
import routes from '../router/constants';
|
||||
import { DASHBOARD_PAGE } from '../router/constants';
|
||||
import DashboardPanelBuilder from '../components/dashboard_panel_builder.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlButton,
|
||||
DashboardPanelBuilder,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
computed: {
|
||||
...mapState('monitoringDashboard', ['panelPreviewYml']),
|
||||
dashboardPageLocation() {
|
||||
return {
|
||||
...this.$route,
|
||||
name: DASHBOARD_PAGE,
|
||||
};
|
||||
},
|
||||
},
|
||||
i18n: {
|
||||
backToDashboard: s__('Metrics|Back to dashboard'),
|
||||
},
|
||||
routes,
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div class="gl-display-flex gl-align-items-baseline">
|
||||
<gl-button
|
||||
v-gl-tooltip
|
||||
icon="go-back"
|
||||
:to="{ name: $options.routes.DASHBOARD_PAGE, params: { dashboard: $route.params.dashboard } }"
|
||||
:aria-label="$options.i18n.backToDashboard"
|
||||
:title="$options.i18n.backToDashboard"
|
||||
class="gl-mr-5"
|
||||
/>
|
||||
<h1 class="gl-mt-5 gl-font-size-h1">{{ s__('Metrics|Add panel') }}</h1>
|
||||
|
||||
<!-- TODO: Add components. See https://gitlab.com/groups/gitlab-org/-/epics/2882 -->
|
||||
<div class="gl-mt-5">
|
||||
<div class="gl-display-flex gl-align-items-baseline gl-mb-5">
|
||||
<gl-button
|
||||
v-gl-tooltip
|
||||
icon="go-back"
|
||||
:to="dashboardPageLocation"
|
||||
:aria-label="$options.i18n.backToDashboard"
|
||||
:title="$options.i18n.backToDashboard"
|
||||
class="gl-mr-5"
|
||||
/>
|
||||
<h1 class="gl-font-size-h1 gl-my-0">{{ s__('Metrics|Add panel') }}</h1>
|
||||
</div>
|
||||
<dashboard-panel-builder />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -41,3 +41,12 @@ export const getPrometheusQueryData = (prometheusEndpoint, params) =>
|
|||
}
|
||||
throw error;
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
export function getPanelJson(panelPreviewEndpoint, panelPreviewYml) {
|
||||
// TODO Use a real backend when it's available
|
||||
// https://gitlab.com/gitlab-org/gitlab/-/issues/228758
|
||||
|
||||
// eslint-disable-next-line @gitlab/require-i18n-strings
|
||||
return Promise.reject(new Error('API Not implemented.'));
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ import getAnnotations from '../queries/getAnnotations.query.graphql';
|
|||
import getDashboardValidationWarnings from '../queries/getDashboardValidationWarnings.query.graphql';
|
||||
import { convertObjectPropsToCamelCase } from '../../lib/utils/common_utils';
|
||||
import { s__, sprintf } from '../../locale';
|
||||
import { getDashboard, getPrometheusQueryData } from '../requests';
|
||||
import { getDashboard, getPrometheusQueryData, getPanelJson } from '../requests';
|
||||
|
||||
import { ENVIRONMENT_AVAILABLE_STATE, DEFAULT_DASHBOARD_PATH, VARIABLE_TYPES } from '../constants';
|
||||
|
||||
|
@ -473,3 +473,30 @@ export const fetchVariableMetricLabelValues = ({ state, commit }, { defaultQuery
|
|||
|
||||
return Promise.all(optionsRequests);
|
||||
};
|
||||
|
||||
// Panel Builder
|
||||
|
||||
export const fetchPanelPreview = ({ state, commit, dispatch }, panelPreviewYml) => {
|
||||
if (!panelPreviewYml) {
|
||||
return null;
|
||||
}
|
||||
|
||||
commit(types.REQUEST_PANEL_PREVIEW, panelPreviewYml);
|
||||
return getPanelJson(state.panelPreviewEndpoint, panelPreviewYml)
|
||||
.then(data => {
|
||||
commit(types.RECEIVE_PANEL_PREVIEW_SUCCESS, data);
|
||||
|
||||
dispatch('fetchPanelPreviewMetrics');
|
||||
})
|
||||
.catch(error => {
|
||||
commit(types.RECEIVE_PANEL_PREVIEW_FAILURE, error);
|
||||
});
|
||||
};
|
||||
|
||||
export const fetchPanelPreviewMetrics = () => {
|
||||
// TODO Use a axios mock instead of spy when backend is implemented
|
||||
// https://gitlab.com/gitlab-org/gitlab/-/issues/228758
|
||||
|
||||
// eslint-disable-next-line @gitlab/require-i18n-strings
|
||||
throw new Error('Not implemented');
|
||||
};
|
||||
|
|
|
@ -46,3 +46,8 @@ export const SET_SHOW_ERROR_BANNER = 'SET_SHOW_ERROR_BANNER';
|
|||
export const SET_PANEL_GROUP_METRICS = 'SET_PANEL_GROUP_METRICS';
|
||||
export const SET_ENVIRONMENTS_FILTER = 'SET_ENVIRONMENTS_FILTER';
|
||||
export const SET_EXPANDED_PANEL = 'SET_EXPANDED_PANEL';
|
||||
|
||||
// Panel preview
|
||||
export const REQUEST_PANEL_PREVIEW = 'REQUEST_PANEL_PREVIEW';
|
||||
export const RECEIVE_PANEL_PREVIEW_SUCCESS = 'RECEIVE_PANEL_PREVIEW_SUCCESS';
|
||||
export const RECEIVE_PANEL_PREVIEW_FAILURE = 'RECEIVE_PANEL_PREVIEW_FAILURE';
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import Vue from 'vue';
|
||||
import { pick } from 'lodash';
|
||||
import * as types from './mutation_types';
|
||||
import { mapToDashboardViewModel, normalizeQueryResponseData } from './utils';
|
||||
import { mapToDashboardViewModel, mapPanelToViewModel, normalizeQueryResponseData } from './utils';
|
||||
import httpStatusCodes from '~/lib/utils/http_status';
|
||||
import { BACKOFF_TIMEOUT } from '../../lib/utils/common_utils';
|
||||
import { BACKOFF_TIMEOUT } from '~/lib/utils/common_utils';
|
||||
import { dashboardEmptyStates, endpointKeys, initialStateKeys, metricStates } from '../constants';
|
||||
import { optionsFromSeriesData } from './variable_mapping';
|
||||
|
||||
|
@ -218,4 +218,24 @@ export default {
|
|||
// Add new options with assign to ensure Vue reactivity
|
||||
Object.assign(variable.options, { values });
|
||||
},
|
||||
|
||||
[types.REQUEST_PANEL_PREVIEW](state, panelPreviewYml) {
|
||||
state.panelPreviewIsLoading = true;
|
||||
|
||||
state.panelPreviewYml = panelPreviewYml;
|
||||
state.panelPreviewGraphData = null;
|
||||
state.panelPreviewError = null;
|
||||
},
|
||||
[types.RECEIVE_PANEL_PREVIEW_SUCCESS](state, payload) {
|
||||
state.panelPreviewIsLoading = false;
|
||||
|
||||
state.panelPreviewGraphData = mapPanelToViewModel(payload);
|
||||
state.panelPreviewError = null;
|
||||
},
|
||||
[types.RECEIVE_PANEL_PREVIEW_FAILURE](state, error) {
|
||||
state.panelPreviewIsLoading = false;
|
||||
|
||||
state.panelPreviewGraphData = null;
|
||||
state.panelPreviewError = error;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -59,6 +59,13 @@ export default () => ({
|
|||
* via the dashboard yml file.
|
||||
*/
|
||||
links: [],
|
||||
|
||||
// Panel editor / builder
|
||||
panelPreviewYml: '',
|
||||
panelPreviewIsLoading: false,
|
||||
panelPreviewGraphData: null,
|
||||
panelPreviewError: null,
|
||||
|
||||
// Other project data
|
||||
dashboardTimezone: timezones.LOCAL,
|
||||
annotations: [],
|
||||
|
|
|
@ -3,6 +3,7 @@ import ReplyPlaceholder from './discussion_reply_placeholder.vue';
|
|||
import ResolveDiscussionButton from './discussion_resolve_button.vue';
|
||||
import ResolveWithIssueButton from './discussion_resolve_with_issue_button.vue';
|
||||
import JumpToNextDiscussionButton from './discussion_jump_to_next_button.vue';
|
||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
|
||||
export default {
|
||||
name: 'DiscussionActions',
|
||||
|
@ -12,6 +13,7 @@ export default {
|
|||
ResolveWithIssueButton,
|
||||
JumpToNextDiscussionButton,
|
||||
},
|
||||
mixins: [glFeatureFlagsMixin()],
|
||||
props: {
|
||||
discussion: {
|
||||
type: Object,
|
||||
|
@ -36,6 +38,9 @@ export default {
|
|||
},
|
||||
},
|
||||
computed: {
|
||||
hideJumpToNextUnresolvedInThreads() {
|
||||
return this.glFeatures.hideJumpToNextUnresolvedInThreads;
|
||||
},
|
||||
resolvableNotes() {
|
||||
return this.discussion.notes.filter(x => x.resolvable);
|
||||
},
|
||||
|
@ -70,7 +75,11 @@ export default {
|
|||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="discussion.resolvable && shouldShowJumpToNextDiscussion"
|
||||
v-if="
|
||||
!hideJumpToNextUnresolvedInThreads &&
|
||||
discussion.resolvable &&
|
||||
shouldShowJumpToNextDiscussion
|
||||
"
|
||||
class="btn-group discussion-actions ml-sm-2"
|
||||
>
|
||||
<jump-to-next-discussion-button :from-discussion-id="discussion.id" />
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
import {
|
||||
GlBadge,
|
||||
GlButton,
|
||||
GlIcon,
|
||||
GlModal,
|
||||
GlModalDirective,
|
||||
GlTooltipDirective,
|
||||
|
@ -27,6 +26,7 @@ import PackageListRow from '../../shared/components/package_list_row.vue';
|
|||
import DependencyRow from './dependency_row.vue';
|
||||
import { numberToHumanSize } from '~/lib/utils/number_utils';
|
||||
import timeagoMixin from '~/vue_shared/mixins/timeago';
|
||||
import FileIcon from '~/vue_shared/components/file_icon.vue';
|
||||
import { generatePackageInfo } from '../utils';
|
||||
import { __, s__ } from '~/locale';
|
||||
import { PackageType, TrackingActions } from '../../shared/constants';
|
||||
|
@ -44,7 +44,7 @@ export default {
|
|||
GlTab,
|
||||
GlTabs,
|
||||
GlTable,
|
||||
GlIcon,
|
||||
FileIcon,
|
||||
GlSprintf,
|
||||
PackageActivity,
|
||||
PackageInformation,
|
||||
|
@ -243,19 +243,24 @@ export default {
|
|||
|
||||
<package-activity />
|
||||
|
||||
<h3 class="gl-font-lg">{{ __('Files') }}</h3>
|
||||
<gl-table
|
||||
:fields="$options.filesTableHeaderFields"
|
||||
:items="filesTableRows"
|
||||
tbody-tr-class="js-file-row"
|
||||
>
|
||||
<template #cell(name)="items">
|
||||
<gl-icon name="doc-code" class="space-right" />
|
||||
<gl-link
|
||||
:href="items.item.downloadPath"
|
||||
class="js-file-download"
|
||||
class="js-file-download gl-relative"
|
||||
@click="track($options.trackingActions.PULL_PACKAGE)"
|
||||
>
|
||||
{{ items.item.name }}
|
||||
<file-icon
|
||||
:file-name="items.item.name"
|
||||
css-classes="gl-relative file-icon"
|
||||
class="gl-mr-1 gl-relative"
|
||||
/>
|
||||
<span class="gl-relative">{{ items.item.name }}</span>
|
||||
</gl-link>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -260,10 +260,6 @@
|
|||
content: '\f0a3';
|
||||
}
|
||||
|
||||
.fa-minus-circle::before {
|
||||
content: '\f056';
|
||||
}
|
||||
|
||||
.fa-bitbucket::before {
|
||||
content: '\f171';
|
||||
}
|
||||
|
|
|
@ -86,7 +86,6 @@
|
|||
height: $input-height;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
color: $gl-text-color-secondary;
|
||||
|
||||
&:hover,
|
||||
|
|
|
@ -82,6 +82,10 @@
|
|||
.gl-h-32 { height: px-to-rem($grid-size * 4); }
|
||||
.gl-h-64 { height: px-to-rem($grid-size * 8); }
|
||||
|
||||
// Migrate this to Gitlab UI when FF is removed
|
||||
// https://gitlab.com/groups/gitlab-org/-/epics/2882
|
||||
.gl-h-200\! { height: px-to-rem($grid-size * 25) !important; }
|
||||
|
||||
.d-sm-table-column {
|
||||
@include media-breakpoint-up(sm) {
|
||||
display: table-column !important;
|
||||
|
|
|
@ -10,6 +10,7 @@ class Service < ApplicationRecord
|
|||
include IgnorableColumns
|
||||
|
||||
ignore_columns %i[title description], remove_with: '13.4', remove_after: '2020-09-22'
|
||||
ignore_columns %i[default], remove_with: '13.5', remove_after: '2020-10-22'
|
||||
|
||||
SERVICE_NAMES = %w[
|
||||
alerts asana assembla bamboo bugzilla buildkite campfire confluence custom_issue_tracker discord
|
||||
|
|
|
@ -96,7 +96,7 @@ module Admin
|
|||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def run_callbacks(batch)
|
||||
if active_external_issue_tracker?
|
||||
if integration.issue_tracker?
|
||||
Project.where(id: batch).update_all(has_external_issue_tracker: true)
|
||||
end
|
||||
|
||||
|
@ -106,10 +106,6 @@ module Admin
|
|||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
def active_external_issue_tracker?
|
||||
integration.issue_tracker? && !integration.default
|
||||
end
|
||||
|
||||
def active_external_wiki?
|
||||
integration.type == 'ExternalWikiService'
|
||||
end
|
||||
|
|
|
@ -66,7 +66,7 @@ module Projects
|
|||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def run_callbacks(batch)
|
||||
if active_external_issue_tracker?
|
||||
if template.issue_tracker?
|
||||
Project.where(id: batch).update_all(has_external_issue_tracker: true)
|
||||
end
|
||||
|
||||
|
@ -76,10 +76,6 @@ module Projects
|
|||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
def active_external_issue_tracker?
|
||||
template.issue_tracker? && !template.default
|
||||
end
|
||||
|
||||
def active_external_wiki?
|
||||
template.type == 'ExternalWikiService'
|
||||
end
|
||||
|
|
|
@ -24,5 +24,5 @@
|
|||
name: value_input_name,
|
||||
placeholder: s_('CiVariables|Input variable value') }
|
||||
= value
|
||||
%button.js-row-remove-button.ci-variable-row-remove-button.table-section.section-5.border-top-0{ type: 'button', 'aria-label': s_('CiVariables|Remove variable row') }
|
||||
= icon('minus-circle')
|
||||
%button.btn.btn-svg.btn-item-remove.js-row-remove-button.ci-variable-row-remove-button.table-section{ type: 'button', 'aria-label': s_('CiVariables|Remove variable row') }
|
||||
= sprite_icon('close')
|
||||
|
|
|
@ -60,5 +60,5 @@
|
|||
value: is_masked,
|
||||
data: { default: is_masked_default.to_s } }
|
||||
= render_if_exists 'ci/variables/environment_scope', form_field: form_field, variable: variable
|
||||
%button.js-row-remove-button.ci-variable-row-remove-button.table-section.section-5.border-top-0{ type: 'button', 'aria-label': s_('CiVariables|Remove variable row') }
|
||||
= icon('minus-circle')
|
||||
%button.btn.btn-svg.js-row-remove-button.ci-variable-row-remove-button.table-section{ type: 'button', 'aria-label': s_('CiVariables|Remove variable row') }
|
||||
= sprite_icon('close')
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
- hide_remember_me = local_assigns.fetch(:hide_remember_me, false)
|
||||
|
||||
.omniauth-container.prepend-top-15
|
||||
.omniauth-container.gl-mt-5
|
||||
%label.label-bold.d-block
|
||||
Sign in with
|
||||
- providers = enabled_button_based_providers
|
||||
|
|
|
@ -12,4 +12,4 @@
|
|||
.form-text.text-muted
|
||||
= s_('GroupSettings|The Auto DevOps pipeline will run if no alternative CI configuration file is found.')
|
||||
= link_to _('More information'), help_page_path('topics/autodevops/index.md'), target: '_blank'
|
||||
= f.submit _('Save changes'), class: 'btn btn-success prepend-top-15'
|
||||
= f.submit _('Save changes'), class: 'btn btn-success gl-mt-5'
|
||||
|
|
|
@ -54,4 +54,4 @@
|
|||
= s_('CICD|Automatic deployment to staging, manual deployment to production')
|
||||
= link_to icon('question-circle'), help_page_path('topics/autodevops/customize.md', anchor: 'incremental-rollout-to-production-premium'), target: '_blank'
|
||||
|
||||
= f.submit _('Save changes'), class: "btn btn-success prepend-top-15", data: { qa_selector: 'save_changes_button' }
|
||||
= f.submit _('Save changes'), class: "btn btn-success gl-mt-5", data: { qa_selector: 'save_changes_button' }
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Updates to file table in package details UI
|
||||
merge_request: 36723
|
||||
author: Adam Alvis (@adamalvis)
|
||||
type: fixed
|
|
@ -156,7 +156,7 @@ for example, without needing to explicitly pass an access token.
|
|||
With a few API endpoints you can use a [GitLab CI/CD job token](../user/project/new_ci_build_permissions_model.md#job-token)
|
||||
to authenticate with the API:
|
||||
|
||||
- [Get job artifacts](jobs.md#get-job-artifacts)
|
||||
- [Get job artifacts](job_artifacts.md#get-job-artifacts)
|
||||
- [Pipeline triggers](pipeline_triggers.md)
|
||||
- [Release creation](releases/index.md#create-a-release)
|
||||
|
||||
|
|
265
doc/api/job_artifacts.md
Normal file
265
doc/api/job_artifacts.md
Normal file
|
@ -0,0 +1,265 @@
|
|||
# Job Artifacts API
|
||||
|
||||
## Get job artifacts
|
||||
|
||||
> The use of `CI_JOB_TOKEN` in the artifacts download API was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/2346) in [GitLab Premium](https://about.gitlab.com/pricing/) 9.5.
|
||||
|
||||
Get the job's artifacts zipped archive of a project.
|
||||
|
||||
```plaintext
|
||||
GET /projects/:id/jobs/:job_id/artifacts
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|-------------|----------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
|
||||
| `job_id` | integer | yes | ID of a job. |
|
||||
| `job_token` **(PREMIUM)** | string | no | To be used with [triggers](../ci/triggers/README.md#when-a-pipeline-depends-on-the-artifacts-of-another-pipeline-premium) for multi-project pipelines. It should be invoked only inside `.gitlab-ci.yml`. Its value is always `$CI_JOB_TOKEN`. |
|
||||
|
||||
Example request using the `PRIVATE-TOKEN` header:
|
||||
|
||||
```shell
|
||||
curl --output artifacts.zip --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/jobs/42/artifacts"
|
||||
```
|
||||
|
||||
To use this in a [`script` definition](../ci/yaml/README.md#script) inside
|
||||
`.gitlab-ci.yml` **(PREMIUM)**, you can use either:
|
||||
|
||||
- The `JOB-TOKEN` header with the GitLab-provided `CI_JOB_TOKEN` variable.
|
||||
For example, the following job will download the artifacts of the job with ID
|
||||
`42`. Note that the command is wrapped into single quotes since it contains a
|
||||
colon (`:`):
|
||||
|
||||
```yaml
|
||||
artifact_download:
|
||||
stage: test
|
||||
script:
|
||||
- 'curl --location --output artifacts.zip --header "JOB-TOKEN: $CI_JOB_TOKEN" "https://gitlab.example.com/api/v4/projects/1/jobs/42/artifacts"'
|
||||
```
|
||||
|
||||
- Or the `job_token` attribute with the GitLab-provided `CI_JOB_TOKEN` variable.
|
||||
For example, the following job will download the artifacts of the job with ID `42`:
|
||||
|
||||
```yaml
|
||||
artifact_download:
|
||||
stage: test
|
||||
script:
|
||||
- 'curl --location --output artifacts.zip "https://gitlab.example.com/api/v4/projects/1/jobs/42/artifacts?job_token=$CI_JOB_TOKEN"'
|
||||
```
|
||||
|
||||
Possible response status codes:
|
||||
|
||||
| Status | Description |
|
||||
|-----------|---------------------------------|
|
||||
| 200 | Serves the artifacts file. |
|
||||
| 404 | Build not found or no artifacts.|
|
||||
|
||||
## Download the artifacts archive
|
||||
|
||||
> The use of `CI_JOB_TOKEN` in the artifacts download API was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/2346) in [GitLab Premium](https://about.gitlab.com/pricing/) 9.5.
|
||||
|
||||
Download the artifacts zipped archive from the latest successful pipeline for
|
||||
the given reference name and job, provided the job finished successfully. This
|
||||
is the same as [getting the job's artifacts](#get-job-artifacts), but by
|
||||
defining the job's name instead of its ID.
|
||||
|
||||
```plaintext
|
||||
GET /projects/:id/jobs/artifacts/:ref_name/download?job=name
|
||||
```
|
||||
|
||||
Parameters
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|-------------|----------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
|
||||
| `ref_name` | string | yes | Branch or tag name in repository. HEAD or SHA references are not supported. |
|
||||
| `job` | string | yes | The name of the job. |
|
||||
| `job_token` **(PREMIUM)** | string | no | To be used with [triggers](../ci/triggers/README.md#when-a-pipeline-depends-on-the-artifacts-of-another-pipeline-premium) for multi-project pipelines. It should be invoked only inside `.gitlab-ci.yml`. Its value is always `$CI_JOB_TOKEN`. |
|
||||
|
||||
Example request using the `PRIVATE-TOKEN` header:
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/jobs/artifacts/master/download?job=test"
|
||||
```
|
||||
|
||||
To use this in a [`script` definition](../ci/yaml/README.md#script) inside
|
||||
`.gitlab-ci.yml` **(PREMIUM)**, you can use either:
|
||||
|
||||
- The `JOB-TOKEN` header with the GitLab-provided `CI_JOB_TOKEN` variable.
|
||||
For example, the following job will download the artifacts of the `test` job
|
||||
of the `master` branch. Note that the command is wrapped into single quotes
|
||||
since it contains a colon (`:`):
|
||||
|
||||
```yaml
|
||||
artifact_download:
|
||||
stage: test
|
||||
script:
|
||||
- 'curl --location --output artifacts.zip --header "JOB-TOKEN: $CI_JOB_TOKEN" "https://gitlab.example.com/api/v4/projects/$CI_PROJECT_ID/jobs/artifacts/master/download?job=test"'
|
||||
```
|
||||
|
||||
- Or the `job_token` attribute with the GitLab-provided `CI_JOB_TOKEN` variable.
|
||||
For example, the following job will download the artifacts of the `test` job
|
||||
of the `master` branch:
|
||||
|
||||
```yaml
|
||||
artifact_download:
|
||||
stage: test
|
||||
script:
|
||||
- 'curl --location --output artifacts.zip "https://gitlab.example.com/api/v4/projects/$CI_PROJECT_ID/jobs/artifacts/master/download?job=test&job_token=$CI_JOB_TOKEN"'
|
||||
```
|
||||
|
||||
Possible response status codes:
|
||||
|
||||
| Status | Description |
|
||||
|-----------|---------------------------------|
|
||||
| 200 | Serves the artifacts file. |
|
||||
| 404 | Build not found or no artifacts.|
|
||||
|
||||
## Download a single artifact file by job ID
|
||||
|
||||
> Introduced in GitLab 10.0
|
||||
|
||||
Download a single artifact file from a job with a specified ID from within
|
||||
the job's artifacts zipped archive. The file is extracted from the archive and
|
||||
streamed to the client.
|
||||
|
||||
```plaintext
|
||||
GET /projects/:id/jobs/:job_id/artifacts/*artifact_path
|
||||
```
|
||||
|
||||
Parameters
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|-----------------|----------------|----------|------------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
|
||||
| `job_id` | integer | yes | The unique job identifier. |
|
||||
| `artifact_path` | string | yes | Path to a file inside the artifacts archive. |
|
||||
|
||||
Example request:
|
||||
|
||||
```shell
|
||||
curl --location --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/jobs/5/artifacts/some/release/file.pdf"
|
||||
```
|
||||
|
||||
Possible response status codes:
|
||||
|
||||
| Status | Description |
|
||||
|-----------|--------------------------------------|
|
||||
| 200 | Sends a single artifact file |
|
||||
| 400 | Invalid path provided |
|
||||
| 404 | Build not found or no file/artifacts |
|
||||
|
||||
## Download a single artifact file from specific tag or branch
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/23538) in GitLab 11.5.
|
||||
|
||||
Download a single artifact file for a specific job of the latest successful
|
||||
pipeline for the given reference name from within the job's artifacts archive.
|
||||
The file is extracted from the archive and streamed to the client.
|
||||
|
||||
```plaintext
|
||||
GET /projects/:id/jobs/artifacts/:ref_name/raw/*artifact_path?job=name
|
||||
```
|
||||
|
||||
Parameters:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|-----------------|----------------|----------|------------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
|
||||
| `ref_name` | string | yes | Branch or tag name in repository. HEAD or SHA references are not supported. |
|
||||
| `artifact_path` | string | yes | Path to a file inside the artifacts archive. |
|
||||
| `job` | string | yes | The name of the job. |
|
||||
|
||||
Example request:
|
||||
|
||||
```shell
|
||||
curl --location --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/jobs/artifacts/master/raw/some/release/file.pdf?job=pdf"
|
||||
```
|
||||
|
||||
Possible response status codes:
|
||||
|
||||
| Status | Description |
|
||||
|-----------|--------------------------------------|
|
||||
| 200 | Sends a single artifact file |
|
||||
| 400 | Invalid path provided |
|
||||
| 404 | Build not found or no file/artifacts |
|
||||
|
||||
## Keep artifacts
|
||||
|
||||
Prevents artifacts from being deleted when expiration is set.
|
||||
|
||||
```plaintext
|
||||
POST /projects/:id/jobs/:job_id/artifacts/keep
|
||||
```
|
||||
|
||||
Parameters
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|-----------|----------------|----------|------------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
|
||||
| `job_id` | integer | yes | ID of a job. |
|
||||
|
||||
Example request:
|
||||
|
||||
```shell
|
||||
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/jobs/1/artifacts/keep"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
{
|
||||
"commit": {
|
||||
"author_email": "admin@example.com",
|
||||
"author_name": "Administrator",
|
||||
"created_at": "2015-12-24T16:51:14.000+01:00",
|
||||
"id": "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
|
||||
"message": "Test the CI integration.",
|
||||
"short_id": "0ff3ae19",
|
||||
"title": "Test the CI integration."
|
||||
},
|
||||
"coverage": null,
|
||||
"allow_failure": false,
|
||||
"download_url": null,
|
||||
"id": 42,
|
||||
"name": "rubocop",
|
||||
"ref": "master",
|
||||
"artifacts": [],
|
||||
"runner": null,
|
||||
"stage": "test",
|
||||
"created_at": "2016-01-11T10:13:33.506Z",
|
||||
"started_at": "2016-01-11T10:13:33.506Z",
|
||||
"finished_at": "2016-01-11T10:15:10.506Z",
|
||||
"duration": 97.0,
|
||||
"status": "failed",
|
||||
"tag": false,
|
||||
"web_url": "https://example.com/foo/bar/-/jobs/42",
|
||||
"user": null
|
||||
}
|
||||
```
|
||||
|
||||
## Delete artifacts
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/25522) in GitLab 11.9.
|
||||
|
||||
Delete artifacts of a job.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/jobs/:job_id/artifacts
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|-----------|----------------|----------|------------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
|
||||
| `job_id` | integer | yes | ID of a job. |
|
||||
|
||||
Example request:
|
||||
|
||||
```shell
|
||||
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/jobs/1/artifacts"
|
||||
```
|
||||
|
||||
NOTE: **Note:**
|
||||
At least Maintainer role is required to delete artifacts.
|
||||
|
||||
If the artifacts were deleted successfully, a response with status `204 No Content` is returned.
|
272
doc/api/jobs.md
272
doc/api/jobs.md
|
@ -428,198 +428,6 @@ Example of response
|
|||
}
|
||||
```
|
||||
|
||||
## Get job artifacts
|
||||
|
||||
> **Notes**:
|
||||
>
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/2893) in GitLab 8.5.
|
||||
> - The use of `CI_JOB_TOKEN` in the artifacts download API was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/2346)
|
||||
> in [GitLab Premium](https://about.gitlab.com/pricing/) 9.5.
|
||||
|
||||
Get the job's artifacts zipped archive of a project.
|
||||
|
||||
```plaintext
|
||||
GET /projects/:id/jobs/:job_id/artifacts
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|-------------|----------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
|
||||
| `job_id` | integer | yes | ID of a job. |
|
||||
| `job_token` **(PREMIUM)** | string | no | To be used with [triggers](../ci/triggers/README.md#when-a-pipeline-depends-on-the-artifacts-of-another-pipeline-premium) for multi-project pipelines. It should be invoked only inside `.gitlab-ci.yml`. Its value is always `$CI_JOB_TOKEN`. |
|
||||
|
||||
Example request using the `PRIVATE-TOKEN` header:
|
||||
|
||||
```shell
|
||||
curl --output artifacts.zip --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/jobs/42/artifacts"
|
||||
```
|
||||
|
||||
To use this in a [`script` definition](../ci/yaml/README.md#script) inside
|
||||
`.gitlab-ci.yml` **(PREMIUM)**, you can use either:
|
||||
|
||||
- The `JOB-TOKEN` header with the GitLab-provided `CI_JOB_TOKEN` variable.
|
||||
For example, the following job will download the artifacts of the job with ID
|
||||
`42`. Note that the command is wrapped into single quotes since it contains a
|
||||
colon (`:`):
|
||||
|
||||
```yaml
|
||||
artifact_download:
|
||||
stage: test
|
||||
script:
|
||||
- 'curl --location --output artifacts.zip --header "JOB-TOKEN: $CI_JOB_TOKEN" "https://gitlab.example.com/api/v4/projects/1/jobs/42/artifacts"'
|
||||
```
|
||||
|
||||
- Or the `job_token` attribute with the GitLab-provided `CI_JOB_TOKEN` variable.
|
||||
For example, the following job will download the artifacts of the job with ID `42`:
|
||||
|
||||
```yaml
|
||||
artifact_download:
|
||||
stage: test
|
||||
script:
|
||||
- 'curl --location --output artifacts.zip "https://gitlab.example.com/api/v4/projects/1/jobs/42/artifacts?job_token=$CI_JOB_TOKEN"'
|
||||
```
|
||||
|
||||
Possible response status codes:
|
||||
|
||||
| Status | Description |
|
||||
|-----------|---------------------------------|
|
||||
| 200 | Serves the artifacts file. |
|
||||
| 404 | Build not found or no artifacts.|
|
||||
|
||||
## Download the artifacts archive
|
||||
|
||||
> **Notes**:
|
||||
>
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/5347) in GitLab 8.10.
|
||||
> - The use of `CI_JOB_TOKEN` in the artifacts download API was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/2346)
|
||||
> in [GitLab Premium](https://about.gitlab.com/pricing/) 9.5.
|
||||
|
||||
Download the artifacts zipped archive from the latest successful pipeline for
|
||||
the given reference name and job, provided the job finished successfully. This
|
||||
is the same as [getting the job's artifacts](#get-job-artifacts), but by
|
||||
defining the job's name instead of its ID.
|
||||
|
||||
```plaintext
|
||||
GET /projects/:id/jobs/artifacts/:ref_name/download?job=name
|
||||
```
|
||||
|
||||
Parameters
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|-------------|----------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
|
||||
| `ref_name` | string | yes | Branch or tag name in repository. HEAD or SHA references are not supported. |
|
||||
| `job` | string | yes | The name of the job. |
|
||||
| `job_token` **(PREMIUM)** | string | no | To be used with [triggers](../ci/triggers/README.md#when-a-pipeline-depends-on-the-artifacts-of-another-pipeline-premium) for multi-project pipelines. It should be invoked only inside `.gitlab-ci.yml`. Its value is always `$CI_JOB_TOKEN`. |
|
||||
|
||||
Example request using the `PRIVATE-TOKEN` header:
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/jobs/artifacts/master/download?job=test"
|
||||
```
|
||||
|
||||
To use this in a [`script` definition](../ci/yaml/README.md#script) inside
|
||||
`.gitlab-ci.yml` **(PREMIUM)**, you can use either:
|
||||
|
||||
- The `JOB-TOKEN` header with the GitLab-provided `CI_JOB_TOKEN` variable.
|
||||
For example, the following job will download the artifacts of the `test` job
|
||||
of the `master` branch. Note that the command is wrapped into single quotes
|
||||
since it contains a colon (`:`):
|
||||
|
||||
```yaml
|
||||
artifact_download:
|
||||
stage: test
|
||||
script:
|
||||
- 'curl --location --output artifacts.zip --header "JOB-TOKEN: $CI_JOB_TOKEN" "https://gitlab.example.com/api/v4/projects/$CI_PROJECT_ID/jobs/artifacts/master/download?job=test"'
|
||||
```
|
||||
|
||||
- Or the `job_token` attribute with the GitLab-provided `CI_JOB_TOKEN` variable.
|
||||
For example, the following job will download the artifacts of the `test` job
|
||||
of the `master` branch:
|
||||
|
||||
```yaml
|
||||
artifact_download:
|
||||
stage: test
|
||||
script:
|
||||
- 'curl --location --output artifacts.zip "https://gitlab.example.com/api/v4/projects/$CI_PROJECT_ID/jobs/artifacts/master/download?job=test&job_token=$CI_JOB_TOKEN"'
|
||||
```
|
||||
|
||||
Possible response status codes:
|
||||
|
||||
| Status | Description |
|
||||
|-----------|---------------------------------|
|
||||
| 200 | Serves the artifacts file. |
|
||||
| 404 | Build not found or no artifacts.|
|
||||
|
||||
## Download a single artifact file by job ID
|
||||
|
||||
> Introduced in GitLab 10.0
|
||||
|
||||
Download a single artifact file from a job with a specified ID from within
|
||||
the job's artifacts zipped archive. The file is extracted from the archive and
|
||||
streamed to the client.
|
||||
|
||||
```plaintext
|
||||
GET /projects/:id/jobs/:job_id/artifacts/*artifact_path
|
||||
```
|
||||
|
||||
Parameters
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|-----------------|----------------|----------|------------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
|
||||
| `job_id` | integer | yes | The unique job identifier. |
|
||||
| `artifact_path` | string | yes | Path to a file inside the artifacts archive. |
|
||||
|
||||
Example request:
|
||||
|
||||
```shell
|
||||
curl --location --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/jobs/5/artifacts/some/release/file.pdf"
|
||||
```
|
||||
|
||||
Possible response status codes:
|
||||
|
||||
| Status | Description |
|
||||
|-----------|--------------------------------------|
|
||||
| 200 | Sends a single artifact file |
|
||||
| 400 | Invalid path provided |
|
||||
| 404 | Build not found or no file/artifacts |
|
||||
|
||||
## Download a single artifact file from specific tag or branch
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/23538) in GitLab 11.5.
|
||||
|
||||
Download a single artifact file for a specific job of the latest successful
|
||||
pipeline for the given reference name from within the job's artifacts archive.
|
||||
The file is extracted from the archive and streamed to the client.
|
||||
|
||||
```plaintext
|
||||
GET /projects/:id/jobs/artifacts/:ref_name/raw/*artifact_path?job=name
|
||||
```
|
||||
|
||||
Parameters:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|-----------------|----------------|----------|------------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
|
||||
| `ref_name` | string | yes | Branch or tag name in repository. HEAD or SHA references are not supported. |
|
||||
| `artifact_path` | string | yes | Path to a file inside the artifacts archive. |
|
||||
| `job` | string | yes | The name of the job. |
|
||||
|
||||
Example request:
|
||||
|
||||
```shell
|
||||
curl --location --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/jobs/artifacts/master/raw/some/release/file.pdf?job=pdf"
|
||||
```
|
||||
|
||||
Possible response status codes:
|
||||
|
||||
| Status | Description |
|
||||
|-----------|--------------------------------------|
|
||||
| 200 | Sends a single artifact file |
|
||||
| 400 | Invalid path provided |
|
||||
| 404 | Build not found or no file/artifacts |
|
||||
|
||||
## Get a log file
|
||||
|
||||
Get a log (trace) of a specific job of a project:
|
||||
|
@ -796,86 +604,6 @@ Example of response
|
|||
}
|
||||
```
|
||||
|
||||
## Keep artifacts
|
||||
|
||||
Prevents artifacts from being deleted when expiration is set.
|
||||
|
||||
```plaintext
|
||||
POST /projects/:id/jobs/:job_id/artifacts/keep
|
||||
```
|
||||
|
||||
Parameters
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|-----------|----------------|----------|------------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
|
||||
| `job_id` | integer | yes | ID of a job. |
|
||||
|
||||
Example request:
|
||||
|
||||
```shell
|
||||
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/jobs/1/artifacts/keep"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
{
|
||||
"commit": {
|
||||
"author_email": "admin@example.com",
|
||||
"author_name": "Administrator",
|
||||
"created_at": "2015-12-24T16:51:14.000+01:00",
|
||||
"id": "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
|
||||
"message": "Test the CI integration.",
|
||||
"short_id": "0ff3ae19",
|
||||
"title": "Test the CI integration."
|
||||
},
|
||||
"coverage": null,
|
||||
"allow_failure": false,
|
||||
"download_url": null,
|
||||
"id": 42,
|
||||
"name": "rubocop",
|
||||
"ref": "master",
|
||||
"artifacts": [],
|
||||
"runner": null,
|
||||
"stage": "test",
|
||||
"created_at": "2016-01-11T10:13:33.506Z",
|
||||
"started_at": "2016-01-11T10:13:33.506Z",
|
||||
"finished_at": "2016-01-11T10:15:10.506Z",
|
||||
"duration": 97.0,
|
||||
"status": "failed",
|
||||
"tag": false,
|
||||
"web_url": "https://example.com/foo/bar/-/jobs/42",
|
||||
"user": null
|
||||
}
|
||||
```
|
||||
|
||||
## Delete artifacts
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/25522) in GitLab 11.9.
|
||||
|
||||
Delete artifacts of a job.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/jobs/:job_id/artifacts
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|-----------|----------------|----------|------------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
|
||||
| `job_id` | integer | yes | ID of a job. |
|
||||
|
||||
Example request:
|
||||
|
||||
```shell
|
||||
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/jobs/1/artifacts"
|
||||
```
|
||||
|
||||
NOTE: **Note:**
|
||||
At least Maintainer role is required to delete artifacts.
|
||||
|
||||
If the artifacts were deleted successfully, a response with status `204 No Content` is returned.
|
||||
|
||||
## Play a job
|
||||
|
||||
Triggers a manual action to start a job.
|
||||
|
|
|
@ -17,7 +17,7 @@ Job artifacts are a list of files and directories created by a job
|
|||
once it finishes. This feature is [enabled by default](../../administration/job_artifacts.md) in all
|
||||
GitLab installations.
|
||||
|
||||
Job artifacts created by GitLab Runner are uploaded to GitLab and are downloadable as a single archive using the GitLab UI or the [GitLab API](../../api/jobs.md#get-job-artifacts).
|
||||
Job artifacts created by GitLab Runner are uploaded to GitLab and are downloadable as a single archive using the GitLab UI or the [GitLab API](../../api/job_artifacts.md#get-job-artifacts).
|
||||
|
||||
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
|
||||
For an overview, watch the video [GitLab CI Pipeline, Artifacts, and Environments](https://www.youtube.com/watch?v=PCKDICEe10s).
|
||||
|
@ -439,7 +439,7 @@ To erase a job:
|
|||
|
||||
## Retrieve artifacts of private projects when using GitLab CI
|
||||
|
||||
In order to retrieve a job artifact of a different project, you might need to use a private token in order to [authenticate and download](../../api/jobs.md#get-job-artifacts) the artifacts.
|
||||
In order to retrieve a job artifact of a different project, you might need to use a private token in order to [authenticate and download](../../api/job_artifacts.md#get-job-artifacts) the artifacts.
|
||||
|
||||
<!-- ## Troubleshooting
|
||||
|
||||
|
|
|
@ -69,11 +69,15 @@ blog about it](https://about.gitlab.com/blog/2015/05/06/why-were-replacing-gitla
|
|||
### Creating a simple `.gitlab-ci.yml` file
|
||||
|
||||
NOTE: **Note:**
|
||||
`.gitlab-ci.yml` is a [YAML](https://en.wikipedia.org/wiki/YAML) file
|
||||
so you have to pay extra attention to indentation. Always use spaces, not tabs.
|
||||
A GitLab team member has made an [unofficial visual pipeline editor](https://unofficial.gitlab.tools/visual-pipelines/).
|
||||
There is a [plan to make it an official part of GitLab](https://gitlab.com/groups/gitlab-org/-/epics/4069)
|
||||
in the future, but it's available for anyone who wants to try it at the above link.
|
||||
|
||||
You need to create a file named `.gitlab-ci.yml` in the root directory of your
|
||||
repository. Below is an example for a Ruby on Rails project.
|
||||
repository. This is a [YAML](https://en.wikipedia.org/wiki/YAML) file
|
||||
so you have to pay extra attention to indentation. Always use spaces, not tabs.
|
||||
|
||||
Below is an example for a Ruby on Rails project:
|
||||
|
||||
```yaml
|
||||
image: "ruby:2.5"
|
||||
|
|
|
@ -97,7 +97,7 @@ This allows you to use that for multi-project pipelines and download artifacts
|
|||
from any project to which you have access as this follows the same principles
|
||||
with the [permission model](../../user/permissions.md#job-permissions).
|
||||
|
||||
Read more about the [jobs API](../../api/jobs.md#download-the-artifacts-archive).
|
||||
Read more about the [jobs API](../../api/job_artifacts.md#download-the-artifacts-archive).
|
||||
|
||||
## Adding a new trigger
|
||||
|
||||
|
|
|
@ -1365,7 +1365,7 @@ check the value of `$CI_COMMIT_BEFORE_SHA`. It has a value of
|
|||
`0000000000000000000000000000000000000000`:
|
||||
|
||||
- In branches with no commits.
|
||||
- Tag pipelines and scheduled pipelines. You should define rules very
|
||||
- In tag pipelines and scheduled pipelines. You should define rules very
|
||||
narrowly if you don't want to skip these.
|
||||
|
||||
To skip pipelines on all empty branches, but also tags and schedules:
|
||||
|
|
|
@ -14999,6 +14999,9 @@ msgstr ""
|
|||
msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
|
||||
msgstr ""
|
||||
|
||||
msgid "Metrics|Copy YAML"
|
||||
msgstr ""
|
||||
|
||||
msgid "Metrics|Create custom dashboard %{fileName}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -15017,6 +15020,12 @@ msgstr ""
|
|||
msgid "Metrics|Current"
|
||||
msgstr ""
|
||||
|
||||
msgid "Metrics|Define and preview panel"
|
||||
msgstr ""
|
||||
|
||||
msgid "Metrics|Define panel YAML to preview panel."
|
||||
msgstr ""
|
||||
|
||||
msgid "Metrics|Delete metric"
|
||||
msgstr ""
|
||||
|
||||
|
@ -15085,6 +15094,15 @@ msgstr ""
|
|||
msgid "Metrics|Open repository"
|
||||
msgstr ""
|
||||
|
||||
msgid "Metrics|Panel YAML"
|
||||
msgstr ""
|
||||
|
||||
msgid "Metrics|Panel YAML copied"
|
||||
msgstr ""
|
||||
|
||||
msgid "Metrics|Preview panel"
|
||||
msgstr ""
|
||||
|
||||
msgid "Metrics|PromQL query is valid"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { GlCard, GlForm, GlFormTextarea, GlAlert } from '@gitlab/ui';
|
||||
import { createStore } from '~/monitoring/stores';
|
||||
import DashboardPanel from '~/monitoring/components/dashboard_panel.vue';
|
||||
import * as types from '~/monitoring/stores/mutation_types';
|
||||
import { metricsDashboardResponse } from '../fixture_data';
|
||||
|
||||
import DashboardPanelBuilder from '~/monitoring/components/dashboard_panel_builder.vue';
|
||||
|
||||
const mockPanel = metricsDashboardResponse.dashboard.panel_groups[0].panels[0];
|
||||
|
||||
describe('dashboard invalid url parameters', () => {
|
||||
let store;
|
||||
let wrapper;
|
||||
let mockShowToast;
|
||||
|
||||
const createComponent = (props = {}, options = {}) => {
|
||||
wrapper = shallowMount(DashboardPanelBuilder, {
|
||||
propsData: { ...props },
|
||||
store,
|
||||
stubs: {
|
||||
GlCard,
|
||||
},
|
||||
mocks: {
|
||||
$toast: {
|
||||
show: mockShowToast,
|
||||
},
|
||||
},
|
||||
options,
|
||||
});
|
||||
};
|
||||
|
||||
const findForm = () => wrapper.find(GlForm);
|
||||
const findTxtArea = () => findForm().find(GlFormTextarea);
|
||||
const findSubmitBtn = () => findForm().find('[type="submit"]');
|
||||
const findClipboardCopyBtn = () => wrapper.find({ ref: 'clipboardCopyBtn' });
|
||||
const findPanel = () => wrapper.find(DashboardPanel);
|
||||
|
||||
beforeEach(() => {
|
||||
mockShowToast = jest.fn();
|
||||
store = createStore();
|
||||
createComponent();
|
||||
jest.spyOn(store, 'dispatch').mockResolvedValue();
|
||||
});
|
||||
|
||||
afterEach(() => {});
|
||||
|
||||
it('is mounted', () => {
|
||||
expect(wrapper.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('displays an empty dashboard panel', () => {
|
||||
expect(findPanel().exists()).toBe(true);
|
||||
expect(findPanel().props('graphData')).toBe(null);
|
||||
});
|
||||
|
||||
it('does not fetch initial data by default', () => {
|
||||
expect(store.dispatch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('yml form', () => {
|
||||
it('form exists and can be submitted', () => {
|
||||
expect(findForm().exists()).toBe(true);
|
||||
expect(findSubmitBtn().exists()).toBe(true);
|
||||
expect(findSubmitBtn().is('[disabled]')).toBe(false);
|
||||
});
|
||||
|
||||
it('form has a text area with a default value', () => {
|
||||
expect(findTxtArea().exists()).toBe(true);
|
||||
|
||||
const value = findTxtArea().attributes('value');
|
||||
|
||||
// Panel definition should contain a title and a type
|
||||
expect(value).toContain('title:');
|
||||
expect(value).toContain('type:');
|
||||
});
|
||||
|
||||
it('"copy to clipboard" button works', () => {
|
||||
findClipboardCopyBtn().vm.$emit('click');
|
||||
const clipboardText = findClipboardCopyBtn().attributes('data-clipboard-text');
|
||||
|
||||
expect(clipboardText).toContain('title:');
|
||||
expect(clipboardText).toContain('type:');
|
||||
|
||||
expect(mockShowToast).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('on submit fetches a panel preview', () => {
|
||||
findForm().vm.$emit('submit', new Event('submit'));
|
||||
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
expect(store.dispatch).toHaveBeenCalledWith(
|
||||
'monitoringDashboard/fetchPanelPreview',
|
||||
expect.stringContaining('title:'),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when form is submitted', () => {
|
||||
beforeEach(() => {
|
||||
store.commit(`monitoringDashboard/${types.REQUEST_PANEL_PREVIEW}`, 'mock yml content');
|
||||
return wrapper.vm.$nextTick();
|
||||
});
|
||||
|
||||
it('submit button is disabled', () => {
|
||||
expect(findSubmitBtn().is('[disabled]')).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there is an error', () => {
|
||||
const mockError = 'an error ocurred!';
|
||||
|
||||
beforeEach(() => {
|
||||
store.commit(`monitoringDashboard/${types.RECEIVE_PANEL_PREVIEW_FAILURE}`, mockError);
|
||||
return wrapper.vm.$nextTick();
|
||||
});
|
||||
|
||||
it('displays an alert', () => {
|
||||
expect(wrapper.find(GlAlert).exists()).toBe(true);
|
||||
expect(wrapper.find(GlAlert).text()).toBe(mockError);
|
||||
});
|
||||
|
||||
it('displays an empty dashboard panel', () => {
|
||||
expect(findPanel().props('graphData')).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when panel data is available', () => {
|
||||
beforeEach(() => {
|
||||
store.commit(`monitoringDashboard/${types.RECEIVE_PANEL_PREVIEW_SUCCESS}`, mockPanel);
|
||||
return wrapper.vm.$nextTick();
|
||||
});
|
||||
|
||||
it('displays no alert', () => {
|
||||
expect(wrapper.find(GlAlert).exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('displays panel with data', () => {
|
||||
const { title, type } = wrapper.find(DashboardPanel).props('graphData');
|
||||
|
||||
expect(title).toBe(mockPanel.title);
|
||||
expect(type).toBe(mockPanel.type);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,6 +1,9 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { GlButton } from '@gitlab/ui';
|
||||
import { DASHBOARD_PAGE } from '~/monitoring/router/constants';
|
||||
import { DASHBOARD_PAGE, PANEL_NEW_PAGE } from '~/monitoring/router/constants';
|
||||
import { createStore } from '~/monitoring/stores';
|
||||
import DashboardPanelBuilder from '~/monitoring/components/dashboard_panel_builder.vue';
|
||||
|
||||
import PanelNewPage from '~/monitoring/pages/panel_new_page.vue';
|
||||
|
||||
const dashboard = 'dashboard.yml';
|
||||
|
@ -15,26 +18,37 @@ const GlButtonStub = {
|
|||
};
|
||||
|
||||
describe('monitoring/pages/panel_new_page', () => {
|
||||
let store;
|
||||
let wrapper;
|
||||
let $route;
|
||||
let $router;
|
||||
|
||||
const mountComponent = (propsData = {}, routeParams = { dashboard }) => {
|
||||
$route = {
|
||||
params: routeParams,
|
||||
const mountComponent = (propsData = {}, route) => {
|
||||
$route = route ?? { name: PANEL_NEW_PAGE, params: { dashboard } };
|
||||
$router = {
|
||||
push: jest.fn(),
|
||||
};
|
||||
|
||||
wrapper = shallowMount(PanelNewPage, {
|
||||
propsData,
|
||||
store,
|
||||
stubs: {
|
||||
GlButton: GlButtonStub,
|
||||
},
|
||||
mocks: {
|
||||
$router,
|
||||
$route,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const findBackButton = () => wrapper.find(GlButtonStub);
|
||||
const findPanelBuilder = () => wrapper.find(DashboardPanelBuilder);
|
||||
|
||||
beforeEach(() => {
|
||||
store = createStore();
|
||||
mountComponent();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
|
@ -42,18 +56,43 @@ describe('monitoring/pages/panel_new_page', () => {
|
|||
|
||||
describe('back to dashboard button', () => {
|
||||
it('is rendered', () => {
|
||||
mountComponent();
|
||||
expect(findBackButton().exists()).toBe(true);
|
||||
expect(findBackButton().props('icon')).toBe('go-back');
|
||||
});
|
||||
|
||||
it('links back to the dashboard', () => {
|
||||
const dashboardLocation = {
|
||||
expect(findBackButton().props('to')).toEqual({
|
||||
name: DASHBOARD_PAGE,
|
||||
params: { dashboard },
|
||||
});
|
||||
});
|
||||
|
||||
it('links back to the dashboard while preserving query params', () => {
|
||||
$route = {
|
||||
name: PANEL_NEW_PAGE,
|
||||
params: { dashboard },
|
||||
query: { another: 'param' },
|
||||
};
|
||||
|
||||
expect(findBackButton().props('to')).toEqual(dashboardLocation);
|
||||
mountComponent({}, $route);
|
||||
|
||||
expect(findBackButton().props('to')).toEqual({
|
||||
name: DASHBOARD_PAGE,
|
||||
params: { dashboard },
|
||||
query: { another: 'param' },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('dashboard panel builder', () => {
|
||||
it('is rendered', () => {
|
||||
expect(findPanelBuilder().exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('page routing', () => {
|
||||
it('route is not updated by default', () => {
|
||||
expect($router.push).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,6 +9,7 @@ import { defaultTimeRange } from '~/vue_shared/constants';
|
|||
import * as getters from '~/monitoring/stores/getters';
|
||||
import { ENVIRONMENT_AVAILABLE_STATE } from '~/monitoring/constants';
|
||||
import { backoffMockImplementation } from 'jest/helpers/backoff_helper';
|
||||
import * as requests from '~/monitoring/requests';
|
||||
|
||||
import { createStore } from '~/monitoring/stores';
|
||||
import * as types from '~/monitoring/stores/mutation_types';
|
||||
|
@ -31,6 +32,7 @@ import {
|
|||
duplicateSystemDashboard,
|
||||
updateVariablesAndFetchData,
|
||||
fetchVariableMetricLabelValues,
|
||||
fetchPanelPreview,
|
||||
} from '~/monitoring/stores/actions';
|
||||
import {
|
||||
gqClient,
|
||||
|
@ -1154,4 +1156,56 @@ describe('Monitoring store actions', () => {
|
|||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchPanelPreview', () => {
|
||||
const mockYmlContent = 'mock yml content';
|
||||
|
||||
it('should not commit or dispatch if payload is empty', () => {
|
||||
testAction(fetchPanelPreview, '', state, [], []);
|
||||
});
|
||||
|
||||
it('should store the yml content and panel in the store and fetch corresponding metrics', () => {
|
||||
const mockPanel = {
|
||||
title: 'title',
|
||||
type: 'area-chart',
|
||||
};
|
||||
|
||||
// TODO Use a axios mock instead of spy when backend is implemented
|
||||
// https://gitlab.com/gitlab-org/gitlab/-/issues/228758
|
||||
jest.spyOn(requests, 'getPanelJson').mockResolvedValue(mockPanel);
|
||||
|
||||
testAction(
|
||||
fetchPanelPreview,
|
||||
'mock yml content',
|
||||
state,
|
||||
[
|
||||
{ type: types.REQUEST_PANEL_PREVIEW, payload: mockYmlContent },
|
||||
{ type: types.RECEIVE_PANEL_PREVIEW_SUCCESS, payload: mockPanel },
|
||||
],
|
||||
[
|
||||
{
|
||||
type: 'fetchPanelPreviewMetrics',
|
||||
},
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
it('should commit a failure when backend fails', () => {
|
||||
const mockError = 'error';
|
||||
// TODO Use a axios mock instead of spy when backend is implemented
|
||||
// https://gitlab.com/gitlab-org/gitlab/-/issues/228758
|
||||
jest.spyOn(requests, 'getPanelJson').mockRejectedValue(mockError);
|
||||
|
||||
testAction(
|
||||
fetchPanelPreview,
|
||||
mockYmlContent,
|
||||
state,
|
||||
[
|
||||
{ type: types.REQUEST_PANEL_PREVIEW, payload: mockYmlContent },
|
||||
{ type: types.RECEIVE_PANEL_PREVIEW_FAILURE, payload: mockError },
|
||||
],
|
||||
[],
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -488,4 +488,42 @@ describe('Monitoring mutations', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('REQUEST_PANEL_PREVIEW', () => {
|
||||
it('saves yml content and resets other preview data', () => {
|
||||
const mockYmlContent = 'mock yml content';
|
||||
mutations[types.REQUEST_PANEL_PREVIEW](stateCopy, mockYmlContent);
|
||||
|
||||
expect(stateCopy.panelPreviewIsLoading).toBe(true);
|
||||
expect(stateCopy.panelPreviewYml).toBe(mockYmlContent);
|
||||
expect(stateCopy.panelPreviewGraphData).toBe(null);
|
||||
expect(stateCopy.panelPreviewError).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('RECEIVE_PANEL_PREVIEW_SUCCESS', () => {
|
||||
it('saves graph data', () => {
|
||||
mutations[types.RECEIVE_PANEL_PREVIEW_SUCCESS](stateCopy, {
|
||||
title: 'My Title',
|
||||
type: 'area-chart',
|
||||
});
|
||||
|
||||
expect(stateCopy.panelPreviewIsLoading).toBe(false);
|
||||
expect(stateCopy.panelPreviewGraphData).toMatchObject({
|
||||
title: 'My Title',
|
||||
type: 'area-chart',
|
||||
});
|
||||
expect(stateCopy.panelPreviewError).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('RECEIVE_PANEL_PREVIEW_FAILURE', () => {
|
||||
it('saves graph data', () => {
|
||||
mutations[types.RECEIVE_PANEL_PREVIEW_FAILURE](stateCopy, 'Error!');
|
||||
|
||||
expect(stateCopy.panelPreviewIsLoading).toBe(false);
|
||||
expect(stateCopy.panelPreviewGraphData).toBe(null);
|
||||
expect(stateCopy.panelPreviewError).toBe('Error!');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -21,7 +21,7 @@ const createUnallowedNote = () =>
|
|||
|
||||
describe('DiscussionActions', () => {
|
||||
let wrapper;
|
||||
const createComponentFactory = (shallow = true) => props => {
|
||||
const createComponentFactory = (shallow = true) => (props, options) => {
|
||||
const store = createStore();
|
||||
const mountFn = shallow ? shallowMount : mount;
|
||||
|
||||
|
@ -35,6 +35,11 @@ describe('DiscussionActions', () => {
|
|||
shouldShowJumpToNextDiscussion: true,
|
||||
...props,
|
||||
},
|
||||
provide: {
|
||||
glFeatures: {
|
||||
hideJumpToNextUnresolvedInThreads: options?.hideJumpToNextUnresolvedInThreads,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -96,6 +101,13 @@ describe('DiscussionActions', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('does not render jump to next discussion button if feature flag is enabled', () => {
|
||||
const createComponent = createComponentFactory();
|
||||
createComponent({}, { hideJumpToNextUnresolvedInThreads: true });
|
||||
|
||||
expect(wrapper.find(JumpToNextDiscussionButton).exists()).toBe(false);
|
||||
});
|
||||
|
||||
describe('events handling', () => {
|
||||
const createComponent = createComponentFactory(false);
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe Admin::PropagateIntegrationService do
|
||||
describe '.propagate' do
|
||||
let(:excluded_attributes) { %w[id project_id inherit_from_id instance created_at updated_at title description] }
|
||||
let(:excluded_attributes) { %w[id project_id inherit_from_id instance created_at updated_at default] }
|
||||
let!(:project) { create(:project) }
|
||||
let!(:instance_integration) do
|
||||
JiraService.create!(
|
||||
|
|
|
@ -20,7 +20,7 @@ RSpec.describe Projects::PropagateServiceTemplate do
|
|||
end
|
||||
|
||||
let!(:project) { create(:project) }
|
||||
let(:excluded_attributes) { %w[id project_id template created_at updated_at title description] }
|
||||
let(:excluded_attributes) { %w[id project_id template created_at updated_at default] }
|
||||
|
||||
it 'creates services for projects' do
|
||||
expect(project.pushover_service).to be_nil
|
||||
|
@ -120,7 +120,7 @@ RSpec.describe Projects::PropagateServiceTemplate do
|
|||
|
||||
describe 'external tracker' do
|
||||
it 'updates the project external tracker' do
|
||||
service_template.update!(category: 'issue_tracker', default: false)
|
||||
service_template.update!(category: 'issue_tracker')
|
||||
|
||||
expect { described_class.propagate(service_template) }
|
||||
.to change { project.reload.has_external_issue_tracker }.to(true)
|
||||
|
|
Loading…
Reference in a new issue