@@ -322,6 +322,92 @@ jobs:
322322 esac
323323 echo "latest tag and release body verified for $RELEASE_SHA"
324324
325+ - name : Republish release to advance published_at
326+ env :
327+ GH_TOKEN : ${{ github.token }}
328+ RELEASE_SHA : ${{ github.sha }}
329+ run : |
330+ START_TIME_ISO=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
331+ START_TIME_EPOCH=$(date -d "$START_TIME_ISO" +%s 2>/dev/null || date -jf "%Y-%m-%dT%H:%M:%SZ" "$START_TIME_ISO" +%s)
332+ echo "Run start time: $START_TIME_ISO ($START_TIME_EPOCH)"
333+
334+ # Get release metadata and ID
335+ RELEASE_DATA=$(gh api "repos/${{ github.repository }}/releases/tags/latest")
336+ RELEASE_ID=$(echo "$RELEASE_DATA" | jq -r '.id')
337+ echo "Release ID: $RELEASE_ID"
338+
339+ PRE_ASSETS=$(echo "$RELEASE_DATA" | jq -c '[.assets[] | {name, id}]')
340+ echo "Pre-toggle assets: $PRE_ASSETS"
341+
342+ # Assert that all 6 managed assets (including manifest and MP3) are present before toggling
343+ EXPECTED_NAMES='["manifest.json","Plurality-english.pdf","Plurality-english.epub","Plurality-traditional-mandarin.pdf","Plurality-traditional-mandarin.epub","Plurality-english.mp3"]'
344+ MISSING_PRE=$(echo "$PRE_ASSETS" | jq --argjson expected "$EXPECTED_NAMES" -r '
345+ ($expected - [.[] | .name]) | join(", ")
346+ ')
347+ if [ -n "$MISSING_PRE" ]; then
348+ echo "Error: Pre-toggle release is missing expected assets: $MISSING_PRE" >&2
349+ exit 1
350+ fi
351+
352+ # Setup a failure-safe trap to guarantee that the release is never left as a draft if the step is cancelled/fails
353+ trap '
354+ echo "Trap activated. Ensuring release status is restored..."
355+ gh api -X PATCH "repos/${{ github.repository }}/releases/$RELEASE_ID" -F draft=false -F make_latest=true >/dev/null || true
356+ ' EXIT
357+
358+ # Toggle draft status to force published_at to advance
359+ echo "Patching release to draft=true..."
360+ gh api -X PATCH "repos/${{ github.repository }}/releases/$RELEASE_ID" -F draft=true >/dev/null
361+
362+ echo "Patching release to draft=false and make_latest=true..."
363+ gh api -X PATCH "repos/${{ github.repository }}/releases/$RELEASE_ID" -F draft=false -F make_latest=true >/dev/null
364+
365+ # Disable the trap on success
366+ trap - EXIT
367+
368+ # Verification assertions on the final state
369+ echo "Verifying final release state..."
370+ FINAL_DATA=$(gh api "repos/${{ github.repository }}/releases/$RELEASE_ID")
371+
372+ DRAFT=$(echo "$FINAL_DATA" | jq -r '.draft')
373+ PRERELEASE=$(echo "$FINAL_DATA" | jq -r '.prerelease')
374+ if [ "$DRAFT" != "false" ] || [ "$PRERELEASE" != "false" ]; then
375+ echo "Error: Final release is draft=$DRAFT, prerelease=$PRERELEASE (expected both false)" >&2
376+ exit 1
377+ fi
378+
379+ TAG=$(echo "$FINAL_DATA" | jq -r '.tag_name')
380+ BODY=$(echo "$FINAL_DATA" | jq -r '.body')
381+ if [ "$TAG" != "latest" ]; then
382+ echo "Error: Final release tag is $TAG (expected latest)" >&2
383+ exit 1
384+ fi
385+ case "$BODY" in
386+ *"$RELEASE_SHA"*) ;;
387+ *) echo "Error: Final release body does not contain $RELEASE_SHA" >&2; exit 1 ;;
388+ esac
389+
390+ POST_ASSETS=$(echo "$FINAL_DATA" | jq -c '[.assets[] | {name, id}]')
391+ echo "Post-toggle assets: $POST_ASSETS"
392+
393+ MISMATCH=$(jq -n --argjson pre "$PRE_ASSETS" --argjson post "$POST_ASSETS" -r '
394+ ($pre - $post) | map(.name + ":" + (.id|tostring)) | join(", ")
395+ ')
396+ if [ -n "$MISMATCH" ]; then
397+ echo "Error: Stored assets mismatch or changed IDs/names: $MISMATCH" >&2
398+ exit 1
399+ fi
400+
401+ PUBLISHED_AT=$(echo "$FINAL_DATA" | jq -r '.published_at')
402+ PUBLISHED_EPOCH=$(date -d "$PUBLISHED_AT" +%s 2>/dev/null || date -jf "%Y-%m-%dT%H:%M:%SZ" "$PUBLISHED_AT" +%s)
403+ echo "Published at: $PUBLISHED_AT ($PUBLISHED_EPOCH)"
404+ if [ "$PUBLISHED_EPOCH" -lt "$START_TIME_EPOCH" ]; then
405+ echo "Error: Final published_at ($PUBLISHED_AT) is before run start ($START_TIME_ISO)" >&2
406+ exit 1
407+ fi
408+
409+ echo "Release successfully republished. published_at advanced from pre-toggle timestamp."
410+
325411 webhook :
326412 runs-on : ubuntu-latest
327413 needs : [release, deploy-pages]
0 commit comments