Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-09-23 00:12:17 +00:00
parent 83020ec58c
commit 5d3bcd82b5
32 changed files with 323 additions and 260 deletions

View File

@ -139,6 +139,7 @@ export const receiveJobLogsSuccess = ({ commit }, data) =>
export const fetchJobLogs = ({ dispatch, state }) => {
dispatch('requestJobLogs');
// update trace endpoint once BE compeletes trace re-naming in #340626
return axios
.get(`${state.detailJob.path}/trace`, { params: { format: 'json' } })
.then(({ data }) => dispatch('receiveJobLogsSuccess', data))

View File

@ -80,13 +80,13 @@ export default {
'isLoading',
'job',
'isSidebarOpen',
'trace',
'isTraceComplete',
'traceSize',
'isTraceSizeVisible',
'jobLog',
'isJobLogComplete',
'jobLogSize',
'isJobLogSizeVisible',
'isScrollBottomDisabled',
'isScrollTopDisabled',
'isScrolledToBottomBeforeReceivingTrace',
'isScrolledToBottomBeforeReceivingJobLog',
'hasError',
'selectedStage',
]),
@ -97,7 +97,7 @@ export default {
'shouldRenderTriggeredLabel',
'hasEnvironment',
'shouldRenderSharedRunnerLimitWarning',
'hasTrace',
'hasJobLog',
'emptyStateIllustration',
'isScrollingDown',
'emptyStateAction',
@ -155,7 +155,7 @@ export default {
this.updateSidebar();
},
beforeDestroy() {
this.stopPollingTrace();
this.stopPollingJobLog();
this.stopPolling();
window.removeEventListener('resize', this.onResize);
window.removeEventListener('scroll', this.updateScroll);
@ -168,7 +168,7 @@ export default {
'toggleSidebar',
'scrollBottom',
'scrollTop',
'stopPollingTrace',
'stopPollingJobLog',
'stopPolling',
'toggleScrollButtons',
'toggleScrollAnimation',
@ -270,7 +270,7 @@ export default {
<div
v-if="job.archived"
class="gl-mt-3 gl-py-2 gl-px-3 gl-align-items-center gl-z-index-1 gl-m-auto archived-job"
:class="{ 'sticky-top gl-border-bottom-0': hasTrace }"
:class="{ 'sticky-top gl-border-bottom-0': hasJobLog }"
data-testid="archived-job"
>
<gl-icon name="lock" class="gl-vertical-align-bottom" />
@ -278,7 +278,7 @@ export default {
</div>
<!-- job log -->
<div
v-if="hasTrace"
v-if="hasJobLog"
class="build-trace-container gl-relative"
:class="{ 'gl-mt-3': !job.archived }"
>
@ -289,22 +289,22 @@ export default {
'has-archived-block': job.archived,
}"
:erase-path="job.erase_path"
:size="traceSize"
:size="jobLogSize"
:raw-path="job.raw_path"
:is-scroll-bottom-disabled="isScrollBottomDisabled"
:is-scroll-top-disabled="isScrollTopDisabled"
:is-trace-size-visible="isTraceSizeVisible"
:is-job-log-size-visible="isJobLogSizeVisible"
:is-scrolling-down="isScrollingDown"
@scrollJobLogTop="scrollTop"
@scrollJobLogBottom="scrollBottom"
/>
<log :trace="trace" :is-complete="isTraceComplete" />
<log :job-log="jobLog" :is-complete="isJobLogComplete" />
</div>
<!-- EO job log -->
<!-- empty state -->
<empty-state
v-if="!hasTrace"
v-if="!hasJobLog"
:illustration-path="emptyStateIllustration.image"
:illustration-size-class="emptyStateIllustration.size"
:title="emptyStateTitle"

View File

@ -44,7 +44,7 @@ export default {
type: Boolean,
required: true,
},
isTraceSizeVisible: {
isJobLogSizeVisible: {
type: Boolean,
required: true,
},
@ -73,7 +73,7 @@ export default {
class="truncated-info gl-display-none gl-sm-display-block gl-float-left"
data-testid="log-truncated-info"
>
<template v-if="isTraceSizeVisible">
<template v-if="isJobLogSizeVisible">
{{ jobLogSize }}
<gl-link
v-if="rawPath"

View File

@ -17,7 +17,7 @@ export default {
type: Object,
required: true,
},
traceEndpoint: {
jobLogEndpoint: {
type: String,
required: true,
},
@ -42,7 +42,7 @@ export default {
<log-line-header
:line="section.line"
:duration="badgeDuration"
:path="traceEndpoint"
:path="jobLogEndpoint"
:is-closed="section.isClosed"
@toggleLine="handleOnClickCollapsibleLine(section)"
/>
@ -53,10 +53,10 @@ export default {
v-if="line.isHeader"
:key="line.line.offset"
:section="line"
:trace-endpoint="traceEndpoint"
:job-log-endpoint="jobLogEndpoint"
@onClickCollapsibleLine="handleOnClickCollapsibleLine"
/>
<log-line v-else :key="line.offset" :line="line" :path="traceEndpoint" />
<log-line v-else :key="line.offset" :line="line" :path="jobLogEndpoint" />
</template>
</template>
<template v-else>
@ -64,7 +64,7 @@ export default {
v-for="line in section.lines"
:key="line.offset"
:line="line"
:path="traceEndpoint"
:path="jobLogEndpoint"
/>
</template>
</template>

View File

@ -10,10 +10,10 @@ export default {
},
computed: {
...mapState([
'traceEndpoint',
'trace',
'isTraceComplete',
'isScrolledToBottomBeforeReceivingTrace',
'jobLogEndpoint',
'jobLog',
'isJobLogComplete',
'isScrolledToBottomBeforeReceivingJobLog',
]),
},
updated() {
@ -39,7 +39,7 @@ export default {
* In order to scroll the page down after `v-html` has finished, we need to use setTimeout
*/
handleScrollDown() {
if (this.isScrolledToBottomBeforeReceivingTrace) {
if (this.isScrolledToBottomBeforeReceivingJobLog) {
setTimeout(() => {
this.scrollBottom();
}, 0);
@ -50,18 +50,18 @@ export default {
</script>
<template>
<code class="job-log d-block" data-qa-selector="job_log_content">
<template v-for="(section, index) in trace">
<template v-for="(section, index) in jobLog">
<collapsible-log-section
v-if="section.isHeader"
:key="`collapsible-${index}`"
:section="section"
:trace-endpoint="traceEndpoint"
:job-log-endpoint="jobLogEndpoint"
@onClickCollapsibleLine="handleOnClickCollapsibleLine"
/>
<log-line v-else :key="section.offset" :line="section" :path="traceEndpoint" />
<log-line v-else :key="section.offset" :line="section" :path="jobLogEndpoint" />
</template>
<div v-if="!isTraceComplete" class="js-log-animation loader-animation pt-3 pl-3">
<div v-if="!isJobLogComplete" class="js-log-animation loader-animation pt-3 pl-3">
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>

View File

@ -18,16 +18,16 @@ import * as types from './mutation_types';
export const init = ({ dispatch }, { endpoint, logState, pagePath }) => {
dispatch('setJobEndpoint', endpoint);
dispatch('setTraceOptions', {
dispatch('setJobLogOptions', {
logState,
pagePath,
});
return Promise.all([dispatch('fetchJob'), dispatch('fetchTrace')]);
return Promise.all([dispatch('fetchJob'), dispatch('fetchJobLog')]);
};
export const setJobEndpoint = ({ commit }, endpoint) => commit(types.SET_JOB_ENDPOINT, endpoint);
export const setTraceOptions = ({ commit }, options) => commit(types.SET_TRACE_OPTIONS, options);
export const setJobLogOptions = ({ commit }, options) => commit(types.SET_JOB_LOG_OPTIONS, options);
export const hideSidebar = ({ commit }) => commit(types.HIDE_SIDEBAR);
export const showSidebar = ({ commit }) => commit(types.SHOW_SIDEBAR);
@ -107,7 +107,7 @@ export const receiveJobError = ({ commit }) => {
};
/**
* Job's Trace
* Job Log
*/
export const scrollTop = ({ dispatch }) => {
scrollUp();
@ -156,59 +156,62 @@ export const toggleScrollAnimation = ({ commit }, toggle) =>
* Responsible to handle automatic scroll
*/
export const toggleScrollisInBottom = ({ commit }, toggle) => {
commit(types.TOGGLE_IS_SCROLL_IN_BOTTOM_BEFORE_UPDATING_TRACE, toggle);
commit(types.TOGGLE_IS_SCROLL_IN_BOTTOM_BEFORE_UPDATING_JOB_LOG, toggle);
};
export const requestTrace = ({ commit }) => commit(types.REQUEST_TRACE);
export const requestJobLog = ({ commit }) => commit(types.REQUEST_JOB_LOG);
export const fetchTrace = ({ dispatch, state }) =>
export const fetchJobLog = ({ dispatch, state }) =>
// update trace endpoint once BE compeletes trace re-naming in #340626
axios
.get(`${state.traceEndpoint}/trace.json`, {
params: { state: state.traceState },
.get(`${state.jobLogEndpoint}/trace.json`, {
params: { state: state.jobLogState },
})
.then(({ data }) => {
dispatch('toggleScrollisInBottom', isScrolledToBottom());
dispatch('receiveTraceSuccess', data);
dispatch('receiveJobLogSuccess', data);
if (data.complete) {
dispatch('stopPollingTrace');
} else if (!state.traceTimeout) {
dispatch('startPollingTrace');
dispatch('stopPollingJobLog');
} else if (!state.jobLogTimeout) {
dispatch('startPollingJobLog');
}
})
.catch((e) => {
if (e.response.status === httpStatusCodes.FORBIDDEN) {
dispatch('receiveTraceUnauthorizedError');
dispatch('receiveJobLogUnauthorizedError');
} else {
reportToSentry('job_actions', e);
dispatch('receiveTraceError');
dispatch('receiveJobLogError');
}
});
export const startPollingTrace = ({ dispatch, commit }) => {
const traceTimeout = setTimeout(() => {
commit(types.SET_TRACE_TIMEOUT, 0);
dispatch('fetchTrace');
export const startPollingJobLog = ({ dispatch, commit }) => {
const jobLogTimeout = setTimeout(() => {
commit(types.SET_JOB_LOG_TIMEOUT, 0);
dispatch('fetchJobLog');
}, 4000);
commit(types.SET_TRACE_TIMEOUT, traceTimeout);
commit(types.SET_JOB_LOG_TIMEOUT, jobLogTimeout);
};
export const stopPollingTrace = ({ state, commit }) => {
clearTimeout(state.traceTimeout);
commit(types.SET_TRACE_TIMEOUT, 0);
commit(types.STOP_POLLING_TRACE);
export const stopPollingJobLog = ({ state, commit }) => {
clearTimeout(state.jobLogTimeout);
commit(types.SET_JOB_LOG_TIMEOUT, 0);
commit(types.STOP_POLLING_JOB_LOG);
};
export const receiveTraceSuccess = ({ commit }, log) => commit(types.RECEIVE_TRACE_SUCCESS, log);
export const receiveTraceError = ({ dispatch }) => {
dispatch('stopPollingTrace');
export const receiveJobLogSuccess = ({ commit }, log) => commit(types.RECEIVE_JOB_LOG_SUCCESS, log);
export const receiveJobLogError = ({ dispatch }) => {
dispatch('stopPollingJobLog');
createFlash({
message: __('An error occurred while fetching the job log.'),
});
};
export const receiveTraceUnauthorizedError = ({ dispatch }) => {
dispatch('stopPollingTrace');
export const receiveJobLogUnauthorizedError = ({ dispatch }) => {
dispatch('stopPollingJobLog');
createFlash({
message: __('The current user is not authorized to access the job log.'),
});
@ -248,6 +251,7 @@ export const fetchJobsForStage = ({ dispatch }, stage = {}) => {
};
export const receiveJobsForStageSuccess = ({ commit }, data) =>
commit(types.RECEIVE_JOBS_FOR_STAGE_SUCCESS, data);
export const receiveJobsForStageError = ({ commit }) => {
commit(types.RECEIVE_JOBS_FOR_STAGE_ERROR);
createFlash({

View File

@ -21,11 +21,12 @@ export const shouldRenderTriggeredLabel = (state) => isString(state.job.started)
export const hasEnvironment = (state) => !isEmpty(state.job.deployment_status);
/**
* Checks if it the job has trace.
* Checks if it the job has a log.
* Used to check if it should render the job log or the empty state
* @returns {Boolean}
*/
export const hasTrace = (state) =>
export const hasJobLog = (state) =>
// update has_trace once BE compeletes trace re-naming in #340626
state.job.has_trace || (!isEmpty(state.job.status) && state.job.status.group === 'running');
export const emptyStateIllustration = (state) => state?.job?.status?.illustration || {};
@ -43,7 +44,7 @@ export const shouldRenderSharedRunnerLimitWarning = (state) =>
!isEmpty(state.job.runners.quota) &&
state.job.runners.quota.used >= state.job.runners.quota.limit;
export const isScrollingDown = (state) => isScrolledToBottom() && !state.isTraceComplete;
export const isScrollingDown = (state) => isScrolledToBottom() && !state.isJobLogComplete;
export const hasRunnersForProject = (state) =>
state?.job?.runners?.available && !state?.job?.runners?.online;

View File

@ -1,5 +1,5 @@
export const SET_JOB_ENDPOINT = 'SET_JOB_ENDPOINT';
export const SET_TRACE_OPTIONS = 'SET_TRACE_OPTIONS';
export const SET_JOB_LOG_OPTIONS = 'SET_JOB_LOG_OPTIONS';
export const HIDE_SIDEBAR = 'HIDE_SIDEBAR';
export const SHOW_SIDEBAR = 'SHOW_SIDEBAR';
@ -12,17 +12,17 @@ export const ENABLE_SCROLL_BOTTOM = 'ENABLE_SCROLL_BOTTOM';
export const ENABLE_SCROLL_TOP = 'ENABLE_SCROLL_TOP';
export const TOGGLE_SCROLL_ANIMATION = 'TOGGLE_SCROLL_ANIMATION';
export const TOGGLE_IS_SCROLL_IN_BOTTOM_BEFORE_UPDATING_TRACE = 'TOGGLE_IS_SCROLL_IN_BOTTOM';
export const TOGGLE_IS_SCROLL_IN_BOTTOM_BEFORE_UPDATING_JOB_LOG = 'TOGGLE_IS_SCROLL_IN_BOTTOM';
export const REQUEST_JOB = 'REQUEST_JOB';
export const RECEIVE_JOB_SUCCESS = 'RECEIVE_JOB_SUCCESS';
export const RECEIVE_JOB_ERROR = 'RECEIVE_JOB_ERROR';
export const REQUEST_TRACE = 'REQUEST_TRACE';
export const SET_TRACE_TIMEOUT = 'SET_TRACE_TIMEOUT';
export const STOP_POLLING_TRACE = 'STOP_POLLING_TRACE';
export const RECEIVE_TRACE_SUCCESS = 'RECEIVE_TRACE_SUCCESS';
export const RECEIVE_TRACE_ERROR = 'RECEIVE_TRACE_ERROR';
export const REQUEST_JOB_LOG = 'REQUEST_JOB_LOG';
export const SET_JOB_LOG_TIMEOUT = 'SET_JOB_LOG_TIMEOUT';
export const STOP_POLLING_JOB_LOG = 'STOP_POLLING_JOB_LOG';
export const RECEIVE_JOB_LOG_SUCCESS = 'RECEIVE_JOB_LOG_SUCCESS';
export const RECEIVE_JOB_LOG_ERROR = 'RECEIVE_JOB_LOG_ERROR';
export const TOGGLE_COLLAPSIBLE_LINE = 'TOGGLE_COLLAPSIBLE_LINE';
export const SET_SELECTED_STAGE = 'SET_SELECTED_STAGE';

View File

@ -1,16 +1,16 @@
import Vue from 'vue';
import { INFINITELY_NESTED_COLLAPSIBLE_SECTIONS_FF } from '../constants';
import * as types from './mutation_types';
import { logLinesParser, logLinesParserLegacy, updateIncrementalTrace } from './utils';
import { logLinesParser, logLinesParserLegacy, updateIncrementalJobLog } from './utils';
export default {
[types.SET_JOB_ENDPOINT](state, endpoint) {
state.jobEndpoint = endpoint;
},
[types.SET_TRACE_OPTIONS](state, options = {}) {
state.traceEndpoint = options.pagePath;
state.traceState = options.logState;
[types.SET_JOB_LOG_OPTIONS](state, options = {}) {
state.jobLogEndpoint = options.pagePath;
state.jobLogState = options.logState;
},
[types.HIDE_SIDEBAR](state) {
@ -20,11 +20,11 @@ export default {
state.isSidebarOpen = true;
},
[types.RECEIVE_TRACE_SUCCESS](state, log = {}) {
[types.RECEIVE_JOB_LOG_SUCCESS](state, log = {}) {
const infinitelyCollapsibleSectionsFlag =
gon.features?.[INFINITELY_NESTED_COLLAPSIBLE_SECTIONS_FF];
if (log.state) {
state.traceState = log.state;
state.jobLogState = log.state;
}
if (log.append) {
@ -32,52 +32,52 @@ export default {
if (log.lines) {
const parsedResult = logLinesParser(
log.lines,
state.auxiliaryPartialTraceHelpers,
state.trace,
state.auxiliaryPartialJobLogHelpers,
state.jobLog,
);
state.trace = parsedResult.parsedLines;
state.auxiliaryPartialTraceHelpers = parsedResult.auxiliaryPartialTraceHelpers;
state.jobLog = parsedResult.parsedLines;
state.auxiliaryPartialJobLogHelpers = parsedResult.auxiliaryPartialJobLogHelpers;
}
} else {
state.trace = log.lines ? updateIncrementalTrace(log.lines, state.trace) : state.trace;
state.jobLog = log.lines ? updateIncrementalJobLog(log.lines, state.jobLog) : state.jobLog;
}
state.traceSize += log.size;
state.jobLogSize += log.size;
} else {
// When the job still does not have a trace
// the trace response will not have a defined
// When the job still does not have a log
// the job log response will not have a defined
// html or size. We keep the old value otherwise these
// will be set to `null`
if (infinitelyCollapsibleSectionsFlag) {
const parsedResult = logLinesParser(log.lines);
state.trace = parsedResult.parsedLines;
state.auxiliaryPartialTraceHelpers = parsedResult.auxiliaryPartialTraceHelpers;
state.jobLog = parsedResult.parsedLines;
state.auxiliaryPartialJobLogHelpers = parsedResult.auxiliaryPartialJobLogHelpers;
} else {
state.trace = log.lines ? logLinesParserLegacy(log.lines) : state.trace;
state.jobLog = log.lines ? logLinesParserLegacy(log.lines) : state.jobLog;
}
state.traceSize = log.size || state.traceSize;
state.jobLogSize = log.size || state.jobLogSize;
}
if (state.traceSize < log.total) {
state.isTraceSizeVisible = true;
if (state.jobLogSize < log.total) {
state.isJobLogSizeVisible = true;
} else {
state.isTraceSizeVisible = false;
state.isJobLogSizeVisible = false;
}
state.isTraceComplete = log.complete || state.isTraceComplete;
state.isJobLogComplete = log.complete || state.isJobLogComplete;
},
[types.SET_TRACE_TIMEOUT](state, id) {
state.traceTimeout = id;
[types.SET_JOB_LOG_TIMEOUT](state, id) {
state.jobLogTimeout = id;
},
/**
* Will remove loading animation
*/
[types.STOP_POLLING_TRACE](state) {
state.isTraceComplete = true;
[types.STOP_POLLING_JOB_LOG](state) {
state.isJobLogComplete = true;
},
/**
@ -137,8 +137,8 @@ export default {
state.isScrollingDown = toggle;
},
[types.TOGGLE_IS_SCROLL_IN_BOTTOM_BEFORE_UPDATING_TRACE](state, toggle) {
state.isScrolledToBottomBeforeReceivingTrace = toggle;
[types.TOGGLE_IS_SCROLL_IN_BOTTOM_BEFORE_UPDATING_JOB_LOG](state, toggle) {
state.isScrolledToBottomBeforeReceivingJobLog = toggle;
},
[types.REQUEST_JOBS_FOR_STAGE](state, stage = {}) {

View File

@ -1,6 +1,6 @@
export default () => ({
jobEndpoint: null,
traceEndpoint: null,
jobLogEndpoint: null,
// sidebar
isSidebarOpen: true,
@ -14,16 +14,16 @@ export default () => ({
isScrollTopDisabled: true,
// Used to check if we should keep the automatic scroll
isScrolledToBottomBeforeReceivingTrace: true,
isScrolledToBottomBeforeReceivingJobLog: true,
trace: [],
isTraceComplete: false,
traceSize: 0,
isTraceSizeVisible: false,
traceTimeout: 0,
jobLog: [],
isJobLogComplete: false,
jobLogSize: 0,
isJobLogSizeVisible: false,
jobLogTimeout: 0,
// used as a query parameter to fetch the trace
traceState: null,
// used as a query parameter to fetch the job log
jobLogState: null,
// sidebar dropdown & list of jobs
isLoadingJobs: false,
@ -32,5 +32,5 @@ export default () => ({
jobs: [],
// to parse partial logs
auxiliaryPartialTraceHelpers: {},
auxiliaryPartialJobLogHelpers: {},
});

View File

@ -131,17 +131,17 @@ export const logLinesParserLegacy = (lines = [], accumulator = []) =>
[...accumulator],
);
export const logLinesParser = (lines = [], previousTraceState = {}, prevParsedLines = []) => {
let currentLineCount = previousTraceState?.prevLineCount ?? 0;
let currentHeader = previousTraceState?.currentHeader;
let isPreviousLineHeader = previousTraceState?.isPreviousLineHeader ?? false;
export const logLinesParser = (lines = [], previousJobLogState = {}, prevParsedLines = []) => {
let currentLineCount = previousJobLogState?.prevLineCount ?? 0;
let currentHeader = previousJobLogState?.currentHeader;
let isPreviousLineHeader = previousJobLogState?.isPreviousLineHeader ?? false;
const parsedLines = prevParsedLines.length > 0 ? prevParsedLines : [];
const sectionsQueue = previousTraceState?.sectionsQueue ?? [];
const sectionsQueue = previousJobLogState?.sectionsQueue ?? [];
for (let i = 0; i < lines.length; i += 1) {
const line = lines[i];
// First run we can use the current index, later runs we have to retrieve the last number of lines
currentLineCount = previousTraceState?.prevLineCount ? currentLineCount + 1 : i + 1;
currentLineCount = previousJobLogState?.prevLineCount ? currentLineCount + 1 : i + 1;
if (line.section_header && !isPreviousLineHeader) {
// If there's no previous line header that means we're at the root of the log
@ -198,7 +198,7 @@ export const logLinesParser = (lines = [], previousTraceState = {}, prevParsedLi
return {
parsedLines,
auxiliaryPartialTraceHelpers: {
auxiliaryPartialJobLogHelpers: {
isPreviousLineHeader,
currentHeader,
sectionsQueue,
@ -241,7 +241,7 @@ export const findOffsetAndRemove = (newLog = [], oldParsed = []) => {
};
/**
* When the trace is not complete, backend may send the last received line
* When the job log is not complete, backend may send the last received line
* in the new response.
*
* We need to check if that is the case by looking for the offset property
@ -250,7 +250,7 @@ export const findOffsetAndRemove = (newLog = [], oldParsed = []) => {
* @param array oldLog
* @param array newLog
*/
export const updateIncrementalTrace = (newLog = [], oldParsed = []) => {
export const updateIncrementalJobLog = (newLog = [], oldParsed = []) => {
const parsedLog = findOffsetAndRemove(newLog, oldParsed);
return logLinesParserLegacy(newLog, parsedLog);

View File

@ -31,7 +31,7 @@ export default () => ({
},
/**
* Logs including trace
* Jobs with logs
*/
logs: {
lines: [],

View File

@ -70,7 +70,7 @@ class ProjectStatistics < ApplicationRecord
end
def update_lfs_objects_size
self.lfs_objects_size = project.lfs_objects.sum(:size)
self.lfs_objects_size = LfsObject.joins(:lfs_objects_projects).where(lfs_objects_projects: { project_id: project.id }).sum(:size)
end
def update_uploads_size

View File

@ -2,7 +2,10 @@
module Projects
class Topic < ApplicationRecord
include Avatarable
validates :name, presence: true, uniqueness: true, length: { maximum: 255 }
validates :description, length: { maximum: 1024 }
has_many :project_topics, class_name: 'Projects::ProjectTopic'
has_many :projects, through: :project_topics

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
class AddAvatarAndDescriptionToTopic < Gitlab::Database::Migration[1.0]
# rubocop:disable Migration/AddLimitToTextColumns
def up
add_column :topics, :avatar, :text
add_column :topics, :description, :text
end
# rubocop:enable Migration/AddLimitToTextColumns
def down
remove_column :topics, :avatar
remove_column :topics, :description
end
end

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
class AddTextLimitToTopicsDescriptionAndAvatar < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
def up
add_text_limit :topics, :description, 1024
add_text_limit :topics, :avatar, 255
end
def down
remove_text_limit :topics, :avatar
remove_text_limit :topics, :description
end
end

View File

@ -0,0 +1 @@
f11b237fab6b4133c73f1d6105d85c8db1548b6d0753b2fd96f613c90a4fa3c1

View File

@ -0,0 +1 @@
e7e9b13874081a7df42d07ccc9a54fb81973210d1c175cd995300f6339d57495

View File

@ -19593,6 +19593,10 @@ CREATE TABLE topics (
name text NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
avatar text,
description text,
CONSTRAINT check_26753fb43a CHECK ((char_length(avatar) <= 255)),
CONSTRAINT check_5d1a07c8c8 CHECK ((char_length(description) <= 1024)),
CONSTRAINT check_7a90d4c757 CHECK ((char_length(name) <= 255))
);

View File

@ -15,7 +15,7 @@ LDAP service that can be configured with GitLab for authentication and group syn
Secure LDAP requires a slightly different configuration than standard LDAP servers.
The steps below cover:
- Configuring the Secure LDAP Client in the Google Admin console.
- Configuring the Secure LDAP Client in the Google administrator console.
- Required GitLab configuration.
## Configuring Google LDAP client

View File

@ -243,7 +243,7 @@ To mirror-push branches and tags only, and avoid attempting to mirror-push prote
git push origin +refs/heads/*:refs/heads/* +refs/tags/*:refs/tags/*
```
Any other namespaces that the admin wants to push can be included there as well via additional patterns.
Any other namespaces that the administrator wants to push can be included there as well via additional patterns.
### Command line tools cannot connect to Gitaly

View File

@ -31,6 +31,10 @@ To authenticate to the Helm repository, you need either:
## Publish a package
WARNING:
The `helm-push` command is broken in Helm 3.7. For more information, see the [open issue](https://github.com/chartmuseum/helm-push/issues/109)
in the Chart Museum project.
NOTE:
You can publish Helm charts with duplicate names or versions. If duplicates exist, GitLab always
returns the chart with the latest version.
@ -123,3 +127,15 @@ Check the [Sidekiq log](../../../administration/logs.md#sidekiqlog)
for any related errors. If you see `Validation failed: Version is invalid`, it means that the
version in your `Chart.yaml` file does not follow [Helm Chart versioning specifications](https://helm.sh/docs/topics/charts/#charts-and-versioning).
To fix the error, use the correct version syntax and upload the chart again.
### `helm push` results in an error
The `helm push` plugin is not yet supported in Helm 3.7. If you try to push a chart using
`helm push`, it produces the following error:
```plaintext
Error: this feature has been marked as experimental and is not enabled by default. Please set HELM_EXPERIMENTAL_OCI=1 in your environment to use this feature
```
To continue to use the plugin, you can push an image using [curl](#use-cicd-to-publish-a-helm-package)
or downgrade your version of Helm.

View File

@ -47,9 +47,9 @@ describe('Job App', () => {
wrapper = mount(JobApp, { propsData: { ...props }, store });
};
const setupAndMount = ({ jobData = {}, traceData = {} } = {}) => {
const setupAndMount = ({ jobData = {}, jobLogData = {} } = {}) => {
mock.onGet(initSettings.endpoint).replyOnce(200, { ...job, ...jobData });
mock.onGet(`${initSettings.pagePath}/trace.json`).reply(200, traceData);
mock.onGet(`${initSettings.pagePath}/trace.json`).reply(200, jobLogData);
const asyncInit = store.dispatch('init', initSettings);
@ -77,11 +77,10 @@ describe('Job App', () => {
const findEmptyState = () => wrapper.find(EmptyState);
const findJobNewIssueLink = () => wrapper.find('[data-testid="job-new-issue"]');
const findJobEmptyStateTitle = () => wrapper.find('[data-testid="job-empty-state-title"]');
const findJobTraceScrollTop = () => wrapper.find('[data-testid="job-controller-scroll-top"]');
const findJobTraceScrollBottom = () =>
wrapper.find('[data-testid="job-controller-scroll-bottom"]');
const findJobTraceController = () => wrapper.find('[data-testid="job-raw-link-controller"]');
const findJobTraceEraseLink = () => wrapper.find('[data-testid="job-log-erase-link"]');
const findJobLogScrollTop = () => wrapper.find('[data-testid="job-controller-scroll-top"]');
const findJobLogScrollBottom = () => wrapper.find('[data-testid="job-controller-scroll-bottom"]');
const findJobLogController = () => wrapper.find('[data-testid="job-raw-link-controller"]');
const findJobLogEraseLink = () => wrapper.find('[data-testid="job-log-erase-link"]');
beforeEach(() => {
mock = new MockAdapter(axios);
@ -315,7 +314,7 @@ describe('Job App', () => {
});
describe('empty states block', () => {
it('renders empty state when job does not have trace and is not running', () =>
it('renders empty state when job does not have log and is not running', () =>
setupAndMount({
jobData: {
has_trace: false,
@ -342,7 +341,7 @@ describe('Job App', () => {
expect(findEmptyState().exists()).toBe(true);
}));
it('does not render empty state when job does not have trace but it is running', () =>
it('does not render empty state when job does not have log but it is running', () =>
setupAndMount({
jobData: {
has_trace: false,
@ -358,7 +357,7 @@ describe('Job App', () => {
expect(findEmptyState().exists()).toBe(false);
}));
it('does not render empty state when job has trace but it is not running', () =>
it('does not render empty state when job has log but it is not running', () =>
setupAndMount({ jobData: { has_trace: true } }).then(() => {
expect(findEmptyState().exists()).toBe(false);
}));
@ -424,10 +423,10 @@ describe('Job App', () => {
});
});
describe('trace controls', () => {
describe('job log controls', () => {
beforeEach(() =>
setupAndMount({
traceData: {
jobLogData: {
html: '<span>Update</span>',
status: 'success',
append: false,
@ -439,16 +438,16 @@ describe('Job App', () => {
);
it('should render scroll buttons', () => {
expect(findJobTraceScrollTop().exists()).toBe(true);
expect(findJobTraceScrollBottom().exists()).toBe(true);
expect(findJobLogScrollTop().exists()).toBe(true);
expect(findJobLogScrollBottom().exists()).toBe(true);
});
it('should render link to raw ouput', () => {
expect(findJobTraceController().exists()).toBe(true);
expect(findJobLogController().exists()).toBe(true);
});
it('should render link to erase job', () => {
expect(findJobTraceEraseLink().exists()).toBe(true);
expect(findJobLogEraseLink().exists()).toBe(true);
});
});
});

View File

@ -18,7 +18,7 @@ describe('Job log controllers', () => {
isScrollTopDisabled: false,
isScrollBottomDisabled: false,
isScrollingDown: true,
isTraceSizeVisible: true,
isJobLogSizeVisible: true,
};
const createWrapper = (props) => {
@ -38,7 +38,7 @@ describe('Job log controllers', () => {
const findScrollBottom = () => wrapper.find('[data-testid="job-controller-scroll-bottom"]');
describe('Truncate information', () => {
describe('with isTraceSizeVisible', () => {
describe('with isJobLogSizeVisible', () => {
beforeEach(() => {
createWrapper();
});
@ -47,31 +47,31 @@ describe('Job log controllers', () => {
expect(findTruncatedInfo().text()).toMatch('499.95 KiB');
});
it('renders link to raw trace', () => {
it('renders link to raw job log', () => {
expect(findRawLink().attributes('href')).toBe(defaultProps.rawPath);
});
});
});
describe('links section', () => {
describe('with raw trace path', () => {
describe('with raw job log path', () => {
beforeEach(() => {
createWrapper();
});
it('renders raw trace link', () => {
it('renders raw job log link', () => {
expect(findRawLinkController().attributes('href')).toBe(defaultProps.rawPath);
});
});
describe('without raw trace path', () => {
describe('without raw job log path', () => {
beforeEach(() => {
createWrapper({
rawPath: null,
});
});
it('does not render raw trace link', () => {
it('does not render raw job log link', () => {
expect(findRawLinkController().exists()).toBe(false);
});
});

View File

@ -6,7 +6,7 @@ describe('Job Log Collapsible Section', () => {
let wrapper;
let origGon;
const traceEndpoint = 'jobs/335';
const jobLogEndpoint = 'jobs/335';
const findCollapsibleLine = () => wrapper.find('.collapsible-line');
const findCollapsibleLineSvg = () => wrapper.find('.collapsible-line svg');
@ -35,7 +35,7 @@ describe('Job Log Collapsible Section', () => {
beforeEach(() => {
createComponent({
section: collapsibleSectionClosed,
traceEndpoint,
jobLogEndpoint,
});
});
@ -52,7 +52,7 @@ describe('Job Log Collapsible Section', () => {
beforeEach(() => {
createComponent({
section: collapsibleSectionOpened,
traceEndpoint,
jobLogEndpoint,
});
});
@ -72,7 +72,7 @@ describe('Job Log Collapsible Section', () => {
it('emits onClickCollapsibleLine on click', () => {
createComponent({
section: collapsibleSectionOpened,
traceEndpoint,
jobLogEndpoint,
});
findCollapsibleLine().trigger('click');

View File

@ -31,8 +31,8 @@ describe('Job Log', () => {
window.gon = { features: { infinitelyCollapsibleSections: false } };
state = {
trace: logLinesParserLegacy(jobLog),
traceEndpoint: 'jobs/id',
jobLog: logLinesParserLegacy(jobLog),
jobLogEndpoint: 'jobs/id',
};
store = new Vuex.Store({
@ -59,7 +59,7 @@ describe('Job Log', () => {
});
it('links to the provided path and correct line number', () => {
expect(wrapper.find('#L1').attributes('href')).toBe(`${state.traceEndpoint}#L1`);
expect(wrapper.find('#L1').attributes('href')).toBe(`${state.jobLogEndpoint}#L1`);
});
});
@ -111,8 +111,8 @@ describe('Job Log, infinitelyCollapsibleSections feature flag enabled', () => {
window.gon = { features: { infinitelyCollapsibleSections: true } };
state = {
trace: logLinesParser(jobLog).parsedLines,
traceEndpoint: 'jobs/id',
jobLog: logLinesParser(jobLog).parsedLines,
jobLogEndpoint: 'jobs/id',
};
store = new Vuex.Store({
@ -139,7 +139,7 @@ describe('Job Log, infinitelyCollapsibleSections feature flag enabled', () => {
});
it('links to the provided path and correct line number', () => {
expect(wrapper.find('#L1').attributes('href')).toBe(`${state.traceEndpoint}#L1`);
expect(wrapper.find('#L1').attributes('href')).toBe(`${state.jobLogEndpoint}#L1`);
});
});

View File

@ -3,7 +3,7 @@ import { TEST_HOST } from 'helpers/test_constants';
import testAction from 'helpers/vuex_action_helper';
import {
setJobEndpoint,
setTraceOptions,
setJobLogOptions,
clearEtagPoll,
stopPolling,
requestJob,
@ -12,12 +12,12 @@ import {
receiveJobError,
scrollTop,
scrollBottom,
requestTrace,
fetchTrace,
startPollingTrace,
stopPollingTrace,
receiveTraceSuccess,
receiveTraceError,
requestJobLog,
fetchJobLog,
startPollingJobLog,
stopPollingJobLog,
receiveJobLogSuccess,
receiveJobLogError,
toggleCollapsibleLine,
requestJobsForStage,
fetchJobsForStage,
@ -51,13 +51,13 @@ describe('Job State actions', () => {
});
});
describe('setTraceOptions', () => {
it('should commit SET_TRACE_OPTIONS mutation', (done) => {
describe('setJobLogOptions', () => {
it('should commit SET_JOB_LOG_OPTIONS mutation', (done) => {
testAction(
setTraceOptions,
setJobLogOptions,
{ pagePath: 'job/872324/trace.json' },
mockedState,
[{ type: types.SET_TRACE_OPTIONS, payload: { pagePath: 'job/872324/trace.json' } }],
[{ type: types.SET_JOB_LOG_OPTIONS, payload: { pagePath: 'job/872324/trace.json' } }],
[],
done,
);
@ -191,17 +191,17 @@ describe('Job State actions', () => {
});
});
describe('requestTrace', () => {
it('should commit REQUEST_TRACE mutation', (done) => {
testAction(requestTrace, null, mockedState, [{ type: types.REQUEST_TRACE }], [], done);
describe('requestJobLog', () => {
it('should commit REQUEST_JOB_LOG mutation', (done) => {
testAction(requestJobLog, null, mockedState, [{ type: types.REQUEST_JOB_LOG }], [], done);
});
});
describe('fetchTrace', () => {
describe('fetchJobLog', () => {
let mock;
beforeEach(() => {
mockedState.traceEndpoint = `${TEST_HOST}/endpoint`;
mockedState.jobLogEndpoint = `${TEST_HOST}/endpoint`;
mock = new MockAdapter(axios);
});
@ -212,14 +212,14 @@ describe('Job State actions', () => {
});
describe('success', () => {
it('dispatches requestTrace, receiveTraceSuccess and stopPollingTrace when job is complete', (done) => {
it('dispatches requestJobLog, receiveJobLogSuccess and stopPollingJobLog when job is complete', (done) => {
mock.onGet(`${TEST_HOST}/endpoint/trace.json`).replyOnce(200, {
html: 'I, [2018-08-17T22:57:45.707325 #1841] INFO -- :',
complete: true,
});
testAction(
fetchTrace,
fetchJobLog,
null,
mockedState,
[],
@ -233,10 +233,10 @@ describe('Job State actions', () => {
html: 'I, [2018-08-17T22:57:45.707325 #1841] INFO -- :',
complete: true,
},
type: 'receiveTraceSuccess',
type: 'receiveJobLogSuccess',
},
{
type: 'stopPollingTrace',
type: 'stopPollingJobLog',
},
],
done,
@ -244,43 +244,43 @@ describe('Job State actions', () => {
});
describe('when job is incomplete', () => {
let tracePayload;
let jobLogPayload;
beforeEach(() => {
tracePayload = {
jobLogPayload = {
html: 'I, [2018-08-17T22:57:45.707325 #1841] INFO -- :',
complete: false,
};
mock.onGet(`${TEST_HOST}/endpoint/trace.json`).replyOnce(200, tracePayload);
mock.onGet(`${TEST_HOST}/endpoint/trace.json`).replyOnce(200, jobLogPayload);
});
it('dispatches startPollingTrace', (done) => {
it('dispatches startPollingJobLog', (done) => {
testAction(
fetchTrace,
fetchJobLog,
null,
mockedState,
[],
[
{ type: 'toggleScrollisInBottom', payload: true },
{ type: 'receiveTraceSuccess', payload: tracePayload },
{ type: 'startPollingTrace' },
{ type: 'receiveJobLogSuccess', payload: jobLogPayload },
{ type: 'startPollingJobLog' },
],
done,
);
});
it('does not dispatch startPollingTrace when timeout is non-empty', (done) => {
mockedState.traceTimeout = 1;
it('does not dispatch startPollingJobLog when timeout is non-empty', (done) => {
mockedState.jobLogTimeout = 1;
testAction(
fetchTrace,
fetchJobLog,
null,
mockedState,
[],
[
{ type: 'toggleScrollisInBottom', payload: true },
{ type: 'receiveTraceSuccess', payload: tracePayload },
{ type: 'receiveJobLogSuccess', payload: jobLogPayload },
],
done,
);
@ -293,15 +293,15 @@ describe('Job State actions', () => {
mock.onGet(`${TEST_HOST}/endpoint/trace.json`).reply(500);
});
it('dispatches requestTrace and receiveTraceError ', (done) => {
it('dispatches requestJobLog and receiveJobLogError ', (done) => {
testAction(
fetchTrace,
fetchJobLog,
null,
mockedState,
[],
[
{
type: 'receiveTraceError',
type: 'receiveJobLogError',
},
],
done,
@ -310,7 +310,7 @@ describe('Job State actions', () => {
});
});
describe('startPollingTrace', () => {
describe('startPollingJobLog', () => {
let dispatch;
let commit;
@ -318,18 +318,18 @@ describe('Job State actions', () => {
dispatch = jest.fn();
commit = jest.fn();
startPollingTrace({ dispatch, commit });
startPollingJobLog({ dispatch, commit });
});
afterEach(() => {
jest.clearAllTimers();
});
it('should save the timeout id but not call fetchTrace', () => {
expect(commit).toHaveBeenCalledWith(types.SET_TRACE_TIMEOUT, expect.any(Number));
it('should save the timeout id but not call fetchJobLog', () => {
expect(commit).toHaveBeenCalledWith(types.SET_JOB_LOG_TIMEOUT, expect.any(Number));
expect(commit.mock.calls[0][1]).toBeGreaterThan(0);
expect(dispatch).not.toHaveBeenCalledWith('fetchTrace');
expect(dispatch).not.toHaveBeenCalledWith('fetchJobLog');
});
describe('after timeout has passed', () => {
@ -337,14 +337,14 @@ describe('Job State actions', () => {
jest.advanceTimersByTime(4000);
});
it('should clear the timeout id and fetchTrace', () => {
expect(commit).toHaveBeenCalledWith(types.SET_TRACE_TIMEOUT, 0);
expect(dispatch).toHaveBeenCalledWith('fetchTrace');
it('should clear the timeout id and fetchJobLog', () => {
expect(commit).toHaveBeenCalledWith(types.SET_JOB_LOG_TIMEOUT, 0);
expect(dispatch).toHaveBeenCalledWith('fetchJobLog');
});
});
});
describe('stopPollingTrace', () => {
describe('stopPollingJobLog', () => {
let origTimeout;
beforeEach(() => {
@ -358,40 +358,40 @@ describe('Job State actions', () => {
window.clearTimeout = origTimeout;
});
it('should commit STOP_POLLING_TRACE mutation ', (done) => {
const traceTimeout = 7;
it('should commit STOP_POLLING_JOB_LOG mutation ', (done) => {
const jobLogTimeout = 7;
testAction(
stopPollingTrace,
stopPollingJobLog,
null,
{ ...mockedState, traceTimeout },
[{ type: types.SET_TRACE_TIMEOUT, payload: 0 }, { type: types.STOP_POLLING_TRACE }],
{ ...mockedState, jobLogTimeout },
[{ type: types.SET_JOB_LOG_TIMEOUT, payload: 0 }, { type: types.STOP_POLLING_JOB_LOG }],
[],
)
.then(() => {
expect(window.clearTimeout).toHaveBeenCalledWith(traceTimeout);
expect(window.clearTimeout).toHaveBeenCalledWith(jobLogTimeout);
})
.then(done)
.catch(done.fail);
});
});
describe('receiveTraceSuccess', () => {
it('should commit RECEIVE_TRACE_SUCCESS mutation ', (done) => {
describe('receiveJobLogSuccess', () => {
it('should commit RECEIVE_JOB_LOG_SUCCESS mutation ', (done) => {
testAction(
receiveTraceSuccess,
receiveJobLogSuccess,
'hello world',
mockedState,
[{ type: types.RECEIVE_TRACE_SUCCESS, payload: 'hello world' }],
[{ type: types.RECEIVE_JOB_LOG_SUCCESS, payload: 'hello world' }],
[],
done,
);
});
});
describe('receiveTraceError', () => {
it('should commit stop polling trace', (done) => {
testAction(receiveTraceError, null, mockedState, [], [{ type: 'stopPollingTrace' }], done);
describe('receiveJobLogError', () => {
it('should commit stop polling job log', (done) => {
testAction(receiveJobLogError, null, mockedState, [], [{ type: 'stopPollingJobLog' }], done);
});
});

View File

@ -102,13 +102,13 @@ describe('Job Store Getters', () => {
});
});
describe('hasTrace', () => {
describe('hasJobLog', () => {
describe('when has_trace is true', () => {
it('returns true', () => {
localState.job.has_trace = true;
localState.job.status = {};
expect(getters.hasTrace(localState)).toEqual(true);
expect(getters.hasJobLog(localState)).toEqual(true);
});
});
@ -117,7 +117,7 @@ describe('Job Store Getters', () => {
localState.job.has_trace = false;
localState.job.status = { group: 'running' };
expect(getters.hasTrace(localState)).toEqual(true);
expect(getters.hasJobLog(localState)).toEqual(true);
});
});
@ -126,7 +126,7 @@ describe('Job Store Getters', () => {
localState.job.has_trace = false;
localState.job.status = { group: 'pending' };
expect(getters.hasTrace(localState)).toEqual(false);
expect(getters.hasJobLog(localState)).toEqual(false);
});
});
});

View File

@ -45,39 +45,39 @@ describe('Jobs Store Mutations', () => {
});
});
describe('RECEIVE_TRACE_SUCCESS', () => {
describe('when trace has state', () => {
it('sets traceState', () => {
describe('RECEIVE_JOB_LOG_SUCCESS', () => {
describe('when job log has state', () => {
it('sets jobLogState', () => {
const stateLog =
'eyJvZmZzZXQiOjczNDQ1MSwibl9vcGVuX3RhZ3MiOjAsImZnX2NvbG9yIjpudWxsLCJiZ19jb2xvciI6bnVsbCwic3R5bGVfbWFzayI6MH0=';
mutations[types.RECEIVE_TRACE_SUCCESS](stateCopy, {
mutations[types.RECEIVE_JOB_LOG_SUCCESS](stateCopy, {
state: stateLog,
});
expect(stateCopy.traceState).toEqual(stateLog);
expect(stateCopy.jobLogState).toEqual(stateLog);
});
});
describe('when traceSize is smaller than the total size', () => {
it('sets isTraceSizeVisible to true', () => {
mutations[types.RECEIVE_TRACE_SUCCESS](stateCopy, { total: 51184600, size: 1231 });
describe('when jobLogSize is smaller than the total size', () => {
it('sets isJobLogSizeVisible to true', () => {
mutations[types.RECEIVE_JOB_LOG_SUCCESS](stateCopy, { total: 51184600, size: 1231 });
expect(stateCopy.isTraceSizeVisible).toEqual(true);
expect(stateCopy.isJobLogSizeVisible).toEqual(true);
});
});
describe('when traceSize is bigger than the total size', () => {
it('sets isTraceSizeVisible to false', () => {
const copy = { ...stateCopy, traceSize: 5118460, size: 2321312 };
describe('when jobLogSize is bigger than the total size', () => {
it('sets isJobLogSizeVisible to false', () => {
const copy = { ...stateCopy, jobLogSize: 5118460, size: 2321312 };
mutations[types.RECEIVE_TRACE_SUCCESS](copy, { total: 511846 });
mutations[types.RECEIVE_JOB_LOG_SUCCESS](copy, { total: 511846 });
expect(copy.isTraceSizeVisible).toEqual(false);
expect(copy.isJobLogSizeVisible).toEqual(false);
});
});
it('sets trace, trace size and isTraceComplete', () => {
mutations[types.RECEIVE_TRACE_SUCCESS](stateCopy, {
it('sets job log size and isJobLogComplete', () => {
mutations[types.RECEIVE_JOB_LOG_SUCCESS](stateCopy, {
append: true,
html,
size: 511846,
@ -85,15 +85,15 @@ describe('Jobs Store Mutations', () => {
lines: [],
});
expect(stateCopy.traceSize).toEqual(511846);
expect(stateCopy.isTraceComplete).toEqual(true);
expect(stateCopy.jobLogSize).toEqual(511846);
expect(stateCopy.isJobLogComplete).toEqual(true);
});
describe('with new job log', () => {
describe('log.lines', () => {
describe('when append is true', () => {
it('sets the parsed log ', () => {
mutations[types.RECEIVE_TRACE_SUCCESS](stateCopy, {
mutations[types.RECEIVE_JOB_LOG_SUCCESS](stateCopy, {
append: true,
size: 511846,
complete: true,
@ -105,7 +105,7 @@ describe('Jobs Store Mutations', () => {
],
});
expect(stateCopy.trace).toEqual([
expect(stateCopy.jobLog).toEqual([
{
offset: 1,
content: [{ text: 'Running with gitlab-runner 11.12.1 (5a147c92)' }],
@ -117,7 +117,7 @@ describe('Jobs Store Mutations', () => {
describe('when it is defined', () => {
it('sets the parsed log ', () => {
mutations[types.RECEIVE_TRACE_SUCCESS](stateCopy, {
mutations[types.RECEIVE_JOB_LOG_SUCCESS](stateCopy, {
append: false,
size: 511846,
complete: true,
@ -126,7 +126,7 @@ describe('Jobs Store Mutations', () => {
],
});
expect(stateCopy.trace).toEqual([
expect(stateCopy.jobLog).toEqual([
{
offset: 0,
content: [{ text: 'Running with gitlab-runner 11.11.1 (5a147c92)' }],
@ -138,7 +138,7 @@ describe('Jobs Store Mutations', () => {
describe('when it is null', () => {
it('sets the default value', () => {
mutations[types.RECEIVE_TRACE_SUCCESS](stateCopy, {
mutations[types.RECEIVE_JOB_LOG_SUCCESS](stateCopy, {
append: true,
html,
size: 511846,
@ -146,30 +146,30 @@ describe('Jobs Store Mutations', () => {
lines: null,
});
expect(stateCopy.trace).toEqual([]);
expect(stateCopy.jobLog).toEqual([]);
});
});
});
});
});
describe('SET_TRACE_TIMEOUT', () => {
it('sets the traceTimeout id', () => {
describe('SET_JOB_LOG_TIMEOUT', () => {
it('sets the jobLogTimeout id', () => {
const id = 7;
expect(stateCopy.traceTimeout).not.toEqual(id);
expect(stateCopy.jobLogTimeout).not.toEqual(id);
mutations[types.SET_TRACE_TIMEOUT](stateCopy, id);
mutations[types.SET_JOB_LOG_TIMEOUT](stateCopy, id);
expect(stateCopy.traceTimeout).toEqual(id);
expect(stateCopy.jobLogTimeout).toEqual(id);
});
});
describe('STOP_POLLING_TRACE', () => {
it('sets isTraceComplete to true', () => {
mutations[types.STOP_POLLING_TRACE](stateCopy);
describe('STOP_POLLING_JOB_LOG', () => {
it('sets isJobLogComplete to true', () => {
mutations[types.STOP_POLLING_JOB_LOG](stateCopy);
expect(stateCopy.isTraceComplete).toEqual(true);
expect(stateCopy.isJobLogComplete).toEqual(true);
});
});
@ -296,12 +296,12 @@ describe('Job Store mutations, feature flag ON', () => {
window.gon = origGon;
});
describe('RECEIVE_TRACE_SUCCESS', () => {
describe('RECEIVE_JOB_LOG_SUCCESS', () => {
describe('with new job log', () => {
describe('log.lines', () => {
describe('when append is true', () => {
it('sets the parsed log ', () => {
mutations[types.RECEIVE_TRACE_SUCCESS](stateCopy, {
mutations[types.RECEIVE_JOB_LOG_SUCCESS](stateCopy, {
append: true,
size: 511846,
complete: true,
@ -313,7 +313,7 @@ describe('Job Store mutations, feature flag ON', () => {
],
});
expect(stateCopy.trace).toEqual([
expect(stateCopy.jobLog).toEqual([
{
offset: 1,
content: [{ text: 'Running with gitlab-runner 11.12.1 (5a147c92)' }],
@ -325,7 +325,7 @@ describe('Job Store mutations, feature flag ON', () => {
describe('when lines are defined', () => {
it('sets the parsed log ', () => {
mutations[types.RECEIVE_TRACE_SUCCESS](stateCopy, {
mutations[types.RECEIVE_JOB_LOG_SUCCESS](stateCopy, {
append: false,
size: 511846,
complete: true,
@ -334,7 +334,7 @@ describe('Job Store mutations, feature flag ON', () => {
],
});
expect(stateCopy.trace).toEqual([
expect(stateCopy.jobLog).toEqual([
{
offset: 0,
content: [{ text: 'Running with gitlab-runner 11.11.1 (5a147c92)' }],
@ -346,7 +346,7 @@ describe('Job Store mutations, feature flag ON', () => {
describe('when lines are null', () => {
it('sets the default value', () => {
mutations[types.RECEIVE_TRACE_SUCCESS](stateCopy, {
mutations[types.RECEIVE_JOB_LOG_SUCCESS](stateCopy, {
append: true,
html,
size: 511846,
@ -354,7 +354,7 @@ describe('Job Store mutations, feature flag ON', () => {
lines: null,
});
expect(stateCopy.trace).toEqual([]);
expect(stateCopy.jobLog).toEqual([]);
});
});
});

View File

@ -1,7 +1,7 @@
import {
logLinesParser,
logLinesParserLegacy,
updateIncrementalTrace,
updateIncrementalJobLog,
parseHeaderLine,
parseLine,
addDurationToHeader,
@ -487,11 +487,11 @@ describe('Jobs Store Utils', () => {
});
});
describe('updateIncrementalTrace', () => {
describe('updateIncrementalJobLog', () => {
describe('without repeated section', () => {
it('concats and parses both arrays', () => {
const oldLog = logLinesParserLegacy(originalTrace);
const result = updateIncrementalTrace(regularIncremental, oldLog);
const result = updateIncrementalJobLog(regularIncremental, oldLog);
expect(result).toEqual([
{
@ -519,7 +519,7 @@ describe('Jobs Store Utils', () => {
describe('with regular line repeated offset', () => {
it('updates the last line and formats with the incremental part', () => {
const oldLog = logLinesParserLegacy(originalTrace);
const result = updateIncrementalTrace(regularIncrementalRepeated, oldLog);
const result = updateIncrementalJobLog(regularIncrementalRepeated, oldLog);
expect(result).toEqual([
{
@ -538,7 +538,7 @@ describe('Jobs Store Utils', () => {
describe('with header line repeated', () => {
it('updates the header line and formats with the incremental part', () => {
const oldLog = logLinesParserLegacy(headerTrace);
const result = updateIncrementalTrace(headerTraceIncremental, oldLog);
const result = updateIncrementalJobLog(headerTraceIncremental, oldLog);
expect(result).toEqual([
{
@ -564,7 +564,7 @@ describe('Jobs Store Utils', () => {
describe('with collapsible line repeated', () => {
it('updates the collapsible line and formats with the incremental part', () => {
const oldLog = logLinesParserLegacy(collapsibleTrace);
const result = updateIncrementalTrace(collapsibleTraceIncremental, oldLog);
const result = updateIncrementalJobLog(collapsibleTraceIncremental, oldLog);
expect(result).toEqual([
{

View File

@ -294,15 +294,17 @@ RSpec.describe ProjectStatistics do
describe '#update_lfs_objects_size' do
let!(:lfs_object1) { create(:lfs_object, size: 23.megabytes) }
let!(:lfs_object2) { create(:lfs_object, size: 34.megabytes) }
let!(:lfs_object3) { create(:lfs_object, size: 34.megabytes) }
let!(:lfs_objects_project1) { create(:lfs_objects_project, project: project, lfs_object: lfs_object1) }
let!(:lfs_objects_project2) { create(:lfs_objects_project, project: project, lfs_object: lfs_object2) }
let!(:lfs_objects_project3) { create(:lfs_objects_project, project: project, lfs_object: lfs_object3) }
before do
statistics.update_lfs_objects_size
end
it "stores the size of related LFS objects" do
expect(statistics.lfs_objects_size).to eq 57.megabytes
expect(statistics.lfs_objects_size).to eq 91.megabytes
end
end

View File

@ -18,5 +18,6 @@ RSpec.describe Projects::Topic do
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_uniqueness_of(:name) }
it { is_expected.to validate_length_of(:name).is_at_most(255) }
it { is_expected.to validate_length_of(:description).is_at_most(1024) }
end
end