Skip to content

Commit d80e643

Browse files
committed
feat: all
1 parent 572248d commit d80e643

5 files changed

Lines changed: 325 additions & 0 deletions

File tree

Makefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
PHONY: build run
2+
3+
build:
4+
zig build
5+
6+
run:
7+
zig build run

build.zig

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
const std = @import("std");
2+
3+
// Although this function looks imperative, note that its job is to
4+
// declaratively construct a build graph that will be executed by an external
5+
// runner.
6+
pub fn build(b: *std.Build) void {
7+
// Standard target options allows the person running `zig build` to choose
8+
// what target to build for. Here we do not override the defaults, which
9+
// means any target is allowed, and the default is native. Other options
10+
// for restricting supported target set are available.
11+
const target = b.standardTargetOptions(.{});
12+
13+
// Standard optimization options allow the person running `zig build` to select
14+
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
15+
// set a preferred release mode, allowing the user to decide how to optimize.
16+
const optimize = b.standardOptimizeOption(.{});
17+
18+
// This creates a "module", which represents a collection of source files alongside
19+
// some compilation options, such as optimization mode and linked system libraries.
20+
// Every executable or library we compile will be based on one or more modules.
21+
const lib_mod = b.createModule(.{
22+
// `root_source_file` is the Zig "entry point" of the module. If a module
23+
// only contains e.g. external object files, you can make this `null`.
24+
// In this case the main source file is merely a path, however, in more
25+
// complicated build scripts, this could be a generated file.
26+
.root_source_file = b.path("src/root.zig"),
27+
.target = target,
28+
.optimize = optimize,
29+
});
30+
31+
// We will also create a module for our other entry point, 'main.zig'.
32+
const exe_mod = b.createModule(.{
33+
// `root_source_file` is the Zig "entry point" of the module. If a module
34+
// only contains e.g. external object files, you can make this `null`.
35+
// In this case the main source file is merely a path, however, in more
36+
// complicated build scripts, this could be a generated file.
37+
.root_source_file = b.path("src/main.zig"),
38+
.target = target,
39+
.optimize = optimize,
40+
});
41+
42+
// Modules can depend on one another using the `std.Build.Module.addImport` function.
43+
// This is what allows Zig source code to use `@import("foo")` where 'foo' is not a
44+
// file path. In this case, we set up `exe_mod` to import `lib_mod`.
45+
exe_mod.addImport("zig_plscommit_lib", lib_mod);
46+
47+
// Now, we will create a static library based on the module we created above.
48+
// This creates a `std.Build.Step.Compile`, which is the build step responsible
49+
// for actually invoking the compiler.
50+
const lib = b.addLibrary(.{
51+
.linkage = .static,
52+
.name = "zig_plscommit",
53+
.root_module = lib_mod,
54+
});
55+
56+
// This declares intent for the library to be installed into the standard
57+
// location when the user invokes the "install" step (the default step when
58+
// running `zig build`).
59+
b.installArtifact(lib);
60+
61+
// This creates another `std.Build.Step.Compile`, but this one builds an executable
62+
// rather than a static library.
63+
const exe = b.addExecutable(.{
64+
.name = "zig_plscommit",
65+
.root_module = exe_mod,
66+
});
67+
68+
const prompter_dep = b.dependency("prompter", .{
69+
.target = target,
70+
.optimize = optimize,
71+
});
72+
73+
exe.root_module.addImport("prompter", prompter_dep.module("prompter"));
74+
75+
// This declares intent for the executable to be installed into the
76+
// standard location when the user invokes the "install" step (the default
77+
// step when running `zig build`).
78+
b.installArtifact(exe);
79+
80+
// This *creates* a Run step in the build graph, to be executed when another
81+
// step is evaluated that depends on it. The next line below will establish
82+
// such a dependency.
83+
const run_cmd = b.addRunArtifact(exe);
84+
85+
// By making the run step depend on the install step, it will be run from the
86+
// installation directory rather than directly from within the cache directory.
87+
// This is not necessary, however, if the application depends on other installed
88+
// files, this ensures they will be present and in the expected location.
89+
run_cmd.step.dependOn(b.getInstallStep());
90+
91+
// This allows the user to pass arguments to the application in the build
92+
// command itself, like this: `zig build run -- arg1 arg2 etc`
93+
if (b.args) |args| {
94+
run_cmd.addArgs(args);
95+
}
96+
97+
// This creates a build step. It will be visible in the `zig build --help` menu,
98+
// and can be selected like this: `zig build run`
99+
// This will evaluate the `run` step rather than the default, which is "install".
100+
const run_step = b.step("run", "Run the app");
101+
run_step.dependOn(&run_cmd.step);
102+
103+
// Creates a step for unit testing. This only builds the test executable
104+
// but does not run it.
105+
const lib_unit_tests = b.addTest(.{
106+
.root_module = lib_mod,
107+
});
108+
109+
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
110+
111+
const exe_unit_tests = b.addTest(.{
112+
.root_module = exe_mod,
113+
});
114+
115+
const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
116+
117+
// Similar to creating the run step earlier, this exposes a `test` step to
118+
// the `zig build --help` menu, providing a way for the user to request
119+
// running the unit tests.
120+
const test_step = b.step("test", "Run unit tests");
121+
test_step.dependOn(&run_lib_unit_tests.step);
122+
test_step.dependOn(&run_exe_unit_tests.step);
123+
}

build.zig.zon

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
.{
2+
// This is the default name used by packages depending on this one. For
3+
// example, when a user runs `zig fetch --save <url>`, this field is used
4+
// as the key in the `dependencies` table. Although the user can choose a
5+
// different name, most users will stick with this provided value.
6+
//
7+
// It is redundant to include "zig" in this name because it is already
8+
// within the Zig package namespace.
9+
.name = .zig_plscommit,
10+
11+
// This is a [Semantic Version](https://semver.org/).
12+
// In a future version of Zig it will be used for package deduplication.
13+
.version = "0.0.0",
14+
15+
// Together with name, this represents a globally unique package
16+
// identifier. This field is generated by the Zig toolchain when the
17+
// package is first created, and then *never changes*. This allows
18+
// unambiguous detection of one package being an updated version of
19+
// another.
20+
//
21+
// When forking a Zig project, this id should be regenerated (delete the
22+
// field and run `zig build`) if the upstream project is still maintained.
23+
// Otherwise, the fork is *hostile*, attempting to take control over the
24+
// original project's identity. Thus it is recommended to leave the comment
25+
// on the following line intact, so that it shows up in code reviews that
26+
// modify the field.
27+
.fingerprint = 0x5ea5675e189ddb99, // Changing this has security and trust implications.
28+
29+
// Tracks the earliest Zig version that the package considers to be a
30+
// supported use case.
31+
.minimum_zig_version = "0.14.0",
32+
33+
// This field is optional.
34+
// Each dependency must either provide a `url` and `hash`, or a `path`.
35+
// `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
36+
// Once all dependencies are fetched, `zig build` no longer requires
37+
// internet connectivity.
38+
.dependencies = .{
39+
.prompter = .{
40+
.url = "git+https://github.com/davidprokopec/zig-prompter/#37f78b8a82411ef9eed99b5ccc2bf4e60df93a1a",
41+
.hash = "prompter-0.1.0-AAAAAEpPAACoy55adcSMiPAQlbF4TIQVaNbkOYYNwp53",
42+
},
43+
},
44+
.paths = .{
45+
"build.zig",
46+
"build.zig.zon",
47+
"src",
48+
// For example...
49+
//"LICENSE",
50+
//"README.md",
51+
},
52+
}

src/main.zig

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
const std = @import("std");
2+
const Prompter = @import("prompter");
3+
4+
const CommitType = struct {
5+
name: []const u8,
6+
description: []const u8,
7+
};
8+
9+
pub fn main() !void {
10+
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
11+
defer {
12+
const check = gpa.deinit();
13+
if (check == .leak) @panic("The allocator has leaked!");
14+
}
15+
const allocator = gpa.allocator();
16+
const out = std.io.getStdOut().writer();
17+
18+
// Parse command line arguments
19+
const args = try std.process.argsAlloc(allocator);
20+
defer std.process.argsFree(allocator, args);
21+
22+
var path_arg: ?[]const u8 = null;
23+
if (args.len > 1) {
24+
path_arg = args[1];
25+
}
26+
27+
// Setup the prompt theme
28+
const theme = Prompter.Themes.SimpleTheme{};
29+
var p = Prompter.Prompt.init(allocator, theme.theme());
30+
31+
// Define commit types with descriptions
32+
const commit_types = [_]CommitType{
33+
.{ .name = "feat", .description = "A new feature" },
34+
.{ .name = "fix", .description = "A bug fix" },
35+
.{ .name = "refactor", .description = "Code change that neither fixes a bug nor adds a feature" },
36+
.{ .name = "revert", .description = "Revert to a previous commit" },
37+
.{ .name = "build", .description = "Changes affecting build system or external dependencies" },
38+
.{ .name = "chore", .description = "Other changes that don't modify src or test files" },
39+
.{ .name = "ci", .description = "Changes to CI configuration files and scripts" },
40+
.{ .name = "docs", .description = "Documentation only changes" },
41+
.{ .name = "perf", .description = "A code change that improves performance" },
42+
.{ .name = "style", .description = "Changes that do not affect the meaning of the code" },
43+
.{ .name = "test", .description = "Adding missing tests or correcting existing tests" },
44+
.{ .name = "debug", .description = "Adding or modifying debug output" },
45+
.{ .name = "custom", .description = "Custom commit type" },
46+
};
47+
48+
// Prepare options for display
49+
var options = try allocator.alloc([]const u8, commit_types.len);
50+
defer allocator.free(options);
51+
52+
// We need to free each option string before exiting
53+
for (commit_types, 0..) |ct, i| {
54+
const option_text = try std.fmt.allocPrint(allocator, "{s}: {s}", .{ ct.name, ct.description });
55+
options[i] = option_text;
56+
}
57+
// Add a defer block to free all the option strings
58+
defer {
59+
for (options) |option| {
60+
allocator.free(option);
61+
}
62+
}
63+
64+
// Prompt for commit type selection
65+
try out.writeAll("Select commit type:\n");
66+
const sel_opt = try p.option("Commit type", options, 0);
67+
68+
// Handle selection abort
69+
if (sel_opt == null) {
70+
try out.writeAll("Commit aborted.\n");
71+
return;
72+
}
73+
74+
// Handle selected commit type
75+
var commit_type: []const u8 = undefined;
76+
var custom_type: []const u8 = undefined;
77+
var needs_to_free_custom_type = false;
78+
79+
if (sel_opt.? == commit_types.len - 1) {
80+
custom_type = try p.string("Enter custom commit type", null);
81+
needs_to_free_custom_type = true;
82+
commit_type = custom_type;
83+
} else {
84+
commit_type = commit_types[sel_opt.?].name;
85+
}
86+
87+
// Prompt for commit message
88+
const commit_message = try p.string("Enter commit message", null);
89+
defer allocator.free(commit_message);
90+
91+
// Build the full commit message
92+
const full_commit_message = try std.fmt.allocPrint(allocator, "{s}: {s}", .{ commit_type, commit_message });
93+
defer allocator.free(full_commit_message);
94+
95+
// Free custom type if it was allocated
96+
defer if (needs_to_free_custom_type) allocator.free(custom_type);
97+
98+
// Execute git commands using process.Child
99+
// Add files to staging
100+
var add_cmd_args = if (path_arg) |path|
101+
[_][]const u8{ "git", "add", path }
102+
else
103+
[_][]const u8{ "git", "add", "." };
104+
105+
var add_cmd = std.process.Child.init(&add_cmd_args, allocator);
106+
add_cmd.stderr_behavior = .Inherit;
107+
add_cmd.stdout_behavior = .Inherit;
108+
109+
const add_term = try add_cmd.spawnAndWait();
110+
111+
if (add_term.Exited != 0) {
112+
try out.writeAll("Error adding files to git\n");
113+
return;
114+
}
115+
116+
// Create commit
117+
var commit_cmd_args = [_][]const u8{ "git", "commit", "-m", full_commit_message };
118+
var commit_cmd = std.process.Child.init(&commit_cmd_args, allocator);
119+
commit_cmd.stderr_behavior = .Inherit;
120+
commit_cmd.stdout_behavior = .Inherit;
121+
122+
const commit_term = try commit_cmd.spawnAndWait();
123+
124+
if (commit_term.Exited != 0) {
125+
try out.writeAll("Error creating commit\n");
126+
return;
127+
}
128+
129+
try out.print("Successfully committed: {s}\n", .{full_commit_message});
130+
}

src/root.zig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//! By convention, root.zig is the root source file when making a library. If
2+
//! you are making an executable, the convention is to delete this file and
3+
//! start with main.zig instead.
4+
const std = @import("std");
5+
const testing = std.testing;
6+
7+
pub export fn add(a: i32, b: i32) i32 {
8+
return a + b;
9+
}
10+
11+
test "basic add functionality" {
12+
try testing.expect(add(3, 7) == 10);
13+
}

0 commit comments

Comments
 (0)