Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 7 additions & 10 deletions .github/verify-windows-signature.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
"""Report whether every executable in the Windows release zip is signed.

The signing chain is silent when it breaks, and it broke two different ways in
a row under a green run: v0.69.0's signing request failed outright, and
v0.69.1's completed while the signed file never reached the release. This reads
the shipped zip itself, so nothing but the published bytes can satisfy it.

Reports rather than blocks — the caller runs it with continue-on-error, since
SignPath is on a self-signed test certificate while the project's OSS
application is under review and publishing must not depend on it. For the same
reason this checks that a certificate is present, not that it chains to a
trusted root.
The signing chain breaks silently — v0.69.0 and v0.69.1 both shipped unsigned
under a green run, two different ways — so this reads the zip itself and
nothing but the published bytes can satisfy it.

It checks that a certificate is present, not that it chains to a trusted root:
SignPath is on a self-signed test certificate pending the project's OSS
application, which is also why the caller runs this with continue-on-error.

Usage: python .github/verify-windows-signature.py <zip>
"""
Expand Down
30 changes: 14 additions & 16 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -159,15 +159,11 @@ jobs:
continue-on-error: true
uses: actions/upload-artifact@v7
with:
# actions/upload-artifact always wraps the upload in its own
# storage zip unless archive: false — since the file we're
# uploading is already a zip, the default produced a zip-of-a-zip
# that SignPath saw as "the artifact", so the configured
# <pe-file> paths never matched (confirmed via a real
# signing-request failure: "Expected path to match exactly 1
# item, but found 0", processing unsigned-windows-zip.zip).
# archive: false uploads the file as-is; the artifact name then
# becomes the file's own name, so `name:` here is ignored.
# The file is already a zip, and upload-artifact wraps its uploads
# in a storage zip unless told not to — SignPath would then look
# for the PE files one level too deep. archive: false also makes
# the artifact take the file's own name (ignoring `name:`), which
# is what the signing step's output directory has to work around.
archive: false
path: target/distrib/worktrunk-x86_64-pc-windows-msvc.zip
- name: Submit SignPath signing request
Expand All @@ -183,14 +179,12 @@ jobs:
github-artifact-id: '${{ steps.upload-unsigned-windows-artifact.outputs.artifact-id }}'
wait-for-completion: true
# A scratch directory, not target/distrib: SignPath names the
# download after the GitHub artifact, which is already
# worktrunk-x86_64-pc-windows-msvc.zip, so writing it alongside the
# unsigned build would land it at …zip.zip and leave the unsigned
# original in place (how v0.69.1 shipped unsigned). The next step
# takes whatever lands here, so SignPath's naming doesn't matter.
# download after the artifact, so it would land beside the build as
# …zip.zip and leave the unsigned original in place — how v0.69.1
# shipped unsigned. The next step takes whatever lands here.
output-artifact-directory: 'target/signpath'
# Without this, the action extracts the returned zip's contents as
# loose files instead of saving back the signed zip.
# Without this the action unpacks the returned zip into loose files
# instead of saving it back whole.
skip-decompress: true
- name: Replace the Windows artifact with the signed one
if: contains(matrix.targets, 'x86_64-pc-windows-msvc') && steps.signpath.outcome == 'success'
Expand All @@ -209,6 +203,10 @@ jobs:
# shipping a corrupt one never is.
python .github/verify-windows-signature.py "${signed[0]}"
mv "${signed[0]}" target/distrib/worktrunk-x86_64-pc-windows-msvc.zip
# Deliberately not continue-on-error, unlike the signing steps: shipping
# unsigned is tolerable while the certificate is a test one, shipping a
# checksum that doesn't match the zip is not. That is also why this
# doesn't fold into the step above.
- name: Recompute checksum for signed Windows artifact
if: contains(matrix.targets, 'x86_64-pc-windows-msvc')
shell: bash
Expand Down
Loading