From cbe8b6dd1fd2eeae6b8fabb5e415e360be6da5e7 Mon Sep 17 00:00:00 2001 From: Emil Lerch Date: Fri, 20 Oct 2023 21:58:34 -0700 Subject: [PATCH] initial commit: cloudflare --- src/CloudflareDeployStep.zig | 87 ++++ src/cloudflare_build.zig | 26 ++ src/cloudflaredeploy.zig | 377 ++++++++++++++++ src/dist/memfs.wasm | Bin 0 -> 140170 bytes src/index.js | 31 ++ src/script_harness.js | 777 +++++++++++++++++++++++++++++++++ src/universal_lambda.zig | 2 + src/universal_lambda_build.zig | 4 + 8 files changed, 1304 insertions(+) create mode 100644 src/CloudflareDeployStep.zig create mode 100644 src/cloudflare_build.zig create mode 100644 src/cloudflaredeploy.zig create mode 100755 src/dist/memfs.wasm create mode 100644 src/index.js create mode 100644 src/script_harness.js diff --git a/src/CloudflareDeployStep.zig b/src/CloudflareDeployStep.zig new file mode 100644 index 0000000..533a311 --- /dev/null +++ b/src/CloudflareDeployStep.zig @@ -0,0 +1,87 @@ +const std = @import("std"); +const cloudflare = @import("cloudflaredeploy.zig"); +const CloudflareDeployStep = @This(); + +pub const base_id: std.Build.Step.Id = .custom; + +step: std.Build.Step, +primary_javascript_file: std.Build.LazyPath, +worker_name: []const u8, +options: Options, + +pub const Options = struct { + /// if set, the primary file will not be read (and may not exist). This data + /// will be used instead + primary_file_data: ?[]const u8 = null, + + /// When set, the Javascript file will be searched/replaced with the target + /// file name for import + wasm_name: ?struct { + search: []const u8, + replace: []const u8, + } = null, + + /// When set, the directory specified will be used rather than the current directory + wasm_dir: ?[]const u8 = null, +}; + +pub fn create( + owner: *std.Build, + worker_name: []const u8, + primary_javascript_file: std.Build.LazyPath, + options: Options, +) *CloudflareDeployStep { + const self = owner.allocator.create(CloudflareDeployStep) catch @panic("OOM"); + self.* = CloudflareDeployStep{ + .step = std.Build.Step.init(.{ + .id = base_id, + .name = owner.fmt("cloudflare deploy {s}", .{primary_javascript_file.getDisplayName()}), + .owner = owner, + .makeFn = make, + }), + .primary_javascript_file = primary_javascript_file, + .worker_name = worker_name, + .options = options, + }; + if (options.primary_file_data == null) + primary_javascript_file.addStepDependencies(&self.step); + return self; +} + +fn make(step: *std.Build.Step, prog_node: *std.Progress.Node) !void { + _ = prog_node; + const b = step.owner; + const self = @fieldParentPtr(CloudflareDeployStep, "step", step); + + var client = std.http.Client{ .allocator = b.allocator }; + defer client.deinit(); + + const script = self.options.primary_file_data orelse + try std.fs.cwd().readFileAlloc(b.allocator, self.primary_javascript_file.path, std.math.maxInt(usize)); + defer if (self.options.primary_file_data == null) b.allocator.free(script); + + var final_script = script; + if (self.options.wasm_name) |n| { + final_script = try std.mem.replaceOwned(u8, b.allocator, script, n.search, n.replace); + if (self.options.primary_file_data == null) b.allocator.free(script); + } + defer if (self.options.wasm_name) |_| b.allocator.free(final_script); + + var al = std.ArrayList(u8).init(b.allocator); + defer al.deinit(); + try cloudflare.pushWorker( + b.allocator, + &client, + self.worker_name, + script, + self.options.wasm_dir orelse ".", + al.writer(), + std.io.getStdErr().writer(), + ); + const start = std.mem.lastIndexOf(u8, al.items, "http").?; + step.name = try std.fmt.allocPrint( + b.allocator, + "cloudflare deploy {s} to {s}", + .{ self.primary_javascript_file.getDisplayName(), al.items[start .. al.items.len - 1] }, + ); +} diff --git a/src/cloudflare_build.zig b/src/cloudflare_build.zig new file mode 100644 index 0000000..9860a20 --- /dev/null +++ b/src/cloudflare_build.zig @@ -0,0 +1,26 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const CloudflareDeployStep = @import("CloudflareDeployStep"); + +const script = @embedFile("index.js"); + +pub fn configureBuild(b: *std.build.Builder, cs: *std.Build.Step.Compile, build_root_src: []const u8) !void { + _ = build_root_src; + const deploy_cmd = CloudflareDeployStep.create( + b, + "zigwasi", + .{ .path = "index.js" }, + .{ + .primary_file_data = script, + .wasm_name = .{ + .search = "zigout.wasm", + .replace = cs.name, + }, + .wasm_dir = b.getInstallDir(.bin, "."), + }, + ); + deploy_cmd.step.dependOn(b.getInstallStep()); + + const deploy_step = b.step("cloudflare", "Deploy as Cloudflare worker (must be compiled with -Dtarget=wasm32-wasi)"); + deploy_step.dependOn(&deploy_cmd.step); +} diff --git a/src/cloudflaredeploy.zig b/src/cloudflaredeploy.zig new file mode 100644 index 0000000..9e67edc --- /dev/null +++ b/src/cloudflaredeploy.zig @@ -0,0 +1,377 @@ +const std = @import("std"); + +var x_auth_token: ?[:0]const u8 = undefined; +var x_auth_email: ?[:0]const u8 = undefined; +var x_auth_key: ?[:0]const u8 = undefined; +var initialized = false; + +const cf_api_base = "https://api.cloudflare.com/client/v4"; + +pub fn main() !u8 { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + var client = std.http.Client{ .allocator = allocator }; + defer client.deinit(); + // .allocator = allocator, + // .proxy = .{ + // .protocol = .plain, + // .host = "localhost", + // .port = 8080, + // }, + // }; + + const stdout_file = std.io.getStdOut().writer(); + var bw = std.io.bufferedWriter(stdout_file); + const stdout = bw.writer(); + + var argIterator = try std.process.argsWithAllocator(allocator); + defer argIterator.deinit(); + const exe_name = argIterator.next().?; + var maybe_name = argIterator.next(); + if (maybe_name == null) { + try usage(std.io.getStdErr().writer(), exe_name); + return 1; + } + const worker_name = maybe_name.?; + if (std.mem.eql(u8, worker_name, "-h")) { + try usage(stdout, exe_name); + return 0; + } + var maybe_script_name = argIterator.next(); + if (maybe_script_name == null) { + try usage(std.io.getStdErr().writer(), exe_name); + return 1; + } + const script = std.fs.cwd().readFileAlloc(allocator, maybe_script_name.?, std.math.maxInt(usize)) catch |err| { + try usage(std.io.getStdErr().writer(), exe_name); + return err; + }; + + pushWorker(allocator, &client, worker_name, script, ".", stdout, std.io.getStdErr().writer()) catch return 1; + try bw.flush(); // don't forget to flush! + return 0; +} + +fn usage(writer: anytype, this: []const u8) !void { + try writer.print("usage: {s}