Skip to content

App Store Approval → Publish Website Changelog #1654

App Store Approval → Publish Website Changelog

App Store Approval → Publish Website Changelog #1654

name: App Store Approval → Publish Website Changelog
on:
schedule:
- cron: "0 * * * *" # every 1 hour
workflow_dispatch:
inputs:
dry_run:
description: "Run without merging PR"
type: boolean
default: false
permissions:
contents: write
pull-requests: write
jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
# ------------------------------------------------------------
# 1. Check App Store approval status
# ------------------------------------------------------------
- name: Check App Store approval status
id: appstore
run: |
npm init -y >/dev/null
npm install jsonwebtoken node-fetch@2 >/dev/null
node <<'EOF'
const fs = require("fs");
const jwt = require("jsonwebtoken");
const fetch = require("node-fetch");
const {
ASC_ISSUER_ID,
ASC_KEY_ID,
ASC_PRIVATE_KEY,
APP_ID
} = process.env;
const token = jwt.sign({}, ASC_PRIVATE_KEY.replace(/\\n/g, "\n"), {
algorithm: "ES256",
issuer: ASC_ISSUER_ID,
header: { kid: ASC_KEY_ID },
expiresIn: "10m",
audience: "appstoreconnect-v1",
});
async function run() {
const res = await fetch(
`https://api.appstoreconnect.apple.com/v1/apps/${APP_ID}/appStoreVersions`,
{ headers: { Authorization: `Bearer ${token}` } }
);
const json = await res.json();
const version = json.data?.[0];
const state = version?.attributes?.appStoreState;
console.log("App Store state:", state);
if (state === "READY_FOR_SALE") {
fs.appendFileSync(process.env.GITHUB_OUTPUT, "approved=true\n");
} else {
fs.appendFileSync(process.env.GITHUB_OUTPUT, "approved=false\n");
}
}
run();
EOF
env:
ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
ASC_PRIVATE_KEY: ${{ secrets.ASC_PRIVATE_KEY }}
APP_ID: ${{ secrets.APP_ID }}
# ------------------------------------------------------------
# 2. Find the PR waiting for App Store approval (by label)
# ------------------------------------------------------------
- name: Find changelog PR by label
if: inputs.dry_run == true || steps.appstore.outputs.approved == 'true'
id: pr
run: |
PRS=$(gh pr list \
--label "publish-after-appstore-approval" \
--state open \
--json number,title \
--repo "$GITHUB_REPOSITORY")
COUNT=$(echo "$PRS" | jq length)
if [ "$COUNT" -eq 0 ]; then
echo "No PR waiting for approval. Exiting."
exit 0
fi
if [ "$COUNT" -gt 1 ]; then
echo "❌ Multiple PRs waiting for approval. Abort."
echo "$PRS"
exit 1
fi
PR_NUMBER=$(echo "$PRS" | jq -r '.[0].number')
echo "Found PR #$PR_NUMBER"
printf "number=%s\n" "$PR_NUMBER" >> "$GITHUB_OUTPUT"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# ------------------------------------------------------------
# 3. Merge the PR
# ------------------------------------------------------------
- name: Merge changelog PR
if: steps.pr.outputs.number != '' && steps.appstore.outputs.approved == 'true' && inputs.dry_run != true
run: gh pr merge ${{ steps.pr.outputs.number }} --merge --repo "$GITHUB_REPOSITORY"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# ------------------------------------------------------------
# 4. Send email notification (with PR link)
# ------------------------------------------------------------
- name: Send email notification
if: steps.pr.outputs.number != '' && (inputs.dry_run == true || steps.appstore.outputs.approved == 'true')
run: |
npm install nodemailer >/dev/null
node <<'EOF'
const nodemailer = require("nodemailer");
const prNumber = process.env.PR_NUMBER;
const repo = process.env.GITHUB_REPOSITORY;
const prUrl = `${process.env.GITHUB_SERVER_URL}/${repo}/pull/${prNumber}`;
const transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: process.env.EMAIL_SMTP_USER,
pass: process.env.EMAIL_SMTP_PASS,
},
});
transporter.sendMail({
from: `"Joodle Release Bot" <${process.env.EMAIL_SMTP_USER}>`,
to: process.env.EMAIL_TO,
subject: "✅ Joodle changelog published",
text: `
Apple approved your app 🎉
The website changelog PR has been merged into main.
PR #${prNumber}
${prUrl}
Users opening Joodle will now see the latest changelog.
`,
});
EOF
env:
EMAIL_SMTP_USER: ${{ secrets.EMAIL_SMTP_USER }}
EMAIL_SMTP_PASS: ${{ secrets.EMAIL_SMTP_PASS }}
EMAIL_TO: ${{ secrets.EMAIL_TO }}
PR_NUMBER: ${{ steps.pr.outputs.number }}