From 2e453aeafc58fbb94204489b2a9a5bb66689d6be Mon Sep 17 00:00:00 2001 From: Charles Fonseca Date: Fri, 24 Apr 2026 17:02:51 -0300 Subject: [PATCH 1/7] ci: add CI workflow for testing and release processes test: standardize store initialization in tests --- .github/workflows/ci.yml | 24 ++++++++++ .github/workflows/release.yml | 88 ++++++++++++++++++++++++++--------- src/testing/list.zig | 60 ++++++++++++------------ src/testing/string.zig | 64 ++++++++++++------------- src/testing/time_series.zig | 14 +++--- 5 files changed, 159 insertions(+), 91 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..89b7dc1 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,24 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + test: + name: Test + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v5 + + - name: Setup Zig + uses: mlugg/setup-zig@v2 + with: + version: master + + - name: Run tests + run: zig build test:all diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index aed3359..96a224a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,8 +9,25 @@ permissions: contents: write jobs: + test: + name: Test + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v5 + + - name: Setup Zig + uses: mlugg/setup-zig@v2 + with: + version: master + + - name: Run tests + run: zig build test:all + build: name: Build ${{ matrix.platform }} + needs: test runs-on: ubuntu-latest strategy: matrix: @@ -18,15 +35,23 @@ jobs: - platform: linux-x86_64 target: x86_64-linux binary: zedis + ext: "" + - platform: linux-aarch64 + target: aarch64-linux + binary: zedis + ext: "" - platform: macos-x86_64 target: x86_64-macos binary: zedis + ext: "" - platform: macos-arm64 target: aarch64-macos binary: zedis - # - platform: windows-x86_64 - # target: x86_64-windows - # binary: zedis.exe + ext: "" + - platform: windows-x86_64 + target: x86_64-windows + binary: zedis.exe + ext: ".exe" steps: - name: Checkout code @@ -44,13 +69,13 @@ jobs: - name: Build run: | zig build -Doptimize=ReleaseFast -Dtarget=${{ matrix.target }} - cp zig-out/bin/${{ matrix.binary }} zedis-${{ steps.version.outputs.VERSION }}-${{ matrix.platform }}${{ matrix.platform == 'windows-x86_64' && '.exe' || '' }} + cp zig-out/bin/${{ matrix.binary }} zedis-${{ steps.version.outputs.VERSION }}-${{ matrix.platform }}${{ matrix.ext }} - name: Upload artifact uses: actions/upload-artifact@v4 with: name: zedis-${{ steps.version.outputs.VERSION }}-${{ matrix.platform }} - path: zedis-${{ steps.version.outputs.VERSION }}-${{ matrix.platform }}${{ matrix.platform == 'windows-x86_64' && '.exe' || '' }} + path: zedis-${{ steps.version.outputs.VERSION }}-${{ matrix.platform }}${{ matrix.ext }} release: name: Create Release @@ -87,44 +112,63 @@ jobs: with: files: | zedis-${{ steps.version.outputs.VERSION }}-linux-x86_64 + zedis-${{ steps.version.outputs.VERSION }}-linux-aarch64 zedis-${{ steps.version.outputs.VERSION }}-macos-x86_64 zedis-${{ steps.version.outputs.VERSION }}-macos-arm64 - # zedis-${{ steps.version.outputs.VERSION }}-windows-x86_64.exe + zedis-${{ steps.version.outputs.VERSION }}-windows-x86_64.exe SHA256SUMS body: | ${{ steps.changelog.outputs.changes }} --- - ## Installation + ## Install + + **Linux (x86_64)** + ```bash + curl -Lo zedis https://github.com/charlesfonseca/zedis/releases/download/${{ steps.version.outputs.VERSION }}/zedis-${{ steps.version.outputs.VERSION }}-linux-x86_64 + chmod +x zedis && sudo mv zedis /usr/local/bin/ + ``` - ### Download Binaries + **Linux (ARM64)** + ```bash + curl -Lo zedis https://github.com/charlesfonseca/zedis/releases/download/${{ steps.version.outputs.VERSION }}/zedis-${{ steps.version.outputs.VERSION }}-linux-aarch64 + chmod +x zedis && sudo mv zedis /usr/local/bin/ + ``` - Pre-built binaries are available for Linux, macOS, and Windows. + **macOS (Apple Silicon)** + ```bash + curl -Lo zedis https://github.com/charlesfonseca/zedis/releases/download/${{ steps.version.outputs.VERSION }}/zedis-${{ steps.version.outputs.VERSION }}-macos-arm64 + chmod +x zedis && sudo mv zedis /usr/local/bin/ + ``` + + **macOS (Intel)** + ```bash + curl -Lo zedis https://github.com/charlesfonseca/zedis/releases/download/${{ steps.version.outputs.VERSION }}/zedis-${{ steps.version.outputs.VERSION }}-macos-x86_64 + chmod +x zedis && sudo mv zedis /usr/local/bin/ + ``` - 1. Download the appropriate binary for your platform - 2. Verify the checksum against SHA256SUMS - 3. Make the binary executable (Linux/macOS): `chmod +x zedis-*` - 4. Run the server: `./zedis-${{ steps.version.outputs.VERSION }}-` + **Windows** — download `zedis-${{ steps.version.outputs.VERSION }}-windows-x86_64.exe` from the assets above. - ### Verify Checksums + ### Verify checksums ```bash sha256sum -c SHA256SUMS ``` - ### Quick Start + ## Quick start ```bash - # Start the server (default: 127.0.0.1:6379) - ./zedis- + # Start server (listens on 127.0.0.1:6379 by default) + zedis - # Connect using redis-cli - redis-cli -h 127.0.0.1 -p 6379 + # Connect + redis-cli ping + # PONG - # Try some commands - SET mykey "Hello, Zedis!" - GET mykey + SET hello world + GET hello + # "world" ``` draft: false prerelease: false diff --git a/src/testing/list.zig b/src/testing/list.zig index 5711f92..1d5df88 100644 --- a/src/testing/list.zig +++ b/src/testing/list.zig @@ -14,7 +14,7 @@ test "LPUSH single element to new list" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -42,7 +42,7 @@ test "LPUSH multiple elements to new list" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -72,7 +72,7 @@ test "LPUSH to existing list" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -110,7 +110,7 @@ test "RPUSH single element to new list" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -137,7 +137,7 @@ test "RPUSH multiple elements to new list" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -167,7 +167,7 @@ test "LPOP from list with single element" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -202,7 +202,7 @@ test "LPOP from non-existing list" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -224,7 +224,7 @@ test "LPOP with count from list with multiple elements" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -264,7 +264,7 @@ test "LPOP with count of 0" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -297,7 +297,7 @@ test "RPOP from list with single element" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -328,7 +328,7 @@ test "RPOP with count from list with multiple elements" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -369,7 +369,7 @@ test "LLEN on existing list" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -402,7 +402,7 @@ test "LLEN on non-existing list" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -424,7 +424,7 @@ test "LLEN on empty list" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -461,7 +461,7 @@ test "Mixed LPUSH and RPUSH operations" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -510,7 +510,7 @@ test "LPOP and RPOP from the same list" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -561,7 +561,7 @@ test "LINDEX get first element" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -595,7 +595,7 @@ test "LINDEX get last element with negative index" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -629,7 +629,7 @@ test "LINDEX with out of range index" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -661,7 +661,7 @@ test "LINDEX on non-existing list" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -685,7 +685,7 @@ test "LSET update element at index" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -731,7 +731,7 @@ test "LSET with negative index" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -777,7 +777,7 @@ test "LSET on non-existing key" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -800,7 +800,7 @@ test "LSET with out of range index" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -834,7 +834,7 @@ test "LRANGE get all elements" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -869,7 +869,7 @@ test "LRANGE get subset of elements" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -906,7 +906,7 @@ test "LRANGE with negative indices" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -943,7 +943,7 @@ test "LRANGE on non-existing list" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -967,7 +967,7 @@ test "LRANGE with out of range indices" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -1001,7 +1001,7 @@ test "LRANGE with reversed range" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; diff --git a/src/testing/string.zig b/src/testing/string.zig index 8d815bd..71bde35 100644 --- a/src/testing/string.zig +++ b/src/testing/string.zig @@ -13,7 +13,7 @@ test "SET command with string value" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -40,7 +40,7 @@ test "SET command with integer value" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -67,7 +67,7 @@ test "GET command with existing string value" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -91,7 +91,7 @@ test "GET command with existing integer value" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -115,7 +115,7 @@ test "GET command with non-existing key" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -137,7 +137,7 @@ test "INCR command on non-existing key" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -163,7 +163,7 @@ test "INCR command on existing integer" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -191,7 +191,7 @@ test "INCR command on string that represents integer" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -219,7 +219,7 @@ test "INCR command on non-integer string" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -242,7 +242,7 @@ test "DECR command on non-existing key" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -268,7 +268,7 @@ test "DECR command on existing integer" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -296,7 +296,7 @@ test "DEL command with single existing key" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -323,7 +323,7 @@ test "DEL command with multiple keys" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -356,7 +356,7 @@ test "DEL command with non-existing key" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -378,7 +378,7 @@ test "APPEND command on non-existing key" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -405,7 +405,7 @@ test "APPEND command on existing key" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -434,7 +434,7 @@ test "STRLEN command on existing key" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -458,7 +458,7 @@ test "STRLEN command on non-existing key" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -480,7 +480,7 @@ test "GETSET command on existing key" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -509,7 +509,7 @@ test "GETSET command on non-existing key" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -536,7 +536,7 @@ test "MGET command with multiple keys" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -563,7 +563,7 @@ test "MSET command with multiple key-value pairs" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -596,7 +596,7 @@ test "SETEX command sets key with expiration" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -624,7 +624,7 @@ test "SETNX command on non-existing key" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -651,7 +651,7 @@ test "SETNX command on existing key" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -680,7 +680,7 @@ test "INCRBY command on non-existing key" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -707,7 +707,7 @@ test "INCRBY command on existing integer" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -736,7 +736,7 @@ test "DECRBY command on non-existing key" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -763,7 +763,7 @@ test "DECRBY command on existing integer" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -792,7 +792,7 @@ test "INCRBYFLOAT command on non-existing key" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -815,7 +815,7 @@ test "INCRBYFLOAT command on existing float" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -841,7 +841,7 @@ test "INCRBYFLOAT command with negative increment" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; diff --git a/src/testing/time_series.zig b/src/testing/time_series.zig index 40c58d2..a0d22ba 100644 --- a/src/testing/time_series.zig +++ b/src/testing/time_series.zig @@ -525,7 +525,7 @@ test "TS.INCRBY increments from zero" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -577,7 +577,7 @@ test "TS.INCRBY increments from existing value" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -630,7 +630,7 @@ test "TS.DECRBY decrements value" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -683,7 +683,7 @@ test "TS.ALTER changes retention" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -722,7 +722,7 @@ test "TS.ALTER changes duplicate policy" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -945,7 +945,7 @@ test "TS.RANGE command with COUNT parameter" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; @@ -1617,7 +1617,7 @@ test "TS.RANGE command with aggregation parameter" { const allocator = arena.allocator(); var clock = Clock.init(testing.io, 0); - var store = try Store.init(allocator, testing.io, &clock, .{.initial_capacity = 4096}); + var store = try Store.init(allocator, testing.io, &clock, .{ .initial_capacity = 4096 }); defer store.deinit(); var buffer: [4096]u8 = undefined; From 6eb333fd8bb2913c3077a4a2619569349374ae25 Mon Sep 17 00:00:00 2001 From: Charles Fonseca Date: Fri, 24 Apr 2026 17:12:05 -0300 Subject: [PATCH 2/7] ci: update actions/checkout and actions/upload-artifact versions; add Docker workflow --- .github/workflows/ci.yml | 2 +- .github/workflows/docker.yml | 43 ++++++++++++++++ .github/workflows/release.yml | 10 ++-- Dockerfile | 96 +++++++++++++++++++++++++++++++++++ docker-bake.hcl | 44 ++++++++++++++++ docker/zedis-distroless.conf | 13 +++++ 6 files changed, 202 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/docker.yml create mode 100644 Dockerfile create mode 100644 docker-bake.hcl create mode 100644 docker/zedis-distroless.conf diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 89b7dc1..cd7b551 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Setup Zig uses: mlugg/setup-zig@v2 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..f049968 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,43 @@ +name: Docker + +on: + push: + tags: + - '*.*.*' + +permissions: + packages: write + +jobs: + docker: + name: Build and push + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v6 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Log in to GHCR + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract version from tag + id: version + run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT + + - name: Build and push all variants + uses: docker/bake-action@v7 + env: + VERSION: ${{ steps.version.outputs.VERSION }} + REGISTRY: ghcr.io/${{ github.repository }} + with: + push: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 96a224a..e043c43 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Setup Zig uses: mlugg/setup-zig@v2 @@ -55,7 +55,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Setup Zig uses: mlugg/setup-zig@v2 @@ -72,7 +72,7 @@ jobs: cp zig-out/bin/${{ matrix.binary }} zedis-${{ steps.version.outputs.VERSION }}-${{ matrix.platform }}${{ matrix.ext }} - name: Upload artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: zedis-${{ steps.version.outputs.VERSION }}-${{ matrix.platform }} path: zedis-${{ steps.version.outputs.VERSION }}-${{ matrix.platform }}${{ matrix.ext }} @@ -84,7 +84,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Extract version from tag id: version @@ -108,7 +108,7 @@ jobs: sha256sum zedis-${{ steps.version.outputs.VERSION }}-* > SHA256SUMS - name: Create GitHub Release - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@v3 with: files: | zedis-${{ steps.version.outputs.VERSION }}-linux-x86_64 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d41ac68 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,96 @@ +# syntax=docker/dockerfile:1 + +############################################################################## +# Stage: install Zig and build a static musl binary (works in all variants) +############################################################################## +FROM alpine:3.20 AS builder + +ARG ZIG_VERSION=0.16.0 +ARG ZIG_MIRROR_URL=https://ziglang.org/download/community-mirrors + +RUN apk add --no-cache curl xz + +RUN case "$(uname -m)" in \ + x86_64) ZIG_ARCH=x86_64 ;; \ + aarch64) ZIG_ARCH=aarch64 ;; \ + *) echo "Unsupported architecture: $(uname -m)" >&2; exit 1 ;; \ + esac && \ + curl -fsSL "${ZIG_MIRROR_URL}/${ZIG_VERSION}/zig-linux-${ZIG_ARCH}-${ZIG_VERSION}.tar.xz" | \ + tar -xJ -C /opt && \ + ln -s /opt/zig-linux-${ZIG_ARCH}-${ZIG_VERSION} /opt/zig && \ + ln -s /opt/zig/zig /usr/local/bin/zig + +WORKDIR /build +COPY build.zig build.zig.zon ./ +COPY src src/ + +RUN zig build -Doptimize=ReleaseSafe -Dtarget="$(uname -m)-linux-musl" + +############################################################################## +# Stage: create data dir with correct ownership for distroless +############################################################################## +FROM busybox:musl AS dirs + +RUN mkdir -p /home/nonroot/data && \ + chown -R 65532:65532 /home/nonroot && \ + touch /home/nonroot/data/.keep + +############################################################################## +# alpine — default variant, smallest with shell access +############################################################################## +FROM alpine:3.20 AS alpine + +RUN apk add --no-cache ca-certificates tzdata && \ + adduser -D -s /sbin/nologin zedis + +COPY --from=builder /build/zig-out/bin/zedis /usr/local/bin/zedis +COPY docker/zedis.conf /etc/zedis/zedis.conf + +RUN mkdir -p /var/lib/zedis/data && \ + chown -R zedis:zedis /var/lib/zedis /etc/zedis + +USER zedis +WORKDIR /var/lib/zedis +EXPOSE 6379 +HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \ + CMD sh -c 'printf "PING\r\n" | nc localhost 6379 -w1 | grep -q PONG' +ENTRYPOINT ["zedis", "/etc/zedis/zedis.conf"] + +############################################################################## +# debian — glibc base, wider ecosystem compatibility +############################################################################## +FROM debian:bookworm-slim AS debian + +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + tzdata \ + netcat-openbsd \ + && rm -rf /var/lib/apt/lists/* \ + && useradd -r -s /usr/sbin/nologin zedis + +COPY --from=builder /build/zig-out/bin/zedis /usr/local/bin/zedis +COPY docker/zedis.conf /etc/zedis/zedis.conf + +RUN mkdir -p /var/lib/zedis/data && \ + chown -R zedis:zedis /var/lib/zedis /etc/zedis + +USER zedis +WORKDIR /var/lib/zedis +EXPOSE 6379 +HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \ + CMD nc -z localhost 6379 +ENTRYPOINT ["zedis", "/etc/zedis/zedis.conf"] + +############################################################################## +# distroless — no shell, smallest attack surface +############################################################################## +FROM gcr.io/distroless/static-debian12 AS distroless + +COPY --from=builder /build/zig-out/bin/zedis /usr/local/bin/zedis +COPY docker/zedis-distroless.conf /etc/zedis/zedis.conf +COPY --from=dirs /home/nonroot/data /home/nonroot/data + +USER nonroot +WORKDIR /home/nonroot +EXPOSE 6379 +ENTRYPOINT ["zedis", "/etc/zedis/zedis.conf"] diff --git a/docker-bake.hcl b/docker-bake.hcl new file mode 100644 index 0000000..df1e44c --- /dev/null +++ b/docker-bake.hcl @@ -0,0 +1,44 @@ +variable "REGISTRY" { + default = "ghcr.io/charlesfonseca/zedis" +} + +variable "VERSION" { + default = "dev" +} + +group "default" { + targets = ["alpine", "debian", "distroless"] +} + +target "_base" { + context = "." + dockerfile = "Dockerfile" + platforms = ["linux/amd64", "linux/arm64"] +} + +target "alpine" { + inherits = ["_base"] + target = "alpine" + tags = [ + "${REGISTRY}:${VERSION}-alpine", + "${REGISTRY}:${VERSION}", + "${REGISTRY}:latest", + ] +} + +target "debian" { + inherits = ["_base"] + target = "debian" + tags = [ + "${REGISTRY}:${VERSION}-debian", + "${REGISTRY}:${VERSION}-slim", + ] +} + +target "distroless" { + inherits = ["_base"] + target = "distroless" + tags = [ + "${REGISTRY}:${VERSION}-distroless", + ] +} diff --git a/docker/zedis-distroless.conf b/docker/zedis-distroless.conf new file mode 100644 index 0000000..75332ba --- /dev/null +++ b/docker/zedis-distroless.conf @@ -0,0 +1,13 @@ +bind 0.0.0.0 +port 6379 +protected-mode no + +dir /home/nonroot/data +dbfilename dump.rdb + +appendonly no + +maxclients 10000 +kv-memory-budget 512mb +temp-arena-size 128mb +eviction-policy allkeys-lru From 8927fc28aed46b58eb34981c0cf4590633b180c0 Mon Sep 17 00:00:00 2001 From: Charles Fonseca Date: Fri, 24 Apr 2026 17:18:06 -0300 Subject: [PATCH 3/7] ci: specify Zig version to 0.16.0 in CI workflow Co-authored-by: Copilot --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cd7b551..849cf98 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: - name: Setup Zig uses: mlugg/setup-zig@v2 with: - version: master + version: 0.16.0 - name: Run tests run: zig build test:all diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e043c43..e92a190 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,7 +20,7 @@ jobs: - name: Setup Zig uses: mlugg/setup-zig@v2 with: - version: master + version: 0.16.0 - name: Run tests run: zig build test:all @@ -60,7 +60,7 @@ jobs: - name: Setup Zig uses: mlugg/setup-zig@v2 with: - version: master + version: 0.16.0 - name: Extract version from tag id: version From b9771565ce1a37df1005a7e423dbdc61114b4264 Mon Sep 17 00:00:00 2001 From: Charles Fonseca Date: Fri, 24 Apr 2026 17:23:32 -0300 Subject: [PATCH 4/7] ci: update Docker login to use Docker Hub and change default registry in docker-bake.hcl --- .github/workflows/docker.yml | 12 ++++-------- docker-bake.hcl | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index f049968..a4b7d4c 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -5,9 +5,6 @@ on: tags: - '*.*.*' -permissions: - packages: write - jobs: docker: name: Build and push @@ -23,12 +20,11 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 - - name: Log in to GHCR + - name: Log in to Docker Hub uses: docker/login-action@v4 with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Extract version from tag id: version @@ -38,6 +34,6 @@ jobs: uses: docker/bake-action@v7 env: VERSION: ${{ steps.version.outputs.VERSION }} - REGISTRY: ghcr.io/${{ github.repository }} + REGISTRY: ${{ secrets.DOCKERHUB_USERNAME }}/zedis with: push: true diff --git a/docker-bake.hcl b/docker-bake.hcl index df1e44c..657d0fb 100644 --- a/docker-bake.hcl +++ b/docker-bake.hcl @@ -1,5 +1,5 @@ variable "REGISTRY" { - default = "ghcr.io/charlesfonseca/zedis" + default = "barddoo/zedis" } variable "VERSION" { From 37e48b072b04c42f201b29542aad9fe235c5e788 Mon Sep 17 00:00:00 2001 From: Charles Fonseca Date: Fri, 24 Apr 2026 18:02:08 -0300 Subject: [PATCH 5/7] ci: update Zig installation process and improve file reading in config --- Dockerfile | 5 ++--- src/config.zig | 16 ++++++---------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/Dockerfile b/Dockerfile index d41ac68..386a731 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,6 @@ FROM alpine:3.20 AS builder ARG ZIG_VERSION=0.16.0 -ARG ZIG_MIRROR_URL=https://ziglang.org/download/community-mirrors RUN apk add --no-cache curl xz @@ -15,9 +14,9 @@ RUN case "$(uname -m)" in \ aarch64) ZIG_ARCH=aarch64 ;; \ *) echo "Unsupported architecture: $(uname -m)" >&2; exit 1 ;; \ esac && \ - curl -fsSL "${ZIG_MIRROR_URL}/${ZIG_VERSION}/zig-linux-${ZIG_ARCH}-${ZIG_VERSION}.tar.xz" | \ + curl -fsSL "https://ziglang.org/download/${ZIG_VERSION}/zig-${ZIG_ARCH}-linux-${ZIG_VERSION}.tar.xz" | \ tar -xJ -C /opt && \ - ln -s /opt/zig-linux-${ZIG_ARCH}-${ZIG_VERSION} /opt/zig && \ + ln -s /opt/zig-${ZIG_ARCH}-linux-${ZIG_VERSION} /opt/zig && \ ln -s /opt/zig/zig /usr/local/bin/zig WORKDIR /build diff --git a/src/config.zig b/src/config.zig index f61d44c..4d68191 100644 --- a/src/config.zig +++ b/src/config.zig @@ -1,5 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); +const Dir = std.Io.Dir; const Client = @import("./client.zig").Client; const ClientHandle = @import("./types.zig").ClientHandle; const eql = std.mem.eql; @@ -189,21 +190,16 @@ pub fn readConfig(allocator: std.mem.Allocator, io: std.Io, args: std.process.Ar } fn readFile(allocator: std.mem.Allocator, io: std.Io, file_name: []const u8) !Config { - var file = try std.Io.Dir.cwd().openFile(io, file_name, .{ .mode = .read_only }); + var file = try Dir.cwd().openFile(io, file_name, .{}); defer file.close(io); var buffer: [1024 * 8]u8 = undefined; - var file_reader = file.reader(io, &buffer); - var reader = &file_reader.interface; + const n = try file.readPositionalAll(io, &buffer, 0); + const content = buffer[0..n]; var config: Config = .{}; - - while (true) { - const line = reader.takeDelimiterExclusive('\n') catch |err| switch (err) { - error.EndOfStream => break, - else => return err, - }; - + var lines = std.mem.splitScalar(u8, content, '\n'); + while (lines.next()) |line| { const trimmed = std.mem.trim(u8, line, " \t\r"); // Skip empty lines and comments From f2f08a8214548102fee375460736cbe1a62ad5d6 Mon Sep 17 00:00:00 2001 From: Charles Fonseca Date: Fri, 24 Apr 2026 18:03:26 -0300 Subject: [PATCH 6/7] ci: update CHANGELOG with new features, fixes, and changes for version 0.1.1 --- CHANGELOG.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb21424..fe913ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed +## [0.1.1] - 2026-04-24 + +### Added + +- Add CI workflow running tests on push to main and pull requests. +- Add multi-variant Docker images: alpine, debian-slim, and distroless. +- Add Docker Bake configuration for parallel multi-platform builds (linux/amd64, linux/arm64). +- Add Docker Hub publishing workflow on tag push. +- Add release binaries for linux-aarch64 and windows-x86_64. + +### Fixed + +- Fix config file loading hanging on async file reader EOF by switching to `readPositionalAll`. +- Fix Dockerfile: drop unnecessary build dependencies, fix symlink trailing slash, use nologin shell for service user, fix healthcheck. + +### Changed + +- Gate release binary builds on passing tests. +- Update all GitHub Actions to latest versions. + ## [0.1.0] - 2026-04-23 ### Added From ebde99b49e207fc6b37c76c2498cac57b6c14439 Mon Sep 17 00:00:00 2001 From: Charles Fonseca Date: Fri, 24 Apr 2026 18:11:21 -0300 Subject: [PATCH 7/7] ci: add Docker usage instructions and image variants to README --- README.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/README.md b/README.md index 46c22d7..5241b49 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,36 @@ OK string ``` +## Docker + +Pre-built images are available on [Docker Hub](https://hub.docker.com/r/barddoo/zedis): + +```bash +docker run -p 6379:6379 barddoo/zedis +``` + +### Variants + +| Tag | Base | Size | Use case | +|-----|------|------|----------| +| `latest`, `x.x.x-alpine` | Alpine 3.20 | Smallest with shell | Default | +| `x.x.x-debian`, `x.x.x-slim` | Debian Bookworm slim | glibc compat | Broader ecosystem | +| `x.x.x-distroless` | Distroless static | No shell | Security-sensitive | + +### Persistent data + +Mount a volume to persist data across restarts: + +```bash +docker run -p 6379:6379 -v zedis-data:/var/lib/zedis/data barddoo/zedis +``` + +### Custom config + +```bash +docker run -p 6379:6379 -v ./zedis.conf:/etc/zedis/zedis.conf barddoo/zedis +``` + ## Development ### Project Structure