Commit 1822133d authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent f237f071babd
# Make sure to update all the similar conditions in other CI config files if you modify these conditions
.if-cache-credentials-schedule: &if-cache-credentials-schedule
if: '$CI_REPO_CACHE_CREDENTIALS && $CI_PIPELINE_SOURCE == "schedule"'
# Builds a cached .tar.gz of the master branch with full history and
# uploads it to Google Cloud Storage. This archive is downloaded by a
# script defined by a CI/CD variable named CI_PRE_CLONE_SCRIPT. This has
......@@ -33,8 +37,6 @@ cache-repo:
- tar cf $TAR_FILENAME .
- gzip $TAR_FILENAME
- gsutil cp $TAR_FILENAME.gz gs://gitlab-ci-git-repo-cache/project-$CI_PROJECT_ID/gitlab-master.tar.gz
only:
variables:
- $CI_REPO_CACHE_CREDENTIALS
refs:
- schedules
rules:
- <<: *if-cache-credentials-schedule
when: on_success
# Make sure to update all the similar conditions in other CI config files if you modify these conditions
.if-canonical-gitlab-tag: &if-canonical-gitlab-tag
if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE =~ /^gitlab-org($|\/)/ && $CI_COMMIT_TAG'
cloud-native-image:
extends: .only:variables-canonical-dot-com
image: ruby:2.6-alpine
dependencies: []
stage: post-test
allow_failure: true
variables:
GIT_DEPTH: "1"
when: manual
script:
- install_gitlab_gem
- CNG_PROJECT_PATH="gitlab-org/build/CNG" BUILD_TRIGGER_TOKEN=$CI_JOB_TOKEN ./scripts/trigger-build cng
only:
refs:
- tags
rules:
- <<: *if-canonical-gitlab-tag
when: manual
......@@ -306,9 +306,9 @@ export default {
</script>
<template>
<form name="eks-cluster-configuration-form">
<h2>
<h4>
{{ s__('ClusterIntegration|Enter the details for your Amazon EKS Kubernetes cluster') }}
</h2>
</h4>
<div class="mb-3" v-html="kubernetesIntegrationHelpText"></div>
<div class="form-group">
<label class="label-bold" for="eks-cluster-name">{{
......
......@@ -83,7 +83,7 @@ export default {
</script>
<template>
<form name="service-credentials-form">
<h2>{{ s__('ClusterIntegration|Authenticate with Amazon Web Services') }}</h2>
<h4>{{ s__('ClusterIntegration|Authenticate with Amazon Web Services') }}</h4>
<p>
{{
s__(
......
......@@ -8,7 +8,6 @@ import { polyfillSticky } from '~/lib/utils/sticky';
import CiHeader from '~/vue_shared/components/header_ci_component.vue';
import Callout from '~/vue_shared/components/callout.vue';
import Icon from '~/vue_shared/components/icon.vue';
import createStore from '../store';
import EmptyState from './empty_state.vue';
import EnvironmentsBlock from './environments_block.vue';
import ErasedBlock from './erased_block.vue';
......@@ -22,7 +21,6 @@ import { isNewJobLogActive } from '../store/utils';
export default {
name: 'JobPageApp',
store: createStore(),
components: {
CiHeader,
Callout,
......@@ -60,27 +58,15 @@ export default {
required: false,
default: null,
},
endpoint: {
type: String,
required: true,
},
terminalPath: {
type: String,
required: false,
default: null,
},
pagePath: {
type: String,
required: true,
},
projectPath: {
type: String,
required: true,
},
logState: {
type: String,
required: true,
},
subscriptionsMoreMinutesUrl: {
type: String,
required: false,
......@@ -161,37 +147,28 @@ export default {
created() {
this.throttled = _.throttle(this.toggleScrollButtons, 100);
this.setJobEndpoint(this.endpoint);
this.setTraceOptions({
logState: this.logState,
pagePath: this.pagePath,
});
this.fetchJob();
this.fetchTrace();
window.addEventListener('resize', this.onResize);
window.addEventListener('scroll', this.updateScroll);
},
mounted() {
this.updateSidebar();
},
destroyed() {
beforeDestroy() {
this.stopPollingTrace();
this.stopPolling();
window.removeEventListener('resize', this.onResize);
window.removeEventListener('scroll', this.updateScroll);
},
methods: {
...mapActions([
'setJobEndpoint',
'setTraceOptions',
'fetchJob',
'fetchJobsForStage',
'hideSidebar',
'showSidebar',
'toggleSidebar',
'fetchTrace',
'scrollBottom',
'scrollTop',
'stopPollingTrace',
'stopPolling',
'toggleScrollButtons',
'toggleScrollAnimation',
]),
......@@ -223,7 +200,7 @@ export default {
<div>
<gl-loading-icon
v-if="isLoading"
:size="2"
size="lg"
class="js-job-loading qa-loading-animation prepend-top-20"
/>
......
import Vue from 'vue';
import JobApp from './components/job_app.vue';
import createStore from './store';
export default () => {
const element = document.getElementById('js-job-vue-app');
const store = createStore();
// Let's start initializing the store (i.e. fetching data) right away
store.dispatch('init', element.dataset);
return new Vue({
el: element,
store,
components: {
JobApp,
},
......
......@@ -14,6 +14,16 @@ import {
scrollUp,
} from '~/lib/utils/scroll_utils';
export const init = ({ dispatch }, { endpoint, logState, pagePath }) => {
dispatch('setJobEndpoint', endpoint);
dispatch('setTraceOptions', {
logState,
pagePath,
});
return Promise.all([dispatch('fetchJob'), dispatch('fetchTrace')]);
};
export const setJobEndpoint = ({ commit }, endpoint) => commit(types.SET_JOB_ENDPOINT, endpoint);
export const setTraceOptions = ({ commit }, options) => commit(types.SET_TRACE_OPTIONS, options);
......@@ -147,7 +157,6 @@ export const toggleScrollisInBottom = ({ commit }, toggle) => {
export const requestTrace = ({ commit }) => commit(types.REQUEST_TRACE);
let traceTimeout;
export const fetchTrace = ({ dispatch, state }) =>
axios
.get(`${state.traceEndpoint}/trace.json`, {
......@@ -157,24 +166,32 @@ export const fetchTrace = ({ dispatch, state }) =>
dispatch('toggleScrollisInBottom', isScrolledToBottom());
dispatch('receiveTraceSuccess', data);
if (!data.complete) {
traceTimeout = setTimeout(() => {
dispatch('fetchTrace');
}, 4000);
} else {
if (data.complete) {
dispatch('stopPollingTrace');
} else if (!state.traceTimeout) {
dispatch('startPollingTrace');
}
})
.catch(() => dispatch('receiveTraceError'));
export const stopPollingTrace = ({ commit }) => {
export const startPollingTrace = ({ dispatch, commit }) => {
const traceTimeout = setTimeout(() => {
commit(types.SET_TRACE_TIMEOUT, 0);
dispatch('fetchTrace');
}, 4000);
commit(types.SET_TRACE_TIMEOUT, traceTimeout);
};
export const stopPollingTrace = ({ state, commit }) => {
clearTimeout(state.traceTimeout);
commit(types.SET_TRACE_TIMEOUT, 0);
commit(types.STOP_POLLING_TRACE);
clearTimeout(traceTimeout);
};
export const receiveTraceSuccess = ({ commit }, log) => commit(types.RECEIVE_TRACE_SUCCESS, log);
export const receiveTraceError = ({ commit }) => {
commit(types.RECEIVE_TRACE_ERROR);
clearTimeout(traceTimeout);
export const receiveTraceError = ({ dispatch }) => {
dispatch('stopPollingTrace');
flash(__('An error occurred while fetching the job log.'));
};
/**
......
......@@ -10,7 +10,6 @@ export const DISABLE_SCROLL_BOTTOM = 'DISABLE_SCROLL_BOTTOM';
export const DISABLE_SCROLL_TOP = 'DISABLE_SCROLL_TOP';
export const ENABLE_SCROLL_BOTTOM = 'ENABLE_SCROLL_BOTTOM';
export const ENABLE_SCROLL_TOP = 'ENABLE_SCROLL_TOP';
// TODO
export const TOGGLE_SCROLL_ANIMATION = 'TOGGLE_SCROLL_ANIMATION';
export const TOGGLE_IS_SCROLL_IN_BOTTOM_BEFORE_UPDATING_TRACE = 'TOGGLE_IS_SCROLL_IN_BOTTOM';
......@@ -20,6 +19,7 @@ export const RECEIVE_JOB_SUCCESS = 'RECEIVE_JOB_SUCCESS';
export const RECEIVE_JOB_ERROR = 'RECEIVE_JOB_ERROR';
export const REQUEST_TRACE = 'REQUEST_TRACE';
export const SET_TRACE_TIMEOUT = 'SET_TRACE_TIMEOUT';
export const STOP_POLLING_TRACE = 'STOP_POLLING_TRACE';
export const RECEIVE_TRACE_SUCCESS = 'RECEIVE_TRACE_SUCCESS';
export const RECEIVE_TRACE_ERROR = 'RECEIVE_TRACE_ERROR';
......
......@@ -53,17 +53,14 @@ export default {
state.isTraceComplete = log.complete || state.isTraceComplete;
},
/**
* Will remove loading animation
*/
[types.STOP_POLLING_TRACE](state) {
state.isTraceComplete = true;
[types.SET_TRACE_TIMEOUT](state, id) {
state.traceTimeout = id;
},
/**
* Will remove loading animation
*/
[types.RECEIVE_TRACE_ERROR](state) {
[types.STOP_POLLING_TRACE](state) {
state.isTraceComplete = true;
},
......
......@@ -22,6 +22,7 @@ export default () => ({
isTraceComplete: false,
traceSize: 0,
isTraceSizeVisible: false,
traceTimeout: 0,
// used as a query parameter to fetch the trace
traceState: null,
......
<script>
import { mapActions, mapState, mapGetters } from 'vuex';
import { GlCard, GlButton, GlLoadingIcon } from '@gitlab/ui';
import Tracking from '~/tracking';
import {
UPDATE_SETTINGS_ERROR_MESSAGE,
UPDATE_SETTINGS_SUCCESS_MESSAGE,
} from '../../shared/constants';
import { mapComputed } from '~/vuex_shared/bindings';
import ExpirationPolicyForm from '../../shared/components/expiration_policy_form.vue';
import ExpirationPolicyFields from '../../shared/components/expiration_policy_fields.vue';
export default {
components: {
ExpirationPolicyForm,
GlCard,
GlButton,
GlLoadingIcon,
ExpirationPolicyFields,
},
mixins: [Tracking.mixin()],
labelsConfig: {
......@@ -22,12 +26,19 @@ export default {
tracking: {
label: 'docker_container_retention_and_expiration_policies',
},
formIsValid: true,
};
},
computed: {
...mapState(['formOptions', 'isLoading']),
...mapGetters({ isEdited: 'getIsEdited' }),
...mapComputed([{ key: 'settings', getter: 'getSettings' }], 'updateSettings'),
isSubmitButtonDisabled() {
return !this.formIsValid || this.isLoading;
},
isCancelButtonDisabled() {
return !this.isEdited || this.isLoading;
},
},
methods: {
...mapActions(['resetSettings', 'saveSettings']),
......@@ -46,12 +57,42 @@ export default {
</script>
<template>
<expiration-policy-form
v-model="settings"
:form-options="formOptions"
:is-loading="isLoading"
:disable-cancel-button="!isEdited"
@submit="submit"
@reset="reset"
/>
<form ref="form-element" @submit.prevent="submit" @reset.prevent="reset">
<gl-card>
<template #header>
{{ s__('ContainerRegistry|Tag expiration policy') }}
</template>
<template #default>
<expiration-policy-fields
v-model="settings"
:form-options="formOptions"
:is-loading="isLoading"
@validated="formIsValid = true"
@invalidated="formIsValid = false"
/>
</template>
<template #footer>
<div class="d-flex justify-content-end">
<gl-button
ref="cancel-button"
type="reset"
class="mr-2 d-block"
:disabled="isCancelButtonDisabled"
>
{{ __('Cancel') }}
</gl-button>
<gl-button
ref="save-button"
type="submit"
:disabled="isSubmitButtonDisabled"
variant="success"
class="d-flex justify-content-center align-items-center js-no-auto-disable"
>
{{ __('Save expiration policy') }}
<gl-loading-icon v-if="isLoading" class="ml-2" />
</gl-button>
</div>
</template>
</gl-card>
</form>
</template>
<script>
import { uniqueId } from 'lodash';
import { GlFormGroup, GlToggle, GlFormSelect, GlFormTextarea } from '@gitlab/ui';
import { s__, __, sprintf } from '~/locale';
import { NAME_REGEX_LENGTH } from '../constants';
import { mapComputedToEvent } from '../utils';
export default {
components: {
GlFormGroup,
GlToggle,
GlFormSelect,
GlFormTextarea,
},
props: {
formOptions: {
type: Object,
required: false,
default: () => ({}),
},
isLoading: {
type: Boolean,
required: false,
default: false,
},
value: {
type: Object,
required: false,
default: () => ({}),
},
labelCols: {
type: [Number, String],
required: false,
default: 3,
},
labelAlign: {
type: String,
required: false,
default: 'right',
},
},
nameRegexPlaceholder: '.*',
selectList: [
{
name: 'expiration-policy-interval',
label: s__('ContainerRegistry|Expiration interval:'),
model: 'older_than',
optionKey: 'olderThan',
},
{
name: 'expiration-policy-schedule',
label: s__('ContainerRegistry|Expiration schedule:'),
model: 'cadence',
optionKey: 'cadence',
},
{
name: 'expiration-policy-latest',
label: s__('ContainerRegistry|Number of tags to retain:'),
model: 'keep_n',
optionKey: 'keepN',
},
],
data() {
return {
uniqueId: uniqueId(),
};
},
computed: {
...mapComputedToEvent(['enabled', 'cadence', 'older_than', 'keep_n', 'name_regex'], 'value'),
policyEnabledText() {
return this.enabled ? __('enabled') : __('disabled');
},
toggleDescriptionText() {
return sprintf(
s__('ContainerRegistry|Docker tag expiration policy is %{toggleStatus}'),
{
toggleStatus: `<strong>${this.policyEnabledText}</strong>`,
},
false,
);
},
regexHelpText() {
return sprintf(
s__(
'ContainerRegistry|Wildcards such as %{codeStart}*-stable%{codeEnd} or %{codeStart}production/*%{codeEnd} are supported. To select all tags, use %{codeStart}.*%{codeEnd}',
),
{
codeStart: '<code>',
codeEnd: '</code>',
},
false,
);
},
nameRegexState() {
return this.name_regex ? this.name_regex.length <= NAME_REGEX_LENGTH : null;
},
fieldsValidity() {
return this.nameRegexState !== false;
},
isFormElementDisabled() {
return !this.enabled || this.isLoading;
},
},
watch: {
fieldsValidity: {
immediate: true,
handler(valid) {
if (valid) {
this.$emit('validated');
} else {
this.$emit('invalidated');
}
},
},
},
methods: {
idGenerator(id) {
return `${id}_${this.uniqueId}`;
},
updateModel(value, key) {
this[key] = value;
},
},
};
</script>
<template>
<div ref="form-elements" class="lh-2">
<gl-form-group
:id="idGenerator('expiration-policy-toggle-group')"
:label-cols="labelCols"
:label-align="labelAlign"
:label-for="idGenerator('expiration-policy-toggle')"
:label="s__('ContainerRegistry|Expiration policy:')"
>
<div class="d-flex align-items-start">
<gl-toggle
:id="idGenerator('expiration-policy-toggle')"
v-model="enabled"
:disabled="isLoading"
/>
<span class="mb-2 ml-1 lh-2" v-html="toggleDescriptionText"></span>
</div>
</gl-form-group>
<gl-form-group
v-for="select in $options.selectList"
:id="idGenerator(`${select.name}-group`)"
:key="select.name"
:label-cols="labelCols"
:label-align="labelAlign"
:label-for="idGenerator(select.name)"
:label="select.label"
>
<gl-form-select
:id="idGenerator(select.name)"
:value="value[select.model]"
:disabled="isFormElementDisabled"
@input="updateModel($event, select.model)"
>
<option
v-for="option in formOptions[select.optionKey]"
:key="option.key"
:value="option.key"
>
{{ option.label }}
</option>
</gl-form-select>
</gl-form-group>
<gl-form-group
:id="idGenerator('expiration-policy-name-matching-group')"
:label-cols="labelCols"
:label-align="labelAlign"
:label-for="idGenerator('expiration-policy-name-matching')"
:label="
s__('ContainerRegistry|Docker tags with names matching this regex pattern will expire:')
"
:state="nameRegexState"
:invalid-feedback="
s__('ContainerRegistry|The value of this input should be less than 255 characters')
"
>
<gl-form-textarea
:id="idGenerator('expiration-policy-name-matching')"
v-model="name_regex"
:placeholder="$options.nameRegexPlaceholder"
:state="nameRegexState"
:disabled="isFormElementDisabled"
trim
/>
<template #description>
<span ref="regex-description" v-html="regexHelpText"></span>
</template>
</gl-form-group>
</div>
</template>
<script>
import { uniqueId } from 'lodash';
import {
GlFormGroup,
GlToggle,
GlFormSelect,