Commit Graph

458 Commits

Author SHA1 Message Date
Rashad Karanouh 6055262012 Deploy website only when twenty-website changes (#21977)
## Problem

Every push to `main` redeploys the website. `cd-deploy-main.yaml`
dispatches the infra `auto-deploy-main` fan-out, which unconditionally
triggered `deploy-website`. Since most PRs don't touch
`packages/twenty-website/**`, the majority were full, identical
Cloudflare builds — wasted CI and noise.

## Fix

Detect website changes here (using the existing reusable
`changed-files.yaml`) and pass the result as a `deploy_website` input on
the **single** `auto-deploy-main` dispatch:

- New `website-changed-files` job checks `packages/twenty-website/**`.
- The existing `auto-deploy-main` dispatch now carries `-f
deploy_website=<true|false>`.
- This repo keeps triggering **only** `auto-deploy-main` — it never
dispatches `deploy-website` directly. `twenty-infra` decides whether to
run the website deploy (per @FelixMalfait's review: the public repo
shouldn't be able to run arbitrary `twenty-infra` workflows directly).
- Server/front deploy is **unchanged** — still every merge.

## Paired change & merge order

Companion PR: **twentyhq/twenty-infra#747** — adds the `deploy_website`
input to `auto-deploy-main` and gates the website dispatch on it.

**Merge twenty-infra#747 FIRST**, then this one. (If this merges first,
it would pass an input the old `auto-deploy-main` doesn't accept,
failing the dispatch. Infra-first only delays website auto-deploys until
this lands — no breakage.)
2026-06-22 21:23:26 +02:00
martmull 1646bdf35e Add twenty-partners on internal ci apps (#21975)
as title

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21975?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-06-22 21:08:40 +02:00
martmull 3030b7d0e5 Add people-data-labs on internal ci apps (#21941)
Add people-data-labs to internal apps CI: remove from
CI_EXCLUDED_APPLICATIONS and unify config with
twenty-discord/twenty-slack.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21941?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-06-22 13:36:33 +02:00
martmull 64385842bb Add twenty-linear on internal ci apps (#21942)
Add twenty-linear to internal apps CI: remove from
CI_EXCLUDED_APPLICATIONS and unify config with
twenty-discord/twenty-slack.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21942?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-06-22 13:14:56 +02:00
martmull 4b868f9b29 Add twenty-for-twenty on internal ci apps (#21944)
Add twenty-for-twenty to internal apps CI: remove from
CI_EXCLUDED_APPLICATIONS and unify config with
twenty-discord/twenty-slack.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21944?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-06-22 13:07:18 +02:00
martmull 8b9e3a0fc3 Add self-hosting on internal ci apps (#21940)
Add self-hosting to internal apps CI: remove from
CI_EXCLUDED_APPLICATIONS and unify config with
twenty-discord/twenty-slack.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21940?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-06-22 12:33:38 +02:00
martmull b354df3c59 Add twenty-fireflies on internal ci apps (#21939)
Add twenty-fireflies to internal apps CI: remove from
CI_EXCLUDED_APPLICATIONS and unify config with
twenty-discord/twenty-slack.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21939?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-06-22 12:30:45 +02:00
martmull cb5d64fefc Add twenty-exa application to internal app ci (#21882)
renamed exa to twenty-exa
add twenty-exa to ci check

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21882?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-06-22 11:36:33 +02:00
martmull 584c567a7f Add twenty-discord on internal ci apps (#21928)
add twenty-discord to ci check

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21928?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-06-22 11:05:43 +02:00
Charles Bochet 153e41e036 ci: block bot contributors from PR commit history (#21926)
## What

Adds a CI check (`Blocked Contributors Check`) that runs on every PR and
**fails** if any commit is attributed to a known bot — via the commit
author, committer, or a `Co-Authored-By:` trailer.

Goal: keep automated agents (Claude, Cursor, Copilot, …) out of Twenty's
contributor history.

## How

- On `pull_request` (`opened`, `synchronize`, `reopened`) it fetches all
PR commits via the GitHub API and matches author/committer name+email
and the full commit message (for trailers) against an editable
blocklist.
- Patterns target **bot identities** (emails / `[bot]` handles), **not**
bare first names — so a human contributor named "Claude" is *not*
flagged.
- On failure it emits `::error::` annotations naming the offending SHA +
what matched, plus remediation guidance (rebase with `--reset-author`,
strip trailers, force-push).

Current blocklist:

```
noreply@anthropic.com
@anthropic.com
cursoragent@cursor.com
copilot-swe-agent[bot]
```

Add a line to block another bot — no logic changes needed.

## Notes

- This workflow only *reports* a failed status. To actually block
merges, add **Blocked Contributors Check** as a required status check in
branch-protection rules for `main` (repo Settings → Branches).
- `@anthropic.com` also blocks any Anthropic-domain identity; narrow to
just `noreply@anthropic.com` if real Anthropic employees may contribute
under their work email.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21926?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-06-22 09:37:13 +02:00
martmull a870e034a6 Add twenty slack to internal application ci (#21849)
- adds `twenty-slack` to internal application ci 
- unify config with twenty-last-contact app
- add base oxlint config to show error twenty-shared is used in internal
app

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21849?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-06-19 14:39:42 +00:00
Abdul Rahman d19b7f8485 Enable getting started translations (#21842)
## Summary

The Getting Started pages on the docs site (docs.twenty.com) were only
ever available in English, never translated into the other supported
languages.

**Root cause:** The Getting Started section (added in #19728) was never
added to the Crowdin source config (`crowdin-docs.yml`), so its `.mdx`
files were never uploaded for translation. Only `user-guide`,
`developers`, and `twenty-ui` were configured.

This also surfaced a related bug: because the pages had no translations,
the navigation generator fell back to the English page path for every
language, duplicating paths like `getting-started/introduction` across
all 14 language navs. Mintlify treats duplicate cross-language paths as
undefined behavior, which broke the language switcher (it always
redirected to `/getting-started/introduction`).

## Changes

- `.github/crowdin-docs.yml` — add `getting-started/**/*.mdx` as a
translation source so the pages get sent to Crowdin.
- `packages/twenty-docs/scripts/fix-translated-links.sh` — add
`getting-started` link-rewriting rules to match the other sections.
- `packages/twenty-docs/scripts/generate-docs-json.ts` — only include a
page in a non-default language when its translated file exists; drop
empty groups/tabs (removes the duplicate cross-language paths that broke
the switcher).
- `packages/twenty-docs/docs.json` — regenerated.



<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21842?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. -->

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-19 14:16:17 +02:00
martmull 576f88b5c5 Update ci internal applications (#21837)
Fix `twenty-last-contact` ci

- add tests
- add tsgo
- update package commands

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21837?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-06-19 13:42:15 +02:00
martmull 8f7d7101ff Add application to test input in workflow (#21830)
https://github.com/twentyhq/twenty/pull/21791 follow up
2026-06-19 11:56:56 +02:00
martmull 5a6c02a7aa Create CI workflow for internal apps (#21791)
as title

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21791?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. -->

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 11:08:52 +02:00
Paul Rastoin b063c7850b Twenty app e2e app prod parity dispatch (#21750)
# Introduction

On main merge or if PR is labelled with specific label, dispatch the app
prod parity check

Note: for the moment not optimal for PR context as it will only set a
status check on the related commit which is not blocking anything for
the moment

related https://github.com/twentyhq/core-team-issues/issues/2557

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21750?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-06-18 08:49:21 +02:00
Charles Bochet 495907c781 ci: remove merge queue, run e2e on push to main (#21722)
## What

Removes the GitHub merge queue and runs what the queue used to gate —
the E2E (Playwright) suite — directly on push to `main`. If that run
fails on `main`, we ping engineering via webhook.

## Why

In the queue, only the `e2e-test` job in `ci-merge-queue.yaml` ran real
work — every other CI workflow's `merge_group` path skipped its
`changed-files-check` and tests, so the queue's status checks for those
were effectively green no-ops. The expensive thing actually gated was
E2E. Moving it to `push: main` validates the merged state post-merge
without the queue's batching overhead.

## Changes

- **Rename** `ci-merge-queue.yaml` → `ci-e2e-main.yaml` (`name: CI E2E
Main`).
- `e2e-test` now triggers on `push` to `main` (the `run-merge-queue` PR
label is kept as a manual opt-in for running E2E on a PR).
  - Status-check job renamed `ci-e2e-main-status-check`.
- New `notify-main-ci-failure` job: on a failed **main push**, `POST`s
to `https://engineering.twenty.com/s/main-ci-failing` with the commit
SHA, actor, and run URL.
- **Strip dead merge-queue config** from the other CI workflows: removed
the `merge_group:` triggers and the now-unreachable `if:
github.event_name != 'merge_group'` guards from `ci-server`,
`ci-shared`, `ci-sdk`, `ci-front-component-renderer`,
`ci-test-docker-compose`, `ci-website`, and the `merge_group:` trigger
from `ci-front`, `ci-ui`, `ci-new-ui`.

## Required follow-up (not in this PR)

The merge queue itself is a **repo setting**, not code. After this
merges, disable **"Require merge queue"** on the `main` ruleset/branch
protection (Settings → Rules), otherwise GitHub keeps batching. Required
status checks tied to the old queue should also be dropped/updated.

## Behavior change

E2E now runs **after** merge rather than blocking it in the queue — a
bad change lands on `main` and then alerts (the webhook is the
mitigation), instead of being held back pre-merge.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21722?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-06-17 18:33:07 +02:00
Raphaël Bosi 66c860574f Point New UI visual regression at the twenty-ui Argos project (#21728)
The `twenty-new-ui` Argos project is being renamed to `twenty-ui` (and
the old `twenty-ui` / `twenty-ui-vs-new-ui` projects removed).

Updates the New UI visual-regression flow to target `twenty-ui`: the
dispatch project mapping and the screenshot artifact name (which must
match `argos-screenshots-${project}`), plus the internal storybook
artifact name for consistency.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21728?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-06-17 14:27:39 +00:00
Raphaël Bosi 9c9c34fccf Remove twenty-ui-deprecated and migrate frontend to twenty-ui (#21596)
Migrates `twenty-front`, `twenty-sdk`, and
`twenty-front-component-renderer` from `twenty-ui-deprecated` to
`twenty-ui` (mechanical import swap — the packages have API parity) and
deletes the deprecated package along with its workspace/CI/config
wiring.

Also adds `@linaria/react`/`@linaria/core` as direct deps of
`twenty-front` (it used them transitively via the deprecated package).

Note: move the required status check from `ci-ui-status-check` to
`ci-new-ui-status-check`.

Argos: the Storybook box-model/button-reset baseline shift (the bulk of
the visual diffs) is isolated in #21665 — Storybook now loads
twenty-ui's global `reset.scss`, which the production app already ships.
Once #21665 merges and this branch is rebased, the remaining Argos diffs
are component-level visual-parity items only.
2026-06-17 09:41:11 +00:00
Paul Rastoin d5c7b735d2 ci: migrate cross-repo dispatch senders to workflow_dispatch (actions:write) (#21648)
# Introduction
Getting rid of the fine grained PAT used to dispatch to internal
repositories.
Repo dispatch requires the contents write permissions which is too wide
for such use
Refactored all senders and target to pass through a workflow dispatch
instead
Creating a centralize app that forges a token with actions: write only
provided permissions to mitigate any token exfiltrations
2026-06-16 15:18:43 +02:00
nitin 12b1dba986 Add call recording scheduling backend (#21629)
This PR adds the backend scheduling slice for call recording. It wires
the `twenty-meeting-bot` internal app to reconcile calendar events,
calendar-channel associations, and workspace member auto-record
preference changes, then schedule, cancel, or reschedule Recall bots
based on the resulting policy.

It also adds the needed calendar-channel owner lookup support, generated
metadata updates, app config/default role updates, unit tests, and CI
for the internal app.

Coming next:
- Recall webhook handling and signature validation
- Stale-state convergence for failed Recall cleanup/recreate cases
- Media, transcript, audio, and video ingestion
- Billing charge flow
- Frontend/settings UI for recording controls

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21629?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-06-16 08:47:09 +00:00
Charles Bochet 97871131a1 fix(server): mitigate integration-test OOM flakiness (#21588)
## Problem

`server-integration-test` shards have been failing intermittently across
unrelated PRs with a distinctive signature: the shard exits code 1 with
**no jest assertion failure, no `Test Suites:` summary, and no V8
`JavaScript heap out of memory` error** — the process just dies mid-run.
Failures hit random shards and clear on re-run (e.g. an unrelated branch
failed shard 6 once, then passed 3× on identical code), while the
`merge_group` gate stays green.

### Root cause

Each shard runs a **single in-band jest process** that boots one shared
NestJS app (`globalSetup` → `app.listen`) and holds it for the entire
shard, driving heavy metadata migrations + cache rebuilds in that one
process. `NODE_OPTIONS=--max-old-space-size=12288` let V8 grow to 12 GB
— *above* the `ubuntu-latest` runner's available RAM (16 GB, shared with
Postgres/Redis/ClickHouse). V8 therefore deferred aggressive GC and grew
past physical memory, so the **OS OOM-killer killed the process before
V8 hit its own ceiling** — which is why there's no heap error and no
jest summary, just a silent exit.

## Changes (CI/test-only — prod runtime untouched)

- **Lower the integration jest heap cap `12288` → `6144`** so V8
self-limits below physical headroom instead of being OS-killed.
Counterintuitively safer: a real leak now surfaces as a *visible* heap
error naming the test, rather than a silent death. (`database:reset`
keeps 12288 — it runs alone, before jest.)
- **Add `--logHeapUsage`** to the integration jest runs to expose the
per-file heap trend for confirming/pinpointing the growth.
- **Split integration tests across 16 shards (was 10)** to lower the
peak working set per shard.
- **Make perf logging a first-class `LoggerService` tool** (per
@prastoin's review): add `LoggerService.perf()` and unify the existing
`time()`/`timeEnd()` helpers into `perfTime()`/`perfTimeEnd()` (now
routed through the driver), all gated by a new `PERF_LOG_ENABLED` config
var. It **defaults on** so real environments keep emitting the
install-perf logs, and `.env.test` sets it `false` to mute the
per-action flood in integration tests. The `application-manifest` and
`validate-build` services were moved from the built-in `Logger` to
`LoggerService` to use it.

## Notes

- `--max-old-space-size` lives only in the `test:integration` nx target;
it is **not** the prod server heap setting, so prod is unaffected.
- This is mitigation. If `--logHeapUsage` shows monotonic growth across
files, there's a real accumulation in the long-lived app (retained
flat-maps / metadata cache) worth a follow-up heap-snapshot fix.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21588?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-06-15 15:54:51 +02:00
Charles Bochet 6a6fe81d3a chore(deps): ignore chokidar in dependabot (#21564)
## Context

Dependabot keeps proposing a chokidar v3 to v4 bump (e.g. #21555), but
chokidar is intentionally pinned to v3 via a `resolutions` entry in the
root `package.json`.

chokidar v4 removed the native macOS FSEvents backend and falls back to
`node:fs.watch`, which hits `EMFILE` on a repo this size. That was
diagnosed and fixed in #20316. The bump can't even be satisfied while
the resolution is in place, so the lockfile and `package.json` end up
inconsistent and CI goes red.

## Change

Add `chokidar` to the dependabot ignore list so it stops re-proposing
the bump every cycle. The pin stays until upstream restores a native
watcher backend.

Closes out #21555.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21564?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-06-14 23:20:04 +02:00
Charles Bochet 47b31acc21 ci: exclude all twenty-apps from dependabot (#21554)
Dependabot currently only excludes `packages/twenty-apps/community/**`,
but the `twenty-apps` package also contains `examples`, `fixtures`, and
`internal` apps that shouldn't be picked up either.

This broadens the `exclude-paths` to `packages/twenty-apps/**` so
Dependabot ignores apps entirely.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21554?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-06-14 20:39:17 +02:00
Charles Bochet 9884769ce4 fix(ci): bump claude-code-action to v1.0.146 (CVE-2026-47751) (#21499)
## What

Bumps both \`anthropics/claude-code-action\` pins in
\`.github/workflows/claude.yml\` from the floating \`v1\` SHA
(\`dde2242\`) to \`v1.0.146\` (\`ac7e24b\`).

## Why

The old pin predates the fix for **CVE-2026-47751** (Tenable
TRA-2026-27, CVSSv4 5.3): claude-code-action checks out the PR head
branch and unconditionally sets \`enableAllProjectMcpServers: true\`, so
an attacker could ship a malicious \`.mcp.json\` in a PR branch and get
arbitrary command execution in the runner — with access to workflow
secrets — once a privileged user triggers the action.

Fixed upstream in **claude-code-action 1.0.78** (released 2026-03-24).
This pins to the current release, v1.0.146.

## Notes
- \`claude.yml\` is the only workflow in the repo referencing the
action; both job invocations (\`claude\` and \`claude-cross-repo\`) were
updated.
- All existing \`with:\` inputs remain valid in v1.0.146 — no deprecated
args.

Ref: https://www.tenable.com/security/research/tra-2026-27

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21499?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-06-12 18:07:57 +02:00
Félix Malfait 79f7c96939 Move Claude workflow concurrency to job level so PR chatter can't cancel queued runs (#21498)
## Problem

A second `@claude` request on a PR while a Claude run is already in
progress gets silently cancelled. Concrete case on #21443: [this
comment](https://github.com/twentyhq/twenty/pull/21443#discussion_r3404037004)
("@claude please investigate and report", 14:24:28) produced [a
run](https://github.com/twentyhq/twenty/actions/runs/27421867558) that
was cancelled 3 seconds after creation, while [the run for an earlier
comment](https://github.com/twentyhq/twenty/actions/runs/27421856742)
was still in progress. Claude never responded.

## Root cause

The `concurrency` block is declared at the **workflow** level, keyed on
the PR number. Two GitHub Actions behaviors combine badly here:

1. A concurrency group holds at most one running + **one pending** run;
every new run entering the group cancels the previously pending one
(`cancel-in-progress: false` only protects the *running* run).
2. Workflow-level concurrency is acquired **before** job-level `if`
conditions are evaluated — so every `issue_comment` /
`pull_request_review_comment` / `pull_request_review` event on the PR
enters the group, even ones with no `@claude` that end up skipped.

So while a Claude run is in progress, any PR activity evicts the queued
`@claude` run. It's even self-defeating: a review-thread reply fires
*two* webhook events (`pull_request_review_comment` + a companion
`pull_request_review` with an empty body), so the companion event
cancels the queued comment run ~2s later. Claude's own "finished" reply
also fires events that kill whatever is queued.

## Fix

Move concurrency to the **job** level. A job whose `if` evaluates false
is skipped before it ever requests the concurrency slot, so only genuine
`@claude` jobs enter the queue. Real Claude runs on the same PR are
still serialized (no parallel pushes to the same branch).

Also gives `claude-cross-repo` its own group keyed on source repo +
issue number — previously a cross-repo dispatch for issue N shared a
group with PR N in this repo and they could needlessly queue behind each
other.

## Remaining limitation

GitHub keeps only one pending job per group: posting three `@claude`
requests while the first is still running will still cancel the second
when the third arrives. Zero-loss queueing would require a unique group
per comment, which would allow concurrent runs pushing to the same PR
branch — not worth it.

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

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21498?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-06-12 17:19:19 +02:00
Raphaël Bosi c4453923f0 Update CI: Argos visual regression for twenty-front storybook (#21454)
## What

Adds Argos visual regression for `twenty-front`, reusing the storybook
CI already builds and the existing sharded test matrix. Stories in the
`modules` and `pages` scopes are captured as PNGs during
`front-sb-test`, merged into one artifact, and pixel-diffed against
`main` on the self-hosted Argos with results posted as a PR comment —
same pipeline as `twenty-ui` (#21210 / #21262).

## How

- **Capture**: `@argos-ci/storybook` vitest plugin, same setup as
`twenty-ui`. Skipped for `performance` stories (nondeterministic
profiling reports). Freezes framer-motion to avoid flaky diffs (#21412).
- **Sharding**: each modules/pages shard uploads a partial artifact; a
new `front-sb-screenshots` job merges them into
`argos-screenshots-twenty-front` (`overwrite: true` so re-runs work).
- **Baselines**: `CI Front` now runs on `push: main` — Argos resolves
base builds by exact merge-base commit, so every main commit needs a
build (#21217/#21222 pattern). Main pushes get a per-SHA concurrency
group so back-to-back merges can't cancel queued runs and leave baseline
gaps; the `performance` scope is dropped on push.
- **Dispatch**: `visual-regression-dispatch.yaml` watches `CI Front` →
`project=twenty-front`.

## Rollout

-  Prod Argos project `twenty-front` created (id 68) +
`ARGOS_TOKEN_FRONT` secret set
-  Merge the twentyhq/ci-privileged companion PR **before** this one
- First PR builds show as *orphan* until the first main push creates a
baseline
  (expected, same as the twenty-ui rollout)
2026-06-12 13:36:16 +00:00
Raphaël Bosi 56e1d886d8 Add CI workflow for people data labs app (#21487)
Adds a dedicated CI workflow for the People Data Labs app that runs
lint, typecheck, and unit tests only when files under
`packages/twenty-apps/internal/people-data-labs/` change.

The app is a standalone yarn project (its own lockfile, pinned published
SDKs), so it isn't covered by the existing package CI workflows. The
workflow gates on the app folder via the reusable `changed-files.yaml`,
installs dependencies inside the app directory, and runs `yarn lint`,
`yarn typecheck`, and `yarn test`. A status-check job aggregates results
and stays green on skips, so it's safe to mark as a required check.


<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21487?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-06-12 12:55:47 +00:00
Raphaël Bosi c596a5e342 Rename twenty-ui to twenty-ui-deprecated and twenty-new-ui to twenty-ui to prepare package release (#21315)
## Description

Promotes the next-gen UI library (formerly `twenty-new-ui`) to the name
**`twenty-ui`** (v0.1.0, publishable) and renames the old package to
**`twenty-ui-deprecated`**. Rewrites ~1,730 `twenty-ui` imports →
`twenty-ui-deprecated`, updates all configs/CI/Docker/deps, and migrates
twenty-front's `Toggle` to the new package (first consumer) as a
drop-in.

## Next steps
- Wire the `ui/v*` publish dispatch (`cd-deploy-tag.yaml` +
`.yarnrc.yml`), then tag `ui/v0.1.0` to publish.
- Continue migrating components from `twenty-ui-deprecated` →
`twenty-ui`.
2026-06-08 18:12:28 +02:00
Charles Bochet b0d29689d2 feat: add Argos visual regression for twenty-new-ui (#21262)
## Summary

- Add `ci-new-ui.yaml` workflow mirroring `ci-ui.yaml` for
`twenty-new-ui` (lint, typecheck, test, Storybook build, Storybook test
with screenshot capture)
- Update `visual-regression-dispatch.yaml` to watch both `CI UI` and `CI
New UI` workflows, with dispatches for three Argos projects:
  1. **Self-hosted twenty-ui pixel diff** (existing, unchanged)
2. **Self-hosted twenty-new-ui pixel diff** (new) -- standard regression
against main
3. **Self-hosted twenty-ui vs twenty-new-ui comparison** (new) --
cross-package visual parity
- Add "Visual regression" documentation section to
`twenty-new-ui/README.md` with local dev workflow (argos-tunnel via
super CLI + `storybook:visual-diff`)
- Resolve open question 4 in README (Argos confirmed as visual
regression tool)

## Companion PR

Requires companion PR on `twentyhq/ci-privileged` for the new
`visual-regression-cloud` dispatch handler, upload script, and
post-comment logic.

## Manual setup

After merging both PRs, create two Argos projects on
argos.twenty-internal.com:
- `twenty-new-ui` (pixel diff)
- `twenty-ui-vs-new-ui` (cross-package comparison, auto-approved branch:
main)

Add secrets `ARGOS_TOKEN_NEW_UI` and `ARGOS_TOKEN_COMPARISON` to
ci-privileged.

## Test plan

- [ ] Verify `CI New UI` workflow triggers on twenty-new-ui changes
- [ ] Verify screenshot artifact `argos-screenshots-twenty-new-ui` is
uploaded
- [ ] Verify dispatch sends `visual-regression-cloud` events to
ci-privileged
- [ ] Verify existing `CI UI` → self-hosted Argos flow is unchanged
2026-06-08 13:04:52 +02:00
Paul Rastoin a78b319877 sdk/v* twenty/v* namespaces (#21247)
# Introduction
Removed never used release dispatch workflow
Now assuming that anyone releasing will create both twenty and npm
family tags
Will create a workflow to ease this later

We will now start to have several github releases, one per namespace
2026-06-05 12:40:50 +00:00
Charles Bochet cd540098f1 fix: pass reference_commit to Argos to resolve orphan PR builds (#21245)
## Summary

- Fixes Argos CI builds showing as "Orphan" (no reference branch) for PR
builds
- Computes the merge-base SHA between the PR head and `main` using the
GitHub API (`compareCommitsWithBasehead`) in the dispatch workflow
- Passes `reference_commit` in the `ci-privileged` dispatch payload so
it can be forwarded to the Argos upload API

## Context

PR builds on Argos were showing as "Orphan" because `ci-privileged`
(where the actual Argos upload happens) has no git history of the
`twenty` repo — it cannot compute the merge-base locally. Without a
`referenceCommit`, Argos can't determine which `main` build to compare
against.

The local `visual-diff.sh` script already passes
`ARGOS_REFERENCE_COMMIT` via `git merge-base HEAD main`, but the CI
pipeline was missing this. This PR adds equivalent logic using the
GitHub API (no checkout needed).

## Note for ci-privileged

The `upload-to-argos.ts` script in `ci-privileged` needs a corresponding
update to read `reference_commit` from the dispatch payload and pass it
as `referenceCommit` in the Argos API call:

```typescript
referenceCommit: process.env.REFERENCE_COMMIT || undefined,
```

## Test plan

- [ ] Verify the workflow runs successfully on a PR (merge-base step
computes a SHA)
- [ ] Confirm Argos PR builds are no longer marked as "Orphan" after the
ci-privileged counterpart is updated
2026-06-05 10:51:16 +02:00
Charles Bochet badeaddd37 fix(ci): ensure ui-sb-test runs on push to main (#21222)
## Summary

- Add explicit `if: always() && needs.ui-sb-build.result == 'success'`
to `ui-sb-test` job

## Context

After merging #21217, the main-branch Argos baseline pipeline doesn't
work: `ui-sb-test` is silently skipped on push to main, so no
`argos-screenshots-twenty-ui` artifact is produced.

**Root cause:** `changed-files-check` is skipped on push events
(PR-only). `ui-sb-build` handles this with `if: always() && ...`, but
`ui-sb-test` has no explicit `if` — GitHub Actions propagates the skip
through the transitive dependency chain (`changed-files-check` →
`ui-sb-build` → `ui-sb-test`).

## Test plan

- Merge this PR and verify the next push to main produces the
`argos-screenshots-twenty-ui` artifact
- Verify `dispatch-main` successfully triggers ci-privileged with the
artifact
2026-06-04 15:11:38 +02:00
Charles Bochet 9042e8a542 feat(ci): Argos main baselines + local visual diff support (#21217)
## Summary

**CI: Main-branch Argos baselines**
- Run storybook build + screenshot capture on `push` to `main` in CI UI
workflow
- Add `dispatch-main` job in visual regression dispatch to forward
main-branch screenshots to ci-privileged
- Simplify `dispatch-pr` by inlining the artifact name and removing
unused `project` output

**Local visual diff support**
- Add `scripts/visual-diff.sh` for running Argos uploads locally via
tunnel
- Add `storybook:visual-diff` Nx target wrapping the script (depends on
`storybook:build`)
- Honor `STORYBOOK_URL` env in `vitest.config.ts` to reuse pre-served
static builds (mirrors twenty-front pattern)
- Support `ARGOS_BUILD_NAME`, `ARGOS_REFERENCE_BRANCH` env overrides in
vitest plugin config

## Context

Argos builds on PRs are all "Orphan" because there's no reference build
on `main` to compare against. The CI changes add the missing piece:
every merge to main now produces screenshots and uploads them to Argos
as reference builds.

The local visual diff script enables developers to run visual regression
checks from their machine against the self-hosted Argos instance via
`kubectl port-forward` (set up by the twenty-infra `argos-tunnel`
command).

## Related

- twentyhq/twenty-argos#1 (backend config for self-hosted HTTPS
redirect)
- twentyhq/twenty-infra#709 (argos-tunnel super CLI command +
self-hosted mode)

## Test plan

- [ ] Verify CI UI runs on next push to main and produces the
`argos-screenshots-twenty-ui` artifact
- [ ] Verify `dispatch-main` triggers and uploads screenshots to Argos
- [ ] Verify subsequent PR builds show diffs against the main baseline
instead of "Orphan"
- [ ] Run `ARGOS_TOKEN=<token> npx nx storybook:visual-diff twenty-ui`
locally with tunnel active
2026-06-04 14:55:08 +02:00
Charles Bochet b53f1832d8 feat(ci): simplify visual regression dispatch to twenty-ui only (#21215)
## Summary

- Only trigger visual regression on `CI UI` workflow (drop `CI Front`)
- Remove tarball re-packaging step — `ci-privileged` now downloads the
artifact directly via GitHub API
- Remove `mode`/`project` parameters from the dispatch payload
(hardcoded to twenty-ui in ci-privileged)
- Pass `run_id` of the triggering CI UI workflow so ci-privileged can
fetch the correct artifact

## Context

Part of the fast visual regression CI initiative. The `ci-privileged`
workflow has been simplified to only handle `twenty-ui` screenshots
uploaded directly to Argos.

## Test plan

- [x] Full E2E verified on production: screenshots → Argos build → diff
results → PR comment
2026-06-04 13:49:44 +02:00
Félix Malfait 2ac515894b feat(settings): add Logs as a dedicated tab in General settings (#21180)
## What & why

The audit-log viewer lived as a full-screen page reachable only via a
"View Logs" button buried in the **Security** tab. This surfaces it as
the **third tab in General settings** (`General | Security | Logs`),
consistent with the other tabs.

## Changes

- **Relocated** the event-logs module
`pages/settings/security/event-logs/` → `modules/settings/event-logs/`
and render it as tab content instead of a `FullScreenContainer` page.
Dropped `SettingsPath.EventLogs`, its route, and the fullscreen handling
in favor of the `general#logs` hash tab.
- **Security tab:** removed the "View Logs" entry; kept the
log-retention setting there.
- **In-tab gating** (shown to users with the Security permission):
Enterprise upgrade card when not entitled, a clear "ClickHouse not
configured" placeholder otherwise (derived from client config), and the
query is skipped when disabled. Replaces a bespoke error component that
string-matched error messages with the shared `SettingsEmptyPlaceholder`
/ `SettingsEnterpriseFeatureGateCard`.
- **Layout:** boxed content column with the table selector + filters
grouped in a `Card` and the results table below, matching settings
conventions. Kept the existing fixed filters (page/event name, member,
period) rather than recreating the record-view filter chips (those are
tightly coupled to record/view context).

Frontend + `twenty-shared` only — no changes to the log query or data.

## Test plan

- [x] `npx nx typecheck twenty-front` and `npx nx lint twenty-front`
pass
- [x] Settings → General shows three tabs; Logs is the third; breadcrumb
stays "Workspace / General"
- [x] With Enterprise + ClickHouse: table selector, filters, refresh,
and the paginated table work
- [x] Non-Enterprise: Enterprise upgrade card shown; no failing query
fires
- [ ] Enterprise without ClickHouse: shows the "ClickHouse not
configured" placeholder
- [ ] Security tab still shows the log-retention setting and the "View
Logs" button is gone
- [ ] A user without the Security permission sees neither the Security
nor Logs tab
2026-06-04 08:47:23 +02:00
Charles Bochet e6614299c6 feat(ci): integrate Argos visual regression via vitest screenshots (#21210)
## Summary

- Adds `@argos-ci/storybook` vitest plugin to `twenty-ui` for automatic
screenshot capture during vitest storybook tests
- Uploads captured screenshots (PNG, ~5MB) as a CI artifact instead of
passing the full storybook build
- Updates the visual regression dispatch workflow to pass
`mode=argos-screenshots` to ci-privileged, which then uploads
screenshots to Argos via CLI

This replaces the 10-minute Storybook screenshot capture with a ~30s
vitest browser-mode approach. The heavy screenshot work happens on free
public runners, while ci-privileged only handles the Argos API upload
(keeping secrets private).

## Architecture

```
twenty (public, free runners)          ci-privileged (private)
─────────────────────────────          ────────────────────────
1. Build storybook-static              4. Download screenshots artifact
2. Vitest captures screenshots         5. `argos upload` → Argos API
3. Upload screenshots artifact         6. Poll for results
                                       7. Post PR comment
```

## Test plan

- [x] Verified locally: vitest captures 225 screenshots in ~28s
- [x] Verified `@argos-ci/cli upload` successfully creates Argos build
from captured screenshots
- [x] Argos diffs computed and results visible via API
- [ ] CI runs end-to-end on a PR
2026-06-04 08:46:37 +02:00
martmull 0671ff3de5 Fix lambda error (#21179)
- move twenty-sdk from dependencies to devDependencies
- add documentation about breaking
- add warning about moving the package to dev dependencies
2026-06-03 16:45:51 +00:00
Thomas des Francs 1642be86f5 Bonapara/twenty codex plugin (#20857)
@martmull v2.0 ;)

---------

Co-authored-by: martmull <martmull@hotmail.fr>
Co-authored-by: bosiraphael <raphael.bosi@gmail.com>
2026-06-02 14:39:14 +00:00
Weiko 9b54200d8c Fix playwright CI (#21024)
## Context
The Install Playwright step ran npx playwright install with no
arguments, which downloads all browsers (Chromium + Firefox + WebKit +
ffmpeg, ~500MB+) on every run with no caching.

Fix:
- Install Chromium only — npx playwright install chromium instead of all
browsers.
- Cache the browser binaries — actions/cache on ~/.cache/ms-playwright,
keyed on the resolved Playwright version (v4-playwright-browsers-${{
runner.os }}-<version>). On a cache hit the install step is skipped
entirely; the cache invalidates automatically when the Playwright
version bumps.
2026-05-28 14:19:58 +00:00
Charles Bochet 2f4ebf8160 Move preview environment workflow to ci-privileged (#20872)
## Summary

Slims `preview-env-dispatch.yaml` to a single dispatch and deletes
`preview-env-keepalive.yaml`. The actual preview-env work moves to
**twentyhq/ci-privileged#22** (must merge as a pair).

## Why

Context: PR #20867 was a credential-exfil attempt against our workflows.
GitHub's default fork-PR-no-secrets policy + our existing gates
(`author_association` checks, `pull_request_target` checking out base,
`enableScripts: false`) neutralized the actual attack — but the audit
surfaced one workflow that *would* have given a malicious external PR
access to a real secret if a maintainer had applied the `preview-app`
label: `preview-env-keepalive.yaml`.

That workflow checked out the PR head SHA, did `docker login` with
`DOCKERHUB_PASSWORD`, then ran the PR's `docker-compose.yml`. A
malicious compose could have mounted `~/.docker/config.json` and
exfiltrated the Dockerhub credential.

After this PR, that workflow lives in `twentyhq/ci-privileged` instead,
paired with a rename of the credential to `DOCKERHUB_RO_TOKEN`
(Dockerhub PAT with `Public Repo Read-only` scope). A read-only PAT has
no exfiltration value — it's equivalent to anonymous Dockerhub access
plus rate-limit headroom — so the credential lives safely on the runner
without further hygiene tricks.

## What this PR does

- **Modifies** `.github/workflows/preview-env-dispatch.yaml`:
- Single dispatch to `twentyhq/ci-privileged` (was: self-dispatch to
twenty for the env + a separate dispatch to ci-privileged for the PR
comment).
  - `permissions: {}` (was: `contents: write`).
  - Drops `preview-env-keepalive.yaml` from the path-trigger list.
- **Deletes** `.github/workflows/preview-env-keepalive.yaml`. The
207-line workflow now lives in
`twentyhq/ci-privileged/.github/workflows/preview-env.yaml`.

Net `twenty` repo change: **-204 lines / +3 lines**.

## Companion PR

twentyhq/ci-privileged#22 — adds the new `preview-env.yaml`, deletes the
now-redundant `post-preview-comment.yaml`.

## Secrets fallout in this repo

After this PR, `DOCKERHUB_PASSWORD` in `twentyhq/twenty` secrets is only
used by `ci-test-docker-compose.yaml`, where:
- It evaluates to empty for fork PRs (GitHub default — secrets aren't
passed to fork-PR workflows).
- It's only needed for internal / merge_queue runs, for Dockerhub
rate-limit headroom on base-image pulls.

Recommend (separate change): also convert the twenty-side
`DOCKERHUB_PASSWORD` to a `Public Repo Read-only` Dockerhub PAT, and
rename it to `DOCKERHUB_RO_TOKEN` for consistency with ci-privileged.
The workflow change for `ci-test-docker-compose.yaml` would just be a
rename — login flow is identical for password vs. PAT.

## Test plan

- [ ] Merge twentyhq/ci-privileged#22 first (so the dispatched event has
a handler)
- [ ] Open an internal PR touching `packages/twenty-docker/**`, confirm
`Preview Environment Dispatch` runs and ci-privileged's `Preview
Environment` workflow runs the docker compose + posts the URL
- [ ] On an external contributor PR, apply the `preview-app` label,
confirm the same flow
- [ ] Confirm closing the PR doesn't break (no cleanup workflow was
changed)
2026-05-23 11:37:37 +00:00
martmull 056e3a4cd8 Add check for breaking api changes (#20848)
- update ci-breaking-changes.yaml so it check for api contrat breaks
- check fails properly when removing fix
https://github.com/twentyhq/twenty/pull/20825
- check it turns green again when adding fix back
2026-05-23 08:50:05 +00:00
martmull 237a943947 Update twenty sdk commands (#20735)
Performs twenty-sdk cli command migration:

Summary

``` ┌─────┬──────────────────────────┬────────────────────────────┬───────────────────────┐
 │  #  │       Old command        │        New command         │        Status         │
 ├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
 │ 1   │ twenty dev [appPath]     │ twenty dev [appPath]       │ Unchanged (now also   │
 │     │                          │                            │ DEFAULT)              │
 ├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
 │ 2   │ twenty dev --once        │ twenty dev --once          │ Unchanged             │
 │     │ [appPath]                │ [appPath]                  │                       │
 ├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
 │ 3   │ twenty dev --watch       │ twenty dev [appPath]       │ --watch flag removed  │
 │     │ [appPath]                │                            │ (was default)         │
 ├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
 │ 4   │ twenty dev --verbose     │ twenty dev --verbose       │ Unchanged             │
 │     │ [appPath]                │ [appPath]                  │                       │
 ├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
 │ 5   │ twenty dev --debug       │ twenty dev --debug         │ Unchanged             │
 │     │ [appPath]                │ [appPath]                  │                       │
 ├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
 │ 6   │ twenty dev --debounceMs  │ twenty dev --debounceMs    │ Unchanged             │
 │     │ <ms> [appPath]           │ <ms> [appPath]             │                       │
 ├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
 │ 7   │ twenty build [appPath]   │ twenty dev:build [appPath] │ Deprecated → colon    │
 │     │                          │                            │ command               │
 ├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
 │ 8   │ twenty build --tarball   │ twenty dev:build --tarball │ Deprecated → colon    │
 │     │ [appPath]                │  [appPath]                 │ command               │
 ├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
 │ 9   │ twenty typecheck         │ twenty dev:typecheck       │ Deprecated → colon    │
 │     │ [appPath]                │ [appPath]                  │ command               │
 ├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
 │ 10  │ twenty logs [appPath]    │ twenty dev:fn-logs         │ Deprecated → colon    │
 │     │                          │ [appPath]                  │ command               │
 ├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
 │ 11  │ twenty logs -n <name>    │ twenty dev:fn-logs -n      │ Deprecated → colon    │
 │     │ [appPath]                │ <name> [appPath]           │ command               │
 ├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
 │ 12  │ twenty logs -u <id>      │ twenty dev:fn-logs -u <id> │ Deprecated → colon    │
 │     │ [appPath]                │  [appPath]                 │ command               │
 ├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
 │ 13  │ twenty exec [appPath]    │ twenty dev:fn-exec         │ Deprecated → colon    │
 │     │                          │ [appPath]                  │ command               │
 ├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
 │ 14  │ twenty exec -n <name>    │ twenty dev:fn-exec -n      │ Deprecated → colon    │
 │     │ [appPath]                │ <name> [appPath]           │ command               │
 ├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
 │ 15  │ twenty exec -u <id>      │ twenty dev:fn-exec -u <id> │ Deprecated → colon    │
 │     │ [appPath]                │  [appPath]                 │ command               │
 ├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
 │ 16  │ twenty exec -p <json>    │ twenty dev:fn-exec -p      │ Deprecated → colon    │
 │     │ [appPath]                │ <json> [appPath]           │ command               │
 ├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
 │ 17  │ twenty exec              │ twenty dev:fn-exec         │ Deprecated → colon    │
 │     │ --postInstall [appPath]  │ --postInstall [appPath]    │ command               │
 ├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
 │ 18  │ twenty exec --preInstall │ twenty dev:fn-exec         │ Deprecated → colon    │
 │     │  [appPath]               │ --preInstall [appPath]     │ command               │
 ├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
 │ 19  │ twenty add [entityType]  │ twenty dev:add             │ Deprecated → colon    │
 │     │                          │ [entityType]               │ command               │
 ├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
 │ 20  │ twenty add --path <path> │ twenty dev:add --path      │ Deprecated → colon    │
 │     │  [entityType]            │ <path> [entityType]        │ command               │
 └─────┴──────────────────────────┴────────────────────────────┴───────────────────────┘

 App lifecycle commands

 ┌─────┬────────────────────────┬────────────────────────────┬─────────────────────────┐
 │  #  │      Old command       │        New command         │         Status          │
 ├─────┼────────────────────────┼────────────────────────────┼─────────────────────────┤
 │ 21  │ twenty publish         │ twenty app:publish         │ Deprecated → colon      │
 │     │ [appPath]              │ [appPath]                  │ command                 │
 ├─────┼────────────────────────┼────────────────────────────┼─────────────────────────┤
 │ 22  │ twenty publish --tag   │ twenty app:publish --tag   │ Deprecated → colon      │
 │     │ <tag> [appPath]        │ <tag> [appPath]            │ command                 │
 ├─────┼────────────────────────┼────────────────────────────┼─────────────────────────┤
 │ 23  │ twenty deploy          │ twenty app:publish         │ Deprecated → colon      │
 │     │ [appPath]              │ --private [appPath]        │ command + --private     │
 ├─────┼────────────────────────┼────────────────────────────┼─────────────────────────┤
 │ 24  │ twenty install         │ twenty app:install         │ Deprecated → colon      │
 │     │ [appPath]              │ [appPath]                  │ command                 │
 ├─────┼────────────────────────┼────────────────────────────┼─────────────────────────┤
 │ 25  │ twenty uninstall       │ twenty app:uninstall       │ Deprecated → colon      │
 │     │ [appPath]              │ [appPath]                  │ command                 │
 ├─────┼────────────────────────┼────────────────────────────┼─────────────────────────┤
 │ 26  │ twenty uninstall -y    │ twenty app:uninstall -y    │ Deprecated → colon      │
 │     │ [appPath]              │ [appPath]                  │ command                 │
 └─────┴────────────────────────┴────────────────────────────┴─────────────────────────┘

 Server commands

 ┌─────┬─────────────────────────┬─────────────────────────────┬──────────────────────┐
 │  #  │       Old command       │         New command         │        Status        │
 ├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
 │ 27  │ twenty server start     │ twenty docker:start         │ Deprecated → colon   │
 │     │                         │                             │ syntax               │
 ├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
 │ 28  │ twenty server start -p  │ twenty docker:start -p      │ Deprecated → colon   │
 │     │ <port>                  │ <port>                      │ syntax               │
 ├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
 │ 29  │ twenty server start     │ twenty docker:start --test  │ Deprecated → colon   │
 │     │ --test                  │                             │ syntax               │
 ├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
 │ 30  │ twenty server stop      │ twenty docker:stop          │ Deprecated → colon   │
 │     │                         │                             │ syntax               │
 ├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
 │ 31  │ twenty server stop      │ twenty docker:stop --test   │ Deprecated → colon   │
 │     │ --test                  │                             │ syntax               │
 ├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
 │ 32  │ twenty server status    │ twenty docker:status        │ Deprecated → colon   │
 │     │                         │                             │ syntax               │
 ├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
 │ 33  │ twenty server status    │ twenty docker:status --test │ Deprecated → colon   │
 │     │ --test                  │                             │ syntax               │
 ├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
 │ 34  │ twenty server logs      │ twenty docker:logs          │ Deprecated → colon   │
 │     │                         │                             │ syntax               │
 ├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
 │ 35  │ twenty server logs -n   │ twenty docker:logs -n       │ Deprecated → colon   │
 │     │ <lines>                 │ <lines>                     │ syntax               │
 ├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
 │ 36  │ twenty server logs      │ twenty docker:logs --test   │ Deprecated → colon   │
 │     │ --test                  │                             │ syntax               │
 ├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
 │ 37  │ twenty server reset     │ twenty docker:reset         │ Deprecated → colon   │
 │     │                         │                             │ syntax               │
 ├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
 │ 38  │ twenty server reset     │ twenty docker:reset --test  │ Deprecated → colon   │
 │     │ --test                  │                             │ syntax               │
 ├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
 │ 39  │ twenty server upgrade   │ twenty docker:upgrade       │ Deprecated → colon   │
 │     │ [version]               │ [version]                   │ syntax               │
 ├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
 │ 40  │ twenty server upgrade   │ twenty docker:upgrade       │ Deprecated → colon   │
 │     │ --test [version]        │ --test [version]            │ syntax               │
 ├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
 │ 41  │ twenty server           │ twenty app:catalog-sync  │ Deprecated → colon   │
 │     │ catalog-sync            │                             │ syntax               │
 ├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
 │ 42  │ twenty server           │ twenty app:catalog-sync  │ Deprecated → colon   │
 │     │ catalog-sync -r <name>  │ -r <name>                   │ syntax               │
 ├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
 │ 43  │ twenty catalog-sync     │ (removed)                   │ Removed (was already │
 │     │                         │                             │  deprecated)         │
 └─────┴─────────────────────────┴─────────────────────────────┴──────────────────────┘

 Remote commands

 ┌─────┬────────────────────────┬──────────────────────────┬──────────────────────────┐
 │  #  │      Old command       │       New command        │          Status          │
 ├─────┼────────────────────────┼──────────────────────────┼──────────────────────────┤
 │ 44  │ twenty remote add      │ twenty remote:add        │ Deprecated → colon       │
 │     │                        │                          │ syntax                   │
 ├─────┼────────────────────────┼──────────────────────────┼──────────────────────────┤
 │ 45  │ twenty remote add --as │ twenty remote:add --as   │ Deprecated → colon       │
 │     │  <name>                │ <name>                   │ syntax                   │
 ├─────┼────────────────────────┼──────────────────────────┼──────────────────────────┤
 │ 46  │ twenty remote add      │ twenty remote:add        │ Deprecated → colon       │
 │     │ --api-key <key>        │ --api-key <key>          │ syntax                   │
 ├─────┼────────────────────────┼──────────────────────────┼──────────────────────────┤
 │ 47  │ twenty remote add      │ twenty remote:add        │ Deprecated → colon       │
 │     │ --api-url <url>        │ --api-url <url>          │ syntax                   │
 ├─────┼────────────────────────┼──────────────────────────┼──────────────────────────┤
 │ 48  │ twenty remote add      │ twenty remote:add        │ Deprecated → colon       │
 │     │ --local                │ --local                  │ syntax                   │
 ├─────┼────────────────────────┼──────────────────────────┼──────────────────────────┤
 │ 49  │ twenty remote add      │ twenty remote:add --test │ Deprecated → colon       │
 │     │ --test                 │                          │ syntax                   │
 ├─────┼────────────────────────┼──────────────────────────┼──────────────────────────┤
 │ 50  │ twenty remote list     │ twenty remote:list       │ Deprecated → colon       │
 │     │                        │                          │ syntax                   │
 ├─────┼────────────────────────┼──────────────────────────┼──────────────────────────┤
 │ 51  │ twenty remote switch   │ twenty remote:use [name] │ Deprecated → colon       │
 │     │ [name]                 │                          │ syntax + renamed         │
 ├─────┼────────────────────────┼──────────────────────────┼──────────────────────────┤
 │ 52  │ twenty remote status   │ twenty remote:status     │ Deprecated → colon       │
 │     │                        │                          │ syntax                   │
 ├─────┼────────────────────────┼──────────────────────────┼──────────────────────────┤
 │ 53  │ twenty remote remove   │ twenty remote:remove     │ Deprecated → colon       │
 │     │ <name>                 │ <name>                   │ syntax                   │
 └─────┴────────────────────────┴──────────────────────────┴──────────────────────────┘
```

---------

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-05-20 15:12:39 +00:00
Félix Malfait a2acf88a57 feat(website): per-PR preview deploys via Worker versions (#20762)
## Summary
Adds review apps for the marketing site. Every PR that touches
`packages/twenty-website/**` or `packages/twenty-shared/**` gets a
per-version Worker preview URL, sticky-commented on the PR, auto-cleaned
up when the PR closes.

Same Cloudflare machinery skew protection rides on, just used for
previews — no extra plan, no extra services. Cleaner than the
GitHub-Actions-runner + Cloudflare-tunnel pattern: previews persist for
the life of the version, accessible from anywhere, no warm-up.

## Files
- **`.github/workflows/website-pr-preview.yaml`** — on PR
open/sync/reopen: builds the Worker with a per-PR `DEPLOYMENT_ID`, runs
`wrangler versions upload --tag pr-<N>` (no production traffic),
sticky-comments the preview URL. Skipped on fork PRs because GitHub
doesn't pass secrets to forks anyway.
- **`.github/workflows/website-pr-preview-cleanup.yaml`** — on PR close:
walks the Worker version list via the CF API, deletes anything tagged
`pr-<N>` (with message-based fallback if the annotation key changes),
updates the sticky comment.
- **`open-next.config.ts`** — `maxNumberOfVersions: 10 → 50` to leave
room for PR previews on top of skew protection's prod-version retention.

## How it looks on a PR

The bot leaves a sticky comment like:

> 🔍 **Website preview** is up at
**https://abc12345-twenty-website-dev.twentyhq.workers.dev**
>
> | | |
> |---|---|
> | Version | `abc12345-...` |
> | Commit | `<sha>` |
> | Bindings | shared with the `dev` Worker (R2 cache + secrets) |
>
> Updates on every push. Auto-deleted when the PR closes.

On close it becomes:

> 🧹 Website preview for this PR was cleaned up after close.

## Twenty repo credentials already provisioned
- `secret CLOUDFLARE_API_TOKEN` — same scoped token the `twenty-infra`
workflow uses
- `var CLOUDFLARE_ACCOUNT_ID` = `67b2bbe4381006564d2b0aa6ce6177be`
- `var CF_PREVIEW_DOMAIN` = `twentyhq` (no `.workers.dev` suffix —
OpenNext appends it;
[opennextjs-cloudflare#811](https://github.com/opennextjs/opennextjs-cloudflare/issues/811))

## Known limitations
- **Shared dev bindings**: PR previews use the dev Worker's R2 bucket +
secrets (Stripe test key, JWT private key). Fine for a read-mostly
marketing site; if two simultaneous PRs ever fight over ISR cache state
we can prefix R2 keys per-PR later.
- **Fork PRs don't get previews**. GitHub Actions doesn't pass
`secrets.*` to fork-PR runs (security), and the wrangler upload requires
the CF token. To enable forks, would need to switch to
`pull_request_target` and gate on a maintainer label — not done here
because the security tradeoff isn't worth it for a marketing-site
preview.
- **Version cap**: 50 versions is the new ceiling, and
`maxVersionAgeDays: 14` auto-prunes anything older. Cleanup-on-close
should keep us well under in steady state.

## Test plan
- [ ] CI on this PR triggers the preview workflow itself; check that the
sticky comment appears with a working URL
- [ ] Hit the URL, click around — should look like a fresh
marketing-site build with this PR's changes
- [ ] Close (don't merge) → cleanup workflow should run; sticky comment
switches to the "cleaned up" message; the version is gone from `wrangler
versions list --name twenty-website-dev`
2026-05-20 14:59:42 +02:00
Félix Malfait c002bc52bd fix(ci): repair preview-environment dispatch (use PAT, not GITHUB_TOKEN) (#20773)
## What
One-line token swap on the same-repo dispatch step in
[`preview-env-dispatch.yaml`](.github/workflows/preview-env-dispatch.yaml#L40):
`secrets.GITHUB_TOKEN` → `secrets.CI_PRIVILEGED_DISPATCH_TOKEN`.

## Why
Regression from [#20476](https://github.com/twentyhq/twenty/pull/20476)
("security: harden CI against supply-chain attacks"), merged 2026-05-12.
That PR replaced

```yaml
uses: peter-evans/repository-dispatch@v2
with:
  token: ${{ secrets.GITHUB_TOKEN }}
  ...
```

with a raw `gh api` call but kept `GITHUB_TOKEN`:

```yaml
env:
  GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
  gh api repos/"$REPOSITORY"/dispatches -f event_type=preview-environment ...
```

The auto-provisioned `GITHUB_TOKEN` can't fire `repository_dispatch` via
`gh api` even when the workflow declares `permissions: contents: write`.
The action used a different code path that worked; the CLI requires a
token with `repo` scope. So every dispatch from this workflow has
returned `403 Resource not accessible by integration` since that PR
merged — except for runs the `author_association` / `preview-app` label
gate skips entirely (which then show "success" because no jobs ran).

Recent failed example:
https://github.com/twentyhq/twenty/actions/runs/26162974597/job/76959379235?pr=20769

## The fix
`secrets.CI_PRIVILEGED_DISPATCH_TOKEN` already exists in repo secrets
and is **already used** by the immediately-following cross-repo dispatch
step in the same file. Using it for the same-repo dispatch too matches
the surrounding code and is consistent with the original hardening
intent (use a scoped PAT, not the auto-provisioned token).

## Test plan
- [ ] Merge this PR
- [ ] Next PR open / sync / reopen on a member's branch → check that
`Preview Environment Dispatch` succeeds (no 403)
- [ ] Confirm `Preview Environment Keep Alive` workflow gets triggered
(the downstream effect of the dispatch)
- [ ] Confirm the tunnel URL sticky comment lands on the PR

Discovered while testing an unrelated PR
([#20762](https://github.com/twentyhq/twenty/pull/20762)). Independent
fix.
2026-05-20 14:57:57 +02:00
Félix Malfait 658bdf3e57 chore(website): rename twenty-website-new → twenty-website (#20745)
## Summary
Follow-up to the Cloudflare/OpenNext migration (#20741). Now that the
legacy `twenty-website` package was already removed in #20270, the
`-new` suffix on the marketing site package is no longer meaningful.

## What changes
- **Directory rename**: `git mv packages/twenty-website-new
packages/twenty-website` (1213 files moved, no content change)
- **Package + nx config**: `package.json` and `project.json` name fields
updated, `sourceRoot` repointed
- **Source refs**: `load-local-articles.ts` and
`load-local-release-notes.ts` had a hardcoded `'twenty-website-new'`
segment in their monorepo-root fallback path;
`app/[locale]/releases/page.tsx` had display strings showing where to
add content
- **External refs**: root `package.json` workspaces, root `CLAUDE.md` /
`README.md`, `twenty-sdk` + `create-twenty-app` READMEs,
`.vscode/twenty.code-workspace`, `.cursor/rules/changelog-process.mdc`,
Crowdin config + the three `website-i18n-*` CI workflows +
`ci-website.yaml`
- **Docker cleanup**:
`packages/twenty-docker/twenty-website-new/Dockerfile` deleted; the two
Makefile targets (`prod-website-new-build` / `prod-website-new-run`)
that referenced it removed — EKS deploy was retired in the Cloudflare
migration
- **`yarn.lock`** regenerated against the new workspace path

## What's deliberately not in this PR
The dev hostname `website-new.twenty-main.com` in `wrangler.jsonc` stays
for now. Migrating it to `website.twenty-main.com` needs coordinated DNS
deletion (current CNAME points at the legacy Docusaurus NLB and serves
503s) and removal of the matching legacy `website` Helm chart in
`twenty-infra`. Flagged as a separate cleanup.

Companion infra PR: https://github.com/twentyhq/twenty-infra/pull/682
(workflow paths + Terraform ECR + docs)

## Test plan
- [x] `yarn install --immutable` resolves clean against the new path
- [x] `npx nx typecheck twenty-website` passes
- [x] `npx nx lint twenty-website` passes
- [ ] CI on this PR confirms the same on a fresh checkout
- [ ] After merge: trigger `Deploy Website` workflow against
`environment=dev` to confirm the renamed working-directory deploys
correctly
2026-05-19 23:42:09 +02:00
martmull d5ff9eb515 Create twenty app improvements (#20688)
create-twenty-app updates:
- remove --example option
- sync --once when scaffolding an applicaiton
- rename --api-url option to --workspace-url
- create a standalone page when scaffolding an app
<img width="1494" height="765" alt="image"
src="https://github.com/user-attachments/assets/0e35ed0c-b0aa-466c-9f56-7939294fd2cf"
/>

---------

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2026-05-19 13:12:47 +00:00
Charles Bochet cf4b4455d3 fix(server): normalize composite defaultValues in manifest converter (unblock app re-install on 2.5-normalized workspaces) (#20615)
## Context

The runtime create-field path and the v2.5
`NormalizeCompositeFieldDefaultsCommand` workspace upgrade both run
composite `defaultValue`s through `nullifyEmptyCompositeDefaultValue`.
The manifest install/sync path was the only write path that skipped it:
[`fromFieldManifestToUniversalFlatFieldMetadata`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/src/engine/core-modules/application/application-manifest/converters/from-field-manifest-to-universal-flat-field-metadata.util.ts)
passed `fieldManifest.defaultValue` through verbatim.

For the SDK-emitted ACTOR system fields (`createdBy` / `updatedBy`),
`twenty-sdk` ships `{ name: "''", source: "'MANUAL'" }`. After the
runtime or the 2.5 normalize command stores them, the workspace row
holds the canonical four-key form `{ context: null, name: null, source:
"'MANUAL'", workspaceMemberId: null }`. The next install computes its TO
map from the manifest, still gets the raw two-key shape, and diffs it
against the normalized FROM. The dispatcher emits a `defaultValue`
update on each system actor field; the flat-field-metadata validator
rejects it with `FIELD_MUTATION_NOT_ALLOWED`, blocking every re-install
of any application that defines a custom object on a v2.5-normalized
workspace.


## Fix

Normalize composite `defaultValue`s inside the converter, reusing the
same `nullifyEmptyCompositeDefaultValue` helper the three other write
paths already share:

-
[`get-default-flat-field-metadata-from-create-field-input.util.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/get-default-flat-field-metadata-from-create-field-input.util.ts)
— `createOneObject` and `createOneField` GraphQL paths.
-
[`sanitize-raw-update-field-input.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/sanitize-raw-update-field-input.ts)
— `updateOneField` GraphQL path.
-
[`2-5-workspace-command-1778000001000-normalize-composite-field-defaults.command.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/src/database/commands/upgrade-version-command/2-5/2-5-workspace-command-1778000001000-normalize-composite-field-defaults.command.ts)
— the upgrade backfill that introduced the divergence.

After the fix, the four write paths agree on the canonical shape, so
re-installs are no-ops on system actor fields regardless of when the 2.5
normalize command ran. Non-composite types pass through unchanged.

## Test

New spec
`from-field-manifest-to-universal-flat-field-metadata.util.spec.ts`
covers:

- Empty-name actor defaults are normalized to the four-key canonical
shape.
- The converter is idempotent: feeding its own output back in produces
the same result (so two consecutive syncs of the same manifest never
emit a `defaultValue` update).
- When the manifest omits `defaultValue`, the converter falls back to
`generateDefaultValue` and normalizes the result.
- Non-composite defaults pass through unchanged.

```
PASS  src/engine/core-modules/application/application-manifest/converters/__tests__/from-field-manifest-to-universal-flat-field-metadata.util.spec.ts
  fromFieldManifestToUniversalFlatFieldMetadata
    composite defaultValue normalization
      ✓ normalizes empty-name actor defaults to the canonical four-key shape
      ✓ is idempotent: re-running the converter on its own output yields the same defaultValue
      ✓ falls back to the generated default and normalizes it when defaultValue is omitted
      ✓ leaves non-composite defaults untouched
Tests: 4 passed
```

## CI gap that let this through

The integration suites covering manifest install (`appDevOnce` against
the test workspace) never re-installed an existing app on a workspace
whose composite fields had already been put through the 2.5 normalize
command. They synced once, then ran assertions on the resulting state;
the second sync that would have re-triggered the `defaultValue` diff was
never exercised.

If we want to catch this class of regression at the integration level
too, we'd add a test that (1) syncs an app whose manifest includes an
ACTOR system field with the raw SDK shape, (2) invokes
`NormalizeCompositeFieldDefaultsCommand` directly on the test workspace,
(3) re-syncs the same manifest, and (4) asserts no
`FIELD_MUTATION_NOT_ALLOWED` errors. The unit-level idempotency check in
this PR is the minimal version of that same coverage. Happy to ship that
integration spec in a follow-up if it'd help.
2026-05-15 18:31:48 +02:00
Paul Rastoin a754a95d38 Regrant id token write to claude for oidc swap (#20564) 2026-05-14 07:19:08 +00:00
martmull 0cc2194399 Simplify create-twenty-app command (#20512)
## Simplify `create-twenty-app` for zero-interaction use

Makes `npx create-twenty-app@latest my-app` a fully non-interactive,
single-command experience suitable for automated environments (Codex,
Claude plugins).

  ### Changes

- **Remove all interactive prompts** — app name, display name,
description, and scaffold confirmation are now derived from CLI args
with sensible defaults. `inquirer` dependency removed
   entirely.
- **Replace OAuth with API key auth** — use the seeded dev API key
(`DEV_API_KEY`) to authenticate against the Docker instance as
`tim@apple.dev`, eliminating the browser-based OAuth
  flow.
- **Docker-first with early validation** — check Docker is installed
before scaffolding; if missing, print the install URL and exit. Detect
alternative runtimes (Podman, nerdctl).
- **Parallel image pull** — `docker pull` runs in the background during
scaffold + dependency install, saving 10-30s on typical runs.
- **Always pull latest image** — ensures the dev server is up-to-date on
every run.
- **Stop detecting port 3000** — only check port 2020 (Docker instance).
- **Update CLI flags** — remove `--skip-local-instance` and `--yes`; add
`--skip-docker`.
- **Update CI workflows and docs** — align e2e workflows, package
README, and template README/cd.yml with the new flow.
2026-05-13 16:44:27 +00:00