-
-
Notifications
You must be signed in to change notification settings - Fork 1
556 lines (513 loc) · 22.4 KB
/
Copy pathrelease.yml
File metadata and controls
556 lines (513 loc) · 22.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
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
name: Release
on:
push:
tags:
- '[0-9]+.[0-9]+.[0-9]+'
workflow_dispatch:
inputs:
tag:
description: "Tag to release (must already exist)"
required: true
play_track:
description: "Play track to upload the AAB to"
required: false
default: "production"
play_release_status:
description: "Play release status (draft or completed)"
required: false
default: "completed"
# Required secrets (optional: workflow falls back to unsigned artifacts
# for workflow_dispatch on feature branches; tagged releases on main are
# blocked by `required-secrets` if any are missing):
#
# KEYSTORE_BASE64 base64 of the .jks/.keystore signing keystore
# KEYSTORE_PASSWORD keystore password
# KEY_ALIAS alias of the signing key
# KEY_PASSWORD password of the signing key
#
# When all four are present, the AAB and APK are signed; otherwise they are
# uploaded unsigned and tagged with `-unsigned` in the filename.
#
# Optional secrets (not blocking, but enable observability features):
#
# GOOGLE_SERVICES_JSON_BASE64 base64 of app/google-services.json. When
# set, Crashlytics is wired into the build
# and the R8 mapping is auto-uploaded to
# Firebase. When unset, the build still
# succeeds but ships without crash reporting.
# PLAY_SERVICE_ACCOUNT_JSON Google Play service-account key (raw
# JSON). When set, the signed AAB rolls
# out to the Play production track with
# per-locale release notes and the full
# store listing (text, screenshots,
# graphics) synced from play/metadata.
# workflow_dispatch can pick another
# track or a draft status. When unset,
# the publish-play job no-ops. The app
# record and its FIRST build must be
# created manually in the Play Console
# before the API can publish.
#
# Pin map (verify with `gh api repos/<owner>/<repo>/git/ref/tags/<tag>`):
# actions/checkout @ v7.0.1 → 3d3c42e5aac5ba805825da76410c181273ba90b1
# actions/setup-java @ v5.7.0 → b6effb05e454b25005698d916606bdc6ffcbf961
# actions/upload-artifact @ v7.0.1 → 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
# actions/download-artifact @ v8.0.1 → 3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
# gradle/actions/setup-gradle @ v6.3.0 → 9c971963bec38e04b3d30dcc455b5382be2fdbfb
# anchore/sbom-action @ v0.24.0 → e22c389904149dbc22b58101806040fa8d37a610
# anchore/scan-action @ v7.4.0 → e1165082ffb1fe366ebaf02d8526e7c4989ea9d2
# github/codeql-action/upload-sarif @ v4.37.6 → 5595ccaf912efad79be6eef63a5619ff05969be3
# softprops/action-gh-release @ v3.0.2 → 3d0d9888cb7fd7b750713d6e236d1fcb99157228
# slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml
# @ v2.1.0 → f7dd8c54c2067bafc12ca7a55595d5ee9b75204a
#
# cosign is installed directly from upstream (see harden job).
permissions:
contents: write
id-token: write
attestations: write
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
jobs:
gates:
name: PR-time gates against tag
# _security.yml declares these; granting less fails the run at startup.
permissions:
contents: read
pull-requests: read
security-events: write
uses: ./.github/workflows/_security.yml
with:
osv_scan_paths: |
.
gitleaks_enabled: true
dependency_review_enabled: false
action_pin_lint_enabled: true
allowlist_expiry_enabled: true
required-secrets:
name: Required release secrets gate
needs: [gates]
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- name: Verify keystore secrets are present for tagged releases
env:
KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
shell: bash
run: |
set -euo pipefail
if [ "${{ github.ref_type }}" != "tag" ] || [ "${{ startsWith(github.ref, 'refs/tags/') }}" != "true" ]; then
echo "::notice::Non-tag release run. Unsigned fallback permitted."
exit 0
fi
missing=()
[ -z "${KEYSTORE_BASE64:-}" ] && missing+=("KEYSTORE_BASE64")
[ -z "${KEYSTORE_PASSWORD:-}" ] && missing+=("KEYSTORE_PASSWORD")
[ -z "${KEY_ALIAS:-}" ] && missing+=("KEY_ALIAS")
[ -z "${KEY_PASSWORD:-}" ] && missing+=("KEY_PASSWORD")
if [ "${#missing[@]}" -gt 0 ]; then
echo "::error::Tagged release on $GITHUB_REF requires keystore secrets, but the following are missing: ${missing[*]}"
echo "::error::Add the missing repository secrets and retry the release. Unsigned -unsigned artifacts are NOT permitted for tags."
exit 1
fi
release:
name: Build + sign APK/AAB
needs: [gates, required-secrets]
runs-on: ubuntu-latest
# Baseline-profile generation now runs per flavor before two release builds.
timeout-minutes: 90
outputs:
version_code: ${{ steps.ver.outputs.code }}
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ inputs.tag || github.ref }}
- name: Set up JDK 17
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
with:
java-version: "17"
distribution: temurin
- name: Setup Gradle
uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0
- name: Pre-install CMake 3.22.1
run: ${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager "cmake;3.22.1"
- name: Decode keystore (if present)
id: keystore
env:
KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
run: |
if [ -n "${KEYSTORE_BASE64}" ]; then
echo "${KEYSTORE_BASE64}" | base64 -d > "${RUNNER_TEMP}/release.keystore"
echo "path=${RUNNER_TEMP}/release.keystore" >> "$GITHUB_OUTPUT"
echo "present=true" >> "$GITHUB_OUTPUT"
else
echo "present=false" >> "$GITHUB_OUTPUT"
echo "::warning::KEYSTORE_BASE64 not set. Release artifacts will be unsigned."
fi
- name: Decode google-services.json (if secret present)
id: firebase
env:
GOOGLE_SERVICES_JSON_BASE64: ${{ secrets.GOOGLE_SERVICES_JSON_BASE64 }}
run: |
if [ -n "${GOOGLE_SERVICES_JSON_BASE64}" ]; then
echo "${GOOGLE_SERVICES_JSON_BASE64}" | base64 -d > app/google-services.json
echo "present=true" >> "$GITHUB_OUTPUT"
echo "Firebase Crashlytics will be enabled in this build."
else
echo "present=false" >> "$GITHUB_OUTPUT"
echo "::warning::GOOGLE_SERVICES_JSON_BASE64 not set. Crashlytics will not be wired into this build."
fi
- name: Compute version from tag
id: ver
env:
TAG_INPUT: ${{ inputs.tag }}
run: |
set -euo pipefail
tag="${TAG_INPUT:-${GITHUB_REF_NAME}}"
name="${tag#v}"
# X.Y.Z → versionCode = max(1, X*10000 + Y*100 + Z). Re-derive locally with
# `git describe --tags` if changing this scheme.
if [[ "$name" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
major="${BASH_REMATCH[1]}"
minor="${BASH_REMATCH[2]}"
patch="${BASH_REMATCH[3]}"
code=$((major * 10000 + minor * 100 + patch))
[ "$code" -lt 1 ] && code=1
else
echo "::warning::Tag '$tag' does not match MAJOR.MINOR.PATCH, falling back to versionCode=1."
code=1
fi
echo "code=$code" >> "$GITHUB_OUTPUT"
echo "name=$name" >> "$GITHUB_OUTPUT"
echo "Resolved versionCode=$code versionName=$name"
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Enable KVM (for Baseline Profile managed-device emulator)
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' \
| sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: Generate Baseline Profile
env:
DISH_VERSION_CODE: ${{ steps.ver.outputs.code }}
DISH_VERSION_NAME: ${{ steps.ver.outputs.name }}
run: ./gradlew :app:generateBaselineProfile
# Two distribution flavors: `github` ships the APK published to GitHub
# Releases (donation surface included), `play` builds the AAB uploaded to
# Google Play (no donation surface, per the Payments policy). Only the
# flavor each channel needs is built.
- name: Build release APK (github) + AAB (play)
env:
DISH_KEYSTORE_FILE: ${{ steps.keystore.outputs.path }}
DISH_KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
DISH_KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
DISH_KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
DISH_VERSION_CODE: ${{ steps.ver.outputs.code }}
DISH_VERSION_NAME: ${{ steps.ver.outputs.name }}
run: |
if [ "${{ steps.firebase.outputs.present }}" = "true" ]; then
./gradlew assembleGithubRelease bundlePlayRelease \
uploadCrashlyticsSymbolFileGithubRelease uploadCrashlyticsSymbolFilePlayRelease
else
./gradlew assembleGithubRelease bundlePlayRelease
fi
- name: Stage artifacts
id: stage
env:
TAG_INPUT: ${{ inputs.tag }}
run: |
mkdir -p dist
suffix=""
if [ "${{ steps.keystore.outputs.present }}" != "true" ]; then
suffix="-unsigned"
fi
tag="${TAG_INPUT:-${GITHUB_REF_NAME}}"
apk=$(find app/build/outputs/apk/github/release -name '*.apk' | head -1)
[ -n "$apk" ] && cp "$apk" "dist/dish-${tag}${suffix}.apk"
# Stable-name copy: releases/latest/download/dish.apk is a permanent
# link to the newest sideload build. dish-website links it, so the
# name is a public API; it never carries the version or the
# -unsigned suffix (tagged builds hard-fail without the keystore).
[ -n "$apk" ] && cp "$apk" dist/dish.apk
aab=$(find app/build/outputs/bundle/playRelease -name '*.aab' | head -1)
[ -n "$aab" ] && cp "$aab" "dist/dish-${tag}${suffix}.aab"
# R8 mapping files for de-obfuscating prod stack traces. Crashlytics
# auto-ingests these when google-services.json is present; we also
# ship them as release assets so external consumers (or a future
# Crashlytics replacement) can de-obfuscate without GCP access.
# One per flavor, named explicitly: the Play upload below has to pick
# the play mapping, and `find | head -1` over two files is arbitrary.
mapping=$(find app/build/outputs/mapping/playRelease -name 'mapping.txt' | head -1)
[ -n "$mapping" ] && cp "$mapping" "dist/dish-${tag}${suffix}-play-mapping.txt"
gh_mapping=$(find app/build/outputs/mapping/githubRelease -name 'mapping.txt' | head -1)
[ -n "$gh_mapping" ] && cp "$gh_mapping" "dist/dish-${tag}${suffix}-github-mapping.txt"
# Native debug symbols for the JNI .so: lets crash reporters
# symbolize native stack frames from the encrypted-wire / rumble
# hot path. Shipped as AGP's Play Console zip; dist/ must hold only
# flat files or the harden job's sign/hash loops break on it.
symbols=$(find app/build/outputs/native-debug-symbols/playRelease -name 'native-debug-symbols.zip' 2>/dev/null | head -1)
[ -n "$symbols" ] && cp "$symbols" "dist/dish-${tag}${suffix}-native-debug-symbols.zip"
ls -l dist/
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: android
path: dist/
publish-play:
name: Upload AAB to Google Play
needs: [release]
runs-on: ubuntu-24.04
timeout-minutes: 30
permissions:
contents: read
steps:
- name: Gate on Play credentials
id: gate
env:
PLAY_SERVICE_ACCOUNT_JSON: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
shell: bash
run: |
set -euo pipefail
if [ -z "${PLAY_SERVICE_ACCOUNT_JSON:-}" ]; then
echo "::notice::PLAY_SERVICE_ACCOUNT_JSON not set. Skipping the Play upload."
echo "enabled=false" >> "$GITHUB_OUTPUT"
else
printf '%s' "${PLAY_SERVICE_ACCOUNT_JSON}" > "${RUNNER_TEMP}/play-key.json"
echo "enabled=true" >> "$GITHUB_OUTPUT"
fi
- name: Checkout
if: ${{ steps.gate.outputs.enabled == 'true' }}
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ inputs.tag || github.ref }}
- name: Download release artifacts
if: ${{ steps.gate.outputs.enabled == 'true' }}
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: android
path: dist
- name: Locate signed AAB and mapping
if: ${{ steps.gate.outputs.enabled == 'true' }}
id: artifacts
shell: bash
run: |
set -euo pipefail
if compgen -G 'dist/*-unsigned.aab' > /dev/null; then
echo "::warning::Only an unsigned AAB was built. Play requires a signed bundle; skipping the upload."
echo "ready=false" >> "$GITHUB_OUTPUT"
exit 0
fi
aab=$(find dist -maxdepth 1 -name '*.aab' | head -1)
if [ -z "$aab" ]; then
echo "::error::No AAB found in the release artifacts."
exit 1
fi
echo "aab=$aab" >> "$GITHUB_OUTPUT"
mapping=$(find dist -maxdepth 1 -name '*-play-mapping.txt' | head -1)
echo "mapping=$mapping" >> "$GITHUB_OUTPUT"
echo "ready=true" >> "$GITHUB_OUTPUT"
- name: Lint metadata before upload
if: ${{ steps.gate.outputs.enabled == 'true' && steps.artifacts.outputs.ready == 'true' }}
env:
EXPECTED_VERSION_CODE: ${{ needs.release.outputs.version_code }}
run: python3 scripts/check_play_metadata.py
- name: Flatten feature graphics and icon for Supply
if: ${{ steps.gate.outputs.enabled == 'true' && steps.artifacts.outputs.ready == 'true' }}
shell: bash
# Supply expects flat images/featureGraphic.png and images/icon.png;
# the repo keeps the active file as a named PNG inside
# images/featureGraphic/ and images/icon/.
run: |
set -euo pipefail
for dir in play/metadata/android/*/images/featureGraphic play/metadata/android/*/images/icon; do
[ -d "$dir" ] || continue
active=$(find "$dir" -maxdepth 1 -name '*.png' | LC_ALL=C sort | head -1)
if [ -n "$active" ]; then
cp "$active" "$(dirname "$dir")/$(basename "$dir").png"
fi
done
- name: Install fastlane
if: ${{ steps.gate.outputs.enabled == 'true' && steps.artifacts.outputs.ready == 'true' }}
run: sudo gem install fastlane -N -v "~> 2.226"
- name: Upload to Play (AAB + store listing)
if: ${{ steps.gate.outputs.enabled == 'true' && steps.artifacts.outputs.ready == 'true' }}
env:
PLAY_TRACK: ${{ inputs.play_track || 'production' }}
PLAY_RELEASE_STATUS: ${{ inputs.play_release_status || 'completed' }}
AAB: ${{ steps.artifacts.outputs.aab }}
MAPPING: ${{ steps.artifacts.outputs.mapping }}
shell: bash
run: |
set -euo pipefail
mapping_args=()
[ -n "${MAPPING}" ] && mapping_args=(--mapping "${MAPPING}")
fastlane supply \
--json_key "${RUNNER_TEMP}/play-key.json" \
--package_name com.tinkernorth.dish \
--track "${PLAY_TRACK}" \
--release_status "${PLAY_RELEASE_STATUS}" \
--aab "${AAB}" \
"${mapping_args[@]}" \
--metadata_path play/metadata/android \
--skip_upload_metadata false \
--skip_upload_images false \
--skip_upload_screenshots false \
--skip_upload_apk true
harden:
name: Harden artifacts (scan, SBOM, sign)
needs: [release]
runs-on: ubuntu-24.04
timeout-minutes: 30
permissions:
contents: read
id-token: write
security-events: write
outputs:
hashes: ${{ steps.hashes.outputs.hashes }}
steps:
- name: Download release artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: android
path: release
- name: Vulnerability scan (Grype)
uses: anchore/scan-action@27805bf3b4e84b4a5c980df22ed233c00390a439 # v7.4.2
id: grype
with:
path: release/
severity-cutoff: high
fail-build: true
only-fixed: false
add-cpes-if-none: true
output-format: sarif
- name: Upload Grype SARIF to code scanning
# Soft-fail: the grype step above is the gate, this is observability.
if: ${{ always() && steps.grype.outputs.sarif != '' }}
continue-on-error: true
uses: github/codeql-action/upload-sarif@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
with:
sarif_file: ${{ steps.grype.outputs.sarif }}
category: release-grype
- name: Generate SBOM (SPDX)
uses: anchore/sbom-action@3ad7283483fc7af8ff2b4ea19663c2d5ca935e26 # v0.24.2
with:
path: release/
format: spdx-json
output-file: release/dish-android.sbom.spdx.json
upload-artifact: false
upload-release-assets: false
- name: Generate SBOM (CycloneDX)
uses: anchore/sbom-action@3ad7283483fc7af8ff2b4ea19663c2d5ca935e26 # v0.24.2
with:
path: release/
format: cyclonedx-json
output-file: release/dish-android.sbom.cdx.json
upload-artifact: false
upload-release-assets: false
- name: Install cosign (upstream binary)
shell: bash
env:
COSIGN_VERSION: "2.6.5"
COSIGN_SHA256: "c3b4f5410e608af03a5eb0aaac84a4313d8da131248e08ff1759ac70c79d1644" # cosign-linux-amd64 v2.6.5
run: |
set -euo pipefail
curl -fsSL -o /tmp/cosign \
"https://github.com/sigstore/cosign/releases/download/v${COSIGN_VERSION}/cosign-linux-amd64"
if [ "${COSIGN_SHA256}" != "TODO_REPLACE_WITH_UPSTREAM_SHA256" ]; then
echo "${COSIGN_SHA256} /tmp/cosign" | sha256sum -c -
else
echo "::warning::COSIGN_SHA256 is unset. Replace with the upstream SHA-256 to enforce binary integrity."
fi
chmod +x /tmp/cosign
sudo mv /tmp/cosign /usr/local/bin/cosign
cosign version
- name: Generate SHA256SUMS
shell: bash
run: |
set -euo pipefail
cd release
find . -maxdepth 1 -type f ! -name 'SHA256SUMS*' -printf '%f\n' \
| LC_ALL=C sort \
| xargs -d '\n' sha256sum --binary > SHA256SUMS
cat SHA256SUMS
- name: Cosign-sign each artifact + SHA256SUMS (keyless)
shell: bash
env:
COSIGN_EXPERIMENTAL: "1"
run: |
set -euo pipefail
cd release
for f in *; do
case "$f" in
*.sig|*.crt|SHA256SUMS.sig|SHA256SUMS.crt) continue ;;
esac
cosign sign-blob --yes \
--output-signature "${f}.sig" \
--output-certificate "${f}.crt" \
"$f"
done
ls -l
- name: Compute base64 SHA256 hashes for SLSA provenance
id: hashes
shell: bash
run: |
set -euo pipefail
cd release
h=$(find . -maxdepth 1 -type f ! -name '*.sig' ! -name '*.crt' \
-printf '%f\n' \
| LC_ALL=C sort \
| xargs -d '\n' sha256sum \
| base64 -w0)
echo "hashes=${h}" >> "$GITHUB_OUTPUT"
- name: Upload hardened bundle
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: release-bundle
path: release/
retention-days: 14
provenance:
name: SLSA L3 provenance
needs: [harden]
permissions:
id-token: write
contents: write
actions: read
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0 # f7dd8c54c2067bafc12ca7a55595d5ee9b75204a
with:
base64-subjects: ${{ needs.harden.outputs.hashes }}
provenance-name: dish-android.intoto.jsonl
upload-assets: false
# The repo is public now; the flag only mattered while it was private
# (signing posts the repo name to the public Rekor log).
private-repository: false
publish:
name: Publish to GitHub Releases
needs: [harden, provenance]
runs-on: ubuntu-24.04
timeout-minutes: 15
permissions:
contents: write
steps:
- name: Download hardened bundle
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: release-bundle
path: release
- name: Download SLSA provenance
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: ${{ needs.provenance.outputs.provenance-name }}
path: release
- name: Show what we're about to publish
run: find release -type f -printf '%p\n'
- name: Upload to GitHub Releases
uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3
with:
tag_name: ${{ inputs.tag || github.ref_name }}
generate_release_notes: true
files: release/**/*