WitNote / dev
AUXteam's picture
Upload folder using huggingface_hub
6a7089a verified
#!/usr/bin/env bash
set -euo pipefail
# dev β€” Pinchtab developer toolkit
# Usage: dev [command] [sub] Run a command directly
# dev Interactive picker
cd "$(dirname "$0")"
BOLD=$'\033[1m'
ACCENT=$'\033[38;2;251;191;36m'
MUTED=$'\033[38;2;90;100;128m'
SUCCESS=$'\033[38;2;0;229;204m'
ERROR=$'\033[38;2;230;57;70m'
NC=$'\033[0m'
# ── Commands ─────────────────────────────────────────────────────────
# name:emoji:description
COMMANDS=(
"build:πŸ”¨:Build the application"
"dev:πŸš€:Build & run"
"run:πŸƒ:Run the application"
"check:πŸ§ͺ:All checks"
"test unit:πŸ”¬:Go unit tests"
"test dashboard:πŸ”¬:Dashboard unit tests"
"e2e:🐳:E2E tests"
" recent:🐳:E2E Recent tests"
" orchestrator:🐳:E2E Orchestrator tests"
" curl:🐳:E2E Curl tests"
" cli:🐳:E2E CLI tests"
"doctor:🩺:Setup dev environment"
"check go:πŸ§ͺ:Go only"
"check dashboard:πŸ§ͺ:Dashboard only"
"check security:πŸ§ͺ:Gosec security scan"
"check docs:πŸ§ͺ:Validate docs JSON"
"binary:πŸ“¦:Build release-style binary into dist/"
" all:πŸ“¦:Build the full release binary matrix into dist/"
"format dashboard:🎨:Run Prettier on dashboard sources"
)
# ── Helpers ──────────────────────────────────────────────────────────
build_e2e_cli_binary() {
echo " ${MUTED}Building static binary for E2E CLI tests...${NC}"
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o tests/e2e/runner-cli/pinchtab ./cmd/pinchtab
echo " ${SUCCESS}βœ“${NC} Binary built"
echo ""
}
show_help() {
echo ""
echo " ${ACCENT}${BOLD}πŸ¦€ Pinchtab Dev${NC}"
echo ""
for cmd in "${COMMANDS[@]}"; do
IFS=':' read -r name emoji desc <<< "$cmd"
if [[ "$name" == " "* ]]; then
local trimmed="${name#"${name%%[![:space:]]*}"}"
printf " ${BOLD}%-18s${NC} ${MUTED}%s${NC}\n" "$trimmed" "$desc"
else
printf " ${SUCCESS}%s${NC} ${BOLD}%-18s${NC} ${MUTED}%s${NC}\n" "$emoji" "$name" "$desc"
fi
done
echo ""
echo " ${MUTED}Usage: dev [command] [sub]${NC}"
echo ""
}
run_command() {
local target="$1"
echo ""
case "$target" in
"check")
echo " ${ACCENT}${BOLD}πŸ§ͺ Running all checks (Go + Dashboard)${NC}"
echo ""
bash scripts/check.sh
local go_exit=$?
echo ""
echo " ${ACCENT}${BOLD}πŸ§ͺ Dashboard checks${NC}"
echo ""
bash scripts/check-dashboard.sh
local dash_exit=$?
if [ $go_exit -ne 0 ] || [ $dash_exit -ne 0 ]; then exit 1; fi
exit 0
;;
"check go")
echo " ${ACCENT}${BOLD}πŸ§ͺ Go checks${NC}"
echo ""
exec bash scripts/check.sh
;;
"check dashboard")
echo " ${ACCENT}${BOLD}πŸ§ͺ Dashboard checks${NC}"
echo ""
exec bash scripts/check-dashboard.sh
;;
"check security")
echo " ${ACCENT}${BOLD}πŸ”’ Security scan${NC}"
echo ""
exec bash scripts/check-gosec.sh
;;
"check docs")
echo " ${ACCENT}${BOLD}πŸ“– Docs validation${NC}"
echo ""
exec bash scripts/check-docs-json.sh
;;
"format dashboard")
echo " ${ACCENT}${BOLD}🎨 Dashboard formatting${NC}"
echo ""
exec bash -lc 'cd dashboard && if command -v bun >/dev/null 2>&1; then bun run format; else npx prettier --write '"'"'src/**/*.{ts,tsx,css}'"'"'; fi'
;;
"test")
echo " ${ACCENT}${BOLD}πŸ”¬ All tests${NC}"
echo ""
exec bash scripts/test.sh all
;;
"test unit")
echo " ${ACCENT}${BOLD}πŸ”¬ Go unit tests${NC}"
echo ""
exec bash scripts/test.sh unit
;;
"test dashboard")
echo " ${ACCENT}${BOLD}πŸ”¬ Dashboard tests${NC}"
echo ""
exec bash scripts/test.sh dashboard
;;
"unit")
echo " ${ACCENT}${BOLD}πŸ”¬ Unit tests${NC}"
echo ""
exec bash scripts/test.sh unit
;;
"system")
echo " ${ACCENT}${BOLD}πŸ”¬ System tests${NC}"
echo ""
exec bash scripts/test.sh system
;;
"e2e"*)
# Ensure extension fixtures are readable and traversable by the docker user (UID 1000)
# Directories need +x to be searchable.
chmod -R 755 tests/e2e/fixtures/test-extension* 2>/dev/null || true
case "$target" in
"e2e")
echo " ${ACCENT}${BOLD}🐳 E2E Recent tests (Docker)${NC}"
echo ""
docker compose -f tests/e2e/docker-compose.yml run --build --rm runner /scenarios-recent/run.sh
local recent_exit=$?
if [ $recent_exit -ne 0 ]; then
echo -e "${ERROR} Recent tests failed. Showing pinchtab logs:${NC}"
docker compose -f tests/e2e/docker-compose.yml logs pinchtab | tail -n 50
docker compose -f tests/e2e/docker-compose.yml down -v 2>/dev/null
exit 1
fi
docker compose -f tests/e2e/docker-compose.yml down -v 2>/dev/null
echo ""
echo " ${ACCENT}${BOLD}🐳 E2E Full Curl tests (Docker)${NC}"
echo ""
docker compose -f tests/e2e/docker-compose.yml up --abort-on-container-exit
local curl_exit=$?
if [ $curl_exit -ne 0 ]; then
docker compose -f tests/e2e/docker-compose.yml logs pinchtab | tail -n 50
fi
docker compose -f tests/e2e/docker-compose.yml down -v 2>/dev/null
echo ""
echo " ${ACCENT}${BOLD}🐳 E2E Orchestrator tests (Docker)${NC}"
echo ""
docker compose -f tests/e2e/docker-compose-orchestrator.yml run --build --rm runner
local orch_exit=$?
if [ $orch_exit -ne 0 ]; then
docker compose -f tests/e2e/docker-compose-orchestrator.yml logs pinchtab | tail -n 50 || true
docker compose -f tests/e2e/docker-compose-orchestrator.yml logs pinchtab-bridge | tail -n 50 || true
fi
docker compose -f tests/e2e/docker-compose-orchestrator.yml down -v 2>/dev/null
echo ""
echo " ${ACCENT}${BOLD}🐳 E2E CLI tests (Docker)${NC}"
echo ""
build_e2e_cli_binary
docker compose -f tests/e2e/docker-compose-cli.yml up --build --abort-on-container-exit
local cli_exit=$?
if [ $cli_exit -ne 0 ]; then
docker compose -f tests/e2e/docker-compose-cli.yml logs pinchtab | tail -n 50
fi
docker compose -f tests/e2e/docker-compose-cli.yml down -v 2>/dev/null
echo ""
if [ $curl_exit -ne 0 ] || [ $orch_exit -ne 0 ] || [ $cli_exit -ne 0 ]; then
echo " ${ERROR}Some E2E tests failed${NC}"
exit 1
fi
echo " ${SUCCESS}All E2E tests passed${NC}"
exit 0
;;
"e2e curl")
echo " ${ACCENT}${BOLD}🐳 E2E Curl tests (Docker)${NC}"
echo ""
docker compose -f tests/e2e/docker-compose.yml up --build --abort-on-container-exit
local curl_exit=$?
if [ $curl_exit -ne 0 ]; then
docker compose -f tests/e2e/docker-compose.yml logs pinchtab | tail -n 50
fi
docker compose -f tests/e2e/docker-compose.yml down -v 2>/dev/null
exit $curl_exit
;;
"e2e cli")
echo " ${ACCENT}${BOLD}🐳 E2E CLI tests (Docker)${NC}"
echo ""
build_e2e_cli_binary
docker compose -f tests/e2e/docker-compose-cli.yml up --build --abort-on-container-exit
local cli_exit=$?
if [ $cli_exit -ne 0 ]; then
docker compose -f tests/e2e/docker-compose-cli.yml logs pinchtab | tail -n 50
fi
docker compose -f tests/e2e/docker-compose-cli.yml down -v 2>/dev/null
exit $cli_exit
;;
"e2e orchestrator")
echo " ${ACCENT}${BOLD}🐳 E2E Orchestrator tests (Docker)${NC}"
echo ""
docker compose -f tests/e2e/docker-compose-orchestrator.yml run --build --rm runner
local orch_exit=$?
if [ $orch_exit -ne 0 ]; then
docker compose -f tests/e2e/docker-compose-orchestrator.yml logs pinchtab | tail -n 50 || true
docker compose -f tests/e2e/docker-compose-orchestrator.yml logs pinchtab-bridge | tail -n 50 || true
fi
docker compose -f tests/e2e/docker-compose-orchestrator.yml down -v 2>/dev/null
exit $orch_exit
;;
"e2e recent")
echo " ${ACCENT}${BOLD}🐳 E2E Recent tests (Docker)${NC}"
echo ""
docker compose -f tests/e2e/docker-compose.yml run --build --rm runner /scenarios-recent/run.sh
local recent_exit=$?
if [ $recent_exit -ne 0 ]; then
echo ""
echo -e "${ERROR} Recent tests failed. Dumping pinchtab logs:${NC}"
docker compose -f tests/e2e/docker-compose.yml logs pinchtab || true
echo ""
echo -e "${ERROR} Filtered Chrome/extension lines:${NC}"
docker compose -f tests/e2e/docker-compose.yml logs pinchtab 2>/dev/null | grep -Ei 'chrome|extension|devtools|load-extension|disable-extensions|headless|warning|error' || true
echo ""
echo -e "${ERROR} Instance inventory:${NC}"
local instances_json
instances_json=$(curl -sf http://localhost:9999/instances || true)
if [ -n "$instances_json" ]; then
echo "$instances_json"
if command -v jq >/dev/null 2>&1; then
while IFS= read -r inst_id; do
[ -z "$inst_id" ] && continue
echo ""
echo -e "${ERROR} Instance logs for ${inst_id}:${NC}"
curl -sf "http://localhost:9999/instances/${inst_id}/logs" || true
done < <(echo "$instances_json" | jq -r '.[].id')
fi
else
echo " unable to fetch /instances from localhost:9999"
fi
fi
docker compose -f tests/e2e/docker-compose.yml down -v 2>/dev/null
exit $recent_exit
;;
esac
;;
"dev") exec bash scripts/dev.sh ;;
"build") exec bash scripts/build.sh ;;
"binary") exec bash scripts/binary.sh ;;
"binary all"|"all") exec bash scripts/binary.sh all ;;
"run") exec bash scripts/run.sh ;;
"doctor") exec bash scripts/doctor.sh ;;
"hooks") exec bash scripts/install-hooks.sh ;;
*)
echo " ${ERROR}Unknown command: $target${NC}"
show_help
exit 1
;;
esac
}
pick_with_gum() {
local options=()
local commands_map=() # parallel array: full command string for each option
local parent=""
for cmd in "${COMMANDS[@]}"; do
IFS=':' read -r name emoji desc <<< "$cmd"
local trimmed="${name#"${name%%[![:space:]]*}"}"
if [[ "$name" == " "* ]]; then
# Subcommand: indent, no emoji β€” prefix with parent
options+=("$(printf ' %-18s %s' "$trimmed" "$desc")")
commands_map+=("$parent $trimmed")
else
parent="$trimmed"
options+=("$(printf '%s %-18s %s' "$emoji" "$trimmed" "$desc")")
commands_map+=("$trimmed")
fi
done
local choice
choice=$(printf '%s\n' "${options[@]}" | gum choose \
--header "πŸ¦€ Pinchtab Dev" \
--header.foreground "#fbbf24" \
--cursor.foreground "#00e5cc" \
--selected.foreground "#00e5cc" \
--item.foreground "#8892b0")
# Find the index of the chosen option and look up the full command
local picked=""
for i in "${!options[@]}"; do
if [[ "${options[$i]}" == "$choice" ]]; then
picked="${commands_map[$i]}"
break
fi
done
# Clear the picker before streaming command output.
printf '\033[2J\033[H'
run_command "$picked"
}
pick_with_select() {
echo ""
echo " ${ACCENT}${BOLD}πŸ¦€ Pinchtab Dev${NC}"
echo ""
local names=()
local i=1
for cmd in "${COMMANDS[@]}"; do
IFS=':' read -r name emoji desc <<< "$cmd"
local trimmed="${name#"${name%%[![:space:]]*}"}"
if [[ "$name" == " "* ]]; then
printf " ${MUTED}[%3d]${NC} ${BOLD}%-18s${NC} ${MUTED}%s${NC}\n" "$i" "$trimmed" "$desc"
else
printf " ${MUTED}[%3d]${NC} ${SUCCESS}%s${NC} ${BOLD}%-18s${NC} ${MUTED}%s${NC}\n" "$i" "$emoji" "$trimmed" "$desc"
fi
names+=("$trimmed")
i=$((i + 1))
done
echo ""
echo -ne " ${BOLD}Pick [1-${#COMMANDS[@]}]:${NC} "
read -r choice
# Handle numeric or name input
if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -le ${#COMMANDS[@]} ]; then
run_command "${names[$((choice - 1))]}"
else
run_command "$choice"
fi
}
# ── Main ─────────────────────────────────────────────────────────────
# Direct command with optional subcommand
if [ $# -gt 0 ]; then
case "$1" in
-h|--help|help) show_help; exit 0 ;;
-i|--interactive)
if command -v gum &>/dev/null; then pick_with_gum; else pick_with_select; fi
exit 0
;;
check)
if [ $# -gt 1 ]; then
run_command "check $2"
else
run_command "check"
fi
;;
test)
if [ $# -gt 1 ]; then
run_command "test $2"
else
run_command "test"
fi
;;
format)
if [ $# -gt 1 ]; then
run_command "format $2"
else
run_command "format"
fi
;;
e2e)
if [ $# -gt 1 ]; then
run_command "e2e $2"
else
run_command "e2e"
fi
;;
binary)
if [ $# -gt 1 ]; then
run_command "binary $2"
else
run_command "binary"
fi
;;
*) run_command "$1" ;;
esac
fi
# No args β†’ interactive picker
if command -v gum &>/dev/null; then
pick_with_gum
else
pick_with_select
fi