mirror of
https://github.com/simstudioai/sim.git
synced 2026-10-24 01:23:02 +08:00
* refactor(utils): consolidate duplicated helpers onto @sim/utils Replaces ~90 hand-rolled reimplementations of error-message extraction, postgres error-code checks, sleep, Math.random, retry/backoff, object filtering/omission, noop, string truncation, date/time formatting, email normalization, and plain-object type guards with the shared @sim/utils exports. Wires check:utils into CI (test-build.yml) so these patterns don't regress. * fix(test): mock @sim/utils/random instead of Math.random in schedule-execute tests The schedules/execute route previously used Math.random() for jitter delay; this consolidation PR switched it to randomInt() from @sim/utils/random, which is backed by crypto.getRandomValues() rather than Math.random(). The route.test.ts spies on Math.random() no longer had any effect, so jitter became real random delay instead of the deterministic 0ms the tests expect, causing intermittent 10s timeouts in CI. * fix(retry): preserve uncapped Retry-After comparison in tools/index.ts parseRetryAfter() caps its return value at 30s by default. tools/index.ts compares the parsed Retry-After against a caller-configured maxDelayMs to decide whether to skip a retry entirely -- capping before that comparison silently defeats the skip check whenever maxDelayMs is configured above 30s, since a Retry-After between 30s and maxDelayMs would incorrectly look "within limits" and get retried instead of skipped (caught by Cursor Bugbot). Added an optional maxMs param (default unchanged) so tools/index.ts can request the raw, uncapped value for its own comparison while backoffWithJitter still clamps the actual sleep duration to maxDelayMs. Added a regression test covering maxDelayMs > 30s. * fix(utils): fall back to Intl-resolved abbreviation for unmapped timezones getTimezoneAbbreviation only covered 9 hardcoded IANA zones and returned the raw IANA string for everything else, degrading schedule descriptions for zones like Europe/Berlin or America/Toronto (caught by Greptile). The deleted local implementation in schedules/utils.ts resolved any valid IANA timezone generically via Intl.DateTimeFormat's short timeZoneName. Restore that as a fallback so only genuinely invalid timezone strings return themselves unchanged.
193 lines
6.9 KiB
YAML
193 lines
6.9 KiB
YAML
name: Test and Build
|
|
|
|
on:
|
|
workflow_call:
|
|
workflow_dispatch:
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
test-build:
|
|
name: Test and Build
|
|
runs-on: blacksmith-8vcpu-ubuntu-2404
|
|
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
|
|
|
- name: Setup Bun
|
|
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
|
|
with:
|
|
bun-version: 1.3.13
|
|
|
|
- name: Setup Node
|
|
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
|
with:
|
|
node-version: latest
|
|
|
|
- name: Mount Bun cache (Sticky Disk)
|
|
uses: useblacksmith/stickydisk@4c034ba57b706cf0e3b4b0ce098c2a3b1071580c # v1
|
|
with:
|
|
key: ${{ github.repository }}-bun-cache
|
|
path: ~/.bun/install/cache
|
|
|
|
- name: Mount node_modules (Sticky Disk)
|
|
uses: useblacksmith/stickydisk@4c034ba57b706cf0e3b4b0ce098c2a3b1071580c # v1
|
|
with:
|
|
key: ${{ github.repository }}-node-modules
|
|
path: ./node_modules
|
|
|
|
- name: Mount Turbo cache (Sticky Disk)
|
|
uses: useblacksmith/stickydisk@4c034ba57b706cf0e3b4b0ce098c2a3b1071580c # v1
|
|
with:
|
|
key: ${{ github.repository }}-turbo-cache
|
|
path: ./.turbo
|
|
|
|
- name: Restore Next.js build cache
|
|
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
|
with:
|
|
path: ./apps/sim/.next/cache
|
|
key: ${{ runner.os }}-nextjs-${{ hashFiles('bun.lock') }}
|
|
restore-keys: |
|
|
${{ runner.os }}-nextjs-
|
|
|
|
- name: Install dependencies
|
|
run: bun install --frozen-lockfile
|
|
|
|
# Surfaces known CVEs in the dependency tree. Non-blocking until the
|
|
# existing advisory backlog is triaged, then flip to a required gate by
|
|
# removing continue-on-error.
|
|
- name: Security audit
|
|
run: bun audit
|
|
continue-on-error: true
|
|
|
|
- name: Validate env flags
|
|
run: |
|
|
FILE="apps/sim/lib/core/config/env-flags.ts"
|
|
ERRORS=""
|
|
|
|
echo "Checking for hardcoded boolean env flags..."
|
|
|
|
# Use perl for multiline matching to catch both:
|
|
# export const isHosted = true
|
|
# export const isHosted =
|
|
# true
|
|
HARDCODED=$(perl -0777 -ne 'while (/export const (is[A-Za-z]+)\s*=\s*\n?\s*(true|false)\b/g) { print " $1 = $2\n" }' "$FILE")
|
|
|
|
if [ -n "$HARDCODED" ]; then
|
|
ERRORS="${ERRORS}\n❌ Env flags must not be hardcoded to boolean literals!\n\nFound hardcoded flags:\n${HARDCODED}\n\nEnv flags should derive their values from environment variables.\n"
|
|
fi
|
|
|
|
echo "Checking env flag naming conventions..."
|
|
|
|
# Check that all export const (except functions) start with 'is'
|
|
# This finds exports like "export const someFlag" that don't start with "is" or "get"
|
|
BAD_NAMES=$(grep -E "^export const [a-z]" "$FILE" | grep -vE "^export const (is|get)" | sed 's/export const \([a-zA-Z]*\).*/ \1/')
|
|
|
|
if [ -n "$BAD_NAMES" ]; then
|
|
ERRORS="${ERRORS}\n❌ Env flags must use 'is' prefix for boolean flags!\n\nFound incorrectly named flags:\n${BAD_NAMES}\n\nExample: 'hostedMode' should be 'isHostedMode'\n"
|
|
fi
|
|
|
|
if [ -n "$ERRORS" ]; then
|
|
echo ""
|
|
echo -e "$ERRORS"
|
|
exit 1
|
|
fi
|
|
|
|
echo "✅ All env flags are properly configured"
|
|
|
|
- name: Check block registry invariants
|
|
run: |
|
|
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
|
BASE_REF="origin/${{ github.base_ref }}"
|
|
git fetch --depth=1 origin "${{ github.base_ref }}" 2>/dev/null || true
|
|
else
|
|
BASE_REF="HEAD~1"
|
|
fi
|
|
bun run apps/sim/scripts/check-block-registry.ts "$BASE_REF"
|
|
|
|
- name: Lint code
|
|
run: bun run lint:check
|
|
|
|
- name: Enforce monorepo boundaries
|
|
run: bun run check:boundaries
|
|
|
|
- name: API contract boundary audit
|
|
run: bun run check:api-validation:strict
|
|
|
|
- name: Shared utils enforcement audit
|
|
run: bun run check:utils
|
|
|
|
- name: Zustand v5 selector audit
|
|
run: bun run check:zustand-v5
|
|
|
|
- name: React Query pattern audit
|
|
run: bun run check:react-query
|
|
|
|
- name: Client boundary import audit
|
|
run: bun run check:client-boundary
|
|
|
|
- name: Bare-icon theme-safety audit
|
|
run: bun run check:bare-icons
|
|
|
|
- name: Icon SVG path validity audit
|
|
run: bun run check:icon-paths
|
|
|
|
- name: Verify realtime prune graph
|
|
run: bun run check:realtime-prune
|
|
|
|
- name: Migration safety (zero-downtime) audit
|
|
run: |
|
|
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
|
BASE_REF="origin/${{ github.base_ref }}"
|
|
git fetch --depth=1 origin "${{ github.base_ref }}" 2>/dev/null || true
|
|
else
|
|
BASE_REF="HEAD~1"
|
|
fi
|
|
bun run check:migrations "$BASE_REF"
|
|
|
|
- name: Type-check realtime server
|
|
run: bunx turbo run type-check --filter=@sim/realtime
|
|
|
|
- name: Run tests with coverage
|
|
env:
|
|
NODE_OPTIONS: '--no-warnings --max-old-space-size=8192'
|
|
NEXT_PUBLIC_APP_URL: 'https://www.sim.ai'
|
|
DATABASE_URL: 'postgresql://postgres:postgres@localhost:5432/simstudio'
|
|
ENCRYPTION_KEY: '7cf672e460e430c1fba707575c2b0e2ad5a99dddf9b7b7e3b5646e630861db1c' # dummy key for CI only
|
|
TURBO_CACHE_DIR: .turbo
|
|
run: bun run test
|
|
|
|
- name: Check schema and migrations are in sync
|
|
working-directory: packages/db
|
|
run: |
|
|
bunx drizzle-kit generate --config=./drizzle.config.ts
|
|
if [ -n "$(git status --porcelain ./migrations)" ]; then
|
|
echo "❌ Schema and migrations are out of sync!"
|
|
echo "Run 'cd packages/db && bunx drizzle-kit generate' and commit the new migrations."
|
|
git status --porcelain ./migrations
|
|
git diff ./migrations
|
|
exit 1
|
|
fi
|
|
echo "✅ Schema and migrations are in sync"
|
|
|
|
- name: Build application
|
|
env:
|
|
NODE_OPTIONS: '--no-warnings --max-old-space-size=8192'
|
|
NEXT_PUBLIC_APP_URL: 'https://www.sim.ai'
|
|
DATABASE_URL: 'postgresql://postgres:postgres@localhost:5432/simstudio'
|
|
STRIPE_SECRET_KEY: 'dummy_key_for_ci_only'
|
|
STRIPE_WEBHOOK_SECRET: 'dummy_secret_for_ci_only'
|
|
RESEND_API_KEY: 'dummy_key_for_ci_only'
|
|
AWS_REGION: 'us-west-2'
|
|
ENCRYPTION_KEY: '7cf672e460e430c1fba707575c2b0e2ad5a99dddf9b7b7e3b5646e630861db1c' # dummy key for CI only
|
|
TURBO_CACHE_DIR: .turbo
|
|
run: bunx turbo run build --filter=sim
|
|
|
|
- name: Upload coverage to Codecov
|
|
uses: codecov/codecov-action@0fb7174895f61a3b6b78fc075e0cd60383518dac # v5
|
|
with:
|
|
directory: ./apps/sim/coverage
|
|
fail_ci_if_error: false
|
|
verbose: true |