Files
gh-action-readme/CLAUDE.md
Ismo Vuorinen ce23f93b74 feat: detect permissions from actions (#137)
* feat: detect permissions from actions

* refactor(test): fix 25 SonarCloud issues by extracting test constants

Resolved all SonarCloud code quality issues for PR #137:
- Fixed 12 string duplication issues (S1192)
- Fixed 13 naming convention issues (S100)

Changes:
- Centralized test constants in appconstants/test_constants.go
  * Added 9 parser test constants for YAML templates
  * Added 3 template test constants for paths and versions
- Updated parser_test.go to use shared constants
- Updated template_test.go to use shared constants
- Renamed 13 test functions to camelCase (removed underscores)

* chore: reduce code duplication

* fix: implement cr fixes

* chore: deduplication
2026-01-04 02:48:29 +02:00

480 lines
12 KiB
Markdown

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
**gh-action-readme** - CLI tool for GitHub Actions documentation generation
## 📝 Template Updates
**Templates are embedded from:** `templates_embed/templates/`
**To modify templates:**
1. Edit template files directly in `templates_embed/templates/`
2. Rebuild the binary: `go build .`
3. Templates are automatically embedded via `//go:embed` directive
**Available template locations:**
- Default: `templates_embed/templates/readme.tmpl`
- GitHub theme: `templates_embed/templates/themes/github/readme.tmpl`
- GitLab theme: `templates_embed/templates/themes/gitlab/readme.tmpl`
- Minimal theme: `templates_embed/templates/themes/minimal/readme.tmpl`
- Professional: `templates_embed/templates/themes/professional/readme.tmpl`
- AsciiDoc theme: `templates_embed/templates/themes/asciidoc/readme.adoc`
**Template embedding:** Handled by `templates_embed/embed.go` using Go's embed directive.
The embedded filesystem is used by default, with fallback to filesystem for development.
## 🚨 CRITICAL: README Protection
**NEVER overwrite `/README.md`** - The root README.md is the main project documentation.
**For testing generation commands:**
```bash
# Safe testing approaches
gh-action-readme gen testdata/example-action/
gh-action-readme gen testdata/composite-action/action.yml
gh-action-readme gen testdata/ --output /tmp/test-output.md
```
## 🏗️ Architecture Overview
### Template Rendering Pipeline
1. **Parser** (`internal/parser.go`):
- Parses `action.yml` files using `goccy/go-yaml`
- Extracts permissions from header comments via `parsePermissionsFromComments()`
- Merges comment and YAML permissions (YAML takes precedence)
- Returns `*ActionYML` struct with all parsed data
1. **Template Data Builder** (`internal/template.go`):
- `BuildTemplateData()` creates comprehensive `TemplateData` struct
- Embeds `*ActionYML` (all action fields accessible via `.Name`, `.Inputs`, `.Permissions`, etc.)
- Detects git repository info (org, repo, default branch)
- Extracts action subdirectory for monorepo support
- Builds `uses:` statement with proper path/version
1. **Template Functions** (`internal/template.go:templateFuncs()`):
- `gitUsesString` - Generates complete `org/repo/path@version` string
- `actionVersion` - Determines version (config override → default branch → "v1")
- `gitOrg`, `gitRepo` - Extract git repository information
- Standard Go template functions: `lower`, `upper`, `replace`, `join`
1. **Renderer** (`internal/template.go:RenderReadme()`):
- Reads template from embedded filesystem via `templates_embed.ReadTemplate()`
- Executes template with `TemplateData`
- Supports multiple formats (md, html, json, asciidoc)
### Key Data Structures
**ActionYML** - Parsed action.yml data:
```go
type ActionYML struct {
Name string
Description string
Inputs map[string]ActionInput
Outputs map[string]ActionOutput
Runs map[string]any
Branding *Branding
Permissions map[string]string // From comments or YAML field
}
```
**TemplateData** - Complete data for rendering:
```go
type TemplateData struct {
*ActionYML // Embedded - all fields accessible directly
Git git.RepoInfo
Config *AppConfig
UsesStatement string // Pre-built "org/repo/path@version"
ActionPath string // For subdirectory extraction
RepoRoot string
Dependencies []dependencies.Dependency
}
```
### Monorepo Action Path Resolution
The tool automatically detects and handles monorepo actions:
```text
Input: /repo/actions/csharp-build/action.yml
Root: /repo
Output: org/repo/actions/csharp-build@main
```
Implementation in `internal/template.go:extractActionSubdirectory()`:
- Calculates relative path from repo root to action directory
- Returns empty string for root-level actions
- Returns subdirectory path for monorepo actions
- Used by `buildUsesString()` to construct proper `uses:` statements
### Permissions Parsing
Supports three sources (merged with priority):
1. **Header comments** (lowest priority):
```yaml
# permissions:
# - contents: read
# - issues: write
```
1. **YAML field** (highest priority):
```yaml
permissions:
contents: write
```
1. **Merged result**: YAML overrides comment values for duplicate keys, all unique keys included
## 🛠️ Development Commands
### Building and Running
```bash
# Build binary
go build .
# Run without installing
go run . gen testdata/example-action/
go run . validate
go run . config show
# Build and run tests
make build
make test
make test-coverage # With coverage report
make test-coverage-html # HTML coverage + open in browser
```
### Testing
```bash
# Run all tests
go test ./...
# Run specific test package
go test ./internal
go test ./internal/wizard
# Run specific test by name
go test ./internal -run TestParsePermissions
go test ./internal -v -run "TestParseActionYML_.*Permissions"
# Run tests with race detection
go test -race ./...
# Test all themes
for theme in default github gitlab minimal professional; do
./gh-action-readme gen testdata/example-action/ --theme $theme --output /tmp/test-$theme.md
done
```
### Linting and Quality
```bash
# Run all linters via pre-commit
make lint
# Run golangci-lint directly
golangci-lint run
golangci-lint run --timeout=5m
# Check editor config compliance
make editorconfig
# Auto-fix editorconfig issues
make editorconfig-fix
# Format code
make format
```
### Security Scanning
```bash
# Run all security checks
make security
# Individual security tools
make vulncheck # Go vulnerability check
make audit # Nancy dependency audit
make trivy # Container security scanner
make gitleaks # Secret detection
```
### Dependencies
```bash
# Check for outdated dependencies
make deps-check
# Update dependencies interactively
make deps-update
# Update all dependencies to latest
make deps-update-all
# Install development tools
make devtools
```
### Pre-commit Hooks
```bash
# Install hooks (run once per clone)
make pre-commit-install
# Update hooks to latest versions
make pre-commit-update
# Run hooks manually
pre-commit run --all-files
```
## ⚙️ Configuration System
### Configuration Hierarchy (highest to lowest priority)
1. **Command-line flags** - Override everything
2. **Action-specific config** - `.ghreadme.yaml` in action directory
3. **Repository config** - `.ghreadme.yaml` in repo root
4. **Global config** - `~/.config/gh-action-readme/config.yaml`
5. **Environment variables** - `GH_README_GITHUB_TOKEN`, `GITHUB_TOKEN`
6. **Defaults** - Built-in fallbacks
### Version Resolution for Usage Examples
Priority order for `uses: org/repo@VERSION`:
1. `Config.Version` - Explicit override (e.g., `version: "v2.0.0"`)
2. `Config.UseDefaultBranch` + `Git.DefaultBranch` - Detected branch (e.g., `@main`)
3. Fallback - `"v1"`
Implemented in `internal/template.go:getActionVersion()`.
### Permissions Parsing
**Comment Format Support:**
- List format: `# permissions:\n# - key: value`
- Object format: `# permissions:\n# key: value`
- Mixed format: Both styles in same block
- Inline comments: `# key: value # explanation`
**Merge Behavior:**
```go
// internal/parser.go:ParseActionYML()
if a.Permissions == nil && commentPermissions != nil {
a.Permissions = commentPermissions // Use comments
} else if a.Permissions != nil && commentPermissions != nil {
// Merge: YAML overrides, add missing from comments
for key, value := range commentPermissions {
if _, exists := a.Permissions[key]; !exists {
a.Permissions[key] = value
}
}
}
```
## 🔄 Adding New Features
### New Theme
1. Create template file:
```bash
touch templates_embed/templates/themes/THEME_NAME/readme.tmpl
```
1. Add theme constant to `appconstants/constants.go`:
```go
ThemeTHEMENAME = "theme-name"
TemplatePathTHEMENAME = "templates/themes/theme-name/readme.tmpl"
```
**Note:** The template path constant still uses `templates/` prefix (not
`templates_embed/templates/`) as this is the logical path used by the code.
The physical file lives in `templates_embed/templates/` but is referenced as
`templates/` in the code.
1. Add to theme resolver in `internal/config.go:resolveThemeTemplate()`:
```go
case appconstants.ThemeTHEMENAME:
templatePath = appconstants.TemplatePathTHEMENAME
```
1. Update `main.go:configThemesHandler()` to list the new theme
2. Rebuild: `go build .`
### New Output Format
1. Add format constant to `appconstants/constants.go`
2. Add case in `internal/generator.go:GenerateFromFile()`:
```go
case appconstants.OutputFormatNEW:
return g.generateNEW(action, outputDir, actionPath)
```
1. Implement generator method:
```go
func (g *Generator) generateNEW(action *ActionYML, outputDir, actionPath string) error {
opts := TemplateOptions{
TemplatePath: g.resolveTemplatePathForFormat(),
Format: "new",
}
// ... implementation
}
```
1. Update CLI help text in `main.go`
### New Template Function
Add to `internal/template.go:templateFuncs()`:
```go
func templateFuncs() template.FuncMap {
return template.FuncMap{
"myFunc": myFuncImplementation,
// ... existing functions
}
}
```
### New Parser Field
When adding fields to `ActionYML`:
1. Update struct in `internal/parser.go`
2. Update `ActionYMLForJSON` in `internal/json_writer.go` (for JSON output)
3. Add field to JSON struct initialization
4. Add tests in `internal/parser_test.go`
5. Update templates if field should be displayed
## 📊 Package Structure
- **`main.go`** - CLI entry point (Cobra commands)
- **`internal/generator.go`** - Core generation orchestration
- **`internal/parser.go`** - Action.yml parsing (including permissions from comments)
- **`internal/template.go`** - Template data building and rendering
- **`internal/config.go`** - Configuration management (Viper)
- **`internal/json_writer.go`** - JSON output format
- **`internal/output.go`** - Colored CLI output
- **`internal/progress.go`** - Progress bars for batch operations
- **`internal/git/`** - Git repository detection
- **`internal/validation/`** - Action.yml validation
- **`internal/wizard/`** - Interactive configuration wizard
- **`internal/dependencies/`** - Dependency analysis for actions
- **`internal/errors/`** - Contextual error handling
- **`appconstants/`** - Application constants
- **`testutil/`** - Testing utilities
- **`templates_embed/`** - Embedded template filesystem
## 🧪 Testing Guidelines
### Test File Locations
- Unit tests: `internal/*_test.go` alongside source files
- Test fixtures: `testdata/example-action/`, `testdata/composite-action/`
- Integration tests: Manual CLI testing with testdata
### Running Specific Tests
```bash
# Parser tests
go test ./internal -v -run TestParse
# Permissions tests
go test ./internal -run ".*Permissions"
# Template tests
go test ./internal -run ".*Template|.*Uses"
# Generator tests
go test ./internal -run "TestGenerator"
# Wizard tests
go test ./internal/wizard -v
```
### Template Testing After Updates
```bash
# 1. Rebuild with updated templates
go build .
# 2. Test all themes
for theme in default github gitlab minimal professional; do
./gh-action-readme gen testdata/example-action/ --theme $theme --output /tmp/test-$theme.md
echo "=== $theme theme ==="
grep -i "permissions" /tmp/test-$theme.md && echo "✅ Found" || echo "❌ Missing"
done
# 4. Test JSON output
./gh-action-readme gen testdata/example-action/ -f json -o /tmp/test.json
cat /tmp/test.json | python3 -m json.tool | grep -A 3 permissions
```
## 📦 Dependency Management
**Automated Updates:**
- Renovate bot runs weekly (Mondays 4am UTC)
- Auto-merges minor/patch updates
- Major updates require manual review
- Groups `golang.org/x` packages together
**Manual Updates:**
```bash
make deps-check # Show outdated
make deps-update # Interactive with go-mod-upgrade
make deps-update-all # Update all to latest
```
## 🔐 Security
**Pre-commit Hooks:**
- `gitleaks` - Secret detection
- `golangci-lint` - Static analysis including security checks
- `editorconfig-checker` - File format validation
**Security Scanning:**
- CodeQL analysis on push/PR
- Go vulnerability check (govulncheck)
- Trivy container scanning
- Nancy dependency audit
**Path Validation:**
All file reads use validated paths to prevent path traversal:
```go
// templates_embed/embed.go:ReadTemplate()
cleanPath := filepath.Clean(templatePath)
if cleanPath != templatePath || strings.Contains(cleanPath, "..") {
return nil, filepath.ErrBadPattern
}
```