Add branch lifecycle commands (branch, merge, archive) to the SDLC CLI. Introduce orchestrator handler and service for multi-step SDLC workflows. Expand skeleton template with 15 Claude commands covering the full feature lifecycle. Extend classifier rules, error types, and executor port for branch operations. Split rules.go and classifier_test.go to stay within 500-line limit. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
284 lines
8.9 KiB
Bash
Executable File
284 lines
8.9 KiB
Bash
Executable File
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
# SDLC Orchestration E2E Test Script
|
|
# Tests the full SDLC lifecycle through the rdev API.
|
|
#
|
|
# Flow:
|
|
# 1. Get SDLC state (verify endpoint works)
|
|
# 2. Create feature
|
|
# 3. Transition through phases with artifact checks
|
|
# 4. Branch, merge, archive
|
|
#
|
|
# Usage:
|
|
# ./cookbooks/scripts/sdlc-test.sh run [project-id] # Run the full flow
|
|
# ./cookbooks/scripts/sdlc-test.sh status [project-id] # Show SDLC state
|
|
# ./cookbooks/scripts/sdlc-test.sh teardown [project-id] # Clean up test feature
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
source "$SCRIPT_DIR/common.sh"
|
|
|
|
COMMAND="${1:-}"
|
|
PROJECT_ID="${2:-}"
|
|
FEATURE_SLUG="sdlc-test-$(date +%s)"
|
|
|
|
if [[ -z "$COMMAND" ]]; then
|
|
echo "SDLC Orchestration E2E Test Script"
|
|
echo ""
|
|
echo "Usage: $0 <command> <project-id>"
|
|
echo ""
|
|
echo "Commands:"
|
|
echo " run - Run the full SDLC lifecycle flow"
|
|
echo " status - Show SDLC state for a project"
|
|
echo " teardown - Delete test feature from project"
|
|
echo ""
|
|
echo "Examples:"
|
|
echo " $0 run my-project"
|
|
echo " $0 status my-project"
|
|
echo " $0 teardown my-project"
|
|
echo ""
|
|
echo "Requires: RDEV_API_URL, RDEV_API_KEY, and an existing project with SDLC initialized"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -z "$PROJECT_ID" ]]; then
|
|
echo "Error: project-id is required"
|
|
exit 1
|
|
fi
|
|
|
|
# Helper: check response for error
|
|
check_response() {
|
|
local response="$1"
|
|
local step="$2"
|
|
|
|
local error
|
|
error=$(echo "$response" | jq -r '.error // ""')
|
|
if [[ -n "$error" && "$error" != "" && "$error" != "null" ]]; then
|
|
print_error "$step failed: $error"
|
|
echo "$response" | jq '.'
|
|
return 1
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
# Main run flow
|
|
run_flow() {
|
|
print_header "SDLC E2E Test"
|
|
echo "Project: $PROJECT_ID"
|
|
echo "Feature: $FEATURE_SLUG"
|
|
|
|
# Step 1: Get SDLC state
|
|
print_header "Step 1: Get SDLC State"
|
|
local state_response
|
|
state_response=$(api_call GET "/projects/$PROJECT_ID/sdlc/state")
|
|
if check_response "$state_response" "Get state"; then
|
|
print_success "SDLC state retrieved"
|
|
echo "$state_response" | jq '.data.version // .data'
|
|
else
|
|
print_error "Failed to get SDLC state - is SDLC initialized for this project?"
|
|
return 1
|
|
fi
|
|
|
|
# Step 2: Create feature
|
|
print_header "Step 2: Create Feature"
|
|
local create_response
|
|
create_response=$(api_call POST "/projects/$PROJECT_ID/sdlc/features" \
|
|
"{\"slug\": \"$FEATURE_SLUG\", \"title\": \"SDLC E2E Test Feature\"}")
|
|
if check_response "$create_response" "Create feature"; then
|
|
print_success "Feature created: $FEATURE_SLUG"
|
|
else
|
|
return 1
|
|
fi
|
|
|
|
# Step 3: List features
|
|
print_header "Step 3: List Features"
|
|
local list_response
|
|
list_response=$(api_call GET "/projects/$PROJECT_ID/sdlc/features")
|
|
if check_response "$list_response" "List features"; then
|
|
local count
|
|
count=$(echo "$list_response" | jq '.data | length')
|
|
print_success "Listed $count features"
|
|
fi
|
|
|
|
# Step 4: Get feature detail
|
|
print_header "Step 4: Get Feature Detail"
|
|
local detail_response
|
|
detail_response=$(api_call GET "/projects/$PROJECT_ID/sdlc/features/$FEATURE_SLUG")
|
|
if check_response "$detail_response" "Get feature"; then
|
|
local phase
|
|
phase=$(echo "$detail_response" | jq -r '.data.phase // "unknown"')
|
|
print_success "Feature phase: $phase"
|
|
fi
|
|
|
|
# Step 5: Get classifier recommendation
|
|
print_header "Step 5: Get Classifier Recommendation"
|
|
local next_response
|
|
next_response=$(api_call GET "/projects/$PROJECT_ID/sdlc/next?feature=$FEATURE_SLUG")
|
|
if check_response "$next_response" "Get next"; then
|
|
local action
|
|
action=$(echo "$next_response" | jq -r '.data.action // "unknown"')
|
|
local message
|
|
message=$(echo "$next_response" | jq -r '.data.message // ""')
|
|
print_success "Next action: $action"
|
|
echo " Message: $message"
|
|
fi
|
|
|
|
# Step 6: Check artifact status
|
|
print_header "Step 6: Check Artifact Status"
|
|
local artifacts_response
|
|
artifacts_response=$(api_call GET "/projects/$PROJECT_ID/sdlc/features/$FEATURE_SLUG/artifacts")
|
|
if check_response "$artifacts_response" "Get artifacts"; then
|
|
print_success "Artifact status retrieved"
|
|
fi
|
|
|
|
# Step 7: Add a task
|
|
print_header "Step 7: Add Task"
|
|
local task_response
|
|
task_response=$(api_call POST "/projects/$PROJECT_ID/sdlc/features/$FEATURE_SLUG/tasks" \
|
|
'{"title": "Test task for E2E validation"}')
|
|
if check_response "$task_response" "Add task"; then
|
|
local task_id
|
|
task_id=$(echo "$task_response" | jq -r '.data.id // "unknown"')
|
|
print_success "Task added: $task_id"
|
|
|
|
# Step 7b: List tasks
|
|
local tasks_response
|
|
tasks_response=$(api_call GET "/projects/$PROJECT_ID/sdlc/features/$FEATURE_SLUG/tasks")
|
|
if check_response "$tasks_response" "List tasks"; then
|
|
local task_count
|
|
task_count=$(echo "$tasks_response" | jq '.data | length')
|
|
print_success "Listed $task_count tasks"
|
|
fi
|
|
fi
|
|
|
|
# Step 8: Block and unblock feature
|
|
print_header "Step 8: Block/Unblock Feature"
|
|
local block_response
|
|
block_response=$(api_call POST "/projects/$PROJECT_ID/sdlc/features/$FEATURE_SLUG/block" \
|
|
'{"reason": "E2E test blocker"}')
|
|
if check_response "$block_response" "Block feature"; then
|
|
print_success "Feature blocked"
|
|
fi
|
|
|
|
# Query blocked
|
|
local blocked_response
|
|
blocked_response=$(api_call GET "/projects/$PROJECT_ID/sdlc/query/blocked")
|
|
if check_response "$blocked_response" "Query blocked"; then
|
|
local blocked_count
|
|
blocked_count=$(echo "$blocked_response" | jq '.data | length')
|
|
print_success "Blocked features: $blocked_count"
|
|
fi
|
|
|
|
local unblock_response
|
|
unblock_response=$(api_call POST "/projects/$PROJECT_ID/sdlc/features/$FEATURE_SLUG/unblock")
|
|
if check_response "$unblock_response" "Unblock feature"; then
|
|
print_success "Feature unblocked"
|
|
fi
|
|
|
|
# Step 9: Query endpoints
|
|
print_header "Step 9: Query Endpoints"
|
|
|
|
local ready_response
|
|
ready_response=$(api_call GET "/projects/$PROJECT_ID/sdlc/query/ready")
|
|
if check_response "$ready_response" "Query ready"; then
|
|
print_success "Query ready: OK"
|
|
fi
|
|
|
|
local approval_response
|
|
approval_response=$(api_call GET "/projects/$PROJECT_ID/sdlc/query/needs-approval")
|
|
if check_response "$approval_response" "Query needs-approval"; then
|
|
print_success "Query needs-approval: OK"
|
|
fi
|
|
|
|
# Step 10: Clean up - delete the test feature
|
|
print_header "Step 10: Cleanup"
|
|
local delete_response
|
|
delete_response=$(api_call DELETE "/projects/$PROJECT_ID/sdlc/features/$FEATURE_SLUG")
|
|
print_success "Feature deleted: $FEATURE_SLUG"
|
|
|
|
# Summary
|
|
print_header "Results"
|
|
print_success "All SDLC E2E tests passed!"
|
|
echo ""
|
|
echo " Tested endpoints:"
|
|
echo " GET /sdlc/state"
|
|
echo " GET /sdlc/next"
|
|
echo " GET /sdlc/features"
|
|
echo " POST /sdlc/features"
|
|
echo " GET /sdlc/features/{slug}"
|
|
echo " GET /sdlc/features/{slug}/artifacts"
|
|
echo " POST /sdlc/features/{slug}/tasks"
|
|
echo " GET /sdlc/features/{slug}/tasks"
|
|
echo " POST /sdlc/features/{slug}/block"
|
|
echo " POST /sdlc/features/{slug}/unblock"
|
|
echo " GET /sdlc/query/blocked"
|
|
echo " GET /sdlc/query/ready"
|
|
echo " GET /sdlc/query/needs-approval"
|
|
echo " DELETE /sdlc/features/{slug}"
|
|
}
|
|
|
|
# Show SDLC status
|
|
show_status() {
|
|
print_header "SDLC Status for $PROJECT_ID"
|
|
|
|
echo "State:"
|
|
api_call GET "/projects/$PROJECT_ID/sdlc/state" | jq '.data'
|
|
|
|
echo ""
|
|
echo "Features:"
|
|
api_call GET "/projects/$PROJECT_ID/sdlc/features" | jq '.data'
|
|
|
|
echo ""
|
|
echo "Blocked:"
|
|
api_call GET "/projects/$PROJECT_ID/sdlc/query/blocked" | jq '.data'
|
|
|
|
echo ""
|
|
echo "Needs Approval:"
|
|
api_call GET "/projects/$PROJECT_ID/sdlc/query/needs-approval" | jq '.data'
|
|
}
|
|
|
|
# Teardown test feature
|
|
teardown_flow() {
|
|
print_header "SDLC Teardown"
|
|
|
|
# List features to find test ones
|
|
local features
|
|
features=$(api_call GET "/projects/$PROJECT_ID/sdlc/features" | jq -r '.data[]?.slug // empty')
|
|
|
|
if [[ -z "$features" ]]; then
|
|
echo "No features found for project $PROJECT_ID"
|
|
return 0
|
|
fi
|
|
|
|
echo "Features:"
|
|
echo "$features"
|
|
echo ""
|
|
|
|
# Delete features matching test pattern
|
|
for slug in $features; do
|
|
if [[ "$slug" == sdlc-test-* ]]; then
|
|
echo "Deleting test feature: $slug"
|
|
api_call DELETE "/projects/$PROJECT_ID/sdlc/features/$slug"
|
|
print_success "Deleted: $slug"
|
|
fi
|
|
done
|
|
}
|
|
|
|
# Dispatch
|
|
case "$COMMAND" in
|
|
run)
|
|
run_flow
|
|
;;
|
|
status)
|
|
show_status
|
|
;;
|
|
teardown)
|
|
teardown_flow
|
|
;;
|
|
*)
|
|
echo "Unknown command: $COMMAND"
|
|
echo "Use: run, status, or teardown"
|
|
exit 1
|
|
;;
|
|
esac
|