44 push :
55 branches :
66 - ' *'
7+ tags :
8+ - ' *'
79 pull_request :
810 workflow_dispatch :
11+ inputs :
12+ release_name :
13+ description : Release name string for prerelease body
14+ required : true
15+ default : manual
916
1017jobs :
1118 matrix :
@@ -26,24 +33,70 @@ jobs:
2633
2734 - name : Generate variant matrix
2835 id : variants
29- env :
30- CHUNK_SIZE : 25
3136 run : |
3237 set -euo pipefail
33- make list-variants > variants.json
3438 cat <<'PY' | sed 's/^ //' | python3 > matrix.json
35- import json, os
39+ import json
3640
37- CHUNK_SIZE = int(os.environ.get("CHUNK_SIZE", "25"))
38- with open("variants.json", "r", encoding="utf-8") as fh:
39- variants = json.load(fh).get("include", [])
41+ # MMCE+MX4SIO is unsupported (embedded IRX conflict); omit any variant with both.
42+ labels = [
43+ "hdd-normal",
44+ "mmce-normal",
45+ "mx4sio-normal",
46+ "usb-normal",
47+ "xfrom-normal",
48+ "hdd-normal+mmce-normal",
49+ "hdd-normal+mx4sio-normal",
50+ "hdd-normal+usb-normal",
51+ "hdd-normal+xfrom-normal",
52+ "mmce-normal+mx4sio-normal",
53+ "mmce-normal+usb-normal",
54+ "mmce-normal+xfrom-normal",
55+ "mx4sio-normal+usb-normal",
56+ "mx4sio-normal+xfrom-normal",
57+ "usb-normal+xfrom-normal",
58+ "hdd-normal+mmce-normal+mx4sio-normal",
59+ "hdd-normal+mmce-normal+usb-normal",
60+ "hdd-normal+mmce-normal+xfrom-normal",
61+ "hdd-normal+mx4sio-normal+usb-normal",
62+ "hdd-normal+mx4sio-normal+xfrom-normal",
63+ "hdd-normal+usb-normal+xfrom-normal",
64+ "mmce-normal+mx4sio-normal+usb-normal",
65+ "mmce-normal+mx4sio-normal+xfrom-normal",
66+ "mmce-normal+usb-normal+xfrom-normal",
67+ "mx4sio-normal+usb-normal+xfrom-normal",
68+ "hdd-normal+mmce-normal+mx4sio-normal+usb-normal",
69+ "hdd-normal+mmce-normal+mx4sio-normal+xfrom-normal",
70+ "hdd-normal+mmce-normal+usb-normal+xfrom-normal",
71+ "hdd-normal+mx4sio-normal+usb-normal+xfrom-normal",
72+ "mmce-normal+mx4sio-normal+usb-normal+xfrom-normal",
73+ "hdd-normal+mmce-normal+mx4sio-normal+usb-normal+xfrom-normal",
74+ ]
75+ device_map = {
76+ "hdd": "HDD",
77+ "mmce": "MMCE",
78+ "mx4sio": "MX4SIO",
79+ "usb": "USB",
80+ "xfrom": "XFROM",
81+ }
82+ flag_order = ["HDD", "MMCE", "MX4SIO", "USB", "XFROM"]
4083
41- batches = []
42- for idx in range(0, len(variants), CHUNK_SIZE):
43- batch = variants[idx : idx + CHUNK_SIZE]
44- batches.append({"index": idx // CHUNK_SIZE, "batch": json.dumps(batch)})
84+ variants = []
85+ for label in labels:
86+ if "mmce-normal" in label and "mx4sio-normal" in label:
87+ continue
88+ flags = {}
89+ for part in label.split("+"):
90+ device, mode = part.split("-", 1)
91+ if mode != "normal":
92+ raise SystemExit(f"Unsupported mode in label: {label}")
93+ if device not in device_map:
94+ raise SystemExit(f"Unknown device in label: {label}")
95+ flags[device_map[device]] = 1
96+ flag_string = " ".join(f"{key}=1" for key in flag_order if key in flags)
97+ variants.append({"name": label, "flags": flag_string})
4598
46- print(json.dumps({"include": batches }, sort_keys=False))
99+ print(json.dumps({"include": variants }, sort_keys=False))
47100 with open("count.txt", "w", encoding="utf-8") as fh:
48101 fh.write(str(len(variants)))
49102 PY
@@ -73,51 +126,120 @@ jobs:
73126 id : meta
74127 run : echo "short_sha=${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
75128
76- - name : Build batch ${{ matrix.index }}
129+ - name : Build ${{ matrix.name }}
77130 env :
78- BATCH_JSON : ${{ matrix.batch }}
131+ VARIANT_NAME : ${{ matrix.name }}
132+ VARIANT_FLAGS : ${{ matrix.flags }}
79133 SHORT_SHA : ${{ steps.meta.outputs.short_sha }}
80134 run : |
81135 set -euo pipefail
82136 python3 - <<'PY'
83- import json, os, shlex, subprocess, sys
137+ import os
138+ import shlex
139+ import subprocess
84140
85- batch = json.loads(os.environ["BATCH_JSON"])
141+ name = os.environ["VARIANT_NAME"]
142+ flags = os.environ.get("VARIANT_FLAGS", "").strip()
86143 short_sha = os.environ["SHORT_SHA"]
87144
88- for entry in batch:
89- name = entry["name"]
90- flags = entry.get("flags", "").strip()
91- outdir = os.path.join("build", "variants", name)
92- base = ["make", f"VARIANT={name}", f"OUTDIR={outdir}", f"COMMIT_HASH={short_sha}"]
93- if flags:
94- base += shlex.split(flags)
95- subprocess.run(base + ["clean"], check=True)
96- subprocess.run(base + ["all"], check=True)
97- cfg = os.path.join(outdir, "BUILD_CONFIG.txt")
98- if not os.path.isfile(cfg):
99- raise SystemExit(f"Missing BUILD_CONFIG.txt for {name}")
145+ outdir = os.path.join("build", "variants", name)
146+ base = ["make", f"VARIANT={name}", f"OUTDIR={outdir}", f"COMMIT_HASH={short_sha}"]
147+ if flags:
148+ base += shlex.split(flags)
149+ subprocess.run(base + ["clean"], check=True)
150+ subprocess.run(base + ["all"], check=True)
151+ cfg = os.path.join(outdir, "BUILD_CONFIG.txt")
152+ if not os.path.isfile(cfg):
153+ raise SystemExit(f"Missing BUILD_CONFIG.txt for {name}")
100154 PY
101155
102- - name : Package batch ${{ matrix.index }}
156+ - name : Stage built ELF for ${{ matrix.name }}
157+ run : |
158+ set -euo pipefail
159+ mkdir -p staged-elves
160+ variant_dir="build/variants/${{ matrix.name }}"
161+ elf_path="$variant_dir/PS2BBL.ELF"
162+ if [ ! -f "$elf_path" ]; then
163+ echo "Missing ELF for ${{ matrix.name }} at $elf_path" >&2
164+ exit 1
165+ fi
166+ mkdir -p "staged-elves/${{ matrix.name }}"
167+ cp "$elf_path" "staged-elves/${{ matrix.name }}/PS2BBLE.ELF"
168+
169+ - name : Upload staged ELF for ${{ matrix.name }}
170+ uses : actions/upload-artifact@v4
171+ with :
172+ name : built-elf-${{ matrix.name }}
173+ path : staged-elves/${{ matrix.name }}/PS2BBLE.ELF
174+ if-no-files-found : error
175+
176+ - name : Package ${{ matrix.name }}
103177 run : |
104178 set -euo pipefail
105179 mkdir -p artifacts
106- for variant_dir in build/variants/*; do
107- [ -d "$variant_dir" ] || continue
108- name=$(basename "$variant_dir")
109- tar -czf "artifacts/${name}.tar.gz" -C "$variant_dir" .
110- done
180+ tar -czf "artifacts/${{ matrix.name }}.tar.gz" -C "build/variants/${{ matrix.name }}" .
111181
112- - name : Upload batch ${{ matrix.index }}
182+ - name : Upload ${{ matrix.name }}
113183 uses : actions/upload-artifact@v4
114184 with :
115- name : ps2bbl-batch- ${{ matrix.index }}-${{ steps.meta.outputs.short_sha }}
185+ name : ps2bbl-${{ matrix.name }}-${{ steps.meta.outputs.short_sha }}
116186 path : artifacts/*.tar.gz
117187 if-no-files-found : error
118188
119- prerelease :
189+ package-elves :
120190 needs : build
191+ runs-on : ubuntu-latest
192+ steps :
193+ - name : Download staged ELFs
194+ uses : actions/download-artifact@v4
195+ with :
196+ pattern : built-elf-*
197+ path : dist
198+ merge-multiple : false
199+
200+ - name : Normalize dist layout
201+ run : |
202+ set -euo pipefail
203+ shopt -s nullglob
204+ for artifact_dir in dist/built-elf-*; do
205+ [ -d "$artifact_dir" ] || continue
206+ name="${artifact_dir##*/built-elf-}"
207+ elf_path="$artifact_dir/PS2BBLE.ELF"
208+ if [ ! -f "$elf_path" ]; then
209+ echo "Missing PS2BBLE.ELF for $name in $artifact_dir" >&2
210+ exit 1
211+ fi
212+ mkdir -p "dist/$name"
213+ cp "$elf_path" "dist/$name/PS2BBLE.ELF"
214+ done
215+ for leftover in dist/built-elf-*; do
216+ [ -d "$leftover" ] || continue
217+ rm -rf "$leftover"
218+ done
219+
220+ - name : Show dist tree
221+ run : |
222+ set -euo pipefail
223+ find dist -maxdepth 3 -print
224+
225+ - name : Zip staged ELFs
226+ run : |
227+ set -euo pipefail
228+ if [ ! -d dist ]; then
229+ echo "dist directory not found" >&2
230+ exit 1
231+ fi
232+ zip -r PS2BBLE.zip dist
233+
234+ - name : Upload PS2BBLE zip
235+ uses : actions/upload-artifact@v4
236+ with :
237+ name : ps2bble-zip-${{ github.sha }}
238+ path : PS2BBLE.zip
239+ if-no-files-found : error
240+
241+ prerelease :
242+ needs : package-elves
121243 if : github.event_name != 'pull_request'
122244 runs-on : ubuntu-latest
123245 permissions :
@@ -131,83 +253,46 @@ jobs:
131253 - name : Checkout repository
132254 uses : actions/checkout@v4
133255
134- - name : Discover available artifacts
135- id : discover_artifacts
136- env :
137- GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
256+ - name : Resolve release name
257+ id : release_name
138258 run : |
139259 set -euo pipefail
140- mkdir -p release-artifacts
141- artifacts=$(gh api --paginate "repos/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/artifacts" -q '.artifacts[].name' | sed -n '/^ps2bbl-/p')
142- count=$(printf "%s\n" "$artifacts" | sed '/^$/d' | wc -l | tr -d ' ')
143- echo "count=$count" >> "$GITHUB_OUTPUT"
144- if [ "$count" = "0" ]; then
145- echo "No matching artifacts were found for prerelease packaging." >> "$GITHUB_STEP_SUMMARY"
260+ if [ -n "${{ github.event.inputs.release_name }}" ]; then
261+ echo "value=${{ github.event.inputs.release_name }}" >> "$GITHUB_OUTPUT"
146262 else
147- {
148- echo "Matched artifacts (${count}):"
149- printf "%s\n" "$artifacts"
150- } >> "$GITHUB_STEP_SUMMARY"
263+ tag_name="${GITHUB_REF_NAME}"
264+ echo "value=${tag_name}" >> "$GITHUB_OUTPUT"
151265 fi
152266
153- - name : Download artifacts
154- if : steps.discover_artifacts.outputs.count != '0'
267+ - name : Download PS2BBLE zip
155268 uses : actions/download-artifact@v4
156269 with :
270+ name : ps2bble-zip-${{ github.sha }}
157271 path : release-artifacts
158- pattern : ps2bbl-*
159- merge-multiple : false
160272
161- - name : Ensure release directories
162- run : mkdir -p release-artifacts release-packages
163-
164- - name : Detect downloaded artifacts
165- id : artifacts
166- run : |
167- echo "Downloaded artifact layout:"
168- find release-artifacts -maxdepth 2 -print || true
169- count=$(find release-artifacts -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l | tr -d ' ')
170- echo "count=$count" >> "$GITHUB_OUTPUT"
171- if [ "$count" = "0" ]; then
172- echo "No artifacts were downloaded; skipping packaging." >> "$GITHUB_STEP_SUMMARY"
173- fi
174-
175- - name : Archive artifacts
176- if : steps.artifacts.outputs.count != '0'
177- run : |
178- for dir in release-artifacts/*; do
179- [ -d "$dir" ] || continue
180- base=$(basename "$dir")
181- tar -czf "release-packages/${base}.tar.gz" -C "$dir" .
182- done
183-
184- - name : Check packaged artifacts
185- id : packages
273+ - name : Verify PS2BBLE.zip
186274 run : |
187- count=$(find release-packages -type f -name '*.tar.gz' | wc -l | tr -d ' ')
188- echo "count=$count" >> "$GITHUB_OUTPUT"
189- if [ "$count" = "0" ]; then
190- echo "No packaged artifacts found; skipping release publication." >> "$GITHUB_STEP_SUMMARY"
275+ set -euo pipefail
276+ if [ ! -f release-artifacts/PS2BBLE.zip ]; then
277+ echo "PS2BBLE.zip not found in release-artifacts" >&2
278+ exit 1
191279 fi
192280
193- - name : Generate release notes
194- run : |
195- python3 scripts/generate_cl_release_notes.py \
196- --artifacts-dir release-artifacts \
197- --packages-dir release-packages \
198- --output RELEASE_BODY.md \
199- --release-sha "${GITHUB_SHA}"
200- echo "### Batch manifest overview" >> "$GITHUB_STEP_SUMMARY"
201- cat RELEASE_BODY.md >> "$GITHUB_STEP_SUMMARY"
202-
203281 - name : Publish prerelease
204- if : steps.packages.outputs.count != '0'
205282 uses : softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2
206283 with :
207284 tag_name : cl-${{ github.sha }}
208285 name : CL prerelease ${{ github.sha }}
209- body_path : RELEASE_BODY.md
286+ body : |
287+ Automated CL prerelease for `${{ steps.release_name.outputs.value }}`
288+ <img width="1536" height="394" alt="logo" src="https://github.com/user-attachments/assets/e536e2cd-4287-413e-9695-8e2aec3d25fb" />
289+
290+ Check the [readme.md](https://github.com/NathanNeurotic/PlayStation2-Basic-BootLoader-Extended#documentation)
291+ Test Sheet: https://github.com/NathanNeurotic/PlayStation2-Basic-BootLoader-Extended/blob/main/BETA_TEST_CHECKLIST.md
292+
293+ ### PS2BBLE.zip contains all versions
294+ Note: MMCE + MX4SIO combined variants are intentionally omitted (unsupported in embedded IRX mode).
210295 prerelease : true
211- files : release-packages/*
296+ files : release-artifacts/PS2BBLE.zip
212297 env :
213298 GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
0 commit comments