Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
3940f59a61
commit
b58ab6c33c
|
@ -1,14 +1,19 @@
|
|||
<script>
|
||||
import InstanceCounts from './instance_counts.vue';
|
||||
import PipelinesChart from './pipelines_chart.vue';
|
||||
|
||||
export default {
|
||||
name: 'InstanceStatisticsApp',
|
||||
components: {
|
||||
InstanceCounts,
|
||||
PipelinesChart,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<instance-counts />
|
||||
<div>
|
||||
<instance-counts />
|
||||
<pipelines-chart />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -0,0 +1,216 @@
|
|||
<script>
|
||||
import { GlLineChart } from '@gitlab/ui/dist/charts';
|
||||
import { GlAlert } from '@gitlab/ui';
|
||||
import { mapKeys, mapValues, pick, some, sum } from 'lodash';
|
||||
import ChartSkeletonLoader from '~/vue_shared/components/resizable_chart/skeleton_loader.vue';
|
||||
import { s__ } from '~/locale';
|
||||
import { formatDateAsMonth, getDayDifference } from '~/lib/utils/datetime_utility';
|
||||
import { getAverageByMonth, sortByDate, extractValues } from '../utils';
|
||||
import pipelineStatsQuery from '../graphql/queries/pipeline_stats.query.graphql';
|
||||
import { TODAY, START_DATE } from '../constants';
|
||||
|
||||
const DATA_KEYS = [
|
||||
'pipelinesTotal',
|
||||
'pipelinesSucceeded',
|
||||
'pipelinesFailed',
|
||||
'pipelinesCanceled',
|
||||
'pipelinesSkipped',
|
||||
];
|
||||
const PREFIX = 'pipelines';
|
||||
|
||||
export default {
|
||||
name: 'PipelinesChart',
|
||||
components: {
|
||||
GlLineChart,
|
||||
GlAlert,
|
||||
ChartSkeletonLoader,
|
||||
},
|
||||
startDate: START_DATE,
|
||||
endDate: TODAY,
|
||||
i18n: {
|
||||
loadPipelineChartError: s__(
|
||||
'InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again.',
|
||||
),
|
||||
noDataMessage: s__('InstanceAnalytics|There is no data available.'),
|
||||
total: s__('InstanceAnalytics|Total'),
|
||||
succeeded: s__('InstanceAnalytics|Succeeded'),
|
||||
failed: s__('InstanceAnalytics|Failed'),
|
||||
canceled: s__('InstanceAnalytics|Canceled'),
|
||||
skipped: s__('InstanceAnalytics|Skipped'),
|
||||
chartTitle: s__('InstanceAnalytics|Pipelines'),
|
||||
yAxisTitle: s__('InstanceAnalytics|Items'),
|
||||
xAxisTitle: s__('InstanceAnalytics|Month'),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
loadingError: null,
|
||||
};
|
||||
},
|
||||
apollo: {
|
||||
pipelineStats: {
|
||||
query: pipelineStatsQuery,
|
||||
variables() {
|
||||
return {
|
||||
firstTotal: this.totalDaysToShow,
|
||||
firstSucceeded: this.totalDaysToShow,
|
||||
firstFailed: this.totalDaysToShow,
|
||||
firstCanceled: this.totalDaysToShow,
|
||||
firstSkipped: this.totalDaysToShow,
|
||||
};
|
||||
},
|
||||
update(data) {
|
||||
const allData = extractValues(data, DATA_KEYS, PREFIX, 'nodes');
|
||||
const allPageInfo = extractValues(data, DATA_KEYS, PREFIX, 'pageInfo');
|
||||
|
||||
return {
|
||||
...mapValues(allData, sortByDate),
|
||||
...allPageInfo,
|
||||
};
|
||||
},
|
||||
result() {
|
||||
if (this.hasNextPage) {
|
||||
this.fetchNextPage();
|
||||
}
|
||||
},
|
||||
error() {
|
||||
this.handleError();
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
isLoading() {
|
||||
return this.$apollo.queries.pipelineStats.loading;
|
||||
},
|
||||
totalDaysToShow() {
|
||||
return getDayDifference(this.$options.startDate, this.$options.endDate);
|
||||
},
|
||||
firstVariables() {
|
||||
const allData = pick(this.pipelineStats, [
|
||||
'nodesTotal',
|
||||
'nodesSucceeded',
|
||||
'nodesFailed',
|
||||
'nodesCanceled',
|
||||
'nodesSkipped',
|
||||
]);
|
||||
const allDayDiffs = mapValues(allData, data => {
|
||||
const firstdataPoint = data[0];
|
||||
if (!firstdataPoint) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Math.max(
|
||||
0,
|
||||
getDayDifference(this.$options.startDate, new Date(firstdataPoint.recordedAt)),
|
||||
);
|
||||
});
|
||||
|
||||
return mapKeys(allDayDiffs, (value, key) => key.replace('nodes', 'first'));
|
||||
},
|
||||
cursorVariables() {
|
||||
const pageInfoKeys = [
|
||||
'pageInfoTotal',
|
||||
'pageInfoSucceeded',
|
||||
'pageInfoFailed',
|
||||
'pageInfoCanceled',
|
||||
'pageInfoSkipped',
|
||||
];
|
||||
|
||||
return extractValues(this.pipelineStats, pageInfoKeys, 'pageInfo', 'endCursor');
|
||||
},
|
||||
hasNextPage() {
|
||||
return (
|
||||
sum(Object.values(this.firstVariables)) > 0 &&
|
||||
some(this.pipelineStats, ({ hasNextPage }) => hasNextPage)
|
||||
);
|
||||
},
|
||||
hasEmptyDataSet() {
|
||||
return this.chartData.every(({ data }) => data.length === 0);
|
||||
},
|
||||
chartData() {
|
||||
const allData = pick(this.pipelineStats, [
|
||||
'nodesTotal',
|
||||
'nodesSucceeded',
|
||||
'nodesFailed',
|
||||
'nodesCanceled',
|
||||
'nodesSkipped',
|
||||
]);
|
||||
const options = { shouldRound: true };
|
||||
return Object.keys(allData).map(key => {
|
||||
const i18nName = key.slice('nodes'.length).toLowerCase();
|
||||
return {
|
||||
name: this.$options.i18n[i18nName],
|
||||
data: getAverageByMonth(allData[key], options),
|
||||
};
|
||||
});
|
||||
},
|
||||
range() {
|
||||
return {
|
||||
min: this.$options.startDate,
|
||||
max: this.$options.endDate,
|
||||
};
|
||||
},
|
||||
differenceInMonths() {
|
||||
const yearDiff = this.$options.endDate.getYear() - this.$options.startDate.getYear();
|
||||
const monthDiff = this.$options.endDate.getMonth() - this.$options.startDate.getMonth();
|
||||
|
||||
return monthDiff + 12 * yearDiff;
|
||||
},
|
||||
chartOptions() {
|
||||
return {
|
||||
xAxis: {
|
||||
...this.range,
|
||||
name: this.$options.i18n.xAxisTitle,
|
||||
type: 'time',
|
||||
splitNumber: this.differenceInMonths + 1,
|
||||
axisLabel: {
|
||||
interval: 0,
|
||||
showMinLabel: false,
|
||||
showMaxLabel: false,
|
||||
align: 'right',
|
||||
formatter: formatDateAsMonth,
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
name: this.$options.i18n.yAxisTitle,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleError() {
|
||||
this.loadingError = true;
|
||||
},
|
||||
fetchNextPage() {
|
||||
this.$apollo.queries.pipelineStats
|
||||
.fetchMore({
|
||||
variables: {
|
||||
...this.firstVariables,
|
||||
...this.cursorVariables,
|
||||
},
|
||||
updateQuery: (previousResult, { fetchMoreResult }) => {
|
||||
return Object.keys(fetchMoreResult).reduce((memo, key) => {
|
||||
const { nodes, ...rest } = fetchMoreResult[key];
|
||||
const previousNodes = previousResult[key].nodes;
|
||||
return { ...memo, [key]: { ...rest, nodes: [...previousNodes, ...nodes] } };
|
||||
}, {});
|
||||
},
|
||||
})
|
||||
.catch(this.handleError);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<h3>{{ $options.i18n.chartTitle }}</h3>
|
||||
<gl-alert v-if="loadingError" variant="danger" :dismissible="false" class="gl-mt-3">
|
||||
{{ this.$options.i18n.loadPipelineChartError }}
|
||||
</gl-alert>
|
||||
<chart-skeleton-loader v-else-if="isLoading" />
|
||||
<gl-alert v-else-if="hasEmptyDataSet" variant="info" :dismissible="false" class="gl-mt-3">
|
||||
{{ $options.i18n.noDataMessage }}
|
||||
</gl-alert>
|
||||
<gl-line-chart v-else :option="chartOptions" :include-legend-avg-max="true" :data="chartData" />
|
||||
</div>
|
||||
</template>
|
|
@ -0,0 +1,5 @@
|
|||
import { getDateInPast } from '~/lib/utils/datetime_utility';
|
||||
|
||||
const TOTAL_DAYS_TO_SHOW = 365;
|
||||
export const TODAY = new Date();
|
||||
export const START_DATE = getDateInPast(TODAY, TOTAL_DAYS_TO_SHOW);
|
|
@ -0,0 +1,4 @@
|
|||
fragment Count on InstanceStatisticsMeasurement {
|
||||
count
|
||||
recordedAt
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
|
||||
#import "./count.fragment.graphql"
|
||||
|
||||
query pipelineStats(
|
||||
$firstTotal: Int
|
||||
$firstSucceeded: Int
|
||||
$firstFailed: Int
|
||||
$firstCanceled: Int
|
||||
$firstSkipped: Int
|
||||
$endCursorTotal: String
|
||||
$endCursorSucceeded: String
|
||||
$endCursorFailed: String
|
||||
$endCursorCanceled: String
|
||||
$endCursorSkipped: String
|
||||
) {
|
||||
pipelinesTotal: instanceStatisticsMeasurements(
|
||||
identifier: PIPELINES
|
||||
first: $firstTotal
|
||||
after: $endCursorTotal
|
||||
) {
|
||||
nodes {
|
||||
...Count
|
||||
}
|
||||
pageInfo {
|
||||
...PageInfo
|
||||
}
|
||||
}
|
||||
pipelinesSucceeded: instanceStatisticsMeasurements(
|
||||
identifier: PIPELINES_SUCCEEDED
|
||||
first: $firstSucceeded
|
||||
after: $endCursorSucceeded
|
||||
) {
|
||||
nodes {
|
||||
...Count
|
||||
}
|
||||
pageInfo {
|
||||
...PageInfo
|
||||
}
|
||||
}
|
||||
pipelinesFailed: instanceStatisticsMeasurements(
|
||||
identifier: PIPELINES_FAILED
|
||||
first: $firstFailed
|
||||
after: $endCursorFailed
|
||||
) {
|
||||
nodes {
|
||||
...Count
|
||||
}
|
||||
pageInfo {
|
||||
...PageInfo
|
||||
}
|
||||
}
|
||||
pipelinesCanceled: instanceStatisticsMeasurements(
|
||||
identifier: PIPELINES_CANCELED
|
||||
first: $firstCanceled
|
||||
after: $endCursorCanceled
|
||||
) {
|
||||
nodes {
|
||||
...Count
|
||||
}
|
||||
pageInfo {
|
||||
...PageInfo
|
||||
}
|
||||
}
|
||||
pipelinesSkipped: instanceStatisticsMeasurements(
|
||||
identifier: PIPELINES_SKIPPED
|
||||
first: $firstSkipped
|
||||
after: $endCursorSkipped
|
||||
) {
|
||||
nodes {
|
||||
...Count
|
||||
}
|
||||
pageInfo {
|
||||
...PageInfo
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import { masks } from 'dateformat';
|
||||
import { mapKeys, mapValues, pick, sortBy } from 'lodash';
|
||||
import { formatDate } from '~/lib/utils/datetime_utility';
|
||||
|
||||
const { isoDate } = masks;
|
||||
|
@ -38,3 +39,31 @@ export function getAverageByMonth(items = [], options = {}) {
|
|||
return [month, avg];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts values given a data set and a set of keys
|
||||
* @example
|
||||
* const data = { fooBar: { baz: 'quis' }, ignored: 'ignored' };
|
||||
* extractValues(data, ['fooBar'], 'foo', 'baz') => { bazBar: 'quis' }
|
||||
* @param {Object} data set to extract values from
|
||||
* @param {Array} dataKeys keys describing where to look for values in the data set
|
||||
* @param {String} replaceKey name key to be replaced in the data set
|
||||
* @param {String} nestedKey key nested in the data set to be extracted,
|
||||
* this is also used to rename the newly created data set
|
||||
* @return {Object} the newly created data set with the extracted values
|
||||
*/
|
||||
export function extractValues(data, dataKeys = [], replaceKey, nestedKey) {
|
||||
return mapKeys(pick(mapValues(data, nestedKey), dataKeys), (value, key) =>
|
||||
key.replace(replaceKey, nestedKey),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new array of items sorted by the date string of each item
|
||||
* @param {Array} items [description]
|
||||
* @param {String} items[0] date string
|
||||
* @return {Array} the new sorted array.
|
||||
*/
|
||||
export function sortByDate(items = []) {
|
||||
return sortBy(items, ({ recordedAt }) => new Date(recordedAt).getTime());
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import { __ } from '~/locale';
|
|||
import { deprecatedCreateFlash as Flash } from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import SSHMirror from './ssh_mirror';
|
||||
import { hide } from '~/tooltips';
|
||||
|
||||
export default class MirrorRepos {
|
||||
constructor(container) {
|
||||
|
@ -115,7 +116,7 @@ export default class MirrorRepos {
|
|||
/* eslint-disable class-methods-use-this */
|
||||
removeRow($target) {
|
||||
const row = $target.closest('tr');
|
||||
$('.js-delete-mirror', row).tooltip('hide');
|
||||
hide($('.js-delete-mirror', row));
|
||||
row.remove();
|
||||
}
|
||||
/* eslint-enable class-methods-use-this */
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
import initEnviroments from '~/environments/';
|
||||
import initEnvironments from '~/environments/';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', initEnviroments);
|
||||
document.addEventListener('DOMContentLoaded', initEnvironments);
|
||||
|
|
|
@ -7,16 +7,15 @@ import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered
|
|||
import { FILTERED_SEARCH } from '~/pages/constants';
|
||||
import { ISSUABLE_INDEX } from '~/pages/projects/constants';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
addExtraTokensForMergeRequests(IssuableFilteredSearchTokenKeys);
|
||||
new IssuableIndex(ISSUABLE_INDEX.MERGE_REQUEST); // eslint-disable-line no-new
|
||||
|
||||
initFilteredSearch({
|
||||
page: FILTERED_SEARCH.MERGE_REQUESTS,
|
||||
filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
|
||||
useDefaultState: true,
|
||||
});
|
||||
addExtraTokensForMergeRequests(IssuableFilteredSearchTokenKeys);
|
||||
|
||||
new IssuableIndex(ISSUABLE_INDEX.MERGE_REQUEST); // eslint-disable-line no-new
|
||||
new ShortcutsNavigation(); // eslint-disable-line no-new
|
||||
new UsersSelect(); // eslint-disable-line no-new
|
||||
initFilteredSearch({
|
||||
page: FILTERED_SEARCH.MERGE_REQUESTS,
|
||||
filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
|
||||
useDefaultState: true,
|
||||
});
|
||||
|
||||
new UsersSelect(); // eslint-disable-line no-new
|
||||
new ShortcutsNavigation(); // eslint-disable-line no-new
|
||||
|
|
|
@ -5,12 +5,10 @@ import initShow from '../init_merge_request_show';
|
|||
import initIssuableHeaderWarning from '~/vue_shared/components/issuable/init_issuable_header_warning';
|
||||
import store from '~/mr_notes/stores';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initShow();
|
||||
if (gon.features && !gon.features.vueIssuableSidebar) {
|
||||
initSidebarBundle();
|
||||
}
|
||||
initMrNotes();
|
||||
initReviewBar();
|
||||
initIssuableHeaderWarning(store);
|
||||
});
|
||||
initShow();
|
||||
if (gon.features && !gon.features.vueIssuableSidebar) {
|
||||
initSidebarBundle();
|
||||
}
|
||||
initMrNotes();
|
||||
initReviewBar();
|
||||
initIssuableHeaderWarning(store);
|
||||
|
|
|
@ -392,6 +392,7 @@ export default {
|
|||
type="submit"
|
||||
category="primary"
|
||||
variant="success"
|
||||
class="js-no-auto-disable"
|
||||
data-qa-selector="run_pipeline_button"
|
||||
>{{ s__('Pipeline|Run Pipeline') }}</gl-button
|
||||
>
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import { s__ } from '~/locale';
|
||||
|
||||
export default {
|
||||
basic: {
|
||||
text: s__('ProjectTemplates|Basic'),
|
||||
icon: '.template-option .icon-basic',
|
||||
},
|
||||
serenity_valley: {
|
||||
text: s__('ProjectTemplates|Serenity Valley'),
|
||||
icon: '.template-option .icon-serenity_valley',
|
||||
},
|
||||
};
|
|
@ -1,5 +1,6 @@
|
|||
import $ from 'jquery';
|
||||
import DEFAULT_PROJECT_TEMPLATES from 'ee_else_ce/projects/default_project_templates';
|
||||
import DEFAULT_SAMPLE_DATA_TEMPLATES from '~/projects/default_sample_data_templates';
|
||||
import { addSelectOnFocusBehaviour } from '../lib/utils/common_utils';
|
||||
import {
|
||||
convertToTitleCase,
|
||||
|
@ -146,7 +147,8 @@ const bindEvents = () => {
|
|||
$selectedIcon.empty();
|
||||
const value = $(this).val();
|
||||
|
||||
const selectedTemplate = DEFAULT_PROJECT_TEMPLATES[value];
|
||||
const selectedTemplate =
|
||||
DEFAULT_PROJECT_TEMPLATES[value] || DEFAULT_SAMPLE_DATA_TEMPLATES[value];
|
||||
$selectedTemplateText.text(selectedTemplate.text);
|
||||
$(selectedTemplate.icon)
|
||||
.clone()
|
||||
|
|
|
@ -67,7 +67,10 @@ class Admin::SessionsController < ApplicationController
|
|||
end
|
||||
|
||||
def valid_otp_attempt?(user)
|
||||
valid_otp_attempt = user.validate_and_consume_otp!(user_params[:otp_attempt])
|
||||
otp_validation_result =
|
||||
::Users::ValidateOtpService.new(user).execute(user_params[:otp_attempt])
|
||||
valid_otp_attempt = otp_validation_result[:status] == :success
|
||||
|
||||
return valid_otp_attempt if Gitlab::Database.read_only?
|
||||
|
||||
valid_otp_attempt || user.invalidate_otp_backup_code!(user_params[:otp_attempt])
|
||||
|
|
|
@ -47,7 +47,10 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
|
|||
end
|
||||
|
||||
def create
|
||||
if current_user.validate_and_consume_otp!(params[:pin_code])
|
||||
otp_validation_result =
|
||||
::Users::ValidateOtpService.new(current_user).execute(params[:pin_code])
|
||||
|
||||
if otp_validation_result[:status] == :success
|
||||
ActiveSession.destroy_all_but_current(current_user, session)
|
||||
|
||||
Users::UpdateService.new(current_user, user: current_user, otp_required_for_login: true).execute! do |user|
|
||||
|
|
|
@ -217,7 +217,7 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def export_csv
|
||||
ExportCsvWorker.perform_async(current_user.id, project.id, finder_options.to_h) # rubocop:disable CodeReuse/Worker
|
||||
IssuableExportCsvWorker.perform_async(:issue, current_user.id, project.id, finder_options.to_h) # rubocop:disable CodeReuse/Worker
|
||||
|
||||
index_path = project_issues_path(project)
|
||||
message = _('Your CSV export has started. It will be emailed to %{email} when complete.') % { email: current_user.notification_email }
|
||||
|
@ -326,7 +326,7 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def store_uri
|
||||
if request.get? && !request.xhr?
|
||||
if request.get? && request.format.html?
|
||||
store_location_for :user, request.fullpath
|
||||
end
|
||||
end
|
||||
|
|
|
@ -264,8 +264,11 @@ class SessionsController < Devise::SessionsController
|
|||
end
|
||||
|
||||
def valid_otp_attempt?(user)
|
||||
user.validate_and_consume_otp!(user_params[:otp_attempt]) ||
|
||||
user.invalidate_otp_backup_code!(user_params[:otp_attempt])
|
||||
otp_validation_result =
|
||||
::Users::ValidateOtpService.new(user).execute(user_params[:otp_attempt])
|
||||
return true if otp_validation_result[:status] == :success
|
||||
|
||||
user.invalidate_otp_backup_code!(user_params[:otp_attempt])
|
||||
end
|
||||
|
||||
def log_audit_event(user, resource, options = {})
|
||||
|
|
|
@ -153,8 +153,10 @@ class IssuableFinder
|
|||
end
|
||||
|
||||
def row_count
|
||||
fast_fail = Feature.enabled?(:soft_fail_count_by_state, params.group || params.project)
|
||||
|
||||
Gitlab::IssuablesCountForState
|
||||
.new(self, nil, fast_fail: Feature.enabled?(:soft_fail_count_by_state, parent))
|
||||
.new(self, nil, fast_fail: fast_fail)
|
||||
.for_state_or_opened(params[:state])
|
||||
end
|
||||
|
||||
|
|
|
@ -82,6 +82,7 @@ module IssuablesHelper
|
|||
when Issue
|
||||
IssueSerializer
|
||||
when MergeRequest
|
||||
opts[:experiment_enabled] = :suggest_pipeline if experiment_enabled?(:suggest_pipeline) && opts[:serializer] == 'widget'
|
||||
MergeRequestSerializer
|
||||
end
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ module Emails
|
|||
|
||||
def issues_csv_email(user, project, csv_data, export_status)
|
||||
@project = project
|
||||
@issues_count = export_status.fetch(:rows_expected)
|
||||
@count = export_status.fetch(:rows_expected)
|
||||
@written_count = export_status.fetch(:rows_written)
|
||||
@truncated = export_status.fetch(:truncated)
|
||||
|
||||
|
|
|
@ -110,6 +110,20 @@ module Emails
|
|||
mail_answer_thread(@merge_request, merge_request_thread_options(mwps_set_by_user_id, recipient_id, reason))
|
||||
end
|
||||
|
||||
def merge_requests_csv_email(user, project, csv_data, export_status)
|
||||
@project = project
|
||||
@count = export_status.fetch(:rows_expected)
|
||||
@written_count = export_status.fetch(:rows_written)
|
||||
@truncated = export_status.fetch(:truncated)
|
||||
|
||||
filename = "#{project.full_path.parameterize}_merge_requests_#{Date.current.iso8601}.csv"
|
||||
attachments[filename] = { content: csv_data, mime_type: 'text/csv' }
|
||||
mail(to: user.notification_email_for(@project.group), subject: subject("Exported merge requests")) do |format|
|
||||
format.html { render layout: 'mailer' }
|
||||
format.text { render layout: 'mailer' }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def setup_merge_request_mail(merge_request_id, recipient_id, present: false)
|
||||
|
|
|
@ -14,7 +14,7 @@ module BlobViewer
|
|||
{}.tap do |h|
|
||||
h[:rendered] = blob.rendered_markup if blob.respond_to?(:rendered_markup)
|
||||
|
||||
if Feature.enabled?(:cached_markdown_blob, blob.project)
|
||||
if Feature.enabled?(:cached_markdown_blob, blob.project, default_enabled: true)
|
||||
h[:cache_key] = ['blob', blob.id, 'commit', blob.commit_id]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -793,7 +793,7 @@ class User < ApplicationRecord
|
|||
end
|
||||
|
||||
def two_factor_otp_enabled?
|
||||
otp_required_for_login?
|
||||
otp_required_for_login? || Feature.enabled?(:forti_authenticator, self)
|
||||
end
|
||||
|
||||
def two_factor_u2f_enabled?
|
||||
|
|
|
@ -67,15 +67,15 @@ class MergeRequestWidgetEntity < Grape::Entity
|
|||
)
|
||||
end
|
||||
|
||||
expose :user_callouts_path, if: -> (*) { Gitlab::Experimentation.enabled?(:suggest_pipeline) } do |_merge_request|
|
||||
expose :user_callouts_path, if: -> (_, opts) { opts[:experiment_enabled] == :suggest_pipeline } do |_merge_request|
|
||||
user_callouts_path
|
||||
end
|
||||
|
||||
expose :suggest_pipeline_feature_id, if: -> (*) { Gitlab::Experimentation.enabled?(:suggest_pipeline) } do |_merge_request|
|
||||
expose :suggest_pipeline_feature_id, if: -> (_, opts) { opts[:experiment_enabled] == :suggest_pipeline } do |_merge_request|
|
||||
SUGGEST_PIPELINE
|
||||
end
|
||||
|
||||
expose :is_dismissed_suggest_pipeline, if: -> (*) { Gitlab::Experimentation.enabled?(:suggest_pipeline) } do |_merge_request|
|
||||
expose :is_dismissed_suggest_pipeline, if: -> (_, opts) { opts[:experiment_enabled] == :suggest_pipeline } do |_merge_request|
|
||||
current_user && current_user.dismissed_callout?(feature_name: SUGGEST_PIPELINE)
|
||||
end
|
||||
|
||||
|
|
|
@ -8,7 +8,8 @@ module MergeRequests
|
|||
# Target attachment size before base64 encoding
|
||||
TARGET_FILESIZE = 15.megabytes
|
||||
|
||||
def initialize(merge_requests)
|
||||
def initialize(merge_requests, project)
|
||||
@project = project
|
||||
@merge_requests = merge_requests
|
||||
end
|
||||
|
||||
|
@ -16,6 +17,10 @@ module MergeRequests
|
|||
csv_builder.render(TARGET_FILESIZE)
|
||||
end
|
||||
|
||||
def email(user)
|
||||
Notify.merge_requests_csv_email(user, @project, csv_data, csv_builder.status).deliver_now
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def csv_builder
|
||||
|
|
|
@ -14,10 +14,16 @@ module Projects
|
|||
def execute
|
||||
return project unless validate_template!
|
||||
|
||||
file = built_in_template&.file
|
||||
file = built_in_template&.file || sample_data_template&.file
|
||||
|
||||
override_params = params.dup
|
||||
params[:file] = file
|
||||
|
||||
if built_in_template
|
||||
params[:file] = built_in_template.file
|
||||
elsif sample_data_template
|
||||
params[:file] = sample_data_template.file
|
||||
params[:sample_data] = true
|
||||
end
|
||||
|
||||
GitlabProjectsImportService.new(current_user, params, override_params).execute
|
||||
ensure
|
||||
|
@ -27,7 +33,7 @@ module Projects
|
|||
private
|
||||
|
||||
def validate_template!
|
||||
return true if built_in_template
|
||||
return true if built_in_template || sample_data_template
|
||||
|
||||
project.errors.add(:template_name, _("'%{template_name}' is unknown or invalid" % { template_name: template_name }))
|
||||
false
|
||||
|
@ -39,6 +45,12 @@ module Projects
|
|||
end
|
||||
end
|
||||
|
||||
def sample_data_template
|
||||
strong_memoize(:sample_data_template) do
|
||||
Gitlab::SampleDataTemplate.find(template_name)
|
||||
end
|
||||
end
|
||||
|
||||
def project
|
||||
@project ||= ::Project.new(namespace_id: params[:namespace_id])
|
||||
end
|
||||
|
|
|
@ -66,6 +66,7 @@ module Projects
|
|||
end
|
||||
|
||||
if template_file
|
||||
data[:sample_data] = params.delete(:sample_data) if params.key?(:sample_data)
|
||||
params[:import_type] = 'gitlab_project'
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Users
|
||||
class ValidateOtpService < BaseService
|
||||
def initialize(current_user)
|
||||
@current_user = current_user
|
||||
@strategy = if Feature.enabled?(:forti_authenticator, current_user)
|
||||
::Gitlab::Auth::Otp::Strategies::FortiAuthenticator.new(current_user)
|
||||
else
|
||||
::Gitlab::Auth::Otp::Strategies::Devise.new(current_user)
|
||||
end
|
||||
end
|
||||
|
||||
def execute(otp_code)
|
||||
strategy.validate(otp_code)
|
||||
rescue StandardError => ex
|
||||
Gitlab::ErrorTracking.log_exception(ex)
|
||||
error(message: ex.message)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :strategy
|
||||
end
|
||||
end
|
|
@ -92,7 +92,7 @@
|
|||
%li.nav-item
|
||||
%div
|
||||
- sign_in_text = allow_signup? ? _('Sign in / Register') : _('Sign in')
|
||||
= link_to sign_in_text, new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in'
|
||||
= link_to sign_in_text, new_session_path(:user, redirect_to_referer: 'yes'), class: 'gl-button btn btn-sign-in'
|
||||
|
||||
%button.navbar-toggler.d-block.d-sm-none{ type: 'button' }
|
||||
%span.sr-only= _('Toggle navigation')
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
%p{ style: 'font-size:18px; text-align:center; line-height:30px;' }
|
||||
- project_link = link_to(@project.full_name, project_url(@project), style: "color:#3777b0; text-decoration:none; display:block;")
|
||||
= _('Your CSV export of %{count} from project %{project_link} has been added to this email as an attachment.').html_safe % { count: pluralize(@written_count, type.to_s), project_link: project_link }
|
||||
- if @truncated
|
||||
%p
|
||||
= _('This attachment has been truncated to avoid exceeding the maximum allowed attachment size of 15MB. %{written_count} of %{count} issues have been included. Consider re-exporting with a narrower selection of issues.') % { written_count: @written_count, count: @count }
|
|
@ -1,6 +1 @@
|
|||
%p{ style: 'font-size:18px; text-align:center; line-height:30px;' }
|
||||
- project_link = link_to(@project.full_name, project_url(@project), style: "color:#3777b0; text-decoration:none; display:block;")
|
||||
= _('Your CSV export of %{issues_count} from project %{project_link} has been added to this email as an attachment.').html_safe % { issues_count: pluralize(@written_count, 'issue'), project_link: project_link }
|
||||
- if @truncated
|
||||
%p
|
||||
= _('This attachment has been truncated to avoid exceeding the maximum allowed attachment size of 15MB. %{written_count} of %{issues_count} issues have been included. Consider re-exporting with a narrower selection of issues.') % { written_count: @written_count, issues_count: @issues_count }
|
||||
= render 'issuable_csv_export', type: :issue
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
= render 'issuable_csv_export', type: :merge_request
|
|
@ -0,0 +1,5 @@
|
|||
<%= _('Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment.') % { written_count: pluralize(@written_count, 'merge request'), project_name: @project.full_name, project_url: project_url(@project) } %>
|
||||
|
||||
<% if @truncated %>
|
||||
<%= _('This attachment has been truncated to avoid exceeding the maximum allowed attachment size of 15MB. %{written_count} of %{merge_requests_count} issues have been included. Consider re-exporting with a narrower selection of issues.') % { written_count: @written_count, merge_requests_count: @merge_requests_count} %>
|
||||
<% end %>
|
|
@ -1,7 +1,21 @@
|
|||
- f ||= local_assigns[:f]
|
||||
|
||||
.project-templates-buttons.import-buttons.col-sm-12
|
||||
= render 'projects/project_templates/built_in_templates'
|
||||
.project-templates-buttons.col-sm-12
|
||||
%ul.nav-tabs.nav-links.nav.scrolling-tabs
|
||||
%li.built-in-tab
|
||||
%a.nav-link.active{ href: "#built-in", data: { toggle: 'tab'} }
|
||||
= _('Built-in')
|
||||
%span.badge.badge-pill= Gitlab::ProjectTemplate.all.count
|
||||
%li.sample-data-templates-tab
|
||||
%a.nav-link{ href: "#sample-data-templates", data: { toggle: 'tab'} }
|
||||
= _('Sample Data')
|
||||
%span.badge.badge-pill= Gitlab::SampleDataTemplate.all.count
|
||||
|
||||
.tab-content
|
||||
.project-templates-buttons.import-buttons.tab-pane.active#built-in
|
||||
= render partial: 'projects/project_templates/template', collection: Gitlab::ProjectTemplate.all
|
||||
.project-templates-buttons.import-buttons.tab-pane#sample-data-templates
|
||||
= render partial: 'projects/project_templates/template', collection: Gitlab::SampleDataTemplate.all
|
||||
|
||||
.project-fields-form
|
||||
= render 'projects/project_templates/project_fields_form'
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
- Gitlab::ProjectTemplate.all.each do |template|
|
||||
.template-option.d-flex.align-items-center{ data: { qa_selector: 'template_option_row' } }
|
||||
.logo.gl-mr-3.px-1
|
||||
= image_tag template.logo, size: 32, class: "btn-template-icon icon-#{template.name}"
|
||||
.description
|
||||
%strong
|
||||
= template.title
|
||||
%br
|
||||
.text-muted
|
||||
= template.description
|
||||
.controls.d-flex.align-items-center
|
||||
%a.btn.btn-default.gl-mr-3{ href: template.preview, rel: 'noopener noreferrer', target: '_blank', data: { track_label: "template_preview", track_property: template.name, track_event: "click_button", track_value: "" } }
|
||||
= _("Preview")
|
||||
%label.btn.btn-success.template-button.choose-template.gl-mb-0{ for: template.name }
|
||||
%input{ type: "radio", autocomplete: "off", name: "project[template_name]", id: template.name, value: template.name, data: { track_label: "template_use", track_property: template.name, track_event: "click_button", track_value: "" } }
|
||||
%span{ data: { qa_selector: 'use_template_button' } }
|
||||
= _("Use template")
|
|
@ -0,0 +1,16 @@
|
|||
.template-option.d-flex.align-items-center{ data: { qa_selector: 'template_option_row' } }
|
||||
.logo.gl-mr-3.px-1
|
||||
= image_tag template.logo, size: 32, class: "btn-template-icon icon-#{template.name}"
|
||||
.description
|
||||
%strong
|
||||
= template.title
|
||||
%br
|
||||
.text-muted
|
||||
= template.description
|
||||
.controls.d-flex.align-items-center
|
||||
%a.btn.gl-button.btn-default.gl-mr-3{ href: template.preview, rel: 'noopener noreferrer', target: '_blank', data: { track_label: "template_preview", track_property: template.name, track_event: "click_button", track_value: "" } }
|
||||
= _("Preview")
|
||||
%label.btn.gl-button.btn-success.template-button.choose-template.gl-mb-0{ for: template.name }
|
||||
%input{ type: "radio", autocomplete: "off", name: "project[template_name]", id: template.name, value: template.name, data: { track_label: "template_use", track_property: template.name, track_event: "click_button", track_value: "" } }
|
||||
%span{ data: { qa_selector: 'use_template_button' } }
|
||||
= _("Use template")
|
|
@ -1,13 +1,13 @@
|
|||
- if show_auto_devops_implicitly_enabled_banner?(project, current_user)
|
||||
.qa-auto-devops-banner.auto-devops-implicitly-enabled-banner.alert.alert-info
|
||||
- more_information_link = link_to _('More information'), help_page_path('topics/autodevops/index.md'), target: '_blank', class: 'alert-link'
|
||||
- auto_devops_message = s_("AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}") % { more_information_link: more_information_link }
|
||||
= auto_devops_message.html_safe
|
||||
.alert-link-group
|
||||
= link_to _('Settings'), project_settings_ci_cd_path(project), class: 'alert-link'
|
||||
|
|
||||
= link_to _('Dismiss'), '#', class: 'hide-auto-devops-implicitly-enabled-banner alert-link', data: { project_id: project.id }
|
||||
.qa-auto-devops-banner.auto-devops-implicitly-enabled-banner.gl-alert.gl-alert-info
|
||||
= sprite_icon('information-o', css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
|
||||
%button.js-close.gl-alert-dismiss{ type: 'button', 'aria-label' => _('Dismiss'), class: 'hide-auto-devops-implicitly-enabled-banner alert-link', data: { project_id: project.id } }
|
||||
= sprite_icon('close', css_class: 'gl-icon')
|
||||
.gl-alert-body
|
||||
= s_("AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found.")
|
||||
- unless Gitlab.config.registry.enabled
|
||||
%div
|
||||
= icon('exclamation-triangle')
|
||||
= _('Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work.')
|
||||
.gl-alert-actions.gl-mt-3
|
||||
= link_to _('Settings'), project_settings_ci_cd_path(project), class: 'alert-link btn gl-button btn-info'
|
||||
= link_to _('More information'), help_page_path('topics/autodevops/index.md'), target: '_blank', class: 'alert-link btn gl-button btn-default gl-ml-2'
|
||||
|
|
|
@ -1571,6 +1571,14 @@
|
|||
:weight: 1
|
||||
:idempotent:
|
||||
:tags: []
|
||||
- :name: issuable_export_csv
|
||||
:feature_category: :issue_tracking
|
||||
:has_external_dependencies:
|
||||
:urgency: :low
|
||||
:resource_boundary: :cpu
|
||||
:weight: 1
|
||||
:idempotent:
|
||||
:tags: []
|
||||
- :name: issue_placement
|
||||
:feature_category: :issue_tracking
|
||||
:has_external_dependencies:
|
||||
|
|
|
@ -15,8 +15,6 @@ class ExportCsvWorker # rubocop:disable Scalability/IdempotentWorker
|
|||
params[:project_id] = project_id
|
||||
params.delete(:sort)
|
||||
|
||||
issues = IssuesFinder.new(@current_user, params).execute
|
||||
|
||||
Issues::ExportCsvService.new(issues, @project).email(@current_user)
|
||||
IssuableExportCsvWorker.perform_async(:issue, @current_user.id, @project.id, params)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class IssuableExportCsvWorker # rubocop:disable Scalability/IdempotentWorker
|
||||
include ApplicationWorker
|
||||
|
||||
feature_category :issue_tracking
|
||||
worker_resource_boundary :cpu
|
||||
loggable_arguments 2
|
||||
|
||||
PERMITTED_TYPES = [:merge_request, :issue].freeze
|
||||
|
||||
def perform(type, current_user_id, project_id, params)
|
||||
@type = type.to_sym
|
||||
check_permitted_type!
|
||||
process_params!(params, project_id)
|
||||
|
||||
@current_user = User.find(current_user_id)
|
||||
@project = Project.find(project_id)
|
||||
@service = service(find_objects(params))
|
||||
|
||||
@service.email(@current_user)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_objects(params)
|
||||
case @type
|
||||
when :issue
|
||||
IssuesFinder.new(@current_user, params).execute
|
||||
when :merge_request
|
||||
MergeRequestsFinder.new(@current_user, params).execute
|
||||
end
|
||||
end
|
||||
|
||||
def service(issuables)
|
||||
case @type
|
||||
when :issue
|
||||
Issues::ExportCsvService.new(issuables, @project)
|
||||
when :merge_request
|
||||
MergeRequests::ExportCsvService.new(issuables, @project)
|
||||
end
|
||||
end
|
||||
|
||||
def process_params!(params, project_id)
|
||||
params.symbolize_keys!
|
||||
params[:project_id] = project_id
|
||||
params.delete(:sort)
|
||||
end
|
||||
|
||||
def check_permitted_type!
|
||||
raise ArgumentError, "type parameter must be :issue or :merge_request, it was #{@type}" unless PERMITTED_TYPES.include?(@type)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Migrate auto devops message from bootstrap
|
||||
merge_request: 45221
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add undo helpers for change_column_type_concurrently and cleanup_concurrent_column_type_change
|
||||
merge_request: 44155
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Enable caching of markdown when viewing blob
|
||||
merge_request: 45367
|
||||
author:
|
||||
type: performance
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix redirects to issue sidebar JSON when visiting the login page
|
||||
merge_request: 45194
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add Sample Data
|
||||
merge_request: 41699
|
||||
author:
|
||||
type: added
|
|
@ -4,4 +4,4 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44300
|
|||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/263406
|
||||
type: development
|
||||
group: group::source code
|
||||
default_enabled: false
|
||||
default_enabled: true
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
name: forti_authenticator
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45055
|
||||
rollout_issue_url:
|
||||
type: development
|
||||
group: group::access
|
||||
default_enabled: false
|
|
@ -1,7 +0,0 @@
|
|||
---
|
||||
name: push_rules_supersede_code_owners
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44126
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/262019
|
||||
type: development
|
||||
group: group::source code
|
||||
default_enabled: false
|
|
@ -4,4 +4,4 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/33277
|
|||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/233895
|
||||
group: group::package
|
||||
type: development
|
||||
default_enabled: false
|
||||
default_enabled: true
|
||||
|
|
|
@ -1022,6 +1022,21 @@ production: &base
|
|||
# cas3:
|
||||
# session_duration: 28800
|
||||
|
||||
# FortiAuthenticator settings
|
||||
forti_authenticator:
|
||||
# Allow using FortiAuthenticator as OTP provider
|
||||
enabled: false
|
||||
|
||||
# Host and port of FortiAuthenticator instance
|
||||
# host: forti_authenticator.example.com
|
||||
# port: 443
|
||||
|
||||
# Username for accessing FortiAuthenticator API
|
||||
# username: john
|
||||
|
||||
# Access token for FortiAuthenticator API
|
||||
# access_token: 123s3cr3t456
|
||||
|
||||
# Shared file storage settings
|
||||
shared:
|
||||
# path: /mnt/gitlab # Default: shared
|
||||
|
|
|
@ -766,6 +766,13 @@ Gitlab.ee do
|
|||
Settings.smartcard['san_extensions'] = false if Settings.smartcard['san_extensions'].nil?
|
||||
end
|
||||
|
||||
#
|
||||
# FortiAuthenticator
|
||||
#
|
||||
Settings['forti_authenticator'] ||= Settingslogic.new({})
|
||||
Settings.forti_authenticator['enabled'] = false if Settings.forti_authenticator['enabled'].nil?
|
||||
Settings.forti_authenticator['port'] = 443 if Settings.forti_authenticator['port'].to_i == 0
|
||||
|
||||
#
|
||||
# Extra customization
|
||||
#
|
||||
|
|
|
@ -152,6 +152,8 @@
|
|||
- 2
|
||||
- - irker
|
||||
- 1
|
||||
- - issuable_export_csv
|
||||
- 1
|
||||
- - issue_placement
|
||||
- 2
|
||||
- - issue_rebalancing
|
||||
|
|
|
@ -146,6 +146,7 @@ failsafe
|
|||
Falco
|
||||
fastlane
|
||||
favicon
|
||||
Figma
|
||||
Filebeat
|
||||
Fio
|
||||
firewalled
|
||||
|
@ -312,6 +313,7 @@ passwordless
|
|||
Patroni
|
||||
performant
|
||||
PgBouncer
|
||||
Phabricator
|
||||
phaser
|
||||
phasers
|
||||
Pipfile
|
||||
|
@ -352,6 +354,7 @@ Python
|
|||
Qualys
|
||||
Rackspace
|
||||
Raspbian
|
||||
Rdoc
|
||||
reachability
|
||||
rebase
|
||||
rebased
|
||||
|
@ -433,7 +436,10 @@ smartcard
|
|||
smartcards
|
||||
SMTP
|
||||
Sobelow
|
||||
Solarized
|
||||
Sourcegraph
|
||||
sparkline
|
||||
sparklines
|
||||
spidering
|
||||
Splunk
|
||||
SpotBugs
|
||||
|
@ -449,6 +455,8 @@ subfolder
|
|||
subfolders
|
||||
subgraph
|
||||
subgraphs
|
||||
subkey
|
||||
subkeys
|
||||
sublicense
|
||||
sublicensed
|
||||
sublicenses
|
||||
|
@ -468,6 +476,7 @@ subtrees
|
|||
sudo
|
||||
syslog
|
||||
tcpdump
|
||||
Thanos
|
||||
Tiller
|
||||
timecop
|
||||
todos
|
||||
|
@ -490,6 +499,7 @@ unarchived
|
|||
unarchives
|
||||
unarchiving
|
||||
unassign
|
||||
unassigning
|
||||
unassigns
|
||||
uncheck
|
||||
unchecked
|
||||
|
|
|
@ -2,6 +2,35 @@
|
|||
|
||||
Uploads represent all user data that may be sent to GitLab as a single file. As an example, avatars and notes' attachments are uploads. Uploads are integral to GitLab functionality, and therefore cannot be disabled.
|
||||
|
||||
## Upload parameters
|
||||
|
||||
> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/214785) in GitLab 13.5.
|
||||
> - It's [deployed behind a feature flag](../user/feature_flags.md), enabled by default.
|
||||
> - It's enabled on GitLab.com.
|
||||
> - It's recommended for production use.
|
||||
> - For GitLab self-managed instances, GitLab administrators can opt to disable it. **(CORE ONLY)**
|
||||
|
||||
In 13.5 and later, upload parameters are passed [between Workhorse and GitLab Rails](../development/architecture.md#simplified-component-overview) differently than they
|
||||
were before.
|
||||
|
||||
This change is deployed behind a feature flag that is **enabled by default**.
|
||||
|
||||
If you experience any issues with upload,
|
||||
[GitLab administrators with access to the GitLab Rails console](./feature_flags.md)
|
||||
can opt to disable it.
|
||||
|
||||
To enable it:
|
||||
|
||||
```ruby
|
||||
Feature.enable(:upload_middleware_jwt_params_handler)
|
||||
```
|
||||
|
||||
To disable it:
|
||||
|
||||
```ruby
|
||||
Feature.disable(:upload_middleware_jwt_params_handler)
|
||||
```
|
||||
|
||||
## Using local storage
|
||||
|
||||
NOTE: **Note:**
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
---
|
||||
stage: Growth
|
||||
group: Expansion
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
|
||||
---
|
||||
|
||||
# Experiments API
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/262725) in GitLab 13.5.
|
||||
|
||||
This API is for listing Experiments [experiment use in development of GitLab](../development/experiment_guide/index.md).
|
||||
|
||||
All methods require user be a [GitLab team member](https://gitlab.com/groups/gitlab-com/-/group_members) for authorization.
|
||||
|
||||
## List all experiments
|
||||
|
||||
Get a list of all experiments, with its enabled status.
|
||||
|
||||
```plaintext
|
||||
GET /experiments
|
||||
```
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/experiments"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"key": "experiment_1",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"key": "experiment_2",
|
||||
"enabled": false
|
||||
}
|
||||
]
|
||||
```
|
|
@ -87,10 +87,12 @@ the `plan` parameter associated with a namespace:
|
|||
]
|
||||
```
|
||||
|
||||
Users on GitLab.com will also see a `max_seats_used` parameter. `max_seats_used`
|
||||
is the highest number of users the group had.
|
||||
Users on GitLab.com will also see `max_seats_used` and `seats_in_use` parameters.
|
||||
`max_seats_used` is the highest number of users the group had. `seats_in_use` is
|
||||
the number of license seats currently being used. Both values are updated
|
||||
once a day.
|
||||
|
||||
`max_seats_used` will be non-zero only for namespaces on paid plans.
|
||||
`max_seats_used` and `seats_in_use` will be non-zero only for namespaces on paid plans.
|
||||
|
||||
```json
|
||||
[
|
||||
|
@ -99,6 +101,7 @@ is the highest number of users the group had.
|
|||
"name": "user1",
|
||||
"billable_members_count": 2,
|
||||
"max_seats_used": 3,
|
||||
"seats_in_use": 2,
|
||||
...
|
||||
}
|
||||
]
|
||||
|
@ -141,6 +144,7 @@ Example response:
|
|||
"members_count_with_descendants": 2,
|
||||
"billable_members_count": 2,
|
||||
"max_seats_used": 0,
|
||||
"seats_in_use": 0,
|
||||
"plan": "default",
|
||||
"trial_ends_on": null,
|
||||
"trial": false
|
||||
|
@ -181,6 +185,7 @@ Example response:
|
|||
"members_count_with_descendants": 2,
|
||||
"billable_members_count": 2,
|
||||
"max_seats_used": 0,
|
||||
"seats_in_use": 0,
|
||||
"plan": "default",
|
||||
"trial_ends_on": null,
|
||||
"trial": false
|
||||
|
@ -208,6 +213,7 @@ Example response:
|
|||
"members_count_with_descendants": 2,
|
||||
"billable_members_count": 2,
|
||||
"max_seats_used": 0,
|
||||
"seats_in_use": 0,
|
||||
"plan": "default",
|
||||
"trial_ends_on": null,
|
||||
"trial": false
|
||||
|
|
|
@ -83,12 +83,17 @@ POST /projects/:id/snippets
|
|||
|
||||
Parameters:
|
||||
|
||||
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
|
||||
- `title` (required) - The title of a snippet
|
||||
- `file_name` (required) - The name of a snippet file
|
||||
- `description` (optional) - The description of a snippet
|
||||
- `content` (required) - The content of a snippet
|
||||
- `visibility` (required) - The snippet's visibility
|
||||
| Attribute | Type | Required | Description |
|
||||
|:------------------|:----------------|:---------|:----------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | integer | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||
| `title` | string | yes | Title of a snippet |
|
||||
| `file_name` | string | no | Deprecated: Use `files` instead. Name of a snippet file |
|
||||
| `content` | string | no | Deprecated: Use `files` instead. Content of a snippet |
|
||||
| `description` | string | no | Description of a snippet |
|
||||
| `visibility` | string | no | Snippet's [visibility](#snippet-visibility-level) |
|
||||
| `files` | array of hashes | no | An array of snippet files |
|
||||
| `files:file_path` | string | yes | File path of the snippet file |
|
||||
| `files:content` | string | yes | Content of the snippet file |
|
||||
|
||||
Example request:
|
||||
|
||||
|
@ -105,9 +110,13 @@ curl --request POST "https://gitlab.com/api/v4/projects/:id/snippets" \
|
|||
{
|
||||
"title" : "Example Snippet Title",
|
||||
"description" : "More verbose snippet description",
|
||||
"file_name" : "example.txt",
|
||||
"content" : "source code \n with multiple lines\n",
|
||||
"visibility" : "private"
|
||||
"visibility" : "private",
|
||||
"files": [
|
||||
{
|
||||
"file_path": "example.txt",
|
||||
"content" : "source code \n with multiple lines\n",
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -121,13 +130,22 @@ PUT /projects/:id/snippets/:snippet_id
|
|||
|
||||
Parameters:
|
||||
|
||||
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
|
||||
- `snippet_id` (required) - The ID of a project's snippet
|
||||
- `title` (optional) - The title of a snippet
|
||||
- `file_name` (optional) - The name of a snippet file
|
||||
- `description` (optional) - The description of a snippet
|
||||
- `content` (optional) - The content of a snippet
|
||||
- `visibility` (optional) - The snippet's visibility
|
||||
| Attribute | Type | Required | Description |
|
||||
|:----------------------|:----------------|:---------|:----------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | integer | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||
| `snippet_id` | integer | yes | The ID of a project's snippet |
|
||||
| `title` | string | no | Title of a snippet |
|
||||
| `file_name` | string | no | Deprecated: Use `files` instead. Name of a snippet file |
|
||||
| `content` | string | no | Deprecated: Use `files` instead. Content of a snippet |
|
||||
| `description` | string | no | Description of a snippet |
|
||||
| `visibility` | string | no | Snippet's [visibility](#snippet-visibility-level) |
|
||||
| `files` | array of hashes | no | An array of snippet files |
|
||||
| `files:action` | string | yes | Type of action to perform on the file, one of: 'create', 'update', 'delete', 'move' |
|
||||
| `files:file_path` | string | no | File path of the snippet file |
|
||||
| `files:previous_path` | string | no | Previous path of the snippet file |
|
||||
| `files:content` | string | no | Content of the snippet file |
|
||||
|
||||
Updates to snippets with multiple files *must* use the `files` attribute.
|
||||
|
||||
Example request:
|
||||
|
||||
|
@ -144,9 +162,14 @@ curl --request PUT "https://gitlab.com/api/v4/projects/:id/snippets/:snippet_id"
|
|||
{
|
||||
"title" : "Updated Snippet Title",
|
||||
"description" : "More verbose snippet description",
|
||||
"file_name" : "new_filename.txt",
|
||||
"content" : "updated source code \n with multiple lines\n",
|
||||
"visibility" : "private"
|
||||
"visibility" : "private",
|
||||
"files": [
|
||||
{
|
||||
"action": "update",
|
||||
"file_path": "example.txt",
|
||||
"content" : "updated source code \n with multiple lines\n"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -2241,6 +2241,113 @@ PUT /projects/:id/transfer
|
|||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
|
||||
| `namespace` | integer/string | yes | The ID or path of the namespace to transfer to project to |
|
||||
|
||||
Example request:
|
||||
|
||||
```shell
|
||||
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/transfer?namespace=14"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 7,
|
||||
"description": "",
|
||||
"name": "hello-world",
|
||||
"name_with_namespace": "cute-cats / hello-world",
|
||||
"path": "hello-world",
|
||||
"path_with_namespace": "cute-cats/hello-world",
|
||||
"created_at": "2020-10-15T16:25:22.415Z",
|
||||
"default_branch": "master",
|
||||
"tag_list": [],
|
||||
"ssh_url_to_repo": "git@gitlab.example.com:cute-cats/hello-world.git",
|
||||
"http_url_to_repo": "https://gitlab.example.com/cute-cats/hello-world.git",
|
||||
"web_url": "https://gitlab.example.com/cute-cats/hello-world",
|
||||
"readme_url": "https://gitlab.example.com/cute-cats/hello-world/-/blob/master/README.md",
|
||||
"avatar_url": null,
|
||||
"forks_count": 0,
|
||||
"star_count": 0,
|
||||
"last_activity_at": "2020-10-15T16:25:22.415Z",
|
||||
"namespace": {
|
||||
"id": 18,
|
||||
"name": "cute-cats",
|
||||
"path": "cute-cats",
|
||||
"kind": "group",
|
||||
"full_path": "cute-cats",
|
||||
"parent_id": null,
|
||||
"avatar_url": null,
|
||||
"web_url": "https://gitlab.example.com/groups/cute-cats"
|
||||
},
|
||||
"_links": {
|
||||
"self": "https://gitlab.example.com/api/v4/projects/7",
|
||||
"issues": "https://gitlab.example.com/api/v4/projects/7/issues",
|
||||
"merge_requests": "https://gitlab.example.com/api/v4/projects/7/merge_requests",
|
||||
"repo_branches": "https://gitlab.example.com/api/v4/projects/7/repository/branches",
|
||||
"labels": "https://gitlab.example.com/api/v4/projects/7/labels",
|
||||
"events": "https://gitlab.example.com/api/v4/projects/7/events",
|
||||
"members": "https://gitlab.example.com/api/v4/projects/7/members"
|
||||
},
|
||||
"packages_enabled": true,
|
||||
"empty_repo": false,
|
||||
"archived": false,
|
||||
"visibility": "private",
|
||||
"resolve_outdated_diff_discussions": false,
|
||||
"container_registry_enabled": true,
|
||||
"container_expiration_policy": {
|
||||
"cadence": "7d",
|
||||
"enabled": false,
|
||||
"keep_n": null,
|
||||
"older_than": null,
|
||||
"name_regex": null,
|
||||
"name_regex_keep": null,
|
||||
"next_run_at": "2020-10-22T16:25:22.746Z"
|
||||
},
|
||||
"issues_enabled": true,
|
||||
"merge_requests_enabled": true,
|
||||
"wiki_enabled": true,
|
||||
"jobs_enabled": true,
|
||||
"snippets_enabled": true,
|
||||
"service_desk_enabled": false,
|
||||
"service_desk_address": null,
|
||||
"can_create_merge_request_in": true,
|
||||
"issues_access_level": "enabled",
|
||||
"repository_access_level": "enabled",
|
||||
"merge_requests_access_level": "enabled",
|
||||
"forking_access_level": "enabled",
|
||||
"wiki_access_level": "enabled",
|
||||
"builds_access_level": "enabled",
|
||||
"snippets_access_level": "enabled",
|
||||
"pages_access_level": "enabled",
|
||||
"emails_disabled": null,
|
||||
"shared_runners_enabled": true,
|
||||
"lfs_enabled": true,
|
||||
"creator_id": 2,
|
||||
"import_status": "none",
|
||||
"open_issues_count": 0,
|
||||
"ci_default_git_depth": 50,
|
||||
"public_jobs": true,
|
||||
"build_timeout": 3600,
|
||||
"auto_cancel_pending_pipelines": "enabled",
|
||||
"build_coverage_regex": null,
|
||||
"ci_config_path": null,
|
||||
"shared_with_groups": [],
|
||||
"only_allow_merge_if_pipeline_succeeds": false,
|
||||
"allow_merge_on_skipped_pipeline": null,
|
||||
"request_access_enabled": true,
|
||||
"only_allow_merge_if_all_discussions_are_resolved": false,
|
||||
"remove_source_branch_after_merge": true,
|
||||
"printing_merge_request_link_enabled": true,
|
||||
"merge_method": "merge",
|
||||
"suggestion_commit_message": null,
|
||||
"auto_devops_enabled": true,
|
||||
"auto_devops_deploy_strategy": "continuous",
|
||||
"autoclose_referenced_issues": true,
|
||||
"approvals_before_merge": 0,
|
||||
"mirror": false,
|
||||
"compliance_frameworks": []
|
||||
}
|
||||
```
|
||||
|
||||
## Branches
|
||||
|
||||
Read more in the [Branches](branches.md) documentation.
|
||||
|
|
|
@ -198,22 +198,40 @@ POST /snippets
|
|||
|
||||
Parameters:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|:--------------|:-------|:---------|:---------------------------------------------------|
|
||||
| `title` | string | yes | Title of a snippet. |
|
||||
| `file_name` | string | yes | Name of a snippet file. |
|
||||
| `content` | string | yes | Content of a snippet. |
|
||||
| `description` | string | no | Description of a snippet. |
|
||||
| `visibility` | string | no | Snippet's [visibility](#snippet-visibility-level). |
|
||||
| Attribute | Type | Required | Description |
|
||||
|:------------------|:----------------|:---------|:--------------------------------------------------------|
|
||||
| `title` | string | yes | Title of a snippet |
|
||||
| `file_name` | string | no | Deprecated: Use `files` instead. Name of a snippet file |
|
||||
| `content` | string | no | Deprecated: Use `files` instead. Content of a snippet |
|
||||
| `description` | string | no | Description of a snippet |
|
||||
| `visibility` | string | no | Snippet's [visibility](#snippet-visibility-level) |
|
||||
| `files` | array of hashes | no | An array of snippet files |
|
||||
| `files:file_path` | string | yes | File path of the snippet file |
|
||||
| `files:content` | string | yes | Content of the snippet file |
|
||||
|
||||
Example request:
|
||||
|
||||
```shell
|
||||
curl --request POST \
|
||||
--data '{"title": "This is a snippet", "content": "Hello world", "description": "Hello World snippet", "file_name": "test.txt", "visibility": "internal" }' \
|
||||
curl --request POST "https://gitlab.example.com/api/v4/snippets" \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header "PRIVATE-TOKEN: <your_access_token>" \
|
||||
"https://gitlab.example.com/api/v4/snippets"
|
||||
-d @snippet.json
|
||||
```
|
||||
|
||||
`snippet.json` used in the above example request:
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "This is a snippet",
|
||||
"description": "Hello World snippet",
|
||||
"visibility": "internal",
|
||||
"files": [
|
||||
{
|
||||
"content": "Hello world",
|
||||
"file_path": "test.txt"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
@ -222,7 +240,6 @@ Example response:
|
|||
{
|
||||
"id": 1,
|
||||
"title": "This is a snippet",
|
||||
"file_name": "test.txt",
|
||||
"description": "Hello World snippet",
|
||||
"visibility": "internal",
|
||||
"author": {
|
||||
|
@ -238,7 +255,16 @@ Example response:
|
|||
"created_at": "2012-06-28T10:52:04Z",
|
||||
"project_id": null,
|
||||
"web_url": "http://example.com/snippets/1",
|
||||
"raw_url": "http://example.com/snippets/1/raw"
|
||||
"raw_url": "http://example.com/snippets/1/raw",
|
||||
"ssh_url_to_repo": "ssh://git@gitlab.example.com:snippets/1.git",
|
||||
"http_url_to_repo": "https://gitlab.example.com/snippets/1.git",
|
||||
"file_name": "test.txt",
|
||||
"files": [
|
||||
{
|
||||
"path": "text.txt",
|
||||
"raw_url": "https://gitlab.example.com/-/snippets/1/raw/master/renamed.md"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -255,23 +281,44 @@ PUT /snippets/:id
|
|||
|
||||
Parameters:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|:--------------|:--------|:---------|:---------------------------------------------------|
|
||||
| `id` | integer | yes | ID of snippet to update. |
|
||||
| `title` | string | no | Title of a snippet. |
|
||||
| `file_name` | string | no | Name of a snippet file. |
|
||||
| `description` | string | no | Description of a snippet. |
|
||||
| `content` | string | no | Content of a snippet. |
|
||||
| `visibility` | string | no | Snippet's [visibility](#snippet-visibility-level). |
|
||||
| Attribute | Type | Required | Description |
|
||||
|:----------------------|:----------------|:---------|:------------------------------------------------------------------------------------|
|
||||
| `id` | integer | yes | ID of snippet to update |
|
||||
| `title` | string | no | Title of a snippet |
|
||||
| `file_name` | string | no | Deprecated: Use `files` instead. Name of a snippet file |
|
||||
| `content` | string | no | Deprecated: Use `files` instead. Content of a snippet |
|
||||
| `description` | string | no | Description of a snippet |
|
||||
| `visibility` | string | no | Snippet's [visibility](#snippet-visibility-level) |
|
||||
| `files` | array of hashes | no | An array of snippet files |
|
||||
| `files:action` | string | yes | Type of action to perform on the file, one of: 'create', 'update', 'delete', 'move' |
|
||||
| `files:file_path` | string | no | File path of the snippet file |
|
||||
| `files:previous_path` | string | no | Previous path of the snippet file |
|
||||
| `files:content` | string | no | Content of the snippet file |
|
||||
|
||||
Updates to snippets with multiple files *must* use the `files` attribute.
|
||||
|
||||
Example request:
|
||||
|
||||
```shell
|
||||
curl --request PUT \
|
||||
--data '{"title": "foo", "content": "bar"}' \
|
||||
curl --request PUT "https://gitlab.example.com/api/v4/snippets/1" \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header "PRIVATE-TOKEN: <your_access_token>" \
|
||||
"https://gitlab.example.com/api/v4/snippets/1"
|
||||
-d @snippet.json
|
||||
```
|
||||
|
||||
`snippet.json` used in the above example request:
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "foo",
|
||||
"files": [
|
||||
{
|
||||
"action": "move",
|
||||
"previous_path": "test.txt",
|
||||
"file_path": "renamed.md"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
@ -280,7 +327,6 @@ Example response:
|
|||
{
|
||||
"id": 1,
|
||||
"title": "test",
|
||||
"file_name": "add.rb",
|
||||
"description": "description of snippet",
|
||||
"visibility": "internal",
|
||||
"author": {
|
||||
|
@ -296,7 +342,16 @@ Example response:
|
|||
"created_at": "2012-06-28T10:52:04Z",
|
||||
"project_id": null,
|
||||
"web_url": "http://example.com/snippets/1",
|
||||
"raw_url": "http://example.com/snippets/1/raw"
|
||||
"raw_url": "http://example.com/snippets/1/raw",
|
||||
"ssh_url_to_repo": "ssh://git@gitlab.example.com:snippets/1.git",
|
||||
"http_url_to_repo": "https://gitlab.example.com/snippets/1.git",
|
||||
"file_name": "renamed.md",
|
||||
"files": [
|
||||
{
|
||||
"path": "renamed.md",
|
||||
"raw_url": "https://gitlab.example.com/-/snippets/1/raw/master/renamed.md"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -176,7 +176,7 @@ class ChangeUsersUsernameStringToText < ActiveRecord::Migration[4.2]
|
|||
end
|
||||
|
||||
def down
|
||||
cleanup_concurrent_column_type_change :users, :username
|
||||
undo_change_column_type_concurrently :users, :username
|
||||
end
|
||||
end
|
||||
```
|
||||
|
@ -197,7 +197,7 @@ class ChangeUsersUsernameStringToTextCleanup < ActiveRecord::Migration[4.2]
|
|||
end
|
||||
|
||||
def down
|
||||
change_column_type_concurrently :users, :username, :string
|
||||
undo_cleanup_concurrent_column_type_change :users, :username, :string
|
||||
end
|
||||
end
|
||||
```
|
||||
|
|
|
@ -58,7 +58,7 @@ To create a new blank project on the **New project** page:
|
|||
Project templates can pre-populate a new project with the necessary files to get you
|
||||
started quickly.
|
||||
|
||||
There are two types of project templates:
|
||||
There are two main types of project templates:
|
||||
|
||||
- [Built-in templates](#built-in-templates), sourced from the following groups:
|
||||
- [`project-templates`](https://gitlab.com/gitlab-org/project-templates)
|
||||
|
|
|
@ -204,6 +204,19 @@ Select the **To-Do List** **{todo-done}** in the navigation bar to view your cur
|
|||
|
||||
![Alert Details Added to do](./img/alert_detail_added_todo_v13_1.png)
|
||||
|
||||
## Link runbooks to alerts
|
||||
|
||||
> Runbook URLs [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39315) in GitLab 13.3.
|
||||
|
||||
When creating alerts from the metrics dashboard for
|
||||
[managed Prometheus instances](../metrics/alerts.md#managed-prometheus-instances),
|
||||
you can link a runbook. When the alert triggers, you can access the runbook through
|
||||
the [chart context menu](../metrics/dashboards/index.md#chart-context-menu) in the
|
||||
upper-right corner of the metrics chart, making it easy for you to locate and access
|
||||
the correct runbook:
|
||||
|
||||
![Linked Runbook in charts](img/link_runbooks_to_alerts_v13_5.png)
|
||||
|
||||
## View the environment that generated the alert
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/232492) in GitLab 13.5.
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 76 KiB |
|
@ -6,74 +6,53 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
# Project integration management
|
||||
|
||||
Project integrations can be configured and enabled by project administrators. As a GitLab instance administrator, you can set default configuration parameters for a given integration that all projects can inherit and use, enabling the integration for all projects that are not already using custom settings.
|
||||
Project integrations can be configured and enabled by project administrators. As a GitLab instance
|
||||
administrator, you can set default configuration parameters for a given integration that all projects
|
||||
can inherit and use. This enables the integration for all projects that are not already using custom
|
||||
settings.
|
||||
|
||||
You can update these default settings at any time, changing the settings used for all
|
||||
projects that are set to use instance-level or group-level defaults. Updating the
|
||||
default settings also enables the integration for all projects that didn't have it
|
||||
already enabled.
|
||||
You can update these default settings at any time, changing the settings used for all projects that
|
||||
are set to use instance-level defaults. Updating the default settings also enables the integration
|
||||
for all projects that didn't have it already enabled.
|
||||
|
||||
Only the complete settings for an integration can be inherited. Per-field inheritance is [planned](https://gitlab.com/groups/gitlab-org/-/epics/2137).
|
||||
Only the complete settings for an integration can be inherited. Per-field inheritance is
|
||||
[planned](https://gitlab.com/groups/gitlab-org/-/epics/2137) as is
|
||||
[group-level management](https://gitlab.com/groups/gitlab-org/-/epics/2543) of integration settings.
|
||||
|
||||
## Manage instance-level default settings for a project integration **(CORE ONLY)**
|
||||
|
||||
> [Introduced in](https://gitlab.com/groups/gitlab-org/-/epics/2137) GitLab 13.3.
|
||||
|
||||
1. Navigate to **Admin Area > Settings > Integrations**.
|
||||
1. Select an integration.
|
||||
1. Select a project integration.
|
||||
1. Enter configuration details and click **Save changes**.
|
||||
|
||||
CAUTION: **Caution:**
|
||||
This may affect all or most of the groups and projects on your GitLab instance. Please review the details below.
|
||||
This may affect all or most of the projects on your GitLab instance. Please review the details
|
||||
below.
|
||||
|
||||
If this is the first time you are setting up instance-level settings for an integration:
|
||||
|
||||
- The integration is enabled for all groups and projects that do not already have this integration configured if you have the **Enable integration** toggle turned on in the instance-level settings.
|
||||
- Groups and projects that already have the integration configured are not affected, but can choose to use the inherited settings at any time.
|
||||
- The integration is enabled for all projects that don't already have this integration configured,
|
||||
if you have the **Enable integration** toggle turned on in the instance-level settings.
|
||||
- Projects that already have the integration configured are not affected, but can choose to use the
|
||||
inherited settings at any time.
|
||||
|
||||
When you make further changes to the instance defaults:
|
||||
|
||||
- They are immediately applied to all groups and projects that have the integration set to use default settings.
|
||||
- They are immediately applied to newer groups and projects, created since you last
|
||||
saved defaults for the integration. If your instance-level default setting has the
|
||||
**Enable integration** toggle turned on, the integration is automatically enabled for
|
||||
all such groups and projects.
|
||||
- Groups and projects with custom settings selected for the integration are not immediately affected and may choose to use the latest defaults at any time.
|
||||
- They are immediately applied to all projects that have the integration set to use default settings.
|
||||
- They are immediately applied to newer projects, created since you last saved defaults for the
|
||||
integration. If your instance-level default setting has the **Enable integration** toggle turned
|
||||
on, the integration is automatically enabled for all such projects.
|
||||
- Projects with custom settings selected for the integration are not immediately affected and may
|
||||
choose to use the latest defaults at any time.
|
||||
|
||||
Only the complete settings for an integration can be inherited. Per-field inheritance
|
||||
is [planned](https://gitlab.com/groups/gitlab-org/-/epics/2137). This would allow
|
||||
administrators to update settings inherited by groups and projects without enabling the
|
||||
integration on all non-configured groups and projects by default.
|
||||
administrators to update settings inherited by projects without enabling the
|
||||
integration on all non-configured projects by default.
|
||||
|
||||
## Manage group-level default settings for a project integration
|
||||
|
||||
> [Introduced in](https://gitlab.com/groups/gitlab-org/-/epics/2543) GitLab 13.5.
|
||||
|
||||
1. Navigate to the group's **Settings > Integrations**.
|
||||
1. Select an integration.
|
||||
1. Enter configuration details and click **Save changes**.
|
||||
|
||||
CAUTION: **Caution:**
|
||||
This may affect all or most of the subgroups and projects belonging to the group. Please review the details below.
|
||||
|
||||
If this is the first time you are setting up group-level settings for an integration:
|
||||
|
||||
- The integration is enabled for all subgroups and projects belonging to the group that do not already have this integration configured if you have the **Enable integration** toggle turned on in the group-level settings.
|
||||
- Subgroups and projects that already have the integration configured are not affected, but can choose to use the inherited settings at any time.
|
||||
|
||||
When you make further changes to the group defaults:
|
||||
|
||||
- They are immediately applied to all subgroups and projects belonging to the group that have the integration set to use default settings.
|
||||
- They are immediately applied to newer subgroups and projects, created since you last saved defaults for the integration.
|
||||
- If your group-level default setting has the **Enable integration** toggle turned on, the integration is automatically enabled for all such subgroups and projects.
|
||||
- Subgroups and projects with custom settings selected for the integration are not immediately affected and may choose to use the latest defaults at any time.
|
||||
|
||||
Only the complete settings for an integration can be inherited. Per-field inheritance
|
||||
is [planned](https://gitlab.com/groups/gitlab-org/-/epics/2137). This would allow
|
||||
administrators to update settings inherited by subgroups and projects without enabling the
|
||||
integration on all non-configured groups and projects by default.
|
||||
|
||||
## Use instance-level or group-level default settings for a project integration
|
||||
## Use instance-level default settings for a project integration
|
||||
|
||||
1. Navigate to **Project > Settings > Integrations**.
|
||||
1. Choose the integration you want to enable or update.
|
||||
|
@ -81,9 +60,9 @@ integration on all non-configured groups and projects by default.
|
|||
1. Ensure the toggle is set to **Enable integration**.
|
||||
1. Click **Save changes**.
|
||||
|
||||
## Use custom settings for a group or project integration
|
||||
## Use custom settings for a project integration
|
||||
|
||||
1. Navigate to project or group's **Settings > Integrations**.
|
||||
1. Navigate to project's **Settings > Integrations**.
|
||||
1. Choose the integration you want to enable or update.
|
||||
1. From the drop-down, select **Use custom settings**.
|
||||
1. Ensure the toggle is set to **Enable integration** and enter all required settings.
|
||||
|
|
|
@ -742,7 +742,6 @@ To enable prevent project forking:
|
|||
- **Audit Events**: View [Audit Events](../../administration/audit_events.md)
|
||||
for the group. **(STARTER ONLY)**
|
||||
- **Pipelines quota**: Keep track of the [pipeline quota](../admin_area/settings/continuous_integration.md) for the group.
|
||||
- **Integrations**: Configure [integrations](../admin_area/settings/project_integration_management.md) for your group.
|
||||
|
||||
#### Storage usage quota **(STARTER)**
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ We flag any significant differences between Redcarpet and CommonMark Markdown in
|
|||
|
||||
If you have a large volume of Markdown files, it can be tedious to determine
|
||||
if they display correctly or not. You can use the
|
||||
[diff_redcarpet_cmark](https://gitlab.com/digitalmoksha/diff_redcarpet_cmark)
|
||||
[`diff_redcarpet_cmark`](https://gitlab.com/digitalmoksha/diff_redcarpet_cmark)
|
||||
tool (not an officially supported product) to generate a list of files and the
|
||||
differences between how RedCarpet and CommonMark render the files. It gives
|
||||
an indication if anything needs to be changed - often nothing needs
|
||||
|
|
|
@ -67,7 +67,7 @@ Images follow this naming convention:
|
|||
```
|
||||
|
||||
If your project is `gitlab.example.com/mynamespace/myproject`, for example,
|
||||
then your image must be named `gitlab.example.com/mynamespace/myproject/my-app` at a mimimum.
|
||||
then your image must be named `gitlab.example.com/mynamespace/myproject/my-app` at a minimum.
|
||||
|
||||
You can append additional names to the end of an image name, up to three levels deep.
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
# Monorepo package management workflows
|
||||
|
||||
Oftentimes, one project or Git repository may contain multiple different
|
||||
subprojects or submodules that all get packaged and published individually.
|
||||
sub-projects or submodules that all get packaged and published individually.
|
||||
|
||||
## Publishing different packages to the parent project
|
||||
|
||||
|
@ -36,9 +36,9 @@ as well as `Foo`.
|
|||
Following the instructions in the
|
||||
[GitLab NPM registry documentation](../npm_registry/index.md),
|
||||
publishing `MyProject` consists of modifying the `package.json` file with a
|
||||
`publishConfig` section, as well as either modifying your local NPM config with
|
||||
`publishConfig` section, as well as either modifying your local NPM configuration with
|
||||
CLI commands like `npm config set`, or saving a `.npmrc` file in the root of the
|
||||
project specifying these config settings.
|
||||
project specifying these configuration settings.
|
||||
|
||||
If you follow the instructions you can publish `MyProject` by running
|
||||
`npm publish` from the root directory.
|
||||
|
@ -65,7 +65,7 @@ A package is associated with a project on GitLab, but the package does not
|
|||
need to be associated with the code in that project. Notice when configuring
|
||||
NPM or Maven, you only use the `Project ID` to set the registry URL that the
|
||||
package is to be uploaded to. If you set this to any project that you have
|
||||
access to and update any other config similarly depending on the package type,
|
||||
access to and update any other configuration similarly depending on the package type,
|
||||
your packages are published to that project. This means you can publish
|
||||
multiple packages to one project, even if their code does not exist in the same
|
||||
place. See the [project registry workflow documentation](./project_registry.md)
|
||||
|
|
|
@ -399,7 +399,7 @@ Administrators can add members with a "minimal access" role to a parent group. S
|
|||
automatically have access to projects and subgroups underneath. To support such access, administrators must explicitly add these "minimal access" users to the specific subgroups/projects.
|
||||
|
||||
Users with minimal access can list the group in the UI and through the API. However, they cannot see
|
||||
details such as projects or subgroups. They do not have access to the group's page or list any of itssubgroups or projects.
|
||||
details such as projects or subgroups. They do not have access to the group's page or list any of its subgroups or projects.
|
||||
|
||||
## Project features
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ The default syntax theme is White, and you can choose among 5 different themes:
|
|||
[Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2389) in 13.0, the theme
|
||||
you choose also applies to the [Web IDE](../project/web_ide/index.md)'s code editor and [Snippets](../snippets.md).
|
||||
The themes are available only in the Web IDE file editor, except for the [dark theme](https://gitlab.com/gitlab-org/gitlab/-/issues/209808) and
|
||||
the [solarized dark theme](https://gitlab.com/gitlab-org/gitlab/-/issues/219228),
|
||||
the [Solarized dark theme](https://gitlab.com/gitlab-org/gitlab/-/issues/219228),
|
||||
which apply to the entire Web IDE screen.
|
||||
|
||||
## Behavior
|
||||
|
@ -131,15 +131,9 @@ You can choose between 2 options:
|
|||
|
||||
### Project overview content
|
||||
|
||||
The project overview content setting allows you to choose what content you want to
|
||||
The **Project overview content** setting allows you to choose what content you want to
|
||||
see on a project’s home page.
|
||||
|
||||
You can choose between 3 options:
|
||||
|
||||
- Files and Readme (default)
|
||||
- Readme
|
||||
- Activity
|
||||
|
||||
### Tab width
|
||||
|
||||
You can set the displayed width of tab characters across various parts of
|
||||
|
|
|
@ -252,7 +252,7 @@ GitLab CI/CD build environment.
|
|||
| `KUBE_NAMESPACE` | The namespace associated with the project's deployment service account. In the format `<project_name>-<project_id>-<environment>`. For GitLab-managed clusters, a matching namespace is automatically created by GitLab in the cluster. If your cluster was created before GitLab 12.2, the default `KUBE_NAMESPACE` is set to `<project_name>-<project_id>`. |
|
||||
| `KUBE_CA_PEM_FILE` | Path to a file containing PEM data. Only present if a custom CA bundle was specified. |
|
||||
| `KUBE_CA_PEM` | (**deprecated**) Raw PEM data. Only if a custom CA bundle was specified. |
|
||||
| `KUBECONFIG` | Path to a file containing `kubeconfig` for this deployment. CA bundle would be embedded if specified. This config also embeds the same token defined in `KUBE_TOKEN` so you likely will only need this variable. This variable name is also automatically picked up by `kubectl` so you won't actually need to reference it explicitly if using `kubectl`. |
|
||||
| `KUBECONFIG` | Path to a file containing `kubeconfig` for this deployment. CA bundle would be embedded if specified. This configuration also embeds the same token defined in `KUBE_TOKEN` so you likely will only need this variable. This variable name is also automatically picked up by `kubectl` so you won't actually need to reference it explicitly if using `kubectl`. |
|
||||
| `KUBE_INGRESS_BASE_DOMAIN` | From GitLab 11.8, this variable can be used to set a domain per cluster. See [cluster domains](#base-domain) for more information. |
|
||||
|
||||
### Custom namespace
|
||||
|
|
|
@ -789,7 +789,7 @@ or with other versions of Python.
|
|||
kubectl edit gateway knative-ingress-gateway --namespace knative-serving
|
||||
```
|
||||
|
||||
Update the gateway to include the following tls: section and configuration:
|
||||
Update the gateway to include the following `tls:` section and configuration:
|
||||
|
||||
```shell
|
||||
tls:
|
||||
|
|
|
@ -58,9 +58,9 @@ relevant language.
|
|||
|
||||
| Language | Implementation |
|
||||
|---|---|
|
||||
| Go | [sourcegraph/lsif-go](https://github.com/sourcegraph/lsif-go) |
|
||||
| JavaScript | [sourcegraph/lsif-node](https://github.com/sourcegraph/lsif-node) |
|
||||
| TypeScript | [sourcegraph/lsif-node](https://github.com/sourcegraph/lsif-node) |
|
||||
| Go | [`sourcegraph/lsif-go`](https://github.com/sourcegraph/lsif-go) |
|
||||
| JavaScript | [`sourcegraph/lsif-node`](https://github.com/sourcegraph/lsif-node) |
|
||||
| TypeScript | [`sourcegraph/lsif-node`](https://github.com/sourcegraph/lsif-node) |
|
||||
|
||||
View a complete list of [available LSIF indexers](https://lsif.dev/#implementations-server) on their website and
|
||||
refer to their documentation to see how to generate an LSIF file for your specific language.
|
||||
|
|
|
@ -75,7 +75,6 @@ be used for merge request approvals:
|
|||
- As [merge request eligible approvers](merge_requests/merge_request_approvals.md#code-owners-as-eligible-approvers).
|
||||
- As required approvers for [protected branches](protected_branches.md#protected-branches-approval-by-code-owners). **(PREMIUM)**
|
||||
|
||||
NOTE: **Note:**
|
||||
Developer or higher [permissions](../permissions.md) are required in order to
|
||||
approve a merge request.
|
||||
|
||||
|
@ -93,12 +92,14 @@ to specify the actual owners and granular permissions.
|
|||
|
||||
Using Code Owners in conjunction with [Protected Branches](protected_branches.md#protected-branches-approval-by-code-owners)
|
||||
will prevent any user who is not specified in the `CODEOWNERS` file from pushing
|
||||
changes for the specified files/paths, even if their role is included in the
|
||||
changes for the specified files/paths, except those included in the
|
||||
**Allowed to push** column. This allows for a more inclusive push strategy, as
|
||||
administrators don't have to restrict developers from pushing directly to the
|
||||
protected branch, but can restrict pushing to certain files where a review by
|
||||
Code Owners is required.
|
||||
|
||||
[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/35097) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.5, users and groups who are allowed to push to protected branches do not require a merge request to merge their feature branches. Thus, they can skip merge request approval rules, Code Owners included.
|
||||
|
||||
## The syntax of Code Owners files
|
||||
|
||||
Files can be specified using the same kind of patterns you would use
|
||||
|
|
|
@ -85,7 +85,7 @@ To display the Deploy Boards for a specific [environment](../../ci/environments/
|
|||
[`kubernetes`](https://docs.gitlab.com/runner/executors/kubernetes.html) executor.
|
||||
1. Configure the [Kubernetes integration](clusters/index.md) in your project for the
|
||||
cluster. The Kubernetes namespace is of particular note as you will need it
|
||||
for your deployment scripts (exposed by the `KUBE_NAMESPACE` env variable).
|
||||
for your deployment scripts (exposed by the `KUBE_NAMESPACE` environment variable).
|
||||
1. Ensure Kubernetes annotations of `app.gitlab.com/env: $CI_ENVIRONMENT_SLUG`
|
||||
and `app.gitlab.com/app: $CI_PROJECT_PATH_SLUG` are applied to the
|
||||
deployments, replica sets, and pods, where `$CI_ENVIRONMENT_SLUG` and
|
||||
|
|
|
@ -69,7 +69,7 @@ brew install git-lfs
|
|||
```
|
||||
|
||||
Once installed, **open your local repository in a terminal window** and
|
||||
install Git LFS in your repo. If you're sure that LFS is already installed,
|
||||
install Git LFS in your repository. If you're sure that LFS is already installed,
|
||||
you can skip this step. If you're unsure, re-installing it won't do any harm:
|
||||
|
||||
```shell
|
||||
|
@ -159,7 +159,7 @@ command line interface, file locks can be created for any file.
|
|||
### View exclusively-locked files
|
||||
|
||||
To list all the files locked with LFS locally, open a terminal window in your
|
||||
repo and run:
|
||||
repository and run:
|
||||
|
||||
```shell
|
||||
git lfs locks
|
||||
|
@ -189,7 +189,7 @@ Suggested workflow for shared projects:
|
|||
1. Lock the file.
|
||||
1. Edit the file.
|
||||
1. Commit your changes.
|
||||
1. Push to the repo.
|
||||
1. Push to the repository.
|
||||
1. Get your changes reviewed, approved, and merged.
|
||||
1. Unlock the file.
|
||||
|
||||
|
|
|
@ -96,7 +96,7 @@ back to both GitLab and GitHub when completed.
|
|||
The mirroring is pull-only by default, so you may create or update the file on
|
||||
GitHub:
|
||||
|
||||
![Edit gitlab-ci.yml file](img/gemnasium/edit_gitlab-ci.png)
|
||||
![Edit YAML file](img/gemnasium/edit_gitlab-ci.png)
|
||||
|
||||
1. Once your file has been committed, a new pipeline will be automatically
|
||||
triggered if your file is valid:
|
||||
|
|
|
@ -101,7 +101,7 @@ If you are using a self-managed GitLab instance or if you are importing from Git
|
|||
1. From the top navigation bar, click **+** and select **New project**.
|
||||
1. Select the **Import project** tab and then select **GitHub**.
|
||||
1. Select the first button to **List your GitHub repositories**. You are redirected to a page on [GitHub](https://github.com) to authorize the GitLab application.
|
||||
1. Click **Authorize gitlabhq**. You are redirected back to GitLab's Import page and all of your GitHub repositories are listed.
|
||||
1. Click **Authorize GitlabHQ**. You are redirected back to GitLab's Import page and all of your GitHub repositories are listed.
|
||||
1. Continue on to [selecting which repositories to import](#selecting-which-repositories-to-import).
|
||||
|
||||
### Using a GitHub token
|
||||
|
@ -119,7 +119,7 @@ If you are not using the GitHub integration, you can still perform an authorizat
|
|||
|
||||
1. Go to <https://github.com/settings/tokens/new>
|
||||
1. Enter a token description.
|
||||
1. Select the repo scope.
|
||||
1. Select the repository scope.
|
||||
1. Click **Generate token**.
|
||||
1. Copy the token hash.
|
||||
1. Go back to GitLab and provide the token to the GitHub importer.
|
||||
|
@ -136,10 +136,10 @@ your GitHub repositories are listed.
|
|||
1. Select the **Import** button next to any number of repositories, or select **Import all repositories**. Additionally,
|
||||
you can filter projects by name. If filter is applied, **Import all repositories** only imports matched repositories.
|
||||
1. The **Status** column shows the import status of each repository. You can choose to leave the page open and it will
|
||||
update in realtime or you can return to it later.
|
||||
update in real-time or you can return to it later.
|
||||
1. Once a repository has been imported, click its GitLab path to open its GitLab URL.
|
||||
|
||||
![Github importer page](img/import_projects_from_github_importer_v12_3.png)
|
||||
![GitHub importer page](img/import_projects_from_github_importer_v12_3.png)
|
||||
|
||||
## Mirroring and pipeline status sharing
|
||||
|
||||
|
@ -149,7 +149,7 @@ your imported repository in sync with its GitHub copy.
|
|||
Additionally, you can configure GitLab to send pipeline status updates back GitHub with the
|
||||
[GitHub Project Integration](../integrations/github.md). **(PREMIUM)**
|
||||
|
||||
If you import your project using [CI/CD for external repo](../../../ci/ci_cd_for_external_repos/index.md), then both
|
||||
If you import your project using [CI/CD for external repository](../../../ci/ci_cd_for_external_repos/index.md), then both
|
||||
of the above are automatically configured. **(PREMIUM)**
|
||||
|
||||
## Improving the speed of imports on self-managed instances
|
||||
|
|
|
@ -18,7 +18,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
1. [From Perforce](perforce.md)
|
||||
1. [From SVN](svn.md)
|
||||
1. [From TFVC](tfvc.md)
|
||||
1. [From repo by URL](repo_by_url.md)
|
||||
1. [From repository by URL](repo_by_url.md)
|
||||
1. [By uploading a manifest file (AOSP)](manifest.md)
|
||||
1. [From Gemnasium](gemnasium.md)
|
||||
1. [From Phabricator](phabricator.md)
|
||||
|
@ -32,7 +32,7 @@ There is also the option of [connecting your external repository to get CI/CD be
|
|||
|
||||
## Migrating from self-managed GitLab to GitLab.com
|
||||
|
||||
If you only need to migrate Git repos, you can [import each project by URL](repo_by_url.md). Issues and merge requests can't be imported.
|
||||
If you only need to migrate Git repositories, you can [import each project by URL](repo_by_url.md). Issues and merge requests can't be imported.
|
||||
|
||||
If you want to retain all metadata like issues and merge requests, you can use
|
||||
the [import/export feature](../settings/import_export.md) to export projects from self-managed GitLab and import those projects into GitLab.com.
|
||||
|
|
|
@ -56,7 +56,7 @@ You can start the import with:
|
|||
1. From your GitLab dashboard click **New project**
|
||||
1. Switch to the **Import project** tab
|
||||
1. Click on the **Manifest file** button
|
||||
1. Provide GitLab with a manifest xml file
|
||||
1. Provide GitLab with a manifest XML file
|
||||
1. Select a group you want to import to (you need to create a group first if you don't have one)
|
||||
1. Click **List available repositories**. At this point, you will be redirected
|
||||
to the import status page with projects list based on the manifest file.
|
||||
|
|
|
@ -20,7 +20,7 @@ Git:
|
|||
it creates an integration record in their proprietary database for every file
|
||||
in the branch, regardless how many were actually changed. Whereas Git was
|
||||
implemented with a different architecture so that a single SHA acts as a pointer
|
||||
to the state of the whole repo after the changes, making it very easy to branch.
|
||||
to the state of the whole repository after the changes, making it very easy to branch.
|
||||
This is what made feature branching workflows so easy to adopt with Git.
|
||||
1. Also, context switching between branches is much easier in Git. If your manager
|
||||
said 'You need to stop work on that new feature and fix this security
|
||||
|
|
|
@ -5,7 +5,7 @@ group: Import
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
|
||||
---
|
||||
|
||||
# Import project from repo by URL
|
||||
# Import project from repository by URL
|
||||
|
||||
You can import your existing repositories by providing the Git URL:
|
||||
|
||||
|
@ -16,4 +16,4 @@ You can import your existing repositories by providing the Git URL:
|
|||
1. Click **Create project** to begin the import process
|
||||
1. Once complete, you will be redirected to your newly created project
|
||||
|
||||
![Import project by repo URL](img/import_projects_from_repo_url.png)
|
||||
![Import project by repository URL](img/import_projects_from_repo_url.png)
|
||||
|
|
|
@ -14,8 +14,8 @@ See the project homepage for further information: <https://gitlab.com/esr/irker>
|
|||
|
||||
## Needed setup
|
||||
|
||||
You will first need an Irker daemon. You can download the Irker code from its
|
||||
repository on <https://gitlab.com/esr/irker>:
|
||||
You will first need an Irker daemon. You can download the Irker code
|
||||
[from its repository](https://gitlab.com/esr/irker):
|
||||
|
||||
```shell
|
||||
git clone https://gitlab.com/esr/irker.git
|
||||
|
@ -55,6 +55,6 @@ case, `Aorimn` is treated as a nick and no more as a channel name.
|
|||
Irker can also join password-protected channels. Users need to append
|
||||
`?key=thesecretpassword` to the channel name. When using this feature remember to
|
||||
**not** put the `#` sign in front of the channel name; failing to do so will
|
||||
result on irker joining a channel literally named `#chan?key=password` henceforth
|
||||
result on Irker joining a channel literally named `#chan?key=password` henceforth
|
||||
leaking the channel key through the `/whois` IRC command (depending on IRC server
|
||||
configuration). This is due to a long standing irker bug.
|
||||
configuration). This is due to a long standing Irker bug.
|
||||
|
|
|
@ -368,7 +368,7 @@ If you're not able to do some of the things above, make sure you have the right
|
|||
|
||||
### First time using an issue board
|
||||
|
||||
> The automatic creation of the **To Do** and **Doing** lists was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/202144) in GitLab 13.4.
|
||||
> The automatic creation of the **To Do** and **Doing** lists was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/202144) in GitLab 13.5.
|
||||
|
||||
The first time you open an issue board, you are presented with the default lists
|
||||
(**Open**, **To Do**, **Doing**, and **Closed**).
|
||||
|
|
|
@ -55,7 +55,7 @@ include:
|
|||
```
|
||||
|
||||
creates an `a11y` job in your CI/CD pipeline, runs
|
||||
Pa11y against the webpages defined in `a11y_urls`, and builds an HTML report for each.
|
||||
Pa11y against the web pages defined in `a11y_urls`, and builds an HTML report for each.
|
||||
|
||||
The report for each URL is saved as an artifact that can be [viewed directly in your browser](../../../ci/pipelines/job_artifacts.md#browsing-artifacts).
|
||||
|
||||
|
|
|
@ -164,8 +164,8 @@ For example:
|
|||
1. Capture the dynamic URL and save it into a `.env` file, e.g. `echo "ENVIRONMENT_URL=$CI_ENVIRONMENT_URL" >> review.env`.
|
||||
1. Set the `.env` file to be a [job artifact](../../../ci/pipelines/job_artifacts.md#job-artifacts).
|
||||
1. In the `load_performance` job:
|
||||
1. Set it to depend on the review job, so it inherits the env file.
|
||||
1. Set the `K6_DOCKER_OPTIONS` variable with the [Docker cli option for env files](https://docs.docker.com/engine/reference/commandline/run/#set-environment-variables--e---env---env-file), for example `--env-file review.env`.
|
||||
1. Set it to depend on the review job, so it inherits the environment file.
|
||||
1. Set the `K6_DOCKER_OPTIONS` variable with the [Docker CLI option for environment files](https://docs.docker.com/engine/reference/commandline/run/#set-environment-variables--e---env---env-file), for example `--env-file review.env`.
|
||||
1. Configure the k6 test script to use the environment variable in it's steps.
|
||||
|
||||
Your `.gitlab-ci.yml` file might be similar to:
|
||||
|
|
|
@ -84,7 +84,7 @@ Click **Expand file** on any file to view the changes for that file.
|
|||
> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-file-by-file-diff-navigation).
|
||||
|
||||
For larger merge requests it might sometimes be useful to review single files at a time. To enable,
|
||||
from your avatar on the top-right navbar, click **Settings**, and go to **Preferences** on the left
|
||||
from your avatar on the top-right navigation bar, click **Settings**, and go to **Preferences** on the left
|
||||
sidebar. Scroll down to the **Behavior** section and select **Show one file at a time on merge request's Changes tab**.
|
||||
Click **Save changes** to apply.
|
||||
|
||||
|
|
|
@ -65,8 +65,8 @@ meaningful commit messages and:
|
|||
## Enabling squash for a merge request
|
||||
|
||||
Anyone who can create or edit a merge request can choose for it to be squashed
|
||||
on the merge request form. Users can select or unselect the checkbox at the moment
|
||||
they are creating the merge request:
|
||||
on the merge request form. Users can select or clear the check box when they
|
||||
create the merge request:
|
||||
|
||||
![Squash commits checkbox on edit form](img/squash_edit_form.png)
|
||||
|
||||
|
|
|
@ -30,6 +30,8 @@ to do it for you.
|
|||
To help you out, we've gathered some instructions on how to do that
|
||||
for the most popular hosting services:
|
||||
|
||||
<!-- vale gitlab.Spelling = NO -->
|
||||
|
||||
- [Amazon](https://docs.aws.amazon.com/AmazonS3/latest/dev/website-hosting-custom-domain-walkthrough.html)
|
||||
- [Bluehost](https://www.bluehost.com/help/article/dns-management-add-edit-or-delete-dns-entries)
|
||||
- [Cloudflare](https://support.cloudflare.com/hc/en-us/articles/201720164-Creating-a-Cloudflare-account-and-adding-a-website)
|
||||
|
@ -41,6 +43,8 @@ for the most popular hosting services:
|
|||
- [Media Temple](https://mediatemple.net/community/products/dv/204403794/how-can-i-change-the-dns-records-for-my-domain)
|
||||
- [Microsoft](https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-2000-server/bb727018(v=technet.10))
|
||||
|
||||
<!-- vale gitlab.Spelling = YES -->
|
||||
|
||||
If your hosting service is not listed above, you can just try to
|
||||
search the web for `how to add dns record on <my hosting service>`.
|
||||
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
redirect_to: 'pages_ci_cd_template.md'
|
||||
---
|
||||
|
||||
This document was moved to [pages_ci_cd_template.md](pages_ci_cd_template.md).
|
||||
This document was moved to [another location](pages_ci_cd_template.md).
|
||||
|
|
|
@ -53,4 +53,4 @@ You can take some **optional** further steps:
|
|||
![Change repo's path](../img/change_path_v12_10.png)
|
||||
|
||||
- Now go to your SSG's configuration file and change the [base URL](../getting_started_part_one.md#urls-and-baseurls)
|
||||
from `"project-name"` to `""`. The project name setting varies by SSG and may not be in the config file.
|
||||
from `"project-name"` to `""`. The project name setting varies by SSG and may not be in the configuration file.
|
||||
|
|
|
@ -185,6 +185,8 @@ When enabled, all merge requests targeting these branches will require approval
|
|||
by a Code Owner per matched rule before they can be merged.
|
||||
Additionally, direct pushes to the protected branch are denied if a rule is matched.
|
||||
|
||||
[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/35097) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.5, users and groups who are allowed to push to protected branches do not require a merge request to merge their feature branches. Thus, they can skip merge request approval rules.
|
||||
|
||||
## Running pipelines on protected branches
|
||||
|
||||
The permission to merge or push to protected branches is used to define if a user can
|
||||
|
|
|
@ -86,7 +86,7 @@ according to the markup language.
|
|||
| [reStructuredText](https://docutils.sourceforge.io/rst.html) | `rst` |
|
||||
| [AsciiDoc](../../asciidoc.md) | `adoc`, `ad`, `asciidoc` |
|
||||
| [Textile](https://textile-lang.com/) | `textile` |
|
||||
| [rdoc](http://rdoc.sourceforge.net/doc/index.html) | `rdoc` |
|
||||
| [Rdoc](http://rdoc.sourceforge.net/doc/index.html) | `rdoc` |
|
||||
| [Org mode](https://orgmode.org/) | `org` |
|
||||
| [creole](http://www.wikicreole.org/) | `creole` |
|
||||
| [MediaWiki](https://www.mediawiki.org/wiki/MediaWiki) | `wiki`, `mediawiki` |
|
||||
|
@ -234,7 +234,7 @@ lock your files to prevent any conflicting changes.
|
|||
|
||||
## Repository's API
|
||||
|
||||
You can access your repos via [repository API](../../../api/repositories.md).
|
||||
You can access your repositories via [repository API](../../../api/repositories.md).
|
||||
|
||||
## Clone in Apple Xcode
|
||||
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 88 KiB |
Binary file not shown.
After Width: | Height: | Size: 88 KiB |
Binary file not shown.
Before Width: | Height: | Size: 67 KiB |
Binary file not shown.
After Width: | Height: | Size: 79 KiB |
|
@ -7,7 +7,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
# Requirements Management **(ULTIMATE)**
|
||||
|
||||
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2703) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.10.
|
||||
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2703) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.10.
|
||||
> - The ability to add and edit a requirement's long description [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/224622) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.5.
|
||||
|
||||
With requirements, you can set criteria to check your products against. They can be based on users,
|
||||
stakeholders, system, software, or anything else you find important to capture.
|
||||
|
@ -22,7 +23,7 @@ When a feature is no longer necessary, you can [archive the related requirement]
|
|||
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
|
||||
For an overview, see [GitLab 12.10 Introduces Requirements Management](https://www.youtube.com/watch?v=uSS7oUNSEoU).
|
||||
|
||||
![requirements list view](img/requirements_list_v13_1.png)
|
||||
![requirements list view](img/requirements_list_v13_5.png)
|
||||
|
||||
## Create a requirement
|
||||
|
||||
|
@ -32,31 +33,43 @@ can create a new requirement.
|
|||
To create a requirement:
|
||||
|
||||
1. From your project page, go to **{requirements}** **Requirements**.
|
||||
1. Click **New requirement**.
|
||||
1. Enter a descriptive title and click **Create requirement**.
|
||||
1. Select **New requirement**.
|
||||
1. Enter a title and description and select **Create requirement**.
|
||||
|
||||
You will see the newly created requirement on the top of the list, as the requirements
|
||||
list is sorted by creation date in descending order.
|
||||
![requirement create view](img/requirement_create_v13_5.png)
|
||||
|
||||
You can see the newly created requirement on the top of the list, with the requirements
|
||||
list being sorted by creation date, in descending order.
|
||||
|
||||
## View a requirement
|
||||
|
||||
You can view a requirement from the list by selecting it.
|
||||
|
||||
![requirement view](img/requirement_view_v13_5.png)
|
||||
|
||||
To edit a requirement while viewing it, select the **Edit** icon (**{pencil}**)
|
||||
next to the requirement title.
|
||||
|
||||
## Edit a requirement
|
||||
|
||||
> - [Added](https://gitlab.com/gitlab-org/gitlab/-/issues/218607) ability to mark a requirement as Satisfied in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.5.
|
||||
> The ability to mark a requirement as Satisfied [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/218607) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.5.
|
||||
|
||||
You can edit a requirement (if you have the necessary privileges) from the requirements
|
||||
list page.
|
||||
|
||||
To edit a requirement:
|
||||
|
||||
1. From the requirements list, click **Edit** (**{pencil}**).
|
||||
1. Update the title in text input field. You can also mark a requirement as satisfied in the edit form by using the checkbox labeled "Satisfied".
|
||||
1. Click **Save changes**.
|
||||
1. From the requirements list, select the **Edit** icon (**{pencil}**).
|
||||
1. Update the title and description in text input field. You can also mark a
|
||||
requirement as satisfied in the edit form by using the check box **Satisfied**.
|
||||
1. Select **Save changes**.
|
||||
|
||||
## Archive a requirement
|
||||
|
||||
You can archive an open requirement (if you have the necessary privileges) while
|
||||
you're in the **Open** tab.
|
||||
|
||||
To archive a requirement, click **Archive** (**{archive}**).
|
||||
To archive a requirement, select **Archive** (**{archive}**).
|
||||
|
||||
As soon as a requirement is archived, it no longer appears in the **Open** tab.
|
||||
|
||||
|
@ -66,7 +79,7 @@ You can view the list of archived requirements in the **Archived** tab.
|
|||
|
||||
![archived requirements list](img/requirements_archived_list_view_v13_1.png)
|
||||
|
||||
To reopen an archived requirement, click **Reopen**.
|
||||
To reopen an archived requirement, select **Reopen**.
|
||||
|
||||
As soon as a requirement is reopened, it no longer appears in the **Archived** tab.
|
||||
|
||||
|
@ -82,7 +95,7 @@ You can search for a requirement from the requirements list page based on the fo
|
|||
To search for a requirement:
|
||||
|
||||
1. In a project, go to **{requirements}** **Requirements > List**.
|
||||
1. Click the **Search or filter results** field. A dropdown menu appears.
|
||||
1. Select the **Search or filter results** field. A dropdown menu appears.
|
||||
1. Select the requirement author from the dropdown or enter plain text to search by requirement title.
|
||||
1. Press <kbd>Enter</kbd> on your keyboard to filter the list.
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
module Banzai
|
||||
module ReferenceParser
|
||||
InvalidReferenceType = Class.new(StandardError)
|
||||
|
||||
# Returns the reference parser class for the given type
|
||||
#
|
||||
# Example:
|
||||
|
@ -11,6 +13,8 @@ module Banzai
|
|||
# This would return the `Banzai::ReferenceParser::IssueParser` class.
|
||||
def self.[](name)
|
||||
const_get("#{name.to_s.camelize}Parser", false)
|
||||
rescue NameError
|
||||
raise InvalidReferenceType
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -111,6 +111,7 @@ module Banzai
|
|||
parser = Banzai::ReferenceParser[type].new(context)
|
||||
|
||||
visible.merge(parser.nodes_visible_to_user(user, nodes))
|
||||
rescue Banzai::ReferenceParser::InvalidReferenceType
|
||||
end
|
||||
|
||||
visible
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue