mirror of
https://github.com/logos-co/logos-package.git
synced 2026-08-27 10:11:10 +00:00
* feat: one semver implementation for the packaging stack
Version handling was duplicated across the packaging repos and the copies
disagreed with each other and with the spec. This makes logos-package the
single home for it.
Precedence is defined by SemVer 2.0.0, so it is delegated to a vendored
library (z4kn4fein/cpp-semver v0.4.0, MIT, single header, C++17):
- numeric pre-release identifiers compare NUMERICALLY, so 1.0.0-rc.2 <
1.0.0-rc.11. Every previous copy got this wrong -- the downloader
compared the whole pre-release tag as one ASCII string, while lgpm and
the package-manager UI dropped the tag entirely (atoi("0-rc1") == 0),
so a pre-release compared EQUAL to its own release.
- build metadata is ignored for precedence (spec 10).
Ranges (^ ~ x * || >= <= > < =) are NOT in the semver spec -- they are an
npm convention the manifests already use -- so that layer is written once,
in include/logos/semver.hpp, on top of the library. It adopts npm's
pre-release rule: a range never matches a pre-release unless the range
itself names one at the same major.minor.patch. Without it `^1.0.0` matches
`2.0.0-alpha`, i.e. an unreleased alpha of the next major satisfies a caret
range on 1.x and can be resolved as a dependency.
Exposed three ways so every consumer reaches the same code:
- include/logos/semver.hpp, header-only and dependency-free (the
package-manager UI is a QML plugin with no native link deps and must be
able to include it without dragging in zlib/ICU/libsodium).
- the lgx_semver_* C ABI.
- `lgx semver compare|sort|satisfies|valid|valid-range`, for the catalog
builder (logos-modules-release-tool's index.py), which is stdlib-only
but already requires lgx -- so it can order versions without growing a
second implementation in Python that could drift.
Manifest range validation now delegates here too; it was a separate regex
that accepted ranges the resolver could not actually evaluate.
Validation is strict but comparison is lenient (parse vs parse_lenient):
real manifests carry partial versions like "1.0", and treating those as
invalid at compare time would sort them below every real version and
silently reorder existing catalogs.
tests/test_semver.cpp covers the spec's own precedence chain verbatim, the
pre-release cases that were previously untested everywhere, and the npm
range rules.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* ci: print full nix build logs
Without -L a failing build is truncated to its last 25 lines, which hides
the first compiler error.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix: include <cstdint> before cpp-semver (Linux/libstdc++ build)
cpp-semver 0.4.0 uses uint64_t throughout but never includes <cstdint>
itself. libc++ drags it in transitively via <string>/<regex>, so this built
on macOS -- but libstdc++ stopped doing that in GCC 13, and on Linux the
header failed to parse with "'uint64_t' does not name a type". That
collapsed semver::version, which surfaced as a wall of bogus "has no member
named 'major'" errors pointing at our own header rather than the real cause.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat: add a headers-only package output
For consumers that need the shared semver implementation
(include/logos/semver.hpp) but must NOT link liblgx.
The package-manager UI is a Qt plugin, and the module builder copies every
*.so/*.dylib an external library ships into the plugin's output lib/.
ui-host then scans that directory and tries to load each file as a Qt
plugin: pointing it at the `lib` output put liblgx.dylib there, ui-host
failed with "is not a Qt plugin", and the entire UI never rendered.
Shipping no library at all is what makes `headers` safe -- there is nothing
to copy.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix: reject silently-widening ranges; install cpp-semver header via FetchContent
Addresses Copilot review on #30.
valid_range accepted '1.x.3', '1..2' and 'x.1', silently widening them to
'1.x' / '1' / '*' — a range claiming more than it means, which breaks the
syntax-validation contract. parse_partial now requires every component after
the first wildcard/empty one to also be a wildcard/empty; trailing wildcards
('1.2.x', '1.x') stay valid. Tests added.
The FetchContent path forced SEMVER_INSTALL off, so a downstream
'cmake --install' would ship logos/semver.hpp without the <semver/semver.hpp>
it includes. Install the fetched header alongside ours. (The find_package /
Nix path already vendors it.)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix: reject a non-SemVer package version at validate/verify time
Manifest::validate() only checked that 'version' was non-empty, never that it
was a valid version. So a package versioned '0.1.2.3' (four sections), 'v1.0.0'
or '1.0' passed 'lgx verify', got published, and only misbehaved later: it's
unparseable to the comparators, so it sorts BELOW every valid version (can
never be 'latest') and orders against other junk by raw byte comparison
(0.1.2.10 < 0.1.2.9).
validate() now requires a full SemVer 2.0.0 version via the shared
logos::semver::valid(), so 'lgx verify' fails loudly at build/publish time
instead. Confirmed end-to-end: a package with version 0.1.2.3 now fails with
"'version' is not a valid SemVer 2.0.0 version".
Every real module in the workspace already uses X.Y.Z, so nothing is broken.
This closes the gap the comparison layer only worked around: comparison stays
lenient (it must tolerate whatever is already in old catalogs), but validation
gates what new packages may ship.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>