All Docs
Getting StartedagentOS Direct DebitUpdated March 20, 2026

Fixing Three CI Failures in One Go — v1.0.75

Fixing Three CI Failures in One Go — v1.0.75

v1.0.75 is a pure infrastructure release. No features changed, no APIs shifted — just three independent CI failures that had stacked up and needed clearing before further development could land cleanly.

Here's what broke and how each one was fixed.


1. Duplicate DATABASE_URL in CI YAML

What happened: The CI workflow file (.github/workflows/ci.yml) defined DATABASE_URL twice under the env block. YAML does not allow duplicate keys at the same level, and GitHub Actions enforces this with a hard parse error — the entire workflow is rejected before a single step runs.

Fix: Removed the duplicate entry. One DATABASE_URL remains:

env:
  DATABASE_URL: "postgresql://dummy:dummy@localhost:5432/dummy"

This is a one-line diff but it was blocking every CI run.


2. drizzle-kit push Refusing to Connect to a Dummy Database

What happened: The build script in package.json was:

"build": "yes '' | npx drizzle-kit push --force && next build"

This runs drizzle-kit push — which attempts a live connection to Postgres — before every next build. In a production or staging deployment, the real DATABASE_URL is set and this works fine. In CI, the URL points to a dummy local Postgres instance that doesn't exist.

The deeper issue is that @neondatabase/serverless (used by this project for Postgres access) only supports remote Neon, Vercel Postgres, and Supabase instances via WebSocket. It refuses to connect to a plain local Postgres URL, so it exits with code 1 — killing the build before next build even starts.

Fix: The build script now checks for a SKIP_DB_PUSH environment variable:

"build": "node -e \"if(!process.env.SKIP_DB_PUSH){require('child_process').execSync('yes \\\"\\\" | npx drizzle-kit push --force',{stdio:'inherit'})}\" && next build"

CI sets SKIP_DB_PUSH=true in the workflow env block:

env:
  SKIP_DB_PUSH: "true"

Production deployments do not set this variable, so drizzle-kit push continues to run as before on real deployments. The schema push behaviour is unchanged outside of CI.


3. Missing dist Module in @saas-factory-live/shell Crashing Tests

What happened: tests/lib/griffin/client.test.ts imports from @saas-factory-live/shell/schema. The shell package's built dist/schema/users module was missing from the installed package, so Node threw a module-not-found error at import time — before Vitest could run a single assertion. The test file showed as a hard failure rather than a test failure.

Fix: Two lightweight mock files were added under src/__mocks__/:

src/__mocks__/shell-schema.ts — Defines the same Drizzle tables the shell package exports, using drizzle-orm/pg-core directly:

import { pgTable, text, timestamp } from "drizzle-orm/pg-core";

export const users = pgTable("users", {
  id: text("id").primaryKey(),
  name: text("name"),
  email: text("email"),
  createdAt: timestamp("created_at").defaultNow(),
  updatedAt: timestamp("updated_at").defaultNow(),
});

// organizations, accounts, sessions, verificationTokens, organizationMembers ...

src/__mocks__/shell.ts — Re-exports everything from shell-schema:

export * from "./shell-schema";

Vitest is then configured in vitest.config.ts to resolve the shell package to these mocks:

resolve: {
  alias: {
    "@": path.resolve(__dirname, "./src"),
    "@saas-factory-live/shell/schema": path.resolve(
      __dirname,
      "./src/__mocks__/shell-schema.ts"
    ),
    "@saas-factory-live/shell": path.resolve(
      __dirname,
      "./src/__mocks__/shell.ts"
    ),
  },
},

The aliases are ordered longest-match-first (shell/schema before shell) so the sub-path import resolves correctly. Application code at runtime still uses the real shell package — these aliases are Vitest-only.


Result

All three fixes together restore a clean CI run. The pipeline now:

  1. Parses the workflow YAML without error
  2. Runs next build without attempting a database connection
  3. Resolves shell package imports in tests without a missing-module crash

No application-level code was changed in this release.