Skip to content

feat(ns): D-554 — ns attr-map + name-symbol meta merge into ns meta #136

feat(ns): D-554 — ns attr-map + name-symbol meta merge into ns meta

feat(ns): D-554 — ns attr-map + name-symbol meta merge into ns meta #136

Workflow file for this run

# CI — the host-local gate on the supported OSes (macOS arm64 + Linux x86_64),
# mirroring the local gate. Each matrix leg runs `scripts/ci_gate.sh`, so CI
# verifies exactly what the maintainer runs per host (single source of truth).
#
# Triggers: pull_request (any target) + push to `main` + manual dispatch.
# The gate has no external runtime dependency beyond Zig 0.16.0 and python3
# (preinstalled on GitHub runners); `zig build -Dwasm` fetches the embedded
# zwasm engine + the zlinter dev-dep from git over the network.
#
# Two-tier, matching the local ADR-0107 (smoke per commit, full batched) +
# ADR-0049 (the Linux full gate is batched, not per-commit) disciplines so CI is
# not wasteful:
# push to main / PR → fast CORE (units + F-012 diff oracle, no e2e shell suite).
# nightly schedule / manual dispatch → FULL gate (+ every e2e step).
# The Zig toolchain, the fetched deps (zwasm/zlinter), and the build cache are
# all preserved across runs (actions/cache), so a warm run rebuilds only what
# changed instead of three cold ReleaseSafe builds — the dominant cost.
name: ci
on:
# Skip CI on doc/scaffolding-only pushes (markdown, .dev/ ledger + ADRs + notes):
# they cannot change build/test outcome, so a ~15-min gate on them is pure waste.
# A commit that also touches src / scripts / build.zig* / flake.nix / .github /
# a root data-yaml still runs (paths-ignore skips only when ALL changed files match).
pull_request:
paths-ignore: ['**/*.md', '.dev/**']
push:
branches: [main]
paths-ignore: ['**/*.md', '.dev/**']
workflow_dispatch:
schedule:
# Nightly full gate (03:17 UTC) — the batched Linux full-e2e coverage that
# ADR-0049 keeps off the per-commit path.
- cron: '17 3 * * *'
permissions:
contents: read
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
gate:
name: gate (${{ matrix.name }})
runs-on: ${{ matrix.runs-on }}
timeout-minutes: 75
strategy:
fail-fast: false
matrix:
include:
- name: aarch64-macos
runs-on: macos-15
zig-dir: zig-aarch64-macos-0.16.0
- name: x86_64-linux
runs-on: ubuntu-22.04
zig-dir: zig-x86_64-linux-0.16.0
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.sha }}
- name: Cache Zig 0.16.0
id: cache-zig
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.local/zig-0.16.0
key: zig-0.16.0-${{ matrix.name }}
- name: Install Zig 0.16.0
if: steps.cache-zig.outputs.cache-hit != 'true'
shell: bash
run: |
set -euo pipefail
source .github/versions.lock
mkdir -p ~/.local
curl -fsSL "https://ziglang.org/download/${ZIG_VERSION}/${{ matrix.zig-dir }}.tar.xz" -o /tmp/zig.tar.xz
tar -xJf /tmp/zig.tar.xz -C ~/.local
mv "$HOME/.local/${{ matrix.zig-dir }}" "$HOME/.local/zig-0.16.0"
- name: Add Zig to PATH
shell: bash
run: echo "$HOME/.local/zig-0.16.0" >> "$GITHUB_PATH"
- name: Pin Zig global cache dir
shell: bash
run: echo "ZIG_GLOBAL_CACHE_DIR=$HOME/.cache/zig" >> "$GITHUB_ENV"
# Cache the fetched deps (zwasm git dep + zlinter, under the global cache's
# p/) AND the build artifacts (global cache + the repo-local .zig-cache),
# keyed on the manifests. A same-manifest run restores everything; a
# manifest change falls back via restore-keys to the newest prior cache and
# rebuilds incrementally (Zig's cache is content-addressed, so a stale
# restore is safe — it only ever causes a rebuild, never a wrong build).
- name: Cache Zig deps + build
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
~/.cache/zig
.zig-cache
key: ${{ matrix.name }}-zigbuild-${{ hashFiles('build.zig.zon', 'build.zig') }}
restore-keys: |
${{ matrix.name }}-zigbuild-
# Gate deps not universally preinstalled: ripgrep (check_debt_id_refs.sh)
# and, on macOS, bash >= 4 — the macos-15 runner resolves `bash` to the
# system 3.2, which cannot parse the e2e harness idioms (heredoc inside
# `$()`, `case` inside `$()`, `declare -A`); 43 gate scripts fail under it
# (first exposed by the 2026-07-02 nightly, the first FULL run on the
# hosted mac runner). Every script is invoked as `bash foo.sh` or via
# `#!/usr/bin/env bash`, so a brew bash 5 on PATH fixes all of them.
# Install only what is absent (fast no-op when the image ships it).
- name: Install gate deps if missing (ripgrep; bash>=4 on macOS)
shell: bash
run: |
if [ "$RUNNER_OS" = "Linux" ]; then
command -v rg >/dev/null 2>&1 || { sudo apt-get update -qq && sudo apt-get install -y -qq ripgrep; }
else
command -v rg >/dev/null 2>&1 || brew install ripgrep
if [ "$(bash -c 'echo "${BASH_VERSINFO[0]}"')" -lt 4 ]; then
brew install bash
fi
fi
- name: Verify toolchain
shell: bash
run: |
zig version
python3 --version
rg --version | head -1
# Check the PATH-resolved bash (what `bash foo.sh` + env-shebang
# scripts actually get), not this step's own shell.
bash --version | head -1
if [ "$(bash -c 'echo "${BASH_VERSINFO[0]}"')" -lt 4 ]; then
echo "::error::gate requires bash >= 4 on PATH (PATH bash is $(bash -c 'echo "$BASH_VERSION"'))"; exit 1
fi
- name: ci_gate.sh
shell: bash
env:
# nightly schedule + manual dispatch → full gate; push/PR → fast core.
CLJW_CI_FULL: ${{ (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && '1' || '0' }}
run: bash scripts/ci_gate.sh