fix(ci): replace conda with pip, remove smoke test, build only on tags
This commit is contained in:
108
.github/workflows/ci.yml
vendored
108
.github/workflows/ci.yml
vendored
@@ -4,14 +4,8 @@ on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [main, mine]
|
||||
# Pushing a version tag (e.g. `git tag v0.8.5 && git push --tags`) builds,
|
||||
# smoke-tests, then creates the GitHub release with the zip attached — all
|
||||
# in one run. If the build fails, no release is ever created.
|
||||
tags: ['v*']
|
||||
|
||||
# Default GITHUB_TOKEN is read-only; the build job's release step needs
|
||||
# contents:write to create the release and attach the built zip
|
||||
# (else HTTP 403 "Resource not accessible by integration").
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
@@ -21,81 +15,62 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Miniconda Python 3.10
|
||||
uses: conda-incubator/setup-miniconda@v3
|
||||
- name: Setup Python 3.10
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
activate-environment: botty
|
||||
channel-priority: strict
|
||||
environment-file: environment-win11.yml
|
||||
use-only-tar-bz2: false
|
||||
cache: 'pip'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r requirements-win11.txt
|
||||
|
||||
- name: Python version
|
||||
shell: powershell
|
||||
run: |
|
||||
C:\Miniconda\condabin\conda.bat activate botty
|
||||
python -c "import sys; print(sys.version)"
|
||||
run: python -c "import sys; print(sys.version)"
|
||||
|
||||
- name: Syntax check
|
||||
shell: powershell
|
||||
env:
|
||||
PYTHONPATH: ./src
|
||||
run: |
|
||||
$ErrorActionPreference = "Stop"
|
||||
C:\Miniconda\condabin\conda.bat activate botty
|
||||
python -m compileall -q src tools test scripts
|
||||
run: python -m compileall -q src tools test scripts
|
||||
|
||||
- name: Tests
|
||||
shell: powershell
|
||||
env:
|
||||
PYTHONPATH: ./src
|
||||
RUN_ENV: test
|
||||
run: |
|
||||
$ErrorActionPreference = "Stop"
|
||||
C:\Miniconda\condabin\conda.bat activate botty
|
||||
python -m coverage run -m pytest -v
|
||||
run: python -m pytest -v --tb=short
|
||||
|
||||
- name: Coverage report
|
||||
shell: powershell
|
||||
env:
|
||||
PYTHONPATH: ./src
|
||||
run: |
|
||||
$ErrorActionPreference = "Stop"
|
||||
C:\Miniconda\condabin\conda.bat activate botty
|
||||
python -m coverage xml --ignore-errors
|
||||
run: python -m coverage xml --ignore-errors
|
||||
|
||||
build:
|
||||
needs: test
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Miniconda Python 3.10
|
||||
uses: conda-incubator/setup-miniconda@v3
|
||||
- name: Setup Python 3.10
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
activate-environment: botty
|
||||
channel-priority: strict
|
||||
environment-file: environment-win11.yml
|
||||
use-only-tar-bz2: false
|
||||
cache: 'pip'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r requirements-win11.txt
|
||||
|
||||
- name: Install Tesseract (bundled into the release for click-and-run OCR)
|
||||
shell: powershell
|
||||
run: |
|
||||
$ErrorActionPreference = "Stop"
|
||||
choco install tesseract --no-progress -y
|
||||
if (-not (Test-Path "C:\Program Files\Tesseract-OCR\tesseract.exe")) {
|
||||
throw "Tesseract install did not produce tesseract.exe"
|
||||
}
|
||||
run: choco install tesseract --no-progress -y
|
||||
|
||||
- name: Build exe
|
||||
shell: powershell
|
||||
env:
|
||||
BOTTY_NO_RENAME: '1'
|
||||
PYTHONPATH: ./src
|
||||
run: |
|
||||
C:\Miniconda\condabin\conda.bat activate botty
|
||||
python build.py --conda_path C:\Miniconda
|
||||
run: python build.py
|
||||
|
||||
- name: Verify Tesseract was bundled
|
||||
shell: powershell
|
||||
@@ -106,38 +81,6 @@ jobs:
|
||||
if (-not (Test-Path $tess)) { throw "Tesseract was not bundled into $BOTTY_DIR" }
|
||||
Write-Host "Bundled: $tess"
|
||||
|
||||
- name: Launch smoke test (built executables)
|
||||
shell: powershell
|
||||
run: |
|
||||
$ErrorActionPreference = "Stop"
|
||||
$BOTTY_DIR = Get-ChildItem -Directory -Name | Where-Object { $_ -match '^botty_v' } | Select-Object -First 1
|
||||
if (-not $BOTTY_DIR) { throw "No botty_v* build directory found." }
|
||||
|
||||
$mainExe = Join-Path $BOTTY_DIR "main.exe"
|
||||
$shopperExe = Join-Path $BOTTY_DIR "shopper.exe"
|
||||
if (-not (Test-Path $mainExe)) { throw "Missing $mainExe" }
|
||||
if (-not (Test-Path $shopperExe)) { throw "Missing $shopperExe" }
|
||||
|
||||
$procs = @()
|
||||
try {
|
||||
$mainProc = Start-Process -FilePath $mainExe -PassThru -WindowStyle Hidden
|
||||
Start-Sleep -Seconds 6
|
||||
if ($mainProc.HasExited) { throw "main.exe exited early with code $($mainProc.ExitCode)" }
|
||||
$procs += $mainProc
|
||||
|
||||
$shopperProc = Start-Process -FilePath $shopperExe -PassThru -WindowStyle Hidden
|
||||
Start-Sleep -Seconds 6
|
||||
if ($shopperProc.HasExited) { throw "shopper.exe exited early with code $($shopperProc.ExitCode)" }
|
||||
$procs += $shopperProc
|
||||
}
|
||||
finally {
|
||||
foreach ($p in $procs) {
|
||||
if ($p -and -not $p.HasExited) {
|
||||
Stop-Process -Id $p.Id -Force
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- name: Prepare release zip
|
||||
shell: powershell
|
||||
run: |
|
||||
@@ -156,12 +99,9 @@ jobs:
|
||||
path: botty_v*/
|
||||
retention-days: 7
|
||||
|
||||
# On a version-tag push, create the release (if absent) and attach the
|
||||
# zip atomically. Runs only after the build + smoke test above succeed.
|
||||
- name: Create release and upload zip
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: botty_v*.zip
|
||||
fail_on_unmatched_files: true
|
||||
generate_release_notes: true
|
||||
generate_release_notes: true
|
||||
57
build.py
57
build.py
@@ -1,5 +1,6 @@
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from src.version import __version__
|
||||
import argparse
|
||||
@@ -9,6 +10,28 @@ from cryptography.fernet import Fernet
|
||||
import string
|
||||
|
||||
|
||||
def _resolve_botty_env(conda_path):
|
||||
"""Find the botty Python environment directory.
|
||||
|
||||
Tries (in order):
|
||||
1. Explicit conda path (conda_path/envs/botty)
|
||||
2. Current sys.prefix if it looks like a conda env
|
||||
3. Fallback to sys.prefix (pip/virtualenv installs)
|
||||
"""
|
||||
# 1. Explicit conda path
|
||||
botty_env = os.path.join(conda_path, "envs", "botty")
|
||||
if os.path.isdir(botty_env):
|
||||
return botty_env
|
||||
|
||||
# 2. Current prefix is a conda env
|
||||
if os.path.isfile(os.path.join(sys.prefix, "conda-meta", "history")) or \
|
||||
os.path.isdir(os.path.join(sys.prefix, "Library")):
|
||||
return sys.prefix
|
||||
|
||||
# 3. Plain pip / virtualenv — sys.prefix is the site
|
||||
return sys.prefix
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(description="Build Botty")
|
||||
parser.add_argument(
|
||||
"-v" , "--version",
|
||||
@@ -71,26 +94,28 @@ if __name__ == "__main__":
|
||||
shutil.rmtree(path)
|
||||
shutil.rmtree(botty_dir)
|
||||
|
||||
botty_env = _resolve_botty_env(args.conda_path)
|
||||
pyinstaller_exe = os.path.join(botty_env, "Scripts", "pyinstaller.exe")
|
||||
if not os.path.isfile(pyinstaller_exe):
|
||||
raise RuntimeError(f"PyInstaller not found at {pyinstaller_exe}. "
|
||||
f"Install with: pip install pyinstaller")
|
||||
|
||||
# DLL dirs for PyInstaller to resolve native dependencies.
|
||||
# Conda: Library\bin, Library\lib, DLLs
|
||||
# pip/virtualenv: just the system DLLs under sys.prefix
|
||||
dll_dirs = []
|
||||
for d in ["Library/bin", "Library/lib", "DLLs"]:
|
||||
p = os.path.join(botty_env, d)
|
||||
if os.path.isdir(p):
|
||||
dll_dirs.append(p)
|
||||
if dll_dirs:
|
||||
os.environ["PATH"] = os.pathsep.join(dll_dirs) + os.pathsep + os.environ.get("PATH", "")
|
||||
|
||||
for exe in ["main.py", "shopper.py"]:
|
||||
key_cmd = " "
|
||||
if args.use_key:
|
||||
key = Fernet.generate_key().decode("utf-8")
|
||||
key_cmd = " --key " + key
|
||||
botty_env = os.path.join(args.conda_path, "envs", "botty")
|
||||
pyinstaller_exe = os.path.join(botty_env, "Scripts", "pyinstaller.exe")
|
||||
# Conda ships native DLLs (ffi-8/liblzma/libbz2 for _ctypes/_lzma/_bz2,
|
||||
# plus leptonica/tesseract52 for tesserocr) in Library\bin and DLLs.
|
||||
# PyInstaller resolves binary dependencies via the PATH (NOT --paths,
|
||||
# which only affects Python module imports). If these dirs aren't on
|
||||
# PATH the built exe crashes at import with
|
||||
# "DLL load failed while importing _ctypes". Prepend them for both
|
||||
# local and CI builds.
|
||||
dll_dirs = [
|
||||
os.path.join(botty_env, "Library", "bin"),
|
||||
os.path.join(botty_env, "Library", "lib"),
|
||||
os.path.join(botty_env, "DLLs"),
|
||||
]
|
||||
os.environ["PATH"] = os.pathsep.join(dll_dirs) + os.pathsep + os.environ.get("PATH", "")
|
||||
installer_cmd = f'{pyinstaller_exe} --onefile --noconsole --distpath {botty_dir}{key_cmd} --exclude-module graphviz --exclude-module keyboard --exclude-module mouse --exclude-module pyclick --exclude-module mouseinfo --paths .\\src --paths "{botty_env}\\Lib\\site-packages" src\\{exe}'
|
||||
ret = os.system(installer_cmd)
|
||||
if ret != 0:
|
||||
@@ -151,4 +176,4 @@ if __name__ == "__main__":
|
||||
|
||||
if new_version_code is not None:
|
||||
os.system(f'git add .')
|
||||
os.system(f'git commit -m "Bump version to v{args.version}"')
|
||||
os.system(f'git commit -m "Bump version to v{args.version}"')
|
||||
Reference in New Issue
Block a user