Attribute world commits to conversations and restore temporary directories
This commit is contained in:
@@ -34,7 +34,7 @@ export function lander(host:any,workspaces:any) {
|
||||
const remote=host.settings().lander?.push
|
||||
if(remote)await run(path,'fetch',remote,entry.target)
|
||||
if(remote)await run(path,'merge','--no-edit','FETCH_HEAD')
|
||||
await run(path,'merge','--no-ff','--no-edit','-m',`Merge from #${entry.conversationId} into ${entry.target}`,entry.tip)
|
||||
await run(path,'merge','--no-ff','--no-edit','-m',`Merge from #${entry.conversationId} into ${entry.target}\n\nSausage-Conversation: ${entry.conversationId}`,entry.tip)
|
||||
const sha=await run(path,'rev-parse','HEAD')
|
||||
for(const configured of host.settings().lander?.checks??[]) {
|
||||
const check=typeof configured==='string'?{command:configured}:configured
|
||||
|
||||
@@ -349,6 +349,15 @@ impl Runtime {
|
||||
.env
|
||||
.push(("PATH".into(), "/usr/local/bin:/bin".into()));
|
||||
}
|
||||
// Keep process attribution aligned with the nearest conversation, including children.
|
||||
if let Some(conversation) = conversations.first() {
|
||||
request
|
||||
.env
|
||||
.retain(|(name, _)| name != "SAUSAGE_CONVERSATION");
|
||||
request
|
||||
.env
|
||||
.push(("SAUSAGE_CONVERSATION".into(), conversation.clone()));
|
||||
}
|
||||
request.cwd = sausage_vfs::resolve("/", &request.cwd);
|
||||
request.env.retain(|(name, _)| name != "PWD");
|
||||
request.env.push(("PWD".into(), request.cwd.clone()));
|
||||
|
||||
@@ -42,6 +42,7 @@ impl World {
|
||||
}
|
||||
|
||||
/// Copy an optional bundle into a new world. Existing worlds retain their programs.
|
||||
/// Ensure `/tmp`, `/run`, and `/var/tmp` exist on every startup.
|
||||
/// Each `.wasm` tool keeps its independent file and gains a relative `/bin/<name>` symlink.
|
||||
/// `/dev` exposes generated null, zero, and random byte devices in every world.
|
||||
/// Missing `/etc/gitconfig` defaults to Sausage <sausage@localhost>. Local repository config overrides it.
|
||||
@@ -50,6 +51,9 @@ impl World {
|
||||
let host_dir = dir.join("host");
|
||||
let fresh = !root_dir.exists();
|
||||
std::fs::create_dir_all(root_dir.join("usr/local/bin"))?;
|
||||
for path in ["tmp", "run", "var/tmp"] {
|
||||
std::fs::create_dir_all(root_dir.join(path))?;
|
||||
}
|
||||
if fresh && let Some(bundle) = userland {
|
||||
for folder in ["bin", "src"] {
|
||||
copy_tree(&bundle.join(folder), &root_dir.join(folder))?;
|
||||
|
||||
@@ -125,6 +125,37 @@ async fn run(fixture: &Fixture, script: &str) -> String {
|
||||
assert_eq!(code, 0, "{}", String::from_utf8_lossy(&output));
|
||||
String::from_utf8(output).unwrap()
|
||||
}
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn conversation_bash_commit_inherits_nearest_attribution() {
|
||||
let fixture = fixture();
|
||||
let mut options = options();
|
||||
options.conversations = vec!["158".into(), "79".into()];
|
||||
options
|
||||
.env
|
||||
.push(("SAUSAGE_CONVERSATION".into(), "wrong".into()));
|
||||
let mut output = Vec::new();
|
||||
let code = fixture
|
||||
.runtime
|
||||
.exec(
|
||||
Command::Shell("sh -c 'git commit --allow-empty --no-gpg-sign -m attributed'".into()),
|
||||
options,
|
||||
&mut |_, bytes| output.extend_from_slice(bytes),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(code, 0, "{}", String::from_utf8_lossy(&output));
|
||||
let commit = std::process::Command::new("git")
|
||||
.args(["log", "worker", "-1", "--format=%B"])
|
||||
.current_dir(fixture._temp.path().join("world/src/demo"))
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(commit.status.success());
|
||||
let text = String::from_utf8(commit.stdout).unwrap();
|
||||
assert!(text.contains("Sausage-Conversation: 158"), "{text}");
|
||||
assert!(!text.contains("Sausage-Conversation: 79"), "{text}");
|
||||
assert!(!text.contains("Sausage-Conversation: wrong"), "{text}");
|
||||
}
|
||||
|
||||
async fn edit(fixture: &Fixture) {
|
||||
let file = fixture
|
||||
.root
|
||||
|
||||
@@ -57,6 +57,24 @@ fn world_copies_programs_and_nested_source_only_once() -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn world_restores_temporary_directories_on_startup() -> anyhow::Result<()> {
|
||||
let temp = tempfile::tempdir()?;
|
||||
for restart in [false, true] {
|
||||
let world = World::open(temp.path())?;
|
||||
for path in ["tmp", "run", "var/tmp"] {
|
||||
let directory = temp.path().join("world").join(path);
|
||||
assert!(
|
||||
directory.is_dir(),
|
||||
"missing /{path} after restart={restart}"
|
||||
);
|
||||
std::fs::remove_dir(&directory)?;
|
||||
}
|
||||
drop(world);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
mod features {
|
||||
//! Acceptance coverage for preview1, identity, inherited IO, services, HTTP, and bundle aliases.
|
||||
use super::*;
|
||||
|
||||
@@ -207,7 +207,29 @@ async fn coordination() -> anyhow::Result<()> {
|
||||
World::open(dir.path())?;
|
||||
prepare(dir.path())?;
|
||||
let _server = serve(dir.path())?;
|
||||
run(dir.path(), "normal").await
|
||||
run(dir.path(), "normal").await?;
|
||||
let output = Command::new("git")
|
||||
.args(["log", "-1", "--format=%s%n%B"])
|
||||
.current_dir(dir.path().join("world/project"))
|
||||
.output()?;
|
||||
assert!(output.status.success());
|
||||
let message = String::from_utf8(output.stdout)?;
|
||||
let submitter = message
|
||||
.lines()
|
||||
.next()
|
||||
.unwrap()
|
||||
.strip_prefix("Merge from #")
|
||||
.unwrap()
|
||||
.split_whitespace()
|
||||
.next()
|
||||
.unwrap();
|
||||
assert!(
|
||||
message
|
||||
.lines()
|
||||
.any(|line| line == format!("Sausage-Conversation: {submitter}")),
|
||||
"{message}"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn restart_child() -> anyhow::Result<()> {
|
||||
|
||||
Reference in New Issue
Block a user