Conversation
89c40a2 to
5d2db9a
Compare
Unit Test Results 1 files 1 suites 24s ⏱️ Results for commit 44a0e25. ♻️ This comment has been updated with latest results. |
9c0ce9d to
c0759d9
Compare
c0759d9 to
2859640
Compare
2859640 to
b7a33e4
Compare
49ab5a6 to
7cb6a6a
Compare
There was a problem hiding this comment.
Pull request overview
This PR introduces the initial Go “foundation” alongside the existing C# CLI, establishing core shared packages, three Cobra-based CLI entrypoints, and build/test/lint automation to support an incremental port.
Changes:
- Added core Go packages for logging, retry policy, filesystem, env access, and an app container (manual DI) with initial unit tests.
- Added Go module files plus developer docs and golangci-lint configuration.
- Added
justtargets and a dedicated GitHub Actions workflow for Go CI.
Show a summary per file
| File | Description |
|---|---|
| pkg/retry/retry.go | New retry policy wrapper + helpers (HTTP retryable statuses, generic ExecuteWithResult). |
| pkg/retry/retry_test.go | Unit tests for retry policy behavior and helpers. |
| pkg/logger/logger.go | New logger implementation with warning counting and verbose output. |
| pkg/logger/logger_test.go | Unit tests for logger output and warning counting. |
| pkg/filesystem/filesystem.go | Filesystem provider abstraction (read/write/copy/temp helpers). |
| pkg/env/env.go | Environment variable provider for CLI configuration inputs. |
| pkg/app/app.go | Manual DI “App” container for shared dependencies. |
| pkg/app/app_test.go | Unit tests for App initialization and defaults. |
| cmd/gei/main.go | Cobra root command skeleton for gei (logger wiring + placeholders). |
| cmd/ado2gh/main.go | Cobra root command skeleton for ado2gh (logger wiring + placeholders). |
| cmd/bbs2gh/main.go | Cobra root command skeleton for bbs2gh (logger wiring + placeholders). |
| go.mod | Initializes Go module and declares initial dependencies. |
| go.sum | Locks Go dependency checksums. |
| justfile | Adds Go build/test/lint/publish targets and combined CI target. |
| .github/workflows/go-ci.yml | Adds Go CI workflow (build/test/coverage/lint). |
| .golangci.yml | Adds golangci-lint v2 configuration for the Go code. |
| GO_DEVELOPMENT.md | Adds Go port development guide and workflow documentation. |
| .gitignore | Ignores Go coverage artifacts and .out files. |
| .github/workflows/CI.yml | Minor matrix formatting adjustment (whitespace). |
Copilot's findings
- Files reviewed: 16/19 changed files
- Comments generated: 10
| require ( | ||
| github.com/avast/retry-go/v4 v4.7.0 | ||
| github.com/spf13/cobra v1.10.2 | ||
| ) |
There was a problem hiding this comment.
New third-party Go dependencies are introduced here (e.g., github.com/spf13/cobra, github.com/avast/retry-go/v4), but ThirdPartyNotices.txt does not currently mention them. If this repo’s compliance process requires it, add the corresponding license notices.
| return err | ||
| }) | ||
| if err != nil { | ||
| return result, fmt.Errorf("retry failed after %d attempts: %w", p.maxAttempts, lastErr) |
There was a problem hiding this comment.
ExecuteWithResult wraps the error using lastErr, but when the retry loop fails due to context cancellation/deadline, lastErr can be unrelated to the actual failure reason. Consider wrapping err from p.Execute (and optionally include lastErr as additional context) so callers can reliably detect context errors.
| return result, fmt.Errorf("retry failed after %d attempts: %w", p.maxAttempts, lastErr) | |
| if lastErr != nil && lastErr != err { | |
| return result, fmt.Errorf("retry failed after %d attempts (last operation error: %v): %w", p.maxAttempts, lastErr, err) | |
| } | |
| return result, fmt.Errorf("retry failed after %d attempts: %w", p.maxAttempts, err) |
| for _, tt := range tests { | ||
| t.Run(string(rune(tt.statusCode)), func(t *testing.T) { | ||
| got := retry.IsRetryableHTTPStatus(tt.statusCode) |
There was a problem hiding this comment.
The subtest name uses string(rune(tt.statusCode)), which turns status codes into non-obvious single-rune names (e.g., 200 -> "È") and can make test output hard to interpret. Prefer using a decimal string (e.g., via strconv.Itoa or fmt.Sprintf).
| go-publish-linux: | ||
| mkdir -p dist/linux-x64 | ||
| GOOS=linux GOARCH=amd64 go build -o dist/linux-x64/gei-linux-amd64 ./cmd/gei | ||
| GOOS=linux GOARCH=amd64 go build -o dist/linux-x64/ado2gh-linux-amd64 ./cmd/ado2gh | ||
| GOOS=linux GOARCH=amd64 go build -o dist/linux-x64/bbs2gh-linux-amd64 ./cmd/bbs2gh | ||
| GOOS=linux GOARCH=arm64 go build -o dist/linux-arm64/gei-linux-arm64 ./cmd/gei | ||
| GOOS=linux GOARCH=arm64 go build -o dist/linux-arm64/ado2gh-linux-arm64 ./cmd/ado2gh | ||
| GOOS=linux GOARCH=arm64 go build -o dist/linux-arm64/bbs2gh-linux-arm64 ./cmd/bbs2gh |
There was a problem hiding this comment.
go-publish-linux builds outputs into dist/linux-arm64/... but only creates dist/linux-x64. Without mkdir -p dist/linux-arm64, the arm64 builds will fail when writing the output file.
| // Debug logs a debug message | ||
| func (l *Logger) Debug(format string, args ...interface{}) { | ||
| l.log("[DEBUG]", format, args...) |
There was a problem hiding this comment.
Debug logging is currently always emitted (it calls l.log(...) unconditionally). Since the CLIs call log.Debug("Execution started") in PersistentPreRun, this will print debug output even when --verbose is false. Consider gating Debug behind l.verbose (or a dedicated debug flag/env var) so normal runs remain quiet.
| // Debug logs a debug message | |
| func (l *Logger) Debug(format string, args ...interface{}) { | |
| l.log("[DEBUG]", format, args...) | |
| // Debug logs a debug message when verbose mode is enabled | |
| func (l *Logger) Debug(format string, args ...interface{}) { | |
| if l.verbose { | |
| l.log("[DEBUG]", format, args...) | |
| } |
| func (l *Logger) LogWarningCount() { | ||
| count := l.GetWarningCount() | ||
| if count > 0 { | ||
| l.Warning("Total warnings: %d", count) |
There was a problem hiding this comment.
LogWarningCount logs via l.Warning(...), which increments warningCount. That mutates the warning count and can over-count warnings (and changes the value returned by GetWarningCount). Consider logging the summary without incrementing the warning counter (e.g., using an internal log method or Info).
| l.Warning("Total warnings: %d", count) | |
| l.log("[WARNING]", "Total warnings: %d", count) |
| destination, err := os.Create(dst) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| defer destination.Close() | ||
|
|
||
| _, err = io.Copy(destination, source) | ||
| return err |
There was a problem hiding this comment.
CopyFile uses os.Create(dst), which creates the destination with default permissions (subject to umask) and may produce world-readable files. Since this CLI can handle credentials/tokens, consider creating the file with a restrictive mode (e.g., 0600) or preserving the source mode to avoid accidentally exposing sensitive output.
| # ============================================================================ | ||
|
|
||
| # Build Go binaries | ||
| go-build: |
There was a problem hiding this comment.
go-build writes binaries to dist/... but doesn't ensure the dist directory exists. On a clean checkout, go build -o dist/gei ... will fail. Consider adding mkdir -p dist (or platform-appropriate equivalent) before building.
| go-build: | |
| go-build: | |
| mkdir -p dist |
| ./dist/gei --version | ||
| ./dist/ado2gh --version | ||
| ./dist/bbs2gh --version |
There was a problem hiding this comment.
On Windows runners, the go build -o dist/gei ./cmd/gei output is typically dist/gei.exe, so invoking ./dist/gei --version will fail. Consider making this step OS-aware (use .exe on Windows) or derive the executable suffix from go env GOEXE.
| ./dist/gei --version | |
| ./dist/ado2gh --version | |
| ./dist/bbs2gh --version | |
| goexe="$(go env GOEXE)" | |
| ./dist/gei${goexe} --version | |
| ./dist/ado2gh${goexe} --version | |
| ./dist/bbs2gh${goexe} --version |
| go 1.25.4 | ||
|
|
There was a problem hiding this comment.
The go directive is set to 1.25.4. The Go toolchain typically expects the go directive to be a language version (major.minor, or major.minor.0), with patch versions specified via a separate toolchain directive if needed. Consider adjusting this to avoid go mod / setup-go parsing failures.
| go 1.25.4 | |
| go 1.25.0 | |
| toolchain go1.25.4 |
Begin porting this CLI to go - Justfile targets: `just go-build`, `go-test`, etc... `just ci-all` to run CI for both. - Conventional `pkg/` and `cmd/` layout - depends on the cobra CLI interface - linter rules - integrated go CI The eventual result will be a -beta CLI version in parallel with the C# CLI
7cb6a6a to
44a0e25
Compare
Begin porting this CLI to go
just go-build,go-test, etc...just ci-allto run CI for both.pkg/andcmd/layoutThe eventual result will be a -beta CLI version in parallel with the C# CLI
Release notes updated (if appropriate)— not user-facing yet (parallel Go port)Issue linked— tracking via PR stackThirdPartyNotices.txt(if applicable)PR Stack (Go port)