Spaces:
Paused
Paused
| name: PR | |
| on: | |
| pull_request: | |
| branches: | |
| - master | |
| concurrency: | |
| group: pr-${{ github.event.pull_request.number }} | |
| cancel-in-progress: true | |
| jobs: | |
| policy: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Block manual lockfile edits | |
| if: github.head_ref != 'chore/refresh-lockfile' | |
| run: | | |
| changed="$(git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}")" | |
| if printf '%s\n' "$changed" | grep -qx 'pnpm-lock.yaml'; then | |
| echo "Do not commit pnpm-lock.yaml in pull requests. CI owns lockfile updates." | |
| exit 1 | |
| fi | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 9.15.4 | |
| run_install: false | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 24 | |
| - name: Validate Dockerfile deps stage | |
| run: | | |
| missing=0 | |
| # Extract only the deps stage from the Dockerfile | |
| deps_stage="$(awk '/^FROM .* AS deps$/{found=1; next} found && /^FROM /{exit} found{print}' Dockerfile)" | |
| if [ -z "$deps_stage" ]; then | |
| echo "::error::Could not extract deps stage from Dockerfile (expected 'FROM ... AS deps')" | |
| exit 1 | |
| fi | |
| # Derive workspace search roots from pnpm-workspace.yaml (exclude dev-only packages) | |
| search_roots="$(grep '^ *- ' pnpm-workspace.yaml | sed 's/^ *- //' | sed 's/\*$//' | grep -v 'examples' | grep -v 'create-paperclip-plugin' | tr '\n' ' ')" | |
| if [ -z "$search_roots" ]; then | |
| echo "::error::Could not derive workspace roots from pnpm-workspace.yaml" | |
| exit 1 | |
| fi | |
| # Check all workspace package.json files are copied in the deps stage | |
| for pkg in $(find $search_roots -maxdepth 2 -name package.json -not -path '*/examples/*' -not -path '*/create-paperclip-plugin/*' -not -path '*/node_modules/*' 2>/dev/null | sort -u); do | |
| dir="$(dirname "$pkg")" | |
| if ! echo "$deps_stage" | grep -q "^COPY ${dir}/package.json"; then | |
| echo "::error::Dockerfile deps stage missing: COPY ${pkg} ${dir}/" | |
| missing=1 | |
| fi | |
| done | |
| # Check patches directory is copied if it exists | |
| if [ -d patches ] && ! echo "$deps_stage" | grep -q '^COPY patches/'; then | |
| echo "::error::Dockerfile deps stage missing: COPY patches/ patches/" | |
| missing=1 | |
| fi | |
| if [ "$missing" -eq 1 ]; then | |
| echo "Dockerfile deps stage is out of sync. Update it to include the missing files." | |
| exit 1 | |
| fi | |
| - name: Validate dependency resolution when manifests change | |
| run: | | |
| changed="$(git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}")" | |
| manifest_pattern='(^|/)package\.json$|^pnpm-workspace\.yaml$|^\.npmrc$|^pnpmfile\.(cjs|js|mjs)$' | |
| if printf '%s\n' "$changed" | grep -Eq "$manifest_pattern"; then | |
| pnpm install --lockfile-only --ignore-scripts --no-frozen-lockfile | |
| fi | |
| verify: | |
| needs: [policy] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 9.15.4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 24 | |
| cache: pnpm | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Typecheck | |
| run: pnpm -r typecheck | |
| - name: Run tests | |
| run: pnpm test:run | |
| - name: Build | |
| run: pnpm build | |
| - name: Release canary dry run | |
| run: | | |
| git checkout -B master HEAD | |
| git checkout -- pnpm-lock.yaml | |
| ./scripts/release.sh canary --skip-verify --dry-run | |
| e2e: | |
| needs: [policy] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 9.15.4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 24 | |
| cache: pnpm | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build | |
| run: pnpm build | |
| - name: Install Playwright | |
| run: npx playwright install --with-deps chromium | |
| - name: Generate Paperclip config | |
| run: | | |
| mkdir -p ~/.paperclip/instances/default | |
| cat > ~/.paperclip/instances/default/config.json << 'CONF' | |
| { | |
| "$meta": { "version": 1, "updatedAt": "2026-01-01T00:00:00.000Z", "source": "onboard" }, | |
| "database": { "mode": "embedded-postgres" }, | |
| "logging": { "mode": "file" }, | |
| "server": { "deploymentMode": "local_trusted", "host": "127.0.0.1", "port": 3100 }, | |
| "auth": { "baseUrlMode": "auto" }, | |
| "storage": { "provider": "local_disk" }, | |
| "secrets": { "provider": "local_encrypted", "strictMode": false } | |
| } | |
| CONF | |
| - name: Run e2e tests | |
| env: | |
| PAPERCLIP_E2E_SKIP_LLM: "true" | |
| run: pnpm run test:e2e | |
| - name: Upload Playwright report | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: playwright-report | |
| path: | | |
| tests/e2e/playwright-report/ | |
| tests/e2e/test-results/ | |
| retention-days: 14 | |