232 lines
5.3 KiB
Bash
Executable File
232 lines
5.3 KiB
Bash
Executable File
#!/bin/bash
|
|
# Project CLI for Gantt Board (API passthrough)
|
|
# Usage: ./project.sh [create|list|get|update|delete] [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'
|
|
Project CLI for Gantt Board
|
|
|
|
USAGE:
|
|
./project.sh [COMMAND] [OPTIONS]
|
|
|
|
COMMANDS:
|
|
create Create a new project
|
|
list List all projects
|
|
get <id-or-name> Get a specific project
|
|
update <id-or-name> Update a project
|
|
delete <id-or-name> Delete a project
|
|
|
|
CREATE OPTIONS:
|
|
--name "Name" Project name (required)
|
|
--description "Description" Project description
|
|
--color "#hexcolor" Project color
|
|
|
|
LIST OPTIONS:
|
|
--json Output as JSON
|
|
|
|
UPDATE OPTIONS:
|
|
--name "Name"
|
|
--description "Description"
|
|
--color "#hexcolor"
|
|
USAGE
|
|
}
|
|
|
|
resolve_project_id() {
|
|
local identifier="$1"
|
|
|
|
if [[ "$identifier" =~ $UUID_PATTERN ]]; then
|
|
echo "$identifier"
|
|
return 0
|
|
fi
|
|
|
|
local response
|
|
response=$(api_call GET "/projects")
|
|
|
|
local project_id
|
|
project_id=$(echo "$response" | jq -r --arg q "$identifier" '
|
|
.projects
|
|
| map(select((.name // "") | ascii_downcase | contains($q | ascii_downcase)))
|
|
| .[0].id // empty
|
|
')
|
|
|
|
if [[ -n "$project_id" ]]; then
|
|
echo "$project_id"
|
|
return 0
|
|
fi
|
|
|
|
log_error "Project '$identifier' not found"
|
|
return 1
|
|
}
|
|
|
|
cmd_create() {
|
|
local name=""
|
|
local description=""
|
|
local color="#3b82f6"
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "${1:-}" in
|
|
--name) name="${2:-}"; shift 2 ;;
|
|
--description) description="${2:-}"; shift 2 ;;
|
|
--color) color="${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 description "$description" --arg color "$color" '
|
|
{
|
|
name: $name,
|
|
description: (if $description == "" then null else $description end),
|
|
color: $color
|
|
}
|
|
')
|
|
|
|
log_info "Creating project..."
|
|
api_call POST "/projects" "$payload" | jq .
|
|
}
|
|
|
|
cmd_list() {
|
|
local output_json=false
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "${1:-}" in
|
|
--json) output_json=true; shift ;;
|
|
*) shift ;;
|
|
esac
|
|
done
|
|
|
|
local response
|
|
response=$(api_call GET "/projects")
|
|
|
|
if [[ "$output_json" == true ]]; then
|
|
echo "$response" | jq '.projects'
|
|
return
|
|
fi
|
|
|
|
local projects
|
|
projects=$(echo "$response" | jq '.projects')
|
|
local count
|
|
count=$(echo "$projects" | jq 'length')
|
|
log_success "Found $count project(s)"
|
|
|
|
printf "%-36s %-25s %-30s\n" "ID" "NAME" "DESCRIPTION"
|
|
printf "%-36s %-25s %-30s\n" "------------------------------------" "-------------------------" "------------------------------"
|
|
|
|
echo "$projects" | jq -r '.[] | [.id, (.name // "" | tostring | .[0:23]), (.description // "" | tostring | .[0:28])] | @tsv' \
|
|
| while IFS=$'\t' read -r id name desc; do
|
|
printf "%-36s %-25s %-30s\n" "$id" "$name" "$desc"
|
|
done
|
|
}
|
|
|
|
cmd_get() {
|
|
local identifier="${1:-}"
|
|
if [[ -z "$identifier" ]]; then
|
|
log_error "Project ID or name required"
|
|
exit 1
|
|
fi
|
|
|
|
local project_id
|
|
project_id=$(resolve_project_id "$identifier")
|
|
|
|
log_info "Fetching project $project_id..."
|
|
api_call GET "/projects/$project_id" | jq '.project'
|
|
}
|
|
|
|
cmd_update() {
|
|
local identifier="${1:-}"
|
|
shift || true
|
|
|
|
if [[ -z "$identifier" ]]; then
|
|
log_error "Project ID or name required"
|
|
exit 1
|
|
fi
|
|
|
|
local project_id
|
|
project_id=$(resolve_project_id "$identifier")
|
|
|
|
local updates='{}'
|
|
while [[ $# -gt 0 ]]; do
|
|
case "${1:-}" in
|
|
--name) updates=$(echo "$updates" | jq --arg v "${2:-}" '. + {name: $v}'); shift 2 ;;
|
|
--description) updates=$(echo "$updates" | jq --arg v "${2:-}" '. + {description: $v}'); shift 2 ;;
|
|
--color) updates=$(echo "$updates" | jq --arg v "${2:-}" '. + {color: $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 "$project_id" '. + {id: $id}')
|
|
|
|
log_info "Updating project $project_id..."
|
|
api_call PATCH "/projects" "$payload" | jq .
|
|
}
|
|
|
|
cmd_delete() {
|
|
local identifier="${1:-}"
|
|
if [[ -z "$identifier" ]]; then
|
|
log_error "Project ID or name required"
|
|
exit 1
|
|
fi
|
|
|
|
local project_id
|
|
project_id=$(resolve_project_id "$identifier")
|
|
|
|
log_info "Deleting project $project_id..."
|
|
api_call DELETE "/projects" "{\"id\": \"$project_id\"}" | jq .
|
|
}
|
|
|
|
check_dependencies
|
|
|
|
COMMAND="${1:-}"
|
|
shift || true
|
|
|
|
case "$COMMAND" in
|
|
create) cmd_create "$@" ;;
|
|
list) cmd_list "$@" ;;
|
|
get) cmd_get "$@" ;;
|
|
update) cmd_update "$@" ;;
|
|
delete) cmd_delete "$@" ;;
|
|
help|--help|-h|"") show_usage ;;
|
|
*)
|
|
log_error "Unknown command: $COMMAND"
|
|
show_usage
|
|
exit 1
|
|
;;
|
|
esac
|