Merge pull request #2636 from storytold/fix/2479-confined-queue-default

Automation: a confined renderQueue.add without output needs a saved project (#2479)
This commit is contained in:
BFlat authored and GitHub committed 2026-10-30 10:19:01 +07:00
commit ab5162ed95
4 files changed
+24 -2

No files matched your search

+1 -1
View File
@@ -64,7 +64,7 @@ impl Backend {
let params = if params.is_null() { json!({}) } else { params };
if let Some(c) = self.confinement.clone() {
let lookup = match c.check_command(id, &params)? {
Some(crate::confine::Lookup::ProjectFile) => Some((crate::confine::Lookup::ProjectFile, self.run("project.summary", json!({}))?)),
Some(l @ (crate::confine::Lookup::ProjectFile | crate::confine::Lookup::QueueDefault)) => Some((l, self.run("project.summary", json!({}))?)),
Some(crate::confine::Lookup::RenderOutputs) => Some((crate::confine::Lookup::RenderOutputs, self.run("renderQueue.list", json!({}))?)),
None => None,
};
+14
View File
@@ -128,6 +128,9 @@ pub enum Lookup {
ProjectFile,
/// Every output of the queued Render Queue items (`renderQueue.list`).
RenderOutputs,
/// Whether the open project has a file (`project.summary` → `path`): a Render Queue item
/// added without `output` writes beside it, or to the home folder before the first save.
QueueDefault,
}
impl Confinement {
@@ -239,6 +242,10 @@ impl Confinement {
if matches!(id, "file.openRecent" | "file.importRecent" | "anim.applyRecentPreset") && !given {
return Err(Error::BadArgs(format!("`{id}`: give `path` while file access is confined")));
}
// Without `output`, an unsaved project's item would write to the home folder (#2479).
if id == "renderQueue.add" && !given {
return Ok(Some(Lookup::QueueDefault));
}
if IMPLICIT_WRITES.contains(&id) && !(given && id == "file.save") {
return Ok(Some(if id == "file.save" || id == "file.incrementAndSave" { Lookup::ProjectFile } else { Lookup::RenderOutputs }));
}
@@ -253,6 +260,13 @@ impl Confinement {
Some(p) => self.check(p, Access::Write).map(drop),
None => Err(Error::BadArgs("the project has no file yet: give `path` (Save As) inside an automation write root".into())),
},
// A saved project's default output sits beside it, and rendering checks it then.
Lookup::QueueDefault => match found.get("path").and_then(Value::as_str) {
Some(_) => Ok(()),
None => Err(Error::BadArgs(
"`renderQueue.add`: the project has no file yet, so the default output would go to the home folder: give `output` inside an automation write root".into(),
)),
},
Lookup::RenderOutputs => {
let items = found.get("items").or(Some(found)).and_then(Value::as_array).cloned().unwrap_or_default();
for it in items.iter().filter(|it| it.get("render").and_then(Value::as_bool) != Some(false)) {
+6
View File
@@ -1416,6 +1416,12 @@ fn automation_roots_confine_file_access() {
assert!(err, "{name} {args} should be refused: {c:?}");
c[0]["text"].as_str().unwrap_or_default().to_string()
};
// #2479: an unsaved project's queue item without `output` would write to the home folder.
call_json(&mut s, "execute_command", json!({"command": "comp.new", "params": {"name": "Unsaved", "width": 64, "height": 64}}));
let msg = denied(&mut s, "execute_command", json!({"command": "renderQueue.add", "params": {"comp": "Unsaved"}}));
assert!(msg.contains("home folder") && msg.contains("give `output`"), "{msg}");
call_json(&mut s, "execute_command", json!({"command": "renderQueue.add", "params": {"comp": "Unsaved", "output": p(&write, "unsaved.mp4")}}));
call_json(&mut s, "execute_command", json!({"command": "renderQueue.remove"}));
// Reading: inside a read root, not outside.
call_json(&mut s, "open_project", json!({"path": p(&read, "source.ecproj")}));
let msg = denied(&mut s, "open_project", json!({"path": p(&outside, "free.ecproj")}));
+3 -1
View File
@@ -11,7 +11,9 @@ written only under a write root; with no write root nothing is written. Every en
(`execute_command`, `command_run`, each `batch` step) is checked before it runs: its file
parameters must be absolute paths inside the roots (symbolic links resolved, no `..`), and so must
any other absolute path in its parameters. Save, Increment and Save and rendering the Render
Queue are checked against the files they write (the open project's, the queued outputs). Scripts
Queue are checked against the files they write (the open project's, the queued outputs);
`renderQueue.add` without `output` needs a saved project, since an unsaved one's default output
would go to the home folder (#2479). Scripts
(`run_script`, `file.runScript`…), Execute File, Reveal in Finder, `$N` references to file paths
in a batch, and the bridge's UI-driving tools (`ui_click`, `ui_key`, `ui_type`, `ui_drag`,
`ui_set`, `script_ui`, `control`) are refused while confined. Without the flags nothing changes.