2026-01-30 00:22:47 +00:00
|
|
|
package gocmd
|
|
|
|
|
|
|
|
|
|
import (
|
refactor(cli): move commands from cmd/ to pkg/ with self-registration
Implements defence in depth through build variants - only compiled code
exists in the binary. Commands now self-register via cli.RegisterCommands()
in their init() functions, mirroring the i18n.RegisterLocales() pattern.
Structure changes:
- cmd/{ai,build,ci,dev,docs,doctor,go,php,pkg,sdk,setup,test,vm}/ → pkg/*/cmd_*.go
- cmd/core_dev.go, cmd/core_ci.go → cmd/variants/{full,ci,php,minimal}.go
- Added pkg/cli/commands.go with RegisterCommands API
- Updated pkg/cli/runtime.go to attach registered commands
Build variants:
- go build → full (21MB, all 13 command groups)
- go build -tags ci → ci (18MB, build/ci/sdk/doctor)
- go build -tags php → php (14MB, php/doctor)
- go build -tags minimal → minimal (11MB, doctor only)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 21:55:55 +00:00
|
|
|
"errors"
|
2026-01-30 00:22:47 +00:00
|
|
|
"fmt"
|
|
|
|
|
"os"
|
|
|
|
|
"os/exec"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"regexp"
|
|
|
|
|
"strings"
|
|
|
|
|
|
2026-01-30 10:32:05 +00:00
|
|
|
"github.com/host-uk/core/pkg/cli"
|
feat(i18n): add translation keys to all CLI commands
Replace hardcoded strings with i18n.T() calls across all cmd/* packages:
- ai, build, ci, dev, docs, doctor, go, php, pkg, sdk, setup, test, vm
Adds 500+ translation keys to en.json for command descriptions,
flag descriptions, labels, messages, and error strings.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 02:37:57 +00:00
|
|
|
"github.com/host-uk/core/pkg/i18n"
|
2026-01-30 00:22:47 +00:00
|
|
|
)
|
|
|
|
|
|
2026-01-30 00:47:54 +00:00
|
|
|
var (
|
|
|
|
|
testCoverage bool
|
|
|
|
|
testPkg string
|
|
|
|
|
testRun string
|
|
|
|
|
testShort bool
|
|
|
|
|
testRace bool
|
|
|
|
|
testJSON bool
|
|
|
|
|
testVerbose bool
|
|
|
|
|
)
|
|
|
|
|
|
2026-01-31 11:39:19 +00:00
|
|
|
func addGoTestCommand(parent *cli.Command) {
|
|
|
|
|
testCmd := &cli.Command{
|
2026-01-30 00:47:54 +00:00
|
|
|
Use: "test",
|
2026-01-30 22:43:39 +00:00
|
|
|
Short: "Run Go tests",
|
|
|
|
|
Long: "Run Go tests with optional coverage, filtering, and race detection",
|
2026-01-31 11:39:19 +00:00
|
|
|
RunE: func(cmd *cli.Command, args []string) error {
|
2026-01-30 00:47:54 +00:00
|
|
|
return runGoTest(testCoverage, testPkg, testRun, testShort, testRace, testJSON, testVerbose)
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-30 22:43:39 +00:00
|
|
|
testCmd.Flags().BoolVar(&testCoverage, "coverage", false, "Generate coverage report")
|
|
|
|
|
testCmd.Flags().StringVar(&testPkg, "pkg", "", "Package to test")
|
|
|
|
|
testCmd.Flags().StringVar(&testRun, "run", "", "Run only tests matching pattern")
|
|
|
|
|
testCmd.Flags().BoolVar(&testShort, "short", false, "Run only short tests")
|
|
|
|
|
testCmd.Flags().BoolVar(&testRace, "race", false, "Enable race detector")
|
|
|
|
|
testCmd.Flags().BoolVar(&testJSON, "json", false, "Output as JSON")
|
|
|
|
|
testCmd.Flags().BoolVarP(&testVerbose, "verbose", "v", false, "Verbose output")
|
2026-01-30 00:47:54 +00:00
|
|
|
|
|
|
|
|
parent.AddCommand(testCmd)
|
2026-01-30 00:22:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func runGoTest(coverage bool, pkg, run string, short, race, jsonOut, verbose bool) error {
|
|
|
|
|
if pkg == "" {
|
|
|
|
|
pkg = "./..."
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
args := []string{"test"}
|
|
|
|
|
|
|
|
|
|
if coverage {
|
|
|
|
|
args = append(args, "-cover")
|
|
|
|
|
} else {
|
|
|
|
|
args = append(args, "-cover")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if run != "" {
|
|
|
|
|
args = append(args, "-run", run)
|
|
|
|
|
}
|
|
|
|
|
if short {
|
|
|
|
|
args = append(args, "-short")
|
|
|
|
|
}
|
|
|
|
|
if race {
|
|
|
|
|
args = append(args, "-race")
|
|
|
|
|
}
|
|
|
|
|
if verbose {
|
|
|
|
|
args = append(args, "-v")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
args = append(args, pkg)
|
|
|
|
|
|
|
|
|
|
if !jsonOut {
|
2026-01-31 11:39:19 +00:00
|
|
|
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("test")), i18n.ProgressSubject("run", "tests"))
|
|
|
|
|
cli.Print(" %s %s\n", dimStyle.Render(i18n.Label("package")), pkg)
|
2026-01-31 23:36:43 +00:00
|
|
|
cli.Blank()
|
2026-01-30 00:22:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cmd := exec.Command("go", args...)
|
|
|
|
|
cmd.Env = append(os.Environ(), "MACOSX_DEPLOYMENT_TARGET=26.0", "CGO_ENABLED=0")
|
|
|
|
|
cmd.Dir, _ = os.Getwd()
|
|
|
|
|
|
|
|
|
|
output, err := cmd.CombinedOutput()
|
|
|
|
|
outputStr := string(output)
|
|
|
|
|
|
|
|
|
|
// Filter linker warnings
|
|
|
|
|
lines := strings.Split(outputStr, "\n")
|
|
|
|
|
var filtered []string
|
|
|
|
|
for _, line := range lines {
|
|
|
|
|
if !strings.Contains(line, "ld: warning:") {
|
|
|
|
|
filtered = append(filtered, line)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
outputStr = strings.Join(filtered, "\n")
|
|
|
|
|
|
|
|
|
|
// Parse results
|
|
|
|
|
passed, failed, skipped := parseTestResults(outputStr)
|
|
|
|
|
cov := parseOverallCoverage(outputStr)
|
|
|
|
|
|
|
|
|
|
if jsonOut {
|
2026-01-31 11:39:19 +00:00
|
|
|
cli.Print(`{"passed":%d,"failed":%d,"skipped":%d,"coverage":%.1f,"exit_code":%d}`,
|
2026-01-30 00:22:47 +00:00
|
|
|
passed, failed, skipped, cov, cmd.ProcessState.ExitCode())
|
2026-01-31 23:36:43 +00:00
|
|
|
cli.Blank()
|
2026-01-30 00:22:47 +00:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Print filtered output if verbose or failed
|
|
|
|
|
if verbose || err != nil {
|
2026-01-31 11:39:19 +00:00
|
|
|
cli.Text(outputStr)
|
2026-01-30 00:22:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Summary
|
|
|
|
|
if err == nil {
|
2026-01-31 23:36:43 +00:00
|
|
|
cli.Print(" %s %s\n", successStyle.Render(cli.Glyph(":check:")), i18n.T("i18n.count.test", passed)+" "+i18n.T("i18n.done.pass"))
|
2026-01-30 00:22:47 +00:00
|
|
|
} else {
|
2026-01-31 23:36:43 +00:00
|
|
|
cli.Print(" %s %s, %s\n", errorStyle.Render(cli.Glyph(":cross:")),
|
2026-01-30 22:43:39 +00:00
|
|
|
i18n.T("i18n.count.test", passed)+" "+i18n.T("i18n.done.pass"),
|
|
|
|
|
i18n.T("i18n.count.test", failed)+" "+i18n.T("i18n.done.fail"))
|
2026-01-30 00:22:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if cov > 0 {
|
2026-01-31 23:36:43 +00:00
|
|
|
cli.Print("\n %s %s\n", cli.KeyStyle.Render(i18n.Label("coverage")), formatCoverage(cov))
|
2026-01-30 00:22:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err == nil {
|
2026-01-31 11:39:19 +00:00
|
|
|
cli.Print("\n%s\n", successStyle.Render(i18n.T("i18n.done.pass")))
|
2026-01-30 00:22:47 +00:00
|
|
|
} else {
|
2026-01-31 11:39:19 +00:00
|
|
|
cli.Print("\n%s\n", errorStyle.Render(i18n.T("i18n.done.fail")))
|
2026-01-30 00:22:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func parseTestResults(output string) (passed, failed, skipped int) {
|
|
|
|
|
passRe := regexp.MustCompile(`(?m)^ok\s+`)
|
|
|
|
|
failRe := regexp.MustCompile(`(?m)^FAIL\s+`)
|
|
|
|
|
skipRe := regexp.MustCompile(`(?m)^\?\s+`)
|
|
|
|
|
|
|
|
|
|
passed = len(passRe.FindAllString(output, -1))
|
|
|
|
|
failed = len(failRe.FindAllString(output, -1))
|
|
|
|
|
skipped = len(skipRe.FindAllString(output, -1))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func parseOverallCoverage(output string) float64 {
|
|
|
|
|
re := regexp.MustCompile(`coverage:\s+([\d.]+)%`)
|
|
|
|
|
matches := re.FindAllStringSubmatch(output, -1)
|
|
|
|
|
if len(matches) == 0 {
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var total float64
|
|
|
|
|
for _, m := range matches {
|
|
|
|
|
var cov float64
|
feat: infrastructure packages and lint cleanup (#281)
* ci: consolidate duplicate workflows and merge CodeQL configs
Remove 17 duplicate workflow files that were split copies of the
combined originals. Each family (CI, CodeQL, Coverage, PR Build,
Alpha Release) had the same job duplicated across separate
push/pull_request/schedule/manual trigger files.
Merge codeql.yml and codescan.yml into a single codeql.yml with
a language matrix covering go, javascript-typescript, python,
and actions — matching the previous default setup coverage.
Remaining workflows (one per family):
- ci.yml (push + PR + manual)
- codeql.yml (push + PR + schedule, all languages)
- coverage.yml (push + PR + manual)
- alpha-release.yml (push + manual)
- pr-build.yml (PR + manual)
- release.yml (tag push)
- agent-verify.yml, auto-label.yml, auto-project.yml
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add collect, config, crypt, plugin packages and fix all lint issues
Add four new infrastructure packages with CLI commands:
- pkg/config: layered configuration (defaults → file → env → flags)
- pkg/crypt: crypto primitives (Argon2id, AES-GCM, ChaCha20, HMAC, checksums)
- pkg/plugin: plugin system with GitHub-based install/update/remove
- pkg/collect: collection subsystem (GitHub, BitcoinTalk, market, papers, excavate)
Fix all golangci-lint issues across the entire codebase (~100 errcheck,
staticcheck SA1012/SA1019/ST1005, unused, ineffassign fixes) so that
`core go qa` passes with 0 issues.
Closes #167, #168, #170, #250, #251, #252, #253, #254, #255, #256
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:34:43 +00:00
|
|
|
_, _ = fmt.Sscanf(m[1], "%f", &cov)
|
2026-01-30 00:22:47 +00:00
|
|
|
total += cov
|
|
|
|
|
}
|
|
|
|
|
return total / float64(len(matches))
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-30 00:47:54 +00:00
|
|
|
var (
|
|
|
|
|
covPkg string
|
|
|
|
|
covHTML bool
|
|
|
|
|
covOpen bool
|
|
|
|
|
covThreshold float64
|
|
|
|
|
)
|
|
|
|
|
|
2026-01-31 11:39:19 +00:00
|
|
|
func addGoCovCommand(parent *cli.Command) {
|
|
|
|
|
covCmd := &cli.Command{
|
2026-01-30 00:47:54 +00:00
|
|
|
Use: "cov",
|
2026-01-30 22:43:39 +00:00
|
|
|
Short: "Run tests with coverage report",
|
|
|
|
|
Long: "Run tests with detailed coverage reports, HTML output, and threshold checking",
|
2026-01-31 11:39:19 +00:00
|
|
|
RunE: func(cmd *cli.Command, args []string) error {
|
2026-01-30 00:47:54 +00:00
|
|
|
pkg := covPkg
|
|
|
|
|
if pkg == "" {
|
|
|
|
|
// Auto-discover packages with tests
|
|
|
|
|
pkgs, err := findTestPackages(".")
|
|
|
|
|
if err != nil {
|
2026-01-31 11:39:19 +00:00
|
|
|
return cli.Wrap(err, i18n.T("i18n.fail.find", "test packages"))
|
2026-01-30 00:47:54 +00:00
|
|
|
}
|
|
|
|
|
if len(pkgs) == 0 {
|
2026-01-30 22:43:39 +00:00
|
|
|
return errors.New("no test packages found")
|
2026-01-30 00:47:54 +00:00
|
|
|
}
|
|
|
|
|
pkg = strings.Join(pkgs, " ")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create temp file for coverage data
|
|
|
|
|
covFile, err := os.CreateTemp("", "coverage-*.out")
|
2026-01-30 00:22:47 +00:00
|
|
|
if err != nil {
|
2026-01-31 11:39:19 +00:00
|
|
|
return cli.Wrap(err, i18n.T("i18n.fail.create", "coverage file"))
|
2026-01-30 00:22:47 +00:00
|
|
|
}
|
2026-01-30 00:47:54 +00:00
|
|
|
covPath := covFile.Name()
|
feat: infrastructure packages and lint cleanup (#281)
* ci: consolidate duplicate workflows and merge CodeQL configs
Remove 17 duplicate workflow files that were split copies of the
combined originals. Each family (CI, CodeQL, Coverage, PR Build,
Alpha Release) had the same job duplicated across separate
push/pull_request/schedule/manual trigger files.
Merge codeql.yml and codescan.yml into a single codeql.yml with
a language matrix covering go, javascript-typescript, python,
and actions — matching the previous default setup coverage.
Remaining workflows (one per family):
- ci.yml (push + PR + manual)
- codeql.yml (push + PR + schedule, all languages)
- coverage.yml (push + PR + manual)
- alpha-release.yml (push + manual)
- pr-build.yml (PR + manual)
- release.yml (tag push)
- agent-verify.yml, auto-label.yml, auto-project.yml
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add collect, config, crypt, plugin packages and fix all lint issues
Add four new infrastructure packages with CLI commands:
- pkg/config: layered configuration (defaults → file → env → flags)
- pkg/crypt: crypto primitives (Argon2id, AES-GCM, ChaCha20, HMAC, checksums)
- pkg/plugin: plugin system with GitHub-based install/update/remove
- pkg/collect: collection subsystem (GitHub, BitcoinTalk, market, papers, excavate)
Fix all golangci-lint issues across the entire codebase (~100 errcheck,
staticcheck SA1012/SA1019/ST1005, unused, ineffassign fixes) so that
`core go qa` passes with 0 issues.
Closes #167, #168, #170, #250, #251, #252, #253, #254, #255, #256
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:34:43 +00:00
|
|
|
_ = covFile.Close()
|
|
|
|
|
defer func() { _ = os.Remove(covPath) }()
|
2026-01-30 00:47:54 +00:00
|
|
|
|
2026-01-31 11:39:19 +00:00
|
|
|
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("coverage")), i18n.ProgressSubject("run", "tests"))
|
2026-01-30 00:47:54 +00:00
|
|
|
// Truncate package list if too long for display
|
|
|
|
|
displayPkg := pkg
|
|
|
|
|
if len(displayPkg) > 60 {
|
|
|
|
|
displayPkg = displayPkg[:57] + "..."
|
2026-01-30 00:22:47 +00:00
|
|
|
}
|
2026-01-31 11:39:19 +00:00
|
|
|
cli.Print(" %s %s\n", dimStyle.Render(i18n.Label("package")), displayPkg)
|
2026-01-31 23:36:43 +00:00
|
|
|
cli.Blank()
|
2026-01-30 00:22:47 +00:00
|
|
|
|
2026-01-30 00:47:54 +00:00
|
|
|
// Run tests with coverage
|
|
|
|
|
// We need to split pkg into individual arguments if it contains spaces
|
|
|
|
|
pkgArgs := strings.Fields(pkg)
|
|
|
|
|
cmdArgs := append([]string{"test", "-coverprofile=" + covPath, "-covermode=atomic"}, pkgArgs...)
|
2026-01-30 00:22:47 +00:00
|
|
|
|
2026-01-30 00:47:54 +00:00
|
|
|
goCmd := exec.Command("go", cmdArgs...)
|
|
|
|
|
goCmd.Env = append(os.Environ(), "MACOSX_DEPLOYMENT_TARGET=26.0")
|
|
|
|
|
goCmd.Stdout = os.Stdout
|
|
|
|
|
goCmd.Stderr = os.Stderr
|
2026-01-30 00:22:47 +00:00
|
|
|
|
2026-01-30 00:47:54 +00:00
|
|
|
testErr := goCmd.Run()
|
2026-01-30 00:22:47 +00:00
|
|
|
|
2026-01-30 00:47:54 +00:00
|
|
|
// Get coverage percentage
|
|
|
|
|
coverCmd := exec.Command("go", "tool", "cover", "-func="+covPath)
|
|
|
|
|
covOutput, err := coverCmd.Output()
|
|
|
|
|
if err != nil {
|
|
|
|
|
if testErr != nil {
|
|
|
|
|
return testErr
|
|
|
|
|
}
|
2026-01-31 11:39:19 +00:00
|
|
|
return cli.Wrap(err, i18n.T("i18n.fail.get", "coverage"))
|
2026-01-30 00:22:47 +00:00
|
|
|
}
|
|
|
|
|
|
2026-01-30 00:47:54 +00:00
|
|
|
// Parse total coverage from last line
|
|
|
|
|
lines := strings.Split(strings.TrimSpace(string(covOutput)), "\n")
|
|
|
|
|
var totalCov float64
|
|
|
|
|
if len(lines) > 0 {
|
|
|
|
|
lastLine := lines[len(lines)-1]
|
|
|
|
|
// Format: "total: (statements) XX.X%"
|
|
|
|
|
if strings.Contains(lastLine, "total:") {
|
|
|
|
|
parts := strings.Fields(lastLine)
|
|
|
|
|
if len(parts) >= 3 {
|
|
|
|
|
covStr := strings.TrimSuffix(parts[len(parts)-1], "%")
|
feat: infrastructure packages and lint cleanup (#281)
* ci: consolidate duplicate workflows and merge CodeQL configs
Remove 17 duplicate workflow files that were split copies of the
combined originals. Each family (CI, CodeQL, Coverage, PR Build,
Alpha Release) had the same job duplicated across separate
push/pull_request/schedule/manual trigger files.
Merge codeql.yml and codescan.yml into a single codeql.yml with
a language matrix covering go, javascript-typescript, python,
and actions — matching the previous default setup coverage.
Remaining workflows (one per family):
- ci.yml (push + PR + manual)
- codeql.yml (push + PR + schedule, all languages)
- coverage.yml (push + PR + manual)
- alpha-release.yml (push + manual)
- pr-build.yml (PR + manual)
- release.yml (tag push)
- agent-verify.yml, auto-label.yml, auto-project.yml
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add collect, config, crypt, plugin packages and fix all lint issues
Add four new infrastructure packages with CLI commands:
- pkg/config: layered configuration (defaults → file → env → flags)
- pkg/crypt: crypto primitives (Argon2id, AES-GCM, ChaCha20, HMAC, checksums)
- pkg/plugin: plugin system with GitHub-based install/update/remove
- pkg/collect: collection subsystem (GitHub, BitcoinTalk, market, papers, excavate)
Fix all golangci-lint issues across the entire codebase (~100 errcheck,
staticcheck SA1012/SA1019/ST1005, unused, ineffassign fixes) so that
`core go qa` passes with 0 issues.
Closes #167, #168, #170, #250, #251, #252, #253, #254, #255, #256
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:34:43 +00:00
|
|
|
_, _ = fmt.Sscanf(covStr, "%f", &totalCov)
|
2026-01-30 00:47:54 +00:00
|
|
|
}
|
2026-01-30 00:22:47 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-30 00:47:54 +00:00
|
|
|
// Print coverage summary
|
2026-01-31 23:36:43 +00:00
|
|
|
cli.Blank()
|
|
|
|
|
cli.Print(" %s %s\n", cli.KeyStyle.Render(i18n.Label("total")), formatCoverage(totalCov))
|
2026-01-30 00:47:54 +00:00
|
|
|
|
|
|
|
|
// Generate HTML if requested
|
|
|
|
|
if covHTML || covOpen {
|
|
|
|
|
htmlPath := "coverage.html"
|
|
|
|
|
htmlCmd := exec.Command("go", "tool", "cover", "-html="+covPath, "-o="+htmlPath)
|
|
|
|
|
if err := htmlCmd.Run(); err != nil {
|
2026-01-31 11:39:19 +00:00
|
|
|
return cli.Wrap(err, i18n.T("i18n.fail.generate", "HTML"))
|
2026-01-30 00:22:47 +00:00
|
|
|
}
|
2026-01-31 11:39:19 +00:00
|
|
|
cli.Print(" %s %s\n", dimStyle.Render(i18n.Label("html")), htmlPath)
|
2026-01-30 00:47:54 +00:00
|
|
|
|
|
|
|
|
if covOpen {
|
|
|
|
|
// Open in browser
|
|
|
|
|
var openCmd *exec.Cmd
|
|
|
|
|
switch {
|
|
|
|
|
case exec.Command("which", "open").Run() == nil:
|
|
|
|
|
openCmd = exec.Command("open", htmlPath)
|
|
|
|
|
case exec.Command("which", "xdg-open").Run() == nil:
|
|
|
|
|
openCmd = exec.Command("xdg-open", htmlPath)
|
|
|
|
|
default:
|
2026-01-31 11:39:19 +00:00
|
|
|
cli.Print(" %s\n", dimStyle.Render("Open coverage.html in your browser"))
|
2026-01-30 00:47:54 +00:00
|
|
|
}
|
|
|
|
|
if openCmd != nil {
|
feat: infrastructure packages and lint cleanup (#281)
* ci: consolidate duplicate workflows and merge CodeQL configs
Remove 17 duplicate workflow files that were split copies of the
combined originals. Each family (CI, CodeQL, Coverage, PR Build,
Alpha Release) had the same job duplicated across separate
push/pull_request/schedule/manual trigger files.
Merge codeql.yml and codescan.yml into a single codeql.yml with
a language matrix covering go, javascript-typescript, python,
and actions — matching the previous default setup coverage.
Remaining workflows (one per family):
- ci.yml (push + PR + manual)
- codeql.yml (push + PR + schedule, all languages)
- coverage.yml (push + PR + manual)
- alpha-release.yml (push + manual)
- pr-build.yml (PR + manual)
- release.yml (tag push)
- agent-verify.yml, auto-label.yml, auto-project.yml
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add collect, config, crypt, plugin packages and fix all lint issues
Add four new infrastructure packages with CLI commands:
- pkg/config: layered configuration (defaults → file → env → flags)
- pkg/crypt: crypto primitives (Argon2id, AES-GCM, ChaCha20, HMAC, checksums)
- pkg/plugin: plugin system with GitHub-based install/update/remove
- pkg/collect: collection subsystem (GitHub, BitcoinTalk, market, papers, excavate)
Fix all golangci-lint issues across the entire codebase (~100 errcheck,
staticcheck SA1012/SA1019/ST1005, unused, ineffassign fixes) so that
`core go qa` passes with 0 issues.
Closes #167, #168, #170, #250, #251, #252, #253, #254, #255, #256
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:34:43 +00:00
|
|
|
_ = openCmd.Run()
|
2026-01-30 00:47:54 +00:00
|
|
|
}
|
2026-01-30 00:22:47 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-30 00:47:54 +00:00
|
|
|
// Check threshold
|
|
|
|
|
if covThreshold > 0 && totalCov < covThreshold {
|
2026-01-31 11:39:19 +00:00
|
|
|
cli.Print("\n%s %.1f%% < %.1f%%\n", errorStyle.Render(i18n.T("i18n.fail.meet", "threshold")), totalCov, covThreshold)
|
2026-01-30 22:43:39 +00:00
|
|
|
return errors.New("coverage below threshold")
|
2026-01-30 00:47:54 +00:00
|
|
|
}
|
2026-01-30 00:22:47 +00:00
|
|
|
|
2026-01-30 00:47:54 +00:00
|
|
|
if testErr != nil {
|
|
|
|
|
return testErr
|
|
|
|
|
}
|
2026-01-30 00:22:47 +00:00
|
|
|
|
2026-01-31 11:39:19 +00:00
|
|
|
cli.Print("\n%s\n", successStyle.Render(i18n.T("i18n.done.pass")))
|
2026-01-30 00:47:54 +00:00
|
|
|
return nil
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-30 22:43:39 +00:00
|
|
|
covCmd.Flags().StringVar(&covPkg, "pkg", "", "Package to test")
|
|
|
|
|
covCmd.Flags().BoolVar(&covHTML, "html", false, "Generate HTML report")
|
|
|
|
|
covCmd.Flags().BoolVar(&covOpen, "open", false, "Open HTML report in browser")
|
|
|
|
|
covCmd.Flags().Float64Var(&covThreshold, "threshold", 0, "Minimum coverage percentage")
|
2026-01-30 00:47:54 +00:00
|
|
|
|
|
|
|
|
parent.AddCommand(covCmd)
|
2026-01-30 00:22:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func findTestPackages(root string) ([]string, error) {
|
|
|
|
|
pkgMap := make(map[string]bool)
|
|
|
|
|
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if !info.IsDir() && strings.HasSuffix(info.Name(), "_test.go") {
|
|
|
|
|
dir := filepath.Dir(path)
|
|
|
|
|
if !strings.HasPrefix(dir, ".") {
|
|
|
|
|
dir = "./" + dir
|
|
|
|
|
}
|
|
|
|
|
pkgMap[dir] = true
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var pkgs []string
|
|
|
|
|
for pkg := range pkgMap {
|
|
|
|
|
pkgs = append(pkgs, pkg)
|
|
|
|
|
}
|
|
|
|
|
return pkgs, nil
|
|
|
|
|
}
|
2026-01-31 23:36:43 +00:00
|
|
|
|
|
|
|
|
func formatCoverage(cov float64) string {
|
|
|
|
|
s := fmt.Sprintf("%.1f%%", cov)
|
|
|
|
|
if cov >= 80 {
|
|
|
|
|
return cli.SuccessStyle.Render(s)
|
|
|
|
|
} else if cov >= 50 {
|
|
|
|
|
return cli.WarningStyle.Render(s)
|
|
|
|
|
}
|
|
|
|
|
return cli.ErrorStyle.Render(s)
|
|
|
|
|
}
|