git.lucas.co / gitsite
the static git browser that builds this site
git clone https://git.lucas.co/gitsite.git

readme.txt (5.2K)

  1 git.lucas.co
  2 ============
  3 
  4 Static git repo browser for Cloudflare Pages. Generates HTML pages
  5 (log, commits with diffs, file browser, refs) for the repos listed in
  6 repos.conf, plus dumb-http clone support so
  7 `git clone https://git.lucas.co/<name>.git` works from static hosting.
  8 
  9 Files
 10 -----
 11 repos.conf   which repos to publish: name|path|description|mode
 12              path is the GitHub URL the repo is mirrored from -- what
 13              this reads HEAD from, not a work tree
 14              mode "clone" = browse + clonable, "browse" = browse only
 15 generate.py  the HTML generator
 16 build.sh     syncs mirrors, runs generate.py, adds clone files
 17 deploy.sh    pushes the built site to Cloudflare Pages
 18 autodeploy.sh
 19              build + deploy, but only when a listed repo's HEAD moved
 20 gitsite.service, gitsite.timer
 21              systemd user units that run autodeploy.sh hourly — see
 22              "Automatic publishing" below; nothing here installs them
 23 post-commit-push
 24              git post-commit hook: pushes the committed branch to origin,
 25              so committing is publishing -- see "Publishing model" below
 26 pre-commit-scan
 27              git pre-commit hook: refuses a commit whose staged changes
 28              look like a secret (gitleaks), since the push hook would
 29              publish it seconds later; warns and lets the commit through
 30              where gitleaks is not installed
 31 install-hooks.sh
 32              symlinks both hooks into every repo in repos.conf
 33 style.css    matches lucas.co (black, white, blue links, Circe)
 34 
 35 Workflow
 36 --------
 37 ./build.sh    # output goes to ~/.cache/gitsite/out (not in Dropbox)
 38 ./deploy.sh   # needs one-time `npx wrangler login`
 39 
 40 Preview locally:
 41 python3 -m http.server -d ~/.cache/gitsite/out 8931
 42 
 43 Automatic publishing
 44 --------------------
 45 gitsite.timer runs autodeploy.sh hourly (randomized up to 5m; Persistent
 46 so a run missed while the machine was off catches up), and autodeploy.sh
 47 rebuilds + deploys only when a repo in repos.conf has a new HEAD. If it
 48 stops running, nothing reaches git.lucas.co and pushed commits look
 49 published but aren't.
 50 
 51 The units are NOT installed by any script here. On a new machine:
 52 
 53     cp gitsite.service gitsite.timer ~/.config/systemd/user/
 54     systemctl --user daemon-reload
 55     systemctl --user enable --now gitsite.timer
 56 
 57 Check:  systemctl --user list-timers gitsite.timer
 58 Log:    ~/.cache/gitsite/autodeploy.log
 59 
 60 Publishing model
 61 ----------------
 62 Committing is NOT publishing; pushing is. Each repo's origin is
 63 https://github.com/lsgalante/<name>.git, GitHub is canonical, and this
 64 site is a mirror of it: repos.conf lists the GitHub URLs, build.sh keeps a
 65 bare mirror of each in ~/.cache/gitsite/mirrors, and autodeploy.sh asks
 66 GitHub (git ls-remote) whether any HEAD moved. So:
 67 
 68     git commit ...              # local only
 69     git push                    # the publishing step
 70                                 # gitsite.timer then deploys it
 71 
 72 Every repo in repos.conf has post-commit-push installed as its post-commit
 73 hook (install-hooks.sh does that; re-run it after cloning a repo fresh or
 74 adding one), so in practice the push happens on commit and a plain commit
 75 IS the publishing step. The hook never forces: a rejected push leaves the
 76 commit local and says so, and you resolve it with git pull / git push as
 77 usual. It does nothing on a detached HEAD or mid-rebase.
 78 
 79 Because of that, pre-commit-scan runs first: with gitleaks installed it
 80 scans the staged diff and refuses a commit that looks like it carries a
 81 credential, so nothing of the kind reaches GitHub. Without gitleaks the
 82 commit goes through with a warning -- install it (pacman -S gitleaks) to
 83 have the check actually run.
 84 
 85 The 27 crates pinned to https://git.lucas.co/<name>.git?rev=... keep
 86 working because the dumb-http clone dirs are built from the same mirrors;
 87 a rev exists here as long as it is reachable on GitHub.
 88 
 89 One-time Cloudflare setup
 90 -------------------------
 91 1. npx wrangler login
 92 2. ./deploy.sh (creates the "git-lucas-co" Pages project on first run)
 93 3. Cloudflare dashboard -> Workers & Pages -> git-lucas-co ->
 94    Custom domains -> add git.lucas.co
 95 
 96 Credentials
 97 -----------
 98 deploy.sh reads ~/.config/gitsite/env (mode 600, NOT versioned), the same
 99 shape restic-backup.sh uses:
100 
101     CLOUDFLARE_API_TOKEN=...
102     CLOUDFLARE_ACCOUNT_ID=...
103 
104 A token is needed for unattended runs: wrangler refuses OAuth in
105 non-interactive environments, so the gitsite.timer unit cannot use a login
106 session. The account id used to be inline in deploy.sh, which stopped being
107 reasonable once this repo started publishing itself to git.lucas.co. It is
108 still in this repo's git history; it is an identifier rather than a
109 credential, and does nothing without the token, which has never been in the
110 repo.
111 
112 The older ~/.config/gitsite-cf-token (token only, no account id) is still
113 honoured as a fallback for a machine that has not been migrated.
114 
115 Notes
116 -----
117 - website repo is mode "browse" because its history is 452MB of media;
118   flip to "clone" in repos.conf if you want it clonable anyway.
119 - Mirrors live in ~/.cache/gitsite/mirrors, repacked into <=20MB packs
120   (Cloudflare rejects files over 25MB). Delete a mirror dir to force a
121   fresh re-mirror.
122 - To add a repo: create it on GitHub (gh repo create lsgalante/<name>
123   --public), push, add a line to repos.conf, run install-hooks.sh, then
124   build.sh + deploy.sh.