fuzzyworld

fuzzyworld-tools / files / bin / fw

fw

Blame: the commit that last changed each line · file · history

623239b7 2026-10-26 Pavel Safronov1#!/bin/bash
f9ca0752 2026-10-27 Pavel Safronov2# fw (laptop side).
3# fw deploy [--ref <branch>] [--here] [--dry-run] build and publish this repo's app (see lib/deploy.mjs)
b75ceb4e 2026-10-27 Pavel Safronov4# fw repoint [DIR] [--https] [--dry-run] move working copies' origin from GitLab to DreamHost
623239b7 2026-10-26 Pavel Safronov5# fw list | new <name> [--public] [--description TEXT] | public <name> | private <name>
03124f41 2026-10-31 Pavel Safronov6# fw usage [--all|--now] Claude Code token use per session (see lib/usage.py)
0e603cf7 2026-10-31 Pavel Safronov7# fw blog list|build|preview|deploy <config> static blog from Markdown (see server/blog.py);
9bb22e48 2026-11-03 Pavel Safronov8# build takes --out DIR and --no-htaccess;
9# deploy takes --dry-run and --yes (else it asks)
0e603cf7 2026-10-31 Pavel Safronov10# fw blog import-wp <config> --wp URL [--dry-run] [--force] [--refresh] [--probe-links]
11# import the WordPress blog (see lib/wp_import.py)
12# fw blog check <config> [--out DIR] compare the built site with the cached WordPress text
9bb22e48 2026-11-03 Pavel Safronov13# fw blog watch <config> [--once] [--dry-run] mirror the source into git and auto-deploy edits (see server/blog_watch.py);
14# fw blog watch install|uninstall <config> [--print] the launchd agent that runs it every 60 s (ONE machine only)
a5a72f55 2026-10-27 Pavel Safronov15# fw describe <name> TEXT | publish <name> | publish-all | ci <name> [branch] | ci-list | ci-watch
85702b14 2026-11-04 Pavel Safronov16# fw admin [--port N] [--no-open] [--code-dir DIR] local web page for reports and blog comments (see lib/admin.py);
17# runs the server-side fw commands over ssh (FW_HOST); FW_CODE_DIR = app folders
e9fdc5db 2026-11-01 Pavel Safronov18# fw comments list|show|approve|reject|remove|untrust|publish|export|ingest|imap-check (blog comments)
55e5fc3e 2026-11-04 Pavel Safronov19# fw issues <repo> public|reviewed|private | list|show|approve|hide|close|reopen|publish|publish-all|ingest|imap-check (bug-report issues)
1758548c 2026-10-27 Pavel Safronov20# (run on DreamHost over ssh)
623239b7 2026-10-26 Pavel Safronov21set -euo pipefail
22HOST=${FW_HOST:-dh}
f9ca0752 2026-10-27 Pavel Safronov23# Follow the symlink npm creates in node_modules/.bin to find this package.
24HERE=$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")/.." && pwd)
623239b7 2026-10-26 Pavel Safronov25
0e603cf7 2026-10-31 Pavel Safronov26# fw blog: runs on the laptop. Markdown and Pygments live in ~/fw/pylib (installed here when missing).
27blog() {
28 local sub=${1:-} config=${2:-} tmp=${TMPDIR:-/tmp}
29 tmp=${tmp%/}
30 local pylib=${FW_PYLIB:-$HOME/fw/pylib} blogpy="$HERE/server/blog.py"
31 case "$sub" in
9bb22e48 2026-11-03 Pavel Safronov32 list|build|preview|deploy|import-wp|check|watch) ;;
33 *) echo "usage: fw blog list|build|preview|deploy|import-wp|check|watch <config> [options]" >&2; exit 2 ;;
0e603cf7 2026-10-31 Pavel Safronov34 esac
35 [ -n "$config" ] || { echo "fw blog $sub: needs the config file (e.g. ~/Blog/fw-blog.json)" >&2; exit 2; }
36 shift 2
37 if ! PYTHONPATH="$pylib" python3 -c 'import markdown, pygments' 2>/dev/null; then
9bb22e48 2026-11-03 Pavel Safronov38 # The launchd agent (watch --once) must never run pip: only an interactive command installs the libraries.
39 if [ "$sub" = watch ] && [[ " $* " == *" --once "* ]]; then
40 echo "fw: Markdown and Pygments are missing from $pylib; run any other fw blog command once to install them" >&2
41 exit 1
42 fi
0e603cf7 2026-10-31 Pavel Safronov43 echo "fw: installing Markdown and Pygments into $pylib" >&2
44 python3 -m pip install --quiet --target "$pylib" Markdown==3.7 Pygments==2.21.0
45 fi
46 case "$sub" in
47 import-wp)
48 # Reads the WordPress REST API (cached in <source>/wordpress/_api/); --dry-run writes only that cache and the report.
49 exec python3 "$HERE/lib/wp_import.py" import "$config" "$@" ;;
50 check)
51 exec python3 "$HERE/lib/wp_import.py" check "$config" "$@" ;;
52 list)
53 python3 "$blogpy" list "$config" ;;
54 build)
55 local out=$tmp/fw-blog-build extra=()
56 while [ $# -gt 0 ]; do
57 case "$1" in
58 --out) out=${2:?--out needs a folder}; shift 2 ;;
59 --no-htaccess) extra+=(--no-htaccess); shift ;;
60 *) echo "fw blog build: unknown option $1 (use --out DIR, --no-htaccess)" >&2; exit 2 ;;
61 esac
62 done
63 python3 "$blogpy" build "$config" --out "$out" ${extra[@]+"${extra[@]}"} ;;
64 preview)
65 # No .htaccess here (python's server ignores it, so redirects are not tested); the feed is at /feed/index.xml.
66 local out=$tmp/fw-blog-preview port=${1:-8000}
67 python3 "$blogpy" build "$config" --out "$out" --no-htaccess
68 echo "preview: http://127.0.0.1:$port/ (ctrl-c to stop)"
69 exec python3 -m http.server --bind 127.0.0.1 --directory "$out" "$port" ;;
9bb22e48 2026-11-03 Pavel Safronov70 watch)
71 # One tick (--once, what the launchd agent runs) or a loop; or install/uninstall the agent: `watch install <config>`.
72 exec python3 "$HERE/server/blog_watch.py" "$config" "$@" ;;
0e603cf7 2026-10-31 Pavel Safronov73 deploy)
9bb22e48 2026-11-03 Pavel Safronov74 # Uploads ONLY a freshly built, audited output folder: never the source folder. Asks first unless --yes.
75 local args=() a
0e603cf7 2026-10-31 Pavel Safronov76 for a in "$@"; do
9bb22e48 2026-11-03 Pavel Safronov77 case "$a" in --dry-run|--yes) args+=("$a") ;; *) echo "fw blog deploy: unknown option $a" >&2; exit 2 ;; esac
0e603cf7 2026-10-31 Pavel Safronov78 done
9bb22e48 2026-11-03 Pavel Safronov79 exec python3 "$blogpy" deploy "$config" --out "$tmp/fw-blog-deploy" ${args[@]+"${args[@]}"} ;;
0e603cf7 2026-10-31 Pavel Safronov80 esac
81}
82
623239b7 2026-10-26 Pavel Safronov83case "${1:-help}" in
0e603cf7 2026-10-31 Pavel Safronov84 blog)
85 shift; blog "$@" ;;
f9ca0752 2026-10-27 Pavel Safronov86 deploy)
87 shift; exec node "$HERE/lib/deploy.mjs" "$@" ;;
03124f41 2026-10-31 Pavel Safronov88 usage)
89 shift; exec python3 "$HERE/lib/usage.py" "$@" ;;
85702b14 2026-11-04 Pavel Safronov90 admin)
91 shift; exec python3 "$HERE/lib/admin.py" "$@" ;;
b75ceb4e 2026-10-27 Pavel Safronov92 repoint)
93 shift; exec bash "$HERE/lib/repoint.sh" "$@" ;;
55e5fc3e 2026-11-04 Pavel Safronov94 list|new|public|private|describe|publish|publish-all|ci|ci-list|ci-watch|comments|issues|help|-h|--help)
623239b7 2026-10-26 Pavel Safronov95 # printf %q keeps arguments (e.g. a description with spaces) intact through ssh.
96 exec ssh "$HOST" "~/bin/fw $(printf '%q ' "$@")" ;;
97 *)
98 echo "fw: unknown command '$1' (try: fw help)" >&2; exit 2 ;;
99esac