Skip to content

chore: drop what the icacls removal left behind #6

chore: drop what the icacls removal left behind

chore: drop what the icacls removal left behind #6

Workflow file for this run

name: Tests & Coverage
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:
jobs:
test:
name: Pytest With Coverage
runs-on: ubuntu-latest
# A hung decoder would otherwise hold a runner for GitHub's 6-hour default.
# The guarded runner below bounds the run itself; this bounds the job around it.
timeout-minutes: 20
# The floor and the ceiling. pyproject declares >=3.11 and classifies up to
# 3.13; running both is what turns that claim into evidence.
strategy:
fail-fast: false
matrix:
python-version: ['3.11', '3.13']
steps:
- name: Check out repository code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install uv
uses: astral-sh/setup-uv@v5
# --cov-fail-under=0 overrides the floor in pyproject FOR THIS JOB. The
# floor is a claim about the project and no single runner can execute the
# whole project: telegram_mcp.visual.capture is Win32 and cannot run here.
# The `coverage` job applies the floor once, to the combined data.
- name: Run tests with coverage
env:
TELEGRAM_API_ID: "12345"
TELEGRAM_API_HASH: "dummy_hash"
TELEGRAM_SESSION_NAME: "test_session"
COVERAGE_FILE: .coverage.linux-py${{ matrix.python-version }}
run: >
uv run python scripts/run_tests_guarded.py --
--cov --cov-report=term-missing --cov-fail-under=0
- name: Upload this leg's coverage data
uses: actions/upload-artifact@v4
with:
# Must be unique per matrix leg: upload-artifact@v4 refuses a second
# upload to the same name, so a static name fails the 3.13 job.
name: coverage-data-linux-py${{ matrix.python-version }}
path: .coverage.linux-py${{ matrix.python-version }}
# The data file starts with a dot, and v4 drops hidden files unless
# told otherwise - which would leave the combine step short a leg.
include-hidden-files: true
# The job above installs the base package, where the .tgs tests skip - which is
# also the proof that the base install works without the optional renderer. This
# job installs the extra so that code path is actually exercised somewhere.
# Run on Windows as well as Linux: rlottie-python is a NATIVE optional
# dependency, so "it installs and renders" is a per-platform fact, not a
# per-project one — and the visual half of this fork is Windows-first, which is
# exactly where an rlottie wheel failing to build would go unnoticed otherwise.
lottie:
name: Pytest With Lottie Renderer (${{ matrix.os }})
runs-on: ${{ matrix.os }}
# The native rlottie decoder is exactly the kind of child that wedges
# without exiting, so this job needs the ceiling more than the others.
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
steps:
- name: Check out repository code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: Fail if the lottie extra did not install
run: >
uv run --extra lottie python -c
"from telegram_mcp.visual.frames import lottie_available;
assert lottie_available(), 'rlottie-python is missing: the .tgs tests would silently skip'"
# The whole suite, not a file list. Selecting by path re-creates the exact
# hole the guard above exists to close: a renderer-gated test added to any
# unlisted file would run with the renderer nowhere and skip silently
# everywhere. Running everything under the extra cannot drift.
# Measured here too. This is the only job that runs on Windows, so it is
# the only place the Win32 capture module executes at all - and the only
# place the .tgs renderer does. Both belong in the combined total.
- name: Run the whole suite with the renderer available
env:
TELEGRAM_API_ID: "12345"
TELEGRAM_API_HASH: "dummy_hash"
TELEGRAM_SESSION_NAME: "test_session"
COVERAGE_FILE: .coverage.lottie-${{ matrix.os }}
run: >
uv run --extra lottie python scripts/run_tests_guarded.py --
-q --cov --cov-report= --cov-fail-under=0
- name: Upload this leg's coverage data
uses: actions/upload-artifact@v4
with:
name: coverage-data-lottie-${{ matrix.os }}
path: .coverage.lottie-${{ matrix.os }}
include-hidden-files: true
# The launchers are PowerShell and Windows-only, and nothing ran their tests
# until now - which is how `tests/test_update_menu.ps1` was able to outlive the
# script it tested. Discovery, not a list, for exactly that reason: a suite
# added here is picked up, and one whose subject is deleted fails loudly.
launchers:
name: PowerShell Launcher Tests
runs-on: windows-latest
timeout-minutes: 10
steps:
- name: Check out repository code
uses: actions/checkout@v4
# test_launchers.ps1 exercises the launcher's Python tee wrapper, and falls
# back to `python` on PATH when .venv is absent - which it is, on a runner.
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Run every PowerShell suite
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$suites = @(Get-ChildItem -Path tests -Filter 'test_*.ps1' -File | Sort-Object Name)
if ($suites.Count -eq 0) { throw 'No PowerShell suites found under tests/ - discovery is broken.' }
Write-Host "Found $($suites.Count) suite(s): $(($suites.Name) -join ', ')"
$failed = @()
foreach ($suite in $suites) {
Write-Host "::group::$($suite.Name)"
& pwsh -NoLogo -NoProfile -ExecutionPolicy Bypass -File $suite.FullName
$code = $LASTEXITCODE
Write-Host '::endgroup::'
if ($code -ne 0) { $failed += "$($suite.Name) (exit $code)" }
}
if ($failed.Count -gt 0) { throw "PowerShell suites failed: $($failed -join '; ')" }
Write-Host "All $($suites.Count) PowerShell suite(s) passed."
# The floor, applied once, to everything that ran.
#
# Neither runner can execute the whole project. telegram_mcp.visual.capture
# is Win32 (PrintWindow) and 110 of its lines cannot run on Linux; the POSIX
# branches of handles.py, owner_only.py and singleton.py cannot run on
# Windows. Measured on the first CI runs this repository ever had, the same
# commit read 85.49% on windows-latest and 81.90% on ubuntu-latest - so
# gating either number alone gates the runner, not the suite.
#
# No separate 'did every leg upload' check: a missing leg takes its platform's
# exclusive modules down to near zero, which puts the total under the floor
# and fails right here. The floor is its own guard.
coverage:
name: Combined Coverage Gate
runs-on: ubuntu-latest
needs: [test, lottie]
timeout-minutes: 10
steps:
# `coverage report` reads the source to say which lines were missed, so
# the checkout is load-bearing here, not habit.
- name: Check out repository code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.13'
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: Collect every leg's coverage data
uses: actions/download-artifact@v4
with:
pattern: coverage-data-*
path: coverage-data
merge-multiple: true
- name: Combine the platforms and apply the floor
run: |
uv run python -m coverage combine coverage-data
uv run python -m coverage report
uv run python -m coverage xml
- name: Upload the combined report
if: always()
uses: actions/upload-artifact@v4
with:
name: coverage-xml-combined
path: coverage.xml