Skip to content
Draft
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
122 changes: 122 additions & 0 deletions .github/actions/node-prepare/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
name: Prepare a Runner For Node.js Tests or Build
description: Setup node, configure registry auth, and install dependencies. Detects package manager from lockfile (pnpm or yarn).
author: Telicent

# Requires permissions -
# - contents: read
# - id-token: write

inputs:
node-version:
required: false
description: The node version to install. Falls back to .nvmrc if not provided.
frozen-lockfile:
required: false
description: Whether to use a frozen/immutable lockfile for install.
default: 'true'
network-concurrency:
required: false
description: Network concurrency for yarn install (ignored for pnpm).
default: '1'
verbose:
required: false
description: Whether to output verbose install logs.
default: 'false'
additional-args:
required: false
description: Additional arguments for the install command.
sanity-check:
required: false
description: Whether to output a sanity check of Node/NPM configuration.
default: 'false'
create-npmrc:
required: false
description: Whether to create the .npmrc file before installing dependencies.
default: 'true'

# Secrets
font-awesome-key:
required: false
description: NPM Font Awesome token.
package-pat:
required: false
description: NPM package feed token.

outputs:
package-manager:
description: Detected package manager (pnpm or yarn).
value: ${{ steps.detect-pm.outputs.pm }}

runs:
using: composite
steps:
- name: Detect package manager
id: detect-pm
run: |
if [ -f pnpm-lock.yaml ]; then
echo "pm=pnpm" >> $GITHUB_OUTPUT
else
echo "pm=yarn" >> $GITHUB_OUTPUT
fi
shell: bash

- name: Setup pnpm via corepack
if: steps.detect-pm.outputs.pm == 'pnpm'
run: |
corepack enable
PM_VERSION=$(node -p "require('./package.json').packageManager?.split('@')[1] || '11.4.0'")
corepack prepare "pnpm@${PM_VERSION}" --activate
shell: bash

- name: Setup node (pnpm)
if: steps.detect-pm.outputs.pm == 'pnpm'
uses: actions/setup-node@v6
with:
node-version: ${{ inputs.node-version || '' }}
node-version-file: ${{ inputs.node-version && '' || '.nvmrc' }}

- name: Get pnpm store path
if: steps.detect-pm.outputs.pm == 'pnpm'
id: pnpm-store
run: echo "dir=$(pnpm store path)" >> $GITHUB_OUTPUT
shell: bash

- name: Cache pnpm store
if: steps.detect-pm.outputs.pm == 'pnpm'
uses: actions/cache@v5
with:
path: ${{ steps.pnpm-store.outputs.dir }}
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}

- name: Setup node (yarn)
if: steps.detect-pm.outputs.pm == 'yarn'
uses: actions/setup-node@v6
with:
node-version: ${{ inputs.node-version || '' }}
node-version-file: ${{ inputs.node-version && '' || '.nvmrc' }}
cache: 'yarn'

- name: Create .npmrc file
if: ${{ inputs.create-npmrc == 'true' }}
run: ${{ github.action_path }}/create-npmrc-file.sh
shell: bash
env:
FONT_AWESOME_KEY: ${{ inputs.font-awesome-key }}
PACKAGE_PAT: ${{ inputs.package-pat }}

- name: Sanity check registries/proxy (no secrets)
if: ${{ inputs.sanity-check == 'true' }}
run: ${{ github.action_path }}/sanity-check.sh
shell: bash
env:
DETECTED_PM: ${{ steps.detect-pm.outputs.pm }}

- name: Install dependencies
run: ${{ github.action_path }}/install-dependencies.sh
shell: bash
env:
DETECTED_PM: ${{ steps.detect-pm.outputs.pm }}
FROZEN_LOCKFILE: ${{ inputs.frozen-lockfile }}
NETWORK_CONCURRENCY: ${{ inputs.network-concurrency }}
VERBOSE: ${{ inputs.verbose }}
ADDITIONAL_ARGS: ${{ inputs.additional-args }}
11 changes: 11 additions & 0 deletions .github/actions/node-prepare/create-npmrc-file.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash

cat > .npmrc << EOF
registry=https://registry.npmjs.org/
@telicent-io:registry=https://npm.pkg.github.com/
//npm.pkg.github.com/:_authToken=$PACKAGE_PAT
@awesome.me:registry=https://npm.fontawesome.com/
@fortawesome:registry=https://npm.fontawesome.com/
//npm.fontawesome.com/:_authToken=$FONT_AWESOME_KEY
always-auth=true
EOF
26 changes: 26 additions & 0 deletions .github/actions/node-prepare/install-dependencies.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/bin/bash

export NODE_OPTIONS="${NODE_OPTIONS:-} --dns-result-order=ipv4first"

if [ "$DETECTED_PM" = "pnpm" ]; then
ARGS=""
if [ "$FROZEN_LOCKFILE" = "true" ]; then
ARGS="$ARGS --frozen-lockfile"
fi
LOCAL_MACHINE=false pnpm install $ARGS
else
ARGS=""
if [ "$FROZEN_LOCKFILE" = "true" ]; then
ARGS="$ARGS --frozen-lockfile"
fi
if [ -n "$NETWORK_CONCURRENCY" ] && [ "$NETWORK_CONCURRENCY" != "" ]; then
ARGS="$ARGS --network-concurrency $NETWORK_CONCURRENCY"
fi
if [ "$VERBOSE" = "true" ]; then
ARGS="$ARGS --verbose"
fi
if [ -n "$ADDITIONAL_ARGS" ]; then
ARGS="$ARGS $ADDITIONAL_ARGS"
fi
LOCAL_MACHINE=false yarn install $ARGS
fi
28 changes: 28 additions & 0 deletions .github/actions/node-prepare/sanity-check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/bin/bash

echo "node: $(node -v) | npm: $(npm -v)"
if [ "$DETECTED_PM" = "pnpm" ]; then
echo "pnpm: $(pnpm -v)"
else
echo "yarn: $(yarn -v)"
fi
echo "npm default registry: $(npm config get registry)"
echo "@telicent-io registry: $(npm config get @telicent-io:registry)"
echo "@fortawesome registry: $(npm config get @fortawesome:registry)"

for v in HTTP_PROXY HTTPS_PROXY NO_PROXY http_proxy https_proxy no_proxy; do
if [ -n "${!v:-}" ]; then echo "$v is set"; fi
done

for h in registry.npmjs.org npm.pkg.github.com npm.fontawesome.com; do
echo "== $h =="
getent hosts "$h" || true
curl -sSIL --max-time 10 "https://$h/" | head -n 1 || echo "curl FAIL: $h"
done

if [ "$DETECTED_PM" = "pnpm" ] && [ -f pnpm-lock.yaml ]; then
echo "Lockfile: pnpm-lock.yaml"
elif [ -f yarn.lock ]; then
echo "Hosts from yarn.lock:"
awk '/^ resolved /{print $2}' yarn.lock | sed -E 's/^"//; s/"$//' | awk -F/ '{print $3}' | sort -u
fi
96 changes: 86 additions & 10 deletions .github/workflows/javascript-test-feature-branch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,21 @@ on:
default: "."
type: string
description: directory to run all run commands from
yarn-test-command:
test-command:
required: false
default: "test"
type: string
description: Yarn test command e.g. "test" or "test:ci"
description: Test command e.g. "test" or "test:ci"
yarn-test-command:
required: false
default: ""
type: string
description: "DEPRECATED: use test-command instead"
package-manager:
required: false
default: "auto"
type: string
description: "Package manager to use: auto (detect from lockfile), yarn, or pnpm"

jobs:
quality:
Expand All @@ -27,44 +37,110 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v6.0.2

- name: Detect package manager
id: detect-pm
working-directory: ${{ inputs.working-directory }}
run: |
if [ "${{ inputs.package-manager }}" != "auto" ]; then
echo "pm=${{ inputs.package-manager }}" >> $GITHUB_OUTPUT
elif [ -f pnpm-lock.yaml ]; then
echo "pm=pnpm" >> $GITHUB_OUTPUT
else
echo "pm=yarn" >> $GITHUB_OUTPUT
fi

- name: Resolve test command
id: test-cmd
run: |
if [ -n "${{ inputs.yarn-test-command }}" ] && [ "${{ inputs.yarn-test-command }}" != "test" ]; then
echo "cmd=${{ inputs.yarn-test-command }}" >> $GITHUB_OUTPUT
else
echo "cmd=${{ inputs.test-command }}" >> $GITHUB_OUTPUT
fi

- name: configure-node
uses: actions/setup-node@v6.4.0
with:
node-version: 20

- name: Get node version
id: node
run: |
echo "version=$(node -v)" >> $GITHUB_OUTPUT

# ── pnpm path ──
- name: Setup pnpm via corepack
if: steps.detect-pm.outputs.pm == 'pnpm'
run: |
corepack enable
PM_VERSION=$(node -p "require('./package.json').packageManager?.split('@')[1] || '11.4.0'")
corepack prepare "pnpm@${PM_VERSION}" --activate

- name: Get pnpm store path
if: steps.detect-pm.outputs.pm == 'pnpm'
id: pnpm-store
run: echo "dir=$(pnpm store path)" >> $GITHUB_OUTPUT

- name: Cache pnpm store
if: steps.detect-pm.outputs.pm == 'pnpm'
uses: actions/cache@v5.0.5
with:
path: ${{ steps.pnpm-store.outputs.dir }}
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ steps.node.outputs.version }}

- name: Configure registry auth (pnpm)
if: steps.detect-pm.outputs.pm == 'pnpm'
run: |
npm config set "@telicent-oss:registry" "https://www.npmjs.com/"
npm config set "//www.npmjs.com//:_authToken" ${{ secrets.NPM_REGISTRY_TOKEN }}

- name: Install dependencies (pnpm)
if: steps.detect-pm.outputs.pm == 'pnpm'
run: pnpm install --frozen-lockfile

# ── yarn path ──
- name: Get yarn cache directory path
if: steps.detect-pm.outputs.pm == 'yarn'
id: yarn-cache-dir-path
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
- name: Cache node modules

- name: Cache node modules (yarn)
if: steps.detect-pm.outputs.pm == 'yarn'
uses: actions/cache@v5.0.5
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-node_modules-${{ hashFiles('**/yarn.lock') }}-${{ steps.node.outputs.version }}

- name: create .yarnrc file
if: steps.detect-pm.outputs.pm == 'yarn'
run: |
npm config set "@telicent-oss:registry" "https://www.npmjs.com/"
npm config set "//www.npmjs.com//:_authToken" ${{ secrets.NPM_REGISTRY_TOKEN }}
- name: Install dependencies
npm config set "//www.npmjs.com//:_authToken" ${{ secrets.NPM_REGISTRY_TOKEN }}

- name: Install dependencies (yarn)
if: steps.detect-pm.outputs.pm == 'yarn'
run: yarn install
- name: yarn tsc --noEmit if needed

# ── shared steps ──
- name: tsc --noEmit if needed
run: |
PM="${{ steps.detect-pm.outputs.pm }}"
if [ -f tsconfig.json ]; then
echo "TypeScript config found."
TSC_IGNORE=$(node -p "require('./package.json').ci?.['shared-workflows/.github/workflows/javascript-test-feature-branch.yml']?.['WARNING: TSC CHECK NO ERROR'] || ''")
if [ "$TSC_IGNORE" != "" ]; then
echo "WARNING flag found in package.json. Running tsc --noEmit (errors will be ignored)..."
yarn tsc --noEmit || echo "tsc failed but error ignored due to warning flag."
$PM tsc --noEmit || echo "tsc failed but error ignored due to warning flag."
else
echo "Running tsc --noEmit..."
yarn tsc --noEmit
$PM tsc --noEmit
fi
else
echo "No TypeScript config found. Skipping tsc..."
fi
- name: run integrated tests
run: yarn ${{ inputs.yarn-test-command }}

- name: Run integrated tests
run: ${{ steps.detect-pm.outputs.pm }} ${{ steps.test-cmd.outputs.cmd }}
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_REGISTRY_TOKEN }}