-
Notifications
You must be signed in to change notification settings - Fork 0
Merge latest upstream starpod changes #28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
80e49fe
8f6ad3e
043d48a
a86c3e7
3466f4c
27a9038
950b634
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,10 @@ | |
| * ATPROTO_APP_PASSWORD - An app password from bsky.app/settings/app-passwords | ||
| * STANDARD_SITE_PUBLICATION_RKEY - The publication record key | ||
| * | ||
| * Optional environment variables: | ||
| * STANDARD_SITE_URL - Your podcast site URL (e.g., https://whiskey.fm), | ||
| * used to poll for rebuilt episode pages when WAIT_FOR_SITE is set | ||
| * | ||
| * Usage: | ||
| * pnpm publish:episodes # publish new episodes only | ||
| * pnpm publish:episodes:backfill # publish all episodes (backfill) | ||
|
|
@@ -29,6 +33,38 @@ import starpodConfig from '../starpod.config'; | |
| import { dasherize } from '../src/utils/dasherize'; | ||
|
|
||
| const BACKFILL = process.argv.includes('--backfill'); | ||
| // Set by the GitHub workflow when it has just triggered a site rebuild: | ||
| // published documents link to episode pages, so wait for the rebuilt site to | ||
| // serve them before publishing. | ||
| const WAIT_FOR_SITE = process.env.WAIT_FOR_SITE === 'true'; | ||
|
|
||
| const PAGE_WAIT_TIMEOUT_MS = 10 * 60 * 1000; | ||
| const PAGE_WAIT_INTERVAL_MS = 15 * 1000; | ||
|
|
||
| async function waitForPage(url: string) { | ||
| const deadline = Date.now() + PAGE_WAIT_TIMEOUT_MS; | ||
|
|
||
| for (;;) { | ||
| try { | ||
| const response = await fetch(url, { method: 'HEAD' }); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- scripts/publish-episodes.ts ---'
cat -n scripts/publish-episodes.ts | sed -n '1,130p'
printf '%s\n' '--- .github/workflows/publish-episodes.yml ---'
cat -n .github/workflows/publish-episodes.yml | sed -n '1,125p'
printf '%s\n' '--- timeout-related references ---'
rg -n -i --glob '!node_modules' --glob '!dist' --glob '!build' \
'waitForPage|AbortController|AbortSignal|timeout|--connect-timeout|--max-time|curl|fetch\(' \
scripts .github package.json tsconfig*.json 2>/dev/null || trueRepository: shipshapecode/www-starpod Length of output: 12470 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- remaining publish script ---'
cat -n scripts/publish-episodes.ts | sed -n '125,215p'
printf '%s\n' '--- remaining workflow ---'
cat -n .github/workflows/publish-episodes.yml | sed -n '120,190p'
printf '%s\n' '--- package metadata ---'
cat -n package.json | sed -n '1,180p'
printf '%s\n' '--- workflow timeout settings ---'
rg -n 'timeout-minutes|timeout|curl|fetch|parseFeed|waitForPage' .github/workflows/publish-episodes.yml scripts/publish-episodes.ts package.jsonRepository: shipshapecode/www-starpod Length of output: 9192 🏁 Script executed: #!/bin/bash
set -eu
python3 - <<'PY'
from pathlib import Path
ts = Path("scripts/publish-episodes.ts").read_text()
workflow = Path(".github/workflows/publish-episodes.yml").read_text()
fetch_line = next(line.strip() for line in ts.splitlines() if "fetch(url" in line)
deadline_check = "if (Date.now() >= deadline)" in ts
fetch_pos = ts.index("await fetch(url")
check_pos = ts.index("if (Date.now() >= deadline)")
workflow_feed = next(line.strip() for line in workflow.splitlines() if 'FEED_BODY=$(curl' in line)
workflow_webhook = next(line.strip() for line in workflow.splitlines() if 'curl -sf -X POST' in line)
print({
"page_fetch": fetch_line,
"page_fetch_has_signal": "signal" in fetch_line,
"deadline_check_exists": deadline_check,
"deadline_check_after_fetch": fetch_pos < check_pos,
"rss_curl": workflow_feed,
"rss_curl_has_connect_timeout": "--connect-timeout" in workflow_feed,
"rss_curl_has_max_time": "--max-time" in workflow_feed,
"webhook_curl": workflow_webhook,
"webhook_curl_has_connect_timeout": "--connect-timeout" in workflow_webhook,
"webhook_curl_has_max_time": "--max-time" in workflow_webhook,
})
PY
printf '%s\n' '--- curl timeout options ---'
curl --help all 2>/dev/null | rg -n -- '--connect-timeout|--max-time' || trueRepository: shipshapecode/www-starpod Length of output: 785 🌐 Web query:
💡 Result: In Node.js 22, the global Citations:
🌐 Web query:
💡 Result: By default, curl has no timeout for the overall transfer operation, and it will wait indefinitely for a response once a connection is established [1][2][3]. Regarding the connection phase specifically, curl does have a built-in default timeout if one is not explicitly configured. Connection Timeout The --connect-timeout option (or CURLOPT_CONNECTTIMEOUT in libcurl) limits the time allowed for the connection phase, which includes DNS resolution and TCP/TLS/QUIC handshakes [1][4][5]. If this is not set, curl uses a built-in default of 300 seconds (5 minutes) [6][7][8][9]. In the libcurl source code, this is defined as DEFAULT_CONNECT_TIMEOUT (300,000 milliseconds) [10][9]. Maximum Time (Total Transfer) The --max-time option (or CURLOPT_TIMEOUT in libcurl) limits the maximum time the entire operation is allowed to take [11][3]. By default, this is set to 0, which means there is no timeout, and the operation will not be aborted based on elapsed time regardless of how long the transfer takes [2][3]. Summary Table Option | Default Value | Description --- | --- | --- --connect-timeout | 300 seconds (5 minutes) | Maximum time allowed for the connection phase [6][10][4]. --max-time | 0 (No timeout) | Maximum time allowed for the entire operation [11][3]. Citations:
Enforce request deadlines for the three external requests. The
📍 Affects 2 files
🤖 Prompt for AI Agents |
||
| if (response.ok) { | ||
| return; | ||
| } | ||
| console.log(` ⏳ ${url} → ${response.status}, waiting for rebuild...`); | ||
| } catch (err) { | ||
| console.log(` ⏳ ${url} unreachable, waiting for rebuild... (${err})`); | ||
| } | ||
|
|
||
| if (Date.now() >= deadline) { | ||
| throw new Error( | ||
| `Timed out waiting for ${url} — site rebuild may have failed. ` + | ||
| 'Episodes will be retried on the next scheduled run.' | ||
| ); | ||
| } | ||
|
|
||
| await new Promise((resolve) => setTimeout(resolve, PAGE_WAIT_INTERVAL_MS)); | ||
| } | ||
| } | ||
|
|
||
| const FeedSchema = object({ | ||
| items: array( | ||
|
|
@@ -56,6 +92,9 @@ async function main() { | |
| const identifier = process.env.ATPROTO_HANDLE; | ||
| const password = process.env.ATPROTO_APP_PASSWORD; | ||
| const publicationRkey = process.env.STANDARD_SITE_PUBLICATION_RKEY; | ||
| // Only needed to poll the rebuilt site before publishing (WAIT_FOR_SITE); | ||
| // the published documents themselves reference the publication AT-URI. | ||
| const siteUrl = process.env.STANDARD_SITE_URL; | ||
|
|
||
| if (!identifier || !password || !publicationRkey) { | ||
| console.log( | ||
|
|
@@ -121,8 +160,32 @@ async function main() { | |
| cursor = response.data.cursor; | ||
| } while (cursor); | ||
|
|
||
| // Vercel deploys are atomic, so new pages go live together — but wait on | ||
| // every unpublished page so publishing can't outrun the rebuild regardless | ||
| // of feed ordering or previously failed publishes. | ||
| if (WAIT_FOR_SITE && !BACKFILL && !siteUrl) { | ||
| console.log( | ||
| '⚠️ WAIT_FOR_SITE is set but STANDARD_SITE_URL is not — skipping the site rebuild wait.' | ||
| ); | ||
| } | ||
| if (WAIT_FOR_SITE && !BACKFILL && siteUrl) { | ||
| const pendingUrls = episodes | ||
| .filter((episode) => !existingPaths.has(`/${dasherize(episode.title)}`)) | ||
| .map( | ||
| (episode) => `${siteUrl.replace(/\/$/, '')}/${dasherize(episode.title)}` | ||
| ); | ||
| if (pendingUrls.length > 0) { | ||
| console.log( | ||
| `⏳ Waiting for rebuilt site to serve ${pendingUrls.length} new episode page(s)...` | ||
| ); | ||
| await Promise.all(pendingUrls.map((url) => waitForPage(url))); | ||
| console.log('✅ Site rebuild is live.'); | ||
| } | ||
| } | ||
|
|
||
| let published = 0; | ||
| let skipped = 0; | ||
| let failed = 0; | ||
|
|
||
| for (const episode of episodes) { | ||
| const slug = dasherize(episode.title); | ||
|
|
@@ -164,13 +227,20 @@ async function main() { | |
| skipped++; | ||
| } else { | ||
| console.error(` ❌ ${episode.title}: ${message}`); | ||
| failed++; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| console.log( | ||
| `\n🎉 Done! Published: ${published}, Skipped: ${skipped}, Total episodes: ${episodes.length}` | ||
| `\n🎉 Done! Published: ${published}, Skipped: ${skipped}, Failed: ${failed}, Total episodes: ${episodes.length}` | ||
| ); | ||
|
|
||
| // Exit nonzero so the workflow doesn't record the feed hash and the failed | ||
| // episodes are retried on the next scheduled run. | ||
| if (failed > 0) { | ||
| throw new Error(`${failed} episode(s) failed to publish.`); | ||
| } | ||
| } | ||
|
|
||
| main().catch((err) => { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Document the
REBUILD_WEBHOOKsecret.These lines require
REBUILD_WEBHOOKfor rebuild-and-wait behavior. The GitHub Actions secret list does not document it. A user who follows the setup steps will publish without the page-readiness wait.Add
REBUILD_WEBHOOKas an optional secret and state that the workflow skips rebuild polling when it is absent.🤖 Prompt for AI Agents