feat(auth): add TOTP two-factor authentication #4031
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |