Skip to content

Edit data for **PCD @ Montreal** #32

Edit data for **PCD @ Montreal**

Edit data for **PCD @ Montreal** #32

name: Zine Intake
on:
issues:
types: [opened, edited, reopened, labeled]
concurrency:
group: new-zine-intake-${{ github.event.issue.number }}
cancel-in-progress: true
permissions:
contents: write
issues: write
pull-requests: write
env:
STATUS_COMMENT_MARKER: <!-- new-zine-intake-status -->
jobs:
publish-zine-review:
if: contains(github.event.issue.labels.*.name, 'new zine')
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v6
with:
ref: ${{ github.event.repository.default_branch }}
fetch-depth: 0
- uses: actions/setup-node@v6
with:
node-version: '24'
cache: npm
cache-dependency-path: pcd-website/package-lock.json
- name: Inspect open zine review pull requests
id: open-prs
uses: actions/github-script@v8
with:
script: |
const { owner, repo } = context.repo;
const branch = `automation/new-zine-${context.issue.number}`;
const pulls = await github.paginate(github.rest.pulls.list, { owner, repo, state: 'open', per_page: 100 });
const reserved = new Set(); const orders = []; let current; let currentSlug = '';
for (const pull of pulls) {
const isCurrent = pull.head.ref === branch && pull.head.repo?.full_name === `${owner}/${repo}`;
const files = await github.paginate(github.rest.pulls.listFiles, { owner, repo, pull_number: pull.number, per_page: 100 });
for (const file of files) {
const match = file.filename.match(/^pcd-website\/src\/content\/zines\/([^/]+)\//);
if (match && file.status !== 'removed' && !isCurrent) reserved.add(match[1]);
if (match && file.status !== 'removed' && isCurrent) currentSlug = match[1];
const indexMatch = file.filename.match(/^pcd-website\/src\/content\/zines\/([^/]+)\/index\.md$/);
if (!indexMatch || file.status === 'removed') continue;
try {
const index = await github.rest.repos.getContent({ owner, repo, path: file.filename, ref: pull.head.sha });
const text = Buffer.from(index.data.content, index.data.encoding).toString('utf8');
const orderMatch = text.match(/^order:\s*(\d+)\s*$/m);
if (!orderMatch) continue;
if (isCurrent) core.setOutput('current_order', orderMatch[1]);
else orders.push(orderMatch[1]);
} catch {}
}
if (isCurrent) current = pull;
}
core.setOutput('reserved_slugs', [...reserved].join(','));
core.setOutput('orders', orders.join(','));
core.setOutput('current_slug', currentSlug);
core.setOutput('current_pr_number', current?.number ?? '');
- name: Install website dependencies
run: npm ci
working-directory: pcd-website
- name: Generate and validate publication-ready zine
id: generate
run: node .github/scripts/process-new-zine-issue.mjs
env:
ZINE_RESERVED_SLUGS: ${{ steps.open-prs.outputs.reserved_slugs }}
ZINE_OPEN_PR_ORDERS: ${{ steps.open-prs.outputs.orders }}
ZINE_CURRENT_SLUG: ${{ steps.open-prs.outputs.current_slug }}
ZINE_CURRENT_ORDER: ${{ steps.open-prs.outputs.current_order }}
- name: Build the production site
if: steps.generate.outputs.valid == 'true'
run: npm run build
working-directory: pcd-website
- name: Publish review branch
if: steps.generate.outputs.valid == 'true'
env:
BRANCH: ${{ steps.generate.outputs.branch }}
MESSAGE: ${{ steps.generate.outputs.commit_message }}
run: |
git config user.name github-actions[bot]
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
git switch -C "$BRANCH"
git add pcd-website/src/content/zines
git commit -m "$MESSAGE"
git push --force-with-lease origin "$BRANCH"
- name: Create or update review pull request
if: steps.generate.outputs.valid == 'true'
id: review-pr
uses: actions/github-script@v8
env:
BRANCH: ${{ steps.generate.outputs.branch }}
TITLE: ${{ steps.generate.outputs.pr_title }}
BODY_PATH: ${{ steps.generate.outputs.pr_body_path }}
with:
script: |
const fs = require('fs'); const body = fs.readFileSync(process.env.BODY_PATH, 'utf8');
const pulls = await github.rest.pulls.list({ owner: context.repo.owner, repo: context.repo.repo, state: 'open', head: `${context.repo.owner}:${process.env.BRANCH}` });
const input = { owner: context.repo.owner, repo: context.repo.repo, title: process.env.TITLE, body };
const pull = pulls.data[0] ? await github.rest.pulls.update({ ...input, pull_number: pulls.data[0].number }) : await github.rest.pulls.create({ ...input, head: process.env.BRANCH, base: context.payload.repository.default_branch });
await github.rest.issues.addLabels({ owner: context.repo.owner, repo: context.repo.repo, issue_number: pull.data.number, labels: ['new zine', 'needs review'] });
try {
await github.rest.issues.removeLabel({ owner: context.repo.owner, repo: context.repo.repo, issue_number: pull.data.number, name: 'needs changes' });
} catch (error) { if (error.status !== 404) throw error; }
core.setOutput('number', pull.data.number); core.setOutput('url', pull.data.html_url);
- name: Upsert successful intake status
if: steps.generate.outputs.valid == 'true'
uses: actions/github-script@v8
env:
PR_NUMBER: ${{ steps.review-pr.outputs.number }}
PR_URL: ${{ steps.review-pr.outputs.url }}
TITLE: ${{ steps.generate.outputs.zine_title }}
with:
script: |
const marker = process.env.STATUS_COMMENT_MARKER;
const body = `${marker}\n**"${process.env.TITLE}"** is ready for review in [#${process.env.PR_NUMBER}](${process.env.PR_URL}).`;
const { data: comments } = await github.rest.issues.listComments({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number });
const existing = comments.find((comment) => comment.user?.type === 'Bot' && comment.body?.includes(marker));
const input = { owner: context.repo.owner, repo: context.repo.repo, body };
if (existing) await github.rest.issues.updateComment({ ...input, comment_id: existing.id }); else await github.rest.issues.createComment({ ...input, issue_number: context.issue.number });
- name: Upsert validation status
if: steps.generate.outputs.valid == 'false'
uses: actions/github-script@v8
env:
COMMENT_PATH: ${{ steps.generate.outputs.validation_comment_path }}
CURRENT_PR_NUMBER: ${{ steps.open-prs.outputs.current_pr_number }}
with:
script: |
const fs = require('fs'); const marker = process.env.STATUS_COMMENT_MARKER;
const pullNumber = Number(process.env.CURRENT_PR_NUMBER);
if (Number.isInteger(pullNumber) && pullNumber > 0) {
const label = { name: 'needs changes', color: 'd93f0b', description: 'Submission changes are required before review can continue' };
try { await github.rest.issues.getLabel({ owner: context.repo.owner, repo: context.repo.repo, name: label.name }); }
catch (error) { if (error.status === 404) await github.rest.issues.createLabel({ owner: context.repo.owner, repo: context.repo.repo, ...label }); else throw error; }
await github.rest.issues.addLabels({ owner: context.repo.owner, repo: context.repo.repo, issue_number: pullNumber, labels: [label.name] });
try { await github.rest.issues.removeLabel({ owner: context.repo.owner, repo: context.repo.repo, issue_number: pullNumber, name: 'needs review' }); }
catch (error) { if (error.status !== 404) throw error; }
}
const body = `${marker}\n> [!WARNING]\n> The existing review PR, if any, was left unchanged.\n\n${fs.readFileSync(process.env.COMMENT_PATH, 'utf8')}`;
const { data: comments } = await github.rest.issues.listComments({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number });
const existing = comments.find((comment) => comment.user?.type === 'Bot' && comment.body?.includes(marker));
const input = { owner: context.repo.owner, repo: context.repo.repo, body };
if (existing) await github.rest.issues.updateComment({ ...input, comment_id: existing.id }); else await github.rest.issues.createComment({ ...input, issue_number: context.issue.number });