Repository navigation
Expand file tree
/
Copy pathtasks.py
More file actions
336 lines (284 loc) · 10.7 KB
/
Copy pathtasks.py
File metadata and controls
336 lines (284 loc) · 10.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
import shlex
from invoke import Context, task
@task
def test(c: Context) -> None:
"""Run the test suite with coverage reporting.
This task executes ``pytest`` and generates a coverage report.
Args:
c: Invoke context used to run shell commands.
Raises:
TypeError: If ``c`` is not an Invoke :class:`Context`.
"""
# Ensure we were passed a valid Context object.
if not isinstance(c, Context):
raise TypeError(f"Expected Invoke Context, got {type(c).__name__!r} instead")
# Build the command string. You can adjust '--cov=gen_surv' if you
# need to cover a different package or add extra pytest flags.
command = (
"poetry run pytest " "--cov=gen_surv " "--cov-report=term " "--cov-report=xml"
)
# Run pytest.
# - warn=True: capture non-zero exit codes without aborting Invoke.
# - pty=False: pytest doesn’t require an interactive TTY here.
result = c.run(command, warn=True, pty=False)
# Check the exit code and report accordingly.
if result is not None and getattr(result, "ok", False):
print("✔️ All tests passed.")
else:
print("❌ Some tests failed.")
exit_code = (
result.exited
if result is not None and hasattr(result, "exited")
else "Unknown"
)
print(f"Exit code: {exit_code}")
stderr_output = (
result.stderr if result is not None and hasattr(result, "stderr") else None
)
if stderr_output:
print("Error output:")
print(stderr_output)
@task
def docs(c: Context) -> None:
"""Build the MkDocs documentation.
Args:
c: Invoke context used to run shell commands.
Raises:
TypeError: If ``c`` is not an Invoke :class:`Context`.
"""
# Verify we have a proper Invoke Context.
if not isinstance(c, Context):
raise TypeError(f"Expected Invoke Context, got {type(c).__name__!r}")
# --strict matches the CI build: warnings such as a broken internal link or
# an unresolved mkdocstrings reference fail rather than shipping quietly.
command = "poetry run mkdocs build --strict --site-dir site"
# Execute the build.
# - warn=True: capture non-zero exits without immediately aborting Invoke.
# - pty=False: mkdocs does not require interactive input.
result = c.run(command, warn=True, pty=False)
# Report on the result of the documentation build.
if result is not None and getattr(result, "ok", False):
print("✔️ Documentation built successfully.")
else:
print("❌ Documentation build failed.")
exit_code = (
result.exited
if result is not None and hasattr(result, "exited")
else "Unknown"
)
print(f"Exit code: {exit_code}")
stderr_output = (
result.stderr if result is not None and hasattr(result, "stderr") else None
)
if stderr_output:
print("Error output:")
print(stderr_output)
@task
def stubs(c: Context) -> None:
"""Generate type stubs for the ``gen_surv`` package.
Args:
c: Invoke context used to run shell commands.
Raises:
TypeError: If ``c`` is not an Invoke :class:`Context`.
"""
# Verify that 'c' is the correct Invoke Context.
if not isinstance(c, Context):
raise TypeError(f"Expected Invoke Context, got {type(c).__name__!r}")
# Build the stubgen command. Adjust '-p gen_surv' or output path if needed.
command = "poetry run stubgen -p gen_surv -o stubs"
# Execute stubgen.
# - warn=True: capture non-zero exit codes without aborting Invoke.
# - pty=False: stubgen does not require interactive input.
result = c.run(command, warn=True, pty=False)
# Report on the outcome of stub generation.
if result is not None and getattr(result, "ok", False):
print("✔️ Type stubs generated successfully in 'stubs/'.")
else:
print("❌ Stub generation failed.")
exit_code = (
result.exited
if result is not None and hasattr(result, "exited")
else "Unknown"
)
print(f"Exit code: {exit_code}")
stderr_output = (
result.stderr if result is not None and hasattr(result, "stderr") else None
)
if stderr_output:
print("Error output:")
print(stderr_output)
@task
def build(c: Context) -> None:
"""Build distribution artifacts using Poetry.
Args:
c: Invoke context used to run shell commands.
Raises:
TypeError: If ``c`` is not an Invoke :class:`Context`.
"""
# Verify that we received a valid Invoke Context.
if not isinstance(c, Context):
raise TypeError(f"Expected Invoke Context, got {type(c).__name__!r}")
# Construct the build command. Adjust if you need custom build options.
command = "poetry build"
# Execute the build.
# - warn=True: capture non-zero exit codes without aborting Invoke.
# - pty=False: no interactive input is required for building.
result = c.run(command, warn=True, pty=False)
# Report the result of the build process.
if result is not None and getattr(result, "ok", False):
print(
"✔️ Build completed successfully. Artifacts are in the 'dist/' directory."
)
else:
print("❌ Build failed.")
exit_code = (
result.exited
if result is not None and hasattr(result, "exited")
else "Unknown"
)
print(f"Exit code: {exit_code}")
stderr_output = (
result.stderr if result is not None and hasattr(result, "stderr") else None
)
if stderr_output:
print("Error output:")
print(stderr_output)
@task
def publish(c: Context) -> None:
"""Build and upload the package to PyPI.
Args:
c: Invoke context used to run shell commands.
"""
# Run the poetry publish command.
# - warn=True: do not abort on non-zero exit, so we can inspect and report.
# - pty=True: allocate a pseudo-TTY for interactive prompts (username/password, etc.).
result = c.run(
"poetry publish --build",
warn=True,
pty=True,
)
# If the exit code is zero, the publish succeeded.
if result.ok:
print("✔️ Package published successfully.")
return
# Otherwise, print out details to help debug.
print("❌ Poetry publish failed.")
print(f"Exit code: {result.exited}")
if result.stderr:
print("Error output:")
print(result.stderr)
else:
print("No stderr output captured.")
@task
def clean(c: Context) -> None:
"""Remove build artifacts and caches.
Args:
c: Invoke context used to run shell commands.
Raises:
TypeError: If ``c`` is not an Invoke :class:`Context`.
"""
# Verify the argument is an Invoke Context.
if not isinstance(c, Context):
raise TypeError(f"Expected Invoke Context, got {type(c).__name__!r}")
# List of paths and files to remove. Adjust if you add new artifacts.
targets = [
"dist",
"build",
"docs/build",
".pytest_cache",
".mypy_cache",
"coverage.xml",
".coverage",
"stubs",
]
# Join targets into a single rm command.
# Using '-rf' to force removal without prompts.
command = f"rm -rf {' '.join(targets)}"
# Execute the cleanup command.
# - warn=True: capture non-zero exits without aborting Invoke.
# - pty=False: no interactive input is required.
result = c.run(command, warn=True, pty=False)
# Report the outcome of the cleanup.
if result.ok:
print("✔️ Cleaned all build artifacts and caches.")
else:
print("❌ Cleanup failed for some targets.")
print(f"Exit code: {result.exited}")
if result.stderr:
print("Error output:")
print(result.stderr)
@task
def gitpush(c: Context) -> None:
"""Commit and push all staged changes.
Args:
c: Invoke context used to run shell commands.
Raises:
TypeError: If ``c`` is not an Invoke :class:`Context`.
"""
# Verify the argument is a valid Invoke Context.
if not isinstance(c, Context):
raise TypeError(f"Expected Invoke Context, got {type(c).__name__!r}")
# Stage all changes.
result_add = c.run("git add .", warn=True, pty=False)
if result_add is None or not getattr(result_add, "ok", False):
print("❌ Failed to stage changes (git add).")
exit_code = (
result_add.exited
if result_add is not None and hasattr(result_add, "exited")
else "Unknown"
)
print(f"Exit code: {exit_code}")
stderr_output = (
result_add.stderr
if result_add is not None and hasattr(result_add, "stderr")
else None
)
if stderr_output:
print("Error output:")
print(stderr_output)
return
try:
# Prompt for a commit message.
message = input("Enter commit message: ").strip()
if not message:
print("Aborting: empty commit message.")
return
# Sanitize the message to prevent shell injection.
sanitized_message = shlex.quote(message)
# Create a signed commit. Use a pseudo-TTY so GPG passphrase can be entered if needed.
result_commit = c.run(
f"git commit -S -m {sanitized_message}",
warn=True,
pty=True,
)
if result_commit is None or not getattr(result_commit, "ok", False):
print("❌ Commit failed.")
exit_code = getattr(result_commit, "exited", "Unknown")
print(f"Exit code: {exit_code}")
stderr_output = getattr(result_commit, "stderr", None)
if stderr_output:
print("Error output:")
print(stderr_output)
return
# Push to the remote repository.
result_push = c.run("git push", warn=True, pty=False)
if result_push is not None and getattr(result_push, "ok", False):
print("✔️ Changes pushed successfully.")
else:
print("❌ Push failed.")
exit_code = (
getattr(result_push, "exited", "Unknown")
if result_push is not None
else "Unknown"
)
print(f"Exit code: {exit_code}")
stderr_output = (
getattr(result_push, "stderr", None)
if result_push is not None
else None
)
if stderr_output:
print("Error output:")
print(stderr_output)
except KeyboardInterrupt:
print("\nAborted by user.")