diff --git a/.github/workflows/prerelease_open_pr_builds.yaml b/.github/workflows/prerelease_open_pr_builds.yaml new file mode 100644 index 00000000..f481b8d6 --- /dev/null +++ b/.github/workflows/prerelease_open_pr_builds.yaml @@ -0,0 +1,117 @@ +name: Prerelease Open PR Builds + +on: + workflow_dispatch: + + workflow_run: + workflows: + - "Build OpenEarable v2 Firmware" + types: + - completed + +permissions: + contents: write + actions: read + pull-requests: read + +env: + TAG: pr-builds + BUILD_WORKFLOW_NAME: "Build OpenEarable v2 Firmware" + +jobs: + prerelease: + if: > + github.event_name != 'workflow_run' || + ( + github.event.workflow_run.conclusion == 'success' && + github.event.workflow_run.event == 'pull_request' + ) + runs-on: ubuntu-latest + steps: + - name: Ensure prerelease exists + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh release view "$TAG" --repo "${{ github.repository }}" >/dev/null 2>&1 || \ + gh release create "$TAG" \ + --repo "${{ github.repository }}" \ + --prerelease \ + --title "Open PR builds" \ + --notes "Rolling prerelease containing build outputs from all open PRs." + - name: List open PRs + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh pr list --repo "${{ github.repository }}" --state open --json number,headRefOid,title --limit 200 > prs.json + - name: Download artifacts from each PR's latest successful build + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + rm -rf collected && mkdir -p collected + jq -c '.[]' prs.json | while read -r pr; do + num=$(echo "$pr" | jq -r .number) + sha=$(echo "$pr" | jq -r .headRefOid) + title=$(echo "$pr" | jq -r .title) + safe_title=$(echo "$title" | sed 's/[^a-zA-Z0-9_-]/_/g' | cut -c1-50) + run_id=$(gh run list \ + --repo "${{ github.repository }}" \ + --workflow "$BUILD_WORKFLOW_NAME" \ + --commit "$sha" \ + --status success \ + --limit 1 \ + --json databaseId \ + --jq '.[0].databaseId') + if [ -z "$run_id" ] || [ "$run_id" = "null" ]; then + echo "PR #$num: no successful run found" + continue + fi + out="collected/pr-$num" + rm -rf "$out" && mkdir -p "$out" + if ! gh run download "$run_id" --repo "${{ github.repository }}" -n "openearable_v2_firmware.elf" -D "$out/elf" 2>&1; then + echo "PR #$num: failed to download firmware.elf artifact (may be expired)" + rm -rf "$out" + continue + fi + if ! gh run download "$run_id" --repo "${{ github.repository }}" -n "openearable_v2_fota.zip" -D "$out/fota" 2>&1; then + echo "PR #$num: failed to download fota.zip artifact (may be expired)" + rm -rf "$out" + continue + fi + (cd "$out/fota" && zip -r "../openearable_v2_fota.zip" .) + mv "$out/elf/openearable_v2_firmware.elf" "$out/openearable_v2_firmware.elf" + rm -rf "$out/elf" "$out/fota" + + echo "$safe_title" > "$out/title.txt" + + echo "PR #$num ($title): artifacts downloaded and prepared successfully" + done + - name: Upload all PR assets to prerelease (overwrite) + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + mkdir -p collected/upload + find collected -type f \( -name "*.elf" -o -name "*.zip" \) | while read -r f; do + pr=$(echo "$f" | sed -n 's#.*/pr-\([0-9]\+\)/.*#\1#p') + base=$(basename "$f") + title_file="collected/pr-$pr/title.txt" + if [ -f "$title_file" ]; then + safe_title=$(cat "$title_file") + asset="pr-${pr}-${safe_title}-${base}" + else + asset="pr-${pr}-${base}" + fi + cp "$f" "collected/upload/$asset" + gh release upload "$TAG" "collected/upload/$asset" --repo "${{ github.repository }}" --clobber + done + # delete assets of closed PRs + open_prs="$(jq -r '.[].number' prs.json || true)" + gh release view "$TAG" --repo "${{ github.repository }}" --json assets -q '.assets[].name' | while read -r asset; do + pr="$(echo "$asset" | sed -n 's/^pr-\([0-9]\+\)-.*$/\1/p')" + [ -n "$pr" ] || continue + if ! echo "$open_prs" | grep -qx "$pr"; then + echo "Deleting asset $asset (PR #$pr is closed/merged)" + gh release delete-asset "$TAG" "$asset" --repo "${{ github.repository }}" --yes + fi + done