forge

package
v0.33.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 24, 2026 License: MIT Imports: 21 Imported by: 0

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

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

func NewCmdInitBitbucket(p *props.Props) *cobra.Command

NewCmdInitBitbucket creates the `init bitbucket` subcommand.

func NewCmdInitGitHub

func NewCmdInitGitHub(p *props.Props) *cobra.Command

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

func RunGitHubInit(ctx context.Context, p *props.Props, cfg setup.Editor) error

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.

func RunGitHubInitCmd

func RunGitHubInitCmd(ctx context.Context, p *props.Props, dir string) error

RunGitHubInitCmd executes the GitHub configuration and writes the results to the config file, which is seeded from the merged init template when absent.

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

func (i *Initialiser) Configure(ctx context.Context, p *props.Props, cfg setup.Editor) error

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

func WithProviderFactory(fn func(config.Reader) (forgeapi.Provider, error)) InitialiserOption

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL