Browse code
feat: initial implementation
Showing 7 changed files
0 | 5 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,42 @@ |
1 |
+#+TITLE: socket-based command runner |
|
2 |
+#+TAGS: utilities |
|
3 |
+#+AUTHOR: Ed Langley |
|
4 |
+ |
|
5 |
+** Introduction |
|
6 |
+ |
|
7 |
+Run a command everytime a line is written to a unix socket, passing |
|
8 |
+the line written as the last argument to the command. Useful in an |
|
9 |
+=after-save-hook= in emacs to run tests after the buffer is saved. |
|
10 |
+Unlike file-watchers, this allows more precise control of when the |
|
11 |
+tests are run, reducing the amount of time spent configuring ignore |
|
12 |
+files and waiting for spurious reruns. |
|
13 |
+ |
|
14 |
+** Building |
|
15 |
+ |
|
16 |
+#+BEGIN_SRC zsh :results output :post proc |
|
17 |
+ zig build --verbose 2>&1 |
|
18 |
+#+END_SRC |
|
19 |
+ |
|
20 |
+** Usage |
|
21 |
+ |
|
22 |
+#+BEGIN_SRC zsh |
|
23 |
+rm /tmp/jest.sock ; $OLDPWD/zig-out/bin/zig-test /tmp/jest.sock npx jest -- |
|
24 |
+#+END_SRC |
|
25 |
+ |
|
26 |
+#+NAME: proc |
|
27 |
+#+BEGIN_SRC elisp :exports none |
|
28 |
+ (defun fix (fn inp) |
|
29 |
+ (cl-loop for old = inp then new |
|
30 |
+ for new = (funcall fn inp) |
|
31 |
+ until (equal new old) |
|
32 |
+ finally (cl-return new))) |
|
33 |
+ (s-join |
|
34 |
+ "\n" |
|
35 |
+ (funcall (fwoar/over (lambda (it) |
|
36 |
+ ( (lambda (v) |
|
37 |
+ (s-replace-regexp " \\(--\\|#\\)" " \\\\\n \\1" v)) |
|
38 |
+ (s-replace-regexp "/nix/store/[^/]*/\\(.* \\)" |
|
39 |
+ "/nix/.../\\1" |
|
40 |
+ (s-replace "/Users/edwlan" "~" it))))) |
|
41 |
+ (s-lines (s-trim *this*)))) |
|
42 |
+#+END_SRC |
0 | 43 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,34 @@ |
1 |
+const std = @import("std"); |
|
2 |
+ |
|
3 |
+pub fn build(b: *std.build.Builder) void { |
|
4 |
+ // Standard target options allows the person running `zig build` to choose |
|
5 |
+ // what target to build for. Here we do not override the defaults, which |
|
6 |
+ // means any target is allowed, and the default is native. Other options |
|
7 |
+ // for restricting supported target set are available. |
|
8 |
+ const target = b.standardTargetOptions(.{}); |
|
9 |
+ |
|
10 |
+ // Standard release options allow the person running `zig build` to select |
|
11 |
+ // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. |
|
12 |
+ const mode = b.standardReleaseOptions(); |
|
13 |
+ |
|
14 |
+ const exe = b.addExecutable("sigexec", "src/main.zig"); |
|
15 |
+ exe.setTarget(target); |
|
16 |
+ exe.setBuildMode(mode); |
|
17 |
+ exe.install(); |
|
18 |
+ |
|
19 |
+ const run_cmd = exe.run(); |
|
20 |
+ run_cmd.step.dependOn(b.getInstallStep()); |
|
21 |
+ if (b.args) |args| { |
|
22 |
+ run_cmd.addArgs(args); |
|
23 |
+ } |
|
24 |
+ |
|
25 |
+ const run_step = b.step("run", "Run the app"); |
|
26 |
+ run_step.dependOn(&run_cmd.step); |
|
27 |
+ |
|
28 |
+ const exe_tests = b.addTest("src/main.zig"); |
|
29 |
+ exe_tests.setTarget(target); |
|
30 |
+ exe_tests.setBuildMode(mode); |
|
31 |
+ |
|
32 |
+ const test_step = b.step("test", "Run unit tests"); |
|
33 |
+ test_step.dependOn(&exe_tests.step); |
|
34 |
+} |
0 | 35 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,95 @@ |
1 |
+{ |
|
2 |
+ "nodes": { |
|
3 |
+ "flake-compat": { |
|
4 |
+ "flake": false, |
|
5 |
+ "locked": { |
|
6 |
+ "lastModified": 1648199409, |
|
7 |
+ "narHash": "sha256-JwPKdC2PoVBkG6E+eWw3j6BMR6sL3COpYWfif7RVb8Y=", |
|
8 |
+ "owner": "edolstra", |
|
9 |
+ "repo": "flake-compat", |
|
10 |
+ "rev": "64a525ee38886ab9028e6f61790de0832aa3ef03", |
|
11 |
+ "type": "github" |
|
12 |
+ }, |
|
13 |
+ "original": { |
|
14 |
+ "owner": "edolstra", |
|
15 |
+ "repo": "flake-compat", |
|
16 |
+ "type": "github" |
|
17 |
+ } |
|
18 |
+ }, |
|
19 |
+ "flake-utils": { |
|
20 |
+ "locked": { |
|
21 |
+ "lastModified": 1629481132, |
|
22 |
+ "narHash": "sha256-JHgasjPR0/J1J3DRm4KxM4zTyAj4IOJY8vIl75v/kPI=", |
|
23 |
+ "owner": "numtide", |
|
24 |
+ "repo": "flake-utils", |
|
25 |
+ "rev": "997f7efcb746a9c140ce1f13c72263189225f482", |
|
26 |
+ "type": "github" |
|
27 |
+ }, |
|
28 |
+ "original": { |
|
29 |
+ "owner": "numtide", |
|
30 |
+ "repo": "flake-utils", |
|
31 |
+ "type": "github" |
|
32 |
+ } |
|
33 |
+ }, |
|
34 |
+ "nixpkgs": { |
|
35 |
+ "locked": { |
|
36 |
+ "lastModified": 1648352901, |
|
37 |
+ "narHash": "sha256-1R31hIpqA+3mKBSayiqUhCm21gw4YRioPuR1WqjlCwU=", |
|
38 |
+ "owner": "nixos", |
|
39 |
+ "repo": "nixpkgs", |
|
40 |
+ "rev": "12417777b226eff91efee8b03578daa76c8178a3", |
|
41 |
+ "type": "github" |
|
42 |
+ }, |
|
43 |
+ "original": { |
|
44 |
+ "owner": "nixos", |
|
45 |
+ "ref": "12417777b226eff91efee8b03578daa76c8178a3", |
|
46 |
+ "repo": "nixpkgs", |
|
47 |
+ "type": "github" |
|
48 |
+ } |
|
49 |
+ }, |
|
50 |
+ "nixpkgs_2": { |
|
51 |
+ "locked": { |
|
52 |
+ "lastModified": 1631288242, |
|
53 |
+ "narHash": "sha256-sXm4KiKs7qSIf5oTAmrlsEvBW193sFj+tKYVirBaXz0=", |
|
54 |
+ "owner": "NixOS", |
|
55 |
+ "repo": "nixpkgs", |
|
56 |
+ "rev": "0e24c87754430cb6ad2f8c8c8021b29834a8845e", |
|
57 |
+ "type": "github" |
|
58 |
+ }, |
|
59 |
+ "original": { |
|
60 |
+ "owner": "NixOS", |
|
61 |
+ "ref": "nixpkgs-unstable", |
|
62 |
+ "repo": "nixpkgs", |
|
63 |
+ "type": "github" |
|
64 |
+ } |
|
65 |
+ }, |
|
66 |
+ "root": { |
|
67 |
+ "inputs": { |
|
68 |
+ "flake-compat": "flake-compat", |
|
69 |
+ "nixpkgs": "nixpkgs", |
|
70 |
+ "zig": "zig" |
|
71 |
+ } |
|
72 |
+ }, |
|
73 |
+ "zig": { |
|
74 |
+ "inputs": { |
|
75 |
+ "flake-utils": "flake-utils", |
|
76 |
+ "nixpkgs": "nixpkgs_2" |
|
77 |
+ }, |
|
78 |
+ "locked": { |
|
79 |
+ "lastModified": 1648341617, |
|
80 |
+ "narHash": "sha256-j4HXFD1eKxCFfMbBYhNnLmXZ7q6cahVqH/Hlq43v8RM=", |
|
81 |
+ "owner": "arqv", |
|
82 |
+ "repo": "zig-overlay", |
|
83 |
+ "rev": "8d79efd701963ac3c9a55755de44e6fd4a6c8d76", |
|
84 |
+ "type": "github" |
|
85 |
+ }, |
|
86 |
+ "original": { |
|
87 |
+ "owner": "arqv", |
|
88 |
+ "repo": "zig-overlay", |
|
89 |
+ "type": "github" |
|
90 |
+ } |
|
91 |
+ } |
|
92 |
+ }, |
|
93 |
+ "root": "root", |
|
94 |
+ "version": 7 |
|
95 |
+} |
0 | 96 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,30 @@ |
1 |
+{ |
|
2 |
+ inputs = { |
|
3 |
+ nixpkgs = { |
|
4 |
+ type = "github"; |
|
5 |
+ owner = "nixos"; |
|
6 |
+ repo = "nixpkgs"; |
|
7 |
+ ref = "12417777b226eff91efee8b03578daa76c8178a3"; |
|
8 |
+ }; |
|
9 |
+ zig.url = "github:arqv/zig-overlay"; |
|
10 |
+ flake-compat = { |
|
11 |
+ url = "github:edolstra/flake-compat"; |
|
12 |
+ flake = false; |
|
13 |
+ }; |
|
14 |
+ }; |
|
15 |
+ |
|
16 |
+ outputs = { |
|
17 |
+ self, |
|
18 |
+ nixpkgs, |
|
19 |
+ zig, |
|
20 |
+ flake-compat, |
|
21 |
+ }: let |
|
22 |
+ pkgs = nixpkgs.legacyPackages.aarch64-darwin; |
|
23 |
+ zig091 = zig.packages.aarch64-darwin."0.9.1"; |
|
24 |
+ in |
|
25 |
+ { |
|
26 |
+ devShell.aarch64-darwin = pkgs.mkShell { |
|
27 |
+ buildInputs = [zig091 nixpkgs.legacyPackages.aarch64-darwin.socat]; |
|
28 |
+ }; |
|
29 |
+ }; |
|
30 |
+} |
0 | 31 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,58 @@ |
1 |
+const std = @import("std"); |
|
2 |
+const net = std.net; |
|
3 |
+ |
|
4 |
+// pub const io_mode = .evented; |
|
5 |
+ |
|
6 |
+pub fn main() anyerror!void { |
|
7 |
+ var allocator = std.heap.page_allocator; |
|
8 |
+ const args = try std.process.argsAlloc(allocator); |
|
9 |
+ defer std.process.argsFree(allocator, args); |
|
10 |
+ |
|
11 |
+ const stdout = std.io.getStdOut().writer(); |
|
12 |
+ try stdout.print("{}", .{args.len}); |
|
13 |
+ try stdout.writeAll("\n"); |
|
14 |
+ |
|
15 |
+ var server = net.StreamServer.init(.{}); |
|
16 |
+ defer server.deinit(); |
|
17 |
+ |
|
18 |
+ try server.listen(net.Address.initUnix(args[1]) catch unreachable); |
|
19 |
+ std.log.warn("listening at {}\n", .{server.listen_address}); |
|
20 |
+ |
|
21 |
+ while (true) { |
|
22 |
+ const client = try allocator.create(Client); |
|
23 |
+ client.* = Client{ |
|
24 |
+ .conn = try server.accept(), |
|
25 |
+ .handle_frame = async client.handle(allocator, args[2..]), |
|
26 |
+ }; |
|
27 |
+ } |
|
28 |
+} |
|
29 |
+ |
|
30 |
+fn nextLine(reader: anytype, buffer: []u8) !?[]const u8 { |
|
31 |
+ var line = (try reader.readUntilDelimiterOrEof( |
|
32 |
+ buffer, |
|
33 |
+ '\n', |
|
34 |
+ )) orelse return null; |
|
35 |
+ // trim annoying windows-only carriage return character |
|
36 |
+ return line; |
|
37 |
+} |
|
38 |
+ |
|
39 |
+const Client = struct { |
|
40 |
+ conn: net.StreamServer.Connection, |
|
41 |
+ handle_frame: @Frame(handle), |
|
42 |
+ fn handle(self: *Client, allocator: std.mem.Allocator, args: []const []u8) !void { |
|
43 |
+ var dynargs = std.ArrayList([]const u8).init(allocator); |
|
44 |
+ defer dynargs.deinit(); |
|
45 |
+ |
|
46 |
+ try dynargs.appendSlice(args); |
|
47 |
+ |
|
48 |
+ try self.conn.stream.writer().writeAll("server: howdy!"); |
|
49 |
+ |
|
50 |
+ // const stdout = std.io.getStdOut().writer(); |
|
51 |
+ var buffer: [1024]u8 = undefined; |
|
52 |
+ var nl = (try nextLine(self.conn.stream.reader(), &buffer)).?; |
|
53 |
+ try dynargs.append(nl); |
|
54 |
+ |
|
55 |
+ const proc = try std.ChildProcess.init(dynargs.toOwnedSlice(), allocator); |
|
56 |
+ _ = try nosuspend proc.spawn(); |
|
57 |
+ } |
|
58 |
+}; |