mirror.yml Reference¶
mirror.yml describes one tool to mirror — where to fetch upstream releases, which platforms to build for, how to test each bundle, and how to report results. The file is consumed by ocx-mirror package sync, ocx-mirror package check, and all ocx-mirror package pipeline subcommands.
Top-level keys¶
| Key | Type | Required | Purpose |
|---|---|---|---|
name |
string | Yes | Tool name, used in log output and notify messages |
target |
object | Yes | OCI registry and repository to push to |
source |
object | Yes | Upstream release source (GitHub Releases or URL index) |
assets |
object | Yes | Platform → regex list mapping for selecting upstream release archives |
asset_type |
string | No | Archive (default) or Binary |
build_timestamp |
string | No | Per-build tag suffix: datetime (default), date, or none. See build_timestamp & GC-safe publishing. |
cascade |
boolean | No | Cascade rolling tags on push (true by default). See build_timestamp & GC-safe publishing. |
versions |
object | No | Version filter (min/max bounds, new_per_run, backfill order) |
verify |
object | No | Checksum verification options |
concurrency |
object | No | Parallel download and push limits |
tests |
array | No* | Commands to run against each installed bundle. Required when pipeline generate ci is used. |
platforms |
object | No* | GHA runner and container matrix. Required when pipeline generate ci is used. |
ocx_mirror |
object | No* | ocx-mirror version pin for generated workflows. Required when any Linux platform declares containers. |
notify |
object | No | Discord webhook notification settings |
The tests, platforms, ocx_mirror, and notify keys are used only by ocx-mirror package pipeline subcommands. sync and check ignore them.
build_timestamp & GC-safe publishing¶
build_timestamp controls the tag a mirrored version is published under. Each (version, platform) push writes a primary tag for that version; with cascade: true (the default) it also re-points the rolling tags X.Y, X, and latest to the newest build.
| Value | Primary tag for 3.28.0 |
Effect |
|---|---|---|
datetime (default) |
3.28.0_20260310142359 |
Unique per build (UTC YYYYMMDDHHMMSS). Never re-pointed. |
date |
3.28.0_20260310 |
Unique per build-day (UTC YYYYMMDD). |
none |
3.28.0 |
Bare version tag. Re-published in place on every rebuild. |
Pre-releases keep their identifier: 3.28.0-rc1 → 3.28.0-rc1_20260310142359. A version that already carries a build suffix is rejected rather than double-stamped.
The garbage-collection hazard of build_timestamp: none
A digest is immutable, but a tag is not. Re-publishing a version under build_timestamp: none — or moving a rolling cascade tag to a newer build — re-points the tag and leaves the previous digest untagged. Once untagged, registry garbage collection can reap it, breaking any consumer ocx.lock pinned to that @sha256: digest. "Digests are immutable" only holds until GC runs.
With datetime or date, every build also lands under its own unique X.Y.Z_<ts> tag that is never re-pointed, so the digest stays permanently reachable even as the rolling cascade tags float. This is the GC-safe choice. Trade-off: storage grows with every build, and the version tag is no longer bare.
Choosing a value:
datetime(default) — GC-safe, no registry configuration required. Recommended for any mirror whose packages are pinned by digest downstream.date— GC-safe across days with coarser tags. Caveat: a second build on the same UTC day re-points that day's tag, orphaning the earlier same-day digest — the within-day hazard remains.none— bare tags only. Use exclusively when the target registry protects referenced digests from GC: a retention policy that keeps untagged manifests still referenced by consumers, an OCI referrers/lock guard, or a guarantee that a version is never re-published (eachX.Y.Ztreated as immutable upstream).
ocx-mirror emits a parse-time warning when build_timestamp: none is combined with cascade, so the hazard surfaces on every validate, check, sync, and pipeline run. It is advisory, not fatal — a registry with retention configured can use none safely.
tests¶
Declares the smoke-test commands to run against each installed bundle. Every entry runs for every (version, platform, container) combination in the matrix.
tests:
- name: version
command: cmake --version
- name: smoke
command: bash ./tests/smoke.sh
Rules:
- Required: must contain at least one entry when used with
pipeline generate ci. namemust be unique within the file and must match^[a-zA-Z][a-zA-Z0-9_-]*$. The name appears as the JUnit test-case name, so it must be stable across runs.commandis a single-line string. Multi-line scripts must be files in the mirror repository and invoked via shell (bash ./tests/smoke.sh,pwsh -File ./tests/smoke.ps1).- No
scriptfield or auto-detection — command-only by design.
Environment exposed to every test command:
| Variable | Value |
|---|---|
OCX_INSTALL_DIR |
Path where ocx package test materialized the package |
OCX_VERSION |
Mirrored version string (e.g., 3.29.0) |
OCX_PLATFORM |
Platform slug (e.g., linux/amd64) |
OCX_IMAGE |
Container image; empty on native legs |
OCX_TEST_NAME |
The tests[].name value for this invocation |
platforms¶
Declares the GHA runner and container matrix for the generated workflow. Each key is a platform slug in <os>/<arch> form.
Container legs are currently rejected by the renderer
pipeline generate ci is native-only after the setup-ocx migration: a spec that declares containers: is rejected with exit 64. The containers fields below remain part of the spec schema but cannot be used with the current renderer.
platforms:
linux/amd64:
runner: ubuntu-latest
containers:
- { image: "ubuntu:24.04", shell: bash }
- { image: "alpine:3.20", shell: sh }
- { image: "fedora:40", shell: bash }
linux/arm64:
runner: ubuntu-24.04-arm
containers:
- { image: "ubuntu:24.04", shell: bash }
- { image: "alpine:3.20", shell: sh }
darwin/arm64:
runner: macos-latest
darwin/amd64:
runner: macos-latest
prefix: ["arch", "-x86_64"]
windows/amd64:
runner: windows-latest
shell: pwsh
tests:
- name: version
command: cmake.exe --version
- name: smoke
command: pwsh -File ./tests/smoke.ps1
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
runner |
string | Yes | GitHub Actions runner label |
containers |
array | No | Container matrix entries. Absent = native mode. Must have ≥1 entry when present. |
containers[].image |
string | Yes | Valid OCI image reference (e.g. ubuntu:24.04) |
containers[].shell |
string | No* | Shell to invoke inside the container. *Required when image name does not match a known default (see below). |
shell |
string | No | Default shell for native legs. Defaults: pwsh on Windows, bash elsewhere. |
prefix |
array of strings | No | Command prefix applied before every test invocation. Defaults: ["arch", "-x86_64"] on darwin/amd64 with a macos-* runner; empty otherwise. |
tests |
array | No | Per-platform test override. When present, replaces the top-level tests: array entirely (no partial merge). |
min_version |
string | No | Inclusive lower bound: the first upstream version this platform applies to. See Version applicability. |
max_version |
string | No | Exclusive upper bound: the first upstream version this platform no longer applies to. |
exclude |
array | No | Individual (version[, range]) holes within the window. See Version applicability. |
Platform key validation:
- Must match
^[a-z0-9_-]+/[a-z0-9_-]+$.
Version applicability¶
Not every platform applies to every release. A platform may be introduced late upstream (its first binary ships at some 0.11.7), dropped at a later release (the upstream stops shipping that OS/arch), or carry a known-broken build for one specific version. Without a per-platform lever, the only knob is the global versions.min/max, which moves the window for all platforms at once — so a single broken (version, platform) either reds the run forever or forces a global version bump that strands the other platforms.
min_version, max_version, and exclude constrain which versions a platform applies to. A (version, platform) pair outside a platform's window — or matched by an exclude entry — is never resolved, scheduled, built, tested, or pushed, and never reds the run. This supersedes the old workaround of bumping the global versions.min to dodge a late-added or dropped platform.
platforms:
windows/arm64:
runner: windows-11-arm
shell: pwsh
min_version: "0.11.7" # platform's first upstream release (inclusive)
exclude:
- version: "0.16.0" # one known-broken release
reason: "aarch64-windows build-exe segfault"
severity: broken # 🔒 row in the Discord report (default)
darwin/amd64:
runner: macos-14
max_version: "11.1.0" # dropped upstream at 11.1.0 (exclusive)
exclude:
- max_version: "9.4.0" # never built anything below 9.4.0
severity: skip # silent — no 🔒 row
exclude entry fields:
| Field | Type | Required | Description |
|---|---|---|---|
version |
string | One of version / range |
Exclude exactly this version. Mutually exclusive with min_version/max_version. |
min_version |
string | One of version / range |
Inclusive lower bound of an excluded range. |
max_version |
string | One of version / range |
Exclusive upper bound of an excluded range. A range may set either bound alone (open-ended). |
reason |
string | No | Surfaced in the 🔒 row for broken excludes. |
severity |
broken | skip |
No | broken (default) drops the pair and surfaces a 🔒 row (plus reason); skip drops it silently. |
Semantics:
min_versionis inclusive,max_versionis exclusive — the same convention as the top-levelversionsbounds.- An
excludeentry must set either a singleversionor amin_version/max_versionrange, not both. - To re-enable a previously-excluded pair, delete the entry — the next clean run backfills it.
- Validation rejects unparseable bounds and conflicting
excludeshapes with exit code 65 (DataError).
Container shell defaults:
alpine*→shubuntu*,debian*,fedora*,rocky*,opensuse*→bash- Any other image:
shellis required.
ocx_mirror¶
Pins the ocx-mirror version used in generated workflow jobs (discover, prepare, push, notify).
ocx_mirror:
release_tag: v0.7.2
rev: abc123def0123456789012345678901234567890
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
release_tag |
string | Yes (when any Linux platform has containers) | ocx-mirror release tag. Used for musl-static artifact download on Linux container legs. Must match ^v\d+\.\d+\.\d+(-[a-z0-9.]+)?$. |
rev |
string | No | Full 40-character git SHA. When set, takes precedence over release_tag for cargo install paths. When both present, release_tag is still used for musl artifact download. Must match ^[0-9a-f]{40}$. |
When all Linux platforms are container-less (native-only mirror), release_tag is optional and rev alone is sufficient.
How ocx-mirror is installed in CI
Generated jobs install the toolchain via the ocx-sh/setup-ocx action, which activates the mirror repository's project toolchain (ocx.toml) onto PATH — ocx-mirror and ocx both come from there.
notify¶
Configures Discord webhook notifications. The webhook fires after the push job completes.
notify:
discord:
webhook_secret: DISCORD_WEBHOOK_URL
user_id: "123456789012345678"
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
discord.webhook_secret |
string | Yes (when notify: is present) |
Name of a GitHub Actions secret whose value is the Discord webhook URL. Must match ^[A-Z][A-Z0-9_]+$. |
discord.user_id |
string | No | Discord user ID (snowflake) to mention on failures. Non-secret — inlined into the workflow as OCX_MIRROR_DISCORD_USER_ID. Must match ^[0-9]{17,20}$. |
Validation:
webhook_secretmust be a secret name, not a URL. Values containingdiscord.com,discordapp.com, or matching^https?://are rejected at parse time with exit code 64 (UsageError). This prevents accidental commit of a live webhook URL into the repository.user_idmust be the numeric snowflake. A URL or@mentionpaste is rejected with exit code 64 (UsageError); any other malformed value is rejected with exit code 65 (DataError).
Messages:
The report posts one Discord message per published version — a single embed each (so a release-heavy run never trips Discord's 1024-character field cap, and each release reads as its own notification). Consecutive messages are paced and a 429 Too Many Requests is retried per Discord's retry_after, so a large backfill stays under the webhook rate limit. Each embed lists that version's platforms with a status chip:
| Chip | Meaning |
|---|---|
| 🟢 | Pushed |
| 🔴 | Test or push failure |
| 🚫 | Expected artifact never arrived (missing bundle / JUnit) |
| 🔒 | Deliberately excluded for this version (a broken exclude entry), with its reason |
When user_id is set, any message that carries a partial or failed version is prefixed with an in-message <@id> mention — scoped to that one user, so @everyone and role pings never fire. Messages with only successful versions never ping.
Notification conditions:
| Condition | Action |
|---|---|
| All versions already existed in the registry, no failures | Silent (no POST sent) |
| New versions published, no failures | Green per-version embeds with published platforms; no mention |
| New versions published, some platforms failed | Yellow/red embeds for the affected versions; mention if user_id set |
| No new versions published, all platforms failed | Red embeds with failure details and run URL; mention if user_id set |
Spec inheritance¶
mirror.yml files support an extends: key for shallow merge from a parent spec. Child keys override parent keys at the top level. This is useful for sharing source and assets across variants of the same tool.
extends: ./base-cmake.yml
target:
registry: private.registry.example.com
repository: internal/cmake
Example: complete spec¶
name: cmake
target:
registry: ocx.sh
repository: cmake
source:
type: github_release
owner: Kitware
repo: CMake
tag_pattern: "^v(?P<version>\\d+\\.\\d+\\.\\d+)$"
assets:
linux/amd64:
- "cmake-.*-linux-x86_64\\.tar\\.gz$"
darwin/arm64:
- "cmake-.*-macos-universal\\.tar\\.gz$"
windows/amd64:
- "cmake-.*-windows-x86_64\\.zip$"
cascade: true
tests:
- name: version
command: cmake --version
- name: ctest
command: ctest --version
platforms:
linux/amd64:
runner: ubuntu-latest
darwin/arm64:
runner: macos-latest
windows/amd64:
runner: windows-latest
shell: pwsh
min_version: "3.20.0" # cmake windows/amd64 mirrored from 3.20 on
exclude:
- version: "3.27.0"
reason: "windows zip repacked upstream"
severity: broken
tests:
- name: version
command: cmake.exe --version
ocx_mirror:
release_tag: v0.7.2
notify:
discord:
webhook_secret: DISCORD_WEBHOOK_URL
user_id: "123456789012345678"