diff --git a/.github/workflows/test-action.yml b/.github/workflows/test-action.yml index a0a4815..b4325d0 100644 --- a/.github/workflows/test-action.yml +++ b/.github/workflows/test-action.yml @@ -310,6 +310,67 @@ jobs: with: python-version: ${{ matrix.python-version }} + # Test that action fails when linting fails + test-failure-behavior: + name: Test Failure Behavior + runs-on: ubuntu-latest + permissions: + contents: write # Need write to test badge commits + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Create badly formatted Python file + run: | + mkdir -p test_project + cat > test_project/bad_code.py << 'EOF' + """Badly formatted Python file to test failure behavior.""" + + def badly_formatted( ): + x=1+2 + y = 3 + 4 + return x+y + + if __name__ == "__main__": + badly_formatted() + EOF + + - name: Run Python Linting (Should Fail) + id: linting + uses: ./ + with: + python-version: '3.11' + black_options: '--check' + generate-badges: 'true' + badges-directory: '.github/test-failure-badges' + update-readme: 'false' + continue-on-error: true + + - name: Verify action failed + run: | + if [ "${{ steps.linting.outcome }}" != "failure" ]; then + echo "❌ Error: Action should have failed but didn't" + echo "Outcome was: ${{ steps.linting.outcome }}" + exit 1 + fi + echo "✓ Action correctly failed when linting failed" + + - name: Verify badges were still generated + run: | + if [ ! -f .github/test-failure-badges/black.svg ]; then + echo "❌ Error: Badges not generated even though they should always be created" + exit 1 + fi + echo "✓ Badges were generated even though linting failed" + + # Verify the badge shows failure status (contains 'failing' text) + if ! grep -q "failing" .github/test-failure-badges/black.svg; then + echo "❌ Error: Badge does not indicate failure" + cat .github/test-failure-badges/black.svg + exit 1 + fi + echo "✓ Badge correctly shows failing status" + # Final summary test-summary: name: Test Summary @@ -324,6 +385,7 @@ jobs: - test-readme-update - test-update-badges-script - test-python-versions + - test-failure-behavior steps: - name: All tests passed run: | @@ -337,3 +399,5 @@ jobs: echo " ✓ README updates" echo " ✓ update_badges.py script" echo " ✓ Multiple Python versions (3.9-3.12)" + echo " ✓ Failure behavior (action fails when linting fails)" + echo " ✓ Badge generation on failure" diff --git a/README.md b/README.md index 1fd894a..5e6fc3b 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,23 @@ When `update-readme` is enabled, the action will automatically insert badge refe ![MyPy](.github/badges/mypy.svg) ``` +## Behavior + +### Failure Handling + +The action is designed to provide comprehensive linting feedback while maintaining proper CI/CD semantics: + +1. **All linting tools always run**: Even if one tool fails, the others will still execute to provide complete feedback +2. **Badges and README are always updated**: Badge generation and README updates happen regardless of linting results, ensuring status badges accurately reflect the current state +3. **The action fails if any linting fails**: After all tools run and badges are updated, the action will fail with a non-zero exit code if any linting tool reported issues +4. **Workflow stops on failure**: When the action fails, subsequent steps in your workflow will not execute (unless they use `if: always()` or `if: failure()`) + +This ensures that: +- You get complete linting feedback in one run +- Status badges are always up-to-date +- Your CI/CD pipeline correctly reports failures +- Failed linting prevents merges when using branch protection rules + ## Inputs | Input | Description | Required | Default | diff --git a/action.yml b/action.yml index 80e9b53..2f18cb4 100644 --- a/action.yml +++ b/action.yml @@ -81,7 +81,7 @@ runs: - name: Run Pylint run: | pylint ${{ inputs.pylint_options }} --reports=y --jobs=0 . 2>&1 | tee pylint_output.txt - echo "PYLINT_EXIT_CODE=$?" >> $GITHUB_ENV + echo "PYLINT_EXIT_CODE=${PIPESTATUS[0]}" >> $GITHUB_ENV shell: bash if: always() continue-on-error: true @@ -101,7 +101,7 @@ runs: - name: Run Black run: | black --check ${{ inputs.black_options }} . 2>&1 | tee black_output.txt - echo "BLACK_EXIT_CODE=$?" >> $GITHUB_ENV + echo "BLACK_EXIT_CODE=${PIPESTATUS[0]}" >> $GITHUB_ENV shell: bash if: always() continue-on-error: true @@ -121,7 +121,7 @@ runs: - name: Run MyPy run: | mypy ${{ inputs.mypy_options }} . 2>&1 | tee mypy_output.txt - echo "MYPY_EXIT_CODE=$?" >> $GITHUB_ENV + echo "MYPY_EXIT_CODE=${PIPESTATUS[0]}" >> $GITHUB_ENV shell: bash if: always() continue-on-error: true @@ -253,4 +253,41 @@ runs: shell: bash if: always() + - name: Check linting results and fail if needed + run: | + echo "Checking linting results..." + FAILED=0 + + if [ "${PYLINT_EXIT_CODE:-0}" != "0" ]; then + echo "❌ Pylint failed with exit code ${PYLINT_EXIT_CODE}" + FAILED=1 + else + echo "✓ Pylint passed" + fi + + if [ "${BLACK_EXIT_CODE:-0}" != "0" ]; then + echo "❌ Black failed with exit code ${BLACK_EXIT_CODE}" + FAILED=1 + else + echo "✓ Black passed" + fi + + if [ "${MYPY_EXIT_CODE:-0}" != "0" ]; then + echo "❌ MyPy failed with exit code ${MYPY_EXIT_CODE}" + FAILED=1 + else + echo "✓ MyPy passed" + fi + + if [ "$FAILED" -eq 1 ]; then + echo "" + echo "❌ Linting failed. Please fix the issues above." + exit 1 + else + echo "" + echo "✅ All linting checks passed!" + fi + shell: bash + if: always() +