diff --git a/.github/workflows/frontend-check-summary.yml b/.github/workflows/frontend-check-summary.yml new file mode 100644 index 000000000..1ce34170f --- /dev/null +++ b/.github/workflows/frontend-check-summary.yml @@ -0,0 +1,157 @@ +name: Frontend Check Summary Bot + +on: + pull_request_target: + branches: [main, master] + paths: + - "FrontEnd/**" + - ".github/workflows/frontend-check-summary.yml" + +permissions: + contents: read + pull-requests: write + issues: write + +jobs: + verify-and-summarize: + runs-on: ubuntu-latest + + defaults: + run: + working-directory: FrontEnd/my-app + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js environment + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + cache-dependency-path: FrontEnd/my-app/package-lock.json + + - name: Install dependencies + id: install + continue-on-error: true + run: npm ci + + - name: Run lint check + id: lint + continue-on-error: true + run: | + if [ "${{ steps.install.outcome }}" != "success" ]; then + echo "status=❌ Failed (dependency install)" >> "$GITHUB_OUTPUT" + echo "result=failure" >> "$GITHUB_OUTPUT" + exit 0 + fi + + if npm run lint; then + echo "status=✅ Passed" >> "$GITHUB_OUTPUT" + echo "result=success" >> "$GITHUB_OUTPUT" + else + echo "status=❌ Failed" >> "$GITHUB_OUTPUT" + echo "result=failure" >> "$GITHUB_OUTPUT" + fi + + - name: Run typecheck + id: tsc + continue-on-error: true + run: | + if [ "${{ steps.install.outcome }}" != "success" ]; then + echo "status=❌ Failed (dependency install)" >> "$GITHUB_OUTPUT" + echo "result=failure" >> "$GITHUB_OUTPUT" + exit 0 + fi + + if npm run typecheck; then + echo "status=✅ Passed" >> "$GITHUB_OUTPUT" + echo "result=success" >> "$GITHUB_OUTPUT" + else + echo "status=❌ Failed" >> "$GITHUB_OUTPUT" + echo "result=failure" >> "$GITHUB_OUTPUT" + fi + + - name: Run unit tests + id: test + continue-on-error: true + run: | + if [ "${{ steps.install.outcome }}" != "success" ]; then + echo "status=❌ Failed (dependency install)" >> "$GITHUB_OUTPUT" + echo "result=failure" >> "$GITHUB_OUTPUT" + exit 0 + fi + + if npm run test; then + echo "status=✅ Passed" >> "$GITHUB_OUTPUT" + echo "result=success" >> "$GITHUB_OUTPUT" + else + echo "status=❌ Failed" >> "$GITHUB_OUTPUT" + echo "result=failure" >> "$GITHUB_OUTPUT" + fi + + - name: Post or update PR summary comment + if: always() && github.event_name == 'pull_request' + uses: actions/github-script@v7 + env: + LINT_STATUS: ${{ steps.lint.outputs.status }} + TSC_STATUS: ${{ steps.tsc.outputs.status }} + TEST_STATUS: ${{ steps.test.outputs.status }} + with: + script: | + const lintStatus = process.env.LINT_STATUS || '⚪ Skipped'; + const tscStatus = process.env.TSC_STATUS || '⚪ Skipped'; + const testStatus = process.env.TEST_STATUS || '⚪ Skipped'; + const checks = [ + { name: 'Lint', status: lintStatus }, + { name: 'TypeScript', status: tscStatus }, + { name: 'Unit Tests', status: testStatus }, + ]; + + const failed = checks.filter((check) => check.status.includes('❌')); + const overall = failed.length === 0 ? '✅ Passed' : '❌ Failed'; + const body = [ + '### Frontend Verification Summary', + '', + `**Overall:** ${overall}`, + '', + ...checks.map((check) => `- ${check.name}: ${check.status}`), + '', + '> This comment is updated automatically after each run.', + ].join('\n'); + + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }); + + const marker = 'Frontend Verification Summary'; + const botComment = comments.find( + (comment) => comment.user.type === 'Bot' && comment.body.includes(marker) + ); + + if (botComment) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: botComment.id, + body, + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body, + }); + } + + + - name: Enforce overall check status + if: always() + run: | + if [ "${{ steps.lint.outputs.result }}" = "failure" ] || [ "${{ steps.tsc.outputs.result }}" = "failure" ] || [ "${{ steps.test.outputs.result }}" = "failure" ] || [ "${{ steps.install.outcome }}" = "failure" ]; then + echo "One or more frontend checks failed." + exit 1 + fi diff --git a/package-lock.json b/package-lock.json index f0dc1589c..3aa38218c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,8 @@ }, "devDependencies": { "lint-staged": "^15.5.2", - "prettier": "^3.8.3" + "prettier": "^3.8.3", + "typescript": "^6.0.3" }, "engines": { "node": ">=18" @@ -2388,6 +2389,20 @@ "license": "0BSD", "peer": true }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/use-intl": { "version": "4.13.0", "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-4.13.0.tgz", diff --git a/package.json b/package.json index e9e012eeb..90194d859 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,8 @@ }, "devDependencies": { "lint-staged": "^15.5.2", - "prettier": "^3.8.3" + "prettier": "^3.8.3", + "typescript": "^6.0.3" }, "dependencies": { "next-intl": "^4.13.0"