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

Fix vercel redeploy #4119

Merged
merged 9 commits into from
Nov 1, 2023
Merged
Show file tree
Hide file tree
Changes from 8 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
54 changes: 32 additions & 22 deletions .github/actions/deploy-vercel/index.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @ts-check

import { Client } from "./vercel.mjs";
import { assert, find, get, getRequiredInput } from "./util.mjs";
import { assert, getRequiredInput, info } from "./util.mjs";

// These inputs are defined in `action.yml`, and should be kept in sync
const token = getRequiredInput("vercel_token");
Expand All @@ -11,25 +11,35 @@ const releaseCommit = getRequiredInput("release_commit");

const client = new Client(token);

console.log(`Fetching team \`${teamName}\``);
const team = await client.teams().then(find("name", teamName));
assert(team, `failed to get team \`${teamName}\``);

console.log(`Fetching project \`${projectName}\``);
const project = await client.projects(team.id).then(find("name", projectName));
assert(project, `failed to get project \`${projectName}\``);

console.log(`Fetching latest deployment`);
const deployment = await client.deployments(team.id, project.id).then(get(0));
assert(deployment, `failed to get latest deployment`);

console.log(`Fetching \`RELEASE_COMMIT\` env var`);
const env = await client.envs(team.id, project.id).then(find("key", "RELEASE_COMMIT"));
assert(env, `failed to get \`RELEASE_COMMIT\` env var`);

console.log(`Setting \`RELEASE_COMMIT\` env to \`${releaseCommit}\``);
await client.setEnv(team.id, project.id, env.id, { key: "RELEASE_COMMIT", value: releaseCommit });

console.log(`Triggering redeploy`);
await client.redeploy(team.id, deployment.uid, "landing");
info`Fetching team "${teamName}"`;
Copy link
Member

Choose a reason for hiding this comment

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

What magic syntax is this O.o

Copy link
Member Author

Choose a reason for hiding this comment

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

const availableTeams = await client.teams();
assert(availableTeams, `failed to get team "${teamName}"`);
const team = availableTeams.find((team) => team.name === teamName);
assert(team, `failed to get team "${teamName}"`);

info`Fetching project "${projectName}"`;
const projectsInTeam = await client.projects(team.id);
const project = projectsInTeam.find((project) => project.name === projectName);
assert(project, `failed to get project "${projectName}"`);

info`Fetching latest production deployment`;
const productionDeployments = await client.deployments(team.id, project.id);
const latestProductionDeployment = productionDeployments[0];
assert(latestProductionDeployment, `failed to get latest production deployment`);

const RELEASE_COMMIT_KEY = "RELEASE_COMMIT";

info`Fetching "${RELEASE_COMMIT_KEY}" env var`;
const environment = await client.envs(team.id, project.id);
const releaseCommitEnv = environment.find((env) => env.key === RELEASE_COMMIT_KEY);
assert(releaseCommitEnv, `failed to get "${RELEASE_COMMIT_KEY}" env var`);

info`Setting "${RELEASE_COMMIT_KEY}" env to "${releaseCommit}"`;
await client.setEnv(team.id, project.id, releaseCommitEnv.id, {
key: RELEASE_COMMIT_KEY,
value: releaseCommit,
});

info`Triggering redeploy`;
await client.redeploy(team.id, latestProductionDeployment.uid, "landing");

54 changes: 27 additions & 27 deletions .github/actions/deploy-vercel/util.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
// @ts-check

/**
* Log a message with level `INFO`
*
* @param {TemplateStringsArray} strings
* @param {any[]} values
*/
export function info(strings, ...values) {
let out = "";
for (let i = 0; i < strings.length; i++) {
out += strings[i];
if (i < values.length) {
out += values[i].toString();
}
}
console.info(out);
}

/**
* Return a GitHub Actions input, returning `null` if it was not set.
*
Expand Down Expand Up @@ -29,37 +46,20 @@ export function getRequiredInput(name) {
* Assert that `value` is truthy, throwing an error if it is not.
*
* @param {any} value
* @param {string} [message]
* @param {string | (() => string)} [message]
* @returns {asserts value}
*/
export function assert(value, message) {
if (!value) {
throw new Error(`assertion failed` + (message ? ` ${message}` : ""));
let error;
if (typeof message === "string") {
error = `assertion failed: ${message}`;
} else if (typeof message === "function") {
error = `assertion failed: ${message()}`;
} else {
error = `assertion failed`;
}
throw new Error(error);
}
}

/**
* Returns a function that attempts to find an object with
* `key` set to `value` in an array of objects with `key` properties.
*
* @template {string} Key
* @template {{ [p in Key]: string }} T
* @param {Key} key
* @param {string} value
* @returns {(a: T[]) => T|null}
*/
export function find(key, value) {
return (a) => a.find((v) => v[key] === value) ?? null;
}

/**
* Returns a function that attempts to retrieve the value at `index` from an array.
*
* @template T
* @param {number} index
* @returns {(a: T[]) => T|null}
*/
export function get(index) {
return (a) => a[index] ?? null;
}

28 changes: 23 additions & 5 deletions .github/actions/deploy-vercel/vercel.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// @ts-check
import { assert } from "./util.mjs";

/**
* @typedef {Record<string, string>} Params
Expand Down Expand Up @@ -104,7 +105,9 @@ export class Client {
* @returns {Promise<Team[]>}
*/
async teams() {
return await this.get("v2/teams").then((r) => r.teams);
const response = await this.get("v2/teams");
assert("teams" in response, () => `failed to get teams: ${JSON.stringify(response)}`);
return response.teams;
}

/**
Expand All @@ -115,7 +118,9 @@ export class Client {
* @returns {Promise<Project[]>}
*/
async projects(teamId) {
return await this.get("v9/projects", { teamId }).then((r) => r.projects);
const response = await this.get("v9/projects", { teamId });
assert("projects" in response, () => `failed to get projects: ${JSON.stringify(response)}`);
return response.projects;
}

/**
Expand All @@ -133,9 +138,17 @@ export class Client {
* @returns {Promise<Deployment[]>}
*/
async deployments(teamId, projectId, target = "production") {
return await this.get("v6/deployments", { teamId, projectId, target, sort: "created" }).then(
(r) => r.deployments
const response = await this.get("v6/deployments", {
teamId,
projectId,
target,
sort: "created",
});
assert(
"deployments" in response,
() => `failed to get deployments: ${JSON.stringify(response)}`
);
return response.deployments;
}

/**
Expand All @@ -146,7 +159,12 @@ export class Client {
* @returns {Promise<Env[]>}
*/
async envs(teamId, projectId) {
return await this.get(`v9/projects/${projectId}/env`, { teamId }).then((r) => r.envs);
const response = await this.get(`v9/projects/${projectId}/env`, { teamId });
assert(
"envs" in response,
() => `failed to get environment variables: ${JSON.stringify(response)}`
);
return response.envs;
}

/**
Expand Down
Loading