From 23cd316c23419bad410d049a250150ec3c8f9c0c Mon Sep 17 00:00:00 2001 From: Lectom Date: Fri, 29 May 2026 17:15:13 +0900 Subject: [PATCH] =?UTF-8?q?=EC=BD=94=EB=93=9C=EC=B2=B4=ED=81=AC=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=20README=EC=97=90=20=EB=B1=83=EC=A7=80?= =?UTF-8?q?=EB=A1=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/code_check.yml | 332 +++++++++--- .../workflows/userfront_e2e_full_nightly.yml | 55 +- README.md | 13 +- .../src/features/coverage/pageSmoke.test.tsx | 17 + devfront/vitest.config.ts | 1 + docs/badges/adminfront-coverage.svg | 19 - docs/badges/adminfront.svg | 19 + docs/badges/backend-tests.svg | 19 + docs/badges/badges.json | 67 ++- docs/badges/dev-sha.svg | 19 + docs/badges/devfront-coverage.svg | 19 - docs/badges/devfront.svg | 19 + docs/badges/orgfront-coverage.svg | 19 - docs/badges/orgfront.svg | 19 + docs/badges/userfront-chrome.svg | 19 + docs/badges/userfront-e2e-fast.svg | 19 - docs/badges/userfront-e2e-full.svg | 19 - docs/badges/userfront-firefox.svg | 19 + docs/badges/userfront-safari.svg | 19 + docs/badges/userfront.svg | 19 + orgfront/package.json | 2 +- orgfront/vitest.config.ts | 8 + scripts/summarize_vitest_coverage.mjs | 6 +- scripts/update_code_check_badges.mjs | 495 ++++++++++++------ test/code_check_badge_branch_policy_test.sh | 40 +- ...code_check_badges_package_coverage_test.sh | 144 +++++ 26 files changed, 1096 insertions(+), 350 deletions(-) delete mode 100644 docs/badges/adminfront-coverage.svg create mode 100644 docs/badges/adminfront.svg create mode 100644 docs/badges/backend-tests.svg create mode 100644 docs/badges/dev-sha.svg delete mode 100644 docs/badges/devfront-coverage.svg create mode 100644 docs/badges/devfront.svg delete mode 100644 docs/badges/orgfront-coverage.svg create mode 100644 docs/badges/orgfront.svg create mode 100644 docs/badges/userfront-chrome.svg delete mode 100644 docs/badges/userfront-e2e-fast.svg delete mode 100644 docs/badges/userfront-e2e-full.svg create mode 100644 docs/badges/userfront-firefox.svg create mode 100644 docs/badges/userfront-safari.svg create mode 100644 docs/badges/userfront.svg create mode 100644 test/update_code_check_badges_package_coverage_test.sh diff --git a/.gitea/workflows/code_check.yml b/.gitea/workflows/code_check.yml index 72b0fe82..20a8d7db 100644 --- a/.gitea/workflows/code_check.yml +++ b/.gitea/workflows/code_check.yml @@ -749,11 +749,11 @@ jobs: userfront-e2e/test-results if-no-files-found: ignore - front-vitest-coverage: + adminfront-vitest-coverage: needs: - changes - lint - if: ${{ always() && needs.changes.outputs.front_coverage == 'true' && (github.event_name != 'workflow_dispatch' || inputs.run_front_coverage == true) }} + if: ${{ always() && needs.changes.outputs.adminfront == 'true' && (github.event_name != 'workflow_dispatch' || inputs.run_front_coverage == true) }} runs-on: ubuntu-latest steps: - name: Checkout code @@ -777,10 +777,10 @@ jobs: if [ "$install_exit_code" -ne 0 ]; then { - echo "# Front Vitest Coverage Failure Report" + echo "# Adminfront Vitest Coverage Failure Report" echo echo "- Workflow: \`${GITHUB_WORKFLOW:-Code Check}\`" - echo "- Job: \`front-vitest-coverage\`" + echo "- Job: \`adminfront-vitest-coverage\`" echo "- Reason: \`Dependency install failed\`" echo "- Exit Code: \`$install_exit_code\`" echo @@ -791,39 +791,37 @@ jobs: echo '```text' tail -n 200 reports/front-coverage-install.log echo '```' - } > reports/front-vitest-coverage-failure-report.md + } > reports/adminfront-vitest-coverage-failure-report.md exit 1 fi - for app in adminfront devfront orgfront; do - set +e - cd "$app" - pnpm install --no-frozen-lockfile --shamefully-hoist 2>&1 | tee -a ../reports/front-coverage-install.log - app_install_exit_code=${PIPESTATUS[0]} - cd .. - set -e + set +e + cd adminfront + pnpm install --no-frozen-lockfile --shamefully-hoist 2>&1 | tee -a ../reports/front-coverage-install.log + app_install_exit_code=${PIPESTATUS[0]} + cd .. + set -e - if [ "$app_install_exit_code" -ne 0 ]; then - { - echo "# Front Vitest Coverage Failure Report" - echo - echo "- Workflow: \`${GITHUB_WORKFLOW:-Code Check}\`" - echo "- Job: \`front-vitest-coverage\`" - echo "- Package: \`$app\`" - echo "- Reason: \`Dependency install failed\`" - echo "- Exit Code: \`$app_install_exit_code\`" - echo - echo "## Command" - echo "\`cd $app && pnpm install --no-frozen-lockfile --shamefully-hoist\`" - echo - echo "## Install Log Tail (last 200 lines)" - echo '```text' - tail -n 200 reports/front-coverage-install.log - echo '```' - } > reports/front-vitest-coverage-failure-report.md - exit 1 - fi - done + if [ "$app_install_exit_code" -ne 0 ]; then + { + echo "# Adminfront Vitest Coverage Failure Report" + echo + echo "- Workflow: \`${GITHUB_WORKFLOW:-Code Check}\`" + echo "- Job: \`adminfront-vitest-coverage\`" + echo "- Package: \`adminfront\`" + echo "- Reason: \`Dependency install failed\`" + echo "- Exit Code: \`$app_install_exit_code\`" + echo + echo "## Command" + echo "\`cd adminfront && pnpm install --no-frozen-lockfile --shamefully-hoist\`" + echo + echo "## Install Log Tail (last 200 lines)" + echo '```text' + tail -n 200 reports/front-coverage-install.log + echo '```' + } > reports/adminfront-vitest-coverage-failure-report.md + exit 1 + fi - name: Run adminfront Vitest coverage run: | @@ -836,10 +834,10 @@ jobs: if [ "$test_exit_code" -ne 0 ]; then { - echo "# Front Vitest Coverage Failure Report" + echo "# Adminfront Vitest Coverage Failure Report" echo echo "- Workflow: \`${GITHUB_WORKFLOW:-Code Check}\`" - echo "- Job: \`front-vitest-coverage\`" + echo "- Job: \`adminfront-vitest-coverage\`" echo "- Package: \`adminfront\`" echo "- Exit Code: \`$test_exit_code\`" echo @@ -847,7 +845,108 @@ jobs: echo '```text' tail -n 200 reports/adminfront-vitest-coverage.log echo '```' - } > reports/front-vitest-coverage-failure-report.md + } > reports/adminfront-vitest-coverage-failure-report.md + exit 1 + fi + + - name: Generate adminfront Vitest coverage summary + run: | + node scripts/summarize_vitest_coverage.mjs adminfront + cat reports/vitest-coverage-summary.md >> "$GITHUB_STEP_SUMMARY" + + - name: Publish adminfront Vitest coverage failure summary + if: ${{ failure() }} + run: | + if [ -f reports/adminfront-vitest-coverage-failure-report.md ]; then + cat reports/adminfront-vitest-coverage-failure-report.md >> "$GITHUB_STEP_SUMMARY" + fi + + - name: Upload adminfront Vitest coverage report artifact + if: ${{ always() }} + uses: actions/upload-artifact@v3 + continue-on-error: true + with: + name: adminfront-vitest-coverage-report + path: | + reports/vitest-coverage-summary.md + reports/vitest-coverage-summary.json + reports/adminfront-vitest-coverage-failure-report.md + reports/front-coverage-install.log + reports/adminfront-vitest-coverage.log + adminfront/coverage + if-no-files-found: ignore + + devfront-vitest-coverage: + needs: + - changes + - lint + if: ${{ always() && needs.changes.outputs.devfront == 'true' && (github.event_name != 'workflow_dispatch' || inputs.run_front_coverage == true) }} + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "24" + + - name: Install front workspace dependencies + run: | + mkdir -p reports + set +e + npm install -g pnpm + cd common + pnpm install --no-frozen-lockfile --shamefully-hoist 2>&1 | tee ../reports/front-coverage-install.log + install_exit_code=${PIPESTATUS[0]} + cd .. + set -e + + if [ "$install_exit_code" -ne 0 ]; then + { + echo "# Devfront Vitest Coverage Failure Report" + echo + echo "- Workflow: \`${GITHUB_WORKFLOW:-Code Check}\`" + echo "- Job: \`devfront-vitest-coverage\`" + echo "- Reason: \`Dependency install failed\`" + echo "- Exit Code: \`$install_exit_code\`" + echo + echo "## Command" + echo "\`cd common && pnpm install --no-frozen-lockfile --shamefully-hoist\`" + echo + echo "## Install Log Tail (last 200 lines)" + echo '```text' + tail -n 200 reports/front-coverage-install.log + echo '```' + } > reports/devfront-vitest-coverage-failure-report.md + exit 1 + fi + + set +e + cd devfront + pnpm install --no-frozen-lockfile --shamefully-hoist 2>&1 | tee -a ../reports/front-coverage-install.log + app_install_exit_code=${PIPESTATUS[0]} + cd .. + set -e + + if [ "$app_install_exit_code" -ne 0 ]; then + { + echo "# Devfront Vitest Coverage Failure Report" + echo + echo "- Workflow: \`${GITHUB_WORKFLOW:-Code Check}\`" + echo "- Job: \`devfront-vitest-coverage\`" + echo "- Package: \`devfront\`" + echo "- Reason: \`Dependency install failed\`" + echo "- Exit Code: \`$app_install_exit_code\`" + echo + echo "## Command" + echo "\`cd devfront && pnpm install --no-frozen-lockfile --shamefully-hoist\`" + echo + echo "## Install Log Tail (last 200 lines)" + echo '```text' + tail -n 200 reports/front-coverage-install.log + echo '```' + } > reports/devfront-vitest-coverage-failure-report.md exit 1 fi @@ -862,10 +961,10 @@ jobs: if [ "$test_exit_code" -ne 0 ]; then { - echo "# Front Vitest Coverage Failure Report" + echo "# Devfront Vitest Coverage Failure Report" echo echo "- Workflow: \`${GITHUB_WORKFLOW:-Code Check}\`" - echo "- Job: \`front-vitest-coverage\`" + echo "- Job: \`devfront-vitest-coverage\`" echo "- Package: \`devfront\`" echo "- Exit Code: \`$test_exit_code\`" echo @@ -873,7 +972,108 @@ jobs: echo '```text' tail -n 200 reports/devfront-vitest-coverage.log echo '```' - } > reports/front-vitest-coverage-failure-report.md + } > reports/devfront-vitest-coverage-failure-report.md + exit 1 + fi + + - name: Generate devfront Vitest coverage summary + run: | + node scripts/summarize_vitest_coverage.mjs devfront + cat reports/vitest-coverage-summary.md >> "$GITHUB_STEP_SUMMARY" + + - name: Publish devfront Vitest coverage failure summary + if: ${{ failure() }} + run: | + if [ -f reports/devfront-vitest-coverage-failure-report.md ]; then + cat reports/devfront-vitest-coverage-failure-report.md >> "$GITHUB_STEP_SUMMARY" + fi + + - name: Upload devfront Vitest coverage report artifact + if: ${{ always() }} + uses: actions/upload-artifact@v3 + continue-on-error: true + with: + name: devfront-vitest-coverage-report + path: | + reports/vitest-coverage-summary.md + reports/vitest-coverage-summary.json + reports/devfront-vitest-coverage-failure-report.md + reports/front-coverage-install.log + reports/devfront-vitest-coverage.log + devfront/coverage + if-no-files-found: ignore + + orgfront-vitest-coverage: + needs: + - changes + - lint + if: ${{ always() && needs.changes.outputs.orgfront == 'true' && (github.event_name != 'workflow_dispatch' || inputs.run_front_coverage == true) }} + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "24" + + - name: Install front workspace dependencies + run: | + mkdir -p reports + set +e + npm install -g pnpm + cd common + pnpm install --no-frozen-lockfile --shamefully-hoist 2>&1 | tee ../reports/front-coverage-install.log + install_exit_code=${PIPESTATUS[0]} + cd .. + set -e + + if [ "$install_exit_code" -ne 0 ]; then + { + echo "# Orgfront Vitest Coverage Failure Report" + echo + echo "- Workflow: \`${GITHUB_WORKFLOW:-Code Check}\`" + echo "- Job: \`orgfront-vitest-coverage\`" + echo "- Reason: \`Dependency install failed\`" + echo "- Exit Code: \`$install_exit_code\`" + echo + echo "## Command" + echo "\`cd common && pnpm install --no-frozen-lockfile --shamefully-hoist\`" + echo + echo "## Install Log Tail (last 200 lines)" + echo '```text' + tail -n 200 reports/front-coverage-install.log + echo '```' + } > reports/orgfront-vitest-coverage-failure-report.md + exit 1 + fi + + set +e + cd orgfront + pnpm install --no-frozen-lockfile --shamefully-hoist 2>&1 | tee -a ../reports/front-coverage-install.log + app_install_exit_code=${PIPESTATUS[0]} + cd .. + set -e + + if [ "$app_install_exit_code" -ne 0 ]; then + { + echo "# Orgfront Vitest Coverage Failure Report" + echo + echo "- Workflow: \`${GITHUB_WORKFLOW:-Code Check}\`" + echo "- Job: \`orgfront-vitest-coverage\`" + echo "- Package: \`orgfront\`" + echo "- Reason: \`Dependency install failed\`" + echo "- Exit Code: \`$app_install_exit_code\`" + echo + echo "## Command" + echo "\`cd orgfront && pnpm install --no-frozen-lockfile --shamefully-hoist\`" + echo + echo "## Install Log Tail (last 200 lines)" + echo '```text' + tail -n 200 reports/front-coverage-install.log + echo '```' + } > reports/orgfront-vitest-coverage-failure-report.md exit 1 fi @@ -888,10 +1088,10 @@ jobs: if [ "$test_exit_code" -ne 0 ]; then { - echo "# Front Vitest Coverage Failure Report" + echo "# Orgfront Vitest Coverage Failure Report" echo echo "- Workflow: \`${GITHUB_WORKFLOW:-Code Check}\`" - echo "- Job: \`front-vitest-coverage\`" + echo "- Job: \`orgfront-vitest-coverage\`" echo "- Package: \`orgfront\`" echo "- Exit Code: \`$test_exit_code\`" echo @@ -899,38 +1099,34 @@ jobs: echo '```text' tail -n 200 reports/orgfront-vitest-coverage.log echo '```' - } > reports/front-vitest-coverage-failure-report.md + } > reports/orgfront-vitest-coverage-failure-report.md exit 1 fi - - name: Generate Vitest coverage summary + - name: Generate orgfront Vitest coverage summary run: | - node scripts/summarize_vitest_coverage.mjs + node scripts/summarize_vitest_coverage.mjs orgfront cat reports/vitest-coverage-summary.md >> "$GITHUB_STEP_SUMMARY" - - name: Publish front Vitest coverage failure summary + - name: Publish orgfront Vitest coverage failure summary if: ${{ failure() }} run: | - if [ -f reports/front-vitest-coverage-failure-report.md ]; then - cat reports/front-vitest-coverage-failure-report.md >> "$GITHUB_STEP_SUMMARY" + if [ -f reports/orgfront-vitest-coverage-failure-report.md ]; then + cat reports/orgfront-vitest-coverage-failure-report.md >> "$GITHUB_STEP_SUMMARY" fi - - name: Upload front Vitest coverage report artifact + - name: Upload orgfront Vitest coverage report artifact if: ${{ always() }} uses: actions/upload-artifact@v3 continue-on-error: true with: - name: front-vitest-coverage-report + name: orgfront-vitest-coverage-report path: | reports/vitest-coverage-summary.md reports/vitest-coverage-summary.json - reports/front-vitest-coverage-failure-report.md + reports/orgfront-vitest-coverage-failure-report.md reports/front-coverage-install.log - reports/adminfront-vitest-coverage.log - reports/devfront-vitest-coverage.log reports/orgfront-vitest-coverage.log - adminfront/coverage - devfront/coverage orgfront/coverage if-no-files-found: ignore @@ -1406,7 +1602,9 @@ jobs: - backend-tests - userfront-tests - userfront-e2e-tests - - front-vitest-coverage + - adminfront-vitest-coverage + - devfront-vitest-coverage + - orgfront-vitest-coverage - adminfront-tests - devfront-tests - orgfront-tests @@ -1423,12 +1621,26 @@ jobs: with: node-version: "24" - - name: Download Vitest coverage report artifact + - name: Download adminfront Vitest coverage report artifact uses: actions/download-artifact@v3 continue-on-error: true with: - name: front-vitest-coverage-report - path: badge-artifacts/front-vitest-coverage-report + name: adminfront-vitest-coverage-report + path: badge-artifacts/adminfront + + - name: Download devfront Vitest coverage report artifact + uses: actions/download-artifact@v3 + continue-on-error: true + with: + name: devfront-vitest-coverage-report + path: badge-artifacts/devfront + + - name: Download orgfront Vitest coverage report artifact + uses: actions/download-artifact@v3 + continue-on-error: true + with: + name: orgfront-vitest-coverage-report + path: badge-artifacts/orgfront - name: Update badge files env: @@ -1438,7 +1650,9 @@ jobs: USERFRONT_RESULT: ${{ needs['userfront-tests'].result }} USERFRONT_E2E_RESULT: ${{ needs['userfront-e2e-tests'].result }} USERFRONT_E2E_FULL: ${{ github.event_name == 'workflow_dispatch' && inputs.run_userfront_e2e_full == true }} - COVERAGE_RESULT: ${{ needs['front-vitest-coverage'].result }} + ADMINFRONT_COVERAGE_RESULT: ${{ needs['adminfront-vitest-coverage'].result }} + DEVFRONT_COVERAGE_RESULT: ${{ needs['devfront-vitest-coverage'].result }} + ORGFRONT_COVERAGE_RESULT: ${{ needs['orgfront-vitest-coverage'].result }} ADMINFRONT_RESULT: ${{ needs['adminfront-tests'].result }} DEVFRONT_RESULT: ${{ needs['devfront-tests'].result }} ORGFRONT_RESULT: ${{ needs['orgfront-tests'].result }} diff --git a/.gitea/workflows/userfront_e2e_full_nightly.yml b/.gitea/workflows/userfront_e2e_full_nightly.yml index a9dc0a49..2492ce54 100644 --- a/.gitea/workflows/userfront_e2e_full_nightly.yml +++ b/.gitea/workflows/userfront_e2e_full_nightly.yml @@ -63,9 +63,9 @@ jobs: git cat-file -e "refs/remotes/origin/badges:dev/${target_sha}/badges.json" 2>/dev/null; then full_message="$( git show "refs/remotes/origin/badges:dev/${target_sha}/badges.json" | - node -e "let input=''; process.stdin.on('data', c => input += c); process.stdin.on('end', () => { const data = JSON.parse(input); process.stdout.write(data.badges?.['userfront-e2e-full']?.message || 'unknown'); });" + node -e "let input=''; process.stdin.on('data', c => input += c); process.stdin.on('end', () => { const data = JSON.parse(input); const keys = ['userfront-chrome', 'userfront-firefox', 'userfront-safari']; const messages = keys.map((key) => data.badges?.[key]?.message || 'unknown'); process.stdout.write(messages.join(',')); });" )" - if [ -n "${full_message}" ] && [ "${full_message}" != "unknown" ]; then + if [ -n "${full_message}" ] && ! printf '%s' "${full_message}" | grep -q "unknown"; then should_run="false" reason="full-result-exists:${full_message}" fi @@ -85,6 +85,13 @@ jobs: if: ${{ needs.lint.result == 'success' && needs.full-test-policy.outputs.should_run == 'true' }} runs-on: ubuntu-latest timeout-minutes: 80 + outputs: + chromium_desktop: ${{ steps.full-results.outputs.chromium_desktop }} + chromium_mobile: ${{ steps.full-results.outputs.chromium_mobile }} + firefox_desktop: ${{ steps.full-results.outputs.firefox_desktop }} + firefox_mobile: ${{ steps.full-results.outputs.firefox_mobile }} + webkit_desktop: ${{ steps.full-results.outputs.webkit_desktop }} + webkit_mobile: ${{ steps.full-results.outputs.webkit_mobile }} steps: - name: Checkout code uses: actions/checkout@v4 @@ -124,9 +131,44 @@ jobs: npx playwright install --with-deps - name: Run full userfront-e2e tests + id: full-results run: | + mkdir -p reports cd userfront-e2e - npm test + workers="${PLAYWRIGHT_WORKERS:-4}" + case "$workers" in + ''|*[!0-9]*|0) workers=4 ;; + esac + any_failure=0 + + run_project() { + output_name="$1" + project_name="$2" + log_path="../reports/userfront-e2e-full-${project_name}.log" + + set +e + echo "[userfront-e2e-full] PLAYWRIGHT_WORKERS=${workers} npx playwright test --project=${project_name}" | tee "$log_path" + PLAYWRIGHT_WORKERS="$workers" npx playwright test --project="$project_name" --reporter=list 2>&1 | tee -a "$log_path" + exit_code=${PIPESTATUS[0]} + set -e + + if [ "$exit_code" -eq 0 ]; then + result="success" + else + result="failure" + any_failure=1 + fi + echo "${output_name}=${result}" >> "$GITHUB_OUTPUT" + } + + run_project chromium_desktop chromium-desktop + run_project chromium_mobile chromium-mobile-webapp + run_project firefox_desktop firefox-desktop + echo "firefox_mobile=skipped" >> "$GITHUB_OUTPUT" + run_project webkit_desktop webkit-desktop + run_project webkit_mobile webkit-mobile-webapp + + exit "$any_failure" - name: Upload userfront-e2e full artifacts if: ${{ always() }} @@ -135,6 +177,7 @@ jobs: with: name: userfront-e2e-full-report path: | + reports/userfront-e2e-full-*.log userfront-e2e/playwright-report userfront-e2e/test-results if-no-files-found: ignore @@ -161,6 +204,12 @@ jobs: env: USERFRONT_E2E_RESULT: ${{ needs.userfront-e2e-full.result }} USERFRONT_E2E_FULL: "true" + USERFRONT_E2E_CHROMIUM_DESKTOP_RESULT: ${{ needs.userfront-e2e-full.outputs.chromium_desktop }} + USERFRONT_E2E_CHROMIUM_MOBILE_RESULT: ${{ needs.userfront-e2e-full.outputs.chromium_mobile }} + USERFRONT_E2E_FIREFOX_DESKTOP_RESULT: ${{ needs.userfront-e2e-full.outputs.firefox_desktop }} + USERFRONT_E2E_FIREFOX_MOBILE_RESULT: ${{ needs.userfront-e2e-full.outputs.firefox_mobile }} + USERFRONT_E2E_WEBKIT_DESKTOP_RESULT: ${{ needs.userfront-e2e-full.outputs.webkit_desktop }} + USERFRONT_E2E_WEBKIT_MOBILE_RESULT: ${{ needs.userfront-e2e-full.outputs.webkit_mobile }} BADGE_UPDATE_CODE_CHECK: "false" BADGE_SOURCE_BRANCH: dev BADGE_SOURCE_SHA: ${{ github.sha }} diff --git a/README.md b/README.md index a6b0bec4..66c161b6 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,10 @@ # Baron SSO -[![dev](https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/dev-sha.svg)](https://gitea.hmac.kr/baron/baron-sso/src/branch/dev) -[![Code Check](https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/code-check.svg)](https://gitea.hmac.kr/baron/baron-sso/actions/workflows/code_check.yml?branch=dev) -[![Biome](https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/biome.svg)](https://gitea.hmac.kr/baron/baron-sso/actions/workflows/code_check.yml?branch=dev) -[![userfront e2e fast](https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/userfront-e2e-fast.svg)](https://gitea.hmac.kr/baron/baron-sso/actions/workflows/code_check.yml?branch=dev) -[![userfront e2e full](https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/userfront-e2e-full.svg)](https://gitea.hmac.kr/baron/baron-sso/actions/workflows/code_check.yml?branch=dev) -[![adminfront coverage](https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/adminfront-coverage.svg)](https://gitea.hmac.kr/baron/baron-sso/actions/workflows/code_check.yml?branch=dev) -[![devfront coverage](https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/devfront-coverage.svg)](https://gitea.hmac.kr/baron/baron-sso/actions/workflows/code_check.yml?branch=dev) -[![orgfront coverage](https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/orgfront-coverage.svg)](https://gitea.hmac.kr/baron/baron-sso/actions/workflows/code_check.yml?branch=dev) +[![dev](https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/dev-sha.svg)](https://gitea.hmac.kr/baron/baron-sso/src/branch/dev) [![Code Check](https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/code-check.svg)](https://gitea.hmac.kr/baron/baron-sso/actions/workflows/code_check.yml?branch=dev) [![Biome](https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/biome.svg)](https://gitea.hmac.kr/baron/baron-sso/actions/workflows/code_check.yml?branch=dev) [![backend](https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/backend-tests.svg)](https://gitea.hmac.kr/baron/baron-sso/actions/workflows/code_check.yml?branch=dev) +[![userfront](https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/userfront.svg)](https://gitea.hmac.kr/baron/baron-sso/actions/workflows/code_check.yml?branch=dev) [![adminfront](https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/adminfront.svg)](https://gitea.hmac.kr/baron/baron-sso/actions/workflows/code_check.yml?branch=dev) [![devfront](https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/devfront.svg)](https://gitea.hmac.kr/baron/baron-sso/actions/workflows/code_check.yml?branch=dev) [![orgfront](https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/orgfront.svg)](https://gitea.hmac.kr/baron/baron-sso/actions/workflows/code_check.yml?branch=dev) +[![chrome](https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/userfront-chrome.svg)](https://gitea.hmac.kr/baron/baron-sso/actions/workflows/userfront_e2e_full_nightly.yml?branch=dev) [![firefox](https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/userfront-firefox.svg)](https://gitea.hmac.kr/baron/baron-sso/actions/workflows/userfront_e2e_full_nightly.yml?branch=dev) [![safari](https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/userfront-safari.svg)](https://gitea.hmac.kr/baron/baron-sso/actions/workflows/userfront_e2e_full_nightly.yml?branch=dev) -badge는 `Code Check`가 `badges` 브랜치의 `latest/`와 `dev//`에 발행합니다. 최신 HTML/LCOV/JSON summary는 Gitea `Code Check`의 `front-vitest-coverage-report` artifact에서 확인할 수 있습니다. +badge는 `Code Check`가 `badges` 브랜치의 `latest/`와 `dev//`에 발행합니다. 최신 HTML/LCOV/JSON summary는 Gitea `Code Check`의 패키지별 `*-vitest-coverage-report` artifact에서 확인할 수 있습니다. **Baron 로그인**은 화이트 라벨링된 가족사의 모든 소프트웨어 Auth를 총괄하는 사용자 인증/인가 허브입니다. diff --git a/devfront/src/features/coverage/pageSmoke.test.tsx b/devfront/src/features/coverage/pageSmoke.test.tsx index 72ccaeab..2c7140f4 100644 --- a/devfront/src/features/coverage/pageSmoke.test.tsx +++ b/devfront/src/features/coverage/pageSmoke.test.tsx @@ -44,6 +44,23 @@ vi.mock("../../lib/i18n", () => ({ }, })); +vi.mock("../../../../common/core/components/audit", () => ({ + AuditLogTable: ({ + logs, + }: { + logs: Array<{ user_id: string; event_type: string }>; + }) => ( +
+ {logs.map((log) => ( +
+ {log.user_id} + {log.event_type} +
+ ))} +
+ ), +})); + const clientSummary = { id: "client-a", name: "Console App", diff --git a/devfront/vitest.config.ts b/devfront/vitest.config.ts index 02b9589f..38793db1 100644 --- a/devfront/vitest.config.ts +++ b/devfront/vitest.config.ts @@ -43,6 +43,7 @@ export default defineConfig({ "../common/**/node_modules/**", "../common/.pnpm-store/**", `${commonRoot}/theme/**`, + `${commonRoot}/core/pagination/*.ts`, `${commonRoot}/core/pagination/*.worker.ts`, `${commonRoot}/core/query/queryClient.ts`, ], diff --git a/docs/badges/adminfront-coverage.svg b/docs/badges/adminfront-coverage.svg deleted file mode 100644 index aec87a00..00000000 --- a/docs/badges/adminfront-coverage.svg +++ /dev/null @@ -1,19 +0,0 @@ - - adminfront coverage: 38.89% - - - - - - - - - - - - adminfront coverage - adminfront coverage - 38.89% - 38.89% - - diff --git a/docs/badges/adminfront.svg b/docs/badges/adminfront.svg new file mode 100644 index 00000000..bbaa16ca --- /dev/null +++ b/docs/badges/adminfront.svg @@ -0,0 +1,19 @@ + + adminfront: unknown + + + + + + + + + + + + adminfront + adminfront + unknown + unknown + + diff --git a/docs/badges/backend-tests.svg b/docs/badges/backend-tests.svg new file mode 100644 index 00000000..f4a3a03f --- /dev/null +++ b/docs/badges/backend-tests.svg @@ -0,0 +1,19 @@ + + backend: unknown + + + + + + + + + + + + backend + backend + unknown + unknown + + diff --git a/docs/badges/badges.json b/docs/badges/badges.json index 90c2dc0e..902801a8 100644 --- a/docs/badges/badges.json +++ b/docs/badges/badges.json @@ -1,8 +1,20 @@ { "schemaVersion": 1, "generatedBy": "scripts/update_code_check_badges.mjs", - "updatedAt": "2026-05-29T03:35:39.250Z", + "updatedAt": "2026-05-29T08:13:02.131Z", + "source": { + "branch": "dev", + "sha": null, + "shortSha": null, + "runId": null, + "runNumber": null + }, "badges": { + "dev-sha": { + "label": "dev", + "message": "unknown", + "color": "#0969da" + }, "code-check": { "label": "code check", "message": "failing", @@ -13,30 +25,45 @@ "message": "passing", "color": "#2ea043" }, - "userfront-e2e-fast": { - "label": "userfront e2e fast", - "message": "failing", - "color": "#cf222e" - }, - "userfront-e2e-full": { - "label": "userfront e2e full", + "backend-tests": { + "label": "backend", "message": "unknown", "color": "#6e7781" }, - "adminfront-coverage": { - "label": "adminfront coverage", - "message": "38.89%", - "color": "#bf8700" + "userfront": { + "label": "userfront", + "message": "unknown", + "color": "#6e7781" }, - "devfront-coverage": { - "label": "devfront coverage", - "message": "8.87%", - "color": "#cf222e" + "adminfront": { + "label": "adminfront", + "message": "unknown", + "color": "#6e7781" }, - "orgfront-coverage": { - "label": "orgfront coverage", - "message": "37.50%", - "color": "#bf8700" + "devfront": { + "label": "devfront", + "message": "unknown", + "color": "#6e7781" + }, + "orgfront": { + "label": "orgfront", + "message": "unknown", + "color": "#6e7781" + }, + "userfront-chrome": { + "label": "chrome", + "message": "unknown", + "color": "#6e7781" + }, + "userfront-firefox": { + "label": "firefox", + "message": "unknown", + "color": "#6e7781" + }, + "userfront-safari": { + "label": "safari", + "message": "unknown", + "color": "#6e7781" } } } diff --git a/docs/badges/dev-sha.svg b/docs/badges/dev-sha.svg new file mode 100644 index 00000000..abe324d2 --- /dev/null +++ b/docs/badges/dev-sha.svg @@ -0,0 +1,19 @@ + + dev: unknown + + + + + + + + + + + + dev + dev + unknown + unknown + + diff --git a/docs/badges/devfront-coverage.svg b/docs/badges/devfront-coverage.svg deleted file mode 100644 index 5f841ca1..00000000 --- a/docs/badges/devfront-coverage.svg +++ /dev/null @@ -1,19 +0,0 @@ - - devfront coverage: 8.87% - - - - - - - - - - - - devfront coverage - devfront coverage - 8.87% - 8.87% - - diff --git a/docs/badges/devfront.svg b/docs/badges/devfront.svg new file mode 100644 index 00000000..4f4a2219 --- /dev/null +++ b/docs/badges/devfront.svg @@ -0,0 +1,19 @@ + + devfront: unknown + + + + + + + + + + + + devfront + devfront + unknown + unknown + + diff --git a/docs/badges/orgfront-coverage.svg b/docs/badges/orgfront-coverage.svg deleted file mode 100644 index 82b8bad1..00000000 --- a/docs/badges/orgfront-coverage.svg +++ /dev/null @@ -1,19 +0,0 @@ - - orgfront coverage: 37.50% - - - - - - - - - - - - orgfront coverage - orgfront coverage - 37.50% - 37.50% - - diff --git a/docs/badges/orgfront.svg b/docs/badges/orgfront.svg new file mode 100644 index 00000000..bda57089 --- /dev/null +++ b/docs/badges/orgfront.svg @@ -0,0 +1,19 @@ + + orgfront: unknown + + + + + + + + + + + + orgfront + orgfront + unknown + unknown + + diff --git a/docs/badges/userfront-chrome.svg b/docs/badges/userfront-chrome.svg new file mode 100644 index 00000000..c8e41adf --- /dev/null +++ b/docs/badges/userfront-chrome.svg @@ -0,0 +1,19 @@ + + chrome: unknown + + + + + + + + + + + + chrome + chrome + unknown + unknown + + diff --git a/docs/badges/userfront-e2e-fast.svg b/docs/badges/userfront-e2e-fast.svg deleted file mode 100644 index 3cfc77f1..00000000 --- a/docs/badges/userfront-e2e-fast.svg +++ /dev/null @@ -1,19 +0,0 @@ - - userfront e2e fast: failing - - - - - - - - - - - - userfront e2e fast - userfront e2e fast - failing - failing - - diff --git a/docs/badges/userfront-e2e-full.svg b/docs/badges/userfront-e2e-full.svg deleted file mode 100644 index 1c8369c8..00000000 --- a/docs/badges/userfront-e2e-full.svg +++ /dev/null @@ -1,19 +0,0 @@ - - userfront e2e full: unknown - - - - - - - - - - - - userfront e2e full - userfront e2e full - unknown - unknown - - diff --git a/docs/badges/userfront-firefox.svg b/docs/badges/userfront-firefox.svg new file mode 100644 index 00000000..8d367f33 --- /dev/null +++ b/docs/badges/userfront-firefox.svg @@ -0,0 +1,19 @@ + + firefox: unknown + + + + + + + + + + + + firefox + firefox + unknown + unknown + + diff --git a/docs/badges/userfront-safari.svg b/docs/badges/userfront-safari.svg new file mode 100644 index 00000000..c290a2f9 --- /dev/null +++ b/docs/badges/userfront-safari.svg @@ -0,0 +1,19 @@ + + safari: unknown + + + + + + + + + + + + safari + safari + unknown + unknown + + diff --git a/docs/badges/userfront.svg b/docs/badges/userfront.svg new file mode 100644 index 00000000..aa2f8eaf --- /dev/null +++ b/docs/badges/userfront.svg @@ -0,0 +1,19 @@ + + userfront: unknown + + + + + + + + + + + + userfront + userfront + unknown + unknown + + diff --git a/orgfront/package.json b/orgfront/package.json index bafa879f..1dd9a519 100644 --- a/orgfront/package.json +++ b/orgfront/package.json @@ -59,6 +59,6 @@ "tailwindcss-animate": "^1.0.7", "typescript": "^6.0.3", "vite": "^8.0.14", - "vitest": "^4.1.6" + "vitest": "4.1.6" } } diff --git a/orgfront/vitest.config.ts b/orgfront/vitest.config.ts index b900b930..d531e319 100644 --- a/orgfront/vitest.config.ts +++ b/orgfront/vitest.config.ts @@ -42,6 +42,14 @@ export default defineConfig({ "../common/**/node_modules/**", "../common/.pnpm-store/**", `${commonRoot}/theme/**`, + `${commonRoot}/core/audit/index.ts`, + `${commonRoot}/core/components/audit/AuditLogTable.tsx`, + `${commonRoot}/core/components/overview/OverviewAxisNotes.tsx`, + `${commonRoot}/core/components/overview/OverviewMetric.tsx`, + `${commonRoot}/core/components/overview/OverviewSelectionChips.tsx`, + `${commonRoot}/core/components/page/PageHeader.tsx`, + `${commonRoot}/core/components/sort/SortableTableHead.tsx`, + `${commonRoot}/core/pagination/*.ts`, `${commonRoot}/core/pagination/*.worker.ts`, `${commonRoot}/core/query/queryClient.ts`, ], diff --git a/scripts/summarize_vitest_coverage.mjs b/scripts/summarize_vitest_coverage.mjs index 78596fbd..443d97ed 100644 --- a/scripts/summarize_vitest_coverage.mjs +++ b/scripts/summarize_vitest_coverage.mjs @@ -2,7 +2,9 @@ import { mkdir, readFile, writeFile } from "node:fs/promises"; import path from "node:path"; const repoRoot = process.cwd(); -const packages = ["adminfront", "devfront", "orgfront"]; +const defaultPackages = ["adminfront", "devfront", "orgfront"]; +const packages = process.argv.slice(2); +const targetPackages = packages.length > 0 ? packages : defaultPackages; function formatPct(value) { return typeof value === "number" ? `${value.toFixed(2)}%` : "n/a"; @@ -63,7 +65,7 @@ function renderMarkdown(rows) { } const rows = []; -for (const packageName of packages) { +for (const packageName of targetPackages) { rows.push(await readCoverageSummary(packageName)); } diff --git a/scripts/update_code_check_badges.mjs b/scripts/update_code_check_badges.mjs index 77590a1b..854c80d8 100644 --- a/scripts/update_code_check_badges.mjs +++ b/scripts/update_code_check_badges.mjs @@ -1,4 +1,4 @@ -import { mkdir, readFile, readdir, writeFile } from "node:fs/promises"; +import { mkdir, readdir, readFile, unlink, writeFile } from "node:fs/promises"; import path from "node:path"; const repoRoot = process.cwd(); @@ -6,78 +6,124 @@ const badgeDir = path.join(repoRoot, "docs", "badges"); const manifestPath = path.join(badgeDir, "badges.json"); const resultStyles = { - success: { message: "passing", color: "#2ea043" }, - failure: { message: "failing", color: "#cf222e" }, - cancelled: { message: "cancelled", color: "#bf8700" }, - skipped: { message: "skipped", color: "#6e7781" }, - unknown: { message: "unknown", color: "#6e7781" }, + success: { message: "passing", color: "#2ea043" }, + failure: { message: "failing", color: "#cf222e" }, + cancelled: { message: "cancelled", color: "#bf8700" }, + skipped: { message: "skipped", color: "#6e7781" }, + unknown: { message: "unknown", color: "#6e7781" }, }; const badgeDefinitions = { - "dev-sha": { label: "dev", message: "unknown", color: "#0969da" }, - "code-check": { label: "code check", message: "unknown", color: "#6e7781" }, - biome: { label: "biome", message: "unknown", color: "#6e7781" }, - "userfront-e2e-fast": { - label: "userfront e2e fast", - message: "unknown", - color: "#6e7781", - }, - "userfront-e2e-full": { - label: "userfront e2e full", - message: "unknown", - color: "#6e7781", - }, - "adminfront-coverage": { - label: "adminfront coverage", - message: "38.89%", - color: "#bf8700", - }, - "devfront-coverage": { - label: "devfront coverage", - message: "8.87%", - color: "#cf222e", - }, - "orgfront-coverage": { - label: "orgfront coverage", - message: "37.50%", - color: "#bf8700", - }, + "dev-sha": { label: "dev", message: "unknown", color: "#0969da" }, + "code-check": { label: "code check", message: "unknown", color: "#6e7781" }, + biome: { label: "biome", message: "unknown", color: "#6e7781" }, + "backend-tests": { + label: "backend", + message: "unknown", + color: "#6e7781", + }, + userfront: { + label: "userfront", + message: "unknown", + color: "#6e7781", + }, + adminfront: { + label: "adminfront", + message: "unknown", + color: "#6e7781", + }, + devfront: { + label: "devfront", + message: "unknown", + color: "#6e7781", + }, + orgfront: { + label: "orgfront", + message: "unknown", + color: "#6e7781", + }, + "userfront-chrome": { + label: "chrome", + message: "unknown", + color: "#6e7781", + }, + "userfront-firefox": { + label: "firefox", + message: "unknown", + color: "#6e7781", + }, + "userfront-safari": { + label: "safari", + message: "unknown", + color: "#6e7781", + }, }; +const deprecatedBadgeKeys = [ + "userfront-e2e-fast", + "userfront-e2e-full", + "adminfront-e2e", + "devfront-e2e", + "orgfront-e2e", + "userfront-coverage", + "adminfront-coverage", + "devfront-coverage", + "orgfront-coverage", +]; + function normalizeResult(result) { - return resultStyles[result] ? result : "unknown"; + return resultStyles[result] ? result : "unknown"; } function styleForResult(result) { - return resultStyles[normalizeResult(result)]; + return resultStyles[normalizeResult(result)]; +} + +function compactResult(result) { + const normalized = normalizeResult(result); + return { + success: "pass", + failure: "fail", + cancelled: "cancel", + skipped: "skip", + unknown: "unknown", + }[normalized]; +} + +function colorForParts(parts) { + const normalized = parts.map(normalizeResult); + if (normalized.includes("failure")) return resultStyles.failure.color; + if (normalized.includes("cancelled")) return resultStyles.cancelled.color; + if (normalized.every((part) => part === "success")) return resultStyles.success.color; + return resultStyles.unknown.color; } function colorForCoverage(percent) { - if (percent >= 80) return "#2ea043"; - if (percent >= 35) return "#bf8700"; - return "#cf222e"; + if (percent >= 80) return "#2ea043"; + if (percent >= 35) return "#bf8700"; + return "#cf222e"; } function escapeXml(value) { - return String(value) - .replace(/&/g, "&") - .replace(//g, ">") - .replace(/"/g, """); + return String(value) + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """); } function textWidth(text) { - return Math.max(38, Math.ceil(String(text).length * 6.8 + 10)); + return Math.max(38, Math.ceil(String(text).length * 6.8 + 10)); } function renderBadge({ label, message, color }) { - const labelWidth = textWidth(label); - const messageWidth = textWidth(message); - const width = labelWidth + messageWidth; - const labelCenter = labelWidth / 2; - const messageCenter = labelWidth + messageWidth / 2; + const labelWidth = textWidth(label); + const messageWidth = textWidth(message); + const width = labelWidth + messageWidth; + const labelCenter = labelWidth / 2; + const messageCenter = labelWidth + messageWidth / 2; - return ` + return ` ${escapeXml(label)}: ${escapeXml(message)} @@ -100,151 +146,284 @@ function renderBadge({ label, message, color }) { } async function readJsonIfExists(filePath) { - try { - return JSON.parse(await readFile(filePath, "utf8")); - } catch { - return null; - } + try { + return JSON.parse(await readFile(filePath, "utf8")); + } catch { + return null; + } } -async function findCoverageSummary(directory) { - const entries = await readdir(directory, { withFileTypes: true }).catch( - () => [], - ); +async function findCoverageSummaries(directory) { + const entries = await readdir(directory, { withFileTypes: true }).catch( + () => [], + ); + const results = []; - for (const entry of entries) { - const entryPath = path.join(directory, entry.name); - if (entry.isFile() && entry.name === "vitest-coverage-summary.json") { - return entryPath; - } - if (entry.isDirectory()) { - const found = await findCoverageSummary(entryPath); - if (found) return found; - } - } + for (const entry of entries) { + const entryPath = path.join(directory, entry.name); + if (entry.isFile() && entry.name === "vitest-coverage-summary.json") { + results.push(entryPath); + continue; + } + if (entry.isDirectory()) { + results.push(...(await findCoverageSummaries(entryPath))); + } + } - return null; + return results; } function updateResultBadge(manifest, key, result) { - const style = styleForResult(result); - manifest.badges[key] = { - ...(manifest.badges[key] ?? badgeDefinitions[key]), - message: style.message, - color: style.color, - }; + const style = styleForResult(result); + manifest.badges[key] = { + ...(manifest.badges[key] ?? badgeDefinitions[key]), + message: style.message, + color: style.color, + }; } -function updateCoverageBadges(manifest, coverageSummary) { - for (const row of coverageSummary.packages ?? []) { - const key = `${row.package}-coverage`; - if (!badgeDefinitions[key]) continue; - const statements = Number(row.statements); - manifest.badges[key] = { - ...(manifest.badges[key] ?? badgeDefinitions[key]), - message: `${statements.toFixed(2)}%`, - color: colorForCoverage(statements), - }; - } +function updateCompactResultBadge(manifest, key, result) { + const style = styleForResult(result); + manifest.badges[key] = { + ...(manifest.badges[key] ?? badgeDefinitions[key]), + label: badgeDefinitions[key]?.label ?? manifest.badges[key]?.label, + message: compactResult(result), + color: style.color, + }; +} + +function coveragePart(result, statements) { + const normalized = normalizeResult(result); + if (normalized !== "success") { + return { + message: compactResult(normalized), + color: styleForResult(normalized).color, + result: normalized, + }; + } + const value = Number(statements); + if (!Number.isFinite(value)) { + return { + message: "unknown", + color: resultStyles.unknown.color, + result: "unknown", + }; + } + return { + message: `${value.toFixed(2)}%`, + color: colorForCoverage(value), + result: "success", + }; +} + +function updatePackageBadge(manifest, key, testResult, coverageResult, statements) { + if (!badgeDefinitions[key]) return; + const test = normalizeResult(testResult); + const coverage = coveragePart(coverageResult, statements); + manifest.badges[key] = { + ...(manifest.badges[key] ?? badgeDefinitions[key]), + label: badgeDefinitions[key].label, + message: `${compactResult(test)} | ${coverage.message}`, + color: test === "failure" || coverage.result === "failure" + ? resultStyles.failure.color + : test === "cancelled" || coverage.result === "cancelled" + ? resultStyles.cancelled.color + : coverage.result === "success" + ? coverage.color + : colorForParts([test, coverage.result]), + }; +} + +function updateBrowserBadge(manifest, key, desktopResult, mobileResult) { + if (!badgeDefinitions[key]) return; + const desktop = normalizeResult(desktopResult); + const mobile = normalizeResult(mobileResult); + manifest.badges[key] = { + ...(manifest.badges[key] ?? badgeDefinitions[key]), + label: badgeDefinitions[key].label, + message: `${compactResult(desktop)} | ${compactResult(mobile)}`, + color: colorForParts([desktop, mobile]), + }; +} + +function normalizeManifestForComparison(value) { + return { + ...value, + updatedAt: null, + source: { + ...(value?.source ?? {}), + runId: null, + runNumber: null, + }, + }; +} + +function manifestsMatchIgnoringRunMetadata(left, right) { + return ( + JSON.stringify(normalizeManifestForComparison(left)) === + JSON.stringify(normalizeManifestForComparison(right)) + ); } function shortSha(value) { - return String(value ?? "").trim().slice(0, 12); + return String(value ?? "") + .trim() + .slice(0, 12); } -const existingManifest = process.env.RESET_BADGES === "true" - ? null - : await readJsonIfExists(manifestPath); -const sourceSha = shortSha(process.env.BADGE_SOURCE_SHA || process.env.GITHUB_SHA); +const existingManifest = + process.env.RESET_BADGES === "true" + ? null + : await readJsonIfExists(manifestPath); +const sourceSha = shortSha( + process.env.BADGE_SOURCE_SHA || process.env.GITHUB_SHA, +); const manifest = { - schemaVersion: 1, - generatedBy: "scripts/update_code_check_badges.mjs", - updatedAt: new Date().toISOString(), - source: { - branch: process.env.BADGE_SOURCE_BRANCH || "dev", - sha: process.env.BADGE_SOURCE_SHA || process.env.GITHUB_SHA || null, - shortSha: sourceSha || null, - runId: process.env.GITHUB_RUN_ID || null, - runNumber: process.env.GITHUB_RUN_NUMBER || null, - }, - badges: { - ...badgeDefinitions, - ...(existingManifest?.badges ?? {}), - }, + schemaVersion: 1, + generatedBy: "scripts/update_code_check_badges.mjs", + updatedAt: new Date().toISOString(), + source: { + branch: process.env.BADGE_SOURCE_BRANCH || "dev", + sha: process.env.BADGE_SOURCE_SHA || process.env.GITHUB_SHA || null, + shortSha: sourceSha || null, + runId: process.env.GITHUB_RUN_ID || null, + runNumber: process.env.GITHUB_RUN_NUMBER || null, + }, + badges: { + ...badgeDefinitions, + ...(existingManifest?.badges ?? {}), + }, }; +for (const key of deprecatedBadgeKeys) { + delete manifest.badges[key]; +} manifest.badges["dev-sha"] = { - ...badgeDefinitions["dev-sha"], - message: sourceSha || "unknown", + ...badgeDefinitions["dev-sha"], + message: sourceSha || "unknown", }; const jobResults = { - lint: process.env.LINT_RESULT, - biome: process.env.BIOME_RESULT, - backend: process.env.BACKEND_RESULT, - userfront: process.env.USERFRONT_RESULT, - userfrontE2e: process.env.USERFRONT_E2E_RESULT, - coverage: process.env.COVERAGE_RESULT, - adminfront: process.env.ADMINFRONT_RESULT, - devfront: process.env.DEVFRONT_RESULT, - orgfront: process.env.ORGFRONT_RESULT, + lint: process.env.LINT_RESULT, + biome: process.env.BIOME_RESULT, + backend: process.env.BACKEND_RESULT, + userfront: process.env.USERFRONT_RESULT, + userfrontE2e: process.env.USERFRONT_E2E_RESULT, + adminfront: process.env.ADMINFRONT_RESULT, + devfront: process.env.DEVFRONT_RESULT, + orgfront: process.env.ORGFRONT_RESULT, +}; +const e2eWasFull = process.env.USERFRONT_E2E_FULL === "true"; +const userfrontFastResult = e2eWasFull ? undefined : jobResults.userfrontE2e; +const browserResults = { + chrome: { + desktop: process.env.USERFRONT_E2E_CHROMIUM_DESKTOP_RESULT, + mobile: process.env.USERFRONT_E2E_CHROMIUM_MOBILE_RESULT, + }, + firefox: { + desktop: process.env.USERFRONT_E2E_FIREFOX_DESKTOP_RESULT, + mobile: process.env.USERFRONT_E2E_FIREFOX_MOBILE_RESULT, + }, + safari: { + desktop: process.env.USERFRONT_E2E_WEBKIT_DESKTOP_RESULT, + mobile: process.env.USERFRONT_E2E_WEBKIT_MOBILE_RESULT, + }, }; -const overallResults = Object.values(jobResults).filter(Boolean); +const legacyCoverageResult = process.env.COVERAGE_RESULT; +const coverageJobResults = { + userfront: process.env.USERFRONT_COVERAGE_RESULT, + adminfront: process.env.ADMINFRONT_COVERAGE_RESULT || legacyCoverageResult, + devfront: process.env.DEVFRONT_COVERAGE_RESULT || legacyCoverageResult, + orgfront: process.env.ORGFRONT_COVERAGE_RESULT || legacyCoverageResult, +}; + +const overallResults = [ + ...Object.values(jobResults), + ...Object.values(coverageJobResults), + ...Object.values(browserResults).flatMap((result) => [ + result.desktop, + result.mobile, + ]), +].filter(Boolean); const hasFailure = overallResults.some((result) => - ["failure", "cancelled"].includes(result), + ["failure", "cancelled"].includes(result), ); -const allSkipped = overallResults.length > 0 && - overallResults.every((result) => result === "skipped"); +const allSkipped = + overallResults.length > 0 && + overallResults.every((result) => result === "skipped"); if (process.env.BADGE_UPDATE_CODE_CHECK !== "false") { - updateResultBadge( - manifest, - "code-check", - overallResults.length === 0 - ? "unknown" - : hasFailure - ? "failure" - : allSkipped - ? "skipped" - : "success", - ); + updateResultBadge( + manifest, + "code-check", + overallResults.length === 0 + ? "unknown" + : hasFailure + ? "failure" + : allSkipped + ? "skipped" + : "success", + ); } updateResultBadge(manifest, "biome", jobResults.biome); +updateCompactResultBadge(manifest, "backend-tests", jobResults.backend); -const e2eWasFull = process.env.USERFRONT_E2E_FULL === "true"; -if (jobResults.userfrontE2e && jobResults.userfrontE2e !== "skipped") { - updateResultBadge( - manifest, - e2eWasFull ? "userfront-e2e-full" : "userfront-e2e-fast", - jobResults.userfrontE2e, - ); +const coverageSummaries = process.env.COVERAGE_SUMMARY_PATH + ? [process.env.COVERAGE_SUMMARY_PATH] + : await findCoverageSummaries(path.join(repoRoot, "badge-artifacts")); +const coverageByPackage = new Map(); +for (const summaryPath of coverageSummaries) { + const coverageSummary = await readJsonIfExists(summaryPath); + for (const row of coverageSummary?.packages ?? []) { + coverageByPackage.set(row.package, row.statements); + } } -if (jobResults.coverage === "failure" || jobResults.coverage === "cancelled") { - for (const key of [ - "adminfront-coverage", - "devfront-coverage", - "orgfront-coverage", - ]) { - updateResultBadge(manifest, key, "failure"); - } -} else { - const coverageSummaryPath = process.env.COVERAGE_SUMMARY_PATH || - (await findCoverageSummary(path.join(repoRoot, "badge-artifacts"))); - const coverageSummary = coverageSummaryPath - ? await readJsonIfExists(coverageSummaryPath) - : null; - if (coverageSummary) { - updateCoverageBadges(manifest, coverageSummary); - } +for (const [key, testResult, coverageResult] of [ + ["userfront", userfrontFastResult, coverageJobResults.userfront], + ["adminfront", jobResults.adminfront, coverageJobResults.adminfront], + ["devfront", jobResults.devfront, coverageJobResults.devfront], + ["orgfront", jobResults.orgfront, coverageJobResults.orgfront], +]) { + if (testResult || coverageResult) { + updatePackageBadge( + manifest, + key, + testResult, + coverageResult, + coverageByPackage.get(key), + ); + } +} + +for (const [browser, result] of Object.entries(browserResults)) { + if (result.desktop || result.mobile) { + updateBrowserBadge( + manifest, + `userfront-${browser}`, + result.desktop, + result.mobile, + ); + } +} + +if ( + existingManifest && + manifestsMatchIgnoringRunMetadata(manifest, existingManifest) +) { + manifest.updatedAt = existingManifest.updatedAt; + manifest.source = existingManifest.source ?? manifest.source; } await mkdir(badgeDir, { recursive: true }); await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`); for (const [key, badge] of Object.entries(manifest.badges)) { - await writeFile(path.join(badgeDir, `${key}.svg`), renderBadge(badge)); + await writeFile(path.join(badgeDir, `${key}.svg`), renderBadge(badge)); +} +for (const key of deprecatedBadgeKeys) { + await unlink(path.join(badgeDir, `${key}.svg`)).catch(() => {}); } console.log(`Updated ${Object.keys(manifest.badges).length} badge files.`); diff --git a/test/code_check_badge_branch_policy_test.sh b/test/code_check_badge_branch_policy_test.sh index 9d9f6c56..5093aa6e 100644 --- a/test/code_check_badge_branch_policy_test.sh +++ b/test/code_check_badge_branch_policy_test.sh @@ -30,13 +30,36 @@ assert_contains "$WORKFLOW_FILE" 'push origin HEAD:${BADGE_BRANCH}' assert_contains "$WORKFLOW_FILE" 'BADGE_SOURCE_SHA: ${{ github.sha }}' assert_contains "$WORKFLOW_FILE" 'BADGE_LATEST_DIR="${BADGE_WORKTREE}/latest"' assert_contains "$WORKFLOW_FILE" 'BADGE_SHA_DIR="${BADGE_WORKTREE}/dev/${GITHUB_SHA}"' +assert_contains "$WORKFLOW_FILE" "adminfront-vitest-coverage:" +assert_contains "$WORKFLOW_FILE" "devfront-vitest-coverage:" +assert_contains "$WORKFLOW_FILE" "orgfront-vitest-coverage:" +if grep -Eq "^[[:space:]]+front-vitest-coverage:$" "$WORKFLOW_FILE"; then + fail "Code Check workflow must use package-specific Vitest coverage jobs" +fi +assert_contains "$WORKFLOW_FILE" "ADMINFRONT_COVERAGE_RESULT: \${{ needs['adminfront-vitest-coverage'].result }}" +assert_contains "$WORKFLOW_FILE" "DEVFRONT_COVERAGE_RESULT: \${{ needs['devfront-vitest-coverage'].result }}" +assert_contains "$WORKFLOW_FILE" "ORGFRONT_COVERAGE_RESULT: \${{ needs['orgfront-vitest-coverage'].result }}" +assert_contains "$WORKFLOW_FILE" "name: adminfront-vitest-coverage-report" +assert_contains "$WORKFLOW_FILE" "name: devfront-vitest-coverage-report" +assert_contains "$WORKFLOW_FILE" "name: orgfront-vitest-coverage-report" if grep -Eq "^[[:space:]]+git push$" "$WORKFLOW_FILE"; then fail "Code Check workflow must not push back to the current branch" fi assert_contains "$README_FILE" "https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/code-check.svg" assert_contains "$README_FILE" "https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/dev-sha.svg" -assert_contains "$README_FILE" "https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/userfront-e2e-full.svg" -assert_contains "$README_FILE" "https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/adminfront-coverage.svg" +assert_contains "$README_FILE" "https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/backend-tests.svg" +assert_contains "$README_FILE" "https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/userfront.svg" +assert_contains "$README_FILE" "https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/adminfront.svg" +assert_contains "$README_FILE" "https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/devfront.svg" +assert_contains "$README_FILE" "https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/orgfront.svg" +assert_contains "$README_FILE" "https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/userfront-chrome.svg" +assert_contains "$README_FILE" "https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/userfront-firefox.svg" +assert_contains "$README_FILE" "https://gitea.hmac.kr/baron/baron-sso/raw/branch/badges/latest/userfront-safari.svg" +assert_not_contains "$README_FILE" "userfront-coverage.svg" +assert_not_contains "$README_FILE" "adminfront-coverage.svg" +assert_not_contains "$README_FILE" "adminfront-e2e.svg" +assert_not_contains "$README_FILE" "userfront-e2e-fast.svg" +assert_not_contains "$README_FILE" "userfront-e2e-full.svg" assert_not_contains "$README_FILE" "](docs/badges/" assert_contains "$FULL_NIGHTLY_WORKFLOW_FILE" "cron: \"0 18 * * *\"" @@ -44,7 +67,18 @@ assert_contains "$FULL_NIGHTLY_WORKFLOW_FILE" "make code-check-lint" assert_contains "$FULL_NIGHTLY_WORKFLOW_FILE" "refs/remotes/origin/badges:dev/\${target_sha}/badges.json" assert_contains "$FULL_NIGHTLY_WORKFLOW_FILE" "full-result-exists:\${full_message}" assert_contains "$FULL_NIGHTLY_WORKFLOW_FILE" "USERFRONT_E2E_FULL: \"true\"" +assert_contains "$FULL_NIGHTLY_WORKFLOW_FILE" "chromium-desktop" +assert_contains "$FULL_NIGHTLY_WORKFLOW_FILE" "chromium-mobile-webapp" +assert_contains "$FULL_NIGHTLY_WORKFLOW_FILE" "firefox-desktop" +assert_contains "$FULL_NIGHTLY_WORKFLOW_FILE" "webkit-desktop" +assert_contains "$FULL_NIGHTLY_WORKFLOW_FILE" "webkit-mobile-webapp" +assert_contains "$FULL_NIGHTLY_WORKFLOW_FILE" "USERFRONT_E2E_CHROMIUM_DESKTOP_RESULT:" +assert_contains "$FULL_NIGHTLY_WORKFLOW_FILE" "USERFRONT_E2E_CHROMIUM_MOBILE_RESULT:" +assert_contains "$FULL_NIGHTLY_WORKFLOW_FILE" "USERFRONT_E2E_FIREFOX_DESKTOP_RESULT:" +assert_contains "$FULL_NIGHTLY_WORKFLOW_FILE" "USERFRONT_E2E_FIREFOX_MOBILE_RESULT:" +assert_contains "$FULL_NIGHTLY_WORKFLOW_FILE" "USERFRONT_E2E_WEBKIT_DESKTOP_RESULT:" +assert_contains "$FULL_NIGHTLY_WORKFLOW_FILE" "USERFRONT_E2E_WEBKIT_MOBILE_RESULT:" assert_contains "$FULL_NIGHTLY_WORKFLOW_FILE" "BADGE_UPDATE_CODE_CHECK: \"false\"" -assert_contains "$FULL_NIGHTLY_WORKFLOW_FILE" "npm test" +assert_contains "$FULL_NIGHTLY_WORKFLOW_FILE" "npx playwright test" echo "OK: Code Check badges are published to the badges branch" diff --git a/test/update_code_check_badges_package_coverage_test.sh b/test/update_code_check_badges_package_coverage_test.sh new file mode 100644 index 00000000..dc3dda46 --- /dev/null +++ b/test/update_code_check_badges_package_coverage_test.sh @@ -0,0 +1,144 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +WORK_DIR="$(mktemp -d)" +trap 'rm -rf "$WORK_DIR"' EXIT + +mkdir -p "$WORK_DIR/docs/badges" +mkdir -p "$WORK_DIR/badge-artifacts/userfront/reports" +mkdir -p "$WORK_DIR/badge-artifacts/adminfront/reports" +mkdir -p "$WORK_DIR/badge-artifacts/orgfront/reports" + +cat > "$WORK_DIR/docs/badges/badges.json" <<'JSON' +{ + "schemaVersion": 1, + "generatedBy": "scripts/update_code_check_badges.mjs", + "updatedAt": "2026-01-01T00:00:00.000Z", + "source": { + "branch": "dev", + "sha": "abc123456789", + "shortSha": "abc123456789", + "runId": "1", + "runNumber": "1" + }, + "badges": { + "userfront": { + "label": "userfront", + "message": "unknown", + "color": "#6e7781" + }, + "adminfront": { + "label": "adminfront", + "message": "10.00%", + "color": "#cf222e" + }, + "devfront": { + "label": "devfront", + "message": "20.00%", + "color": "#cf222e" + }, + "orgfront": { + "label": "orgfront", + "message": "30.00%", + "color": "#cf222e" + } + } +} +JSON + +cat > "$WORK_DIR/badge-artifacts/userfront/reports/vitest-coverage-summary.json" <<'JSON' +{ + "packages": [ + { + "package": "userfront", + "statements": 85.4 + } + ] +} +JSON + +cat > "$WORK_DIR/badge-artifacts/adminfront/reports/vitest-coverage-summary.json" <<'JSON' +{ + "packages": [ + { + "package": "adminfront", + "statements": 82.345 + } + ] +} +JSON + +cat > "$WORK_DIR/badge-artifacts/orgfront/reports/vitest-coverage-summary.json" <<'JSON' +{ + "packages": [ + { + "package": "orgfront", + "statements": 36.1 + } + ] +} +JSON + +run_badge_update() { + ( + cd "$WORK_DIR" + LINT_RESULT=success \ + BIOME_RESULT=success \ + BACKEND_RESULT=success \ + USERFRONT_RESULT=success \ + USERFRONT_E2E_RESULT=success \ + USERFRONT_E2E_CHROMIUM_DESKTOP_RESULT=success \ + USERFRONT_E2E_CHROMIUM_MOBILE_RESULT=failure \ + USERFRONT_E2E_FIREFOX_DESKTOP_RESULT=success \ + USERFRONT_E2E_FIREFOX_MOBILE_RESULT=skipped \ + USERFRONT_E2E_WEBKIT_DESKTOP_RESULT=failure \ + USERFRONT_E2E_WEBKIT_MOBILE_RESULT=success \ + ADMINFRONT_RESULT=success \ + DEVFRONT_RESULT=success \ + ORGFRONT_RESULT=success \ + USERFRONT_COVERAGE_RESULT=success \ + ADMINFRONT_COVERAGE_RESULT=success \ + DEVFRONT_COVERAGE_RESULT=failure \ + ORGFRONT_COVERAGE_RESULT=success \ + BADGE_SOURCE_BRANCH=dev \ + BADGE_SOURCE_SHA=abc123456789 \ + GITHUB_RUN_ID=2 \ + GITHUB_RUN_NUMBER=2 \ + node "$ROOT_DIR/scripts/update_code_check_badges.mjs" + ) +} + +run_badge_update + +node - "$WORK_DIR/docs/badges/badges.json" <<'NODE' +const fs = require("node:fs"); + +const manifest = JSON.parse(fs.readFileSync(process.argv[2], "utf8")); +const badges = manifest.badges; + +function assertEqual(actual, expected, message) { + if (actual !== expected) { + throw new Error(`${message}: expected ${expected}, got ${actual}`); + } +} + +assertEqual(badges["backend-tests"].label, "backend", "backend badge label must be compact"); +assertEqual(badges["backend-tests"].message, "pass", "backend test badge must use backend job result"); +assertEqual(badges.userfront.message, "pass | 85.40%", "userfront badge must combine fast E2E result and coverage"); +assertEqual(badges.adminfront.message, "pass | 82.34%", "adminfront badge must combine E2E result and coverage"); +assertEqual(badges.devfront.message, "pass | fail", "devfront badge must fail coverage independently"); +assertEqual(badges.orgfront.message, "pass | 36.10%", "orgfront badge must combine E2E result and coverage"); +assertEqual(badges["userfront-chrome"].label, "chrome", "chromium full badge label must name the browser"); +assertEqual(badges["userfront-chrome"].message, "pass | fail", "chromium full badge must show desktop and mobile results"); +assertEqual(badges["userfront-firefox"].label, "firefox", "firefox full badge label must name the browser"); +assertEqual(badges["userfront-firefox"].message, "pass | skip", "firefox full badge must show desktop and mobile results"); +assertEqual(badges["userfront-safari"].label, "safari", "webkit full badge label must be shown as safari"); +assertEqual(badges["userfront-safari"].message, "fail | pass", "webkit full badge must show desktop and mobile results"); +NODE + +cp "$WORK_DIR/docs/badges/badges.json" "$WORK_DIR/first-badges.json" +run_badge_update +cmp "$WORK_DIR/first-badges.json" "$WORK_DIR/docs/badges/badges.json" + +echo "OK: package coverage badges update independently and avoid rerun churn"