diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-12-20 16:37:47 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-12-20 16:37:47 +0300 |
commit | aee0a117a889461ce8ced6fcf73207fe017f1d99 (patch) | |
tree | 891d9ef189227a8445d83f35c1b0fc99573f4380 /app/assets/javascripts/runner | |
parent | 8d46af3258650d305f53b819eabf7ab18d22f59e (diff) |
Add latest changes from gitlab-org/gitlab@14-6-stable-eev14.6.0-rc42
Diffstat (limited to 'app/assets/javascripts/runner')
17 files changed, 214 insertions, 110 deletions
diff --git a/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue b/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue index 3edb658eaf5..f8220553db6 100644 --- a/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue +++ b/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue @@ -3,12 +3,12 @@ import { GlBadge, GlLink } from '@gitlab/ui'; import createFlash from '~/flash'; import { fetchPolicies } from '~/lib/graphql'; import { updateHistory } from '~/lib/utils/url_utility'; -import { sprintf, __ } from '~/locale'; import RegistrationDropdown from '../components/registration/registration_dropdown.vue'; import RunnerFilteredSearchBar from '../components/runner_filtered_search_bar.vue'; import RunnerList from '../components/runner_list.vue'; import RunnerName from '../components/runner_name.vue'; +import RunnerOnlineStat from '../components/stat/runner_online_stat.vue'; import RunnerPagination from '../components/runner_pagination.vue'; import RunnerTypeTabs from '../components/runner_type_tabs.vue'; @@ -38,6 +38,7 @@ export default { RunnerFilteredSearchBar, RunnerList, RunnerName, + RunnerOnlineStat, RunnerPagination, RunnerTypeTabs, }, @@ -110,17 +111,12 @@ export default { noRunnersFound() { return !this.runnersLoading && !this.runners.items.length; }, - activeRunnersMessage() { - return sprintf(__('Runners currently online: %{active_runners_count}'), { - active_runners_count: this.activeRunnersCount, - }); - }, searchTokens() { return [ statusTokenConfig, { ...tagTokenConfig, - recentTokenValuesStorageKey: `${this.$options.filteredSearchNamespace}-recent-tags`, + recentSuggestionsStorageKey: `${this.$options.filteredSearchNamespace}-recent-tags`, }, ]; }, @@ -165,6 +161,8 @@ export default { </script> <template> <div> + <runner-online-stat class="gl-py-6 gl-px-5" :value="activeRunnersCount" /> + <div class="gl-display-flex gl-align-items-center gl-flex-direction-column-reverse gl-md-flex-direction-row gl-mt-3 gl-md-mt-0" > @@ -194,11 +192,7 @@ export default { v-model="search" :tokens="searchTokens" :namespace="$options.filteredSearchNamespace" - > - <template #runner-count> - {{ activeRunnersMessage }} - </template> - </runner-filtered-search-bar> + /> <div v-if="noRunnersFound" class="gl-text-center gl-p-5"> {{ __('No runners found') }} diff --git a/app/assets/javascripts/runner/components/cells/runner_actions_cell.vue b/app/assets/javascripts/runner/components/cells/runner_actions_cell.vue index c4bddb7b398..33f7a67aba4 100644 --- a/app/assets/javascripts/runner/components/cells/runner_actions_cell.vue +++ b/app/assets/javascripts/runner/components/cells/runner_actions_cell.vue @@ -1,27 +1,29 @@ <script> -import { GlButton, GlButtonGroup, GlTooltipDirective } from '@gitlab/ui'; +import { GlButton, GlButtonGroup, GlModalDirective, GlTooltipDirective } from '@gitlab/ui'; import createFlash from '~/flash'; -import { __, s__ } from '~/locale'; +import { __, s__, sprintf } from '~/locale'; import runnerDeleteMutation from '~/runner/graphql/runner_delete.mutation.graphql'; import runnerActionsUpdateMutation from '~/runner/graphql/runner_actions_update.mutation.graphql'; import { captureException } from '~/runner/sentry_utils'; +import { getIdFromGraphQLId } from '~/graphql_shared/utils'; +import RunnerDeleteModal from '../runner_delete_modal.vue'; -const i18n = { - I18N_EDIT: __('Edit'), - I18N_PAUSE: __('Pause'), - I18N_RESUME: __('Resume'), - I18N_REMOVE: __('Remove'), - I18N_REMOVE_CONFIRMATION: s__('Runners|Are you sure you want to delete this runner?'), -}; +const I18N_EDIT = __('Edit'); +const I18N_PAUSE = __('Pause'); +const I18N_RESUME = __('Resume'); +const I18N_DELETE = s__('Runners|Delete runner'); +const I18N_DELETED_TOAST = s__('Runners|Runner %{name} was deleted'); export default { name: 'RunnerActionsCell', components: { GlButton, GlButtonGroup, + RunnerDeleteModal, }, directives: { GlTooltip: GlTooltipDirective, + GlModal: GlModalDirective, }, props: { runner: { @@ -48,21 +50,29 @@ export default { // mouseout listeners don't run leaving the tooltip stuck return ''; } - return this.isActive ? i18n.I18N_PAUSE : i18n.I18N_RESUME; + return this.isActive ? I18N_PAUSE : I18N_RESUME; }, deleteTitle() { - // Prevent a "sticky" tooltip: If element gets removed, - // mouseout listeners don't run and leaving the tooltip stuck - return this.deleting ? '' : i18n.I18N_REMOVE; + if (this.deleting) { + // Prevent a "sticky" tooltip: If this button is disabled, + // mouseout listeners don't run leaving the tooltip stuck + return ''; + } + return I18N_DELETE; + }, + runnerId() { + return getIdFromGraphQLId(this.runner.id); + }, + runnerName() { + return `#${this.runnerId} (${this.runner.shortSha})`; + }, + runnerDeleteModalId() { + return `delete-runner-modal-${this.runnerId}`; }, }, methods: { async onToggleActive() { this.updating = true; - // TODO In HAML iteration we had a confirmation modal via: - // data-confirm="_('Are you sure?')" - // this may not have to ported, this is an easily reversible operation - try { const toggledActive = !this.runner.active; @@ -91,12 +101,8 @@ export default { }, async onDelete() { - // TODO Replace confirmation with gl-modal - // eslint-disable-next-line no-alert - if (!window.confirm(i18n.I18N_REMOVE_CONFIRMATION)) { - return; - } - + // Deleting stays "true" until this row is removed, + // should only change back if the operation fails. this.deleting = true; try { const { @@ -115,11 +121,13 @@ export default { }); if (errors && errors.length) { throw new Error(errors.join(' ')); + } else { + // Use $root to have the toast message stay after this element is removed + this.$root.$toast?.show(sprintf(I18N_DELETED_TOAST, { name: this.runnerName })); } } catch (e) { - this.onError(e); - } finally { this.deleting = false; + this.onError(e); } }, @@ -133,14 +141,15 @@ export default { captureException({ error, component: this.$options.name }); }, }, - i18n, + I18N_EDIT, + I18N_DELETE, }; </script> <template> <gl-button-group> <!-- - This button appears for administratos: those with + This button appears for administrators: those with access to the adminUrl. More advanced permissions policies will allow more granular permissions. @@ -148,16 +157,14 @@ export default { --> <gl-button v-if="runner.adminUrl" - v-gl-tooltip.hover.viewport + v-gl-tooltip.hover.viewport="$options.I18N_EDIT" :href="runner.adminUrl" - :title="$options.i18n.I18N_EDIT" - :aria-label="$options.i18n.I18N_EDIT" + :aria-label="$options.I18N_EDIT" icon="pencil" data-testid="edit-runner" /> <gl-button - v-gl-tooltip.hover.viewport - :title="toggleActiveTitle" + v-gl-tooltip.hover.viewport="toggleActiveTitle" :aria-label="toggleActiveTitle" :icon="toggleActiveIcon" :loading="updating" @@ -165,14 +172,20 @@ export default { @click="onToggleActive" /> <gl-button - v-gl-tooltip.hover.viewport - :title="deleteTitle" + v-gl-tooltip.hover.viewport="deleteTitle" + v-gl-modal="runnerDeleteModalId" :aria-label="deleteTitle" icon="close" :loading="deleting" variant="danger" data-testid="delete-runner" - @click="onDelete" + /> + + <runner-delete-modal + :ref="runnerDeleteModalId" + :modal-id="runnerDeleteModalId" + :runner-name="runnerName" + @primary="onDelete" /> </gl-button-group> </template> diff --git a/app/assets/javascripts/runner/components/cells/runner_status_cell.vue b/app/assets/javascripts/runner/components/cells/runner_status_cell.vue index 9ba1192bc8c..473cd7e9794 100644 --- a/app/assets/javascripts/runner/components/cells/runner_status_cell.vue +++ b/app/assets/javascripts/runner/components/cells/runner_status_cell.vue @@ -1,14 +1,12 @@ <script> import { GlTooltipDirective } from '@gitlab/ui'; -import RunnerContactedStateBadge from '../runner_contacted_state_badge.vue'; +import RunnerStatusBadge from '../runner_status_badge.vue'; import RunnerPausedBadge from '../runner_paused_badge.vue'; -import { I18N_LOCKED_RUNNER_DESCRIPTION, I18N_PAUSED_RUNNER_DESCRIPTION } from '../../constants'; - export default { components: { - RunnerContactedStateBadge, + RunnerStatusBadge, RunnerPausedBadge, }, directives: { @@ -25,16 +23,12 @@ export default { return !this.runner.active; }, }, - i18n: { - I18N_LOCKED_RUNNER_DESCRIPTION, - I18N_PAUSED_RUNNER_DESCRIPTION, - }, }; </script> <template> <div> - <runner-contacted-state-badge :runner="runner" size="sm" /> + <runner-status-badge :runner="runner" size="sm" /> <runner-paused-badge v-if="paused" size="sm" /> </div> </template> diff --git a/app/assets/javascripts/runner/components/cells/runner_summary_cell.vue b/app/assets/javascripts/runner/components/cells/runner_summary_cell.vue index 3b476997915..937ec631633 100644 --- a/app/assets/javascripts/runner/components/cells/runner_summary_cell.vue +++ b/app/assets/javascripts/runner/components/cells/runner_summary_cell.vue @@ -1,7 +1,7 @@ <script> import { GlIcon, GlTooltipDirective } from '@gitlab/ui'; -import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue'; +import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue'; import RunnerName from '../runner_name.vue'; import RunnerTypeBadge from '../runner_type_badge.vue'; diff --git a/app/assets/javascripts/runner/components/runner_delete_modal.vue b/app/assets/javascripts/runner/components/runner_delete_modal.vue new file mode 100644 index 00000000000..8be216a7eb5 --- /dev/null +++ b/app/assets/javascripts/runner/components/runner_delete_modal.vue @@ -0,0 +1,51 @@ +<script> +import { GlModal } from '@gitlab/ui'; +import { __, s__, 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_CANCEL = __('Cancel'); + +export default { + components: { + GlModal, + }, + props: { + runnerName: { + type: String, + required: true, + }, + }, + computed: { + title() { + return sprintf(I18N_TITLE, { name: this.runnerName }); + }, + }, + methods: { + onPrimary() { + this.$refs.modal.hide(); + }, + }, + actionPrimary: { text: I18N_PRIMARY, attributes: { variant: 'danger' } }, + actionCancel: { text: I18N_CANCEL }, + I18N_BODY, +}; +</script> + +<template> + <gl-modal + ref="modal" + size="sm" + :title="title" + :action-primary="$options.actionPrimary" + :action-cancel="$options.actionCancel" + v-bind="$attrs" + v-on="$listeners" + @primary="onPrimary" + > + {{ $options.I18N_BODY }} + </gl-modal> +</template> diff --git a/app/assets/javascripts/runner/components/runner_filtered_search_bar.vue b/app/assets/javascripts/runner/components/runner_filtered_search_bar.vue index a9dfec35479..f0f8bbdf5df 100644 --- a/app/assets/javascripts/runner/components/runner_filtered_search_bar.vue +++ b/app/assets/javascripts/runner/components/runner_filtered_search_bar.vue @@ -76,24 +76,18 @@ export default { }; </script> <template> - <div + <filtered-search class="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" - > - <filtered-search - v-bind="$attrs" - :namespace="namespace" - recent-searches-storage-key="runners-search" - :sort-options="$options.sortOptions" - :initial-filter-value="initialFilterValue" - :tokens="tokens" - :initial-sort-by="initialSortBy" - :search-input-placeholder="__('Search or filter results...')" - data-testid="runners-filtered-search" - @onFilter="onFilter" - @onSort="onSort" - /> - <div class="gl-text-right" data-testid="runner-count"> - <slot name="runner-count"></slot> - </div> - </div> + v-bind="$attrs" + :namespace="namespace" + recent-searches-storage-key="runners-search" + :sort-options="$options.sortOptions" + :initial-filter-value="initialFilterValue" + :tokens="tokens" + :initial-sort-by="initialSortBy" + :search-input-placeholder="__('Search or filter results...')" + data-testid="runners-filtered-search" + @onFilter="onFilter" + @onSort="onSort" + /> </template> diff --git a/app/assets/javascripts/runner/components/runner_list.vue b/app/assets/javascripts/runner/components/runner_list.vue index f8dbc469c22..023308dbac2 100644 --- a/app/assets/javascripts/runner/components/runner_list.vue +++ b/app/assets/javascripts/runner/components/runner_list.vue @@ -1,27 +1,26 @@ <script> import { GlTable, GlTooltipDirective, GlSkeletonLoader } from '@gitlab/ui'; +import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; -import { __, s__ } from '~/locale'; +import { formatNumber, __, s__ } from '~/locale'; import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue'; +import { RUNNER_JOB_COUNT_LIMIT } from '../constants'; import RunnerActionsCell from './cells/runner_actions_cell.vue'; import RunnerSummaryCell from './cells/runner_summary_cell.vue'; import RunnerStatusCell from './cells/runner_status_cell.vue'; import RunnerTags from './runner_tags.vue'; -const tableField = ({ key, label = '', width = 10 }) => { +const tableField = ({ key, label = '', thClasses = [] }) => { return { key, label, thClass: [ - `gl-w-${width}p`, 'gl-bg-transparent!', 'gl-border-b-solid!', 'gl-border-b-gray-100!', - 'gl-py-5!', - 'gl-px-0!', 'gl-border-b-1!', + ...thClasses, ], - tdClass: ['gl-py-5!', 'gl-px-1!'], tdAttr: { 'data-testid': `td-${key}`, }, @@ -32,6 +31,7 @@ export default { components: { GlTable, GlSkeletonLoader, + TooltipOnTruncate, TimeAgo, RunnerActionsCell, RunnerSummaryCell, @@ -53,6 +53,12 @@ export default { }, }, methods: { + formatJobCount(jobCount) { + if (jobCount > RUNNER_JOB_COUNT_LIMIT) { + return `${formatNumber(RUNNER_JOB_COUNT_LIMIT)}+`; + } + return formatNumber(jobCount); + }, runnerTrAttr(runner) { if (runner) { return { @@ -64,10 +70,11 @@ export default { }, fields: [ tableField({ key: 'status', label: s__('Runners|Status') }), - tableField({ key: 'summary', label: s__('Runners|Runner ID'), width: 30 }), + tableField({ key: 'summary', label: s__('Runners|Runner ID'), thClasses: ['gl-lg-w-25p'] }), tableField({ key: 'version', label: __('Version') }), tableField({ key: 'ipAddress', label: __('IP Address') }), - tableField({ key: 'tagList', label: __('Tags'), width: 20 }), + tableField({ key: 'jobCount', label: __('Jobs') }), + tableField({ key: 'tagList', label: __('Tags'), thClasses: ['gl-lg-w-25p'] }), tableField({ key: 'contactedAt', label: __('Last contact') }), tableField({ key: 'actions', label: '' }), ], @@ -82,6 +89,7 @@ export default { :tbody-tr-attr="runnerTrAttr" data-testid="runner-list" stacked="md" + primary-key="id" fixed > <template v-if="!runners.length" #table-busy> @@ -101,11 +109,19 @@ export default { </template> <template #cell(version)="{ item: { version } }"> - {{ version }} + <tooltip-on-truncate class="gl-display-block gl-text-truncate" :title="version"> + {{ version }} + </tooltip-on-truncate> </template> <template #cell(ipAddress)="{ item: { ipAddress } }"> - {{ ipAddress }} + <tooltip-on-truncate class="gl-display-block gl-text-truncate" :title="ipAddress"> + {{ ipAddress }} + </tooltip-on-truncate> + </template> + + <template #cell(jobCount)="{ item: { jobCount } }"> + {{ formatJobCount(jobCount) }} </template> <template #cell(tagList)="{ item: { tagList } }"> diff --git a/app/assets/javascripts/runner/components/runner_contacted_state_badge.vue b/app/assets/javascripts/runner/components/runner_status_badge.vue index b4727f832f8..0823876a187 100644 --- a/app/assets/javascripts/runner/components/runner_contacted_state_badge.vue +++ b/app/assets/javascripts/runner/components/runner_status_badge.vue @@ -1,14 +1,17 @@ <script> import { GlBadge, GlTooltipDirective } from '@gitlab/ui'; -import { s__, sprintf } from '~/locale'; +import { __, s__, sprintf } from '~/locale'; import { getTimeago } from '~/lib/utils/datetime_utility'; import { - I18N_ONLINE_RUNNER_DESCRIPTION, - I18N_OFFLINE_RUNNER_DESCRIPTION, + I18N_ONLINE_RUNNER_TIMEAGO_DESCRIPTION, I18N_NOT_CONNECTED_RUNNER_DESCRIPTION, + I18N_OFFLINE_RUNNER_TIMEAGO_DESCRIPTION, + I18N_STALE_RUNNER_DESCRIPTION, STATUS_ONLINE, - STATUS_OFFLINE, STATUS_NOT_CONNECTED, + STATUS_NEVER_CONTACTED, + STATUS_OFFLINE, + STATUS_STALE, } from '../constants'; export default { @@ -29,31 +32,39 @@ export default { if (this.runner.contactedAt) { return getTimeago().format(this.runner.contactedAt); } - return null; + // Prevent "just now" from being rendered, in case data is missing. + return __('n/a'); }, badge() { - switch (this.runner.status) { + switch (this.runner?.status) { case STATUS_ONLINE: return { variant: 'success', label: s__('Runners|online'), - tooltip: sprintf(I18N_ONLINE_RUNNER_DESCRIPTION, { + tooltip: sprintf(I18N_ONLINE_RUNNER_TIMEAGO_DESCRIPTION, { timeAgo: this.contactedAtTimeAgo, }), }; + case STATUS_NOT_CONNECTED: + case STATUS_NEVER_CONTACTED: + return { + variant: 'muted', + label: s__('Runners|not connected'), + tooltip: I18N_NOT_CONNECTED_RUNNER_DESCRIPTION, + }; case STATUS_OFFLINE: return { variant: 'muted', label: s__('Runners|offline'), - tooltip: sprintf(I18N_OFFLINE_RUNNER_DESCRIPTION, { + tooltip: sprintf(I18N_OFFLINE_RUNNER_TIMEAGO_DESCRIPTION, { timeAgo: this.contactedAtTimeAgo, }), }; - case STATUS_NOT_CONNECTED: + case STATUS_STALE: return { - variant: 'muted', - label: s__('Runners|not connected'), - tooltip: I18N_NOT_CONNECTED_RUNNER_DESCRIPTION, + variant: 'warning', + label: s__('Runners|stale'), + tooltip: I18N_STALE_RUNNER_DESCRIPTION, }; default: return null; diff --git a/app/assets/javascripts/runner/components/search_tokens/status_token_config.js b/app/assets/javascripts/runner/components/search_tokens/status_token_config.js index 9963048ae1d..4b356fa47ed 100644 --- a/app/assets/javascripts/runner/components/search_tokens/status_token_config.js +++ b/app/assets/javascripts/runner/components/search_tokens/status_token_config.js @@ -7,6 +7,7 @@ import { STATUS_ONLINE, STATUS_OFFLINE, STATUS_NOT_CONNECTED, + STATUS_STALE, PARAM_KEY_STATUS, } from '../../constants'; @@ -16,6 +17,7 @@ const options = [ { value: STATUS_ONLINE, title: s__('Runners|Online') }, { value: STATUS_OFFLINE, title: s__('Runners|Offline') }, { value: STATUS_NOT_CONNECTED, title: s__('Runners|Not connected') }, + { value: STATUS_STALE, title: s__('Runners|Stale') }, ]; export const statusTokenConfig = { diff --git a/app/assets/javascripts/runner/components/search_tokens/tag_token.vue b/app/assets/javascripts/runner/components/search_tokens/tag_token.vue index ab67ac608e2..7461308ab91 100644 --- a/app/assets/javascripts/runner/components/search_tokens/tag_token.vue +++ b/app/assets/javascripts/runner/components/search_tokens/tag_token.vue @@ -68,7 +68,6 @@ export default { :config="config" :suggestions-loading="loading" :suggestions="tags" - :recent-suggestions-storage-key="config.recentTokenValuesStorageKey" @fetch-suggestions="fetchTags" v-on="$listeners" > diff --git a/app/assets/javascripts/runner/components/stat/runner_online_stat.vue b/app/assets/javascripts/runner/components/stat/runner_online_stat.vue new file mode 100644 index 00000000000..b92b9badef0 --- /dev/null +++ b/app/assets/javascripts/runner/components/stat/runner_online_stat.vue @@ -0,0 +1,17 @@ +<script> +import { GlSingleStat } from '@gitlab/ui/dist/charts'; + +export default { + components: { + GlSingleStat, + }, +}; +</script> +<template> + <gl-single-stat + v-bind="$attrs" + variant="success" + :title="s__('Runners|Online Runners')" + :meta-text="s__('Runners|online')" + /> +</template> diff --git a/app/assets/javascripts/runner/constants.js b/app/assets/javascripts/runner/constants.js index 3952e2398e0..355f3054917 100644 --- a/app/assets/javascripts/runner/constants.js +++ b/app/assets/javascripts/runner/constants.js @@ -1,6 +1,7 @@ import { s__ } from '~/locale'; export const RUNNER_PAGE_SIZE = 20; +export const RUNNER_JOB_COUNT_LIMIT = 1000; export const GROUP_RUNNER_COUNT_LIMIT = 1000; export const I18N_FETCH_ERROR = s__('Runners|Something went wrong while fetching runner data.'); @@ -14,15 +15,18 @@ export const I18N_GROUP_RUNNER_DESCRIPTION = s__( export const I18N_PROJECT_RUNNER_DESCRIPTION = s__('Runners|Associated with one or more projects'); // Status -export const I18N_ONLINE_RUNNER_DESCRIPTION = s__( +export const I18N_ONLINE_RUNNER_TIMEAGO_DESCRIPTION = s__( 'Runners|Runner is online; last contact was %{timeAgo}', ); -export const I18N_OFFLINE_RUNNER_DESCRIPTION = s__( - 'Runners|No recent contact from this runner; last contact was %{timeAgo}', -); export const I18N_NOT_CONNECTED_RUNNER_DESCRIPTION = s__( 'Runners|This runner has never connected to this instance', ); +export const I18N_OFFLINE_RUNNER_TIMEAGO_DESCRIPTION = s__( + 'Runners|No recent contact from this runner; last contact was %{timeAgo}', +); +export const I18N_STALE_RUNNER_DESCRIPTION = s__( + 'Runners|No contact from this runner in over 3 months', +); export const I18N_LOCKED_RUNNER_DESCRIPTION = s__('Runners|You cannot assign to other projects'); export const I18N_PAUSED_RUNNER_DESCRIPTION = s__('Runners|Not available to run jobs'); @@ -54,9 +58,12 @@ export const PROJECT_TYPE = 'PROJECT_TYPE'; export const STATUS_ACTIVE = 'ACTIVE'; export const STATUS_PAUSED = 'PAUSED'; + export const STATUS_ONLINE = 'ONLINE'; -export const STATUS_OFFLINE = 'OFFLINE'; export const STATUS_NOT_CONNECTED = 'NOT_CONNECTED'; +export const STATUS_NEVER_CONTACTED = 'NEVER_CONTACTED'; +export const STATUS_OFFLINE = 'OFFLINE'; +export const STATUS_STALE = 'STALE'; // CiRunnerAccessLevel diff --git a/app/assets/javascripts/runner/graphql/get_group_runners.query.graphql b/app/assets/javascripts/runner/graphql/get_group_runners.query.graphql index 3e5109b1ac4..6da9e276f74 100644 --- a/app/assets/javascripts/runner/graphql/get_group_runners.query.graphql +++ b/app/assets/javascripts/runner/graphql/get_group_runners.query.graphql @@ -13,6 +13,7 @@ query getGroupRunners( $sort: CiRunnerSort ) { group(fullPath: $groupFullPath) { + id runners( membership: DESCENDANTS before: $before diff --git a/app/assets/javascripts/runner/graphql/get_runner.query.graphql b/app/assets/javascripts/runner/graphql/get_runner.query.graphql index c294cb9bf22..59c55eae060 100644 --- a/app/assets/javascripts/runner/graphql/get_runner.query.graphql +++ b/app/assets/javascripts/runner/graphql/get_runner.query.graphql @@ -1,6 +1,8 @@ #import "ee_else_ce/runner/graphql/runner_details.fragment.graphql" query getRunner($id: CiRunnerID!) { + # We have an id in deeply nested fragment + # eslint-disable-next-line @graphql-eslint/require-id-when-available runner(id: $id) { ...RunnerDetails } diff --git a/app/assets/javascripts/runner/graphql/runner_node.fragment.graphql b/app/assets/javascripts/runner/graphql/runner_node.fragment.graphql index 98f2dab26ca..169f6ffd2ea 100644 --- a/app/assets/javascripts/runner/graphql/runner_node.fragment.graphql +++ b/app/assets/javascripts/runner/graphql/runner_node.fragment.graphql @@ -8,7 +8,8 @@ fragment RunnerNode on CiRunner { ipAddress active locked + jobCount tagList contactedAt - status + status(legacyMode: null) } diff --git a/app/assets/javascripts/runner/graphql/runner_update.mutation.graphql b/app/assets/javascripts/runner/graphql/runner_update.mutation.graphql index ea622fd4958..8d1b75828be 100644 --- a/app/assets/javascripts/runner/graphql/runner_update.mutation.graphql +++ b/app/assets/javascripts/runner/graphql/runner_update.mutation.graphql @@ -5,6 +5,8 @@ mutation runnerUpdate($input: RunnerUpdateInput!) { runnerUpdate(input: $input) { + # We have an id in deep nested fragment + # eslint-disable-next-line @graphql-eslint/require-id-when-available runner { ...RunnerDetails } diff --git a/app/assets/javascripts/runner/group_runners/group_runners_app.vue b/app/assets/javascripts/runner/group_runners/group_runners_app.vue index c3dfa885f27..a58a53a6a0d 100644 --- a/app/assets/javascripts/runner/group_runners/group_runners_app.vue +++ b/app/assets/javascripts/runner/group_runners/group_runners_app.vue @@ -9,6 +9,7 @@ import RegistrationDropdown from '../components/registration/registration_dropdo import RunnerFilteredSearchBar from '../components/runner_filtered_search_bar.vue'; import RunnerList from '../components/runner_list.vue'; import RunnerName from '../components/runner_name.vue'; +import RunnerOnlineStat from '../components/stat/runner_online_stat.vue'; import RunnerPagination from '../components/runner_pagination.vue'; import RunnerTypeTabs from '../components/runner_type_tabs.vue'; @@ -35,6 +36,7 @@ export default { RunnerFilteredSearchBar, RunnerList, RunnerName, + RunnerOnlineStat, RunnerPagination, RunnerTypeTabs, }, @@ -145,6 +147,8 @@ export default { <template> <div> + <runner-online-stat class="gl-py-6 gl-px-5" :value="groupRunnersCount" /> + <div class="gl-display-flex gl-align-items-center"> <runner-type-tabs v-model="search" @@ -164,11 +168,7 @@ export default { v-model="search" :tokens="searchTokens" :namespace="filteredSearchNamespace" - > - <template #runner-count> - {{ runnerCountMessage }} - </template> - </runner-filtered-search-bar> + /> <div v-if="noRunnersFound" class="gl-text-center gl-p-5"> {{ __('No runners found') }} |