Documentation
¶
Overview ¶
Package wpmigrate is the WordPress Migrator (spec §4.7): an honest Discovery Report, a 7-step wizard, a job-based import pipeline with resume/retry/dry-run, Gutenberg conversion, media download and URL rewrite, Yoast mapping, a WooCommerce catalog adapter and a WXR fallback.
Index ¶
- Constants
- Variables
- func IsAuthError(err error) bool
- type AuthSummary
- type CheckIssue
- type Client
- func (c *Client) All(ctx context.Context, path string, q url.Values, limitPages int, ...) (int, error)
- func (c *Client) Count(ctx context.Context, path string, q url.Values) (int, error)
- func (c *Client) Get(ctx context.Context, path string, q url.Values) (*Response, error)
- func (c *Client) GetJSON(ctx context.Context, path string, q url.Values, out any) (*Response, error)
- func (c *Client) GetURL(ctx context.Context, rawURL string) (*Response, error)
- func (c *Client) HasAuth() bool
- func (c *Client) ReadCollection(ctx context.Context, path string, q url.Values, limitPages int, ...) (CollectionRead, error)
- func (c *Client) SiteURL() string
- type CollectionRead
- type ContentStatePlan
- type CountRow
- type FieldMap
- type Finding
- type LinkIssue
- type LogEntry
- type Mapping
- type MigrationView
- type Module
- type Options
- type PathStatus
- type PreviewResult
- type ProductImporter
- type ProductMediaReplacer
- type Report
- type Response
- type Result
- type Service
- type SiteInfo
- type SiteSummary
- type SlugChange
- type SlugConflict
- type Source
- type SourceInput
- type SourceView
- type Status
- type StatusError
- type TypeMap
- type ValidateReport
- type ValidationIssue
- type WPContent
- type WPMedia
- type WPMenu
- type WPMenuItem
- type WPProduct
- type WPTerm
- type WPUser
- type WXRFile
Constants ¶
const ( StatusDraft = "draft" StatusScanned = "scanned" StatusMapped = "mapped" StatusPreviewed = "previewed" StatusRunning = "running" StatusPaused = "paused" StatusCompleted = "completed" StatusFailed = "failed" StatusValidated = "validated" )
Migration statuses.
const ( ItemPending = "pending" ItemDone = "done" ItemFailed = "failed" ItemSkipped = "skipped" ItemPreview = "preview" )
Item statuses.
const ( SlugSkip = "skip" SlugOverwrite = "overwrite" SlugSuffix = "suffix" )
Slug conflict policies (Options.SlugPolicy).
const ( TargetSkip = "skip" TargetCreate = "create" // create a content type with the WordPress slug TargetProduct = "commerce.product" // WooCommerce catalog adapter )
Type targets.
const Permission = "migrations.run"
Permission gates the wizard, the API and the MCP tools.
const ProductImporterService = "wpmigrate.product_importer"
ProductImporterService is the kernel service name an adapter registers a ProductImporter under (the commerce adapter, at integration).
const ServiceName = "wpmigrate"
ServiceName is the kernel service name of Service.
const ( // SettingRatePerSecond is the default outbound request rate per source // (REST reads and media downloads); a migration's Options.RatePerSecond // overrides it. SettingRatePerSecond = "wpmigrate.rate_per_second" )
Variables ¶
var ( ErrNotFound = errors.New("wpmigrate: not found") ErrAuthFailed = errors.New("wpmigrate: auth_failed") ErrBadState = errors.New("wpmigrate: invalid state for this action") ErrUnreachable = errors.New("wpmigrate: site_unreachable") ErrNotWordPress = errors.New("wpmigrate: not_wordpress") ErrInvalidSource = errors.New("wpmigrate: invalid source") )
Errors.
Functions ¶
func IsAuthError ¶
IsAuthError reports a credentials problem: any 401, or a 403 whose WordPress code is an authentication/permission code. Other 403s (a security plugin hiding an endpoint) are not auth failures.
Types ¶
type AuthSummary ¶
type AuthSummary struct {
Mode string `json:"mode"`
OK bool `json:"ok"`
Error string `json:"error,omitempty"`
}
AuthSummary says how the source was read. Mode is anonymous or app_password; Error is "auth_failed" when credentials were rejected.
type CheckIssue ¶
type CheckIssue struct {
ContentID string `json:"content_id"`
Kind string `json:"kind"`
Path string `json:"path"`
Outcome string `json:"outcome"`
Reason string `json:"reason"`
Code string `json:"code"`
OldRef string `json:"old_ref"`
NewRef string `json:"new_ref"`
CheckedAt string `json:"checked_at"`
}
CheckIssue records a check for which no HTTP response was received. Reason is a stable code; request errors and credentials are never stored.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client reads a WordPress site's REST API: Basic auth with an application password, paging via X-WP-TotalPages, retries with backoff on 429/5xx and network errors. The URL is user-entered and must be http(s).
func (*Client) All ¶
func (c *Client) All(ctx context.Context, path string, q url.Values, limitPages int, fn func(json.RawMessage) error) (int, error)
All walks every page of a collection, calling fn per element. limitPages <= 0 means no limit (bounded by maxPages).
func (*Client) Get ¶
Get fetches /wp-json<path>?q. Non-2xx responses return *StatusError together with the response.
func (*Client) GetJSON ¶
func (c *Client) GetJSON(ctx context.Context, path string, q url.Values, out any) (*Response, error)
GetJSON fetches path and decodes the body into out.
func (*Client) ReadCollection ¶
type CollectionRead ¶
type CollectionRead struct {
Visible int `json:"visible"`
ReportedTotal int `json:"reported_total"`
ReportedPages int `json:"reported_pages"`
PagesRead int `json:"pages_read"`
CountKnown bool `json:"count_known"`
PagesKnown bool `json:"pages_known"`
Limited bool `json:"limited"`
}
CollectionRead separates accessible rows from WordPress's pre-permission-filter bounds.
type ContentStatePlan ¶
type CountRow ¶
type CountRow struct {
Kind string `json:"kind"`
Source int `json:"source"`
Imported int `json:"imported"`
Skipped int `json:"skipped"`
Failed int `json:"failed"`
}
CountRow compares source and imported counts of one kind.
type Finding ¶
type Finding struct {
Kind string `json:"kind"`
Label map[string]string `json:"label"`
Count int `json:"count"`
Note map[string]string `json:"note,omitempty"`
Items []string `json:"items,omitempty"`
// Sampled marks counts taken from the first page (100 items) of pages
// and posts rather than the whole site.
Sampled bool `json:"sampled,omitempty"`
}
Finding is one line of a bucket. Items lists names when useful (page slugs with custom templates, shortcode tags).
type LinkIssue ¶
type LinkIssue struct {
ContentID string `json:"content_id"`
Title string `json:"title"`
URL string `json:"url"`
}
LinkIssue is a link or image that does not resolve locally.
type LogEntry ¶
type LogEntry struct {
ID string `json:"id"`
At string `json:"at"`
Level string `json:"level"`
ItemID string `json:"item_id,omitempty"`
Message string `json:"message"`
Meta map[string]any `json:"meta,omitempty"`
}
LogEntry is one migration_log row.
type Mapping ¶
type Mapping struct {
Types map[string]TypeMap `json:"types"` // wp type → target
Taxonomies map[string]string `json:"taxonomies"` // wp taxonomy → local taxonomy ("" skips)
Fields map[string]map[string]FieldMap `json:"fields"` // wp type → meta key → field
SkipSlugs []string `json:"skip_slugs"` // pages never migrated (plugin pages)
}
Mapping decides where WordPress data goes (wizard steps 3 and 4).
type MigrationView ¶
type MigrationView struct {
ID string `json:"id"`
SourceID string `json:"source_id"`
SourceName string `json:"source_name"`
SourceURL string `json:"source_url"`
SourceKind string `json:"source_kind"`
Status string `json:"status"`
DryRun bool `json:"dry_run"`
Step string `json:"step"`
Failed int `json:"failed"` // failed items
Options Options `json:"options"`
CreatedAt string `json:"created_at"`
StartedAt string `json:"started_at"`
FinishedAt string `json:"finished_at"`
}
MigrationView is a migration summary.
type Module ¶
type Module struct{}
Module is the wpmigrate kernel.Module.
type Options ¶
type Options struct {
SlugPolicy string `json:"slug_policy"` // skip | overwrite | suffix (default)
KeepClasses bool `json:"keep_classes"` // keep wp-block-* classes in converted HTML
SkipUsers bool `json:"skip_users"` // map every author to the migration's creator
Currency string `json:"currency"` // product currency when the source has none (WXR)
// RatePerSecond caps outbound requests to the source (REST reads and
// media downloads), 1–20; 0 = unset: setting wpmigrate.rate_per_second,
// else 4.
RatePerSecond float64 `json:"rate_per_second,omitempty"`
}
Options tune a migration.
type PathStatus ¶
type PathStatus struct {
ContentID string `json:"content_id"`
Path string `json:"path"`
Status int `json:"status"`
}
PathStatus is a local URL that did not render.
type PreviewResult ¶
type PreviewResult struct {
MigrationID string `json:"migration_id"`
Counts map[string]map[string]int `json:"counts"` // kind → would-action (create|update|reuse|skip|...) → n
SlugConflicts []SlugConflict `json:"slug_conflicts"`
Shortcodes map[string]int `json:"shortcodes"`
UnknownBlocks map[string]int `json:"unknown_blocks"`
MarkdownRepaired int `json:"markdown_repaired" doc:"Bodies whose raw Markdown would be converted to HTML."`
MediaCount int `json:"media_count"`
MediaBytes int64 `json:"media_bytes"`
Notes []string `json:"notes"` // i18n keys (e.g. wpmigrate.preview.needs_commerce)
ContentStateCounts map[string]map[string]int `` /* 218-byte string literal not displayed */
ContentStates []ContentStatePlan `json:"content_states,omitempty"`
// OptionsKept: a re-run reused this migration and kept its stored
// options; the options passed to this call were ignored.
OptionsKept bool `json:"options_kept,omitempty"`
}
PreviewResult summarizes a dry run.
func PreviewWXR ¶
func PreviewWXR(ctx context.Context, deps *kernel.Deps, f WXRFile, opts Options) (*PreviewResult, error)
PreviewWXR creates a WXR source and migration for f, scans it and runs the dry-run preview (no content is written). The source and migration stay listed in the wizard, where the import can be started later.
type ProductImporter ¶
type ProductImporter interface {
UpsertFromWP(ctx context.Context, p WPProduct) (localID string, err error)
}
ProductImporter creates or updates a catalog product from a WordPress product (implemented by the commerce module's adapter).
type ProductMediaReplacer ¶
type ProductMediaReplacer interface {
ReplaceMediaRef(ctx context.Context, oldID, newID string) error
}
ProductMediaReplacer is optionally implemented by the ProductImporter: when a re-imported WordPress file replaces a media item, the migrator asks it to move product images from oldID to newID before deleting the old media. Without it, the old media is kept whenever this source imported products.
type Report ¶
type Report struct {
Site SiteSummary `json:"site"`
Auth AuthSummary `json:"auth"`
Available []Finding `json:"available"`
Partial []Finding `json:"partial"`
ContentStateCounts map[string]map[string]int `` /* 204-byte string literal not displayed */
Counts map[string]int `json:"counts"`
Collections map[string]CollectionRead `json:"collections,omitempty"`
Plugins map[string]bool `json:"plugins"`
Shortcodes map[string]int `json:"shortcodes"`
Templates map[string]int `json:"templates"`
OffloadedMediaHost string `json:"offloaded_media_host"`
SourceKind string `json:"source_kind"` // rest | wxr
MetaKeys map[string][]string `json:"meta_keys"` // wp type → meta keys seen in samples
GeneratedAt string `json:"generated_at"`
}
Report is the Discovery Report (spec §4.7 step 2): what the source exposes, sorted into three buckets.
type Result ¶
type Result struct {
MigrationID string `json:"migration_id"`
SourceID string `json:"source_id"`
Status string `json:"status"`
Counts map[string]map[string]int `json:"counts"`
Report *ValidateReport `json:"report,omitempty"`
OptionsKept bool `json:"options_kept,omitempty"` // see PreviewResult.OptionsKept
}
Result is the outcome of ImportWXRFile.
func ImportWXR ¶
ImportWXR imports f synchronously: it creates a WXR source and a migration, scans, runs the whole pipeline and validates.
func ImportWXRFile ¶
func ImportWXRFile(ctx context.Context, deps *kernel.Deps, path string, opts Options) (*Result, error)
ImportWXRFile imports a WXR export synchronously (the `gorun import-wxr` command): it creates a WXR source and a migration, scans, runs the whole pipeline and validates. deps must come from a booted kernel with the content, taxonomy, media, menus and redirects modules.
type Service ¶
type Service interface {
Discover(ctx context.Context, sourceID string) (*Report, error)
Preview(ctx context.Context, migrationID string) (*PreviewResult, error)
Start(ctx context.Context, migrationID string) error
Pause(ctx context.Context, migrationID string) error
Resume(ctx context.Context, migrationID string) error
RetryFailed(ctx context.Context, migrationID string) error
Status(ctx context.Context, migrationID string) (*Status, error)
Log(ctx context.Context, migrationID string, limit int) ([]LogEntry, error)
}
Service is the migrator API used by the admin wizard, REST and MCP.
type SiteInfo ¶
type SiteInfo struct {
Name string `json:"name"`
URL string `json:"url"`
Home string `json:"home"`
Description string `json:"description"`
Language string `json:"language"`
Version string `json:"version"`
FrontPageID int `json:"front_page_id"`
// FromSettings is true when title/tagline came from /wp/v2/settings
// (authenticated) rather than the public index.
FromSettings bool `json:"from_settings"`
PostsPageID int `json:"posts_page_id"`
}
SiteInfo is the source site's identity.
type SiteSummary ¶
type SiteSummary struct {
Name string `json:"name"`
URL string `json:"url"`
Version string `json:"version"`
Locale string `json:"locale"`
}
SiteSummary identifies the source site.
type SlugChange ¶
type SlugChange struct {
Type string `json:"type"`
From string `json:"from"`
To string `json:"to"`
Path string `json:"path"`
}
SlugChange is a WordPress slug that changed on import.
type SlugConflict ¶
type SlugConflict struct {
Type string `json:"type"`
WPID string `json:"wp_id"`
Slug string `json:"slug"`
FinalSlug string `json:"final_slug"`
Policy string `json:"policy"`
Reason string `json:"reason"` // existing | duplicate_in_source
}
SlugConflict is a WordPress slug that cannot be kept as is.
type Source ¶
type Source interface {
Kind() string
Site(ctx context.Context) (SiteInfo, error)
Users(ctx context.Context) ([]WPUser, error)
Terms(ctx context.Context, taxonomies []string) ([]WPTerm, error)
Media(ctx context.Context) ([]WPMedia, error)
Content(ctx context.Context, types []string) ([]WPContent, error)
Products(ctx context.Context) ([]WPProduct, error)
Menus(ctx context.Context) ([]WPMenu, error)
}
Source reads a WordPress site; restSource (REST API) and wxrSource (WXR export) implement it so one pipeline serves both. Methods return ErrUnavailable (wrapped) when the source cannot provide the data.
type SourceInput ¶
type SourceInput struct {
Name string `json:"name"`
URL string `json:"url"`
Username string `json:"username"`
AppPassword string `json:"app_password"`
}
SourceInput creates a REST source. AppPassword is encrypted at rest and never returned.
type SourceView ¶
type SourceView struct {
ID string `json:"id"`
Name string `json:"name"`
Kind string `json:"kind"`
URL string `json:"url"`
Username string `json:"username"`
HasPassword bool `json:"has_password"`
CreatedAt string `json:"created_at"`
LastDiscoveryAt string `json:"last_discovery_at"`
}
SourceView is a source as shown to users (no secret).
type Status ¶
type Status struct {
ID string `json:"id"`
SourceID string `json:"source_id"`
Status string `json:"status"`
DryRun bool `json:"dry_run"`
Step string `json:"step"`
Phase int `json:"phase"`
Counts map[string]map[string]int `json:"counts"` // kind → item status → n
Total int `json:"total"`
Done int `json:"done"`
Failed int `json:"failed"`
Skipped int `json:"skipped"`
Pending int `json:"pending"`
Percent int `json:"percent"`
StartedAt string `json:"started_at"`
FinishedAt string `json:"finished_at"`
CreatedAt string `json:"created_at"`
}
Status is a migration's progress.
type StatusError ¶
StatusError is a non-2xx REST response; Code is WordPress's error code (rest_forbidden_context, incorrect_password, …) when present.
func (*StatusError) Error ¶
func (e *StatusError) Error() string
type TypeMap ¶
type TypeMap struct {
Target string `json:"target"` // post | page | <existing type> | create | skip | commerce.product
Name string `json:"name"` // WordPress label (for created types)
}
TypeMap maps one WordPress post type.
type ValidateReport ¶
type ValidateReport struct {
MigrationID string `json:"migration_id"`
GeneratedAt string `json:"generated_at"`
Errors []ValidationIssue `json:"errors"`
Warnings []ValidationIssue `json:"warnings"`
Readiness string `json:"readiness"`
Scope []string `json:"scope"`
Limitations []string `json:"limitations"`
MediaChecked int `json:"media_checked"`
Counts []CountRow `json:"counts"`
RenderChecked int `json:"render_checked"`
RenderFailures []PathStatus `json:"render_failures"`
UnverifiedChecks []CheckIssue `json:"unverified_checks"`
BrokenLinks []LinkIssue `json:"broken_links"`
MissingMedia []LinkIssue `json:"missing_media"`
Shortcodes map[string]int `json:"shortcodes"`
UnknownBlocks map[string]int `json:"unknown_blocks"`
MarkdownRepaired int `json:"markdown_repaired" doc:"Imported bodies whose raw Markdown (lists, pipe tables, inline code) was converted to HTML."`
SlugChanges []SlugChange `json:"slug_changes"`
Markdown string `json:"markdown"`
}
ValidateReport is step 7: counts per kind, render checks, broken links, missing media, unresolved shortcodes and slug changes.
type ValidationIssue ¶
type ValidationIssue struct {
ContentID string `json:"content_id"`
Kind string `json:"kind"`
Code string `json:"code"`
OldRef string `json:"old_ref"`
NewRef string `json:"new_ref"`
CheckedAt string `json:"checked_at"`
Status int `json:"status,omitempty"`
}
ValidationIssue contains safe references and stable failure codes, never raw errors.
type WPContent ¶
type WPContent struct {
ID int `json:"id"`
Type string `json:"type"`
Slug string `json:"slug"`
Title string `json:"title"`
Link string `json:"link"`
Status string `json:"status"`
Date string `json:"date"` // GMT, RFC3339 or "2006-01-02 15:04:05"
Modified string `json:"modified"`
Raw string `json:"raw"`
Rendered string `json:"rendered"`
Excerpt string `json:"excerpt"`
Template string `json:"template"`
Parent int `json:"parent"`
MenuOrder int `json:"menu_order"`
Author int `json:"author"`
FeaturedMedia int `json:"featured_media"`
Terms map[string][]int `json:"terms"` // WP taxonomy → term ids
Meta map[string]any `json:"meta"`
Yoast map[string]any `json:"yoast"`
ACF map[string]any `json:"acf"`
IsFront bool `json:"is_front"`
}
WPContent is a post of any public type.
type WPMedia ¶
type WPMedia struct {
ID int `json:"id"`
URL string `json:"url"`
Slug string `json:"slug"`
Mime string `json:"mime"`
Alt string `json:"alt"`
Caption string `json:"caption"`
Title string `json:"title"`
Modified string `json:"modified"`
Bytes int64 `json:"bytes"`
Sizes []string `json:"sizes"`
}
WPMedia is an attachment.
type WPMenu ¶
type WPMenu struct {
ID int `json:"id"`
Slug string `json:"slug"`
Name string `json:"name"`
Location string `json:"location"`
Items []WPMenuItem `json:"items"`
}
WPMenu is a navigation menu with its items.
type WPMenuItem ¶
type WPMenuItem struct {
ID int `json:"id"`
Parent int `json:"parent"`
Order int `json:"order"`
Title string `json:"title"`
URL string `json:"url"`
Type string `json:"type"`
Object string `json:"object"`
ObjectID int `json:"object_id"`
}
WPMenuItem is a menu entry; Object/ObjectID reference a post or term when Type is post_type or taxonomy.
type WPProduct ¶
type WPProduct struct {
ID int
Slug, Name, Description, ShortDescription string
PriceMinor int64
Currency string
Categories []string
ImageURLs []string
Meta map[string]string
}
WPProduct is a WooCommerce catalog product as read by the migrator. It is identical to commerce.WPProduct (the adapter maps it 1:1). Categories are category names; Meta holds meta_data (e.g. _mcbb_server, _mcbb_plan, _mcbb_months) when the source exposes it (wc/v3 with auth, or WXR).
type WPTerm ¶
type WPTerm struct {
ID int `json:"id"`
Taxonomy string `json:"taxonomy"`
Slug string `json:"slug"`
Name string `json:"name"`
Description string `json:"description"`
Parent int `json:"parent"`
Count int `json:"count"`
}
WPTerm is a taxonomy term.
type WPUser ¶
type WPUser struct {
ID int `json:"id"`
Login string `json:"login"`
Slug string `json:"slug"`
Name string `json:"name"`
Email string `json:"email"`
}
WPUser is an author.
type WXRFile ¶
type WXRFile struct {
Path string
SourceName string // "" → "WXR <file name>"
// ResetOptions replaces a reused migration's stored options with the
// ones passed in; otherwise a re-run keeps them (OptionsKept reports
// when the passed options were ignored).
ResetOptions bool
// Render, when set, answers the validate step's render checks
// in-process (the CLI passes the booted app's handler, so no server has
// to run); otherwise they go over HTTP to the configured base URL.
Render http.Handler
}
WXRFile describes a WXR export imported outside the wizard (the CLI).
Source Files
¶
- adapter_commerce.go
- admin_progress.go
- admin_wizard.go
- api.go
- client.go
- collection_scope.go
- contract.go
- discovery.go
- dryrun.go
- generate.go
- mapping.go
- markdown_repair.go
- module.go
- pipeline.go
- ratelimit.go
- report.go
- rewrite_images.go
- service.go
- source.go
- source_rest.go
- source_wxr.go
- source_wxr_images.go
- steps_content.go
- steps_media.go
- steps_menus.go
- steps_redirects.go
- steps_rewrite.go
- steps_terms.go
- steps_users.go
- store.go
- validate.go
- validation_media.go
- validation_report_privacy.go
- watchdog.go
- wc_adapter.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package gutenberg parses WordPress block markup (<!-- wp:ns/name {json} --> … <!-- /wp:ns/name -->) into a tree and converts it to clean HTML for GoRun (spec §4.7).
|
Package gutenberg parses WordPress block markup (<!-- wp:ns/name {json} --> … <!-- /wp:ns/name -->) into a tree and converts it to clean HTML for GoRun (spec §4.7). |
|
Package wxr parses WordPress eXtended RSS exports (Tools → Export), the fallback source when the REST API hides data (postmeta such as the MCBB product mapping, drafts, menus).
|
Package wxr parses WordPress eXtended RSS exports (Tools → Export), the fallback source when the REST API hides data (postmeta such as the MCBB product mapping, drafts, menus). |