diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fc7c627..176e517 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,10 +3,12 @@ name: CI on: push: branches: - - "**" + - "main" tags: - "v*" pull_request: + branches: + - "main" permissions: contents: write @@ -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 diff --git a/docs/implement.md b/docs/implement.md index f2e8c2b..0445e59 100644 --- a/docs/implement.md +++ b/docs/implement.md @@ -11,6 +11,13 @@ This document describes how `codex-auth` stores accounts, synchronizes auth file - `~/.codex/accounts/registry.json.bak.` - `~/.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. diff --git a/src/registry.zig b/src/registry.zig index 7e57169..c56959d 100644 --- a/src/registry.zig +++ b/src/registry.zig @@ -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 {