diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d2c57d5ca..afbdf7bc6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,340 +36,3 @@ jobs: node-version: 18 - run: npm ci - run: npm test - - e2e_tests: - name: Playwright Tests - runs-on: ubuntu-latest - permissions: - contents: read - - services: - backend: - image: ghcr.io/ydb-platform/local-ydb:nightly - ports: - - 2135:2135 - - 8765:8765 - options: --hostname localhost -e YDB_ALLOW_ORIGIN="http://localhost:3000" - - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 18 - cache: npm - - - name: Install dependencies - run: npm ci - - - name: Install Playwright deps - run: npm run test:e2e:install - - - name: Run Playwright tests - id: run_tests - run: npm run test:e2e - env: - CI: true - PLAYWRIGHT_VIDEO: 'on' - - - name: Upload Playwright artifacts - if: always() - uses: actions/upload-artifact@v3 - with: - name: playwright-artifacts - path: playwright-artifacts - retention-days: 5 - - - name: Get test results - if: always() - id: test-results - run: | - echo "Current directory: $(pwd)" - echo "Listing playwright-artifacts directory:" - ls -R playwright-artifacts - - if [ -f "playwright-artifacts/test-results.json" ]; then - echo "Parsing JSON file:" - total=$(jq '.stats.expected + .stats.unexpected + .stats.flaky + .stats.skipped' playwright-artifacts/test-results.json) - passed=$(jq '.stats.expected' playwright-artifacts/test-results.json) - failed=$(jq '.stats.unexpected' playwright-artifacts/test-results.json) - flaky=$(jq '.stats.flaky' playwright-artifacts/test-results.json) - skipped=$(jq '.stats.skipped' playwright-artifacts/test-results.json) - - echo "Parsed values:" - echo "Total: $total" - echo "Passed: $passed" - echo "Failed: $failed" - echo "Flaky: $flaky" - echo "Skipped: $skipped" - else - echo "test-results.json file not found" - total=0 - passed=0 - failed=0 - flaky=0 - skipped=0 - fi - - echo "total=$total" >> $GITHUB_OUTPUT - echo "passed=$passed" >> $GITHUB_OUTPUT - echo "failed=$failed" >> $GITHUB_OUTPUT - echo "flaky=$flaky" >> $GITHUB_OUTPUT - echo "skipped=$skipped" >> $GITHUB_OUTPUT - - bundle_size: - name: Check Bundle Size - runs-on: ubuntu-latest - if: ${{github.event.action != 'checks_requested'}} - outputs: - current_size: ${{ steps.current_size.outputs.size }} - main_size: ${{ steps.main_size.outputs.size }} - diff: ${{ steps.size_diff.outputs.diff }} - percent: ${{ steps.size_diff.outputs.percent }} - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 18 - cache: npm - - - name: Install dependencies - run: npm ci - - - name: Build bundle (current branch) - run: npm run build - - - name: Get current bundle size - id: current_size - run: | - size=$(du -sb build | cut -f1) - echo "size=$size" >> $GITHUB_OUTPUT - - - name: Checkout main branch - uses: actions/checkout@v4 - with: - ref: main - - - name: Install dependencies (main) - run: npm ci - - - name: Build bundle (main branch) - run: npm run build - - - name: Get main bundle size - id: main_size - run: | - size=$(du -sb build | cut -f1) - echo "size=$size" >> $GITHUB_OUTPUT - - - name: Calculate size difference - id: size_diff - run: | - current=${{ steps.current_size.outputs.size }} - main=${{ steps.main_size.outputs.size }} - diff=$((current - main)) - if [ "$main" -ne "0" ]; then - percent=$(awk "BEGIN {printf \"%.2f\", ($diff/$main) * 100}") - else - percent="N/A" - fi - echo "diff=$diff" >> $GITHUB_OUTPUT - echo "percent=$percent" >> $GITHUB_OUTPUT - - deploy_report: - name: Deploy Test Report - needs: [e2e_tests] - if: ${{always() && (github.ref == 'refs/heads/main' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository))}} - runs-on: ubuntu-latest - permissions: - contents: write - pages: write - - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Fetch gh-pages branch - run: | - git fetch origin gh-pages:gh-pages - mkdir gh-pages - git --work-tree=gh-pages checkout gh-pages -- . - - - name: Download Playwright artifacts - uses: actions/download-artifact@v3 - with: - name: playwright-artifacts - path: playwright-artifacts - - - name: Copy new report - run: | - if [ "${{ github.event_name }}" = "pull_request" ]; then - REPORT_DIR="${{ github.event.pull_request.number }}" - else - REPORT_DIR="main" - fi - rm -rf gh-pages/$REPORT_DIR - mkdir -p gh-pages/$REPORT_DIR - cp -r playwright-artifacts/playwright-report/* gh-pages/$REPORT_DIR/ - - - name: Deploy report to GitHub Pages - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: gh-pages - destination_dir: . - force_orphan: true - - update_pr: - name: Update PR Description - needs: [e2e_tests, bundle_size] - if: ${{always() && github.event_name == 'pull_request'}} - runs-on: ubuntu-latest - permissions: - pull-requests: write - - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Download Playwright artifacts - uses: actions/download-artifact@v3 - with: - name: playwright-artifacts - path: playwright-artifacts - - - name: Count new tests - id: count_tests - run: | - git fetch origin main:main - new_tests=0 - - # Get list of changed test files - for file in $(git diff --name-only main...HEAD | grep -E '^tests/suites/.*\.(spec|test)\.(ts|tsx|js|jsx)$'); do - # Count tests in current version - if git show HEAD:"$file" > /dev/null 2>&1; then - current_tests=$(git show HEAD:"$file" | grep -E "test\([\'\"]" | wc -l) - else - current_tests=0 - fi - - # Count tests in main version - if git show main:"$file" > /dev/null 2>&1; then - base_tests=$(git show main:"$file" | grep -E "test\([\'\"]" | wc -l) - else - base_tests=0 - fi - - # Add difference to total - ((new_tests += current_tests - base_tests)) - done - - echo "new_tests=$new_tests" >> $GITHUB_OUTPUT - - - name: Update PR description - uses: actions/github-script@v6 - with: - github-token: ${{secrets.GITHUB_TOKEN}} - script: | - const fs = require('fs'); - const testResultsPath = 'playwright-artifacts/test-results.json'; - let testResults; - - if (fs.existsSync(testResultsPath)) { - const rawData = fs.readFileSync(testResultsPath); - const data = JSON.parse(rawData); - testResults = { - total: data.stats.expected + data.stats.unexpected + data.stats.flaky + data.stats.skipped, - passed: data.stats.expected, - failed: data.stats.unexpected, - flaky: data.stats.flaky, - skipped: data.stats.skipped - }; - } else { - console.log('Test results file not found'); - testResults = { total: 0, passed: 0, failed: 0, flaky: 0, skipped: 0 }; - } - - const reportUrl = `https://${context.repo.owner}.github.io/${context.repo.repo}/${context.issue.number}/`; - const status = testResults.failed > 0 ? '❌ FAILED' : (testResults.flaky > 0 ? '⚠️ FLAKY' : '✅ PASSED'); - const statusColor = testResults.failed > 0 ? 'red' : (testResults.flaky > 0 ? 'orange' : 'green'); - - const currentSize = parseInt('${{ needs.bundle_size.outputs.current_size }}'); - const mainSize = parseInt('${{ needs.bundle_size.outputs.main_size }}'); - const diff = parseInt('${{ needs.bundle_size.outputs.diff }}'); - const percent = '${{ needs.bundle_size.outputs.percent }}'; - - const formatSize = (size) => { - if (size >= 1024) { - return `${(size / (1024 * 1024)).toFixed(2)} MB`; - } - return `${(size / 1024).toFixed(2)} KB`; - }; - - const bundleStatus = percent === 'N/A' ? '⚠️' : - parseFloat(percent) > 0 ? '🔺' : - parseFloat(percent) < 0 ? '🔽' : '✅'; - - const newTests = parseInt('${{ steps.count_tests.outputs.new_tests }}'); - const testsStatus = newTests > 0 ? '✨' : '➖'; - - const ciSection = `## CI Results - - ### Test Status: ${status} - 📊 [Full Report](${reportUrl}) - - | Total | Passed | Failed | Flaky | Skipped | New Tests | - |:-----:|:------:|:------:|:-----:|:-------:|:---------:| - | ${testResults.total} | ${testResults.passed} | ${testResults.failed} | ${testResults.flaky} | ${testResults.skipped} | ${testsStatus} ${newTests} | - - ### Bundle Size: ${bundleStatus} - Current: ${formatSize(currentSize)} | Main: ${formatSize(mainSize)} - Diff: ${diff > 0 ? '+' : ''}${formatSize(Math.abs(diff))} (${percent === 'N/A' ? 'N/A' : `${percent}%`}) - - ${ - percent === 'N/A' ? '⚠️ Unable to calculate change.' : - parseFloat(percent) > 0 ? '⚠️ Bundle size increased. Please review.' : - parseFloat(percent) < 0 ? '✅ Bundle size decreased.' : '✅ Bundle size unchanged.' - } - -
- ℹ️ CI Information - - - Test recordings for failed tests are available in the full report. - - Bundle size is measured for the entire 'dist' directory. - - 📊 indicates links to detailed reports. - - 🔺 indicates increase, 🔽 decrease, and ✅ no change in bundle size. - - ${testsStatus} indicates ${newTests} new test cases added in this PR. -
`; - - const { data: pullRequest } = await github.rest.pulls.get({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.issue.number, - }); - - const currentBody = pullRequest.body || ''; - const ciSectionRegex = /## CI Results[\s\S]*?(?=\n## (?!CI Results)|$)/; - - let newBody = currentBody; - if (ciSectionRegex.test(newBody)) { - newBody = newBody.replace(ciSectionRegex, ciSection); - } else { - newBody += '\n\n' + ciSection; - } - - await github.rest.pulls.update({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.issue.number, - body: newBody, - }); diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml new file mode 100644 index 000000000..efc40c1f2 --- /dev/null +++ b/.github/workflows/quality.yml @@ -0,0 +1,345 @@ +name: Quality Checks + +on: + pull_request: + branches: ['**'] + push: + branches: [main] + +jobs: + e2e_tests: + name: Playwright Tests + runs-on: ubuntu-latest + permissions: + contents: read + + services: + backend: + image: ghcr.io/ydb-platform/local-ydb:nightly + ports: + - 2135:2135 + - 8765:8765 + options: --hostname localhost -e YDB_ALLOW_ORIGIN="http://localhost:3000" + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 18 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Install Playwright deps + run: npm run test:e2e:install + + - name: Run Playwright tests + id: run_tests + run: npm run test:e2e + env: + CI: true + PLAYWRIGHT_VIDEO: 'on' + + - name: Upload Playwright artifacts + if: always() + uses: actions/upload-artifact@v3 + with: + name: playwright-artifacts + path: playwright-artifacts + retention-days: 5 + + - name: Get test results + if: always() + id: test-results + run: | + echo "Current directory: $(pwd)" + echo "Listing playwright-artifacts directory:" + ls -R playwright-artifacts + + if [ -f "playwright-artifacts/test-results.json" ]; then + echo "Parsing JSON file:" + total=$(jq '.stats.expected + .stats.unexpected + .stats.flaky + .stats.skipped' playwright-artifacts/test-results.json) + passed=$(jq '.stats.expected' playwright-artifacts/test-results.json) + failed=$(jq '.stats.unexpected' playwright-artifacts/test-results.json) + flaky=$(jq '.stats.flaky' playwright-artifacts/test-results.json) + skipped=$(jq '.stats.skipped' playwright-artifacts/test-results.json) + + echo "Parsed values:" + echo "Total: $total" + echo "Passed: $passed" + echo "Failed: $failed" + echo "Flaky: $flaky" + echo "Skipped: $skipped" + else + echo "test-results.json file not found" + total=0 + passed=0 + failed=0 + flaky=0 + skipped=0 + fi + + echo "total=$total" >> $GITHUB_OUTPUT + echo "passed=$passed" >> $GITHUB_OUTPUT + echo "failed=$failed" >> $GITHUB_OUTPUT + echo "flaky=$flaky" >> $GITHUB_OUTPUT + echo "skipped=$skipped" >> $GITHUB_OUTPUT + + bundle_size: + name: Check Bundle Size + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + outputs: + current_size: ${{ steps.current_size.outputs.size }} + main_size: ${{ steps.main_size.outputs.size }} + diff: ${{ steps.size_diff.outputs.diff }} + percent: ${{ steps.size_diff.outputs.percent }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 18 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Build bundle (current branch) + run: npm run build + + - name: Get current bundle size + id: current_size + run: | + size=$(du -sb build | cut -f1) + echo "size=$size" >> $GITHUB_OUTPUT + + - name: Checkout main branch + uses: actions/checkout@v4 + with: + ref: main + + - name: Install dependencies (main) + run: npm ci + + - name: Build bundle (main branch) + run: npm run build + + - name: Get main bundle size + id: main_size + run: | + size=$(du -sb build | cut -f1) + echo "size=$size" >> $GITHUB_OUTPUT + + - name: Calculate size difference + id: size_diff + run: | + current=${{ steps.current_size.outputs.size }} + main=${{ steps.main_size.outputs.size }} + diff=$((current - main)) + if [ "$main" -ne "0" ]; then + percent=$(awk "BEGIN {printf \"%.2f\", ($diff/$main) * 100}") + else + percent="N/A" + fi + echo "diff=$diff" >> $GITHUB_OUTPUT + echo "percent=$percent" >> $GITHUB_OUTPUT + + deploy_report: + name: Deploy Test Report + needs: [e2e_tests] + if: always() && (github.event_name == 'push' && github.ref == 'refs/heads/main' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository)) + runs-on: ubuntu-latest + permissions: + contents: write + pages: write + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Fetch gh-pages branch + run: | + git fetch origin gh-pages:gh-pages + mkdir gh-pages + git --work-tree=gh-pages checkout gh-pages -- . + + - name: Download Playwright artifacts + uses: actions/download-artifact@v3 + with: + name: playwright-artifacts + path: playwright-artifacts + + - name: Copy new report + run: | + if [ "${{ github.event_name }}" = "pull_request" ]; then + REPORT_DIR="${{ github.event.pull_request.number }}" + else + REPORT_DIR="main" + fi + rm -rf gh-pages/$REPORT_DIR + mkdir -p gh-pages/$REPORT_DIR + cp -r playwright-artifacts/playwright-report/* gh-pages/$REPORT_DIR/ + + - name: Deploy report to GitHub Pages + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: gh-pages + destination_dir: . + force_orphan: true + + update_pr: + name: Update PR Description + needs: [e2e_tests, bundle_size] + if: always() && github.event_name == 'pull_request' + runs-on: ubuntu-latest + permissions: + pull-requests: write + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Download Playwright artifacts + uses: actions/download-artifact@v3 + with: + name: playwright-artifacts + path: playwright-artifacts + + - name: Count new tests + id: count_tests + run: | + git fetch origin main:main + new_tests=0 + + # Get list of changed test files + for file in $(git diff --name-only main...HEAD | grep -E '^tests/suites/.*\.(spec|test)\.(ts|tsx|js|jsx)$'); do + # Count tests in current version + if git show HEAD:"$file" > /dev/null 2>&1; then + current_tests=$(git show HEAD:"$file" | grep -E "test\([\'\"]" | wc -l) + else + current_tests=0 + fi + + # Count tests in main version + if git show main:"$file" > /dev/null 2>&1; then + base_tests=$(git show main:"$file" | grep -E "test\([\'\"]" | wc -l) + else + base_tests=0 + fi + + # Add difference to total + ((new_tests += current_tests - base_tests)) + done + + echo "new_tests=$new_tests" >> $GITHUB_OUTPUT + + - name: Update PR description + uses: actions/github-script@v6 + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + const fs = require('fs'); + const testResultsPath = 'playwright-artifacts/test-results.json'; + let testResults; + + if (fs.existsSync(testResultsPath)) { + const rawData = fs.readFileSync(testResultsPath); + const data = JSON.parse(rawData); + testResults = { + total: data.stats.expected + data.stats.unexpected + data.stats.flaky + data.stats.skipped, + passed: data.stats.expected, + failed: data.stats.unexpected, + flaky: data.stats.flaky, + skipped: data.stats.skipped + }; + } else { + console.log('Test results file not found'); + testResults = { total: 0, passed: 0, failed: 0, flaky: 0, skipped: 0 }; + } + + const reportUrl = `https://${context.repo.owner}.github.io/${context.repo.repo}/${context.issue.number}/`; + const status = testResults.failed > 0 ? '❌ FAILED' : (testResults.flaky > 0 ? '⚠️ FLAKY' : '✅ PASSED'); + const statusColor = testResults.failed > 0 ? 'red' : (testResults.flaky > 0 ? 'orange' : 'green'); + + const currentSize = parseInt('${{ needs.bundle_size.outputs.current_size }}'); + const mainSize = parseInt('${{ needs.bundle_size.outputs.main_size }}'); + const diff = parseInt('${{ needs.bundle_size.outputs.diff }}'); + const percent = '${{ needs.bundle_size.outputs.percent }}'; + + const formatSize = (size) => { + if (size >= 1024) { + return `${(size / (1024 * 1024)).toFixed(2)} MB`; + } + return `${(size / 1024).toFixed(2)} KB`; + }; + + const bundleStatus = percent === 'N/A' ? '⚠️' : + parseFloat(percent) > 0 ? '🔺' : + parseFloat(percent) < 0 ? '🔽' : '✅'; + + const newTests = parseInt('${{ steps.count_tests.outputs.new_tests }}'); + const testsStatus = newTests > 0 ? '✨' : '➖'; + + const ciSection = `## CI Results + + ### Test Status: ${status} + 📊 [Full Report](${reportUrl}) + + | Total | Passed | Failed | Flaky | Skipped | New Tests | + |:-----:|:------:|:------:|:-----:|:-------:|:---------:| + | ${testResults.total} | ${testResults.passed} | ${testResults.failed} | ${testResults.flaky} | ${testResults.skipped} | ${testsStatus} ${newTests} | + + ### Bundle Size: ${bundleStatus} + Current: ${formatSize(currentSize)} | Main: ${formatSize(mainSize)} + Diff: ${diff > 0 ? '+' : ''}${formatSize(Math.abs(diff))} (${percent === 'N/A' ? 'N/A' : `${percent}%`}) + + ${ + percent === 'N/A' ? '⚠️ Unable to calculate change.' : + parseFloat(percent) > 0 ? '⚠️ Bundle size increased. Please review.' : + parseFloat(percent) < 0 ? '✅ Bundle size decreased.' : '✅ Bundle size unchanged.' + } + +
+ ℹ️ CI Information + + - Test recordings for failed tests are available in the full report. + - Bundle size is measured for the entire 'dist' directory. + - 📊 indicates links to detailed reports. + - 🔺 indicates increase, 🔽 decrease, and ✅ no change in bundle size. + - ${testsStatus} indicates ${newTests} new test cases added in this PR. +
`; + + const { data: pullRequest } = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number, + }); + + const currentBody = pullRequest.body || ''; + const ciSectionRegex = /## CI Results[\s\S]*?(?=\n## (?!CI Results)|$)/; + + let newBody = currentBody; + if (ciSectionRegex.test(newBody)) { + newBody = newBody.replace(ciSectionRegex, ciSection); + } else { + newBody += '\n\n' + ciSection; + } + + await github.rest.pulls.update({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number, + body: newBody, + });