Skip to content

Instantly share code, notes, and snippets.

@aboqasem
Last active March 29, 2023 00:08
Show Gist options
  • Save aboqasem/c13cd589e42698928ee0bf4556255dd0 to your computer and use it in GitHub Desktop.
Save aboqasem/c13cd589e42698928ee0bf4556255dd0 to your computer and use it in GitHub Desktop.
Script to ignore building & deploying unaffected Nx app in Vercel
// @ts-check
const { exec } = require('child_process');
/**
* @typedef {{ command: string; description?: string; } | string} CommandOptions
* @typedef {{ out?: string; err?: string; code: number | null }} CommandOutput
*/
/** @type {(options: CommandOptions) => Promise<CommandOutput>} */
function execute(options) {
const command = typeof options === 'object' ? options.command : options;
const description = typeof options === 'object' ? options.description : undefined;
description && console.info(`[${execute.name}] - ${description}`);
console.info(`[${execute.name}] > ${command}`);
const child = exec(command);
/** @type {string | undefined} */
let out;
/** @type {string | undefined} */
let err;
return new Promise((resolve) => {
child.stdout?.on('data', (data) => {
const outData = data.toString();
out = (out ?? '') + outData;
process.stdout.write(data);
});
child.stderr?.on('data', (data) => {
const errData = data.toString();
err = (err ?? '') + errData;
process.stderr.write(data);
});
child.on('exit', (code) => {
resolve({ out, err, code });
});
});
}
module.exports = { execute };
// @ts-check
/**
* Edit the command in 'Vercel Project > Project Settings > Git > Ignored Build Step' to: `node should-vercel-build.js <APP_NAME>`
* Add GitHub access token as `GITHUB_ACCESS_TOKEN` environment variable with `repo_deployment` scope enabled.
*/
const { execute } = require('./execute');
const appName = process.argv[2];
// production, preview, or development.
const vercelEnv = process.env.VERCEL_ENV;
const owner = process.env.VERCEL_GIT_REPO_OWNER;
const repo = process.env.VERCEL_GIT_REPO_SLUG;
const currCommitSha = process.env.VERCEL_GIT_COMMIT_SHA;
const auth = process.env.GITHUB_ACCESS_TOKEN;
const required = { appName, vercelEnv, auth, owner, repo, currCommitSha };
async function shouldBuild() {
if (
Object.entries(required).some(([k, v]) => {
if (!v) {
console.warn(`⚠️ - Required arg '${k}' not set.`);
return true;
}
return false;
})
) {
return true;
}
const packageJson = require('../../package.json');
const nxDependencies = Object.keys(packageJson.dependencies).filter((dep) =>
dep.includes('@nrwl'),
);
const nxDevDependencies = Object.keys(packageJson.devDependencies).filter((dep) =>
dep.includes('@nrwl'),
);
await execute(`npm install @octokit/rest ${nxDependencies.join(' ')}`);
await execute(`npm install -D typescript ${nxDevDependencies.join(' ')}`);
const { Octokit } = require('@octokit/rest');
const octokit = new Octokit({ auth });
const lastDeployment = await octokit.repos
.listDeployments({
owner,
repo,
environment: `${vercelEnv[0].toUpperCase()}${vercelEnv.substring(1)}`,
per_page: 1,
// skip current deployment
page: 2,
})
.then(({ data }) => data[0]);
console.debug('Last deployment:', lastDeployment);
const lastDeployedCommitSha = lastDeployment.sha;
return execute({
command: `npx nx print-affected --base=${lastDeployedCommitSha} --head=${currCommitSha} --target=build --select=projects`,
description: `Checking whether ${appName} has changed since last deployment`,
}).then(({ out, err }) => {
// build & deploy if
if (
// an error happened
code ||
// or no output received
out === undefined ||
// or the app has changed since last deployed commit
out
.split(', ')
.map((v) => v.trim())
.includes(appName)
) {
return true;
}
return false;
});
}
async function main() {
await shouldBuild()
.then((result) => {
if (result) {
console.log('✅ - Build can proceed');
return process.exit(1);
}
console.log('🛑 - Build ignored');
return process.exit(0);
})
.catch((e) => {
console.error(e);
console.log('✅ - Build can proceed');
return process.exit(1);
});
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment