Changing node versions in CIs without root access

Cover Image for Changing node versions in CIs without root access
Felix Christl
Felix Christl

When you build NodeJS applications and want to use a continuous integration pipeline, you luckily mostly have the change to pick an explicit Node version to use. We at HipSquare mostly use GitLab including its great CI, where jobs run on a Docker executor and you can choose a Node version by simply selecting which image to use for a job:

build app:
  stage: build
  image: node:16
    - yarn --frozen-lockfile
    - yarn build

AppCenter is... different

However, sometimes, we build web apps that are supposed to run in a native container on mobile devices. The most common framework to achieve that is the great Capacitor.

Building mobile applications that are ready for shipment in app stores is something that is not that easily done in GitLab CI (or other standard CI systems). It is possible, but cumbersome. AppCenter is a really neat solution. In its simplest configuration, it takes a Git repository that contains an Android Studio project for Android apps or an Xcode project for iOS apps and automatically builds, signs and publishes the apps.

The process works very nicely for native applications. However, for Capacitor apps, there are some extra steps to take:

  • Install dependencies from package.json,
  • perform a production build (e.g. yarn build --production),
  • synchronize the Capacitor project with the Android Studio or Xcode projects (i.e. cap sync).

For many use cases, it will be practical or even required to set an explicit NodeJS version. However, AppCenter only provides this capability for React Native apps, not for "standard" iOS/Android apps, which is what Capacitor-based apps ultimately are.

We have been struggling with this on multiple projects and have not come to resort to a simple solution that hopefully helps someone else out there as well, whether on AppCenter or another CI solution that does not permit explicitly setting NodeJS versions: We use n inside the CI, with a little workaround to allow execution without superuser privileges:

# Install `n` node versions here. We need a non-default folder as we don't have root access
export N_PREFIX=/tmp/n

# Put the `n` path at the beginning of the `PATH` list to make sure we use the `n`-installed Node binary
export PATH=/tmp/n/bin:$PATH

# Install `n` itself
yarn global add n

# Set an explicit Node version
n 16

# Continue with the rest of your script normally
yarn install --frozen-lockfile
yarn build