diff --git a/README.md b/README.md index 672b770..4251b1b 100644 --- a/README.md +++ b/README.md @@ -166,10 +166,11 @@ Building the project with default configuration will result in script name `__ma ### Signing Windows Binaries -If you would like to sign Windows binaries, you can set `certificate` and the action will also take care of signing all binaries. -It is also recommended to use `certificate-password`. +If you would like to sign Windows binaries, you can configure Azure Key Vault credentials and the action will automatically sign all binaries after building. Signing is only performed for `windows-amd64` platform builds. -The `certificate` should be a PFX (Personal Information Exchange) certificate file encoded in base64 format. +The action uses the [espressif/release-sign](https://github.com/espressif/release-sign) action internally, which requires Azure credentials to access a certificate stored in Azure Key Vault. If the Azure client secret is not set, signing will be skipped with a warning message. + +To enable signing, you must explicitly pass the Azure credentials as inputs from your workflow. Set the following secrets in your repository and pass them to the action: ```yaml - name: Build Python executable @@ -178,8 +179,12 @@ The `certificate` should be a PFX (Personal Information Exchange) certificate fi scripts: 'app.py' output-dir: './dist' target-platform: 'windows-amd64' - certificate: ${{ secrets.CERTIFICATE }} - certificate-password: ${{ secrets.CERTIFICATE_PASSWORD }} + # Azure credentials for signing (must be explicitly passed) + azure-client-id: ${{ secrets.AZURE_CLIENT_ID }} + azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }} + azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }} + azure-keyvault-uri: ${{ secrets.AZURE_KEYVAULT_URI }} + azure-keyvault-cert-name: ${{ secrets.AZURE_KEYVAULT_CERT_NAME }} ``` ### Complete Workflow @@ -275,12 +280,24 @@ jobs: | `install-deps-command` | Command to install project dependencies | `"uv pip install -e ."` | `"uv pip install -r requirements.txt"` | | `additional-arm-packages` | ARMv7 ONLY: Additional system packages | `""` | `"openssl libffi-dev"` | | `test-command-args` | Command arguments to test executables | `"--help"` | `"--version"` | -| `certificate` | Certificate to use for signing binaries | `""` | `${{ secrets.CERTIFICATE }}` | -| `certificate-password` | Password for the certificate | `""` | `${{ secrets.CERTIFICATE_PASSWORD }}` | > [!IMPORTANT] > 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). +### Optional Inputs for Signing Binaries + +For signing binaries on Windows, this action uses the [espressif/release-sign](https://github.com/espressif/release-sign) action. The following inputs are optional but required if you want to sign your Windows executables. + +Signing is optional but strongly recommended. The action will produce a warning if a Windows executable was built but was not signed. + +| Input | Description | Default | Example | +|---------------------------|----------------------------------|----------|-------------------------------------------| +| `azure-client-id` | Azure client ID for signing | `""` | `${{ secrets.AZURE_CLIENT_ID }}` | +| `azure-client-secret` | Azure client secret for signing | `""` | `${{ secrets.AZURE_CLIENT_SECRET }}` | +| `azure-tenant-id` | Azure tenant ID for signing | `""` | `${{ secrets.AZURE_TENANT_ID }}` | +| `azure-keyvault-uri` | Azure key vault URI for signing | `""` | `${{ secrets.AZURE_KEYVAULT_URI }}` | +| `azure-keyvault-cert-name`| Azure key vault certificate name | `""` | `${{ secrets.AZURE_KEYVAULT_CERT_NAME }}` | + ## Outputs | Output | Description | diff --git a/Sign-File.ps1 b/Sign-File.ps1 deleted file mode 100644 index ba8f5c5..0000000 --- a/Sign-File.ps1 +++ /dev/null @@ -1,79 +0,0 @@ -[CmdletBinding()] -param ( - [Parameter()] - [String] - $Path -) - - -function FindSignTool { - $SignTool = "signtool.exe" - if (Get-Command $SignTool -ErrorAction SilentlyContinue) { - return $SignTool - } - $SignTool = "${env:ProgramFiles(x86)}\Windows Kits\10\bin\x64\signtool.exe" - if (Test-Path -Path $SignTool -PathType Leaf) { - return $SignTool - } - $SignTool = "${env:ProgramFiles(x86)}\Windows Kits\10\bin\x86\signtool.exe" - if (Test-Path -Path $SignTool -PathType Leaf) { - return $SignTool - } - $sdkVers = "10.0.26100.0", "10.0.22000.0", "10.0.20348.0", "10.0.19041.0", "10.0.17763.0" - Foreach ($ver in $sdkVers) - { - $SignTool = "${env:ProgramFiles(x86)}\Windows Kits\10\bin\${ver}\x64\signtool.exe" - if (Test-Path -Path $SignTool -PathType Leaf) { - return $SignTool - } - } - "signtool.exe not found" - Exit 1 -} - -function SignFile { - param( - [Parameter()] - [String] - $Path - ) - - $SignTool = FindSignTool - "Using: $SignTool" - "Signing file: $Path" - $CertificateFile = [system.io.path]::GetTempPath() + "certificate.pfx" - - if ($null -eq $env:CERTIFICATE) { - "CERTIFICATE variable not set, unable to sign the file" - Exit 1 - } - - if ("" -eq $env:CERTIFICATE) { - "CERTIFICATE variable is empty, unable to sign the file" - Exit 1 - } - - $SignParameters = @("sign", "/tr", 'http://timestamp.digicert.com', "/td", "SHA256", "/f", $CertificateFile, "/fd", "SHA256") - if ($env:CERTIFICATE_PASSWORD) { - "CERTIFICATE_PASSWORD detected, using the password" - $SignParameters += "/p" - $SignParameters += $env:CERTIFICATE_PASSWORD - } - $SignParameters += $Path - - [byte[]]$CertificateBytes = [convert]::FromBase64String($env:CERTIFICATE) - [IO.File]::WriteAllBytes($CertificateFile, $CertificateBytes) - - &$SignTool $SignParameters - - if (0 -eq $LASTEXITCODE) { - Remove-Item $CertificateFile - } else { - Remove-Item $CertificateFile - "Signing failed" - Exit 1 - } - -} - -SignFile ${Path} diff --git a/action.yml b/action.yml index 43c24c1..dfa4f3f 100644 --- a/action.yml +++ b/action.yml @@ -60,12 +60,24 @@ inputs: description: Command arguments to test binaries (e.g. "--help") required: false default: --help - certificate: - description: Certificate to use for signing binaries + azure-client-id: + description: Azure client ID for espressif/release-sign action required: false default: '' - certificate-password: - description: Password for the certificate + azure-client-secret: + description: Azure client secret for espressif/release-sign action + required: false + default: '' + azure-tenant-id: + description: Azure tenant ID for espressif/release-sign action + required: false + default: '' + azure-keyvault-uri: + description: Azure key vault URI for espressif/release-sign action + required: false + default: '' + azure-keyvault-cert-name: + description: Azure key vault certificate name for espressif/release-sign action required: false default: '' @@ -265,21 +277,27 @@ runs: "${{ steps.setup-platform.outputs.exe-extension }}" \ "${{ inputs.test-command-args }}" - - name: Sign binaries - if: inputs.target-platform == 'windows-amd64' - env: - CERTIFICATE: ${{ inputs.certificate }} - CERTIFICATE_PASSWORD: ${{ inputs.certificate-password }} + - name: Check signing certificate + if: | + inputs.target-platform == 'windows-amd64' && inputs.azure-client-secret == '' shell: pwsh - run: |- - if ([string]::IsNullOrEmpty($env:CERTIFICATE)) { - Write-Host "::warning title=Signing::Certificate is not set, skipping signing" - exit 0 - } + run: | + Write-Host "::warning title=Signing::Azure client secret is not set, skipping signing" + + - name: Sign binaries + if: | + inputs.target-platform == 'windows-amd64' && inputs.azure-client-secret != '' + uses: espressif/release-sign@master + with: + path: ${{ inputs.output-dir }} + azure-client-id: ${{ inputs.azure-client-id }} + azure-client-secret: ${{ inputs.azure-client-secret }} + azure-tenant-id: ${{ inputs.azure-tenant-id }} + azure-keyvault-uri: ${{ inputs.azure-keyvault-uri }} + azure-keyvault-cert-name: ${{ inputs.azure-keyvault-cert-name }} - $pythonFiles = "${{ inputs.scripts }}".Split(' ') - foreach ($file in $pythonFiles) { - $baseName = [System.IO.Path]::GetFileNameWithoutExtension($file) - $executable = "./${{ inputs.output-dir }}/${baseName}${{ steps.setup-platform.outputs.exe-extension }}" - & (Join-Path $env:GITHUB_ACTION_PATH "Sign-File.ps1") -Path $executable - } + - name: Remove leftover signature files + if: | + inputs.target-platform == 'windows-amd64' && inputs.azure-client-secret != '' + shell: bash + run: find ./${{ inputs.output-dir }} -name "*.sig" -type f -delete