Skip to content

Fix Composer global home lookup order (#586) - #1421

Merged
Mikola Lysenko (mikolalysenko) merged 5 commits into
mainfrom
agent/fix-composer-xdg-home-order
Oct 12, 2026
Merged

Mikola Lysenko (mikolalysenko) merged 5 commits into
mainfrom
agent/fix-composer-xdg-home-order

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Oct 12, 2026 •

Copy link
Copy Markdown
Collaborator

LLM Description written by Claude Code:claude-opus-5-5

Fixes #586

Summary

Global Composer discovery (scan -g, apply -g, vex -g) now finds a Composer 2 global install in the XDG config dir even when an old ~/.composer directory is still around and composer isn't on PATH.

Root cause

When COMPOSER_HOME is unset and composer global config home can't run (composer not on PATH, e.g. a composer.phar install), get_composer_home falls back to composer_home_candidates (crates/socket-patch-core/src/crawlers/composer_crawler.rs). That list probed ~/.composer before the XDG home, which is Composer 1's rule. Composer 2's Factory::getHomeDir checks $XDG_CONFIG_HOME/composer (or ~/.config/composer) first on an XDG system (any XDG_* env var set, or /etc/xdg exists), and only then ~/.composer. So a leftover ~/.composer, even an empty one, hid the real global install and global commands silently found nothing (exit 0).

Fix

  • On an XDG system, probe the XDG home first, then ~/.composer, matching Composer 2. XDG detection mirrors Composer's useXdg. An XDG_CONFIG_HOME in the supplied environment also counts.
  • An empty XDG_CONFIG_HOME means ~/.config, as in Composer. A relative one is skipped rather than guessed, so it can't put a directory Composer wouldn't use ahead of ~/.composer.
  • Windows and non-XDG systems keep their current order. ~/.config/composer stays as a last probe everywhere.
  • The candidate list is built on the blocking pool (via Fix Composer vendor-dir config cascade (#439, #686) #1408's default_composer_home), since the XDG check stats /etc/xdg.
  • composer_home_candidates keeps its signature, so it doesn't conflict with Fix Composer vendor-dir config cascade (#439, #686) #1408, which also calls it.

Trade-off: Composer 1 still checks ~/.composer first. socket-patch can't tell which Composer major is installed without running composer, and when composer is on PATH its own answer already wins. So the fallback follows Composer 2. Composer 1 is end-of-life, and packagist stopped serving it on 2025-09-01.

Tests (red → green)

Issue case Test Before fix After
#586: stale empty ~/.composer + real ~/.config/composer, XDG system covgap_crawlers_composer_crawler::get_vendor_paths_global_xdg_home_outranks_stale_dot_composer FAILED (left: [], the issue's exact symptom) ok
#586: XDG_CONFIG_HOME set, ~/.composer/vendor also present covgap_crawlers_composer_crawler::get_vendor_paths_global_xdg_config_home_outranks_stale_dot_composer FAILED (picked ~/.composer/vendor) ok
Candidate order: XDG vs non-XDG, empty, relative and supplied XDG_CONFIG_HOME composer_crawler::tests::test_home_candidates_* (3 unit tests) new ok

Commands run locally (Linux, as root):

  • cargo fmt --all -- --check: ok
  • cargo clippy --workspace --all-features -- -D warnings: ok
  • cargo test --workspace --all-features --no-fail-fast: all 281 composer tests pass. 13 tests fail only in this sandbox: chmod-based write-failure tests that can't fail as root (*_write_failure_*, *unremovable*, relax_loop_must_not_traverse_symlinked_root, etc.), plus e2e_bun_lockb, which needs network access to patches-api.socket.dev. None touch Composer code. CI runs them as non-root.
  • The Composer e2e suites (e2e_vendor_composer_build, e2e_redirect_composer_build) exercise project mode, not the global-home fallback, so they weren't re-run locally. CI runs them.
  • No wrapper (npm/, pypi/, gem/) changes are needed: they only dispatch to the binary.

/code-review high

I fixed every confirmed finding in ab0ffcb except one. Not fixed: "Composer 1 users with both dirs get the XDG dir". That is the intended trade-off described above.

🤖 Generated with Claude Code

https://claude.ai/code/session_011PgPZPKvQDRtAkxyHR3Pe8


Generated by Claude Code

Assisted-by: Claude Code:claude-opus-5-5
When composer isn't on PATH and COMPOSER_HOME is unset, global
discovery guessed the Composer home in Composer 1's order and checked
~/.composer first. Composer 2 checks the XDG config dir first on an
XDG system (any XDG_* variable set, or /etc/xdg present), so a
leftover ~/.composer hid the real global install and scan -g,
apply -g and vex -g silently found nothing.

Probe $XDG_CONFIG_HOME/composer (or ~/.config/composer) before
~/.composer on XDG systems, as Composer 2's Factory::getHomeDir does.
Non-XDG systems and Windows keep their current order.

Fixes #586

Assisted-by: Claude Code:claude-opus-5-5
The env() helper in the composer crawler tests is only used by
unix-only tests, so it would be dead code (and a clippy error) on
Windows.

Assisted-by: Claude Code:claude-opus-5-5
Follow-ups from review of the XDG-first order:

- Count an XDG_CONFIG_HOME in the supplied environment as an XDG
  system, so it is never dropped from the probes.
- Skip the XDG home when XDG_CONFIG_HOME is relative instead of
  guessing ~/.config, which Composer would not use; an empty value
  still means ~/.config, as in Composer.
- Build the candidate list on the blocking pool, since the XDG check
  stats /etc/xdg.
- Clear XDG_CONFIG_HOME in a shim test that relies on the HOME
  fallback, and fix a mislabeled unit-test assertion.

Refs #586

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko
Mikola Lysenko (mikolalysenko) marked this pull request as ready for review October 12, 2026 08:31
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@cursor cursor Bot 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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit ab0ffcb. Configure here.

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

[agent] coverage failed on ab0ffcb in -p socket-patch-cli --test in_process_edge_cases (job 114727527547). This is the intermittent failure tracked in #1416: the same binary failed on #1410 and passed on a re-run of the same SHA. This PR changes only the Composer global-home fallback in crawlers/composer_crawler.rs and its core tests, and doesn't touch that binary. Every other test target in the job passed, and all Composer tests pass locally. No fix exists yet; #1416 is waiting for someone with log access to get the panic text (the log tool here returns only the last 5,000 of ~16,000 lines). Re-running the failed job once.


Generated by Claude Code

@mikolalysenko Mikola Lysenko (mikolalysenko) added the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 12, 2026
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Ready for review (burn-down agent)


Generated by Claude Code

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Final review brief

What it does: Fixes #586. When neither COMPOSER_HOME nor composer global config home answers, socket-patch now probes the global Composer home in Composer 2's order. On an XDG system it checks $XDG_CONFIG_HOME/composer (or ~/.config/composer) before ~/.composer. An XDG system is one where any XDG_* env var is set or /etc/xdg exists, the same test as Composer's useXdg(). Before this, a stale ~/.composer beat the real XDG home.

Risk: low. It only changes the last-resort fallback. Windows ordering is the same as before. On non-XDG systems, ~/.config/composer is still probed last, so nothing that used to be found is lost.

Look here:

Verified:

  • Read the full diff and compared it with Composer 2's Factory::getHomeDir().
  • Local test runs at ab0ffcb: composer_crawler unit tests 38/38, covgap_crawlers_composer_crawler 5/5, global_probe_spawn_e2e 10/10, crawler_composer_e2e 33/33.
  • Both new regression tests fail against the main-branch composer_crawler.rs.
  • CI ci-ok and clippy are green on ab0ffcb, and Bugbot is clean.
  • No unresolved threads, and CHANGELOG.md is untouched.

Changes I made: none.

Open questions (non-blocking):

  1. uses_xdg() reads the real process env and /etc/xdg, not the injected var closure. Production is unaffected, and the tests call composer_home_candidates_for(.., xdg) with an explicit flag instead.
  2. There are two intended differences from Composer. A relative XDG_CONFIG_HOME is skipped, where Composer would resolve it against its cwd. With HOME unset, the code still probes an absolute XDG home, where Composer would throw.

Auto-merge is armed, so approving sends this straight to the merge queue.


Generated by Claude Code

@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 12, 2026
@mikolalysenko Mikola Lysenko (mikolalysenko) removed the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 12, 2026
…g-home-order

# Conflicts:
#	crates/socket-patch-core/src/crawlers/composer_crawler.rs
Merged via the queue into main with commit 790089c Oct 12, 2026
53 checks passed
@mikolalysenko
Mikola Lysenko (mikolalysenko) deleted the agent/fix-composer-xdg-home-order branch October 12, 2026 16: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

3 participants