Skip to content

feat(ruby): remove omitted basic auth fields from SDK API, add WireMock auth matching#14411

Open
Swimburger wants to merge 26 commits intomainfrom
devin/1774997764-basic-auth-optional-ruby-sdk
Open

feat(ruby): remove omitted basic auth fields from SDK API, add WireMock auth matching#14411
Swimburger wants to merge 26 commits intomainfrom
devin/1774997764-basic-auth-optional-ruby-sdk

Conversation

@Swimburger
Copy link
Copy Markdown
Member

@Swimburger Swimburger commented Mar 31, 2026

Description

Refs #14378

Split from #14378 (one PR per generator).

When usernameOmit or passwordOmit flags are set in the IR's BasicAuthScheme, the generated Ruby SDK now completely removes the omitted field from the constructor parameters (rather than making it optional/nilable). Internally, the omitted field is treated as an empty string when encoding the Authorization header. Default behavior (both fields required) is preserved when no omit flags are set.

Additionally, WireMock stub mappings now include exact Authorization header matching at request time. Basic auth endpoints use equalTo with the precise base64-encoded value; bearer/header auth endpoints use regex matches. This means WireMock will reject requests with incorrect auth headers (returning 404) rather than silently accepting them.

Changes Made

Ruby Generator

  • RootClientGenerator.tsgetAuthenticationParameters(): Per-field omit checks using typed scheme.usernameOmit/scheme.passwordOmit (IR SDK v66 has typed fields — no cast needed). When a field's omit flag is true, the corresponding keyword parameter is excluded entirely from the generated constructor.
  • RootClientGenerator.tsgetInitializeMethod(): Per-field condition and encoding logic:
    • Credential string is built conditionally to avoid cosmetic #{""} in generated code (e.g., password omitted → "#{username}:" not "#{username}:#{""}")
    • Nil-check conditions only reference non-omitted fields (e.g., passwordOmit: true → condition is !username.nil? only)
    • Both-omitted edge case: the scheme is skipped entirely via continue (no auth header sent)
    • Control flow fix: Replaced i === 0 index check for if/elsif determination with an isFirstBlock tracking variable. When the first basic auth scheme is skipped (e.g., both fields omitted), subsequent schemes now correctly emit if instead of elsif.
    • end statement is emitted after the loop using an emittedAnyBlock flag, preventing dangling end when all schemes are skipped.
  • EndpointSnippetGenerator.ts (dynamic snippets): Updated getConstructorBasicAuthArgs to check usernameOmit/passwordOmit and exclude omitted fields from generated snippet code. Uses as unknown as Record<string, unknown> cast (necessary — @fern-api/dynamic-ir-sdk lacks typed omit fields).
  • DynamicSnippetsConverter.ts (shared CLI): Updated convertAuth() to pass usernameOmit/passwordOmit from the main IR through to the dynamic IR data. Uses an intersection type (BasicAuth & { usernameOmit?: boolean; passwordOmit?: boolean }) since the dynamic IR type definition doesn't declare these fields yet. (This change is shared across all generator PRs and may already be in main via the merged TS PR feat(typescript): support omitting username/password in basic auth when configured in IR #14406.)

Wire Tests

  • WireTestGenerator.ts:
    • buildAuthParamsForSetup(): Per-field omit checks — skips omitted fields when generating client constructor args in wire test setup.
    • generateEndpointTestMethod(): Now emits verify_authorization_header() call after verify_request_count() when basic auth is configured, asserting the exact Authorization header value on captured WireMock requests.
    • New buildExpectedAuthorizationHeader(): Computes the expected Basic <base64> header value based on omit flags (e.g., password omitted → Basic base64("test-username:")).
  • WireTestSetupGenerator.ts: Added verify_authorization_header helper method to the generated WireMockTestCase base class. Queries WireMock admin API for captured requests and asserts the Authorization header matches the expected value.

Shared: mock-utils (WireMock mapping generation)

  • packages/commons/mock-utils/index.ts: Added Authorization header matchers to WireMock stub mappings for all authenticated endpoints:
    • Basic auth: equalTo with exact base64-encoded value (e.g., "Basic dGVzdC11c2VybmFtZTo=" for username-only). Handles usernameOmit/passwordOmit — omitted fields become empty strings in the encoding. Both-omitted skips the header entirely.
    • Bearer auth: matches: "Bearer .+" regex pattern
    • Header auth: matches: ".+" regex pattern on the configured header name
    • Simplified condition from if (endpoint.auth && !(endpoint.security != null && endpoint.security.length > 0)) to if (endpoint.auth) — the old condition blocked auth matching when endpoint.security was populated, even for simple single-scheme endpoints like basic auth.
    • Updated WireMockMapping interface to support both matches and equalTo for headers.

Seed & Config

  • versions.yml: New 1.4.0 entry (irVersion: 66)
  • seed.yml: Enabled enableWireTests: true for the basic-auth-pw-omitted fixture
  • New basic-auth-pw-omitted test fixture with password: omit: true, plus full seed output at seed/ruby-sdk-v2/basic-auth-pw-omitted/ (including wire-tests output folder)
  • Regenerated seed output: Ruby (basic-auth, basic-auth-pw-omitted, exhaustive, examples) and Rust (exhaustive, examples, simple-api) wiremock mappings updated with auth header matchers

Testing

  • Seed snapshot generated for basic-auth-pw-omitted fixture (including wire-tests output)
  • Seed snapshot regenerated for basic-auth fixture (updated with auth header assertions)
  • Seed snapshot regenerated for Ruby exhaustive and examples fixtures (bearer auth headers added to mappings)
  • Seed snapshot regenerated for Rust exhaustive, examples, and simple-api fixtures (bearer auth headers added to mappings)
  • Verified exact base64 values: basic-auth-pw-omittedBasic dGVzdC11c2VybmFtZTo= (base64 of test-username:), basic-authBasic dGVzdC11c2VybmFtZTp0ZXN0LXBhc3N3b3Jk (base64 of test-username:test-password)
  • Existing seed fixtures unchanged (no regressions)
  • Biome lint passes

⚠️ Human Review Checklist

  1. mock-utils security condition change: The old code skipped auth header matching when endpoint.security was populated (e.g., [{"Basic":[]}]). This was too restrictive — it blocked auth headers for all basic-auth endpoints. The new condition (if (endpoint.auth)) is simpler but broader. Verify this doesn't cause issues for endpoints with complex multi-scheme security configurations.
  2. mock-utils is shared code: This change affects ALL generators with wire tests, not just Ruby. Rust seed output also changed (bearer auth headers added to exhaustive, examples, simple-api mappings). Go mappings were unaffected (no diff in wiremock-mappings.json).
  3. Two layers of auth validation: Wire tests now validate auth via both (a) WireMock mapping-level equalTo/matches (request-time rejection) and (b) post-request verify_authorization_header() inspection. The mapping-level check is the primary one; the post-request check provides more explicit error messages. Consider whether both are needed.
  4. usernameOmit/passwordOmit runtime cast in mock-utils: Uses scheme as unknown as Record<string, unknown> because @fern-fern/ir-sdk types may lag behind the actual IR. Verify this matches the runtime IR structure.
  5. isFirstBlock / emittedAnyBlock correctness: Verify that end is emitted exactly once after the loop when at least one block was written, and not at all when every scheme is skipped.
  6. credentialStr conditional logic: The else branch covers both the normal both-present case AND the both-omitted case — but both-omitted is unreachable because the continue above skips the iteration.
  7. Dynamic snippets cast is intentional: EndpointSnippetGenerator.ts still uses as unknown as Record<string, unknown> because @fern-api/dynamic-ir-sdk lacks typed omit fields.
  8. DynamicSnippetsConverter is shared code: The change to DynamicSnippetsConverter.ts affects all generators, not just Ruby. This same fix is cherry-picked across all 6 basic-auth-omit PRs.

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


Open with Devin

… configured in IR

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.

Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

Devin Review found 2 potential issues.

View 3 additional findings in Devin Review.

Open in Devin Review

omitted entirely.
type: feat
createdAt: "2026-03-31"
irVersion: 61
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot Mar 31, 2026

Choose a reason for hiding this comment

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

🔴 irVersion 61 in versions.yml strips usernameOmit/passwordOmit fields, making the entire feature non-functional

The versions.yml entry for v1.1.14 declares irVersion: 61, but the usernameOmit and passwordOmit fields on BasicAuthScheme were introduced in IR v63. When the Fern CLI runs this generator, it migrates the IR down from latest to v61 using the migration chain. The v63-to-v62 migration at packages/cli/generation/ir-migrations/src/migrations/v63-to-v62/migrateFromV63ToV62.ts:140-147 explicitly strips these fields from the BasicAuthScheme. As a result, the generator will never receive usernameOmit or passwordOmit in production — the scheme.usernameOmit === true check (RootClientGenerator.ts:129-130) will always evaluate to false because the field is undefined after IR migration. The seed output in seed/ruby-sdk-v2/basic-auth-pw-omitted/lib/seed/client.rb confirms this: the generated client still takes password: as a required parameter despite the test definition setting omit: true. Additionally, the IR migration entry at v63-to-v62 has [GeneratorName.RUBY_SDK]: GeneratorWasNeverUpdatedToConsumeNewIR and needs to be updated to register version "1.1.14" so the CLI knows not to migrate down past v63 for this generator version.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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.

The IR version mismatch (v61 vs v63) is a known limitation and out of scope for this PR per discussion with the maintainer. The generator code is forward-compatible and will activate once the IR version is bumped in a separate PR.

Swimburger and others added 3 commits April 1, 2026 15:53
…instead of coarse eitherOmitted flag

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

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…, use empty string internally

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration devin-ai-integration bot changed the title feat(ruby-sdk): support optional username/password in basic auth when configured in IR feat(ruby-sdk): remove omitted basic auth fields from SDK API, use empty string internally Apr 2, 2026
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View 7 additional findings in Devin Review.

Open in Devin Review

Comment on lines +126 to +128
const scheme = basicAuthScheme as unknown as Record<string, unknown>;
const usernameOmitted = scheme.usernameOmit === true;
const passwordOmitted = scheme.passwordOmit === true;
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot Apr 2, 2026

Choose a reason for hiding this comment

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

🔴 Unnecessary as unknown as Record<string, unknown> cast bypasses type safety for usernameOmit/passwordOmit

FernIr.AuthScheme.Basic extends FernIr.BasicAuthScheme which already declares usernameOmit: boolean | undefined and passwordOmit: boolean | undefined (packages/ir-sdk/src/sdk/api/resources/auth/types/BasicAuthScheme.ts:10-15). The filter's type guard at line 105–106 narrows basicAuthSchemes elements to FernIr.AuthScheme & { type: "basic" }, which resolves to FernIr.AuthScheme.Basic. The as unknown as Record<string, unknown> cast is unnecessary and violates the repository rule in CLAUDE.md: "Never use as unknown as X. These are escape hatches that bypass the type system entirely. If the types don't line up, fix the types." The code can directly use basicAuthScheme.usernameOmit and basicAuthScheme.passwordOmit.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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.

Good catch — the Ruby generator imports BasicAuthScheme from @fern-fern/ir-sdk v61 which doesn't have usernameOmit/passwordOmit in its type definitions, so basicAuthScheme.usernameOmit would be a type error at compile time. The as unknown as Record<string, unknown> cast is necessary for this IR version. Once the IR version is bumped to v63+, this cast can be replaced with direct access.

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.

This is a false positive. The Ruby generator imports from @fern-fern/ir-sdk@^61.7.0 (IR v61), not @fern-api/ir-sdk. The v61 BasicAuthScheme type does not have usernameOmit/passwordOmit fields — those were added in IR v63. The as unknown as Record<string, unknown> cast is necessary because the fields don't exist in the type definition at this IR version. Fixing this would require bumping the Ruby generator to IR v63, which is out of scope for this PR.

Swimburger and others added 2 commits April 2, 2026 17:24
…non-mandatory

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…if when first scheme skipped

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

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

Choose a reason for hiding this comment

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

Devin Review found 2 new potential issues.

View 7 additional findings in Devin Review.

Open in Devin Review

Comment on lines +365 to +367
const schemeRecord = scheme as unknown as Record<string, unknown>;
const usernameOmitted = schemeRecord.usernameOmit === true;
const passwordOmitted = schemeRecord.passwordOmit === true;
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot Apr 2, 2026

Choose a reason for hiding this comment

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

🔴 Unnecessary as unknown as Record<string, unknown> cast in getAuthenticationParameters for basic auth omit fields

Inside case "basic" of the switch on scheme.type, TypeScript narrows scheme to FernIr.AuthScheme.Basic which extends FernIr.BasicAuthScheme — a type that already has usernameOmit: boolean | undefined and passwordOmit: boolean | undefined (packages/ir-sdk/src/sdk/api/resources/auth/types/BasicAuthScheme.ts:10-15). The as unknown as Record<string, unknown> cast is unnecessary and violates the repository rule in CLAUDE.md: "Never use as unknown as X." The code can directly use scheme.usernameOmit and scheme.passwordOmit.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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.

Same as above — false positive. The Ruby generator uses @fern-fern/ir-sdk@^61.7.0 (IR v61), which doesn't have usernameOmit/passwordOmit on BasicAuthScheme. The cast is required at this IR version.

Swimburger and others added 6 commits April 2, 2026 21:28
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>
…elog and code comment

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>
devin-ai-integration[bot]

This comment was marked as resolved.

Swimburger and others added 4 commits April 3, 2026 20:24
…dOmit

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…@fern-fern/ir-sdk)

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

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>
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View 7 additional findings in Devin Review.

Open in Devin Review

Comment on lines +216 to +219
// usernameOmit/passwordOmit may exist in newer IR versions
const authRecord = auth as unknown as Record<string, unknown>;
const usernameOmitted = !!authRecord.usernameOmit;
const passwordOmitted = !!authRecord.passwordOmit;
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot Apr 3, 2026

Choose a reason for hiding this comment

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

🔴 Dynamic IR BasicAuth type not updated — usernameOmit/passwordOmit smuggled via untyped object spread

The dynamic IR BasicAuth definition (packages/ir-sdk/fern/apis/ir-types-latest/definition/dynamic/auth.yml:22-25) only has username and password. The converter at DynamicSnippetsConverter.ts:736-749 smuggles usernameOmit/passwordOmit as extra runtime properties through Auth.basic()'s ...value spread (packages/ir-sdk/src/sdk/api/resources/dynamic/resources/auth/types/Auth.ts:50). The EndpointSnippetGenerator.ts:217 then reads them via as unknown as Record<string, unknown>. This is fragile: any code path that reconstructs the BasicAuth object (e.g., the V66→V65 IR migration at packages/cli/generation/ir-migrations/src/migrations/v66-to-v65/migrateFromV66ToV65.ts:1780-1783 which explicitly creates { username, password }) will silently drop these properties. The proper fix is to add usernameOmit and passwordOmit as optional fields on the dynamic IR's BasicAuth type definition, then regenerate the SDK types.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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.

This is a known limitation. The FernIr.dynamic.BasicAuth type from the dynamic IR SDK doesn't have typed usernameOmit/passwordOmit fields yet. The as unknown as Record<string, unknown> cast is necessary because the published SDK package lacks these properties even though they exist in the IR schema. Updating the dynamic IR types is out of scope for this PR per maintainer instruction ("Fix the non-IR changes"). The cast will be removed once the published IR SDK is updated to include these fields.

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.

Acknowledged — the proper fix is to add usernameOmit/passwordOmit to the dynamic IR's BasicAuth type definition. However, updating the dynamic IR type definition is an IR change, which is out of scope for this PR per maintainer instruction ("Fix the non-IR changes"). The cast is a deliberate workaround to avoid modifying the IR SDK types.

The DynamicSnippetsConverter now passes these fields through as extra properties on the dynamic IR data, and the dynamic snippets generators read them via cast. This is intentionally temporary — once the dynamic IR types are updated to include these fields (in a separate IR PR), these casts can be removed.

Swimburger and others added 2 commits April 3, 2026 21:54
…to dynamic IR

The DynamicSnippetsConverter was constructing dynamic BasicAuth with only
username and password fields, dropping usernameOmit/passwordOmit from the
main IR's BasicAuthScheme. This caused dynamic snippets generators to
always include omitted auth fields (e.g. $password) since they couldn't
detect the omit flags in the dynamic IR data.

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>
devin-ai-integration[bot]

This comment was marked as resolved.

…fe API, bump to 1.4.0)

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

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

github-actions bot commented Apr 6, 2026

SDK Generation Benchmark Results

Comparing PR branch against main baseline.

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
csharp-sdk square N/A N/A 131s N/A
go-sdk square N/A N/A 293s N/A
java-sdk square N/A N/A 322s N/A
php-sdk square N/A N/A 123s N/A
python-sdk square N/A N/A 162s N/A
ruby-sdk-v2 square N/A N/A 160s N/A
rust-sdk square N/A N/A 132s N/A
swift-sdk square N/A N/A 141s N/A
ts-sdk square N/A N/A 143s N/A

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).

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Comment on lines +135 to +144
if (!usernameOmitted && !passwordOmitted) {
condition = `!${usernameName}.nil? && !${passwordName}.nil?`;
} else if (usernameOmitted && !passwordOmitted) {
condition = `!${passwordName}.nil?`;
} else if (!usernameOmitted && passwordOmitted) {
condition = `!${usernameName}.nil?`;
} else {
// Both fields omitted — skip auth header entirely when auth is non-mandatory
continue;
}
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.

Critical bug: When auth is required (isAuthOptional=false) and a single basic auth scheme has an omitted field, the generated code will unconditionally set the Authorization header even when the non-omitted field is nil.

For example, if passwordOmit=true and auth is required:

  • The condition check at line 140 (!${usernameName}.nil?) is calculated but never used
  • The code falls through to line 156's else branch which unconditionally sets the header
  • This generates: headers["Authorization"] = "Basic #{Base64.strict_encode64("#{username}:#{""}")}"
  • If username is nil, this produces invalid Basic auth: "Basic #{Base64.strict_encode64("#{nil}:")}""Basic Og=="

Fix: When a field is omitted but auth is required, the condition check should still be applied:

if (!usernameOmitted && !passwordOmitted) {
    // Both required - check both or neither based on isAuthOptional
    condition = `!${usernameName}.nil? && !${passwordName}.nil?`;
} else if (usernameOmitted && !passwordOmitted) {
    condition = `!${passwordName}.nil?`;
} else if (!usernameOmitted && passwordOmitted) {
    condition = `!${usernameName}.nil?`;
} else {
    continue;
}

// Always use condition when there's a non-omitted field that could be nil
if (isAuthOptional || basicAuthSchemes.length > 1 || usernameOmitted || passwordOmitted) {
    // Use conditional logic
} else {
    // Both fields present and required
}

Spotted by Graphite

Fix in Graphite


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

This comment came from an experimental review—please leave feedback if it was helpful/unhelpful. Learn more about experimental comments here.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 7, 2026

SDK Generation Benchmark Results

Comparing PR branch against main baseline.

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
ruby-sdk-v2 square N/A N/A 153s N/A

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).

@devin-ai-integration
Copy link
Copy Markdown
Contributor

Self-review response to Graphite comment (unconditional header when auth is mandatory + field omitted):

This is pre-existing behavior, not a regression. The original code on main also unconditionally sets the Authorization header in the else branch (auth mandatory, single scheme) — no nil-check for either field. The non-omitted field (username) is a required keyword argument in the generated Ruby constructor (def initialize(username:, ...)), so nil would only occur from deliberate misuse (Client.new(username: nil)).

Adding a nil guard only when a field is omitted — but not when both fields are present — would be inconsistent. If we want defensive nil-checks for mandatory auth, that should be done uniformly for all basic auth (omit and non-omit) as a separate follow-up.

When a field is omitted, the credential string is now built cleanly
(e.g., "#{username}:" instead of "#{username}:#{""}").

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

github-actions bot commented Apr 7, 2026

SDK Generation Benchmark Results

Comparing PR branch against main baseline.

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
ruby-sdk-v2 square N/A N/A 151s N/A

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).

@Swimburger Swimburger changed the title feat(ruby-sdk): remove omitted basic auth fields from SDK API, use empty string internally feat(ruby): remove omitted basic auth fields from SDK API, use empty string internally Apr 7, 2026
- Enable enableWireTests for basic-auth-pw-omitted in seed.yml
- Fix WireTestGenerator.buildAuthParamsForSetup to skip omitted fields
- Generated wire test correctly instantiates client with username only (no password param)

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

github-actions bot commented Apr 7, 2026

SDK Generation Benchmark Results

Comparing PR branch against main baseline.

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
ruby-sdk-v2 square N/A N/A 152s N/A

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).

- Add verify_authorization_header helper to WireMockTestCase base class
- Wire tests now assert the exact Authorization header value on captured requests
- basic-auth-pw-omitted: asserts 'Basic base64(test-username:)' (empty password)
- basic-auth: asserts 'Basic base64(test-username:test-password)' (both fields)

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

This comment was marked as resolved.

Swimburger and others added 2 commits April 7, 2026 16:22
…word are omitted

When both usernameOmit and passwordOmit are true, RootClientGenerator skips the
Authorization header entirely. buildExpectedAuthorizationHeader now matches this
behavior by returning null (no assertion) instead of 'Basic Og==' (base64(':')).

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

- Basic auth: uses equalTo with exact base64-encoded credentials
- Bearer auth: uses matches with 'Bearer .+' pattern
- Header auth: uses matches with '.+' pattern
- Handles usernameOmit/passwordOmit for basic auth (omitted field = empty string)
- Regenerated seed output for Ruby (basic-auth, basic-auth-pw-omitted, exhaustive, examples) and Rust (exhaustive, examples, simple-api)

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration devin-ai-integration bot changed the title feat(ruby): remove omitted basic auth fields from SDK API, use empty string internally feat(ruby): remove omitted basic auth fields from SDK API, add WireMock auth matching Apr 7, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 7, 2026

SDK Generation Benchmark Results

Comparing PR branch against latest nightly baseline on main (2026-04-07T04:47:12Z).

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
ruby-sdk-v2 square 151s 189s 153s +2s (+1.3%)

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-07T04:47:12Z). Trigger benchmark-baseline to refresh.

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.

3 participants