mirror of
https://github.com/ivuorinen/actions.git
synced 2026-03-08 03:56:36 +00:00
chore(claude): add hooks, skills, and agents for Claude Code
Add auto-formatting hooks (ruff, shfmt, prettier, actionlint), rules.yml edit blocker, 5 skills (/release, /test-action, /new-action, /validate, /check-pins), and 2 subagents (action-validator, test-coverage-reviewer). Update CLAUDE.md with hook documentation.
This commit is contained in:
30
.claude/agents/action-validator.md
Normal file
30
.claude/agents/action-validator.md
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
You review action.yml files against the repository's critical prevention rules.
|
||||||
|
|
||||||
|
Check each action.yml file for these violations:
|
||||||
|
|
||||||
|
1. All external action refs are SHA-pinned (not @main/@v1)
|
||||||
|
2. All internal action refs use `ivuorinen/actions/name@SHA` format
|
||||||
|
3. Shell scripts use `set -eu` (POSIX, not bash)
|
||||||
|
4. Steps with referenced outputs have `id:` fields
|
||||||
|
5. Tool availability checked before use (`command -v`)
|
||||||
|
6. Variables properly quoted (`"$var"`)
|
||||||
|
7. `$GITHUB_OUTPUT` uses `printf`, not `echo`
|
||||||
|
8. No nested `${{ }}` in quoted YAML strings
|
||||||
|
9. Token inputs use `${{ github.token }}` default
|
||||||
|
10. Fallbacks provided for tools not on all runners
|
||||||
|
|
||||||
|
Run `actionlint` on each file. Report violations with file path, line, and fix suggestion.
|
||||||
|
|
||||||
|
To find all action.yml files:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
find . -name "action.yml" -not -path "./.git/*"
|
||||||
|
```
|
||||||
|
|
||||||
|
For each file, read it and check against all 10 rules. Then run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
actionlint <file>
|
||||||
|
```
|
||||||
|
|
||||||
|
Output a summary table of violations found, grouped by action.
|
||||||
33
.claude/agents/test-coverage-reviewer.md
Normal file
33
.claude/agents/test-coverage-reviewer.md
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
You review test coverage for GitHub Actions in this monorepo.
|
||||||
|
|
||||||
|
For each action:
|
||||||
|
|
||||||
|
1. Read the action.yml to understand inputs, outputs, and steps
|
||||||
|
2. Read the corresponding test files in `_tests/unit/<action-name>/`
|
||||||
|
3. Check if all inputs have validation tests
|
||||||
|
4. Check if error paths are tested (missing required inputs, invalid values)
|
||||||
|
5. Check if shell scripts have edge case tests (spaces in paths, empty strings, special chars)
|
||||||
|
6. Report coverage gaps with specific test suggestions
|
||||||
|
|
||||||
|
To find all actions and their tests:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ls -d */action.yml | sed 's|/action.yml||'
|
||||||
|
ls -d _tests/unit/*/
|
||||||
|
```
|
||||||
|
|
||||||
|
Compare the two lists to find actions without any tests.
|
||||||
|
|
||||||
|
For each action with tests, check coverage of:
|
||||||
|
|
||||||
|
- All required inputs validated
|
||||||
|
- All optional inputs with defaults tested
|
||||||
|
- Error conditions (missing inputs, invalid formats)
|
||||||
|
- Edge cases in shell logic (empty strings, special characters, spaces in paths)
|
||||||
|
- Output values verified
|
||||||
|
|
||||||
|
Output a coverage report with:
|
||||||
|
|
||||||
|
- Actions with no tests (critical)
|
||||||
|
- Actions with partial coverage (list missing test cases)
|
||||||
|
- Actions with good coverage (brief confirmation)
|
||||||
16
.claude/hooks/block-rules-yml.sh
Executable file
16
.claude/hooks/block-rules-yml.sh
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
# Read JSON input from stdin to get the file path
|
||||||
|
INPUT=$(cat)
|
||||||
|
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.filePath // empty')
|
||||||
|
|
||||||
|
if [ -z "$FILE_PATH" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$FILE_PATH" in
|
||||||
|
*/rules.yml)
|
||||||
|
echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"rules.yml files are auto-generated. Run make update-validators instead."}}'
|
||||||
|
;;
|
||||||
|
esac
|
||||||
39
.claude/hooks/post-edit-write.sh
Executable file
39
.claude/hooks/post-edit-write.sh
Executable file
@@ -0,0 +1,39 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
# Read JSON input from stdin to get the file path
|
||||||
|
INPUT=$(cat)
|
||||||
|
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.filePath // empty')
|
||||||
|
|
||||||
|
if [ -z "$FILE_PATH" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$FILE_PATH" in
|
||||||
|
*/rules.yml)
|
||||||
|
# rules.yml should not be reached here (blocked by PreToolUse),
|
||||||
|
# but skip formatting just in case
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*.py)
|
||||||
|
ruff format --quiet "$FILE_PATH" 2>/dev/null || true
|
||||||
|
ruff check --fix --quiet "$FILE_PATH" 2>/dev/null || true
|
||||||
|
;;
|
||||||
|
*.sh)
|
||||||
|
shfmt -w "$FILE_PATH" 2>/dev/null || true
|
||||||
|
shellcheck "$FILE_PATH" 2>&1 || true
|
||||||
|
;;
|
||||||
|
*.yml|*.yaml|*.json)
|
||||||
|
npx prettier --write "$FILE_PATH" 2>/dev/null || true
|
||||||
|
;;
|
||||||
|
*.md)
|
||||||
|
npx prettier --write "$FILE_PATH" 2>/dev/null || true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Run actionlint on action.yml files
|
||||||
|
case "$FILE_PATH" in
|
||||||
|
*/action.yml)
|
||||||
|
actionlint "$FILE_PATH" 2>&1 || true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
26
.claude/settings.json
Normal file
26
.claude/settings.json
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"hooks": {
|
||||||
|
"PreToolUse": [
|
||||||
|
{
|
||||||
|
"matcher": "Edit|Write",
|
||||||
|
"hooks": [
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-rules-yml.sh"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"PostToolUse": [
|
||||||
|
{
|
||||||
|
"matcher": "Edit|Write",
|
||||||
|
"hooks": [
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/post-edit-write.sh"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
39
.claude/skills/check-pins/SKILL.md
Normal file
39
.claude/skills/check-pins/SKILL.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
---
|
||||||
|
name: check-pins
|
||||||
|
description: Verify all action references are properly SHA-pinned
|
||||||
|
disable-model-invocation: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# Check SHA-Pinned Action References
|
||||||
|
|
||||||
|
## 1. Check version references
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make check-version-refs
|
||||||
|
```
|
||||||
|
|
||||||
|
This verifies that all `ivuorinen/actions/*` references in `action.yml` files use SHA-pinned commits.
|
||||||
|
|
||||||
|
## 2. Check local references
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make check-local-refs
|
||||||
|
```
|
||||||
|
|
||||||
|
This verifies that test workflows use `./action-name` format (local references are allowed in tests).
|
||||||
|
|
||||||
|
## 3. Interpret results
|
||||||
|
|
||||||
|
**Violations to fix:**
|
||||||
|
|
||||||
|
- `@main` or `@v*` references in `action.yml` files must be replaced with full SHA commits
|
||||||
|
- `./action-name` in `action.yml` (non-test) files must use `ivuorinen/actions/action-name@<SHA>`
|
||||||
|
- External actions must be pinned to SHA commits, not version tags
|
||||||
|
|
||||||
|
**How to get the current SHA:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git rev-parse HEAD
|
||||||
|
```
|
||||||
|
|
||||||
|
Use this SHA when pinning internal action references after committing changes.
|
||||||
60
.claude/skills/new-action/SKILL.md
Normal file
60
.claude/skills/new-action/SKILL.md
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
---
|
||||||
|
name: new-action
|
||||||
|
description: Scaffold a new GitHub Action with all required files
|
||||||
|
disable-model-invocation: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# Scaffold a New GitHub Action
|
||||||
|
|
||||||
|
## 1. Gather information
|
||||||
|
|
||||||
|
Ask the user for:
|
||||||
|
|
||||||
|
- **Action name** (kebab-case, e.g. `my-new-action`)
|
||||||
|
- **Description** (one line)
|
||||||
|
- **Category** (setup, linting, testing, build, publishing, repository, utility)
|
||||||
|
- **Inputs** (name, description, required, default for each)
|
||||||
|
- **What it does** (shell commands, composite steps, etc.)
|
||||||
|
|
||||||
|
## 2. Create directory and action.yml
|
||||||
|
|
||||||
|
Create `<action-name>/action.yml` following the existing action patterns:
|
||||||
|
|
||||||
|
- Use `composite` runs type
|
||||||
|
- Include `set -eu` in shell scripts (POSIX sh, not bash)
|
||||||
|
- Use `${{ github.token }}` for token defaults
|
||||||
|
- Pin all external action references to SHA commits
|
||||||
|
- Pin internal action references using `ivuorinen/actions/action-name@<SHA>`
|
||||||
|
- Add `id:` to steps whose outputs are referenced
|
||||||
|
|
||||||
|
## 3. Generate validation rules
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make update-validators
|
||||||
|
```
|
||||||
|
|
||||||
|
This generates `<action-name>/rules.yml` from the action's inputs.
|
||||||
|
|
||||||
|
## 4. Generate test scaffolding
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make generate-tests
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. Generate README
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make docs
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. Run validation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make all
|
||||||
|
```
|
||||||
|
|
||||||
|
Fix any issues before considering the action complete.
|
||||||
|
|
||||||
|
## 7. Update repository overview
|
||||||
|
|
||||||
|
Remind the user to update the Serena memory `repository_overview` if they use Serena.
|
||||||
57
.claude/skills/release/SKILL.md
Normal file
57
.claude/skills/release/SKILL.md
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
---
|
||||||
|
name: release
|
||||||
|
description: Create a new CalVer release with validation checks
|
||||||
|
disable-model-invocation: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# Release Workflow
|
||||||
|
|
||||||
|
Follow these steps to create a new CalVer release:
|
||||||
|
|
||||||
|
## 1. Pre-flight checks
|
||||||
|
|
||||||
|
Run the full validation pipeline:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make all
|
||||||
|
```
|
||||||
|
|
||||||
|
If any step fails, fix the issues before proceeding.
|
||||||
|
|
||||||
|
## 2. Check version references
|
||||||
|
|
||||||
|
Verify all action references are properly pinned:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make check-version-refs
|
||||||
|
make check-local-refs
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Prepare the release
|
||||||
|
|
||||||
|
Run release preparation (updates version references):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make release-prep
|
||||||
|
```
|
||||||
|
|
||||||
|
Review the changes with `git diff`.
|
||||||
|
|
||||||
|
## 4. Confirm with user
|
||||||
|
|
||||||
|
Ask the user to confirm:
|
||||||
|
|
||||||
|
- The version number (defaults to `vYYYY.MM.DD` based on today's date)
|
||||||
|
- That all changes look correct
|
||||||
|
|
||||||
|
## 5. Create the release
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make release VERSION=vYYYY.MM.DD
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace `vYYYY.MM.DD` with the confirmed version.
|
||||||
|
|
||||||
|
## 6. Verify
|
||||||
|
|
||||||
|
Show the user the created tag and any output from the release process.
|
||||||
34
.claude/skills/test-action/SKILL.md
Normal file
34
.claude/skills/test-action/SKILL.md
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
---
|
||||||
|
name: test-action
|
||||||
|
description: Run tests for a specific GitHub Action by name
|
||||||
|
disable-model-invocation: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# Test a Specific Action
|
||||||
|
|
||||||
|
## 1. Identify the action
|
||||||
|
|
||||||
|
Ask the user which action to test if not already specified.
|
||||||
|
List available actions if needed:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ls -d */action.yml | sed 's|/action.yml||'
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Run tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make test-action ACTION=<action-name>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Display results
|
||||||
|
|
||||||
|
Show the test output. If tests fail, read the relevant test files in `_tests/unit/<action-name>/` and the action's `action.yml` to help diagnose the issue.
|
||||||
|
|
||||||
|
## 4. Coverage (optional)
|
||||||
|
|
||||||
|
If the user wants coverage information:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make test-coverage
|
||||||
|
```
|
||||||
49
.claude/skills/validate/SKILL.md
Normal file
49
.claude/skills/validate/SKILL.md
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
---
|
||||||
|
name: validate
|
||||||
|
description: Run full validation pipeline (docs, format, lint, test)
|
||||||
|
disable-model-invocation: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# Full Validation Pipeline
|
||||||
|
|
||||||
|
Run the complete validation pipeline:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make all
|
||||||
|
```
|
||||||
|
|
||||||
|
This runs in order: `docs` -> `format` -> `lint` -> `test`
|
||||||
|
|
||||||
|
## If validation fails
|
||||||
|
|
||||||
|
### Formatting issues
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make format
|
||||||
|
```
|
||||||
|
|
||||||
|
Then re-run `make all`.
|
||||||
|
|
||||||
|
### Linting issues
|
||||||
|
|
||||||
|
- **actionlint**: Check action.yml syntax, step IDs, expression usage
|
||||||
|
- **shellcheck**: POSIX compliance, quoting, variable usage
|
||||||
|
- **ruff**: Python style and errors
|
||||||
|
- **markdownlint**: Markdown formatting
|
||||||
|
- **prettier**: YAML/JSON/MD formatting
|
||||||
|
|
||||||
|
### Test failures
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make test
|
||||||
|
```
|
||||||
|
|
||||||
|
Read the failing test output and fix the underlying action or test.
|
||||||
|
|
||||||
|
### Documentation drift
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make docs
|
||||||
|
```
|
||||||
|
|
||||||
|
Regenerates READMEs from action.yml files.
|
||||||
10
CLAUDE.md
10
CLAUDE.md
@@ -25,12 +25,22 @@
|
|||||||
### Folders
|
### Folders
|
||||||
|
|
||||||
- `.serena/` – Internal config (do not edit)
|
- `.serena/` – Internal config (do not edit)
|
||||||
|
- `.claude/hooks/` – Claude Code hook scripts (auto-format, lint, block rules.yml edits)
|
||||||
|
- `.claude/skills/` – Claude Code skills (`/release`, `/test-action`, `/new-action`, `/validate`, `/check-pins`)
|
||||||
|
- `.claude/agents/` – Claude Code subagents (action-validator, test-coverage-reviewer)
|
||||||
- `.github/` – Workflows/templates
|
- `.github/` – Workflows/templates
|
||||||
- `_tests/` – ShellSpec tests
|
- `_tests/` – ShellSpec tests
|
||||||
- `_tools/` – Helper tools
|
- `_tools/` – Helper tools
|
||||||
- `validate-inputs/` – Python validation system + tests
|
- `validate-inputs/` – Python validation system + tests
|
||||||
- `*/rules.yml` – Auto-generated validation rules
|
- `*/rules.yml` – Auto-generated validation rules
|
||||||
|
|
||||||
|
### Claude Code Hooks
|
||||||
|
|
||||||
|
**Auto-formatting**: PostToolUse hooks auto-format files on Edit/Write (ruff for .py, shfmt for .sh, prettier for .yml/.yaml/.json/.md, actionlint for action.yml)
|
||||||
|
**Blocked edits**: PreToolUse hook blocks direct edits to `rules.yml` (auto-generated, use `make update-validators`)
|
||||||
|
**Hook schema**: `matcher` is a regex string matching tool names (e.g. `"Edit|Write"`), not an object. File filtering done in hook scripts via stdin JSON (`jq -r '.tool_input.file_path'`)
|
||||||
|
**Reference**: `$CLAUDE_PROJECT_DIR` for project-relative paths in hook commands
|
||||||
|
|
||||||
### Memory System
|
### Memory System
|
||||||
|
|
||||||
**Location**: `.serena/memories/` (9 consolidated memories for context)
|
**Location**: `.serena/memories/` (9 consolidated memories for context)
|
||||||
|
|||||||
Reference in New Issue
Block a user