@@ -45,33 +45,26 @@ def material_insiders() -> Iterator[bool]: # noqa: D103
4545
4646
4747@duty
48- def changelog (ctx : Context ) -> None :
48+ def changelog (ctx : Context , bump : str = "" ) -> None :
4949 """Update the changelog in-place with latest commits.
5050
5151 Parameters:
52- ctx: The context instance ( passed automatically) .
52+ bump: Bump option passed to git-changelog .
5353 """
5454 from git_changelog .cli import main as git_changelog
5555
56- ctx .run (git_changelog , args = [[]], title = "Updating changelog" )
56+ args = [f"--bump={ bump } " ] if bump else []
57+ ctx .run (git_changelog , args = [args ], title = "Updating changelog" , command = "git-changelog" )
5758
5859
5960@duty (pre = ["check_quality" , "check_types" , "check_docs" , "check_dependencies" , "check-api" ])
6061def check (ctx : Context ) -> None : # noqa: ARG001
61- """Check it all!
62-
63- Parameters:
64- ctx: The context instance (passed automatically).
65- """
62+ """Check it all!"""
6663
6764
6865@duty
6966def check_quality (ctx : Context ) -> None :
70- """Check the code quality.
71-
72- Parameters:
73- ctx: The context instance (passed automatically).
74- """
67+ """Check the code quality."""
7568 ctx .run (
7669 ruff .check (* PY_SRC_LIST , config = "config/ruff.toml" ),
7770 title = pyprefix ("Checking code quality" ),
@@ -81,11 +74,7 @@ def check_quality(ctx: Context) -> None:
8174
8275@duty
8376def check_dependencies (ctx : Context ) -> None :
84- """Check for vulnerabilities in dependencies.
85-
86- Parameters:
87- ctx: The context instance (passed automatically).
88- """
77+ """Check for vulnerabilities in dependencies."""
8978 # retrieve the list of dependencies
9079 requirements = ctx .run (
9180 ["uv" , "pip" , "freeze" ],
@@ -102,11 +91,7 @@ def check_dependencies(ctx: Context) -> None:
10291
10392@duty
10493def check_docs (ctx : Context ) -> None :
105- """Check if the documentation builds correctly.
106-
107- Parameters:
108- ctx: The context instance (passed automatically).
109- """
94+ """Check if the documentation builds correctly."""
11095 Path ("htmlcov" ).mkdir (parents = True , exist_ok = True )
11196 Path ("htmlcov/index.html" ).touch (exist_ok = True )
11297 with material_insiders ():
@@ -119,11 +104,7 @@ def check_docs(ctx: Context) -> None:
119104
120105@duty
121106def check_types (ctx : Context ) -> None :
122- """Check that the code is correctly typed.
123-
124- Parameters:
125- ctx: The context instance (passed automatically).
126- """
107+ """Check that the code is correctly typed."""
127108 os .environ ["MYPYPATH" ] = "src"
128109 ctx .run (
129110 mypy .run (* PY_SRC_LIST , config_file = "config/mypy.ini" ),
@@ -134,11 +115,7 @@ def check_types(ctx: Context) -> None:
134115
135116@duty
136117def check_api (ctx : Context ) -> None :
137- """Check for API breaking changes.
138-
139- Parameters:
140- ctx: The context instance (passed automatically).
141- """
118+ """Check for API breaking changes."""
142119 from griffe .cli import check as g_check
143120
144121 griffe_check = lazy (g_check , name = "griffe.check" )
@@ -150,31 +127,11 @@ def check_api(ctx: Context) -> None:
150127 )
151128
152129
153- @duty (silent = True )
154- def clean (ctx : Context ) -> None :
155- """Delete temporary files.
156-
157- Parameters:
158- ctx: The context instance (passed automatically).
159- """
160- def _rm (* targets : str ) -> None :
161- for target in targets :
162- ctx .run (f"rm -rf { target } " )
163-
164- def _find_rm (* targets : str ) -> None :
165- for target in targets :
166- ctx .run (f"find . -type d -name '{ target } ' | xargs rm -rf" )
167-
168- _rm ("build" , "dist" , ".coverage*" , "htmlcov" , "site" , ".pdm-build" )
169- _find_rm (".cache" , ".pytest_cache" , ".mypy_cache" , ".ruff_cache" , "__pycache__" )
170-
171-
172130@duty
173131def docs (ctx : Context , host : str = "127.0.0.1" , port : int = 8000 ) -> None :
174132 """Serve the documentation (localhost:8000).
175133
176134 Parameters:
177- ctx: The context instance (passed automatically).
178135 host: The host to serve the docs from.
179136 port: The port to serve the docs on.
180137 """
@@ -188,11 +145,7 @@ def docs(ctx: Context, host: str = "127.0.0.1", port: int = 8000) -> None:
188145
189146@duty
190147def docs_deploy (ctx : Context ) -> None :
191- """Deploy the documentation on GitHub pages.
192-
193- Parameters:
194- ctx: The context instance (passed automatically).
195- """
148+ """Deploy the documentation on GitHub pages."""
196149 os .environ ["DEPLOY" ] = "true"
197150 with material_insiders () as insiders :
198151 if not insiders :
@@ -218,24 +171,50 @@ def docs_deploy(ctx: Context) -> None:
218171
219172@duty
220173def format (ctx : Context ) -> None :
221- """Run formatting tools on the code.
222-
223- Parameters:
224- ctx: The context instance (passed automatically).
225- """
174+ """Run formatting tools on the code."""
226175 ctx .run (
227176 ruff .check (* PY_SRC_LIST , config = "config/ruff.toml" , fix_only = True , exit_zero = True ),
228177 title = "Auto-fixing code" ,
229178 )
230179 ctx .run (ruff .format (* PY_SRC_LIST , config = "config/ruff.toml" ), title = "Formatting code" )
231180
232181
233- @duty (post = ["docs-deploy" ])
234- def release (ctx : Context , version : str ) -> None :
182+ @duty
183+ def build (ctx : Context ) -> None :
184+ """Build source and wheel distributions."""
185+ from build .__main__ import main as pyproject_build
186+
187+ ctx .run (
188+ pyproject_build ,
189+ args = [()],
190+ title = "Building source and wheel distributions" ,
191+ command = "pyproject-build" ,
192+ pty = PTY ,
193+ )
194+
195+
196+ @duty
197+ def publish (ctx : Context ) -> None :
198+ """Publish source and wheel distributions to PyPI."""
199+ from twine .cli import dispatch as twine_upload
200+
201+ if not Path ("dist" ).exists ():
202+ ctx .run ("false" , title = "No distribution files found" )
203+ dists = [str (dist ) for dist in Path ("dist" ).iterdir ()]
204+ ctx .run (
205+ twine_upload ,
206+ args = [["upload" , "-r" , "pypi" , "--skip-existing" , * dists ]],
207+ title = "Publish source and wheel distributions to PyPI" ,
208+ command = "twine upload -r pypi --skip-existing dist/*" ,
209+ pty = PTY ,
210+ )
211+
212+
213+ @duty (post = ["build" , "publish" , "docs-deploy" ])
214+ def release (ctx : Context , version : str = "" ) -> None :
235215 """Release a new Python package.
236216
237217 Parameters:
238- ctx: The context instance (passed automatically).
239218 version: The new version number to use.
240219 """
241220 origin = ctx .run ("git config --get remote.origin.url" , silent = True )
@@ -244,22 +223,18 @@ def release(ctx: Context, version: str) -> None:
244223 lambda : False ,
245224 title = "Not releasing from insiders repository (do that from public repo instead!)" ,
246225 )
226+ if not (version := (version or input ("> Version to release: " )).strip ()):
227+ ctx .run ("false" , title = "A version must be provided" )
247228 ctx .run ("git add pyproject.toml CHANGELOG.md" , title = "Staging files" , pty = PTY )
248229 ctx .run (["git" , "commit" , "-m" , f"chore: Prepare release { version } " ], title = "Committing changes" , pty = PTY )
249230 ctx .run (f"git tag { version } " , title = "Tagging commit" , pty = PTY )
250231 ctx .run ("git push" , title = "Pushing commits" , pty = False )
251232 ctx .run ("git push --tags" , title = "Pushing tags" , pty = False )
252- ctx .run ("pyproject-build" , title = "Building dist/wheel" , pty = PTY )
253- ctx .run ("twine upload --skip-existing dist/*" , title = "Publishing version" , pty = PTY )
254233
255234
256235@duty (silent = True , aliases = ["coverage" ])
257236def cov (ctx : Context ) -> None :
258- """Report coverage as text and HTML.
259-
260- Parameters:
261- ctx: The context instance (passed automatically).
262- """
237+ """Report coverage as text and HTML."""
263238 ctx .run (coverage .combine , nofail = True )
264239 ctx .run (coverage .report (rcfile = "config/coverage.ini" ), capture = False )
265240 ctx .run (coverage .html (rcfile = "config/coverage.ini" ))
@@ -270,7 +245,6 @@ def test(ctx: Context, match: str = "") -> None:
270245 """Run the test suite.
271246
272247 Parameters:
273- ctx: The context instance (passed automatically).
274248 match: A pytest expression to filter selected tests.
275249 """
276250 py_version = f"{ sys .version_info .major } { sys .version_info .minor } "
@@ -280,28 +254,3 @@ def test(ctx: Context, match: str = "") -> None:
280254 title = pyprefix ("Running tests" ),
281255 command = f"pytest -c config/pytest.ini -n auto -k{ match !r} --color=yes tests" ,
282256 )
283-
284-
285- @duty
286- def vscode (ctx : Context ) -> None :
287- """Configure VSCode.
288-
289- This task will overwrite the following files,
290- so make sure to back them up:
291-
292- - `.vscode/launch.json`
293- - `.vscode/settings.json`
294- - `.vscode/tasks.json`
295-
296- Parameters:
297- ctx: The context instance (passed automatically).
298- """
299-
300- def update_config (filename : str ) -> None :
301- source_file = Path ("config" , "vscode" , filename )
302- target_file = Path (".vscode" , filename )
303- target_file .parent .mkdir (exist_ok = True )
304- target_file .write_text (source_file .read_text ())
305-
306- for filename in ("launch.json" , "settings.json" , "tasks.json" ):
307- ctx .run (update_config , args = [filename ], title = f"Update .vscode/{ filename } " )
0 commit comments