1
0
forked from baron/baron-sso

코드체크 결과 README에 뱃지로 추가

This commit is contained in:
2026-05-29 17:15:13 +09:00
parent 5ddfc6c81b
commit 23cd316c23
26 changed files with 1096 additions and 350 deletions

View File

@@ -749,11 +749,11 @@ jobs:
userfront-e2e/test-results userfront-e2e/test-results
if-no-files-found: ignore if-no-files-found: ignore
front-vitest-coverage: adminfront-vitest-coverage:
needs: needs:
- changes - changes
- lint - 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 runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
@@ -777,10 +777,10 @@ jobs:
if [ "$install_exit_code" -ne 0 ]; then if [ "$install_exit_code" -ne 0 ]; then
{ {
echo "# Front Vitest Coverage Failure Report" echo "# Adminfront Vitest Coverage Failure Report"
echo echo
echo "- Workflow: \`${GITHUB_WORKFLOW:-Code Check}\`" echo "- Workflow: \`${GITHUB_WORKFLOW:-Code Check}\`"
echo "- Job: \`front-vitest-coverage\`" echo "- Job: \`adminfront-vitest-coverage\`"
echo "- Reason: \`Dependency install failed\`" echo "- Reason: \`Dependency install failed\`"
echo "- Exit Code: \`$install_exit_code\`" echo "- Exit Code: \`$install_exit_code\`"
echo echo
@@ -791,39 +791,37 @@ jobs:
echo '```text' echo '```text'
tail -n 200 reports/front-coverage-install.log tail -n 200 reports/front-coverage-install.log
echo '```' echo '```'
} > reports/front-vitest-coverage-failure-report.md } > reports/adminfront-vitest-coverage-failure-report.md
exit 1 exit 1
fi fi
for app in adminfront devfront orgfront; do set +e
set +e cd adminfront
cd "$app" pnpm install --no-frozen-lockfile --shamefully-hoist 2>&1 | tee -a ../reports/front-coverage-install.log
pnpm install --no-frozen-lockfile --shamefully-hoist 2>&1 | tee -a ../reports/front-coverage-install.log app_install_exit_code=${PIPESTATUS[0]}
app_install_exit_code=${PIPESTATUS[0]} cd ..
cd .. set -e
set -e
if [ "$app_install_exit_code" -ne 0 ]; then if [ "$app_install_exit_code" -ne 0 ]; then
{ {
echo "# Front Vitest Coverage Failure Report" echo "# Adminfront Vitest Coverage Failure Report"
echo echo
echo "- Workflow: \`${GITHUB_WORKFLOW:-Code Check}\`" echo "- Workflow: \`${GITHUB_WORKFLOW:-Code Check}\`"
echo "- Job: \`front-vitest-coverage\`" echo "- Job: \`adminfront-vitest-coverage\`"
echo "- Package: \`$app\`" echo "- Package: \`adminfront\`"
echo "- Reason: \`Dependency install failed\`" echo "- Reason: \`Dependency install failed\`"
echo "- Exit Code: \`$app_install_exit_code\`" echo "- Exit Code: \`$app_install_exit_code\`"
echo echo
echo "## Command" echo "## Command"
echo "\`cd $app && pnpm install --no-frozen-lockfile --shamefully-hoist\`" echo "\`cd adminfront && pnpm install --no-frozen-lockfile --shamefully-hoist\`"
echo echo
echo "## Install Log Tail (last 200 lines)" echo "## Install Log Tail (last 200 lines)"
echo '```text' echo '```text'
tail -n 200 reports/front-coverage-install.log tail -n 200 reports/front-coverage-install.log
echo '```' echo '```'
} > reports/front-vitest-coverage-failure-report.md } > reports/adminfront-vitest-coverage-failure-report.md
exit 1 exit 1
fi fi
done
- name: Run adminfront Vitest coverage - name: Run adminfront Vitest coverage
run: | run: |
@@ -836,10 +834,10 @@ jobs:
if [ "$test_exit_code" -ne 0 ]; then if [ "$test_exit_code" -ne 0 ]; then
{ {
echo "# Front Vitest Coverage Failure Report" echo "# Adminfront Vitest Coverage Failure Report"
echo echo
echo "- Workflow: \`${GITHUB_WORKFLOW:-Code Check}\`" echo "- Workflow: \`${GITHUB_WORKFLOW:-Code Check}\`"
echo "- Job: \`front-vitest-coverage\`" echo "- Job: \`adminfront-vitest-coverage\`"
echo "- Package: \`adminfront\`" echo "- Package: \`adminfront\`"
echo "- Exit Code: \`$test_exit_code\`" echo "- Exit Code: \`$test_exit_code\`"
echo echo
@@ -847,7 +845,108 @@ jobs:
echo '```text' echo '```text'
tail -n 200 reports/adminfront-vitest-coverage.log tail -n 200 reports/adminfront-vitest-coverage.log
echo '```' 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 exit 1
fi fi
@@ -862,10 +961,10 @@ jobs:
if [ "$test_exit_code" -ne 0 ]; then if [ "$test_exit_code" -ne 0 ]; then
{ {
echo "# Front Vitest Coverage Failure Report" echo "# Devfront Vitest Coverage Failure Report"
echo echo
echo "- Workflow: \`${GITHUB_WORKFLOW:-Code Check}\`" echo "- Workflow: \`${GITHUB_WORKFLOW:-Code Check}\`"
echo "- Job: \`front-vitest-coverage\`" echo "- Job: \`devfront-vitest-coverage\`"
echo "- Package: \`devfront\`" echo "- Package: \`devfront\`"
echo "- Exit Code: \`$test_exit_code\`" echo "- Exit Code: \`$test_exit_code\`"
echo echo
@@ -873,7 +972,108 @@ jobs:
echo '```text' echo '```text'
tail -n 200 reports/devfront-vitest-coverage.log tail -n 200 reports/devfront-vitest-coverage.log
echo '```' 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 exit 1
fi fi
@@ -888,10 +1088,10 @@ jobs:
if [ "$test_exit_code" -ne 0 ]; then if [ "$test_exit_code" -ne 0 ]; then
{ {
echo "# Front Vitest Coverage Failure Report" echo "# Orgfront Vitest Coverage Failure Report"
echo echo
echo "- Workflow: \`${GITHUB_WORKFLOW:-Code Check}\`" echo "- Workflow: \`${GITHUB_WORKFLOW:-Code Check}\`"
echo "- Job: \`front-vitest-coverage\`" echo "- Job: \`orgfront-vitest-coverage\`"
echo "- Package: \`orgfront\`" echo "- Package: \`orgfront\`"
echo "- Exit Code: \`$test_exit_code\`" echo "- Exit Code: \`$test_exit_code\`"
echo echo
@@ -899,38 +1099,34 @@ jobs:
echo '```text' echo '```text'
tail -n 200 reports/orgfront-vitest-coverage.log tail -n 200 reports/orgfront-vitest-coverage.log
echo '```' echo '```'
} > reports/front-vitest-coverage-failure-report.md } > reports/orgfront-vitest-coverage-failure-report.md
exit 1 exit 1
fi fi
- name: Generate Vitest coverage summary - name: Generate orgfront Vitest coverage summary
run: | run: |
node scripts/summarize_vitest_coverage.mjs node scripts/summarize_vitest_coverage.mjs orgfront
cat reports/vitest-coverage-summary.md >> "$GITHUB_STEP_SUMMARY" 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() }} if: ${{ failure() }}
run: | run: |
if [ -f reports/front-vitest-coverage-failure-report.md ]; then if [ -f reports/orgfront-vitest-coverage-failure-report.md ]; then
cat reports/front-vitest-coverage-failure-report.md >> "$GITHUB_STEP_SUMMARY" cat reports/orgfront-vitest-coverage-failure-report.md >> "$GITHUB_STEP_SUMMARY"
fi fi
- name: Upload front Vitest coverage report artifact - name: Upload orgfront Vitest coverage report artifact
if: ${{ always() }} if: ${{ always() }}
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
continue-on-error: true continue-on-error: true
with: with:
name: front-vitest-coverage-report name: orgfront-vitest-coverage-report
path: | path: |
reports/vitest-coverage-summary.md reports/vitest-coverage-summary.md
reports/vitest-coverage-summary.json 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/front-coverage-install.log
reports/adminfront-vitest-coverage.log
reports/devfront-vitest-coverage.log
reports/orgfront-vitest-coverage.log reports/orgfront-vitest-coverage.log
adminfront/coverage
devfront/coverage
orgfront/coverage orgfront/coverage
if-no-files-found: ignore if-no-files-found: ignore
@@ -1406,7 +1602,9 @@ jobs:
- backend-tests - backend-tests
- userfront-tests - userfront-tests
- userfront-e2e-tests - userfront-e2e-tests
- front-vitest-coverage - adminfront-vitest-coverage
- devfront-vitest-coverage
- orgfront-vitest-coverage
- adminfront-tests - adminfront-tests
- devfront-tests - devfront-tests
- orgfront-tests - orgfront-tests
@@ -1423,12 +1621,26 @@ jobs:
with: with:
node-version: "24" node-version: "24"
- name: Download Vitest coverage report artifact - name: Download adminfront Vitest coverage report artifact
uses: actions/download-artifact@v3 uses: actions/download-artifact@v3
continue-on-error: true continue-on-error: true
with: with:
name: front-vitest-coverage-report name: adminfront-vitest-coverage-report
path: badge-artifacts/front-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 - name: Update badge files
env: env:
@@ -1438,7 +1650,9 @@ jobs:
USERFRONT_RESULT: ${{ needs['userfront-tests'].result }} USERFRONT_RESULT: ${{ needs['userfront-tests'].result }}
USERFRONT_E2E_RESULT: ${{ needs['userfront-e2e-tests'].result }} USERFRONT_E2E_RESULT: ${{ needs['userfront-e2e-tests'].result }}
USERFRONT_E2E_FULL: ${{ github.event_name == 'workflow_dispatch' && inputs.run_userfront_e2e_full == true }} 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 }} ADMINFRONT_RESULT: ${{ needs['adminfront-tests'].result }}
DEVFRONT_RESULT: ${{ needs['devfront-tests'].result }} DEVFRONT_RESULT: ${{ needs['devfront-tests'].result }}
ORGFRONT_RESULT: ${{ needs['orgfront-tests'].result }} ORGFRONT_RESULT: ${{ needs['orgfront-tests'].result }}

View File

@@ -63,9 +63,9 @@ jobs:
git cat-file -e "refs/remotes/origin/badges:dev/${target_sha}/badges.json" 2>/dev/null; then git cat-file -e "refs/remotes/origin/badges:dev/${target_sha}/badges.json" 2>/dev/null; then
full_message="$( full_message="$(
git show "refs/remotes/origin/badges:dev/${target_sha}/badges.json" | 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" should_run="false"
reason="full-result-exists:${full_message}" reason="full-result-exists:${full_message}"
fi fi
@@ -85,6 +85,13 @@ jobs:
if: ${{ needs.lint.result == 'success' && needs.full-test-policy.outputs.should_run == 'true' }} if: ${{ needs.lint.result == 'success' && needs.full-test-policy.outputs.should_run == 'true' }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 80 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: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -124,9 +131,44 @@ jobs:
npx playwright install --with-deps npx playwright install --with-deps
- name: Run full userfront-e2e tests - name: Run full userfront-e2e tests
id: full-results
run: | run: |
mkdir -p reports
cd userfront-e2e 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 - name: Upload userfront-e2e full artifacts
if: ${{ always() }} if: ${{ always() }}
@@ -135,6 +177,7 @@ jobs:
with: with:
name: userfront-e2e-full-report name: userfront-e2e-full-report
path: | path: |
reports/userfront-e2e-full-*.log
userfront-e2e/playwright-report userfront-e2e/playwright-report
userfront-e2e/test-results userfront-e2e/test-results
if-no-files-found: ignore if-no-files-found: ignore
@@ -161,6 +204,12 @@ jobs:
env: env:
USERFRONT_E2E_RESULT: ${{ needs.userfront-e2e-full.result }} USERFRONT_E2E_RESULT: ${{ needs.userfront-e2e-full.result }}
USERFRONT_E2E_FULL: "true" 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_UPDATE_CODE_CHECK: "false"
BADGE_SOURCE_BRANCH: dev BADGE_SOURCE_BRANCH: dev
BADGE_SOURCE_SHA: ${{ github.sha }} BADGE_SOURCE_SHA: ${{ github.sha }}

View File

@@ -1,15 +1,10 @@
# Baron SSO # 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) [![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)
[![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) [![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)
[![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) [![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)
[![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)
badge는 `Code Check``badges` 브랜치의 `latest/``dev/<commit-sha>/`에 발행합니다. 최신 HTML/LCOV/JSON summary는 Gitea `Code Check``front-vitest-coverage-report` artifact에서 확인할 수 있습니다. badge는 `Code Check``badges` 브랜치의 `latest/``dev/<commit-sha>/`에 발행합니다. 최신 HTML/LCOV/JSON summary는 Gitea `Code Check`패키지별 `*-vitest-coverage-report` artifact에서 확인할 수 있습니다.
**Baron 로그인**은 화이트 라벨링된 가족사의 모든 소프트웨어 Auth를 총괄하는 사용자 인증/인가 허브입니다. **Baron 로그인**은 화이트 라벨링된 가족사의 모든 소프트웨어 Auth를 총괄하는 사용자 인증/인가 허브입니다.

View File

@@ -44,6 +44,23 @@ vi.mock("../../lib/i18n", () => ({
}, },
})); }));
vi.mock("../../../../common/core/components/audit", () => ({
AuditLogTable: ({
logs,
}: {
logs: Array<{ user_id: string; event_type: string }>;
}) => (
<div>
{logs.map((log) => (
<div key={`${log.user_id}-${log.event_type}`}>
<span>{log.user_id}</span>
<span>{log.event_type}</span>
</div>
))}
</div>
),
}));
const clientSummary = { const clientSummary = {
id: "client-a", id: "client-a",
name: "Console App", name: "Console App",

View File

@@ -43,6 +43,7 @@ export default defineConfig({
"../common/**/node_modules/**", "../common/**/node_modules/**",
"../common/.pnpm-store/**", "../common/.pnpm-store/**",
`${commonRoot}/theme/**`, `${commonRoot}/theme/**`,
`${commonRoot}/core/pagination/*.ts`,
`${commonRoot}/core/pagination/*.worker.ts`, `${commonRoot}/core/pagination/*.worker.ts`,
`${commonRoot}/core/query/queryClient.ts`, `${commonRoot}/core/query/queryClient.ts`,
], ],

View File

@@ -1,19 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="191" height="20" role="img" aria-label="adminfront coverage: 38.89%">
<title>adminfront coverage: 38.89%</title>
<linearGradient id="s" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<clipPath id="r"><rect width="191" height="20" rx="3" fill="#fff"/></clipPath>
<g clip-path="url(#r)">
<rect width="140" height="20" fill="#555"/>
<rect x="140" width="51" height="20" fill="#bf8700"/>
<rect width="191" height="20" fill="url(#s)"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="11">
<text x="70" y="15" fill="#010101" fill-opacity=".3">adminfront coverage</text>
<text x="70" y="14">adminfront coverage</text>
<text x="165.5" y="15" fill="#010101" fill-opacity=".3">38.89%</text>
<text x="165.5" y="14">38.89%</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1002 B

View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" width="136" height="20" role="img" aria-label="adminfront: unknown">
<title>adminfront: unknown</title>
<linearGradient id="s" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<clipPath id="r"><rect width="136" height="20" rx="3" fill="#fff"/></clipPath>
<g clip-path="url(#r)">
<rect width="78" height="20" fill="#555"/>
<rect x="78" width="58" height="20" fill="#6e7781"/>
<rect width="136" height="20" fill="url(#s)"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="11">
<text x="39" y="15" fill="#010101" fill-opacity=".3">adminfront</text>
<text x="39" y="14">adminfront</text>
<text x="107" y="15" fill="#010101" fill-opacity=".3">unknown</text>
<text x="107" y="14">unknown</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 964 B

View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" width="116" height="20" role="img" aria-label="backend: unknown">
<title>backend: unknown</title>
<linearGradient id="s" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<clipPath id="r"><rect width="116" height="20" rx="3" fill="#fff"/></clipPath>
<g clip-path="url(#r)">
<rect width="58" height="20" fill="#555"/>
<rect x="58" width="58" height="20" fill="#6e7781"/>
<rect width="116" height="20" fill="url(#s)"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="11">
<text x="29" y="15" fill="#010101" fill-opacity=".3">backend</text>
<text x="29" y="14">backend</text>
<text x="87" y="15" fill="#010101" fill-opacity=".3">unknown</text>
<text x="87" y="14">unknown</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 950 B

View File

@@ -1,8 +1,20 @@
{ {
"schemaVersion": 1, "schemaVersion": 1,
"generatedBy": "scripts/update_code_check_badges.mjs", "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": { "badges": {
"dev-sha": {
"label": "dev",
"message": "unknown",
"color": "#0969da"
},
"code-check": { "code-check": {
"label": "code check", "label": "code check",
"message": "failing", "message": "failing",
@@ -13,30 +25,45 @@
"message": "passing", "message": "passing",
"color": "#2ea043" "color": "#2ea043"
}, },
"userfront-e2e-fast": { "backend-tests": {
"label": "userfront e2e fast", "label": "backend",
"message": "failing",
"color": "#cf222e"
},
"userfront-e2e-full": {
"label": "userfront e2e full",
"message": "unknown", "message": "unknown",
"color": "#6e7781" "color": "#6e7781"
}, },
"adminfront-coverage": { "userfront": {
"label": "adminfront coverage", "label": "userfront",
"message": "38.89%", "message": "unknown",
"color": "#bf8700" "color": "#6e7781"
}, },
"devfront-coverage": { "adminfront": {
"label": "devfront coverage", "label": "adminfront",
"message": "8.87%", "message": "unknown",
"color": "#cf222e" "color": "#6e7781"
}, },
"orgfront-coverage": { "devfront": {
"label": "orgfront coverage", "label": "devfront",
"message": "37.50%", "message": "unknown",
"color": "#bf8700" "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"
} }
} }
} }

19
docs/badges/dev-sha.svg Normal file
View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" width="96" height="20" role="img" aria-label="dev: unknown">
<title>dev: unknown</title>
<linearGradient id="s" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<clipPath id="r"><rect width="96" height="20" rx="3" fill="#fff"/></clipPath>
<g clip-path="url(#r)">
<rect width="38" height="20" fill="#555"/>
<rect x="38" width="58" height="20" fill="#0969da"/>
<rect width="96" height="20" fill="url(#s)"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="11">
<text x="19" y="15" fill="#010101" fill-opacity=".3">dev</text>
<text x="19" y="14">dev</text>
<text x="67" y="15" fill="#010101" fill-opacity=".3">unknown</text>
<text x="67" y="14">unknown</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 931 B

View File

@@ -1,19 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="170" height="20" role="img" aria-label="devfront coverage: 8.87%">
<title>devfront coverage: 8.87%</title>
<linearGradient id="s" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<clipPath id="r"><rect width="170" height="20" rx="3" fill="#fff"/></clipPath>
<g clip-path="url(#r)">
<rect width="126" height="20" fill="#555"/>
<rect x="126" width="44" height="20" fill="#cf222e"/>
<rect width="170" height="20" fill="url(#s)"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="11">
<text x="63" y="15" fill="#010101" fill-opacity=".3">devfront coverage</text>
<text x="63" y="14">devfront coverage</text>
<text x="148" y="15" fill="#010101" fill-opacity=".3">8.87%</text>
<text x="148" y="14">8.87%</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 986 B

19
docs/badges/devfront.svg Normal file
View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" width="123" height="20" role="img" aria-label="devfront: unknown">
<title>devfront: unknown</title>
<linearGradient id="s" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<clipPath id="r"><rect width="123" height="20" rx="3" fill="#fff"/></clipPath>
<g clip-path="url(#r)">
<rect width="65" height="20" fill="#555"/>
<rect x="65" width="58" height="20" fill="#6e7781"/>
<rect width="123" height="20" fill="url(#s)"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="11">
<text x="32.5" y="15" fill="#010101" fill-opacity=".3">devfront</text>
<text x="32.5" y="14">devfront</text>
<text x="94" y="15" fill="#010101" fill-opacity=".3">unknown</text>
<text x="94" y="14">unknown</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 958 B

View File

@@ -1,19 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="177" height="20" role="img" aria-label="orgfront coverage: 37.50%">
<title>orgfront coverage: 37.50%</title>
<linearGradient id="s" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<clipPath id="r"><rect width="177" height="20" rx="3" fill="#fff"/></clipPath>
<g clip-path="url(#r)">
<rect width="126" height="20" fill="#555"/>
<rect x="126" width="51" height="20" fill="#bf8700"/>
<rect width="177" height="20" fill="url(#s)"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="11">
<text x="63" y="15" fill="#010101" fill-opacity=".3">orgfront coverage</text>
<text x="63" y="14">orgfront coverage</text>
<text x="151.5" y="15" fill="#010101" fill-opacity=".3">37.50%</text>
<text x="151.5" y="14">37.50%</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 994 B

19
docs/badges/orgfront.svg Normal file
View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" width="123" height="20" role="img" aria-label="orgfront: unknown">
<title>orgfront: unknown</title>
<linearGradient id="s" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<clipPath id="r"><rect width="123" height="20" rx="3" fill="#fff"/></clipPath>
<g clip-path="url(#r)">
<rect width="65" height="20" fill="#555"/>
<rect x="65" width="58" height="20" fill="#6e7781"/>
<rect width="123" height="20" fill="url(#s)"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="11">
<text x="32.5" y="15" fill="#010101" fill-opacity=".3">orgfront</text>
<text x="32.5" y="14">orgfront</text>
<text x="94" y="15" fill="#010101" fill-opacity=".3">unknown</text>
<text x="94" y="14">unknown</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 958 B

View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" width="109" height="20" role="img" aria-label="chrome: unknown">
<title>chrome: unknown</title>
<linearGradient id="s" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<clipPath id="r"><rect width="109" height="20" rx="3" fill="#fff"/></clipPath>
<g clip-path="url(#r)">
<rect width="51" height="20" fill="#555"/>
<rect x="51" width="58" height="20" fill="#6e7781"/>
<rect width="109" height="20" fill="url(#s)"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="11">
<text x="25.5" y="15" fill="#010101" fill-opacity=".3">chrome</text>
<text x="25.5" y="14">chrome</text>
<text x="80" y="15" fill="#010101" fill-opacity=".3">unknown</text>
<text x="80" y="14">unknown</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 950 B

View File

@@ -1,19 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="191" height="20" role="img" aria-label="userfront e2e fast: failing">
<title>userfront e2e fast: failing</title>
<linearGradient id="s" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<clipPath id="r"><rect width="191" height="20" rx="3" fill="#fff"/></clipPath>
<g clip-path="url(#r)">
<rect width="133" height="20" fill="#555"/>
<rect x="133" width="58" height="20" fill="#cf222e"/>
<rect width="191" height="20" fill="url(#s)"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="11">
<text x="66.5" y="15" fill="#010101" fill-opacity=".3">userfront e2e fast</text>
<text x="66.5" y="14">userfront e2e fast</text>
<text x="162" y="15" fill="#010101" fill-opacity=".3">failing</text>
<text x="162" y="14">failing</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1002 B

View File

@@ -1,19 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="191" height="20" role="img" aria-label="userfront e2e full: unknown">
<title>userfront e2e full: unknown</title>
<linearGradient id="s" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<clipPath id="r"><rect width="191" height="20" rx="3" fill="#fff"/></clipPath>
<g clip-path="url(#r)">
<rect width="133" height="20" fill="#555"/>
<rect x="133" width="58" height="20" fill="#6e7781"/>
<rect width="191" height="20" fill="url(#s)"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="11">
<text x="66.5" y="15" fill="#010101" fill-opacity=".3">userfront e2e full</text>
<text x="66.5" y="14">userfront e2e full</text>
<text x="162" y="15" fill="#010101" fill-opacity=".3">unknown</text>
<text x="162" y="14">unknown</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1002 B

View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" width="116" height="20" role="img" aria-label="firefox: unknown">
<title>firefox: unknown</title>
<linearGradient id="s" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<clipPath id="r"><rect width="116" height="20" rx="3" fill="#fff"/></clipPath>
<g clip-path="url(#r)">
<rect width="58" height="20" fill="#555"/>
<rect x="58" width="58" height="20" fill="#6e7781"/>
<rect width="116" height="20" fill="url(#s)"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="11">
<text x="29" y="15" fill="#010101" fill-opacity=".3">firefox</text>
<text x="29" y="14">firefox</text>
<text x="87" y="15" fill="#010101" fill-opacity=".3">unknown</text>
<text x="87" y="14">unknown</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 950 B

View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" width="109" height="20" role="img" aria-label="safari: unknown">
<title>safari: unknown</title>
<linearGradient id="s" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<clipPath id="r"><rect width="109" height="20" rx="3" fill="#fff"/></clipPath>
<g clip-path="url(#r)">
<rect width="51" height="20" fill="#555"/>
<rect x="51" width="58" height="20" fill="#6e7781"/>
<rect width="109" height="20" fill="url(#s)"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="11">
<text x="25.5" y="15" fill="#010101" fill-opacity=".3">safari</text>
<text x="25.5" y="14">safari</text>
<text x="80" y="15" fill="#010101" fill-opacity=".3">unknown</text>
<text x="80" y="14">unknown</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 950 B

19
docs/badges/userfront.svg Normal file
View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" width="130" height="20" role="img" aria-label="userfront: unknown">
<title>userfront: unknown</title>
<linearGradient id="s" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<clipPath id="r"><rect width="130" height="20" rx="3" fill="#fff"/></clipPath>
<g clip-path="url(#r)">
<rect width="72" height="20" fill="#555"/>
<rect x="72" width="58" height="20" fill="#6e7781"/>
<rect width="130" height="20" fill="url(#s)"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="11">
<text x="36" y="15" fill="#010101" fill-opacity=".3">userfront</text>
<text x="36" y="14">userfront</text>
<text x="101" y="15" fill="#010101" fill-opacity=".3">unknown</text>
<text x="101" y="14">unknown</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 960 B

View File

@@ -59,6 +59,6 @@
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"typescript": "^6.0.3", "typescript": "^6.0.3",
"vite": "^8.0.14", "vite": "^8.0.14",
"vitest": "^4.1.6" "vitest": "4.1.6"
} }
} }

View File

@@ -42,6 +42,14 @@ export default defineConfig({
"../common/**/node_modules/**", "../common/**/node_modules/**",
"../common/.pnpm-store/**", "../common/.pnpm-store/**",
`${commonRoot}/theme/**`, `${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/pagination/*.worker.ts`,
`${commonRoot}/core/query/queryClient.ts`, `${commonRoot}/core/query/queryClient.ts`,
], ],

View File

@@ -2,7 +2,9 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
import path from "node:path"; import path from "node:path";
const repoRoot = process.cwd(); 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) { function formatPct(value) {
return typeof value === "number" ? `${value.toFixed(2)}%` : "n/a"; return typeof value === "number" ? `${value.toFixed(2)}%` : "n/a";
@@ -63,7 +65,7 @@ function renderMarkdown(rows) {
} }
const rows = []; const rows = [];
for (const packageName of packages) { for (const packageName of targetPackages) {
rows.push(await readCoverageSummary(packageName)); rows.push(await readCoverageSummary(packageName));
} }

View File

@@ -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"; import path from "node:path";
const repoRoot = process.cwd(); const repoRoot = process.cwd();
@@ -6,78 +6,124 @@ const badgeDir = path.join(repoRoot, "docs", "badges");
const manifestPath = path.join(badgeDir, "badges.json"); const manifestPath = path.join(badgeDir, "badges.json");
const resultStyles = { const resultStyles = {
success: { message: "passing", color: "#2ea043" }, success: { message: "passing", color: "#2ea043" },
failure: { message: "failing", color: "#cf222e" }, failure: { message: "failing", color: "#cf222e" },
cancelled: { message: "cancelled", color: "#bf8700" }, cancelled: { message: "cancelled", color: "#bf8700" },
skipped: { message: "skipped", color: "#6e7781" }, skipped: { message: "skipped", color: "#6e7781" },
unknown: { message: "unknown", color: "#6e7781" }, unknown: { message: "unknown", color: "#6e7781" },
}; };
const badgeDefinitions = { const badgeDefinitions = {
"dev-sha": { label: "dev", message: "unknown", color: "#0969da" }, "dev-sha": { label: "dev", message: "unknown", color: "#0969da" },
"code-check": { label: "code check", message: "unknown", color: "#6e7781" }, "code-check": { label: "code check", message: "unknown", color: "#6e7781" },
biome: { label: "biome", message: "unknown", color: "#6e7781" }, biome: { label: "biome", message: "unknown", color: "#6e7781" },
"userfront-e2e-fast": { "backend-tests": {
label: "userfront e2e fast", label: "backend",
message: "unknown", message: "unknown",
color: "#6e7781", color: "#6e7781",
}, },
"userfront-e2e-full": { userfront: {
label: "userfront e2e full", label: "userfront",
message: "unknown", message: "unknown",
color: "#6e7781", color: "#6e7781",
}, },
"adminfront-coverage": { adminfront: {
label: "adminfront coverage", label: "adminfront",
message: "38.89%", message: "unknown",
color: "#bf8700", color: "#6e7781",
}, },
"devfront-coverage": { devfront: {
label: "devfront coverage", label: "devfront",
message: "8.87%", message: "unknown",
color: "#cf222e", color: "#6e7781",
}, },
"orgfront-coverage": { orgfront: {
label: "orgfront coverage", label: "orgfront",
message: "37.50%", message: "unknown",
color: "#bf8700", 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) { function normalizeResult(result) {
return resultStyles[result] ? result : "unknown"; return resultStyles[result] ? result : "unknown";
} }
function styleForResult(result) { 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) { function colorForCoverage(percent) {
if (percent >= 80) return "#2ea043"; if (percent >= 80) return "#2ea043";
if (percent >= 35) return "#bf8700"; if (percent >= 35) return "#bf8700";
return "#cf222e"; return "#cf222e";
} }
function escapeXml(value) { function escapeXml(value) {
return String(value) return String(value)
.replace(/&/g, "&amp;") .replace(/&/g, "&amp;")
.replace(/</g, "&lt;") .replace(/</g, "&lt;")
.replace(/>/g, "&gt;") .replace(/>/g, "&gt;")
.replace(/"/g, "&quot;"); .replace(/"/g, "&quot;");
} }
function textWidth(text) { 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 }) { function renderBadge({ label, message, color }) {
const labelWidth = textWidth(label); const labelWidth = textWidth(label);
const messageWidth = textWidth(message); const messageWidth = textWidth(message);
const width = labelWidth + messageWidth; const width = labelWidth + messageWidth;
const labelCenter = labelWidth / 2; const labelCenter = labelWidth / 2;
const messageCenter = labelWidth + messageWidth / 2; const messageCenter = labelWidth + messageWidth / 2;
return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="20" role="img" aria-label="${escapeXml(label)}: ${escapeXml(message)}"> return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="20" role="img" aria-label="${escapeXml(label)}: ${escapeXml(message)}">
<title>${escapeXml(label)}: ${escapeXml(message)}</title> <title>${escapeXml(label)}: ${escapeXml(message)}</title>
<linearGradient id="s" x2="0" y2="100%"> <linearGradient id="s" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/> <stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
@@ -100,151 +146,284 @@ function renderBadge({ label, message, color }) {
} }
async function readJsonIfExists(filePath) { async function readJsonIfExists(filePath) {
try { try {
return JSON.parse(await readFile(filePath, "utf8")); return JSON.parse(await readFile(filePath, "utf8"));
} catch { } catch {
return null; return null;
} }
} }
async function findCoverageSummary(directory) { async function findCoverageSummaries(directory) {
const entries = await readdir(directory, { withFileTypes: true }).catch( const entries = await readdir(directory, { withFileTypes: true }).catch(
() => [], () => [],
); );
const results = [];
for (const entry of entries) { for (const entry of entries) {
const entryPath = path.join(directory, entry.name); const entryPath = path.join(directory, entry.name);
if (entry.isFile() && entry.name === "vitest-coverage-summary.json") { if (entry.isFile() && entry.name === "vitest-coverage-summary.json") {
return entryPath; results.push(entryPath);
} continue;
if (entry.isDirectory()) { }
const found = await findCoverageSummary(entryPath); if (entry.isDirectory()) {
if (found) return found; results.push(...(await findCoverageSummaries(entryPath)));
} }
} }
return null; return results;
} }
function updateResultBadge(manifest, key, result) { function updateResultBadge(manifest, key, result) {
const style = styleForResult(result); const style = styleForResult(result);
manifest.badges[key] = { manifest.badges[key] = {
...(manifest.badges[key] ?? badgeDefinitions[key]), ...(manifest.badges[key] ?? badgeDefinitions[key]),
message: style.message, message: style.message,
color: style.color, color: style.color,
}; };
} }
function updateCoverageBadges(manifest, coverageSummary) { function updateCompactResultBadge(manifest, key, result) {
for (const row of coverageSummary.packages ?? []) { const style = styleForResult(result);
const key = `${row.package}-coverage`; manifest.badges[key] = {
if (!badgeDefinitions[key]) continue; ...(manifest.badges[key] ?? badgeDefinitions[key]),
const statements = Number(row.statements); label: badgeDefinitions[key]?.label ?? manifest.badges[key]?.label,
manifest.badges[key] = { message: compactResult(result),
...(manifest.badges[key] ?? badgeDefinitions[key]), color: style.color,
message: `${statements.toFixed(2)}%`, };
color: colorForCoverage(statements), }
};
} 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) { function shortSha(value) {
return String(value ?? "").trim().slice(0, 12); return String(value ?? "")
.trim()
.slice(0, 12);
} }
const existingManifest = process.env.RESET_BADGES === "true" const existingManifest =
? null process.env.RESET_BADGES === "true"
: await readJsonIfExists(manifestPath); ? null
const sourceSha = shortSha(process.env.BADGE_SOURCE_SHA || process.env.GITHUB_SHA); : await readJsonIfExists(manifestPath);
const sourceSha = shortSha(
process.env.BADGE_SOURCE_SHA || process.env.GITHUB_SHA,
);
const manifest = { const manifest = {
schemaVersion: 1, schemaVersion: 1,
generatedBy: "scripts/update_code_check_badges.mjs", generatedBy: "scripts/update_code_check_badges.mjs",
updatedAt: new Date().toISOString(), updatedAt: new Date().toISOString(),
source: { source: {
branch: process.env.BADGE_SOURCE_BRANCH || "dev", branch: process.env.BADGE_SOURCE_BRANCH || "dev",
sha: process.env.BADGE_SOURCE_SHA || process.env.GITHUB_SHA || null, sha: process.env.BADGE_SOURCE_SHA || process.env.GITHUB_SHA || null,
shortSha: sourceSha || null, shortSha: sourceSha || null,
runId: process.env.GITHUB_RUN_ID || null, runId: process.env.GITHUB_RUN_ID || null,
runNumber: process.env.GITHUB_RUN_NUMBER || null, runNumber: process.env.GITHUB_RUN_NUMBER || null,
}, },
badges: { badges: {
...badgeDefinitions, ...badgeDefinitions,
...(existingManifest?.badges ?? {}), ...(existingManifest?.badges ?? {}),
}, },
}; };
for (const key of deprecatedBadgeKeys) {
delete manifest.badges[key];
}
manifest.badges["dev-sha"] = { manifest.badges["dev-sha"] = {
...badgeDefinitions["dev-sha"], ...badgeDefinitions["dev-sha"],
message: sourceSha || "unknown", message: sourceSha || "unknown",
}; };
const jobResults = { const jobResults = {
lint: process.env.LINT_RESULT, lint: process.env.LINT_RESULT,
biome: process.env.BIOME_RESULT, biome: process.env.BIOME_RESULT,
backend: process.env.BACKEND_RESULT, backend: process.env.BACKEND_RESULT,
userfront: process.env.USERFRONT_RESULT, userfront: process.env.USERFRONT_RESULT,
userfrontE2e: process.env.USERFRONT_E2E_RESULT, userfrontE2e: process.env.USERFRONT_E2E_RESULT,
coverage: process.env.COVERAGE_RESULT, adminfront: process.env.ADMINFRONT_RESULT,
adminfront: process.env.ADMINFRONT_RESULT, devfront: process.env.DEVFRONT_RESULT,
devfront: process.env.DEVFRONT_RESULT, orgfront: process.env.ORGFRONT_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) => const hasFailure = overallResults.some((result) =>
["failure", "cancelled"].includes(result), ["failure", "cancelled"].includes(result),
); );
const allSkipped = overallResults.length > 0 && const allSkipped =
overallResults.every((result) => result === "skipped"); overallResults.length > 0 &&
overallResults.every((result) => result === "skipped");
if (process.env.BADGE_UPDATE_CODE_CHECK !== "false") { if (process.env.BADGE_UPDATE_CODE_CHECK !== "false") {
updateResultBadge( updateResultBadge(
manifest, manifest,
"code-check", "code-check",
overallResults.length === 0 overallResults.length === 0
? "unknown" ? "unknown"
: hasFailure : hasFailure
? "failure" ? "failure"
: allSkipped : allSkipped
? "skipped" ? "skipped"
: "success", : "success",
); );
} }
updateResultBadge(manifest, "biome", jobResults.biome); updateResultBadge(manifest, "biome", jobResults.biome);
updateCompactResultBadge(manifest, "backend-tests", jobResults.backend);
const e2eWasFull = process.env.USERFRONT_E2E_FULL === "true"; const coverageSummaries = process.env.COVERAGE_SUMMARY_PATH
if (jobResults.userfrontE2e && jobResults.userfrontE2e !== "skipped") { ? [process.env.COVERAGE_SUMMARY_PATH]
updateResultBadge( : await findCoverageSummaries(path.join(repoRoot, "badge-artifacts"));
manifest, const coverageByPackage = new Map();
e2eWasFull ? "userfront-e2e-full" : "userfront-e2e-fast", for (const summaryPath of coverageSummaries) {
jobResults.userfrontE2e, 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, testResult, coverageResult] of [
for (const key of [ ["userfront", userfrontFastResult, coverageJobResults.userfront],
"adminfront-coverage", ["adminfront", jobResults.adminfront, coverageJobResults.adminfront],
"devfront-coverage", ["devfront", jobResults.devfront, coverageJobResults.devfront],
"orgfront-coverage", ["orgfront", jobResults.orgfront, coverageJobResults.orgfront],
]) { ]) {
updateResultBadge(manifest, key, "failure"); if (testResult || coverageResult) {
} updatePackageBadge(
} else { manifest,
const coverageSummaryPath = process.env.COVERAGE_SUMMARY_PATH || key,
(await findCoverageSummary(path.join(repoRoot, "badge-artifacts"))); testResult,
const coverageSummary = coverageSummaryPath coverageResult,
? await readJsonIfExists(coverageSummaryPath) coverageByPackage.get(key),
: null; );
if (coverageSummary) { }
updateCoverageBadges(manifest, coverageSummary); }
}
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 mkdir(badgeDir, { recursive: true });
await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`); await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
for (const [key, badge] of Object.entries(manifest.badges)) { 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.`); console.log(`Updated ${Object.keys(manifest.badges).length} badge files.`);

View File

@@ -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_SOURCE_SHA: ${{ github.sha }}'
assert_contains "$WORKFLOW_FILE" 'BADGE_LATEST_DIR="${BADGE_WORKTREE}/latest"' 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" '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 if grep -Eq "^[[:space:]]+git push$" "$WORKFLOW_FILE"; then
fail "Code Check workflow must not push back to the current branch" fail "Code Check workflow must not push back to the current branch"
fi 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/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/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/backend-tests.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/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_not_contains "$README_FILE" "](docs/badges/"
assert_contains "$FULL_NIGHTLY_WORKFLOW_FILE" "cron: \"0 18 * * *\"" 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" "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" "full-result-exists:\${full_message}"
assert_contains "$FULL_NIGHTLY_WORKFLOW_FILE" "USERFRONT_E2E_FULL: \"true\"" 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" "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" echo "OK: Code Check badges are published to the badges branch"

View File

@@ -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"