Skip to content

Commit c978735

Browse files
committed
Add db baseline command and mc-style alias syntax
- ow <alias> db migrate instead of ow --alias <alias> db migrate - db baseline marks all migrations as applied (for existing DBs)
1 parent 5b2c823 commit c978735

3 files changed

Lines changed: 119 additions & 10 deletions

File tree

README.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,13 @@ Requires a `db` type alias.
4949

5050
```bash
5151
# Run pending migrations
52-
ow --alias infra db migrate
52+
ow infra db migrate
5353

5454
# Check migration status
55-
ow --alias infra db status
55+
ow infra db status
56+
57+
# Baseline existing database (mark all migrations as applied without running them)
58+
ow infra db baseline
5659
```
5760

5861
### Using Aliases
@@ -61,8 +64,9 @@ ow --alias infra db status
6164
# Use default alias
6265
ow db status
6366

64-
# Use specific alias
65-
ow --alias infra db migrate
67+
# Use specific alias (mc-style, alias as first argument)
68+
ow infra db migrate
69+
ow prod db status
6670
```
6771

6872
## Config File Format

src/commands/db.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ pub enum DbCommand {
3232

3333
/// Show migration status
3434
Status,
35+
36+
/// Mark all migrations as applied without running them (for existing databases)
37+
Baseline,
3538
}
3639

3740
impl DbCommand {
@@ -42,6 +45,7 @@ impl DbCommand {
4245
match self {
4346
Self::Migrate => cmd_migrate(&pool).await,
4447
Self::Status => cmd_status(&pool).await,
48+
Self::Baseline => cmd_baseline(&pool).await,
4549
}
4650
}
4751
}
@@ -137,3 +141,63 @@ async fn cmd_status(pool: &PgPool) -> Result<(), DbError> {
137141

138142
Ok(())
139143
}
144+
145+
async fn cmd_baseline(pool: &PgPool) -> Result<(), DbError> {
146+
// Create _sqlx_migrations table if it doesn't exist
147+
sqlx::query(
148+
r#"
149+
CREATE TABLE IF NOT EXISTS _sqlx_migrations (
150+
version BIGINT PRIMARY KEY,
151+
description TEXT NOT NULL,
152+
installed_on TIMESTAMPTZ NOT NULL DEFAULT now(),
153+
success BOOLEAN NOT NULL,
154+
checksum BYTEA NOT NULL,
155+
execution_time BIGINT NOT NULL
156+
)
157+
"#,
158+
)
159+
.execute(pool)
160+
.await?;
161+
162+
// Check if already baselined
163+
let count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM _sqlx_migrations")
164+
.fetch_one(pool)
165+
.await?;
166+
167+
if count > 0 {
168+
println!(
169+
"{} Database already has {} migration(s) recorded.",
170+
"Warning:".yellow().bold(),
171+
count
172+
);
173+
println!("Use '{}' to check status.", "ow db status".cyan());
174+
return Ok(());
175+
}
176+
177+
println!("Marking all migrations as applied...\n");
178+
179+
for migration in MIGRATOR.iter() {
180+
sqlx::query(
181+
r#"
182+
INSERT INTO _sqlx_migrations (version, description, success, checksum, execution_time)
183+
VALUES ($1, $2, true, $3, 0)
184+
"#,
185+
)
186+
.bind(migration.version)
187+
.bind(&*migration.description)
188+
.bind(&migration.checksum[..])
189+
.execute(pool)
190+
.await?;
191+
192+
println!(" {} {}", "Baseline".blue(), migration.description);
193+
}
194+
195+
// Drop old _migrations table if it exists
196+
sqlx::query("DROP TABLE IF EXISTS _migrations")
197+
.execute(pool)
198+
.await?;
199+
200+
println!("\n{}", "Baseline complete.".green().bold());
201+
202+
Ok(())
203+
}

src/main.rs

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,12 @@ use colored::Colorize;
66

77
use commands::alias::AliasCommand;
88
use commands::db::DbCommand;
9+
use config::Config;
910

1011
#[derive(Parser)]
1112
#[command(name = "ow")]
1213
#[command(author, version, about = "OpenWorkers CLI", long_about = None)]
1314
struct Cli {
14-
/// Use a specific alias
15-
#[arg(long, global = true)]
16-
alias: Option<String>,
17-
1815
#[command(subcommand)]
1916
command: Commands,
2017
}
@@ -34,13 +31,57 @@ enum Commands {
3431
},
3532
}
3633

34+
/// Extract alias from args if first arg matches a known alias.
35+
/// Returns (alias, filtered_args) where filtered_args has the alias removed.
36+
fn extract_alias_from_args() -> (Option<String>, Vec<String>) {
37+
let args: Vec<String> = std::env::args().collect();
38+
39+
// Need at least: program name + potential alias + command
40+
if args.len() < 2 {
41+
return (None, args);
42+
}
43+
44+
let potential_alias = &args[1];
45+
46+
// Skip if it looks like a flag or is a known command
47+
if potential_alias.starts_with('-') {
48+
return (None, args);
49+
}
50+
51+
let known_commands = ["alias", "db", "help", "--help", "-h", "--version", "-V"];
52+
53+
if known_commands.contains(&potential_alias.as_str()) {
54+
return (None, args);
55+
}
56+
57+
// Check if it's a known alias
58+
if let Ok(config) = Config::load() {
59+
if config.get_alias(potential_alias).is_some() {
60+
// Remove the alias from args
61+
let mut filtered: Vec<String> = Vec::with_capacity(args.len() - 1);
62+
filtered.push(args[0].clone());
63+
filtered.extend(args[2..].iter().cloned());
64+
return (Some(potential_alias.clone()), filtered);
65+
}
66+
}
67+
68+
(None, args)
69+
}
70+
3771
#[tokio::main]
3872
async fn main() {
39-
let cli = Cli::parse();
73+
let (alias, args) = extract_alias_from_args();
74+
75+
let cli = match Cli::try_parse_from(&args) {
76+
Ok(cli) => cli,
77+
Err(e) => {
78+
e.exit();
79+
}
80+
};
4081

4182
let result = match cli.command {
4283
Commands::Alias { command } => command.run().map_err(|e| e.to_string()),
43-
Commands::Db { command } => command.run(cli.alias).await.map_err(|e| e.to_string()),
84+
Commands::Db { command } => command.run(alias).await.map_err(|e| e.to_string()),
4485
};
4586

4687
if let Err(e) = result {

0 commit comments

Comments
 (0)