Skip to content

Commit 02794db

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

File tree

3 files changed

+520
-0
lines changed

3 files changed

+520
-0
lines changed
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
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+
while true; do
73+
# Check if exit code file exists (task completed)
74+
if [ -f "$exit_code_file" ]; then
75+
local exit_code
76+
exit_code=$(cat "$exit_code_file")
77+
echo "[$HOOK_NAME] Deployment process finished with exit code: $exit_code"
78+
break
79+
fi
80+
81+
# Show progress if output file exists
82+
if [ -f "$output_file" ]; then
83+
local current_lines
84+
current_lines=$(wc -l < "$output_file" | tr -d ' ')
85+
if [ "$current_lines" != "$last_line_count" ]; then
86+
local elapsed=$(($(date +%s) - start_time))
87+
local minutes=$((elapsed / 60))
88+
local seconds=$((elapsed % 60))
89+
echo "[$HOOK_NAME] Progress: $current_lines lines | Elapsed: ${minutes}m ${seconds}s | $(date +%H:%M:%S)"
90+
91+
# Show latest activity
92+
tail -3 "$output_file" | sed 's/\x1b\[[0-9;]*m//g' | grep -v "^$" | tail -1 | sed "s/^/[$HOOK_NAME] Latest: /"
93+
94+
last_line_count=$current_lines
95+
fi
96+
fi
97+
98+
# Sleep before next check
99+
sleep 15
100+
done
101+
102+
# Calculate total time
103+
local end_time
104+
end_time=$(date +%s)
105+
local total_time=$((end_time - start_time))
106+
local minutes=$((total_time / 60))
107+
local seconds=$((total_time % 60))
108+
109+
# Determine status and send notification
110+
if [ "$exit_code" -eq 0 ]; then
111+
notify_completion "COMPLETE" "Maestro cluster deployment completed successfully in ${minutes}m ${seconds}s!"
112+
echo ""
113+
echo "[$HOOK_NAME] Total deployment time: ${minutes}m ${seconds}s"
114+
echo "[$HOOK_NAME] Output file: $output_file"
115+
return 0
116+
else
117+
notify_completion "FAILED" "Deployment failed with exit code $exit_code after ${minutes}m ${seconds}s"
118+
echo ""
119+
echo "[$HOOK_NAME] Total deployment time: ${minutes}m ${seconds}s"
120+
echo "[$HOOK_NAME] Output file: $output_file"
121+
return 1
122+
fi
123+
}
124+
125+
# Function to send Slack notification
126+
send_slack_notification() {
127+
local status=$1
128+
local message=$2
129+
local webhook_url=$3
130+
131+
if [ -z "$webhook_url" ]; then
132+
return 1
133+
fi
134+
135+
# Check if curl is available
136+
if ! check_command "curl" "false"; then
137+
echo "[$HOOK_NAME] Skipping Slack notification - curl not available"
138+
return 1
139+
fi
140+
141+
# Determine color based on status
142+
local color="good"
143+
local emoji=":white_check_mark:"
144+
if [[ "$status" == "FAILED" ]]; then
145+
color="danger"
146+
emoji=":x:"
147+
elif [[ "$status" == "COMPLETE" ]]; then
148+
color="good"
149+
emoji=":white_check_mark:"
150+
fi
151+
152+
# Create JSON payload using jq for safe escaping
153+
local payload
154+
if command -v jq &> /dev/null; then
155+
# Use jq for safe JSON construction
156+
payload=$(jq -n \
157+
--arg color "$color" \
158+
--arg title "$emoji Maestro Deployment $status" \
159+
--arg text "$message" \
160+
--arg footer "Maestro Deployment Monitor" \
161+
--argjson ts "$(date +%s)" \
162+
'{attachments: [{color: $color, title: $title, text: $text, footer: $footer, ts: $ts}]}')
163+
else
164+
# Fallback: manually escape quotes and backslashes
165+
local escaped_message="${message//\\/\\\\}"
166+
escaped_message="${escaped_message//\"/\\\"}"
167+
local escaped_status="${status//\\/\\\\}"
168+
escaped_status="${escaped_status//\"/\\\"}"
169+
payload=$(cat <<EOF
170+
{
171+
"attachments": [
172+
{
173+
"color": "$color",
174+
"title": "$emoji Maestro Deployment $escaped_status",
175+
"text": "$escaped_message",
176+
"footer": "Maestro Deployment Monitor",
177+
"ts": $(date +%s)
178+
}
179+
]
180+
}
181+
EOF
182+
)
183+
fi
184+
185+
# Send to Slack and capture exit status
186+
local curl_exit_code
187+
if curl -X POST -H 'Content-type: application/json' \
188+
--data "$payload" \
189+
"$webhook_url" \
190+
--silent --show-error; then
191+
curl_exit_code=0
192+
else
193+
curl_exit_code=$?
194+
echo "[$HOOK_NAME] ERROR: Failed to send Slack notification (curl exit code: $curl_exit_code)"
195+
fi
196+
197+
return $curl_exit_code
198+
}
199+
200+
# Function to send notification
201+
notify_completion() {
202+
local status=$1
203+
local message=$2
204+
205+
echo ""
206+
echo "=========================================="
207+
echo "[$HOOK_NAME] DEPLOYMENT $status"
208+
echo "Message: $message"
209+
echo "Time: $(date)"
210+
echo "=========================================="
211+
echo ""
212+
213+
# Send Slack notification if webhook is configured
214+
if [ -n "$SLACK_WEBHOOK_URL" ]; then
215+
echo "[$HOOK_NAME] Sending Slack notification..."
216+
if send_slack_notification "$status" "$message" "$SLACK_WEBHOOK_URL"; then
217+
echo "[$HOOK_NAME] Slack notification sent successfully"
218+
else
219+
echo "[$HOOK_NAME] Failed to send Slack notification"
220+
fi
221+
fi
222+
223+
# Also send system notification if available
224+
if command -v osascript &> /dev/null; then
225+
# macOS notification - escape message for AppleScript
226+
local safe_message="${message//\\/\\\\}"
227+
safe_message="${safe_message//\"/\\\"}"
228+
osascript -e "display notification \"$safe_message\" with title \"Maestro Deployment $status\""
229+
elif command -v notify-send &> /dev/null; then
230+
# Linux notification - use safe argument passing
231+
notify-send -- "Maestro Deployment $status" "$message"
232+
fi
233+
}
234+
235+
# Main execution
236+
case "${1:-notify}" in
237+
monitor)
238+
monitor_deployment "$2"
239+
exit $?
240+
;;
241+
notify)
242+
notify_completion "${2:-COMPLETE}" "${3:-Deployment finished}"
243+
;;
244+
*)
245+
echo "Usage: $0 {monitor <task_id>|notify <status> <message>}"
246+
exit 1
247+
;;
248+
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)