Skip to content

Conversation

@Kinin-Code-Offical
Copy link
Owner

@Kinin-Code-Offical Kinin-Code-Offical commented Dec 22, 2025

Closes #24

Summary:

  • Add optional signing setup using base64 PFX secret in release workflow.
  • Sign both exe and installer when cert is available.

Tests:

  • npm ci (PASS)
  • npm run lint (PASS)
  • npm test (PASS)
  • npm run build (PASS)
  • npm run package (PASS)
  • npm run smoke:exe (PASS)
  • npm run installer (PASS)
  • npm run stage (PASS)

Rollback:

  • Revert this commit to remove signing steps from the release workflow.

Summary by Sourcery

Integrate optional Windows code signing into the release workflow for the CLI executable and installer when a signing certificate is provided.

CI:

  • Add a PowerShell step to prepare a signing certificate from base64-encoded secrets in the release workflow.
  • Add a conditional step to sign the built executable and installer artifacts during the release job when signing configuration is available.

Copilot AI review requested due to automatic review settings December 22, 2025 02:36
@sourcery-ai
Copy link

sourcery-ai bot commented Dec 22, 2025

Reviewer's Guide

Adds optional Windows code-signing support to the release workflow by preparing a signing certificate from encrypted secrets and signing both the CLI executable and installer artifacts when a certificate is available.

Sequence diagram for optional certificate preparation and artifact signing in release workflow

sequenceDiagram
  participant GA as GitHubActions
  participant Job as ReleaseJob
  participant Secrets as GitHubSecrets
  participant Runner as RunnerEnvironment
  participant SignScript as sign-exe.ps1

  GA->>Job: Start release workflow
  Job->>Runner: Checkout repository

  Job->>Runner: Step Prepare signing certificate
  Runner->>Secrets: Read CLOUDSQLCTL_SIGN_CERT_B64
  Runner->>Secrets: Read CLOUDSQLCTL_SIGN_PWD
  alt Signing cert provided
    Runner->>Runner: Decode base64 PFX
    Runner->>Runner: Write PFX to runner temp
    Runner->>Runner: Export CLOUDSQLCTL_SIGN_CERT to GITHUB_ENV
    Runner->>Runner: Export CLOUDSQLCTL_SIGN_PWD to GITHUB_ENV
  else No signing cert
    Runner->>Runner: Log skipping signing setup
  end

  Job->>Runner: Use Node.js 22.x
  Job->>Runner: Build exe and installer

  Job->>Runner: Step Sign artifacts (conditional)
  alt CLOUDSQLCTL_SIGN_CERT not empty
    Runner->>SignScript: Sign bin/cloudsqlctl.exe
    Runner->>SignScript: Sign dist/cloudsqlctl-setup.exe
  else CLOUDSQLCTL_SIGN_CERT empty
    Runner->>Runner: Skip signing step
  end

  Job->>Runner: Generate docs
  Runner-->>GA: Complete release job
Loading

File-Level Changes

Change Details Files
Introduce optional signing certificate preparation step in the release workflow using base64-encoded PFX and password secrets.
  • Adds a PowerShell step to decode a base64-encoded PFX certificate from secrets and write it to a temporary file on the runner.
  • Exports the certificate path and password into GITHUB_ENV so later steps can access them.
  • Gracefully no-ops with a log message when no signing certificate secret is provided.
.github/workflows/release.yml
Sign generated Windows executable and installer artifacts when signing certificate is available.
  • Adds a conditional workflow step that runs only when the signing certificate environment variable is set.
  • Invokes an existing PowerShell script twice to sign both the built CLI executable and the installer setup executable.
.github/workflows/release.yml

Assessment against linked issues

Issue Objective Addressed Explanation
#24 Enable code signing of the built executable (exe) as part of the release CI workflow.
#24 Enable code signing of the installer artifact as part of the release CI workflow.
#24 Integrate signing into CI in a way that uses a configured signing certificate (e.g., via secrets) and remains optional when no cert is provided.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • In the Sign artifacts step you’re already using shell: pwsh, so you can invoke tools/sign-exe.ps1 directly (e.g., ./tools/sign-exe.ps1 -ExePath ...) instead of spawning powershell twice with -ExecutionPolicy Bypass, which simplifies the step and avoids mixing Windows PowerShell vs. PowerShell Core.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In the `Sign artifacts` step you’re already using `shell: pwsh`, so you can invoke `tools/sign-exe.ps1` directly (e.g., `./tools/sign-exe.ps1 -ExePath ...`) instead of spawning `powershell` twice with `-ExecutionPolicy Bypass`, which simplifies the step and avoids mixing Windows PowerShell vs. PowerShell Core.

## Individual Comments

### Comment 1
<location> `.github/workflows/release.yml:40` </location>
<code_context>
+          $certPath = Join-Path $env:RUNNER_TEMP "cloudsqlctl-signing.pfx"
+          [IO.File]::WriteAllBytes($certPath, [Convert]::FromBase64String($env:CLOUDSQLCTL_SIGN_CERT_B64))
+          "CLOUDSQLCTL_SIGN_CERT=$certPath" | Out-File -FilePath $env:GITHUB_ENV -Append
+          "CLOUDSQLCTL_SIGN_PWD=$env:CLOUDSQLCTL_SIGN_PWD" | Out-File -FilePath $env:GITHUB_ENV -Append
+
       - name: Use Node.js 22.x
</code_context>

<issue_to_address>
**🚨 suggestion (security):** Avoid broadening the exposure of the signing password by putting it into GITHUB_ENV.

Writing `CLOUDSQLCTL_SIGN_PWD` to `GITHUB_ENV` makes it available to all later steps in the job, unnecessarily widening exposure if any of those steps are compromised or misconfigured. Since the secret is already available in this step’s environment, prefer passing it only to the signing step (e.g., via `env:` on that step or as an argument to the signing script) instead of promoting it to a job-wide variable.

Suggested implementation:

```
          $certPath = Join-Path $env:RUNNER_TEMP "cloudsqlctl-signing.pfx"
          [IO.File]::WriteAllBytes($certPath, [Convert]::FromBase64String($env:CLOUDSQLCTL_SIGN_CERT_B64))
          "CLOUDSQLCTL_SIGN_CERT=$certPath" | Out-File -FilePath $env:GITHUB_ENV -Append

```

```
      - name: Sign artifacts
        if: ${{ env.CLOUDSQLCTL_SIGN_CERT != '' }}
        env:
          CLOUDSQLCTL_SIGN_PWD: ${{ secrets.CLOUDSQLCTL_SIGN_PWD }}
        shell: pwsh
        run: |
          powershell -ExecutionPolicy Bypass -File tools/sign-exe.ps1 -ExePath "bin/cloudsqlctl.exe"

```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

$certPath = Join-Path $env:RUNNER_TEMP "cloudsqlctl-signing.pfx"
[IO.File]::WriteAllBytes($certPath, [Convert]::FromBase64String($env:CLOUDSQLCTL_SIGN_CERT_B64))
"CLOUDSQLCTL_SIGN_CERT=$certPath" | Out-File -FilePath $env:GITHUB_ENV -Append
"CLOUDSQLCTL_SIGN_PWD=$env:CLOUDSQLCTL_SIGN_PWD" | Out-File -FilePath $env:GITHUB_ENV -Append
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 suggestion (security): Avoid broadening the exposure of the signing password by putting it into GITHUB_ENV.

Writing CLOUDSQLCTL_SIGN_PWD to GITHUB_ENV makes it available to all later steps in the job, unnecessarily widening exposure if any of those steps are compromised or misconfigured. Since the secret is already available in this step’s environment, prefer passing it only to the signing step (e.g., via env: on that step or as an argument to the signing script) instead of promoting it to a job-wide variable.

Suggested implementation:

          $certPath = Join-Path $env:RUNNER_TEMP "cloudsqlctl-signing.pfx"
          [IO.File]::WriteAllBytes($certPath, [Convert]::FromBase64String($env:CLOUDSQLCTL_SIGN_CERT_B64))
          "CLOUDSQLCTL_SIGN_CERT=$certPath" | Out-File -FilePath $env:GITHUB_ENV -Append

      - name: Sign artifacts
        if: ${{ env.CLOUDSQLCTL_SIGN_CERT != '' }}
        env:
          CLOUDSQLCTL_SIGN_PWD: ${{ secrets.CLOUDSQLCTL_SIGN_PWD }}
        shell: pwsh
        run: |
          powershell -ExecutionPolicy Bypass -File tools/sign-exe.ps1 -ExePath "bin/cloudsqlctl.exe"

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds optional code signing support for Windows executables and installers in the release workflow. Signing is performed using a base64-encoded PFX certificate stored in GitHub Secrets, and gracefully skips signing when credentials are not available.

Key Changes:

  • Adds certificate preparation step that decodes base64 PFX and stores it temporarily
  • Implements artifact signing step for both the standalone exe and installer
  • Uses conditional logic to make signing optional based on secret availability

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +76 to +77
powershell -ExecutionPolicy Bypass -File tools/sign-exe.ps1 -ExePath "bin/cloudsqlctl.exe"
powershell -ExecutionPolicy Bypass -File tools/sign-exe.ps1 -ExePath "dist/cloudsqlctl-setup.exe"
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant PowerShell invocation. Since this step already uses shell: pwsh (line 74), the commands are already running in PowerShell. The powershell -ExecutionPolicy Bypass -File wrapper spawns an unnecessary nested PowerShell process.

The commands should be called directly using dot-sourcing or by invoking the script file with &. For example:

  • Change to: & tools/sign-exe.ps1 -ExePath "bin/cloudsqlctl.exe"

This pattern is consistent with lines 33-40 and 97-120 in the same workflow, where PowerShell commands are executed directly when using shell: pwsh.

Suggested change
powershell -ExecutionPolicy Bypass -File tools/sign-exe.ps1 -ExePath "bin/cloudsqlctl.exe"
powershell -ExecutionPolicy Bypass -File tools/sign-exe.ps1 -ExePath "dist/cloudsqlctl-setup.exe"
& tools/sign-exe.ps1 -ExePath "bin/cloudsqlctl.exe"
& tools/sign-exe.ps1 -ExePath "dist/cloudsqlctl-setup.exe"

Copilot uses AI. Check for mistakes.
run: |
powershell -ExecutionPolicy Bypass -File tools/sign-exe.ps1 -ExePath "bin/cloudsqlctl.exe"
powershell -ExecutionPolicy Bypass -File tools/sign-exe.ps1 -ExePath "dist/cloudsqlctl-setup.exe"
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The certificate file should be explicitly deleted after signing completes to minimize the time sensitive credentials exist on disk. While RUNNER_TEMP is automatically cleaned up after the job finishes, explicitly removing the certificate immediately after use follows security best practices for credential handling.

Consider adding cleanup after the signing step, either:

  1. Add a cleanup command at the end of the "Sign artifacts" step
  2. Add a separate cleanup step with if: always() to ensure cleanup even if signing fails

For example, at the end of the signing step:

Remove-Item -Path $env:CLOUDSQLCTL_SIGN_CERT -Force -ErrorAction SilentlyContinue
Suggested change
- name: Cleanup signing certificate
if: ${{ always() && env.CLOUDSQLCTL_SIGN_CERT != '' }}
shell: pwsh
run: |
Remove-Item -Path $env:CLOUDSQLCTL_SIGN_CERT -Force -ErrorAction SilentlyContinue

Copilot uses AI. Check for mistakes.
@Kinin-Code-Offical
Copy link
Owner Author

Checks green; ready to merge.

@Kinin-Code-Offical Kinin-Code-Offical merged commit 20f5888 into main Dec 22, 2025
5 checks passed
@Kinin-Code-Offical Kinin-Code-Offical deleted the p2/code-signing-ci branch December 22, 2025 02:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

P2: Code signing for exe + installer (CI integration)

2 participants