magic/.github/workflows/main-wasm.yml

256 lines
11 KiB
YAML

name: CI-wasm
# Builds the Magic WebAssembly target (both the non-TCL and TCL variants)
# on every push and pull request as a CI check. **Publishing** only happens
# when a release tag of the form v<x.y.z>... is pushed — that gate is the
# manual release trigger:
#
# # bump magic/VERSION, commit, push to default branch
# git tag v8.3.638
# git push origin v8.3.638
#
# The tag name (minus the leading "v") provides the base; the workflow appends
# the commit date and short SHA: v8.3.799 → 8.3.799020261231+git01234cde.
# Forks publish under their own namespace via the @<owner>/ scope.
on:
push:
pull_request:
workflow_dispatch:
inputs:
emsdk_version:
description: 'emsdk version to build with (default: latest; pin a version number to bisect)'
type: string
default: 'latest'
tcl_ref:
description: 'TCL ref to build against (default: auto-resolve latest stable tag). Use a tag like core-9-0-3, a branch, or a commit SHA to bisect a regression.'
type: string
default: ''
tcl_repo_url:
description: 'TCL repository URL (default: https://github.com/tcltk/tcl.git)'
type: string
default: ''
dry_run:
description: 'Dry run: pack only, do not publish even on tag pushes'
type: boolean
default: true
# actions/upload-artifact@v5 still runs on Node.js 20. Force Node 24 to
# silence the deprecation warning until upload-artifact ships a Node-24
# release. Drop this once upgraded.
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
permissions:
contents: read
packages: write
jobs:
build-wasm:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 2
- name: Set up Node.js
uses: actions/setup-node@v5
with:
node-version: '22'
registry-url: 'https://npm.pkg.github.com'
- name: Install emsdk
env:
# Defaults to latest so CI tracks emsdk HEAD and catches breakage early.
# Override via workflow_dispatch to pin a specific version when needed
# (e.g. to bisect a regression or verify a post-build.sh patch still applies).
EMSDK_VERSION: ${{ github.event.inputs.emsdk_version || 'latest' }}
run: |
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install "$EMSDK_VERSION"
./emsdk activate "$EMSDK_VERSION"
# Dump native + emscripten preprocessor defines. Useful for diagnosing
# WASM-build differences after an emsdk bump.
- name: Emscripten Diagnostic
run: |
source ./emsdk/emsdk_env.sh
echo "===== gcc -dM -E - ====="; echo | gcc -dM -E - | sort
echo "===== g++ -dM -E - ====="; echo | g++ -dM -E - | sort
echo "===== emcc -dM -E - ====="; echo | emcc -dM -E - | sort
echo "===== em++ -dM -E - ====="; echo | em++ -dM -E - | sort
# Determine which TCL ref to build against.
# Priority: workflow_dispatch input > auto-resolved latest stable tag.
# TCL stable releases follow the core-<major>-<even_minor>-<patch>
# naming convention; core-9-0-x is the current stable series.
# Falls back to main only if no release tags are found at all.
- name: Resolve TCL ref
id: resolve-tcl
env:
TCL_REPO_URL: ${{ github.event.inputs.tcl_repo_url || 'https://github.com/tcltk/tcl.git' }}
TCL_REF_INPUT: ${{ github.event.inputs.tcl_ref || '' }}
run: |
if [ -n "$TCL_REF_INPUT" ]; then
TCL_REF="$TCL_REF_INPUT"
echo "Using workflow_dispatch TCL_REF: $TCL_REF"
else
TCL_REF=$(git ls-remote --tags --sort=-version:refname "$TCL_REPO_URL" \
'refs/tags/core-9-0-*' \
| grep -v '\^{}' \
| head -1 \
| awk '{print $2}' \
| sed 's|refs/tags/||')
if [ -z "$TCL_REF" ]; then
TCL_REF=main
echo "Warning: no stable core-9-0-x tag found, falling back to main"
else
echo "Auto-resolved latest stable TCL tag: $TCL_REF"
fi
fi
echo "tcl_ref=$TCL_REF" >> "$GITHUB_OUTPUT"
echo "tcl_repo_url=$TCL_REPO_URL" >> "$GITHUB_OUTPUT"
# Clone tcltk/tcl into a sibling directory at the resolved ref.
# Done as an explicit step so the exact commit is visible in the job
# log. The source tree is read-only — the WASM build runs inside magic.
- name: Clone tcltk/tcl
env:
TCL_REPO_URL: ${{ steps.resolve-tcl.outputs.tcl_repo_url }}
TCL_REF: ${{ steps.resolve-tcl.outputs.tcl_ref }}
run: |
# autocrlf=false: ubuntu-latest is already LF, but make it explicit.
git -c core.autocrlf=false clone "$TCL_REPO_URL" ../tcl
cd ../tcl
git checkout --detach "$TCL_REF"
echo "=== TCL commit ==="
git log -n1 --format="commit %H%nauthor %an <%ae>%ndate %ci%nref %D%n%n %s"
- name: Build WASM — both variants (tcl + notcl)
env:
TCL_REF: ${{ steps.resolve-tcl.outputs.tcl_ref }}
TCL_REPO_URL: ${{ steps.resolve-tcl.outputs.tcl_repo_url }}
run: |
source ./emsdk/emsdk_env.sh
bash npm/build.sh --variant=both
- name: Run example tests (non-TCL variant)
run: cd npm && npm test
- name: Run full test suite (TCL variant)
run: cd npm && npm run test:tcl
# Dump generated text outputs (.ext, .spice, .cif, …) into the CI log
# so a regression in extraction / netlisting / cifoutput is visible
# without having to download artifacts. The .gds output is binary —
# skip it and just record its size.
- name: Display example outputs
run: |
shopt -s nullglob
for dir in npm/examples/output npm/examples/output-tcl; do
[ -d "$dir" ] || continue
echo "======== $dir ========"
for f in "$dir"/*; do
name=$(basename "$f")
case "$f" in
*.gds) echo "===== $name (binary, $(wc -c < "$f") bytes — skipped) =====" ;;
*) echo "===== $name ====="; cat "$f" ;;
esac
done
done
# The release gate. We publish a new npm version only when a tag of the
# shape v<x.y.z>... is pushed.
#
# Version scheme (per dmiles' recommendation):
# {MAJOR}.{MINOR}.{PATCH}0{YYYYMMDD}+git{SHORT_SHA}
# e.g. v8.3.799 pushed on 2026-12-31 → 8.3.799020261231+git01234cde
#
# The leading zero between PATCH and date keeps the number readable and
# ensures correct numeric ordering: 799020261231 < 800020261231.
# Build metadata (+git...) is ignored by npm for comparison but retained
# for traceability. Security patches for the 799 series can be inserted
# as later dates (799020270101, 799020270201, …) and are matched by the
# range <=8.3.799900000000 or <8.3.8000000000000.
- name: Determine release version (tag-driven only)
id: release
run: |
date=$(git show -s --format=%cs | tr -d '-')
hash=$(git show -s --format=%h)
if [ "${{ github.event_name }}" = "push" ] && \
echo "${{ github.ref }}" | grep -Eq '^refs/tags/v[0-9]+\.[0-9]+\.[0-9]+'; then
tag="${GITHUB_REF#refs/tags/}"
base="${tag#v}"
echo "publish=true" >> "$GITHUB_OUTPUT"
echo "version=${base}0${date}+git${hash}" >> "$GITHUB_OUTPUT"
echo "Tag release: $tag → npm version ${base}0${date}+git${hash}"
else
base=$(cat VERSION)
echo "publish=false" >> "$GITHUB_OUTPUT"
echo "version=${base}0${date}+git${hash}" >> "$GITHUB_OUTPUT"
echo "Non-tag build: will not publish."
fi
- name: Set package version and scope
env:
VERSION: ${{ steps.release.outputs.version }}
run: |
# Scope the package to the repo owner so it lands in the right
# GitHub Packages namespace regardless of who hosts the repo.
SCOPED_NAME="@${{ github.repository_owner }}/magic-vlsi-wasm"
cd npm
npm pkg set name="$SCOPED_NAME"
npm pkg set publishConfig.registry="https://npm.pkg.github.com"
npm version "$VERSION" --no-git-tag-version --allow-same-version
- name: Pack
run: ./npm/pack.sh
- name: Upload tarball as artifact
uses: actions/upload-artifact@v5
with:
name: magic-vlsi-wasm-npm
path: npm/*.tgz
- name: Publish to GitHub Packages
if: steps.release.outputs.publish == 'true' && github.event.inputs.dry_run != 'true'
run: cd npm && npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Write a Markdown summary visible next to the artifacts on the Actions
# page. Captures the exact versions used so a future regression can be
# bisected without scrolling through raw logs.
- name: Build summary
if: always()
env:
TCL_REF: ${{ steps.resolve-tcl.outputs.tcl_ref }}
TCL_REPO_URL: ${{ steps.resolve-tcl.outputs.tcl_repo_url }}
run: |
source ./emsdk/emsdk_env.sh 2>/dev/null || true
EMCC_VER=$(emcc --version 2>/dev/null | head -1 || echo "unavailable")
GCC_VER=$(gcc --version 2>/dev/null | head -1 || echo "unavailable")
NODE_VER=$(node --version 2>/dev/null || echo "unavailable")
MAGIC_VER=$(cat VERSION 2>/dev/null || echo "unavailable")
if [ -d ../tcl/.git ]; then
TCL_SHA=$(cd ../tcl && git rev-parse HEAD)
TCL_DATE=$(cd ../tcl && git log -1 --format="%ci")
TCL_SUBJECT=$(cd ../tcl && git log -1 --format="%s")
else
TCL_SHA="(not cloned)"; TCL_DATE=""; TCL_SUBJECT=""
fi
printf '## Build info\n\n' >> "$GITHUB_STEP_SUMMARY"
printf '| Component | Details |\n' >> "$GITHUB_STEP_SUMMARY"
printf '|-----------|----------|\n' >> "$GITHUB_STEP_SUMMARY"
printf '| Magic | `%s` |\n' "$MAGIC_VER" >> "$GITHUB_STEP_SUMMARY"
printf '| Emscripten | %s |\n' "$EMCC_VER" >> "$GITHUB_STEP_SUMMARY"
printf '| GCC | %s |\n' "$GCC_VER" >> "$GITHUB_STEP_SUMMARY"
printf '| Node.js | %s |\n' "$NODE_VER" >> "$GITHUB_STEP_SUMMARY"
printf '| TCL repo | %s |\n' "$TCL_REPO_URL" >> "$GITHUB_STEP_SUMMARY"
printf '| TCL ref | `%s` |\n' "$TCL_REF" >> "$GITHUB_STEP_SUMMARY"
printf '| TCL commit | `%s` |\n' "$TCL_SHA" >> "$GITHUB_STEP_SUMMARY"
printf '| TCL date | %s |\n' "$TCL_DATE" >> "$GITHUB_STEP_SUMMARY"
printf '| TCL subject | %s |\n' "$TCL_SUBJECT" >> "$GITHUB_STEP_SUMMARY"