Fail a bare invocation when stdout is not a terminal #4
Reference in New Issue
Block a user
Delete Branch "benvin/fix-invocation-guards"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Puppet's
generate()feeds this binary's stdout intoparsejson(), so an argv that lost its common name printed help and exited zero, feeding help text into the catalog instead of failing the compile.term.IsTerminalioctl, so/dev/nulland the one the runtime reopens onto a closed fd 1 are not mistaken for one.--config ""rather than resolve it, and ignore a relativeXDG_CONFIG_HOMEas the spec requires.x/termv0.45.0 andx/sysv0.47.0, the newest pair still declaringgo 1.25.0.Puppet's generate() reads stdout through a pipe with combine: true, so help text on a zero exit lands in the catalog. Help now requires an interactive stdout; a piped one exits non-zero with nothing on stdout. - gate the no-args help path on an isTerminal(stdout) check - reject an explicitly empty --config instead of resolving it - ignore a relative XDG_CONFIG_HOME, per the XDG spec - give the integration subprocess a hermetic HOME/XDG_CONFIG_HOME - pass []string{} to run(), so cobra cannot fall back to os.Argscmd/certmanager/main.go:53—ModeCharDeviceis set for/dev/null, socertmanager > /dev/nullprints help and exits 0; a bare call with fd 1 closed does the same (the Go runtime reopens 0/1/2 onto/dev/null). Both contradict README's "prints help and exits zero only when stdout is a terminal" → detect with a tty ioctl (golang.org/x/term.IsTerminal(int(f.Fd()))), not a stat mode bit.cmd/certmanager/main_test.go:851—/dev/ttyopen fails in a TTY-less CI container, soTestIsTerminalskips there, and every other test injectsinteractivethroughrunWith; hardwiringisTerminaltoreturn falseleavesgo test ./...entirely green. The true branch is unpinned in CI → open/dev/ptmxinstead of/dev/tty.test/integration/certmanager_test.go:511— only the XDG leg is poisoned; the HOME leg resolvespoisoned/.config/certmanager/config.yaml, which the test never creates, so ahermeticEnvthat dropped theHOME=entry would still pass → write the poisoned config underpoisoned/.config/certmanager/too.--config ''rejection and the relative-XDG_CONFIG_HOMEchange are config-resolution work unrelated to the stdout gate → separate PR.cmd/certmanager/main_test.go:873—run(nil, f, &stderr)leaves cobra to fall back toos.Args[1:], so this sub-case never exercises a bare invocation; undergo test ./... -args fooit runs ascertmanager fooand would issue against whatever/opt/certmanager/config.yamlresolves to on the host (verified: the exit-code assertions stop firing, only theisTerminalones remain). → pass[]string{}, as the other call sites and the PR body already claim.test/integration/certmanager_test.go:292—TestHermeticEnvSetsBothConfigLegsasserts only the shape of the returned env, so replacinghermeticEnv's body withos.Environ()still passes it on any host without~/.config/certmanager/config.yaml. → assert the two values differ from the process's ownHOME/XDG_CONFIG_HOME.go.mod:7-8—x/term v0.45.0andx/sys v0.47.0still declarego 1.25.0; onlyv0.46.0/v0.48.0move togo 1.26.0. → the pin can move up one release without forcing the module'sgo` line.