Problem
Consumer repositories currently synchronize thin workflow wrappers from resources/github-actions/*.yml, and those wrappers call reusable workflows from this repository using php-fast-forward/dev-tools/.github/workflows/<workflow>.yml@main.
Inside the reusable workflows, when local composite actions are needed, the workflow sparse-checks out .github/actions from php-fast-forward/dev-tools into .dev-tools-actions. In consumer runs that checkout currently resolves against main as well.
That means a consumer repository can run automation from the current development state of dev-tools, not necessarily from the latest published fast-forward/dev-tools release installed in that consumer. If main contains workflow or action changes that have not been released yet, consumers can see behavior that does not match the package version they depend on.
Why This Matters
This creates a subtle version skew across the ecosystem:
- the consumer's Composer dependency may be pinned to a released
fast-forward/dev-tools version;
- the consumer's copied workflow wrapper may still point to reusable workflows on
@main;
- the reusable workflow may checkout local action implementations from
main;
- the action implementation may expect PHP classes, scripts, files, or command behavior that only exist on
main, not in the consumer's installed release.
The result can be hard-to-debug CI anomalies where the workflow logic and the package code are from different points in time.
Current Surfaces
Examples of the current pattern:
resources/github-actions/tests.yml calls php-fast-forward/dev-tools/.github/workflows/tests.yml@main.
resources/github-actions/changelog.yml calls php-fast-forward/dev-tools/.github/workflows/changelog.yml@main.
resources/github-actions/reports.yml, wiki.yml, wiki-maintenance.yml, review.yml, auto-assign.yml, and label-sync.yml follow the same wrapper model.
- reusable workflows then checkout
.github/actions into .dev-tools-actions, commonly resolving php-fast-forward/dev-tools at main for consumer repositories.
- docs currently describe the sparse checkout model, but do not define a release/version contract for which ref should be used.
Proposed Direction
Define and implement a versioning policy for consumer-facing reusable workflows and the local action source they checkout.
This issue should evaluate at least these approaches:
-
Pin action checkout refs to the latest published release
- Reusable workflows could checkout
.github/actions at the same released tag as the reusable workflow ref.
- This reduces unreleased action drift, but we need a reliable way to know the intended ref from inside the workflow.
-
Pin consumer wrappers to released tags instead of @main
resources/github-actions/*.yml could be rendered with a concrete @vX.Y.Z ref during release or sync.
- Dependabot could then manage workflow updates in consumers similarly to dependency updates.
- Release automation would need to guarantee wrappers are updated consistently when a new DevTools release is prepared.
-
Keep wrappers on @main, but pin only .github/actions checkout
- This reduces action-source drift while preserving central reusable workflow updates.
- It still leaves reusable workflow YAML on
main, so it may not fully solve version skew.
-
Expose a workflow input for dev-tools-ref
- Consumer wrappers could pass
dev-tools-ref explicitly.
- Defaults could remain
main for development, but generated consumer wrappers could use a release tag.
- This may be the most flexible route, but it requires touching every reusable workflow that checks out
.github/actions.
-
Release-managed wrapper regeneration
- During release preparation, update
resources/github-actions/*.yml to point at the release tag that is about to be published.
- Ensure the packaged/synchronized workflow wrappers and docs explain how consumers receive updates.
- Evaluate whether Dependabot should be configured to raise PRs when these
uses: php-fast-forward/dev-tools/...@vX.Y.Z references lag behind.
Implementation Strategy
Start with a design pass before changing every workflow:
- inventory every consumer wrapper under
resources/github-actions/ and every reusable workflow under .github/workflows/ that sparse-checks out .github/actions;
- decide whether the authoritative ref should be
main, latest release tag, Composer package version, or an explicit wrapper input;
- define how release automation updates the chosen ref;
- define how consumer repositories receive and update that ref through
dev-tools:sync and/or Dependabot;
- update documentation so the release/version contract is explicit;
- only then apply the workflow changes consistently.
Requirements
- Consumer repositories MUST NOT unexpectedly execute unreleased
.github/actions implementations when their wrappers are intended to represent a released DevTools version.
- The selected policy MUST be consistent across all packaged workflow wrappers, not only one workflow.
- Reusable workflows that checkout
.github/actions MUST use a deterministic ref that matches the selected policy.
- Release automation MUST keep generated wrappers, reusable workflow refs, and documentation aligned.
- The solution SHOULD preserve a convenient development path for this repository's own PRs, where workflows need to test in-branch changes before release.
- The solution SHOULD explain how Dependabot or another automation will update workflow refs in consumers if the final design uses versioned tags.
- The solution MUST avoid requiring consumers to manually edit every workflow after each DevTools release.
Open Questions
- Should consumer wrappers point at
@main, immutable release tags such as @v1.21.0, or a moving major/minor tag such as @v1?
- If wrappers use release tags, should
dev-tools:sync rewrite them to the installed Composer package version or to the latest known upstream release?
- Can Dependabot reliably update reusable workflow references that point to
php-fast-forward/dev-tools/.github/workflows/*.yml@vX.Y.Z?
- Should
.github/actions checkout use the same ref as the reusable workflow, or should it have an independent input/default?
- How do we handle this repository's own workflows so PRs can still validate unreleased action changes before those actions are tagged?
- Should release preparation fail if packaged workflow wrappers still point to
@main or to a stale release ref?
Non-goals
- Redesigning the workflow architecture unrelated to version/ref consistency.
- Removing reusable workflows or going back to copying full workflow implementations into every consumer.
- Solving arbitrary consumer workflow customization drift in the same change.
Acceptance Criteria
Functional Criteria
Regression Criteria
Problem
Consumer repositories currently synchronize thin workflow wrappers from
resources/github-actions/*.yml, and those wrappers call reusable workflows from this repository usingphp-fast-forward/dev-tools/.github/workflows/<workflow>.yml@main.Inside the reusable workflows, when local composite actions are needed, the workflow sparse-checks out
.github/actionsfromphp-fast-forward/dev-toolsinto.dev-tools-actions. In consumer runs that checkout currently resolves againstmainas well.That means a consumer repository can run automation from the current development state of
dev-tools, not necessarily from the latest publishedfast-forward/dev-toolsrelease installed in that consumer. Ifmaincontains workflow or action changes that have not been released yet, consumers can see behavior that does not match the package version they depend on.Why This Matters
This creates a subtle version skew across the ecosystem:
fast-forward/dev-toolsversion;@main;main;main, not in the consumer's installed release.The result can be hard-to-debug CI anomalies where the workflow logic and the package code are from different points in time.
Current Surfaces
Examples of the current pattern:
resources/github-actions/tests.ymlcallsphp-fast-forward/dev-tools/.github/workflows/tests.yml@main.resources/github-actions/changelog.ymlcallsphp-fast-forward/dev-tools/.github/workflows/changelog.yml@main.resources/github-actions/reports.yml,wiki.yml,wiki-maintenance.yml,review.yml,auto-assign.yml, andlabel-sync.ymlfollow the same wrapper model..github/actionsinto.dev-tools-actions, commonly resolvingphp-fast-forward/dev-toolsatmainfor consumer repositories.Proposed Direction
Define and implement a versioning policy for consumer-facing reusable workflows and the local action source they checkout.
This issue should evaluate at least these approaches:
Pin action checkout refs to the latest published release
.github/actionsat the same released tag as the reusable workflow ref.Pin consumer wrappers to released tags instead of
@mainresources/github-actions/*.ymlcould be rendered with a concrete@vX.Y.Zref during release or sync.Keep wrappers on
@main, but pin only.github/actionscheckoutmain, so it may not fully solve version skew.Expose a workflow input for
dev-tools-refdev-tools-refexplicitly.mainfor development, but generated consumer wrappers could use a release tag..github/actions.Release-managed wrapper regeneration
resources/github-actions/*.ymlto point at the release tag that is about to be published.uses: php-fast-forward/dev-tools/...@vX.Y.Zreferences lag behind.Implementation Strategy
Start with a design pass before changing every workflow:
resources/github-actions/and every reusable workflow under.github/workflows/that sparse-checks out.github/actions;main, latest release tag, Composer package version, or an explicit wrapper input;dev-tools:syncand/or Dependabot;Requirements
.github/actionsimplementations when their wrappers are intended to represent a released DevTools version..github/actionsMUST use a deterministic ref that matches the selected policy.Open Questions
@main, immutable release tags such as@v1.21.0, or a moving major/minor tag such as@v1?dev-tools:syncrewrite them to the installed Composer package version or to the latest known upstream release?php-fast-forward/dev-tools/.github/workflows/*.yml@vX.Y.Z?.github/actionscheckout use the same ref as the reusable workflow, or should it have an independent input/default?@mainor to a stale release ref?Non-goals
Acceptance Criteria
Functional Criteria
.github/actionscheckout refs.resources/github-actions/follow the chosen policy consistently..github/actionsuse the selected deterministic ref strategy.dev-tools:sync, Dependabot, release automation, or a combination.Regression Criteria
@mainusage where the selected policy forbids it.