## What Adds a pre-merge CI check that proves a database created and seeded by the **oldest supported release** (`twentycrm/twenty:v1.22` from Docker Hub) can be upgraded by the **current version built from source**, and that the upgraded instance comes up healthy with its data still queryable. Runs only on PRs touching the upgrade path (`upgrade-version-command/**` + `core-modules/upgrade/**`), and blocks the PR via `ci-server-status-check`. ## How New reusable workflow `ci-cross-version-upgrade.yaml` (`workflow_call` + `workflow_dispatch`), called from `ci-server.yaml` after `server-build` so the build cache is populated in-run: 1. **Services** — `postgres:16` (prod parity) + `redis:7` on a docker network. 2. **Old version** — pull `twentycrm/twenty:v1.22`, boot it against the DB, `workspace:seed:dev`, sanity-check the seed via `psql`. 3. **New version (from source)** — restore the `server-build` nx cache (best-effort: a miss just cold-builds), `nx build`, run the `upgrade` command against the same DB, `start:ci`, poll `/healthz`. 4. **Smoke** — assert `upgrade:status` shows `Instance: Up to date` / `0 behind, 0 failed`, then run companies/people/metadata GraphQL queries. The job is always invoked but gated by a `skip` input (computed from the upgrade-paths `changed-files` check), with a `no-op` job reporting success when skipped — so the status check always resolves instead of leaving a dangling skipped job, mirroring the twenty-infra pattern. Unlike the equivalent post-merge gate in infra-twenty, this is **pre-merge**, uses **native PR path filtering** (no compare API), and **reuses the from-source build cache** instead of pulling an ECR image — no cross-repo plumbing, no skipped-commit gap. ## Security note No credentials are committed. `APP_SECRET` is generated fresh per run (`openssl rand`, `::add-mask::`'d) and shared between the old container and the from-source server within the job; the smoke-test API token is minted at runtime via `workspace:generate-api-key` against the upgraded server and masked in logs. ## Verified with a real run Validated end-to-end by temporarily touching the upgrade path to trigger the job (trigger commit since dropped), in [CI Server run `28097035272`](https://github.com/twentyhq/twenty/actions/runs/28097035272) → [`cross-version-upgrade` job](https://github.com/twentyhq/twenty/actions/runs/28097035272/job/83189453451) ✅ **all steps green**: - v1.22 container boot → `workspace:seed:dev` → `psql` seed sanity check ✅ - from-source build (nx cache restored) → `upgrade` → **56 workspace(s) succeeded, 0 failed** ✅ - server healthy → API token minted at runtime via `workspace:generate-api-key` ✅ - `upgrade:status` → `Instance: Up to date`, `0 behind, 0 failed` ✅ - companies / people / metadata GraphQL smoke queries ✅ - `no-op` job correctly skipped (real job ran because the gate matched) ✅ The three assumptions originally flagged for first-run all held; one bug was found and fixed in the process — `upgrade:status` colorizes via `chalk` even with `NO_COLOR`, so the assertion now strips ANSI escapes before grepping. > Note: the overall `ci-server` run shows a failure from an **unrelated flaky integration test** (`if-else-workflow.integration-spec.ts`, `column workspaceMember.region does not exist` in shard 11). The same trigger commit passed all 16 integration shards in the prior run — it's a pre-existing flake, not caused by this PR. ## Note Still keeping the equivalent one inside infra-twenty as an final bottleneck just in case <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22065?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-light.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
The #1 Open-Source CRM
Website ·
Documentation ·
Roadmap ·
Discord ·
Figma
Why Twenty
Twenty gives technical teams the building blocks for a custom CRM that meets complex business needs and quickly adapts as the business evolves. Twenty is the CRM you build, ship, and version like the rest of your stack.
Learn more about why we built Twenty
Installation
Cloud
The fastest way to get started. Sign up at twenty.com and spin up a workspace in under a minute, with no infrastructure to manage and always up to date.
Build an app
Scaffold a new app with the Twenty CLI:
npx create-twenty-app my-app
Define objects, fields, and views as code:
import { defineObject, FieldType } from 'twenty-sdk/define';
export default defineObject({
nameSingular: 'deal',
namePlural: 'deals',
labelSingular: 'Deal',
labelPlural: 'Deals',
fields: [
{ name: 'name', label: 'Name', type: FieldType.TEXT },
{ name: 'amount', label: 'Amount', type: FieldType.CURRENCY },
{ name: 'closeDate', label: 'Close Date', type: FieldType.DATE_TIME },
],
});
Then ship it to your workspace:
npx twenty app:publish --private
See the app development guide for objects, views, agents, and logic functions.
Self-hosting
Run Twenty on your own infrastructure with Docker Compose, or contribute locally via the local setup guide.
Everything you need
Twenty gives you the building blocks of a modern CRM (objects, views, workflows, and agents) and lets you extend them as code. Here's a tour of what's in the box.
Want to go deeper? Read the User Guide for product walkthroughs, or the
Documentation for developer reference.
|
|
|
|
|
|
Stack
TypeScript
Nx
NestJS, with BullMQ,
PostgreSQL,
Redis
React, with Jotai, Linaria and Lingui
Thanks
Thanks to these amazing services that we use and recommend for code review (Greptile), catching bugs (Sentry) and translating (Crowdin).
Join the Community
Star the repo ·
Discord ·
Feature requests ·
Releases ·
X ·
LinkedIn ·
Crowdin ·
Contribute





