diff --git a/.github/workflows/scripts.yml b/.github/workflows/scripts.yml
new file mode 100644
index 00000000..6e0491b1
--- /dev/null
+++ b/.github/workflows/scripts.yml
@@ -0,0 +1,50 @@
+name: Scripts
+
+on:
+ push:
+ branches: [main]
+ paths:
+ - "scripts/**"
+ - "tests/scripts/**"
+ - ".github/workflows/scripts.yml"
+ pull_request:
+ branches: [main]
+ paths:
+ - "scripts/**"
+ - "tests/scripts/**"
+ - ".github/workflows/scripts.yml"
+
+concurrency:
+ group: scripts-${{ github.ref }}
+ cancel-in-progress: true
+
+permissions: {}
+
+jobs:
+ test:
+ name: Test scripts
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ permissions:
+ contents: read
+ steps:
+ - name: Checkout
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ with:
+ persist-credentials: false
+
+ - name: Set up Python
+ uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
+ with:
+ python-version: "3.12"
+
+ - name: Set up uv
+ uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
+ with:
+ enable-cache: true
+
+ - name: Install dependencies
+ run: uv sync --all-extras --frozen
+
+ - name: Run script tests
+ run: uv run pytest tests/scripts
diff --git a/README.md b/README.md
index a1be8bea..bf894e7b 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
-
+
RAMPART
diff --git a/docs/contributing/release-process.md b/docs/contributing/release-process.md
index 08c66098..850b2b58 100644
--- a/docs/contributing/release-process.md
+++ b/docs/contributing/release-process.md
@@ -32,38 +32,32 @@ If you are incrementing the minor version, search the codebase for the new minor
If you find functionality to remove, merge the removal PR to `main` before proceeding.
-## 4. Update the Version
+## 4. Prepare Release Metadata
-### pyproject.toml
-Set the version in `pyproject.toml` to the version established in step 2.
+Before tagging the release:
-```toml
-[project]
-name = "RAMPART"
-version = "x.y.z"
-```
-
-### Update README File
-The README file is published to PyPI and also needs to be updated so the links work properly. _Note: There may not be any links to update, but it is good practice to check in case our README changes._
+- Do not add or update a version in `pyproject.toml`. The release version comes from the Git tag.
+- Review `README.md` for repository-relative links that need to work on PyPI.
+- Keep image links under `docs/images/` relative. During package builds, `scripts/hatch_build.py` rewrites them to raw GitHub URLs pinned to the release version.
+- For other repository-relative links, use absolute `https://github.com/microsoft/RAMPART/...` URLs on `main`, or extend `scripts/hatch_build.py` to rewrite them at build time.
+- Merge any required README or metadata changes to `main` before continuing to step 5.
-Replace all “main” links like “doc/index.md” with “raw” links that have the correct version number, i.e., “https://raw.githubusercontent.com/microsoft/RAMPART/releases/vx.y.z/docs/index.md”.
+!!! note "Versioning"
+ RAMPART uses Hatch VCS with setuptools-scm's `semver-pep440-release-branch` scheme. After `v0.2.0`, builds from `main` use `0.3.0.devN`, builds from `releases/v0.2` use `0.2.1.devN`, and a tagged commit uses the exact tag. Git only considers tags in the current commit's ancestry, so create `vx.y.0` on `main` before branching; patch tags remain on `releases/vx.y` and do not affect `main`. Until `v0.2.0` is tagged on `main`, development builds use the `0.1.0.devN` fallback; these builds are not published. See the [setuptools-scm version scheme documentation](https://github.com/pypa/setuptools-scm/blob/main/docs/extending.md#available-implementations).
-For images, update using the “raw” link, e.g., “https://raw.githubusercontent.com/microsoft/RAMPART/releases/vx.y.z/docs/images/RAMPART.png”.
+## 5. Tag the Minor Release on `main` and Create the Release Branch
-For directories, update using the “tree” link, e.g., “https://github.com/microsoft/RAMPART/tree/releases/vx.y.z/docs/usage"
+For the first release in a minor series, tag `vx.y.0` on `main`, then create the long-lived `releases/vx.y` branch. Patch releases reuse this branch.
-This is required for the release branch because PyPI does not pick up other files besides the README, which results in local links breaking.
-
-## 5. Publish the Release Branch to GitHub
-
-Commit your changes to a release branch and push the tag:
+Confirm any release-prep changes have already merged to `main`, then:
```bash
-git checkout -b releases/vx.y.z
-git commit -am "release vx.y.z"
-git push origin releases/vx.y.z
-git tag -a vx.y.z -m "vx.y.z release"
-git push --tags
+git checkout main
+git pull origin main
+
+# Create the tag and release branch.
+git tag -a vx.y.0 -m "vx.y.0 release"
+git checkout -b releases/vx.y vx.y.0
```
@@ -120,21 +114,35 @@ Confirm the version matches the release and the package is installed under the e
uv run pytest path/to/RAMPART/tests/integration/test_smoke.py -v
```
-If you need to make changes to fix issues found during testing, cherry-pick from `main` after the fix lands:
+For a minor release, if testing finds an issue, land the fix on `main`, recreate the local tag on the fixed commit, and fast-forward the local release branch:
+
+```bash
+git checkout main && git pull origin main
+git tag -d vx.y.0
+git tag -a vx.y.0 -m "vx.y.0 release"
+git checkout releases/vx.y
+git merge --ff-only vx.y.0
+```
+
+For a patch release, land the additional fix on `main`, then update the release branch and local tag:
```bash
-git checkout main && git pull
-git log main # find the commit hash to cherry-pick
-git checkout releases/vx.y.z
-git cherry-pick
-git push origin releases/vx.y.z
-git tag -a vx.y.z -m "vx.y.z release" --force
-git push --tags --force
+git checkout releases/vx.y
+git cherry-pick
+git tag -d vx.y.z
+git tag -a vx.y.z -m "vx.y.z release"
```
-Rebuild the package after any cherry-pick and re-test.
+Rebuild the package and re-test.
+
+## 8. Publish the Git References and Package
+
+After all tests pass, push the release branch and tag:
-## 8. Publish to PyPI
+```bash
+git push origin releases/vx.y
+git push origin vx.y.z
+```
Create a PyPI account if you don't have one and ask another maintainer to add you to the `rampart` project. Before publishing, have an API token scoped to the project ready (create one in your PyPI project settings).
@@ -149,14 +157,14 @@ If successful, the URL `https://pypi.org/project/rampart/x.y.z/` will return the
After the release is on PyPI, open a PR to `main` containing only:
-- In line with PyPA [versioning guidance](https://packaging.python.org/en/latest/discussions/versioning/), bump the version in `pyproject.toml` to the next development version (e.g., `x.y.(z+1).dev0` or `x.(y+1).0.dev0`, depending on the next planned release).
+- Any follow-up documentation or metadata updates needed after the release. Do not bump the package version in `pyproject.toml`.
- Replace any references to the previous release version in the codebase with the new released version (without `.dev0`) where applicable (e.g., installation docs that pin to the latest tag).
-Open this PR from a branch separate from your `releases/vx.y.z` branch.
+Open this PR from a branch separate from your `releases/vx.y` branch.
## 10. Create the GitHub Release
-Go to the [releases page](https://github.com/microsoft/RAMPART/releases), select **Draft a new release**, and choose the tag you pushed in step 5. Click **Generate release notes** to pre-populate the description.
+Go to the [releases page](https://github.com/microsoft/RAMPART/releases), select **Draft a new release**, and choose the tag you pushed in step 8. Click **Generate release notes** to pre-populate the description.
Structure the description as:
@@ -189,9 +197,9 @@ Re-run the full test suite after bumping — PyRIT changes are a common source o
---
-### Patch Releases (Cherry-Pick Process)
+### Patch Releases
-A patch release (e.g., `0.2.0` → `0.2.1`) ships a targeted fix — typically a security patch or a critical bug fix — without including other in-flight changes from `main`.
+A patch release (e.g., `0.2.0` to `0.2.1`) ships a targeted fix, typically a security patch or a critical bug fix, without including other in-flight changes from `main`. Each minor series has one long-lived branch named `releases/vx.y`; every patch for that series is cherry-picked from `main` onto that branch.
#### When to use a patch release
@@ -199,44 +207,42 @@ A patch release (e.g., `0.2.0` → `0.2.1`) ships a targeted fix — typically a
- A critical bug was found in the latest release that blocks users.
- The fix is already merged to `main`, but `main` contains other changes that aren't ready for release.
-#### Abbreviated steps
+#### Steps
-1. **Create a release branch from the previous tag**, not from `main`:
+1. **Choose the next patch version**. Increment the patch component from the latest published tag and confirm that the new tag does not already exist. For example, use `v0.2.1` after `v0.2.0`:
+
+ ```bash
+ git fetch origin --tags
+ git tag --list "vx.y.*"
+ ```
+
+2. **Check out the existing minor release branch**:
```bash
git fetch origin
- git checkout -b releases/vx.y.z vx.y.(z-1)
+ git checkout releases/vx.y
+ git pull --ff-only origin releases/vx.y
```
-2. **Cherry-pick the fix** from `main`:
+3. **Cherry-pick the fix** after it has merged to `main`:
```bash
git cherry-pick
```
- Resolve any conflicts manually. Patch-sized fixes typically apply cleanly.
+ Resolve any conflicts manually. Patch-sized fixes typically apply cleanly. Cherry-pick only the commits intended for the patch; do not merge `main` into the release branch.
-3. **Bump the version** in `pyproject.toml` to the new patch version. Also update any version-pinned links in `README.md`.
+4. **Update release-specific references** as needed, such as documentation that names the patch version (for example, "Fixed in v0.2.1") or `README.md` links pinned to a release tag. Skip this commit if no references need updating.
```bash
- git commit -am "Bump version to x.y.z"
+ git add
+ git commit -m "Prepare x.y.z release"
```
-4. **Push and tag**:
+5. **Create the tag locally**. Do not push the branch or tag until testing passes:
```bash
- git push origin releases/vx.y.z
git tag -a vx.y.z -m "vx.y.z release"
- git push --tags
```
-5. **Follow the regular release process from step 6 onward**: build, test, publish to PyPI, update `main`, and create the GitHub release. Patch release notes should clearly state the reason for the patch (e.g., "Security fix for…" or "Critical bug fix for…").
-
-#### Key differences from a regular release
-
-| Aspect | Regular release | Patch release |
-|---|---|---|
-| Branch base | `main` | Previous release tag |
-| Changes included | Everything on `main` | Only cherry-picked fix(es) |
-| Deprecated code removal | Yes (if minor bump) | No |
-| Release notes | Full changelog with curated summary | Short, focused on the reason for the patch |
+6. **Follow the regular release process from step 6 onward**: build, test, push the branch and tag, publish to PyPI, update `main`, and create the GitHub release. Patch release notes should clearly state the reason for the patch (e.g., "Security fix for…" or "Critical bug fix for…").
diff --git a/pyproject.toml b/pyproject.toml
index 32a59a6b..f5501017 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,12 +1,11 @@
[build-system]
-requires = ["setuptools"]
-build-backend = "setuptools.build_meta"
+requires = ["hatchling>=1.30.1", "hatch-vcs>=0.5.0", "setuptools-scm>=10"]
+build-backend = "hatchling.build"
[project]
name = "RAMPART"
-version = "0.1.1.dev0"
description = "A pytest-native safety testing framework for agentic AI applications"
-readme = "README.md"
+dynamic = ["readme", "version"]
license = "MIT"
requires-python = ">=3.11"
authors = [
@@ -43,6 +42,8 @@ onedrive = [
[dependency-groups]
dev = [
+ "hatch-vcs>=0.5.0",
+ "hatchling>=1.30.1",
"pre-commit>=4.5.1",
"pytest-cov>=6.1.0",
"pytest-xdist[psutil]>=3.8.0",
@@ -64,14 +65,18 @@ Issues = "https://github.com/microsoft/RAMPART/issues"
[project.entry-points.pytest11]
rampart = "rampart.pytest_plugin.plugin"
-[tool.setuptools.packages.find]
-exclude = ["site*", "docs*", "tests*", "scripts*"]
+[tool.hatch.metadata.hooks.custom]
+path = "scripts/hatch_build.py"
-[tool.setuptools.package-data]
-rampart = [
- "drivers/prompts/*.yaml",
- "evaluators/prompts/*.yaml",
-]
+[tool.hatch.version]
+source = "vcs"
+
+[tool.hatch.version.raw-options]
+version_scheme = "semver-pep440-release-branch"
+local_scheme = "no-local-version"
+
+[tool.hatch.build.targets.wheel]
+packages = ["rampart"]
[tool.coverage.run]
source = ["rampart"]
@@ -83,6 +88,7 @@ show_missing = true
skip_empty = true
[tool.pytest.ini_options]
+pythonpath = ["scripts"]
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "session"
markers = [
@@ -105,6 +111,9 @@ extend-ignore = [
]
[tool.ruff.lint.per-file-ignores]
+"scripts/hatch_build.py" = [
+ "INP001", # Top-level build hook
+]
"tests/**" = [
"S101", # assert is pytest's API
"D100", "D101", "D102", "D104", "D107", # no docstrings needed
@@ -141,9 +150,10 @@ max-args = 10
[tool.ty.environment]
python-version = "3.11"
+extra-paths = ["scripts"]
[tool.ty.src]
-include = ["rampart", "tests"]
+include = ["rampart", "tests", "scripts"]
[tool.uv.sources]
pyrit = { git = "https://github.com/microsoft/PyRIT", rev = "6dc8b94139757390286bbce7d53c1f7e58e66e29" } # v0.13.0
diff --git a/scripts/hatch_build.py b/scripts/hatch_build.py
new file mode 100644
index 00000000..c6b3cc62
--- /dev/null
+++ b/scripts/hatch_build.py
@@ -0,0 +1,87 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT license.
+"""Hatchling metadata hooks for RAMPART package builds."""
+
+from __future__ import annotations
+
+import re
+from pathlib import Path
+
+from hatchling.metadata.plugin.interface import MetadataHookInterface
+
+# These patterns only rewrite ``docs/images/`` paths; images stored elsewhere
+# (for example ``assets/``) pass through unchanged and must already use an
+# absolute URL to render on PyPI.
+_GITHUB_IMAGE_URL_PATTERNS = (
+ re.compile(r"(https://github\.com/microsoft/RAMPART/raw/)main(/docs/images/)"),
+ re.compile(
+ r"(https://raw\.githubusercontent\.com/microsoft/RAMPART/)main(/docs/images/)",
+ ),
+)
+_RELATIVE_HTML_IMAGE_URL_PATTERNS = (
+ re.compile(r'(
]*\bsrc=")(?:\./)?(docs/images/[^"]+)(")'),
+ re.compile(r"(
]*\bsrc=')(?:\./)?(docs/images/[^']+)(')"),
+)
+# The URL group stops at whitespace so an optional Markdown title, as in
+# ````, is preserved in group 3 instead of
+# being folded into the rewritten image URL.
+_RELATIVE_MARKDOWN_IMAGE_URL_PATTERN = re.compile(
+ r"(!\[[^\]]*\]\()(?:\./)?(docs/images/[^)\s]+)([^)]*\))",
+)
+
+
+def _readme_ref(version: str) -> str:
+ """Return the Git ref to use for README image URLs."""
+ if ".dev" in version or "+" in version:
+ return "main"
+
+ return f"v{version}"
+
+
+def _raw_image_url(*, readme_ref: str, image_path: str) -> str:
+ """Return an absolute GitHub raw URL for a README image."""
+ return (
+ f"https://raw.githubusercontent.com/microsoft/RAMPART/{readme_ref}/{image_path}"
+ )
+
+
+def _render_readme(*, root: Path, version: str) -> str:
+ """Return README content rendered for package metadata."""
+ readme = (root / "README.md").read_text(encoding="utf-8")
+ readme_ref = _readme_ref(version)
+
+ for pattern in _GITHUB_IMAGE_URL_PATTERNS:
+ readme = pattern.sub(rf"\g<1>{readme_ref}\g<2>", readme)
+
+ for pattern in _RELATIVE_HTML_IMAGE_URL_PATTERNS:
+ readme = pattern.sub(
+ lambda match: (
+ f"{match.group(1)}"
+ f"{_raw_image_url(readme_ref=readme_ref, image_path=match.group(2))}"
+ f"{match.group(3)}"
+ ),
+ readme,
+ )
+
+ return _RELATIVE_MARKDOWN_IMAGE_URL_PATTERN.sub(
+ lambda match: (
+ f"{match.group(1)}"
+ f"{_raw_image_url(readme_ref=readme_ref, image_path=match.group(2))}"
+ f"{match.group(3)}"
+ ),
+ readme,
+ )
+
+
+class ReadmeMetadataHook(MetadataHookInterface):
+ """Generate PyPI README metadata with release-pinned image URLs."""
+
+ def update(self, metadata: dict[str, object]) -> None:
+ """Update project metadata in-place."""
+ metadata["readme"] = {
+ "content-type": "text/markdown",
+ "text": _render_readme(
+ root=Path(self.root),
+ version=str(metadata["version"]),
+ ),
+ }
diff --git a/tests/scripts/__init__.py b/tests/scripts/__init__.py
new file mode 100644
index 00000000..9a045456
--- /dev/null
+++ b/tests/scripts/__init__.py
@@ -0,0 +1,2 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT license.
diff --git a/tests/scripts/test_hatch_build.py b/tests/scripts/test_hatch_build.py
new file mode 100644
index 00000000..43e484bc
--- /dev/null
+++ b/tests/scripts/test_hatch_build.py
@@ -0,0 +1,93 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT license.
+
+"""Tests for the packaging README metadata hook in ``scripts/hatch_build.py``."""
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+import hatch_build
+
+if TYPE_CHECKING:
+ from pathlib import Path
+
+RAW = "https://raw.githubusercontent.com/microsoft/RAMPART"
+
+
+def _write_readme(root: Path, body: str) -> None:
+ (root / "README.md").write_text(body, encoding="utf-8")
+
+
+class TestReadmeRef:
+ """_readme_ref pins released versions and falls back to main otherwise."""
+
+ def test_release_version_uses_tag(self) -> None:
+ assert hatch_build._readme_ref("0.1.0") == "v0.1.0"
+
+ def test_dev_version_uses_main(self) -> None:
+ assert hatch_build._readme_ref("0.1.1.dev35") == "main"
+
+ def test_local_version_uses_main(self) -> None:
+ assert hatch_build._readme_ref("0.1.0+g1234567") == "main"
+
+
+class TestRawImageUrl:
+ """_raw_image_url builds a ref-pinned raw GitHub URL."""
+
+ def test_builds_pinned_url(self) -> None:
+ url = hatch_build._raw_image_url(
+ readme_ref="v0.1.0",
+ image_path="docs/images/RAMPART.svg",
+ )
+ assert url == f"{RAW}/v0.1.0/docs/images/RAMPART.svg"
+
+
+class TestRenderReadme:
+ """_render_readme rewrites docs/images references for the release ref."""
+
+ def test_relative_html_image_rewritten(self, tmp_path: Path) -> None:
+ _write_readme(tmp_path, '
')
+ out = hatch_build._render_readme(root=tmp_path, version="0.1.0")
+ assert f'src="{RAW}/v0.1.0/docs/images/RAMPART.svg"' in out
+
+ def test_dotslash_html_image_rewritten(self, tmp_path: Path) -> None:
+ _write_readme(tmp_path, '
')
+ out = hatch_build._render_readme(root=tmp_path, version="0.1.0")
+ assert f"{RAW}/v0.1.0/docs/images/RAMPART.svg" in out
+ assert "./docs/images" not in out
+
+ def test_markdown_image_rewritten(self, tmp_path: Path) -> None:
+ _write_readme(tmp_path, "")
+ out = hatch_build._render_readme(root=tmp_path, version="0.1.0")
+ assert out == f""
+
+ def test_titled_markdown_image_preserves_title(self, tmp_path: Path) -> None:
+ _write_readme(tmp_path, '')
+ out = hatch_build._render_readme(root=tmp_path, version="0.1.0")
+ assert out == f''
+
+ def test_absolute_github_raw_main_repinned(self, tmp_path: Path) -> None:
+ _write_readme(
+ tmp_path,
+ '
',
+ )
+ out = hatch_build._render_readme(root=tmp_path, version="0.1.0")
+ assert "raw/v0.1.0/docs/images/RAMPART.svg" in out
+ assert "raw/main/docs/images" not in out
+
+ def test_absolute_raw_host_main_repinned(self, tmp_path: Path) -> None:
+ _write_readme(tmp_path, f"{RAW}/main/docs/images/RAMPART.svg")
+ out = hatch_build._render_readme(root=tmp_path, version="0.1.0")
+ assert f"{RAW}/v0.1.0/docs/images/RAMPART.svg" in out
+ assert f"{RAW}/main/docs/images" not in out
+
+ def test_dev_version_keeps_main_ref(self, tmp_path: Path) -> None:
+ _write_readme(tmp_path, "")
+ out = hatch_build._render_readme(root=tmp_path, version="0.1.1.dev35")
+ assert out == f""
+
+ def test_no_image_passthrough(self, tmp_path: Path) -> None:
+ body = "# RAMPART\n\nNo images, only [a link](https://example.com).\n"
+ _write_readme(tmp_path, body)
+ assert hatch_build._render_readme(root=tmp_path, version="0.1.0") == body
diff --git a/uv.lock b/uv.lock
index ad308da4..be310074 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1065,6 +1065,34 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/69/b2/119f6e6dcbd96f9069ce9a2665e0146588dc9f88f29549711853645e736a/h2-4.3.0-py3-none-any.whl", hash = "sha256:c438f029a25f7945c69e0ccf0fb951dc3f73a5f6412981daee861431b70e2bdd", size = 61779, upload-time = "2025-08-23T18:12:17.779Z" },
]
+[[package]]
+name = "hatch-vcs"
+version = "0.5.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "hatchling" },
+ { name = "setuptools-scm" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/6b/b0/4cc743d38adbee9d57d786fa496ed1daadb17e48589b6da8fa55717a0746/hatch_vcs-0.5.0.tar.gz", hash = "sha256:0395fa126940340215090c344a2bf4e2a77bcbe7daab16f41b37b98c95809ff9", size = 11424, upload-time = "2025-05-27T05:16:04.49Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5f/48/1f85cee4b7b4f40b9b814b1febbc661bda6ced9649e410a0b74f6e415dd0/hatch_vcs-0.5.0-py3-none-any.whl", hash = "sha256:b49677dbdc597460cc22d01b27ab3696f5e16a21ecf2700fb01bc28e1f2a04a7", size = 8507, upload-time = "2025-05-27T05:16:03.184Z" },
+]
+
+[[package]]
+name = "hatchling"
+version = "1.30.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "packaging" },
+ { name = "pathspec" },
+ { name = "pluggy" },
+ { name = "trove-classifiers" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/63/4c/8717ccb844b4fa5a5ba6352e97d743ed24e9a22cf90b7c109c17030a46a1/hatchling-1.30.1.tar.gz", hash = "sha256:eee4fd45357f72ebb3d7a42e5d72cfb5e29ed426d79e8836288926c4258d5f2e", size = 56929, upload-time = "2026-06-02T00:09:41.487Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/56/49/2797ec0ef88008a653a8867bb8d1e5c223cd2df8e40390dd5c6a0279cbc5/hatchling-1.30.1-py3-none-any.whl", hash = "sha256:161eacafb3c6f91526e92116d21426369f2c36e98c36a864f11a96345ad4ee31", size = 77489, upload-time = "2026-06-02T00:09:40.139Z" },
+]
+
[[package]]
name = "hf-xet"
version = "1.5.1"
@@ -2997,7 +3025,6 @@ wheels = [
[[package]]
name = "rampart"
-version = "0.1.1.dev0"
source = { editable = "." }
dependencies = [
{ name = "jinja2" },
@@ -3016,6 +3043,8 @@ onedrive = [
[package.dev-dependencies]
dev = [
+ { name = "hatch-vcs" },
+ { name = "hatchling" },
{ name = "pre-commit" },
{ name = "pytest-cov" },
{ name = "pytest-xdist", extra = ["psutil"] },
@@ -3044,6 +3073,8 @@ provides-extras = ["onedrive"]
[package.metadata.requires-dev]
dev = [
+ { name = "hatch-vcs", specifier = ">=0.5.0" },
+ { name = "hatchling", specifier = ">=1.30.1" },
{ name = "pre-commit", specifier = ">=4.5.1" },
{ name = "pytest-cov", specifier = ">=6.1.0" },
{ name = "pytest-xdist", extras = ["psutil"], specifier = ">=3.8.0" },
@@ -3331,6 +3362,29 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d6/02/12c73fd423eb9577b97fc1924966b929eff7074ae6b2e15dd3d30cb9e4ae/segno-1.6.6-py3-none-any.whl", hash = "sha256:28c7d081ed0cf935e0411293a465efd4d500704072cdb039778a2ab8736190c7", size = 76503, upload-time = "2025-03-12T22:12:48.106Z" },
]
+[[package]]
+name = "setuptools"
+version = "82.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/4f/db/cfac1baf10650ab4d1c111714410d2fbb77ac5a616db26775db562c8fab2/setuptools-82.0.1.tar.gz", hash = "sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9", size = 1152316, upload-time = "2026-03-09T12:47:17.221Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", hash = "sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb", size = 1006223, upload-time = "2026-03-09T12:47:15.026Z" },
+]
+
+[[package]]
+name = "setuptools-scm"
+version = "10.1.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "packaging" },
+ { name = "setuptools" },
+ { name = "vcs-versioning" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a6/3e/edb74671eca6429f375244d1d6395c11b7d4832cda772e4c630141e121c7/setuptools_scm-10.1.1.tar.gz", hash = "sha256:c9eed4754da1a25016d49c1b3cd09c7c8e65f816b5afb8195bf2ac3c6748f23a", size = 66514, upload-time = "2026-06-22T14:15:44.086Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fa/a8/3e86057d0d6274e57b9b0b40cb14b90832552c33a8f855b3675843686284/setuptools_scm-10.1.1-py3-none-any.whl", hash = "sha256:4660e6a3b1764ff4b11188de93c26f02396ef294784a75e031e0a5f60c2379fe", size = 27692, upload-time = "2026-06-22T14:15:42.804Z" },
+]
+
[[package]]
name = "shellingham"
version = "1.5.4"
@@ -3589,6 +3643,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/2e/24/32361f5d0e2eff7ff1881ac6833b6b090cfe34515b1ee9082636cbe69442/treelib-1.8.0-py3-none-any.whl", hash = "sha256:5235d1ebf988c5026f26ce6e5e0cd470007f16d4978185f5c9b3eee8a25aef81", size = 30728, upload-time = "2025-06-29T15:06:48.248Z" },
]
+[[package]]
+name = "trove-classifiers"
+version = "2026.6.1.19"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/c2/e3/7ca82ee24c82d344584abd5b8637b3bd056f2900226e8d82fc22f1184b92/trove_classifiers-2026.6.1.19.tar.gz", hash = "sha256:c5132b4b61a829d11cfbd2d72e97f20a45ed6edb95e45c5efdeb5e00836b2745", size = 17059, upload-time = "2026-06-01T19:41:34.649Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7c/a4/81502f486f01db95bc8320646a8a12511f5e556cb63d5e224d91816605c4/trove_classifiers-2026.6.1.19-py3-none-any.whl", hash = "sha256:ab4c4ec93cc4a4e7815fa759906e05e6bb3f2fbd92ea0f897288c6a43efd15b3", size = 14211, upload-time = "2026-06-01T19:41:33.434Z" },
+]
+
[[package]]
name = "ty"
version = "0.0.56"
@@ -3750,6 +3813,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/e4/16/c1fd27e9549f3c4baf1dc9c20c456cd2f822dbf8de9f463824b0c0357e06/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e", size = 4296730, upload-time = "2025-10-16T22:17:00.744Z" },
]
+[[package]]
+name = "vcs-versioning"
+version = "2.0.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "packaging" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c6/5d/e6c5d8be9f637b7ac6cb83c2c51675c431092b76543866f9c152f3321a76/vcs_versioning-2.0.1.tar.gz", hash = "sha256:0e827e50ff98c3b74961bc9bab1bdb494d6ef9f9624bc0466afbafef23ff0d8c", size = 127905, upload-time = "2026-06-22T14:15:51.531Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/36/51/b9b812b8d09584d8cdfc4c19328e5a86e72f6da310d529cc009d194ac6a7/vcs_versioning-2.0.1-py3-none-any.whl", hash = "sha256:fa1a7e49745fb968af54d1422e1f6bcde2563046f2c283a1a8d720184ece6ea1", size = 105175, upload-time = "2026-06-22T14:15:50.132Z" },
+]
+
[[package]]
name = "virtualenv"
version = "21.5.1"