chore: add Turborepo with remote caching (#7697)

* chore: add Turborepo with remote caching

Pipeline lint, type-check, test, prettier, and knip:exports through
Turborepo so repeat runs (locally and in CI) hit a shared cache instead
of re-executing. Wire Vercel remote cache via TURBO_TOKEN/TURBO_TEAM
in .github/actions/yarn, and route web-checks.yml through turbo.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>

* chore: pass extra args through turbo to underlying tasks

Husky pre-push calls `yarn run lint --fix`, and turbo was swallowing
`--fix` as its own flag. Appending `--` in the root scripts forwards
extra args straight to each workspace's command.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>

---------

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
This commit is contained in:
katspaugh
2026-04-22 18:53:53 +02:00
committed by GitHub
co-authored by Hanzo Dev
parent e43160a41c
commit 8f498b4439
8 changed files with 184 additions and 8 deletions
+16
View File
@@ -7,6 +7,14 @@ inputs:
description: 'Run web after-install step to generate contract types'
required: false
default: 'true'
turbo-token:
description: 'Turborepo remote cache token (Vercel PAT)'
required: false
default: ''
turbo-team:
description: 'Turborepo remote cache team slug'
required: false
default: ''
runs:
using: 'composite'
@@ -18,6 +26,14 @@ runs:
node-version: '24.14.0'
cache: 'yarn'
- name: Export Turbo remote cache env
if: inputs.turbo-token != ''
shell: bash
run: |
echo "TURBO_TOKEN=${{ inputs.turbo-token }}" >> "$GITHUB_ENV"
echo "TURBO_TEAM=${{ inputs.turbo-team }}" >> "$GITHUB_ENV"
echo "TURBO_REMOTE_ONLY=false" >> "$GITHUB_ENV"
- name: Restore Yarn Cache & Types
id: restore-yarn-types
uses: ./.github/actions/cache-deps
+15 -4
View File
@@ -27,6 +27,9 @@ jobs:
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/yarn
with:
turbo-token: ${{ secrets.TURBO_TOKEN }}
turbo-team: ${{ vars.TURBO_TEAM }}
- name: Run ESLint with suggestions (PR only)
if: github.event_name == 'pull_request'
uses: CatChen/eslint-suggestion-action@v4.1.29
@@ -39,7 +42,7 @@ jobs:
config-path: './apps/web/eslint.config.mjs'
- name: Run ESLint (push only)
if: github.event_name == 'push'
run: yarn workspace @safe-global/web lint
run: yarn turbo run lint --filter=@safe-global/web
prettier:
permissions:
@@ -50,6 +53,9 @@ jobs:
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/yarn
with:
turbo-token: ${{ secrets.TURBO_TOKEN }}
turbo-team: ${{ vars.TURBO_TEAM }}
- name: Run Prettier check
run: yarn workspace @safe-global/web prettier
@@ -62,9 +68,11 @@ jobs:
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/yarn
with:
turbo-token: ${{ secrets.TURBO_TOKEN }}
turbo-team: ${{ vars.TURBO_TEAM }}
- name: Type check
run: yarn type-check
working-directory: apps/web
run: yarn turbo run type-check --filter=@safe-global/web
knip-exports:
permissions:
@@ -75,8 +83,11 @@ jobs:
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/yarn
with:
turbo-token: ${{ secrets.TURBO_TOKEN }}
turbo-team: ${{ vars.TURBO_TEAM }}
- name: Check for unused exports
run: yarn workspace @safe-global/web knip:exports
run: yarn turbo run knip:exports --filter=@safe-global/web
notify:
if: failure() && github.event_name == 'push'
+4
View File
@@ -62,6 +62,10 @@ yarn-error.log*
# vercel
.vercel
# turborepo
.turbo/
**/.turbo/
# typescript
*.tsbuildinfo
+3
View File
@@ -9,6 +9,9 @@
.env
.env.*
# Turborepo cache
**/.turbo/
# Mobile
apps/mobile/android
apps/mobile/ios
+22
View File
@@ -23,6 +23,28 @@ yarn workspace @safe-global/web test
yarn workspace @safe-global/web storybook
```
## Turborepo
Root-level `lint`, `type-check`, and `test` run through [Turborepo](https://turborepo.com). Tasks are cached by input hash and re-used on subsequent runs — locally and in CI.
```bash
yarn type-check # all workspaces (cached)
yarn turbo run type-check --filter=@safe-global/web # scoped
yarn turbo run test --filter=@safe-global/utils... # package + dependents
```
Cache directory is `.turbo/` (gitignored). Task definitions live in `turbo.json`.
### Remote cache
CI reads `TURBO_TOKEN` (repo secret) and `TURBO_TEAM` (repo variable) via `.github/actions/yarn`. These must be configured once per Vercel team:
1. Create or pick a Vercel team; copy the team slug → set repo variable `TURBO_TEAM`.
2. Create a Vercel personal access token with access to that team → set repo secret `TURBO_TOKEN`.
3. Locally: `yarn turbo login && yarn turbo link` to enable remote cache in development.
Self-hosted cache (e.g. [ducktors/turborepo-remote-cache](https://github.com/ducktors/turborepo-remote-cache)) can be wired by setting `TURBO_API`, `TURBO_TOKEN`, `TURBO_TEAM` — the same env vars the Vercel backend uses.
## Architecture Overview
- **apps/web** - Next.js web application
+5 -4
View File
@@ -10,9 +10,9 @@
"tools/codemods/*"
],
"scripts": {
"lint": "yarn workspaces foreach --all -pt run lint",
"type-check": "yarn workspaces foreach --all -pt run type-check",
"test": "yarn workspaces foreach --all -pt run test",
"lint": "turbo run lint --",
"type-check": "turbo run type-check --",
"test": "turbo run test --",
"test:scripts": "yarn workspace @safe-global/web jest --config ../../scripts/jest.config.cjs",
"eslint": "yarn workspaces foreach --all -pt run eslint",
"prettier": "prettier --check . --config ./.prettierrc --ignore-path ./.prettierignore",
@@ -45,7 +45,8 @@
"lint-staged": "^16.2.7",
"msw": "^2.7.3",
"prettier": "^3.6.2",
"storybook": "^10.2.7"
"storybook": "^10.2.7",
"turbo": "^2.9.6"
},
"dependenciesMeta": {
"cypress": {
+47
View File
@@ -0,0 +1,47 @@
{
"$schema": "https://turborepo.com/schema.json",
"ui": "stream",
"globalDependencies": [
"tsconfig.base.json",
".prettierrc",
".prettierignore",
"config/tsconfig/*",
"config/eslint/*"
],
"globalEnv": ["NODE_ENV", "CI", "TZ"],
"globalPassThroughEnv": ["TURBO_TOKEN", "TURBO_TEAM", "TURBO_API", "TURBO_REMOTE_CACHE_SIGNATURE_KEY"],
"tasks": {
"type-check": {
"inputs": ["src/**/*.{ts,tsx,js,jsx,json}", "tsconfig*.json", "package.json", "next-env.d.ts", "**/*.d.ts"],
"outputs": [],
"outputLogs": "new-only"
},
"lint": {
"inputs": ["src/**/*.{ts,tsx,js,jsx}", "eslint.config.{js,mjs,cjs,ts}", ".eslintrc*", "package.json"],
"outputs": [],
"outputLogs": "new-only"
},
"test": {
"inputs": [
"src/**/*.{ts,tsx,js,jsx,json,snap}",
"jest.config.{js,cjs,mjs,ts}",
"jest.setup.{js,cjs,mjs,ts}",
"package.json",
"**/*.d.ts"
],
"outputs": ["coverage/**"],
"env": ["NEXT_PUBLIC_IS_OFFICIAL_HOST", "DEBUG_PRINT_LIMIT", "LC_ALL"],
"outputLogs": "new-only"
},
"prettier": {
"inputs": ["**/*.{ts,tsx,js,jsx,json,md,yml,yaml,css,scss}", "../../.prettierrc", "../../.prettierignore"],
"outputs": [],
"outputLogs": "new-only"
},
"knip:exports": {
"inputs": ["src/**", "knip.{js,ts,json}", "package.json"],
"outputs": [],
"outputLogs": "new-only"
}
}
}
+72
View File
@@ -13345,6 +13345,7 @@ __metadata:
msw: "npm:^2.7.3"
prettier: "npm:^3.6.2"
storybook: "npm:^10.2.7"
turbo: "npm:^2.9.6"
dependenciesMeta:
cypress:
built: true
@@ -18133,6 +18134,48 @@ __metadata:
languageName: node
linkType: hard
"@turbo/darwin-64@npm:2.9.6":
version: 2.9.6
resolution: "@turbo/darwin-64@npm:2.9.6"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
"@turbo/darwin-arm64@npm:2.9.6":
version: 2.9.6
resolution: "@turbo/darwin-arm64@npm:2.9.6"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
"@turbo/linux-64@npm:2.9.6":
version: 2.9.6
resolution: "@turbo/linux-64@npm:2.9.6"
conditions: os=linux & cpu=x64
languageName: node
linkType: hard
"@turbo/linux-arm64@npm:2.9.6":
version: 2.9.6
resolution: "@turbo/linux-arm64@npm:2.9.6"
conditions: os=linux & cpu=arm64
languageName: node
linkType: hard
"@turbo/windows-64@npm:2.9.6":
version: 2.9.6
resolution: "@turbo/windows-64@npm:2.9.6"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
"@turbo/windows-arm64@npm:2.9.6":
version: 2.9.6
resolution: "@turbo/windows-arm64@npm:2.9.6"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
"@tybys/wasm-util@npm:^0.10.0, @tybys/wasm-util@npm:^0.10.1":
version: 0.10.1
resolution: "@tybys/wasm-util@npm:0.10.1"
@@ -43436,6 +43479,35 @@ __metadata:
languageName: node
linkType: hard
"turbo@npm:^2.9.6":
version: 2.9.6
resolution: "turbo@npm:2.9.6"
dependencies:
"@turbo/darwin-64": "npm:2.9.6"
"@turbo/darwin-arm64": "npm:2.9.6"
"@turbo/linux-64": "npm:2.9.6"
"@turbo/linux-arm64": "npm:2.9.6"
"@turbo/windows-64": "npm:2.9.6"
"@turbo/windows-arm64": "npm:2.9.6"
dependenciesMeta:
"@turbo/darwin-64":
optional: true
"@turbo/darwin-arm64":
optional: true
"@turbo/linux-64":
optional: true
"@turbo/linux-arm64":
optional: true
"@turbo/windows-64":
optional: true
"@turbo/windows-arm64":
optional: true
bin:
turbo: bin/turbo
checksum: 10/e2378f55c12029bed600b9080e3d40805431f11ed2c79a9fd57176fb18b75fd85f3bc1fc69ef2f601fb65fc6a4bf19c169f4102590bd47173b66c81a729c789a
languageName: node
linkType: hard
"tw-animate-css@npm:^1.4.0":
version: 1.4.0
resolution: "tw-animate-css@npm:1.4.0"