Skip to content

ci: use native 'mcpp index update' for the sandbox index refresh #277

ci: use native 'mcpp index update' for the sandbox index refresh

ci: use native 'mcpp index update' for the sandbox index refresh #277

Workflow file for this run

name: validate
on:
pull_request:
paths: ["pkgs/**/*.lua", "tests/**", "README.md", ".github/workflows/validate.yml"]
push:
branches: [main]
schedule:
# nightly full regression — exercises every smoke suite regardless of diff
- cron: "0 6 * * *"
workflow_dispatch:
env:
# 0.0.96: carries the windows scanner symlink-escape crash fix (mcpp#230),
# so all platforms ride the same version again. Older floors of note:
# 0.0.94 fixed feature-gated `sources` under `mcpp test` (mcpp#218);
# 0.0.91 added standard = "c++fly" to the resolver grammar, so c++fly
# descriptors get the lint WARN below, not a hard grammar-parse rejection.
MCPP_VERSION: "0.0.96"
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install lua
run: sudo apt-get install -y --no-install-recommends lua5.4
- name: Lint package descriptors
run: |
fail=0
for f in pkgs/*/*.lua; do
# 1. Lua syntax check — load (= compile) without executing.
# `loadfile(name, 't')` rejects bytecode and parses text only.
if ! lua5.4 -e "assert(loadfile('$f', 't'))" >/dev/null 2>&1; then
echo "::error file=$f::lua syntax error"
fail=1
fi
# 2. xpkg V1 baseline: the file has to populate `package = { ... }`
# with at least `spec`, `name`, and an `xpm` table. Form A vs
# Form B (mcpp = "<path>" / mcpp = { ... }) is descriptor-author
# choice and not enforced here.
for needle in 'spec *=' 'name *=' 'xpm *='; do
if ! grep -q "$needle" "$f"; then
echo "::error file=$f::missing required field ($needle)"
fail=1
fi
done
# 3. Package version identifiers and dependency versions should be
# bare versions ("1.2.3"), not upstream tag names ("v1.2.3").
# Download URLs may still contain refs/tags/v* when upstream
# uses that tag spelling.
if grep -nE '\["v[0-9]+|\["[^"]+"\][[:space:]]*=[[:space:]]*"v[0-9]+' "$f"; then
echo "::error file=$f::version identifiers must not use a leading v"
fail=1
fi
# 4. Mirror table sanity: when a download `url` is written as a
# { GLOBAL=..., CN=... } table, both regions must be present and
# the CN entry must point at the gitcode mcpp-res mirror.
if ! lua5.4 tests/check_mirror_urls.lua "$f"; then
fail=1
fi
# 5. c++fly admission policy (mcpp design 2026-07-14 §11-Q2, v1):
# c++fly means "toolchain's latest level + every experimental
# gate" — deliberately toolchain-dependent, so a published
# package built with it is not reproducible for consumers.
# Policy: WARN (never fail) and observe ecosystem usage before
# deciding whether to tighten. Two spellings: `language = ` is
# the descriptor's inline mcpp-segment key; `standard = ` covers
# mcpp.toml content embedded in heredoc/generated_files blocks.
if grep -nE '\b(language|standard)[[:space:]]*=[[:space:]]*"c\+\+fly"' "$f" >/dev/null; then
echo "::warning file=$f::declares C++ standard \"c++fly\" (experimental playground mode) — toolchain-dependent and non-reproducible for consumers; published packages should pin a concrete standard (c++23/c++26)"
fi
done
[ $fail -eq 0 ] && echo "All package files valid."
exit $fail
# ── Single-source-of-truth grammar check ─────────────────────────
# `mcpp xpkg parse` uses EXACTLY the resolver's parser, so what
# passes here is what builds for users of the pinned MCPP_VERSION.
# Strict by default: unknown mcpp-segment keys fail (they would be
# silently ignored at build time). This also mechanically enforces
# the rollout rule "floor first, new grammar after": descriptors
# needing a newer grammar cannot pass a lint pinned to an older mcpp.
- name: Download pinned mcpp
run: |
curl -L -fsS -o mcpp.tar.gz \
"https://github.com/mcpp-community/mcpp/releases/download/v${MCPP_VERSION}/mcpp-${MCPP_VERSION}-linux-x86_64.tar.gz"
tar -xzf mcpp.tar.gz
echo "MCPP=$PWD/mcpp-${MCPP_VERSION}-linux-x86_64/bin/mcpp" >> "$GITHUB_ENV"
- name: Parse descriptors with the resolver grammar (mcpp xpkg parse)
run: |
fail=0
for f in pkgs/*/*.lua; do
if ! "$MCPP" xpkg parse "$f" > /dev/null; then
echo "::error file=$f::mcpp xpkg parse failed (resolver grammar)"
fail=1
fi
done
[ $fail -eq 0 ] && echo "All descriptors parse with mcpp ${MCPP_VERSION}."
exit $fail
mirror-cn-reachable:
# Closed-loop guard for the CN mirror: every CN url referenced by a
# descriptor must be a live, downloadable gitcode release asset.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install lua
run: sudo apt-get install -y --no-install-recommends lua5.4
- name: Check CN mirror assets are reachable
run: |
fail=0
# collect unique CN urls across all descriptors
: > /tmp/cn.tsv
for f in pkgs/*/*.lua; do
lua5.4 tests/list_cn_urls.lua "$f" >> /tmp/cn.tsv || true
done
sort -u /tmp/cn.tsv -o /tmp/cn.tsv
total=$(grep -c . /tmp/cn.tsv || true)
echo "checking $total CN mirror url(s)"
while IFS=$'\t' read -r url sha; do
[ -z "$url" ] && continue
# follow redirects; gitcode release assets resolve to object storage
code=$(curl -fsSL -o /dev/null -w '%{http_code}' --retry 2 --max-time 60 "$url" || echo "000")
if [ "$code" != "200" ]; then
echo "::error::CN mirror unreachable ($code): $url"
fail=1
else
echo "ok: $url"
fi
done < /tmp/cn.tsv
[ $fail -eq 0 ] && echo "All CN mirror urls reachable."
exit $fail
# ── The whole test surface, as a mcpp workspace ───────────────────────
# mcpp-index is a mcpp [workspace]; every per-library test project under
# tests/examples/ is a member. `mcpp test --workspace` builds + runs each
# member's tests/ (behavioral assertions) on each OS — members self-gate by
# `[target.'cfg(...)']` (e.g. the X11/glfw stack is linux-only, openblas is
# windows-only), so one command covers the matrix with no shell driver.
# The ~/.mcpp/registry cache carries the built compat packages (xpkgs) across
# runs, so repeat builds are fast.
workspace:
name: workspace (${{ matrix.platform }})
runs-on: ${{ matrix.os }}
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
include:
# Archive names are derived from env.MCPP_VERSION in the Download
# step — bumping the pin is a ONE-line change (hardcoded versions
# here once 404'd a pin bump).
- platform: linux
os: ubuntu-latest
suffix: linux-x86_64
ext: tar.gz
mcpp: bin/mcpp
xlings: registry/bin/xlings
mcpp_version: "0.0.96" # keep in sync with env.MCPP_VERSION
- platform: macos
os: macos-15
suffix: macosx-arm64
ext: tar.gz
mcpp: bin/mcpp
xlings: registry/bin/xlings
mcpp_version: "0.0.96" # keep in sync with env.MCPP_VERSION
- platform: windows
os: windows-latest
suffix: windows-x86_64
ext: zip
mcpp: bin/mcpp.exe
xlings: registry/bin/xlings.exe
mcpp_version: "0.0.96" # keep in sync with env.MCPP_VERSION
env:
MCPP_EFFECTIVE: ${{ matrix.mcpp_version }}
steps:
- uses: actions/checkout@v4
- name: Restore mcpp registry cache
uses: actions/cache@v4
with:
# Holds toolchains AND the built compat packages (data/xpkgs), so a
# repeat `mcpp test` rebuilds little.
path: ~/.mcpp/registry
key: mcpp-registry-${{ runner.os }}-${{ env.MCPP_EFFECTIVE }}-${{ hashFiles('pkgs/**/*.lua', 'tests/**', '.github/workflows/validate.yml') }}
restore-keys: |
mcpp-registry-${{ runner.os }}-${{ env.MCPP_EFFECTIVE }}-
- name: Download mcpp
shell: bash
env:
MCPP_ARCHIVE: mcpp-${{ env.MCPP_EFFECTIVE }}-${{ matrix.suffix }}.${{ matrix.ext }}
MCPP_ROOT: mcpp-${{ env.MCPP_EFFECTIVE }}-${{ matrix.suffix }}
run: |
curl -L -fsS -o "$MCPP_ARCHIVE" \
"https://github.com/mcpp-community/mcpp/releases/download/v${MCPP_EFFECTIVE}/${MCPP_ARCHIVE}"
case "$MCPP_ARCHIVE" in
*.zip) powershell -NoProfile -Command "Expand-Archive -Force -Path '${MCPP_ARCHIVE}' -DestinationPath '.'" ;;
*) tar -xzf "$MCPP_ARCHIVE" ;;
esac
root="$PWD/$MCPP_ROOT"
mkdir -p "$HOME/.mcpp/registry"
cp -a "$root/registry/." "$HOME/.mcpp/registry/"
if [[ "$RUNNER_OS" == "Windows" ]]; then
echo "MCPP=$(cygpath -m "$root/${{ matrix.mcpp }}")" >> "$GITHUB_ENV"
echo "MCPP_VENDORED_XLINGS=$(cygpath -m "$root/${{ matrix.xlings }}")" >> "$GITHUB_ENV"
echo "$(cygpath -m "$root/bin")" >> "$GITHUB_PATH"
else
echo "MCPP=$root/${{ matrix.mcpp }}" >> "$GITHUB_ENV"
echo "MCPP_VENDORED_XLINGS=$root/${{ matrix.xlings }}" >> "$GITHUB_ENV"
echo "$root/bin" >> "$GITHUB_PATH"
fi
# compat.ffmpeg carries NASM .asm sources. No host install needed:
# mcpp >= 0.0.95 resolves nasm itself (PATH → sandbox → auto
# `xlings install nasm`), lazily, only when the plan has .asm units —
# same self-bootstrap contract as the llvm toolchain. The sandbox copy
# lands in ~/.mcpp/registry, so the cache carries it across runs.
# The refresh below is needed because release archives vendor an index
# snapshot and the bootstrap is offline-first: 0.0.96 shipped with a
# nasm.lua deployed engines can't resolve — it "installs" empty
# (mcpp#232; fixed in xim-pkgindex#398 after the release was cut).
# One `mcpp index update` (native wrapper over `xlings update` with
# the sandbox env + transient-network retry) lets the bootstrap see
# the fixed descriptor. Drop once a mcpp release vendors a post-#398
# index snapshot.

Check failure on line 223 in .github/workflows/validate.yml

View workflow run for this annotation

GitHub Actions / .github/workflows/validate.yml

Invalid workflow file

You have an error in your yaml syntax on line 223
- name: Refresh sandbox package index (nasm bootstrap, mcpp#232)
if: runner.os == 'Linux'
shell: bash
run: "$MCPP" index update
- name: mcpp test --workspace
shell: bash
env:
MCPP_INDEX_MIRROR: GLOBAL
run: |
"$MCPP" --version
# No `timeout` wrapper: absent on macOS runners; job-level timeout-minutes bounds it.
"$MCPP" test --workspace
- name: tinyhttps module package smoke
shell: bash
env:
MCPP_INDEX_MIRROR: GLOBAL
run: bash tests/smoke_tinyhttps_module.sh
# ── Exception: the public `imgui` C++23-module package (namespace "") ──
# Not yet a workspace member: its package has an empty namespace (the builtin
# default index), which a workspace member can't point at a local path the way
# namespaced compat.*/nlohmann packages can. Until mcpp can map the default
# namespace to a local index, this one keeps a thin driver that reseeds the
# default index from the repo. Tracked in the rearchitecture design doc.
imgui-module:
runs-on: ubuntu-latest
timeout-minutes: 60
env:
MCPP_EFFECTIVE: "0.0.96" # keep in sync with env.MCPP_VERSION
steps:
- uses: actions/checkout@v4
- name: Restore mcpp registry cache
uses: actions/cache@v4
with:
path: ~/.mcpp/registry
key: mcpp-registry-${{ runner.os }}-${{ env.MCPP_EFFECTIVE }}-${{ hashFiles('pkgs/**/*.lua', 'tests/**', '.github/workflows/validate.yml') }}
restore-keys: |
mcpp-registry-${{ runner.os }}-${{ env.MCPP_EFFECTIVE }}-
- name: Download mcpp
run: |
curl -L -fsS -o mcpp.tar.gz \
"https://github.com/mcpp-community/mcpp/releases/download/v${MCPP_VERSION}/mcpp-${MCPP_VERSION}-linux-x86_64.tar.gz"
tar -xzf mcpp.tar.gz
root="$PWD/mcpp-${MCPP_VERSION}-linux-x86_64"
mkdir -p "$HOME/.mcpp/registry"
cp -a "$root/registry/." "$HOME/.mcpp/registry/"
echo "MCPP=$root/bin/mcpp" >> "$GITHUB_ENV"
echo "MCPP_VENDORED_XLINGS=$root/registry/bin/xlings" >> "$GITHUB_ENV"
echo "$root/bin" >> "$GITHUB_PATH"
- name: imgui module package smoke
env:
MCPP_INDEX_MIRROR: GLOBAL
run: |
"$MCPP" --version
timeout 1800 bash tests/smoke_imgui_module.sh