feat: add gitea bash CLI with silly personality
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,164 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# gitea — your friendly neighborhood Gitea CLI
|
||||||
|
# It creates repos. It pushes code. It occasionally judges your commit messages.
|
||||||
|
# Usage: gitea <command> [options]
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cmd_help() {
|
||||||
|
cat <<'EOF'
|
||||||
|
gitea — your friendly neighborhood Gitea CLI
|
||||||
|
|
||||||
|
Usage: gitea <command> [options]
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
create <name> [--private] [--description "..."] Bring a new repo into this world
|
||||||
|
remote <name> Wire up git origin with token auth
|
||||||
|
push Yeet current branch to origin
|
||||||
|
delete <name> Send a repo into the void (confirms first)
|
||||||
|
list Show your repos (sorted by last updated)
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
gitea create my-project --private --description "Top secret stuff"
|
||||||
|
gitea remote my-project
|
||||||
|
gitea push
|
||||||
|
|
||||||
|
Config files (~/.config/gitea/):
|
||||||
|
token Your API token (required)
|
||||||
|
username Your Gitea username (required)
|
||||||
|
host Gitea host override (default: git.sethpc.xyz)
|
||||||
|
|
||||||
|
First time? Run /gitea-setup in Claude Code to get started.
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# Short-circuit help/unknown before loading config
|
||||||
|
case "${1:-help}" in
|
||||||
|
help|--help|-h) cmd_help; exit 0 ;;
|
||||||
|
create|remote|push|delete|list|ls) ;;
|
||||||
|
*) echo "Unknown command: $1 (try 'gitea help' — I promise it's helpful)" >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# --- Config ---
|
||||||
|
GITEA_HOST="git.sethpc.xyz"
|
||||||
|
if [[ -f "$HOME/.config/gitea/host" ]]; then
|
||||||
|
GITEA_HOST="$(< "$HOME/.config/gitea/host")"
|
||||||
|
GITEA_HOST="${GITEA_HOST%%[[:space:]]}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
GITEA_USER=""
|
||||||
|
if [[ -f "$HOME/.config/gitea/username" ]]; then
|
||||||
|
GITEA_USER="$(< "$HOME/.config/gitea/username")"
|
||||||
|
GITEA_USER="${GITEA_USER%%[[:space:]]}"
|
||||||
|
fi
|
||||||
|
[[ -z "$GITEA_USER" ]] && { echo "ERROR: No username found. Run /gitea-setup or put your username in ~/.config/gitea/username" >&2; exit 1; }
|
||||||
|
|
||||||
|
GITEA_TOKEN="${GITEA_TOKEN:-}"
|
||||||
|
if [[ -z "$GITEA_TOKEN" && -f "$HOME/.config/gitea/token" ]]; then
|
||||||
|
GITEA_TOKEN="$(< "$HOME/.config/gitea/token")"
|
||||||
|
GITEA_TOKEN="${GITEA_TOKEN%%[[:space:]]}"
|
||||||
|
fi
|
||||||
|
[[ -z "$GITEA_TOKEN" ]] && { echo "ERROR: No token found. Run /gitea-setup or put your token in ~/.config/gitea/token" >&2; exit 1; }
|
||||||
|
|
||||||
|
API_BASE="https://${GITEA_HOST}/api/v1"
|
||||||
|
|
||||||
|
# --- Helpers ---
|
||||||
|
api() {
|
||||||
|
local method="$1" endpoint="$2"
|
||||||
|
shift 2
|
||||||
|
curl -sf -X "$method" "${API_BASE}${endpoint}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||||
|
"$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Commands ---
|
||||||
|
cmd_create() {
|
||||||
|
local name="" private=false description=""
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--private|-p) private=true; shift ;;
|
||||||
|
--public) private=false; shift ;;
|
||||||
|
--description|-d) description="$2"; shift 2 ;;
|
||||||
|
-*) echo "Unknown option: $1 (I don't know what that means and I'm too proud to guess)" >&2; exit 1 ;;
|
||||||
|
*) name="$1"; shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
[[ -z "$name" ]] && { echo "Usage: gitea create <name> [--private] [--description \"...\"]"; echo " (You forgot the name. The repo needs a name. We all need names.)" >&2; exit 1; }
|
||||||
|
|
||||||
|
local payload
|
||||||
|
payload=$(jq -n \
|
||||||
|
--arg name "$name" \
|
||||||
|
--argjson private "$private" \
|
||||||
|
--arg desc "$description" \
|
||||||
|
'{name: $name, private: $private, description: $desc, auto_init: false}')
|
||||||
|
|
||||||
|
local resp
|
||||||
|
resp=$(api POST "/user/repos" -d "$payload") || { echo "FAIL: Gitea said no. Check your token and network." >&2; exit 1; }
|
||||||
|
|
||||||
|
local html_url
|
||||||
|
html_url=$(echo "$resp" | jq -r '.html_url')
|
||||||
|
echo "Created: ${html_url}"
|
||||||
|
echo "Clone: https://${GITEA_HOST}/${GITEA_USER}/${name}.git"
|
||||||
|
echo ""
|
||||||
|
echo "Next up: 'gitea remote ${name}' to wire up your origin. You know the drill."
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_remote() {
|
||||||
|
local name="${1:-}"
|
||||||
|
[[ -z "$name" ]] && { echo "Usage: gitea remote <repo-name>"; echo " (Which repo? I can't read minds. Yet.)" >&2; exit 1; }
|
||||||
|
|
||||||
|
local url="https://${GITEA_HOST}/${GITEA_USER}/${name}.git"
|
||||||
|
|
||||||
|
git config credential.helper 'store'
|
||||||
|
local cred_file="${HOME}/.git-credentials"
|
||||||
|
local cred_entry="https://${GITEA_USER}:${GITEA_TOKEN}@${GITEA_HOST}"
|
||||||
|
if ! grep -qF "${GITEA_HOST}" "$cred_file" 2>/dev/null; then
|
||||||
|
echo "$cred_entry" >> "$cred_file"
|
||||||
|
chmod 600 "$cred_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if git remote get-url origin &>/dev/null; then
|
||||||
|
git remote set-url origin "$url"
|
||||||
|
echo "Updated origin -> ${url}"
|
||||||
|
else
|
||||||
|
git remote add origin "$url"
|
||||||
|
echo "Added origin -> ${url}"
|
||||||
|
fi
|
||||||
|
echo "You're locked and loaded. 'gitea push' when ready."
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_push() {
|
||||||
|
local branch
|
||||||
|
branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null) || { echo "This isn't a git repo. I checked." >&2; exit 1; }
|
||||||
|
echo "Pushing ${branch} to origin... hold onto your bits."
|
||||||
|
git push -u origin "$branch"
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_delete() {
|
||||||
|
local name="${1:-}"
|
||||||
|
[[ -z "$name" ]] && { echo "Usage: gitea delete <repo-name>"; echo " (Delete what? Be specific. Destruction requires precision.)" >&2; exit 1; }
|
||||||
|
|
||||||
|
read -rp "Delete ${GITEA_USER}/${name}? This is permanent. No take-backs. [y/N] " confirm
|
||||||
|
[[ "$confirm" =~ ^[Yy]$ ]] || { echo "Crisis averted. Repo lives another day."; exit 0; }
|
||||||
|
|
||||||
|
if api DELETE "/repos/${GITEA_USER}/${name}"; then
|
||||||
|
echo "Deleted: ${GITEA_USER}/${name}. It's gone. Pour one out."
|
||||||
|
else
|
||||||
|
echo "FAIL: Could not delete ${GITEA_USER}/${name}. Maybe it's already gone? Maybe it's fighting back?" >&2; exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_list() {
|
||||||
|
local resp
|
||||||
|
resp=$(api GET "/user/repos?limit=50&sort=updated") || { echo "FAIL: Couldn't fetch repos. Is Gitea awake?" >&2; exit 1; }
|
||||||
|
echo "$resp" | jq -r '.[] | "\(.name)\t\(if .private then "private" else "public" end)\t\(.description // "")"' | column -t -s$'\t'
|
||||||
|
}
|
||||||
|
|
||||||
|
case "${1:-help}" in
|
||||||
|
create) shift; cmd_create "$@" ;;
|
||||||
|
remote) shift; cmd_remote "$@" ;;
|
||||||
|
push) shift; cmd_push "$@" ;;
|
||||||
|
delete) shift; cmd_delete "$@" ;;
|
||||||
|
list|ls) shift; cmd_list "$@" ;;
|
||||||
|
esac
|
||||||
Reference in New Issue
Block a user