devin-install-scheduler 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,32 @@
1
+ Metadata-Version: 2.4
2
+ Name: devin-install-scheduler
3
+ Version: 0.1.0
4
+ Summary: Shared daily-job installer for the devin-state packages — cron, Task Scheduler, or config-registry fallback.
5
+ Author: Icaro0310
6
+ License: MIT
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ Provides-Extra: dev
10
+ Requires-Dist: pytest>=8; extra == "dev"
11
+
12
+ # devin-install-scheduler
13
+
14
+ Shared installer for the `devin-state` packages' daily jobs. Registers a
15
+ tagged `crontab` line on Linux/macOS, a `schtasks` entry on Windows, or a
16
+ job record in `<config-dir>/.devin-ecosystem/scheduled.json` (the F6
17
+ elapsed fallback ticked by devin-powerups' `UserPromptSubmit` hook).
18
+
19
+ Used by `devin-backup` (`devin-backup-daily`) and `devin-janitor`
20
+ (`devin-janitor-daily`). Not a Devin-facing CLI — library only.
21
+
22
+ ```python
23
+ from devin_install_scheduler import install_daily
24
+
25
+ install_daily(
26
+ job_name="my-job-daily",
27
+ command=f'"{sys.executable}" -m my_pkg.cli run',
28
+ )
29
+ ```
30
+
31
+ > Unofficial community project. Not affiliated with, endorsed by, or
32
+ > sponsored by Cognition AI. "Devin" is a trademark of Cognition AI.
@@ -0,0 +1,21 @@
1
+ # devin-install-scheduler
2
+
3
+ Shared installer for the `devin-state` packages' daily jobs. Registers a
4
+ tagged `crontab` line on Linux/macOS, a `schtasks` entry on Windows, or a
5
+ job record in `<config-dir>/.devin-ecosystem/scheduled.json` (the F6
6
+ elapsed fallback ticked by devin-powerups' `UserPromptSubmit` hook).
7
+
8
+ Used by `devin-backup` (`devin-backup-daily`) and `devin-janitor`
9
+ (`devin-janitor-daily`). Not a Devin-facing CLI — library only.
10
+
11
+ ```python
12
+ from devin_install_scheduler import install_daily
13
+
14
+ install_daily(
15
+ job_name="my-job-daily",
16
+ command=f'"{sys.executable}" -m my_pkg.cli run',
17
+ )
18
+ ```
19
+
20
+ > Unofficial community project. Not affiliated with, endorsed by, or
21
+ > sponsored by Cognition AI. "Devin" is a trademark of Cognition AI.
@@ -0,0 +1,22 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "devin-install-scheduler"
7
+ version = "0.1.0"
8
+ description = "Shared daily-job installer for the devin-state packages — cron, Task Scheduler, or config-registry fallback."
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.10"
12
+ authors = [{ name = "Icaro0310" }]
13
+ dependencies = []
14
+
15
+ [project.optional-dependencies]
16
+ dev = ["pytest>=8"]
17
+
18
+ [tool.setuptools.packages.find]
19
+ where = ["src"]
20
+
21
+ [tool.pytest.ini_options]
22
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,5 @@
1
+ """Shared daily-job installer for devin-state packages."""
2
+
3
+ from devin_install_scheduler.install import install_daily
4
+
5
+ __all__ = ["install_daily"]
@@ -0,0 +1,132 @@
1
+ """Install a daily scheduled job for a devin-state package.
2
+
3
+ The F6 scheduling foundation — the same backends and registry format as
4
+ ``devin-powerups/tools/schedule.py``:
5
+
6
+ - **cron** (Linux/macOS): a tagged ``crontab`` line.
7
+ - **tasksch** (Windows): a daily ``schtasks`` entry.
8
+ - **elapsed** (corporate fallback): a job record in
9
+ ``<config-dir>/.devin-ecosystem/scheduled.json`` that the F6
10
+ ``UserPromptSubmit`` hook ticks — see powerups' schedule.py.
11
+
12
+ Callers supply the job name and the command to schedule; this module owns
13
+ backend selection and the registry write.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import json
19
+ import os
20
+ import shutil
21
+ import subprocess
22
+ import sys
23
+ import tempfile
24
+ import time
25
+ from pathlib import Path
26
+
27
+
28
+ def _config_dir(override: str | None) -> Path:
29
+ if override:
30
+ return Path(override).expanduser()
31
+ env = os.environ.get("DEVIN_CONFIG_DIR")
32
+ if env:
33
+ return Path(env).expanduser()
34
+ if sys.platform.startswith("win"):
35
+ base = os.environ.get("APPDATA") or str(
36
+ Path.home() / "AppData" / "Roaming")
37
+ return Path(base) / "Devin"
38
+ if sys.platform == "darwin":
39
+ return Path.home() / "Library" / "Application Support" / "Devin"
40
+ xdg = os.environ.get("XDG_CONFIG_HOME") or str(Path.home() / ".config")
41
+ for name in ("Devin", "devin"):
42
+ if (Path(xdg) / name).is_dir():
43
+ return Path(xdg) / name
44
+ return Path(xdg) / "devin"
45
+
46
+
47
+ def _registry_path(config_dir: Path) -> Path:
48
+ return config_dir / ".devin-ecosystem" / "scheduled.json"
49
+
50
+
51
+ def _load_registry(config_dir: Path) -> dict:
52
+ try:
53
+ data = json.loads(_registry_path(config_dir).read_text(
54
+ encoding="utf-8"))
55
+ if isinstance(data.get("jobs"), dict):
56
+ return data
57
+ except (OSError, ValueError):
58
+ pass
59
+ return {"version": 1, "jobs": {}}
60
+
61
+
62
+ def _save_registry(config_dir: Path, reg: dict) -> None:
63
+ p = _registry_path(config_dir)
64
+ p.parent.mkdir(parents=True, exist_ok=True)
65
+ fd, tmp = tempfile.mkstemp(dir=p.parent, suffix=".tmp")
66
+ with os.fdopen(fd, "w", encoding="utf-8") as fh:
67
+ json.dump(reg, fh, indent=2)
68
+ os.replace(tmp, p)
69
+
70
+
71
+ def _install_cron(command: str, cron_tag: str) -> bool:
72
+ """Append the tagged daily line to crontab. Returns False if no cron."""
73
+ # shutil.which IS the capability check: no crontab.exe exists on a
74
+ # vanilla Windows PATH, so an explicit sys.platform guard adds nothing
75
+ # and makes the line-building logic untestable there.
76
+ if not shutil.which("crontab"):
77
+ return False
78
+ out = subprocess.run(["crontab", "-l"], capture_output=True, text=True)
79
+ current = out.stdout if out.returncode == 0 else ""
80
+ kept = [l for l in current.splitlines() if cron_tag not in l]
81
+ kept.append(f"@daily {command} {cron_tag}")
82
+ subprocess.run(["crontab", "-"], input="\n".join(kept) + "\n",
83
+ text=True, check=True)
84
+ return True
85
+
86
+
87
+ def _install_tasksch(command: str, job_name: str) -> bool:
88
+ if not sys.platform.startswith("win") or not shutil.which("schtasks"):
89
+ return False
90
+ subprocess.run(
91
+ ["schtasks", "/create", "/f", "/tn", job_name,
92
+ "/sc", "DAILY", "/tr", command], check=True)
93
+ return True
94
+
95
+
96
+ def install_daily(job_name: str,
97
+ command: str,
98
+ config_dir: str | None = None,
99
+ backend: str = "auto") -> dict:
100
+ """Register a daily job; returns what was done."""
101
+ cron_tag = f"# devin-ecosystem:{job_name}"
102
+ cfg = _config_dir(config_dir)
103
+ used = backend
104
+ if backend == "auto":
105
+ if _install_tasksch(command, job_name):
106
+ used = "tasksch"
107
+ elif _install_cron(command, cron_tag):
108
+ used = "cron"
109
+ else:
110
+ used = "elapsed"
111
+ elif backend == "tasksch":
112
+ _install_tasksch(command, job_name)
113
+ elif backend == "cron":
114
+ if not _install_cron(command, cron_tag):
115
+ raise RuntimeError("cron backend requested but `crontab` "
116
+ "is not available")
117
+ elif backend != "elapsed":
118
+ raise ValueError(f"unknown backend {backend!r}")
119
+
120
+ reg = _load_registry(cfg)
121
+ reg["jobs"][job_name] = {
122
+ "command": command,
123
+ "guarded_command": command,
124
+ "interval_h": 24,
125
+ "backend": used,
126
+ "requires_devin_closed": False,
127
+ "installed_at": int(time.time()),
128
+ "last_run": 0,
129
+ }
130
+ _save_registry(cfg, reg)
131
+ return {"job": job_name, "backend": used, "command": command,
132
+ "registry": str(_registry_path(cfg))}
@@ -0,0 +1,32 @@
1
+ Metadata-Version: 2.4
2
+ Name: devin-install-scheduler
3
+ Version: 0.1.0
4
+ Summary: Shared daily-job installer for the devin-state packages — cron, Task Scheduler, or config-registry fallback.
5
+ Author: Icaro0310
6
+ License: MIT
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ Provides-Extra: dev
10
+ Requires-Dist: pytest>=8; extra == "dev"
11
+
12
+ # devin-install-scheduler
13
+
14
+ Shared installer for the `devin-state` packages' daily jobs. Registers a
15
+ tagged `crontab` line on Linux/macOS, a `schtasks` entry on Windows, or a
16
+ job record in `<config-dir>/.devin-ecosystem/scheduled.json` (the F6
17
+ elapsed fallback ticked by devin-powerups' `UserPromptSubmit` hook).
18
+
19
+ Used by `devin-backup` (`devin-backup-daily`) and `devin-janitor`
20
+ (`devin-janitor-daily`). Not a Devin-facing CLI — library only.
21
+
22
+ ```python
23
+ from devin_install_scheduler import install_daily
24
+
25
+ install_daily(
26
+ job_name="my-job-daily",
27
+ command=f'"{sys.executable}" -m my_pkg.cli run',
28
+ )
29
+ ```
30
+
31
+ > Unofficial community project. Not affiliated with, endorsed by, or
32
+ > sponsored by Cognition AI. "Devin" is a trademark of Cognition AI.
@@ -0,0 +1,10 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/devin_install_scheduler/__init__.py
4
+ src/devin_install_scheduler/install.py
5
+ src/devin_install_scheduler.egg-info/PKG-INFO
6
+ src/devin_install_scheduler.egg-info/SOURCES.txt
7
+ src/devin_install_scheduler.egg-info/dependency_links.txt
8
+ src/devin_install_scheduler.egg-info/requires.txt
9
+ src/devin_install_scheduler.egg-info/top_level.txt
10
+ tests/test_install.py
@@ -0,0 +1,43 @@
1
+ import json
2
+ import subprocess
3
+ from pathlib import Path
4
+
5
+ import pytest
6
+
7
+ from devin_install_scheduler import install as sched
8
+ from devin_install_scheduler import install_daily
9
+
10
+
11
+ def test_elapsed_writes_f6_registry(tmp_path):
12
+ res = install_daily("devin-test-daily", '"py" -m pkg.cli run',
13
+ config_dir=str(tmp_path / "cfg"), backend="elapsed")
14
+ assert res["backend"] == "elapsed"
15
+ reg = json.loads(Path(res["registry"]).read_text())
16
+ job = reg["jobs"]["devin-test-daily"]
17
+ assert job["command"] == '"py" -m pkg.cli run'
18
+ assert job["guarded_command"] == job["command"]
19
+ assert job["interval_h"] == 24
20
+ assert job["requires_devin_closed"] is False
21
+ assert job["installed_at"] > 0 and job["last_run"] == 0
22
+
23
+
24
+ def test_bad_backend(tmp_path):
25
+ with pytest.raises(ValueError):
26
+ install_daily("j", "cmd", config_dir=str(tmp_path), backend="bogus")
27
+
28
+
29
+ def test_cron_tagged_line(tmp_path, monkeypatch):
30
+ calls = []
31
+ monkeypatch.setattr(sched.shutil, "which", lambda _c: "/usr/bin/crontab")
32
+
33
+ def fake_run(argv, **kw):
34
+ calls.append((argv, kw))
35
+ return subprocess.CompletedProcess(argv, 0, "", "")
36
+
37
+ monkeypatch.setattr(sched.subprocess, "run", fake_run)
38
+ res = install_daily("devin-test-daily", '"py" -m pkg.cli run',
39
+ config_dir=str(tmp_path / "cfg"), backend="cron")
40
+ assert res["backend"] == "cron"
41
+ stdin = calls[-1][1]["input"]
42
+ assert "@daily" in stdin
43
+ assert "# devin-ecosystem:devin-test-daily" in stdin