Skip to content

Commit d651d51

Browse files
authored
Lint, audit and harden build workflow (#11)
* Add CI workflow with actionlint and zizor * Explicitly declare build workflow permissions * Pin all actions to commit hashes * Do not persist actions/checkout credentials * Improve release title and notes * Include source distribution in release assets
1 parent 4cf9891 commit d651d51

3 files changed

Lines changed: 139 additions & 25 deletions

File tree

.github/workflows/build.yml

Lines changed: 63 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ on:
55
ref:
66
description: PyInstaller tag/ref
77
required: true
8-
default: v6.17.0
8+
default: 3f596f66feebe3a7d247248f95f76c071d08b832 # v6.17.0
99
type: string
1010
x64:
1111
description: Build Windows x64 / win_amd64
@@ -20,28 +20,19 @@ on:
2020
default: true
2121
type: boolean
2222

23-
permissions:
24-
contents: read
23+
permissions: {}
2524

2625
jobs:
2726
prepare:
2827
name: Prepare
2928
runs-on: ubuntu-latest
3029
outputs:
31-
head_sha: ${{ steps.get_target.outputs.head_sha }}
3230
matrix: ${{ steps.build_matrix.outputs.matrix }}
3331
release_title: ${{ steps.release_info.outputs.release_title }}
3432
release_tag: ${{ steps.release_info.outputs.release_tag }}
3533
release_notes: ${{ steps.release_info.outputs.release_notes }}
3634

3735
steps:
38-
- uses: actions/checkout@v6
39-
40-
- name: Get target commitish
41-
id: get_target
42-
run: |
43-
echo "head_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
44-
4536
- name: Build matrix
4637
id: build_matrix
4738
env:
@@ -84,31 +75,79 @@ jobs:
8475
with open(os.environ['GITHUB_OUTPUT'], 'a') as f:
8576
f.write(f'matrix={json.dumps(matrix)}')
8677
78+
- name: Checkout
79+
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
80+
with:
81+
fetch-depth: 0 # Needed for git-describe
82+
repository: pyinstaller/pyinstaller
83+
ref: ${{ inputs.ref }}
84+
persist-credentials: false
85+
86+
- name: Git describe
87+
id: git_describe
88+
shell: bash
89+
run: |
90+
git describe --tags # So the script will exit on error
91+
echo "tag=$(git describe --tags)" >> "${GITHUB_OUTPUT}"
92+
8793
- name: Generate release info
8894
id: release_info
8995
env:
9096
REF: ${{ inputs.ref }}
97+
TAG: ${{ steps.git_describe.outputs.tag }}
9198
MATRIX: ${{ steps.build_matrix.outputs.matrix }}
9299
shell: python
93100
run: |
94101
import datetime as dt
95102
import json
96103
import os
104+
import re
97105
ref = os.environ['REF']
106+
tag = os.environ['TAG']
98107
builds = ', '.join(element['name'] for element in json.loads(os.environ['MATRIX']))
99108
timestamp = dt.datetime.now(tz=dt.timezone.utc).strftime('%Y.%m.%d.%H%M%S')
109+
notes = ['PyInstaller']
110+
if re.fullmatch(r'v\d+\.\d+\.\d+', tag):
111+
notes.append(f'[{tag}](https://github.com/pyinstaller/pyinstaller/releases/tag/{tag})')
112+
else:
113+
notes.append(tag)
114+
notes.append(f'builds for Windows {builds}')
115+
if ref != tag:
116+
if re.fullmatch(r'[\da-f]+', ref):
117+
notes.append(f'(from https://github.com/pyinstaller/pyinstaller/commit/{ref})')
118+
else:
119+
notes.append('from {ref}')
100120
outputs = {
101-
'release_title': f'{ref} @ {timestamp}',
121+
'release_title': f'{tag} @ {timestamp}',
102122
'release_tag': timestamp,
103-
'release_notes': f'PyInstaller {ref} builds for Windows {builds}',
123+
'release_notes': ' '.join(notes),
104124
}
105125
print(json.dumps(outputs, indent=2))
106126
with open(os.environ['GITHUB_OUTPUT'], 'a') as f:
107127
f.write('\n'.join(f'{key}={value}' for key, value in outputs.items()))
108128
129+
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
130+
with:
131+
python-version: "3.14"
132+
133+
- name: Build source distribution
134+
run: |
135+
python -m pip install -U build hatchling
136+
python -m build --no-isolation --sdist --outdir=dist .
137+
138+
- name: Upload artifacts
139+
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
140+
with:
141+
name: pyinstaller-${{ inputs.ref }}-sdist
142+
path: |
143+
dist/*
144+
compression-level: 0
145+
109146
build:
110147
name: Build ${{ matrix.name }} package
111-
needs: prepare
148+
needs: [prepare]
149+
permissions:
150+
contents: read
112151
runs-on: ${{ matrix.runner }}
113152
strategy:
114153
fail-fast: false
@@ -120,7 +159,7 @@ jobs:
120159

121160
steps:
122161
- name: Set up MinGW
123-
uses: yt-dlp/setup-msys2@v2
162+
uses: yt-dlp/setup-msys2@4f806de0a5a7294ffabaff804b38a9b435a73bda # v2.30.0
124163
with:
125164
update: true
126165
msystem: ${{ matrix.sys }}
@@ -131,10 +170,11 @@ jobs:
131170
mingw-w64-${{ matrix.env }}-python-pip
132171
133172
- name: Checkout
134-
uses: actions/checkout@v6
173+
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
135174
with:
136175
repository: pyinstaller/pyinstaller
137176
ref: ${{ inputs.ref }}
177+
persist-credentials: false
138178

139179
- name: Build bootloader
140180
env:
@@ -144,7 +184,7 @@ jobs:
144184
cd bootloader
145185
python ./waf --target-arch="${ARCH}" --"${COMPILER}" distclean all
146186
147-
- name: Build package
187+
- name: Build wheel
148188
env:
149189
PYI_WHEEL_TAG: ${{ matrix.wheel_tag }}
150190
PYI_PLATFORM: ${{ matrix.platform }}
@@ -153,7 +193,7 @@ jobs:
153193
python -m build --no-isolation --wheel --outdir=dist .
154194
155195
- name: Upload artifacts
156-
uses: actions/upload-artifact@v4
196+
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
157197
with:
158198
name: pyinstaller-${{ inputs.ref }}-${{ matrix.name }}
159199
path: |
@@ -164,23 +204,22 @@ jobs:
164204
name: Release
165205
needs: [prepare, build]
166206
permissions:
167-
contents: write
207+
contents: write # Needed by gh to publish release to Github
168208
runs-on: ubuntu-latest
169209
env:
170-
HEAD_SHA: ${{ needs.prepare.outputs.head_sha }}
171210
RELEASE_TITLE: ${{ needs.prepare.outputs.release_title }}
172211
RELEASE_TAG: ${{ needs.prepare.outputs.release_tag }}
173212
RELEASE_NOTES: ${{ needs.prepare.outputs.release_notes }}
174213

175214
steps:
176-
- uses: actions/checkout@v6
215+
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
177216
with:
178-
fetch-depth: 0
217+
persist-credentials: false
179218

180-
- uses: actions/download-artifact@v4
219+
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
181220
with:
182221
path: artifact
183-
pattern: pyinstaller-*
222+
pattern: pyinstaller-${{ inputs.ref }}-*
184223
merge-multiple: true
185224

186225
- name: Publish release
@@ -189,7 +228,6 @@ jobs:
189228
run: |
190229
gh release create \
191230
--notes "${RELEASE_NOTES}" \
192-
--target "${HEAD_SHA}" \
193231
--title "${RELEASE_TITLE}" \
194232
"${RELEASE_TAG}" \
195233
artifact/*

.github/workflows/ci.yml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
name: Lint and audit
2+
on:
3+
push:
4+
branches: [master]
5+
paths:
6+
- .github/*.yml
7+
- .github/workflows/*
8+
pull_request:
9+
branches: [master]
10+
paths:
11+
- .github/*.yml
12+
- .github/workflows/*
13+
14+
permissions: {}
15+
16+
concurrency:
17+
group: ci-${{ github.event.pull_request.number || github.ref }}
18+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
19+
20+
env:
21+
ACTIONLINT_VERSION: "1.7.9"
22+
ACTIONLINT_SHA256SUM: 233b280d05e100837f4af1433c7b40a5dcb306e3aa68fb4f17f8a7f45a7df7b4
23+
ACTIONLINT_REPO: https://github.com/rhysd/actionlint
24+
25+
jobs:
26+
actionlint:
27+
name: actionlint
28+
permissions:
29+
contents: read
30+
runs-on: ubuntu-latest
31+
steps:
32+
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
33+
with:
34+
persist-credentials: false
35+
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
36+
with:
37+
python-version: "3.14"
38+
- name: Install requirements
39+
env:
40+
ACTIONLINT_TARBALL: ${{ format('actionlint_{0}_linux_amd64.tar.gz', env.ACTIONLINT_VERSION) }}
41+
shell: bash
42+
run: |
43+
sudo apt -y install shellcheck
44+
python -m pip install -U pyflakes
45+
curl -LO "${ACTIONLINT_REPO}/releases/download/v${ACTIONLINT_VERSION}/${ACTIONLINT_TARBALL}"
46+
printf '%s %s' "${ACTIONLINT_SHA256SUM}" "${ACTIONLINT_TARBALL}" | sha256sum -c -
47+
tar xvzf "${ACTIONLINT_TARBALL}" actionlint
48+
chmod +x actionlint
49+
- name: Run actionlint
50+
run: |
51+
./actionlint -color
52+
53+
zizmor:
54+
name: zizmor
55+
permissions:
56+
contents: read
57+
actions: read # Needed by zizmorcore/zizmor-action if repository is private
58+
runs-on: ubuntu-latest
59+
steps:
60+
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
61+
with:
62+
persist-credentials: false
63+
- name: Run zizmor
64+
uses: zizmorcore/zizmor-action@e639db99335bc9038abc0e066dfcd72e23d26fb4 # v0.3.0
65+
with:
66+
advanced-security: false
67+
persona: pedantic
68+
version: v1.19.0

.github/zizmor.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
rules:
2+
concurrency-limits:
3+
ignore:
4+
- build.yml # Can only be dispatched by maintainers
5+
unpinned-uses:
6+
config:
7+
policies:
8+
"*": hash-pin

0 commit comments

Comments
 (0)