Skip to content

Commit 1862edb

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

File tree

5 files changed

+544
-0
lines changed

5 files changed

+544
-0
lines changed

.claude/hooks/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
config.sh

.claude/hooks/config.sh.example

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/bin/bash
2+
#
3+
# Deployment Monitor Hook Configuration
4+
#
5+
# Copy this file to config.sh and set your values:
6+
# cp config.sh.example config.sh
7+
#
8+
# Note: config.sh is gitignored to keep your webhook URL secure
9+
10+
# Slack Webhook URL for deployment notifications
11+
# Get this from: https://api.slack.com/messaging/webhooks
12+
# Example: https://hooks.slack.com/services/YOUR/WEBHOOK/URL
13+
SLACK_WEBHOOK_URL=""
14+
15+
# Optional: Customize notification behavior
16+
# NOTIFY_ON_START=false
17+
# NOTIFY_ON_PROGRESS=false
18+
# NOTIFY_ON_COMPLETE=true
19+
# NOTIFY_ON_FAILURE=true
Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
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 or create .claude/hooks/config.sh
14+
15+
set -e
16+
17+
HOOK_NAME="deployment-monitor"
18+
19+
# Load configuration if exists
20+
# WARNING: This file will be executed. Ensure config.sh is not writable by untrusted users.
21+
CONFIG_FILE="$(dirname "$0")/config.sh"
22+
if [ -f "$CONFIG_FILE" ]; then
23+
# Validate config file security before sourcing
24+
if [ -O "$CONFIG_FILE" ] || [ "$(stat -f %Su "$CONFIG_FILE" 2>/dev/null || stat -c %U "$CONFIG_FILE" 2>/dev/null)" = "$USER" ]; then
25+
# Check if file is not world-writable
26+
if [ "$(stat -f %OLp "$CONFIG_FILE" 2>/dev/null || stat -c %a "$CONFIG_FILE" 2>/dev/null | cut -c3)" != "2" ] && \
27+
[ "$(stat -f %OLp "$CONFIG_FILE" 2>/dev/null || stat -c %a "$CONFIG_FILE" 2>/dev/null | cut -c3)" != "6" ]; then
28+
# shellcheck disable=SC1090
29+
source "$CONFIG_FILE"
30+
else
31+
echo "[$HOOK_NAME] WARNING: Skipping config.sh - file is world-writable (security risk)"
32+
fi
33+
else
34+
echo "[$HOOK_NAME] WARNING: Skipping config.sh - file not owned by current user (security risk)"
35+
fi
36+
fi
37+
38+
# Function to check if required commands are available
39+
check_command() {
40+
local cmd=$1
41+
local required=$2
42+
43+
if ! command -v "$cmd" &> /dev/null; then
44+
if [ "$required" = "true" ]; then
45+
echo "[$HOOK_NAME] ERROR: Required command '$cmd' is not installed"
46+
return 1
47+
else
48+
echo "[$HOOK_NAME] WARNING: Optional command '$cmd' is not installed"
49+
return 0
50+
fi
51+
fi
52+
return 0
53+
}
54+
55+
# Function to monitor deployment
56+
monitor_deployment() {
57+
local task_id=$1
58+
59+
if [ -z "$task_id" ]; then
60+
echo "[$HOOK_NAME] ERROR: Task ID required"
61+
echo "Usage: $0 monitor <task_id>"
62+
exit 1
63+
fi
64+
65+
local output_file="/tmp/claude/-Users-hchenxa-Documents-hchenxa-src-acm-maestro/tasks/${task_id}.output"
66+
local exit_code_file="/tmp/claude/-Users-hchenxa-Documents-hchenxa-src-acm-maestro/tasks/${task_id}.exit_code"
67+
local start_time=$(date +%s)
68+
69+
echo "[$HOOK_NAME] Monitoring deployment task: $task_id"
70+
echo "[$HOOK_NAME] Started at: $(date)"
71+
echo ""
72+
73+
# Wait for the deployment to complete
74+
local last_line_count=0
75+
while true; do
76+
# Check if exit code file exists (task completed)
77+
if [ -f "$exit_code_file" ]; then
78+
local exit_code=$(cat "$exit_code_file")
79+
echo "[$HOOK_NAME] Deployment process finished with exit code: $exit_code"
80+
break
81+
fi
82+
83+
# Show progress if output file exists
84+
if [ -f "$output_file" ]; then
85+
local current_lines=$(wc -l < "$output_file" | tr -d ' ')
86+
if [ "$current_lines" != "$last_line_count" ]; then
87+
local elapsed=$(($(date +%s) - start_time))
88+
local minutes=$((elapsed / 60))
89+
local seconds=$((elapsed % 60))
90+
echo "[$HOOK_NAME] Progress: $current_lines lines | Elapsed: ${minutes}m ${seconds}s | $(date +%H:%M:%S)"
91+
92+
# Show latest activity
93+
tail -3 "$output_file" | sed 's/\x1b\[[0-9;]*m//g' | grep -v "^$" | tail -1 | sed "s/^/[$HOOK_NAME] Latest: /"
94+
95+
last_line_count=$current_lines
96+
fi
97+
fi
98+
99+
# Sleep before next check
100+
sleep 15
101+
done
102+
103+
# Calculate total time
104+
local 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+
payload=$(cat <<EOF
168+
{
169+
"attachments": [
170+
{
171+
"color": "$color",
172+
"title": "$emoji Maestro Deployment $status",
173+
"text": "$escaped_message",
174+
"footer": "Maestro Deployment Monitor",
175+
"ts": $(date +%s)
176+
}
177+
]
178+
}
179+
EOF
180+
)
181+
fi
182+
183+
# Send to Slack and capture exit status
184+
local curl_exit_code
185+
if curl -X POST -H 'Content-type: application/json' \
186+
--data "$payload" \
187+
"$webhook_url" \
188+
--silent --show-error; then
189+
curl_exit_code=0
190+
else
191+
curl_exit_code=$?
192+
echo "[$HOOK_NAME] ERROR: Failed to send Slack notification (curl exit code: $curl_exit_code)"
193+
fi
194+
195+
return $curl_exit_code
196+
}
197+
198+
# Function to send notification
199+
notify_completion() {
200+
local status=$1
201+
local message=$2
202+
203+
echo ""
204+
echo "=========================================="
205+
echo "[$HOOK_NAME] DEPLOYMENT $status"
206+
echo "Message: $message"
207+
echo "Time: $(date)"
208+
echo "=========================================="
209+
echo ""
210+
211+
# Send Slack notification if webhook is configured
212+
if [ -n "$SLACK_WEBHOOK_URL" ]; then
213+
echo "[$HOOK_NAME] Sending Slack notification..."
214+
if send_slack_notification "$status" "$message" "$SLACK_WEBHOOK_URL"; then
215+
echo "[$HOOK_NAME] Slack notification sent successfully"
216+
else
217+
echo "[$HOOK_NAME] Failed to send Slack notification"
218+
fi
219+
fi
220+
221+
# Also send system notification if available
222+
if command -v osascript &> /dev/null; then
223+
# macOS notification - escape message for AppleScript
224+
local safe_message="${message//\\/\\\\}"
225+
safe_message="${safe_message//\"/\\\"}"
226+
osascript -e "display notification \"$safe_message\" with title \"Maestro Deployment $status\""
227+
elif command -v notify-send &> /dev/null; then
228+
# Linux notification - use safe argument passing
229+
notify-send -- "Maestro Deployment $status" "$message"
230+
fi
231+
}
232+
233+
# Main execution
234+
case "${1:-notify}" in
235+
monitor)
236+
monitor_deployment "$2"
237+
exit $?
238+
;;
239+
notify)
240+
notify_completion "${2:-COMPLETE}" "${3:-Deployment finished}"
241+
;;
242+
*)
243+
echo "Usage: $0 {monitor <task_id>|notify <status> <message>}"
244+
exit 1
245+
;;
246+
esac

.claude/skills/README.md

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
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. Configure the hook:
61+
```bash
62+
cd .claude/hooks
63+
cp config.sh.example config.sh
64+
# Edit config.sh and set your SLACK_WEBHOOK_URL
65+
```
66+
67+
3. Or set it as an environment variable:
68+
```bash
69+
export SLACK_WEBHOOK_URL="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
70+
```
71+
72+
**Usage:**
73+
```bash
74+
# Monitor a deployment task in real-time
75+
.claude/hooks/deployment-monitor.sh monitor <task_id>
76+
77+
# Example:
78+
.claude/hooks/deployment-monitor.sh monitor b4ac6c1
79+
80+
# Send a manual completion notification
81+
.claude/hooks/deployment-monitor.sh notify "COMPLETE" "Deployment finished successfully"
82+
83+
# Send a failure notification
84+
.claude/hooks/deployment-monitor.sh notify "FAILED" "Deployment failed with errors"
85+
```
86+
87+
**What the monitor does:**
88+
1. Tracks the deployment task by its task ID
89+
2. Shows real-time progress updates (line count, elapsed time)
90+
3. Displays the latest deployment activity
91+
4. Detects when the task completes
92+
5. Automatically sends notifications (Slack + desktop) when done
93+
6. Reports final status and deployment duration
94+
95+
The hook will:
96+
1. Send a Slack notification (if configured) with color-coded messages
97+
2. Send desktop notifications on macOS (via osascript) or Linux (via notify-send)
98+
99+
## How Skills Work
100+
101+
Skills are invoked in Claude Code using the `/` prefix followed by the skill name. When you run a skill:
102+
103+
1. Claude Code reads the skill markdown file
104+
2. Executes the bash script in the Implementation section
105+
3. Returns the output to you in the chat
106+
107+
Skills are a powerful way to automate complex, multi-step workflows that you perform frequently.
108+
109+
## Creating New Skills
110+
111+
To create a new skill:
112+
113+
1. Create a new `.md` file in `.claude/skills/`
114+
2. Include these sections:
115+
- Title and description
116+
- Prerequisites
117+
- Usage example
118+
- Implementation (bash script in a code block)
119+
3. Make sure the bash script is well-commented and handles errors
120+
121+
See `SKILL.md` as an example.

0 commit comments

Comments
 (0)