Skip to content

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

Open
Mikola Lysenko (mikolalysenko) wants to merge 4 commits into
mainfrom
agent/fix-composer-xdg-home-order
Open

Mikola Lysenko (mikolalysenko) wants to merge 4 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, 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

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review

Projects

None yet

2 participants