ADRs
ADR 0023 — Deploy pipeline (project → connected repo)
  • Date: 2026-05-26
  • Status: Accepted
  • Affects:
    • apps/api/src/deploy.ts (new) — POST /api/v1/projects/:id/deploy
    • apps/api/src/index.ts — mount deployApp
    • apps/web/src/lib/api-client.tsdeployProject(id) function
    • apps/web/src/components/deploy-button.tsx (new) — UI button + modal
    • apps/web/src/app/app/workflow/page.tsx — embed DeployButton
  • References: ADR 0020 (Layout Grid), ADR 0021 (30K MAU ceiling), ADR 0022 (multi-env)

Context

После ADR 0022 multi-env setup project content застревал в Postgres — нет automated механизма push'а composition state в connected repo. Юзер хочет workflow:

  1. Edit composition в ARNO UI (Test environment)
  2. Click Deploy → composition serialized → committed в connected repo
  3. CI repo (CF Pages git integration / GitHub Actions) auto-deploys → environment updated

Это Phase 4 ADR 0020 в минимальной форме (без version history, без conflict detection, без multi-device sync).

Decision

Single endpoint POST /api/v1/projects/:id/deploy который:

  1. Verifies project ownership (existing requireOwnedProject)
  2. Loads connected_repo row (required — 400 если не connected)
  3. Loads all screen_composition rows для project
  4. Serialises каждый screen → pages/{screenId}.layout.json
  5. Atomic batch commit на connected_repo.default_branch через ARNO GitHub App
  6. Returns { commit: { sha, url, message }, branch, repo, screensDeployed }

Key design choices

1. Direct push на default_branch (no PR).

  • Trunk-based dev pattern (ADR 0021 §"trunk-based development")
  • Promotion test→prod = manual PR staging → main на GitHub
  • Future enhancement: optional ?openPR=true query для PR-based deploy

2. Atomic single commit для всех screens.

  • Tree built up-front, single createCommit call, single updateRef
  • Если любой blob upload падает — createTree не call'ится, commit не происходит
  • Юзер видит либо «all screens deployed» либо «failed, retry»

3. Empty repo / empty project safe.

  • Empty screens → создаётся pages/_arno_deploy.json marker (traceability)
  • Идемпотентность: если tree identical с base — noop: true, нет empty commit

4. Format: legacy instances JSONB shape.

  • Не zones model (ADR 0020 D6) пока
  • Зачем: совместимость с current backend API contract + localStorage migration ещё не финализирована
  • TODO: migrate format когда zones model становится canonical (Phase 4 full implementation)

5. Single source repo per project (connected_repo schema).

  • Один connected_repo row на project. Можно использовать один repo с двумя branches для двух projects (наш случай: arno-design main + staging для Arno-dev/Arno-test).
  • Branch determined by connected_repo.default_branch field (можно настраивать SQL update).

6. Auth через ARNO GitHub App installation token.

  • Reuses Phase 13 sync infrastructure (getInstallationToken)
  • Installation_id stored в connected_repo.installationId per project
  • 58-min token cache в KV (SYNC namespace)

7. Commit attribution.

  • Commit author = ARNO GitHub App bot
  • Commit message includes project name + timestamp + screen count
  • TODO: co-author = real user (требует GitHub username из user.login)

Rejected alternatives

AlternativeReason
Open PR вместо direct pushAdds friction для simple Deploy. PR pattern useful для production deploys (можно добавить opt-in). Trunk-based dev — direct push acceptable для staging.
One commit per screenPollutes git history. Atomic batch — cleaner diff.
Background job queue (Cloudflare Queues)Overkill для 30K MAU ceiling (ADR 0021). Sync response достаточна для < 50 screens per project.
Frontend builds TSX → pushCoupling: frontend знает internal model. Backend authoritative — single endpoint.
Use git-provider PR helpers вместо raw commit primitivesInit endpoint pattern (createBlob+Tree+Commit+updateRef) уже работает — reuse без extra abstraction.
Zones model serialization прямо сейчасFrontend localStorage migration не финализирована. Backend всё ещё receives legacy instances. Format upgrade — отдельная задача.
Multi-file commit через GitHub Contents API individual PUTsНе атомарно (between PUTs commit history fragmented). Tree-based — atomic.

Consequences

Positive

  • Один click → state в git → CI deploy. Closes "test → dev promotion" loop (ADR 0022).
  • Atomic commit — clear git history, easy to revert (single commit revert).
  • Reuses всю Phase 13 sync infra — no new auth / install flow.
  • Format pages/{screenId}.layout.json соответствует ADR 0020 §D6 future state.
  • Idempotent — repeated deploy без изменений = noop.

Negative

  • No conflict detection — если два юзера deploy одновременно, второй overrides первого (last-write-wins).
  • No history viewer в ARNO UI — юзер должен идти в GitHub для diff/rollback.
  • No rollback button — revert через GitHub UI (revert commit + PR merge).
  • No preview / dry-run — нельзя посмотреть что commit'нится до Deploy.

Mitigations / Future work

  • Conflict detection через If-Match eTag header (Phase 4 ADR 0020 spec) — добавить когда multi-user editing включится
  • History viewer как separate ADR (показывать git log в UI)
  • Rollback button = wrapper над GitHub revert API
  • Preview = dry-run mode ?dryRun=true returning diff без commit

Performance

  • ~50 screens per project = 50 blob uploads + 1 tree + 1 commit + 1 ref update = ~53 GitHub API calls
  • All blob uploads parallel (Promise.all) — ~1-2 sec total at p50
  • Worker CPU ms minimal (just JSON serialization)
  • Fits CF Workers Bundled (50ms CPU limit) easily

Cost (per ADR 0021 30K MAU ceiling)

  • GitHub API rate limit: 5K req/h per installation. 30K MAU × 1 deploy/day = ~1.25 req/s avg. Burst tolerable.
  • CF Workers: negligible (Deploy is rare operation, не каждый request).

Implementation status

  • Backend endpoint (apps/api/src/deploy.ts)
  • Mounted в apps/api/src/index.ts
  • Frontend API client (deployProject in api-client.ts)
  • UI button + modal (deploy-button.tsx + CSS)
  • Embedded в workflow page
  • E2E test (deferred until Playwright setup)
  • Conflict detection (deferred Phase 4 full impl)

Re-evaluation triggers

  • Multi-user editing включается → add conflict detection + locking
  • Cost spike → add rate limiting per project (e.g. max 1 deploy/minute)
  • PR workflow requested → add ?openPR=true query option
  • Format mismatch (когда clients стартуют writeить zones format в Postgres) → migrate JSON shape

References

  • ADR 0020 Phase 4 — GitHub serialization roadmap
  • ADR 0021 — 30K MAU ceiling determines tier choices (no queues, sync response OK)
  • ADR 0022 — multi-env setup defined trunk-based pattern
  • Master spec §I.3.2 — connected_repo, GitHub App, Phase 13 sync
  • apps/api/src/init.ts — referenced pattern для commit pipeline

Changelog

  • 2026-05-26 v1.0: Initial ADR. Backend + UI implementation done. Awaiting verify on test env.