Documentation
¶
Overview ¶
Package outbox holds the ticket writes that have not reached the remote yet.
It exists because of a decision about how the tool should feel: claiming a ticket works offline. Being offline is rare enough that it must not dictate the interaction — so a claim is written locally straight away and the push is caught up when there is a network again. Something has to hold the write in between, and that is this.
It also settles a problem that would otherwise sit in the write path. Every ticket write funnels through core/ticket's Mutate and Create, and those are called from the TUI and from the merge driver as well as from the CLI. A synchronous push there would hang the board on the network and would have the merge driver pushing in the middle of a git merge. With an outbox, the write path only files the write; sending happens outside it.
The outbox lives under the per-working-tree state directory, not in the repository: an unsent write is this machine's business, and committing it would publish it in the very moment it is meant to be waiting.
Index ¶
- type Box
- func (b *Box) Drop(id string) error
- func (b *Box) DropEpic(name string) error
- func (b *Box) DropMilestone(key string) error
- func (b *Box) Flush(s Sender) ([]Result, error)
- func (b *Box) List() ([]Entry, error)
- func (b *Box) Pending(id string) (Entry, bool)
- func (b *Box) PendingEpic(name string) (Entry, bool)
- func (b *Box) PendingMilestone(key string) (Entry, bool)
- func (b *Box) Queue(id string, op Op, content []byte, lease, by string) error
- func (b *Box) QueueEpic(name string, content []byte, lease, by string) error
- func (b *Box) QueueMigration(key, file string, migrated []byte, from, by string) error
- func (b *Box) QueueMilestone(key, file string, op Op, content []byte, lease, by string) error
- func (b *Box) RekeyMilestone(oldKey, newKey, file string, content []byte, lease string) error
- type Entry
- type Kind
- type Migration
- type Op
- type Outcome
- type Result
- type Sender
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Box ¶
type Box struct{ Dir string }
Box is the queue for one working tree.
func (*Box) DropMilestone ¶ added in v0.5.0
DropMilestone removes a queued write of a milestone.
func (*Box) Flush ¶
Flush sends what it can and reports each entry.
It stops at the first unreachable remote instead of walking the rest of the queue. The network is a property of the machine, not of the ticket: once one push has proved there is no route, every following attempt would wait for the same timeout, and a command the user ran for another reason entirely would sit there for as long as the queue is.
A lost race is the opposite case and does not stop anything: it is about one ticket, and the next one may well go through.
func (*Box) List ¶
List returns every unsent write, oldest first. Ticket ids are ULIDs, so sorting by id is chronological by creation; QueuedAt orders by when the write was filed, which is what a flush should follow.
func (*Box) Pending ¶
Pending returns the unsent write for one ticket, if there is one. The board asks this to mark a card as carrying something not yet sent.
func (*Box) PendingEpic ¶ added in v0.5.1
PendingEpic returns the unsent write of one epic. It is what an epic is read from while it waits: with no file on disk, the queue holds the only copy of an edit made offline.
func (*Box) PendingMilestone ¶ added in v0.5.0
PendingMilestone returns the unsent write for one milestone key, if there is one, in either folder a milestone write is filed in.
func (*Box) Queue ¶
Queue files a write, superseding any earlier unsent write for the same ticket. Superseding is correct rather than lossy: the entry carries the whole ticket, so the newer bytes already contain everything the older ones said.
func (*Box) QueueEpic ¶ added in v0.5.1
QueueEpic files a pending write of an epic, under the same rules.
func (*Box) QueueMigration ¶ added in v0.5.0
QueueMigration files the first write of a milestone's ref when it moves an older file to its ID: the migrated content, and the old ref's tip (or "none"), which are sent as a commit of their own. Later edits replace the content and leave the migration as it is.
func (*Box) QueueMilestone ¶ added in v0.3.0
QueueMilestone files a pending write of a milestone file, under the same rules: whole content, the lease the remote last confirmed, superseding any earlier unsent write of the same milestone.
func (*Box) RekeyMilestone ¶ added in v0.5.0
RekeyMilestone replaces the write an older build queued under oldKey — a milestone's name — with one under newKey, its ID, naming the file to commit. The new entry keeps when and by whom the old one was queued. If newKey already has a write queued, that one stands: it is newer than anything an old build could have said, and the old entry is dropped.
type Entry ¶
type Entry struct {
ID string `json:"id"`
Op Op `json:"op"`
Ref string `json:"ref"`
// Kind says which namespace ID belongs to. Absent on an entry written
// before there were two, which means a ticket.
Kind Kind `json:"kind,omitempty"`
// Lease is the ref SHA the remote last confirmed to us, and it is the
// compare-and-swap value. It survives being superseded: a second local
// write while the first is still unsent replaces the content but keeps
// this, because the question the remote answers is "has anyone written
// since I last saw it", and our own unsent write is not an answer to that.
Lease string `json:"lease"`
// Content is the whole ticket file, not a patch. The file is the API, and
// a queued write that replayed a diff could apply cleanly onto a state
// nobody ever reviewed.
Content string `json:"content,omitempty"`
// File is the file name a milestone is committed under on its ref. Empty
// on a ticket, and on a milestone entry an older build queued, whose ref
// carried <key>.md.
File string `json:"file,omitempty"`
// Migration is set on a milestone's first write when it moves an older
// milestone file to its ID: what that file migrated to, and where it came
// from. It is sent as a commit of its own before Content, whatever edits
// replaced Content in the meantime.
Migration *Migration `json:"migration,omitempty"`
QueuedAt time.Time `json:"queued-at"`
UpdatedAt time.Time `json:"updated-at"`
By string `json:"by,omitempty"`
}
Entry is one ticket's pending write.
type Kind ¶ added in v0.3.0
type Kind string
Kind is which namespace a queued write belongs to: a ticket, keyed by its ulid, or a milestone, keyed by its name.
It is part of the path an entry is filed under, not only a field inside it. One flat directory keyed by the bare name would collide the moment somebody named a milestone after a ticket handle — and the collision would be silent, one unsent write overwriting the other.
const ( // KindTicket is a ticket's own ref. It is the zero value's meaning too, so // an entry queued by an older build — which had no kinds and no // subdirectory — still reads back as the ticket write it is. KindTicket Kind = "tickets" // KindMilestone is a milestone file's ref. KindMilestone Kind = "milestones" // KindEpic is an epic's ref. An epic has no file on disk, so this // queue is also where an unsent epic is read back from. KindEpic Kind = "epics" )
type Migration ¶ added in v0.5.0
type Migration struct {
File string `json:"file"`
Content string `json:"content"`
// From is the tip of the old name ref the file came from, "none" without one.
From string `json:"from"`
}
Migration is the move of an older milestone file to its ID, as the commit that records it holds it.
type Outcome ¶
type Outcome string
Outcome is what became of one queued write during a flush.
const ( // Sent means the remote accepted it and the entry is gone. Sent Outcome = "sent" // Rejected means someone else wrote the ticket first. The entry is // dropped: replaying it would only lose again, and the local file is still // there for the board to merge the ref into. Rejected Outcome = "rejected" // Unsent means the remote could not be reached. The entry stays. Unsent Outcome = "unsent" // Failed means git refused for some other reason. The entry stays, because // the cause is unknown and discarding a write on an unknown error is the // one outcome that loses work. Failed Outcome = "failed" )
type Result ¶
type Result struct {
ID string
Kind Kind
Op Op
Outcome Outcome
Err error
// File is the file a milestone entry was to be committed under, which is
// what carries the name a person knows it by; empty for a ticket.
File string
}
Result reports one entry's fate.
type Sender ¶
type Sender interface {
Write(id string, content []byte, lease string) (string, error)
Delete(id, lease string) error
WriteMilestone(key, file string, content []byte, lease string) (string, error)
WriteEpic(name string, content []byte, lease string) (string, error)
}
Sender is the transport a flush pushes through. It is an interface so the queue's behaviour — what it keeps, what it drops, when it stops — can be tested without a remote, and so nothing in here depends on a network being reachable.