Skip to content

Latest commit

 

History

History
105 lines (72 loc) · 10.8 KB

CONTRIBUTING.md

File metadata and controls

105 lines (72 loc) · 10.8 KB

Contributing to the Didit Ruby SDK

Thank you for helping. Read docs/DESIGN.md first: it is the shared contract of all seven Didit server SDKs, and when the code and the design disagree, the code is wrong.

Setup

You need Ruby 3.1 or newer and Bundler. The type check (Steep) needs Ruby 3.3 or newer; on older Rubies the Gemfile leaves it out.

bundle install

Layout

Path What Edited by hand
openapi/verification.json, openapi/account.json, openapi/webhook-vectors.json Upstream copies Never (spec sync only)
openapi/overlay.json SDK naming (x-didit-sdk) for operations that do not carry it upstream yet Yes
openapi/deprecated-operations.json Removed or renamed operations, kept as deprecated methods for one minor version Spec sync only
codegen/ The generator and its tests (codegen/spec) Yes
lib/didit/resources/, lib/didit/resources.rb, lib/didit/types/, lib/didit/types.rb, sig/didit/resources/, sig/didit/resources.rbs, sig/didit/types/, spec/generated/ Generated layer Never
lib/didit.rb, lib/didit/*.rb, lib/didit/internal/, lib/didit/webhooks*, lib/didit/authentication* Hand-written core: client, transport, retries, errors, pagination, files, webhooks, Authentication Yes
sig/ (outside the generated paths) RBS signatures of the core, one file per lib/ file Yes

Generated files start with Code generated by codegen from openapi/*.json. DO NOT EDIT. To change them, change the generator in codegen/ or openapi/overlay.json, then regenerate. CI fails when the committed generated code is stale.

Commands

bundle exec rake generate         # regenerate the generated layer from openapi/
bundle exec rake generate:check   # fail when the committed generated code is stale
bundle exec rake spec             # RSpec over spec/ (live tests are skipped)
bundle exec rake codegen:spec     # generator tests in codegen/spec
bundle exec rake rubocop          # RuboCop
bundle exec steep check           # RBS signatures checked with Steep (Ruby 3.3+)
bundle exec rake build            # build the gem into pkg/ (never publishes)
COVERAGE=1 bundle exec rspec      # fails under 90 % line coverage of the hand-written core
ruby codegen/spec_sync.rb         # download the upstream spec, regenerate, summarise the changes

bundle exec rake runs generate:check, spec, codegen:spec and rubocop.

Changing the API surface

The API surface comes from the OpenAPI documents published at docs.didit.me (mintlify-docs). To add or change an endpoint, change the backend and its OpenAPI document there; the spec sync then brings it here and to the other six SDKs.

  • openapi/overlay.json holds the naming of operations that do not carry x-didit-sdk upstream. Keys are "<METHOD> <path>" for verification.json and "account <METHOD> <path>" for account.json; each value has resource (dot path of snake_case names), method, auth, server and, for application-scoped paths, scope (DESIGN section 4). When upstream carries x-didit-sdk, the upstream value wins and the overlay entry is deleted.
  • A new server operation without naming makes the generator fail with a message that names it. The fix is an overlay entry, or x-didit-sdk upstream.
  • A removed or renamed operation is never deleted silently: the spec sync records it in openapi/deprecated-operations.json and the generator keeps it as a deprecated method for one minor version (DESIGN section 3.1).
  • After any change in openapi/ or codegen/, run bundle exec rake generate and commit the result with the change.

The spec sync

.github/workflows/spec-sync.yml runs daily. When an upstream document changed, it regenerates the code, runs the checks and opens a pull request titled chore(spec): sync API spec YYYY-MM-DD whose body lists added, removed and changed operations and schemas and calls out anything breaking. The gems, the generator and the checks run in a job with a read-only token; a second job, which installs nothing, pushes the commit and opens or updates the pull request. You may push commits to the branch of that pull request (for example an overlay entry when the generator asks for one). Later syncs do not overwrite a branch that holds commits from people; merge or close the pull request and the next sync picks up anything that changed since. If a run pushed the branch but could not open the pull request (for example before the Actions setting below was ticked), the next run opens it.

Checks and live smoke tests

CI (.github/workflows/ci.yml) runs the tests on Ruby 3.1, 3.2, 3.3, 3.4 and 4.0 on Linux and on Ruby 3.4 on macOS and Windows, the Authentication tests with jwt 2.7.0 (the gemspec's floor) and 2.10.2 (JWT_VERSION), RuboCop, Steep, the generated code check, the coverage gate, a build and clean install of the gem, a gitleaks scan of the full history (.gitleaks.toml) and a check that CLAUDE.md and AGENTS.md are identical. Run the same commands before you open a pull request.

Unit tests never touch the network (WebMock blocks it). Fixtures are synthetic: test webhook secrets start with whsec_test_, and keys and tokens are generated at test time.

Live smoke tests are tagged live: true and run only when DIDIT_SANDBOX_API_KEY holds the API key of a Didit sandbox application; DIDIT_SANDBOX_WORKFLOW_ID is optional. They never run in CI.

DIDIT_SANDBOX_API_KEY=... bundle exec rspec --tag live

Commits and releases

Pull request titles follow Conventional Commits with the type that matches the semver impact: fix: is a patch, feat: a minor version, and feat!: or a BREAKING CHANGE: footer a major version (a minor version while the SDK is 0.x). chore(spec): sync pull requests appear in the changelog and release at least a patch version; other chore, ci, docs, test, refactor, style and build commits release nothing on their own. Dependabot pull requests are chore(deps) and ci; retitle one that changes a runtime dependency in didit-sdk.gemspec to fix(deps): ... so that it ships.

Pull requests are squash-merged, so the title becomes the commit that release-please reads. Public API changes need tests and a README update.

release-please (.github/workflows/release.yml) keeps a release pull request open with the next version (lib/didit/version.rb, .release-please-manifest.json) and the generated CHANGELOG.md. Never edit the changelog by hand. Merging the release pull request makes the next run create the vX.Y.Z tag and the GitHub release. The build job of the same workflow then checks out the tag, checks that it equals Didit::VERSION, runs the full test suite and builds the gem. The publish job waits for a reviewer of the rubygems environment; once approved, it checks that the gem is the one the build job tested, attests its build provenance, attaches it to the GitHub release and pushes it to RubyGems.org with trusted publishing. Only the publish job can obtain the OIDC token, and it installs and runs nothing from the repository or its bundle. If a step fails for a transient reason, re-run the failed jobs (RubyGems.org refuses a second push of the same version); if the tagged code itself fails, fix main and release the next patch version.

1.0.0 was released on 2026-10-29. Merging the release pull request is the release approval (Alex, 2026-10-29); nobody tags or publishes by hand. The workflow runs while the repository variable RELEASES_ENABLED is true. To force a specific version, merge a commit on main whose message ends with the footer Release-As: X.Y.Z first.

One-time registry setup (done for 1.0.0 on 2026-10-29)

  1. RubyGems.org: use the Didit account (owner didit) with MFA enabled, and add the other Didit maintainers as owners of didit-sdk once it exists, each with MFA.
  2. Trusted publisher: didit-sdk does not exist on RubyGems.org yet, so add a pending trusted publisher from the account settings (gem name didit-sdk, GitHub Actions, repository didit-protocol/sdk-ruby, workflow file release.yml, environment rubygems). The first push from the workflow creates the gem; after that the publisher is listed on the gem's trusted publishers page. No API key is stored in GitHub.
  3. GitHub environment rubygems (Settings > Environments): the trusted publisher's environment, with no required reviewers (merging the release pull request is the approval); deployment branches may be limited to main (the publish job runs on pushes to main). Create it before setting RELEASES_ENABLED: a job that names a missing environment creates it without any protection rule.
  4. Tag ruleset for v* (Settings > Rules > Rulesets): restrict creation, update and deletion, with only the identity that creates the release tags on the bypass list (the role, team or GitHub App behind RELEASE_PLEASE_TOKEN). If that identity cannot be added to the bypass list (for example the default GITHUB_TOKEN), restrict update and deletion only.
  5. Settings > Actions > General: "Allow GitHub Actions to create and approve pull requests", so release-please and the spec sync can open their pull requests.
  6. Optional repository secret RELEASE_PLEASE_TOKEN: a fine-grained token with contents, issues and pull requests read and write on this repository only (for a GitHub App, mint its token in the workflow with actions/create-github-app-token and pass that instead). Without it the release pull request gets no CI run, because pull requests opened with the default GITHUB_TOKEN start no workflows.
  7. Settings > Collaborators and teams: give @didit-protocol/backend, the code owners in .github/CODEOWNERS, write access; GitHub ignores code owners without it.
  8. The repository is public and the repository variable RELEASES_ENABLED is true (both since 2026-10-29).
  9. Check a release with gh attestation verify didit-sdk-X.Y.Z.gem --repo didit-protocol/sdk-ruby.

Keep the SDKs in sync

Didit has seven server SDKs: sdk-python, sdk-node, sdk-go, sdk-java, sdk-php, sdk-ruby and sdk-dotnet. This is a hard rule (see CLAUDE.md):

  • Any public API change in service-didit-verification or service-didit-auth must reach the OpenAPI documents in mintlify-docs, and from there every server SDK through the spec sync pull request.
  • A change to hand-written core behaviour that DESIGN.md describes (client options, errors, retries, pagination, webhooks, Authentication) is made in all seven repositories, one pull request each, and DESIGN.md is updated in all seven in the same words.
  • Feature parity: a capability shipped in one server SDK ships in the other six.

Every pull request states its parity: the same change in the other six repositories, or why it does not apply.

Security

Report vulnerabilities privately, never in an issue: see SECURITY.md. Never commit secrets, API keys or real customer data.