Skip to content

Commit

Permalink
feat: Implement auto deploy on pull requests
Browse files Browse the repository at this point in the history
Use 'auto_deploy_on: pr' configuration to enable automatic deployments
in a pull request.

FIXES: #10
  • Loading branch information
imella committed Dec 27, 2019
1 parent 6dc73c3 commit 65e8dde
Show file tree
Hide file tree
Showing 7 changed files with 1,016 additions and 51 deletions.
35 changes: 35 additions & 0 deletions src/auto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { hash } from "./util";
import { v4 as uuid } from "uuid";
import { PayloadRepository } from "@octokit/webhooks";
import Webhooks from "@octokit/webhooks";
import { handlePRDeploy } from "./pr-deploy";

export function match(auto: string | undefined, ref: string) {
if (!auto) return false;
Expand Down Expand Up @@ -287,4 +288,38 @@ export function auto(
context.payload.repository
);
});

// Auto deploy on open PR event
app.on("pull_request.opened", async context => {
const conf = await config(context.github, context.repo());
for (const target in conf) {
const targetVal = conf[target]!;
if (targetVal.auto_deploy_on === 'pr') {
await handlePRDeploy(
context,
`/deploy ${target}`,
context.payload.number,
context.payload.sender.login,
lockStore
);
}
};
})

// Auto deploy on push to PR
app.on("pull_request.synchronize", async context => {
const conf = await config(context.github, context.repo());
for (const target in conf) {
const targetVal = conf[target]!;
if (targetVal.auto_deploy_on === 'pr') {
await handlePRDeploy(
context,
`/deploy ${target}`,
context.payload.number,
context.payload.sender.login,
lockStore
);
}
};
})
}
108 changes: 58 additions & 50 deletions src/pr-deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,63 +3,71 @@ import { LockStore } from "./store";
import { deploy } from "./deploy";
import { logCtx, canWrite } from "./util";

export function prDeploy(app: Application, locker: LockStore) {
/**
* Handles /deploy commands inside a pull request.
*/
async function handlePRDeploy(
context: Context,
command: string,
kv: LockStore
) {
context.log.info(
logCtx(context, { command }),
"pr deploy: handling command"
);
try {
const target = command.split(" ")[1];
const pr = await context.github.pulls.get({
owner: context.payload.repository.owner.login,
repo: context.payload.repository.name,
pull_number: context.payload.issue.number
});
/**
* Handles /deploy commands inside a pull request.
*/
export async function handlePRDeploy(
context: Context,
command: string,
prNumber: number,
user: string,
kv: LockStore
) {
context.log.info(
logCtx(context, { command }),
"pr deploy: handling command"
);
try {
const target = command.split(" ")[1];
const pr = await context.github.pulls.get({
owner: context.payload.repository.owner.login,
repo: context.payload.repository.name,
pull_number: prNumber
});

const write = await canWrite(
context.github,
context.repo({ username: context.payload.comment.user.login })
);
if (!write) {
context.log.info(
logCtx(context, {}),
"pr deploy: no write priviledges"
);
return;
}

await deploy(
context.github,
context.log,
kv,
context.repo({
target,
ref: pr.data.head.ref,
sha: pr.data.head.sha,
pr: pr.data
})
const write = await canWrite(
context.github,
context.repo({ username: user })
);
if (!write) {
context.log.info(
logCtx(context, {}),
"pr deploy: no write priviledges"
);
} catch (error) {
await context.github.issues.createComment({
owner: context.payload.repository.owner.login,
repo: context.payload.repository.name,
issue_number: context.payload.issue.number,
body: `:rotating_light: Failed to trigger deployment. :rotating_light:\n${error.message}`
});
return;
}
await deploy(
context.github,
context.log,
kv,
context.repo({
target,
ref: pr.data.head.ref,
sha: pr.data.head.sha,
pr: pr.data
})
);
} catch (error) {
await context.github.issues.createComment({
owner: context.payload.repository.owner.login,
repo: context.payload.repository.name,
issue_number: prNumber,
body: `:rotating_light: Failed to trigger deployment. :rotating_light:\n${error.message}`
});
}
}

export function prDeploy(app: Application, locker: LockStore) {

app.on("issue_comment.created", async context => {
if (context.payload.comment.body.startsWith("/deploy")) {
await handlePRDeploy(context, context.payload.comment.body, locker);
await handlePRDeploy(
context,
context.payload.comment.body,
context.payload.issue.number,
context.payload.comment.user.login,
locker
);
}
});
}
18 changes: 18 additions & 0 deletions test/auto.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,24 @@ describe("auto", () => {
expect(deploy.isDone()).toBe(true);
});

test("creates a deployment on pull request opened", async () => {
const deploy = factory.deploy();
factory.config({ valid: true });
factory.noDeployments();

await probot.receive(factory.prOpened());
expect(deploy.isDone()).toBe(true);
})

test("creates a deployment on push to pull request", async () => {
const deploy = factory.deploy();
factory.config({ valid: true });
factory.noDeployments();

await probot.receive(factory.prSync());
expect(deploy.isDone()).toBe(true);
})

test("no deployment if locked", async () => {
const deploy = factory.deploy();
factory.config({ valid: true });
Expand Down
13 changes: 12 additions & 1 deletion test/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ const fixtures = {
push: require("./fixtures/push.json"),
status: require("./fixtures/status.json"),
ref: require("./fixtures/ref.json"),
prOpened: require("./fixtures/pull_request.opened.json"),
prSync: require("./fixtures/pull_request.synchronize.json"),
prClosed: require("./fixtures/pull_request.closed.json"),
commit: require("./fixtures/commit.json"),
checkRunCreated: require("./fixtures/check_run.created.json"),
Expand Down Expand Up @@ -158,6 +160,16 @@ export const prDeployComment = (env: string): any => ({
}
});

export const prOpened = (): any => ({
name: "pull_request",
payload: fixtures.prOpened
});

export const prSync = (): any => ({
name: "pull_request",
payload: fixtures.prSync
});

export const prClosed = (): any => ({
name: "pull_request",
payload: fixtures.prClosed
Expand Down Expand Up @@ -207,4 +219,3 @@ export const errorComment = (expected: string) =>
return true;
})
.reply(200);

1 change: 1 addition & 0 deletions test/fixtures/deploy-valid.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module.exports = `
review:
auto_deploy_on: pr
transient_environment: true
production_environment: false
required_contexts:
Expand Down

0 comments on commit 65e8dde

Please sign in to comment.