Last active
March 29, 2023 00:08
-
-
Save aboqasem/c13cd589e42698928ee0bf4556255dd0 to your computer and use it in GitHub Desktop.
Script to ignore building & deploying unaffected Nx app in Vercel
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @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 }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @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