gitlink-cli/workflows/lib/common.sh

170 lines
6.0 KiB
Bash

#!/usr/bin/env bash
# Common utilities for gitlink-cli workflow scripts
set -euo pipefail
# Trap SIGPIPE to prevent premature exit when piping through head/truncate
trap '' PIPE
# Ensure jq is available (WinGet installs to non-default PATH on Windows)
if ! command -v jq &>/dev/null; then
for d in "$LOCALAPPDATA/Microsoft/WinGet/Links" "$HOME/AppData/Local/Microsoft/WinGet/Links"; do
[[ -d "$d" ]] && export PATH="$d:$PATH"
done
fi
# Ensure CLAUDE_CODE_GIT_BASH_PATH is set for Windows (needed by claude CLI)
if [[ -z "${CLAUDE_CODE_GIT_BASH_PATH:-}" ]] && command -v cygpath &>/dev/null; then
export CLAUDE_CODE_GIT_BASH_PATH="$(cygpath -w "$(which bash)")"
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'
# ── Logging ──────────────────────────────────────────────────────────
log_step() { echo -e "${BLUE}[STEP]${NC} $*"; }
log_ok() { echo -e "${GREEN}[ OK]${NC} $*"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
log_err() { echo -e "${RED}[ ERR]${NC} $*" >&2; }
log_info() { echo -e "${CYAN}[INFO]${NC} $*"; }
log_title(){ echo -e "\n${BOLD}══════ $* ══════${NC}\n"; }
# ── Auth Check ───────────────────────────────────────────────────────
check_auth() {
# Check env var first, then try CLI auth status
if [[ -n "${GITLINK_TOKEN:-}" ]]; then
log_ok "GITLINK_TOKEN is set"
return 0
fi
local status
status=$(gitlink-cli auth status 2>&1)
if echo "$status" | grep -qi "logged in\|✓"; then
log_ok "Authenticated: $(echo "$status" | sed -n 's/.*as //p' | tr -d '[:space:]')"
return 0
fi
log_err "Not authenticated. Please login first:"
log_info " gitlink-cli auth login"
log_info " export GITLINK_TOKEN=\"your-private-token\""
exit 1
}
# ── JSON Helpers ─────────────────────────────────────────────────────
# Extract a field from CLI JSON output (Envelope: {ok, data, ...})
json_ok() {
echo "$1" | jq -r '.ok // false' 2>/dev/null
}
json_data() {
echo "$1" | jq -r '.data' 2>/dev/null
}
json_get() {
echo "$1" | jq -r "$2" 2>/dev/null
}
json_error() {
echo "$1" | jq -r '.error.message // "unknown error"' 2>/dev/null
}
# ── CLI Wrapper ──────────────────────────────────────────────────────
GL="gitlink-cli"
gl_run() {
local output
# Always use JSON format for scripting
output=$("$GL" "$@" --format json 2>&1) || true
echo "$output"
}
gl_check() {
local output
output=$(gl_run "$@")
# Check if output is valid JSON (use here-string to avoid SIGPIPE)
if ! jq empty <<< "$output" 2>/dev/null; then
log_err "Command failed (non-JSON response): $GL $*"
log_err "$output"
return 1
fi
if [[ "$(json_ok "$output")" != "true" ]]; then
log_err "Command failed: $GL $*"
log_err "$(json_error "$output")"
return 1
fi
echo "$output"
}
# ── Owner/Repo Detection ────────────────────────────────────────────
detect_owner_repo() {
local remote_url
remote_url=$(git remote get-url origin 2>/dev/null || echo "")
if [[ -z "$remote_url" ]]; then
log_err "No git remote 'origin' found. Use --owner and --repo flags."
exit 1
fi
# Parse gitlink URL patterns
# https://gitlink.org.cn/owner/repo.git or git@gitlink.org.cn:owner/repo.git
if [[ "$remote_url" =~ gitlink\.org\.cn[:/]([^/]+)/([^/.]+) ]]; then
DETECTED_OWNER="${BASH_REMATCH[1]}"
DETECTED_REPO="${BASH_REMATCH[2]}"
else
log_err "Cannot parse owner/repo from remote: $remote_url"
exit 1
fi
}
require_owner_repo() {
if [[ -z "${OWNER:-}" || -z "${REPO:-}" ]]; then
detect_owner_repo
OWNER="${OWNER:-$DETECTED_OWNER}"
REPO="${REPO:-$DETECTED_REPO}"
fi
log_info "Using: ${OWNER}/${REPO}"
}
# ── Confirmation ─────────────────────────────────────────────────────
confirm() {
local msg="${1:-Proceed?}"
if [[ "${DRY_RUN:-false}" == "true" ]]; then
log_warn "[DRY RUN] Would execute: $msg"
return 1
fi
read -rp "$(echo -e "${YELLOW}$msg [y/N]${NC} ")" answer
[[ "$answer" =~ ^[Yy] ]]
}
# ── Date Helpers ─────────────────────────────────────────────────────
date_today() {
date +%Y-%m-%d
}
date_week_ago() {
date -d "7 days ago" +%Y-%m-%d 2>/dev/null || date -v-7d +%Y-%m-%d 2>/dev/null
}
date_month_ago() {
date -d "30 days ago" +%Y-%m-%d 2>/dev/null || date -v-30d +%Y-%m-%d 2>/dev/null
}
# ── Parameter Parsing ────────────────────────────────────────────────
parse_common_args() {
while [[ $# -gt 0 ]]; do
case "$1" in
--owner) OWNER="$2"; shift 2 ;;
--repo) REPO="$2"; shift 2 ;;
--dry-run) DRY_RUN="true"; shift ;;
--help|-h) usage; exit 0 ;;
*) break ;;
esac
done
}
# ── Section Divider ──────────────────────────────────────────────────
divider() {
echo -e "${CYAN}────────────────────────────────────────────────${NC}"
}