Skip to content

Commit 38e924f

Browse files
committed
feat(windows): Add signing of binaries
1 parent f4bb0dd commit 38e924f

File tree

3 files changed

+127
-2
lines changed

3 files changed

+127
-2
lines changed

README.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,24 @@ This will create executable named `my_script` instead of `__main__.py`.
134134
test-command-args: '--version'
135135
```
136136

137+
### Signing Windows Binaries
138+
139+
If you would like to sign Windows binaries, you can set `certificate` and the action will also take care of signing all binaries.
140+
It is also recommended to use `certificate-password`.
141+
142+
The `certificate` should be a PFX (Personal Information Exchange) certificate file encoded in base64 format.
143+
144+
```yaml
145+
- name: Build Python executable
146+
uses: ./.github/actions/pyinstaller-build-multiarch
147+
with:
148+
scripts: 'app.py'
149+
output-dir: './dist'
150+
target-platform: 'windows-amd64'
151+
certificate: ${{ secrets.CERTIFICATE }}
152+
certificate-password: ${{ secrets.CERTIFICATE_PASSWORD }}
153+
```
154+
137155
### Complete Workflow
138156

139157
Here you can see a simplified version of workflow used in [esptool](https://github.com/espressif/esptool/) repository:
@@ -180,7 +198,7 @@ jobs:
180198
target-platform: ${{ matrix.platform }}
181199
include-data-dirs: |
182200
{
183-
"epstool.py": [
201+
"esptool.py": [
184202
{"src": "${{ env.STUBS_DIR }}1", "dest": "${{ env.STUBS_DIR }}1"},
185203
{"src": "${{ env.STUBS_DIR }}2", "dest": "${{ env.STUBS_DIR }}2"},
186204
]
@@ -221,9 +239,11 @@ jobs:
221239
| `install-deps-command` | Command to install project dependencies | `"pip install --user --prefer-binary -e ."` | `"pip install -r requirements.txt"` |
222240
| `additional-arm-packages` | ARMv7 ONLY: Additional system packages | `""` | `"openssl libffi-dev"` |
223241
| `test-command-args` | Command arguments to test binaries | `"--help"` | `"--version"` |
242+
| `certificate` | Certificate to use for signing binaries | `""` | `${{ secrets.CERTIFICATE }}` |
243+
| `certificate-password` | Password for the certificate | `""` | `${{ secrets.CERTIFICATE_PASSWORD }}` |
224244

225245
> [!IMPORTANT]
226-
> Be careful when changing `pyinstaller-version` as it might lead to increased false positives with anti-virus software. It is recommended to check your binaries with antivurs software such as [Virustotal](https://www.virustotal.com/gui/home/upload).
246+
> Be careful when changing `pyinstaller-version` as it might lead to increased false positives with anti-virus software. It is recommended to check your binaries with antivirus software such as [Virustotal](https://www.virustotal.com/gui/home/upload).
227247

228248
## Outputs
229249

Sign-File.ps1

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
[CmdletBinding()]
2+
param (
3+
[Parameter()]
4+
[String]
5+
$Path
6+
)
7+
8+
9+
function FindSignTool {
10+
$SignTool = "signtool.exe"
11+
if (Get-Command $SignTool -ErrorAction SilentlyContinue) {
12+
return $SignTool
13+
}
14+
$SignTool = "${env:ProgramFiles(x86)}\Windows Kits\10\bin\x64\signtool.exe"
15+
if (Test-Path -Path $SignTool -PathType Leaf) {
16+
return $SignTool
17+
}
18+
$SignTool = "${env:ProgramFiles(x86)}\Windows Kits\10\bin\x86\signtool.exe"
19+
if (Test-Path -Path $SignTool -PathType Leaf) {
20+
return $SignTool
21+
}
22+
$sdkVers = "10.0.22000.0", "10.0.20348.0", "10.0.19041.0", "10.0.17763.0"
23+
Foreach ($ver in $sdkVers)
24+
{
25+
$SignTool = "${env:ProgramFiles(x86)}\Windows Kits\10\bin\${ver}\x64\signtool.exe"
26+
if (Test-Path -Path $SignTool -PathType Leaf) {
27+
return $SignTool
28+
}
29+
}
30+
"signtool.exe not found"
31+
Exit 1
32+
}
33+
34+
function SignEsptool {
35+
param(
36+
[Parameter()]
37+
[String]
38+
$Path
39+
)
40+
41+
$SignTool = FindSignTool
42+
"Using: $SignTool"
43+
$CertificateFile = [system.io.path]::GetTempPath() + "certificate.pfx"
44+
45+
if ($null -eq $env:CERTIFICATE) {
46+
"CERTIFICATE variable not set, unable to sign the file"
47+
Exit 1
48+
}
49+
50+
if ("" -eq $env:CERTIFICATE) {
51+
"CERTIFICATE variable is empty, unable to sign the file"
52+
Exit 1
53+
}
54+
55+
$SignParameters = @("sign", "/tr", 'http://timestamp.digicert.com', "/td", "SHA256", "/f", $CertificateFile, "/fd", "SHA256")
56+
if ($env:CERTIFICATE_PASSWORD) {
57+
"CERTIFICATE_PASSWORD detected, using the password"
58+
$SignParameters += "/p"
59+
$SignParameters += $env:CERTIFICATE_PASSWORD
60+
}
61+
$SignParameters += $Path
62+
63+
[byte[]]$CertificateBytes = [convert]::FromBase64String($env:CERTIFICATE)
64+
[IO.File]::WriteAllBytes($CertificateFile, $CertificateBytes)
65+
66+
&$SignTool $SignParameters
67+
68+
if (0 -eq $LASTEXITCODE) {
69+
Remove-Item $CertificateFile
70+
} else {
71+
Remove-Item $CertificateFile
72+
"Signing failed"
73+
Exit 1
74+
}
75+
76+
}
77+
78+
SignEsptool ${Path}

action.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@ inputs:
6060
description: Command arguments to test binaries (e.g. "--help")
6161
required: false
6262
default: --help
63+
certificate:
64+
description: Certificate to use for signing binaries
65+
required: false
66+
default: ''
67+
certificate-password:
68+
description: Password for the certificate
69+
required: false
70+
default: ''
6371

6472
outputs:
6573
executable-extension:
@@ -320,3 +328,22 @@ runs:
320328
exit 1
321329
fi
322330
done
331+
332+
- name: Sign binaries
333+
if: inputs.target-platform == 'windows-amd64'
334+
env:
335+
CERTIFICATE: ${{ inputs.certificate }}
336+
CERTIFICATE_PASSWORD: ${{ inputs.certificate-password }}
337+
shell: pwsh
338+
run: |-
339+
if ([string]::IsNullOrEmpty($env:CERTIFICATE)) {
340+
Write-Host "::warning title=Signing::Certificate is not set, skipping signing"
341+
exit 0
342+
}
343+
344+
$pythonFiles = "${{ env.PYTHON_FILES_STR }}".Split(' ')
345+
foreach ($file in $pythonFiles) {
346+
$baseName = [System.IO.Path]::GetFileNameWithoutExtension($file)
347+
$executable = "./${{ inputs.output-dir }}/${baseName}${{ steps.setup-platform.outputs.exe-extension }}"
348+
& (Join-Path $env:GITHUB_ACTION_PATH "Sign-File.ps1") -Path $executable
349+
}

0 commit comments

Comments
 (0)