diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index c5ab427..6453bd3 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -450,6 +450,8 @@ jobs: permissions: contents: read id-token: write + env: + WINDOWS_GORELEASER_DIST: ${{ runner.temp }}/go-win-dist outputs: windows_manifest: ${{ steps.generate-windows-manifest.outputs.windows_manifest }} steps: @@ -561,6 +563,8 @@ jobs: RELEASE_TAG: ${{ inputs.tag }} run: | export BUILD_STARTED_ON=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + export WINDOWS_GORELEASER_DIST="${WINDOWS_GORELEASER_DIST//\\//}" + mkdir -p "$WINDOWS_GORELEASER_DIST" # Generate GoReleaser config envsubst < templates/.goreleaser-windows-template.yaml.tmpl | tee "_generated/.goreleaser.windows.yaml" @@ -579,18 +583,65 @@ jobs: GITHUB_TOKEN: ${{ secrets.RELENG_GITHUB_TOKEN }} GORELEASER_KEY: ${{ secrets.GORELEASER_PRO_KEY }} - - name: Flatten MSI directory structure + - name: Stage Windows dist assets shell: pwsh + env: + REPO_NAME: ${{ github.event.repository.name }} + RELEASE_TAG: ${{ inputs.tag }} + run: | + if (Test-Path "_caller/dist") { + Remove-Item "_caller/dist" -Recurse -Force + } + New-Item -ItemType Directory -Force -Path "_caller/dist" + + $zipName = "${env:REPO_NAME}-${env:RELEASE_TAG}-windows-amd64.zip" + $msiName = "${env:REPO_NAME}_${env:RELEASE_TAG}_windows_amd64.msi" + $artifacts = @( + $zipName, + "$zipName.sig", + "$zipName.cert", + $msiName, + "$msiName.sig", + "$msiName.cert" + ) + + $artifacts | ForEach-Object { + $artifact = $_ + $found = Get-ChildItem -Path $env:WINDOWS_GORELEASER_DIST -Recurse -File | Where-Object { $_.Name -eq $artifact } + if ($found.Count -ne 1) { + Write-Error "Expected exactly one Windows dist output named $artifact, found $($found.Count)" + exit 1 + } + Write-Host "Copying $artifact to dist root" + Copy-Item $found[0].FullName "_caller/dist/$artifact" + } + + - name: Generate Windows dist SBOMs + shell: pwsh + env: + REPO_NAME: ${{ github.event.repository.name }} + RELEASE_TAG: ${{ inputs.tag }} run: | - # GoReleaser Pro puts MSI files in dist/msi// subdirectory - # Copy all files to dist root to match binaries job pattern - $msiDir = "_caller/dist/msi" - if (Test-Path $msiDir) { - Get-ChildItem $msiDir -Recurse -File | ForEach-Object { - Write-Host "Copying $($_.Name) to dist root" - Copy-Item $_.FullName -Destination "_caller/dist/" + $artifacts = @( + "${env:REPO_NAME}-${env:RELEASE_TAG}-windows-amd64.zip", + "${env:REPO_NAME}_${env:RELEASE_TAG}_windows_amd64.msi" + ) + Push-Location "_caller/dist" + try { + $artifacts | ForEach-Object { + $artifact = $_ + if (!(Test-Path $artifact)) { + Write-Error "Expected Windows dist artifact was not generated: $artifact" + exit 1 + } + $sbomPath = "$artifact.sbom.json" + syft "file:$artifact" -o "spdx-json=$sbomPath" + if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE + } } - Write-Host "✅ Flattened MSI directory structure" + } finally { + Pop-Location } - name: Generate SLSA provenance for Windows artifacts @@ -604,7 +655,7 @@ jobs: PROVENANCE_COUNT=0 # Find all downloadable archives (zip and msi files) - # MSI files are flattened to dist root by previous step + # MSI files are staged to dist root by the previous step for artifact in "${CALLER_DIST}"/*.zip "${CALLER_DIST}"/*.msi; do [ -f "$artifact" ] || continue [[ "$artifact" == *checksums* ]] && continue @@ -639,7 +690,7 @@ jobs: SIGNED_COUNT=0 # Require every Windows release artifact to have an SPDX SBOM. - # MSI files are flattened to dist root by the previous step. + # MSI files are staged to dist root by the previous step. for artifact in "${CALLER_DIST}"/*.zip "${CALLER_DIST}"/*.msi; do [ -f "$artifact" ] || continue [[ "$artifact" == *checksums* ]] && continue @@ -698,7 +749,7 @@ jobs: --content-type "application/zip" } - # Upload MSI files (flattened to dist root by earlier step) + # Upload MSI files staged to dist root by the earlier step $msiFiles = Get-ChildItem "_caller/dist/*.msi" -ErrorAction SilentlyContinue foreach ($msi in $msiFiles) { Write-Host "Uploading $($msi.Name) to S3..." diff --git a/cmd/generate-windows-manifest/main.go b/cmd/generate-windows-manifest/main.go index d17457c..85ebe06 100644 --- a/cmd/generate-windows-manifest/main.go +++ b/cmd/generate-windows-manifest/main.go @@ -76,7 +76,7 @@ func main() { fmt.Fprintf(os.Stderr, "✅ Added zip asset: windows-amd64 -> %s\n", filename) } - // Find and process MSI files (flattened to dist root by workflow) + // Find and process MSI files staged to dist root by workflow msiFiles, err := filepath.Glob(filepath.Join(distDir, "*.msi")) if err != nil { fmt.Fprintf(os.Stderr, "generate-windows-manifest: error finding MSI files: %v\n", err) @@ -141,7 +141,7 @@ func buildAsset(filePath, filename, mediaType, baseURL, distDir string) (*pb.Ass sizeBytes := info.Size() href := fmt.Sprintf("%s/%s", baseURL, filename) - // Check for signature and certificate files (all in dist root after flatten step) + // Check for signature and certificate files (all in dist root after staging) var signatureHref, certificateHref *string sigPath := filepath.Join(distDir, filename+".sig") if _, err := os.Stat(sigPath); err == nil { diff --git a/templates/.goreleaser-windows-template.yaml.tmpl b/templates/.goreleaser-windows-template.yaml.tmpl index 5f3aad8..abc348a 100644 --- a/templates/.goreleaser-windows-template.yaml.tmpl +++ b/templates/.goreleaser-windows-template.yaml.tmpl @@ -2,6 +2,7 @@ ## This runs on Windows runner with Wix Toolset version: 2 project_name: "${REPO_NAME}" +dist: '${WINDOWS_GORELEASER_DIST}' builds: - binary: "${REPO_NAME}" env: @@ -31,15 +32,6 @@ release: disable: true checksum: disable: true -sboms: - - id: sbom-archive - artifacts: archive - ids: - - windows-archive - - id: sbom-msi - artifacts: installer - ids: - - windows-msi signs: - id: cosign-archives output: true