From 5566eed1c8e0f2eec77f383dccae5f675c1f1009 Mon Sep 17 00:00:00 2001 From: John Coffey Date: Sat, 22 Aug 2026 18:23:48 -0700 Subject: [PATCH] Fix module path and restore cmd/ omitted by gitignore Rename module github.com/johnellis/stalwart-migrator -> github.com/LINUXexpert-org/stalwart-migrator to match the repository location, so the module resolves under `go get`. The initial commit's .gitignore listed the compiled binary as a bare `stalwart-migrate` pattern, which Git matches at any depth -- so it also excluded the cmd/stalwart-migrate/ source directory, and the initial commit shipped without the CLI entrypoint. Anchor the pattern to the repo root as /stalwart-migrate and add the four missing files. go build, go vet, and go test ./... all pass. --- .gitignore | 2 +- cmd/stalwart-migrate/main.go | 53 +++++ cmd/stalwart-migrate/preflight.go | 59 +++++ cmd/stalwart-migrate/run.go | 247 ++++++++++++++++++++ cmd/stalwart-migrate/status.go | 62 +++++ go.mod | 2 +- internal/backup/backup.go | 2 +- internal/backup/backup_test.go | 2 +- internal/preflight/checks.go | 4 +- internal/preflight/checks_test.go | 2 +- internal/recovery/recovery.go | 2 +- internal/recovery/recovery_test.go | 2 +- internal/validate/bootcheck.go | 6 +- internal/validate/bootcheck_test.go | 2 +- internal/validate/content_integrity.go | 4 +- internal/validate/content_integrity_test.go | 4 +- 16 files changed, 438 insertions(+), 17 deletions(-) create mode 100644 cmd/stalwart-migrate/main.go create mode 100644 cmd/stalwart-migrate/preflight.go create mode 100644 cmd/stalwart-migrate/run.go create mode 100644 cmd/stalwart-migrate/status.go diff --git a/.gitignore b/.gitignore index b1dfadd..be17731 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ # Binaries /bin/ /dist/ -stalwart-migrate +/stalwart-migrate *.exe # Test / coverage diff --git a/cmd/stalwart-migrate/main.go b/cmd/stalwart-migrate/main.go new file mode 100644 index 0000000..4d69fe8 --- /dev/null +++ b/cmd/stalwart-migrate/main.go @@ -0,0 +1,53 @@ +// Command stalwart-migrate drives an in-place Stalwart Mail Server upgrade +// (0.15.5 -> latest) through preflight checks, a defense-in-depth backup, +// a checkpointed migration, and post-migration validation, with rollback +// available at every step. See ARCHITECTURE.md for the full design. +package main + +import ( + "fmt" + "os" +) + +func main() { + if len(os.Args) < 2 { + usage() + os.Exit(1) + } + + var err error + switch os.Args[1] { + case "preflight": + err = runPreflight(os.Args[2:]) + case "run": + err = runRun(os.Args[2:]) + case "status": + err = runStatus(os.Args[2:]) + case "rollback": + err = fmt.Errorf("not implemented yet: see internal/rollback") + case "confirm": + err = fmt.Errorf("not implemented yet: see internal/checkpoint") + case "report": + err = fmt.Errorf("not implemented yet: see internal/validate") + default: + usage() + os.Exit(1) + } + + if err != nil { + fmt.Fprintln(os.Stderr, "stalwart-migrate:", err) + os.Exit(1) + } +} + +func usage() { + fmt.Fprintln(os.Stderr, `usage: stalwart-migrate [flags] + +commands: + preflight run read-only checks and print the migration plan + run --dry-run: simulate and validate against a sandbox (real cutover isn't implemented yet) + status show the state of an in-progress or completed run + rollback restore the pre-migration backup for a given run + confirm close the rollback window for a completed run + report print the validation report for a run`) +} diff --git a/cmd/stalwart-migrate/preflight.go b/cmd/stalwart-migrate/preflight.go new file mode 100644 index 0000000..3ba9fb3 --- /dev/null +++ b/cmd/stalwart-migrate/preflight.go @@ -0,0 +1,59 @@ +package main + +import ( + "context" + "flag" + "fmt" + "os" + + "github.com/LINUXexpert-org/stalwart-migrator/internal/checkpoint" + "github.com/LINUXexpert-org/stalwart-migrator/internal/preflight" +) + +func runPreflight(args []string) error { + fs := flag.NewFlagSet("preflight", flag.ExitOnError) + binaryPath := fs.String("binary", "/usr/local/bin/stalwart", "path to the installed stalwart binary") + configPath := fs.String("config", "/etc/stalwart/config.toml", "path to stalwart's config file") + dataDir := fs.String("data-dir", "/var/lib/stalwart", "stalwart data directory") + containerName := fs.String("container", "stalwart", "docker container name, if applicable") + adminURL := fs.String("admin-url", "", "base URL for a JMAP reachability check (optional but recommended)") + adminUser := fs.String("admin-user", "", "admin username for the reachability check") + adminPassword := fs.String("admin-password", os.Getenv("STALWART_MIGRATE_ADMIN_PASSWORD"), + "admin password for the reachability check (or set STALWART_MIGRATE_ADMIN_PASSWORD)") + targetVersion := fs.String("target", "latest", `target Stalwart version, or "latest"`) + minFree := fs.Float64("min-free-multiple", 2.0, "required free disk space as a multiple of the data directory size") + stateDir := fs.String("state-dir", checkpoint.DefaultBaseDir, "directory to store run checkpoints in") + if err := fs.Parse(args); err != nil { + return err + } + + store := checkpoint.NewStore(*stateDir) + rs, err := store.Create("", *targetVersion) + if err != nil { + return fmt.Errorf("create run: %w", err) + } + + checker := preflight.New(preflight.Options{ + BinaryPath: *binaryPath, + ConfigPath: *configPath, + DataDir: *dataDir, + ContainerName: *containerName, + AdminURL: *adminURL, + AdminUser: *adminUser, + AdminPassword: *adminPassword, + TargetVersion: *targetVersion, + MinFreeMultiple: *minFree, + }) + + report, err := checker.Run(context.Background(), store, rs) + fmt.Print(report.String()) + fmt.Printf("\nrun id: %s\n", rs.RunID) + if err != nil { + return fmt.Errorf("preflight run %s failed to complete: %w", rs.RunID, err) + } + if report.Blocking() { + return fmt.Errorf("preflight found blocking issues for run %s - see FAIL lines above", rs.RunID) + } + fmt.Println("preflight passed - safe to proceed with `stalwart-migrate run`") + return nil +} diff --git a/cmd/stalwart-migrate/run.go b/cmd/stalwart-migrate/run.go new file mode 100644 index 0000000..7b6e750 --- /dev/null +++ b/cmd/stalwart-migrate/run.go @@ -0,0 +1,247 @@ +package main + +import ( + "context" + "flag" + "fmt" + "net/http" + "os" + "path/filepath" + + "github.com/LINUXexpert-org/stalwart-migrator/internal/backup" + "github.com/LINUXexpert-org/stalwart-migrator/internal/checkpoint" + "github.com/LINUXexpert-org/stalwart-migrator/internal/plan" + "github.com/LINUXexpert-org/stalwart-migrator/internal/preflight" + "github.com/LINUXexpert-org/stalwart-migrator/internal/recovery" + "github.com/LINUXexpert-org/stalwart-migrator/internal/validate" +) + +// runRun implements `stalwart-migrate run`. Only --dry-run is available +// today: a real cutover needs internal/rollback (so a failed migration can +// actually be undone) and real systemd/Docker service control, neither of +// which exist yet - see ARCHITECTURE.md ยง8. Committing to a real migration +// without a working rollback would violate the one thing this whole tool +// exists to guarantee, so `run` without --dry-run refuses rather than doing +// it partway. +// +// --dry-run runs preflight and a real backup (see the caveat printed below +// about why backup still touches the live data directory), then - if the +// plan crosses the 0.15/0.16 boundary - clones the verified backup into a +// disposable sandbox, converts the settings snapshot to point at that +// sandbox (via migrate_v016.py's own documented --patch-paths mechanism, +// not by this tool guessing at config.json's schema), runs the real +// recovery-mode migration against the sandbox, and boots the result +// normally to confirm it comes up. Nothing at the real binary path or the +// real service is ever touched. +// +// Every byte a dry run writes - the fs-backup copy, the settings/principals +// dumps, the downloaded migrate_v016.py, the sandbox clone and its +// config/export files - lives under one per-run directory +// (work-dir/) that a deferred cleanup at the bottom of this +// function removes on every exit path: success, a failed check partway +// through, or an early refusal. The only thing left behind afterward is the +// checkpoint's state.json under --state-dir, which is exactly the +// success/failure log a rerun's `status ` reads - not bulk data. +// --keep-artifacts opts out, for when a failure needs inspecting. +func runRun(args []string) (err error) { + fs := flag.NewFlagSet("run", flag.ExitOnError) + binaryPath := fs.String("binary", "/usr/local/bin/stalwart", "path to the currently-installed stalwart binary") + targetBinaryPath := fs.String("target-binary", "", "path to an already-downloaded target-version stalwart binary (required to simulate a major-boundary migration)") + configPath := fs.String("config", "/etc/stalwart/config.toml", "path to stalwart's current config file") + dataDir := fs.String("data-dir", "/var/lib/stalwart", "stalwart data directory") + containerName := fs.String("container", "stalwart", "docker container name, if applicable") + adminURL := fs.String("admin-url", "", "base URL for the live instance's admin/JMAP API (required)") + adminUser := fs.String("admin-user", "", "admin username") + adminPassword := fs.String("admin-password", os.Getenv("STALWART_MIGRATE_ADMIN_PASSWORD"), + "admin password (or set STALWART_MIGRATE_ADMIN_PASSWORD)") + targetVersion := fs.String("target", "latest", `target Stalwart version, or "latest"`) + stateDir := fs.String("state-dir", checkpoint.DefaultBaseDir, "directory to store run checkpoints in") + workDir := fs.String("work-dir", "/var/lib/stalwart-migrator/work", "scratch directory for backups, dumps, and the dry-run sandbox (cleaned up afterward - see --keep-artifacts)") + stalwartCLI := fs.String("stalwart-cli", "stalwart-cli", "path to the stalwart-cli binary") + pythonPath := fs.String("python", "python3", "path to python3") + migrationScriptSHA256 := fs.String("migration-script-sha256", "", "pinned sha256 of migrate_v016.py (recommended; see preflight/backup output for the hash to pin after a first unpinned run)") + recoveryPort := fs.Int("recovery-port", 8080, "port recovery mode's HTTP listener binds, per UPGRADING/v0_16.md's own examples") + minFree := fs.Float64("min-free-multiple", 2.0, "required free disk space as a multiple of the data directory size") + dryRun := fs.Bool("dry-run", false, "simulate and validate the migration against a disposable sandbox, without touching production") + keepArtifacts := fs.Bool("keep-artifacts", false, "don't delete work-dir/ afterward (the fs-backup copy, dumps, and sandbox) - useful for inspecting a failure") + if err := fs.Parse(args); err != nil { + return err + } + + if !*dryRun { + return fmt.Errorf( + "real (non-dry-run) migrations aren't available yet: internal/rollback and real systemd/Docker service control " + + "aren't implemented, so this tool can't yet guarantee it can undo a failed cutover. Run with --dry-run to " + + "validate the migration mechanics against a disposable sandbox copy of your data - nothing in production is touched", + ) + } + if *adminURL == "" { + return fmt.Errorf("--admin-url is required") + } + + ctx := context.Background() + httpClient := &http.Client{} + + if err := os.MkdirAll(*workDir, 0o750); err != nil { + return fmt.Errorf("create work dir %s: %w", *workDir, err) + } + + store := checkpoint.NewStore(*stateDir) + rs, err := store.Create("", *targetVersion) + if err != nil { + return fmt.Errorf("create run: %w", err) + } + fmt.Printf("run id: %s\n\n", rs.RunID) + + runWorkDir := filepath.Join(*workDir, rs.RunID) + logPath := filepath.Join(*stateDir, rs.RunID, "state.json") + defer func() { + if _, statErr := os.Stat(runWorkDir); os.IsNotExist(statErr) { + return // nothing was ever written (e.g. refused before backup ran) + } + if *keepArtifacts { + fmt.Printf("\nartifacts kept at %s (--keep-artifacts) - remove manually when done inspecting\n", runWorkDir) + return + } + outcome := "succeeded" + if err != nil { + outcome = "failed" + } + if rmErr := os.RemoveAll(runWorkDir); rmErr != nil { + fmt.Fprintf(os.Stderr, "\nwarning: dry run %s, but failed to clean up %s: %v (remove it manually)\n", outcome, runWorkDir, rmErr) + return + } + fmt.Printf("\ndry run %s - cleaned up %s; the run log is at %s\n", outcome, runWorkDir, logPath) + }() + + fmt.Println("--- preflight ---") + checker := preflight.New(preflight.Options{ + BinaryPath: *binaryPath, ConfigPath: *configPath, DataDir: *dataDir, ContainerName: *containerName, + AdminURL: *adminURL, AdminUser: *adminUser, AdminPassword: *adminPassword, + TargetVersion: *targetVersion, MinFreeMultiple: *minFree, HTTPClient: httpClient, + }) + pfReport, err := checker.Run(ctx, store, rs) + fmt.Print(pfReport.String()) + if err != nil { + return fmt.Errorf("preflight failed to complete: %w", err) + } + if pfReport.Blocking() { + return fmt.Errorf("preflight found blocking issues - see FAIL lines above") + } + + p, err := plan.Decide(rs.SourceVersion, rs.TargetVersion) + if err != nil { + return fmt.Errorf("plan: %w", err) + } + fmt.Printf("\nplan: %s\n", p.Reason) + + fmt.Println("\n--- backup ---") + fmt.Println("(dry-run does not stop the live Stalwart service itself - no service control is implemented yet. " + + "For a guaranteed-consistent snapshot, stop stalwart before running this; otherwise the filesystem copy may " + + "reflect a live, in-use store. This is unrelated to whether production gets touched - it never does.)") + + backupDir := filepath.Join(runWorkDir, "backup") + scriptDest := filepath.Join(runWorkDir, "migrate_v016.py") + settingsPath := filepath.Join(runWorkDir, "settings.json") + principalsPath := filepath.Join(runWorkDir, "principals.json") + + backupOpts := backup.Options{ + BinaryPath: *binaryPath, + SkipBinaryPreservation: true, // dry-run: never touch the production binary + DataDir: *dataDir, + BackupDir: backupDir, + MigrationScriptSHA256: *migrationScriptSHA256, + ScriptDestPath: scriptDest, + AdminURL: *adminURL, + AdminUser: *adminUser, + AdminPassword: *adminPassword, + SettingsDumpPath: settingsPath, + PrincipalsDumpPath: principalsPath, + PythonPath: *pythonPath, + HTTPClient: httpClient, + } + bkReport, err := backup.Run(ctx, store, rs, backupOpts) + fmt.Print(bkReport.String()) + if err != nil { + return fmt.Errorf("backup failed: %w", err) + } + + if !p.CrossesMajorBoundary { + fmt.Println("\nthis is a same-boundary patch upgrade: there's no recovery-mode phase to simulate. " + + "preflight and backup above are as far as a dry-run goes for this path - a real run would be a binary swap and restart.") + return nil + } + + if *targetBinaryPath == "" { + return fmt.Errorf("--target-binary is required to simulate a major-boundary migration (0.15 -> 0.16 crosses one here)") + } + + fmt.Println("\n--- convert (settings -> sandbox config) ---") + sandboxDataDir := filepath.Join(runWorkDir, "sandbox-data") + sandboxConfigPath := filepath.Join(runWorkDir, "sandbox-config.json") + sandboxExportPath := filepath.Join(runWorkDir, "sandbox-export.json") + + if _, err := store.RunStep(rs, checkpoint.PhaseStage, "clone-sandbox-data", func() (checkpoint.StepOutcome, error) { + manifest, err := backup.CopyDataDir(backupDir, sandboxDataDir) + if err != nil { + return checkpoint.StepOutcome{}, err + } + return checkpoint.StepOutcome{Detail: fmt.Sprintf("cloned the verified backup (%d files) into the sandbox at %s", len(manifest.Files), sandboxDataDir)}, nil + }); err != nil { + return fmt.Errorf("clone sandbox data: %w", err) + } + fmt.Printf("cloned verified backup into sandbox: %s\n", sandboxDataDir) + + if _, err := store.RunStep(rs, checkpoint.PhaseStage, "convert-settings", func() (checkpoint.StepOutcome, error) { + if err := backup.RunSettingsConvert(ctx, backup.SettingsConvertOptions{ + PythonPath: *pythonPath, ScriptPath: scriptDest, + SettingsPath: settingsPath, PrincipalsPath: principalsPath, + ConfigPath: sandboxConfigPath, OutputPath: sandboxExportPath, + PatchPaths: map[string]string{*dataDir: sandboxDataDir}, + }); err != nil { + return checkpoint.StepOutcome{}, err + } + return checkpoint.StepOutcome{Detail: fmt.Sprintf("generated %s and %s, patched to point at the sandbox", sandboxConfigPath, sandboxExportPath)}, nil + }); err != nil { + return fmt.Errorf("convert settings: %w", err) + } + fmt.Println("generated sandbox config.json and export.json") + + fmt.Println("\n--- recovery-mode migration (against the sandbox) ---") + listenURL := fmt.Sprintf("http://127.0.0.1:%d/", *recoveryPort) + recReport, err := recovery.Run(ctx, store, rs, recovery.Options{ + BinaryPath: *targetBinaryPath, ConfigPath: sandboxConfigPath, ListenURL: listenURL, + AdminUser: "admin", ApplyFiles: []string{sandboxExportPath}, CLIBinaryPath: *stalwartCLI, + HTTPClient: httpClient, + }) + fmt.Print(recReport.String()) + if err != nil { + return fmt.Errorf("recovery-mode migration against the sandbox failed: %w", err) + } + + fmt.Println("\n--- boot check (normal boot of the migrated sandbox) ---") + if rs.PreflightSnapshot != nil { + fmt.Println("(comparing against the pre-migration account/mailbox snapshot preflight captured - " + + "this is the actual no-data-loss check, not just a reachability probe)") + } else { + fmt.Println("(no pre-migration snapshot to compare against - preflight couldn't capture one, most likely " + + "because --admin-url wasn't set; only reachability is checked)") + } + valReport, err := validate.Run(ctx, store, rs, validate.BootCheckOptions{ + BinaryPath: *targetBinaryPath, ConfigPath: sandboxConfigPath, ListenURL: listenURL, HTTPClient: httpClient, + ContentIntegrityBefore: rs.PreflightSnapshot, + AdminUser: *adminUser, + AdminPassword: *adminPassword, + }) + fmt.Print(valReport.String()) + if err != nil { + return fmt.Errorf("post-migration validation failed: %w", err) + } + + verified := "the migration mechanics succeeded" + if rs.PreflightSnapshot != nil { + verified = "the migration mechanics succeeded AND every account/mailbox message count matched before vs. after" + } + fmt.Printf("\nDRY RUN COMPLETE for run %s: %s, against a disposable sandbox copy of your data. Nothing in production was touched.\n", rs.RunID, verified) + return nil +} diff --git a/cmd/stalwart-migrate/status.go b/cmd/stalwart-migrate/status.go new file mode 100644 index 0000000..77e2959 --- /dev/null +++ b/cmd/stalwart-migrate/status.go @@ -0,0 +1,62 @@ +package main + +import ( + "flag" + "fmt" + + "github.com/LINUXexpert-org/stalwart-migrator/internal/checkpoint" +) + +func runStatus(args []string) error { + fs := flag.NewFlagSet("status", flag.ExitOnError) + stateDir := fs.String("state-dir", checkpoint.DefaultBaseDir, "directory runs are checkpointed in") + if err := fs.Parse(args); err != nil { + return err + } + + store := checkpoint.NewStore(*stateDir) + + if fs.NArg() == 0 { + ids, err := store.List() + if err != nil { + return fmt.Errorf("list runs: %w", err) + } + if len(ids) == 0 { + fmt.Println("no runs found in", *stateDir) + return nil + } + fmt.Println("runs (newest first):") + for _, id := range ids { + fmt.Println(" ", id) + } + return nil + } + + runID := fs.Arg(0) + rs, err := store.Load(runID) + if err != nil { + return fmt.Errorf("load run %s: %w", runID, err) + } + + fmt.Printf("run: %s\n", rs.RunID) + fmt.Printf("source: %s\n", rs.SourceVersion) + fmt.Printf("target: %s\n", rs.TargetVersion) + fmt.Printf("topology: deployment=%s store=%s\n", rs.Topology.DeploymentKind, rs.Topology.StoreBackend) + fmt.Printf("rollback window closed: %v\n", rs.RollbackWindowClosed) + fmt.Println("steps:") + for _, step := range rs.Steps { + tag := string(step.Status) + if step.Verdict != "" { + tag = step.Verdict + } + line := fmt.Sprintf(" [%-4s] %s/%s", tag, step.Phase, step.Name) + if step.Detail != "" { + line += " - " + step.Detail + } + if step.Error != "" { + line += " (error: " + step.Error + ")" + } + fmt.Println(line) + } + return nil +} diff --git a/go.mod b/go.mod index 869cfdd..d9952bb 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ -module github.com/johnellis/stalwart-migrator +module github.com/LINUXexpert-org/stalwart-migrator go 1.26.5 diff --git a/internal/backup/backup.go b/internal/backup/backup.go index 5cc57bb..ece1c3c 100644 --- a/internal/backup/backup.go +++ b/internal/backup/backup.go @@ -8,7 +8,7 @@ import ( "path/filepath" "strings" - "github.com/johnellis/stalwart-migrator/internal/checkpoint" + "github.com/LINUXexpert-org/stalwart-migrator/internal/checkpoint" ) // Options configures a full backup pass. Which fields matter depends on diff --git a/internal/backup/backup_test.go b/internal/backup/backup_test.go index 0da492b..7d00575 100644 --- a/internal/backup/backup_test.go +++ b/internal/backup/backup_test.go @@ -9,7 +9,7 @@ import ( "path/filepath" "testing" - "github.com/johnellis/stalwart-migrator/internal/checkpoint" + "github.com/LINUXexpert-org/stalwart-migrator/internal/checkpoint" ) func TestBackupRunEndToEndAndResume(t *testing.T) { diff --git a/internal/preflight/checks.go b/internal/preflight/checks.go index 49eba04..2895d1e 100644 --- a/internal/preflight/checks.go +++ b/internal/preflight/checks.go @@ -8,8 +8,8 @@ import ( "strings" "time" - "github.com/johnellis/stalwart-migrator/internal/checkpoint" - "github.com/johnellis/stalwart-migrator/internal/stalwartapi" + "github.com/LINUXexpert-org/stalwart-migrator/internal/checkpoint" + "github.com/LINUXexpert-org/stalwart-migrator/internal/stalwartapi" ) // Options configures a Checker. Every field has a conservative default diff --git a/internal/preflight/checks_test.go b/internal/preflight/checks_test.go index 4445555..ee1901a 100644 --- a/internal/preflight/checks_test.go +++ b/internal/preflight/checks_test.go @@ -11,7 +11,7 @@ import ( "strings" "testing" - "github.com/johnellis/stalwart-migrator/internal/checkpoint" + "github.com/LINUXexpert-org/stalwart-migrator/internal/checkpoint" ) // writeFakeBinary creates a shell script that behaves like `stalwart diff --git a/internal/recovery/recovery.go b/internal/recovery/recovery.go index 8289bd2..a4b2a2f 100644 --- a/internal/recovery/recovery.go +++ b/internal/recovery/recovery.go @@ -8,7 +8,7 @@ import ( "net/http" "time" - "github.com/johnellis/stalwart-migrator/internal/checkpoint" + "github.com/LINUXexpert-org/stalwart-migrator/internal/checkpoint" ) // Options configures one full recovery-mode migration cycle: starting the diff --git a/internal/recovery/recovery_test.go b/internal/recovery/recovery_test.go index 4c9befd..3e30afd 100644 --- a/internal/recovery/recovery_test.go +++ b/internal/recovery/recovery_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - "github.com/johnellis/stalwart-migrator/internal/checkpoint" + "github.com/LINUXexpert-org/stalwart-migrator/internal/checkpoint" ) func TestRecoveryRunEndToEndAndResume(t *testing.T) { diff --git a/internal/validate/bootcheck.go b/internal/validate/bootcheck.go index d242301..1814df8 100644 --- a/internal/validate/bootcheck.go +++ b/internal/validate/bootcheck.go @@ -6,9 +6,9 @@ import ( "net/http" "time" - "github.com/johnellis/stalwart-migrator/internal/checkpoint" - "github.com/johnellis/stalwart-migrator/internal/recovery" - "github.com/johnellis/stalwart-migrator/internal/stalwartapi" + "github.com/LINUXexpert-org/stalwart-migrator/internal/checkpoint" + "github.com/LINUXexpert-org/stalwart-migrator/internal/recovery" + "github.com/LINUXexpert-org/stalwart-migrator/internal/stalwartapi" ) // BootCheckOptions configures a normal (non-recovery-mode) boot of the diff --git a/internal/validate/bootcheck_test.go b/internal/validate/bootcheck_test.go index 914149f..5c7d4f1 100644 --- a/internal/validate/bootcheck_test.go +++ b/internal/validate/bootcheck_test.go @@ -9,7 +9,7 @@ import ( "testing" "time" - "github.com/johnellis/stalwart-migrator/internal/checkpoint" + "github.com/LINUXexpert-org/stalwart-migrator/internal/checkpoint" ) func freePort(t *testing.T) int { diff --git a/internal/validate/content_integrity.go b/internal/validate/content_integrity.go index 8bacf7e..000b913 100644 --- a/internal/validate/content_integrity.go +++ b/internal/validate/content_integrity.go @@ -6,8 +6,8 @@ import ( "sort" "strings" - "github.com/johnellis/stalwart-migrator/internal/checkpoint" - "github.com/johnellis/stalwart-migrator/internal/stalwartapi" + "github.com/LINUXexpert-org/stalwart-migrator/internal/checkpoint" + "github.com/LINUXexpert-org/stalwart-migrator/internal/stalwartapi" ) // MailboxDelta is one mailbox whose message count didn't match between the diff --git a/internal/validate/content_integrity_test.go b/internal/validate/content_integrity_test.go index cd94e44..1a0d794 100644 --- a/internal/validate/content_integrity_test.go +++ b/internal/validate/content_integrity_test.go @@ -8,8 +8,8 @@ import ( "strings" "testing" - "github.com/johnellis/stalwart-migrator/internal/checkpoint" - "github.com/johnellis/stalwart-migrator/internal/stalwartapi" + "github.com/LINUXexpert-org/stalwart-migrator/internal/checkpoint" + "github.com/LINUXexpert-org/stalwart-migrator/internal/stalwartapi" ) // jmapEnvelope mirrors the wire shape stalwartapi.Client.call() parses.