This repository has been archived on 2026-06-22. You can view files and clone it, but you cannot make any changes to its state, such as pushing and creating new issues, pull requests or comments.
lnbits-sensei/modules/dev-env/scripts/status.sh
Padreug e38d313db2 feat(dev-env): backport matured dev-env implementation from /etc/nixos
Replace the stub dev-env with the real, working implementation that grew
in the reference machine config — de-identified for the public scaffold.

Nix layer:
- options.nix: full project schema (url/upstream/fork/category/
  worktreeRoot/worktrees{branch,path,remote}/isClone/deployFlakeInput),
  deploy.targets, github.forkUser, writeDirenvHints. Drops the
  forgejo-URL block + deploy-flake auto-derivation (incoherent in a
  scaffold that uses explicit per-project urls).
- lib.nix: mkProject + worktreePath/bareRepoPath/projectRemotes,
  generalized to the explicit-url model (origin falls back to upstream).
- config.nix: renders /etc/dev-env/{config.sh,projects.json,
  tmux-sessions.json}, installs helpers via writeShellScriptBin, loads
  shell functions into interactive shells, wires the git pre-commit hook.

Scripts (config-driven, read /etc/dev-env at runtime):
- bootstrap.sh, nav.sh, worktree.sh, pr-helpers.sh, rebase.sh,
  status.sh, deploy.sh, regtest.sh, tmux-launch.sh.
- Stripped aiolabs/forgejo/bitspire/lamassu/webapp hardcoding; the
  github-fork remote is renamed 'fork' to match git.remotes vocabulary.
- Removes the dev.sh stub (the matured impl uses discrete commands +
  shell functions, not a unified 'dev' CLI).

presets/example.nix: a worked, generic project list replacing the
identity-specific aiolabs preset. tests/smoke.nix + flake checks
exercise the schema; 'nix flake check' is green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 21:18:49 +02:00

145 lines
4.7 KiB
Bash

#!/usr/bin/env bash
# dev-status: show divergence and dirty state of every dev-env worktree
#
# Reads /etc/dev-env/projects.json and walks every declared worktree,
# reporting dirty state and ahead/behind counts vs origin (and upstream,
# for projects that declare one).
set -euo pipefail
if [[ -r /etc/dev-env/config.sh ]]; then
# shellcheck disable=SC1091
source /etc/dev-env/config.sh
fi
PROJECTS_JSON="${DEVENV_PROJECTS_JSON:-/etc/dev-env/projects.json}"
if [[ ! -r "$PROJECTS_JSON" ]]; then
echo "projects.json not found at $PROJECTS_JSON" >&2
exit 1
fi
if ! command -v jq >/dev/null; then
echo "jq required" >&2
exit 1
fi
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m'
issues=()
header() {
echo ""
echo -e "${BOLD}${CYAN}═══════════════════════════════════════════════════════${NC}"
echo -e "${BOLD}${CYAN} $1${NC}"
echo -e "${BOLD}${CYAN}═══════════════════════════════════════════════════════${NC}"
}
check_one() {
local label="$1" path="$2" has_upstream="${3:-false}"
if [[ ! -d "$path/.git" ]] && [[ ! -f "$path/.git" ]]; then
printf " ${YELLOW}${NC} %s: not present\n" "$label"
issues+=("$label: not present")
return
fi
git -C "$path" fetch --all --quiet 2>/dev/null || true
local branch icons="" status_str=""
branch="$(git -C "$path" branch --show-current 2>/dev/null || echo '?')"
[[ -n "$(git -C "$path" status --porcelain)" ]] && {
icons+="${YELLOW}${NC} "
status_str+="dirty "
}
local behind ahead
behind=$(git -C "$path" rev-list --count "HEAD..origin/$branch" 2>/dev/null || echo 0)
ahead=$(git -C "$path" rev-list --count "origin/$branch..HEAD" 2>/dev/null || echo 0)
(( behind > 0 )) && icons+="${RED}${NC}$behind "
(( ahead > 0 )) && icons+="${GREEN}${NC}$ahead "
if [[ "$has_upstream" == "true" ]] && git -C "$path" remote get-url upstream &>/dev/null; then
local ub="main"
git -C "$path" rev-parse upstream/main &>/dev/null || ub="master"
local ub_behind
ub_behind=$(git -C "$path" rev-list --count "HEAD..upstream/$ub" 2>/dev/null || echo 0)
if (( ub_behind > 0 )); then
icons+="${CYAN}${NC}$ub_behind "
issues+=("$label: $ub_behind behind upstream/$ub")
fi
fi
[[ -z "$icons" ]] && icons="${GREEN}${NC}"
printf " %-45s %s (%s)\n" "$label" "$icons" "$branch"
}
header "Development Environment Status"
echo -e " ${BLUE}Date:${NC} $(date '+%Y-%m-%d %H:%M')"
echo -e " ${BLUE}Host:${NC} $(hostname)"
echo -e " ${BLUE}Root:${NC} ${DEV_ROOT:-?}"
# Walk every project and worktree from the JSON.
mapfile -t PROJECTS < <(jq -r 'keys[]' "$PROJECTS_JSON")
for proj in "${PROJECTS[@]}"; do
echo ""
echo -e "${BLUE}─── $proj ───${NC}"
is_clone="$(jq -r --arg p "$proj" '.[$p].isClone' "$PROJECTS_JSON")"
has_upstream_decl="$(jq -r --arg p "$proj" '.[$p].remotes.upstream != null' "$PROJECTS_JSON")"
if [[ "$is_clone" == "true" ]]; then
clone_path="$(jq -r --arg p "$proj" '.[$p].clonePath' "$PROJECTS_JSON")"
check_one "$proj" "$clone_path" "$has_upstream_decl"
else
while IFS=$'\t' read -r wt_name wt_path; do
[[ -z "$wt_name" || "$wt_path" == "null" ]] && continue
check_one "$proj/$wt_name" "$wt_path" "$has_upstream_decl"
done < <(jq -r --arg p "$proj" '
.[$p].worktrees
| to_entries[]
| "\(.key)\t\(.value.path)"
' "$PROJECTS_JSON")
fi
done
# Docker
echo ""
echo -e "${BLUE}─── Docker ───${NC}"
if command -v docker &>/dev/null; then
running="$(docker ps --format '{{.Names}}' 2>/dev/null | wc -l)"
echo -e " ${BLUE}Containers running:${NC} $running"
fi
header "Summary"
if (( ${#issues[@]} == 0 )); then
echo -e " ${GREEN}✓ All worktrees are clean and in sync!${NC}"
else
echo -e " ${YELLOW}Issues found:${NC}"
for issue in "${issues[@]}"; do
echo -e " ${YELLOW}${NC} $issue"
done
fi
cat <<EOF
${BLUE}Legend:${NC}
${GREEN}✓${NC} clean ${YELLOW}●${NC} dirty ${GREEN}↑${NC} ahead origin
${RED}↓${NC} behind origin ${CYAN}⇣${NC} behind upstream
${BLUE}Quick actions:${NC}
wts sync all worktrees with origin
wtu <repo> fetch upstream + show divergence
rebase status which forks need rebasing onto upstream
dev-env-bootstrap materialize missing worktrees
EOF