Documentation
¶
Overview ¶
Package store keeps a little workflow state on disk between sessions, in two SQLite databases under the OS-native data directory. workflow.db is the cache, what a session can see again: the commit scope last used in a repository, what was announced, and the last issue list seen. kept.db is what the user decided and a session cannot see again: whom a forge owner is on Slack, a repository's groups, and the favorite directories. Neither ever holds a secret, and callers key them only by credential-free identifiers.
Each schema is Third Normal Form and every table is STRICT: repeating groups — the cached issue list — are a parent row and one child row each, no derived value is stored, and each column's type is enforced at write time. Timestamps are RFC3339 UTC text, a child's foreign key cascades from the row that owns it and restricts toward a Slack user or group others share, and a write that replaces a group runs in one transaction. Callers sanitize text on the way in and treat what they read back as untrusted, since a file on disk can be tampered.
A store is a value, and a disabled one no-ops every method, so a caller need not special-case the privacy opt-out. Each operation opens its own short-lived connection, so there is no handle to close and the terminal interface and the web server can share the files; WAL and a busy timeout keep their writes from colliding.
Each file's schema has one version, stamped into it. A workflow.db written at another is discarded and started fresh, since what it held is seen again. A kept.db written at another is never discarded: it is left as it is, for the user to remove.
Index ¶
- Variables
- func Clean(dir string, scope CleanScope) error
- func DefaultDir() (string, error)
- func Dir(goos, home string, lookupEnv func(string) (string, bool)) (string, error)
- func HumanBytes(size int64) string
- type Announce
- type CachedIssue
- type CleanScope
- type DataFile
- type DataKind
- type Favorite
- type Held
- type OwnerLink
- type SlackTarget
- type Store
- func (s Store) Announces(ctx context.Context, repo string) ([]Announce, error)
- func (s Store) CacheIssues(ctx context.Context, instance, view string, issues []CachedIssue, ...) error
- func (s Store) CachedIssues(ctx context.Context, instance, view string) ([]CachedIssue, bool, error)
- func (s Store) Favor(ctx context.Context, dir string, now time.Time) error
- func (s Store) Favorites(ctx context.Context) ([]Favorite, error)
- func (s Store) ForgetOwner(ctx context.Context, forgeHost, workspace, owner string) error
- func (s Store) Keeps() bool
- func (s Store) LastGroups(ctx context.Context, repo, workspace string) ([]string, bool, error)
- func (s Store) LastScope(ctx context.Context, repo string) (string, bool, error)
- func (s Store) LinkOwner(ctx context.Context, forgeHost, workspace string, decision OwnerLink, ...) error
- func (s Store) OwnerLinks(ctx context.Context, forgeHost, workspace string) ([]OwnerLink, error)
- func (s Store) ReadOnly() Store
- func (s Store) RecordAnnounce(ctx context.Context, repo string, announce Announce, now time.Time) error
- func (s Store) RecordGroups(ctx context.Context, repo, workspace string, ids []string, now time.Time) error
- func (s Store) RecordScope(ctx context.Context, repo, scope string, now time.Time) error
- func (s Store) RepoGroups(ctx context.Context, repo, workspace string) ([]SlackTarget, error)
- func (s Store) SetRepoGroups(ctx context.Context, repo, workspace string, groups []SlackTarget, ...) error
- func (s Store) Unfavor(ctx context.Context, dir string) error
- func (s Store) Writes() bool
Constants ¶
This section is empty.
Variables ¶
var ErrCleanRefused = errors.New("not the store's own files to clean")
ErrCleanRefused reports a clean that would reach past the store's own plain files: a relative directory, or a symlink or directory where a database file belongs. Nothing is removed.
var ErrInvalidOwner = errors.New("not a forge owner")
ErrInvalidOwner reports a forge owner not shaped like a CODEOWNERS user or team.
var ErrInvalidSlackID = errors.New("not a Slack ID of the right kind")
ErrInvalidSlackID reports a Slack ID of the wrong shape, or of the wrong kind for what it is linked to.
var ErrKeptSchemaDiffers = errors.New(
"the kept data was written by a build of workflow with another schema; " +
"run `workflow db-clean --all` to start it fresh")
ErrKeptSchemaDiffers reports a kept database written at another schema version than this build's. It is left as it is: it reads as empty, and a write is refused rather than risk what it holds.
var ErrNoDir = errors.New("could not determine a data directory for the store")
ErrNoDir reports that no OS-native data directory could be determined.
var ErrNoWorkspace = errors.New("no Slack workspace to keep Slack links under")
ErrNoWorkspace reports a Slack link read or made under no Slack workspace, whose links no one could tell from another workspace's.
var ErrNotADirectoryPath = errors.New("a favorite is an absolute path to a directory, written plainly")
ErrNotADirectoryPath is a favorite that is not an absolute, clean path.
var ErrNotCleaned = errors.New("could not remove a store file")
ErrNotCleaned reports a file a clean could not remove, as one another program holds open can be on Windows.
Functions ¶
func Clean ¶ added in v0.5.0
func Clean(dir string, scope CleanScope) error
Clean removes the cache's database from dir, with its companions, and with CleanAll the kept data's too. It refuses, removing nothing, a relative dir or anything but a plain file where a database file belongs. Every file is set aside before any is removed, and one that cannot be set aside puts the rest back, so a file another program holds open fails the clean without leaving half a database behind. What an earlier clean set aside and could not remove goes first.
func DefaultDir ¶
DefaultDir is the OS-native directory workflow keeps its store in, from the running machine's home and environment.
func Dir ¶
Dir is the OS-native store directory for a platform, home and environment, taken as arguments so every platform's path can be exercised from one machine: %AppData%\workflow on Windows, ~/Library/Application Support/workflow on macOS, and $XDG_STATE_HOME or ~/.local/state/workflow elsewhere. A variable that is not an absolute path is ignored, as the XDG specification says to: joined, it would put the store wherever workflow happened to start.
func HumanBytes ¶ added in v0.9.0
HumanBytes is a size in bytes, KiB or MiB, with one decimal past bytes: how every surface shows a database file's size.
Types ¶
type Announce ¶
Announce is one announcement made in a repository: which pull request, and the moment it marked — opened, or merged. The moment is an opaque int the interface assigns, so the store need not know the interface's vocabulary.
type CachedIssue ¶
type CachedIssue struct {
Key string
Summary string
Status string
StatusCategory string
Type string
Priority string
}
CachedIssue is the store's shape of a cached issue — the few non-secret fields a first pane needs, kept as the store's own type so it stays decoupled from the interface's domain type. status_category is stored beside status even though a Jira status maps to one category: the mapping is Jira's, not ours to derive.
type CleanScope ¶ added in v0.5.0
type CleanScope int
CleanScope says how much a clean removes.
const ( CleanCache CleanScope = iota CleanAll )
The scopes of a clean: the cache alone, or the kept data too.
func (CleanScope) Consequence ¶ added in v0.9.0
func (s CleanScope) Consequence() string
Consequence is what a clean of the scope takes with it, in the words every surface's last look warns with before anything is removed.
type DataFile ¶ added in v0.5.0
DataFile is one database file in the store directory: its name, its kind, its size with its write-ahead log and shared-memory companions, and what it holds, where that could be read.
type DataKind ¶ added in v0.5.0
type DataKind string
DataKind says what a database file holds: conveniences a session sees again, or what the user decided.
type OwnerLink ¶ added in v0.5.0
type OwnerLink struct {
Owner string
Team bool
OnSlack bool
Slack SlackTarget
}
OwnerLink is what was decided for one forge owner: whether they are a team, which links to a Slack user group, or a person, who links to a Slack user; whether they are on Slack; and if so, as whom.
type SlackTarget ¶ added in v0.5.0
SlackTarget is a Slack user or user group, by its ID and the label it was last seen with — the store's own shape, in plain strings.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store points at the on-disk store. Its zero value, and any disabled store, no-op every method, so persistence-off behaves exactly as no store at all.
func New ¶
New points a store at dir. Disabled keeps the code path identical for a user who opts out of persistence: every method no-ops.
func (Store) Announces ¶
Announces are every announcement recorded for a repository, so the interface can open knowing what has already been posted. A disabled store reports none.
func (Store) CacheIssues ¶
func (s Store) CacheIssues(ctx context.Context, instance, view string, issues []CachedIssue, now time.Time) error
CacheIssues stores issues as the list last seen for a view of an instance, replacing any earlier one in a single transaction so a re-cache never leaves a half-updated view. A disabled or read-only store caches nothing.
func (Store) CachedIssues ¶
func (s Store) CachedIssues(ctx context.Context, instance, view string) ([]CachedIssue, bool, error)
CachedIssues is the issue list last cached for a view of a Jira instance, in the tracker's order, and whether the view was cached at all — a view cached with no issues reports found with an empty slice, distinct from one never cached. A disabled store, or one with no instance, reports nothing.
func (Store) Favor ¶ added in v0.6.0
Favor marks dir a favorite, keeping when it was first marked. A row for dir whose time does not read back is one Favorites leaves out, so its time is written afresh rather than kept. A disabled or read-only store records nothing.
func (Store) Favorites ¶ added in v0.6.0
Favorites is the directories you marked, by path. A row of the wrong shape is left out. A disabled store, or one with none, reports none.
func (Store) ForgetOwner ¶ added in v0.5.0
ForgetOwner drops what was decided for a forge owner on a host in a Slack workspace, whatever the case of owner, so they are asked again there. What was decided in other workspaces is kept, and so then is the decision. A disabled or read-only store forgets nothing.
func (Store) Keeps ¶ added in v0.5.0
Keeps reports a store that keeps anything: neither disabled nor without a directory. A read-only store keeps what is already on disk, so it keeps.
func (Store) LastGroups ¶ added in v0.5.0
LastGroups is the group IDs of a workspace last chosen for a repository's announcement, by ID, and whether a choice was recorded at all, in any workspace — a choice of no group is still one. An ID of the wrong shape is left out. A disabled store reports no choice.
func (Store) LastScope ¶
LastScope is the commit scope last recorded for repo, and whether one was. A disabled store, or a repo with nothing recorded, reports no scope.
func (Store) LinkOwner ¶ added in v0.5.0
func (s Store) LinkOwner(ctx context.Context, forgeHost, workspace string, decision OwnerLink, now time.Time) error
LinkOwner records what was decided for a forge owner on a host in a Slack workspace, and whether they are a person or a team, as the caller tells: whom they are on Slack there, or that they are not on Slack there, replacing only that workspace's decision. It refuses an owner of the wrong shape for its kind with ErrInvalidOwner, a Slack ID of the wrong shape or kind — a user group for a team, a user for a person — with ErrInvalidSlackID, and a decision under no workspace with ErrNoWorkspace. The owner is kept in lower case, since both forges read user and team names without regard to case. A disabled or read-only store records nothing.
func (Store) OwnerLinks ¶ added in v0.5.0
OwnerLinks is every owner decided on a forge host as a Slack workspace sees them, by owner in lower case: linked there, or decided not on Slack there. An owner decided only in other workspaces is left out, so they read as never decided here and are asked again. A row of the wrong shape — a tampered or corrupt file's — is left out too, and each label is sanitized and capped. A disabled store, or a host with nothing decided, reports none.
func (Store) ReadOnly ¶ added in v0.4.0
ReadOnly is the store for a dry run: it reads what is already on disk, reads nothing where there is no store yet rather than create one, and no-ops every write.
func (Store) RecordAnnounce ¶
func (s Store) RecordAnnounce(ctx context.Context, repo string, announce Announce, now time.Time) error
RecordAnnounce remembers that a pull request was announced at a moment, so a later session knows not to offer announcing it again. A disabled or read-only store records nothing.
func (Store) RecordGroups ¶ added in v0.5.0
func (s Store) RecordGroups(ctx context.Context, repo, workspace string, ids []string, now time.Time) error
RecordGroups remembers the groups of a workspace just chosen for a repository's announcement, replacing that workspace's last choice in one transaction. Only the repository's own groups are remembered: an announcement also tags groups linked to owning teams, which need not be listed, and refusing the whole choice over one of those would lose the rest of it. A disabled or read-only store records nothing.
func (Store) RecordScope ¶
RecordScope remembers scope as the one last used in repo, replacing any earlier one. A disabled or read-only store records nothing.
func (Store) RepoGroups ¶ added in v0.5.0
RepoGroups is the Slack user groups of a workspace a repository may tag, by label. A row of the wrong shape is left out, and each label is sanitized and capped. A disabled store, or a repository with none, reports none.
func (Store) SetRepoGroups ¶ added in v0.5.0
func (s Store) SetRepoGroups(ctx context.Context, repo, workspace string, groups []SlackTarget, now time.Time) error
SetRepoGroups replaces the Slack user groups of a workspace a repository may tag, in one transaction, leaving other workspaces' groups as they are. It refuses any ID not a group's with ErrInvalidSlackID, and groups under no workspace with ErrNoWorkspace. A group dropped from the list drops out of the last choice too. A disabled or read-only store records nothing.