Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
d4194db620
commit
f9ddf689da
|
@ -1,6 +1,6 @@
|
|||
import _ from 'lodash';
|
||||
import Api from '~/api';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { s__ } from '~/locale';
|
||||
import * as types from './mutation_types';
|
||||
|
@ -71,7 +71,9 @@ export const createContextCommits = ({ state }, { commits, forceReload = false }
|
|||
})
|
||||
.catch(() => {
|
||||
if (forceReload) {
|
||||
createFlash(s__('ContextCommits|Failed to create context commits. Please try again.'));
|
||||
createFlash({
|
||||
message: s__('ContextCommits|Failed to create context commits. Please try again.'),
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -111,7 +113,9 @@ export const removeContextCommits = ({ state }, forceReload = false) =>
|
|||
})
|
||||
.catch(() => {
|
||||
if (forceReload) {
|
||||
createFlash(s__('ContextCommits|Failed to delete context commits. Please try again.'));
|
||||
createFlash({
|
||||
message: s__('ContextCommits|Failed to delete context commits. Please try again.'),
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Api from '~/api';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
|
||||
import { s__ } from '~/locale';
|
||||
import * as types from './mutation_types';
|
||||
|
@ -21,5 +21,7 @@ export const receiveStatisticsSuccess = ({ commit }, statistics) =>
|
|||
|
||||
export const receiveStatisticsError = ({ commit }, error) => {
|
||||
commit(types.RECEIVE_STATISTICS_ERROR, error);
|
||||
createFlash(s__('AdminDashboard|Error loading the statistics. Please try again'));
|
||||
createFlash({
|
||||
message: s__('AdminDashboard|Error loading the statistics. Please try again'),
|
||||
});
|
||||
};
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import $ from 'jquery';
|
||||
import initPopover from '~/blob/suggest_gitlab_ci_yml';
|
||||
import initCodeQualityWalkthrough from '~/code_quality_walkthrough';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import { disableButtonIfEmptyField, setCookie } from '~/lib/utils/common_utils';
|
||||
import Tracking from '~/tracking';
|
||||
import BlobFileDropzone from '../blob/blob_file_dropzone';
|
||||
|
@ -84,7 +84,11 @@ export default () => {
|
|||
initPopovers();
|
||||
initCodeQualityWalkthroughStep();
|
||||
})
|
||||
.catch((e) => createFlash(e));
|
||||
.catch((e) =>
|
||||
createFlash({
|
||||
message: e,
|
||||
}),
|
||||
);
|
||||
|
||||
cancelLink.on('click', () => {
|
||||
window.onbeforeunload = null;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import $ from 'jquery';
|
||||
import EditorLite from '~/editor/editor_lite';
|
||||
import { FileTemplateExtension } from '~/editor/extensions/editor_file_template_ext';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { addEditorMarkdownListeners } from '~/lib/utils/text_markdown';
|
||||
import { insertFinalNewline } from '~/lib/utils/text_utility';
|
||||
|
@ -21,7 +21,11 @@ export default class EditBlob {
|
|||
this.editor.use(new MarkdownExtension());
|
||||
addEditorMarkdownListeners(this.editor);
|
||||
})
|
||||
.catch((e) => createFlash(`${BLOB_EDITOR_ERROR}: ${e}`));
|
||||
.catch((e) =>
|
||||
createFlash({
|
||||
message: `${BLOB_EDITOR_ERROR}: ${e}`,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
this.initModePanesAndLinks();
|
||||
|
@ -94,7 +98,11 @@ export default class EditBlob {
|
|||
currentPane.empty().append(data);
|
||||
currentPane.renderGFM();
|
||||
})
|
||||
.catch(() => createFlash(BLOB_PREVIEW_ERROR));
|
||||
.catch(() =>
|
||||
createFlash({
|
||||
message: BLOB_PREVIEW_ERROR,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
this.$toggleButton.show();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Vue from 'vue';
|
||||
import { deprecatedCreateFlash as createFlash } from '../flash';
|
||||
import createFlash from '../flash';
|
||||
import axios from '../lib/utils/axios_utils';
|
||||
import { __ } from '../locale';
|
||||
import DivergenceGraph from './components/divergence_graph.vue';
|
||||
|
@ -51,6 +51,8 @@ export default (endpoint, defaultBranch) => {
|
|||
});
|
||||
})
|
||||
.catch(() =>
|
||||
createFlash(__('Error fetching diverging counts for branches. Please try again.')),
|
||||
createFlash({
|
||||
message: __('Error fetching diverging counts for branches. Please try again.'),
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Api from '~/api';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { __ } from '~/locale';
|
||||
import * as types from './mutation_types';
|
||||
|
@ -48,7 +48,9 @@ export const addVariable = ({ state, dispatch }) => {
|
|||
dispatch('fetchVariables');
|
||||
})
|
||||
.catch((error) => {
|
||||
createFlash(error.response.data[0]);
|
||||
createFlash({
|
||||
message: error.response.data[0],
|
||||
});
|
||||
dispatch('receiveAddVariableError', error);
|
||||
});
|
||||
};
|
||||
|
@ -78,7 +80,9 @@ export const updateVariable = ({ state, dispatch }) => {
|
|||
dispatch('fetchVariables');
|
||||
})
|
||||
.catch((error) => {
|
||||
createFlash(error.response.data[0]);
|
||||
createFlash({
|
||||
message: error.response.data[0],
|
||||
});
|
||||
dispatch('receiveUpdateVariableError', error);
|
||||
});
|
||||
};
|
||||
|
@ -105,7 +109,9 @@ export const fetchVariables = ({ dispatch, state }) => {
|
|||
dispatch('receiveVariablesSuccess', prepareDataForDisplay(data.variables));
|
||||
})
|
||||
.catch(() => {
|
||||
createFlash(__('There was an error fetching the variables.'));
|
||||
createFlash({
|
||||
message: __('There was an error fetching the variables.'),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -133,7 +139,9 @@ export const deleteVariable = ({ dispatch, state }) => {
|
|||
dispatch('fetchVariables');
|
||||
})
|
||||
.catch((error) => {
|
||||
createFlash(error.response.data[0]);
|
||||
createFlash({
|
||||
message: error.response.data[0],
|
||||
});
|
||||
dispatch('receiveDeleteVariableError', error);
|
||||
});
|
||||
};
|
||||
|
@ -154,7 +162,9 @@ export const fetchEnvironments = ({ dispatch, state }) => {
|
|||
dispatch('receiveEnvironmentsSuccess', prepareEnvironments(res.data));
|
||||
})
|
||||
.catch(() => {
|
||||
createFlash(__('There was an error fetching the environments information.'));
|
||||
createFlash({
|
||||
message: __('There was an error fetching the environments information.'),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
|
||||
import { DEFAULT_REGION } from '../constants';
|
||||
|
@ -102,7 +102,9 @@ export const createClusterSuccess = (_, location) => {
|
|||
|
||||
export const createClusterError = ({ commit }, error) => {
|
||||
commit(types.CREATE_CLUSTER_ERROR, error);
|
||||
createFlash(getErrorMessage(error));
|
||||
createFlash({
|
||||
message: getErrorMessage(error),
|
||||
});
|
||||
};
|
||||
|
||||
export const setRegion = ({ commit }, payload) => {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Api from '~/api';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import { __ } from '~/locale';
|
||||
import * as types from './mutation_types';
|
||||
|
||||
|
@ -26,7 +26,9 @@ const receiveFreezePeriod = (store, request) => {
|
|||
dispatch('fetchFreezePeriods');
|
||||
})
|
||||
.catch((error) => {
|
||||
createFlash(__('Error: Unable to create deploy freeze'));
|
||||
createFlash({
|
||||
message: __('Error: Unable to create deploy freeze'),
|
||||
});
|
||||
dispatch('receiveFreezePeriodError', error);
|
||||
});
|
||||
};
|
||||
|
@ -58,7 +60,9 @@ export const fetchFreezePeriods = ({ commit, state }) => {
|
|||
commit(types.RECEIVE_FREEZE_PERIODS_SUCCESS, data);
|
||||
})
|
||||
.catch(() => {
|
||||
createFlash(__('There was an error fetching the deploy freezes.'));
|
||||
createFlash({
|
||||
message: __('There was an error fetching the deploy freezes.'),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import Cookies from 'js-cookie';
|
||||
import Vue from 'vue';
|
||||
import api from '~/api';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import { diffViewerModes } from '~/ide/constants';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { handleLocationHash, historyPushState, scrollToElement } from '~/lib/utils/common_utils';
|
||||
|
@ -240,7 +240,10 @@ export const fetchCoverageFiles = ({ commit, state }) => {
|
|||
coveragePoll.stop();
|
||||
}
|
||||
},
|
||||
errorCallback: () => createFlash(__('Something went wrong on our end. Please try again!')),
|
||||
errorCallback: () =>
|
||||
createFlash({
|
||||
message: __('Something went wrong on our end. Please try again!'),
|
||||
}),
|
||||
});
|
||||
|
||||
coveragePoll.makeRequest();
|
||||
|
@ -504,7 +507,11 @@ export const saveDiffDiscussion = ({ state, dispatch }, { note, formData }) => {
|
|||
.then((discussion) => dispatch('assignDiscussionsToDiff', [discussion]))
|
||||
.then(() => dispatch('updateResolvableDiscussionsCounts', null, { root: true }))
|
||||
.then(() => dispatch('closeDiffFileCommentForm', formData.diffFile.file_hash))
|
||||
.catch(() => createFlash(s__('MergeRequests|Saving the comment failed')));
|
||||
.catch(() =>
|
||||
createFlash({
|
||||
message: s__('MergeRequests|Saving the comment failed'),
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
export const toggleTreeOpen = ({ commit }, path) => {
|
||||
|
@ -595,7 +602,9 @@ export const cacheTreeListWidth = (_, size) => {
|
|||
|
||||
export const receiveFullDiffError = ({ commit }, filePath) => {
|
||||
commit(types.RECEIVE_FULL_DIFF_ERROR, filePath);
|
||||
createFlash(s__('MergeRequest|Error loading full diff. Please try again.'));
|
||||
createFlash({
|
||||
message: s__('MergeRequest|Error loading full diff. Please try again.'),
|
||||
});
|
||||
};
|
||||
|
||||
export const setExpandedDiffLines = ({ commit }, { file, data }) => {
|
||||
|
@ -727,7 +736,9 @@ export const setSuggestPopoverDismissed = ({ commit, state }) =>
|
|||
commit(types.SET_SHOW_SUGGEST_POPOVER);
|
||||
})
|
||||
.catch(() => {
|
||||
createFlash(s__('MergeRequest|Error dismissing suggestion popover. Please try again.'));
|
||||
createFlash({
|
||||
message: s__('MergeRequest|Error dismissing suggestion popover. Please try again.'),
|
||||
});
|
||||
});
|
||||
|
||||
export function changeCurrentCommit({ dispatch, commit, state }, { commitId }) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import { visitUrl } from '~/lib/utils/url_utility';
|
||||
import { __ } from '~/locale';
|
||||
import service from '../services';
|
||||
|
@ -17,7 +17,11 @@ export const updateStatus = ({ commit }, { endpoint, redirectUrl, status }) =>
|
|||
|
||||
return resp.data.result;
|
||||
})
|
||||
.catch(() => createFlash(__('Failed to update issue status')));
|
||||
.catch(() =>
|
||||
createFlash({
|
||||
message: __('Failed to update issue status'),
|
||||
}),
|
||||
);
|
||||
|
||||
export const updateResolveStatus = ({ commit, dispatch }, params) => {
|
||||
commit(types.SET_UPDATING_RESOLVE_STATUS, true);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import Poll from '~/lib/utils/poll';
|
||||
import { __ } from '~/locale';
|
||||
import service from '../../services';
|
||||
|
@ -26,7 +26,9 @@ export function startPollingStacktrace({ commit }, endpoint) {
|
|||
},
|
||||
errorCallback: () => {
|
||||
commit(types.SET_LOADING_STACKTRACE, false);
|
||||
createFlash(__('Failed to load stacktrace.'));
|
||||
createFlash({
|
||||
message: __('Failed to load stacktrace.'),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import Poll from '~/lib/utils/poll';
|
||||
import { __ } from '~/locale';
|
||||
import Service from '../../services';
|
||||
|
@ -33,7 +33,9 @@ export function startPolling({ state, commit, dispatch }) {
|
|||
},
|
||||
errorCallback: () => {
|
||||
commit(types.SET_LOADING, false);
|
||||
createFlash(__('Failed to load errors from Sentry.'));
|
||||
createFlash({
|
||||
message: __('Failed to load errors from Sentry.'),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { refreshCurrentPage } from '~/lib/utils/url_utility';
|
||||
import { __ } from '~/locale';
|
||||
|
@ -46,7 +46,10 @@ export const requestSettings = ({ commit }) => {
|
|||
export const receiveSettingsError = ({ commit }, { response = {} }) => {
|
||||
const message = response.data && response.data.message ? response.data.message : '';
|
||||
|
||||
createFlash(`${__('There was an error saving your changes.')} ${message}`, 'alert');
|
||||
createFlash({
|
||||
message: `${__('There was an error saving your changes.')} ${message}`,
|
||||
type: 'alert',
|
||||
});
|
||||
commit(types.UPDATE_SETTINGS_LOADING, false);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { visitUrl } from '~/lib/utils/url_utility';
|
||||
import { __ } from '~/locale';
|
||||
|
@ -55,7 +55,9 @@ export const receiveFeatureFlagSuccess = ({ commit }, response) =>
|
|||
commit(types.RECEIVE_FEATURE_FLAG_SUCCESS, response);
|
||||
export const receiveFeatureFlagError = ({ commit }) => {
|
||||
commit(types.RECEIVE_FEATURE_FLAG_ERROR);
|
||||
createFlash(__('Something went wrong on our end. Please try again!'));
|
||||
createFlash({
|
||||
message: __('Something went wrong on our end. Please try again!'),
|
||||
});
|
||||
};
|
||||
|
||||
export const toggleActive = ({ commit }, active) => commit(types.TOGGLE_ACTIVE, active);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { __ } from '~/locale';
|
||||
import AjaxFilter from '../droplab/plugins/ajax_filter';
|
||||
import { deprecatedCreateFlash as createFlash } from '../flash';
|
||||
import createFlash from '../flash';
|
||||
import DropdownUtils from './dropdown_utils';
|
||||
import FilteredSearchDropdown from './filtered_search_dropdown';
|
||||
import FilteredSearchTokenizer from './filtered_search_tokenizer';
|
||||
|
@ -27,7 +27,9 @@ export default class DropdownAjaxFilter extends FilteredSearchDropdown {
|
|||
searchValueFunction: this.getSearchInput.bind(this),
|
||||
loadingTemplate: this.loadingTemplate,
|
||||
onError() {
|
||||
createFlash(__('An error occurred fetching the dropdown data.'));
|
||||
createFlash({
|
||||
message: __('An error occurred fetching the dropdown data.'),
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import $ from 'jquery';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { parseQueryStringIntoObject } from '~/lib/utils/common_utils';
|
||||
import { __ } from '~/locale';
|
||||
|
@ -16,7 +16,10 @@ export default class GpgBadges {
|
|||
badges.html('<span class="gl-spinner gl-spinner-orange gl-spinner-sm"></span>');
|
||||
badges.children().attr('aria-label', __('Loading'));
|
||||
|
||||
const displayError = () => createFlash(__('An error occurred while loading commit signatures'));
|
||||
const displayError = () =>
|
||||
createFlash({
|
||||
message: __('An error occurred while loading commit signatures'),
|
||||
});
|
||||
|
||||
const endpoint = tag.data('signaturesPath');
|
||||
if (!endpoint) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { refreshCurrentPage } from '~/lib/utils/url_utility';
|
||||
import { __ } from '~/locale';
|
||||
|
@ -38,5 +38,8 @@ export const receiveGrafanaIntegrationUpdateError = (_, error) => {
|
|||
const { response } = error;
|
||||
const message = response.data && response.data.message ? response.data.message : '';
|
||||
|
||||
createFlash(`${__('There was an error saving your changes.')} ${message}`, 'alert');
|
||||
createFlash({
|
||||
message: `${__('There was an error saving your changes.')} ${message}`,
|
||||
type: 'alert',
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { GlModal, GlButton } from '@gitlab/ui';
|
||||
import { mapActions, mapState, mapGetters } from 'vuex';
|
||||
import { deprecatedCreateFlash as flash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import { __, sprintf, s__ } from '~/locale';
|
||||
import { modalTypes } from '../../constants';
|
||||
import { trimPathComponents, getPathParent } from '../../utils';
|
||||
|
@ -57,16 +57,16 @@ export default {
|
|||
|
||||
if (this.modalType === modalTypes.rename) {
|
||||
if (this.entries[this.entryName] && !this.entries[this.entryName].deleted) {
|
||||
flash(
|
||||
sprintf(s__('The name "%{name}" is already taken in this directory.'), {
|
||||
createFlash({
|
||||
message: sprintf(s__('The name "%{name}" is already taken in this directory.'), {
|
||||
name: this.entryName,
|
||||
}),
|
||||
'alert',
|
||||
document,
|
||||
null,
|
||||
false,
|
||||
true,
|
||||
);
|
||||
type: 'alert',
|
||||
parent: document,
|
||||
actionConfig: null,
|
||||
fadeTransition: false,
|
||||
addBodyClass: true,
|
||||
});
|
||||
} else {
|
||||
let parentPath = this.entryName.split('/');
|
||||
const name = parentPath.pop();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { deprecatedCreateFlash as flash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import { __ } from '~/locale';
|
||||
import { leftSidebarViews, PERMISSION_READ_MR, MAX_MR_FILES_AUTO_OPEN } from '../../constants';
|
||||
import service from '../../services';
|
||||
|
@ -34,14 +34,14 @@ export const getMergeRequestsForBranch = (
|
|||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
flash(
|
||||
__(`Error fetching merge requests for ${branchId}`),
|
||||
'alert',
|
||||
document,
|
||||
null,
|
||||
false,
|
||||
true,
|
||||
);
|
||||
createFlash({
|
||||
message: __(`Error fetching merge requests for ${branchId}`),
|
||||
type: 'alert',
|
||||
parent: document,
|
||||
actionConfig: null,
|
||||
fadeTransition: false,
|
||||
addBodyClass: true,
|
||||
});
|
||||
throw e;
|
||||
});
|
||||
};
|
||||
|
@ -236,7 +236,7 @@ export const openMergeRequest = async (
|
|||
|
||||
await dispatch('openMergeRequestChanges', changes);
|
||||
} catch (e) {
|
||||
flash(__('Error while loading the merge request. Please try again.'));
|
||||
createFlash({ message: __('Error while loading the merge request. Please try again.') });
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { deprecatedCreateFlash as flash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import httpStatus from '~/lib/utils/http_status';
|
||||
import * as terminalService from '../../../../services/terminals';
|
||||
|
@ -26,7 +26,7 @@ export const receiveStartSessionSuccess = ({ commit, dispatch }, data) => {
|
|||
};
|
||||
|
||||
export const receiveStartSessionError = ({ dispatch }) => {
|
||||
flash(messages.UNEXPECTED_ERROR_STARTING);
|
||||
createFlash({ message: messages.UNEXPECTED_ERROR_STARTING });
|
||||
dispatch('killSession');
|
||||
};
|
||||
|
||||
|
@ -59,7 +59,7 @@ export const receiveStopSessionSuccess = ({ dispatch }) => {
|
|||
};
|
||||
|
||||
export const receiveStopSessionError = ({ dispatch }) => {
|
||||
flash(messages.UNEXPECTED_ERROR_STOPPING);
|
||||
createFlash({ message: messages.UNEXPECTED_ERROR_STOPPING });
|
||||
dispatch('killSession');
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { deprecatedCreateFlash as flash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import * as messages from '../messages';
|
||||
import * as types from '../mutation_types';
|
||||
|
@ -42,7 +42,7 @@ export const receiveSessionStatusSuccess = ({ commit, dispatch }, data) => {
|
|||
};
|
||||
|
||||
export const receiveSessionStatusError = ({ dispatch }) => {
|
||||
flash(messages.UNEXPECTED_ERROR_STATUS);
|
||||
createFlash({ message: messages.UNEXPECTED_ERROR_STATUS });
|
||||
dispatch('killSession');
|
||||
};
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ import ImportStatus from '../../components/import_status.vue';
|
|||
import { STATUSES } from '../../constants';
|
||||
import addValidationErrorMutation from '../graphql/mutations/add_validation_error.mutation.graphql';
|
||||
import removeValidationErrorMutation from '../graphql/mutations/remove_validation_error.mutation.graphql';
|
||||
import groupQuery from '../graphql/queries/group.query.graphql';
|
||||
import groupAndProjectQuery from '../graphql/queries/groupAndProject.query.graphql';
|
||||
|
||||
const DEBOUNCE_INTERVAL = 300;
|
||||
|
||||
|
@ -47,21 +47,21 @@ export default {
|
|||
},
|
||||
|
||||
apollo: {
|
||||
existingGroup: {
|
||||
query: groupQuery,
|
||||
existingGroupAndProject: {
|
||||
query: groupAndProjectQuery,
|
||||
debounce: DEBOUNCE_INTERVAL,
|
||||
variables() {
|
||||
return {
|
||||
fullPath: this.fullPath,
|
||||
};
|
||||
},
|
||||
update({ existingGroup }) {
|
||||
update({ existingGroup, existingProject }) {
|
||||
const variables = {
|
||||
field: 'new_name',
|
||||
sourceGroupId: this.group.id,
|
||||
};
|
||||
|
||||
if (!existingGroup) {
|
||||
if (!existingGroup && !existingProject) {
|
||||
this.$apollo.mutate({
|
||||
mutation: removeValidationErrorMutation,
|
||||
variables,
|
||||
|
@ -71,7 +71,7 @@ export default {
|
|||
mutation: addValidationErrorMutation,
|
||||
variables: {
|
||||
...variables,
|
||||
message: s__('BulkImport|Name already exists.'),
|
||||
message: this.$options.i18n.NAME_ALREADY_EXISTS,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -115,6 +115,10 @@ export default {
|
|||
return joinPaths(gon.relative_url_root || '/', this.fullPath);
|
||||
},
|
||||
},
|
||||
|
||||
i18n: {
|
||||
NAME_ALREADY_EXISTS: s__('BulkImport|Name already exists.'),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
query group($fullPath: ID!) {
|
||||
existingGroup: group(fullPath: $fullPath) {
|
||||
id
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
query groupAndProject($fullPath: ID!) {
|
||||
existingGroup: group(fullPath: $fullPath) {
|
||||
id
|
||||
}
|
||||
|
||||
existingProject: project(fullPath: $fullPath) {
|
||||
id
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import Visibility from 'visibilityjs';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
|
||||
import httpStatusCodes from '~/lib/utils/http_status';
|
||||
|
@ -75,19 +75,19 @@ const fetchReposFactory = ({ reposPath = isRequired() }) => ({ state, commit })
|
|||
if (hasRedirectInError(e)) {
|
||||
redirectToUrlInError(e);
|
||||
} else if (tooManyRequests(e)) {
|
||||
createFlash(
|
||||
sprintf(s__('ImportProjects|%{provider} rate limit exceeded. Try again later'), {
|
||||
createFlash({
|
||||
message: sprintf(s__('ImportProjects|%{provider} rate limit exceeded. Try again later'), {
|
||||
provider: capitalizeFirstCharacter(provider),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
commit(types.RECEIVE_REPOS_ERROR);
|
||||
} else {
|
||||
createFlash(
|
||||
sprintf(s__('ImportProjects|Requesting your %{provider} repositories failed'), {
|
||||
createFlash({
|
||||
message: sprintf(s__('ImportProjects|Requesting your %{provider} repositories failed'), {
|
||||
provider,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
commit(types.RECEIVE_REPOS_ERROR);
|
||||
}
|
||||
|
@ -126,7 +126,9 @@ const fetchImportFactory = (importPath = isRequired()) => ({ state, commit, gett
|
|||
)
|
||||
: s__('ImportProjects|Importing the project failed');
|
||||
|
||||
createFlash(flashMessage);
|
||||
createFlash({
|
||||
message: flashMessage,
|
||||
});
|
||||
|
||||
commit(types.RECEIVE_IMPORT_ERROR, repoId);
|
||||
});
|
||||
|
@ -149,7 +151,9 @@ export const fetchJobsFactory = (jobsPath = isRequired()) => ({ state, commit, d
|
|||
if (hasRedirectInError(e)) {
|
||||
redirectToUrlInError(e);
|
||||
} else {
|
||||
createFlash(s__('ImportProjects|Update of imported projects with realtime changes failed'));
|
||||
createFlash({
|
||||
message: s__('ImportProjects|Update of imported projects with realtime changes failed'),
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
@ -175,7 +179,9 @@ const fetchNamespacesFactory = (namespacesPath = isRequired()) => ({ commit }) =
|
|||
commit(types.RECEIVE_NAMESPACES_SUCCESS, convertObjectPropsToCamelCase(data, { deep: true })),
|
||||
)
|
||||
.catch(() => {
|
||||
createFlash(s__('ImportProjects|Requesting namespaces failed'));
|
||||
createFlash({
|
||||
message: s__('ImportProjects|Requesting namespaces failed'),
|
||||
});
|
||||
|
||||
commit(types.RECEIVE_NAMESPACES_ERROR);
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { refreshCurrentPage } from '~/lib/utils/url_utility';
|
||||
import { ERROR_MSG } from './constants';
|
||||
|
@ -22,7 +22,10 @@ export default class IncidentsSettingsService {
|
|||
.catch(({ response }) => {
|
||||
const message = response?.data?.message || '';
|
||||
|
||||
createFlash(`${ERROR_MSG} ${message}`, 'alert');
|
||||
createFlash({
|
||||
message: `${ERROR_MSG} ${message}`,
|
||||
type: 'alert',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import {
|
|||
getBoardSortableDefaultOptions,
|
||||
sortableStart,
|
||||
} from '~/boards/mixins/sortable_default_options';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { s__ } from '~/locale';
|
||||
|
||||
|
@ -15,7 +15,9 @@ const updateIssue = (url, issueList, { move_before_id, move_after_id }) =>
|
|||
group_full_path: issueList.dataset.groupFullPath,
|
||||
})
|
||||
.catch(() => {
|
||||
createFlash(s__("ManualOrdering|Couldn't save the order of the issues"));
|
||||
createFlash({
|
||||
message: s__("ManualOrdering|Couldn't save the order of the issues"),
|
||||
});
|
||||
});
|
||||
|
||||
const initManualOrdering = (draggableSelector = 'li.issue') => {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* eslint-disable func-names, no-underscore-dangle, consistent-return */
|
||||
|
||||
import $ from 'jquery';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import { __ } from '~/locale';
|
||||
import eventHub from '~/vue_merge_request_widget/event_hub';
|
||||
import axios from './lib/utils/axios_utils';
|
||||
|
@ -36,11 +36,11 @@ function MergeRequest(opts) {
|
|||
document.querySelector('#task_status_short').innerText = result.task_status_short;
|
||||
},
|
||||
onError: () => {
|
||||
createFlash(
|
||||
__(
|
||||
createFlash({
|
||||
message: __(
|
||||
'Someone edited this merge request at the same time you did. Please refresh the page to see changes.',
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -93,7 +93,9 @@ MergeRequest.prototype.initMRBtnListeners = function () {
|
|||
})
|
||||
.catch(() => {
|
||||
draftToggle.removeAttribute('disabled');
|
||||
createFlash(__('Something went wrong. Please try again.'));
|
||||
createFlash({
|
||||
message: __('Something went wrong. Please try again.'),
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -169,7 +171,10 @@ MergeRequest.hideCloseButton = function () {
|
|||
|
||||
MergeRequest.toggleDraftStatus = function (title, isReady) {
|
||||
if (isReady) {
|
||||
createFlash(__('The merge request can now be merged.'), 'notice');
|
||||
createFlash({
|
||||
message: __('The merge request can now be merged.'),
|
||||
type: 'notice',
|
||||
});
|
||||
}
|
||||
const titleEl = document.querySelector('.merge-request .detail-page-description .title');
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as Sentry from '@sentry/browser';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { convertToFixedRange } from '~/lib/utils/datetime_range';
|
||||
import { convertObjectPropsToCamelCase } from '../../lib/utils/common_utils';
|
||||
|
@ -134,15 +134,17 @@ export const fetchDashboard = ({ state, commit, dispatch, getters }) => {
|
|||
if (state.showErrorBanner) {
|
||||
if (error.response.data && error.response.data.message) {
|
||||
const { message } = error.response.data;
|
||||
createFlash(
|
||||
sprintf(
|
||||
createFlash({
|
||||
message: sprintf(
|
||||
s__('Metrics|There was an error while retrieving metrics. %{message}'),
|
||||
{ message },
|
||||
false,
|
||||
),
|
||||
);
|
||||
});
|
||||
} else {
|
||||
createFlash(s__('Metrics|There was an error while retrieving metrics'));
|
||||
createFlash({
|
||||
message: s__('Metrics|There was an error while retrieving metrics'),
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -174,7 +176,10 @@ export const fetchDashboardData = ({ state, dispatch, getters }) => {
|
|||
dispatch('fetchDeploymentsData');
|
||||
|
||||
if (!state.timeRange) {
|
||||
createFlash(s__(`Metrics|Invalid time range, please verify.`), 'warning');
|
||||
createFlash({
|
||||
message: s__(`Metrics|Invalid time range, please verify.`),
|
||||
type: 'warning',
|
||||
});
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
|
@ -202,7 +207,10 @@ export const fetchDashboardData = ({ state, dispatch, getters }) => {
|
|||
});
|
||||
})
|
||||
.catch(() => {
|
||||
createFlash(s__(`Metrics|There was an error while retrieving metrics`), 'warning');
|
||||
createFlash({
|
||||
message: s__(`Metrics|There was an error while retrieving metrics`),
|
||||
type: 'warning',
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -254,7 +262,9 @@ export const fetchDeploymentsData = ({ state, dispatch }) => {
|
|||
.then((resp) => resp.data)
|
||||
.then((response) => {
|
||||
if (!response || !response.deployments) {
|
||||
createFlash(s__('Metrics|Unexpected deployment data response from prometheus endpoint'));
|
||||
createFlash({
|
||||
message: s__('Metrics|Unexpected deployment data response from prometheus endpoint'),
|
||||
});
|
||||
}
|
||||
|
||||
dispatch('receiveDeploymentsDataSuccess', response.deployments);
|
||||
|
@ -262,7 +272,9 @@ export const fetchDeploymentsData = ({ state, dispatch }) => {
|
|||
.catch((error) => {
|
||||
Sentry.captureException(error);
|
||||
dispatch('receiveDeploymentsDataFailure');
|
||||
createFlash(s__('Metrics|There was an error getting deployment information.'));
|
||||
createFlash({
|
||||
message: s__('Metrics|There was an error getting deployment information.'),
|
||||
});
|
||||
});
|
||||
};
|
||||
export const receiveDeploymentsDataSuccess = ({ commit }, data) => {
|
||||
|
@ -290,9 +302,11 @@ export const fetchEnvironmentsData = ({ state, dispatch }) => {
|
|||
)
|
||||
.then((environments) => {
|
||||
if (!environments) {
|
||||
createFlash(
|
||||
s__('Metrics|There was an error fetching the environments data, please try again'),
|
||||
);
|
||||
createFlash({
|
||||
message: s__(
|
||||
'Metrics|There was an error fetching the environments data, please try again',
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
dispatch('receiveEnvironmentsDataSuccess', environments);
|
||||
|
@ -300,7 +314,9 @@ export const fetchEnvironmentsData = ({ state, dispatch }) => {
|
|||
.catch((err) => {
|
||||
Sentry.captureException(err);
|
||||
dispatch('receiveEnvironmentsDataFailure');
|
||||
createFlash(s__('Metrics|There was an error getting environments information.'));
|
||||
createFlash({
|
||||
message: s__('Metrics|There was an error getting environments information.'),
|
||||
});
|
||||
});
|
||||
};
|
||||
export const requestEnvironmentsData = ({ commit }) => {
|
||||
|
@ -332,7 +348,9 @@ export const fetchAnnotations = ({ state, dispatch, getters }) => {
|
|||
.then(parseAnnotationsResponse)
|
||||
.then((annotations) => {
|
||||
if (!annotations) {
|
||||
createFlash(s__('Metrics|There was an error fetching annotations. Please try again.'));
|
||||
createFlash({
|
||||
message: s__('Metrics|There was an error fetching annotations. Please try again.'),
|
||||
});
|
||||
}
|
||||
|
||||
dispatch('receiveAnnotationsSuccess', annotations);
|
||||
|
@ -340,7 +358,9 @@ export const fetchAnnotations = ({ state, dispatch, getters }) => {
|
|||
.catch((err) => {
|
||||
Sentry.captureException(err);
|
||||
dispatch('receiveAnnotationsFailure');
|
||||
createFlash(s__('Metrics|There was an error getting annotations information.'));
|
||||
createFlash({
|
||||
message: s__('Metrics|There was an error getting annotations information.'),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -377,9 +397,11 @@ export const fetchDashboardValidationWarnings = ({ state, dispatch, getters }) =
|
|||
.catch((err) => {
|
||||
Sentry.captureException(err);
|
||||
dispatch('receiveDashboardValidationWarningsFailure');
|
||||
createFlash(
|
||||
s__('Metrics|There was an error getting dashboard validation warnings information.'),
|
||||
);
|
||||
createFlash({
|
||||
message: s__(
|
||||
'Metrics|There was an error getting dashboard validation warnings information.',
|
||||
),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -480,11 +502,14 @@ export const fetchVariableMetricLabelValues = ({ state, commit }, { defaultQuery
|
|||
commit(types.UPDATE_VARIABLE_METRIC_LABEL_VALUES, { variable, label, data });
|
||||
})
|
||||
.catch(() => {
|
||||
createFlash(
|
||||
sprintf(s__('Metrics|There was an error getting options for variable "%{name}".'), {
|
||||
name: variable.name,
|
||||
}),
|
||||
);
|
||||
createFlash({
|
||||
message: sprintf(
|
||||
s__('Metrics|There was an error getting options for variable "%{name}".'),
|
||||
{
|
||||
name: variable.name,
|
||||
},
|
||||
),
|
||||
});
|
||||
});
|
||||
optionsRequests.push(optionsRequest);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { mapActions, mapGetters, mapState } from 'vuex';
|
||||
import { getDraftReplyFormData, getDraftFormData } from '~/batch_comments/utils';
|
||||
import { TEXT_DIFF_POSITION_TYPE, IMAGE_DIFF_POSITION_TYPE } from '~/diffs/constants';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import { clearDraft } from '~/lib/utils/autosave';
|
||||
import { s__ } from '~/locale';
|
||||
import { formatLineRange } from '~/notes/components/multiline_comment_utils';
|
||||
|
@ -42,7 +42,9 @@ export default {
|
|||
this.handleClearForm(this.discussion.line_code);
|
||||
})
|
||||
.catch(() => {
|
||||
createFlash(s__('MergeRequests|An error occurred while saving the draft comment.'));
|
||||
createFlash({
|
||||
message: s__('MergeRequests|An error occurred while saving the draft comment.'),
|
||||
});
|
||||
});
|
||||
},
|
||||
addToReview(note) {
|
||||
|
@ -80,7 +82,9 @@ export default {
|
|||
}
|
||||
})
|
||||
.catch(() => {
|
||||
createFlash(s__('MergeRequests|An error occurred while saving the draft comment.'));
|
||||
createFlash({
|
||||
message: s__('MergeRequests|An error occurred while saving the draft comment.'),
|
||||
});
|
||||
});
|
||||
},
|
||||
handleClearForm(lineCode) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { refreshCurrentPage } from '~/lib/utils/url_utility';
|
||||
import { __ } from '~/locale';
|
||||
|
@ -35,5 +35,8 @@ export const receiveSaveChangesError = (_, error) => {
|
|||
const { response = {} } = error;
|
||||
const message = response.data && response.data.message ? response.data.message : '';
|
||||
|
||||
createFlash(`${__('There was an error saving your changes.')} ${message}`, 'alert');
|
||||
createFlash({
|
||||
message: `${__('There was an error saving your changes.')} ${message}`,
|
||||
type: 'alert',
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Api from '~/api';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { DELETE_PACKAGE_ERROR_MESSAGE } from '~/packages/shared/constants';
|
||||
import {
|
||||
|
@ -43,7 +43,9 @@ export const requestPackagesList = ({ dispatch, state }, params = {}) => {
|
|||
dispatch('receivePackagesListSuccess', { data, headers });
|
||||
})
|
||||
.catch(() => {
|
||||
createFlash(FETCH_PACKAGES_LIST_ERROR_MESSAGE);
|
||||
createFlash({
|
||||
message: FETCH_PACKAGES_LIST_ERROR_MESSAGE,
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
dispatch('setLoading', false);
|
||||
|
@ -52,7 +54,9 @@ export const requestPackagesList = ({ dispatch, state }, params = {}) => {
|
|||
|
||||
export const requestDeletePackage = ({ dispatch, state }, { _links }) => {
|
||||
if (!_links || !_links.delete_api_path) {
|
||||
createFlash(DELETE_PACKAGE_ERROR_MESSAGE);
|
||||
createFlash({
|
||||
message: DELETE_PACKAGE_ERROR_MESSAGE,
|
||||
});
|
||||
const error = new Error(MISSING_DELETE_PATH_ERROR);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
@ -65,10 +69,15 @@ export const requestDeletePackage = ({ dispatch, state }, { _links }) => {
|
|||
const page = getNewPaginationPage(currentPage, perPage, total - 1);
|
||||
|
||||
dispatch('requestPackagesList', { page });
|
||||
createFlash(DELETE_PACKAGE_SUCCESS_MESSAGE, 'success');
|
||||
createFlash({
|
||||
message: DELETE_PACKAGE_SUCCESS_MESSAGE,
|
||||
type: 'success',
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
dispatch('setLoading', false);
|
||||
createFlash(DELETE_PACKAGE_ERROR_MESSAGE);
|
||||
createFlash({
|
||||
message: DELETE_PACKAGE_ERROR_MESSAGE,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
|
@ -2,7 +2,7 @@ import emojiRegex from 'emoji-regex';
|
|||
import $ from 'jquery';
|
||||
import GfmAutoComplete from 'ee_else_ce/gfm_auto_complete';
|
||||
import * as Emoji from '~/emoji';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import { __ } from '~/locale';
|
||||
import EmojiMenu from './emoji_menu';
|
||||
|
||||
|
@ -81,4 +81,8 @@ Emoji.initEmojiMap()
|
|||
}
|
||||
});
|
||||
})
|
||||
.catch(() => createFlash(__('Failed to load emoji list.')));
|
||||
.catch(() =>
|
||||
createFlash({
|
||||
message: __('Failed to load emoji list.'),
|
||||
}),
|
||||
);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Visibility from 'visibilityjs';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import { historyPushState, buildUrlWithCurrentLocation } from '~/lib/utils/common_utils';
|
||||
import Poll from '~/lib/utils/poll';
|
||||
import { __ } from '~/locale';
|
||||
|
@ -169,7 +169,11 @@ export default {
|
|||
this.service
|
||||
.postAction(endpoint)
|
||||
.then(() => this.updateTable())
|
||||
.catch(() => createFlash(__('An error occurred while making the request.')));
|
||||
.catch(() =>
|
||||
createFlash({
|
||||
message: __('An error occurred while making the request.'),
|
||||
}),
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -189,9 +193,11 @@ export default {
|
|||
.runMRPipeline(options)
|
||||
.then(() => this.updateTable())
|
||||
.catch(() => {
|
||||
createFlash(
|
||||
__('An error occurred while trying to run a new pipeline for this merge request.'),
|
||||
);
|
||||
createFlash({
|
||||
message: __(
|
||||
'An error occurred while trying to run a new pipeline for this merge request.',
|
||||
),
|
||||
});
|
||||
})
|
||||
.finally(() => this.store.toggleIsRunningPipeline(false));
|
||||
},
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { s__ } from '~/locale';
|
||||
import * as types from './mutation_types';
|
||||
|
@ -12,7 +12,9 @@ export const fetchSummary = ({ state, commit, dispatch }) => {
|
|||
commit(types.SET_SUMMARY, data);
|
||||
})
|
||||
.catch(() => {
|
||||
createFlash(s__('TestReports|There was an error fetching the summary.'));
|
||||
createFlash({
|
||||
message: s__('TestReports|There was an error fetching the summary.'),
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
dispatch('toggleLoading');
|
||||
|
@ -36,7 +38,9 @@ export const fetchTestSuite = ({ state, commit, dispatch }, index) => {
|
|||
.get(state.suiteEndpoint, { params: { build_ids } })
|
||||
.then(({ data }) => commit(types.SET_SUITE, { suite: data, index }))
|
||||
.catch(() => {
|
||||
createFlash(s__('TestReports|There was an error fetching the test suite.'));
|
||||
createFlash({
|
||||
message: s__('TestReports|There was an error fetching the test suite.'),
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
dispatch('toggleLoading');
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { GlSafeHtmlDirective as SafeHtml, GlButton, GlModal, GlModalDirective } from '@gitlab/ui';
|
||||
import { escape } from 'lodash';
|
||||
import { deprecatedCreateFlash as Flash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { s__, sprintf } from '~/locale';
|
||||
|
||||
|
@ -85,15 +85,16 @@ Please update your Git repository remotes as soon as possible.`),
|
|||
return axios
|
||||
.put(this.actionUrl, putData)
|
||||
.then((result) => {
|
||||
Flash(result.data.message, 'notice');
|
||||
createFlash({ message: result.data.message, type: 'notice' });
|
||||
this.username = username;
|
||||
this.isRequestPending = false;
|
||||
})
|
||||
.catch((error) => {
|
||||
Flash(
|
||||
error?.response?.data?.message ||
|
||||
createFlash({
|
||||
message:
|
||||
error?.response?.data?.message ||
|
||||
s__('Profiles|An error occurred while updating your username, please try again.'),
|
||||
);
|
||||
});
|
||||
this.isRequestPending = false;
|
||||
throw error;
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as Sentry from '@sentry/browser';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { joinPaths } from '~/lib/utils/url_utility';
|
||||
import { __ } from '~/locale';
|
||||
|
@ -13,7 +13,9 @@ export default {
|
|||
commit(types.COMMITS_AUTHORS, authors);
|
||||
},
|
||||
receiveAuthorsError() {
|
||||
createFlash(__('An error occurred fetching the project authors.'));
|
||||
createFlash({
|
||||
message: __('An error occurred fetching the project authors.'),
|
||||
});
|
||||
},
|
||||
fetchAuthors({ dispatch, state }, author = null) {
|
||||
const { projectId } = state;
|
||||
|
|
|
@ -23,7 +23,7 @@ Your caret can stop touching a `rawReference` can happen in a variety of ways:
|
|||
and hide the `AddIssuableForm` area.
|
||||
|
||||
*/
|
||||
import { deprecatedCreateFlash as Flash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import { __ } from '~/locale';
|
||||
import {
|
||||
relatedIssuesRemoveErrorMap,
|
||||
|
@ -122,11 +122,11 @@ export default {
|
|||
})
|
||||
.catch((res) => {
|
||||
if (res && res.status !== 404) {
|
||||
Flash(relatedIssuesRemoveErrorMap[this.issuableType]);
|
||||
createFlash({ message: relatedIssuesRemoveErrorMap[this.issuableType] });
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Flash(pathIndeterminateErrorMap[this.issuableType]);
|
||||
createFlash({ message: pathIndeterminateErrorMap[this.issuableType] });
|
||||
}
|
||||
},
|
||||
onToggleAddRelatedIssuesForm() {
|
||||
|
@ -155,7 +155,7 @@ export default {
|
|||
if (response && response.data && response.data.message) {
|
||||
errorMessage = response.data.message;
|
||||
}
|
||||
Flash(errorMessage);
|
||||
createFlash({ message: errorMessage });
|
||||
})
|
||||
.finally(() => {
|
||||
this.isSubmitting = false;
|
||||
|
@ -176,7 +176,7 @@ export default {
|
|||
})
|
||||
.catch(() => {
|
||||
this.store.setRelatedIssues([]);
|
||||
Flash(__('An error occurred while fetching issues.'));
|
||||
createFlash({ message: __('An error occurred while fetching issues.') });
|
||||
})
|
||||
.finally(() => {
|
||||
this.isFetching = false;
|
||||
|
@ -197,7 +197,7 @@ export default {
|
|||
}
|
||||
})
|
||||
.catch(() => {
|
||||
Flash(__('An error occurred while reordering issues.'));
|
||||
createFlash({ message: __('An error occurred while reordering issues.') });
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { normalizeHeaders } from '~/lib/utils/common_utils';
|
||||
import { s__ } from '~/locale';
|
||||
|
@ -29,6 +29,8 @@ export const fetchMergeRequests = ({ state, dispatch }) => {
|
|||
})
|
||||
.catch(() => {
|
||||
dispatch('receiveDataError');
|
||||
createFlash(s__('Something went wrong while fetching related merge requests.'));
|
||||
createFlash({
|
||||
message: s__('Something went wrong while fetching related merge requests.'),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import { redirectTo } from '~/lib/utils/url_utility';
|
||||
import { s__ } from '~/locale';
|
||||
import createReleaseMutation from '~/releases/graphql/mutations/create_release.mutation.graphql';
|
||||
|
@ -39,7 +39,9 @@ export const fetchRelease = async ({ commit, state }) => {
|
|||
commit(types.RECEIVE_RELEASE_SUCCESS, release);
|
||||
} catch (error) {
|
||||
commit(types.RECEIVE_RELEASE_ERROR, error);
|
||||
createFlash(s__('Release|Something went wrong while getting the release details.'));
|
||||
createFlash({
|
||||
message: s__('Release|Something went wrong while getting the release details.'),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -124,7 +126,9 @@ export const createRelease = async ({ commit, dispatch, state, getters }) => {
|
|||
dispatch('receiveSaveReleaseSuccess', response.data.releaseCreate.release.links.selfUrl);
|
||||
} catch (error) {
|
||||
commit(types.RECEIVE_SAVE_RELEASE_ERROR, error);
|
||||
createFlash(s__('Release|Something went wrong while creating a new release.'));
|
||||
createFlash({
|
||||
message: s__('Release|Something went wrong while creating a new release.'),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -214,6 +218,8 @@ export const updateRelease = async ({ commit, dispatch, state, getters }) => {
|
|||
dispatch('receiveSaveReleaseSuccess', state.release._links.self);
|
||||
} catch (error) {
|
||||
commit(types.RECEIVE_SAVE_RELEASE_ERROR, error);
|
||||
createFlash(s__('Release|Something went wrong while saving the release details.'));
|
||||
createFlash({
|
||||
message: s__('Release|Something went wrong while saving the release details.'),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import { __ } from '~/locale';
|
||||
import { PAGE_SIZE } from '~/releases/constants';
|
||||
import allReleasesQuery from '~/releases/graphql/queries/all_releases.query.graphql';
|
||||
|
@ -57,7 +57,9 @@ export const fetchReleases = ({ dispatch, commit, state }, { before, after }) =>
|
|||
|
||||
export const receiveReleasesError = ({ commit }) => {
|
||||
commit(types.RECEIVE_RELEASES_ERROR);
|
||||
createFlash(__('An error occurred while fetching the releases. Please try again.'));
|
||||
createFlash({
|
||||
message: __('An error occurred while fetching the releases. Please try again.'),
|
||||
});
|
||||
};
|
||||
|
||||
export const setSorting = ({ commit }, data) => commit(types.SET_SORTING, data);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { backOff } from '~/lib/utils/common_utils';
|
||||
import statusCodes from '~/lib/utils/http_status';
|
||||
|
@ -59,7 +59,9 @@ export const fetchFunctions = ({ dispatch }, { functionsPath }) => {
|
|||
.then((data) => {
|
||||
if (data === TIMEOUT) {
|
||||
dispatch('receiveFunctionsTimeout');
|
||||
createFlash(__('Loading functions timed out. Please reload the page to try again.'));
|
||||
createFlash({
|
||||
message: __('Loading functions timed out. Please reload the page to try again.'),
|
||||
});
|
||||
} else if (data.functions !== null && data.functions.length) {
|
||||
dispatch('receiveFunctionsSuccess', data);
|
||||
} else {
|
||||
|
@ -68,7 +70,9 @@ export const fetchFunctions = ({ dispatch }, { functionsPath }) => {
|
|||
})
|
||||
.catch((error) => {
|
||||
dispatch('receiveFunctionsError', error);
|
||||
createFlash(error);
|
||||
createFlash({
|
||||
message: error,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -120,6 +124,8 @@ export const fetchMetrics = ({ dispatch }, { metricsPath, hasPrometheus }) => {
|
|||
})
|
||||
.catch((error) => {
|
||||
dispatch('receiveMetricsError', error);
|
||||
createFlash(error);
|
||||
createFlash({
|
||||
message: error,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import $ from 'jquery';
|
||||
import { spriteIcon } from '~/lib/utils/common_utils';
|
||||
import FilesCommentButton from './files_comment_button';
|
||||
import { deprecatedCreateFlash as createFlash } from './flash';
|
||||
import createFlash from './flash';
|
||||
import initImageDiffHelper from './image_diff/helpers/init_image_diff';
|
||||
import axios from './lib/utils/axios_utils';
|
||||
import { __ } from './locale';
|
||||
|
@ -95,7 +95,9 @@ export default class SingleFileDiff {
|
|||
if (cb) cb();
|
||||
})
|
||||
.catch(() => {
|
||||
createFlash(__('An error occurred while retrieving diff'));
|
||||
createFlash({
|
||||
message: __('An error occurred while retrieving diff'),
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { GlLoadingIcon } from '@gitlab/ui';
|
||||
import BlobHeaderEdit from '~/blob/components/blob_edit_header.vue';
|
||||
import { deprecatedCreateFlash as Flash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { getBaseURL, joinPaths } from '~/lib/utils/url_utility';
|
||||
import { sprintf } from '~/locale';
|
||||
|
@ -63,7 +63,7 @@ export default {
|
|||
.catch((e) => this.flashAPIFailure(e));
|
||||
},
|
||||
flashAPIFailure(err) {
|
||||
Flash(sprintf(SNIPPET_BLOB_CONTENT_FETCH_ERROR, { err }));
|
||||
createFlash({ message: sprintf(SNIPPET_BLOB_CONTENT_FETCH_ERROR, { err }) });
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Api from '~/api';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { __ } from '~/locale';
|
||||
import * as types from './mutation_types';
|
||||
|
@ -24,7 +24,9 @@ export function fetchBranches({ commit, state }, search = '') {
|
|||
.catch(({ response }) => {
|
||||
const { status } = response;
|
||||
commit(types.RECEIVE_BRANCHES_ERROR, status);
|
||||
createFlash(__('Failed to load branches. Please try again.'));
|
||||
createFlash({
|
||||
message: __('Failed to load branches. Please try again.'),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -41,7 +43,9 @@ export const fetchMilestones = ({ commit, state }, search_title = '') => {
|
|||
.catch(({ response }) => {
|
||||
const { status } = response;
|
||||
commit(types.RECEIVE_MILESTONES_ERROR, status);
|
||||
createFlash(__('Failed to load milestones. Please try again.'));
|
||||
createFlash({
|
||||
message: __('Failed to load milestones. Please try again.'),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -57,7 +61,9 @@ export const fetchLabels = ({ commit, state }, search = '') => {
|
|||
.catch(({ response }) => {
|
||||
const { status } = response;
|
||||
commit(types.RECEIVE_LABELS_ERROR, status);
|
||||
createFlash(__('Failed to load labels. Please try again.'));
|
||||
createFlash({
|
||||
message: __('Failed to load labels. Please try again.'),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -80,7 +86,9 @@ function fetchUser(options = {}) {
|
|||
.catch(({ response }) => {
|
||||
const { status } = response;
|
||||
commit(`RECEIVE_${action}_ERROR`, status);
|
||||
createFlash(errorMessage);
|
||||
createFlash({
|
||||
message: errorMessage,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/331695
|
|||
milestone: '14.0'
|
||||
type: development
|
||||
group: group::continuous integration
|
||||
default_enabled: false
|
||||
default_enabled: true
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: redirect_to_latest_template_jobs_browser_performance_testing
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63144
|
||||
rollout_issue_url:
|
||||
milestone: '14.0'
|
||||
type: development
|
||||
group: group::pipeline authoring
|
||||
default_enabled: false
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: redirect_to_latest_template_jobs_deploy
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63144
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/332660
|
||||
milestone: '14.0'
|
||||
type: development
|
||||
group: group::pipeline authoring
|
||||
default_enabled: false
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: redirect_to_latest_template_security_api_fuzzing
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63144
|
||||
rollout_issue_url:
|
||||
milestone: '14.0'
|
||||
type: development
|
||||
group: group::pipeline authoring
|
||||
default_enabled: false
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: redirect_to_latest_template_security_dast
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63144
|
||||
rollout_issue_url:
|
||||
milestone: '14.0'
|
||||
type: development
|
||||
group: group::pipeline authoring
|
||||
default_enabled: false
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: redirect_to_latest_template_terraform
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63144
|
||||
rollout_issue_url:
|
||||
milestone: '14.0'
|
||||
type: development
|
||||
group: group::pipeline authoring
|
||||
default_enabled: false
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: redirect_to_latest_template_verify_browser_performance
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63144
|
||||
rollout_issue_url:
|
||||
milestone: '14.0'
|
||||
type: development
|
||||
group: group::pipeline authoring
|
||||
default_enabled: false
|
|
@ -42,7 +42,7 @@ There should be no more than one Sentinel on the same machine though.
|
|||
|
||||
You also need to take into consideration the underlying network topology,
|
||||
making sure you have redundant connectivity between Redis / Sentinel and
|
||||
GitLab instances, otherwise the networks will become a single point of
|
||||
GitLab instances, otherwise the networks become a single point of
|
||||
failure.
|
||||
|
||||
Running Redis in a scaled environment requires a few things:
|
||||
|
@ -73,7 +73,7 @@ whole cluster down, invalidating the failover effort.
|
|||
|
||||
## Recommended setup
|
||||
|
||||
For a minimal setup, you will install the Omnibus GitLab package in `3`
|
||||
For a minimal setup, you need to install the Omnibus GitLab package in `3`
|
||||
**independent** machines, both with **Redis** and **Sentinel**:
|
||||
|
||||
- Redis Primary + Sentinel
|
||||
|
@ -84,7 +84,7 @@ If you are not sure or don't understand why and where the amount of nodes come
|
|||
from, read [Redis setup overview](#redis-setup-overview) and
|
||||
[Sentinel setup overview](#sentinel-setup-overview).
|
||||
|
||||
For a recommended setup that can resist more failures, you will install
|
||||
For a recommended setup that can resist more failures, you need to install
|
||||
the Omnibus GitLab package in `5` **independent** machines, both with
|
||||
**Redis** and **Sentinel**:
|
||||
|
||||
|
@ -99,9 +99,9 @@ the Omnibus GitLab package in `5` **independent** machines, both with
|
|||
You must have at least `3` Redis servers: `1` primary, `2` Replicas, and they
|
||||
need to each be on independent machines (see explanation above).
|
||||
|
||||
You can have additional Redis nodes, that will help survive a situation
|
||||
You can have additional Redis nodes, that helps to survive a situation
|
||||
where more nodes goes down. Whenever there is only `2` nodes online, a failover
|
||||
will not be initiated.
|
||||
is not initiated.
|
||||
|
||||
As an example, if you have `6` Redis nodes, a maximum of `3` can be
|
||||
simultaneously down.
|
||||
|
@ -117,7 +117,7 @@ in a failover situation, any **Replica** can be promoted as the new **Primary**
|
|||
the Sentinel servers.
|
||||
|
||||
The replication requires authentication, so you need to define a password to
|
||||
protect all Redis nodes and the Sentinels. They will all share the same
|
||||
protect all Redis nodes and the Sentinels. All of them share the same
|
||||
password, and all instances must be able to talk to
|
||||
each other over the network.
|
||||
|
||||
|
@ -130,7 +130,7 @@ of Sentinels agreeing a node is down) to be able to start a failover.
|
|||
|
||||
Whenever the **quorum** is met, the **majority** of all known Sentinel nodes
|
||||
need to be available and reachable, so that they can elect the Sentinel **leader**
|
||||
who will take all the decisions to restore the service availability by:
|
||||
who takes all the decisions to restore the service availability by:
|
||||
|
||||
- Promoting a new **Primary**
|
||||
- Reconfiguring the other **Replicas** and make them point to the new **Primary**
|
||||
|
@ -150,7 +150,7 @@ consensus algorithm to be effective in the case of a failure.
|
|||
|
||||
In a `3` nodes topology, you can only afford `1` Sentinel node going down.
|
||||
Whenever the **majority** of the Sentinels goes down, the network partition
|
||||
protection prevents destructive actions and a failover **will not be started**.
|
||||
protection prevents destructive actions and a failover **is not started**.
|
||||
|
||||
Here are some examples:
|
||||
|
||||
|
@ -159,11 +159,11 @@ Here are some examples:
|
|||
|
||||
The **Leader** election can sometimes fail the voting round when **consensus**
|
||||
is not achieved (see the odd number of nodes requirement above). In that case,
|
||||
a new attempt will be made after the amount of time defined in
|
||||
a new attempt is made after the amount of time defined in
|
||||
`sentinel['failover_timeout']` (in milliseconds).
|
||||
|
||||
NOTE:
|
||||
We will see where `sentinel['failover_timeout']` is defined later.
|
||||
We can see where `sentinel['failover_timeout']` is defined later.
|
||||
|
||||
The `failover_timeout` variable has a lot of different use cases. According to
|
||||
the official documentation:
|
||||
|
@ -183,7 +183,7 @@ the official documentation:
|
|||
|
||||
- The maximum time a failover in progress waits for all the replicas to be
|
||||
reconfigured as replicas of the new primary. However even after this time
|
||||
the replicas will be reconfigured by the Sentinels anyway, but not with
|
||||
the replicas are reconfigured by the Sentinels anyway, but not with
|
||||
the exact parallel-syncs progression as specified.
|
||||
|
||||
## Configuring Redis
|
||||
|
@ -195,7 +195,7 @@ If you already have Redis installed and running, read how to
|
|||
[switch from a single-machine installation](#switching-from-an-existing-single-machine-installation).
|
||||
|
||||
NOTE:
|
||||
Redis nodes (both primary and replica) will need the same password defined in
|
||||
Redis nodes (both primary and replica) need the same password defined in
|
||||
`redis['password']`. At any time during a failover the Sentinels can
|
||||
reconfigure a node and change its status from primary to replica and vice versa.
|
||||
|
||||
|
@ -218,14 +218,14 @@ The requirements for a Redis setup are the following:
|
|||
|
||||
### Switching from an existing single-machine installation
|
||||
|
||||
If you already have a single-machine GitLab install running, you will need to
|
||||
If you already have a single-machine GitLab install running, you need to
|
||||
replicate from this machine first, before de-activating the Redis instance
|
||||
inside it.
|
||||
|
||||
Your single-machine install will be the initial **Primary**, and the `3` others
|
||||
Your single-machine install is the initial **Primary**, and the `3` others
|
||||
should be configured as **Replica** pointing to this machine.
|
||||
|
||||
After replication catches up, you will need to stop services in the
|
||||
After replication catches up, you need to stop services in the
|
||||
single-machine install, to rotate the **Primary** to one of the new nodes.
|
||||
|
||||
Make the required changes in configuration and restart the new nodes again.
|
||||
|
@ -259,7 +259,7 @@ If you fail to replicate first, you may loose data (unprocessed background jobs)
|
|||
# sure you add extra firewall rules to prevent unauthorized access.
|
||||
redis['bind'] = '10.0.0.1'
|
||||
|
||||
# Define a port so Redis can listen for TCP requests which will allow other
|
||||
# Define a port so Redis can listen for TCP requests which allows other
|
||||
# machines to connect to it.
|
||||
redis['port'] = 6379
|
||||
|
||||
|
@ -303,7 +303,7 @@ Read more about [roles](https://docs.gitlab.com/omnibus/roles/).
|
|||
# sure you add extra firewall rules to prevent unauthorized access.
|
||||
redis['bind'] = '10.0.0.2'
|
||||
|
||||
# Define a port so Redis can listen for TCP requests which will allow other
|
||||
# Define a port so Redis can listen for TCP requests which allows other
|
||||
# machines to connect to it.
|
||||
redis['port'] = 6379
|
||||
|
||||
|
@ -333,8 +333,8 @@ You can specify multiple roles like sentinel and Redis as:
|
|||
Read more about [roles](https://docs.gitlab.com/omnibus/roles/).
|
||||
|
||||
These values don't have to be changed again in `/etc/gitlab/gitlab.rb` after
|
||||
a failover, as the nodes will be managed by the Sentinels, and even after a
|
||||
`gitlab-ctl reconfigure`, they will get their configuration restored by
|
||||
a failover, as the nodes are managed by the Sentinels, and even after a
|
||||
`gitlab-ctl reconfigure`, they get their configuration restored by
|
||||
the same Sentinels.
|
||||
|
||||
### Step 3. Configuring the Redis Sentinel instances
|
||||
|
@ -342,7 +342,7 @@ the same Sentinels.
|
|||
NOTE:
|
||||
If you are using an external Redis Sentinel instance, be sure
|
||||
to exclude the `requirepass` parameter from the Sentinel
|
||||
configuration. This parameter will cause clients to report `NOAUTH
|
||||
configuration. This parameter causes clients to report `NOAUTH
|
||||
Authentication required.`. [Redis Sentinel 3.2.x does not support
|
||||
password authentication](https://github.com/antirez/redis/issues/3279).
|
||||
|
||||
|
@ -362,8 +362,8 @@ multiple machines with the Sentinel daemon.
|
|||
|
||||
---
|
||||
|
||||
1. SSH into the server that will host Redis Sentinel.
|
||||
1. **You can omit this step if the Sentinels will be hosted in the same node as
|
||||
1. SSH into the server that hosts Redis Sentinel.
|
||||
1. **You can omit this step if the Sentinels is hosted in the same node as
|
||||
the other Redis instances.**
|
||||
|
||||
[Download/install](https://about.gitlab.com/install/) the
|
||||
|
@ -389,7 +389,7 @@ multiple machines with the Sentinel daemon.
|
|||
# The IP of the primary Redis node.
|
||||
redis['master_ip'] = '10.0.0.1'
|
||||
|
||||
# Define a port so Redis can listen for TCP requests which will allow other
|
||||
# Define a port so Redis can listen for TCP requests which allows other
|
||||
# machines to connect to it.
|
||||
redis['port'] = 6379
|
||||
|
||||
|
@ -437,7 +437,7 @@ multiple machines with the Sentinel daemon.
|
|||
##
|
||||
## - The maximum time a failover in progress waits for all the replica to be
|
||||
## reconfigured as replicas of the new primary. However even after this time
|
||||
## the replicas will be reconfigured by the Sentinels anyway, but not with
|
||||
## the replicas are reconfigured by the Sentinels anyway, but not with
|
||||
## the exact parallel-syncs progression as specified.
|
||||
# sentinel['failover_timeout'] = 60000
|
||||
```
|
||||
|
@ -511,7 +511,7 @@ If you enable Monitoring, it must be enabled on **all** Redis servers.
|
|||
retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z),
|
||||
}
|
||||
|
||||
# Set the network addresses that the exporters will listen on
|
||||
# Set the network addresses that the exporters listen on
|
||||
node_exporter['listen_address'] = '0.0.0.0:9100'
|
||||
redis_exporter['listen_address'] = '0.0.0.0:9121'
|
||||
```
|
||||
|
@ -528,7 +528,7 @@ In a real world usage, you would also set up firewall rules to prevent
|
|||
unauthorized access from other machines and block traffic from the
|
||||
outside (Internet).
|
||||
|
||||
We will use the same `3` nodes with **Redis** + **Sentinel** topology
|
||||
We use the same `3` nodes with **Redis** + **Sentinel** topology
|
||||
discussed in [Redis setup overview](#redis-setup-overview) and
|
||||
[Sentinel setup overview](#sentinel-setup-overview) documentation.
|
||||
|
||||
|
@ -540,11 +540,11 @@ Here is a list and description of each **machine** and the assigned **IP**:
|
|||
- `10.0.0.4`: GitLab application
|
||||
|
||||
Please note that after the initial configuration, if a failover is initiated
|
||||
by the Sentinel nodes, the Redis nodes will be reconfigured and the **Primary**
|
||||
will change permanently (including in `redis.conf`) from one node to the other,
|
||||
by the Sentinel nodes, the Redis nodes are reconfigured and the **Primary**
|
||||
changes permanently (including in `redis.conf`) from one node to the other,
|
||||
until a new failover is initiated again.
|
||||
|
||||
The same thing will happen with `sentinel.conf` that will be overridden after the
|
||||
The same thing happens with `sentinel.conf` that is overridden after the
|
||||
initial execution, after any new sentinel node starts watching the **Primary**,
|
||||
or a failover promotes a different **Primary** node.
|
||||
|
||||
|
@ -691,7 +691,7 @@ To make this work with Sentinel:
|
|||
```
|
||||
|
||||
NOTE:
|
||||
For each persistence class, GitLab will default to using the
|
||||
For each persistence class, GitLab defaults to using the
|
||||
configuration specified in `gitlab_rails['redis_sentinels']` unless
|
||||
overridden by the previously described settings.
|
||||
|
||||
|
@ -726,7 +726,7 @@ redis_replica_role['enable'] = true # enable only one of them
|
|||
|
||||
# When Redis primary or Replica role are enabled, the following services are
|
||||
# enabled/disabled. Note that if Redis and Sentinel roles are combined, both
|
||||
# services will be enabled.
|
||||
# services are enabled.
|
||||
|
||||
# The following services are disabled
|
||||
sentinel['enable'] = false
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
---
|
||||
stage: Release
|
||||
group: Release
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
type: concepts, howto
|
||||
---
|
||||
|
||||
# Group-level protected environments API **(PREMIUM)**
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/215888) in [GitLab Premium](https://about.gitlab.com/pricing/) 14.0.
|
||||
> - [Deployed behind a feature flag](../user/feature_flags.md), disabled by default.
|
||||
> - Disabled on GitLab.com.
|
||||
> - Not recommended for production use.
|
||||
> - To use in GitLab self-managed instances, ask a GitLab administrator to [enable it](../ci/environments/protected_environments.md#enable-or-disable-group-level-protected-environments). **(FREE SELF)**
|
||||
|
||||
This in-development feature might not be available for your use. There can be
|
||||
[risks when enabling features still in development](../user/feature_flags.md#risks-when-enabling-features-still-in-development).
|
||||
Refer to this feature's version history for more details.
|
||||
|
||||
Read more about [group-level protected environments](../ci/environments/protected_environments.md#group-level-protected-environments),
|
||||
|
||||
## Valid access levels
|
||||
|
||||
The access levels are defined in the `ProtectedEnvironment::DeployAccessLevel::ALLOWED_ACCESS_LEVELS` method.
|
||||
Currently, these levels are recognized:
|
||||
|
||||
```plaintext
|
||||
30 => Developer access
|
||||
40 => Maintainer access
|
||||
60 => Admin access
|
||||
```
|
||||
|
||||
## List group-level protected environments
|
||||
|
||||
Gets a list of protected environments from a group.
|
||||
|
||||
```shell
|
||||
GET /groups/:id/protected_environments
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) maintained by the authenticated user. |
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/5/protected_environments/"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"name":"production",
|
||||
"deploy_access_levels":[
|
||||
{
|
||||
"access_level":40,
|
||||
"access_level_description":"Maintainers",
|
||||
"user_id":null,
|
||||
"group_id":null
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Get a single protected environment
|
||||
|
||||
Gets a single protected environment.
|
||||
|
||||
```shell
|
||||
GET /groups/:id/protected_environments/:name
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) maintained by the authenticated user. |
|
||||
| `name` | string | yes | The deployment tier of the protected environment. One of `production`, `staging`, `testing`, `development`, or `other`. Read more about [deployment tiers](../ci/environments/index.md#deployment-tier-of-environments).|
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/5/protected_environments/production"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
{
|
||||
"name":"production",
|
||||
"deploy_access_levels":[
|
||||
{
|
||||
"access_level":40,
|
||||
"access_level_description":"Maintainers",
|
||||
"user_id":null,
|
||||
"group_id":null
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Protect an environment
|
||||
|
||||
Protects a single environment.
|
||||
|
||||
```shell
|
||||
POST /groups/:id/protected_environments
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) maintained by the authenticated user. |
|
||||
| `name` | string | yes | The deployment tier of the protected environment. One of `production`, `staging`, `testing`, `development`, or `other`. Read more about [deployment tiers](../ci/environments/index.md#deployment-tier-of-environments).|
|
||||
| `deploy_access_levels` | array | yes | Array of access levels allowed to deploy, with each described by a hash. One of `user_id`, `group_id` or `access_level`. They take the form of `{user_id: integer}`, `{group_id: integer}` or `{access_level: integer}` respectively. |
|
||||
|
||||
The assignable `user_id` are the users who belong to the given group with the Maintainer role (or above).
|
||||
The assignable `group_id` are the sub-groups under the given group.
|
||||
|
||||
```shell
|
||||
curl --header 'Content-Type: application/json' --request POST --data '{"name": "production", "deploy_access_levels": [{"group_id": 9899826}]}' --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/22034114/protected_environments"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
{
|
||||
"name":"production",
|
||||
"deploy_access_levels":[
|
||||
{
|
||||
"access_level":40,
|
||||
"access_level_description":"protected-access-group",
|
||||
"user_id":null,
|
||||
"group_id":9899826
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Unprotect environment
|
||||
|
||||
Unprotects the given protected environment.
|
||||
|
||||
```shell
|
||||
DELETE /groups/:id/protected_environments/:name
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) maintained by the authenticated user. |
|
||||
| `name` | string | yes | The deployment tier of the protected environment. One of `production`, `staging`, `testing`, `development`, or `other`. Read more about [deployment tiers](../ci/environments/index.md#deployment-tier-of-environments).|
|
||||
|
||||
```shell
|
||||
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/5/protected_environments/staging"
|
||||
```
|
||||
|
||||
The response should return a 200 code.
|
|
@ -154,6 +154,129 @@ be re-entered if the environment is re-protected.
|
|||
|
||||
For more information, see [Deployment safety](deployment_safety.md).
|
||||
|
||||
## Group-level protected environments
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/215888) in [GitLab Premium](https://about.gitlab.com/pricing/) 14.0.
|
||||
> - [Deployed behind a feature flag](../../user/feature_flags.md), disabled by default.
|
||||
> - Disabled on GitLab.com.
|
||||
> - Not recommended for production use.
|
||||
> - To use in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-group-level-protected-environments). **(FREE SELF)**
|
||||
|
||||
This in-development feature might not be available for your use. There can be
|
||||
[risks when enabling features still in development](../../user/feature_flags.md#risks-when-enabling-features-still-in-development).
|
||||
Refer to this feature's version history for more details.
|
||||
|
||||
Typically, large enterprise organizations have an explicit permission boundary
|
||||
between [developers and operators](https://about.gitlab.com/topics/devops/).
|
||||
Developers build and test their code, and operators deploy and monitor the
|
||||
application. With group-level protected environments, the permission of each
|
||||
group is carefully configured in order to prevent unauthorized access and
|
||||
maintain proper separation of duty. Group-level protected environments
|
||||
extend the [project-level protected environments](#protecting-environments)
|
||||
to the group-level.
|
||||
|
||||
The permissions of deployments can be illustrated in the following table:
|
||||
|
||||
| Environment | Developer | Operator | Category |
|
||||
|-------------|------------|----------|----------|
|
||||
| Development | Allowed | Allowed | Lower environment |
|
||||
| Testing | Allowed | Allowed | Lower environment |
|
||||
| Staging | Disallowed | Allowed | Higher environment |
|
||||
| Production | Disallowed | Allowed | Higher environment |
|
||||
|
||||
_(Reference: [Deployment environments on Wikipedia](https://en.wikipedia.org/wiki/Deployment_environment))_
|
||||
|
||||
### Group-level protected environments names
|
||||
|
||||
Contrary to project-level protected environments, group-level protected
|
||||
environments use the [deployment tier](index.md#deployment-tier-of-environments)
|
||||
as their name.
|
||||
|
||||
A group may consist of many project environments that have unique names.
|
||||
For example, Project-A has a `gprd` environment and Project-B has a `Production`
|
||||
environment, so protecting a specific environment name doesn't scale well.
|
||||
By using deployment tiers, both are recognized as `production` deployment tier
|
||||
and are protected at the same time.
|
||||
|
||||
### Configure group-level memberships
|
||||
|
||||
In an enterprise organization, with thousands of projects under a single group,
|
||||
ensuring that all of the [project-level protected environments](#protecting-environments)
|
||||
are properly configured is not a scalable solution. For example, a developer
|
||||
might gain privileged access to a higher environment when they are added as a
|
||||
maintainer to a new project. Group-level protected environments can be a solution
|
||||
in this situation.
|
||||
|
||||
To maximize the effectiveness of group-level protected environments,
|
||||
[group-level memberships](../../user/group/index.md) must be correctly
|
||||
configured:
|
||||
|
||||
- Operators should be assigned the [maintainer role](../../user/permissions.md)
|
||||
(or above) to the top-level group. They can maintain CI/CD configurations for
|
||||
the higher environments (such as production) in the group-level settings page,
|
||||
wnich includes group-level protected environments,
|
||||
[group-level runners](../runners/README.md#group-runners),
|
||||
[group-level clusters](../../user/group/clusters/index.md), etc. Those
|
||||
configurations are inherited to the child projects as read-only entries.
|
||||
This ensures that only operators can configure the organization-wide
|
||||
deployment ruleset.
|
||||
- Developers should be assigned the [developer role](../../user/permissions.md)
|
||||
(or below) at the top-level group, or explicitly assigned to a child project
|
||||
as maintainers. They do *NOT* have access to the CI/CD configurations in the
|
||||
top-level group, so operators can ensure that the critical configuration won't
|
||||
be accidentally changed by the developers.
|
||||
- For sub-groups and child projects:
|
||||
- Regarding [sub-groups](../../user/group/subgroups/index.md), if a higher
|
||||
group has configured the group-level protected environment, the lower groups
|
||||
cannot override it.
|
||||
- [Project-level protected environments](#protecting-environments) can be
|
||||
combined with the group-level setting. If both group-level and project-level
|
||||
environment configurations exist, the user must be allowed in **both**
|
||||
rulesets in order to run a deployment job.
|
||||
- Within a project or a sub-group of the top-level group, developers can be
|
||||
safely assigned the Maintainer role to tune their lower environments (such
|
||||
as `testing`).
|
||||
|
||||
Having this configuration in place:
|
||||
|
||||
- If a user is about to run a deployment job in a project and allowed to deploy
|
||||
to the environment, the deployment job proceeds.
|
||||
- If a user is about to run a deployment job in a project but disallowed to
|
||||
deploy to the environment, the deployment job fails with an error message.
|
||||
|
||||
### Protect a group-level environment
|
||||
|
||||
To protect a group-level environment:
|
||||
|
||||
1. Make sure your environments have the correct
|
||||
[`deployment_tier`](index.md#deployment-tier-of-environments) defined in
|
||||
`gitlab-ci.yml`.
|
||||
1. Configure the group-level protected environments via the
|
||||
[REST API](../../api/group_protected_environments.md).
|
||||
|
||||
NOTE:
|
||||
Configuration [via the UI](https://gitlab.com/gitlab-org/gitlab/-/issues/325249)
|
||||
is scheduled for a later release.
|
||||
|
||||
### Enable or disable Group-level protected environments **(FREE SELF)**
|
||||
|
||||
Group-level protected environments is under development and not ready for production use. It is
|
||||
deployed behind a feature flag that is **disabled by default**.
|
||||
[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md)
|
||||
can enable it.
|
||||
|
||||
To enable it:
|
||||
|
||||
```ruby
|
||||
Feature.enable(:group_level_protected_environments)
|
||||
```
|
||||
|
||||
To disable it:
|
||||
|
||||
```ruby
|
||||
Feature.disable(:group_level_protected_environments)
|
||||
```
|
||||
|
||||
<!-- ## Troubleshooting
|
||||
|
||||
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
|
||||
|
|
|
@ -307,6 +307,26 @@ include:
|
|||
- remote: https://gitlab.com/gitlab-org/gitlab/-/raw/v13.0.1-ee/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml
|
||||
```
|
||||
|
||||
### Use a feature flag to roll out a `latest` template
|
||||
|
||||
With a major version release like 13.0 or 14.0, [stable templates](#stable-version) must be
|
||||
updated with their corresponding [latest template versions](#latest-version).
|
||||
It may be hard to gauge the impact of this change, so use the `redirect_to_latest_template_<name>`
|
||||
feature flag to test the impact on a subset of users. Using a feature flag can help
|
||||
reduce the risk of reverts or rollbacks on production.
|
||||
|
||||
For example, to redirect the stable `Jobs/Deploy` template to its latest template in 25% of
|
||||
projects on `gitlab.com`:
|
||||
|
||||
```shell
|
||||
/chatops run feature set redirect_to_latest_template_jobs_deploy 25 --actors
|
||||
```
|
||||
|
||||
After you're confident the latest template can be moved to stable:
|
||||
|
||||
1. Update the stable template with the content of the latest version.
|
||||
1. Remove the corresponding feature flag.
|
||||
|
||||
### Further reading
|
||||
|
||||
There is an [open issue](https://gitlab.com/gitlab-org/gitlab/-/issues/17716) about
|
||||
|
|
|
@ -33,6 +33,13 @@ You can change the maximum push size for your repository.
|
|||
Navigate to **Admin Area > Settings > General**, then expand **Account and Limit**.
|
||||
From here, you can increase or decrease by changing the value in `Maximum push size (MB)`.
|
||||
|
||||
NOTE:
|
||||
When you [add files to a repository](../../project/repository/web_editor.md#create-a-file)
|
||||
through the web UI, the maximum **attachment** size is the limiting factor,
|
||||
because the [web server](../../../development/architecture.md#components)
|
||||
must receive the file before GitLab can generate the commit.
|
||||
Use [Git LFS](../../../topics/git/lfs/index.md) to add large files to a repository.
|
||||
|
||||
## Max import size
|
||||
|
||||
You can change the maximum file size for imports in GitLab.
|
||||
|
|
|
@ -89,8 +89,9 @@ artifacts, as described in the [troubleshooting documentation](../../../administ
|
|||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50889) in GitLab Core 13.9.
|
||||
|
||||
When enabled (default), the artifacts for the most recent pipeline for a ref are
|
||||
locked against deletion and kept regardless of the expiry time.
|
||||
When enabled (default), the artifacts of the most recent pipeline for each Git ref
|
||||
([branches and tags](https://git-scm.com/book/en/v2/Git-Internals-Git-References))
|
||||
are locked against deletion and kept regardless of the expiry time.
|
||||
|
||||
When disabled, the latest artifacts for any **new** successful or fixed pipelines
|
||||
are allowed to expire.
|
||||
|
|
|
@ -7,7 +7,7 @@ type: reference, concepts
|
|||
|
||||
# Squash and merge **(FREE)**
|
||||
|
||||
> - [Moved](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/18956) to GitLab Free in 11.0.
|
||||
> - [Moved](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/18956) from GitLab Premium to GitLab Free in 11.0.
|
||||
|
||||
With squash and merge you can combine all your merge request's commits into one
|
||||
and retain a clean history.
|
||||
|
|
|
@ -6,7 +6,7 @@ module Gitlab
|
|||
module External
|
||||
module File
|
||||
class Template < Base
|
||||
attr_reader :location, :project
|
||||
attr_reader :location
|
||||
|
||||
SUFFIX = '.gitlab-ci.yml'
|
||||
|
||||
|
@ -41,7 +41,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def fetch_template_content
|
||||
Gitlab::Template::GitlabCiYmlTemplate.find(template_name, project)&.content
|
||||
Gitlab::Template::GitlabCiYmlTemplate.find(template_name, context.project)&.content
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -68,11 +68,19 @@ module Gitlab
|
|||
end
|
||||
|
||||
def expand_value(value, keep_undefined: false)
|
||||
value.gsub(ExpandVariables::VARIABLES_REGEXP) do
|
||||
value.gsub(Item::VARIABLES_REGEXP) do
|
||||
match = Regexp.last_match
|
||||
result = @variables_by_key[match[1] || match[2]]&.value
|
||||
result ||= match[0] if keep_undefined
|
||||
result
|
||||
if match[:key]
|
||||
# we matched variable
|
||||
if variable = @variables_by_key[match[:key]]
|
||||
variable.value
|
||||
elsif keep_undefined
|
||||
match[0]
|
||||
end
|
||||
else
|
||||
# we escape sequence
|
||||
match[0]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -7,6 +7,9 @@ module Gitlab
|
|||
class Item
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
VARIABLES_REGEXP = /\$\$|%%|\$(?<key>[a-zA-Z_][a-zA-Z0-9_]*)|\${\g<key>?}|%\g<key>%/.freeze.freeze
|
||||
VARIABLE_REF_CHARS = %w[$ %].freeze
|
||||
|
||||
def initialize(key:, value:, public: true, file: false, masked: false, raw: false)
|
||||
raise ArgumentError, "`#{key}` must be of type String or nil value, while it was: #{value.class}" unless
|
||||
value.is_a?(String) || value.nil?
|
||||
|
@ -34,9 +37,9 @@ module Gitlab
|
|||
strong_memoize(:depends_on) do
|
||||
next if raw
|
||||
|
||||
next unless ExpandVariables.possible_var_reference?(value)
|
||||
next unless self.class.possible_var_reference?(value)
|
||||
|
||||
value.scan(ExpandVariables::VARIABLES_REGEXP).map(&:first)
|
||||
value.scan(VARIABLES_REGEXP).filter_map(&:last)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -64,6 +67,12 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
def self.possible_var_reference?(value)
|
||||
return unless value
|
||||
|
||||
VARIABLE_REF_CHARS.any? { |symbol| value.include?(symbol) }
|
||||
end
|
||||
|
||||
def to_s
|
||||
return to_runner_variable.to_s unless depends_on
|
||||
|
||||
|
|
|
@ -5,11 +5,20 @@ module Gitlab
|
|||
class GitlabCiYmlTemplate < BaseTemplate
|
||||
BASE_EXCLUDED_PATTERNS = [%r{\.latest\.}].freeze
|
||||
|
||||
TEMPLATES_WITH_LATEST_VERSION = {
|
||||
'Jobs/Deploy' => true,
|
||||
'Jobs/Browser-Performance-Testing' => true,
|
||||
'Security/API-Fuzzing' => true,
|
||||
'Security/DAST' => true,
|
||||
'Terraform' => true
|
||||
}.freeze
|
||||
|
||||
def description
|
||||
"# This file is a template, and might need editing before it works on your project."
|
||||
end
|
||||
|
||||
class << self
|
||||
extend ::Gitlab::Utils::Override
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
def extension
|
||||
|
@ -54,6 +63,31 @@ module Gitlab
|
|||
excluded_patterns: self.excluded_patterns
|
||||
)
|
||||
end
|
||||
|
||||
override :find
|
||||
def find(key, project = nil)
|
||||
if try_redirect_to_latest?(key, project)
|
||||
key += '.latest'
|
||||
end
|
||||
|
||||
super(key, project)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# To gauge the impact of the latest template,
|
||||
# you can redirect the stable template to the latest template by enabling the feature flag.
|
||||
# See https://docs.gitlab.com/ee/development/cicd/templates.html#versioning for more information.
|
||||
def try_redirect_to_latest?(key, project)
|
||||
return false unless templates_with_latest_version[key]
|
||||
|
||||
flag_name = "redirect_to_latest_template_#{key.underscore.tr('/', '_')}"
|
||||
::Feature.enabled?(flag_name, project, default_enabled: :yaml)
|
||||
end
|
||||
|
||||
def templates_with_latest_version
|
||||
TEMPLATES_WITH_LATEST_VERSION
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,7 +5,7 @@ import * as actions from '~/ci_variable_list/store/actions';
|
|||
import * as types from '~/ci_variable_list/store/mutation_types';
|
||||
import getInitialState from '~/ci_variable_list/store/state';
|
||||
import { prepareDataForDisplay, prepareEnvironments } from '~/ci_variable_list/store/utils';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import mockData from '../services/mock_data';
|
||||
|
||||
|
@ -240,7 +240,9 @@ describe('CI variable list store actions', () => {
|
|||
mock.onGet(state.endpoint).reply(500);
|
||||
|
||||
testAction(actions.fetchVariables, {}, state, [], [{ type: 'requestVariables' }], () => {
|
||||
expect(createFlash).toHaveBeenCalledWith('There was an error fetching the variables.');
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: 'There was an error fetching the variables.',
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -278,9 +280,9 @@ describe('CI variable list store actions', () => {
|
|||
[],
|
||||
[{ type: 'requestEnvironments' }],
|
||||
() => {
|
||||
expect(createFlash).toHaveBeenCalledWith(
|
||||
'There was an error fetching the environments information.',
|
||||
);
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: 'There was an error fetching the environments information.',
|
||||
});
|
||||
done();
|
||||
},
|
||||
);
|
||||
|
|
|
@ -24,7 +24,7 @@ import {
|
|||
CREATE_CLUSTER_ERROR,
|
||||
} from '~/create_cluster/eks_cluster/store/mutation_types';
|
||||
import createState from '~/create_cluster/eks_cluster/store/state';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
|
||||
jest.mock('~/flash');
|
||||
|
@ -358,7 +358,9 @@ describe('EKS Cluster Store Actions', () => {
|
|||
testAction(actions.createClusterError, payload, state, [
|
||||
{ type: CREATE_CLUSTER_ERROR, payload },
|
||||
]).then(() => {
|
||||
expect(createFlash).toHaveBeenCalledWith(payload.name[0]);
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: payload.name[0],
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,7 +4,7 @@ import Api from '~/api';
|
|||
import * as actions from '~/deploy_freeze/store/actions';
|
||||
import * as types from '~/deploy_freeze/store/mutation_types';
|
||||
import getInitialState from '~/deploy_freeze/store/state';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { freezePeriodsFixture, timezoneDataFixture } from '../helpers';
|
||||
|
||||
|
@ -189,9 +189,9 @@ describe('deploy freeze store actions', () => {
|
|||
[{ type: types.REQUEST_FREEZE_PERIODS }],
|
||||
[],
|
||||
() =>
|
||||
expect(createFlash).toHaveBeenCalledWith(
|
||||
'There was an error fetching the deploy freezes.',
|
||||
),
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: 'There was an error fetching the deploy freezes.',
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -54,7 +54,7 @@ import {
|
|||
} from '~/diffs/store/actions';
|
||||
import * as types from '~/diffs/store/mutation_types';
|
||||
import * as utils from '~/diffs/store/utils';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import * as commonUtils from '~/lib/utils/common_utils';
|
||||
import { mergeUrlParams } from '~/lib/utils/url_utility';
|
||||
|
@ -293,7 +293,9 @@ describe('DiffsStoreActions', () => {
|
|||
|
||||
testAction(fetchCoverageFiles, {}, { endpointCoverage }, [], [], () => {
|
||||
expect(createFlash).toHaveBeenCalledTimes(1);
|
||||
expect(createFlash).toHaveBeenCalledWith(expect.stringMatching('Something went wrong'));
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: expect.stringMatching('Something went wrong'),
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,7 +2,7 @@ import MockAdapter from 'axios-mock-adapter';
|
|||
import testAction from 'helpers/vuex_action_helper';
|
||||
import * as actions from '~/error_tracking/store/actions';
|
||||
import * as types from '~/error_tracking/store/mutation_types';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { visitUrl } from '~/lib/utils/url_utility';
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import MockAdapter from 'axios-mock-adapter';
|
|||
import testAction from 'helpers/vuex_action_helper';
|
||||
import * as actions from '~/error_tracking/store/details/actions';
|
||||
import * as types from '~/error_tracking/store/details/mutation_types';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import Poll from '~/lib/utils/poll';
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import MockAdapter from 'axios-mock-adapter';
|
|||
import testAction from 'helpers/vuex_action_helper';
|
||||
import * as actions from '~/error_tracking/store/list/actions';
|
||||
import * as types from '~/error_tracking/store/list/mutation_types';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import httpStatusCodes from '~/lib/utils/http_status';
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import { GlButton } from '@gitlab/ui';
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { TEST_HOST } from 'helpers/test_constants';
|
||||
import { mountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import GrafanaIntegration from '~/grafana_integration/components/grafana_integration.vue';
|
||||
import { createStore } from '~/grafana_integration/store';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
|
@ -112,10 +112,10 @@ describe('grafana integration component', () => {
|
|||
.$nextTick()
|
||||
.then(jest.runAllTicks)
|
||||
.then(() =>
|
||||
expect(createFlash).toHaveBeenCalledWith(
|
||||
`There was an error saving your changes. ${message}`,
|
||||
'alert',
|
||||
),
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: `There was an error saving your changes. ${message}`,
|
||||
type: 'alert',
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Vue from 'vue';
|
||||
import { createComponentWithStore } from 'helpers/vue_mount_component_helper';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import modal from '~/ide/components/new_dropdown/modal.vue';
|
||||
import { createStore } from '~/ide/stores';
|
||||
|
||||
|
@ -182,14 +182,14 @@ describe('new file modal component', () => {
|
|||
|
||||
vm.submitForm();
|
||||
|
||||
expect(createFlash).toHaveBeenCalledWith(
|
||||
'The name "test-path/test" is already taken in this directory.',
|
||||
'alert',
|
||||
expect.anything(),
|
||||
null,
|
||||
false,
|
||||
true,
|
||||
);
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: 'The name "test-path/test" is already taken in this directory.',
|
||||
type: 'alert',
|
||||
parent: expect.anything(),
|
||||
actionConfig: null,
|
||||
fadeTransition: false,
|
||||
addBodyClass: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('does not throw error when target entry does not exist', () => {
|
||||
|
|
|
@ -2,7 +2,7 @@ import MockAdapter from 'axios-mock-adapter';
|
|||
import { range } from 'lodash';
|
||||
import { TEST_HOST } from 'helpers/test_constants';
|
||||
import testAction from 'helpers/vuex_action_helper';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import { leftSidebarViews, PERMISSION_READ_MR, MAX_MR_FILES_AUTO_OPEN } from '~/ide/constants';
|
||||
import service from '~/ide/services';
|
||||
import { createStore } from '~/ide/stores';
|
||||
|
@ -145,7 +145,9 @@ describe('IDE store merge request actions', () => {
|
|||
.dispatch('getMergeRequestsForBranch', { projectId: TEST_PROJECT, branchId: 'bar' })
|
||||
.catch(() => {
|
||||
expect(createFlash).toHaveBeenCalled();
|
||||
expect(createFlash.mock.calls[0][0]).toBe('Error fetching merge requests for bar');
|
||||
expect(createFlash.mock.calls[0][0].message).toBe(
|
||||
'Error fetching merge requests for bar',
|
||||
);
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
|
@ -562,7 +564,9 @@ describe('IDE store merge request actions', () => {
|
|||
|
||||
openMergeRequest(store, mr)
|
||||
.catch(() => {
|
||||
expect(createFlash).toHaveBeenCalledWith(expect.any(String));
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: expect.any(String),
|
||||
});
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import MockAdapter from 'axios-mock-adapter';
|
||||
import testAction from 'helpers/vuex_action_helper';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import * as actions from '~/ide/stores/modules/terminal/actions/session_controls';
|
||||
import { STARTING, PENDING, STOPPING, STOPPED } from '~/ide/stores/modules/terminal/constants';
|
||||
import * as messages from '~/ide/stores/modules/terminal/messages';
|
||||
|
@ -89,7 +89,9 @@ describe('IDE store terminal session controls actions', () => {
|
|||
it('flashes message', () => {
|
||||
actions.receiveStartSessionError({ dispatch });
|
||||
|
||||
expect(createFlash).toHaveBeenCalledWith(messages.UNEXPECTED_ERROR_STARTING);
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: messages.UNEXPECTED_ERROR_STARTING,
|
||||
});
|
||||
});
|
||||
|
||||
it('sets session status', () => {
|
||||
|
@ -161,7 +163,9 @@ describe('IDE store terminal session controls actions', () => {
|
|||
it('flashes message', () => {
|
||||
actions.receiveStopSessionError({ dispatch });
|
||||
|
||||
expect(createFlash).toHaveBeenCalledWith(messages.UNEXPECTED_ERROR_STOPPING);
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: messages.UNEXPECTED_ERROR_STOPPING,
|
||||
});
|
||||
});
|
||||
|
||||
it('kills the session', () => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import MockAdapter from 'axios-mock-adapter';
|
||||
import testAction from 'helpers/vuex_action_helper';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import * as actions from '~/ide/stores/modules/terminal/actions/session_status';
|
||||
import { PENDING, RUNNING, STOPPING, STOPPED } from '~/ide/stores/modules/terminal/constants';
|
||||
import * as messages from '~/ide/stores/modules/terminal/messages';
|
||||
|
@ -115,7 +115,9 @@ describe('IDE store terminal session controls actions', () => {
|
|||
it('flashes message', () => {
|
||||
actions.receiveSessionStatusError({ dispatch });
|
||||
|
||||
expect(createFlash).toHaveBeenCalledWith(messages.UNEXPECTED_ERROR_STATUS);
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: messages.UNEXPECTED_ERROR_STATUS,
|
||||
});
|
||||
});
|
||||
|
||||
it('kills the session', () => {
|
||||
|
|
|
@ -5,11 +5,15 @@ import VueApollo from 'vue-apollo';
|
|||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import { STATUSES } from '~/import_entities/constants';
|
||||
import ImportTableRow from '~/import_entities/import_groups/components/import_table_row.vue';
|
||||
import groupQuery from '~/import_entities/import_groups/graphql/queries/group.query.graphql';
|
||||
import addValidationErrorMutation from '~/import_entities/import_groups/graphql/mutations/add_validation_error.mutation.graphql';
|
||||
import removeValidationErrorMutation from '~/import_entities/import_groups/graphql/mutations/remove_validation_error.mutation.graphql';
|
||||
import groupAndProjectQuery from '~/import_entities/import_groups/graphql/queries/groupAndProject.query.graphql';
|
||||
import { availableNamespacesFixture } from '../graphql/fixtures';
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
||||
const { i18n: I18N } = ImportTableRow;
|
||||
|
||||
const getFakeGroup = (status) => ({
|
||||
web_url: 'https://fake.host/',
|
||||
full_path: 'fake_group_1',
|
||||
|
@ -25,6 +29,7 @@ const getFakeGroup = (status) => ({
|
|||
|
||||
const EXISTING_GROUP_TARGET_NAMESPACE = 'existing-group';
|
||||
const EXISTING_GROUP_PATH = 'existing-path';
|
||||
const EXISTING_PROJECT_PATH = 'existing-project-path';
|
||||
|
||||
describe('import table row', () => {
|
||||
let wrapper;
|
||||
|
@ -41,13 +46,19 @@ describe('import table row', () => {
|
|||
const createComponent = (props) => {
|
||||
apolloProvider = createMockApollo([
|
||||
[
|
||||
groupQuery,
|
||||
groupAndProjectQuery,
|
||||
({ fullPath }) => {
|
||||
const existingGroup =
|
||||
fullPath === `${EXISTING_GROUP_TARGET_NAMESPACE}/${EXISTING_GROUP_PATH}`
|
||||
? { id: 1 }
|
||||
: null;
|
||||
return Promise.resolve({ data: { existingGroup } });
|
||||
|
||||
const existingProject =
|
||||
fullPath === `${EXISTING_GROUP_TARGET_NAMESPACE}/${EXISTING_PROJECT_PATH}`
|
||||
? { id: 1 }
|
||||
: null;
|
||||
|
||||
return Promise.resolve({ data: { existingGroup, existingProject } });
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
@ -173,7 +184,7 @@ describe('import table row', () => {
|
|||
});
|
||||
|
||||
describe('validations', () => {
|
||||
it('Reports invalid group name when name is not matching regex', () => {
|
||||
it('reports invalid group name when name is not matching regex', () => {
|
||||
createComponent({
|
||||
group: {
|
||||
...getFakeGroup(STATUSES.NONE),
|
||||
|
@ -188,7 +199,7 @@ describe('import table row', () => {
|
|||
expect(wrapper.text()).toContain('Please choose a group URL with no special characters.');
|
||||
});
|
||||
|
||||
it('Reports invalid group name if relevant validation error exists', async () => {
|
||||
it('reports invalid group name if relevant validation error exists', async () => {
|
||||
const FAKE_ERROR_MESSAGE = 'fake error';
|
||||
|
||||
createComponent({
|
||||
|
@ -208,5 +219,101 @@ describe('import table row', () => {
|
|||
|
||||
expect(wrapper.text()).toContain(FAKE_ERROR_MESSAGE);
|
||||
});
|
||||
|
||||
it('sets validation error when targetting existing group', async () => {
|
||||
const testGroup = getFakeGroup(STATUSES.NONE);
|
||||
|
||||
createComponent({
|
||||
group: {
|
||||
...testGroup,
|
||||
import_target: {
|
||||
target_namespace: EXISTING_GROUP_TARGET_NAMESPACE,
|
||||
new_name: EXISTING_GROUP_PATH,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
jest.spyOn(wrapper.vm.$apollo, 'mutate');
|
||||
|
||||
jest.runOnlyPendingTimers();
|
||||
await nextTick();
|
||||
|
||||
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
|
||||
mutation: addValidationErrorMutation,
|
||||
variables: {
|
||||
field: 'new_name',
|
||||
message: I18N.NAME_ALREADY_EXISTS,
|
||||
sourceGroupId: testGroup.id,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('sets validation error when targetting existing project', async () => {
|
||||
const testGroup = getFakeGroup(STATUSES.NONE);
|
||||
|
||||
createComponent({
|
||||
group: {
|
||||
...testGroup,
|
||||
import_target: {
|
||||
target_namespace: EXISTING_GROUP_TARGET_NAMESPACE,
|
||||
new_name: EXISTING_PROJECT_PATH,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
jest.spyOn(wrapper.vm.$apollo, 'mutate');
|
||||
|
||||
jest.runOnlyPendingTimers();
|
||||
await nextTick();
|
||||
|
||||
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
|
||||
mutation: addValidationErrorMutation,
|
||||
variables: {
|
||||
field: 'new_name',
|
||||
message: I18N.NAME_ALREADY_EXISTS,
|
||||
sourceGroupId: testGroup.id,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('clears validation error when target is updated', async () => {
|
||||
const testGroup = getFakeGroup(STATUSES.NONE);
|
||||
|
||||
createComponent({
|
||||
group: {
|
||||
...testGroup,
|
||||
import_target: {
|
||||
target_namespace: EXISTING_GROUP_TARGET_NAMESPACE,
|
||||
new_name: EXISTING_PROJECT_PATH,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
jest.runOnlyPendingTimers();
|
||||
await nextTick();
|
||||
|
||||
jest.spyOn(wrapper.vm.$apollo, 'mutate');
|
||||
|
||||
await wrapper.setProps({
|
||||
group: {
|
||||
...testGroup,
|
||||
import_target: {
|
||||
target_namespace: 'valid_namespace',
|
||||
new_name: 'valid_path',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
jest.runOnlyPendingTimers();
|
||||
await nextTick();
|
||||
|
||||
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
|
||||
mutation: removeValidationErrorMutation,
|
||||
variables: {
|
||||
field: 'new_name',
|
||||
sourceGroupId: testGroup.id,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { TEST_HOST } from 'helpers/test_constants';
|
||||
import testAction from 'helpers/vuex_action_helper';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import { STATUSES } from '~/import_entities/constants';
|
||||
import actionsFactory from '~/import_entities/import_projects/store/actions';
|
||||
import { getImportTarget } from '~/import_entities/import_projects/store/getters';
|
||||
|
@ -168,7 +168,9 @@ describe('import_projects store actions', () => {
|
|||
[],
|
||||
);
|
||||
|
||||
expect(createFlash).toHaveBeenCalledWith('Provider rate limit exceeded. Try again later');
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: 'Provider rate limit exceeded. Try again later',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -245,7 +247,9 @@ describe('import_projects store actions', () => {
|
|||
[],
|
||||
);
|
||||
|
||||
expect(createFlash).toHaveBeenCalledWith('Importing the project failed');
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: 'Importing the project failed',
|
||||
});
|
||||
});
|
||||
|
||||
it('commits REQUEST_IMPORT and RECEIVE_IMPORT_ERROR and shows detailed error message on an unsuccessful request with errors fields in response', async () => {
|
||||
|
@ -266,7 +270,9 @@ describe('import_projects store actions', () => {
|
|||
[],
|
||||
);
|
||||
|
||||
expect(createFlash).toHaveBeenCalledWith(`Importing the project failed: ${ERROR_MESSAGE}`);
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: `Importing the project failed: ${ERROR_MESSAGE}`,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -365,7 +371,9 @@ describe('import_projects store actions', () => {
|
|||
[],
|
||||
);
|
||||
|
||||
expect(createFlash).toHaveBeenCalledWith('Requesting namespaces failed');
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: 'Requesting namespaces failed',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import AxiosMockAdapter from 'axios-mock-adapter';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import { ERROR_MSG } from '~/incidents_settings/constants';
|
||||
import IncidentsSettingsService from '~/incidents_settings/incidents_settings_service';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
|
@ -37,7 +37,10 @@ describe('IncidentsSettingsService', () => {
|
|||
mock.onPatch().reply(httpStatusCodes.BAD_REQUEST);
|
||||
|
||||
return service.updateSettings({}).then(() => {
|
||||
expect(createFlash).toHaveBeenCalledWith(expect.stringContaining(ERROR_MSG), 'alert');
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: expect.stringContaining(ERROR_MSG),
|
||||
type: 'alert',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
issuable1,
|
||||
issuable2,
|
||||
} from 'jest/vue_shared/components/issue/related_issuable_mock_data';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import RelatedIssuesRoot from '~/related_issues/components/related_issues_root.vue';
|
||||
import { linkedIssueTypesMap } from '~/related_issues/constants';
|
||||
|
@ -195,7 +195,9 @@ describe('RelatedIssuesRoot', () => {
|
|||
wrapper.vm.onPendingFormSubmit(input);
|
||||
|
||||
return waitForPromises().then(() => {
|
||||
expect(createFlash).toHaveBeenCalledWith(message);
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { backoffMockImplementation } from 'helpers/backoff_helper';
|
||||
import testAction from 'helpers/vuex_action_helper';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import * as commonUtils from '~/lib/utils/common_utils';
|
||||
import statusCodes from '~/lib/utils/http_status';
|
||||
|
@ -257,9 +257,9 @@ describe('Monitoring store actions', () => {
|
|||
'receiveMetricsDashboardFailure',
|
||||
new Error('Request failed with status code 500'),
|
||||
);
|
||||
expect(createFlash).toHaveBeenCalledWith(
|
||||
expect.stringContaining(mockDashboardsErrorResponse.message),
|
||||
);
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: expect.stringContaining(mockDashboardsErrorResponse.message),
|
||||
});
|
||||
done();
|
||||
})
|
||||
.catch(done.fail);
|
||||
|
@ -1148,9 +1148,9 @@ describe('Monitoring store actions', () => {
|
|||
return testAction(fetchVariableMetricLabelValues, { defaultQueryParams }, state, [], []).then(
|
||||
() => {
|
||||
expect(createFlash).toHaveBeenCalledTimes(1);
|
||||
expect(createFlash).toHaveBeenCalledWith(
|
||||
expect.stringContaining('error getting options for variable "label1"'),
|
||||
);
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: expect.stringContaining('error getting options for variable "label1"'),
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { GlButton, GlLink, GlFormGroup, GlFormInput, GlFormSelect } from '@gitlab/ui';
|
||||
import { mount, shallowMount } from '@vue/test-utils';
|
||||
import { TEST_HOST } from 'helpers/test_constants';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { refreshCurrentPage } from '~/lib/utils/url_utility';
|
||||
import { timezones } from '~/monitoring/format_date';
|
||||
|
@ -203,10 +203,10 @@ describe('operation settings external dashboard component', () => {
|
|||
.$nextTick()
|
||||
.then(jest.runAllTicks)
|
||||
.then(() =>
|
||||
expect(createFlash).toHaveBeenCalledWith(
|
||||
`There was an error saving your changes. ${message}`,
|
||||
'alert',
|
||||
),
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: `There was an error saving your changes. ${message}`,
|
||||
type: 'alert',
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,7 +2,7 @@ import axios from 'axios';
|
|||
import MockAdapter from 'axios-mock-adapter';
|
||||
import testAction from 'helpers/vuex_action_helper';
|
||||
import Api from '~/api';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import { MISSING_DELETE_PATH_ERROR } from '~/packages/list/constants';
|
||||
import * as actions from '~/packages/list/stores/actions';
|
||||
import * as types from '~/packages/list/stores/mutation_types';
|
||||
|
@ -241,7 +241,9 @@ describe('Actions Package list store', () => {
|
|||
`('should reject and createFlash when $property is missing', ({ actionPayload }, done) => {
|
||||
testAction(actions.requestDeletePackage, actionPayload, null, [], []).catch((e) => {
|
||||
expect(e).toEqual(new Error(MISSING_DELETE_PATH_ERROR));
|
||||
expect(createFlash).toHaveBeenCalledWith(DELETE_PACKAGE_ERROR_MESSAGE);
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: DELETE_PACKAGE_ERROR_MESSAGE,
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,7 +2,7 @@ import MockAdapter from 'axios-mock-adapter';
|
|||
import { getJSONFixture } from 'helpers/fixtures';
|
||||
import { TEST_HOST } from 'helpers/test_constants';
|
||||
import testAction from 'helpers/vuex_action_helper';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import * as actions from '~/pipelines/stores/test_reports/actions';
|
||||
import * as types from '~/pipelines/stores/test_reports/mutation_types';
|
||||
|
|
|
@ -2,7 +2,7 @@ import { GlModal } from '@gitlab/ui';
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { TEST_HOST } from 'helpers/test_constants';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
|
||||
import UpdateUsername from '~/profile/account/components/update_username.vue';
|
||||
|
@ -146,7 +146,9 @@ describe('UpdateUsername component', () => {
|
|||
|
||||
await expect(wrapper.vm.onConfirm()).rejects.toThrow();
|
||||
|
||||
expect(createFlash).toBeCalledWith('Invalid username');
|
||||
expect(createFlash).toBeCalledWith({
|
||||
message: 'Invalid username',
|
||||
});
|
||||
});
|
||||
|
||||
it("shows a fallback error message if the error response doesn't have a `message` property", async () => {
|
||||
|
@ -156,9 +158,9 @@ describe('UpdateUsername component', () => {
|
|||
|
||||
await expect(wrapper.vm.onConfirm()).rejects.toThrow();
|
||||
|
||||
expect(createFlash).toBeCalledWith(
|
||||
'An error occurred while updating your username, please try again.',
|
||||
);
|
||||
expect(createFlash).toBeCalledWith({
|
||||
message: 'An error occurred while updating your username, please try again.',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import axios from 'axios';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import testAction from 'helpers/vuex_action_helper';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import actions from '~/projects/commits/store/actions';
|
||||
import * as types from '~/projects/commits/store/mutation_types';
|
||||
import createState from '~/projects/commits/store/state';
|
||||
|
@ -39,7 +39,9 @@ describe('Project commits actions', () => {
|
|||
actions.receiveAuthorsError(mockDispatchContext);
|
||||
|
||||
expect(createFlash).toHaveBeenCalledTimes(1);
|
||||
expect(createFlash).toHaveBeenCalledWith('An error occurred fetching the project authors.');
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: 'An error occurred fetching the project authors.',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import MockAdapter from 'axios-mock-adapter';
|
||||
import testAction from 'helpers/vuex_action_helper';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import * as actions from '~/related_merge_requests/store/actions';
|
||||
import * as types from '~/related_merge_requests/store/mutation_types';
|
||||
|
@ -100,7 +100,9 @@ describe('RelatedMergeRequest store actions', () => {
|
|||
[{ type: 'requestData' }, { type: 'receiveDataError' }],
|
||||
() => {
|
||||
expect(createFlash).toHaveBeenCalledTimes(1);
|
||||
expect(createFlash).toHaveBeenCalledWith(expect.stringMatching('Something went wrong'));
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: expect.stringMatching('Something went wrong'),
|
||||
});
|
||||
|
||||
done();
|
||||
},
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { cloneDeep } from 'lodash';
|
||||
import { getJSONFixture } from 'helpers/fixtures';
|
||||
import testAction from 'helpers/vuex_action_helper';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import { redirectTo } from '~/lib/utils/url_utility';
|
||||
import { ASSET_LINK_TYPE } from '~/releases/constants';
|
||||
import createReleaseAssetLinkMutation from '~/releases/graphql/mutations/create_release_link.mutation.graphql';
|
||||
|
@ -151,9 +151,9 @@ describe('Release edit/new actions', () => {
|
|||
it(`shows a flash message`, () => {
|
||||
return actions.fetchRelease({ commit: jest.fn(), state, rootState: state }).then(() => {
|
||||
expect(createFlash).toHaveBeenCalledTimes(1);
|
||||
expect(createFlash).toHaveBeenCalledWith(
|
||||
'Something went wrong while getting the release details.',
|
||||
);
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: 'Something went wrong while getting the release details.',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -352,9 +352,9 @@ describe('Release edit/new actions', () => {
|
|||
.createRelease({ commit: jest.fn(), dispatch: jest.fn(), state, getters: {} })
|
||||
.then(() => {
|
||||
expect(createFlash).toHaveBeenCalledTimes(1);
|
||||
expect(createFlash).toHaveBeenCalledWith(
|
||||
'Something went wrong while creating a new release.',
|
||||
);
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: 'Something went wrong while creating a new release.',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -483,9 +483,9 @@ describe('Release edit/new actions', () => {
|
|||
await actions.updateRelease({ commit, dispatch, state, getters });
|
||||
|
||||
expect(createFlash).toHaveBeenCalledTimes(1);
|
||||
expect(createFlash).toHaveBeenCalledWith(
|
||||
'Something went wrong while saving the release details.',
|
||||
);
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: 'Something went wrong while saving the release details.',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -503,9 +503,9 @@ describe('Release edit/new actions', () => {
|
|||
await actions.updateRelease({ commit, dispatch, state, getters });
|
||||
|
||||
expect(createFlash).toHaveBeenCalledTimes(1);
|
||||
expect(createFlash).toHaveBeenCalledWith(
|
||||
'Something went wrong while saving the release details.',
|
||||
);
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: 'Something went wrong while saving the release details.',
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import AxiosMockAdapter from 'axios-mock-adapter';
|
|||
import { TEST_HOST } from 'helpers/test_constants';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import BlobHeaderEdit from '~/blob/components/blob_edit_header.vue';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { joinPaths } from '~/lib/utils/url_utility';
|
||||
import SnippetBlobEdit from '~/snippets/components/snippet_blob_edit.vue';
|
||||
|
@ -125,9 +125,9 @@ describe('Snippet Blob Edit component', () => {
|
|||
it('should call flash', async () => {
|
||||
await waitForPromises();
|
||||
|
||||
expect(createFlash).toHaveBeenCalledWith(
|
||||
"Can't fetch content for the blob: Error: Request failed with status code 500",
|
||||
);
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: "Can't fetch content for the blob: Error: Request failed with status code 500",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Vue from 'vue';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import WorkInProgress from '~/vue_merge_request_widget/components/states/work_in_progress.vue';
|
||||
import eventHub from '~/vue_merge_request_widget/event_hub';
|
||||
|
||||
|
@ -63,10 +63,10 @@ describe('Wip', () => {
|
|||
setImmediate(() => {
|
||||
expect(vm.isMakingRequest).toBeTruthy();
|
||||
expect(eventHub.$emit).toHaveBeenCalledWith('UpdateWidgetData', mrObj);
|
||||
expect(createFlash).toHaveBeenCalledWith(
|
||||
'The merge request can now be merged.',
|
||||
'notice',
|
||||
);
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: 'The merge request can now be merged.',
|
||||
type: 'notice',
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,7 +3,7 @@ import MockAdapter from 'axios-mock-adapter';
|
|||
import testAction from 'helpers/vuex_action_helper';
|
||||
import { mockBranches } from 'jest/vue_shared/components/filtered_search_bar/mock_data';
|
||||
import Api from '~/api';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import createFlash from '~/flash';
|
||||
import httpStatusCodes from '~/lib/utils/http_status';
|
||||
import * as actions from '~/vue_shared/components/filtered_search_bar/store/modules/filters/actions';
|
||||
import * as types from '~/vue_shared/components/filtered_search_bar/store/modules/filters/mutation_types';
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require 'fast_spec_helper'
|
||||
require 'rspec-parameterized'
|
||||
|
||||
RSpec.describe ExpandVariables do
|
||||
shared_examples 'common variable expansion' do |expander|
|
||||
|
@ -231,41 +232,4 @@ RSpec.describe ExpandVariables do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#possible_var_reference?' do
|
||||
context 'table tests' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
where do
|
||||
{
|
||||
"empty value": {
|
||||
value: '',
|
||||
result: false
|
||||
},
|
||||
"normal value": {
|
||||
value: 'some value',
|
||||
result: false
|
||||
},
|
||||
"simple expansions": {
|
||||
value: 'key$variable',
|
||||
result: true
|
||||
},
|
||||
"complex expansions": {
|
||||
value: 'key${variable}${variable2}',
|
||||
result: true
|
||||
},
|
||||
"complex expansions for Windows": {
|
||||
value: 'key%variable%%variable2%',
|
||||
result: true
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
with_them do
|
||||
subject { ExpandVariables.possible_var_reference?(value) }
|
||||
|
||||
it { is_expected.to eq(result) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,6 +20,13 @@ RSpec.describe 'CI YML Templates' do
|
|||
all_templates - excluded_templates
|
||||
end
|
||||
|
||||
before do
|
||||
stub_feature_flags(
|
||||
redirect_to_latest_template_terraform: false,
|
||||
redirect_to_latest_template_security_api_fuzzing: false,
|
||||
redirect_to_latest_template_security_dast: false)
|
||||
end
|
||||
|
||||
with_them do
|
||||
let(:content) do
|
||||
if template_name == 'Security/DAST-API.gitlab-ci.yml'
|
||||
|
|
|
@ -70,6 +70,43 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Item do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.possible_var_reference?' do
|
||||
context 'table tests' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
where do
|
||||
{
|
||||
"empty value": {
|
||||
value: '',
|
||||
result: false
|
||||
},
|
||||
"normal value": {
|
||||
value: 'some value',
|
||||
result: false
|
||||
},
|
||||
"simple expansions": {
|
||||
value: 'key$variable',
|
||||
result: true
|
||||
},
|
||||
"complex expansions": {
|
||||
value: 'key${variable}${variable2}',
|
||||
result: true
|
||||
},
|
||||
"complex expansions for Windows": {
|
||||
value: 'key%variable%%variable2%',
|
||||
result: true
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
with_them do
|
||||
subject { Gitlab::Ci::Variables::Collection::Item.possible_var_reference?(value) }
|
||||
|
||||
it { is_expected.to eq(result) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#depends_on' do
|
||||
let(:item) { Gitlab::Ci::Variables::Collection::Item.new(**variable) }
|
||||
|
||||
|
@ -128,7 +165,7 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Item do
|
|||
end
|
||||
|
||||
it 'supports using an active record resource' do
|
||||
variable = create(:ci_variable, key: 'CI_VAR', value: '123')
|
||||
variable = build(:ci_variable, key: 'CI_VAR', value: '123')
|
||||
resource = described_class.fabricate(variable)
|
||||
|
||||
expect(resource).to be_a(described_class)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require 'fast_spec_helper'
|
||||
require 'rspec-parameterized'
|
||||
|
||||
RSpec.describe Gitlab::Ci::Variables::Collection::Sort do
|
||||
describe '#initialize with non-Collection value' do
|
||||
|
@ -57,9 +58,9 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Sort do
|
|||
},
|
||||
"variable containing escaped variable reference": {
|
||||
variables: [
|
||||
{ key: 'variable_a', value: 'value' },
|
||||
{ key: 'variable_b', value: '$$variable_a' },
|
||||
{ key: 'variable_c', value: '$variable_b' }
|
||||
{ key: 'variable_c', value: '$variable_a' },
|
||||
{ key: 'variable_a', value: 'value' }
|
||||
],
|
||||
expected_errors: nil
|
||||
}
|
||||
|
@ -144,11 +145,11 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Sort do
|
|||
},
|
||||
"variable containing escaped variable reference": {
|
||||
variables: [
|
||||
{ key: 'variable_c', value: '$variable_b' },
|
||||
{ key: 'variable_b', value: '$$variable_a' },
|
||||
{ key: 'variable_c', value: '$variable_a' },
|
||||
{ key: 'variable_a', value: 'value' }
|
||||
],
|
||||
result: %w[variable_a variable_b variable_c]
|
||||
result: %w[variable_b variable_a variable_c]
|
||||
}
|
||||
}
|
||||
end
|
||||
|
|
|
@ -253,6 +253,11 @@ RSpec.describe Gitlab::Ci::Variables::Collection do
|
|||
value: 'key${MISSING_VAR}-${CI_JOB_NAME}',
|
||||
result: 'key${MISSING_VAR}-test-1',
|
||||
keep_undefined: true
|
||||
},
|
||||
"escaped characters are kept intact": {
|
||||
value: 'key-$TEST1-%%HOME%%-$${HOME}',
|
||||
result: 'key-test-3-%%HOME%%-$${HOME}',
|
||||
keep_undefined: false
|
||||
}
|
||||
}
|
||||
end
|
||||
|
@ -315,6 +320,14 @@ RSpec.describe Gitlab::Ci::Variables::Collection do
|
|||
],
|
||||
keep_undefined: false
|
||||
},
|
||||
"escaped characters in complex expansions are kept intact": {
|
||||
variables: [
|
||||
{ key: 'variable3', value: 'key_${variable}_$${HOME}_%%HOME%%' },
|
||||
{ key: 'variable', value: '$variable2' },
|
||||
{ key: 'variable2', value: 'value2' }
|
||||
],
|
||||
keep_undefined: false
|
||||
},
|
||||
"array with cyclic dependency": {
|
||||
variables: [
|
||||
{ key: 'variable', value: '$variable2' },
|
||||
|
@ -415,6 +428,30 @@ RSpec.describe Gitlab::Ci::Variables::Collection do
|
|||
{ key: 'variable3', value: 'keyvalueresult' }
|
||||
]
|
||||
},
|
||||
"escaped characters in complex expansions keeping undefined are kept intact": {
|
||||
variables: [
|
||||
{ key: 'variable3', value: 'key_${variable}_$${HOME}_%%HOME%%' },
|
||||
{ key: 'variable', value: '$variable2' },
|
||||
{ key: 'variable2', value: 'value' }
|
||||
],
|
||||
keep_undefined: true,
|
||||
result: [
|
||||
{ key: 'variable', value: 'value' },
|
||||
{ key: 'variable2', value: 'value' },
|
||||
{ key: 'variable3', value: 'key_value_$${HOME}_%%HOME%%' }
|
||||
]
|
||||
},
|
||||
"escaped characters in complex expansions discarding undefined are kept intact": {
|
||||
variables: [
|
||||
{ key: 'variable2', value: 'key_${variable4}_$${HOME}_%%HOME%%' },
|
||||
{ key: 'variable', value: 'value_$${HOME}_%%HOME%%' }
|
||||
],
|
||||
keep_undefined: false,
|
||||
result: [
|
||||
{ key: 'variable', value: 'value_$${HOME}_%%HOME%%' },
|
||||
{ key: 'variable2', value: 'key__$${HOME}_%%HOME%%' }
|
||||
]
|
||||
},
|
||||
"out-of-order expansion": {
|
||||
variables: [
|
||||
{ key: 'variable3', value: 'key$variable2$variable' },
|
||||
|
@ -441,7 +478,7 @@ RSpec.describe Gitlab::Ci::Variables::Collection do
|
|||
{ key: 'variable3', value: 'keyresultvalue' }
|
||||
]
|
||||
},
|
||||
"missing variable": {
|
||||
"missing variable discarding original": {
|
||||
variables: [
|
||||
{ key: 'variable2', value: 'key$variable' }
|
||||
],
|
||||
|
@ -485,6 +522,19 @@ RSpec.describe Gitlab::Ci::Variables::Collection do
|
|||
{ key: 'variable3', value: 'key_$variable2_value2' }
|
||||
]
|
||||
},
|
||||
"variable value referencing password with special characters": {
|
||||
variables: [
|
||||
{ key: 'VAR', value: '$PASSWORD' },
|
||||
{ key: 'PASSWORD', value: 'my_password$$_%%_$A' },
|
||||
{ key: 'A', value: 'value' }
|
||||
],
|
||||
keep_undefined: false,
|
||||
result: [
|
||||
{ key: 'VAR', value: 'my_password$$_%%_value' },
|
||||
{ key: 'PASSWORD', value: 'my_password$$_%%_value' },
|
||||
{ key: 'A', value: 'value' }
|
||||
]
|
||||
},
|
||||
"cyclic dependency causes original array to be returned": {
|
||||
variables: [
|
||||
{ key: 'variable', value: '$variable2' },
|
||||
|
|
|
@ -21,6 +21,55 @@ RSpec.describe Gitlab::Template::GitlabCiYmlTemplate do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.find' do
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:other_project) { create(:project) }
|
||||
|
||||
described_class::TEMPLATES_WITH_LATEST_VERSION.keys.each do |key|
|
||||
it "finds the latest template for #{key}" do
|
||||
result = described_class.find(key, project)
|
||||
expect(result.full_name).to eq("#{key}.latest.gitlab-ci.yml")
|
||||
expect(result.content).to be_present
|
||||
end
|
||||
|
||||
context 'when `redirect_to_latest_template` feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags("redirect_to_latest_template_#{key.underscore.tr('/', '_')}".to_sym => false)
|
||||
end
|
||||
|
||||
it "finds the stable template for #{key}" do
|
||||
result = described_class.find(key, project)
|
||||
expect(result.full_name).to eq("#{key}.gitlab-ci.yml")
|
||||
expect(result.content).to be_present
|
||||
end
|
||||
end
|
||||
|
||||
context 'when `redirect_to_latest_template` feature flag is enabled on the project' do
|
||||
before do
|
||||
stub_feature_flags("redirect_to_latest_template_#{key.underscore.tr('/', '_')}".to_sym => project)
|
||||
end
|
||||
|
||||
it "finds the latest template for #{key}" do
|
||||
result = described_class.find(key, project)
|
||||
expect(result.full_name).to eq("#{key}.latest.gitlab-ci.yml")
|
||||
expect(result.content).to be_present
|
||||
end
|
||||
end
|
||||
|
||||
context 'when `redirect_to_latest_template` feature flag is enabled on the other project' do
|
||||
before do
|
||||
stub_feature_flags("redirect_to_latest_template_#{key.underscore.tr('/', '_')}".to_sym => other_project)
|
||||
end
|
||||
|
||||
it "finds the stable template for #{key}" do
|
||||
result = described_class.find(key, project)
|
||||
expect(result.full_name).to eq("#{key}.gitlab-ci.yml")
|
||||
expect(result.content).to be_present
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#content' do
|
||||
it 'loads the full file' do
|
||||
gitignore = subject.new(Rails.root.join('lib/gitlab/ci/templates/Ruby.gitlab-ci.yml'))
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
@ -53,14 +52,6 @@ func (e *entry) Inject(w http.ResponseWriter, r *http.Request, sendData string)
|
|||
}
|
||||
}
|
||||
|
||||
func detectFileContentType(fileName string) string {
|
||||
contentType := mime.TypeByExtension(filepath.Ext(fileName))
|
||||
if contentType == "" {
|
||||
contentType = "application/octet-stream"
|
||||
}
|
||||
return contentType
|
||||
}
|
||||
|
||||
func unpackFileFromZip(ctx context.Context, archivePath, encodedFilename string, headers http.Header, output io.Writer) error {
|
||||
fileName, err := zipartifacts.DecodeFileEntry(encodedFilename)
|
||||
if err != nil {
|
||||
|
@ -97,7 +88,15 @@ func unpackFileFromZip(ctx context.Context, archivePath, encodedFilename string,
|
|||
|
||||
// Write http headers about the file
|
||||
headers.Set("Content-Length", contentLength)
|
||||
headers.Set("Content-Type", detectFileContentType(fileName))
|
||||
|
||||
// Using application/octet-stream tells the client that we don't
|
||||
// really know what Content-Type is. Since this file is being sent
|
||||
// as attachment, browsers don't need to know to save the
|
||||
// file. Chrome doesn't appear to pay attention to Content-Type when
|
||||
// Content-Disposition is an attachment, and Firefox only uses it if there
|
||||
// is no extension in the filename. Thus, there's no need for
|
||||
// Workhorse to guess Content-Type based on the filename.
|
||||
headers.Set("Content-Type", "application/octet-stream")
|
||||
headers.Set("Content-Disposition", "attachment; filename=\""+escapeQuotes(basename)+"\"")
|
||||
// Copy file body to client
|
||||
if _, err := io.Copy(output, reader); err != nil {
|
||||
|
|
|
@ -54,7 +54,7 @@ func TestDownloadingFromValidArchive(t *testing.T) {
|
|||
|
||||
testhelper.RequireResponseHeader(t, response,
|
||||
"Content-Type",
|
||||
"text/plain; charset=utf-8")
|
||||
"application/octet-stream")
|
||||
testhelper.RequireResponseHeader(t, response,
|
||||
"Content-Disposition",
|
||||
"attachment; filename=\"test.txt\"")
|
||||
|
@ -88,7 +88,7 @@ func TestDownloadingFromValidHTTPArchive(t *testing.T) {
|
|||
|
||||
testhelper.RequireResponseHeader(t, response,
|
||||
"Content-Type",
|
||||
"text/plain; charset=utf-8")
|
||||
"application/octet-stream")
|
||||
testhelper.RequireResponseHeader(t, response,
|
||||
"Content-Disposition",
|
||||
"attachment; filename=\"test.txt\"")
|
||||
|
|
Loading…
Reference in New Issue