mirror of
https://github.com/ivuorinen/gh-action-readme.git
synced 2026-01-26 11:14:04 +00:00
This commit represents a comprehensive refactoring of the codebase focused on improving code quality, testability, and maintainability. Key improvements: - Implement dependency injection and interface-based architecture - Add comprehensive test framework with fixtures and test suites - Fix all linting issues (errcheck, gosec, staticcheck, goconst, etc.) - Achieve full EditorConfig compliance across all files - Replace hardcoded test data with proper fixture files - Add configuration loader with hierarchical config support - Improve error handling with contextual information - Add progress indicators for better user feedback - Enhance Makefile with help system and improved editorconfig commands - Consolidate constants and remove deprecated code - Strengthen validation logic for GitHub Actions - Add focused consumer interfaces for better separation of concerns Testing improvements: - Add comprehensive integration tests - Implement test executor pattern for better test organization - Create extensive YAML fixture library for testing - Fix all failing tests and improve test coverage - Add validation test fixtures to avoid embedded YAML in Go files Build and tooling: - Update Makefile to show help by default - Fix editorconfig commands to use eclint properly - Add comprehensive help documentation to all make targets - Improve file selection patterns to avoid glob errors This refactoring maintains backward compatibility while significantly improving the internal architecture and developer experience.
262 lines
7.1 KiB
Go
262 lines
7.1 KiB
Go
package internal
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
)
|
|
|
|
// getVersion returns the current version - can be overridden at build time.
|
|
var getVersion = func() string {
|
|
return "0.1.0" // Default version, should be overridden at build time
|
|
}
|
|
|
|
// JSONOutput represents the structured JSON documentation output.
|
|
type JSONOutput struct {
|
|
Meta MetaInfo `json:"meta"`
|
|
Action ActionYMLForJSON `json:"action"`
|
|
Documentation DocumentationInfo `json:"documentation"`
|
|
Examples []ExampleInfo `json:"examples"`
|
|
Generated GeneratedInfo `json:"generated"`
|
|
}
|
|
|
|
// MetaInfo contains metadata about the documentation generation.
|
|
type MetaInfo struct {
|
|
Version string `json:"version"`
|
|
Format string `json:"format"`
|
|
Schema string `json:"schema"`
|
|
Generator string `json:"generator"`
|
|
}
|
|
|
|
// ActionYMLForJSON represents the action.yml data in JSON format.
|
|
type ActionYMLForJSON struct {
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
Inputs map[string]ActionInputForJSON `json:"inputs,omitempty"`
|
|
Outputs map[string]ActionOutputForJSON `json:"outputs,omitempty"`
|
|
Runs map[string]any `json:"runs"`
|
|
Branding *BrandingForJSON `json:"branding,omitempty"`
|
|
}
|
|
|
|
// ActionInputForJSON represents an input parameter in JSON format.
|
|
type ActionInputForJSON struct {
|
|
Description string `json:"description"`
|
|
Required bool `json:"required"`
|
|
Default any `json:"default,omitempty"`
|
|
}
|
|
|
|
// ActionOutputForJSON represents an output parameter in JSON format.
|
|
type ActionOutputForJSON struct {
|
|
Description string `json:"description"`
|
|
}
|
|
|
|
// BrandingForJSON represents branding information in JSON format.
|
|
type BrandingForJSON struct {
|
|
Icon string `json:"icon"`
|
|
Color string `json:"color"`
|
|
}
|
|
|
|
// DocumentationInfo contains information about the generated documentation.
|
|
type DocumentationInfo struct {
|
|
Title string `json:"title"`
|
|
Description string `json:"description"`
|
|
Usage string `json:"usage"`
|
|
Badges []BadgeInfo `json:"badges,omitempty"`
|
|
Sections []SectionInfo `json:"sections"`
|
|
Links map[string]string `json:"links"`
|
|
}
|
|
|
|
// BadgeInfo represents a documentation badge.
|
|
type BadgeInfo struct {
|
|
Name string `json:"name"`
|
|
URL string `json:"url"`
|
|
Alt string `json:"alt"`
|
|
}
|
|
|
|
// SectionInfo represents a documentation section.
|
|
type SectionInfo struct {
|
|
Title string `json:"title"`
|
|
Content string `json:"content"`
|
|
Type string `json:"type"` // "inputs", "outputs", "examples", "text"
|
|
}
|
|
|
|
// ExampleInfo represents a usage example.
|
|
type ExampleInfo struct {
|
|
Title string `json:"title"`
|
|
Description string `json:"description"`
|
|
Code string `json:"code"`
|
|
Language string `json:"language"`
|
|
}
|
|
|
|
// GeneratedInfo contains metadata about when and how the documentation was generated.
|
|
type GeneratedInfo struct {
|
|
Timestamp string `json:"timestamp"`
|
|
Tool string `json:"tool"`
|
|
Version string `json:"version"`
|
|
Theme string `json:"theme,omitempty"`
|
|
}
|
|
|
|
// JSONWriter handles JSON output generation.
|
|
type JSONWriter struct {
|
|
Config *AppConfig
|
|
}
|
|
|
|
// NewJSONWriter creates a new JSON writer.
|
|
func NewJSONWriter(config *AppConfig) *JSONWriter {
|
|
return &JSONWriter{Config: config}
|
|
}
|
|
|
|
// Write generates JSON documentation from the action data.
|
|
func (jw *JSONWriter) Write(action *ActionYML, outputPath string) error {
|
|
jsonOutput := jw.convertToJSONOutput(action)
|
|
|
|
// Marshal to JSON with indentation
|
|
data, err := json.MarshalIndent(jsonOutput, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Write to file
|
|
return os.WriteFile(outputPath, data, FilePermDefault) // #nosec G306 -- JSON output file permissions
|
|
}
|
|
|
|
// convertToJSONOutput converts ActionYML to structured JSON output.
|
|
func (jw *JSONWriter) convertToJSONOutput(action *ActionYML) *JSONOutput {
|
|
// Convert inputs
|
|
inputs := make(map[string]ActionInputForJSON)
|
|
for key, input := range action.Inputs {
|
|
inputs[key] = ActionInputForJSON(input)
|
|
}
|
|
|
|
// Convert outputs
|
|
outputs := make(map[string]ActionOutputForJSON)
|
|
for key, output := range action.Outputs {
|
|
outputs[key] = ActionOutputForJSON(output)
|
|
}
|
|
|
|
// Convert branding
|
|
var branding *BrandingForJSON
|
|
if action.Branding != nil {
|
|
branding = &BrandingForJSON{
|
|
Icon: action.Branding.Icon,
|
|
Color: action.Branding.Color,
|
|
}
|
|
}
|
|
|
|
// Generate badges
|
|
var badges []BadgeInfo
|
|
if branding != nil {
|
|
badges = append(badges, BadgeInfo{
|
|
Name: "Icon",
|
|
URL: "https://img.shields.io/badge/icon-" + branding.Icon + "-" + branding.Color,
|
|
Alt: branding.Icon,
|
|
})
|
|
}
|
|
badges = append(badges,
|
|
BadgeInfo{
|
|
Name: "GitHub Action",
|
|
URL: "https://img.shields.io/badge/GitHub%20Action-" + action.Name + "-blue",
|
|
Alt: "GitHub Action",
|
|
},
|
|
BadgeInfo{
|
|
Name: "License",
|
|
URL: "https://img.shields.io/badge/license-MIT-green",
|
|
Alt: "MIT License",
|
|
},
|
|
)
|
|
|
|
// Generate examples
|
|
examples := []ExampleInfo{
|
|
{
|
|
Title: "Basic Usage",
|
|
Description: "Basic example of using " + action.Name,
|
|
Code: jw.generateBasicExample(action),
|
|
Language: "yaml",
|
|
},
|
|
}
|
|
|
|
// Build sections
|
|
sections := []SectionInfo{
|
|
{
|
|
Title: "Overview",
|
|
Content: action.Description,
|
|
Type: "text",
|
|
},
|
|
}
|
|
|
|
if len(action.Inputs) > 0 {
|
|
sections = append(sections, SectionInfo{
|
|
Title: "Inputs",
|
|
Content: "Input parameters for this action",
|
|
Type: "inputs",
|
|
})
|
|
}
|
|
|
|
if len(action.Outputs) > 0 {
|
|
sections = append(sections, SectionInfo{
|
|
Title: "Outputs",
|
|
Content: "Output parameters from this action",
|
|
Type: "outputs",
|
|
})
|
|
}
|
|
|
|
return &JSONOutput{
|
|
Meta: MetaInfo{
|
|
Version: "1.0.0",
|
|
Format: "gh-action-readme-json",
|
|
Schema: "https://github.com/ivuorinen/gh-action-readme/schema/v1",
|
|
Generator: "gh-action-readme",
|
|
},
|
|
Action: ActionYMLForJSON{
|
|
Name: action.Name,
|
|
Description: action.Description,
|
|
Inputs: inputs,
|
|
Outputs: outputs,
|
|
Runs: action.Runs,
|
|
Branding: branding,
|
|
},
|
|
Documentation: DocumentationInfo{
|
|
Title: action.Name,
|
|
Description: action.Description,
|
|
Usage: jw.generateBasicExample(action),
|
|
Badges: badges,
|
|
Sections: sections,
|
|
Links: map[string]string{
|
|
"action.yml": "./action.yml",
|
|
"repository": "https://github.com/your-org/" + action.Name,
|
|
},
|
|
},
|
|
Examples: examples,
|
|
Generated: GeneratedInfo{
|
|
Timestamp: time.Now().UTC().Format(time.RFC3339),
|
|
Tool: "gh-action-readme",
|
|
Version: getVersion(),
|
|
Theme: jw.Config.Theme,
|
|
},
|
|
}
|
|
}
|
|
|
|
// generateBasicExample creates a basic usage example.
|
|
func (jw *JSONWriter) generateBasicExample(action *ActionYML) string {
|
|
example := "- name: " + action.Name + "\n"
|
|
example += " uses: your-org/" + action.Name + "@v1"
|
|
|
|
if len(action.Inputs) > 0 {
|
|
example += "\n with:"
|
|
for key, input := range action.Inputs {
|
|
value := "value"
|
|
if input.Default != nil {
|
|
if str, ok := input.Default.(string); ok {
|
|
value = str
|
|
} else {
|
|
value = fmt.Sprintf("%v", input.Default)
|
|
}
|
|
}
|
|
example += "\n " + key + ": \"" + value + "\""
|
|
}
|
|
}
|
|
|
|
return example
|
|
}
|