88 lines
2.3 KiB
Python
88 lines
2.3 KiB
Python
#!/usr/bin/env python3
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import subprocess
|
|
import sys
|
|
import shlex
|
|
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(description="Run a command until it fails")
|
|
parser.add_argument(
|
|
"-n",
|
|
"--tries",
|
|
type=int,
|
|
default=0,
|
|
help="Maximum number of attempts; 0 means keep going until failure",
|
|
)
|
|
parser.add_argument(
|
|
"-v",
|
|
"--verbose",
|
|
action="count",
|
|
default=0,
|
|
help="Increase progress output",
|
|
)
|
|
parser.add_argument(
|
|
"-q",
|
|
"--quiet",
|
|
action="count",
|
|
default=0,
|
|
help="Reduce extra progress output",
|
|
)
|
|
parser.add_argument(
|
|
"command",
|
|
nargs=argparse.REMAINDER,
|
|
help="Command to run, prefixed by -- if it has its own flags",
|
|
)
|
|
return parser.parse_args()
|
|
|
|
|
|
def progress_level(verbose: int, quiet: int) -> int:
|
|
return max(verbose - quiet, 0)
|
|
|
|
|
|
def print_progress(level: int, message: str, *, minimum_level: int = 0) -> None:
|
|
if level >= minimum_level:
|
|
print(message, file=sys.stderr, flush=True)
|
|
|
|
|
|
def main() -> int:
|
|
args = parse_args()
|
|
|
|
command = list(args.command)
|
|
if command[:1] == ["--"]:
|
|
command = command[1:]
|
|
if not command:
|
|
print("usage: plsfail [options] -- command [args...]", file=sys.stderr)
|
|
return 2
|
|
|
|
level = progress_level(args.verbose, args.quiet)
|
|
tries = args.tries
|
|
attempt = 0
|
|
|
|
while True:
|
|
attempt += 1
|
|
print_progress(level, f"try {attempt}: running {shlex.join(command)}")
|
|
result = subprocess.run(command, capture_output=True, text=True)
|
|
|
|
if result.returncode != 0:
|
|
print_progress(level, f"try {attempt}: failed with exit code {result.returncode}")
|
|
if result.stdout:
|
|
sys.stdout.write(result.stdout)
|
|
sys.stdout.flush()
|
|
if result.stderr:
|
|
sys.stderr.write(result.stderr)
|
|
sys.stderr.flush()
|
|
return result.returncode
|
|
|
|
print_progress(level, f"try {attempt}: ok")
|
|
|
|
if tries > 0 and attempt >= tries:
|
|
print_progress(level, f"completed {attempt} tries without failure")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|