Skip to content

feat(ruby): annotate endpoint availability in generated Ruby v2 SDK#15115

Open
Swimburger wants to merge 7 commits intomainfrom
devin/1776443720-ruby-availability-annotations
Open

feat(ruby): annotate endpoint availability in generated Ruby v2 SDK#15115
Swimburger wants to merge 7 commits intomainfrom
devin/1776443720-ruby-availability-annotations

Conversation

@Swimburger
Copy link
Copy Markdown
Member

@Swimburger Swimburger commented Apr 17, 2026

Description

Adds optional YARD availability tags (@deprecated / @beta) to Ruby v2 SDK endpoint methods, mirroring the TypeScript generator's getAvailabilityDocs behavior. Gated behind a new opt-in custom config flag so existing customers see no change.

Mechanism used for each AvailabilityStatus:

  • DEPRECATED — YARD's native @deprecated tag (with the IR message appended when present).
  • IN_DEVELOPMENT — free-form doc line matching the TS wording: @beta This endpoint is in development and may change. (+ <message> if provided).
  • PRE_RELEASE — free-form doc line: @beta This endpoint is in pre-release and may change. (+ <message> if provided).
  • GENERAL_AVAILABILITY / undefined — no extra docs emitted.

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 generateAvailabilityAnnotations

The new annotations are gated behind generateAvailabilityAnnotations (boolean, defaults to false) in the generator's custom config. Because YARD's @deprecated surfaces 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 to main.

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.

# generators.yml
generators:
  - name: fernapi/fern-ruby-sdk-v2
    config:
      generateAvailabilityAnnotations: true

A TODO(next-major): flip default to true comment 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

  • Added generators/ruby-v2/sdk/src/endpoint/utils/getAvailabilityDocs.ts — small helper mirroring the TS getAvailabilityDocs helper, but accepting FernIr.Availability | undefined directly so it is trivially unit-testable.
  • Wired the helper into HttpEndpointGenerator.generateEnhancedDocstring, which is the single place Ruby v2 assembles the per-method docstring. The availability line is appended after any existing endpoint.docs, joined with \n.
  • Added generateAvailabilityAnnotations: z.boolean().optional() to BaseRubyCustomConfigSchema (shared base for all ruby-v2 generators) with a TODO(next-major) comment documenting the eventual default flip. Call site in generateEnhancedDocstring checks this.context.customConfig.generateAvailabilityAnnotations === true so undefined/false both short-circuit — no diff against main.
  • Added a unit test (getAvailabilityDocs.test.ts) covering every AvailabilityStatus variant with and without a message.
  • Added an unreleased changelog entry under generators/ruby-v2/sdk/changes/unreleased/endpoint-availability-annotations.yml describing the flag, its default, and the plan to flip it in the next major bump.
  • Set defaultCustomConfig.generateAvailabilityAnnotations: true in seed/ruby-sdk-v2/seed.yml so every seed fixture exercises the annotation emission, and regenerated all ruby-sdk-v2 seed snapshots. Customer-facing default in BaseRubyCustomConfigSchema remains false.

Updates since last revision

  • Flipped the flag on by default in seed only via defaultCustomConfig in seed/ruby-sdk-v2/seed.yml. Every fixture's .fern/metadata.json now records generateAvailabilityAnnotations: true. Two fixtures produce Ruby code deltas — exhaustive/wire-tests (four endpoints carrying deprecated / pre-release / in-development) and imdb (two endpoints). Every other fixture's IR has no availability set, so the only snapshot change is metadata.json recording the new config key.
  • Full seed suite (node --enable-source-maps packages/seed/dist/cli.cjs test --generator ruby-sdk-v2 --skip-scripts) passes 124/124 locally, and CI's seed-test-results (ruby-sdk-v2) is green on the committed snapshots.
  • Flag-off parity: the customer-facing default in BaseRubyCustomConfigSchema is still false (unchanged), and the flag-off path is exercised by the unit test; no dedicated flag-off seed fixture was added.
  • Renamed the flag from generateEndpointAvailability to generateAvailabilityAnnotations per earlier reviewer feedback so future availability surfaces can opt in under the same flag.
  • Earlier revisions gated emission behind the feature flag and addressed Devin Review comments (@beta warning JSDoc wording on the helper; assertNever in the default case).

Testing

  • Unit tests added — pnpm vitest run src/endpoint/utils/__test__/getAvailabilityDocs.test.ts (8/8 pass locally).
  • pnpm compile + lint clean.
  • Full ruby-sdk-v2 seed suite (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.
  • Flag-on seed coverage: exhaustive/wire-tests/lib/seed/endpoints/http_methods/client.rb and imdb/lib/seed/imdb/client.rb show 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.).
  • Flag-off parity: customer-facing schema default is still false; flag-off path exercised by the unit test. No dedicated flag-off seed fixture.

Review checklist / risks worth a closer look

  • Gating semantics: the call site uses this.context.customConfig.generateAvailabilityAnnotations === true. Explicit === true means undefined/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.foo style; not matching that here since this flag needs explicit opt-in semantics.
  • Seed-only default flip: seed/ruby-sdk-v2/seed.yml sets the flag true via defaultCustomConfig, which does NOT change the customer-facing default in BaseRubyCustomConfigSchema (still false). Worth confirming this is the intended split.
  • Shared schema: the flag lives on BaseRubyCustomConfigSchema in @fern-api/ruby-ast, so it is technically visible to any other ruby-v2 generator (model, dynamic-snippets, etc.). Only HttpEndpointGenerator reads it today — desirable given the generic name, since future model/webhook-level annotations would read the same flag.
  • Docstring assembly: generateEnhancedDocstring joins endpoint.docs and the availability line with \n. Look at the imdb snapshot 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.
  • Single wiring site: TS calls getAvailabilityDocs from three endpoint implementations (default / streaming / file-download). Ruby v2 funnels every HTTP endpoint through HttpEndpointGenerator.generateUnpagedMethod, so wiring once is sufficient — please confirm there is no separate streaming/file-download path I missed.
  • No dedicated flag-off seed fixture: the flag-off path is covered by the unit test only; seed fixtures all run with the flag on.

Link to Devin session: https://app.devin.ai/sessions/98b85b0fbce445f08c5b2055e98da667
Requested by: @Swimburger


Open with Devin

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration
Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Copy link
Copy Markdown

@claude claude bot left a comment

Choose a reason for hiding this comment

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

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.

@devin-ai-integration devin-ai-integration bot changed the title feat(ruby-v2): annotate endpoint availability in generated SDK feat(ruby): annotate endpoint availability in generated Ruby v2 SDK Apr 17, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 17, 2026

SDK Generation Benchmark Results

Comparing PR branch against latest nightly baseline on main (2026-04-17T04:58:39Z).

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
ruby-sdk-v2 square 102s 156s 72s -30s (-29.4%)

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-04-17T04:58:39Z). Trigger benchmark-baseline to refresh.
Last updated: 2026-04-17 20:07 UTC

devin-ai-integration[bot]

This comment was marked as resolved.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
devin-ai-integration[bot]

This comment was marked as resolved.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Comment on lines +17 to +18
const message = availability.message;
return message != null ? `@deprecated ${message}` : "@deprecated";
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.

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;
Suggested change
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

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

Swimburger and others added 4 commits April 17, 2026 17:30
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant