Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec4ff5dfa2 |
@@ -2,11 +2,3 @@ node_modules/
|
|||||||
dist/
|
dist/
|
||||||
.astro/
|
.astro/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
# Content lives in the ctao/content repo (build overlays it; scripts/link-content.sh locally)
|
|
||||||
src/content/news/
|
|
||||||
src/content/pages/
|
|
||||||
public/uploads/
|
|
||||||
|
|
||||||
# Session scratch
|
|
||||||
.tmp/
|
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<!-- Screenshot probe: headless Chrome clamps --window-size below ~500px, so a
|
||||||
|
fixed-width iframe gives a true narrow layout viewport. Copied into dist/
|
||||||
|
by shoot.sh; not part of the site. -->
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>probe</title>
|
||||||
|
<style>html,body{margin:0;background:#b0b4c0}iframe{border:0;display:block}</style>
|
||||||
|
<script>
|
||||||
|
var q = new URLSearchParams(location.search);
|
||||||
|
var f = document.createElement('iframe');
|
||||||
|
f.src = q.get('p') || '/';
|
||||||
|
f.style.width = (q.get('w') || 390) + 'px';
|
||||||
|
f.style.height = (q.get('h') || 2000) + 'px';
|
||||||
|
f.onload = function () {
|
||||||
|
var s = +(q.get('s') || 0);
|
||||||
|
// behavior:instant — the site sets scroll-behavior:smooth, which would
|
||||||
|
// leave the screenshot mid-animation
|
||||||
|
if (s) f.contentWindow.scrollTo({ top: s, behavior: 'instant' });
|
||||||
|
};
|
||||||
|
document.addEventListener('DOMContentLoaded', function () { document.body.appendChild(f); });
|
||||||
|
</script>
|
||||||
|
After Width: | Height: | Size: 675 KiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 609 KiB |
|
After Width: | Height: | Size: 346 KiB |
|
After Width: | Height: | Size: 750 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 317 KiB |
|
After Width: | Height: | Size: 400 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 594 KiB |
|
After Width: | Height: | Size: 669 KiB |
|
After Width: | Height: | Size: 317 KiB |
|
After Width: | Height: | Size: 453 KiB |
|
After Width: | Height: | Size: 495 KiB |
|
After Width: | Height: | Size: 595 KiB |
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 420 KiB |
|
After Width: | Height: | Size: 669 KiB |
|
After Width: | Height: | Size: 600 KiB |
|
After Width: | Height: | Size: 535 KiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 97 KiB |
|
After Width: | Height: | Size: 120 KiB |
|
After Width: | Height: | Size: 135 KiB |
|
After Width: | Height: | Size: 221 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 91 KiB |
|
After Width: | Height: | Size: 424 KiB |
|
After Width: | Height: | Size: 2.5 MiB |
|
After Width: | Height: | Size: 675 KiB |
|
After Width: | Height: | Size: 2.5 MiB |
|
After Width: | Height: | Size: 2.5 MiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 609 KiB |
|
After Width: | Height: | Size: 346 KiB |
|
After Width: | Height: | Size: 750 KiB |
|
After Width: | Height: | Size: 119 KiB |
|
After Width: | Height: | Size: 135 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 396 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 117 KiB |
|
After Width: | Height: | Size: 424 KiB |
|
After Width: | Height: | Size: 535 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 361 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 109 KiB |
|
After Width: | Height: | Size: 110 KiB |
|
After Width: | Height: | Size: 133 KiB |
|
After Width: | Height: | Size: 141 KiB |
|
After Width: | Height: | Size: 221 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 49 KiB |
@@ -0,0 +1,45 @@
|
|||||||
|
#!/bin/zsh
|
||||||
|
# Screenshot helper for the CSS polish pass. $1 = output dir (before|after)
|
||||||
|
# Narrow widths go through dist/__probe.html (a fixed-width iframe): headless
|
||||||
|
# Chrome clamps --window-size below ~500px, so 360/390/480 shots would silently
|
||||||
|
# be 500px-wide renders cropped to the window.
|
||||||
|
set -e
|
||||||
|
CH="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
||||||
|
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||||
|
OUT="$(cd "$(dirname "$0")" && pwd)/$1"
|
||||||
|
mkdir -p "$OUT"
|
||||||
|
cp "$(dirname "$0")/__probe.html" "$ROOT/dist/__probe.html"
|
||||||
|
shot() { # name url w h
|
||||||
|
"$CH" --headless=new --disable-gpu --hide-scrollbars --window-size=$3,$4 \
|
||||||
|
--screenshot="$OUT/$1.png" "$2" >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
wide() { shot "$1" "http://localhost:4330$2" $3 $4; } # name path w h
|
||||||
|
narrow() { # name path w h (h = iframe height)
|
||||||
|
shot "$1" "http://localhost:4330/__probe.html?w=$3&h=$4&p=$2" $(($3 + 20)) $(($4 + 10))
|
||||||
|
}
|
||||||
|
W='/news/ctao-and-cabildo-of-la-palma-organise-women-of-ctao-2026'
|
||||||
|
wide home "/" 1440 2600
|
||||||
|
narrow home-390 "/" 390 2200
|
||||||
|
wide news "/news" 1440 2600
|
||||||
|
narrow news-390 "/news" 390 2400
|
||||||
|
wide news-768 "/news" 768 2200
|
||||||
|
wide art6 "/news/6" 1440 2600
|
||||||
|
narrow art6-390 "/news/6" 390 2600
|
||||||
|
wide artwomen "$W" 1440 2600
|
||||||
|
narrow artwomen-390 "$W" 390 2600
|
||||||
|
wide artwomen-1280 "$W" 1280 2000
|
||||||
|
wide search "/search?q=lst" 1440 2000
|
||||||
|
narrow search-390 "/search?q=lst" 390 1800
|
||||||
|
wide support "/support" 1440 1800
|
||||||
|
narrow support-360 "/support" 360 1500
|
||||||
|
wide privacy "/pages/privacy" 1440 1400
|
||||||
|
narrow privacy-390 "/pages/privacy" 390 1500
|
||||||
|
wide nf "/404-nonexistent" 1440 1200
|
||||||
|
narrow nf-390 "/404-nonexistent" 390 1300
|
||||||
|
wide proposals "/proposals" 1440 1600
|
||||||
|
narrow proposals-390 "/proposals" 390 1700
|
||||||
|
wide dashboard "/dashboard" 1440 1600
|
||||||
|
narrow dashboard-480 "/dashboard" 480 1900
|
||||||
|
wide login "/login" 1440 1200
|
||||||
|
narrow login-390 "/login" 390 1100
|
||||||
|
echo "shots in $OUT"
|
||||||
@@ -65,10 +65,6 @@ missing). Everything else runs inside containers.
|
|||||||
10. **[R]** Verify: `curl -s -o /dev/null -w '%{http_code}' http://localhost:8080/`
|
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).
|
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)
|
## CMS sign-in (Sveltia ↔ Gitea OAuth)
|
||||||
|
|
||||||
- Gitea OAuth2 app (PKCE, `confidential_client=false`, no secret) with
|
- Gitea OAuth2 app (PKCE, `confidential_client=false`, no secret) with
|
||||||
|
|||||||
@@ -43,22 +43,18 @@ head_of() {
|
|||||||
code_sha=$(head_of "$CODE_REPO")
|
code_sha=$(head_of "$CODE_REPO")
|
||||||
content_sha=$(head_of "$CONTENT_REPO")
|
content_sha=$(head_of "$CONTENT_REPO")
|
||||||
# Gitea down/unreachable is a transient, not a unit failure — exit 0 quietly
|
# Gitea down/unreachable is a transient, not a unit failure — exit 0 quietly
|
||||||
# instead of painting the journal red every 10 s. Name the repo: a 404 here
|
# instead of painting the journal red every 10 s.
|
||||||
# also means "repo/branch missing or renamed", not just "Gitea down".
|
for sha in "$code_sha" "$content_sha"; do
|
||||||
[[ "$code_sha" =~ ^[0-9a-f]{40}$ ]] || { echo "poll failed for $CODE_REPO@$BRANCH (gitea down, or repo/branch missing) — skipping"; exit 0; }
|
if [[ ! "$sha" =~ ^[0-9a-f]{40}$ ]]; then
|
||||||
[[ "$content_sha" =~ ^[0-9a-f]{40}$ ]] || { echo "poll failed for $CONTENT_REPO@$BRANCH (gitea down, or repo/branch missing) — skipping"; exit 0; }
|
echo "poll failed (gitea unreachable?) — skipping this tick"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
release="${code_sha:0:12}-${content_sha:0:12}" # code+content pin the release
|
release="${code_sha:0:12}-${content_sha:0:12}" # code+content pin the release
|
||||||
# Skip only if this pair is both recorded AND still present in releases/
|
# Skip only if this pair is both recorded AND still present in releases/
|
||||||
# (a deleted release dir must trigger a rebuild, not an eternal skip).
|
# (a deleted release dir must trigger a rebuild, not an eternal skip).
|
||||||
[[ "$release" == "$(cat "$BASE/state/last-built" 2>/dev/null)" \
|
[[ "$release" == "$(cat "$BASE/state/last-built" 2>/dev/null)" \
|
||||||
&& -d "$BASE/releases/$release" ]] && exit 0
|
&& -d "$BASE/releases/$release" ]] && exit 0
|
||||||
# A release that already failed is not retried until either repo moves —
|
|
||||||
# otherwise one bad commit (e.g. broken frontmatter) turns into a full
|
|
||||||
# rebuild every 10 s on a shared VM. The failure is loud once, then quiet.
|
|
||||||
if [[ "$release" == "$(cat "$BASE/state/last-failed" 2>/dev/null)" ]]; then
|
|
||||||
echo "skipping $release — build failed before; push a fix to retry"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "building code=$code_sha content=$content_sha"
|
echo "building code=$code_sha content=$content_sha"
|
||||||
t0=$(date +%s)
|
t0=$(date +%s)
|
||||||
@@ -110,8 +106,7 @@ podman run --rm --network="$BUILD_NETNS" --memory=1g \
|
|||||||
npm run build
|
npm run build
|
||||||
rm -rf "../releases/$RELEASE"
|
rm -rf "../releases/$RELEASE"
|
||||||
cp -a dist "../releases/$RELEASE"
|
cp -a dist "../releases/$RELEASE"
|
||||||
' || { echo "$release" > "$BASE/state/last-failed"; echo "BUILD FAILED for $release (see above) — will not retry until a new commit"; exit 1; }
|
'
|
||||||
rm -f "$BASE/state/last-failed"
|
|
||||||
|
|
||||||
# --- 3. Atomic publish: symlink flip via rename(2) — no half-published moment.
|
# --- 3. Atomic publish: symlink flip via rename(2) — no half-published moment.
|
||||||
# mv -T is GNU (the target host is Rocky); when testing on macOS put a
|
# mv -T is GNU (the target host is Rocky); when testing on macOS put a
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
# activate a service that is still running.
|
# activate a service that is still running.
|
||||||
|
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=CTAO portal demo — rebuild if the code or content repo has new commits
|
Description=CTAO portal demo — rebuild if the content repo has new commits
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
# OnBootSec covers reboot. Enable: systemctl --user enable --now ctao-portal-build.timer
|
# OnBootSec covers reboot. Enable: systemctl --user enable --now ctao-portal-build.timer
|
||||||
|
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=CTAO portal demo — poll the code and content repos every 10 s
|
Description=CTAO portal demo — poll the content repo every 10 s
|
||||||
|
|
||||||
[Timer]
|
[Timer]
|
||||||
OnActiveSec=5
|
OnActiveSec=5
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Sveltia CMS config — backend: Gitea behind the Cyfronet ingress (HTTPS
|
# Sveltia CMS config — backend: Gitea behind the Cyfronet ingress (HTTPS
|
||||||
# vhost). Editors log in via Gitea OAuth (PKCE, no client secret); edits are
|
# vhost). Editors log in via Gitea OAuth (PKCE, no client secret); edits are
|
||||||
# committed to the ctao/content repo. Gitea's ROOT_URL and CORS are pinned to
|
# committed to the ctao/portal repo. Gitea's ROOT_URL and CORS are pinned to
|
||||||
# these origins in deploy/ctao-demo-gitea.container.
|
# these origins in deploy/ctao-demo-gitea.container.
|
||||||
backend:
|
backend:
|
||||||
name: gitea
|
name: gitea
|
||||||
|
|||||||
@@ -112,7 +112,7 @@
|
|||||||
const unit = opts.unit || 'result';
|
const unit = opts.unit || 'result';
|
||||||
status.textContent = hits.length
|
status.textContent = hits.length
|
||||||
? hits.length + ' ' + unit + (hits.length === 1 ? '' : 's') + ' for “' + input.value.trim() + '”'
|
? hits.length + ' ' + unit + (hits.length === 1 ? '' : 's') + ' for “' + input.value.trim() + '”'
|
||||||
: 'No results for “' + input.value.trim() + '”. Try a shorter or different term.';
|
: 'No results for “' + input.value.trim() + '” — try a shorter or different term.';
|
||||||
// Off-DOM build + atomic swap (no transient page-height collapse)
|
// Off-DOM build + atomic swap (no transient page-height collapse)
|
||||||
const frag = document.createDocumentFragment();
|
const frag = document.createDocumentFragment();
|
||||||
for (const p of hits) {
|
for (const p of hits) {
|
||||||
|
|||||||
|
After Width: | Height: | Size: 162 KiB |
|
After Width: | Height: | Size: 592 KiB |
|
After Width: | Height: | Size: 217 KiB |
|
After Width: | Height: | Size: 290 KiB |
|
After Width: | Height: | Size: 476 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 180 KiB |
|
After Width: | Height: | Size: 230 KiB |
|
After Width: | Height: | Size: 193 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 188 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 64 KiB |