v0.x: Props.IO, and the setup wizards' test seams¶
Spec 0198, landing in phases. This note grows with them.
Phase 1: the streams on Props¶
Props.IO(props.IOinterface,props.StdIOdefault) is the invocation's stdin, stdout and stderr, and whether a person is at them. A nil field reads as the process's streams throughGetIO(); nothing changes for a tool that never sets it. The root fills it from cobra in its pre-run.--accessible/GTB_ACCESSIBLE=trueon every GTB-based tool asks for line prompts (huh's accessible mode) instead of a full-screen form. The gtb generator's wizards (generate project,gtb wizard,generate command,generate add-flag) honour it too, and answers can be piped: the runner hands the form one line per read, because huh reads ahead otherwise and lost every answer after the first. One limit remains upstream: huh's accessible mode prompts for every page, including those a previous answer would hide, so the project wizard over a pipe needs an answer for each page in order; a terminal, or the flags, is the reliable route for it.setup.RunForm(ctx, p, form)is how a framework form runs: on the IO's streams, with the IO's accessible decision, refusing a stdin that is neither a terminal nor accessible (setup.ErrNonInteractive).- Tests drive real forms through
Props.IOwithinternal/formtest; see the testing guide. The form-creator options come out in phase 2.
Phase 2: the wizards run real forms¶
Every setup wizard is one huh form run through setup.RunForm. The options
that let a caller substitute a form are gone, and so are the parameters that
only existed to pass one through:
| Removed | Was on |
|---|---|
ai.FormOption, ai.WithAIForm |
ai.RunAIInit, ai.NewCmdInitAI, ai.NewAIInitialiser (each loses its opts ...) |
forge.AuthFormOption, forge.WithAuthForm, forge.WithAuthForms |
the single-token initialisers |
forge.DualFormOption, forge.WithDualForm, forge.WithDualForms |
forge.RunBitbucketInit, forge.RunBitbucketInitCmd (each loses its opts ...) |
forge.WithSSHKeySelectForm, forge.WithSSHKeyPathForm, forge.WithPassphraseForm, forge.WithUploadConfirmForm, forge.GenerateKeyOption, forge.WithGenerateKeyOptions, forge.WithSSHForms |
the SSH stage; forge.ConfigureSSHKey now takes a context.Context first |
telemetry.FormOption, telemetry.WithForm |
telemetry.NewTelemetryInitialiser (loses its opts ...) |
root.OutdatedVersionOption, root.WithForm, root.WithInteractive, root.ConsentOption, root.WithConsentInteractive |
the pre-run's update and consent prompts |
A test that used one of these sets Props.IO instead and answers the real
form; see the testing guide.
Three wizards now refuse a value huh's accessible mode could not collect
(a password prompt with no terminal behind it is left blank, silently):
forge.ErrNoTokenEntered, forge.ErrCredentialsIncomplete and
forge.ErrPassphraseTooShort.
Phase 3: nothing in pkg/ names the process's streams¶
utils.IsInteractiveis removed, and with it thepkg/utilspackage (it was the last thing in it). Ask the Props:p.GetIO().Interactive(). It read the process's stdin, which is not the invocation's under a test or an embedding, and it said "terminal" of a/dev/nullstdin.setup.InitOptions.Interactiveis removed:setup.Initialisereadsp.GetIO().Interactive(). A test setsProps.IO.config.WithInteractiveCheckonconfig.NewCmdEditis removed for the same reason; the editor subprocess runs on the IO's streams.credentialposture.DiscoverModeEnvironmentandcredentialposture.StorageModeOptionstake the IO after the context;setup.StorageModeGrouptakes the*props.Propsafter the context.setup.RunFormOn(ctx, io, form)isRunFormfor a caller holding the streams rather than the Props;setup.WithIO(io)hands them to an updater built without Props (setup.NewOfflineUpdater).SelfUpdater.UpdateFromFiletakes acontext.Contextfirst, andupdate.Updaterwith it.- The dependency-substitution options are unexported or removed (spec
0198 D4, a clean break). Where a tool used one, the
props.Toolfield is the way to say the same thing:
| Gone | Instead |
|---|---|
setup.WithReleaseProvider(p) |
props.Tool.ReleaseProvider = p |
setup.WithEmbeddedKeys(k...) |
props.Tool.Signing.EmbeddedKeys = [][]byte{k...} |
setup.WithRequireChecksum(b) |
props.Tool.Signing.RequireChecksum = &b |
setup.WithKeyResolver(r) |
none: the resolver chain is built from update.key_source; ask for a Tool.Signing field if you need another source |
setup.WithMaxBinaryDownloadSize, setup.WithMaxChecksumsSize |
none: the bounds are the Default* constants; ask for a Tool field if a release exceeds them |
setup.WithAuthCheck(p, keys...) |
write the setup.Middleware yourself; the middleware how-to shows the twelve lines |
forge.WithProviderFactory, forge.WithKeyManager, forge.WithSSHOptions, setup.WithExecLookPath, setup.WithOsExecutable |
in-package test seams; nothing for a tool to do |