Skip to content

feat(cli): migrate to Cobra and add token get command#21

Merged
appleboy merged 3 commits intomainfrom
feat/cobra-token-get
Mar 13, 2026
Merged

feat(cli): migrate to Cobra and add token get command#21
appleboy merged 3 commits intomainfrom
feat/cobra-token-get

Conversation

@appleboy
Copy link
Member

Summary

  • Migrate CLI from the standard flag package to Cobra, establishing a proper command tree (authgate-cli, token, token get, version)
  • Refactor flag vars from pointers to plain values; register via registerFlags() using Cobra persistent flags
  • Split config init into initStoreConfig() (token store + client ID only) and initConfig() (full, calls initStoreConfig as subroutine) — eliminates duplication and fixes a latent bug where both functions shared the same configInitialized guard
  • Add token get [--json] subcommand to print a stored access token without triggering the full auth flow or server-URL warnings
  • Add table-driven tests for runTokenGet covering plain output, JSON output, and missing-token error cases
  • Simplify tui/token_view.go string prefix handling with strings.CutPrefix

Test plan

  • ./bin/authgate-cli --version prints version
  • ./bin/authgate-cli --help shows all subcommands and global flags
  • ./bin/authgate-cli token get --help shows --json flag
  • ./bin/authgate-cli token get --client-id=<uuid> prints access token (no HTTP warning)
  • ./bin/authgate-cli token get --client-id=<uuid> --json prints full JSON
  • ./bin/authgate-cli --client-id=<uuid> still triggers the normal auth flow
  • make test passes
  • make lint passes

🤖 Generated with Claude Code

- Migrate the CLI from the standard flag package to Cobra, introducing a root command, subcommands, and proper version handling
- Refactor configuration and flag handling to use Cobra persistent flags and plain variables instead of pointer flags
- Split configuration initialization to allow minimal setup for local-only commands like token get
- Add a token command with a get subcommand to print stored access tokens, with optional JSON output
- Add tests covering token get behavior, including JSON output and missing-token errors
- Update dependencies to include Cobra and its related packages
- Simplify string prefix handling in the TUI token storage display using newer standard library helpers

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Copilot AI review requested due to automatic review settings March 12, 2026 10:02
@codecov-commenter
Copy link

codecov-commenter commented Mar 12, 2026

Codecov Report

❌ Patch coverage is 14.28571% with 114 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
config.go 0.00% 46 Missing ⚠️
main.go 0.00% 33 Missing ⚠️
token_cmd.go 38.00% 30 Missing and 1 partial ⚠️
tui/token_view.go 0.00% 4 Missing ⚠️

📢 Thoughts on this report? Let us know!

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Migrates the authgate-cli entrypoint to a Cobra-based command tree and adds a token get subcommand to read stored tokens without running the full auth flow.

Changes:

  • Replace flag-based CLI parsing with Cobra commands (authgate-cli, token get, version) and persistent flags registration.
  • Split config initialization into initStoreConfig() (token store + client ID) and initConfig() (full config + warnings/client setup).
  • Add token get [--json] plus table-driven tests; simplify TUI storage-location parsing using strings.CutPrefix.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
main.go Introduces Cobra root command execution and subcommands.
config.go Reworks flag handling into Cobra persistent flags and splits store-only vs full config init.
token_cmd.go Adds token / token get command and runTokenGet implementation.
token_cmd_test.go Adds tests for runTokenGet plain, JSON, and missing-token cases.
tui/token_view.go Refactors backend prefix parsing to strings.CutPrefix.
go.mod Adds Cobra dependency.
go.sum Adds checksums for Cobra’s dependency graph.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +35 to +38
cmd := &cobra.Command{
Use: "get",
Short: "Print the stored access token",
RunE: func(cmd *cobra.Command, args []string) error {
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

token get currently accepts and ignores extra positional arguments. Consider setting Args: cobra.NoArgs (or similar) so authgate-cli token get foo fails fast with a helpful message instead of silently ignoring user input.

Copilot uses AI. Check for mistakes.
Comment on lines +38 to +45
RunE: func(cmd *cobra.Command, args []string) error {
initStoreConfig()
code := runTokenGet(tokenStore, clientID, jsonOutput, os.Stdout, os.Stderr)
if code != 0 {
os.Exit(code)
}
return nil
},
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like the root command, this subcommand calls os.Exit inside RunE, which prevents error propagation and complicates testing/embedding. Prefer returning an error (and optionally using cmd.SetOut/SetErr plus SilenceUsage) and letting main() decide the exit code.

Copilot uses AI. Check for mistakes.
Comment on lines +78 to +81
enc := json.NewEncoder(stdout)
enc.SetIndent("", " ")
_ = enc.Encode(out)
return 0
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

runTokenGet ignores JSON encoding errors (_ = enc.Encode(out)). If stdout is closed or write fails, the command will still exit 0 and produce partial/invalid output. Handle the encode error and return a non-zero code (and write a useful message to stderr).

Copilot uses AI. Check for mistakes.
token_cmd.go Outdated
Comment on lines +70 to +73
out := tokenGetOutput{
AccessToken: tok.AccessToken,
RefreshToken: tok.RefreshToken,
TokenType: tok.TokenType,
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--json output includes the refresh token and access token in cleartext. This is easy to leak via shell history, logs, or CI output and is inconsistent with the TUI’s masking approach. Consider omitting refresh_token from default JSON output (or gating it behind a separate explicit flag) and/or clearly warning in the help text that this prints secrets.

Copilot uses AI. Check for mistakes.
Comment on lines +25 to +30
setup: func(s credstore.Store[credstore.Token]) {
_ = s.Save("test-id", credstore.Token{
AccessToken: "my-access-token",
ExpiresAt: time.Now().Add(time.Hour),
ClientID: "test-id",
})
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test setup ignores the return value from Store.Save. If the store fails to write (permissions/path issues), the test will continue and fail in a less clear way. Check the error and t.Fatalf to make the failure explicit.

Copilot uses AI. Check for mistakes.
Comment on lines +42 to +47
setup: func(s credstore.Store[credstore.Token]) {
_ = s.Save("test-id", credstore.Token{
AccessToken: "my-access-token",
ExpiresAt: time.Now().Add(time.Hour),
ClientID: "test-id",
})
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test setup also ignores the return value from Store.Save. Checking the error and failing the test immediately will make write failures much easier to diagnose.

Copilot uses AI. Check for mistakes.
Comment on lines +25 to +37
if err := buildRootCmd().Execute(); err != nil {
os.Exit(1)
}
}

// Select UI manager based on environment detection
uiManager := tui.SelectManager()
func buildRootCmd() *cobra.Command {
rootCmd := &cobra.Command{
Use: "authgate-cli",
Short: "OAuth 2.0 authentication CLI",
Version: getVersion(),
SilenceUsage: true,
SilenceErrors: true,
RunE: func(cmd *cobra.Command, args []string) error {
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SilenceErrors: true combined with main() exiting on Execute() error without printing it will cause user-facing failures (e.g., unknown flag/invalid args) to exit with code 1 and no message. Either print the returned error in main() (to stderr) or stop silencing errors and let Cobra print them (keeping SilenceUsage: true is fine).

Copilot uses AI. Check for mistakes.
Comment on lines +37 to +45
RunE: func(cmd *cobra.Command, args []string) error {
initConfig()
uiManager := tui.SelectManager()
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
exitCode := run(ctx, uiManager)
stop()
os.Exit(exitCode)
return nil
},
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid calling os.Exit inside Cobra RunE. Exiting here bypasses Cobra's normal error/return flow and makes the command harder to test or reuse (and will skip any future defers added above). Prefer returning an error (or a sentinel error type carrying an exit code) and handle the process exit in main() after Execute() returns.

Copilot uses AI. Check for mistakes.
- Print Cobra errors to stderr in main() instead of silently exiting;
  introduce exitCodeError to propagate non-zero exit codes through
  Cobras error chain without printing a redundant message
- Replace os.Exit calls inside RunE with exitCodeError returns so
  defers run correctly and commands remain testable
- Add Args: cobra.NoArgs to token get to reject unexpected positional args
- Handle JSON encode error in runTokenGet instead of ignoring it
- Remove refresh_token from --json output to avoid leaking long-lived
  secrets into logs and shell history
- Check Store.Save errors in tests with t.Fatalf for clearer diagnostics

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@appleboy appleboy merged commit 58dddc2 into main Mar 13, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants