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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/environments')
-rw-r--r--app/assets/javascripts/environments/components/canary_ingress.vue15
-rw-r--r--app/assets/javascripts/environments/components/canary_update_modal.vue2
-rw-r--r--app/assets/javascripts/environments/components/commit.vue54
-rw-r--r--app/assets/javascripts/environments/components/deploy_board.vue68
-rw-r--r--app/assets/javascripts/environments/components/deploy_board_wrapper.vue86
-rw-r--r--app/assets/javascripts/environments/components/deployment.vue217
-rw-r--r--app/assets/javascripts/environments/components/empty_state.vue4
-rw-r--r--app/assets/javascripts/environments/components/environment_pin.vue15
-rw-r--r--app/assets/javascripts/environments/components/new_environment_item.vue77
-rw-r--r--app/assets/javascripts/environments/components/new_environments_app.vue12
-rw-r--r--app/assets/javascripts/environments/graphql/mutations/cancel_auto_stop.mutation.graphql4
-rw-r--r--app/assets/javascripts/environments/graphql/mutations/set_environment_to_change_canary.mutation.graphql3
-rw-r--r--app/assets/javascripts/environments/graphql/queries/environment_to_change_canary.query.graphql4
-rw-r--r--app/assets/javascripts/environments/graphql/resolvers.js11
-rw-r--r--app/assets/javascripts/environments/graphql/typedefs.graphql3
15 files changed, 537 insertions, 38 deletions
diff --git a/app/assets/javascripts/environments/components/canary_ingress.vue b/app/assets/javascripts/environments/components/canary_ingress.vue
index 02d660a91c1..30f3f9dfc75 100644
--- a/app/assets/javascripts/environments/components/canary_ingress.vue
+++ b/app/assets/javascripts/environments/components/canary_ingress.vue
@@ -17,6 +17,11 @@ export default {
required: true,
type: Object,
},
+ graphql: {
+ required: false,
+ type: Boolean,
+ default: false,
+ },
},
ingressOptions: Array(100 / 5 + 1)
.fill(0)
@@ -47,11 +52,17 @@ export default {
canaryWeightId() {
return uniqueId('canary-weight-');
},
+ weight() {
+ if (this.graphql) {
+ return this.canaryIngress.canaryWeight;
+ }
+ return this.canaryIngress.canary_weight;
+ },
stableWeight() {
- return (100 - this.canaryIngress.canary_weight).toString();
+ return (100 - this.weight).toString();
},
canaryWeight() {
- return this.canaryIngress.canary_weight.toString();
+ return this.weight.toString();
},
},
methods: {
diff --git a/app/assets/javascripts/environments/components/canary_update_modal.vue b/app/assets/javascripts/environments/components/canary_update_modal.vue
index 8b1121c7158..fd4885a9dbd 100644
--- a/app/assets/javascripts/environments/components/canary_update_modal.vue
+++ b/app/assets/javascripts/environments/components/canary_update_modal.vue
@@ -71,7 +71,7 @@ export default {
mutation: updateCanaryIngress,
variables: {
input: {
- id: this.environment.global_id,
+ id: this.environment.global_id || this.environment.globalId,
weight: this.weight,
},
},
diff --git a/app/assets/javascripts/environments/components/commit.vue b/app/assets/javascripts/environments/components/commit.vue
new file mode 100644
index 00000000000..54b94480685
--- /dev/null
+++ b/app/assets/javascripts/environments/components/commit.vue
@@ -0,0 +1,54 @@
+<script>
+import { GlAvatar, GlAvatarLink, GlLink, GlTooltipDirective as GlTooltip } from '@gitlab/ui';
+import { escape } from 'lodash';
+
+export default {
+ components: {
+ GlAvatar,
+ GlAvatarLink,
+ GlLink,
+ },
+ directives: {
+ GlTooltip,
+ },
+ props: {
+ commit: {
+ required: true,
+ type: Object,
+ },
+ },
+ computed: {
+ commitMessage() {
+ return this.commit?.message;
+ },
+ commitAuthorPath() {
+ // eslint-disable-next-line @gitlab/require-i18n-strings
+ return this.commit?.author?.path || `mailto:${escape(this.commit?.authorEmail)}`;
+ },
+ commitAuthorAvatar() {
+ return this.commit?.author?.avatarUrl || this.commit?.authorGravatarUrl;
+ },
+ commitAuthor() {
+ return this.commit?.author?.name || this.commit?.authorName;
+ },
+ commitPath() {
+ return this.commit?.commitPath;
+ },
+ },
+};
+</script>
+<template>
+ <div data-testid="deployment-commit" class="gl-display-flex gl-align-items-center">
+ <gl-avatar-link v-gl-tooltip :title="commitAuthor" :href="commitAuthorPath">
+ <gl-avatar :size="16" :src="commitAuthorAvatar" />
+ </gl-avatar-link>
+ <gl-link
+ v-gl-tooltip
+ :title="commitMessage"
+ :href="commitPath"
+ class="gl-ml-3 gl-str-truncated"
+ >
+ {{ commitMessage }}
+ </gl-link>
+ </div>
+</template>
diff --git a/app/assets/javascripts/environments/components/deploy_board.vue b/app/assets/javascripts/environments/components/deploy_board.vue
index c642a07fd1e..8a379ebdf66 100644
--- a/app/assets/javascripts/environments/components/deploy_board.vue
+++ b/app/assets/javascripts/environments/components/deploy_board.vue
@@ -1,5 +1,4 @@
<script>
-/* eslint-disable @gitlab/vue-require-i18n-strings */
/**
* Renders a deploy board.
*
@@ -17,11 +16,11 @@ import {
GlTooltip,
GlTooltipDirective,
GlSafeHtmlDirective as SafeHtml,
+ GlSprintf,
} from '@gitlab/ui';
import { isEmpty } from 'lodash';
-import { n__ } from '~/locale';
+import { s__, n__ } from '~/locale';
import instanceComponent from '~/vue_shared/components/deployment_instance.vue';
-import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { STATUS_MAP, CANARY_STATUS } from '../constants';
import CanaryIngress from './canary_ingress.vue';
@@ -32,13 +31,13 @@ export default {
GlIcon,
GlLoadingIcon,
GlLink,
+ GlSprintf,
GlTooltip,
},
directives: {
GlTooltip: GlTooltipDirective,
SafeHtml,
},
- mixins: [glFeatureFlagsMixin()],
props: {
deployBoardData: {
type: Object,
@@ -57,6 +56,11 @@ export default {
required: false,
default: '',
},
+ graphql: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
computed: {
canRenderDeployBoard() {
@@ -65,8 +69,15 @@ export default {
canRenderEmptyState() {
return this.isEmpty;
},
+ canaryIngress() {
+ if (this.graphql) {
+ return this.deployBoardData.canaryIngress;
+ }
+
+ return this.deployBoardData.canary_ingress;
+ },
canRenderCanaryWeight() {
- return !isEmpty(this.deployBoardData.canary_ingress);
+ return !isEmpty(this.canaryIngress);
},
instanceCount() {
const { instances } = this.deployBoardData;
@@ -90,8 +101,20 @@ export default {
deployBoardSvg() {
return deployBoardSvg;
},
+ rollbackUrl() {
+ if (this.graphql) {
+ return this.deployBoardData.rollbackUrl;
+ }
+ return this.deployBoardData.rollback_url;
+ },
+ abortUrl() {
+ if (this.graphql) {
+ return this.deployBoardData.abortUrl;
+ }
+ return this.deployBoardData.abort_url;
+ },
deployBoardActions() {
- return this.deployBoardData.rollback_url || this.deployBoardData.abort_url;
+ return this.rollbackUrl || this.abortUrl;
},
statuses() {
// Canary is not a pod status but it needs to be in the legend.
@@ -106,7 +129,17 @@ export default {
changeCanaryWeight(weight) {
this.$emit('changeCanaryWeight', weight);
},
+ podName(instance) {
+ if (this.graphql) {
+ return instance.podName;
+ }
+
+ return instance.pod_name;
+ },
},
+ emptyStateText: s__(
+ 'DeployBoards|To see deployment progress for your environments, make sure you are deploying to %{codeStart}$KUBE_NAMESPACE%{codeEnd} and annotating with %{codeStart}app.gitlab.com/app=$CI_PROJECT_PATH_SLUG%{codeEnd} and %{codeStart}app.gitlab.com/env=$CI_ENVIRONMENT_SLUG%{codeEnd}.',
+ ),
};
</script>
<template>
@@ -152,7 +185,7 @@ export default {
:key="i"
:status="instance.status"
:tooltip-text="instance.tooltip"
- :pod-name="instance.pod_name"
+ :pod-name="podName(instance)"
:logs-path="logsPath"
:stable="instance.stable"
/>
@@ -163,22 +196,23 @@ export default {
<canary-ingress
v-if="canRenderCanaryWeight"
class="deploy-board-canary-ingress"
- :canary-ingress="deployBoardData.canary_ingress"
+ :canary-ingress="canaryIngress"
+ :graphql="graphql"
@change="changeCanaryWeight"
/>
<section v-if="deployBoardActions" class="deploy-board-actions">
<gl-link
- v-if="deployBoardData.rollback_url"
- :href="deployBoardData.rollback_url"
+ v-if="rollbackUrl"
+ :href="rollbackUrl"
class="btn"
data-method="post"
rel="nofollow"
>{{ __('Rollback') }}</gl-link
>
<gl-link
- v-if="deployBoardData.abort_url"
- :href="deployBoardData.abort_url"
+ v-if="abortUrl"
+ :href="abortUrl"
class="btn btn-danger btn-inverted"
data-method="post"
rel="nofollow"
@@ -196,11 +230,11 @@ export default {
__('Kubernetes deployment not found')
}}</span>
<span>
- To see deployment progress for your environments, make sure you are deploying to
- <code>$KUBE_NAMESPACE</code> and annotating with
- <code>app.gitlab.com/app=$CI_PROJECT_PATH_SLUG</code>
- and
- <code>app.gitlab.com/env=$CI_ENVIRONMENT_SLUG</code>.
+ <gl-sprintf :message="$options.emptyStateText">
+ <template #code="{ content }">
+ <code>{{ content }}</code>
+ </template>
+ </gl-sprintf>
</span>
</section>
</div>
diff --git a/app/assets/javascripts/environments/components/deploy_board_wrapper.vue b/app/assets/javascripts/environments/components/deploy_board_wrapper.vue
new file mode 100644
index 00000000000..d9d77088ad3
--- /dev/null
+++ b/app/assets/javascripts/environments/components/deploy_board_wrapper.vue
@@ -0,0 +1,86 @@
+<script>
+import { GlCollapse, GlButton } from '@gitlab/ui';
+import { __, s__ } from '~/locale';
+import setEnvironmentToChangeCanaryMutation from '../graphql/mutations/set_environment_to_change_canary.mutation.graphql';
+import DeployBoard from './deploy_board.vue';
+
+export default {
+ components: {
+ DeployBoard,
+ GlButton,
+ GlCollapse,
+ },
+ props: {
+ rolloutStatus: {
+ required: true,
+ type: Object,
+ },
+ environment: {
+ required: true,
+ type: Object,
+ },
+ },
+ data() {
+ return { visible: false };
+ },
+ computed: {
+ icon() {
+ return this.visible ? 'angle-down' : 'angle-right';
+ },
+ label() {
+ return this.visible ? this.$options.i18n.collapse : this.$options.i18n.expand;
+ },
+ isLoading() {
+ return this.rolloutStatus.status === 'loading';
+ },
+ isEmpty() {
+ return this.rolloutStatus.status === 'not_found';
+ },
+ },
+ methods: {
+ toggleCollapse() {
+ this.visible = !this.visible;
+ },
+ changeCanaryWeight(weight) {
+ this.$apollo.mutate({
+ mutation: setEnvironmentToChangeCanaryMutation,
+ variables: {
+ environment: this.environment,
+ weight,
+ },
+ });
+ },
+ },
+ i18n: {
+ collapse: __('Collapse'),
+ expand: __('Expand'),
+ pods: s__('DeployBoard|Kubernetes Pods'),
+ },
+};
+</script>
+<template>
+ <div>
+ <div>
+ <gl-button
+ class="gl-mr-4 gl-min-w-fit-content"
+ :icon="icon"
+ :aria-label="label"
+ size="small"
+ category="tertiary"
+ @click="toggleCollapse"
+ />
+ <span>{{ $options.i18n.pods }}</span>
+ </div>
+ <gl-collapse :visible="visible">
+ <deploy-board
+ :deploy-board-data="rolloutStatus"
+ :is-loading="isLoading"
+ :is-empty="isEmpty"
+ :environment="environment"
+ graphql
+ class="gl-reset-bg!"
+ @changeCanaryWeight="changeCanaryWeight"
+ />
+ </gl-collapse>
+ </div>
+</template>
diff --git a/app/assets/javascripts/environments/components/deployment.vue b/app/assets/javascripts/environments/components/deployment.vue
index ef43ca6bc33..f98edb6bb7d 100644
--- a/app/assets/javascripts/environments/components/deployment.vue
+++ b/app/assets/javascripts/environments/components/deployment.vue
@@ -1,25 +1,240 @@
<script>
+import {
+ GlBadge,
+ GlButton,
+ GlCollapse,
+ GlIcon,
+ GlLink,
+ GlTooltipDirective as GlTooltip,
+ GlTruncate,
+} from '@gitlab/ui';
+import { GlBreakpointInstance } from '@gitlab/ui/dist/utils';
+import { __, s__ } from '~/locale';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import DeploymentStatusBadge from './deployment_status_badge.vue';
+import Commit from './commit.vue';
export default {
components: {
+ ClipboardButton,
+ Commit,
DeploymentStatusBadge,
+ GlBadge,
+ GlButton,
+ GlCollapse,
+ GlIcon,
+ GlLink,
+ GlTruncate,
+ TimeAgoTooltip,
+ },
+ directives: {
+ GlTooltip,
},
props: {
deployment: {
type: Object,
required: true,
},
+ latest: {
+ type: Boolean,
+ default: false,
+ required: false,
+ },
+ },
+ data() {
+ return { visible: false };
},
computed: {
status() {
return this.deployment?.status;
},
+ iid() {
+ return this.deployment?.iid;
+ },
+ shortSha() {
+ return this.commit?.shortId;
+ },
+ createdAt() {
+ return this.deployment?.createdAt;
+ },
+ isMobile() {
+ return !GlBreakpointInstance.isDesktop();
+ },
+ detailsButton() {
+ return this.visible
+ ? { text: this.$options.i18n.hideDetails, icon: 'expand-up' }
+ : { text: this.$options.i18n.showDetails, icon: 'expand-down' };
+ },
+ detailsButtonClasses() {
+ return this.isMobile ? 'gl-sr-only' : '';
+ },
+ commit() {
+ return this.deployment?.commit;
+ },
+ user() {
+ return this.deployment?.user;
+ },
+ username() {
+ return `@${this.user.username}`;
+ },
+ userPath() {
+ return this.user?.path;
+ },
+ deployable() {
+ return this.deployment?.deployable;
+ },
+ jobName() {
+ return this.deployable?.name;
+ },
+ jobPath() {
+ return this.deployable?.buildPath;
+ },
+ refLabel() {
+ return this.deployment?.tag ? this.$options.i18n.tag : this.$options.i18n.branch;
+ },
+ ref() {
+ return this.deployment?.ref;
+ },
+ refName() {
+ return this.ref?.name;
+ },
+ refPath() {
+ return this.ref?.refPath;
+ },
+ },
+ methods: {
+ toggleCollapse() {
+ this.visible = !this.visible;
+ },
+ },
+ i18n: {
+ latestBadge: s__('Deployment|Latest Deployed'),
+ deploymentId: s__('Deployment|Deployment ID'),
+ copyButton: __('Copy commit SHA'),
+ commitSha: __('Commit SHA'),
+ showDetails: __('Show details'),
+ hideDetails: __('Hide details'),
+ triggerer: s__('Deployment|Triggerer'),
+ job: __('Job'),
+ api: __('API'),
+ branch: __('Branch'),
+ tag: __('Tag'),
},
+ headerClasses: [
+ 'gl-display-flex',
+ 'gl-align-items-flex-start',
+ 'gl-md-align-items-center',
+ 'gl-justify-content-space-between',
+ 'gl-pr-6',
+ ],
+ headerDetailsClasses: [
+ 'gl-display-flex',
+ 'gl-flex-direction-column',
+ 'gl-md-flex-direction-row',
+ 'gl-align-items-flex-start',
+ 'gl-md-align-items-center',
+ 'gl-font-sm',
+ 'gl-text-gray-700',
+ ],
+ deploymentStatusClasses: [
+ 'gl-display-flex',
+ 'gl-gap-x-3',
+ 'gl-mr-0',
+ 'gl-md-mr-5',
+ 'gl-mb-3',
+ 'gl-md-mb-0',
+ ],
};
</script>
<template>
<div>
- <deployment-status-badge v-if="status" :status="status" />
+ <div :class="$options.headerClasses">
+ <div :class="$options.headerDetailsClasses">
+ <div :class="$options.deploymentStatusClasses">
+ <deployment-status-badge v-if="status" :status="status" />
+ <gl-badge v-if="latest" variant="info">{{ $options.i18n.latestBadge }}</gl-badge>
+ </div>
+ <div class="gl-display-flex gl-align-items-center gl-gap-x-5">
+ <div
+ v-if="iid"
+ v-gl-tooltip
+ class="gl-display-flex"
+ :title="$options.i18n.deploymentId"
+ :aria-label="$options.i18n.deploymentId"
+ >
+ <gl-icon ref="deployment-iid-icon" name="deployments" />
+ <span class="gl-ml-2">#{{ iid }}</span>
+ </div>
+ <div
+ v-if="shortSha"
+ data-testid="deployment-commit-sha"
+ class="gl-font-monospace gl-display-flex gl-align-items-center"
+ >
+ <gl-icon ref="deployment-commit-icon" name="commit" class="gl-mr-2" />
+ <span v-gl-tooltip :title="$options.i18n.commitSha">{{ shortSha }}</span>
+ <clipboard-button
+ :text="shortSha"
+ category="tertiary"
+ :title="$options.i18n.copyButton"
+ size="small"
+ />
+ </div>
+ <time-ago-tooltip v-if="createdAt" :time="createdAt" class="gl-display-flex">
+ <template #default="{ timeAgo }">
+ <gl-icon name="calendar" />
+ <span class="gl-mr-2 gl-white-space-nowrap">{{ timeAgo }}</span>
+ </template>
+ </time-ago-tooltip>
+ </div>
+ </div>
+ <gl-button
+ ref="details-toggle"
+ category="tertiary"
+ :icon="detailsButton.icon"
+ :button-text-classes="detailsButtonClasses"
+ @click="toggleCollapse"
+ >
+ {{ detailsButton.text }}
+ </gl-button>
+ </div>
+ <commit v-if="commit" :commit="commit" class="gl-mt-3" />
+ <gl-collapse :visible="visible">
+ <div
+ class="gl-display-flex gl-md-align-items-center gl-mt-5 gl-flex-direction-column gl-md-flex-direction-row gl-pr-4 gl-md-pr-0"
+ >
+ <div v-if="user" class="gl-display-flex gl-flex-direction-column gl-md-max-w-15p">
+ <span class="gl-text-gray-500">{{ $options.i18n.triggerer }}</span>
+ <gl-link :href="userPath" class="gl-font-monospace gl-mt-3">
+ <gl-truncate :text="username" with-tooltip />
+ </gl-link>
+ </div>
+ <div
+ class="gl-display-flex gl-flex-direction-column gl-md-pl-7 gl-md-max-w-15p gl-mt-4 gl-md-mt-0"
+ >
+ <span class="gl-text-gray-500" :class="{ 'gl-ml-3': !deployable }">
+ {{ $options.i18n.job }}
+ </span>
+ <gl-link v-if="jobPath" :href="jobPath" class="gl-font-monospace gl-mt-3">
+ <gl-truncate :text="jobName" with-tooltip position="middle" />
+ </gl-link>
+ <span v-else-if="jobName" class="gl-font-monospace gl-mt-3">
+ <gl-truncate :text="jobName" with-tooltip position="middle" />
+ </span>
+ <gl-badge v-else class="gl-font-monospace gl-mt-3" variant="info">
+ {{ $options.i18n.api }}
+ </gl-badge>
+ </div>
+ <div
+ v-if="ref"
+ class="gl-display-flex gl-flex-direction-column gl-md-pl-7 gl-md-max-w-15p gl-mt-4 gl-md-mt-0"
+ >
+ <span class="gl-text-gray-500">{{ refLabel }}</span>
+ <gl-link :href="refPath" class="gl-font-monospace gl-mt-3">
+ <gl-truncate :text="refName" with-tooltip />
+ </gl-link>
+ </div>
+ </div>
+ </gl-collapse>
</div>
</template>
diff --git a/app/assets/javascripts/environments/components/empty_state.vue b/app/assets/javascripts/environments/components/empty_state.vue
index 977da12e8a9..36b9b647af7 100644
--- a/app/assets/javascripts/environments/components/empty_state.vue
+++ b/app/assets/javascripts/environments/components/empty_state.vue
@@ -12,10 +12,10 @@ export default {
<template>
<div class="empty-state">
<div class="text-content">
- <h4 class="blank-state-title js-blank-state-title">
+ <h4 class="js-blank-state-title">
{{ s__("Environments|You don't have any environments right now") }}
</h4>
- <p class="blank-state-text">
+ <p>
{{
s__(`Environments|Environments are places where
code gets deployed, such as staging or production.`)
diff --git a/app/assets/javascripts/environments/components/environment_pin.vue b/app/assets/javascripts/environments/components/environment_pin.vue
index 0b753d53ee3..f5a83b97552 100644
--- a/app/assets/javascripts/environments/components/environment_pin.vue
+++ b/app/assets/javascripts/environments/components/environment_pin.vue
@@ -6,6 +6,7 @@
import { GlDropdownItem } from '@gitlab/ui';
import { __ } from '~/locale';
import eventHub from '../event_hub';
+import cancelAutoStopMutation from '../graphql/mutations/cancel_auto_stop.mutation.graphql';
export default {
components: {
@@ -16,10 +17,22 @@ export default {
type: String,
required: true,
},
+ graphql: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
methods: {
onPinClick() {
- eventHub.$emit('cancelAutoStop', this.autoStopUrl);
+ if (this.graphql) {
+ this.$apollo.mutate({
+ mutation: cancelAutoStopMutation,
+ variables: { autoStopUrl: this.autoStopUrl },
+ });
+ } else {
+ eventHub.$emit('cancelAutoStop', this.autoStopUrl);
+ }
},
},
title: __('Prevent auto-stopping'),
diff --git a/app/assets/javascripts/environments/components/new_environment_item.vue b/app/assets/javascripts/environments/components/new_environment_item.vue
index d3624103c13..27a763fb9c4 100644
--- a/app/assets/javascripts/environments/components/new_environment_item.vue
+++ b/app/assets/javascripts/environments/components/new_environment_item.vue
@@ -4,10 +4,12 @@ import {
GlDropdown,
GlButton,
GlLink,
+ GlSprintf,
GlTooltipDirective as GlTooltip,
} from '@gitlab/ui';
-import { __ } from '~/locale';
+import { __, s__ } from '~/locale';
import { truncate } from '~/lib/utils/text_utility';
+import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import isLastDeployment from '../graphql/queries/is_last_deployment.query.graphql';
import ExternalUrl from './environment_external_url.vue';
import Actions from './environment_actions.vue';
@@ -18,6 +20,7 @@ import Monitoring from './environment_monitoring.vue';
import Terminal from './environment_terminal_button.vue';
import Delete from './environment_delete.vue';
import Deployment from './deployment.vue';
+import DeployBoardWrapper from './deploy_board_wrapper.vue';
export default {
components: {
@@ -25,19 +28,23 @@ export default {
GlDropdown,
GlButton,
GlLink,
+ GlSprintf,
Actions,
Deployment,
+ DeployBoardWrapper,
ExternalUrl,
StopComponent,
Rollback,
Monitoring,
Pin,
Terminal,
+ TimeAgoTooltip,
Delete,
},
directives: {
GlTooltip,
},
+ inject: ['helpPagePath'],
props: {
environment: {
required: true,
@@ -60,6 +67,10 @@ export default {
i18n: {
collapse: __('Collapse'),
expand: __('Expand'),
+ emptyState: s__(
+ 'Environments|There are no deployments for this environment yet. %{linkStart}Learn more about setting up deployments.%{linkEnd}',
+ ),
+ autoStopIn: s__('Environment|Auto stop %{time}'),
},
data() {
return { visible: false };
@@ -83,12 +94,15 @@ export default {
upcomingDeployment() {
return this.environment?.upcomingDeployment;
},
+ hasDeployment() {
+ return Boolean(this.environment?.upcomingDeployment || this.environment?.lastDeployment);
+ },
actions() {
if (!this.lastDeployment) {
return [];
}
- const { manualActions = [], scheduledActions = [] } = this.lastDeployment;
- const combinedActions = [...manualActions, ...scheduledActions];
+ const { manualActions, scheduledActions } = this.lastDeployment;
+ const combinedActions = [...(manualActions ?? []), ...(scheduledActions ?? [])];
return combinedActions.map((action) => ({
...action,
}));
@@ -133,6 +147,9 @@ export default {
displayName() {
return truncate(this.name, 80);
},
+ rolloutStatus() {
+ return this.environment?.rolloutStatus;
+ },
},
methods: {
toggleCollapse() {
@@ -144,7 +161,15 @@ export default {
'gl-border-t-solid',
'gl-border-1',
'gl-py-5',
- 'gl-pl-7',
+ 'gl-md-pl-7',
+ 'gl-bg-gray-10',
+ ],
+ deployBoardClasses: [
+ 'gl-border-gray-100',
+ 'gl-border-t-solid',
+ 'gl-border-1',
+ 'gl-py-4',
+ 'gl-md-pl-7',
'gl-bg-gray-10',
],
};
@@ -176,7 +201,14 @@ export default {
{{ displayName }}
</gl-link>
</div>
- <div>
+ <div class="gl-display-flex gl-align-items-center">
+ <p v-if="canShowAutoStopDate" class="gl-font-sm gl-text-gray-700 gl-mr-5 gl-mb-0">
+ <gl-sprintf :message="$options.i18n.autoStopIn">
+ <template #time>
+ <time-ago-tooltip :time="environment.autoStopAt" css-class="gl-font-weight-bold" />
+ </template>
+ </gl-sprintf>
+ </p>
<div class="btn-group table-action-buttons" role="group">
<external-url
v-if="externalUrl"
@@ -224,6 +256,7 @@ export default {
<pin
v-if="canShowAutoStopDate"
:auto-stop-url="autoStopPath"
+ graphql
data-track-action="click_button"
data-track-label="environment_pin"
/>
@@ -254,11 +287,37 @@ export default {
</div>
</div>
<gl-collapse :visible="visible">
- <div v-if="lastDeployment" :class="$options.deploymentClasses">
- <deployment :deployment="lastDeployment" :class="{ 'gl-ml-7': inFolder }" />
+ <template v-if="hasDeployment">
+ <div v-if="lastDeployment" :class="$options.deploymentClasses">
+ <deployment
+ :deployment="lastDeployment"
+ :class="{ 'gl-ml-7': inFolder }"
+ latest
+ class="gl-pl-4"
+ />
+ </div>
+ <div v-if="upcomingDeployment" :class="$options.deploymentClasses">
+ <deployment
+ :deployment="upcomingDeployment"
+ :class="{ 'gl-ml-7': inFolder }"
+ class="gl-pl-4"
+ />
+ </div>
+ </template>
+ <div v-else :class="$options.deploymentClasses">
+ <gl-sprintf :message="$options.i18n.emptyState">
+ <template #link="{ content }">
+ <gl-link :href="helpPagePath">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
</div>
- <div v-if="upcomingDeployment" :class="$options.deploymentClasses">
- <deployment :deployment="upcomingDeployment" :class="{ 'gl-ml-7': inFolder }" />
+ <div v-if="rolloutStatus" :class="$options.deployBoardClasses">
+ <deploy-board-wrapper
+ :rollout-status="rolloutStatus"
+ :environment="environment"
+ :class="{ 'gl-ml-7': inFolder }"
+ class="gl-pl-4"
+ />
</div>
</gl-collapse>
</div>
diff --git a/app/assets/javascripts/environments/components/new_environments_app.vue b/app/assets/javascripts/environments/components/new_environments_app.vue
index cb36e226d0e..3699f39b611 100644
--- a/app/assets/javascripts/environments/components/new_environments_app.vue
+++ b/app/assets/javascripts/environments/components/new_environments_app.vue
@@ -8,16 +8,19 @@ import pageInfoQuery from '../graphql/queries/page_info.query.graphql';
import environmentToDeleteQuery from '../graphql/queries/environment_to_delete.query.graphql';
import environmentToRollbackQuery from '../graphql/queries/environment_to_rollback.query.graphql';
import environmentToStopQuery from '../graphql/queries/environment_to_stop.query.graphql';
+import environmentToChangeCanaryQuery from '../graphql/queries/environment_to_change_canary.query.graphql';
import EnvironmentFolder from './new_environment_folder.vue';
import EnableReviewAppModal from './enable_review_app_modal.vue';
import StopEnvironmentModal from './stop_environment_modal.vue';
import EnvironmentItem from './new_environment_item.vue';
import ConfirmRollbackModal from './confirm_rollback_modal.vue';
import DeleteEnvironmentModal from './delete_environment_modal.vue';
+import CanaryUpdateModal from './canary_update_modal.vue';
export default {
components: {
DeleteEnvironmentModal,
+ CanaryUpdateModal,
ConfirmRollbackModal,
EnvironmentFolder,
EnableReviewAppModal,
@@ -56,6 +59,12 @@ export default {
environmentToStop: {
query: environmentToStopQuery,
},
+ environmentToChangeCanary: {
+ query: environmentToChangeCanaryQuery,
+ },
+ weight: {
+ query: environmentToChangeCanaryQuery,
+ },
},
inject: ['newEnvironmentPath', 'canCreateEnvironment'],
i18n: {
@@ -80,6 +89,8 @@ export default {
environmentToDelete: {},
environmentToRollback: {},
environmentToStop: {},
+ environmentToChangeCanary: {},
+ weight: 0,
};
},
computed: {
@@ -186,6 +197,7 @@ export default {
<delete-environment-modal :environment="environmentToDelete" graphql />
<stop-environment-modal :environment="environmentToStop" graphql />
<confirm-rollback-modal :environment="environmentToRollback" graphql />
+ <canary-update-modal :environment="environmentToChangeCanary" :weight="weight" />
<gl-tabs
:action-secondary="addEnvironment"
:action-primary="openReviewAppModal"
diff --git a/app/assets/javascripts/environments/graphql/mutations/cancel_auto_stop.mutation.graphql b/app/assets/javascripts/environments/graphql/mutations/cancel_auto_stop.mutation.graphql
index 22dfb8a7a89..0b473495710 100644
--- a/app/assets/javascripts/environments/graphql/mutations/cancel_auto_stop.mutation.graphql
+++ b/app/assets/javascripts/environments/graphql/mutations/cancel_auto_stop.mutation.graphql
@@ -1,5 +1,5 @@
-mutation cancelAutoStop($environment: LocalEnvironment) {
- cancelAutoStop(environment: $environment) @client {
+mutation cancelAutoStop($autoStopUrl: String!) {
+ cancelAutoStop(autoStopUrl: $autoStopUrl) @client {
errors
}
}
diff --git a/app/assets/javascripts/environments/graphql/mutations/set_environment_to_change_canary.mutation.graphql b/app/assets/javascripts/environments/graphql/mutations/set_environment_to_change_canary.mutation.graphql
new file mode 100644
index 00000000000..0f48c1f5c05
--- /dev/null
+++ b/app/assets/javascripts/environments/graphql/mutations/set_environment_to_change_canary.mutation.graphql
@@ -0,0 +1,3 @@
+mutation SetEnvironmentToChangeCanary($environment: LocalEnvironmentInput, $weight: Int!) {
+ setEnvironmentToChangeCanary(environment: $environment, weight: $weight) @client
+}
diff --git a/app/assets/javascripts/environments/graphql/queries/environment_to_change_canary.query.graphql b/app/assets/javascripts/environments/graphql/queries/environment_to_change_canary.query.graphql
new file mode 100644
index 00000000000..b582ae55ba1
--- /dev/null
+++ b/app/assets/javascripts/environments/graphql/queries/environment_to_change_canary.query.graphql
@@ -0,0 +1,4 @@
+query environmentToChangeCanary {
+ environmentToChangeCanary @client
+ weight @client
+}
diff --git a/app/assets/javascripts/environments/graphql/resolvers.js b/app/assets/javascripts/environments/graphql/resolvers.js
index 812fa0c81f0..dc763b77157 100644
--- a/app/assets/javascripts/environments/graphql/resolvers.js
+++ b/app/assets/javascripts/environments/graphql/resolvers.js
@@ -10,6 +10,7 @@ import pollIntervalQuery from './queries/poll_interval.query.graphql';
import environmentToRollbackQuery from './queries/environment_to_rollback.query.graphql';
import environmentToStopQuery from './queries/environment_to_stop.query.graphql';
import environmentToDeleteQuery from './queries/environment_to_delete.query.graphql';
+import environmentToChangeCanaryQuery from './queries/environment_to_change_canary.query.graphql';
import pageInfoQuery from './queries/page_info.query.graphql';
const buildErrors = (errors = []) => ({
@@ -134,9 +135,15 @@ export const resolvers = (endpoint) => ({
data: { environmentToRollback: environment },
});
},
- cancelAutoStop(_, { environment: { autoStopPath } }) {
+ setEnvironmentToChangeCanary(_, { environment, weight }, { client }) {
+ client.writeQuery({
+ query: environmentToChangeCanaryQuery,
+ data: { environmentToChangeCanary: environment, weight },
+ });
+ },
+ cancelAutoStop(_, { autoStopUrl }) {
return axios
- .post(autoStopPath)
+ .post(autoStopUrl)
.then(() => buildErrors())
.catch((err) =>
buildErrors([
diff --git a/app/assets/javascripts/environments/graphql/typedefs.graphql b/app/assets/javascripts/environments/graphql/typedefs.graphql
index c02f6b2838a..b4d1f7326f6 100644
--- a/app/assets/javascripts/environments/graphql/typedefs.graphql
+++ b/app/assets/javascripts/environments/graphql/typedefs.graphql
@@ -77,9 +77,10 @@ extend type Mutation {
stopEnvironment(environment: LocalEnvironmentInput): LocalErrors
deleteEnvironment(environment: LocalEnvironmentInput): LocalErrors
rollbackEnvironment(environment: LocalEnvironmentInput): LocalErrors
- cancelAutoStop(environment: LocalEnvironmentInput): LocalErrors
+ cancelAutoStop(autoStopUrl: String!): LocalErrors
setEnvironmentToDelete(environment: LocalEnvironmentInput): LocalErrors
setEnvironmentToRollback(environment: LocalEnvironmentInput): LocalErrors
setEnvironmentToStop(environment: LocalEnvironmentInput): LocalErrors
+ setEnvironmentToChangeCanary(environment: LocalEnvironmentInput, weight: Int): LocalErrors
action(environment: LocalEnvironmentInput): LocalErrors
}