forked from RetroDECK/RetroDECK
-
Notifications
You must be signed in to change notification settings - Fork 0
753 lines (647 loc) · 31.8 KB
/
build_retrodeck.yml
File metadata and controls
753 lines (647 loc) · 31.8 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
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
name: "Build RetroDECK"
on:
push:
branches:
- cooker*
paths:
- '.github/workflows/**'
- 'automation_tools/**'
- 'config/**'
- 'functions/**'
- '*.sh'
- 'net.retrodeck.retrodeck.yml'
- 'net.retrodeck.retrodeck.metainfo.xml'
- 'tools/**'
# Disabled because is dangerous to execute outside code unsupervised
# pull_request_target:
# types: [opened, synchronize, reopened]
# branches:
# - cooker*
# - feat/*
pull_request:
types: [opened, synchronize, reopened]
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
# Build RetroDECK Job
Build_RetroDECK:
#runs-on: retrodeck
runs-on: ubuntu-latest
outputs:
VERSION: ${{ steps.build-retrodeck.outputs.VERSION }}
BUILD_DURATION: ${{ steps.build-retrodeck.outputs.BUILD_DURATION }}
DISK_USED_BEFORE: ${{ steps.build-retrodeck.outputs.DISK_USED_BEFORE }}
DISK_USED_AFTER: ${{ steps.build-retrodeck.outputs.DISK_USED_AFTER }}
DISK_DELTA: ${{ steps.build-retrodeck.outputs.DISK_DELTA }}
OUT_FOLDER_SIZE: ${{ steps.build-retrodeck.outputs.OUT_FOLDER_SIZE }}
CPU_AVG_PCT: ${{ steps.build-retrodeck.outputs.CPU_AVG_PCT }}
CPU_PEAK_PCT: ${{ steps.build-retrodeck.outputs.CPU_PEAK_PCT }}
RAM_PEAK_KB: ${{ steps.build-retrodeck.outputs.RAM_PEAK_KB }}
RAM_AVG_BYTES: ${{ steps.build-retrodeck.outputs.RAM_AVG_BYTES }}
steps:
# Remove Stuck Mounts
- name: Remove stuck mounts
run: |
# Remove stuck mounts if the directory exists and is mounted
ROFILES_DIR=$(find "$HOME" -type d -path "*/_work/RetroDECK/RetroDECK/.flatpak-builder/rofiles" 2>/dev/null | head -n 1)
if [ -n "$ROFILES_DIR" ]; then
# Check if any subdirectory is a mount point and unmount it
for subdir in "$ROFILES_DIR"/*; do
if [ -d "$subdir" ] && mountpoint -q "$subdir"; then
sudo umount -f "$subdir"
fi
done
fi
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@main
with:
# this might remove tools that are actually needed,
# if set to "true" but frees about 6 GB
tool-cache: false
# all of these default to true, but feel free to set to
# "false" if necessary for your workflow
android: true
dotnet: true
haskell: true
large-packages: true
docker-images: true
swap-storage: true
# Clone Repository
- name: Clone RetroDECK repo
if: github.event_name != 'pull_request_target'
uses: actions/checkout@v4
with:
submodules: true
# Clone the target branch (eg. cooker)
- name: Clone Target Branch
if: github.event_name == 'pull_request_target'
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.base.ref }} # Branch target
submodules: true
# Because we're using pull_request_target, we need to merge the PR code
- name: Merge and Validate PR Code
if: github.event_name == 'pull_request_target'
run: |
echo "Fetching PR..."
git fetch origin pull/${{ github.event.pull_request.number }}/head:pr
git merge --no-ff pr || {
echo "Merge conflict detected.";
exit 1;
}
echo "Validation:"
git branch
git log -1 --oneline
- name: Lint .sh scripts
run: |
set -euo pipefail
# Find all .sh files excluding archive_*, developer_toolbox and automation-tools directories and .git
mapfile -d '' files < <(find . -type f -name '*.sh' -not -path './archive_*/*' -not -path './.git/*' -not -path './automation-tools/*' -not -path './developer_toolbox/*' -print0)
if [ ${#files[@]} -eq 0 ]; then
echo "No .sh files found"
exit 0
fi
for f in "${files[@]}"; do
echo "Checking $f"
bash -n "$f"
done
# Until we add support for Flathub-beta the artifacts creation is skipped on cooker
- name: "Build Flatpak"
id: build-retrodeck
run: |
set -euo pipefail
echo "[metrics] Preparing instrumented build run..."
# Disk usage (bytes) for the filesystem where workspace is mounted
disk_used_before=$(df --output=used -B1 . | tail -n1 | tr -d '[:space:]')
# Timestamp start
start_ts=$(date +%s.%N)
# Prepare temp files
monitor_log=$(mktemp)
time_stderr=$(mktemp)
build_stdout=$(mktemp)
# Read initial CPU counters
read -r cpu_prev_total cpu_prev_idle < <(awk '/^cpu /{idle=$5; s=0; for(i=2;i<=NF;i++) s+=$i; print s, idle}' /proc/stat)
sum_cpu=0
samples=0
max_cpu=0
max_mem_bytes=0
sum_mem=0
# Choose build command (keep original behaviour)
if [[ "$GITHUB_REF" == "refs/heads/main" ]]; then
BUILD_CMD=(/usr/bin/time -v /bin/bash ./retrodeck_builder.sh --cicd)
else
BUILD_CMD=(/usr/bin/time -v /bin/bash ./retrodeck_builder.sh --cicd --no-artifacts)
fi
# Start the build (time -v to capture peak RSS). Run in background so we can sample system metrics.
echo "[metrics] Starting build command: ${BUILD_CMD[*]}"
"${BUILD_CMD[@]}" 1>"$build_stdout" 2>"$time_stderr" &
BUILD_PID=$!
# Background sampler: sample system CPU (%) and memory (bytes) every second while the build runs
(
while kill -0 "$BUILD_PID" 2>/dev/null; do
read -r cpu_total cpu_idle < <(awk '/^cpu /{idle=$5; s=0; for(i=2;i<=NF;i++) s+=$i; print s, idle}' /proc/stat)
delta_total=$((cpu_total - cpu_prev_total))
delta_idle=$((cpu_idle - cpu_prev_idle))
if [ "$delta_total" -gt 0 ]; then
cpu_pct=$(awk "BEGIN {printf \"%.2f\", 100 * ($delta_total - $delta_idle) / $delta_total}")
else
cpu_pct="0.00"
fi
cpu_prev_total=$cpu_total; cpu_prev_idle=$cpu_idle
mem_used_bytes=$(awk '/MemTotal/ {t=$2} /MemAvailable/ {a=$2} END {print (t-a)*1024}' /proc/meminfo)
sum_cpu=$(awk "BEGIN{print $sum_cpu + $cpu_pct}")
sum_mem=$(awk "BEGIN{print $sum_mem + $mem_used_bytes}")
samples=$((samples+1))
max_cpu=$(awk "BEGIN{print ($cpu_pct > $max_cpu) ? $cpu_pct : $max_cpu}")
if [ "$mem_used_bytes" -gt "$max_mem_bytes" ]; then max_mem_bytes=$mem_used_bytes; fi
echo "$(date +%s) $cpu_pct $mem_used_bytes" >> "$monitor_log"
sleep 1
done
# Save summary
echo "$samples $sum_cpu $max_cpu $max_mem_bytes $sum_mem" > "${monitor_log}.summary"
) &
MONITOR_PID=$!
# Wait for the build to finish and capture its exit code
wait "$BUILD_PID" || true
build_exit_code=$?
# Wait for the sampler to finish (it should exit once the build process is gone)
wait "$MONITOR_PID" || true
end_ts=$(date +%s.%N)
duration_seconds=$(awk "BEGIN{printf \"%.2f\", $end_ts - $start_ts}")
# Read monitor summary (compute averages)
if [ -f "${monitor_log}.summary" ]; then
read samples sum_cpu max_cpu max_mem_bytes sum_mem < <(cat "${monitor_log}.summary")
if [ "$samples" -gt 0 ]; then
avg_cpu=$(awk "BEGIN{printf \"%.2f\", $sum_cpu / $samples}")
avg_mem_bytes=$(awk "BEGIN{printf \"%d\", $sum_mem / $samples}")
else
avg_cpu="0.00"
avg_mem_bytes=0
fi
else
avg_cpu="0.00"
max_cpu=0
avg_mem_bytes=0
max_mem_bytes=0
fi
# Try to extract process peak RSS from /usr/bin/time output (kbytes)
ram_peak_kb=$(awk -F: '/Maximum resident set size/ {gsub(/[^0-9]/,"",$2); print $2}' "$time_stderr" | tail -n1 || true)
if [ -z "$ram_peak_kb" ]; then
if [ -n "$max_mem_bytes" ]; then
ram_peak_kb=$((max_mem_bytes/1024))
else
ram_peak_kb=0
fi
fi
# Disk after
disk_used_after=$(df --output=used -B1 . | tail -n1 | tr -d '[:space:]')
disk_delta=$((disk_used_after - disk_used_before))
# OUT_FOLDER size (if set by the builder it will be available in environment for later steps; attempt to detect)
if [ -n "${OUT_FOLDER:-}" ] && [ -d "${OUT_FOLDER}" ]; then
out_size_bytes=$(du -sb "${OUT_FOLDER}" | cut -f1)
elif [ -d ./out ]; then
out_size_bytes=$(du -sb ./out | cut -f1)
else
out_size_bytes=0
fi
# human-readable helper
hr() { if command -v numfmt >/dev/null 2>&1; then numfmt --to=iec-i --suffix=B --format="%.2f" "$1"; else awk 'function hr(x){ split("B KiB MiB GiB TiB",u); for(i=1;x>=1024 && i<5;i++) x/=1024; printf "%.2f %s", x, u[i]; } END{hr('$1')}' ; fi }
# Export metrics as step outputs for other jobs
echo "BUILD_DURATION=${duration_seconds}s" >> $GITHUB_OUTPUT
echo "DISK_USED_BEFORE=${disk_used_before}" >> $GITHUB_OUTPUT
echo "DISK_USED_AFTER=${disk_used_after}" >> $GITHUB_OUTPUT
echo "DISK_DELTA=${disk_delta}" >> $GITHUB_OUTPUT
echo "OUT_FOLDER_SIZE=${out_size_bytes}" >> $GITHUB_OUTPUT
echo "CPU_AVG_PCT=${avg_cpu}" >> $GITHUB_OUTPUT
echo "CPU_PEAK_PCT=${max_cpu}" >> $GITHUB_OUTPUT
echo "RAM_PEAK_KB=${ram_peak_kb}" >> $GITHUB_OUTPUT
echo "RAM_AVG_BYTES=${avg_mem_bytes}" >> $GITHUB_OUTPUT
# Print a concise console summary
echo "[metrics] Build duration: ${duration_seconds}s"
echo "[metrics] Disk before: $(hr $disk_used_before) after: $(hr $disk_used_after) delta: $(hr $disk_delta)"
echo "[metrics] OUT_FOLDER size: $(hr $out_size_bytes)"
echo "[metrics] CPU avg: ${avg_cpu}% peak: ${max_cpu}%"
echo "[metrics] RAM avg: $(hr $avg_mem_bytes) peak (process RSS): ${ram_peak_kb} KB"
# Return the original build exit code so the job fails if the build failed
exit $build_exit_code
# - name: Generate a token for Rekku
# id: generate-rekku-token
# uses: actions/create-github-app-token@v1
# with:
# app-id: ${{ vars.REKKU_APP_ID }}
# private-key: ${{ secrets.REKKU_PRIVATE_KEY }}
# repositories: "RetroDECK,Cooker"
# owner: "RetroDECK"
- name: Split artifacts
run: |
for file in "${OUT_FOLDER}"/*; do
if [ -f "$file" ]; then
size_bytes=$(stat -c%s "$file")
if [ "$size_bytes" -ge $((2 * 1024 * 1024 * 1024)) ]; then
echo "Compressing and splitting $file (size: $size_bytes bytes) with 7z..."
# 7z automatically creates .001, .002, ... when using -v option
7z a -t7z -v1900m "${file}.7z" "$file"
# Remove original file if desired
rm -f "$file"
fi
fi
done
# Upload artifacts for other jobs
# OUT_FOLDER is initialized in the retrodeck_builder.sh script
- name: Upload Build Artifacts
uses: actions/upload-artifact@v4
with:
name: retrodeck-artifacts
include-hidden-files: true
path: |
${{ env.OUT_FOLDER }}/*
components/components_version_list.md
GitHub-publish:
runs-on: ubuntu-latest
needs: [Build_RetroDECK]
outputs:
TAG: ${{ steps.version-tag.outputs.TAG }}
MAKE_LATEST: ${{ steps.version-tag.outputs.MAKE_LATEST }}
RELEASE_BODY: ${{ steps.generate-body.outputs.RELEASE_BODY }}
IS_DRAFT: ${{ steps.set-repo.outputs.IS_DRAFT }}
env:
VERSION: ${{ needs.Build_RetroDECK.outputs.VERSION }}
steps:
- name: Install xmlstarlet
run: sudo apt-get update && sudo apt-get install -y xmlstarlet
# Clone Repository
- name: Clone RetroDECK repo
uses: actions/checkout@v4
- name: Generate a token for Rekku
id: generate-rekku-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ vars.REKKU_APP_ID }}
private-key: ${{ secrets.REKKU_PRIVATE_KEY }}
repositories: "RetroDECK,Cooker,Artifacts"
owner: "RetroDECK"
- name: Download all workflow run artifacts
uses: actions/download-artifact@v4.1.8
# Getting branch name, this needs as PR should be managed in a different way
- name: Get Branch Name
run: |
if [[ "$GITHUB_EVENT_NAME" == "pull_request" || "$GITHUB_EVENT_NAME" == "pull_request_target" ]]; then
branch_name="$GITHUB_HEAD_REF"
else
branch_name="$GITHUB_REF_NAME"
fi
echo "Branch name: $branch_name"
echo "BRANCH_NAME=$branch_name" >> $GITHUB_ENV
# Generates a version tag based on the event type (main branch, PR, or cooker) and sets it as output.
- name: Generate Version Tag
id: version-tag
run: |
# Ensure the actual version was successfully passed from the previous job
if [[ -z "$VERSION" ]]; then
echo "[ERROR] Failed to read the VERSION from GitHub environmental variables."
exit 1
fi
# Determine the tag based on the GitHub event context
if [[ "$GITHUB_EVENT_NAME" == "pull_request" || "$GITHUB_EVENT_NAME" == "pull_request_target" ]]; then
# Pull request tag, sanitize the source branch
source_branch="${GITHUB_HEAD_REF//\//-}"
TAG="PR-$source_branch-${{ github.run_id }}"
MAKE_LATEST=false
else
# Other branches (cooker, main branches)
TAG="$VERSION"
MAKE_LATEST=true
fi
echo "TAG=$TAG" >> $GITHUB_ENV
echo "MAKE_LATEST=$MAKE_LATEST" >> $GITHUB_ENV
echo "TAG=$TAG" >> $GITHUB_OUTPUT
echo "MAKE_LATEST=$MAKE_LATEST" >> $GITHUB_OUTPUT
# Temporary disabled as the script is broken
# - name: "Updating release notes in metainfo"
# run: "automation_tools/metainfo_management.sh"
# Get Commits Since Last Published Release (Cooker only)
- name: Get commits since last published release
id: get-commits
if: github.ref != 'refs/heads/main'
run: |
# If this is a Pull Request
if [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then
echo "[INFO] Pull Request detected."
BASE_REF=${GITHUB_BASE_REF}
echo "[INFO] Base ref: $BASE_REF"
git fetch origin $BASE_REF
git log origin/$BASE_REF..HEAD --pretty=format:"- %s" > commits_list.txt
cp commits_list.txt commits_since_main.txt
else
# Get the latest published release tag
LATEST_TAG=$(git describe --tags $(git rev-list --tags --max-count=1) 2>/dev/null || echo "")
if [ -z "$LATEST_TAG" ]; then
echo "[INFO] No previous tag found."
echo "- No previous release." > commits_list.txt
else
echo "[INFO] Latest tag: $LATEST_TAG"
git log ${LATEST_TAG}..HEAD --pretty=format:"- %s" > commits_list.txt
fi
# Get the latest tag on the main branch
LATEST_MAIN_REF=$(git tag --merged origin/main --sort=-creatordate | head -n 1 || echo "")
if [ -z "$LATEST_MAIN_REF" ]; then
echo "[INFO] No tag found on main branch."
echo "- No main release found." > commits_since_main.txt
else
echo "[INFO] Latest tag on main: $LATEST_MAIN_REF"
git log ${LATEST_MAIN_REF}..HEAD --pretty=format:"- %s" > commits_since_main.txt
fi
fi
echo "COMMITS_FILE=commits_list.txt" >> $GITHUB_ENV
echo "COMMITS_MAIN_FILE=commits_since_main.txt" >> $GITHUB_ENV
# Generate Release Body
- name: Generate release body text
id: generate-body
run: |
RELEASE_BODY_FILE="release_body.md"
echo "# Release Notes" > $RELEASE_BODY_FILE
echo "This is a cooker snapshot based on [this commit](https://github.com/${{ github.repository }}/commit/${{ github.sha }}), from branch [${{ env.BRANCH_NAME }}](https://github.com/RetroDECK/RetroDECK/tree/feat/${{ env.BRANCH_NAME }})." >> $RELEASE_BODY_FILE
echo "" >> $RELEASE_BODY_FILE
# Add components version information at the top
if [ -f retrodeck-artifacts/components/components-version ]; then
COMPONENTS_VERSION=$(cat retrodeck-artifacts/components/components-version)
if [ -f retrodeck-artifacts/components/components-release-url ]; then
COMPONENTS_URL=$(cat retrodeck-artifacts/components/components-release-url)
else
COMPONENTS_URL="https://github.com/RetroDECK/components/releases/tag/$COMPONENTS_VERSION"
fi
echo "## Components Information" >> $RELEASE_BODY_FILE
echo "**Components Version:** [\`$COMPONENTS_VERSION\`]($COMPONENTS_URL)" >> $RELEASE_BODY_FILE
elif [ -f components/components-version ]; then
COMPONENTS_VERSION=$(cat components/components-version)
if [ -f components/components-release-url ]; then
COMPONENTS_URL=$(cat components/components-release-url)
else
COMPONENTS_URL="https://github.com/RetroDECK/components/releases/tag/$COMPONENTS_VERSION"
fi
echo "## Components Information" >> $RELEASE_BODY_FILE
echo "**Components Version:** [\`$COMPONENTS_VERSION\`]($COMPONENTS_URL)" >> $RELEASE_BODY_FILE
fi
echo "**Cooker Builds** are for testers only – see [How‑to: Start Testing](https://retrodeck.readthedocs.io/en/latest/wiki_development/testing/retrodeck-testing/)." >> $RELEASE_BODY_FILE
echo "Do not file GitHub issues for Cooker builds. These nightly releases contain **unstable features** and may destabilize or corrupt your RetroDECK setup. The RetroDECK team is **not liable** for any data loss." >> $RELEASE_BODY_FILE
echo "" >> $RELEASE_BODY_FILE
# --- Build metrics from Build_RetroDECK job (if available)
DISK_BEFORE_BYTES="${{ needs.Build_RetroDECK.outputs.DISK_USED_BEFORE || '' }}"
DISK_AFTER_BYTES="${{ needs.Build_RetroDECK.outputs.DISK_USED_AFTER || '' }}"
DISK_DELTA_BYTES="${{ needs.Build_RetroDECK.outputs.DISK_DELTA || '' }}"
OUT_FOLDER_BYTES="${{ needs.Build_RetroDECK.outputs.OUT_FOLDER_SIZE || '' }}"
CPU_AVG="${{ needs.Build_RetroDECK.outputs.CPU_AVG_PCT || '' }}"
CPU_PEAK="${{ needs.Build_RetroDECK.outputs.CPU_PEAK_PCT || '' }}"
RAM_PEAK_KB="${{ needs.Build_RetroDECK.outputs.RAM_PEAK_KB || '' }}"
RAM_AVG_BYTES="${{ needs.Build_RetroDECK.outputs.RAM_AVG_BYTES || '' }}"
BUILD_DURATION="${{ needs.Build_RetroDECK.outputs.BUILD_DURATION || '' }}"
if [ -n "$BUILD_DURATION" ] || [ -n "$DISK_BEFORE_BYTES" ]; then
echo "" >> $GITHUB_STEP_SUMMARY
echo "## 📊 Build metrics" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Metric | Value |" >> $GITHUB_STEP_SUMMARY
echo "|---|---|" >> $GITHUB_STEP_SUMMARY
echo "| Duration | \\`$BUILD_DURATION\\` |" >> $GITHUB_STEP_SUMMARY
hr() { if command -v numfmt >/dev/null 2>&1; then numfmt --to=iec-i --suffix=B --format="%.2f" "$1"; else awk 'function hr(x){ split("B KiB MiB GiB TiB",u); for(i=1;x>=1024 && i<5;i++) x/=1024; printf "%.2f %s", x, u[i]; } END{hr('$1')}' ; fi }
if [ -n "$DISK_BEFORE_BYTES" ]; then echo "| Disk (before) | \\`$(hr $DISK_BEFORE_BYTES)\\` |" >> $GITHUB_STEP_SUMMARY; fi
if [ -n "$DISK_AFTER_BYTES" ]; then echo "| Disk (after) | \\`$(hr $DISK_AFTER_BYTES)\\` |" >> $GITHUB_STEP_SUMMARY; fi
if [ -n "$DISK_DELTA_BYTES" ]; then echo "| Disk delta (approx.) | \\`$(hr $DISK_DELTA_BYTES)\\` |" >> $GITHUB_STEP_SUMMARY; fi
if [ -n "$OUT_FOLDER_BYTES" ]; then echo "| Artifacts size (out/) | \\`$(hr $OUT_FOLDER_BYTES)\\` |" >> $GITHUB_STEP_SUMMARY; fi
if [ -n "$CPU_AVG" ]; then echo "| CPU average | \\`$CPU_AVG%\\` |" >> $GITHUB_STEP_SUMMARY; fi
if [ -n "$CPU_PEAK" ]; then echo "| CPU peak | \\`$CPU_PEAK%\\` |" >> $GITHUB_STEP_SUMMARY; fi
if [ -n "$RAM_AVG_BYTES" ]; then echo "| RAM average | \\`$(hr $RAM_AVG_BYTES)\\` |" >> $GITHUB_STEP_SUMMARY; fi
if [ -n "$RAM_PEAK_KB" ]; then echo "| RAM peak (process RSS) | \\`$(hr $((RAM_PEAK_KB * 1024)))\\` |" >> $GITHUB_STEP_SUMMARY; fi
fi
echo "" >> $RELEASE_BODY_FILE
# Append changelog from metainfo
echo "## Changelog" >> $RELEASE_BODY_FILE
xmlstarlet sel -t -v "/component/releases/release[1]/description//text()" ./net.retrodeck.retrodeck.metainfo.xml \
| sed 's/^[[:space:]]*//;s/[[:space:]]*$//' \
| awk 'NF' \
| sed -E '
s/^[[:space:]]*(.*[^[:space:]]):$/### \1/;
t title
s/^/- /
:title
' >> $RELEASE_BODY_FILE
# Fetch latest main to compare against
git fetch origin main
# Build comparison link
COMPARE_URL="https://github.com/${{ github.repository }}/compare/main...${{ github.sha }}"
# Append comparison link to release body
echo "" >> $RELEASE_BODY_FILE
echo "---" >> $RELEASE_BODY_FILE
echo "" >> $RELEASE_BODY_FILE
echo "[Check changes since latest main release](${COMPARE_URL})" >> $RELEASE_BODY_FILE
echo "" >> $RELEASE_BODY_FILE
# Append components version list (detailed)
if [ -f retrodeck-artifacts/components/components_version_list.md ]; then
cat retrodeck-artifacts/components/components_version_list.md >> $RELEASE_BODY_FILE
else
echo "WARNING: components_version_list.md not found." >> $RELEASE_BODY_FILE
fi
echo "" >> $RELEASE_BODY_FILE
# Output the final body
echo "RELEASE_BODY<<EOF" >> $GITHUB_OUTPUT
cat $RELEASE_BODY_FILE >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
# Determine if Target Repository is Main or not, in that case is a Cooker build
- name: Determine target repository and draft status
id: set-repo
run: |
if [[ "$GITHUB_REF" == "refs/heads/main" ]]; then
REPO_NAME="RetroDECK"
IS_DRAFT="true"
else
REPO_NAME="Cooker"
IS_DRAFT="false"
fi
echo "REPO_NAME=$REPO_NAME" >> $GITHUB_ENV
echo "IS_DRAFT=$IS_DRAFT" >> $GITHUB_ENV
echo "REPO_NAME=$REPO_NAME" >> $GITHUB_OUTPUT
echo "IS_DRAFT=$IS_DRAFT" >> $GITHUB_OUTPUT
- name: "[DEBUG] Listing produced artifacts"
run: |
echo "Produced artifacts:"
ls -lah retrodeck-artifacts/out
# Publish Release
- name: Publish release
id: publish-release
uses: ncipollo/release-action@v1
with:
tag: ${{ env.TAG }}
name: "RetroDECK ${{ env.TAG }}"
body: ${{ steps.generate-body.outputs.RELEASE_BODY }}
artifacts: "retrodeck-artifacts/out/*"
allowUpdates: true
omitBodyDuringUpdate: true
makeLatest: ${{ env.MAKE_LATEST }}
repo: ${{ env.REPO_NAME }}
token: ${{ steps.generate-rekku-token.outputs.token }}
draft: ${{ env.IS_DRAFT }}
# Display Job Summary
- name: Display Job Summary
run: |
RELEASE_URL="https://github.com/RetroDECK/${{ env.REPO_NAME }}/releases/tag/${{ env.TAG }}"
# Read components version if available
COMPONENTS_VERSION=""
COMPONENTS_URL=""
REQUESTED_TAG=""
if [ -f "retrodeck-artifacts/components/components-version" ]; then
COMPONENTS_VERSION=$(cat retrodeck-artifacts/components/components-version)
if [ -f "retrodeck-artifacts/components/components-release-url" ]; then
COMPONENTS_URL=$(cat retrodeck-artifacts/components/components-release-url)
else
COMPONENTS_URL="https://github.com/RetroDECK/components/releases/tag/$COMPONENTS_VERSION"
fi
elif [ -f "components/components-version" ]; then
COMPONENTS_VERSION=$(cat components/components-version)
if [ -f "components/components-release-url" ]; then
COMPONENTS_URL=$(cat components/components-release-url)
else
COMPONENTS_URL="https://github.com/RetroDECK/components/releases/tag/$COMPONENTS_VERSION"
fi
fi
# Create job summary
echo "# Release Published Successfully!" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| **Release URL** | [${{ env.TAG }}]($RELEASE_URL) |" >> $GITHUB_STEP_SUMMARY
echo "| **Tag** | \`${{ env.TAG }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| **Repository** | \`${{ env.REPO_NAME }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| **Branch** | \`${{ env.BRANCH_NAME }}\` |" >> $GITHUB_STEP_SUMMARY
if [ -n "$COMPONENTS_VERSION" ]; then
if [ -n "$COMPONENTS_URL" ]; then
echo "| **Components Version** | [\`$COMPONENTS_VERSION\`]($COMPONENTS_URL) |" >> $GITHUB_STEP_SUMMARY
else
echo "| **Components Version** | \`$COMPONENTS_VERSION\` |" >> $GITHUB_STEP_SUMMARY
fi
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Direct Link:** $RELEASE_URL" >> $GITHUB_STEP_SUMMARY
# Add Library Hunter status if available
if [ -n "${{ needs.Library_Hunter.outputs.HUNT_STATUS }}" ]; then
echo "" >> $GITHUB_STEP_SUMMARY
echo "## 🔍 Library Hunter Status" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Status:** ${{ needs.Library_Hunter.outputs.HUNT_STATUS }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "${{ needs.Library_Hunter.outputs.HUNT_MESSAGE }}" >> $GITHUB_STEP_SUMMARY
fi
# Add components information if available
if [ -f "retrodeck-artifacts/components/components_version_list.md" ]; then
echo "" >> $GITHUB_STEP_SUMMARY
echo "## Components Used" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
cat retrodeck-artifacts/components/components_version_list.md >> $GITHUB_STEP_SUMMARY
elif [ -f "components/components_version_list.md" ]; then
echo "" >> $GITHUB_STEP_SUMMARY
echo "## Components Used" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
cat components/components_version_list.md >> $GITHUB_STEP_SUMMARY
fi
# Also log to console
echo "Release published successfully!"
echo "Release URL: $RELEASE_URL"
echo "Tag: ${{ env.TAG }}"
echo "Repository: ${{ env.REPO_NAME }}"
echo "Branch: ${{ env.BRANCH_NAME }}"
if [ -n "$COMPONENTS_VERSION" ]; then
echo "🔧 Components Version: $COMPONENTS_VERSION"
fi
# Create notice
echo "::notice title=Release Published::Release ${{ env.TAG }} has been published to $RELEASE_URL"
- name: Post PR comment with artifacts link
if: github.event_name == 'pull_request_target' || github.event_name == 'pull_request'
uses: marocchino/sticky-pull-request-comment@v2
with:
GITHUB_TOKEN: ${{ steps.generate-rekku-token.outputs.token }}
header: "RetroDECK Build Artifacts"
message: |
A build for this `pull request` has been produced.
Codename: **${{ env.TAG }}**
Build artifacts can be found [here](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}#artifact-retrodeck-artifacts) and they include:
- RetroDECK Flatpak: `RetroDECK.flatpak`
- Flatpak file SHA256 checksum: `RetroDECK.flatpak.sha`
- Flatpak Artifact Bundle: `RetroDECK-Artifact.tar.gz`, not useful for testing or end users
# Rewrite Tag (for Main Branch Only)
- name: Clone RetroDECK repo
if: github.ref == 'refs/heads/main'
uses: actions/checkout@v4
with:
submodules: true
- name: Rewrite Tag
if: github.ref == 'refs/heads/main'
run: |
git submodule deinit -f --all
git fetch --tags
if git rev-parse --verify "${{ env.TAG }}" >/dev/null 2>&1; then
git tag -d "${{ env.TAG }}"
git push --delete origin "${{ env.TAG }}"
fi
git tag "${{ env.TAG }}"
git push origin "${{ env.TAG }}"
env:
GITHUB_TOKEN: ${{ steps.generate-rekku-token.outputs.token }}
# As backup we're even publishing the build on our own selfhosted Forgejo instance (kept commented until instance available)
# Forgejo Publish Job (main branch only)
# Forgejo-publish:
# runs-on: ubuntu-latest
# needs: GitHub-publish
# env:
# TAG: ${{ needs.GitHub-publish.outputs.TAG }}
# RELEASE_BODY: "${{ needs.GitHub-publish.outputs.RELEASE_BODY }} || No release body found"
# MAKE_LATEST: ${{ needs.GitHub-publish.outputs.MAKE_LATEST }}
# IS_DRAFT: "${{ needs.GitHub-publish.outputs.IS_DRAFT }}"
# steps:
# - name: Download all workflow run artifacts
# uses: actions/download-artifact@v4.1.8
#
# - name: Forgejo-publish
# if: github.ref == 'refs/heads/main'
# uses: RetroDECK/components-template/.github/workflows/fogejo_publish_release.yml@main
# with:
# release_body: "${{ needs.GitHub-publish.outputs.RELEASE_BODY }} || No release body found"
# artifacts: "retrodeck-artifacts/out/*"
# tag: ${{ env.TAG }}
# make_latest: ${{ env.MAKE_LATEST }}
# draft: ${{ env.IS_DRAFT }}
# Automated Tests
Automated_Tests:
runs-on: ubuntu-latest
needs: Build_RetroDECK
continue-on-error: true
steps:
# Clone Repository
- name: Clone RetroDECK repo
uses: actions/checkout@v4
with:
submodules: true
# Download RetroDECK Artifacts
- name: Download all workflow run artifacts
uses: actions/download-artifact@v4.1.8
# Install Dependencies
- name: Install dependencies
run: curl "https://raw.githubusercontent.com/RetroDECK/components-template/main/automation_tools/install_dependencies.sh" | bash
# Install RetroDECK Flatpak
- name: Install RetroDECK Flatpak
continue-on-error: true
run: |
ls -lah retrodeck-artifacts
flatpak install --user --bundle --noninteractive -y "retrodeck-artifacts/RetroDECK"*".flatpak"
# Run Post Build Checks
- name: Run Post Build Checks
continue-on-error: true
run: /bin/bash ./automation_tools/post_build_check.sh
# Search for Missing Libraries
- name: Search for Missing Libraries
continue-on-error: true
run: /bin/bash ./automation_tools/search_missing_libs.sh
# Uninstall RetroDECK Flatpak (only needed on non-ubuntu-latest / self-hosted runners)
- name: Uninstall RetroDECK Flatpak
if: ${{ github.runner_environment == 'self-hosted' }}
run: |
flatpak remove --user --noninteractive -y net.retrodeck.retrodeck