Compare commits
2 Commits
9346daa04e
...
00e0c51ac0
Author | SHA1 | Date | |
---|---|---|---|
00e0c51ac0 | |||
af6edbc83a |
29
.github/workflows/zig-build.yaml
vendored
Normal file
29
.github/workflows/zig-build.yaml
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
name: Generic zig build
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
- '!zig-develop*'
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: elerch/setup-zig@v3
|
||||||
|
with:
|
||||||
|
version: 0.12.0
|
||||||
|
- uses: Hanaasagi/zig-action-cache@v1.1.5
|
||||||
|
- name: Build project
|
||||||
|
run: zig build --summary all
|
||||||
|
- name: Run tests
|
||||||
|
run: zig build test --summary all
|
||||||
|
- name: Notify
|
||||||
|
uses: elerch/action-notify-ntfy@v2.github
|
||||||
|
if: env.GITEA_ACTIONS == 'true'
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.NTFY_HOST }}
|
||||||
|
topic: ${{ secrets.NTFY_TOPIC }}
|
||||||
|
status: ${{ job.status }}
|
||||||
|
user: ${{ secrets.NTFY_USER }}
|
||||||
|
password: ${{ secrets.NTFY_PASSWORD }}
|
|
@ -35,7 +35,7 @@ pub fn create(
|
||||||
fn make(step: *std.Build.Step, prog_node: *std.Progress.Node) !void {
|
fn make(step: *std.Build.Step, prog_node: *std.Progress.Node) !void {
|
||||||
_ = prog_node;
|
_ = prog_node;
|
||||||
const b = step.owner;
|
const b = step.owner;
|
||||||
const self = @fieldParentPtr(CloudflareDeployStep, "step", step);
|
const self = @as(*CloudflareDeployStep, @fieldParentPtr("step", step));
|
||||||
|
|
||||||
var client = std.http.Client{ .allocator = b.allocator };
|
var client = std.http.Client{ .allocator = b.allocator };
|
||||||
defer client.deinit();
|
defer client.deinit();
|
||||||
|
|
182
src/main.zig
182
src/main.zig
|
@ -3,7 +3,9 @@ const std = @import("std");
|
||||||
var x_auth_token: ?[:0]const u8 = undefined;
|
var x_auth_token: ?[:0]const u8 = undefined;
|
||||||
var x_auth_email: ?[:0]const u8 = undefined;
|
var x_auth_email: ?[:0]const u8 = undefined;
|
||||||
var x_auth_key: ?[:0]const u8 = undefined;
|
var x_auth_key: ?[:0]const u8 = undefined;
|
||||||
var initialized = false;
|
|
||||||
|
var auth_header_buf: [16 * 1024]u8 = undefined;
|
||||||
|
var auth_headers: ?[]std.http.Header = null;
|
||||||
|
|
||||||
const cf_api_base = "https://api.cloudflare.com/client/v4";
|
const cf_api_base = "https://api.cloudflare.com/client/v4";
|
||||||
|
|
||||||
|
@ -28,7 +30,7 @@ pub fn main() !u8 {
|
||||||
var argIterator = try std.process.argsWithAllocator(allocator);
|
var argIterator = try std.process.argsWithAllocator(allocator);
|
||||||
defer argIterator.deinit();
|
defer argIterator.deinit();
|
||||||
const exe_name = argIterator.next().?;
|
const exe_name = argIterator.next().?;
|
||||||
var maybe_name = argIterator.next();
|
const maybe_name = argIterator.next();
|
||||||
if (maybe_name == null) {
|
if (maybe_name == null) {
|
||||||
try usage(std.io.getStdErr().writer(), exe_name);
|
try usage(std.io.getStdErr().writer(), exe_name);
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -38,7 +40,7 @@ pub fn main() !u8 {
|
||||||
try usage(stdout, exe_name);
|
try usage(stdout, exe_name);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
var maybe_script_name = argIterator.next();
|
const maybe_script_name = argIterator.next();
|
||||||
if (maybe_script_name == null) {
|
if (maybe_script_name == null) {
|
||||||
try usage(std.io.getStdErr().writer(), exe_name);
|
try usage(std.io.getStdErr().writer(), exe_name);
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -80,7 +82,7 @@ pub fn pushWorker(
|
||||||
var wasm = try loadWasm(allocator, script);
|
var wasm = try loadWasm(allocator, script);
|
||||||
defer wasm.deinit();
|
defer wasm.deinit();
|
||||||
|
|
||||||
var accountid = std.os.getenv("CLOUDFLARE_ACCOUNT_ID");
|
var accountid = std.posix.getenv("CLOUDFLARE_ACCOUNT_ID");
|
||||||
const account_id_free = accountid == null;
|
const account_id_free = accountid == null;
|
||||||
if (accountid == null) accountid = try getAccountId(allocator, client);
|
if (accountid == null) accountid = try getAccountId(allocator, client);
|
||||||
defer if (account_id_free) allocator.free(accountid.?);
|
defer if (account_id_free) allocator.free(accountid.?);
|
||||||
|
@ -167,20 +169,19 @@ fn loadWasm(allocator: std.mem.Allocator, script: []const u8) !Wasm {
|
||||||
|
|
||||||
fn getAccountId(allocator: std.mem.Allocator, client: *std.http.Client) ![:0]const u8 {
|
fn getAccountId(allocator: std.mem.Allocator, client: *std.http.Client) ![:0]const u8 {
|
||||||
const url = cf_api_base ++ "/accounts/";
|
const url = cf_api_base ++ "/accounts/";
|
||||||
var headers = std.http.Headers.init(allocator);
|
var raw_body = std.ArrayList(u8).init(allocator);
|
||||||
defer headers.deinit();
|
defer raw_body.deinit();
|
||||||
try addAuthHeaders(&headers);
|
const req = try client.fetch(.{
|
||||||
var req = try client.request(.GET, try std.Uri.parse(url), headers, .{});
|
.response_storage = .{ .dynamic = &raw_body },
|
||||||
defer req.deinit();
|
.method = .GET,
|
||||||
try req.start();
|
.location = .{ .url = url },
|
||||||
try req.wait();
|
.privileged_headers = try createAuthHeaders(),
|
||||||
if (req.response.status != .ok) {
|
});
|
||||||
std.debug.print("Status is {}\n", .{req.response.status});
|
if (req.status != .ok) {
|
||||||
|
std.debug.print("Status is {}\n", .{req.status});
|
||||||
return error.RequestFailed;
|
return error.RequestFailed;
|
||||||
}
|
}
|
||||||
var json_reader = std.json.reader(allocator, req.reader());
|
var body = try std.json.parseFromSlice(std.json.Value, allocator, raw_body.items, .{});
|
||||||
defer json_reader.deinit();
|
|
||||||
var body = try std.json.parseFromTokenSource(std.json.Value, allocator, &json_reader, .{});
|
|
||||||
defer body.deinit();
|
defer body.deinit();
|
||||||
const arr = body.value.object.get("result").?.array.items;
|
const arr = body.value.object.get("result").?.array.items;
|
||||||
if (arr.len == 0) return error.NoAccounts;
|
if (arr.len == 0) return error.NoAccounts;
|
||||||
|
@ -192,23 +193,24 @@ fn enableWorker(allocator: std.mem.Allocator, client: *std.http.Client, account_
|
||||||
const enable_script = cf_api_base ++ "/accounts/{s}/workers/scripts/{s}/subdomain";
|
const enable_script = cf_api_base ++ "/accounts/{s}/workers/scripts/{s}/subdomain";
|
||||||
const url = try std.fmt.allocPrint(allocator, enable_script, .{ account_id, name });
|
const url = try std.fmt.allocPrint(allocator, enable_script, .{ account_id, name });
|
||||||
defer allocator.free(url);
|
defer allocator.free(url);
|
||||||
var headers = std.http.Headers.init(allocator);
|
var raw_body = std.ArrayList(u8).init(allocator);
|
||||||
|
defer raw_body.deinit();
|
||||||
|
var headers = std.ArrayList(std.http.Header).init(allocator);
|
||||||
defer headers.deinit();
|
defer headers.deinit();
|
||||||
try addAuthHeaders(&headers);
|
try addAuthHeaders(&headers);
|
||||||
try headers.append("Content-Type", "application/json; charset=UTF-8");
|
try headers.append(.{ .name = "Content-Type", .value = "application/json; charset=UTF-8" });
|
||||||
var req = try client.request(.POST, try std.Uri.parse(url), headers, .{});
|
const req = try client.fetch(.{
|
||||||
defer req.deinit();
|
.response_storage = .{ .dynamic = &raw_body },
|
||||||
|
.method = .POST,
|
||||||
const request_payload =
|
.payload =
|
||||||
\\{ "enabled": true }
|
\\{ "enabled": true }
|
||||||
;
|
,
|
||||||
req.transfer_encoding = .{ .content_length = @as(u64, request_payload.len) };
|
.location = .{ .url = url },
|
||||||
try req.start();
|
.privileged_headers = headers.items,
|
||||||
try req.writeAll(request_payload);
|
});
|
||||||
try req.finish();
|
|
||||||
try req.wait();
|
if (req.status != .ok) {
|
||||||
if (req.response.status != .ok) {
|
std.debug.print("Status is {}\n", .{req.status});
|
||||||
std.debug.print("Status is {}\n", .{req.response.status});
|
|
||||||
return error.RequestFailed;
|
return error.RequestFailed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -219,17 +221,18 @@ fn getSubdomain(allocator: std.mem.Allocator, client: *std.http.Client, account_
|
||||||
const url = try std.fmt.allocPrint(allocator, get_subdomain, .{account_id});
|
const url = try std.fmt.allocPrint(allocator, get_subdomain, .{account_id});
|
||||||
defer allocator.free(url);
|
defer allocator.free(url);
|
||||||
|
|
||||||
var headers = std.http.Headers.init(allocator);
|
var raw_body = std.ArrayList(u8).init(allocator);
|
||||||
|
defer raw_body.deinit();
|
||||||
|
var headers = std.ArrayList(std.http.Header).init(allocator);
|
||||||
defer headers.deinit();
|
defer headers.deinit();
|
||||||
try addAuthHeaders(&headers);
|
const req = try client.fetch(.{
|
||||||
var req = try client.request(.GET, try std.Uri.parse(url), headers, .{});
|
.response_storage = .{ .dynamic = &raw_body },
|
||||||
defer req.deinit();
|
.method = .GET,
|
||||||
try req.start();
|
.location = .{ .url = url },
|
||||||
try req.wait();
|
.privileged_headers = try createAuthHeaders(),
|
||||||
if (req.response.status != .ok) return error.RequestNotOk;
|
});
|
||||||
var json_reader = std.json.reader(allocator, req.reader());
|
if (req.status != .ok) return error.RequestNotOk;
|
||||||
defer json_reader.deinit();
|
var body = try std.json.parseFromSlice(std.json.Value, allocator, raw_body.items, .{});
|
||||||
var body = try std.json.parseFromTokenSource(std.json.Value, allocator, &json_reader, .{});
|
|
||||||
defer body.deinit();
|
defer body.deinit();
|
||||||
return try allocator.dupe(u8, body.value.object.get("result").?.object.get("subdomain").?.string);
|
return try allocator.dupe(u8, body.value.object.get("result").?.object.get("subdomain").?.string);
|
||||||
}
|
}
|
||||||
|
@ -270,11 +273,11 @@ fn putNewWorker(allocator: std.mem.Allocator, client: *std.http.Client, worker:
|
||||||
"{[memfs]s}\r\n" ++
|
"{[memfs]s}\r\n" ++
|
||||||
"------formdata-undici-032998177938--";
|
"------formdata-undici-032998177938--";
|
||||||
|
|
||||||
var headers = std.http.Headers.init(allocator);
|
var headers = std.ArrayList(std.http.Header).init(allocator);
|
||||||
defer headers.deinit();
|
defer headers.deinit();
|
||||||
try addAuthHeaders(&headers);
|
try addAuthHeaders(&headers);
|
||||||
// TODO: fix this
|
// TODO: fix this
|
||||||
try headers.append("Content-Type", "multipart/form-data; boundary=----formdata-undici-032998177938");
|
try headers.append(.{ .name = "Content-Type", .value = "multipart/form-data; boundary=----formdata-undici-032998177938" });
|
||||||
const request_payload = try std.fmt.allocPrint(allocator, deploy_request, .{
|
const request_payload = try std.fmt.allocPrint(allocator, deploy_request, .{
|
||||||
.script = script,
|
.script = script,
|
||||||
.wasm_name = worker.wasm.name,
|
.wasm_name = worker.wasm.name,
|
||||||
|
@ -282,41 +285,28 @@ fn putNewWorker(allocator: std.mem.Allocator, client: *std.http.Client, worker:
|
||||||
.memfs = memfs,
|
.memfs = memfs,
|
||||||
});
|
});
|
||||||
defer allocator.free(request_payload);
|
defer allocator.free(request_payload);
|
||||||
// Get content length. For some reason it's forcing a chunked transfer type without this.
|
var raw_body = std.ArrayList(u8).init(allocator);
|
||||||
// That's not entirely a bad idea, but for now I want to match what I see
|
defer raw_body.deinit();
|
||||||
// coming through wrangler
|
const req = try client.fetch(.{
|
||||||
const cl = try std.fmt.allocPrint(allocator, "{d}", .{request_payload.len});
|
.response_storage = .{ .dynamic = &raw_body },
|
||||||
defer allocator.free(cl);
|
.method = .PUT,
|
||||||
try headers.append("Content-Length", cl);
|
.payload = request_payload,
|
||||||
var req = try client.request(.PUT, try std.Uri.parse(url), headers, .{});
|
.location = .{ .url = url },
|
||||||
defer req.deinit();
|
.privileged_headers = headers.items,
|
||||||
|
});
|
||||||
|
|
||||||
req.transfer_encoding = .{ .content_length = @as(u64, request_payload.len) };
|
|
||||||
try req.start();
|
|
||||||
|
|
||||||
// Workaround for https://github.com/ziglang/zig/issues/15626
|
|
||||||
const max_bytes: usize = 1 << 14;
|
|
||||||
var inx: usize = 0;
|
|
||||||
while (request_payload.len > inx) {
|
|
||||||
try req.writeAll(request_payload[inx..@min(request_payload.len, inx + max_bytes)]);
|
|
||||||
inx += max_bytes;
|
|
||||||
}
|
|
||||||
try req.finish();
|
|
||||||
try req.wait();
|
|
||||||
// std.debug.print("Status is {}\n", .{req.response.status});
|
// std.debug.print("Status is {}\n", .{req.response.status});
|
||||||
// std.debug.print("Url is {s}\n", .{url});
|
// std.debug.print("Url is {s}\n", .{url});
|
||||||
if (req.response.status != .ok) {
|
if (req.status != .ok) {
|
||||||
std.debug.print("Status is {}\n", .{req.response.status});
|
std.debug.print("Status is {}\n", .{req.status});
|
||||||
if (req.response.status == .bad_request)
|
if (req.status == .bad_request)
|
||||||
worker.errors = getErrors(allocator, &req) catch null;
|
worker.errors = getErrors(allocator, raw_body.items) catch null;
|
||||||
return error.RequestFailed;
|
return error.RequestFailed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getErrors(allocator: std.mem.Allocator, req: *std.http.Client.Request) !?[][]const u8 {
|
fn getErrors(allocator: std.mem.Allocator, response_body: []const u8) !?[][]const u8 {
|
||||||
var json_reader = std.json.reader(allocator, req.reader());
|
var body = try std.json.parseFromSlice(std.json.Value, allocator, response_body, .{});
|
||||||
defer json_reader.deinit();
|
|
||||||
var body = try std.json.parseFromTokenSource(std.json.Value, allocator, &json_reader, .{});
|
|
||||||
defer body.deinit();
|
defer body.deinit();
|
||||||
const arr = body.value.object.get("errors").?.array.items;
|
const arr = body.value.object.get("errors").?.array.items;
|
||||||
if (arr.len == 0) return null;
|
if (arr.len == 0) return null;
|
||||||
|
@ -332,40 +322,54 @@ fn workerExists(allocator: std.mem.Allocator, client: *std.http.Client, account_
|
||||||
const existence_check = cf_api_base ++ "/accounts/{s}/workers/services/{s}";
|
const existence_check = cf_api_base ++ "/accounts/{s}/workers/services/{s}";
|
||||||
const url = try std.fmt.allocPrint(allocator, existence_check, .{ account_id, name });
|
const url = try std.fmt.allocPrint(allocator, existence_check, .{ account_id, name });
|
||||||
defer allocator.free(url);
|
defer allocator.free(url);
|
||||||
var headers = std.http.Headers.init(allocator);
|
var headers = std.ArrayList(std.http.Header).init(allocator);
|
||||||
defer headers.deinit();
|
defer headers.deinit();
|
||||||
try addAuthHeaders(&headers);
|
const req = try client.fetch(.{
|
||||||
var req = try client.request(.GET, try std.Uri.parse(url), headers, .{});
|
.method = .GET,
|
||||||
defer req.deinit();
|
.location = .{ .url = url },
|
||||||
try req.start();
|
.privileged_headers = try createAuthHeaders(),
|
||||||
try req.wait();
|
});
|
||||||
// std.debug.print("Status is {}\n", .{req.response.status});
|
// std.debug.print("Status is {}\n", .{req.response.status});
|
||||||
// std.debug.print("Url is {s}\n", .{url});
|
// std.debug.print("Url is {s}\n", .{url});
|
||||||
return req.response.status == .ok;
|
return req.status == .ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
threadlocal var auth_buf: [1024]u8 = undefined;
|
threadlocal var auth_buf: [1024]u8 = undefined;
|
||||||
|
|
||||||
fn addAuthHeaders(headers: *std.http.Headers) !void {
|
fn addAuthHeaders(headers: *std.ArrayList(std.http.Header)) !void {
|
||||||
if (!initialized) {
|
try headers.appendSlice(try createAuthHeaders());
|
||||||
x_auth_email = std.os.getenv("CLOUDFLARE_EMAIL");
|
}
|
||||||
x_auth_key = std.os.getenv("CLOUDFLARE_API_KEY");
|
fn createAuthHeaders() ![]const std.http.Header {
|
||||||
x_auth_token = std.os.getenv("CLOUDFLARE_API_TOKEN");
|
if (auth_headers) |h| return h;
|
||||||
initialized = true;
|
|
||||||
}
|
// auth_headers not defined - go make it happen
|
||||||
|
var fb_alloc = std.heap.FixedBufferAllocator.init(&auth_header_buf);
|
||||||
|
const alloc = fb_alloc.allocator();
|
||||||
|
var ah = std.ArrayList(std.http.Header).init(alloc);
|
||||||
|
|
||||||
|
x_auth_email = std.posix.getenv("CLOUDFLARE_EMAIL");
|
||||||
|
x_auth_key = std.posix.getenv("CLOUDFLARE_API_KEY");
|
||||||
|
x_auth_token = std.posix.getenv("CLOUDFLARE_API_TOKEN");
|
||||||
|
|
||||||
|
blk: {
|
||||||
if (x_auth_token) |tok| {
|
if (x_auth_token) |tok| {
|
||||||
var auth = try std.fmt.bufPrint(auth_buf[0..], "Bearer {s}", .{tok});
|
const auth = try std.fmt.bufPrint(auth_buf[0..], "Bearer {s}", .{tok});
|
||||||
try headers.append("Authorization", auth);
|
try ah.append(.{ .name = "Authorization", .value = auth });
|
||||||
return;
|
break :blk;
|
||||||
}
|
}
|
||||||
if (x_auth_email) |email| {
|
if (x_auth_email) |email| {
|
||||||
if (x_auth_key == null)
|
if (x_auth_key == null)
|
||||||
return error.MissingCloudflareApiKeyEnvironmentVariable;
|
return error.MissingCloudflareApiKeyEnvironmentVariable;
|
||||||
try headers.append("X-Auth-Email", email);
|
try ah.append(.{ .name = "X-Auth-Email", .value = email });
|
||||||
try headers.append("X-Auth-Key", x_auth_key.?);
|
try ah.append(.{ .name = "X-Auth-Key", .value = x_auth_key.? });
|
||||||
|
break :blk;
|
||||||
}
|
}
|
||||||
return error.NoCfAuthenticationEnvironmentVariablesSet;
|
return error.NoCfAuthenticationEnvironmentVariablesSet;
|
||||||
|
}
|
||||||
|
auth_headers = ah.items;
|
||||||
|
return auth_headers.?;
|
||||||
}
|
}
|
||||||
|
|
||||||
test "simple test" {
|
test "simple test" {
|
||||||
var list = std.ArrayList(i32).init(std.testing.allocator);
|
var list = std.ArrayList(i32).init(std.testing.allocator);
|
||||||
defer list.deinit(); // try commenting this out and see if zig detects the memory leak!
|
defer list.deinit(); // try commenting this out and see if zig detects the memory leak!
|
||||||
|
|
Loading…
Reference in New Issue
Block a user