Skip to content
Merged
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
508 changes: 36 additions & 472 deletions README.md

Large diffs are not rendered by default.

466 changes: 466 additions & 0 deletions docs/USER_GUIDE.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Runtime
rich>=13.0.0
certifi>=2024.8.30

# Dev / Quality
ruff>=0.9.0
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
rich>=13.0.0
certifi>=2024.8.30
37 changes: 37 additions & 0 deletions task_runner/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,12 +216,49 @@ def _add_execution_options(parser):
help="Random delay between tasks in seconds, e.g. '60-120' (default). "
"Use '0' to disable, or a single number for fixed delay.",
)
ctrl_group.add_argument(
"--timeout",
type=int,
default=None,
metavar="SEC",
help="Max execution time per task in seconds (default: 2400 = 40min). "
"The task is killed and marked failed if it exceeds this limit.",
)
ctrl_group.add_argument(
"--git-safety",
action="store_true",
help="Check workspace git status and create safety tag before execution",
)

# Notification
notify_group = parser.add_argument_group("notification")
notify_toggle = notify_group.add_mutually_exclusive_group()
notify_toggle.add_argument(
"--notify",
dest="notify_enabled",
action="store_true",
default=None,
help="Enable webhook notifications (default: auto-detect from env/config)",
)
notify_toggle.add_argument(
"--no-notify",
dest="notify_enabled",
action="store_false",
help="Disable all webhook notifications",
)
notify_group.add_argument(
"--notify-each",
action="store_true",
default=False,
help="Send a notification for every completed task (not just failures/summary)",
)
notify_group.add_argument(
"--wecom-webhook",
default=None,
metavar="URL",
help="WeCom bot webhook URL (overrides TASK_RUNNER_WECOM_WEBHOOK env var)",
)

# Output control
output_group = parser.add_argument_group("output control")
verbosity = output_group.add_mutually_exclusive_group()
Expand Down
15 changes: 15 additions & 0 deletions task_runner/commands/run_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,17 @@ def _execute(args, dry_run: bool = False) -> int:

delay_range = parse_delay_range(getattr(args, "delay", None))

# Resolve per-task timeout
from ..config import MAX_EXECUTION_SECONDS

cli_timeout = getattr(args, "timeout", None)
max_execution_seconds = cli_timeout if cli_timeout is not None else MAX_EXECUTION_SECONDS

# ── Notification settings ──
notify_enabled = getattr(args, "notify_enabled", None)
notify_each = getattr(args, "notify_each", False)
wecom_webhook = getattr(args, "wecom_webhook", None)

executor = TaskExecutor(
project_config=config,
task_set=task_set,
Expand All @@ -201,6 +212,10 @@ def _execute(args, dry_run: bool = False) -> int:
verbose=verbose,
quiet=quiet,
delay_range=delay_range,
max_execution_seconds=max_execution_seconds,
notify_enabled=notify_enabled,
notify_each=notify_each,
wecom_webhook=wecom_webhook,
)

result_code = executor.run()
Expand Down
8 changes: 8 additions & 0 deletions task_runner/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ class ToolConfig:
}


# ─── Execution Limits ────────────────────────────────────────────

# Maximum time (in seconds) a single task is allowed to run before being
# killed and marked as failed. 40 minutes by default; override via
# ``--timeout`` CLI flag or project-level configuration.
MAX_EXECUTION_SECONDS: int = 2400


# ─── Proxy Environment Variables ─────────────────────────────────

PROXY_ENV_KEYS = [
Expand Down
Loading
Loading