| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- using GLib;
- namespace Spry.Cli {
- /**
- * `spry deploy` — packages and deploys the application as a container
- * image through USM, delegating to `usm manifest deploy` in the
- * application directory.
- *
- * USM resolves the whole graph inside the image build: the system
- * package manager provides the platform libraries and toolchain, the
- * configured (or `--repository`-named) USM repositories provide the
- * Web-Stack, and the application's own `MANIFEST.usm`/`usm-scripts/`
- * (scaffolded by `spry new`) drive its build and install. Flags pass
- * through to `usm manifest deploy` verbatim; `--exec` defaults to
- * `<app> 8080` so the container serves on the documented port.
- * `--system`/`--spm` map to usm's `--base`/`--spm` (see
- * {@link Deploy.resolve_system}).
- */
- public class Deploy : GLib.Object {
- /**
- * The default USM repository used when no `--repository` is given:
- * the hosted web-stack repository on packages.astrologue.nz.
- */
- public const string DEFAULT_REPOSITORY = "https://packages.astrologue.nz/repo/web-stack/repo.usmr";
- /** `--system fedora` base image ({@link Deploy.resolve_system}). */
- public const string SYSTEM_FEDORA_BASE = "registry.fedoraproject.org/fedora:latest";
- /** `--system debian` base image ({@link Deploy.resolve_system}). */
- public const string SYSTEM_DEBIAN_BASE = "docker.io/library/debian:latest";
- /** `--system ubuntu` base image ({@link Deploy.resolve_system}). */
- public const string SYSTEM_UBUNTU_BASE = "docker.io/library/ubuntu:latest";
- /** `--system alpine` base image ({@link Deploy.resolve_system}). */
- public const string SYSTEM_ALPINE_BASE = "docker.io/library/alpine:latest";
- /** `--system gentoo` base image ({@link Deploy.resolve_system}). */
- public const string SYSTEM_GENTOO_BASE = "docker.io/gentoo/stage3:latest";
- /**
- * Resolves `--system`/`--spm`/`--base` into the base image and SPM
- * handed to `usm manifest deploy`.
- *
- * `--system` is mutually exclusive with `--spm` and an explicit
- * `--base` (it expands to both); a combined invocation is reported
- * as an error listing the conflicting flags. When none of the three
- * is given, spry keeps today's behaviour via the fedora-equivalent
- * default: only `--spm dnf` is set and NO base image, so usm's own
- * fedora base constant keeps deciding the image and its identity
- * is unchanged.
- *
- * @param system the `--system` value or null
- * @param base_image null, or the explicitly passed `--base` value;
- * set to the expanded base image on success
- * @param spm null, or the explicitly passed `--spm` value; set to
- * the expanded SPM name on success
- * @return false (after printing the problem) on conflicting or
- * unknown flags, true otherwise
- */
- public static bool resolve_system(string? system, ref string? base_image, ref string? spm) {
- if (system != null) {
- var conflicts = new string[0];
- if (spm != null) {
- conflicts += "--spm";
- }
- if (base_image != null) {
- conflicts += "--base";
- }
- if (conflicts.length > 0) {
- 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",
- system, string.joinv("/", conflicts));
- return false;
- }
- switch (system) {
- case "fedora":
- base_image = SYSTEM_FEDORA_BASE;
- spm = "dnf";
- return true;
- case "debian":
- base_image = SYSTEM_DEBIAN_BASE;
- spm = "apt";
- return true;
- case "ubuntu":
- base_image = SYSTEM_UBUNTU_BASE;
- spm = "apt";
- return true;
- case "alpine":
- base_image = SYSTEM_ALPINE_BASE;
- spm = "apk";
- return true;
- case "gentoo":
- base_image = SYSTEM_GENTOO_BASE;
- spm = "emerge";
- return true;
- default:
- stderr.printf("Error: '%s' is not a valid --system value (expected fedora, debian, ubuntu, alpine or gentoo).\n", system);
- return false;
- }
- }
- if (base_image == null && spm == null) {
- // The fedora-equivalent default deliberately passes no
- // --base: usm's own fedora base constant keeps deciding
- // the image, so default deploys stay byte-identical
- spm = "dnf";
- }
- return true;
- }
- /**
- * Runs `usm manifest deploy` in {@link app_dir} with the collected
- * flags. The USM binary is resolved from `--usm` or PATH; a missing
- * binary is reported with install guidance rather than a raw spawn
- * error. Returns the delegate process's exit status.
- *
- * @param spm the `--spm` value resolved by {@link resolve_system},
- * or null to let usm default (repo-only resolution)
- */
- public static int run(string app_dir, string app_name, string? usm_path,
- string? exec_command, string? base_image, string? spm, string[] repositories,
- string? installer_url, bool no_build, bool verbose = false) throws Error {
- var usm = usm_path != null ? (!)usm_path : Environment.find_program_in_path("usm");
- if (usm == null) {
- stderr.printf("Error: usm not found on PATH — install USM (or pass --usm <path-to-usm>) to deploy.\n");
- return 1;
- }
- string[] argv = { (!)usm, "manifest", "deploy",
- "--exec", exec_command ?? @"$app_name 8080" };
- if (base_image != null) {
- argv += "--base";
- argv += base_image;
- }
- if (spm != null) {
- argv += "--spm";
- argv += spm;
- }
- if (repositories.length > 0) {
- foreach (var repository in repositories) {
- argv += "--repository";
- argv += repository;
- }
- }
- else {
- // No explicit repositories: default to the hosted web-stack
- // repository so stack dependencies resolve out of the box
- argv += "--repository";
- argv += DEFAULT_REPOSITORY;
- }
- if (installer_url != null) {
- argv += "--installer-url";
- argv += installer_url;
- }
- if (no_build) {
- argv += "--no-build";
- }
- if (verbose) {
- argv += "--verbose";
- }
- var shown = "";
- foreach (var word in argv[1:argv.length]) {
- shown += (shown.length > 0 ? " " : "") + (word.contains(" ") ? "\"" + word + "\"" : word);
- }
- stdout.printf("Running: %s\n", shown);
- var launcher = new SubprocessLauncher(SubprocessFlags.INHERIT_FDS);
- launcher.set_cwd(app_dir);
- var process = launcher.spawnv(argv);
- try {
- process.wait_check();
- return 0;
- }
- catch(Error e) {
- return process.get_exit_status();
- }
- }
- }
- }
|