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
13 changes: 7 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ name: CI
on:
push:
branches:
- "**"
- "main"
tags:
- "v*"
pull_request:
branches:
- "main"

permissions:
contents: write
Expand All @@ -29,11 +31,10 @@ jobs:
with:
version: 0.15.1

- name: Zig version
run: zig version

- name: Test
run: zig build test
- name: Zig test
run: |
zig version
zig build test

- name: Build release binary
run: zig build -Doptimize=ReleaseSafe
Expand Down
7 changes: 7 additions & 0 deletions docs/implement.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ This document describes how `codex-auth` stores accounts, synchronizes auth file
- `~/.codex/accounts/registry.json.bak.<timestamp>`
- `~/.codex/sessions/...`

`codex-auth` resolves `codex_home` in this order:

1. `CODEX_HOME` (when set and non-empty)
2. `HOME/.codex`
3. `USERPROFILE/.codex` (Windows fallback)
4. `HOMEDRIVE + HOMEPATH + "/.codex"` (Windows fallback)

## Testing Conventions (BDD Style on std.testing)

- The project keeps using Zig native tests (`zig build test`) for CI and local checks.
Expand Down
42 changes: 35 additions & 7 deletions src/registry.zig
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,43 @@ fn freeAccountRecord(allocator: std.mem.Allocator, rec: *const AccountRecord) vo
}
}

pub fn resolveCodexHome(allocator: std.mem.Allocator) ![]u8 {
if (std.process.getEnvVarOwned(allocator, "CODEX_HOME")) |val| {
if (val.len > 0) return val;
fn getNonEmptyEnvVarOwned(allocator: std.mem.Allocator, name: []const u8) !?[]u8 {
const val = std.process.getEnvVarOwned(allocator, name) catch |err| switch (err) {
error.EnvironmentVariableNotFound => return null,
else => return err,
};
if (val.len == 0) {
allocator.free(val);
} else |_| {}
return null;
}
return val;
}

pub fn resolveCodexHome(allocator: std.mem.Allocator) ![]u8 {
if (try getNonEmptyEnvVarOwned(allocator, "CODEX_HOME")) |val| return val;

if (try getNonEmptyEnvVarOwned(allocator, "HOME")) |home| {
defer allocator.free(home);
return try std.fs.path.join(allocator, &[_][]const u8{ home, ".codex" });
}

if (try getNonEmptyEnvVarOwned(allocator, "USERPROFILE")) |user_profile| {
defer allocator.free(user_profile);
return try std.fs.path.join(allocator, &[_][]const u8{ user_profile, ".codex" });
}

const home_drive = try getNonEmptyEnvVarOwned(allocator, "HOMEDRIVE");
defer if (home_drive) |v| allocator.free(v);
const home_path = try getNonEmptyEnvVarOwned(allocator, "HOMEPATH");
defer if (home_path) |v| allocator.free(v);

if (home_drive != null and home_path != null) {
const combined = try std.mem.concat(allocator, u8, &[_][]const u8{ home_drive.?, home_path.? });
defer allocator.free(combined);
return try std.fs.path.join(allocator, &[_][]const u8{ combined, ".codex" });
}

const home = try std.process.getEnvVarOwned(allocator, "HOME");
defer allocator.free(home);
return try std.fs.path.join(allocator, &[_][]const u8{ home, ".codex" });
return error.EnvironmentVariableNotFound;
}

pub fn ensureAccountsDir(allocator: std.mem.Allocator, codex_home: []const u8) !void {
Expand Down