Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 46 additions & 27 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,52 @@ on:

jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]

include:
- os: ubuntu-latest
artifact_name: zerver-example-linux
artifact_path: zig-out/bin/zerver_example
- os: macos-latest
artifact_name: zerver-example-macos
artifact_path: zig-out/bin/zerver_example
- os: windows-latest
artifact_name: zerver-example-windows
artifact_path: zig-out/bin/zerver_example.exe
runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v4

- name: Cache Zig build cache
id: cache-zig
uses: actions/cache@v4
with:
path: ~/.zig-cache
key: ${{ runner.os }}-zig-${{ hashFiles('build.zig') }}
restore-keys: |
${{ runner.os }}-zig-

- name: Install Zig
uses: ziglang/setup-zig@v1
with:
zig-version: 0.15.2

- name: Build
run: zig build

- name: Test
run: zig test

- name: Check formatting
run: zig fmt --check src/ examples/
- uses: actions/checkout@v4

- name: Cache Zig build cache
id: cache-zig
uses: actions/cache@v4
with:
path: |
zig-cache
~/.cache/zig
~/AppData/Local/zig
key: ${{ runner.os }}-zig-${{ hashFiles('build.zig', 'main.zig', 'src/**', 'tests/**', 'examples/**', 'third_party/libuv/**') }}
restore-keys: |
${{ runner.os }}-zig-

- name: Install Zig
uses: ziglang/setup-zig@v1
with:
zig-version: 0.15.2

- name: Build (ReleaseSafe)
run: zig build -Doptimize=ReleaseSafe

- name: Run tests
run: zig build test

- name: Check formatting
run: zig fmt --check .

- name: Upload executable
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact_name }}
path: ${{ matrix.artifact_path }}
if-no-files-found: error
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,20 @@ We welcome discussion, feedback, and contributions on the design. Please review

---

## Configuration

Runtime settings live in `config.json`. The file now centralises:

- database driver, file path, pool size, and busy timeout
- blocking thread pool sizing for legacy/middleware work
- reactor pools (continuation workers, effector workers, compute pool type/size)
- HTTP server bind address
- observability endpoints and Tempo auto-detection

Tuning pool sizes here avoids recompilation; `runtime/config.zig` loads the `reactor` section and feeds those values into the job and task systems during startup.

---

## Full Example: Todo CRUD API

For a complete, runnable example demonstrating all of Zerver's core features working together, see [`examples/todo_crud.zig`](examples/todo_crud.zig).
Expand Down
146 changes: 146 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,71 @@
const std = @import("std");

const libuv_source_files = [_][]const u8{
"third_party/libuv/src/fs-poll.c",
"third_party/libuv/src/idna.c",
"third_party/libuv/src/inet.c",
"third_party/libuv/src/random.c",
"third_party/libuv/src/strscpy.c",
"third_party/libuv/src/strtok.c",
"third_party/libuv/src/thread-common.c",
"third_party/libuv/src/threadpool.c",
"third_party/libuv/src/timer.c",
"third_party/libuv/src/uv-common.c",
"third_party/libuv/src/uv-data-getter-setters.c",
"third_party/libuv/src/version.c",
"third_party/libuv/src/win/async.c",
"third_party/libuv/src/win/core.c",
"third_party/libuv/src/win/detect-wakeup.c",
"third_party/libuv/src/win/dl.c",
"third_party/libuv/src/win/error.c",
"third_party/libuv/src/win/fs.c",
"third_party/libuv/src/win/fs-event.c",
"third_party/libuv/src/win/getaddrinfo.c",
"third_party/libuv/src/win/getnameinfo.c",
"third_party/libuv/src/win/handle.c",
"third_party/libuv/src/win/loop-watcher.c",
"third_party/libuv/src/win/pipe.c",
"third_party/libuv/src/win/thread.c",
"third_party/libuv/src/win/poll.c",
"third_party/libuv/src/win/process.c",
"third_party/libuv/src/win/process-stdio.c",
"third_party/libuv/src/win/signal.c",
"third_party/libuv/src/win/snprintf.c",
"third_party/libuv/src/win/stream.c",
"third_party/libuv/src/win/tcp.c",
"third_party/libuv/src/win/tty.c",
"third_party/libuv/src/win/udp.c",
"third_party/libuv/src/win/util.c",
"third_party/libuv/src/win/winapi.c",
"third_party/libuv/src/win/winsock.c",
};

const libuv_system_libs = [_][]const u8{
"psapi",
"user32",
"advapi32",
"iphlpapi",
"userenv",
"ws2_32",
"dbghelp",
"ole32",
"shell32",
};

fn addLibuv(b: *std.Build, artifact: *std.Build.Step.Compile) void {
artifact.root_module.addIncludePath(b.path("third_party/libuv/include"));
artifact.root_module.addIncludePath(b.path("third_party/libuv/src"));
inline for (libuv_source_files) |path| {
artifact.addCSourceFile(.{ .file = b.path(path), .flags = &[_][]const u8{} });
}
artifact.root_module.addCMacro("WIN32_LEAN_AND_MEAN", "1");
artifact.root_module.addCMacro("_WIN32_WINNT", "0x0A00");
artifact.root_module.addCMacro("_CRT_DECLARE_NONSTDC_NAMES", "0");
inline for (libuv_system_libs) |name| {
artifact.linkSystemLibrary(name);
}
}

pub fn build(b: *std.Build) void {
// Check Zig version compatibility (build-time check)
comptime {
Expand Down Expand Up @@ -32,6 +98,8 @@ pub fn build(b: *std.Build) void {
},
});
exe.linkLibC();
addLibuv(b, exe);
b.installArtifact(exe);

const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
Expand All @@ -44,6 +112,11 @@ pub fn build(b: *std.Build) void {
const zerver_mod = b.createModule(.{
.root_source_file = b.path("src/zerver/root.zig"),
});
zerver_mod.addIncludePath(b.path("third_party/libuv/include"));
zerver_mod.addIncludePath(b.path("third_party/libuv/src"));
zerver_mod.addCMacro("WIN32_LEAN_AND_MEAN", "1");
zerver_mod.addCMacro("_WIN32_WINNT", "0x0A00");
zerver_mod.addCMacro("_CRT_DECLARE_NONSTDC_NAMES", "0");

// Development helper steps
const test_step = b.step("test", "Run all tests");
Expand All @@ -67,6 +140,79 @@ pub fn build(b: *std.Build) void {
test_exe.linkLibC();
test_step.dependOn(&b.addRunArtifact(test_exe).step);

const libuv_smoke = b.addExecutable(.{
.name = "libuv_smoke",
.root_module = b.createModule(.{
.root_source_file = b.path("tests/libuv_smoke.zig"),
.target = target,
.optimize = optimize,
}),
});
libuv_smoke.linkLibC();
addLibuv(b, libuv_smoke);
const libuv_smoke_run = b.addRunArtifact(libuv_smoke);
test_step.dependOn(&libuv_smoke_run.step);
const libuv_smoke_step = b.step("libuv_smoke", "Run the libuv smoke test");
libuv_smoke_step.dependOn(&libuv_smoke_run.step);

const join_tests = b.addTest(.{
.root_module = b.createModule(.{
.root_source_file = b.path("tests/unit/reactor_join.zig"),
.target = target,
.optimize = optimize,
}),
});
join_tests.root_module.addImport("zerver", zerver_mod);
const join_tests_run = b.addRunArtifact(join_tests);
const reactor_tests_step = b.step("reactor_tests", "Run reactor unit tests");
reactor_tests_step.dependOn(&join_tests_run.step);

const job_tests = b.addTest(.{
.root_module = b.createModule(.{
.root_source_file = b.path("tests/unit/reactor_job_system.zig"),
.target = target,
.optimize = optimize,
}),
});
job_tests.root_module.addImport("zerver", zerver_mod);
const job_tests_run = b.addRunArtifact(job_tests);
reactor_tests_step.dependOn(&job_tests_run.step);

const effectors_tests = b.addTest(.{
.root_module = b.createModule(.{
.root_source_file = b.path("tests/unit/reactor_effectors.zig"),
.target = target,
.optimize = optimize,
}),
});
effectors_tests.root_module.addImport("zerver", zerver_mod);
effectors_tests.linkLibC();
addLibuv(b, effectors_tests);
const effectors_tests_run = b.addRunArtifact(effectors_tests);
reactor_tests_step.dependOn(&effectors_tests_run.step);

const saga_tests = b.addTest(.{
.root_module = b.createModule(.{
.root_source_file = b.path("tests/unit/reactor_saga.zig"),
.target = target,
.optimize = optimize,
}),
});
saga_tests.root_module.addImport("zerver", zerver_mod);
const saga_tests_run = b.addRunArtifact(saga_tests);
reactor_tests_step.dependOn(&saga_tests_run.step);

const task_system_tests = b.addTest(.{
.root_module = b.createModule(.{
.root_source_file = b.path("tests/unit/reactor_task_system.zig"),
.target = target,
.optimize = optimize,
}),
});
task_system_tests.root_module.addImport("zerver", zerver_mod);
const task_system_tests_run = b.addRunArtifact(task_system_tests);
reactor_tests_step.dependOn(&task_system_tests_run.step);

const fmt_step = b.step("fmt", "Format all Zig files");
const fmt_cmd = b.addSystemCommand(&[_][]const u8{ "zig", "fmt", "." });
fmt_step.dependOn(&fmt_cmd.step);
Expand Down
22 changes: 22 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
{
"project": {
"name": "Zerver",
"version": "0.1.0",
"description": "Zerver is a backend framework for Zig that gives you X-ray vision into your API. It's built on the idea that observability isn't a feature you add later—it's the architecture.",
"repository": "https://github.com/monstercameron/Zerver"
},
"database": {
"driver": "sqlite",
"path": "resources/blog.db",
Expand All @@ -8,6 +14,22 @@
"thread_pool": {
"worker_count": 4
},
"reactor": {
"enabled": true,
"continuation_pool": {
"size": 4,
"queue_capacity": 1024
},
"effector_pool": {
"size": 4,
"queue_capacity": 1024
},
"compute_pool": {
"type": "disabled",
"size": 0,
"queue_capacity": 0
}
},
"server": {
"host": "127.0.0.1",
"port": 8080
Expand Down
Loading
Loading