Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CLI and settings parsing #4

Merged
merged 3 commits into from
May 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,15 @@ You can do this by creating a configuration section in `pixi.toml` or `pyproject
[tool.pixi-diff-to-markdown]
split-tables = "platform"
hide-tables = false
enable-change-type-column = true
enable-explicit-column = false
enable-package-type-column = false
change-type-column = true
explicit-column = false
package-type-column = false
```

You can also override the configuration options by passing them as arguments to `pixi-diff-to-markdown`.

```bash
pixi update --json | pixi-diff-to-markdown update --split-tables=platform --enable-explicit-column
pixi update --json | pixi-diff-to-markdown update --split-tables=platform --explicit-column
```

### `split-tables`
Expand All @@ -66,18 +66,18 @@ For a large amount of `environments` and `platforms`, it might be easier to read

Whether to hide the tables in a collapsible object ([example true](./tests/resources/output/split-tables-platform_hide-tables-True_change-type-True_explicit-type-False_package-type-False.md), [example false](./tests/resources/output/split-tables-platform_hide-tables-False_change-type-True_explicit-type-False_package-type-False.md))

### `enable-change-type-column`
### `change-type-column`

Whether to enable the `Change` column in the output ([example true](./tests/resources/output/split-tables-platform_hide-tables-False_change-type-True_explicit-type-False_package-type-False.md), [example false](./tests/resources/output/split-tables-platform_hide-tables-False_change-type-False_explicit-type-False_package-type-False.md)).

### `enable-explicit-column`
### `explicit-column`

Whether to enable the `Explicit` column in the output ([example true](./tests/resources/output/split-tables-platform_hide-tables-False_change-type-True_explicit-type-True_package-type-False.md), [example false](./tests/resources/output/split-tables-platform_hide-tables-False_change-type-True_explicit-type-False_package-type-False.md)).
If a dependency is explicitly defined in `pixi.toml`, it will be marked as `Explicit`. Otherwise, it will be marked as `Implicit`.

If this is set to `false`, the `Explicit` column will be omitted and the explicitly defined dependencies will be marked as *cursive*.

### `enable-package-type-column`
### `package-type-column`

Whether to enable the `Package Type` column in the output ([example true](./tests/resources/output/split-tables-platform_hide-tables-False_change-type-True_explicit-type-False_package-type-True.md), [example false](./tests/resources/output/split-tables-platform_hide-tables-False_change-type-True_explicit-type-False_package-type-False.md)).
This column will show whether the dependency is a `conda` or `pypi` package.
264 changes: 264 additions & 0 deletions pixi.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions pixi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ python = ">=3.12"
pydantic = ">=2.7.1,<2.8"
py-rattler = ">=0.5.0,<0.6"
ordered_enum = ">=0.0.8,<0.1"
typer = ">=0.12.3,<0.13"
pydantic-settings = ">=2.2.1,<2.3"

[host-dependencies]
pip = "*"
Expand Down
203 changes: 2 additions & 201 deletions pixi_diff_to_markdown/__main__.py
Original file line number Diff line number Diff line change
@@ -1,203 +1,4 @@
import json
from typing import Literal

from pixi_diff_to_markdown.models import (
ChangeType,
Configuration,
Environments,
UpdateSpec,
calculate_change_type,
)

# TODO: pypi support


def update_spec_to_table_line(
package_name: str, update_spec: UpdateSpec, configuration: Configuration
) -> str:
change_type = calculate_change_type(update_spec)
if change_type == ChangeType.ADDED:
before = ""
after = update_spec.after.version # type: ignore[union-attr]
elif change_type == ChangeType.REMOVED:
before = update_spec.before.version # type: ignore[union-attr]
after = ""
elif change_type == ChangeType.BUILD:
before = update_spec.before.build # type: ignore[union-attr]
after = update_spec.after.build # type: ignore[union-attr]
else:
before = update_spec.before.version # type: ignore[union-attr]
after = update_spec.after.version # type: ignore[union-attr]
if (
change_type == ChangeType.MAJOR_DOWN
or change_type == ChangeType.MINOR_DOWN
or change_type == ChangeType.PATCH_DOWN
):
maybe_downgrade_ref = "[^2]"
else:
maybe_downgrade_ref = ""
add_explicit_type = configuration["enable_explicit_type_column"]
add_change_type = configuration["enable_change_type_column"]
add_package_type = configuration["enable_package_type_column"]
if not add_explicit_type and update_spec.explicit:
package_name_formatted = f"*{package_name}*"
else:
package_name_formatted = package_name

return (
f"| {package_name_formatted + maybe_downgrade_ref} |"
f" {before} |"
f" {after} |"
f"{f" {change_type.value} |" if add_change_type else ""}"
f"{f" {str(update_spec.explicit).lower()} |" if add_explicit_type else ""}"
f"{f' {update_spec.type_} |' if add_package_type else ''}"
)


def generate_output(data: Environments, configuration: Configuration) -> str:
if configuration["split_tables"] == "no":
return generate_table_no_split_tables(data, configuration)
elif configuration["split_tables"] == "environment":
return generate_table_environment_split_tables(data, configuration)
elif configuration["split_tables"] == "platform":
return generate_table_platform_split_tables(data, configuration)


def generate_header(
split_type: Literal["no", "environment", "platform"], configuration: Configuration
):
add_change_type = configuration["enable_change_type_column"]
add_explicit_type = configuration["enable_explicit_type_column"]
add_package_type = configuration["enable_package_type_column"]
if split_type == "no":
prefix = "| Environment "
elif split_type == "environment":
prefix = "| Platform "
else:
assert split_type == "platform"
prefix = ""
header_line1 = (
f"{prefix}| Dependency{"[^1]" if not add_explicit_type else ""} | Before | After |"
f"{" Change |" if add_change_type else ""}"
f"{" Explicit |" if add_explicit_type else ""}"
f"{" Package |" if add_package_type else ""}"
)
header_line2 = f"{"| -: " if split_type != "platform" else ""}| - | - | - |{" - |" if add_change_type else ""}{" - |" if add_explicit_type else ""}{" - |" if add_package_type else ""}"
return header_line1 + "\n" + header_line2


def generate_footnotes() -> str:
return """[^1]: *Cursive* means explicit dependency.
[^2]: Dependency got downgraded.
"""


def generate_table_no_split_tables(
data: Environments, configuration: Configuration
) -> str:
header = generate_header("no", configuration)
lines = []
for environment, platforms in data.root.items():
for platform, dependencies in platforms.root.items():
lines_platform = [
update_spec_to_table_line(package_name, update_spec, configuration)
for (package_name, update_spec) in sorted(
dependencies.root.items(), key=lambda x: (x[1], x[0])
)
]
lines_platform[0] = f"| {environment} / {platform} {lines_platform[0]}"
for i in range(1, len(lines_platform)):
lines_platform[i] = "|" + lines_platform[i]
lines.extend(lines_platform)
lines.append("")
content = "\n".join(lines)

footnote = generate_footnotes()
table = header + "\n" + content + "\n" + footnote
return table


def generate_table_environment_split_tables(
data: Environments, configuration: Configuration
) -> str:
header = generate_header("environment", configuration)
lines = []
for environment, platforms in data.root.items():
if configuration["hide_tables"]:
lines.append("<details>")
lines.append(f"<summary>{environment}</summary>")
else:
lines.append(f"## {environment}")
lines.append("")
lines.append(header)
for platform, dependencies in platforms.root.items():
lines_platform = [
update_spec_to_table_line(package_name, update_spec, configuration)
for (package_name, update_spec) in sorted(
dependencies.root.items(), key=lambda x: (x[1], x[0])
)
]
lines_platform[0] = f"| {platform} {lines_platform[0]}"
for i in range(1, len(lines_platform)):
lines_platform[i] = "|" + lines_platform[i]
lines.extend(lines_platform)
if configuration["hide_tables"]:
lines.append("")
lines.append("</details>")
lines.append("")
content = "\n".join(lines)
footnote = generate_footnotes()
table = content + "\n" + footnote
return table


def generate_table_platform_split_tables(
data: Environments, configuration: Configuration
) -> str:
header = generate_header("platform", configuration)
lines = []
for environment, platforms in data.root.items():
lines.append(f"# {environment}")
lines.append("")
for platform, dependencies in platforms.root.items():
if configuration["hide_tables"]:
lines.append("<details>")
lines.append(f"<summary>{platform}</summary>")
else:
lines.append(f"## {platform}")
lines.append("")
lines.append(header)
lines_platform = [
update_spec_to_table_line(package_name, update_spec, configuration)
for (package_name, update_spec) in sorted(
dependencies.root.items(), key=lambda x: (x[1], x[0])
)
]
lines.extend(lines_platform)
if configuration["hide_tables"]:
lines.append("")
lines.append("</details>")
lines.append("")
content = "\n".join(lines)
footnote = generate_footnotes()
table = content + "\n" + footnote
return table


def main():
configuration: Configuration = {
"enable_change_type_column": True,
"enable_package_type_column": False,
"enable_explicit_type_column": False,
"split_tables": "no",
"hide_tables": False,
}
with open("tests/resources/test.json") as f:
data = json.load(f)
data_parsed = Environments(data)
output = generate_output(data_parsed, configuration)
print(output)

from pixi_diff_to_markdown.cli import app

if __name__ == "__main__":
main()
app()
58 changes: 58 additions & 0 deletions pixi_diff_to_markdown/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# ruff: noqa: UP007
from sys import stdin
from typing import Annotated, Optional

import typer

from pixi_diff_to_markdown.diff import generate_output
from pixi_diff_to_markdown.models import Environments
from pixi_diff_to_markdown.settings import Settings, SplitTables

app = typer.Typer()


@app.command(
help="Convert `pixi update --json` diff to markdown. Reads from stdin and writes to stdout: `pixi --json | pixi diff-to-markdown > output.md`"
)
def main(
change_type_column: Annotated[
Optional[bool],
typer.Option(help="Enable the change type column.", show_default=False),
] = None,
package_type_column: Annotated[
Optional[bool],
typer.Option(
help="Enable the package type (conda/pypi) column.", show_default=False
),
] = None,
explicit_column: Annotated[
Optional[bool],
typer.Option(
help="Enable the explicit (explicit/implicit) column.", show_default=False
),
] = None,
split_tables: Annotated[
Optional[SplitTables],
typer.Option(help="In what way to split tables.", show_default=False),
] = None,
hide_tables: Annotated[
Optional[bool],
typer.Option(
help="Whether to hide tables in a collapsible element.", show_default=False
),
] = None,
):
settings_dict = {
"change-type-column": change_type_column,
"package-type-column": package_type_column,
"explicit-column": explicit_column,
"split-tables": split_tables,
"hide-tables": hide_tables,
}
settings = Settings.model_validate(
{k: v for k, v in settings_dict.items() if v is not None}
)
data = "".join(stdin.readlines())
data_parsed = Environments.model_validate_json(data)
output = generate_output(data_parsed, settings)
print(output)
Loading