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 and/or npm/tcl.ref, commit, push to default branch # git tag v8.3.638 # git push origin v8.3.638 # # The tag name (minus the leading "v") becomes the published npm version. # 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' 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 # Clone intubun/tcl into a sibling directory at the pinned ref from # npm/tcl.ref. npm/build.sh would do this on its own, but doing it as # an explicit step makes the resolved SHA visible at the top of the # job log and keeps the build step's output focused on the C build. # The TCL source tree is treated as read-only — the actual WASM build # runs inside magic (toolchains/emscripten/build-tcl-wasm.sh). - name: Pin and clone tcltk/tcl run: | . npm/tcl.ref : "${TCL_REPO_URL:=https://github.com/tcltk/tcl.git}" : "${TCL_REF:=main}" echo "Pinned TCL: $TCL_REF ($TCL_REPO_URL)" # 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" ) - name: Build WASM — both variants (tcl + notcl) 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 f in npm/examples/output/*; do name=$(basename "$f") case "$f" in *.gds) echo "===== $name (binary, $(wc -c < "$f") bytes — skipped) =====" ;; *) echo "===== $name ====="; cat "$f" ;; esac done # The release gate. We publish a new npm version only when a tag of the # shape v... is pushed. The tag name (minus the leading "v") is # taken as the npm version verbatim — so `v8.3.638` → npm 8.3.638. - name: Determine release version (tag-driven only) id: release run: | 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/}" echo "publish=true" >> "$GITHUB_OUTPUT" echo "version=${tag#v}" >> "$GITHUB_OUTPUT" echo "Tag release: $tag → npm version ${tag#v}" else # For non-tag CI runs, use a dev-suffixed version so the packed # tarball is still consumable for local inspection / artifact upload. base=$(cat VERSION) date=$(git show -s --format=%cs | tr -d '-') hash=$(git show -s --format=%h) echo "publish=false" >> "$GITHUB_OUTPUT" echo "version=${base}-${date}.${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 }}