Skip to content

Route build-step composer calls through ephpm composer (opt-in) - #36

Open
luthermonson wants to merge 1 commit into
mainfrom
feat/ephpm-composer-routing
Open

luthermonson wants to merge 1 commit into
mainfrom
feat/ephpm-composer-routing

Conversation

@luthermonson

Copy link
Copy Markdown
Contributor

What

Adds an opt-in flag that routes a preview's Composer invocations through the embedded ephpm composer (vivacity) fast Rust installer, so PR users' build steps transparently use the fast installer without changing their manifests and without breaking vivacity's fallback.

  • New config knob: --use-ephpm-composer / SWITCHBOARD_EPHPM_COMPOSER (default off, opt-in).

When on:

  • Every build:/seed: command whose leading token is exactly composer is rewritten to ephpm composer before it runs in the tenant sandbox (route_composer_command).
  • The implicit composer install (run when a manifest declares no build steps and a composer.json exists) runs as ephpm composer install … instead of the single-token --composer binary.

PR authors' manifests are never edited — only the command switchboard constructs and executes changes.

Approach: token-rewrite (recommended, chosen)

Switchboard already wraps every untrusted step as ephpm exec … --site <key> -- sh -c "cd '<dir>' && <cmd>". The change rewrites the leading composer token of <cmd> to ephpm composer. This is deterministic, shell-agnostic (doesn't depend on shopt -s expand_aliases), and recursion-safe.

Only a genuine leading composer token is rewritten:

  • composer install --no-devephpm composer install --no-dev
  • my-composer …, php composer.phar …, composer.phar …, echo composer …, and non-leading composer occurrences pass through untouched.
  • An already-routed ephpm composer … (leading token ephpm) is left alone — no double-rewrite.

Recursion safety

vivacity's out-of-scope fallback shells out with Command::new("composer") — a PATH search that ignores shell aliases. Switchboard rewrites only its own constructed command and never shadows composer on PATH, so that fallback resolves to the host's real PHP composer, not back into the rewrite.

Operator requirement (documented on the flag and in the README): the host must keep a real PHP composer on PATH, and an ephpm binary that carries the composer subcommand, for the fallback to work.

Tests

Rust unit/integration tests (in src/deployer.rs / src/config.rs):

  1. Rewrite on — leading composerephpm composer (args preserved; bare composer; leading whitespace preserved), plus the end-to-end sandbox argv asserts the executed command is ephpm exec … -- sh -c "cd '<dir>' && ephpm composer install --no-dev" and the program is still the ephpm binary.
  2. Flag off — command byte-for-byte unchanged (no regression).
  3. Non-leading / look-alikephp artisan … composer, echo composer …, my-composer …, composer.phar …, php composer.phar …, and re-entered ephpm composer … are all left untouched.
  4. Config: --use-ephpm-composer parses; defaults to off.

Full suite: 314 passed, 0 failed (cargo test). cargo clippy --all-targets clean.

Recursion-safety verified separately with stub ephpm/composer binaries on PATH: driving the exact constructed command once produced exactly one ephpm composer invocation followed by one real composer invocation, exit 0, no hang — proving the fallback reaches the real composer with no infinite shim recursion.

…composer`

Add an opt-in `--use-ephpm-composer` / `SWITCHBOARD_EPHPM_COMPOSER` flag
(default off) that routes a preview's Composer invocations through the
embedded `ephpm composer` (vivacity) fast Rust installer, without touching
PR authors' manifests and without breaking vivacity's fallback.

When on:
- each `build:`/`seed:` command whose leading token is exactly `composer`
  is rewritten to `ephpm composer` before it runs in the tenant sandbox
  (`route_composer_command`), and
- the implicit `composer install` (run when a manifest declares no build
  steps and a `composer.json` exists) runs as `ephpm composer install …`
  instead of the single-token `--composer` binary.

Only a genuine leading `composer` token is rewritten — `my-composer`,
`php composer.phar`, `echo composer`, and non-leading occurrences pass
through untouched, as does an already-routed `ephpm composer …`.

Recursion-safe by construction: switchboard rewrites only its own
constructed command and never shadows `composer` on `PATH`, so vivacity's
out-of-scope fallback (`Command::new("composer")`, a `PATH` search that
ignores shell aliases) still resolves to the host's real PHP composer. The
host must therefore keep a real PHP `composer` on `PATH` for the fallback;
this is documented on the flag and in the README config table.

Tests: pure-rewrite cases (leading rewrite, flag-off no-op, non-leading /
look-alike ignored), the end-to-end sandbox argv (`ephpm exec … -- sh -c
"cd … && ephpm composer install …"`), and the config flag parse. Full
suite green (314 passed). Recursion-safety verified separately with stub
`ephpm`/`composer` binaries: one `ephpm composer` hop reaches the real
`composer` and terminates (no infinite shim recursion).
@ephpm

ephpm Bot commented Sep 15, 2026

Copy link
Copy Markdown

ePHPm Preview — deployed (health check pending)

URL https://ephpm-switchboard-pr-36.preview.ephpm.dev
Framework PHP
PHP 8.5
Deployed in 61.1s

Preview updates automatically on each push to this PR.

1 similar comment
@ephpm

ephpm Bot commented Sep 15, 2026

Copy link
Copy Markdown

ePHPm Preview — deployed (health check pending)

URL https://ephpm-switchboard-pr-36.preview.ephpm.dev
Framework PHP
PHP 8.5
Deployed in 61.1s

Preview updates automatically on each push to this PR.

@ephpm

ephpm Bot commented Sep 15, 2026

Copy link
Copy Markdown

ePHPm Preview — deployed (health check pending)

URL https://ephpm-switchboard-pr-36.preview.ephpm.dev
Framework PHP
PHP 8.5
Deployed in 60.7s

Preview updates automatically on each push to this PR.

@ephpm
ephpm Bot temporarily deployed to preview-pr-36 September 15, 2026 23:48 Inactive
@ephpm
ephpm Bot temporarily deployed to preview-pr-36 September 15, 2026 23:48 Inactive
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