Skip to content

Commit

Permalink
Hooks (#1449)
Browse files Browse the repository at this point in the history
  • Loading branch information
aljazerzen authored Jan 31, 2025
1 parent 16ab350 commit 78a4441
Show file tree
Hide file tree
Showing 33 changed files with 547 additions and 177 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ jobs:
- run: |
cargo test --workspace --test=${{ matrix.test }} --features portable_tests
env:
RUST_TEST_THREADS: '1'
portable-tests-windows:
needs: musl-test
Expand Down
2 changes: 1 addition & 1 deletion src/branch/merge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub async fn main(
None => anyhow::bail!("The branch '{}' doesn't exist", cmd.target_branch),
};

let migration_context = migrations::Context::for_project(&project)?;
let migration_context = migrations::Context::for_project(project)?;
let mut merge_migrations =
get_merge_migrations(source_connection, &mut target_connection).await?;

Expand Down
4 changes: 2 additions & 2 deletions src/branch/rebase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub async fn main(
&temp_branch,
source_connection,
&mut temp_branch_connection,
&project,
project,
cli_opts,
!options.no_apply,
)
Expand Down Expand Up @@ -84,7 +84,7 @@ async fn rebase(
branch: &str,
source_connection: &mut Connection,
target_connection: &mut Connection,
project: &project::Context,
project: project::Context,
cli_opts: &Options,
apply_migrations: bool,
) -> anyhow::Result<()> {
Expand Down
15 changes: 12 additions & 3 deletions src/branch/switch.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::branch;
use crate::branch::connections::connect_if_branch_exists;
use crate::branch::context::Context;
use crate::branch::create::create_branch;
use crate::connect::Connector;
use crate::{branch, hooks, print};

pub async fn run(
options: &Command,
Expand All @@ -15,6 +15,10 @@ pub async fn run(
anyhow::bail!("");
}

if let Some(project) = &context.get_project().await? {
hooks::on_action("branch.switch.before", project).await?;
}

let current_branch = if let Some(mut connection) = connect_if_branch_exists(connector).await? {
let current_branch = context.get_current_branch(&mut connection).await?;
if current_branch == options.target_branch {
Expand Down Expand Up @@ -60,15 +64,20 @@ pub async fn run(
}
};

eprintln!(
print::msg!(
"Switching from '{}' to '{}'",
current_branch, options.target_branch
current_branch,
options.target_branch
);

context
.update_current_branch(&options.target_branch)
.await?;

if let Some(project) = &context.get_project().await? {
hooks::on_action("branch.switch.after", project).await?;
}

Ok(branch::CommandResult {
new_branch: Some(options.target_branch.clone()),
})
Expand Down
20 changes: 17 additions & 3 deletions src/branch/wipe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ use crate::branch::context::Context;
use crate::commands::ExitCode;
use crate::connect::Connector;
use crate::portable::exit_codes;
use crate::{print, question};
use crate::{hooks, print, question};

pub async fn main(
cmd: &Command,
_context: &Context,
context: &Context,
connector: &mut Connector,
) -> anyhow::Result<()> {
let connection = connect_if_branch_exists(connector.branch(&cmd.target_branch)?).await?;
Expand All @@ -30,10 +30,24 @@ pub async fn main(
}
}

let (status, _warnings) = connection.execute("RESET SCHEMA TO initial", &()).await?;
do_wipe(&mut connection, context).await?;
Ok(())
}

pub async fn do_wipe(
connection: &mut crate::connect::Connection,
context: &Context,
) -> Result<(), anyhow::Error> {
if let Some(project) = context.get_project().await? {
hooks::on_action("branch.wipe.before", &project).await?;
}

let (status, _warnings) = connection.execute("RESET SCHEMA TO initial", &()).await?;
print::completion(status);

if let Some(project) = context.get_project().await? {
hooks::on_action("branch.wipe.after", &project).await?;
}
Ok(())
}

Expand Down
2 changes: 1 addition & 1 deletion src/cli/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ fn try_project_init(new_layout: bool) -> anyhow::Result<InitResult> {
server_start_conf: None,
cloud_opts: options.clone(),
};
project::init::init_existing(&init, &project, &options)?;
project::init::init_existing(&init, project, &options)?;
Ok(Initialized)
} else {
Ok(NotAProject)
Expand Down
24 changes: 13 additions & 11 deletions src/commands/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,33 +58,35 @@ pub async fn drop(
}

pub async fn wipe(
cli: &mut Connection,
options: &WipeDatabase,
_: &Options,
connection: &mut Connection,
cmd: &WipeDatabase,
options: &Options,
) -> Result<(), anyhow::Error> {
if cli.get_version().await?.specific().major >= 5 {
eprintln!("'database wipe' is deprecated in {BRANDING} 5+. Please use 'branch wipe'");
let context = crate::branch::context::Context::new(options).await?;

if connection.get_version().await?.specific().major >= 5 {
print::warn!("'database wipe' is deprecated in {BRANDING} 5+. Please use 'branch wipe'");
}

if cli.get_version().await?.specific() < "3.0-alpha.2".parse().unwrap() {
if connection.get_version().await?.specific() < "3.0-alpha.2".parse().unwrap() {
return Err(anyhow::anyhow!(
"The `database wipe` command is only \
supported in {BRANDING} >= 3.0"
))
.hint("Use `database drop`, `database create`")?;
}
if !options.non_interactive {
if !cmd.non_interactive {
let q = question::Confirm::new_dangerous(format!(
"Do you really want to wipe \
the contents of the database {:?}?",
cli.database()
connection.database()
));
if !cli.ping_while(q.async_ask()).await? {
if !connection.ping_while(q.async_ask()).await? {
print::error!("Canceled.");
return Err(ExitCode::new(exit_codes::NOT_CONFIRMED).into());
}
}
let (status, _warnings) = cli.execute("RESET SCHEMA TO initial", &()).await?;
print::completion(&status);

crate::branch::wipe::do_wipe(connection, &context).await?;
Ok(())
}
65 changes: 65 additions & 0 deletions src/hooks/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use crate::portable::{project, windows};
use crate::print::{self, Highlight};

#[tokio::main(flavor = "current_thread")]
pub async fn on_action_sync(
action: &'static str,
project: &project::Context,
) -> anyhow::Result<()> {
on_action(action, project).await
}

pub async fn on_action(action: &'static str, project: &project::Context) -> anyhow::Result<()> {
let Some(hook) = get_hook(action, &project.manifest) else {
return Ok(());
};

print::msg!("{}", format!("hook {action}: {hook}").muted());

// run
let status = if !cfg!(windows) {
std::process::Command::new("/bin/sh")
.arg("-c")
.arg(hook)
.current_dir(&project.location.root)
.status()?
} else {
let wsl = windows::try_get_wsl()?;
wsl.sh(&project.location.root)
.arg("-c")
.arg(hook)
.run_for_status()
.await?
};

// abort on error
if !status.success() {
return Err(anyhow::anyhow!(
"Hook {action} exited with status {status}."
));
}
Ok(())
}

fn get_hook<'m>(
action: &'static str,
manifest: &'m project::manifest::Manifest,
) -> Option<&'m str> {
let hooks = manifest.hooks.as_ref()?;
let hook = match action {
"project.init.before" => &hooks.project.as_ref()?.init.as_ref()?.before,
"project.init.after" => &hooks.project.as_ref()?.init.as_ref()?.after,
"branch.switch.before" => &hooks.branch.as_ref()?.switch.as_ref()?.before,
"branch.switch.after" => &hooks.branch.as_ref()?.switch.as_ref()?.after,
"branch.wipe.before" => &hooks.branch.as_ref()?.wipe.as_ref()?.before,
"branch.wipe.after" => &hooks.branch.as_ref()?.wipe.as_ref()?.after,
"migration.apply.before" => &hooks.migration.as_ref()?.apply.as_ref()?.before,
"migration.apply.after" => &hooks.migration.as_ref()?.apply.as_ref()?.after,
"migration.rebase.before" => &hooks.migration.as_ref()?.rebase.as_ref()?.before,
"migration.rebase.after" => &hooks.migration.as_ref()?.rebase.as_ref()?.after,
"migration.merge.before" => &hooks.migration.as_ref()?.merge.as_ref()?.before,
"migration.merge.after" => &hooks.migration.as_ref()?.merge.as_ref()?.after,
_ => panic!("unknown action"),
};
hook.as_deref()
}
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ mod error_display;
mod format;
mod highlight;
mod hint;
mod hooks;
mod interactive;
mod interrupt;
mod log_levels;
Expand Down
35 changes: 20 additions & 15 deletions src/migrations/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,48 @@ use std::path::PathBuf;
use crate::migrations::options::MigrationConfig;
use crate::portable::project;

use gel_tokio::get_project_path;

pub struct Context {
pub schema_dir: PathBuf,

pub quiet: bool,

pub project: Option<project::Context>,
}

impl Context {
pub async fn from_project_or_config(
pub async fn for_migration_config(
cfg: &MigrationConfig,
quiet: bool,
) -> anyhow::Result<Context> {
let project = project::load_ctx(None).await?;

let schema_dir = if let Some(schema_dir) = &cfg.schema_dir {
schema_dir.clone()
} else if let Some(manifest_path) = get_project_path(None, true).await? {
let config = project::manifest::read(&manifest_path)?;
config
.project()
.resolve_schema_dir(manifest_path.parent().unwrap())?
} else if let Some(project) = &project {
project.resolve_schema_dir()?
} else {
let default_dir: PathBuf = "./dbschema".into();
if !default_dir.exists() {
anyhow::bail!("`dbschema` directory doesn't exist. Either create one or provide path via --schema-dir.");
anyhow::bail!("`dbschema` directory doesn't exist. Either create one, init a project or provide its path via --schema-dir.");
}
default_dir
};

Ok(Context { schema_dir, quiet })
Ok(Context {
schema_dir,
quiet,
project,
})
}
pub fn for_project(project: &project::Context) -> anyhow::Result<Context> {
pub fn for_project(project: project::Context) -> anyhow::Result<Context> {
let schema_dir = project
.manifest
.project()
.resolve_schema_dir(&project.location.root)?;
Ok(Context {
schema_dir: project
.manifest
.project()
.resolve_schema_dir(&project.location.root)?,
schema_dir,
quiet: false,
project: Some(project),
})
}
}
3 changes: 2 additions & 1 deletion src/migrations/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -820,7 +820,7 @@ async fn _create(
options: &Options,
create: &CreateMigration,
) -> anyhow::Result<()> {
let ctx = Context::from_project_or_config(&create.cfg, false).await?;
let ctx = Context::for_migration_config(&create.cfg, false).await?;

if dev_mode::check_client(cli).await? {
let dev_num = query_row::<i64>(
Expand Down Expand Up @@ -1030,6 +1030,7 @@ async fn start_migration() {
let ctx = Context {
schema_dir,
quiet: false,
project: None,
};

let res = gen_start_migration(&ctx).await.unwrap();
Expand Down
4 changes: 2 additions & 2 deletions src/migrations/edit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ pub async fn edit_no_check(
_common: &Options,
options: &MigrationEdit,
) -> Result<(), anyhow::Error> {
let ctx = Context::from_project_or_config(&options.cfg, false).await?;
let ctx = Context::for_migration_config(&options.cfg, false).await?;
// TODO(tailhook) do we have to make the full check of whether there are no
// gaps and parent revisions are okay?
let (_n, path) = read_names(&ctx)
Expand Down Expand Up @@ -139,7 +139,7 @@ async fn _edit(
_common: &Options,
options: &MigrationEdit,
) -> anyhow::Result<()> {
let ctx = Context::from_project_or_config(&options.cfg, false).await?;
let ctx = Context::for_migration_config(&options.cfg, false).await?;
// TODO(tailhook) do we have to make the full check of whether there are no
// gaps and parent revisions are okay?
let (n, path) = cli
Expand Down
3 changes: 2 additions & 1 deletion src/migrations/extract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ pub async fn extract(
_opts: &Options,
params: &ExtractMigrations,
) -> anyhow::Result<()> {
let src_ctx = Context::from_project_or_config(&params.cfg, params.non_interactive).await?;
let src_ctx = Context::for_migration_config(&params.cfg, params.non_interactive).await?;
let current = migration::read_all(&src_ctx, false).await?;
let mut disk_iter = current.into_iter();

Expand All @@ -56,6 +56,7 @@ pub async fn extract(
let temp_ctx = Context {
schema_dir: temp_dir.path().to_path_buf(),
quiet: false,
project: None,
};
let mut to_delete = Vec::new();

Expand Down
Loading

0 comments on commit 78a4441

Please sign in to comment.