bootstrap: projects management repo
This commit is contained in:
124
tools/projectsctl
Executable file
124
tools/projectsctl
Executable file
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO="${PROJREPO:-$(cd "$(dirname "$0")/.." && pwd)}"
|
||||
LOCK="$REPO/.projects.lock"
|
||||
TS="$(date +%F_%H%M%S)"
|
||||
cd "$REPO"
|
||||
|
||||
need() { command -v "$1" >/dev/null 2>&1 || { echo "ERROR: missing $1"; exit 1; }; }
|
||||
need jq
|
||||
need git
|
||||
need python3
|
||||
need flock
|
||||
|
||||
exec 9>"$LOCK"
|
||||
flock -n 9 || { echo "ERROR: projectsctl is already running"; exit 2; }
|
||||
|
||||
backup() {
|
||||
mkdir -p "$REPO/backups"
|
||||
cp -a "$REPO/projects.json" "$REPO/backups/projects.json.$TS"
|
||||
}
|
||||
|
||||
render() {
|
||||
./tools/render_projects_index.py >/dev/null
|
||||
}
|
||||
|
||||
git_sync() {
|
||||
git rev-parse --is-inside-work-tree >/dev/null
|
||||
git checkout main >/dev/null 2>&1 || true
|
||||
git pull --rebase --autostash >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
git_commit_push() {
|
||||
if git diff --quiet && git diff --cached --quiet; then
|
||||
echo "No changes to commit."
|
||||
exit 0
|
||||
fi
|
||||
git add projects.json PROJECTS_INDEX.md
|
||||
git commit -m "projects: update ($TS)" >/dev/null
|
||||
git push origin main >/dev/null
|
||||
echo "OK: committed and pushed to main."
|
||||
}
|
||||
|
||||
cmd="${1:-}"; shift || true
|
||||
|
||||
case "$cmd" in
|
||||
add)
|
||||
name="${1:-}"; status="${2:-active}"; shift 2 || true
|
||||
repo="${1:-}"; local_path="${2:-}"; shift 2 || true
|
||||
notes="${*:-}"
|
||||
[[ -n "$name" ]] || { echo "usage: projectsctl add <name> [status] [repo] [local_path] [notes...]"; exit 1; }
|
||||
|
||||
git_sync
|
||||
backup
|
||||
|
||||
tmp="$(mktemp)"
|
||||
jq --arg name "$name" --arg status "$status" --arg repo "$repo" --arg lp "$local_path" --arg notes "$notes" '
|
||||
.projects |= ( . + [ {name:$name,status:$status,type:"",repo:$repo,local_path:$lp,notes:$notes} ] )
|
||||
' projects.json > "$tmp"
|
||||
mv "$tmp" projects.json
|
||||
|
||||
render
|
||||
git_commit_push
|
||||
;;
|
||||
|
||||
set-status)
|
||||
name="${1:-}"; status="${2:-}"
|
||||
[[ -n "$name" && -n "$status" ]] || { echo "usage: projectsctl set-status <name> <active|paused|archived>"; exit 1; }
|
||||
|
||||
git_sync
|
||||
backup
|
||||
|
||||
tmp="$(mktemp)"
|
||||
jq --arg name "$name" --arg status "$status" '
|
||||
.projects |= (map(if .name==$name then .status=$status else . end))
|
||||
' projects.json > "$tmp"
|
||||
mv "$tmp" projects.json
|
||||
|
||||
render
|
||||
git_commit_push
|
||||
;;
|
||||
|
||||
update-notes)
|
||||
name="${1:-}"; shift || true
|
||||
notes="${*:-}"
|
||||
[[ -n "$name" && -n "$notes" ]] || { echo "usage: projectsctl update-notes <name> <notes...>"; exit 1; }
|
||||
|
||||
git_sync
|
||||
backup
|
||||
|
||||
tmp="$(mktemp)"
|
||||
jq --arg name "$name" --arg notes "$notes" '
|
||||
.projects |= (map(if .name==$name then .notes=$notes else . end))
|
||||
' projects.json > "$tmp"
|
||||
mv "$tmp" projects.json
|
||||
|
||||
render
|
||||
git_commit_push
|
||||
;;
|
||||
|
||||
list)
|
||||
python3 - <<'PY'
|
||||
import json
|
||||
p=json.load(open("projects.json"))
|
||||
for pr in p.get("projects",[]):
|
||||
print(f"{pr.get('status',''):8} {pr.get('name','')}")
|
||||
PY
|
||||
;;
|
||||
|
||||
render)
|
||||
render
|
||||
echo "OK: rendered PROJECTS_INDEX.md"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "usage:"
|
||||
echo " projectsctl add <name> [status] [repo] [local_path] [notes...]"
|
||||
echo " projectsctl set-status <name> <active|paused|archived>"
|
||||
echo " projectsctl update-notes <name> <notes...>"
|
||||
echo " projectsctl list"
|
||||
echo " projectsctl render"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user