Skip to content

feat(cmd)!: Add add sub command to replace previous --add flag#11

Merged
JerryAgbesi merged 3 commits intomainfrom
feat/add-subcommand
Apr 21, 2026
Merged

feat(cmd)!: Add add sub command to replace previous --add flag#11
JerryAgbesi merged 3 commits intomainfrom
feat/add-subcommand

Conversation

@JerryAgbesi
Copy link
Copy Markdown
Owner

@JerryAgbesi JerryAgbesi commented Apr 21, 2026

Summary

Replace the -a, --add root flag with a dedicated add subcommand so write operations live under their own verb (git/gh-style).

Behavioral changes:

  • New: skipper add <user@host[:port]>. Inherits the -c, --config persistent flag. Uses cobra.ExactArgs(2) for arg validation.
  • Removed: -a, --add flag and its runRoot branch.
  • sshconfig.AddHost now returns (*Host, bool, error) — the bool is true only when a new entry was written. Duplicate alias+target is still idempotent (no error) but
    the CLI now prints host "" already exists for , no change instead of claiming a write happened.

Docs / release:

  • README.md: removed -a, --add row, added a Commands table, updated examples.
  • CHANGELOG.md: new file, Keep a Changelog format, v0.2.0 section with Added + BREAKING migration snippet.
  • .goreleaser.yaml: added changelog.groups so the release page renders a Breaking Changes section for !: commits, and a release.header linking to CHANGELOG.md.

Related issue

Closes #7

Summary by CodeRabbit

  • New Features

    • Introduced add subcommand for adding SSH host aliases: add <alias> <user@host[:port]>.
    • Reports when an add is idempotent (host already exists) without changing config.
  • Breaking Changes

    • Removed the global --add flag; use the add subcommand.
  • Documentation

    • Updated README to command-based usage; added CHANGELOG and migration guidance.
    • Clarified contribution guideline about avoiding unnecessary emojis.
  • Tests

    • Added and updated tests covering the add subcommand.
  • Chores

    • Improved release changelog generation and GitHub release notes.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 21, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a276020a-c595-4a55-ac5d-b8475e883f1a

📥 Commits

Reviewing files that changed from the base of the PR and between 963f56c and 394555f.

📒 Files selected for processing (3)
  • .goreleaser.yaml
  • AGENTS.md
  • CHANGELOG.md
✅ Files skipped from review due to trivial changes (2)
  • .goreleaser.yaml
  • CHANGELOG.md

📝 Walkthrough

Walkthrough

Replaces the root --add flag with a new add subcommand; updates CLI, tests, docs, and changelog config; changes internal/sshconfig.AddHost to return an additional created bool; tests and callers updated accordingly.

Changes

Cohort / File(s) Summary
Configuration & Documentation
\.goreleaser.yaml, AGENTS.md, CHANGELOG.md, README.md
Switch goreleaser changelog to GitHub source with grouped headings; add contribution note in AGENTS.md; add KEEP A CHANGELOG CHANGELOG.md with v0.2.0 docs; update README to use skipper add subcommand and remove --add flag references.
Add Subcommand Implementation
cmd/add.go, cmd/add_test.go
Introduce new Cobra add command requiring <alias> and <user@host[:port]>; implement runAdd which resolves config path and calls addHost; add command tests for successful add and arg-count validation.
Root Command Refactor & Tests
cmd/root.go, cmd/root_test.go
Remove --add flag and related handling from root; adapt tests to use shared testAlias and to expect the new created boolean return from addHost; drop tests tied to deprecated flag behavior.
SSH Config Writer & Tests
internal/sshconfig/writer.go, internal/sshconfig/writer_test.go
Change AddHost signature to return (addedHost *Host, created bool, err error); return created=false for validation/duplicate/file errors and true when a new entry is written; update tests to capture three return values.

Sequence Diagram

sequenceDiagram
    actor User
    participant CLI as "skipper add"
    participant Validator as "Arg Validator"
    participant Resolver as "Config Resolver"
    participant AddHost as "addHost()"
    participant Writer as "sshconfig.AddHost"
    participant File as "SSH Config File"

    User->>CLI: skipper add devone user@10.0.0.8:9000 -c /path
    CLI->>Validator: validate arg count & formats
    Validator-->>CLI: valid
    CLI->>Resolver: resolveConfigPath(configPath)
    Resolver-->>CLI: /path/to/config
    CLI->>AddHost: addHost(path, "devone", "user@...:9000")
    AddHost->>Writer: AddHost(path, host)
    Writer->>File: read/parse existing entries
    Writer->>File: check for duplicate alias/identity
    alt Entry exists (identical)
        Writer-->>AddHost: (host, false, nil)
    else New entry
        Writer->>File: append new host entry
        Writer-->>AddHost: (host, true, nil)
    end
    AddHost-->>CLI: (host, created)
    alt created == true
        CLI->>User: "added host devone …"
    else
        CLI->>User: "host already exists (no change)"
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I hopped from flag to command today,
No more --add, now add leads the way.
I nibbled tests and stitched the log,
A tiny hop, a tidy cog.
Hooray for tidy CLI play! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: replacing the --add flag with a dedicated add subcommand, which is the core objective of the pull request.
Linked Issues check ✅ Passed All primary coding requirements from #7 are implemented: new add subcommand with required arguments [#7], removed --add flag [#7], updated AddHost return signature [#7], CLI messaging for idempotent behavior [#7], documentation updates [#7], and test coverage [#7].
Out of Scope Changes check ✅ Passed Changes are scope-appropriate: the new add subcommand, removal of --add flag, and signature updates to AddHost all directly support the issue objectives. Configuration updates to .goreleaser.yaml support release documentation of the breaking change, and documentation updates explain the migration path. No extraneous modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/add-subcommand

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (5)
internal/sshconfig/writer_test.go (1)

59-59: Consider adding assertions for the created boolean.

The tests correctly capture three return values but discard the created boolean. While cmd/root_test.go covers this behavior at the integration level, adding explicit unit-level assertions here would strengthen coverage.

For example, in TestAddHostWritesSafeIdentityFile:

_, created, err := AddHost(configPath, Host{...})
if !created {
    t.Fatal("expected created=true for new host")
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/sshconfig/writer_test.go` at line 59, Add an assertion for the
boolean return from AddHost in writer_test.go: when calling AddHost (the
function under test) capture the created bool (e.g., _, created, err :=
AddHost(...)) and assert that created is true for cases expecting a new host and
false when expecting no creation; update TestAddHostWritesSafeIdentityFile and
other relevant tests to check the created value and fail the test
(t.Fatal/t.Fatalf) when the value is not as expected.
cmd/add_test.go (2)

34-50: Consider adding test for malformed target.

Per PR objectives, tests should cover "malformed target" scenarios. This would verify the ParseTarget validation path.

Suggested additional test
func TestAddCommandRejectsMalformedTarget(t *testing.T) {
	path := filepath.Join(t.TempDir(), "config")

	out := new(bytes.Buffer)
	rootCmd.SetOut(out)
	rootCmd.SetErr(out)
	rootCmd.SetArgs([]string{"add", "devone", "invalid-target", "-c", path})

	err := rootCmd.Execute()
	if err == nil {
		t.Fatal("expected error for malformed target")
	}

	if !strings.Contains(err.Error(), "user@host") {
		t.Fatalf("expected format error, got %v", err)
	}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/add_test.go` around lines 34 - 50, Add a new test that verifies the
ParseTarget validation path by asserting the add command returns a
malformed-target error when given an invalid target string: create a test
similar to TestAddCommandRejectsWrongArgCount (e.g.,
TestAddCommandRejectsMalformedTarget) that sets rootCmd args to
{"add","devone","invalid-target","-c", path}, calls rootCmd.Execute(), asserts
err is non-nil and that err.Error() contains the expected "user@host" (or the
ParseTarget format hint); place the test alongside
TestAddCommandRejectsWrongArgCount so it exercises ParseTarget and
rootCmd.Execute paths.

12-50: Test isolation: shared rootCmd state may cause flakiness.

Both tests mutate the global rootCmd (via SetOut, SetErr, SetArgs) without resetting state between tests. This can cause issues if tests run in parallel or if other tests in the package also use rootCmd.

Consider resetting state after each test:

Suggested cleanup pattern
 func TestAddCommandWritesHost(t *testing.T) {
 	path := filepath.Join(t.TempDir(), "config")
 
 	out := new(bytes.Buffer)
 	rootCmd.SetOut(out)
 	rootCmd.SetErr(out)
 	rootCmd.SetArgs([]string{"add", "devone", "user@10.0.0.8:9000", "-c", path})
+	defer func() {
+		rootCmd.SetOut(nil)
+		rootCmd.SetErr(nil)
+		rootCmd.SetArgs(nil)
+	}()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/add_test.go` around lines 12 - 50, The tests are mutating the shared
global rootCmd via SetOut/SetErr/SetArgs which can cause flakiness; update each
test (TestAddCommandWritesHost and TestAddCommandRejectsWrongArgCount) to use a
fresh command instance or to fully reset rootCmd state before/after the test:
create or call a factory that returns a new rootCmd, call SetOut/SetErr/SetArgs
on that instance, run Execute on it, and ensure any global flags/args are
cleared (or defer a reset) so subsequent tests are unaffected; reference
rootCmd, SetOut, SetErr, SetArgs, and Execute when making the change.
CHANGELOG.md (2)

8-8: Add a date placeholder and reference link for v0.2.0.

Per Keep a Changelog format, version headers should include release dates (e.g., ## [v0.2.0] - YYYY-MM-DD or ## [v0.2.0] - Unreleased). Additionally, the reference link at the bottom is missing for v0.2.0.

Suggested changes
-## [v0.2.0]
+## [v0.2.0] - Unreleased

 ...

 [0.1.5]: https://github.com/JerryAgbesi/skipper/releases/tag/v0.1.5
+[v0.2.0]: https://github.com/JerryAgbesi/skipper/releases/tag/v0.2.0
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CHANGELOG.md` at line 8, Update the CHANGELOG entry for "## [v0.2.0]" to
include a date placeholder per Keep a Changelog (e.g., change "## [v0.2.0]" to
"## [v0.2.0] - Unreleased" or "## [v0.2.0] - YYYY-MM-DD") and add the missing
reference link for v0.2.0 at the bottom of the file (add a link like "[v0.2.0]:
<compare-or-release-URL>" matching the other release link formats so the header
and footnote reference are consistent).

8-35: Inconsistent version format: [v0.2.0] vs [0.1.5].

The v0.2.0 header uses a v prefix while v0.1.5 omits it. Consider using a consistent format throughout the changelog.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CHANGELOG.md` around lines 8 - 35, The changelog has inconsistent version
tags: `[v0.2.0]` vs `[0.1.5]`; normalize them by removing the leading "v" from
`[v0.2.0]` (change to `[0.2.0]`) and ensure any corresponding reference/link
labels or anchor lines match the same format (e.g., update any `[v0.2.0]: ...`
entries if present) so both headers and link footers use the same plain numeric
style as `[0.1.5]`.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@AGENTS.md`:
- Line 29: Correct the spelling of "unnesessary" to "unnecessary" in the
contributor guideline phrase "avoid using unnesessary emojis."; update the
sentence to read "avoid using unnecessary emojis." and ensure any other
occurrences of "unnesessary" in AGENTS.md are similarly fixed for consistency.

---

Nitpick comments:
In `@CHANGELOG.md`:
- Line 8: Update the CHANGELOG entry for "## [v0.2.0]" to include a date
placeholder per Keep a Changelog (e.g., change "## [v0.2.0]" to "## [v0.2.0] -
Unreleased" or "## [v0.2.0] - YYYY-MM-DD") and add the missing reference link
for v0.2.0 at the bottom of the file (add a link like "[v0.2.0]:
<compare-or-release-URL>" matching the other release link formats so the header
and footnote reference are consistent).
- Around line 8-35: The changelog has inconsistent version tags: `[v0.2.0]` vs
`[0.1.5]`; normalize them by removing the leading "v" from `[v0.2.0]` (change to
`[0.2.0]`) and ensure any corresponding reference/link labels or anchor lines
match the same format (e.g., update any `[v0.2.0]: ...` entries if present) so
both headers and link footers use the same plain numeric style as `[0.1.5]`.

In `@cmd/add_test.go`:
- Around line 34-50: Add a new test that verifies the ParseTarget validation
path by asserting the add command returns a malformed-target error when given an
invalid target string: create a test similar to
TestAddCommandRejectsWrongArgCount (e.g., TestAddCommandRejectsMalformedTarget)
that sets rootCmd args to {"add","devone","invalid-target","-c", path}, calls
rootCmd.Execute(), asserts err is non-nil and that err.Error() contains the
expected "user@host" (or the ParseTarget format hint); place the test alongside
TestAddCommandRejectsWrongArgCount so it exercises ParseTarget and
rootCmd.Execute paths.
- Around line 12-50: The tests are mutating the shared global rootCmd via
SetOut/SetErr/SetArgs which can cause flakiness; update each test
(TestAddCommandWritesHost and TestAddCommandRejectsWrongArgCount) to use a fresh
command instance or to fully reset rootCmd state before/after the test: create
or call a factory that returns a new rootCmd, call SetOut/SetErr/SetArgs on that
instance, run Execute on it, and ensure any global flags/args are cleared (or
defer a reset) so subsequent tests are unaffected; reference rootCmd, SetOut,
SetErr, SetArgs, and Execute when making the change.

In `@internal/sshconfig/writer_test.go`:
- Line 59: Add an assertion for the boolean return from AddHost in
writer_test.go: when calling AddHost (the function under test) capture the
created bool (e.g., _, created, err := AddHost(...)) and assert that created is
true for cases expecting a new host and false when expecting no creation; update
TestAddHostWritesSafeIdentityFile and other relevant tests to check the created
value and fail the test (t.Fatal/t.Fatalf) when the value is not as expected.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 129bedae-b69e-4cef-86d1-31c8a9bd8b20

📥 Commits

Reviewing files that changed from the base of the PR and between da0aecc and 963f56c.

📒 Files selected for processing (10)
  • .goreleaser.yaml
  • AGENTS.md
  • CHANGELOG.md
  • README.md
  • cmd/add.go
  • cmd/add_test.go
  • cmd/root.go
  • cmd/root_test.go
  • internal/sshconfig/writer.go
  • internal/sshconfig/writer_test.go

Comment thread AGENTS.md
- Every feature must ship with tests covering it; no feature lands without test coverage.
- Add or update tests whenever behavior changes, and keep `go test ./...` green.
- Update `README.md` when user-facing behavior or flags change.
- avoid using unnesessary emojis.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix spelling in this contributor guideline.

There’s a typo in “unnesessary”; it should be “unnecessary.”

Proposed patch
-- avoid using unnesessary emojis.
+- avoid using unnecessary emojis.

Based on learnings: Avoid using unnecessary emojis.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- avoid using unnesessary emojis.
- avoid using unnecessary emojis.
🧰 Tools
🪛 LanguageTool

[grammar] ~29-~29: Ensure spelling is correct
Context: ...behavior or flags change. - avoid using unnesessary emojis. ## Commit messages Short, imp...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AGENTS.md` at line 29, Correct the spelling of "unnesessary" to "unnecessary"
in the contributor guideline phrase "avoid using unnesessary emojis."; update
the sentence to read "avoid using unnecessary emojis." and ensure any other
occurrences of "unnesessary" in AGENTS.md are similarly fixed for consistency.

@JerryAgbesi JerryAgbesi force-pushed the feat/add-subcommand branch from 963f56c to 394555f Compare April 21, 2026 09:25
@JerryAgbesi JerryAgbesi merged commit 2f7a728 into main Apr 21, 2026
6 checks passed
@JerryAgbesi JerryAgbesi changed the title Add add sub command to replace previous --add flag feat(cmd)!: Add add sub command to replace previous --add flag Apr 21, 2026
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.

[feature] Replace --add flag with an add subcommand

1 participant