Skip to content
10 changes: 7 additions & 3 deletions src/server/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import { execFile } from 'child_process';
import { promisify } from 'util';
import { GithubPullRequestActions, PullRequestStatus, Labels } from 'shared/constants';
import { GithubPullRequestActions, PullRequestStatus, PrTriggerLabels } from 'shared/constants';
import GlobalConfigService from 'server/services/globalConfig';
import { GenerateDeployTagOptions, WaitUntilOptions, EnableKillswitchOptions } from 'server/lib/types';

Expand Down Expand Up @@ -145,11 +145,15 @@ export const enableKillSwitch = async ({
action as GithubPullRequestActions
);
const isClosed = status === PullRequestStatus.CLOSED && !isOpened;
const isDisabled = labels.includes(Labels.DISABLED);
const isDisabled = Array.isArray(labels)
? labels.some((item) => PrTriggerLabels.DISABLED.includes(item))
: PrTriggerLabels.DEPLOY.includes(labels);
Comment on lines +148 to +150

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If labels is not an array, is the PrTriggerLabels.DEPLOY.includes(labels); expression valid? If labels is not an array and labels is equal to a DEPLOY label like lets-go! then isDisabled will resolve to true but that seems like it might be unexpected.

should it be PrTriggerLabels.DISABLED.includes(labels) instead?

Copy link
Author

@lucien-UD lucien-UD Jun 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the enableKillSwitch function takes a labels value that defaults to an empty array...

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So what happens if your PR only has a single label and the single label is let-it-go!?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in theory.... nothing should happen... ill test it

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So after a bit of testing the let-it-go! label doesnt make sense since it just disabling auto-deply... ive update the label to reflect that and not use lifecycle wordage

if (isClosed || isDisabled) {
return true;
}
const isForceDeploy = labels.includes(Labels.DEPLOY);
const isForceDeploy = Array.isArray(labels)
? labels.some((item) => PrTriggerLabels.DEPLOY.includes(item))
: PrTriggerLabels.DEPLOY.includes(labels);
if (isForceDeploy) {
return false;
}
Expand Down
15 changes: 11 additions & 4 deletions src/server/services/activityStream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
DeployStatus,
CommentParser,
Labels,
PrTriggerLabels,
DeployTypes,
CLIDeployTypes,
PullRequestStatus,
Expand All @@ -41,7 +42,11 @@ const logger = rootLogger.child({
filename: 'services/activityStream.ts',
});

const TO_DEPLOY_THIS_ENV = `To deploy this environment, just add a \`${Labels.DEPLOY}\` label. Add a \`${Labels.DISABLED}\` to do the opposite. ↗️\n\n`;
const TO_DEPLOY_THIS_ENV = `
**To deploy this environment:** Add the **\`${PrTriggerLabels.DEPLOY[0]}\`** label to this PR

**To disable auto deployments:** Add the **\`${PrTriggerLabels.DISABLED[0]}\`** label to this PR
`;
const COMMENT_EDIT_DESCRIPTION = `You can use the section below to redeploy and update the dev environment for this pull request.\n\n\n`;
const GIT_SERVICE_URL = 'https://github.com';

Expand Down Expand Up @@ -611,7 +616,9 @@ export default class ActivityStream extends BaseService {
].includes(buildStatus as BuildStatus);
const isDeployed = buildStatus === BuildStatus.DEPLOYED;
let deployStatus;
const hasDeployLabel = labels?.includes(Labels.DEPLOY);
const hasDeployLabel = Array.isArray(labels)
? labels.some((item) => PrTriggerLabels.DEPLOY.includes(item))
: PrTriggerLabels.DEPLOY.includes(labels);
//TODO review the env tag here, should it be tools or dev?
const tags = { uuid, repositoryName, branchName, env: 'prd', service: 'lifecycle-job', statsEvent: 'deployment' };
const eventDetails = {
Expand Down Expand Up @@ -759,9 +766,9 @@ export default class ActivityStream extends BaseService {
message += '## ⏳ Pending\n';
message += `The Ephemeral Environment has been torn down or does not exist.`;
if (isBot) {
message += `\n\n**This PR is created by a bot user, add ${Labels.DEPLOY} to build environment**`;
message += `\n\n**This PR is created by a bot user, add ${PrTriggerLabels.DEPLOY[0]} to build environment**`;
} else {
message += `\n\n*Note: If ${Labels.DISABLED} label present, remove to build environment*`;
message += `\n\n**Note: If \`${PrTriggerLabels.DISABLED[0]}\` label present, remove to build environment**`;
}
} else if (isBuilding) {
message += '## 🏗️ Building\n';
Expand Down
12 changes: 9 additions & 3 deletions src/server/services/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export default class BuildService extends BaseService {
if (build.pullRequest?.repository != null) {
const isActive = await this.db.services.PullRequest.lifecycleEnabledForPullRequest(build.pullRequest);
// Either we want the PR status to be closed or
// if deployOnUpdate at the PR level (with the lifecycle-disabled! label)
// if deployOnUpdate at the PR level (with the PrTriggerLabels.DEPLOY labels)
if (
build.pullRequest.status === 'closed' ||
(isActive === false && build.pullRequest.deployOnUpdate === false)
Expand Down Expand Up @@ -416,7 +416,7 @@ export default class BuildService extends BaseService {
}

/**
* Deploy an existing build/PR (usually happens when adding the lifecycle-deploy! label)
* Deploy an existing build/PR (usually happens when adding a label(s) listed in PrTriggerLabels.DEPLOY)
* @param build Build associates to a PR
* @param deploy deploy on changed?
*/
Expand Down Expand Up @@ -946,7 +946,13 @@ export default class BuildService extends BaseService {
});
logger.debug(`[BUILD ${build.uuid}] Found ${helmDeploys.length} helm deploys`);
logger.debug(`[BUILD ${build.uuid}] Found ${k8sDeploys.length} deploys to generate manifests for`);
const manifest = k8s.generateManifest({ build, deploys: k8sDeploys, uuid: build.uuid, namespace, serviceAccountName });
const manifest = k8s.generateManifest({
build,
deploys: k8sDeploys,
uuid: build.uuid,
namespace,
serviceAccountName,
});
if (manifest && manifest.replace('---', '').trim().length > 0) {
await build.$query().patch({ manifest });
await k8s.applyManifests(build);
Expand Down
49 changes: 40 additions & 9 deletions src/server/services/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@ import _ from 'lodash';
import Service from './_service';
import rootLogger from 'server/lib/logger';
import { IssueCommentEvent, PullRequestEvent, PushEvent } from '@octokit/webhooks-types';
import { GithubPullRequestActions, GithubWebhookTypes, PullRequestStatus, Labels } from 'shared/constants';
import {
GithubPullRequestActions,
GithubWebhookTypes,
PullRequestStatus,
Labels,
PrTriggerLabels,
} from 'shared/constants';
import { JOB_VERSION } from 'shared/config';
import { NextApiRequest } from 'next';
import * as github from 'server/lib/github';
Expand Down Expand Up @@ -140,14 +146,30 @@ export default class GithubService extends Service {
lifecycleConfig,
});

// if auto deploy, add deploy label`
if (isDeploy)
// if auto deploy, add deploy label
if (isDeploy) {
const currentLabels = labels.map((l) => l.name);
const hasDeployLabel = currentLabels.some((label) =>
Array.isArray(PrTriggerLabels.DEPLOY)
? PrTriggerLabels.DEPLOY.includes(label)
: label === PrTriggerLabels.DEPLOY
);
const updatedLabels = hasDeployLabel
? currentLabels
: [
...new Set([
...currentLabels,
...(Array.isArray(PrTriggerLabels.DEPLOY) ? PrTriggerLabels.DEPLOY : [PrTriggerLabels.DEPLOY]),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like it would add all the deploy labels to the PR. Do we want to it to just add the first deploy label instead?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it matter? like just adding the first label makes it cleaner or something?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know how much it matters, but the reasons I can think of are:

  • If we want people to use the first label why do we add all of them?
  • Do you need to remove multiple deploy labels now to tear down your env?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you only need to remove the initial label used

]),
];

await github.updatePullRequestLabels({
installationId,
pullRequestNumber: number,
fullName,
labels: labels.map((l) => l.name).concat(['lifecycle-deploy!']),
labels: updatedLabels,
});
}
} else if (isClosed) {
build = await this.db.models.Build.findOne({
pullRequestId,
Expand All @@ -157,12 +179,18 @@ export default class GithubService extends Service {
return;
}
await this.db.services.BuildService.deleteBuild(build);
// remove lifecycle-deploy! label on PR close
// remove the PrTriggerLabels.DEPLOY label(s) on PR close
const currentLabels = labels.map((l) => l.name);
const deployLabelsToRemove = Array.isArray(PrTriggerLabels.DEPLOY)
? PrTriggerLabels.DEPLOY
: [PrTriggerLabels.DEPLOY];
const updatedLabels = currentLabels.filter((label) => !deployLabelsToRemove.includes(label));

await github.updatePullRequestLabels({
installationId,
pullRequestNumber: number,
fullName,
labels: labels.map((l) => l.name).filter((v) => v !== Labels.DEPLOY),
labels: updatedLabels,
});
}
} catch (error) {
Expand Down Expand Up @@ -246,8 +274,8 @@ export default class GithubService extends Service {
);

if (pullRequest.deployOnUpdate === false) {
// when pullRequest.deployOnUpdate is false, it means that there is no `lifecycle-deploy!` label
// or there is `lifecycle-disabled!` label in the PR
// when pullRequest.deployOnUpdate is false, it means that there are no PrTriggerLabels.DEPLOY labels
// or there are PrTriggerLabels.DISABLED labels in the PR
return this.db.services.BuildService.deleteBuild(build);
}

Expand Down Expand Up @@ -481,7 +509,10 @@ export default class GithubService extends Service {
const branch = pullRequest?.branchName;
try {
const isBot = await this.db.services.BotUser.isBotUser(user);
const hasDeployLabel = labelNames.includes(Labels.DEPLOY);
const deployLabels = Array.isArray(PrTriggerLabels.DEPLOY)
? PrTriggerLabels.DEPLOY.map((label) => label.toLowerCase())
: [PrTriggerLabels.DEPLOY];
const hasDeployLabel = labelNames.some((label) => deployLabels.includes(label));
const isDeploy = hasDeployLabel || autoDeploy;
const isKillSwitch = await enableKillSwitch({
isBotUser: isBot,
Expand Down
4 changes: 2 additions & 2 deletions src/server/services/pullRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { UniqueViolationError } from 'objection';
import _ from 'lodash';
import * as github from 'server/lib/github';
import { JOB_VERSION } from 'shared/config';
import { Labels } from 'shared/constants';
import { PrTriggerLabels } from 'shared/constants';
import { redisClient } from 'server/lib/dependencies';

export interface PullRequestOptions {
Expand Down Expand Up @@ -111,7 +111,7 @@ export default class PullRequestService extends BaseService {
pullRequest.repository.githubInstallationId,
pullRequest.repository.fullName.split('/')[0],
pullRequest.repository.fullName.split('/')[1],
[Labels.DEPLOY],
PrTriggerLabels.DEPLOY,
'open'
);
return hasLabel;
Expand Down
7 changes: 5 additions & 2 deletions src/shared/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,17 @@ export enum CommentParser {
}

export enum Labels {
DEPLOY = 'lifecycle-deploy!',
DISABLED = 'lifecycle-disabled!',
DEPLOY_STG = 'lifecycle-stg-deploy!',
ENABLE_LIFECYCLE_STATUS_COMMENTS = 'lifecycle-status-comments!', // deprecated, no longer used
DISABLE_BUILD_COMMENTS = 'disable-build-comments!', // add this label to disable build status comments
PURGE_FASTLY_SERVICE_CACHE = 'lifecycle-cache-purge!',
}

export const PrTriggerLabels = {
DEPLOY: ['lets-go!', 'lifecycle-deploy!'],
DISABLED: ['auto-deploy-disabled!', 'lifecycle-disabled!'],
};

export enum FeatureFlags {
// if enabled will not set defaultUUID for services that are not checked or don't exisit in the environment
NO_DEFAULT_ENV_RESOLVE = 'no-default-env-resolve',
Expand Down
Loading