|
|
@@ -184,9 +184,11 @@ namespace ClankerSaddle.Services {
|
|
|
* absolute system shares (/usr/share, /usr/local/share — the
|
|
|
* packaged layout where /proc/self/exe's neighbours are not
|
|
|
* the install prefix, e.g. a container image with the binary
|
|
|
- * copied elsewhere), then the source tree (<exe>/../../canon,
|
|
|
- * <exe>/../canon, ./canon). Empty when nothing resolves — the
|
|
|
- * endpoints then report an absent canon.
|
|
|
+ * copied elsewhere), then the deployment slot's data
|
|
|
+ * directory (the usmrad layout — see {@link slot_candidates}),
|
|
|
+ * then the source tree (<exe>/../../canon, <exe>/../canon,
|
|
|
+ * ./canon). Empty when nothing resolves — the endpoints then
|
|
|
+ * report an absent canon.
|
|
|
*/
|
|
|
public string directory() {
|
|
|
if (directory_cache != null) {
|
|
|
@@ -216,22 +218,72 @@ namespace ClankerSaddle.Services {
|
|
|
* where the app looked.
|
|
|
*/
|
|
|
public string[] candidates() {
|
|
|
+ var found = new string[0];
|
|
|
var exe_dir = exe_directory();
|
|
|
- return {
|
|
|
- // installed: <prefix>/bin/clanker-saddle + share/…
|
|
|
- GLib.Path.build_filename(exe_dir, "..", "share",
|
|
|
- "clanker-saddle", "canon"),
|
|
|
- // packaged layout where the binary's neighbours are not
|
|
|
- // the install prefix: the absolute system shares
|
|
|
- "/usr/share/clanker-saddle/canon",
|
|
|
- "/usr/local/share/clanker-saddle/canon",
|
|
|
- // build dir run (builddir sits beside src/)
|
|
|
- GLib.Path.build_filename(exe_dir, "..", "..", "canon"),
|
|
|
- GLib.Path.build_filename(exe_dir, "..", "canon"),
|
|
|
- GLib.Path.build_filename(exe_dir, "canon"),
|
|
|
- // a bare ./canon next to the cwd (tests, dev runs)
|
|
|
- "canon"
|
|
|
- };
|
|
|
+ // installed: <prefix>/bin/clanker-saddle + share/…
|
|
|
+ found += GLib.Path.build_filename(exe_dir, "..", "share",
|
|
|
+ "clanker-saddle", "canon");
|
|
|
+ // packaged layout where the binary's neighbours are not
|
|
|
+ // the install prefix: the absolute system shares
|
|
|
+ found += "/usr/share/clanker-saddle/canon";
|
|
|
+ found += "/usr/local/share/clanker-saddle/canon";
|
|
|
+ // the usmrad deployment slot (deduped against the system
|
|
|
+ // shares above so the system path keeps its precedence)
|
|
|
+ foreach (var slot in slot_candidates()) {
|
|
|
+ found += slot;
|
|
|
+ }
|
|
|
+ // build dir run (builddir sits beside src/)
|
|
|
+ found += GLib.Path.build_filename(exe_dir, "..", "..", "canon");
|
|
|
+ found += GLib.Path.build_filename(exe_dir, "..", "canon");
|
|
|
+ found += GLib.Path.build_filename(exe_dir, "canon");
|
|
|
+ // a bare ./canon next to the cwd (tests, dev runs)
|
|
|
+ found += "canon";
|
|
|
+ return found;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The canon directories of the usmrad DEPLOYMENT SLOT layout.
|
|
|
+ *
|
|
|
+ * usmrad (the USM radio: a git-tracking deployer) clones the
|
|
|
+ * tracked branch to <appdir>/repo, builds it, and installs
|
|
|
+ * the manifest's provided resources into one of two A/B
|
|
|
+ * slots — <appdir>/<slot>/usr/… — then flips a `current`
|
|
|
+ * symlink onto the fresh slot and launches the binary
|
|
|
+ * through it. The canon rides that install as
|
|
|
+ * <appdir>/<slot>/usr/share/clanker-saddle/canon (the
|
|
|
+ * manifest provides each canon file as a res: resource, so
|
|
|
+ * usm's install phase copies it beside the binary).
|
|
|
+ *
|
|
|
+ * The candidates are built from $USMRAD_APPDIR (default
|
|
|
+ * /app): the `current` link first (the running deployment),
|
|
|
+ * then the concrete a/b slots (a restart racing a flip).
|
|
|
+ * Candidates that duplicate one of the absolute system
|
|
|
+ * shares are dropped — the system path keeps its precedence
|
|
|
+ * and its place in the candidates() order, so a canon
|
|
|
+ * installed at the system path is never shadowed by a slot
|
|
|
+ * copy. The exe's own directory is deliberately NOT an
|
|
|
+ * anchor: /proc/self/exe resolves through the `current`
|
|
|
+ * link, whose `..` climb lands in the appdir, not the slot.
|
|
|
+ */
|
|
|
+ private string[] slot_candidates() {
|
|
|
+ var found = new string[0];
|
|
|
+ var seen = new GLib.GenericSet<string>(GLib.str_hash, GLib.str_equal);
|
|
|
+ seen.add("/usr/share/clanker-saddle/canon");
|
|
|
+ seen.add("/usr/local/share/clanker-saddle/canon");
|
|
|
+ var appdir = (GLib.Environment.get_variable("USMRAD_APPDIR") ?? "")
|
|
|
+ .strip();
|
|
|
+ if (appdir.length == 0) {
|
|
|
+ appdir = "/app";
|
|
|
+ }
|
|
|
+ foreach (var slot in new string[] {"current", "a", "b"}) {
|
|
|
+ var candidate = GLib.Path.build_filename(
|
|
|
+ appdir, slot, "usr", "share", "clanker-saddle", "canon");
|
|
|
+ if (!seen.contains(candidate)) {
|
|
|
+ seen.add(candidate);
|
|
|
+ found += candidate;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return found;
|
|
|
}
|
|
|
|
|
|
/**
|