Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
dcf94a7641
commit
62cd7010ef
|
@ -36,6 +36,23 @@
|
|||
<<: *gitaly-ruby-gems-cache
|
||||
policy: push # We want to rebuild the cache from scratch to ensure stale dependencies are cleaned up.
|
||||
|
||||
.gitaly-binaries-cache: &gitaly-binaries-cache
|
||||
key:
|
||||
files:
|
||||
- GITALY_SERVER_VERSION
|
||||
prefix: "gitaly-binaries"
|
||||
paths:
|
||||
- tmp/tests/gitaly/_build/bin/
|
||||
- tmp/tests/gitaly/config.toml
|
||||
- tmp/tests/gitaly/gitaly2.config.toml
|
||||
- tmp/tests/gitaly/internal/
|
||||
- tmp/tests/gitaly/internal_gitaly2/
|
||||
- tmp/tests/gitaly/internal_sockets/
|
||||
- tmp/tests/gitaly/Makefile
|
||||
- tmp/tests/gitaly/praefect.config.toml
|
||||
- tmp/tests/gitaly/ruby/
|
||||
policy: pull
|
||||
|
||||
.go-pkg-cache: &go-pkg-cache
|
||||
key: "go-pkg-v1"
|
||||
paths:
|
||||
|
@ -97,6 +114,7 @@
|
|||
cache:
|
||||
- *ruby-gems-cache
|
||||
- *gitaly-ruby-gems-cache
|
||||
- *gitaly-binaries-cache
|
||||
- *go-pkg-cache
|
||||
|
||||
.setup-test-env-cache-push:
|
||||
|
@ -105,6 +123,11 @@
|
|||
- *gitaly-ruby-gems-cache-push
|
||||
- *go-pkg-cache-push
|
||||
|
||||
.gitaly-binaries-cache-push:
|
||||
cache:
|
||||
- <<: *gitaly-binaries-cache
|
||||
policy: push # We want to rebuild the cache from scratch to ensure stale dependencies are cleaned up.
|
||||
|
||||
.rails-cache:
|
||||
cache:
|
||||
- *ruby-gems-cache
|
||||
|
|
|
@ -177,6 +177,14 @@ update-setup-test-env-cache:
|
|||
artifacts:
|
||||
paths: [] # This job's purpose is only to update the cache.
|
||||
|
||||
update-gitaly-binaries-cache:
|
||||
extends:
|
||||
- setup-test-env
|
||||
- .gitaly-binaries-cache-push
|
||||
- .shared:rules:update-gitaly-binaries-cache
|
||||
artifacts:
|
||||
paths: [] # This job's purpose is only to update the cache.
|
||||
|
||||
.coverage-base:
|
||||
extends:
|
||||
- .default-retry
|
||||
|
@ -203,13 +211,6 @@ update-static-analysis-cache:
|
|||
- .shared:rules:update-cache
|
||||
stage: prepare
|
||||
script:
|
||||
- git log -n 1 --pretty=format:%H -- .rubocop.yml
|
||||
- git log -n 1 --pretty=format:%H -- .rubocop_manual_todo.yml
|
||||
- git log -n 1 --pretty=format:%H -- .rubocop_todo.yml
|
||||
- git log -n 1 --pretty=format:%H -- rubocop/rubocop-migrations.yml
|
||||
- git log -n 1 --pretty=format:%H -- rubocop/rubocop-usage-data.yml
|
||||
- git log -n 1 --pretty=format:%H -- rubocop/rubocop-code_reuse.yml
|
||||
- bundle exec scripts/debug-rubocop spec/factories/namespace/aggregation_schedules.rb
|
||||
- run_timed_command "bundle exec rubocop --parallel" # For the moment we only cache `tmp/rubocop_cache` so we don't need to run all the tasks.
|
||||
|
||||
static-analysis:
|
||||
|
@ -220,13 +221,6 @@ static-analysis:
|
|||
parallel: 4
|
||||
script:
|
||||
- run_timed_command "retry yarn install --frozen-lockfile"
|
||||
- git log -n 1 --pretty=format:%H -- .rubocop.yml
|
||||
- git log -n 1 --pretty=format:%H -- .rubocop_manual_todo.yml
|
||||
- git log -n 1 --pretty=format:%H -- .rubocop_todo.yml
|
||||
- git log -n 1 --pretty=format:%H -- rubocop/rubocop-migrations.yml
|
||||
- git log -n 1 --pretty=format:%H -- rubocop/rubocop-usage-data.yml
|
||||
- git log -n 1 --pretty=format:%H -- rubocop/rubocop-code_reuse.yml
|
||||
- bundle exec scripts/debug-rubocop spec/factories/namespace/aggregation_schedules.rb
|
||||
- scripts/static-analysis
|
||||
|
||||
static-analysis as-if-foss:
|
||||
|
|
|
@ -111,6 +111,9 @@
|
|||
- ".gitlab/ci/build-images.gitlab-ci.yml"
|
||||
- ".gitlab/ci/qa.gitlab-ci.yml"
|
||||
|
||||
.gitaly-patterns: &gitaly-patterns
|
||||
- "GITALY_SERVER_VERSION"
|
||||
|
||||
.workhorse-patterns: &workhorse-patterns
|
||||
- "GITLAB_WORKHORSE_VERSION"
|
||||
- "workhorse/**/*"
|
||||
|
@ -310,6 +313,11 @@
|
|||
- <<: *if-security-schedule
|
||||
- <<: *if-merge-request-title-update-caches
|
||||
|
||||
.shared:rules:update-gitaly-binaries-cache:
|
||||
rules:
|
||||
- <<: *if-merge-request-title-update-caches
|
||||
- changes: *gitaly-patterns
|
||||
|
||||
######################
|
||||
# Build images rules #
|
||||
######################
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
import axios from '~/lib/utils/axios_utils';
|
||||
import { buildApiUrl } from './api_utils';
|
||||
|
||||
const PROJECT_VSA_PATH_BASE = '/:project_path/-/analytics/value_stream_analytics/value_streams';
|
||||
const PROJECT_VSA_STAGES_PATH = `${PROJECT_VSA_PATH_BASE}/:value_stream_id/stages`;
|
||||
|
||||
const buildProjectValueStreamPath = (projectPath, valueStreamId = null) => {
|
||||
if (valueStreamId) {
|
||||
return buildApiUrl(PROJECT_VSA_STAGES_PATH)
|
||||
.replace(':project_path', projectPath)
|
||||
.replace(':value_stream_id', valueStreamId);
|
||||
}
|
||||
return buildApiUrl(PROJECT_VSA_PATH_BASE).replace(':project_path', projectPath);
|
||||
};
|
||||
|
||||
export const getProjectValueStreams = (projectPath) => {
|
||||
const url = buildProjectValueStreamPath(projectPath);
|
||||
return axios.get(url);
|
||||
};
|
||||
|
||||
export const getProjectValueStreamStages = (projectPath, valueStreamId) => {
|
||||
const url = buildProjectValueStreamPath(projectPath, valueStreamId);
|
||||
return axios.get(url);
|
||||
};
|
||||
|
||||
// NOTE: legacy VSA request use a different path
|
||||
// the `requestPath` provides a full url for the request
|
||||
export const getProjectValueStreamStageData = ({ requestPath, stageId, params }) =>
|
||||
axios.get(`${requestPath}/events/${stageId}`, { params });
|
||||
|
||||
export const getProjectValueStreamMetrics = (requestPath, params) =>
|
||||
axios.get(requestPath, { params });
|
|
@ -58,6 +58,7 @@ export default {
|
|||
'stages',
|
||||
'summary',
|
||||
'startDate',
|
||||
'permissions',
|
||||
]),
|
||||
...mapGetters(['pathNavigationData']),
|
||||
displayStageEvents() {
|
||||
|
@ -68,7 +69,7 @@ export default {
|
|||
return this.selectedStageReady && this.isEmptyStage;
|
||||
},
|
||||
displayNoAccess() {
|
||||
return this.selectedStageReady && !this.selectedStage.isUserAllowed;
|
||||
return this.selectedStageReady && !this.isUserAllowed(this.selectedStage.id);
|
||||
},
|
||||
selectedStageReady() {
|
||||
return !this.isLoadingStage && this.selectedStage;
|
||||
|
@ -91,25 +92,18 @@ export default {
|
|||
]),
|
||||
handleDateSelect(startDate) {
|
||||
this.setDateRange({ startDate });
|
||||
this.fetchCycleAnalyticsData();
|
||||
},
|
||||
isActiveStage(stage) {
|
||||
return stage.slug === this.selectedStage.slug;
|
||||
},
|
||||
onSelectStage(stage) {
|
||||
if (this.isLoadingStage || this.selectedStage?.slug === stage?.slug) return;
|
||||
|
||||
this.setSelectedStage(stage);
|
||||
if (!stage.isUserAllowed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.fetchStageData();
|
||||
},
|
||||
dismissOverviewDialog() {
|
||||
this.isOverviewDialogDismissed = true;
|
||||
Cookies.set(OVERVIEW_DIALOG_COOKIE, '1', { expires: 365 });
|
||||
},
|
||||
isUserAllowed(id) {
|
||||
const { permissions } = this;
|
||||
return Boolean(permissions?.[id]);
|
||||
},
|
||||
},
|
||||
dayRangeOptions: [7, 30, 90],
|
||||
i18n: {
|
||||
|
|
|
@ -1,2 +1,8 @@
|
|||
export const DEFAULT_DAYS_TO_DISPLAY = 30;
|
||||
export const OVERVIEW_STAGE_ID = 'overview';
|
||||
|
||||
export const DEFAULT_VALUE_STREAM = {
|
||||
id: 'default',
|
||||
slug: 'default',
|
||||
name: 'default',
|
||||
};
|
||||
|
|
|
@ -8,10 +8,11 @@ Vue.use(Translate);
|
|||
export default () => {
|
||||
const store = createStore();
|
||||
const el = document.querySelector('#js-cycle-analytics');
|
||||
const { noAccessSvgPath, noDataSvgPath, requestPath } = el.dataset;
|
||||
const { noAccessSvgPath, noDataSvgPath, requestPath, fullPath } = el.dataset;
|
||||
|
||||
store.dispatch('initializeVsa', {
|
||||
requestPath,
|
||||
fullPath,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-new
|
||||
|
@ -24,6 +25,7 @@ export default () => {
|
|||
props: {
|
||||
noDataSvgPath,
|
||||
noAccessSvgPath,
|
||||
fullPath,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -1,27 +1,60 @@
|
|||
import {
|
||||
getProjectValueStreamStages,
|
||||
getProjectValueStreams,
|
||||
getProjectValueStreamStageData,
|
||||
getProjectValueStreamMetrics,
|
||||
} from '~/api/analytics_api';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { __ } from '~/locale';
|
||||
import { DEFAULT_DAYS_TO_DISPLAY } from '../constants';
|
||||
import { DEFAULT_DAYS_TO_DISPLAY, DEFAULT_VALUE_STREAM } from '../constants';
|
||||
import * as types from './mutation_types';
|
||||
|
||||
export const fetchCycleAnalyticsData = ({
|
||||
state: { requestPath, startDate },
|
||||
dispatch,
|
||||
commit,
|
||||
}) => {
|
||||
export const setSelectedValueStream = ({ commit, dispatch }, valueStream) => {
|
||||
commit(types.SET_SELECTED_VALUE_STREAM, valueStream);
|
||||
return dispatch('fetchValueStreamStages');
|
||||
};
|
||||
|
||||
export const fetchValueStreamStages = ({ commit, state }) => {
|
||||
const { fullPath, selectedValueStream } = state;
|
||||
commit(types.REQUEST_VALUE_STREAM_STAGES);
|
||||
|
||||
return getProjectValueStreamStages(fullPath, selectedValueStream.id)
|
||||
.then(({ data }) => commit(types.RECEIVE_VALUE_STREAM_STAGES_SUCCESS, data))
|
||||
.catch(({ response: { status } }) => {
|
||||
commit(types.RECEIVE_VALUE_STREAM_STAGES_ERROR, status);
|
||||
});
|
||||
};
|
||||
|
||||
export const receiveValueStreamsSuccess = ({ commit, dispatch }, data = []) => {
|
||||
commit(types.RECEIVE_VALUE_STREAMS_SUCCESS, data);
|
||||
if (data.length) {
|
||||
const [firstStream] = data;
|
||||
return dispatch('setSelectedValueStream', firstStream);
|
||||
}
|
||||
return dispatch('setSelectedValueStream', DEFAULT_VALUE_STREAM);
|
||||
};
|
||||
|
||||
export const fetchValueStreams = ({ commit, dispatch, state }) => {
|
||||
const { fullPath } = state;
|
||||
commit(types.REQUEST_VALUE_STREAMS);
|
||||
|
||||
return getProjectValueStreams(fullPath)
|
||||
.then(({ data }) => dispatch('receiveValueStreamsSuccess', data))
|
||||
.then(() => dispatch('setSelectedStage'))
|
||||
.catch(({ response: { status } }) => {
|
||||
commit(types.RECEIVE_VALUE_STREAMS_ERROR, status);
|
||||
});
|
||||
};
|
||||
|
||||
export const fetchCycleAnalyticsData = ({ state: { requestPath, startDate }, commit }) => {
|
||||
commit(types.REQUEST_CYCLE_ANALYTICS_DATA);
|
||||
|
||||
return axios
|
||||
.get(requestPath, {
|
||||
params: { 'cycle_analytics[start_date]': startDate },
|
||||
})
|
||||
return getProjectValueStreamMetrics(requestPath, { 'cycle_analytics[start_date]': startDate })
|
||||
.then(({ data }) => commit(types.RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS, data))
|
||||
.then(() => dispatch('setSelectedStage'))
|
||||
.then(() => dispatch('fetchStageData'))
|
||||
.catch(() => {
|
||||
commit(types.RECEIVE_CYCLE_ANALYTICS_DATA_ERROR);
|
||||
createFlash({
|
||||
message: __('There was an error while fetching value stream analytics data.'),
|
||||
message: __('There was an error while fetching value stream summary data.'),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -29,10 +62,11 @@ export const fetchCycleAnalyticsData = ({
|
|||
export const fetchStageData = ({ state: { requestPath, selectedStage, startDate }, commit }) => {
|
||||
commit(types.REQUEST_STAGE_DATA);
|
||||
|
||||
return axios
|
||||
.get(`${requestPath}/events/${selectedStage.name}.json`, {
|
||||
params: { 'cycle_analytics[start_date]': startDate },
|
||||
})
|
||||
return getProjectValueStreamStageData({
|
||||
requestPath,
|
||||
stageId: selectedStage.id,
|
||||
params: { 'cycle_analytics[start_date]': startDate },
|
||||
})
|
||||
.then(({ data }) => {
|
||||
// when there's a query timeout, the request succeeds but the error is encoded in the response data
|
||||
if (data?.error) {
|
||||
|
@ -44,15 +78,26 @@ export const fetchStageData = ({ state: { requestPath, selectedStage, startDate
|
|||
.catch(() => commit(types.RECEIVE_STAGE_DATA_ERROR));
|
||||
};
|
||||
|
||||
export const setSelectedStage = ({ commit, state: { stages } }, selectedStage = null) => {
|
||||
export const setSelectedStage = ({ dispatch, commit, state: { stages } }, selectedStage = null) => {
|
||||
const stage = selectedStage || stages[0];
|
||||
commit(types.SET_SELECTED_STAGE, stage);
|
||||
return dispatch('fetchStageData');
|
||||
};
|
||||
|
||||
export const setDateRange = ({ commit }, { startDate = DEFAULT_DAYS_TO_DISPLAY }) =>
|
||||
const refetchData = (dispatch, commit) => {
|
||||
commit(types.SET_LOADING, true);
|
||||
return Promise.resolve()
|
||||
.then(() => dispatch('fetchValueStreams'))
|
||||
.then(() => dispatch('fetchCycleAnalyticsData'))
|
||||
.finally(() => commit(types.SET_LOADING, false));
|
||||
};
|
||||
|
||||
export const setDateRange = ({ dispatch, commit }, { startDate = DEFAULT_DAYS_TO_DISPLAY }) => {
|
||||
commit(types.SET_DATE_RANGE, { startDate });
|
||||
return refetchData(dispatch, commit);
|
||||
};
|
||||
|
||||
export const initializeVsa = ({ commit, dispatch }, initialData = {}) => {
|
||||
commit(types.INITIALIZE_VSA, initialData);
|
||||
return dispatch('fetchCycleAnalyticsData');
|
||||
return refetchData(dispatch, commit);
|
||||
};
|
||||
|
|
|
@ -1,8 +1,18 @@
|
|||
export const INITIALIZE_VSA = 'INITIALIZE_VSA';
|
||||
|
||||
export const SET_LOADING = 'SET_LOADING';
|
||||
export const SET_SELECTED_VALUE_STREAM = 'SET_SELECTED_VALUE_STREAM';
|
||||
export const SET_SELECTED_STAGE = 'SET_SELECTED_STAGE';
|
||||
export const SET_DATE_RANGE = 'SET_DATE_RANGE';
|
||||
|
||||
export const REQUEST_VALUE_STREAMS = 'REQUEST_VALUE_STREAMS';
|
||||
export const RECEIVE_VALUE_STREAMS_SUCCESS = 'RECEIVE_VALUE_STREAMS_SUCCESS';
|
||||
export const RECEIVE_VALUE_STREAMS_ERROR = 'RECEIVE_VALUE_STREAMS_ERROR';
|
||||
|
||||
export const REQUEST_VALUE_STREAM_STAGES = 'REQUEST_VALUE_STREAM_STAGES';
|
||||
export const RECEIVE_VALUE_STREAM_STAGES_SUCCESS = 'RECEIVE_VALUE_STREAM_STAGES_SUCCESS';
|
||||
export const RECEIVE_VALUE_STREAM_STAGES_ERROR = 'RECEIVE_VALUE_STREAM_STAGES_ERROR';
|
||||
|
||||
export const REQUEST_CYCLE_ANALYTICS_DATA = 'REQUEST_CYCLE_ANALYTICS_DATA';
|
||||
export const RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS = 'RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS';
|
||||
export const RECEIVE_CYCLE_ANALYTICS_DATA_ERROR = 'RECEIVE_CYCLE_ANALYTICS_DATA_ERROR';
|
||||
|
|
|
@ -1,34 +1,61 @@
|
|||
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
|
||||
import { decorateData, decorateEvents, formatMedianValues } from '../utils';
|
||||
import * as types from './mutation_types';
|
||||
|
||||
export default {
|
||||
[types.INITIALIZE_VSA](state, { requestPath }) {
|
||||
[types.INITIALIZE_VSA](state, { requestPath, fullPath }) {
|
||||
state.requestPath = requestPath;
|
||||
state.fullPath = fullPath;
|
||||
},
|
||||
[types.SET_LOADING](state, loadingState) {
|
||||
state.isLoading = loadingState;
|
||||
},
|
||||
[types.SET_SELECTED_VALUE_STREAM](state, selectedValueStream = {}) {
|
||||
state.selectedValueStream = convertObjectPropsToCamelCase(selectedValueStream, { deep: true });
|
||||
},
|
||||
[types.SET_SELECTED_STAGE](state, stage) {
|
||||
state.isLoadingStage = true;
|
||||
state.selectedStage = stage;
|
||||
state.isLoadingStage = false;
|
||||
},
|
||||
[types.SET_DATE_RANGE](state, { startDate }) {
|
||||
state.startDate = startDate;
|
||||
},
|
||||
[types.REQUEST_VALUE_STREAMS](state) {
|
||||
state.valueStreams = [];
|
||||
},
|
||||
[types.RECEIVE_VALUE_STREAMS_SUCCESS](state, valueStreams = []) {
|
||||
state.valueStreams = valueStreams;
|
||||
},
|
||||
[types.RECEIVE_VALUE_STREAMS_ERROR](state) {
|
||||
state.valueStreams = [];
|
||||
},
|
||||
[types.REQUEST_VALUE_STREAM_STAGES](state) {
|
||||
state.stages = [];
|
||||
},
|
||||
[types.RECEIVE_VALUE_STREAM_STAGES_SUCCESS](state, { stages = [] }) {
|
||||
state.stages = stages.map((s) => ({
|
||||
...convertObjectPropsToCamelCase(s, { deep: true }),
|
||||
// NOTE: we set the component type here to match the current behaviour
|
||||
// this can be removed when we migrate to the update stage table
|
||||
// https://gitlab.com/gitlab-org/gitlab/-/issues/326704
|
||||
component: `stage-${s.id}-component`,
|
||||
}));
|
||||
},
|
||||
[types.RECEIVE_VALUE_STREAM_STAGES_ERROR](state) {
|
||||
state.stages = [];
|
||||
},
|
||||
[types.REQUEST_CYCLE_ANALYTICS_DATA](state) {
|
||||
state.isLoading = true;
|
||||
state.stages = [];
|
||||
state.hasError = false;
|
||||
},
|
||||
[types.RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS](state, data) {
|
||||
state.isLoading = false;
|
||||
const { stages, summary, medians } = decorateData(data);
|
||||
state.stages = stages;
|
||||
const { summary, medians } = decorateData(data);
|
||||
state.permissions = data.permissions;
|
||||
state.summary = summary;
|
||||
state.medians = formatMedianValues(medians);
|
||||
state.hasError = false;
|
||||
},
|
||||
[types.RECEIVE_CYCLE_ANALYTICS_DATA_ERROR](state) {
|
||||
state.isLoading = false;
|
||||
state.stages = [];
|
||||
state.hasError = true;
|
||||
},
|
||||
[types.REQUEST_STAGE_DATA](state) {
|
||||
|
|
|
@ -2,11 +2,14 @@ import { DEFAULT_DAYS_TO_DISPLAY } from '../constants';
|
|||
|
||||
export default () => ({
|
||||
requestPath: '',
|
||||
fullPath: '',
|
||||
startDate: DEFAULT_DAYS_TO_DISPLAY,
|
||||
stages: [],
|
||||
summary: [],
|
||||
analytics: [],
|
||||
stats: [],
|
||||
valueStreams: [],
|
||||
selectedValueStream: {},
|
||||
selectedStage: {},
|
||||
selectedStageEvents: [],
|
||||
selectedStageError: '',
|
||||
|
@ -15,4 +18,5 @@ export default () => ({
|
|||
isLoading: false,
|
||||
isLoadingStage: false,
|
||||
isEmptyStage: false,
|
||||
permissions: {},
|
||||
});
|
||||
|
|
|
@ -2,31 +2,9 @@ import { unescape } from 'lodash';
|
|||
import { sanitize } from '~/lib/dompurify';
|
||||
import { roundToNearestHalf, convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
|
||||
import { parseSeconds } from '~/lib/utils/datetime_utility';
|
||||
import { dasherize } from '~/lib/utils/text_utility';
|
||||
import { __, s__, sprintf } from '../locale';
|
||||
import { s__, sprintf } from '../locale';
|
||||
import DEFAULT_EVENT_OBJECTS from './default_event_objects';
|
||||
|
||||
const EMPTY_STAGE_TEXTS = {
|
||||
issue: __(
|
||||
'The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.',
|
||||
),
|
||||
plan: __(
|
||||
'The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.',
|
||||
),
|
||||
code: __(
|
||||
'The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.',
|
||||
),
|
||||
test: __(
|
||||
'The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.',
|
||||
),
|
||||
review: __(
|
||||
'The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.',
|
||||
),
|
||||
staging: __(
|
||||
'The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.',
|
||||
),
|
||||
};
|
||||
|
||||
/**
|
||||
* These `decorate` methods will be removed when me migrate to the
|
||||
* new table layout https://gitlab.com/gitlab-org/gitlab/-/issues/326704
|
||||
|
@ -43,33 +21,12 @@ const mapToEvent = (event, stage) => {
|
|||
|
||||
export const decorateEvents = (events, stage) => events.map((event) => mapToEvent(event, stage));
|
||||
|
||||
/*
|
||||
* NOTE: We currently use the `name` field since the project level stages are in memory
|
||||
* once we migrate to a default value stream https://gitlab.com/gitlab-org/gitlab/-/issues/326705
|
||||
* we can use the `id` to identify which median we are using
|
||||
*/
|
||||
const mapToStage = (permissions, { name, ...rest }) => {
|
||||
const slug = dasherize(name.toLowerCase());
|
||||
return {
|
||||
...rest,
|
||||
name,
|
||||
id: name,
|
||||
slug,
|
||||
active: false,
|
||||
isUserAllowed: permissions[slug],
|
||||
emptyStageText: EMPTY_STAGE_TEXTS[slug],
|
||||
component: `stage-${slug}-component`,
|
||||
};
|
||||
};
|
||||
|
||||
const mapToSummary = ({ value, ...rest }) => ({ ...rest, value: value || '-' });
|
||||
const mapToMedians = ({ id, value }) => ({ id, value });
|
||||
const mapToMedians = ({ name: id, value }) => ({ id, value });
|
||||
|
||||
export const decorateData = (data = {}) => {
|
||||
const { permissions, stats, summary } = data;
|
||||
const stages = stats?.map((item) => mapToStage(permissions, item)) || [];
|
||||
const { stats: stages, summary } = data;
|
||||
return {
|
||||
stages,
|
||||
summary: summary?.map((item) => mapToSummary(item)) || [],
|
||||
medians: stages?.map((item) => mapToMedians(item)) || [],
|
||||
};
|
||||
|
|
|
@ -31,7 +31,7 @@ function organizeQuery(obj, isFallbackKey = false) {
|
|||
}
|
||||
|
||||
function format(searchTerm, isFallbackKey = false) {
|
||||
const queryObject = queryToObject(searchTerm);
|
||||
const queryObject = queryToObject(searchTerm, { legacySpacesDecode: true });
|
||||
const organizeQueryObject = organizeQuery(queryObject, isFallbackKey);
|
||||
const formattedQuery = objectToQuery(organizeQueryObject);
|
||||
|
||||
|
|
|
@ -414,29 +414,35 @@ export function getWebSocketUrl(path) {
|
|||
*
|
||||
* @param {String} query from "document.location.search"
|
||||
* @param {Object} options
|
||||
* @param {Boolean} options.gatherArrays - gather array values into an Array
|
||||
* @param {Boolean?} options.gatherArrays - gather array values into an Array
|
||||
* @param {Boolean?} options.legacySpacesDecode - (deprecated) plus symbols (+) are not replaced with spaces, false by default
|
||||
* @returns {Object}
|
||||
*
|
||||
* ex: "?one=1&two=2" into {one: 1, two: 2}
|
||||
*/
|
||||
export function queryToObject(query, options = {}) {
|
||||
const { gatherArrays = false } = options;
|
||||
export function queryToObject(query, { gatherArrays = false, legacySpacesDecode = false } = {}) {
|
||||
const removeQuestionMarkFromQuery = String(query).startsWith('?') ? query.slice(1) : query;
|
||||
return removeQuestionMarkFromQuery.split('&').reduce((accumulator, curr) => {
|
||||
const [key, value] = curr.split('=');
|
||||
if (value === undefined) {
|
||||
return accumulator;
|
||||
}
|
||||
const decodedValue = decodeURIComponent(value);
|
||||
|
||||
const decodedValue = legacySpacesDecode ? decodeURIComponent(value) : decodeUrlParameter(value);
|
||||
|
||||
if (gatherArrays && key.endsWith('[]')) {
|
||||
const decodedKey = decodeURIComponent(key.slice(0, -2));
|
||||
const decodedKey = legacySpacesDecode
|
||||
? decodeURIComponent(key.slice(0, -2))
|
||||
: decodeUrlParameter(key.slice(0, -2));
|
||||
|
||||
if (!Array.isArray(accumulator[decodedKey])) {
|
||||
accumulator[decodedKey] = [];
|
||||
}
|
||||
accumulator[decodedKey].push(decodedValue);
|
||||
} else {
|
||||
accumulator[decodeURIComponent(key)] = decodedValue;
|
||||
const decodedKey = legacySpacesDecode ? decodeURIComponent(key) : decodeUrlParameter(key);
|
||||
|
||||
accumulator[decodedKey] = decodedValue;
|
||||
}
|
||||
|
||||
return accumulator;
|
||||
|
|
|
@ -175,7 +175,7 @@ export const graphDataValidatorForAnomalyValues = (graphData) => {
|
|||
* Returns `null` if no parameters form a time range.
|
||||
*/
|
||||
export const timeRangeFromUrl = (search = window.location.search) => {
|
||||
const params = queryToObject(search);
|
||||
const params = queryToObject(search, { legacySpacesDecode: true });
|
||||
return timeRangeFromParams(params);
|
||||
};
|
||||
|
||||
|
@ -228,7 +228,7 @@ export const convertVariablesForURL = (variables) =>
|
|||
* @returns {Object} The custom variables defined by the user in the URL
|
||||
*/
|
||||
export const templatingVariablesFromUrl = (search = window.location.search) => {
|
||||
const params = queryToObject(search);
|
||||
const params = queryToObject(search, { legacySpacesDecode: true });
|
||||
// pick the params with variable prefix
|
||||
const paramsWithVars = pickBy(params, (val, key) => key.startsWith(VARIABLE_PREFIX));
|
||||
// remove the prefix before storing in the Vuex store
|
||||
|
@ -289,7 +289,7 @@ export const timeRangeToUrl = (timeRange, url = window.location.href) => {
|
|||
* @throws Will throw an error if Panel cannot be located.
|
||||
*/
|
||||
export const expandedPanelPayloadFromUrl = (dashboard, search = window.location.search) => {
|
||||
const params = queryToObject(search);
|
||||
const params = queryToObject(search, { legacySpacesDecode: true });
|
||||
|
||||
// Search for the panel if any of the search params is identified
|
||||
if (params.group || params.title || params.y_label) {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { queryToObject } from '~/lib/utils/url_utility';
|
||||
import { FILTERED_SEARCH_TERM } from './constants';
|
||||
|
||||
export const getQueryParams = (query) => queryToObject(query, { gatherArrays: true });
|
||||
export const getQueryParams = (query) =>
|
||||
queryToObject(query, { gatherArrays: true, legacySpacesDecode: true });
|
||||
|
||||
export const keyValueToFilterToken = (type, data) => ({ type, value: { data } });
|
||||
|
||||
|
|
|
@ -8,10 +8,7 @@ import createStore from './store';
|
|||
import { initTopbar } from './topbar';
|
||||
|
||||
export const initSearchApp = () => {
|
||||
// Similar to url_utility.decodeUrlParameter
|
||||
// Our query treats + as %20. This replaces the query + symbols with %20.
|
||||
const sanitizedSearch = window.location.search.replace(/\+/g, '%20');
|
||||
const query = queryToObject(sanitizedSearch);
|
||||
const query = queryToObject(window.location.search);
|
||||
|
||||
const store = createStore({ query });
|
||||
|
||||
|
|
|
@ -142,11 +142,11 @@ function extractNameAndOperator(filterName) {
|
|||
* '?myFilterName=foo'
|
||||
* gets translated into:
|
||||
* { myFilterName: { value: 'foo', operator: '=' } }
|
||||
* @param {String} query URL quert string, e.g. from `window.location.search`
|
||||
* @param {String} query URL query string, e.g. from `window.location.search`
|
||||
* @return {Object} filter object with filter names and their values
|
||||
*/
|
||||
export function urlQueryToFilter(query = '') {
|
||||
const filters = queryToObject(query, { gatherArrays: true });
|
||||
const filters = queryToObject(query, { gatherArrays: true, legacySpacesDecode: true });
|
||||
return Object.keys(filters).reduce((memo, key) => {
|
||||
const value = filters[key];
|
||||
if (!value) {
|
||||
|
|
|
@ -337,6 +337,9 @@ h1 {
|
|||
.d-none {
|
||||
display: none !important;
|
||||
}
|
||||
.d-inline-block {
|
||||
display: inline-block !important;
|
||||
}
|
||||
.d-block {
|
||||
display: block !important;
|
||||
}
|
||||
|
@ -344,6 +347,9 @@ h1 {
|
|||
.d-sm-none {
|
||||
display: none !important;
|
||||
}
|
||||
.d-sm-inline-block {
|
||||
display: inline-block !important;
|
||||
}
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.d-md-block {
|
||||
|
@ -351,6 +357,9 @@ h1 {
|
|||
}
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
.d-lg-none {
|
||||
display: none !important;
|
||||
}
|
||||
.d-lg-block {
|
||||
display: block !important;
|
||||
}
|
||||
|
@ -2287,24 +2296,11 @@ body.gl-dark {
|
|||
.gl-display-none {
|
||||
display: none;
|
||||
}
|
||||
@media (min-width: 62rem) {
|
||||
.gl-lg-display-none {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media (min-width: 36rem) {
|
||||
.gl-sm-display-block {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
.gl-display-inline-block {
|
||||
display: inline-block;
|
||||
}
|
||||
@media (min-width: 36rem) {
|
||||
.gl-sm-display-inline-block {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
.gl-absolute {
|
||||
position: absolute;
|
||||
}
|
||||
|
|
|
@ -322,6 +322,9 @@ h1 {
|
|||
.d-none {
|
||||
display: none !important;
|
||||
}
|
||||
.d-inline-block {
|
||||
display: inline-block !important;
|
||||
}
|
||||
.d-block {
|
||||
display: block !important;
|
||||
}
|
||||
|
@ -329,6 +332,9 @@ h1 {
|
|||
.d-sm-none {
|
||||
display: none !important;
|
||||
}
|
||||
.d-sm-inline-block {
|
||||
display: inline-block !important;
|
||||
}
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.d-md-block {
|
||||
|
@ -336,6 +342,9 @@ h1 {
|
|||
}
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
.d-lg-none {
|
||||
display: none !important;
|
||||
}
|
||||
.d-lg-block {
|
||||
display: block !important;
|
||||
}
|
||||
|
@ -2070,24 +2079,11 @@ body.sidebar-refactoring
|
|||
.gl-display-none {
|
||||
display: none;
|
||||
}
|
||||
@media (min-width: 62rem) {
|
||||
.gl-lg-display-none {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media (min-width: 36rem) {
|
||||
.gl-sm-display-block {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
.gl-display-inline-block {
|
||||
display: inline-block;
|
||||
}
|
||||
@media (min-width: 36rem) {
|
||||
.gl-sm-display-inline-block {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
.gl-absolute {
|
||||
position: absolute;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::AppearancesController < Admin::ApplicationController
|
||||
class Admin::ApplicationSettings::AppearancesController < Admin::ApplicationController
|
||||
before_action :set_appearance, except: :create
|
||||
|
||||
feature_category :navigation
|
||||
|
@ -16,7 +16,7 @@ class Admin::AppearancesController < Admin::ApplicationController
|
|||
@appearance = Appearance.new(appearance_params)
|
||||
|
||||
if @appearance.save
|
||||
redirect_to admin_appearances_path, notice: _('Appearance was successfully created.')
|
||||
redirect_to admin_application_settings_appearances_path, notice: _('Appearance was successfully created.')
|
||||
else
|
||||
render action: 'show'
|
||||
end
|
||||
|
@ -24,7 +24,7 @@ class Admin::AppearancesController < Admin::ApplicationController
|
|||
|
||||
def update
|
||||
if @appearance.update(appearance_params)
|
||||
redirect_to admin_appearances_path, notice: _('Appearance was successfully updated.')
|
||||
redirect_to admin_application_settings_appearances_path, notice: _('Appearance was successfully updated.')
|
||||
else
|
||||
render action: 'show'
|
||||
end
|
||||
|
@ -35,21 +35,21 @@ class Admin::AppearancesController < Admin::ApplicationController
|
|||
|
||||
@appearance.save
|
||||
|
||||
redirect_to admin_appearances_path, notice: _('Logo was successfully removed.')
|
||||
redirect_to admin_application_settings_appearances_path, notice: _('Logo was successfully removed.')
|
||||
end
|
||||
|
||||
def header_logos
|
||||
@appearance.remove_header_logo!
|
||||
@appearance.save
|
||||
|
||||
redirect_to admin_appearances_path, notice: _('Header logo was successfully removed.')
|
||||
redirect_to admin_application_settings_appearances_path, notice: _('Header logo was successfully removed.')
|
||||
end
|
||||
|
||||
def favicon
|
||||
@appearance.remove_favicon!
|
||||
@appearance.save
|
||||
|
||||
redirect_to admin_appearances_path, notice: _('Favicon was successfully removed.')
|
||||
redirect_to admin_application_settings_appearances_path, notice: _('Favicon was successfully removed.')
|
||||
end
|
||||
|
||||
private
|
|
@ -10,7 +10,7 @@ class Import::BulkImportsController < ApplicationController
|
|||
|
||||
POLLING_INTERVAL = 3_000
|
||||
|
||||
rescue_from BulkImports::Clients::Http::ConnectionError, with: :bulk_import_connection_error
|
||||
rescue_from BulkImports::Clients::HTTP::ConnectionError, with: :bulk_import_connection_error
|
||||
|
||||
def configure
|
||||
session[access_token_key] = configure_params[access_token_key]&.strip
|
||||
|
@ -86,7 +86,7 @@ class Import::BulkImportsController < ApplicationController
|
|||
end
|
||||
|
||||
def client
|
||||
@client ||= BulkImports::Clients::Http.new(
|
||||
@client ||= BulkImports::Clients::HTTP.new(
|
||||
uri: session[url_key],
|
||||
token: session[access_token_key],
|
||||
per_page: params[:per_page],
|
||||
|
|
|
@ -9,7 +9,7 @@ module BulkImports
|
|||
@relation = relation
|
||||
@entity = @pipeline_tracker.entity
|
||||
@configuration = @entity.bulk_import.configuration
|
||||
@client = Clients::Http.new(uri: @configuration.url, token: @configuration.access_token)
|
||||
@client = Clients::HTTP.new(uri: @configuration.url, token: @configuration.access_token)
|
||||
end
|
||||
|
||||
def started?
|
||||
|
|
|
@ -39,6 +39,7 @@ module Ci
|
|||
has_one :deployment, as: :deployable, class_name: 'Deployment'
|
||||
has_one :pending_state, class_name: 'Ci::BuildPendingState', inverse_of: :build
|
||||
has_one :queuing_entry, class_name: 'Ci::PendingBuild', foreign_key: :build_id
|
||||
has_one :runtime_metadata, class_name: 'Ci::RunningBuild', foreign_key: :build_id
|
||||
has_many :trace_sections, class_name: 'Ci::BuildTraceSection'
|
||||
has_many :trace_chunks, class_name: 'Ci::BuildTraceChunk', foreign_key: :build_id, inverse_of: :build
|
||||
has_many :report_results, class_name: 'Ci::BuildReportResult', inverse_of: :build
|
||||
|
@ -310,7 +311,22 @@ module Ci
|
|||
after_transition pending: any do |build, transition|
|
||||
Ci::UpdateBuildQueueService.new.pop(build, transition)
|
||||
end
|
||||
|
||||
after_transition any => [:running] do |build, transition|
|
||||
Ci::UpdateBuildQueueService.new.track(build, transition)
|
||||
end
|
||||
|
||||
after_transition running: any do |build, transition|
|
||||
Ci::UpdateBuildQueueService.new.untrack(build, transition)
|
||||
|
||||
Ci::BuildRunnerSession.where(build: build).delete_all
|
||||
end
|
||||
|
||||
# rubocop:enable CodeReuse/ServiceClass
|
||||
#
|
||||
after_transition pending: :running do |build|
|
||||
build.ensure_metadata.update_timeout_state
|
||||
end
|
||||
|
||||
after_transition pending: :running do |build|
|
||||
build.deployment&.run
|
||||
|
@ -364,14 +380,6 @@ module Ci
|
|||
end
|
||||
end
|
||||
|
||||
after_transition pending: :running do |build|
|
||||
build.ensure_metadata.update_timeout_state
|
||||
end
|
||||
|
||||
after_transition running: any do |build|
|
||||
Ci::BuildRunnerSession.where(build: build).delete_all
|
||||
end
|
||||
|
||||
after_transition any => [:skipped, :canceled] do |build, transition|
|
||||
if transition.to_name == :skipped
|
||||
build.deployment&.skip
|
||||
|
@ -1068,16 +1076,26 @@ module Ci
|
|||
options.dig(:allow_failure_criteria, :exit_codes).present?
|
||||
end
|
||||
|
||||
def all_queuing_entries
|
||||
# We can have only one queuing entry, because there is a unique index on
|
||||
# `build_id`, but we need a relation to remove this single queuing entry
|
||||
# more efficiently in a single statement without actually load data.
|
||||
def create_queuing_entry!
|
||||
::Ci::PendingBuild.upsert_from_build!(self)
|
||||
end
|
||||
|
||||
##
|
||||
# We can have only one queuing entry or running build tracking entry,
|
||||
# because there is a unique index on `build_id` in each table, but we need
|
||||
# a relation to remove these entries more efficiently in a single statement
|
||||
# without actually loading data.
|
||||
#
|
||||
def all_queuing_entries
|
||||
::Ci::PendingBuild.where(build_id: self.id)
|
||||
end
|
||||
|
||||
def create_queuing_entry!
|
||||
::Ci::PendingBuild.upsert_from_build!(self)
|
||||
def all_runtime_metadata
|
||||
::Ci::RunningBuild.where(build_id: self.id)
|
||||
end
|
||||
|
||||
def shared_runner_build?
|
||||
runner&.instance_type?
|
||||
end
|
||||
|
||||
protected
|
||||
|
|
|
@ -8,7 +8,7 @@ module Ci
|
|||
belongs_to :build, class_name: 'Ci::Build'
|
||||
|
||||
def self.upsert_from_build!(build)
|
||||
entry = self.new(build: build, project: build.project)
|
||||
entry = self.new(build: build, project: build.project, protected: build.protected?)
|
||||
|
||||
entry.validate!
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Ci
|
||||
class RunningBuild < ApplicationRecord
|
||||
extend Gitlab::Ci::Model
|
||||
|
||||
belongs_to :project
|
||||
belongs_to :build, class_name: 'Ci::Build'
|
||||
belongs_to :runner, class_name: 'Ci::Runner'
|
||||
|
||||
enum runner_type: ::Ci::Runner.runner_types
|
||||
|
||||
def self.upsert_shared_runner_build!(build)
|
||||
unless build.shared_runner_build?
|
||||
raise ArgumentError, 'build has not been picked by a shared runner'
|
||||
end
|
||||
|
||||
entry = self.new(build: build,
|
||||
project: build.project,
|
||||
runner: build.runner,
|
||||
runner_type: build.runner.runner_type)
|
||||
|
||||
entry.validate!
|
||||
|
||||
self.upsert(entry.attributes.compact, returning: %w[build_id], unique_by: :build_id)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -38,7 +38,7 @@ class Commit
|
|||
cache_markdown_field :description, pipeline: :commit_description, limit: 1.megabyte
|
||||
|
||||
# Share the cache used by the markdown fields
|
||||
attr_mentionable :full_title, pipeline: :single_line, limit: 1.kilobyte
|
||||
attr_mentionable :title, pipeline: :single_line
|
||||
attr_mentionable :description, pipeline: :commit_description, limit: 1.megabyte
|
||||
|
||||
class << self
|
||||
|
|
|
@ -24,7 +24,7 @@ module Enums
|
|||
project_deleted: 15,
|
||||
ci_quota_exceeded: 16,
|
||||
pipeline_loop_detected: 17,
|
||||
no_matching_runner: 18,
|
||||
no_matching_runner: 18, # not used anymore, but cannot be deleted because of old data
|
||||
insufficient_bridge_permissions: 1_001,
|
||||
downstream_bridge_project_not_found: 1_002,
|
||||
invalid_bridge_trigger: 1_003,
|
||||
|
|
|
@ -9,8 +9,6 @@ class GroupDeployToken < ApplicationRecord
|
|||
validates :deploy_token_id, uniqueness: { scope: [:group_id] }
|
||||
|
||||
def has_access_to?(requested_project)
|
||||
return false unless Feature.enabled?(:allow_group_deploy_token, default_enabled: true)
|
||||
|
||||
requested_project_group = requested_project&.group
|
||||
return false unless requested_project_group
|
||||
return true if requested_project_group.id == group_id
|
||||
|
|
|
@ -6,9 +6,9 @@ module Boards
|
|||
include ActiveRecord::ConnectionAdapters::Quoting
|
||||
|
||||
def execute
|
||||
return items.order_closed_date_desc if list&.closed?
|
||||
items = init_collection
|
||||
|
||||
ordered_items
|
||||
order(items)
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
|
@ -17,7 +17,7 @@ module Boards
|
|||
keys = metadata_fields.keys
|
||||
# TODO: eliminate need for SQL literal fragment
|
||||
columns = Arel.sql(metadata_fields.values_at(*keys).join(', '))
|
||||
results = item_model.where(id: items.select(issuables[:id])).pluck(columns)
|
||||
results = item_model.where(id: init_collection.select(issuables[:id])).pluck(columns)
|
||||
|
||||
Hash[keys.zip(results.flatten)]
|
||||
end
|
||||
|
@ -29,7 +29,7 @@ module Boards
|
|||
{ size: 'COUNT(*)' }
|
||||
end
|
||||
|
||||
def ordered_items
|
||||
def order(items)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
|
@ -47,8 +47,8 @@ module Boards
|
|||
|
||||
# We memoize the query here since the finder methods we use are quite complex. This does not memoize the result of the query.
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def items
|
||||
strong_memoize(:items) do
|
||||
def init_collection
|
||||
strong_memoize(:init_collection) do
|
||||
filter(finder.execute).reorder(nil)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,7 +11,9 @@ module Boards
|
|||
|
||||
private
|
||||
|
||||
def ordered_items
|
||||
def order(items)
|
||||
return items.order_closed_date_desc if list&.closed?
|
||||
|
||||
items.order_by_position_and_priority(with_cte: params[:search].present?)
|
||||
end
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ module BulkImports
|
|||
end
|
||||
|
||||
def http_client
|
||||
@http_client ||= BulkImports::Clients::Http.new(
|
||||
@http_client ||= BulkImports::Clients::HTTP.new(
|
||||
uri: configuration.url,
|
||||
token: configuration.access_token
|
||||
)
|
||||
|
|
|
@ -1,91 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Ci
|
||||
module PipelineCreation
|
||||
class DropNotRunnableBuildsService
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
def initialize(pipeline)
|
||||
@pipeline = pipeline
|
||||
end
|
||||
|
||||
##
|
||||
# We want to run this service exactly once,
|
||||
# before the first pipeline processing call
|
||||
#
|
||||
def execute
|
||||
return unless ::Feature.enabled?(:ci_drop_new_builds_when_ci_quota_exceeded, project, default_enabled: :yaml)
|
||||
return unless pipeline.created?
|
||||
|
||||
load_runners
|
||||
validate_build_matchers
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :pipeline
|
||||
attr_reader :instance_runners, :private_runners
|
||||
delegate :project, to: :pipeline
|
||||
|
||||
def load_runners
|
||||
@instance_runners, @private_runners = project
|
||||
.all_runners
|
||||
.active
|
||||
.online
|
||||
.runner_matchers
|
||||
.partition(&:instance_type?)
|
||||
end
|
||||
|
||||
def validate_build_matchers
|
||||
pipeline.build_matchers.each do |build_matcher|
|
||||
failure_reason = validate_build_matcher(build_matcher)
|
||||
next unless failure_reason
|
||||
|
||||
drop_all_builds(build_matcher.build_ids, failure_reason)
|
||||
end
|
||||
end
|
||||
|
||||
def validate_build_matcher(build_matcher)
|
||||
return if matching_private_runners?(build_matcher)
|
||||
return if matching_instance_runners?(build_matcher)
|
||||
|
||||
matching_failure_reason(build_matcher)
|
||||
end
|
||||
|
||||
##
|
||||
# We skip pipeline processing until we drop all required builds. Otherwise
|
||||
# as we drop the first build, the remaining builds to be dropped could
|
||||
# transition to other states by `PipelineProcessWorker` running async.
|
||||
#
|
||||
def drop_all_builds(build_ids, failure_reason)
|
||||
pipeline.builds.id_in(build_ids).each do |build|
|
||||
build.drop(failure_reason, skip_pipeline_processing: true)
|
||||
end
|
||||
end
|
||||
|
||||
def matching_private_runners?(build_matcher)
|
||||
private_runners
|
||||
.find { |matcher| matcher.matches?(build_matcher) }
|
||||
.present?
|
||||
end
|
||||
|
||||
def matching_instance_runners?(build_matcher)
|
||||
instance_runners
|
||||
.find { |matcher| matching_criteria(matcher, build_matcher) }
|
||||
.present?
|
||||
end
|
||||
|
||||
# Overridden in EE
|
||||
def matching_criteria(runner_matcher, build_matcher)
|
||||
runner_matcher.matches?(build_matcher)
|
||||
end
|
||||
|
||||
# Overridden in EE
|
||||
def matching_failure_reason(build_matcher)
|
||||
:no_matching_runner
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Ci::PipelineCreation::DropNotRunnableBuildsService.prepend_mod_with('Ci::PipelineCreation::DropNotRunnableBuildsService')
|
|
@ -10,9 +10,10 @@ module Ci
|
|||
end
|
||||
|
||||
def execute
|
||||
DropNotRunnableBuildsService.new(pipeline).execute
|
||||
Ci::ProcessPipelineService.new(pipeline).execute
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
::Ci::PipelineCreation::StartPipelineService.prepend_mod_with('Ci::PipelineCreation::StartPipelineService')
|
||||
|
|
|
@ -48,6 +48,47 @@ module Ci
|
|||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Add shared runner build tracking entry (used for queuing).
|
||||
#
|
||||
def track(build, transition)
|
||||
return unless Feature.enabled?(:ci_track_shared_runner_builds, build.project, default_enabled: :yaml)
|
||||
return unless build.shared_runner_build?
|
||||
|
||||
raise InvalidQueueTransition unless transition.to == 'running'
|
||||
|
||||
transition.within_transaction do
|
||||
result = ::Ci::RunningBuild.upsert_shared_runner_build!(build)
|
||||
|
||||
unless result.empty?
|
||||
metrics.increment_queue_operation(:shared_runner_build_new)
|
||||
|
||||
result.rows.dig(0, 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Remove a runtime build tracking entry for a shared runner build (used for
|
||||
# queuing).
|
||||
#
|
||||
def untrack(build, transition)
|
||||
return unless Feature.enabled?(:ci_untrack_shared_runner_builds, build.project, default_enabled: :yaml)
|
||||
return unless build.shared_runner_build?
|
||||
|
||||
raise InvalidQueueTransition unless transition.from == 'running'
|
||||
|
||||
transition.within_transaction do
|
||||
removed = build.all_runtime_metadata.delete_all
|
||||
|
||||
if removed > 0
|
||||
metrics.increment_queue_operation(:shared_runner_build_done)
|
||||
|
||||
build.id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Unblock runner associated with given project / build
|
||||
#
|
||||
|
|
|
@ -11,6 +11,7 @@ module Projects
|
|||
@initialize_with_readme = Gitlab::Utils.to_boolean(@params.delete(:initialize_with_readme))
|
||||
@import_data = @params.delete(:import_data)
|
||||
@relations_block = @params.delete(:relations_block)
|
||||
@default_branch = @params.delete(:default_branch)
|
||||
|
||||
build_topics
|
||||
end
|
||||
|
@ -130,20 +131,16 @@ module Projects
|
|||
access_level: group_access_level)
|
||||
end
|
||||
|
||||
if Feature.enabled?(:specialized_project_authorization_workers, default_enabled: :yaml)
|
||||
AuthorizedProjectUpdate::ProjectCreateWorker.perform_async(@project.id)
|
||||
# AuthorizedProjectsWorker uses an exclusive lease per user but
|
||||
# specialized workers might have synchronization issues. Until we
|
||||
# compare the inconsistency rates of both approaches, we still run
|
||||
# AuthorizedProjectsWorker but with some delay and lower urgency as a
|
||||
# safety net.
|
||||
@project.group.refresh_members_authorized_projects(
|
||||
blocking: false,
|
||||
priority: UserProjectAccessChangedService::LOW_PRIORITY
|
||||
)
|
||||
else
|
||||
@project.group.refresh_members_authorized_projects(blocking: false)
|
||||
end
|
||||
AuthorizedProjectUpdate::ProjectCreateWorker.perform_async(@project.id)
|
||||
# AuthorizedProjectsWorker uses an exclusive lease per user but
|
||||
# specialized workers might have synchronization issues. Until we
|
||||
# compare the inconsistency rates of both approaches, we still run
|
||||
# AuthorizedProjectsWorker but with some delay and lower urgency as a
|
||||
# safety net.
|
||||
@project.group.refresh_members_authorized_projects(
|
||||
blocking: false,
|
||||
priority: UserProjectAccessChangedService::LOW_PRIORITY
|
||||
)
|
||||
else
|
||||
@project.add_maintainer(@project.namespace.owner, current_user: current_user)
|
||||
end
|
||||
|
@ -151,7 +148,7 @@ module Projects
|
|||
|
||||
def create_readme
|
||||
commit_attrs = {
|
||||
branch_name: @project.default_branch_or_main,
|
||||
branch_name: @default_branch.presence || @project.default_branch_or_main,
|
||||
commit_message: 'Initial commit',
|
||||
file_path: 'README.md',
|
||||
file_content: "# #{@project.name}\n\n#{@project.description}"
|
||||
|
|
|
@ -23,22 +23,18 @@ module Projects
|
|||
private
|
||||
|
||||
def setup_authorizations(group, group_access = nil)
|
||||
if Feature.enabled?(:specialized_project_authorization_project_share_worker, default_enabled: :yaml)
|
||||
AuthorizedProjectUpdate::ProjectGroupLinkCreateWorker.perform_async(
|
||||
project.id, group.id, group_access)
|
||||
AuthorizedProjectUpdate::ProjectGroupLinkCreateWorker.perform_async(
|
||||
project.id, group.id, group_access)
|
||||
|
||||
# AuthorizedProjectsWorker uses an exclusive lease per user but
|
||||
# specialized workers might have synchronization issues. Until we
|
||||
# compare the inconsistency rates of both approaches, we still run
|
||||
# AuthorizedProjectsWorker but with some delay and lower urgency as a
|
||||
# safety net.
|
||||
group.refresh_members_authorized_projects(
|
||||
blocking: false,
|
||||
priority: UserProjectAccessChangedService::LOW_PRIORITY
|
||||
)
|
||||
else
|
||||
group.refresh_members_authorized_projects(blocking: false)
|
||||
end
|
||||
# AuthorizedProjectsWorker uses an exclusive lease per user but
|
||||
# specialized workers might have synchronization issues. Until we
|
||||
# compare the inconsistency rates of both approaches, we still run
|
||||
# AuthorizedProjectsWorker but with some delay and lower urgency as a
|
||||
# safety net.
|
||||
group.refresh_members_authorized_projects(
|
||||
blocking: false,
|
||||
priority: UserProjectAccessChangedService::LOW_PRIORITY
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
- parsed_with_gfm = (_("Content parsed with %{link}.") % { link: link_to('GitLab Flavored Markdown', help_page_path('user/markdown'), target: '_blank') }).html_safe
|
||||
|
||||
= form_for @appearance, url: admin_appearances_path, html: { class: 'gl-mt-3' } do |f|
|
||||
= form_for @appearance, url: admin_application_settings_appearances_path, html: { class: 'gl-mt-3' } do |f|
|
||||
= form_errors(@appearance)
|
||||
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
|||
= image_tag @appearance.header_logo_path, class: 'appearance-light-logo-preview'
|
||||
- if @appearance.persisted?
|
||||
%br
|
||||
= link_to _('Remove header logo'), header_logos_admin_appearances_path, data: { confirm: _("Header logo will be removed. Are you sure?") }, method: :delete, class: "btn gl-button btn-danger btn-danger-secondary btn-sm"
|
||||
= link_to _('Remove header logo'), header_logos_admin_application_settings_appearances_path, data: { confirm: _("Header logo will be removed. Are you sure?") }, method: :delete, class: "btn gl-button btn-danger btn-danger-secondary btn-sm"
|
||||
%hr
|
||||
= f.hidden_field :header_logo_cache
|
||||
= f.file_field :header_logo, class: "", accept: 'image/*'
|
||||
|
@ -35,7 +35,7 @@
|
|||
= image_tag @appearance.favicon_path, class: 'appearance-light-logo-preview'
|
||||
- if @appearance.persisted?
|
||||
%br
|
||||
= link_to _('Remove favicon'), favicon_admin_appearances_path, data: { confirm: _("Favicon will be removed. Are you sure?") }, method: :delete, class: "btn gl-button btn-danger btn-danger-secondary btn-sm"
|
||||
= link_to _('Remove favicon'), favicon_admin_application_settings_appearances_path, data: { confirm: _("Favicon will be removed. Are you sure?") }, method: :delete, class: "btn gl-button btn-danger btn-danger-secondary btn-sm"
|
||||
%hr
|
||||
= f.hidden_field :favicon_cache
|
||||
= f.file_field :favicon, class: '', accept: 'image/*'
|
||||
|
@ -44,7 +44,7 @@
|
|||
%br
|
||||
= _("Images with incorrect dimensions are not resized automatically, and may result in unexpected behavior.")
|
||||
|
||||
= render partial: 'admin/appearances/system_header_footer_form', locals: { form: f }
|
||||
= render partial: 'admin/application_settings/appearances/system_header_footer_form', locals: { form: f }
|
||||
|
||||
%hr
|
||||
.row
|
||||
|
@ -67,7 +67,7 @@
|
|||
= image_tag @appearance.logo_path, class: 'appearance-logo-preview'
|
||||
- if @appearance.persisted?
|
||||
%br
|
||||
= link_to _('Remove logo'), logo_admin_appearances_path, data: { confirm: _("Logo will be removed. Are you sure?") }, method: :delete, class: "btn gl-button btn-danger btn-danger-secondary btn-sm remove-logo"
|
||||
= link_to _('Remove logo'), logo_admin_application_settings_appearances_path, data: { confirm: _("Logo will be removed. Are you sure?") }, method: :delete, class: "btn gl-button btn-danger btn-danger-secondary btn-sm remove-logo"
|
||||
%hr
|
||||
= f.hidden_field :logo_cache
|
||||
= f.file_field :logo, class: "", accept: 'image/*'
|
||||
|
@ -106,7 +106,7 @@
|
|||
.mt-4
|
||||
- if @appearance.persisted?
|
||||
Preview last save:
|
||||
= link_to _('Sign-in page'), preview_sign_in_admin_appearances_path, class: 'btn', target: '_blank', rel: 'noopener noreferrer'
|
||||
= link_to _('Sign-in page'), preview_sign_in_admin_application_settings_appearances_path, class: 'btn', target: '_blank', rel: 'noopener noreferrer'
|
||||
= link_to _('New project page'), new_project_path, class: 'btn', target: '_blank', rel: 'noopener noreferrer'
|
||||
|
||||
- if @appearance.updated_at
|
|
@ -37,7 +37,7 @@
|
|||
- search_menu_item = top_nav_search_menu_item_attrs
|
||||
%li.nav-item.d-none.d-lg-block.m-auto
|
||||
= render 'layouts/search' unless current_controller?(:search)
|
||||
%li.nav-item{ class: use_top_nav_redesign ? "gl-display-none gl-sm-display-inline-block gl-lg-display-none" : "gl-display-inline-block gl-lg-display-none" }
|
||||
%li.nav-item{ class: use_top_nav_redesign ? 'd-none d-sm-inline-block d-lg-none' : 'd-inline-block d-lg-none' }
|
||||
= link_to search_menu_item.fetch(:href), title: search_menu_item.fetch(:title), aria: { label: search_menu_item.fetch(:title) }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
|
||||
= sprite_icon(search_menu_item.fetch(:icon))
|
||||
- if header_link?(:issues)
|
||||
|
|
|
@ -234,19 +234,7 @@
|
|||
%strong.fly-out-top-item-name
|
||||
= _('Labels')
|
||||
|
||||
= nav_link(controller: :appearances) do
|
||||
= link_to admin_appearances_path do
|
||||
.nav-icon-container
|
||||
= sprite_icon('appearance')
|
||||
%span.nav-item-name
|
||||
= _('Appearance')
|
||||
%ul.sidebar-sub-level-items.is-fly-out-only
|
||||
= nav_link(controller: :appearances, html_options: { class: "fly-out-top-item" } ) do
|
||||
= link_to admin_appearances_path do
|
||||
%strong.fly-out-top-item-name
|
||||
= _('Appearance')
|
||||
|
||||
= nav_link(controller: [:application_settings, :integrations]) do
|
||||
= nav_link(controller: [:application_settings, :integrations, :appearances]) do
|
||||
= link_to general_admin_application_settings_path, class: 'has-sub-items' do
|
||||
.nav-icon-container
|
||||
= sprite_icon('settings')
|
||||
|
@ -255,7 +243,7 @@
|
|||
|
||||
%ul.sidebar-sub-level-items{ data: { qa_selector: 'admin_sidebar_settings_submenu_content' } }
|
||||
-# This active_nav_link check is also used in `app/views/layouts/admin.html.haml`
|
||||
= nav_link(controller: [:application_settings, :integrations], html_options: { class: "fly-out-top-item" } ) do
|
||||
= nav_link(controller: [:application_settings, :integrations, :appearances], html_options: { class: "fly-out-top-item" } ) do
|
||||
= link_to general_admin_application_settings_path do
|
||||
%strong.fly-out-top-item-name
|
||||
= _('Settings')
|
||||
|
@ -302,6 +290,10 @@
|
|||
= link_to network_admin_application_settings_path, title: _('Network'), data: { qa_selector: 'admin_settings_network_item' } do
|
||||
%span
|
||||
= _('Network')
|
||||
= nav_link(controller: :appearances ) do
|
||||
= link_to admin_application_settings_appearances_path do
|
||||
%span
|
||||
= _('Appearance')
|
||||
= nav_link(path: 'application_settings#preferences') do
|
||||
= link_to preferences_admin_application_settings_path, title: _('Preferences'), data: { qa_selector: 'admin_settings_preferences_link' } do
|
||||
%span
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
- avatar_classes = ['avatar-container', 'rect-avatar', 'group-avatar']
|
||||
- avatar_classes << avatar_size_class
|
||||
|
||||
= link_to group_path(@group), title: @group.name do
|
||||
= link_to group_path(@group), title: @group.name, data: { qa_selector: 'group_scope_link' } do
|
||||
%span{ class: avatar_classes }
|
||||
= group_icon(@group, class: ['avatar', 'avatar-tile', avatar_size_class])
|
||||
%span.sidebar-context-title
|
||||
|
|
|
@ -19,14 +19,14 @@
|
|||
- paths = group_overview_nav_link_paths
|
||||
= nav_link(path: paths, unless: -> { current_path?('groups/contribution_analytics#show') }, html_options: { class: 'home' }) do
|
||||
- information_link = sidebar_refactor_enabled? ? activity_group_path(@group) : group_path(@group)
|
||||
= link_to information_link, class: 'has-sub-items' do
|
||||
= link_to information_link, class: 'has-sub-items', data: { qa_selector: 'group_information_link' } do
|
||||
.nav-icon-container
|
||||
- sprite = sidebar_refactor_enabled? ? 'group' : 'home'
|
||||
= sprite_icon(sprite)
|
||||
%span.nav-item-name
|
||||
= group_information_title(@group)
|
||||
|
||||
%ul.sidebar-sub-level-items
|
||||
%ul.sidebar-sub-level-items{ data: { qa_selector: 'group_information_submenu'} }
|
||||
= nav_link(path: paths, html_options: { class: "fly-out-top-item" } ) do
|
||||
= link_to information_link do
|
||||
%strong.fly-out-top-item-name
|
||||
|
|
|
@ -5,11 +5,13 @@
|
|||
|
||||
- if @conflict
|
||||
.gl-alert.gl-alert-danger.gl-mb-5.gl-mt-5
|
||||
= sprite_icon('error', size: 16, css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
|
||||
.gl-alert-body
|
||||
Someone edited the file the same time you did. Please check out
|
||||
= link_to "the file", project_blob_path(@project, tree_join(@branch_name, @file_path)), target: "_blank", rel: 'noopener noreferrer', class: 'gl-link'
|
||||
and make sure your changes will not unintentionally remove theirs.
|
||||
.gl-alert-container
|
||||
= sprite_icon('error', size: 16, css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
|
||||
.gl-alert-content
|
||||
.gl-alert-body
|
||||
Someone edited the file the same time you did. Please check out
|
||||
= link_to _('the file'), project_blob_path(@project, tree_join(@branch_name, @file_path)), target: "_blank", rel: 'noopener noreferrer', class: 'gl-link'
|
||||
and make sure your changes will not unintentionally remove theirs.
|
||||
|
||||
%h3.page-title.blob-edit-page-title
|
||||
Edit file
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
- page_title _("Value Stream Analytics")
|
||||
- add_page_specific_style 'page_bundles/cycle_analytics'
|
||||
- svgs = { empty_state_svg_path: image_path("illustrations/analytics/cycle-analytics-empty-chart.svg"), no_data_svg_path: image_path("illustrations/analytics/cycle-analytics-empty-chart.svg"), no_access_svg_path: image_path("illustrations/analytics/no-access.svg") }
|
||||
- initial_data = { request_path: project_cycle_analytics_path(@project) }.merge!(svgs)
|
||||
- initial_data = { request_path: project_cycle_analytics_path(@project), full_path: @project.full_path }.merge!(svgs)
|
||||
|
||||
#js-cycle-analytics{ data: initial_data }
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
- avatar_size = sidebar_refactor_disabled? ? 40 : 32
|
||||
- avatar_size_class = sidebar_refactor_disabled? ? 's40' : 's32'
|
||||
|
||||
= link_to scope_menu.link, **scope_menu.container_html_options do
|
||||
= link_to scope_menu.link, **scope_menu.container_html_options, data: { qa_selector: 'project_scope_link' } do
|
||||
%span{ class: ['avatar-container', 'rect-avatar', 'project-avatar', avatar_size_class] }
|
||||
= source_icon(scope_menu.container, alt: scope_menu.title, class: ['avatar', 'avatar-tile', avatar_size_class], width: avatar_size, height: avatar_size)
|
||||
%span.sidebar-context-title
|
||||
|
|
|
@ -24,7 +24,7 @@ module BulkImports
|
|||
end
|
||||
|
||||
def http_client(configuration)
|
||||
@client ||= Clients::Http.new(
|
||||
@client ||= Clients::HTTP.new(
|
||||
uri: configuration.url,
|
||||
token: configuration.access_token
|
||||
)
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: allow_group_deploy_token
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23460
|
||||
rollout_issue_url:
|
||||
milestone: '12.8'
|
||||
type: development
|
||||
group: group::release
|
||||
default_enabled: true
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: ci_track_shared_runner_builds
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62912
|
||||
rollout_issue_url:
|
||||
milestone: '14.0'
|
||||
type: development
|
||||
group: group::pipeline execution
|
||||
default_enabled: false
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: ci_untrack_shared_runner_builds
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62912
|
||||
rollout_issue_url:
|
||||
milestone: '14.0'
|
||||
type: development
|
||||
group: group::pipeline execution
|
||||
default_enabled: false
|
|
@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/326111
|
|||
milestone: '13.11'
|
||||
type: development
|
||||
group: group::editor
|
||||
default_enabled: false
|
||||
default_enabled: true
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: specialized_project_authorization_project_share_worker
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/32864
|
||||
rollout_issue_url:
|
||||
milestone: '13.2'
|
||||
type: development
|
||||
group: group::access
|
||||
default_enabled: true
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: specialized_project_authorization_workers
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/31377
|
||||
rollout_issue_url:
|
||||
milestone: '13.0'
|
||||
type: development
|
||||
group: group::access
|
||||
default_enabled: true
|
|
@ -124,15 +124,6 @@ namespace :admin do
|
|||
end
|
||||
end
|
||||
|
||||
resource :appearances, only: [:show, :create, :update], path: 'appearance' do
|
||||
member do
|
||||
get :preview_sign_in
|
||||
delete :logo
|
||||
delete :header_logos
|
||||
delete :favicon
|
||||
end
|
||||
end
|
||||
|
||||
resource :application_settings, only: :update do
|
||||
resources :services, only: [:index, :edit, :update]
|
||||
resources :integrations, only: [:edit, :update] do
|
||||
|
@ -153,6 +144,15 @@ namespace :admin do
|
|||
get :status_create_self_monitoring_project
|
||||
delete :delete_self_monitoring_project
|
||||
get :status_delete_self_monitoring_project
|
||||
|
||||
resource :appearances, only: [:show, :create, :update], path: 'appearance', module: 'application_settings' do
|
||||
member do
|
||||
get :preview_sign_in
|
||||
delete :logo
|
||||
delete :header_logos
|
||||
delete :favicon
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
resources :plan_limits, only: :create
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddRunningBuildsTable < ActiveRecord::Migration[6.0]
|
||||
def up
|
||||
create_table :ci_running_builds do |t|
|
||||
t.references :build, index: { unique: true }, null: false, foreign_key: { to_table: :ci_builds, on_delete: :cascade }
|
||||
t.references :project, index: true, null: false, foreign_key: { on_delete: :cascade }
|
||||
t.references :runner, index: true, null: false, foreign_key: { to_table: :ci_runners, on_delete: :cascade }
|
||||
t.datetime_with_timezone :created_at, null: false, default: -> { 'NOW()' }
|
||||
t.integer :runner_type, limit: 2, null: false
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
drop_table :ci_running_builds
|
||||
end
|
||||
end
|
|
@ -0,0 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RemoveTemporaryIndexOnSecurityFindingsScanId < ActiveRecord::Migration[6.1]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
INDEX_NAME = 'tmp_index_on_security_findings_scan_id'
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
remove_concurrent_index_by_name :security_findings, INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
add_concurrent_index :security_findings, :scan_id, where: 'uuid is null', name: INDEX_NAME
|
||||
end
|
||||
end
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddProtectedAttributeToPendingBuilds < ActiveRecord::Migration[6.1]
|
||||
def change
|
||||
add_column :ci_pending_builds, :protected, :boolean, null: false, default: false
|
||||
end
|
||||
end
|
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class MigrateProtectedAttributeToPendingBuilds < ActiveRecord::Migration[6.1]
|
||||
include ::Gitlab::Database::DynamicModelHelpers
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
return unless Gitlab.dev_or_test_env? || Gitlab.com?
|
||||
|
||||
each_batch_range('ci_pending_builds', of: 1000) do |min, max|
|
||||
execute <<~SQL
|
||||
UPDATE ci_pending_builds
|
||||
SET protected = true
|
||||
FROM ci_builds
|
||||
WHERE ci_pending_builds.build_id = ci_builds.id
|
||||
AND ci_builds.protected = true
|
||||
AND ci_pending_builds.id BETWEEN #{min} AND #{max}
|
||||
SQL
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
# no op
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddIndexToProtectedPendingBuilds < ActiveRecord::Migration[6.1]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
INDEX_NAME = 'index_ci_pending_builds_id_on_protected_partial'
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_concurrent_index :ci_pending_builds, :id, where: 'protected = true', name: INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index_by_name :ci_pending_builds, INDEX_NAME
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
d4a0098c30cd1acea008fa5f1cfb4c23d5b5b894eab2b72f5004acc5233f2576
|
|
@ -0,0 +1 @@
|
|||
88f16dc06371d320a1245de68aba5ed4ad7cd8f15c4e5898619a751840981072
|
|
@ -0,0 +1 @@
|
|||
dab13c78f6f758c63be923277c0f31e4cce4e30f77a8dc2983a9bb1500a454f9
|
|
@ -0,0 +1 @@
|
|||
ce21070d44a34081c6babd14e6a1b607bad5ed9047b18f4ef0beb64b5a2ce120
|
|
@ -0,0 +1 @@
|
|||
3ad279a7c57e433a8ee349dabd2536c1de9055936b05c26b5469606067eb90d4
|
|
@ -10799,7 +10799,8 @@ CREATE TABLE ci_pending_builds (
|
|||
id bigint NOT NULL,
|
||||
build_id bigint NOT NULL,
|
||||
project_id bigint NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
protected boolean DEFAULT false NOT NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE ci_pending_builds_id_seq
|
||||
|
@ -11150,6 +11151,24 @@ CREATE SEQUENCE ci_runners_id_seq
|
|||
|
||||
ALTER SEQUENCE ci_runners_id_seq OWNED BY ci_runners.id;
|
||||
|
||||
CREATE TABLE ci_running_builds (
|
||||
id bigint NOT NULL,
|
||||
build_id bigint NOT NULL,
|
||||
project_id bigint NOT NULL,
|
||||
runner_id bigint NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
runner_type smallint NOT NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE ci_running_builds_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
ALTER SEQUENCE ci_running_builds_id_seq OWNED BY ci_running_builds.id;
|
||||
|
||||
CREATE TABLE ci_sources_pipelines (
|
||||
id integer NOT NULL,
|
||||
project_id integer,
|
||||
|
@ -19686,6 +19705,8 @@ ALTER TABLE ONLY ci_runner_projects ALTER COLUMN id SET DEFAULT nextval('ci_runn
|
|||
|
||||
ALTER TABLE ONLY ci_runners ALTER COLUMN id SET DEFAULT nextval('ci_runners_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY ci_running_builds ALTER COLUMN id SET DEFAULT nextval('ci_running_builds_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY ci_sources_pipelines ALTER COLUMN id SET DEFAULT nextval('ci_sources_pipelines_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY ci_sources_projects ALTER COLUMN id SET DEFAULT nextval('ci_sources_projects_id_seq'::regclass);
|
||||
|
@ -20891,6 +20912,9 @@ ALTER TABLE ONLY ci_runner_projects
|
|||
ALTER TABLE ONLY ci_runners
|
||||
ADD CONSTRAINT ci_runners_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY ci_running_builds
|
||||
ADD CONSTRAINT ci_running_builds_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY ci_sources_pipelines
|
||||
ADD CONSTRAINT ci_sources_pipelines_pkey PRIMARY KEY (id);
|
||||
|
||||
|
@ -22776,6 +22800,8 @@ CREATE INDEX index_ci_minutes_additional_packs_on_namespace_id_purchase_xid ON c
|
|||
|
||||
CREATE UNIQUE INDEX index_ci_namespace_monthly_usages_on_namespace_id_and_date ON ci_namespace_monthly_usages USING btree (namespace_id, date);
|
||||
|
||||
CREATE INDEX index_ci_pending_builds_id_on_protected_partial ON ci_pending_builds USING btree (id) WHERE (protected = true);
|
||||
|
||||
CREATE UNIQUE INDEX index_ci_pending_builds_on_build_id ON ci_pending_builds USING btree (build_id);
|
||||
|
||||
CREATE INDEX index_ci_pending_builds_on_project_id ON ci_pending_builds USING btree (project_id);
|
||||
|
@ -22886,6 +22912,12 @@ CREATE INDEX index_ci_runners_on_token ON ci_runners USING btree (token);
|
|||
|
||||
CREATE INDEX index_ci_runners_on_token_encrypted ON ci_runners USING btree (token_encrypted);
|
||||
|
||||
CREATE UNIQUE INDEX index_ci_running_builds_on_build_id ON ci_running_builds USING btree (build_id);
|
||||
|
||||
CREATE INDEX index_ci_running_builds_on_project_id ON ci_running_builds USING btree (project_id);
|
||||
|
||||
CREATE INDEX index_ci_running_builds_on_runner_id ON ci_running_builds USING btree (runner_id);
|
||||
|
||||
CREATE INDEX index_ci_sources_pipelines_on_pipeline_id ON ci_sources_pipelines USING btree (pipeline_id);
|
||||
|
||||
CREATE INDEX index_ci_sources_pipelines_on_project_id ON ci_sources_pipelines USING btree (project_id);
|
||||
|
@ -25028,8 +25060,6 @@ CREATE INDEX tmp_idx_deduplicate_vulnerability_occurrences ON vulnerability_occu
|
|||
|
||||
CREATE INDEX tmp_idx_on_namespaces_delayed_project_removal ON namespaces USING btree (id) WHERE (delayed_project_removal = true);
|
||||
|
||||
CREATE INDEX tmp_index_on_security_findings_scan_id ON security_findings USING btree (scan_id) WHERE (uuid IS NULL);
|
||||
|
||||
CREATE INDEX tmp_index_on_vulnerabilities_non_dismissed ON vulnerabilities USING btree (id) WHERE (state <> 2);
|
||||
|
||||
CREATE UNIQUE INDEX uniq_pkgs_deb_grp_architectures_on_distribution_id_and_name ON packages_debian_group_architectures USING btree (distribution_id, name);
|
||||
|
@ -26708,6 +26738,9 @@ ALTER TABLE ONLY vulnerability_scanners
|
|||
ALTER TABLE ONLY reviews
|
||||
ADD CONSTRAINT fk_rails_5ca11d8c31 FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY ci_running_builds
|
||||
ADD CONSTRAINT fk_rails_5ca491d360 FOREIGN KEY (runner_id) REFERENCES ci_runners(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY epic_issues
|
||||
ADD CONSTRAINT fk_rails_5d942936b4 FOREIGN KEY (epic_id) REFERENCES epics(id) ON DELETE CASCADE;
|
||||
|
||||
|
@ -27413,6 +27446,9 @@ ALTER TABLE ONLY geo_hashed_storage_attachments_events
|
|||
ALTER TABLE ONLY merge_request_reviewers
|
||||
ADD CONSTRAINT fk_rails_d9fec24b9d FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY ci_running_builds
|
||||
ADD CONSTRAINT fk_rails_da45cfa165 FOREIGN KEY (build_id) REFERENCES ci_builds(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY jira_imports
|
||||
ADD CONSTRAINT fk_rails_da617096ce FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL;
|
||||
|
||||
|
@ -27425,6 +27461,9 @@ ALTER TABLE ONLY issues_prometheus_alert_events
|
|||
ALTER TABLE ONLY board_user_preferences
|
||||
ADD CONSTRAINT fk_rails_dbebdaa8fe FOREIGN KEY (board_id) REFERENCES boards(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY ci_running_builds
|
||||
ADD CONSTRAINT fk_rails_dc1d0801e8 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY vulnerability_occurrence_pipelines
|
||||
ADD CONSTRAINT fk_rails_dc3ae04693 FOREIGN KEY (occurrence_id) REFERENCES vulnerability_occurrences(id) ON DELETE CASCADE;
|
||||
|
||||
|
|
|
@ -1171,7 +1171,7 @@ POST /projects
|
|||
| `ci_config_path` | string | **{dotted-circle}** No | The path to CI configuration file. |
|
||||
| `container_expiration_policy_attributes` | hash | **{dotted-circle}** No | Update the image cleanup policy for this project. Accepts: `cadence` (string), `keep_n` (integer), `older_than` (string), `name_regex` (string), `name_regex_delete` (string), `name_regex_keep` (string), `enabled` (boolean). Valid values for `cadence` are: `1d` (every day), `7d` (every week), `14d` (every two weeks), `1month` (every month), or `3month` (every quarter). |
|
||||
| `container_registry_enabled` | boolean | **{dotted-circle}** No | Enable container registry for this project. |
|
||||
| `default_branch` | string | **{dotted-circle}** No | The [default branch](../user/project/repository/branches/default.md) name. |
|
||||
| `default_branch` | string | **{dotted-circle}** No | The [default branch](../user/project/repository/branches/default.md) name. Requires `initialize_with_readme` to be `true`. |
|
||||
| `description` | string | **{dotted-circle}** No | Short project description. |
|
||||
| `emails_disabled` | boolean | **{dotted-circle}** No | Disable email notifications. |
|
||||
| `external_authorization_classification_label` **(PREMIUM)** | string | **{dotted-circle}** No | The classification label for the project. |
|
||||
|
@ -1246,6 +1246,7 @@ POST /projects/user/:user_id
|
|||
| `ci_config_path` | string | **{dotted-circle}** No | The path to CI configuration file. |
|
||||
| `container_registry_enabled` | boolean | **{dotted-circle}** No | Enable container registry for this project. |
|
||||
| `description` | string | **{dotted-circle}** No | Short project description. |
|
||||
| `default_branch` | string | **{dotted-circle}** No | The [default branch](../user/project/repository/branches/default.md) name. Requires `initialize_with_readme` to be `true`. |
|
||||
| `emails_disabled` | boolean | **{dotted-circle}** No | Disable email notifications. |
|
||||
| `external_authorization_classification_label` **(PREMIUM)** | string | **{dotted-circle}** No | The classification label for the project. |
|
||||
| `forking_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
|
||||
|
|
|
@ -557,6 +557,7 @@ request, be sure to start the `dont-interrupt-me` job before pushing.
|
|||
1. These cache definitions are composed of [multiple atomic caches](../ci/yaml/README.md#multiple-caches).
|
||||
1. Only 6 specific jobs, running in 2-hourly scheduled pipelines, are pushing (i.e. updating) to the caches:
|
||||
- `update-setup-test-env-cache`, defined in [`.gitlab/ci/rails.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/rails.gitlab-ci.yml).
|
||||
- `update-gitaly-binaries-cache`, defined in [`.gitlab/ci/rails.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/rails.gitlab-ci.yml).
|
||||
- `update-static-analysis-cache`, defined in [`.gitlab/ci/rails.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/rails.gitlab-ci.yml).
|
||||
- `update-qa-cache`, defined in [`.gitlab/ci/qa.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/qa.gitlab-ci.yml).
|
||||
- `update-assets-compile-production-cache`, defined in [`.gitlab/ci/frontend.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/frontend.gitlab-ci.yml).
|
||||
|
|
|
@ -16,15 +16,25 @@ staging and canary deployments,
|
|||
|
||||
## Custom buildpacks
|
||||
|
||||
If the automatic buildpack detection fails for your project, or if you want to
|
||||
use a custom buildpack, you can override the buildpack using a project CI/CD variable
|
||||
or a `.buildpacks` file in your project:
|
||||
If the automatic buildpack detection fails for your project, or if you
|
||||
need more control over your build, you can customize the buildpacks
|
||||
used for the build.
|
||||
|
||||
- **Project variable** - Create a project variable `BUILDPACK_URL` with the URL
|
||||
of the buildpack to use.
|
||||
- **`.buildpacks` file** - Add a file in your project's repository called `.buildpacks`,
|
||||
and add the URL of the buildpack to use on a line in the file. If you want to
|
||||
use multiple buildpacks, enter one buildpack per line.
|
||||
### Custom buildpacks with Cloud Native Buildpacks
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28165) in GitLab 12.10.
|
||||
|
||||
Specify either:
|
||||
|
||||
- The CI/CD variable `BUILDPACK_URL` according to [`pack`'s specifications](https://buildpacks.io/docs/app-developer-guide/specific-buildpacks/).
|
||||
- A [`project.toml` project descriptor](https://buildpacks.io/docs/app-developer-guide/using-project-descriptor/) with the buildpacks you would like to include.
|
||||
|
||||
### Custom buildpacks with Herokuish
|
||||
|
||||
Specify either:
|
||||
|
||||
- The CI/CD variable `BUILDPACK_URL`.
|
||||
- A `.buildpacks` file at the root of your project, containing one buildpack URL per line.
|
||||
|
||||
The buildpack URL can point to either a Git repository URL or a tarball URL.
|
||||
For Git repositories, you can point to a specific Git reference (such as
|
||||
|
@ -347,8 +357,8 @@ applications.
|
|||
| `ADDITIONAL_HOSTS` | Fully qualified domain names specified as a comma-separated list that are added to the Ingress hosts. |
|
||||
| `<ENVIRONMENT>_ADDITIONAL_HOSTS` | For a specific environment, the fully qualified domain names specified as a comma-separated list that are added to the Ingress hosts. This takes precedence over `ADDITIONAL_HOSTS`. |
|
||||
| `AUTO_DEVOPS_ATOMIC_RELEASE` | As of GitLab 13.0, Auto DevOps uses [`--atomic`](https://v2.helm.sh/docs/helm/#options-43) for Helm deployments by default. Set this variable to `false` to disable the use of `--atomic` |
|
||||
| `AUTO_DEVOPS_BUILD_IMAGE_CNB_ENABLED` | When set to a non-empty value and no `Dockerfile` is present, Auto Build builds your application using Cloud Native Buildpacks instead of Herokuish. [More details](stages.md#auto-build-using-cloud-native-buildpacks-beta). |
|
||||
| `AUTO_DEVOPS_BUILD_IMAGE_CNB_BUILDER` | The builder used when building with Cloud Native Buildpacks. The default builder is `heroku/buildpacks:18`. [More details](stages.md#auto-build-using-cloud-native-buildpacks-beta). |
|
||||
| `AUTO_DEVOPS_BUILD_IMAGE_CNB_ENABLED` | Set to `false` to use Herokuish instead of Cloud Native Buildpacks with Auto Build. [More details](stages.md#auto-build-using-cloud-native-buildpacks). |
|
||||
| `AUTO_DEVOPS_BUILD_IMAGE_CNB_BUILDER` | The builder used when building with Cloud Native Buildpacks. The default builder is `heroku/buildpacks:18`. [More details](stages.md#auto-build-using-cloud-native-buildpacks). |
|
||||
| `AUTO_DEVOPS_BUILD_IMAGE_EXTRA_ARGS` | Extra arguments to be passed to the `docker build` command. Note that using quotes doesn't prevent word splitting. [More details](#passing-arguments-to-docker-build). |
|
||||
| `AUTO_DEVOPS_BUILD_IMAGE_FORWARDED_CI_VARIABLES` | A [comma-separated list of CI/CD variable names](#forward-cicd-variables-to-the-build-environment) to be forwarded to the build environment (the buildpack builder or `docker build`). |
|
||||
| `AUTO_DEVOPS_CHART` | Helm Chart used to deploy your apps. Defaults to the one [provided by GitLab](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/tree/master/assets/auto-deploy-app). |
|
||||
|
@ -358,7 +368,7 @@ applications.
|
|||
| `AUTO_DEVOPS_CHART_REPOSITORY_PASSWORD` | From GitLab 11.11, used to set a password to connect to the Helm repository. Defaults to no credentials. Also set `AUTO_DEVOPS_CHART_REPOSITORY_USERNAME`. |
|
||||
| `AUTO_DEVOPS_DEPLOY_DEBUG` | From GitLab 13.1, if this variable is present, Helm outputs debug logs. |
|
||||
| `AUTO_DEVOPS_ALLOW_TO_FORCE_DEPLOY_V<N>` | From [auto-deploy-image](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image) v1.0.0, if this variable is present, a new major version of chart is forcibly deployed. For more information, see [Ignore warnings and continue deploying](upgrading_auto_deploy_dependencies.md#ignore-warnings-and-continue-deploying). |
|
||||
| `BUILDPACK_URL` | Buildpack's full URL. Can point to either [a Git repository URL or a tarball URL](#custom-buildpacks). |
|
||||
| `BUILDPACK_URL` | Buildpack's full URL. [Must point to a URL supported by Pack or Herokuish](#custom-buildpacks). |
|
||||
| `CANARY_ENABLED` | From GitLab 11.0, used to define a [deploy policy for canary environments](#deploy-policy-for-canary-environments). |
|
||||
| `CANARY_PRODUCTION_REPLICAS` | Number of canary replicas to deploy for [Canary Deployments](../../user/project/canary_deployments.md) in the production environment. Takes precedence over `CANARY_REPLICAS`. Defaults to 1. |
|
||||
| `CANARY_REPLICAS` | Number of canary replicas to deploy for [Canary Deployments](../../user/project/canary_deployments.md). Defaults to 1. |
|
||||
|
|
|
@ -33,15 +33,24 @@ your own `Dockerfile`, you must either:
|
|||
- Override the default values by
|
||||
[customizing the Auto Deploy Helm chart](customize.md#custom-helm-chart).
|
||||
|
||||
### Auto Build using Heroku buildpacks
|
||||
### Auto Build using Cloud Native Buildpacks
|
||||
|
||||
> - Introduced in [GitLab 12.10](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28165).
|
||||
> - Auto Build using Cloud Native Buildpacks by default was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63351) in GitLab 14.0.
|
||||
|
||||
Auto Build builds an application using a project's `Dockerfile` if present. If no
|
||||
`Dockerfile` is present, it uses [Herokuish](https://github.com/gliderlabs/herokuish)
|
||||
and [Heroku buildpacks](https://devcenter.heroku.com/articles/buildpacks)
|
||||
to detect and build the application into a Docker image.
|
||||
`Dockerfile` is present, Auto Build builds your application using
|
||||
[Cloud Native Buildpacks](https://buildpacks.io) to detect and build the
|
||||
application into a Docker image. The feature uses the
|
||||
[`pack` command](https://github.com/buildpacks/pack).
|
||||
The default [builder](https://buildpacks.io/docs/concepts/components/builder/)
|
||||
is `heroku/buildpacks:18` but a different builder can be selected using
|
||||
the CI/CD variable `AUTO_DEVOPS_BUILD_IMAGE_CNB_BUILDER`.
|
||||
|
||||
Each buildpack requires your project's repository to contain certain files for
|
||||
Auto Build to build your application successfully. For example, your application's
|
||||
Auto Build to build your application successfully. The structure is
|
||||
specific to the builder and buildpacks you have selected.
|
||||
For example, when using the Heroku's builder (the default), your application's
|
||||
root directory must contain the appropriate file for your application's
|
||||
language:
|
||||
|
||||
|
@ -51,40 +60,39 @@ language:
|
|||
For the requirements of other languages and frameworks, read the
|
||||
[Heroku buildpacks documentation](https://devcenter.heroku.com/articles/buildpacks#officially-supported-buildpacks).
|
||||
|
||||
NOTE:
|
||||
Auto Test still uses Herokuish, as test suite detection is not
|
||||
yet part of the Cloud Native Buildpack specification. For more information, see
|
||||
[this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/212689).
|
||||
|
||||
### Auto Build using Herokuish
|
||||
|
||||
> [Replaced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63351) with Cloud Native Buildpacks in GitLab 14.0.
|
||||
|
||||
Prior to GitLab 14.0, [Herokuish](https://github.com/gliderlabs/herokuish) was
|
||||
the default build method for projects without a `Dockerfile`. Herokuish can
|
||||
still be used by setting the CI/CD variable `AUTO_DEVOPS_BUILD_IMAGE_CNB_ENABLED`
|
||||
to `false`.
|
||||
|
||||
NOTE:
|
||||
If Auto Build fails despite the project meeting the buildpack requirements, set
|
||||
a project CI/CD variable `TRACE=true` to enable verbose logging, which may help you
|
||||
troubleshoot.
|
||||
|
||||
### Auto Build using Cloud Native Buildpacks (beta)
|
||||
|
||||
> Introduced in [GitLab 12.10](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28165).
|
||||
|
||||
Auto Build supports building your application using [Cloud Native Buildpacks](https://buildpacks.io)
|
||||
through the [`pack` command](https://github.com/buildpacks/pack). To use Cloud Native Buildpacks,
|
||||
set the CI/CD variable `AUTO_DEVOPS_BUILD_IMAGE_CNB_ENABLED` to a non-empty
|
||||
value. The default builder is `heroku/buildpacks:18` but a different builder
|
||||
can be selected using the CI/CD variable `AUTO_DEVOPS_BUILD_IMAGE_CNB_BUILDER`.
|
||||
|
||||
Cloud Native Buildpacks (CNBs) are an evolution of Heroku buildpacks, and
|
||||
GitLab expects them to eventually supersede Herokuish-based builds within Auto DevOps. For more
|
||||
information, see [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/212692).
|
||||
### Moving from Herokuish to Cloud Native Buildpacks
|
||||
|
||||
Builds using Cloud Native Buildpacks support the same options as builds using
|
||||
Heroku buildpacks, with the following caveats:
|
||||
Herokuish, with the following caveats:
|
||||
|
||||
- The buildpack must be a Cloud Native Buildpack. A Heroku buildpack can be
|
||||
converted to a Cloud Native Buildpack using Heroku's
|
||||
[`cnb-shim`](https://github.com/heroku/cnb-shim).
|
||||
- `BUILDPACK_URL` must be in a form
|
||||
- `BUILDPACK_URL` must be in a format
|
||||
[supported by `pack`](https://buildpacks.io/docs/app-developer-guide/specific-buildpacks/).
|
||||
- The `/bin/herokuish` command is not present in the resulting image, and prefixing
|
||||
- The `/bin/herokuish` command is not present in the built image, and prefixing
|
||||
commands with `/bin/herokuish procfile exec` is no longer required (nor possible).
|
||||
|
||||
NOTE:
|
||||
Auto Test still uses Herokuish, as test suite detection is not
|
||||
yet part of the Cloud Native Buildpack specification. For more information, see
|
||||
[this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/212689).
|
||||
Instead, custom commands should be prefixed with `/cnb/lifecycle/launcher`
|
||||
to receive the correct execution environment.
|
||||
|
||||
## Auto Test
|
||||
|
||||
|
@ -461,15 +469,16 @@ If present, `DB_MIGRATE` is run as a shell command within an application pod as
|
|||
a Helm pre-upgrade hook.
|
||||
|
||||
For example, in a Rails application in an image built with
|
||||
[Herokuish](https://github.com/gliderlabs/herokuish):
|
||||
[Cloud Native Buildpacks](#auto-build-using-cloud-native-buildpacks):
|
||||
|
||||
- `DB_INITIALIZE` can be set to `RAILS_ENV=production /bin/herokuish procfile exec bin/rails db:setup`
|
||||
- `DB_MIGRATE` can be set to `RAILS_ENV=production /bin/herokuish procfile exec bin/rails db:migrate`
|
||||
- `DB_INITIALIZE` can be set to `RAILS_ENV=production /cnb/lifecycle/launcher bin/rails db:setup`
|
||||
- `DB_MIGRATE` can be set to `RAILS_ENV=production /cnb/lifecycle/launcher bin/rails db:migrate`
|
||||
|
||||
Unless your repository contains a `Dockerfile`, your image is built with
|
||||
Herokuish, and you must prefix commands run in these images with
|
||||
`/bin/herokuish procfile exec` (for Herokuish) or `/cnb/lifecycle/launcher`
|
||||
(for Cloud Native Buildpacks) to replicate the environment where your
|
||||
Cloud Native Buildpacks, and you must prefix commands run in these images with
|
||||
`/cnb/lifecycle/launcher`, (or `/bin/herokuish procfile exec` when
|
||||
using [Herokuish](#auto-build-using-herokuish))
|
||||
to replicate the environment where your
|
||||
application runs.
|
||||
|
||||
### Upgrade auto-deploy-app Chart
|
||||
|
@ -508,14 +517,10 @@ workers:
|
|||
sidekiq:
|
||||
replicaCount: 1
|
||||
command:
|
||||
- /bin/herokuish
|
||||
- procfile
|
||||
- exec
|
||||
- /cnb/lifecycle/launcher
|
||||
- sidekiq
|
||||
preStopCommand:
|
||||
- /bin/herokuish
|
||||
- procfile
|
||||
- exec
|
||||
- /cnb/lifecycle/launcher
|
||||
- sidekiqctl
|
||||
- quiet
|
||||
terminationGracePeriodSeconds: 60
|
||||
|
|
|
@ -9,7 +9,7 @@ disqus_identifier: 'https://docs.gitlab.com/ee/customization/branded_login_page.
|
|||
# GitLab Appearance **(FREE SELF)**
|
||||
|
||||
There are several options for customizing the appearance of a self-managed instance
|
||||
of GitLab. These settings are accessed from the **Admin Area** in the **Appearance**
|
||||
of GitLab. These settings are accessed from the **Admin Area** in the **Settings > Appearance**
|
||||
section.
|
||||
|
||||
## Navigation bar
|
||||
|
|
|
@ -5,16 +5,22 @@ module API
|
|||
class Snippet < BasicSnippet
|
||||
expose :author, using: Entities::UserBasic
|
||||
expose :file_name do |snippet|
|
||||
snippet.file_name_on_repo || snippet.file_name
|
||||
snippet_files.first || snippet.file_name
|
||||
end
|
||||
expose :files do |snippet, options|
|
||||
snippet.list_files.map do |file|
|
||||
snippet_files.map do |file|
|
||||
{
|
||||
path: file,
|
||||
raw_url: Gitlab::UrlBuilder.build(snippet, file: file, ref: snippet.repository.root_ref)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def snippet_files
|
||||
@snippet_files ||= object.list_files
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -234,6 +234,7 @@ module API
|
|||
params do
|
||||
optional :name, type: String, desc: 'The name of the project'
|
||||
optional :path, type: String, desc: 'The path of the repository'
|
||||
optional :default_branch, type: String, desc: 'The default branch of the project'
|
||||
at_least_one_of :name, :path
|
||||
use :optional_create_project_params
|
||||
use :create_params
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module BulkImports
|
||||
module Clients
|
||||
class Http
|
||||
class HTTP
|
||||
API_VERSION = 'v4'
|
||||
DEFAULT_PAGE = 1
|
||||
DEFAULT_PER_PAGE = 30
|
||||
|
|
|
@ -24,7 +24,7 @@ module BulkImports
|
|||
attr_reader :query
|
||||
|
||||
def http_client(configuration)
|
||||
@http_client ||= BulkImports::Clients::Http.new(
|
||||
@http_client ||= BulkImports::Clients::HTTP.new(
|
||||
uri: configuration.url,
|
||||
token: configuration.access_token,
|
||||
per_page: 100
|
||||
|
|
|
@ -17,7 +17,7 @@ module BulkImports
|
|||
private
|
||||
|
||||
def http_client(configuration)
|
||||
@http_client ||= BulkImports::Clients::Http.new(
|
||||
@http_client ||= BulkImports::Clients::HTTP.new(
|
||||
uri: configuration.url,
|
||||
token: configuration.access_token,
|
||||
per_page: 100
|
||||
|
|
|
@ -33,7 +33,9 @@ module Gitlab
|
|||
:queue_replication_lag,
|
||||
:runner_pre_assign_checks_failed,
|
||||
:runner_pre_assign_checks_success,
|
||||
:runner_queue_tick
|
||||
:runner_queue_tick,
|
||||
:shared_runner_build_new,
|
||||
:shared_runner_build_done
|
||||
].to_set.freeze
|
||||
|
||||
QUEUE_DEPTH_HISTOGRAMS = [
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
build:
|
||||
stage: build
|
||||
image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-build-image:v0.6.0"
|
||||
image: 'registry.gitlab.com/gitlab-org/cluster-integration/auto-build-image:v1.0.0'
|
||||
variables:
|
||||
DOCKER_TLS_CERTDIR: ""
|
||||
DOCKER_TLS_CERTDIR: ''
|
||||
services:
|
||||
- name: "docker:20.10.6-dind"
|
||||
- name: 'docker:20.10.6-dind'
|
||||
command: ['--tls=false', '--host=tcp://0.0.0.0:2375']
|
||||
script:
|
||||
- |
|
||||
|
|
|
@ -32967,6 +32967,9 @@ msgstr ""
|
|||
msgid "There was an error while fetching value stream analytics duration data."
|
||||
msgstr ""
|
||||
|
||||
msgid "There was an error while fetching value stream summary data."
|
||||
msgstr ""
|
||||
|
||||
msgid "There was an error with the reCAPTCHA. Please solve the reCAPTCHA again."
|
||||
msgstr ""
|
||||
|
||||
|
@ -39385,6 +39388,9 @@ msgstr ""
|
|||
msgid "the correct format."
|
||||
msgstr ""
|
||||
|
||||
msgid "the file"
|
||||
msgstr ""
|
||||
|
||||
msgid "the following issue(s)"
|
||||
msgstr ""
|
||||
|
||||
|
|
28
qa/qa.rb
28
qa/qa.rb
|
@ -338,7 +338,7 @@ module QA
|
|||
autoload :Jenkins, 'qa/page/project/settings/services/jenkins'
|
||||
autoload :Prometheus, 'qa/page/project/settings/services/prometheus'
|
||||
end
|
||||
autoload :Operations, 'qa/page/project/settings/operations'
|
||||
autoload :Monitor, 'qa/page/project/settings/monitor'
|
||||
autoload :Alerts, 'qa/page/project/settings/alerts'
|
||||
autoload :Integrations, 'qa/page/project/settings/integrations'
|
||||
end
|
||||
|
@ -347,7 +347,9 @@ module QA
|
|||
autoload :CiCd, 'qa/page/project/sub_menus/ci_cd'
|
||||
autoload :Common, 'qa/page/project/sub_menus/common'
|
||||
autoload :Issues, 'qa/page/project/sub_menus/issues'
|
||||
autoload :Operations, 'qa/page/project/sub_menus/operations'
|
||||
autoload :Monitor, 'qa/page/project/sub_menus/monitor'
|
||||
autoload :Deployments, 'qa/page/project/sub_menus/deployments'
|
||||
autoload :Infrastructure, 'qa/page/project/sub_menus/infrastructure'
|
||||
autoload :Repository, 'qa/page/project/sub_menus/repository'
|
||||
autoload :Settings, 'qa/page/project/sub_menus/settings'
|
||||
autoload :Project, 'qa/page/project/sub_menus/project'
|
||||
|
@ -370,25 +372,29 @@ module QA
|
|||
autoload :Index, 'qa/page/project/milestone/index'
|
||||
end
|
||||
|
||||
module Operations
|
||||
module Deployments
|
||||
module Environments
|
||||
autoload :Index, 'qa/page/project/operations/environments/index'
|
||||
autoload :Show, 'qa/page/project/operations/environments/show'
|
||||
autoload :Index, 'qa/page/project/deployments/environments/index'
|
||||
autoload :Show, 'qa/page/project/deployments/environments/show'
|
||||
end
|
||||
end
|
||||
|
||||
module Infrastructure
|
||||
module Kubernetes
|
||||
autoload :Index, 'qa/page/project/operations/kubernetes/index'
|
||||
autoload :Add, 'qa/page/project/operations/kubernetes/add'
|
||||
autoload :AddExisting, 'qa/page/project/operations/kubernetes/add_existing'
|
||||
autoload :Show, 'qa/page/project/operations/kubernetes/show'
|
||||
autoload :Index, 'qa/page/project/infrastructure/kubernetes/index'
|
||||
autoload :Add, 'qa/page/project/infrastructure/kubernetes/add'
|
||||
autoload :AddExisting, 'qa/page/project/infrastructure/kubernetes/add_existing'
|
||||
autoload :Show, 'qa/page/project/infrastructure/kubernetes/show'
|
||||
end
|
||||
end
|
||||
|
||||
module Monitor
|
||||
module Metrics
|
||||
autoload :Show, 'qa/page/project/operations/metrics/show'
|
||||
autoload :Show, 'qa/page/project/monitor/metrics/show'
|
||||
end
|
||||
|
||||
module Incidents
|
||||
autoload :Index, 'qa/page/project/operations/incidents/index'
|
||||
autoload :Index, 'qa/page/project/monitor/incidents/index'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@ module QA
|
|||
element :group_members_item
|
||||
element :group_milestones_link
|
||||
element :group_settings
|
||||
element :group_information_link
|
||||
element :group_information_submenu
|
||||
end
|
||||
|
||||
view 'app/views/groups/sidebar/_packages_settings.html.haml' do
|
||||
|
@ -24,8 +26,10 @@ module QA
|
|||
end
|
||||
|
||||
def click_group_members_item
|
||||
within_sidebar do
|
||||
click_element(:group_members_item)
|
||||
hover_element(:group_information_link) do
|
||||
within_submenu(:group_information_submenu) do
|
||||
click_element(:group_members_item)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module QA
|
||||
module Page
|
||||
module Project
|
||||
module Operations
|
||||
module Deployments
|
||||
module Environments
|
||||
class Index < Page::Base
|
||||
view 'app/assets/javascripts/environments/components/environment_item.vue' do
|
|
@ -3,7 +3,7 @@
|
|||
module QA
|
||||
module Page
|
||||
module Project
|
||||
module Operations
|
||||
module Deployments
|
||||
module Environments
|
||||
class Show < Page::Base
|
||||
view 'app/views/projects/environments/_external_url.html.haml' do
|
|
@ -3,7 +3,7 @@
|
|||
module QA
|
||||
module Page
|
||||
module Project
|
||||
module Operations
|
||||
module Infrastructure
|
||||
module Kubernetes
|
||||
class Add < Page::Base
|
||||
view 'app/views/clusters/clusters/new.html.haml' do
|
|
@ -3,7 +3,7 @@
|
|||
module QA
|
||||
module Page
|
||||
module Project
|
||||
module Operations
|
||||
module Infrastructure
|
||||
module Kubernetes
|
||||
class AddExisting < Page::Base
|
||||
view 'app/views/clusters/clusters/user/_form.html.haml' do
|
||||
|
@ -32,7 +32,7 @@ module QA
|
|||
end
|
||||
|
||||
def add_cluster!
|
||||
click_element :add_kubernetes_cluster_button, Page::Project::Operations::Kubernetes::Show
|
||||
click_element :add_kubernetes_cluster_button, Page::Project::Infrastructure::Kubernetes::Show
|
||||
end
|
||||
|
||||
def uncheck_rbac!
|
|
@ -3,7 +3,7 @@
|
|||
module QA
|
||||
module Page
|
||||
module Project
|
||||
module Operations
|
||||
module Infrastructure
|
||||
module Kubernetes
|
||||
class Index < Page::Base
|
||||
view 'app/views/clusters/clusters/_empty_state.html.haml' do
|
|
@ -3,7 +3,7 @@
|
|||
module QA
|
||||
module Page
|
||||
module Project
|
||||
module Operations
|
||||
module Infrastructure
|
||||
module Kubernetes
|
||||
class Show < Page::Base
|
||||
view 'app/assets/javascripts/clusters/components/applications.vue' do
|
||||
|
@ -77,7 +77,7 @@ module QA
|
|||
end
|
||||
|
||||
def save_domain
|
||||
click_element :save_changes_button, Page::Project::Operations::Kubernetes::Show
|
||||
click_element :save_changes_button, Page::Project::Infrastructure::Kubernetes::Show
|
||||
end
|
||||
|
||||
def wait_for_cluster_health
|
|
@ -8,7 +8,9 @@ module QA
|
|||
include SubMenus::Project
|
||||
include SubMenus::CiCd
|
||||
include SubMenus::Issues
|
||||
include SubMenus::Operations
|
||||
include SubMenus::Deployments
|
||||
include SubMenus::Monitor
|
||||
include SubMenus::Infrastructure
|
||||
include SubMenus::Repository
|
||||
include SubMenus::Settings
|
||||
include SubMenus::Packages
|
||||
|
@ -26,8 +28,10 @@ module QA
|
|||
end
|
||||
|
||||
def click_activity
|
||||
within_sidebar do
|
||||
click_element(:sidebar_menu_item_link, menu_item: 'Activity')
|
||||
hover_project_information do
|
||||
within_submenu do
|
||||
click_element(:sidebar_menu_item_link, menu_item: 'Activity')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -38,8 +42,21 @@ module QA
|
|||
end
|
||||
|
||||
def click_members
|
||||
hover_project_information do
|
||||
within_submenu do
|
||||
click_element(:sidebar_menu_item_link, menu_item: 'Members')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def hover_project_information
|
||||
within_sidebar do
|
||||
click_element(:sidebar_menu_link, menu_item: 'Members')
|
||||
scroll_to_element(:sidebar_menu_link, menu_item: 'Project information')
|
||||
find_element(:sidebar_menu_link, menu_item: 'Project information').hover
|
||||
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module QA
|
||||
module Page
|
||||
module Project
|
||||
module Operations
|
||||
module Monitor
|
||||
module Incidents
|
||||
class Index < Page::Base
|
||||
view 'app/assets/javascripts/incidents/components/incidents_list.vue' do
|
|
@ -5,7 +5,7 @@ require 'securerandom'
|
|||
module QA
|
||||
module Page
|
||||
module Project
|
||||
module Operations
|
||||
module Monitor
|
||||
module Metrics
|
||||
class Show < Page::Base
|
||||
EXPECTED_TITLE = 'Memory Usage (Total)'
|
||||
|
@ -134,4 +134,4 @@ module QA
|
|||
end
|
||||
end
|
||||
|
||||
QA::Page::Project::Operations::Metrics::Show.prepend_mod_with('Page::Project::Operations::Metrics::Show', namespace: QA)
|
||||
QA::Page::Project::Monitor::Metrics::Show.prepend_mod_with('Page::Project::Monitor::Metrics::Show', namespace: QA)
|
|
@ -4,7 +4,7 @@ module QA
|
|||
module Page
|
||||
module Project
|
||||
module Settings
|
||||
class Operations < Page::Base
|
||||
class Monitor < Page::Base
|
||||
include QA::Page::Settings::Common
|
||||
|
||||
view 'app/assets/javascripts/incidents_settings/components/incidents_settings_tabs.vue' do
|
|
@ -0,0 +1,40 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
module Page
|
||||
module Project
|
||||
module SubMenus
|
||||
module Deployments
|
||||
extend QA::Page::PageConcern
|
||||
|
||||
def self.included(base)
|
||||
super
|
||||
|
||||
base.class_eval do
|
||||
include QA::Page::Project::SubMenus::Common
|
||||
end
|
||||
end
|
||||
|
||||
def go_to_deployments_environments
|
||||
hover_deployments do
|
||||
within_submenu do
|
||||
click_element(:sidebar_menu_item_link, menu_item: 'Environments')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def hover_deployments
|
||||
within_sidebar do
|
||||
scroll_to_element(:sidebar_menu_link, menu_item: 'Deployments')
|
||||
find_element(:sidebar_menu_link, menu_item: 'Deployments').hover
|
||||
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,40 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
module Page
|
||||
module Project
|
||||
module SubMenus
|
||||
module Infrastructure
|
||||
extend QA::Page::PageConcern
|
||||
|
||||
def self.included(base)
|
||||
super
|
||||
|
||||
base.class_eval do
|
||||
include QA::Page::Project::SubMenus::Common
|
||||
end
|
||||
end
|
||||
|
||||
def go_to_infrastructure_kubernetes
|
||||
hover_infrastructure do
|
||||
within_submenu do
|
||||
click_link('Kubernetes clusters')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def hover_infrastructure
|
||||
within_sidebar do
|
||||
scroll_to_element(:sidebar_menu_link, menu_item: 'Infrastructure')
|
||||
find_element(:sidebar_menu_link, menu_item: 'Infrastructure').hover
|
||||
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -4,7 +4,7 @@ module QA
|
|||
module Page
|
||||
module Project
|
||||
module SubMenus
|
||||
module Operations
|
||||
module Monitor
|
||||
extend QA::Page::PageConcern
|
||||
|
||||
def self.included(base)
|
||||
|
@ -15,32 +15,16 @@ module QA
|
|||
end
|
||||
end
|
||||
|
||||
def go_to_operations_environments
|
||||
hover_operations do
|
||||
within_submenu do
|
||||
click_element(:sidebar_menu_item_link, menu_item: 'Environments')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def go_to_operations_metrics
|
||||
hover_operations do
|
||||
def go_to_monitor_metrics
|
||||
hover_monitor do
|
||||
within_submenu do
|
||||
click_element(:sidebar_menu_item_link, menu_item: 'Metrics')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def go_to_operations_kubernetes
|
||||
hover_operations do
|
||||
within_submenu do
|
||||
click_link('Kubernetes')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def go_to_operations_incidents
|
||||
hover_operations do
|
||||
def go_to_monitor_incidents
|
||||
hover_monitor do
|
||||
within_submenu do
|
||||
click_element(:sidebar_menu_item_link, menu_item: 'Incidents')
|
||||
end
|
||||
|
@ -49,10 +33,10 @@ module QA
|
|||
|
||||
private
|
||||
|
||||
def hover_operations
|
||||
def hover_monitor
|
||||
within_sidebar do
|
||||
scroll_to_element(:sidebar_menu_link, menu_item: 'Operations')
|
||||
find_element(:sidebar_menu_link, menu_item: 'Operations').hover
|
||||
scroll_to_element(:sidebar_menu_link, menu_item: 'Monitor')
|
||||
find_element(:sidebar_menu_link, menu_item: 'Monitor').hover
|
||||
|
||||
yield
|
||||
end
|
|
@ -12,13 +12,17 @@ module QA
|
|||
|
||||
base.class_eval do
|
||||
include QA::Page::Project::SubMenus::Common
|
||||
|
||||
view 'app/views/shared/nav/_scope_menu_body.html.haml' do
|
||||
element :project_scope_link
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def click_project
|
||||
retry_on_exception do
|
||||
within_sidebar do
|
||||
click_element(:sidebar_menu_link, menu_item: 'Project overview')
|
||||
click_element(:project_scope_link)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -53,10 +53,10 @@ module QA
|
|||
end
|
||||
end
|
||||
|
||||
def go_to_operations_settings
|
||||
def go_to_monitor_settings
|
||||
hover_settings do
|
||||
within_submenu do
|
||||
click_element(:sidebar_menu_item_link, menu_item: 'Operations')
|
||||
click_element(:sidebar_menu_item_link, menu_item: 'Monitor')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,22 +12,22 @@ module QA
|
|||
end
|
||||
|
||||
attribute :ingress_ip do
|
||||
Page::Project::Operations::Kubernetes::Show.perform(&:ingress_ip)
|
||||
Page::Project::Infrastructure::Kubernetes::Show.perform(&:ingress_ip)
|
||||
end
|
||||
|
||||
def fabricate!
|
||||
project.visit!
|
||||
|
||||
Page::Project::Menu.perform(
|
||||
&:go_to_operations_kubernetes)
|
||||
&:go_to_infrastructure_kubernetes)
|
||||
|
||||
Page::Project::Operations::Kubernetes::Index.perform(
|
||||
Page::Project::Infrastructure::Kubernetes::Index.perform(
|
||||
&:add_kubernetes_cluster)
|
||||
|
||||
Page::Project::Operations::Kubernetes::Add.perform(
|
||||
Page::Project::Infrastructure::Kubernetes::Add.perform(
|
||||
&:add_existing_cluster)
|
||||
|
||||
Page::Project::Operations::Kubernetes::AddExisting.perform do |cluster_page|
|
||||
Page::Project::Infrastructure::Kubernetes::AddExisting.perform do |cluster_page|
|
||||
cluster_page.set_cluster_name(@cluster.cluster_name)
|
||||
cluster_page.set_api_url(@cluster.api_url)
|
||||
cluster_page.set_ca_certificate(@cluster.ca_certificate)
|
||||
|
@ -36,7 +36,7 @@ module QA
|
|||
cluster_page.add_cluster!
|
||||
end
|
||||
|
||||
Page::Project::Operations::Kubernetes::Show.perform do |show|
|
||||
Page::Project::Infrastructure::Kubernetes::Show.perform do |show|
|
||||
# We must wait a few seconds for permissions to be set up correctly for new cluster
|
||||
sleep 25
|
||||
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
# TODO: Remove `:requires_admin` meta when the feature flag is removed
|
||||
RSpec.describe 'Verify', :runner, :requires_admin do
|
||||
RSpec.describe 'Verify', :runner do
|
||||
describe 'Pipeline creation and processing' do
|
||||
let(:executor) { "qa-runner-#{Time.now.to_i}" }
|
||||
let(:max_wait) { 30 }
|
||||
let(:feature_flag) { :ci_drop_new_builds_when_ci_quota_exceeded }
|
||||
|
||||
let(:project) do
|
||||
Resource::Project.fabricate_via_api! do |project|
|
||||
|
@ -28,8 +26,6 @@ module QA
|
|||
|
||||
it 'users creates a pipeline which gets processed', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1849' do
|
||||
# TODO: Convert back to :smoke once proved to be stable. Related issue: https://gitlab.com/gitlab-org/gitlab/-/issues/300909
|
||||
tags_mismatch_status = Runtime::Feature.enabled?(feature_flag, project: project) ? :failed : :pending
|
||||
|
||||
Flow::Login.sign_in
|
||||
|
||||
Resource::Repository::Commit.fabricate_via_api! do |commit|
|
||||
|
@ -75,7 +71,7 @@ module QA
|
|||
{
|
||||
'test-success': :passed,
|
||||
'test-failure': :failed,
|
||||
'test-tags-mismatch': tags_mismatch_status,
|
||||
'test-tags-mismatch': :pending,
|
||||
'test-artifacts': :passed
|
||||
}.each do |job, status|
|
||||
Page::Project::Pipeline::Show.perform do |pipeline|
|
||||
|
|
|
@ -81,11 +81,11 @@ module QA
|
|||
job.click_element(:pipeline_path)
|
||||
end
|
||||
|
||||
Page::Project::Menu.perform(&:go_to_operations_environments)
|
||||
Page::Project::Operations::Environments::Index.perform do |index|
|
||||
Page::Project::Menu.perform(&:go_to_deployments_environments)
|
||||
Page::Project::Deployments::Environments::Index.perform do |index|
|
||||
index.click_environment_link('production')
|
||||
end
|
||||
Page::Project::Operations::Environments::Show.perform do |show|
|
||||
Page::Project::Deployments::Environments::Show.perform do |show|
|
||||
show.view_deployment do
|
||||
expect(page).to have_content('Hello World!')
|
||||
expect(page).to have_content('you_can_see_this_variable')
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue