Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changelog/actions-tagger-release-channels.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
category: changed
type: patch
---

Replace the repository PHP channel updater with actions-tagger@v2 on stable published/edited release events in branch mode; select the newest stable tag per major before promoting v0/v1 branches and enable the native latest tag only for the global newest version. Publication credentials and channel protections require separate approval.
3 changes: 0 additions & 3 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@
# Contributor-only configuration and test infrastructure.
/tests/ export-ignore
/.github/workflows/ export-ignore
/.github/ReleaseChannelUpdater.php export-ignore
/.github/update-release-channels.php export-ignore
/.github/update-release-channels.md export-ignore
/AGENTS.md export-ignore
/skills.json export-ignore
/phpunit.xml export-ignore
Expand Down
572 changes: 0 additions & 572 deletions .github/ReleaseChannelUpdater.php

This file was deleted.

72 changes: 0 additions & 72 deletions .github/update-release-channels.md

This file was deleted.

41 changes: 0 additions & 41 deletions .github/update-release-channels.php

This file was deleted.

11 changes: 0 additions & 11 deletions .github/workflows/repository-changelog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,3 @@ jobs:
source: tags
prepare-release: true
publish: true
release-channels:
needs: changelog
if: >-
needs.changelog.outputs.published-version != '' &&
needs.changelog.outputs.published-sha != ''
permissions:
contents: write
uses: ./.github/workflows/repository-release-channels.yml
with:
tag: ${{ needs.changelog.outputs.published-tag }}
expected-sha: ${{ needs.changelog.outputs.published-sha }}
134 changes: 48 additions & 86 deletions .github/workflows/repository-release-channels.yml
Original file line number Diff line number Diff line change
@@ -1,100 +1,62 @@
name: 'Repository: Release Channels'
on:
workflow_call:
inputs:
tag:
description: Existing published stable 0.x full release tag.
type: string
required: true
expected-sha:
description: Exact commit of the verified published release.
type: string
required: true
workflow_dispatch:
inputs:
tag:
description: Existing published stable 0.x full release tag.
type: string
required: true
expected-sha:
description: Exact commit of the verified published release.
type: string
required: true
release:
types: [published, edited]
permissions: {}
concurrency:
group: release-channels-${{ github.repository }}
cancel-in-progress: false
jobs:
channel:
if: >-
github.repository == 'php-fast-forward/code-standards' &&
github.event.release.draft == false && github.event.release.prerelease == false
runs-on: ubuntu-latest
timeout-minutes: 10
timeout-minutes: 5
environment: release-channels
permissions:
contents: write
contents: read
steps:
- name: Require a trusted repository workflow
id: runtime
- name: Select the newest stable release for its major
id: release
env:
FF_REPOSITORY: ${{ github.repository }}
FF_JOB_JSON: ${{ toJSON(job) }}
FF_EVENT: ${{ github.event_name }}
FF_REF: ${{ github.ref }}
FF_ACTION: ${{ github.event.action }}
FF_MERGED: ${{ github.event.pull_request.merged }}
FF_HEAD_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name }}
FF_HEAD_BRANCH: ${{ github.event.pull_request.head.ref }}
FF_BASE_BRANCH: ${{ github.event.pull_request.base.ref }}
FF_MERGE_SHA: ${{ github.event.pull_request.merge_commit_sha }}
FF_EXPECTED_SHA: ${{ inputs.expected-sha }}
run: |
set -euo pipefail
if [[ "$FF_REPOSITORY" != php-fast-forward/code-standards ]]; then
echo '::error::Release channels belong only to the canonical repository.'
exit 1
fi
if [[ "$FF_EVENT" == workflow_dispatch ]]; then
[[ "$FF_REF" == refs/heads/main ]] || exit 1
elif [[ "$FF_EVENT" != pull_request_target || "$FF_ACTION" != closed \
|| "$FF_MERGED" != true || "$FF_HEAD_REPOSITORY" != "$FF_REPOSITORY" \
|| "$FF_HEAD_BRANCH" != changelog/version || "$FF_BASE_BRANCH" != main \
|| "$FF_MERGE_SHA" != "$FF_EXPECTED_SHA" ]]; then
echo '::error::Use verified managed-merge publication or manual recovery from main.'
exit 1
fi
runtime=$(jq --slurp --raw-output --exit-status '
select(length == 1) | .[0] | select(type == "object")
| select(.workflow_repository | type == "string")
| select(.workflow_repository == "php-fast-forward/code-standards")
| select(.workflow_sha | type == "string")
| select(.workflow_sha | length == 40 and test("^[a-f0-9]+$"))
| .workflow_sha
' <<< "$FF_JOB_JSON") || {
echo '::error::Invalid executing workflow identity in native job context.' >&2
exit 1
}
{
printf 'repository=php-fast-forward/code-standards\n'
printf 'sha=%s\n' "$runtime"
} >> "$GITHUB_OUTPUT"
- name: Check out this exact trusted implementation
uses: actions/checkout@v7
FF_RELEASE_TAG: ${{ github.event.release.tag_name }}
uses: actions/github-script@v8
with:
repository: ${{ steps.runtime.outputs.repository }}
ref: ${{ steps.runtime.outputs.sha }}
persist-credentials: false
- uses: shivammathur/setup-php@v2
script: |
const tag = process.env.FF_RELEASE_TAG;
const version = name => {
const match = /^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$/.exec(name);
return match?.[0] === name ? match.slice(1).map(BigInt) : null;
};
const selected = version(tag);
if (!selected) throw new Error('The release version cannot be compared.');
const tags = await github.paginate(github.rest.repos.listTags, { ...context.repo, per_page: 100 });
const compare = (left, right) => {
for (let index = 0; index < 3; ++index) {
if (left[index] !== right[index]) return left[index] > right[index] ? 1 : -1;
}
return 0;
};
const stable = tags.map(item => version(item.name)).filter(Boolean);
core.setOutput('promote', !stable.some(item => item[0] === selected[0] && compare(item, selected) > 0));
core.setOutput('latest', !stable.some(item => compare(item, selected) > 0));
- name: Create the repository-scoped channel token
if: steps.release.outputs.promote == 'true'
id: channel-app
uses: actions/create-github-app-token@v3.3.0
with:
php-version: '8.3'
coverage: none
tools: composer:v2
- name: Install repository-only verification dependencies
env:
COMPOSER_HOME: ${{ runner.temp }}/release-channel-composer-home
COMPOSER_CACHE_DIR: ${{ runner.temp }}/release-channel-composer-cache
run: composer install --no-plugins --no-scripts --no-interaction --prefer-dist --no-progress
- name: Advance the verified released v0 branch
env:
GH_TOKEN: ${{ github.token }}
FF_REPOSITORY: ${{ github.repository }}
FF_TAG: ${{ inputs.tag }}
FF_EXPECTED_SHA: ${{ inputs.expected-sha }}
run: php .github/update-release-channels.php "$FF_REPOSITORY" "$FF_TAG" "$FF_EXPECTED_SHA"
client-id: ${{ vars.RELEASE_CHANNEL_APP_CLIENT_ID }}
private-key: ${{ secrets.app-private-key }}
owner: php-fast-forward
repositories: code-standards
permission-contents: write
permission-workflows: write
skip-token-revoke: 'false'
- name: Update the released major branch
if: steps.release.outputs.promote == 'true'
uses: Actions-R-Us/actions-tagger@v2
with:
token: ${{ steps.channel-app.outputs.token }}
prefer_branch_releases: 'true'
publish_latest_tag: ${{ steps.release.outputs.latest }}
7 changes: 4 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,10 @@ reusables under `.github/workflows/code-standards*.yml`, own event workflows use
- Consumers use the verified released `v0` channel or a reviewed immutable commit.
Bind runtime/helper checkout to the called job's actual workflow repository/SHA,
never another moving-ref lookup. Own CI must work before v0 exists. Only
confirmed 0.x publications may advance v0, with an exact old-OID lease and no
force/regression. GitHub channel refs are not Composer/CPX constraints.
confirmed stable publications may promote their matching major channel. The
actions-tagger branch/tag integration requires stable-version selection and
approved publication/name-reservation setup; its mutation has no CAS.
GitHub channel refs are not Composer/CPX constraints.

## Work Guidance

Expand Down Expand Up @@ -89,7 +91,6 @@ for the exact local base/head before publication; do not claim remote CI evidenc
- [Static-analysis evaluation](docs/static-analysis-evaluation.rst): candidate engines, whole-project analysis and unresolved domain ownership.
- [Architecture decision](docs/architecture.rst): library/tool dependencies, CPX/autoload limits and bootstrap without circular prerequisites.
- [Automation](docs/automation.rst): trust, ref/bootstrap contracts and consumer adoption.
- [Released channel helper](.github/update-release-channels.md): repository-only publication verification, CAS update and permission-failure recovery; never creates releases or changes repository settings.
- [Instruction map](docs/instruction-map.rst): single root scope and skill ownership.
- [Consumer skill](resources/skills/fast-forward-code-standards/SKILL.md): portable consumption procedure; no automatic installation or rollout.
- [PHPDoc skill](resources/skills/fast-forward-phpdoc/SKILL.md): concise source documentation and implementation-independent semantic contracts; no global installation required.
3 changes: 1 addition & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@
"require-dev": {
"llm/skills": "^1.15",
"phpunit/phpunit": "^12.5",
"symfony/filesystem": "^7.4 || ^8.0",
"symfony/http-client": "^7.4 || ^8.0"
"symfony/filesystem": "^7.4 || ^8.0"
},
"suggest": {
"ext-dom": "Generate native XML reports for optional magic-number analysis.",
Expand Down
Loading
Loading