This repository has been archived on 2026-06-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
rta-handbook/scripts/setup-section0-docs.sh
2026-06-08 10:06:07 -07:00

361 lines
11 KiB
Bash
Executable File

#!/usr/bin/env sh
set -eu
REPO_URL="${REPO_URL:-http://100.64.0.1:30087/section0/rta-handbook.git}"
TARGET_DIR="${TARGET_DIR:-$HOME/Developer/Section0/rta-handbook}"
COMMAND_DIR="${COMMAND_DIR:-$HOME/.local/bin}"
COMMAND_NAME="${COMMAND_NAME:-section0-docs}"
COMMAND_PATH="$COMMAND_DIR/$COMMAND_NAME"
SERVER_URL="${SECTION0_SERVER_URL:-https://ops.virgil.info/md-to-section0-api}"
step() {
printf "\n==> %s\n" "$*"
}
if ! command -v git >/dev/null 2>&1; then
echo "git is required but was not found on PATH" >&2
exit 1
fi
step "Preparing local Markdown workspace"
if [ -d "$TARGET_DIR/.git" ]; then
echo "Repo already exists: $TARGET_DIR"
git -C "$TARGET_DIR" remote set-url origin "$REPO_URL"
git -C "$TARGET_DIR" fetch origin
git -C "$TARGET_DIR" checkout main
git -C "$TARGET_DIR" pull --ff-only
else
mkdir -p "$(dirname "$TARGET_DIR")"
git clone "$REPO_URL" "$TARGET_DIR"
fi
git -C "$TARGET_DIR" config pull.ff only
step "Installing helper command"
mkdir -p "$COMMAND_DIR"
cat > "$COMMAND_PATH" <<EOF
#!/usr/bin/env sh
set -eu
REPO_DIR="$TARGET_DIR"
SERVER_URL="$SERVER_URL"
SESSION_DIR="\${SECTION0_SESSION_DIR:-\$HOME/.config/section0-docs}"
SESSION_PATH="\$SESSION_DIR/session.json"
usage() {
cat <<USAGE
section0-docs - helper for Section 0 shared Markdown docs
Commands:
auth login open Authentik and install Git credentials
auth finish finish login from a printed device code
auth status show current saved login
configure set Git author name/email for this repo
doctor check clone, author, remote, and read access
open print the repo path
pull pull latest Markdown with --ff-only
status show Git status
commit commit all current changes with MESSAGE
push push current branch
push-test prove write access with a temporary remote branch
help show this help
Examples:
section0-docs auth login
section0-docs auth finish DEVICE_CODE
section0-docs auth status
section0-docs doctor
section0-docs configure
section0-docs pull
section0-docs status
section0-docs push-test
MESSAGE="Update concept notes" section0-docs commit
section0-docs push
AFFiNE is read-only for these docs. Make lasting changes in Markdown; the
AFFiNE copies are refreshed from Git.
USAGE
}
need_python() {
command -v python3 >/dev/null 2>&1 || {
echo "python3 is required for Authentik login JSON handling" >&2
exit 1
}
}
json_get() {
need_python
python3 -c 'import json,sys; data=json.load(sys.stdin); cur=data
for part in sys.argv[1].split("."):
cur = cur.get(part, "") if isinstance(cur, dict) else ""
print(cur if cur is not None else "")' "\$1"
}
open_url() {
if command -v open >/dev/null 2>&1; then
open "\$1" >/dev/null 2>&1 || true
elif command -v xdg-open >/dev/null 2>&1; then
xdg-open "\$1" >/dev/null 2>&1 || true
fi
}
credential_host() {
need_python
python3 -c 'from urllib.parse import urlparse; import sys
url=urlparse(sys.argv[1])
print(url.netloc)' "\$1"
}
http_json() {
curl --connect-timeout 5 --max-time 15 -fsS "\$@"
}
install_git_credential() {
remote="\$1"
username="\$2"
password="\$3"
host="\$(credential_host "\$remote")"
protocol="\$(printf "%s" "\$remote" | sed -n "s#^\\([^:/]*\\)://.*#\\1#p")"
[ -n "\$protocol" ] || protocol="http"
mkdir -p "\$SESSION_DIR"
credential_path="\$SESSION_DIR/git-credentials"
touch "\$credential_path"
chmod 600 "\$credential_path"
git -C "\$REPO_DIR" config --local --replace-all credential.helper ""
git -C "\$REPO_DIR" config --local --add credential.helper "store --file=\$credential_path"
printf "protocol=%s\nhost=%s\nusername=%s\npassword=%s\n\n" "\$protocol" "\$host" "\$username" "\$password" \
| git -C "\$REPO_DIR" credential approve
chmod 600 "\$credential_path"
}
auth_login() {
need_python
mkdir -p "\$SESSION_DIR"
device_json="\$(curl -fsS -X POST "\$SERVER_URL/device")"
code="\$(printf "%s" "\$device_json" | json_get code)"
auth_url="\$(printf "%s" "\$device_json" | json_get authUrl)"
[ -n "\$code" ] && [ -n "\$auth_url" ] || {
echo "login server did not return a device code" >&2
exit 1
}
echo "Opening Authentik login:"
echo " \$auth_url"
echo "Device code:"
echo " \$code"
echo "Token check:"
echo " \$SERVER_URL/device/\$code/token"
open_url "\$auth_url"
if [ "\${SECTION0_AUTH_POLL:-}" != "1" ]; then
echo "When the browser says login succeeded, return here and press Enter."
printf "Press Enter to finish login: "
IFS= read -r _section0_continue
auth_finish "\$code"
return
fi
echo "Waiting for login..."
token_json=""
i=0
while [ "\$i" -lt 90 ]; do
token_json="\$(http_json "\$SERVER_URL/device/\$code/token" 2>/dev/null || true)"
access_token="\$(printf "%s" "\${token_json:-{}}" | json_get accessToken 2>/dev/null || true)"
[ -n "\$access_token" ] && break
status="\$(printf "%s" "\${token_json:-{}}" | json_get status 2>/dev/null || true)"
if [ \$((i % 5)) -eq 0 ]; then
printf "\n still waiting%s\n" "\${status:+ (\$status)}"
else
printf "."
fi
i=\$((i + 1))
sleep 2
done
printf "\n"
[ -n "\${access_token:-}" ] || {
echo "timed out waiting for Authentik login" >&2
echo "If the browser says login succeeded, run:" >&2
echo " section0-docs auth finish \$code" >&2
exit 1
}
complete_login "\$token_json" "\$access_token"
}
auth_finish() {
need_python
mkdir -p "\$SESSION_DIR"
code="\${1:-}"
[ -n "\$code" ] || {
echo "usage: section0-docs auth finish DEVICE_CODE" >&2
exit 1
}
token_json="\$(http_json "\$SERVER_URL/device/\$code/token")"
access_token="\$(printf "%s" "\$token_json" | json_get accessToken 2>/dev/null || true)"
[ -n "\$access_token" ] || {
echo "No completed login token for code: \$code" >&2
printf "%s\n" "\$token_json" >&2
exit 1
}
complete_login "\$token_json" "\$access_token"
}
complete_login() {
token_json="\$1"
access_token="\$2"
echo "Login accepted; requesting Section 0 Git access..."
access_json="\$(http_json -X POST -H "Authorization: Bearer \$access_token" "\$SERVER_URL/section0/git/access")"
ok="\$(printf "%s" "\$access_json" | json_get ok)"
[ "\$ok" = "True" ] || [ "\$ok" = "true" ] || {
echo "Git access broker did not return credentials:" >&2
printf "%s\n" "\$access_json" >&2
exit 1
}
remote="\$(printf "%s" "\$access_json" | json_get remote)"
git_username="\$(printf "%s" "\$access_json" | json_get git.username)"
git_password="\$(printf "%s" "\$access_json" | json_get git.password)"
author_name="\$(printf "%s" "\$access_json" | json_get author.name)"
author_email="\$(printf "%s" "\$access_json" | json_get author.email)"
[ -n "\$remote" ] && [ -n "\$git_username" ] && [ -n "\$git_password" ] || {
echo "Git access broker response was incomplete" >&2
exit 1
}
git -C "\$REPO_DIR" remote set-url origin "\$remote"
git -C "\$REPO_DIR" config user.name "\$author_name"
git -C "\$REPO_DIR" config user.email "\$author_email"
echo "Storing Git credential..."
install_git_credential "\$remote" "\$git_username" "\$git_password"
python3 -c 'import json,sys
token=json.loads(sys.argv[1])
access=json.loads(sys.argv[2])
print(json.dumps({
"authenticated": True,
"serverUrl": sys.argv[3],
"authentik": token.get("authentik", {}),
"author": access.get("author", {}),
"remote": access.get("remote", ""),
"credentialUser": access.get("git", {}).get("username", "")
}, indent=2))' "\$token_json" "\$access_json" "\$SERVER_URL" > "\$SESSION_PATH"
chmod 600 "\$SESSION_PATH"
echo "Authenticated: \$author_email"
echo "Git remote: \$remote"
echo "Git user: \$git_username"
echo "Session: \$SESSION_PATH"
}
case "\${1:-help}" in
auth)
case "\${2:-}" in
login)
auth_login
;;
finish)
auth_finish "\${3:-}"
;;
status)
if [ -f "\$SESSION_PATH" ]; then
cat "\$SESSION_PATH"
else
echo "not authenticated; run: section0-docs auth login" >&2
exit 1
fi
;;
*)
echo "usage: section0-docs auth <login|status>" >&2
exit 1
;;
esac
;;
configure)
current_name="\$(git -C "\$REPO_DIR" config user.name || true)"
current_email="\$(git -C "\$REPO_DIR" config user.email || true)"
printf "Git author name [%s]: " "\$current_name"
IFS= read -r author_name
printf "Git author email [%s]: " "\$current_email"
IFS= read -r author_email
if [ -n "\$author_name" ]; then
git -C "\$REPO_DIR" config user.name "\$author_name"
elif [ -z "\$current_name" ]; then
echo "Git author name is required" >&2
exit 1
fi
if [ -n "\$author_email" ]; then
git -C "\$REPO_DIR" config user.email "\$author_email"
elif [ -z "\$current_email" ]; then
echo "Git author email is required" >&2
exit 1
fi
;;
doctor)
remote="\$(git -C "\$REPO_DIR" remote get-url origin)"
branch="\$(git -C "\$REPO_DIR" branch --show-current)"
author_name="\$(git -C "\$REPO_DIR" config user.name || true)"
author_email="\$(git -C "\$REPO_DIR" config user.email || true)"
echo "Repo: \$REPO_DIR"
echo "Remote: \$remote"
echo "Branch: \$branch"
if [ -n "\$author_name" ] && [ -n "\$author_email" ]; then
echo "Git author: \$author_name <\$author_email>"
else
echo "Git author: missing; run section0-docs configure"
fi
git -C "\$REPO_DIR" ls-remote --heads origin main >/dev/null
echo "Read access: ok"
;;
open)
printf "%s\n" "\$REPO_DIR"
;;
pull)
git -C "\$REPO_DIR" pull --ff-only
;;
status)
git -C "\$REPO_DIR" status --short --branch
;;
commit)
: "\${MESSAGE:?Set MESSAGE before running commit}"
git -C "\$REPO_DIR" add .
git -C "\$REPO_DIR" commit -m "\$MESSAGE"
;;
push)
git -C "\$REPO_DIR" push
;;
push-test)
branch="section0-smoke-\${USER:-user}-\$(date +%Y%m%d%H%M%S)"
git -C "\$REPO_DIR" push origin HEAD:refs/heads/"\$branch"
git -C "\$REPO_DIR" push origin :refs/heads/"\$branch"
echo "Write access: ok"
;;
help|--help|-h)
usage
;;
*)
usage >&2
exit 1
;;
esac
EOF
chmod +x "$COMMAND_PATH"
cat <<EOF
Ready.
Repo: $TARGET_DIR
Remote: $REPO_URL
Helper: $COMMAND_PATH
Daily flow:
$COMMAND_NAME auth login
$COMMAND_NAME doctor
$COMMAND_NAME pull
# edit Markdown
$COMMAND_NAME status
MESSAGE="Update docs" $COMMAND_NAME commit
$COMMAND_NAME push
Contributor setup:
$COMMAND_NAME configure
$COMMAND_NAME push-test
AFFiNE updates after the Markdown docs are refreshed from Git.
If $COMMAND_DIR is not on PATH, add this to your shell profile:
export PATH="$COMMAND_DIR:\$PATH"
EOF