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... 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 @/ 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--- # 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... 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"