Attribute world commits to conversations and restore temporary directories

This commit is contained in:
2026-11-08 03:15:15 +00:00
parent de351919dd
commit 62283e7733
6 changed files with 86 additions and 2 deletions
+1 -1
View File
@@ -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
+9
View File
@@ -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()));
+4
View File
@@ -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))?;
+31
View File
@@ -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
+18
View File
@@ -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::*;
+23 -1
View File
@@ -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<()> {