-
-
Notifications
You must be signed in to change notification settings - Fork 2
285 lines (235 loc) · 13.4 KB
/
create-release.yml
File metadata and controls
285 lines (235 loc) · 13.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
name: Create GitHub Release
on:
workflow_call:
inputs:
version:
required: true
type: string
secrets:
RELEASE_TOKEN:
required: true
GPG_PUBLIC_KEY:
required: true
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Download Build Artifacts
uses: actions/download-artifact@v8
with:
path: artifacts/ # Download all artifacts into a base directory
- name: List downloaded artifacts and prepare paths
id: prep_paths
run: |
echo "Listing downloaded artifacts..."
find artifacts -type f | sort
# Find the specific files needed
INSTALLER_PATH=$(find artifacts/windows-installer -name "*.exe" -type f | head -n 1)
BUILD_INFO_PATH=$(find artifacts/build-info -name "build_info.json" -type f | head -n 1)
if [ -z "$INSTALLER_PATH" ]; then
echo "::error::Windows installer (.exe) not found in artifacts/windows-installer"
exit 1
fi
if [ -z "$BUILD_INFO_PATH" ]; then
echo "::warning::build_info.json not found in artifacts/build-info"
# Decide if this is fatal or not, for now just warn
fi
echo "Found Installer: $INSTALLER_PATH"
echo "Found Build Info: $BUILD_INFO_PATH"
echo "installer_path=$INSTALLER_PATH" >> $GITHUB_OUTPUT
echo "build_info_path=$BUILD_INFO_PATH" >> $GITHUB_OUTPUT
echo "installer_filename=$(basename "$INSTALLER_PATH")" >> $GITHUB_OUTPUT
echo "build_info_filename=$(basename "$BUILD_INFO_PATH")" >> $GITHUB_OUTPUT
- name: Calculate Specific Checksums
id: calc_hashes
run: |
INSTALLER_PATH="${{ steps.prep_paths.outputs.installer_path }}"
BUILD_INFO_PATH="${{ steps.prep_paths.outputs.build_info_path }}"
echo "Calculating SHA256 for $INSTALLER_PATH..."
EXE_HASH=$(sha256sum "$INSTALLER_PATH" | awk '{print $1}')
echo "exe_hash=$EXE_HASH" >> $GITHUB_OUTPUT
echo "Calculated EXE hash: $EXE_HASH"
if [ -n "$BUILD_INFO_PATH" ]; then
echo "Calculating SHA256 for $BUILD_INFO_PATH..."
BUILD_INFO_HASH=$(sha256sum "$BUILD_INFO_PATH" | awk '{print $1}')
echo "build_info_hash=$BUILD_INFO_HASH" >> $GITHUB_OUTPUT
echo "Calculated build info hash: $BUILD_INFO_HASH"
else
echo "Build info file not found, skipping hash calculation"
fi
- name: Install cosign
uses: sigstore/cosign-installer@v4.1.1
- name: Sign Windows Installer with Cosign
id: sign_installer
run: |
INSTALLER_PATH="${{ steps.prep_paths.outputs.installer_path }}"
echo "Signing $INSTALLER_PATH..."
cosign sign-blob --yes "$INSTALLER_PATH" --output-signature "${INSTALLER_PATH}.sig" --bundle "${INSTALLER_PATH}.sigstore.bundle"
if [ -f "${INSTALLER_PATH}.sig" ]; then
echo "Signature created at ${INSTALLER_PATH}.sig"
echo "signature_path=${INSTALLER_PATH}.sig" >> $GITHUB_OUTPUT
else
echo "::warning::.sig file was not generated by cosign; proceeding with Sigstore bundle verification only."
echo "signature_path=" >> $GITHUB_OUTPUT
fi
echo "Bundle created at ${INSTALLER_PATH}.sigstore.bundle"
echo "bundle_path=${INSTALLER_PATH}.sigstore.bundle" >> $GITHUB_OUTPUT
- name: Prepare Verification Instructions (docs/verify.md)
run: |
mkdir -p docs # Ensure docs directory exists
# Define variables for clarity
INSTALLER_FILENAME="${{ steps.prep_paths.outputs.installer_filename }}"
INSTALLER_BUNDLE_FILENAME="${INSTALLER_FILENAME}.sigstore.bundle"
RELEASE_TAG="v${{ inputs.version }}"
REPO_URL="https://github.com/${{ github.repository }}"
RELEASE_URL="$REPO_URL/releases/tag/$RELEASE_TAG"
# Identity should point to *this* workflow (create-release.yml) as it's doing the signing now
CERT_IDENTITY="$REPO_URL/.github/workflows/create-release.yml@refs/tags/$RELEASE_TAG"
CERT_ISSUER="https://token.actions.githubusercontent.com" # Standard issuer for GitHub Actions OIDC
# Generate the verify.md content
cat > docs/verify.md << EOF
# Verifying Simkl Media Player Scrobbler Releases ($RELEASE_TAG)
This document explains how to verify the authenticity and integrity of the Simkl Media Player Scrobbler release files (\`.exe\`) downloaded from GitHub. Verification ensures the files haven't been tampered with since they were built and signed by the official GitHub Actions release workflow.
We use [Sigstore/cosign](https://github.com/sigstore/cosign) for signing and verification.
## 1. Prerequisites
You'll need the following tools installed:
* **cosign**: Follow the [official installation guide](https://docs.sigstore.dev/cosign/installation/).
* **sha256sum** (Linux/macOS) or **certutil** (Windows): For calculating SHA256 hashes.
* Windows: \`certutil -hashfile <filename> SHA256\`
* Linux/macOS: \`sha256sum <filename>\`
## 2. Download Release Assets
From the [GitHub Releases page]($RELEASE_URL), download the following files into the same directory:
* The Windows installer: \`$INSTALLER_FILENAME\`
* The installer's Sigstore bundle: \`$INSTALLER_BUNDLE_FILENAME\`
## 3. Verify Signatures with Cosign
Verify the digital signatures of the downloaded installer using \`cosign\`. This confirms it was signed by the expected GitHub Actions workflow.
Run the following command in your terminal (in the directory where you downloaded the files):
\`\`\`bash
# Verify the Windows Installer signature (preferred, using bundle)
cosign verify-blob \\
--bundle "$INSTALLER_BUNDLE_FILENAME" \\
"$INSTALLER_FILENAME"
# Optional alternative (if a .sig file is provided in a future release)
# cosign verify-blob \\
# --certificate-identity "$CERT_IDENTITY" \\
# --certificate-oidc-issuer "$CERT_ISSUER" \\
# --signature "<installer>.sig" \\
# "$INSTALLER_FILENAME"
\`\`\`
If verification is successful, you will see a "Verified OK" message. This confirms the file was signed by the official release workflow (\`create-release.yml\`) for the tag \`$RELEASE_TAG\`. **Do not proceed if verification fails.**
*Note: The \`--certificate-identity\` points to this workflow (\`create-release.yml\`) and the specific release tag (\`$RELEASE_TAG\`) that signed the file.*
## 4. Verify Checksums Manually
As an additional check, manually calculate the SHA256 hash of the downloaded file and compare it against the hash listed in the table within the GitHub Release notes.
**Example Commands:**
* **Linux/macOS:**
\`\`\`bash
sha256sum "$INSTALLER_FILENAME"
\`\`\`
* **Windows (Command Prompt or PowerShell):**
\`\`\`cmd
certutil -hashfile "$INSTALLER_FILENAME" SHA256
\`\`\`
Compare the output hash with the corresponding hash in the release notes table. They must match exactly. **Do not use the file if the hash does not match.**
## Build Provenance
This release incorporates artifacts built and verified by GitHub Actions workflow run #${{ github.run_number }} (ID: ${{ github.run_id }}).
View the release workflow log: $REPO_URL/actions/runs/${{ github.run_id }}
EOF
echo "Generated verification instructions: docs/verify.md"
- name: Setup GitHub CLI
run: |
gh --version || (
echo "Installing GitHub CLI..."
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null
sudo apt update
sudo apt install gh
)
- name: Create/Update Release and Upload Artifacts
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
GH_TOKEN: ${{ secrets.RELEASE_TOKEN }} # Some tools might prefer GH_TOKEN
run: |
VERSION="${{ inputs.version }}"
TAG_NAME="v$VERSION"
INSTALLER_PATH="${{ steps.prep_paths.outputs.installer_path }}"
INSTALLER_FILENAME="${{ steps.prep_paths.outputs.installer_filename }}"
INSTALLER_SIG_PATH="${{ steps.sign_installer.outputs.signature_path }}"
INSTALLER_BUNDLE_PATH="${{ steps.sign_installer.outputs.bundle_path }}"
BUILD_INFO_FILENAME="${{ steps.prep_paths.outputs.build_info_filename }}"
VERIFY_DOC_PATH="docs/verify.md"
WORKFLOW_RUN_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
echo "Release Tag: $TAG_NAME"
echo "Installer: $INSTALLER_PATH"
echo "Installer Sig: ${INSTALLER_SIG_PATH:-<not generated>}"
echo "Installer Bundle: $INSTALLER_BUNDLE_PATH"
echo "Build Info Filename: $BUILD_INFO_FILENAME"
echo "Verification Doc: $VERIFY_DOC_PATH"
# Construct the verification section for the release body
# Use filenames from prep_paths step and hashes from calc_hashes step
HASH_TABLE=$(cat <<EOF
**Checksums:**
| File | SHA256 Checksum |
|----------------------|--------------------------------------------------|
| \`$INSTALLER_FILENAME\` | \`${{ steps.calc_hashes.outputs.exe_hash }}\` |
| \`$BUILD_INFO_FILENAME\` | \`${{ steps.calc_hashes.outputs.build_info_hash }}\` |
**Build Info:**
The build info file contains detailed information about this build and is available in the workflow artifacts: [$BUILD_INFO_FILENAME]($WORKFLOW_RUN_URL)
**Verification:**
Please follow the instructions in the [Verification Guide](https://github.com/${{ github.repository }}/releases/download/$TAG_NAME/verify.md) to verify the signatures and checksums of the downloaded files.
EOF
)
# Check if release already exists
if gh release view "$TAG_NAME" --json id --jq .id &>/dev/null; then
echo "Release $TAG_NAME already exists. Updating notes and uploading artifacts."
EXISTING_BODY=$(gh release view "$TAG_NAME" --json body --jq .body)
# Simple strategy: Replace the whole body if it exists, otherwise use generated notes + hash table
# More sophisticated: Find a marker and replace only the verification section
# For now, let's append if the hash table isn't obviously there
if ! echo "$EXISTING_BODY" | grep -q "SHA256 Checksum"; then
echo "Appending verification info to existing notes..."
# Use printf for better control over newlines
NEW_BODY=$(printf "%s\n\n---\n\n%s" "$EXISTING_BODY" "$HASH_TABLE")
gh release edit "$TAG_NAME" --notes "$NEW_BODY"
else
echo "Verification table seems present. Consider manual review/update if needed."
# Potentially replace existing table here if needed
fi
else
echo "Creating new release $TAG_NAME..."
# Create a draft release first to capture generated notes
gh release create "$TAG_NAME" \
--title "Release $VERSION" \
--generate-notes \
--discussion-category "Releases" \
# Get the body of the draft (which includes generated notes)
DRAFT_BODY=$(gh release view "$TAG_NAME" --json body --jq .body)
# Construct the final body for the new release
NEW_BODY=$(printf "%s\n\n---\n\n%s" "$DRAFT_BODY" "$HASH_TABLE")
# Update the draft release with the full body and publish it
echo "Updating draft release with full notes and publishing..."
gh release edit "$TAG_NAME" --notes "$NEW_BODY" --draft=false
fi
# Upload all required artifacts, overwriting if they exist
echo "Uploading artifacts..."
gh release upload "$TAG_NAME" "$INSTALLER_PATH" --clobber
if [ -n "$INSTALLER_SIG_PATH" ] && [ -f "$INSTALLER_SIG_PATH" ]; then
gh release upload "$TAG_NAME" "$INSTALLER_SIG_PATH" --clobber
else
echo "Skipping .sig upload: signature file not present."
fi
gh release upload "$TAG_NAME" "$INSTALLER_BUNDLE_PATH" --clobber
gh release upload "$TAG_NAME" "$VERIFY_DOC_PATH" --clobber
# Note: Not uploading the build_info.json file as requested
# Optional: Keep or remove the delete-artifact step as needed
- name: Delete build artifacts
uses: geekyeggo/delete-artifact@v6
with:
name: |
python-package