Skip to content

feat(pkgs): add public opencv module package 0.0.2 (Form A, linux) (#77) #286

feat(pkgs): add public opencv module package 0.0.2 (Form A, linux) (#77)

feat(pkgs): add public opencv module package 0.0.2 (Form A, linux) (#77) #286

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:
# Full history: the member-selection step below diffs against the PR
# base to decide which workspace members to test.
- uses: actions/checkout@v4
with:
fetch-depth: 0
- 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.
- name: Refresh sandbox package index (nasm bootstrap, mcpp#232)
if: runner.os == 'Linux'
shell: bash
run: '"$MCPP" index update'
# ── Selective member testing ──────────────────────────────────────
# `mcpp test --workspace` builds every member (opencv, ffmpeg, …) and
# dominates CI wall-clock, while a PR almost always touches one
# package. Map changed files → affected members and test only those:
# pkgs/<x>/<lib>.lua → members whose mcpp.toml references <lib>
# tests/examples/<m>/** → member <m>
# Run the FULL workspace when the change can affect everything:
# non-PR events (push to main, the nightly cron, dispatch), this
# workflow file (it carries the mcpp version pins, so a version bump
# always re-validates every package), the workspace manifest, or
# shared test scripts. Docs-only changes select nothing.
# Note: bash 3.2 on macOS runners — no associative arrays here.
- name: Select affected workspace members
shell: bash
run: |
full() { echo "MEMBERS=__ALL__" >> "$GITHUB_ENV"; echo "full run: $1"; exit 0; }
[ "${{ github.event_name }}" = "pull_request" ] || full "event=${{ github.event_name }}"
base="origin/${{ github.base_ref }}"
changed=$(git diff --name-only "$base"...HEAD)
printf 'changed files vs %s:\n%s\n' "$base" "$changed"
sel=""
add() { case " $sel " in *" $1 "*) ;; *) sel="$sel $1" ;; esac; }
while IFS= read -r f; do
[ -n "$f" ] || continue
case "$f" in
.github/workflows/validate.yml|tests/*.sh|tools/*) full "$f" ;;
mcpp.toml)
# Workspace manifest. Every new-package PR appends to the
# members list, so that alone must NOT force a full run:
# select the added members; anything else in this file
# (indices, settings) affects everyone → full.
if ! diff -q <(git show "$base:mcpp.toml" | grep -v 'tests/examples/') \
<(grep -v 'tests/examples/' mcpp.toml) >/dev/null; then
full "mcpp.toml non-member change"
fi
for p in $(comm -13 <(git show "$base:mcpp.toml" | grep -o 'tests/examples/[A-Za-z0-9._-]*' | sort -u) \
<(grep -o 'tests/examples/[A-Za-z0-9._-]*' mcpp.toml | sort -u)); do
add "${p#tests/examples/}"
done ;;
tests/examples/*)
m=${f#tests/examples/}; m=${m%%/*}
# A deleted/renamed member dir implies a mcpp.toml edit,
# which already forces a full run above.
[ -d "tests/examples/$m" ] && add "$m" ;;
pkgs/*.lua|pkgs/*/*.lua)
lib=$(basename "$f" .lua); lib=${lib#compat.}
hit=0
for mt in tests/examples/*/mcpp.toml; do
if grep -q "$lib" "$mt"; then add "$(basename "$(dirname "$mt")")"; hit=1; fi
done
[ "$hit" = 1 ] || echo "note: no workspace member exercises $f" ;;
*.md|docs/*|.agents/*|.github/*) : ;;
*) full "unclassified change: $f" ;;
esac
done <<EOF
$changed
EOF
sel=${sel# }
echo "MEMBERS=$sel" >> "$GITHUB_ENV"
echo "selected members: ${sel:-<none>}"
- name: mcpp test (workspace or affected members)
shell: bash
env:
MCPP_INDEX_MIRROR: GLOBAL
run: |
"$MCPP" --version
# No `timeout` wrapper: absent on macOS runners; job-level timeout-minutes bounds it.
if [ "$MEMBERS" = "__ALL__" ]; then
"$MCPP" test --workspace
elif [ -z "$MEMBERS" ]; then
echo "No workspace member affected by this change — nothing to test."
else
rc=0
for m in $MEMBERS; do
echo "::group::mcpp test -p $m"
"$MCPP" test -p "$m" || rc=1
echo "::endgroup::"
done
exit $rc
fi
- 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
# ── Exception: the public `ffmpeg` C++23-module package (namespace "") ──
# Same default-namespace limitation as imgui-module above — validated via a
# reseeding smoke driver instead of a workspace member. Linux-only: its
# compat.ffmpeg dependency ships a linux-x86_64 configure snapshot (macOS is
# blocked on mcpp#229 dependency cfg-conditional sources).
ffmpeg-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: ffmpeg module package smoke
env:
MCPP_INDEX_MIRROR: GLOBAL
run: |
"$MCPP" --version
timeout 2700 bash tests/smoke_ffmpeg_module.sh
# ── Exception: the public `opencv` C++23-module package (namespace "") ──
# Same default-namespace limitation as imgui-module above — validated via a
# reseeding smoke driver instead of a workspace member. Linux-only: its
# compat.opencv5 dependency ships a linux-x86_64 config snapshot (macOS is
# blocked on mcpp#229 dependency cfg-conditional sources).
opencv-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: opencv module package smoke
env:
MCPP_INDEX_MIRROR: GLOBAL
run: |
"$MCPP" --version
timeout 2700 bash tests/smoke_opencv_module.sh