Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .github/scripts/netlify-purge-alias.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env bash
# Delete every deploy on $NETLIFY_SITE_ID published under $ALIAS, so the alias
# is unbound and the next deploy is the only thing serving it.
set -euo pipefail

api() { curl -sf -H "Authorization: Bearer $NETLIFY_AUTH_TOKEN" "$@"; }

for page in 1 2 3 4 5; do
deploys=$(api "https://api.netlify.com/api/v1/sites/$NETLIFY_SITE_ID/deploys?per_page=100&page=$page")
[ "$(echo "$deploys" | jq length)" = 0 ] && break
for id in $(echo "$deploys" | jq -r --arg a "$ALIAS" \
'.[] | select(.branch == $a or (.deploy_url // "" | contains("//" + $a + "--"))) | .id'); do
echo "deleting deploy $id"
api -X DELETE "https://api.netlify.com/api/v1/deploys/$id" -o /dev/null
done
done
33 changes: 33 additions & 0 deletions .github/scripts/use-workspace-packages.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/usr/bin/env node
// Rewrite a nested site's dependencies on packages this monorepo publishes to
// `workspace:*`, so it builds against the branch instead of npm.
import { readFile, writeFile, rm } from "node:fs/promises";
import { execFileSync } from "node:child_process";
import { join } from "node:path";

const dir = process.argv[2];
if (!dir) throw new Error("usage: use-workspace-packages.mjs <site-dir>");

const workspace = JSON.parse(
execFileSync("pnpm", ["-r", "list", "--depth", "-1", "--json"], {
encoding: "utf8",
}),
);
const local = new Set(
workspace.filter((p) => p.name && p.path !== dir).map((p) => p.name),
);

const manifestPath = join(dir, "package.json");
const manifest = JSON.parse(await readFile(manifestPath, "utf8"));

for (const field of ["dependencies", "devDependencies"]) {
for (const name of Object.keys(manifest[field] ?? {})) {
if (!local.has(name)) continue;
manifest[field][name] = "workspace:*";
console.log(`${name} -> workspace:*`);
}
}

await writeFile(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
await rm(join(dir, "pnpm-workspace.yaml"), { force: true });
await rm(join(dir, "pnpm-lock.yaml"), { force: true });
124 changes: 124 additions & 0 deletions .github/workflows/preview.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
name: patchwork preview

on:
pull_request:
types: [opened, synchronize, reopened, closed]

env:
SHELL_REPO: inkandswitch/patchwork.inkandswitch.com
SITE_DIR: sites/patchwork.inkandswitch.com
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
ALIAS: patchwork-preview-${{ github.event.number }}
MARKER: "<!-- patchwork-preview -->"

jobs:
preview:
if: github.event.action != 'closed'
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4

- uses: actions/checkout@v4
with:
repository: ${{ env.SHELL_REPO }}
path: ${{ env.SITE_DIR }}

- uses: pnpm/action-setup@v4
with:
run_install: false

- uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm

- name: build the shell against this branch's packages
run: |
node .github/scripts/use-workspace-packages.mjs "$GITHUB_WORKSPACE/$SITE_DIR"
pnpm install --no-frozen-lockfile
pnpm build

- name: free the alias
run: .github/scripts/netlify-purge-alias.sh

- name: deploy
id: deploy
working-directory: ${{ env.SITE_DIR }}
run: |
set -o pipefail
out=$(pnpm --package=netlify-cli dlx netlify deploy \
--dir=dist \
--no-build \
--alias="$ALIAS" \
--message="patchwork-core#${{ github.event.number }} @ ${{ github.event.pull_request.head.sha }}" \
--json)
echo "$out" | jq .
echo "url=$(echo "$out" | jq -er .deploy_url)" >> "$GITHUB_OUTPUT"

- name: verify the deploy serves this build
env:
SHELL_URL: ${{ steps.deploy.outputs.url }}
run: |
set -o pipefail
html=$(curl -sfL "$SHELL_URL")
for asset in $(echo "$html" | grep -oE '/assets/[A-Za-z0-9_.-]+\.js' | sort -u); do
curl -sfL --output /dev/null "$SHELL_URL$asset" || {
echo "::error::$SHELL_URL$asset is missing"
exit 1
}
done

- uses: actions/github-script@v7
env:
SHELL_URL: ${{ steps.deploy.outputs.url }}
with:
script: |
const marker = process.env.MARKER
const sha = context.payload.pull_request.head.sha.slice(0, 7)
const body = [
marker,
`## ✅ Patchwork glance ready`,
``,
`## ${process.env.SHELL_URL}`,
``,
`A peek at \`${process.env.SHELL_REPO}\`, built with the @inkandswitch/patchwork- packages as of this PR at \`${sha}\`.`,
].join("\n")
const {owner, repo} = context.repo
const issue_number = context.issue.number
const comments = await github.rest.issues.listComments({owner, repo, issue_number})
const existing = comments.data.find(c => c.body.startsWith(marker))
if (existing) {
await github.rest.issues.updateComment({owner, repo, comment_id: existing.id, body})
} else {
await github.rest.issues.createComment({owner, repo, issue_number, body})
}

teardown:
if: github.event.action == 'closed'
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4

- run: .github/scripts/netlify-purge-alias.sh

- uses: actions/github-script@v7
with:
script: |
const marker = process.env.MARKER
const {owner, repo} = context.repo
const issue_number = context.issue.number
const comments = await github.rest.issues.listComments({owner, repo, issue_number})
const existing = comments.data.find(c => c.body.startsWith(marker))
if (existing) {
await github.rest.issues.updateComment({
owner, repo, comment_id: existing.id,
body: `${marker}\n### 🪡 Patchwork \n\nTorn down.`,
})
}
8 changes: 8 additions & 0 deletions .github/workflows/trigger.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
- name: update preview.patchwork.inkandswitch.com
env:
GH_TOKEN: ${{ secrets.PATCHWORK_PREVIEW_DISPATCH_TOKEN }}
run: |
gh api --method POST \
repos/inkandswitch/patchwork.inkandswitch.com/dispatches \
-f event_type=core-main \
-F "client_payload[sha]=$GITHUB_SHA"
Loading