Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/.codebase-info/directory-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,5 @@ proof9/ # Claude Code plugin: hooks/, tests/, README.md
tests/ # core/, cli/, adapters/, integration/, lifecycle/, e2e/, ci/, …
docs/ # GOLDEN_PATH, CLI_WIREFRAME, PRODUCT_ROADMAP, VISION, CLI guide, images/
scripts/ # lifecycle runner, quickstart-cleanroom/, ci/ helpers, verify scripts
site/ # Static codeframe.sh page (no build, no JS); make-og-image.sh rebuilds the card
site/ # Static codeframe.sh page (no build, no JS); tools/site/make-og-image.sh rebuilds the card, tools/site/deploy.sh publishes it
```
2 changes: 1 addition & 1 deletion site/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<link rel="icon" href="favicon.ico" sizes="any">
<link rel="apple-touch-icon" href="apple-touch-icon.png">

<!-- Social card. Crawlers need absolute URLs; regenerate the image with make-og-image.sh. -->
<!-- Social card. Crawlers need absolute URLs; regenerate the image with tools/site/make-og-image.sh. -->
<meta property="og:type" content="website">
<meta property="og:url" content="https://codeframe.sh/">
<meta property="og:title" content="CodeFRAME: regression memory for coding agents">
Expand Down
51 changes: 51 additions & 0 deletions tools/site/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Site tooling

Scripts for the static `codeframe.sh` page in `site/`. They live here rather than
in `site/` so that everything under `site/` is servable, and a deploy can copy the
directory as-is with no exclude list to remember.

| Script | What it does |
|---|---|
| `make-og-image.sh` | Regenerates `site/og-image.png` (the 1200x630 social card) and `site/proof9-loop-still.png`. Needs ffmpeg and DejaVu fonts. |
| `make-banner.py` | Builds the outlined banner SVG. Usage is in its docstring. |
| `deploy.sh` | Publishes `site/` at `origin/main` to the web root. Runs on the server. |

## Deploying

The trigger is manual. After a change to `site/` merges:

```bash
tailscale ssh root@frank-prod-server deploy-codeframe-site
```

One-time setup on the server. It needs `git`, `rsync` and the `www-data` user, which
nginx creates; the script refuses to start without that user rather than failing
part-way through.

```bash
git clone --depth 1 https://github.com/frankbria/codeframe /opt/codeframe-site
install -m 755 /opt/codeframe-site/tools/site/deploy.sh /usr/local/bin/deploy-codeframe-site
```

Re-run the `install` line whenever `deploy.sh` itself changes; the installed copy
does not update itself.

What a run does:

- pulls the public repository, so no credential is needed and none is stored;
- keeps one rolling backup of the web root it replaces, at `/root/codeframe.sh-predeploy.tgz`.
It is skipped on a first deploy, when the root already holds the same commit, and after
an interrupted run, so a re-run after a bad or broken deploy cannot overwrite the last
good copy;
- mirrors `site/` into the web root with `rsync --delete`, so an asset removed or
renamed in the repository disappears on the next deploy;
- writes the deployed commit to `.deployed-sha` in the web root.

Paths can be overridden with `REPO_URL`, `SRC`, `DOCROOT` and `BACKUP`, which is how
the script is tested in a throwaway container.

Check what is live:

```bash
tailscale ssh root@frank-prod-server cat /var/www/codeframe.sh/.deployed-sha
```
62 changes: 62 additions & 0 deletions tools/site/deploy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#!/usr/bin/env bash
# Publish site/ at origin/main to the codeframe.sh web root.
#
# Runs ON the web server, not on a workstation. Installed as
# /usr/local/bin/deploy-codeframe-site; see tools/site/README.md.
#
# The repository is public, so this pulls with no credential. rsync --delete
# makes the web root an exact copy of site/: an asset removed or renamed in the
# repository disappears here on the next run.
set -euo pipefail

REPO_URL=${REPO_URL:-https://github.com/frankbria/codeframe}
SRC=${SRC:-/opt/codeframe-site}
DOCROOT=${DOCROOT:-/var/www/codeframe.sh}
BACKUP=${BACKUP:-/root/codeframe.sh-predeploy.tgz}
# Present only while a sync is under way. If it survives, a previous run died
# mid-sync and the web root is a torn mix of two commits.
INCOMPLETE=${INCOMPLETE:-${BACKUP%.tgz}.incomplete}

# A clone killed part-way leaves a .git with no commit, and every later fetch
# would fail. Remove only that: a checkout with a commit is kept even if fetch
# fails, and a directory that is not a git checkout is never deleted.
if [ -d "$SRC/.git" ] && ! git -C "$SRC" rev-parse -q --verify HEAD >/dev/null 2>&1; then
rm -rf "$SRC"
fi
if [ ! -d "$SRC/.git" ]; then
git clone -q --depth 1 "$REPO_URL" "$SRC"
fi
Comment thread
frankbria marked this conversation as resolved.
git -C "$SRC" fetch -q --depth 1 origin main
git -C "$SRC" reset -q --hard origin/main
HEAD_SHA=$(git -C "$SRC" rev-parse HEAD)

# Fail before touching anything if the web server user is missing, rather than
# part-way through with a live site and no record of the deploy.
id -u www-data >/dev/null 2>&1 || { echo "deploy: user www-data does not exist; create it or install nginx first" >&2; exit 1; }

# One rolling backup of what is being replaced, taken only when the web root is
# a complete deploy of a different commit. Skipped on a first deploy (no root),
# on a re-run at the same commit, and after an interrupted run (torn root): in
# each of those cases a backup would replace the last good copy with something
# that is not one. The sync below still runs, so a re-run repairs the root.
if [ -d "$DOCROOT" ] && [ ! -e "$INCOMPLETE" ] \
&& [ "$(cat "$DOCROOT/.deployed-sha" 2>/dev/null)" != "$HEAD_SHA" ]; then
# Written aside and renamed: a tar that fails part-way (disk full) must not
# truncate the backup it was meant to replace.
tar -C "$(dirname "$DOCROOT")" -czf "$BACKUP.tmp" "$(basename "$DOCROOT")"
mv "$BACKUP.tmp" "$BACKUP"
fi

# The marker goes down before the first change to the web root and comes up
# after the last, so no interruption can leave a changed root without it.
touch "$INCOMPLETE"
mkdir -p "$DOCROOT"
# .well-known/ is left alone in case certificate renewal ever uses this root.
rsync -a --delete --exclude '/.well-known/' --exclude '/.deployed-sha' "$SRC/site/" "$DOCROOT/"
chown -R www-data:www-data "$DOCROOT"
# Success is recorded only after the last step that can fail, so a failure
# leaves the previous sha and the marker in place for the next run to repair.
echo "$HEAD_SHA" > "$DOCROOT/.deployed-sha"
rm -f "$INCOMPLETE"

echo "deployed ${HEAD_SHA:0:12} to $DOCROOT"
2 changes: 1 addition & 1 deletion site/make-banner.py → tools/site/make-banner.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
Needs fontTools (and cairosvg for the PNG), plus IBM Plex Sans and IBM Plex Mono
as .woff, e.g. from `npm pack @ibm/plex-sans @ibm/plex-mono` (fonts/complete/woff/).

python3 site/make-banner.py site/codeframe-lockup-reverse.svg \
python3 tools/site/make-banner.py site/codeframe-lockup-reverse.svg \
<plex-sans>/fonts/complete/woff <plex-mono>/fonts/complete/woff \
codeframe_github_header.svg
python3 -c "import cairosvg; cairosvg.svg2png(url='codeframe_github_header.svg', \
Expand Down
3 changes: 2 additions & 1 deletion site/make-og-image.sh → tools/site/make-og-image.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
# (the reduced-motion still: the demo GIF's last frame). Needs ffmpeg + DejaVu fonts.
# Edit the COPY strings below when marketing copy changes, then re-run.
set -euo pipefail
cd "$(dirname "$0")"
# Lives in tools/site/; its inputs and outputs are in site/.
cd "$(dirname "$0")/../../site"

# COPY: card strings
LINE1="Your coding agent can't quietly"
Expand Down
Loading