Skip to content

Extract run() int and push os.Exit to the edge #55

Description

@korya

Rearchitecture step 2 of 6. Sequence: #54 → 2 → #56 → #48 → #18 → #41.

Problem

die() calls os.Exit from the middle of library-shaped code, so every error path in the program terminates the process:

func die(rc int, format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, "\nError: "+format, args...)
	os.Exit(rc)
}

It is reached from mustParseLogLevel, mustParseHostMappings, and three sites inside the cobra Run closure. There is no seam between "decide what went wrong" and "terminate" — which means no error path can be exercised without spawning a subprocess. None of them are.

This is the architectural reason coverage sits at 39%, and it is upstream of several filed bugs: there was no cheap place to put validation, so validation didn't get written.

Suggested fix

The standard Go shape — a testable core, with process termination pushed to the edge:

Before:

func main() {
	cmd := &cobra.Command{ /* … */ }   // die() → os.Exit() from anywhere inside
	if err := cmd.ExecuteContext(context.Background()); err != nil {
		die(103, "%s", err)
	}
}

After:

func main() {
	os.Exit(run(os.Args[1:], os.Stdout, os.Stderr))
}

func run(args []string, stdout, stderr io.Writer) int {
	// … returns an exit code; never calls os.Exit
}

die becomes an error value carrying a code, not a call that ends the world:

type exitError struct {
	code int
	msg  string
}

Why this is step 2

It unblocks nearly everything else:

Scope

This is a mechanical restructuring with no behaviour change intended — same exit codes, same messages, same stream for each. Any behaviour change that falls out should be filed separately rather than folded in, so the diff stays reviewable.

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestwontfixThis will not be worked on

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions