Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions .github/actions/build-shared/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
name: Build Node.js (shared libraries)
description: >
Downloads the slim tarball built by the `build-tarball` job, extracts it,
installs Nix (+ cachix + sccache), then builds Node.js and runs the CI
test suite inside the pinned nix-shell.

inputs:
system:
description: System label (e.g. x86_64-linux, aarch64-darwin).
required: true
extra-nix-args:
description: Additional arguments appended to the nix-shell invocation.
required: false
default: ''
cachix-auth-token:
description: Cachix auth token for nodejs.cachix.org.
required: false
default: ''

runs:
using: composite
steps:
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
if: ${{ github.event_name != 'workflow_dispatch' }}
with:
name: tarballs
path: tarballs

- name: Extract tarball
if: ${{ github.event_name != 'workflow_dispatch' }}
shell: bash
run: |
tar xzf tarballs/*.tar.gz -C "$RUNNER_TEMP"
echo "TAR_DIR=$RUNNER_TEMP/$(basename tarballs/*.tar.gz .tar.gz)" >> "$GITHUB_ENV"

- uses: cachix/install-nix-action@96951a368ba55167b55f1c916f7d416bac6505fe # v31.10.3
with:
extra_nix_config: sandbox = true

- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: nodejs
authToken: ${{ inputs.cachix-auth-token }}

- name: Configure sccache
if: github.base_ref == 'main' || github.ref_name == 'main'
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
core.exportVariable('SCCACHE_GHA_ENABLED', 'on');
core.exportVariable('ACTIONS_CACHE_SERVICE_V2', 'on');
core.exportVariable('ACTIONS_RESULTS_URL', process.env.ACTIONS_RESULTS_URL || '');
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
core.exportVariable('NIX_SCCACHE', '(import <nixpkgs> {}).sccache');

- name: Build Node.js and run tests
shell: bash
run: |
nix-shell \
-I "nixpkgs=$TAR_DIR/tools/nix/pkgs.nix" \
--pure --keep TAR_DIR --keep FLAKY_TESTS \
--keep SCCACHE_GHA_ENABLED --keep ACTIONS_CACHE_SERVICE_V2 --keep ACTIONS_RESULTS_URL --keep ACTIONS_RUNTIME_TOKEN \
--arg loadJSBuiltinsDynamically false \
--arg useSeparateDerivationForV8 true \
--arg ccache "${NIX_SCCACHE:-null}" \
--arg devTools '[]' \
--arg benchmarkTools '[]' \
${{ endsWith(inputs.system, '-darwin') && '--arg withAmaro false --arg withLief false --arg withSQLite false --arg withFFI false --arg extraConfigFlags ''["--without-inspector" "--without-node-options"]'' \' || '\' }}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would need to be moved to inputs.extra-nix-args

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I figured only extras (not shared between all uses of the composite action) would go through those inputs.

${{ inputs.extra-nix-args }} \
--run '
make -C "$TAR_DIR" run-ci -j4 V=1 TEST_CI_ARGS="-p actions --measure-flakiness 9 --skip-tests=$CI_SKIP_TESTS"
' "$TAR_DIR/shell.nix"
97 changes: 57 additions & 40 deletions .github/workflows/test-shared.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ on:
- vcbuild.bat
- .**
- '!.github/workflows/test-shared.yml'
- '!.github/actions/build-shared/**'
types: [opened, synchronize, reopened, ready_for_review]
push:
branches:
Expand Down Expand Up @@ -97,13 +98,17 @@ on:
- vcbuild.bat
- .**
- '!.github/workflows/test-shared.yml'
- '!.github/actions/build-shared/**'

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

env:
FLAKY_TESTS: keep_retrying
# Latest OpenSSL major.minor cycle we support running tests with.
# The nixpkgs updater regenerates the OpenSSL matrix using this value.
SUPPORTED_OPENSSL_VERSION: '4.0'

permissions:
contents: read
Expand Down Expand Up @@ -153,50 +158,62 @@ jobs:
name: '${{ matrix.system }}: with shared libraries'
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
if: ${{ github.event_name != 'workflow_dispatch' }}
with:
name: tarballs
path: tarballs

- name: Extract tarball
persist-credentials: false
sparse-checkout: .github/actions
- uses: ./.github/actions/build-shared
if: ${{ github.event_name != 'workflow_dispatch' }}
run: |
tar xzf tarballs/*.tar.gz -C "$RUNNER_TEMP"
echo "TAR_DIR=$RUNNER_TEMP/$(basename tarballs/*.tar.gz .tar.gz)" >> "$GITHUB_ENV"

- uses: cachix/install-nix-action@96951a368ba55167b55f1c916f7d416bac6505fe # v31.10.3
with:
extra_nix_config: sandbox = true
system: ${{ matrix.system }}
cachix-auth-token: ${{ secrets.CACHIX_AUTH_TOKEN }}

- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
# Builds and tests Node.js with shared libraries against every supported
# OpenSSL release version available in the repo-pinned nixpkgs. The default
# shared `openssl` from tools/nix/sharedLibDeps.nix is overridden per matrix
# entry, while all other shared libs remain at their defaults. Only runs on
# a single runner/system (aarch64-linux) to keep the matrix to a minimum.
build-openssl:
needs:
- build-tarball
strategy:
fail-fast: false
matrix:
openssl:
# BEGIN_OPENSSL_MATRIX (autogenerated by tools/dep_updaters/update-nixpkgs-pin.sh)
- version: 4.0.0
attr: openssl_4_0
continue-on-error: false
- version: 3.6.1
attr: openssl_3_6
continue-on-error: false
- version: 3.0.19
attr: openssl_3
continue-on-error: false
- version: 1.1.1w
attr: openssl_1_1
continue-on-error: false
# END_OPENSSL_MATRIX
name: 'aarch64-linux: with shared ${{ matrix.openssl.attr }} (${{ matrix.openssl.version }})'
runs-on: ubuntu-24.04-arm
continue-on-error: ${{ matrix.openssl['continue-on-error'] }}
env:
OPENSSL_ATTR: ${{ matrix.openssl.attr }}
OPENSSL_VERSION: ${{ matrix.openssl.version }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
name: nodejs
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}

- name: Configure sccache
if: github.base_ref == 'main' || github.ref_name == 'main'
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
persist-credentials: false
sparse-checkout: .github/actions
- uses: ./.github/actions/build-shared
with:
script: |
core.exportVariable('SCCACHE_GHA_ENABLED', 'on');
core.exportVariable('ACTIONS_CACHE_SERVICE_V2', 'on');
core.exportVariable('ACTIONS_RESULTS_URL', process.env.ACTIONS_RESULTS_URL || '');
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
core.exportVariable('NIX_SCCACHE', '(import <nixpkgs> {}).sccache');

- name: Build Node.js and run tests
run: |
nix-shell \
-I "nixpkgs=$TAR_DIR/tools/nix/pkgs.nix" \
--pure --keep TAR_DIR --keep FLAKY_TESTS \
--keep SCCACHE_GHA_ENABLED --keep ACTIONS_CACHE_SERVICE_V2 --keep ACTIONS_RESULTS_URL --keep ACTIONS_RUNTIME_TOKEN \
--arg loadJSBuiltinsDynamically false \
--arg useSeparateDerivationForV8 true \
--arg ccache "${NIX_SCCACHE:-null}" \
--arg devTools '[]' \
--arg benchmarkTools '[]' \
${{ endsWith(matrix.system, '-darwin') && '--arg withAmaro false --arg withLief false --arg withSQLite false --arg withFFI false --arg extraConfigFlags ''["--without-inspector" "--without-node-options"]'' \' || '\' }}
--run '
make -C "$TAR_DIR" run-ci -j4 V=1 TEST_CI_ARGS="-p actions --measure-flakiness 9 --skip-tests=$CI_SKIP_TESTS"
' "$TAR_DIR/shell.nix"
system: aarch64-linux
cachix-auth-token: ${{ secrets.CACHIX_AUTH_TOKEN }}
# Override just the `openssl` attr of the default shared-lib set with
# the matrix-selected nixpkgs attribute (e.g. `openssl_3_6`). All
# other shared libs (brotli, cares, libuv, …) keep their defaults.
# `permittedInsecurePackages` whitelists just the matrix-selected
# release (e.g. `openssl-1.1.1w`) so EOL-with-extended-support
# cycles evaluate without relaxing nixpkgs' meta check globally.
extra-nix-args: --arg sharedLibDeps "(import $TAR_DIR/tools/nix/sharedLibDeps.nix {}) // { openssl = (import $TAR_DIR/tools/nix/pkgs.nix { config.permittedInsecurePackages = [ \"openssl-$OPENSSL_VERSION\" ]; }).$OPENSSL_ATTR; }"
57 changes: 56 additions & 1 deletion tools/dep_updaters/update-nixpkgs-pin.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ set -ex

BASE_DIR=$(cd "$(dirname "$0")/../.." && pwd)
NIXPKGS_PIN_FILE="$BASE_DIR/tools/nix/pkgs.nix"
TEST_SHARED_WORKFLOW_FILE="$BASE_DIR/.github/workflows/test-shared.yml"

NIXPKGS_REPO=$(grep 'repo =' "$NIXPKGS_PIN_FILE" | awk -F'"' '{ print $2 }')
CURRENT_VERSION_SHA1=$(grep 'rev =' "$NIXPKGS_PIN_FILE" | awk -F'"' '{ print $2 }')
Expand All @@ -25,12 +26,66 @@ TMP_FILE=$(mktemp)
sed "s/$CURRENT_VERSION_SHA1/$NEW_UPSTREAM_SHA1/;s/$CURRENT_TARBALL_HASH/$NEW_TARBALL_HASH/" "$NIXPKGS_PIN_FILE" > "$TMP_FILE"
mv "$TMP_FILE" "$NIXPKGS_PIN_FILE"

SUPPORTED_OPENSSL_VERSION=$(sed -nE "s/^[[:space:]]*SUPPORTED_OPENSSL_VERSION:[[:space:]]*'([^']+)'[[:space:]]*$/\1/p" "$TEST_SHARED_WORKFLOW_FILE" | head -n1)

if [ -z "$SUPPORTED_OPENSSL_VERSION" ]; then
echo "Could not resolve SUPPORTED_OPENSSL_VERSION from $TEST_SHARED_WORKFLOW_FILE" >&2
exit 1
fi

OPENSSL_MATRIX_BLOCK=$("$BASE_DIR/tools/nix/collect-openssl-matrix.sh" | jq -r --arg supported "$SUPPORTED_OPENSSL_VERSION" '
# Compare OpenSSL major.minor cycles as numeric tuples.
def cycle_tuple($v):
($v | capture("^(?<cycle>[0-9]+\\.[0-9]+)").cycle | split(".") | map(tonumber));
[
" # BEGIN_OPENSSL_MATRIX (autogenerated by tools/dep_updaters/update-nixpkgs-pin.sh)",
(
.[]
| " - version: \(.version)\n attr: \(.attr)\n continue-on-error: \(cycle_tuple(.version) > cycle_tuple($supported))"
),
" # END_OPENSSL_MATRIX"
]
| join("\n")
')

if ! grep -q "BEGIN_OPENSSL_MATRIX" "$TEST_SHARED_WORKFLOW_FILE"; then
echo "Could not find BEGIN_OPENSSL_MATRIX marker in $TEST_SHARED_WORKFLOW_FILE" >&2
exit 1
fi

if ! grep -q "END_OPENSSL_MATRIX" "$TEST_SHARED_WORKFLOW_FILE"; then
echo "Could not find END_OPENSSL_MATRIX marker in $TEST_SHARED_WORKFLOW_FILE" >&2
exit 1
fi

TMP_WORKFLOW_FILE=$(mktemp)
TMP_BLOCK_FILE=$(mktemp)
printf '%s\n' "$OPENSSL_MATRIX_BLOCK" > "$TMP_BLOCK_FILE"

awk -v block_file="$TMP_BLOCK_FILE" '
/BEGIN_OPENSSL_MATRIX/ {
while ((getline line < block_file) > 0) {
print line;
}
close(block_file);
in_block = 1;
next;
}
/END_OPENSSL_MATRIX/ {
in_block = 0;
next;
}
!in_block { print }
' "$TEST_SHARED_WORKFLOW_FILE" > "$TMP_WORKFLOW_FILE"
mv "$TMP_WORKFLOW_FILE" "$TEST_SHARED_WORKFLOW_FILE"
rm -f "$TMP_BLOCK_FILE"

cat -<<EOF
All done!

Please git add and commit the new version:

$ git add $NIXPKGS_PIN_FILE
$ git add $NIXPKGS_PIN_FILE $TEST_SHARED_WORKFLOW_FILE
$ git commit -m 'tools: bump nixpkgs-unstable pin to $NEW_VERSION'
EOF

Expand Down
63 changes: 63 additions & 0 deletions tools/nix/collect-openssl-matrix.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/bin/sh
#
# Emits the JSON source data of OpenSSL releases to test Node.js against with
# shared libraries.
#
# This helper is used by tools/dep_updaters/update-nixpkgs-pin.sh to
# regenerate the autogenerated OpenSSL matrix block in
# .github/workflows/test-shared.yml.
#
# Output (stdout): a JSON array with shape
# [{ "version": "3.6.1", "attr": "openssl_3_6" }, ...]
#
# Usage: ./tools/nix/collect-openssl-matrix.sh

set -eu

here=$(cd -- "$(dirname -- "$0")" && pwd)

# 1. Enumerate every `openssl_N` / `openssl_N_M` attribute exposed by the
# repo-pinned nixpkgs. `tryEval` skips aliases that raise (e.g.
# `openssl_3_0` → renamed to `openssl_3`) so we only keep attributes
# that resolve to a real derivation with a `.version`.
nix_json=$(nix-instantiate --eval --strict --json -E "
let
pkgs = import $here/pkgs.nix {};
names = builtins.filter
(n: builtins.match \"openssl_[0-9]+(_[0-9]+)?\" n != null)
(builtins.attrNames pkgs);
safe = builtins.filter (n:
let t = builtins.tryEval pkgs.\${n}; in
t.success && (builtins.tryEval t.value.version).success) names;
in map (n: { attr = n; version = pkgs.\${n}.version; }) safe
")

# 2. Resolve the OpenSSL version the `build` job already covers (the default
# from sharedLibDeps.nix) so we can drop it from the matrix to avoid
# duplicate coverage.
default_openssl_version=$(nix-instantiate --eval --strict --json -E "
(import $here/sharedLibDeps.nix {}).openssl.version
" | jq -r .)

# 3. Fetch OpenSSL release versions from endoflife.date, keep entries that
# are either not past EOL or still under extended support, then pick the
# first nix attr whose `.version` starts with the release version
# followed by `.` / letter / end-of-string (so "3.6" matches "3.6.1",
# "1.1.1" matches "1.1.1w", and "1.1" does NOT swallow "1.1.1").
# Releases without a matching nix attr and the one covered by default in
# `build` are dropped.
curl -sf https://endoflife.date/api/openssl.json \
Comment on lines +42 to +49
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be better to have a manually curated list (by manually, I mean: the bot would open PRs to update the list every Sunday)? The logic is quite hard to grasp, and it's a lot of repetitive work that does not scale well if we wanted to add e.g. non-OpenSSL variants.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uff, there's an idea, nixpgs actually has BoringSSL too. I'd do this in a follow-up.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And it'd have to be the same job that does nixpkgs-unstable updates, else we'd run the risk of the two being open at the same time and not being in sync between their merges.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

| jq -c \
--argjson nix "$nix_json" \
--arg default_version "$default_openssl_version" '
(now | strftime("%Y-%m-%d")) as $today |
[ .[]
| select(.eol == false or .eol > $today or .extendedSupport == true)
| .cycle as $v
| ($nix
| map(select(.version | test("^" + ($v | gsub("\\."; "\\.")) + "([.a-z]|$)")))
| first) as $m
| select($m != null)
| select($m.version != $default_version)
| { version: $m.version, attr: $m.attr }
]'
Loading