migrate

package
v0.19.2 Latest Latest
Warning

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

Go to latest
Published: Sep 1, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

Documentation

Overview

Package migrate exports a mirror into an issuetap fixture document — the seed a fresh standalone workspace loads one-shot (origin/issuetap.yaml, GDK-1264). Reads are mirror-only; the single origin round-trip is the attachment byte download, and the caller owns that client.

The document shapes mirror issuetap's internal/fixtures.Doc YAML contract (that package is internal, so the structs are declared here). Three rules came out of the GDK-1262 spike and must not regress:

  1. priorities emit in id order — issuetap assigns priority_rank by catalog position, so encounter order flips the ranking.
  2. links emit each issue's own mirror rows verbatim, both directions — the fixture load is single-sided (runtime AddIssueLink is what materializes both projections), so a pair exists on both ends only if both ends declare it.
  3. the status catalog includes every status id the changelog references, not just the ones issues currently sit in.

Index

Constants

View Source
const MaxAttachmentBytes = 16 << 20

MaxAttachmentBytes caps one inlined file. The fixture is a YAML document read into memory; a file past this stays metadata-only and is reported.

Variables

This section is empty.

Functions

func Build

func Build(ctx context.Context, db *sql.DB, opt Options) (*Doc, *Stats, error)

Build reads the mirror and assembles the fixture document. db must be a mirror connection (read-only is fine); nothing is written.

func InlineAttachments

func InlineAttachments(ctx context.Context, doc *Doc, fetch Fetch, st *Stats)

InlineAttachments downloads each attachment's bytes and inlines them into the document — printable text/* as Text, anything else as std base64. A missing file (404) or an oversized one keeps its metadata row and is counted, never fatal: a partial archive that says what is missing beats no archive.

Types

type Attachment

type Attachment struct {
	Filename   string `json:"filename"`
	MimeType   string `json:"mimeType,omitempty"`
	Text       string `json:"text,omitempty"`
	DataBase64 string `json:"dataBase64,omitempty"`
	Author     string `json:"author,omitempty"`
	Created    string `json:"created,omitempty"`
	// ContentID is the origin content id for the byte download
	// (external_id when set, else the store row id — the same rule as
	// store.AttachmentOrigin). Never emitted.
	ContentID string `json:"-"`
	// Size is the mirror's byte count, used to skip oversized files
	// before spending the download. Never emitted.
	Size int64 `json:"-"`
	// SourceURL is the mirror's stored origin content URL (non-Jira
	// sources). Non-empty means the bytes do not live behind Jira's
	// /attachment/content/{id} and this pass skips them. Never emitted.
	SourceURL string `json:"-"`
}

type Comment

type Comment struct {
	Author  string `json:"author,omitempty"`
	Body    string `json:"body"`
	Created string `json:"created,omitempty"`
}

type Doc

type Doc struct {
	Users      []User      `json:"users,omitempty"`
	Projects   []Project   `json:"projects,omitempty"`
	Statuses   []Status    `json:"statuses,omitempty"`
	Priorities []Priority  `json:"priorities,omitempty"`
	IssueTypes []IssueType `json:"issueTypes,omitempty"`
	Issues     []Issue     `json:"issues,omitempty"`
	Spaces     []Space     `json:"spaces,omitempty"`
	Pages      []Page      `json:"pages,omitempty"`
}

Doc is the fixture document. Key names follow issuetap's internal/fixtures.Doc (its json and yaml tags agree); only the parts a mirror can fill are declared. Emitted as JSON — see cmdMigrate for why the seed is never yaml.Marshal'd.

type Fetch

type Fetch func(ctx context.Context, contentID string) (status int, body []byte, err error)

Fetch downloads one attachment's bytes by content id. Implemented by the caller over the source origin client (Jira's and issuetap's /attachment/content/{id} are the same shape). A 404 returns status 404 with err nil.

type History

type History struct {
	At     string        `json:"at"`
	Author string        `json:"author,omitempty"`
	Items  []HistoryItem `json:"items"`
}

type HistoryItem

type HistoryItem struct {
	Field      string `json:"field"`
	From       string `json:"from,omitempty"`
	FromString string `json:"fromString,omitempty"`
	To         string `json:"to,omitempty"`
	ToString   string `json:"toString,omitempty"`
}

type Issue

type Issue struct {
	Key         string       `json:"key"`
	Summary     string       `json:"summary"`
	Description string       `json:"description,omitempty"`
	Project     string       `json:"project,omitempty"`
	Type        string       `json:"type,omitempty"`   // id
	Status      string       `json:"status,omitempty"` // id
	Priority    string       `json:"priority,omitempty"`
	Assignee    string       `json:"assignee,omitempty"`
	Reporter    string       `json:"reporter,omitempty"`
	Parent      string       `json:"parent,omitempty"`
	Labels      []string     `json:"labels,omitempty"`
	Components  []string     `json:"components,omitempty"`
	FixVersions []string     `json:"fixVersions,omitempty"`
	Duedate     string       `json:"duedate,omitempty"`
	Resolution  string       `json:"resolution,omitempty"`
	Created     string       `json:"created,omitempty"`
	Updated     string       `json:"updated,omitempty"`
	Comments    []Comment    `json:"comments,omitempty"`
	Attachments []Attachment `json:"attachments,omitempty"`
	Links       []Link       `json:"links,omitempty"`
	History     []History    `json:"history,omitempty"`
}

type IssueType

type IssueType struct {
	ID             string `json:"id"`
	Name           string `json:"name"`
	HierarchyLevel int    `json:"hierarchyLevel,omitempty"`
	Subtask        bool   `json:"subtask,omitempty"`
}
type Link struct {
	Type    string `json:"type"`
	Inward  string `json:"inward,omitempty"`
	Outward string `json:"outward,omitempty"`
}

type Options

type Options struct {
	Projects []string // issue project keys
	Spaces   []string // wiki space keys
}

Options selects what leaves the mirror. Empty means everything mirrored.

type Page

type Page struct {
	ID       string        `json:"id,omitempty"`
	Title    string        `json:"title"`
	Space    string        `json:"space"`
	Version  int           `json:"version,omitempty"`
	When     string        `json:"when,omitempty"`
	Author   string        `json:"author,omitempty"`
	Body     string        `json:"body,omitempty"`
	Labels   []string      `json:"labels,omitempty"`
	Parent   string        `json:"parent,omitempty"`
	Comments []PageComment `json:"comments,omitempty"`
}

type PageComment

type PageComment struct {
	Author string `json:"author,omitempty"`
	Body   string `json:"body"`
	When   string `json:"when,omitempty"`
}

type Priority

type Priority struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

type Project

type Project struct {
	Key  string `json:"key"`
	Name string `json:"name"`
}

type Space

type Space struct {
	Key  string `json:"key"`
	Name string `json:"name"`
}

type Stats

type Stats struct {
	Projects []string
	Spaces   []string

	Issues       int
	Comments     int
	Attachments  int
	Links        int
	History      int
	Pages        int
	PageComments int
	Users        int

	// Derived columns the target must reproduce from the migrated
	// changelog (they are never stored in the fixture).
	ReopenSum int
	EpicKeys  int

	// Formatting nodes flattened to plain text by the fixture path
	// (descriptions, comments and page bodies all load as adf.Doc(text)).
	LossCodeBlock int
	LossMedia     int
	LossTable     int

	// Dropped because the other end is outside the migrated set.
	DroppedLinks       int
	DroppedParents     []string
	DroppedPageParents int

	// Not migrated (the fixture has no slot, or out of scope).
	DevLinks     int
	CustomIssues int
	SprintIssues int

	// Attachment byte pass.
	AttachInlined  int
	AttachMissing  int // origin answered 404 — metadata kept
	AttachTooLarge int
	AttachSkipURL  int // stored origin URL (non-Jira source) — out of scope
	AttachErrors   []string

	MissingUsers    []string // referenced account ids absent from the users catalog
	UnnamedStatuses []string // history-only status ids with no display name
}

Stats is the honest half of the export: what was counted, what was flattened, what was dropped and why. The verification report is built from it — silent loss and reported loss are different products.

type Status

type Status struct {
	ID       string `json:"id"`
	Name     string `json:"name"`
	Category string `json:"category"` // new | indeterminate | done
}

type User

type User struct {
	AccountID   string `json:"accountId,omitempty"`
	DisplayName string `json:"displayName"`
	Email       string `json:"email,omitempty"`
	AccountType string `json:"accountType,omitempty"`
}

type VerifyRow

type VerifyRow struct {
	Metric   string `json:"metric"`
	Source   int    `json:"source"`
	Migrated int    `json:"migrated"`
}

VerifyRow is one line of the migration report: a metric counted on the source mirror at export time against the same metric on the freshly filled target mirror.

func VerifyMirror

func VerifyMirror(ctx context.Context, db *sql.DB, st *Stats) ([]VerifyRow, error)

VerifyMirror re-counts the exported axes on the target mirror. The derived rows (reopens, epic keys) matter most: they are never stored in the fixture, so equality proves the migrated changelog reproduces them.

Jump to

Keyboard shortcuts

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