Files
twenty/.github/workflows/ci-server.yaml
Thomas des Francs d168df965e Simplify page layout manifest authoring (#25513)
## Summary

Vertical-list widgets can now omit `position` and use array order, with
an optional top-level `heightBehavior`. For example, `heightBehavior:
'TAB_VIEWPORT'` makes a widget fill the tab without repeating the tab
mode or an index.

Existing Canvas tabs, explicit widget indices, and nested height
settings keep their behavior. SDK validation warns about deprecated
shapes and explains the replacement; it does not rewrite manifests.
Shared validation also checks the new height setting and viewport
ordering before installation.

Existing apps keep their published SDK syntax. This PR updates the local
SDK fixture and current API documentation; app migrations can follow
after the SDK is published.

Stack 3/4, following the CI prerequisite and runtime height support. The
final PR migrates eligible stored Canvas tabs.

Review and merge order:

1. [App CI repairs](https://github.com/twentyhq/twenty/pull/25531)
2. [Widget height
behavior](https://github.com/twentyhq/twenty/pull/25512)
3. [Manifest authoring](https://github.com/twentyhq/twenty/pull/25513)
4. [Existing Canvas data
migration](https://github.com/twentyhq/twenty/pull/25511)

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/25513?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-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-09-11 15:32:47 +00:00

416 lines
15 KiB
YAML

name: CI Server
on:
pull_request:
merge_group:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
env:
SERVER_BUILD_CACHE_KEY: server-build
jobs:
changed-files-check:
if: github.event_name != 'merge_group'
uses: ./.github/workflows/changed-files.yaml
with:
files: |
package.json
yarn.lock
packages/twenty-server/**
packages/twenty-front/src/generated/**
packages/twenty-front/src/generated-metadata/**
packages/twenty-front/src/generated-admin/**
packages/twenty-client-sdk/**
packages/twenty-emails/**
packages/twenty-shared/**
upgrade-changed-files-check:
if: github.event_name != 'merge_group'
uses: ./.github/workflows/changed-files.yaml
with:
files: |
packages/twenty-server/src/database/commands/upgrade-version-command/**
packages/twenty-server/src/engine/core-modules/upgrade/**
server-build:
needs: changed-files-check
if: needs.changed-files-check.outputs.any_changed == 'true'
timeout-minutes: 30
runs-on: ubuntu-latest
steps:
- name: Fetch custom Github Actions and base branch history
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
fetch-depth: 10
- name: Install dependencies
uses: ./.github/actions/yarn-install
- name: Restore server build cache
id: restore-server-build-cache
uses: ./.github/actions/restore-cache
with:
key: ${{ env.SERVER_BUILD_CACHE_KEY }}
- name: Build twenty-shared
run: npx nx build twenty-shared
- name: Server / Write .env
run: npx nx reset:env twenty-server
- name: Server / Build
run: npx nx build twenty-server
- name: Save server build cache
uses: ./.github/actions/save-cache
with:
key: ${{ steps.restore-server-build-cache.outputs.cache-primary-key }}
server-lint-typecheck:
needs: changed-files-check
if: needs.changed-files-check.outputs.any_changed == 'true'
timeout-minutes: 30
runs-on: ubuntu-latest
steps:
- name: Fetch custom Github Actions and base branch history
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
fetch-depth: 10
- name: Install dependencies
uses: ./.github/actions/yarn-install
- name: Build twenty-shared
run: npx nx build twenty-shared
- name: Server / Run lint, typecheck & lingui extract
uses: ./.github/actions/nx-affected
with:
tag: scope:backend
tasks: lint,typecheck,lingui:extract
# Separate step so compile never races extract on the .po files.
- name: Server / Run lingui compile
uses: ./.github/actions/nx-affected
with:
tag: scope:backend
tasks: lingui:compile
server-previous-version-upgrade-mutation-guard:
if: github.event_name != 'merge_group'
timeout-minutes: 5
runs-on: ubuntu-latest
steps:
- name: Fetch custom Github Actions and base branch history
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
fetch-depth: 2
# HEAD is the pull_request merge ref; its first parent is the exact main
# commit it was merged with. pull_request.base.sha can lag behind that,
# making commands merged into main since then look added by this PR.
- name: Resolve merged base commit
id: merged-base
shell: bash
run: |
if git rev-parse -q --verify HEAD^2 >/dev/null; then
BASE_SHA=$(git rev-parse HEAD^1)
else
BASE_SHA="${{ github.event.pull_request.base.sha }}"
fi
echo "sha=$BASE_SHA" >> "$GITHUB_OUTPUT"
- name: Validate upgrade command mutations
uses: ./.github/actions/upgrade-mutation-guard
with:
base_sha: ${{ steps.merged-base.outputs.sha }}
allow_previous_version_mutation: ${{ contains(github.event.pull_request.labels.*.name, 'ci:allow-previous-version-upgrade-mutation') }}
server-validation:
needs: server-build
timeout-minutes: 30
runs-on: ubuntu-latest
services:
postgres:
image: postgres:18
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis
ports:
- 6379:6379
steps:
- name: Fetch custom Github Actions and base branch history
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
fetch-depth: 10
- name: Install dependencies
uses: ./.github/actions/yarn-install
- name: Restore server build cache
uses: ./.github/actions/restore-cache
with:
key: ${{ env.SERVER_BUILD_CACHE_KEY }}
- name: Build twenty-shared
run: npx nx build twenty-shared
- name: Server / Write .env
run: npx nx reset:env twenty-server
- name: Server / Build
run: npx nx build twenty-server
- name: Server / Create DB
run: |
PGPASSWORD=postgres psql -h localhost -p 5432 -U postgres -d postgres -c 'CREATE DATABASE "default";'
PGPASSWORD=postgres psql -h localhost -p 5432 -U postgres -d postgres -c 'CREATE DATABASE "test";'
npx nx run twenty-server:database:init:prod
- name: Worker / Run
run: |
timeout 30s npx nx run twenty-server:worker || exit_code=$?
if [ $exit_code -eq 124 ]; then
exit 0
elif [ $exit_code -ne 0 ]; then
exit $exit_code
fi
- name: Server / Start
run: npx nx start:ci twenty-server &
- name: Waiting for server starting...
run: |
for i in {1..60}; do
if curl -f http://localhost:3000/healthz; then
echo "Server ready!"
exit 0
fi
echo "Waiting..."
sleep 2
done
echo "Server did not become healthy in time" >&2
exit 1
- name: Server / Check for Pending Migrations
run: |
npx nx database:migrate:generate twenty-server -- --name pending-migration-check || true
if ! git diff --quiet; then
echo "::error::Unexpected migration files were generated. Please run 'npx nx database:migrate:generate twenty-server -- --name <migration-name>' and commit the result."
echo ""
echo "The following migration changes were detected:"
echo "==================================================="
git diff
echo "==================================================="
echo ""
git checkout -- .
exit 1
fi
- name: Check for Pending Code Generation
run: |
HAS_ERRORS=false
npx nx run twenty-front:graphql:generate
npx nx run twenty-front:graphql:generate --configuration=metadata
npx nx run twenty-front:graphql:generate --configuration=admin
if ! git diff --quiet -- packages/twenty-front/src/generated packages/twenty-front/src/generated-metadata packages/twenty-front/src/generated-admin; then
echo "::error::GraphQL schema changes detected. Please run the three graphql:generate configurations ('data', 'metadata', 'admin') and commit the changes."
echo ""
echo "The following GraphQL schema changes were detected:"
echo "==================================================="
git diff -- packages/twenty-front/src/generated packages/twenty-front/src/generated-metadata packages/twenty-front/src/generated-admin
echo "==================================================="
echo ""
HAS_ERRORS=true
fi
npx nx run twenty-client-sdk:generate-metadata-client
if ! git diff --quiet -- packages/twenty-client-sdk/src/metadata/generated; then
echo "::error::SDK metadata client changes detected. Please run 'npx nx run twenty-client-sdk:generate-metadata-client' and commit the changes."
echo ""
echo "The following SDK metadata client changes were detected:"
echo "==================================================="
git diff -- packages/twenty-client-sdk/src/metadata/generated
echo "==================================================="
echo ""
HAS_ERRORS=true
fi
if [ "$HAS_ERRORS" = true ]; then
exit 1
fi
server-test:
needs: server-build
timeout-minutes: 30
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4]
steps:
- name: Fetch custom Github Actions and base branch history
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
fetch-depth: 10
- name: Install dependencies
uses: ./.github/actions/yarn-install
- name: Restore server build cache
uses: ./.github/actions/restore-cache
with:
key: ${{ env.SERVER_BUILD_CACHE_KEY }}
- name: Build twenty-shared
run: npx nx build twenty-shared
- name: Server / Run Tests (shard ${{ matrix.shard }}/4)
run: npx nx run twenty-server:test:ci --shard=${{ matrix.shard }}/4
server-integration-test:
timeout-minutes: 30
runs-on: ubuntu-latest
needs: server-build
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
services:
postgres:
image: postgres:18
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis
ports:
- 6379:6379
clickhouse:
image: clickhouse/clickhouse-server:25.8.8
env:
CLICKHOUSE_PASSWORD: clickhousePassword
CLICKHOUSE_URL: "http://default:clickhousePassword@localhost:8123/twenty"
ports:
- 8123:8123
- 9000:9000
options: >-
--health-cmd "clickhouse-client --host=localhost --port=9000 --user=default --password=clickhousePassword --query='SELECT 1'"
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
NODE_ENV: test
ANALYTICS_ENABLED: true
CLICKHOUSE_URL: "http://default:clickhousePassword@localhost:8123/twenty"
CLICKHOUSE_PASSWORD: clickhousePassword
SHARD_COUNTER: 16
steps:
- name: Fetch custom Github Actions and base branch history
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
fetch-depth: 10
- name: Install dependencies
uses: ./.github/actions/yarn-install
- name: Update .env.test for integrations tests
run: |
echo "" >> .env.test
echo "IS_BILLING_ENABLED=true" >> .env.test
echo "BILLING_STRIPE_API_KEY=test-api-key" >> .env.test
echo "BILLING_STRIPE_BASE_PLAN_PRODUCT_ID=test-base-plan-product-id" >> .env.test
echo "BILLING_STRIPE_WEBHOOK_SECRET=test-webhook-secret" >> .env.test
echo "BILLING_PLAN_REQUIRED_LINK=http://localhost:3001/stripe-redirection" >> .env.test
- name: Restore server build cache
uses: ./.github/actions/restore-cache
with:
key: ${{ env.SERVER_BUILD_CACHE_KEY }}
- name: Server / Build
run: npx nx build twenty-server
- name: Build dependencies
run: |
npx nx build twenty-shared
npx nx build twenty-emails
- name: Server / Create Test DB
run: |
PGPASSWORD=postgres psql -h localhost -p 5432 -U postgres -d postgres -c 'CREATE DATABASE "test";'
- name: Run ClickHouse migrations
run: npx nx clickhouse:migrate twenty-server
- name: Run ClickHouse seeds
run: npx nx clickhouse:seed twenty-server
- name: Server / Run Integration Tests
uses: ./.github/actions/nx-affected
with:
tag: scope:backend
tasks: 'test:integration'
configuration: 'with-db-reset'
args: ${{ format('--shard={0}/{1}', matrix.shard, env.SHARD_COUNTER) }}
# The secure-deployment suite boots the app as a production https
# deployment (SERVER_URL is env-only, forced by its jest config), so it
# cannot ride the main run. One shard is enough.
- name: Server / Run secure-deployment integration tests
if: matrix.shard == 1
run: npx nx run twenty-server:test:integration:secure
cross-version-upgrade:
needs: [upgrade-changed-files-check, server-build]
uses: ./.github/workflows/ci-cross-version-upgrade.yaml
with:
skip: ${{ needs.upgrade-changed-files-check.outputs.any_changed != 'true' }}
discover-public-apps:
needs: changed-files-check
if: needs.changed-files-check.outputs.any_changed == 'true'
uses: ./.github/workflows/discover-apps.yaml
with:
scope: public
changed-only: false
server-apps-install-smoke:
needs: [discover-public-apps, server-build]
timeout-minutes: 30
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
app: ${{ fromJSON(needs.discover-public-apps.outputs.matrix) }}
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
fetch-depth: 10
- name: Spawn Twenty server from monorepo
id: twenty
uses: ./.github/actions/spawn-twenty-server
with:
source: local
server-build-cache-key: ${{ env.SERVER_BUILD_CACHE_KEY }}
- name: Install app against server
uses: ./.github/actions/test-twenty-app
with:
api-url: ${{ steps.twenty.outputs.server-url }}
api-key: ${{ steps.twenty.outputs.api-key }}
app-path: ${{ matrix.app.path }}
mode: installation-only
ci-server-status-check:
if: always() && !cancelled()
timeout-minutes: 5
runs-on: ubuntu-latest
needs:
[
changed-files-check,
server-build,
server-lint-typecheck,
server-previous-version-upgrade-mutation-guard,
server-validation,
server-test,
server-integration-test,
cross-version-upgrade,
discover-public-apps,
server-apps-install-smoke,
]
steps:
- name: Fail job if any needs failed
if: contains(needs.*.result, 'failure')
run: exit 1