* 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
12 KiB
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:
- Edit template files directly in
templates_embed/templates/ - Rebuild the binary:
go build . - Templates are automatically embedded via
//go:embeddirective
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:
# 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
- Parser (
internal/parser.go):
- Parses
action.ymlfiles usinggoccy/go-yaml - Extracts permissions from header comments via
parsePermissionsFromComments() - Merges comment and YAML permissions (YAML takes precedence)
- Returns
*ActionYMLstruct with all parsed data
- Template Data Builder (
internal/template.go):
BuildTemplateData()creates comprehensiveTemplateDatastruct- 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
- Template Functions (
internal/template.go:templateFuncs()):
gitUsesString- Generates completeorg/repo/path@versionstringactionVersion- Determines version (config override → default branch → "v1")gitOrg,gitRepo- Extract git repository information- Standard Go template functions:
lower,upper,replace,join
- 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:
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:
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:
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 properuses:statements
Permissions Parsing
Supports three sources (merged with priority):
- Header comments (lowest priority):
# permissions:
# - contents: read
# - issues: write
- YAML field (highest priority):
permissions:
contents: write
- Merged result: YAML overrides comment values for duplicate keys, all unique keys included
🛠️ Development Commands
Building and Running
# 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
# 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
# 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
# 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
# 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
# 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)
- Command-line flags - Override everything
- Action-specific config -
.ghreadme.yamlin action directory - Repository config -
.ghreadme.yamlin repo root - Global config -
~/.config/gh-action-readme/config.yaml - Environment variables -
GH_README_GITHUB_TOKEN,GITHUB_TOKEN - Defaults - Built-in fallbacks
Version Resolution for Usage Examples
Priority order for uses: org/repo@VERSION:
Config.Version- Explicit override (e.g.,version: "v2.0.0")Config.UseDefaultBranch+Git.DefaultBranch- Detected branch (e.g.,@main)- 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:
// 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
- Create template file:
touch templates_embed/templates/themes/THEME_NAME/readme.tmpl
- Add theme constant to
appconstants/constants.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.
- Add to theme resolver in
internal/config.go:resolveThemeTemplate():
case appconstants.ThemeTHEMENAME:
templatePath = appconstants.TemplatePathTHEMENAME
-
Update
main.go:configThemesHandler()to list the new theme -
Rebuild:
go build .
New Output Format
-
Add format constant to
appconstants/constants.go -
Add case in
internal/generator.go:GenerateFromFile():
case appconstants.OutputFormatNEW:
return g.generateNEW(action, outputDir, actionPath)
- Implement generator method:
func (g *Generator) generateNEW(action *ActionYML, outputDir, actionPath string) error {
opts := TemplateOptions{
TemplatePath: g.resolveTemplatePathForFormat(),
Format: "new",
}
// ... implementation
}
- Update CLI help text in
main.go
New Template Function
Add to internal/template.go:templateFuncs():
func templateFuncs() template.FuncMap {
return template.FuncMap{
"myFunc": myFuncImplementation,
// ... existing functions
}
}
New Parser Field
When adding fields to ActionYML:
- Update struct in
internal/parser.go - Update
ActionYMLForJSONininternal/json_writer.go(for JSON output) - Add field to JSON struct initialization
- Add tests in
internal/parser_test.go - Update templates if field should be displayed
📊 Package Structure
main.go- CLI entry point (Cobra commands)internal/generator.go- Core generation orchestrationinternal/parser.go- Action.yml parsing (including permissions from comments)internal/template.go- Template data building and renderinginternal/config.go- Configuration management (Viper)internal/json_writer.go- JSON output formatinternal/output.go- Colored CLI outputinternal/progress.go- Progress bars for batch operationsinternal/git/- Git repository detectioninternal/validation/- Action.yml validationinternal/wizard/- Interactive configuration wizardinternal/dependencies/- Dependency analysis for actionsinternal/errors/- Contextual error handlingappconstants/- Application constantstestutil/- Testing utilitiestemplates_embed/- Embedded template filesystem
🧪 Testing Guidelines
Test File Locations
- Unit tests:
internal/*_test.goalongside source files - Test fixtures:
testdata/example-action/,testdata/composite-action/ - Integration tests: Manual CLI testing with testdata
Running Specific Tests
# 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
# 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/xpackages together
Manual Updates:
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 detectiongolangci-lint- Static analysis including security checkseditorconfig-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:
// templates_embed/embed.go:ReadTemplate()
cleanPath := filepath.Clean(templatePath)
if cleanPath != templatePath || strings.Contains(cleanPath, "..") {
return nil, filepath.ErrBadPattern
}