Export the agent e2e suite as a release bundle for downstream repos - #166
Merged
Conversation
Packages Conductor.AI.E2eTests/ into a self-contained, version-stamped tarball — conductor-ai-e2e-csharp-<version>.tar.gz — attached to every GitHub release, so downstream repos (e.g. orkes-io/orkes-conductor) can pin the e2e suite to the exact csharp-sdk release they run against. Mirrors conductor-oss/javascript-sdk#134. - scripts/package-e2e-bundle.sh stages the 27 e2e sources verbatim and generates a standalone csproj that swaps the ProjectReference for PackageReference conductor-ai@<version>, so a run exercises the published package. Two .NET-specific wrinkles: Settings (LlmModel / ServerUrl) is vendored, since in-repo it arrives via <Compile Include> from the examples project; and AssemblyName stays Conductor.AI.E2eTests because Conductor.AI grants it InternalsVisibleTo. - Bundles ship check-results.py + run.sh: every test is a [SkippableFact] that skips on an unreachable server, so an all-skipped run would otherwise read as green. Same guard the in-repo agent-e2e workflow applies to its TRX output. - scripts/test-package-e2e-bundle.sh statically validates the bundle (source parity, package pin, no ProjectReference/<Compile> escapes, assembly name, no leftover placeholders). --build additionally packs the local SDK into a temp feed and compiles against it. - release-agent-e2e-bundle.yml packages, validates, writes sha256 sidecars and uploads on release: [published], plus workflow_dispatch to attach bundles to existing releases. Tags here are bare versions. Conductor/conductor-csharp.csproj pinned its README at the Dockerfile-only path /package/Conductor/README.md, so `dotnet pack` failed anywhere else (the validator's --build mode needs it). ../README.md resolves to the same file inside the Docker build and matches Conductor.AI.csproj. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ling-senpeng13
force-pushed
the
e2e/release-bundle
branch
from
July 29, 2026 17:33
0d43b90 to
8a6fea9
Compare
Codecov Report✅ All modified and coverable lines are covered by tests.
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
Eight review findings, roughly "the validator could not catch the bugs it existed for, and the guard failed open". 1. The release workflow validated a stand-in, not the shipped artifact. It packaged at the real version, then the validator re-packaged at 9.9.9-test in a temp dir and checked THAT, so every version-dependent assertion (@Version@ sweep, conductor-ai pin) never saw the uploaded tarball. The validator now takes --tarball/--version and the workflow points it at what it just built. Version shape is also validated in package-e2e-bundle.sh, since $VERSION is an unescaped sed replacement — a '/' or '&' silently corrupted every file. 2. Script injection: `TAG="${{ inputs.tag }}"` inside `run:` is substituted before the shell parses it. Tags now arrive via env, and are shape-checked against ^v?N.N.N(-suffix)?$ before use. 3. The vacuous-run guard failed open. No python3 -> warn and continue, and since `dotnet test` exits 0 on an all-skipped run, a dead server reported a pass — exactly what the guard exists to prevent. run.sh now refuses to start without python3, checked up front rather than after a 20-minute run. python3 is listed in the bundle README prerequisites. 4. The guard was duplicated (inline heredoc in agent-e2e.yml, generated copy in the bundle) and keyed to a magic string from E2eFixture with nothing tying them together — reword the skip message and both lanes silently stop detecting dead servers. Extracted to scripts/check-results.py, copied verbatim into the bundle and invoked by agent-e2e.yml. The validator asserts the two copies are identical, that UNREACHABLE_MARKER still appears in E2eFixture.cs, and that agent-e2e.yml has not re-inlined its own copy. 5. No drift detection on the generated csproj. It writes the TFM and four test package versions longhand, so an in-repo bump left the bundle on stale xunit/Test.Sdk. The validator now diffs both csprojs (TFM + shared pins). 6. Packager and validator shared one blind spot: both used `find -maxdepth 1 -name '*.cs'`, so the parity count agreed whether or not a subdirectory was dropped. The packager copies recursively (excluding obj/ bin/) and the validator compares full relative path sets, requiring the only extra to be the vendored Shared/Settings.cs. 7. --build, the only check that proves the bundle compiles, ran nowhere. Added an e2e_bundle job to CI Build: it needs no published package, so a bundle that cannot compile now fails the PR instead of shipping on a release. 8. Bundle validity depended on an uncoordinated publish. Both workflows fire on `release: published` independently, so a failed NuGet publish still attached a bundle pinning a nonexistent version. Packaging still does not wait (it is static), but upload now blocks on conductor-ai appearing in the nuget.org flat container, up to 20 minutes. Incidental: the validator errored on unknown args only when they were $1, so a typo silently ran static-only and printed ALL CHECKS PASSED. Proper arg loop now. Verified: full validator green in both modes (self-packaged and --tarball, with and without --build; compiles 0 warnings / 0 errors). Negative-tested that the new checks actually bite — hostile version rejected (exit 1), a tarball with one source removed fails parity, and an in-repo xunit bump fails the drift check with an actionable message. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
kowser-orkes
approved these changes
Jul 29, 2026
kowser-orkes
requested changes
Jul 29, 2026
kowser-orkes
left a comment
Contributor
There was a problem hiding this comment.
Javascript sdk should use 3.0.0-rc4
kowser-orkes
approved these changes
Jul 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Packages the agent e2e suite (
Conductor.AI.E2eTests/) into a self-contained, version-stamped tarball —conductor-ai-e2e-csharp-<version>.tar.gz— attached to every GitHub release, so downstream repos (e.g. orkes-io/orkes-conductor) can pin the e2e suite to the exact csharp-sdk release they run against. Mirrors conductor-oss/javascript-sdk#134.scripts/package-e2e-bundle.sh— stages the 27 e2e sources verbatim + a standalone csproj that swaps theProjectReferenceforPackageReference conductor-ai@<version>, so a run exercises the published package. Two .NET-specific wrinkles the JS bundle didn't have:Settings(LlmModel/ServerUrl) is vendored into the bundle, since in-repo it arrives via<Compile Include>from the examples project; andAssemblyNamemust stayConductor.AI.E2eTestsbecauseConductor.AIgrants itInternalsVisibleToand a few suites read internal types.check-results.py+run.sh. Every test is a[SkippableFact]that skips when the server is unreachable, so a dead server yields an all-skipped run that reads as green —run.shfails on 0 executed or server-unreachable skips, the same guardagent-e2e.ymlapplies in-repo.scripts/test-package-e2e-bundle.sh— static validator (source parity, package pin, noProjectReference/<Compile>escapes, assembly name preserved, no leftover@VERSION@; no network).--buildadditionally packs the local SDK into a temp feed and compiles the bundle against it — the compile-time proof it needs nothing but the package..github/workflows/release-agent-e2e-bundle.yml— packages, validates, generates sha256 sidecars, uploads to the release; same release event asrelease.yml, plusworkflow_dispatchto attach bundles to existing releases. Release tags in this repo are bare versions (3.0.0-rc2), so the tag is used verbatim andvis stripped only for the NuGet version.Changes outside the bundle-export scope
Flagging these explicitly, since they touch existing files rather than adding new ones. All three are consequences of review findings rather than the original goal — happy to split any of them out if reviewers would rather see them separately.
Conductor/conductor-csharp.csproj/package/Conductor/README.md→../README.mddotnet packfailed anywhere else — which the validator's--buildmode needs. See below..github/workflows/agent-e2e.ymlpython3 scripts/check-results.pyE2eFixture.cs, with nothing tying the copies together. Deduplicating it necessarily edits this workflow. No behavior change — same logic, same marker..github/workflows/pull_request.ymle2e_bundlejob--buildis the only check that proves the bundle compiles, and it ran nowhere. It needs no published package, so a bundle that can't compile should fail the PR rather than ship attached to a release. Note this adds a job to every PR.On the csproj fix
Conductor/conductor-csharp.csprojpinned its README at/package/Conductor/README.md— a Dockerfile-only path, sodotnet packfailed anywhere else. Changed to../README.md, which resolves to the same file inside the Docker build (Dockerfile copies/README.mdto both/package/Conductor/README.mdand/package/README.md; workdir is/package/Conductor) and matches whatConductor.AI.csprojalready does.release.ymlpacks viadocker build --target=publish_release, so the release path is unaffected.Review hardening (second commit)
A review of the first commit turned up eight issues, mostly variants of the validator couldn't catch the bugs it existed for, and the guard failed open:
9.9.9-testin a temp dir and checked that — so every version-dependent assertion never saw the uploaded tarball. Validator now takes--tarball/--version; the workflow points it at what it just built.$VERSIONis also shape-checked in the packager, since it's an unescapedsedreplacement (a/or&silently corrupted every stamped file).TAG="${{ inputs.tag }}"insiderun:is substituted before the shell parses the line. Tags now arrive viaenv:and are shape-checked before use.dotnet testexits 0 on an all-skipped run, a dead server reported a pass — exactly what the guard prevents.run.shnow refuses to start without python3, checked up front rather than after a 20-minute run. python3 added to the bundle README prerequisites.scripts/check-results.py, copied verbatim into the bundle and invoked byagent-e2e.yml. The validator asserts both copies are identical, thatUNREACHABLE_MARKERstill appears inE2eFixture.cs, and thatagent-e2e.ymlhasn't re-inlined its own.find -maxdepth 1 -name '*.cs', so the parity count agreed whether or not a subdirectory was dropped. Packager copies recursively (excludingobj/,bin/); validator compares full relative path sets, requiring the only extra to be the vendoredShared/Settings.cs.--buildran nowhere. Added thee2e_bundleCI job (see the scope table above).release: publishedindependently, so a failed NuGet publish still attached a bundle pinning a nonexistent version. Packaging still doesn't wait (it's static), but upload now blocks onconductor-aiappearing in the nuget.org flat container, up to 20 minutes. This delays when the asset shows up on a release, and fails the job outright if the publish fails.Also: the validator errored on unknown args only when they were
$1, so a typo silently ran static-only and printedALL CHECKS PASSED. Proper arg loop now.Validation
--tarball, each with and without--build; compiles 0 warnings / 0 errors against a locally packedconductor-ai 9.9.9-testwith no project references, confirming internals access survives the package boundarydotnet test --list-testson the extracted bundle discovers 157 tests — identical to the in-repo project1.0/x&y→ rejected (exit 1); tarball with one source removed →FAIL: bundle is missing e2e sources; in-repo xunit bumped →test package drift ... xunit: repo '2.9.9' vs bundle '2.9.3'docker build --target=pack_releasesucceeds with the relative README path, and the resultingconductor-csharpnupkg containsREADME.mdNot run here: the suite end-to-end, which needs a live server + mcp-testkit + real LLM calls — that's
agent-e2e.ymlon this PR, over the same sources.Follow-up
The first release cut after this merges is the first one to carry a bundle; the consuming lane in orkes-io/orkes-conductor pins that version.
Known and deliberately left alone: the tarball isn't reproducible (no
--sort/--mtime/--owner), andgh release upload --clobberreplaces a bundle and its sha256 sidecar together, so downstream verification can't detect a pinned bundle changing underneath it.