Skip to content

Merge pull request #299 from link-foundation/issue-298-d29274924d59 #220

Merge pull request #299 from link-foundation/issue-298-d29274924d59

Merge pull request #299 from link-foundation/issue-298-d29274924d59 #220

Workflow file for this run

name: js
on:
push:
branches: main
paths:
- 'js/**'
- '.github/workflows/js.yml'
pull_request:
paths:
- 'js/**'
- '.github/workflows/js.yml'
workflow_dispatch:
inputs:
verbose:
description: 'Print extra diagnostics (never secret values)'
type: boolean
required: false
default: false
permissions:
contents: read
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CI_VERBOSE: ${{ inputs.verbose && 'true' || vars.CI_VERBOSE || 'false' }}
defaults:
run:
working-directory: js
jobs:
findChangedJsFiles:
runs-on: ubuntu-latest
timeout-minutes: 10
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-detect-changes
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
outputs:
isJsFilesChanged: ${{ steps.setIsJsFilesChangedOutput.outputs.isJsFilesChanged }}
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
fetch-depth: 0
- name: Get changed files using defaults
id: changed-files
uses: tj-actions/changed-files@v47
- name: Set output isJsFilesChanged
id: setIsJsFilesChangedOutput
env:
ALL_CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
run: |
isJsFilesChanged='false'
# workflow_dispatch has no diff to inspect, so treat it as "changed".
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
isJsFilesChanged='true'
fi
echo "Changed files: $ALL_CHANGED_FILES"
for changedFile in $ALL_CHANGED_FILES; do
if [[ $changedFile == js/* ]] || [[ $changedFile == .github/workflows/js.yml ]]; then
isJsFilesChanged='true'
break
fi
done
echo "isJsFilesChanged=${isJsFilesChanged}" >> "$GITHUB_OUTPUT"
echo "isJsFilesChanged: ${isJsFilesChanged}"
format:
needs: [findChangedJsFiles]
if: ${{ needs.findChangedJsFiles.outputs.isJsFilesChanged == 'true' }}
runs-on: ubuntu-latest
timeout-minutes: 10
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-format
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
submodules: true
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Install Dependencies
run: bun install --frozen-lockfile
# Uses the pinned devDependency rather than `npx prettier`, which would
# silently resolve a different version on every run.
- name: Check formatting with Prettier
run: bun run format:check
lint:
needs: [findChangedJsFiles, format]
if: ${{ needs.findChangedJsFiles.outputs.isJsFilesChanged == 'true' }}
runs-on: ubuntu-latest
timeout-minutes: 10
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-lint
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
submodules: true
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Install Dependencies
run: bun install --frozen-lockfile
- name: Lint
run: bun run lint
test:
needs: [findChangedJsFiles, format, lint]
if: ${{ needs.findChangedJsFiles.outputs.isJsFilesChanged == 'true' }}
runs-on: ubuntu-latest
timeout-minutes: 15
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-test
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
submodules: true
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Install Dependencies
run: bun install --frozen-lockfile
- name: Build
run: bun run build
- name: Test
run: bun test
publishToNpm:
needs: [findChangedJsFiles, test]
if: ${{ needs.findChangedJsFiles.outputs.isJsFilesChanged == 'true' && github.event_name == 'push' && github.ref == 'refs/heads/main' }}
runs-on: ubuntu-latest
timeout-minutes: 15
concurrency:
group: ${{ github.workflow }}-publish-npm
cancel-in-progress: false
permissions:
contents: read
# Required for npm trusted publishing (OIDC). Harmless when the package
# is not configured for it; npm then falls back to NODE_AUTH_TOKEN.
id-token: write
outputs:
published: ${{ steps.publish.outputs.published }}
version: ${{ steps.version-check.outputs.version }}
name: ${{ steps.version-check.outputs.name }}
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
submodules: true
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
# npm itself does the publishing: trusted publishing needs npm >= 11.5.1,
# and setup-node writes the registry entry ~/.npmrc that npm reads.
- name: Setup Node
uses: actions/setup-node@v7
with:
node-version: '22'
registry-url: 'https://registry.npmjs.org'
# Node 22 bundles npm 10.x; trusted publishing (OIDC) needs >= 11.5.1.
# Pinned to the major rather than @latest so a future npm 12 cannot
# change publish behaviour without a commit here.
- name: Upgrade npm for trusted publishing
run: npm install -g npm@11
- name: Install Dependencies
run: bun install --frozen-lockfile
- name: Build
run: bun run build
- name: Check if version already published
id: version-check
run: |
set -euo pipefail
PACKAGE_VERSION=$(node -p "require('./package.json').version")
PACKAGE_NAME=$(node -p "require('./package.json').name")
echo "version=$PACKAGE_VERSION" >> "$GITHUB_OUTPUT"
echo "name=$PACKAGE_NAME" >> "$GITHUB_OUTPUT"
echo "Package: $PACKAGE_NAME@$PACKAGE_VERSION"
if npm view "$PACKAGE_NAME@$PACKAGE_VERSION" version >/dev/null 2>&1; then
echo "Version $PACKAGE_VERSION already exists on npm"
echo "should_publish=false" >> "$GITHUB_OUTPUT"
else
echo "Version $PACKAGE_VERSION does not exist on npm"
echo "should_publish=true" >> "$GITHUB_OUTPUT"
fi
- name: Publish to npm
id: publish
env:
# Bootstrap fallback for as long as trusted publishing is not
# configured for this package on npmjs.com.
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
set -euo pipefail
if [ "${{ steps.version-check.outputs.should_publish }}" != "true" ]; then
echo "published=skipped" >> "$GITHUB_OUTPUT"
echo "::notice::Version ${{ steps.version-check.outputs.version }} is already on npm, nothing to publish"
exit 0
fi
# Presence only, never the value.
if [ -n "$NODE_AUTH_TOKEN" ]; then
echo "npm credential: NPM_TOKEN present"
else
echo "npm credential: NPM_TOKEN absent, relying on OIDC trusted publishing"
fi
# Keep the log outside the package directory: npm builds the tarball
# from the working tree, so a log written here ships inside it.
PUBLISH_LOG="$RUNNER_TEMP/publish.log"
if ! npm publish --access public 2>&1 | tee "$PUBLISH_LOG"; then
if grep -q 'ENEEDAUTH\|E401\|E403' "$PUBLISH_LOG"; then
echo "::error::npm rejected the credentials. Either configure trusted publishing for ${{ steps.version-check.outputs.name }} on npmjs.com or set the NPM_TOKEN secret."
fi
if grep -q 'E422\|provenance' "$PUBLISH_LOG"; then
echo "::error::npm rejected the provenance attestation. Trusted publishing signs the package with this repository's identity, so package.json must carry a matching \"repository.url\" (https://github.com/${{ github.repository }})."
fi
echo "published=failed" >> "$GITHUB_OUTPUT"
exit 1
fi
echo "published=true" >> "$GITHUB_OUTPUT"
- name: Verify the package is really on npm
if: steps.publish.outputs.published == 'true'
env:
PACKAGE_NAME: ${{ steps.version-check.outputs.name }}
PACKAGE_VERSION: ${{ steps.version-check.outputs.version }}
run: |
set -euo pipefail
# shellcheck source=scripts/ci/registry-probe.sh
. "$GITHUB_WORKSPACE/scripts/ci/registry-probe.sh"
wait_for_registry \
"${PACKAGE_NAME}@${PACKAGE_VERSION} on npm" \
"https://registry.npmjs.org/${PACKAGE_NAME}/${PACKAGE_VERSION}" 10 15
publishRelease:
runs-on: ubuntu-latest
timeout-minutes: 10
needs: [findChangedJsFiles, publishToNpm]
# `result == 'success'` was true even when the publish step was skipped,
# so releases were cut for versions that were never pushed.
if: ${{ needs.findChangedJsFiles.outputs.isJsFilesChanged == 'true' && needs.publishToNpm.outputs.published == 'true' && github.event_name == 'push' && github.ref == 'refs/heads/main' }}
permissions:
contents: write
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
submodules: true
- name: Create GitHub release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PACKAGE_VERSION: ${{ needs.publishToNpm.outputs.version }}
PACKAGE_NAME: ${{ needs.publishToNpm.outputs.name }}
run: |
set -euo pipefail
TAG="js_${PACKAGE_VERSION}"
if gh release view "$TAG" >/dev/null 2>&1; then
echo "Release $TAG already exists"
exit 0
fi
gh release create "$TAG" \
--title "[JS] $PACKAGE_VERSION" \
--notes "https://www.npmjs.com/package/$PACKAGE_NAME"