Feat: Create Photographer Script and Storybook Tutorial #711
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 Quality Gate - Standalone | |
| on: | |
| pull_request: | |
| branches: ['leader'] | |
| workflow_dispatch: | |
| inputs: | |
| branch: | |
| description: 'Branch to check' | |
| required: true | |
| default: 'leader' | |
| concurrency: | |
| group: pr-quality-standalone-${{ github.event.pull_request.number || github.ref || github.run_id }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| checks: write # Required for dorny/test-reporter | |
| actions: read # Required for artifact downloads | |
| pull-requests: write # Required for gh pr comment | |
| issues: write # Required for posting comments | |
| env: | |
| FORCE_COLOR: 1 | |
| # Unique session identifier for concurrent runs | |
| SESSION_ID: ${{ github.event.pull_request.number || github.ref_name || github.run_id }} | |
| # Maximum bytes to include from each log file in failure comments | |
| LOG_TRUNCATE_BYTES: 20000 | |
| jobs: | |
| # =========================================================================== | |
| # SINGLE JOB: SETUP AND ALL TESTS | |
| # Sets up environment and runs all quality checks, tests in one job | |
| # =========================================================================== | |
| test: | |
| name: 🧪 Setup & All Tests | |
| runs-on: self-hosted | |
| timeout-minutes: 25 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ inputs.branch || github.ref }} | |
| fetch-depth: 0 | |
| clean: true # 🧹 Wipes previous run artifacts to ensure a fresh start | |
| - name: Define Dynamic Cache Path | |
| shell: bash | |
| # This expands $HOME (e.g., /home/ari) and saves it to the workflow env | |
| run: echo "PLAYWRIGHT_BROWSERS_PATH=$HOME/.cache/ms-playwright" >> $GITHUB_ENV | |
| - name: Sanitize SESSION_ID | |
| shell: bash | |
| run: | | |
| # Replace / with - in SESSION_ID to prevent invalid artifact names | |
| SAFE_SESSION_ID=$(echo "${{ env.SESSION_ID }}" | tr '/' '-') | |
| echo "SESSION_ID=$SAFE_SESSION_ID" >> $GITHUB_ENV | |
| # --- GLOBAL PERSISTENCE CONFIG --- | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| run_install: false | |
| - name: Configure Persistent Store | |
| shell: bash | |
| run: | | |
| mkdir -p ~/.local/share/pnpm/store | |
| # Use session-specific cache directories to prevent conflicts | |
| mkdir -p ~/.cache/pnpm-${{ env.SESSION_ID }} | |
| pnpm config set store-dir ~/.local/share/pnpm/store | |
| pnpm config set cache-dir ~/.cache/pnpm-${{ env.SESSION_ID }} | |
| pnpm config set side-effects-cache true | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '20.x' | |
| # --------------------------------- | |
| - name: Create Runtime Envs | |
| run: | | |
| mkdir -p logs coverage playwright-report test-results | |
| echo "NEXTAUTH_URL=http://127.0.0.1:3000" > .env.local | |
| echo "NEXTAUTH_SECRET=ci-secret-${{ env.SESSION_ID }}" >> .env.local | |
| - name: Run Setup Script | |
| run: | | |
| chmod +x ./scripts/setup.sh | |
| ./scripts/setup.sh | |
| # This installs dependencies into ./node_modules | |
| # 1. Get version for cache key | |
| - name: Get installed Playwright version | |
| id: playwright-version | |
| shell: bash | |
| run: echo "VERSION=$(npx playwright --version | cut -d ' ' -f 2)" >> $GITHUB_OUTPUT | |
| # 2. Cache the browser binaries in the HOME directory | |
| #- name: Cache Playwright Browsers | |
| # id: playwright-cache | |
| # uses: actions/cache@v4 | |
| # with: | |
| # path: ${{ env.PLAYWRIGHT_BROWSERS_PATH }} | |
| # key: ${{ runner.os }}-playwright-${{ steps.playwright-version.outputs.VERSION }} | |
| # restore-keys: | | |
| # ${{ runner.os }}-playwright- | |
| # 3. Install Browsers (ONLY if cache miss) | |
| # NOTE: If running on a new server, install OS deps manually once with: | |
| # sudo env PATH="$(dirname $(which node)):$PATH" npx playwright install-deps | |
| - name: Install Playwright Browsers | |
| # if: steps.playwright-cache.outputs.cache-hit != 'true' | |
| # CHANGE: Removed --with-deps (System deps are already on your server) | |
| run: pnpm exec playwright install chromium | |
| - name: Cache Next.js Build | |
| uses: actions/cache@v4 | |
| with: | |
| path: ${{ github.workspace }}/.next/cache | |
| key: ${{ runner.os }}-nextjs-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }} | |
| restore-keys: | | |
| ${{ runner.os }}-nextjs-${{ hashFiles('**/pnpm-lock.yaml') }}- | |
| # ADD THIS LINE: Matches any previous Next.js cache | |
| ${{ runner.os }}-nextjs- | |
| - name: Lint Code | |
| id: lint | |
| run: | | |
| set -o pipefail | |
| pnpm run lint 2>&1 | tee logs/lint-output.log | |
| - name: Verify Build | |
| id: build | |
| run: | | |
| set -o pipefail | |
| pnpm run build 2>&1 | tee logs/build-output.log | |
| - name: Run Infrastructure Tests and Generate JUnit Report | |
| id: infra_tests | |
| # Run tests and output the report file to a specific path, capture output | |
| run: | | |
| set -o pipefail | |
| PLAYWRIGHT_JUNIT_OUTPUT_NAME=test-results/infra-results.xml pnpm run test:infra --reporter=junit 2>&1 | tee logs/infra-output.log | |
| # continue-on-error allows subsequent steps to run for failure reporting | |
| continue-on-error: true | |
| - name: Publish Infra Test Results to GitHub Checks | |
| # This action reads the generated XML and displays it in the Checks/Summary tab | |
| uses: dorny/test-reporter@v1 | |
| if: always() && steps.infra_tests.outcome != 'skipped' # Ensures the report is published even if tests failed, but not if skipped | |
| with: | |
| name: Infrastructure Tests Report # The name of the check run | |
| path: test-results/infra-results.xml # Path to the generated XML file | |
| reporter: java-junit | |
| fail-on-error: true # Fails the overall workflow job if any tests failed | |
| - name: Run Unit Tests | |
| id: unit_tests | |
| run: | | |
| set -o pipefail | |
| pnpm run test:unit 2>&1 | tee logs/unit-output.log | |
| continue-on-error: true | |
| - name: Publish Unit Test Results to GitHub Checks | |
| # This action reads the generated XML and displays it in the Checks/Summary tab | |
| uses: dorny/test-reporter@v1 | |
| if: always() && steps.unit_tests.outcome != 'skipped' # Ensures the report is published even if tests failed, but not if skipped | |
| with: | |
| name: Unit Tests Report # The name of the check run | |
| path: test-results/unit-results.xml # Path to the generated XML file | |
| reporter: java-junit | |
| fail-on-error: true # Fails the overall workflow job if any tests failed | |
| - name: Run Visual Tests | |
| id: visual_tests | |
| run: | | |
| set -o pipefail | |
| pnpm run test:visual 2>&1 | tee logs/visual-output.log | |
| # NOTE: This generates files in the default ./blob-report directory | |
| continue-on-error: true | |
| # --- NEW SECTION: PERFORMANCE PROFILING --- | |
| - name: Run Performance Profiling | |
| id: perf_tests | |
| if: always() && steps.build.outcome == 'success' # Only run if build passed | |
| run: | | |
| set -o pipefail | |
| # 1. Defensive Cleanup: Ensure no stray servers from visual tests are hogging port 3000 | |
| pnpm run pm2:delete || true | |
| fuser -k 3000/tcp || true | |
| # 2. Run the Combined Profiling Suite | |
| # (Ensure 'profile:all' is defined in package.json as: "bash scripts/profile-server.sh && bash scripts/profile-frontend.sh") | |
| pnpm run profile:all 2>&1 | tee logs/performance-output.log | |
| continue-on-error: true | |
| # ------------------------------------------ | |
| - name: Publish Visual Test Results to GitHub Checks | |
| uses: dorny/test-reporter@v1 | |
| if: always() && steps.visual_tests.outcome != 'skipped' | |
| with: | |
| name: Visual Tests Report | |
| path: test-results/results.xml | |
| reporter: java-junit | |
| fail-on-error: true | |
| - name: Merge Playwright Reports | |
| if: always() && steps.visual_tests.outcome != 'skipped' | |
| run: npx playwright merge-reports --reporter html ./blob-report | |
| # The command now correctly points to the default directory containing blob reports | |
| - name: Upload Test Results | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: test-results-${{ env.SESSION_ID }} | |
| path: | | |
| coverage/ | |
| playwright-report/ | |
| test-results/ | |
| logs/ | |
| reports/performance/ | |
| retention-days: 7 | |
| # Post comment on PR when tests fail | |
| - name: Report Failure to Jules | |
| if: always() && (steps.lint.outcome == 'failure' || steps.build.outcome == 'failure' || steps.infra_tests.outcome == 'failure' || steps.unit_tests.outcome == 'failure' || steps.visual_tests.outcome == 'failure' || steps.perf_tests.outcome == 'failure') | |
| env: | |
| GH_TOKEN: ${{ secrets.ARI_PAT }} | |
| run: | | |
| # 1. Identify the PR Number | |
| # Try getting it from the event (pull_request) | |
| PR_NUMBER="${{ github.event.pull_request.number }}" | |
| # If triggered by workflow_dispatch, find the PR associated with this branch | |
| if [ -z "$PR_NUMBER" ] || [ "$PR_NUMBER" == "null" ]; then | |
| PR_NUMBER=$(gh pr list --head "${{ github.ref_name }}" --json number --jq '.[0].number') | |
| fi | |
| if [ -z "$PR_NUMBER" ] || [ "$PR_NUMBER" == "null" ]; then | |
| echo "::warning::No Pull Request found for this branch. Skipping comment." | |
| exit 0 | |
| fi | |
| # 2. Build the list of failed checks and collect logs to temp files | |
| FAILED_CHECKS="" | |
| # Define comment file path in workspace | |
| COMMENT_FILE="test-results/pr-comment.md" | |
| # Ensure test-results exists (it should, but safety first) | |
| mkdir -p test-results | |
| # Initialize the comment file with header | |
| # We construct the content directly into the final file to avoid temp file issues | |
| # 1. Collect failed checks | |
| if [ "${{ steps.lint.outcome }}" == "failure" ]; then | |
| FAILED_CHECKS="${FAILED_CHECKS}Lint, " | |
| fi | |
| if [ "${{ steps.build.outcome }}" == "failure" ]; then | |
| FAILED_CHECKS="${FAILED_CHECKS}Build, " | |
| fi | |
| if [ "${{ steps.infra_tests.outcome }}" == "failure" ]; then | |
| FAILED_CHECKS="${FAILED_CHECKS}Infrastructure Tests, " | |
| fi | |
| if [ "${{ steps.unit_tests.outcome }}" == "failure" ]; then | |
| FAILED_CHECKS="${FAILED_CHECKS}Unit Tests, " | |
| fi | |
| if [ "${{ steps.visual_tests.outcome }}" == "failure" ]; then | |
| FAILED_CHECKS="${FAILED_CHECKS}Visual Tests, " | |
| fi | |
| if [ "${{ steps.perf_tests.outcome }}" == "failure" ]; then | |
| FAILED_CHECKS="${FAILED_CHECKS}Performance Profiling, " | |
| fi | |
| FAILED_CHECKS="${FAILED_CHECKS%, }" | |
| # 2. Write Header | |
| echo "@jules fix ${FAILED_CHECKS}" > "$COMMENT_FILE" | |
| echo "" >> "$COMMENT_FILE" | |
| echo "<details>" >> "$COMMENT_FILE" | |
| echo "<summary><strong>Failed Test Report Log</strong></summary>" >> "$COMMENT_FILE" | |
| # 3. Append Logs | |
| append_log() { | |
| local log_file="$1" | |
| local test_name="$2" | |
| if [ -f "$log_file" ]; then | |
| echo "" >> "$COMMENT_FILE" | |
| echo "### ${test_name} Log" >> "$COMMENT_FILE" | |
| echo '```' >> "$COMMENT_FILE" | |
| tail -c "${{ env.LOG_TRUNCATE_BYTES }}" "$log_file" | sed -r 's/\x1b\[[0-9;]*m//g' >> "$COMMENT_FILE" | |
| echo '```' >> "$COMMENT_FILE" | |
| else | |
| echo "" >> "$COMMENT_FILE" | |
| echo "### ${test_name} Log" >> "$COMMENT_FILE" | |
| echo "_Log file not found: ${log_file}_" >> "$COMMENT_FILE" | |
| fi | |
| } | |
| if [ "${{ steps.lint.outcome }}" == "failure" ]; then | |
| append_log "logs/lint-output.log" "Lint" | |
| fi | |
| if [ "${{ steps.build.outcome }}" == "failure" ]; then | |
| append_log "logs/build-output.log" "Build" | |
| fi | |
| if [ "${{ steps.infra_tests.outcome }}" == "failure" ]; then | |
| append_log "logs/infra-output.log" "Infrastructure Tests" | |
| fi | |
| if [ "${{ steps.unit_tests.outcome }}" == "failure" ]; then | |
| append_log "logs/unit-output.log" "Unit Tests" | |
| fi | |
| if [ "${{ steps.visual_tests.outcome }}" == "failure" ]; then | |
| append_log "logs/visual-output.log" "Visual Tests" | |
| fi | |
| if [ "${{ steps.perf_tests.outcome }}" == "failure" ]; then | |
| append_log "logs/performance-output.log" "Performance Test Output" | |
| # Also attach the specific background logs if they exist | |
| append_log "logs/ws-stress-client.log" "WebSocket Stress Client" | |
| append_log "logs/frontend-perf-server.log" "Frontend Perf Server" | |
| fi | |
| # 4. Close Details | |
| echo "</details>" >> "$COMMENT_FILE" | |
| # 5. Post the Comment | |
| if [ -s "$COMMENT_FILE" ]; then | |
| gh pr comment "$PR_NUMBER" --body-file "$COMMENT_FILE" | |
| else | |
| echo "::error::Comment file is empty, skipping comment." | |
| exit 1 | |
| fi | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| if: always() | |
| with: | |
| ref: ${{ inputs.branch || github.ref }} | |
| fetch-depth: 0 | |
| # CHANGED: Set to false to keep .next/cache and node_modules on disk | |
| clean: false | |
| - name: Clean Workspace (Preserve Cache) | |
| if: always() | |
| run: | | |
| # Delete everything EXCEPT .next and node_modules | |
| find . -maxdepth 1 -mindepth 1 \ | |
| -not -name '.next' \ | |
| -not -name 'node_modules' \ | |
| -not -name '.git' \ | |
| -not -name '.cache' \ | |
| -exec rm -rf {} + | |
| # Reset git state to ensure code is fresh | |
| git reset --hard HEAD | |
| git clean -fdx --exclude .next --exclude node_modules --exclude .eslintcache --exclude .cache | |
| # Fail the job if any tests failed (must be at the very end) | |
| - name: Fail Workflow on Test Failures | |
| if: always() && (steps.lint.outcome == 'failure' || steps.build.outcome == 'failure' || steps.infra_tests.outcome == 'failure' || steps.unit_tests.outcome == 'failure' || steps.visual_tests.outcome == 'failure' || steps.perf_tests.outcome == 'failure') | |
| run: | | |
| echo "One or more tests failed. Failing the workflow." | |
| exit 1 |