-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement most of the Diesel CLI for 0.4.0
This adds all the plumbing, and 3 of the 4 commands that I want to ship with the CLI for 0.4.0. This adds the ability to run, revert, and redo migrations. There's still a lot of things I'd like to improve about the CLI. We need to give better error output in cases where something unexpected happens, and I'd like to proxy unknown subcommands to `diesel-subcommand` (this last one appears to be [a limitation of clap](clap-rs/clap#372). I've opted to make `diesel_cli` be a separate crate, as cargo doesn't allow you to declare dependencies as only for executables. I don't want to add `clap` or `chrono` (which I'll be adding as a dependency in the follow up to this commit) to become hard dependencies of diesel itself. Another unrelated enhancement I'd like to eventually make is adding `dotenv` support. I think this should be optional, but if it doesn't become a hard dependency of diesel itself, maybe it doesn't matter?
- Loading branch information
Showing
5 changed files
with
144 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
#!/bin/sh | ||
cd diesel_cli && cargo run -- $@ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
#!/bin/sh | ||
(cd diesel && cargo test --features unstable) && | ||
(cd diesel_cli && cargo test) && | ||
(cd diesel_tests && cargo test --features unstable --no-default-features) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
[package] | ||
name = "diesel_cli" | ||
version = "0.4.0" | ||
authors = ["Sean Griffin <[email protected]>"] | ||
|
||
[[bin]] | ||
name = "diesel" | ||
|
||
[dependencies] | ||
diesel = "^0.3.0" | ||
clap = "^1.5.5" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
#[macro_use] | ||
extern crate clap; | ||
extern crate diesel; | ||
|
||
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; | ||
use diesel::migrations; | ||
use std::env; | ||
|
||
fn main() { | ||
let database_arg = || Arg::with_name("DATABASE_URL") | ||
.long("database-url") | ||
.help("Specifies the database URL to connect to. Falls back to \ | ||
the DATABASE_URL environment variable if unspecified.") | ||
.takes_value(true); | ||
|
||
let migration_subcommand = SubCommand::with_name("migration") | ||
.setting(AppSettings::VersionlessSubcommands) | ||
.subcommand( | ||
SubCommand::with_name("run") | ||
.about("Runs all pending migrations") | ||
.arg(database_arg()) | ||
).subcommand( | ||
SubCommand::with_name("revert") | ||
.about("Reverts the latest run migration") | ||
.arg(database_arg()) | ||
).subcommand( | ||
SubCommand::with_name("redo") | ||
.about("Reverts and re-runs the latest migration. Useful \ | ||
for testing that a migration can in fact be reverted.") | ||
.arg(database_arg()) | ||
).subcommand( | ||
SubCommand::with_name("generate") | ||
.about("Generate a new migration with the given name, and \ | ||
the current timestamp as the version") | ||
.arg(Arg::with_name("MIGRATION_NAME") | ||
.help("The name of the migration to create") | ||
.required(true) | ||
) | ||
).setting(AppSettings::SubcommandRequiredElseHelp); | ||
|
||
let matches = App::new("diesel") | ||
.version(env!("CARGO_PKG_VERSION")) | ||
.setting(AppSettings::VersionlessSubcommands) | ||
.subcommand(migration_subcommand) | ||
.setting(AppSettings::SubcommandRequiredElseHelp) | ||
.get_matches(); | ||
|
||
match matches.subcommand() { | ||
("migration", Some(matches)) => run_migration_command(matches), | ||
_ => unreachable!(), | ||
} | ||
} | ||
|
||
// FIXME: We can improve the error handling instead of `unwrap` here. | ||
fn run_migration_command(matches: &ArgMatches) { | ||
match matches.subcommand() { | ||
("run", Some(args)) => { | ||
migrations::run_pending_migrations(&connection(&database_url(args))) | ||
.unwrap(); | ||
} | ||
("revert", Some(args)) => { | ||
migrations::revert_latest_migration(&connection(&database_url(args))) | ||
.unwrap(); | ||
} | ||
("redo", Some(args)) => { | ||
let connection = connection(&database_url(args)); | ||
connection.transaction(|| { | ||
let reverted_version = try!(migrations::revert_latest_migration(&connection)); | ||
migrations::run_migration_with_version(&connection, &reverted_version) | ||
}).unwrap(); | ||
} | ||
("generate", Some(args)) => { | ||
panic!("Migration generator is not implemented this pass") | ||
} | ||
_ => unreachable!("The cli parser should prevent reaching here"), | ||
} | ||
} | ||
|
||
fn database_url(matches: &ArgMatches) -> String { | ||
matches.value_of("DATABASE_URL") | ||
.map(|s| s.into()) | ||
.or(env::var("DATABASE_URL").ok()) | ||
.expect("The --database-url argument must be passed, \ | ||
or the DATABASE_URL environment variable must be set.") | ||
} | ||
|
||
fn connection(database_url: &str) -> diesel::Connection { | ||
diesel::Connection::establish(database_url) | ||
.expect(&format!("Error connecting to {}", database_url)) | ||
} |