Skip to content
Closed
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
55 changes: 55 additions & 0 deletions .github/workflows/zig-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: zig-tests

on:
pull_request:
push:

permissions:
contents: read

env:
ZIG_VERSION: 0.15.2

jobs:
test:
name: test-${{ matrix.name }}
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- name: ubuntu
runner: ubuntu-24.04
zig_archive: zig-x86_64-linux-0.15.2.tar.xz
zig_dir: zig-x86_64-linux-0.15.2
- name: macos
runner: macos-14
zig_archive: zig-aarch64-macos-0.15.2.tar.xz
zig_dir: zig-aarch64-macos-0.15.2
- name: windows
runner: windows-2022
zig_archive: zig-x86_64-windows-0.15.2.zip
zig_dir: zig-x86_64-windows-0.15.2

steps:
- uses: actions/checkout@v4

- name: Install Zig
if: runner.os != 'Windows'
shell: bash
run: |
set -euo pipefail
curl -L "https://ziglang.org/download/${ZIG_VERSION}/${{ matrix.zig_archive }}" -o zig.tar.xz
tar -xf zig.tar.xz
Comment on lines +42 to +43
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Verify Zig download before executing in CI

In .github/workflows/zig-tests.yml (checked the Install Zig steps for both non-Windows and Windows), the workflow downloads Zig archives directly from the network and immediately extracts/runs them without any checksum or signature verification. This creates a supply-chain risk: if the download endpoint or transit path is compromised, attacker-controlled binaries can execute in CI (including push runs where secrets may be present). Add an integrity check (e.g., pinned SHA-256 per archive/version, or verified signature) before extraction in both install branches.

Useful? React with 👍 / 👎.

echo "$PWD/${{ matrix.zig_dir }}" >> "$GITHUB_PATH"

- name: Install Zig
if: runner.os == 'Windows'
shell: pwsh
run: |
Invoke-WebRequest -Uri "https://ziglang.org/download/$env:ZIG_VERSION/${{ matrix.zig_archive }}" -OutFile zig.zip
Expand-Archive -Path zig.zip -DestinationPath .
Add-Content -Path $env:GITHUB_PATH -Value (Join-Path $PWD '${{ matrix.zig_dir }}')

- name: Run tests
run: zig build test
10 changes: 7 additions & 3 deletions src/index.zig
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const builtin = @import("builtin");
const std = @import("std");
const compat = @import("compat.zig");

Expand Down Expand Up @@ -968,6 +969,8 @@ pub const MmapTrigramIndex = struct {
}

fn initFromDiskInner(dir_path: []const u8, allocator: std.mem.Allocator) !?MmapTrigramIndex {
if (comptime builtin.os.tag == .windows) return null;

const postings_path = try std.fmt.allocPrint(allocator, "{s}/trigram.postings", .{dir_path});
defer allocator.free(postings_path);
const lookup_path = try std.fmt.allocPrint(allocator, "{s}/trigram.lookup", .{dir_path});
Expand Down Expand Up @@ -1082,8 +1085,10 @@ pub const MmapTrigramIndex = struct {
for (self.file_table) |p| self.allocator.free(p);
self.allocator.free(self.file_table);
self.file_set.deinit();
std.posix.munmap(self.postings_data);
std.posix.munmap(self.lookup_data);
if (comptime builtin.os.tag != .windows) {
std.posix.munmap(self.postings_data);
std.posix.munmap(self.lookup_data);
}
}

pub fn fileCount(self: *const MmapTrigramIndex) u32 {
Expand Down Expand Up @@ -2188,4 +2193,3 @@ pub const SparseNgramIndex = struct {
return @intCast(self.file_ngrams.count());
}
};

28 changes: 24 additions & 4 deletions src/tests.zig
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const builtin = @import("builtin");
const std = @import("std");
const testing = std.testing;

Expand Down Expand Up @@ -1049,7 +1050,9 @@ test "explorer: removeFile frees owned map key" {
try testing.expect(explorer.dep_graph.count() == 0);
}
test "watcher: queue overflow is explicit" {
var queue = watcher.EventQueue{};
const queue = try testing.allocator.create(watcher.EventQueue);
defer testing.allocator.destroy(queue);
queue.* = .{};

var pushed: usize = 0;
while (true) : (pushed += 1) {
Expand All @@ -1068,7 +1071,9 @@ test "watcher: queue overflow is explicit" {
}

test "watcher: queue event copies path bytes" {
var queue = watcher.EventQueue{};
const queue = try testing.allocator.create(watcher.EventQueue);
defer testing.allocator.destroy(queue);
queue.* = .{};
const original = try testing.allocator.dupe(u8, "tmp/deleted.zig");
try testing.expect(queue.push(watcher.FsEvent.init(original, .deleted, 99) orelse unreachable));
testing.allocator.free(original);
Expand Down Expand Up @@ -1452,7 +1457,9 @@ test "regression: searchContent frees empty trigram candidate slice" {
}

test "regression: queue push stays non-blocking when full" {
var queue = watcher.EventQueue{};
const queue = try testing.allocator.create(watcher.EventQueue);
defer testing.allocator.destroy(queue);
queue.* = .{};

var pushed: usize = 0;
while (true) : (pushed += 1) {
Expand Down Expand Up @@ -4530,8 +4537,9 @@ test "issue-151: Go block comments skipped" {
try testing.expect(func_count == 1); // only realFunc
}


test "issue-150: --help prints usage" {
if (comptime builtin.os.tag == .windows) return error.SkipZigTest;

const result = try std.process.Child.run(.{
.allocator = testing.allocator,
.argv = &.{ "zig", "build", "run", "--", "--help" },
Expand All @@ -4545,6 +4553,8 @@ test "issue-150: --help prints usage" {
}

test "issue-150: -h prints usage" {
if (comptime builtin.os.tag == .windows) return error.SkipZigTest;

const result = try std.process.Child.run(.{
.allocator = testing.allocator,
.argv = &.{ "zig", "build", "run", "--", "-h" },
Expand Down Expand Up @@ -4577,6 +4587,8 @@ test "issue-148: idle timeout is 10 minutes" {
}

test "issue-148: POLLHUP detects closed pipe" {
if (comptime builtin.os.tag == .windows) return error.SkipZigTest;

// Verify the polling infrastructure works for pipe-based transports
const pipe = try std.posix.pipe();
defer std.posix.close(pipe[0]);
Expand Down Expand Up @@ -4670,6 +4682,8 @@ const MmapTrigramIndex = @import("index.zig").MmapTrigramIndex;
const AnyTrigramIndex = @import("index.zig").AnyTrigramIndex;

test "issue-164: mmap trigram index returns same candidates as heap index" {
if (comptime builtin.os.tag == .windows) return error.SkipZigTest;

var arena = std.heap.ArenaAllocator.init(testing.allocator);
defer arena.deinit();
const allocator = arena.allocator();
Expand Down Expand Up @@ -4710,6 +4724,8 @@ test "issue-164: mmap trigram index returns same candidates as heap index" {
}

test "issue-164: mmap binary search on sorted lookup table" {
if (comptime builtin.os.tag == .windows) return error.SkipZigTest;

var arena = std.heap.ArenaAllocator.init(testing.allocator);
defer arena.deinit();
const allocator = arena.allocator();
Expand Down Expand Up @@ -4745,11 +4761,15 @@ test "issue-164: mmap binary search on sorted lookup table" {
}

test "issue-164: mmap handles missing files gracefully" {
if (comptime builtin.os.tag == .windows) return error.SkipZigTest;

const result = MmapTrigramIndex.initFromDisk("/tmp/nonexistent-codedb-test-dir-164", testing.allocator);
try testing.expect(result == null);
}

test "issue-164: AnyTrigramIndex dispatches to mmap variant" {
if (comptime builtin.os.tag == .windows) return error.SkipZigTest;

var arena = std.heap.ArenaAllocator.init(testing.allocator);
defer arena.deinit();
const allocator = arena.allocator();
Expand Down
Loading