Skip to content

Commit cebe6c6

Browse files
mattkramAnaconda Bot
and
Anaconda Bot
authored
feat: Run hooks on self and add really nice CLI output to README.md (#72)
* Exclude coverage output in any directory * Add self hooks to .pre-commig-config.yaml * Add cog script to generate CLI help text output * Add really nice CLI help output * Fix entrypoints * Ignore targets injected from Github actions * chore(pre-commit): linting * Try disabling markdown * chore(pre-commit): linting * Revert "Try disabling markdown" This reverts commit 7680cfd. * Disable actual cogging until we can fix formatting issues * chore(pre-commit): linting * Also cog if Makefile changes * Invoke the callback without a command --------- Co-authored-by: Anaconda Bot <[email protected]>
1 parent b62fcbc commit cebe6c6

8 files changed

+152
-21
lines changed

Diff for: .gitignore

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ __pycache__/
77
env/
88

99
# Test coverage output
10-
/.coverage
11-
/coverage.xml
10+
.coverage
11+
coverage.xml

Diff for: .pre-commit-config.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ repos:
66
- id: end-of-file-fixer
77
- id: check-json
88
- id: check-yaml
9+
- repo: https://github.com/anaconda/pre-commit-hooks
10+
rev: v24.5.2
11+
hooks:
12+
- id: cog
13+
files: README.md|Makefile
14+
- id: generate-renovate-annotations
915
- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
1016
rev: v2.13.0
1117
hooks:

Diff for: Makefile

+3
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,7 @@ type-check: ## Run static type checks
3434
test: ## Run all the unit tests
3535
$(conda_run) pytest
3636

37+
cog-readme: ## Run cog on the README.md to generate command output
38+
$(conda_run) run-cog README.md
39+
3740
.PHONY: $(MAKECMDGOALS)

Diff for: README.md

+62-8
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,59 @@ An example usage is shown below:
3232
]
3333
```
3434

35+
The hook is backed by a CLI command, whose help output is reproduced below:
36+
37+
<!-- [[[cog
38+
#import os, sys; sys.path.insert(0, os.path.join(os.getcwd(), "dev"))
39+
#from generate_cli_output import main
40+
#main(command="generate-renovate-annotations --help")
41+
]]] -->
42+
<!-- [[[end]]] -->
43+
```shell
44+
Usage: generate-renovate-annotations [OPTIONS] ENV_FILES... COMMAND [ARGS]...
45+
46+
Generate Renovate comments for a list of conda environment files.
47+
For each file, we:
48+
49+
• Run a command to ensure the environment is created/updated
50+
• Extract a list of installed packages in that environment, including pip
51+
• Generate a Renovate annotation comment, including the package name and
52+
channel. This step also allows for overriding the index of pip packages.
53+
• Pin the exact installed version of each dependency.
54+
55+
╭─ Arguments ──────────────────────────────────────────────────────────────────╮
56+
* env_files ENV_FILES... A list of conda environment files, │
57+
│ typically passed in from pre-commit │
58+
│ automatically │
59+
│ [default: None] │
60+
│ [required] │
61+
╰──────────────────────────────────────────────────────────────────────────────╯
62+
╭─ Options ────────────────────────────────────────────────────────────────────╮
63+
│ --internal-pip-package TEXT One or more packages to pull │
64+
│ from the │
65+
│ --internal-pip-index-url │
66+
│ [default: None] │
67+
│ --internal-pip-index-url TEXT An optional extra pip index URL, │
68+
│ used in conjunction with the │
69+
│ --internal-pip-package option │
70+
│ --create-command TEXT A command to invoke at each │
71+
│ parent directory of all │
72+
│ environment files to ensure the │
73+
│ conda environment is created and │
74+
│ updated │
75+
│ [default: make setup] │
76+
│ --environment-selector TEXT A string used to select the
77+
│ conda environment, either │
78+
│ prefix-based (recommended) or │
79+
│ named │
80+
│ [default: -p ./env] │
81+
│ --disable-environment-creation If set, environment will not be │
82+
│ created/updated before │
83+
│ annotations are added. │
84+
│ --help Show this message and exit. │
85+
╰──────────────────────────────────────────────────────────────────────────────╯
86+
```
87+
3588
## run-cog
3689
3790
The `run-cog` hook can be used to run the [`cog`](https://nedbatchelder.com/code/cog) tool automatically to generate code when committing a file.
@@ -92,14 +145,15 @@ import os, sys; sys.path.insert(0, os.path.join(os.getcwd(), "dev"))
92145
from generate_makefile_targets_table import main; main()
93146
]]] -->
94147
<!-- THE FOLLOWING CODE IS GENERATED BY COG VIA PRE-COMMIT. ANY MANUAL CHANGES WILL BE LOST. -->
95-
| Target | Description |
96-
|-----------------|-----------------------------------------------|
97-
| `help` | Display help on all Makefile targets |
98-
| `setup` | Setup local conda environment for development |
99-
| `install-hooks` | Download + install all pre-commit hooks |
100-
| `pre-commit` | Run pre-commit against all files |
101-
| `type-check` | Run static type checks |
102-
| `test` | Run all the unit tests |
148+
| Target | Description |
149+
|-----------------|--------------------------------------------------------------------------|
150+
| `help` | Display help on all Makefile targets |
151+
| `setup` | Setup local conda environment for development |
152+
| `install-hooks` | Download + install all pre-commit hooks |
153+
| `pre-commit` | Run pre-commit against all files |
154+
| `type-check` | Run static type checks |
155+
| `test` | Run all the unit tests |
156+
| `cog-readme` | Run cog on the README.md to generate command output |
103157
<!-- [[[end]]] -->
104158
105159
> **Note:** Interestingly, the table above is generated by the `cog` hook defined in this repo :smile:

Diff for: dev/generate_cli_output.py

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import shlex
2+
import subprocess
3+
4+
import cog
5+
6+
OUTPUT_STR_FORMAT = """\
7+
```{language}
8+
{text}
9+
```\
10+
"""
11+
12+
13+
def main(command: str, language: str = "shell"):
14+
"""Run a command in a subprocess and send the resulting output to cog's output, wrapped in a shell code block."""
15+
raw_text = subprocess.check_output(shlex.split(command), text=True)
16+
output = OUTPUT_STR_FORMAT.format(language=language, text=raw_text.strip())
17+
for line in output.splitlines():
18+
cog.outl(line.rstrip())

Diff for: dev/generate_makefile_targets_table.py

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import re
12
import subprocess
23
from textwrap import dedent
34
from typing import NamedTuple
@@ -34,6 +35,10 @@ def main():
3435
f"|{'-'*(max_target_len + 2)}|{'-'*(max_description_len + 2):{max_description_len}s}|"
3536
)
3637
for t in makefile_targets:
38+
# In GitHub Actions, we get superfluous targets like `make[1]`, so ignore those
39+
if re.search(r"make\[[0-9]+]", t.target):
40+
continue
41+
3742
target_str = f"`{t.target}`"
3843
cog.outl(
3944
f"| {target_str:{max_target_len}s} | {t.description:{max_description_len}s} |"

Diff for: pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ requires-python = ">=3.8"
1818
version = "0.1.0"
1919

2020
[project.scripts]
21-
generate-renovate-annotations = "anaconda_pre_commit_hooks.add_renovate_annotations:main"
21+
generate-renovate-annotations = "anaconda_pre_commit_hooks.add_renovate_annotations:app"
2222
run-cog = "anaconda_pre_commit_hooks.run_cog:main"
2323

2424
[tool.mypy]

Diff for: src/anaconda_pre_commit_hooks/add_renovate_annotations.py

+55-10
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
IndexOverrides = dict[PackageName, IndexUrl]
2929

3030

31+
app = typer.Typer(rich_markup_mode="markdown", add_completion=False)
32+
33+
3134
class Dependency(TypedDict):
3235
name: str
3336
channel: str
@@ -218,16 +221,62 @@ def parse_pip_index_overrides(
218221
return pip_index_overrides
219222

220223

224+
@app.callback(invoke_without_command=True, no_args_is_help=True)
221225
def cli(
222-
env_files: list[Path],
223-
internal_pip_package: Annotated[Optional[list[str]], typer.Option()] = None,
224-
internal_pip_index_url: Annotated[str, typer.Option()] = "",
225-
create_command: Annotated[str, typer.Option()] = DEFAULT_CREATE_COMMAND,
226-
environment_selector: Annotated[str, typer.Option()] = DEFAULT_ENVIRONMENT_SELECTOR,
226+
env_files: Annotated[
227+
list[Path],
228+
typer.Argument(
229+
help="A list of conda environment files, typically passed in from pre-commit automatically"
230+
),
231+
],
232+
internal_pip_package: Annotated[
233+
Optional[list[str]],
234+
typer.Option(
235+
help="One or more packages to pull from the --internal-pip-index-url"
236+
),
237+
] = None,
238+
internal_pip_index_url: Annotated[
239+
str,
240+
typer.Option(
241+
help="An optional extra pip index URL, used in conjunction with the --internal-pip-package option"
242+
),
243+
] = "",
244+
create_command: Annotated[
245+
str,
246+
typer.Option(
247+
help="A command to invoke at each parent directory of all environment files to ensure the conda environment is created and updated"
248+
),
249+
] = DEFAULT_CREATE_COMMAND,
250+
environment_selector: Annotated[
251+
str,
252+
typer.Option(
253+
help="A string used to select the conda environment, either prefix-based (recommended) or named"
254+
),
255+
] = DEFAULT_ENVIRONMENT_SELECTOR,
227256
disable_environment_creation: Annotated[
228-
bool, typer.Option("--disable-environment-creation")
257+
bool,
258+
typer.Option(
259+
"--disable-environment-creation",
260+
help="If set, environment will not be created/updated before annotations are added.",
261+
),
229262
] = False,
230263
) -> None:
264+
"""Generate Renovate comments for a list of `conda` environment files.
265+
266+
For each file, we:
267+
268+
* Run a command to ensure the environment is created/updated
269+
270+
* Extract a list of installed packages in that environment, including pip
271+
272+
* Generate a Renovate annotation comment, including the package name and channel.
273+
274+
This step also allows for overriding the index of pip packages.
275+
276+
* Pin the exact installed version of each dependency.
277+
278+
"""
279+
231280
# Construct a mapping of package name to index URL based on CLI options
232281
pip_index_overrides = parse_pip_index_overrides(
233282
internal_pip_index_url, internal_pip_package or []
@@ -247,7 +296,3 @@ def cli(
247296
add_comments_to_env_file(
248297
env_file, deps, pip_index_overrides=pip_index_overrides
249298
)
250-
251-
252-
def main() -> None:
253-
typer.run(cli) # pragma: nocover

0 commit comments

Comments
 (0)