feat(ruby): annotate endpoint availability in generated Ruby v2 SDK#15115
feat(ruby): annotate endpoint availability in generated Ruby v2 SDK#15115Swimburger wants to merge 7 commits intomainfrom
Conversation
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.
Tip: disable this comment in your organization's Code Review settings.
SDK Generation Benchmark ResultsComparing PR branch against latest nightly baseline on Full benchmark table (click to expand)
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 |
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>
| const message = availability.message; | ||
| return message != null ? `@deprecated ${message}` : "@deprecated"; |
There was a problem hiding this comment.
Empty string edge case: When availability.message is an empty string "", the null check passes and produces "@deprecated " with a trailing space. Same issue exists on lines 22 and 26 for InDevelopment and PreRelease statuses.
Consider checking for empty strings:
const message = availability.message;
return message != null && message.length > 0 ? `@deprecated ${message}` : "@deprecated";Similarly for lines 22 and 26:
return availability.message != null && availability.message.length > 0
? `${warning} ${availability.message}`
: warning;| const message = availability.message; | |
| return message != null ? `@deprecated ${message}` : "@deprecated"; | |
| const message = availability.message; | |
| return message != null && message.length > 0 ? `@deprecated ${message}` : "@deprecated"; | |
Spotted by Graphite
Is this helpful? React 👍 or 👎 to let us know.
…lability flag 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>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…abilityAnnotations default on Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Description
Adds optional YARD availability tags (
@deprecated/@beta) to Ruby v2 SDK endpoint methods, mirroring the TypeScript generator'sgetAvailabilityDocsbehavior. Gated behind a new opt-in custom config flag so existing customers see no change.Mechanism used for each
AvailabilityStatus:@deprecatedtag (with the IRmessageappended when present).@beta This endpoint is in development and may change.(+<message>if provided).@beta This endpoint is in pre-release and may change.(+<message>if provided).Scope is Ruby v2 only. The legacy Ruby v1 generator source was removed from this monorepo in 1c167b9ec56 ("chore(ruby): remove legacy ruby generator source code") and superseded by
generators/ruby-v2/, so there is nothing to mirror this change into on the v1 side.Opt-in via
generateAvailabilityAnnotationsThe new annotations are gated behind
generateAvailabilityAnnotations(boolean, defaults tofalse) in the generator's custom config. Because YARD's@deprecatedsurfaces as a warning in strict lint setups (and-Werror-style configurations in other languages was the requester's concern), turning this on is a breaking change for downstream consumers that treat warnings as errors. With the flag off, the generator's output is byte-for-byte identical tomain.The flag is named generically (not
generateEndpointAvailability) so future availability surfaces — services, types, object properties, enum values, webhook payloads, websocket channels/messages, errors — can opt in under the same flag without introducing a new one. This PR only implements the HTTP endpoint case; the other surfaces remain unannotated for now.A
TODO(next-major): flip default to truecomment is in the schema next to the flag so the next major-version owner has a clear signal. The changelog calls out the same plan.Changes Made
generators/ruby-v2/sdk/src/endpoint/utils/getAvailabilityDocs.ts— small helper mirroring the TSgetAvailabilityDocshelper, but acceptingFernIr.Availability | undefineddirectly so it is trivially unit-testable.HttpEndpointGenerator.generateEnhancedDocstring, which is the single place Ruby v2 assembles the per-method docstring. The availability line is appended after any existingendpoint.docs, joined with\n.generateAvailabilityAnnotations: z.boolean().optional()toBaseRubyCustomConfigSchema(shared base for all ruby-v2 generators) with aTODO(next-major)comment documenting the eventual default flip. Call site ingenerateEnhancedDocstringchecksthis.context.customConfig.generateAvailabilityAnnotations === truesoundefined/falseboth short-circuit — no diff againstmain.getAvailabilityDocs.test.ts) covering everyAvailabilityStatusvariant with and without amessage.generators/ruby-v2/sdk/changes/unreleased/endpoint-availability-annotations.ymldescribing the flag, its default, and the plan to flip it in the next major bump.defaultCustomConfig.generateAvailabilityAnnotations: trueinseed/ruby-sdk-v2/seed.ymlso every seed fixture exercises the annotation emission, and regenerated all ruby-sdk-v2 seed snapshots. Customer-facing default inBaseRubyCustomConfigSchemaremainsfalse.Updates since last revision
defaultCustomConfiginseed/ruby-sdk-v2/seed.yml. Every fixture's.fern/metadata.jsonnow recordsgenerateAvailabilityAnnotations: true. Two fixtures produce Ruby code deltas —exhaustive/wire-tests(four endpoints carryingdeprecated/pre-release/in-development) andimdb(two endpoints). Every other fixture's IR has noavailabilityset, so the only snapshot change ismetadata.jsonrecording the new config key.node --enable-source-maps packages/seed/dist/cli.cjs test --generator ruby-sdk-v2 --skip-scripts) passes 124/124 locally, and CI'sseed-test-results (ruby-sdk-v2)is green on the committed snapshots.BaseRubyCustomConfigSchemais stillfalse(unchanged), and the flag-off path is exercised by the unit test; no dedicated flag-off seed fixture was added.generateEndpointAvailabilitytogenerateAvailabilityAnnotationsper earlier reviewer feedback so future availability surfaces can opt in under the same flag.@beta warningJSDoc wording on the helper;assertNeverin the default case).Testing
pnpm vitest run src/endpoint/utils/__test__/getAvailabilityDocs.test.ts(8/8 pass locally).pnpm compile+ lint clean.pnpm seed:build && node --enable-source-maps packages/seed/dist/cli.cjs test --generator ruby-sdk-v2 --skip-scripts): 124/124 pass with committed snapshots.exhaustive/wire-tests/lib/seed/endpoints/http_methods/client.rbandimdb/lib/seed/imdb/client.rbshow the new YARD tags (@deprecated,@deprecated <message>,@beta This endpoint is in pre-release and may change.,@beta This endpoint is in development and may change.).false; flag-off path exercised by the unit test. No dedicated flag-off seed fixture.Review checklist / risks worth a closer look
this.context.customConfig.generateAvailabilityAnnotations === true. Explicit=== truemeansundefined/false/any other value all skip the branch — intentional so that an unset flag matches current behavior. Sibling flags in the same file use the!customConfig.foostyle; not matching that here since this flag needs explicit opt-in semantics.seed/ruby-sdk-v2/seed.ymlsets the flagtrueviadefaultCustomConfig, which does NOT change the customer-facing default inBaseRubyCustomConfigSchema(stillfalse). Worth confirming this is the intended split.BaseRubyCustomConfigSchemain@fern-api/ruby-ast, so it is technically visible to any other ruby-v2 generator (model,dynamic-snippets, etc.). OnlyHttpEndpointGeneratorreads it today — desirable given the generic name, since future model/webhook-level annotations would read the same flag.generateEnhancedDocstringjoinsendpoint.docsand the availability line with\n. Look at theimdbsnapshot where an endpoint already has docs — the tag ends up on a line adjacent to the existing docs with no blank line separator, which is YARD-compatible but worth a glance.getAvailabilityDocsfrom three endpoint implementations (default / streaming / file-download). Ruby v2 funnels every HTTP endpoint throughHttpEndpointGenerator.generateUnpagedMethod, so wiring once is sufficient — please confirm there is no separate streaming/file-download path I missed.Link to Devin session: https://app.devin.ai/sessions/98b85b0fbce445f08c5b2055e98da667
Requested by: @Swimburger