feat(ci): cut over to unified ci.yml, delete legacy workflows (PR 5/6)#60
Merged
Merged
Conversation
Complete the cutover planned in docs/superpowers/specs/2026-04-23-
local-ci-unification-design.md. The new ci.yml (PR 4) is now the sole
CI + publish path; the parallel-run grace period is over.
Deleted workflows:
build-php-core.yml, build-extension.yml — superseded by
ci.yml::pipeline (phpup build cell + phpup test)
plan.yml, plan-and-build.yml, on-push.yml — orchestrator layer
replaced by ci.yml's inline matrix
integration-test.yml, compat-harness.yml — fixtures now run
inside each pipeline cell via phpup test (see
test/compat/fixtures.yaml)
nightly.yml, manual.yml, gc-bundles.yml — scheduled/dispatch
entry points removed; gc-bundles still usable on-demand as
`go run ./cmd/gc-bundles --org buildrush --min-age-days N`
bootstrap.yml — depended on plan.yml + build-php-core.yml +
build-extension.yml; dead after those deletions
ci-lint.yml — folded into ci.yml::lint (adds `go vet` step;
golangci-lint / gofmt / mod-tidy / test / build were already
covered by ci.yml's lint+unit jobs)
Deleted scripts:
test/smoke/local-ci.sh — docker smoke replaced by `make ci-cell`,
which runs one full pipeline cell (phpup build cell + phpup test)
locally. The `local-ci` Makefile target is removed with it.
Publish cutover:
ci.yml::publish drops the `vars.PHPUP_NEW_CI_PUBLISH == '1'` gate
and the "(new ci.yml)" marker in the lockfile commit message —
it's now the sole writer to ghcr.io and the lockfile.
Housekeeping:
- Makefile: drop `check-fast` (was only distinct from `check` via
the now-gone local-ci smoke), retarget `check` at the fast path.
- CLAUDE.md: Quality Gates + Testing sections updated to point at
`make ci-cell` for full-cell validation instead of the deleted
local-ci smoke.
- CONTRIBUTING.md: refer to ci.yml instead of ci-lint.yml; note
that gc-bundles is now manual.
- README.md: v2-compat section now describes fixture coverage
inside ci.yml::pipeline instead of the deleted compat-harness.yml.
- Refresh stale code comments in cmd/{planner,gc-bundles,phpup},
internal/build/, internal/planner/, internal/registry/ that
pointed at deleted workflow files.
Verification: make check passes; verified ci.yml is the only CI
workflow triggering on push/PR to main (watch-*.yml and
security-rebuild.yml re-trigger ci.yml; release-please.yml +
check-release-pr.yml are orthogonal release tooling, retained).
4 tasks
phramz
added a commit
that referenced
this pull request
Apr 24, 2026
Adds an on-demand rebuild surface to ci.yml and rewires the stale dispatch glue that PR #60 (legacy workflow cutover) left hanging. ci.yml - New `workflow_dispatch` trigger with a `force: boolean` input. Defaults to false; a force=true dispatch skips the spec-hash cache-probe in every pipeline cell so security-class rebuilds produce fresh artifacts even when the inputs are unchanged. - `pipeline` cell threads `inputs.force` into `make ci-cell` via a FORCE env var so the downstream phpup gets --force. internal/build + Makefile - phpOpts / extOpts / cellOpts grow a Force field; each parse* FlagSet exposes --force. - BuildPHP and BuildExt bypass the LookupBySpec cache-probe when opts.Force is set, printing a "--force, skipping cache-probe" note so the pipeline log is obvious about why the build ran. - BuildCell forwards --force to both downstream BuildPHP and each BuildExt so a cell invocation does the right thing end-to-end. - `make ci-cell FORCE=1` threads through to the CLI. - Unit test: TestBuildPHP_Force_BypassesCacheHit seeds a layout with an identically-spec-hashed bundle, invokes BuildPHP with --force, asserts the runner was called and no "cache hit" line hit stdout. Closes the gap that the plain cache-hit test left. security-rebuild.yml - Pre-existing: `uses: ./.github/workflows/plan-and-build.yml` — but that file was deleted in PR #60, so any dispatch failed at parse-time. Replaced with a single actions/github-script@v7 step that calls actions.createWorkflowDispatch against ci.yml with force=true. Preserves the `repository_dispatch: [security- rebuild]` public entry point. watch-*.yml - Both workflows previously emitted peter-evans/repository-dispatch events (`php-release-detected`, `runner-image-updated`) that no workflow listened for — a silent no-op. Swap both steps to actions/github-script workflow_dispatch calls against ci.yml (force=false — a new upstream signal doesn't by itself invalidate existing bundles; catalog updates land separately). - watch-runner-images.yml also pulls the github token out of the run: block into an env var to quiet the workflow-injection pre-commit guard. The force ergonomics split intentionally: - routine main push + nightly observer poll: no force; pipeline short-circuits unchanged cells via the existing spec-hash probe. - manual "gh workflow run ci.yml -f force=true" + security-rebuild dispatch: force=true rebuilds everything. Verification: make check green; 15/15 Node tests; all 21 Go packages pass. The new TestBuildPHP_Force_BypassesCacheHit fails as expected if the --force conditional is reverted to the old unconditional cache-probe, and passes with the new branch.
3 tasks
phramz
added a commit
that referenced
this pull request
Apr 24, 2026
Adds a new `smoke-action` job to ci.yml that invokes the public
action.yml + src/index.js wrapper the way a user's
`uses: buildrush/setup-php@v1` would — previously not covered
anywhere in CI.
Coverage gap this closes:
- ci.yml::pipeline exercises phpup as a CLI inside bare-ubuntu
containers (bundle build + fixture probe). It bypasses the
composite-action entry point entirely.
- A PR that broke action.yml (typo an input name, wrong output
binding, etc.) or src/index.js (input parsing regression,
misresolved cache path) would pass every existing job and still
ship a broken action.
How the new job runs the PR's phpup, not a released one:
src/index.js:runMain() checks existsSync($RUNNER_TOOL_CACHE/
buildrush-bin/phpup) before hitting the network. We `go build`
the PR's phpup binary directly into that cache path, then invoke
`uses: ./` — the wrapper finds the binary, skips the release
download, and the end-to-end path is: PR's action.yml → PR's
src/index.js → PR's phpup. Zero changes to src/index.js were
needed; the cache-hit path was the right hook.
Matrix is deliberately narrower than `pipeline`:
- 2 OS (jammy, noble) × 2 arch (amd64, arm64) × 1 PHP (8.4) = 4 cells
- Wrapper is OS-invariant and arch-variant; 4 cells catch the
real regressions without multiplying the existing 20-cell
`pipeline` cost.
Assertions per cell:
- php -v succeeds (smoke)
- extension_loaded("redis") + extension_loaded("intl")
(extensions input was honored + extensions loaded at runtime)
- ini_get("memory_limit") == "256M"
(ini-values input was parsed + applied)
Wiring:
- smoke-action needs: pipeline (fails fast if the CLI side broke)
- publish needs: [pipeline, smoke-action] (a broken wrapper on
main would otherwise publish a bundle the action can't use)
v2 drop-in compat (the coverage lost when compat-harness.yml was
deleted in PR #60) is deferred to a follow-up PR — this one keeps
the change surface small and easy to revert.
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fifth of the six-PR rollout in
docs/superpowers/specs/2026-04-23-local-ci-unification-design.md. PR 4 (#59) shipped the unifiedci.ymlrunning alongside the legacy pipeline; this PR retires the legacy side.Deleted (13 files):
build-php-core.yml,build-extension.yml,plan.yml,plan-and-build.yml,on-push.yml,integration-test.yml,compat-harness.yml,nightly.yml,manual.yml,gc-bundles.yml,bootstrap.yml(depended on deleted workflows),ci-lint.yml(folded intoci.yml::lint).test/smoke/local-ci.sh— replaced bymake ci-cell OS=… ARCH=… PHP=….Flipped:
ci.yml::publishdrops thevars.PHPUP_NEW_CI_PUBLISH == '1'gate and the(new ci.yml)marker in the lockfile commit message — it's now the sole GHCR + lockfile writer.Folded + retargeted:
ci.yml::lintgained thego vetstep from the deletedci-lint.yml. Everything else ci-lint used to cover (golangci-lint, gofmt, mod-tidy, go test, go build) was already exercised byci.yml'slint+unit+pipelinejobs.Makefile: droppedcheck-fast(became redundant withoutlocal-ci) and thelocal-citarget.make checkis the pre-push gate;make ci-cellreproduces one pipeline cell locally.Retained (per design):
ci.yml,release-please.yml,check-release-pr.yml(release engineering),security-rebuild.yml,watch-*.yml(orthogonal; re-triggerci.yml).Docs refreshed:
CLAUDE.mdQuality Gates + Testing sections point atmake ci-cellinstead of the deleted local-ci smoke.CONTRIBUTING.mdrefers toci.yml(notci-lint.yml) and notes gc-bundles is now manual on-demand.README.mdv2-compat section describes fixture coverage insideci.yml::pipelineinstead of the deletedcompat-harness.yml.cmd/{planner,gc-bundles,phpup},internal/build/,internal/planner/,internal/registry/that pointed at deleted workflow files have been refreshed to describe current behavior.Test plan
make check— green locallyci.ymlgreen on this PR (lint + unit + 16 Pipeline cells + publish job should only fire post-merge onmain)mainrunsci.yml::publish(previously gated off) — verify GHCR receives bundles and the lockfile-update commit lands onmain