Skip to content

fix(cli): support multi-spec on-prem generation and preflight SDK migration - #17782

Open
ATechAdventurer wants to merge 9 commits into
fern-api:mainfrom
ATechAdventurer:fix/onprem-multi-spec
Open

ATechAdventurer wants to merge 9 commits into
fern-api:mainfrom
ATechAdventurer:fix/onprem-multi-spec

Conversation

@ATechAdventurer

@ATechAdventurer ATechAdventurer commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Address the on-prem QA report where a Python SDK workspace contains OpenAPI and AsyncAPI and local generation refuses the two sources.

  • Inspect the exact image selected for execution, including registry overrides and digest pins, for com.postman.sdk-gen.adapter.multi-spec="true".
  • Pass all mounted supported specs into SDK Config IR in declaration order, preserving namespaces, only when the image declares the capability.
  • Preserve each source's import settings and attach a distinct filename-derived identity for composition diagnostics.
  • Pull uncached images using the existing Docker Hub authentication and image-pull helpers before inspecting their capability, so first-time runs work without a manual pull.
  • Fail closed for missing/false labels or failed inspection. Single-source runs skip inspection and retain existing behavior. Unsupported source types remain rejected.
  • Prepare all docs migration edits before writing sdk-config.yml or any docs file. Unsupported custom settings and malformed later version files now fail without leaving validation-induced partial output.

Verification

  • pnpm fern-dev:build passed, including 95 compilation/build tasks.
  • Full local-workspace-runner suite: 287 passed, including 43 capability/config tests. Includes old-image refusal, true/missing/false labels, failed inspection/pull, authenticate-pull-reinspect ordering, exact Podman runner/digest arguments, mixed-spec IR order, namespaces, per-source import settings, unsupported secondary type, and single-source no-probe behavior.
  • SDK migration/docs tests: 40 passed. New command regressions fail before the fix and pass afterward.
  • Biome checks passed for changed CLI code.
  • Full CLI manual run: USE_FERN_RC=true FERN_RC_NAMESPACE=multi-spec-qa FERN_GENERATOR_NETWORK=none fern-dev generate --group python-sdk --local generated and formatted Python from OpenAPI + AsyncAPI using a locally built capable adapter. Imported the generated SDK and accessed REST and WebSocket methods.
  • Repeated against the existing unlabelled rc image: refused before container generation with an upgrade instruction.
  • Cold-cache live check: pushed a capable QA image to a temporary localhost registry, removed its local reference, then ran the full CLI command. It pulled, inspected and generated successfully with the generator network disabled.
  • Real sdk migrate with unsupported docs import settings failed while leaving sdk-config.yml absent. Successful migration, help output, and invalid group handling were also exercised.

Rollout and limits

Companion runtime PR: https://github.com/postman-eng/sdk-gen-core/pull/2527

Requires the companion sdk-gen-core change and newly published adapter images carrying the capability label. This PR does not relabel existing images or assume a generator version has the capability.

The exact QA migration error was not supplied; the migration fix covers reproduced validation/read/parse ordering failures, not all possible errors. It does not make multi-file filesystem writes transactional: a later write failure can still leave partial output.

The actual ElevenLabs specs were unavailable; QA used repository Petstore/WebSocket fixtures. Docker was exercised live; Podman was covered through runner-selection tests. Fern paths lie outside this session's LSP root, so the successful full TypeScript build is the diagnostic evidence for those files.


Devin Review

@nitpickybot nitpickybot 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.

AI Review Summary

Two changes bundled here: a Docker-label capability probe that lets multi-spec workspaces reach capable adapter images, and a prepare-then-write reordering in fern sdk migrate so validation failures no longer leave partial output. The migration reordering looks correct and is well covered by the new tests. The capability probe has rough edges around cancellation during the preflight pull and the uniqueness/consistency of the new spec id.

  • 🔵 2 suggestion(s)

Review Scope

  • Mode: Full pull request
  • Range: Full pull request through 10f7132d09010a8f7500f1798dfa1f73266c7e82
  • Current range coverage: Complete
  • Cumulative pull request coverage: Complete

This review is complete for the current scope. Addressing all critical and warning findings above satisfies the review; suggestions are optional. Request another review only if subsequent changes introduce new behavior or materially expand the pull request's scope.

if (result.exitCode !== 0) {
// Execution normally pulls missing images, but this gate runs before execution.
await ensureDockerHubOatLogin({ imageName, runner, logger });
await pullImage(imageName, runner);

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.

🔵 suggestion

pullImage is invoked without an AbortSignal, and imageSupportsMultiSpec doesn't accept one either. A pull of a large adapter image during this preflight probe therefore isn't cancellable, so Ctrl-C here waits for the whole pull to finish. Consider threading the run's signal from ContainerExecutionEnvironment/runGenerator into imageSupportsMultiSpec and passing it to both loggingExeca and pullImage.


const specs = entries.map(
(entry): SourceSpec => ({
...(entries.length > 1 ? { id: basename(entry.specPath) } : {}),

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.

🔵 suggestion

id is only set when entries.length > 1 and is derived from basename(specPath). Two things worth reconsidering:

  1. Single-spec runs get no id at all, so spec identity is inconsistent across run shapes — if the adapter surfaces id in diagnostics, single-spec output silently loses it.
  2. basename isn't unique: specs/v1/openapi.yml and specs/v2/openapi.yml both collapse to openapi.yml, making composition diagnostics ambiguous in exactly the multi-spec case this is meant to serve. Folding in the namespace (or the manifest index) would disambiguate.

@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 bugs or issues to report.

Devin Review

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.

1 participant