Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/ci/runner')
-rw-r--r--app/assets/javascripts/ci/runner/admin_runner_show/admin_runner_show_app.vue16
-rw-r--r--app/assets/javascripts/ci/runner/admin_runners/admin_runners_app.vue8
-rw-r--r--app/assets/javascripts/ci/runner/components/cells/runner_summary_cell.vue18
-rw-r--r--app/assets/javascripts/ci/runner/components/registration/registration_dropdown.vue106
-rw-r--r--app/assets/javascripts/ci/runner/components/registration/registration_token.vue1
-rw-r--r--app/assets/javascripts/ci/runner/components/registration/registration_token_reset_dropdown_item.vue34
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_delete_action.vue126
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_delete_button.vue126
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_delete_disclosure_dropdown_item.vue38
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_delete_modal.vue3
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_detail.vue4
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_edit_button.vue10
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_edit_disclosure_dropdown_item.vue29
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_header.vue46
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_header_actions.vue80
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_list_empty_state.vue54
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_pause_action.vue89
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_pause_button.vue97
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_pause_disclosure_dropdown_item.vue34
-rw-r--r--app/assets/javascripts/ci/runner/constants.js21
-rw-r--r--app/assets/javascripts/ci/runner/graphql/list/list_item_shared.fragment.graphql6
-rw-r--r--app/assets/javascripts/ci/runner/group_runner_show/group_runner_show_app.vue16
-rw-r--r--app/assets/javascripts/ci/runner/group_runners/group_runners_app.vue12
23 files changed, 607 insertions, 367 deletions
diff --git a/app/assets/javascripts/ci/runner/admin_runner_show/admin_runner_show_app.vue b/app/assets/javascripts/ci/runner/admin_runner_show/admin_runner_show_app.vue
index d385d32fd9d..c2ec8462a0e 100644
--- a/app/assets/javascripts/ci/runner/admin_runner_show/admin_runner_show_app.vue
+++ b/app/assets/javascripts/ci/runner/admin_runner_show/admin_runner_show_app.vue
@@ -4,10 +4,8 @@ import { TYPENAME_CI_RUNNER } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import { visitUrl } from '~/lib/utils/url_utility';
-import RunnerDeleteButton from '../components/runner_delete_button.vue';
-import RunnerEditButton from '../components/runner_edit_button.vue';
-import RunnerPauseButton from '../components/runner_pause_button.vue';
import RunnerHeader from '../components/runner_header.vue';
+import RunnerHeaderActions from '../components/runner_header_actions.vue';
import RunnerDetailsTabs from '../components/runner_details_tabs.vue';
import { I18N_FETCH_ERROR } from '../constants';
@@ -18,10 +16,8 @@ import { saveAlertToLocalStorage } from '../local_storage_alert/save_alert_to_lo
export default {
name: 'AdminRunnerShowApp',
components: {
- RunnerDeleteButton,
- RunnerEditButton,
- RunnerPauseButton,
RunnerHeader,
+ RunnerHeaderActions,
RunnerDetailsTabs,
},
props: {
@@ -80,9 +76,11 @@ export default {
<div>
<runner-header v-if="runner" :runner="runner">
<template #actions>
- <runner-edit-button v-if="canUpdate && runner.editAdminUrl" :href="runner.editAdminUrl" />
- <runner-pause-button v-if="canUpdate" :runner="runner" />
- <runner-delete-button v-if="canDelete" :runner="runner" @deleted="onDeleted" />
+ <runner-header-actions
+ :runner="runner"
+ :edit-path="runner.editAdminUrl"
+ @deleted="onDeleted"
+ />
</template>
</runner-header>
<runner-details-tabs v-if="runner" :runner="runner" />
diff --git a/app/assets/javascripts/ci/runner/admin_runners/admin_runners_app.vue b/app/assets/javascripts/ci/runner/admin_runners/admin_runners_app.vue
index 4d88feebe53..2168685e703 100644
--- a/app/assets/javascripts/ci/runner/admin_runners/admin_runners_app.vue
+++ b/app/assets/javascripts/ci/runner/admin_runners/admin_runners_app.vue
@@ -126,10 +126,6 @@ export default {
isSearchFiltered() {
return isSearchFiltered(this.search);
},
- shouldShowCreateRunnerWorkflow() {
- // create_runner_workflow_for_admin feature flag
- return this.glFeatures.createRunnerWorkflowForAdmin;
- },
},
watch: {
search: {
@@ -193,14 +189,14 @@ export default {
/>
<div class="gl-w-full gl-md-w-auto gl-display-flex">
- <gl-button v-if="shouldShowCreateRunnerWorkflow" :href="newRunnerPath" variant="confirm">
+ <gl-button :href="newRunnerPath" variant="confirm">
{{ s__('Runners|New instance runner') }}
</gl-button>
<registration-dropdown
class="gl-ml-3"
:registration-token="registrationToken"
:type="$options.INSTANCE_TYPE"
- right
+ placement="right"
/>
</div>
</div>
diff --git a/app/assets/javascripts/ci/runner/components/cells/runner_summary_cell.vue b/app/assets/javascripts/ci/runner/components/cells/runner_summary_cell.vue
index 9f4ce14f704..cc31afea88c 100644
--- a/app/assets/javascripts/ci/runner/components/cells/runner_summary_cell.vue
+++ b/app/assets/javascripts/ci/runner/components/cells/runner_summary_cell.vue
@@ -1,6 +1,6 @@
<script>
import { GlIcon, GlSprintf, GlTooltipDirective } from '@gitlab/ui';
-import { sprintf, __ } from '~/locale';
+import { sprintf, __, formatNumber } from '~/locale';
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
@@ -49,6 +49,12 @@ export default {
managersCount() {
return this.runner.managers?.count || 0;
},
+ firstIpAddress() {
+ return this.runner.managers?.nodes?.[0]?.ipAddress || null;
+ },
+ additionalIpAddressCount() {
+ return this.managersCount - 1;
+ },
jobCount() {
return formatJobCount(this.runner.jobCount);
},
@@ -63,6 +69,9 @@ export default {
return null;
},
},
+ methods: {
+ formatNumber,
+ },
i18n: {
I18N_NO_DESCRIPTION,
I18N_LOCKED_RUNNER_DESCRIPTION,
@@ -120,8 +129,11 @@ export default {
</gl-sprintf>
</runner-summary-field>
- <runner-summary-field v-if="runner.ipAddress" icon="disk" :tooltip="__('IP Address')">
- {{ runner.ipAddress }}
+ <runner-summary-field v-if="firstIpAddress" icon="disk" :tooltip="__('IP Address')">
+ {{ firstIpAddress }}
+ <template v-if="additionalIpAddressCount"
+ >(+{{ formatNumber(additionalIpAddressCount) }})</template
+ >
</runner-summary-field>
<runner-summary-field icon="pipeline" data-testid="job-count" :tooltip="__('Jobs')">
diff --git a/app/assets/javascripts/ci/runner/components/registration/registration_dropdown.vue b/app/assets/javascripts/ci/runner/components/registration/registration_dropdown.vue
index 2fdf8456615..0154cd2a3ec 100644
--- a/app/assets/javascripts/ci/runner/components/registration/registration_dropdown.vue
+++ b/app/assets/javascripts/ci/runner/components/registration/registration_dropdown.vue
@@ -1,5 +1,11 @@
<script>
-import { GlDropdown, GlDropdownForm, GlDropdownItem, GlDropdownDivider, GlIcon } from '@gitlab/ui';
+import {
+ GlDisclosureDropdown,
+ GlDropdownForm,
+ GlDisclosureDropdownItem,
+ GlDisclosureDropdownGroup,
+ GlIcon,
+} from '@gitlab/ui';
import { s__ } from '~/locale';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import RunnerInstructionsModal from '~/vue_shared/components/runner_instructions/runner_instructions_modal.vue';
@@ -20,12 +26,15 @@ export default {
showInstallationInstructions: s__(
'Runners|Show runner installation and registration instructions',
),
+ supportForRegistrationTokensDeprecated: s__(
+ 'Runners|Support for registration tokens is deprecated',
+ ),
},
components: {
- GlDropdown,
+ GlDisclosureDropdown,
+ GlDisclosureDropdownItem,
+ GlDisclosureDropdownGroup,
GlDropdownForm,
- GlDropdownItem,
- GlDropdownDivider,
GlIcon,
RegistrationToken,
RunnerInstructionsModal,
@@ -51,14 +60,6 @@ export default {
};
},
computed: {
- isDeprecated() {
- // Show a compact version when used as secondary option
- // create_runner_workflow_for_admin or create_runner_workflow_for_namespace
- return (
- this.glFeatures?.createRunnerWorkflowForAdmin ||
- this.glFeatures?.createRunnerWorkflowForNamespace
- );
- },
actionText() {
switch (this.type) {
case INSTANCE_TYPE:
@@ -71,30 +72,6 @@ export default {
return I18N_REGISTER_RUNNER;
}
},
- dropdownText() {
- if (this.isDeprecated) {
- return '';
- }
- return this.actionText;
- },
- dropdownToggleClass() {
- if (this.isDeprecated) {
- return ['gl-px-3!'];
- }
- return [];
- },
- dropdownCategory() {
- if (this.isDeprecated) {
- return 'tertiary';
- }
- return 'primary';
- },
- dropdownVariant() {
- if (this.isDeprecated) {
- return 'default';
- }
- return 'confirm';
- },
},
methods: {
onShowInstructionsClick() {
@@ -103,46 +80,51 @@ export default {
onTokenReset(token) {
this.currentRegistrationToken = token;
- this.$refs.runnerRegistrationDropdown.hide(true);
+ this.$refs.runnerRegistrationDropdown.close();
+ },
+ onCopy() {
+ this.$refs.runnerRegistrationDropdown.close();
},
},
};
</script>
<template>
- <gl-dropdown
+ <gl-disclosure-dropdown
ref="runnerRegistrationDropdown"
- menu-class="gl-w-auto!"
- :text="dropdownText"
- :toggle-class="dropdownToggleClass"
- :variant="dropdownVariant"
- :category="dropdownCategory"
+ :toggle-text="actionText"
+ toggle-class="gl-px-3!"
+ variant="default"
+ category="tertiary"
v-bind="$attrs"
+ icon="ellipsis_v"
+ text-sr-only
+ no-caret
>
- <template v-if="isDeprecated" #button-content>
- <span class="gl-sr-only">{{ actionText }}</span>
- <gl-icon name="ellipsis_v" />
- </template>
<gl-dropdown-form class="gl-p-4!">
- <registration-token input-id="token-value" :value="currentRegistrationToken">
- <template v-if="isDeprecated" #label-description>
+ <registration-token input-id="token-value" :value="currentRegistrationToken" @copy="onCopy">
+ <template #label-description>
<gl-icon name="warning" class="gl-text-orange-500" />
<span class="gl-text-secondary">
- {{ s__('Runners|Support for registration tokens is deprecated') }}
+ {{ $options.i18n.supportForRegistrationTokensDeprecated }}
</span>
</template>
</registration-token>
</gl-dropdown-form>
- <gl-dropdown-divider />
- <gl-dropdown-item @click.capture.native.stop="onShowInstructionsClick">
- {{ $options.i18n.showInstallationInstructions }}
- <runner-instructions-modal
- ref="runnerInstructionsModal"
- :registration-token="currentRegistrationToken"
- data-testid="runner-instructions-modal"
- />
- </gl-dropdown-item>
- <gl-dropdown-divider />
- <registration-token-reset-dropdown-item :type="type" @tokenReset="onTokenReset" />
- </gl-dropdown>
+ <gl-disclosure-dropdown-group bordered>
+ <gl-disclosure-dropdown-item @action="onShowInstructionsClick">
+ <template #list-item>
+ {{ $options.i18n.showInstallationInstructions }}
+ <runner-instructions-modal
+ ref="runnerInstructionsModal"
+ :registration-token="currentRegistrationToken"
+ data-testid="runner-instructions-modal"
+ />
+ </template>
+ </gl-disclosure-dropdown-item>
+ </gl-disclosure-dropdown-group>
+ <gl-disclosure-dropdown-group bordered>
+ <registration-token-reset-dropdown-item :type="type" @tokenReset="onTokenReset" />
+ </gl-disclosure-dropdown-group>
+ </gl-disclosure-dropdown>
</template>
diff --git a/app/assets/javascripts/ci/runner/components/registration/registration_token.vue b/app/assets/javascripts/ci/runner/components/registration/registration_token.vue
index b196bccf66f..339c92a427f 100644
--- a/app/assets/javascripts/ci/runner/components/registration/registration_token.vue
+++ b/app/assets/javascripts/ci/runner/components/registration/registration_token.vue
@@ -31,6 +31,7 @@ export default {
onCopy() {
// value already in the clipboard, simply notify the user
this.$toast?.show(s__('Runners|Registration token copied!'));
+ this.$emit('copy');
},
},
I18N_COPY_BUTTON_TITLE: s__('Runners|Copy registration token'),
diff --git a/app/assets/javascripts/ci/runner/components/registration/registration_token_reset_dropdown_item.vue b/app/assets/javascripts/ci/runner/components/registration/registration_token_reset_dropdown_item.vue
index 6ce88fc54de..47ca3ed6227 100644
--- a/app/assets/javascripts/ci/runner/components/registration/registration_token_reset_dropdown_item.vue
+++ b/app/assets/javascripts/ci/runner/components/registration/registration_token_reset_dropdown_item.vue
@@ -1,5 +1,5 @@
<script>
-import { GlDropdownItem, GlLoadingIcon, GlModal, GlModalDirective } from '@gitlab/ui';
+import { GlDisclosureDropdownItem, GlLoadingIcon, GlModal, GlModalDirective } from '@gitlab/ui';
import { createAlert } from '~/alert';
import { TYPENAME_GROUP, TYPENAME_PROJECT } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
@@ -19,7 +19,7 @@ export default {
name: 'RunnerRegistrationTokenReset',
i18n,
components: {
- GlDropdownItem,
+ GlDisclosureDropdownItem,
GlLoadingIcon,
GlModal,
},
@@ -124,18 +124,20 @@ export default {
};
</script>
<template>
- <gl-dropdown-item v-gl-modal="$options.modalId">
- {{ __('Reset registration token') }}
- <gl-modal
- size="sm"
- :modal-id="$options.modalId"
- :action-primary="actionPrimary"
- :action-secondary="actionSecondary"
- :title="$options.i18n.modalTitle"
- @primary="handleModalPrimary"
- >
- <p>{{ $options.i18n.modalCopy }}</p>
- </gl-modal>
- <gl-loading-icon v-if="loading" inline />
- </gl-dropdown-item>
+ <gl-disclosure-dropdown-item v-gl-modal="$options.modalId">
+ <template #list-item>
+ {{ __('Reset registration token') }}
+ <gl-modal
+ size="sm"
+ :modal-id="$options.modalId"
+ :action-primary="actionPrimary"
+ :action-secondary="actionSecondary"
+ :title="$options.i18n.modalTitle"
+ @primary="handleModalPrimary"
+ >
+ <p>{{ $options.i18n.modalCopy }}</p>
+ </gl-modal>
+ <gl-loading-icon v-if="loading" inline />
+ </template>
+ </gl-disclosure-dropdown-item>
</template>
diff --git a/app/assets/javascripts/ci/runner/components/runner_delete_action.vue b/app/assets/javascripts/ci/runner/components/runner_delete_action.vue
new file mode 100644
index 00000000000..db8133c1ccb
--- /dev/null
+++ b/app/assets/javascripts/ci/runner/components/runner_delete_action.vue
@@ -0,0 +1,126 @@
+<script>
+import runnerDeleteMutation from '~/ci/runner/graphql/shared/runner_delete.mutation.graphql';
+import { createAlert } from '~/alert';
+import { sprintf, s__ } from '~/locale';
+import { captureException } from '~/ci/runner/sentry_utils';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import { I18N_DELETED_TOAST } from '../constants';
+import RunnerDeleteModal from './runner_delete_modal.vue';
+
+/**
+ * Component that wraps a delete GraphQL mutation for the
+ * runner, given its id.
+ *
+ * You can use the slot to define a presentation for the
+ * delete action, like a button or dropdown item.
+ *
+ * Usage:
+ *
+ * ```vue
+ * <runner-delete-action
+ * #default="{ loading, onClick }"
+ * :runner="runner"
+ * @done="onDeleted"
+ * >
+ * <button :disabled="loading" @click="onClick"> Delete! </button>
+ * </runner-pause-action>
+ * ```
+ *
+ */
+export default {
+ name: 'RunnerDeleteAction',
+ components: {
+ RunnerDeleteModal,
+ },
+ props: {
+ runner: {
+ type: Object,
+ required: true,
+ validator: (runner) => {
+ return runner?.id && runner?.shortSha;
+ },
+ },
+ },
+ emits: ['done'],
+ data() {
+ return {
+ loading: false,
+ };
+ },
+ computed: {
+ runnerId() {
+ return getIdFromGraphQLId(this.runner.id);
+ },
+ runnerName() {
+ return `#${this.runnerId} (${this.runner.shortSha})`;
+ },
+ runnerManagersCount() {
+ return this.runner.managers?.count || 0;
+ },
+ runnerDeleteModalId() {
+ return `delete-runner-modal-${this.runnerId}`;
+ },
+ },
+ methods: {
+ onClick() {
+ this.$refs.modal.show();
+ },
+ async onDelete() {
+ // "loading" stays "true" until this row is removed,
+ // should only change back if the operation fails.
+ this.loading = true;
+ try {
+ await this.$apollo.mutate({
+ mutation: runnerDeleteMutation,
+ variables: {
+ input: {
+ id: this.runner.id,
+ },
+ },
+ update: (cache, { data }) => {
+ const { errors } = data.runnerDelete;
+
+ if (errors?.length) {
+ this.onError(new Error(errors.join(' ')));
+ return;
+ }
+
+ this.$emit('done', {
+ message: sprintf(I18N_DELETED_TOAST, { name: this.runnerName }),
+ });
+
+ // Remove deleted runner from the cache
+ const cacheId = cache.identify(this.runner);
+ cache.evict({ id: cacheId });
+ cache.gc();
+ },
+ });
+ } catch (e) {
+ this.onError(e);
+ }
+ },
+ onError(error) {
+ this.loading = false;
+ const { message } = error;
+ const title = sprintf(s__('Runners|Runner %{runnerName} failed to delete'), {
+ runnerName: this.runnerName,
+ });
+
+ createAlert({ title, message });
+ captureException({ error, component: this.$options.name });
+ },
+ },
+};
+</script>
+<template>
+ <div>
+ <slot :loading="loading" :on-click="onClick"></slot>
+ <runner-delete-modal
+ ref="modal"
+ :modal-id="runnerDeleteModalId"
+ :runner-name="runnerName"
+ :managers-count="runnerManagersCount"
+ @primary="onDelete"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/ci/runner/components/runner_delete_button.vue b/app/assets/javascripts/ci/runner/components/runner_delete_button.vue
index 3560521e8d7..d228a022032 100644
--- a/app/assets/javascripts/ci/runner/components/runner_delete_button.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_delete_button.vue
@@ -1,30 +1,21 @@
<script>
-import { GlButton, GlModalDirective, GlTooltipDirective } from '@gitlab/ui';
-import runnerDeleteMutation from '~/ci/runner/graphql/shared/runner_delete.mutation.graphql';
-import { createAlert } from '~/alert';
-import { sprintf, s__ } from '~/locale';
-import { captureException } from '~/ci/runner/sentry_utils';
-import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import { I18N_DELETE_RUNNER, I18N_DELETED_TOAST } from '../constants';
-import RunnerDeleteModal from './runner_delete_modal.vue';
+import { GlButton, GlTooltipDirective } from '@gitlab/ui';
+import { I18N_DELETE_RUNNER } from '../constants';
+import RunnerDeleteAction from './runner_delete_action.vue';
export default {
name: 'RunnerDeleteButton',
components: {
GlButton,
- RunnerDeleteModal,
+ RunnerDeleteAction,
},
directives: {
GlTooltip: GlTooltipDirective,
- GlModal: GlModalDirective,
},
props: {
runner: {
type: Object,
required: true,
- validator: (runner) => {
- return runner?.id && runner?.shortSha;
- },
},
compact: {
type: Boolean,
@@ -39,17 +30,11 @@ export default {
};
},
computed: {
- runnerId() {
- return getIdFromGraphQLId(this.runner.id);
- },
- runnerName() {
- return `#${this.runnerId} (${this.runner.shortSha})`;
- },
- runnerManagersCount() {
- return this.runner.managers?.count || 0;
- },
- runnerDeleteModalId() {
- return `delete-runner-modal-${this.runnerId}`;
+ buttonContent() {
+ if (this.compact) {
+ return null;
+ }
+ return I18N_DELETE_RUNNER;
},
icon() {
if (this.compact) {
@@ -57,12 +42,6 @@ export default {
}
return '';
},
- buttonContent() {
- if (this.compact) {
- return null;
- }
- return I18N_DELETE_RUNNER;
- },
buttonClass() {
// Ensure a square button is shown when compact: true.
// Without this class we will have distorted/rectangular button.
@@ -78,83 +57,36 @@ export default {
return null;
},
tooltip() {
- // Only show basic "delete" tooltip when compact.
- // Also prevent a "sticky" tooltip: If this button is
- // loading, mouseout listeners don't run leaving the tooltip stuck
- if (this.compact && !this.deleting) {
+ if (this.compact) {
return I18N_DELETE_RUNNER;
}
return '';
},
},
methods: {
- async onDelete() {
- // Deleting stays "true" until this row is removed,
- // should only change back if the operation fails.
- this.deleting = true;
- try {
- await this.$apollo.mutate({
- mutation: runnerDeleteMutation,
- variables: {
- input: {
- id: this.runner.id,
- },
- },
- update: (cache, { data }) => {
- const { errors } = data.runnerDelete;
-
- if (errors?.length) {
- this.onError(new Error(errors.join(' ')));
- return;
- }
-
- this.$emit('deleted', {
- message: sprintf(I18N_DELETED_TOAST, { name: this.runnerName }),
- });
-
- // Remove deleted runner from the cache
- const cacheId = cache.identify(this.runner);
- cache.evict({ id: cacheId });
- cache.gc();
- },
- });
- } catch (e) {
- this.onError(e);
- }
- },
- onError(error) {
- this.deleting = false;
- const { message } = error;
- const title = sprintf(s__('Runners|Runner %{runnerName} failed to delete'), {
- runnerName: this.runnerName,
- });
-
- createAlert({ title, message });
- captureException({ error, component: this.$options.name });
+ onDone(event) {
+ this.$emit('deleted', event);
},
},
};
</script>
<template>
- <div v-gl-tooltip="tooltip" class="btn-group">
- <gl-button
- v-gl-modal="runnerDeleteModalId"
- :aria-label="ariaLabel"
- :icon="icon"
- :class="buttonClass"
- :loading="deleting"
- variant="danger"
- category="secondary"
- v-bind="$attrs"
- >
- {{ buttonContent }}
- </gl-button>
- <runner-delete-modal
- :modal-id="runnerDeleteModalId"
- :runner-name="runnerName"
- :managers-count="runnerManagersCount"
- @primary="onDelete"
- />
- </div>
+ <runner-delete-action class="btn-group" :runner="runner" @done="onDone">
+ <template #default="{ loading, onClick }">
+ <gl-button
+ v-gl-tooltip="loading ? '' : tooltip"
+ :aria-label="ariaLabel"
+ :icon="icon"
+ :class="buttonClass"
+ :loading="loading"
+ variant="danger"
+ category="secondary"
+ v-bind="$attrs"
+ @click="onClick"
+ >
+ {{ buttonContent }}
+ </gl-button>
+ </template>
+ </runner-delete-action>
</template>
diff --git a/app/assets/javascripts/ci/runner/components/runner_delete_disclosure_dropdown_item.vue b/app/assets/javascripts/ci/runner/components/runner_delete_disclosure_dropdown_item.vue
new file mode 100644
index 00000000000..0a81974a6d0
--- /dev/null
+++ b/app/assets/javascripts/ci/runner/components/runner_delete_disclosure_dropdown_item.vue
@@ -0,0 +1,38 @@
+<script>
+import { GlDisclosureDropdownItem } from '@gitlab/ui';
+import { I18N_DELETE } from '../constants';
+import RunnerDeleteAction from './runner_delete_action.vue';
+
+export default {
+ name: 'RunnerDeleteDisclosureDropdownItem',
+ components: {
+ GlDisclosureDropdownItem,
+ RunnerDeleteAction,
+ },
+ props: {
+ runner: {
+ type: Object,
+ required: true,
+ },
+ },
+ emits: ['deleted'],
+ methods: {
+ onDone(event) {
+ this.$emit('deleted', event);
+ },
+ },
+ I18N_DELETE,
+};
+</script>
+
+<template>
+ <runner-delete-action :runner="runner" @done="onDone">
+ <template #default="{ onClick }">
+ <gl-disclosure-dropdown-item @action="onClick">
+ <template #list-item>
+ <span class="gl-text-red-500">{{ $options.I18N_DELETE }}</span>
+ </template>
+ </gl-disclosure-dropdown-item>
+ </template>
+ </runner-delete-action>
+</template>
diff --git a/app/assets/javascripts/ci/runner/components/runner_delete_modal.vue b/app/assets/javascripts/ci/runner/components/runner_delete_modal.vue
index 93f79fd67ea..124ac0b4e73 100644
--- a/app/assets/javascripts/ci/runner/components/runner_delete_modal.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_delete_modal.vue
@@ -52,6 +52,9 @@ export default {
},
},
methods: {
+ show() {
+ this.$refs.modal.show();
+ },
onPrimary() {
this.$refs.modal.hide();
},
diff --git a/app/assets/javascripts/ci/runner/components/runner_detail.vue b/app/assets/javascripts/ci/runner/components/runner_detail.vue
index 9e8055a8432..496985ff7ac 100644
--- a/app/assets/javascripts/ci/runner/components/runner_detail.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_detail.vue
@@ -40,12 +40,12 @@ export default {
<template>
<div class="gl-display-contents">
- <dt class="gl-mb-5 gl-mr-6 gl-max-w-26">
+ <dt class="gl-mb-5 gl-mr-6 gl-max-w-26" data-testid="label-slot">
<template v-if="label || $scopedSlots.label">
<slot name="label">{{ label }}</slot>
</template>
</dt>
- <dd class="gl-mb-5">
+ <dd class="gl-mb-5" data-testid="value-slot">
<template v-if="value || $scopedSlots.value">
<slot name="value">{{ value }}</slot>
</template>
diff --git a/app/assets/javascripts/ci/runner/components/runner_edit_button.vue b/app/assets/javascripts/ci/runner/components/runner_edit_button.vue
index 33e0acaf5c0..b4efd72b082 100644
--- a/app/assets/javascripts/ci/runner/components/runner_edit_button.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_edit_button.vue
@@ -9,15 +9,23 @@ export default {
directives: {
GlTooltip: GlTooltipDirective,
},
+ props: {
+ href: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ },
I18N_EDIT,
};
</script>
<template>
<gl-button
+ v-if="href"
v-gl-tooltip="$options.I18N_EDIT"
- v-bind="$attrs"
:aria-label="$options.I18N_EDIT"
+ :href="href"
icon="pencil"
v-on="$listeners"
/>
diff --git a/app/assets/javascripts/ci/runner/components/runner_edit_disclosure_dropdown_item.vue b/app/assets/javascripts/ci/runner/components/runner_edit_disclosure_dropdown_item.vue
new file mode 100644
index 00000000000..d0dcc04c3dc
--- /dev/null
+++ b/app/assets/javascripts/ci/runner/components/runner_edit_disclosure_dropdown_item.vue
@@ -0,0 +1,29 @@
+<script>
+import { GlDisclosureDropdownItem } from '@gitlab/ui';
+
+import { I18N_EDIT } from '../constants';
+
+export default {
+ name: 'RunnerEditDisclosureDropdownItem',
+ components: {
+ GlDisclosureDropdownItem,
+ },
+ props: {
+ href: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ },
+ computed: {
+ item() {
+ return { text: I18N_EDIT, href: this.href };
+ },
+ },
+ I18N_EDIT,
+};
+</script>
+
+<template>
+ <gl-disclosure-dropdown-item v-if="href" :item="item" />
+</template>
diff --git a/app/assets/javascripts/ci/runner/components/runner_header.vue b/app/assets/javascripts/ci/runner/components/runner_header.vue
index f46e894bf2e..55a33ef2074 100644
--- a/app/assets/javascripts/ci/runner/components/runner_header.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_header.vue
@@ -32,31 +32,29 @@ export default {
};
</script>
<template>
- <div
- class="gl-display-flex gl-justify-content-space-between gl-align-items-flex-start gl-gap-3 gl-flex-wrap gl-py-5"
- >
- <div>
+ <div class="gl-py-5">
+ <div class="gl-display-flex gl-justify-content-space-between">
<h1 class="gl-font-size-h-display gl-my-0">{{ name }}</h1>
- <div class="gl-display-flex gl-align-items-flex-start gl-gap-3 gl-flex-wrap gl-mt-3">
- <runner-status-badge :contacted-at="runner.contactedAt" :status="runner.status" />
- <runner-type-badge :type="runner.runnerType" />
- <span v-if="runner.createdAt">
- <gl-sprintf :message="__('%{locked} created %{timeago}')">
- <template #locked>
- <gl-icon
- v-if="runner.locked"
- v-gl-tooltip="$options.I18N_LOCKED_RUNNER_DESCRIPTION"
- name="lock"
- :aria-label="$options.I18N_LOCKED_RUNNER_DESCRIPTION"
- />
- </template>
- <template #timeago>
- <time-ago :time="runner.createdAt" />
- </template>
- </gl-sprintf>
- </span>
- </div>
+ <slot name="actions"></slot>
+ </div>
+ <div class="gl-display-flex gl-align-items-flex-start gl-gap-3 gl-flex-wrap gl-mt-3">
+ <runner-status-badge :contacted-at="runner.contactedAt" :status="runner.status" />
+ <runner-type-badge :type="runner.runnerType" />
+ <span v-if="runner.createdAt">
+ <gl-sprintf :message="__('%{locked} created %{timeago}')">
+ <template #locked>
+ <gl-icon
+ v-if="runner.locked"
+ v-gl-tooltip="$options.I18N_LOCKED_RUNNER_DESCRIPTION"
+ name="lock"
+ :aria-label="$options.I18N_LOCKED_RUNNER_DESCRIPTION"
+ />
+ </template>
+ <template #timeago>
+ <time-ago :time="runner.createdAt" />
+ </template>
+ </gl-sprintf>
+ </span>
</div>
- <div class="gl-display-flex gl-gap-3 gl-flex-wrap"><slot name="actions"></slot></div>
</div>
</template>
diff --git a/app/assets/javascripts/ci/runner/components/runner_header_actions.vue b/app/assets/javascripts/ci/runner/components/runner_header_actions.vue
new file mode 100644
index 00000000000..bc6f184bd4d
--- /dev/null
+++ b/app/assets/javascripts/ci/runner/components/runner_header_actions.vue
@@ -0,0 +1,80 @@
+<script>
+import { GlDisclosureDropdown } from '@gitlab/ui';
+
+import RunnerDeleteButton from './runner_delete_button.vue';
+import RunnerEditButton from './runner_edit_button.vue';
+import RunnerPauseButton from './runner_pause_button.vue';
+
+import RunnerEditDisclosureDropdownItem from './runner_edit_disclosure_dropdown_item.vue';
+import RunnerPauseDisclosureDropdownItem from './runner_pause_disclosure_dropdown_item.vue';
+import RunnerDeleteDisclosureDropdownItem from './runner_delete_disclosure_dropdown_item.vue';
+
+export default {
+ name: 'RunnerHeaderActions',
+ components: {
+ GlDisclosureDropdown,
+
+ RunnerDeleteButton,
+ RunnerEditButton,
+ RunnerPauseButton,
+
+ RunnerEditDisclosureDropdownItem,
+ RunnerPauseDisclosureDropdownItem,
+ RunnerDeleteDisclosureDropdownItem,
+ },
+ props: {
+ runner: {
+ type: Object,
+ required: true,
+ },
+ editPath: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ },
+ computed: {
+ canUpdate() {
+ return this.runner.userPermissions?.updateRunner;
+ },
+ canDelete() {
+ return this.runner.userPermissions?.deleteRunner;
+ },
+ },
+ methods: {
+ onDeleted(event) {
+ this.$emit('deleted', event);
+ },
+ },
+};
+</script>
+
+<template>
+ <div v-if="canUpdate || canDelete">
+ <!-- sm and up screens -->
+ <div class="gl-display-none gl-sm-display-flex gl-gap-3">
+ <runner-edit-button v-if="canUpdate" :href="editPath" />
+ <runner-pause-button v-if="canUpdate" :runner="runner" />
+ <runner-delete-button v-if="canDelete" :runner="runner" @deleted="onDeleted" />
+ </div>
+
+ <!-- xs screens -->
+ <div class="gl-sm-display-none">
+ <gl-disclosure-dropdown
+ icon="ellipsis_v"
+ :toggle-text="s__('Runner|Runner actions')"
+ text-sr-only
+ category="tertiary"
+ no-caret
+ >
+ <runner-edit-disclosure-dropdown-item v-if="canUpdate" :href="editPath" />
+ <runner-pause-disclosure-dropdown-item v-if="canUpdate" :runner="runner" />
+ <runner-delete-disclosure-dropdown-item
+ v-if="canDelete"
+ :runner="runner"
+ @deleted="onDeleted"
+ />
+ </gl-disclosure-dropdown>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/ci/runner/components/runner_list_empty_state.vue b/app/assets/javascripts/ci/runner/components/runner_list_empty_state.vue
index d2836962a97..a4a489074c3 100644
--- a/app/assets/javascripts/ci/runner/components/runner_list_empty_state.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_list_empty_state.vue
@@ -11,7 +11,6 @@ import {
I18N_CREATE_RUNNER_LINK,
I18N_STILL_USING_REGISTRATION_TOKENS,
I18N_CONTACT_ADMIN_TO_REGISTER,
- I18N_FOLLOW_REGISTRATION_INSTRUCTIONS,
I18N_NO_RESULTS,
I18N_EDIT_YOUR_SEARCH,
} from '~/ci/runner/constants';
@@ -44,15 +43,6 @@ export default {
default: null,
},
},
- computed: {
- shouldShowCreateRunnerWorkflow() {
- // create_runner_workflow_for_admin or create_runner_workflow_for_namespace
- return (
- this.glFeatures?.createRunnerWorkflowForAdmin ||
- this.glFeatures?.createRunnerWorkflowForNamespace
- );
- },
- },
modalId: 'runners-empty-state-instructions-modal',
svgHeight: 145,
EMPTY_STATE_SVG_URL,
@@ -63,7 +53,6 @@ export default {
I18N_CREATE_RUNNER_LINK,
I18N_STILL_USING_REGISTRATION_TOKENS,
I18N_CONTACT_ADMIN_TO_REGISTER,
- I18N_FOLLOW_REGISTRATION_INSTRUCTIONS,
I18N_NO_RESULTS,
I18N_EDIT_YOUR_SEARCH,
};
@@ -85,39 +74,22 @@ export default {
>
<template #description>
{{ $options.I18N_RUNNERS_ARE_AGENTS }}
- <template v-if="shouldShowCreateRunnerWorkflow">
- <gl-sprintf v-if="newRunnerPath" :message="$options.I18N_CREATE_RUNNER_LINK">
- <template #link="{ content }">
- <gl-link :href="newRunnerPath">{{ content }}</gl-link>
- </template>
- </gl-sprintf>
- <template v-if="registrationToken">
- <br />
- <gl-link v-gl-modal="$options.modalId">{{
- $options.I18N_STILL_USING_REGISTRATION_TOKENS
- }}</gl-link>
- <runner-instructions-modal
- :modal-id="$options.modalId"
- :registration-token="registrationToken"
- />
- </template>
- <template v-if="!newRunnerPath && !registrationToken">
- {{ $options.I18N_CONTACT_ADMIN_TO_REGISTER }}
- </template>
- </template>
- <gl-sprintf
- v-else-if="registrationToken"
- :message="$options.I18N_FOLLOW_REGISTRATION_INSTRUCTIONS"
- >
+ <gl-sprintf v-if="newRunnerPath" :message="$options.I18N_CREATE_RUNNER_LINK">
<template #link="{ content }">
- <gl-link v-gl-modal="$options.modalId">{{ content }}</gl-link>
- <runner-instructions-modal
- :modal-id="$options.modalId"
- :registration-token="registrationToken"
- />
+ <gl-link :href="newRunnerPath">{{ content }}</gl-link>
</template>
</gl-sprintf>
- <template v-else>
+ <template v-if="registrationToken">
+ <br />
+ <gl-link v-gl-modal="$options.modalId">{{
+ $options.I18N_STILL_USING_REGISTRATION_TOKENS
+ }}</gl-link>
+ <runner-instructions-modal
+ :modal-id="$options.modalId"
+ :registration-token="registrationToken"
+ />
+ </template>
+ <template v-if="!newRunnerPath && !registrationToken">
{{ $options.I18N_CONTACT_ADMIN_TO_REGISTER }}
</template>
</template>
diff --git a/app/assets/javascripts/ci/runner/components/runner_pause_action.vue b/app/assets/javascripts/ci/runner/components/runner_pause_action.vue
new file mode 100644
index 00000000000..184d6a83381
--- /dev/null
+++ b/app/assets/javascripts/ci/runner/components/runner_pause_action.vue
@@ -0,0 +1,89 @@
+<script>
+import runnerTogglePausedMutation from '~/ci/runner/graphql/shared/runner_toggle_paused.mutation.graphql';
+import { createAlert } from '~/alert';
+import { captureException } from '~/ci/runner/sentry_utils';
+
+/**
+ * Renderless component that wraps a GraphQL pause mutation for the
+ * runner, given its id and current "paused" value.
+ *
+ * You can use the slot to define a presentation for the delete action,
+ * like a button or dropdown item.
+
+ * Usage:
+ *
+ * ```vue
+ * <runner-pause-action
+ * #default="{ loading, onClick }"
+ * :runner="runner"
+ * @done="onToggled"
+ * >
+ * <button :disabled="loading" @click="onClick">{{ runner.paused ? 'Go!' : 'Stop!' }}</button>
+ * </runner-pause-action>
+ * ```
+ *
+ */
+export default {
+ name: 'RunnerPauseAction',
+ props: {
+ runner: {
+ type: Object,
+ required: true,
+ },
+ compact: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ emits: ['done'],
+ data() {
+ return {
+ loading: false,
+ };
+ },
+ methods: {
+ async onClick() {
+ this.loading = true;
+ try {
+ const input = {
+ id: this.runner.id,
+ paused: !this.runner.paused,
+ };
+
+ const {
+ data: {
+ runnerUpdate: { errors },
+ },
+ } = await this.$apollo.mutate({
+ mutation: runnerTogglePausedMutation,
+ variables: {
+ input,
+ },
+ });
+
+ if (errors && errors.length) {
+ throw new Error(errors.join(' '));
+ }
+ this.$emit('done');
+ } catch (e) {
+ this.onError(e);
+ } finally {
+ this.loading = false;
+ }
+ },
+ onError(error) {
+ const { message } = error;
+
+ createAlert({ message });
+ captureException({ error, component: this.$options.name });
+ },
+ },
+ render() {
+ return this.$scopedSlots.default({
+ onClick: this.onClick,
+ loading: this.loading,
+ });
+ },
+};
+</script>
diff --git a/app/assets/javascripts/ci/runner/components/runner_pause_button.vue b/app/assets/javascripts/ci/runner/components/runner_pause_button.vue
index d16c8f98bad..15bb54027c7 100644
--- a/app/assets/javascripts/ci/runner/components/runner_pause_button.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_pause_button.vue
@@ -1,14 +1,14 @@
<script>
import { GlButton, GlTooltipDirective } from '@gitlab/ui';
-import runnerTogglePausedMutation from '~/ci/runner/graphql/shared/runner_toggle_paused.mutation.graphql';
-import { createAlert } from '~/alert';
-import { captureException } from '~/ci/runner/sentry_utils';
-import { I18N_PAUSE, I18N_PAUSE_TOOLTIP, I18N_RESUME, I18N_RESUME_TOOLTIP } from '../constants';
+
+import { I18N_RESUME, I18N_PAUSE, I18N_PAUSE_TOOLTIP, I18N_RESUME_TOOLTIP } from '../constants';
+import RunnerPauseAction from './runner_pause_action.vue';
export default {
name: 'RunnerPauseButton',
components: {
GlButton,
+ RunnerPauseAction,
},
directives: {
GlTooltip: GlTooltipDirective,
@@ -25,96 +25,47 @@ export default {
},
},
emits: ['toggledPaused'],
- data() {
- return {
- updating: false,
- };
- },
computed: {
isPaused() {
return this.runner.paused;
},
+ tooltip() {
+ return this.isPaused ? I18N_RESUME_TOOLTIP : I18N_PAUSE_TOOLTIP;
+ },
icon() {
return this.isPaused ? 'play' : 'pause';
},
label() {
return this.isPaused ? I18N_RESUME : I18N_PAUSE;
},
- buttonContent() {
- if (this.compact) {
- return null;
- }
- return this.label;
- },
ariaLabel() {
if (this.compact) {
return this.label;
}
return null;
},
- tooltip() {
- // Prevent a "sticky" tooltip: If this button is disabled,
- // mouseout listeners don't run leaving the tooltip stuck
- if (!this.updating) {
- return this.isPaused ? I18N_RESUME_TOOLTIP : I18N_PAUSE_TOOLTIP;
- }
- return '';
- },
- },
- methods: {
- async onToggle() {
- this.updating = true;
- try {
- const input = {
- id: this.runner.id,
- paused: !this.isPaused,
- };
-
- const {
- data: {
- runnerUpdate: { errors },
- },
- } = await this.$apollo.mutate({
- mutation: runnerTogglePausedMutation,
- variables: {
- input,
- },
- });
-
- if (errors && errors.length) {
- throw new Error(errors.join(' '));
- }
- this.$emit('toggledPaused');
- } catch (e) {
- this.onError(e);
- } finally {
- this.updating = false;
+ buttonContent() {
+ if (this.compact) {
+ return null;
}
- },
- onError(error) {
- const { message } = error;
-
- createAlert({ message });
- captureException({ error, component: this.$options.name });
+ return this.label;
},
},
};
</script>
<template>
- <gl-button
- v-gl-tooltip="tooltip"
- v-bind="$attrs"
- :aria-label="ariaLabel"
- :icon="icon"
- :loading="updating"
- @click="onToggle"
- v-on="$listeners"
- >
- <!--
- Use <template v-if> to ensure a square button is shown when compact: true.
- Sending empty content will still show a distorted/rectangular button.
- -->
- <template v-if="buttonContent">{{ buttonContent }}</template>
- </gl-button>
+ <runner-pause-action :runner="runner" @done="$emit('toggledPaused')">
+ <template #default="{ loading, onClick }">
+ <gl-button
+ v-gl-tooltip="loading ? '' : tooltip"
+ :icon="icon"
+ :aria-label="ariaLabel"
+ :loading="loading"
+ @click="onClick"
+ >
+ <template v-if="buttonContent">{{ buttonContent }}</template>
+ </gl-button>
+ </template>
+ </runner-pause-action>
</template>
diff --git a/app/assets/javascripts/ci/runner/components/runner_pause_disclosure_dropdown_item.vue b/app/assets/javascripts/ci/runner/components/runner_pause_disclosure_dropdown_item.vue
new file mode 100644
index 00000000000..3dd5e227a4a
--- /dev/null
+++ b/app/assets/javascripts/ci/runner/components/runner_pause_disclosure_dropdown_item.vue
@@ -0,0 +1,34 @@
+<script>
+import { GlDisclosureDropdownItem } from '@gitlab/ui';
+
+import { I18N_RESUME, I18N_PAUSE } from '../constants';
+import RunnerPauseAction from './runner_pause_action.vue';
+
+export default {
+ name: 'RunnerPauseDisclosureDropdownItem',
+ components: {
+ GlDisclosureDropdownItem,
+ RunnerPauseAction,
+ },
+ props: {
+ runner: {
+ type: Object,
+ required: true,
+ },
+ },
+ emits: ['toggledPaused'],
+ computed: {
+ item() {
+ return { text: this.runner.paused ? I18N_RESUME : I18N_PAUSE };
+ },
+ },
+};
+</script>
+
+<template>
+ <runner-pause-action :runner="runner" @done="$emit('toggledPaused')">
+ <template #default="{ onClick }">
+ <gl-disclosure-dropdown-item :item="item" @action="onClick" />
+ </template>
+ </runner-pause-action>
+</template>
diff --git a/app/assets/javascripts/ci/runner/constants.js b/app/assets/javascripts/ci/runner/constants.js
index 40841696ead..203f97876de 100644
--- a/app/assets/javascripts/ci/runner/constants.js
+++ b/app/assets/javascripts/ci/runner/constants.js
@@ -1,4 +1,5 @@
import { __, s__ } from '~/locale';
+import { DOCS_URL } from 'jh_else_ce/lib/utils/url_utility';
export const RUNNER_TYPENAME = 'CiRunner'; // __typename
@@ -90,6 +91,7 @@ export const I18N_PAUSED_DESCRIPTION = s__('Runners|Not accepting jobs');
export const I18N_RESUME = __('Resume');
export const I18N_RESUME_TOOLTIP = s__('Runners|Resume accepting jobs');
+export const I18N_DELETE = s__('Runners|Delete');
export const I18N_DELETE_RUNNER = s__('Runners|Delete runner');
export const I18N_DELETED_TOAST = s__('Runners|Runner %{name} was deleted');
@@ -117,9 +119,6 @@ export const I18N_STILL_USING_REGISTRATION_TOKENS = s__('Runners|Still using reg
export const I18N_CONTACT_ADMIN_TO_REGISTER = s__(
'Runners|To register new runners, contact your administrator.',
);
-export const I18N_FOLLOW_REGISTRATION_INSTRUCTIONS = s__(
- 'Runners|Follow the %{linkStart}installation and registration instructions%{linkEnd} to set up a runner.',
-);
// No runners found
export const I18N_NO_RESULTS = s__('Runners|No results found');
@@ -271,12 +270,10 @@ export const DEFAULT_PLATFORM = LINUX_PLATFORM;
// Runner docs are in a separate repository and are not shipped with GitLab
// they are rendered as external URLs.
-export const INSTALL_HELP_URL = 'https://docs.gitlab.com/runner/install';
-export const EXECUTORS_HELP_URL = 'https://docs.gitlab.com/runner/executors/';
-export const SERVICE_COMMANDS_HELP_URL =
- 'https://docs.gitlab.com/runner/commands/#service-related-commands';
-export const CHANGELOG_URL = 'https://gitlab.com/gitlab-org/gitlab-runner/blob/main/CHANGELOG.md';
-export const DOCKER_HELP_URL = 'https://docs.gitlab.com/runner/install/docker.html';
-export const KUBERNETES_HELP_URL = 'https://docs.gitlab.com/runner/install/kubernetes.html';
-export const RUNNER_MANAGERS_HELP_URL =
- 'https://docs.gitlab.com/runner/fleet_scaling/#workers-executors-and-autoscaling-capabilities';
+export const INSTALL_HELP_URL = `${DOCS_URL}/runner/install`;
+export const EXECUTORS_HELP_URL = `${DOCS_URL}/runner/executors/`;
+export const SERVICE_COMMANDS_HELP_URL = `${DOCS_URL}/runner/commands/#service-related-commands`;
+export const CHANGELOG_URL = `https://gitlab.com/gitlab-org/gitlab-runner/blob/main/CHANGELOG.md`;
+export const DOCKER_HELP_URL = `${DOCS_URL}/runner/install/docker.html`;
+export const KUBERNETES_HELP_URL = `${DOCS_URL}/runner/install/kubernetes.html`;
+export const RUNNER_MANAGERS_HELP_URL = `${DOCS_URL}/runner/fleet_scaling/#workers-executors-and-autoscaling-capabilities`;
diff --git a/app/assets/javascripts/ci/runner/graphql/list/list_item_shared.fragment.graphql b/app/assets/javascripts/ci/runner/graphql/list/list_item_shared.fragment.graphql
index c0b888e758b..7ad9605d0a4 100644
--- a/app/assets/javascripts/ci/runner/graphql/list/list_item_shared.fragment.graphql
+++ b/app/assets/javascripts/ci/runner/graphql/list/list_item_shared.fragment.graphql
@@ -6,7 +6,6 @@ fragment ListItemShared on CiRunner {
runnerType
shortSha
version
- ipAddress
paused
locked
jobCount
@@ -22,8 +21,11 @@ fragment ListItemShared on CiRunner {
updateRunner
deleteRunner
}
- managers {
+ managers(first: 1) {
count
+ nodes {
+ ipAddress
+ }
}
groups(first: 1) {
nodes {
diff --git a/app/assets/javascripts/ci/runner/group_runner_show/group_runner_show_app.vue b/app/assets/javascripts/ci/runner/group_runner_show/group_runner_show_app.vue
index e885cf45c5a..4b570db772f 100644
--- a/app/assets/javascripts/ci/runner/group_runner_show/group_runner_show_app.vue
+++ b/app/assets/javascripts/ci/runner/group_runner_show/group_runner_show_app.vue
@@ -4,10 +4,8 @@ import { TYPENAME_CI_RUNNER } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import { visitUrl } from '~/lib/utils/url_utility';
-import RunnerDeleteButton from '../components/runner_delete_button.vue';
-import RunnerEditButton from '../components/runner_edit_button.vue';
-import RunnerPauseButton from '../components/runner_pause_button.vue';
import RunnerHeader from '../components/runner_header.vue';
+import RunnerHeaderActions from '../components/runner_header_actions.vue';
import RunnerDetailsTabs from '../components/runner_details_tabs.vue';
import { I18N_FETCH_ERROR } from '../constants';
@@ -18,10 +16,8 @@ import { saveAlertToLocalStorage } from '../local_storage_alert/save_alert_to_lo
export default {
name: 'GroupRunnerShowApp',
components: {
- RunnerDeleteButton,
- RunnerEditButton,
- RunnerPauseButton,
RunnerHeader,
+ RunnerHeaderActions,
RunnerDetailsTabs,
},
props: {
@@ -85,9 +81,11 @@ export default {
<div>
<runner-header v-if="runner" :runner="runner">
<template #actions>
- <runner-edit-button v-if="canUpdate && editGroupRunnerPath" :href="editGroupRunnerPath" />
- <runner-pause-button v-if="canUpdate" :runner="runner" />
- <runner-delete-button v-if="canDelete" :runner="runner" @deleted="onDeleted" />
+ <runner-header-actions
+ :runner="runner"
+ :edit-path="editGroupRunnerPath"
+ @deleted="onDeleted"
+ />
</template>
</runner-header>
diff --git a/app/assets/javascripts/ci/runner/group_runners/group_runners_app.vue b/app/assets/javascripts/ci/runner/group_runners/group_runners_app.vue
index 74523bc335f..71584c40a38 100644
--- a/app/assets/javascripts/ci/runner/group_runners/group_runners_app.vue
+++ b/app/assets/javascripts/ci/runner/group_runners/group_runners_app.vue
@@ -155,10 +155,6 @@ export default {
isSearchFiltered() {
return isSearchFiltered(this.search);
},
- shouldShowCreateRunnerWorkflow() {
- // create_runner_workflow_for_namespace feature flag
- return this.glFeatures.createRunnerWorkflowForNamespace;
- },
},
watch: {
search: {
@@ -231,11 +227,7 @@ export default {
/>
<div class="gl-w-full gl-md-w-auto gl-display-flex">
- <gl-button
- v-if="shouldShowCreateRunnerWorkflow && newRunnerPath"
- :href="newRunnerPath"
- variant="confirm"
- >
+ <gl-button v-if="newRunnerPath" :href="newRunnerPath" variant="confirm">
{{ s__('Runners|New group runner') }}
</gl-button>
<registration-dropdown
@@ -243,7 +235,7 @@ export default {
class="gl-ml-3"
:registration-token="registrationToken"
:type="$options.GROUP_TYPE"
- right
+ placement="right"
/>
</div>
</div>