repoactive 0.2.3.dev2__tar.gz → 0.2.4.dev1__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.
- {repoactive-0.2.3.dev2 → repoactive-0.2.4.dev1}/PKG-INFO +1 -1
- {repoactive-0.2.3.dev2 → repoactive-0.2.4.dev1}/pyproject.toml +1 -1
- {repoactive-0.2.3.dev2 → repoactive-0.2.4.dev1}/src/repoactive/jj.py +15 -7
- {repoactive-0.2.3.dev2 → repoactive-0.2.4.dev1}/src/repoactive/runner.py +36 -11
- {repoactive-0.2.3.dev2 → repoactive-0.2.4.dev1}/.gitignore +0 -0
- {repoactive-0.2.3.dev2 → repoactive-0.2.4.dev1}/LICENSE +0 -0
- {repoactive-0.2.3.dev2 → repoactive-0.2.4.dev1}/README.md +0 -0
- {repoactive-0.2.3.dev2 → repoactive-0.2.4.dev1}/src/repoactive/__init__.py +0 -0
- {repoactive-0.2.3.dev2 → repoactive-0.2.4.dev1}/src/repoactive/cli.py +0 -0
- {repoactive-0.2.3.dev2 → repoactive-0.2.4.dev1}/src/repoactive/config.py +0 -0
- {repoactive-0.2.3.dev2 → repoactive-0.2.4.dev1}/src/repoactive/constants.py +0 -0
- {repoactive-0.2.3.dev2 → repoactive-0.2.4.dev1}/src/repoactive/graph.py +0 -0
- {repoactive-0.2.3.dev2 → repoactive-0.2.4.dev1}/src/repoactive/jobtree.py +0 -0
- {repoactive-0.2.3.dev2 → repoactive-0.2.4.dev1}/src/repoactive/lock.py +0 -0
- {repoactive-0.2.3.dev2 → repoactive-0.2.4.dev1}/src/repoactive/platforms/__init__.py +0 -0
- {repoactive-0.2.3.dev2 → repoactive-0.2.4.dev1}/src/repoactive/platforms/base.py +0 -0
- {repoactive-0.2.3.dev2 → repoactive-0.2.4.dev1}/src/repoactive/platforms/github.py +0 -0
- {repoactive-0.2.3.dev2 → repoactive-0.2.4.dev1}/src/repoactive/platforms/gitlab.py +0 -0
- {repoactive-0.2.3.dev2 → repoactive-0.2.4.dev1}/src/repoactive/progress.py +0 -0
- {repoactive-0.2.3.dev2 → repoactive-0.2.4.dev1}/src/repoactive/settings.py +0 -0
- {repoactive-0.2.3.dev2 → repoactive-0.2.4.dev1}/src/repoactive/ui.py +0 -0
- {repoactive-0.2.3.dev2 → repoactive-0.2.4.dev1}/src/repoactive/updates.py +0 -0
|
@@ -8,7 +8,7 @@ import tempfile
|
|
|
8
8
|
import time
|
|
9
9
|
from collections.abc import Generator
|
|
10
10
|
from dataclasses import dataclass
|
|
11
|
-
from datetime import datetime
|
|
11
|
+
from datetime import UTC, datetime
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
|
|
14
14
|
from repoactive.constants import JOB_TRAILER_KEY
|
|
@@ -215,8 +215,8 @@ class JJ:
|
|
|
215
215
|
def edit(self, revision: str) -> None:
|
|
216
216
|
self._run("edit", revision)
|
|
217
217
|
|
|
218
|
-
def restore(self,
|
|
219
|
-
self._run("restore", "--
|
|
218
|
+
def restore(self, *, source_rev: str, destination_rev: str) -> None:
|
|
219
|
+
self._run("restore", "--from", source_rev, "--into", destination_rev)
|
|
220
220
|
|
|
221
221
|
def rebase(self, *onto: str) -> None:
|
|
222
222
|
onto_args = [arg for parent in onto for arg in ("--onto", parent)]
|
|
@@ -400,8 +400,10 @@ class JJ:
|
|
|
400
400
|
if name.strip()
|
|
401
401
|
}
|
|
402
402
|
|
|
403
|
-
def
|
|
404
|
-
|
|
403
|
+
def last_job_commit_date(
|
|
404
|
+
self, *, job_name: str, base: str, since: datetime
|
|
405
|
+
) -> datetime | None:
|
|
406
|
+
"""Return the committer date of the most recent job commit on ``base``, or ``None``.
|
|
405
407
|
|
|
406
408
|
Matches commits that have a ``Repoactive-Job: <job_name>`` trailer and a
|
|
407
409
|
committer date at or after ``since``. Used to throttle jobs: a recent
|
|
@@ -410,16 +412,22 @@ class JJ:
|
|
|
410
412
|
The trailer is matched via jj's trailer parsing, which only considers the
|
|
411
413
|
final paragraph of the description, so a stray matching line in the body
|
|
412
414
|
is correctly ignored.
|
|
415
|
+
|
|
416
|
+
Returns the newest matching committer timestamp, or ``None`` if no match.
|
|
413
417
|
"""
|
|
414
418
|
revset = f'::{base} & committer_date(after:"{_jj_timestamp(since)}")'
|
|
415
419
|
template = f"""
|
|
416
420
|
if (trailers.any(|t| t.key() == "{JOB_TRAILER_KEY}" && t.value() == "{job_name}"),
|
|
417
|
-
"
|
|
421
|
+
committer.timestamp().utc().format("%Y-%m-%dT%H:%M:%S") ++ "\\n",
|
|
418
422
|
""
|
|
419
423
|
)
|
|
420
424
|
"""
|
|
421
425
|
output = self._run("log", "--no-graph", "-r", revset, "-T", template)
|
|
422
|
-
|
|
426
|
+
lines = [line.strip() for line in output.splitlines() if line.strip()]
|
|
427
|
+
if not lines:
|
|
428
|
+
return None
|
|
429
|
+
# jj log returns newest-first; take the first line.
|
|
430
|
+
return datetime.fromisoformat(lines[0]).replace(tzinfo=UTC)
|
|
423
431
|
|
|
424
432
|
def git_push_bookmarks(self, *bookmarks: str) -> None:
|
|
425
433
|
"""Push bookmarks to the remote.
|
|
@@ -622,22 +622,43 @@ def _build_generated_jobs(
|
|
|
622
622
|
return emitted
|
|
623
623
|
|
|
624
624
|
|
|
625
|
-
def
|
|
626
|
-
"""
|
|
625
|
+
def _format_duration(seconds: float) -> str:
|
|
626
|
+
"""Format a duration in seconds as a human-readable string like '3d 2h' or '45m'."""
|
|
627
|
+
seconds = int(seconds)
|
|
628
|
+
days, seconds = divmod(seconds, 86400)
|
|
629
|
+
hours, seconds = divmod(seconds, 3600)
|
|
630
|
+
minutes, seconds = divmod(seconds, 60)
|
|
631
|
+
if days:
|
|
632
|
+
parts = [f"{days}d"]
|
|
633
|
+
if hours:
|
|
634
|
+
parts.append(f"{hours}h")
|
|
635
|
+
return " ".join(parts)
|
|
636
|
+
if hours:
|
|
637
|
+
parts = [f"{hours}h"]
|
|
638
|
+
if minutes:
|
|
639
|
+
parts.append(f"{minutes}m")
|
|
640
|
+
return " ".join(parts)
|
|
641
|
+
if minutes:
|
|
642
|
+
return f"{minutes}m"
|
|
643
|
+
return f"{seconds}s"
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
def _on_cooldown(job: Job, repo_path: Path) -> datetime | None:
|
|
647
|
+
"""Return the last-run timestamp if the job is still on cooldown, else None."""
|
|
627
648
|
delta = job.cooldown_timedelta()
|
|
628
649
|
if delta is None:
|
|
629
|
-
return
|
|
650
|
+
return None
|
|
630
651
|
base = job.base_branch or "trunk()"
|
|
631
652
|
since = datetime.now(UTC) - delta
|
|
632
|
-
|
|
653
|
+
last_run = JJ(repo_path).last_job_commit_date(job_name=job.name, base=base, since=since)
|
|
633
654
|
logger.debug(
|
|
634
|
-
"[%s] cooldown check: base=%s since=%s ->
|
|
655
|
+
"[%s] cooldown check: base=%s since=%s -> last_run=%s",
|
|
635
656
|
job.name,
|
|
636
657
|
base,
|
|
637
658
|
since.isoformat(),
|
|
638
|
-
|
|
659
|
+
last_run,
|
|
639
660
|
)
|
|
640
|
-
return
|
|
661
|
+
return last_run
|
|
641
662
|
|
|
642
663
|
|
|
643
664
|
def _validate_selection(
|
|
@@ -828,8 +849,13 @@ def _dispatch_job( # noqa: PLR0913
|
|
|
828
849
|
# emits nothing, throttling its whole fan-out as a unit (the dual trailer
|
|
829
850
|
# records a recent child landing as the generator's). See
|
|
830
851
|
# docs/adr/0004-job-generators.md.
|
|
831
|
-
if _on_cooldown(resolved_job, repo_path):
|
|
832
|
-
|
|
852
|
+
if last_run := _on_cooldown(resolved_job, repo_path):
|
|
853
|
+
elapsed = datetime.now(UTC) - last_run
|
|
854
|
+
elapsed_str = _format_duration(elapsed.total_seconds())
|
|
855
|
+
print(
|
|
856
|
+
f"==> [{job.name}] on cooldown ({resolved_job.cooldown_period}),"
|
|
857
|
+
f" last run {elapsed_str} ago, skipped"
|
|
858
|
+
)
|
|
833
859
|
summary.on_cooldown.add(job.name)
|
|
834
860
|
# Treat like a no-op run so dependents proceed on the base branch.
|
|
835
861
|
summary.results[job.name] = JobResult(
|
|
@@ -1005,8 +1031,7 @@ def _absorb_results(
|
|
|
1005
1031
|
if not content_unchanged:
|
|
1006
1032
|
if abs_ws is None:
|
|
1007
1033
|
abs_ws = stack.enter_context(repo.temp_workspace("repoactive-absorb"))
|
|
1008
|
-
abs_ws.
|
|
1009
|
-
abs_ws.restore(new_cid)
|
|
1034
|
+
abs_ws.restore(source_rev=new_cid, destination_rev=old_cid)
|
|
1010
1035
|
repo.describe_revision(old_cid, message)
|
|
1011
1036
|
elif _strip_boxquote_and_trailers(
|
|
1012
1037
|
repo.get_description(old_cid)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|