a deploy that changes only the renderer runs it on the active generation
Signed-off-by: dawn <90008@klbr.net>
211 unmodified linesExpand all21221321421521621721821922022122222322422522622722822923023123223323423595 unmodified lines211 unmodified linesExpand all match git_xrpc::prepare(&config.output_root) { Ok(written) => eprintln!("prepared the git read maps of {written} repos"), Err(error) => tracing::error!("prepare the git read maps: {error:#}"), } let before = publish::current_generation(&config.output_root); if let Err(error) = writes::publish_scoped(&config, None).await { tracing::error!("rebuild with the new binary: {error:#}"); } // an activation starts the renderer itself, a new one alone needs a run of its own let current = publish::current_generation(&config.output_root); if let Some(current) = current.filter(|current| before.as_ref() == Some(current)) && generate::site_hook::changed(&config) { let at = publish::unix_now().to_string(); if let Err(error) = git_ssh::spawn_detached(&config, ["after-activation", ¤t, "--at", &at]) { tracing::error!("start the new site hook: {error:#}"); } } Ok(()) } Command::Crawl => { crawl::request_all(&config).await;95 unmodified lines282 unmodified linesExpand all283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340282 unmodified linesExpand all } } panic!("no hosted copy of {did}")}
#[test]fn a_deploy_that_changes_only_the_renderer_runs_it_once() { let root = tempfile::tempdir().unwrap(); let records = root.path().join("records"); std::fs::create_dir_all(&records).unwrap(); std::fs::create_dir_all(root.path().join("state")).unwrap(); let config = write_config(root.path(), &records); knot_static(&config, &["init"]); let source = root.path().join("source"); std::fs::create_dir_all(&source).unwrap(); git(&source, ["init", "-q", "-b", "main"]); commit(&source, "README.md", "# hook\n", "initial"); common::knot_static_raw( &config, MASTER_KEY_ENV, &MASTER_KEY, [ "repo", "import", "--owner", OWNER, "--rkey", "hooked", "--name", "hooked", "--default-branch", "main", source.to_str().unwrap(), ], &[], ); let id = generate(&config).expect("a first generation"); knot_static(&config, &["activate", &id]); wait_for(&records.join("0-hook.json"));
knot_static(&config, &["prepare"]); std::thread::sleep(Duration::from_secs(2)); assert_eq!(calls(&records), 1, "the same renderer isn't run again");
let text = std::fs::read_to_string(&config).unwrap(); std::fs::write(&config, text.replace("\"hook\"]", "\"renderer-v2\"]")).unwrap(); knot_static(&config, &["prepare"]); let again = wait_for(&records.join("1-hook.json")); assert_eq!( again["generation"], id.as_str(), "the active generation, not a new one" );
knot_static(&config, &["prepare"]); std::thread::sleep(Duration::from_secs(2)); assert_eq!(calls(&records), 2, "once");}15 unmodified lines16171819202122232425330 unmodified linesExpand all35635735835936036136236336436536636736836937037137237337437537637737837938038138238338438516 unmodified lines15 unmodified linespub(crate) const HOOK_FILE: &str = "hook.json";pub(crate) const DIFF_FILE: &str = "xrpc-diff.jsonl";const LOCK_FILE: &str = "runtime/site-hook.lock";const PID_FILE: &str = "runtime/site-hook.pid";/// the command and args the last renderer was started withconst STARTED_FILE: &str = "runtime/site-hook.started.json";const LOG_FILE: &str = "site-hook.log";const SUPERSEDE_WAIT: Duration = Duration::from_secs(2);
pub(crate) struct Activation<'a> {330 unmodified linesExpand all .spawn() .with_context(|| format!("start {}", hook.command.display()))?; crate::cas::atomic_write(&root.join(PID_FILE), child.id().to_string().as_bytes())?; std::thread::spawn(move || child.wait()); crate::cas::atomic_write( &root.join(STARTED_FILE), &serde_json::to_vec(&started(hook))?, )?; Ok(())}
fn started(hook: &SiteHook) -> Value { json!({ "command": hook.command, "args": hook.args })}
/// the configured renderer isn't the one the last hook run started, a deploy changed itpub(crate) fn changed(config: &ValidatedConfig) -> bool { let Some(hook) = &config.activate.site_hook else { return false; }; let last = std::fs::read(config.output_root.join(STARTED_FILE)) .ok() .and_then(|bytes| serde_json::from_slice::<Value>(&bytes).ok()); last.as_ref() != Some(&started(hook))}
fn supersede(root: &Path, lock: &std::fs::File) -> anyhow::Result<()> { let group = std::fs::read_to_string(root.join(PID_FILE)) .ok() .and_then(|pid| pid.trim().parse::<libc::pid_t>().ok())16 unmodified lines