| 65 |
65 |
|
pub app: AppId,
|
| 66 |
66 |
|
pub version: Version,
|
| 67 |
67 |
|
pub target: Target,
|
|
68 |
+ |
/// Name of the host this target builds on (resolved from the topology by the
|
|
69 |
+ |
/// runner). Recipes read it via `build_host()` so one per-platform recipe can
|
|
70 |
+ |
/// dispatch to the right host across arches (linux x86_64 -> fw13, aarch64 ->
|
|
71 |
+ |
/// astra) without hard-coding a host name.
|
|
72 |
+ |
pub build_host: String,
|
|
73 |
+ |
/// The app's checkout path (topology `repo`, `~`-prefixed). Recipes read it
|
|
74 |
+ |
/// via `repo()` to `cd` into the checkout — commands don't auto-cd, and each
|
|
75 |
+ |
/// `sh` is a fresh shell.
|
|
76 |
+ |
pub repo: String,
|
| 68 |
77 |
|
pub target_run_id: i64,
|
| 69 |
78 |
|
/// Capability-scoped executor per build host. Recipe commands dispatch
|
| 70 |
79 |
|
/// through these — the transport (local / ssh / in-session agent) and the
|
| 96 |
105 |
|
app: AppId,
|
| 97 |
106 |
|
version: Version,
|
| 98 |
107 |
|
target: Target,
|
|
108 |
+ |
build_host: String,
|
|
109 |
+ |
repo: String,
|
| 99 |
110 |
|
target_run_id: i64,
|
| 100 |
111 |
|
execs: Arc<ExecutorMap>,
|
| 101 |
112 |
|
host_ssh: HashMap<String, String>,
|
| 109 |
120 |
|
app,
|
| 110 |
121 |
|
version,
|
| 111 |
122 |
|
target,
|
|
123 |
+ |
build_host,
|
|
124 |
+ |
repo,
|
| 112 |
125 |
|
target_run_id,
|
| 113 |
126 |
|
execs,
|
| 114 |
127 |
|
host_ssh,
|
| 306 |
319 |
|
Box::new(EvalAltResult::ErrorRuntime(e.to_string().into(), rhai::Position::NONE))
|
| 307 |
320 |
|
}
|
| 308 |
321 |
|
|
| 309 |
|
- |
/// Read `tauri.conf.json`'s version for `app`, from its checkout on the daemon
|
| 310 |
|
- |
/// host. Used by `version_of()` and the runner's default-version path.
|
| 311 |
|
- |
pub fn version_from_tauri_conf(repo: &str) -> Result<Version> {
|
| 312 |
|
- |
let path = expand_tilde(repo).join("src-tauri").join("tauri.conf.json");
|
| 313 |
|
- |
let raw = std::fs::read_to_string(&path)
|
| 314 |
|
- |
.with_context(|| format!("reading {}", path.display()))?;
|
| 315 |
|
- |
let v: serde_json::Value = serde_json::from_str(&raw).context("parsing tauri.conf.json")?;
|
| 316 |
|
- |
let ver = v.get("version").and_then(|x| x.as_str()).context("no `version` in tauri.conf.json")?;
|
| 317 |
|
- |
Version::parse(ver).map_err(|e| anyhow::anyhow!(e))
|
|
322 |
+ |
/// Read the app's version from its checkout on the daemon host. With
|
|
323 |
+ |
/// `version_path` set (topology `version_path`), read exactly that file — a
|
|
324 |
+ |
/// `.json` as a Tauri config, anything else as a `Cargo.toml`. Unset (the Tauri
|
|
325 |
+ |
/// default), try `src-tauri/tauri.conf.json` then the root `Cargo.toml`. Used by
|
|
326 |
+ |
/// the runner's default-version path.
|
|
327 |
+ |
pub fn version_from_repo(repo: &str, version_path: Option<&str>) -> Result<Version> {
|
|
328 |
+ |
let root = expand_tilde(repo);
|
|
329 |
+ |
if let Some(vp) = version_path {
|
|
330 |
+ |
let path = root.join(vp);
|
|
331 |
+ |
let raw = std::fs::read_to_string(&path)
|
|
332 |
+ |
.with_context(|| format!("reading version file {}", path.display()))?;
|
|
333 |
+ |
let ver = if vp.ends_with(".json") {
|
|
334 |
+ |
version_from_tauri_json(&raw)?
|
|
335 |
+ |
} else {
|
|
336 |
+ |
version_from_cargo_toml(&raw)?
|
|
337 |
+ |
};
|
|
338 |
+ |
return Version::parse(&ver).map_err(|e| anyhow::anyhow!(e));
|
|
339 |
+ |
}
|
|
340 |
+ |
let tauri_conf = root.join("src-tauri").join("tauri.conf.json");
|
|
341 |
+ |
if tauri_conf.exists() {
|
|
342 |
+ |
let raw = std::fs::read_to_string(&tauri_conf)
|
|
343 |
+ |
.with_context(|| format!("reading {}", tauri_conf.display()))?;
|
|
344 |
+ |
return Version::parse(&version_from_tauri_json(&raw)?).map_err(|e| anyhow::anyhow!(e));
|
|
345 |
+ |
}
|
|
346 |
+ |
let cargo_toml = root.join("Cargo.toml");
|
|
347 |
+ |
let raw = std::fs::read_to_string(&cargo_toml)
|
|
348 |
+ |
.with_context(|| format!("reading {} (no tauri.conf.json either)", cargo_toml.display()))?;
|
|
349 |
+ |
Version::parse(&version_from_cargo_toml(&raw)?).map_err(|e| anyhow::anyhow!(e))
|
|
350 |
+ |
}
|
|
351 |
+ |
|
|
352 |
+ |
/// Extract `version` from raw `tauri.conf.json` text.
|
|
353 |
+ |
fn version_from_tauri_json(raw: &str) -> Result<String> {
|
|
354 |
+ |
let v: serde_json::Value = serde_json::from_str(raw).context("parsing tauri.conf.json")?;
|
|
355 |
+ |
v.get("version")
|
|
356 |
+ |
.and_then(|x| x.as_str())
|
|
357 |
+ |
.map(str::to_owned)
|
|
358 |
+ |
.context("no `version` in tauri.conf.json")
|
|
359 |
+ |
}
|
|
360 |
+ |
|
|
361 |
+ |
/// Extract the version from raw `Cargo.toml` text — `[package].version` (a leaf
|
|
362 |
+ |
/// crate) or `[workspace.package].version` (a workspace that sets it).
|
|
363 |
+ |
fn version_from_cargo_toml(raw: &str) -> Result<String> {
|
|
364 |
+ |
let doc: toml::Value = toml::from_str(raw).context("parsing Cargo.toml")?;
|
|
365 |
+ |
doc.get("package")
|
|
366 |
+ |
.and_then(|p| p.get("version"))
|
|
367 |
+ |
.or_else(|| doc.get("workspace").and_then(|w| w.get("package")).and_then(|p| p.get("version")))
|
|
368 |
+ |
.and_then(|v| v.as_str())
|
|
369 |
+ |
.map(str::to_owned)
|
|
370 |
+ |
.context("no `[package].version` or `[workspace.package].version` in Cargo.toml")
|
| 318 |
371 |
|
}
|
| 319 |
372 |
|
|
| 320 |
373 |
|
/// Expand a leading `~/` to `$HOME`. Paths in the topology are written with `~`.
|
| 395 |
448 |
|
});
|
| 396 |
449 |
|
}
|
| 397 |
450 |
|
|
|
451 |
+ |
// --- version() -> string: the version being built (no-arg form) ---
|
|
452 |
+ |
{
|
|
453 |
+ |
let ctx = ctx.clone();
|
|
454 |
+ |
engine.register_fn("version", move || -> String { ctx.version.to_string() });
|
|
455 |
+ |
}
|
|
456 |
+ |
|
|
457 |
+ |
// --- build_host() -> string: the host this target builds on ---
|
|
458 |
+ |
{
|
|
459 |
+ |
let ctx = ctx.clone();
|
|
460 |
+ |
engine.register_fn("build_host", move || -> String { ctx.build_host.clone() });
|
|
461 |
+ |
}
|
|
462 |
+ |
|
|
463 |
+ |
// --- repo() -> string: the app's checkout path (`~`-prefixed) ---
|
|
464 |
+ |
{
|
|
465 |
+ |
let ctx = ctx.clone();
|
|
466 |
+ |
engine.register_fn("repo", move || -> String { ctx.repo.clone() });
|
|
467 |
+ |
}
|
|
468 |
+ |
|
|
469 |
+ |
// --- target() / platform() / arch(): the target axis, for one per-platform
|
|
470 |
+ |
// recipe to branch on arch (bundle paths differ between x86_64/aarch64). ---
|
|
471 |
+ |
{
|
|
472 |
+ |
let ctx = ctx.clone();
|
|
473 |
+ |
engine.register_fn("target", move || -> String { ctx.target.to_string() });
|
|
474 |
+ |
}
|
|
475 |
+ |
{
|
|
476 |
+ |
let ctx = ctx.clone();
|
|
477 |
+ |
engine.register_fn("platform", move || -> String { ctx.target.platform.as_str().to_string() });
|
|
478 |
+ |
}
|
|
479 |
+ |
{
|
|
480 |
+ |
let ctx = ctx.clone();
|
|
481 |
+ |
engine.register_fn("arch", move || -> String { ctx.target.arch.as_str().to_string() });
|
|
482 |
+ |
}
|
|
483 |
+ |
|
| 398 |
484 |
|
// --- secret(key) -> string (file under secrets_root; never logged) ---
|
| 399 |
485 |
|
{
|
| 400 |
486 |
|
let ctx = ctx.clone();
|
| 803 |
889 |
|
assert_eq!(expand_tilde("/abs/path"), PathBuf::from("/abs/path"));
|
| 804 |
890 |
|
}
|
| 805 |
891 |
|
|
|
892 |
+ |
// ---- version resolution ----
|
|
893 |
+ |
|
|
894 |
+ |
#[test]
|
|
895 |
+ |
fn version_from_tauri_json_reads_version() {
|
|
896 |
+ |
assert_eq!(version_from_tauri_json(r#"{"version":"0.4.2"}"#).unwrap(), "0.4.2");
|
|
897 |
+ |
assert!(version_from_tauri_json(r#"{"productName":"X"}"#).is_err());
|
|
898 |
+ |
}
|
|
899 |
+ |
|
|
900 |
+ |
#[test]
|
|
901 |
+ |
fn version_from_cargo_toml_prefers_package_then_workspace() {
|
|
902 |
+ |
// A leaf crate's [package].version.
|
|
903 |
+ |
assert_eq!(
|
|
904 |
+ |
version_from_cargo_toml("[package]\nname = \"x\"\nversion = \"0.5.0\"\n").unwrap(),
|
|
905 |
+ |
"0.5.0"
|
|
906 |
+ |
);
|
|
907 |
+ |
// A workspace that sets [workspace.package].version.
|
|
908 |
+ |
assert_eq!(
|
|
909 |
+ |
version_from_cargo_toml("[workspace.package]\nversion = \"1.2.3\"\n").unwrap(),
|
|
910 |
+ |
"1.2.3"
|
|
911 |
+ |
);
|
|
912 |
+ |
// No version anywhere -> error, not a panic.
|
|
913 |
+ |
assert!(version_from_cargo_toml("[workspace]\nmembers = []\n").is_err());
|
|
914 |
+ |
}
|
|
915 |
+ |
|
|
916 |
+ |
#[test]
|
|
917 |
+ |
fn version_from_repo_default_and_explicit_paths() {
|
|
918 |
+ |
let tmp = tempfile::tempdir().unwrap();
|
|
919 |
+ |
let root = tmp.path();
|
|
920 |
+ |
|
|
921 |
+ |
// Tauri app: default path reads src-tauri/tauri.conf.json.
|
|
922 |
+ |
let tauri = root.join("tauri");
|
|
923 |
+ |
std::fs::create_dir_all(tauri.join("src-tauri")).unwrap();
|
|
924 |
+ |
std::fs::write(tauri.join("src-tauri/tauri.conf.json"), r#"{"version":"0.4.2"}"#).unwrap();
|
|
925 |
+ |
assert_eq!(
|
|
926 |
+ |
version_from_repo(tauri.to_str().unwrap(), None).unwrap().to_string(),
|
|
927 |
+ |
"0.4.2"
|
|
928 |
+ |
);
|
|
929 |
+ |
|
|
930 |
+ |
// Workspace egui app: no tauri.conf.json, explicit version_path at a member crate.
|
|
931 |
+ |
let ws = root.join("ws");
|
|
932 |
+ |
std::fs::create_dir_all(ws.join("crates/app")).unwrap();
|
|
933 |
+ |
std::fs::write(ws.join("Cargo.toml"), "[workspace]\nmembers = [\"crates/app\"]\n").unwrap();
|
|
934 |
+ |
std::fs::write(
|
|
935 |
+ |
ws.join("crates/app/Cargo.toml"),
|
|
936 |
+ |
"[package]\nname = \"app\"\nversion = \"0.5.0\"\n",
|
|
937 |
+ |
)
|
|
938 |
+ |
.unwrap();
|
|
939 |
+ |
assert_eq!(
|
|
940 |
+ |
version_from_repo(ws.to_str().unwrap(), Some("crates/app/Cargo.toml"))
|
|
941 |
+ |
.unwrap()
|
|
942 |
+ |
.to_string(),
|
|
943 |
+ |
"0.5.0"
|
|
944 |
+ |
);
|
|
945 |
+ |
}
|
|
946 |
+ |
|
| 806 |
947 |
|
// ---- publish step-success gate ----
|
| 807 |
948 |
|
|
| 808 |
949 |
|
fn target(s: &str) -> Target {
|