Skip to content

refactor: use MigratorTraitSelf to eliminate global migration state - #2753

Open
ctron wants to merge 1 commit into
guacsec:mainfrom
ctron:refactor/migrator-trait-self_1
Open

ctron wants to merge 1 commit into
guacsec:mainfrom
ctron:refactor/migrator-trait-self_1

Conversation

@ctron

@ctron ctron commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Replace global state with explicit state passing — Migrator is now a struct carrying DispatchBackend and Options, implementing SeaORM 2.x's MigratorTraitSelf (instance methods) instead of the static MigratorTrait
  • Remove side-channel machinery — LazyLock globals, task_local! statics, init_storage() (with block_on), MigrationWithData::new(), and run_with_test() are all eliminated
  • Thread storage/options explicitly — Database::migrate(), refresh(), bootstrap() now take a &Migrator; CLI commands and tests construct migrators directly

Motivation

Data migrations need runtime state (storage backend, concurrency options) passed into their up/down methods. SeaORM 1.x's MigratorTrait::migrations() was a static method with no self, so the project used LazyLock globals and task_local! statics as a side channel to smuggle state into migrations. SeaORM 2.x added MigratorTraitSelf with fn migrations(&self), making this workaround unnecessary.

Test plan

  • cargo xtask precommit passes (schemas, openapi, clippy, fmt, check)
  • Migration tests pass (cargo test -p migration)
  • Data migration tests pass (migration/tests/data/)
  • Vulnerability endpoint migration test passes
  • trustd db migrate still works with explicit storage config

🤖 Generated with Claude Code

Summary by Sourcery

Adopt SeaORM's instance-based migration API to eliminate global migration state and pass runtime migration dependencies explicitly.

Enhancements:

  • Replace static migration execution with instance-based migrators that explicitly carry storage backends and migration options.
  • Pass migrator state through database setup, embedded database creation, CLI commands, server initialization, dataset generation, and tests.
  • Remove global and task-local migration state and the associated test-only execution helpers.

Build:

  • Remove the migration crate's direct futures dependency.

Tests:

  • Update migration, database, server, and data-migration tests to construct and use explicit migrator instances.

…ion state

Replace the LazyLock/task_local side channel used to smuggle storage and
options into data migrations with SeaORM 2.x's MigratorTraitSelf trait.

Migrator is now a struct carrying DispatchBackend and Options as fields.
The instance method migrations(&self) constructs MigrationWithData
wrappers directly from the migrator's state, removing the need for
global statics, task-local scoping, and the run_with_test() helper.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@sourcery-ai

sourcery-ai Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Refactors migration execution from static SeaORM APIs plus global/task-local state to explicit Migrator instances carrying storage and options, and threads those instances through database helpers, CLI commands, application startup, tests, and tooling.

Sequence diagram for explicit migration execution

sequenceDiagram
    participant Caller
    participant Database
    participant Migrator
    participant SeaORM
    participant DataMigration
    Caller->>Migrator: new(storage, options)
    Caller->>Database: migrate(migrator)
    Database->>Migrator: up(database, None)
    Migrator->>Migrator: migrations()
    Migrator->>SeaORM: execute migrations
    SeaORM->>DataMigration: up(manager)
    DataMigration->>DataMigration: use storage and options
Loading

File-Level Changes

Change Details Files
Replace static migration execution with instance-based migrators that carry storage and migration options.
  • Introduced a stateful Migrator with constructor-based dependency injection.
  • Implemented SeaORM 2.x MigratorTraitSelf and built data-migration wrappers from instance state.
  • Updated migration tests and CLI entry points to construct and invoke migrator instances.
migration/src/lib.rs
migration/src/data/mod.rs
migration/src/data/migration.rs
migration/src/main.rs
migration/tests/data/m0002010.rs
migration/tests/data/main.rs
migration/tests/previous.rs
migration/tests/tests.rs
Remove global and task-local state used to provide runtime dependencies to data migrations.
  • Deleted lazy global storage/options initialization and task-local test overrides.
  • Removed MigrationWithData::new() and run_with_test().
  • Passed cloned storage and options explicitly while converting data migrations.
migration/src/data/migration.rs
migration/src/data/mod.rs
migration/Cargo.toml
Propagate explicit migrator dependencies through database setup, application startup, tests, and tooling.
  • Changed migrate, refresh, bootstrap, and embedded database helpers to accept &Migrator.
  • Constructed migrators with configured storage/options in server initialization, test contexts, snapshot setup, and dataset generation.
  • Updated vulnerability, profile, and migration tests to use explicit migrators.
common/db/src/lib.rs
common/db/src/embedded.rs
modules/fundamental/src/vulnerability/endpoints/test.rs
server/src/openapi.rs
server/src/profile/api.rs
server/src/profile/mod.rs
test-context/src/ctx/default.rs
test-context/src/ctx/migration/snapshot/mod.rs
xtask/src/dataset.rs
Expose storage and data-migration options through the database CLI and reuse them across migration commands.
  • Added flattened storage and migration option arguments to the database command.
  • Initialized storage once and passed a configured migrator to create, migrate, and refresh operations.
  • Passed the resolved backend and options directly to data migration execution.
trustd/src/db.rs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@ctron
ctron requested a review from jcrossley3 October 9, 2026 12:30

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 3 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="migration/src/main.rs" line_range="7" />
<code_context>
 #[tokio::main]
 async fn main() {
-    cli::run_cli(migration::Migrator).await;
+    let storage = StorageConfig::parse()
+        .into_storage(false)
+        .await
</code_context>
<issue_to_address>
**Migration commands are rejected**

When the migration binary is invoked with a migration subcommand or its flags, `StorageConfig::parse()` parses the process arguments before `cli::run_cli` receives them, so migration commands such as `up` or `status` are rejected as unknown storage arguments and the migration CLI exits.

Parse storage settings without consuming the migration CLI arguments, or combine storage and migration arguments in one parser.
</issue_to_address>

### Comment 2
<location path="trustd/src/db.rs" line_range="15" />
<code_context>
     pub(crate) command: Command,
     #[command(flatten)]
     pub(crate) database: Database,
+    /// Location of the storage
</code_context>
<issue_to_address>
**Data command flags are rejected**

When a caller supplies storage or migration-option flags after `trustify db data`, clap defines `StorageConfig` and `Options` on `Run` rather than `Data`, so previously accepted flags after `data` are rejected as unknown arguments and the requested data migration does not run.

Preserve the flattened storage and migration options on `Data`, or make them global so existing command forms remain accepted.

Also at `trustd/src/db.rs:16-18`.
</issue_to_address>

### Comment 3
<location path="migration/src/main.rs" line_range="11" />
<code_context>
+        .into_storage(false)
+        .await
+        .expect("failed to initialize storage");
+    cli::run_cli(migration::Migrator::new(storage, ())).await;
 }
</code_context>
<issue_to_address>
**Configured migration options are ignored**

When a `MIGRATION_DATA_*` variable is set for the standalone migration binary, `Migrator::new(storage, ())` uses default `Options`, so configured skip and runner settings are ignored and data migrations run with defaults, including migrations the operator intended to skip.

Construct `Options` from the environment and pass it to the migrator.

Also at `server/src/profile/api.rs:465-466`, `xtask/src/dataset.rs:131`.
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 3 findings to address first, and the refactor changes which storage backend and options data migrations use, so an incorrect wiring could write incorrect derived values or leave migrations incomplete in the database. Reverting restores the previous runner, but any already-written values would need to be recomputed or the affected migration rerun.

Blocking findings: migration/src/main.rs:7, trustd/src/db.rs:15, migration/src/main.rs:11


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread migration/src/main.rs
#[tokio::main]
async fn main() {
cli::run_cli(migration::Migrator).await;
let storage = StorageConfig::parse()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High · Migration commands are rejected

When the migration binary is invoked with a migration subcommand or its flags, StorageConfig::parse() parses the process arguments before cli::run_cli receives them, so migration commands such as up or status are rejected as unknown storage arguments and the migration CLI exits.

Parse storage settings without consuming the migration CLI arguments, or combine storage and migration arguments in one parser.

Prompt for AI agents
In `migration/src/main.rs` at line 7:

**Migration commands are rejected**

When the migration binary is invoked with a migration subcommand or its flags, `StorageConfig::parse()` parses the process arguments before `cli::run_cli` receives them, so migration commands such as `up` or `status` are rejected as unknown storage arguments and the migration CLI exits.

Parse storage settings without consuming the migration CLI arguments, or combine storage and migration arguments in one parser.

Comment thread trustd/src/db.rs
#[command(flatten)]
pub(crate) database: Database,
/// Location of the storage
#[command(flatten)]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Data command flags are rejected

When a caller supplies storage or migration-option flags after trustify db data, clap defines StorageConfig and Options on Run rather than Data, so previously accepted flags after data are rejected as unknown arguments and the requested data migration does not run.

Preserve the flattened storage and migration options on Data, or make them global so existing command forms remain accepted.

Also at trustd/src/db.rs:16-18.

Prompt for AI agents
In `trustd/src/db.rs` at line 15:

**Data command flags are rejected**

When a caller supplies storage or migration-option flags after `trustify db data`, clap defines `StorageConfig` and `Options` on `Run` rather than `Data`, so previously accepted flags after `data` are rejected as unknown arguments and the requested data migration does not run.

Preserve the flattened storage and migration options on `Data`, or make them global so existing command forms remain accepted.

Also at `trustd/src/db.rs:16-18`.

Comment thread migration/src/main.rs
.into_storage(false)
.await
.expect("failed to initialize storage");
cli::run_cli(migration::Migrator::new(storage, ())).await;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High · Configured migration options are ignored

When a MIGRATION_DATA_* variable is set for the standalone migration binary, Migrator::new(storage, ()) uses default Options, so configured skip and runner settings are ignored and data migrations run with defaults, including migrations the operator intended to skip.

Construct Options from the environment and pass it to the migrator.

Also at server/src/profile/api.rs:465-466, xtask/src/dataset.rs:131.

Prompt for AI agents
In `migration/src/main.rs` at line 11:

**Configured migration options are ignored**

When a `MIGRATION_DATA_*` variable is set for the standalone migration binary, `Migrator::new(storage, ())` uses default `Options`, so configured skip and runner settings are ignored and data migrations run with defaults, including migrations the operator intended to skip.

Construct `Options` from the environment and pass it to the migrator.

Also at `server/src/profile/api.rs:465-466`, `xtask/src/dataset.rs:131`.

@ctron
ctron requested review from a team and removed request for jcrossley3 October 9, 2026 12:31

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant