-
Notifications
You must be signed in to change notification settings - Fork 27
Expand file tree
/
Copy pathJustfile
More file actions
171 lines (140 loc) · 6.68 KB
/
Justfile
File metadata and controls
171 lines (140 loc) · 6.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# Unraid MCP Server — Justfile
# Run `just` to list available recipes
default:
@just --list
# ── Development ───────────────────────────────────────────────────────────────
# Start development server (hot-reload via watchfiles)
dev:
uv run python -m unraid_mcp
# Run tests
test:
uv run pytest tests/ -v
# Run linter (ruff)
lint:
uv run ruff check .
# Format code (ruff)
fmt:
uv run ruff format .
# Type-check (pyright / mypy)
typecheck:
uv run pyright unraid_mcp/ || uv run mypy unraid_mcp/
# Validate skills SKILL.md files exist and are non-empty
validate-skills:
@echo "=== Validating skills ==="
@for f in skills/*/SKILL.md; do \
if [ -f "$$f" ]; then \
echo " ✓ $$f"; \
else \
echo " ✗ SKILL.md not found in skills/"; \
exit 1; \
fi; \
done
@echo "Skills validation passed"
# Build Docker image
build:
docker build -t unraid-mcp .
# ── Docker Compose ────────────────────────────────────────────────────────────
# Start containers
up:
docker compose up -d
# Stop containers
down:
docker compose down
# Restart containers
restart:
docker compose restart
# Tail container logs
logs:
docker compose logs -f
# Check /health endpoint
health:
@PORT=$$(grep -E '^UNRAID_MCP_PORT=' .env 2>/dev/null | cut -d= -f2 || echo 6970); \
curl -sf "http://localhost:$$PORT/health" | python3 -m json.tool || echo "Health check failed"
# ── Live Testing ──────────────────────────────────────────────────────────────
# Run live integration tests against a running server
test-live:
uv run pytest tests/ -v -m live
# Run HTTP end-to-end smoke-test against the local server (auto-reads token from ~/.unraid-mcp/.env)
test-http:
bash tests/mcporter/test-http.sh
# Run HTTP e2e test with auth disabled (for gateway-protected deployments)
test-http-no-auth:
bash tests/mcporter/test-http.sh --skip-auth
# Run HTTP e2e test against a remote URL
# Usage: just test-http-remote https://unraid.tootie.tv/mcp
test-http-remote url:
bash tests/mcporter/test-http.sh --url {{url}} --skip-auth
# ── Setup ─────────────────────────────────────────────────────────────────────
# Create .env from .env.example if missing
setup:
@if [ ! -f .env ]; then \
cp .env.example .env && chmod 600 .env; \
echo "Created .env from .env.example — fill in your credentials"; \
else \
echo ".env already exists"; \
fi
# Generate a secure bearer token for UNRAID_MCP_TOKEN
gen-token:
@python3 -c "import secrets; print(secrets.token_urlsafe(32))"
# ── Quality Gates ─────────────────────────────────────────────────────────────
# Run docker security checks
check-contract:
bash bin/check-docker-security.sh
bash bin/check-no-baked-env.sh
bash bin/ensure-ignore-files.sh --check
# ── CLI Generation ────────────────────────────────────────────────────────────
# Generate a standalone CLI for this server (requires running server)
generate-cli:
#!/usr/bin/env bash
set -euo pipefail
PORT="${UNRAID_MCP_PORT:-6970}"
echo "⚠ Server must be running on port ${PORT} (run 'just dev' first)"
echo "⚠ Generated CLI embeds your OAuth token — do not commit or share"
mkdir -p dist dist/.cache
current_hash=$(timeout 10 curl -sf \
-H "Authorization: Bearer $MCP_TOKEN" \
-H "Accept: application/json, text/event-stream" \
"http://localhost:${PORT}/mcp/tools/list" 2>/dev/null | sha256sum | cut -d' ' -f1 || echo "nohash")
cache_file="dist/.cache/unraid-mcp-cli.schema_hash"
if [[ -f "$cache_file" ]] && [[ "$(cat "$cache_file")" == "$current_hash" ]] && [[ -f "dist/unraid-mcp-cli" ]]; then
echo "SKIP: unraid-mcp tool schema unchanged — use existing dist/unraid-mcp-cli"
exit 0
fi
timeout 30 mcporter generate-cli \
--command "http://localhost:${PORT}/mcp" \
--header "Authorization: Bearer $MCP_TOKEN" \
--name unraid-mcp-cli \
--output dist/unraid-mcp-cli
printf '%s' "$current_hash" > "$cache_file"
echo "✓ Generated dist/unraid-mcp-cli (requires bun at runtime)"
# ── Cleanup ───────────────────────────────────────────────────────────────────
# Remove build artifacts, caches, and compiled files
clean:
rm -rf dist/ build/ .pytest_cache/ .ruff_cache/ .mypy_cache/ htmlcov/ .coverage coverage.xml
find . -type d -name __pycache__ -not -path './.venv/*' -exec rm -rf {} + 2>/dev/null || true
find . -name '*.pyc' -not -path './.venv/*' -delete 2>/dev/null || true
@echo "Cleaned build artifacts"
# Publish: bump version, tag, push (triggers PyPI + Docker publish)
publish bump="patch":
#!/usr/bin/env bash
set -euo pipefail
[ "$(git branch --show-current)" = "main" ] || { echo "Switch to main first"; exit 1; }
[ -z "$(git status --porcelain)" ] || { echo "Commit or stash changes first"; exit 1; }
git pull origin main
CURRENT=$(grep -m1 "^version" pyproject.toml | sed "s/.*\"\(.*\)\".*/\1/")
IFS="." read -r major minor patch <<< "$CURRENT"
case "{{bump}}" in
major) major=$((major+1)); minor=0; patch=0 ;;
minor) minor=$((minor+1)); patch=0 ;;
patch) patch=$((patch+1)) ;;
*) echo "Usage: just publish [major|minor|patch]"; exit 1 ;;
esac
NEW="${major}.${minor}.${patch}"
echo "Version: ${CURRENT} → ${NEW}"
sed -i "s/^version = \"${CURRENT}\"/version = \"${NEW}\"/" pyproject.toml
for f in .claude-plugin/plugin.json .codex-plugin/plugin.json gemini-extension.json; do
[ -f "$f" ] && python3 -c "import json; d=json.load(open(\"$f\")); d[\"version\"]=\"${NEW}\"; json.dump(d,open(\"$f\",\"w\"),indent=2); open(\"$f\",\"a\").write(\"
\")"
done
git add -A && git commit -m "release: v${NEW}" && git tag "v${NEW}" && git push origin main --tags
echo "Tagged v${NEW} — publish workflow will run automatically"