I need to make a release
Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit 3b43bd86; unless a sentence says otherwise, nothing here was built or executed. Read at 3b43bd86 from CMakeLists.txt, cmake/Version.cmake, cmake/DependencyVersions.cmake, cmake/InstallCxx.cmake, docs/releasing.md, CHANGELOG.md, Doxyfile, README.md, the workflows and the local tag list; no step was executed. CNA has cut two tagged releases, v0.1.0-alpha.1 and v0.1.0.
This page is a checklist for a human maintainer cutting a CNA release, grounded in what the TARGET tree at 3b43bd86 actually contains: where the version lives, what is generated from it, what a release entry has to record, how the sibling checkouts are now required rather than only recorded, and which numbers are deliberately not the product version. It marks each step as automated by the repository, manual, or not established in the source. CNA has a documented tag-and-changelog procedure and no release automation; nothing here was run. The user-facing account of the tags is on Releases & versioning.
TARGET is not a release. The snapshot is 948 commits after v0.1.0 on the next branch, and its product version string is still 0.1.0 with an empty pre-release identifier, so CNA::getVersionString() reports exactly what the tag reports and CNA::isPreReleaseVersion() is false. The two trees are not interchangeable: the [Unreleased] section records a C++ ABI break since the tag. Identify a build by its Git commit. No newer tag exists.
Owner and read first
The version is decided in exactly one place, the root CMakeLists.txt: project(CNA VERSION 0.1.0 LANGUAGES CXX) supplies the numeric components (CMake's VERSION accepts numbers only) and set(CNA_VERSION_PRERELEASE "") supplies the pre-release identifier, empty on a final release, joined into CNA_VERSION_STRING. The pre-release identifier is deliberately a normal variable, not a cache entry, so an existing build directory cannot keep reporting the previous release after a bump. Read, in order:
docs/releasing.md: the procedure, marked “Current as of 0.1.0”; it is the only written process and this page follows it.cmake/Version.cmakeandVersion.hpp.in: how the single decision becomes<build>/generated/include/CNA/Version.hpp.cmake/DependencyVersions.cmake: the sibling versions a release requires and the configure-time check that enforces them.CHANGELOG.md: its format, the[Unreleased]section, the[0.1.0]entry as the current template, and the link definitions at the bottom.abi.h,ABI_VERSIONING.mdandRELEASE_GATE.md: the independent C ABI version and its own gate.
Four numbers that are not one number
| Identity | Value at TARGET | Where decided | Moves when |
|---|---|---|---|
| CNA product version | 0.1.0, unchanged since the tag | Root CMakeLists.txt | A release is cut, by hand |
| Native C ABI version | 0.50.0, experimental (the v0.1.0 tree carried 0.46.0) | modules/c-api/include/CNA/C/abi.h (three macros) | The ABI changes, independently of any product release; see Update the C API |
struct_version fields | Per structure | The C headers | One C structure changes |
| XNA API level | XNA 4.0, fixed | Not a CNA version | Never |
The siblings sharp-runtime, easy-gl and meta-gl and every external language binding have their own versions and cadence. The generated header exposes CNA_VERSION_MAJOR/MINOR/PATCH (preprocessor-testable literals), CNA_VERSION_PRERELEASE (without the leading -, empty on a final release), CNA_VERSION_STRING, and the constexpr functions CNA::getVersionMajor(), getVersionMinor(), getVersionPatch(), getVersionPreRelease(), getVersionString() and isPreReleaseVersion(). Consumers that derive from it: CMake consumers through CNA_VERSION, Apple bundles through CNA_APPLE_BUNDLE_VERSION (a cache entry defaulting to PROJECT_VERSION, the numeric components only, because a bundle version cannot carry a pre-release identifier), the configure banner (CNA: version …) and the Inspector's hello message (cnaVersion).
What a release promises, and what it does not
- Semantic Versioning, pre-1.0.
CHANGELOG.mdanddocs/releasing.mdstate that while the major version is 0, a minor bump may change the public API; pre-release identifiers runalpha.N,beta.N,rc.N, ordered as SemVer orders them, and0.1.0-alpha.1precedes0.1.0. - A release is a Git tag plus a changelog entry. There is no separate release branch and nothing is published to a package registry. The local tag list holds three tags:
v0.1.0-alpha.1(2026-08-20),v0.1.0(2026-10-10, an annotated tag “CNA 0.1.0” on the commitchore(release): prepare v0.1.0) and theaudit-2026-07-completemarker. No workflow triggers on tags or uploads a release artifact. Whether a GitHub Release object exists is not recorded in the source. - Uneven by design. The 0.1.0 entry's known limitations say pre-release quality remains: a minor release may change the public API, renderer coverage is uneven by design, the known per-renderer bugs are in
NEXT.md§5 (a file no longer in the tree at TARGET), and “Contenthas no general.xnbreader”. A release does not promise that every renderer, platform or binding works. The last clause is narrower than it reads: CNA registers a set of built-in XNB readers by canonical name (see XNB content), so copy the claim into new release notes only with that qualification (read fromCHANGELOG.mdat 3b43bd86; not executed). - No promise about the C ABI. The C ABI has its own gate, which reads “Not ready” at TARGET, and its
0.xminors have moved four times since the 0.1.0 tag (0.46.0 to 0.50.0; version history). Bindings are separate projects that each target one ABI generation and are not qualified against the snapshot (C API internals). - What is not established. The sources say nothing about a support window, a deprecation policy, signed or binary artifacts, or which CI workflows must pass before a tag. Do not add such claims to release notes.
Checklist
“Repo” means the repository automates or checks it; “manual” means a person edits or runs it; “not established” means the source does not say.
| # | Step | What to do and check | Status |
|---|---|---|---|
| 0 | Choose the branch | The 0.1.0 entry says the release was “cut from next”; its tag commit is contained in next, develop and main, and the clone's origin/HEAD names main. docs/releasing.md still shows git push origin develop in its push step. The snapshot is on next. Which branch carries the next tag is not written down beyond that | Not established |
| 1 | Choose the version | Edit project(CNA VERSION …) and/or CNA_VERSION_PRERELEASE (empty on a final release); no other file may hard-code the number | Manual |
| 2 | Update the hand-kept copies | Doxyfile PROJECT_NUMBER (currently 0.1.0) and CHANGELOG.md, as docs/releasing.md requires. The README's “Current release: 0.1.0” line is a third hand-kept copy that the procedure does not list | Manual |
| 3 | Write the changelog entry | Move [Unreleased] into ## [x.y.z] — YYYY-MM-DD and add the two link definitions at the bottom. At TARGET, [Unreleased] holds Changed (the link-footprint work and its C++ ABI break, CNA_ENABLE_GAMER_SERVICES and CNA_OPTIMIZE_FOR_SIZE, the removal of SDL3_image, linker-independent runtime ordering, the generated C API route boundaries, the renderer-classification renames), Removed (four renderer identities) and Fixed (C API and implicit-device fixes). Whether it covers all 948 commits since the tag is not established; check it against the commit range before renaming it | Manual; completeness not established |
| 4 | Require the sibling releases | sharp-runtime, easy-gl and meta-gl are sibling checkouts consumed by add_subdirectory, so a CNA tag does not select them. Release them first, check their tags out beside CNA, and set CNA_REQUIRED_SHARP_RUNTIME_VERSION, CNA_REQUIRED_EASYGL_VERSION and CNA_REQUIRED_METAGL_VERSION (0.1.0, 0.1.1 and 0.4.1 at TARGET). Configuration then refuses a checkout that declares another minor line, a pre-release of the required version, or no version at all, accepting the required version or a later patch release; -DCNA_CHECK_DEPENDENCY_VERSIONS=OFF downgrades the refusal to a warning. The check compares versions, not commits, so record the exact commits in the entry's Dependencies table. Everything else is pinned by submodule gitlinks and the GIT_TAG values in cmake/ThirdParty*.cmake and cmake/RendererSelection.cmake | Repo for the version check; manual for the sibling releases and the recorded commits |
| 5 | C ABI | Confirm abi.h matches the baseline and the release-gate record (version discipline); the gate verdict governs publishing an experimental C ABI and is independent of the product tag | Repo (build-free workflows on push); a publishing step is not established |
| 6 | Build and test | docs/releasing.md now names one tree, cmake-build-multi: OPENGLES3 as the default plus OPENGL33, WEBGPU, SDL_GPU, FNA3D, SOFTWARE, HEADLESS and STUB, the only tree that exercises all three siblings. Its tests run through tools/platform/run_gpu_tests_private.sh on a private display, and the configure output must show CNA: version <x.y.z> and one accepted-version line per sibling. Eighteen workflow files exist (16 trigger on pushes or pull requests to next, develop and main; the Windows content-pipeline workflow runs on pushes to its own branch or by hand, and the D3D Windows lane is manual). Which of them must be green before a tag is not established | Manual for the release run; repo for the CI it runs; CI requirement not established |
| 7 | Version tests | VersionTest.* in VersionTests.cpp are structural on purpose (components non-negative, string starts with the numeric components and ends with -<prerelease> exactly when there is one, no v prefix, macros and accessors agree), so a bump needs no test edit | Repo |
| 8 | Commit | Add the version-bearing files by explicit name (CMakeLists.txt, Doxyfile, CHANGELOG.md, and cmake/DependencyVersions.cmake when step 4 changed it), never git add -A | Manual |
| 9 | Tag | An annotated tag vX.Y.Z[-pre] with message CNA X.Y.Z[-pre]; the tag string has the v, the version string never does | Manual |
| 10 | Push | Only when the project owner asks; push the branch and the tag explicitly | Manual |
| 11 | Open the next cycle | Restore an empty ## [Unreleased] section if step 3 consumed it | Manual |
| 12 | Publish artifacts or release notes | No GitHub Release, package, binary or signature procedure exists in the source, and no workflow runs on a tag. A note in docs/devices-build.md still says the project has no release process to attach an artifact to; docs/releasing.md is the only written procedure | Not established |
| 13 | Downstream | Bindings, samples and this site pin CNA revisions independently; after a release, update the site's pinned snapshot (Update libcna.com) and expect each binding to need its own ABI migration | Manual; owned outside the CNA repository |
Anatomy of a changelog entry
The format follows Keep a Changelog. Two release entries exist in CHANGELOG.md: ## [0.1.0-alpha.1] — 2026-08-20, which named a state of develop rather than a delta, and ## [0.1.0] — 2026-10-10, the first delta entry and the template to follow. The 0.1.0 entry has these parts, in order:
- A short statement of what the entry describes: where it was cut from and that its lists are the changes since the previous tag.
- Dependencies: a table of the three siblings with the required version and the tag and commit each was verified against, then a pointer to the submodule gitlinks and
GIT_TAGvalues that pin everything else. - Added, Removed, Changed and Fixed, with concrete surface, the reason for each decision, and links to the documents that define them.
- Known limitations, stated as limits rather than as a roadmap, including the tests that failed in the release tree before the release and are carried rather than fixed.
- Verification: the configuration that was built, its build and test counts, every failure with its explanation, and the toolchain and sibling tags it ran against.
- Link definitions at the bottom:
[Unreleased]as a compare against the latest tag, the release as a compare against the previous tag, andreleases/tagfor the first one, all underlibcna/cna.
The [Unreleased] section uses Changed, Removed and Fixed at TARGET. Several of its entries are written as records of why a decision was made (for instance the renderer retirement with its permanently reserved C ABI values, or the C++ ABI break with the overrides an IPlatform implementation must rename), which is the level of evidence a release entry should keep.
Verify the tagged tree
None of these were run for this page. They are the checks the sources imply once a tag exists:
git describe --tags # the tag name, or tag-N-g<hash> after it
git rev-parse 'vX.Y.Z^{commit}' # the commit the tag names
cmake -S . -B <build> ... # the banner prints "CNA: version X.Y.Z[-pre]"
# and one accepted-version line per sibling
grep CNA_VERSION_STRING <build>/generated/include/CNA/Version.hpp
Then run the structural VersionTest.* cases through the core test binary and, if the C API is part of the claim, build it in a separate CNA_BUILD_C_API=ON tree and run ctest -R '^CApi'. Both installed CMake packages named CNA, the C API's and the C++ SDK's (CNA_INSTALL_CXX), take their package version from abi.h, not from the product version, so find_package(CNA 0.1 CONFIG) compares against the C ABI (0.50.0 at TARGET) and a request written as “0.1” is not a statement about the product release.
Traps
- Two trees, one string. Until step 1 is done, every snapshot after the tag reports
0.1.0, including trees whose C++ ABI no longer matches the release. Recordgit rev-parse HEADin bug reports and evidence, not the version. - Numbers you cannot bump together. Raising the product version does not touch the ABI version, and a C ABI step does not need a product release. Do not describe either as implying the other.
- A sibling the check does not pin. The configure-time check accepts any later patch release of the required minor line and compares versions, not commits, and
CNA_CHECK_DEPENDENCY_VERSIONS=OFFturns it into a warning. A reproducible release still needs the exact sibling commits in its Dependencies table. - Pointing links at a moving branch. A branch head moves past the tag; release notes and documentation should link a tag or a commit permalink.
- Pre-release identifiers and Apple. A bundle version cannot contain a pre-release identifier such as
-beta.1, which is whyCNA_APPLE_BUNDLE_VERSIONdefaults to the numeric components. - A reconstructed changelog is a claim. State counts and capabilities from the tree at the tag, not from the previous entry; see Known uncertainty and history.
Review checklist
- One source of truth edited; Doxyfile, changelog and README copies agree with it; the configure banner prints the intended string.
- The changelog entry describes the changes since the previous tag, carries the Dependencies table with the sibling versions and commits, and mentions the C ABI version and any ABI step since the previous release.
- The Verification section names the configuration, the test counts and every failure; steps marked not established are stated as such in the release notes rather than implied.
- The tag is annotated, on the intended branch, and pushed explicitly, with owner approval.
- Bindings, samples and the documentation pin are told which revision to expect.
Related: Maintenance and pin policy, Blast radius and readiness and Change build configuration.
Deep dives on this topic
Long-form pages that explain the exact semantics, invariants and evidence behind this subject.
- Project records as evidence: plans, ledgers, versions, handoffs and re-audits — How to use and write CNA's plans, handoffs, audits and gates as evidence: roles, stable task IDs, executable ledgers, version coordinates, defect-ledger rules and documentation re-audits.
Related pages
The same subsystem is explained at four altitudes. These are the neighbouring pages at each one.
- User guide
- Releases & versioning · Semantic Versioning before 1.0 · Experimental C API: version history
- Architecture
- C API and bindings architecture
- Internals
- C API internals
- Maintainer workflow
- Maintenance & pin policy · Known uncertainty and history · Update the C API
- Tests and validation
- Test architecture
- Reference
- CMake option index