c15bcd3bec6f2e1269ddbec0c2a97aaa67d01eb5
419
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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.
|
||
|
|
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) |
||
|
|
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 |
||
|
|
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> |
||
|
|
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` |
||
|
|
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. |
||
|
|
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 |
||
|
|
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> |
||
|
|
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. |
||
|
|
a754a95d38 | Regrant id token write to claude for oidc swap (#20564) | ||
|
|
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 `[email protected]`, 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. |
||
|
|
565995e715 |
security: harden CI against supply-chain attacks (#20476)
- Pin all third-party actions to SHA - Gate claude.yml triggers to internal authors with Harden-Runner egress audit - Ignore fork-PR lifecycle scripts - Narrow cross-repo dispatch payloads - Add 7d npm release-age gate - Add CODEOWNERS on .github/** and .yarnrc.yml --------- Co-authored-by: prastoin <[email protected]> |
||
|
|
b6b4824104 |
ci(preview-env): drop yarn -- separator so --light reaches the seed command (#20479)
## Summary Follow-up to #20464. That PR added `--light` to the preview env seed command but left the `--` between `yarn command:prod` and the script args. After yarn strips its own `--`, nest-commander still sees `argv: [..., '--', 'workspace:seed:dev', '--light']`. Commander.js treats `--` as the end-of-options marker, so `--light` is parsed as a positional arg and silently ignored — the seed runs in full mode (Apple + YCombinator + Empty3 + Empty4) and Empty4 still ends up as the default workspace. ## Evidence In the preview run on `f706cc052b` (which had #20464's `--light` flag), the seed step took only ~40s but the `GqlTypeGenerator` log emits four regenerations across two workspaces with custom objects: - 28 standard → 28 + 5 custom (`rocket, surveyResult, employmentHistory, petCareAgreement, pet`) — matches Apple - 28 standard → 28 + 1 custom (`surveyResult`) — matches YCombinator With `--light` actually applied, `getLightConfig` returns `{ objects: [], fields: [] }` so no custom objects should be generated. The working `twenty-app-dev` invocation in `packages/twenty-docker/twenty-app-dev/rootfs/etc/s6-overlay/scripts/init-db.sh:66` is `yarn command:prod workspace:seed:dev --light` — no `--`. Matching that fixes it. ## Test plan - [ ] Trigger the preview-app label on a PR, confirm only the Apple workspace is created and `[email protected]` signs in there - [ ] Confirm the seed step still passes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 <[email protected]> |
||
|
|
8d54ff6ca0 |
fix(ci): probe real schema in breaking-changes server readiness check (#20465)
## Summary The `GraphQL and OpenAPI Breaking Changes Detection` workflow has been posting graphql-inspector stack traces as PR comments — see [#20445 comment](https://github.com/twentyhq/twenty/pull/20445#issuecomment-4421142635) for an example. ### Root cause - The wait step probed readiness with `curl -s URL > /dev/null 2>&1`, which exits 0 for **any** HTTP response — including 5xx and GraphQL error JSON. NestJS opens the HTTP listener before the workspace schema cache is fully populated, so the wait often completed while the server still served auth/metadata error JSON. - The introspection download therefore wrote a small (~154-byte) error payload instead of the real schema. `jq empty` in the validation step only checks JSON *syntax*, so `{"errors":[...]}` passed validation. - `graphql-inspector diff` then failed with `Unable to read JSON file: ... Not valid JSON content`, the workflow swallowed the error into the diff markdown, and the bot posted that stack trace verbatim on the PR. In the failing run, the main-branch files were 154 B (GraphQL) and 112 B (REST 500); the current-branch files in the same run were 600 KB–2.8 MB. ### Fix - Wait steps now POST an authenticated introspection (`{ __schema { queryType { name } } }`) and require `.data.__schema` plus a 2xx response from `/rest/open-api/core` (`curl -f`) before declaring the server ready. - Validation step now checks for the expected shape (`.data.__schema` for GraphQL, `.openapi`/`.swagger` for OpenAPI) and includes the first 200 bytes of any bad payload in the warning, so when something genuinely goes wrong the next debugger has a real lead instead of a generic stack trace. ## Test plan - [ ] CI runs against this branch — the workflow's own readiness probes are now exercised against the real server, so a green run validates the new check. - [ ] If the readiness probe still passes but downloads regress, the strengthened validation step will surface the payload in the workflow logs instead of posting a graphql-inspector stack trace on the PR. 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
009f597eec |
ci(preview-env): use --light seed so Apple is the default workspace (#20464)
## Summary - Pass `--light` to `workspace:seed:dev` in the preview env keepalive workflow so only the Apple workspace is created - Avoids `Empty4` being picked as the default workspace at sign-in (which has no users), making the prefilled `[email protected]` credentials land on a useful workspace ## Why `workspace:seed:dev` (no flag) seeds Apple + YCombinator + Empty3 + Empty4. Preview envs run in single-workspace mode (`IS_MULTIWORKSPACE_ENABLED=false`), so [`WorkspaceDomainsService.getDefaultWorkspace`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/src/engine/core-modules/domain/workspace-domains/services/workspace-domains.service.ts) returns the most recently created workspace — Empty4 — which has no users. Users hitting the preview URL therefore see "Welcome, Empty4." and can't sign in. Same failure mode #19822 fixed for `twenty-app-dev`. ## Test plan - [ ] Trigger the `preview-app` label on a PR and confirm the preview URL signs in to the Apple workspace, not Empty4 - [ ] Confirm the seed step still passes (no `Empty3`/`Empty4` references break it) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 <[email protected]> |
||
|
|
3d8207af0f |
ci(preview-env): replace bore.pub with Cloudflare quick tunnel (#20459)
## Summary `bore.pub`'s public server has been increasingly unreliable: tunnels register fine on the runner side (our `Create Tunnel` step always succeeds), but the bore.pub side later stops accepting inbound traffic, leaving the preview environment unreachable for the rest of the 5h keep-alive window with no signal back to the runner. Recent symptom: `curl http://bore.pub:50422` → `Couldn't connect to server`, while the corresponding action keeps sleeping. This PR replaces the `codetalkio/expose-tunnel` action with a direct invocation of `cloudflared` running an account-less [Cloudflare quick tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/do-more-with-tunnels/trycloudflare/). The tunnel is served from Cloudflare's edge so reliability is materially better, and the URL is HTTPS by default (`https://*.trycloudflare.com`), which also eliminates the mixed-content issues we'd hit when `SERVER_URL` was `http://bore.pub:port`. ## What changes - `Create Tunnel` step now: - Downloads a pinned `cloudflared` binary (`2026.3.0`) - Starts `cloudflared tunnel --url http://localhost:3000` in the background, logging to `$RUNNER_TEMP/cloudflared.log` - Polls the log for `https://<name>.trycloudflare.com` (up to 2 minutes), failing fast if the process exits - Writes the URL to the `tunnel-url` step output — same name as before, so no downstream changes needed - `Cleanup` step kills the `cloudflared` process for hygiene ## What stays the same - `SERVER_URL` plumbing through `.env` → `docker compose up` - `tunnel-url` artifact - `$GITHUB_STEP_SUMMARY` formatting - PR-comment dispatch (`twentyhq/ci-privileged`) - 5h keep-alive sleep ## Trade-offs - Quick tunnels are explicitly labelled by Cloudflare for "testing/development" use without an SLA. For our preview-env use case (ephemeral, per-PR) that fits, but if we ever need stable URLs on a custom domain we'd move to *named* tunnels — same `cloudflared` binary, plus a free Cloudflare account + delegated domain + a service token stored as a repo secret. Strictly additive when we want it. - `cloudflared` is pinned to `2026.3.0` to avoid surprise breakage from upstream releases. Bumping is a one-line change. ## Testing **Locally (macOS) — verified end-to-end:** - `cloudflared tunnel --url http://localhost:18080` against a `python3 -m http.server` - Regex `https://[a-zA-Z0-9-]+\.trycloudflare\.com` correctly extracts the URL from the log - `curl $URL/` returns the upstream server's response (HTTP 200, ~0.5s) - Process supervision: if `cloudflared` dies mid-wait, the step fails fast instead of hitting the 2-min timeout **Validation:** - `actionlint` passes (the remaining shellcheck warnings are in pre-existing steps, not my changes) - `shellcheck` on the new Create Tunnel script: clean **What's not testable from a PR (and why):** - The full keep-alive workflow runs on `repository_dispatch`, which always uses the workflow file from `main`. So the cloudflared logic only runs against PR contents *after* merge. - I'll trigger a one-off Ubuntu-runner test of just the install + URL extraction logic via a throwaway branch (`workflow_dispatch`-only) and link the run here before this merges. ## Test plan - [ ] Throwaway run validates: cloudflared installs on `ubuntu-latest`, prints the URL, regex matches, tunnel is reachable from outside the runner. - [ ] After merge, the next PR's preview environment uses `*.trycloudflare.com` instead of `bore.pub:port`, and the URL stays reachable for the full 5h window. - [ ] PR-comment bot still posts the preview URL correctly (link should now be `https://*.trycloudflare.com`). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 <[email protected]> |
||
|
|
7f4f2e932c |
Simplify dispatch pr review (#20397)
# Introduction Sending minimal information for required metadata to be fetched afterwards |
||
|
|
e294d74e07 |
Detail steps during create twenty app (#20374)
## Before <img width="391" height="188" alt="image" src="https://github.com/user-attachments/assets/78a0139f-d992-49cb-98ff-531bdbadc64b" /> ## After <img width="419" height="773" alt="image" src="https://github.com/user-attachments/assets/da9b36a4-74e0-4445-b8c5-7a2329eb4f46" /> |
||
|
|
ca58c7f15e |
Fix auto draft workflow (#20357)
# Introduction Cannot use github graphql mutation with a `GITHUB_TOKEN` needs a authenticated one Dispatching to ci-priv to do so |
||
|
|
a3224880e0 |
External contributor auto-draft and dispatch pr-review event type (#20329)
# Introduction ## Auto draft On external contributor PR creation auto draft it and comment stating that it needs to be marked as ready for review when it is ## Caveats / future improvement Lets iterate first but we can imagine future pain points such as: - Cubic only runs on ready for review PRs - Expected green ci before turning ready to be review ? ( we could invoke cubic ourselves )\ - Auto close external contributors draft PR after x duration ## Auto review Once a PR started to be review or is being synchronized then auto dispatch auto review |
||
|
|
c983ac9f82 |
ci: add ci-website workflow for twenty-website-new (#20281)
## Summary - Recreates the `ci-website.yaml` workflow that was removed alongside `twenty-website` in #20270, now scoped to `twenty-website-new`. - Replaces the old build-only job with a `[lint, typecheck, test]` matrix run via `./.github/actions/nx-affected` on `tag:scope:website` — same idiom used by `ci-shared.yaml`. - Path filter watches `packages/twenty-website-new/**` and `packages/twenty-shared/**` (since website-new depends on `twenty-shared`), plus `package.json` / `yarn.lock`. ## Test plan - [ ] CI Website workflow appears on this PR and the `lint`, `typecheck`, `test` matrix jobs all pass - [ ] `ci-website-status-check` is green |
||
|
|
59107b5b23 | Remove twenty-website package. (#20270) | ||
|
|
d2cfbf319b |
[Website] Implement translations. (#20171)
As title. --------- Co-authored-by: Cursor <[email protected]> |
||
|
|
5bdbfe651e |
chore(deps): bump postal-mime from 2.6.1 to 2.7.4 (#20150)
Bumps [postal-mime](https://github.com/postalsys/postal-mime) from 2.6.1 to 2.7.4. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/postalsys/postal-mime/releases">postal-mime's releases</a>.</em></p> <blockquote> <h2>v2.7.4</h2> <h2><a href="https://github.com/postalsys/postal-mime/compare/v2.7.3...v2.7.4">2.7.4</a> (2026-03-17)</h2> <h3>Bug Fixes</h3> <ul> <li>add missing originalKey to Header type and Uint8Array to Attachment content (<a href="https://github.com/postalsys/postal-mime/commit/92cc91c1c8477e0462cb0e93ddf8ea6aec6534d0">92cc91c</a>)</li> <li>include originalKey in parsed headers output (<a href="https://github.com/postalsys/postal-mime/commit/83521c87f62e5e095ae09913c70798f20e2ab347">83521c8</a>)</li> <li>preserve __esModule and .default in CJS build for bundler interop (<a href="https://github.com/postalsys/postal-mime/commit/1466910e31608b9e5307724ecc6a0a3a70556048">1466910</a>)</li> <li>prevent RFC 2047 encoded-word address fabrication (<a href="https://github.com/postalsys/postal-mime/commit/844f92023d49d819ef13b9ad5c50b7c346eb02d3">844f920</a>)</li> </ul> <h2>v2.7.3</h2> <h2><a href="https://github.com/postalsys/postal-mime/compare/v2.7.2...v2.7.3">2.7.3</a> (2026-01-09)</h2> <h3>Bug Fixes</h3> <ul> <li>correct TypeScript type definitions to match implementation (<a href="https://github.com/postalsys/postal-mime/commit/b225d7cca422cb9bc3ab5301e94c4c0bef9a69e2">b225d7c</a>)</li> </ul> <h2>v2.7.2</h2> <h2><a href="https://github.com/postalsys/postal-mime/compare/v2.7.1...v2.7.2">2.7.2</a> (2026-01-08)</h2> <h3>Bug Fixes</h3> <ul> <li>add null checks for contentType.parsed access (<a href="https://github.com/postalsys/postal-mime/commit/ad8f4c62e0972fd0244859ee5a5184b2cac26395">ad8f4c6</a>)</li> <li>improve RFC compliance for MIME parsing (<a href="https://github.com/postalsys/postal-mime/commit/e004c3acb29d72ed7eaf1b0b66351cf8b82b970d">e004c3a</a>)</li> </ul> <h2>v2.7.1</h2> <h2><a href="https://github.com/postalsys/postal-mime/compare/v2.7.0...v2.7.1">2.7.1</a> (2025-12-22)</h2> <h3>Bug Fixes</h3> <ul> <li>Add null checks for contentDisposition.parsed access (<a href="https://github.com/postalsys/postal-mime/commit/fd54c37093cc64737c6bb17986bc9d052d2d5add">fd54c37</a>)</li> </ul> <h2>v2.7.0</h2> <h2><a href="https://github.com/postalsys/postal-mime/compare/v2.6.1...v2.7.0">2.7.0</a> (2025-12-22)</h2> <h3>Features</h3> <ul> <li>add headerLines property exposing raw header lines (<a href="https://github.com/postalsys/postal-mime/commit/c79a02ab05d9cac44e05e95a433752ff292aa5eb">c79a02a</a>)</li> </ul> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/postalsys/postal-mime/blob/master/CHANGELOG.md">postal-mime's changelog</a>.</em></p> <blockquote> <h2><a href="https://github.com/postalsys/postal-mime/compare/v2.7.3...v2.7.4">2.7.4</a> (2026-03-17)</h2> <h3>Bug Fixes</h3> <ul> <li>add missing originalKey to Header type and Uint8Array to Attachment content (<a href="https://github.com/postalsys/postal-mime/commit/92cc91c1c8477e0462cb0e93ddf8ea6aec6534d0">92cc91c</a>)</li> <li>include originalKey in parsed headers output (<a href="https://github.com/postalsys/postal-mime/commit/83521c87f62e5e095ae09913c70798f20e2ab347">83521c8</a>)</li> <li>preserve __esModule and .default in CJS build for bundler interop (<a href="https://github.com/postalsys/postal-mime/commit/1466910e31608b9e5307724ecc6a0a3a70556048">1466910</a>)</li> <li>prevent RFC 2047 encoded-word address fabrication (<a href="https://github.com/postalsys/postal-mime/commit/844f92023d49d819ef13b9ad5c50b7c346eb02d3">844f920</a>)</li> </ul> <h2><a href="https://github.com/postalsys/postal-mime/compare/v2.7.2...v2.7.3">2.7.3</a> (2026-01-09)</h2> <h3>Bug Fixes</h3> <ul> <li>correct TypeScript type definitions to match implementation (<a href="https://github.com/postalsys/postal-mime/commit/b225d7cca422cb9bc3ab5301e94c4c0bef9a69e2">b225d7c</a>)</li> </ul> <h2><a href="https://github.com/postalsys/postal-mime/compare/v2.7.1...v2.7.2">2.7.2</a> (2026-01-08)</h2> <h3>Bug Fixes</h3> <ul> <li>add null checks for contentType.parsed access (<a href="https://github.com/postalsys/postal-mime/commit/ad8f4c62e0972fd0244859ee5a5184b2cac26395">ad8f4c6</a>)</li> <li>improve RFC compliance for MIME parsing (<a href="https://github.com/postalsys/postal-mime/commit/e004c3acb29d72ed7eaf1b0b66351cf8b82b970d">e004c3a</a>)</li> </ul> <h2><a href="https://github.com/postalsys/postal-mime/compare/v2.7.0...v2.7.1">2.7.1</a> (2025-12-22)</h2> <h3>Bug Fixes</h3> <ul> <li>Add null checks for contentDisposition.parsed access (<a href="https://github.com/postalsys/postal-mime/commit/fd54c37093cc64737c6bb17986bc9d052d2d5add">fd54c37</a>)</li> </ul> <h2><a href="https://github.com/postalsys/postal-mime/compare/v2.6.1...v2.7.0">2.7.0</a> (2025-12-22)</h2> <h3>Features</h3> <ul> <li>add headerLines property exposing raw header lines (<a href="https://github.com/postalsys/postal-mime/commit/c79a02ab05d9cac44e05e95a433752ff292aa5eb">c79a02a</a>)</li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/postalsys/postal-mime/commit/178f1ef0b1cd0047e1b8e690beabfec541b4daa7"><code>178f1ef</code></a> chore(master): release 2.7.4 (<a href="https://redirect.github.com/postalsys/postal-mime/issues/88">#88</a>)</li> <li><a href="https://github.com/postalsys/postal-mime/commit/1f7ba618d42d34b779157dfa33794cbae383a24d"><code>1f7ba61</code></a> chore: bump devDependencies</li> <li><a href="https://github.com/postalsys/postal-mime/commit/83521c87f62e5e095ae09913c70798f20e2ab347"><code>83521c8</code></a> fix: include originalKey in parsed headers output</li> <li><a href="https://github.com/postalsys/postal-mime/commit/b0d7b11550a2a3c65a52a2adf4f8281058023cab"><code>b0d7b11</code></a> test: improve test coverage across codebase</li> <li><a href="https://github.com/postalsys/postal-mime/commit/ebc5ce619649d13ad72f4d12414f3e337a9e248c"><code>ebc5ce6</code></a> refactor: simplify and clean up codebase</li> <li><a href="https://github.com/postalsys/postal-mime/commit/1466910e31608b9e5307724ecc6a0a3a70556048"><code>1466910</code></a> fix: preserve __esModule and .default in CJS build for bundler interop</li> <li><a href="https://github.com/postalsys/postal-mime/commit/844f92023d49d819ef13b9ad5c50b7c346eb02d3"><code>844f920</code></a> fix: prevent RFC 2047 encoded-word address fabrication</li> <li><a href="https://github.com/postalsys/postal-mime/commit/24dc6c64dfb43d89a8c8837ec941c96ebfa2c1fa"><code>24dc6c6</code></a> test: update type check test with originalKey property</li> <li><a href="https://github.com/postalsys/postal-mime/commit/92cc91c1c8477e0462cb0e93ddf8ea6aec6534d0"><code>92cc91c</code></a> fix: add missing originalKey to Header type and Uint8Array to Attachment content</li> <li><a href="https://github.com/postalsys/postal-mime/commit/aa5baeafa6ffd093ab447c22d20e5da25051faff"><code>aa5baea</code></a> docs: add link to full documentation site</li> <li>Additional commits viewable in <a href="https://github.com/postalsys/postal-mime/compare/v2.6.1...v2.7.4">compare view</a></li> </ul> </details> <br /> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> --------- Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Charles Bochet <[email protected]> |
||
|
|
a90895e167 |
[Website] locale-segment routing and shared Lingui factory (#20079)
**1. Shared Lingui factory in `twenty-shared`**
- Extracted `createI18nInstanceFactory` into
`packages/twenty-shared/src/i18n/create-i18n-instance-factory.ts` so
every package gets the same per-render Lingui bootstrap with a
per-locale singleton cache and a `SOURCE_LOCALE` fallback.
- `twenty-emails/src/utils/i18n.utils.ts` now consumes the shared
factory.
**2. `twenty-website-new` Lingui bootstrap + Crowdin wiring**
- `lingui.config.ts`, `src/lib/i18n/*`, `nx run
twenty-website-new:lingui:{extract,compile}`.
- 31 locale PO files generated; minified compiled output kept out of
Prettier and Oxlint.
- `i18n-{push,pull}.yaml` workflows updated to include
`twenty-website-new` in Crowdin sync.
**3. `app/[locale]/...` segment routing with English at the root**
- All marketing routes moved under `src/app/[locale]/`; static
generation preserved (15 routes × 31 locales = 465 prerendered URLs).
- Middleware behavior:
- `/{en}/...` → 301 redirect to unprefixed canonical.
- `/{non-en}/...` → pass through, set `NEXT_LOCALE` cookie.
### What this PR explicitly does not do (deferred)
- Lingui-wrapping the actual marketing copy. Keys, build pipeline, and
runtime are wired; copy migration is a separate, reviewer-friendlier
PR.
|
||
|
|
1e7c1691f4 |
[CI] Prevent previous version upgrade sequence mutation (#20075)
# Introduction Prevent any PR to target a previous already released twenty version by mistake. Especially useful for existing opened PR introducing commands into an upgrade that has just been released leading to a `TWENTY_CURRENT_VERSION` bump <img width="3150" height="1158" alt="image" src="https://github.com/user-attachments/assets/b83d211f-a061-4d63-ae7a-354d7851ec08" /> ## Bypass If intentional add `ci:allow-previous-version-upgrade-mutation` label to the PR and re-run the failed job <img width="3150" height="1158" alt="image" src="https://github.com/user-attachments/assets/f94ee630-d87b-4477-9e50-bf6773a8a280" /> This will require a brand new ci from a commit introduced after the label has been added |
||
|
|
66857ca77b |
Remove cross version upgrade placeholder (#19940)
Moving to somewhere else to leverage docker cache |
||
|
|
65e01400c0 |
Cross version ci placeholder (#19932)
Created this empty workflow so it appears on main and is pickable from a different branch to start testing the whole flow |
||
|
|
75848ff8ea |
feat: move admin panel to dedicated /admin-panel GraphQL endpoint (#19852)
## Summary Splits admin-panel resolvers off the shared `/metadata` GraphQL endpoint onto a dedicated `/admin-panel` endpoint. The backend plumbing mirrors the existing `metadata` / `core` pattern (new scope, decorator, module, factory), and admin types now live in their own `generated-admin/graphql.ts` on the frontend — dropping 877 lines of admin noise from `generated-metadata`. ## Why - **Smaller attack surface on `/metadata`** — every authenticated user hits that endpoint; admin ops don't belong there. - **Independent complexity limits and monitoring** per endpoint. - **Cleaner module boundaries** — admin is a cross-cutting concern that doesn't match the "shared-schema configuration" meaning of `/metadata`. - **Deploy / blast-radius isolation** — a broken admin query can't affect `/metadata`. Runtime behavior, auth, and authorization are unchanged — this is a relocation, not a re-permissioning. All existing guards (`WorkspaceAuthGuard`, `UserAuthGuard`, `SettingsPermissionGuard(SECURITY)` at class level; `AdminPanelGuard` / `ServerLevelImpersonateGuard` at method level) remain on `AdminPanelResolver`. ## What changed ### Backend - `@AdminResolver()` decorator with scope `'admin'`, naming parallels `CoreResolver` / `MetadataResolver`. - `AdminPanelGraphQLApiModule` + `adminPanelModuleFactory` registered at `/admin-panel`, same Yoga hook set as the metadata factory (Sentry tracing, error handler, introspection-disabling in prod, complexity validation). - Middleware chain on `/admin-panel` is identical to `/metadata`. - `@nestjs/graphql` patch extended: `resolverSchemaScope?: 'core' | 'metadata' | 'admin'`. - `AdminPanelResolver` class decorator swapped from `@MetadataResolver()` to `@AdminResolver()` — no other changes. ### Frontend - `codegen-admin.cjs` → `src/generated-admin/graphql.ts` (982 lines). - `codegen-metadata.cjs` excludes admin paths; metadata file shrinks by 877 lines. - `ApolloAdminProvider` / `useApolloAdminClient` follow the existing `ApolloCoreProvider` / `useApolloCoreClient` pattern, wired inside `AppRouterProviders` alongside the core provider. - 37 admin consumer files migrated: imports switched to `~/generated-admin/graphql` and `client: useApolloAdminClient()` is passed to `useQuery` / `useMutation`. - Three files intentionally kept on `generated-metadata` because they consume non-admin Documents: `useHandleImpersonate.ts`, `SettingsAdminApplicationRegistrationDangerZone.tsx`, `SettingsAdminApplicationRegistrationGeneralToggles.tsx`. ### CI - `ci-server.yaml` runs all three `graphql:generate` configurations and diff-checks all three generated dirs. ## Authorization (unchanged, but audited while reviewing) Every one of the 38 methods on `AdminPanelResolver` has a method-level guard: - `AdminPanelGuard` (32 methods) — requires `canAccessFullAdminPanel === true` - `ServerLevelImpersonateGuard` (6 methods: user/workspace lookup + chat thread views) — requires `canImpersonate === true` On top of the class-level guards above. No resolver method is accessible without these flags + `SECURITY` permission in the workspace. ## Test plan - [ ] Dev server boots; `/graphql`, `/metadata`, `/admin-panel` all mapped as separate GraphQL routes (confirmed locally during development). - [ ] `nx typecheck twenty-server` passes. - [ ] `nx typecheck twenty-front` passes. - [ ] `nx lint:diff-with-main twenty-server` and `twenty-front` both clean. - [ ] Manual smoke test: log in with a user who has `canAccessFullAdminPanel=true`, open the admin panel at `/settings/admin-panel`, verify each tab loads (General, Health, Config variables, AI, Apps, Workspace details, User details, chat threads). - [ ] Manual smoke test: log in with a user who has `canImpersonate=false` and `canAccessFullAdminPanel=false`, hit `/admin-panel` directly with a raw GraphQL request, confirm permission error on every operation. - [ ] Production deploy note: reverse proxy / ingress must route the new `/admin-panel` path to the Nest server. If the proxy has an explicit allowlist, infra change required before cutover. ## Follow-ups (out of scope here) - Consider cutting over the three `SettingsAdminApplicationRegistration*` components to admin-scope versions of the app-registration operations so the admin page is fully on the admin endpoint. - The `renderGraphiQL` double-assignment in `admin-panel.module-factory.ts` is copied from `metadata.module-factory.ts` — worth cleaning up in both. |
||
|
|
69d228d8a1 |
Deprecate IS_RECORD_TABLE_WIDGET_ENABLED feature flag (#19662)
## Summary - Removes the `IS_RECORD_TABLE_WIDGET_ENABLED` feature flag, making the record table widget unconditionally available in dashboard widget type selection - The flag was already seeded as `true` for all new workspaces and only gated UI visibility in one component (`SidePanelPageLayoutDashboardWidgetTypeSelect`) - Cleans up the flag from `FeatureFlagKey` enum, dev seeder, and test mocks ## Analysis The flag only controlled whether the "View" (Record Table) widget option appeared in the dashboard widget type selector. The entire record table widget infrastructure (rendering, creation hooks, GraphQL types, `RECORD_TABLE` enum in `WidgetType`) is independent of the flag and fully implemented. No backend logic depends on this flag. --------- Co-authored-by: Claude Opus 4.6 <[email protected]> Co-authored-by: Charles Bochet <[email protected]> |
||
|
|
b37ef3e7da |
Add app-path input to deploy and install composite actions (#19589)
Supports monorepo layouts where the app isn't at the repo root. Defaults to '.' for backward compatibility. Made-with: Cursor |
||
|
|
baf2fc4cc9 |
Fix sdk-e2e-test: ensure DB is ready before server starts (#19583)
## Summary
- The `sdk-e2e-test` CI job has been failing since at least April 9th
because the nx dependency graph runs `database:reset` and
`start:ci-if-needed` in **parallel** — neither depends on the other. The
server starts before the DB tables are created, crashes with `relation
"core.keyValuePair" does not exist`, and `wait-on` times out after 10
minutes.
- Replace the `nx-affected` orchestration for e2e with explicit
**sequential** CI steps: build → create DB → reset DB → start server →
wait for health → run tests.
- Add server log dump on failure for easier future debugging.
## Root cause
In `packages/twenty-sdk/project.json`, the `test:e2e` target has:
```json
"dependsOn": [
"build",
{ "target": "database:reset", "projects": "twenty-server" },
{ "target": "start:ci-if-needed", "projects": "twenty-server" }
]
```
Since `database:reset` and `start:ci-if-needed` don't depend on each
other, nx can (and does) run them concurrently. `start:ci-if-needed`
fires `nohup nest start &` and immediately completes. The server process
tries to query `core.keyValuePair` before `database:reset` creates it →
crash → wait-on timeout → job failure.
|
||
|
|
c26c0b9d71 |
Use app's own OAuth credentials for CoreApiClient generation (#19563)
## Summary - **SDK (`dev` & `dev --once`)**: After app registration, the CLI now obtains an `APPLICATION_ACCESS` token via `client_credentials` grant using the app's own `clientId`/`clientSecret`, and uses that token for CoreApiClient schema introspection — instead of the user's `config.accessToken` which returns the full unscoped schema. - **Config**: `oauthClientSecret` is now persisted alongside `oauthClientId` in `~/.twenty/config.json` when creating a new app registration, so subsequent `dev`/`dev --once` runs can obtain fresh app tokens without re-registration. - **CI action**: `spawn-twenty-app-dev-test` now outputs a proper `API_KEY` JWT (signed with the seeded dev workspace secret) instead of the previous hardcoded `ACCESS` token — giving consumers a real API key rather than a user session token. ## Motivation When developing Twenty apps, `yarn twenty dev` was using the CLI user's OAuth token for GraphQL schema introspection during CoreApiClient generation. This token (type `ACCESS`) has no `applicationId` claim, so the server returns the **full workspace schema** — including all objects — rather than the scoped schema the app should see at runtime (filtered by `applicationId`). This caused a discrepancy: the generated CoreApiClient contained fields the app couldn't actually query at runtime with its `APPLICATION_ACCESS` token. By switching to `client_credentials` grant, the SDK now introspects with the same token type the app will use in production, ensuring the generated client accurately reflects the app's runtime capabilities. |
||
|
|
c99db7d9c6 |
Fix server-validation ci pending instance command detection (#19558)
https://github.com/twentyhq/twenty/actions/runs/24246052659/job/70792870185 |
||
|
|
36fbfca069 |
Add application-logs module with driver pattern for logic function log persistence (#19486)
## Summary
- Introduces a new `application-logs` core module with a driver pattern
(disabled/console/clickhouse) to capture and persist logic function
execution logs
- Adds a ClickHouse `applicationLog` table with per-line log storage,
30-day TTL, and `ORDER BY (workspaceId, timestamp, applicationId,
logicFunctionId)`
- Surfaces application logs in the existing frontend audit logs table as
a new "Application Logs" source with dedicated columns (Function,
Timestamp, Level, Message, Execution ID)
## Details
**Write path**: `LogicFunctionExecutorService.handleExecutionResult()`
parses the multi-line log string from driver output into individual `{
timestamp, level, message }` entries, generates an execution UUID, and
passes them to `ApplicationLogsService.writeLogs()` which delegates to
the configured driver.
**Driver pattern**: Follows the exception-handler module style (Symbol
injection token + `forRootAsync` dynamic module). Three drivers:
- `DISABLED` (default) — no-op, prevents information leaking
- `CONSOLE` — structured stdout logging with level-based `console.*`
calls
- `CLICKHOUSE` — inserts rows into the `applicationLog` ClickHouse table
**Read path**: Extends the existing event-logs module by adding
`APPLICATION_LOG` to the `EventLogTable` enum, table name mapping, and
normalization logic.
**Config**: New `APPLICATION_LOG_DRIVER_TYPE` environment variable
(default: `DISABLED`).
|
||
|
|
9b8cb610c3 |
Flush Redis between server runs in breaking changes CI (#19491)
## Summary Added a Redis cache flush step in the breaking changes CI workflow to prevent stale data contamination between consecutive server runs. ## Key Changes - Added a new workflow step that executes `redis-cli FLUSHALL` between the current branch server run and the main branch server run - Includes error handling to log a warning if the Redis flush fails, without blocking the workflow - Added explanatory comments documenting why this step is necessary ## Implementation Details The Redis flush is necessary because both the current branch and main branch servers share the same Redis instance during CI testing. The `CoreEntityCacheService` and `WorkspaceCacheService` persist cached entities across process restarts, which can cause stale data from the current branch server to contaminate the main branch server comparison. This step ensures a clean cache state before switching branches. https://claude.ai/code/session_01BVMacfAXDMNx5WAFtP7GgW Co-authored-by: Claude <[email protected]> |
||
|
|
bc7b5aee58 |
chore: centralize deploy/install CD actions in twentyhq/twenty (#19454)
## Summary - Adds `deploy-twenty-app` and `install-twenty-app` composite actions to `.github/actions/` so app repos can reference them remotely — same pattern as `spawn-twenty-app-dev-test` for CI - Updates `cd.yml` in template, hello-world, and postcard to use `twentyhq/twenty/.github/actions/deploy-twenty-app@main` / `install-twenty-app@main` instead of local `./.github/actions/` copies - Removes the 6 local action files that were duplicated across template and example apps **Before** (each app repo carried its own action copies): ```yaml uses: ./.github/actions/deploy ``` **After** (centralized, like CI): ```yaml uses: twentyhq/twenty/.github/actions/deploy-twenty-app@main ``` Made with [Cursor](https://cursor.com) |
||
|
|
6cd3f2db2b |
chore: replace spawn-twenty-app-dev-test with native postgres/redis services (#19449)
## Summary - Replaces the `spawn-twenty-app-dev-test` Docker action with native GitHub Actions services (`postgres:18` + `redis`) and direct server startup (`npx nx start:ci twenty-server`) - Aligns with the pattern already used by `ci-sdk.yaml` for e2e tests - Removes dependency on the `twenty-app-dev` Docker image for CI Updated workflows: - `ci-example-app-postcard` - `ci-example-app-hello-world` - `ci-create-app-e2e-minimal` - `ci-create-app-e2e-hello-world` - `ci-create-app-e2e-postcard` Server setup pattern: 1. Postgres 18 + Redis as job services 2. `CREATE DATABASE "test"` via psql 3. `npx nx run twenty-server:database:reset` (migrations + seed) 4. `nohup npx nx start:ci twenty-server &` 5. `npx wait-on http://localhost:3000/healthz` Made with [Cursor](https://cursor.com) |
||
|
|
15eb3e7edc |
feat(sdk): use config file as single source of truth, remove env var fallbacks (#19409)
## Summary - **Config as source of truth**: `~/.twenty/config.json` is now the single source of truth for SDK authentication — env var fallbacks have been removed from the config resolution chain. - **Test instance support**: `twenty server start --test` spins up a dedicated Docker instance on port 2021 with its own config (`config.test.json`), so integration tests don't interfere with the dev environment. - **API key auth for marketplace**: Removed `UserAuthGuard` from `MarketplaceResolver` so API key tokens (workspace-scoped) can call `installMarketplaceApp`. - **CI for example apps**: Added monorepo CI workflows for `hello-world` and `postcard` example apps to catch regressions. - **Simplified CI**: All `ci-create-app-e2e` and example app workflows now use a shared `spawn-twenty-app-dev-test` action (Docker-based) instead of building the server from source. Consolidated auth env vars to `TWENTY_API_URL` + `TWENTY_API_KEY`. - **Template publishing fix**: `create-twenty-app` template now correctly preserves `.github/` and `.gitignore` through npm publish (stored without leading dot, renamed after copy). ## Test plan - [x] CI SDK (lint, typecheck, unit, integration, e2e) — all green - [x] CI Example App Hello World — green - [x] CI Example App Postcard — green - [x] CI Create App E2E minimal — green - [x] CI Front, CI Server, CI Shared — green |
||
|
|
83d30f8b76 |
feat: Send email from UI — inline reply composer & SendEmail mutation (#19363)
## Summary - **Inline email reply**: Replace external email client redirects (Gmail/Outlook deeplinks) with an in-app email composer. Users can reply to email threads directly from the email thread widget or via the command menu. - **SendEmail GraphQL mutation**: New backend mutation that reuses `EmailComposerService` for body sanitization, recipient validation, and SMTP dispatch via the existing outbound messaging infrastructure. - **Side panel compose page**: Command menu "Reply" action now opens a side-panel compose email page with pre-filled To, Subject, and In-Reply-To fields. ### Backend - `SendEmailResolver` with `SendEmailInput` / `SendEmailOutputDTO` - `SendEmailModule` wired into `CoreEngineModule` - Reuses `EmailComposerService` + `MessagingMessageOutboundService` ### Frontend - `EmailComposer` / `EmailComposerFields` components - `useSendEmail`, `useReplyContext`, `useEmailComposerState` hooks - `useOpenComposeEmailInSidePanel` + `SidePanelComposeEmailPage` - `EmailThreadWidget` inline Reply bar with toggle composer - `ReplyToEmailThreadCommand` now opens side-panel instead of external links ### Seeds - Added `handle` field to message participant seeds for realistic email addresses - Seed `connectedAccount` and `messageChannel` in correct batch order ## Test plan - [ ] Open an email thread on a person/company record → verify "Reply..." bar appears below the last message - [ ] Click "Reply..." → composer opens inline with pre-filled To and Subject - [ ] Type a message and click Send → email is sent via SMTP, composer closes - [ ] Use command menu Reply action → side panel opens with compose email page - [ ] Verify Send/Cancel buttons work correctly in side panel - [ ] Test with Cc/Bcc toggle in composer fields - [ ] Verify error handling: invalid recipients, missing connected account Made with [Cursor](https://cursor.com) --------- Co-authored-by: Claude Opus 4.6 <[email protected]> |
||
|
|
e062343802 |
Refactor typeorm migration lifecycle and generation (#19275)
# Introduction Typeorm migration are now associated to a given twenty-version from the `UPGRADE_COMMAND_SUPPORTED_VERSIONS` that the current twenty core engine handles This way when we upgrade we retrieve the migrations that need to be run, this will be useful for the cross-version incremental upgrade so we preserve sequentiality ## What's new To generate ```sh npx nx database:migrate:generate twenty-server -- --name add-index-to-users ``` To apply all ```sh npx nx database:migrate twenty-server ``` ## Next Introduce slow and fast typeorm migration in order to get rid of the save point pattern in our code base Create a clean and dedicated `InstanceUpgradeService` abstraction |
||
|
|
119014f86d |
Improve apps (#19256)
- simplify the base application template - remove --exhaustive option and replace by a --example option like in next.js https://nextjs.org/docs/app/api-reference/cli - Fix some bugs and logs - add a post-card app in twenty-apps/examples/ |
||
|
|
331c223e73 |
Fix create e2e app ci set version flakiness (#19278)
Calling npm version in // with interdependent packages fait des chocapics <img width="225" height="225" alt="image" src="https://github.com/user-attachments/assets/6d10e72a-87dc-4745-b078-7dd1b4706203" /> |
||
|
|
16e3e38b79 |
Improve getting started doc (#19138)
- improves `packages/twenty-docs/developers/extend/apps/getting-started.mdx` --------- Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> |
||
|
|
ac8e0d4217 |
Replace twentycrm/twenty-postgres-spilo with official postgres:16 in CI (#19182)
## Summary
- Replaces `twentycrm/twenty-postgres-spilo` with the official
`postgres:16` image across all 7 CI workflow files
- Removes Docker Hub `credentials` blocks from all service containers
(postgres, redis, clickhouse)
- Removes the `Login to Docker Hub` step from the breaking changes
workflow
## Context
Fork PRs cannot access repository secrets/variables, causing `${{
vars.DOCKERHUB_USERNAME }}` and `${{ secrets.DOCKERHUB_PASSWORD }}` to
resolve to empty strings. GitHub Actions rejects empty credential values
at template validation time, failing the job before any step runs.
The custom spilo image was the original reason credentials were needed
(to avoid Docker Hub rate limits on non-official images). The only
Postgres extensions required in CI (`uuid-ossp`, `unaccent`) are built
into the official `postgres:16` image. Official Docker Hub images have
significantly higher pull rate limits and don't require authentication.
|
||
|
|
37908114fc |
[SDK] Extract twenty-front-component-renderer outside of twenty-sdk ( 2.8MB ) (#19021)
Followup https://github.com/twentyhq/twenty/pull/19010 ## Dependency diagram ``` ┌─────────────────────┐ │ twenty-front │ │ (React frontend) │ └─────────┬───────────┘ │ imports runtime: │ FrontComponentRenderer │ FrontComponentRendererWithSdkClient │ useFrontComponentExecutionContext ▼ ┌──────────────────────────────────┐ ┌─────────────────────────┐ │ twenty-front-component-renderer │────────▶│ twenty-sdk │ │ (remote-dom host + worker) │ │ (app developer SDK) │ │ │ │ │ │ imports from twenty-sdk: │ │ Public API: │ │ • types only: │ │ defineFrontComponent │ │ FrontComponentExecutionContext│ │ navigate, closeSide… │ │ NavigateFunction │ │ useFrontComponent… │ │ CloseSidePanelFunction │ │ Command components │ │ CommandConfirmation… │ │ conditional avail. │ │ OpenCommandConfirmation… │ │ │ │ EnqueueSnackbarFunction │ │ Internal only: │ │ etc. │ │ frontComponentHost… │ │ │ │ front-component-build │ │ owns locally: │ │ esbuild plugins │ │ • ALLOWED_HTML_ELEMENTS │ │ │ │ • EVENT_TO_REACT │ └────────────┬────────────┘ │ • HTML_TAG_TO_CUSTOM_ELEMENT… │ │ │ • SerializedEventData │ │ types │ • PropertySchema │ ▼ │ • frontComponentHostComm… │ ┌─────────────────────────┐ │ (local ref to globalThis) │ │ twenty-shared │ │ • setFrontComponentExecution… │ │ (common types/utils) │ │ (local impl, same keys) │ │ AppPath, SidePanelP… │ │ │ │ EnqueueSnackbarParams │ └──────────────────────────────────┘ │ isDefined, … │ │ └─────────────────────────┘ │ also depends on ▼ twenty-shared (types) @remote-dom/* (runtime) @quilted/threads (runtime) react (runtime) ``` **Key points:** - **`twenty-front`** depends on the renderer, **not** on `twenty-sdk` directly (for rendering) - **`twenty-front-component-renderer`** depends on `twenty-sdk` for **types only** (function signatures, `FrontComponentExecutionContext`). The runtime bridge (`frontComponentHostCommunicationApi`) is shared via `globalThis` keys, not module imports - **`twenty-sdk`** has no dependency on the renderer — clean one-way dependency - The renderer owns all remote-dom infrastructure (element schemas, event mappings, custom element tags) that was previously leaking through the SDK's public API - The SDK's `./build` entry point was removed entirely (unused) |
||
|
|
8fa3962e1c |
feat: add resumable stream support for agent chat (#19107)
## Overview Add resumable stream support for agent chat to allow clients to reconnect and resume streaming responses if the connection is interrupted (e.g., during page refresh). ## Changes ### Backend (Twenty Server) - Add `activeStreamId` column to `AgentChatThreadEntity` to track ongoing streams - Create `AgentChatResumableStreamService` to manage Redis-backed resumable streams using the `resumable-stream` library with ioredis - Extend `AgentChatController` with: - `GET /:threadId/stream` endpoint to resume an existing stream - `DELETE /:threadId/stream` endpoint to stop an active stream - Update `AgentChatStreamingService` to store streams in Redis and track active stream IDs - Add `resumable-stream@^2.2.12` dependency to package.json ### Frontend (Twenty Front) - Update `useAgentChat` hook to: - Use a persistent transport with `prepareReconnectToStreamRequest` for resumable streams - Export `resumeStream` function from useChat - Add `handleStop` callback to clear active stream on DELETE endpoint - Use thread ID as stable message ID instead of including message count - Add stream resumption logic in `AgentChatAiSdkStreamEffect` component to automatically call `resumeStream()` when switching threads ## Database Migration New migration `1774003611071-add-active-stream-id-to-agent-chat-thread` adds the `activeStreamId` column to store the current resumable stream identifier. --------- Co-authored-by: Claude Opus 4.6 <[email protected]> |
||
|
|
c0086646fd |
[APP] Stricter API assertions (#19114)
# Introduction Improved the ci assertions to be sure the default logic function worked fully ## what's next About to introduce a s3 + lambda e2e test to cover the lambda driver too in addition of the local driver |
||
|
|
191a277ddf |
fix: invalidate rolesPermissions cache + add Docker Hub auth to CI (#19044)
## Summary ### Cache invalidation fix - After migrating object/field permissions to syncable entities (#18609, #18751, #18567), changes to `flatObjectPermissionMaps`, `flatFieldPermissionMaps`, or `flatPermissionFlagMaps` no longer triggered `rolesPermissions` cache invalidation - This caused stale permission data to be served, leading to flaky `permissions-on-relations` integration tests and potentially incorrect permission enforcement in production after object permission upserts - Adds the three permission-related flat map keys to the condition that triggers `rolesPermissions` cache recomputation in `WorkspaceMigrationRunnerService.getLegacyCacheInvalidationPromises` - Clears memoizer after recomputation to prevent concurrent `getOrRecompute` calls from caching stale data ### Docker Hub rate limit fix - CI service containers (postgres, redis, clickhouse) and `docker run`/`docker build` steps were pulling from Docker Hub **unauthenticated**, hitting the 100-pull-per-6-hour rate limit on shared GitHub-hosted runner IPs - Adds `credentials` blocks to all service container definitions and `docker/login-action` steps before `docker run`/`docker compose` commands - Uses `vars.DOCKERHUB_USERNAME` + `secrets.DOCKERHUB_PASSWORD` (matching the existing twenty-infra convention) - Affected workflows: ci-server, ci-merge-queue, ci-breaking-changes, ci-zapier, ci-sdk, ci-create-app-e2e, ci-website, ci-test-docker-compose, preview-env-keepalive, spawn-twenty-docker-image action |
||
|
|
281bb6d783 |
Guard yarn database:migrate:prod (#19008)
## Motivations A lot of self hosters hands up using the `yarn database:migrated:prod` either manually or through AI assisted debug while they try to upgrade an instance while their workspace is still blocked in a previous one Leading to their whole database permanent corruption ## What happened Replaced the direct call the the typeorm cli to a command calling it programmatically, adding a layer of security in case a workspace seems to be blocked in a previous version than the one just before the one being installed ( e.g 1.0 when you try to upgrade from 1.1 to 1.2 ) For our cloud we still need a way to bypass this security explaining the -f flag ## Remark Centralized this logic and refactored creating new services `WorkspaceVersionService` and `CoreEngineVersionService` that will become useful for the upcoming upgrade refactor Related to https://github.com/twentyhq/twenty-infra/pull/529 |