Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support the workflow_run event #147

Merged
merged 4 commits into from
Apr 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
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
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ This will trigger on all pushes and automatically update any pull requests, if c

For more information on customising event triggers, see [Github's documentation](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/events-that-trigger-workflows#push-event-push).

The following events are supported:
* push
* pull_request
* workflow_run

## Configuration
The following configuration options are supported. To change any of these, simply specify it as an `env` value in your workflow file.
Expand Down Expand Up @@ -101,10 +105,10 @@ Here's a screenshot:
* There is [an open issue in the Github community forum](https://github.saobby.my.eu.orgmunity/t5/GitHub-Actions/Triggering-a-new-workflow-from-another-workflow/td-p/31676) tracking this

## Coming soon
* Conflict handling
* Rebase support
* Label negation support
* Token support in custom merge messages
* `schedule` event support

## Also see
* [automerge](https://github.com/pascalgn/automerge-action/) for automatic merging of PRs
Expand Down
19 changes: 17 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@
"@actions/core": "^1.2.6",
"@actions/github": "^4.0.0",
"@octokit/types": "^6.13.0",
"@octokit/webhooks": "^9.0.0",
"@types/node": "^14.14.37",
"@vercel/ncc": "^0.27.0",
"ttypescript": "^1.5.12",
"typescript": "^4.2.4"
},
"devDependencies": {
Expand All @@ -33,16 +35,29 @@
"eslint-plugin-jest": "^24.3.5",
"eslint-plugin-prettier": "^3.3.1",
"jest": "^26.6.3",
"jest-ts-auto-mock": "^2.0.0",
"nock": "^13.0.11",
"prettier": "^2.2.1",
"ts-auto-mock": "^3.1.2",
"ts-jest": "^26.5.4"
},
"jest": {
"preset": "ts-jest",
"clearMocks": true,
"collectCoverage": true,
"coverageDirectory": "coverage",
"coverageProvider": "v8",
"testEnvironment": "node"
"globals": {
"ts-jest": {
"compiler": "ttypescript"
}
},
"preset": "ts-jest",
"setupFiles": [
"<rootDir>/test/config.ts"
],
"testEnvironment": "node",
"transform": {
".(ts|tsx)": "ts-jest"
}
}
}
96 changes: 67 additions & 29 deletions src/autoupdater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,87 @@ import * as github from '@actions/github';
import { GitHub } from '@actions/github/lib/utils';
import * as ghCore from '@actions/core';
import * as octokit from '@octokit/types';
import {
PullRequestEvent,
PushEvent,
Repository,
WebhookEvent,
WorkflowRunEvent,
} from '@octokit/webhooks-definitions/schema';
import { ConfigLoader } from './config-loader';
import { Endpoints } from '@octokit/types';

type PullRequest =
| PullRequestResponse['data']
| PullRequestEvent['pull_request'];
type PullRequestResponse = Endpoints['GET /repos/{owner}/{repo}/pulls/{pull_number}']['response'];
type MergeParameters = Endpoints['POST /repos/{owner}/{repo}/merges']['parameters'];

export class AutoUpdater {
eventData: any;
// See https://docs.github.com/en/developers/webhooks-and-events/webhook-events-and-payloads
eventData: WebhookEvent;
config: ConfigLoader;
octokit: InstanceType<typeof GitHub>;

constructor(
config: ConfigLoader,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
eventData: any,
) {
constructor(config: ConfigLoader, eventData: WebhookEvent) {
this.eventData = eventData;
this.config = config;
this.octokit = github.getOctokit(this.config.githubToken());
}

async handlePush(): Promise<number> {
const { ref, repository } = this.eventData;
const { ref, repository } = this.eventData as PushEvent;

ghCore.info(`Handling push event on ref '${ref}'`);

return await this.pulls(ref, repository);
}

async handlePullRequest(): Promise<boolean> {
const { action, pull_request } = this.eventData as PullRequestEvent;

ghCore.info(`Handling pull_request event triggered by action '${action}'`);

const isUpdated = await this.update(pull_request);
if (isUpdated) {
ghCore.info(
'Auto update complete, pull request branch was updated with changes from the base branch.',
);
} else {
ghCore.info('Auto update complete, no changes were made.');
}

return isUpdated;
}

async handleWorkflowRun(): Promise<number> {
const { workflow_run: workflowRun, repository } = this
.eventData as WorkflowRunEvent;
const { head_branch: branch, event } = workflowRun;

if (!['push', 'pull_request'].includes(event)) {
ghCore.error(
`workflow_run events triggered via ${event} workflows are not supported.`,
);
return 0;
}

// This may not be possible given the check above, but here for safety.
if (!branch) {
ghCore.warning('Event was not on a branch, skipping.');
return 0;
}

ghCore.info(
`Handling workflow_run event triggered by '${event}' on '${branch}'`,
);

// The `pull_request` event is handled the same way as `push` as we may
// get multiple PRs.
return await this.pulls(`refs/heads/${branch}`, repository);
}

async pulls(ref: string, repository: Repository): Promise<number> {
if (!ref.startsWith('refs/heads/')) {
ghCore.warning('Push event was not on a branch, skipping.');
return 0;
Expand All @@ -37,7 +92,7 @@ export class AutoUpdater {

let updated = 0;
const paginatorOpts = this.octokit.pulls.list.endpoint.merge({
owner: repository.owner.name,
owner: repository.owner.name || repository.owner.login,
repo: repository.name,
base: baseBranch,
state: 'open',
Expand Down Expand Up @@ -66,24 +121,7 @@ export class AutoUpdater {
return updated;
}

async handlePullRequest(): Promise<boolean> {
const { action } = this.eventData;

ghCore.info(`Handling pull_request event triggered by action '${action}'`);

const isUpdated = await this.update(this.eventData.pull_request);
if (isUpdated) {
ghCore.info(
'Auto update complete, pull request branch was updated with changes from the base branch.',
);
} else {
ghCore.info('Auto update complete, no changes were made.');
}

return isUpdated;
}

async update(pull: PullRequestResponse['data']): Promise<boolean> {
async update(pull: PullRequest): Promise<boolean> {
const { ref } = pull.head;
ghCore.info(`Evaluating pull request #${pull.number}...`);

Expand Down Expand Up @@ -131,7 +169,7 @@ export class AutoUpdater {
return true;
}

async prNeedsUpdate(pull: PullRequestResponse['data']): Promise<boolean> {
async prNeedsUpdate(pull: PullRequest): Promise<boolean> {
if (pull.merged === true) {
ghCore.warning('Skipping pull request, already merged.');
return false;
Expand Down Expand Up @@ -170,7 +208,7 @@ export class AutoUpdater {
if (excludedLabels.length > 0) {
for (const label of pull.labels) {
if (label.name === undefined) {
ghCore.warning(`Label name is undefined, continuing.`);
ghCore.debug(`Label name is undefined, continuing.`);
continue;
}
if (excludedLabels.includes(label.name)) {
Expand Down Expand Up @@ -209,7 +247,7 @@ export class AutoUpdater {

for (const label of pull.labels) {
if (label.name === undefined) {
ghCore.warning(`Label name is undefined, continuing.`);
ghCore.debug(`Label name is undefined, continuing.`);
continue;
}

Expand Down
12 changes: 5 additions & 7 deletions src/router.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import { WebhookEvent } from '@octokit/webhooks-definitions/schema';
import { AutoUpdater } from '../src/autoupdater';
import { ConfigLoader } from '../src/config-loader';

export class Router {
eventData: any;
updater: AutoUpdater;

constructor(
config: ConfigLoader,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
eventData: any,
) {
constructor(config: ConfigLoader, eventData: WebhookEvent) {
this.updater = new AutoUpdater(config, eventData);
}

Expand All @@ -24,9 +20,11 @@ export class Router {
await this.updater.handlePullRequest();
} else if (eventName === 'push') {
await this.updater.handlePush();
} else if (eventName === 'workflow_run') {
await this.updater.handleWorkflowRun();
} else {
throw new Error(
`Unknown event type '${eventName}', only 'push' and 'pull_request' are supported.`,
`Unknown event type '${eventName}', only 'push', 'pull_request' and 'workflow_run' are supported.`,
);
}
}
Expand Down
Loading