Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
51a7dc1b9d
commit
7504274091
|
|
@ -0,0 +1,200 @@
|
|||
<script>
|
||||
import {
|
||||
GlDropdown,
|
||||
GlDropdownItem,
|
||||
GlModal,
|
||||
GlModalDirective,
|
||||
GlSprintf,
|
||||
GlFormGroup,
|
||||
GlFormInput,
|
||||
} from '@gitlab/ui';
|
||||
import { s__, __, sprintf } from '~/locale';
|
||||
import { DELETE_AGENT_MODAL_ID } from '../constants';
|
||||
import deleteAgent from '../graphql/mutations/delete_agent.mutation.graphql';
|
||||
import getAgentsQuery from '../graphql/queries/get_agents.query.graphql';
|
||||
import { removeAgentFromStore } from '../graphql/cache_update';
|
||||
|
||||
export default {
|
||||
i18n: {
|
||||
dropdownText: __('More options'),
|
||||
deleteButton: s__('ClusterAgents|Delete agent'),
|
||||
modalTitle: __('Are you sure?'),
|
||||
modalBody: s__(
|
||||
'ClusterAgents|Are you sure you want to delete this agent? You cannot undo this.',
|
||||
),
|
||||
modalInputLabel: s__('ClusterAgents|To delete the agent, type %{name} to confirm:'),
|
||||
modalAction: s__('ClusterAgents|Delete'),
|
||||
modalCancel: __('Cancel'),
|
||||
successMessage: s__('ClusterAgents|%{name} successfully deleted'),
|
||||
defaultError: __('An error occurred. Please try again.'),
|
||||
},
|
||||
components: {
|
||||
GlDropdown,
|
||||
GlDropdownItem,
|
||||
GlModal,
|
||||
GlSprintf,
|
||||
GlFormGroup,
|
||||
GlFormInput,
|
||||
},
|
||||
directives: {
|
||||
GlModalDirective,
|
||||
},
|
||||
inject: ['projectPath'],
|
||||
props: {
|
||||
agent: {
|
||||
required: true,
|
||||
type: Object,
|
||||
validator: (value) => ['id', 'name'].every((prop) => value[prop]),
|
||||
},
|
||||
defaultBranchName: {
|
||||
default: '.noBranch',
|
||||
required: false,
|
||||
type: String,
|
||||
},
|
||||
maxAgents: {
|
||||
default: null,
|
||||
required: false,
|
||||
type: Number,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
error: null,
|
||||
deleteConfirmText: null,
|
||||
agentName: this.agent.name,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
getAgentsQueryVariables() {
|
||||
return {
|
||||
defaultBranchName: this.defaultBranchName,
|
||||
first: this.maxAgents,
|
||||
last: null,
|
||||
projectPath: this.projectPath,
|
||||
};
|
||||
},
|
||||
modalId() {
|
||||
return sprintf(DELETE_AGENT_MODAL_ID, {
|
||||
agentName: this.agent.name,
|
||||
});
|
||||
},
|
||||
primaryModalProps() {
|
||||
return {
|
||||
text: this.$options.i18n.modalAction,
|
||||
attributes: [
|
||||
{ disabled: this.loading || this.disableModalSubmit, loading: this.loading },
|
||||
{ variant: 'danger' },
|
||||
],
|
||||
};
|
||||
},
|
||||
cancelModalProps() {
|
||||
return {
|
||||
text: this.$options.i18n.modalCancel,
|
||||
attributes: [],
|
||||
};
|
||||
},
|
||||
disableModalSubmit() {
|
||||
return this.deleteConfirmText !== this.agent.name;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async deleteAgent() {
|
||||
if (this.disableModalSubmit || this.loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const { errors } = await this.deleteAgentMutation();
|
||||
|
||||
if (errors.length) {
|
||||
throw new Error(errors[0]);
|
||||
}
|
||||
} catch (error) {
|
||||
this.error = error?.message || this.$options.i18n.defaultError;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
const successMessage = sprintf(this.$options.i18n.successMessage, { name: this.agentName });
|
||||
|
||||
this.$toast.show(this.error || successMessage);
|
||||
|
||||
this.$refs.modal.hide();
|
||||
}
|
||||
},
|
||||
deleteAgentMutation() {
|
||||
return this.$apollo
|
||||
.mutate({
|
||||
mutation: deleteAgent,
|
||||
variables: {
|
||||
input: {
|
||||
id: this.agent.id,
|
||||
},
|
||||
},
|
||||
update: (store) => {
|
||||
const deleteClusterAgent = this.agent;
|
||||
removeAgentFromStore(
|
||||
store,
|
||||
deleteClusterAgent,
|
||||
getAgentsQuery,
|
||||
this.getAgentsQueryVariables,
|
||||
);
|
||||
},
|
||||
})
|
||||
|
||||
.then(({ data: { clusterAgentDelete } }) => {
|
||||
return clusterAgentDelete;
|
||||
});
|
||||
},
|
||||
hideModal() {
|
||||
this.loading = false;
|
||||
this.error = null;
|
||||
this.deleteConfirmText = null;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<gl-dropdown
|
||||
icon="ellipsis_v"
|
||||
right
|
||||
:disabled="loading"
|
||||
:text="$options.i18n.dropdownText"
|
||||
text-sr-only
|
||||
category="tertiary"
|
||||
no-caret
|
||||
>
|
||||
<gl-dropdown-item v-gl-modal-directive="modalId">
|
||||
{{ $options.i18n.deleteButton }}
|
||||
</gl-dropdown-item>
|
||||
</gl-dropdown>
|
||||
|
||||
<gl-modal
|
||||
ref="modal"
|
||||
:modal-id="modalId"
|
||||
:title="$options.i18n.modalTitle"
|
||||
:action-primary="primaryModalProps"
|
||||
:action-cancel="cancelModalProps"
|
||||
size="sm"
|
||||
@primary="deleteAgent"
|
||||
@hide="hideModal"
|
||||
>
|
||||
<p>{{ $options.i18n.modalBody }}</p>
|
||||
|
||||
<gl-form-group>
|
||||
<template #label>
|
||||
<gl-sprintf :message="$options.i18n.modalInputLabel">
|
||||
<template #name>
|
||||
<code>{{ agent.name }}</code>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</template>
|
||||
<gl-form-input v-model="deleteConfirmText" @keydown.enter="deleteAgent" />
|
||||
</gl-form-group>
|
||||
</gl-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -1,21 +1,23 @@
|
|||
<script>
|
||||
import {
|
||||
GlLink,
|
||||
GlModalDirective,
|
||||
GlTable,
|
||||
GlIcon,
|
||||
GlSprintf,
|
||||
GlTooltip,
|
||||
GlPopover,
|
||||
} from '@gitlab/ui';
|
||||
import { s__ } from '~/locale';
|
||||
import { GlLink, GlTable, GlIcon, GlSprintf, GlTooltip, GlPopover } from '@gitlab/ui';
|
||||
import { s__, __ } from '~/locale';
|
||||
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
import timeagoMixin from '~/vue_shared/mixins/timeago';
|
||||
import { helpPagePath } from '~/helpers/help_page_helper';
|
||||
import { INSTALL_AGENT_MODAL_ID, AGENT_STATUSES } from '../constants';
|
||||
import { AGENT_STATUSES } from '../constants';
|
||||
import { getAgentConfigPath } from '../clusters_util';
|
||||
import AgentOptions from './agent_options.vue';
|
||||
|
||||
export default {
|
||||
i18n: {
|
||||
nameLabel: s__('ClusterAgents|Name'),
|
||||
statusLabel: s__('ClusterAgents|Connection status'),
|
||||
lastContactLabel: s__('ClusterAgents|Last contact'),
|
||||
configurationLabel: s__('ClusterAgents|Configuration'),
|
||||
optionsLabel: __('Options'),
|
||||
troubleshootingText: s__('ClusterAgents|Learn how to troubleshoot'),
|
||||
neverConnectedText: s__('ClusterAgents|Never'),
|
||||
},
|
||||
components: {
|
||||
GlLink,
|
||||
GlTable,
|
||||
|
|
@ -24,14 +26,10 @@ export default {
|
|||
GlTooltip,
|
||||
GlPopover,
|
||||
TimeAgoTooltip,
|
||||
},
|
||||
directives: {
|
||||
GlModalDirective,
|
||||
AgentOptions,
|
||||
},
|
||||
mixins: [timeagoMixin],
|
||||
INSTALL_AGENT_MODAL_ID,
|
||||
AGENT_STATUSES,
|
||||
|
||||
troubleshooting_link: helpPagePath('user/clusters/agent/index', {
|
||||
anchor: 'troubleshooting',
|
||||
}),
|
||||
|
|
@ -40,6 +38,16 @@ export default {
|
|||
required: true,
|
||||
type: Array,
|
||||
},
|
||||
defaultBranchName: {
|
||||
default: '.noBranch',
|
||||
required: false,
|
||||
type: String,
|
||||
},
|
||||
maxAgents: {
|
||||
default: null,
|
||||
required: false,
|
||||
type: Number,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
fields() {
|
||||
|
|
@ -47,22 +55,27 @@ export default {
|
|||
return [
|
||||
{
|
||||
key: 'name',
|
||||
label: s__('ClusterAgents|Name'),
|
||||
label: this.$options.i18n.nameLabel,
|
||||
tdClass,
|
||||
},
|
||||
{
|
||||
key: 'status',
|
||||
label: s__('ClusterAgents|Connection status'),
|
||||
label: this.$options.i18n.statusLabel,
|
||||
tdClass,
|
||||
},
|
||||
{
|
||||
key: 'lastContact',
|
||||
label: s__('ClusterAgents|Last contact'),
|
||||
label: this.$options.i18n.lastContactLabel,
|
||||
tdClass,
|
||||
},
|
||||
{
|
||||
key: 'configuration',
|
||||
label: s__('ClusterAgents|Configuration'),
|
||||
label: this.$options.i18n.configurationLabel,
|
||||
tdClass,
|
||||
},
|
||||
{
|
||||
key: 'options',
|
||||
label: this.$options.i18n.optionsLabel,
|
||||
tdClass,
|
||||
},
|
||||
];
|
||||
|
|
@ -118,7 +131,7 @@ export default {
|
|||
</p>
|
||||
<p class="gl-mb-0">
|
||||
<gl-link :href="$options.troubleshooting_link" target="_blank" class="gl-font-sm">
|
||||
{{ s__('ClusterAgents|Learn how to troubleshoot') }}</gl-link
|
||||
{{ $options.i18n.troubleshootingText }}</gl-link
|
||||
>
|
||||
</p>
|
||||
</gl-popover>
|
||||
|
|
@ -127,7 +140,7 @@ export default {
|
|||
<template #cell(lastContact)="{ item }">
|
||||
<span data-testid="cluster-agent-last-contact">
|
||||
<time-ago-tooltip v-if="item.lastContact" :time="item.lastContact" />
|
||||
<span v-else>{{ s__('ClusterAgents|Never') }}</span>
|
||||
<span v-else>{{ $options.i18n.neverConnectedText }}</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
|
|
@ -140,5 +153,13 @@ export default {
|
|||
<span v-else>{{ getAgentConfigPath(item.name) }}</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template #cell(options)="{ item }">
|
||||
<agent-options
|
||||
:agent="item"
|
||||
:default-branch-name="defaultBranchName"
|
||||
:max-agents="maxAgents"
|
||||
/>
|
||||
</template>
|
||||
</gl-table>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -151,7 +151,11 @@ export default {
|
|||
|
||||
<section v-else-if="agentList">
|
||||
<div v-if="agentList.length">
|
||||
<agent-table :agents="agentList" />
|
||||
<agent-table
|
||||
:agents="agentList"
|
||||
:default-branch-name="defaultBranchName"
|
||||
:max-agents="cursor.first"
|
||||
/>
|
||||
|
||||
<div v-if="showPagination" class="gl-display-flex gl-justify-content-center gl-mt-5">
|
||||
<gl-keyset-pagination v-bind="agentPageInfo" @prev="prevPage" @next="nextPage" />
|
||||
|
|
|
|||
|
|
@ -242,3 +242,5 @@ export const EVENT_ACTIONS_CHANGE = 'change_tab';
|
|||
|
||||
export const MODAL_TYPE_EMPTY = 'empty_state';
|
||||
export const MODAL_TYPE_REGISTER = 'agent_registration';
|
||||
|
||||
export const DELETE_AGENT_MODAL_ID = 'delete-agent-modal-%{agentName}';
|
||||
|
|
|
|||
|
|
@ -63,3 +63,25 @@ export function addAgentConfigToStore(
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function removeAgentFromStore(store, deleteClusterAgent, query, variables) {
|
||||
if (!hasErrors(deleteClusterAgent)) {
|
||||
const sourceData = store.readQuery({
|
||||
query,
|
||||
variables,
|
||||
});
|
||||
|
||||
const data = produce(sourceData, (draftData) => {
|
||||
draftData.project.clusterAgents.nodes = draftData.project.clusterAgents.nodes.filter(
|
||||
({ id }) => id !== deleteClusterAgent.id,
|
||||
);
|
||||
draftData.project.clusterAgents.count -= 1;
|
||||
});
|
||||
|
||||
store.writeQuery({
|
||||
query,
|
||||
variables,
|
||||
data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
mutation deleteClusterAgent($input: ClusterAgentDeleteInput!) {
|
||||
clusterAgentDelete(input: $input) {
|
||||
errors
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
import { GlToast } from '@gitlab/ui';
|
||||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import loadClusters from './load_clusters';
|
||||
import loadMainView from './load_main_view';
|
||||
|
||||
Vue.use(GlToast);
|
||||
Vue.use(VueApollo);
|
||||
|
||||
export default () => {
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ module Repositories
|
|||
end
|
||||
|
||||
def should_auto_link?
|
||||
return false unless Feature.enabled?(:lfs_auto_link_fork_source, project)
|
||||
return false unless Feature.enabled?(:lfs_auto_link_fork_source, project, default_enabled: :yaml)
|
||||
return false unless project.forked?
|
||||
|
||||
# Sanity check in case for some reason the user doesn't have access to the parent
|
||||
|
|
|
|||
|
|
@ -20,7 +20,14 @@ class OnboardingProgress < ApplicationRecord
|
|||
:issue_created,
|
||||
:issue_auto_closed,
|
||||
:repository_imported,
|
||||
:repository_mirrored
|
||||
:repository_mirrored,
|
||||
:secure_dependency_scanning_run,
|
||||
:secure_container_scanning_run,
|
||||
:secure_dast_run,
|
||||
:secure_secret_detection_run,
|
||||
:secure_coverage_fuzzing_run,
|
||||
:secure_api_fuzzing_run,
|
||||
:secure_cluster_image_scanning_run
|
||||
].freeze
|
||||
|
||||
scope :incomplete_actions, -> (actions) do
|
||||
|
|
@ -52,12 +59,19 @@ class OnboardingProgress < ApplicationRecord
|
|||
where(namespace: namespace).any?
|
||||
end
|
||||
|
||||
def register(namespace, action)
|
||||
return unless root_namespace?(namespace) && ACTIONS.include?(action)
|
||||
def register(namespace, actions)
|
||||
actions = Array(actions)
|
||||
return unless root_namespace?(namespace) && actions.difference(ACTIONS).empty?
|
||||
|
||||
action_column = column_name(action)
|
||||
onboarding_progress = find_by(namespace: namespace, action_column => nil)
|
||||
onboarding_progress&.update!(action_column => Time.current)
|
||||
onboarding_progress = find_by(namespace: namespace)
|
||||
return unless onboarding_progress
|
||||
|
||||
now = Time.current
|
||||
nil_actions = actions.select { |action| onboarding_progress[column_name(action)].nil? }
|
||||
return if nil_actions.empty?
|
||||
|
||||
updates = nil_actions.inject({}) { |sum, action| sum.merge!({ column_name(action) => now }) }
|
||||
onboarding_progress.update!(updates)
|
||||
end
|
||||
|
||||
def completed?(namespace, action)
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/348243
|
|||
milestone: '14.6'
|
||||
type: development
|
||||
group: group::source code
|
||||
default_enabled: false
|
||||
default_enabled: true
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddSecureScanningActionsToOnboardingProgresses < Gitlab::Database::Migration[1.0]
|
||||
def change
|
||||
change_table(:onboarding_progresses, bulk: true) do |t|
|
||||
t.column :secure_dependency_scanning_run_at, :datetime_with_timezone
|
||||
t.column :secure_container_scanning_run_at, :datetime_with_timezone
|
||||
t.column :secure_dast_run_at, :datetime_with_timezone
|
||||
t.column :secure_secret_detection_run_at, :datetime_with_timezone
|
||||
t.column :secure_coverage_fuzzing_run_at, :datetime_with_timezone
|
||||
t.column :secure_cluster_image_scanning_run_at, :datetime_with_timezone
|
||||
t.column :secure_api_fuzzing_run_at, :datetime_with_timezone
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
80c1ad5815ef68ab1a7d63566d478683b3f9a5169ed15ecd6f44f7f542d40dc8
|
||||
|
|
@ -16778,7 +16778,14 @@ CREATE TABLE onboarding_progresses (
|
|||
issue_auto_closed_at timestamp with time zone,
|
||||
repository_imported_at timestamp with time zone,
|
||||
repository_mirrored_at timestamp with time zone,
|
||||
issue_created_at timestamp with time zone
|
||||
issue_created_at timestamp with time zone,
|
||||
secure_dependency_scanning_run_at timestamp with time zone,
|
||||
secure_container_scanning_run_at timestamp with time zone,
|
||||
secure_dast_run_at timestamp with time zone,
|
||||
secure_secret_detection_run_at timestamp with time zone,
|
||||
secure_coverage_fuzzing_run_at timestamp with time zone,
|
||||
secure_cluster_image_scanning_run_at timestamp with time zone,
|
||||
secure_api_fuzzing_run_at timestamp with time zone
|
||||
);
|
||||
|
||||
CREATE SEQUENCE onboarding_progresses_id_seq
|
||||
|
|
|
|||
|
|
@ -671,7 +671,7 @@ Data that was created on the primary while the secondary was paused is lost.
|
|||
|
||||
If you are running GitLab 14.5 and later:
|
||||
|
||||
1. SSH to every Sidekiq, PostgresSQL, and Gitaly node in the **secondary** site and run one of the following commands:
|
||||
1. For each node outside of the **secondary** Kubernetes cluster using Omnibus such as PostgreSQL or Gitaly, SSH into the node and run one of the following commands:
|
||||
|
||||
- To promote the secondary node to primary:
|
||||
|
||||
|
|
@ -685,19 +685,17 @@ If you are running GitLab 14.5 and later:
|
|||
sudo gitlab-ctl geo promote --force
|
||||
```
|
||||
|
||||
1. SSH into each Rails node on your **secondary** site and run one of the following commands:
|
||||
1. Find the `toolbox` pod:
|
||||
|
||||
- To promote the secondary node to primary:
|
||||
```shell
|
||||
kubectl --namespace gitlab get pods -lapp=toolbox
|
||||
```
|
||||
|
||||
```shell
|
||||
sudo gitlab-ctl geo promote
|
||||
```
|
||||
1. Promote the secondary:
|
||||
|
||||
- To promote the secondary node to primary **without any further confirmation**:
|
||||
|
||||
```shell
|
||||
sudo gitlab-ctl geo promote --force
|
||||
```
|
||||
```shell
|
||||
kubectl --namespace gitlab exec -ti gitlab-geo-toolbox-XXX -- gitlab-rake geo:set_secondary_as_primary
|
||||
```
|
||||
|
||||
If you are running GitLab 14.4 and earlier:
|
||||
|
||||
|
|
@ -708,8 +706,6 @@ If you are running GitLab 14.4 and earlier:
|
|||
sudo gitlab-ctl promote-db
|
||||
```
|
||||
|
||||
In GitLab 12.8 and earlier, see [Message: `sudo: gitlab-pg-ctl: command not found`](../replication/troubleshooting.md#message-sudo-gitlab-pg-ctl-command-not-found).
|
||||
|
||||
1. Edit `/etc/gitlab/gitlab.rb` on the database node in the **secondary** site to
|
||||
reflect its new status as **primary** by removing any lines that enabled the
|
||||
`geo_secondary_role`:
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ description: "Set and configure Git protocol v2"
|
|||
> [Re-enabled](https://gitlab.com/gitlab-org/gitlab/-/issues/27828) in GitLab 12.8.
|
||||
|
||||
Git protocol v2 improves the v1 wire protocol in several ways and is
|
||||
enabled by default in GitLab for HTTP requests. In order to enable SSH,
|
||||
further configuration is needed by the administrator.
|
||||
enabled by default in GitLab for HTTP requests. To enable SSH, additional
|
||||
configuration is required by an administrator.
|
||||
|
||||
More details about the new features and improvements are available in
|
||||
the [Google Open Source Blog](https://opensource.googleblog.com/2018/05/introducing-git-protocol-version-2.html)
|
||||
|
|
@ -48,7 +48,7 @@ sudo systemctl restart ssh
|
|||
|
||||
## Instructions
|
||||
|
||||
In order to use the new protocol, clients need to either pass the configuration
|
||||
To use the new protocol, clients need to either pass the configuration
|
||||
`-c protocol.version=2` to the Git command, or set it globally:
|
||||
|
||||
```shell
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments"
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Commits API **(FREE)**
|
||||
|
|
@ -502,9 +502,8 @@ Example response:
|
|||
|
||||
Adds a comment to a commit.
|
||||
|
||||
In order to post a comment in a particular line of a particular file, you must
|
||||
specify the full commit SHA, the `path`, the `line` and `line_type` should be
|
||||
`new`.
|
||||
To post a comment in a particular line of a particular file, you must specify
|
||||
the full commit SHA, the `path`, the `line`, and `line_type` should be `new`.
|
||||
|
||||
The comment is added at the end of the last commit if at least one of the
|
||||
cases below is valid:
|
||||
|
|
|
|||
|
|
@ -3837,13 +3837,13 @@ The following keywords are deprecated.
|
|||
### Globally-defined `types`
|
||||
|
||||
WARNING:
|
||||
`types` is deprecated, and could be removed in a future release.
|
||||
`types` is deprecated, and is [scheduled to be removed in GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/346823).
|
||||
Use [`stages`](#stages) instead.
|
||||
|
||||
### Job-defined `type`
|
||||
|
||||
WARNING:
|
||||
`type` is deprecated, and could be removed in one of the future releases.
|
||||
`type` is deprecated, and is [scheduled to be removed in GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/346823).
|
||||
Use [`stage`](#stage) instead.
|
||||
|
||||
### Globally-defined `image`, `services`, `cache`, `before_script`, `after_script`
|
||||
|
|
|
|||
|
|
@ -1,15 +1,14 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments"
|
||||
type: howto, reference
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Edit files through the command line **(FREE)**
|
||||
|
||||
When [working with Git from the command line](start-using-git.md), you need to
|
||||
use more than just the Git commands. There are several basic commands that you should
|
||||
learn, in order to make full use of the command line.
|
||||
learn to make full use of the command line.
|
||||
|
||||
## Start working on your project
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments"
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
disqus_identifier: 'https://docs.gitlab.com/ee/workflow/gitlab_flow.html'
|
||||
---
|
||||
|
||||
|
|
@ -47,9 +47,11 @@ For a video introduction of how this works in GitLab, see [GitLab Flow](https://
|
|||
|
||||
<!-- vale gitlab.Spelling = YES -->
|
||||
|
||||
Git flow was one of the first proposals to use Git branches, and it has received a lot of attention.
|
||||
It suggests a `main` branch and a separate `develop` branch, as well as supporting branches for features, releases, and hotfixes.
|
||||
The development happens on the `develop` branch, moves to a release branch, and is finally merged into the `main` branch.
|
||||
Git flow was one of the first proposals to use Git branches, and it has received
|
||||
a lot of attention. It suggests a `main` branch and a separate `develop` branch,
|
||||
with supporting branches for features, releases, and hotfixes. The development
|
||||
happens on the `develop` branch, moves to a release branch, and is finally merged
|
||||
into the `main` branch.
|
||||
|
||||
Git flow is a well-defined standard, but its complexity introduces two problems.
|
||||
The first problem is that developers must use the `develop` branch and not `main`. `main` is reserved for code that is released to production.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
stage: none
|
||||
group: unassigned
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
type: reference
|
||||
---
|
||||
|
||||
# Sign-in restrictions **(FREE SELF)**
|
||||
|
|
@ -21,8 +20,11 @@ To access sign-in restriction settings:
|
|||
|
||||
You can restrict the password authentication for web interface and Git over HTTP(S):
|
||||
|
||||
- **Web interface**: When this feature is disabled, the **Standard** sign-in tab is removed and an [external authentication provider](../../../administration/auth/index.md) must be used.
|
||||
- **Git over HTTP(S)**: When this feature is disabled, a [Personal Access Token](../../profile/personal_access_tokens.md) must be used to authenticate.
|
||||
- **Web interface**: When this feature is disabled, the **Standard** sign-in tab
|
||||
is removed and an [external authentication provider](../../../administration/auth/index.md)
|
||||
must be used.
|
||||
- **Git over HTTP(S)**: When this feature is disabled, a [Personal Access Token](../../profile/personal_access_tokens.md)
|
||||
or LDAP password must be used to authenticate.
|
||||
|
||||
In the event of an external authentication provider outage, use the [GitLab Rails console](../../../administration/operations/rails_console.md) to [re-enable the standard web sign-in form](../../../administration/troubleshooting/gitlab_rails_cheat_sheet.md#re-enable-standard-web-sign-in-form). This configuration can also be changed over the [Application settings REST API](../../../api/settings.md#change-application-settings) while authenticating with an administrator account's personal access token.
|
||||
|
||||
|
|
|
|||
|
|
@ -136,7 +136,22 @@ with the following differences:
|
|||
|
||||
## Remove an agent
|
||||
|
||||
1. Get the `<cluster-agent-id>` and the `<cluster-agent-token-id>` from a query in the interactive GraphQL explorer.
|
||||
You can remove an agent using the [GitLab UI](#remove-an-agent-through-the-gitlab-ui) or through the [GraphQL API](#remove-an-agent-with-the-gitlab-graphql-api).
|
||||
|
||||
### Remove an agent through the GitLab UI
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/323055) in GitLab 14.7.
|
||||
|
||||
To remove an agent from the UI:
|
||||
|
||||
1. Go to your agent's configuration repository.
|
||||
1. From your project's sidebar, select **Infrastructure > Kubernetes clusters**.
|
||||
1. Select your agent from the table, and then in the **Options** column, click the vertical ellipsis
|
||||
(**{ellipsis_v}**) button and select **Delete agent**.
|
||||
|
||||
### Remove an agent with the GitLab GraphQL API
|
||||
|
||||
1. Get the `<cluster-agent-token-id>` from a query in the interactive GraphQL explorer.
|
||||
For GitLab.com, go to <https://gitlab.com/-/graphql-explorer> to open GraphQL Explorer.
|
||||
For self-managed GitLab instances, go to `https://gitlab.example.com/-/graphql-explorer`, replacing `gitlab.example.com` with your own instance's URL.
|
||||
|
||||
|
|
@ -157,7 +172,7 @@ For self-managed GitLab instances, go to `https://gitlab.example.com/-/graphql-e
|
|||
}
|
||||
```
|
||||
|
||||
1. Remove an Agent record with GraphQL by deleting the `clusterAgent` and the `clusterAgentToken`.
|
||||
1. Remove an agent record with GraphQL by deleting the `clusterAgentToken`.
|
||||
|
||||
```graphql
|
||||
mutation deleteAgent {
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ Badges directly associated with a project can be configured on the
|
|||
|
||||
## Placeholders
|
||||
|
||||
The URL a badge points to, as well as the image URL, can contain placeholders
|
||||
Both the URL a badge points to and the image URL can contain placeholders
|
||||
which are evaluated when displaying the badge. The following placeholders
|
||||
are available:
|
||||
|
||||
|
|
|
|||
|
|
@ -639,6 +639,9 @@ You can then see the issue's status in the issues list and the epic tree.
|
|||
After an issue is closed, its health status can't be edited and the **Edit** button becomes disabled
|
||||
until the issue is reopened.
|
||||
|
||||
You can also set and clear health statuses using the `/health_status` and `/clear_health_status`
|
||||
[quick actions](../quick_actions.md#issues-merge-requests-and-epics).
|
||||
|
||||
## Publish an issue **(ULTIMATE)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30906) in GitLab 13.1.
|
||||
|
|
|
|||
|
|
@ -2,13 +2,10 @@
|
|||
stage: Create
|
||||
group: Source Code
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
type: reference, concepts
|
||||
---
|
||||
|
||||
# Squash and merge **(FREE)**
|
||||
|
||||
> - [Moved](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/18956) from GitLab Premium to GitLab Free in 11.0.
|
||||
|
||||
With squash and merge you can combine all your merge request's commits into one
|
||||
and retain a clean history.
|
||||
|
||||
|
|
@ -75,7 +72,7 @@ can be either selected or unselected:
|
|||
|
||||

|
||||
|
||||
Note that Squash and Merge might not be available depending on the project's configuration
|
||||
Squash and Merge might not be available depending on the project's configuration
|
||||
for [Squash Commit Options](#squash-commits-options).
|
||||
|
||||
## Commit metadata for squashed commits
|
||||
|
|
@ -88,9 +85,9 @@ The squashed commit has the following metadata:
|
|||
|
||||
## Squash and fast-forward merge
|
||||
|
||||
When a project has the [fast-forward merge setting enabled](fast_forward_merge.md#enabling-fast-forward-merges), the merge
|
||||
request must be able to be fast-forwarded without squashing in order to squash
|
||||
it. This is because squashing is only available when accepting a merge request,
|
||||
When a project has the [fast-forward merge setting enabled](fast_forward_merge.md#enabling-fast-forward-merges),
|
||||
the merge request must be able to be fast-forwarded without squashing to squash
|
||||
it. This is because squashing is available only when accepting a merge request,
|
||||
so a merge request may need to be rebased before squashing, even though
|
||||
squashing can itself be considered equivalent to rebasing.
|
||||
|
||||
|
|
|
|||
|
|
@ -47,76 +47,78 @@ threads. Some quick actions might not be available to all subscription tiers.
|
|||
|
||||
<!-- Keep this table sorted alphabetically -->
|
||||
|
||||
| Command | Issue | Merge request | Epic | Action |
|
||||
|:--------------------------------------|:-----------------------|:-----------------------|:-----------------------|:-------|
|
||||
| `/add_contacts email1 email2` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add one or more [CRM contacts](../crm/index.md) ([introduced in GitLab 14.6](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73413)). |
|
||||
| `/approve` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Approve the merge request. |
|
||||
| `/assign @user1 @user2` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Assign one or more users. |
|
||||
| `/assign me` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Assign yourself. |
|
||||
| `/assign_reviewer @user1 @user2` or `/reviewer @user1 @user2` or `/request_review @user1 @user2` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Assign one or more users as reviewers. |
|
||||
| `/assign_reviewer me` or `/reviewer me` or `/request_review me` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Assign yourself as a reviewer. |
|
||||
| `/award :emoji:` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Toggle emoji award. |
|
||||
| `/child_epic <epic>` | **{dotted-circle}** No | **{dotted-circle}** No | **{check-circle}** Yes | Add child epic to `<epic>`. The `<epic>` value should be in the format of `&epic`, `group&epic`, or a URL to an epic ([introduced in GitLab 12.0](https://gitlab.com/gitlab-org/gitlab/-/issues/7330)). |
|
||||
| `/clear_weight` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Clear weight. |
|
||||
| `/clone <path/to/project> [--with_notes]`| **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Clone the issue to given project, or the current one if no arguments are given ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9421) in GitLab 13.7). Copies as much data as possible as long as the target project contains equivalent labels, milestones, and so on. Does not copy comments or system notes unless `--with_notes` is provided as an argument. |
|
||||
| `/close` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Close. |
|
||||
| `/confidential` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Make confidential. |
|
||||
| `/copy_metadata <!merge_request>` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Copy labels and milestone from another merge request in the project. |
|
||||
| `/copy_metadata <#issue>` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Copy labels and milestone from another issue in the project. |
|
||||
| `/create_merge_request <branch name>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Create a new merge request starting from the current issue. |
|
||||
| `/done` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Mark to do as done. |
|
||||
| `/draft` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Toggle the draft status. |
|
||||
| `/due <date>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set due date. Examples of valid `<date>` include `in 2 days`, `this Friday` and `December 31st`. |
|
||||
| `/duplicate <#issue>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Close this issue and mark as a duplicate of another issue. **(FREE)** Also, mark both as related. |
|
||||
| `/epic <epic>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add to epic `<epic>`. The `<epic>` value should be in the format of `&epic`, `group&epic`, or a URL to an epic. |
|
||||
| `/estimate <time>` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Set time estimate. For example, `/estimate 1mo 2w 3d 4h 5m`. Learn more about [time tracking](time_tracking.md). |
|
||||
| `/invite_email email1 email2` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add up to six email participants. This action is behind feature flag `issue_email_participants` and is not yet supported in issue templates. |
|
||||
| `/iteration *iteration:"iteration name"` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set iteration. For example, to set the `Late in July` iteration: `/iteration *iteration:"Late in July"` ([introduced in GitLab 13.1](https://gitlab.com/gitlab-org/gitlab/-/issues/196795)). |
|
||||
| `/label ~label1 ~label2` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Add one or more labels. Label names can also start without a tilde (`~`), but mixed syntax is not supported. |
|
||||
| `/lock` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Lock the discussions. |
|
||||
| `/merge` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Merge changes. Depending on the project setting, this may be [when the pipeline succeeds](merge_requests/merge_when_pipeline_succeeds.md), or adding to a [Merge Train](../../ci/pipelines/merge_trains.md). |
|
||||
| `/milestone %milestone` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Set milestone. |
|
||||
| `/move <path/to/project>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Move this issue to another project. |
|
||||
| `/parent_epic <epic>` | **{dotted-circle}** No | **{dotted-circle}** No | **{check-circle}** Yes | Set parent epic to `<epic>`. The `<epic>` value should be in the format of `&epic`, `group&epic`, or a URL to an epic ([introduced in GitLab 12.1](https://gitlab.com/gitlab-org/gitlab/-/issues/10556)). |
|
||||
| `/promote` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Promote issue to epic. |
|
||||
| `/promote_to_incident` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Promote issue to incident ([introduced in GitLab 14.5](https://gitlab.com/gitlab-org/gitlab/-/issues/296787)). |
|
||||
| `/publish` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Publish issue to an associated [Status Page](../../operations/incident_management/status_page.md) ([Introduced in GitLab 13.0](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30906)) |
|
||||
| `/reassign @user1 @user2` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Replace current assignees with those specified. |
|
||||
| `/reassign_reviewer @user1 @user2` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Replace current reviewers with those specified. |
|
||||
| `/rebase` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Rebase source branch. This schedules a background task that attempts to rebase the changes in the source branch on the latest commit of the target branch. If `/rebase` is used, `/merge` is ignored to avoid a race condition where the source branch is merged or deleted before it is rebased. If there are merge conflicts, GitLab displays a message that a rebase cannot be scheduled. Rebase failures are displayed with the merge request status. |
|
||||
| `/relabel ~label1 ~label2` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Replace current labels with those specified. |
|
||||
| `/relate #issue1 #issue2` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Mark issues as related. |
|
||||
| `/remove_child_epic <epic>` | **{dotted-circle}** No | **{dotted-circle}** No | **{check-circle}** Yes | Remove child epic from `<epic>`. The `<epic>` value should be in the format of `&epic`, `group&epic`, or a URL to an epic ([introduced in GitLab 12.0](https://gitlab.com/gitlab-org/gitlab/-/issues/7330)). |
|
||||
| `/remove_contacts email1 email2` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Remove one or more [CRM contacts](../crm/index.md) ([introduced in GitLab 14.6](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73413)). |
|
||||
| `/remove_due_date` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Remove due date. |
|
||||
| `/remove_epic` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Remove from epic. |
|
||||
| `/remove_estimate` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Remove time estimate. |
|
||||
| `/remove_iteration` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Remove iteration ([introduced in GitLab 13.1](https://gitlab.com/gitlab-org/gitlab/-/issues/196795)). |
|
||||
| `/remove_milestone` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Remove milestone. |
|
||||
| `/remove_parent_epic` | **{dotted-circle}** No | **{dotted-circle}** No | **{check-circle}** Yes | Remove parent epic from epic ([introduced in GitLab 12.1](https://gitlab.com/gitlab-org/gitlab/-/issues/10556)). |
|
||||
| `/remove_time_spent` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Remove time spent. |
|
||||
| `/remove_zoom` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Remove Zoom meeting from this issue ([introduced in GitLab 12.4](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/16609)). |
|
||||
| `/reopen` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Reopen. |
|
||||
| `/severity <severity>` | **{check-circle}** Yes | **{check-circle}** No | **{check-circle}** No | Set the severity. Options for `<severity>` are `S1` ... `S4`, `critical`, `high`, `medium`, `low`, `unknown`. [Introduced in GitLab 14.2](https://gitlab.com/gitlab-org/gitlab/-/issues/334045). |
|
||||
| `/shrug <comment>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Append the comment with `¯\_(ツ)_/¯`. |
|
||||
| `/spend <time> [<date>]` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Add or subtract spent time. Optionally, specify the date that time was spent on. For example, `/spend 1mo 2w 3d 4h 5m 2018-08-26` or `/spend -1h 30m`. Learn more about [time tracking](time_tracking.md). |
|
||||
| `/submit_review` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Submit a pending review ([introduced in GitLab 12.7](https://gitlab.com/gitlab-org/gitlab/-/issues/8041)). |
|
||||
| `/subscribe` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Subscribe to notifications. |
|
||||
| `/tableflip <comment>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Append the comment with `(╯°□°)╯︵ ┻━┻`. |
|
||||
| `/target_branch <local branch name>` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Set target branch. |
|
||||
| `/title <new title>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Change title. |
|
||||
| `/todo` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Add a to-do item. |
|
||||
| `/unapprove` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Unapprove the merge request. ([introduced in GitLab 14.3](https://gitlab.com/gitlab-org/gitlab/-/issues/8103)|
|
||||
| `/unassign @user1 @user2` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Remove specific assignees. |
|
||||
| `/unassign` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Remove all assignees. |
|
||||
| `/unassign_reviewer @user1 @user2` or `/remove_reviewer @user1 @user2` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Remove specific reviewers. |
|
||||
| `/unassign_reviewer` or `/remove_reviewer` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Remove all reviewers. |
|
||||
| `/unlabel ~label1 ~label2` or `/remove_label ~label1 ~label2` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Remove specified labels. |
|
||||
| `/unlabel` or `/remove_label` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Remove all labels. |
|
||||
| `/unlock` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Unlock the discussions. |
|
||||
| `/unsubscribe` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Unsubscribe from notifications. |
|
||||
| `/weight <value>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set weight. Valid options for `<value>` include `0`, `1`, `2`, and so on. |
|
||||
| `/zoom <Zoom URL>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add Zoom meeting to this issue ([introduced in GitLab 12.4](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/16609)). |
|
||||
| Command | Issue | Merge request | Epic | Action |
|
||||
|:-------------------------------------------------------------------------------------------------|:-----------------------|:-----------------------|:-----------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `/add_contacts email1 email2` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add one or more [CRM contacts](../crm/index.md) ([introduced in GitLab 14.6](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73413)). |
|
||||
| `/approve` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Approve the merge request. |
|
||||
| `/assign @user1 @user2` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Assign one or more users. |
|
||||
| `/assign me` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Assign yourself. |
|
||||
| `/assign_reviewer @user1 @user2` or `/reviewer @user1 @user2` or `/request_review @user1 @user2` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Assign one or more users as reviewers. |
|
||||
| `/assign_reviewer me` or `/reviewer me` or `/request_review me` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Assign yourself as a reviewer. |
|
||||
| `/award :emoji:` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Toggle emoji award. |
|
||||
| `/child_epic <epic>` | **{dotted-circle}** No | **{dotted-circle}** No | **{check-circle}** Yes | Add child epic to `<epic>`. The `<epic>` value should be in the format of `&epic`, `group&epic`, or a URL to an epic ([introduced in GitLab 12.0](https://gitlab.com/gitlab-org/gitlab/-/issues/7330)). |
|
||||
| `/clear_health_status` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Clear [health status](issues/managing_issues.md#health-status) ([introduced in GitLab 14.7](https://gitlab.com/gitlab-org/gitlab/-/issues/213814)). |
|
||||
| `/clear_weight` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Clear weight. |
|
||||
| `/clone <path/to/project> [--with_notes]` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Clone the issue to given project, or the current one if no arguments are given ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9421) in GitLab 13.7). Copies as much data as possible as long as the target project contains equivalent labels, milestones, and so on. Does not copy comments or system notes unless `--with_notes` is provided as an argument. |
|
||||
| `/close` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Close. |
|
||||
| `/confidential` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Make confidential. |
|
||||
| `/copy_metadata <!merge_request>` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Copy labels and milestone from another merge request in the project. |
|
||||
| `/copy_metadata <#issue>` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Copy labels and milestone from another issue in the project. |
|
||||
| `/create_merge_request <branch name>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Create a new merge request starting from the current issue. |
|
||||
| `/done` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Mark to do as done. |
|
||||
| `/draft` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Toggle the draft status. |
|
||||
| `/due <date>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set due date. Examples of valid `<date>` include `in 2 days`, `this Friday` and `December 31st`. |
|
||||
| `/duplicate <#issue>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Close this issue and mark as a duplicate of another issue. **(FREE)** Also, mark both as related. |
|
||||
| `/epic <epic>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add to epic `<epic>`. The `<epic>` value should be in the format of `&epic`, `group&epic`, or a URL to an epic. |
|
||||
| `/estimate <time>` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Set time estimate. For example, `/estimate 1mo 2w 3d 4h 5m`. Learn more about [time tracking](time_tracking.md). |
|
||||
| `/health_status <value>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set [health status](issues/managing_issues.md#health-status). Valid options for `<value>` are `on_track`, `needs_attention`, and `at_risk` ([introduced in GitLab 14.7](https://gitlab.com/gitlab-org/gitlab/-/issues/213814)). |
|
||||
| `/invite_email email1 email2` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add up to six email participants. This action is behind feature flag `issue_email_participants` and is not yet supported in issue templates. |
|
||||
| `/iteration *iteration:"iteration name"` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set iteration. For example, to set the `Late in July` iteration: `/iteration *iteration:"Late in July"` ([introduced in GitLab 13.1](https://gitlab.com/gitlab-org/gitlab/-/issues/196795)). |
|
||||
| `/label ~label1 ~label2` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Add one or more labels. Label names can also start without a tilde (`~`), but mixed syntax is not supported. |
|
||||
| `/lock` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Lock the discussions. |
|
||||
| `/merge` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Merge changes. Depending on the project setting, this may be [when the pipeline succeeds](merge_requests/merge_when_pipeline_succeeds.md), or adding to a [Merge Train](../../ci/pipelines/merge_trains.md). |
|
||||
| `/milestone %milestone` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Set milestone. |
|
||||
| `/move <path/to/project>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Move this issue to another project. |
|
||||
| `/parent_epic <epic>` | **{dotted-circle}** No | **{dotted-circle}** No | **{check-circle}** Yes | Set parent epic to `<epic>`. The `<epic>` value should be in the format of `&epic`, `group&epic`, or a URL to an epic ([introduced in GitLab 12.1](https://gitlab.com/gitlab-org/gitlab/-/issues/10556)). |
|
||||
| `/promote` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Promote issue to epic. |
|
||||
| `/promote_to_incident` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Promote issue to incident ([introduced in GitLab 14.5](https://gitlab.com/gitlab-org/gitlab/-/issues/296787)). |
|
||||
| `/publish` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Publish issue to an associated [Status Page](../../operations/incident_management/status_page.md) ([Introduced in GitLab 13.0](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30906)) |
|
||||
| `/reassign @user1 @user2` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Replace current assignees with those specified. |
|
||||
| `/reassign_reviewer @user1 @user2` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Replace current reviewers with those specified. |
|
||||
| `/rebase` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Rebase source branch. This schedules a background task that attempts to rebase the changes in the source branch on the latest commit of the target branch. If `/rebase` is used, `/merge` is ignored to avoid a race condition where the source branch is merged or deleted before it is rebased. If there are merge conflicts, GitLab displays a message that a rebase cannot be scheduled. Rebase failures are displayed with the merge request status. |
|
||||
| `/relabel ~label1 ~label2` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Replace current labels with those specified. |
|
||||
| `/relate #issue1 #issue2` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Mark issues as related. |
|
||||
| `/remove_child_epic <epic>` | **{dotted-circle}** No | **{dotted-circle}** No | **{check-circle}** Yes | Remove child epic from `<epic>`. The `<epic>` value should be in the format of `&epic`, `group&epic`, or a URL to an epic ([introduced in GitLab 12.0](https://gitlab.com/gitlab-org/gitlab/-/issues/7330)). |
|
||||
| `/remove_contacts email1 email2` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Remove one or more [CRM contacts](../crm/index.md) ([introduced in GitLab 14.6](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73413)). |
|
||||
| `/remove_due_date` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Remove due date. |
|
||||
| `/remove_epic` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Remove from epic. |
|
||||
| `/remove_estimate` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Remove time estimate. |
|
||||
| `/remove_iteration` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Remove iteration ([introduced in GitLab 13.1](https://gitlab.com/gitlab-org/gitlab/-/issues/196795)). |
|
||||
| `/remove_milestone` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Remove milestone. |
|
||||
| `/remove_parent_epic` | **{dotted-circle}** No | **{dotted-circle}** No | **{check-circle}** Yes | Remove parent epic from epic ([introduced in GitLab 12.1](https://gitlab.com/gitlab-org/gitlab/-/issues/10556)). |
|
||||
| `/remove_time_spent` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Remove time spent. |
|
||||
| `/remove_zoom` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Remove Zoom meeting from this issue ([introduced in GitLab 12.4](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/16609)). |
|
||||
| `/reopen` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Reopen. |
|
||||
| `/severity <severity>` | **{check-circle}** Yes | **{check-circle}** No | **{check-circle}** No | Set the severity. Options for `<severity>` are `S1` ... `S4`, `critical`, `high`, `medium`, `low`, `unknown`. [Introduced in GitLab 14.2](https://gitlab.com/gitlab-org/gitlab/-/issues/334045). |
|
||||
| `/shrug <comment>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Append the comment with `¯\_(ツ)_/¯`. |
|
||||
| `/spend <time> [<date>]` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Add or subtract spent time. Optionally, specify the date that time was spent on. For example, `/spend 1mo 2w 3d 4h 5m 2018-08-26` or `/spend -1h 30m`. Learn more about [time tracking](time_tracking.md). |
|
||||
| `/submit_review` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Submit a pending review ([introduced in GitLab 12.7](https://gitlab.com/gitlab-org/gitlab/-/issues/8041)). |
|
||||
| `/subscribe` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Subscribe to notifications. |
|
||||
| `/tableflip <comment>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Append the comment with `(╯°□°)╯︵ ┻━┻`. |
|
||||
| `/target_branch <local branch name>` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Set target branch. |
|
||||
| `/title <new title>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Change title. |
|
||||
| `/todo` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Add a to-do item. |
|
||||
| `/unapprove` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Unapprove the merge request. ([introduced in GitLab 14.3](https://gitlab.com/gitlab-org/gitlab/-/issues/8103) |
|
||||
| `/unassign @user1 @user2` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Remove specific assignees. |
|
||||
| `/unassign` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Remove all assignees. |
|
||||
| `/unassign_reviewer @user1 @user2` or `/remove_reviewer @user1 @user2` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Remove specific reviewers. |
|
||||
| `/unassign_reviewer` or `/remove_reviewer` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Remove all reviewers. |
|
||||
| `/unlabel ~label1 ~label2` or `/remove_label ~label1 ~label2` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Remove specified labels. |
|
||||
| `/unlabel` or `/remove_label` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Remove all labels. |
|
||||
| `/unlock` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Unlock the discussions. |
|
||||
| `/unsubscribe` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Unsubscribe from notifications. |
|
||||
| `/weight <value>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set weight. Valid options for `<value>` include `0`, `1`, `2`, and so on. |
|
||||
| `/zoom <Zoom URL>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add Zoom meeting to this issue ([introduced in GitLab 12.4](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/16609)). |
|
||||
|
||||
## Commit messages
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,10 @@
|
|||
category: quickactions
|
||||
redis_slot: quickactions
|
||||
aggregation: weekly
|
||||
- name: i_quickactions_clear_health_status
|
||||
category: quickactions
|
||||
redis_slot: quickactions
|
||||
aggregation: weekly
|
||||
- name: i_quickactions_clone
|
||||
category: quickactions
|
||||
redis_slot: quickactions
|
||||
|
|
@ -263,6 +267,10 @@
|
|||
category: quickactions
|
||||
redis_slot: quickactions
|
||||
aggregation: weekly
|
||||
- name: i_quickactions_health_status
|
||||
category: quickactions
|
||||
redis_slot: quickactions
|
||||
aggregation: weekly
|
||||
- name: i_quickactions_wip
|
||||
category: quickactions
|
||||
redis_slot: quickactions
|
||||
|
|
|
|||
|
|
@ -7329,6 +7329,9 @@ msgstr ""
|
|||
msgid "Clear due date"
|
||||
msgstr ""
|
||||
|
||||
msgid "Clear health status"
|
||||
msgstr ""
|
||||
|
||||
msgid "Clear recent searches"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -7347,9 +7350,15 @@ msgstr ""
|
|||
msgid "Clear weight"
|
||||
msgstr ""
|
||||
|
||||
msgid "Cleared health status."
|
||||
msgstr ""
|
||||
|
||||
msgid "Cleared weight."
|
||||
msgstr ""
|
||||
|
||||
msgid "Clears health status."
|
||||
msgstr ""
|
||||
|
||||
msgid "Clears weight."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -7503,6 +7512,9 @@ msgstr ""
|
|||
msgid "Cluster type must be specificed for Stages::ClusterEndpointInserter"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterAgents|%{name} successfully deleted"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterAgents|%{number} of %{total} agents"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -7560,6 +7572,9 @@ msgstr ""
|
|||
msgid "ClusterAgents|An unknown error occurred. Please try again."
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterAgents|Are you sure you want to delete this agent? You cannot undo this."
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterAgents|Certificate"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -7605,6 +7620,12 @@ msgstr ""
|
|||
msgid "ClusterAgents|Date created"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterAgents|Delete"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterAgents|Delete agent"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterAgents|Deprecated"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -7718,6 +7739,9 @@ msgstr[1] ""
|
|||
msgid "ClusterAgents|This agent has no tokens"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterAgents|To delete the agent, type %{name} to confirm:"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterAgents|To install a new agent, first add the agent's configuration file to this repository. %{linkStart}What's the agent's configuration file?%{linkEnd}"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -23038,6 +23062,9 @@ msgstr ""
|
|||
msgid "More information."
|
||||
msgstr ""
|
||||
|
||||
msgid "More options"
|
||||
msgstr ""
|
||||
|
||||
msgid "More than %{number_commits_distance} commits different with %{default_branch}"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -32296,6 +32323,12 @@ msgstr ""
|
|||
msgid "Set due date"
|
||||
msgstr ""
|
||||
|
||||
msgid "Set health status"
|
||||
msgstr ""
|
||||
|
||||
msgid "Set health status to %{health_status}."
|
||||
msgstr ""
|
||||
|
||||
msgid "Set iteration"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -32452,6 +32485,9 @@ msgstr ""
|
|||
msgid "Sets %{epic_ref} as parent epic."
|
||||
msgstr ""
|
||||
|
||||
msgid "Sets health status to %{health_status}."
|
||||
msgstr ""
|
||||
|
||||
msgid "Sets target branch to %{branch_name}."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,211 @@
|
|||
import { GlDropdown, GlDropdownItem, GlModal, GlFormInput } from '@gitlab/ui';
|
||||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import { ENTER_KEY } from '~/lib/utils/keys';
|
||||
import getAgentsQuery from '~/clusters_list/graphql/queries/get_agents.query.graphql';
|
||||
import deleteAgentMutation from '~/clusters_list/graphql/mutations/delete_agent.mutation.graphql';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import AgentOptions from '~/clusters_list/components/agent_options.vue';
|
||||
import { MAX_LIST_COUNT } from '~/clusters_list/constants';
|
||||
import { getAgentResponse, mockDeleteResponse, mockErrorDeleteResponse } from '../mocks/apollo';
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
||||
const projectPath = 'path/to/project';
|
||||
const defaultBranchName = 'default';
|
||||
const maxAgents = MAX_LIST_COUNT;
|
||||
const agent = {
|
||||
id: 'agent-id',
|
||||
name: 'agent-name',
|
||||
webPath: 'agent-webPath',
|
||||
};
|
||||
|
||||
describe('AgentOptions', () => {
|
||||
let wrapper;
|
||||
let toast;
|
||||
let apolloProvider;
|
||||
let deleteResponse;
|
||||
|
||||
const findModal = () => wrapper.findComponent(GlModal);
|
||||
const findDropdown = () => wrapper.findComponent(GlDropdown);
|
||||
const findDeleteBtn = () => wrapper.findComponent(GlDropdownItem);
|
||||
const findInput = () => wrapper.findComponent(GlFormInput);
|
||||
const findPrimaryAction = () => findModal().props('actionPrimary');
|
||||
const findPrimaryActionAttributes = (attr) => findPrimaryAction().attributes[0][attr];
|
||||
|
||||
const createMockApolloProvider = ({ mutationResponse }) => {
|
||||
deleteResponse = jest.fn().mockResolvedValue(mutationResponse);
|
||||
|
||||
return createMockApollo([[deleteAgentMutation, deleteResponse]]);
|
||||
};
|
||||
|
||||
const writeQuery = () => {
|
||||
apolloProvider.clients.defaultClient.cache.writeQuery({
|
||||
query: getAgentsQuery,
|
||||
variables: {
|
||||
projectPath,
|
||||
defaultBranchName,
|
||||
first: maxAgents,
|
||||
last: null,
|
||||
},
|
||||
data: getAgentResponse.data,
|
||||
});
|
||||
};
|
||||
|
||||
const createWrapper = ({ mutationResponse = mockDeleteResponse } = {}) => {
|
||||
apolloProvider = createMockApolloProvider({ mutationResponse });
|
||||
const provide = {
|
||||
projectPath,
|
||||
};
|
||||
const propsData = {
|
||||
defaultBranchName,
|
||||
maxAgents,
|
||||
agent,
|
||||
};
|
||||
|
||||
toast = jest.fn();
|
||||
|
||||
wrapper = shallowMountExtended(AgentOptions, {
|
||||
apolloProvider,
|
||||
provide,
|
||||
propsData,
|
||||
mocks: { $toast: { show: toast } },
|
||||
stubs: { GlModal },
|
||||
});
|
||||
wrapper.vm.$refs.modal.hide = jest.fn();
|
||||
|
||||
writeQuery();
|
||||
return wrapper.vm.$nextTick();
|
||||
};
|
||||
|
||||
const submitAgentToDelete = async () => {
|
||||
findDeleteBtn().vm.$emit('click');
|
||||
findInput().vm.$emit('input', agent.name);
|
||||
await findModal().vm.$emit('primary');
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
return createWrapper({});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
apolloProvider = null;
|
||||
deleteResponse = null;
|
||||
toast = null;
|
||||
});
|
||||
|
||||
describe('delete agent action', () => {
|
||||
it('displays a delete button', () => {
|
||||
expect(findDeleteBtn().text()).toBe('Delete agent');
|
||||
});
|
||||
|
||||
describe('when clicking the delete button', () => {
|
||||
beforeEach(() => {
|
||||
findDeleteBtn().vm.$emit('click');
|
||||
});
|
||||
|
||||
it('displays a delete confirmation modal', () => {
|
||||
expect(findModal().isVisible()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe.each`
|
||||
condition | agentName | isDisabled | mutationCalled
|
||||
${'the input with agent name is missing'} | ${''} | ${true} | ${false}
|
||||
${'the input with agent name is incorrect'} | ${'wrong-name'} | ${true} | ${false}
|
||||
${'the input with agent name is correct'} | ${agent.name} | ${false} | ${true}
|
||||
`('when $condition', ({ agentName, isDisabled, mutationCalled }) => {
|
||||
beforeEach(() => {
|
||||
findDeleteBtn().vm.$emit('click');
|
||||
findInput().vm.$emit('input', agentName);
|
||||
});
|
||||
|
||||
it(`${isDisabled ? 'disables' : 'enables'} the modal primary button`, () => {
|
||||
expect(findPrimaryActionAttributes('disabled')).toBe(isDisabled);
|
||||
});
|
||||
|
||||
describe('when user clicks the modal primary button', () => {
|
||||
beforeEach(async () => {
|
||||
await findModal().vm.$emit('primary');
|
||||
});
|
||||
|
||||
if (mutationCalled) {
|
||||
it('calls the delete mutation', () => {
|
||||
expect(deleteResponse).toHaveBeenCalledWith({ input: { id: agent.id } });
|
||||
});
|
||||
} else {
|
||||
it("doesn't call the delete mutation", () => {
|
||||
expect(deleteResponse).not.toHaveBeenCalled();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('when user presses the enter button', () => {
|
||||
beforeEach(async () => {
|
||||
await findInput().vm.$emit('keydown', new KeyboardEvent({ key: ENTER_KEY }));
|
||||
});
|
||||
|
||||
if (mutationCalled) {
|
||||
it('calls the delete mutation', () => {
|
||||
expect(deleteResponse).toHaveBeenCalledWith({ input: { id: agent.id } });
|
||||
});
|
||||
} else {
|
||||
it("doesn't call the delete mutation", () => {
|
||||
expect(deleteResponse).not.toHaveBeenCalled();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('when agent was deleted successfully', () => {
|
||||
beforeEach(async () => {
|
||||
await submitAgentToDelete();
|
||||
});
|
||||
|
||||
it('calls the toast action', () => {
|
||||
expect(toast).toHaveBeenCalledWith(`${agent.name} successfully deleted`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when getting an error deleting agent', () => {
|
||||
beforeEach(async () => {
|
||||
await createWrapper({ mutationResponse: mockErrorDeleteResponse });
|
||||
|
||||
submitAgentToDelete();
|
||||
});
|
||||
|
||||
it('displays the error message', () => {
|
||||
expect(toast).toHaveBeenCalledWith('could not delete agent');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the delete modal was closed', () => {
|
||||
beforeEach(async () => {
|
||||
const loadingResponse = new Promise(() => {});
|
||||
await createWrapper({ mutationResponse: loadingResponse });
|
||||
|
||||
submitAgentToDelete();
|
||||
});
|
||||
|
||||
it('reenables the options dropdown', async () => {
|
||||
expect(findPrimaryActionAttributes('loading')).toBe(true);
|
||||
expect(findDropdown().attributes('disabled')).toBe('true');
|
||||
|
||||
await findModal().vm.$emit('hide');
|
||||
|
||||
expect(findPrimaryActionAttributes('loading')).toBe(false);
|
||||
expect(findDropdown().attributes('disabled')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('clears the agent name input', async () => {
|
||||
expect(findInput().attributes('value')).toBe(agent.name);
|
||||
|
||||
await findModal().vm.$emit('hide');
|
||||
|
||||
expect(findInput().attributes('value')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,16 +1,22 @@
|
|||
import { GlLink, GlIcon } from '@gitlab/ui';
|
||||
import AgentTable from '~/clusters_list/components/agent_table.vue';
|
||||
import AgentOptions from '~/clusters_list/components/agent_options.vue';
|
||||
import { ACTIVE_CONNECTION_TIME } from '~/clusters_list/constants';
|
||||
import { mountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import { stubComponent } from 'helpers/stub_component';
|
||||
import timeagoMixin from '~/vue_shared/mixins/timeago';
|
||||
|
||||
const connectedTimeNow = new Date();
|
||||
const connectedTimeInactive = new Date(connectedTimeNow.getTime() - ACTIVE_CONNECTION_TIME);
|
||||
|
||||
const provideData = {
|
||||
projectPath: 'path/to/project',
|
||||
};
|
||||
const propsData = {
|
||||
agents: [
|
||||
{
|
||||
name: 'agent-1',
|
||||
id: 'agent-1-id',
|
||||
configFolder: {
|
||||
webPath: '/agent/full/path',
|
||||
},
|
||||
|
|
@ -21,6 +27,7 @@ const propsData = {
|
|||
},
|
||||
{
|
||||
name: 'agent-2',
|
||||
id: 'agent-2-id',
|
||||
webPath: '/agent-2',
|
||||
status: 'active',
|
||||
lastContact: connectedTimeNow.getTime(),
|
||||
|
|
@ -34,6 +41,7 @@ const propsData = {
|
|||
},
|
||||
{
|
||||
name: 'agent-3',
|
||||
id: 'agent-3-id',
|
||||
webPath: '/agent-3',
|
||||
status: 'inactive',
|
||||
lastContact: connectedTimeInactive.getTime(),
|
||||
|
|
@ -48,6 +56,10 @@ const propsData = {
|
|||
],
|
||||
};
|
||||
|
||||
const AgentOptionsStub = stubComponent(AgentOptions, {
|
||||
template: `<div></div>`,
|
||||
});
|
||||
|
||||
describe('AgentTable', () => {
|
||||
let wrapper;
|
||||
|
||||
|
|
@ -57,15 +69,21 @@ describe('AgentTable', () => {
|
|||
const findLastContactText = (at) => wrapper.findAllByTestId('cluster-agent-last-contact').at(at);
|
||||
const findConfiguration = (at) =>
|
||||
wrapper.findAllByTestId('cluster-agent-configuration-link').at(at);
|
||||
const findAgentOptions = () => wrapper.findAllComponents(AgentOptions);
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = mountExtended(AgentTable, { propsData });
|
||||
wrapper = mountExtended(AgentTable, {
|
||||
propsData,
|
||||
provide: provideData,
|
||||
stubs: {
|
||||
AgentOptions: AgentOptionsStub,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
if (wrapper) {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -108,5 +126,9 @@ describe('AgentTable', () => {
|
|||
expect(findLink.exists()).toBe(hasLink);
|
||||
expect(findConfiguration(lineNumber).text()).toBe(agentPath);
|
||||
});
|
||||
|
||||
it('displays actions menu for each agent', () => {
|
||||
expect(findAgentOptions()).toHaveLength(3);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -75,3 +75,15 @@ export const getAgentResponse = {
|
|||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const mockDeleteResponse = {
|
||||
data: { clusterAgentDelete: { errors: [] } },
|
||||
};
|
||||
|
||||
export const mockErrorDeleteResponse = {
|
||||
data: {
|
||||
clusterAgentDelete: {
|
||||
errors: ['could not delete agent'],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -131,29 +131,86 @@ RSpec.describe OnboardingProgress do
|
|||
end
|
||||
|
||||
describe '.register' do
|
||||
subject(:register_action) { described_class.register(namespace, action) }
|
||||
context 'for a single action' do
|
||||
subject(:register_action) { described_class.register(namespace, action) }
|
||||
|
||||
context 'when the namespace was onboarded' do
|
||||
before do
|
||||
described_class.onboard(namespace)
|
||||
context 'when the namespace was onboarded' do
|
||||
before do
|
||||
described_class.onboard(namespace)
|
||||
end
|
||||
|
||||
it 'registers the action for the namespace' do
|
||||
expect { register_action }.to change { described_class.completed?(namespace, action) }.from(false).to(true)
|
||||
end
|
||||
|
||||
it 'does not override timestamp', :aggregate_failures do
|
||||
expect(described_class.find_by_namespace_id(namespace.id).subscription_created_at).to be_nil
|
||||
register_action
|
||||
expect(described_class.find_by_namespace_id(namespace.id).subscription_created_at).not_to be_nil
|
||||
expect { described_class.register(namespace, action) }.not_to change { described_class.find_by_namespace_id(namespace.id).subscription_created_at }
|
||||
end
|
||||
|
||||
context 'when the action does not exist' do
|
||||
let(:action) { :foo }
|
||||
|
||||
it 'does not register the action for the namespace' do
|
||||
expect { register_action }.not_to change { described_class.completed?(namespace, action) }.from(nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'registers the action for the namespace' do
|
||||
expect { register_action }.to change { described_class.completed?(namespace, action) }.from(false).to(true)
|
||||
end
|
||||
|
||||
context 'when the action does not exist' do
|
||||
let(:action) { :foo }
|
||||
|
||||
context 'when the namespace was not onboarded' do
|
||||
it 'does not register the action for the namespace' do
|
||||
expect { register_action }.not_to change { described_class.completed?(namespace, action) }.from(nil)
|
||||
expect { register_action }.not_to change { described_class.completed?(namespace, action) }.from(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the namespace was not onboarded' do
|
||||
it 'does not register the action for the namespace' do
|
||||
expect { register_action }.not_to change { described_class.completed?(namespace, action) }.from(false)
|
||||
context 'for multiple actions' do
|
||||
let(:action1) { :security_scan_enabled }
|
||||
let(:action2) { :secure_dependency_scanning_run }
|
||||
let(:actions) { [action1, action2] }
|
||||
|
||||
subject(:register_action) { described_class.register(namespace, actions) }
|
||||
|
||||
context 'when the namespace was onboarded' do
|
||||
before do
|
||||
described_class.onboard(namespace)
|
||||
end
|
||||
|
||||
it 'registers the actions for the namespace' do
|
||||
expect { register_action }.to change {
|
||||
[described_class.completed?(namespace, action1), described_class.completed?(namespace, action2)]
|
||||
}.from([false, false]).to([true, true])
|
||||
end
|
||||
|
||||
it 'does not override timestamp', :aggregate_failures do
|
||||
described_class.register(namespace, [action1])
|
||||
expect(described_class.find_by_namespace_id(namespace.id).security_scan_enabled_at).not_to be_nil
|
||||
expect(described_class.find_by_namespace_id(namespace.id).secure_dependency_scanning_run_at).to be_nil
|
||||
|
||||
expect { described_class.register(namespace, [action1, action2]) }.not_to change {
|
||||
described_class.find_by_namespace_id(namespace.id).security_scan_enabled_at
|
||||
}
|
||||
expect(described_class.find_by_namespace_id(namespace.id).secure_dependency_scanning_run_at).not_to be_nil
|
||||
end
|
||||
|
||||
context 'when one of the actions does not exist' do
|
||||
let(:action2) { :foo }
|
||||
|
||||
it 'does not register any action for the namespace' do
|
||||
expect { register_action }.not_to change {
|
||||
[described_class.completed?(namespace, action1), described_class.completed?(namespace, action2)]
|
||||
}.from([false, nil])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the namespace was not onboarded' do
|
||||
it 'does not register the action for the namespace' do
|
||||
expect { register_action }.not_to change { described_class.completed?(namespace, action1) }.from(false)
|
||||
expect { described_class.register(namespace, action) }.not_to change { described_class.completed?(namespace, action2) }.from(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue