Deploy.vala 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. using GLib;
  2. namespace Spry.Cli {
  3. /**
  4. * `spry deploy` — packages and deploys the application as a container
  5. * image through USM, delegating to `usm manifest deploy` in the
  6. * application directory.
  7. *
  8. * USM resolves the whole graph inside the image build: the system
  9. * package manager provides the platform libraries and toolchain, the
  10. * configured (or `--repository`-named) USM repositories provide the
  11. * Web-Stack, and the application's own `MANIFEST.usm`/`usm-scripts/`
  12. * (scaffolded by `spry new`) drive its build and install. Flags pass
  13. * through to `usm manifest deploy` verbatim; `--exec` defaults to
  14. * `<app> 8080` so the container serves on the documented port.
  15. * `--system`/`--spm` map to usm's `--base`/`--spm` (see
  16. * {@link Deploy.resolve_system}).
  17. */
  18. public class Deploy : GLib.Object {
  19. /**
  20. * The default USM repository used when no `--repository` is given:
  21. * the hosted web-stack repository on packages.astrologue.nz.
  22. */
  23. public const string DEFAULT_REPOSITORY = "https://packages.astrologue.nz/repo/web-stack/repo.usmr";
  24. /** `--system fedora` base image ({@link Deploy.resolve_system}). */
  25. public const string SYSTEM_FEDORA_BASE = "registry.fedoraproject.org/fedora:latest";
  26. /** `--system debian` base image ({@link Deploy.resolve_system}). */
  27. public const string SYSTEM_DEBIAN_BASE = "docker.io/library/debian:latest";
  28. /** `--system ubuntu` base image ({@link Deploy.resolve_system}). */
  29. public const string SYSTEM_UBUNTU_BASE = "docker.io/library/ubuntu:latest";
  30. /** `--system alpine` base image ({@link Deploy.resolve_system}). */
  31. public const string SYSTEM_ALPINE_BASE = "docker.io/library/alpine:latest";
  32. /** `--system gentoo` base image ({@link Deploy.resolve_system}). */
  33. public const string SYSTEM_GENTOO_BASE = "docker.io/gentoo/stage3:latest";
  34. /**
  35. * Resolves `--system`/`--spm`/`--base` into the base image and SPM
  36. * handed to `usm manifest deploy`.
  37. *
  38. * `--system` is mutually exclusive with `--spm` and an explicit
  39. * `--base` (it expands to both); a combined invocation is reported
  40. * as an error listing the conflicting flags. When none of the three
  41. * is given, spry keeps today's behaviour via the fedora-equivalent
  42. * default: only `--spm dnf` is set and NO base image, so usm's own
  43. * fedora base constant keeps deciding the image and its identity
  44. * is unchanged.
  45. *
  46. * @param system the `--system` value or null
  47. * @param base_image null, or the explicitly passed `--base` value;
  48. * set to the expanded base image on success
  49. * @param spm null, or the explicitly passed `--spm` value; set to
  50. * the expanded SPM name on success
  51. * @return false (after printing the problem) on conflicting or
  52. * unknown flags, true otherwise
  53. */
  54. public static bool resolve_system(string? system, ref string? base_image, ref string? spm) {
  55. if (system != null) {
  56. var conflicts = new string[0];
  57. if (spm != null) {
  58. conflicts += "--spm";
  59. }
  60. if (base_image != null) {
  61. conflicts += "--base";
  62. }
  63. if (conflicts.length > 0) {
  64. stderr.printf("Error: --system %s cannot be combined with %s — --system already expands to a base image and an SPM. Pass either --system, or --spm/--base directly.\n",
  65. system, string.joinv("/", conflicts));
  66. return false;
  67. }
  68. switch (system) {
  69. case "fedora":
  70. base_image = SYSTEM_FEDORA_BASE;
  71. spm = "dnf";
  72. return true;
  73. case "debian":
  74. base_image = SYSTEM_DEBIAN_BASE;
  75. spm = "apt";
  76. return true;
  77. case "ubuntu":
  78. base_image = SYSTEM_UBUNTU_BASE;
  79. spm = "apt";
  80. return true;
  81. case "alpine":
  82. base_image = SYSTEM_ALPINE_BASE;
  83. spm = "apk";
  84. return true;
  85. case "gentoo":
  86. base_image = SYSTEM_GENTOO_BASE;
  87. spm = "emerge";
  88. return true;
  89. default:
  90. stderr.printf("Error: '%s' is not a valid --system value (expected fedora, debian, ubuntu, alpine or gentoo).\n", system);
  91. return false;
  92. }
  93. }
  94. if (base_image == null && spm == null) {
  95. // The fedora-equivalent default deliberately passes no
  96. // --base: usm's own fedora base constant keeps deciding
  97. // the image, so default deploys stay byte-identical
  98. spm = "dnf";
  99. }
  100. return true;
  101. }
  102. /**
  103. * Runs `usm manifest deploy` in {@link app_dir} with the collected
  104. * flags. The USM binary is resolved from `--usm` or PATH; a missing
  105. * binary is reported with install guidance rather than a raw spawn
  106. * error. Returns the delegate process's exit status.
  107. *
  108. * @param spm the `--spm` value resolved by {@link resolve_system},
  109. * or null to let usm default (repo-only resolution)
  110. */
  111. public static int run(string app_dir, string app_name, string? usm_path,
  112. string? exec_command, string? base_image, string? spm, string[] repositories,
  113. string? installer_url, bool no_build, bool verbose = false) throws Error {
  114. var usm = usm_path != null ? (!)usm_path : Environment.find_program_in_path("usm");
  115. if (usm == null) {
  116. stderr.printf("Error: usm not found on PATH — install USM (or pass --usm <path-to-usm>) to deploy.\n");
  117. return 1;
  118. }
  119. string[] argv = { (!)usm, "manifest", "deploy",
  120. "--exec", exec_command ?? @"$app_name 8080" };
  121. if (base_image != null) {
  122. argv += "--base";
  123. argv += base_image;
  124. }
  125. if (spm != null) {
  126. argv += "--spm";
  127. argv += spm;
  128. }
  129. if (repositories.length > 0) {
  130. foreach (var repository in repositories) {
  131. argv += "--repository";
  132. argv += repository;
  133. }
  134. }
  135. else {
  136. // No explicit repositories: default to the hosted web-stack
  137. // repository so stack dependencies resolve out of the box
  138. argv += "--repository";
  139. argv += DEFAULT_REPOSITORY;
  140. }
  141. if (installer_url != null) {
  142. argv += "--installer-url";
  143. argv += installer_url;
  144. }
  145. if (no_build) {
  146. argv += "--no-build";
  147. }
  148. if (verbose) {
  149. argv += "--verbose";
  150. }
  151. var shown = "";
  152. foreach (var word in argv[1:argv.length]) {
  153. shown += (shown.length > 0 ? " " : "") + (word.contains(" ") ? "\"" + word + "\"" : word);
  154. }
  155. stdout.printf("Running: %s\n", shown);
  156. var launcher = new SubprocessLauncher(SubprocessFlags.INHERIT_FDS);
  157. launcher.set_cwd(app_dir);
  158. var process = launcher.spawnv(argv);
  159. try {
  160. process.wait_check();
  161. return 0;
  162. }
  163. catch(Error e) {
  164. return process.get_exit_status();
  165. }
  166. }
  167. }
  168. }