Documentation
¶
Overview ¶
Package forge provides a single, provider-parameterised interactive setup initialiser for git forges.
One generic Initialiser is driven by a Profile describing a forge's credential shape. Two profiles are registered:
- GitHub (SingleToken): a token recorded via an env-var reference, the OS keychain, or a literal in config; forge-driven OAuth login with a manual-PAT fallback on headless hosts; and optional SSH key discovery, generation, and upload. Registered with the `init github` command (NewCmdInitGitHub) and an embedded asset bundle.
- Bitbucket (DualUserPass): the dual-credential model (username + app_password) across the same three storage modes — env-var mode records two env-var names, keychain mode stores a single JSON blob, literal mode writes both fields to config. No login, no SSH. Registered with the `init bitbucket` command (NewCmdInitBitbucket).
Runtime credential resolution lives in pkg/vcs and the forge providers; this package handles first-run setup, not API client construction. All three storage modes honour the credential-storage hardening spec: literal mode is refused under CI, and every write commits its mode's keys exclusively so switching modes never leaves a stale secret or reference behind.
Index ¶
- func ConfigureSSHKey(profile Profile, props *props.Props, cfg config.Reader, ...) (string, string, error)
- func NewCmdInitBitbucket(p *props.Props) *cobra.Command
- func NewCmdInitGitHub(p *props.Props) *cobra.Command
- func RunBitbucketInit(ctx context.Context, p *props.Props, cfg setup.Editor, opts ...DualFormOption) error
- func RunBitbucketInitCmd(ctx context.Context, p *props.Props, dir string, opts ...DualFormOption) error
- func RunGitHubInit(ctx context.Context, p *props.Props, cfg setup.Editor) error
- func RunGitHubInitCmd(ctx context.Context, p *props.Props, dir string) error
- type AuthConfig
- type AuthFormOption
- type ConfigureSSHKeyOption
- type CredentialShape
- type DualConfig
- type DualFormOption
- type GenerateKeyOption
- type Initialiser
- type InitialiserOption
- type Profile
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ConfigureSSHKey ¶
func ConfigureSSHKey(profile Profile, props *props.Props, cfg config.Reader, opts ...ConfigureSSHKeyOption) (string, string, error)
ConfigureSSHKey runs the interactive SSH key configuration flow.
func NewCmdInitBitbucket ¶
NewCmdInitBitbucket creates the `init bitbucket` subcommand.
func NewCmdInitGitHub ¶
NewCmdInitGitHub creates the `init github` subcommand.
func RunBitbucketInit ¶
func RunBitbucketInit(ctx context.Context, p *props.Props, cfg setup.Editor, opts ...DualFormOption) error
RunBitbucketInit executes the wizard against an existing config container, typically invoked by NewCmdInitBitbucket. Optional [DualFormOption]s are propagated into the wizard so tests can inject deterministic form creators, mirroring pkg/setup/ai.RunAIInit.
func RunBitbucketInitCmd ¶
func RunBitbucketInitCmd(ctx context.Context, p *props.Props, dir string, opts ...DualFormOption) error
RunBitbucketInitCmd materialises the target config (seeded from the merged init template when absent) and runs the wizard over it; writes land in the file as they are applied, with 0600 permissions. Optional [DualFormOption]s are propagated into the wizard so tests can inject deterministic form creators, mirroring pkg/setup/ai.RunAIInit.
func RunGitHubInit ¶
RunGitHubInit forcibly runs both login and SSH configuration regardless of current state. This is used by the explicit `init github` command. ctx is the command context: it carries no deadline of its own so the OAuth device flow can run at human pace, while Ctrl-C aborts an in-flight poll.
Types ¶
type AuthConfig ¶
type AuthConfig struct {
// StorageMode is set by the storage-mode selector. Defaults to
// [credentials.ModeEnvVar] when the form presents the choice.
StorageMode credentials.Mode
// EnvVarName is the env var NAME recorded under <prefix>.auth.env in
// env-var mode. Ignored in keychain/literal modes.
EnvVarName string
// FetchToken is true when the user wants the wizard to run OAuth (or the
// manual fallback) on their behalf. Only relevant in env-var mode —
// keychain/literal always need a token.
FetchToken bool
// Token is the captured token from OAuth / manual entry. Cleared after it
// has been written (or displayed for env-var mode) so it does not linger in
// the AuthConfig longer than necessary.
Token string
}
AuthConfig captures the single-token wizard's output from each stage. All fields are populated incrementally so test-injected form creators can override any subset and the runner still produces a coherent config.
type AuthFormOption ¶
type AuthFormOption func(*authFormConfig)
AuthFormOption configures form creators for the single-token auth wizard. Used by tests to inject deterministic form-answering creators without driving a real TTY.
func WithAuthForm ¶
func WithAuthForm( creator func(*AuthConfig) []*huh.Form, displayOnceCreator func(envVarName, token string) *huh.Form, ) AuthFormOption
WithAuthForm injects custom form creators into the single-token flow for testability. The creator returns forms in order:
[0] storage-mode selector [1] env-var name input [2] "fetch token now?" confirm [3] display-once token view (takes envVarName, token)
Returning fewer forms is allowed — the runner skips stages whose slot is nil or absent. The display-once creator has a different signature because it needs the captured token passed in.
type ConfigureSSHKeyOption ¶
type ConfigureSSHKeyOption func(*configureSSHKeyConfig)
ConfigureSSHKeyOption is a functional option for ConfigureSSHKey.
func WithGenerateKeyOptions ¶
func WithGenerateKeyOptions(opts ...GenerateKeyOption) ConfigureSSHKeyOption
WithGenerateKeyOptions passes options through to the key generation step.
func WithSSHKeyPathForm ¶
func WithSSHKeyPathForm(creator func(*string) *huh.Form) ConfigureSSHKeyOption
WithSSHKeyPathForm overrides the SSH key path input form (for testing).
func WithSSHKeySelectForm ¶
func WithSSHKeySelectForm(creator func(*string, []huh.Option[string]) *huh.Form) ConfigureSSHKeyOption
WithSSHKeySelectForm overrides the SSH key selection form (for testing).
type CredentialShape ¶
type CredentialShape int
CredentialShape discriminates the credential layout a forge setup wizard captures: a single API token, or a username + app-password pair.
const ( // SingleToken is a one-credential wizard (e.g. GitHub): three storage // modes over a single token, optional forge-driven login and SSH upload. SingleToken CredentialShape = iota // DualUserPass is a two-credential wizard (e.g. Bitbucket): three storage // modes over a username + app-password pair, no login, no SSH. DualUserPass )
type DualConfig ¶
type DualConfig struct {
StorageMode credentials.Mode
// Username and AppPassword hold the collected credentials for keychain and
// literal modes. Unused in env-var mode.
Username string
AppPassword string
// UsernameEnvName and AppPasswordEnvName hold env-var names for env-var
// mode. Default to the profile's fallback env-var names.
UsernameEnvName string
AppPasswordEnvName string
}
DualConfig captures the dual-credential wizard's outputs. Fields unused by the selected storage mode are ignored.
type DualFormOption ¶
type DualFormOption func(*dualFormConfig)
DualFormOption configures the dual-credential init form for testability.
func WithDualForm ¶
func WithDualForm(creator func(*DualConfig) []*huh.Form) DualFormOption
WithDualForm injects custom form creators into the dual-credential wizard for testing. The creator returns forms in order:
[0] storage-mode selector [1] env-var names (env-var mode only) [2] username + app_password inputs (keychain / literal modes)
Returning fewer forms is allowed — the runner skips stages whose slot is nil or absent.
type GenerateKeyOption ¶
type GenerateKeyOption func(*generateKeyConfig)
GenerateKeyOption is a functional option for SSH key generation.
func WithKeyManager ¶
func WithKeyManager(factory func(config.Reader) (forgeapi.KeyManager, error)) GenerateKeyOption
WithKeyManager overrides the forgeapi.KeyManager constructor used when uploading SSH keys. Tests pass a factory returning a fake; production callers omit it to get the registered provider's key-upload capability.
func WithPassphraseForm ¶
func WithPassphraseForm(creator func(*string) *huh.Form) GenerateKeyOption
WithPassphraseForm overrides the passphrase input form (for testing).
func WithUploadConfirmForm ¶
func WithUploadConfirmForm(creator func(*bool) *huh.Form) GenerateKeyOption
WithUploadConfirmForm overrides the upload confirmation form (for testing).
type Initialiser ¶
type Initialiser struct {
// SkipLogin / SkipKey suppress the login and SSH stages of a single-token
// profile. Ignored by dual-credential profiles.
SkipLogin bool
SkipKey bool
// contains filtered or unexported fields
}
Initialiser is the single, profile-parameterised setup.Initialiser shared by every forge setup wizard. The Profile it carries selects the single-token or dual-credential flow and supplies every provider-specific value; the injectable seams below let tests drive the flow without a real forge, keychain, or TTY.
func New ¶
func New(_ *props.Props, profile Profile, opts ...InitialiserOption) *Initialiser
New constructs a profile-driven Initialiser with production defaults (the registered forge provider and the CLI prompter) and applies opts.
func NewBitbucketInitialiser ¶
func NewBitbucketInitialiser(p *props.Props, opts ...InitialiserOption) *Initialiser
NewBitbucketInitialiser builds the dual-credential Bitbucket initialiser.
func NewGitHubInitialiser ¶
func NewGitHubInitialiser(p *props.Props, skipLogin, skipKey bool, opts ...InitialiserOption) *Initialiser
NewGitHubInitialiser builds the single-token GitHub initialiser. Its asset bundle is registered from init() via setup.RegisterAssets, applied for enabled features at root construction.
func (*Initialiser) Configure ¶
Configure runs the interactive wizard for the profile's credential shape. ctx is the caller's context — it deliberately carries no stage-wide deadline (see setup.Initialiser); keychain operations derive their own KeychainOpTimeout bounds at each call site.
func (*Initialiser) IsConfigured ¶
func (i *Initialiser) IsConfigured(cfg config.Reader) bool
IsConfigured reports whether the profile's credential (and, for single-token profiles that offer it, SSH) is already present in the config.
func (*Initialiser) Name ¶
func (i *Initialiser) Name() string
Name returns the human-readable label for this initialiser.
type InitialiserOption ¶
type InitialiserOption func(*Initialiser)
InitialiserOption configures an Initialiser.
func WithAuthForms ¶
func WithAuthForms(opts ...AuthFormOption) InitialiserOption
WithAuthForms propagates [AuthFormOption]s into the single-token wizard. Tests use this to inject deterministic form creators via WithAuthForm.
func WithDualForms ¶
func WithDualForms(opts ...DualFormOption) InitialiserOption
WithDualForms propagates [DualFormOption]s into the dual-credential wizard. Tests use this to inject deterministic form creators via WithDualForm.
func WithPrompter ¶
func WithPrompter(p forgeapi.Prompter) InitialiserOption
WithPrompter overrides the forgeapi.Prompter that renders the device-code step. Tests pass a no-op prompter; production callers omit it to get the default CLI prompter.
func WithProviderFactory ¶
WithProviderFactory overrides the forge provider constructor used for interactive login. Tests pass a factory returning a fake provider (optionally implementing forgeapi.Authenticator); production callers omit it to get the registered provider.
type Profile ¶
type Profile struct {
// Provider is the forge registry key passed to [forgeapi.Lookup]
// (e.g. "github").
Provider string
// ConfigPrefix is the config-key namespace (e.g. "github" →
// "github.auth.env"). It reproduces each provider's existing key layout
// exactly so pre-existing configs keep resolving.
ConfigPrefix string
// Label is the human-facing provider name used in prompts and errors
// (e.g. "GitHub").
Label string
// DisplayName is the initialiser's Name() label shown by the setup
// runner (e.g. "GitHub integration").
DisplayName string
// Feature is the feature flag that gates this initialiser.
Feature props.FeatureCmd
// Host is the default web host: the manual-token URL host and the forge
// provider's release-source host.
Host string
// KeychainAccount is the account portion of the "<service>/<account>"
// keychain reference (e.g. "github.auth").
KeychainAccount string
// Credential selects the single-token or dual-credential flow.
Credential CredentialShape
// FallbackEnv is the well-known token env var used by the single-token
// flow's already-configured detection and env-var-name default
// (e.g. "GITHUB_TOKEN"). SingleToken only.
FallbackEnv string
// OffersSSH enables the SSH key discovery/generation/upload stage.
// SingleToken only.
OffersSSH bool
// OffersLogin enables the forge [forgeapi.Authenticator] login attempt
// before the manual-token fallback. SingleToken only.
OffersLogin bool
// UserFallbackEnv / PassFallbackEnv are the well-known env-var-name
// defaults for the dual flow (e.g. "BITBUCKET_USERNAME" /
// "BITBUCKET_APP_PASSWORD"). DualUserPass only.
UserFallbackEnv string
PassFallbackEnv string
}
Profile describes a forge's interactive setup shape. One generic Initialiser is parameterised by a Profile so GitHub and Bitbucket (and any future forge) share the storage-mode wizard, the single-credential-key exclusivity invariant, and the config-write plumbing — differing only in the per-provider values collected here.