Skip to content

Report deployed git version in x-ts-version - #1213

Open
dhruv8sh wants to merge 6 commits into
mainfrom
feat/git-version-header
Open

dhruv8sh wants to merge 6 commits into
mainfrom
feat/git-version-header

Conversation

@dhruv8sh

Copy link
Copy Markdown
Collaborator

Summary

  • x-ts-version now reports the deployed git version: the tag, else the branch, else exactly the first 6 characters of the commit. It is compiled in from the build-time TRUSTED_SERVER_GIT_VERSION (set by the deploy pipeline), falling back to local git, and is omitted when neither is available.
  • The Fastly service version it used to carry moves to x-ts-fastly-version. Under Viceroy, main today answers with x-ts-version: 0, which is the Fastly version, not a Trusted Server one. Breaking for anything that reads x-ts-version as the Fastly version number.
  • Every adapter sends x-ts-version, including the Fastly GET /health fast path that deploy health checks probe. Operator response_headers still apply last.

Changes

File Change
crates/trusted-server-core/build.rs Resolve the version (override, then local git) and emit TS_GIT_VERSION; rerun-if-env-changed=TRUSTED_SERVER_GIT_VERSION; watch the worktree HEAD, the current branch's ref, refs/tags and packed-refs (existing paths only; other branches and refs/remotes skipped)
crates/trusted-server-core/build_support/git_version.rs Pure resolver shared with the test via #[path]; visible-ASCII rule
crates/trusted-server-core/tests/git_version_resolve.rs Resolver tests: precedence, 6-char rule, blanks, non-ASCII and inner-space overrides
crates/trusted-server-core/src/constants.rs HEADER_X_TS_FASTLY_VERSION, TS_GIT_VERSION
crates/trusted-server-core/src/version_header.rs git_version_header_value, apply_git_version_header (+ testable variants)
crates/trusted-server-core/src/lib.rs Register version_header
crates/trusted-server-adapter-fastly/src/middleware.rs Git version to x-ts-version, FASTLY_SERVICE_VERSION to x-ts-fastly-version; doc comments
crates/trusted-server-adapter-fastly/src/main.rs /health fast path sets x-ts-version
crates/trusted-server-adapter-{axum,cloudflare,spin}/src/middleware.rs Call apply_git_version_header before operator headers
crates/trusted-server-adapter-axum/tests/routes.rs, crates/trusted-server-adapter-spin/src/app.rs /health route tests assert the header
docs/guide/first-party-proxy.md Example no longer overrides X-TS-Version
CHANGELOG.md Breaking-change entry
docs/superpowers/{specs,plans}/2026-09-25-git-version-header*.md Design and plan

Closes

Closes #1212

Related: #325 (introduced x-ts-version as the Fastly version)

Test plan

  • cargo test-fastly && cargo test-axum && cargo test-cloudflare && cargo test-spin
  • All eight clippy aliases (clippy-fastly, -axum, -cloudflare, -cloudflare-wasm, -spin-native, -spin-wasm, -cli, -codegen)
  • cargo fmt --all -- --check
  • Parity: cargo test --manifest-path crates/trusted-server-integration-tests/Cargo.toml --test parity
  • JS tests: cd crates/trusted-server-js/lib && npx vitest run (1130 passed; no JS changes. Run with NODE_OPTIONS=--no-webstorage locally, because Node 26's built-in localStorage shadows jsdom's. The repo pins Node 24.12.0, where this doesn't apply.)
  • JS format: cd crates/trusted-server-js/lib && npm run format
  • Docs format: cd docs && npm run format
  • WASM build: cargo build --package trusted-server-adapter-fastly --release --target wasm32-wasip1
  • Manual testing via fastly compute serve: offline end-to-end under Viceroy (below)

End-to-end under Viceroy. Release Wasm served with fastly compute serve --file <wasm> and a local config pushed with ts config push --adapter fastly --local, set up the same way as scripts/smoke-fastly.sh. Each case curls GET /health and GET / and asserts 200. x-geo-info-available on / proves the response went through apply_finalize_headers. Viceroy reports FASTLY_SERVICE_VERSION=0.

Case Build /health x-ts-version / x-ts-version / x-ts-fastly-version
Override TRUSTED_SERVER_GIT_VERSION=v9.9.9-test v9.9.9-test v9.9.9-test 0
Warm-cache rebuild …=v9.9.9-test2, no clean; Cargo: "the env variable TRUSTED_SERVER_GIT_VERSION changed" v9.9.9-test2 v9.9.9-test2 0
No env, on a branch unset feat/git-version-header same 0
No env, detached unset; rebuild triggered by the worktree HEAD 6-char hash same 0
No env, tagged HEAD unset; local tag the tag same 0
Non-git tree copy without .git, unset absent absent 0

It also checks the rebuild triggers. A new tag alone triggers a rebuild through refs/tags. Moving the current branch off a tagged commit triggers one through the branch ref and switches the value from the tag to the branch. A simulated fetch (git update-ref refs/remotes/...) and a ref update on another branch both leave core Fresh. An override of v1-été prints a cargo:warning and falls back to local git.

Checklist

  • Changes follow AGENTS.md conventions
  • No unwrap() in production code — use expect("should ...")
  • Uses tracing macros (not println!) — this repo uses log per AGENTS.md; the only println! calls are the cargo: directives in build.rs
  • New code has tests
  • No secrets or credentials committed

Signed-off-by: dhruv8sh <dhruv8sh@proton.me>
Signed-off-by: dhruv8sh <dhruv8sh@proton.me>
…rsion on Fastly

Signed-off-by: dhruv8sh <dhruv8sh@proton.me>
Signed-off-by: dhruv8sh <dhruv8sh@proton.me>
Signed-off-by: dhruv8sh <dhruv8sh@proton.me>
@dhruv8sh dhruv8sh self-assigned this Sep 25, 2026
@dhruv8sh
dhruv8sh requested review from ChristianPavilonis, aram356 and prk-Jr and removed request for ChristianPavilonis September 25, 2026 11:49
@aram356 aram356 added this to the 202610 milestone Sep 28, 2026

@ChristianPavilonis ChristianPavilonis left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Review summary

Reviewed cd704a7aacb4df866c471cfafe3cf6d130ad5fcb against a4e01eb55fe940bd02b2426dccd46050704c54c5. Requesting changes for the two P2 correctness issues documented inline. Both were reproduced in temporary fixtures without editing repository files.

Validation

The 10 resolver tests, 7 core header tests, and focused Fastly, Axum, Cloudflare, and Spin tests passed. Consecutive override builds emitted the requested versions without cleaning the target cache; removing the override restored the local branch value. All 20 reported CI checks pass.

The second pass exercised the shipped header helper with repository-locked dependencies and the unchanged build script in a packed-ref Cargo fixture. No duplicate review feedback was present. Full Viceroy HTTP smoke testing and live Cloudflare/Spin deployment were not independently repeated.

/// Sets `x-ts-version` to `version`, or leaves it unset when `version` is
/// unknown or invalid.
pub fn apply_git_version_header_from(version: Option<&str>, response: &mut Response) {
if let Some(value) = header_value_from(version) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

P2: Remove an upstream version when the compiled version is unknown

When a build has neither git metadata nor TRUSTED_SERVER_GIT_VERSION, this branch does nothing rather than ensuring the header is absent. Publisher pass-through preserves origin response headers, so an upstream x-ts-version survives and is presented as this deployment's version. This contradicts the documented omission behavior and can mislead external deployment checks.

Reproduced with the shipped helper and repository-locked dependencies: starting with x-ts-version: 42, both None and an invalid version left 42 intact; a valid v2.0.0 correctly replaced it. The existing omission test starts with an empty response and misses this case.

Remove the existing header before conditionally inserting the compiled value. Operator response headers still run afterward and can intentionally override it. Add regression coverage starting with a pre-existing header.

Suggested change
if let Some(value) = header_value_from(version) {
response.headers_mut().remove(HEADER_X_TS_VERSION);
if let Some(value) = header_value_from(version) {

if let Some(common_dir) = git(&["rev-parse", "--path-format=absolute", "--git-common-dir"]) {
let common_dir = Path::new(&common_dir);
if let Some(branch_ref) = git(&["symbolic-ref", "-q", "HEAD"]) {
rerun_if_exists(&common_dir.join(branch_ref));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

P2: Detect creation of the current branch's loose ref

If the current branch exists only in packed-refs, rerun_if_exists omits its loose ref from Cargo's watch list. Advancing that branch creates the loose ref without changing HEAD, packed-refs, or refs/tags, so a warm build retains the previous compiled version.

Reproduced in a temporary Cargo fixture using this unchanged build script: tag the current commit v1.2.3, run git pack-refs --all --prune, and build. Then change application code, commit, and rebuild without an override. The new executable printed changed code: Some("v1.2.3"); all watched git mtimes were unchanged and the build script did not rerun. Executing the resolver afresh returned main.

The design explicitly acknowledges this limitation, but it means newer code can identify itself as an earlier release, undermining the purpose of the header. When the loose ref is absent, watch its nearest existing parent directory, or accept watching the missing current-ref path. Add a warm-cache regression covering a tagged, packed branch advancing to a new commit.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Report the deployed git version in x-ts-version

3 participants