mirror of
https://github.com/jetzig-framework/jetzig.git
synced 2025-05-14 14:06:08 +00:00
Deployment bundle
This needs some work (and testing on Windows) but it solves simple cases and provides a starting point for a more advanced bundler.
This commit is contained in:
parent
cc0a1c5792
commit
09bbcebb56
14
cli/cli.zig
14
cli/cli.zig
@ -4,6 +4,7 @@ const init = @import("commands/init.zig");
|
||||
const update = @import("commands/update.zig");
|
||||
const generate = @import("commands/generate.zig");
|
||||
const server = @import("commands/server.zig");
|
||||
const bundle = @import("commands/bundle.zig");
|
||||
|
||||
const Options = struct {
|
||||
help: bool = false,
|
||||
@ -19,6 +20,7 @@ const Options = struct {
|
||||
.update = "Update current project to latest version of Jetzig",
|
||||
.generate = "Generate scaffolding",
|
||||
.server = "Run a development server",
|
||||
.bundle = "Create a deployment bundle",
|
||||
.help = "Print help and exit",
|
||||
},
|
||||
};
|
||||
@ -29,8 +31,10 @@ const Verb = union(enum) {
|
||||
update: update.Options,
|
||||
generate: generate.Options,
|
||||
server: server.Options,
|
||||
bundle: bundle.Options,
|
||||
g: generate.Options,
|
||||
s: server.Options,
|
||||
b: bundle.Options,
|
||||
};
|
||||
|
||||
/// Main entrypoint for `jetzig` executable. Parses command line args and generates a new
|
||||
@ -52,7 +56,7 @@ pub fn main() !void {
|
||||
}
|
||||
};
|
||||
|
||||
if (options.options.help or options.verb == null) {
|
||||
if ((!options.options.help and options.verb == null) or (options.options.help and options.verb == null)) {
|
||||
try args.printHelp(Options, "jetzig", writer);
|
||||
try writer.writeAll(
|
||||
\\
|
||||
@ -62,6 +66,7 @@ pub fn main() !void {
|
||||
\\ update Update current project to latest version of Jetzig.
|
||||
\\ generate Generate scaffolding.
|
||||
\\ server Run a development server.
|
||||
\\ bundle Create a deployment bundle.
|
||||
\\
|
||||
\\ Pass --help to any command for more information, e.g. `jetzig init --help`
|
||||
\\
|
||||
@ -100,6 +105,13 @@ fn run(allocator: std.mem.Allocator, options: args.ParseArgsResult(Options, Verb
|
||||
options.positionals,
|
||||
.{ .help = options.options.help },
|
||||
),
|
||||
.b, .bundle => |opts| bundle.run(
|
||||
allocator,
|
||||
opts,
|
||||
writer,
|
||||
options.positionals,
|
||||
.{ .help = options.options.help },
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
139
cli/commands/bundle.zig
Normal file
139
cli/commands/bundle.zig
Normal file
@ -0,0 +1,139 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
const args = @import("args");
|
||||
|
||||
const util = @import("../util.zig");
|
||||
|
||||
/// Command line options for the `bundle` command.
|
||||
pub const Options = struct {
|
||||
optimize: enum { Debug, ReleaseFast, ReleaseSmall } = .ReleaseFast,
|
||||
arch: enum { x86_64, aarch64, default } = .default,
|
||||
os: enum { linux, macos, windows, default } = .default,
|
||||
|
||||
pub const meta = .{
|
||||
.full_text =
|
||||
\\Creates a deployment bundle.
|
||||
\\
|
||||
\\On Windows, `tar.exe` is used to generate a `.zip` file.
|
||||
\\
|
||||
\\On other operating systems, `tar` is used to generate a `.tar.gz` file.
|
||||
\\
|
||||
\\The deployment bundle contains a compiled executable with the `public/` and `static/`
|
||||
\\directories included. This bundle can be copied to a deployment server, unpacked, and
|
||||
\\launched in place.
|
||||
,
|
||||
.option_docs = .{
|
||||
.optimize = "Set optimization level, must be one of { Debug, ReleaseFast, ReleaseSmall } (default: ReleaseFast)",
|
||||
.arch = "Set build target CPU architecture, must be one of { x86_64, aarch64 } (default: Current CPU arch)",
|
||||
.os = "Set build target operating system, must be one of { linux, macos, windows } (default: Current OS)",
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/// Run the deployment bundle generator. Create an archive containing the Jetzig executable,
|
||||
/// with `public/` and `static/` directories.
|
||||
pub fn run(
|
||||
allocator: std.mem.Allocator,
|
||||
options: Options,
|
||||
writer: anytype,
|
||||
positionals: [][]const u8,
|
||||
other_options: struct { help: bool },
|
||||
) !void {
|
||||
_ = positionals;
|
||||
if (other_options.help) {
|
||||
try args.printHelp(Options, "jetzig bundle", writer);
|
||||
return;
|
||||
}
|
||||
|
||||
std.debug.print("Compiling bundle...\n", .{});
|
||||
var cwd = try util.detectJetzigProjectDir();
|
||||
defer cwd.close();
|
||||
|
||||
const path = try cwd.realpathAlloc(allocator, ".");
|
||||
defer allocator.free(path);
|
||||
|
||||
if (try util.locateExecutable(allocator, cwd, .{ .relative = true })) |executable| {
|
||||
defer allocator.free(executable);
|
||||
|
||||
var tar_argv = std.ArrayList([]const u8).init(allocator);
|
||||
defer tar_argv.deinit();
|
||||
|
||||
var install_argv = std.ArrayList([]const u8).init(allocator);
|
||||
defer install_argv.deinit();
|
||||
|
||||
try install_argv.appendSlice(&[_][]const u8{ "zig", "build" });
|
||||
|
||||
switch (builtin.os.tag) {
|
||||
.windows => try tar_argv.appendSlice(&[_][]const u8{
|
||||
"tar.exe",
|
||||
"-a",
|
||||
"-c",
|
||||
"-f",
|
||||
"bundle.zip",
|
||||
executable,
|
||||
}),
|
||||
else => try tar_argv.appendSlice(&[_][]const u8{
|
||||
"tar",
|
||||
"--transform=s,^,jetzig/,",
|
||||
"--transform=s,^jetzig/zig-out/bin/,jetzig/,",
|
||||
"-zcf",
|
||||
"bundle.tar.gz",
|
||||
executable,
|
||||
}),
|
||||
}
|
||||
|
||||
switch (options.optimize) {
|
||||
.ReleaseFast => try install_argv.append("-Doptimize=ReleaseFast"),
|
||||
.ReleaseSmall => try install_argv.append("-Doptimize=ReleaseSmall"),
|
||||
.Debug => try install_argv.append("-Doptimize=Debug"),
|
||||
}
|
||||
|
||||
var target_buf = std.ArrayList([]const u8).init(allocator);
|
||||
defer target_buf.deinit();
|
||||
|
||||
try target_buf.append("-Dtarget=");
|
||||
switch (options.arch) {
|
||||
.x86_64 => try target_buf.append("x86_64"),
|
||||
.aarch64 => try target_buf.append("aarch64"),
|
||||
.default => try target_buf.append(@tagName(builtin.cpu.arch)),
|
||||
}
|
||||
|
||||
try target_buf.append("-");
|
||||
|
||||
switch (options.os) {
|
||||
.linux => try target_buf.append("linux"),
|
||||
.macos => try target_buf.append("macos"),
|
||||
.windows => try target_buf.append("windows"),
|
||||
.default => try target_buf.append(@tagName(builtin.os.tag)),
|
||||
}
|
||||
|
||||
const target = try std.mem.concat(allocator, u8, target_buf.items);
|
||||
defer allocator.free(target);
|
||||
|
||||
try install_argv.append(target);
|
||||
try install_argv.append("install");
|
||||
|
||||
var public_dir: ?std.fs.Dir = cwd.openDir("public", .{}) catch null;
|
||||
defer if (public_dir) |*dir| dir.close();
|
||||
|
||||
var static_dir: ?std.fs.Dir = cwd.openDir("static", .{}) catch null;
|
||||
defer if (static_dir) |*dir| dir.close();
|
||||
|
||||
if (public_dir != null) try tar_argv.append("public");
|
||||
if (static_dir != null) try tar_argv.append("static");
|
||||
|
||||
try util.runCommand(allocator, path, install_argv.items);
|
||||
try util.runCommand(allocator, path, tar_argv.items);
|
||||
|
||||
switch (builtin.os.tag) {
|
||||
.windows => std.debug.print("Bundle `bundle.zip` generated successfully.", .{}),
|
||||
else => std.debug.print("Bundle `bundle.tar.gz` generated successfully.", .{}),
|
||||
}
|
||||
util.printSuccess();
|
||||
} else {
|
||||
std.debug.print("Unable to locate compiled executable. Exiting.", .{});
|
||||
util.printFailure();
|
||||
std.os.exit(1);
|
||||
}
|
||||
}
|
@ -1,11 +1,12 @@
|
||||
const std = @import("std");
|
||||
|
||||
const args = @import("args");
|
||||
|
||||
const util = @import("../util.zig");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
pub const watch_changes_pause_duration = 1 * 1000 * 1000 * 1000;
|
||||
|
||||
/// Command line options for the `update` command.
|
||||
/// Command line options for the `server` command.
|
||||
pub const Options = struct {
|
||||
reload: bool = true,
|
||||
|
||||
@ -68,7 +69,7 @@ pub fn run(
|
||||
&[_][]const u8{ "zig", "build", "-Djetzig_runner=true", "install" },
|
||||
);
|
||||
|
||||
const exe_path = try locateExecutable(allocator, cwd);
|
||||
const exe_path = try util.locateExecutable(allocator, cwd, .{});
|
||||
if (exe_path == null) {
|
||||
std.debug.print("Unable to locate compiled executable. Exiting.\n", .{});
|
||||
std.os.exit(1);
|
||||
@ -137,34 +138,3 @@ fn totalMtime(allocator: std.mem.Allocator, cwd: std.fs.Dir, sub_path: []const u
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
fn locateExecutable(allocator: std.mem.Allocator, dir: std.fs.Dir) !?[]const u8 {
|
||||
const file = dir.openFile(".jetzig", .{}) catch |err| {
|
||||
switch (err) {
|
||||
error.FileNotFound => return null,
|
||||
else => return err,
|
||||
}
|
||||
};
|
||||
const content = try file.readToEndAlloc(allocator, 1024);
|
||||
defer allocator.free(content);
|
||||
|
||||
const exe_name = util.strip(content);
|
||||
const suffix = if (builtin.os.tag == .windows) ".exe" else "";
|
||||
const full_name = try std.mem.concat(allocator, u8, &[_][]const u8{ exe_name, suffix });
|
||||
defer allocator.free(full_name);
|
||||
|
||||
// XXX: Will fail if user sets a custom install path.
|
||||
var bin_dir = try dir.openDir("zig-out/bin", .{ .iterate = true });
|
||||
defer bin_dir.close();
|
||||
|
||||
var walker = try bin_dir.walk(allocator);
|
||||
defer walker.deinit();
|
||||
|
||||
while (try walker.next()) |entry| {
|
||||
if (entry.kind == .file and std.mem.eql(u8, entry.path, full_name)) {
|
||||
return try bin_dir.realpathAlloc(allocator, entry.path);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
41
cli/util.zig
41
cli/util.zig
@ -1,4 +1,5 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
/// Decode a base64 string, used for parsing out build artifacts generated by the CLI program's
|
||||
/// build.zig which are stored in the executable as a module.
|
||||
@ -167,3 +168,43 @@ pub fn githubUrl(allocator: std.mem.Allocator) ![]const u8 {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Attempt to locate the main application executable in `zig-out/bin/`
|
||||
pub fn locateExecutable(
|
||||
allocator: std.mem.Allocator,
|
||||
dir: std.fs.Dir,
|
||||
options: struct { relative: bool = false },
|
||||
) !?[]const u8 {
|
||||
const file = dir.openFile(".jetzig", .{}) catch |err| {
|
||||
switch (err) {
|
||||
error.FileNotFound => return null,
|
||||
else => return err,
|
||||
}
|
||||
};
|
||||
const content = try file.readToEndAlloc(allocator, 1024);
|
||||
defer allocator.free(content);
|
||||
|
||||
const exe_name = strip(content);
|
||||
const suffix = if (builtin.os.tag == .windows) ".exe" else "";
|
||||
const full_name = try std.mem.concat(allocator, u8, &[_][]const u8{ exe_name, suffix });
|
||||
defer allocator.free(full_name);
|
||||
|
||||
// XXX: Will fail if user sets a custom install path.
|
||||
var bin_dir = try dir.openDir("zig-out/bin", .{ .iterate = true });
|
||||
defer bin_dir.close();
|
||||
|
||||
var walker = try bin_dir.walk(allocator);
|
||||
defer walker.deinit();
|
||||
|
||||
while (try walker.next()) |entry| {
|
||||
if (entry.kind == .file and std.mem.eql(u8, entry.path, full_name)) {
|
||||
if (options.relative) {
|
||||
return try std.fs.path.join(allocator, &[_][]const u8{ "zig-out", "bin", entry.path });
|
||||
} else {
|
||||
return try bin_dir.realpathAlloc(allocator, entry.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user