Skip to content

fix(executor): move auto-stamp out of run_hook into explicit API#12

Merged
Sunrisepeak merged 1 commit intomainfrom
fix/auto-stamp-explicit-api
May 2, 2026
Merged

fix(executor): move auto-stamp out of run_hook into explicit API#12
Sunrisepeak merged 1 commit intomainfrom
fix/auto-stamp-explicit-api

Conversation

@Sunrisepeak
Copy link
Copy Markdown
Member

Summary

`v0.0.35` placed auto-stamp inside `run_hook`, which broke xlings's install pipeline for packages whose install hook silently no-ops (e.g. patchelf — tarball has no top-level dir so the hook's `os.mv` finds nothing to move).

Move stamp logic into a separate `apply_install_stamp_if_empty(ctx)` method that consumers call AFTER all their install paths complete.

Repro on v0.0.35 + xlings 0.4.12

`xlings self install` → `xim-x-patchelf/0.18.0/` ends up containing only `.xim-installed` instead of the patchelf binary. E2E-02 self-install test fails with "patchelf binary not found after self install".

Root cause

xlings's install pipeline:

  1. `run_hook(Install)` — patchelf install hook runs, silently no-ops (extracted dir doesn't match expected name)
  2. `stage_extracted_payload_` — copies extracted files into install_dir, ONLY IF dir is empty
  3. `normalize_file_install_` — final layout pass

`v0.0.35` wrote `.xim-installed` at the end of step 1, so step 2's "is dir empty?" check returned false and the fallback was skipped. Result: install_dir contains only the stamp, no payload.

Fix

`run_hook` is now stamp-free. Consumers explicitly call `apply_install_stamp_if_empty(ctx)` after their full install pipeline. xlings will add the call site in its installer.cppm in a follow-up PR.

Tests

  • `ApplyInstallStamp_WritesStampWhenInstallDirEmpty` — happy path
  • `ApplyInstallStamp_SkipsWhenInstallDirNonEmpty` — won't clobber existing content
  • `RunHook_DoesNotImplicitlyStamp` — regression guard against v0.0.35 behavior
  • `ApplyInstallStamp_IsIdempotent` — calling twice is safe

26/26 ExecutorTest pass locally.

Tag plan

Ship as `v0.0.36`.

The auto-stamp inside run_hook (introduced in v0.0.35) wrote
.xim-installed BEFORE consumers could finish their install pipeline.
For xlings, the install pipeline is:

    1. run_hook(Install)
    2. stage_extracted_payload_ if install_dir empty + extracted_root
    3. normalize_file_install_

The stage 2 fallback exists for packages whose install hook silently
no-ops (e.g. patchelf: the tarball has no top-level dir, so
`os.mv(extracted_dir, install_dir)` finds nothing to move and the dir
stays empty). Auto-stamp inside step 1 wrote the stamp before step 2
could probe "is install_dir empty?", so step 2 skipped and the package
ended up with just the stamp file — no actual binary.

Reproduced: `xlings self install` left
$XLINGS_HOME/data/xpkgs/xim-x-patchelf/0.18.0/ containing only
.xim-installed; E2E-02 self-install test failed with "patchelf binary
not found after self install".

Fix: move the stamp logic into a separate method
`apply_install_stamp_if_empty(ctx)` that consumers call AFTER all
their install paths complete (hook + extracted-payload + default
script install). run_hook becomes side-effect-free with respect to
the stamp.

Tests:
  - ApplyInstallStamp_WritesStampWhenInstallDirEmpty
  - ApplyInstallStamp_SkipsWhenInstallDirNonEmpty
  - RunHook_DoesNotImplicitlyStamp (regression guard)
  - ApplyInstallStamp_IsIdempotent
26/26 ExecutorTest pass.

Migration: consumers calling auto-stamp implicitly via run_hook (only
xlings, via mcpplibs-xpkg 0.0.35) must add an explicit
`exec->apply_install_stamp_if_empty(ctx)` call after their
extracted-payload fallback. Drop-in for v0.0.35; ship as v0.0.36.
@Sunrisepeak Sunrisepeak merged commit 278983a into main May 2, 2026
3 checks passed
Sunrisepeak added a commit to d2learn/xlings that referenced this pull request May 2, 2026
…ty call

PR #256's first push (0.0.35) regressed E2E-02 because libxpkg's
auto-stamp ran inside run_hook, writing .xim-installed before xlings's
stage_extracted_payload_ fallback could probe "is install_dir empty?".
patchelf's tarball has no top-level dir, so its install hook silently
no-ops; without the fallback, install_dir ended up containing only
the stamp file → "patchelf binary not found after self install".

libxpkg PR #12 / v0.0.36 moved auto-stamp into a separate
apply_install_stamp_if_empty(ctx) method that consumers call after
their full install pipeline. This commit:

  1. Bumps add_requires 0.0.35 → 0.0.36 (carries the API split)
  2. Adds the explicit apply_install_stamp_if_empty call in
     installer.cppm right after stage_extracted_payload_ +
     normalize_file_install_, so:
       hook → stage extracted payload → normalize → stamp
     The stamp now sees the FINAL install_dir state and only fires
     for genuinely-empty wrapper packages (linux-headers etc.).

Local E2E verified: `xlings self install` populates
xim-x-patchelf/0.18.0/bin/patchelf correctly.

libxpkg PR: openxlings/libxpkg#12 (merged, v0.0.36)
Sunrisepeak added a commit to d2learn/xlings that referenced this pull request May 2, 2026
…#256)

* feat(deps): bump mcpplibs-xpkg 0.0.34 → 0.0.35 (auto-stamp framework)

Picks up libxpkg's auto-stamp executor framework: after a successful
install hook returns and install_dir is non-empty, the executor writes
a `.xim-installed` stamp file (INI key=value: schema, name, version,
platform) into install_dir if the dir is empty.

This unblocks thin delegator packages — those whose actual payload lives
in a separately-installed dep (e.g. linux-headers wraps
scode:linux-headers, fromsource:* aliases) — from xlings's
"installed: yes/no" probe. Without a stamp, the probe checks install_dir
for content; an empty install_dir reports "no" → every dependent install
re-triggers the wrapper's install hook + config hook unnecessarily.

No version bump in this PR (deferring 0.4.13 release until other in-flight
work lands). The 0.0.35 → 0.4.x consumption path is just an internal
xrepo dependency bump; existing xlings 0.4.12 binaries are unaffected.

libxpkg PR: openxlings/libxpkg#10 (merged)
libxpkg tag: v0.0.35
mcpplibs-index: mcpplibs/mcpplibs-index@20e7543

* fixup: bump 0.0.35 → 0.0.36 + add explicit apply_install_stamp_if_empty call

PR #256's first push (0.0.35) regressed E2E-02 because libxpkg's
auto-stamp ran inside run_hook, writing .xim-installed before xlings's
stage_extracted_payload_ fallback could probe "is install_dir empty?".
patchelf's tarball has no top-level dir, so its install hook silently
no-ops; without the fallback, install_dir ended up containing only
the stamp file → "patchelf binary not found after self install".

libxpkg PR #12 / v0.0.36 moved auto-stamp into a separate
apply_install_stamp_if_empty(ctx) method that consumers call after
their full install pipeline. This commit:

  1. Bumps add_requires 0.0.35 → 0.0.36 (carries the API split)
  2. Adds the explicit apply_install_stamp_if_empty call in
     installer.cppm right after stage_extracted_payload_ +
     normalize_file_install_, so:
       hook → stage extracted payload → normalize → stamp
     The stamp now sees the FINAL install_dir state and only fires
     for genuinely-empty wrapper packages (linux-headers etc.).

Local E2E verified: `xlings self install` populates
xim-x-patchelf/0.18.0/bin/patchelf correctly.

libxpkg PR: openxlings/libxpkg#12 (merged, v0.0.36)
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.

1 participant