diff --git a/.github/bump_version.py b/.github/bump_version.py new file mode 100644 index 0000000..bb0fd6d --- /dev/null +++ b/.github/bump_version.py @@ -0,0 +1,79 @@ +"""Infer semver bump from towncrier fragment types and update version.""" + +import re +import sys +from pathlib import Path + + +def get_current_version(pyproject_path: Path) -> str: + text = pyproject_path.read_text() + match = re.search(r'^version\s*=\s*"(\d+\.\d+\.\d+)"', text, re.MULTILINE) + if not match: + print( + "Could not find version in pyproject.toml", + file=sys.stderr, + ) + sys.exit(1) + return match.group(1) + + +def infer_bump(changelog_dir: Path) -> str: + fragments = [ + f + for f in changelog_dir.iterdir() + if f.is_file() and f.name != ".gitkeep" + ] + if not fragments: + print("No changelog fragments found", file=sys.stderr) + sys.exit(1) + + categories = {f.suffix.lstrip(".") for f in fragments} + for f in fragments: + parts = f.stem.split(".") + if len(parts) >= 2: + categories.add(parts[-1]) + + if "breaking" in categories: + return "major" + if "added" in categories or "removed" in categories: + return "minor" + return "patch" + + +def bump_version(version: str, bump: str) -> str: + major, minor, patch = (int(x) for x in version.split(".")) + if bump == "major": + return f"{major + 1}.0.0" + elif bump == "minor": + return f"{major}.{minor + 1}.0" + else: + return f"{major}.{minor}.{patch + 1}" + + +def update_file(path: Path, old_version: str, new_version: str): + text = path.read_text() + updated = text.replace( + f'version = "{old_version}"', + f'version = "{new_version}"', + ) + if updated != text: + path.write_text(updated) + print(f" Updated {path}") + + +def main(): + root = Path(__file__).resolve().parent.parent + pyproject = root / "pyproject.toml" + changelog_dir = root / "changelog.d" + + current = get_current_version(pyproject) + bump = infer_bump(changelog_dir) + new = bump_version(current, bump) + + print(f"Version: {current} -> {new} ({bump})") + + update_file(pyproject, current, new) + + +if __name__ == "__main__": + main() diff --git a/.github/workflows/changelog_entry.yaml b/.github/workflows/changelog_entry.yaml index 911c28e..c74ac3d 100644 --- a/.github/workflows/changelog_entry.yaml +++ b/.github/workflows/changelog_entry.yaml @@ -1,29 +1,21 @@ -name: Versioning +name: Changelog on: pull_request: branches: [ main ] jobs: - check-changelog-entry: - name: Changelog entry check + check-changelog: + name: Check changelog fragment runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - - name: Check for changelog entry + - uses: actions/checkout@v4 + - name: Check for changelog fragment run: | - if [ ! -f "changelog_entry.yaml" ]; then - echo "Error: changelog_entry.yaml file is missing." - echo "Please add a changelog_entry.yaml file at the root of the repository." + FRAGMENTS=$(find changelog.d -type f ! -name '.gitkeep' | wc -l) + if [ "$FRAGMENTS" -eq 0 ]; then + echo "::error::No changelog fragment found in changelog.d/" + echo "Add one with: echo 'Description.' > changelog.d/\$(git branch --show-current)..md" + echo "Types: added, changed, fixed, removed, breaking" exit 1 fi - - # Check if the file is empty - if [ ! -s "changelog_entry.yaml" ]; then - echo "Error: changelog_entry.yaml file is empty." - echo "Please add content to the changelog_entry.yaml file." - exit 1 - fi - - echo "Changelog entry found and is not empty." diff --git a/.github/workflows/versioning.yaml b/.github/workflows/versioning.yaml index 06a5fec..5df2b1f 100644 --- a/.github/workflows/versioning.yaml +++ b/.github/workflows/versioning.yaml @@ -7,7 +7,7 @@ on: - main paths: - - changelog_entry.yaml + - changelog.d/** - "!pyproject.toml" jobs: @@ -19,17 +19,17 @@ jobs: - name: Checkout repo uses: actions/checkout@v4 with: - repository: ${{ github.event.pull_request.head.repo.full_name }} - ref: ${{ github.event.pull_request.head.ref }} token: ${{ secrets.POLICYENGINE_GITHUB }} + fetch-depth: 0 - name: Setup Python uses: actions/setup-python@v5 with: python-version: 3.13 - name: Build changelog - run: pip install yaml-changelog && make changelog - - name: Preview changelog update - run: ".github/get-changelog-diff.sh" + run: | + pip install towncrier + python .github/bump_version.py + towncrier build --yes --version $(python -c "import re; print(re.search(r'version = \"(.+?)\"', open('pyproject.toml').read()).group(1))") - name: Update changelog uses: EndBug/add-and-commit@v9 with: @@ -60,4 +60,3 @@ jobs: user: __token__ password: ${{ secrets.PYPI }} skip-existing: true - diff --git a/Makefile b/Makefile index e8a7e0b..95ae627 100644 --- a/Makefile +++ b/Makefile @@ -29,12 +29,8 @@ clean: rm -rf docs/_build/ changelog: - build-changelog changelog.yaml --output changelog.yaml --update-last-date --start-from 0.1.0 --append-file changelog_entry.yaml - build-changelog changelog.yaml --org PolicyEngine --repo microcalibrate --output CHANGELOG.md --template .github/changelog_template.md - bump-version changelog.yaml pyproject.toml - rm changelog_entry.yaml || true - touch changelog_entry.yaml - + python .github/bump_version.py + towncrier build --yes --version $$(python -c "import re; print(re.search(r'version = \"(.+?)\"', open('pyproject.toml').read()).group(1))") dashboard-install: cd microcalibration-dashboard && npm install diff --git a/changelog_entry.yaml b/changelog.d/.gitkeep similarity index 100% rename from changelog_entry.yaml rename to changelog.d/.gitkeep diff --git a/changelog.d/migrate-to-towncrier.changed.md b/changelog.d/migrate-to-towncrier.changed.md new file mode 100644 index 0000000..865484a --- /dev/null +++ b/changelog.d/migrate-to-towncrier.changed.md @@ -0,0 +1 @@ +Migrated from changelog_entry.yaml to towncrier fragments to eliminate merge conflicts. diff --git a/changelog.yaml b/changelog.yaml deleted file mode 100644 index 81a55cb..0000000 --- a/changelog.yaml +++ /dev/null @@ -1,165 +0,0 @@ -- changes: - added: - - Initialized project. - date: 2025-06-18 13:44:19 - version: 0.1.0 -- bump: minor - changes: - changed: - - Initialized changelogging. - date: 2025-06-18 13:19:30 -- bump: minor - changes: - added: - - Basic Calibration input checks. - date: 2025-06-19 16:34:04 -- bump: minor - changes: - added: - - Logging performance across epochs when calibrating. - date: 2025-06-20 11:03:38 -- bump: minor - changes: - added: - - Summary of calibration results. - date: 2025-06-20 12:55:18 -- bump: minor - changes: - added: - - Test for warning logic in Calibration() input checks. - date: 2025-06-23 10:15:54 -- bump: minor - changes: - added: - - Calibration performance dashboard. - date: 2025-06-24 09:23:16 -- bump: minor - changes: - added: - - Adding the calibration performance dashboard link to documentation. - date: 2025-06-24 10:25:50 -- bump: minor - changes: - added: - - Creating github artifact to save calibration log for test. - - Interface to load CSVs from GitHub. - date: 2025-06-24 13:08:56 -- bump: minor - changes: - added: - - Small performance dashboard fix. - date: 2025-06-24 16:38:27 -- bump: minor - changes: - added: - - Estimate function, over loss matrix. - date: 2025-06-25 14:31:58 -- bump: minor - changes: - added: - - Adding GitHub artifact comparison to dashboard. - date: 2025-06-25 14:58:22 -- bump: minor - changes: - added: - - Creating deeplinks. - date: 2025-06-25 17:27:46 -- bump: minor - changes: - added: - - Adding total loss and error over epoch plots to dashboard. - - Ordering targets alphanumerically in dashboard. - date: 2025-06-26 11:01:53 -- bump: patch - changes: - fixed: - - Final weights are now consistent with the training log. - date: 2025-06-26 11:41:39 -- bump: patch - changes: - changed: - - Loading dashboard automatically when sharing a deeplink. - date: 2025-06-26 11:47:53 -- bump: patch - changes: - changed: - - Subsample to 10 epochs when loading dashboard. - date: 2025-06-30 13:40:33 -- bump: patch - changes: - changed: - - Increase limit to csv size. - date: 2025-06-30 15:30:19 -- bump: patch - changes: - changed: - - Taking abs val for abs_rel_error denominator. - date: 2025-06-30 17:15:57 -- bump: minor - changes: - added: - - Normalization parameter to handle multi-level geography calibration. - date: 2025-07-07 11:51:26 -- bump: patch - changes: - changed: - - Normalization parameter to handle multi-level geography calibration added to - Calibration class. - date: 2025-07-07 12:34:47 -- bump: minor - changes: - added: - - Add excluded_targets logic to handle holdout targets. - date: 2025-07-15 10:32:10 -- bump: minor - changes: - added: - - Adding analytical assessment of targets to Calibration class. - - Enhance dashboard to show all targets even if not overlapping and better font - / table view. - date: 2025-07-21 16:09:24 -- bump: minor - changes: - added: - - L0 regularization logic. - date: 2025-07-25 13:26:12 -- bump: minor - changes: - added: - - A function to evaluate whether estimates are within desired tolerance levels. - date: 2025-07-25 14:42:01 -- bump: minor - changes: - added: - - Adding column to sort by calibration difference to dashboard comparisons page. - - Ensure pagination so all repo branches are visible in the github loading. - date: 2025-08-04 15:53:14 -- bump: patch - changes: - added: - - Added a parameter to adjust the learning rate of the sparse optimizer. - - Fixed label in dashboard that incorrectly displayed 'estimate' instead of 'target'. - date: 2025-08-11 15:25:07 -- bump: patch - changes: - changed: - - Moved to PolicyEngine's L0 package for regularization implementation. - - Moved to python 3.13. - date: 2025-08-22 07:42:30 -- bump: minor - changes: - added: - - Add hyperparameter tuning for L0 implementation with option to holdout targets. - - Add method to evaluate robustness of calibration to target holdouts. - date: 2025-08-22 11:04:35 -- bump: minor - changes: - added: - - Adding documentation for L0, hyperparameter tuning and robustness checks. - date: 2025-08-22 13:47:07 -- bump: patch - changes: - fixed: - - Fixed critical React Server Components CVE vulnerabilities (CVE-2025-55182, - CVE-2025-66478) - date: 2026-01-06 00:04:35 diff --git a/pyproject.toml b/pyproject.toml index bb06f16..369ea05 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,8 @@ dev = [ "mypy", "build", "linecheck", - "yaml-changelog>=0.1.7", + "yaml-changelog>=0.1.7", "towncrier>=24.8.0", + ] docs = [ @@ -72,3 +73,36 @@ policyengine-calibrate = "policyengine_calibrate:main" [build-system] requires = ["hatchling"] build-backend = "hatchling.build" + +[tool.towncrier] +package = "microcalibrate" +directory = "changelog.d" +filename = "CHANGELOG.md" +title_format = "## [{version}] - {project_date}" +issue_format = "" +underlines = ["", "", ""] + +[[tool.towncrier.type]] +directory = "breaking" +name = "Breaking changes" +showcontent = true + +[[tool.towncrier.type]] +directory = "added" +name = "Added" +showcontent = true + +[[tool.towncrier.type]] +directory = "changed" +name = "Changed" +showcontent = true + +[[tool.towncrier.type]] +directory = "fixed" +name = "Fixed" +showcontent = true + +[[tool.towncrier.type]] +directory = "removed" +name = "Removed" +showcontent = true