Files

9.3 KiB
Raw Permalink Blame History

Deploy: CTAO portal demo on strapi-experimental.cyfronet

Everything runs rootless as user strapi. Footprint on the machine: ONE directory (~/ctao-portal-demo/) + 4 unit files in ~/.config/. Uninstall restores the machine exactly (see bottom). No secrets in any file.

Two Gitea repos by design: ctao/portal (code, developed by the team) and ctao/content (Markdown + uploads, committed by the CMS). The build overlays content onto code; a push to EITHER republishes the site.

~/ctao-portal-demo/
├── gitea-data/    # Gitea state (repos, SQLite, accounts)  ← the ONLY thing worth backing up
├── gitea-config/  # Gitea app.ini (generated from env)
├── repo/          # clone of the code repo (created by first build)
├── content/       # clone of the content repo (created by first build)
├── releases/      # <code>-<content>/ dirs + `current` symlink (what nginx serves)
├── state/         # last-built release id
├── npm-cache/     # npm cache for the build container (created by build.sh)
├── bin/build.sh   # copied from deploy/ (source of truth stays in the code repo)
└── config/nginx.conf

Host prerequisites: podman, curl, jq (build.sh checks and says which is missing). Everything else runs inside containers.

Port What Exposed as
3000 Gitea https://astro-git.isl-dev.grid.cyfronet.pl (ingress vhost)
8080 portal (nginx, static) https://astro.isl-dev.grid.cyfronet.pl (ingress vhost)

Publish the simplified portal from main

  1. Check that ctao/content:main contains the four Example files in pages/ (listed in src/data/examples.json) and the uploads they reference. They are already committed there (a3452d7, "demo lorem ipsum pages"). News and other page files can remain in the content repository; this portal loads only the four Example files.
  2. Push the content branch to Gitea as ctao/content:main, and the code branch as ctao/portal:main. These are separate pushes; Bitbucket is not the deployment remote.
  3. Copy the updated build.sh to ~/ctao-portal-demo/bin/build.sh and the updated ctao-portal-build.service to ~/.config/systemd/user/. Reload systemd with systemctl --user daemon-reload. The service explicitly sets CODE_BRANCH=main and CONTENT_BRANCH=main; the script defaults agree.
  4. Start the build service or let its timer run. It overlays pages/ and uploads/, clears the previous news overlay, and runs npm run check before the atomic release switch. Missing Example pages fail the build.
  5. Verify the four /example/…/ pages and empty /news/. Sign in at /admin/, save an Example title, and check that a commit appears on ctao/content:main and the rebuilt page reflects it.

The OAuth client and public portal origin stay the same. Sveltia's backend.branch in public/admin/config.yml must always match CONTENT_BRANCH. Branches are selected independently; a single BRANCH environment variable is no longer used. For a separate preview hostname, update the site origin, OAuth redirect, and Gitea CORS together.

To undo the simplification in shared code history, use git revert -m 1 <simplification-merge-commit> on main and publish the resulting commit. The previous portal code is also preserved at 0772462. For an immediate deployment rollback, restore the previous release and worker script/service configuration. Content has its own history: reverting a code commit does not revert CMS edits. Keep the old content files available if the previous portal may be restored.

Install (each step reviewed before running; [W] = writes to the machine)

  1. [W] loginctl enable-linger $USER. Without lingering every user unit dies at logout and nothing starts after a reboot. Verify: loginctl show-user $USER -p Linger → Linger=yes.
  2. [W] mkdir -p ~/ctao-portal-demo/{gitea-data,gitea-config,releases,state,bin,config} ~/.config/containers/systemd ~/.config/systemd/user
  3. [W] Copy files from this dir (scp):
    • ctao-demo-gitea.container, ctao-demo-web.container → ~/.config/containers/systemd/
    • ctao-portal-build.service, ctao-portal-build.timer → ~/.config/systemd/user/
    • build.sh → ~/ctao-portal-demo/bin/ (chmod +x)
    • nginx.conf → ~/ctao-portal-demo/config/
    • Containerfile.build → anywhere (needed once, for the next step)
  4. [W] Pull the images at the digests pinned in the unit files (one-time, needs internet) and build the build image: podman pull docker.io/gitea/gitea@sha256:<digest from ctao-demo-gitea.container> podman pull docker.io/library/nginx@sha256:<digest from ctao-demo-web.container> podman build -t localhost/ctao-portal-build:1 -f Containerfile.build . (Upgrading later = pick new digests deliberately, update the pins in the .container files / Containerfile.build, re-pull, re-build.)
  5. [W] systemctl --user daemon-reload && systemctl --user start ctao-demo-gitea
  6. [W] Create the Gitea admin. Run it interactively in a terminal so the password never lands in a file or shell history: podman exec -it ctao-demo-gitea gitea admin user create --admin --username <you> --email <you@…> --random-password
  7. [W] In the Gitea UI: create org ctao with repos portal and content (both public read). The content main branch needs the four Example pages described above. Then, from your workstation, push both over the vhost with a repo-scoped token: git push https://<user>:<token>@astro-git.isl-dev.grid.cyfronet.pl/ctao/portal.git main git push https://<user>:<token>@astro-git.isl-dev.grid.cyfronet.pl/ctao/content.git main
  8. [W] systemctl --user enable --now ctao-portal-build.timer. The first run clones + npm ci + builds (minutes); later runs are seconds. Wait until journalctl --user -u ctao-portal-build -n 5 shows published <sha> (starting nginx earlier just serves 404s until the first build lands).
  9. [W] systemctl --user start ctao-demo-web
  10. [R] Verify: curl -s -o /dev/null -w '%{http_code}' http://localhost:8080/ and journalctl --user -u ctao-portal-build -n 20 (shows measured build times).

Editor accounts need WRITE access to ctao/content (Gitea → repo → Collaborators, or a team); public read alone lets them sign in but every save fails.

CMS sign-in (Sveltia ↔ Gitea OAuth)

  • Gitea OAuth2 app (PKCE, confidential_client=false, no secret) with redirect https://astro.isl-dev.grid.cyfronet.pl/admin/; its client id is the app_id in public/admin/config.yml.
  • CORS is pinned in ctao-demo-gitea.container: ALLOW_DOMAIN takes the FULL portal origin with scheme (a bare hostname silently disables CORS in Gitea 1.27; there is no SCHEME key) and HEADERS must include Authorization or authenticated API calls from the browser fail.
  • OAuth requires a secure context: the vhosts must stay HTTPS.
  • Changing origins later = edit the quadlet env + the OAuth app's redirect URI + config.yml, then systemctl --user daemon-reload && systemctl --user restart ctao-demo-gitea.

Uninstall (leaves only podman's own storage metadata)

systemctl --user disable --now ctao-portal-build.timer
systemctl --user stop ctao-demo-web ctao-demo-gitea
rm ~/.config/containers/systemd/ctao-demo-*.container \
   ~/.config/systemd/user/ctao-portal-build.{service,timer}
systemctl --user daemon-reload
podman rmi localhost/ctao-portal-build:1 docker.io/gitea/gitea:1.27-rootless \
  docker.io/library/nginx:stable-alpine docker.io/library/node:24-alpine
rm -rf ~/ctao-portal-demo
loginctl disable-linger $USER   # only if nothing else of yours should survive logout

Notes

  • A release that fails to build is not retried until either repo gets a new commit (state/last-failed guards against a 10 s rebuild loop). To force a retry of the SAME release (e.g. after fixing bin/build.sh itself): rm ~/ctao-portal-demo/state/last-failed.

  • Only pushes to the machine's Gitea auto-deploy (code and content repos alike). Changes to deploy/* need a manual re-copy: build.sh → bin/, nginx.conf → config/ + systemctl --user restart ctao-demo-web, unit files → ~/.config/… + systemctl --user daemon-reload (+ restart). This is deliberate: the build pipeline must not execute host-side code straight from the content repo.

  • Publish latency = poll (≤10 s) + build (measured: astro build 5–7 s on the 2 vCPU VM, whole build.sh 16–21 s; every build's time lands in the journal).

  • Internet needed only for: image pulls (install) and npm ci when the lockfile changes. Routine rebuilds are fully offline.

  • Memory caps (MemoryHigh) keep us polite next to Outline + Strapi; the build container is capped at 1 GB via podman run --memory. Watch the first npm ci + build in the journal; if it OOMs inside its cgroup (contained, just retries), raise the cap.

  • Build isolation: build containers join the Gitea container's network namespace (--network=container:ctao-demo-gitea): repo/npm code sees Gitea on localhost:3000 but cannot reach host loopback services. (Rootless netavark bridges don't work here: no ip_tables kernel module, no sudo.)

  • Secrets inventory: Gitea admin password (typed interactively, lives only in Gitea's DB) and repo-scoped push tokens (managed in Gitea); that's the complete list. Build/poll/serve use none.