Files
playground/launchd_plist.py
2023-02-18 07:39:08 +01:00

85 lines
2.3 KiB
Python
Executable File

#!/usr/bin/env python3
import argparse
import logging
import plistlib
import sys
import typing
from pathlib import Path
def parse_args(argv: list[str]) -> typing.Tuple[argparse.Namespace, list[str]]:
def parse_env(env: str) -> typing.Tuple[str, str]:
return env.split("=", maxsplit=1)
def parse_path(path: str) -> Path:
return Path(path).expanduser().resolve()
arger = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
arger.add_argument(
"-e", "--env", dest="env", metavar="ENVVAR", type=parse_env, nargs="+", help="Environment variables as K=V list"
)
arger.add_argument("-w", "--cwd", dest="cwd", default=Path.cwd(), type=parse_path, help="Working directory")
arger.add_argument("-l", "--label", required=True, help="Label")
arger.add_argument(
"--install",
action="store_true",
help="Install the plist to ~/Library/LaunchAgents/",
)
if not argv:
arger.print_help()
sys.exit(1)
args, unknown = arger.parse_known_args()
if unknown and unknown[0] == "--":
unknown = unknown[1:]
if not unknown:
arger.print_help()
sys.exit(1)
return args, unknown
def generate_plist(label: str, args: list[str], env: dict, cwd: Path) -> str:
doc = {
"Label": label,
"ProgramArguments": args,
"EnvironmentVariables": env,
"WorkingDirectory": str(cwd),
"RunAtLoad": True,
"StandardOutPath": str(cwd / f"{label}.stdout.log"),
"StandardErrorPath": str(cwd / f"{label}.stderr.log"),
}
return plistlib.dumps(
doc,
fmt=plistlib.FMT_XML,
sort_keys=True,
).decode()
def main():
args, unknown = parse_args(sys.argv[1:])
print(args, unknown)
plist_doc = generate_plist(
label=args.label,
args=unknown,
env=dict(args.env),
cwd=args.cwd,
)
print(plist_doc)
if args.install:
install_dir: Path = Path("~/Library/LaunchAgents/").expanduser()
logging.info(f"Installing plist to {install_dir}")
install_path = install_dir / f"{args.label}.plist"
install_path.write_text(plist_doc)
logging.info(f'Run "launchctl load -w {install_path}" to start the service')
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
main()