Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ both `vX.Y.Z` and `latest`. The image is the static binary in a
image; see `Dockerfile.goreleaser`.

The binary version is embedded at build time via `-ldflags -X
main.version=...`; `tailcat --version` prints it. Builds made with
main.version=...`; `tailcat version` prints it. Builds made with
`go install github.com/tailscale/tailcat/cmd/tailcat@vX.Y.Z` instead
report the module version from the Go build info.

Expand Down
16 changes: 16 additions & 0 deletions cmd/tailcat/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,22 @@ func buildTailcatBinary(dir string) error {
return nil
}

// TestVersionFlag verifies that "tailcat --version" works even though
// --version is not a registered flag; main special-cases it on parse
// failure. The advertised interface is the version subcommand, but
// nixpkgs' versionCheckHook runs "tailcat --version" and depends on
// its exit status and output.
func TestVersionFlag(t *testing.T) {
bin := buildTailcat(t)
out, err := exec.Command(bin, "--version").CombinedOutput()
if err != nil {
t.Fatalf("tailcat --version: %v\n%s", err, out)
}
if v := strings.TrimSpace(string(out)); v == "" || strings.Contains(v, "\n") {
t.Errorf("output = %q; want a single non-empty version line", out)
}
}

// testNoopCommand returns a child command that exits successfully,
// for tests that only care that a wrapper ran it. Windows has no
// "true" binary.
Expand Down
19 changes: 10 additions & 9 deletions cmd/tailcat/tailcat.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ var (
flagKey *string
flagAllow *string
flagVerbose *bool
flagVersion *bool
flagFullAddress *bool
flagJSON *bool
flagDERPMapURL *string
Expand Down Expand Up @@ -89,7 +88,6 @@ func newRootCommand() *ff.Command {
flagServe = rootFS.StringLong("serve", "", "comma-separated list of port numbers, port ranges, or service names to serve; the same list the serve subcommand takes as arguments. Service names are: 'all' (serve all ports), 'exit-node' (run an exit node for all addresses), 'no-auth-ssh' (auth-free SSH server). If empty, it accepts a single connection on any port, writes it to stdout, and exits.")
flagKey = rootFS.StringLong("key", "", "'new' for an ephemeral key. If empty, the default saved key is used if it exists ('default' in server mode, 'client-default' in client modes; see genkey), else an ephemeral key. Otherwise the path to a *.private.json or a name like 'foo' to read it from $CONFIG/tailcat/keys/foo.private.json")
flagVerbose = rootFS.BoolLong("verbose", "be verbose")
flagVersion = rootFS.BoolLong("version", "print the tailcat version and exit")
flagJSON = rootFS.BoolLong("json", "in server mode, write {\"listenAddr\": ...} JSON to stdout")
flagDERPMapURL = rootFS.StringLong("derpmap-url", tailcat.DefaultDERPMapURL, "URL of the JSON DERP map used to resolve or auto-select a DERP region")

Expand Down Expand Up @@ -202,7 +200,7 @@ func newRootCommand() *ff.Command {
{
Name: "version",
Usage: "tailcat version",
ShortHelp: "print the tailcat version (like --version)",
ShortHelp: "print the tailcat version",
Exec: func(ctx context.Context, args []string) error {
fmt.Println(versionString())
return nil
Expand Down Expand Up @@ -497,13 +495,16 @@ func usagef(format string, args ...any) error {
func main() {
root := newRootCommand()
err := root.Parse(os.Args[1:])
if err != nil && slices.Contains(os.Args[1:], "--version") {
// The advertised way to get the version is the version
// subcommand, but nixpkgs' versionCheckHook runs "tailcat
// --version", so keep that working as an unadvertised alias.
// It's not a registered flag, so it only shows up here as a
// parse failure.
fmt.Println(versionString())
return
}
if err == nil {
// The --version flag short-circuits everything else,
// including subcommand dispatch.
if *flagVersion {
fmt.Println(versionString())
return
}
if *flagVerbose {
tailcat.Verbose = true
}
Expand Down