migrate

package
v0.9.3 Latest Latest
Warning

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

Go to latest
Published: Jul 30, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package migrate upgrades a .spectackle workspace from an older schema stamp to the current one, in place, when the workspace is opened.

Why this is not the throwaway path T-0094 rejected

T-0094 was rejected on the grounds that a one-off rewrite of one repository's records does not justify a permanent tool, and T-0137 was rejected in its favor. This is a different animal. An ID-scheme change (ADR-0013) reaches every workspace every user of a released tool already carries, and the schema stamp's documented answer to a mismatch is a hard error saying there is no migration (see workspace.SchemaStamp, spec.Load). Shipping the new scheme without a migration would turn every existing workspace into an unreadable one, and the honest advice to users would be to regenerate and lose their history.

Why it does not depend on the packages it rewrites

The migration reads and writes raw bytes: work.md as text, journal.ndjson as generic JSON objects, spec.md and config.yaml as text with a front-matter stamp. It deliberately does not use internal/item, internal/journal or internal/spec, for two reasons that both matter.

The first is ordering. Those packages refuse a mismatched stamp — that is the very behavior being fixed — so a migration built on them could not read its own input. Nothing above them can help either: workspace.Detect fails on the old config stamp, so the migration has to run before a Root exists, on nothing but a directory path.

The second is fidelity. Parsing a journal event into the Go struct and re-marshalling it would silently drop any field the struct does not know and normalize the ones it does. A migration must be a rewrite of the fields it understands and a byte-faithful passthrough of everything else, so it decodes into an ordered generic form instead.

Properties

Idempotent: a workspace already on the current stamp is untouched and reports nothing. Deterministic: new IDs are derived from each record's own creation timestamp in the journal plus its legacy ID as a seed (see ids.MintRecordIDFrom), so two clones migrating the same committed records converge instead of diverging and colliding on the next merge, and the archive keeps its chronology instead of collapsing into the migration moment. Atomic and recoverable: every new file content is computed in memory before anything is written, the originals are copied into a retained backup directory first, and a crash mid-write is detected and rolled back on the next open (see Recover).

Index

Constants

View Source
const (
	From = "v0"
	To   = "v1"
)

From is the stamp this migration upgrades, To the stamp it produces. A bundle on any other stamp is refused exactly as before — there is one migration, not a chain, and pretending otherwise would silently mangle a workspace from a future version.

View Source
const Dot = ".spectackle"

Dot mirrors workspace.Dot. It is duplicated rather than imported to keep this package free of the dependency that would make the import cycle (see the package comment: internal/item imports internal/workspace, and this package must not import either).

Variables

View Source
var ErrUnknownStamp = errors.New("migrate: unknown schema stamp")

ErrUnknownStamp reports a bundle stamped with something this migration does not understand — a workspace written by a newer version, most likely.

Functions

func Needed

func Needed(dir string) (bool, error)

Needed reports whether the workspace at dir carries the old stamp, by probing only the root bundle. A nested bundle left behind at the old stamp can only happen if a write was interrupted, and that case is caught by Recover rather than by this probe.

Types

type Report

type Report struct {
	Migrated bool              // false = already current, nothing written
	Remap    map[string]string // legacy ID -> new ID, every record found
	Files    []string          // workspace-relative paths rewritten, sorted
	Events   int               // journal events whose content changed
	Backup   string            // workspace-relative path of the retained backup
}

Report describes what a Run did, or would have done.

func Recover

func Recover(dir string) (Report, error)

Recover rolls back an interrupted migration: a retained backup with no completion marker means the process died between the first and last write, so the workspace is a mix of old and new files. Restoring the backup returns it to a state that loads, which Run then migrates again from scratch.

Reported as Migrated when it actually restored something.

func Run

func Run(dir string) (Report, error)

Run migrates the workspace rooted at dir, if it needs it.

It is safe and cheap to call on every open: the common case is a stamp scan over the bundle files and an immediate return with Migrated false.

Jump to

Keyboard shortcuts

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