Skip to content

Commit b090d30

Browse files
committed
ci(release): add gated workflow-dispatch release publisher
Add a manual release workflow that runs full validation and test matrix before creating the version tag and GitHub Release, preventing bad tags from reaching Packagist.
1 parent b3abd6b commit b090d30

1 file changed

Lines changed: 118 additions & 0 deletions

File tree

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
name: Prepare and Publish Release
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
version:
7+
description: "Release version without v prefix (example: 1.0.0)"
8+
required: true
9+
type: string
10+
ref:
11+
description: "Git ref to release (branch, tag, or commit SHA)"
12+
required: false
13+
default: "main"
14+
type: string
15+
prerelease:
16+
description: "Mark GitHub Release as prerelease"
17+
required: false
18+
default: false
19+
type: boolean
20+
confirm:
21+
description: "Type RELEASE to confirm publish"
22+
required: true
23+
type: string
24+
25+
permissions:
26+
contents: write
27+
28+
concurrency:
29+
group: release-${{ github.event.inputs.version }}
30+
cancel-in-progress: false
31+
32+
jobs:
33+
gate:
34+
name: Gate / P${{ matrix.php }} - ${{ matrix.stability }} - ${{ matrix.os }}
35+
runs-on: ${{ matrix.os }}
36+
timeout-minutes: 10
37+
strategy:
38+
fail-fast: false
39+
matrix:
40+
os: [ubuntu-latest, windows-latest]
41+
php: ["8.5", "8.4", "8.3", "8.2"]
42+
stability: [prefer-lowest, prefer-stable]
43+
44+
steps:
45+
- name: Validate release request
46+
if: matrix.os == 'ubuntu-latest' && matrix.php == '8.5' && matrix.stability == 'prefer-stable'
47+
shell: bash
48+
run: |
49+
test "${{ inputs.confirm }}" = "RELEASE" || {
50+
echo "confirm input must be exactly RELEASE"
51+
exit 1
52+
}
53+
[[ "${{ inputs.version }}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]] || {
54+
echo "version must be semver-like (for example: 1.0.0)"
55+
exit 1
56+
}
57+
58+
- name: Checkout code
59+
uses: actions/checkout@v6
60+
with:
61+
ref: ${{ inputs.ref }}
62+
63+
- name: Setup PHP
64+
uses: shivammathur/setup-php@v2
65+
with:
66+
php-version: ${{ matrix.php }}
67+
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo, xdebug
68+
coverage: xdebug
69+
70+
- name: Composer validate (strict)
71+
run: composer validate --strict
72+
73+
- name: Install dependencies
74+
run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction
75+
76+
- name: Security audit (canonical job only)
77+
if: matrix.os == 'ubuntu-latest' && matrix.php == '8.5' && matrix.stability == 'prefer-stable'
78+
run: composer audit --format=plain
79+
80+
- name: Execute tests
81+
run: vendor/bin/phpunit --coverage-clover coverage.xml
82+
83+
publish:
84+
name: Publish Tag and GitHub Release
85+
runs-on: ubuntu-latest
86+
needs: gate
87+
steps:
88+
- name: Checkout code
89+
uses: actions/checkout@v6
90+
with:
91+
fetch-depth: 0
92+
ref: ${{ inputs.ref }}
93+
94+
- name: Verify tag does not already exist
95+
shell: bash
96+
run: |
97+
TAG="v${{ inputs.version }}"
98+
if git ls-remote --exit-code --tags origin "refs/tags/${TAG}" >/dev/null 2>&1; then
99+
echo "Tag ${TAG} already exists on origin"
100+
exit 1
101+
fi
102+
103+
- name: Create and publish release
104+
env:
105+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
106+
shell: bash
107+
run: |
108+
TAG="v${{ inputs.version }}"
109+
FLAGS=""
110+
if [ "${{ inputs.prerelease }}" = "true" ]; then
111+
FLAGS="${FLAGS} --prerelease"
112+
fi
113+
114+
gh release create "${TAG}" \
115+
--target "${{ inputs.ref }}" \
116+
--title "${TAG}" \
117+
--generate-notes \
118+
${FLAGS}

0 commit comments

Comments
 (0)