Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci-next-js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ on:
- 'prettier.config.js'

jobs:
build:
test:
runs-on: ubuntu-latest

steps:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci-react-router.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ on:
- 'prettier.config.js'

jobs:
build:
test:
runs-on: ubuntu-latest

steps:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci-tanstack-start.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ on:
- 'prettier.config.js'

jobs:
build:
test:
runs-on: ubuntu-latest

steps:
Expand Down
185 changes: 180 additions & 5 deletions .github/workflows/generate-stats.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,184 @@ jobs:
- name: Setup pnpm
uses: pnpm/action-setup@v4

- name: Setup Node.js
- name: Setup Node.js (no cache for clean measurements)
uses: actions/setup-node@v4
with:
node-version: '24'
cache: 'pnpm'
# No cache - we want clean measurements

- name: Install dependencies
- name: Detect changed packages
id: detect
run: |
# Check which packages changed in the last commit
PACKAGES=""
if git diff --name-only HEAD~1 HEAD | grep -q "packages/starter-next-js/"; then
PACKAGES="$PACKAGES next"
fi
if git diff --name-only HEAD~1 HEAD | grep -q "packages/starter-react-router/"; then
PACKAGES="$PACKAGES react-router"
fi
if git diff --name-only HEAD~1 HEAD | grep -q "packages/starter-tanstack-start-react/"; then
PACKAGES="$PACKAGES tanstack"
fi
if git diff --name-only HEAD~1 HEAD | grep -q "packages/starter-nuxt/"; then
PACKAGES="$PACKAGES nuxt"
fi

# If no packages changed but package.json changed, measure all
if [ -z "$PACKAGES" ] && git diff --name-only HEAD~1 HEAD | grep -q "package.json"; then
PACKAGES="next react-router tanstack nuxt"
fi

echo "packages=$PACKAGES" >> $GITHUB_OUTPUT
echo "Changed packages: $PACKAGES"

- name: Measure install time (clean, no cache)
id: install
if: steps.detect.outputs.packages != ''
run: |
START=$(date +%s%N)
pnpm install --frozen-lockfile
END=$(date +%s%N)
ELAPSED=$((($END - $START) / 1000000))
echo "time=$ELAPSED" >> $GITHUB_OUTPUT
echo "Install time: ${ELAPSED}ms"

- name: Measure Next.js builds
if: contains(steps.detect.outputs.packages, 'next')
id: next
run: |
# Cold build
START=$(date +%s%N)
pnpm build:next
END=$(date +%s%N)
COLD=$((($END - $START) / 1000000))

# Warm build
START=$(date +%s%N)
pnpm build:next
END=$(date +%s%N)
WARM=$((($END - $START) / 1000000))

echo "cold=$COLD" >> $GITHUB_OUTPUT
echo "warm=$WARM" >> $GITHUB_OUTPUT
echo "Next.js - Cold: ${COLD}ms, Warm: ${WARM}ms"

- name: Measure React Router builds
if: contains(steps.detect.outputs.packages, 'react-router')
id: react_router
run: |
# Cold build
START=$(date +%s%N)
pnpm build:react-router
END=$(date +%s%N)
COLD=$((($END - $START) / 1000000))

# Warm build
START=$(date +%s%N)
pnpm build:react-router
END=$(date +%s%N)
WARM=$((($END - $START) / 1000000))

echo "cold=$COLD" >> $GITHUB_OUTPUT
echo "warm=$WARM" >> $GITHUB_OUTPUT
echo "React Router - Cold: ${COLD}ms, Warm: ${WARM}ms"

- name: Measure TanStack Start builds
if: contains(steps.detect.outputs.packages, 'tanstack')
id: tanstack
run: |
# Cold build
START=$(date +%s%N)
pnpm build:tanstack
END=$(date +%s%N)
COLD=$((($END - $START) / 1000000))

# Warm build
START=$(date +%s%N)
pnpm build:tanstack
END=$(date +%s%N)
WARM=$((($END - $START) / 1000000))

echo "cold=$COLD" >> $GITHUB_OUTPUT
echo "warm=$WARM" >> $GITHUB_OUTPUT
echo "TanStack Start - Cold: ${COLD}ms, Warm: ${WARM}ms"

- name: Measure Nuxt builds
if: contains(steps.detect.outputs.packages, 'nuxt')
id: nuxt
run: |
# Cold build
START=$(date +%s%N)
pnpm build:nuxt
END=$(date +%s%N)
COLD=$((($END - $START) / 1000000))

# Warm build
START=$(date +%s%N)
pnpm build:nuxt
END=$(date +%s%N)
WARM=$((($END - $START) / 1000000))

echo "cold=$COLD" >> $GITHUB_OUTPUT
echo "warm=$WARM" >> $GITHUB_OUTPUT
echo "Nuxt - Cold: ${COLD}ms, Warm: ${WARM}ms"

- name: Save all CI stats
if: steps.detect.outputs.packages != ''
run: |
TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)

# Save Next.js stats
if [[ "${{ steps.detect.outputs.packages }}" == *"next"* ]]; then
mkdir -p packages/starter-next-js
echo '{
"installTimeMs": ${{ steps.install.outputs.time }},
"coldBuildTimeMs": ${{ steps.next.outputs.cold }},
"warmBuildTimeMs": ${{ steps.next.outputs.warm }},
"timingMeasuredAt": "'$TIMESTAMP'",
"runner": "ubuntu-latest"
}' > packages/starter-next-js/.ci-stats.json
fi

# Save React Router stats
if [[ "${{ steps.detect.outputs.packages }}" == *"react-router"* ]]; then
mkdir -p packages/starter-react-router
echo '{
"installTimeMs": ${{ steps.install.outputs.time }},
"coldBuildTimeMs": ${{ steps.react_router.outputs.cold }},
"warmBuildTimeMs": ${{ steps.react_router.outputs.warm }},
"timingMeasuredAt": "'$TIMESTAMP'",
"runner": "ubuntu-latest"
}' > packages/starter-react-router/.ci-stats.json
fi

# Save TanStack Start stats
if [[ "${{ steps.detect.outputs.packages }}" == *"tanstack"* ]]; then
mkdir -p packages/starter-tanstack-start-react
echo '{
"installTimeMs": ${{ steps.install.outputs.time }},
"coldBuildTimeMs": ${{ steps.tanstack.outputs.cold }},
"warmBuildTimeMs": ${{ steps.tanstack.outputs.warm }},
"timingMeasuredAt": "'$TIMESTAMP'",
"runner": "ubuntu-latest"
}' > packages/starter-tanstack-start-react/.ci-stats.json
fi

# Save Nuxt stats
if [[ "${{ steps.detect.outputs.packages }}" == *"nuxt"* ]]; then
mkdir -p packages/starter-nuxt
echo '{
"installTimeMs": ${{ steps.install.outputs.time }},
"coldBuildTimeMs": ${{ steps.nuxt.outputs.cold }},
"warmBuildTimeMs": ${{ steps.nuxt.outputs.warm }},
"timingMeasuredAt": "'$TIMESTAMP'",
"runner": "ubuntu-latest"
}' > packages/starter-nuxt/.ci-stats.json
fi

- name: Install dependencies (if not already installed)
if: steps.detect.outputs.packages == ''
run: pnpm install --frozen-lockfile

- name: Generate stats
Expand All @@ -53,15 +224,19 @@ jobs:

# Commit and push changes
git add .
git commit -m "Update stats after starter changes"
git commit -m "Update CI stats and generated stats after starter changes"
git push --force-with-lease origin automated-stats-update

# Create PR if it doesn't exist
if ! gh pr view automated-stats-update &>/dev/null; then
gh pr create \
--title "Update stats after starter changes" \
--title "Update CI stats and generated stats after starter changes" \
--body "Automated stats update triggered by changes to starter packages.

Includes:
- CI build time measurements (.ci-stats.json)
- Generated framework comparison stats

This PR was automatically created and will auto-merge if all checks pass." \
--base main \
--head automated-stats-update
Expand Down
7 changes: 7 additions & 0 deletions packages/starter-next-js/.ci-stats.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"installTimeMs": 10880,
"coldBuildTimeMs": 7559,
"warmBuildTimeMs": 7491,
"timingMeasuredAt": "2026-01-12T09:30:56Z",
"runner": "ubuntu-latest"
}
4 changes: 2 additions & 2 deletions packages/starter-next-js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
},
"devDependencies": {
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/node": "^25.0.6",
"@types/react": "^19.2.8",
"@types/react-dom": "^19",
"eslint-config-next": "16.1.1",
"tailwindcss": "^4"
Expand Down
7 changes: 7 additions & 0 deletions packages/starter-react-router/.ci-stats.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"installTimeMs": 10880,
"coldBuildTimeMs": 3025,
"warmBuildTimeMs": 3019,
"timingMeasuredAt": "2026-01-12T09:30:56Z",
"runner": "ubuntu-latest"
}
7 changes: 7 additions & 0 deletions packages/starter-tanstack-start-react/.ci-stats.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"installTimeMs": 10880,
"coldBuildTimeMs": 9658,
"warmBuildTimeMs": 9355,
"timingMeasuredAt": "2026-01-12T09:30:56Z",
"runner": "ubuntu-latest"
}
4 changes: 4 additions & 0 deletions packages/stats-generator/src/create-stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { join } from 'node:path'
import { getStarterPackages } from './get-starter-packages.ts'
import { packagesDir } from './constants.ts'
import { saveStats } from './save-stats.ts'
import { getCIStats } from './get-ci-stats.ts'
import type { FrameworkStats, PackageJson } from './types.ts'

async function createStats() {
Expand All @@ -21,9 +22,12 @@ async function createStats() {
const prodCount = Object.keys(dependencies).length
const devCount = Object.keys(devDependencies).length

const ciStats = await getCIStats(pkgDir)

const stats: FrameworkStats = {
prodDependencies: prodCount,
devDependencies: devCount,
...(ciStats ?? {}),
}

await saveStats(pkgDir, stats)
Expand Down
15 changes: 15 additions & 0 deletions packages/stats-generator/src/get-ci-stats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { readFile } from 'node:fs/promises'
import { join } from 'node:path'
import { packagesDir } from './constants.ts'
import { CIStats } from './types.ts'

export async function getCIStats(pkgDir: string) {
const ciStatsPath = join(packagesDir, pkgDir, '.ci-stats.json')

try {
const content = await readFile(ciStatsPath, 'utf-8')
return JSON.parse(content) as CIStats
} catch (error) {
return null
}
}
9 changes: 8 additions & 1 deletion packages/stats-generator/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
export interface FrameworkStats {
export interface CIStats {
installTimeMs?: number
coldBuildTimeMs?: number
warmBuildTimeMs?: number
timingMeasuredAt?: string
}

export interface FrameworkStats extends CIStats {
prodDependencies: number
devDependencies: number
}
Expand Down
Loading
Loading