|
|
@@ -5,13 +5,14 @@ namespace Spry.Cli {
|
|
|
/**
|
|
|
* `spry` — the Spry application CLI.
|
|
|
*
|
|
|
- * Scaffolds Statum + Spry applications (`spry new`), grows them with
|
|
|
- * app-owned page/action/resource/auth templates (`spry add …`), maintains
|
|
|
- * the static key material in `web-config.json` (`spry keys`), and
|
|
|
- * generates/builds the multi-stage Docker image (`spry docker`). Edits to
|
|
|
- * generated projects happen only within `// spry:*-begin/end` markers in
|
|
|
- * `src/main.vala` and `# spry:*-begin/end` markers in `meson.build`, so
|
|
|
- * `add` commands are idempotent and never reformat surrounding user code.
|
|
|
+ * Scaffolds Statum + Spry applications (`spry new`, including their USM
|
|
|
+ * packaging), grows them with app-owned page/action/resource/auth
|
|
|
+ * templates (`spry add …`), maintains the static key material in
|
|
|
+ * `web-config.json` (`spry keys`), and ships them as container images
|
|
|
+ * through USM (`spry deploy`). Edits to generated projects happen only
|
|
|
+ * within `// spry:*-begin/end` markers in `src/main.vala` and
|
|
|
+ * `# spry:*-begin/end` markers in `meson.build`, so `add` commands are
|
|
|
+ * idempotent and never reformat surrounding user code.
|
|
|
*/
|
|
|
public class SpryCli : Object {
|
|
|
|
|
|
@@ -20,9 +21,14 @@ namespace Spry.Cli {
|
|
|
private static bool force = false;
|
|
|
private static string? route = null;
|
|
|
private static string? out_path = null;
|
|
|
- private static string? tag = null;
|
|
|
- private static bool no_build = false;
|
|
|
- private static string? stack_dir = null;
|
|
|
+ private static string? usm = null;
|
|
|
+ private static string? deploy_exec = null;
|
|
|
+ private static string? deploy_base = null;
|
|
|
+ [CCode (array_length = false, null_terminated = true)]
|
|
|
+ private static string[] deploy_repositories = null;
|
|
|
+ private static string? deploy_installer_url = null;
|
|
|
+ private static bool deploy_no_build = false;
|
|
|
+ private static bool deploy_verbose = false;
|
|
|
private static int dev_port = 8080;
|
|
|
private static bool dev_fresh = false;
|
|
|
private static bool dev_no_run = false;
|
|
|
@@ -48,10 +54,14 @@ namespace Spry.Cli {
|
|
|
{ null }
|
|
|
};
|
|
|
|
|
|
- private const OptionEntry[] docker_options = {
|
|
|
- { "tag", 't', 0, OptionArg.STRING, ref tag, "Image tag (default: the application name)", "TAG" },
|
|
|
- { "no-build", '\0', 0, OptionArg.NONE, ref no_build, "Only generate Dockerfile/.dockerignore; do not run the image build", null },
|
|
|
- { "stack-dir", '\0', 0, OptionArg.FILENAME, ref stack_dir, "Web-Stack checkout to COPY Statum/Spry from (default: the app's sibling directory)", "DIR" },
|
|
|
+ private const OptionEntry[] deploy_options = {
|
|
|
+ { "usm", '\0', 0, OptionArg.FILENAME, ref usm, "usm binary to delegate to (default: search PATH)", "FILE" },
|
|
|
+ { "exec", '\0', 0, OptionArg.STRING, ref deploy_exec, "Container command (default: \"<app> 8080\")", "CMD" },
|
|
|
+ { "base", '\0', 0, OptionArg.STRING, ref deploy_base, "Base image (default: usm's fedora:43)", "IMAGE" },
|
|
|
+ { "repository", '\0', 0, OptionArg.STRING_ARRAY, ref deploy_repositories, "USM repository (.usmr) to resolve from; repeatable (default: the machine's configured repositories)", "FILE" },
|
|
|
+ { "installer-url", '\0', 0, OptionArg.STRING, ref deploy_installer_url, "USM installer source override (file://… for local testing)", "URL" },
|
|
|
+ { "no-build", '\0', 0, OptionArg.NONE, ref deploy_no_build, "Only generate the deploy context; do not build the image", null },
|
|
|
+ { "verbose", 'v', 0, OptionArg.NONE, ref deploy_verbose, "Stream package script output to the terminal during the image build", null },
|
|
|
{ null }
|
|
|
};
|
|
|
|
|
|
@@ -102,8 +112,8 @@ namespace Spry.Cli {
|
|
|
return cmd_add(slice(args, 1));
|
|
|
case "keys":
|
|
|
return cmd_keys(slice(args, 1));
|
|
|
- case "docker":
|
|
|
- return cmd_docker(slice(args, 1));
|
|
|
+ case "deploy":
|
|
|
+ return cmd_deploy(slice(args, 1));
|
|
|
case "dev":
|
|
|
return cmd_dev(slice(args, 1));
|
|
|
default:
|
|
|
@@ -134,7 +144,9 @@ namespace Spry.Cli {
|
|
|
" add register Add the registration page\n" +
|
|
|
" add user-management Add the admin user-management page\n" +
|
|
|
" keys [--out FILE] Generate/merge static keys into web-config.json\n" +
|
|
|
- " docker [--tag T] [--no-build] Generate (and optionally build) a Dockerfile\n" +
|
|
|
+ " deploy [--exec CMD] [--base I] Build the USM container image (usm manifest\n" +
|
|
|
+ " deploy; --repository/--installer-url/\n" +
|
|
|
+ " --no-build pass through)\n" +
|
|
|
" dev [--port N] [--fresh] Build, run and watch: rebuild + restart on save\n" +
|
|
|
"\n" +
|
|
|
"Options:\n" +
|
|
|
@@ -185,7 +197,13 @@ namespace Spry.Cli {
|
|
|
write_app_file(app_dir, "src/entrypoints/HomeEntrypoint.vala", Generator.fill(Templates.HOME_ENTRYPOINT, vars));
|
|
|
write_app_file(app_dir, ".gitignore", Generator.fill(Templates.GITIGNORE, vars));
|
|
|
write_app_file(app_dir, "README.md", Generator.fill(Templates.README, vars));
|
|
|
- Docker.generate(app_dir, name);
|
|
|
+
|
|
|
+ // USM packaging: the manifest + scripts that let `spry deploy`
|
|
|
+ // (usm manifest deploy) build and install the app in-container
|
|
|
+ write_app_file(app_dir, "MANIFEST.usm", Generator.fill(Templates.MANIFEST_USM, vars));
|
|
|
+ write_app_file(app_dir, "usm-scripts/build.sh", Templates.USM_BUILD_SH, 0755);
|
|
|
+ write_app_file(app_dir, "usm-scripts/install.sh", Templates.USM_INSTALL_SH, 0755);
|
|
|
+ write_app_file(app_dir, ".usmignore", Templates.USMIGNORE);
|
|
|
|
|
|
var config_path = Path.build_filename(app_dir, "web-config.json");
|
|
|
try {
|
|
|
@@ -378,7 +396,7 @@ namespace Spry.Cli {
|
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
- // spry keys / spry docker
|
|
|
+ // spry keys / spry deploy
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
|
|
private static int cmd_keys(string[] args) throws Error, OptionError {
|
|
|
@@ -387,16 +405,41 @@ namespace Spry.Cli {
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
- private static int cmd_docker(string[] args) throws Error, OptionError {
|
|
|
- parse(args, "- generate (and optionally build) the Dockerfile", docker_options);
|
|
|
+ private static int cmd_deploy(string[] args) throws Error, OptionError {
|
|
|
+ string[] repositories;
|
|
|
+ string[] remaining;
|
|
|
+ extract_repositories(args, out repositories, out remaining);
|
|
|
+ parse(remaining, "- build the USM container image via usm manifest deploy", deploy_options);
|
|
|
var app = require_app();
|
|
|
- Docker.generate(app.dir, app.name);
|
|
|
- if (no_build) {
|
|
|
- return 0;
|
|
|
+ return Deploy.run(app.dir, app.name, usm, deploy_exec, deploy_base,
|
|
|
+ repositories, deploy_installer_url, deploy_no_build, deploy_verbose);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Collects repeatable `--repository FILE` / `--repository=FILE`
|
|
|
+ * arguments into {@link repositories}, leaving the rest in
|
|
|
+ * {@link remaining} so the GLib option context (whose string-array
|
|
|
+ * capture is unreliable here) never sees them.
|
|
|
+ */
|
|
|
+ private static void extract_repositories(string[] args, out string[] repositories, out string[] remaining) {
|
|
|
+ var found = new string[0];
|
|
|
+ var rest = new string[args.length];
|
|
|
+ rest[0] = args[0];
|
|
|
+ int out_index = 1;
|
|
|
+ for (int i = 1; i < args.length; i++) {
|
|
|
+ if (args[i] == "--repository" && i + 1 < args.length) {
|
|
|
+ found += args[++i];
|
|
|
+ }
|
|
|
+ else if (args[i].has_prefix("--repository=")) {
|
|
|
+ found += args[i].substring("--repository=".length);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ rest[out_index++] = args[i];
|
|
|
+ }
|
|
|
}
|
|
|
- var stack = stack_dir ?? Path.get_dirname(app.dir);
|
|
|
- Docker.build(app.dir, app.name, tag ?? app.name, stack);
|
|
|
- return 0;
|
|
|
+ rest.length = out_index;
|
|
|
+ repositories = found;
|
|
|
+ remaining = rest;
|
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
@@ -497,8 +540,8 @@ namespace Spry.Cli {
|
|
|
Generator.write_file(path, Generator.insert_marked(Generator.read_file(path), begin, end, snippet));
|
|
|
}
|
|
|
|
|
|
- private static void write_app_file(string app_dir, string relative, string contents) throws Error {
|
|
|
- Generator.write_file(Path.build_filename(app_dir, relative), contents);
|
|
|
+ private static void write_app_file(string app_dir, string relative, string contents, uint32 mode = 0644) throws Error {
|
|
|
+ Generator.write_file(Path.build_filename(app_dir, relative), contents, mode);
|
|
|
}
|
|
|
|
|
|
/**
|