diff --git a/.gitea/workflows/lint.yml b/.gitea/workflows/lint.yml new file mode 100644 index 0000000..d3fdec2 --- /dev/null +++ b/.gitea/workflows/lint.yml @@ -0,0 +1,148 @@ +# PSScriptAnalyzer + Python lint — runs on every push/PR that touches code files. +# Requires PSScriptAnalyzer installed on the runner host: +# Install-Module PSScriptAnalyzer -Scope AllUsers -Force +# +# Failures block the PR. Fix warnings with: +# Invoke-ScriptAnalyzer -Path scripts\ -Recurse -Fix + +name: Lint + +on: + push: + paths: + - '**.ps1' + - '**.psm1' + - '**.py' + - 'pyproject.toml' + - '.gitea/workflows/lint.yml' + pull_request: + paths: + - '**.ps1' + - '**.psm1' + - '**.py' + - 'pyproject.toml' + - '.gitea/workflows/lint.yml' + +jobs: + pssa: + runs-on: windows-build + timeout-minutes: 10 + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Run PSScriptAnalyzer + shell: powershell + run: | + $ErrorActionPreference = 'Stop' + + if (-not (Get-Module -ListAvailable PSScriptAnalyzer)) { + Write-Host "Installing PSScriptAnalyzer..." + Install-Module PSScriptAnalyzer -Scope CurrentUser -Force -AllowClobber + } + + $settings = Join-Path $PWD 'PSScriptAnalyzerSettings.psd1' + $paths = @('scripts', 'template', 'Setup-Host.ps1') + $results = $paths | ForEach-Object { + if (Test-Path $_) { + Invoke-ScriptAnalyzer -Path $_ -Recurse -Settings $settings -Severity Error,Warning + } + } + + if ($results) { + $results | Format-Table -AutoSize + Write-Error "PSScriptAnalyzer found $($results.Count) issue(s). Fix before merging." + exit 1 + } + else { + Write-Host "PSScriptAnalyzer: no issues found." + } + + python: + runs-on: windows-build + timeout-minutes: 20 + + env: + PYTHONIOENCODING: utf-8 + VENV_DIR: F:\CI\python\venv + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Setup venv + install package + shell: powershell + run: | + $ErrorActionPreference = 'Stop' + + # Production venv lives at $env:VENV_DIR (F:\CI\python\venv per AGENTS.md). + # The system Python used to bootstrap it is configured once in + # runner/config.yaml as CI_PYTHON_LAUNCHER (single source of truth). + if (-not $env:CI_PYTHON_LAUNCHER) { + throw "CI_PYTHON_LAUNCHER not set. Configure it in runner/config.yaml (runner.envs) and restart act_runner." + } + if (-not (Test-Path $env:CI_PYTHON_LAUNCHER)) { + throw "CI_PYTHON_LAUNCHER points to '$env:CI_PYTHON_LAUNCHER' which does not exist." + } + + $venvPy = Join-Path $env:VENV_DIR 'Scripts\python.exe' + + # Probe: a broken venv (pyvenv.cfg pointing to a deleted base interpreter) + # writes "No Python at ..." to stderr; under $ErrorActionPreference='Stop' + # native stderr becomes a terminating exception (AGENTS.md error #12), + # so probe with SilentlyContinue + try/catch. + $venvOk = $false + if (Test-Path $venvPy) { + try { + $prevEAP = $ErrorActionPreference + $ErrorActionPreference = 'SilentlyContinue' + & $venvPy --version 2>&1 | Out-Null + if ($LASTEXITCODE -eq 0) { $venvOk = $true } + } + catch { $venvOk = $false } + finally { $ErrorActionPreference = $prevEAP } + } + + if (-not $venvOk) { + Write-Host "Venv missing or broken, (re)bootstrapping at $env:VENV_DIR with $env:CI_PYTHON_LAUNCHER" + if (Test-Path $env:VENV_DIR) { + Remove-Item $env:VENV_DIR -Recurse -Force + } + & $env:CI_PYTHON_LAUNCHER -m venv $env:VENV_DIR + if ($LASTEXITCODE -ne 0) { throw "venv creation failed" } + } + + Write-Host "Using venv python: $venvPy" + & $venvPy --version + if ($LASTEXITCODE -ne 0) { throw "python --version failed" } + + & $venvPy -m pip install --upgrade pip + if ($LASTEXITCODE -ne 0) { throw "pip upgrade failed" } + & $venvPy -m pip install -e ".[dev]" + if ($LASTEXITCODE -ne 0) { throw "pip install failed" } + + - name: ruff + shell: powershell + run: | + $venvPy = Join-Path $env:VENV_DIR 'Scripts\python.exe' + & $venvPy -m ruff check src tests/python + if ($LASTEXITCODE -ne 0) { throw "ruff failed" } + + - name: mypy --strict + shell: powershell + run: | + $venvPy = Join-Path $env:VENV_DIR 'Scripts\python.exe' + & $venvPy -m mypy --strict src + if ($LASTEXITCODE -ne 0) { throw "mypy failed" } + + - name: pytest (coverage >= 90%) + shell: powershell + run: | + $venvPy = Join-Path $env:VENV_DIR 'Scripts\python.exe' + & $venvPy -m pytest tests/python --cov=ci_orchestrator --cov-fail-under=90 + if ($LASTEXITCODE -ne 0) { throw "pytest failed" }