Documentation
¶
Overview ¶
Package config owns the workflow configuration file, which holds the credentials for the services workflow talks to.
Index ¶
- Constants
- Variables
- func CheckBaseURL(raw string) error
- func Create(path string, cfg Config) error
- func CreateLayers(files Files, cfg Config, over Revision) error
- func DisplayURL(raw string) string
- func KeyRefusals() []error
- func LoadLayersAt(files Files) (Config, Revision, error)
- func Redact(secret string) string
- func RedactURL(raw string) string
- func RepoRoot(dir string) string
- func Save(path string, cfg Config) error
- func SaveEdit(edit Edit) (Config, Revision, error)
- func SharedMode(path string) (os.FileMode, bool)
- type AuthMode
- type Branch
- type Commit
- type Config
- type Credential
- type Edit
- type FieldError
- type Files
- type Forge
- type Issues
- type Jira
- type JiraView
- type Messaging
- type MessagingKind
- type MessagingMode
- type PullRequest
- type Revision
- type Secret
- type Store
- type Taskwarrior
- type Timing
- type UI
Constants ¶
const ( // NoConfigHeadline says no configuration was found. NoConfigHeadline = "No " + FileName + " found." // InitStep names the command that writes a starting configuration. InitStep = "Create one with `workflow config init`." // DoctorStep names the command that checks it. DoctorStep = "Then run `workflow doctor` to see what is still missing." )
Guidance shown wherever a configuration is missing, so the first steps read the same on every surface.
const CurrentVersion = "1"
CurrentVersion is the configuration format version this build writes and reads. A file may leave it out — an unversioned file is read as the current version — but a file that names a version this build does not know is refused rather than half-read against a format it was not written for.
const DefaultCIInterval = 20 * time.Second
DefaultCIInterval is how often CI is asked about while it runs when timing.ci_interval names no interval, and how long the forge's answer serves the web page's stream: often enough to see a check finish soon after it does, rarely enough not to spend a forge's rate limit on it.
const DefaultRequestTimeout = 10 * time.Second
DefaultRequestTimeout bounds every request to a service when timing.request_timeout names none — each doctor --online check, and every request the interface and the web server make. Go's http.Client has no default deadline, so an unreachable on-prem host would otherwise hang doctor, or leave a pane loading forever.
const FileMode os.FileMode = 0o600
FileMode is the permission a configuration file is written with. It holds API tokens, so nobody but the owner may read it.
const FileName = ".workflow.json"
FileName is the configuration file's name in both search locations.
const MotionReduce = "reduce"
MotionReduce is the ui.motion that draws every state still, in the terminal interface and on a command's live line: no mark turns, no rule fills, no bar springs.
Variables ¶
var ( // ErrNotFound reports that no configuration file exists in either location. ErrNotFound = errors.New("no " + FileName + " found") // ErrInvalid reports a file that exists but could not be understood. ErrInvalid = errors.New("invalid " + FileName) // ErrUnknownVersion reports a file naming a format version this build does // not know how to read. ErrUnknownVersion = errors.New("unknown configuration version") // ErrInvalidBaseURL reports a jira.base_url that is not an absolute https // URL, or an http one to this machine. ErrInvalidBaseURL = errors.New("jira.base_url is not an absolute https URL, or http to this machine") // ErrCredentialInBaseURL reports userinfo embedded in jira.base_url. ErrCredentialInBaseURL = errors.New("jira.base_url carries a username and password") // ErrSameName reports two names a map holds apart that are matched as one: // two jira.headers equal but for case, or two branch.prefixes types. ErrSameName = errors.New("two names that are matched as one") // ErrUnknownForgeKind reports a forge.kind that names neither forge. ErrUnknownForgeKind = errors.New("forge.kind is not github or gitlab") )
Errors returned by this package. Callers distinguish them with errors.Is.
var ( // ErrUnknownKeyAction reports a ui.keys entry naming an action that does not // exist — a typo in the action id. ErrUnknownKeyAction = errors.New("ui.keys names a key action that does not exist") // ErrKeyConflict reports two actions bound to the same key where both are // live at once, so a press would be ambiguous. ErrKeyConflict = errors.New("ui.keys binds two actions to one key in the same context") // ErrKeyNotRebindable reports a ui.keys entry moving an action whose keys // cannot be one key: jump-to-pane answers the pane numbers, one per pane. ErrKeyNotRebindable = errors.New("ui.keys moves an action whose keys cannot be rebound") // ErrInterruptEdits reports interrupt moved onto a key that types or edits // text. Interrupt is answered before any filter, prompt or text box, so on // such a key it would quit mid-sentence and lose what was written. ErrInterruptEdits = errors.New("ui.keys moves interrupt onto a key that types or edits text") // ErrTextFieldKey reports an overlay's key moved onto one its focused text // field edits or moves its cursor with — a character, an arrow, or a // readline key such as ctrl+w. The overlay answers the key first, so the // field would lose that edit. ErrTextFieldKey = errors.New("ui.keys moves an overlay's key onto one its text field edits with") // ErrKeyConfirmsItself reports an action that asks first — its press opens // a last look, which apply confirms — sharing a key with apply, so the key // pressed twice would ask and then confirm, as enter twice on Local data // would remove everything. ErrKeyConfirmsItself = errors.New("ui.keys puts an action that asks first on the key that confirms it") )
The refusals of a ui.keys map the terminal interface cannot run, which its key check returns and every caller tells apart with errors.Is.
var ErrChangedOnDisk = errors.New("the configuration file changed on disk")
ErrChangedOnDisk is a save's refusal: the files are no longer at the revision the write was to be made over, so writing would lose whatever changed them.
var ErrCredentialInRepository = errors.New("a credential the configuration files do not already hold is " +
"never written into a repository's file, which git could commit; set it in the home directory's file")
ErrCredentialInRepository is an edit that would write into a repository's file, a file in a working tree git could commit, a credential the files read did not hold.
var ErrHomeOnly = errors.New("only the home directory's file may set it")
ErrHomeOnly is a repository's file setting what only the home directory's file may: a program to run, or an environment variable to read.
var ErrInvalidBranch = errors.New("invalid branch template")
ErrInvalidBranch reports a branch template that could hide the issue key.
var ErrInvalidCommit = errors.New("invalid commit default")
ErrInvalidCommit reports a commit default that a Conventional Commit would not accept.
var ErrInvalidMessaging = errors.New("invalid messaging")
ErrInvalidMessaging reports a messaging block naming a service this build does not post to.
var ErrInvalidPullRequest = errors.New("invalid pull request convention")
ErrInvalidPullRequest reports a pull-request convention a caller could not honor.
var ErrInvalidTaskwarriorProgram = errors.New(
"taskwarrior.program must be a program name or an absolute path, on one line")
ErrInvalidTaskwarriorProgram reports a taskwarrior.program that is not a name looked up on PATH or an absolute path, on one line: a path relative to the working directory names another program in each directory workflow starts in, and a name or a path cannot hold a line break or a NUL.
var ErrInvalidTiming = errors.New("invalid timing")
ErrInvalidTiming reports a timing setting that is not a positive duration.
var ErrInvalidUI = errors.New("invalid ui setting")
ErrInvalidUI reports a ui setting the terminal interface could not honor.
var ErrInvalidView = errors.New("invalid jira view")
ErrInvalidView reports a jira view missing its name or its query.
var ErrLinkedFile = errors.New("a configuration file in a repository may not be a link")
ErrLinkedFile is a repository's configuration file that is a link: a working tree can carry one to any file of the user's, which a save would replace.
var ErrNotARevision = errors.New("not a configuration file revision")
ErrNotARevision is ParseRevision's refusal of text no Revision writes.
var ErrTokenNotKept = errors.New("the keychain did not keep the Jira token typed; nothing was saved")
ErrTokenNotKept is a save's refusal: the keychain did not keep the Jira token typed into the editor, so nothing was written.
var ErrUntrustedFile = errors.New("someone other than you could have written this configuration file")
ErrUntrustedFile is a configuration file someone other than the user could have written: one another user owns, or one others may write.
Functions ¶
func CheckBaseURL ¶ added in v0.4.0
CheckBaseURL reports whether raw can be jira.base_url: an absolute https URL with a host, or an http one to this machine, since every request carries the token and every jira.headers value; and with no username or password, which net/http would turn into an Authorization header competing with the configured token. The error never quotes raw, which may carry that password.
func Create ¶ added in v0.7.0
Create writes the configuration to a new file at path, at FileMode, and writes nothing when anything is already there, a link included, so a link planted where the file goes cannot steer its credentials elsewhere. The refusal wraps fs.ErrExist.
func CreateLayers ¶ added in v0.7.0
CreateLayers writes the configuration as SaveLayers does, but only as a new file: like Create, it writes nothing when anything — a link included — is already where the file goes.
func DisplayURL ¶
DisplayURL is a URL as it may be shown: a password in it masked, and an unset one said so. Both doctor and the terminal interface show jira.base_url, and when each did this by hand, one of them forgot the mask.
func KeyRefusals ¶ added in v0.9.0
func KeyRefusals() []error
KeyRefusals are every refusal of a ui.keys map, so a caller sorting errors by kind counts a new one without a second edit.
func LoadLayersAt ¶ added in v0.5.0
LoadLayersAt reads the configuration files names, the repository's over the home directory's, with the revision of the pair. Each file's keys are checked on their own, so an unknown one names its file; the layers are then merged and validated as one, since two valid files can still disagree. With neither file there, it is the defaults at the no-file revision.
func Redact ¶
Redact masks a secret, keeping only enough of the tail to recognize it, and none of one too short to spare it. A mask it gave is left as it is, since a surface masks what it was handed again and sends a mask back as it was shown, which KeepStored reads as the secret kept.
func RedactURL ¶
RedactURL masks the userinfo of a URL, leaving the rest readable.
An address is not a secret, so it is shown in full — but a git remote can carry https://user:password@, and doctor prints it into output the bug report template asks people to paste into a public issue. The userinfo is masked whole rather than by its password alone, because which half holds the part worth hiding is the writer's choice, not something to be guessed from here.
func RepoRoot ¶ added in v0.0.6
RepoRoot returns the root of the repository dir sits in — the nearest ancestor holding a .git — so a file written there is found from any subdirectory below it. It returns dir unchanged when no repository encloses it, which is where a lone file still belongs.
func Save ¶
Save writes the configuration to path at FileMode, replacing whatever is there, or the file a link there points at. The new file is made in the directory of the file it replaces, so that directory must be writable too.
func SaveEdit ¶ added in v0.7.0
SaveEdit writes an edited configuration over the read it was made from, keeping each credential the editor left masked, and returns what it wrote with the revision it left. Files changed since the read are refused with ErrChangedOnDisk, and a credential the files read did not hold that would land in a repository's file with ErrCredentialInRepository; either way nothing is written. A Jira token typed is kept in the keychain item for its address where KeepJiraToken is wired, and so lands in no file. Every credential but those placed elsewhere is checked before any is placed, since placing the Slack secrets spends the refresh token typed, and every one before the keychain is handed the token, which replaces the item it held. Neither is done for a write the files would then refuse.
Types ¶
type Branch ¶
type Branch struct {
// Template shapes the name from {prefix}, {key} and {slug}; empty keeps the
// default "{prefix}/{key}-{slug}". It must contain {key}, because everything
// the interface derives from a branch reads the issue key back out of it.
Template string `json:"template"`
// Prefixes maps an issue type to its branch prefix, such as {"bug": "bugfix"};
// the type is matched without regard to case. A type not listed takes
// DefaultPrefix. Configuring this replaces the built-in map rather than adding
// to it, so what the file says is the whole rule.
Prefixes map[string]string `json:"prefixes"`
// DefaultPrefix is the prefix for a type not in Prefixes; empty keeps "feat".
DefaultPrefix string `json:"default_prefix"`
// SlugLimit caps the summary slug in a branch name, in characters; zero keeps
// the built-in 48.
SlugLimit int `json:"slug_limit"`
}
Branch shapes the branch name workflow proposes for an issue, so a team can match its own convention. Every field is optional; empty keeps the default.
func (Branch) Naming ¶ added in v0.3.0
func (b Branch) Naming() convention.BranchNaming
Naming is how a branch is named for an issue under this section: the configured template and prefixes, and the built-in convention for any piece left empty.
type Commit ¶ added in v0.0.4
type Commit struct {
// DefaultScope pre-fills the scope field when no kept draft has one. It must
// be a scope a Conventional Commit subject would accept, or the composer would
// open already flagged.
DefaultScope string `json:"default_scope"`
// Types are the commit types the composer offers and validates against, in the
// order to offer them; empty keeps the built-in Conventional Commit types.
// Each is a lowercase word, matched as a branch prefix too.
Types []string `json:"types"`
// SubjectLimit is the longest a subject may be, in characters; zero keeps the
// built-in 72.
SubjectLimit int `json:"subject_limit"`
// RefsTrailer labels the issue trailer added to a commit body, such as
// "Closes"; empty keeps "Refs". It is a single trailer word, no colon.
RefsTrailer string `json:"refs_trailer"`
}
Commit shapes the commit messages the composer helps write, so a team with its own conventions does not fight the defaults. Every field is optional; empty or zero keeps the built-in default.
type Config ¶
type Config struct {
// Version is the file format's version; empty means the current one. It is
// first so `config init` writes it at the top of the file.
Version string `json:"version"`
Jira Jira `json:"jira"`
Messaging Messaging `json:"messaging"`
Forge Forge `json:"forge"`
UI UI `json:"ui"`
Timing Timing `json:"timing"`
Branch Branch `json:"branch"`
Commit Commit `json:"commit"`
PullRequest PullRequest `json:"pull_request"`
Store Store `json:"store"`
Taskwarrior Taskwarrior `json:"taskwarrior"`
Issues Issues `json:"issues"`
// Path is the file a save of this configuration writes: the repository's
// file when there is one, otherwise the home directory's. It is not part of
// the file format.
Path string `json:"-"`
// Files are the files this configuration was read from, the repository's
// layered over the home directory's. Not part of the file format either.
Files Files `json:"-"`
}
Config is the whole configuration file.
func Default ¶
func Default() Config
Default is the configuration before any file is read: every setting that has a default holds it. A file is decoded over it, so a setting the file leaves out keeps its default — which a bool cannot do by itself, since its zero value is false.
Every error path returns it as well. The interface still opens without a configuration, to say what is wrong, and it should open working normally.
func KeepStored ¶ added in v0.7.0
func KeepStored(incoming, stored Config, removed []Credential) Config
KeepStored keeps each stored secret when its incoming field is empty or still the masked value Redacted gave the editor — a configuration editor, the web's Settings or the terminal's, sends the masked form back unchanged, and must not overwrite the real secret with the mask — unless the editor removed it.
func Load ¶
Load reads the configuration that applies: the files Locate finds, the repository's layered over the home directory's.
func Parse ¶ added in v0.0.6
Parse decodes and validates a configuration from r, over the defaults. Unknown keys are an error, and the same validators a file read runs apply — so a configuration written over the web API is held to exactly the standard a file on disk is. It does not set Path; that belongs to the file it came from.
func Template ¶
func Template() Config
Template is the starting configuration `workflow config init` writes.
func (Config) CIInterval ¶
CIInterval is the configured gap between CI checks, or zero when none is set.
func (Config) Layers ¶ added in v0.5.0
Layers are the files c was read from, or the one file Path names when it was set without them, as a configuration built in place of a read is: a file of the user's own, which is the home file.
func (Config) Missing ¶
Missing names the configuration fields that are still empty, in the order a person would fill them in. An empty result means the configuration is complete.
Jira is optional: with no jira.base_url the forge's own issues are the tracker, so no Jira field is required. Once jira.base_url is set, Jira is the tracker and its token is required with it.
func (Config) Problems ¶ added in v0.0.6
Problems reports settings that load but are unwise — a webhook sent over plain http — as opposed to Missing, which reports what is still empty. A configuration can be complete and still not work, so doctor should say which values are bad rather than call such a file all clear; a value that can never work is refused when the file loads instead.
func (Config) RedactText ¶ added in v0.4.0
RedactText masks every credential c holds wherever text carries it, each as Redacted shows it — a token, a webhook URL or a Jira header value down to its recognizable tail, the userinfo of jira.base_url whole — so text another program wrote, which could quote one, can be printed.
func (Config) RequestTimeout ¶
RequestTimeout is the configured per-request timeout, or zero when none is set, for the caller to fall back to its own default.
type Credential ¶ added in v0.8.0
type Credential string
Credential names a credential an editor can remove, by its path in the file.
const ( CredentialJiraToken Credential = "jira.token" CredentialForgeToken Credential = "forge.token" CredentialWebhookURL Credential = "messaging.webhook_url" CredentialClientSecret Credential = "messaging.client_secret" CredentialRefreshToken Credential = "messaging.refresh_token" )
The credentials an editor can remove. A Jira header is removed by leaving it out of jira.headers, and the Slack access token goes with the client secret or the refresh token it was made from. Each is a path in the file, not a credential, which gosec's name-based check cannot tell.
type Edit ¶ added in v0.7.0
type Edit struct {
// Files are the configuration files the read was made from.
Files Files
// Read is the configuration the editor was seeded from, unmasked.
Read Config
// Over is the revision of the files the read found.
Over Revision
// Edited is what the editor holds: a credential left masked or empty
// stands for Read's.
Edited Config
// Removed are the credentials the editor removed: each is written empty,
// whatever Edited holds for it.
Removed []Credential
// PlaceSlackCredentials keeps a Slack user token's secrets, typed into the
// editor, where the configuration keeps them, and answers the configuration
// to write. Nil keeps them in the file.
PlaceSlackCredentials func(Config) (Config, error)
// KeepJiraToken keeps a Jira token typed into the editor in the OS
// keychain, under the item service names, so the file written reads it
// from there. Nil, where no keychain is wired, keeps it in the file.
KeepJiraToken func(service, secret string) error
}
Edit is a configuration changed in an editor — the web's Settings, or the terminal's — over a read of it, with each credential the editor was shown masked.
type FieldError ¶ added in v0.11.0
FieldError is a validation's refusal of one setting, which it names by its dotted key as the file spells it: jira.base_url, branch.slug_limit.
func FieldErrors ¶ added in v0.11.0
func FieldErrors(err error) []FieldError
FieldErrors is each setting err refuses, in the order Parse validates them, through any wrapping; none for an error that refuses no one setting, such as a key the configuration does not have.
func (FieldError) Error ¶ added in v0.11.0
func (e FieldError) Error() string
Error is the refusal, which already says which setting it refuses and why.
func (FieldError) Unwrap ¶ added in v0.11.0
func (e FieldError) Unwrap() error
Unwrap is the refusal, so errors.Is still finds its kind.
type Files ¶ added in v0.5.0
Files are the configuration files that apply to a directory: the home directory's, and the repository's layered over it, setting by setting. Either may be empty, for no file there.
func Locate ¶ added in v0.5.0
Locate finds the configuration files that apply: the nearest one walking up from workDir, no higher than the repository root and no higher than workDir outside one, layered over the one in homeDir. The home directory's file is the home file wherever it is found from. It returns ErrNotFound when neither location has one.
func (Files) Each ¶ added in v0.5.0
Each is every file f names, the home directory's first, as it is read.
type Forge ¶
type Forge struct {
// Kind is "github" or "gitlab", and is needed only for an on-premises host
// whose name says neither — a GitHub Enterprise Server and a self-managed
// GitLab look identical from a git remote, and their APIs differ.
Kind string `json:"kind"`
// Host is the host Kind and Token were written for, such as
// git.example.com.
Host string `json:"host"`
// Token is a GitHub or GitLab personal access token, consulted only when
// neither the environment nor the forge's own CLI supplies one.
//
// It is deliberately absent from Missing(). Reporting it as missing would
// fail `workflow doctor` for everyone correctly relying on `gh auth login`,
// which is the common case and the one worth encouraging.
Token Secret `json:"token"`
// CLI routes forge API calls through the forge's own command-line tool — gh
// for GitHub, glab for GitLab — instead of over HTTP directly, so the login
// those tools already hold carries the request. This is what reaches a forge
// behind an SSO gateway a bare token cannot. It falls back to HTTP when the
// tool is not installed.
CLI bool `json:"cli"`
}
Forge describes the Git forge credential — which workflow usually does not need to hold at all.
type Issues ¶ added in v0.5.0
type Issues struct {
// Forge adds the issues assigned to you on the repository's own forge,
// GitHub or GitLab, to the list beside Jira's: for a repository whose
// project tracks its work there. It is set per repository, in that
// repository's file, over the home file's default. With no Jira, the
// forge's issues are the whole list whatever it says.
Forge bool `json:"forge"`
}
Issues is where the Issues list draws from beyond Jira.
type Jira ¶
type Jira struct {
// BaseURL is the root of the Jira instance, e.g. https://jira.example.com.
BaseURL string `json:"base_url"`
// Token is a personal access token. Leave it empty to take the token from
// TokenCommand or TokenEnv instead, so the file holds no secret.
Token Secret `json:"token"`
// TokenCommand is a program that prints the token, such as
// "pass show jira/token"; TokenEnv is an environment variable that holds it.
// Only the home directory's file may set either.
TokenCommand string `json:"token_command"`
TokenEnv string `json:"token_env"`
// Keychain reads the token from the OS keychain item KeychainService names
// for BaseURL, so the file holds no secret. It names no program and reads
// only the item for the address in effect, so any file may set it, and a
// repository's file that moves BaseURL keeps it: it then reads the item
// for its own address, never the one for the home file's.
Keychain bool `json:"keychain"`
// User is optional. Leave it empty for token (Bearer) authentication; set it
// to authenticate with HTTP Basic instead.
User string `json:"user"`
// Headers are extra HTTP headers sent with every Jira request, added after
// the token, for a Jira reached through a proxy that wants one of its own —
// an SSO gateway checking a header, say. A value may be a secret, so each
// is a Secret, masked wherever the configuration is shown.
Headers map[string]Secret `json:"headers"`
// Views are named issue lists the pane can move between, each a label and its
// JQL. Empty keeps the one built-in list: open issues assigned to you.
Views []JiraView `json:"views"`
// Project is the Jira project key, such as "PROJ". When set, only a branch
// naming a key in that project is read as an issue, so a name like
// fix/UTF-8-decoding is not mistaken for one. Empty falls back to a looser
// guard that rejects common technical tokens (UTF, SHA, CVE) by shape alone.
Project string `json:"project"`
// MarkdownComments rewrites a comment written in Markdown as the wiki markup
// Jira renders before posting it, so headings, emphasis, code and links come
// out formatted rather than literal. On by default; false posts a comment
// unchanged, for one already written in wiki markup.
MarkdownComments bool `json:"markdown_comments"`
// ReviewStatus is the status an issue moves to once its pull request is open,
// e.g. "In Review". Opening a pull request offers the change to this status by
// name — "in review" and "in progress" share a category, so the loop cannot
// pick it out on its own. Empty makes no offer.
ReviewStatus string `json:"review_status"`
}
Jira describes how to reach an on-premises Jira instance.
func (Jira) Configured ¶ added in v0.0.5
Configured reports that a Jira instance is set as the issue tracker, by its base URL. When it is not, the forge's own issues back the Issues pane instead.
func (Jira) KeychainService ¶ added in v0.9.0
KeychainService is the name of the OS keychain item the token for BaseURL is kept in: one item for each address, so a token is only ever read back for the address it was kept for. An address and the same one with trailing slashes, which a request reaches alike, name one item.
type JiraView ¶ added in v0.0.3
JiraView is a named issue list: a label and the JQL that fills it, so the interface can offer more than the one built-in "assigned to me" list — a sprint, a team filter, the unassigned pile.
type Messaging ¶ added in v0.1.0
type Messaging struct {
// Kind is the service posts go to: "slack" (the default when empty),
// "teams", "discord", or a plain "webhook". It decides the body and the link
// markup the notifier sends.
Kind MessagingKind `json:"kind"`
// ClientID is the Slack app's client ID, which a rotating user token is
// refreshed with. It is no secret; `workflow slack login` writes it.
ClientID string `json:"client_id"`
// ClientSecret, RefreshToken, AccessToken and ExpiresAt are the Slack user
// token's rotating credentials when this file is where they are kept — on
// Linux and Windows, or on macOS once they are put here — rather than in
// the keychain. workflow rewrites the last three after every refresh. The
// access token starts "xoxe.xoxp-", the refresh token "xoxe-1-"; ExpiresAt
// is RFC 3339 UTC.
ClientSecret Secret `json:"client_secret"`
RefreshToken Secret `json:"refresh_token"`
AccessToken Secret `json:"access_token"`
ExpiresAt string `json:"expires_at"`
// WebhookURL is an incoming webhook. It is a credential in its own right —
// anyone holding it can post to that channel — so it is masked wherever a
// token would be.
WebhookURL Secret `json:"webhook_url"`
// Channel is the channel updates are posted to, e.g. "#dev-workflow". It
// applies to a Slack user token only; a webhook carries its own channel.
Channel string `json:"channel"`
// Channels are further channels a user-token post may be routed to at post
// time, for a change that concerns another team. The default Channel is
// always available too.
Channels []string `json:"channels"`
// Announcement shapes the Slack review message from named placeholders —
// {author}, {noun}, {title}, {url}, {key}, {summary}, {issue_url}. Empty, or
// any non-Slack kind, uses the built-in message. Substituted values are
// always escaped.
Announcement string `json:"announcement"`
// AutoAnnounce arms the announcement of a pull request the terminal or
// --web opens, to post once its CI passes. On by default; false arms
// nothing, as a repository with no CI may want, while asking to announce
// once CI passes still waits.
AutoAnnounce bool `json:"auto_announce"`
}
Messaging describes how workflow posts team updates. Kind picks the service; Slack alone offers two transports, of which a configuration sets up exactly one — a rotating user token, which posts as you anywhere you can post and reports back what it posted, or an incoming webhook fixed to one channel — while Teams, Discord and a plain webhook post over an incoming webhook only.
func (Messaging) ChannelChoices ¶ added in v0.1.0
ChannelChoices are the channels a user-token post can be sent to: the default channel first, then the alternates, without duplicates or blanks. A webhook carries its own channel and offers none.
func (Messaging) HoldsUserTokenSecrets ¶ added in v0.5.0
HoldsUserTokenSecrets reports that this file keeps the user token's rotating credentials — the client secret or a token — which makes the file, not the keychain, where they are read and written.
func (Messaging) Mode ¶ added in v0.1.0
func (m Messaging) Mode() MessagingMode
Mode reports how this configuration posts. Parse refuses a Slack block that sets up both a user token and a webhook, and a user token on any other kind, so at most one transport is ever set up.
type MessagingKind ¶ added in v0.1.0
type MessagingKind string
MessagingKind is the service a messaging block posts to. Empty is read as Slack, so a block written before more services existed still works.
const ( // KindSlack posts to Slack, with a rotating user token or over an incoming // webhook. KindSlack MessagingKind = "slack" // KindTeams posts to a Microsoft Teams incoming webhook. KindTeams MessagingKind = "teams" // KindDiscord posts to a Discord webhook. KindDiscord MessagingKind = "discord" // KindWebhook posts plain text to any incoming webhook. KindWebhook MessagingKind = "webhook" )
func (MessagingKind) Known ¶ added in v0.1.0
func (k MessagingKind) Known() bool
Known reports whether k is a kind this build understands. An empty kind is known: it is read as Slack for a block written before kinds existed.
func (MessagingKind) Service ¶ added in v0.1.0
func (k MessagingKind) Service() string
Service names the messaging service for display: the pane title and doctor.
type MessagingMode ¶ added in v0.1.0
type MessagingMode int
MessagingMode is the transport a message travels over.
const ( // MessagingNone means no messaging credential is configured. MessagingNone MessagingMode = iota // MessagingUser posts with a Slack user token, as you, to a channel you can // post in. Only Slack has one; the other services post over a webhook. MessagingUser // MessagingWebhook posts to an incoming webhook, which carries its own // channel. MessagingWebhook )
func (MessagingMode) String ¶ added in v0.1.0
func (m MessagingMode) String() string
String names the transport for humans.
type PullRequest ¶ added in v0.3.0
type PullRequest struct {
// TitleSource decides where a pull request's title comes from: "commit" (the
// default) takes the branch's oldest commit, "issue" takes the issue it names.
TitleSource string `json:"title_source"`
}
PullRequest shapes the pull requests workflow proposes, so a team can match its own convention. Every field is optional; empty keeps the built-in default.
type Revision ¶ added in v0.3.1
type Revision struct {
// contains filtered or unexported fields
}
Revision is one state of the configuration file: a keyed SHA-256 (HMAC) of its bytes, or, as the zero value, no file at all. Two revisions are the same state exactly when they are ==. A hash of the contents rather than a modification time, since an edit that keeps the size inside one clock tick still changes the hash, and a touch that changes nothing does not.
func ParseRevision ¶ added in v0.3.1
ParseRevision reads a revision back from its String form.
func RevisionOfLayers ¶ added in v0.5.0
RevisionOfLayers reads the revision of the configuration files names.
func SaveLayers ¶ added in v0.5.0
SaveLayers writes the configuration to the file files saves to, while the pair is still at the revision over; otherwise it writes nothing and returns ErrChangedOnDisk. Written over a home file, the repository's file holds only what differs from it, so a setting inherited — a token among them — is never copied into a file in a working tree, and a later change at home still reaches it. It returns the revision of the pair it leaves.
type Secret ¶ added in v0.0.6
type Secret string
Secret is a credential the configuration carries: a token, or a webhook URL that is a credential in its own right.
It is a named type with a String and a GoString that mask rather than a bare string, and that is a guard, not a decoration: every verb a string takes — %v, %s, %q, %x, %X and %#v — goes through one of them and yields the mask, as does a Secret in an exported field or a map printed with %v, %+v or %#v. fmt calls neither method for a verb a string rejects (%d, %t, ...), which go vet's printf check, on in the linters, flags, nor for a Secret in an unexported field. Redact already masks wherever a configuration is shown on purpose; this stands behind it, so a future raw %v of a Config cannot print what Redact was never asked to hide. Reading the real value takes an explicit Reveal, which is easy to find in review and impossible to do by accident.
func (Secret) GoString ¶ added in v0.4.0
GoString masks the secret under %#v, quoted so the output still reads as Go syntax: "****", or "" for an empty Secret.
type Store ¶ added in v0.3.0
type Store struct {
// Disabled turns the store off, so workflow keeps nothing between
// sessions: nothing the user decides, and none of the conveniences, which
// it works out afresh each time. No announcement tags anyone, since who is
// whom cannot be kept: the terminal's ready-for-review preview lists each
// code owner as not linked and offers no link, and workflow announce and
// the web tag no one. No favorites are kept. For a machine where no
// workflow state should touch the disk.
Disabled bool `json:"disabled"`
}
Store controls the on-disk store that keeps a little workflow state between sessions: the conveniences a session can see again — the commit scope last used in a repository, what was announced, the last issue list seen — and what the user decided — whom each code owner is on Slack, the groups a repository tags, the favorite directories. It is on by default and never holds a secret.
type Taskwarrior ¶ added in v0.4.1
type Taskwarrior struct {
// Program is the task program to run: a name looked up on PATH, or a path.
// Only the home directory's file may set it.
// Empty tries every task in an absolute PATH directory, in order, and keeps
// the first that answers as Taskwarrior — go-task, the Taskfile runner, is
// also called task.
Program string `json:"program"`
// Disabled turns the integration off even where Taskwarrior is installed.
Disabled bool `json:"disabled"`
}
Taskwarrior configures the optional Taskwarrior integration, on whenever a Taskwarrior 3.5.0 or newer is found.
type Timing ¶
type Timing struct {
// RequestTimeout bounds each request to a service. The default is
// DefaultRequestTimeout.
RequestTimeout string `json:"request_timeout"`
// CIInterval is how often CI is asked about while it runs, and how often
// the web page's stream asks the forge about the branch. The default is
// DefaultCIInterval.
CIInterval string `json:"ci_interval"`
}
Timing is how long the interface waits, for a network or a service that is slower or more rate-limited than the defaults assume. Each is a Go duration string, such as "20s" or "3m"; empty keeps the default.
type UI ¶
type UI struct {
// Mouse captures the mouse, so a click focuses a pane or selects a row.
// Capturing it takes away the terminal's own click-and-drag selection,
// which is why it can be turned off here, and toggled with m in a session.
Mouse bool `json:"mouse"`
// ASCII draws borders and glyphs in plain ASCII, for a terminal or font
// without box-drawing characters, and so the live line a command keeps
// while it waits. There is no reliable way to detect that, so it is a
// setting rather than a guess.
ASCII bool `json:"ascii"`
// Color is whether to draw the system hues: empty (auto) draws them when the
// terminal allows, "never" turns them off while keeping bold, faint and the
// reverse-video cursor, which carry meaning without color.
Color string `json:"color"`
// Motion is whether the terminal interface, and the live line a command
// keeps while it waits, move: empty turns the in-flight mark, fills a
// stage's rule as it completes and springs the CI bar; "reduce" draws
// every state still, for anyone motion distracts or harms.
Motion string `json:"motion"`
// Accessible hands each multi-field form in the terminal the whole
// terminal as a series of plain prompts, one question at a time, which a
// screen reader reads as it would any command's, instead of drawing it
// over the screen. The ACCESSIBLE environment variable, set to anything,
// does the same.
Accessible bool `json:"accessible"`
// Notify rings the terminal, and sends a desktop notification where the
// terminal relays one, when CI finishes — and the --web page says so in its
// header — so a developer who stepped away is told rather than having to
// check back. Off unless set.
Notify bool `json:"notify"`
// CommentsShown is how many of an issue's most recent comments the detail
// pane draws. Zero keeps the built-in default.
CommentsShown int `json:"comments_shown"`
// Keys rebinds the interface's keys. Each entry maps an action to the single
// key that should trigger it, e.g. {"commit": "C", "comment": "ctrl+e"}; the
// help then shows the new key. An action left out keeps its default. The
// interface refuses to start on a map that names an action it does not know,
// moves jump-to-pane (its keys are the pane numbers, which no one key can
// stand in for), or binds two actions in the same context to one key. The
// actions it can rebind are listed under "Rebinding keys" in
// docs/content/docs/configuration.md, which a terminal test holds to the
// bound ones.
Keys map[string]string `json:"keys"`
// WebShortcuts turns on the --web page's single-key shortcuts — the
// terminal's keys, ui.keys applied, pressed outside a text field. Off
// unless set, since a character key that acts on its own surprises a
// screen reader or speech user, who presses keys for other reasons (WCAG
// 2.1.4). The ? sheet and the command palette, on ctrl+k or cmd+k, work
// either way.
WebShortcuts bool `json:"web_shortcuts"`
}
UI is how the terminal interface behaves, how a command's live line is drawn, and the one keyboard setting the --web page reads beside its keys.
func (UI) AccessibleForms ¶ added in v0.11.0
AccessibleForms reports whether the terminal's forms ask as plain prompts: ui.accessible on, or ACCESSIBLE, whose value is accessibleEnv, set to anything at all.
func (UI) DrawColor ¶
DrawColor reports whether the system hues should be drawn. NO_COLOR set to a non-empty value and ui.color "never" both turn them off; bold, faint and reverse stay. An empty NO_COLOR keeps them, as no-color.org asks.
func (UI) ReducedMotion ¶ added in v0.11.0
ReducedMotion reports whether the interface, and a command's live line, draw every state still.