4 Commits

Author SHA1 Message Date
5891409fdb print real time
All checks were successful
Python package / lint (push) Successful in 6s
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Successful in 8s
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Successful in 5s
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to TestPyPI (push) Successful in 5s
2024-09-14 18:32:39 -06:00
0e9eba4900 add dry-run 2024-09-14 17:59:49 -06:00
e02737abd1 fix behavior with infinite retries
All checks were successful
Python package / lint (push) Successful in 7s
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Successful in 9s
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Successful in 5s
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to TestPyPI (push) Successful in 4s
2024-09-14 17:50:59 -06:00
4671cf5fac get rid of default config path 2024-09-14 17:46:39 -06:00
2 changed files with 43 additions and 24 deletions

View File

@ -6,6 +6,8 @@ Yes, I know Gotify doesn't have "goat" in its name but it sounds like it.
## Configuration ## Configuration
Configuration lives in a TOML file which should have (at a minimum) the following:
```toml ```toml
server = "https://gotify.example.com" server = "https://gotify.example.com"
app_token = "app_token_from_gotify" app_token = "app_token_from_gotify"
@ -15,7 +17,7 @@ app_token = "app_token_from_gotify"
## Usage ## Usage
``` ```
goat_monitor --config ./config.toml --retries -1 -- <COMMAND TO MONITOR> goat_monitor --config ./config.toml --retries 3 -- <COMMAND TO MONITOR>
``` ```
The command can be any shell command including arbitrarily many options / arguments. The command can be any shell command including arbitrarily many options / arguments.

View File

@ -17,7 +17,8 @@ from goat_monitor._version import __version__ # type: ignore
@click.argument("command", nargs=-1, required=False, type=str) @click.argument("command", nargs=-1, required=False, type=str)
@click.option( @click.option(
"--config", "--config",
default="~/.config/goat_monitor.toml", # default="~/.config/goat_monitor.toml",
required=True,
type=click.Path(path_type=Path), type=click.Path(path_type=Path),
help="Use a .toml configuration file", help="Use a .toml configuration file",
) )
@ -33,7 +34,13 @@ from goat_monitor._version import __version__ # type: ignore
default=False, default=False,
help="Print version and exit", help="Print version and exit",
) )
def wrap(command: List[str], config: Path, retries: int, version): @click.option(
"--dry-run",
is_flag=True,
default=False,
help="Run command without sending any notifications",
)
def wrap(command: List[str], config: Path, retries: int, version: bool, dry_run: bool):
"""Wrap an arbitrary command with gotify notifications""" """Wrap an arbitrary command with gotify notifications"""
if version: if version:
@ -45,6 +52,7 @@ def wrap(command: List[str], config: Path, retries: int, version):
settings = toml.load(f) settings = toml.load(f)
# gotify configuration # gotify configuration
if not dry_run:
url = settings["server"] url = settings["server"]
app_token = settings["app_token"] app_token = settings["app_token"]
with gotify.Gotify(base_url=url, app_token=app_token) as gotify_connection: with gotify.Gotify(base_url=url, app_token=app_token) as gotify_connection:
@ -58,22 +66,28 @@ def wrap(command: List[str], config: Path, retries: int, version):
if retries < 0: if retries < 0:
raise ValueError("Invalid number of retries specified") raise ValueError("Invalid number of retries specified")
for attempt in range(retries + 1): attempt = 0
while attempt <= retries:
# run the command # run the command
result = subprocess.run( with subprocess.Popen(
" ".join(command), " ".join(command),
shell=True, shell=True,
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
encoding="utf-8", ) as proc:
) stdout = ""
while True:
c = proc.stdout.read(1)
if c == b"":
break
s = c.decode("utf-8")
stdout += s
print(s, end="", flush=True)
return_code = proc.wait()
# TODO: print lines real time as the subprocess runs if return_code:
print(result.stdout, end="")
if result.returncode:
# failed # failed
title = f"Command failed with exit code {result.returncode}" title = f"Command failed with exit code {return_code}"
if attempt < retries: if attempt < retries:
title += f" - Retrying (attempt {attempt+1}/{retries})" title += f" - Retrying (attempt {attempt+1}/{retries})"
else: else:
@ -83,7 +97,7 @@ def wrap(command: List[str], config: Path, retries: int, version):
title = "Command succeeded" title = "Command succeeded"
MAX_LINES = 20 MAX_LINES = 20
lines = result.stdout.splitlines() lines = stdout.splitlines()
if len(lines) > MAX_LINES: if len(lines) > MAX_LINES:
lines = lines[-MAX_LINES:] lines = lines[-MAX_LINES:]
message = ( message = (
@ -93,14 +107,17 @@ def wrap(command: List[str], config: Path, retries: int, version):
+ "\n".join(lines) + "\n".join(lines)
) )
if not dry_run:
with gotify.Gotify(base_url=url, app_token=app_token) as gotify_connection: with gotify.Gotify(base_url=url, app_token=app_token) as gotify_connection:
gotify_connection.create_message(message=message, title=title) gotify_connection.create_message(message=message, title=title)
if not result.returncode: if not return_code:
# only retry on failure # only retry on failure
break break
sys.exit(result.returncode) attempt += 1
sys.exit(return_code)
# %% main # %% main