diff options
Diffstat (limited to 'app/assets/javascripts')
17 files changed, 450 insertions, 62 deletions
diff --git a/app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue b/app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue index d377f0f2654..432271d2075 100644 --- a/app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue +++ b/app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue @@ -1,5 +1,5 @@ <script> -import { GlTable, GlIcon, GlTooltipDirective } from '@gitlab/ui'; +import { GlTable, GlIcon, GlTooltipDirective, GlLoadingIcon } from '@gitlab/ui'; import { s__, __ } from '~/locale'; import Tracking from '~/tracking'; import { trackAlertIntegrationsViewsOptions } from '../constants'; @@ -27,6 +27,7 @@ export default { components: { GlTable, GlIcon, + GlLoadingIcon, }, directives: { GlTooltip: GlTooltipDirective, @@ -37,10 +38,15 @@ export default { required: false, default: () => [], }, + loading: { + type: Boolean, + required: false, + default: false, + }, }, fields: [ { - key: 'activated', + key: 'active', label: __('Status'), }, { @@ -78,12 +84,13 @@ export default { :empty-text="$options.i18n.emptyState" :items="integrations" :fields="$options.fields" + :busy="loading" stacked="md" :tbody-tr-class="tbodyTrClass" show-empty > - <template #cell(activated)="{ item }"> - <span v-if="item.activated" data-testid="integration-activated-status"> + <template #cell(active)="{ item }"> + <span v-if="item.active" data-testid="integration-activated-status"> <gl-icon v-gl-tooltip name="check-circle-filled" @@ -104,6 +111,10 @@ export default { {{ $options.i18n.status.disabled.name }} </span> </template> + + <template #table-busy> + <gl-loading-icon size="lg" color="dark" class="mt-3" /> + </template> </gl-table> </div> </template> diff --git a/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue b/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue index 1edb8f1c921..1576faf13a9 100644 --- a/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue +++ b/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue @@ -1,6 +1,8 @@ <script> import { s__ } from '~/locale'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; +import { fetchPolicies } from '~/lib/graphql'; +import getIntegrationsQuery from '../graphql/queries/get_integrations.query.graphql'; import IntegrationsList from './alerts_integrations_list.vue'; import SettingsFormOld from './alerts_settings_form_old.vue'; import SettingsFormNew from './alerts_settings_form_new.vue'; @@ -19,19 +21,52 @@ export default { prometheus: { default: {}, }, + projectPath: { + default: '', + }, + }, + apollo: { + integrations: { + fetchPolicy: fetchPolicies.CACHE_AND_NETWORK, + query: getIntegrationsQuery, + variables() { + return { + projectPath: this.projectPath, + }; + }, + update(data) { + const { alertManagementIntegrations: { nodes: list = [] } = {} } = data.project || {}; + + return { + list, + }; + }, + error() { + this.errored = true; + }, + }, + }, + data() { + return { + errored: false, + integrations: {}, + }; }, computed: { - integrations() { + loading() { + return this.$apollo.queries.integrations.loading; + }, + intergrationsOptionsOld() { return [ { name: s__('AlertSettings|HTTP endpoint'), type: s__('AlertsIntegrations|HTTP endpoint'), - activated: this.generic.activated, + active: this.generic.activated, }, { name: s__('AlertSettings|External Prometheus'), type: s__('AlertsIntegrations|Prometheus'), - activated: this.prometheus.activated, + active: this.prometheus.activated, }, ]; }, @@ -41,7 +76,10 @@ export default { <template> <div> - <integrations-list :integrations="integrations" /> + <integrations-list + :integrations="glFeatures.httpIntegrationsList ? integrations.list : intergrationsOptionsOld" + :loading="loading" + /> <settings-form-new v-if="glFeatures.httpIntegrationsList" /> <settings-form-old v-else /> </div> diff --git a/app/assets/javascripts/alerts_settings/graphql/fragments/integration_item.fragment.graphql b/app/assets/javascripts/alerts_settings/graphql/fragments/integration_item.fragment.graphql new file mode 100644 index 00000000000..6d9307959df --- /dev/null +++ b/app/assets/javascripts/alerts_settings/graphql/fragments/integration_item.fragment.graphql @@ -0,0 +1,9 @@ +fragment IntegrationItem on AlertManagementIntegration { + id + type + active + name + url + token + apiUrl +} diff --git a/app/assets/javascripts/alerts_settings/graphql/queries/get_integrations.query.graphql b/app/assets/javascripts/alerts_settings/graphql/queries/get_integrations.query.graphql new file mode 100644 index 00000000000..228dd5fb176 --- /dev/null +++ b/app/assets/javascripts/alerts_settings/graphql/queries/get_integrations.query.graphql @@ -0,0 +1,11 @@ +#import "../fragments/integration_item.fragment.graphql" + +query getIntegrations($projectPath: ID!) { + project(fullPath: $projectPath) { + alertManagementIntegrations { + nodes { + ...IntegrationItem + } + } + } +} diff --git a/app/assets/javascripts/alerts_settings/index.js b/app/assets/javascripts/alerts_settings/index.js index 80f06a094b7..8f22b8d1dcd 100644 --- a/app/assets/javascripts/alerts_settings/index.js +++ b/app/assets/javascripts/alerts_settings/index.js @@ -1,7 +1,11 @@ import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import createDefaultClient from '~/lib/graphql'; import { parseBoolean } from '~/lib/utils/common_utils'; import AlertSettingsWrapper from './components/alerts_settings_wrapper.vue'; +Vue.use(VueApollo); + export default el => { if (!el) { return null; @@ -24,8 +28,22 @@ export default el => { opsgenieMvcFormPath, opsgenieMvcEnabled, opsgenieMvcTargetUrl, + projectPath, } = el.dataset; + const apolloProvider = new VueApollo({ + defaultClient: createDefaultClient( + {}, + { + cacheConfig: {}, + }, + ), + }); + + apolloProvider.clients.defaultClient.cache.writeData({ + data: {}, + }); + return new Vue({ el, provide: { @@ -51,7 +69,9 @@ export default el => { opsgenieMvcTargetUrl, opsgenieMvcIsAvailable: parseBoolean(opsgenieMvcAvailable), }, + projectPath, }, + apolloProvider, components: { AlertSettingsWrapper, }, diff --git a/app/assets/javascripts/feature_flags/components/edit_feature_flag.vue b/app/assets/javascripts/feature_flags/components/edit_feature_flag.vue index 26b18f9bf5a..9ec65bb0b43 100644 --- a/app/assets/javascripts/feature_flags/components/edit_feature_flag.vue +++ b/app/assets/javascripts/feature_flags/components/edit_feature_flag.vue @@ -103,7 +103,7 @@ export default { > {{ $options.translations.newFlagAlert }} </gl-alert> - <gl-loading-icon v-if="isLoading" /> + <gl-loading-icon v-if="isLoading" size="xl" class="gl-mt-7" /> <template v-else-if="!isLoading && !hasError"> <gl-alert v-if="deprecatedAndEditable" variant="warning" :dismissible="false" class="gl-my-5"> diff --git a/app/assets/javascripts/pages/admin/runners/index.js b/app/assets/javascripts/pages/admin/runners/index.js index e60c6133c7c..104b7eeaf96 100644 --- a/app/assets/javascripts/pages/admin/runners/index.js +++ b/app/assets/javascripts/pages/admin/runners/index.js @@ -1,11 +1,12 @@ import initFilteredSearch from '~/pages/search/init_filtered_search'; import AdminRunnersFilteredSearchTokenKeys from '~/filtered_search/admin_runners_filtered_search_token_keys'; import { FILTERED_SEARCH } from '~/pages/constants'; +import { initInstallRunner } from '~/pages/shared/mount_runner_instructions'; -document.addEventListener('DOMContentLoaded', () => { - initFilteredSearch({ - page: FILTERED_SEARCH.ADMIN_RUNNERS, - filteredSearchTokenKeys: AdminRunnersFilteredSearchTokenKeys, - useDefaultState: true, - }); +initFilteredSearch({ + page: FILTERED_SEARCH.ADMIN_RUNNERS, + filteredSearchTokenKeys: AdminRunnersFilteredSearchTokenKeys, + useDefaultState: true, }); + +initInstallRunner(); diff --git a/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js b/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js index e8d8c985ade..3456048d718 100644 --- a/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js +++ b/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js @@ -4,18 +4,18 @@ import initFilteredSearch from '~/pages/search/init_filtered_search'; import GroupRunnersFilteredSearchTokenKeys from '~/filtered_search/group_runners_filtered_search_token_keys'; import { FILTERED_SEARCH } from '~/pages/constants'; import initSharedRunnersForm from '~/group_settings/mount_shared_runners'; +import { initInstallRunner } from '~/pages/shared/mount_runner_instructions'; -document.addEventListener('DOMContentLoaded', () => { - // Initialize expandable settings panels - initSettingsPanels(); +// Initialize expandable settings panels +initSettingsPanels(); - initFilteredSearch({ - page: FILTERED_SEARCH.ADMIN_RUNNERS, - filteredSearchTokenKeys: GroupRunnersFilteredSearchTokenKeys, - anchor: FILTERED_SEARCH.GROUP_RUNNERS_ANCHOR, - useDefaultState: false, - }); - - initSharedRunnersForm(); - initVariableList(); +initFilteredSearch({ + page: FILTERED_SEARCH.ADMIN_RUNNERS, + filteredSearchTokenKeys: GroupRunnersFilteredSearchTokenKeys, + anchor: FILTERED_SEARCH.GROUP_RUNNERS_ANCHOR, + useDefaultState: false, }); + +initSharedRunnersForm(); +initVariableList(); +initInstallRunner(); diff --git a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js index d18cde4ac87..5d4c1595342 100644 --- a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js +++ b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js @@ -4,32 +4,32 @@ import registrySettingsApp from '~/registry/settings/registry_settings_bundle'; import initVariableList from '~/ci_variable_list'; import initDeployFreeze from '~/deploy_freeze'; import initSettingsPipelinesTriggers from '~/ci_settings_pipeline_triggers'; +import { initInstallRunner } from '~/pages/shared/mount_runner_instructions'; -document.addEventListener('DOMContentLoaded', () => { - // Initialize expandable settings panels - initSettingsPanels(); +// Initialize expandable settings panels +initSettingsPanels(); - const runnerToken = document.querySelector('.js-secret-runner-token'); - if (runnerToken) { - const runnerTokenSecretValue = new SecretValues({ - container: runnerToken, - }); - runnerTokenSecretValue.init(); - } - - initVariableList(); - - // hide extra auto devops settings based checkbox state - const autoDevOpsExtraSettings = document.querySelector('.js-extra-settings'); - const instanceDefaultBadge = document.querySelector('.js-instance-default-badge'); - document.querySelector('.js-toggle-extra-settings').addEventListener('click', event => { - const { target } = event; - if (instanceDefaultBadge) instanceDefaultBadge.style.display = 'none'; - autoDevOpsExtraSettings.classList.toggle('hidden', !target.checked); +const runnerToken = document.querySelector('.js-secret-runner-token'); +if (runnerToken) { + const runnerTokenSecretValue = new SecretValues({ + container: runnerToken, }); + runnerTokenSecretValue.init(); +} - registrySettingsApp(); - initDeployFreeze(); +initVariableList(); - initSettingsPipelinesTriggers(); +// hide extra auto devops settings based checkbox state +const autoDevOpsExtraSettings = document.querySelector('.js-extra-settings'); +const instanceDefaultBadge = document.querySelector('.js-instance-default-badge'); +document.querySelector('.js-toggle-extra-settings').addEventListener('click', event => { + const { target } = event; + if (instanceDefaultBadge) instanceDefaultBadge.style.display = 'none'; + autoDevOpsExtraSettings.classList.toggle('hidden', !target.checked); }); + +registrySettingsApp(); +initDeployFreeze(); + +initSettingsPipelinesTriggers(); +initInstallRunner(); diff --git a/app/assets/javascripts/pages/shared/mount_runner_instructions.js b/app/assets/javascripts/pages/shared/mount_runner_instructions.js new file mode 100644 index 00000000000..b7662155339 --- /dev/null +++ b/app/assets/javascripts/pages/shared/mount_runner_instructions.js @@ -0,0 +1,32 @@ +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import createDefaultClient from '~/lib/graphql'; +import InstallRunnerInstructions from '~/vue_shared/components/runner_instructions/runner_instructions.vue'; + +Vue.use(VueApollo); + +export function initInstallRunner(componentId = 'js-install-runner') { + const installRunnerEl = document.getElementById(componentId); + const { projectPath, groupPath } = installRunnerEl?.dataset; + + if (installRunnerEl) { + const defaultClient = createDefaultClient(); + + const apolloProvider = new VueApollo({ + defaultClient, + }); + + // eslint-disable-next-line no-new + new Vue({ + el: installRunnerEl, + apolloProvider, + provide: { + projectPath, + groupPath, + }, + render(createElement) { + return createElement(InstallRunnerInstructions); + }, + }); + } +} diff --git a/app/assets/javascripts/pipelines/components/graph/job_item.vue b/app/assets/javascripts/pipelines/components/graph/job_item.vue index 7aee2573ce1..09f40601fbf 100644 --- a/app/assets/javascripts/pipelines/components/graph/job_item.vue +++ b/app/assets/javascripts/pipelines/components/graph/job_item.vue @@ -119,6 +119,9 @@ export default { }, }, methods: { + hideTooltips() { + this.$root.$emit('bv::hide::tooltip'); + }, pipelineActionRequestComplete() { this.$emit('pipelineActionRequestComplete'); }, @@ -135,7 +138,7 @@ export default { :class="jobClasses" class="js-pipeline-graph-job-link qa-job-link menu-item" data-testid="job-with-link" - @click.stop + @click.stop="hideTooltips" > <job-name-component :name="job.name" :status="job.status" /> </gl-link> diff --git a/app/assets/javascripts/static_site_editor/components/edit_area.vue b/app/assets/javascripts/static_site_editor/components/edit_area.vue index 56f1a26f005..5b2d0207e98 100644 --- a/app/assets/javascripts/static_site_editor/components/edit_area.vue +++ b/app/assets/javascripts/static_site_editor/components/edit_area.vue @@ -50,12 +50,12 @@ export default { }, data() { return { - saveable: false, parsedSource: parseSourceFile(this.preProcess(true, this.content)), editorMode: EDITOR_TYPES.wysiwyg, - isModified: false, hasMatter: false, isDrawerOpen: false, + isModified: false, + isSaveable: false, }; }, imageRepository: imageRepository(), @@ -85,8 +85,11 @@ export default { return templatedContent; }, refreshEditHelpers() { - this.isModified = this.parsedSource.isModified(); - this.hasMatter = this.parsedSource.hasMatter(); + const { isModified, hasMatter, isMatterValid } = this.parsedSource; + this.isModified = isModified(); + this.hasMatter = hasMatter(); + const hasValidMatter = this.hasMatter ? isMatterValid() : true; + this.isSaveable = this.isModified && hasValidMatter; }, onDrawerOpen() { this.isDrawerOpen = true; @@ -142,12 +145,12 @@ export default { @input="onInputChange" @uploadImage="onUploadImage" /> - <unsaved-changes-confirm-dialog :modified="isModified" /> + <unsaved-changes-confirm-dialog :modified="isSaveable" /> <publish-toolbar class="gl-fixed gl-left-0 gl-bottom-0 gl-w-full" :has-settings="hasSettings" :return-url="returnUrl" - :saveable="isModified" + :saveable="isSaveable" :saving-changes="savingChanges" @editSettings="onDrawerOpen" @submit="onSubmit" diff --git a/app/assets/javascripts/static_site_editor/services/front_matterify.js b/app/assets/javascripts/static_site_editor/services/front_matterify.js index cbf0fffd515..60a5d799d11 100644 --- a/app/assets/javascripts/static_site_editor/services/front_matterify.js +++ b/app/assets/javascripts/static_site_editor/services/front_matterify.js @@ -16,6 +16,7 @@ export const frontMatterify = source => { const NO_FRONTMATTER = { source, matter: null, + hasMatter: false, spacing: null, content: source, delimiter: null, @@ -53,6 +54,7 @@ export const frontMatterify = source => { return { source, matter, + hasMatter: true, spacing, content, delimiter, diff --git a/app/assets/javascripts/static_site_editor/services/parse_source_file.js b/app/assets/javascripts/static_site_editor/services/parse_source_file.js index d4fc8b2edb6..39126eb7bcc 100644 --- a/app/assets/javascripts/static_site_editor/services/parse_source_file.js +++ b/app/assets/javascripts/static_site_editor/services/parse_source_file.js @@ -1,15 +1,18 @@ import { frontMatterify, stringify } from './front_matterify'; const parseSourceFile = raw => { - const remake = source => frontMatterify(source); - - let editable = remake(raw); + let editable; const syncContent = (newVal, isBody) => { if (isBody) { editable.content = newVal; } else { - editable = remake(newVal); + try { + editable = frontMatterify(newVal); + editable.isMatterValid = true; + } catch (e) { + editable.isMatterValid = false; + } } }; @@ -23,10 +26,15 @@ const parseSourceFile = raw => { const isModified = () => stringify(editable) !== raw; - const hasMatter = () => Boolean(editable.matter); + const hasMatter = () => editable.hasMatter; + + const isMatterValid = () => editable.isMatterValid; + + syncContent(raw); return { matter, + isMatterValid, syncMatter, content, syncContent, diff --git a/app/assets/javascripts/vue_shared/components/runner_instructions/graphql/queries/get_runner_platforms.query.graphql b/app/assets/javascripts/vue_shared/components/runner_instructions/graphql/queries/get_runner_platforms.query.graphql new file mode 100644 index 00000000000..ff0626167a9 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/runner_instructions/graphql/queries/get_runner_platforms.query.graphql @@ -0,0 +1,20 @@ +query getRunnerPlatforms($projectPath: ID!, $groupPath: ID!) { + runnerPlatforms { + nodes { + name + humanReadableName + architectures { + nodes { + name + downloadLocation + } + } + } + } + project(fullPath: $projectPath) { + id + } + group(fullPath: $groupPath) { + id + } +} diff --git a/app/assets/javascripts/vue_shared/components/runner_instructions/graphql/queries/get_runner_setup.query.graphql b/app/assets/javascripts/vue_shared/components/runner_instructions/graphql/queries/get_runner_setup.query.graphql new file mode 100644 index 00000000000..643c1991807 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/runner_instructions/graphql/queries/get_runner_setup.query.graphql @@ -0,0 +1,16 @@ +query runnerSetupInstructions( + $platform: String! + $architecture: String! + $projectId: ID! + $groupId: ID! +) { + runnerSetup( + platform: $platform + architecture: $architecture + projectId: $projectId + groupId: $groupId + ) { + installInstructions + registerInstructions + } +} diff --git a/app/assets/javascripts/vue_shared/components/runner_instructions/runner_instructions.vue b/app/assets/javascripts/vue_shared/components/runner_instructions/runner_instructions.vue new file mode 100644 index 00000000000..995922454c4 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/runner_instructions/runner_instructions.vue @@ -0,0 +1,214 @@ +<script> +import { + GlAlert, + GlButton, + GlModal, + GlModalDirective, + GlButtonGroup, + GlDropdown, + GlDropdownItem, + GlIcon, +} from '@gitlab/ui'; +import { __, s__ } from '~/locale'; +import getRunnerPlatforms from './graphql/queries/get_runner_platforms.query.graphql'; +import getRunnerSetupInstructions from './graphql/queries/get_runner_setup.query.graphql'; + +export default { + components: { + GlAlert, + GlButton, + GlButtonGroup, + GlDropdown, + GlDropdownItem, + GlModal, + GlIcon, + }, + directives: { + GlModalDirective, + }, + inject: { + projectPath: { + default: '', + }, + groupPath: { + default: '', + }, + }, + apollo: { + runnerPlatforms: { + query: getRunnerPlatforms, + variables() { + return { + projectPath: this.projectPath, + groupPath: this.groupPath, + }; + }, + update(data) { + return data; + }, + error() { + this.showAlert = true; + }, + }, + }, + data() { + return { + showAlert: false, + selectedPlatformArchitectures: [], + selectedPlatform: {}, + selectedArchitecture: {}, + runnerPlatforms: {}, + instructions: {}, + }; + }, + computed: { + isPlatformSelected() { + return Object.keys(this.selectedPlatform).length > 0; + }, + instructionsEmpty() { + return this.instructions && Object.keys(this.instructions).length === 0; + }, + groupId() { + return this.runnerPlatforms?.group?.id ?? ''; + }, + projectId() { + return this.runnerPlatforms?.project?.id ?? ''; + }, + platforms() { + return this.runnerPlatforms.runnerPlatforms?.nodes; + }, + }, + methods: { + selectPlatform(name) { + this.selectedPlatform = this.platforms.find(platform => platform.name === name); + this.selectedPlatformArchitectures = this.selectedPlatform?.architectures?.nodes; + [this.selectedArchitecture] = this.selectedPlatformArchitectures; + this.selectArchitecture(this.selectedArchitecture); + }, + selectArchitecture(architecture) { + this.selectedArchitecture = architecture; + + this.$apollo.addSmartQuery('instructions', { + variables() { + return { + platform: this.selectedPlatform.name, + architecture: this.selectedArchitecture.name, + projectId: this.projectId, + groupId: this.groupId, + }; + }, + query: getRunnerSetupInstructions, + update(data) { + return data?.runnerSetup; + }, + error() { + this.showAlert = true; + }, + }); + }, + toggleAlert(state) { + this.showAlert = state; + }, + }, + modalId: 'installation-instructions-modal', + i18n: { + installARunner: __('Install a Runner'), + architecture: s__('Runners|Architecture'), + downloadInstallBinary: s__('Runners|Download and Install Binary'), + downloadLatestBinary: s__('Runners|Download Latest Binary'), + registerRunner: s__('Runners|Register Runner'), + method: __('Method'), + fetchError: s__('An error has occurred fetching instructions'), + instructions: __('Show Runner installation instructions'), + }, + closeButton: { + text: __('Close'), + attributes: [{ variant: 'default' }], + }, +}; +</script> +<template> + <div> + <gl-button v-gl-modal-directive="$options.modalId" data-testid="show-modal-button"> + {{ $options.i18n.instructions }} + </gl-button> + <gl-modal + :modal-id="$options.modalId" + :title="$options.i18n.installARunner" + :action-secondary="$options.closeButton" + > + <gl-alert v-if="showAlert" variant="danger" @dismiss="toggleAlert(false)"> + {{ $options.i18n.fetchError }} + </gl-alert> + <h5>{{ __('Environment') }}</h5> + <gl-button-group class="gl-mb-5"> + <gl-button + v-for="platform in platforms" + :key="platform.name" + data-testid="platform-button" + @click="selectPlatform(platform.name)" + > + {{ platform.humanReadableName }} + </gl-button> + </gl-button-group> + <template v-if="isPlatformSelected"> + <h5> + {{ $options.i18n.architecture }} + </h5> + <gl-dropdown class="gl-mb-5" :text="selectedArchitecture.name"> + <gl-dropdown-item + v-for="architecture in selectedPlatformArchitectures" + :key="architecture.name" + data-testid="architecture-dropdown-item" + @click="selectArchitecture(architecture)" + > + {{ architecture.name }} + </gl-dropdown-item> + </gl-dropdown> + <div class="gl-display-flex gl-align-items-center gl-mb-5"> + <h5>{{ $options.i18n.downloadInstallBinary }}</h5> + <gl-button + class="gl-ml-auto" + :href="selectedArchitecture.downloadLocation" + download + data-testid="binary-download-button" + > + {{ $options.i18n.downloadLatestBinary }} + </gl-button> + </div> + </template> + <template v-if="!instructionsEmpty"> + <div class="gl-display-flex"> + <pre class="bg-light gl-flex-fill-1" data-testid="binary-instructions"> + {{ instructions.installInstructions }} + </pre> + <gl-button + class="gl-align-self-start gl-ml-2 gl-mt-2" + category="tertiary" + variant="link" + :data-clipboard-text="instructions.installationInstructions" + > + <gl-icon name="copy-to-clipboard" /> + </gl-button> + </div> + + <hr /> + <h5 class="gl-mb-5">{{ $options.i18n.registerRunner }}</h5> + <h5 class="gl-mb-5">{{ $options.i18n.method }}</h5> + <div class="gl-display-flex"> + <pre class="bg-light gl-flex-fill-1" data-testid="runner-instructions"> + {{ instructions.registerInstructions }} + </pre> + <gl-button + class="gl-align-self-start gl-ml-2 gl-mt-2" + category="tertiary" + variant="link" + :data-clipboard-text="instructions.registerInstructions" + > + <gl-icon name="copy-to-clipboard" /> + </gl-button> + </div> + </template> + </gl-modal> + </div> +</template> |