mirror of
https://github.com/KytyPS5/kytyps5.github.io.git
synced 2026-10-09 08:49:37 -07:00
268 lines
12 KiB
YAML
268 lines
12 KiB
YAML
name: Convert compat issue to report PR
|
|
|
|
# `/compat` comment command on a compat mirror issue (created by the "Mirror
|
|
# game-status issues" poller): converts the mirror's upstream game-status
|
|
# report into src/content/compat/<game>-<os>.md and opens ONE PR for that game
|
|
# (branch compat/<game>-<os>). One game per PR keeps reviews separate — the
|
|
# old flow dumped every converted game into a single standing report PR.
|
|
#
|
|
# The mirror body's `## Source` footer records the upstream KytyPS5 issue
|
|
# number / URL / creation date (see scripts/lib/status-issues.mjs); the rest
|
|
# of the body is the upstream issue form, parsed by scripts/issue-to-compat.mjs.
|
|
# Re-running `/compat` (e.g. after the upstream issue was fixed) overwrites
|
|
# that game's report and updates the same PR in place. On success the comment
|
|
# gets a 👀 reaction and the mirror issue is closed — closed mirror = converted.
|
|
#
|
|
# Fields the issue can't express correctly can be fixed first with the /setos /
|
|
# /setid /settitle /setstatus commands (compat-set-fields.yml): they append a
|
|
# `## Overrides` section to the mirror body which issue-to-compat.mjs reads
|
|
# here — overrides win over the values parsed from the issue (e.g. an OS that
|
|
# doesn't generalize, or a missing PPSA-XXXXX). Comment `/compat` again after
|
|
# setting fields.
|
|
#
|
|
# Failures (not a mirror, unparseable fields, PR-step hiccup) post the reason
|
|
# back on the issue as a comment — `❌ /compat failed. <reason>` — so the
|
|
# maintainer sees it without digging through the workflow log; the run still
|
|
# goes red. The failing step writes /tmp/compat-failure.txt and the final
|
|
# "Post the failure as a comment" step (if: failure()) reads it.
|
|
on:
|
|
issue_comment:
|
|
types: [created]
|
|
workflow_dispatch:
|
|
inputs:
|
|
issue_number:
|
|
description: "Mirror issue number to convert"
|
|
required: true
|
|
type: string
|
|
|
|
permissions:
|
|
contents: write
|
|
pull-requests: write
|
|
issues: write # reaction + close the mirror issue + failure comments
|
|
|
|
# Serialize /compat runs per issue so two quick comments can't race on the
|
|
# same branch.
|
|
concurrency:
|
|
group: compat-convert-${{ github.event.issue.number || inputs.issue_number }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
convert:
|
|
if: >
|
|
github.event_name == 'workflow_dispatch' ||
|
|
(github.event.issue.pull_request == null &&
|
|
startsWith(github.event.comment.body, '/compat') &&
|
|
contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association))
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 22
|
|
cache: npm
|
|
- run: npm ci
|
|
|
|
- name: Resolve mirror issue context
|
|
id: issue_context
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
ISSUE_NUMBER: ${{ github.event.issue.number || inputs.issue_number }}
|
|
EVENT_BODY: ${{ github.event.issue.body }}
|
|
EVENT_TITLE: ${{ github.event.issue.title }}
|
|
REPO: ${{ github.repository }}
|
|
run: |
|
|
node - <<'NODE'
|
|
(async () => {
|
|
const fs = require("node:fs");
|
|
const { mirrorSource } = await import("./scripts/lib/status-issues.mjs");
|
|
let body = process.env.EVENT_BODY ?? "";
|
|
let title = process.env.EVENT_TITLE ?? "";
|
|
const issueNum = process.env.ISSUE_NUMBER;
|
|
const repo = process.env.REPO;
|
|
const token = process.env.GH_TOKEN;
|
|
let issueUrl = `https://github.com/${repo}/issues/${issueNum}`;
|
|
|
|
if ((!body || !title) && issueNum) {
|
|
const res = await fetch(`https://api.github.com/repos/${repo}/issues/${issueNum}`, {
|
|
headers: { authorization: `Bearer ${token}`, "user-agent": "gh-actions" }
|
|
});
|
|
if (!res.ok) {
|
|
const msg = `Failed to fetch mirror issue #${issueNum}: HTTP ${res.status}`;
|
|
fs.writeFileSync("/tmp/compat-failure.txt", msg);
|
|
console.error("::error::" + msg);
|
|
process.exit(1);
|
|
}
|
|
const data = await res.json();
|
|
body = data.body ?? "";
|
|
title = data.title ?? title;
|
|
issueUrl = data.html_url || issueUrl;
|
|
}
|
|
|
|
fs.writeFileSync("/tmp/mirror-issue-body.txt", body);
|
|
const src = mirrorSource(body);
|
|
if (!src) {
|
|
const msg =
|
|
"This issue is not a compat mirror — its body has no `## Source` footer " +
|
|
"linking a KytyPS5 issue. /compat converts issues mirrored by the " +
|
|
"\"Mirror game-status issues\" workflow.";
|
|
fs.writeFileSync("/tmp/compat-failure.txt", msg);
|
|
console.error("::error::" + msg);
|
|
process.exit(1);
|
|
}
|
|
const cleanTitle = String(title ?? "").replace(/[\r\n]+/g, " ").trim();
|
|
const out = process.env.GITHUB_OUTPUT;
|
|
if (out) {
|
|
fs.appendFileSync(
|
|
out,
|
|
`mirror_number=${issueNum}\nmirror_title=${cleanTitle}\nmirror_url=${issueUrl}\nnumber=${src.number}\nurl=${src.url}\ncreated=${src.created}\n`
|
|
);
|
|
}
|
|
})();
|
|
NODE
|
|
|
|
- name: Convert the issue to a report
|
|
id: convert
|
|
env:
|
|
ISSUE_DATE: ${{ steps.issue_context.outputs.created }}
|
|
ISSUE_NUM: ${{ steps.issue_context.outputs.number }}
|
|
ISSUE_URL: ${{ steps.issue_context.outputs.url }}
|
|
MIRROR_TITLE: ${{ steps.issue_context.outputs.mirror_title }}
|
|
run: |
|
|
# Capture the converter's output so a failure can be posted back as
|
|
# a readable comment (the final step reads /tmp/compat-failure.txt).
|
|
if ! CONVERT_OUT="$(node scripts/issue-to-compat.mjs \
|
|
--issue-body-file /tmp/mirror-issue-body.txt \
|
|
--issue-title "$MIRROR_TITLE" \
|
|
--date "$ISSUE_DATE" \
|
|
--source "KytyPS5 issue #$ISSUE_NUM" \
|
|
--source-url "$ISSUE_URL" 2>&1)"; then
|
|
MSG="$(printf '%s' "$CONVERT_OUT" | sed -n 's/^\[issue-to-compat\] //p' | tail -1)"
|
|
[ -n "$MSG" ] || MSG="$(printf '%s' "$CONVERT_OUT" | tail -3 | tr '\n' ' ')"
|
|
printf '%s' "$MSG" > /tmp/compat-failure.txt
|
|
exit 1
|
|
fi
|
|
SLUG="$(printf '%s\n' "$CONVERT_OUT" | sed -n 's#^\[issue-to-compat\] wrote src/content/compat/\([^/]*\)\.md$#\1#p' | head -1)"
|
|
[ -n "$SLUG" ] || {
|
|
echo "::error::The converter did not report a written report file."
|
|
printf 'The converter did not report a written report file.' > /tmp/compat-failure.txt
|
|
exit 1
|
|
}
|
|
TITLE="$(sed -n 's/^title: "\(.*\)"$/\1/p' "src/content/compat/$SLUG.md" | head -1)"
|
|
OS="$(sed -n 's/^os: "\(.*\)"$/\1/p' "src/content/compat/$SLUG.md" | head -1)"
|
|
echo "slug=$SLUG" >> "$GITHUB_OUTPUT"
|
|
echo "pr_title=compat: $TITLE ($OS)" >> "$GITHUB_OUTPUT"
|
|
if ! node scripts/validate-compat.mjs >/tmp/compat-validate.log 2>&1; then
|
|
printf 'The generated report is invalid — %s' "$(tail -1 /tmp/compat-validate.log)" > /tmp/compat-failure.txt
|
|
exit 1
|
|
fi
|
|
|
|
- name: Open or update the report PR
|
|
id: cpr
|
|
uses: peter-evans/create-pull-request@v6
|
|
continue-on-error: true
|
|
with:
|
|
token: ${{ secrets.ACTIONS_BOT_TOKEN || secrets.GITHUB_TOKEN }}
|
|
add-paths: |
|
|
src/content/compat/
|
|
commit-message: "${{ steps.convert.outputs.pr_title }}"
|
|
branch: compat/${{ steps.convert.outputs.slug }}
|
|
title: ${{ steps.convert.outputs.pr_title }}
|
|
body: |
|
|
Auto-converted from ${{ steps.issue_context.outputs.mirror_url }} via `/compat`.
|
|
|
|
${{ steps.issue_context.outputs.url }}
|
|
|
|
<!-- mirror-issue: ${{ steps.issue_context.outputs.mirror_number }} -->
|
|
delete-branch: true
|
|
|
|
- name: Fail the job if the report PR could not be opened
|
|
if: steps.cpr.outcome == 'failure'
|
|
env:
|
|
SLUG: ${{ steps.convert.outputs.slug }}
|
|
run: |
|
|
printf 'Failed to open or update the report PR (branch compat/%s) — the branch may already exist without a PR. See the workflow log for the create-pull-request error.' "$SLUG" > /tmp/compat-failure.txt
|
|
exit 1
|
|
|
|
- name: Enable auto-merge on the report PR
|
|
id: auto_merge
|
|
if: steps.cpr.outcome == 'success'
|
|
env:
|
|
GH_TOKEN: ${{ secrets.ACTIONS_BOT_TOKEN || secrets.GITHUB_TOKEN }}
|
|
CPR_NUMBER: ${{ steps.cpr.outputs.pull-request-number }}
|
|
CPR_URL: ${{ steps.cpr.outputs.pull-request-url }}
|
|
SLUG: ${{ steps.convert.outputs.slug }}
|
|
run: |
|
|
PR_NUMBER="$CPR_NUMBER"
|
|
PR_URL="$CPR_URL"
|
|
if [ -z "$PR_NUMBER" ]; then
|
|
PR_NUMBER="$(gh pr list --head "compat/${SLUG}" --state open --json number --jq '.[0].number // empty')"
|
|
if [ -n "$PR_NUMBER" ]; then
|
|
PR_URL="$(gh pr view "$PR_NUMBER" --json url --jq '.url // empty')"
|
|
fi
|
|
fi
|
|
|
|
if [ -n "$PR_NUMBER" ]; then
|
|
echo "pr_number=$PR_NUMBER" >> "$GITHUB_OUTPUT"
|
|
echo "pr_url=$PR_URL" >> "$GITHUB_OUTPUT"
|
|
if gh pr merge "$PR_NUMBER" --auto --squash; then
|
|
echo "enabled=true" >> "$GITHUB_OUTPUT"
|
|
else
|
|
PR_STATUS="$(gh pr view "$PR_NUMBER" --json autoMergeRequest,state --jq '.autoMergeRequest != null or .state == "MERGED"' 2>/dev/null || true)"
|
|
if [ "$PR_STATUS" = "true" ]; then
|
|
echo "enabled=true" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "enabled=false" >> "$GITHUB_OUTPUT"
|
|
echo "::warning::Failed to enable auto-merge on PR #$PR_NUMBER"
|
|
fi
|
|
fi
|
|
else
|
|
echo "enabled=noop" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
- name: Acknowledge and close the mirror
|
|
if: steps.cpr.outcome == 'success' && steps.convert.outputs.slug != ''
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
EVENT_NAME: ${{ github.event_name }}
|
|
REPO: ${{ github.repository }}
|
|
COMMENT_ID: ${{ github.event.comment.id }}
|
|
PR_TITLE: ${{ steps.convert.outputs.pr_title }}
|
|
PR_URL: ${{ steps.auto_merge.outputs.pr_url || steps.cpr.outputs.pull-request-url }}
|
|
AUTO_MERGE_ENABLED: ${{ steps.auto_merge.outputs.enabled }}
|
|
MIRROR_NUMBER: ${{ steps.issue_context.outputs.mirror_number }}
|
|
run: |
|
|
if [ "$EVENT_NAME" = "issue_comment" ]; then
|
|
gh api --method POST \
|
|
"repos/${REPO}/issues/comments/${COMMENT_ID}/reactions" \
|
|
-f content=eyes || true
|
|
fi
|
|
|
|
if [ "$AUTO_MERGE_ENABLED" = "false" ]; then
|
|
gh issue comment "$MIRROR_NUMBER" \
|
|
-b "⚠️ Report PR was opened: ${PR_TITLE} (${PR_URL}), but auto-merge could not be enabled. The mirror issue will remain open until the PR is merged manually."
|
|
elif [ "$AUTO_MERGE_ENABLED" = "noop" ]; then
|
|
gh issue close "$MIRROR_NUMBER" \
|
|
--comment "Compatibility report (${PR_TITLE}) is already up to date on main; no pull request needed."
|
|
else
|
|
gh issue close "$MIRROR_NUMBER" \
|
|
--comment "Converted — report PR: ${PR_TITLE} (${PR_URL})"
|
|
fi
|
|
|
|
- name: Post the failure as a comment
|
|
if: failure()
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
REPO: ${{ github.repository }}
|
|
TARGET_ISSUE: ${{ steps.issue_context.outputs.mirror_number || inputs.issue_number || github.event.issue.number }}
|
|
run: |
|
|
if [ ! -f /tmp/compat-failure.txt ]; then
|
|
echo "no captured failure message — skipping comment"
|
|
exit 0
|
|
fi
|
|
if [ -n "$TARGET_ISSUE" ]; then
|
|
MSG="$(cat /tmp/compat-failure.txt)"
|
|
gh api --method POST \
|
|
"repos/${REPO}/issues/${TARGET_ISSUE}/comments" \
|
|
-f "body=❌ **/compat failed.** $MSG" || true
|
|
fi
|