diff --git a/workflow-steps/install-node/README.md b/workflow-steps/install-node/README.md new file mode 100644 index 0000000..fd78f85 --- /dev/null +++ b/workflow-steps/install-node/README.md @@ -0,0 +1,33 @@ +## Usage + +> [!WARNING] +> This step current does not support Windows yet. + +```yaml +- name: Install Node + uses: 'nrwl/nx-cloud-workflows/v4/workflow-steps/install-node/main.yaml' + inputs: + node_version: '20' +``` + +### Options + +#### node_version + +The node version to be installed. Any valid [nvm](https://github.com/nvm-sh/nvm/blob/master/README.md#usage) version is accepted. +This input is optional, as the step will also check for a `.nvmrc` file in the root of the repository. +If the file is present, the step will install the version specified in the file. +If the file is not present, the step will not install any node version and leave the default installed node version on the image for subsequent steps + +For those using `volta`, the `volta.node` field in the `package.json` will also be picked up and used if present. + +The current order of precedence is: + +1. `node_version` input +1. `.nvmrc` file +1. `volta.node` field in `package.json` + +For example: + +- Install a specific node version: `node_version: '20.11.1'` +- Install latest of major node version: `node_version: '20'` diff --git a/workflow-steps/install-node/main.js b/workflow-steps/install-node/main.js new file mode 100644 index 0000000..a299a5b --- /dev/null +++ b/workflow-steps/install-node/main.js @@ -0,0 +1,94 @@ +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; + + // 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(); + + 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', + )}`, + ); + } + + function getVoltaNodeVersion() { + try { + if (existsSync('package.json')) { + const packageJsonContents = + JSON.parse(readFileSync('package.json')) ?? {}; + + return packageJsonContents['volta']?.['node']; + } + } catch (e) { + return null; + } + } + + function isUsingNvm() { + try { + return existsSync('.nvmrc'); + } catch (e) { + return false; + } + } + + function runNvmInstall(version) { + try { + // enable nvm and then run the install command + // nvm command isn't available since nx agents don't run the bash profile + const installNodeWithNvm = `. $NVM_DIR/nvm.sh && nvm install ${ + 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`; + + execSync( + [ + installNodeWithNvm, + reenableCorePack, + reinstallPackageManagers, + printVersions, + saveEnvVars, + ].join(' && '), + { + stdio: 'inherit', + }, + ); + } catch (e) { + console.error(e); + throw new Error( + `Failed to install node version using nvm ${version || ''}`, + ); + } + } +} diff --git a/workflow-steps/install-node/main.yaml b/workflow-steps/install-node/main.yaml new file mode 100644 index 0000000..02110f1 --- /dev/null +++ b/workflow-steps/install-node/main.yaml @@ -0,0 +1,9 @@ +name: Install Node +description: Install a specific version of Node.js via nvm +inputs: + - name: node_version + description: 'The node version to be installed' + +definition: + using: 'node' + main: workflow-steps/install-node/main.js