Skip to content

feat: shared-schemas #58

feat: shared-schemas

feat: shared-schemas #58

Workflow file for this run

name: Lint PR Changes
on:
pull_request:
types: [opened, reopened]
issue_comment:
types: [created]
jobs:
lint:
# Run on PR events or when the comment is "/linter" on a PR
if: |
github.event_name == 'pull_request' ||
(github.event_name == 'issue_comment' &&
github.event.issue.pull_request &&
contains(github.event.comment.body, '/linter'))
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
issues: write
steps:
- name: Get PR details
id: pr
uses: actions/github-script@v7
with:
script: |
let prNumber, prRef;
if (context.eventName === 'pull_request') {
prNumber = context.payload.pull_request.number;
prRef = context.payload.pull_request.head.ref;
} else if (context.eventName === 'issue_comment') {
prNumber = context.payload.issue.number;
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber
});
prRef = pr.head.ref;
}
core.setOutput('number', prNumber);
core.setOutput('ref', prRef);
return { prNumber, prRef };
- name: React to comment (if triggered by comment)
if: github.event_name == 'issue_comment'
uses: actions/github-script@v7
with:
script: |
await github.rest.reactions.createForIssueComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: context.payload.comment.id,
content: 'rocket'
});
- name: Checkout PR
uses: actions/checkout@v4
with:
ref: ${{ steps.pr.outputs.ref }}
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9.0.0
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
- name: Setup pnpm cache
uses: actions/cache@v4
with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Cache Prisma engines
uses: actions/cache@v4
with:
path: |
~/.cache/prisma
packages/prisma-db/generated
key: ${{ runner.os }}-prisma-${{ hashFiles('packages/prisma-db/schema.prisma') }}
restore-keys: |
${{ runner.os }}-prisma-
- name: Cache built packages
uses: actions/cache@v4
with:
path: |
packages/**/dist
packages/**/.turbo
key: ${{ runner.os }}-packages-build-${{ hashFiles('packages/**/package.json', 'packages/**/tsconfig.json', 'packages/**/*.ts') }}
restore-keys: |
${{ runner.os }}-packages-build-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Generate Prisma Client
run: |
cd packages/prisma-db
# Retry up to 3 times if Prisma's binary server fails
for i in 1 2 3; do
if pnpm exec prisma generate; then
echo "Prisma client generated successfully"
break
else
if [ $i -eq 3 ]; then
echo "Failed to generate Prisma client after 3 attempts"
exit 1
fi
echo "Attempt $i failed, retrying in 5 seconds..."
sleep 5
fi
done
- name: Build packages
run: |
# Build only the packages (not the apps)
# This generates TypeScript types for @repo/utils-core and other packages
pnpm --filter "./packages/**" build || echo "Some packages don't have build scripts, continuing..."
- name: Get changed files
id: changed-files
run: |
# Fetch the base branch
git fetch origin ${{ github.base_ref || 'main' }}:${{ github.base_ref || 'main' }}
# Get list of changed files (excluding deleted files)
CHANGED_FILES=$(git diff --name-only --diff-filter=d origin/${{ github.base_ref || 'main' }}...HEAD)
# Filter to only lintable files (ts, tsx, js, jsx, mjs, cjs)
LINTABLE_FILES=$(echo "$CHANGED_FILES" | grep -E '\.(ts|tsx|js|jsx|mjs|cjs)$' || true)
# Remove empty lines, node_modules, and commonly ignored files
LINTABLE_FILES=$(echo "$LINTABLE_FILES" | grep -v 'node_modules' | grep -v '^$' | grep -v 'eslint.config' | grep -v '.eslintrc' || true)
if [ -z "$LINTABLE_FILES" ]; then
echo "No lintable files changed"
echo "has_files=false" >> $GITHUB_OUTPUT
else
echo "Changed lintable files:"
echo "$LINTABLE_FILES"
echo "has_files=true" >> $GITHUB_OUTPUT
# Save files to a temporary file for later use
echo "$LINTABLE_FILES" > /tmp/changed_files.txt
fi
- name: Run ESLint on changed files
if: steps.changed-files.outputs.has_files == 'true'
id: eslint
run: |
set +e # Don't exit on error, we want to capture the output
# Read changed files
CHANGED_FILES=$(cat /tmp/changed_files.txt)
# Dynamically discover workspaces using pnpm
echo "Discovering workspaces..."
WORKSPACES=$(pnpm list -r --depth -1 --parseable 2>/dev/null | grep -v "^$(pwd)$" || true)
if [ -z "$WORKSPACES" ]; then
echo "No workspaces found"
exit 0
fi
echo "Found workspaces:"
echo "$WORKSPACES"
LINT_EXIT_CODE=0
LINT_OUTPUT=""
FAILED_WORKSPACES=""
REPO_ROOT=$(pwd)
# Process each workspace - continue even if one fails
while IFS= read -r workspace_path; do
workspace_name=$(basename "$workspace_path")
# Get relative path from repo root
workspace_relative_path=$(python3 -c "import os.path; print(os.path.relpath('$workspace_path', '$REPO_ROOT'))")
# Find files in this workspace
workspace_files=$(echo "$CHANGED_FILES" | grep "^$workspace_relative_path/" || true)
if [ -n "$workspace_files" ]; then
echo "::group::Linting $workspace_name ($workspace_relative_path)"
cd "$workspace_path"
RELATIVE_FILES=""
for file in $workspace_files; do
RELATIVE_FILE=$(echo "$file" | sed "s|^$workspace_relative_path/||")
RELATIVE_FILES="$RELATIVE_FILES $RELATIVE_FILE"
done
echo "Files: $RELATIVE_FILES"
# Run eslint and capture output - don't stop on error
WORKSPACE_EXIT=0
WORKSPACE_OUTPUT=$(npx eslint $RELATIVE_FILES --max-warnings 0 --no-warn-ignored 2>&1) || WORKSPACE_EXIT=$?
if [ $WORKSPACE_EXIT -ne 0 ]; then
LINT_EXIT_CODE=1
LINT_OUTPUT="${LINT_OUTPUT}\n### ${workspace_name} ($workspace_relative_path) Linting Issues\n\`\`\`\n${WORKSPACE_OUTPUT}\n\`\`\`\n"
FAILED_WORKSPACES="${FAILED_WORKSPACES}- ${workspace_name} ($workspace_relative_path)\n"
echo "$WORKSPACE_OUTPUT"
echo "::warning::${workspace_name} linting failed, but continuing to check other workspaces..."
else
echo "✅ ${workspace_name} files passed linting"
fi
cd "$REPO_ROOT"
echo "::endgroup::"
fi
done <<< "$WORKSPACES"
# Log summary of failed workspaces if any
if [ $LINT_EXIT_CODE -ne 0 ]; then
echo "::error::Linting failed in the following workspaces:"
echo -e "$FAILED_WORKSPACES" | while read line; do
[ -n "$line" ] && echo "::error:: $line"
done
fi
# Save output for comment
if [ $LINT_EXIT_CODE -ne 0 ]; then
echo "lint_failed=true" >> $GITHUB_OUTPUT
echo -e "$LINT_OUTPUT" > /tmp/lint_output.txt
else
echo "lint_failed=false" >> $GITHUB_OUTPUT
fi
exit $LINT_EXIT_CODE
- name: Comment on PR (Success)
if: |
always() &&
steps.changed-files.outputs.has_files == 'true' &&
steps.eslint.outputs.lint_failed != 'true'
uses: actions/github-script@v7
with:
script: |
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: ${{ steps.pr.outputs.number }},
body: '✅ All changed files passed linting checks!'
});
- name: Comment on PR (Failure)
if: |
always() &&
steps.changed-files.outputs.has_files == 'true' &&
steps.eslint.outputs.lint_failed == 'true'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const lintOutput = fs.readFileSync('/tmp/lint_output.txt', 'utf8');
const body = `❌ Linting issues found in changed files:\n\n${lintOutput}\n\nPlease fix these issues before merging.`;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: ${{ steps.pr.outputs.number }},
body: body
});
- name: Comment on PR (No files)
if: |
always() &&
steps.changed-files.outputs.has_files == 'false'
uses: actions/github-script@v7
with:
script: |
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: ${{ steps.pr.outputs.number }},
body: 'ℹ️ No lintable files were changed in this PR.'
});
- name: Fail if linting failed
if: steps.eslint.outputs.lint_failed == 'true'
run: exit 1