From fc4df037bba551801b89a7fa5eb51bdd6790a594 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Thu, 1 Aug 2024 05:09:08 +0700 Subject: [PATCH] chore(common): use `nvm` to select version of node for builds Used by default only on build agents. See docs/build/node.md and resources/build/_builder_nvm.sh for documentation of this change. Fixes: #11376 --- resources/build/_builder_nvm.sh | 51 +++++++++++++++++++++++++++++++ resources/build/jq.inc.sh | 44 +++++++++++++------------- resources/shellHelperFunctions.sh | 45 ++++++++++++++++++++++++++- 3 files changed, 118 insertions(+), 22 deletions(-) create mode 100755 resources/build/_builder_nvm.sh diff --git a/resources/build/_builder_nvm.sh b/resources/build/_builder_nvm.sh new file mode 100755 index 0000000000..cccbb6b055 --- /dev/null +++ b/resources/build/_builder_nvm.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash + +# See also /docs/build/node.md + +set -e +set -u + +if [[ $# -lt 1 ]]; then + echo "This script is called by shellHelperFunctions.sh, _select_node_version_with_nvm()" + echo "during build.sh configure steps. It is not intended to be called directly, as it" + echo "is a wrapper for nvm." + exit 99 +fi + +REQUIRED_NODE_VERSION="$1" + +if [[ -z "${NVM_DIR+x}" ]]; then + export NVM_DIR="$HOME/.nvm" +fi + +# +# nvm on macos and linux is a shell function. `source`ing it into our scripts is +# fragile because it uses some of the same variable names that we do. We have +# marked our most precious variables as `readonly`, which breaks nvm re-use of +# them also! Safest way to work around this is to load nvm in a child process +# and run it there. Downside is then of course we don't get the `PATH` changes +# that nvm does for us, so we have to do it ourselves back in the caller, +# `_select_node_version_with_nvm()`. +# +# Note: the readonly attribute for variables is not inherited by child +# processes. +# +# See also https://github.com/nvm-sh/nvm/issues/3257 (among others) +# + +type -t nvm >/dev/null || { + source "$NVM_DIR/nvm.sh" + type -t nvm >/dev/null || { + echo "Failed to find nvm" + exit 1 + } +} + +nvm install "$REQUIRED_NODE_VERSION" +nvm use "$REQUIRED_NODE_VERSION" + +# Beware the hardcoded path below -- it should already be in the system PATH + +mkdir -p "$HOME/.keyman" +rm -f "$HOME/.keyman/node" +ln -s "$NVM_BIN" "$HOME/.keyman/node" diff --git a/resources/build/jq.inc.sh b/resources/build/jq.inc.sh index cf68aaf4b2..d1d7c32c23 100644 --- a/resources/build/jq.inc.sh +++ b/resources/build/jq.inc.sh @@ -6,27 +6,29 @@ # Linux/macOS: jq # -## START STANDARD BUILD SCRIPT INCLUDE -# adjust relative paths as necessary -JQ_THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -# . "${THIS_SCRIPT%/*}/build-utils.sh" -## END STANDARD BUILD SCRIPT INCLUDE +if [[ -z "${JQ+x}" ]]; then + ## START STANDARD BUILD SCRIPT INCLUDE + # adjust relative paths as necessary + JQ_THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" + # . "${THIS_SCRIPT%/*}/build-utils.sh" + ## END STANDARD BUILD SCRIPT INCLUDE -case "${OSTYPE}" in - "cygwin") - JQ=$(dirname "$JQ_THIS_SCRIPT")/jq-win64.exe - ;; - "msys") - JQ=$(dirname "$JQ_THIS_SCRIPT")/jq-win64.exe - ;; - *) - JQ=jq - ;; -esac + case "${OSTYPE}" in + "cygwin") + JQ=$(dirname "$JQ_THIS_SCRIPT")/jq-win64.exe + ;; + "msys") + JQ=$(dirname "$JQ_THIS_SCRIPT")/jq-win64.exe + ;; + *) + JQ=jq + ;; + esac -readonly JQ + readonly JQ -# JQ with inplace file replacement -function jqi() { - cat <<< "$($JQ -c "$1" < "$2")" > "$2" -} + # JQ with inplace file replacement + function jqi() { + cat <<< "$($JQ -c "$1" < "$2")" > "$2" + } +fi \ No newline at end of file diff --git a/resources/shellHelperFunctions.sh b/resources/shellHelperFunctions.sh index e93b2ed50c..7ded208d44 100755 --- a/resources/shellHelperFunctions.sh +++ b/resources/shellHelperFunctions.sh @@ -1,5 +1,16 @@ #!/usr/bin/env bash +# We only import JQ if not already defined; jq is used by +# _select_node_version_with_nvm() + +if [[ -z "${JQ+x}" ]]; then + if [[ "$BUILDER_OS" == win ]]; then + . "$KEYMAN_ROOT/resources/build/jq.inc.sh" + else + JQ=jq + fi +fi + # Allows for a quick macOS check for those scripts requiring a macOS environment. verify_on_mac() { if [[ "${OSTYPE}" != "darwin"* ]]; then @@ -243,6 +254,13 @@ verify_npm_setup() { fi builder_set_module_has_been_built /external/npm-ci + # If we are on CI environment, automatically select a node version with nvm + # Also, a developer can set KEYMAN_USE_NVM variable to get this behaviour + # automatically too (see /docs/build/node.md) + if [[ "$VERSION_ENVIRONMENT" != local || ! -z "${KEYMAN_USE_NVM+x}" ]]; then + _select_node_version_with_nvm + fi + # Check if Node.JS/npm is installed. type npm >/dev/null ||\ builder_die "Build environment setup error detected! Please ensure Node.js is installed!" @@ -252,4 +270,29 @@ verify_npm_setup() { try_multiple_times npm ci popd > /dev/null -} \ No newline at end of file +} + +# Use nvm to select a node version according to package.json +# see /docs/build/node.md +_select_node_version_with_nvm() { + local REQUIRED_NODE_VERSION="$("$JQ" -r '.engines.node' "$KEYMAN_ROOT/package.json")" + + if [[ $BUILDER_OS != win ]]; then + # launch nvm in a sub process, see _builder_nvm.sh for details + "$KEYMAN_ROOT/resources/build/_builder_nvm.sh" "$REQUIRED_NODE_VERSION" + else + nvm install "$REQUIRED_NODE_VERSION" + nvm use "$REQUIRED_NODE_VERSION" + fi + + # Now, check that the node version is correct, on all systems + + # Note: On windows, `nvm use` and `nvm install` always return success. + # https://github.com/coreybutler/nvm-windows/issues/738 + + # note the 'v' prefix that node emits (and npm doesn't!) + local CURRENT_NODE_VERSION="$(node --version)" + if [[ "$CURRENT_NODE_VERSION" != "v$REQUIRED_NODE_VERSION" ]]; then + builder_die "Attempted to select node.js version $REQUIRED_NODE_VERSION but found $CURRENT_NODE_VERSION instead" + fi +}