Skip to content

Commit 76ca8be

Browse files
committed
GH Actions: automate release verification steps
While the actual releasing can not be fully automated (due to security concerns related to signing releases in a GHA workflow), there are a number of verification checks which are part of the release workflow, which _can_ be automated. These checks were previously done as manual spot-checks. With the new workflow, they will now be executed structurally and automatically whenever a release is published on GitHub. The checks which are automated via this workflow are as follows: * For the PHAR files which can be downloaded from the GH "releases" page, the (unversioned) PHAR files published on the GH Pages website and the versioned PHAR files published on the GH Pages website for Phive (but which can also be downloaded manually), the following checks will now be run automatically: - Is the PHAR file available and can it be downloaded ? - Is the ASC (detached signature) file available and can it be downloaded ? - Verify the PHAR file via the attestation (which was created when the PHAR was created for a tag). - Verify the PHAR file is GPG signed and the signature matches. - Verify the PHAR file is functional (simple command runs without problems). - Verify the version with which the PHAR file identifies itself is the expected version. * For Phive: - Install via Phive. This will automatically also check the PHAR file is signed and the signature matches. - Verify the Phive installed PHAR file via the attestation. - Verify the Phive installed PHAR file is functional (simple command runs without problems). - Verify the version with which the PHAR file identifies itself is the expected version.
1 parent 0d969c9 commit 76ca8be

File tree

1 file changed

+197
-0
lines changed

1 file changed

+197
-0
lines changed

.github/workflows/verify-release.yml

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
name: Verify release
2+
3+
on:
4+
# Run whenever a release is published.
5+
release:
6+
types: [published]
7+
# And whenever this workflow is updated.
8+
push:
9+
paths:
10+
- '.github/workflows/verify-release.yml'
11+
pull_request:
12+
paths:
13+
- '.github/workflows/verify-release.yml'
14+
# Allow manually triggering the workflow.
15+
workflow_dispatch:
16+
17+
# Cancels all previous workflow runs for the same branch that have not yet completed.
18+
concurrency:
19+
# The concurrency group contains the workflow name and the branch name.
20+
group: ${{ github.workflow }}-${{ github.ref }}
21+
cancel-in-progress: true
22+
23+
jobs:
24+
##################################################################################
25+
# Verify the release is available in all the right places and works as expected. #
26+
##################################################################################
27+
verify-available-downloads:
28+
runs-on: ubuntu-latest
29+
30+
strategy:
31+
fail-fast: false
32+
matrix:
33+
download_flavour:
34+
- "Release assets"
35+
- "Unversioned web"
36+
- "Versioned web"
37+
pharfile:
38+
- 'phpcs'
39+
- 'phpcbf'
40+
41+
name: "${{ matrix.download_flavour }}: ${{ matrix.pharfile }}"
42+
43+
steps:
44+
- name: Retrieve latest release info
45+
uses: octokit/request-action@v2.x
46+
id: get_latest_release
47+
with:
48+
route: GET /repos/PHPCSStandards/PHP_CodeSniffer/releases/latest
49+
env:
50+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
51+
52+
- name: "DEBUG: Show API request failure status"
53+
if: ${{ failure() }}
54+
run: "echo No release found. Request failed with status ${{ steps.get_latest_release.outputs.status }}"
55+
56+
- name: Grab latest tag name from API response
57+
id: version
58+
run: |
59+
echo "TAG=${{ fromJson(steps.get_latest_release.outputs.data).tag_name }}" >> "$GITHUB_OUTPUT"
60+
61+
- name: "DEBUG: Show tag name found in API response"
62+
run: "echo ${{ steps.version.outputs.TAG }}"
63+
64+
- name: Set source URL and file name
65+
id: source
66+
shell: bash
67+
run: |
68+
if [[ "${{ matrix.download_flavour }}" == "Release assets" ]]; then
69+
echo 'SRC=https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/latest/download/' >> "$GITHUB_OUTPUT"
70+
echo "FILE=${{ matrix.pharfile }}.phar" >> "$GITHUB_OUTPUT"
71+
elif [[ "${{ matrix.download_flavour }}" == "Unversioned web" ]]; then
72+
echo 'SRC=https://phars.phpcodesniffer.com/' >> "$GITHUB_OUTPUT"
73+
echo "FILE=${{ matrix.pharfile }}.phar" >> "$GITHUB_OUTPUT"
74+
else
75+
echo 'SRC=https://phars.phpcodesniffer.com/phars/' >> "$GITHUB_OUTPUT"
76+
echo "FILE=${{ matrix.pharfile }}-${{ steps.version.outputs.TAG }}.phar" >> "$GITHUB_OUTPUT"
77+
fi
78+
79+
- name: Verify PHAR file is available and download
80+
run: "wget -O ${{ steps.source.outputs.FILE }} ${{ steps.source.outputs.SRC }}${{ steps.source.outputs.FILE }}"
81+
82+
- name: Verify signature file is available and download
83+
run: "wget -O ${{ steps.source.outputs.FILE }}.asc ${{ steps.source.outputs.SRC }}${{ steps.source.outputs.FILE }}.asc"
84+
85+
- name: "DEBUG: List files"
86+
run: ls -Rlh
87+
88+
- name: Verify attestation of the PHAR file
89+
run: gh attestation verify ${{ steps.source.outputs.FILE }} -o PHPCSStandards
90+
env:
91+
GH_TOKEN: ${{ github.token }}
92+
93+
- name: Download public key
94+
env:
95+
FINGERPRINT: "0x689DAD778FF08760E046228BA978220305CD5C32"
96+
run: gpg --keyserver "hkps://keys.openpgp.org" --recv-keys "$FINGERPRINT"
97+
98+
- name: Verify signature of the PHAR file
99+
run: gpg --verify ${{ steps.source.outputs.FILE }}.asc ${{ steps.source.outputs.FILE }}
100+
101+
- name: Setup PHP
102+
uses: shivammathur/setup-php@v2
103+
with:
104+
php-version: 'latest'
105+
ini-values: error_reporting=-1, display_errors=On
106+
coverage: none
107+
108+
# Note: the `.` is in the command to make it work for both PHPCS as well PHPCBF.
109+
- name: Verify the PHAR is nominally functional
110+
run: php ${{ steps.source.outputs.FILE }} . -e --standard=PSR12
111+
112+
- name: Grab the version
113+
id: asset_version
114+
env:
115+
FILE_NAME: ${{ steps.source.outputs.FILE }}
116+
# yamllint disable-line rule:line-length
117+
run: echo "VERSION=$(php "$FILE_NAME" --version | grep --only-matching --max-count=1 --extended-regexp '\b[0-9]+(\.[0-9]+)+')" >> "$GITHUB_OUTPUT"
118+
119+
- name: "DEBUG: Show grabbed version"
120+
run: echo ${{ steps.asset_version.outputs.VERSION }}
121+
122+
- name: Fail the build if the PHAR is not the correct version
123+
if: ${{ steps.asset_version.outputs.VERSION != steps.version.outputs.TAG }}
124+
run: exit 1
125+
126+
# #########################################
127+
# Verify install via PHIVE.
128+
# #########################################
129+
verify-phive:
130+
runs-on: ubuntu-latest
131+
132+
strategy:
133+
fail-fast: false
134+
matrix:
135+
pharfile:
136+
- 'phpcs'
137+
- 'phpcbf'
138+
139+
name: "PHIVE: ${{ matrix.pharfile }}"
140+
141+
steps:
142+
- name: Retrieve latest release info
143+
uses: octokit/request-action@v2.x
144+
id: get_latest_release
145+
with:
146+
route: GET /repos/PHPCSStandards/PHP_CodeSniffer/releases/latest
147+
env:
148+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
149+
150+
- name: "DEBUG: Show API request failure status"
151+
if: ${{ failure() }}
152+
run: "echo No release found. Request failed with status ${{ steps.get_latest_release.outputs.status }}"
153+
154+
- name: Grab latest tag name from API response
155+
id: version
156+
run: |
157+
echo "TAG=${{ fromJson(steps.get_latest_release.outputs.data).tag_name }}" >> "$GITHUB_OUTPUT"
158+
159+
- name: "DEBUG: Show tag name found in API response"
160+
run: "echo ${{ steps.version.outputs.TAG }}"
161+
162+
- name: Setup PHP
163+
uses: shivammathur/setup-php@v2
164+
with:
165+
php-version: 'latest'
166+
ini-values: error_reporting=-1, display_errors=On
167+
coverage: none
168+
tools: phive
169+
170+
- name: Install
171+
run: phive install ${{ matrix.pharfile }} --copy --trust-gpg-keys 689DAD778FF08760E046228BA978220305CD5C32
172+
173+
- name: "DEBUG: List files"
174+
run: ls -R
175+
176+
- name: Verify attestation of the PHAR file
177+
run: gh attestation verify ./tools/${{ matrix.pharfile }} -o PHPCSStandards
178+
env:
179+
GH_TOKEN: ${{ github.token }}
180+
181+
# Note: the `.` is in the command to make it work for both PHPCS as well PHPCBF.
182+
- name: Verify the PHAR is nominally functional
183+
run: php ./tools/${{ matrix.pharfile }} . -e --standard=PSR12
184+
185+
- name: Grab the version
186+
id: asset_version
187+
env:
188+
FILE_NAME: ./tools/${{ matrix.pharfile }}
189+
# yamllint disable-line rule:line-length
190+
run: echo "VERSION=$(php "$FILE_NAME" --version | grep --only-matching --max-count=1 --extended-regexp '\b[0-9]+(\.[0-9]+)+')" >> "$GITHUB_OUTPUT"
191+
192+
- name: "DEBUG: Show grabbed version"
193+
run: echo ${{ steps.asset_version.outputs.VERSION }}
194+
195+
- name: Fail the build if the PHAR is not the correct version
196+
if: ${{ steps.asset_version.outputs.VERSION != steps.version.outputs.TAG }}
197+
run: exit 1

0 commit comments

Comments
 (0)