Expert guidance on Turborepo build orchestration and remote caching workflow...
This skill covers the build orchestration and remote caching patterns used in the Squareone monorepo.
ā ļø ALWAYS use root-level pnpm scripts - Never run individual package scripts or call turbo directly (unless environment variables are pre-set in CI/CD).
Only the root package.json scripts use the wrapper (scripts/turbo-wrapper.js) that enables remote caching with authentication.
Individual package.json scripts bypass the wrapper and remote caching, resulting in slower builds and missed cache hits.
# Root script with filter (from repository root)
pnpm test --filter @lsst-sqre/squared
# Root scripts for all packages
pnpm build
pnpm lint
pnpm type-check
pnpm dev --filter squareone
# Individual package script (bypasses wrapper!)
cd packages/squared && pnpm test
# Direct turbo call without env vars (bypasses wrapper!)
turbo run test --filter @lsst-sqre/squared
# Running from package directory (bypasses wrapper!)
cd apps/squareone && pnpm dev
Direct turbo calls are acceptable only when TURBO_API, TURBO_TOKEN, and TURBO_TEAM are already set as environment variables:
# In CI/CD pipelines with pre-set env vars
export TURBO_API="https://roundtable.lsst.cloud/turborepo-cache"
export TURBO_TOKEN="$SECRET_TOKEN"
export TURBO_TEAM="team_squareone"
turbo run build # OK in this context
In Docker builds or CI/CD where these env vars are injected, direct turbo calls work because the wrapper script detects them first (Priority 1).
# Build all packages and apps
pnpm build
# Build specific package
pnpm build --filter @lsst-sqre/squared
# Build specific app
pnpm build --filter squareone
# Build with increased memory (if needed)
NODE_OPTIONS="--max_old_space_size=4096" pnpm build
# Start all dev servers
pnpm dev
# Start specific app dev server
pnpm dev --filter squareone
# Start specific package dev server
pnpm dev --filter @lsst-sqre/squared
# Run all tests
pnpm test
# Run tests for specific package
pnpm test --filter @lsst-sqre/squared
# Run Storybook tests
pnpm test-storybook
pnpm test-storybook:watch
pnpm test-storybook --filter @lsst-sqre/squared
# Run ESLint
pnpm lint
pnpm lint --filter squareone
# Run TypeScript type checking
pnpm type-check
pnpm type-check --filter @lsst-sqre/squared
# Format code with Prettier
pnpm format
# Start Storybook for all packages
pnpm storybook
# Start Storybook for specific package
pnpm storybook --filter @lsst-sqre/squared
# Build Storybook static site
pnpm build-storybook --filter squared
Turborepo's filter syntax allows targeting specific packages:
# By package name
pnpm build --filter @lsst-sqre/squared
# By app name (no scope prefix for apps)
pnpm dev --filter squareone
# Multiple filters
pnpm test --filter @lsst-sqre/squared --filter squareone
# Dependents (packages that depend on this)
pnpm build --filter ...@lsst-sqre/squared
# Dependencies (packages this depends on)
pnpm build --filter @lsst-sqre/squared...
The monorepo uses a custom Turborepo cache server at https://roundtable.lsst.cloud/turborepo-cache for faster builds through remote caching.
See the complete documentation at docs/dev/remote-cache.rst.
The turbo-wrapper.js script checks for authentication in priority order:
TURBO_API, TURBO_TOKEN, TURBO_TEAM all set (CI/CD).env.op file + op CLI available (secure local development).env file present (local development without 1Password)When running commands, you'll see:
š Using environment variables for Turborepo remote cache authenticationš Using 1Password for Turborepo remote cache authenticationš Using .env for Turborepo remote cache authenticationā¹ļø Running Turborepo without remote cache (local cache only)Create .env.op file from template:
cp .env.op.example .env.op
Edit .env.op to reference your 1Password vault items
Install 1Password CLI:
brew install 1password-cli
Sign in to 1Password:
op signin lsstit.1password.com
Create .env file from template:
cp .env.example .env
Add your credentials to .env:
TURBO_API=https://roundtable.lsst.cloud/turborepo-cache
TURBO_TOKEN=your_token_here
TURBO_TEAM=team_squareone
Never commit .env files - they're in .gitignore
write:turborepo scope.env fileLook for these indicators in Turborepo output:
>>> FULL TURBO
>>> Remote caching enabled
@lsst-sqre/squared:build: cache hit, replaying output...
squareone:build: cache hit, replaying output...
For detailed caching information:
TURBO_LOG_LEVEL=debug pnpm build
Symptoms: No "Remote caching enabled" message, slow builds
Solutions:
write:turborepo scopeTURBO_LOG_LEVEL=debug to see detailed logs.env or .env.opSymptoms: Warning message about 1Password CLI not available
Solutions:
# Verify installation
op --version
# Install if needed
brew install 1password-cli
# Sign in
op signin lsstit.1password.com
Symptoms: Build failures or incorrect behavior despite clean checkout
Solutions:
# Force rebuild (ignore cache)
pnpm build --force
# Clear local turbo cache
rm -rf node_modules/.cache/turbo
# Clear all node_modules and reinstall
pnpm clean && pnpm install
Symptoms: Build fails with mysterious errors
Solutions:
pnpm installpnpm clean && pnpm buildSymptoms: Specific package fails to build or isn't found
Solutions:
# Verify package name is correct
ls packages/
# Check turbo.json for correct package name
cat turbo.json
# Try building just that package
pnpm build --filter @lsst-sqre/package-name
# Check package.json for valid build script
cat packages/package-name/package.json
Sometimes useful for testing local-only builds:
# Rename config files
mv .env .env.backup
mv .env.op .env.op.backup
# Or use build:local script (if available)
pnpm build:local
# Or call turbo directly (bypasses wrapper)
npx turbo build
The monorepo's build pipeline is configured in turbo.json:
{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "dist/**", "build/**"]
},
"test": {
"dependsOn": ["build"],
"outputs": ["coverage/**"]
},
"lint": {},
"type-check": {}
}
}
build - Depends on dependencies' builds (^build), caches outputstest - Depends on build, caches coveragelint - No dependencies, can run in paralleltype-check - No dependencies, can run in parallelTurborepo caches specified output directories:
.next/** - Next.js build outputdist/** - Package build outputbuild/** - Other build artifactscoverage/** - Test coverage reportsThe monorepo has these package relationships:
@lsst-sqre/squared (component library)
āāā @lsst-sqre/global-css (styles)
ā āāā @lsst-sqre/rubin-style-dictionary (tokens)
āāā @lsst-sqre/eslint-config
āāā @lsst-sqre/tsconfig
squareone (Next.js app)
āāā @lsst-sqre/squared
āāā @lsst-sqre/global-css
āāā @lsst-sqre/eslint-config
Building squared automatically builds its dependencies (global-css, rubin-style-dictionary).
In CI/CD pipelines:
# GitHub Actions example
env:
TURBO_API: ${{ secrets.TURBO_API }}
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
steps:
- run: pnpm install
- run: pnpm build # Uses environment variables for remote cache
- run: pnpm test
Environment variables take priority, so the wrapper automatically uses them without needing .env files.
# Increase Node.js memory for large builds
NODE_OPTIONS="--max_old_space_size=4096" pnpm build
# Parallel execution is automatic via Turborepo
# No need to manually parallelize
# Use filters to avoid unnecessary work
pnpm test --filter @lsst-sqre/squared # Only test one package
# Remote cache dramatically speeds up CI/CD
# and switching branches
pnpm install # Update dependencies
pnpm build # Rebuild (uses remote cache if possible)
pnpm lint # Check code style
pnpm type-check # Check TypeScript
pnpm test # Run tests
packages/ or apps/pnpm-workspace.yaml (usually automatic)package.jsonturbo.json if neededpnpm install from root# 1. Clean everything
pnpm clean
# 2. Reinstall dependencies
pnpm install
# 3. Build with debug logging
TURBO_LOG_LEVEL=debug pnpm build --force
# 4. Check specific package
pnpm build --filter @lsst-sqre/package-name --force
docs/dev/remote-cache.rst - Complete remote cache documentationscripts/turbo-wrapper.js - Wrapper script source codeturbo.json - Pipeline configurationpnpm-workspace.yaml - Workspace configurationThe Turborepo cache server is deployed as part of the Rubin Science Platform:
write:turborepo scopeComponents:
turborepo-cache-proxy - Exchanges Gafaelfawr token for cache authenticationturborepo-remote-cache - Cache server implementation