Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/schema-forge-cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "schema-forge-cli"
version = "0.37.3"
version = "0.37.4"
edition = "2021"

[[bin]]
Expand Down
43 changes: 30 additions & 13 deletions crates/schema-forge-cli/src/commands/apply.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use console::Term;
use schema_forge_core::migration::{DiffEngine, MigrationSafety};
use schema_forge_acton::DynSchemaBackend;
use schema_forge_core::{migration::MigrationSafety, types::SchemaDefinition};

use super::schema_update::SchemaUpdate;

use crate::cli::{ApplyArgs, GlobalOpts};
use crate::commands::parse::parse_all_schemas_with_global;
Expand All @@ -22,19 +25,25 @@ pub async fn run(

let backend = super::connect_backend(&db_params, output).await?;

apply_to_backend(&args, &schemas, backend.as_ref(), output).await
}

pub(super) async fn apply_to_backend(
args: &ApplyArgs,
schemas: &[SchemaDefinition],
backend: &dyn DynSchemaBackend,
output: &OutputContext,
) -> Result<(), CliError> {
let mut total_steps = 0usize;
let mut applied_schemas = 0usize;
let mut metadata_only_updates = 0usize;

for schema in &schemas {
for schema in schemas {
let existing = backend.load_schema_metadata(&schema.name).await?;

let plan = if let Some(old) = existing {
DiffEngine::diff(&old, schema)
} else {
DiffEngine::create_new(schema)
};

if plan.is_empty() {
let update = SchemaUpdate::plan(existing.as_ref(), schema);
let plan = &update.migration;
if update.is_empty() {
output.status(&format!(" {} .... no changes", schema.name.as_str()));
continue;
}
Expand Down Expand Up @@ -75,7 +84,12 @@ pub async fn run(
let safety_label = plan.overall_safety();
match output.mode {
OutputMode::Human => {
if plan.steps.len() == 1
if plan.is_empty() {
output.status(&format!(
" {:<16} METADATA UPDATE (0 migration steps)",
schema.name.as_str()
));
} else if plan.steps.len() == 1
&& matches!(
&plan.steps[0],
schema_forge_core::migration::MigrationStep::CreateSchema { .. }
Expand All @@ -102,17 +116,19 @@ pub async fn run(
}

if !args.dry_run {
backend.apply_migration(&schema.name, &plan.steps).await?;
backend.store_schema_metadata(schema).await?;
update.persist(backend).await?;
}

if plan.is_empty() {
metadata_only_updates += 1;
}
total_steps += plan.steps.len();
applied_schemas += 1;
}

// Generate policies if requested
if args.with_policies && !args.dry_run {
for schema in &schemas {
for schema in schemas {
let policies = schema_forge_acton::cedar::generate_cedar_policies(schema);
output.status(&format!(
" Generated {} Cedar policies for {}",
Expand Down Expand Up @@ -140,6 +156,7 @@ pub async fn run(
"dry_run": args.dry_run,
"schemas_applied": applied_schemas,
"total_steps": total_steps,
"metadata_only_updates": metadata_only_updates,
});
output.print_json(&json);
}
Expand Down
64 changes: 46 additions & 18 deletions crates/schema-forge-cli/src/commands/migrate.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use console::Term;
use schema_forge_core::migration::DiffEngine;
use schema_forge_acton::DynSchemaBackend;
use schema_forge_core::types::SchemaDefinition;

use super::schema_update::SchemaUpdate;

use crate::cli::{GlobalOpts, MigrateArgs};
use crate::commands::parse::parse_all_schemas_with_global;
Expand All @@ -20,11 +23,20 @@ pub async fn run(

let backend = super::connect_backend(&db_params, output).await?;

migrate_on_backend(&args, &schemas, backend.as_ref(), output).await
}

pub(super) async fn migrate_on_backend(
args: &MigrateArgs,
schemas: &[SchemaDefinition],
backend: &dyn DynSchemaBackend,
output: &OutputContext,
) -> Result<(), CliError> {
let mut plans = Vec::new();
let mut total_steps = 0usize;
let mut schemas_affected = 0usize;

for schema in &schemas {
for schema in schemas {
// Filter by --schema if specified
if let Some(ref filter) = args.schema {
if schema.name.as_str() != filter {
Expand All @@ -33,13 +45,10 @@ pub async fn run(
}

let existing = backend.load_schema_metadata(&schema.name).await?;
let plan = if let Some(old) = existing {
DiffEngine::diff(&old, schema)
} else {
DiffEngine::create_new(schema)
};
let update = SchemaUpdate::plan(existing.as_ref(), schema);
let plan = &update.migration;

if plan.is_empty() {
if update.is_empty() {
if output.mode == OutputMode::Human {
output.status(&format!("{} (no changes)", schema.name.as_str()));
}
Expand All @@ -48,16 +57,26 @@ pub async fn run(
schemas_affected += 1;
}

plans.push((schema, plan));
plans.push(update);
}

// Render plan
match output.mode {
OutputMode::Human => {
println!("Migration plan for {} schemas:", plans.len());
println!();
for (schema, plan) in &plans {
for update in &plans {
let schema = &update.schema;
let plan = &update.migration;
if update.is_empty() {
continue;
}
if plan.is_empty() {
println!(
"{} (metadata update, 0 migration steps)",
schema.name.as_str()
);
println!();
continue;
}
println!(
Expand All @@ -79,8 +98,10 @@ pub async fn run(
OutputMode::Json => {
let json_plans: Vec<serde_json::Value> = plans
.iter()
.filter(|(_, p)| !p.is_empty())
.map(|(schema, plan)| {
.filter(|update| !update.is_empty())
.map(|update| {
let schema = &update.schema;
let plan = &update.migration;
let steps: Vec<serde_json::Value> = plan
.steps
.iter()
Expand All @@ -93,6 +114,7 @@ pub async fn run(
.collect();
serde_json::json!({
"schema": schema.name.as_str(),
"metadata_changed": update.metadata_changed,
"safety": plan.overall_safety().to_string(),
"steps": steps,
})
Expand All @@ -106,10 +128,15 @@ pub async fn run(
output.print_json(&json);
}
OutputMode::Plain => {
for (schema, plan) in &plans {
if plan.is_empty() {
for update in &plans {
let schema = &update.schema;
let plan = &update.migration;
if update.is_empty() {
continue;
}
if plan.is_empty() {
println!("{}\tmetadata update\tsafe", schema.name.as_str());
}
for step in &plan.steps {
println!("{}\t{}\t{}", schema.name.as_str(), step, step.safety());
}
Expand All @@ -119,8 +146,10 @@ pub async fn run(

// Execute if requested
if args.execute {
for (schema, plan) in &plans {
if plan.is_empty() {
for update in &plans {
let schema = &update.schema;
let plan = &update.migration;
if update.is_empty() {
continue;
}

Expand All @@ -146,8 +175,7 @@ pub async fn run(
}
}

backend.apply_migration(&schema.name, &plan.steps).await?;
backend.store_schema_metadata(schema).await?;
update.persist(backend).await?;
}

output.success(&format!(
Expand Down
1 change: 1 addition & 0 deletions crates/schema-forge-cli/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub mod login;
pub mod migrate;
pub mod parse;
pub mod policies;
mod schema_update;
pub mod serve;
#[cfg(feature = "embedded-console")]
pub mod serve_console;
Expand Down
Loading