Skip to content
This repository was archived by the owner on Sep 13, 2023. It is now read-only.

Commit a6667d4

Browse files
new IsAllowedToLand function that includes an existing request check (#89)
* new IsAllowedToLand function that includes an existing request check * Move self-dep failsafe
1 parent 1c7f5c4 commit a6667d4

File tree

3 files changed

+53
-29
lines changed

3 files changed

+53
-29
lines changed

src/bitbucket/BitbucketClient.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export class BitbucketClient {
1717

1818
constructor(private config: Config) {}
1919

20-
async isAllowedToLand(pullRequestId: number, permissionLevel: IPermissionMode) {
20+
async isAllowedToMerge(pullRequestId: number, permissionLevel: IPermissionMode) {
2121
const pullRequest: BB.PullRequest = await this.bitbucket.getPullRequest(pullRequestId);
2222
const buildStatuses = await this.bitbucket.getPullRequestBuildStatuses(pullRequestId);
2323
const author = pullRequest.author;

src/lib/Runner.ts

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,17 @@ export class Runner {
4949
return this.queue.getRunning();
5050
};
5151

52+
getWaitingAndQueued = async () => {
53+
const queued = await this.getQueue();
54+
const waiting = await this.queue.getStatusesForWaitingRequests();
55+
return [...queued, ...waiting];
56+
};
57+
5258
moveFromQueueToRunning = async (landRequestStatus: LandRequestStatus, lockId: Date) => {
5359
const landRequest = landRequestStatus.request;
5460
const running = await this.getRunning();
5561
const runningTargetingSameBranch = running.filter(
56-
build =>
57-
build.request.pullRequest.targetBranch === landRequest.pullRequest.targetBranch &&
58-
// Failsafe to prevent self-dependencies
59-
build.request.pullRequestId !== landRequest.pullRequestId,
62+
build => build.request.pullRequest.targetBranch === landRequest.pullRequest.targetBranch,
6063
);
6164
const maxConcurrentBuilds = this.getMaxConcurrentBuilds();
6265
if (runningTargetingSameBranch.length >= maxConcurrentBuilds) {
@@ -76,9 +79,10 @@ export class Runner {
7679
landRequest.triggererAaid,
7780
);
7881
const commit = landRequest.forCommit;
79-
const isAllowedToLand = await this.client.isAllowedToLand(
82+
const isAllowedToLand = await this.isAllowedToLand(
8083
landRequest.pullRequestId,
8184
triggererUserMode,
85+
this.getRunning,
8286
);
8387

8488
// ToDo: Extra checks, should probably not be here, but this will do for now
@@ -108,10 +112,10 @@ export class Runner {
108112
return landRequest.setStatus('aborted', 'PR commit changed between landing and running');
109113
}
110114

111-
if (isAllowedToLand.errors.length > 0) {
115+
if (isAllowedToLand.errors.length > 0 || isAllowedToLand.existingRequest) {
112116
Logger.error('LandRequest no longer passes land checks', {
113117
namespace: 'lib:runner:moveFromQueueToRunning',
114-
errors: isAllowedToLand.errors,
118+
isAllowedToLand,
115119
landRequestStatus,
116120
pullRequestId: landRequest.pullRequestId,
117121
landRequestId: landRequest.id,
@@ -120,8 +124,12 @@ export class Runner {
120124
return landRequest.setStatus('fail', 'Unable to land due to failed land checks');
121125
}
122126

127+
const runningExceptSelf = runningTargetingSameBranch.filter(
128+
// Failsafe to prevent self-dependencies
129+
build => build.request.pullRequestId !== landRequest.pullRequestId,
130+
);
123131
const dependencies = [];
124-
for (const queueItem of runningTargetingSameBranch) {
132+
for (const queueItem of runningExceptSelf) {
125133
if ((await queueItem.request.getFailedDependencies()).length === 0) {
126134
dependencies.push(queueItem);
127135
}
@@ -492,19 +500,18 @@ export class Runner {
492500
const triggererUserMode = await permissionService.getPermissionForUser(
493501
landRequest.triggererAaid,
494502
);
495-
const isAllowedToLand = await this.client.isAllowedToLand(pullRequestId, triggererUserMode);
503+
const isAllowedToLand = await this.isAllowedToLand(
504+
pullRequestId,
505+
triggererUserMode,
506+
this.getQueue,
507+
);
496508

497509
if (isAllowedToLand.errors.length === 0) {
498-
const queue = await this.getQueue();
499-
const existingBuild = queue.find(
500-
q => q.request.pullRequestId === landRequest.pullRequestId,
501-
);
502-
if (existingBuild) {
510+
if (isAllowedToLand.existingRequest) {
503511
Logger.warn('Already has existing Land build', {
504512
pullRequestId,
505513
landRequestId: landRequest.id,
506514
landRequestStatus,
507-
existingBuild,
508515
namespace: 'lib:runner:checkWaitingLandRequests',
509516
});
510517
await landRequest.setStatus('aborted', 'Already has existing Land build');
@@ -633,6 +640,26 @@ export class Runner {
633640
}
634641
};
635642

643+
async isAllowedToLand(
644+
pullRequestId: number,
645+
permissionLevel: IPermissionMode,
646+
queueFetcher: () => Promise<LandRequestStatus[]>,
647+
) {
648+
const isAllowedToMerge = await this.client.isAllowedToMerge(pullRequestId, permissionLevel);
649+
let existingRequest = false;
650+
const queue = await queueFetcher();
651+
for (const queueItem of queue) {
652+
if (queueItem.request.pullRequestId === pullRequestId) {
653+
existingRequest = true;
654+
break;
655+
}
656+
}
657+
return {
658+
existingRequest,
659+
...isAllowedToMerge,
660+
};
661+
}
662+
636663
getState = async (requestingUser: ISessionUser): Promise<RunnerState> => {
637664
const requestingUserMode = await permissionService.getPermissionForUser(requestingUser.aaid);
638665
const [

src/routes/bitbucket/proxy/index.ts

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export function proxyRoutes(runner: Runner, client: BitbucketClient) {
3030

3131
const errors: string[] = [];
3232
const warnings: string[] = [];
33-
const landCheckErrors: string[] = [];
33+
let existingRequest = false;
3434
const bannerMessage = await runner.getBannerMessageState();
3535

3636
const permissionLevel = await permissionService.getPermissionForUser(aaid);
@@ -39,19 +39,16 @@ export function proxyRoutes(runner: Runner, client: BitbucketClient) {
3939
if (pauseState) {
4040
errors.push(`Builds have been manually paused: "${pauseState.reason}"`);
4141
} else {
42-
const landChecks = await client.isAllowedToLand(prId, permissionLevel);
42+
const landChecks = await runner.isAllowedToLand(
43+
prId,
44+
permissionLevel,
45+
runner.getWaitingAndQueued,
46+
);
4347
warnings.push(...landChecks.warnings);
4448
errors.push(...landChecks.errors);
45-
landCheckErrors.push(...landChecks.errors);
46-
47-
const queued = await runner.queue.getQueue();
48-
const waiting = await runner.queue.getStatusesForWaitingRequests();
49-
50-
for (const queueItem of [...queued, ...waiting]) {
51-
if (queueItem.request.pullRequest.prId === prId) {
52-
errors.push('This PR has already been queued, patience young padawan');
53-
break;
54-
}
49+
if (landChecks.existingRequest) {
50+
existingRequest = true;
51+
errors.push('This PR has already been queued, patience young padawan');
5552
}
5653
}
5754
} else {
@@ -66,7 +63,7 @@ export function proxyRoutes(runner: Runner, client: BitbucketClient) {
6663

6764
res.json({
6865
canLand: errors.length === 0,
69-
canLandWhenAble: errors.length === landCheckErrors.length && prSettings.allowLandWhenAble,
66+
canLandWhenAble: !existingRequest && prSettings.allowLandWhenAble,
7067
errors,
7168
warnings,
7269
bannerMessage,

0 commit comments

Comments
 (0)