Skip to content

chore: unify semver handling across the packaging stack - #93

Closed
dlipicar wants to merge 9 commits into
masterfrom
chore/unify-semver
Closed

chore: unify semver handling across the packaging stack#93
dlipicar wants to merge 9 commits into
masterfrom
chore/unify-semver

Conversation

@dlipicar

Copy link
Copy Markdown
Contributor

Entry point for a 6-PR chain. Feed this to /merge-chain.

Why

Version handling was duplicated across the packaging repos and the copies disagreed — with each other and with the spec. Pre-release ordering (-alpha / -beta / -rcN) was reported as broken. It was, but the survey found six divergent implementations, and the worst one wasn't a duplicate sort at all: the catalog was never ordered by semver.

index.py sorted each package's versions[] by releasedAt timestamp, and every client reads versions[0] as "latest":

BEFORE (by releasedAt)              AFTER (by semver precedence)
[0] 2.0.0-alpha  <- "latest"        [0] 2.0.0        <- "latest"
[1] 1.2.1                           [1] 2.0.0-rc.11
[2] 2.0.0                           [2] 2.0.0-rc.2
[3] 2.0.0-rc.11                     [3] 2.0.0-alpha
[4] 2.0.0-rc.2                      [4] 1.2.1

So publishing 2.0.0-alpha after 1.9.0 advertised an unreleased alpha to every user as the newest release. A backport cut after a higher version did the same, as did a forced republish.

Three more real bugs, each proven by replicating the old semantics exactly:

Bug Where
1.0.0-rc.2 ranked above 1.0.0-rc.11 (pre-release tag compared as one ASCII string; lgpm and the UI dropped it entirely — atoi("0-rc1") == 0) all five copies
^1.0.0 matched 2.0.0-alpha — an unreleased alpha of the next major satisfying a caret range on 1.x, resolvable as a dependency downloader
dependency resolution and the catalog read-time re-sort both ranked by releasedAt, so a late-published backport beat a higher version downloader
lgpm install --dir sorted .lgx filenames, so 1.9.0 was installed last and clobbered 1.10.0 package-manager

The design

Split the problem the way the specs do.

Precedence is specified → delegated to a vendored library, z4kn4fein/cpp-semver v0.4.0 (MIT, single header, C++17, zero deps).

Ranges are not in the semver spec^ ~ x * || are an npm convention the manifests already use — so that layer is written once, in logos-package/include/logos/semver.hpp, on top of the library. It adopts npm's pre-release rule, which is what stops ^1.0.0 resolving to 2.0.0-alpha.

Two findings worth recording, because both contradict their own documentation:

  • neargye-semver — the only semver library in nixpkgs — is not actually SemVer 2.0.0 compliant. Its pre-release is an enum {alpha,beta,rc} + uint16: it cannot represent 1.0.0-alpha.beta or build metadata, and majors cap at 65535. Hence vendoring.
  • cpp-semver has no range support at all (the author's Python/Kotlin ports do; the C++ one doesn't).

index.py shells out to a new lgx semver subcommand rather than growing a second implementation in Python — it's stdlib-only but already required lgx for exactly the two subcommands that sort. So this is genuinely one implementation across both languages, and the catalog cannot drift from the clients again.

Merge order

# PR Notes
1 logos-co/logos-package#30 root — the shared implementation
2 logos-co/logos-package-manager#25 ┐ parallel
2 logos-co/logos-package-downloader#17 ┘ parallel
3 logos-co/logos-package-manager-ui#51
4 logos-co/logos-modules-release-tool#4
5 this re-pin

Every downstream flake.lock is pinned at #30's branch head so its CI can build; each must be re-pinned to master as it merges. logos-modules-release-tool#4's CI builds lgx from logos-package master, so it stays red until #30 lands and then goes green on its own.

This PR

  • Registers the release/catalog pipeline repos as workspace submodules. They're non-flake (index.py is a stdlib Python script; -action and -base are Actions YAML and bash), so they join as plain submodules and stay out of the flake dep graph. Only -tool takes a code change.
  • package-manager-ui gains a logos-package input — it's the one repo in the chain that needed a new dependency edge — so the workspace follows it, keeping a single logos-package (and a single nixpkgs) across the graph. It takes headers only: no liblgx, ICU, libsodium or zlib enters the plugin (otool -L verified).

⚠️ dep-graph.nix is edited by hand here, deliberately. ws sync-graph regenerates from local submodule state, and in this workspace that reverted logos-protocol to an older rev and deleted logos-delivery-demo plus all seven logos-evm-* entries. Only the package-manager-ui edge actually changed, so that one line is applied directly.

Verification

ws test logos-package logos-package-manager logos-package-downloader --auto-local   # 3 passed
ws build logos-package-manager-ui logos-package-manager-module --auto-local          # OK
python3 -m unittest        # 9 pass, driving the real lgx binary

The release-tool tests were run against the old implementation too: 4 of 7 fail, so they're genuine regression tests rather than tautologies.

🤖 Generated with Claude Code

Ties together the six-PR chain that replaces five divergent semver
implementations -- and a catalog that was not sorted by semver at all --
with a single one, living in logos-package.

  logos-package#30           the shared implementation (root)
  logos-package-manager#25   \ both consume it
  logos-package-downloader#17/
  logos-package-manager-ui#51 header-only, via the flake's preConfigure
  logos-modules-release-tool#4 catalog ordering, via `lgx semver`

Registers the release/catalog pipeline repos as workspace submodules. They
are non-flake (index.py is a stdlib Python script; -action and -base are
GitHub Actions YAML and bash), so they join as plain submodules and stay out
of the flake dep graph. Only -tool takes a code change.

package-manager-ui gains a logos-package input -- it is the one repo in the
chain that needed a new dependency edge -- so the workspace follows it to
keep a single logos-package (and a single nixpkgs) across the graph.

dep-graph.nix is edited by hand rather than regenerated: `ws sync-graph`
rebuilds from local submodule state, which here reverted logos-protocol to
an older rev and deleted logos-delivery-demo plus all seven logos-evm-*
entries. Only the package-manager-ui edge actually changed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 14, 2026 21:02

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the Logos workspace wiring to support a broader effort to unify SemVer handling across the packaging/release stack by registering additional release/catalog repositories as submodules and adding a new dependency edge so logos-package-manager-ui can follow the shared logos-package semver implementation.

Changes:

  • Add logos-modules-release-* repos to the workspace submodule registry (scripts/ws, .gitmodules).
  • Add logos-package as a declared dependency for logos-package-manager-ui (nix/dep-graph.nix).
  • Configure logos-package-manager-ui to follow the workspace logos-package input (flake.nix).

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

File Description
scripts/ws Registers release/catalog pipeline repos as non-flake workspace submodules.
nix/dep-graph.nix Adds logos-package to logos-package-manager-ui deps/follows in the workspace dep graph.
flake.nix Adds inputs.logos-package.follows = "logos-package" for logos-package-manager-ui.
.gitmodules Adds the three new logos-modules-release-* submodules.

Comment thread flake.nix
Comment on lines 230 to 236
logos-package-manager-ui = {
url = "github:logos-co/logos-package-manager-ui/7c8c3acd7b1215fef956a05b9423ab169a09becc";
inputs.logos-module-builder.follows = "logos-module-builder";
inputs.logos-package.follows = "logos-package";
inputs.package_downloader.follows = "logos-package-downloader-module";
inputs.package_manager.follows = "logos-package-manager-module";
};
Comment thread nix/dep-graph.nix Outdated
logos-package-manager-module = { deps = [ "logos-module-builder" "logos-package-manager" ]; follows = { "logos-module-builder" = "logos-module-builder"; "logos-package-manager" = "logos-package-manager"; }; hasTests = false; };
logos-package-downloader-module = { deps = [ "logos-module-builder" "logos-package-downloader" ]; follows = { "logos-module-builder" = "logos-module-builder"; "logos-package-downloader" = "logos-package-downloader"; }; hasTests = false; };
logos-package-manager-ui = { deps = [ "logos-module-builder" "logos-package-manager-module" "logos-package-downloader-module" ]; follows = { "logos-module-builder" = "logos-module-builder"; "package_downloader" = "logos-package-downloader-module"; "package_manager" = "logos-package-manager-module"; }; hasTests = false; };
logos-package-manager-ui = { deps = [ "logos-module-builder" "logos-package" "logos-package-manager-module" "logos-package-downloader-module" ]; follows = { "logos-module-builder" = "logos-module-builder"; "logos-package" = "logos-package"; "package_downloader" = "logos-package-downloader-module"; "package_manager" = "logos-package-manager-module"; }; hasTests = false; };
dlipicar and others added 2 commits July 14, 2026 18:35
logos-package: <cstdint> before cpp-semver (libstdc++ needs it, libc++ leaks
it) plus a headers-only package output.

package-manager-ui: consume that headers output rather than the library --
the builder was copying liblgx.dylib next to the plugin and ui-host tried to
load it as a Qt plugin, taking the whole UI down.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pins the four chain repos at their PR branch heads so the workspace can
actually build them. These must be re-pinned to the merged master commits as
the chain lands -- that is what the workspace re-pin is for.

dep-graph: sync-graph emits logos-package first in package-manager-ui's deps;
the hand-applied edit had it second, which the CI staleness check caught.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dlipicar

Copy link
Copy Markdown
Contributor Author

Both Copilot comments here are already resolved. They were accurate at PR-creation time — the pinned logos-package-manager-ui rev didn't yet expose a logos-package input — but the chain has since moved on: the workspace now pins the UI at a rev that introduces inputs.logos-package, flake.lock was regenerated, and the follow edge in both flake.nix and nix/dep-graph.nix is live. Workspace CI is green, which exercises exactly that edge.

basecamp's AppsModel had a seventh copy of the buggy versionCmp (drives the
app-manager Upgrade/Downgrade decision); logos-basecamp#257 reroutes it to the
shared logos::semver and re-pins the chain inputs it consumes. Bump the
submodule + flake pin so the integrated workspace build ships it. The
logos-package follow edge already existed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dlipicar

Copy link
Copy Markdown
Contributor Author

Folded in logos-co/logos-basecamp#257 — basecamp's AppsModel::versionCmp was a seventh copy of the buggy comparator (it drives the app-manager's Upgrade/Downgrade decision). Bumped the submodule + flake pin; the logos-package follow edge already existed, and the dep-graph already had the basecamp→logos-package edge.

Updated merge order: logos-package#30 → package-manager#25 / package-downloader#17 → package-manager-ui#51 / basecamp#257 → modules-release-tool#4 → this.

dlipicar and others added 5 commits July 15, 2026 15:35
logos-co/logos-package#30 merged. Move the logos-package submodule + flake
pin to master (8d4236f), and bump the downstream repos to their re-pinned
branch heads (they now consume logos-package master). pm/pd/ui/basecamp stay
on their branches until their own PRs merge.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#17)

Both merged. Move the pm (202af6f) + pd (736f8eb) submodules and flake pins
to master, and bump the basecamp submodule to its pm-master re-pin. ui stays
on its branch until #51 merges.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Both module re-pins merged. Move logos-package-manager-module (439b580) and
logos-package-downloader-module (02fcb4a) submodules + flake pins to master,
and bump the ui + basecamp submodules to their module-re-pinned heads. ui
stays on its branch until #51 merges.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
logos-package-manager-ui#51 merged -> ui submodule + flake to master
(33f5ba0). basecamp -> 7769ed9 (its pushed branch head, which the previous
workspace pin d1b9b68 was an unpushed ancestor of).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
basecamp#257 rebased onto master (#244); the previous pin 7769ed9 was
orphaned by the force-push. Point at the rebased head.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dlipicar dlipicar closed this Jul 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants