Repository navigation
chore(deps): bump the tauri group across 1 directory with 2 updates #350
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Windows installer check | |
| # Builds the NSIS installer (the only place the custom template + branding + | |
| # languages are exercised — the PR `ci.yml` matrix runs clippy/tests but never | |
| # bundles), then runs a packaged lifecycle smoke on Windows x64: | |
| # install → first launch → upgrade → uninstall. | |
| # | |
| # Also probes Authenticode on the installer and installed PE files in | |
| # non-blocking (optional) mode until a real provider is configured, and proves | |
| # the signCommand plumbing itself with a throwaway self-signed certificate (no | |
| # real secrets involved — see docs/windows-signing.md). | |
| # | |
| # ARM64 is NOT smoke-tested here: GitHub-hosted runners are x64, and a | |
| # cross-built ARM64 PE is not runtime verification. See docs/windows-signing.md. | |
| # | |
| # CI shell is already `pwsh` — invoke helper scripts in-process with | |
| # `& .\scripts\...ps1` (nested `pwsh -File ... -Path @(…)` breaks array binding). | |
| on: | |
| pull_request: | |
| paths: | |
| - "src-tauri/installer/**" | |
| - "src-tauri/tauri.conf.json" | |
| - "src-tauri/Cargo.toml" | |
| - "Cargo.toml" | |
| - "Cargo.lock" | |
| - "src-tauri/src/**" | |
| - "crates/**" | |
| - "src/**" | |
| - "package.json" | |
| - "package-lock.json" | |
| - "scripts/windows-*.ps1" | |
| - "scripts/sign-windows-authenticode.ps1" | |
| - "scripts/verify-windows-authenticode.ps1" | |
| - "scripts/resolve-esigner-cka-installer.ps1" | |
| - ".github/workflows/win-installer-check.yml" | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: win-installer-check-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| esigner-archive-check: | |
| name: eSigner CKA archive extraction check | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| # Secret-free dry run on every PR that touches this script or its pin: | |
| # downloads the real, publicly pinned SSL.com eSigner CKA release | |
| # archive, verifies its SHA-256, and asserts the extraction/rename | |
| # logic still resolves the shipped, build-stamped executable (e.g. | |
| # "SSL.COM eSigner CKA_1.0.6_build_20230829.exe") to the fixed | |
| # `eSigner_CKA_Installer.exe` path that release.yml's credentialed | |
| # "Provision eSigner CKA certificate" step depends on. No installer is | |
| # ever executed here. This is what would have caught (and now | |
| # prevents a regression of) the archive-layout mismatch described in | |
| # scripts/resolve-esigner-cka-installer.ps1's header comment: a future | |
| # SSL.com release that changes the archive layout, or a bad pin bump, | |
| # fails here instead of on the first real release attempt. | |
| - name: Resolve + assert eSigner CKA installer path | |
| shell: pwsh | |
| run: | | |
| $dest = Join-Path $env:RUNNER_TEMP "esigner-archive-check" | |
| $installer = & ./scripts/resolve-esigner-cka-installer.ps1 -DestinationDir $dest -Stage "esigner-archive-check" | |
| $expected = Join-Path $dest "eSigner_CKA_Installer.exe" | |
| if ($installer -ne $expected) { | |
| Write-Host "::error::[esigner-archive-check] resolved path '$installer' does not match expected '$expected'" | |
| exit 1 | |
| } | |
| if (-not (Test-Path -LiteralPath $installer -PathType Leaf)) { | |
| Write-Host "::error::[esigner-archive-check] resolved installer '$installer' does not exist on disk" | |
| exit 1 | |
| } | |
| Write-Host "[esigner-archive-check] OK: $installer" | |
| # Windows PowerShell 5.1 smoke for the signCommand hook. `tauri build` runs | |
| # scripts/sign-windows-authenticode.ps1 with `powershell` (5.1), which reads | |
| # BOM-less files as the ANSI code page, so a non-ASCII character in a string | |
| # can break parsing there while pwsh 7 (UTF-8 default) parses it fine. Every | |
| # other job here uses `shell: pwsh`, so this is the only place 5.1 is | |
| # exercised on its own, with no provider configured: the exact | |
| # default-release path that must keep working unchanged. | |
| signing-hook-ps51: | |
| name: signCommand hook under Windows PowerShell 5.1 | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Parse signing scripts with Windows PowerShell 5.1 | |
| shell: powershell | |
| run: | | |
| Write-Host "PSVersion=$($PSVersionTable.PSVersion)" | |
| $failed = $false | |
| foreach ($f in "sign-windows-authenticode.ps1", "verify-windows-authenticode.ps1", "resolve-esigner-cka-installer.ps1") { | |
| $errors = $null | |
| $null = [System.Management.Automation.Language.Parser]::ParseFile((Join-Path $PWD "scripts\$f"), [ref]$null, [ref]$errors) | |
| if ($errors -and $errors.Count -gt 0) { | |
| $failed = $true | |
| $errors | ForEach-Object { Write-Host "::error::$f`: $($_.Message) (line $($_.Extent.StartLineNumber))" } | |
| } | |
| else { Write-Host "OK: $f parses under PS 5.1" } | |
| } | |
| if ($failed) { exit 1 } | |
| # Invoke the hook exactly the way tauri.conf.json does (powershell -File, | |
| # cwd = src-tauri) so an unset provider is proven to be a clean no-op. | |
| - name: signCommand no-provider no-op (default release path) | |
| shell: cmd | |
| working-directory: src-tauri | |
| env: | |
| WINDOWS_SIGNING_PROVIDER: "" | |
| WINDOWS_CERTIFICATE: "" | |
| run: | | |
| powershell -NoProfile -ExecutionPolicy Bypass -File ../scripts/sign-windows-authenticode.ps1 -Path "%SystemRoot%\System32\notepad.exe" | |
| if errorlevel 1 exit /b 1 | |
| - name: signCommand unknown provider fails loudly | |
| shell: cmd | |
| working-directory: src-tauri | |
| env: | |
| WINDOWS_SIGNING_PROVIDER: bogus | |
| run: | | |
| powershell -NoProfile -ExecutionPolicy Bypass -File ../scripts/sign-windows-authenticode.ps1 -Path "%SystemRoot%\System32\notepad.exe" | |
| if not errorlevel 1 ( | |
| echo ::error::sign-windows-authenticode.ps1 accepted an unknown provider | |
| exit /b 1 | |
| ) | |
| exit /b 0 | |
| nsis: | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 | |
| with: | |
| node-version: 24 | |
| cache: npm | |
| - uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 # stable | |
| with: | |
| targets: x86_64-pc-windows-msvc | |
| - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 | |
| - run: npm ci | |
| # ── Create a throwaway self-signed code-signing certificate ─────────── | |
| # Generated fresh every run, kept in CurrentUser\My only for the | |
| # seconds it takes to export it below, then removed. Never a real | |
| # secret and never trusted by anything — exported as a base64 PFX | |
| # with a random per-run password purely so the very next step can | |
| # feed it into the build exactly the way a real | |
| # WINDOWS_SIGNING_PROVIDER=local-pfx release would | |
| # (WINDOWS_CERTIFICATE / WINDOWS_CERTIFICATE_PASSWORD). | |
| - name: Create throwaway self-signed code-signing certificate | |
| id: throwaway-cert | |
| shell: pwsh | |
| run: | | |
| $cert = New-SelfSignedCertificate ` | |
| -Type CodeSigningCert ` | |
| -Subject "CN=Codex App Manager CI Signing Proof" ` | |
| -CertStoreLocation Cert:\CurrentUser\My ` | |
| -KeyExportPolicy Exportable ` | |
| -KeyUsage DigitalSignature ` | |
| -KeyAlgorithm RSA ` | |
| -KeyLength 2048 ` | |
| -NotAfter (Get-Date).AddDays(1) | |
| $thumbprint = $cert.Thumbprint | |
| Write-Host "Created throwaway cert thumbprint=$thumbprint" | |
| $proofDir = Join-Path $env:RUNNER_TEMP "signcommand-proof" | |
| New-Item -ItemType Directory -Force -Path $proofDir | Out-Null | |
| $pfxPath = Join-Path $proofDir "proof.pfx" | |
| $pfxPassword = [guid]::NewGuid().ToString("n") | |
| $securePass = ConvertTo-SecureString -String $pfxPassword -AsPlainText -Force | |
| Export-PfxCertificate -Cert $cert -FilePath $pfxPath -Password $securePass | Out-Null | |
| Remove-Item -LiteralPath "Cert:\CurrentUser\My\$thumbprint" -ErrorAction SilentlyContinue | |
| $pfxBase64 = [Convert]::ToBase64String([IO.File]::ReadAllBytes($pfxPath)) | |
| Remove-Item -LiteralPath $proofDir -Recurse -Force -ErrorAction SilentlyContinue | |
| # Throwaway values, but mask them anyway so nothing in this job can | |
| # ever echo them (matches the "never log secrets" rule of the | |
| # signing script and docs). | |
| Write-Host "::add-mask::$pfxPassword" | |
| Write-Host "::add-mask::$pfxBase64" | |
| "thumbprint=$thumbprint" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8 | |
| "THROWAWAY_CERT_PFX_BASE64=$pfxBase64" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 | |
| "THROWAWAY_CERT_PFX_PASSWORD=$pfxPassword" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 | |
| # NSIS only — just needs to compile the template, resolve the brand images, | |
| # and link all languages. NO real signing secrets here on purpose: this is | |
| # a pull_request trigger running PR-authored code (config/template/hooks), | |
| # so exposing TAURI_SIGNING_PRIVATE_KEY / a real signing provider's | |
| # credentials would let a PR exfiltrate keys. createUpdaterArtifacts is | |
| # off. The throwaway certificate above is NOT a real secret — it is | |
| # generated fresh in this same job, never chains to a trusted root, and | |
| # grants no capability beyond this one run — so feeding it through | |
| # WINDOWS_SIGNING_PROVIDER=local-pfx here is safe on a PR trigger and | |
| # lets Tauri's own bundle.windows.signCommand hook | |
| # (scripts/sign-windows-authenticode.ps1) actually sign all three PE | |
| # layers (main exe, NSIS uninstaller, installer) during this real | |
| # `tauri build`, rather than only testing the sign script in isolation. | |
| - name: Build NSIS installer (signed with throwaway certificate) | |
| id: build | |
| shell: bash | |
| env: | |
| WINDOWS_SIGNING_PROVIDER: local-pfx | |
| WINDOWS_CERTIFICATE: ${{ env.THROWAWAY_CERT_PFX_BASE64 }} | |
| WINDOWS_CERTIFICATE_PASSWORD: ${{ env.THROWAWAY_CERT_PFX_PASSWORD }} | |
| run: | | |
| echo "::group::[build] tauri nsis x86_64" | |
| npm run tauri build -- --bundles nsis --target x86_64-pc-windows-msvc | |
| echo "::endgroup::" | |
| - name: Locate installer + PE arch diagnostic | |
| id: artifact | |
| shell: pwsh | |
| run: | | |
| $nsis = "target/x86_64-pc-windows-msvc/release/bundle/nsis" | |
| $setup = Get-ChildItem -Path $nsis -Filter "*-setup.exe" -ErrorAction Stop | | |
| Select-Object -First 1 | |
| if (-not $setup) { | |
| Write-Host "::error::[build] no *-setup.exe under $nsis" | |
| exit 1 | |
| } | |
| $main = "target/x86_64-pc-windows-msvc/release/codex-app-manager.exe" | |
| if (-not (Test-Path -LiteralPath $main)) { | |
| Write-Host "::error::[build] missing main PE: $main" | |
| exit 1 | |
| } | |
| Write-Host "INSTALLER=$($setup.FullName)" | |
| "installer=$($setup.FullName)" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8 | |
| # In-process invoke — do not nest `pwsh -File` (array -Path binding breaks). | |
| # Hard-assert the app PE only; NSIS -setup.exe is a host stub and may | |
| # not match the payload architecture. | |
| & .\scripts\windows-pe-arch.ps1 -Path $main -ExpectMachine 0x8664 | |
| & .\scripts\windows-pe-arch.ps1 -Path $setup.FullName | |
| # ── Prove the signCommand plumbing end-to-end without a real certificate ─ | |
| # Asserts the installer this job just built for real (not a copy) | |
| # carries a signature whose SignerCertificate.Thumbprint matches the | |
| # throwaway cert created above — i.e. that Tauri's own signCommand | |
| # hook actually ran during `tauri build` and actually signed with the | |
| # certificate this job supplied it, not just that the sign script can | |
| # sign something in isolation. Mode=optional because a self-signed | |
| # cert never chains to a trusted root (Status is expected to be | |
| # non-Valid); -ExpectedThumbprint is what makes this a hard assertion | |
| # regardless of Status — see verify-windows-authenticode.ps1's header | |
| # comment. | |
| # Deliberately NOT checked here: | |
| # target/.../release/codex-app-manager.exe. tauri-bundler | |
| # signs a copy of the main exe for embedding in the installer and then | |
| # restores that on-disk build output to its unsigned original (tauri-cli | |
| # 2.11.5, bundle.rs "Restore unsigned and unpatched binary"), so it is | |
| # NotSigned by design. The signed main exe and the NSIS uninstaller | |
| # (embedded via `!uninstfinalize`) exist as files only after | |
| # installation, so the packaged lifecycle smoke step below asserts | |
| # them, with the same thumbprint. | |
| - name: Assert installer carries the throwaway certificate | |
| shell: pwsh | |
| run: | | |
| & .\scripts\verify-windows-authenticode.ps1 ` | |
| -Path @("${{ steps.artifact.outputs.installer }}") ` | |
| -Mode optional ` | |
| -ExpectedThumbprint "${{ steps.throwaway-cert.outputs.thumbprint }}" ` | |
| -Stage "sign-proof" | |
| # Always `optional` here, independent of vars.AUTHENTICODE_REQUIRED: this | |
| # job signs with a throwaway self-signed certificate that can never be | |
| # Status=Valid, so `required` mode would fail every PR once production | |
| # enforcement is switched on. Enforcement applies to release.yml only; | |
| # the thumbprint assertions in this job are what prove the signing hook. | |
| - name: Authenticode probe (optional / non-blocking) | |
| shell: pwsh | |
| run: | | |
| & .\scripts\verify-windows-authenticode.ps1 ` | |
| -Path "${{ steps.artifact.outputs.installer }}" ` | |
| -Mode optional ` | |
| -Stage "sign-verify" | |
| # ── Packaged lifecycle smoke, with the throwaway cert threaded through ── | |
| # -ExpectedThumbprint here is what proves the INSTALLED main exe and | |
| # the NSIS uninstaller were signed with the throwaway certificate: | |
| # both exist as signed files only once install has written them (the | |
| # on-disk build-output main exe is restored to unsigned by | |
| # tauri-bundler) — see windows-packaged-smoke.ps1's "sign-verify | |
| # (installed PE)" stage. | |
| - name: Packaged lifecycle smoke (install / launch / upgrade / uninstall) | |
| shell: pwsh | |
| run: | | |
| & .\scripts\windows-packaged-smoke.ps1 ` | |
| -Installer "${{ steps.artifact.outputs.installer }}" ` | |
| -AuthenticodeMode optional ` | |
| -ExpectedThumbprint "${{ steps.throwaway-cert.outputs.thumbprint }}" | |
| # Upload even when smoke fails so the built installer can be inspected. | |
| # Still requires the build/locate steps to have produced a setup.exe. | |
| - name: Upload installer | |
| if: ${{ always() && steps.artifact.outcome == 'success' }} | |
| uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7 | |
| with: | |
| name: nsis-installer | |
| path: target/x86_64-pc-windows-msvc/release/bundle/nsis/*-setup.exe | |
| if-no-files-found: error |