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 args = @import("args");
|
||||
const init = @import("commands/init.zig");
|
||||
const update = @import("commands/update.zig");
|
||||
const generate = @import("commands/generate.zig");
|
||||
|
||||
const Options = struct {
|
||||
@ -14,6 +15,7 @@ const Options = struct {
|
||||
.usage_summary = "[COMMAND]",
|
||||
.option_docs = .{
|
||||
.init = "Initialize a new project",
|
||||
.update = "Update current project to latest version of Jetzig",
|
||||
.generate = "Generate scaffolding",
|
||||
.help = "Print help and exit",
|
||||
},
|
||||
@ -22,6 +24,7 @@ const Options = struct {
|
||||
|
||||
const Verb = union(enum) {
|
||||
init: init.Options,
|
||||
update: update.Options,
|
||||
generate: generate.Options,
|
||||
g: generate.Options,
|
||||
};
|
||||
@ -52,6 +55,7 @@ pub fn main() !void {
|
||||
\\Commands:
|
||||
\\
|
||||
\\ init Initialize a new project.
|
||||
\\ update Update current project to latest version of Jetzig.
|
||||
\\ generate Generate scaffolding.
|
||||
\\
|
||||
\\ 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,
|
||||
.{ .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;
|
||||
}
|
||||
|
||||
const github_url = try githubUrl(allocator);
|
||||
const github_url = try util.githubUrl(allocator);
|
||||
defer allocator.free(github_url);
|
||||
|
||||
if (other_options.help) {
|
||||
@ -87,10 +87,10 @@ pub fn run(
|
||||
install_dir = try std.fs.cwd().makeOpenPath(input_install_path, .{});
|
||||
}
|
||||
|
||||
const real_path = try install_dir.realpathAlloc(allocator, ".");
|
||||
defer allocator.free(real_path);
|
||||
const realpath = try install_dir.realpathAlloc(allocator, ".");
|
||||
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);
|
||||
try writer.writeAll(output);
|
||||
|
||||
@ -184,7 +184,7 @@ pub fn run(
|
||||
null,
|
||||
);
|
||||
|
||||
try runCommand(allocator, real_path, &[_][]const u8{
|
||||
try util.runCommand(allocator, realpath, &[_][]const u8{
|
||||
"zig",
|
||||
"fetch",
|
||||
"--save",
|
||||
@ -208,38 +208,7 @@ pub fn run(
|
||||
\\And then browse to http://localhost:8080/
|
||||
\\
|
||||
\\
|
||||
, .{real_path});
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
, .{realpath});
|
||||
}
|
||||
|
||||
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.
|
||||
fn promptInput(
|
||||
allocator: std.mem.Allocator,
|
||||
@ -384,19 +312,19 @@ fn promptInput(
|
||||
|
||||
// Initialize a new Git repository when setting up a new project (optional).
|
||||
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",
|
||||
"init",
|
||||
".",
|
||||
});
|
||||
|
||||
try runCommand(allocator, install_dir, &[_][]const u8{
|
||||
try util.runCommand(allocator, install_dir, &[_][]const u8{
|
||||
"git",
|
||||
"add",
|
||||
".",
|
||||
});
|
||||
|
||||
try runCommand(allocator, install_dir, &[_][]const u8{
|
||||
try util.runCommand(allocator, install_dir, &[_][]const u8{
|
||||
"git",
|
||||
"commit",
|
||||
"-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;
|
||||
}
|
||||
|
||||
/// 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