mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-12 11:13:24 +00:00
Fix Flatpak updates and bump to 0.24.1 (#6649)
* fix(flatpak): allow release checks and self-update helpers * test: keep Flatpak permission parsing within strict Clippy rules * chore: bump version to 0.24.1 for the Flatpak update fix * test: locate CLI binaries after Nextest archive relocation * docs: explain the Flatpak update permission bootstrap
This commit is contained in:
Generated
+30
-30
@@ -6313,7 +6313,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vectorcraft"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
dependencies = [
|
||||
"arboard",
|
||||
"block2 0.6.2",
|
||||
@@ -6360,7 +6360,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vectorcraft-affinity"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"flate2",
|
||||
@@ -6371,14 +6371,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vectorcraft-apischema"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vectorcraft-brush"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
dependencies = [
|
||||
"kurbo 0.13.1",
|
||||
"serde",
|
||||
@@ -6390,7 +6390,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vectorcraft-cad"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
dependencies = [
|
||||
"image",
|
||||
"kurbo 0.13.1",
|
||||
@@ -6406,7 +6406,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vectorcraft-cli"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
dependencies = [
|
||||
"log",
|
||||
"objc2-core-foundation",
|
||||
@@ -6429,7 +6429,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vectorcraft-color"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
dependencies = [
|
||||
"kurbo 0.13.1",
|
||||
"moxcms",
|
||||
@@ -6441,7 +6441,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vectorcraft-doc"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"hayro-ccitt",
|
||||
@@ -6460,7 +6460,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vectorcraft-effects"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
dependencies = [
|
||||
"image",
|
||||
"kurbo 0.13.1",
|
||||
@@ -6478,7 +6478,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vectorcraft-engine"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
dependencies = [
|
||||
"cfb",
|
||||
"chrono",
|
||||
@@ -6531,7 +6531,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vectorcraft-eps"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
dependencies = [
|
||||
"craft-pdf",
|
||||
"encoding_rs",
|
||||
@@ -6559,7 +6559,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vectorcraft-format"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
dependencies = [
|
||||
"craft-vectorcraft-format",
|
||||
"flate2",
|
||||
@@ -6575,7 +6575,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vectorcraft-geom"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
dependencies = [
|
||||
"kurbo 0.13.1",
|
||||
"proptest",
|
||||
@@ -6587,7 +6587,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vectorcraft-js"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"getrandom 0.4.3",
|
||||
@@ -6597,7 +6597,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vectorcraft-mcp"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
dependencies = [
|
||||
"image",
|
||||
"log",
|
||||
@@ -6616,7 +6616,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vectorcraft-metafile"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
dependencies = [
|
||||
"image",
|
||||
"kurbo 0.13.1",
|
||||
@@ -6630,7 +6630,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vectorcraft-pathops"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
dependencies = [
|
||||
"kurbo 0.13.1",
|
||||
"linesweeper",
|
||||
@@ -6642,7 +6642,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vectorcraft-pdf"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"chrono",
|
||||
@@ -6676,7 +6676,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vectorcraft-plugins"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
dependencies = [
|
||||
"craft-vectorcraft-format",
|
||||
"image",
|
||||
@@ -6695,7 +6695,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vectorcraft-render"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"gif 0.14.2",
|
||||
@@ -6725,7 +6725,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vectorcraft-scriptcheck"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
dependencies = [
|
||||
"proptest",
|
||||
"unicode-properties",
|
||||
@@ -6733,7 +6733,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vectorcraft-svg"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
dependencies = [
|
||||
"craft-docpath",
|
||||
"flate2",
|
||||
@@ -6760,7 +6760,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vectorcraft-testkit"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
dependencies = [
|
||||
"fax",
|
||||
"image",
|
||||
@@ -6778,7 +6778,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vectorcraft-text"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
dependencies = [
|
||||
"harfrust 0.13.3",
|
||||
"icu_segmenter",
|
||||
@@ -6799,7 +6799,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vectorcraft-three-d"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
dependencies = [
|
||||
"vectorcraft-geom",
|
||||
"vectorcraft-pathops",
|
||||
@@ -6807,7 +6807,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vectorcraft-tools"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
dependencies = [
|
||||
"image",
|
||||
"kurbo 0.13.1",
|
||||
@@ -6824,7 +6824,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vectorcraft-trace"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
dependencies = [
|
||||
"image",
|
||||
"kurbo 0.13.1",
|
||||
@@ -6836,7 +6836,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vectorcraft-ui-egui"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"craft-keymap",
|
||||
@@ -6874,7 +6874,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vectorcraft-web"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
dependencies = [
|
||||
"craft-web-api",
|
||||
"eframe",
|
||||
@@ -8006,7 +8006,7 @@ checksum = "9440ea3e5aeabb0ac63af70daf835274065238cdd0cec83418f417eae38bacee"
|
||||
|
||||
[[package]]
|
||||
name = "xtask"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ members = ["crates/*", "apps/*", "xtask"]
|
||||
default-members = ["crates/*", "apps/vectorcraft", "apps/vectorcraft-cli", "xtask"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
edition = "2024"
|
||||
license = "MIT OR Apache-2.0"
|
||||
rust-version = "1.95"
|
||||
|
||||
@@ -3,8 +3,6 @@ use std::process::{Command, Stdio};
|
||||
|
||||
use serde_json::{Value, json};
|
||||
|
||||
const BIN: &str = env!("CARGO_BIN_EXE_vectorcraft-cli");
|
||||
|
||||
/// Returns the path of `name` in `vectorcraft-cli-test-<pid>` in the temp folder. The first call removes
|
||||
/// that folder and creates it again empty.
|
||||
fn tmp(name: &str) -> std::path::PathBuf {
|
||||
@@ -27,7 +25,12 @@ fn approval_param_needs_the_start_up_flag(key: &str, value: Value) {
|
||||
std::fs::write(&steps, json!([{"command": "plugin.install", "params": {"dataBase64": module, key: value}}]).to_string()).unwrap();
|
||||
let config = empty_dir(std::env::temp_dir().join(format!("vectorcraft-cli-test-{key}-{}", std::process::id())));
|
||||
let run = |extra: &[&str]| {
|
||||
Command::new(BIN).env("VECTORCRAFT_CONFIG_DIR", &config).args(["run", "--commands", steps.to_str().unwrap()]).args(extra).output().unwrap()
|
||||
Command::new(crate::bin())
|
||||
.env("VECTORCRAFT_CONFIG_DIR", &config)
|
||||
.args(["run", "--commands", steps.to_str().unwrap()])
|
||||
.args(extra)
|
||||
.output()
|
||||
.unwrap()
|
||||
};
|
||||
let out = run(&[]);
|
||||
assert!(!out.status.success());
|
||||
@@ -38,7 +41,7 @@ fn approval_param_needs_the_start_up_flag(key: &str, value: Value) {
|
||||
|
||||
let mcp_config = empty_dir(std::env::temp_dir().join(format!("vectorcraft-cli-test-mcp-{key}-{}", std::process::id())));
|
||||
let mcp_install = |extra: &[&str]| -> Value {
|
||||
let mut child = Command::new(BIN)
|
||||
let mut child = Command::new(crate::bin())
|
||||
.env("VECTORCRAFT_CONFIG_DIR", &mcp_config)
|
||||
.args(["mcp", "--headless"])
|
||||
.args(extra)
|
||||
@@ -77,22 +80,23 @@ fn plugin_grants_need_the_start_up_flag() {
|
||||
approval_param_needs_the_start_up_flag("grant", json!(["doc.read"]));
|
||||
approval_param_needs_the_start_up_flag("allowHeavy", json!(true));
|
||||
|
||||
let out = Command::new(BIN).args(["mcp", "--allow-plugin-grants", "--connect", "127.0.0.1:1"]).output().unwrap();
|
||||
let out = Command::new(crate::bin()).args(["mcp", "--allow-plugin-grants", "--connect", "127.0.0.1:1"]).output().unwrap();
|
||||
assert!(!out.status.success());
|
||||
assert!(String::from_utf8_lossy(&out.stderr).contains("--allow-plugin-grants applies to the headless server"));
|
||||
let root = std::env::temp_dir();
|
||||
for flag in ["--automation-read-root", "--automation-write-root"] {
|
||||
let out = Command::new(BIN).args(["mcp", "--allow-plugin-grants", flag, root.to_str().unwrap()]).stdin(Stdio::null()).output().unwrap();
|
||||
let out =
|
||||
Command::new(crate::bin()).args(["mcp", "--allow-plugin-grants", flag, root.to_str().unwrap()]).stdin(Stdio::null()).output().unwrap();
|
||||
assert!(!out.status.success(), "{flag}");
|
||||
assert!(String::from_utf8_lossy(&out.stderr).contains("--allow-plugin-grants can't be used with"), "{flag}");
|
||||
}
|
||||
let usage = Command::new(BIN).arg("help").output().unwrap();
|
||||
let usage = Command::new(crate::bin()).arg("help").output().unwrap();
|
||||
assert!(String::from_utf8_lossy(&usage.stdout).contains("--allow-plugin-grants lets plugin.install's grant and allowHeavy take effect"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn commands_prints_catalogue() {
|
||||
let out = Command::new(BIN).arg("commands").output().unwrap();
|
||||
let out = Command::new(crate::bin()).arg("commands").output().unwrap();
|
||||
assert!(out.status.success());
|
||||
let v: Value = serde_json::from_slice(&out.stdout).unwrap();
|
||||
let ids: Vec<&str> = v.as_array().unwrap().iter().filter_map(|c| c["id"].as_str()).collect();
|
||||
@@ -104,7 +108,7 @@ fn run_batch_exports() {
|
||||
let svg = tmp("batch.svg");
|
||||
let png = tmp("batch.png");
|
||||
let dc = tmp("batch.vectorcraft");
|
||||
let out = Command::new(BIN)
|
||||
let out = Command::new(crate::bin())
|
||||
.args(["run", "--cmd", "file.new", "--params", r#"{"width":200,"height":100}"#])
|
||||
.args(["--cmd", "shape.ellipse", "--params", r#"{"x":10,"y":10,"width":80,"height":60}"#])
|
||||
.args(["--cmd", "paint.setFill", "--params", r##"{"color":"#3366ff"}"##])
|
||||
@@ -121,7 +125,7 @@ fn run_batch_exports() {
|
||||
assert_eq!(u32::from_be_bytes(png_bytes[16..20].try_into().unwrap()), 400);
|
||||
|
||||
// Re-open the native file and inspect.
|
||||
let out = Command::new(BIN).args(["run", "--in", dc.to_str().unwrap(), "--cmd", "document.inspect"]).output().unwrap();
|
||||
let out = Command::new(crate::bin()).args(["run", "--in", dc.to_str().unwrap(), "--cmd", "document.inspect"]).output().unwrap();
|
||||
assert!(out.status.success(), "{}", String::from_utf8_lossy(&out.stderr));
|
||||
let last: Value = serde_json::from_str(String::from_utf8(out.stdout).unwrap().lines().last().unwrap()).unwrap();
|
||||
assert_eq!(last["result"]["artboards"][0]["width"], 200.0);
|
||||
@@ -137,7 +141,7 @@ fn scale_sizes_raster_exports_only() {
|
||||
let svg = svg.to_str().unwrap();
|
||||
let (plain, scaled, png) = (tmp("plain.dxf"), tmp("scaled.dxf"), tmp("scaled.png"));
|
||||
let ok = |args: &[&str]| {
|
||||
let out = Command::new(BIN).args(args).output().unwrap();
|
||||
let out = Command::new(crate::bin()).args(args).output().unwrap();
|
||||
assert!(out.status.success(), "{args:?}: {}", String::from_utf8_lossy(&out.stderr));
|
||||
};
|
||||
ok(&["convert", svg, plain.to_str().unwrap()]);
|
||||
@@ -151,11 +155,11 @@ fn scale_sizes_raster_exports_only() {
|
||||
|
||||
#[test]
|
||||
fn run_reports_errors() {
|
||||
let out = Command::new(BIN).args(["run", "--cmd", "nope.nothing"]).output().unwrap();
|
||||
let out = Command::new(crate::bin()).args(["run", "--cmd", "nope.nothing"]).output().unwrap();
|
||||
assert!(!out.status.success());
|
||||
let stderr = String::from_utf8_lossy(&out.stderr).to_string();
|
||||
assert!(stderr.contains("step 0 (nope.nothing)"), "{stderr}");
|
||||
let out = Command::new(BIN).args(["run", "--params", "{}"]).output().unwrap();
|
||||
let out = Command::new(crate::bin()).args(["run", "--params", "{}"]).output().unwrap();
|
||||
assert!(!out.status.success());
|
||||
}
|
||||
|
||||
@@ -184,7 +188,7 @@ fn run_reads_steps_from_a_commands_file() {
|
||||
{"id": "object.setProps", "params": {"id": "$2.ids.0", "name": "barcode"}},
|
||||
]);
|
||||
std::fs::write(&steps, serde_json::to_string_pretty(&file).unwrap()).unwrap();
|
||||
let out = Command::new(BIN)
|
||||
let out = Command::new(crate::bin())
|
||||
.args(["run", "--cmd", "file.new", "--params", r#"{"width":200,"height":100}"#, "--commands", steps.to_str().unwrap()])
|
||||
.args(["--cmd", "document.node", "--params", r#"{"id":"$2.ids.0"}"#, "--export", svg.to_str().unwrap()])
|
||||
.output()
|
||||
@@ -204,7 +208,7 @@ fn run_reads_steps_from_a_commands_file() {
|
||||
#[test]
|
||||
fn run_reads_json_lines_from_standard_input() {
|
||||
let svg = tmp("stdin.svg");
|
||||
let mut child = Command::new(BIN)
|
||||
let mut child = Command::new(crate::bin())
|
||||
.args(["run", "--commands", "-", "--export", svg.to_str().unwrap()])
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
@@ -227,7 +231,7 @@ fn run_reads_json_lines_from_standard_input() {
|
||||
#[test]
|
||||
fn run_refuses_malformed_commands_naming_the_step() {
|
||||
let fails = |args: &[&str]| {
|
||||
let out = Command::new(BIN).args(args).stdin(Stdio::null()).output().unwrap();
|
||||
let out = Command::new(crate::bin()).args(args).stdin(Stdio::null()).output().unwrap();
|
||||
assert!(!out.status.success(), "{args:?}");
|
||||
(String::from_utf8_lossy(&out.stderr).to_string(), json_lines(&out.stdout).len())
|
||||
};
|
||||
@@ -275,11 +279,11 @@ fn run_refuses_malformed_commands_naming_the_step() {
|
||||
/// `--help` and `-h` right after a subcommand print the usage that `vectorcraft-cli help` prints.
|
||||
#[test]
|
||||
fn help_after_a_subcommand_prints_the_usage() {
|
||||
let usage = Command::new(BIN).arg("help").output().unwrap();
|
||||
let usage = Command::new(crate::bin()).arg("help").output().unwrap();
|
||||
assert!(usage.status.success() && String::from_utf8_lossy(&usage.stdout).contains("USAGE:"));
|
||||
for sub in ["mcp", "run", "commands", "convert", "info", "bench", "perf"] {
|
||||
for flag in ["--help", "-h"] {
|
||||
let out = Command::new(BIN).args([sub, flag]).stdin(Stdio::null()).output().unwrap();
|
||||
let out = Command::new(crate::bin()).args([sub, flag]).stdin(Stdio::null()).output().unwrap();
|
||||
assert!(out.status.success(), "{sub} {flag}: {}", String::from_utf8_lossy(&out.stderr));
|
||||
assert!(out.stdout == usage.stdout, "{sub} {flag}: {}", String::from_utf8_lossy(&out.stdout));
|
||||
}
|
||||
@@ -290,7 +294,7 @@ fn help_after_a_subcommand_prints_the_usage() {
|
||||
/// with them.
|
||||
#[test]
|
||||
fn help_later_in_the_line_prints_the_usage() {
|
||||
let usage = Command::new(BIN).arg("help").output().unwrap();
|
||||
let usage = Command::new(crate::bin()).arg("help").output().unwrap();
|
||||
let mut wrong = vec![];
|
||||
for args in [
|
||||
&["mcp", "--headless", "--help"][..],
|
||||
@@ -311,7 +315,7 @@ fn help_later_in_the_line_prints_the_usage() {
|
||||
&["perf", "--paths", "many", "-h"],
|
||||
&["convert", "--decimals", "many", "--help"],
|
||||
] {
|
||||
let out = Command::new(BIN).args(args).stdin(Stdio::null()).output().unwrap();
|
||||
let out = Command::new(crate::bin()).args(args).stdin(Stdio::null()).output().unwrap();
|
||||
if !out.status.success() || out.stdout != usage.stdout {
|
||||
wrong.push(format!("{args:?}: {}{}", String::from_utf8_lossy(&out.stderr), String::from_utf8_lossy(&out.stdout)));
|
||||
}
|
||||
@@ -322,17 +326,18 @@ fn help_later_in_the_line_prints_the_usage() {
|
||||
/// Further along the line, `-h` is the value of the option before it.
|
||||
#[test]
|
||||
fn help_as_an_option_value_goes_to_the_option() {
|
||||
let out = Command::new(BIN).args(["run", "--cmd", "-h"]).output().unwrap();
|
||||
let out = Command::new(crate::bin()).args(["run", "--cmd", "-h"]).output().unwrap();
|
||||
let err = String::from_utf8_lossy(&out.stderr);
|
||||
assert!(!out.status.success() && err.contains("vectorcraft-cli: step 0 (-h):") && out.stdout.is_empty(), "{err}");
|
||||
let out = Command::new(BIN).args(["convert", "in.svg", "--range", "-h"]).output().unwrap();
|
||||
let out = Command::new(crate::bin()).args(["convert", "in.svg", "--range", "-h"]).output().unwrap();
|
||||
let err = String::from_utf8_lossy(&out.stderr);
|
||||
assert!(!out.status.success() && err.contains("convert needs IN and OUT") && out.stdout.is_empty(), "{err}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mcp_headless_over_stdio() {
|
||||
let mut child = Command::new(BIN).args(["mcp", "--headless"]).stdin(Stdio::piped()).stdout(Stdio::piped()).stderr(Stdio::null()).spawn().unwrap();
|
||||
let mut child =
|
||||
Command::new(crate::bin()).args(["mcp", "--headless"]).stdin(Stdio::piped()).stdout(Stdio::piped()).stderr(Stdio::null()).spawn().unwrap();
|
||||
{
|
||||
let mut stdin = child.stdin.take().unwrap();
|
||||
for m in [
|
||||
@@ -359,13 +364,13 @@ fn mcp_headless_over_stdio() {
|
||||
/// plug-ins.
|
||||
#[test]
|
||||
fn safe_mode_mcp_does_not_connect_to_a_running_app() {
|
||||
let out = Command::new(BIN).args(["--safe-mode", "mcp", "--connect", "127.0.0.1:9"]).env("VECTORCRAFT_NO_PREFS", "1").output().unwrap();
|
||||
let out = Command::new(crate::bin()).args(["--safe-mode", "mcp", "--connect", "127.0.0.1:9"]).env("VECTORCRAFT_NO_PREFS", "1").output().unwrap();
|
||||
let err = String::from_utf8_lossy(&out.stderr);
|
||||
assert!(!out.status.success() && err.contains("--safe-mode runs a headless session"), "{err}");
|
||||
// Something listening on the default address, as a running app does; a running app or another
|
||||
// program already holding it serves as well.
|
||||
let _app = std::net::TcpListener::bind(vectorcraft_mcp::DEFAULT_ADDR);
|
||||
let out = Command::new(BIN).args(["--safe-mode", "mcp"]).env("VECTORCRAFT_NO_PREFS", "1").stdin(Stdio::null()).output().unwrap();
|
||||
let out = Command::new(crate::bin()).args(["--safe-mode", "mcp"]).env("VECTORCRAFT_NO_PREFS", "1").stdin(Stdio::null()).output().unwrap();
|
||||
let err = String::from_utf8_lossy(&out.stderr);
|
||||
assert!(out.status.success() && err.contains("MCP server on stdio (headless)"), "{err}");
|
||||
}
|
||||
@@ -402,7 +407,7 @@ fn mcp_automation_roots_take_several_folders() {
|
||||
}
|
||||
let dir = |d: &str| base.join(d).to_string_lossy().into_owned();
|
||||
let file = |d: &str, f: &str| base.join(d).join(f).to_string_lossy().into_owned();
|
||||
let mut child = Command::new(BIN)
|
||||
let mut child = Command::new(crate::bin())
|
||||
.args(["mcp", "--automation-read-root", &dir("in"), &format!("--automation-read-root={}", dir("in2"))])
|
||||
.args(["--automation-write-root", &dir("out"), "--automation-write-root", &dir("out2")])
|
||||
.stdin(Stdio::piped())
|
||||
@@ -457,7 +462,7 @@ fn automation_roots(base: &std::path::Path) {
|
||||
}
|
||||
let dir = |d: &str| base.join(d).to_string_lossy().into_owned();
|
||||
let fails = |args: &[&str], why: &str| {
|
||||
let out = Command::new(BIN).args(args).stdin(Stdio::null()).output().unwrap();
|
||||
let out = Command::new(crate::bin()).args(args).stdin(Stdio::null()).output().unwrap();
|
||||
let err = String::from_utf8_lossy(&out.stderr);
|
||||
assert!(!out.status.success() && err.contains(why), "{args:?}: {err}");
|
||||
};
|
||||
@@ -469,7 +474,7 @@ fn automation_roots(base: &std::path::Path) {
|
||||
"a running app enforces its own roots: start it with `vectorcraft --control <port>",
|
||||
);
|
||||
|
||||
let mut child = Command::new(BIN)
|
||||
let mut child = Command::new(crate::bin())
|
||||
.args(["mcp", "--automation-read-root", &dir("in"), &format!("--automation-write-root={}", dir("out"))])
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
@@ -530,7 +535,7 @@ fn automation_roots(base: &std::path::Path) {
|
||||
fn mcp_protocol_over_stdio() {
|
||||
use std::io::{BufRead, BufReader};
|
||||
let mut child =
|
||||
Command::new(BIN).args(["mcp", "--headless"]).stdin(Stdio::piped()).stdout(Stdio::piped()).stderr(Stdio::piped()).spawn().unwrap();
|
||||
Command::new(crate::bin()).args(["mcp", "--headless"]).stdin(Stdio::piped()).stdout(Stdio::piped()).stderr(Stdio::piped()).spawn().unwrap();
|
||||
let mut stdin = child.stdin.take().unwrap();
|
||||
let mut lines = BufReader::new(child.stdout.take().unwrap()).lines();
|
||||
let mut notes = vec![];
|
||||
@@ -711,7 +716,7 @@ fn run_command(id: u64, command: &str, params: Value) -> Value {
|
||||
#[test]
|
||||
fn closed_stdout_ends_quietly() {
|
||||
use std::io::Read;
|
||||
let mut child = Command::new(BIN).arg("commands").stdout(Stdio::piped()).stderr(Stdio::piped()).spawn().unwrap();
|
||||
let mut child = Command::new(crate::bin()).arg("commands").stdout(Stdio::piped()).stderr(Stdio::piped()).spawn().unwrap();
|
||||
let mut first = [0u8; 16];
|
||||
child.stdout.as_mut().unwrap().read_exact(&mut first).unwrap();
|
||||
drop(child.stdout.take());
|
||||
@@ -728,13 +733,13 @@ fn run_with_a_closed_stdout_ends_quietly_and_finishes_the_batch() {
|
||||
let _ = std::fs::remove_file(&svg);
|
||||
let rect = r#"{"x":1,"y":1,"width":5,"height":5}"#;
|
||||
let args = ["run", "--cmd", "document.json", "--cmd", "shape.rectangle", "--params", rect, "--export", svg.to_str().unwrap()];
|
||||
let mut child = Command::new(BIN).args(args).stdout(Stdio::piped()).stderr(Stdio::piped()).spawn().unwrap();
|
||||
let mut child = Command::new(crate::bin()).args(args).stdout(Stdio::piped()).stderr(Stdio::piped()).spawn().unwrap();
|
||||
drop(child.stdout.take());
|
||||
let out = child.wait_with_output().unwrap();
|
||||
let err = String::from_utf8_lossy(&out.stderr);
|
||||
assert!(out.status.success() && err.is_empty(), "{:?}: {err}", out.status);
|
||||
assert!(svg.is_file(), "the export after the closed reader still ran");
|
||||
let out = Command::new(BIN).args(["run", "--cmd", "not-a-command"]).stdout(Stdio::null()).output().unwrap();
|
||||
let out = Command::new(crate::bin()).args(["run", "--cmd", "not-a-command"]).stdout(Stdio::null()).output().unwrap();
|
||||
assert!(!out.status.success());
|
||||
}
|
||||
|
||||
@@ -749,7 +754,7 @@ fn config_dir(name: &str) -> std::path::PathBuf {
|
||||
/// One `vectorcraft-cli run` with `steps` ((command, params)) against the settings folder `config`;
|
||||
/// the result of each step.
|
||||
fn run_in(config: &std::path::Path, steps: &[(&str, Value)]) -> Vec<Value> {
|
||||
let mut cmd = Command::new(BIN);
|
||||
let mut cmd = Command::new(crate::bin());
|
||||
cmd.arg("run").env("VECTORCRAFT_CONFIG_DIR", config).env_remove("VECTORCRAFT_NO_PREFS");
|
||||
for (id, params) in steps {
|
||||
cmd.args(["--cmd", id, "--params", ¶ms.to_string()]);
|
||||
@@ -820,7 +825,7 @@ fn saved_pdf_presets_and_loaded_profiles_survive_a_restart() {
|
||||
#[test]
|
||||
fn runs_without_preferences_remember_nothing() {
|
||||
let config = config_dir("noprefs");
|
||||
let out = Command::new(BIN)
|
||||
let out = Command::new(crate::bin())
|
||||
.args(["run", "--cmd", "pdf.preset.save", "--params", r#"{"name":"Press"}"#])
|
||||
.env("VECTORCRAFT_CONFIG_DIR", &config)
|
||||
.env("VECTORCRAFT_NO_PREFS", "1")
|
||||
|
||||
@@ -4,8 +4,6 @@ use std::process::Command;
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
const BIN: &str = env!("CARGO_BIN_EXE_vectorcraft-cli");
|
||||
|
||||
/// Returns the path of `name` in `vectorcraft-cli-fileio-<pid>` in the temp folder. The first call
|
||||
/// removes that folder and creates it again empty.
|
||||
fn tmp(name: &str) -> String {
|
||||
@@ -20,7 +18,7 @@ fn tmp(name: &str) -> String {
|
||||
}
|
||||
|
||||
fn ok(args: &[&str]) -> String {
|
||||
let out = Command::new(BIN).args(args).output().unwrap();
|
||||
let out = Command::new(crate::bin()).args(args).output().unwrap();
|
||||
assert!(out.status.success(), "{args:?}: {}", String::from_utf8_lossy(&out.stderr));
|
||||
String::from_utf8(out.stdout).unwrap()
|
||||
}
|
||||
@@ -130,7 +128,7 @@ fn raster_exports_use_document_resolution_unless_overridden() {
|
||||
fn raster_export_rejects_invalid_ppi() {
|
||||
for ppi in ["0", "-150", "NaN", "inf", "high"] {
|
||||
for args in [vec!["run", "--export", "unused.png", "--ppi", ppi], vec!["convert", "unused.svg", "unused.png", "--ppi", ppi]] {
|
||||
let out = Command::new(BIN).args(&args).output().unwrap();
|
||||
let out = Command::new(crate::bin()).args(&args).output().unwrap();
|
||||
assert!(!out.status.success(), "{args:?}");
|
||||
assert!(String::from_utf8_lossy(&out.stderr).contains("positive finite number"), "{args:?}: {out:?}");
|
||||
}
|
||||
@@ -228,7 +226,7 @@ fn convert_writes_legacy_ai_with_its_options() {
|
||||
assert!(r["warnings"].to_string().contains("hidden objects are left out"), "{r}");
|
||||
assert!(std::fs::read(&one).unwrap().starts_with(b"%!PS-Adobe-3.0"));
|
||||
// A value the engine doesn't take is an error.
|
||||
let bad = Command::new(BIN).args(["convert", &src, &tmp("bad.ai"), "--ai-version", "7", "--hidden-objects", "behind"]).output().unwrap();
|
||||
let bad = Command::new(crate::bin()).args(["convert", &src, &tmp("bad.ai"), "--ai-version", "7", "--hidden-objects", "behind"]).output().unwrap();
|
||||
assert!(!bad.status.success());
|
||||
assert!(String::from_utf8_lossy(&bad.stderr).contains("hiddenObjects"), "{}", String::from_utf8_lossy(&bad.stderr));
|
||||
}
|
||||
@@ -253,7 +251,7 @@ fn convert_and_run_write_ait_templates() {
|
||||
assert_eq!(info(ait)["artboards"].as_array().unwrap().len(), 2, "{ait}");
|
||||
}
|
||||
// A template holds every artboard: one can't be picked.
|
||||
let one = Command::new(BIN).args(["convert", &pdf, &tmp("one.ait"), "--artboard", "0"]).output().unwrap();
|
||||
let one = Command::new(crate::bin()).args(["convert", &pdf, &tmp("one.ait"), "--artboard", "0"]).output().unwrap();
|
||||
assert!(!one.status.success());
|
||||
assert!(String::from_utf8_lossy(&one.stderr).contains("every artboard"), "{}", String::from_utf8_lossy(&one.stderr));
|
||||
}
|
||||
|
||||
@@ -6,3 +6,11 @@ mod cli;
|
||||
mod fileio;
|
||||
mod mcp_e2e;
|
||||
mod script;
|
||||
|
||||
/// Nextest relocates archived binaries; Cargo's compile-time path still names the build runner.
|
||||
fn bin() -> std::path::PathBuf {
|
||||
std::env::var_os("NEXTEST_BIN_EXE_vectorcraft_cli")
|
||||
.or_else(|| std::env::var_os("CARGO_BIN_EXE_vectorcraft-cli"))
|
||||
.map(std::path::PathBuf::from)
|
||||
.unwrap_or_else(|| env!("CARGO_BIN_EXE_vectorcraft-cli").into())
|
||||
}
|
||||
|
||||
@@ -6,8 +6,6 @@ use std::process::{Child, ChildStdin, ChildStdout, Command, Stdio};
|
||||
use serde_json::{Value, json};
|
||||
use vectorcraft_testkit::raster::Image;
|
||||
|
||||
const BIN: &str = env!("CARGO_BIN_EXE_vectorcraft-cli");
|
||||
|
||||
struct Mcp {
|
||||
child: Child,
|
||||
stdin: Option<ChildStdin>,
|
||||
@@ -17,7 +15,7 @@ struct Mcp {
|
||||
|
||||
impl Mcp {
|
||||
fn spawn() -> Self {
|
||||
let mut child = Command::new(BIN)
|
||||
let mut child = Command::new(crate::bin())
|
||||
.args(["mcp", "--headless"])
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
|
||||
@@ -2,11 +2,9 @@ use std::process::Command;
|
||||
|
||||
use serde_json::{Value, json};
|
||||
|
||||
const BIN: &str = env!("CARGO_BIN_EXE_vectorcraft-cli");
|
||||
|
||||
/// `run` with `script.run` after `file.new`, with `params`.
|
||||
fn run_script(params: &Value) -> std::process::Output {
|
||||
Command::new(BIN).args(["run", "--cmd", "file.new", "--cmd", "script.run", "--params", ¶ms.to_string()]).output().unwrap()
|
||||
Command::new(crate::bin()).args(["run", "--cmd", "file.new", "--cmd", "script.run", "--params", ¶ms.to_string()]).output().unwrap()
|
||||
}
|
||||
|
||||
/// `run` runs a script headless: the reply holds the report, with what the script logged and the
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use std::process::{Command, Stdio};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
const BIN: &str = env!("CARGO_BIN_EXE_vectorcraft");
|
||||
|
||||
/// Invalid roots must exit before desktop initialization on every platform, including a CI
|
||||
/// machine with no display. A regression must fail rather than hang with a window open.
|
||||
fn rejects(args: &[&str], message: &str) {
|
||||
@@ -10,7 +8,7 @@ fn rejects(args: &[&str], message: &str) {
|
||||
}
|
||||
|
||||
fn rejects_with_env(args: &[&str], env: &[(&str, &str)], message: &str) {
|
||||
let mut child = Command::new(BIN)
|
||||
let mut child = Command::new(crate::bin())
|
||||
.args(args)
|
||||
.env_remove("VECTORCRAFT_CONTROL_PORT")
|
||||
.env_remove("VECTORCRAFT_AUTOMATION_READ_ROOT")
|
||||
@@ -62,7 +60,7 @@ fn missing_empty_or_option_operands_fail_before_startup() {
|
||||
#[test]
|
||||
fn repeated_roots_are_accepted_for_both_spellings() {
|
||||
for flag in ["--automation-read-root", "--automation-write-root"] {
|
||||
let out = Command::new(BIN).args([flag, ".", format!("{flag}=.").as_str(), flag, "./", "--version"]).output().unwrap();
|
||||
let out = Command::new(crate::bin()).args([flag, ".", format!("{flag}=.").as_str(), flag, "./", "--version"]).output().unwrap();
|
||||
assert!(out.status.success(), "{flag}: {}", String::from_utf8_lossy(&out.stderr));
|
||||
assert_eq!(String::from_utf8(out.stdout).unwrap(), format!("vectorcraft {}\n", env!("CARGO_PKG_VERSION")));
|
||||
assert!(out.stderr.is_empty());
|
||||
@@ -73,7 +71,8 @@ fn repeated_roots_are_accepted_for_both_spellings() {
|
||||
|
||||
#[test]
|
||||
fn explicit_dash_folder_does_not_consume_version() {
|
||||
let out = Command::new(BIN).args(["--automation-read-root=-folder", "--automation-write-root", "./-folder", "--version"]).output().unwrap();
|
||||
let out =
|
||||
Command::new(crate::bin()).args(["--automation-read-root=-folder", "--automation-write-root", "./-folder", "--version"]).output().unwrap();
|
||||
assert!(out.status.success(), "{}", String::from_utf8_lossy(&out.stderr));
|
||||
assert_eq!(String::from_utf8(out.stdout).unwrap(), format!("vectorcraft {}\n", env!("CARGO_PKG_VERSION")));
|
||||
assert!(out.stderr.is_empty());
|
||||
@@ -95,7 +94,7 @@ fn control_without_a_valid_port_fails_before_startup() {
|
||||
|
||||
#[test]
|
||||
fn version_wins_over_a_bad_control_port_variable() {
|
||||
let out = Command::new(BIN).arg("--version").env("VECTORCRAFT_CONTROL_PORT", "abc").output().unwrap();
|
||||
let out = Command::new(crate::bin()).arg("--version").env("VECTORCRAFT_CONTROL_PORT", "abc").output().unwrap();
|
||||
assert!(out.status.success(), "{}", String::from_utf8_lossy(&out.stderr));
|
||||
assert_eq!(String::from_utf8(out.stdout).unwrap(), format!("vectorcraft {}\n", env!("CARGO_PKG_VERSION")));
|
||||
}
|
||||
|
||||
@@ -6,12 +6,10 @@ use std::process::{Command, Output, Stdio};
|
||||
use std::sync::mpsc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
const BIN: &str = env!("CARGO_BIN_EXE_vectorcraft");
|
||||
|
||||
/// Runs the desktop binary without a display and with a control port it cannot parse, so a
|
||||
/// regression exits with that error instead of opening a window.
|
||||
fn run(args: &[&str], env: &[(&str, &str)]) -> Output {
|
||||
let mut child = Command::new(BIN)
|
||||
let mut child = Command::new(crate::bin())
|
||||
.args(args)
|
||||
.env_remove("DISPLAY")
|
||||
.env_remove("WAYLAND_DISPLAY")
|
||||
|
||||
@@ -4,3 +4,11 @@
|
||||
|
||||
mod automation_args;
|
||||
mod help;
|
||||
|
||||
/// Nextest relocates archived binaries; Cargo's compile-time path still names the build runner.
|
||||
fn bin() -> std::path::PathBuf {
|
||||
std::env::var_os("NEXTEST_BIN_EXE_vectorcraft")
|
||||
.or_else(|| std::env::var_os("CARGO_BIN_EXE_vectorcraft"))
|
||||
.map(std::path::PathBuf::from)
|
||||
.unwrap_or_else(|| env!("CARGO_BIN_EXE_vectorcraft").into())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
//! Release and source Flatpaks must allow release checks and host update helpers.
|
||||
fn finish_args(manifest: &str) -> Vec<&str> {
|
||||
manifest
|
||||
.split("finish-args:")
|
||||
.nth(1)
|
||||
.unwrap_or_default()
|
||||
.lines()
|
||||
.take_while(|line| line.is_empty() || line.starts_with(' ') || line.starts_with('#'))
|
||||
.filter_map(|line| line.trim().strip_prefix("- "))
|
||||
.map(|arg| arg.trim_matches(['\'', '"']))
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flatpak_update_permissions() {
|
||||
let manifests = [
|
||||
include_str!("../../../packaging/linux/flatpak/ai.storyteller.vectorcraft.bundle.yml"),
|
||||
include_str!("../../../packaging/linux/flatpak/ai.storyteller.vectorcraft.yml"),
|
||||
];
|
||||
let args = manifests.map(finish_args);
|
||||
assert_eq!(args[0], args[1], "source and release permissions must agree");
|
||||
for permissions in args {
|
||||
for required in ["--share=network", "--talk-name=org.freedesktop.Flatpak"] {
|
||||
assert!(permissions.contains(&required), "missing update permission: {required}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,12 @@ pick that branch for a full run. Run on any other branch, it is a dry run: the L
|
||||
FreeBSD and web jobs build and upload their artifacts, the environment refuses the signing jobs,
|
||||
and no draft release is created.
|
||||
|
||||
Flatpak permission repairs need one update through the existing host-side installation method.
|
||||
After that update, Help > Check for Updates can inspect and update standalone bundles.
|
||||
Repository-managed installations continue to use their package manager; launcher-pinned
|
||||
versions keep their selected version. Both Flatpak manifests must grant network access,
|
||||
the host Flatpak interface, and read-only access to the shared version choices.
|
||||
|
||||
## What gets built
|
||||
|
||||
| Platform | Artifacts | Built on |
|
||||
|
||||
@@ -17,6 +17,8 @@ runtime-version: '25.08'
|
||||
sdk: org.freedesktop.Sdk
|
||||
command: vectorcraft
|
||||
finish-args:
|
||||
# The shared updater inspects and updates this installation via flatpak-spawn --host.
|
||||
- --talk-name=org.freedesktop.Flatpak
|
||||
- --filesystem=xdg-data/ArtCraft/versions:ro
|
||||
# X11 shared memory (MIT-SHM). Without it the X11 fallback is slow or fails.
|
||||
- --share=ipc
|
||||
|
||||
@@ -17,6 +17,8 @@ sdk-extensions:
|
||||
- org.freedesktop.Sdk.Extension.rust-stable
|
||||
command: vectorcraft
|
||||
finish-args:
|
||||
# The shared updater inspects and updates this installation via flatpak-spawn --host.
|
||||
- --talk-name=org.freedesktop.Flatpak
|
||||
- --filesystem=xdg-data/ArtCraft/versions:ro
|
||||
# Keep identical to ai.storyteller.vectorcraft.bundle.yml (the release bundle); comments there.
|
||||
- --share=ipc
|
||||
|
||||
Reference in New Issue
Block a user