#!/bin/bash # Sprint Rollover Script # Sprint state is date-derived; this script only handles task rollover for ended sprints. set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/lib/api_client.sh" TODAY=$(date +%Y-%m-%d) LOG_FILE="${LOG_FILE:-/tmp/sprint-auto-status.log}" log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" } # Get current date in ISO format get_today() { date +%Y-%m-%d } # Get all sprints get_all_sprints() { api_call GET "/sprints" | jq -r '.sprints // []' } # Determine what status a sprint should have based on date get_sprint_target_status() { local start_date="$1" local end_date="$2" local today="${3:-$(get_today)}" if [[ "$today" < "$start_date" ]]; then echo "planning" elif [[ "$today" > "$end_date" ]]; then echo "completed" else echo "active" fi } # Get incomplete tasks for a sprint (status not in done, canceled, closed) get_incomplete_tasks() { local sprint_id="$1" api_call GET "/tasks?scope=all" | \ jq --arg sid "$sprint_id" ' .tasks | map(select( .sprintId == $sid and (.status != "done" and .status != "canceled" and .status != "closed" and .status != "archived") )) ' } # Get current active sprint (the one containing today) get_current_active_sprint() { local today="${1:-$(get_today)}" api_call GET "/sprints" | \ jq --arg today "$today" ' .sprints | map(select(.start_date <= $today and .end_date >= $today)) | .[0] ' } # Move task to new sprint move_task_to_sprint() { local task_id="$1" local new_sprint_id="$2" local old_sprint_name="$3" log "Moving task $task_id to sprint $new_sprint_id (from $old_sprint_name)" # Get existing task local task task=$(api_call GET "/tasks?taskId=$(urlencode "$task_id")&include=detail" | jq '.tasks[0]') if [[ -z "$task" || "$task" == "null" ]]; then log "✗ Task $task_id not found" return 1 fi # Update sprint ID and add comment local now now=$(date -u +"%Y-%m-%dT%H:%M:%SZ") local comment comment="Auto-rolled over from $old_sprint_name on $(get_today)" local updated_task updated_task=$(echo "$task" | \ jq --arg sid "$new_sprint_id" \ --arg comment "$comment" \ --arg createdAt "$now" \ --arg commentId "$(date +%s)-$RANDOM" ' .sprintId = $sid | .comments = (.comments // []) + [{ id: $commentId, text: $comment, createdAt: $createdAt, commentAuthorId: "system", replies: [] }] ') if api_call POST "/tasks" "{\"task\": $updated_task}" > /dev/null 2>&1; then log "✓ Successfully moved task $task_id" return 0 else log "✗ Failed to move task $task_id" return 1 fi } # Main function to process sprint updates process_sprint_updates() { log "=== Starting Sprint Rollover Run ===" log "Today: $(get_today)" # Get all sprints local sprints sprints=$(get_all_sprints) if [[ -z "$sprints" || "$sprints" == "[]" ]]; then log "No sprints found" return 0 fi local rollover_count=0 # Process each sprint while IFS= read -r sprint; do [[ -z "$sprint" ]] && continue local sprint_id local start_date local end_date local sprint_name sprint_id=$(echo "$sprint" | jq -r '.id // empty') start_date=$(echo "$sprint" | jq -r '.start_date // .startDate // empty') end_date=$(echo "$sprint" | jq -r '.end_date // .endDate // empty') sprint_name=$(echo "$sprint" | jq -r '.name // "Unnamed"') [[ -z "$sprint_id" ]] && continue # Determine target status local target_status target_status=$(get_sprint_target_status "$start_date" "$end_date") log "Sprint: $sprint_name (ID: $sprint_id) - Derived status: $target_status" # Rollover applies only to completed (ended) sprints. if [[ "$target_status" == "completed" ]]; then log "Sprint $sprint_name has ended - checking for tasks to roll over" # Get the new current sprint local current_sprint current_sprint=$(get_current_active_sprint) if [[ -n "$current_sprint" && "$current_sprint" != "null" ]]; then local new_sprint_id new_sprint_id=$(echo "$current_sprint" | jq -r '.id') # Get incomplete tasks local incomplete_tasks incomplete_tasks=$(get_incomplete_tasks "$sprint_id") local task_count task_count=$(echo "$incomplete_tasks" | jq 'length') if [[ "$task_count" -gt 0 ]]; then log "Found $task_count incomplete tasks to roll over" # Move each task while IFS= read -r task_id; do [[ -z "$task_id" ]] && continue if move_task_to_sprint "$task_id" "$new_sprint_id" "$sprint_name"; then ((rollover_count++)) fi done < <(echo "$incomplete_tasks" | jq -r '.[].id' 2>/dev/null) else log "No incomplete tasks found" fi else log "Warning: No current active sprint found for rollover" fi fi done < <(echo "$sprints" | jq -c '.[]' 2>/dev/null) log "=== Sprint Update Complete ===" log "Rolled over: $rollover_count tasks" return 0 } # Show help show_help() { cat << 'HELP' Sprint Rollover Script USAGE: ./sprint-auto-status.sh [command] COMMANDS: run Run the rollover process for ended sprints dry-run Show what would happen without making changes cleanup Clean up old log files help Show this help message ENVIRONMENT: LOG_FILE Path to log file (default: /tmp/sprint-auto-status.log) API_URL Gantt Board API URL (default: http://localhost:3000/api) EXAMPLES: ./sprint-auto-status.sh run LOG_FILE=/var/log/sprint-status.log ./sprint-auto-status.sh run HELP } # Main execution case "${1:-}" in run) process_sprint_updates ;; dry-run) log "=== DRY RUN MODE - No changes will be made ===" # In dry-run mode, we'd print what would happen log "Would check all sprints and derive state from dates on $(get_today)" log "Would roll over incomplete tasks from ended sprints" ;; cleanup) rm -f /tmp/sprint-auto-status.log log "Cleaned up log files" ;; help|--help|-h|"") show_help ;; *) log_error "Unknown command: $1" show_help exit 1 ;; esac