build: simplify dependency setup and CI workflows #14
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: PR Checks | |
| on: | |
| pull_request: | |
| branches: | |
| - main | |
| # Uncomment to enable Git Flow workflow | |
| # - develop | |
| # - feature/** | |
| # - release/** | |
| # - hotfix/** | |
| types: | |
| - opened | |
| - synchronize | |
| - reopened | |
| - edited | |
| - ready_for_review | |
| permissions: | |
| pull-requests: write | |
| contents: read | |
| issues: write | |
| statuses: write | |
| env: | |
| PYTHON_VERSION: "3.12" | |
| jobs: | |
| pr-title-check: | |
| name: Validate PR Title | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check PR title format | |
| uses: amannn/action-semantic-pull-request@v6 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| with: | |
| types: | | |
| feat | |
| fix | |
| docs | |
| style | |
| refactor | |
| perf | |
| test | |
| build | |
| ci | |
| chore | |
| requireScope: false | |
| pr-description-check: | |
| name: Validate PR Description | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check PR has description | |
| uses: actions/github-script@v8 | |
| with: | |
| script: | | |
| const pr = context.payload.pull_request; | |
| if (!pr.body || pr.body.length < 50) { | |
| core.setFailed('PR description is too short or missing. Please provide a detailed description.'); | |
| } | |
| lint: | |
| name: Lint & Format Code | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: Install uv and cache dependencies | |
| uses: astral-sh/setup-uv@v7 | |
| with: | |
| enable-cache: true | |
| cache-dependency-glob: | | |
| pyproject.toml | |
| uv.lock | |
| - name: Install project dependencies | |
| run: | | |
| uv lock | |
| uv sync | |
| - name: Run ruff linter | |
| run: uv run ruff check src/ tests/ | |
| - name: Run ruff formatter check | |
| run: uv run ruff format --check src/ tests/ | |
| mypy: | |
| name: Mypy Type Check | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: Install uv and cache dependencies | |
| uses: astral-sh/setup-uv@v7 | |
| with: | |
| enable-cache: true | |
| cache-dependency-glob: | | |
| pyproject.toml | |
| uv.lock | |
| - name: Install project dependencies | |
| run: | | |
| uv lock | |
| uv sync | |
| - name: Run mypy | |
| run: uv run mypy src/ | |
| test: | |
| name: Run Tests | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: Install uv and cache dependencies | |
| uses: astral-sh/setup-uv@v7 | |
| with: | |
| enable-cache: true | |
| cache-dependency-glob: | | |
| pyproject.toml | |
| uv.lock | |
| - name: Install project dependencies | |
| run: | | |
| uv lock | |
| uv sync | |
| - name: Run tests with coverage | |
| run: | | |
| mkdir -p .log/coverage | |
| uv run pytest | |
| - name: Upload coverage report as artifact | |
| if: always() | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: coverage-report-${{ env.PYTHON_VERSION }}-${{ github.run_id }} | |
| path: | | |
| .log/coverage/htmlcov | |
| .log/coverage/coverage.xml | |
| - name: Upload coverage reports to Codecov | |
| uses: codecov/codecov-action@v5 | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| override_pr: ${{ github.event.pull_request.number }} | |
| slug: ${{ github.repository }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| conflict-check: | |
| name: Check for Merge Conflicts | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Check for merge conflict markers | |
| run: | | |
| if git grep -l '<<<<<<< HEAD' -- ':!.github/' || \ | |
| git grep -l '>>>>>>> ' -- ':!.github/' || \ | |
| git grep -l '=======' -- ':!.github/' ; then | |
| echo "Merge conflict markers found!" | |
| exit 1 | |
| fi | |
| size-check: | |
| name: Check File Sizes | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Check for large files | |
| run: | | |
| large_files=$(find . -type f -size +10M -not -path "./.git/*") | |
| if [ -n "$large_files" ]; then | |
| echo "Large files detected (>10MB):" | |
| echo "$large_files" | |
| exit 1 | |
| fi | |
| pr-checks-summary: | |
| name: PR Checks Summary | |
| runs-on: ubuntu-latest | |
| needs: [pr-title-check, pr-description-check, lint, mypy, test, conflict-check, size-check] | |
| if: always() | |
| steps: | |
| - name: Check all jobs passed | |
| run: | | |
| if [ "${{ contains(join(fromjson(toJSON(needs.*.result)), ',') , 'failure') }}" == "true" ] || \ | |
| [ "${{ contains(join(fromjson(toJSON(needs.*.result)), ',') , 'cancelled') }}" == "true" ]; then | |
| echo "One or more required PR checks failed or were cancelled." | |
| exit 1 | |
| fi | |
| echo "All required PR checks passed! ✅" | |
| pr-labeler: | |
| name: Apply PR Labels | |
| runs-on: ubuntu-latest | |
| needs: pr-checks-summary | |
| continue-on-error: true | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Apply labels based on PR title | |
| uses: actions/github-script@v8 | |
| with: | |
| script: | | |
| const prTitle = context.payload.pull_request.title.toLowerCase(); | |
| const labels = []; | |
| if (prTitle.startsWith('feat:')) labels.push('feature'); | |
| else if (prTitle.startsWith('fix:')) labels.push('bug'); | |
| else if (prTitle.startsWith('docs:')) labels.push('documentation'); | |
| else if (prTitle.startsWith('style:')) labels.push('enhancement'); | |
| else if (prTitle.startsWith('refactor:')) labels.push('refactor'); | |
| else if (prTitle.startsWith('perf:')) labels.push('enhancement'); | |
| else if (prTitle.startsWith('test:')) labels.push('enhancement'); | |
| else if (prTitle.startsWith('build:')) labels.push('enhancement'); | |
| else if (prTitle.startsWith('ci:')) labels.push('ci'); | |
| else if (prTitle.startsWith('chore:')) labels.push('enhancement'); | |
| if (labels.length > 0) { | |
| core.info(`Applying labels: ${labels.join(', ')}`); | |
| await github.rest.issues.addLabels({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.payload.pull_request.number, | |
| labels: labels | |
| }); | |
| } |