CTAO Science Portal — static site + git-based CMS
Public portal for CTAO news, built as a fully static site. Editors get a browser WYSIWYG editor (Sveltia CMS) that commits Markdown to a Gitea repo; a build job turns commits into static HTML. No application or database runs on the public path.
Live demo: https://astro.isl-dev.grid.cyfronet.pl · Gitea on the demo machine: https://astro-git.isl-dev.grid.cyfronet.pl
Repository layout & deployment
Code and content are SEPARATE repositories, so editorial commits never mix with development:
| Repo | Holds | Source of truth | Who commits |
|---|---|---|---|
ctao-portal (this one, Bitbucket) |
Templates, CSS, deploy | development | the team |
ctao/content (Gitea on the demo machine) |
news/, pages/, uploads/ |
editorial | Sveltia CMS / editors |
The demo machine polls ITS Gitea (ctao/portal mirror + ctao/content) and
republishes on a push to either — so a push to Bitbucket does NOT deploy;
deploying code = pushing it to the machine's Gitea (a deliberate step). The
build overlays content onto code (deploy/build.sh); the content paths are
gitignored here.
Stack
- Astro (static output). Pages are
.astrotemplates (plain HTML with a JS frontmatter block). No React/Vue/Svelte components, no client-side framework. - Plain CSS — everything lives in
src/styles/global.cssas design tokens (custom properties) + rules. No Tailwind, no preprocessor. The design system (tokens, type scale, spacing, motion policy) is documented inDESIGN.md— read it before touching styles. - Sveltia CMS — a single prebuilt JS bundle, vendored in
public/vendor/sveltia-cms.jsand loaded on/admin/(src/pages/admin/index.astro), configured bypublic/admin/config.yml. It runs entirely in the editor's browser and talks to the Gitea API (OAuth PKCE). There is no CMS server. To update it:curl -sL https://unpkg.com/@sveltia/cms/dist/sveltia-cms.js -o public/vendor/sveltia-cms.js - Runtime dependencies: none beyond Astro. This is deliberate — keep it that way.
Develop
Prerequisites: Node 22.19 or newer (the machine builds on Node 24), and
network access to the demo machine's Gitea (Cyfronet network / VPN) for the
content clone below. Without content the site still builds, but with no
articles and npm run check reports the footer's page links as broken.
npm ci
git clone https://astro-git.isl-dev.grid.cyfronet.pl/ctao/content.git ../ctao-content
./scripts/link-content.sh # copies news/pages/uploads into the dev tree
npm run dev # http://localhost:4321
npm run build # static output in dist/
npm run check # deterministic gate: astro check (types) + build + internal-link check
tsconfig.json excludes public/ on purpose: the vendored CMS bundle there is
2 MB of minified JS and would make astro check run out of memory.
Layout
| Path | What |
|---|---|
src/pages/ |
Routes (.astro templates), incl. news/, search, RSS/sitemap |
src/layouts/Base.astro |
HTML shell: head, header/nav, footer |
src/content/news/*.md |
Articles (from the content repo — gitignored here; schema in src/content.config.ts) |
src/styles/global.css |
All CSS: tokens + components + prose |
DESIGN.md |
Design handoff: where the tokens live, brand basics, practical notes |
public/admin/ |
CMS config (config.yml) + editor preview styles (preview.css) |
public/uploads/ |
Editor-uploaded media (from the content repo — gitignored here) |
deploy/ |
Runbook + container/systemd units for the demo machine — see deploy/README.md |
Editing content
Editors use /admin/ (link in the footer) and sign in with a Gitea account.
Saving commits to main; the machine polls and republishes automatically
(seconds). Full history/rollback = git history in Gitea. Articles with
draft: true are excluded from the build.
Note for styling work: public/admin/preview.css mirrors the .prose rules
from global.css so the editor preview matches the site 1:1 — keep them in
sync (both files carry a KEEP IN SYNC comment).
Upgrading
Versions are pinned exactly; an upgrade is a deliberate change, never a side
effect of npm install. Astro ships security fixes only for the current and
one previous major, so plan one upgrade per major rather than skipping several.
npx @astrojs/upgrade # moves astro and official integrations together
npm run check # types + build + links must pass
npm run preview # verify the real dist/ output, not the dev server
No experimental flags in astro.config.mjs: they can change in minor releases.