Skip to content

ci: clean windows disk space in background #145311

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,11 @@ jobs:
cd src/ci/citool
CARGO_INCREMENTAL=0 CARGO_TARGET_DIR=../../../build/citool cargo build

- name: wait for Windows disk cleanup to finish
if: ${{ matrix.free_disk && startsWith(matrix.os, 'windows-') }}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

later, if we want to do the same for linux, we can remove startsWith(matrix.os, 'windows-')

run: |
python3 src/ci/scripts/free-disk-space-windows-wait.py

- name: run the build
run: |
set +e
Expand Down
72 changes: 72 additions & 0 deletions src/ci/scripts/free-disk-space-windows-start.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
"""
Start freeing disk space on Windows in the background by launching
the PowerShell cleanup script, and recording the PID in a file,
so later steps can wait for completion.
"""

import subprocess
from pathlib import Path
from free_disk_space_windows_util import get_pid_file, get_log_file, run_main


def get_cleanup_script() -> Path:
script_dir = Path(__file__).resolve().parent
cleanup_script = script_dir / "free-disk-space-windows.ps1"
if not cleanup_script.exists():
raise Exception(f"Cleanup script '{cleanup_script}' not found")
return cleanup_script


def write_pid(pid: int):
pid_file = get_pid_file()
if pid_file.exists():
raise Exception(f"Pid file '{pid_file}' already exists")
pid_file.write_text(str(pid))
print(f"wrote pid {pid} in file {pid_file}")


def launch_cleanup_process():
cleanup_script = get_cleanup_script()
log_file_path = get_log_file()
# Launch the PowerShell cleanup in the background and redirect logs.
try:
with open(log_file_path, "w", encoding="utf-8") as log_file:
proc = subprocess.Popen(
[
"pwsh",
# Suppress PowerShell startup banner/logo for cleaner logs.
"-NoLogo",
# Don't load user/system profiles. Ensures a clean, predictable environment.
"-NoProfile",
# Disable interactive prompts. Required for CI to avoid hangs.
"-NonInteractive",
# Execute the specified script file (next argument).
"-File",
str(cleanup_script),
],
# Write child stdout to the log file.
stdout=log_file,
# Merge stderr into stdout for a single, ordered log stream.
stderr=subprocess.STDOUT,
)
print(
f"Started free-disk-space cleanup in background. "
f"pid={proc.pid}; log_file={log_file_path}"
)
return proc
except FileNotFoundError as e:
raise Exception("pwsh not found on PATH; cannot start disk cleanup.") from e


def main() -> int:
proc = launch_cleanup_process()

# Write pid of the process to a file, so that later steps can read it and wait
# until the process completes.
write_pid(proc.pid)

return 0


if __name__ == "__main__":
run_main(main)
77 changes: 77 additions & 0 deletions src/ci/scripts/free-disk-space-windows-wait.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"""
Wait for the background Windows disk cleanup process.
"""

import ctypes
import time
from free_disk_space_windows_util import get_pid_file, get_log_file, run_main


def is_process_running(pid: int) -> bool:
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
processHandle = ctypes.windll.kernel32.OpenProcess(
PROCESS_QUERY_LIMITED_INFORMATION, 0, pid
)
if processHandle == 0:
# The process is not running.
# If you don't have the sufficient rights to check if a process is running,
# zero is also returned. But in GitHub Actions we have these rights.
return False
else:
ctypes.windll.kernel32.CloseHandle(processHandle)
return True


def print_logs():
"""Print the logs from the cleanup script."""
log_file = get_log_file()
if log_file.exists():
print("free-disk-space logs:")
# Print entire log; replace undecodable bytes to avoid exceptions.
try:
with open(log_file, "r", encoding="utf-8", errors="replace") as f:
print(f.read())
except Exception as e:
raise Exception(f"Failed to read log file '{log_file}'") from e
else:
print(f"::warning::Log file '{log_file}' not found")


def read_pid_from_file() -> int:
"""Read the PID from the pid file."""

pid_file = get_pid_file()
if not pid_file.exists():
raise Exception(
f"No background free-disk-space process to wait for: pid file {pid_file} not found"
)

pid_file_content = pid_file.read_text().strip()

# Delete the file if it exists
pid_file.unlink(missing_ok=True)

try:
# Read the first line and convert to int.
pid = int(pid_file_content.splitlines()[0])
return pid
except Exception as e:
raise Exception(
f"Error while parsing the pid file with content '{pid_file_content!r}'"
) from e


def main() -> int:
pid = read_pid_from_file()

# Poll until process exits
while is_process_running(pid):
time.sleep(3)

print_logs()

return 0


if __name__ == "__main__":
run_main(main)
2 changes: 1 addition & 1 deletion src/ci/scripts/free-disk-space.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ set -euo pipefail
script_dir=$(dirname "$0")

if [[ "${RUNNER_OS:-}" == "Windows" ]]; then
pwsh $script_dir/free-disk-space-windows.ps1
python3 "$script_dir/free-disk-space-windows-start.py"
else
$script_dir/free-disk-space-linux.sh
fi
29 changes: 29 additions & 0 deletions src/ci/scripts/free_disk_space_windows_util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""
Utilities for Windows disk space cleanup scripts.
"""

import os
from pathlib import Path
import sys


def get_temp_dir() -> Path:
"""Get the temporary directory set by GitHub Actions."""
return Path(os.environ.get("RUNNER_TEMP"))


def get_pid_file() -> Path:
return get_temp_dir() / "free-disk-space.pid"


def get_log_file() -> Path:
return get_temp_dir() / "free-disk-space.log"


def run_main(main_fn):
exit_code = 1
try:
exit_code = main_fn()
except Exception as e:
print(f"::error::{e}")
sys.exit(exit_code)
Loading