Deploy.vala 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  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. /** `--system fedora` base image ({@link Deploy.resolve_system}). */
  20. public const string SYSTEM_FEDORA_BASE = "registry.fedoraproject.org/fedora:latest";
  21. /** `--system debian` base image ({@link Deploy.resolve_system}). */
  22. public const string SYSTEM_DEBIAN_BASE = "docker.io/library/debian:latest";
  23. /** `--system ubuntu` base image ({@link Deploy.resolve_system}). */
  24. public const string SYSTEM_UBUNTU_BASE = "docker.io/library/ubuntu:latest";
  25. /** `--system alpine` base image ({@link Deploy.resolve_system}). */
  26. public const string SYSTEM_ALPINE_BASE = "docker.io/library/alpine:latest";
  27. /** `--system gentoo` base image ({@link Deploy.resolve_system}). */
  28. public const string SYSTEM_GENTOO_BASE = "docker.io/gentoo/stage3:latest";
  29. /**
  30. * Resolves `--system`/`--spm`/`--base` into the base image and SPM
  31. * handed to `usm manifest deploy`.
  32. *
  33. * `--system` is mutually exclusive with `--spm` and an explicit
  34. * `--base` (it expands to both); a combined invocation is reported
  35. * as an error listing the conflicting flags. When none of the three
  36. * is given, spry keeps today's behaviour via the fedora-equivalent
  37. * default: only `--spm dnf` is set and NO base image, so usm's own
  38. * fedora base constant keeps deciding the image and its identity
  39. * is unchanged.
  40. *
  41. * @param system the `--system` value or null
  42. * @param base_image null, or the explicitly passed `--base` value;
  43. * set to the expanded base image on success
  44. * @param spm null, or the explicitly passed `--spm` value; set to
  45. * the expanded SPM name on success
  46. * @return false (after printing the problem) on conflicting or
  47. * unknown flags, true otherwise
  48. */
  49. public static bool resolve_system(string? system, ref string? base_image, ref string? spm) {
  50. if (system != null) {
  51. var conflicts = new string[0];
  52. if (spm != null) {
  53. conflicts += "--spm";
  54. }
  55. if (base_image != null) {
  56. conflicts += "--base";
  57. }
  58. if (conflicts.length > 0) {
  59. 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",
  60. system, string.joinv("/", conflicts));
  61. return false;
  62. }
  63. switch (system) {
  64. case "fedora":
  65. base_image = SYSTEM_FEDORA_BASE;
  66. spm = "dnf";
  67. return true;
  68. case "debian":
  69. base_image = SYSTEM_DEBIAN_BASE;
  70. spm = "apt";
  71. return true;
  72. case "ubuntu":
  73. base_image = SYSTEM_UBUNTU_BASE;
  74. spm = "apt";
  75. return true;
  76. case "alpine":
  77. base_image = SYSTEM_ALPINE_BASE;
  78. spm = "apk";
  79. return true;
  80. case "gentoo":
  81. base_image = SYSTEM_GENTOO_BASE;
  82. spm = "emerge";
  83. return true;
  84. default:
  85. stderr.printf("Error: '%s' is not a valid --system value (expected fedora, debian, ubuntu, alpine or gentoo).\n", system);
  86. return false;
  87. }
  88. }
  89. if (base_image == null && spm == null) {
  90. // The fedora-equivalent default deliberately passes no
  91. // --base: usm's own fedora base constant keeps deciding
  92. // the image, so default deploys stay byte-identical
  93. spm = "dnf";
  94. }
  95. return true;
  96. }
  97. /**
  98. * Runs `usm manifest deploy` in {@link app_dir} with the collected
  99. * flags. The USM binary is resolved from `--usm` or PATH; a missing
  100. * binary is reported with install guidance rather than a raw spawn
  101. * error. Returns the delegate process's exit status.
  102. *
  103. * @param spm the `--spm` value resolved by {@link resolve_system},
  104. * or null to let usm default (repo-only resolution)
  105. */
  106. public static int run(string app_dir, string app_name, string? usm_path,
  107. string? exec_command, string? base_image, string? spm, string[] repositories,
  108. string? installer_url, bool no_build, bool verbose = false) throws Error {
  109. var usm = usm_path != null ? (!)usm_path : Environment.find_program_in_path("usm");
  110. if (usm == null) {
  111. stderr.printf("Error: usm not found on PATH — install USM (or pass --usm <path-to-usm>) to deploy.\n");
  112. return 1;
  113. }
  114. string[] argv = { (!)usm, "manifest", "deploy",
  115. "--exec", exec_command ?? @"$app_name 8080" };
  116. if (base_image != null) {
  117. argv += "--base";
  118. argv += base_image;
  119. }
  120. if (spm != null) {
  121. argv += "--spm";
  122. argv += spm;
  123. }
  124. foreach (var repository in repositories) {
  125. argv += "--repository";
  126. argv += repository;
  127. }
  128. if (installer_url != null) {
  129. argv += "--installer-url";
  130. argv += installer_url;
  131. }
  132. if (no_build) {
  133. argv += "--no-build";
  134. }
  135. if (verbose) {
  136. argv += "--verbose";
  137. }
  138. var shown = "";
  139. foreach (var word in argv[1:argv.length]) {
  140. shown += (shown.length > 0 ? " " : "") + (word.contains(" ") ? "\"" + word + "\"" : word);
  141. }
  142. stdout.printf("Running: %s\n", shown);
  143. var launcher = new SubprocessLauncher(SubprocessFlags.INHERIT_FDS);
  144. launcher.set_cwd(app_dir);
  145. var process = launcher.spawnv(argv);
  146. try {
  147. process.wait_check();
  148. return 0;
  149. }
  150. catch(Error e) {
  151. return process.get_exit_status();
  152. }
  153. }
  154. }
  155. }