banger hasn't shipped a public release — every "legacy", "pre-opt-in",
"previously", "migration note", "no longer" reference in the tree is
pinning against a state no real user's install has ever been in.
That scaffolding has weight: it's a coordinate system future readers
have to decode, and it keeps dead code alive.
Removed (code):
- internal/daemon/ssh_client_config.go
- vmSSHConfigIncludeBegin / vmSSHConfigIncludeEnd constants and
every `removeManagedBlock(existing, vm...)` call they enabled
(legacy inline `Host *.vm` block scrub)
- cleanupLegacySSHConfigDir (+ its caller in syncVMSSHClientConfig)
— wiped a pre-opt-in sibling file under $ConfigDir/ssh
- sameDirOrParent + resolvePathForComparison — only ever used
by cleanupLegacySSHConfigDir
- the "also check legacy marker" fallback in
UserSSHIncludeInstalled / UninstallUserSSHInclude
- internal/store/migrations.go
- migrateDropDeadImageColumns (migration 2) + its slice entry
- dropColumnIfExists (orphaned after the above)
- addColumnIfMissing + the whole "columns added across the pre-
versioning lifetime" block at the end of migrateBaseline —
subsumed into the baseline CREATE TABLE
- `packages_path TEXT` column on the images table (the
throwaway migration 2 dropped it, but there was never any
reader)
- internal/daemon/vm.go
- vmDNSRecordName local wrapper — was justified as "avoid
pulling vmdns into every file"; three of four callers already
imported vmdns directly, so inline the one stray call
- internal/cli/cli_test.go
- TestLegacyRemovedCommandIsRejected (`tui` subcommand never
shipped)
Removed / simplified (tests):
- ssh_client_config_test.go: dropped TestSameDirOrParentHandlesSymlinks,
TestSyncVMSSHClientConfigPreservesUserKeyInLegacyDir,
TestSyncVMSSHClientConfigNarrowsCleanupToLegacyFile,
TestSyncVMSSHClientConfigLeavesUnexpectedLegacyContents,
TestInstallUserSSHIncludeMigratesLegacyInlineBlock, plus the
"legacy posture" regression strings in the remaining happy-path
test; TestUninstallUserSSHIncludeRemovesBothMarkerBlocks collapsed
to a single-block test
- migrations_test.go: dropped TestMigrateDropDeadImageColumns_AcrossInstallPaths,
TestDropColumnIfExistsIsIdempotent; TestOpenReadOnlyDoesNotRunMigrations
simplified to test against the baseline marker
Removed (docs):
- README.md "**Migration note.**" blockquote about the SSH-key path move
- docs/advanced.md parenthetical "(the old behaviour)"
Reworded (comments):
- Dropped "Previously this file also contained LogLevel DEBUG3..."
history from vm_disk.go's sshdGuestConfig doc
- Dropped "Call sites that previously read vm.Runtime.{PID,...}"
from vm_handles.go; now documents the current contract
- Dropped "Pre-v0.1 the defaults are" scaffolding in doctor_test.go
- Dropped "no longer does its own git inspection" phrasing in vm_run.go
- Dropped the "(also cleans up legacy inline block from pre-opt-in
builds)" aside on the `ssh-config` CLI docstring
- Renamed test var `legacyKey` → `existingKey` in vm_test.go; its
purpose was "pre-existing authorized_keys line," not banger-legacy
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
85 lines
2.9 KiB
Go
85 lines
2.9 KiB
Go
package cli
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"banger/internal/daemon"
|
|
"banger/internal/paths"
|
|
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
// newSSHConfigCommand exposes the opt-in ergonomics for `ssh <name>.vm`.
|
|
// Default mode prints current status + the exact Include line the user
|
|
// can paste into ~/.ssh/config themselves. --install does the include
|
|
// for them inside a marker-fenced block; --uninstall reverses it.
|
|
func newSSHConfigCommand() *cobra.Command {
|
|
var (
|
|
install bool
|
|
uninstall bool
|
|
)
|
|
cmd := &cobra.Command{
|
|
Use: "ssh-config",
|
|
Short: "Manage the optional `ssh <name>.vm` shortcut",
|
|
Long: `Banger keeps a self-contained SSH client config under its own config
|
|
directory (never touching ~/.ssh/config on its own). Opt in to the
|
|
convenience shortcut that lets you run 'ssh <name>.vm' from any
|
|
terminal, bypassing 'banger vm ssh':
|
|
|
|
banger ssh-config # print status + copy-paste snippet
|
|
banger ssh-config --install # add an Include line to ~/.ssh/config
|
|
banger ssh-config --uninstall # remove banger's Include from ~/.ssh/config
|
|
`,
|
|
Args: noArgsUsage("usage: banger ssh-config [--install|--uninstall]"),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
if install && uninstall {
|
|
return fmt.Errorf("use only one of --install or --uninstall")
|
|
}
|
|
layout, err := paths.Resolve()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
bangerConfig := daemon.BangerSSHConfigPath(layout)
|
|
switch {
|
|
case install:
|
|
if err := daemon.InstallUserSSHInclude(layout); err != nil {
|
|
return err
|
|
}
|
|
_, err = fmt.Fprintf(cmd.OutOrStdout(),
|
|
"added Include %s to ~/.ssh/config — `ssh <name>.vm` will now route through banger\n",
|
|
bangerConfig,
|
|
)
|
|
return err
|
|
case uninstall:
|
|
if err := daemon.UninstallUserSSHInclude(); err != nil {
|
|
return err
|
|
}
|
|
_, err = fmt.Fprintln(cmd.OutOrStdout(), "removed banger's entries from ~/.ssh/config")
|
|
return err
|
|
default:
|
|
installed, err := daemon.UserSSHIncludeInstalled()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out := cmd.OutOrStdout()
|
|
fmt.Fprintf(out, "banger ssh_config: %s\n", bangerConfig)
|
|
if installed {
|
|
fmt.Fprintln(out, "status: included from ~/.ssh/config")
|
|
fmt.Fprintln(out, "")
|
|
fmt.Fprintln(out, "`ssh <name>.vm` is enabled. Run `banger ssh-config --uninstall` to revert.")
|
|
} else {
|
|
fmt.Fprintln(out, "status: not included (opt-in)")
|
|
fmt.Fprintln(out, "")
|
|
fmt.Fprintln(out, "Enable `ssh <name>.vm` in two ways:")
|
|
fmt.Fprintln(out, " banger ssh-config --install")
|
|
fmt.Fprintln(out, "or add this line to ~/.ssh/config yourself:")
|
|
fmt.Fprintf(out, " Include %s\n", bangerConfig)
|
|
}
|
|
return nil
|
|
}
|
|
},
|
|
}
|
|
cmd.Flags().BoolVar(&install, "install", false, "add an Include line to ~/.ssh/config")
|
|
cmd.Flags().BoolVar(&uninstall, "uninstall", false, "remove banger's Include from ~/.ssh/config")
|
|
return cmd
|
|
}
|