Skip to content
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
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ just ci # Full CI simulation (checks + tests + examples + bench co
just test # Lib + doc tests (fast)
just test-all # All tests (Rust, benchmark inputs, and Python)
just examples # Run all examples
just update # Update dependency locks and repository-owned Cargo tools
just update # Update dependency requirements, locks, and repository-owned Cargo tools
just update-version vX.Y.Z # Update release metadata without upgrading dependencies
```

Expand Down
12 changes: 6 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ Use `just fix` when you intentionally want formatters and automatic fixes to
change files. Run `just --list` for the full command surface.

Use `just update` for deliberate dependency and tool maintenance. It composes
`just update-dependencies`, which advances Cargo dependency requirements and
the Cargo/uv locks, with `just update-cargo-tools`, which upgrades only the
Cargo CLI packages owned by `setup-tools` and atomically reconciles their root
`justfile` pins. The tool updater requires `cargo-install-update` from the
`cargo-update` package and does not touch unrelated Cargo executables or uv's
user-global tool environments.
`just update-dependencies`, which advances Cargo dependency requirements, exact
Python development-tool pins, and the Cargo/uv locks, with
`just update-cargo-tools`, which upgrades only the Cargo CLI packages owned by
`setup-tools` and atomically reconciles their root `justfile` pins. The tool
updater requires `cargo-install-update` from the `cargo-update` package and does
not touch unrelated Cargo executables or uv's user-global tool environments.

The repository uses `cargo-nextest` for runnable Rust tests, `cargo-machete`
for unused-dependency checks, and `just cargo-lock-check` to verify that the
Expand Down
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions docs/RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ git switch main
git pull --ff-only
```

Refresh dependency requirements, lockfiles, and repository-owned Cargo tool
pins before creating the release branch:
Refresh Cargo dependency requirements, exact Python development-tool pins,
lockfiles, and repository-owned Cargo tool pins before creating the release
branch:

```bash
just update
Expand Down
15 changes: 12 additions & 3 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -1084,11 +1084,20 @@ update-cargo-tools: _ensure-uv
cargo install-update --locked "${packages[@]}"
uv run --locked update-cargo-tool-pins

# Advance Cargo dependency declarations, update Cargo and uv locks, then sync uv dev tools.
[doc('Update Cargo.toml dependency requirements and all Cargo/uv locked dependencies.')]
update-dependencies: _ensure-uv _ensure-cargo-edit
# Advance Cargo and exact Python development requirements plus their lockfiles.
[doc('Update Cargo and Python development requirements plus all Cargo/uv locked dependencies.')]
update-dependencies: update-cargo-dependencies update-python-dependencies

# Advance Cargo dependency declarations and lockfile entries.
[doc('Update Cargo.toml dependency requirements and Cargo.lock.')]
update-cargo-dependencies: _ensure-cargo-edit
cargo upgrade
cargo update

# Resolve latest Python development tools, retain exact pins, and sync the environment.
[doc('Update exact dependency-groups.dev pins and uv.lock through uv.')]
update-python-dependencies: _ensure-uv
uv run --locked update-python-dev-pins
uv lock --upgrade
uv sync --locked --group dev

Expand Down
10 changes: 6 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,12 @@ tag-release = "tag_release:main"
check-docs-version-sync = "check_docs_version_sync:main"
update-release-version = "update_release_version:main"
update-cargo-tool-pins = "update_cargo_tool_pins:main"
update-python-dev-pins = "update_python_dev_pins:main"

# Configure setuptools to find modules in scripts/ directory.
[tool.setuptools]
package-dir = { "" = "scripts" }
py-modules = [ "archive_changelog", "archive_performance", "bench_compare", "benchmark_contract", "check_docs_version_sync", "check_semgrep_fixtures", "criterion_dim_plot", "performance_artifacts", "postprocess_changelog", "subprocess_utils", "tag_release", "update_cargo_tool_pins", "update_release_version" ]
py-modules = [ "archive_changelog", "archive_performance", "bench_compare", "benchmark_contract", "check_docs_version_sync", "check_semgrep_fixtures", "criterion_dim_plot", "performance_artifacts", "postprocess_changelog", "subprocess_utils", "tag_release", "update_cargo_tool_pins", "update_python_dev_pins", "update_release_version" ]

[tool.ruff]
line-length = 160
Expand Down Expand Up @@ -99,6 +100,7 @@ known-first-party = [
"subprocess_utils",
"tag_release",
"update_cargo_tool_pins",
"update_python_dev_pins",
]
force-single-line = false
split-on-trailing-comma = true
Expand Down Expand Up @@ -149,10 +151,10 @@ package = true
dev = [
"actionlint-py==1.7.12.24",
"pytest==9.1.1",
"ruff==0.16.2",
"semgrep==1.172.0",
"ruff==0.16.4",
"semgrep==1.174.0",
"shellcheck-py==0.11.0.1",
"shfmt-py==4.0.0",
"ty==0.0.69",
"ty==0.0.73",
"yamllint==1.38.0",
]
9 changes: 6 additions & 3 deletions scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ uv sync --locked --group dev
### Updating dependencies and repository-owned tools

Run `just update` for the deliberate maintenance workflow. It updates Cargo and
uv dependency declarations and locks, upgrades only the Cargo CLI packages
owned by `setup-tools`, and then uses `update-cargo-tool-pins` to reconcile the
installed package versions with the root `justfile` atomically.
exact Python development-tool declarations and their locks, upgrades only the
Cargo CLI packages owned by `setup-tools`, and then reconciles the installed
package versions with the root `justfile` atomically. The Python updater asks
uv to resolve one cross-platform tool set before applying all changed exact
pins together; it does not change runtime or build-system requirements.

## How to use it

Expand Down Expand Up @@ -282,6 +284,7 @@ validates SemVer, and handles GitHub's 125KB tag-annotation size limit.
| `postprocess_changelog.py` | Normalize and reflow generated git-cliff Markdown safely |
| `subprocess_utils.py` | Safe subprocess wrappers for git commands |
| `update_cargo_tool_pins.py` | Reconcile repository-owned Cargo tool pins with installed versions |
| `update_python_dev_pins.py` | Resolve and advance exact Python development-tool pins through uv |
| `update_release_version.py` | Transactionally update deterministic release-version metadata |

See `docs/RELEASING.md` for the full release workflow.
13 changes: 9 additions & 4 deletions scripts/postprocess_changelog.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import sys
from dataclasses import dataclass
from pathlib import Path
from typing import cast

# rumdl MD013 line-length limit used by this project.
MAX_LINE_WIDTH = 160
Expand Down Expand Up @@ -216,17 +217,17 @@ def _squash_heading_parts(line: str) -> tuple[str, str, str] | None:
if match is None:
return None

raw_prefix = match.group("prefix")
raw_prefix = cast("str", match.group("prefix"))
kind = re.sub(r"\([^)]+\)", "", raw_prefix).rstrip("!").casefold()
label = _SQUASH_HEADING_LABELS.get(kind)
if label is None:
return None

title = match.group("title").strip()
title = cast("str", match.group("title")).strip()
if not title:
return None

return match.group("indent"), label, title[0].upper() + title[1:]
return cast("str", match.group("indent")), label, title[0].upper() + title[1:]


def _normalize_squash_heading(line: str, *, nested: bool = False) -> str:
Expand Down Expand Up @@ -678,7 +679,11 @@ def _fence_parts(line: str) -> tuple[str, str, str] | None:
match = _FENCE_RE.fullmatch(line)
if match is None:
return None
return match.group("indent"), match.group("fence"), match.group("info")
return (
cast("str", match.group("indent")),
cast("str", match.group("fence")),
cast("str", match.group("info")),
)


def _opening_code_fence(line: str) -> _CodeFence | None:
Expand Down
48 changes: 30 additions & 18 deletions scripts/subprocess_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import subprocess
import tempfile
from pathlib import Path
from typing import Any
from typing import Any, cast

type RunKwargs = dict[str, Any]

Expand Down Expand Up @@ -105,10 +105,13 @@ def run_git_command(
"""
git_path = get_safe_executable("git")
run_kwargs = _build_run_kwargs("run_git_command", **kwargs)
return subprocess.run( # noqa: S603,PLW1510
[git_path, *args],
cwd=cwd,
**run_kwargs,
return cast(
"subprocess.CompletedProcess[str]",
subprocess.run( # noqa: S603,PLW1510
[git_path, *args],
cwd=cwd,
**run_kwargs,
),
)


Expand All @@ -134,10 +137,13 @@ def run_cargo_command(
"""
cargo_path = get_safe_executable("cargo")
run_kwargs = _build_run_kwargs("run_cargo_command", **kwargs)
return subprocess.run( # noqa: S603,PLW1510
[cargo_path, *args],
cwd=cwd,
**run_kwargs,
return cast(
"subprocess.CompletedProcess[str]",
subprocess.run( # noqa: S603,PLW1510
[cargo_path, *args],
cwd=cwd,
**run_kwargs,
),
)


Expand Down Expand Up @@ -165,10 +171,13 @@ def run_safe_command(
"""
command_path = get_safe_executable(command)
run_kwargs = _build_run_kwargs(f"run_safe_command for {command}", **kwargs)
return subprocess.run( # noqa: S603,PLW1510
[command_path, *args],
cwd=cwd,
**run_kwargs,
return cast(
"subprocess.CompletedProcess[str]",
subprocess.run( # noqa: S603,PLW1510
[command_path, *args],
cwd=cwd,
**run_kwargs,
),
)


Expand Down Expand Up @@ -279,11 +288,14 @@ def run_git_command_with_input(
with tempfile.TemporaryFile() as stdin:
stdin.write(payload)
stdin.seek(0)
return subprocess.run( # noqa: S603,PLW1510
[git_path, *args],
cwd=cwd,
stdin=stdin,
**run_kwargs,
return cast(
"subprocess.CompletedProcess[str]",
subprocess.run( # noqa: S603,PLW1510
[git_path, *args],
cwd=cwd,
stdin=stdin,
**run_kwargs,
),
)


Expand Down
3 changes: 3 additions & 0 deletions scripts/tests/test_justfile_discoverability.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,17 @@ def test_update_workflow_composes_scoped_dependency_and_tool_updates() -> None:
"""Update recipes should cover repo state without touching unrelated global tools."""
recipes = just_recipes()
update_dependencies = {dependency["recipe"] for dependency in recipes["update"]["dependencies"]}
dependency_updates = {dependency["recipe"] for dependency in recipes["update-dependencies"]["dependencies"]}

assert update_dependencies == {"update-cargo-tools", "update-dependencies"}
assert dependency_updates == {"update-cargo-dependencies", "update-python-dependencies"}

dependency_result = run_just("--dry-run", "update-dependencies")
dependency_update = dependency_result.stdout + dependency_result.stderr
assert "cargo upgrade" in dependency_update
assert "cargo upgrade --incompatible allow" not in dependency_update
assert "cargo update" in dependency_update
assert "update-python-dev-pins" in dependency_update
assert "uv lock --upgrade" in dependency_update
assert "uv sync --locked --group dev" in dependency_update
assert "cargo install-update --all" not in dependency_update
Expand Down
Loading
Loading