Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .flake8
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[flake8]
max-line-length = 100
extend-ignore = E203, W503
exclude = venv, .git, __pycache__, build, dist, htmlcov
extend-ignore = E203, W503, E231
exclude = venv, .git, __pycache__, build, dist, htmlcov
80 changes: 37 additions & 43 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,78 +39,72 @@ jobs:

steps:
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Cache pip dependencies
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt

- name: Run tests with coverage
run: |
set -e
python -m pytest --cov=. --cov-report=xml --cov-report=term-missing --cov-report=html -v

- name: Run coverage tracker
run: |
set -e
python coverage_tracker.py
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Generate coverage summary
if: matrix.python-version == '3.11'
run: |
# Create coverage summary for job summary
echo "## 📊 Coverage Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY

# Get current coverage
CURRENT_COV=$(python -m pytest --cov=. --cov-report=term --quiet | grep TOTAL | awk '{print $4}' | sed 's/%//')

if [ -f .coverage_baseline.json ]; then
python -c "
import json
import subprocess
import re
import os

# Get current coverage
result = subprocess.run(['python', '-m', 'pytest', '--cov=.', '--cov-report=term-missing', '--quiet'], capture_output=True, text=True)
match = re.search(r'TOTAL.*?(\d+)%', result.stdout)
current_cov = float(match.group(1)) if match else 0

# Get baseline
try:
with open('.coverage_baseline.json') as f:
baseline = json.load(f)
baseline_cov = baseline['total_coverage']
except:
baseline_cov = current_cov

diff = current_cov - baseline_cov

with open(os.environ['GITHUB_STEP_SUMMARY'], 'a') as f:
f.write('\n## 📊 Coverage Summary\n\n')
f.write(f'- **Current Coverage**: {current_cov}%\n')
f.write(f'- **Baseline Coverage**: {baseline_cov}%\n')
f.write(f'- **Change**: {diff:+.1f}%\n\n')

if diff >= 0:
f.write('✅ Coverage maintained or improved\n')
elif diff >= -2:
f.write('📊 Coverage within acceptable tolerance (2%)\n')
else:
f.write('⚠️ Significant coverage decline detected\n')
"
# Get baseline coverage
BASELINE_COV=$(python -c "import json; print(json.load(open('.coverage_baseline.json'))['total_coverage'])")

# Calculate difference
DIFF=$(echo "$CURRENT_COV - $BASELINE_COV" | bc 2>/dev/null || echo "0")

echo "- **Current Coverage**: ${CURRENT_COV}%" >> $GITHUB_STEP_SUMMARY
echo "- **Baseline Coverage**: ${BASELINE_COV}%" >> $GITHUB_STEP_SUMMARY
echo "- **Change**: ${DIFF}%" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY

# Add status based on difference
if (( $(echo "$DIFF >= 0" | bc -l) )); then
echo "✅ Coverage maintained or improved" >> $GITHUB_STEP_SUMMARY
elif (( $(echo "$DIFF >= -2" | bc -l) )); then
echo "📊 Coverage within acceptable tolerance (2%)" >> $GITHUB_STEP_SUMMARY
else
echo "⚠️ Significant coverage decline detected" >> $GITHUB_STEP_SUMMARY
fi
else
echo "- **Current Coverage**: ${CURRENT_COV}%" >> $GITHUB_STEP_SUMMARY
echo "- **Baseline**: Not available" >> $GITHUB_STEP_SUMMARY
fi

- name: Upload coverage to Codecov
if: matrix.python-version == '3.11'
uses: codecov/codecov-action@v4
Expand All @@ -120,14 +114,14 @@ with open(os.environ['GITHUB_STEP_SUMMARY'], 'a') as f:
name: codecov-umbrella
fail_ci_if_error: false
continue-on-error: true

- name: Archive coverage reports
if: matrix.python-version == '3.11'
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: htmlcov/

- name: Upload coverage baseline
if: matrix.python-version == '3.11' && github.ref == 'refs/heads/main'
uses: actions/upload-artifact@v4
Expand Down
134 changes: 34 additions & 100 deletions .github/workflows/coverage-guard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
contents: read
pull-requests: write
issues: write

steps:
- uses: actions/checkout@v4
with:
Expand Down Expand Up @@ -54,115 +54,49 @@ jobs:
else
MAIN_COVERAGE="unknown"
fi

# Get current coverage from pytest output
CURRENT_COVERAGE=$(python -c "
import subprocess
result = subprocess.run(['python', '-m', 'pytest', '--cov=.', '--cov-report=term-missing', '--quiet'], capture_output=True, text=True)
import re
match = re.search(r'TOTAL.*?(\d+)%', result.stdout)
print(match.group(1) if match else '0')
")


# Get current coverage from the last pytest run
CURRENT_COVERAGE=$(python -m pytest --cov=. --cov-report=term --quiet | grep TOTAL | awk '{print $4}' | sed 's/%//')

echo "MAIN_COVERAGE=$MAIN_COVERAGE" >> $GITHUB_OUTPUT
echo "CURRENT_COVERAGE=$CURRENT_COVERAGE" >> $GITHUB_OUTPUT

# Create GitHub Job Summary using Python for comparisons

# Create GitHub Job Summary
echo "# 📊 Coverage Report" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY

if [ "$MAIN_COVERAGE" != "unknown" ]; then
python -c "
import os

main_cov = float('$MAIN_COVERAGE')
current_cov = float('$CURRENT_COVERAGE')
diff = current_cov - main_cov

with open(os.environ['GITHUB_STEP_SUMMARY'], 'w') as f:
f.write('# 📊 Coverage Report\n\n')
f.write('| Branch | Coverage | Change |\n')
f.write('|--------|----------|--------|\n')
f.write(f'| main | {main_cov}% | baseline |\n')
f.write(f'| ${{ github.head_ref }} | {current_cov}% | {diff:+.1f}% |\n\n')

if diff > 2:
f.write(f'✅ **Coverage improved significantly** by {diff:.1f}%\n')
elif diff > 0:
f.write(f'✅ **Coverage improved** by {diff:.1f}%\n')
elif diff >= -2:
f.write(f'📊 **Coverage maintained** ({diff:+.1f}% change, within 2% tolerance)\n\n')
f.write('> ✅ **Within tolerance** - small coverage changes are acceptable.\n')
else:
f.write(f'⚠️ **Coverage declined significantly** by {abs(diff):.1f}%\n\n')
f.write('> ❌ **Significant regression detected!** This exceeds the 2% tolerance.\n')
"
else
echo "# 📊 Coverage Report" >> $GITHUB_STEP_SUMMARY
echo "| Branch | Coverage | Change |" >> $GITHUB_STEP_SUMMARY
echo "|--------|----------|--------|" >> $GITHUB_STEP_SUMMARY
echo "| main | ${MAIN_COVERAGE}% | baseline |" >> $GITHUB_STEP_SUMMARY

# Calculate difference using bc or awk
DIFF=$(echo "$CURRENT_COVERAGE - $MAIN_COVERAGE" | bc 2>/dev/null || echo "$CURRENT_COVERAGE - $MAIN_COVERAGE" | awk '{print $1 - $3}')

echo "| PR | ${CURRENT_COVERAGE}% | ${DIFF}% |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY

# Add status message based on difference
if (( $(echo "$DIFF > 2" | bc -l) )); then
echo "✅ **Coverage improved significantly**" >> $GITHUB_STEP_SUMMARY
elif (( $(echo "$DIFF > 0" | bc -l) )); then
echo "✅ **Coverage improved**" >> $GITHUB_STEP_SUMMARY
elif (( $(echo "$DIFF >= -2" | bc -l) )); then
echo "📊 **Coverage maintained** (within 2% tolerance)" >> $GITHUB_STEP_SUMMARY
else
echo "⚠️ **Coverage declined significantly**" >> $GITHUB_STEP_SUMMARY
echo "> ❌ **Significant regression detected!** This exceeds the 2% tolerance." >> $GITHUB_STEP_SUMMARY
fi
else
echo "⚠️ **No baseline available** - this may be the first run." >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Current Coverage |" >> $GITHUB_STEP_SUMMARY
echo "|------------------|" >> $GITHUB_STEP_SUMMARY
echo "| ${CURRENT_COVERAGE}% |" >> $GITHUB_STEP_SUMMARY
fi

- name: Comment PR with coverage results
if: github.event_name == 'pull_request' && steps.coverage.outputs.MAIN_COVERAGE != 'unknown'
continue-on-error: true
uses: actions/github-script@v7
with:
script: |
try {
const mainCoverage = '${{ steps.coverage.outputs.MAIN_COVERAGE }}';
const currentCoverage = '${{ steps.coverage.outputs.CURRENT_COVERAGE }}';
const diff = parseFloat(currentCoverage) - parseFloat(mainCoverage);

console.log(`Coverage data: main=${mainCoverage}%, current=${currentCoverage}%, diff=${diff.toFixed(1)}%`);

let emoji, status, message;
if (diff > 2) {
emoji = "✅";
status = "improved significantly";
message = `Coverage ${status} by ${diff.toFixed(1)}%`;
} else if (diff > 0) {
emoji = "✅";
status = "improved";
message = `Coverage ${status} by ${diff.toFixed(1)}%`;
} else if (diff >= -2) {
emoji = "📊";
status = "maintained";
message = `Coverage ${status} (${diff.toFixed(1)}% change, within tolerance)`;
} else {
emoji = "⚠️";
status = "declined significantly";
message = `Coverage ${status} by ${Math.abs(diff).toFixed(1)}%`;
}

const body = `## ${emoji} Coverage Report

| Branch | Coverage |
|--------|----------|
| main | ${mainCoverage}% |
| ${context.payload.pull_request.head.ref} | ${currentCoverage}% |

**${message}** (${mainCoverage}% → ${currentCoverage}%)

${diff < -2 ? '❌ **Significant regression detected!** This exceeds the 2% tolerance.' : ''}${diff >= -2 && diff < 0 ? '✅ **Within tolerance** - small coverage changes are acceptable.' : ''}`;

console.log(`Attempting to comment on PR #${context.payload.pull_request.number}`);

const result = await github.rest.issues.createComment({
issue_number: context.payload.pull_request.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: body
});

console.log(`Comment created successfully: ${result.data.html_url}`);

} catch (error) {
console.error('Error creating PR comment:', error);
console.error('Context payload:', JSON.stringify(context.payload, null, 2));
// Don't fail the workflow if commenting fails
}
# PR commenting removed - use GitHub Actions summary instead
# The coverage report is available in the workflow summary

- name: Upload coverage reports
uses: codecov/codecov-action@v4
Expand All @@ -171,4 +105,4 @@ ${diff < -2 ? '❌ **Significant regression detected!** This exceeds the 2% tole
flags: unittests
name: pr-coverage
fail_ci_if_error: false
continue-on-error: true
continue-on-error: true
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ jobs:
name: codecov-umbrella
fail_ci_if_error: false
continue-on-error: true

- name: Upload coverage baseline
if: matrix.python-version == '3.11' && github.ref == 'refs/heads/main'
uses: actions/upload-artifact@v4
with:
name: coverage-baseline
path: .coverage_baseline.json
retention-days: 30
retention-days: 30
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ htmlcov/
.coverage_baseline.json
export_env.sh
.env
.env.local
.env.local
35 changes: 35 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- id: check-merge-conflict

# Python formatting
- repo: https://github.com/psf/black
rev: 23.7.0
hooks:
- id: black

# Import sorting
- repo: https://github.com/pycqa/isort
rev: 5.12.0
hooks:
- id: isort
args: ["--profile", "black"]

# Python linting
- repo: https://github.com/pycqa/flake8
rev: 6.0.0
hooks:
- id: flake8

# Type checking (optional but recommended)
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.5.1
hooks:
- id: mypy
additional_dependencies: [types-requests]
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ URL Watcher - A Python application that monitors URLs for changes and sends SMS
- Always run linting before committing: `flake8 . && black --check .`
- Use black for formatting: `black .`
- Max line length is configured to 100 characters
- SMS notifications require TEXTBELT_API_KEY and SMS_PHONE_NUMBER environment variables
- SMS notifications require TEXTBELT_API_KEY and SMS_PHONE_NUMBER environment variables
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ A simple Python tool that monitors websites for content changes and sends SMS no
```bash
# Single check
python url_watcher.py https://example.com

# Continuous monitoring
python url_watcher.py https://example.com --continuous

# With SMS alerts
python url_watcher.py https://example.com --sms
```
Expand All @@ -58,4 +58,4 @@ pytest

## License

MIT
MIT
Loading
Loading