fix: improve error handling and path resolution in license generation

This commit is contained in:
0xJacky
2025-12-09 02:58:13 +00:00
parent 84e9f31f6a
commit de9783005d
8 changed files with 339 additions and 111 deletions
Binary file not shown.
+63 -9
View File
@@ -34,10 +34,16 @@ type ComponentInfo struct {
func main() {
log.Println("Generating license information...")
repoRoot, err := locateRepoRoot()
if err != nil {
log.Fatalf("Error locating repository root: %v", err)
}
log.Printf("INFO: Repository root resolved to %s", repoRoot)
var info ComponentInfo
// Generate backend licenses
backendLicenses, err := generateBackendLicenses()
backendLicenses, err := generateBackendLicenses(repoRoot)
if err != nil {
log.Printf("Error generating backend licenses: %v", err)
} else {
@@ -46,7 +52,7 @@ func main() {
}
// Generate frontend licenses
frontendLicenses, err := generateFrontendLicenses()
frontendLicenses, err := generateFrontendLicenses(repoRoot)
if err != nil {
log.Printf("Error generating frontend licenses: %v", err)
} else {
@@ -83,7 +89,7 @@ func main() {
compressed.Len(), float64(compressed.Len())/float64(len(jsonData))*100)
// Write compressed data to file
outputPath := "internal/license/licenses.xz"
outputPath := filepath.Join(repoRoot, "internal", "license", "licenses.xz")
log.Printf("INFO: Writing compressed data to %s", outputPath)
err = os.MkdirAll(filepath.Dir(outputPath), 0755)
if err != nil {
@@ -103,16 +109,19 @@ func main() {
log.Printf(" - Output file: %s", outputPath)
}
func generateBackendLicenses() ([]License, error) {
func generateBackendLicenses(repoRoot string) ([]License, error) {
var licenses []License
log.Println("INFO: Collecting backend Go modules...")
// Read go.mod file directly
goModPath := "go.mod"
goModPath := filepath.Join(repoRoot, "go.mod")
if _, err := os.Stat(goModPath); err != nil {
return nil, fmt.Errorf("failed to locate go.mod at %s: %v", goModPath, err)
}
data, err := os.ReadFile(goModPath)
if err != nil {
return nil, fmt.Errorf("failed to read go.mod: %v", err)
return nil, fmt.Errorf("failed to read go.mod at %s: %v", goModPath, err)
}
// Parse go.mod content to extract dependencies
@@ -303,13 +312,13 @@ func generateBackendLicenses() ([]License, error) {
return licenses, nil
}
func generateFrontendLicenses() ([]License, error) {
func generateFrontendLicenses(repoRoot string) ([]License, error) {
var licenses []License
log.Println("INFO: Collecting frontend npm packages...")
// Read package.json
packagePath := "app/package.json"
packagePath := filepath.Join(repoRoot, "app", "package.json")
if _, err := os.Stat(packagePath); os.IsNotExist(err) {
return nil, fmt.Errorf("package.json not found at %s", packagePath)
}
@@ -760,3 +769,48 @@ func getGoVersion() string {
return "Unknown"
}
// locateRepoRoot returns the directory containing go.mod so relative paths stay stable.
func locateRepoRoot() (string, error) {
goModPath, err := locateGoModPath()
if err != nil {
return "", err
}
return filepath.Dir(goModPath), nil
}
// locateGoModPath finds the module root so the tool works from any working directory.
func locateGoModPath() (string, error) {
// Prefer go env GOMOD which respects module-aware mode.
cmd := exec.Command("go", "env", "GOMOD")
output, err := cmd.Output()
if err == nil {
path := strings.TrimSpace(string(output))
if path != "" {
if _, statErr := os.Stat(path); statErr == nil {
return path, nil
}
}
}
// Fallback: walk upwards from the current working directory.
dir, err := os.Getwd()
if err != nil {
return "", err
}
for {
candidate := filepath.Join(dir, "go.mod")
if info, statErr := os.Stat(candidate); statErr == nil && !info.IsDir() {
return candidate, nil
}
parent := filepath.Dir(dir)
if parent == dir {
break
}
dir = parent
}
return "", fmt.Errorf("go.mod not found from current directory or parents")
}
+174
View File
@@ -0,0 +1,174 @@
//go:generate go run .
package main
import (
"flag"
"fmt"
"log"
"os"
"path/filepath"
"runtime"
"strings"
"unicode"
)
type fileReport struct {
path string
insertions int
}
var (
dryRun = flag.Bool("dry-run", false, "only report issues without modifying files")
targetDir string
rootDir string
)
func main() {
flag.Parse()
log.SetFlags(0)
if err := resolvePaths(); err != nil {
log.Fatalf("resolve paths: %v", err)
}
reports, totalInsertions, err := processDirectory(*dryRun)
if err != nil {
log.Fatalf("scan failed: %v", err)
}
if len(reports) == 0 {
log.Println("No spacing issues detected.")
return
}
for _, r := range reports {
relative := r.path
if rel, err := filepath.Rel(rootDir, r.path); err == nil {
relative = rel
}
fmt.Printf("%s: inserted %d space(s)\n", relative, r.insertions)
}
if *dryRun {
log.Printf("Dry run complete. %d potential insertion(s) across %d file(s).", totalInsertions, len(reports))
return
}
log.Printf("Completed fixes. Inserted %d space(s) across %d file(s).", totalInsertions, len(reports))
}
func resolvePaths() error {
_, file, _, ok := runtime.Caller(0)
if !ok {
return fmt.Errorf("unable to determine caller")
}
rootDir = filepath.Clean(filepath.Join(filepath.Dir(file), "../.."))
targetDir = filepath.Join(rootDir, "app/src/language")
info, err := os.Stat(targetDir)
if err != nil {
return fmt.Errorf("stat language directory: %w", err)
}
if !info.IsDir() {
return fmt.Errorf("language path is not a directory: %s", targetDir)
}
return nil
}
func processDirectory(dryRun bool) ([]fileReport, int, error) {
reports := make([]fileReport, 0)
totalInsertions := 0
err := filepath.Walk(targetDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
if !isSupportedFile(path) {
return nil
}
original, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("read %s: %w", path, err)
}
fixed, insertions := fixContent(string(original))
if insertions == 0 {
return nil
}
if !dryRun {
if err := os.WriteFile(path, []byte(fixed), info.Mode().Perm()); err != nil {
return fmt.Errorf("write %s: %w", path, err)
}
}
reports = append(reports, fileReport{
path: path,
insertions: insertions,
})
totalInsertions += insertions
return nil
})
return reports, totalInsertions, err
}
func isSupportedFile(path string) bool {
switch strings.ToLower(filepath.Ext(path)) {
case ".po", ".pot", ".ts":
return true
default:
return false
}
}
func fixContent(text string) (string, int) {
runes := []rune(text)
if len(runes) == 0 {
return text, 0
}
var builder strings.Builder
builder.Grow(len(runes) + 16)
insertions := 0
for i := 0; i < len(runes); i++ {
current := runes[i]
builder.WriteRune(current)
if i == len(runes)-1 {
break
}
next := runes[i+1]
if needsSpace(current, next) {
builder.WriteRune(' ')
insertions++
}
}
return builder.String(), insertions
}
func needsSpace(left, right rune) bool {
if isHan(left) && isASCIIAlphaNum(right) {
return true
}
if isASCIIAlphaNum(left) && isHan(right) {
return true
}
return false
}
func isHan(r rune) bool {
return unicode.Is(unicode.Han, r)
}
func isASCIIAlphaNum(r rune) bool {
return r <= unicode.MaxASCII && ((r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9'))
}
Binary file not shown.