-
-
Notifications
You must be signed in to change notification settings - Fork 228
613 lines (576 loc) · 27.6 KB
/
Copy pathbuild-images.yml
File metadata and controls
613 lines (576 loc) · 27.6 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
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
#############################################################################
# Build FPP SD card images for Pi, Pi64, BBB, and BB64.
#
# Runs:
# - workflow_dispatch: manual trigger. Pick the branch/tag to build from the
# "Use workflow from" dropdown. Options: build a subset
# of platforms (for quickly testing this workflow), and
# optionally check "make_release" to create/update a
# release for that ref (name = ref, "v" prefix stripped)
# -- add "overwrite_existing" to replace an existing
# release's assets in place instead of failing.
# - schedule: nightly at 04:00 UTC
# - push (release tag): release build (major version >= 10 only); results are
# attached to a GitHub release named from the tag. Tags
# are numeric with NO 'v' prefix (10.0-beta) -- see the
# on.push.tags filter below and src/fppversion.sh.
#
# Strategy:
# - Pi64 / BB64 build on GitHub's free arm64 runners (native, fast)
# - Pi / BBB build on x86_64 runners via qemu-arm-static (slow, but
# avoids needing self-hosted hardware)
#
# All four build scripts use FPP_SRC_DIR pointing at the checkout, so local
# changes in the tree are picked up without needing to push first.
#############################################################################
name: Build Images
on:
workflow_dispatch:
inputs:
version:
description: 'One-off build version string (ignored if "Create/update a release" is checked; blank = nightly-YYYYMMDD)'
required: false
default: ''
platforms:
description: 'Platforms to build (use a subset to test this workflow quickly)'
type: choice
required: false
default: 'all'
options:
- 'all'
- 'pi'
- 'pi64'
- 'bb64'
- 'bbb'
- 'pi,pi64'
- 'bb64,bbb'
make_release:
description: 'Create/update a release from the branch or tag picked above ("Use workflow from"). Release name always matches that ref (leading "v" stripped).'
type: boolean
required: false
default: false
overwrite_existing:
description: 'If a release already exists for that ref, replace its assets in place instead of failing'
type: boolean
required: false
default: false
schedule:
- cron: '0 4 * * *'
push:
# Auto-build only release tags for major version >= 10. Tags are numeric
# with NO 'v' prefix: src/fppversion.sh runs the tag through `git describe`
# and does MAJOR_VERSION=$(cut -f1 -d.), so a leading 'v' would corrupt every
# displayed version (v10 instead of 10).
#
# `[1-9][0-9]*` matches any tag whose major version has two-or-more digits
# (10.0-beta, 10.4, 11.2, 100.0 ...) and so skips every single-digit major
# (9.6, 9.5.4, 8.x ...). The 9.x and earlier branches are NOT wired for
# automated image creation, so they must stay manual. `!*-master` excludes
# the X.x-master branch-point markers (e.g. 10.x-master); 'nightly' is
# skipped automatically since it does not start with a digit.
tags:
- '[1-9][0-9]*'
- '!*-master'
# A manual re-run on the same ref should cancel a running build to avoid
# wasting a 2-hour qemu job when you realize you tagged the wrong commit.
concurrency:
group: build-images-${{ github.ref }}
cancel-in-progress: true
jobs:
prep:
runs-on: ubuntu-latest
if: github.repository == 'FalconChristmas/fpp' # Only run nightly builds on main repo
outputs:
version: ${{ steps.ver.outputs.version }}
is_release: ${{ steps.ver.outputs.is_release }}
is_nightly: ${{ steps.ver.outputs.is_nightly }}
steps:
- name: Determine version string
id: ver
run: |
# Tag push -> release build. on.push.tags only fires this workflow for
# release-shaped tags, which by FPP convention carry NO 'v' prefix
# (10.0-beta, 9.5.3): src/fppversion.sh feeds the tag straight through
# `git describe`, so the version string IS the tag verbatim -- do not
# strip anything.
if [ "${{ github.event_name }}" = "push" ] && [[ "$GITHUB_REF" == refs/tags/* ]]; then
V="${GITHUB_REF#refs/tags/}"
echo "is_release=true" >> "$GITHUB_OUTPUT"
echo "is_nightly=false" >> "$GITHUB_OUTPUT"
# Manual dispatch, "make_release" checked -> release build from
# whatever branch/tag was picked in the "Use workflow from" dropdown.
# The release name always matches that ref (never the free-typed
# "version" input), same as the tag-push path above; a leading "v"
# is stripped since branches follow "vX.Y" but tags/releases/
# fppversion.sh all use the bare "X.Y" form (see header comment).
elif [ "${{ github.event.inputs.make_release }}" = "true" ]; then
V="${{ github.ref_name }}"
case "$V" in
v[0-9]*) V="${V#v}" ;;
esac
# Guard rail: the ref actually built is whatever's picked in the
# "Use workflow from" branch/tag selector at the top of the Run
# workflow form -- a control separate from the "version" text
# input below, defaults to "master", and is easy to leave
# unchanged. Refuse to silently turn a forgotten default into a
# release named "master"/"dev"/etc; require something that
# actually looks like a release ref (starts with a digit, same
# shape the tag-push trigger requires).
case "$V" in
[0-9]*) : ;;
*)
echo "::error::make_release is checked but the ref actually built ('${{ github.ref_name }}') doesn't look like a release branch/tag (expected e.g. 10.0-beta or v10.0-beta). This is controlled by the 'Use workflow from' picker at the top of the Run workflow form, NOT the 'version' text box -- it defaults to 'master', so it's easy to leave unchanged by mistake. Refusing to create a release named '${{ github.ref_name }}'."
exit 1
;;
esac
echo "is_release=true" >> "$GITHUB_OUTPUT"
echo "is_nightly=false" >> "$GITHUB_OUTPUT"
# Manual dispatch with explicit version -> one-off build
elif [ -n "${{ github.event.inputs.version }}" ]; then
V="${{ github.event.inputs.version }}"
echo "is_release=false" >> "$GITHUB_OUTPUT"
echo "is_nightly=false" >> "$GITHUB_OUTPUT"
# Schedule or manual dispatch without version -> nightly.
# Use fixed "nightly" string so artifact filenames are stable and
# URLs like releases/download/nightly/FPP-vnightly-Pi64.img.zip
# don't change night to night.
else
V="nightly"
echo "is_release=false" >> "$GITHUB_OUTPUT"
echo "is_nightly=true" >> "$GITHUB_OUTPUT"
fi
echo "version=$V" >> "$GITHUB_OUTPUT"
echo "Selected version: $V"
build:
needs: prep
strategy:
fail-fast: false
matrix:
include:
- platform: pi
runner: ubuntu-24.04-arm
script: SD/build-image-pi.sh
extra_args: "--arch armhf"
suffix: Pi
- platform: pi64
runner: ubuntu-24.04-arm
script: SD/build-image-pi.sh
extra_args: "--arch arm64"
suffix: Pi64
- platform: bb64
runner: ubuntu-24.04-arm
script: SD/build-image-bb64.sh
extra_args: ""
suffix: BB64
- platform: bbb
runner: ubuntu-24.04-arm
script: SD/build-image-bbb.sh
extra_args: ""
suffix: BBB
runs-on: ${{ matrix.runner }}
# GHA hard cap is 360 min. armhf-under-qemu builds are the long pole
# (~2-3 hours for Pi, expected similar for BBB).
timeout-minutes: 350
steps:
- name: Skip if platform not requested
id: gate
run: |
REQ="${{ github.event.inputs.platforms }}"
if [ -n "$REQ" ] && [ "$REQ" != "all" ]; then
case ",$REQ," in
*",${{ matrix.platform }},"*) echo "skip=false" >> "$GITHUB_OUTPUT" ;;
*) echo "skip=true" >> "$GITHUB_OUTPUT" ;;
esac
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi
- name: Checkout
if: steps.gate.outputs.skip == 'false'
uses: actions/checkout@v5
with:
submodules: recursive
# fppversion.sh runs `git describe` inside the chroot against the
# rsynced tree; without full history and tags it fails with
# "fatal: No names found, cannot describe anything."
fetch-depth: 0
fetch-tags: true
# Don't leave the short-lived runner token in .git/config --
# the tree gets baked into the shipped image and the token
# would (a) expire, breaking the user's first Upgrade, and
# (b) ship a credential we should never be publishing.
persist-credentials: false
- name: Resolve release branch to build from
# A release IMAGE must ship /opt/fpp on a real tracking BRANCH, never a
# detached tag. Two runtime paths depend on it:
# * scripts/git_pull updates via `git rebase @{u}` -- an upstream only
# exists on a branch, so a detached-tag checkout fails every user's
# "Check for Updates" with "HEAD does not point to a branch".
# * www/common.php keys version display + upgrade offers off a
# `v<major>.<minor>` branch name; a detached HEAD reads back garbage.
# actions/checkout leaves a tag build in detached HEAD, and the branch
# usually has fixes PAST the tag, so we must check out the branch, not
# the tagged commit.
#
# Mapping (FPP convention, one branch per minor series): tag 10.1.2 ->
# branch v10.1. The 10.0 beta is the exception -- its branch is literally
# v10.0-beta -- so when v<major>.<minor> is absent, fall back to the
# single origin/v* branch that contains the tagged commit. A
# workflow_dispatch run already picks the branch in "Use workflow from",
# so its ref_name is used verbatim.
if: steps.gate.outputs.skip == 'false'
id: relbranch
run: |
set -euo pipefail
# Ensure every origin branch is present -- needed both to resolve here
# and because the rsynced .git is what FPP_Install.sh (--skip-clone)
# checks out inside the image. Unauthenticated fetch is fine (public
# repo; persist-credentials:false scrubbed the token).
git fetch --no-tags --quiet origin '+refs/heads/*:refs/remotes/origin/*' || true
if [ "${{ github.event_name }}" = "push" ] && [[ "${{ github.ref }}" == refs/tags/* ]]; then
TAG="${{ github.ref_name }}"
TAG_SHA="$(git rev-list -n1 "$TAG")"
CONV="v$(printf '%s' "$TAG" | sed -E 's/^([0-9]+\.[0-9]+).*/\1/')"
BRANCH=""
if git show-ref --verify --quiet "refs/remotes/origin/$CONV" \
&& git merge-base --is-ancestor "$TAG_SHA" "origin/$CONV"; then
BRANCH="$CONV"
else
CANDS="$(git branch -r --contains "$TAG_SHA" --list 'origin/v[0-9]*' | sed 's#^[* ]*origin/##' | sort -u)"
N="$(printf '%s\n' "$CANDS" | grep -c . || true)"
if [ "$N" = "1" ]; then
BRANCH="$CANDS"
elif [ "$N" -gt 1 ]; then
echo "::error::Tag '$TAG' is contained in multiple release branches ($(echo $CANDS)); refusing to guess. Expected $CONV."
exit 1
fi
fi
if [ -z "$BRANCH" ]; then
echo "::error::No release branch found for tag '$TAG' (expected origin/$CONV, or exactly one origin/v* branch containing the tag). Push the release branch before tagging so images can track it."
exit 1
fi
else
# workflow_dispatch / schedule: build the branch that was checked out.
BRANCH="${{ github.ref_name }}"
fi
echo "Resolved release branch: $BRANCH"
echo "branch=$BRANCH" >> "$GITHUB_OUTPUT"
echo "FPPBRANCH=$BRANCH" >> "$GITHUB_ENV"
- name: Maximize disk space
# Default GH runners have ~14 GB free; BBB / Pi builds easily use
# 12-15 GB (base image + work image + artifacts). Strip the pre-
# installed SDKs that nothing in this workflow uses.
if: steps.gate.outputs.skip == 'false'
run: |
sudo rm -rf \
/usr/share/dotnet \
/usr/share/swift \
/usr/local/lib/android \
/opt/ghc \
/usr/local/.ghcup \
/usr/local/share/boost \
/opt/hostedtoolcache/CodeQL \
2>/dev/null || true
docker system prune -af 2>/dev/null || true
df -h /
- name: Install build dependencies
if: steps.gate.outputs.skip == 'false'
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
qemu-user-static binfmt-support \
parted dosfstools e2fsprogs \
zerofree squashfs-tools zip xz-utils \
wget ca-certificates rsync
- name: Register qemu-arm binfmt
# qemu-user-static on arm64 Ubuntu doesn't register qemu-arm because
# the host arch is different; on x86_64 it usually registers fine,
# but be defensive.
if: steps.gate.outputs.skip == 'false'
run: |
if [ ! -f /proc/sys/fs/binfmt_misc/qemu-arm ]; then
echo "Registering qemu-arm binfmt manually..."
sudo sh -c 'printf ":qemu-arm:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/bin/qemu-arm-static:OCF" > /proc/sys/fs/binfmt_misc/register' || true
fi
ls /proc/sys/fs/binfmt_misc/qemu-arm 2>/dev/null || \
echo "WARNING: qemu-arm still not registered; armhf chroot will fail"
- name: Resolve base image cache id
# The BB builders discover the latest rcn-ee image at run time (rcn-ee
# keeps only a rolling window of dated builds), so the base filename --
# not the script -- is what changes week to week. Ask the script which
# image it would download and key the cache on that, so the ~6 of 7
# nightly runs where the base is unchanged get a cache hit instead of
# re-downloading ~1GB. Pi pins its base in-script, so its hash is stable.
id: baseid
if: steps.gate.outputs.skip == 'false'
run: |
case "${{ matrix.platform }}" in
bb64|bbb)
URL="$(bash ./${{ matrix.script }} --print-base-image-url 2>/dev/null || true)"
if [ -n "$URL" ]; then
ID="$(basename "$URL")"
else
echo "WARNING: base-image resolve failed; using script hash" >&2
ID="fallback-${{ hashFiles(matrix.script) }}"
fi
;;
*)
ID="${{ hashFiles(matrix.script) }}"
;;
esac
echo "id=$ID" >> "$GITHUB_OUTPUT"
echo "Base image cache id: $ID"
- name: Cache base image download
if: steps.gate.outputs.skip == 'false'
uses: actions/cache@v5
with:
path: build/*.img.xz
# Keyed on the resolved base-image filename (BB) or script hash (Pi)
# so the cache tracks the actual image, not just the script source.
# No restore-keys: an exact miss means a new base, which should be
# downloaded fresh rather than restoring a stale prior week's image.
key: base-image-${{ matrix.platform }}-${{ steps.baseid.outputs.id }}
- name: Build image
if: steps.gate.outputs.skip == 'false'
# Pass the RESOLVED release branch (see "Resolve release branch" above),
# never github.ref_name: on a tag push ref_name is the tag, and building
# that leaves the image's /opt/fpp detached (broken self-update). Passing
# it as a --branch CLI arg -- not the FPPBRANCH env var -- is also
# required because plain `sudo` strips the environment, which is how CI
# run 30107796268 silently fell back to the script's "master" default
# and shipped every 10.0-beta image on master.
run: |
sudo ./${{ matrix.script }} \
--version "${{ needs.prep.outputs.version }}" \
--os-version "$(date -u +%Y-%m)" \
--branch "${{ steps.relbranch.outputs.branch }}" \
--use-local-src \
${{ matrix.extra_args }}
- name: List output
if: steps.gate.outputs.skip == 'false'
run: ls -la output/
- name: Upload image artifacts
if: steps.gate.outputs.skip == 'false'
uses: actions/upload-artifact@v6
with:
name: FPP-${{ needs.prep.outputs.version }}-${{ matrix.suffix }}
path: |
output/FPP-v*-${{ matrix.suffix }}.img.zip
output/${{ matrix.suffix }}-*.fppos
output/ccache-${{ matrix.suffix }}.tar.gz
if-no-files-found: error
retention-days: 14
release:
# Only run on tag push. Creates a draft release so the maintainer can
# add notes before publishing. Attaches all four platforms' artifacts.
needs: [prep, build]
if: needs.prep.outputs.is_release == 'true'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Free disk space
# The four platforms' artifacts total ~11 GB. Strip unused preinstalled
# SDKs so the download + release staging can't exhaust the runner's
# ~14 GB root disk (see the nightly_release job for the failure this
# guards against).
run: |
sudo rm -rf \
/usr/share/dotnet \
/usr/share/swift \
/usr/local/lib/android \
/opt/ghc \
/usr/local/.ghcup \
/usr/local/share/boost \
/opt/hostedtoolcache/CodeQL \
2>/dev/null || true
df -h /
- name: Download all artifacts
uses: actions/download-artifact@v5
with:
path: artifacts
- name: Flatten artifact directory
run: |
mkdir -p release-files
# mv, not cp: copying would keep a second ~11 GB duplicate of every
# artifact on disk and can blow the runner's root volume.
find artifacts -type f \( -name '*.img.zip' -o -name '*.fppos' -o -name 'ccache-*.tar.gz' \) \
-exec mv -v {} release-files/ \;
ls -la release-files/
- name: Create draft release
# Skipped for manual "overwrite_existing" runs: this step always
# creates fresh, so it would fail with "already_exists" whenever the
# release we're meant to be replacing assets on is already there.
if: github.event.inputs.overwrite_existing != 'true'
uses: softprops/action-gh-release@v2
with:
draft: true
name: "FPP ${{ needs.prep.outputs.version }}"
tag_name: ${{ needs.prep.outputs.version }}
files: release-files/*
fail_on_unmatched_files: true
- name: Create or update release (overwrite existing assets)
# Manual-dispatch-only path (overwrite_existing is only ever set via
# workflow_dispatch inputs; a tag push leaves it unset/false). Moves
# the release's tag to the commit that was just built -- needed when
# re-releasing from a branch dropdown pick rather than a fresh tag
# push -- then upserts the release and replaces its assets in place,
# mirroring the nightly_release job's non-destructive pattern.
if: github.event.inputs.overwrite_existing == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
run: |
set -euo pipefail
TAG="${{ needs.prep.outputs.version }}"
TITLE="FPP ${{ needs.prep.outputs.version }}"
if gh api "repos/${{ github.repository }}/git/refs/tags/$TAG" >/dev/null 2>&1; then
echo "Moving '$TAG' tag to ${{ github.sha }}..."
gh api -X PATCH "repos/${{ github.repository }}/git/refs/tags/$TAG" \
-f sha="${{ github.sha }}" -F force=true >/dev/null
else
echo "Creating '$TAG' tag at ${{ github.sha }}..."
gh api -X POST "repos/${{ github.repository }}/git/refs" \
-f ref="refs/tags/$TAG" -f sha="${{ github.sha }}" >/dev/null
fi
if gh release view "$TAG" >/dev/null 2>&1; then
echo "Release '$TAG' already exists; replacing its assets in place."
gh release upload "$TAG" release-files/* --clobber
else
echo "No existing release for '$TAG'; creating a new draft."
gh release create "$TAG" release-files/* \
--draft \
--title "$TITLE" \
--verify-tag
fi
nightly_release:
# Runs on schedule + schedule-like manual dispatch. Publishes / replaces
# the "nightly" pre-release so URLs like
# https://github.com/<owner>/<repo>/releases/download/nightly/<file>
# always point at last night's artifacts. Marked prerelease so GitHub's
# /releases/latest/download/ stays pointed at the last real versioned
# release -- nightly doesn't pollute the stable URL.
needs: [prep, build]
# Run even if SOME build legs failed, as long as prep succeeded and this is
# a nightly. A single platform flaking (e.g. a transient qemu/mold linker
# crash) should not block publishing the platforms that DID build -- the
# failed platform simply keeps its asset from the previous nightly (the
# publish step below is non-destructive). Versioned/tag RELEASES stay
# all-or-nothing: that is the separate `release` job, which keeps the
# default success() gate and only runs when every platform succeeds.
if: ${{ !cancelled() && needs.prep.result == 'success' && needs.prep.outputs.is_nightly == 'true' }}
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Free disk space
# The four platforms' artifacts total ~11 GB. Downloading them plus
# staging for upload previously exhausted the runner's ~14 GB root disk
# and crashed the job MID-PUBLISH -- which, with the old
# delete-then-create logic below, wiped the nightly release entirely.
# Strip unused preinstalled SDKs for ~20 GB of headroom.
run: |
sudo rm -rf \
/usr/share/dotnet \
/usr/share/swift \
/usr/local/lib/android \
/opt/ghc \
/usr/local/.ghcup \
/usr/local/share/boost \
/opt/hostedtoolcache/CodeQL \
2>/dev/null || true
df -h /
- name: Checkout (for gh CLI to know the repo)
uses: actions/checkout@v5
with:
fetch-depth: 1
- name: Download all artifacts
uses: actions/download-artifact@v5
with:
path: artifacts
- name: Flatten artifact directory
run: |
mkdir -p release-files
# mv, not cp: copying kept a second ~11 GB duplicate of every artifact
# on disk, which is what tipped the runner over into "No space left on
# device" and triggered the wipeout.
find artifacts -type f \( -name '*.img.zip' -o -name '*.fppos' -o -name 'ccache-*.tar.gz' \) \
-exec mv -v {} release-files/ \;
ls -la release-files/
- name: Publish / update nightly pre-release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_SHA: ${{ github.sha }}
REPO: ${{ github.repository }}
run: |
set -euo pipefail
BUILD_DATE="$(date -u +%Y-%m-%d)"
SHORT="${GH_SHA:0:7}"
# Resilient nightly: this job runs even when some platforms failed to
# build. Publish whatever landed this run; a platform missing from
# this run keeps its asset from the previous nightly (the upload
# below is --clobber, never a delete). Report fresh vs carried-over
# so a partial nightly is never silent.
shopt -s nullglob
staged=(release-files/*)
if [ ${#staged[@]} -eq 0 ]; then
echo "No artifacts were produced by any platform this run;" \
"leaving the existing nightly release untouched." >&2
exit 0
fi
echo "Platform coverage for this nightly:"
for suffix in Pi Pi64 BB64 BBB; do
if [ -e "release-files/FPP-vnightly-$suffix.img.zip" ]; then
echo " [fresh] $suffix"
else
echo " [carried over] $suffix (build failed this run; keeping previous nightly's asset)"
fi
done
NOTES="Automated nightly build (unreleased development snapshot).
**Build date:** $BUILD_DATE UTC
**Commit:** [\`$SHORT\`](../../commit/$GH_SHA)
Static download URLs (always point at the most recent nightly):
- \`releases/download/nightly/FPP-vnightly-Pi.img.zip\`
- \`releases/download/nightly/FPP-vnightly-Pi64.img.zip\`
- \`releases/download/nightly/FPP-vnightly-BB64.img.zip\`
- \`releases/download/nightly/FPP-vnightly-BBB.img.zip\`
> :warning: Nightly builds are unreleased development snapshots.
> Latest production release can be found at https://github.com/FalconChristmas/fpp/releases/latest"
# NON-DESTRUCTIVE replacement. The old code deleted the release+tag
# BEFORE recreating it, so any failure between the two (a disk-full
# runner, a transient API error, a cancelled job) left the repo with
# NO nightly release at all. Instead:
# 1. force-move the 'nightly' tag to this commit,
# 2. create the release only if missing, else edit it in place,
# 3. overwrite assets with --clobber.
# The release is never in a deleted state, so a partial run can only
# ever leave last night's assets -- never zero.
# 1. Move (or create) the nightly tag to this commit.
if gh api "repos/$REPO/git/refs/tags/nightly" >/dev/null 2>&1; then
echo "Moving 'nightly' tag to $SHORT..."
gh api -X PATCH "repos/$REPO/git/refs/tags/nightly" \
-f sha="$GH_SHA" -F force=true >/dev/null
else
echo "Creating 'nightly' tag at $SHORT..."
gh api -X POST "repos/$REPO/git/refs" \
-f ref="refs/tags/nightly" -f sha="$GH_SHA" >/dev/null
fi
# 2. Create the release if it doesn't exist, else refresh its notes.
if gh release view nightly >/dev/null 2>&1; then
echo "Updating existing 'nightly' release..."
gh release edit nightly \
--prerelease \
--title "FPP Nightly ($BUILD_DATE)" \
--notes "$NOTES"
else
echo "Creating 'nightly' pre-release..."
gh release create nightly \
--prerelease \
--verify-tag \
--title "FPP Nightly ($BUILD_DATE)" \
--notes "$NOTES"
fi
# 3. Upload/overwrite assets. Filenames are deterministic (fixed
# "nightly" version + fixed platform suffixes) so there are no orphans
# to prune; --clobber replaces each asset in place.
echo "Uploading assets..."
gh release upload nightly release-files/* --clobber