install self sign cert for validation #1
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: Build Windows | ||
| on: | ||
| workflow_call: | ||
| inputs: | ||
| version: | ||
| required: true | ||
| type: string | ||
| build_type: | ||
| required: true | ||
| type: string | ||
| installer_name: | ||
| required: true | ||
| type: string | ||
| jobs: | ||
| build-windows: | ||
| env: | ||
| AC_USERNAME: ${{ secrets.AC_USERNAME }} | ||
| AC_PASSWORD: ${{ secrets.AC_PASSWORD }} | ||
| BUILD_TYPE: ${{ inputs.build_type || '' }} | ||
| VERSION: ${{ inputs.version }} | ||
| permissions: | ||
| contents: "read" | ||
| id-token: "write" | ||
| runs-on: windows-latest | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
| - name: Download pubspec.yaml | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| name: pubspec | ||
| - name: Set up Go | ||
| uses: actions/setup-go@v5 | ||
| with: | ||
| go-version-file: "go.mod" | ||
| - name: Install WebView2 Runtime | ||
| shell: pwsh | ||
| run: | | ||
| Invoke-WebRequest -Uri "https://go.microsoft.com/fwlink/p/?LinkId=2124703" -OutFile "MicrosoftEdgeWebView2Setup.exe" | ||
| Start-Process -FilePath ".\MicrosoftEdgeWebView2Setup.exe" -ArgumentList "/silent", "/install" -Wait | ||
| - name: Set up MinGW | ||
| run: choco install mingw -y | ||
| - name: Install Flutter | ||
| uses: subosito/flutter-action@v2.19.0 | ||
| with: | ||
| channel: stable | ||
| flutter-version-file: pubspec.yaml | ||
| - name: Enable Flutter Desktop Support | ||
| run: | | ||
| flutter config --enable-windows-desktop | ||
| - name: Decode APP_ENV | ||
| uses: timheuer/base64-to-file@v1.2 | ||
| with: | ||
| fileName: "app.env" | ||
| fileDir: ${{ github.workspace }} | ||
| encodedString: ${{ secrets.APP_ENV }} | ||
| - name: Cache Dart pub global cache | ||
| uses: actions/cache@v4 | ||
| with: | ||
| path: ~/.pub-cache | ||
| key: ${{ runner.os }}-dart-pub-cache-${{ hashFiles('**/pubspec.lock') }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-dart-pub-cache- | ||
| - name: Install Inno Setup 6 | ||
| shell: pwsh | ||
| run: choco install -y innosetup | ||
| - name: Read app version from pubspec.yaml | ||
| shell: pwsh | ||
| run: | | ||
| $version = (Select-String -Path 'pubspec.yaml' -Pattern '^version:\s*(.+)$').Matches[0].Groups[1].Value.Trim() | ||
| $name = (Select-String -Path 'pubspec.yaml' -Pattern '^name:\s*(.+)$').Matches[0].Groups[1].Value.Trim() | ||
| echo "APP_VERSION=$version" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 | ||
| echo "APP_NAME=$name" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 | ||
| Write-Host "APP_NAME=$name" | ||
| Write-Host "APP_VERSION=$version" | ||
| - name: Build Windows release | ||
| shell: pwsh | ||
| run: | | ||
| dart pub global activate flutter_distributor | ||
| make windows-release | ||
| flutter_distributor package ` | ||
| --platform windows ` | ||
| --targets "exe" ` | ||
| --skip-clean ` | ||
| --build-dart-define=BUILD_TYPE=${{ env.BUILD_TYPE }} ` | ||
| --build-dart-define=VERSION=${{ inputs.version }} ` | ||
| --flutter-build-args=verbose | ||
| Move-Item "dist/$env:APP_VERSION/$env:APP_NAME-$env:APP_VERSION-windows-setup.exe" "${{ inputs.installer_name }}.exe" | ||
| - name: Upload unsigned artifact for SignPath | ||
| id: upload-unsigned | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: unsigned-installer | ||
| path: ${{ inputs.installer_name }}.exe | ||
| retention-days: 1 | ||
| - name: Download unsigned artifact | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| name: unsigned-installer | ||
| path: ./unsigned | ||
| - name: Sign EXE with SignPath (REST API) | ||
| id: sign | ||
| shell: pwsh | ||
| run: | | ||
| $response = Invoke-WebRequest -Method POST ` | ||
| -Uri "https://app.signpath.io/API/v1/${{ vars.SIGNPATH_ORG_ID }}/SigningRequests" ` | ||
| -Headers @{ "Authorization" = "Bearer ${{ secrets.SIGNPATH_API_TOKEN }}" } ` | ||
| -Form @{ | ||
| "ProjectSlug" = "${{ vars.SIGNPATH_PROJECT_SLUG }}" | ||
| "SigningPolicySlug" = "${{ vars.SIGNPATH_SIGNING_POLICY_SLUG }}" | ||
| "Artifact" = Get-Item "./unsigned/${{ inputs.installer_name }}.exe" | ||
| "Description" = "GitHub Actions build - ${{ inputs.version }}" | ||
| } | ||
| if ($response.StatusCode -eq 201) { | ||
| $signRequestUrl = $response.Headers.Location | ||
| Write-Host "Signing request submitted: $signRequestUrl" | ||
| echo "signing_request_url=$signRequestUrl" >> $env:GITHUB_OUTPUT | ||
| } else { | ||
| Write-Error "Failed to submit signing request" | ||
| exit 1 | ||
| } | ||
| - name: Wait for signing completion and download | ||
| shell: pwsh | ||
| run: | | ||
| $signRequestUrl = "${{ steps.sign.outputs.signing_request_url }}" | ||
| $maxAttempts = 60 | ||
| $attempt = 0 | ||
| Write-Host "Waiting for signing to complete..." | ||
| while ($attempt -lt $maxAttempts) { | ||
| Start-Sleep -Seconds 10 | ||
| $attempt++ | ||
| $status = Invoke-RestMethod -Method GET ` | ||
| -Uri $signRequestUrl ` | ||
| -Headers @{ "Authorization" = "Bearer ${{ secrets.SIGNPATH_API_TOKEN }}" } | ||
| Write-Host "Status: $($status.Status) (attempt $attempt/$maxAttempts)" | ||
| if ($status.Status -eq "Completed") { | ||
| Write-Host "Signing completed successfully!" | ||
| # Download signed artifact | ||
| New-Item -ItemType Directory -Force -Path "./signed" | ||
| Invoke-WebRequest -Method GET ` | ||
| -Uri "$signRequestUrl/SignedArtifact" ` | ||
| -Headers @{ "Authorization" = "Bearer ${{ secrets.SIGNPATH_API_TOKEN }}" } ` | ||
| -OutFile "./signed/${{ inputs.installer_name }}.exe" | ||
| Write-Host "Downloaded signed artifact" | ||
| # Import test certificate for validation (TEMPORARY - remove after getting real EV cert) | ||
| if ("${{ inputs.build_type }}" -eq "nightly" -or "${{ inputs.build_type }}" -eq "test") { | ||
| $certPem = @" | ||
| ${{ vars.SIGNPATH_SELF_SIGN_CERT }} | ||
| "@ | ||
| $certPath = "test-cert.pem" | ||
| $certPem | Out-File -FilePath $certPath -Encoding ASCII | ||
| Import-Certificate -FilePath $certPath -CertStoreLocation Cert:\LocalMachine\Root | ||
| Import-Certificate -FilePath $certPath -CertStoreLocation Cert:\LocalMachine\TrustedPublisher | ||
| Write-Host "✓ Test certificate imported to Trusted Root (temporary for testing)" | ||
| } | ||
| # Verify signature | ||
| $sig = Get-AuthenticodeSignature -FilePath "./signed/${{ inputs.installer_name }}.exe" | ||
| Write-Host "=== Signature Verification ===" | ||
| Write-Host "Status: $($sig.Status)" | ||
| Write-Host "Status Message: $($sig.StatusMessage)" | ||
| Write-Host "Signer Certificate Subject: $($sig.SignerCertificate.Subject)" | ||
| Write-Host "Timestamp: $($sig.TimeStamperCertificate.NotBefore)" | ||
| Write-Host "Certificate Thumbprint: $($sig.SignerCertificate.Thumbprint)" | ||
| Write-Host "==============================" | ||
| if ($sig.Status -ne "Valid") { | ||
| Write-Error "Signature verification failed! Status: $($sig.Status)" | ||
| exit 1 | ||
| } | ||
| Write-Host "✓ Signature verified successfully!" | ||
| exit 0 | ||
| } elseif ($status.Status -eq "Failed" -or $status.Status -eq "Denied") { | ||
| Write-Error "Signing failed with status: $($status.Status)" | ||
| exit 1 | ||
| } | ||
| } | ||
| Write-Error "Timeout waiting for signing to complete" | ||
| exit 1 | ||
| - name: Upload Windows installer | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: lantern-installer-exe | ||
| path: signed/${{ inputs.installer_name }}.exe | ||
| retention-days: 2 | ||