Skip to content

Commit a17d74b

Browse files
committed
add claude skills to create the maestro cluster
Signed-off-by: hchenxa <huichen@redhat.com>
1 parent 9f44c62 commit a17d74b

File tree

3 files changed

+532
-0
lines changed

3 files changed

+532
-0
lines changed
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
#!/bin/bash
2+
#
3+
# Deployment Monitor Hook
4+
# This hook monitors long-running deployment processes and notifies when complete
5+
#
6+
# Usage: Can be called after triggering a deployment to monitor its progress
7+
#
8+
# Dependencies:
9+
# - Required: bash, wc, tail, sed, grep, cat, tr, date, sleep
10+
# - Optional: curl (for Slack notifications), osascript (for macOS notifications), notify-send (for Linux notifications)
11+
#
12+
# Configuration:
13+
# Set SLACK_WEBHOOK_URL environment variable for Slack notifications
14+
15+
set -e
16+
17+
HOOK_NAME="deployment-monitor"
18+
19+
# Configuration is loaded from environment variables:
20+
# - SLACK_WEBHOOK_URL: Optional Slack webhook URL for notifications
21+
22+
# Function to check if required commands are available
23+
check_command() {
24+
local cmd=$1
25+
local required=$2
26+
27+
if ! command -v "$cmd" &> /dev/null; then
28+
if [ "$required" = "true" ]; then
29+
echo "[$HOOK_NAME] ERROR: Required command '$cmd' is not installed"
30+
return 1
31+
else
32+
echo "[$HOOK_NAME] WARNING: Optional command '$cmd' is not installed"
33+
return 0
34+
fi
35+
fi
36+
return 0
37+
}
38+
39+
# Function to monitor deployment
40+
monitor_deployment() {
41+
local task_id=$1
42+
43+
if [ -z "$task_id" ]; then
44+
echo "[$HOOK_NAME] ERROR: Task ID required"
45+
echo "Usage: $0 monitor <task_id>"
46+
exit 1
47+
fi
48+
49+
# Build task output paths dynamically based on current working directory
50+
local cwd_sanitized
51+
cwd_sanitized=$(pwd | tr '/' '-' | sed 's/^-//')
52+
local task_dir="/tmp/claude/-${cwd_sanitized}/tasks"
53+
54+
# Ensure task directory exists
55+
if [ ! -d "$task_dir" ]; then
56+
echo "[$HOOK_NAME] WARNING: Task directory does not exist: $task_dir"
57+
echo "[$HOOK_NAME] Creating directory..."
58+
mkdir -p "$task_dir"
59+
fi
60+
61+
local output_file="${task_dir}/${task_id}.output"
62+
local exit_code_file="${task_dir}/${task_id}.exit_code"
63+
local start_time
64+
start_time=$(date +%s)
65+
66+
echo "[$HOOK_NAME] Monitoring deployment task: $task_id"
67+
echo "[$HOOK_NAME] Started at: $(date)"
68+
echo ""
69+
70+
# Wait for the deployment to complete
71+
local last_line_count=0
72+
local max_wait_seconds=${MONITOR_TIMEOUT:-7200} # Default 2 hours
73+
while true; do
74+
# Check for timeout
75+
local elapsed=$(($(date +%s) - start_time))
76+
if [ "$elapsed" -ge "$max_wait_seconds" ]; then
77+
local minutes=$((max_wait_seconds / 60))
78+
echo ""
79+
echo "[$HOOK_NAME] ERROR: Maximum wait time (${minutes} minutes) reached"
80+
notify_completion "FAILED" "Deployment monitoring timed out after ${minutes} minutes without completion"
81+
return 2
82+
fi
83+
84+
# Check if exit code file exists (task completed)
85+
if [ -f "$exit_code_file" ]; then
86+
local exit_code
87+
exit_code=$(cat "$exit_code_file")
88+
echo "[$HOOK_NAME] Deployment process finished with exit code: $exit_code"
89+
break
90+
fi
91+
92+
# Show progress if output file exists
93+
if [ -f "$output_file" ]; then
94+
local current_lines
95+
current_lines=$(wc -l < "$output_file" | tr -d ' ')
96+
if [ "$current_lines" != "$last_line_count" ]; then
97+
local elapsed=$(($(date +%s) - start_time))
98+
local minutes=$((elapsed / 60))
99+
local seconds=$((elapsed % 60))
100+
echo "[$HOOK_NAME] Progress: $current_lines lines | Elapsed: ${minutes}m ${seconds}s | $(date +%H:%M:%S)"
101+
102+
# Show latest activity
103+
tail -3 "$output_file" | sed 's/\x1b\[[0-9;]*m//g' | grep -v "^$" | tail -1 | sed "s/^/[$HOOK_NAME] Latest: /"
104+
105+
last_line_count=$current_lines
106+
fi
107+
fi
108+
109+
# Sleep before next check
110+
sleep 15
111+
done
112+
113+
# Calculate total time
114+
local end_time
115+
end_time=$(date +%s)
116+
local total_time=$((end_time - start_time))
117+
local minutes=$((total_time / 60))
118+
local seconds=$((total_time % 60))
119+
120+
# Determine status and send notification
121+
if [ "$exit_code" -eq 0 ]; then
122+
notify_completion "COMPLETE" "Maestro cluster deployment completed successfully in ${minutes}m ${seconds}s!"
123+
echo ""
124+
echo "[$HOOK_NAME] Total deployment time: ${minutes}m ${seconds}s"
125+
echo "[$HOOK_NAME] Output file: $output_file"
126+
return 0
127+
else
128+
notify_completion "FAILED" "Deployment failed with exit code $exit_code after ${minutes}m ${seconds}s"
129+
echo ""
130+
echo "[$HOOK_NAME] Total deployment time: ${minutes}m ${seconds}s"
131+
echo "[$HOOK_NAME] Output file: $output_file"
132+
return 1
133+
fi
134+
}
135+
136+
# Function to send Slack notification
137+
send_slack_notification() {
138+
local status=$1
139+
local message=$2
140+
local webhook_url=$3
141+
142+
if [ -z "$webhook_url" ]; then
143+
return 1
144+
fi
145+
146+
# Check if curl is available
147+
if ! check_command "curl" "false"; then
148+
echo "[$HOOK_NAME] Skipping Slack notification - curl not available"
149+
return 1
150+
fi
151+
152+
# Determine color based on status
153+
local color="good"
154+
local emoji=":white_check_mark:"
155+
if [[ "$status" == "FAILED" ]]; then
156+
color="danger"
157+
emoji=":x:"
158+
elif [[ "$status" == "COMPLETE" ]]; then
159+
color="good"
160+
emoji=":white_check_mark:"
161+
fi
162+
163+
# Create JSON payload using jq for safe escaping
164+
local payload
165+
if command -v jq &> /dev/null; then
166+
# Use jq for safe JSON construction
167+
payload=$(jq -n \
168+
--arg color "$color" \
169+
--arg title "$emoji Maestro Deployment $status" \
170+
--arg text "$message" \
171+
--arg footer "Maestro Deployment Monitor" \
172+
--argjson ts "$(date +%s)" \
173+
'{attachments: [{color: $color, title: $title, text: $text, footer: $footer, ts: $ts}]}')
174+
else
175+
# Fallback: manually escape quotes and backslashes
176+
local escaped_message="${message//\\/\\\\}"
177+
escaped_message="${escaped_message//\"/\\\"}"
178+
local escaped_status="${status//\\/\\\\}"
179+
escaped_status="${escaped_status//\"/\\\"}"
180+
payload=$(cat <<EOF
181+
{
182+
"attachments": [
183+
{
184+
"color": "$color",
185+
"title": "$emoji Maestro Deployment $escaped_status",
186+
"text": "$escaped_message",
187+
"footer": "Maestro Deployment Monitor",
188+
"ts": $(date +%s)
189+
}
190+
]
191+
}
192+
EOF
193+
)
194+
fi
195+
196+
# Send to Slack and capture exit status
197+
# --fail ensures curl returns non-zero on HTTP 4xx/5xx errors
198+
local curl_exit_code
199+
if curl -X POST -H 'Content-type: application/json' \
200+
--data "$payload" \
201+
"$webhook_url" \
202+
--silent --show-error --fail; then
203+
curl_exit_code=0
204+
else
205+
curl_exit_code=$?
206+
echo "[$HOOK_NAME] ERROR: Failed to send Slack notification (curl exit code: $curl_exit_code)"
207+
fi
208+
209+
return $curl_exit_code
210+
}
211+
212+
# Function to send notification
213+
notify_completion() {
214+
local status=$1
215+
local message=$2
216+
217+
echo ""
218+
echo "=========================================="
219+
echo "[$HOOK_NAME] DEPLOYMENT $status"
220+
echo "Message: $message"
221+
echo "Time: $(date)"
222+
echo "=========================================="
223+
echo ""
224+
225+
# Send Slack notification if webhook is configured
226+
if [ -n "$SLACK_WEBHOOK_URL" ]; then
227+
echo "[$HOOK_NAME] Sending Slack notification..."
228+
if send_slack_notification "$status" "$message" "$SLACK_WEBHOOK_URL"; then
229+
echo "[$HOOK_NAME] Slack notification sent successfully"
230+
else
231+
echo "[$HOOK_NAME] Failed to send Slack notification"
232+
fi
233+
fi
234+
235+
# Also send system notification if available
236+
if command -v osascript &> /dev/null; then
237+
# macOS notification - escape message for AppleScript
238+
local safe_message="${message//\\/\\\\}"
239+
safe_message="${safe_message//\"/\\\"}"
240+
osascript -e "display notification \"$safe_message\" with title \"Maestro Deployment $status\""
241+
elif command -v notify-send &> /dev/null; then
242+
# Linux notification - use safe argument passing
243+
notify-send -- "Maestro Deployment $status" "$message"
244+
fi
245+
}
246+
247+
# Main execution
248+
case "${1:-notify}" in
249+
monitor)
250+
monitor_deployment "$2"
251+
exit $?
252+
;;
253+
notify)
254+
notify_completion "${2:-COMPLETE}" "${3:-Deployment finished}"
255+
;;
256+
*)
257+
echo "Usage: $0 {monitor <task_id>|notify <status> <message>}"
258+
exit 1
259+
;;
260+
esac

.claude/skills/README.md

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Maestro Claude Skills
2+
3+
This directory contains custom Claude Code skills for Maestro development and operations.
4+
5+
## Available Skills
6+
7+
### setup-maestro-cluster (SKILL.md)
8+
9+
Sets up a long-running Maestro cluster environment using Azure ARO-HCP infrastructure.
10+
11+
**Usage:**
12+
```bash
13+
/setup-maestro-cluster
14+
```
15+
16+
**What it does:**
17+
1. Verifies Azure CLI installation and login status
18+
2. Checks that you're logged into the "ARO Hosted Control Planes" Azure account
19+
3. Clones the ARO-HCP repository to a temporary location
20+
4. Sets required environment variables (USER, PERSIST, GITHUB_ACTIONS, GOTOOLCHAIN)
21+
5. Runs `make personal-dev-env` to deploy the environment
22+
6. Monitors and reports deployment status
23+
24+
**Prerequisites:**
25+
- Azure CLI installed (`brew install azure-cli` on macOS)
26+
- Logged into correct Azure account: `az login`
27+
- Valid Azure permissions for resource creation
28+
29+
**Environment Variables Set:**
30+
- `USER=oasis`
31+
- `PERSIST=true`
32+
- `GITHUB_ACTIONS=true`
33+
- `GOTOOLCHAIN=go1.24.4`
34+
35+
## Hooks
36+
37+
### deployment-monitor.sh
38+
39+
A hook that monitors long-running deployment processes and sends notifications.
40+
41+
**Features:**
42+
- Desktop notifications (macOS/Linux)
43+
- Slack notifications via webhook
44+
- Customizable status messages
45+
- Real-time deployment monitoring
46+
47+
**Dependencies:**
48+
- Required: `bash`, `wc`, `tail`, `sed`, `grep`, `cat`, `tr`, `date`, `sleep` (standard Unix tools)
49+
- Optional: `curl` (for Slack notifications), `osascript` (for macOS notifications), `notify-send` (for Linux notifications)
50+
51+
**Configuration:**
52+
53+
To enable Slack notifications:
54+
55+
1. Create a Slack webhook:
56+
- Go to <https://api.slack.com/messaging/webhooks>
57+
- Create an Incoming Webhook for your channel
58+
- Copy the webhook URL
59+
60+
2. Set the webhook URL as an environment variable:
61+
```bash
62+
export SLACK_WEBHOOK_URL="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
63+
```
64+
65+
**Usage:**
66+
```bash
67+
# Monitor a deployment task in real-time
68+
.claude/hooks/deployment-monitor.sh monitor <task_id>
69+
70+
# Example:
71+
.claude/hooks/deployment-monitor.sh monitor b4ac6c1
72+
73+
# Send a manual completion notification
74+
.claude/hooks/deployment-monitor.sh notify "COMPLETE" "Deployment finished successfully"
75+
76+
# Send a failure notification
77+
.claude/hooks/deployment-monitor.sh notify "FAILED" "Deployment failed with errors"
78+
```
79+
80+
**What the monitor does:**
81+
1. Tracks the deployment task by its task ID
82+
2. Shows real-time progress updates (line count, elapsed time)
83+
3. Displays the latest deployment activity
84+
4. Detects when the task completes
85+
5. Automatically sends notifications (Slack + desktop) when done
86+
6. Reports final status and deployment duration
87+
88+
The hook will:
89+
1. Send a Slack notification (if configured) with color-coded messages
90+
2. Send desktop notifications on macOS (via osascript) or Linux (via notify-send)
91+
92+
## How Skills Work
93+
94+
Skills are invoked in Claude Code using the `/` prefix followed by the skill name. When you run a skill:
95+
96+
1. Claude Code reads the skill markdown file
97+
2. Executes the bash script in the Implementation section
98+
3. Returns the output to you in the chat
99+
100+
Skills are a powerful way to automate complex, multi-step workflows that you perform frequently.
101+
102+
## Creating New Skills
103+
104+
To create a new skill:
105+
106+
1. Create a new `.md` file in `.claude/skills/`
107+
2. Include these sections:
108+
- Title and description
109+
- Prerequisites
110+
- Usage example
111+
- Implementation (bash script in a code block)
112+
3. Make sure the bash script is well-commented and handles errors
113+
114+
See `SKILL.md` as an example.

0 commit comments

Comments
 (0)