feat: harden install.bat + add CI checks for launcher scripts

install.bat:
- Add conda self-test (conda --version) before env create
- Verify botty python.exe exists after env creation
- Smoke-test key imports (cv2, tesserocr, discord, etc.)

CI (.github/workflows/ci.yml):
- Add test_setup_bat_files.py to test matrix

test/test_setup_bat_files.py (7 tests):
- All expected .bat files exist
- No hardcoded usernames (alex, alexpolo, ultimate) in run scripts
- No absolute home paths in run scripts (must use %~dp0 / %USERNAME%)
- All run_*.bat source find_python.bat (no duplicated conda detection)
- find_python.bat checks >= 6 conda locations
- install.bat has conda self-test and python verification
This commit is contained in:
alex
2026-05-23 23:15:24 +02:00
parent dec3269a0c
commit 40a207be9a
3 changed files with 167 additions and 1 deletions

View File

@@ -36,7 +36,7 @@ jobs:
run: |
C:\Miniconda\condabin\conda.bat activate botty
python -c "import sys; print(sys.version)"
coverage run -m pytest test/smoke_test.py test/nip/ test/game_stats_test.py test/closest_non_hud_test.py test/transmute/ -v -s
coverage run -m pytest test/smoke_test.py test/nip/ test/game_stats_test.py test/closest_non_hud_test.py test/transmute/ test/test_setup_bat_files.py -v -s
- name: Coverage
shell: powershell

View File

@@ -35,6 +35,20 @@ exit /b 1
echo Found conda: %CONDA_EXE%
echo.
:: --- Test conda works (catches broken base env, missing pywin32, etc.) ---
echo Testing conda...
"%CONDA_EXE%" --version >nul 2>&1
if %errorlevel% neq 0 (
echo.
echo ERROR: conda found but failed to run. Your conda installation may be broken.
echo Try re-installing Miniforge: https://github.com/conda-forge/miniforge
echo.
pause
exit /b 1
)
echo conda is working.
echo.
:: --- Create or update the botty env ---
"%CONDA_EXE%" env list | findstr /C:"botty" >nul 2>&1
if %errorlevel% equ 0 (
@@ -52,6 +66,43 @@ if %errorlevel% neq 0 (
exit /b 1
)
:: --- Verify botty env has Python ---
set "PYTHON="
for %%C in (
"%USERPROFILE%\miniforge3\envs\botty\python.exe"
"%USERPROFILE%\miniconda3\envs\botty\python.exe"
"%USERPROFILE%\anaconda3\envs\botty\python.exe"
"%ProgramData%\miniforge3\envs\botty\python.exe"
"%ProgramData%\miniconda3\envs\botty\python.exe"
"%ProgramData%\anaconda3\envs\botty\python.exe"
) do (
if exist %%C (
set "PYTHON=%%~C"
goto :env_created
)
)
echo.
echo ERROR: botty env was created but python.exe was not found.
echo This usually means the env build failed. Check output above.
pause
exit /b 1
:env_created
echo Botty Python: %PYTHON%
:: --- Smoke test: import key dependencies ---
echo.
echo Verifying dependencies...
"%PYTHON%" -c "import cv2, mss, numpy, tesserocr, discord, transitions, rapidfuzz" >nul 2>&1
if %errorlevel% neq 0 (
echo.
echo WARNING: Some imports failed. Try running install.bat again, or check:
echo development.md for manual troubleshooting.
) else (
echo All key dependencies verified.
)
echo.
echo ============================================
echo Installation complete!

View File

@@ -0,0 +1,115 @@
"""Test that launcher bat files are well-formed and portable.
Checks:
1. All expected .bat files exist
2. No hardcoded usernames or absolute paths (except conda paths)
3. find_python.bat is referenced by run_*.bat
"""
import os
import re
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
EXPECTED_BATS = [
"install.bat",
"find_python.bat",
"run_botty.bat",
"run_asset_extractor.bat",
"run_quest_debug.bat",
]
USERNAMES_TO_BLOCK = ["alex", "alexpolo", "ultimate"]
def _read(name):
return open(os.path.join(ROOT, name)).read()
def _bat_exists(name):
assert os.path.isfile(os.path.join(ROOT, name)), f"{name} is missing from repo root"
class TestBatFilesExist:
def test_all_bats_present(self):
for name in EXPECTED_BATS:
_bat_exists(name)
class TestNoHardcodedUsernames:
"""run_*.bat and find_python.bat must never contain a real username."""
_CHECKED = [
"find_python.bat",
"run_botty.bat",
"run_asset_extractor.bat",
"run_quest_debug.bat",
]
def test_no_hardcoded_usernames(self):
for name in self._CHECKED:
content = _read(name).lower()
for uname in USERNAMES_TO_BLOCK:
for line in content.split("\n"):
stripped = line.strip().lstrip(":").lstrip("*")
if uname in stripped and not stripped.startswith(":"):
raise AssertionError(
f"{name} contains hardcoded username '{uname}' on: "
f"{line.strip()}"
)
def test_no_absolute_home_paths(self):
for name in self._CHECKED:
content = _read(name)
# Find C:\Users\ followed by a literal username (not %USERNAME%)
bad = re.findall(r"C:\\Users\\([^%\s]+)", content)
bad = [b for b in bad if b != "%USERNAME%"]
assert not bad, f"{name} has hardcoded home path(s): {bad}"
class TestFindPythonUsage:
"""All run_*.bat should source find_python.bat."""
_RUN_BATS = [
"run_botty.bat",
"run_asset_extractor.bat",
"run_quest_debug.bat",
]
def test_run_bats_call_find_python(self):
for name in self._RUN_BATS:
content = _read(name)
assert "find_python.bat" in content, (
f"{name} does not call find_python.bat -- "
f"source the shared helper instead of duplicating conda detection"
)
def test_find_python_has_conda_locations(self):
content = _read("find_python.bat")
location_count = (
content.count("miniforge3")
+ content.count("miniconda3")
+ content.count("anaconda3")
)
assert location_count >= 6, (
f"find_python.bat only checks {location_count} conda locations, "
f"expected >= 6"
)
class TestInstallBat:
def test_conda_self_test(self):
content = _read("install.bat")
has_test = any(
kw in content
for kw in ["--version", "conda test", "testing conda", "test conda"]
)
assert has_test, (
"install.bat should verify conda works before creating the env"
)
def test_verifies_python_after_env_create(self):
content = _read("install.bat")
assert "python.exe" in content, (
"install.bat should verify botty env python.exe exists after env create"
)