moat-src 0.8.5__py3-none-any.whl → 0.8.11__py3-none-any.whl
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.
- moat/src/_cfg.yaml +2 -0
- moat/src/_main.py +295 -201
- moat/src/_templates/moat/_main.py +23 -0
- moat/src/test.py +1 -1
- {moat_src-0.8.5.dist-info → moat_src-0.8.11.dist-info}/METADATA +2 -2
- moat_src-0.8.11.dist-info/RECORD +14 -0
- {moat_src-0.8.5.dist-info → moat_src-0.8.11.dist-info}/WHEEL +1 -1
- moat_src-0.8.11.dist-info/top_level.txt +1 -0
- build/lib/moat/src/__init__.py +0 -0
- build/lib/moat/src/_main.py +0 -1113
- build/lib/moat/src/_templates/moat/__init__.py +0 -3
- build/lib/moat/src/inspect.py +0 -65
- build/lib/moat/src/test.py +0 -89
- debian/.gitignore +0 -8
- debian/changelog +0 -78
- debian/control +0 -20
- debian/moat-src/usr/lib/python3/dist-packages/moat/src/__init__.py +0 -0
- debian/moat-src/usr/lib/python3/dist-packages/moat/src/_main.py +0 -1113
- debian/moat-src/usr/lib/python3/dist-packages/moat/src/_templates/moat/__init__.py +0 -3
- debian/moat-src/usr/lib/python3/dist-packages/moat/src/_templates/pyproject.default.yaml +0 -146
- debian/moat-src/usr/lib/python3/dist-packages/moat/src/_templates/pyproject.forced.yaml +0 -77
- debian/moat-src/usr/lib/python3/dist-packages/moat/src/inspect.py +0 -65
- debian/moat-src/usr/lib/python3/dist-packages/moat/src/test.py +0 -89
- debian/rules +0 -10
- moat/src/_templates/pyproject.default.yaml +0 -146
- moat/src/_templates/pyproject.forced.yaml +0 -77
- moat_src-0.8.5.dist-info/RECORD +0 -30
- moat_src-0.8.5.dist-info/top_level.txt +0 -4
- {build/lib/moat/src/_templates → moat/src/_templates/packaging}/pyproject.default.yaml +0 -0
- {build/lib/moat/src/_templates → moat/src/_templates/packaging}/pyproject.forced.yaml +0 -0
- {moat_src-0.8.5.dist-info → moat_src-0.8.11.dist-info}/licenses/LICENSE.txt +0 -0
moat/src/_main.py
CHANGED
@@ -13,33 +13,34 @@ from pathlib import Path
|
|
13
13
|
import asyncclick as click
|
14
14
|
import git
|
15
15
|
import tomlkit
|
16
|
-
from anyio import run_process
|
17
16
|
from moat.util import P, add_repr, attrdict, make_proc, yload, yprint
|
18
17
|
from packaging.requirements import Requirement
|
19
|
-
from attrs import define,field
|
20
|
-
from shutil import rmtree,copyfile,copytree
|
18
|
+
from attrs import define, field
|
19
|
+
from shutil import rmtree, copyfile, copytree
|
21
20
|
from contextlib import suppress
|
22
21
|
|
23
22
|
logger = logging.getLogger(__name__)
|
24
23
|
|
25
|
-
PACK=Path("packaging")
|
26
|
-
ARCH=subprocess.check_output(["dpkg","--print-architecture"]).decode("utf-8").strip()
|
24
|
+
PACK = Path("packaging")
|
25
|
+
ARCH = subprocess.check_output(["dpkg", "--print-architecture"]).decode("utf-8").strip()
|
27
26
|
|
28
|
-
|
27
|
+
|
28
|
+
def dash(n: str) -> str:
|
29
29
|
"""
|
30
30
|
moat.foo.bar > foo-bar
|
31
31
|
foo.bar > ext-foo-bar
|
32
32
|
"""
|
33
|
-
if n in ("main","moat"):
|
33
|
+
if n in ("main", "moat"):
|
34
34
|
return "main"
|
35
35
|
if "." not in n: # also applies to single-name packages
|
36
36
|
return n
|
37
37
|
|
38
38
|
if not n.startswith("moat."):
|
39
|
-
return "ext-"+n.replace("-",".")
|
40
|
-
return n.replace(".","-")[5:]
|
39
|
+
return "ext-" + n.replace("-", ".")
|
40
|
+
return n.replace(".", "-")[5:]
|
41
|
+
|
41
42
|
|
42
|
-
def undash(n:str) -> str:
|
43
|
+
def undash(n: str) -> str:
|
43
44
|
"""
|
44
45
|
foo-bar > moat.foo.bar
|
45
46
|
ext-foo-bar > foo.bar
|
@@ -47,85 +48,97 @@ def undash(n:str) -> str:
|
|
47
48
|
if "." in n:
|
48
49
|
return n
|
49
50
|
|
50
|
-
if n in ("main","moat"):
|
51
|
+
if n in ("main", "moat"):
|
51
52
|
return "moat"
|
52
53
|
if n.startswith("ext-"):
|
53
|
-
return n.replace("-",".")[4:]
|
54
|
-
return "moat."+n.replace("-",".")
|
54
|
+
return n.replace("-", ".")[4:]
|
55
|
+
return "moat." + n.replace("-", ".")
|
56
|
+
|
55
57
|
|
56
58
|
class ChangedError(RuntimeError):
|
57
|
-
def __init__(subsys,tag,head):
|
59
|
+
def __init__(subsys, tag, head):
|
58
60
|
self.subsys = subsys
|
59
61
|
self.tag = tag
|
60
62
|
self.head = head
|
63
|
+
|
61
64
|
def __str__(self):
|
62
65
|
s = self.subsys or "Something"
|
63
66
|
if head is None:
|
64
|
-
head="HEAD"
|
67
|
+
head = "HEAD"
|
65
68
|
else:
|
66
69
|
head = head.hexsha[:9]
|
67
70
|
return f"{s} changed between {tag.name} and {head}"
|
68
71
|
|
69
|
-
class _Common:
|
70
72
|
|
71
|
-
|
73
|
+
class _Common:
|
74
|
+
def next_tag(self, major: bool = False, minor: bool = False):
|
72
75
|
tag = self.last_tag
|
73
76
|
try:
|
74
|
-
n = [
|
77
|
+
n = [int(x) for x in tag.split(".")]
|
75
78
|
if len(n) != 3:
|
76
79
|
raise ValueError(n)
|
77
80
|
except ValueError:
|
78
81
|
raise ValueError(f"Tag {tag} not in major#.minor#.patch# form.") from None
|
79
82
|
|
80
83
|
if major:
|
81
|
-
n = [n[0]+1,0,0]
|
84
|
+
n = [n[0] + 1, 0, 0]
|
82
85
|
elif minor:
|
83
|
-
n = [n[0],n[1]+1,0]
|
86
|
+
n = [n[0], n[1] + 1, 0]
|
84
87
|
else:
|
85
|
-
n = [n[0],n[1],n[2]+1]
|
88
|
+
n = [n[0], n[1], n[2] + 1]
|
86
89
|
return ".".join(str(x) for x in n)
|
87
90
|
|
91
|
+
|
88
92
|
@define
|
89
93
|
class Package(_Common):
|
90
|
-
_repo:Repo = field(repr=False)
|
91
|
-
name:str = field()
|
92
|
-
under:str = field(init=False,repr=False)
|
93
|
-
path:Path = field(init=False,repr=False)
|
94
|
-
files:set(Path) = field(init=False,factory=set,repr=False)
|
95
|
-
subs:dict[str,Package] = field(factory=dict,init=False,repr=False)
|
96
|
-
hidden:bool = field(init=False,repr=False)
|
94
|
+
_repo: Repo = field(repr=False)
|
95
|
+
name: str = field()
|
96
|
+
under: str = field(init=False, repr=False)
|
97
|
+
path: Path = field(init=False, repr=False)
|
98
|
+
files: set(Path) = field(init=False, factory=set, repr=False)
|
99
|
+
subs: dict[str, Package] = field(factory=dict, init=False, repr=False)
|
100
|
+
hidden: bool = field(init=False, repr=False)
|
97
101
|
|
98
102
|
def __init__(self, repo, name):
|
99
|
-
self.__attrs_init__(repo,name)
|
100
|
-
self.under = name.replace(".","_")
|
103
|
+
self.__attrs_init__(repo, name)
|
104
|
+
self.under = name.replace(".", "_")
|
101
105
|
self.path = Path(*name.split("."))
|
102
|
-
self.hidden = not (PACK/self.dash).exists()
|
106
|
+
self.hidden = not (PACK / self.dash).exists()
|
103
107
|
|
104
108
|
@property
|
105
109
|
def dash(self):
|
106
110
|
return dash(self.name)
|
107
111
|
|
108
112
|
def __eq__(self, other):
|
109
|
-
return self.name==other.name
|
113
|
+
return self.name == other.name
|
110
114
|
|
111
115
|
def __hash__(self):
|
112
116
|
return hash(self.name)
|
113
117
|
|
118
|
+
@property
|
119
|
+
def verstr(self):
|
120
|
+
v=self.vers
|
121
|
+
return f"{v.tag}-{v.pkg}"
|
122
|
+
|
114
123
|
@property
|
115
124
|
def vers(self):
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
v = attrdict(
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
+
try:
|
126
|
+
v = self._repo.versions[self.dash]
|
127
|
+
except KeyError:
|
128
|
+
self._repo.versions[self.dash] = v = attrdict()
|
129
|
+
else:
|
130
|
+
if not isinstance(v, dict):
|
131
|
+
tag, commit = v
|
132
|
+
v = attrdict(
|
133
|
+
tag=tag,
|
134
|
+
pkg=1,
|
135
|
+
rev=commit,
|
136
|
+
)
|
137
|
+
self._repo.versions[self.dash] = v
|
125
138
|
return v
|
126
139
|
|
127
140
|
@vers.setter
|
128
|
-
def vers(self,d):
|
141
|
+
def vers(self, d):
|
129
142
|
v = self.vers
|
130
143
|
v.update(d)
|
131
144
|
return v
|
@@ -140,13 +153,13 @@ class Package(_Common):
|
|
140
153
|
|
141
154
|
@property
|
142
155
|
def mdash(self):
|
143
|
-
d=dash(self.name)
|
156
|
+
d = dash(self.name)
|
144
157
|
if d.startswith("ext-"):
|
145
158
|
return d[4:]
|
146
159
|
else:
|
147
|
-
return "moat-"+d
|
160
|
+
return "moat-" + d
|
148
161
|
|
149
|
-
def populate(self, path:Path, real=None):
|
162
|
+
def populate(self, path: Path, real=None):
|
150
163
|
"""
|
151
164
|
Collect this package's file names.
|
152
165
|
"""
|
@@ -154,7 +167,7 @@ class Package(_Common):
|
|
154
167
|
for fn in path.iterdir():
|
155
168
|
if fn.name == "__pycache__":
|
156
169
|
continue
|
157
|
-
if (sb := self.subs.get(fn.name,None)) is not None:
|
170
|
+
if (sb := self.subs.get(fn.name, None)) is not None:
|
158
171
|
sb.populate(fn, real=self if sb.hidden else None)
|
159
172
|
else:
|
160
173
|
(real or self).files.add(fn)
|
@@ -165,41 +178,49 @@ class Package(_Common):
|
|
165
178
|
"""
|
166
179
|
if not self.files:
|
167
180
|
raise ValueError(f"No files in {self.name}?")
|
168
|
-
p = Path("packaging")/self.dash
|
181
|
+
p = Path("packaging") / self.dash / "src"
|
169
182
|
with suppress(FileNotFoundError):
|
170
|
-
rmtree(p/"moat")
|
171
|
-
dest = p/self.path
|
183
|
+
rmtree(p / "moat")
|
184
|
+
dest = p / self.path
|
172
185
|
dest.mkdir(parents=True)
|
173
186
|
for f in self.files:
|
174
|
-
pf=p/f
|
175
|
-
pf.parent.mkdir(parents=True,exist_ok=True)
|
187
|
+
pf = p / f
|
188
|
+
pf.parent.mkdir(parents=True, exist_ok=True)
|
176
189
|
if f.is_dir():
|
177
190
|
copytree(f, pf, symlinks=False)
|
178
191
|
else:
|
179
192
|
copyfile(f, pf, follow_symlinks=True)
|
180
|
-
|
193
|
+
|
194
|
+
p = Path("packaging") / self.dash
|
195
|
+
licd = p / "LICENSE.txt"
|
181
196
|
if not licd.exists():
|
182
197
|
copyfile("LICENSE.txt", licd)
|
183
198
|
|
184
|
-
def has_changes(self, main:bool|None=None) -> bool:
|
199
|
+
def has_changes(self, main: bool | None = None) -> bool:
|
185
200
|
"""
|
186
201
|
Test whether the given subsystem changed
|
187
202
|
between the head and the @tag commit
|
188
203
|
"""
|
189
|
-
commit = self.last_commit
|
190
204
|
head = self._repo.head.commit
|
205
|
+
try:
|
206
|
+
lc = self.last_commit
|
207
|
+
except AttributeError:
|
208
|
+
return True
|
191
209
|
for d in head.diff(self.last_commit):
|
192
|
-
if
|
210
|
+
if (
|
211
|
+
self._repo.repo_for(d.a_path, main) != self.name
|
212
|
+
and self._repo.repo_for(d.b_path, main) != self.name
|
213
|
+
):
|
193
214
|
continue
|
194
215
|
return True
|
195
216
|
return False
|
196
217
|
|
197
218
|
|
198
|
-
class Repo(git.Repo,_Common):
|
219
|
+
class Repo(git.Repo, _Common):
|
199
220
|
"""Amend git.Repo with tag caching and pseudo-submodule splitting"""
|
200
221
|
|
201
222
|
moat_tag = None
|
202
|
-
_last_tag=None
|
223
|
+
_last_tag = None
|
203
224
|
|
204
225
|
def __init__(self, *a, **k):
|
205
226
|
super().__init__(*a, **k)
|
@@ -219,13 +240,12 @@ class Repo(git.Repo,_Common):
|
|
219
240
|
self.versions = yload(f, attr=True)
|
220
241
|
|
221
242
|
def write_tags(self):
|
222
|
-
with open("versions.yaml","w") as f:
|
223
|
-
yprint(self.versions,f)
|
243
|
+
with open("versions.yaml", "w") as f:
|
244
|
+
yprint(self.versions, f)
|
224
245
|
self.index.add("versions.yaml")
|
225
246
|
|
226
|
-
|
227
247
|
@property
|
228
|
-
def last_tag(self) -> Tag|None:
|
248
|
+
def last_tag(self) -> Tag | None:
|
229
249
|
"""
|
230
250
|
Return the most-recent tag for this repo
|
231
251
|
"""
|
@@ -240,7 +260,7 @@ class Repo(git.Repo,_Common):
|
|
240
260
|
self._last_tag = t
|
241
261
|
return t
|
242
262
|
|
243
|
-
raise ValueError(
|
263
|
+
raise ValueError("No tags found")
|
244
264
|
|
245
265
|
@property
|
246
266
|
def last_commit(self) -> str:
|
@@ -259,7 +279,7 @@ class Repo(git.Repo,_Common):
|
|
259
279
|
def parts(self):
|
260
280
|
return self._repos.values()
|
261
281
|
|
262
|
-
def tags_of(self, c:Commit) -> Sequence[Tag]:
|
282
|
+
def tags_of(self, c: Commit) -> Sequence[Tag]:
|
263
283
|
return self._commit_tags[c]
|
264
284
|
|
265
285
|
def _add_repo(self, name):
|
@@ -268,10 +288,10 @@ class Repo(git.Repo,_Common):
|
|
268
288
|
if dn in self._repos:
|
269
289
|
return self._repos[dn]
|
270
290
|
|
271
|
-
p = Package(self,pn)
|
291
|
+
p = Package(self, pn)
|
272
292
|
self._repos[dn] = p
|
273
293
|
if "." in pn:
|
274
|
-
par,nam = pn.rsplit(".",1)
|
294
|
+
par, nam = pn.rsplit(".", 1)
|
275
295
|
pp = self._add_repo(par)
|
276
296
|
pp.subs[nam] = p
|
277
297
|
return p
|
@@ -287,18 +307,18 @@ class Repo(git.Repo,_Common):
|
|
287
307
|
|
288
308
|
self._repos["main"].populate(Path("moat"))
|
289
309
|
|
290
|
-
def repo_for(self, path:Path|str, main:bool|None) -> str:
|
310
|
+
def repo_for(self, path: Path | str, main: bool | None) -> str:
|
291
311
|
"""
|
292
312
|
Given a file path, returns the subrepo in question
|
293
313
|
"""
|
294
314
|
sc = self._repos["main"]
|
295
|
-
path=Path(path)
|
315
|
+
path = Path(path)
|
296
316
|
|
297
317
|
if main is not False and path.parts[0] == "moat":
|
298
318
|
name = "moat"
|
299
319
|
for p in path.parts[1:]:
|
300
320
|
if p in sc.subs:
|
301
|
-
name += "."+p
|
321
|
+
name += "." + p
|
302
322
|
sc = sc.subs[p]
|
303
323
|
else:
|
304
324
|
break
|
@@ -312,7 +332,6 @@ class Repo(git.Repo,_Common):
|
|
312
332
|
|
313
333
|
return None
|
314
334
|
|
315
|
-
|
316
335
|
def commits(self, ref=None):
|
317
336
|
"""Iterate over topo sort of commits following @ref, or HEAD.
|
318
337
|
|
@@ -334,7 +353,7 @@ class Repo(git.Repo,_Common):
|
|
334
353
|
yield ref
|
335
354
|
work.extend(ref.parents)
|
336
355
|
|
337
|
-
def has_changes(self, main:bool|None=None) -> bool:
|
356
|
+
def has_changes(self, main: bool | None = None) -> bool:
|
338
357
|
"""
|
339
358
|
Test whether any subsystem changed since the "tagged" commit
|
340
359
|
|
@@ -348,8 +367,7 @@ class Repo(git.Repo,_Common):
|
|
348
367
|
return True
|
349
368
|
return False
|
350
369
|
|
351
|
-
|
352
|
-
def tagged(self, c:Commit=None) -> Tag|None:
|
370
|
+
def tagged(self, c: Commit = None) -> Tag | None:
|
353
371
|
"""Return a commit's tag name.
|
354
372
|
Defaults to the head commit.
|
355
373
|
Returns None if no tag, raises ValueError if more than one is found.
|
@@ -371,7 +389,6 @@ class Repo(git.Repo,_Common):
|
|
371
389
|
return tt[0].name
|
372
390
|
|
373
391
|
|
374
|
-
|
375
392
|
@click.group(short_help="Manage MoaT itself")
|
376
393
|
async def cli():
|
377
394
|
"""
|
@@ -393,22 +410,27 @@ def fix_deps(deps: list[str], tags: dict[str, str]) -> bool:
|
|
393
410
|
return work
|
394
411
|
|
395
412
|
|
396
|
-
def run_tests(pkg: str|None, *opts) -> bool:
|
413
|
+
def run_tests(pkg: str | None, *opts) -> bool:
|
397
414
|
"""Run subtests for subpackage @pkg."""
|
398
415
|
|
399
416
|
if pkg is None:
|
400
417
|
tests = Path("tests")
|
401
418
|
else:
|
402
|
-
tests = dash(pkg).replace("-","_")
|
403
|
-
tests = Path("tests")/tests
|
419
|
+
tests = dash(pkg).replace("-", "_")
|
420
|
+
tests = Path("tests") / tests
|
404
421
|
|
405
422
|
if not Path(tests):
|
406
423
|
# No tests. Assume it's OK.
|
407
424
|
return True
|
408
425
|
try:
|
409
426
|
print("\n*** Testing:", pkg)
|
410
|
-
|
411
|
-
|
427
|
+
subprocess.run(
|
428
|
+
["python3", "-mpytest", *opts, tests],
|
429
|
+
stdin=sys.stdin,
|
430
|
+
stdout=sys.stdout,
|
431
|
+
stderr=sys.stderr,
|
432
|
+
check=True,
|
433
|
+
)
|
412
434
|
except subprocess.CalledProcessError:
|
413
435
|
return False
|
414
436
|
else:
|
@@ -550,18 +572,32 @@ def apply_hooks(repo, force=False):
|
|
550
572
|
|
551
573
|
pt = Path(__file__).parent / "_hooks"
|
552
574
|
for f in pt.iterdir():
|
553
|
-
if not force:
|
554
|
-
|
555
|
-
continue
|
575
|
+
if not force and f.name in seen:
|
576
|
+
continue
|
556
577
|
t = h / f.name
|
557
578
|
d = f.read_text()
|
558
579
|
t.write_text(d)
|
559
580
|
t.chmod(0o755)
|
560
581
|
|
561
582
|
|
562
|
-
|
583
|
+
@cli.command
|
584
|
+
@click.argument("part", type=str)
|
585
|
+
def setup(part):
|
563
586
|
"""
|
564
|
-
|
587
|
+
Create a new MoaT subcommand.
|
588
|
+
"""
|
589
|
+
repo = Repo(None)
|
590
|
+
if "-" in part:
|
591
|
+
part = undash(part)
|
592
|
+
|
593
|
+
(Path("packaging") / dash(part)).mkdir()
|
594
|
+
(Path("packaging") / dash(part)).mkdir()
|
595
|
+
apply_templates(repo, part)
|
596
|
+
|
597
|
+
|
598
|
+
def apply_templates(repo: Repo, part):
|
599
|
+
"""
|
600
|
+
Apply template files to this component.
|
565
601
|
"""
|
566
602
|
commas = (
|
567
603
|
P("tool.tox.tox.envlist"),
|
@@ -569,14 +605,11 @@ def apply_templates(repo):
|
|
569
605
|
P("tool.pylint.messages_control.disable"),
|
570
606
|
)
|
571
607
|
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
rdot = ".".join(mtp)
|
578
|
-
rpath = "/".join(mtp)
|
579
|
-
runder = "_".join(mtp)
|
608
|
+
pt = part.split(".")
|
609
|
+
rname = dash(part)
|
610
|
+
rdot = part
|
611
|
+
rpath = "/".join(pt)
|
612
|
+
runder = "_".join(pt)
|
580
613
|
repl = Replace(
|
581
614
|
SUBNAME=rname,
|
582
615
|
SUBDOT=rdot,
|
@@ -584,7 +617,7 @@ def apply_templates(repo):
|
|
584
617
|
SUBUNDER=runder,
|
585
618
|
)
|
586
619
|
pt = (Path(__file__).parent / "_templates").joinpath
|
587
|
-
pr = Path(
|
620
|
+
pr = Path.cwd().joinpath
|
588
621
|
with pt("pyproject.forced.yaml").open("r") as f:
|
589
622
|
t1 = yload(f)
|
590
623
|
with pt("pyproject.default.yaml").open("r") as f:
|
@@ -592,27 +625,13 @@ def apply_templates(repo):
|
|
592
625
|
try:
|
593
626
|
with pr("pyproject.toml").open("r") as f:
|
594
627
|
proj = tomlkit.load(f)
|
595
|
-
try:
|
596
|
-
tx = proj["tool"]["tox"]["legacy_tox_ini"]
|
597
|
-
except KeyError:
|
598
|
-
pass
|
599
|
-
else:
|
600
|
-
txp = RawConfigParser()
|
601
|
-
txp.read_string(tx)
|
602
|
-
td = {}
|
603
|
-
for k, v in txp.items():
|
604
|
-
td[k] = ttd = dict()
|
605
|
-
for kk, vv in v.items():
|
606
|
-
if isinstance(vv, str) and vv[0] == "\n":
|
607
|
-
vv = [x.strip() for x in vv.strip().split("\n")]
|
608
|
-
ttd[kk] = vv
|
609
|
-
proj["tool"]["tox"] = td
|
610
628
|
|
611
629
|
for p in commas:
|
612
630
|
decomma(proj, p)
|
613
631
|
|
614
632
|
except FileNotFoundError:
|
615
633
|
proj = tomlkit.TOMLDocument()
|
634
|
+
|
616
635
|
mod = default_dict(t1, proj, t2, repl=repl, cls=tomlkit.items.Table)
|
617
636
|
try:
|
618
637
|
proc = proj["tool"]["moat"]["fixup"]
|
@@ -630,31 +649,7 @@ def apply_templates(repo):
|
|
630
649
|
for p in commas:
|
631
650
|
encomma(proj, p)
|
632
651
|
|
633
|
-
|
634
|
-
tx = proj["tool"]["tox"]
|
635
|
-
except KeyError:
|
636
|
-
pass
|
637
|
-
else:
|
638
|
-
txi = io.StringIO()
|
639
|
-
txp = RawConfigParser()
|
640
|
-
for k, v in tx.items():
|
641
|
-
if k != "DEFAULT":
|
642
|
-
txp.add_section(k)
|
643
|
-
for kk, vv in v.items():
|
644
|
-
if isinstance(vv, (tuple, list)):
|
645
|
-
vv = "\n " + "\n ".join(str(x) for x in vv)
|
646
|
-
txp.set(k, kk, vv)
|
647
|
-
txp.write(txi)
|
648
|
-
txi = txi.getvalue()
|
649
|
-
txi = "\n" + txi.replace("\n\t", "\n ")
|
650
|
-
proj["tool"]["tox"] = dict(
|
651
|
-
legacy_tox_ini=tomlkit.items.String.from_raw(
|
652
|
-
txi,
|
653
|
-
type_=tomlkit.items.StringType.MLB,
|
654
|
-
),
|
655
|
-
)
|
656
|
-
|
657
|
-
projp = Path(repo.working_dir) / "pyproject.toml"
|
652
|
+
projp = Path("packaging") / rname / "pyproject.toml"
|
658
653
|
projp.write_text(proj.as_string())
|
659
654
|
repo.index.add(projp)
|
660
655
|
|
@@ -678,6 +673,11 @@ def apply_templates(repo):
|
|
678
673
|
pr("moat", "__init__.py").write_text(init)
|
679
674
|
repo.index.add(pr("moat", "__init__.py"))
|
680
675
|
|
676
|
+
if not pr("moat", "_main.py").exists():
|
677
|
+
main = repl(pt("moat", "_main.py").read_text())
|
678
|
+
pr("moat", "_main.py").write_text(main)
|
679
|
+
repo.index.add(pr("moat", "_main.py"))
|
680
|
+
|
681
681
|
tst = pr("tests")
|
682
682
|
if not tst.is_dir():
|
683
683
|
tst.mkdir()
|
@@ -732,16 +732,17 @@ def tags():
|
|
732
732
|
else:
|
733
733
|
print(f"{r.dash} {tag}")
|
734
734
|
|
735
|
+
|
735
736
|
@cli.command()
|
736
737
|
@click.option("-r", "--run", is_flag=True, help="actually do the tagging")
|
737
738
|
@click.option("-m", "--minor", is_flag=True, help="create a new minor version")
|
738
739
|
@click.option("-M", "--major", is_flag=True, help="create a new major version")
|
739
740
|
@click.option("-s", "--subtree", type=str, help="Tag this partial module")
|
740
741
|
@click.option("-v", "--tag", "force", type=str, help="Use this explicit tag value")
|
741
|
-
@click.option("-q", "--query","--show","show", is_flag=True, help="Show the latest tag")
|
742
|
-
@click.option("-f", "--force","FORCE", is_flag=True, help="replace an existing tag")
|
742
|
+
@click.option("-q", "--query", "--show", "show", is_flag=True, help="Show the latest tag")
|
743
|
+
@click.option("-f", "--force", "FORCE", is_flag=True, help="replace an existing tag")
|
743
744
|
@click.option("-b", "--build", is_flag=True, help="set/increment the build number")
|
744
|
-
def tag(run,minor,major,subtree,force,FORCE,show,build):
|
745
|
+
def tag(run, minor, major, subtree, force, FORCE, show, build):
|
745
746
|
"""
|
746
747
|
Tag the repository (or a subtree).
|
747
748
|
|
@@ -782,48 +783,56 @@ def tag(run,minor,major,subtree,force,FORCE,show,build):
|
|
782
783
|
elif FORCE or build:
|
783
784
|
tag = r.last_tag
|
784
785
|
else:
|
785
|
-
tag = r.next_tag(major,minor)
|
786
|
+
tag = r.next_tag(major, minor)
|
786
787
|
|
787
788
|
if run or subtree:
|
788
789
|
if subtree:
|
789
790
|
sb = repo.part(r.dash)
|
790
791
|
if build:
|
791
792
|
sb.vers.pkg += 1
|
792
|
-
sb.vers.rev=repo.head.commit.hexsha
|
793
|
+
sb.vers.rev = repo.head.commit.hexsha
|
793
794
|
else:
|
794
795
|
sb.vers = attrdict(
|
795
796
|
tag=tag,
|
796
797
|
pkg=1,
|
797
798
|
rev=repo.head.commit.hexsha,
|
798
|
-
|
799
|
+
)
|
799
800
|
repo.write_tags()
|
800
801
|
else:
|
801
|
-
git.TagReference.create(repo,tag, force=FORCE)
|
802
|
+
git.TagReference.create(repo, tag, force=FORCE)
|
802
803
|
print(f"{tag}")
|
803
804
|
else:
|
804
805
|
print(f"{tag} DRY_RUN")
|
805
806
|
|
806
807
|
|
807
|
-
@cli.command(
|
808
|
+
@cli.command(
|
809
|
+
epilog="""
|
808
810
|
The default for building Debian packages is '--no-sign --build=binary'.
|
809
811
|
'--no-sign' is dropped when you use '--deb'.
|
810
812
|
The binary-only build is currently unconditional.
|
811
813
|
|
812
814
|
The default for uploading to Debian via 'dput' is '--unchecked ext';
|
813
815
|
it is dropped when you use '--dput'.
|
814
|
-
"""
|
816
|
+
""",
|
817
|
+
)
|
815
818
|
@click.option("-f", "--no-dirty", is_flag=True, help="don't check for dirtiness (DANGER)")
|
816
819
|
@click.option("-F", "--no-tag", is_flag=True, help="don't check for tag uptodate-ness (DANGER)")
|
817
820
|
@click.option("-D", "--no-deb", is_flag=True, help="don't build Debian packages")
|
818
821
|
@click.option("-C", "--no-commit", is_flag=True, help="don't commit the result")
|
819
|
-
@click.option(
|
822
|
+
@click.option(
|
823
|
+
"-V",
|
824
|
+
"--no-version",
|
825
|
+
is_flag=True,
|
826
|
+
help="don't update dependency versions in pyproject files",
|
827
|
+
)
|
820
828
|
@click.option("-P", "--no-pypi", is_flag=True, help="don't push to PyPI")
|
821
829
|
@click.option("-T", "--no-test", is_flag=True, help="don't run tests")
|
822
|
-
@click.option("-
|
823
|
-
@click.option("-
|
824
|
-
@click.option("-
|
830
|
+
@click.option("-G", "--test-chg", is_flag=True, help="rebuild if changes file doesn't exist")
|
831
|
+
@click.option("-o", "--pytest", "pytest_opts", type=str, multiple=True, help="Options for pytest")
|
832
|
+
@click.option("-d", "--deb", "deb_opts", type=str, multiple=True, help="Options for debuild")
|
833
|
+
@click.option("-p", "--dput", "dput_opts", type=str, multiple=True, help="Options for dput")
|
825
834
|
@click.option("-r", "--run", is_flag=True, help="actually do the tagging")
|
826
|
-
@click.option("-s", "--skip", "skip_", type=str,multiple=True, help="skip these repos")
|
835
|
+
@click.option("-s", "--skip", "skip_", type=str, multiple=True, help="skip these repos")
|
827
836
|
@click.option("-m", "--minor", is_flag=True, help="create a new minor version")
|
828
837
|
@click.option("-M", "--major", is_flag=True, help="create a new major version")
|
829
838
|
@click.option("-t", "--tag", "forcetag", type=str, help="Use this explicit tag value")
|
@@ -836,10 +845,38 @@ it is dropped when you use '--dput'.
|
|
836
845
|
help="Update external dependency",
|
837
846
|
)
|
838
847
|
@click.argument("parts", nargs=-1)
|
839
|
-
|
848
|
+
@click.pass_obj
|
849
|
+
async def build(
|
850
|
+
obj,
|
851
|
+
no_commit,
|
852
|
+
no_dirty,
|
853
|
+
no_test,
|
854
|
+
no_tag,
|
855
|
+
no_pypi,
|
856
|
+
parts,
|
857
|
+
dput_opts,
|
858
|
+
pytest_opts,
|
859
|
+
deb_opts,
|
860
|
+
run,
|
861
|
+
test_chg,
|
862
|
+
version,
|
863
|
+
no_version,
|
864
|
+
no_deb,
|
865
|
+
skip_,
|
866
|
+
major,
|
867
|
+
minor,
|
868
|
+
forcetag,
|
869
|
+
autotag,
|
870
|
+
):
|
840
871
|
"""
|
841
872
|
Rebuild all modified packages.
|
842
873
|
"""
|
874
|
+
cfg = obj.cfg
|
875
|
+
g_done = cfg.get("src", {}).get("done")
|
876
|
+
if g_done is not None:
|
877
|
+
g_done = Path(g_done)
|
878
|
+
else:
|
879
|
+
g_done = Path("/tmp/nonexistent")
|
843
880
|
repo = Repo(None)
|
844
881
|
|
845
882
|
tags = dict(version)
|
@@ -848,7 +885,7 @@ async def build(no_commit, no_dirty, no_test, no_tag, no_pypi, parts, dput_opts,
|
|
848
885
|
for sn in s.split(","):
|
849
886
|
skip.add(dash(sn))
|
850
887
|
parts = set(dash(s) for s in parts)
|
851
|
-
debversion={}
|
888
|
+
debversion = {}
|
852
889
|
|
853
890
|
if no_tag and not no_version:
|
854
891
|
print("Warning: not updating moat versions in pyproject files", file=sys.stderr)
|
@@ -860,28 +897,30 @@ async def build(no_commit, no_dirty, no_test, no_tag, no_pypi, parts, dput_opts,
|
|
860
897
|
raise click.UsageError("Can't use an explicit tag with changing minor or major!")
|
861
898
|
|
862
899
|
if forcetag is None:
|
863
|
-
forcetag = repo.next_tag(major,minor)
|
900
|
+
forcetag = repo.next_tag(major, minor)
|
864
901
|
|
865
|
-
full = False
|
866
902
|
if parts:
|
867
|
-
repos = [
|
903
|
+
repos = [repo.part(x) for x in parts]
|
868
904
|
else:
|
869
905
|
if not skip:
|
870
|
-
|
871
|
-
repos = [
|
906
|
+
pass
|
907
|
+
repos = [
|
908
|
+
x
|
909
|
+
for x in repo.parts
|
910
|
+
if not x.hidden and x.dash not in skip and not (PACK / x.dash / "SKIP").exists()
|
911
|
+
]
|
872
912
|
|
873
913
|
for name in PACK.iterdir():
|
874
914
|
if name.suffix != ".changes":
|
875
915
|
continue
|
876
|
-
name=name.stem
|
877
|
-
name,vers,_ = name.split("_")
|
916
|
+
name = name.stem
|
917
|
+
name, vers, _ = name.split("_")
|
878
918
|
if name.startswith("moat-"):
|
879
919
|
name = name[5:]
|
880
920
|
else:
|
881
|
-
name = "ext-"+name
|
882
|
-
debversion[name]=vers.rsplit("-",1)[0]
|
921
|
+
name = "ext-" + name
|
922
|
+
debversion[name] = vers.rsplit("-", 1)[0]
|
883
923
|
|
884
|
-
|
885
924
|
# Step 0: basic check
|
886
925
|
if not no_dirty:
|
887
926
|
if repo.is_dirty(index=False, working_tree=True, untracked_files=True, submodules=False):
|
@@ -895,14 +934,22 @@ async def build(no_commit, no_dirty, no_test, no_tag, no_pypi, parts, dput_opts,
|
|
895
934
|
if autotag:
|
896
935
|
for r in repos:
|
897
936
|
if r.has_changes(True):
|
937
|
+
try:
|
938
|
+
nt = r.next_tag()
|
939
|
+
except AttributeError:
|
940
|
+
nt = "1.0.0" if major else "0.1.0" if minor else "0.0.1"
|
898
941
|
r.vers = attrdict(
|
899
|
-
tag=
|
942
|
+
tag=nt,
|
900
943
|
pkg=1,
|
901
944
|
rev=repo.head.commit.hexsha,
|
902
|
-
|
945
|
+
)
|
946
|
+
logger.debug("Changes: %s %s",r.name,r.verstr)
|
903
947
|
elif r.has_changes(False):
|
904
948
|
r.vers.pkg += 1
|
905
|
-
r.vers.rev=repo.head.commit.hexsha
|
949
|
+
r.vers.rev = repo.head.commit.hexsha
|
950
|
+
logger.debug("Build Changes: %s %s",r.name,r.verstr)
|
951
|
+
else:
|
952
|
+
logger.debug("No Changes: %s %s",r.name,r.verstr)
|
906
953
|
|
907
954
|
elif not no_tag:
|
908
955
|
err = set()
|
@@ -910,7 +957,7 @@ async def build(no_commit, no_dirty, no_test, no_tag, no_pypi, parts, dput_opts,
|
|
910
957
|
try:
|
911
958
|
tag = r.last_tag
|
912
959
|
except KeyError:
|
913
|
-
rd = PACK/r.dash
|
960
|
+
rd = PACK / r.dash
|
914
961
|
p = rd / "pyproject.toml"
|
915
962
|
if not p.is_file():
|
916
963
|
continue
|
@@ -936,15 +983,15 @@ async def build(no_commit, no_dirty, no_test, no_tag, no_pypi, parts, dput_opts,
|
|
936
983
|
fails.add(p.name)
|
937
984
|
if fails:
|
938
985
|
if not run:
|
939
|
-
print(
|
986
|
+
print("*** Tests failed:", *fails, file=sys.stderr)
|
940
987
|
else:
|
941
|
-
print(
|
942
|
-
print(
|
988
|
+
print("Failed tests:", *fails, file=sys.stderr)
|
989
|
+
print("Fix and try again.", file=sys.stderr)
|
943
990
|
return
|
944
991
|
|
945
992
|
# Step 3: set version and fix versioned dependencies
|
946
993
|
for r in repos:
|
947
|
-
rd = PACK/r.dash
|
994
|
+
rd = PACK / r.dash
|
948
995
|
p = rd / "pyproject.toml"
|
949
996
|
if not p.is_file():
|
950
997
|
# bad=True
|
@@ -975,52 +1022,89 @@ async def build(no_commit, no_dirty, no_test, no_tag, no_pypi, parts, dput_opts,
|
|
975
1022
|
# Step 3: copy to packaging dir
|
976
1023
|
for r in repos:
|
977
1024
|
r.copy()
|
978
|
-
|
1025
|
+
|
979
1026
|
# Step 4: build Debian package
|
980
1027
|
if not no_deb:
|
981
1028
|
if not deb_opts:
|
982
1029
|
deb_opts = ["--no-sign"]
|
983
1030
|
|
984
1031
|
for r in repos:
|
985
|
-
rd=PACK/r.dash
|
1032
|
+
rd = PACK / r.dash
|
986
1033
|
p = rd / "debian"
|
987
1034
|
if not p.is_dir():
|
988
1035
|
continue
|
1036
|
+
if not (rd/"debian"/"changelog").exists():
|
1037
|
+
subprocess.run(
|
1038
|
+
[
|
1039
|
+
"debchange",
|
1040
|
+
"--create",
|
1041
|
+
"--newversion", f"{r.last_tag}-{r.vers.pkg}",
|
1042
|
+
"--package", r.mdash,
|
1043
|
+
f"Initial release for {forcetag}",
|
1044
|
+
],
|
1045
|
+
cwd=rd,
|
1046
|
+
check=True,
|
1047
|
+
stdout=sys.stdout,
|
1048
|
+
stderr=sys.stderr,
|
1049
|
+
)
|
1050
|
+
|
989
1051
|
try:
|
990
|
-
res = subprocess.run(
|
991
|
-
|
1052
|
+
res = subprocess.run(
|
1053
|
+
["dpkg-parsechangelog", "-l", "debian/changelog", "-S", "version"],
|
1054
|
+
cwd=rd,
|
1055
|
+
check=True,
|
1056
|
+
stdout=subprocess.PIPE,
|
1057
|
+
)
|
1058
|
+
tag,ptag = res.stdout.strip().decode("utf-8").rsplit("-", 1)
|
1059
|
+
ptag = int(ptag)
|
992
1060
|
ltag = r.last_tag
|
993
|
-
if tag != ltag:
|
994
|
-
subprocess.run(
|
995
|
-
|
1061
|
+
if tag != ltag or r.vers.pkg != ptag:
|
1062
|
+
subprocess.run(
|
1063
|
+
[
|
1064
|
+
"debchange",
|
1065
|
+
"--distribution",
|
1066
|
+
"unstable",
|
1067
|
+
"--newversion",
|
1068
|
+
f"{ltag}-{r.vers.pkg}",
|
1069
|
+
f"New release for {forcetag}",
|
1070
|
+
],
|
1071
|
+
cwd=rd,
|
1072
|
+
check=True,
|
1073
|
+
)
|
1074
|
+
repo.index.add(p / "changelog")
|
1075
|
+
|
1076
|
+
changes = PACK / f"{r.mdash}_{ltag}-{r.vers.pkg}_{ARCH}.changes"
|
1077
|
+
if debversion.get(r.dash, "") != ltag or r.vers.pkg != ptag or test_chg and not changes.exists():
|
996
1078
|
|
997
|
-
if debversion.get(r.dash,"") != ltag:
|
998
1079
|
subprocess.run(["debuild", "--build=binary"] + deb_opts, cwd=rd, check=True)
|
999
1080
|
except subprocess.CalledProcessError:
|
1000
1081
|
if not run:
|
1001
|
-
print("*** Failure packaging",r.name,file=sys.stderr)
|
1082
|
+
print("*** Failure packaging", r.name, file=sys.stderr)
|
1002
1083
|
else:
|
1003
|
-
print("Failure packaging",r.name,file=sys.stderr)
|
1004
|
-
|
1084
|
+
print("Failure packaging", r.name, file=sys.stderr)
|
1085
|
+
no_commit=True
|
1086
|
+
no_deb=True
|
1087
|
+
no_pypi=True
|
1005
1088
|
|
1006
1089
|
# Step 5: build PyPI package
|
1007
1090
|
if not no_pypi:
|
1008
|
-
err=set()
|
1009
|
-
up=set()
|
1091
|
+
err = set()
|
1092
|
+
up = set()
|
1010
1093
|
for r in repos:
|
1011
|
-
rd=PACK/r.dash
|
1094
|
+
rd = PACK / r.dash
|
1012
1095
|
p = rd / "pyproject.toml"
|
1013
1096
|
if not p.is_file():
|
1014
1097
|
continue
|
1015
1098
|
tag = r.last_tag
|
1016
1099
|
name = r.dash
|
1017
1100
|
if name.startswith("ext-"):
|
1018
|
-
name=name[4:]
|
1101
|
+
name = name[4:]
|
1019
1102
|
else:
|
1020
|
-
name="moat-"+r.dash
|
1103
|
+
name = "moat-" + r.dash
|
1104
|
+
|
1105
|
+
targz = rd / "dist" / f"{r.under}-{tag}.tar.gz"
|
1106
|
+
done = rd / "dist" / f"{r.under}-{tag}.done"
|
1021
1107
|
|
1022
|
-
targz = rd/"dist"/f"{r.under}-{tag}.tar.gz"
|
1023
|
-
done = rd/"dist"/f"{r.under}-{tag}.done"
|
1024
1108
|
if targz.is_file():
|
1025
1109
|
if not done.exists():
|
1026
1110
|
up.add(r)
|
@@ -1039,50 +1123,60 @@ async def build(no_commit, no_dirty, no_test, no_tag, no_pypi, parts, dput_opts,
|
|
1039
1123
|
print("Build errors:", file=sys.stderr)
|
1040
1124
|
print(*err, file=sys.stderr)
|
1041
1125
|
print("Please fix and try again.", file=sys.stderr)
|
1042
|
-
|
1043
|
-
|
1126
|
+
no_commit=True
|
1127
|
+
no_deb=True
|
1128
|
+
|
1044
1129
|
# Step 6: upload PyPI package
|
1045
|
-
|
1046
|
-
err=set()
|
1130
|
+
elif run:
|
1131
|
+
err = set()
|
1047
1132
|
for r in up:
|
1048
|
-
rd=PACK/r.dash
|
1133
|
+
rd = PACK / r.dash
|
1049
1134
|
p = rd / "pyproject.toml"
|
1050
1135
|
if not p.is_file():
|
1051
1136
|
continue
|
1052
1137
|
tag = r.last_tag
|
1053
1138
|
name = r.dash
|
1054
1139
|
if name.startswith("ext-"):
|
1055
|
-
name=name[4:]
|
1140
|
+
name = name[4:]
|
1056
1141
|
else:
|
1057
|
-
name="moat-"+r.dash
|
1058
|
-
targz = Path("dist")/f"{r.under}-{tag}.tar.gz"
|
1059
|
-
whl = Path("dist")/f"{r.under}-{tag}-py3-none-any.whl"
|
1142
|
+
name = "moat-" + r.dash
|
1143
|
+
targz = Path("dist") / f"{r.under}-{tag}.tar.gz"
|
1144
|
+
whl = Path("dist") / f"{r.under}-{tag}-py3-none-any.whl"
|
1060
1145
|
try:
|
1061
|
-
res = subprocess.run(
|
1146
|
+
res = subprocess.run(
|
1147
|
+
["twine", "upload", str(targz), str(whl)],
|
1148
|
+
cwd=rd,
|
1149
|
+
check=True,
|
1150
|
+
)
|
1062
1151
|
except subprocess.CalledProcessError:
|
1063
1152
|
err.add(r.name)
|
1064
1153
|
else:
|
1065
|
-
done = rd/"dist"/f"{r.under}-{tag}.done"
|
1154
|
+
done = rd / "dist" / f"{r.under}-{tag}.done"
|
1066
1155
|
done.touch()
|
1067
1156
|
if err:
|
1068
1157
|
print("Upload errors:", file=sys.stderr)
|
1069
1158
|
print(*err, file=sys.stderr)
|
1070
1159
|
print("Please fix(?) and try again.", file=sys.stderr)
|
1071
|
-
|
1160
|
+
no_commit=True
|
1161
|
+
no_deb=True
|
1072
1162
|
|
1073
1163
|
# Step 7: upload Debian package
|
1074
1164
|
if run and not no_deb:
|
1075
1165
|
err = set()
|
1076
1166
|
if not dput_opts:
|
1077
|
-
dput_opts = ["-u","ext"]
|
1167
|
+
dput_opts = ["-u", "ext"]
|
1078
1168
|
for r in repos:
|
1079
1169
|
ltag = r.last_tag
|
1080
|
-
if not (PACK/r.dash/"debian").is_dir():
|
1170
|
+
if not (PACK / r.dash / "debian").is_dir():
|
1081
1171
|
continue
|
1082
|
-
changes = PACK/f"{r.mdash}_{ltag}-{r.vers.pkg}_{ARCH}.changes"
|
1083
|
-
done = PACK/f"{r.mdash}_{ltag}-{r.vers.pkg}_{ARCH}.done"
|
1172
|
+
changes = PACK / f"{r.mdash}_{ltag}-{r.vers.pkg}_{ARCH}.changes"
|
1173
|
+
done = PACK / f"{r.mdash}_{ltag}-{r.vers.pkg}_{ARCH}.done"
|
1084
1174
|
if done.exists():
|
1085
1175
|
continue
|
1176
|
+
if g_done is not None:
|
1177
|
+
gdone = g_done / f"{r.mdash}_{ltag}-{r.vers.pkg}_{ARCH}.done"
|
1178
|
+
if gdone.exists():
|
1179
|
+
continue
|
1086
1180
|
try:
|
1087
1181
|
subprocess.run(["dput", *dput_opts, str(changes)], check=True)
|
1088
1182
|
except subprocess.CalledProcessError:
|
@@ -1093,7 +1187,7 @@ async def build(no_commit, no_dirty, no_test, no_tag, no_pypi, parts, dput_opts,
|
|
1093
1187
|
print("Upload errors:", file=sys.stderr)
|
1094
1188
|
print(*err, file=sys.stderr)
|
1095
1189
|
print("Please fix(?) and try again.", file=sys.stderr)
|
1096
|
-
|
1190
|
+
no_commit=True
|
1097
1191
|
|
1098
1192
|
# Step 8: commit the result
|
1099
1193
|
if run:
|