Skip to content

Commit 8609120

Browse files
committed
fix: tighten codex cli sync detection
1 parent 97d387a commit 8609120

File tree

5 files changed

+114
-34
lines changed

5 files changed

+114
-34
lines changed

.github/workflows/codex-cli-watch.yml

Lines changed: 91 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,76 @@ jobs:
3232
set -euo pipefail
3333
3434
SUBMODULE_PATH="submodules/openai-codex"
35-
SURFACE_PATHS=(
36-
"codex-rs"
37-
"docs"
38-
"README.md"
39-
"CHANGELOG.md"
40-
"Cargo.toml"
41-
"Cargo.lock"
42-
"package.json"
35+
MODELS_FILE="codex-rs/core/models.json"
36+
CONFIG_SCHEMA_FILE="codex-rs/core/config.schema.json"
37+
FLAG_SOURCE_PATHS=(
38+
"codex-rs/cli/src/main.rs"
39+
"codex-rs/exec/src/cli.rs"
40+
"codex-rs/exec/src/main.rs"
4341
)
4442
43+
read_file_at_sha() {
44+
local sha="$1"
45+
local path="$2"
46+
git -C "$SUBMODULE_PATH" show "$sha:$path" 2>/dev/null || true
47+
}
48+
49+
extract_flag_snapshot() {
50+
local sha="$1"
51+
local path
52+
53+
for path in "${FLAG_SOURCE_PATHS[@]}"; do
54+
read_file_at_sha "$sha" "$path"
55+
done | grep -Eo -- '--[a-z0-9][a-z0-9-]*' | sort -u || true
56+
}
57+
58+
extract_model_snapshot() {
59+
local sha="$1"
60+
read_file_at_sha "$sha" "$MODELS_FILE" \
61+
| jq -r '.models[]? | (.id // .slug // empty)' \
62+
| sed '/^$/d' \
63+
| sort -u || true
64+
}
65+
66+
extract_feature_snapshot() {
67+
local sha="$1"
68+
read_file_at_sha "$sha" "$CONFIG_SCHEMA_FILE" \
69+
| jq -r '.properties.features.properties? | keys[]?' \
70+
| sed '/^$/d' \
71+
| sort -u || true
72+
}
73+
74+
format_snapshot_changes() {
75+
local before_file="$1"
76+
local after_file="$2"
77+
local empty_message="$3"
78+
79+
local added
80+
local removed
81+
local formatted=""
82+
83+
added="$(comm -13 "$before_file" "$after_file" | sed 's/^/- added `/' | sed 's/$/`/' || true)"
84+
removed="$(comm -23 "$before_file" "$after_file" | sed 's/^/- removed `/' | sed 's/$/`/' || true)"
85+
86+
if [ -n "$added" ]; then
87+
formatted="$added"
88+
fi
89+
90+
if [ -n "$removed" ]; then
91+
if [ -n "$formatted" ]; then
92+
formatted="$formatted"$'\n'"$removed"
93+
else
94+
formatted="$removed"
95+
fi
96+
fi
97+
98+
if [ -z "$formatted" ]; then
99+
formatted="$empty_message"
100+
fi
101+
102+
printf '%s\n' "$formatted"
103+
}
104+
45105
if [ ! -d "$SUBMODULE_PATH/.git" ] && [ ! -f "$SUBMODULE_PATH/.git" ]; then
46106
echo "Submodule not initialized: $SUBMODULE_PATH"
47107
echo "has_update=false" >> "$GITHUB_OUTPUT"
@@ -85,29 +145,30 @@ jobs:
85145
commits='- No commit summary available.'
86146
fi
87147
88-
surface_diff="$(git -C "$SUBMODULE_PATH" diff --unified=0 "$current_sha" "$latest_sha" -- "${SURFACE_PATHS[@]}" || true)"
148+
tmp_dir="$(mktemp -d)"
149+
trap 'rm -rf "$tmp_dir"' EXIT
89150
90-
changed_flags="$(printf '%s\n' "$surface_diff" | grep -Eo -- '--[a-z0-9][a-z0-9-]*' | sort -u | head -n 200 || true)"
91-
changed_models="$(printf '%s\n' "$surface_diff" | grep -Eo -- 'gpt-[0-9][a-z0-9.-]*' | sort -u | head -n 200 || true)"
92-
changed_features="$(printf '%s\n' "$surface_diff" | grep -Eo -- 'features\.[a-zA-Z0-9_.-]+' | sed 's/^features\.//' | sort -u | head -n 200 || true)"
151+
extract_flag_snapshot "$current_sha" > "$tmp_dir/flags-before.txt"
152+
extract_flag_snapshot "$latest_sha" > "$tmp_dir/flags-after.txt"
153+
extract_model_snapshot "$current_sha" > "$tmp_dir/models-before.txt"
154+
extract_model_snapshot "$latest_sha" > "$tmp_dir/models-after.txt"
155+
extract_feature_snapshot "$current_sha" > "$tmp_dir/features-before.txt"
156+
extract_feature_snapshot "$latest_sha" > "$tmp_dir/features-after.txt"
93157
94-
if [ -z "$changed_flags" ]; then
95-
changed_flags='- (no CLI flag tokens detected from diff)'
96-
else
97-
changed_flags="$(printf '%s\n' "$changed_flags" | sed 's/^/- `/' | sed 's/$/`/')"
98-
fi
158+
changed_flags="$(format_snapshot_changes \
159+
"$tmp_dir/flags-before.txt" \
160+
"$tmp_dir/flags-after.txt" \
161+
"- (no CLI flag surface change detected from CLI sources)")"
99162
100-
if [ -z "$changed_models" ]; then
101-
changed_models='- (no model tokens detected from diff)'
102-
else
103-
changed_models="$(printf '%s\n' "$changed_models" | sed 's/^/- `/' | sed 's/$/`/')"
104-
fi
163+
changed_models="$(format_snapshot_changes \
164+
"$tmp_dir/models-before.txt" \
165+
"$tmp_dir/models-after.txt" \
166+
"- (no model catalog change detected from models.json)")"
105167
106-
if [ -z "$changed_features" ]; then
107-
changed_features='- (no feature tokens detected from diff)'
108-
else
109-
changed_features="$(printf '%s\n' "$changed_features" | sed 's/^/- `/' | sed 's/$/`/')"
110-
fi
168+
changed_features="$(format_snapshot_changes \
169+
"$tmp_dir/features-before.txt" \
170+
"$tmp_dir/features-after.txt" \
171+
"- (no feature flag surface change detected from config schema)")"
111172
112173
{
113174
echo "cli_changed_files<<EOF"
@@ -214,13 +275,13 @@ jobs:
214275
'## Changed files (CLI-relevant)',
215276
changedFiles || '- (No file list available)',
216277
'',
217-
'## Potential CLI flag changes from diff',
278+
'## Detected CLI flag surface changes',
218279
changedFlags,
219280
'',
220-
'## Potential model changes from diff',
281+
'## Detected model catalog changes',
221282
changedModels,
222283
'',
223-
'## Potential feature changes from diff',
284+
'## Detected feature flag changes',
224285
changedFeatures,
225286
'',
226287
'## Commits',

CodexSharpSDK.Tests/Integration/CodexCliSmokeTests.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ public class CodexCliSmokeTests
3030
private const string StatusCommand = "status";
3131
private const string HelpFlag = "--help";
3232
private const string VersionToken = "codex-cli";
33+
private const string RootHelpToken = "Codex CLI";
34+
private const string RootHelpCommandToken = "exec";
3335
private const string ExecHelpToken = "Run Codex non-interactively";
3436
private const string NotLoggedInToken = "Not logged in";
3537
private const string NotAuthenticatedToken = "Not authenticated";
@@ -54,6 +56,19 @@ public async Task CodexCli_Smoke_VersionCommand_ReturnsCodexCliVersion()
5456
await Assert.That(output.Contains(VersionToken, StringComparison.OrdinalIgnoreCase)).IsTrue();
5557
}
5658

59+
[Test]
60+
public async Task CodexCli_Smoke_HelpCommand_ReturnsRootCommands()
61+
{
62+
var executablePath = ResolveExecutablePath();
63+
64+
var result = await RunCodexAsync(executablePath, null, HelpFlag);
65+
await Assert.That(result.ExitCode).IsEqualTo(0);
66+
67+
var output = string.Concat(result.StandardOutput, result.StandardError);
68+
await Assert.That(output.Contains(RootHelpToken, StringComparison.Ordinal)).IsTrue();
69+
await Assert.That(output.Contains(RootHelpCommandToken, StringComparison.Ordinal)).IsTrue();
70+
}
71+
5772
[Test]
5873
public async Task CodexCli_Smoke_ExecHelpCommand_ReturnsSuccess()
5974
{

Directory.Build.props

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
</PropertyGroup>
1818

1919
<!-- NuGet package metadata and versioning -->
20-
<PropertyGroup Condition="'$(IsPackable)' == 'true'">
20+
<PropertyGroup>
2121
<Authors>ManagedCode</Authors>
2222
<Company>ManagedCode</Company>
2323
<RepositoryType>GitHub</RepositoryType>
@@ -28,11 +28,11 @@
2828
<PublishRepositoryUrl>true</PublishRepositoryUrl>
2929
<EmbedUntrackedSources>true</EmbedUntrackedSources>
3030
<EnablePackageValidation>true</EnablePackageValidation>
31-
<Version>0.1.2</Version>
31+
<Version>0.1.3</Version>
3232
<PackageVersion>$(Version)</PackageVersion>
3333
</PropertyGroup>
3434

35-
<ItemGroup Condition="'$(IsPackable)' == 'true'">
35+
<ItemGroup>
3636
<None Include="$(MSBuildThisFileDirectory)README.md" Pack="true" PackagePath="/" Visible="false" />
3737
</ItemGroup>
3838

docs/Features/release-and-sync-automation.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,16 @@ Keep package quality and upstream Codex CLI parity automatically verified throug
3535
- CI must run build and tests on every push/PR.
3636
- CI and Release workflows must execute full solution tests before smoke subsets, excluding auth-required tests with `-- --treenode-filter "/*/*/*/*[RequiresCodexAuth!=true]"`.
3737
- Codex CLI smoke test workflow steps must run `CodexCli_Smoke_*` via `CodexSharpSDK.Tests` project scope to avoid false `zero tests ran` failures in non-smoke test assemblies.
38+
- Codex CLI smoke validation must cover both `codex --help` and `codex exec --help`, proving root and non-interactive help surfaces stay discoverable.
3839
- Release workflow must build/test before pack/publish.
3940
- Release workflow must read package version from `Directory.Build.props`.
4041
- Release workflow must validate semantic version format before packaging.
42+
- Release workflow must fail if the produced `.nupkg` version does not match `Directory.Build.props`.
4143
- Release workflow must use generated GitHub release notes.
4244
- Release workflow must create/push git tag `v<version>` before publishing GitHub release.
4345
- Codex CLI watch runs daily and opens issue when upstream `openai/codex` changed since pinned submodule SHA.
4446
- Completing a Codex CLI sync issue must update the pinned `submodules/openai-codex` commit after validation.
45-
- Sync issue body must include detected candidate changes for CLI flags/models/features and actionable checklist.
47+
- Sync issue body must derive flag changes from CLI source snapshots, model changes from `codex-rs/core/models.json`, and feature changes from `codex-rs/core/config.schema.json` so alerts stay actionable.
4648
- Sync issue must assign Copilot by default.
4749
- Duplicate sync issue for same upstream SHA is not allowed.
4850

@@ -71,6 +73,7 @@ flowchart LR
7173
- `codex features list`
7274
- `dotnet build ManagedCode.CodexSharpSDK.slnx -c Release -warnaserror`
7375
- `dotnet test --solution ManagedCode.CodexSharpSDK.slnx -c Release`
76+
- `dotnet pack CodexSharpSDK/CodexSharpSDK.csproj -c Release --no-build -o artifacts`
7477

7578
### Workflow mapping
7679

docs/Testing/strategy.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Verify `ManagedCode.CodexSharpSDK` behavior against real Codex CLI contracts, wi
1515
- Use the real installed `codex` CLI for process interaction tests; do not use `FakeCodexProcessRunner` doubles.
1616
- Treat `codex` as a prerequisite for real integration runs and install it in CI/local setup before running those tests.
1717
- CI validates Codex CLI smoke behavior on Linux/macOS/Windows without requiring login: CLI must be discoverable and invokable.
18+
- Smoke coverage validates both `codex --help` and `codex exec --help` before unauthenticated login checks.
1819
- Cross-platform CI smoke also validates unauthenticated behavior in an isolated profile (`codex login status` must report `Not logged in`), proving binary discovery + process launch without relying on local credentials.
1920
- Real integration runs must use existing Codex CLI login/session; test harness does not use API key environment variables.
2021
- Real integration model selection must be explicit: set `CODEX_TEST_MODEL` or define `model` in `~/.codex/config.toml` (no hardcoded fallback model).

0 commit comments

Comments
 (0)