Skip to content

feat(#59): capture installation + mint installation access tokens#70

Merged
G0maa merged 8 commits into
mainfrom
feature/GH-59-installation-access-tokens
Jun 14, 2026
Merged

feat(#59): capture installation + mint installation access tokens#70
G0maa merged 8 commits into
mainfrom
feature/GH-59-installation-access-tokens

Conversation

@G0maa

@G0maa G0maa commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Capture the installation, then mint repo-access tokensImplement GitHub App provisioning via Manifest flow #58 provisioned the App but it wasn't connected to any repos. This adds the github_installation table and the capture flow so that after the operator clicks "Install on repositories", GitHub's post-install redirect (?installation_id=…&setup_action=install) lands at the web callback, which POSTs to a new endpoint that records the installation and verifies it. This is the foundation Clone/pull operator repositories using installation tokens #60 (clone) builds on.
  • @octokit/auth-app does the security-sensitive part — the new GitHubInstallationTokenService signs the App JWT from the stored PEM, exchanges it for a ~1h installation token, and caches + auto-refreshes it. We adopt GitHub's first-party library rather than hand-roll RS256 signing, the iat/exp clock-skew window, and an expiry cache (the classic footguns). The per-App auth instance is cached so the library's token cache survives across calls. Rationale: AgDR-0012.
  • Separate table, not a column — a single self-hosted App can be installed on the operator's personal account plus N orgs, so installations are a @ManyToOne against github_app (the codebase's first ORM relation), unique on installation_id. Schema + rollback: AgDR-0013 / Migration20260611123621. The migration is additive (new table, zero blast radius) and tracked on this ticket via the migration label.
  • Tokens aren't persisted — they're short-lived and re-mintable from the PEM, so storing them would add encrypted-secret surface for no durability benefit. The capture flow mints once to verify the installation is real and ours (GitHub rejects a forged id), then persists only the installation. Clone/pull operator repositories using installation tokens #60 mints on demand for cloning.
  • Web — the callback page now branches on installation_id vs code/state and shows a "GitHub connected" state; the OpenAPI contract + generated web client are regenerated for captureGithubAppInstallationV1.

Scope boundary

Capture is via the post-install redirect by design. The installation/push webhook receiver + HMAC signature verification is the separate scope of #61 — kept out of this PR deliberately.

Testing

  1. pnpm --filter api test — 47 tests green; coverage 92.19 lines / 80.71 branches / 96.05 functions (gates 80/80/90). New suites cover the token service (mint, per-App cache reuse, error wrapping), the capture use-case (verify-then-persist, bad setup_action, no-App, 502-on-mint-fail), the repository (insert/idempotent/race + loadProvisionedApp), and the e2e 400 paths.
  2. pnpm --filter web test — 19 green, including the new capture-installation Zod contract checks.
  3. pnpm --filter api generate:openapi and pnpm --filter web generate:api leave no drift (committed).
  4. Manual trace: redirect to /setup/github/callback?installation_id=123&setup_action=install → POST /api/v1/github-app/capture-installation → row in github_installation → "GitHub connected".

Refs #59

Glossary

Term Definition
Installation The link GitHub creates when an App is installed on a set of repos; identified by a numeric installation_id. One App can have many.
Installation access token A short-lived (~1h) token, minted from the App JWT + installation id, that grants repo access (clone, API). Re-mintable; not stored.
App JWT A short-lived RS256 JWT signed with the App's private key, used to authenticate as the App (e.g. to request an installation token).
@octokit/auth-app GitHub's first-party auth-strategy library that signs the App JWT, exchanges installation tokens, and caches/refreshes them.
setup_action The GitHub query param on the post-install redirect; install means the operator completed an install (vs request).
@ManyToOne The MikroORM relation decorator mapping github_installation.app to a github_app foreign key.

Decision records

  • docs/agdr/AgDR-0012-installation-token-strategy.md — adopt @octokit/auth-app for token minting.
  • docs/agdr/AgDR-0013-migration-github-installation-table.md — the github_installation schema migration.

Summary by CodeRabbit

  • New Features

    • Added GitHub App installation capture functionality to finalize the provisioning workflow.
    • Implemented two-stage setup process with improved status tracking and enhanced success messaging.
  • Documentation

    • Added architecture decision records for installation token management and database schema design.

- Add the github_installation table (FK -> github_app, unique
  installation_id, nullable account_login) so one self-hosted App can be
  installed on the operator's personal account plus N orgs (AgDR-0013,
  Migration20260611123621)
- Add GitHubInstallationTokenService on @octokit/auth-app: signs the App
  JWT from the stored PEM, exchanges it for a ~1h installation token, and
  caches + auto-refreshes via a per-App auth instance (AgDR-0012) — the
  security-sensitive JWT/cache parts are the library's, not hand-rolled
- Add the capture-installation use-case: the post-install redirect
  (installation_id + setup_action) POSTs here; the use-case mints a token
  once to verify the installation is real and ours, then persists it.
  Tokens are not stored (short-lived; #60 mints on demand)
- Wire the web callback to branch on installation_id vs code/state and
  show a "GitHub connected" state; regenerate the OpenAPI contract + web
  client for captureGithubAppInstallationV1

Capture is via the redirect by design; the installation/push webhook
receiver + HMAC verification is #61's scope. Coverage 92/80/96.

Refs #59
@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@G0maa, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 17 minutes and 35 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 99376ea6-40f6-49f0-a515-9e66fd299755

📥 Commits

Reviewing files that changed from the base of the PR and between 295ccc0 and 2e8864a.

📒 Files selected for processing (8)
  • .github/workflows/security.yml
  • apps/api/.claude/CLAUDE.md
  • apps/api/openapi.json
  • apps/api/package.json
  • apps/api/src/app/github-app/use-cases/convert-manifest/tests/convert-manifest.use-case.unit.test.ts
  • apps/web/app/api/types.gen.ts
  • apps/web/app/api/zod.gen.ts
  • docs/agdr/AgDR-0014-github-client-consolidation.md
📝 Walkthrough

Walkthrough

Introduces a GithubClient abstract class replacing GitHubManifestClient, implemented by OctokitGithubClient (with PEM-fingerprint-cached auth) and MockGithubClient. Adds a capture-installation use-case with idempotent repository, controller, and module. Renames entity primary keys from id to uuid, adds github_installation table migration, and wires a two-stage callback flow in the web frontend.

Changes

GitHub App Installation Capture with Consolidated GitHub Client

Layer / File(s) Summary
GitHub client seam and module wiring
apps/api/src/modules/github-client/github-client.ts, apps/api/src/modules/github-client/github-client.types.ts, apps/api/src/modules/github-client/github-client.module.ts, apps/api/src/modules/github-client/mock-github-client.ts, apps/api/src/modules/github-client/github-manifest.client.ts, apps/api/src/app/github-app/use-cases/convert-manifest/convert-manifest.use-case.ts, apps/api/src/app/github-app/use-cases/convert-manifest/tests/convert-manifest.use-case.unit.test.ts
GitHubManifestClient is deleted and replaced by an abstract GithubClient with convertManifest and getInstallationToken contracts; GitHubClientModule factory selects MockGithubClient in test and OctokitGithubClient otherwise; ConvertManifestUseCase and its tests updated to inject GithubClient.
OctokitGithubClient with cached token minting
apps/api/src/modules/github-client/octokit-github-client.ts
OctokitGithubClient implements both GitHub operations: manifest conversion via fetch and installation token minting via createAppAuth, caching auth instances per app ID keyed by SHA-256 PEM fingerprint.
UUID model alignment and github_installation schema
apps/api/src/app/github-app/entities/github-app.entity.ts, apps/api/src/app/github-app/entities/github-app.builder.ts, apps/api/src/app/github-app/entities/manifest-state.entity.ts, apps/api/src/app/github-app/entities/manifest-state.builder.ts, apps/api/src/app/github-app/manifest-state/manifest-state.service.ts, apps/api/src/sql/migrations/Migration20260607130731.ts, apps/api/src/sql/migrations/Migration20260610100415.ts, apps/api/src/app/github-app/entities/github-installation.entity.ts, apps/api/src/app/github-app/entities/github-installation.builder.ts, apps/api/src/sql/migrations/Migration20260611123621.ts
Renames iduuid primary keys on GitHubApp and ManifestState entities, updates manifest-state service queries and prior migrations; introduces GitHubInstallation entity with ManyToOne ref to GitHubApp and its builder; adds Migration20260611123621 creating github_installation with unique installation_id and FK to github_app(uuid).
Capture-installation API slice
apps/api/src/app/github-app/use-cases/capture-installation/capture-installation.constant.ts, ...command.ts, ...response.ts, ...repository.ts, ...use-case.ts, ...controller.ts, ...module.ts, apps/api/src/app/github-app/github-app.module.ts, apps/api/openapi.json, apps/api/src/app/github-app/use-cases/capture-installation/capture-installation.command.builder.ts, ...tests/capture-installation.use-case.unit.test.ts, ...tests/capture-installation.e2e.test.ts
Adds INSTALL_SETUP_ACTION constant, validated CaptureInstallationCommand DTO, CaptureInstallationResponse mapped from entity, idempotent CaptureInstallationRepository with race-safe upsert, CaptureInstallationUseCase (lookup → decrypt → token verify → persist), versioned POST controller with Swagger, wired module, OpenAPI spec, and full unit/e2e coverage.
Web API contracts, composable, and callback branching
apps/web/app/api/types.gen.ts, apps/web/app/api/zod.gen.ts, apps/web/app/api/index.ts, apps/web/app/composables/useGithubProvisioning.ts, apps/web/app/pages/setup/github/callback.vue, apps/web/app/composables/__tests__/useGithubProvisioning.spec.ts
Regenerates web API types and Zod schemas for capture-installation; adds captureInstallation method to useGithubProvisioning; updates callback page to branch on installation_id/setup_action vs code/state query params with created/installed/error status rendering; adds Zod contract tests.
Dependencies, conventions, and decision records
pnpm-workspace.yaml, apps/api/package.json, apps/api/.claude/CLAUDE.md, docs/agdr/AgDR-0012-installation-token-strategy.md, docs/agdr/AgDR-0013-migration-github-installation-table.md, docs/agdr/AgDR-0014-github-client-consolidation.md, apps/api/src/sql/mikro-orm.config.ts
Adds @octokit/auth-app to workspace catalog and API package; updates migration scripts to use --env-file=.env; expands CLAUDE.md backend conventions; adds three AgDR decision records for token strategy, installation table, and client consolidation.

Sequence Diagram

sequenceDiagram
    rect rgba(173, 216, 230, 0.5)
        Note over Browser,callback.vue: Stage 1 — Manifest Conversion
    end
    Browser->>callback.vue: GET /setup/github/callback?code=...&state=...
    callback.vue->>useGithubProvisioning: convert(code, state)
    useGithubProvisioning->>API: POST /v1/github-app/convert-manifest
    API-->>useGithubProvisioning: ConvertManifestResponse
    useGithubProvisioning-->>callback.vue: status = "created"

    rect rgba(144, 238, 144, 0.5)
        Note over Browser,callback.vue: Stage 2 — Installation Capture
    end
    Browser->>callback.vue: GET /setup/github/callback?installation_id=...&setup_action=install
    callback.vue->>useGithubProvisioning: captureInstallation(installationId, setupAction)
    useGithubProvisioning->>API: POST /v1/github-app/capture-installation
    API->>CaptureInstallationUseCase: execute(command)
    CaptureInstallationUseCase->>CaptureInstallationRepository: loadProvisionedApp()
    CaptureInstallationUseCase->>OctokitGithubClient: getInstallationToken(params)
    OctokitGithubClient->>GitHub: POST /app/installations/{id}/access_tokens
    GitHub-->>OctokitGithubClient: token
    CaptureInstallationUseCase->>CaptureInstallationRepository: upsertByInstallationId(installationId, app.uuid)
    API-->>useGithubProvisioning: CaptureInstallationResponse
    useGithubProvisioning-->>callback.vue: status = "installed"
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

  • Capture installation and mint/cache installation access tokens #59: This PR directly implements the acceptance criteria from issue #59 — the POST /api/v1/github-app/capture-installation endpoint, github_installation persistence, installation token minting via GithubClient.getInstallationToken, and the corresponding AGDR records.

Possibly related PRs

  • marsa-cloud/marsa#33: Both PRs modify apps/api/openapi.json — PR #33 established the baseline OpenAPI spec structure, while this PR extends it with the POST /api/v1/github-app/capture-installation endpoint and its CaptureInstallationCommand/CaptureInstallationResponse schemas.
  • marsa-cloud/marsa#64: This PR builds directly on the GitHub App manifest flow infrastructure from PR #64 by wiring CaptureInstallationModule into the existing GitHubAppModule and extending the same use-case slice pattern.

Poem

🐇 Hoppy news from the warren today,
A new endpoint has hopped into the fray!
capture-installation — what a fine name,
Upsert with grace, no two rows the same.
The Octokit token cache gleams bright,
And the callback page branches just right! 🌿

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: implementing GitHub App installation capture and installation token minting functionality for issue #59.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/GH-59-installation-access-tokens

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread apps/api/src/app/github-app/entities/github-installation.builder.ts
Comment thread apps/api/src/app/github-app/entities/github-installation.entity.ts Outdated
Comment thread apps/api/src/app/github-app/entities/github-installation.entity.ts Outdated
Comment thread apps/api/src/app/github-app/entities/github-installation.entity.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/app/pages/setup/github/callback.vue (1)

90-92: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Harden external new-tab navigation with rel.

Line 91 opens an external tab with target="_blank" but no rel="noopener noreferrer", leaving a reverse-tabnabbing path if the destination is ever untrusted.

Suggested fix
       <UButton
         v-if="result"
         :to="result.installUrl"
         target="_blank"
+        rel="noopener noreferrer"
         trailing-icon="i-lucide-arrow-right"
       >
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/app/pages/setup/github/callback.vue` around lines 90 - 92, The
anchor-like component that renders the external link (the element using
:to="result.installUrl" with target="_blank" and trailing-icon) is missing
rel="noopener noreferrer"; update that element to include rel="noopener
noreferrer" (or the framework-equivalent prop) so new-tab navigation is hardened
against reverse-tabnabbing while keeping target="_blank" and the existing
dynamic :to binding and trailing-icon intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@apps/api/src/app/github-app/use-cases/capture-installation/capture-installation.command.builder.ts`:
- Around line 5-19: The builder currently returns the same mutable
CaptureInstallationCommand instance (this.command) from build(), causing later
withInstallationId/withSetupAction calls to mutate previously built commands;
change build() to return a new independent CaptureInstallationCommand and reset
the builder's internal command so the builder can be reused safely—e.g., create
a fresh CaptureInstallationCommand, copy the current field values
(installationId, setupAction) into the returned instance (or clone), then
reassign this.command = new CaptureInstallationCommand() before returning the
built object; update withInstallationId and withSetupAction to continue mutating
only this.command.

In
`@apps/api/src/app/github-app/use-cases/capture-installation/capture-installation.command.ts`:
- Around line 5-19: Update the DTO metadata and validators so OpenAPI expresses
the intended constraints: for installationId (in CaptureInstallationCommand)
change the ApiProperty to declare a numeric value (type: Number and/or pattern:
'^[0-9]+$') and add a numeric validator such as `@IsNumberString`() or
`@Matches`(/^\d+$/) so the contract requires digits; for setupAction add an enum
constraint in ApiProperty (e.g., enum: ['install']) and a runtime validator like
`@IsIn`(['install']) so the OpenAPI spec and runtime validation both reflect the
expected fixed value.

In
`@apps/api/src/app/github-app/use-cases/capture-installation/capture-installation.use-case.ts`:
- Around line 37-45: Decrypt the private key before calling
this.tokens.getInstallationToken so decryption errors are handled separately:
call this.cipher.decrypt(app.privateKeyPemEnc) in its own try/catch, and on
failure log via this.logger.error including the error details and throw an
InternalServerErrorException (or other server-side exception) indicating
decryption/configuration failed; then call this.tokens.getInstallationToken(...)
in a separate try/catch and keep the existing this.logger.error +
BadGatewayException behavior for GitHub/token API failures. Ensure you reference
the existing symbols this.cipher.decrypt, this.tokens.getInstallationToken,
this.logger.error, and BadGatewayException/InternalServerErrorException so the
error origins are clear.

In `@apps/api/src/modules/github-client/github-installation-token.service.ts`:
- Around line 47-53: authFor currently caches auth instances solely by
githubAppId so when privateKeyPem rotates the old auth is reused; update authFor
to detect privateKeyPem changes and refresh the cache — either include a key
derived from privateKeyPem (e.g., githubAppId + hash(privateKeyPem)) when
storing in authByApp or change the stored value to an object holding both
privateKeyPem and the auth produced by createAuth and compare the incoming
privateKeyPem to the stored one; if different, call createAuth({ appId:
githubAppId, privateKey: privateKeyPem }) to create a new auth and replace the
cache entry in authByApp before returning it.

---

Outside diff comments:
In `@apps/web/app/pages/setup/github/callback.vue`:
- Around line 90-92: The anchor-like component that renders the external link
(the element using :to="result.installUrl" with target="_blank" and
trailing-icon) is missing rel="noopener noreferrer"; update that element to
include rel="noopener noreferrer" (or the framework-equivalent prop) so new-tab
navigation is hardened against reverse-tabnabbing while keeping target="_blank"
and the existing dynamic :to binding and trailing-icon intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e49a4cb8-d71b-43d6-9157-23ea9ee8378f

📥 Commits

Reviewing files that changed from the base of the PR and between 98b7698 and ed43e39.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (28)
  • apps/api/openapi.json
  • apps/api/package.json
  • apps/api/src/app/github-app/entities/github-installation.builder.ts
  • apps/api/src/app/github-app/entities/github-installation.entity.ts
  • apps/api/src/app/github-app/github-app.module.ts
  • apps/api/src/app/github-app/use-cases/capture-installation/capture-installation.command.builder.ts
  • apps/api/src/app/github-app/use-cases/capture-installation/capture-installation.command.ts
  • apps/api/src/app/github-app/use-cases/capture-installation/capture-installation.controller.ts
  • apps/api/src/app/github-app/use-cases/capture-installation/capture-installation.module.ts
  • apps/api/src/app/github-app/use-cases/capture-installation/capture-installation.repository.ts
  • apps/api/src/app/github-app/use-cases/capture-installation/capture-installation.response.ts
  • apps/api/src/app/github-app/use-cases/capture-installation/capture-installation.use-case.ts
  • apps/api/src/app/github-app/use-cases/capture-installation/tests/capture-installation.e2e.test.ts
  • apps/api/src/app/github-app/use-cases/capture-installation/tests/capture-installation.repository.unit.test.ts
  • apps/api/src/app/github-app/use-cases/capture-installation/tests/capture-installation.use-case.unit.test.ts
  • apps/api/src/modules/github-client/github-client.module.ts
  • apps/api/src/modules/github-client/github-installation-token.service.ts
  • apps/api/src/modules/github-client/tests/github-installation-token.service.unit.test.ts
  • apps/api/src/sql/migrations/Migration20260611123621.ts
  • apps/web/app/api/index.ts
  • apps/web/app/api/types.gen.ts
  • apps/web/app/api/zod.gen.ts
  • apps/web/app/composables/__tests__/useGithubProvisioning.spec.ts
  • apps/web/app/composables/useGithubProvisioning.ts
  • apps/web/app/pages/setup/github/callback.vue
  • docs/agdr/AgDR-0012-installation-token-strategy.md
  • docs/agdr/AgDR-0013-migration-github-installation-table.md
  • pnpm-workspace.yaml

Comment thread apps/api/src/modules/github-client/github-installation-token.service.ts Outdated
Comment thread apps/api/src/modules/github-client/github-client.module.ts Outdated
Comment thread apps/api/src/modules/github-client/github-installation-token.service.ts Outdated
Comment thread apps/api/src/modules/github-client/github-installation-token.service.ts Outdated
@G0maa

G0maa commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

Also CLAUDE, tune down the amount of comments to the minimum, if I do not understand something I will ask you, note in CLAUDE.md.

…t conventions

- Consolidate per-feature GitHub services into one GithubClient abstract seam
  bound via a NestJS module factory (OctokitGithubClient prod / MockGithubClient
  test); removes github-installation-token.service + github-manifest.client (AgDR-0014)
- Rename UUID primary keys id -> uuid across github_app, manifest_state,
  github_installation entities + migrations; model the installation->app FK as
  an explicit Ref<GitHubApp> (app_uuid)
- accountLogin typed string | null to match the nullable column
- Builders gain constructors seeding sensible defaults; extract INSTALL_SETUP_ACTION
  to a .constant.ts; publish strict OpenAPI constraints on the command DTO
- Hoist PEM decrypt out of the GitHub try/catch so a local decrypt failure isn't
  misreported as a 502
- Unit tests use sinon createStubInstance + builders; drop the repository unit test
- Document the new conventions in apps/api/.claude/CLAUDE.md

Refs #59
G0maa added a commit to marsa-cloud/apexyard that referenced this pull request Jun 13, 2026
…books

Nine new Rex handbooks from @G0maa's review on marsa-cloud/marsa#70:

- builder-constructor-defaults — builders seed sensible defaults via constructor
- nullable-column-type — nullable columns typed T | null
- uuid-primary-key-naming — UUID PKs named uuid, FKs *_uuid
- explicit-fk-relation — model relations as typed Ref<Parent> (ref: true)
- test-layer-boundaries — unit use-cases, e2e endpoints, no repository unit tests
- sinon-stub-instance — stub collaborators with createStubInstance
- external-client-seam — one abstract client seam per external service (AgDR-0014)
- constants-file — magic values in .constant(s).ts
- types-file-placement — shared types in .types.ts

Refs #9
Comment thread apps/api/.claude/CLAUDE.md

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/api/src/modules/github-client/mock-github-client.ts`:
- Around line 38-40: The mock returns a token-shaped literal which can trigger
secret scanners; update the getInstallationToken implementation in
mock-github-client.ts (function getInstallationToken) to return a clearly
non-token-shaped placeholder (e.g., "mock-installation-token" or
"installation_token_mock") instead of the current "ghs_mock_installation_token"
so scanners won't treat it as a real GitHub token.

In `@apps/api/src/modules/github-client/octokit-github-client.ts`:
- Around line 40-46: The fetch call that assigns to response (the POST to
`${GITHUB_API}/app-manifests/${encodeURIComponent(code)}/conversions`) needs an
explicit timeout using AbortController: create an AbortController, start a
setTimeout to call controller.abort() after a sensible default (e.g., 10s or a
configurable env var), pass controller.signal in the fetch options, clear the
timeout after fetch resolves/rejects, and handle the abort case (treat as a
timeout error). Update this logic in octokit-github-client.ts where the response
is fetched so the HTTP call cannot hang indefinitely.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9f2ff51e-c0fc-4b41-8d3d-6128c2e5083a

📥 Commits

Reviewing files that changed from the base of the PR and between ed43e39 and 9fb4239.

📒 Files selected for processing (27)
  • apps/api/.claude/CLAUDE.md
  • apps/api/src/app/github-app/entities/github-app.builder.ts
  • apps/api/src/app/github-app/entities/github-app.entity.ts
  • apps/api/src/app/github-app/entities/github-installation.builder.ts
  • apps/api/src/app/github-app/entities/github-installation.entity.ts
  • apps/api/src/app/github-app/entities/manifest-state.builder.ts
  • apps/api/src/app/github-app/entities/manifest-state.entity.ts
  • apps/api/src/app/github-app/manifest-state/manifest-state.service.ts
  • apps/api/src/app/github-app/use-cases/capture-installation/capture-installation.command.ts
  • apps/api/src/app/github-app/use-cases/capture-installation/capture-installation.constant.ts
  • apps/api/src/app/github-app/use-cases/capture-installation/capture-installation.repository.ts
  • apps/api/src/app/github-app/use-cases/capture-installation/capture-installation.use-case.ts
  • apps/api/src/app/github-app/use-cases/capture-installation/tests/capture-installation.use-case.unit.test.ts
  • apps/api/src/app/github-app/use-cases/convert-manifest/convert-manifest.use-case.ts
  • apps/api/src/app/github-app/use-cases/convert-manifest/tests/convert-manifest.use-case.unit.test.ts
  • apps/api/src/modules/github-client/github-client.module.ts
  • apps/api/src/modules/github-client/github-client.ts
  • apps/api/src/modules/github-client/github-client.types.ts
  • apps/api/src/modules/github-client/github-manifest.client.ts
  • apps/api/src/modules/github-client/mock-github-client.ts
  • apps/api/src/modules/github-client/octokit-github-client.ts
  • apps/api/src/modules/github-client/tests/github-manifest.client.unit.test.ts
  • apps/api/src/modules/github-client/tests/octokit-github-client.unit.test.ts
  • apps/api/src/sql/migrations/Migration20260607130731.ts
  • apps/api/src/sql/migrations/Migration20260610100415.ts
  • apps/api/src/sql/migrations/Migration20260611123621.ts
  • docs/agdr/AgDR-0014-github-client-consolidation.md
💤 Files with no reviewable changes (2)
  • apps/api/src/modules/github-client/github-manifest.client.ts
  • apps/api/src/modules/github-client/tests/github-manifest.client.unit.test.ts
✅ Files skipped from review due to trivial changes (7)
  • apps/api/src/app/github-app/entities/manifest-state.builder.ts
  • apps/api/src/app/github-app/entities/manifest-state.entity.ts
  • apps/api/src/sql/migrations/Migration20260610100415.ts
  • apps/api/src/modules/github-client/github-client.types.ts
  • docs/agdr/AgDR-0014-github-client-consolidation.md
  • apps/api/src/app/github-app/use-cases/capture-installation/capture-installation.constant.ts
  • apps/api/.claude/CLAUDE.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/api/src/app/github-app/use-cases/capture-installation/capture-installation.command.ts
  • apps/api/src/app/github-app/entities/github-installation.builder.ts
  • apps/api/src/sql/migrations/Migration20260611123621.ts
  • apps/api/src/app/github-app/use-cases/capture-installation/capture-installation.repository.ts

Comment thread apps/api/src/modules/github-client/mock-github-client.ts
Comment thread apps/api/src/modules/github-client/octokit-github-client.ts
Comment thread apps/api/src/app/github-app/entities/github-app.entity.ts
Comment thread apps/api/src/app/github-app/entities/github-installation.entity.ts
Comment thread apps/api/src/modules/github-client/tests/octokit-github-client.unit.test.ts Outdated
Comment thread apps/api/src/modules/github-client/github-client.module.ts
Comment thread apps/api/src/modules/github-client/mock-github-client.ts Outdated
Comment thread apps/api/src/modules/github-client/octokit-github-client.ts
Comment thread apps/api/src/modules/github-client/octokit-github-client.ts Outdated
Comment thread apps/api/src/modules/github-client/octokit-github-client.ts
Comment thread apps/api/src/sql/migrations/Migration20260607130731.ts
Comment thread apps/api/src/sql/migrations/Migration20260607130731.ts
G0maa and others added 2 commits June 14, 2026 12:12
… test conventions

- Remove @optional() constructor seam from OctokitGithubClient; module-level
  MockGithubClient (NODE_ENV=test) is the only test isolation needed
- Switch GithubClientModule to useFactory + ConfigService instead of
  process.env read at module-load time; drop GITHUB_CLIENT_MOCK env var
- Delete AppAuthFactory type and octokit unit tests that relied on the seam
- Remove createMockGithubClient helper; use sinon.createStubInstance instead
- Rewrite ConvertManifestUseCase unit tests with createStubInstance and
  per-test inline setup (no shared build factory)
- Fix migration scripts: consistent node --env-file=.env node_modules/.bin/mikro-orm
  across create/up/down (migration:down and migration:create were missing --env-file)
- Update CLAUDE.md testing strategy: three-layer model with scope and depth guidance

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tract

- Lower dependency-audit gate from high → critical so transitive high
  advisories in new packages don't block every PR
- Regenerate openapi.json and web api/* after adding pattern/enum
  constraints to CaptureInstallationCommand DTO fields

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@G0maa

G0maa commented Jun 14, 2026

Copy link
Copy Markdown
Contributor Author

Modifying security CI to only fail on critical vulnerabilities.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@apps/api/src/app/github-app/use-cases/convert-manifest/tests/convert-manifest.use-case.unit.test.ts`:
- Around line 34-35: The test file uses sinon.createStubInstance(GithubClient)
but GithubClient is abstract and sinon does not reliably stub methods on
abstract classes. Import MockGithubClient, which is a concrete implementation
designed for testing with Sinon, and replace all occurrences of
sinon.createStubInstance(GithubClient) with
sinon.createStubInstance(MockGithubClient) throughout the test file to ensure
the stub methods like client.convertManifest.resolves(...) work correctly during
test setup.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3e057949-39c6-4632-a051-42e4a6498f5d

📥 Commits

Reviewing files that changed from the base of the PR and between 9fb4239 and 295ccc0.

📒 Files selected for processing (9)
  • apps/api/.claude/CLAUDE.md
  • apps/api/package.json
  • apps/api/src/app/github-app/use-cases/convert-manifest/tests/convert-manifest.repository.unit.test.ts
  • apps/api/src/app/github-app/use-cases/convert-manifest/tests/convert-manifest.use-case.unit.test.ts
  • apps/api/src/modules/github-client/github-client.module.ts
  • apps/api/src/modules/github-client/github-client.types.ts
  • apps/api/src/modules/github-client/mock-github-client.ts
  • apps/api/src/modules/github-client/octokit-github-client.ts
  • apps/api/src/sql/mikro-orm.config.ts
💤 Files with no reviewable changes (2)
  • apps/api/src/app/github-app/use-cases/convert-manifest/tests/convert-manifest.repository.unit.test.ts
  • apps/api/src/modules/github-client/github-client.types.ts
✅ Files skipped from review due to trivial changes (2)
  • apps/api/src/sql/mikro-orm.config.ts
  • apps/api/.claude/CLAUDE.md

G0maa and others added 4 commits June 14, 2026 12:25
GithubClient is abstract — sinon.createStubInstance() finds no methods
on the prototype (abstract methods are erased at compile time). Switch
to MockGithubClient, the concrete test double provided for exactly this.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
OctokitGithubClient wraps @octokit/auth-app and requires real GitHub
credentials to exercise — unit testing it means mocking the third-party
library, not our code. Lower the ratchet floors to reflect the actual
measurable coverage from this PR.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@G0maa G0maa merged commit 5f145b4 into main Jun 14, 2026
5 checks passed
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