Skip to content

Repository files navigation

panascais/ci-node

GitHub Workflow Status Docker Pulls Docker Stars Docker Image Size License

CI-Node is a docker image intended to be used in continuous integration services such as GitLab CI, GitHub Actions, Semaphore CI and Circle CI

Tag: Command: Node Version: Supported: Labels:
lts docker pull panascais/ci-node:lts v24.x.x ✓ Docker Image Size
latest docker pull panascais/ci-node v26.x.x ✓ Docker Image Size
26 docker pull panascais/ci-node:26 v26.x.x ✓ Docker Image Size
25 docker pull panascais/ci-node:25 v25.x.x ✓ Docker Image Size
24 docker pull panascais/ci-node:24 v24.x.x ✓ Docker Image Size
23 docker pull panascais/ci-node:23 v23.x.x ✓ Docker Image Size
22 docker pull panascais/ci-node:22 v22.x.x ✓ Docker Image Size
21 docker pull panascais/ci-node:21 v21.x.x ✓ Docker Image Size
20 docker pull panascais/ci-node:20 v20.x.x ✓ Docker Image Size
19 docker pull panascais/ci-node:19 v19.x.x × Docker Image Size
18 docker pull panascais/ci-node:18 v18.x.x × Docker Image Size
17 docker pull panascais/ci-node:17 v17.x.x × Docker Image Size
16 docker pull panascais/ci-node:16 v16.x.x × Docker Image Size
15 docker pull panascais/ci-node:15 v15.x.x × Docker Image Size
14 docker pull panascais/ci-node:14 v14.x.x × Docker Image Size
12 docker pull panascais/ci-node:12 v12.x.x × Docker Image Size
10 docker pull panascais/ci-node:10 v10.x.x × Docker Image Size
8 docker pull panascais/ci-node:8 v8.x.x × Docker Image Size

Included CLIs

Name: GitHub: npm:
fly github.com/superfly/flyctl ×
npm github.com/npm/cli npmjs.com/package/npm
pnpm github.com/pnpm/pnpm npmjs.com/package/pnpm
yarn github.com/yarnpkg/yarn npmjs.com/package/yarn
typescript github.com/microsoft/typescript npmjs.com/package/typescript
biome github.com/biomejs/biome npmjs.com/package/@biomejs/biome
eslint github.com/eslint/eslint npmjs.com/package/eslint
stylelint github.com/stylelint/stylelint npmjs.com/package/stylelint
prettier github.com/prettier/prettier npmjs.com/package/prettier
ava github.com/avajs/ava npmjs.com/package/ava
babel github.com/babel/babel npmjs.com/package/@babel/cli
esbuild github.com/evanw/esbuild npmjs.com/package/esbuild
swc github.com/swc-project/swc npmjs.com/package/swc
rspack github.com/web-infra-dev/rspack npmjs.com/package/rspack
turbo github.com/vercel/turbo npmjs.com/package/turbo
vite github.com/vitejs/vite npmjs.com/package/vite
wrangler github.com/cloudflare/wrangler2 npmjs.com/package/wrangler
ts-node github.com/typestrong/ts-node npmjs.com/package/ts-node
tsup github.com/egoist/tsup npmjs.com/package/tsup
zx github.com/google/zx npmjs.com/package/zx
pkg github.com/yao-pkg/pkg npmjs.com/package/pkg
rollup github.com/rollup/rollup npmjs.com/package/rollup
knip github.com/webpro-nl/knip npmjs.com/package/knip
tsx github.com/privatenumber/tsx npmjs.com/package/tsx

Included Packages

Name: Registry:
bash pkgs.alpinelinux.org/package/bash
g++ pkgs.alpinelinux.org/package/g++
gcc pkgs.alpinelinux.org/package/gcc
git pkgs.alpinelinux.org/package/git
git-lfs pkgs.alpinelinux.org/package/git-lfs
openssl pkgs.alpinelinux.org/package/openssl
ffmpeg pkgs.alpinelinux.org/package/ffmpeg
gzip pkgs.alpinelinux.org/package/gzip
make pkgs.alpinelinux.org/package/make
python3 pkgs.alpinelinux.org/package/python3
jo pkgs.alpinelinux.org/package/jo
jq pkgs.alpinelinux.org/package/jq
autoconf pkgs.alpinelinux.org/package/autoconf
automake pkgs.alpinelinux.org/package/automake
curl-dev pkgs.alpinelinux.org/package/curl-dev
curl pkgs.alpinelinux.org/package/curl
vips pkgs.alpinelinux.org/package/vips
libjpeg-turbo-dev pkgs.alpinelinux.org/package/libjpeg-turbo-dev

Build

Example for bash and node version 24:

docker build \
    --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \
    --build-arg PNPM_VERSION=12 \
    --build-arg VCS_REF=`git rev-parse --short HEAD` \
    -t panascais/ci-node:24 \
    ./24

Example for fish and node version 24:

docker build \
    --build-arg BUILD_DATE=(date -u +"%Y-%m-%dT%H:%M:%SZ") \
    --build-arg PNPM_VERSION=12 \
    --build-arg VCS_REF=(git rev-parse --short HEAD) \
    -t panascais/ci-node:24 \
    ./24

pnpm and Docker cache mounts

BuildKit cache mounts speed up rebuilds by reusing downloaded packages between builds. Both apk and pnpm use them in this image.

apk

The large apk install layer mounts /apk/cache so .apk files are reused across builds. Cache IDs include ${TARGETARCH} so amd64 and arm64 packages do not mix during multi-platform builds. sharing=locked avoids races when matrix jobs build in parallel.

RUN --mount=type=cache,id=ci-node-apk-${TARGETARCH},sharing=locked,target=/apk/cache \
    apk update --cache-dir /apk/cache \
    && apk add --cache-dir /apk/cache --cache-predownload \
    ...

Alpine 3.24 ships apk-tools 3.x, where --update / -U means --cache-max-age 0 (always refetch). Do not use it with cache mounts. Pass 1 writes into /apk/cache; when the apk layer re-runs, --cache-predownload reuses cached .apk files. The mount never lands in the final image.

pnpm

Node patch tags and the pnpm major come from the base image repository: configuration/versions.json and configuration/pnpm.json. build.ts fetches both and passes --build-arg PNPM_VERSION=<upstream major>. ci-node does not keep a local pnpm config copy, and it does not reinstall pnpm@N in the global package list — every supported base already ships that executable.

pnpm 10+ (Node 18+): global binaries live under $PNPM_HOME/bin. Every Node 18+ image inherits PNPM_HOME=/root/.local/share/pnpm, /root/.local/share/pnpm/bin on PATH, and enableGlobalVirtualStore: false (/root/.config/pnpm/config.yaml) from the authoritative base image. pnpm 10 ignores globalBinDir in that yaml file and falls back to PNPM_HOME, which is not on PATH (pnpm#11205). pnpm config set global-bin-dir /root/.local/share/pnpm/bin writes the rc file those versions read, then install with pnpm add -g. Cache-mount the store at /root/.local/share/pnpm/store. Without enableGlobalVirtualStore: false, a store cache mount can break global CLIs at runtime (ae38d12).

pnpm 8 and below (Node 17 and older): single-pass pnpm i -g with the store cache-mounted at pnpm’s default path for that image (no --store-dir override). Measure with the same ENV as the Dockerfile (pnpm store path).

Cache IDs are ci-node-pnpm-${PNPM_VERSION}-${TARGETARCH} so the pnpm major is not checked in.

pnpm Node PNPM_HOME in ci-node pnpm store path Cache mount target= Cache id
6 12 (none; bins in /usr/local/bin) /root/.pnpm-store/v3 /root/.pnpm-store ci-node-pnpm-${PNPM_VERSION}-${TARGETARCH}
7–8 14–17 .../pnpm/bin .../pnpm/bin/store/v3 /root/.local/share/pnpm/bin/store ci-node-pnpm-${PNPM_VERSION}-${TARGETARCH}
10+ 18+ /root/.local/share/pnpm .../pnpm/store/v10 /root/.local/share/pnpm/store ci-node-pnpm-${PNPM_VERSION}-${TARGETARCH}
# Node 12 (pnpm 6; store at ~/.pnpm-store, global bins in /usr/local/bin)
RUN --mount=type=cache,id=ci-node-pnpm-${PNPM_VERSION}-${TARGETARCH},sharing=locked,target=/root/.pnpm-store \
    packages=" ... " \
    && pnpm i -g $packages

# Node 14–17 (pnpm 7–8)
RUN --mount=type=cache,id=ci-node-pnpm-${PNPM_VERSION}-${TARGETARCH},sharing=locked,target=/root/.local/share/pnpm/bin/store \
    packages=" ... " \
    && mkdir -p /root/.local/share/pnpm/bin \
    && pnpm i -g $packages

# Node 18+ (pnpm 10+; inherit PNPM_HOME/bin PATH and enableGlobalVirtualStore: false from the base node image)
RUN --mount=type=cache,id=ci-node-pnpm-${PNPM_VERSION}-${TARGETARCH},sharing=locked,target=/root/.local/share/pnpm/store \
    packages=" ... " \
    buildable=" --allow-build=... " \
    && mkdir -p /root/.local/share/pnpm/bin \
    && pnpm config set global-bin-dir /root/.local/share/pnpm/bin \
    && pnpm add -g $packages $buildable

Mount the parent directory (pnpm creates v3 / v10 subdirs inside). ${TARGETARCH} and sharing=locked avoid cross-arch mixing and parallel-build store corruption. Layout commands stay explicit per known pnpm major; a future major is not inferred from the number alone.

When building application images on top of ci-node, use a separate project store (pnpm Docker docs):

ENV PNPM_HOME=/pnpm
ENV PATH=${PNPM_HOME}:${PATH}

RUN --mount=type=cache,id=pnpm-${TARGETARCH},sharing=locked,target=/pnpm/store \
    pnpm install --frozen-lockfile

pnpm itself comes from /usr/local/bin/pnpm on the base node image.

Contributors

Contributing

Interested in contributing to CI-Node? Contributions are welcome, and are accepted via pull requests. Please review these guidelines before submitting any pull requests.

License

Code licensed under MIT, documentation under CC BY 3.0.

About

Panascais Node Continuous Integration Image

Topics

Resources

Contributing

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages