skip posts marked draft: true
Browse filesLets unpublished posts live in the content repo alongside published ones.
Also documents using a token URL for a private content repo.
- Dockerfile +2 -3
- README.md +13 -3
- scripts/fetch-news.mjs +6 -2
- src/content/news/index.ts +7 -0
- src/content/news/types.ts +4 -1
Dockerfile
CHANGED
|
@@ -30,9 +30,8 @@ COPY . .
|
|
| 30 |
# `*.md` files into the image; leave it unset and the /news section simply
|
| 31 |
# does not appear. The clone itself is done by the `prebuild` npm script, so
|
| 32 |
# that Nixpacks builds (which never read this Dockerfile) behave identically.
|
| 33 |
-
#
|
| 34 |
-
#
|
| 35 |
-
# carrying an access token would leak to anyone who can pull the image.
|
| 36 |
ARG NEWS_CONTENT_REPO=
|
| 37 |
ARG NEWS_CONTENT_REF=main
|
| 38 |
ENV NEWS_CONTENT_REPO=$NEWS_CONTENT_REPO
|
|
|
|
| 30 |
# `*.md` files into the image; leave it unset and the /news section simply
|
| 31 |
# does not appear. The clone itself is done by the `prebuild` npm script, so
|
| 32 |
# that Nixpacks builds (which never read this Dockerfile) behave identically.
|
| 33 |
+
# For a private repo the URL carries a token — see the README for what that
|
| 34 |
+
# means for the built image.
|
|
|
|
| 35 |
ARG NEWS_CONTENT_REPO=
|
| 36 |
ARG NEWS_CONTENT_REF=main
|
| 37 |
ENV NEWS_CONTENT_REPO=$NEWS_CONTENT_REPO
|
README.md
CHANGED
|
@@ -38,9 +38,12 @@ which never reads the `Dockerfile`, and `prebuild` is the one hook both build pa
|
|
| 38 |
On Coolify these must be marked as **build variables**, not just runtime env vars, or the
|
| 39 |
build will not see them.
|
| 40 |
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
|
|
|
|
|
|
|
|
|
| 44 |
|
| 45 |
A clone failure fails the build rather than silently shipping a site with no
|
| 46 |
announcements.
|
|
@@ -54,6 +57,7 @@ title: Ranking switches from Elo to Arena Score
|
|
| 54 |
date: 2026-07-23
|
| 55 |
summary: Optional one-line teaser shown in the listing.
|
| 56 |
author: Optional byline
|
|
|
|
| 57 |
---
|
| 58 |
|
| 59 |
Body in Markdown. Headings, lists, links, tables, code and block quotes are styled.
|
|
@@ -62,6 +66,12 @@ Body in Markdown. Headings, lists, links, tables, code and block quotes are styl
|
|
| 62 |
`title` and `date` are required; a post missing either is skipped with a build warning.
|
| 63 |
Posts are listed newest first by `date`.
|
| 64 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
Publishing a post is a push to the content repo followed by a redeploy of the frontend —
|
| 66 |
the Markdown is baked into the image, so a rebuild is what makes a new post appear.
|
| 67 |
|
|
|
|
| 38 |
On Coolify these must be marked as **build variables**, not just runtime env vars, or the
|
| 39 |
build will not see them.
|
| 40 |
|
| 41 |
+
For a **private** content repo, put an access token in the URL
|
| 42 |
+
(`https://<token>@github.com/<org>/<repo>.git`). Note that the token is then recorded in
|
| 43 |
+
the built image. TS-Arena's own deployment accepts that: the images never leave internal
|
| 44 |
+
hosts, and anyone with Docker access there can read the content repo anyway. If you run
|
| 45 |
+
an instance where that is not true, use a public repo or a read-only token you are willing
|
| 46 |
+
to rotate.
|
| 47 |
|
| 48 |
A clone failure fails the build rather than silently shipping a site with no
|
| 49 |
announcements.
|
|
|
|
| 57 |
date: 2026-07-23
|
| 58 |
summary: Optional one-line teaser shown in the listing.
|
| 59 |
author: Optional byline
|
| 60 |
+
draft: true
|
| 61 |
---
|
| 62 |
|
| 63 |
Body in Markdown. Headings, lists, links, tables, code and block quotes are styled.
|
|
|
|
| 66 |
`title` and `date` are required; a post missing either is skipped with a build warning.
|
| 67 |
Posts are listed newest first by `date`.
|
| 68 |
|
| 69 |
+
`draft: true` keeps a post out of the build entirely — no page, no route, no listing entry
|
| 70 |
+
— so unfinished posts can sit in the content repo next to the published ones. Drop the
|
| 71 |
+
line (or set it to `false`) to publish on the next redeploy. The draft's Markdown file is
|
| 72 |
+
still copied into the image, it is just never rendered or routed; treat drafts as hidden
|
| 73 |
+
from site visitors, not as a secret from anyone who can read the image.
|
| 74 |
+
|
| 75 |
Publishing a post is a push to the content repo followed by a redeploy of the frontend —
|
| 76 |
the Markdown is baked into the image, so a rebuild is what makes a new post appear.
|
| 77 |
|
scripts/fetch-news.mjs
CHANGED
|
@@ -10,6 +10,10 @@
|
|
| 10 |
* NEWS_CONTENT_REPO clone URL of the content repo (unset = no news)
|
| 11 |
* NEWS_CONTENT_REF branch or tag to clone (default: main)
|
| 12 |
*
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
* Deliberately lives in npm-script land rather than in the Dockerfile: the
|
| 14 |
* Coolify apps build with Nixpacks, which never reads the Dockerfile. Hooking
|
| 15 |
* into `prebuild` is the one place both build paths go through.
|
|
@@ -30,8 +34,6 @@ if (!repo) {
|
|
| 30 |
process.exit(0);
|
| 31 |
}
|
| 32 |
|
| 33 |
-
// Never print `repo` itself: if someone ignores the advice and uses a URL with
|
| 34 |
-
// an embedded token, it must not end up in the build log.
|
| 35 |
console.log(`[news] cloning news content (ref: ${ref})`);
|
| 36 |
|
| 37 |
// Clone into a staging directory and only swap it in once it succeeds, so a
|
|
@@ -44,6 +46,8 @@ fs.mkdirSync(path.dirname(TARGET), { recursive: true });
|
|
| 44 |
try {
|
| 45 |
execFileSync('git', ['clone', '--depth', '1', '--branch', ref, repo, staging], {
|
| 46 |
stdio: ['ignore', 'inherit', 'pipe'],
|
|
|
|
|
|
|
| 47 |
});
|
| 48 |
} catch (err) {
|
| 49 |
// Fail the build loudly. A silent fallback to "no news" would ship a site
|
|
|
|
| 10 |
* NEWS_CONTENT_REPO clone URL of the content repo (unset = no news)
|
| 11 |
* NEWS_CONTENT_REF branch or tag to clone (default: main)
|
| 12 |
*
|
| 13 |
+
* For a private repo, put the access token in the URL. TS-Arena's own deploys
|
| 14 |
+
* are internal-only and anyone with Docker access on those hosts can read the
|
| 15 |
+
* content repo anyway, so the token ending up in the image is acceptable there.
|
| 16 |
+
*
|
| 17 |
* Deliberately lives in npm-script land rather than in the Dockerfile: the
|
| 18 |
* Coolify apps build with Nixpacks, which never reads the Dockerfile. Hooking
|
| 19 |
* into `prebuild` is the one place both build paths go through.
|
|
|
|
| 34 |
process.exit(0);
|
| 35 |
}
|
| 36 |
|
|
|
|
|
|
|
| 37 |
console.log(`[news] cloning news content (ref: ${ref})`);
|
| 38 |
|
| 39 |
// Clone into a staging directory and only swap it in once it succeeds, so a
|
|
|
|
| 46 |
try {
|
| 47 |
execFileSync('git', ['clone', '--depth', '1', '--branch', ref, repo, staging], {
|
| 48 |
stdio: ['ignore', 'inherit', 'pipe'],
|
| 49 |
+
// Bad credentials should fail the build, not hang it on a username prompt.
|
| 50 |
+
env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
|
| 51 |
});
|
| 52 |
} catch (err) {
|
| 53 |
// Fail the build loudly. A silent fallback to "no news" would ship a site
|
src/content/news/index.ts
CHANGED
|
@@ -57,6 +57,13 @@ function readPosts(): NewsPost[] {
|
|
| 57 |
continue;
|
| 58 |
}
|
| 59 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
posts.push({
|
| 61 |
slug,
|
| 62 |
title,
|
|
|
|
| 57 |
continue;
|
| 58 |
}
|
| 59 |
|
| 60 |
+
// Drafts stay in the content repo but never reach the site. Not rendered
|
| 61 |
+
// and not routed, so there is no unlisted URL to stumble onto either.
|
| 62 |
+
if (data.draft === true) {
|
| 63 |
+
console.log(`[news] skipping draft content/news/${fileName}`);
|
| 64 |
+
continue;
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
posts.push({
|
| 68 |
slug,
|
| 69 |
title,
|
src/content/news/types.ts
CHANGED
|
@@ -7,13 +7,16 @@
|
|
| 7 |
* date: 2026-07-23
|
| 8 |
* summary: One-line teaser shown in the listing.
|
| 9 |
* author: TS-Arena Team
|
|
|
|
| 10 |
* ---
|
| 11 |
*
|
| 12 |
* Body in Markdown…
|
| 13 |
* ```
|
| 14 |
*
|
| 15 |
* `title` and `date` are required; a post missing either is skipped with a
|
| 16 |
-
* build-time warning rather than breaking the build.
|
|
|
|
|
|
|
| 17 |
*/
|
| 18 |
export interface NewsPostMetadata {
|
| 19 |
/** URL slug — derived from the file name (sans `.md`). */
|
|
|
|
| 7 |
* date: 2026-07-23
|
| 8 |
* summary: One-line teaser shown in the listing.
|
| 9 |
* author: TS-Arena Team
|
| 10 |
+
* draft: true
|
| 11 |
* ---
|
| 12 |
*
|
| 13 |
* Body in Markdown…
|
| 14 |
* ```
|
| 15 |
*
|
| 16 |
* `title` and `date` are required; a post missing either is skipped with a
|
| 17 |
+
* build-time warning rather than breaking the build. `draft: true` keeps a
|
| 18 |
+
* post out of the build entirely, so unpublished work can live in the content
|
| 19 |
+
* repo alongside what is live.
|
| 20 |
*/
|
| 21 |
export interface NewsPostMetadata {
|
| 22 |
/** URL slug — derived from the file name (sans `.md`). */
|