Fix levenshtein_ratio_recent_history comparing snapshot to timestamp key #11916
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: ChangeDetection.io App Test | |
| # Triggers the workflow on push or pull request events | |
| on: [push, pull_request] | |
| jobs: | |
| lint-code: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Lint with Ruff | |
| run: | | |
| pip install ruff | |
| # Check for syntax errors and undefined names, and gettext misuse | |
| ruff check . --select E9,F63,F7,F82,INT | |
| # Complete check with errors treated as warnings | |
| ruff check . --exit-zero | |
| - name: Validate OpenAPI spec | |
| run: | | |
| pip install openapi-spec-validator | |
| python3 -c "from openapi_spec_validator import validate_spec; import yaml; validate_spec(yaml.safe_load(open('docs/api-spec.yaml')))" | |
| lint-translations: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Check .po files with msgfmt | |
| run: | | |
| sudo apt-get install -y gettext | |
| find changedetectionio/translations -name "*.po" | while read f; do | |
| echo "Checking $f" | |
| msgfmt --check-format -o /dev/null "$f" | |
| done | |
| - name: Lint .pot template with dennis | |
| run: | | |
| pip install "$(grep -E '^dennis ?>=' requirements.txt)" | |
| dennis-cmd lint --strict changedetectionio/translations/messages.pot | |
| - name: Lint .po files with dennis | |
| run: | | |
| dennis-cmd lint --strict --excluderules=W302 changedetectionio/translations/*/LC_MESSAGES/messages.po | |
| # W302 (unchanged) is excluded due to high false-positive rate in this codebase: | |
| # many msgstrs intentionally match msgid (units like "AI", "LLM", and proper nouns). | |
| - name: Check translation catalog is up-to-date | |
| run: | | |
| pip install "$(grep -E '^babel==' requirements.txt)" | |
| python setup.py extract_messages | |
| python setup.py update_catalog | |
| python setup.py compile_catalog | |
| # Ignore POT-Creation-Date timestamp lines — they change on every extract_messages run | |
| if git diff changedetectionio/translations | grep -v 'POT-Creation-Date' | grep -qE '^[+-][^+-]'; then | |
| echo "ERROR: Translation catalog is out of sync. Run: python setup.py extract_messages && python setup.py update_catalog && python setup.py compile_catalog" | |
| git diff --stat changedetectionio/translations | |
| exit 1 | |
| fi | |
| lint-template-i18n: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Check for fragmented gettext calls in templates | |
| run: | | |
| python3 << 'PYEOF' | |
| import re, sys | |
| from pathlib import Path | |
| # Detects adjacent {{ _(...) }} calls on the same line separated only by HTML | |
| # tags, whitespace, or non-translating Jinja2 variables — the anti-pattern of | |
| # splitting a single sentence across multiple msgids. | |
| # See https://github.com/dgtlmoon/changedetection.io/issues/4074 for background. | |
| # | |
| # The correct fix is to consolidate fragments into one entire-sentence msgid, | |
| # injecting dynamic values via %(name)s kwargs — per the GNU gettext manual | |
| # sections "Entire sentences" and "No string concatenation". See PR #4076 for | |
| # worked examples of each consolidation pattern. | |
| # | |
| # BASELINE: this limit reflects pre-existing violations present when this check | |
| # was introduced. It must only ever go DOWN. Each time you fix a template, lower | |
| # the limit by the number of lines fixed so the improvement is locked in. | |
| # When the count reaches 0, replace the baseline check with a hard sys.exit(1). | |
| BASELINE_LIMIT = 44 | |
| FRAGMENT_RE = re.compile( | |
| r'\{\{[^{}]*\b_\s*\([^)]*\)[^{}]*\}\}' | |
| r'(?:\s*(?:<[^>]+>|\{\{(?![^}]*\b_\s*\()[^}]*\}\})\s*)+' | |
| r'\{\{[^{}]*\b_\s*\([^)]*\)[^{}]*\}\}' | |
| ) | |
| violations = [] | |
| for f in sorted(Path('changedetectionio').rglob('*.html')): | |
| for lineno, line in enumerate(f.read_text().splitlines(), 1): | |
| if FRAGMENT_RE.search(line): | |
| violations.append(f"{f}:{lineno}: {line.strip()[:120]}") | |
| count = len(violations) | |
| print(f"Fragmented i18n calls found: {count} (limit: {BASELINE_LIMIT})") | |
| for v in violations: | |
| print(v) | |
| if count > BASELINE_LIMIT: | |
| print(f"\nERROR: {count} fragmented gettext calls exceed the baseline of {BASELINE_LIMIT}.") | |
| print("Consolidate adjacent _() calls into a single entire-sentence msgid.") | |
| print("See https://github.com/dgtlmoon/changedetection.io/issues/4074 for patterns.") | |
| sys.exit(1) | |
| PYEOF | |
| test-application-3-10: | |
| # Only run on push to master (including PR merges) | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/master' | |
| needs: [lint-code, lint-translations, lint-template-i18n] | |
| uses: ./.github/workflows/test-stack-reusable-workflow.yml | |
| with: | |
| python-version: '3.10' | |
| test-application-3-11: | |
| # Always run | |
| needs: [lint-code, lint-translations, lint-template-i18n] | |
| uses: ./.github/workflows/test-stack-reusable-workflow.yml | |
| with: | |
| python-version: '3.11' | |
| test-application-3-12: | |
| # Only run on push to master (including PR merges) | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/master' | |
| needs: [lint-code, lint-translations, lint-template-i18n] | |
| uses: ./.github/workflows/test-stack-reusable-workflow.yml | |
| with: | |
| python-version: '3.12' | |
| skip-pypuppeteer: true | |
| test-application-3-13: | |
| # Only run on push to master (including PR merges) | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/master' | |
| needs: [lint-code, lint-translations, lint-template-i18n] | |
| uses: ./.github/workflows/test-stack-reusable-workflow.yml | |
| with: | |
| python-version: '3.13' | |
| skip-pypuppeteer: true | |
| test-application-3-14: | |
| #if: github.event_name == 'push' && github.ref == 'refs/heads/master' | |
| needs: [lint-code, lint-translations, lint-template-i18n] | |
| uses: ./.github/workflows/test-stack-reusable-workflow.yml | |
| with: | |
| python-version: '3.14' | |
| skip-pypuppeteer: false |