use std::fs;
use crate::deploy::test_support::{
FLOCKFILE, Fixture, Shepherd, commit_on_origin, fixture_with_previous_release,
};
use crate::deploy::{Outcome, deploy};
use crate::error::Error;
use crate::flockfile::OVERRIDE;
use crate::hook::Ending;
use crate::{fixtures, swap};
fn operators_override(fixture: &Fixture, text: &str) {
fs::write(
fixture.origin.path().join(".gitignore"),
format!("{OVERRIDE}\n"),
)
.expect("write .gitignore");
fs::write(fixture.origin.path().join(OVERRIDE), text).expect("write override");
}
#[tokio::test]
async fn a_release_hook_runs_after_the_build_and_before_the_swap() {
let mut fixture = fixture_with_previous_release();
let previous = fixture.state.deployed.clone().expect("a previous release");
fs::write(
fixture.origin.path().join("Flockfile.toml"),
format!("{FLOCKFILE}\n[dog.deploy.build]\ncommand = 'touch built'\n"),
)
.expect("write Flockfile");
operators_override(
&fixture,
"[dog.deploy.release]\ncommand = 'readlink ../../current > current-at-hook.txt; \
ls built > build-seen-by-hook.txt'\n",
);
let second = commit_on_origin(&fixture, "second.txt");
let outcome = deploy(
&Shepherd::ready(),
&fixture.tree,
&mut fixture.state,
&fixtures::dog_config(),
)
.await
.expect("completes");
assert_eq!(
outcome,
Outcome::Deployed {
sha: second.clone()
}
);
let release = fixture.tree.release(&second);
let at_hook = fs::read_to_string(release.join("current-at-hook.txt")).expect("hook ran");
assert_eq!(
std::path::Path::new(at_hook.trim()),
fixture.tree.release(&previous)
);
assert!(
release.join("build-seen-by-hook.txt").is_file(),
"hook ran before the build"
);
assert_eq!(
swap::resolve(&fixture.tree.current()).unwrap(),
Some(release),
"and the deploy went on to swap"
);
}
#[tokio::test]
async fn a_failing_release_hook_never_moves_current_and_holds_the_sha() {
let mut fixture = fixture_with_previous_release();
let previous = fixture.state.deployed.clone().expect("a previous release");
operators_override(&fixture, "[dog.deploy.release]\ncommand = 'exit 4'\n");
let second = commit_on_origin(&fixture, "second.txt");
let daemon = Shepherd::ready();
let err = deploy(
&daemon,
&fixture.tree,
&mut fixture.state,
&fixtures::dog_config(),
)
.await
.expect_err("the hook fails");
assert!(
matches!(
err,
Error::Hook {
name: "release",
ending: Ending::Exited(Some(4))
}
),
"{err:?}"
);
assert_eq!(
swap::resolve(&fixture.tree.current()).unwrap(),
Some(fixture.tree.release(&previous))
);
assert_eq!(daemon.reload_count(), 0, "nothing may be reloaded");
assert_eq!(fixture.state.deployed.as_deref(), Some(previous.as_str()));
assert_eq!(fixture.state.failed.as_deref(), Some(second.as_str()));
}
#[tokio::test]
async fn a_failing_build_runs_no_release_hook() {
let mut fixture = fixture_with_previous_release();
fs::write(
fixture.origin.path().join("Flockfile.toml"),
format!("{FLOCKFILE}\n[dog.deploy.build]\ncommand = 'exit 3'\n"),
)
.expect("write Flockfile");
operators_override(
&fixture,
"[dog.deploy.release]\ncommand = 'touch hook-ran'\n",
);
let second = commit_on_origin(&fixture, "second.txt");
let err = deploy(
&Shepherd::ready(),
&fixture.tree,
&mut fixture.state,
&fixtures::dog_config(),
)
.await
.expect_err("the build fails");
assert!(matches!(err, Error::Build { status: Some(3) }), "{err:?}");
assert!(
!fixture.tree.release(&second).join("hook-ran").exists(),
"the hook ran after a failed build"
);
}