Skip to content

feat(cli): add respect-per-spec-base-path setting for per-spec x-fern-base-path - #17495

Merged
adidavid014 merged 6 commits into
mainfrom
devin/1787255075-per-spec-base-path
Aug 25, 2026
Merged

feat(cli): add respect-per-spec-base-path setting for per-spec x-fern-base-path#17495
adidavid014 merged 6 commits into
mainfrom
devin/1787255075-per-spec-base-path

Conversation

@adidavid014

@adidavid014 adidavid014 commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Description

Linear ticket: Refs

An OpenAPI document's plain x-fern-base-path is stored as a single API-wide value on the IR. With multiple specs, merge() collapsed them (ir1.basePath ?? ir2.basePath), so one document's base path was silently dropped and the survivor was inlined into every endpoint path — including endpoints from other specs/namespaces, since the IR no longer records which document an endpoint came from.

Reported by a customer with two specs: an OAuth spec (x-fern-base-path: /, path /oauth/token) and their main API (x-fern-base-path: /api/v3). / won, /api/v3 never reached the generated SDK, and no base_url worked for both: tenant root → API calls lose /api/v3 (308), …/api/v3 → the token call becomes /api/v3/oauth/token (401). They were patching _build_url in the generated client to work around it.

This adds an opt-in OpenAPI setting, respect-per-spec-base-path (default false), that applies each document's plain base path to that document's own endpoints during parsing, before the merge. With it enabled the customer case resolves to oauth/token and api/v3/aa-sequence/items under a tenant-root base_url. Default-off, so no existing output changes — anyone who compensated by putting the dropped prefix in base_url opts in and moves base_url back to the host root.

Changes Made

  • openapi-ir-parser/src/openapi/v3/generateIr.ts: when the setting is on, prefix each converted endpoint path with the document's plain (non-parameterized) x-fern-base-path and omit it from the document IR root; / stays a no-op and already-prefixed paths are not double-prefixed. Parameterized base paths (e.g. /{tenant}/v1) still live at the root so their path params stay on the client. Webhooks are unchanged — WebhookWithExample has no path field.
  • openapi-ir-parser/src/parse.ts: base-path merging moved into mergeBasePath(), which reports a conflict (context.failWithoutThrowing) when two documents declare differing parameterized base paths and the setting is on, instead of silently keeping one. With the setting off the old ir1.basePath ?? ir2.basePath behavior is preserved exactly.
  • Plumb respect-per-spec-base-pathrespectPerSpecBasePath through the settings schemas, generators.yml definition + regenerated JSON schemas, convertGeneratorsConfiguration, getAPIDefinitionSettings, LegacyApiSpecAdapter, and parser options (mirrors respect-parameter-content).
  • CLI changelog entry (feat).
  • Updated README.md generator (if applicable)

Testing

  • Unit tests added/updated — two importer fixtures: per-spec-base-path (setting on: basePath: null, oauth/token, api/v3/items) and per-spec-base-path-disabled (setting off: old collapsed behavior). Pre-existing snapshots, incl. x-fern-base-path, are unchanged.
  • Manual testing completed — rebuilt dev CLI and ran fern ir on a repro of the customer's generators.yml. Setting on: top-level basePath: null, oauth endpoint fullPath: oauth/token, main endpoint fullPath: api/v3/aa-sequence/items, and the oauth::POST /oauth/token auth reference still resolves. Setting absent: identical to main (oauth/token, aa-sequence/items).
  • Importer suites pass apart from the pre-existing url-reference fixtures that require fetching a live GitHub URL.

Note on php-sdk seed

The two new test-definitions fixtures are picked up by every generator's seed matrix, including php-sdk. The generated PHP fails composer analyze (phpstan: Variable $clientId on left side of ?? always exists and is not nullable) because mandatory OAuth client credentials are non-nullable in the root client while the OAuth provider still emits $clientId ?? ''. That is a pre-existing PHP generator issue unrelated to this change (the -disabled fixture, which keeps the old behavior, fails identically), so both fixtures are added to seed/php-sdk/seed.yml allowedFailures alongside the existing cli-oauth entry.


Written by Devin

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 1 additional finding.

Open in Devin Review

@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Docs Generation Benchmark Results

Comparing PR branch against median of 5 nightly run(s) on main (latest: 2026-08-24T04:15:25Z).

Fixture main PR Delta
docs 270.7s (n=5) 256.7s (35 versions) -14.0s (-5.2%)

Docs generation runs fern generate --docs --preview end-to-end against the benchmark fixture with 35 API versions (each version: markdown processing + OpenAPI-to-IR + FDR upload).
Delta is computed against the nightly baseline on main.
Baseline from nightly run(s) on main (latest: 2026-08-24T04:15:25Z). Trigger benchmark-baseline to refresh.
Last updated: 2026-08-24 20:14 UTC

@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

SDK Generation Benchmark Results

Comparing PR branch against median of 5 nightly run(s) on main (latest: 2026-08-24T04:15:25Z).

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
csharp-sdk square 75s (n=5) 113s (n=5) 95s +20s (+26.7%)
go-sdk square 137s (n=5) 298s (n=5) 141s +4s (+2.9%)
java-sdk square 239s (n=5) 285s (n=5) 223s -16s (-6.7%)
php-sdk square 64s (n=5) N/A 56s -8s (-12.5%)
python-sdk square 152s (n=5) 252s (n=5) 153s +1s (+0.7%)
ruby-sdk-v2 square 95s (n=5) 125s (n=5) 105s +10s (+10.5%)
rust-sdk square 228s (n=5) 215s (n=5) 194s -34s (-14.9%)
swift-sdk square 60s (n=5) 450s (n=5) 76s +16s (+26.7%)
ts-sdk square 138s (n=5) 150s (n=5) 179s +41s (+29.7%)

main (generator): generator-only time via --skip-scripts (includes Docker image build, container startup, IR parsing, and code generation — this is the same Docker-based flow customers use via fern generate). main (E2E): full customer-observable time including build/test scripts (nightly baseline, informational). Delta is computed against generator-only baseline.
⚠️ = generation exited with a non-zero exit code (timing may not reflect a successful run).
Baseline from nightly runs on main (latest: 2026-08-24T04:15:25Z). Trigger benchmark-baseline to refresh.
Last updated: 2026-08-24 20:16 UTC

@fern-api
fern-api Bot force-pushed the devin/1787255075-per-spec-base-path branch from efb5b22 to 250def4 Compare August 24, 2026 18:06
@fern-api fern-api Bot changed the title fix(cli): apply OpenAPI base paths per spec feat(cli): add respect-per-spec-base-path setting for per-spec x-fern-base-path Aug 24, 2026
fern-api Bot and others added 3 commits August 24, 2026 18:23
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@fern-api
fern-api Bot force-pushed the devin/1787255075-per-spec-base-path branch from 250def4 to b85eccc Compare August 24, 2026 18:24
@adidavid014
adidavid014 merged commit b01a906 into main Aug 25, 2026
225 checks passed
@adidavid014
adidavid014 deleted the devin/1787255075-per-spec-base-path branch August 25, 2026 15:04
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.

2 participants