Skip to content

Deploy pipeline tightening#1

Merged
Systemsaholic merged 43 commits intomainfrom
develop
Jan 7, 2026
Merged

Deploy pipeline tightening#1
Systemsaholic merged 43 commits intomainfrom
develop

Conversation

@Systemsaholic
Copy link
Copy Markdown
Owner

@Systemsaholic Systemsaholic commented Jan 7, 2026

Summary

  • ✅ Fixed Admin Vercel build chain: database → shared-types → admin
  • ✅ Added smoke tests to both Dev and Prod workflows with retry logic
  • ✅ Using Railway domains for reliable SSL in smoke tests

Changes

  • Updated buildCommand for Admin Vercel projects to build dependencies in order
  • Added smoke-test job to both workflows (10 retries, 10s apart)
  • Smoke tests use Railway domains directly for reliable SSL

Test plan

  • Dev workflow passes all jobs including smoke test
  • Prod workflow passes all jobs including smoke test
  • Verify Railway "Wait for CI" enabled in dashboard

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • API health endpoint and post-deploy smoke tests for deployment validation
    • Containerized production image and streamlined start/build scripts
  • Bug Fixes

    • Migrations guarded to run only in development or when explicitly enabled
  • Documentation

    • Extensive CI/CD, deployment, environment, and local development guides added
  • Chores

    • Updated ORM and migration tooling versions; improved deployment flows and ignore rules

✏️ Tip: You can customize this high-level summary in your review settings.

Systemsaholic and others added 30 commits January 6, 2026 17:16
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Bump drizzle-kit from ^0.24.2 to ^0.30.3 (fixes TypeError in column defaults)
- Remove invalid [project] and [edge_functions] sections from supabase config.toml
- Add .gitignore files for Vercel project configs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Upgrade drizzle-orm to latest for drizzle-kit 0.30.3 compatibility
- Update Supabase db.major_version to 17 (matches remote)
- Migration history repaired on remote (reverted orphan records)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Prevents interactive prompts that hang in CI environments.
strict: true in drizzle.config.ts requires confirmation for changes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
strict: true forces interactive confirmation even with --force flag.
Setting strict: false allows CI to run without prompts.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
drizzle-kit push is interactive and hangs in CI.
db:migrate uses runMigrations() from @tailfire/database
which properly applies migration files non-interactively.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@tailfire/database needs to be compiled before db:migrate can import it.
Added build step for the database package in both workflows.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Replace amondnet/vercel-action with direct Vercel CLI commands
- Use vercel pull → build → deploy --prebuilt pattern
- Add pnpm install and database build steps
- Set working-directory per app for correct project context
- Fixes "Unexpected error" from Vercel action

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add step to build @tailfire/database and @tailfire/shared-types
- Fixes "Module not found: @tailfire/shared-types/api" error

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update drizzle-orm from ^0.33.0 to ^0.45.1
- Fixes TypeScript build error due to version mismatch

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove prebuilt mode which causes symlink issues with pnpm monorepos
- Let Vercel handle the full build pipeline on their infrastructure

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Configure installCommand to run from monorepo root
- Configure buildCommand to build workspace packages first
- Simplify workflow to use Vercel's remote build

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Use Vercel REST API to set rootDirectory, installCommand, and framework
- Deploy from repo root so Vercel has access to all workspace packages
- Remove app-level vercel.json files (settings managed via API)
- Split into parallel jobs for each app deployment

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Configure build command, start command, and health check path
for Railway deployment in monorepo setup.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Configure Railpack build and deploy settings for monorepo.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Configure start command for Railpack monorepo deployment.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add start script to root package.json
- Add build:api script for filtered API build
- Update railpack.json with explicit build step

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Build @tailfire/shared-types and @tailfire/database before @tailfire/api
to ensure TypeScript types are available during API compilation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The object syntax { "type": "copy" } was being passed to shell instead
of being interpreted by Railpack. Removed it and added pwd/ls -la for
debugging.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Railpack custom steps don't copy files to build context, causing
"No package.json found" errors. Switching to multi-stage Dockerfile
for deterministic monorepo builds.

- Add root Dockerfile with multi-stage build
- Remove railpack.json, Procfile, and API-level configs
- Railway will auto-detect Dockerfile

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Systemsaholic and others added 12 commits January 6, 2026 22:40
Uses Railway config-as-code with proper workspace dependency build order.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add multi-stage Dockerfile for @tailfire/api
- Remove railway.toml (Dockerfile is more reliable for pnpm monorepos)
- Build order: database → shared-types → api
- Includes health check and production optimizations

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove BuildKit cache mounts that require Railway-specific prefix.
Simpler approach that works across all Docker builders.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The production runner stage was missing the migration files which
caused the app to fail on startup when running migrations.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Changed from packages/database/drizzle to packages/database/src/migrations
which is the actual location of Drizzle migration files.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The api-prod service expects this config file.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Dockerfile is at root, so railway.toml should be there too.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add retry-based health check (10 attempts × 10s) to deploy-dev.yml
- Add retry-based health check to deploy-prod.yml
- Smoke tests run after Vercel deploys complete
- Tests api-dev.tailfire.ca (dev) and api.tailfire.ca (prod)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Admin depends on @tailfire/shared-types/api subpath export.
Vercel needs to build shared-types first to generate dist/api/index.js.

Added buildCommand to Vercel project config that builds shared-types
before building Admin.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
shared-types depends on @tailfire/database, so the build order must be:
database → shared-types → admin

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Custom domain SSL certs may not be immediately provisioned.
Railway domains are guaranteed to work.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented Jan 7, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
tailfire-admin Ready Ready Preview, Comment Jan 7, 2026 6:04pm
tailfire-client Ready Ready Preview, Comment Jan 7, 2026 6:04pm
tailfire-ota Ready Ready Preview, Comment Jan 7, 2026 6:04pm

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 7, 2026

📝 Walkthrough

Walkthrough

Moves CI/CD from Vercel Actions to CLI/API-driven deployments, adds a NestJS health endpoint, guards migrations on startup, introduces database build/migrate steps, updates Docker/Railway config, upgrades drizzle packages, and expands documentation and vercel.json build configs for frontends.

Changes

Cohort / File(s) Summary
CI/CD Workflows
.github/workflows/deploy-dev.yml, .github/workflows/deploy-prod.yml
Replaced Vercel GitHub Action with Vercel CLI + API PATCH rootDirectory flow, added DB build & pnpm db:migrate, and added a smoke-test job that retries API health checks.
Docker & Platform Config
Dockerfile, railway.toml
Added multi-stage pnpm monorepo Dockerfile; configured Railway healthcheck path /api/v1/health, timeout 300s, and restart policy with retries.
API Health endpoint
apps/api/src/health/health.controller.ts, apps/api/src/health/health.module.ts, apps/api/src/health/index.ts, apps/api/src/app.module.ts
New HealthModule and HealthController exposing GET /health returning status and ISO timestamp; wired into AppModule.
Startup / Migrations
apps/api/src/main.ts, apps/api/drizzle.config.ts
Startup migrations now run only when NODE_ENV=development or RUN_MIGRATIONS_ON_STARTUP=true; drizzle.config strict set to false.
Dependencies & Scripts
apps/api/package.json, packages/database/package.json, packages/shared-types/package.json, package.json
Upgraded drizzle-orm and drizzle-kit versions; added dotenv and express to api deps; added root scripts build:api and start.
Vercel build configs
apps/admin/vercel.json, apps/client/vercel.json, apps/ota/vercel.json
Added per-app vercel.json files specifying install/build commands and framework for Next.js deployments.
Ignore rules
.gitignore, apps/admin/.gitignore, apps/client/.gitignore, apps/ota/.gitignore
Added .vercel ignore entries and apps/ota/supabase/.temp/ to top-level .gitignore.
OTA Supabase config
apps/ota/supabase/config.toml
Removed [project] header, bumped DB major_version 15→17, updated auth site_url and redirect URLs to use port 3102, removed [edge_functions].
Documentation
README.md, docs/CI_CD.md, docs/DEPLOYMENT_API.md, docs/ENVIRONMENTS.md, docs/LOCAL_DEV.md
Large README expansion and four new docs covering CI/CD, Railway API deployment, environments, and local development workflows.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant GH as GitHub Actions
    participant CLI as Vercel CLI (runner)
    participant VAPI as Vercel API
    participant Registry as PNPM/Monorepo build
    participant DB as Database migrations (drizzle / packages)
    participant API as Tailfire API (Railway)
    participant Smoke as Smoke-test job

    rect rgb(240,248,255)
    Note over GH,CLI: Deploy job starts
    end

    GH->>Registry: pnpm install & build (`@database`, shared-types, api)
    Registry-->>GH: build artifacts
    GH->>VAPI: PATCH project rootDirectory for app (apps/admin|ota|client)
    VAPI-->>GH: 200 OK
    GH->>CLI: vercel deploy --prebuilt/--confirm (uses rootDirectory)
    CLI->>VAPI: create preview/prod deployment
    VAPI-->>CLI: deployment URL
    CLI-->>GH: deployment result (URL)
    GH->>DB: pnpm --filter `@tailfire/database` build && pnpm db:migrate
    DB-->>GH: migrations applied

    rect rgb(240,255,240)
    GH->>API: trigger smoke-test (wait for all deployments)
    API-->>GH: /health => {status:ok}
    end

    Note right of Smoke: retries on failure (backoff)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped through CI with a CLI cheer,
Patched roots and built the DB near.
Health pings echo, Docker hums along,
Docs and configs sing a deploy-song.
Carrots of code — small hops, big cheer! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Deploy pipeline tightening' directly relates to the main changes in the PR: refactored deployment workflows with improved build orchestration, added smoke tests, and configured Vercel deployments.

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


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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 12

🤖 Fix all issues with AI agents
In @.github/workflows/deploy-dev.yml:
- Around line 83-94: The "Configure Vercel project for monorepo" step currently
uses `curl -s` which hides failures; modify the `curl` invocation used in that
step (the PATCH to api.vercel.com) to fail the workflow on HTTP errors (e.g.,
use `--fail-with-body` or remove `-s` and inspect the HTTP status), capture and
echo the response body for logs, and exit non‑zero if the response indicates an
error so the workflow does not continue with a stale configuration.
- Around line 142-159: The smoke-test job is using a hardcoded Railway URL and
can race with the API deployment; in the smoke-test job (step "Health check API
(retry)") replace the literal https://tailfire-api-dev-dev.up.railway.app with a
repository variable/secret like ${{ vars.DEV_API_URL }} or ${{
secrets.DEV_API_URL }} and update the job's needs to include the API deployment
job (or add an explicit wait step that polls Railway/Deployment API for
readiness) and/or increase retries/delay to avoid the race; ensure the
health-check loop reads the URL from the variable and that the workflow
dependency or polling targets the actual API deployment rather than assuming DNS
propagation.

In @.github/workflows/deploy-prod.yml:
- Around line 147-164: The smoke-test job currently hardcodes the Railway URL in
the "Health check API (retry)" step (the curl target
https://api-prod-production-c75c.up.railway.app/api/v1/health), which is
fragile; change that step to read the base API host from a repository
variable/secret (e.g., API_BASE_URL or PROD_API_URL) or use the custom domain
(api.tailfire.ca) and then curl ${API_BASE_URL}/api/v1/health (or construct the
health path from the variable) so the URL can be changed without editing the
workflow.

In @apps/api/drizzle.config.ts:
- Line 14: The config currently sets strict: false which disables
interactive/guard checks; add explicit production safeguards instead of relying
on separate CI secrets by (1) preventing automatic migrations when
PROD_DATABASE_URL is present unless an explicit env flag (e.g.,
ALLOW_PROD_MIGRATIONS) is set and confirmed, (2) adding pre-migration hooks that
create automated backups using your DB backup tooling and verifying backup
success before running migrations, (3) gating production deploys/migrations
behind a manual approval step in CI or requiring a signed-off variable to
proceed, and (4) adding documented and tested rollback procedures invoked by
migration failure handlers; locate the strict setting in drizzle.config.ts and
implement these checks/hooks around the migration invocation points that read
PROD_DATABASE_URL / DEV_DATABASE_URL.

In @apps/api/package.json:
- Line 84: Upgrade to drizzle-kit ^0.30.3 introduces breaking changes: run `npx
drizzle-kit up` to convert existing migrations to the new folder format
(journal.json removed), audit all migration files and any DDL SQL generated by
your codebase for removed IF NOT EXISTS usage and adjust or guard against
duplicate-object errors, search for any code expecting postgres.js to return
Date objects (e.g., places using Date methods on query results) and convert to
parsing ISO strings into Date where needed, find and update any automation or
npm scripts (e.g., scripts invoking `drizzle-kit drop`) to remove or replace the
removed CLI commands, and then thoroughly test db:generate and db:migrate flows
in a non-production environment (run db:generate, db:migrate, and drizzle-kit up
first) before deploying.

In @apps/api/src/main.ts:
- Around line 16-33: The migration gating uses process.env.NODE_ENV without a
safe fallback, causing migrations to be skipped if NODE_ENV is unset; update the
startup logic around shouldRunMigrations to either default NODE_ENV to
'development' when undefined or enforce/validate that NODE_ENV is explicitly set
(and fail early with a clear message), e.g. adjust the checks using
process.env.NODE_ENV and RUN_MIGRATIONS_ON_STARTUP and add a startup validation
that logs an error and exits if NODE_ENV is missing; reference the
shouldRunMigrations variable and the runMigrations(databaseUrl) call so the
change ensures migrations run in local dev or the process fails fast and informs
the developer to set NODE_ENV or add a provided .env.example.

In @apps/ota/supabase/config.toml:
- Line 14: The config entry major_version = 17 was changed but you must verify
PostgreSQL 17 compatibility before flipping production: run a staged pg_upgrade
or pg_dump/pg_restore, test app codepaths that use JSON, unqualified object
names and PL/pgSQL cursors, confirm extensions and index/REINDEX behavior, and
if your hosted Supabase remains on PG15 keep local parity by creating
supabase/.temp/postgres-version containing "15" to force the Supabase CLI to use
v15; alternatively, once all tests pass and extensions are confirmed, keep
major_version = 17.

In @docs/ENVIRONMENTS.md:
- Around line 44-49: The fenced code block showing localhost URLs lacks a
language identifier; update the block delimiter from ``` to ```bash so the
snippet in docs/ENVIRONMENTS.md (the four-line block containing
"http://localhost:3100" through "http://localhost:3103") has a language tag for
proper linting and syntax highlighting.

In @docs/LOCAL_DEV.md:
- Line 162: Update the Markdown heading that currently reads "### Frontend Apps
(`apps/*/\env.local`)" to remove the unnecessary escape and correct the filename
to "### Frontend Apps (`apps/*/.env.local`)" so the heading accurately shows the
.env.local pattern; search for the exact string "`apps/*/\\env.local`" or the
heading "### Frontend Apps" to locate and replace it.
- Around line 18-21: The quick start docs reference missing template files;
either add .env.example templates for apps/api, apps/admin, apps/ota, and
apps/client (e.g., create apps/api/.env.example, apps/admin/.env.example,
apps/ota/.env.example, apps/client/.env.example with the required sample vars)
or update LOCAL_DEV.md to copy the correct existing files (e.g., change
apps/admin/.env.example -> apps/admin/.env.local.example or point to the actual
env template names) and adjust the cp commands in the snippet so the listed
filenames match the repository; ensure the doc commands and the repo templates
use the same filenames (referenced symbols: LOCAL_DEV.md snippet and the files
apps/api/.env.example, apps/admin/.env.example, apps/ota/.env.example,
apps/client/.env.example).

In @packages/database/package.json:
- Line 26: The project dependency was bumped to drizzle-kit 0.30.3 which
contains breaking changes from 0.24.0; review and update migrations and code to
be compatible: audit migration files and run drizzle-kit up/upgrade to migrate
folder/file structure, re-generate and test migrations to catch removed IF NOT
EXISTS behavior in PostgreSQL DDL, update any schemaFilter usage to the new
glob/schemas semantics, and replace deprecated APIs (e.g., migrate usages of
.enableRLS() to pgTable.withRLS(...)) across code and CI/CD migration/push
workflows before merging.
🧹 Nitpick comments (6)
railway.toml (1)

6-6: Consider whether 300s health check timeout is necessary.

The 5-minute health check timeout is quite generous. If your API typically starts faster, consider reducing this to fail faster on genuine issues. However, if database migrations or warm-up processes justify this duration, the current setting is appropriate.

apps/api/package.json (2)

56-56: Consider removing the redundant dotenv dependency.

NestJS applications using @nestjs/config (line 37) don't need an explicit dotenv dependency—@nestjs/config handles environment variable loading internally. Adding dotenv creates redundancy and potential version conflicts.

Similarly, @types/dotenv (line 78) becomes unnecessary if dotenv is removed.

♻️ Remove redundant dotenv dependencies
-    "dotenv": "^16.6.1",
     "drizzle-orm": "^0.45.1",
     "@types/express": "^5.0.0",
-    "@types/dotenv": "^8.2.3",
     "@types/jest": "^29.5.14",

Also applies to: 78-78


58-58: Note: express is already a transitive dependency.

The express package is already provided transitively through @nestjs/platform-express (line 43). While explicitly declaring it in dependencies doesn't cause issues, it may be unnecessary unless you're importing Express types or utilities directly outside the NestJS context.

Dockerfile (1)

77-79: Improve the healthcheck implementation with better error handling.

The inline Node.js HTTP call correctly targets /api/v1/health (verified: global prefix is api/v1 + controller path health). However, it lacks error handling for network failures and timeouts, which means the healthcheck could hang or exit silently on errors.

♻️ Proposed refactor using a dedicated healthcheck script

Create a healthcheck script at apps/api/healthcheck.js:

const http = require('http');

const options = {
  host: 'localhost',
  port: 3101,
  path: '/api/v1/health',
  timeout: 2000,
};

const request = http.request(options, (res) => {
  console.log(`Health check status: ${res.statusCode}`);
  process.exit(res.statusCode === 200 ? 0 : 1);
});

request.on('error', (err) => {
  console.error('Health check failed:', err.message);
  process.exit(1);
});

request.on('timeout', () => {
  console.error('Health check timed out');
  request.destroy();
  process.exit(1);
});

request.end();

Then update the Dockerfile:

+# Copy healthcheck script
+COPY --from=builder /app/apps/api/healthcheck.js ./apps/api/

 # Health check
 HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
-  CMD node -e "require('http').get('http://localhost:3101/api/v1/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1))"
+  CMD node apps/api/healthcheck.js
docs/CI_CD.md (1)

18-53: Consider adding a language identifier to the ASCII diagram code block.

The static analysis tool flagged this fenced code block as missing a language specifier. For ASCII diagrams, you can use text or plaintext to silence the linter while preserving formatting.

📝 Suggested fix
-```
+```text
 ┌─────────────────────────────────────────────────────────────────┐
 │                        deploy-dev.yml                           │
README.md (1)

7-21: Add language identifier to the directory structure code block.

The static analysis tool flagged this fenced code block as missing a language specifier. Use text or plaintext for directory trees.

📝 Suggested fix
-```
+```text
 tailfire/
 ├── apps/
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b9a3acd and aa483f6.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (26)
  • .github/workflows/deploy-dev.yml
  • .github/workflows/deploy-prod.yml
  • .gitignore
  • Dockerfile
  • README.md
  • apps/admin/.gitignore
  • apps/api/drizzle.config.ts
  • apps/api/package.json
  • apps/api/src/app.module.ts
  • apps/api/src/health/health.controller.ts
  • apps/api/src/health/health.module.ts
  • apps/api/src/health/index.ts
  • apps/api/src/main.ts
  • apps/client/.gitignore
  • apps/ota/.gitignore
  • apps/ota/supabase/config.toml
  • docs/CI_CD.md
  • docs/DEPLOYMENT_API.md
  • docs/ENVIRONMENTS.md
  • docs/LOCAL_DEV.md
  • package.json
  • packages/database/package.json
  • packages/database/tsconfig.tsbuildinfo
  • packages/shared-types/package.json
  • packages/shared-types/tsconfig.tsbuildinfo
  • railway.toml
🧰 Additional context used
🧬 Code graph analysis (1)
apps/api/src/health/health.module.ts (1)
apps/api/src/app.module.ts (1)
  • Module (32-115)
🪛 LanguageTool
docs/DEPLOYMENT_API.md

[style] ~149-~149: To form a complete sentence, be sure to include a subject.
Context: ...Behavior - RUN_MIGRATIONS_ON_STARTUP should be false (or unset) - Migrations run ...

(MISSING_IT_THERE)

🪛 markdownlint-cli2 (0.18.1)
docs/CI_CD.md

18-18: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

docs/ENVIRONMENTS.md

44-44: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

README.md

7-7: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (24)
apps/client/.gitignore (1)

1-1: Standard best practice for Vercel CLI workflows.

Adding .vercel to gitignore is the correct approach, as it contains local Vercel CLI configuration that should not be committed to version control.

apps/admin/.gitignore (1)

1-1: Standard best practice for Vercel CLI workflows.

Adding .vercel to gitignore is the correct approach, as it contains local Vercel CLI configuration that should not be committed to version control.

apps/ota/.gitignore (1)

1-1: Standard best practice for Vercel CLI workflows.

Adding .vercel to gitignore is the correct approach, as it contains local Vercel CLI configuration that should not be committed to version control.

.gitignore (1)

10-11: LGTM!

Both additions align with best practices:

  • Line 10: *.tsbuildinfo correctly ignores TypeScript incremental build cache files—a standard practice for any TypeScript project using tsc's incremental mode.
  • Line 11: apps/ota/supabase/.temp/ appropriately ignores Supabase CLI temporary artifacts specific to the OTA app.

The patterns are consistent with the existing ignore rules (e.g., .turbo, dist, build) and support the deployment infrastructure changes in this PR.

packages/shared-types/package.json (1)

39-39: LGTM! Drizzle version is consistent with database package.

The drizzle-orm version update to ^0.45.1 aligns with the same update in packages/database/package.json. As a devDependency, the impact is limited to development and testing.

packages/database/package.json (1)

21-21: Test compatibility with drizzle-orm ^0.45.1 before deployment.

This version jump (11 minor versions) introduces breaking changes relevant to this codebase:

  • jsonb/postgres-js behavior: Raw JSON values now returned instead of stringified. Affects: user-profiles, trips, trip-media, suppliers, lodging-details, dining-details, custom-cruise-details, cruise-ships, and other jsonb columns.
  • Timestamp/date mapping: PostgreSQL driver behavior aligned; verify date column handling throughout schema.
  • Error handling: DrizzleQueryError wrapper introduced (v0.44); audit error handling in client.ts, seed scripts, and migrations.
  • Indexes API changes (v0.31): Review index definitions in itinerary-templates, tags, and activity-documents schemas.

Validate migrations in packages/database/src/migrations/ and test the cruise-data smoke test (__tests__/cruise-data-smoke.test.ts) against the new version.

railway.toml (1)

1-8: LGTM! Dockerfile and health endpoint verified.

The Railway configuration is properly implemented. The Dockerfile exists at the repository root with a multi-stage pnpm build, and the /api/v1/health endpoint is implemented in apps/api/src/health/health.controller.ts as a public NestJS route that returns status information. The health check timeout, restart policy, and retry limits are appropriately configured for production deployment.

apps/ota/supabase/config.toml (1)

30-33: Port changes align with the documented port scheme.

The update from port 3000 to 3102 for the OTA app correctly implements the port assignment strategy documented in docs/LOCAL_DEV.md (lines 42, 49), which reserves the 31xx range for all Tailfire services.

apps/api/src/health/health.controller.ts (1)

1-14: Clean health check implementation.

The health endpoint implementation is straightforward and appropriate:

  • The @Public() decorator correctly exempts the endpoint from authentication (necessary for automated health checks)
  • Returns a simple, parseable response with status and timestamp
  • Integrates well with the CI/CD smoke test workflows mentioned in the PR objectives
apps/api/src/health/index.ts (1)

1-2: Standard barrel export pattern.

The barrel export structure follows NestJS conventions for module organization.

docs/LOCAL_DEV.md (1)

1-302: Comprehensive and well-structured local development guide.

This documentation provides excellent coverage of:

  • Clear prerequisites and quick start instructions
  • Well-reasoned port assignment strategy aligned with configuration changes
  • Thorough database command reference with safety guardrails
  • Practical troubleshooting scenarios
  • Proper cross-references to related documentation

The guide will significantly improve developer onboarding and day-to-day workflows.

Dockerfile (2)

1-27: LGTM! Well-structured dependency installation.

The base and deps stages follow Docker best practices with proper layer caching (copying package.json files before full source) and reproducible builds (frozen lockfile).


43-46: LGTM! Build order correctly enforced.

The explicit dependency order (database → shared-types → api) aligns with the PR objectives and prevents build race conditions in the monorepo.

docs/ENVIRONMENTS.md (3)

1-34: LGTM! Clear domain mapping documentation.

The domain structure and port assignments are well-organized and provide clear guidance for each environment.


86-125: LGTM! Comprehensive environment variable documentation.

The environment variable matrix provides clear guidance across all environments and apps, with proper defaults documented.


126-168: LGTM! Clear Supabase configuration documentation.

The Supabase project configuration and auth URL setup are well-documented with appropriate environment-specific details.

apps/api/src/app.module.ts (1)

30-30: LGTM! Health module properly integrated.

The HealthModule is correctly imported and registered with a clear comment explaining its purpose for Railway monitoring.

Also applies to: 96-98

apps/api/src/health/health.module.ts (1)

1-7: LGTM! Standard NestJS module implementation.

The HealthModule follows NestJS conventions and correctly registers the HealthController.

docs/CI_CD.md (1)

151-172: Missing smoke-test job in the workflow jobs summary tables.

The PR adds a smoke-test job to both deploy-dev.yml and deploy-prod.yml workflows, but these tables don't include it. Consider adding:

Job Purpose Dependencies
smoke-test Verify API health post-deployment deploy-admin, deploy-ota, deploy-client
docs/DEPLOYMENT_API.md (1)

1-347: Comprehensive and well-structured deployment documentation.

The documentation covers all essential aspects of Railway deployment including build strategies, health checks, environment configuration, migration guards, and troubleshooting. The environment variable template with clear placeholders is helpful for onboarding.

.github/workflows/deploy-dev.yml (1)

33-40: Good enforcement of build order for migrations.

Building the database package before running migrations ensures the Drizzle schema is compiled and available. This correctly addresses the dependency ordering issue.

README.md (1)

23-100: Well-structured README with comprehensive documentation.

The expanded README provides clear quick start instructions, documentation links, deployment information, and tech stack details. The tables are well-organized and the documentation hierarchy is logical.

.github/workflows/deploy-prod.yml (2)

33-40: Correct build order enforcement for production migrations.

Consistent with the dev workflow, the database package is built before running migrations, ensuring proper dependency ordering.


88-122: > Likely an incorrect or invalid review comment.

Comment thread .github/workflows/deploy-dev.yml Outdated
Comment on lines +83 to +94
- name: Configure Vercel project for monorepo
run: |
curl -s -X PATCH "https://api.vercel.com/v9/projects/${{ secrets.VERCEL_ADMIN_PROJECT_ID }}" \
-H "Authorization: Bearer ${{ secrets.VERCEL_TOKEN }}" \
-H "Content-Type: application/json" \
-d '{"rootDirectory":"apps/admin","installCommand":"pnpm install","buildCommand":"pnpm --filter @tailfire/database build && pnpm --filter @tailfire/shared-types build && pnpm --filter @tailfire/admin build","framework":"nextjs"}'

- name: Deploy Admin (Preview)
run: vercel deploy --token=${{ secrets.VERCEL_TOKEN }}
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_ADMIN_PROJECT_ID }}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Vercel API calls may silently fail; consider adding error handling.

The curl -s flag suppresses error output. If the API call fails (rate limit, auth issue, etc.), the workflow continues with potentially stale project configuration. Consider using --fail-with-body or checking the response.

🔧 Suggested improvement
       - name: Configure Vercel project for monorepo
         run: |
-          curl -s -X PATCH "https://api.vercel.com/v9/projects/${{ secrets.VERCEL_ADMIN_PROJECT_ID }}" \
+          response=$(curl -s -w "\n%{http_code}" -X PATCH "https://api.vercel.com/v9/projects/${{ secrets.VERCEL_ADMIN_PROJECT_ID }}" \
             -H "Authorization: Bearer ${{ secrets.VERCEL_TOKEN }}" \
             -H "Content-Type: application/json" \
-            -d '{"rootDirectory":"apps/admin","installCommand":"pnpm install","buildCommand":"pnpm --filter @tailfire/database build && pnpm --filter @tailfire/shared-types build && pnpm --filter @tailfire/admin build","framework":"nextjs"}'
+            -d '{"rootDirectory":"apps/admin","installCommand":"pnpm install","buildCommand":"pnpm --filter @tailfire/database build && pnpm --filter @tailfire/shared-types build && pnpm --filter @tailfire/admin build","framework":"nextjs"}')
+          http_code=$(echo "$response" | tail -n1)
+          if [ "$http_code" -ge 400 ]; then
+            echo "Vercel API error: $response"
+            exit 1
+          fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Configure Vercel project for monorepo
run: |
curl -s -X PATCH "https://api.vercel.com/v9/projects/${{ secrets.VERCEL_ADMIN_PROJECT_ID }}" \
-H "Authorization: Bearer ${{ secrets.VERCEL_TOKEN }}" \
-H "Content-Type: application/json" \
-d '{"rootDirectory":"apps/admin","installCommand":"pnpm install","buildCommand":"pnpm --filter @tailfire/database build && pnpm --filter @tailfire/shared-types build && pnpm --filter @tailfire/admin build","framework":"nextjs"}'
- name: Deploy Admin (Preview)
run: vercel deploy --token=${{ secrets.VERCEL_TOKEN }}
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_ADMIN_PROJECT_ID }}
- name: Configure Vercel project for monorepo
run: |
response=$(curl -s -w "\n%{http_code}" -X PATCH "https://api.vercel.com/v9/projects/${{ secrets.VERCEL_ADMIN_PROJECT_ID }}" \
-H "Authorization: Bearer ${{ secrets.VERCEL_TOKEN }}" \
-H "Content-Type: application/json" \
-d '{"rootDirectory":"apps/admin","installCommand":"pnpm install","buildCommand":"pnpm --filter @tailfire/database build && pnpm --filter @tailfire/shared-types build && pnpm --filter @tailfire/admin build","framework":"nextjs"}')
http_code=$(echo "$response" | tail -n1)
if [ "$http_code" -ge 400 ]; then
echo "Vercel API error: $response"
exit 1
fi
- name: Deploy Admin (Preview)
run: vercel deploy --token=${{ secrets.VERCEL_TOKEN }}
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_ADMIN_PROJECT_ID }}
🤖 Prompt for AI Agents
In @.github/workflows/deploy-dev.yml around lines 83 - 94, The "Configure Vercel
project for monorepo" step currently uses `curl -s` which hides failures; modify
the `curl` invocation used in that step (the PATCH to api.vercel.com) to fail
the workflow on HTTP errors (e.g., use `--fail-with-body` or remove `-s` and
inspect the HTTP status), capture and echo the response body for logs, and exit
non‑zero if the response indicates an error so the workflow does not continue
with a stale configuration.

Comment on lines +142 to +159
smoke-test:
name: Smoke Test API
runs-on: ubuntu-latest
needs: [deploy-admin, deploy-ota, deploy-client]
steps:
- name: Health check API (retry)
run: |
# Using Railway domain directly (SSL cert for custom domain pending)
for i in {1..10}; do
if curl -sf https://tailfire-api-dev-dev.up.railway.app/api/v1/health; then
echo "✅ API health check passed"
exit 0
fi
echo "Attempt $i failed, retrying in 10s..."
sleep 10
done
echo "❌ API health check failed after 10 attempts"
exit 1
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Hardcoded Railway URL and potential timing issue.

Two concerns:

  1. Hardcoded URL: The Railway domain is hardcoded. Consider using a repository variable or secret (e.g., ${{ vars.DEV_API_URL }}) for maintainability.

  2. Race condition: The smoke test depends on frontend deployments, but the API is deployed separately by Railway. If Railway deployment is slow or "Wait for CI" is misconfigured, the smoke test might hit a stale API version.

🔧 Suggested improvement
       - name: Health check API (retry)
         run: |
-          # Using Railway domain directly (SSL cert for custom domain pending)
+          API_URL="${{ vars.DEV_API_URL || 'https://tailfire-api-dev-dev.up.railway.app' }}"
           for i in {1..10}; do
-            if curl -sf https://tailfire-api-dev-dev.up.railway.app/api/v1/health; then
+            if curl -sf "${API_URL}/api/v1/health"; then
               echo "✅ API health check passed"
               exit 0
             fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
smoke-test:
name: Smoke Test API
runs-on: ubuntu-latest
needs: [deploy-admin, deploy-ota, deploy-client]
steps:
- name: Health check API (retry)
run: |
# Using Railway domain directly (SSL cert for custom domain pending)
for i in {1..10}; do
if curl -sf https://tailfire-api-dev-dev.up.railway.app/api/v1/health; then
echo "✅ API health check passed"
exit 0
fi
echo "Attempt $i failed, retrying in 10s..."
sleep 10
done
echo "❌ API health check failed after 10 attempts"
exit 1
smoke-test:
name: Smoke Test API
runs-on: ubuntu-latest
needs: [deploy-admin, deploy-ota, deploy-client]
steps:
- name: Health check API (retry)
run: |
API_URL="${{ vars.DEV_API_URL || 'https://tailfire-api-dev-dev.up.railway.app' }}"
for i in {1..10}; do
if curl -sf "${API_URL}/api/v1/health"; then
echo "✅ API health check passed"
exit 0
fi
echo "Attempt $i failed, retrying in 10s..."
sleep 10
done
echo "❌ API health check failed after 10 attempts"
exit 1
🤖 Prompt for AI Agents
In @.github/workflows/deploy-dev.yml around lines 142 - 159, The smoke-test job
is using a hardcoded Railway URL and can race with the API deployment; in the
smoke-test job (step "Health check API (retry)") replace the literal
https://tailfire-api-dev-dev.up.railway.app with a repository variable/secret
like ${{ vars.DEV_API_URL }} or ${{ secrets.DEV_API_URL }} and update the job's
needs to include the API deployment job (or add an explicit wait step that polls
Railway/Deployment API for readiness) and/or increase retries/delay to avoid the
race; ensure the health-check loop reads the URL from the variable and that the
workflow dependency or polling targets the actual API deployment rather than
assuming DNS propagation.

Comment on lines +147 to +164
smoke-test:
name: Smoke Test API
runs-on: ubuntu-latest
needs: [deploy-admin, deploy-ota, deploy-client]
steps:
- name: Health check API (retry)
run: |
# Using Railway domain directly for reliable SSL
for i in {1..10}; do
if curl -sf https://api-prod-production-c75c.up.railway.app/api/v1/health; then
echo "✅ API health check passed"
exit 0
fi
echo "Attempt $i failed, retrying in 10s..."
sleep 10
done
echo "❌ API health check failed after 10 attempts"
exit 1
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Hardcoded production Railway URL is fragile.

The production smoke test uses a hardcoded Railway-generated URL (api-prod-production-c75c.up.railway.app). This URL may change if the Railway service is recreated. Consider:

  1. Using a repository secret/variable for the URL
  2. Using the custom domain (api.tailfire.ca) once SSL is configured
🔧 Suggested improvement
       - name: Health check API (retry)
         run: |
-          # Using Railway domain directly for reliable SSL
+          API_URL="${{ vars.PROD_API_URL || 'https://api-prod-production-c75c.up.railway.app' }}"
           for i in {1..10}; do
-            if curl -sf https://api-prod-production-c75c.up.railway.app/api/v1/health; then
+            if curl -sf "${API_URL}/api/v1/health"; then
               echo "✅ API health check passed"
               exit 0
             fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
smoke-test:
name: Smoke Test API
runs-on: ubuntu-latest
needs: [deploy-admin, deploy-ota, deploy-client]
steps:
- name: Health check API (retry)
run: |
# Using Railway domain directly for reliable SSL
for i in {1..10}; do
if curl -sf https://api-prod-production-c75c.up.railway.app/api/v1/health; then
echo "✅ API health check passed"
exit 0
fi
echo "Attempt $i failed, retrying in 10s..."
sleep 10
done
echo "❌ API health check failed after 10 attempts"
exit 1
smoke-test:
name: Smoke Test API
runs-on: ubuntu-latest
needs: [deploy-admin, deploy-ota, deploy-client]
steps:
- name: Health check API (retry)
run: |
API_URL="${{ vars.PROD_API_URL || 'https://api-prod-production-c75c.up.railway.app' }}"
for i in {1..10}; do
if curl -sf "${API_URL}/api/v1/health"; then
echo "✅ API health check passed"
exit 0
fi
echo "Attempt $i failed, retrying in 10s..."
sleep 10
done
echo "❌ API health check failed after 10 attempts"
exit 1
🤖 Prompt for AI Agents
In @.github/workflows/deploy-prod.yml around lines 147 - 164, The smoke-test job
currently hardcodes the Railway URL in the "Health check API (retry)" step (the
curl target https://api-prod-production-c75c.up.railway.app/api/v1/health),
which is fragile; change that step to read the base API host from a repository
variable/secret (e.g., API_BASE_URL or PROD_API_URL) or use the custom domain
(api.tailfire.ca) and then curl ${API_BASE_URL}/api/v1/health (or construct the
health path from the variable) so the URL can be changed without editing the
workflow.

},
verbose: true,
strict: true,
strict: false, // CI requires non-interactive mode; --force handles data loss
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check for migration safeguards in CI workflows and migration scripts

echo "=== Checking CI workflows for migration commands ==="
find .github/workflows -name "*.yml" -o -name "*.yaml" 2>/dev/null | head -20

echo -e "\n=== Searching for migration/drizzle commands in workflows ==="
rg "db:migrate|drizzle-kit|db push|db pull" .github/workflows -A 3 -B 2 2>/dev/null || echo "No matches found"

echo -e "\n=== Checking for backup or safety mechanisms ==="
rg "backup|rollback|--force|--protected" .github/workflows apps/api -A 2 -B 1 2>/dev/null || echo "No matches found"

echo -e "\n=== Checking drizzle.config.ts structure ==="
find . -name "drizzle.config.ts" -o -name "drizzle.config.js" 2>/dev/null | xargs cat 2>/dev/null

echo -e "\n=== Searching for migration-related environment variables or conditions ==="
rg "NODE_ENV|ENVIRONMENT|PROD|STAGING" .github/workflows -A 1 2>/dev/null | head -30

Repository: Systemsaholic/tailfire

Length of output: 8312


Add explicit safeguards for production migrations before relying on strict mode being disabled.

The CI workflows differentiate environments via separate database secrets (PROD_DATABASE_URL vs DEV_DATABASE_URL), but no explicit safeguards for production migrations are evident in the visible workflow configurations. Verify that:

  1. Pre-deployment migration testing is enforced in non-production environments
  2. Backup and restore procedures for production are automated and tested
  3. Production deployments require manual approval gates or are protected via Supabase organization-level safeguards
  4. Migration rollback procedures are documented and tested

Without these safeguards, disabling strict mode increases the risk of unrecoverable production data loss.

🤖 Prompt for AI Agents
In @apps/api/drizzle.config.ts at line 14, The config currently sets strict:
false which disables interactive/guard checks; add explicit production
safeguards instead of relying on separate CI secrets by (1) preventing automatic
migrations when PROD_DATABASE_URL is present unless an explicit env flag (e.g.,
ALLOW_PROD_MIGRATIONS) is set and confirmed, (2) adding pre-migration hooks that
create automated backups using your DB backup tooling and verifying backup
success before running migrations, (3) gating production deploys/migrations
behind a manual approval step in CI or requiring a signed-off variable to
proceed, and (4) adding documented and tested rollback procedures invoked by
migration failure handlers; locate the strict setting in drizzle.config.ts and
implement these checks/hooks around the migration invocation points that read
PROD_DATABASE_URL / DEV_DATABASE_URL.

Comment thread apps/api/package.json
"date-fns-tz": "^3.2.0",
"drizzle-orm": "^0.33.0",
"dotenv": "^16.6.1",
"drizzle-orm": "^0.45.1",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

drizzle-orm breaking changes 0.33.0 to 0.45.1 upgrade

💡 Result:

Short summary of breaking / upgrade-impacting changes between v0.33.0 and v0.45.1 and upgrade checklist.

Key changes you must check

  • postgres-js json/jsonb handling: postgres-js clients passed to Drizzle now return raw JSON values (not stringified). If you used postgres-js + jsonb you must audit inserts/reads and any code that assumed stringified JSON. [1]
  • New dialects & libsql/Turso: Turso (LibSQL) support added — requires newer @libsql/client and changes to drizzle-kit config/migration behavior for Turso/SQLite. If you use libsql/Turso, upgrade drizzle-kit and @libsql/client per the kit changelog. [3]
  • Relations / RQB changes (RQB v2 work): Drizzle introduced a new relations/query-builder version and migration docs exist — if you use relation-builder APIs follow the RQBv1→RQBv2 migration guide. [4]
  • Error / driver behavior changes: Drizzle introduced DrizzleQueryError (wraps driver errors) and changed some driver mapping behavior for dates/timestamps across drivers — check any code relying on raw driver error shapes or previously-returned Date vs string types. [3]
  • Type and client changes: Several releases adjusted $client typing and driver mappers (e.g., pg mappers, bun-sql handling of Date instances). Rebuild TypeScript and fix any type regressions. [1][3]
  • Minor-breaking/behavioral fixes that can affect apps: e.g., $onUpdate handling for SQL values, allowing subqueries in select fields, changes to numeric/decimal handling and SQLite/MySQL bigint modes — audit queries that depend on previous SQL text shapes or implicit type conversions. [1][3]

Practical upgrade steps

  1. Read the release notes between your current version and target (0.34…0.45) and the drizzle-kit notes for migration changes. Start with the v0.33.0 and v0.45.x release notes. [1][2][3]
  2. Upgrade incrementally (e.g., 0.33 → 0.38 → 0.41 → 0.44 → 0.45) rather than jumping straight to 0.45. Run tests at each step.
  3. If using postgres-js + jsonb: search for places that stringify JSON before insert or that expect string values on select and update them to work with raw JSON objects. Test migrations and inserts. [2]
  4. If using Turso/LibSQL: upgrade @libsql/client as required and update drizzle.config / kit migration settings per kit changelog. [3]
  5. Rebuild TypeScript, fix any compile errors from $client/types changes, and update code that inspects driver errors to handle DrizzleQueryError. [1][3]
  6. Run full integration tests (schema, migrations, inserts/updates/selects, relations) and examine produced SQL/migration diffs before applying to production.

Sources / reading (start here)

  • Drizzle ORM GitHub releases (0.45.1, 0.45.0 notes). [1]
  • Drizzle ORM release 0.33.0 notes (postgres-js jsonb change). [2]
  • Drizzle docs / release notes summary (Turso/libsql, DrizzleQueryError, RQB changes). [3]
  • RQB v1 → v2 migration / relations docs referenced in release notes. [4]

If you want, I can:

  • produce a step-by-step incremental upgrade plan for your repo (give me package.json and which drivers/dialects you use), or
  • extract and summarize every changelog entry between 0.33.0 and 0.45.1.

🏁 Script executed:

# First, check the full package.json to see which database drivers are used
cat apps/api/package.json

Repository: Systemsaholic/tailfire

Length of output: 3531


🏁 Script executed:

# Find drizzle configuration and database setup files
find . -type f -name "drizzle.config.*" -o -name "*db*" -type f | grep -E "(config|setup|migrate|schema)" | head -20

Repository: Systemsaholic/tailfire

Length of output: 132


🏁 Script executed:

# Check the drizzle configs to identify the database driver
cat -n apps/api/drizzle.config.ts
echo "---"
cat -n packages/database/drizzle.config.ts

Repository: Systemsaholic/tailfire

Length of output: 930


🏁 Script executed:

# Look for database client setup - check packages/database for connection code
find packages/database/src -type f -name "*.ts" | head -10

Repository: Systemsaholic/tailfire

Length of output: 526


🏁 Script executed:

# Check the database client to see which driver is used
cat -n packages/database/src/client.ts

Repository: Systemsaholic/tailfire

Length of output: 1528


🏁 Script executed:

# Check if the schema uses JSONB/JSON fields
rg -n "jsonb|json" packages/database/src/schema/ --type ts | head -20

Repository: Systemsaholic/tailfire

Length of output: 2669


🏁 Script executed:

# Check for JSONB usage in the codebase - looking for places where JSON is inserted or read
rg -n "jsonb\|JSON\|stringify" packages/database/src --type ts -A 2 | head -30

Repository: Systemsaholic/tailfire

Length of output: 48


🏁 Script executed:

# Search for places where JSONB columns are being inserted/updated/read
rg -n "officeAddress|socialMediaLinks|customFields|contactSnapshot|emergencyContactInline|metadata|features|contactInfo|coordinates" packages/database/src apps/api/src --type ts -B 2 -A 2 | head -50

Repository: Systemsaholic/tailfire

Length of output: 3626


Audit postgres-js JSON handling in the extensive JSONB column usage.

The upgrade from 0.33.0 to ^0.45.1 includes a critical breaking change: postgres-js now returns raw JSON values instead of stringified JSON. This codebase uses JSONB columns extensively (user profiles, trips, media, transportation details, suppliers, port info, dining details, and cruise sailings). You must audit all code that reads from these JSONB columns to ensure it handles raw JSON objects rather than strings, particularly in any serialization, validation, or type-checking logic. Additionally, verify that migrations and seed data continue to work correctly with the new JSON handling behavior.

port = 54322
shadow_port = 54320
major_version = 15
major_version = 17
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

PostgreSQL 15 to 17 breaking changes migration guide

💡 Result:

Here’s a concise migration checklist of the notable breaking/incompatible changes and recommended actions when moving PostgreSQL 15 → 16 → 17. Sources are PostgreSQL release notes and docs (links cited).

Summary checklist (do these before upgrading)

  1. Read release notes for your target minor release (16.x then 17.x). [1][2][3]
  2. Test upgrade in a staging environment (pg_upgrade and pg_dump/restore paths). [1][2][3]
  3. Verify all extensions are available/compatible on the target major versions. [1][2]
  4. Run regression tests and app integration tests, check queries, permissions, and replication. [2][3]

Key incompatible/behavior changes and actions

From 15 → 16

  • Default permissions on public schema: new clusters remove PUBLIC CREATE on schema public — review/revoke CREATE where needed. [1]
  • PL/pgSQL bound cursor assignment changed: portal name assignment happens at OPEN (may change behavior of code that relied on old names) — update cursor code or assign portal names before OPEN. [2]
  • NULLS NOT DISTINCT indexes disallowed for primary keys — search for such indexes and change schema if present. [2]
  • REINDEX DATABASE/reindexdb behavior changed to skip system-catalog indexes by default (use REINDEX SYSTEM or reindexdb --system to include them). Review any scripts that relied on previous behavior. [2]

From 16 → 17

  • Functions use a safe search_path during maintenance operations — code or extensions that relied on non-default search_path during maintenance may fail; qualify object names or adjust code accordingly. [3]
  • New/changed SQL/JSON functions and JSON constructor behavior (some default/collate handling fixes) — test JSON/SQL/JSON code paths. [3]
  • Client/libpq fixes and tightened checks (some security-related behavior changes) — test client applications and any custom libpq usage. [3]
  • Misc. index and BRIN/numeric index changes (some minor-version notes advise reindexing after upgrade). Reindex any BRIN or bloom-like indexes if recommended. [2][3]

Other items to check (applies across upgrades)

  • pg_dump/pg_restore / pg_upgrade: major-version upgrade requires dump/restore or pg_upgrade; patch-version upgrades normally do not. Follow the recommended upgrade path in docs. [1][2][3]
  • Recreate or verify constraints on partitioned tables if you used partition detach/foreign keys (noted in 16.x minor releases). Reindex BRIN bloom indexes after some minor releases if advised. [2]
  • Obsoleted or renamed utilities/features: check Appendix on obsolete/renamed features (e.g., recovery.conf merging, pg_xlogdump → pg_waldump, pg_resetxlog → pg_resetwal) and update scripts/docs accordingly. [4]
  • Security-related behavior: minor releases often tighten permission/security checks — test roles, RLS, views, and privilege-dependent code. [2][3]

Quick practical upgrade steps

  1. Build a test clone of production DB (pg_dump/pg_restore or file-based pg_upgrade). [1][2]
  2. Run the application test suite; enable extended logging to capture SQL errors/warnings.
  3. Search schema/code for known-risk items (unqualified object names, reliance on public schema CREATE, cursor code, NULLS NOT DISTINCT indexes). Fix or prepare rollback.
  4. Reindex indexes flagged by release notes (BRIN/numeric/etc.) and run ANALYZE. [2][3]
  5. Verify extensions (reinstall/upgrade if necessary).
  6. Plan downtime/rollback and perform production upgrade.

Primary sources (read fully before upgrading)

  • PostgreSQL 15 release notes (Migration to Version 15). [1]
  • PostgreSQL 16 release notes (Migration to Version 16 and minor-version migration notes). [2]
  • PostgreSQL 17 release notes (Migration to Version 17 and changes). [3]
  • Appendix — Obsolete or Renamed Features (check for renamed utilities/features). [4]

References
[1] PostgreSQL 15 release notes (Migration to Version 15).
[2] PostgreSQL 16 release notes (Migration to Version 16).
[3] PostgreSQL 17 release notes (Migration to Version 17).
[4] PostgreSQL 17 Appendix: Obsolete or Renamed Features.

Would you like a tailored pre-upgrade checklist for your database (schema scan script, extension list, or sample queries to find problematic constructs)?


🌐 Web query:

Supabase CLI PostgreSQL 17 version support local development

💡 Result:

Short answer: Yes — the Supabase CLI now pulls Postgres 17 images for local dev by default (the platform migrated to Postgres 17), and this has caused version-mismatch issues with older hosted projects. You can override the local image version by placing the desired version string in supabase/.temp/postgres-version (or use the restore workflow which documents the same method). References: Supabase changelog (Postgres 17 rollout), GitHub issue reporting the CLI pulling Postgres 17, and the docs showing the supabase/.temp/postgres-version override. [1][2][3]

Sources

  • Supabase changelog: Postgres 17 rollout and extension changes. [1]
  • GitHub issue: supabase CLI starting Postgres 17 causing db-diff/version mismatch. [2]
  • Supabase docs: restore/start guidance showing echoing a version into supabase/.temp/postgres-version to control local Postgres image. [3]

Verify PostgreSQL 17 compatibility and address confirmed breaking changes.

Upgrading from PostgreSQL 15 to 17 requires addressing documented breaking changes:

15→16 breaking changes:

  • Public schema permissions: new clusters revoke CREATE on schema public — review role permissions
  • PL/pgSQL bound cursor behavior: portal names assigned at OPEN instead of declaration — update cursor code if affected
  • Indexes with NULLS NOT DISTINCT disallowed for primary keys — check schema for such indexes
  • REINDEX DATABASE now skips system-catalog indexes by default — update scripts that relied on previous behavior

16→17 breaking changes:

  • Functions use safe search_path during maintenance — unqualified object names in code/extensions may fail; qualify object names or adjust code
  • JSON/SQL function and constructor behavior changes — test JSON code paths
  • Client/libpq security-related tightening — test client applications

Required actions:

  • Test upgrade in staging using pg_upgrade or pg_dump/pg_restore
  • Verify all extensions are compatible with PostgreSQL 17
  • Review application code and schema for constructs listed above
  • Reindex BRIN and numeric indexes if advised by release notes
  • Supabase CLI note: Supabase CLI now defaults to PostgreSQL 17 for local development. If your hosted Supabase project is on PostgreSQL 15, override the local CLI version by creating supabase/.temp/postgres-version with the content 15 to maintain local/prod parity during development.
🤖 Prompt for AI Agents
In @apps/ota/supabase/config.toml at line 14, The config entry major_version =
17 was changed but you must verify PostgreSQL 17 compatibility before flipping
production: run a staged pg_upgrade or pg_dump/pg_restore, test app codepaths
that use JSON, unqualified object names and PL/pgSQL cursors, confirm extensions
and index/REINDEX behavior, and if your hosted Supabase remains on PG15 keep
local parity by creating supabase/.temp/postgres-version containing "15" to
force the Supabase CLI to use v15; alternatively, once all tests pass and
extensions are confirmed, keep major_version = 17.

Comment thread docs/ENVIRONMENTS.md
Comment on lines +44 to +49
```
http://localhost:3100
http://localhost:3101
http://localhost:3102
http://localhost:3103
```
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add language identifier to code block.

The fenced code block is missing a language identifier, which affects Markdown linting and syntax highlighting.

📝 Proposed fix
 ### Local Development (Default)
 
 When `CORS_ORIGINS` is not set, the API allows:
-```
+```bash
 http://localhost:3100
 http://localhost:3101
 http://localhost:3102
 http://localhost:3103

</details>

Based on static analysis hints.

<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion

🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

44-44: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
In @docs/ENVIRONMENTS.md around lines 44 - 49, The fenced code block showing
localhost URLs lacks a language identifier; update the block delimiter from ```
to ```bash so the snippet in docs/ENVIRONMENTS.md (the four-line block
containing "http://localhost:3100" through "http://localhost:3103") has a
language tag for proper linting and syntax highlighting.

Comment thread docs/LOCAL_DEV.md
Comment on lines +18 to +21
cp apps/api/.env.example apps/api/.env
cp apps/admin/.env.example apps/admin/.env.local
cp apps/ota/.env.example apps/ota/.env.local
cp apps/client/.env.example apps/client/.env.local
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for existence of .env.example files in all apps
fd -t f '\.env\.example$' apps/

Repository: Systemsaholic/tailfire

Length of output: 48


Create missing .env.example files for all apps or correct the quick start documentation.

The quick start instructions reference .env.example files in apps/api, apps/admin, apps/ota, and apps/client that do not exist in the repository. Users following these steps will encounter "No such file or directory" errors when running the copy commands. Either create these template files in each app directory or update the documentation with the correct initialization approach.

🤖 Prompt for AI Agents
In @docs/LOCAL_DEV.md around lines 18 - 21, The quick start docs reference
missing template files; either add .env.example templates for apps/api,
apps/admin, apps/ota, and apps/client (e.g., create apps/api/.env.example,
apps/admin/.env.example, apps/ota/.env.example, apps/client/.env.example with
the required sample vars) or update LOCAL_DEV.md to copy the correct existing
files (e.g., change apps/admin/.env.example -> apps/admin/.env.local.example or
point to the actual env template names) and adjust the cp commands in the
snippet so the listed filenames match the repository; ensure the doc commands
and the repo templates use the same filenames (referenced symbols: LOCAL_DEV.md
snippet and the files apps/api/.env.example, apps/admin/.env.example,
apps/ota/.env.example, apps/client/.env.example).

Comment thread docs/LOCAL_DEV.md
ENABLE_SWAGGER_DOCS=true
```

### Frontend Apps (`apps/*/\env.local`)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix the escaped backslash in the heading.

The heading contains apps/*/\env.local with an unnecessary backslash escape before env. It should be apps/*/.env.local instead.

📝 Proposed fix
-### Frontend Apps (`apps/*/\env.local`)
+### Frontend Apps (`apps/*/.env.local`)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
### Frontend Apps (`apps/*/\env.local`)
### Frontend Apps (`apps/*/.env.local`)
🤖 Prompt for AI Agents
In @docs/LOCAL_DEV.md at line 162, Update the Markdown heading that currently
reads "### Frontend Apps (`apps/*/\env.local`)" to remove the unnecessary escape
and correct the filename to "### Frontend Apps (`apps/*/.env.local`)" so the
heading accurately shows the .env.local pattern; search for the exact string
"`apps/*/\\env.local`" or the heading "### Frontend Apps" to locate and replace
it.

"devDependencies": {
"@types/node": "^20.12.0",
"drizzle-kit": "^0.24.0",
"drizzle-kit": "^0.30.3",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

drizzle-kit 0.30.3 breaking changes from 0.24.0

💡 Result:

Here are the notable breaking / migration-relevant changes introduced between drizzle-kit v0.24.0 and the v0.30.x series (including 0.30.3):

  • Full drizzle-kit rewrite (new diff/DDL snapshot architecture, faster introspection, new migration detection) — expect changed folder layout and migration workflow. [1][4]
  • Migration folder/file structure and journal changes — older snapshots/journal layout migrated to new format; run drizzle-kit up/upgrade to migrate existing migrations. [1][2]
  • schemaFilter behavior changed: drizzle-kit starts managing all schemas defined in code (supports glob patterns); you may need to adjust schemaFilter. [4]
  • New CLI flags and behavior: added pull --init (marks first pulled migration as applied) and new push options (including --force to auto-accept data-loss statements in later 0.31+ releases); check commands when migrating. [4][3]
  • PostgreSQL dialect DDL changed to align with other dialects — removals of IF NOT EXISTS / $DO and stricter DDL (previously could silently skip existing objects). This can cause previously-ignored DDL to fail (intended). [4]
  • Removal/deprecation of some commands/options: e.g., drizzle-kit drop removed. Also .enableRLS() deprecated in favor of pgTable.withRLS(...). Adjust code using deprecated APIs. [4]
  • Index API & related PostgreSQL API changes (index definition API changed across 0.30→0.31; review index declarations if you target Postgres). [3]
  • Implicit transactions behavior for generated SQL/migrations changed (BEGIN/COMMIT handling and behavior around index concurrently and failed migrations — check generated SQL). [3]
  • Misc: many driver/column-type fixes and small behavioral changes (SQLite composite PK ordering fix, new MySQL types, various driver fixes) that may affect migrations or introspection. Review release notes for dialect-specific effects. [2][3][4]

References (read the full notes and migration guidance before upgrading):

  • Drizzle docs / latest releases and release notes. [1][3]
  • drizzle-kit / drizzle-orm GitHub release notes (0.24.0 and 0.30.x series). [2][4]

If you want, I can fetch and list exact breaking-change lines from each release changelog (0.25 → 0.26 → … → 0.30.3) so you can see per-version diffs.


🌐 Web query:

drizzle-kit changelog version 0.25 0.26 0.27 0.28 0.29 0.30 breaking changes

💡 Result:

Here are the breaking changes for drizzle-kit versions 0.25 → 0.30 (concise per version):

  • 0.25 — Turso/libsql changes: introduce a turso dialect; migrate drizzle.config and require @libsql/client >= 0.10.0 for migrate usage. (Turso-specific migration guidance). [1]

  • 0.26 — Postgres enum & view behavior changes: improved enum operations (add before/after, move, rename, drop with column-alter workflow) and push/generate limitations for view SQL changes. [2]

  • 0.27 — New PostgreSQL features with breaking implications: Row-Level Security (RLS) API additions and related schema/role behavior (requires matching drizzle-orm support); other tooling/compat notes tied to this release. [3]

  • 0.28 — Removed support for filtering by nested relations: the table object in where callbacks no longer includes fields from with/extras (existing nested-relation filters will break). [4]

  • 0.29 — Requires newer Drizzle ORM/Kit pairing (minimum pairing notes) and query-builder/type changes (methods restricted to single invocation in many cases); MySQL bigint unsigned support added (type/typing-related breaking changes). [5]

  • 0.30 — PostgreSQL DDL behavior changes: Postgres dialect no longer emits IF NOT EXISTS / $DO-like statements for DDL (so previously-ignored errors will now fail); Postgres timestamp mapping changed (postgres.js driver date handling → strings / toISOString changes). [6]

Sources:

  1. drizzle-kit@0.25 release notes. [1]
  2. drizzle-kit@0.26 release notes (enum & view updates). [2]
  3. drizzle-kit@0.27 release notes (RLS). [3]
  4. drizzle-kit v0.28 breaking changes (nested relation filtering removed). [4]
  5. drizzle-kit v0.29 release notes (types / pairing). [5]
  6. drizzle-kit v0.30 release notes (Postgres DDL & timestamp mapping). [6]

If you want, I can paste the exact changelog lines or link the specific release pages.


Review migration paths for drizzle-kit 0.30.3 before upgrading from 0.24.0.

This includes a full rewrite with significant breaking changes: migration folder/file structure will change (run drizzle-kit up/upgrade to migrate existing migrations), PostgreSQL DDL no longer emits IF NOT EXISTS statements (previously ignored errors will now fail), schemaFilter behavior has changed to manage all schemas with glob patterns, and several APIs are deprecated (e.g., .enableRLS()pgTable.withRLS(...)). Verify that existing migrations are compatible and test migration generation and push workflows before deploying.

🤖 Prompt for AI Agents
In @packages/database/package.json at line 26, The project dependency was bumped
to drizzle-kit 0.30.3 which contains breaking changes from 0.24.0; review and
update migrations and code to be compatible: audit migration files and run
drizzle-kit up/upgrade to migrate folder/file structure, re-generate and test
migrations to catch removed IF NOT EXISTS behavior in PostgreSQL DDL, update any
schemaFilter usage to the new glob/schemas semantics, and replace deprecated
APIs (e.g., migrate usages of .enableRLS() to pgTable.withRLS(...)) across code
and CI/CD migration/push workflows before merging.

- Add vercel.json to each app with installCommand and buildCommand
- Keep API PATCH for rootDirectory only (needed for Vercel to find vercel.json)
- This ensures pnpm is used instead of npm for workspace: protocol support

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
.github/workflows/deploy-prod.yml (1)

85-99: CLI-based deployment enables monorepo support.

The CLI approach with dynamic rootDirectory configuration allows Vercel to locate the per-app vercel.json files in the monorepo structure.

Note: The rootDirectory PATCH is idempotent, so running it on every deployment is safe, though slightly redundant if the setting persists between runs.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aa483f6 and 3e8dbae.

📒 Files selected for processing (8)
  • .github/workflows/deploy-dev.yml
  • .github/workflows/deploy-prod.yml
  • apps/admin/vercel.json
  • apps/client/vercel.json
  • apps/ota/vercel.json
  • docs/CI_CD.md
  • docs/DEPLOYMENT_API.md
  • docs/ENVIRONMENTS.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/deploy-dev.yml
🧰 Additional context used
🪛 LanguageTool
docs/DEPLOYMENT_API.md

[style] ~259-~259: To form a complete sentence, be sure to include a subject.
Context: ...Behavior - RUN_MIGRATIONS_ON_STARTUP should be false (or unset) - Migrations run ...

(MISSING_IT_THERE)

🪛 markdownlint-cli2 (0.18.1)
docs/DEPLOYMENT_API.md

49-49: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


54-54: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

docs/CI_CD.md

18-18: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

docs/ENVIRONMENTS.md

44-44: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Deploy OTA to Vercel
  • GitHub Check: Deploy Admin to Vercel
  • GitHub Check: Deploy Client to Vercel
🔇 Additional comments (12)
apps/client/vercel.json (1)

1-5: LGTM! Clean Vercel configuration for monorepo.

The configuration correctly navigates to the monorepo root and uses pnpm's workspace filtering to build the client app. This aligns with the CLI-based deployment approach documented in the PR.

apps/ota/vercel.json (1)

1-5: LGTM! Consistent with other app configurations.

The OTA Vercel configuration follows the same pattern as the client app, correctly using pnpm workspaces and scoped builds.

apps/admin/vercel.json (1)

1-5: LGTM! Dependency build chain ensures correct build order.

The sequential buildCommand (database → shared-types → admin) addresses the build order issue mentioned in the PR objectives. This ensures dependencies are built before the admin app, preventing build failures from missing types or database artifacts.

docs/CI_CD.md (5)

18-57: ASCII diagrams clearly illustrate deployment flows.

The deployment flow diagrams effectively visualize the job sequences and dependencies for both dev and prod workflows.

Note: The markdownlint warning about missing language specification on line 18 is a false positive—ASCII art diagrams don't need a language specifier.


61-125: Migration strategy is well-documented.

The dual-migration approach (Drizzle for core schema, Supabase CLI for FDW) is clearly explained with practical examples. The guard logic documentation helps prevent confusion about environment-specific behavior.


127-178: Railway deployment and job dependencies clearly explained.

The documentation effectively explains Railway's auto-deploy behavior and the importance of the "Wait for CI" feature. The job dependency tables provide a quick reference for understanding workflow orchestration.


180-229: Branch protection and secrets reference is helpful.

Providing exact check names and configuration steps makes it easy to set up branch protection rules. The secrets table serves as a useful reference for repository configuration.


231-266: Deployment targets and rollback procedures complete the documentation.

The rollback procedures include an important warning about manual migration rollbacks, which could prevent critical mistakes. The related documentation links help users navigate to additional resources.

.github/workflows/deploy-prod.yml (2)

33-40: Database build step ensures migrations have compiled artifacts.

Building the database package before running migrations ensures that all necessary compiled artifacts are available. The migration command change to pnpm db:migrate aligns with the updated tooling approach.


108-145: Consistent deployment pattern across all frontend apps.

The OTA and Client deployments follow the same CLI-based pattern as Admin, ensuring consistency across the deployment pipeline.

docs/DEPLOYMENT_API.md (2)

49-51: Add language identifiers to CORS configuration code blocks.

The CORS origins code blocks lack language identifiers, affecting Markdown linting and syntax highlighting.

📝 Proposed fix
 ### Preview Deployments (Vercel)
 
 For Vercel preview deployments, you have two options:
 
 1. **Add explicit preview URLs** to the allowlist as they're created
 2. **Implement dynamic origin checking** in the API (the default CORS middleware does not support wildcards like `*.vercel.app`)
 
 Example dynamic origin checker pattern:
 ```typescript
 origin: (origin, callback) => {
   const allowed = process.env.CORS_ORIGINS?.split(',') || []
   if (!origin || allowed.includes(origin) || origin.endsWith('.vercel.app')) {
     callback(null, true)
   } else {
     callback(new Error('Not allowed by CORS'))
   }
 }

And for the CORS Origins sections:

```diff
 ### CORS Origins by Environment
 
 **Development:**
-```
+```bash
 https://tailfire-dev.phoenixvoyages.ca,https://ota-dev.phoenixvoyages.ca,https://client-dev.phoenixvoyages.ca,http://localhost:3100,http://localhost:3102,http://localhost:3103
-```
+```
 
 **Production:**
-```
+```bash
 https://tailfire.phoenixvoyages.ca,https://ota.phoenixvoyages.ca,https://client.phoenixvoyages.ca,https://phoenixvoyages.ca,https://www.phoenixvoyages.ca
-```
+```

Also applies to: 54-56


1-449: Well-documented Railway deployment guide.

The API deployment documentation comprehensively covers environments, variables, migrations, health checks, DNS configuration, and troubleshooting. The organization is clear, and guidance aligns well with the PR objectives around pipeline tightening and migration guards. Documentation accurately reflects the deployment workflow with Railway "Wait for CI" integration and health endpoint validation.

@Systemsaholic Systemsaholic merged commit 47b0365 into main Jan 7, 2026
12 checks passed
@railway-app railway-app Bot temporarily deployed to tailfire / api January 9, 2026 12:34 Inactive
Systemsaholic added a commit that referenced this pull request Mar 20, 2026
1. TransportationAddressInput: propagate manual text on blur and clear
   form state when input is emptied (Codex issue #1)
2. detail-mapper: add coerce.toNumber for parsing numeric DB strings
3. transportation-details.service: use toNullableStrict for lat/lng
   on create (preserves 0), toNumber on format (string→number)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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