ci: start Nexus in CI to serve patched npm packages

- Add .github/actions/setup-npm composite action:
  Starts Nexus, waits for health, inits npm repos, publishes patches, npm install
  Caches /tmp/nexus-data between runs for fast restarts
- Add deployment/nexus-ci-init.sh: lightweight Nexus init for CI (npm repos only)
- Replace all 9 manual Setup Node + npm install steps in gated-pipeline.yml
  with the composite action
- Re-enable @esbuild-kit:registry in .npmrc (Nexus now always available)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-10 00:06:09 +00:00
parent cb9b3901bd
commit f1ecb3f089
4 changed files with 179 additions and 57 deletions

62
.github/actions/setup-npm/action.yml vendored Normal file
View File

@@ -0,0 +1,62 @@
name: 'Setup npm with Nexus'
description: 'Starts Nexus, publishes patched packages, then runs npm install'
inputs:
node-version:
description: 'Node.js version'
required: false
default: '20'
runs:
using: composite
steps:
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
- name: Cache Nexus data
uses: actions/cache@v4
with:
path: /tmp/nexus-data
key: nexus-data-v1-${{ hashFiles('deployment/npm-patches/**', 'deployment/nexus-init.sh') }}
restore-keys: |
nexus-data-v1-
- name: Start Nexus
shell: bash
run: |
docker run -d --name nexus \
-p 8091:8081 \
-v /tmp/nexus-data:/nexus-data \
--platform linux/amd64 \
sonatype/nexus3:3.75.0
echo "Nexus starting..."
- name: Wait for Nexus
shell: bash
run: |
echo "Waiting for Nexus to be healthy (up to 3 minutes)..."
timeout 180 bash -c '
until curl -sf http://localhost:8091/service/rest/v1/status -u admin:nexus >/dev/null 2>&1 || \
curl -sf http://localhost:8091/service/rest/v1/status >/dev/null 2>&1; do
echo " still waiting..."
sleep 10
done
'
echo "Nexus is up"
- name: Initialise Nexus (npm repos)
shell: bash
env:
NEXUS_URL: http://localhost:8091
NEXUS_ADMIN_NEW_PASS: nexus
run: bash deployment/nexus-ci-init.sh
- name: Publish patched npm packages
shell: bash
run: bash deployment/publish-npm-patches.sh
- name: Install npm dependencies
shell: bash
run: npm install

View File

@@ -398,13 +398,10 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
- name: Setup npm with Nexus
uses: ./.github/actions/setup-npm
with:
node-version: 20
- name: Install dependencies
run: npm install
node-version: '20'
- name: Build workspace packages
run: npm run build --workspaces --if-present 2>&1 || true
@@ -435,13 +432,10 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
- name: Setup npm with Nexus
uses: ./.github/actions/setup-npm
with:
node-version: 20
- name: Install dependencies
run: npm install
node-version: '20'
- name: Build workspace packages
run: npm run build --workspaces --if-present 2>&1 || true
@@ -483,13 +477,10 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
- name: Setup npm with Nexus
uses: ./.github/actions/setup-npm
with:
node-version: 20
- name: Install dependencies
run: npm install
node-version: '20'
- name: Run dependency audit
run: |
@@ -696,13 +687,10 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
- name: Setup npm with Nexus
uses: ./.github/actions/setup-npm
with:
node-version: 20
- name: Install dependencies
run: npm install
node-version: '20'
- name: Run unit tests with coverage
run: |
@@ -769,13 +757,10 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
- name: Setup npm with Nexus
uses: ./.github/actions/setup-npm
with:
node-version: 20
- name: Install dependencies
run: npm install
node-version: '20'
- name: Install Playwright Browsers
run: npx playwright install --with-deps chromium
@@ -819,13 +804,10 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
- name: Setup npm with Nexus
uses: ./.github/actions/setup-npm
with:
node-version: 20
- name: Install dependencies
run: npm install
node-version: '20'
- name: Install Playwright Browsers
run: npx playwright install --with-deps chromium
@@ -932,13 +914,10 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
- name: Setup npm with Nexus
uses: ./.github/actions/setup-npm
with:
node-version: 20
- name: Install dependencies
run: npm install
node-version: '20'
- name: Build workspace packages
run: npm run build --workspaces --if-present 2>&1 || true
@@ -1226,13 +1205,10 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
- name: Setup npm with Nexus
uses: ./.github/actions/setup-npm
with:
node-version: 20
- name: Install dependencies
run: npm install
node-version: '20'
- name: Build for staging
run: npm run build -w frontends/nextjs
@@ -1279,13 +1255,10 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
- name: Setup npm with Nexus
uses: ./.github/actions/setup-npm
with:
node-version: 20
- name: Install dependencies
run: npm install
node-version: '20'
- name: Build for production
run: npm run build -w frontends/nextjs

5
.npmrc
View File

@@ -42,9 +42,8 @@ workspaces-update=true
# These are documented in package.json engines field
# Current: Node 22.22.1, npm 11.11.0
# SCOPED NEXUS REGISTRY - @esbuild-kit patched packages
# Start Nexus: cd deployment && docker compose -f docker-compose.nexus.yml up -d
# Publish patches: cd deployment && ./publish-npm-patches.sh
# Local dev: cd deployment && docker compose -f docker-compose.nexus.yml up -d
# CI: started automatically via .github/actions/setup-npm composite action
@esbuild-kit:registry=http://localhost:8091/repository/npm-group/
//localhost:8091/repository/npm-group/:_auth=YWRtaW46bmV4dXM=

88
deployment/nexus-ci-init.sh Executable file
View File

@@ -0,0 +1,88 @@
#!/bin/sh
# Lightweight Nexus initialisation for CI — npm repos only (no Docker, no Artifactory).
# Full local dev setup uses nexus-init.sh via docker compose.
set -e
NEXUS_URL="${NEXUS_URL:-http://localhost:8091}"
NEW_PASS="${NEXUS_ADMIN_NEW_PASS:-nexus}"
PASS_FILE="/tmp/nexus-data/admin.password"
log() { echo "[nexus-ci-init] $*"; }
# ── Resolve admin password ──────────────────────────────────────────────────
HTTP=$(curl -s -o /dev/null -w "%{http_code}" \
"$NEXUS_URL/service/rest/v1/status" -u "admin:$NEW_PASS")
if [ "$HTTP" = "200" ]; then
log "Already initialised with password '$NEW_PASS'"
elif [ -f "$PASS_FILE" ]; then
INIT_PASS=$(cat "$PASS_FILE")
log "First run: setting admin password..."
curl -sf -X PUT \
"$NEXUS_URL/service/rest/v1/security/users/admin/change-password" \
-u "admin:$INIT_PASS" -H "Content-Type: text/plain" -d "$NEW_PASS"
log "Admin password set"
else
log "ERROR: cannot authenticate and no password file found"
exit 1
fi
AUTH="admin:$NEW_PASS"
# ── Enable anonymous access ────────────────────────────────────────────────
curl -sf -X PUT "$NEXUS_URL/service/rest/v1/security/anonymous" \
-u "$AUTH" -H "Content-Type: application/json" \
-d '{"enabled":true,"userId":"anonymous","realmName":"NexusAuthorizingRealm"}' || true
log "Anonymous access enabled"
# Enable npm token realm
curl -sf -X PUT "$NEXUS_URL/service/rest/v1/security/realms/active" \
-u "$AUTH" -H "Content-Type: application/json" \
-d '["NexusAuthenticatingRealm","NpmToken"]' || true
# ── npm-hosted (patched packages) ─────────────────────────────────────────
HTTP=$(curl -s -o /dev/null -w "%{http_code}" -X POST \
"$NEXUS_URL/service/rest/v1/repositories/npm/hosted" \
-u "$AUTH" -H "Content-Type: application/json" -d '{
"name": "npm-hosted",
"online": true,
"storage": {"blobStoreName": "default", "strictContentTypeValidation": true, "writePolicy": "allow"}
}')
case "$HTTP" in
201) log "npm-hosted repo created" ;;
400) log "npm-hosted repo already exists" ;;
*) log "ERROR creating npm-hosted: HTTP $HTTP"; exit 1 ;;
esac
# ── npm-proxy (npmjs.org cache) ───────────────────────────────────────────
HTTP=$(curl -s -o /dev/null -w "%{http_code}" -X POST \
"$NEXUS_URL/service/rest/v1/repositories/npm/proxy" \
-u "$AUTH" -H "Content-Type: application/json" -d '{
"name": "npm-proxy",
"online": true,
"storage": {"blobStoreName": "default", "strictContentTypeValidation": true},
"proxy": {"remoteUrl": "https://registry.npmjs.org", "contentMaxAge": 1440, "metadataMaxAge": 1440},
"httpClient": {"blocked": false, "autoBlock": true},
"negativeCache": {"enabled": true, "timeToLive": 1440}
}')
case "$HTTP" in
201) log "npm-proxy repo created" ;;
400) log "npm-proxy repo already exists" ;;
*) log "ERROR creating npm-proxy: HTTP $HTTP"; exit 1 ;;
esac
# ── npm-group (combines hosted + proxy) ──────────────────────────────────
HTTP=$(curl -s -o /dev/null -w "%{http_code}" -X POST \
"$NEXUS_URL/service/rest/v1/repositories/npm/group" \
-u "$AUTH" -H "Content-Type: application/json" -d '{
"name": "npm-group",
"online": true,
"storage": {"blobStoreName": "default", "strictContentTypeValidation": true},
"group": {"memberNames": ["npm-hosted", "npm-proxy"]}
}')
case "$HTTP" in
201) log "npm-group repo created" ;;
400) log "npm-group repo already exists" ;;
*) log "ERROR creating npm-group: HTTP $HTTP"; exit 1 ;;
esac
log "Nexus CI init complete"