Scaffold Go CLI application with stubbed auth command#1
Conversation
- Initialize Go module (github.com/linuxfoundation/lfx-cli) - Adopt urfave/cli/v3 for subcommand routing - Add urfave/cli-docs/v3 for Markdown doc generation via hidden 'lfx docs' - Stub out auth login/token/status/logout and api subcommands - Add Makefile mirroring lfx-mcp build/check conventions - Configure GoReleaser for linux/darwin/windows amd64+arm64 builds (windows/arm64 excluded) - Add release-tag workflow to publish binaries via GoReleaser on tag push, with per-job permissions and all actions pinned to commit SHA - Add MegaLinter workflow/config, .cspell.json, .lycheeignore, .yamllint, and .gitignore - Pin the license-header-check reusable workflow to a commit SHA (no tags published upstream) and disable Go module caching in the release workflow to address zizmor's unpinned-uses and cache-poisoning audits - Add AGENTS.md with build, test, and contribution guidance - Update README with install instructions and usage Assisted-by: github-copilot:claude-sonnet-5 Signed-off-by: Eric Searcy <eric@linuxfoundation.org>
There was a problem hiding this comment.
Pull request overview
Scaffolds the initial Go-based LFX CLI, including stub commands, documentation generation, release automation, and development tooling.
Changes:
- Adds the CLI entry point and stubbed auth/API commands.
- Adds build, lint, documentation, and contributor tooling.
- Configures multi-platform releases and GitHub workflows.
Reviewed changes
Copilot reviewed 16 out of 18 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
README.md |
Adds installation and usage guidance. |
Makefile |
Adds development and build targets. |
internal/commands/docs.go |
Adds Markdown documentation generation. |
internal/commands/auth.go |
Adds stubbed authentication commands. |
internal/commands/api.go |
Adds the stubbed API command. |
go.sum |
Records dependency checksums. |
go.mod |
Defines the module and dependencies. |
cmd/lfx/main.go |
Adds the CLI entry point. |
AGENTS.md |
Documents development workflows. |
.yamllint |
Configures YAML linting. |
.mega-linter.yml |
Configures MegaLinter. |
.lycheeignore |
Adds link-check exclusions. |
.goreleaser.yaml |
Configures release artifacts. |
.gitignore |
Adds generated-file exclusions. |
.github/workflows/release-tag.yml |
Publishes tagged releases. |
.github/workflows/mega-linter.yml |
Runs MegaLinter on pull requests. |
.github/workflows/license-header-check.yml |
Pins the reusable license workflow. |
.cspell.json |
Configures spell checking. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Go source files used 'Copyright The Linux Foundation and contributors.' but the reusable license-header-check workflow is configured with the longer 'Copyright The Linux Foundation and each contributor to LFX.' string, causing CI to flag all four new Go files as missing their license header. Assisted-by: github-copilot:claude-sonnet-5 Signed-off-by: Eric Searcy <eric@linuxfoundation.org>
- Remove stray ENABLE_LINTERS allow-list from .mega-linter.yml that was silently restricting MegaLinter to only run zizmor - Flatten the Makefile build target so it always rebuilds instead of relying on a stale file-timestamp prerequisite that misses git revision, deleted files, and go.mod changes - Point install-tools at the golangci-lint/v2 module path instead of the obsolete v1 module - Constrain the GoReleaser action version to '~> v2' instead of 'latest', so a future v3 release can't silently break old tags Assisted-by: github-copilot:claude-sonnet-5 Signed-off-by: Eric Searcy <eric@linuxfoundation.org>
- Fall back to the module version from debug.ReadBuildInfo() when no -ldflags version is injected (e.g. 'make build' or 'go install ...@latest'), instead of always reporting 'dev' - Drop the Makefile's separate 'git describe' VERSION/-X main.version injection now that main.go derives the same information from Go's automatic VCS build-info stamping; GoReleaser still injects the precise tagged version via -ldflags - Serialize 'all' (clean/check/build) and 'check' (fmt before the read-only checks) via recursive $(MAKE) calls so 'make -j' can't race on bin/lfx or on source files being formatted while read Assisted-by: github-copilot:claude-sonnet-5 Signed-off-by: Eric Searcy <eric@linuxfoundation.org>
Address review comments from copilot-pull-request-reviewer: - .github/workflows/build.yaml: added a standalone Build workflow that runs go build, gofmt -l, go vet, and go test on every PR, since MegaLinter's revive-only run (GO_GOLANGCI_LINT disabled) never compiles the Go packages, so CI could otherwise pass non-building code (per copilot-pull-request-reviewer) - AGENTS.md: clarified the Package Comments section to explain that MegaLinter's GO_REVIVE_CLI_LINT_MODE defaults to list_of_files (verified locally with revive 1.15.0), under which revive loses per-package grouping and requires the doc comment on every file, rather than overstating Revive's own package-comments rule (per copilot-pull-request-reviewer) - Makefile: reworded the build-flags comment to accurately describe that a local build's pseudo-version already encodes VCS revision and dirty state (verified via go version -m), while go install of a tagged version reports the resolved tag in a different format (per copilot-pull-request-reviewer) No change made to the release-tag.yml/AGENTS.md release procedure (flagged as a possible workflow trigger issue): `gh release create` run by a human triggers the tag-push workflow normally; the GITHUB_TOKEN recursion guard only suppresses workflow-triggered-workflow chains, not human-driven API calls. This matches the identical, working pattern in lfx-mcp. Resolves 4 review threads. Signed-off-by: Eric Searcy <eric@linuxfoundation.org>
- .cspell.json: added "gofmt" to the word list, introduced by the new build.yaml workflow - .mega-linter.yml: added REPOSITORY_KICS to DISABLE_ERRORS_LINTERS. It's currently disabled upstream by MegaLinter itself (supply chain compromise), so marking it non-blocking here prevents a future upstream restore from unexpectedly breaking the build before we've reviewed it Signed-off-by: Eric Searcy <eric@linuxfoundation.org>
Review Feedback Addressed (round 2)Changes Made
No Change Needed
Threads Resolved4 of 4 unresolved threads addressed in this iteration. |
- .cspell.json: added "kics" to the word list, flagged by the newly added REPOSITORY_KICS comment in .mega-linter.yml - .mega-linter.yml: added SPELL_CSPELL to DISABLE_ERRORS_LINTERS. There's no easy local feedback loop for spelling issues before pushing, so keep them non-blocking to avoid unnecessary PR cycles for words that just need adding to the dictionary. Signed-off-by: Eric Searcy <eric@linuxfoundation.org>
| # Build flags. Version is not injected via -ldflags: `go build` already | ||
| # embeds a pseudo-version (encoding the VCS revision, commit time, and | ||
| # dirty state) into the binary's module version, which main.go reads via | ||
| # debug.ReadBuildInfo() as a fallback. A local `make build` therefore | ||
| # reports a traceable pseudo-version like v0.0.0-<time>-<revision>[+dirty], | ||
| # while `go install ...@vX.Y.Z` reports the resolved tag instead; the | ||
| # formats differ, but both identify the exact source that was built. |
There was a problem hiding this comment.
This is based on pre-Go 1.24 semantics, which no longer apply here. Prior to Go 1.24, debug.ReadBuildInfo().Main.Version was always (devel) for a local build of the main module, full stop. Go 1.24 changed this (see https://tip.golang.org/doc/go1.24): go build now sets the main module's version to a VCS-derived pseudo-version (with a +dirty suffix if applicable) instead of (devel), whenever local VCS info is available and -buildvcs isn't disabled.
This repo's go.mod requires go 1.26.5, well past that change. Reproduced on current HEAD with no -buildvcs override anywhere in the repo (GOFLAGS empty, default -buildvcs=auto):
$ go build -ldflags="-s -w" -o /tmp/lfx_verify ./cmd/lfx
$ /tmp/lfx_verify --version
lfx version v0.0.0-20260714204748-822de299abeb
$ go version -m /tmp/lfx_verify | grep -i vcs
build vcs=git
build vcs.revision=822de299abeb6c140efa6b07b0e73ee5c0e57141
build vcs.time=2026-07-14T20:47:48Z
build vcs.modified=false
Main.Version is the pseudo-version, not (devel), and correctly reflects the actual commit. main.go's != "(devel)" check only falls back to the literal dev string when there's genuinely no VCS info available (e.g. building from a tarball with no .git), not for a normal git checkout. No change needed.
- Makefile: added revive to the install-tools target (it was missing, even though `make revive` already existed and expected it on PATH) - Makefile: install-tools now warns if $(go env GOPATH)/bin isn't on PATH, since installed tools (golangci-lint, goreleaser, revive) won't be found otherwise Signed-off-by: Eric Searcy <eric@linuxfoundation.org>
dealako
left a comment
There was a problem hiding this comment.
Hey @emsearcy — nice work on this scaffold. This is the 9th automated review pass on the PR (8 rounds from Copilot plus this one), and it shows: the codebase is in excellent shape. I ran an independent review myself plus dispatched a security-auditor and a code-reviewer-pro subagent in parallel to cross-check.
Overview: Clean Go module scaffold using urfave/cli/v3, stub auth/api commands with clear ticket references for follow-on work, a hidden docs command for Markdown generation, and solid CI/release tooling (pinned Actions, scoped permissions, GoReleaser v2 pinned, a dedicated build.yaml that actually compiles/vets/tests the code since MegaLinter's Revive-only pass doesn't). Version resolution in main.go (ldflags → debug.ReadBuildInfo() → "dev") is correct and well-commented.
Reconciliation with prior Copilot rounds: I checked all 8 prior Copilot review threads — license header wording, golangci-lint v2 module path, GoReleaser version pinning, Makefile phony/ordering (parallel-make races), MegaLinter's stray ENABLE_LINTERS allow-list, the missing go build/test coverage gap (now closed by build.yaml), Revive package-comments guidance, and ReadBuildInfo Go 1.24+ semantics — all were fixed with clear, verifiable commits, and the two most recent Copilot passes (after fce9541/822de29/a4df953) found zero new issues. Nothing outstanding from those rounds.
My review + subagent findings:
- 🔴 Blocking: 0
- 🟡 Minor: 0
- ⚪ Nit: 1 —
Makefile:108-110(install-tools) installs golangci-lint/goreleaser/revive via@latest. Flagged by my security subagent for supply-chain consistency with the rest of the repo's SHA-pinning discipline. After self-challenge: this target isn't invoked by any CI workflow (MegaLinter uses its own pinned Docker image;build.yamldoesn't call it), so it has no bearing on release reproducibility or CI security — it only affects a contributor's local toolchain. Worth a follow-up if you want fully deterministic local tooling, but not blocking. - ❔ Question: 0
No security findings of consequence, no test-coverage gap worth flagging (stub commands with no branching logic; acceptance criteria explicitly call for manual verification here), no correctness issues, no merge conflicts.
Final decision: ✅ Approved with minor comments (the one nit above, non-blocking)
| @echo "Installing development tools..." | ||
| go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest | ||
| go install github.com/goreleaser/goreleaser/v2@latest | ||
| go install github.com/mgechev/revive@latest |
There was a problem hiding this comment.
[nit] Unpinned dev-tool installs
Issue: golangci-lint, goreleaser, and revive are installed via @latest here.
Proof: this target isn't invoked by CI (MegaLinter uses its own pinned Docker image at v9.6.0; build.yaml doesn't call make install-tools), so this only affects a contributor's local toolchain — no release reproducibility or CI security impact.
Why it matters: minor inconsistency with the rest of the repo's SHA/version-pinning discipline; a contributor's local linter version could drift from what they expect.
Fix: optional — pin to specific released versions (e.g. @v2.x.y) if fully deterministic local tooling matters to you. Not blocking.
Summary
Scaffolds the initial Go CLI application structure per LFXV2-2511.
github.com/linuxfoundation/lfx-cli)urfave/cli/v3for subcommand routingurfave/cli-docs/v3for Markdown doc generation via a hiddenlfx docscommandlfx auth login/token/status/logoutandlfx api(no-op, to be implemented in follow-on tickets)lfx-mcp's build/check conventions.cspell.json,.lycheeignore,.yamllint, and.gitignorelicense-header-checkreusable workflow to a commit SHA (no tags published upstream) and disables Go module caching in the release workflow, addressing zizmor'sunpinned-usesandcache-poisoningauditsAGENTS.mdwith build, test, and contribution guidanceAcceptance criteria (all verified locally)
go build ./cmd/lfxproduces a working binarylfx --helpshows available commandslfx auth loginprints a stub messagelfx docsemits structured Markdown for all commandsgoreleaser check) and publishes via tag-push workflowJira
LFXV2-2511
🤖 Generated with GitHub Copilot (via OpenCode)