Skip to content

Commit 8aec2d2

Browse files
fulfarodevclaude
andcommitted
ci: GitHub Actions pipelines for validation, install test, and release
- validate.yml: plugin structure, frontmatter, YAML templates, file completeness, docs - install-test.yml: cross-platform (Linux, macOS, Windows) install verification - release.yml: auto-create GitHub releases with archives on tag push - README badges: CI status, license, version, Claude Code Plugin Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 18828be commit 8aec2d2

4 files changed

Lines changed: 355 additions & 1 deletion

File tree

.github/workflows/install-test.yml

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
name: Install Test
2+
3+
on:
4+
push:
5+
branches: [main, develop]
6+
pull_request:
7+
branches: [main, develop]
8+
9+
jobs:
10+
install-test:
11+
name: Plugin Install Test
12+
runs-on: ${{ matrix.os }}
13+
strategy:
14+
matrix:
15+
os: [ubuntu-latest, macos-latest, windows-latest]
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: Verify plugin structure for Claude Code
20+
shell: bash
21+
run: |
22+
echo "=== Verifying plugin can be installed by Claude Code ==="
23+
24+
# Check required plugin manifest
25+
if [ ! -f ".claude-plugin/plugin.json" ]; then
26+
echo "ERROR: .claude-plugin/plugin.json not found"
27+
exit 1
28+
fi
29+
echo "plugin.json found"
30+
31+
# Check commands directory with .md files
32+
cmd_count=$(ls commands/*.md 2>/dev/null | wc -l)
33+
if [ "$cmd_count" -eq 0 ]; then
34+
echo "ERROR: No command files found in commands/"
35+
exit 1
36+
fi
37+
echo "Found $cmd_count command files"
38+
39+
# Check agents directory with .md files
40+
agent_count=$(ls agents/*.md 2>/dev/null | wc -l)
41+
if [ "$agent_count" -eq 0 ]; then
42+
echo "ERROR: No agent files found in agents/"
43+
exit 1
44+
fi
45+
echo "Found $agent_count agent files"
46+
47+
# Check templates
48+
tpl_count=$(ls templates/*.yaml 2>/dev/null | wc -l)
49+
echo "Found $tpl_count template files"
50+
51+
# Check schemas
52+
schema_count=$(ls schemas/*.md 2>/dev/null | wc -l)
53+
echo "Found $schema_count schema files"
54+
55+
# Summary
56+
echo ""
57+
echo "=== Plugin Summary ==="
58+
python3 -c "
59+
import json
60+
with open('.claude-plugin/plugin.json') as f:
61+
p = json.load(f)
62+
print(f' Name: {p[\"name\"]}')
63+
print(f' Version: {p[\"version\"]}')
64+
print(f' License: {p[\"license\"]}')
65+
"
66+
echo " Commands: $cmd_count"
67+
echo " Agents: $agent_count"
68+
echo " Templates: $tpl_count"
69+
echo " Schemas: $schema_count"
70+
echo ""
71+
echo "Plugin is ready for: claude plugins add ./"

.github/workflows/release.yml

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
8+
permissions:
9+
contents: write
10+
11+
jobs:
12+
release:
13+
name: Create GitHub Release
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@v4
17+
with:
18+
fetch-depth: 0
19+
20+
- name: Validate plugin before release
21+
run: |
22+
python3 -c "
23+
import json
24+
with open('.claude-plugin/plugin.json') as f:
25+
data = json.load(f)
26+
tag = '${GITHUB_REF_NAME}'.lstrip('v')
27+
if data['version'] != tag:
28+
print(f'ERROR: plugin.json version ({data[\"version\"]}) does not match tag ({tag})')
29+
exit(1)
30+
print(f'Version match: {data[\"version\"]}')
31+
"
32+
33+
- name: Extract changelog for this version
34+
id: changelog
35+
run: |
36+
VERSION="${GITHUB_REF_NAME#v}"
37+
# Extract the section for this version from CHANGELOG.md
38+
awk "/^## \[${VERSION}\]/{flag=1; next} /^## \[/{flag=0} flag" CHANGELOG.md > /tmp/release-notes.md
39+
if [ ! -s /tmp/release-notes.md ]; then
40+
echo "No changelog entry found for ${VERSION}, using default"
41+
echo "Release ${GITHUB_REF_NAME}" > /tmp/release-notes.md
42+
fi
43+
cat /tmp/release-notes.md
44+
45+
- name: Create release archive
46+
run: |
47+
VERSION="${GITHUB_REF_NAME}"
48+
# Create a clean archive without .git
49+
mkdir -p dist
50+
tar --exclude='.git' --exclude='dist' -czf "dist/astromesh-leia-${VERSION}.tar.gz" .
51+
zip -r "dist/astromesh-leia-${VERSION}.zip" . -x '.git/*' 'dist/*'
52+
echo "Created archives:"
53+
ls -lh dist/
54+
55+
- name: Create GitHub Release
56+
uses: softprops/action-gh-release@v2
57+
with:
58+
body_path: /tmp/release-notes.md
59+
files: |
60+
dist/astromesh-leia-*.tar.gz
61+
dist/astromesh-leia-*.zip
62+
generate_release_notes: true
63+
env:
64+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/validate.yml

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
name: Validate Plugin
2+
3+
on:
4+
push:
5+
branches: [main, develop]
6+
pull_request:
7+
branches: [main, develop]
8+
9+
jobs:
10+
validate-structure:
11+
name: Validate Plugin Structure
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v4
15+
16+
- name: Validate plugin.json
17+
run: |
18+
echo "Checking plugin.json exists and is valid JSON..."
19+
python3 -c "
20+
import json, sys
21+
with open('.claude-plugin/plugin.json') as f:
22+
data = json.load(f)
23+
required = ['name', 'version', 'description', 'author', 'license']
24+
missing = [k for k in required if k not in data]
25+
if missing:
26+
print(f'Missing required fields: {missing}')
27+
sys.exit(1)
28+
print(f'Plugin: {data[\"name\"]} v{data[\"version\"]}')
29+
print('plugin.json is valid')
30+
"
31+
32+
- name: Validate command frontmatter
33+
run: |
34+
echo "Checking all commands have valid YAML frontmatter..."
35+
errors=0
36+
for f in commands/*.md; do
37+
if ! head -1 "$f" | grep -q "^---$"; then
38+
echo "ERROR: $f missing frontmatter delimiter"
39+
errors=$((errors + 1))
40+
fi
41+
if ! grep -q "^description:" "$f"; then
42+
echo "ERROR: $f missing description field"
43+
errors=$((errors + 1))
44+
fi
45+
done
46+
echo "Checked $(ls commands/*.md | wc -l) commands"
47+
if [ $errors -gt 0 ]; then
48+
echo "Found $errors errors"
49+
exit 1
50+
fi
51+
echo "All commands valid"
52+
53+
- name: Validate agent frontmatter
54+
run: |
55+
echo "Checking all agents have valid frontmatter..."
56+
errors=0
57+
for f in agents/*.md; do
58+
if ! head -1 "$f" | grep -q "^---$"; then
59+
echo "ERROR: $f missing frontmatter delimiter"
60+
errors=$((errors + 1))
61+
fi
62+
for field in name description tools model; do
63+
if ! grep -q "^${field}:" "$f"; then
64+
echo "ERROR: $f missing $field field"
65+
errors=$((errors + 1))
66+
fi
67+
done
68+
done
69+
echo "Checked $(ls agents/*.md | wc -l) agents"
70+
if [ $errors -gt 0 ]; then
71+
echo "Found $errors errors"
72+
exit 1
73+
fi
74+
echo "All agents valid"
75+
76+
- name: Validate YAML templates
77+
run: |
78+
echo "Checking all templates are valid YAML..."
79+
python3 -c "
80+
import yaml, glob, sys
81+
errors = 0
82+
for f in sorted(glob.glob('templates/*.yaml')):
83+
try:
84+
with open(f) as fh:
85+
data = yaml.safe_load(fh)
86+
if data.get('apiVersion') != 'astromesh/v1':
87+
print(f'ERROR: {f} wrong apiVersion: {data.get(\"apiVersion\")}')
88+
errors += 1
89+
if data.get('kind') != 'Agent':
90+
print(f'ERROR: {f} wrong kind: {data.get(\"kind\")}')
91+
errors += 1
92+
if not data.get('metadata', {}).get('name'):
93+
print(f'ERROR: {f} missing metadata.name')
94+
errors += 1
95+
print(f' {f}: {data[\"metadata\"][\"name\"]} - OK')
96+
except Exception as e:
97+
print(f'ERROR: {f} invalid YAML: {e}')
98+
errors += 1
99+
print(f'Checked {len(glob.glob(\"templates/*.yaml\"))} templates')
100+
if errors:
101+
sys.exit(1)
102+
print('All templates valid')
103+
"
104+
105+
- name: Validate file completeness
106+
run: |
107+
echo "Checking expected files exist..."
108+
errors=0
109+
expected_commands="leia create deploy status logs test templates config bootstrap teardown"
110+
for cmd in $expected_commands; do
111+
if [ ! -f "commands/${cmd}.md" ]; then
112+
echo "ERROR: missing commands/${cmd}.md"
113+
errors=$((errors + 1))
114+
fi
115+
done
116+
117+
expected_agents="leia-interpreter leia-architect leia-operator leia-tester leia-doctor"
118+
for agent in $expected_agents; do
119+
if [ ! -f "agents/${agent}.md" ]; then
120+
echo "ERROR: missing agents/${agent}.md"
121+
errors=$((errors + 1))
122+
fi
123+
done
124+
125+
expected_schemas="astromesh-v1-agent nexus-api whatsapp-config orchestration-patterns"
126+
for schema in $expected_schemas; do
127+
if [ ! -f "schemas/${schema}.md" ]; then
128+
echo "ERROR: missing schemas/${schema}.md"
129+
errors=$((errors + 1))
130+
fi
131+
done
132+
133+
for f in .claude-plugin/plugin.json README.md LICENSE CHANGELOG.md; do
134+
if [ ! -f "$f" ]; then
135+
echo "ERROR: missing $f"
136+
errors=$((errors + 1))
137+
fi
138+
done
139+
140+
if [ $errors -gt 0 ]; then
141+
echo "Found $errors missing files"
142+
exit 1
143+
fi
144+
echo "All expected files present"
145+
146+
validate-docs:
147+
name: Validate Documentation
148+
runs-on: ubuntu-latest
149+
steps:
150+
- uses: actions/checkout@v4
151+
152+
- name: Check documentation files exist
153+
run: |
154+
expected_docs="architecture commands-reference agents-guide templates-guide configuration whatsapp-setup troubleshooting"
155+
errors=0
156+
for doc in $expected_docs; do
157+
if [ ! -f "docs/${doc}.md" ]; then
158+
echo "ERROR: missing docs/${doc}.md"
159+
errors=$((errors + 1))
160+
fi
161+
done
162+
163+
expected_tutorials="first-agent business-templates multi-tenant advanced-patterns"
164+
for tut in $expected_tutorials; do
165+
if [ ! -f "docs/tutorials/${tut}.md" ]; then
166+
echo "ERROR: missing docs/tutorials/${tut}.md"
167+
errors=$((errors + 1))
168+
fi
169+
done
170+
171+
if [ $errors -gt 0 ]; then
172+
exit 1
173+
fi
174+
echo "All documentation files present"
175+
176+
- name: Check for broken internal links
177+
run: |
178+
echo "Checking for broken internal markdown links..."
179+
errors=0
180+
for f in $(find docs -name "*.md") README.md; do
181+
# Extract relative links like [text](path.md) or [text](../path.md)
182+
links=$(grep -oP '\]\((?!http)(?!#)([^)]+\.md)\)' "$f" 2>/dev/null | sed 's/\](\(.*\))/\1/' || true)
183+
dir=$(dirname "$f")
184+
for link in $links; do
185+
resolved="$dir/$link"
186+
if [ ! -f "$resolved" ]; then
187+
echo "WARNING: $f references $link (resolved: $resolved) - not found"
188+
fi
189+
done
190+
done
191+
echo "Link check complete"

README.md

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# astromesh-leia
22

3+
[![Validate Plugin](https://github.com/monaccode/astromesh-leia/actions/workflows/validate.yml/badge.svg)](https://github.com/monaccode/astromesh-leia/actions/workflows/validate.yml)
4+
[![Install Test](https://github.com/monaccode/astromesh-leia/actions/workflows/install-test.yml/badge.svg)](https://github.com/monaccode/astromesh-leia/actions/workflows/install-test.yml)
5+
[![Release](https://github.com/monaccode/astromesh-leia/actions/workflows/release.yml/badge.svg)](https://github.com/monaccode/astromesh-leia/releases)
6+
[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
7+
[![Version](https://img.shields.io/badge/version-0.1.0-green.svg)](https://github.com/monaccode/astromesh-leia/releases/tag/v0.1.0)
8+
[![Claude Code Plugin](https://img.shields.io/badge/Claude_Code-Plugin-blueviolet.svg)](https://claude.com/claude-code)
9+
310
A Claude Code plugin that provides a natural-language interface for creating, deploying, and managing AI agents on astromesh-nexus clusters. Named after Leia, a lemon beagle, this plugin takes you from business idea to deployed WhatsApp agent in minutes -- no Kubernetes expertise required.
411

512
## Architecture
@@ -38,11 +45,32 @@ graph LR
3845
NOD --> LLM
3946
```
4047

41-
## Quick Start
48+
## Install
4249

4350
```bash
51+
# Clone and install
4452
git clone https://github.com/monaccode/astromesh-leia.git
4553
claude plugins add ./astromesh-leia
54+
55+
# Or install from a release
56+
curl -L https://github.com/monaccode/astromesh-leia/releases/latest/download/astromesh-leia-v0.1.0.tar.gz | tar xz
57+
claude plugins add ./astromesh-leia
58+
```
59+
60+
## Quick Start
61+
62+
```bash
63+
# 1. Bootstrap a local nexus cluster
64+
/leia bootstrap local
65+
66+
# 2. Create your first WhatsApp agent
67+
/leia I need a customer support bot for my coffee shop
68+
69+
# 3. Check status
70+
/leia status
71+
72+
# 4. Test it
73+
/leia test coffee-support
4674
```
4775

4876
## Command Reference

0 commit comments

Comments
 (0)