use std::path::PathBuf;
use crate::config::DogConfig;
use crate::daemon::Daemon;
use crate::deploy::reload::{Landed, land, named};
use crate::deploy::rollback::{roll_back, undo_swap};
use crate::deploy::{Outcome, Staged, record_landing};
use crate::error::Error;
use crate::paths::Tree;
use crate::state::State;
use crate::{hook, swap};
pub(crate) async fn run_release_hook(
tree: &Tree,
head: &str,
staged: &Staged,
config: &DogConfig,
) -> Result<(), Error> {
hook::run(
&staged.hooks.release,
&hook::Context {
sheep: tree.sheep(),
sha: head,
release: &tree.release(head),
as_user: staged.app.user.as_deref(),
passthrough: &config.passthrough,
budget: config.build_timeout,
cache: &tree.cache_target(),
built: staged.spec.command.is_some(),
},
)
.await
}
pub(crate) async fn swap_and_land<D: Daemon>(
daemon: &D,
tree: &Tree,
state: &mut State,
head: &str,
started_at: Option<PathBuf>,
staged: &Staged,
config: &DogConfig,
) -> Result<Outcome, Error> {
let sheep = tree.sheep();
let release = tree.release(head);
let Staged {
app, spec, hooks, ..
} = staged;
let previous = swap::resolve(&tree.current())?;
if previous != started_at {
return Err(Error::Raced {
sheep: sheep.to_owned(),
started: named(started_at.as_deref()),
found: named(previous.as_deref()),
});
}
swap::point_at(&tree.current(), &release)?;
match land(daemon, sheep, app, state.verify).await {
Landed::Verified => {
match record_landing(tree, state, head) {
Ok(()) => {
if let Err(err) = hook::run_after(
&hooks.after,
&hook::Context {
sheep,
sha: head,
release: &release,
as_user: app.user.as_deref(),
passthrough: &config.passthrough,
budget: config.build_timeout,
cache: &tree.cache_target(),
built: spec.command.is_some(),
},
)
.await
{
eprintln!(
"shep-deploy: {sheep}: deployed {head}, but the after hook failed: \
{err}"
);
}
}
Err(err) => {
eprintln!(
"shep-deploy: {sheep}: deployed {head}, but could not record it: {err}"
);
if hooks.after.command.is_some() {
eprintln!(
"shep-deploy: {sheep}: the after hook was skipped, because the record \
does not name {head}"
);
}
}
}
Ok(Outcome::Deployed {
sha: head.to_owned(),
})
}
Landed::NotStarted(source) => {
undo_swap(tree, state, previous.as_deref(), head, &source)?;
Err(source)
}
Landed::NotVerified { why, patience } => {
let to = roll_back(
daemon,
tree,
state,
app,
previous.as_deref(),
head,
&why,
patience,
)
.await?;
Ok(Outcome::RolledBack { to, why })
}
Landed::Failed { source, patience } => {
let to = roll_back(
daemon,
tree,
state,
app,
previous.as_deref(),
head,
&source.to_string(),
patience,
)
.await?;
Err(Error::RolledBack {
to,
source: Box::new(source),
})
}
}
}