Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion crates/launcher-common/src/standard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,18 @@ mod tests {
#[test]
fn baked_standard_parses() {
let s = LauncherStandard::baked().expect("baked standard must parse");
assert_eq!(s.spec_version, "0.1.0");

// Derive the expected version from the baked source rather than
// hardcoding it. This assertion previously read "0.1.0" and had gone
// stale against a standard that declares 0.2.0 - the test failed for
// a reason that had nothing to do with parsing, which is what it is
// named for. Deriving it means the check cannot rot again.
let declared = BAKED_STANDARD
.lines()
.find_map(|l| l.trim().strip_prefix("version = "))
.map(|v| v.trim().trim_matches('"'))
.expect("baked standard must declare a version");
assert_eq!(s.spec_version, declared);
assert!(!s.spec_version.is_empty(), "spec version must not be empty");
}
}
76 changes: 76 additions & 0 deletions crates/launcher-common/src/template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,79 @@ pub fn render(
tera.render("launcher.sh", &ctx)
.context("rendering launcher template")
}

#[cfg(test)]
mod tests {
use super::*;
use crate::config::{LauncherConfig, Project, Repo, Runtime, RuntimeKind};
use crate::standard::LauncherStandard;

fn sample_config() -> LauncherConfig {
LauncherConfig {
project: Project {
name: "foo".into(),
display: "Foo".into(),
description: Some("Foo desc".into()),
categories: vec!["Development".into()],
version: None,
license: None,
generic_name: Some("Foo Thing".into()),
},
repo: Repo {
path: "/tmp/foo".into(),
},
runtime: Runtime {
kind: RuntimeKind::Process,
port: None,
url: None,
startup_command_search: vec![],
command: vec!["foo".into()],
pid_file: None,
log_file: None,
wait_for_url_timeout_seconds: 15,
},
icon: None,
integration: None,
soft_attach: None,
exceptions: None,
}
}

/// The template source must OPEN with the shebang.
///
/// It previously opened with a Tera comment block, so every launcher the
/// generator emitted began with a blank line: shellcheck SC2148, and a
/// script the kernel will not dispatch by shebang. That single template
/// defect produced ~20 identical one-line fix PRs across the estate, each
/// of which the next `launch-scaffolder realign` would have overwritten.
#[test]
fn template_source_opens_with_shebang() {
assert_eq!(
LAUNCHER_TEMPLATE.lines().next(),
Some("#!/usr/bin/env bash"),
"the shebang must be the literal first line of launcher.sh.tera"
);
}

/// And the RENDERED output must too — the property that actually matters.
/// Asserting only on the template source would miss Tera emitting leading
/// whitespace of its own, which is precisely how the original defect
/// escaped notice.
#[test]
fn rendered_launcher_starts_with_shebang_on_line_one() {
let cfg = sample_config();
let std_ = LauncherStandard::baked().expect("baked standard should parse");
let out = render(&cfg, &std_, None).expect("template should render");

assert!(
out.starts_with("#!/usr/bin/env bash\n"),
"rendered launcher must begin with the shebang; got: {:?}",
&out[..out.len().min(60)]
);
assert_eq!(
out.lines().next(),
Some("#!/usr/bin/env bash"),
"shebang must be on line 1 of the rendered launcher"
);
}
}
11 changes: 10 additions & 1 deletion templates/launcher.sh.tera
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
#!/usr/bin/env bash
{# NOTE: the shebang MUST be the literal first line of this template.
It previously sat below this comment block, and every launcher the
generator emitted began with a blank line -- shellcheck SC2148, and a
script whose kernel shebang is not on line 1. That produced ~20
identical one-line fix PRs across the estate, each of which would have
been overwritten by the next `launch-scaffolder realign`. Do not move
the shebang below a Tera tag again, whatever whitespace-control markers
the tag carries.
-#}
{#-
SPDX-License-Identifier: MPL-2.0
Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
Expand All @@ -13,7 +23,6 @@
All three shapes emit the same --integ / --disinteg / --help cross-platform
surface so desktop integration stays uniform across the estate.
-#}
#!/usr/bin/env bash
# SPDX-License-Identifier: MPL-2.0
# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
#
Expand Down
Loading