#!/usr/bin/env python3 import base64 import os import pathlib import sys import winrm def require_env(name: str) -> str: value = os.getenv(name, "").strip() if not value: raise SystemExit(f"Missing required environment variable: {name}") return value def optional_env(name: str, default: str) -> str: value = os.getenv(name, "").strip() return value if value else default def ps_quote(value: str) -> str: return value.replace("'", "''") def main() -> int: host = require_env("KK_DEPLOY_HOST") port = optional_env("KK_DEPLOY_PORT", "5985") username = require_env("KK_DEPLOY_USERNAME") password = require_env("KK_DEPLOY_PASSWORD") deploy_root = optional_env("KK_DEPLOY_ROOT", r"C:\kkFileView-5.0") health_url = optional_env("KK_DEPLOY_HEALTH_URL", "http://127.0.0.1:8012/") artifact_name = optional_env("KK_DEPLOY_ARTIFACT_NAME", "kkfileview-server-jar") repository = require_env("GITHUB_REPOSITORY_NAME") run_id = require_env("GITHUB_RUN_ID_VALUE") github_token = require_env("GITHUB_TOKEN_VALUE") dry_run = optional_env("KK_DEPLOY_DRY_RUN", "false").lower() script_path = pathlib.Path(__file__).with_name("remote_windows_deploy.ps1") script_body = script_path.read_text(encoding="utf-8") bootstrap = f""" $Repository = '{ps_quote(repository)}' $RunId = '{ps_quote(run_id)}' $ArtifactName = '{ps_quote(artifact_name)}' $GitHubToken = '{ps_quote(github_token)}' $DeployRoot = '{ps_quote(deploy_root)}' $HealthUrl = '{ps_quote(health_url)}' $DryRun = '{ps_quote(dry_run)}' """ payload = (bootstrap + "\n" + script_body).encode("utf-8-sig") payload_b64 = base64.b64encode(payload).decode("ascii") endpoint = f"http://{host}:{port}/wsman" session = winrm.Session(endpoint, auth=(username, password), transport="ntlm") remote_b64_path = r"C:\Windows\Temp\kkfileview_deploy.b64" remote_ps1_path = r"C:\Windows\Temp\kkfileview_deploy.ps1" prep = session.run_ps( f""" $ErrorActionPreference = 'Stop' if (Test-Path '{ps_quote(remote_b64_path)}') {{ Remove-Item '{ps_quote(remote_b64_path)}' -Force }} if (Test-Path '{ps_quote(remote_ps1_path)}') {{ Remove-Item '{ps_quote(remote_ps1_path)}' -Force }} New-Item -ItemType File -Path '{ps_quote(remote_b64_path)}' -Force | Out-Null """ ) if prep.status_code != 0: sys.stderr.write(prep.std_err.decode("utf-8", errors="ignore")) return prep.status_code chunk_size = 1200 for start in range(0, len(payload_b64), chunk_size): chunk = payload_b64[start : start + chunk_size] append = session.run_ps( f"Add-Content -LiteralPath '{ps_quote(remote_b64_path)}' -Value '{chunk}'" ) if append.status_code != 0: sys.stderr.write(append.std_err.decode("utf-8", errors="ignore")) return append.status_code result = session.run_ps( f""" $ErrorActionPreference = 'Stop' $raw = Get-Content -LiteralPath '{ps_quote(remote_b64_path)}' -Raw [System.IO.File]::WriteAllBytes('{ps_quote(remote_ps1_path)}', [Convert]::FromBase64String($raw)) powershell -NoProfile -ExecutionPolicy Bypass -File '{ps_quote(remote_ps1_path)}' $code = $LASTEXITCODE Remove-Item '{ps_quote(remote_b64_path)}' -Force -ErrorAction SilentlyContinue Remove-Item '{ps_quote(remote_ps1_path)}' -Force -ErrorAction SilentlyContinue exit $code """ ) stdout = result.std_out.decode("utf-8", errors="ignore").strip() stderr = result.std_err.decode("utf-8", errors="ignore").strip() if stdout: print(stdout) if stderr: print(stderr, file=sys.stderr) return result.status_code if __name__ == "__main__": raise SystemExit(main())