279 lines
7.0 KiB
Bash
Executable File
279 lines
7.0 KiB
Bash
Executable File
#!/bin/bash
|
|
# Sprint CLI for Gantt Board (API passthrough)
|
|
# Usage: ./sprint.sh [create|list|get|update|delete|close] [options]
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
# shellcheck source=./lib/api_client.sh
|
|
source "$SCRIPT_DIR/lib/api_client.sh"
|
|
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m'
|
|
|
|
log_info() { echo -e "${BLUE}i${NC} $1"; }
|
|
log_success() { echo -e "${GREEN}ok${NC} $1"; }
|
|
log_warning() { echo -e "${YELLOW}warn${NC} $1"; }
|
|
log_error() { echo -e "${RED}error${NC} $1"; }
|
|
|
|
UUID_PATTERN='^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'
|
|
|
|
check_dependencies() {
|
|
if ! command -v jq >/dev/null 2>&1; then
|
|
log_error "jq is required. Install with: brew install jq"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
show_usage() {
|
|
cat << 'USAGE'
|
|
Sprint CLI for Gantt Board
|
|
|
|
USAGE:
|
|
./sprint.sh [COMMAND] [OPTIONS]
|
|
|
|
COMMANDS:
|
|
create Create a new sprint
|
|
list List all sprints
|
|
get <id-or-name> Get a specific sprint
|
|
update <id-or-name> Update a sprint
|
|
delete <id-or-name> Delete a sprint
|
|
close <id-or-name> Mark sprint completed
|
|
|
|
CREATE OPTIONS:
|
|
--name "Name" Sprint name (required)
|
|
--goal "Goal" Sprint goal
|
|
--start-date "YYYY-MM-DD" Start date
|
|
--end-date "YYYY-MM-DD" End date
|
|
--status [planning|active|completed] Status (default: planning)
|
|
|
|
LIST OPTIONS:
|
|
--status <status> Filter by status
|
|
--active Show sprints in progress for today (start <= today <= end)
|
|
--json Output as JSON
|
|
USAGE
|
|
}
|
|
|
|
resolve_sprint_id() {
|
|
local identifier="$1"
|
|
|
|
if [[ "$identifier" =~ $UUID_PATTERN ]]; then
|
|
echo "$identifier"
|
|
return 0
|
|
fi
|
|
|
|
local response
|
|
response=$(api_call GET "/sprints")
|
|
|
|
local sprint_id
|
|
sprint_id=$(echo "$response" | jq -r --arg q "$identifier" '
|
|
.sprints
|
|
| map(select((.name // "") | ascii_downcase | contains($q | ascii_downcase)))
|
|
| .[0].id // empty
|
|
')
|
|
|
|
if [[ -n "$sprint_id" ]]; then
|
|
echo "$sprint_id"
|
|
return 0
|
|
fi
|
|
|
|
log_error "Sprint '$identifier' not found"
|
|
return 1
|
|
}
|
|
|
|
cmd_create() {
|
|
local name=""
|
|
local goal=""
|
|
local start_date=""
|
|
local end_date=""
|
|
local status="planning"
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "${1:-}" in
|
|
--name) name="${2:-}"; shift 2 ;;
|
|
--goal) goal="${2:-}"; shift 2 ;;
|
|
--start-date) start_date="${2:-}"; shift 2 ;;
|
|
--end-date) end_date="${2:-}"; shift 2 ;;
|
|
--status) status="${2:-}"; shift 2 ;;
|
|
*) shift ;;
|
|
esac
|
|
done
|
|
|
|
if [[ -z "$name" ]]; then
|
|
log_error "Name is required (use --name)"
|
|
exit 1
|
|
fi
|
|
|
|
local payload
|
|
payload=$(jq -n \
|
|
--arg name "$name" \
|
|
--arg goal "$goal" \
|
|
--arg startDate "$start_date" \
|
|
--arg endDate "$end_date" \
|
|
--arg status "$status" \
|
|
'{
|
|
name: $name,
|
|
goal: (if $goal == "" then null else $goal end),
|
|
startDate: (if $startDate == "" then null else $startDate end),
|
|
endDate: (if $endDate == "" then null else $endDate end),
|
|
status: $status
|
|
}')
|
|
|
|
log_info "Creating sprint..."
|
|
api_call POST "/sprints" "$payload" | jq .
|
|
}
|
|
|
|
cmd_list() {
|
|
local filter_status=""
|
|
local active_only=false
|
|
local output_json=false
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "${1:-}" in
|
|
--status) filter_status="${2:-}"; shift 2 ;;
|
|
--active) active_only=true; shift ;;
|
|
--json) output_json=true; shift ;;
|
|
*) shift ;;
|
|
esac
|
|
done
|
|
|
|
local endpoint="/sprints"
|
|
local query_params=()
|
|
if [[ -n "$filter_status" ]]; then
|
|
query_params+=("status=$(urlencode "$filter_status")")
|
|
fi
|
|
if [[ "$active_only" == true ]]; then
|
|
local today
|
|
today=$(date +%Y-%m-%d)
|
|
query_params+=("inProgress=true")
|
|
query_params+=("onDate=$(urlencode "$today")")
|
|
fi
|
|
if [[ ${#query_params[@]} -gt 0 ]]; then
|
|
endpoint+="?$(IFS='&'; echo "${query_params[*]}")"
|
|
fi
|
|
|
|
local response
|
|
response=$(api_call GET "$endpoint")
|
|
|
|
local sprints_json
|
|
sprints_json=$(echo "$response" | jq '.sprints')
|
|
|
|
if [[ "$output_json" == true ]]; then
|
|
echo "$sprints_json" | jq .
|
|
return
|
|
fi
|
|
|
|
local count
|
|
count=$(echo "$sprints_json" | jq 'length')
|
|
log_success "Found $count sprint(s)"
|
|
|
|
printf "%-36s %-25s %-10s %-12s %-12s\n" "ID" "NAME" "STATUS" "START" "END"
|
|
printf "%-36s %-25s %-10s %-12s %-12s\n" "------------------------------------" "-------------------------" "----------" "------------" "------------"
|
|
|
|
echo "$sprints_json" | jq -r '.[] | [.id, (.name // "" | tostring | .[0:23]), (.status // "N/A"), (.start_date // .startDate // "N/A"), (.end_date // .endDate // "N/A")] | @tsv' \
|
|
| while IFS=$'\t' read -r id name status start end; do
|
|
printf "%-36s %-25s %-10s %-12s %-12s\n" "$id" "$name" "$status" "$start" "$end"
|
|
done
|
|
}
|
|
|
|
cmd_get() {
|
|
local identifier="${1:-}"
|
|
if [[ -z "$identifier" ]]; then
|
|
log_error "Sprint ID or name required"
|
|
exit 1
|
|
fi
|
|
|
|
local sprint_id
|
|
sprint_id=$(resolve_sprint_id "$identifier")
|
|
|
|
log_info "Fetching sprint $sprint_id..."
|
|
api_call GET "/sprints/$sprint_id" | jq '.sprint'
|
|
}
|
|
|
|
cmd_update() {
|
|
local identifier="${1:-}"
|
|
shift || true
|
|
|
|
if [[ -z "$identifier" ]]; then
|
|
log_error "Sprint ID or name required"
|
|
exit 1
|
|
fi
|
|
|
|
local sprint_id
|
|
sprint_id=$(resolve_sprint_id "$identifier")
|
|
|
|
local updates='{}'
|
|
while [[ $# -gt 0 ]]; do
|
|
case "${1:-}" in
|
|
--name) updates=$(echo "$updates" | jq --arg v "${2:-}" '. + {name: $v}'); shift 2 ;;
|
|
--goal) updates=$(echo "$updates" | jq --arg v "${2:-}" '. + {goal: $v}'); shift 2 ;;
|
|
--start-date) updates=$(echo "$updates" | jq --arg v "${2:-}" '. + {startDate: $v}'); shift 2 ;;
|
|
--end-date) updates=$(echo "$updates" | jq --arg v "${2:-}" '. + {endDate: $v}'); shift 2 ;;
|
|
--status) updates=$(echo "$updates" | jq --arg v "${2:-}" '. + {status: $v}'); shift 2 ;;
|
|
*) shift ;;
|
|
esac
|
|
done
|
|
|
|
if [[ "$updates" == "{}" ]]; then
|
|
log_warning "No update fields specified"
|
|
exit 0
|
|
fi
|
|
|
|
local payload
|
|
payload=$(echo "$updates" | jq --arg id "$sprint_id" '. + {id: $id}')
|
|
|
|
log_info "Updating sprint $sprint_id..."
|
|
api_call PATCH "/sprints" "$payload" | jq .
|
|
}
|
|
|
|
cmd_close() {
|
|
local identifier="${1:-}"
|
|
if [[ -z "$identifier" ]]; then
|
|
log_error "Sprint ID or name required"
|
|
exit 1
|
|
fi
|
|
|
|
local sprint_id
|
|
sprint_id=$(resolve_sprint_id "$identifier")
|
|
|
|
log_info "Closing sprint $sprint_id..."
|
|
api_call POST "/sprints/close" "{\"id\": \"$sprint_id\"}" | jq .
|
|
}
|
|
|
|
cmd_delete() {
|
|
local identifier="${1:-}"
|
|
if [[ -z "$identifier" ]]; then
|
|
log_error "Sprint ID or name required"
|
|
exit 1
|
|
fi
|
|
|
|
local sprint_id
|
|
sprint_id=$(resolve_sprint_id "$identifier")
|
|
|
|
log_info "Deleting sprint $sprint_id..."
|
|
api_call DELETE "/sprints" "{\"id\": \"$sprint_id\"}" | jq .
|
|
}
|
|
|
|
check_dependencies
|
|
|
|
COMMAND="${1:-}"
|
|
shift || true
|
|
|
|
case "$COMMAND" in
|
|
create) cmd_create "$@" ;;
|
|
list) cmd_list "$@" ;;
|
|
get) cmd_get "$@" ;;
|
|
update) cmd_update "$@" ;;
|
|
close) cmd_close "$@" ;;
|
|
delete) cmd_delete "$@" ;;
|
|
help|--help|-h|"") show_usage ;;
|
|
*)
|
|
log_error "Unknown command: $COMMAND"
|
|
show_usage
|
|
exit 1
|
|
;;
|
|
esac
|