Skip to content

Commit 69f703f

Browse files
committed
feat(windows): Add signing of binaries
1 parent d264606 commit 69f703f

3 files changed

Lines changed: 126 additions & 0 deletions

File tree

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,24 @@ Building the project with default configuration will result in script name `__ma
164164
test-command-args: '--version'
165165
```
166166

167+
### Signing Windows Binaries
168+
169+
If you would like to sign Windows binaries, you can set `certificate` and the action will also take care of signing all binaries.
170+
It is also recommended to use `certificate-password`.
171+
172+
The `certificate` should be a PFX (Personal Information Exchange) certificate file encoded in base64 format.
173+
174+
```yaml
175+
- name: Build Python executable
176+
uses: espressif/python-binary-action@master
177+
with:
178+
scripts: 'app.py'
179+
output-dir: './dist'
180+
target-platform: 'windows-amd64'
181+
certificate: ${{ secrets.CERTIFICATE }}
182+
certificate-password: ${{ secrets.CERTIFICATE_PASSWORD }}
183+
```
184+
167185
### Complete Workflow
168186

169187
Here you can see a simplified version of workflow used in [esptool](https://github.com/espressif/esptool/) repository:
@@ -257,6 +275,8 @@ jobs:
257275
| `install-deps-command` | Command to install project dependencies | `"pip install --user --prefer-binary -e ."` | `"pip install -r requirements.txt"` |
258276
| `additional-arm-packages` | ARMv7 ONLY: Additional system packages | `""` | `"openssl libffi-dev"` |
259277
| `test-command-args` | Command arguments to test executables | `"--help"` | `"--version"` |
278+
| `certificate` | Certificate to use for signing binaries | `""` | `${{ secrets.CERTIFICATE }}` |
279+
| `certificate-password` | Password for the certificate | `""` | `${{ secrets.CERTIFICATE_PASSWORD }}` |
260280

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

Sign-File.ps1

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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 SignFile {
35+
param(
36+
[Parameter()]
37+
[String]
38+
$Path
39+
)
40+
41+
$SignTool = FindSignTool
42+
"Using: $SignTool"
43+
"Signing file: $Path"
44+
$CertificateFile = [system.io.path]::GetTempPath() + "certificate.pfx"
45+
46+
if ($null -eq $env:CERTIFICATE) {
47+
"CERTIFICATE variable not set, unable to sign the file"
48+
Exit 1
49+
}
50+
51+
if ("" -eq $env:CERTIFICATE) {
52+
"CERTIFICATE variable is empty, unable to sign the file"
53+
Exit 1
54+
}
55+
56+
$SignParameters = @("sign", "/tr", 'http://timestamp.digicert.com', "/td", "SHA256", "/f", $CertificateFile, "/fd", "SHA256")
57+
if ($env:CERTIFICATE_PASSWORD) {
58+
"CERTIFICATE_PASSWORD detected, using the password"
59+
$SignParameters += "/p"
60+
$SignParameters += $env:CERTIFICATE_PASSWORD
61+
}
62+
$SignParameters += $Path
63+
64+
[byte[]]$CertificateBytes = [convert]::FromBase64String($env:CERTIFICATE)
65+
[IO.File]::WriteAllBytes($CertificateFile, $CertificateBytes)
66+
67+
&$SignTool $SignParameters
68+
69+
if (0 -eq $LASTEXITCODE) {
70+
Remove-Item $CertificateFile
71+
} else {
72+
Remove-Item $CertificateFile
73+
"Signing failed"
74+
Exit 1
75+
}
76+
77+
}
78+
79+
SignFile ${Path}

action.yml

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

6371
outputs:
6472
executable-extension:
@@ -313,3 +321,22 @@ runs:
313321
exit 1
314322
fi
315323
done
324+
325+
- name: Sign binaries
326+
if: inputs.target-platform == 'windows-amd64'
327+
env:
328+
CERTIFICATE: ${{ inputs.certificate }}
329+
CERTIFICATE_PASSWORD: ${{ inputs.certificate-password }}
330+
shell: pwsh
331+
run: |-
332+
if ([string]::IsNullOrEmpty($env:CERTIFICATE)) {
333+
Write-Host "::warning title=Signing::Certificate is not set, skipping signing"
334+
exit 0
335+
}
336+
337+
$pythonFiles = "${{ inputs.scripts }}".Split(' ')
338+
foreach ($file in $pythonFiles) {
339+
$baseName = [System.IO.Path]::GetFileNameWithoutExtension($file)
340+
$executable = "./${{ inputs.output-dir }}/${baseName}${{ steps.setup-platform.outputs.exe-extension }}"
341+
& (Join-Path $env:GITHUB_ACTION_PATH "Sign-File.ps1") -Path $executable
342+
}

0 commit comments

Comments
 (0)