tmuxpull 0.2.0__tar.gz → 0.2.1__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.
- {tmuxpull-0.2.0 → tmuxpull-0.2.1}/PKG-INFO +35 -1
- {tmuxpull-0.2.0 → tmuxpull-0.2.1}/README.md +34 -0
- {tmuxpull-0.2.0 → tmuxpull-0.2.1}/bin/rebase-all +4 -0
- {tmuxpull-0.2.0 → tmuxpull-0.2.1}/bin/rebase-all.py +38 -0
- {tmuxpull-0.2.0 → tmuxpull-0.2.1}/pyproject.toml +5 -1
- {tmuxpull-0.2.0 → tmuxpull-0.2.1}/src/tmuxpull/__init__.py +38 -0
- tmuxpull-0.2.1/tests/test_version.py +90 -0
- {tmuxpull-0.2.0 → tmuxpull-0.2.1}/uv.lock +0 -1
- {tmuxpull-0.2.0 → tmuxpull-0.2.1}/.gitignore +0 -0
- {tmuxpull-0.2.0 → tmuxpull-0.2.1}/LICENSE +0 -0
- {tmuxpull-0.2.0 → tmuxpull-0.2.1}/mise.toml +0 -0
- {tmuxpull-0.2.0 → tmuxpull-0.2.1}/scripts/gen_script.py +0 -0
- {tmuxpull-0.2.0 → tmuxpull-0.2.1}/src/tmuxpull/__main__.py +0 -0
- {tmuxpull-0.2.0 → tmuxpull-0.2.1}/tests/test_ignore.py +0 -0
- {tmuxpull-0.2.0 → tmuxpull-0.2.1}/tests/test_report.py +0 -0
- {tmuxpull-0.2.0 → tmuxpull-0.2.1}/tests/test_scenarios.py +0 -0
- {tmuxpull-0.2.0 → tmuxpull-0.2.1}/tests/test_script_sync.py +0 -0
- {tmuxpull-0.2.0 → tmuxpull-0.2.1}/tests/test_tmuxpull.py +0 -0
- {tmuxpull-0.2.0 → tmuxpull-0.2.1}/tests/test_zsh_parity.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: tmuxpull
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Keep every git repo under a directory in step with its remote, concurrently, with a tmux session per repo that needs you
|
|
5
5
|
Project-URL: Homepage, https://github.com/nguyengg/tmuxpull
|
|
6
6
|
Project-URL: Repository, https://github.com/nguyengg/tmuxpull.git
|
|
@@ -121,6 +121,40 @@ tmuxpull [-d DEPTH] [-j JOBS] [--tmux {on,off}] [--rebase-pushed]
|
|
|
121
121
|
- `--no-log` — Don't write a report file
|
|
122
122
|
- `-v, --verbose` — Show incoming commit subjects (`-v` = top 3, `-vv` = all)
|
|
123
123
|
- `--dry-run` — List repos that would be processed, then exit
|
|
124
|
+
- `-V, --version` — Print the version and the file it's running from, then exit
|
|
125
|
+
|
|
126
|
+
### Am I running the latest?
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
$ tmuxpull --version
|
|
130
|
+
tmuxpull 0.2.1 (/home/you/.local/share/uv/tools/tmuxpull/lib/python3.14/site-packages/tmuxpull/__init__.py)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
The path is there because one machine can easily have three copies on `PATH` —
|
|
134
|
+
a `pip install` into whichever Python was current, a `uv tool install`, and a
|
|
135
|
+
curl'd standalone script — and the version number alone won't tell you which one
|
|
136
|
+
just ran. The second field is always the bare version, so `tmuxpull --version |
|
|
137
|
+
awk '{print $2}'` is scriptable.
|
|
138
|
+
|
|
139
|
+
Compare against what's published:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
curl -s https://pypi.org/pypi/tmuxpull/json | grep -o '"version":"[^"]*"' | head -1
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Upgrading depends on how it was installed:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
uv tool upgrade tmuxpull # uv tool install
|
|
149
|
+
pip install -U tmuxpull # pip install
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
A `pip install` into a version-managed Python (mise, pyenv, asdf) is worth
|
|
153
|
+
avoiding: the package lives under that exact interpreter, so the next Python
|
|
154
|
+
upgrade silently leaves it behind — or drops it off `PATH` entirely. `uv tool
|
|
155
|
+
install tmuxpull` keeps it in its own environment instead. The curl one-liners
|
|
156
|
+
need no upgrade at all: they read `main` directly, so they're current the moment
|
|
157
|
+
a fix lands.
|
|
124
158
|
|
|
125
159
|
### Per-repo git config
|
|
126
160
|
|
|
@@ -94,6 +94,40 @@ tmuxpull [-d DEPTH] [-j JOBS] [--tmux {on,off}] [--rebase-pushed]
|
|
|
94
94
|
- `--no-log` — Don't write a report file
|
|
95
95
|
- `-v, --verbose` — Show incoming commit subjects (`-v` = top 3, `-vv` = all)
|
|
96
96
|
- `--dry-run` — List repos that would be processed, then exit
|
|
97
|
+
- `-V, --version` — Print the version and the file it's running from, then exit
|
|
98
|
+
|
|
99
|
+
### Am I running the latest?
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
$ tmuxpull --version
|
|
103
|
+
tmuxpull 0.2.1 (/home/you/.local/share/uv/tools/tmuxpull/lib/python3.14/site-packages/tmuxpull/__init__.py)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The path is there because one machine can easily have three copies on `PATH` —
|
|
107
|
+
a `pip install` into whichever Python was current, a `uv tool install`, and a
|
|
108
|
+
curl'd standalone script — and the version number alone won't tell you which one
|
|
109
|
+
just ran. The second field is always the bare version, so `tmuxpull --version |
|
|
110
|
+
awk '{print $2}'` is scriptable.
|
|
111
|
+
|
|
112
|
+
Compare against what's published:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
curl -s https://pypi.org/pypi/tmuxpull/json | grep -o '"version":"[^"]*"' | head -1
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Upgrading depends on how it was installed:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
uv tool upgrade tmuxpull # uv tool install
|
|
122
|
+
pip install -U tmuxpull # pip install
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
A `pip install` into a version-managed Python (mise, pyenv, asdf) is worth
|
|
126
|
+
avoiding: the package lives under that exact interpreter, so the next Python
|
|
127
|
+
upgrade silently leaves it behind — or drops it off `PATH` entirely. `uv tool
|
|
128
|
+
install tmuxpull` keeps it in its own environment instead. The curl one-liners
|
|
129
|
+
need no upgrade at all: they read `main` directly, so they're current the moment
|
|
130
|
+
a fix lands.
|
|
97
131
|
|
|
98
132
|
### Per-repo git config
|
|
99
133
|
|
|
@@ -35,6 +35,8 @@
|
|
|
35
35
|
set -uo pipefail
|
|
36
36
|
|
|
37
37
|
# --- defaults ------------------------------------------------------------
|
|
38
|
+
# Kept in step with src/tmuxpull/__init__.py's __version__ by a test.
|
|
39
|
+
version="0.2.1"
|
|
38
40
|
max_depth=2
|
|
39
41
|
jobs=8
|
|
40
42
|
typeset -a excludes
|
|
@@ -73,6 +75,7 @@ Options:
|
|
|
73
75
|
-v Verbose: -v shows top 3 incoming commit subjects per repo,
|
|
74
76
|
-vv shows all
|
|
75
77
|
--dry-run List repos and exit
|
|
78
|
+
-V, --version Show the version and which copy is running, then exit
|
|
76
79
|
-h, --help Show this help
|
|
77
80
|
|
|
78
81
|
Per-repo git config:
|
|
@@ -93,6 +96,7 @@ while [[ $# -gt 0 ]]; do
|
|
|
93
96
|
-v) ((verbose++)); shift ;;
|
|
94
97
|
-vv) verbose=2; shift ;;
|
|
95
98
|
--dry-run) dry_run=true; shift ;;
|
|
99
|
+
-V|--version) printf 'tmuxpull %s (%s)\n' "$version" "${0:A}"; exit 0 ;;
|
|
96
100
|
-h|--help) usage; exit 0 ;;
|
|
97
101
|
--) shift; break ;;
|
|
98
102
|
-*) echo "Unknown option: $1" >&2; usage; exit 1 ;;
|
|
@@ -60,6 +60,12 @@ from typing import Iterable
|
|
|
60
60
|
import libtmux
|
|
61
61
|
|
|
62
62
|
|
|
63
|
+
# Single source of truth for the version: pyproject.toml reads it from here
|
|
64
|
+
# (hatch dynamic version), so the PyPI package, the generated standalone script
|
|
65
|
+
# and `--version` can never disagree.
|
|
66
|
+
__version__ = "0.2.1"
|
|
67
|
+
|
|
68
|
+
|
|
63
69
|
# Directories that are never a repo we want to descend into.
|
|
64
70
|
_SKIP_DIRS: frozenset[str] = frozenset(
|
|
65
71
|
{
|
|
@@ -923,6 +929,32 @@ async def _run(repos: list[Repo], jobs: int, verbose: int, opts: Options) -> lis
|
|
|
923
929
|
return results
|
|
924
930
|
|
|
925
931
|
|
|
932
|
+
def version_line() -> str:
|
|
933
|
+
"""Version plus the file it is running from.
|
|
934
|
+
|
|
935
|
+
The path matters as much as the number: the same machine can have a PyPI
|
|
936
|
+
install, a `uv tool` install and a curl'd standalone script on PATH, and
|
|
937
|
+
this says which one you just ran.
|
|
938
|
+
"""
|
|
939
|
+
return f"tmuxpull {__version__} ({Path(__file__).resolve()})"
|
|
940
|
+
|
|
941
|
+
|
|
942
|
+
class _VersionAction(argparse.Action):
|
|
943
|
+
"""Print version_line() verbatim.
|
|
944
|
+
|
|
945
|
+
argparse's built-in "version" action runs its text through the help
|
|
946
|
+
formatter, which re-wraps at terminal width and splits the path onto its
|
|
947
|
+
own line -- so `--version` output would depend on how wide the window is.
|
|
948
|
+
"""
|
|
949
|
+
|
|
950
|
+
def __init__(self, option_strings: list[str], dest: str, **kw: object) -> None:
|
|
951
|
+
super().__init__(option_strings, dest, nargs=0, **kw) # type: ignore[arg-type]
|
|
952
|
+
|
|
953
|
+
def __call__(self, parser, namespace, values, option_string=None) -> None: # noqa: ANN001
|
|
954
|
+
print(version_line())
|
|
955
|
+
parser.exit()
|
|
956
|
+
|
|
957
|
+
|
|
926
958
|
def main() -> None:
|
|
927
959
|
ap = argparse.ArgumentParser(
|
|
928
960
|
prog="tmuxpull",
|
|
@@ -941,6 +973,12 @@ def main() -> None:
|
|
|
941
973
|
"main/master/trunk)."
|
|
942
974
|
),
|
|
943
975
|
)
|
|
976
|
+
ap.add_argument(
|
|
977
|
+
"-V",
|
|
978
|
+
"--version",
|
|
979
|
+
action=_VersionAction,
|
|
980
|
+
help="Show the version and which copy is running, then exit.",
|
|
981
|
+
)
|
|
944
982
|
ap.add_argument(
|
|
945
983
|
"-d",
|
|
946
984
|
"--max-depth",
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tmuxpull"
|
|
7
|
-
|
|
7
|
+
dynamic = ["version"]
|
|
8
8
|
description = "Keep every git repo under a directory in step with its remote, concurrently, with a tmux session per repo that needs you"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -42,6 +42,10 @@ tmuxpull = "tmuxpull.__main__:main"
|
|
|
42
42
|
[tool.hatch.build.targets.wheel]
|
|
43
43
|
packages = ["src/tmuxpull"]
|
|
44
44
|
|
|
45
|
+
# One place to bump: src/tmuxpull/__init__.py's __version__.
|
|
46
|
+
[tool.hatch.version]
|
|
47
|
+
path = "src/tmuxpull/__init__.py"
|
|
48
|
+
|
|
45
49
|
[tool.ruff]
|
|
46
50
|
line-length = 120
|
|
47
51
|
target-version = "py311"
|
|
@@ -50,6 +50,12 @@ from typing import Iterable
|
|
|
50
50
|
import libtmux
|
|
51
51
|
|
|
52
52
|
|
|
53
|
+
# Single source of truth for the version: pyproject.toml reads it from here
|
|
54
|
+
# (hatch dynamic version), so the PyPI package, the generated standalone script
|
|
55
|
+
# and `--version` can never disagree.
|
|
56
|
+
__version__ = "0.2.1"
|
|
57
|
+
|
|
58
|
+
|
|
53
59
|
# Directories that are never a repo we want to descend into.
|
|
54
60
|
_SKIP_DIRS: frozenset[str] = frozenset(
|
|
55
61
|
{
|
|
@@ -913,6 +919,32 @@ async def _run(repos: list[Repo], jobs: int, verbose: int, opts: Options) -> lis
|
|
|
913
919
|
return results
|
|
914
920
|
|
|
915
921
|
|
|
922
|
+
def version_line() -> str:
|
|
923
|
+
"""Version plus the file it is running from.
|
|
924
|
+
|
|
925
|
+
The path matters as much as the number: the same machine can have a PyPI
|
|
926
|
+
install, a `uv tool` install and a curl'd standalone script on PATH, and
|
|
927
|
+
this says which one you just ran.
|
|
928
|
+
"""
|
|
929
|
+
return f"tmuxpull {__version__} ({Path(__file__).resolve()})"
|
|
930
|
+
|
|
931
|
+
|
|
932
|
+
class _VersionAction(argparse.Action):
|
|
933
|
+
"""Print version_line() verbatim.
|
|
934
|
+
|
|
935
|
+
argparse's built-in "version" action runs its text through the help
|
|
936
|
+
formatter, which re-wraps at terminal width and splits the path onto its
|
|
937
|
+
own line -- so `--version` output would depend on how wide the window is.
|
|
938
|
+
"""
|
|
939
|
+
|
|
940
|
+
def __init__(self, option_strings: list[str], dest: str, **kw: object) -> None:
|
|
941
|
+
super().__init__(option_strings, dest, nargs=0, **kw) # type: ignore[arg-type]
|
|
942
|
+
|
|
943
|
+
def __call__(self, parser, namespace, values, option_string=None) -> None: # noqa: ANN001
|
|
944
|
+
print(version_line())
|
|
945
|
+
parser.exit()
|
|
946
|
+
|
|
947
|
+
|
|
916
948
|
def main() -> None:
|
|
917
949
|
ap = argparse.ArgumentParser(
|
|
918
950
|
prog="tmuxpull",
|
|
@@ -931,6 +963,12 @@ def main() -> None:
|
|
|
931
963
|
"main/master/trunk)."
|
|
932
964
|
),
|
|
933
965
|
)
|
|
966
|
+
ap.add_argument(
|
|
967
|
+
"-V",
|
|
968
|
+
"--version",
|
|
969
|
+
action=_VersionAction,
|
|
970
|
+
help="Show the version and which copy is running, then exit.",
|
|
971
|
+
)
|
|
934
972
|
ap.add_argument(
|
|
935
973
|
"-d",
|
|
936
974
|
"--max-depth",
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""The version must be one number, reported the same way by every copy.
|
|
2
|
+
|
|
3
|
+
There are four places it could drift: the module, the packaging metadata, the
|
|
4
|
+
generated standalone script, and the zsh port. `__version__` is the source of
|
|
5
|
+
truth (pyproject reads it via hatch's dynamic version) and these pin the rest
|
|
6
|
+
to it.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import re
|
|
11
|
+
import shutil
|
|
12
|
+
import subprocess
|
|
13
|
+
import sys
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
import pytest
|
|
17
|
+
|
|
18
|
+
import tmuxpull
|
|
19
|
+
from tmuxpull import __version__, version_line
|
|
20
|
+
|
|
21
|
+
ROOT = Path(__file__).resolve().parent.parent
|
|
22
|
+
ZSH_SCRIPT = ROOT / "bin" / "rebase-all"
|
|
23
|
+
STANDALONE = ROOT / "bin" / "rebase-all.py"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _env() -> dict[str, str]:
|
|
27
|
+
import os
|
|
28
|
+
|
|
29
|
+
env = dict(os.environ)
|
|
30
|
+
env["PYTHONPATH"] = str(ROOT / "src")
|
|
31
|
+
return env
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_version_is_a_release_number():
|
|
35
|
+
assert re.fullmatch(r"\d+\.\d+\.\d+([a-z0-9.]*)?", __version__), __version__
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_packaging_metadata_matches_the_module():
|
|
39
|
+
"""Proves pyproject's hatch dynamic version is wired to __version__."""
|
|
40
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
installed = version("tmuxpull")
|
|
44
|
+
except PackageNotFoundError:
|
|
45
|
+
pytest.skip("tmuxpull is not installed in this environment")
|
|
46
|
+
assert installed == __version__
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_pyproject_does_not_hardcode_a_second_version():
|
|
50
|
+
body = (ROOT / "pyproject.toml").read_text(encoding="utf-8")
|
|
51
|
+
assert 'dynamic = ["version"]' in body
|
|
52
|
+
assert '[tool.hatch.version]' in body
|
|
53
|
+
assert not re.search(r'(?m)^version = ', body), "a literal version would drift"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_version_line_names_the_running_file():
|
|
57
|
+
line = version_line()
|
|
58
|
+
assert line.startswith(f"tmuxpull {__version__} (")
|
|
59
|
+
assert str(Path(tmuxpull.__file__).resolve()) in line
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_cli_version_flag_prints_and_exits_zero():
|
|
63
|
+
for flag in ("--version", "-V"):
|
|
64
|
+
proc = subprocess.run(
|
|
65
|
+
[sys.executable, "-m", "tmuxpull", flag],
|
|
66
|
+
capture_output=True,
|
|
67
|
+
text=True,
|
|
68
|
+
env=_env(),
|
|
69
|
+
)
|
|
70
|
+
assert proc.returncode == 0, proc.stderr
|
|
71
|
+
out = (proc.stdout + proc.stderr).strip()
|
|
72
|
+
assert out.startswith(f"tmuxpull {__version__} ")
|
|
73
|
+
# the second field is the bare version, so `--version | awk '{print $2}'` works
|
|
74
|
+
assert out.split()[1] == __version__
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_standalone_script_carries_the_same_version():
|
|
78
|
+
body = STANDALONE.read_text(encoding="utf-8")
|
|
79
|
+
assert f'__version__ = "{__version__}"' in body
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@pytest.mark.skipif(shutil.which("zsh") is None, reason="zsh not available")
|
|
83
|
+
def test_zsh_port_reports_the_same_version():
|
|
84
|
+
proc = subprocess.run(
|
|
85
|
+
[str(ZSH_SCRIPT), "--version"], capture_output=True, text=True, env=_env()
|
|
86
|
+
)
|
|
87
|
+
assert proc.returncode == 0, proc.stderr
|
|
88
|
+
out = proc.stdout.strip()
|
|
89
|
+
assert out.startswith(f"tmuxpull {__version__} (")
|
|
90
|
+
assert out.split()[1] == __version__
|
|
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
|