Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .github/workflows/pr-linter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ on:
types:
- completed

# Trigger when a status is updated (CodeBuild leads to statuses)
status: {}

# Trigger when a check suite is completed (GitHub actions and CodeCov create checks)
check_suite:
types: [completed]
Expand Down
2 changes: 1 addition & 1 deletion tools/@aws-cdk/prlint/constants.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

export const DEFAULT_LINTER_LOGIN = 'aws-cdk-automation';

export const CODE_BUILD_CONTEXT = 'AWS CodeBuild us-east-1 (AutoBuildv2Project1C6BFA3F-wQm2hXv2jqQv)';
export const CODE_BUILD_WORKFLOW_FILE = 'codebuild-pr-build.yml';

/**
* Types of exemption labels in aws-cdk project.
Expand Down
19 changes: 1 addition & 18 deletions tools/@aws-cdk/prlint/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import * as github from '@actions/github';
import { Octokit } from '@octokit/rest';
import { StatusEvent, PullRequestEvent, CheckSuiteEvent } from '@octokit/webhooks-definitions/schema';
import { PullRequestLinter } from './lint';
import { LinterActions } from './linter-base';
import { DEFAULT_LINTER_LOGIN } from './constants';

/**
Expand Down Expand Up @@ -42,23 +41,7 @@ async function run() {
linterLogin: process.env.LINTER_LOGIN || DEFAULT_LINTER_LOGIN,
});

let actions: LinterActions | undefined;

switch (github.context.eventName) {
case 'status':
// Triggering on a 'status' event is solely used to see if the CodeBuild
// job just turned green, and adding certain 'ready for review' labels
// if it does.
const statusPayload = github.context.payload as StatusEvent;
console.log('validating status event');
actions = await prLinter.validateStatusEvent(statusPayload);
break;

default:
// This is the main PR validator action.
actions = await prLinter.validatePullRequestTarget();
break;
}
const actions = await prLinter.validatePullRequestTarget();

if (actions) {
console.log(actions);
Expand Down
21 changes: 7 additions & 14 deletions tools/@aws-cdk/prlint/lint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import { findModulePath, moduleStability } from './module';
import { breakingModules } from './parser';
import { CheckRun, GitHubFile, GitHubPr, Review, sumChanges, summarizeRunConclusions } from "./github";
import { TestResult, ValidationCollector } from './results';
import { CODE_BUILD_CONTEXT, CODECOV_CHECKS, Exemption } from './constants';
import { StatusEvent } from '@octokit/webhooks-definitions/schema';
import { CODE_BUILD_WORKFLOW_FILE, CODECOV_CHECKS, Exemption } from './constants';
import { LinterActions, mergeLinterActions, PR_FROM_MAIN_ERROR, PullRequestLinterBase } from './linter-base';

/**
Expand All @@ -20,21 +19,15 @@ export class PullRequestLinter extends PullRequestLinterBase {
* @param sha the commit sha to evaluate
*/
private async codeBuildJobSucceeded(sha: string): Promise<boolean> {
const statuses = await this.client.repos.listCommitStatusesForRef({
const statuses = await this.client.actions.listWorkflowRuns({
owner: this.prParams.owner,
repo: this.prParams.repo,
ref: sha,
head_sha: sha,
workflow_id: CODE_BUILD_WORKFLOW_FILE,
});
let status = statuses.data.filter(status => status.context === CODE_BUILD_CONTEXT).map(status => status.state);
console.log("CodeBuild Commit Statuses: ", status);
return statuses.data.some(status => status.context === CODE_BUILD_CONTEXT && status.state === 'success');
}

public async validateStatusEvent(status: StatusEvent): Promise<LinterActions> {
if (status.context === CODE_BUILD_CONTEXT && status.state === 'success') {
return this.assessNeedsReview();
}
return {};
let conclusions = statuses.data.workflow_runs.filter(run => run.status === 'completed').map(run => run.conclusion);
console.log("CodeBuild Commit Conclusions: ", conclusions);
return conclusions.some(conclusion => conclusion === 'success');
}

/**
Expand Down
155 changes: 52 additions & 103 deletions tools/@aws-cdk/prlint/test/lint.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import * as path from 'path';
import { GitHubFile, GitHubLabel, GitHubPr } from '../github';
import { CODE_BUILD_CONTEXT, CODECOV_CHECKS } from '../constants';
import { CODECOV_CHECKS } from '../constants';
import { PullRequestLinter } from '../lint';
import { StatusEvent } from '@octokit/webhooks-definitions/schema';
import { createOctomock, OctoMock } from './octomock';

type GitHubFileName = Omit<GitHubFile, 'deletions' | 'additions'>;
Expand Down Expand Up @@ -589,13 +588,22 @@ describe('integration tests required on features', () => {
});
});

describe('assess needs review from status event', () => {
describe('assess needs review', () => {
const pr = {
title: 'chore(s3): something',
draft: false,
mergeable_state: 'behind',
number: 1234,
labels: [{ name: 'p2' }],
};
const files = [
{
filename: 'some-test.test.ts',
},
{
filename: 'readme.md',
},
];
beforeEach(() => {
mockListReviews.mockImplementation(() => {
return {
Expand All @@ -606,12 +614,8 @@ describe('integration tests required on features', () => {

test('needs a review', async () => {
// WHEN
const prLinter = configureMock(pr);
await legacyValidateStatusEvent(prLinter, {
sha: SHA,
context: CODE_BUILD_CONTEXT,
state: 'success',
} as any);
const prLinter = configureMock(pr, files);
await legacyValidatePullRequestTarget(prLinter);

// THEN
expect(mockAddLabel.mock.calls[0][0]).toEqual({
Expand All @@ -626,12 +630,8 @@ describe('integration tests required on features', () => {
test('needs a review and is p1', async () => {
// WHEN
pr.labels = [{ name: 'p1' }];
const prLinter = configureMock(pr);
await legacyValidateStatusEvent(prLinter, {
sha: SHA,
context: CODE_BUILD_CONTEXT,
state: 'success',
} as any);
const prLinter = configureMock(pr, files);
await legacyValidatePullRequestTarget(prLinter);

// THEN
expect(mockAddLabel.mock.calls[0][0]).toEqual({
Expand All @@ -657,12 +657,8 @@ describe('integration tests required on features', () => {
];

// WHEN
const prLinter = configureMock(pr);
await legacyValidateStatusEvent(prLinter, {
sha: SHA,
context: CODE_BUILD_CONTEXT,
state: 'success',
} as any);
const prLinter = configureMock(pr, files);
await legacyValidatePullRequestTarget(prLinter);

// THEN
expect(mockRemoveLabel.mock.calls[0][0]).toEqual({
Expand All @@ -688,12 +684,8 @@ describe('integration tests required on features', () => {
];

// WHEN
const prLinter = configureMock(pr);
await legacyValidateStatusEvent(prLinter, {
sha: SHA,
context: CODE_BUILD_CONTEXT,
state: 'success',
} as any);
const prLinter = configureMock(pr, files);
await legacyValidatePullRequestTarget(prLinter);

// THEN
expect(mockAddLabel.mock.calls[0][0]).toEqual({
Expand Down Expand Up @@ -725,12 +717,8 @@ describe('integration tests required on features', () => {
];

// WHEN
const prLinter = configureMock(pr);
await legacyValidateStatusEvent(prLinter, {
sha: SHA,
context: CODE_BUILD_CONTEXT,
state: 'success',
} as any);
const prLinter = configureMock(pr, files);
await legacyValidatePullRequestTarget(prLinter);

// THEN
expect(mockRemoveLabel.mock.calls[0][0]).toEqual({
Expand Down Expand Up @@ -758,12 +746,8 @@ describe('integration tests required on features', () => {
];

// WHEN
const prLinter = configureMock(pr);
await legacyValidateStatusEvent(prLinter, {
sha: SHA,
context: CODE_BUILD_CONTEXT,
state: 'success',
} as any);
const prLinter = configureMock(pr, files);
await legacyValidatePullRequestTarget(prLinter);

// THEN
expect(mockRemoveLabel.mock.calls[0][0]).toEqual({
Expand Down Expand Up @@ -791,12 +775,8 @@ describe('integration tests required on features', () => {
];

// WHEN
const prLinter = configureMock(pr);
await legacyValidateStatusEvent(prLinter, {
sha: SHA,
context: CODE_BUILD_CONTEXT,
state: 'success',
} as any);
const prLinter = configureMock(pr, files);
await legacyValidatePullRequestTarget(prLinter);

// THEN
expect(mockRemoveLabel.mock.calls[0][0]).toEqual({
Expand Down Expand Up @@ -831,12 +811,8 @@ describe('integration tests required on features', () => {
];

// WHEN
const prLinter = configureMock(pr);
await legacyValidateStatusEvent(prLinter, {
sha: SHA,
context: CODE_BUILD_CONTEXT,
state: 'success',
} as any);
const prLinter = configureMock(pr, files);
await legacyValidatePullRequestTarget(prLinter);

// THEN
expect(mockRemoveLabel.mock.calls[0][0]).toEqual({
Expand Down Expand Up @@ -869,12 +845,8 @@ describe('integration tests required on features', () => {
];

// WHEN
const prLinter = configureMock(pr);
await legacyValidateStatusEvent(prLinter, {
sha: SHA,
context: CODE_BUILD_CONTEXT,
state: 'success',
} as any);
const prLinter = configureMock(pr, files);
await legacyValidatePullRequestTarget(prLinter);

// THEN
expect(mockRemoveLabel.mock.calls[0][0]).toEqual({
Expand All @@ -899,12 +871,8 @@ describe('integration tests required on features', () => {
(pr as any).labels = [];

// WHEN
const prLinter = configureMock(pr);
await legacyValidateStatusEvent(prLinter, {
sha: SHA,
context: CODE_BUILD_CONTEXT,
state: 'success',
} as any);
const prLinter = configureMock(pr, files);
await legacyValidatePullRequestTarget(prLinter);

// THEN
expect(mockRemoveLabel.mock.calls).toEqual([]);
Expand All @@ -927,12 +895,8 @@ describe('integration tests required on features', () => {
];

// WHEN
const prLinter = configureMock(pr);
await legacyValidateStatusEvent(prLinter, {
sha: SHA,
context: CODE_BUILD_CONTEXT,
state: 'success',
} as any);
const prLinter = configureMock(pr, files);
await legacyValidatePullRequestTarget(prLinter);

// THEN
expect(mockRemoveLabel.mock.calls).toEqual([]);
Expand All @@ -956,12 +920,8 @@ describe('integration tests required on features', () => {
];

// WHEN
const prLinter = configureMock(pr);
await legacyValidateStatusEvent(prLinter, {
sha: SHA,
context: CODE_BUILD_CONTEXT,
state: 'success',
} as any);
const prLinter = configureMock(pr, files);
await legacyValidatePullRequestTarget(prLinter);

// THEN
expect(mockRemoveLabel.mock.calls[0][0]).toEqual({
Expand All @@ -986,12 +946,8 @@ describe('integration tests required on features', () => {
(pr as any).labels = [];

// WHEN
const prLinter = configureMock(pr);
await legacyValidateStatusEvent(prLinter, {
sha: SHA,
context: CODE_BUILD_CONTEXT,
state: 'success',
} as any);
const prLinter = configureMock(pr, files);
await legacyValidatePullRequestTarget(prLinter);

// THEN
expect(mockRemoveLabel.mock.calls).toEqual([]);
Expand Down Expand Up @@ -1019,12 +975,8 @@ describe('integration tests required on features', () => {
];

// WHEN
const prLinter = configureMock(pr);
await legacyValidateStatusEvent(prLinter, {
sha: SHA,
context: CODE_BUILD_CONTEXT,
state: 'success',
} as any);
const prLinter = configureMock(pr, files);
await legacyValidatePullRequestTarget(prLinter);

// THEN
expect(mockRemoveLabel.mock.calls).toEqual([]);
Expand Down Expand Up @@ -1367,12 +1319,6 @@ function configureMock(pr: Subset<GitHubPr>, prFiles?: GitHubFileName[], existin
});
octomock.issues.addLabels = mockAddLabel;
octomock.issues.removeLabel = mockRemoveLabel;
octomock.repos.listCommitStatusesForRef.mockImplementation(() => ({
data: [{
context: CODE_BUILD_CONTEXT,
state: 'success',
}],
}));

// We need to pretend that all CodeCov checks are passing by default, otherwise
// the linter will complain about these even in tests that aren't testing for this.
Expand All @@ -1384,6 +1330,19 @@ function configureMock(pr: Subset<GitHubPr>, prFiles?: GitHubFileName[], existin
})),
}));

// Mock workflow runs to simulate successful CodeBuild jobs
octomock.actions.listWorkflowRuns.mockImplementation(() => ({
data: {
workflow_runs: [
{
head_sha: SHA,
status: 'completed',
conclusion: 'success',
},
],
},
}));

const linter = new PullRequestLinter({
owner: 'aws',
repo: 'aws-cdk',
Expand Down Expand Up @@ -1420,13 +1379,3 @@ async function legacyValidatePullRequestTarget(prLinter: PullRequestLinter) {
await prLinter.executeActions(actions);
prLinter.actionsToException(actions);
}

/**
* Same as for validatePullRequesTarget
*
* @deprecated Assert on the contents of `LinterActions` instead.
*/
async function legacyValidateStatusEvent(prLinter: PullRequestLinter, statusEvent: StatusEvent) {
const actions = await prLinter.validateStatusEvent(statusEvent);
await prLinter.executeActions(actions);
}
2 changes: 1 addition & 1 deletion tools/@aws-cdk/prlint/test/linter-base.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ beforeEach(() => {
},
});
octomock.pulls.listReviews.mockReturnValue({ data: [] });
octomock.repos.listCommitStatusesForRef.mockReturnValue({ data: [] });
octomock.actions.listWorkflowRuns.mockReturnValue({ data: [] });
});

test('ignore if dismissing reviews throws a specific "already dismissed" error', async () => {
Expand Down
Loading
Loading