feat(xpkg): publish 0.0.47 - #128
Merged
Merged
Conversation
Adds `src`/`dst` for `type = "files"` assets and an `args` list separate from `alias`, both needed by xlings to place and switch files that are not programs. See openxlings/libxpkg#31. Artifact is the GitHub tag tarball; the CN entry is the same bytes under the gitcode mirror, verified by downloading both and comparing: fe879e8f52ea5a7f316ca54bca1fa393febb8e2a23a3de852b0c2be1918a0be9
Sunrisepeak
added a commit
to openxlings/xlings
that referenced
this pull request
Jul 26, 2026
**File assets.** A package that ships something which is neither a program nor a library -- headers under their own directory, pkg-config files, certificates -- had no way to say so. `includedir` could only mean "this directory becomes sysroot include". So index recipes grew their own file-placing helpers instead: seven of them, in three languages, with two contradictory conflict policies, and four invisible to the tool that is meant to switch and remove what they write. `type = "files"` with a `src`/`dst` pair says it, using the fields libxpkg 0.0.47 added (openxlings/libxpkg#31, published via mcpplibs/mcpp-index#128). Both ends stay relative and the resolver enforces it: a payload is shared between subos and reference-counted, so an absolute destination recorded against one would be right for the subos that installed it and wrong for every other. Destinations are also constrained to `usr/`, `etc/` and `share/` -- `bin/` belongs to the shims, and anything walking upward leaves the subos entirely. A recipe that trips either gets a refusal naming the field, not a surprising placement. Placement reuses the library path: same rename-based replacement, same skip-when-already-correct, so `use` re-materializing on every invocation costs a stat rather than a window with the file absent. **Date-based release identity.** `x.y.z` is retired; this release is `2026.7.27.0` -- year, month, day, same-day sequence, no leading zeros. The scheme is safe for already-released clients, and that is verified rather than assumed. `self update` expands to `install xlings@latest` plus `use xlings latest`, and the latter sorts through `version_key_greater`, which compares components numerically: [2026,7,27,0] against [0,4,68] decides on the first one. A new test pins that against every 0.x a user might be on. Writing that test found the reason leading zeros must not be used, which is sharper than the tidiness argument the design doc gave. When every component ties, the comparator falls back to a lexicographic tiebreak to keep the order total -- `sort` requires that. So `2026.07.27.0` and `2026.7.27.0` are not one key spelled two ways; they are two distinct keys, numerically equal and ordered by string bytes. A database holding both would have two entries for one version and `latest` would pick by accident. The test now pins that they are distinguishable, which is exactly why only one spelling may be written. Verified: 452 unit tests across 11 binaries; E2E-34 extended with a declared file asset that must move with the release at every step; group switch, multi-version removal, home-config lock, subos install and idempotent install all still pass.
Sunrisepeak
added a commit
to openxlings/xlings
that referenced
this pull request
Jul 26, 2026
…s mixing versions (#410) * fix(xvm): make libraries follow the release they belong to A library was already a first-class entry: registered with a payload path, a source name and a destination name, carried through the binding group like any other member, and materialized correctly at install. What it never had was materialization on *switch*. `plan_use_switch` decided library work by reading `VData::libdir`. Nothing in the tree writes that field. Not the registration batch -- `RegistrationNode` has no such member -- and not any other path; the only mention outside the struct is the JSON reader. On a real installation it is absent from all 372 entries. So the planner emitted no library work at all and `xlings use` was a silent no-op for libraries, while `install_libs()` sat at zero call sites. What that produced, concretely: install openssl 3.1.5, then 3.2.0. The install path overwrites the sysroot link, so the library is at 3.2.0. The headers travel a different route entirely and stay at 3.1.5. `use` in either direction moves neither and reports success both times. The user compiles against one version and links against another, and nothing says so until it fails at run time. Every fact needed to place a library was already in the entry -- `path` + `sourceName` + `destinationName`, exactly what the install path uses. The planner was reading the wrong field. `xvm::library_placement` now resolves an entry to `{source, name}` through accessors moved into xvm.types, so registration and the planner cannot drift; `MemberSwitch` carries the placement instead of a directory; and the two installer call sites that were also gated on `libdir` go through the same resolver. Placement replaces by rename rather than remove-then-link. Two versions of a library share a soname, so a switch overwrites the same name, and `use` re-materializes the active release on every invocation to repair a drifted sysroot -- remove-then-link would open a window with the library absent on every one of those calls. Only a genuinely different soname is unlinked. Legacy state is covered: entries written before 0.4.70 have no per-version `kind`, so the resolver falls back to the target-level `type`, which those 246 targets do have. Without that an upgrade would quietly stop switching every library the user already had. `install_libs` / `install_libdir` / `remove_libdir` are gone -- the first had no callers, the other two were reachable only through the field that is never written. Verified: 6 new cases (XvmLibrarySwitch) + two existing switch-plan cases reworked to cover a real library member; 441 tests, 11 binaries green. Restoring the `libdir` read fails the new suite. * fix(doctor): a release anchor is not a broken payload A library-only package has no program of its own, so its recipe registers the package name with no bindir purely to give the libraries something to bind to -- `cairo.lua` writes `xvm.add(package.name)` and nothing else. With `type` unset the C++ side defaults it to "program", so the entry claims to be an executable that will never exist, and `self doctor` reports it as a broken payload with a hint (`xlings install <pkg>@<ver>`) that cannot fix it because nothing is wrong. `xvm::is_binding_root` recognises the shape: some other entry names this one as the root of its release. Both forms count -- the provider group written since 0.4.70, and the legacy pairwise edge where a member records `bindings[root][memberVersion] = rootVersion`. An upgraded installation is full of the second, so recognising only the first would leave every existing anchor misreported. Reported, but as what it is, rather than suppressed. Staying silent would hide the rarer case of a genuine program whose payload directory survived while its executable did not: that entry is also a binding root, so it lands on the same line and the user still sees it. Measured against a real 372-entry installation: `self doctor` went from 9 broken payloads to 8 plus 2 release anchors, with `binutils@2.42` and `cairo@1.18.0` reclassified. (Two further findings became visible only because the panel is a fixed 24 lines and reclassifying freed the slots -- they were always there.) Correcting a number I put in the design doc and in #408: I claimed 31 false positives. That was my own script's count of program-typed entries with no same-named executable in the payload root, not doctor's output. doctor resolves through `resolve_executable`, which searches `bin/` as well, so most of those 31 resolve fine and were never reported. The real figure is 2. Both documents are corrected in follow-up commits. Verified: 3 new cases (XvmBindingRoot) covering the provider-group form, the legacy pairwise form, and a standalone program that must stay reportable. 444 tests, 11 binaries green. * fix(xim): stop a non-active version from overwriting the active library Two defects, both found by putting the real CLI in front of a package that ships a program, a library and a header -- the shape of openssl, which is where the original report came from. **Libraries were being placed in a directory nothing reads.** `cmd_use` resolved the sysroot library directory as `<subos>/usr/lib`, while the install path has always used `<subos>/lib` (`Config::paths().libDir`). On a real installation that is 94 entries against one. The two never disagreed in practice only because the switch side emitted no library work at all -- `VData::libdir`, the field it keyed on, has no writer -- so making libraries switch would have quietly started filling a second, unused directory. **Installing a second version clobbered the first one's library.** The `Library` filesystem effect had no `active` gate. `InstallHeaders` grew one in 0.4.70 for exactly this reason and `Library` was missed, so `xlings install pkg@2.0.0` while 1.0.0 was active moved the library to 2.0.0 and left the headers on 1.0.0. The sysroot then held two releases at once: compile against one, link against the other, and nothing reports it until run time. E2E-34 walks the whole sequence and asserts the library and the header agree at every step: 1. install v1 → both materialize at v1 2. install v2 → v1 stays active, the sysroot does not move 3. use v2 → both move 4. use v1 → both move back 4b. use from the library → the program follows too 5. remove, empty uninstall() → the whole release deregisters Scenario 5 is the other thing this pins. Removal has been provider-scoped since 0.4.70 -- with no `xvm.remove` ops the installer finds every entry whose `bindingGroup.provider` matches and takes the release out -- but that was read out of the code and never tested. The fixture's `uninstall()` returns true and does nothing, so if the mechanism does not work the entries survive and the scenario fails. It is the prerequisite for deleting the hand-written teardown boilerplate every recipe currently carries. Both defects were reproduced by the test before being fixed; scenario 1 failed on the wrong directory and scenario 2 on the missing gate. Verified: E2E-34 green; xvm_group_switch, remove_multi_version, subos_xpkg_install, home_config_lock all still pass; 444 unit tests, 11 binaries green. * docs(release): record the library-switching fixes in the 0.4.70 notes * test(xvm): build the expected library path with `path`, not a POSIX literal The planner joins the payload directory and the source name with `std::filesystem::path`, which yields native separators. Comparing the result against "/pkg/openssl/3.1.5/lib64/libssl.so.3" therefore passes on POSIX and can never pass on Windows, where the value is "/pkg/openssl/3.1.5/lib64\\libssl.so.3". Windows CI caught it. Five other assertions in the same suite use substring matches and were unaffected. Build the expectation the same way the value is built. * docs(release): adopt the compound release identity `(0.4.70) 2026.07.27.0` -- semantic version plus release date and same-day sequence. The semantic version stays machine-readable: `self update` compares it and both indexes key on it, so a date-only identity would leave already-released clients unable to tell whether an upgrade exists. * feat(xvm): declared file assets, and date-based release identity **File assets.** A package that ships something which is neither a program nor a library -- headers under their own directory, pkg-config files, certificates -- had no way to say so. `includedir` could only mean "this directory becomes sysroot include". So index recipes grew their own file-placing helpers instead: seven of them, in three languages, with two contradictory conflict policies, and four invisible to the tool that is meant to switch and remove what they write. `type = "files"` with a `src`/`dst` pair says it, using the fields libxpkg 0.0.47 added (openxlings/libxpkg#31, published via mcpplibs/mcpp-index#128). Both ends stay relative and the resolver enforces it: a payload is shared between subos and reference-counted, so an absolute destination recorded against one would be right for the subos that installed it and wrong for every other. Destinations are also constrained to `usr/`, `etc/` and `share/` -- `bin/` belongs to the shims, and anything walking upward leaves the subos entirely. A recipe that trips either gets a refusal naming the field, not a surprising placement. Placement reuses the library path: same rename-based replacement, same skip-when-already-correct, so `use` re-materializing on every invocation costs a stat rather than a window with the file absent. **Date-based release identity.** `x.y.z` is retired; this release is `2026.7.27.0` -- year, month, day, same-day sequence, no leading zeros. The scheme is safe for already-released clients, and that is verified rather than assumed. `self update` expands to `install xlings@latest` plus `use xlings latest`, and the latter sorts through `version_key_greater`, which compares components numerically: [2026,7,27,0] against [0,4,68] decides on the first one. A new test pins that against every 0.x a user might be on. Writing that test found the reason leading zeros must not be used, which is sharper than the tidiness argument the design doc gave. When every component ties, the comparator falls back to a lexicographic tiebreak to keep the order total -- `sort` requires that. So `2026.07.27.0` and `2026.7.27.0` are not one key spelled two ways; they are two distinct keys, numerically equal and ordered by string bytes. A database holding both would have two entries for one version and `latest` would pick by accident. The test now pins that they are distinguishable, which is exactly why only one spelling may be written. Verified: 452 unit tests across 11 binaries; E2E-34 extended with a declared file asset that must move with the release at every step; group switch, multi-version removal, home-config lock, subos install and idempotent install all still pass.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
libxpkg 0.0.47 —— 为 xlings 的 files 资产模型提供声明能力。
新增
src/dsttype = "files"的资产声明。两端都相对(src 相对 payload 根,dst 相对 subos 根)—— payload 跨 subos 共享且引用计数,绝对路径只对安装它的那个 subos 正确argsalias分开的注入参数列表。此前唯一办法是拼进 alias 字符串,消费方按第一个空格切分,路径含空格即废xvm.files{src,dst}上游:openxlings/libxpkg#31(4 个新测试,CI 绿)
产物
三个平台段各加一条,GLOBAL + CN 双 URL:
两个 URL 的内容逐字节相同 —— 都下载下来比对过 sha256,与 0.0.46 的做法一致(0.0.46 的 CN/GLOBAL/索引记录三者 sha256 也完全相同)。
CN 产物已用
gtc上传并回验:GET 200,133404 字节,sha256 匹配。(gitcode 的HEAD返回 401,校验必须用GET。)解包目录为
libxpkg-0.0.47/,符合 Form A 描述符对<verdir>/libxpkg-<tag>/mcpp.toml的要求。