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:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-06-20 13:43:29 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-06-20 13:43:29 +0300
commit3b1af5cc7ed2666ff18b718ce5d30fa5a2756674 (patch)
tree3bc4a40e0ee51ec27eabf917c537033c0c5b14d4 /app/assets/javascripts/ci/runner
parent9bba14be3f2c211bf79e15769cd9b77bc73a13bc (diff)
Add latest changes from gitlab-org/gitlab@16-1-stable-eev16.1.0-rc42
Diffstat (limited to 'app/assets/javascripts/ci/runner')
-rw-r--r--app/assets/javascripts/ci/runner/admin_new_runner/admin_new_runner_app.vue6
-rw-r--r--app/assets/javascripts/ci/runner/admin_runner_show/admin_runner_show_app.vue4
-rw-r--r--app/assets/javascripts/ci/runner/components/cells/runner_status_cell.vue11
-rw-r--r--app/assets/javascripts/ci/runner/components/cells/runner_summary_cell.vue9
-rw-r--r--app/assets/javascripts/ci/runner/components/registration/platforms_drawer.vue6
-rw-r--r--app/assets/javascripts/ci/runner/components/registration/registration_feedback_banner.vue2
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_create_form.vue41
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_delete_button.vue4
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_delete_modal.vue49
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_details.vue44
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_form_fields.vue214
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_header.vue38
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_jobs_empty_state.vue8
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_list_empty_state.vue79
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_managers_badge.vue47
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_managers_detail.vue111
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_managers_table.vue75
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_name.vue12
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_pause_button.vue16
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_status_badge.vue20
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_update_form.vue143
-rw-r--r--app/assets/javascripts/ci/runner/constants.js26
-rw-r--r--app/assets/javascripts/ci/runner/graphql/edit/runner_fields_shared.fragment.graphql2
-rw-r--r--app/assets/javascripts/ci/runner/graphql/list/list_item_shared.fragment.graphql5
-rw-r--r--app/assets/javascripts/ci/runner/graphql/shared/runner_toggle_paused.mutation.graphql (renamed from app/assets/javascripts/ci/runner/graphql/shared/runner_toggle_active.mutation.graphql)4
-rw-r--r--app/assets/javascripts/ci/runner/graphql/show/runner_details_shared.fragment.graphql5
-rw-r--r--app/assets/javascripts/ci/runner/graphql/show/runner_manager.fragment.graphql5
-rw-r--r--app/assets/javascripts/ci/runner/graphql/show/runner_manager_shared.fragment.graphql12
-rw-r--r--app/assets/javascripts/ci/runner/graphql/show/runner_managers.query.graphql13
-rw-r--r--app/assets/javascripts/ci/runner/group_new_runner/group_new_runner_app.vue6
-rw-r--r--app/assets/javascripts/ci/runner/group_runner_show/group_runner_show_app.vue4
-rw-r--r--app/assets/javascripts/ci/runner/project_new_runner/project_new_runner_app.vue6
-rw-r--r--app/assets/javascripts/ci/runner/runner_update_form_utils.js4
-rw-r--r--app/assets/javascripts/ci/runner/utils.js11
34 files changed, 707 insertions, 335 deletions
diff --git a/app/assets/javascripts/ci/runner/admin_new_runner/admin_new_runner_app.vue b/app/assets/javascripts/ci/runner/admin_new_runner/admin_new_runner_app.vue
index e4d47fba464..f0a41a5949e 100644
--- a/app/assets/javascripts/ci/runner/admin_new_runner/admin_new_runner_app.vue
+++ b/app/assets/javascripts/ci/runner/admin_new_runner/admin_new_runner_app.vue
@@ -1,6 +1,6 @@
<script>
import { createAlert, VARIANT_SUCCESS } from '~/alert';
-import { redirectTo, setUrlParams } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
+import { visitUrl, setUrlParams } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
import RegistrationCompatibilityAlert from '~/ci/runner/components/registration/registration_compatibility_alert.vue';
@@ -32,7 +32,7 @@ export default {
message: s__('Runners|Runner created.'),
variant: VARIANT_SUCCESS,
});
- redirectTo(ephemeralRegisterUrl); // eslint-disable-line import/no-deprecated
+ visitUrl(ephemeralRegisterUrl);
},
onError(error) {
createAlert({ message: error.message });
@@ -60,7 +60,7 @@ export default {
<hr aria-hidden="true" />
- <h2 class="gl-font-weight-normal gl-font-lg gl-my-5">
+ <h2 class="gl-font-size-h2 gl-my-5">
{{ s__('Runners|Platform') }}
</h2>
<runner-platforms-radio-group v-model="platform" />
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 668a55d2437..d385d32fd9d 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
@@ -2,7 +2,7 @@
import { createAlert, VARIANT_SUCCESS } from '~/alert';
import { TYPENAME_CI_RUNNER } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
-import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
+import { visitUrl } from '~/lib/utils/url_utility';
import RunnerDeleteButton from '../components/runner_delete_button.vue';
import RunnerEditButton from '../components/runner_edit_button.vue';
@@ -71,7 +71,7 @@ export default {
},
onDeleted({ message }) {
saveAlertToLocalStorage({ message, variant: VARIANT_SUCCESS });
- redirectTo(this.runnersPath); // eslint-disable-line import/no-deprecated
+ visitUrl(this.runnersPath);
},
},
};
diff --git a/app/assets/javascripts/ci/runner/components/cells/runner_status_cell.vue b/app/assets/javascripts/ci/runner/components/cells/runner_status_cell.vue
index 4d04b5d4b14..e287e4e17d1 100644
--- a/app/assets/javascripts/ci/runner/components/cells/runner_status_cell.vue
+++ b/app/assets/javascripts/ci/runner/components/cells/runner_status_cell.vue
@@ -20,7 +20,13 @@ export default {
},
computed: {
paused() {
- return !this.runner.active;
+ return this.runner.paused;
+ },
+ contactedAt() {
+ return this.runner.contactedAt;
+ },
+ status() {
+ return this.runner.status;
},
},
};
@@ -29,7 +35,8 @@ export default {
<template>
<div>
<runner-status-badge
- :runner="runner"
+ :contacted-at="contactedAt"
+ :status="status"
class="gl-display-inline-block gl-max-w-full gl-text-truncate"
/>
<runner-paused-badge
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 f24fb5575ae..9f4ce14f704 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
@@ -8,6 +8,7 @@ import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import RunnerName from '../runner_name.vue';
import RunnerTags from '../runner_tags.vue';
import RunnerTypeBadge from '../runner_type_badge.vue';
+import RunnerManagersBadge from '../runner_managers_badge.vue';
import { formatJobCount } from '../../utils';
import {
@@ -29,6 +30,7 @@ export default {
RunnerName,
RunnerTags,
RunnerTypeBadge,
+ RunnerManagersBadge,
RunnerUpgradeStatusIcon: () =>
import('ee_component/ci/runner/components/runner_upgrade_status_icon.vue'),
UserAvatarLink,
@@ -44,6 +46,9 @@ export default {
},
},
computed: {
+ managersCount() {
+ return this.runner.managers?.count || 0;
+ },
jobCount() {
return formatJobCount(this.runner.jobCount);
},
@@ -75,6 +80,8 @@ export default {
<slot :runner="runner" name="runner-name">
<runner-name :runner="runner" />
</slot>
+
+ <runner-managers-badge :count="managersCount" size="sm" class="gl-vertical-align-middle" />
<gl-icon
v-if="runner.locked"
v-gl-tooltip
@@ -87,7 +94,7 @@ export default {
<div class="gl-mb-3 gl-ml-auto gl-display-inline-flex gl-max-w-full">
<template v-if="runner.version">
<div class="gl-flex-shrink-0">
- <runner-upgrade-status-icon :runner="runner" />
+ <runner-upgrade-status-icon :upgrade-status="runner.upgradeStatus" />
<gl-sprintf :message="$options.i18n.I18N_VERSION_LABEL">
<template #version>{{ runner.version }}</template>
</gl-sprintf>
diff --git a/app/assets/javascripts/ci/runner/components/registration/platforms_drawer.vue b/app/assets/javascripts/ci/runner/components/registration/platforms_drawer.vue
index ff182c61ccf..9cf2572c924 100644
--- a/app/assets/javascripts/ci/runner/components/registration/platforms_drawer.vue
+++ b/app/assets/javascripts/ci/runner/components/registration/platforms_drawer.vue
@@ -42,8 +42,8 @@ export default {
};
},
computed: {
- drawerHeightOffset() {
- return getContentWrapperHeight('.content-wrapper');
+ getDrawerHeaderHeight() {
+ return getContentWrapperHeight();
},
architectureOptions() {
return platformArchitectures({ platform: this.selectedPlatform });
@@ -86,7 +86,7 @@ export default {
<template>
<gl-drawer
:open="open"
- :header-height="drawerHeightOffset"
+ :header-height="getDrawerHeaderHeight"
:z-index="$options.DRAWER_Z_INDEX"
data-testid="runner-platforms-drawer"
@close="onClose"
diff --git a/app/assets/javascripts/ci/runner/components/registration/registration_feedback_banner.vue b/app/assets/javascripts/ci/runner/components/registration/registration_feedback_banner.vue
index fe19977f783..6fd4edf5847 100644
--- a/app/assets/javascripts/ci/runner/components/registration/registration_feedback_banner.vue
+++ b/app/assets/javascripts/ci/runner/components/registration/registration_feedback_banner.vue
@@ -1,5 +1,5 @@
<script>
-import ILLUSTRATION_URL from '@gitlab/svgs/dist/illustrations/multi-editor_all_changes_committed_empty.svg?url';
+import ILLUSTRATION_URL from '@gitlab/svgs/dist/illustrations/rocket-launch-md.svg?url';
import { GlBanner } from '@gitlab/ui';
import { s__ } from '~/locale';
import UserCalloutDismisser from '~/vue_shared/components/user_callout_dismisser.vue';
diff --git a/app/assets/javascripts/ci/runner/components/runner_create_form.vue b/app/assets/javascripts/ci/runner/components/runner_create_form.vue
index 6107b4dd3ea..1b363174d28 100644
--- a/app/assets/javascripts/ci/runner/components/runner_create_form.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_create_form.vue
@@ -9,7 +9,7 @@ import {
DEFAULT_ACCESS_LEVEL,
PROJECT_TYPE,
GROUP_TYPE,
- INSTANCE_TYPE,
+ I18N_CREATE_ERROR,
} from '../constants';
export default {
@@ -40,11 +40,13 @@ export default {
return {
saving: false,
runner: {
+ runnerType: this.runnerType,
description: '',
maintenanceNote: '',
paused: false,
accessLevel: DEFAULT_ACCESS_LEVEL,
runUntagged: false,
+ locked: false,
tagList: '',
maximumTimeout: '',
},
@@ -57,26 +59,22 @@ export default {
if (this.runnerType === GROUP_TYPE) {
return {
...input,
- runnerType: GROUP_TYPE,
groupId: this.groupId,
};
}
if (this.runnerType === PROJECT_TYPE) {
return {
...input,
- runnerType: PROJECT_TYPE,
projectId: this.projectId,
};
}
- return {
- ...input,
- runnerType: INSTANCE_TYPE,
- };
+ return input;
},
},
methods: {
async onSubmit() {
this.saving = true;
+
try {
const {
data: {
@@ -90,16 +88,29 @@ export default {
});
if (errors?.length) {
- this.$emit('error', new Error(errors.join(' ')));
- } else {
- this.onSuccess(runner);
+ this.onError(new Error(errors.join(' ')), true);
+ return;
+ }
+
+ if (!runner?.ephemeralRegisterUrl) {
+ // runner is missing information, report issue and
+ // fail naviation to register page.
+ this.onError(new Error(I18N_CREATE_ERROR));
+ return;
}
+
+ this.onSuccess(runner);
} catch (error) {
+ this.onError(error);
+ }
+ },
+ onError(error, isValidationError = false) {
+ if (!isValidationError) {
captureException({ error, component: this.$options.name });
- this.$emit('error', error);
- } finally {
- this.saving = false;
}
+
+ this.$emit('error', error);
+ this.saving = false;
},
onSuccess(runner) {
this.$emit('saved', runner);
@@ -111,9 +122,9 @@ export default {
<gl-form @submit.prevent="onSubmit">
<runner-form-fields v-model="runner" />
- <div class="gl-display-flex">
+ <div class="gl-display-flex gl-mt-6">
<gl-button type="submit" variant="confirm" class="js-no-auto-disable" :loading="saving">
- {{ __('Submit') }}
+ {{ s__('Runners|Create runner') }}
</gl-button>
</div>
</gl-form>
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 020487fc727..3560521e8d7 100644
--- a/app/assets/javascripts/ci/runner/components/runner_delete_button.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_delete_button.vue
@@ -45,6 +45,9 @@ export default {
runnerName() {
return `#${this.runnerId} (${this.runner.shortSha})`;
},
+ runnerManagersCount() {
+ return this.runner.managers?.count || 0;
+ },
runnerDeleteModalId() {
return `delete-runner-modal-${this.runnerId}`;
},
@@ -150,6 +153,7 @@ export default {
<runner-delete-modal
:modal-id="runnerDeleteModalId"
:runner-name="runnerName"
+ :managers-count="runnerManagersCount"
@primary="onDelete"
/>
</div>
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 8be216a7eb5..93f79fd67ea 100644
--- a/app/assets/javascripts/ci/runner/components/runner_delete_modal.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_delete_modal.vue
@@ -1,12 +1,9 @@
<script>
import { GlModal } from '@gitlab/ui';
-import { __, s__, sprintf } from '~/locale';
+import { __, s__, n__, sprintf } from '~/locale';
const I18N_TITLE = s__('Runners|Delete runner %{name}?');
-const I18N_BODY = s__(
- 'Runners|The runner will be permanently deleted and no longer available for projects or groups in the instance. Are you sure you want to continue?',
-);
-const I18N_PRIMARY = s__('Runners|Delete runner');
+const I18N_TITLE_PLURAL = s__('Runners|Delete %{count} runners?');
const I18N_CANCEL = __('Cancel');
export default {
@@ -18,10 +15,40 @@ export default {
type: String,
required: true,
},
+ managersCount: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
},
computed: {
+ count() {
+ // Only show count if MORE than 1 manager, for 0 we still
+ // assume 1 runner that happens to be disconnected.
+ return this.managersCount > 1 ? this.managersCount : 1;
+ },
title() {
- return sprintf(I18N_TITLE, { name: this.runnerName });
+ if (this.count === 1) {
+ return sprintf(I18N_TITLE, { name: this.runnerName });
+ }
+ return sprintf(I18N_TITLE_PLURAL, { count: this.count });
+ },
+ body() {
+ return n__(
+ 'Runners|The runner will be permanently deleted and no longer available for projects or groups in the instance. Are you sure you want to continue?',
+ 'Runners|%d runners will be permanently deleted and no longer available for projects or groups in the instance. Are you sure you want to continue?',
+ this.count,
+ );
+ },
+ actionPrimary() {
+ return {
+ text: n__(
+ 'Runners|Permanently delete runner',
+ 'Runners|Permanently delete %d runners',
+ this.count,
+ ),
+ attributes: { variant: 'danger' },
+ };
},
},
methods: {
@@ -29,9 +56,7 @@ export default {
this.$refs.modal.hide();
},
},
- actionPrimary: { text: I18N_PRIMARY, attributes: { variant: 'danger' } },
- actionCancel: { text: I18N_CANCEL },
- I18N_BODY,
+ ACTION_CANCEL: { text: I18N_CANCEL },
};
</script>
@@ -40,12 +65,12 @@ export default {
ref="modal"
size="sm"
:title="title"
- :action-primary="$options.actionPrimary"
- :action-cancel="$options.actionCancel"
+ :action-primary="actionPrimary"
+ :action-cancel="$options.ACTION_CANCEL"
v-bind="$attrs"
v-on="$listeners"
@primary="onPrimary"
>
- {{ $options.I18N_BODY }}
+ {{ body }}
</gl-modal>
</template>
diff --git a/app/assets/javascripts/ci/runner/components/runner_details.vue b/app/assets/javascripts/ci/runner/components/runner_details.vue
index 6eba8f2e49f..8c1280cffb9 100644
--- a/app/assets/javascripts/ci/runner/components/runner_details.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_details.vue
@@ -1,20 +1,28 @@
<script>
-import { GlIntersperse, GlLink } from '@gitlab/ui';
+import { GlIntersperse, GlLink, GlSprintf } from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper';
import { s__ } from '~/locale';
import HelpPopover from '~/vue_shared/components/help_popover.vue';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import { timeIntervalInWords } from '~/lib/utils/datetime_utility';
-import { ACCESS_LEVEL_REF_PROTECTED, GROUP_TYPE, PROJECT_TYPE } from '../constants';
+import {
+ ACCESS_LEVEL_REF_PROTECTED,
+ GROUP_TYPE,
+ PROJECT_TYPE,
+ RUNNER_MANAGERS_HELP_URL,
+ I18N_STATUS_NEVER_CONTACTED,
+} from '../constants';
import RunnerDetail from './runner_detail.vue';
import RunnerGroups from './runner_groups.vue';
import RunnerProjects from './runner_projects.vue';
import RunnerTags from './runner_tags.vue';
+import RunnerManagersDetail from './runner_managers_detail.vue';
export default {
components: {
GlIntersperse,
GlLink,
+ GlSprintf,
HelpPopover,
RunnerDetail,
RunnerMaintenanceNoteDetail: () =>
@@ -26,6 +34,7 @@ export default {
RunnerUpgradeStatusAlert: () =>
import('ee_component/ci/runner/components/runner_upgrade_status_alert.vue'),
RunnerTags,
+ RunnerManagersDetail,
TimeAgo,
},
props: {
@@ -76,6 +85,8 @@ export default {
},
},
ACCESS_LEVEL_REF_PROTECTED,
+ RUNNER_MANAGERS_HELP_URL,
+ I18N_STATUS_NEVER_CONTACTED,
};
</script>
@@ -90,7 +101,7 @@ export default {
<runner-detail :label="s__('Runners|Description')" :value="runner.description" />
<runner-detail
:label="s__('Runners|Last contact')"
- :empty-value="s__('Runners|Never contacted')"
+ :empty-value="$options.I18N_STATUS_NEVER_CONTACTED"
>
<template v-if="runner.contactedAt" #value>
<time-ago :time="runner.contactedAt" />
@@ -150,6 +161,33 @@ export default {
class="gl-pt-4 gl-border-t-gray-100 gl-border-t-1 gl-border-t-solid"
:value="runner.maintenanceNoteHtml"
/>
+
+ <runner-detail>
+ <template #label>
+ {{ s__('Runners|Runners') }}
+ <help-popover>
+ <gl-sprintf
+ :message="
+ s__(
+ 'Runners|Runners are grouped when they have the same authentication token. This happens when you re-use a runner configuration in more than one runner manager. %{linkStart}How does this work?%{linkEnd}',
+ )
+ "
+ >
+ <template #link="{ content }"
+ ><gl-link
+ :href="$options.RUNNER_MANAGERS_HELP_URL"
+ target="_blank"
+ class="gl-reset-font-size"
+ >{{ content }}</gl-link
+ ></template
+ >
+ </gl-sprintf>
+ </help-popover>
+ </template>
+ <template #value>
+ <runner-managers-detail :runner="runner" />
+ </template>
+ </runner-detail>
</dl>
</div>
diff --git a/app/assets/javascripts/ci/runner/components/runner_form_fields.vue b/app/assets/javascripts/ci/runner/components/runner_form_fields.vue
index e37ac5e6e26..d090a562ff7 100644
--- a/app/assets/javascripts/ci/runner/components/runner_form_fields.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_form_fields.vue
@@ -1,7 +1,16 @@
<script>
-import { GlFormGroup, GlFormCheckbox, GlFormInput, GlLink, GlSprintf } from '@gitlab/ui';
+import { isEqual } from 'lodash';
+import {
+ GlFormGroup,
+ GlFormCheckbox,
+ GlFormInput,
+ GlIcon,
+ GlLink,
+ GlSprintf,
+ GlSkeletonLoader,
+} from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper';
-import { ACCESS_LEVEL_NOT_PROTECTED, ACCESS_LEVEL_REF_PROTECTED } from '../constants';
+import { ACCESS_LEVEL_NOT_PROTECTED, ACCESS_LEVEL_REF_PROTECTED, PROJECT_TYPE } from '../constants';
export default {
name: 'RunnerFormFields',
@@ -9,8 +18,10 @@ export default {
GlFormGroup,
GlFormCheckbox,
GlFormInput,
+ GlIcon,
GlLink,
GlSprintf,
+ GlSkeletonLoader,
RunnerMaintenanceNoteField: () =>
import('ee_component/ci/runner/components/runner_maintenance_note_field.vue'),
},
@@ -20,15 +31,32 @@ export default {
default: null,
required: false,
},
+ loading: {
+ type: Boolean,
+ default: false,
+ required: false,
+ },
},
data() {
return {
- model: {
- ...this.value,
- },
+ model: null,
};
},
+ computed: {
+ canBeLockedToProject() {
+ return this.value?.runnerType === PROJECT_TYPE;
+ },
+ },
watch: {
+ value: {
+ handler(newVal, oldVal) {
+ // update only when values change, avoids infinite loop
+ if (!isEqual(newVal, oldVal)) {
+ this.model = { ...newVal };
+ }
+ },
+ immediate: true,
+ },
model: {
handler() {
this.$emit('input', this.model);
@@ -45,96 +73,122 @@ export default {
</script>
<template>
<div>
- <h2 class="gl-font-weight-normal gl-font-lg gl-my-5">
+ <h2 class="gl-font-size-h2 gl-my-5">
+ {{ s__('Runners|Tags') }}
+ </h2>
+ <gl-skeleton-loader v-if="loading" :lines="12" />
+ <template v-else-if="model">
+ <gl-form-group :label="__('Tags')" label-for="runner-tags">
+ <template #description>
+ <gl-sprintf
+ :message="
+ s__('Runners|Multiple tags must be separated by a comma. For example, %{example}.')
+ "
+ >
+ <template #example>
+ <!-- eslint-disable-next-line @gitlab/vue-require-i18n-strings -->
+ <code>macos, shared</code>
+ </template>
+ </gl-sprintf>
+ </template>
+ <template #label-description>
+ <gl-sprintf
+ :message="
+ s__(
+ 'Runners|Add tags for the types of jobs the runner processes to ensure that the runner only runs jobs that you intend it to. %{helpLinkStart}Learn more.%{helpLinkEnd}',
+ )
+ "
+ >
+ <template #helpLink="{ content }">
+ <gl-link :href="$options.HELP_LABELS_PAGE_PATH" target="_blank">{{
+ content
+ }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </template>
+ <gl-form-input id="runner-tags" v-model="model.tagList" name="tags" />
+ </gl-form-group>
+ <gl-form-checkbox v-model="model.runUntagged" name="run-untagged">
+ {{ __('Run untagged jobs') }}
+ <template #help>
+ {{ s__('Runners|Use the runner for jobs without tags in addition to tagged jobs.') }}
+ </template>
+ </gl-form-checkbox>
+ </template>
+
+ <hr aria-hidden="true" />
+
+ <h2 class="gl-font-size-h2 gl-my-5">
{{ s__('Runners|Details') }}
{{ __('(optional)') }}
</h2>
- <gl-form-group :label="s__('Runners|Runner description')" label-for="runner-description">
- <gl-form-input id="runner-description" v-model="model.description" name="description" />
- </gl-form-group>
- <runner-maintenance-note-field v-model="model.maintenanceNote" class="gl-mt-5" />
+ <gl-skeleton-loader v-if="loading" :lines="15" />
+ <template v-else-if="model">
+ <gl-form-group :label="s__('Runners|Runner description')" label-for="runner-description">
+ <gl-form-input id="runner-description" v-model="model.description" name="description" />
+ </gl-form-group>
+ <runner-maintenance-note-field v-model="model.maintenanceNote" class="gl-mt-5" />
+ </template>
<hr aria-hidden="true" />
- <h2 class="gl-font-weight-normal gl-font-lg gl-my-5">
+ <h2 class="gl-font-size-h2 gl-my-5">
{{ s__('Runners|Configuration') }}
{{ __('(optional)') }}
</h2>
- <div class="gl-mb-5">
- <gl-form-checkbox v-model="model.paused" name="paused">
- {{ __('Paused') }}
- <template #help>
- {{ s__('Runners|Stop the runner from accepting new jobs.') }}
- </template>
- </gl-form-checkbox>
-
- <gl-form-checkbox
- v-model="model.accessLevel"
- name="protected"
- :value="$options.ACCESS_LEVEL_REF_PROTECTED"
- :unchecked-value="$options.ACCESS_LEVEL_NOT_PROTECTED"
- >
- {{ __('Protected') }}
- <template #help>
- {{ s__('Runners|Use the runner on pipelines for protected branches only.') }}
- </template>
- </gl-form-checkbox>
-
- <gl-form-checkbox v-model="model.runUntagged" name="run-untagged">
- {{ __('Run untagged jobs') }}
- <template #help>
- {{ s__('Runners|Use the runner for jobs without tags in addition to tagged jobs.') }}
- </template>
- </gl-form-checkbox>
- </div>
+ <gl-skeleton-loader v-if="loading" :lines="15" />
+ <template v-else-if="model">
+ <div class="gl-mb-5">
+ <gl-form-checkbox v-model="model.paused" name="paused">
+ {{ __('Paused') }}
+ <template #help>
+ {{ s__('Runners|Stop the runner from accepting new jobs.') }}
+ </template>
+ </gl-form-checkbox>
- <gl-form-group :label="__('Tags')" label-for="runner-tags">
- <template #description>
- <gl-sprintf
- :message="
- s__('Runners|Multiple tags must be separated by a comma. For example, %{example}.')
- "
+ <gl-form-checkbox
+ v-model="model.accessLevel"
+ name="protected"
+ :value="$options.ACCESS_LEVEL_REF_PROTECTED"
+ :unchecked-value="$options.ACCESS_LEVEL_NOT_PROTECTED"
>
- <template #example>
- <!-- eslint-disable-next-line @gitlab/vue-require-i18n-strings -->
- <code>macos, shared</code>
+ {{ __('Protected') }}
+ <template #help>
+ {{ s__('Runners|Use the runner on pipelines for protected branches only.') }}
</template>
- </gl-sprintf>
- </template>
- <template #label-description>
- <gl-sprintf
- :message="
- s__(
- 'Runners|Add tags for the types of jobs the runner processes to ensure that the runner only runs jobs that you intend it to. %{helpLinkStart}Learn more.%{helpLinkEnd}',
- )
- "
- >
- <template #helpLink="{ content }">
- <gl-link :href="$options.HELP_LABELS_PAGE_PATH" target="_blank">{{ content }}</gl-link>
+ </gl-form-checkbox>
+
+ <gl-form-checkbox v-if="canBeLockedToProject" v-model="model.locked" name="locked">
+ {{ __('Lock to current projects') }} <gl-icon name="lock" />
+ <template #help>
+ {{
+ s__(
+ 'Runners|Use the runner for the currently assigned projects only. Only administrators can change the assigned projects.',
+ )
+ }}
</template>
- </gl-sprintf>
- </template>
- <gl-form-input id="runner-tags" v-model="model.tagList" name="tags" />
- </gl-form-group>
+ </gl-form-checkbox>
+ </div>
- <gl-form-group
- :label="__('Maximum job timeout')"
- :label-description="
- s__(
- 'Runners|Maximum amount of time the runner can run before it terminates. If a project has a shorter job timeout period, the job timeout period of the instance runner is used instead.',
- )
- "
- label-for="runner-max-timeout"
- :description="s__('Runners|Enter the number of seconds.')"
- >
- <gl-form-input
- id="runner-max-timeout"
- v-model.number="model.maximumTimeout"
- name="max-timeout"
- type="number"
- />
- </gl-form-group>
+ <gl-form-group
+ :label="__('Maximum job timeout')"
+ :label-description="
+ s__(
+ 'Runners|Maximum amount of time the runner can run before it terminates. If a project has a shorter job timeout period, the job timeout period of the instance runner is used instead.',
+ )
+ "
+ label-for="runner-max-timeout"
+ :description="s__('Runners|Enter the number of seconds.')"
+ >
+ <gl-form-input
+ id="runner-max-timeout"
+ v-model.number="model.maximumTimeout"
+ name="max-timeout"
+ type="number"
+ />
+ </gl-form-group>
+ </template>
</div>
</template>
diff --git a/app/assets/javascripts/ci/runner/components/runner_header.vue b/app/assets/javascripts/ci/runner/components/runner_header.vue
index 874c234ca4c..f46e894bf2e 100644
--- a/app/assets/javascripts/ci/runner/components/runner_header.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_header.vue
@@ -1,9 +1,8 @@
<script>
import { GlIcon, GlSprintf, GlTooltipDirective } from '@gitlab/ui';
-import { sprintf } from '~/locale';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
-import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import { I18N_DETAILS_TITLE, I18N_LOCKED_RUNNER_DESCRIPTION } from '../constants';
+import { I18N_LOCKED_RUNNER_DESCRIPTION } from '../constants';
+import { formatRunnerName } from '../utils';
import RunnerTypeBadge from './runner_type_badge.vue';
import RunnerStatusBadge from './runner_status_badge.vue';
@@ -25,12 +24,8 @@ export default {
},
},
computed: {
- paused() {
- return !this.runner.active;
- },
- heading() {
- const id = getIdFromGraphQLId(this.runner.id);
- return sprintf(I18N_DETAILS_TITLE, { runner_id: id });
+ name() {
+ return formatRunnerName(this.runner);
},
},
I18N_LOCKED_RUNNER_DESCRIPTION,
@@ -38,16 +33,16 @@ export default {
</script>
<template>
<div
- class="gl-display-flex gl-justify-content-space-between gl-align-items-center gl-gap-3 gl-flex-wrap gl-py-5 gl-border-b-1 gl-border-b-solid gl-border-b-gray-100"
+ class="gl-display-flex gl-justify-content-space-between gl-align-items-flex-start gl-gap-3 gl-flex-wrap gl-py-5"
>
- <div class="gl-display-flex gl-align-items-flex-start gl-gap-3 gl-flex-wrap">
- <runner-status-badge :runner="runner" />
- <runner-type-badge v-if="runner" :type="runner.runnerType" />
- <span>
- <template v-if="runner.createdAt">
- <gl-sprintf :message="__('%{runner} created %{timeago}')">
- <template #runner>
- <strong>{{ heading }}</strong>
+ <div>
+ <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"
@@ -59,11 +54,8 @@ export default {
<time-ago :time="runner.createdAt" />
</template>
</gl-sprintf>
- </template>
- <template v-else>
- <strong>{{ heading }}</strong>
- </template>
- </span>
+ </span>
+ </div>
</div>
<div class="gl-display-flex gl-gap-3 gl-flex-wrap"><slot name="actions"></slot></div>
</div>
diff --git a/app/assets/javascripts/ci/runner/components/runner_jobs_empty_state.vue b/app/assets/javascripts/ci/runner/components/runner_jobs_empty_state.vue
index c30a824120d..4e68c2ea71a 100644
--- a/app/assets/javascripts/ci/runner/components/runner_jobs_empty_state.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_jobs_empty_state.vue
@@ -1,5 +1,5 @@
<script>
-import EMPTY_STATE_SVG_URL from '@gitlab/svgs/dist/illustrations/pipelines_empty.svg?url';
+import EMPTY_STATE_SVG_URL from '@gitlab/svgs/dist/illustrations/empty-state/empty-pipeline-md.svg?url';
import { GlEmptyState } from '@gitlab/ui';
import { s__ } from '~/locale';
@@ -19,7 +19,11 @@ export default {
</script>
<template>
- <gl-empty-state :svg-path="$options.EMPTY_STATE_SVG_URL" :title="$options.i18n.title">
+ <gl-empty-state
+ :svg-path="$options.EMPTY_STATE_SVG_URL"
+ :svg-height="150"
+ :title="$options.i18n.title"
+ >
<template #description>
<p>{{ $options.i18n.description }}</p>
</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 8606c22db34..d2836962a97 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
@@ -1,10 +1,20 @@
<script>
-import EMPTY_STATE_SVG_URL from '@gitlab/svgs/dist/illustrations/pipelines_empty.svg?url';
-import FILTERED_SVG_URL from '@gitlab/svgs/dist/illustrations/magnifying-glass.svg?url';
+import EMPTY_STATE_SVG_URL from '@gitlab/svgs/dist/illustrations/empty-state/empty-pipeline-md.svg?url';
+import FILTERED_SVG_URL from '@gitlab/svgs/dist/illustrations/empty-state/empty-search-md.svg?url';
import { GlEmptyState, GlLink, GlSprintf, GlModalDirective } from '@gitlab/ui';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import RunnerInstructionsModal from '~/vue_shared/components/runner_instructions/runner_instructions_modal.vue';
+import {
+ I18N_GET_STARTED,
+ I18N_RUNNERS_ARE_AGENTS,
+ 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';
export default {
components: {
@@ -38,9 +48,8 @@ export default {
shouldShowCreateRunnerWorkflow() {
// create_runner_workflow_for_admin or create_runner_workflow_for_namespace
return (
- this.newRunnerPath &&
- (this.glFeatures?.createRunnerWorkflowForAdmin ||
- this.glFeatures?.createRunnerWorkflowForNamespace)
+ this.glFeatures?.createRunnerWorkflowForAdmin ||
+ this.glFeatures?.createRunnerWorkflowForNamespace
);
},
},
@@ -48,35 +57,59 @@ export default {
svgHeight: 145,
EMPTY_STATE_SVG_URL,
FILTERED_SVG_URL,
+
+ I18N_GET_STARTED,
+ I18N_RUNNERS_ARE_AGENTS,
+ 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,
};
</script>
<template>
<gl-empty-state
v-if="isSearchFiltered"
- :title="s__('Runners|No results found')"
+ :title="$options.I18N_NO_RESULTS"
:svg-path="$options.FILTERED_SVG_URL"
:svg-height="$options.svgHeight"
- :description="s__('Runners|Edit your search and try again')"
+ :description="$options.I18N_EDIT_YOUR_SEARCH"
/>
<gl-empty-state
v-else
- :title="s__('Runners|Get started with runners')"
+ :title="$options.I18N_GET_STARTED"
:svg-path="$options.EMPTY_STATE_SVG_URL"
:svg-height="$options.svgHeight"
>
- <template v-if="registrationToken" #description>
+ <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
- :message="
- s__(
- 'Runners|Runners are the agents that run your CI/CD jobs. Follow the %{linkStart}installation and registration instructions%{linkEnd} to set up a runner.',
- )
- "
+ v-else-if="registrationToken"
+ :message="$options.I18N_FOLLOW_REGISTRATION_INSTRUCTIONS"
>
- <template v-if="shouldShowCreateRunnerWorkflow" #link="{ content }">
- <gl-link :href="newRunnerPath">{{ content }}</gl-link>
- </template>
- <template v-else #link="{ content }">
+ <template #link="{ content }">
<gl-link v-gl-modal="$options.modalId">{{ content }}</gl-link>
<runner-instructions-modal
:modal-id="$options.modalId"
@@ -84,13 +117,9 @@ export default {
/>
</template>
</gl-sprintf>
- </template>
- <template v-else #description>
- {{
- s__(
- 'Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator.',
- )
- }}
+ <template v-else>
+ {{ $options.I18N_CONTACT_ADMIN_TO_REGISTER }}
+ </template>
</template>
</gl-empty-state>
</template>
diff --git a/app/assets/javascripts/ci/runner/components/runner_managers_badge.vue b/app/assets/javascripts/ci/runner/components/runner_managers_badge.vue
new file mode 100644
index 00000000000..d298d8ded82
--- /dev/null
+++ b/app/assets/javascripts/ci/runner/components/runner_managers_badge.vue
@@ -0,0 +1,47 @@
+<script>
+import { GlBadge, GlTooltipDirective } from '@gitlab/ui';
+import { formatNumber, s__, sprintf } from '~/locale';
+
+export default {
+ name: 'RunnerManagersBadge',
+ components: {
+ GlBadge,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ count: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
+ },
+ computed: {
+ shouldShowBadge() {
+ // runner managers can be grouped, but this information is only shown
+ // when we have 2 or more.
+ return this.count >= 2;
+ },
+ formattedCount() {
+ return formatNumber(this.count);
+ },
+ tooltip() {
+ return sprintf(s__('Runners|%{count} runners in this group'), {
+ count: this.formattedCount,
+ });
+ },
+ },
+};
+</script>
+<template>
+ <gl-badge
+ v-if="shouldShowBadge"
+ v-gl-tooltip="tooltip"
+ variant="muted"
+ icon="container-image"
+ v-bind="$attrs"
+ >
+ {{ formattedCount }}
+ </gl-badge>
+</template>
diff --git a/app/assets/javascripts/ci/runner/components/runner_managers_detail.vue b/app/assets/javascripts/ci/runner/components/runner_managers_detail.vue
new file mode 100644
index 00000000000..5cc1bbef481
--- /dev/null
+++ b/app/assets/javascripts/ci/runner/components/runner_managers_detail.vue
@@ -0,0 +1,111 @@
+<script>
+import { GlCollapse, GlButton, GlIcon, GlSkeletonLoader } from '@gitlab/ui';
+import { __, s__, formatNumber } from '~/locale';
+import { createAlert } from '~/alert';
+import runnerManagersQuery from '../graphql/show/runner_managers.query.graphql';
+import { I18N_FETCH_ERROR } from '../constants';
+import { captureException } from '../sentry_utils';
+import { tableField } from '../utils';
+import RunnerManagersTable from './runner_managers_table.vue';
+
+export default {
+ name: 'RunnerManagersDetail',
+ components: {
+ GlCollapse,
+ GlButton,
+ GlIcon,
+ GlSkeletonLoader,
+ RunnerManagersTable,
+ },
+ props: {
+ runner: {
+ type: Object,
+ required: true,
+ validator: (runner) => {
+ return Boolean(runner?.id);
+ },
+ },
+ },
+ data() {
+ return {
+ skip: true,
+ expanded: false,
+ managers: [],
+ };
+ },
+ apollo: {
+ managers: {
+ query: runnerManagersQuery,
+ skip() {
+ return this.skip;
+ },
+ variables() {
+ return { runnerId: this.runner.id };
+ },
+ update({ runner }) {
+ return runner?.managers?.nodes || [];
+ },
+ error(error) {
+ createAlert({ message: I18N_FETCH_ERROR });
+ captureException({ error, component: this.$options.name });
+ },
+ },
+ },
+ computed: {
+ runnerManagersCount() {
+ return this.runner?.managers?.count || 0;
+ },
+ runnerManagersCountFormatted() {
+ return formatNumber(this.runnerManagersCount);
+ },
+ icon() {
+ return this.expanded ? 'chevron-down' : 'chevron-right';
+ },
+ text() {
+ return this.expanded ? __('Hide details') : __('Show details');
+ },
+ loading() {
+ return this.$apollo?.queries.managers.loading;
+ },
+ },
+ methods: {
+ fetchManagers() {
+ this.skip = false;
+ },
+ toggleExpanded() {
+ this.expanded = !this.expanded;
+ },
+ },
+ fields: [
+ tableField({ key: 'systemId', label: s__('Runners|System ID') }),
+ tableField({
+ key: 'contactedAt',
+ label: s__('Runners|Last contact'),
+ tdClass: ['gl-text-right'],
+ thClasses: ['gl-text-right'],
+ }),
+ ],
+};
+</script>
+
+<template>
+ <div>
+ <gl-icon name="container-image" class="gl-text-secondary" />
+ {{ runnerManagersCountFormatted }}
+ <gl-button
+ v-if="runnerManagersCount"
+ variant="link"
+ @mouseover.once="fetchManagers"
+ @focus.once="fetchManagers"
+ @click.once="fetchManagers"
+ @click="toggleExpanded"
+ >
+ <gl-icon :name="icon" /> {{ text }}
+ </gl-button>
+
+ <gl-collapse :visible="expanded" class="gl-mt-5">
+ <gl-skeleton-loader v-if="loading" />
+ <runner-managers-table v-else-if="managers.length" :items="managers" />
+ </gl-collapse>
+ </div>
+</template>
diff --git a/app/assets/javascripts/ci/runner/components/runner_managers_table.vue b/app/assets/javascripts/ci/runner/components/runner_managers_table.vue
new file mode 100644
index 00000000000..10790c398b0
--- /dev/null
+++ b/app/assets/javascripts/ci/runner/components/runner_managers_table.vue
@@ -0,0 +1,75 @@
+<script>
+import { GlIntersperse, GlTableLite } from '@gitlab/ui';
+import HelpPopover from '~/vue_shared/components/help_popover.vue';
+import { s__ } from '~/locale';
+import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
+import { tableField } from '../utils';
+import { I18N_STATUS_NEVER_CONTACTED } from '../constants';
+import RunnerStatusBadge from './runner_status_badge.vue';
+
+export default {
+ name: 'RunnerManagersTable',
+ components: {
+ GlTableLite,
+ TimeAgo,
+ HelpPopover,
+ GlIntersperse,
+ RunnerStatusBadge,
+ RunnerUpgradeStatusIcon: () =>
+ import('ee_component/ci/runner/components/runner_upgrade_status_icon.vue'),
+ },
+ props: {
+ items: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ },
+ fields: [
+ tableField({ key: 'systemId', label: s__('Runners|System ID') }),
+ tableField({ key: 'status', label: s__('Runners|Status') }),
+ tableField({ key: 'version', label: s__('Runners|Version') }),
+ tableField({ key: 'ipAddress', label: s__('Runners|IP Address') }),
+ tableField({ key: 'executorName', label: s__('Runners|Executor') }),
+ tableField({ key: 'architecturePlatform', label: s__('Runners|Arch/Platform') }),
+ tableField({
+ key: 'contactedAt',
+ label: s__('Runners|Last contact'),
+ tdClass: ['gl-text-right'],
+ thClasses: ['gl-text-right'],
+ }),
+ ],
+ I18N_STATUS_NEVER_CONTACTED,
+};
+</script>
+
+<template>
+ <gl-table-lite :fields="$options.fields" :items="items">
+ <template #head(systemId)="{ label }">
+ {{ label }}
+ <help-popover>
+ {{ s__('Runners|The unique ID for each runner that uses this configuration.') }}
+ </help-popover>
+ </template>
+ <template #cell(status)="{ item = {} }">
+ <runner-status-badge :contacted-at="item.contactedAt" :status="item.status" />
+ </template>
+ <template #cell(version)="{ item = {} }">
+ {{ item.version }}
+ <template v-if="item.revision">({{ item.revision }})</template>
+ <runner-upgrade-status-icon :upgrade-status="item.upgradeStatus" />
+ </template>
+ <template #cell(architecturePlatform)="{ item = {} }">
+ <gl-intersperse separator="/">
+ <span v-if="item.architectureName">{{ item.architectureName }}</span>
+ <span v-if="item.platformName">{{ item.platformName }}</span>
+ </gl-intersperse>
+ </template>
+ <template #cell(contactedAt)="{ item = {} }">
+ <template v-if="item.contactedAt">
+ <time-ago :time="item.contactedAt" />
+ </template>
+ <template v-else>{{ $options.I18N_STATUS_NEVER_CONTACTED }}</template>
+ </template>
+ </gl-table-lite>
+</template>
diff --git a/app/assets/javascripts/ci/runner/components/runner_name.vue b/app/assets/javascripts/ci/runner/components/runner_name.vue
index d4ecfd2d776..a877ff0f06c 100644
--- a/app/assets/javascripts/ci/runner/components/runner_name.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_name.vue
@@ -1,5 +1,5 @@
<script>
-import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import { formatRunnerName } from '../utils';
export default {
props: {
@@ -8,13 +8,13 @@ export default {
required: true,
},
},
- methods: {
- getIdFromGraphQLId,
+ computed: {
+ name() {
+ return formatRunnerName(this.runner);
+ },
},
};
</script>
<template>
- <span class="gl-font-weight-bold gl-vertical-align-middle"
- >#{{ getIdFromGraphQLId(runner.id) }} ({{ runner.shortSha }})</span
- >
+ <span class="gl-font-weight-bold gl-vertical-align-middle">{{ name }}</span>
</template>
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 a27af232e97..d16c8f98bad 100644
--- a/app/assets/javascripts/ci/runner/components/runner_pause_button.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_pause_button.vue
@@ -1,6 +1,6 @@
<script>
import { GlButton, GlTooltipDirective } from '@gitlab/ui';
-import runnerToggleActiveMutation from '~/ci/runner/graphql/shared/runner_toggle_active.mutation.graphql';
+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';
@@ -31,14 +31,14 @@ export default {
};
},
computed: {
- isActive() {
- return this.runner.active;
+ isPaused() {
+ return this.runner.paused;
},
icon() {
- return this.isActive ? 'pause' : 'play';
+ return this.isPaused ? 'play' : 'pause';
},
label() {
- return this.isActive ? I18N_PAUSE : I18N_RESUME;
+ return this.isPaused ? I18N_RESUME : I18N_PAUSE;
},
buttonContent() {
if (this.compact) {
@@ -56,7 +56,7 @@ export default {
// Prevent a "sticky" tooltip: If this button is disabled,
// mouseout listeners don't run leaving the tooltip stuck
if (!this.updating) {
- return this.isActive ? I18N_PAUSE_TOOLTIP : I18N_RESUME_TOOLTIP;
+ return this.isPaused ? I18N_RESUME_TOOLTIP : I18N_PAUSE_TOOLTIP;
}
return '';
},
@@ -67,7 +67,7 @@ export default {
try {
const input = {
id: this.runner.id,
- active: !this.isActive,
+ paused: !this.isPaused,
};
const {
@@ -75,7 +75,7 @@ export default {
runnerUpdate: { errors },
},
} = await this.$apollo.mutate({
- mutation: runnerToggleActiveMutation,
+ mutation: runnerTogglePausedMutation,
variables: {
input,
},
diff --git a/app/assets/javascripts/ci/runner/components/runner_status_badge.vue b/app/assets/javascripts/ci/runner/components/runner_status_badge.vue
index d084408781e..c2c52bd756a 100644
--- a/app/assets/javascripts/ci/runner/components/runner_status_badge.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_status_badge.vue
@@ -26,21 +26,27 @@ export default {
GlTooltip: GlTooltipDirective,
},
props: {
- runner: {
- required: true,
- type: Object,
+ contactedAt: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ status: {
+ type: String,
+ required: false,
+ default: null,
},
},
computed: {
contactedAtTimeAgo() {
- if (this.runner.contactedAt) {
- return getTimeago().format(this.runner.contactedAt);
+ if (this.contactedAt) {
+ return getTimeago().format(this.contactedAt);
}
// Prevent "just now" from being rendered, in case data is missing.
return __('never');
},
badge() {
- switch (this.runner?.status) {
+ switch (this.status) {
case STATUS_ONLINE:
return {
icon: 'status-active',
@@ -68,7 +74,7 @@ export default {
variant: 'warning',
label: I18N_STATUS_STALE,
// runner may have contacted (or not) and be stale: consider both cases.
- tooltip: this.runner.contactedAt
+ tooltip: this.contactedAt
? this.timeAgoTooltip(I18N_STALE_TIMEAGO_TOOLTIP)
: I18N_STALE_NEVER_CONTACTED_TOOLTIP,
};
diff --git a/app/assets/javascripts/ci/runner/components/runner_update_form.vue b/app/assets/javascripts/ci/runner/components/runner_update_form.vue
index 2d34c551d6d..6b94e594f1c 100644
--- a/app/assets/javascripts/ci/runner/components/runner_update_form.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_update_form.vue
@@ -1,23 +1,16 @@
<script>
-import {
- GlButton,
- GlIcon,
- GlForm,
- GlFormCheckbox,
- GlFormGroup,
- GlFormInputGroup,
- GlSkeletonLoader,
- GlTooltipDirective,
-} from '@gitlab/ui';
+import { GlButton, GlForm } from '@gitlab/ui';
+import RunnerFormFields from '~/ci/runner/components/runner_form_fields.vue';
+import { createAlert, VARIANT_SUCCESS } from '~/alert';
+import { visitUrl } from '~/lib/utils/url_utility';
+import { __ } from '~/locale';
+import { captureException } from '~/ci/runner/sentry_utils';
+
import {
modelToUpdateMutationVariables,
runnerToModel,
} from 'ee_else_ce/ci/runner/runner_update_form_utils';
-import { createAlert, VARIANT_SUCCESS } from '~/alert';
-import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
-import { __ } from '~/locale';
-import { captureException } from '~/ci/runner/sentry_utils';
-import { ACCESS_LEVEL_NOT_PROTECTED, ACCESS_LEVEL_REF_PROTECTED, PROJECT_TYPE } from '../constants';
+import { ACCESS_LEVEL_NOT_PROTECTED, ACCESS_LEVEL_REF_PROTECTED } from '../constants';
import runnerUpdateMutation from '../graphql/edit/runner_update.mutation.graphql';
import { saveAlertToLocalStorage } from '../local_storage_alert/save_alert_to_local_storage';
@@ -25,20 +18,11 @@ export default {
name: 'RunnerUpdateForm',
components: {
GlButton,
- GlIcon,
GlForm,
- GlFormCheckbox,
- GlFormGroup,
- GlFormInputGroup,
- GlSkeletonLoader,
- RunnerMaintenanceNoteField: () =>
- import('ee_component/ci/runner/components/runner_maintenance_note_field.vue'),
+ RunnerFormFields,
RunnerUpdateCostFactorFields: () =>
import('ee_component/ci/runner/components/runner_update_cost_factor_fields.vue'),
},
- directives: {
- GlTooltip: GlTooltipDirective,
- },
props: {
runner: {
type: Object,
@@ -59,19 +43,17 @@ export default {
data() {
return {
saving: false,
- model: runnerToModel(this.runner),
+ model: null,
};
},
computed: {
- canBeLockedToProject() {
- return this.runner?.runnerType === PROJECT_TYPE;
+ runnerType() {
+ return this.runner?.runnerType;
},
},
watch: {
- runner(newVal, oldVal) {
- if (oldVal === null) {
- this.model = runnerToModel(newVal);
- }
+ runner(val) {
+ this.model = runnerToModel(val);
},
},
methods: {
@@ -101,7 +83,7 @@ export default {
},
onSuccess() {
saveAlertToLocalStorage({ message: __('Changes saved.'), variant: VARIANT_SUCCESS });
- redirectTo(this.runnerPath); // eslint-disable-line import/no-deprecated
+ visitUrl(this.runnerPath);
},
onError(message) {
this.saving = false;
@@ -114,99 +96,8 @@ export default {
</script>
<template>
<gl-form @submit.prevent="onSubmit">
- <h4 class="gl-font-lg gl-my-5">{{ s__('Runners|Details') }}</h4>
-
- <gl-skeleton-loader v-if="loading" />
-
- <template v-else>
- <gl-form-group :label="__('Description')" data-testid="runner-field-description">
- <gl-form-input-group v-model="model.description" />
- </gl-form-group>
- <runner-maintenance-note-field v-model="model.maintenanceNote" />
- </template>
-
- <hr />
-
- <h4 class="gl-font-lg gl-my-5">{{ s__('Runners|Configuration') }}</h4>
-
- <template v-if="loading">
- <gl-skeleton-loader v-for="i in 3" :key="i" />
- </template>
- <template v-else>
- <div class="gl-mb-5">
- <gl-form-checkbox
- v-model="model.active"
- data-testid="runner-field-paused"
- :value="false"
- :unchecked-value="true"
- >
- {{ __('Paused') }}
- <template #help>
- {{ s__('Runners|Stop the runner from accepting new jobs.') }}
- </template>
- </gl-form-checkbox>
-
- <gl-form-checkbox
- v-model="model.accessLevel"
- data-testid="runner-field-protected"
- :value="$options.ACCESS_LEVEL_REF_PROTECTED"
- :unchecked-value="$options.ACCESS_LEVEL_NOT_PROTECTED"
- >
- {{ __('Protected') }}
- <template #help>
- {{ s__('Runners|Use the runner on pipelines for protected branches only.') }}
- </template>
- </gl-form-checkbox>
-
- <gl-form-checkbox v-model="model.runUntagged" data-testid="runner-field-run-untagged">
- {{ __('Run untagged jobs') }}
- <template #help>
- {{ s__('Runners|Use the runner for jobs without tags, in addition to tagged jobs.') }}
- </template>
- </gl-form-checkbox>
-
- <gl-form-checkbox
- v-if="canBeLockedToProject"
- v-model="model.locked"
- data-testid="runner-field-locked"
- >
- {{ __('Lock to current projects') }} <gl-icon name="lock" />
- <template #help>
- {{
- s__(
- 'Runners|Use the runner for the currently assigned projects only. Only administrators can change the assigned projects.',
- )
- }}
- </template>
- </gl-form-checkbox>
- </div>
-
- <gl-form-group
- data-testid="runner-field-max-timeout"
- :label="__('Maximum job timeout')"
- :description="
- s__(
- 'Runners|Enter the number of seconds. This timeout takes precedence over lower timeouts set for the project.',
- )
- "
- >
- <gl-form-input-group v-model.number="model.maximumTimeout" type="number" />
- </gl-form-group>
-
- <gl-form-group
- data-testid="runner-field-tags"
- :label="__('Tags')"
- :description="
- __(
- 'You can set up jobs to only use runners with specific tags. Separate tags with commas.',
- )
- "
- >
- <gl-form-input-group v-model="model.tagList" />
- </gl-form-group>
-
- <runner-update-cost-factor-fields v-model="model" />
- </template>
+ <runner-form-fields v-model="model" :loading="loading" />
+ <runner-update-cost-factor-fields v-model="model" :runner-type="runnerType" />
<div class="gl-mt-6">
<gl-button
diff --git a/app/assets/javascripts/ci/runner/constants.js b/app/assets/javascripts/ci/runner/constants.js
index 4e36a410a66..40841696ead 100644
--- a/app/assets/javascripts/ci/runner/constants.js
+++ b/app/assets/javascripts/ci/runner/constants.js
@@ -9,7 +9,9 @@ export const RUNNER_DETAILS_PROJECTS_PAGE_SIZE = 5;
export const RUNNER_DETAILS_JOBS_PAGE_SIZE = 30;
export const I18N_FETCH_ERROR = s__('Runners|Something went wrong while fetching runner data.');
-export const I18N_DETAILS_TITLE = s__('Runners|Runner #%{runner_id}');
+export const I18N_CREATE_ERROR = s__(
+ 'Runners|An error occurred while creating the runner. Please try again.',
+);
export const FILTER_CSS_CLASSES =
'gl-bg-gray-10 gl-p-5 gl-border-solid gl-border-gray-100 gl-border-0 gl-border-t-1 gl-border-b-1';
@@ -103,6 +105,26 @@ export const I18N_CREATED_AT_BY_LABEL = s__('Runners|Created %{timeAgo} by %{ava
export const I18N_SHOW_ONLY_INHERITED = s__('Runners|Show only inherited');
export const I18N_ADMIN = s__('Runners|Administrator');
+// No runners registered
+export const I18N_GET_STARTED = s__('Runners|Get started with runners');
+export const I18N_RUNNERS_ARE_AGENTS = s__(
+ 'Runners|Runners are the agents that run your CI/CD jobs.',
+);
+export const I18N_CREATE_RUNNER_LINK = s__(
+ 'Runners|%{linkStart}Create a new runner%{linkEnd} to get started.',
+);
+export const I18N_STILL_USING_REGISTRATION_TOKENS = s__('Runners|Still using registration tokens?');
+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');
+export const I18N_EDIT_YOUR_SEARCH = s__('Runners|Edit your search and try again');
+
// Runner details
export const JOBS_ROUTE_PATH = '/jobs'; // vue-router route path
@@ -256,3 +278,5 @@ export const SERVICE_COMMANDS_HELP_URL =
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';
diff --git a/app/assets/javascripts/ci/runner/graphql/edit/runner_fields_shared.fragment.graphql b/app/assets/javascripts/ci/runner/graphql/edit/runner_fields_shared.fragment.graphql
index d18b80511fb..41ec9967d90 100644
--- a/app/assets/javascripts/ci/runner/graphql/edit/runner_fields_shared.fragment.graphql
+++ b/app/assets/javascripts/ci/runner/graphql/edit/runner_fields_shared.fragment.graphql
@@ -2,7 +2,7 @@ fragment RunnerFieldsShared on CiRunner {
id
shortSha
runnerType
- active
+ paused
accessLevel
runUntagged
locked
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 4eebcd01be6..c0b888e758b 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
@@ -7,7 +7,7 @@ fragment ListItemShared on CiRunner {
shortSha
version
ipAddress
- active
+ paused
locked
jobCount
tagList
@@ -22,6 +22,9 @@ fragment ListItemShared on CiRunner {
updateRunner
deleteRunner
}
+ managers {
+ count
+ }
groups(first: 1) {
nodes {
id
diff --git a/app/assets/javascripts/ci/runner/graphql/shared/runner_toggle_active.mutation.graphql b/app/assets/javascripts/ci/runner/graphql/shared/runner_toggle_paused.mutation.graphql
index 9b15570dbc0..e862a20750f 100644
--- a/app/assets/javascripts/ci/runner/graphql/shared/runner_toggle_active.mutation.graphql
+++ b/app/assets/javascripts/ci/runner/graphql/shared/runner_toggle_paused.mutation.graphql
@@ -1,11 +1,11 @@
# Mutation executed for the pause/resume button in the
# runner list and details views.
-mutation runnerToggleActive($input: RunnerUpdateInput!) {
+mutation runnerTogglePaused($input: RunnerUpdateInput!) {
runnerUpdate(input: $input) {
runner {
id
- active
+ paused
}
errors
}
diff --git a/app/assets/javascripts/ci/runner/graphql/show/runner_details_shared.fragment.graphql b/app/assets/javascripts/ci/runner/graphql/show/runner_details_shared.fragment.graphql
index bd53fb29bd0..1a2ad59650e 100644
--- a/app/assets/javascripts/ci/runner/graphql/show/runner_details_shared.fragment.graphql
+++ b/app/assets/javascripts/ci/runner/graphql/show/runner_details_shared.fragment.graphql
@@ -2,7 +2,7 @@ fragment RunnerDetailsShared on CiRunner {
id
shortSha
runnerType
- active
+ paused
accessLevel
runUntagged
locked
@@ -20,6 +20,9 @@ fragment RunnerDetailsShared on CiRunner {
tokenExpiresAt
version
editAdminUrl
+ managers {
+ count
+ }
userPermissions {
updateRunner
deleteRunner
diff --git a/app/assets/javascripts/ci/runner/graphql/show/runner_manager.fragment.graphql b/app/assets/javascripts/ci/runner/graphql/show/runner_manager.fragment.graphql
new file mode 100644
index 00000000000..b630786b3d5
--- /dev/null
+++ b/app/assets/javascripts/ci/runner/graphql/show/runner_manager.fragment.graphql
@@ -0,0 +1,5 @@
+#import "./runner_manager_shared.fragment.graphql"
+
+fragment CiRunnerManager on CiRunnerManager {
+ ...CiRunnerManagerShared
+}
diff --git a/app/assets/javascripts/ci/runner/graphql/show/runner_manager_shared.fragment.graphql b/app/assets/javascripts/ci/runner/graphql/show/runner_manager_shared.fragment.graphql
new file mode 100644
index 00000000000..ead005d1252
--- /dev/null
+++ b/app/assets/javascripts/ci/runner/graphql/show/runner_manager_shared.fragment.graphql
@@ -0,0 +1,12 @@
+fragment CiRunnerManagerShared on CiRunnerManager {
+ id
+ systemId
+ status
+ version
+ revision
+ executorName
+ architectureName
+ platformName
+ ipAddress
+ contactedAt
+}
diff --git a/app/assets/javascripts/ci/runner/graphql/show/runner_managers.query.graphql b/app/assets/javascripts/ci/runner/graphql/show/runner_managers.query.graphql
new file mode 100644
index 00000000000..cc16267e619
--- /dev/null
+++ b/app/assets/javascripts/ci/runner/graphql/show/runner_managers.query.graphql
@@ -0,0 +1,13 @@
+#import "ee_else_ce/ci/runner/graphql/show/runner_manager.fragment.graphql"
+
+query getRunnerManagers($runnerId: CiRunnerID!) {
+ runner(id: $runnerId) {
+ id
+ managers {
+ count
+ nodes {
+ ...CiRunnerManager
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/ci/runner/group_new_runner/group_new_runner_app.vue b/app/assets/javascripts/ci/runner/group_new_runner/group_new_runner_app.vue
index 67d29daf66f..2e1706ddae9 100644
--- a/app/assets/javascripts/ci/runner/group_new_runner/group_new_runner_app.vue
+++ b/app/assets/javascripts/ci/runner/group_new_runner/group_new_runner_app.vue
@@ -1,6 +1,6 @@
<script>
import { createAlert, VARIANT_SUCCESS } from '~/alert';
-import { redirectTo, setUrlParams } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
+import { visitUrl, setUrlParams } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
import RegistrationCompatibilityAlert from '~/ci/runner/components/registration/registration_compatibility_alert.vue';
@@ -38,7 +38,7 @@ export default {
message: s__('Runners|Runner created.'),
variant: VARIANT_SUCCESS,
});
- redirectTo(ephemeralRegisterUrl); // eslint-disable-line import/no-deprecated
+ visitUrl(ephemeralRegisterUrl);
},
onError(error) {
createAlert({ message: error.message });
@@ -66,7 +66,7 @@ export default {
<hr aria-hidden="true" />
- <h2 class="gl-font-weight-normal gl-font-lg gl-my-5">
+ <h2 class="gl-font-size-h2 gl-my-5">
{{ s__('Runners|Platform') }}
</h2>
<runner-platforms-radio-group v-model="platform" />
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 1318bf5a2e6..e885cf45c5a 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
@@ -2,7 +2,7 @@
import { createAlert, VARIANT_SUCCESS } from '~/alert';
import { TYPENAME_CI_RUNNER } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
-import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
+import { visitUrl } from '~/lib/utils/url_utility';
import RunnerDeleteButton from '../components/runner_delete_button.vue';
import RunnerEditButton from '../components/runner_edit_button.vue';
@@ -76,7 +76,7 @@ export default {
},
onDeleted({ message }) {
saveAlertToLocalStorage({ message, variant: VARIANT_SUCCESS });
- redirectTo(this.runnersPath); // eslint-disable-line import/no-deprecated
+ visitUrl(this.runnersPath);
},
},
};
diff --git a/app/assets/javascripts/ci/runner/project_new_runner/project_new_runner_app.vue b/app/assets/javascripts/ci/runner/project_new_runner/project_new_runner_app.vue
index f0ae54c0232..51f5a9ce8d9 100644
--- a/app/assets/javascripts/ci/runner/project_new_runner/project_new_runner_app.vue
+++ b/app/assets/javascripts/ci/runner/project_new_runner/project_new_runner_app.vue
@@ -1,6 +1,6 @@
<script>
import { createAlert, VARIANT_SUCCESS } from '~/alert';
-import { redirectTo, setUrlParams } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
+import { visitUrl, setUrlParams } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
import RegistrationCompatibilityAlert from '~/ci/runner/components/registration/registration_compatibility_alert.vue';
@@ -38,7 +38,7 @@ export default {
message: s__('Runners|Runner created.'),
variant: VARIANT_SUCCESS,
});
- redirectTo(ephemeralRegisterUrl); // eslint-disable-line import/no-deprecated
+ visitUrl(ephemeralRegisterUrl);
},
onError(error) {
createAlert({ message: error.message });
@@ -66,7 +66,7 @@ export default {
<hr aria-hidden="true" />
- <h2 class="gl-font-weight-normal gl-font-lg gl-my-5">
+ <h2 class="gl-font-size-h2 gl-my-5">
{{ s__('Runners|Platform') }}
</h2>
<runner-platforms-radio-group v-model="platform" />
diff --git a/app/assets/javascripts/ci/runner/runner_update_form_utils.js b/app/assets/javascripts/ci/runner/runner_update_form_utils.js
index 3b519fa7d71..6f6c9f64af0 100644
--- a/app/assets/javascripts/ci/runner/runner_update_form_utils.js
+++ b/app/assets/javascripts/ci/runner/runner_update_form_utils.js
@@ -4,7 +4,7 @@ export const runnerToModel = (runner) => {
description,
maximumTimeout,
accessLevel,
- active,
+ paused,
locked,
runUntagged,
tagList = [],
@@ -15,7 +15,7 @@ export const runnerToModel = (runner) => {
description,
maximumTimeout,
accessLevel,
- active,
+ paused,
locked,
runUntagged,
tagList: tagList.join(', '),
diff --git a/app/assets/javascripts/ci/runner/utils.js b/app/assets/javascripts/ci/runner/utils.js
index 1ca0a9e86b5..bb1ffca62ee 100644
--- a/app/assets/javascripts/ci/runner/utils.js
+++ b/app/assets/javascripts/ci/runner/utils.js
@@ -1,4 +1,5 @@
import { formatNumber } from '~/locale';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { RUNNER_JOB_COUNT_LIMIT } from './constants';
/**
@@ -81,3 +82,13 @@ export const getPaginationVariables = (pagination, pageSize = 10) => {
export const parseInterval = (interval) => {
return typeof interval === 'string' ? parseInt(interval, 10) : null;
};
+
+/**
+ * Creates formatted runner name
+ *
+ * @param {Object} runner - Runner object
+ * @returns Formatted name
+ */
+export const formatRunnerName = ({ id, shortSha }) => {
+ return `#${getIdFromGraphQLId(id)} (${shortSha})`;
+};