dev v2.0.3 #130
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| pull_request: | |
| branches: ['*'] | |
| workflow_dispatch: | |
| jobs: | |
| backend-lint: | |
| name: Backend Lint | |
| runs-on: ubuntu-latest | |
| if: github.actor != 'dependabot[bot]' | |
| outputs: | |
| pylint: ${{ steps.pylint.outputs.passed }} | |
| ruff: ${{ steps.ruff.outputs.passed }} | |
| mypy: ${{ steps.mypy.outputs.passed }} | |
| pylint_output: ${{ steps.pylint.outputs.summary }} | |
| ruff_output: ${{ steps.ruff.outputs.summary }} | |
| mypy_output: ${{ steps.mypy.outputs.summary }} | |
| defaults: | |
| run: | |
| working-directory: backend | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.14' | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v5 | |
| - name: Cache uv dependencies | |
| uses: actions/cache@v5 | |
| with: | |
| path: ~/.cache/uv | |
| key: ${{ runner.os }}-uv-${{ hashFiles('backend/uv.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-uv- | |
| - name: Install dependencies | |
| run: uv sync --locked --all-extras | |
| - name: Run pylint | |
| id: pylint | |
| run: | | |
| if uv run pylint . > pylint-output.txt 2>&1; then | |
| echo "passed=true" >> $GITHUB_OUTPUT | |
| echo "summary=No issues" >> $GITHUB_OUTPUT | |
| else | |
| echo "passed=false" >> $GITHUB_OUTPUT | |
| count=$(grep -c ":" pylint-output.txt || echo "?") | |
| echo "summary=$count issues" >> $GITHUB_OUTPUT | |
| fi | |
| continue-on-error: true | |
| - name: Upload pylint output | |
| if: always() | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: pylint-output | |
| path: backend/pylint-output.txt | |
| retention-days: 1 | |
| - name: Run ruff | |
| id: ruff | |
| run: | | |
| if uv run ruff check . > ruff-output.txt 2>&1; then | |
| echo "passed=true" >> $GITHUB_OUTPUT | |
| echo "summary=No issues" >> $GITHUB_OUTPUT | |
| else | |
| echo "passed=false" >> $GITHUB_OUTPUT | |
| count=$(grep -c ":" ruff-output.txt || echo "?") | |
| echo "summary=$count issues" >> $GITHUB_OUTPUT | |
| fi | |
| continue-on-error: true | |
| - name: Upload ruff output | |
| if: always() | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: ruff-output | |
| path: backend/ruff-output.txt | |
| retention-days: 1 | |
| - name: Run mypy | |
| id: mypy | |
| run: | | |
| if uv run mypy . > mypy-output.txt 2>&1; then | |
| echo "passed=true" >> $GITHUB_OUTPUT | |
| echo "summary=No issues" >> $GITHUB_OUTPUT | |
| else | |
| echo "passed=false" >> $GITHUB_OUTPUT | |
| count=$(grep -c "error:" mypy-output.txt || echo "?") | |
| echo "summary=$count errors" >> $GITHUB_OUTPUT | |
| fi | |
| continue-on-error: true | |
| - name: Upload mypy output | |
| if: always() | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: mypy-output | |
| path: backend/mypy-output.txt | |
| retention-days: 1 | |
| frontend-eslint: | |
| name: ESLint (${{ matrix.app }}) | |
| runs-on: ubuntu-latest | |
| if: github.actor != 'dependabot[bot]' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| app: [admin-app, user-app] | |
| outputs: | |
| admin_passed: ${{ steps.result.outputs.admin_passed }} | |
| user_passed: ${{ steps.result.outputs.user_passed }} | |
| admin_summary: ${{ steps.result.outputs.admin_summary }} | |
| user_summary: ${{ steps.result.outputs.user_summary }} | |
| defaults: | |
| run: | |
| working-directory: frontend/${{ matrix.app }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '20' | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: latest | |
| - name: Get pnpm store directory | |
| id: pnpm-cache | |
| run: echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT | |
| - name: Setup pnpm cache | |
| uses: actions/cache@v5 | |
| with: | |
| path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }} | |
| key: ${{ runner.os }}-pnpm-${{ hashFiles('frontend/${{ matrix.app }}/pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pnpm- | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Run ESLint | |
| id: eslint | |
| run: | | |
| if pnpm run lint:eslint > eslint-output.txt 2>&1; then | |
| echo "passed=true" >> $GITHUB_OUTPUT | |
| echo "summary=No issues" >> $GITHUB_OUTPUT | |
| else | |
| echo "passed=false" >> $GITHUB_OUTPUT | |
| count=$(grep -c "^/" eslint-output.txt || echo "?") | |
| echo "summary=$count files with issues" >> $GITHUB_OUTPUT | |
| fi | |
| continue-on-error: true | |
| - name: Set matrix output | |
| id: result | |
| run: | | |
| if [ "${{ matrix.app }}" == "admin-app" ]; then | |
| echo "admin_passed=${{ steps.eslint.outputs.passed }}" >> $GITHUB_OUTPUT | |
| echo "admin_summary=${{ steps.eslint.outputs.summary }}" >> $GITHUB_OUTPUT | |
| else | |
| echo "user_passed=${{ steps.eslint.outputs.passed }}" >> $GITHUB_OUTPUT | |
| echo "user_summary=${{ steps.eslint.outputs.summary }}" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Upload eslint output | |
| if: always() | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: eslint-${{ matrix.app }}-output | |
| path: frontend/${{ matrix.app }}/eslint-output.txt | |
| retention-days: 1 | |
| frontend-typescript: | |
| name: TypeScript (${{ matrix.app }}) | |
| runs-on: ubuntu-latest | |
| if: github.actor != 'dependabot[bot]' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| app: [admin-app, user-app] | |
| outputs: | |
| admin_passed: ${{ steps.result.outputs.admin_passed }} | |
| user_passed: ${{ steps.result.outputs.user_passed }} | |
| admin_summary: ${{ steps.result.outputs.admin_summary }} | |
| user_summary: ${{ steps.result.outputs.user_summary }} | |
| defaults: | |
| run: | |
| working-directory: frontend/${{ matrix.app }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '20' | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: latest | |
| - name: Get pnpm store directory | |
| id: pnpm-cache | |
| run: echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT | |
| - name: Setup pnpm cache | |
| uses: actions/cache@v5 | |
| with: | |
| path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }} | |
| key: ${{ runner.os }}-pnpm-${{ hashFiles('frontend/${{ matrix.app }}/pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pnpm- | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Run TypeScript | |
| id: typescript | |
| run: | | |
| if pnpm run lint:types > typescript-output.txt 2>&1; then | |
| echo "passed=true" >> $GITHUB_OUTPUT | |
| echo "summary=No issues" >> $GITHUB_OUTPUT | |
| else | |
| echo "passed=false" >> $GITHUB_OUTPUT | |
| count=$(grep -c "error TS" typescript-output.txt || echo "?") | |
| echo "summary=$count errors" >> $GITHUB_OUTPUT | |
| fi | |
| continue-on-error: true | |
| - name: Set matrix output | |
| id: result | |
| run: | | |
| if [ "${{ matrix.app }}" == "admin-app" ]; then | |
| echo "admin_passed=${{ steps.typescript.outputs.passed }}" >> $GITHUB_OUTPUT | |
| echo "admin_summary=${{ steps.typescript.outputs.summary }}" >> $GITHUB_OUTPUT | |
| else | |
| echo "user_passed=${{ steps.typescript.outputs.passed }}" >> $GITHUB_OUTPUT | |
| echo "user_summary=${{ steps.typescript.outputs.summary }}" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Upload typescript output | |
| if: always() | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: typescript-${{ matrix.app }}-output | |
| path: frontend/${{ matrix.app }}/typescript-output.txt | |
| retention-days: 1 | |
| frontend-scss: | |
| name: SCSS (${{ matrix.app }}) | |
| runs-on: ubuntu-latest | |
| if: github.actor != 'dependabot[bot]' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| app: [admin-app, user-app] | |
| outputs: | |
| admin_passed: ${{ steps.result.outputs.admin_passed }} | |
| user_passed: ${{ steps.result.outputs.user_passed }} | |
| admin_summary: ${{ steps.result.outputs.admin_summary }} | |
| user_summary: ${{ steps.result.outputs.user_summary }} | |
| defaults: | |
| run: | |
| working-directory: frontend/${{ matrix.app }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '20' | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: latest | |
| - name: Get pnpm store directory | |
| id: pnpm-cache | |
| run: echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT | |
| - name: Setup pnpm cache | |
| uses: actions/cache@v5 | |
| with: | |
| path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }} | |
| key: ${{ runner.os }}-pnpm-${{ hashFiles('frontend/${{ matrix.app }}/pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pnpm- | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Run SCSS Lint | |
| id: scss | |
| run: | | |
| if pnpm run lint:scss > scss-output.txt 2>&1; then | |
| echo "passed=true" >> $GITHUB_OUTPUT | |
| echo "summary=No issues" >> $GITHUB_OUTPUT | |
| else | |
| echo "passed=false" >> $GITHUB_OUTPUT | |
| count=$(grep -c "✖" scss-output.txt || echo "?") | |
| echo "summary=$count issues" >> $GITHUB_OUTPUT | |
| fi | |
| continue-on-error: true | |
| - name: Set matrix output | |
| id: result | |
| run: | | |
| if [ "${{ matrix.app }}" == "admin-app" ]; then | |
| echo "admin_passed=${{ steps.scss.outputs.passed }}" >> $GITHUB_OUTPUT | |
| echo "admin_summary=${{ steps.scss.outputs.summary }}" >> $GITHUB_OUTPUT | |
| else | |
| echo "user_passed=${{ steps.scss.outputs.passed }}" >> $GITHUB_OUTPUT | |
| echo "user_summary=${{ steps.scss.outputs.summary }}" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Upload scss output | |
| if: always() | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: scss-${{ matrix.app }}-output | |
| path: frontend/${{ matrix.app }}/scss-output.txt | |
| retention-days: 1 | |
| summary: | |
| name: CI Summary | |
| runs-on: ubuntu-latest | |
| if: always() && github.event_name == 'pull_request' && github.actor != 'dependabot[bot]' | |
| needs: [backend-lint, frontend-eslint, frontend-typescript, frontend-scss] | |
| permissions: | |
| pull-requests: write | |
| contents: read | |
| steps: | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v7 | |
| with: | |
| path: artifacts | |
| - name: Generate Summary Comment | |
| id: summary | |
| run: | | |
| check_icon() { | |
| if [ "$1" == "true" ]; then | |
| echo "[x]" | |
| else | |
| echo "[ ]" | |
| fi | |
| } | |
| status_text() { | |
| if [ "$1" == "true" ]; then | |
| echo "Passed" | |
| else | |
| echo "Failed" | |
| fi | |
| } | |
| PYLINT="${{ needs.backend-lint.outputs.pylint }}" | |
| RUFF="${{ needs.backend-lint.outputs.ruff }}" | |
| MYPY="${{ needs.backend-lint.outputs.mypy }}" | |
| ESLINT_ADMIN="${{ needs.frontend-eslint.outputs.admin_passed }}" | |
| ESLINT_USER="${{ needs.frontend-eslint.outputs.user_passed }}" | |
| TS_ADMIN="${{ needs.frontend-typescript.outputs.admin_passed }}" | |
| TS_USER="${{ needs.frontend-typescript.outputs.user_passed }}" | |
| SCSS_ADMIN="${{ needs.frontend-scss.outputs.admin_passed }}" | |
| SCSS_USER="${{ needs.frontend-scss.outputs.user_passed }}" | |
| all_passed="true" | |
| for check in "$PYLINT" "$RUFF" "$MYPY" "$ESLINT_ADMIN" "$ESLINT_USER" "$TS_ADMIN" "$TS_USER" "$SCSS_ADMIN" "$SCSS_USER"; do | |
| if [ "$check" != "true" ]; then | |
| all_passed="false" | |
| break | |
| fi | |
| done | |
| { | |
| echo "## CI Results" | |
| echo "" | |
| echo "### Backend" | |
| echo "" | |
| echo "- $(check_icon $PYLINT) **Pylint** - $(status_text $PYLINT)" | |
| echo "- $(check_icon $RUFF) **Ruff** - $(status_text $RUFF)" | |
| echo "- $(check_icon $MYPY) **Mypy** - $(status_text $MYPY)" | |
| echo "" | |
| echo "### Frontend (user-app)" | |
| echo "" | |
| echo "- $(check_icon $ESLINT_USER) **ESLint** - $(status_text $ESLINT_USER)" | |
| echo "- $(check_icon $TS_USER) **TypeScript** - $(status_text $TS_USER)" | |
| echo "- $(check_icon $SCSS_USER) **SCSS** - $(status_text $SCSS_USER)" | |
| echo "" | |
| echo "### Frontend (admin-app)" | |
| echo "" | |
| echo "- $(check_icon $ESLINT_ADMIN) **ESLint** - $(status_text $ESLINT_ADMIN)" | |
| echo "- $(check_icon $TS_ADMIN) **TypeScript** - $(status_text $TS_ADMIN)" | |
| echo "- $(check_icon $SCSS_ADMIN) **SCSS** - $(status_text $SCSS_ADMIN)" | |
| echo "" | |
| if [ "$all_passed" == "true" ]; then | |
| echo "---" | |
| echo "" | |
| echo "**All checks passed.**" | |
| else | |
| echo "---" | |
| echo "" | |
| echo "<details><summary>View failed check outputs</summary>" | |
| echo "" | |
| if [ "$PYLINT" != "true" ] && [ -f "artifacts/pylint-output/pylint-output.txt" ]; then | |
| echo "#### Pylint" | |
| echo "" | |
| echo '```' | |
| head -50 artifacts/pylint-output/pylint-output.txt | |
| echo '```' | |
| echo "" | |
| fi | |
| if [ "$RUFF" != "true" ] && [ -f "artifacts/ruff-output/ruff-output.txt" ]; then | |
| echo "#### Ruff" | |
| echo "" | |
| echo '```' | |
| head -50 artifacts/ruff-output/ruff-output.txt | |
| echo '```' | |
| echo "" | |
| fi | |
| if [ "$MYPY" != "true" ] && [ -f "artifacts/mypy-output/mypy-output.txt" ]; then | |
| echo "#### Mypy" | |
| echo "" | |
| echo '```' | |
| head -50 artifacts/mypy-output/mypy-output.txt | |
| echo '```' | |
| echo "" | |
| fi | |
| if [ "$ESLINT_USER" != "true" ] && [ -f "artifacts/eslint-user-app-output/eslint-output.txt" ]; then | |
| echo "#### ESLint (user-app)" | |
| echo "" | |
| echo '```' | |
| head -50 artifacts/eslint-user-app-output/eslint-output.txt | |
| echo '```' | |
| echo "" | |
| fi | |
| if [ "$ESLINT_ADMIN" != "true" ] && [ -f "artifacts/eslint-admin-app-output/eslint-output.txt" ]; then | |
| echo "#### ESLint (admin-app)" | |
| echo "" | |
| echo '```' | |
| head -50 artifacts/eslint-admin-app-output/eslint-output.txt | |
| echo '```' | |
| echo "" | |
| fi | |
| if [ "$TS_USER" != "true" ] && [ -f "artifacts/typescript-user-app-output/typescript-output.txt" ]; then | |
| echo "#### TypeScript (user-app)" | |
| echo "" | |
| echo '```' | |
| head -50 artifacts/typescript-user-app-output/typescript-output.txt | |
| echo '```' | |
| echo "" | |
| fi | |
| if [ "$TS_ADMIN" != "true" ] && [ -f "artifacts/typescript-admin-app-output/typescript-output.txt" ]; then | |
| echo "#### TypeScript (admin-app)" | |
| echo "" | |
| echo '```' | |
| head -50 artifacts/typescript-admin-app-output/typescript-output.txt | |
| echo '```' | |
| echo "" | |
| fi | |
| if [ "$SCSS_USER" != "true" ] && [ -f "artifacts/scss-user-app-output/scss-output.txt" ]; then | |
| echo "#### SCSS (user-app)" | |
| echo "" | |
| echo '```' | |
| head -50 artifacts/scss-user-app-output/scss-output.txt | |
| echo '```' | |
| echo "" | |
| fi | |
| if [ "$SCSS_ADMIN" != "true" ] && [ -f "artifacts/scss-admin-app-output/scss-output.txt" ]; then | |
| echo "#### SCSS (admin-app)" | |
| echo "" | |
| echo '```' | |
| head -50 artifacts/scss-admin-app-output/scss-output.txt | |
| echo '```' | |
| echo "" | |
| fi | |
| echo "</details>" | |
| fi | |
| echo "" | |
| echo "<!-- ci-summary-marker -->" | |
| } > summary.md | |
| if [ "$all_passed" == "true" ]; then | |
| echo "all_passed=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "all_passed=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Post PR Comment | |
| uses: peter-evans/create-or-update-comment@v5 | |
| with: | |
| issue-number: ${{ github.event.pull_request.number }} | |
| body-path: summary.md | |
| - name: Set final status | |
| run: | | |
| if [ "${{ steps.summary.outputs.all_passed }}" != "true" ]; then | |
| echo "Some checks failed" | |
| exit 1 | |
| fi |