Skip to content

Commit

Permalink
fix(install-node): retry install if failed with backoff (#71)
Browse files Browse the repository at this point in the history
sometimes networks be networking and we run into flaky installs trying
to download. This should allow some basic retry to help smooth over some
issues.
  • Loading branch information
barbados-clemens authored and github-actions[bot] committed Oct 28, 2024
1 parent a27937f commit d1f3eb9
Showing 1 changed file with 78 additions and 49 deletions.
127 changes: 78 additions & 49 deletions workflow-steps/install-node/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,39 @@ const { platform } = require('os');
const { execSync } = require('child_process');
const { existsSync, readFileSync } = require('fs');

if (platform === 'win32') {
throw new Error('Windows is not supported with this reuseable step yet.');
} else {
// Allow using inputs or env until we fully switch to inputs
const nodeVersionInput =
process.env.NX_CLOUD_INPUT_node_version || process.env.NODE_VERSION;
async function main() {
if (platform === 'win32') {
throw new Error('Windows is not supported with this reuseable step yet.');
} else {
// Allow using inputs or env until we fully switch to inputs
const nodeVersionInput =
process.env.NX_CLOUD_INPUT_node_version || process.env.NODE_VERSION;
const maxRetries = process.env.NX_CLOUD_INPUT_max_retries || 3;

// set defaults incase they are not set yet
process.env.NVM_DIR ??= '/home/workflows/.nvm';
process.env.COREPACK_ENABLE_AUTO_PIN ??= 0;
// set defaults incase they are not set yet
process.env.NVM_DIR ??= '/home/workflows/.nvm';
process.env.COREPACK_ENABLE_AUTO_PIN ??= 0;

const maybeVoltaNodeVersion = getVoltaNodeVersion();
const maybeVoltaNodeVersion = getVoltaNodeVersion();

if (nodeVersionInput) {
runNvmInstall(nodeVersionInput);
} else if (isUsingNvm()) {
// nvm will auto detect version in .nvmrc, no need to pass version
runNvmInstall(null);
} else if (maybeVoltaNodeVersion) {
runNvmInstall(maybeVoltaNodeVersion);
} else {
console.warn(
`No node version specified. You can use the step inputs to define a node version.`,
);
console.log(
`Falling back to the default node version in the base image. ${execSync(
'node -v',
)}`,
);
if (nodeVersionInput) {
await runNvmInstall(nodeVersionInput, maxRetries);
} else if (isUsingNvm()) {
// nvm will auto detect version in .nvmrc, no need to pass version
await runNvmInstall(null, maxRetries);
} else if (maybeVoltaNodeVersion) {
await runNvmInstall(maybeVoltaNodeVersion, maxRetries);
} else {
console.warn(
`No node version specified. You can use the step inputs to define a node version.`,
);
console.log(
`Falling back to the default node version in the base image. ${execSync(
'node -v',
)}`,
);
}
}

function getVoltaNodeVersion() {
try {
if (existsSync('package.json')) {
Expand All @@ -54,24 +56,23 @@ if (platform === 'win32') {
}
}

function runNvmInstall(version) {
try {
// enable nvm and then run the install command with -b to only install pre-build binaries
// nvm command isn't available since nx agents don't run the bash profile
const installNodeWithNvm = `. $NVM_DIR/nvm.sh && nvm install -b ${
version || ''
} --default`;
const reenableCorePack = `corepack enable`;
// install outside of the current directory,
// otherwise corepack errors if a different package mangager is used than is defined in the workspace
const reinstallPackageManagers = `cd .. && corepack prepare yarn@1 && corepack prepare pnpm@8`;
const printVersions = ['node', 'npm', 'yarn', 'pnpm']
.map((cmd) => `echo "${cmd}: $(${cmd} -v)"`)
.join(' && ');

// path will be updated via nvm to include the new node versions,
const saveEnvVars = `echo "PATH=$PATH\nNVM_DIR=${process.env.NVM_DIR}\nCOREPACK_ENABLE_AUTO_PIN=0" >> $NX_CLOUD_ENV`;
async function runNvmInstall(version, maxRetries = 3) {
// enable nvm and then run the install command with -b to only install pre-build binaries
// nvm command isn't available since nx agents don't run the bash profile
const installNodeWithNvm = `. $NVM_DIR/nvm.sh && nvm install -b ${
version || ''
} --default`;
const reenableCorePack = `corepack enable`;
// install outside of the current directory,
// otherwise corepack errors if a different package mangager is used than is defined in the workspace
const reinstallPackageManagers = `cd .. && corepack prepare yarn@1 && corepack prepare pnpm@8`;
const printVersions = ['node', 'npm', 'yarn', 'pnpm']
.map((cmd) => `echo "${cmd}: $(${cmd} -v)"`)
.join(' && ');

// path will be updated via nvm to include the new node versions,
const saveEnvVars = `echo "PATH=$PATH\nNVM_DIR=${process.env.NVM_DIR}\nCOREPACK_ENABLE_AUTO_PIN=0" >> $NX_CLOUD_ENV`;
const run = () =>
execSync(
[
installNodeWithNvm,
Expand All @@ -84,11 +85,39 @@ if (platform === 'win32') {
stdio: 'inherit',
},
);
} catch (e) {
console.error(e);
throw new Error(
`Failed to install node version using nvm ${version || ''}`,
);

let retryCount = 0;

while (retryCount < maxRetries) {
try {
run();
break;
} catch (e) {
retryCount++;

if (retryCount >= maxRetries) {
throw new Error(
`Failed to install node version using nvm ${version || ''}`,
);
}

const delay = Math.max(
3_000,
Math.pow(2, retryCount) * Math.random() * 1_250,
);
console.log(
`Installing node failed. Retrying install in ${(delay / 1000).toFixed(
0,
)} seconds...`,
);
if (process.env.NX_VERBOSE_LOGGING === 'true') {
console.warn(e);
}

await new Promise((resolve) => setTimeout(resolve, delay));
}
}
}
}

main();

0 comments on commit d1f3eb9

Please sign in to comment.