2021-04-07 18:09:45 +00:00
|
|
|
<script>
|
|
|
|
import {
|
|
|
|
GlAlert,
|
|
|
|
GlButton,
|
|
|
|
GlModal,
|
|
|
|
GlButtonGroup,
|
|
|
|
GlDropdown,
|
|
|
|
GlDropdownItem,
|
|
|
|
GlIcon,
|
|
|
|
GlLoadingIcon,
|
|
|
|
GlSkeletonLoader,
|
2021-04-30 15:09:50 +00:00
|
|
|
GlResizeObserverDirective,
|
2021-04-07 18:09:45 +00:00
|
|
|
} from '@gitlab/ui';
|
2021-04-30 15:09:50 +00:00
|
|
|
import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
|
2021-04-07 18:09:45 +00:00
|
|
|
import { isEmpty } from 'lodash';
|
|
|
|
import { __, s__ } from '~/locale';
|
|
|
|
import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue';
|
|
|
|
import {
|
|
|
|
INSTRUCTIONS_PLATFORMS_WITHOUT_ARCHITECTURES,
|
2021-10-25 18:12:16 +00:00
|
|
|
REGISTRATION_TOKEN_PLACEHOLDER,
|
2021-04-07 18:09:45 +00:00
|
|
|
} from './constants';
|
|
|
|
import getRunnerPlatformsQuery from './graphql/queries/get_runner_platforms.query.graphql';
|
|
|
|
import getRunnerSetupInstructionsQuery from './graphql/queries/get_runner_setup.query.graphql';
|
|
|
|
|
|
|
|
export default {
|
|
|
|
components: {
|
|
|
|
GlAlert,
|
|
|
|
GlButton,
|
|
|
|
GlButtonGroup,
|
|
|
|
GlDropdown,
|
|
|
|
GlDropdownItem,
|
|
|
|
GlModal,
|
|
|
|
GlIcon,
|
|
|
|
GlLoadingIcon,
|
|
|
|
GlSkeletonLoader,
|
|
|
|
ModalCopyButton,
|
|
|
|
},
|
2021-04-30 15:09:50 +00:00
|
|
|
directives: {
|
|
|
|
GlResizeObserver: GlResizeObserverDirective,
|
|
|
|
},
|
2021-04-07 18:09:45 +00:00
|
|
|
props: {
|
|
|
|
modalId: {
|
|
|
|
type: String,
|
2021-10-22 18:13:20 +00:00
|
|
|
required: false,
|
|
|
|
default: 'runner-instructions-modal',
|
2021-04-07 18:09:45 +00:00
|
|
|
},
|
2021-10-25 18:12:16 +00:00
|
|
|
registrationToken: {
|
|
|
|
type: String,
|
|
|
|
required: false,
|
|
|
|
default: null,
|
|
|
|
},
|
2022-05-02 09:10:46 +00:00
|
|
|
defaultPlatformName: {
|
|
|
|
type: String,
|
|
|
|
required: false,
|
|
|
|
default: null,
|
|
|
|
},
|
2021-04-07 18:09:45 +00:00
|
|
|
},
|
|
|
|
apollo: {
|
|
|
|
platforms: {
|
|
|
|
query: getRunnerPlatformsQuery,
|
2022-06-07 15:08:12 +00:00
|
|
|
skip() {
|
|
|
|
// Only load instructions once the modal is shown
|
|
|
|
return !this.shown;
|
|
|
|
},
|
2021-04-07 18:09:45 +00:00
|
|
|
update(data) {
|
2022-06-01 12:09:17 +00:00
|
|
|
return (
|
|
|
|
data?.runnerPlatforms?.nodes.map(({ name, humanReadableName, architectures }) => {
|
|
|
|
return {
|
|
|
|
name,
|
|
|
|
humanReadableName,
|
|
|
|
architectures: architectures?.nodes || [],
|
|
|
|
};
|
|
|
|
}) ?? []
|
|
|
|
);
|
2021-04-07 18:09:45 +00:00
|
|
|
},
|
|
|
|
result() {
|
2022-06-01 12:09:17 +00:00
|
|
|
// If it is set and available, select the defaultSelectedPlatform.
|
|
|
|
// Otherwise, select the first available platform
|
|
|
|
this.selectPlatform(this.defaultPlatformName || this.platforms?.[0].name);
|
2021-04-07 18:09:45 +00:00
|
|
|
},
|
|
|
|
error() {
|
|
|
|
this.toggleAlert(true);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
instructions: {
|
|
|
|
query: getRunnerSetupInstructionsQuery,
|
|
|
|
skip() {
|
2022-06-07 15:08:12 +00:00
|
|
|
return !this.shown || !this.selectedPlatform;
|
2021-04-07 18:09:45 +00:00
|
|
|
},
|
|
|
|
variables() {
|
|
|
|
return {
|
2022-06-01 12:09:17 +00:00
|
|
|
platform: this.selectedPlatform,
|
|
|
|
architecture: this.selectedArchitecture || '',
|
2021-04-07 18:09:45 +00:00
|
|
|
};
|
|
|
|
},
|
|
|
|
update(data) {
|
|
|
|
return data?.runnerSetup;
|
|
|
|
},
|
|
|
|
error() {
|
|
|
|
this.toggleAlert(true);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
data() {
|
|
|
|
return {
|
2022-06-07 15:08:12 +00:00
|
|
|
shown: false,
|
2021-04-07 18:09:45 +00:00
|
|
|
platforms: [],
|
|
|
|
selectedPlatform: null,
|
|
|
|
selectedArchitecture: null,
|
|
|
|
showAlert: false,
|
|
|
|
instructions: {},
|
2021-04-30 15:09:50 +00:00
|
|
|
platformsButtonGroupVertical: false,
|
2021-04-07 18:09:45 +00:00
|
|
|
};
|
|
|
|
},
|
|
|
|
computed: {
|
|
|
|
instructionsEmpty() {
|
|
|
|
return isEmpty(this.instructions);
|
|
|
|
},
|
2022-06-01 12:09:17 +00:00
|
|
|
architectures() {
|
|
|
|
return this.platforms.find(({ name }) => name === this.selectedPlatform)?.architectures || [];
|
2021-04-07 18:09:45 +00:00
|
|
|
},
|
2022-06-01 12:09:17 +00:00
|
|
|
binaryUrl() {
|
|
|
|
return this.architectures.find(({ name }) => name === this.selectedArchitecture)
|
|
|
|
?.downloadLocation;
|
2021-04-07 18:09:45 +00:00
|
|
|
},
|
|
|
|
instructionsWithoutArchitecture() {
|
2022-06-01 12:09:17 +00:00
|
|
|
return INSTRUCTIONS_PLATFORMS_WITHOUT_ARCHITECTURES[this.selectedPlatform]?.instructions;
|
2021-04-07 18:09:45 +00:00
|
|
|
},
|
|
|
|
runnerInstallationLink() {
|
2022-06-01 12:09:17 +00:00
|
|
|
return INSTRUCTIONS_PLATFORMS_WITHOUT_ARCHITECTURES[this.selectedPlatform]?.link;
|
2021-04-07 18:09:45 +00:00
|
|
|
},
|
2021-10-25 18:12:16 +00:00
|
|
|
registerInstructionsWithToken() {
|
|
|
|
const { registerInstructions } = this.instructions || {};
|
|
|
|
|
|
|
|
if (this.registrationToken) {
|
2022-05-31 12:09:12 +00:00
|
|
|
return registerInstructions?.replace(
|
|
|
|
REGISTRATION_TOKEN_PLACEHOLDER,
|
|
|
|
this.registrationToken,
|
|
|
|
);
|
2021-10-25 18:12:16 +00:00
|
|
|
}
|
|
|
|
return registerInstructions;
|
|
|
|
},
|
2021-04-07 18:09:45 +00:00
|
|
|
},
|
2022-06-07 15:08:12 +00:00
|
|
|
updated() {
|
|
|
|
// Refocus on dom changes, after loading data
|
|
|
|
this.refocusSelectedPlatformButton();
|
|
|
|
},
|
2021-04-07 18:09:45 +00:00
|
|
|
methods: {
|
2021-10-22 18:13:20 +00:00
|
|
|
show() {
|
|
|
|
this.$refs.modal.show();
|
|
|
|
},
|
2022-06-07 15:08:12 +00:00
|
|
|
onShown() {
|
|
|
|
this.shown = true;
|
|
|
|
this.refocusSelectedPlatformButton();
|
|
|
|
},
|
|
|
|
refocusSelectedPlatformButton() {
|
|
|
|
// On modal opening, the first focusable element is auto-focused by bootstrap-vue
|
|
|
|
// This can be confusing for users, because the wrong platform button can
|
|
|
|
// get focused when setting a `defaultPlatformName`.
|
|
|
|
// This method refocuses the expected button.
|
|
|
|
// See more about this auto-focus: https://bootstrap-vue.org/docs/components/modal#auto-focus-on-open
|
2022-06-01 12:09:17 +00:00
|
|
|
this.$refs[this.selectedPlatform]?.[0].$el.focus();
|
2022-05-02 09:10:46 +00:00
|
|
|
},
|
2022-06-01 12:09:17 +00:00
|
|
|
selectPlatform(platformName) {
|
|
|
|
this.selectedPlatform = platformName;
|
2021-04-07 18:09:45 +00:00
|
|
|
|
2022-05-31 12:09:12 +00:00
|
|
|
// Update architecture when platform changes
|
2022-06-01 12:09:17 +00:00
|
|
|
const arch = this.architectures.find(({ name }) => name === this.selectedArchitecture);
|
2022-05-31 12:09:12 +00:00
|
|
|
if (arch) {
|
2022-06-01 12:09:17 +00:00
|
|
|
this.selectArchitecture(arch.name);
|
2022-05-31 12:09:12 +00:00
|
|
|
} else {
|
2022-06-01 12:09:17 +00:00
|
|
|
this.selectArchitecture(this.architectures[0]?.name);
|
2021-04-07 18:09:45 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
selectArchitecture(architecture) {
|
|
|
|
this.selectedArchitecture = architecture;
|
|
|
|
},
|
|
|
|
toggleAlert(state) {
|
|
|
|
this.showAlert = state;
|
|
|
|
},
|
2021-04-30 15:09:50 +00:00
|
|
|
onPlatformsButtonResize() {
|
|
|
|
if (bp.getBreakpointSize() === 'xs') {
|
|
|
|
this.platformsButtonGroupVertical = true;
|
|
|
|
} else {
|
|
|
|
this.platformsButtonGroupVertical = false;
|
|
|
|
}
|
|
|
|
},
|
2021-04-07 18:09:45 +00:00
|
|
|
},
|
|
|
|
i18n: {
|
2022-06-07 15:08:12 +00:00
|
|
|
environment: __('Environment'),
|
2021-04-07 18:09:45 +00:00
|
|
|
installARunner: s__('Runners|Install a runner'),
|
|
|
|
architecture: s__('Runners|Architecture'),
|
|
|
|
downloadInstallBinary: s__('Runners|Download and install binary'),
|
|
|
|
downloadLatestBinary: s__('Runners|Download latest binary'),
|
|
|
|
registerRunnerCommand: s__('Runners|Command to register runner'),
|
|
|
|
fetchError: s__('Runners|An error has occurred fetching instructions'),
|
|
|
|
copyInstructions: s__('Runners|Copy instructions'),
|
2022-06-07 15:08:12 +00:00
|
|
|
viewInstallationInstructions: s__('Runners|View installation instructions'),
|
2021-04-07 18:09:45 +00:00
|
|
|
},
|
|
|
|
closeButton: {
|
|
|
|
text: __('Close'),
|
|
|
|
attributes: [{ variant: 'default' }],
|
|
|
|
},
|
|
|
|
};
|
|
|
|
</script>
|
|
|
|
<template>
|
|
|
|
<gl-modal
|
2021-10-22 18:13:20 +00:00
|
|
|
ref="modal"
|
2021-04-07 18:09:45 +00:00
|
|
|
:modal-id="modalId"
|
|
|
|
:title="$options.i18n.installARunner"
|
|
|
|
:action-secondary="$options.closeButton"
|
2021-10-22 18:13:20 +00:00
|
|
|
v-bind="$attrs"
|
2022-05-02 09:10:46 +00:00
|
|
|
v-on="$listeners"
|
2022-06-07 15:08:12 +00:00
|
|
|
@shown="onShown"
|
2021-04-07 18:09:45 +00:00
|
|
|
>
|
|
|
|
<gl-alert v-if="showAlert" variant="danger" @dismiss="toggleAlert(false)">
|
|
|
|
{{ $options.i18n.fetchError }}
|
|
|
|
</gl-alert>
|
|
|
|
|
2022-06-01 12:09:17 +00:00
|
|
|
<gl-skeleton-loader v-if="!platforms.length && $apollo.loading" />
|
2021-04-07 18:09:45 +00:00
|
|
|
|
2022-06-01 12:09:17 +00:00
|
|
|
<template v-if="platforms.length">
|
2021-04-07 18:09:45 +00:00
|
|
|
<h5>
|
2022-06-07 15:08:12 +00:00
|
|
|
{{ $options.i18n.environment }}
|
2021-04-07 18:09:45 +00:00
|
|
|
</h5>
|
2021-04-30 15:09:50 +00:00
|
|
|
<div v-gl-resize-observer="onPlatformsButtonResize">
|
|
|
|
<gl-button-group
|
|
|
|
:vertical="platformsButtonGroupVertical"
|
|
|
|
:class="{ 'gl-w-full': platformsButtonGroupVertical }"
|
|
|
|
class="gl-mb-3"
|
|
|
|
data-testid="platform-buttons"
|
2021-04-07 18:09:45 +00:00
|
|
|
>
|
2021-04-30 15:09:50 +00:00
|
|
|
<gl-button
|
|
|
|
v-for="platform in platforms"
|
|
|
|
:key="platform.name"
|
2022-05-02 09:10:46 +00:00
|
|
|
:ref="platform.name"
|
2022-06-01 12:09:17 +00:00
|
|
|
:selected="selectedPlatform === platform.name"
|
|
|
|
@click="selectPlatform(platform.name)"
|
2021-04-30 15:09:50 +00:00
|
|
|
>
|
|
|
|
{{ platform.humanReadableName }}
|
|
|
|
</gl-button>
|
|
|
|
</gl-button-group>
|
|
|
|
</div>
|
2021-04-07 18:09:45 +00:00
|
|
|
</template>
|
2022-06-01 12:09:17 +00:00
|
|
|
<template v-if="architectures.length">
|
2021-04-07 18:09:45 +00:00
|
|
|
<template v-if="selectedPlatform">
|
|
|
|
<h5>
|
|
|
|
{{ $options.i18n.architecture }}
|
2021-07-07 09:08:35 +00:00
|
|
|
<gl-loading-icon v-if="$apollo.loading" size="sm" inline />
|
2021-04-07 18:09:45 +00:00
|
|
|
</h5>
|
|
|
|
|
2022-06-01 12:09:17 +00:00
|
|
|
<gl-dropdown class="gl-mb-3" :text="selectedArchitecture">
|
2021-04-07 18:09:45 +00:00
|
|
|
<gl-dropdown-item
|
2022-06-01 12:09:17 +00:00
|
|
|
v-for="architecture in architectures"
|
2021-04-07 18:09:45 +00:00
|
|
|
:key="architecture.name"
|
|
|
|
:is-check-item="true"
|
2022-06-01 12:09:17 +00:00
|
|
|
:is-checked="selectedArchitecture === architecture.name"
|
2021-04-07 18:09:45 +00:00
|
|
|
data-testid="architecture-dropdown-item"
|
2022-06-01 12:09:17 +00:00
|
|
|
@click="selectArchitecture(architecture.name)"
|
2021-04-07 18:09:45 +00:00
|
|
|
>
|
|
|
|
{{ architecture.name }}
|
|
|
|
</gl-dropdown-item>
|
|
|
|
</gl-dropdown>
|
2021-04-30 15:09:50 +00:00
|
|
|
<div class="gl-sm-display-flex gl-align-items-center gl-mb-3">
|
2021-04-07 18:09:45 +00:00
|
|
|
<h5>{{ $options.i18n.downloadInstallBinary }}</h5>
|
|
|
|
<gl-button
|
2022-06-01 12:09:17 +00:00
|
|
|
v-if="binaryUrl"
|
2021-04-07 18:09:45 +00:00
|
|
|
class="gl-ml-auto"
|
2022-06-01 12:09:17 +00:00
|
|
|
:href="binaryUrl"
|
2021-04-07 18:09:45 +00:00
|
|
|
download
|
|
|
|
icon="download"
|
|
|
|
data-testid="binary-download-button"
|
|
|
|
>
|
|
|
|
{{ $options.i18n.downloadLatestBinary }}
|
|
|
|
</gl-button>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
<template v-if="!instructionsEmpty">
|
|
|
|
<div class="gl-display-flex">
|
|
|
|
<pre
|
2021-06-15 12:10:11 +00:00
|
|
|
class="gl-bg-gray gl-flex-grow-1 gl-white-space-pre-line"
|
2021-04-07 18:09:45 +00:00
|
|
|
data-testid="binary-instructions"
|
|
|
|
>{{ instructions.installInstructions }}</pre
|
|
|
|
>
|
|
|
|
<modal-copy-button
|
|
|
|
:title="$options.i18n.copyInstructions"
|
|
|
|
:text="instructions.installInstructions"
|
|
|
|
:modal-id="$options.modalId"
|
|
|
|
css-classes="gl-align-self-start gl-ml-2 gl-mt-2"
|
|
|
|
category="tertiary"
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<h5 class="gl-mb-3">{{ $options.i18n.registerRunnerCommand }}</h5>
|
|
|
|
<div class="gl-display-flex">
|
|
|
|
<pre
|
2021-06-15 12:10:11 +00:00
|
|
|
class="gl-bg-gray gl-flex-grow-1 gl-white-space-pre-line"
|
2021-04-07 18:09:45 +00:00
|
|
|
data-testid="register-command"
|
2021-10-25 18:12:16 +00:00
|
|
|
>{{ registerInstructionsWithToken }}</pre
|
2021-04-07 18:09:45 +00:00
|
|
|
>
|
|
|
|
<modal-copy-button
|
|
|
|
:title="$options.i18n.copyInstructions"
|
2021-10-25 18:12:16 +00:00
|
|
|
:text="registerInstructionsWithToken"
|
2021-04-07 18:09:45 +00:00
|
|
|
:modal-id="$options.modalId"
|
|
|
|
css-classes="gl-align-self-start gl-ml-2 gl-mt-2"
|
|
|
|
category="tertiary"
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
<template v-else>
|
|
|
|
<div>
|
|
|
|
<p>{{ instructionsWithoutArchitecture }}</p>
|
|
|
|
<gl-button :href="runnerInstallationLink">
|
|
|
|
<gl-icon name="external-link" />
|
2022-06-07 15:08:12 +00:00
|
|
|
{{ $options.i18n.viewInstallationInstructions }}
|
2021-04-07 18:09:45 +00:00
|
|
|
</gl-button>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
</gl-modal>
|
|
|
|
</template>
|