Skip to content

Commit e1e23f2

Browse files
committed
feat(envlite): add up subcommand (init + foreground serve)
`up` runs the same Phase 0–8 sequence as `init`, then re-probes the resolved port and streams `php -S` in the foreground via the same `envlite_proc_stream` path `serve` uses. Same flags as `init`: `--port=N` and `--no-build`. Generalize `envlite_init_phase_guard` → `envlite_phase_guard($sub, $n, $fn)` so phase-failure messages carry the active subcommand name (`envlite up: phase 5: …`). Switching the final step from `proc_open` to `pcntl_exec` so the envlite process is replaced in place is intentionally a follow-up; it needs a Windows-fallback decision (`pcntl` is unavailable there).
1 parent 0a45e19 commit e1e23f2

4 files changed

Lines changed: 95 additions & 3 deletions

File tree

plans/ENVLITE_SPECIFICATION.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ shorthand for the full command line.
6161
|---|---|
6262
| (no args), `help`, `--help`, `-h` | Print usage and exit 0. |
6363
| `init` | Run all setup phases. Leaves the repo ready to `serve` and to run tests. |
64+
| `up` | Run all setup phases, then start the dev server in the foreground. Equivalent to `init` followed by `serve`. |
6465
| `serve` | Exec the dev server on the discovered/cached port. Foreground; respond to Ctrl-C. |
6566
| `clean` | Remove envlite-managed files (manifest entries). Does not touch `node_modules/`, `vendor/`, or build artifacts under `src/`. |
6667

@@ -79,6 +80,13 @@ shorthand for the full command line.
7980
- `--port=N` skips Phase 1 discovery and uses the given port. Updates
8081
`.envlite/port` to N.
8182
- `--no-build` skips Phase 3. Useful when iterating on PHP-only changes.
83+
- `up [--port=N] [--no-build]`
84+
- Same flag semantics as `init`. After all phases succeed, `up`
85+
re-probes the resolved port and runs `php -S` in the foreground —
86+
the same invocation `serve` uses. The currently shipped form spawns
87+
`php -S` as a child process via `proc_open` (matching `serve`); a
88+
follow-up will switch to `pcntl_exec` so the envlite process is
89+
replaced in place.
8290
- `serve` (no flags; the cached port is the source of truth)
8391
- `clean` (no flags)
8492

@@ -891,6 +899,10 @@ are not worth the output-interleaving and error-handling complexity in
891899
the initial implementation. A future revision may parallelize. Phase
892900
8 must always run last in `init` — it has the most predecessors.
893901

902+
`up` runs the same Phase 0–8 sequence as `init`, then performs the same
903+
bind-probe + foreground `php -S` invocation as `serve`. It introduces no
904+
new phases.
905+
894906
---
895907

896908
## Idempotency rules (summary)

tools/local-env/envlite.php

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ function envlite_help_text(): string {
1111
1212
Subcommands:
1313
init [--port=N] [--no-build] Run all setup phases.
14+
up [--port=N] [--no-build] Run init phases, then start the dev server.
1415
serve Run the dev server on the cached port.
1516
clean Remove envlite-managed files.
1617
help Print this help.
@@ -739,6 +740,7 @@ function envlite_main(array $argv): int {
739740
return 0;
740741
}
741742
if ($sub === 'init') { return envlite_cmd_init($args, $force); }
743+
if ($sub === 'up') { return envlite_cmd_up($args, $force); }
742744
if ($sub === 'serve') { return envlite_cmd_serve($args, $force); }
743745
if ($sub === 'clean') { return envlite_cmd_clean($args, $force); }
744746

@@ -796,15 +798,70 @@ function envlite_cmd_init(array $args, bool $force): int {
796798
[8, function () use ($repoRoot, $resolvedPort) { envlite_phase8_install_site($repoRoot, $resolvedPort); }],
797799
];
798800
foreach ($phases as [$n, $fn]) {
799-
$rc = envlite_init_phase_guard($n, $fn);
801+
$rc = envlite_phase_guard('init', $n, $fn);
800802
if ($rc !== 0) { return $rc; }
801803
}
802804

803805
fwrite(STDERR, "envlite init: ok — http://127.0.0.1:$resolvedPort/ (admin / password)\n");
804806
return 0;
805807
}
806808

807-
function envlite_init_phase_guard(int $n, callable $fn): int {
809+
function envlite_cmd_up(array $args, bool $force): int {
810+
$port = null;
811+
$noBuild = false;
812+
foreach ($args as $a) {
813+
if ($a === '--no-build') { $noBuild = true; continue; }
814+
if (preg_match('/^--port=(\d+)$/', $a, $m)) {
815+
$port = (int) $m[1];
816+
if ($port < 1 || $port > 65535) {
817+
envlite_log('up', "invalid --port value: $a");
818+
return 2;
819+
}
820+
continue;
821+
}
822+
envlite_log('up', "unknown argument: $a");
823+
return 2;
824+
}
825+
826+
$repoRoot = getcwd();
827+
828+
envlite_phase0_run($repoRoot);
829+
envlite_observe_ht_sqlite($repoRoot);
830+
831+
$resolvedPort = envlite_phase1_discover_port($repoRoot, $port);
832+
fwrite(STDERR, "envlite up: port $resolvedPort\n");
833+
834+
envlite_phase2_npm_ci($repoRoot);
835+
if (!$noBuild) {
836+
envlite_phase3_build_dev($repoRoot);
837+
}
838+
envlite_phase4_composer_install($repoRoot);
839+
840+
$phases = [
841+
[5, function () use ($repoRoot, $force) { envlite_phase5_install($repoRoot, $force); }],
842+
[6, function () use ($repoRoot, $force) { envlite_phase6_install($repoRoot, $force); }],
843+
[7, function () use ($repoRoot, $resolvedPort, $force) { envlite_phase7_install($repoRoot, $resolvedPort, $force); }],
844+
[8, function () use ($repoRoot, $resolvedPort) { envlite_phase8_install_site($repoRoot, $resolvedPort); }],
845+
];
846+
foreach ($phases as [$n, $fn]) {
847+
$rc = envlite_phase_guard('up', $n, $fn);
848+
if ($rc !== 0) { return $rc; }
849+
}
850+
851+
if (!envlite_phase1_port_is_free($resolvedPort)) {
852+
envlite_log('up', "failed to bind 127.0.0.1:$resolvedPort");
853+
return 1;
854+
}
855+
856+
fwrite(STDERR, "envlite up: serving http://127.0.0.1:$resolvedPort/ (admin / password)\n");
857+
$exit = envlite_proc_stream(
858+
['php', '-S', "127.0.0.1:$resolvedPort", '-t', 'src', __DIR__ . '/router.php'],
859+
$repoRoot
860+
);
861+
return $exit === 0 ? 0 : 1;
862+
}
863+
864+
function envlite_phase_guard(string $sub, int $n, callable $fn): int {
808865
try {
809866
$fn();
810867
return 0;
@@ -814,7 +871,7 @@ function envlite_init_phase_guard(int $n, callable $fn): int {
814871
if (strpos($msg, $prefix) !== 0) {
815872
$msg = $prefix . $msg;
816873
}
817-
envlite_log('init', $msg);
874+
envlite_log($sub, $msg);
818875
return 1;
819876
}
820877
}

tools/local-env/tests/test_dispatch.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,12 @@ function test_dispatch_unknown_subcommand_returns_two() {
1313
function test_dispatch_force_flag_recognized() {
1414
envlite_assert_eq(2, envlite_main(['envlite.php', '--force', 'bogus']));
1515
}
16+
17+
function test_dispatch_up_rejects_unknown_arg() {
18+
envlite_assert_eq(2, envlite_main(['envlite.php', 'up', '--bogus']));
19+
}
20+
21+
function test_dispatch_up_rejects_bad_port() {
22+
envlite_assert_eq(2, envlite_main(['envlite.php', 'up', '--port=0']));
23+
envlite_assert_eq(2, envlite_main(['envlite.php', 'up', '--port=70000']));
24+
}

tools/local-env/tests/test_up.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
function test_up_help_lists_up_subcommand() {
3+
$help = envlite_help_text();
4+
envlite_assert(strpos($help, 'up ') !== false, 'help text mentions up');
5+
}
6+
7+
function test_up_unknown_subcommand_arg_returns_two() {
8+
envlite_assert_eq(2, envlite_main(['envlite.php', 'up', '--no-such-flag']));
9+
}
10+
11+
function test_up_invalid_port_returns_two() {
12+
envlite_assert_eq(2, envlite_main(['envlite.php', 'up', '--port=notanumber']));
13+
envlite_assert_eq(2, envlite_main(['envlite.php', 'up', '--port=99999']));
14+
}

0 commit comments

Comments
 (0)