mirror of
https://github.com/jetzig-framework/jetzig.git
synced 2025-05-14 14:06:08 +00:00
Add update command to CLI tool
Automatically update to latest version of Jetzig with `jetzig update`.
This commit is contained in:
parent
2a25084de6
commit
bec5f9c905
11
cli/cli.zig
11
cli/cli.zig
@ -1,6 +1,7 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const args = @import("args");
|
const args = @import("args");
|
||||||
const init = @import("commands/init.zig");
|
const init = @import("commands/init.zig");
|
||||||
|
const update = @import("commands/update.zig");
|
||||||
const generate = @import("commands/generate.zig");
|
const generate = @import("commands/generate.zig");
|
||||||
|
|
||||||
const Options = struct {
|
const Options = struct {
|
||||||
@ -14,6 +15,7 @@ const Options = struct {
|
|||||||
.usage_summary = "[COMMAND]",
|
.usage_summary = "[COMMAND]",
|
||||||
.option_docs = .{
|
.option_docs = .{
|
||||||
.init = "Initialize a new project",
|
.init = "Initialize a new project",
|
||||||
|
.update = "Update current project to latest version of Jetzig",
|
||||||
.generate = "Generate scaffolding",
|
.generate = "Generate scaffolding",
|
||||||
.help = "Print help and exit",
|
.help = "Print help and exit",
|
||||||
},
|
},
|
||||||
@ -22,6 +24,7 @@ const Options = struct {
|
|||||||
|
|
||||||
const Verb = union(enum) {
|
const Verb = union(enum) {
|
||||||
init: init.Options,
|
init: init.Options,
|
||||||
|
update: update.Options,
|
||||||
generate: generate.Options,
|
generate: generate.Options,
|
||||||
g: generate.Options,
|
g: generate.Options,
|
||||||
};
|
};
|
||||||
@ -52,6 +55,7 @@ pub fn main() !void {
|
|||||||
\\Commands:
|
\\Commands:
|
||||||
\\
|
\\
|
||||||
\\ init Initialize a new project.
|
\\ init Initialize a new project.
|
||||||
|
\\ update Update current project to latest version of Jetzig.
|
||||||
\\ generate Generate scaffolding.
|
\\ generate Generate scaffolding.
|
||||||
\\
|
\\
|
||||||
\\ Pass --help to any command for more information, e.g. `jetzig init --help`
|
\\ Pass --help to any command for more information, e.g. `jetzig init --help`
|
||||||
@ -77,6 +81,13 @@ fn run(allocator: std.mem.Allocator, options: args.ParseArgsResult(Options, Verb
|
|||||||
options.positionals,
|
options.positionals,
|
||||||
.{ .help = options.options.help },
|
.{ .help = options.options.help },
|
||||||
),
|
),
|
||||||
|
.update => |opts| update.run(
|
||||||
|
allocator,
|
||||||
|
opts,
|
||||||
|
writer,
|
||||||
|
options.positionals,
|
||||||
|
.{ .help = options.options.help },
|
||||||
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ pub fn run(
|
|||||||
install_path = arg;
|
install_path = arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
const github_url = try githubUrl(allocator);
|
const github_url = try util.githubUrl(allocator);
|
||||||
defer allocator.free(github_url);
|
defer allocator.free(github_url);
|
||||||
|
|
||||||
if (other_options.help) {
|
if (other_options.help) {
|
||||||
@ -87,10 +87,10 @@ pub fn run(
|
|||||||
install_dir = try std.fs.cwd().makeOpenPath(input_install_path, .{});
|
install_dir = try std.fs.cwd().makeOpenPath(input_install_path, .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
const real_path = try install_dir.realpathAlloc(allocator, ".");
|
const realpath = try install_dir.realpathAlloc(allocator, ".");
|
||||||
defer allocator.free(real_path);
|
defer allocator.free(realpath);
|
||||||
|
|
||||||
const output = try std.fmt.allocPrint(allocator, "Creating new project in {s}\n\n", .{real_path});
|
const output = try std.fmt.allocPrint(allocator, "Creating new project in {s}\n\n", .{realpath});
|
||||||
defer allocator.free(output);
|
defer allocator.free(output);
|
||||||
try writer.writeAll(output);
|
try writer.writeAll(output);
|
||||||
|
|
||||||
@ -184,7 +184,7 @@ pub fn run(
|
|||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
|
|
||||||
try runCommand(allocator, real_path, &[_][]const u8{
|
try util.runCommand(allocator, realpath, &[_][]const u8{
|
||||||
"zig",
|
"zig",
|
||||||
"fetch",
|
"fetch",
|
||||||
"--save",
|
"--save",
|
||||||
@ -208,38 +208,7 @@ pub fn run(
|
|||||||
\\And then browse to http://localhost:8080/
|
\\And then browse to http://localhost:8080/
|
||||||
\\
|
\\
|
||||||
\\
|
\\
|
||||||
, .{real_path});
|
, .{realpath});
|
||||||
}
|
|
||||||
|
|
||||||
fn runCommand(allocator: std.mem.Allocator, install_path: []const u8, argv: []const []const u8) !void {
|
|
||||||
const result = try std.process.Child.run(.{ .allocator = allocator, .argv = argv, .cwd = install_path });
|
|
||||||
defer allocator.free(result.stdout);
|
|
||||||
defer allocator.free(result.stderr);
|
|
||||||
|
|
||||||
const command = try std.mem.join(allocator, " ", argv);
|
|
||||||
defer allocator.free(command);
|
|
||||||
|
|
||||||
std.debug.print("[exec] {s}", .{command});
|
|
||||||
|
|
||||||
if (result.term.Exited != 0) {
|
|
||||||
util.printFailure();
|
|
||||||
std.debug.print(
|
|
||||||
\\
|
|
||||||
\\Error running command: {s}
|
|
||||||
\\
|
|
||||||
\\[stdout]:
|
|
||||||
\\
|
|
||||||
\\{s}
|
|
||||||
\\
|
|
||||||
\\[stderr]:
|
|
||||||
\\
|
|
||||||
\\{s}
|
|
||||||
\\
|
|
||||||
, .{ command, result.stdout, result.stderr });
|
|
||||||
return error.JetzigCommandError;
|
|
||||||
} else {
|
|
||||||
util.printSuccess();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Replace = struct {
|
const Replace = struct {
|
||||||
@ -308,47 +277,6 @@ fn writeSourceFile(install_dir: std.fs.Dir, path: []const u8, content: []const u
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a full GitHub URL for passing to `zig fetch`.
|
|
||||||
fn githubUrl(allocator: std.mem.Allocator) ![]const u8 {
|
|
||||||
var client = std.http.Client{ .allocator = allocator };
|
|
||||||
defer client.deinit();
|
|
||||||
|
|
||||||
const url = "https://api.github.com/repos/jetzig-framework/jetzig/branches/main";
|
|
||||||
const extra_headers = &[_]std.http.Header{.{ .name = "X-GitHub-Api-Version", .value = "2022-11-28" }};
|
|
||||||
|
|
||||||
var response_storage = std.ArrayList(u8).init(allocator);
|
|
||||||
defer response_storage.deinit();
|
|
||||||
|
|
||||||
const fetch_result = try client.fetch(.{
|
|
||||||
.location = .{ .url = url },
|
|
||||||
.extra_headers = extra_headers,
|
|
||||||
.response_storage = .{ .dynamic = &response_storage },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (fetch_result.status != .ok) {
|
|
||||||
std.debug.print("Error fetching from GitHub: {s}\n", .{url});
|
|
||||||
return error.JetzigCommandError;
|
|
||||||
}
|
|
||||||
|
|
||||||
const parsed_response = try std.json.parseFromSlice(
|
|
||||||
struct { commit: struct { sha: []const u8 } },
|
|
||||||
allocator,
|
|
||||||
response_storage.items,
|
|
||||||
.{ .ignore_unknown_fields = true },
|
|
||||||
);
|
|
||||||
defer parsed_response.deinit();
|
|
||||||
|
|
||||||
return try std.mem.concat(
|
|
||||||
allocator,
|
|
||||||
u8,
|
|
||||||
&[_][]const u8{
|
|
||||||
"https://github.com/jetzig-framework/jetzig/archive/",
|
|
||||||
parsed_response.value.commit.sha,
|
|
||||||
".tar.gz",
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prompt a user for input and return the result. Accepts an optional default value.
|
// Prompt a user for input and return the result. Accepts an optional default value.
|
||||||
fn promptInput(
|
fn promptInput(
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
@ -384,19 +312,19 @@ fn promptInput(
|
|||||||
|
|
||||||
// Initialize a new Git repository when setting up a new project (optional).
|
// Initialize a new Git repository when setting up a new project (optional).
|
||||||
fn gitSetup(allocator: std.mem.Allocator, install_dir: *std.fs.Dir) !void {
|
fn gitSetup(allocator: std.mem.Allocator, install_dir: *std.fs.Dir) !void {
|
||||||
try runCommand(allocator, install_dir, &[_][]const u8{
|
try util.runCommand(allocator, install_dir, &[_][]const u8{
|
||||||
"git",
|
"git",
|
||||||
"init",
|
"init",
|
||||||
".",
|
".",
|
||||||
});
|
});
|
||||||
|
|
||||||
try runCommand(allocator, install_dir, &[_][]const u8{
|
try util.runCommand(allocator, install_dir, &[_][]const u8{
|
||||||
"git",
|
"git",
|
||||||
"add",
|
"add",
|
||||||
".",
|
".",
|
||||||
});
|
});
|
||||||
|
|
||||||
try runCommand(allocator, install_dir, &[_][]const u8{
|
try util.runCommand(allocator, install_dir, &[_][]const u8{
|
||||||
"git",
|
"git",
|
||||||
"commit",
|
"commit",
|
||||||
"-m",
|
"-m",
|
||||||
|
72
cli/commands/update.zig
Normal file
72
cli/commands/update.zig
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const args = @import("args");
|
||||||
|
const util = @import("../util.zig");
|
||||||
|
|
||||||
|
/// Command line options for the `update` command.
|
||||||
|
pub const Options = struct {
|
||||||
|
pub const meta = .{
|
||||||
|
.usage_summary = "[NAME=jetzig]",
|
||||||
|
.full_text =
|
||||||
|
\\Updates the current project to the latest version of Jetzig.
|
||||||
|
\\
|
||||||
|
\\Optionally pass a positional argument to save the dependency to `build.zig.zon` with an
|
||||||
|
\\alternative name.
|
||||||
|
\\
|
||||||
|
\\Equivalent to running `zig fetch --save=jetzig https://github.com/jetzig-framework/jetzig/archive/<latest-commit>.tar.gz`
|
||||||
|
\\
|
||||||
|
\\Example:
|
||||||
|
\\
|
||||||
|
\\ jetzig update
|
||||||
|
\\ jetzig update web
|
||||||
|
,
|
||||||
|
.option_docs = .{
|
||||||
|
.path = "Set the output path relative to the current directory (default: current directory)",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Run the `jetzig update` command.
|
||||||
|
pub fn run(
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
options: Options,
|
||||||
|
writer: anytype,
|
||||||
|
positionals: [][]const u8,
|
||||||
|
other_options: struct { help: bool },
|
||||||
|
) !void {
|
||||||
|
_ = options;
|
||||||
|
if (other_options.help) {
|
||||||
|
try args.printHelp(Options, "jetzig update", writer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (positionals.len > 1) {
|
||||||
|
std.debug.print("Expected at most 1 positional argument, found {}\n", .{positionals.len});
|
||||||
|
return error.JetzigCommandError;
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = if (positionals.len > 0) positionals[0] else "jetzig";
|
||||||
|
|
||||||
|
const github_url = try util.githubUrl(allocator);
|
||||||
|
defer allocator.free(github_url);
|
||||||
|
|
||||||
|
const save_arg = try std.mem.concat(allocator, u8, &[_][]const u8{ "--save=", name });
|
||||||
|
defer allocator.free(save_arg);
|
||||||
|
|
||||||
|
var cwd = try util.detectJetzigProjectDir();
|
||||||
|
defer cwd.close();
|
||||||
|
|
||||||
|
const realpath = try std.fs.realpathAlloc(allocator, ".");
|
||||||
|
defer allocator.free(realpath);
|
||||||
|
|
||||||
|
try util.runCommand(allocator, realpath, &[_][]const u8{
|
||||||
|
"zig",
|
||||||
|
"fetch",
|
||||||
|
save_arg,
|
||||||
|
github_url,
|
||||||
|
});
|
||||||
|
|
||||||
|
std.debug.print(
|
||||||
|
\\Update complete.
|
||||||
|
\\
|
||||||
|
, .{});
|
||||||
|
}
|
73
cli/util.zig
73
cli/util.zig
@ -94,3 +94,76 @@ pub fn isCamelCase(input: []const u8) bool {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Runs a command as a child process and verifies successful exit code.
|
||||||
|
pub fn runCommand(allocator: std.mem.Allocator, install_path: []const u8, argv: []const []const u8) !void {
|
||||||
|
const result = try std.process.Child.run(.{ .allocator = allocator, .argv = argv, .cwd = install_path });
|
||||||
|
defer allocator.free(result.stdout);
|
||||||
|
defer allocator.free(result.stderr);
|
||||||
|
|
||||||
|
const command = try std.mem.join(allocator, " ", argv);
|
||||||
|
defer allocator.free(command);
|
||||||
|
|
||||||
|
std.debug.print("[exec] {s}", .{command});
|
||||||
|
|
||||||
|
if (result.term.Exited != 0) {
|
||||||
|
printFailure();
|
||||||
|
std.debug.print(
|
||||||
|
\\
|
||||||
|
\\Error running command: {s}
|
||||||
|
\\
|
||||||
|
\\[stdout]:
|
||||||
|
\\
|
||||||
|
\\{s}
|
||||||
|
\\
|
||||||
|
\\[stderr]:
|
||||||
|
\\
|
||||||
|
\\{s}
|
||||||
|
\\
|
||||||
|
, .{ command, result.stdout, result.stderr });
|
||||||
|
return error.JetzigCommandError;
|
||||||
|
} else {
|
||||||
|
printSuccess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate a full GitHub URL for passing to `zig fetch`.
|
||||||
|
pub fn githubUrl(allocator: std.mem.Allocator) ![]const u8 {
|
||||||
|
var client = std.http.Client{ .allocator = allocator };
|
||||||
|
defer client.deinit();
|
||||||
|
|
||||||
|
const url = "https://api.github.com/repos/jetzig-framework/jetzig/branches/main";
|
||||||
|
const extra_headers = &[_]std.http.Header{.{ .name = "X-GitHub-Api-Version", .value = "2022-11-28" }};
|
||||||
|
|
||||||
|
var response_storage = std.ArrayList(u8).init(allocator);
|
||||||
|
defer response_storage.deinit();
|
||||||
|
|
||||||
|
const fetch_result = try client.fetch(.{
|
||||||
|
.location = .{ .url = url },
|
||||||
|
.extra_headers = extra_headers,
|
||||||
|
.response_storage = .{ .dynamic = &response_storage },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (fetch_result.status != .ok) {
|
||||||
|
std.debug.print("Error fetching from GitHub: {s}\n", .{url});
|
||||||
|
return error.JetzigCommandError;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsed_response = try std.json.parseFromSlice(
|
||||||
|
struct { commit: struct { sha: []const u8 } },
|
||||||
|
allocator,
|
||||||
|
response_storage.items,
|
||||||
|
.{ .ignore_unknown_fields = true },
|
||||||
|
);
|
||||||
|
defer parsed_response.deinit();
|
||||||
|
|
||||||
|
return try std.mem.concat(
|
||||||
|
allocator,
|
||||||
|
u8,
|
||||||
|
&[_][]const u8{
|
||||||
|
"https://github.com/jetzig-framework/jetzig/archive/",
|
||||||
|
parsed_response.value.commit.sha,
|
||||||
|
".tar.gz",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user