Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
687bfbe932
commit
f6e2d0776a
26 changed files with 828 additions and 488 deletions
|
@ -1,5 +1,7 @@
|
|||
{
|
||||
"ee/*": { "type": "ee" },
|
||||
"app/*": { "type": "ce" },
|
||||
"lib/*": { "type": "ce" },
|
||||
"config/initializers/*.rb": {
|
||||
"alternate": "spec/initializers/{}_spec.rb",
|
||||
"type": "source"
|
||||
|
@ -57,37 +59,40 @@
|
|||
"type": "source"
|
||||
},
|
||||
"app/presenters/*.rb": {
|
||||
"alternate": "spec/app/presenters/{}_spec.rb",
|
||||
"related": "ee/app/presenters/ee/{}.rb",
|
||||
"type": "source"
|
||||
},
|
||||
"app/serializers/*.rb": {
|
||||
"alternate": "spec/app/serializers/{}_spec.rb",
|
||||
"related": "ee/app/serializers/ee/{}.rb",
|
||||
"type": "source"
|
||||
},
|
||||
"app/services/*.rb": {
|
||||
"alternate": "spec/app/services/{}_spec.rb",
|
||||
"related": "ee/app/services/ee/{}.rb",
|
||||
"type": "source"
|
||||
},
|
||||
"app/uploaders/*.rb": {
|
||||
"alternate": "spec/app/uploaders/{}_spec.rb",
|
||||
"related": "ee/app/uploaders/ee/{}.rb",
|
||||
"type": "source"
|
||||
},
|
||||
"app/validators/*.rb": {
|
||||
"alternate": "spec/app/validators/{}_spec.rb",
|
||||
"related": "ee/app/validators/ee/{}.rb",
|
||||
"type": "source"
|
||||
},
|
||||
"app/views/*.rb": {
|
||||
"alternate": "spec/app/views/{}_spec.rb",
|
||||
"related": "ee/app/views/ee/{}.rb",
|
||||
"type": "source"
|
||||
},
|
||||
"app/workers/*.rb": {
|
||||
"alternate": "spec/app/workers/{}_spec.rb",
|
||||
"related": "ee/app/workers/ee/{}.rb",
|
||||
"type": "source"
|
||||
},
|
||||
"app/*.rb": {
|
||||
"alternate": "spec/{}_spec.rb",
|
||||
"type": "source"
|
||||
},
|
||||
"spec/*_spec.rb": {
|
||||
"alternate": "app/{}.rb",
|
||||
"type": "test"
|
||||
|
@ -124,8 +129,79 @@
|
|||
"alternate": "ee/lib/api/{}.rb",
|
||||
"type": "test"
|
||||
},
|
||||
"ee/app/controllers/ee/*.rb": {
|
||||
"alternate": "ee/spec/{}_spec.rb",
|
||||
"related": "app/controllers/{}.rb",
|
||||
"type": "source"
|
||||
},
|
||||
"ee/app/finders/ee/*.rb": {
|
||||
"alternate": "ee/spec/{}_spec.rb",
|
||||
"related": "app/finders/{}.rb",
|
||||
"type": "source"
|
||||
},
|
||||
"ee/app/graphql/ee/*.rb": {
|
||||
"alternate": "ee/spec/{}_spec.rb",
|
||||
"related": "app/graphql/{}.rb",
|
||||
"type": "source"
|
||||
},
|
||||
"ee/app/helpers/ee/*.rb": {
|
||||
"alternate": "ee/spec/{}_spec.rb",
|
||||
"related": "app/helpers/{}.rb",
|
||||
"type": "source"
|
||||
},
|
||||
"ee/app/mailers/ee/*.rb": {
|
||||
"alternate": "ee/spec/{}_spec.rb",
|
||||
"related": "app/mailers/{}.rb",
|
||||
"type": "source"
|
||||
},
|
||||
"ee/app/models/ee/*.rb": {
|
||||
"alternate": "ee/spec/{}_spec.rb",
|
||||
"related": "app/models/{}.rb",
|
||||
"type": "source"
|
||||
},
|
||||
"ee/app/policies/ee/*.rb": {
|
||||
"alternate": "ee/spec/{}_spec.rb",
|
||||
"related": "app/policies/{}.rb",
|
||||
"type": "source"
|
||||
},
|
||||
"ee/app/presenters/ee/*.rb": {
|
||||
"alternate": "ee/spec/{}_spec.rb",
|
||||
"related": "app/presenters/{}.rb",
|
||||
"type": "source"
|
||||
},
|
||||
"ee/app/serializers/ee/*.rb": {
|
||||
"alternate": "spec/app/serializers/{}_spec.rb",
|
||||
"related": "app/serializers/{}.rb",
|
||||
"type": "source"
|
||||
},
|
||||
"ee/app/services/ee/*.rb": {
|
||||
"alternate": "spec/app/services/{}_spec.rb",
|
||||
"related": "app/services/{}.rb",
|
||||
"type": "source"
|
||||
},
|
||||
"ee/app/uploaders/ee/*.rb": {
|
||||
"alternate": "spec/app/uploaders/{}_spec.rb",
|
||||
"related": "app/uploaders/{}.rb",
|
||||
"type": "source"
|
||||
},
|
||||
"ee/app/validators/ee/*.rb": {
|
||||
"alternate": "spec/app/validators/{}_spec.rb",
|
||||
"related": "app/validators/{}.rb",
|
||||
"type": "source"
|
||||
},
|
||||
"ee/app/views/ee/*.rb": {
|
||||
"alternate": "spec/app/views/{}_spec.rb",
|
||||
"related": "app/views/{}.rb",
|
||||
"type": "source"
|
||||
},
|
||||
"ee/app/workers/ee/*.rb": {
|
||||
"alternate": "spec/app/workers/{}_spec.rb",
|
||||
"related": "app/workers/{}.rb",
|
||||
"type": "source"
|
||||
},
|
||||
"ee/app/*.rb": {
|
||||
"alternate": "ee/spec/{}_spec.rb",
|
||||
"related": "app/{}.rb",
|
||||
"type": "source"
|
||||
},
|
||||
"ee/spec/*_spec.rb": {
|
||||
|
@ -136,6 +212,11 @@
|
|||
"alternate": "ee/spec/lib/{}_spec.rb",
|
||||
"type": "source"
|
||||
},
|
||||
"ee/lib/ee/*.rb": {
|
||||
"alternate": "ee/spec/lib/{}_spec.rb",
|
||||
"related": "lib/{}.rb",
|
||||
"type": "source"
|
||||
},
|
||||
"ee/spec/lib/*_spec.rb": {
|
||||
"alternate": "ee/lib/{}.rb",
|
||||
"type": "test"
|
||||
|
@ -154,16 +235,18 @@
|
|||
},
|
||||
"ee/app/assets/javascripts/*.js": {
|
||||
"alternate": "ee/spec/frontend/{}_spec.js",
|
||||
"related": "app/assets/javascripts/{}.js",
|
||||
"type": "source"
|
||||
},
|
||||
"ee/app/assets/javascripts/*.vue": {
|
||||
"alternate": "ee/spec/frontend/{}_spec.js",
|
||||
"related": "app/assets/javascripts/{}.vue",
|
||||
"type": "source"
|
||||
},
|
||||
"ee/spec/frontend/*_spec.js": {
|
||||
"alternate": ["ee/app/assets/javascripts/{}.vue", "ee/app/assets/javascripts/{}.js"],
|
||||
"type": "test"
|
||||
},
|
||||
"*.rb": {"dispatch": "bundle exec rubocop {file}"},
|
||||
"*_spec.rb": {"dispatch": "bundle exec rspec {file}"}
|
||||
"*.rb": { "dispatch": "bundle exec rubocop {file}" },
|
||||
"*_spec.rb": { "dispatch": "bundle exec rspec {file}" }
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ export default {
|
|||
|
||||
computed: {
|
||||
isReference() {
|
||||
return this.nodeType === 'reference';
|
||||
return this.nodeType.startsWith('reference');
|
||||
},
|
||||
|
||||
isCommand() {
|
||||
|
@ -96,7 +96,7 @@ export default {
|
|||
getText(item) {
|
||||
if (this.isEmoji) return item.e;
|
||||
|
||||
switch (this.nodeType === 'reference' && this.nodeProps.referenceType) {
|
||||
switch (this.isReference && this.nodeProps.referenceType) {
|
||||
case 'user':
|
||||
return `${this.char}${item.username}`;
|
||||
case 'issue':
|
||||
|
@ -105,12 +105,13 @@ export default {
|
|||
case 'snippet':
|
||||
return `${this.char}${item.id}`;
|
||||
case 'milestone':
|
||||
case 'label':
|
||||
return `${this.char}${item.title}`;
|
||||
case 'label':
|
||||
return item.title;
|
||||
case 'command':
|
||||
return `${this.char}${item.name} `;
|
||||
return `${this.char}${item.name}`;
|
||||
case 'epic':
|
||||
return `${item.reference}`;
|
||||
return item.reference;
|
||||
case 'vulnerability':
|
||||
return `[vulnerability:${item.id}]`;
|
||||
default:
|
||||
|
@ -119,17 +120,35 @@ export default {
|
|||
},
|
||||
|
||||
getProps(item) {
|
||||
const props = {};
|
||||
|
||||
if (this.isEmoji) {
|
||||
return {
|
||||
Object.assign(props, {
|
||||
name: item.name,
|
||||
unicodeVersion: item.u,
|
||||
title: item.d,
|
||||
moji: item.e,
|
||||
...this.nodeProps,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return this.nodeProps;
|
||||
if (this.isLabel || this.isMilestone) {
|
||||
Object.assign(props, {
|
||||
originalText: `${this.char}${
|
||||
/\W/.test(item.title) ? JSON.stringify(item.title) : item.title
|
||||
}`,
|
||||
});
|
||||
}
|
||||
|
||||
if (this.isLabel) {
|
||||
Object.assign(props, {
|
||||
text: item.title,
|
||||
color: item.color,
|
||||
});
|
||||
}
|
||||
|
||||
Object.assign(props, this.nodeProps);
|
||||
|
||||
return props;
|
||||
},
|
||||
|
||||
onKeyDown({ event }) {
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
<script>
|
||||
import { NodeViewWrapper } from '@tiptap/vue-2';
|
||||
import { GlLabel } from '@gitlab/ui';
|
||||
import { isScopedLabel } from '~/lib/utils/common_utils';
|
||||
|
||||
export default {
|
||||
name: 'DetailsWrapper',
|
||||
components: {
|
||||
NodeViewWrapper,
|
||||
GlLabel,
|
||||
},
|
||||
props: {
|
||||
node: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
isScopedLabel() {
|
||||
return isScopedLabel({ title: this.node.attrs.originalText });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<node-view-wrapper class="gl-display-inline-block">
|
||||
<gl-label
|
||||
size="sm"
|
||||
:scoped="isScopedLabel"
|
||||
:background-color="node.attrs.color"
|
||||
:title="node.attrs.text"
|
||||
/>
|
||||
</node-view-wrapper>
|
||||
</template>
|
|
@ -46,22 +46,10 @@ export default Node.create({
|
|||
tag: 'a.gfm:not([data-link=true])',
|
||||
priority: PARSE_HTML_PRIORITY_HIGHEST,
|
||||
},
|
||||
{
|
||||
tag: 'span.gl-label',
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
renderHTML({ node }) {
|
||||
return [
|
||||
'a',
|
||||
{
|
||||
class: node.attrs.className,
|
||||
href: '#',
|
||||
'data-reference-type': node.attrs.referenceType,
|
||||
'data-original': node.attrs.originalText,
|
||||
},
|
||||
node.attrs.text,
|
||||
];
|
||||
return ['a', { href: '#' }, node.attrs.text];
|
||||
},
|
||||
});
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
import { VueNodeViewRenderer } from '@tiptap/vue-2';
|
||||
import { SCOPED_LABEL_DELIMITER } from '~/vue_shared/components/sidebar/labels_select_widget/constants';
|
||||
import LabelWrapper from '../components/wrappers/label.vue';
|
||||
import Reference from './reference';
|
||||
|
||||
export default Reference.extend({
|
||||
name: 'reference_label',
|
||||
|
||||
addAttributes() {
|
||||
return {
|
||||
...this.parent(),
|
||||
text: {
|
||||
default: null,
|
||||
parseHTML: (element) => {
|
||||
const text = element.querySelector('.gl-label-text').textContent;
|
||||
const scopedText = element.querySelector('.gl-label-text-scoped')?.textContent;
|
||||
if (!scopedText) return text;
|
||||
return `${text}${SCOPED_LABEL_DELIMITER}${scopedText}`;
|
||||
},
|
||||
},
|
||||
color: {
|
||||
default: null,
|
||||
parseHTML: (element) => element.querySelector('.gl-label-text').style.backgroundColor,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
parseHTML() {
|
||||
return [{ tag: 'span.gl-label' }];
|
||||
},
|
||||
|
||||
addNodeView() {
|
||||
return new VueNodeViewRenderer(LabelWrapper);
|
||||
},
|
||||
});
|
|
@ -34,7 +34,10 @@ function createSuggestionPlugin({
|
|||
tiptapEditor
|
||||
.chain()
|
||||
.focus()
|
||||
.insertContentAt(range, [{ type: nodeType, attrs: props }])
|
||||
.insertContentAt(range, [
|
||||
{ type: nodeType, attrs: props },
|
||||
{ type: 'text', text: ' ' },
|
||||
])
|
||||
.run();
|
||||
},
|
||||
|
||||
|
@ -82,7 +85,7 @@ function createSuggestionPlugin({
|
|||
},
|
||||
|
||||
onUpdate(props) {
|
||||
component.updateProps(props);
|
||||
component?.updateProps(props);
|
||||
|
||||
if (!props.clientRect) {
|
||||
return;
|
||||
|
@ -100,12 +103,12 @@ function createSuggestionPlugin({
|
|||
return true;
|
||||
}
|
||||
|
||||
return component.ref?.onKeyDown(props);
|
||||
return component?.ref?.onKeyDown(props);
|
||||
},
|
||||
|
||||
onExit() {
|
||||
popup?.[0].destroy();
|
||||
component.destroy();
|
||||
component?.destroy();
|
||||
},
|
||||
};
|
||||
},
|
||||
|
@ -151,7 +154,7 @@ export default Node.create({
|
|||
editor: this.editor,
|
||||
char: '~',
|
||||
dataSource: gl.GfmAutoComplete?.dataSources.labels,
|
||||
nodeType: 'reference',
|
||||
nodeType: 'reference_label',
|
||||
nodeProps: {
|
||||
referenceType: 'label',
|
||||
},
|
||||
|
|
|
@ -43,6 +43,7 @@ import OrderedList from '../extensions/ordered_list';
|
|||
import Paragraph from '../extensions/paragraph';
|
||||
import PasteMarkdown from '../extensions/paste_markdown';
|
||||
import Reference from '../extensions/reference';
|
||||
import ReferenceLabel from '../extensions/reference_label';
|
||||
import ReferenceDefinition from '../extensions/reference_definition';
|
||||
import Sourcemap from '../extensions/sourcemap';
|
||||
import Strike from '../extensions/strike';
|
||||
|
@ -132,6 +133,7 @@ export const createContentEditor = ({
|
|||
Paragraph,
|
||||
PasteMarkdown.configure({ eventHub, renderMarkdown }),
|
||||
Reference,
|
||||
ReferenceLabel,
|
||||
ReferenceDefinition,
|
||||
Sourcemap,
|
||||
Strike,
|
||||
|
|
|
@ -33,6 +33,7 @@ import MathInline from '../extensions/math_inline';
|
|||
import OrderedList from '../extensions/ordered_list';
|
||||
import Paragraph from '../extensions/paragraph';
|
||||
import Reference from '../extensions/reference';
|
||||
import ReferenceLabel from '../extensions/reference_label';
|
||||
import ReferenceDefinition from '../extensions/reference_definition';
|
||||
import Strike from '../extensions/strike';
|
||||
import Subscript from '../extensions/subscript';
|
||||
|
@ -61,6 +62,7 @@ import {
|
|||
renderHTMLNode,
|
||||
renderContent,
|
||||
renderBulletList,
|
||||
renderReference,
|
||||
preserveUnchanged,
|
||||
bold,
|
||||
italic,
|
||||
|
@ -184,9 +186,8 @@ const defaultSerializerConfig = {
|
|||
[ListItem.name]: preserveUnchanged(defaultMarkdownSerializer.nodes.list_item),
|
||||
[OrderedList.name]: preserveUnchanged(renderOrderedList),
|
||||
[Paragraph.name]: preserveUnchanged(defaultMarkdownSerializer.nodes.paragraph),
|
||||
[Reference.name]: (state, node) => {
|
||||
state.write(node.attrs.originalText || node.attrs.text);
|
||||
},
|
||||
[Reference.name]: renderReference,
|
||||
[ReferenceLabel.name]: renderReference,
|
||||
[ReferenceDefinition.name]: preserveUnchanged({
|
||||
render: (state, node, parent, index, same, sourceMarkdown) => {
|
||||
const nextSibling = parent.maybeChild(index + 1);
|
||||
|
|
|
@ -423,6 +423,10 @@ export function renderOrderedList(state, node) {
|
|||
});
|
||||
}
|
||||
|
||||
export function renderReference(state, node) {
|
||||
state.write(node.attrs.originalText || node.attrs.text);
|
||||
}
|
||||
|
||||
const generateBoldTags = (wrapTagName = openTag) => {
|
||||
return (_, mark) => {
|
||||
const type = /^(\*\*|__|<strong|<b).*/.exec(mark.attrs.sourceMarkdown)?.[1];
|
||||
|
|
|
@ -866,3 +866,36 @@ To enable debug output in the rails console, [enter the rails console](#rails-co
|
|||
```ruby
|
||||
Rails.logger.level = Logger::DEBUG
|
||||
```
|
||||
|
||||
#### Get all error messages associated with groups, subgroups, members, and requesters
|
||||
|
||||
Collect error messages associated with groups, subgroups, members, and requesters. This
|
||||
captures error messages that may not appear in the Web interface. This can be especially helpful
|
||||
for troubleshooting issues with [LDAP group sync](ldap_synchronization.md#group-sync)
|
||||
and unexpected behavior with users and their membership in groups and subgroups.
|
||||
|
||||
```ruby
|
||||
# Find the group and subgroup
|
||||
group = Group.find_by_full_path("parent_group")
|
||||
subgroup = Group.find_by_full_path("parent_group/child_group")
|
||||
|
||||
# Group and subgroup errors
|
||||
group.valid?
|
||||
group.errors.map(&:full_messages)
|
||||
|
||||
subgroup.valid?
|
||||
subgroup.errors.map(&:full_messages)
|
||||
|
||||
# Group and subgroup errors for the members AND requesters
|
||||
group.requesters.map(&:valid?)
|
||||
group.requesters.map(&:errors).map(&:full_messages)
|
||||
group.members.map(&:valid?)
|
||||
group.members.map(&:errors).map(&:full_messages)
|
||||
group.members_and_requesters.map(&:errors).map(&:full_messages)
|
||||
|
||||
subgroup.requesters.map(&:valid?)
|
||||
subgroup.requesters.map(&:errors).map(&:full_messages)
|
||||
subgroup.members.map(&:valid?)
|
||||
subgroup.members.map(&:errors).map(&:full_messages)
|
||||
subgroup.members_and_requesters.map(&:errors).map(&:full_messages)
|
||||
```
|
||||
|
|
|
@ -265,135 +265,6 @@ group = Group.find_by_full_path 'group'
|
|||
user.max_member_access_for_group group.id
|
||||
```
|
||||
|
||||
## Groups
|
||||
|
||||
### Transfer group to another location
|
||||
|
||||
```ruby
|
||||
user = User.find_by_username('<username>')
|
||||
group = Group.find_by_name("<group_name>")
|
||||
parent_group = Group.find_by(id: "<group_id>")
|
||||
service = ::Groups::TransferService.new(group, user)
|
||||
service.execute(parent_group)
|
||||
```
|
||||
|
||||
### Count unique users in a group and subgroups
|
||||
|
||||
```ruby
|
||||
group = Group.find_by_path_or_name("groupname")
|
||||
members = []
|
||||
for member in group.members_with_descendants
|
||||
members.push(member.user_name)
|
||||
end
|
||||
|
||||
members.uniq.length
|
||||
```
|
||||
|
||||
```ruby
|
||||
group = Group.find_by_path_or_name("groupname")
|
||||
|
||||
# Count users from subgroup and up (inherited)
|
||||
group.members_with_parents.count
|
||||
|
||||
# Count users from the parent group and down (specific grants)
|
||||
parent.members_with_descendants.count
|
||||
```
|
||||
|
||||
### Find groups that are pending deletion
|
||||
|
||||
```ruby
|
||||
#
|
||||
# This section lists all the groups which are pending deletion
|
||||
#
|
||||
Group.all.each do |g|
|
||||
if g.marked_for_deletion?
|
||||
puts "Group ID: #{g.id}"
|
||||
puts "Group name: #{g.name}"
|
||||
puts "Group path: #{g.full_path}"
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### Delete a group
|
||||
|
||||
```ruby
|
||||
GroupDestroyWorker.perform_async(group_id, user_id)
|
||||
```
|
||||
|
||||
### Modify group project creation
|
||||
|
||||
```ruby
|
||||
# Project creation levels: 0 - No one, 1 - Maintainers, 2 - Developers + Maintainers
|
||||
group = Group.find_by_path_or_name('group-name')
|
||||
group.project_creation_level=0
|
||||
```
|
||||
|
||||
### Modify group - disable 2FA requirement
|
||||
|
||||
WARNING:
|
||||
When disabling the 2FA Requirement on a subgroup, the whole parent group (including all subgroups) is affected by this change.
|
||||
|
||||
```ruby
|
||||
group = Group.find_by_path_or_name('group-name')
|
||||
group.require_two_factor_authentication=false
|
||||
group.save
|
||||
```
|
||||
|
||||
### Check and toggle a feature for all projects in a group
|
||||
|
||||
```ruby
|
||||
projects = Group.find_by_name('_group_name').projects
|
||||
projects.each do |p|
|
||||
state = p.<feature-name>?
|
||||
|
||||
if state
|
||||
puts "#{p.name} has <feature-name> already enabled. Skipping..."
|
||||
else
|
||||
puts "#{p.name} didn't have <feature-name> enabled. Enabling..."
|
||||
p.project_feature.update!(builds_access_level: ProjectFeature::PRIVATE)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
To find features that can be toggled, run `pp p.project_feature`.
|
||||
Available permission levels are listed in
|
||||
[concerns/featurable.rb](https://gitlab.com/gitlab-org/gitlab/blob/master/app/models/concerns/featurable.rb).
|
||||
|
||||
### Get all error messages associated with groups, subgroups, members, and requesters
|
||||
|
||||
Collect error messages associated with groups, subgroups, members, and requesters. This
|
||||
captures error messages that may not appear in the Web interface. This can be especially helpful
|
||||
for troubleshooting issues with [LDAP group sync](../auth/ldap/ldap_synchronization.md#group-sync)
|
||||
and unexpected behavior with users and their membership in groups and subgroups.
|
||||
|
||||
```ruby
|
||||
# Find the group and subgroup
|
||||
group = Group.find_by_full_path("parent_group")
|
||||
subgroup = Group.find_by_full_path("parent_group/child_group")
|
||||
|
||||
# Group and subgroup errors
|
||||
group.valid?
|
||||
group.errors.map(&:full_messages)
|
||||
|
||||
subgroup.valid?
|
||||
subgroup.errors.map(&:full_messages)
|
||||
|
||||
# Group and subgroup errors for the members AND requesters
|
||||
group.requesters.map(&:valid?)
|
||||
group.requesters.map(&:errors).map(&:full_messages)
|
||||
group.members.map(&:valid?)
|
||||
group.members.map(&:errors).map(&:full_messages)
|
||||
group.members_and_requesters.map(&:errors).map(&:full_messages)
|
||||
|
||||
subgroup.requesters.map(&:valid?)
|
||||
subgroup.requesters.map(&:errors).map(&:full_messages)
|
||||
subgroup.members.map(&:valid?)
|
||||
subgroup.members.map(&:errors).map(&:full_messages)
|
||||
subgroup.members_and_requesters.map(&:errors).map(&:full_messages)
|
||||
```
|
||||
|
||||
## Routes
|
||||
|
||||
## Merge requests
|
||||
|
||||
### Close a merge request
|
||||
|
|
|
@ -153,7 +153,7 @@ curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://git
|
|||
Trigger a pipeline by using a pipeline [trigger token](../ci/triggers/index.md#create-a-trigger-token)
|
||||
or a [CI/CD job token](../ci/jobs/ci_job_token.md) for authentication.
|
||||
|
||||
With a CI/CD job token, the [triggered pipeline is a multi-project pipeline](../ci/jobs/ci_job_token.md#trigger-a-multi-project-pipeline-by-using-a-cicd-job-token).
|
||||
With a CI/CD job token, the [triggered pipeline is a multi-project pipeline](../ci/pipelines/downstream_pipelines.md#trigger-a-multi-project-pipeline-by-using-the-api).
|
||||
The job that authenticates the request becomes associated with the upstream pipeline,
|
||||
which is visible on the [pipeline graph](../ci/pipelines/downstream_pipelines.md#view-multi-project-pipelines-in-pipeline-graphs).
|
||||
|
||||
|
|
|
@ -107,7 +107,18 @@ identifying abstract concepts and are subject to changes as we refine the design
|
|||
- **Catalog** is the collection of projects that are set to contain components.
|
||||
- **Version** is the release name of a tag in the project, which allows components to be pinned to a specific revision.
|
||||
|
||||
## Characteristics of a component
|
||||
## Definition of pipeline component
|
||||
|
||||
A pipeline component is a reusable single-purpose building block that abstracts away a single pipeline configuration unit. Components are used to compose a part or entire pipeline configuration.
|
||||
It can optionally take input parameters and set output data to be adaptable and reusable in different pipeline contexts,
|
||||
while encapsulating and isolating implementation details.
|
||||
|
||||
Components allow a pipeline to be assembled by using abstractions instead of having all the details defined in one place.
|
||||
When using a component in a pipeline, a user shouldn't need to know the implementation details of the component and should
|
||||
only rely on the provided interface. The interface will have a version / revision, so that users understand which revision they are interfacing with.
|
||||
|
||||
A pipeline component defines its type which indicates in which context of the pipeline configuration the component can be used.
|
||||
For example, a component of type X can only be used according to the type X use-case.
|
||||
|
||||
For best experience with any systems made of components it's fundamental that components are single purpose,
|
||||
isolated, reusable and resolvable.
|
||||
|
@ -118,7 +129,7 @@ isolated, reusable and resolvable.
|
|||
- **Reusability:** a component is designed to be used in different pipelines.
|
||||
Depending on the assumptions it's built on a component can be more or less generic.
|
||||
Generic components are more reusable but may require more customization.
|
||||
- **Resolvable:** When a component depends on another component, this dependency needs to be explicit and trackable. Hidden dependencies can lead to myriads of problems.
|
||||
- **Resolvable:** When a component depends on another component, this dependency must be explicit and trackable.
|
||||
|
||||
## Proposal
|
||||
|
||||
|
|
|
@ -20,7 +20,8 @@ You can use a GitLab CI/CD job token to authenticate with specific API endpoints
|
|||
(scoped to the job's project, when the `ci_job_token_scope` feature flag is enabled).
|
||||
- [Get job artifacts](../../api/job_artifacts.md#get-job-artifacts).
|
||||
- [Get job token's job](../../api/jobs.md#get-job-tokens-job).
|
||||
- [Pipeline triggers](../../api/pipeline_triggers.md), using the `token=` parameter.
|
||||
- [Pipeline triggers](../../api/pipeline_triggers.md), using the `token=` parameter
|
||||
to [trigger a multi-project pipeline](../pipelines/downstream_pipelines.md#trigger-a-multi-project-pipeline-by-using-the-api).
|
||||
- [Releases](../../api/releases/index.md) and [Release links](../../api/releases/links.md).
|
||||
- [Terraform plan](../../user/infrastructure/index.md).
|
||||
|
||||
|
@ -99,28 +100,6 @@ The job token scope is only for controlling access to private projects.
|
|||
There is [a proposal](https://gitlab.com/groups/gitlab-org/-/epics/3559) to improve
|
||||
the feature with more strategic control of the access permissions.
|
||||
|
||||
## Trigger a multi-project pipeline by using a CI/CD job token
|
||||
|
||||
> `CI_JOB_TOKEN` for multi-project pipelines was [moved](https://gitlab.com/gitlab-org/gitlab/-/issues/31573) from GitLab Premium to GitLab Free in 12.4.
|
||||
|
||||
You can use the `CI_JOB_TOKEN` to [trigger multi-project pipelines](../../api/pipeline_triggers.md#trigger-a-pipeline-with-a-token)
|
||||
from a CI/CD job.
|
||||
|
||||
For example:
|
||||
|
||||
```yaml
|
||||
trigger_pipeline:
|
||||
stage: deploy
|
||||
script:
|
||||
- curl --request POST --form "token=$CI_JOB_TOKEN" --form ref=main "https://gitlab.example.com/api/v4/projects/9/trigger/pipeline"
|
||||
rules:
|
||||
- if: $CI_COMMIT_TAG
|
||||
environment: production
|
||||
```
|
||||
|
||||
If you use the `CI_PIPELINE_SOURCE` [predefined CI/CD variable](../variables/predefined_variables.md)
|
||||
in a pipeline triggered this way, [the value is `pipeline` (not `triggered`)](../triggers/index.md#configure-cicd-jobs-to-run-in-triggered-pipelines).
|
||||
|
||||
## Download an artifact from a different pipeline **(PREMIUM)**
|
||||
|
||||
> `CI_JOB_TOKEN` for artifacts download with the API was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/2346) in GitLab 9.5.
|
||||
|
|
|
@ -7,33 +7,59 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
# Downstream pipelines **(FREE)**
|
||||
|
||||
A downstream pipeline is any GitLab CI/CD pipeline triggered by another pipeline.
|
||||
A downstream pipeline can be either:
|
||||
Downstream pipelines run independently and concurrently to the upstream pipeline
|
||||
that triggered them.
|
||||
|
||||
- A [parent-child pipeline](downstream_pipelines.md#parent-child-pipelines), which is a downstream pipeline triggered
|
||||
in the same project as the first pipeline.
|
||||
- A [multi-project pipeline](#multi-project-pipelines), which is a downstream pipeline triggered
|
||||
in a different project than the first pipeline.
|
||||
- A [parent-child pipeline](downstream_pipelines.md#parent-child-pipelines) is a downstream pipeline
|
||||
triggered in the *same* project as the first pipeline.
|
||||
- A [multi-project pipeline](#multi-project-pipelines) is a downstream pipeline triggered
|
||||
in a *different* project than the first pipeline.
|
||||
|
||||
Parent-child pipelines and multi-project pipelines can sometimes be used for similar purposes,
|
||||
but there are some key differences.
|
||||
You can sometimes use parent-child pipelines and multi-project pipelines for similar purposes,
|
||||
but there are [key differences](pipeline_architectures.md).
|
||||
|
||||
Parent-child pipelines:
|
||||
## Parent-child pipelines
|
||||
|
||||
A parent pipeline is one that triggers a downstream pipeline in the same project.
|
||||
The downstream pipeline is called a child pipeline. Child pipelines:
|
||||
|
||||
- Run under the same project, ref, and commit SHA as the parent pipeline.
|
||||
- Affect the overall status of the ref the pipeline runs against. For example,
|
||||
- Do not directly affect the overall status of the ref the pipeline runs against. For example,
|
||||
if a pipeline fails for the main branch, it's common to say that "main is broken".
|
||||
The status of child pipelines don't directly affect the status of the ref, unless the child
|
||||
The status of child pipelines only affects the status of the ref if the child
|
||||
pipeline is triggered with [`strategy:depend`](../yaml/index.md#triggerstrategy).
|
||||
- Are automatically canceled if the pipeline is configured with [`interruptible`](../yaml/index.md#interruptible)
|
||||
when a new pipeline is created for the same ref.
|
||||
- Display only the parent pipelines in the pipeline index page. Child pipelines are
|
||||
visible when visiting their parent pipeline's page.
|
||||
- Are limited to 2 levels of nesting. A parent pipeline can trigger multiple child pipelines,
|
||||
and those child pipeline can trigger multiple child pipelines (`A -> B -> C`).
|
||||
- Are not displayed in the pipeline index page. You can only view child pipelines on
|
||||
their parent pipeline's page.
|
||||
|
||||
### Nested child pipelines
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/29651) in GitLab 13.4.
|
||||
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/243747) in GitLab 13.5.
|
||||
|
||||
Parent and child pipelines were introduced with a maximum depth of one level of child
|
||||
pipelines, which was later increased to two. A parent pipeline can trigger many child
|
||||
pipelines, and these child pipelines can trigger their own child pipelines. It's not
|
||||
possible to trigger another level of child pipelines.
|
||||
|
||||
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
|
||||
For an overview, see [Nested Dynamic Pipelines](https://youtu.be/C5j3ju9je2M).
|
||||
|
||||
## Multi-project pipelines
|
||||
|
||||
A pipeline in one project can trigger downstream pipelines in another project,
|
||||
called multi-project pipelines. The user triggering the upstream pipeline must be able to
|
||||
start pipelines in the downstream project, otherwise [the downstream pipeline fails to start](#trigger-job-fails-and-does-not-create-multi-project-pipeline).
|
||||
|
||||
For example, you might deploy your web application from three different GitLab projects.
|
||||
With multi-project pipelines you can trigger a pipeline in each project, where each
|
||||
has its own build, test, and deploy process. You can visualize the connected pipelines
|
||||
in one place, including all cross-project interdependencies.
|
||||
|
||||
Multi-project pipelines:
|
||||
|
||||
- Are triggered from another pipeline, but the upstream (triggering) pipeline does
|
||||
- Are triggered from another project's pipeline, but the upstream (triggering) pipeline does
|
||||
not have much control over the downstream (triggered) pipeline. However, it can
|
||||
choose the ref of the downstream pipeline, and pass CI/CD variables to it.
|
||||
- Affect the overall status of the ref of the project it runs in, but does not
|
||||
|
@ -46,75 +72,86 @@ Multi-project pipelines:
|
|||
that happened to be triggered by an external project. They are all visible on the pipeline index page.
|
||||
- Are independent, so there are no nesting limits.
|
||||
|
||||
## Multi-project pipelines
|
||||
Learn more in the "Cross-project Pipeline Triggering and Visualization" demo at
|
||||
[GitLab@learn](https://about.gitlab.com/learn/), in the Continuous Integration section.
|
||||
|
||||
> [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/199224) to GitLab Free in 12.8.
|
||||
If you use a public project to trigger downstream pipelines in a private project,
|
||||
make sure there are no confidentiality problems. The upstream project's pipelines page
|
||||
always displays:
|
||||
|
||||
You can set up [GitLab CI/CD](../index.md) across multiple projects, so that a pipeline
|
||||
in one project can trigger a downstream pipeline in another project. You can visualize the entire pipeline
|
||||
in one place, including all cross-project interdependencies.
|
||||
|
||||
For example, you might deploy your web application from three different projects in GitLab.
|
||||
Each project has its own build, test, and deploy process. With multi-project pipelines you can
|
||||
visualize the entire pipeline, including all build and test stages for all three projects.
|
||||
|
||||
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
|
||||
For an overview, see the [Multi-project pipelines demo](https://www.youtube.com/watch?v=g_PIwBM1J84).
|
||||
|
||||
Multi-project pipelines are also useful for larger products that require cross-project interdependencies, like those
|
||||
with a [microservices architecture](https://about.gitlab.com/blog/2016/08/16/trends-in-version-control-land-microservices/).
|
||||
Learn more in the [Cross-project Pipeline Triggering and Visualization demo](https://about.gitlab.com/learn/)
|
||||
at GitLab@learn, in the Continuous Integration section.
|
||||
|
||||
If you trigger a pipeline in a downstream private project, on the upstream project's pipelines page,
|
||||
you can view:
|
||||
|
||||
- The name of the project.
|
||||
- The name of the downstream project.
|
||||
- The status of the pipeline.
|
||||
|
||||
If you have a public project that can trigger downstream pipelines in a private project,
|
||||
make sure there are no confidentiality problems.
|
||||
## Trigger a downstream pipeline from a job in the `.gitlab-ci.yml` file
|
||||
|
||||
### Trigger a multi-project pipeline from a job in your `.gitlab-ci.yml` file
|
||||
Use the [`trigger`](../yaml/index.md#trigger) keyword in your `.gitlab-ci.yml` file
|
||||
to create a job that triggers a downstream pipeline. This job is called a trigger job.
|
||||
|
||||
> [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/199224) to GitLab Free in 12.8.
|
||||
After the trigger job starts, the initial status of the job is `pending` while GitLab
|
||||
attempts to create the downstream pipeline. If the downstream pipeline is created,
|
||||
GitLab marks the job as passed, otherwise the job failed. Alternatively,
|
||||
you can [set the trigger job to show the downstream pipeline's status](#mirror-the-status-of-a-downstream-pipeline-in-the-trigger-job)
|
||||
instead.
|
||||
|
||||
When you use the [`trigger`](../yaml/index.md#trigger) keyword to create a multi-project
|
||||
pipeline in your `.gitlab-ci.yml` file, you create what is called a *trigger job*. For example:
|
||||
For example:
|
||||
|
||||
::Tabs
|
||||
|
||||
:::TabTitle Multi-project pipeline
|
||||
|
||||
```yaml
|
||||
rspec:
|
||||
stage: test
|
||||
script: bundle exec rspec
|
||||
|
||||
staging:
|
||||
variables:
|
||||
ENVIRONMENT: staging
|
||||
stage: deploy
|
||||
trigger: my/deployment
|
||||
trigger_job:
|
||||
trigger:
|
||||
project: project-group/my-downstream-project
|
||||
```
|
||||
|
||||
In this example, after the `rspec` job succeeds in the `test` stage,
|
||||
the `staging` trigger job starts. The initial status of this
|
||||
job is `pending`.
|
||||
:::TabTitle Parent-child pipeline
|
||||
|
||||
GitLab then creates a downstream pipeline in the
|
||||
`my/deployment` project and, as soon as the pipeline is created, the
|
||||
`staging` job succeeds. The full path to the project is `my/deployment`.
|
||||
```yaml
|
||||
trigger_job:
|
||||
trigger:
|
||||
include:
|
||||
- local: path/to/child-pipeline.yml
|
||||
```
|
||||
|
||||
You can view the status for the pipeline, or you can display
|
||||
[the downstream pipeline's status instead](#mirror-the-status-of-a-downstream-pipeline-in-the-trigger-job).
|
||||
::EndTabs
|
||||
|
||||
The user that creates the upstream pipeline must be able to create pipelines in the
|
||||
downstream project (`my/deployment`) too. If the downstream project is not found,
|
||||
or the user does not have [permission](../../user/permissions.md) to create a pipeline there,
|
||||
the `staging` job is marked as _failed_.
|
||||
### Use `rules` to control downstream pipeline jobs
|
||||
|
||||
#### Specify a downstream pipeline branch
|
||||
You can use CI/CD variables or the [`rules`](../yaml/index.md#rulesif) keyword to
|
||||
[control job behavior](../jobs/job_control.md) for downstream pipelines.
|
||||
|
||||
You can specify a branch name for the downstream pipeline to use.
|
||||
GitLab uses the commit on the head of the branch to
|
||||
create the downstream pipeline.
|
||||
When a downstream pipeline is triggered with the [`trigger`](../yaml/index.md#trigger) keyword,
|
||||
the value of the [`$CI_PIPELINE_SOURCE` predefined variable](../variables/predefined_variables.md)
|
||||
for all jobs is:
|
||||
|
||||
- `pipeline` for multi-project pipelines.
|
||||
- `parent` for parent-child pipelines.
|
||||
|
||||
For example, with a multi-project pipeline:
|
||||
|
||||
```yaml
|
||||
job1:
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "pipeline"
|
||||
script: echo "This job runs in multi-project pipelines only"
|
||||
|
||||
job2:
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
script: echo "This job runs in merge request pipelines only"
|
||||
|
||||
job3:
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "pipeline"
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
script: echo "This job runs in both multi-project and merge request pipelines"
|
||||
```
|
||||
|
||||
### Specify a branch for multi-project pipelines
|
||||
|
||||
You can specify a branch name for a multi-project pipeline to use. GitLab uses
|
||||
the commit on the head of the branch to create the downstream pipeline:
|
||||
|
||||
```yaml
|
||||
rspec:
|
||||
|
@ -137,112 +174,11 @@ Use:
|
|||
In [GitLab 12.4 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/10126), variable expansion is
|
||||
supported.
|
||||
|
||||
Pipelines triggered on a protected branch in a downstream project use the [role](../../user/permissions.md)
|
||||
of the user that ran the trigger job in the upstream project. If the user does not
|
||||
have permission to run CI/CD pipelines against the protected branch, the pipeline fails. See
|
||||
[pipeline security for protected branches](index.md#pipeline-security-on-protected-branches).
|
||||
### Use a child pipeline configuration file in a different project
|
||||
|
||||
#### Use `rules` or `only`/`except` with multi-project pipelines
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/205157) in GitLab 13.5.
|
||||
|
||||
You can use CI/CD variables or the [`rules`](../yaml/index.md#rulesif) keyword to
|
||||
[control job behavior](../jobs/job_control.md) for multi-project pipelines. When a
|
||||
downstream pipeline is triggered with the [`trigger`](../yaml/index.md#trigger) keyword,
|
||||
the value of the [`$CI_PIPELINE_SOURCE` predefined variable](../variables/predefined_variables.md)
|
||||
is `pipeline` for all its jobs.
|
||||
|
||||
If you use [`only/except`](../yaml/index.md#only--except) to control job behavior, use the
|
||||
[`pipelines`](../yaml/index.md#onlyrefs--exceptrefs) keyword.
|
||||
|
||||
### Trigger a multi-project pipeline by using the API
|
||||
|
||||
> [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/31573) to GitLab Free in 12.4.
|
||||
|
||||
When you use the [`CI_JOB_TOKEN` to trigger pipelines](../jobs/ci_job_token.md),
|
||||
GitLab recognizes the source of the job token. The pipelines become related,
|
||||
so you can visualize their relationships on pipeline graphs.
|
||||
|
||||
These relationships are displayed in the pipeline graph by showing inbound and
|
||||
outbound connections for upstream and downstream pipeline dependencies.
|
||||
|
||||
When using:
|
||||
|
||||
- CI/CD variables or [`rules`](../yaml/index.md#rulesif) to control job behavior, the value of
|
||||
the [`$CI_PIPELINE_SOURCE` predefined variable](../variables/predefined_variables.md) is
|
||||
`pipeline` for multi-project pipeline triggered through the API with `CI_JOB_TOKEN`.
|
||||
- [`only/except`](../yaml/index.md#only--except) to control job behavior, use the
|
||||
`pipelines` keyword.
|
||||
|
||||
## Parent-child pipelines
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/16094) in GitLab 12.7.
|
||||
|
||||
As pipelines grow more complex, a few related problems start to emerge:
|
||||
|
||||
- The staged structure, where all steps in a stage must be completed before the first
|
||||
job in next stage begins, causes arbitrary waits, slowing things down.
|
||||
- Configuration for the single global pipeline becomes very long and complicated,
|
||||
making it hard to manage.
|
||||
- Imports with [`include`](../yaml/index.md#include) increase the complexity of the configuration, and create the potential
|
||||
for namespace collisions where jobs are unintentionally duplicated.
|
||||
- Pipeline UX can become unwieldy with so many jobs and stages to work with.
|
||||
|
||||
Additionally, sometimes the behavior of a pipeline needs to be more dynamic. The ability
|
||||
to choose to start sub-pipelines (or not) is a powerful ability, especially if the
|
||||
YAML is dynamically generated.
|
||||
|
||||
![Parent pipeline graph expanded](img/parent_pipeline_graph_expanded_v14_3.png)
|
||||
|
||||
Similarly to [multi-project pipelines](#multi-project-pipelines), a pipeline can trigger a
|
||||
set of concurrently running downstream child pipelines, but in the same project:
|
||||
|
||||
- Child pipelines still execute each of their jobs according to a stage sequence, but
|
||||
would be free to continue forward through their stages without waiting for unrelated
|
||||
jobs in the parent pipeline to finish.
|
||||
- The configuration is split up into smaller child pipeline configurations. Each child pipeline contains only relevant steps which are
|
||||
easier to understand. This reduces the cognitive load to understand the overall configuration.
|
||||
- Imports are done at the child pipeline level, reducing the likelihood of collisions.
|
||||
|
||||
Child pipelines work well with other GitLab CI/CD features:
|
||||
|
||||
- Use [`rules: changes`](../yaml/index.md#ruleschanges) to trigger pipelines only when
|
||||
certain files change. This is useful for monorepos, for example.
|
||||
- Since the parent pipeline in `.gitlab-ci.yml` and the child pipeline run as normal
|
||||
pipelines, they can have their own behaviors and sequencing in relation to triggers.
|
||||
|
||||
See the [`trigger`](../yaml/index.md#trigger) keyword documentation for full details on how to
|
||||
include the child pipeline configuration.
|
||||
|
||||
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
|
||||
For an overview, see [Parent-Child Pipelines feature demo](https://youtu.be/n8KpBSqZNbk).
|
||||
|
||||
NOTE:
|
||||
The artifact containing the generated YAML file must not be [larger than 5MB](https://gitlab.com/gitlab-org/gitlab/-/issues/249140).
|
||||
|
||||
### Trigger a parent-child pipeline
|
||||
|
||||
The simplest case is [triggering a child pipeline](../yaml/index.md#trigger) using a
|
||||
local YAML file to define the pipeline configuration. In this case, the parent pipeline
|
||||
triggers the child pipeline, and continues without waiting:
|
||||
|
||||
```yaml
|
||||
microservice_a:
|
||||
trigger:
|
||||
include: path/to/microservice_a.yml
|
||||
```
|
||||
|
||||
You can include multiple files when defining a child pipeline. The child pipeline's
|
||||
configuration is composed of all configuration files merged together:
|
||||
|
||||
```yaml
|
||||
microservice_a:
|
||||
trigger:
|
||||
include:
|
||||
- local: path/to/microservice_a.yml
|
||||
- template: Security/SAST.gitlab-ci.yml
|
||||
```
|
||||
|
||||
In [GitLab 13.5 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/205157),
|
||||
you can use [`include:file`](../yaml/index.md#includefile) to trigger child pipelines
|
||||
You can use [`include:file`](../yaml/index.md#includefile) to trigger child pipelines
|
||||
with a configuration file in a different project:
|
||||
|
||||
```yaml
|
||||
|
@ -254,119 +190,150 @@ microservice_a:
|
|||
file: '/path/to/child-pipeline.yml'
|
||||
```
|
||||
|
||||
The maximum number of entries that are accepted for `trigger:include` is three.
|
||||
### Combine multiple child pipeline configuration files
|
||||
|
||||
### Merge request child pipelines
|
||||
|
||||
To trigger a child pipeline as a [merge request pipeline](merge_request_pipelines.md) we need to:
|
||||
|
||||
- Set the trigger job to run on merge requests:
|
||||
You can include up to three configuration files when defining a child pipeline. The child pipeline's
|
||||
configuration is composed of all configuration files merged together:
|
||||
|
||||
```yaml
|
||||
# parent .gitlab-ci.yml
|
||||
microservice_a:
|
||||
trigger:
|
||||
include: path/to/microservice_a.yml
|
||||
rules:
|
||||
- if: $CI_MERGE_REQUEST_ID
|
||||
include:
|
||||
- local: path/to/microservice_a.yml
|
||||
- template: Security/SAST.gitlab-ci.yml
|
||||
- project: 'my-group/my-pipeline-library'
|
||||
ref: 'main'
|
||||
file: '/path/to/child-pipeline.yml'
|
||||
```
|
||||
|
||||
- Configure the child pipeline by either:
|
||||
|
||||
- Setting all jobs in the child pipeline to evaluate in the context of a merge request:
|
||||
|
||||
```yaml
|
||||
# child path/to/microservice_a.yml
|
||||
workflow:
|
||||
rules:
|
||||
- if: $CI_MERGE_REQUEST_ID
|
||||
|
||||
job1:
|
||||
script: ...
|
||||
|
||||
job2:
|
||||
script: ...
|
||||
```
|
||||
|
||||
- Alternatively, setting the rule per job. For example, to create only `job1` in
|
||||
the context of merge request pipelines:
|
||||
|
||||
```yaml
|
||||
# child path/to/microservice_a.yml
|
||||
job1:
|
||||
script: ...
|
||||
rules:
|
||||
- if: $CI_MERGE_REQUEST_ID
|
||||
|
||||
job2:
|
||||
script: ...
|
||||
```
|
||||
|
||||
### Dynamic child pipelines
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/35632) in GitLab 12.9.
|
||||
|
||||
Instead of running a child pipeline from a static YAML file, you can define a job that runs
|
||||
your own script to generate a YAML file, which is then used to trigger a child pipeline.
|
||||
You can trigger a child pipeline from a YAML file generated in a job, instead of a
|
||||
static file saved in your project. This technique can be very powerful for generating pipelines
|
||||
targeting content that changed or to build a matrix of targets and architectures.
|
||||
|
||||
This technique can be very powerful in generating pipelines targeting content that changed or to
|
||||
build a matrix of targets and architectures.
|
||||
The artifact containing the generated YAML file must not be [larger than 5MB](https://gitlab.com/gitlab-org/gitlab/-/issues/249140).
|
||||
|
||||
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
|
||||
For an overview, see [Create child pipelines using dynamically generated configurations](https://youtu.be/nMdfus2JWHM).
|
||||
|
||||
We also have an example project using
|
||||
[Dynamic Child Pipelines with Jsonnet](https://gitlab.com/gitlab-org/project-templates/jsonnet)
|
||||
which shows how to use a data templating language to generate your `.gitlab-ci.yml` at runtime.
|
||||
You could use a similar process for other templating languages like
|
||||
For an example project that generates a dynamic child pipeline, see
|
||||
[Dynamic Child Pipelines with Jsonnet](https://gitlab.com/gitlab-org/project-templates/jsonnet).
|
||||
This project shows how to use a data templating language to generate your `.gitlab-ci.yml` at runtime.
|
||||
You can use a similar process for other templating languages like
|
||||
[Dhall](https://dhall-lang.org/) or [ytt](https://get-ytt.io/).
|
||||
|
||||
#### Trigger a dynamic child pipeline
|
||||
|
||||
To trigger a child pipeline from a dynamically generated configuration file:
|
||||
|
||||
1. Generate the configuration file in a job and save it as an [artifact](../yaml/index.md#artifactspaths):
|
||||
|
||||
```yaml
|
||||
generate-config:
|
||||
stage: build
|
||||
script: generate-ci-config > generated-config.yml
|
||||
artifacts:
|
||||
paths:
|
||||
- generated-config.yml
|
||||
```
|
||||
|
||||
1. Configure the trigger job to run after the job that generated the configuration file,
|
||||
and set `include: artifact` to the generated artifact:
|
||||
|
||||
```yaml
|
||||
child-pipeline:
|
||||
stage: test
|
||||
trigger:
|
||||
include:
|
||||
- artifact: generated-config.yml
|
||||
job: generate-config
|
||||
```
|
||||
|
||||
In this example, `generated-config.yml` is extracted from the artifacts and used as the configuration
|
||||
for triggering the child pipeline.
|
||||
|
||||
The artifact path is parsed by GitLab, not the runner, so the path must match the
|
||||
syntax for the OS running GitLab. If GitLab is running on Linux but using a Windows
|
||||
runner for testing, the path separator for the trigger job would be `/`. Other CI/CD
|
||||
configuration for jobs, like scripts, that use the Windows runner would use `\`.
|
||||
runner for testing, the path separator for the trigger job is `/`. Other CI/CD
|
||||
configuration for jobs that use the Windows runner, like scripts, use `\`.
|
||||
|
||||
For example, to trigger a child pipeline from a dynamically generated configuration file:
|
||||
### Run child pipelines with merge request pipelines
|
||||
|
||||
To trigger a child pipeline as a [merge request pipeline](merge_request_pipelines.md):
|
||||
|
||||
1. Set the trigger job to run on merge requests:
|
||||
|
||||
```yaml
|
||||
# parent .gitlab-ci.yml
|
||||
microservice_a:
|
||||
trigger:
|
||||
include: path/to/microservice_a.yml
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
```
|
||||
|
||||
1. Configure the child pipeline jobs to run in merge request pipelines:
|
||||
|
||||
- With [`workflow:rules`](../yaml/index.md#workflowrules):
|
||||
|
||||
```yaml
|
||||
# child path/to/microservice_a.yml
|
||||
workflow:
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
|
||||
job1:
|
||||
script: ...
|
||||
|
||||
job2:
|
||||
script: ...
|
||||
```
|
||||
|
||||
- By configuring [rules](../yaml/index.md#rules) for each job:
|
||||
|
||||
```yaml
|
||||
# child path/to/microservice_a.yml
|
||||
job1:
|
||||
script: ...
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
|
||||
job2:
|
||||
script: ...
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
```
|
||||
|
||||
## Trigger a multi-project pipeline by using the API
|
||||
|
||||
You can use the [CI/CD job token (`CI_JOB_TOKEN`)](../jobs/ci_job_token.md) with the
|
||||
[pipeline trigger API endpoint](../../api/pipeline_triggers.md#trigger-a-pipeline-with-a-token)
|
||||
to trigger multi-project pipelines from a CI/CD job. GitLab recognizes the source of the job token
|
||||
and marks the pipelines as related. In the pipeline graph, the relationships are displayed
|
||||
as inbound and outbound connections for upstream and downstream pipeline dependencies.
|
||||
|
||||
For example:
|
||||
|
||||
```yaml
|
||||
generate-config:
|
||||
stage: build
|
||||
script: generate-ci-config > generated-config.yml
|
||||
artifacts:
|
||||
paths:
|
||||
- generated-config.yml
|
||||
|
||||
child-pipeline:
|
||||
stage: test
|
||||
trigger:
|
||||
include:
|
||||
- artifact: generated-config.yml
|
||||
job: generate-config
|
||||
trigger_pipeline:
|
||||
stage: deploy
|
||||
script:
|
||||
- curl --request POST --form "token=$CI_JOB_TOKEN" --form ref=main "https://gitlab.example.com/api/v4/projects/9/trigger/pipeline"
|
||||
rules:
|
||||
- if: $CI_COMMIT_TAG
|
||||
environment: production
|
||||
```
|
||||
|
||||
The `generated-config.yml` is extracted from the artifacts and used as the configuration
|
||||
for triggering the child pipeline.
|
||||
|
||||
In GitLab 12.9, the child pipeline could fail to be created in certain cases, causing the parent pipeline to fail.
|
||||
This is [resolved](https://gitlab.com/gitlab-org/gitlab/-/issues/209070) in GitLab 12.10.
|
||||
|
||||
### Nested child pipelines
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/29651) in GitLab 13.4.
|
||||
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/243747) in GitLab 13.5.
|
||||
|
||||
Parent and child pipelines were introduced with a maximum depth of one level of child
|
||||
pipelines, which was later increased to two. A parent pipeline can trigger many child
|
||||
pipelines, and these child pipelines can trigger their own child pipelines. It's not
|
||||
possible to trigger another level of child pipelines.
|
||||
|
||||
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
|
||||
For an overview, see [Nested Dynamic Pipelines](https://youtu.be/C5j3ju9je2M).
|
||||
|
||||
## View a downstream pipeline
|
||||
|
||||
> Hover behavior for pipeline cards [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/197140/) in GitLab 13.2.
|
||||
|
||||
In the [pipeline graph view](index.md#view-full-pipeline-graph), downstream pipelines display
|
||||
as a list of cards on the right of the graph.
|
||||
as a list of cards on the right of the graph. Hover over the pipeline's card to view
|
||||
which job triggered the downstream pipeline.
|
||||
|
||||
### Retry a downstream pipeline
|
||||
|
||||
|
@ -390,9 +357,6 @@ To cancel a downstream pipeline that is still running, select **Cancel** (**{can
|
|||
|
||||
### Mirror the status of a downstream pipeline in the trigger job
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11238) in GitLab Premium 12.3.
|
||||
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/199224) to GitLab Free in 12.8.
|
||||
|
||||
You can mirror the pipeline status from the triggered pipeline to the source trigger job
|
||||
by using [`strategy: depend`](../yaml/index.md#triggerstrategy):
|
||||
|
||||
|
@ -549,8 +513,9 @@ The `ENVIRONMENT` variable is passed to every job defined in a downstream
|
|||
pipeline. It is available as a variable when GitLab Runner picks a job.
|
||||
|
||||
In the following configuration, the `MY_VARIABLE` variable is passed to the downstream pipeline
|
||||
that is created when the `trigger-downstream` job is queued. This is because `trigger-downstream`
|
||||
job inherits variables declared in global variables blocks, and then we pass these variables to a downstream pipeline.
|
||||
that is created when the `trigger-downstream` job is queued. This behavior is because `trigger-downstream`
|
||||
job inherits variables declared in [global `variables`](../yaml/index.md#variables) blocks,
|
||||
and then GitLab passes these variables to the downstream pipeline.
|
||||
|
||||
```yaml
|
||||
variables:
|
||||
|
@ -562,7 +527,7 @@ trigger-downstream:
|
|||
trigger: my/project
|
||||
```
|
||||
|
||||
### Prevent global variables from being passed
|
||||
#### Prevent global variables from being passed
|
||||
|
||||
You can stop global variables from reaching the downstream pipeline by using the [`inherit:variables` keyword](../yaml/index.md#inheritvariables).
|
||||
For example, in a [multi-project pipeline](#multi-project-pipelines):
|
||||
|
@ -645,3 +610,16 @@ For example, in a [multi-project pipeline](#multi-project-pipelines):
|
|||
ref: master
|
||||
artifacts: true
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Trigger job fails and does not create multi-project pipeline
|
||||
|
||||
With multi-project pipelines, the trigger job fails and does not create the downstream pipeline if:
|
||||
|
||||
- The downstream project is not found.
|
||||
- The user that creates the upstream pipeline does not have [permission](../../user/permissions.md)
|
||||
to create pipelines in the downstream project.
|
||||
- The downstream pipeline targets a protected branch and the user does not have permission
|
||||
to run pipelines against the protected branch. See [pipeline security for protected branches](index.md#pipeline-security-on-protected-branches)
|
||||
for more information.
|
||||
|
|
|
@ -10,15 +10,21 @@ type: reference
|
|||
Pipelines are the fundamental building blocks for CI/CD in GitLab. This page documents
|
||||
some of the important concepts related to them.
|
||||
|
||||
There are three main ways to structure your pipelines, each with their
|
||||
You can structure your pipelines with different methods, each with their
|
||||
own advantages. These methods can be mixed and matched if needed:
|
||||
|
||||
- [Basic](#basic-pipelines): Good for straightforward projects where all the configuration is in one easy to find place.
|
||||
- [Directed Acyclic Graph](#directed-acyclic-graph-pipelines): Good for large, complex projects that need efficient execution.
|
||||
- [Child/Parent Pipelines](#child--parent-pipelines): Good for monorepos and projects with lots of independently defined components.
|
||||
- [Parent-child pipelines](#parent-child-pipelines): Good for monorepos and projects with lots of independently defined components.
|
||||
|
||||
For more details about
|
||||
any of the keywords used below, check out our [CI YAML reference](../yaml/index.md) for details.
|
||||
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
|
||||
For an overview, see the [Parent-Child Pipelines feature demo](https://youtu.be/n8KpBSqZNbk).
|
||||
|
||||
- [Multi-project pipelines](downstream_pipelines.md#multi-project-pipelines): Good for larger products that require cross-project interdependencies,
|
||||
like those with a [microservices architecture](https://about.gitlab.com/blog/2016/08/16/trends-in-version-control-land-microservices/).
|
||||
|
||||
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
|
||||
For an overview, see the [Multi-project pipelines demo](https://www.youtube.com/watch?v=g_PIwBM1J84).
|
||||
|
||||
## Basic Pipelines
|
||||
|
||||
|
@ -163,12 +169,29 @@ deploy_b:
|
|||
environment: production
|
||||
```
|
||||
|
||||
## Child / Parent Pipelines
|
||||
## Parent-child pipelines
|
||||
|
||||
In the examples above, it's clear we've got two types of things that could be built independently.
|
||||
This is an ideal case for using [Child / Parent Pipelines](downstream_pipelines.md#parent-child-pipelines)) via
|
||||
the [`trigger` keyword](../yaml/index.md#trigger). It separates out the configuration
|
||||
into multiple files, keeping things very simple. You can also combine this with:
|
||||
As pipelines grow more complex, a few related problems start to emerge:
|
||||
|
||||
- The staged structure, where all steps in a stage must complete before the first
|
||||
job in next stage begins, causes waits that slow things down.
|
||||
- Configuration for the single global pipeline becomes
|
||||
hard to manage.
|
||||
- Imports with [`include`](../yaml/index.md#include) increase the complexity of the configuration, and can cause
|
||||
namespace collisions where jobs are unintentionally duplicated.
|
||||
- Pipeline UX has too many jobs and stages to work with.
|
||||
|
||||
Additionally, sometimes the behavior of a pipeline needs to be more dynamic. The ability
|
||||
to choose to start sub-pipelines (or not) is a powerful ability, especially if the
|
||||
YAML is dynamically generated.
|
||||
|
||||
![Parent pipeline graph expanded](img/parent_pipeline_graph_expanded_v14_3.png)
|
||||
|
||||
In the [basic pipeline](#basic-pipelines) and [directed acyclic graph](#directed-acyclic-graph-pipelines)
|
||||
examples above, there are two packages that could be built independently.
|
||||
These cases are ideal for using [parent-child pipelines](downstream_pipelines.md#parent-child-pipelines).
|
||||
It separates out the configuration into multiple files, keeping things simpler.
|
||||
You can combine parent-child pipelines with:
|
||||
|
||||
- The [`rules` keyword](../yaml/index.md#rules): For example, have the child pipelines triggered only
|
||||
when there are changes to that area.
|
||||
|
|
|
@ -3978,7 +3978,7 @@ trigger-multi-project-pipeline:
|
|||
|
||||
**Related topics**:
|
||||
|
||||
- [Multi-project pipeline configuration examples](../pipelines/downstream_pipelines.md#trigger-a-multi-project-pipeline-from-a-job-in-your-gitlab-ciyml-file).
|
||||
- [Multi-project pipeline configuration examples](../pipelines/downstream_pipelines.md#trigger-a-downstream-pipeline-from-a-job-in-the-gitlab-ciyml-file).
|
||||
- To run a pipeline for a specific branch, tag, or commit, you can use a [trigger token](../triggers/index.md)
|
||||
to authenticate with the [pipeline triggers API](../../api/pipeline_triggers.md).
|
||||
The trigger token is different than the `trigger` keyword.
|
||||
|
@ -4006,7 +4006,7 @@ trigger-child-pipeline:
|
|||
|
||||
**Related topics**:
|
||||
|
||||
- [Child pipeline configuration examples](../pipelines/downstream_pipelines.md#trigger-a-parent-child-pipeline).
|
||||
- [Child pipeline configuration examples](../pipelines/downstream_pipelines.md#trigger-a-downstream-pipeline-from-a-job-in-the-gitlab-ciyml-file).
|
||||
|
||||
#### `trigger:project`
|
||||
|
||||
|
@ -4042,7 +4042,7 @@ trigger-multi-project-pipeline:
|
|||
|
||||
**Related topics**:
|
||||
|
||||
- [Multi-project pipeline configuration examples](../pipelines/downstream_pipelines.md#trigger-a-multi-project-pipeline-from-a-job-in-your-gitlab-ciyml-file).
|
||||
- [Multi-project pipeline configuration examples](../pipelines/downstream_pipelines.md#trigger-a-downstream-pipeline-from-a-job-in-the-gitlab-ciyml-file).
|
||||
- To run a pipeline for a specific branch, tag, or commit, you can also use a [trigger token](../triggers/index.md)
|
||||
to authenticate with the [pipeline triggers API](../../api/pipeline_triggers.md).
|
||||
The trigger token is different than the `trigger` keyword.
|
||||
|
|
|
@ -805,3 +805,45 @@ To find and store an array of groups based on an SQL query in the [rails console
|
|||
Group.find_by_sql("SELECT * FROM namespaces WHERE name LIKE '%oup'")
|
||||
=> [#<Group id:3 @test-group>, #<Group id:4 @template-group/template-subgroup>]
|
||||
```
|
||||
|
||||
### Transfer subgroup to another location using Rails console
|
||||
|
||||
If transferring a group doesn't work through the UI or API, you may want to attempt the transfer in a [Rails console session](../../administration/operations/rails_console.md#starting-a-rails-console-session):
|
||||
|
||||
WARNING:
|
||||
Any command that changes data directly could be damaging if not run correctly, or under the right conditions. We highly recommend running them in a test environment with a backup of the instance ready to be restored, just in case.
|
||||
|
||||
```ruby
|
||||
user = User.find_by_username('<username>')
|
||||
group = Group.find_by_name("<group_name>")
|
||||
## Set parent_group = nil to make the subgroup a top-level group
|
||||
parent_group = Group.find_by(id: "<group_id>")
|
||||
service = ::Groups::TransferService.new(group, user)
|
||||
service.execute(parent_group)
|
||||
```
|
||||
|
||||
### Find groups pending deletion using Rails console
|
||||
|
||||
If you need to find all the groups that are pending deletion, you can use the following command in a [Rails console session](../../administration/operations/rails_console.md#starting-a-rails-console-session):
|
||||
|
||||
```ruby
|
||||
Group.all.each do |g|
|
||||
if g.marked_for_deletion?
|
||||
puts "Group ID: #{g.id}"
|
||||
puts "Group name: #{g.name}"
|
||||
puts "Group path: #{g.full_path}"
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### Delete a group using Rails console
|
||||
|
||||
At times, a group deletion may get stuck. If needed, in a [Rails console session](../../administration/operations/rails_console.md#starting-a-rails-console-session),
|
||||
you can attempt to delete a group using the following command:
|
||||
|
||||
WARNING:
|
||||
Any command that changes data directly could be damaging if not run correctly, or under the right conditions. We highly recommend running them in a test environment with a backup of the instance ready to be restored, just in case.
|
||||
|
||||
```ruby
|
||||
GroupDestroyWorker.new.perform(group_id, user_id)
|
||||
```
|
||||
|
|
139
doc/user/project/remote_development/index.md
Normal file
139
doc/user/project/remote_development/index.md
Normal file
|
@ -0,0 +1,139 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Editor
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Remote Development **(FREE)**
|
||||
|
||||
DISCLAIMER:
|
||||
This page contains information related to upcoming products, features, and functionality.
|
||||
It is important to note that the information presented is for informational purposes only.
|
||||
Please do not rely on this information for purchasing or planning purposes.
|
||||
As with all projects, the items mentioned on this page are subject to change or delay.
|
||||
The development, release, and timing of any products, features, or functionality remain at the
|
||||
sole discretion of GitLab Inc.
|
||||
|
||||
You can use the [Web IDE](../web_ide/index.md) to commit changes to a project directly from your web browser without installing any dependencies or cloning any repositories. The Web IDE, however, lacks a native runtime environment on which you would compile code, run tests, or generate real-time feedback in the IDE. For a more complete IDE experience, you can pair the Web IDE with a Remote Development environment that has been properly configured to run as a host.
|
||||
|
||||
## Connect a remote machine to the Web IDE
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- A remote virtual machine with root access
|
||||
- A domain address resolving to that machine
|
||||
- Docker installation
|
||||
|
||||
To connect a remote machine to the Web IDE, you must:
|
||||
|
||||
1. [Generate Let's Encrypt certificates](#generate-lets-encrypt-certificates).
|
||||
1. [Connect a development environment to the Web IDE](#connect-a-development-environment-to-the-web-ide).
|
||||
|
||||
### Generate Let's Encrypt certificates
|
||||
|
||||
To generate Let's Encrypt certificates:
|
||||
|
||||
1. [Point a domain to your remote machine](#point-a-domain-to-your-remote-machine).
|
||||
1. [Install Certbot](#install-certbot).
|
||||
1. [Generate the certificates](#generate-the-certificates).
|
||||
|
||||
#### Point a domain to your remote machine
|
||||
|
||||
To point a domain to your remote machine, create an `A` record from `example.remote.gitlab.dev` to `1.2.3.4`.
|
||||
|
||||
#### Install Certbot
|
||||
|
||||
[Certbot](https://certbot.eff.org/) is a free and open-source software tool that automatically uses Let's Encrypt certificates on manually administrated websites to enable HTTPS.
|
||||
|
||||
To install Certbot, run the following command:
|
||||
|
||||
```shell
|
||||
sudo apt-get update
|
||||
sudo apt-get install certbot
|
||||
```
|
||||
|
||||
#### Generate the certificates
|
||||
|
||||
```shell
|
||||
export EMAIL="YOUR_EMAIL@example.com"
|
||||
export DOMAIN="example.remote.gitlab.dev"
|
||||
|
||||
certbot -d "${DOMAIN}" \
|
||||
-m "${EMAIL}" \
|
||||
--config-dir ~/.certbot/config \
|
||||
--logs-dir ~/.certbot/logs \
|
||||
--work-dir ~/.certbot/work \
|
||||
--manual \
|
||||
--preferred-challenges dns certonly
|
||||
```
|
||||
|
||||
### Connect a development environment to the Web IDE
|
||||
|
||||
To connect a development environment to the Web IDE:
|
||||
|
||||
1. [Create a development environment](#manage-a-development-environment).
|
||||
1. [Fetch a token](#fetch-a-token).
|
||||
1. [Connect to the Web IDE](#connect-to-the-web-ide).
|
||||
|
||||
#### Manage a development environment
|
||||
|
||||
**Create a development environment**
|
||||
|
||||
```shell
|
||||
export CERTS_DIR="/home/ubuntu/.certbot/config/live/${DOMAIN}"
|
||||
export PROJECTS_DIR="/home/ubuntu"
|
||||
|
||||
docker run -d \
|
||||
--name my-environment \
|
||||
-p 3443:3443 \
|
||||
-v "${CERTS_DIR}/fullchain.pem:/gitlab-rd-web-ide/certs/fullchain.pem" \
|
||||
-v "${CERTS_DIR}/privkey.pem:/gitlab-rd-web-ide/certs/privkey.pem" \
|
||||
-v "${PROJECTS_DIR}:/projects" \
|
||||
registry.gitlab.com/gitlab-com/create-stage/editor-poc/remote-development/gitlab-rd-web-ide-docker:0.1 \
|
||||
--log-level warn --domain "${DOMAIN}" --ignore-version-mismatch
|
||||
```
|
||||
|
||||
The new development environment starts automatically.
|
||||
|
||||
**Stop a development environment**
|
||||
|
||||
```shell
|
||||
docker container stop my-environment
|
||||
```
|
||||
|
||||
**Start a development environment**
|
||||
|
||||
```shell
|
||||
docker container start my-environment
|
||||
```
|
||||
|
||||
The token changes every time you restart the development environment.
|
||||
|
||||
**Remove a development environment**
|
||||
|
||||
To remove a development environment:
|
||||
|
||||
1. Stop the development environment.
|
||||
1. Run the following command:
|
||||
|
||||
```shell
|
||||
docker container rm my-environment
|
||||
```
|
||||
|
||||
#### Fetch a token
|
||||
|
||||
```shell
|
||||
docker exec my-environment cat TOKEN
|
||||
```
|
||||
|
||||
#### Connect to the Web IDE
|
||||
|
||||
To connect to the Web IDE:
|
||||
|
||||
1. Run the following command:
|
||||
|
||||
```shell
|
||||
echo "https://gitlab-org.gitlab.io/gitlab-web-ide?remoteHost=${DOMAIN}:3443&hostPath=/projects"
|
||||
```
|
||||
|
||||
1. Go to that URL and enter the [token you fetched](#fetch-a-token).
|
|
@ -559,3 +559,33 @@ If this fails, display why it doesn't work with:
|
|||
project = Project.find_by_full_path('<project_path>')
|
||||
project.delete_error
|
||||
```
|
||||
|
||||
### Toggle a feature for all projects within a group
|
||||
|
||||
While toggling a feature in a project can be done through the [projects API](../../api/projects.md),
|
||||
you may need to do this for a large number of projects.
|
||||
|
||||
To toggle a specific feature, you can [start a Rails console session](../../administration/operations/rails_console.md#starting-a-rails-console-session)
|
||||
and run the following function:
|
||||
|
||||
WARNING:
|
||||
Any command that changes data directly could be damaging if not run correctly, or under the right conditions. We highly recommend running them in a test environment with a backup of the instance ready to be restored, just in case.
|
||||
|
||||
```ruby
|
||||
projects = Group.find_by_name('_group_name').projects
|
||||
projects.each do |p|
|
||||
## replace <feature-name> with the appropriate feature name in all instances
|
||||
state = p.<feature-name>
|
||||
|
||||
if state != 0
|
||||
puts "#{p.name} has <feature-name> already enabled. Skipping..."
|
||||
else
|
||||
puts "#{p.name} didn't have <feature-name> enabled. Enabling..."
|
||||
p.project_feature.update!(<feature-name>: ProjectFeature::PRIVATE)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
To find features that can be toggled, run `pp p.project_feature`.
|
||||
Available permission levels are listed in
|
||||
[concerns/featurable.rb](https://gitlab.com/gitlab-org/gitlab/blob/master/app/models/concerns/featurable.rb).
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem 'gitlab-qa', '~> 8', '>= 8.7.0', require: 'gitlab/qa'
|
||||
gem 'gitlab-qa', '~> 8', '>= 8.8.0', require: 'gitlab/qa'
|
||||
gem 'activesupport', '~> 6.1.4.7' # This should stay in sync with the root's Gemfile
|
||||
gem 'allure-rspec', '~> 2.18.0'
|
||||
gem 'capybara', '~> 3.35.0'
|
||||
gem 'capybara', '~> 3.37.1'
|
||||
gem 'capybara-screenshot', '~> 1.0.26'
|
||||
gem 'rake', '~> 13'
|
||||
gem 'rspec', '~> 3.11'
|
||||
|
|
|
@ -27,8 +27,9 @@ GEM
|
|||
binding_ninja (0.2.3)
|
||||
builder (3.2.4)
|
||||
byebug (11.1.3)
|
||||
capybara (3.35.3)
|
||||
capybara (3.37.1)
|
||||
addressable
|
||||
matrix
|
||||
mini_mime (>= 0.1.3)
|
||||
nokogiri (~> 1.8)
|
||||
rack (>= 1.6.0)
|
||||
|
@ -99,14 +100,15 @@ GEM
|
|||
gitlab (4.18.0)
|
||||
httparty (~> 0.18)
|
||||
terminal-table (>= 1.5.1)
|
||||
gitlab-qa (8.7.0)
|
||||
gitlab-qa (8.8.0)
|
||||
activesupport (~> 6.1)
|
||||
gitlab (~> 4.18.0)
|
||||
http (~> 5.0)
|
||||
nokogiri (~> 1.10)
|
||||
rainbow (~> 3.0.0)
|
||||
rainbow (>= 3, < 4)
|
||||
table_print (= 1.5.7)
|
||||
zeitwerk (~> 2.4)
|
||||
toxiproxy (~> 2.0.2)
|
||||
zeitwerk (>= 2, < 3)
|
||||
google-apis-compute_v1 (0.51.0)
|
||||
google-apis-core (>= 0.7.2, < 2.a)
|
||||
google-apis-core (0.9.0)
|
||||
|
@ -165,6 +167,7 @@ GEM
|
|||
rake (~> 13.0)
|
||||
macaddr (1.7.2)
|
||||
systemu (~> 2.6.5)
|
||||
matrix (0.4.2)
|
||||
memoist (0.16.2)
|
||||
method_source (1.0.0)
|
||||
mime-types (3.4.1)
|
||||
|
@ -266,6 +269,7 @@ GEM
|
|||
terminal-table (3.0.2)
|
||||
unicode-display_width (>= 1.1.1, < 3)
|
||||
timecop (0.9.5)
|
||||
toxiproxy (2.0.2)
|
||||
trailblazer-option (0.1.2)
|
||||
tzinfo (2.0.5)
|
||||
concurrent-ruby (~> 1.0)
|
||||
|
@ -300,7 +304,7 @@ DEPENDENCIES
|
|||
activesupport (~> 6.1.4.7)
|
||||
airborne (~> 0.3.7)
|
||||
allure-rspec (~> 2.18.0)
|
||||
capybara (~> 3.35.0)
|
||||
capybara (~> 3.37.1)
|
||||
capybara-screenshot (~> 1.0.26)
|
||||
chemlab (~> 0.10)
|
||||
chemlab-library-www-gitlab-com (~> 0.1)
|
||||
|
@ -310,7 +314,7 @@ DEPENDENCIES
|
|||
faraday-retry (~> 2.0)
|
||||
fog-core (= 2.1.0)
|
||||
fog-google (~> 1.19)
|
||||
gitlab-qa (~> 8, >= 8.7.0)
|
||||
gitlab-qa (~> 8, >= 8.8.0)
|
||||
influxdb-client (~> 1.17)
|
||||
knapsack (~> 4.0)
|
||||
nokogiri (~> 1.13, >= 1.13.9)
|
||||
|
|
|
@ -770,18 +770,18 @@
|
|||
# responsibility of unit tests. These tests are about the structure of the HTML.
|
||||
uri_substitution: *uri_substitution
|
||||
data_attribute_id_substitution:
|
||||
- regex: '(data-user|data-project|data-issue|data-iid|data-merge-request|data-milestone)(=")(\d+?)(")'
|
||||
- regex: '(data-user|data-project|data-issue|data-iid|data-merge-request|data-milestone|data-label)(=")(\d+?)(")'
|
||||
replacement: '\1\2ID\4'
|
||||
text_attribute_substitution:
|
||||
- regex: '(title)(=")(.+?)(")'
|
||||
- regex: '(title)(=")([^"]*)(")'
|
||||
replacement: '\1\2TEXT\4'
|
||||
path_attribute_id_substitution:
|
||||
- regex: '(group|project)(\d+)'
|
||||
replacement: '\1ID'
|
||||
markdown: |-
|
||||
Hi @gfm_user - thank you for reporting this bug (#1) we hope to fix it in %1.1 as part of !1
|
||||
Hi @gfm_user - thank you for reporting this ~"UX bug" (#1) we hope to fix it in %1.1 as part of !1
|
||||
html: |-
|
||||
<p data-sourcepos="1:1-1:92" dir="auto">Hi <a href="/gfm_user" data-reference-type="user" data-user="1" data-container="body" data-placement="top" class="gfm gfm-project_member js-user-link" title="John Doe1">@gfm_user</a> - thank you for reporting this bug (<a href="/group1/project1/-/issues/1" data-reference-type="issue" data-original="#1" data-link="false" data-link-reference="false" data-project="11" data-issue="11" data-project-path="group1/project1" data-iid="1" data-issue-type="issue" data-container="body" data-placement="top" title="My title 1" class="gfm gfm-issue">#1</a>) we hope to fix it in <a href="/group1/project1/-/milestones/1" data-reference-type="milestone" data-original="%1.1" data-link="false" data-link-reference="false" data-project="11" data-milestone="11" data-container="body" data-placement="top" title="" class="gfm gfm-milestone has-tooltip">%1.1</a> as part of <a href="/group1/project1/-/merge_requests/1" data-reference-type="merge_request" data-original="!1" data-link="false" data-link-reference="false" data-project="11" data-merge-request="11" data-project-path="group1/project1" data-iid="1" data-container="body" data-placement="top" title="My title 2" class="gfm gfm-merge_request">!1</a></p>
|
||||
<p data-sourcepos="1:1-1:98" dir="auto">Hi <a href="/gfm_user" data-reference-type="user" data-user="1" data-container="body" data-placement="top" class="gfm gfm-project_member js-user-link" title="John Doe1">@gfm_user</a> - thank you for reporting this <span class="gl-label gl-label-sm"><a href="/groupID/projectID/-/issues?label_name=UX+bug" data-reference-type="label" data-original='~"UX bug"' data-link="false" data-link-reference="false" data-project="ID" data-label="2" data-container="body" data-placement="top" title="TEXT" class="gfm gfm-label has-tooltip gl-link gl-label-link"><span class="gl-label-text gl-label-text-light" data-container="body" data-html="true" style="background-color: #990000">UX bug</span></a></span> (<a href="/group1/project1/-/issues/1" data-reference-type="issue" data-original="#1" data-link="false" data-link-reference="false" data-project="11" data-issue="11" data-project-path="group1/project1" data-iid="1" data-issue-type="issue" data-container="body" data-placement="top" title="My title 1" class="gfm gfm-issue">#1</a>) we hope to fix it in <a href="/group1/project1/-/milestones/1" data-reference-type="milestone" data-original="%1.1" data-link="false" data-link-reference="false" data-project="11" data-milestone="11" data-container="body" data-placement="top" title="" class="gfm gfm-milestone has-tooltip">%1.1</a> as part of <a href="/group1/project1/-/merge_requests/1" data-reference-type="merge_request" data-original="!1" data-link="false" data-link-reference="false" data-project="11" data-merge-request="11" data-project-path="group1/project1" data-iid="1" data-container="body" data-placement="top" title="My title 2" class="gfm gfm-merge_request">!1</a></p>
|
||||
- name: strike
|
||||
markdown: |-
|
||||
~~del~~
|
||||
|
|
|
@ -21,7 +21,9 @@ describe('~/content_editor/components/suggestions_dropdown', () => {
|
|||
const exampleUser = { username: 'root', avatar_url: 'root_avatar.png', type: 'User' };
|
||||
const exampleIssue = { iid: 123, title: 'Test Issue' };
|
||||
const exampleMergeRequest = { iid: 224, title: 'Test MR' };
|
||||
const exampleMilestone = { iid: 21, title: '1.3' };
|
||||
const exampleMilestone1 = { iid: 21, title: '13' };
|
||||
const exampleMilestone2 = { iid: 24, title: 'Milestone with spaces' };
|
||||
|
||||
const exampleCommand = {
|
||||
name: 'due',
|
||||
description: 'Set due date',
|
||||
|
@ -32,7 +34,19 @@ describe('~/content_editor/components/suggestions_dropdown', () => {
|
|||
title: '❓ Remote Development | Solution validation',
|
||||
reference: 'gitlab-org&8884',
|
||||
};
|
||||
const exampleLabel = {
|
||||
const exampleLabel1 = {
|
||||
title: 'Create',
|
||||
color: '#E44D2A',
|
||||
type: 'GroupLabel',
|
||||
textColor: '#FFFFFF',
|
||||
};
|
||||
const exampleLabel2 = {
|
||||
title: 'Weekly Team Announcement',
|
||||
color: '#E44D2A',
|
||||
type: 'GroupLabel',
|
||||
textColor: '#FFFFFF',
|
||||
};
|
||||
const exampleLabel3 = {
|
||||
title: 'devops::create',
|
||||
color: '#E44D2A',
|
||||
type: 'GroupLabel',
|
||||
|
@ -67,10 +81,13 @@ describe('~/content_editor/components/suggestions_dropdown', () => {
|
|||
${'reference'} | ${'user'} | ${'@'} | ${exampleUser} | ${`@root`} | ${{}}
|
||||
${'reference'} | ${'issue'} | ${'#'} | ${exampleIssue} | ${`#123`} | ${{}}
|
||||
${'reference'} | ${'merge_request'} | ${'!'} | ${exampleMergeRequest} | ${`!224`} | ${{}}
|
||||
${'reference'} | ${'milestone'} | ${'%'} | ${exampleMilestone} | ${`%1.3`} | ${{}}
|
||||
${'reference'} | ${'command'} | ${'/'} | ${exampleCommand} | ${'/due '} | ${{}}
|
||||
${'reference'} | ${'milestone'} | ${'%'} | ${exampleMilestone1} | ${`%13`} | ${{}}
|
||||
${'reference'} | ${'milestone'} | ${'%'} | ${exampleMilestone2} | ${`%Milestone with spaces`} | ${{ originalText: '%"Milestone with spaces"' }}
|
||||
${'reference'} | ${'command'} | ${'/'} | ${exampleCommand} | ${'/due'} | ${{}}
|
||||
${'reference'} | ${'epic'} | ${'&'} | ${exampleEpic} | ${`gitlab-org&8884`} | ${{}}
|
||||
${'reference'} | ${'label'} | ${'~'} | ${exampleLabel} | ${`~devops::create`} | ${{}}
|
||||
${'reference'} | ${'label'} | ${'~'} | ${exampleLabel1} | ${`Create`} | ${{}}
|
||||
${'reference'} | ${'label'} | ${'~'} | ${exampleLabel2} | ${`Weekly Team Announcement`} | ${{ originalText: '~"Weekly Team Announcement"' }}
|
||||
${'reference'} | ${'label'} | ${'~'} | ${exampleLabel3} | ${`devops::create`} | ${{ originalText: '~"devops::create"', text: 'devops::create' }}
|
||||
${'reference'} | ${'vulnerability'} | ${'[vulnerability:'} | ${exampleVulnerability} | ${`[vulnerability:60850147]`} | ${{}}
|
||||
${'reference'} | ${'snippet'} | ${'$'} | ${exampleSnippet} | ${`$2420859`} | ${{}}
|
||||
${'emoji'} | ${'emoji'} | ${':'} | ${exampleEmoji} | ${`😃`} | ${insertedEmojiProps}
|
||||
|
@ -130,7 +147,7 @@ describe('~/content_editor/components/suggestions_dropdown', () => {
|
|||
referenceType | char | reference | displaysID
|
||||
${'issue'} | ${'#'} | ${exampleIssue} | ${true}
|
||||
${'merge_request'} | ${'!'} | ${exampleMergeRequest} | ${true}
|
||||
${'milestone'} | ${'%'} | ${exampleMilestone} | ${false}
|
||||
${'milestone'} | ${'%'} | ${exampleMilestone1} | ${false}
|
||||
`('rendering $referenceType references', ({ referenceType, char, reference, displaysID }) => {
|
||||
it(`displays ${referenceType} ID and title`, () => {
|
||||
buildWrapper({
|
||||
|
@ -172,20 +189,26 @@ describe('~/content_editor/components/suggestions_dropdown', () => {
|
|||
});
|
||||
|
||||
describe('rendering label references', () => {
|
||||
it('displays label title and color', () => {
|
||||
it.each`
|
||||
label | displayedTitle | displayedColor
|
||||
${exampleLabel1} | ${'Create'} | ${'rgb(228, 77, 42)' /* #E44D2A */}
|
||||
${exampleLabel2} | ${'Weekly Team Announcement'} | ${'rgb(228, 77, 42)' /* #E44D2A */}
|
||||
${exampleLabel3} | ${'devops::create'} | ${'rgb(228, 77, 42)' /* #E44D2A */}
|
||||
`('displays label title and color', ({ label, displayedTitle, displayedColor }) => {
|
||||
buildWrapper({
|
||||
propsData: {
|
||||
char: '~',
|
||||
nodeProps: {
|
||||
referenceType: 'label',
|
||||
},
|
||||
items: [exampleLabel],
|
||||
items: [label],
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.text()).toContain(`${exampleLabel.title}`);
|
||||
expect(wrapper.text()).toContain(displayedTitle);
|
||||
expect(wrapper.text()).not.toContain('"'); // no quotes in the dropdown list
|
||||
expect(wrapper.findByTestId('label-color-box').attributes().style).toEqual(
|
||||
`background-color: rgb(228, 77, 42);`, // #E44D2A
|
||||
`background-color: ${displayedColor};`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
import { GlLabel } from '@gitlab/ui';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import LabelWrapper from '~/content_editor/components/wrappers/label.vue';
|
||||
|
||||
describe('content/components/wrappers/label', () => {
|
||||
let wrapper;
|
||||
|
||||
const createWrapper = async (node = {}) => {
|
||||
wrapper = shallowMountExtended(LabelWrapper, {
|
||||
propsData: { node },
|
||||
});
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it("renders a GlLabel with the node's text and color", () => {
|
||||
createWrapper({ attrs: { color: '#ff0000', text: 'foo bar', originalText: '~"foo bar"' } });
|
||||
|
||||
const glLabel = wrapper.findComponent(GlLabel);
|
||||
|
||||
expect(glLabel.props()).toMatchObject(
|
||||
expect.objectContaining({
|
||||
title: 'foo bar',
|
||||
backgroundColor: '#ff0000',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('renders a scoped label if there is a "::" in the label', () => {
|
||||
createWrapper({ attrs: { color: '#ff0000', text: 'foo::bar', originalText: '~"foo::bar"' } });
|
||||
|
||||
expect(wrapper.findComponent(GlLabel).props().scoped).toBe(true);
|
||||
});
|
||||
});
|
|
@ -13,6 +13,8 @@ RSpec.shared_context 'API::Markdown Golden Master shared context' do |markdown_y
|
|||
let_it_be(:project) { create(:project, :public, :repository, group: group) }
|
||||
|
||||
let_it_be(:label) { create(:label, project: project, title: 'bug') }
|
||||
let_it_be(:label2) { create(:label, project: project, title: 'UX bug') }
|
||||
|
||||
let_it_be(:milestone) { create(:milestone, project: project, title: '1.1') }
|
||||
let_it_be(:issue) { create(:issue, project: project) }
|
||||
let_it_be(:merge_request) { create(:merge_request, source_project: project) }
|
||||
|
|
Loading…
Reference in a new issue