From 5fa5937ff32d5d53f637170c4615ba849295497a Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 29 Aug 2026 20:01:16 +0000 Subject: [PATCH 1/8] docs: add CHANGELOG.md, populated back to v8.7.1, and auto-update it on release Adds a Keep a Changelog-style CHANGELOG.md covering every release from v8.7.1 through v9.0.38, built from tag history, GitHub Release notes, and merged PRs. Consecutive metadata-only releases are condensed into ranged entries; real code changes (bug fixes, new APIs, the strong-naming saga in v9.0.0/v9.0.1, TFM migrations, the Extensions package, the metadata-automation itself, etc.) get their own entries. github-actions-metadata-update.sh now appends a CHANGELOG.md entry for the release it's about to open a PR for, in the same commit as the metadata sync. The version number is already known at that point (it's the upstream tag being synced, copied verbatim), so there is nothing to guess. This was chosen over updating the changelog from finalize_metadata_release.yml because that workflow only tags an existing commit and calls the Releases API - it can't push a follow-up commit of its own, since main's branch-protection ruleset requires every push to go through a PR with no bypass for any actor, including this automation's own bot account (the same reason the sync switched from a direct push to a PR in the first place). Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01WVGRJyQwtvBGXAmWswxyqW --- CHANGELOG.md | 245 ++++++++++++++++++++++++++ README.md | 4 +- lib/github-actions-metadata-update.sh | 30 +++- 3 files changed, 277 insertions(+), 2 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..9a022784 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,245 @@ +# Changelog + +All notable changes to `libphonenumber-csharp` and its companion `libphonenumber-csharp.extensions` +package are documented here. + +**This project's release cadence is unusual for a changelog.** Nearly every release exists purely to +track a new upstream [google/libphonenumber](https://github.com/google/libphonenumber) metadata +release (about every two weeks), cut automatically by +[`create_new_release_on_new_metadata_update.yml`](.github/workflows/create_new_release_on_new_metadata_update.yml). +Those releases get a single mechanical entry here, added by +[`lib/github-actions-metadata-update.sh`](lib/github-actions-metadata-update.sh) in the very same +commit that syncs the metadata — nothing here is guessed ahead of time, since the version number is +already known: it's copied straight from the upstream tag being synced. Long runs of consecutive +metadata-only releases are condensed into a single ranged entry so this file stays readable; every +release still has a matching entry, so no version number is skipped. + +Entries that describe an actual code change — a bug fix, a new API, a breaking change — were written +by hand at review time. For the exhaustive per-PR detail behind any release (including the routine +ones), see its [GitHub Release](https://github.com/twcclegg/libphonenumber-csharp/releases), whose +notes are auto-generated from merged PR titles. + +Format loosely follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versioning tracks +whatever upstream `google/libphonenumber` release this port is synced to, not strict +[SemVer](https://semver.org/) — see +["Why keep libphonenumber-csharp up to date?"](README.md#why-keep-libphonenumber-csharp-up-to-date) +in the README for why staying current matters even for patch-looking bumps. + + + +## [v9.0.38](https://github.com/twcclegg/libphonenumber-csharp/compare/v9.0.37...v9.0.38) - 2026-08-28 + +A larger-than-usual release: the metadata-sync automation itself was reworked, alongside a batch of +Extensions API additions and hardening fixes. + +- Added (Extensions): `PhoneNumber.TryParse`/`TryParseValid` 2-argument overloads matching the + conventional .NET `TryParse` shape; `PhoneNumberTypeConverter` for `TypeConverter`-based binding + (config binding, `PropertyGrid`, etc.); `PhoneNumberAttribute`, a `DataAnnotations` + `ValidationAttribute` backed by real validation (`PhoneNumberUtil.IsValidNumber`) instead of the + framework's loose `[Phone]` regex. (#405) +- Security: fixed a path-traversal issue in `BuildLocaleNames` and an uncaught parse exception in + `PhoneNumberTypeConverter`. (#428) +- Fixed: `NumberParseException` (rather than `ArgumentOutOfRangeException`) on malformed RFC3966 + `phone-context` ordering (#406); three `PhoneNumberMatcher`/`AsYouTypeFormatter` parity gaps + against the upstream Java source (#408); unbounded growth in `AsYouTypeFormatter` and missing + null-guards in `PhoneNumberUtil` (#402). +- Changed: metadata-sync automation now opens a PR (authenticated as the `libphonenumber-csharp-bot` + account) and auto-merges it, instead of pushing straight to `main` — see the "Metadata updates" + design note this same change added to CHANGELOG.md automation, below. Also: added a fuzzing target + for the parsing surface (FsCheck properties + SharpFuzz, #392); migrated the solution to `.slnx` + and picked up a transitive `AngleSharp` advisory fix (#388); added build-provenance attestation for + published packages (#400). +- A large batch of purely internal idiomatic-C# cleanups and documentation refreshes with no + behavior change is omitted here — see the release notes for the full PR list. +- Metadata update to upstream [libphonenumber v9.0.38](https://github.com/google/libphonenumber/releases/tag/v9.0.38). + +## [v9.0.32 – v9.0.37](https://github.com/twcclegg/libphonenumber-csharp/compare/v9.0.31...v9.0.37) - 2026-06-05 – 2026-08-14 + +6 metadata-only releases tracking upstream libphonenumber. No source changes. + +## [v9.0.31](https://github.com/twcclegg/libphonenumber-csharp/compare/v9.0.30...v9.0.31) - 2026-05-23 + +- Added: cross-platform embedded debug symbols for both packages. (#340) +- Metadata update to upstream v9.0.31. + +## [v9.0.30](https://github.com/twcclegg/libphonenumber-csharp/compare/v9.0.29...v9.0.30) - 2026-05-07 + +- Added: `net10.0` target framework. +- Changed: geocoding and time-zone prefix maps are now generated to per-region binary files at build + time (`PhoneNumbers.MetadataBuilder`) and loaded from embedded resources at runtime, replacing the + bundled `geocoding.zip` and runtime text parsing. This completes the same "binary metadata" move + already made for the phone/short-number/alternate-format XML. +- Metadata update to upstream v9.0.30. + +## [v9.0.2 – v9.0.29](https://github.com/twcclegg/libphonenumber-csharp/compare/v9.0.1...v9.0.29) - 2025-03-28 – 2026-04-25 + +28 releases, almost entirely metadata syncs and routine CI/dependency maintenance (Dependabot +bumps, `net9.0` support added and later dropped again as it went out of support). One test-only fix +kept CI green after a `System.Collections.Immutable` behavior change (#297). No user-facing source +changes. + +## [v9.0.1](https://github.com/twcclegg/libphonenumber-csharp/compare/v9.0.0...v9.0.1) - 2025-03-23 + +- Reverted the assembly strong-name signing added one release earlier in v9.0.0 (below), after it + caused problems for consumers. (#291) + +## [v9.0.0](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.55...v9.0.0) - 2025-03-11 + +- **Breaking:** both packages' assemblies are strong-name signed. (#288) Bumped the major version + because it changes assembly identity — reverted the very next release, v9.0.1 above, after it + turned out to break more consumers than it helped. + +## [v8.13.51 – v8.13.55](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.50...v8.13.55) - 2024-12-02 – 2025-02-14 + +5 metadata-only releases tracking upstream libphonenumber. No source changes. + +## [v8.13.50](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.49...v8.13.50) - 2024-11-16 + +- Added: `net9.0` target framework (dropped again once out of support — see the v9.0.2–v9.0.29 entry + above). (#273) + +## [v8.13.45 – v8.13.49](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.44...v8.13.49) - 2024-09-06 – 2024-11-04 + +5 metadata-only releases tracking upstream libphonenumber. No source changes. + +## [v8.13.44](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.43...v8.13.44) - 2024-08-26 + +- Added: `net8.0` target framework; dropped `net7.0`. (#264) + +## [v8.13.41 – v8.13.43](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.40...v8.13.43) - 2024-07-25 – 2024-08-09 + +3 metadata-only releases tracking upstream libphonenumber. No source changes. + +## [v8.13.40](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.39...v8.13.40) - 2024-07-03 + +- Fixed: area-code information was lost for all Mexican (MX) numbers. + +## [v8.13.28 – v8.13.39](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.27...v8.13.39) - 2024-01-25 – 2024-06-15 + +12 releases, mostly metadata syncs. Code coverage now runs and uploads on every PR instead of only +on `main` (#245, #250). No other source changes. + +## [v8.13.27](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.26...v8.13.27) - 2023-12-21 + +- Fixed: a null-reference exception during number normalization when given a null input. (#207) + +## [v8.13.26](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.25...v8.13.26) - 2023-11-29 + +- Changed: metadata updates are now proposed and published automatically instead of by hand — the + first `feat: automatic upgrade to vX.Y.Z` release, and the origin of most of the entries in this + file. (#192) +- Added: Dependabot configuration for NuGet package updates. (#185) + +## [v8.13.25](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.24...v8.13.25) - 2023-11-20 + +- Fixed: a leading `+` was incorrectly stripped/ignored when parsing. (#184) + +## [v8.13.19 – v8.13.24](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.18...v8.13.24) - 2023-08-22 – 2023-10-31 + +6 metadata-only releases tracking upstream libphonenumber. No source changes. + +## [v8.13.18](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.17...v8.13.18) - 2023-08-03 + +- Changed: reduced allocations on the parsing/formatting hot path by skipping internal + protobuf-style builder objects. (#180) + +## [v8.13.17](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.16...v8.13.17) - 2023-07-26 + +- Changed: reduced unnecessary regex construction and usage on hot paths. (#178) + +## [v8.13.12 – v8.13.16](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.11...v8.13.16) - 2023-06-15 – 2023-07-11 + +5 metadata-only releases tracking upstream libphonenumber. No source changes. + +## [v8.13.11](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.10...v8.13.11) - 2023-04-27 + +Metadata-only release tracking upstream libphonenumber. No source changes. + +## [v8.13.10](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.9...v8.13.10) - 2023-04-20 + +- Added: `net6.0` target framework. +- Changed: `Format()` uses stack-allocated `Span` instead of heap allocations on the + formatting hot path. (#166) + +## [v8.13.9](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.8...v8.13.9) - 2023-04-10 + +Metadata-only release tracking upstream libphonenumber. No source changes. + +## [v8.13.8](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.7...v8.13.8) - 2023-03-27 + +- Added (Extensions): a `System.Text.Json` converter for `PhoneNumber`. + +## [v8.13.3 – v8.13.7](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.2...v8.13.7) - 2022-12-22 – 2023-03-03 + +5 metadata-only releases tracking upstream libphonenumber. No source changes. + +## [v8.13.2](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.1...v8.13.2) - 2022-12-08 + +- Added: `PhoneNumberToTimeZonesMapper`, mapping a phone number to its candidate IANA time zones. + (#163) + +## [v8.13.1](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.0...v8.13.1) - 2022-11-28 + +- Added: the `libphonenumber-csharp.extensions` NuGet package, for C#-idiomatic helpers that have no + Java-upstream equivalent. (#164) + +## [v8.12.44 – v8.13.0](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.43...v8.13.0) - 2022-02-24 – 2022-11-08 + +16 metadata-only releases tracking upstream libphonenumber. No source changes. + +## [v8.12.39 – v8.12.43](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.38...v8.12.43) - 2022-02-14 + +- Removed: the `net35` target framework and other end-of-life targets. (#146) +- 5 metadata-only patch releases shipped the same day as this change. + +## [v8.12.35 – v8.12.38](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.34...v8.12.38) - 2021-12-15 + +- Added: a way to refresh a `PhoneNumberUtil` instance's metadata from a stream at runtime, without + restarting the process. (#142) +- 4 metadata-only patch releases shipped the same day as this change. + +## [v8.11.4 – v8.12.34](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.11.3...v8.12.34) - 2020-02-13 – 2021-10-07 + +35 metadata-only releases tracking upstream libphonenumber. No source changes. + +## [v8.11.3](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.11.2...v8.11.3) - 2020-02-03 + +- Fixed: numbers with multiple leading zeroes were parsed incorrectly. (#111) + +## [v8.10.1 – v8.11.2](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.10.0...v8.11.2) - 2018-11-28 – 2020-01-15 + +26 metadata-only releases tracking upstream libphonenumber. No source changes. + +## [v8.10.0](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.9.16...v8.10.0) - 2018-11-28 + +- Upstream libphonenumber 8.10.0 changed how `AsYouTypeFormatter` chooses between international and + national-dialing rules, correctly formatting some numbers that use a national prefix but aren't + internationally diallable. (#89) + +## [v8.9.9 – v8.9.16](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.9.8...v8.9.16) - 2018-06-29 – 2018-10-19 + +8 metadata-only releases tracking upstream libphonenumber. No source changes. + +## [v8.9.8](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.9.7...v8.9.8) - 2018-06-15 + +- Added: `ShortNumberUtil`, providing information about short codes — the predecessor of today's + `ShortNumberInfo`. (#64) + +## [v8.9.4.1 – v8.9.7](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.9.3...v8.9.7) - 2018-04-17 – 2018-05-30 + +4 metadata-only releases tracking upstream libphonenumber. No source changes. + +## [v8.9.3](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.9.2...v8.9.3) - 2018-04-05 + +- Added: a `net35` target framework, using embedded XML resources instead of the standard resource + pipeline. (#58) (Dropped again in v8.12.39–v8.12.43, 2022-02-14, above.) + +## [v8.8.0 – v8.9.2](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.7.1...v8.9.2) - 2017-08-22 – 2018-03-19 + +16 metadata-only releases tracking upstream libphonenumber. No source changes. + +## [v8.7.1](https://github.com/twcclegg/libphonenumber-csharp/releases/tag/v8.7.1) - 2017-08-03 + +Earliest release with a recoverable history and the baseline for this changelog. See +[GitHub Releases](https://github.com/twcclegg/libphonenumber-csharp/releases) for anything published +before it. diff --git a/README.md b/README.md index 4ad1d9a4..41c35d26 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ See [this](csharp/README.md) for details about the port. Phone number metadata is updated in the Google repo approximately every two weeks. This library is automatically updated by a [scheduled github action](https://github.com/twcclegg/libphonenumber-csharp/actions/workflows/create_new_release_on_new_metadata_update.yml) to include the latest metadata, usually within a day. See [Metadata updates](#metadata-updates) for how that works and how to run it manually. +See [CHANGELOG.md](CHANGELOG.md) for release history. + ## Installation Run the following command to add this library to your project @@ -185,7 +187,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for the build settings that will fail CI ## Metadata updates -The [`create_new_release_on_new_metadata_update`](https://github.com/twcclegg/libphonenumber-csharp/actions/workflows/create_new_release_on_new_metadata_update.yml) workflow runs daily and drives [`lib/github-actions-metadata-update.sh`](lib/github-actions-metadata-update.sh). When the latest `google/libphonenumber` release is newer than the published NuGet package, it copies the upstream `resources/`, regenerates `resources/locale/country_names.txt`, then commits, pushes to a `metadata-update/*` branch and opens a PR against `main` with auto-merge enabled. The push and PR authenticate as the dedicated `libphonenumber-csharp-bot` account (via the `BOT_ACCESS_TOKEN` secret) rather than the default `GITHUB_TOKEN`, since GitHub requires a maintainer to manually approve workflow runs on PRs opened with `GITHUB_TOKEN`. Once that PR's required checks pass and it merges, [`finalize_metadata_release`](https://github.com/twcclegg/libphonenumber-csharp/actions/workflows/finalize_metadata_release.yml) tags the merge commit, creates a matching GitHub release, and dispatches the NuGet publish. +The [`create_new_release_on_new_metadata_update`](https://github.com/twcclegg/libphonenumber-csharp/actions/workflows/create_new_release_on_new_metadata_update.yml) workflow runs daily and drives [`lib/github-actions-metadata-update.sh`](lib/github-actions-metadata-update.sh). When the latest `google/libphonenumber` release is newer than the published NuGet package, it copies the upstream `resources/`, regenerates `resources/locale/country_names.txt`, adds a [CHANGELOG.md](CHANGELOG.md) entry for the release, then commits, pushes to a `metadata-update/*` branch and opens a PR against `main` with auto-merge enabled. The push and PR authenticate as the dedicated `libphonenumber-csharp-bot` account (via the `BOT_ACCESS_TOKEN` secret) rather than the default `GITHUB_TOKEN`, since GitHub requires a maintainer to manually approve workflow runs on PRs opened with `GITHUB_TOKEN`. Once that PR's required checks pass and it merges, [`finalize_metadata_release`](https://github.com/twcclegg/libphonenumber-csharp/actions/workflows/finalize_metadata_release.yml) tags the merge commit, creates a matching GitHub release, and dispatches the NuGet publish. The changelog entry is written in the same PR rather than afterwards: `main`'s branch-protection ruleset requires every push to go through a PR with no bypass for any actor (including this automation's own bot account), so `finalize_metadata_release` — which only tags an existing commit and calls the Releases API — has no way to push a follow-up commit of its own. The version number is already known at PR-open time (it's copied straight from the upstream tag), so there's nothing to guess. Before doing any of that it inspects the upstream diff and stops if it contains `.java` or `.proto` files, because changes to the Java sources may need porting by hand and an unattended metadata bump would silently skip them. diff --git a/lib/github-actions-metadata-update.sh b/lib/github-actions-metadata-update.sh index f8d60467..01297c3e 100644 --- a/lib/github-actions-metadata-update.sh +++ b/lib/github-actions-metadata-update.sh @@ -329,6 +329,7 @@ if isTrue "${DRY_RUN}"; then log "dry run complete, a real run would now:" log " - replace ${GITHUB_ACTION_WORKING_DIRECTORY}/resources with $(find "${UPSTREAM_RESOURCES}" -type f | wc -l | tr -d ' ') files from ${UPSTREAM_GITHUB_RELEASE_TAG}" log " - regenerate resources/locale/country_names.txt with $(java -version 2>&1 | head -n 1 || echo 'the local jdk')" + log " - add a CHANGELOG.md entry for ${UPSTREAM_GITHUB_RELEASE_TAG}" log " - commit \"feat: automatic upgrade to ${UPSTREAM_GITHUB_RELEASE_TAG}\" on ${BRANCH} and push it" log " - open a PR from ${BRANCH} into main and enable auto-merge" log " - once that PR's required checks pass and it merges, finalize-metadata-release.sh creates release ${UPSTREAM_GITHUB_RELEASE_TAG} and dispatches ${PUBLISH_WORKFLOW}" @@ -361,6 +362,33 @@ if [ -z "$(git status --porcelain)" ]; then exit 0 fi +# Record this release in CHANGELOG.md in the same commit as the metadata sync, rather than as a +# separate PR once the tag exists: the version number is already known here (it's +# UPSTREAM_GITHUB_RELEASE_TAG itself - this port tracks upstream's version 1:1), so there is +# nothing to guess. The finalize step (finalize-metadata-release.sh) only tags and releases an +# existing commit; it can't push a follow-up commit of its own; main's branch-protection ruleset +# requires a PR for every push, with no bypass for any actor, including this automation's own +# bot account - the same reason this script opens a PR instead of pushing directly (see the +# file-level comment above). Doing it here keeps everything in the one PR that already goes +# through that ruleset. +CHANGELOG_FILE="${GITHUB_ACTION_WORKING_DIRECTORY}/CHANGELOG.md" +CHANGELOG_MARKER='' +if [ -f "${CHANGELOG_FILE}" ] && grep -qF "${CHANGELOG_MARKER}" "${CHANGELOG_FILE}"; then + CHANGELOG_ENTRY_FILE="${WORK_DIR}/changelog-entry.md" + cat >"${CHANGELOG_ENTRY_FILE}" < Date: Sun, 30 Aug 2026 01:21:08 +0000 Subject: [PATCH 2/8] docs: rebuild CHANGELOG.md from actual per-tag commit history The previous CHANGELOG.md draft bucketed large swaths of real work into "metadata-only" ranges by pattern-matching version numbers rather than checking what actually shipped in each tag. In particular v9.0.31-v9.0.37 was condensed to "6 metadata-only releases, no source changes" even though that stretch contains dotnet10 TFM support, AOT compatibility fixes, a full performance-optimization pass, a redesigned demo website, XXE hardening of the XML metadata parser, SECURITY.md/CODE_OF_CONDUCT/ CODEOWNERS, and deterministic-build work. This rebuild classifies all 212 consecutive tag-to-tag ranges from v8.7.1 to v9.0.38 by actually walking their commit logs and diffs, using `git log`/`git show` against the real history plus cross-referencing PR numbers where a commit subject alone was unclear. Ranges with zero hand-authored source/test/docs/dependency changes (metadata-only syncs, version-bump-only project-file touches, and mechanical LocaleData.cs regenerations) are condensed into ranged entries as before; every one of the 100+ ranges that touched real work gets its own entry, sized to what happened - a one-liner for a small fix, multiple ### subsections for a large release. Several misattributions in the previous draft were also corrected (e.g. net10.0 support actually landed in v9.0.32, not v9.0.30; the Extensions NuGet package and PhoneNumberToTimeZonesMapper both shipped in v8.13.2, not split across v8.13.1/v8.13.2). Every one of the 213 tags is covered by exactly one heading, verified against classification2.tsv with no gaps or duplicates, and headings are in strict descending version order. The `` marker automation in lib/github-actions-metadata-update.sh needed no changes - re-verified its `sed`-based insertion against the rebuilt file, and re-ran `bash -n` and shellcheck on the script. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01WVGRJyQwtvBGXAmWswxyqW --- CHANGELOG.md | 764 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 670 insertions(+), 94 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a022784..86fef883 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,14 +10,29 @@ release (about every two weeks), cut automatically by Those releases get a single mechanical entry here, added by [`lib/github-actions-metadata-update.sh`](lib/github-actions-metadata-update.sh) in the very same commit that syncs the metadata — nothing here is guessed ahead of time, since the version number is -already known: it's copied straight from the upstream tag being synced. Long runs of consecutive -metadata-only releases are condensed into a single ranged entry so this file stays readable; every -release still has a matching entry, so no version number is skipped. - -Entries that describe an actual code change — a bug fix, a new API, a breaking change — were written -by hand at review time. For the exhaustive per-PR detail behind any release (including the routine -ones), see its [GitHub Release](https://github.com/twcclegg/libphonenumber-csharp/releases), whose -notes are auto-generated from merged PR titles. +already known: it's copied straight from the upstream tag being synced. + +That said, **real work constantly lands on `main` between metadata syncs and rides along in whatever +release happens to come next** — a version number by itself says nothing about how much changed. +This file was rebuilt from the actual commit history (not from the version-number pattern) to reflect +that: every release range that touched hand-written source, tests, docs, CI/build configuration, or a +shipped package's dependencies gets its own entry, sized to what actually happened — a one-liner for +a small fix, several bullets under `### Added`/`### Fixed`/`### Changed`/`### Performance` for a +large one. Two categories of file changes are folded into the plain metadata-sync entries even though +they're not `resources/*.xml` themselves, because they carry no hand-authored content: the generated +`LocaleData.cs` (regenerated by the same script from the local JDK on every sync, see +[`lib/DumpLocale.java`](lib/DumpLocale.java)) and version-bump-only touches to a `.csproj`/`.sln`/ +`appveyor.yml` (a bumped version string, nothing else in the diff). Long runs of releases with +*no* such changes are condensed into a single ranged entry so this file stays readable; every release +still has a matching entry, so no version number is skipped. One stretch of early history (October +2019) briefly synced this repo's full upstream multi-language source tree (`cpp/`, `java/`, +`debian/`) alongside the C# port; those files are called out but not itemized. + +Entries that describe an actual code change were written by hand at review time, tracing each claim +back to the commit(s) or PR(s) named next to it. For the exhaustive per-PR detail behind any release +(including the routine ones), see its +[GitHub Release](https://github.com/twcclegg/libphonenumber-csharp/releases), whose notes are +auto-generated from merged PR titles. Format loosely follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versioning tracks whatever upstream `google/libphonenumber` release this port is synced to, not strict @@ -30,53 +45,221 @@ in the README for why staying current matters even for patch-looking bumps. ## [v9.0.38](https://github.com/twcclegg/libphonenumber-csharp/compare/v9.0.37...v9.0.38) - 2026-08-28 A larger-than-usual release: the metadata-sync automation itself was reworked, alongside a batch of -Extensions API additions and hardening fixes. - -- Added (Extensions): `PhoneNumber.TryParse`/`TryParseValid` 2-argument overloads matching the - conventional .NET `TryParse` shape; `PhoneNumberTypeConverter` for `TypeConverter`-based binding - (config binding, `PropertyGrid`, etc.); `PhoneNumberAttribute`, a `DataAnnotations` - `ValidationAttribute` backed by real validation (`PhoneNumberUtil.IsValidNumber`) instead of the - framework's loose `[Phone]` regex. (#405) -- Security: fixed a path-traversal issue in `BuildLocaleNames` and an uncaught parse exception in +Extensions API additions, security/hardening fixes, and a large internal code-quality pass. + +### Added +- (Extensions) `PhoneNumber.TryParse`/`TryParseValid` 2-argument overloads matching the conventional + .NET `TryParse` shape; `PhoneNumberTypeConverter` for `TypeConverter`-based binding (config + binding, `PropertyGrid`, etc.); `PhoneNumberAttribute`, a `DataAnnotations` `ValidationAttribute` + backed by real validation (`PhoneNumberUtil.IsValidNumber`) instead of the framework's loose + `[Phone]` regex. (#405) +- A SharpFuzz/FsCheck-based fuzzing target for the parsing surface (`PhoneNumbers.Fuzz`, not part of + the solution, run weekly by `fuzz.yml`). (#392) +- Build-provenance attestation for the published NuGet packages. (#400) + +### Changed +- Metadata-sync automation now opens a PR (authenticated as a dedicated `libphonenumber-csharp-bot` + account, not the ambient `GITHUB_TOKEN`) and auto-merges it, instead of pushing straight to `main`. + (#431, #434) `finalize_metadata_release.yml` then tags the merge commit and dispatches the NuGet + publish. This CHANGELOG's own entries are now written into that same commit by the sync script. +- Migrated the solution file to the `.slnx` format. (#388) +- Upgraded bUnit to 2.x, clearing a transitive `AngleSharp` advisory (GHSA-pgww-w46g-26qg). (#398) +- Moved the locale country-names table out of generated C# and into a binary resource per country, + read on demand by `LocaleNames` (mirrors the phone/short-number/geocoding "binary metadata" move + from v9.0.29–v9.0.32, below); `LocaleData` still exposes the whole table for callers outside the + library. (#395) +- Dropped the restore lock files and locked-mode CI restores added the previous release (v9.0.37) — + they didn't earn their complexity; `NuGetAudit` and pinned-exact-versions-in-`Directory.Packages.props` + do the job restore locking was meant for. (#396) Package-validation baselines for both packable + projects now compare against the 9.0.37 release. (#394) +- A large batch of purely internal idiomatic-C# cleanups (`is`-null pattern matching, `TryGetValue`, + LINQ `Select`/`Where`, `??=`, sealing singleton-style types, `BinaryPrimitives` in `ByteBuffer`) and + CodeQL-driven fixes (redundant `ToString()`/`GetHashCode()` calls, `GetType()` comparisons, + invariant-culture formatting) — no behavior change. (#409, #412–#421, #424 and others) +- CodeQL now runs on manual dispatch as well as its schedule, and excludes generated `obj/` output. + (#407, #423) + +### Fixed +- Security: a path-traversal issue in `BuildLocaleNames` and an uncaught parse exception in `PhoneNumberTypeConverter`. (#428) -- Fixed: `NumberParseException` (rather than `ArgumentOutOfRangeException`) on malformed RFC3966 - `phone-context` ordering (#406); three `PhoneNumberMatcher`/`AsYouTypeFormatter` parity gaps - against the upstream Java source (#408); unbounded growth in `AsYouTypeFormatter` and missing - null-guards in `PhoneNumberUtil` (#402). -- Changed: metadata-sync automation now opens a PR (authenticated as the `libphonenumber-csharp-bot` - account) and auto-merges it, instead of pushing straight to `main` — see the "Metadata updates" - design note this same change added to CHANGELOG.md automation, below. Also: added a fuzzing target - for the parsing surface (FsCheck properties + SharpFuzz, #392); migrated the solution to `.slnx` - and picked up a transitive `AngleSharp` advisory fix (#388); added build-provenance attestation for - published packages (#400). -- A large batch of purely internal idiomatic-C# cleanups and documentation refreshes with no - behavior change is omitted here — see the release notes for the full PR list. -- Metadata update to upstream [libphonenumber v9.0.38](https://github.com/google/libphonenumber/releases/tag/v9.0.38). - -## [v9.0.32 – v9.0.37](https://github.com/twcclegg/libphonenumber-csharp/compare/v9.0.31...v9.0.37) - 2026-06-05 – 2026-08-14 - -6 metadata-only releases tracking upstream libphonenumber. No source changes. +- `NumberParseException` (rather than `ArgumentOutOfRangeException`) on malformed RFC3966 + `phone-context` ordering. (#406) +- Several `PhoneNumberMatcher`/`AsYouTypeFormatter` parity gaps against the upstream Java source, + including `formattingRuleHasFirstGroupOnly` and `containsMoreThanOneSlashInNationalNumber`. (#408) +- Unbounded growth in `AsYouTypeFormatter` and missing null-guards in `PhoneNumberUtil`. (#402) +- `FormatOutOfCountryKeepingAlphaChars` no longer throws when region metadata is missing. + +### Docs +- Reconciled stale `Todo` sections and refreshed documentation for the binary-metadata, build, and CI + changes made across v9.0.29–v9.0.38. + +## [v9.0.37](https://github.com/twcclegg/libphonenumber-csharp/compare/v9.0.36...v9.0.37) - 2026-08-14 + +A dedicated performance-optimization release, alongside build-reproducibility and package-validation +work and new benchmark coverage — not a metadata-only release, despite the version bump looking +routine. + +### Performance +- A long series of allocation- and regex-reduction changes on the parsing/formatting hot path: + matching against slices instead of materializing them, skipping regex match groups when stripping a + national prefix, capturing every extension alternative into one named group, truncating the lookup + prefix by integer division instead of a string round-trip, reading the flyweight prefix-map buffer + from an array instead of a stream, and not rendering the country-calling-code test to a string. +- Froze the country-calling-code-to-region-code map and the short-number/timezone lookup tables into + frozen collections; sized the `Normalize` stack buffer to the input instead of a fixed allocation. + +### Added +- Benchmarks for the offline geocoder (previously untested for performance); new benchmarks + measuring the parse, validate, and format phases separately and covering cold-start and + representative real-world input shapes; a robustness benchmark/test checking the public API + survives hostile input. +- CI now benchmarks the base commit on the same runner as the PR branch before posting a comparison, + and only reports statistically significant changes (finished the following release, v9.0.38's + `#425`). + +### Changed +- Enabled `EnablePackageValidation` on both packable projects (`PhoneNumbers`, `PhoneNumbers.Extensions`). +- Restore lock files were committed and checked in CI for reproducible restores (reverted again the + next release, v9.0.38, above, once it was clear the complexity wasn't paying for itself). +- Centralized build settings into `Directory.Build.props` and package versions into + `Directory.Packages.props` (Central Package Management) — the structure CLAUDE.md now documents. +- Pinned the SDK major version and added a build-reproducibility check; cached NuGet packages in CI. +- Dropped the out-of-support `net9.0` target framework and the `netframework4.8` test target; guarded + nullable annotations by TFM name rather than by .NET version. +- `NuGetAudit` findings no longer fail the build outright. + +### Fixed +- The offline geocoder threw instead of returning nothing for a region with no localized name. +- A race between parallel inner builds cleaning the generated binary-metadata output. +- `GetSupportedRegions` returned a mutable reference instead of a copy; region sets are now frozen. +- Oversized input could overflow the stack buffer in `Normalize`. +- The release tag is now pinned to the commit just pushed, fixing a release-tag-determinism bug. +- Exceptions thrown from `PhoneNumberUtil` now format with invariant culture and name their arguments. + +## [v9.0.36](https://github.com/twcclegg/libphonenumber-csharp/compare/v9.0.35...v9.0.36) - 2026-08-03 + +### Changed +- Removed AppVeyor entirely — CI is GitHub Actions only from here on (matches CLAUDE.md's "There are + no Windows runners"). +- Compatible workflows now run on ARM64 runners for faster builds. +- `lib/github-actions-metadata-update.sh` gained a `--dry-run` option, opt-in overrides for the + java/proto-file check, hardening for bash 3.2, scoped git identity, and no longer hard-codes the + target repository. +- The GitHub Release now links back to the published NuGet packages and the upstream libphonenumber + release it tracks; `publish_nuget.yml` is dispatched only after the release tag is created; CI now + verifies release packaging directly. +- Ported the corresponding upstream Java behavior changes for v9.0.36. + +## [v9.0.35](https://github.com/twcclegg/libphonenumber-csharp/compare/v9.0.34...v9.0.35) - 2026-07-17 + +Metadata release; the metadata-update script's temporary java-file-check override from the previous +release was restored to its normal (enforcing) state. + +## [v9.0.34](https://github.com/twcclegg/libphonenumber-csharp/compare/v9.0.33...v9.0.34) - 2026-07-03 + +Minor CI/tooling release: rotated the NuGet API key, moved AppVeyor to VS2026 with .NET 10, and +temporarily relaxed the metadata script's java-file check to let this sync through (restored the +next release, above). + +## [v9.0.33](https://github.com/twcclegg/libphonenumber-csharp/compare/v9.0.32...v9.0.33) - 2026-06-22 + +Security- and process-hardening release, alongside two metadata-parity fixes — one of the releases the +previous CHANGELOG draft incorrectly called metadata-only. + +### Added +- `SECURITY.md`, `CODE_OF_CONDUCT.md`, and `CODEOWNERS`. +- [OpenSSF Scorecard](https://securityscorecards.dev/) workflow (`scorecard.yml`), running on `main` + and on branch-protection changes. +- Deterministic-build verification. + +### Fixed +- Hardened the custom-metadata XML parser (`BuildMetadataFromXml`, used by the legacy + `PhoneNumberUtil(Stream)` constructor) against XXE (XML External Entity) attacks. +- Hardened GitHub Actions workflows following security best practices (least-privilege permissions, + pinned actions) and refreshed several READMEs to match. +- Ported an upstream v9.0.33 E.164 short-code fix and a previously-missed Mexican (MX) area-code + change from v9.0.30 (above). + +## [v9.0.32](https://github.com/twcclegg/libphonenumber-csharp/compare/v9.0.31...v9.0.32) - 2026-06-05 + +Another release the previous CHANGELOG draft called metadata-only that actually shipped a new target +framework, AOT compatibility, and a redesigned demo site. + +### Added +- `net10.0` target framework. (#328) +- Cold-start benchmarks, in addition to the existing steady-state ones. + +### Changed +- Made the library AOT-compatible (`IsAotCompatible`, trim/AOT analyzer warnings now build errors on + modern TFMs) — the Blazor WASM demo depends on this too. +- Beautified the Blazor demo website's UI. (#348) +- Reduced the performance-test job's scope for CI speed; restored `System.IO.Compression` after an + earlier removal attempt (v9.0.31, below) needed it back. ## [v9.0.31](https://github.com/twcclegg/libphonenumber-csharp/compare/v9.0.30...v9.0.31) - 2026-05-23 -- Added: cross-platform embedded debug symbols for both packages. (#340) -- Metadata update to upstream v9.0.31. +### Added +- A Blazor WebAssembly demo site (`PhoneNumbers.Demo`), deployed to GitHub Pages by + `deploy-demo.yml` — doubles as proof the library works trimmed under WASM. (#342, #343) +- Cross-platform embedded debug symbols for both packages. (#340) +- Test coverage for `DataModelsAndExceptions` and `BuildPrefixMapFromBin`. (#337) +- CodeQL now also runs on pull requests, not just on a schedule. (#332) + +### Changed +- Refactored the offline geocoder to use the binary prefix map the same way the Java version does. + (#334) +- Removed the now-unneeded `System.IO.Compression` package reference. (#339) +- Several fixes to how the performance-test PR comment reports its results. (#335, #336, #338) +- Dependabot now also tracks the new demo projects' dependencies. (#344) ## [v9.0.30](https://github.com/twcclegg/libphonenumber-csharp/compare/v9.0.29...v9.0.30) - 2026-05-07 -- Added: `net10.0` target framework. -- Changed: geocoding and time-zone prefix maps are now generated to per-region binary files at build - time (`PhoneNumbers.MetadataBuilder`) and loaded from embedded resources at runtime, replacing the - bundled `geocoding.zip` and runtime text parsing. This completes the same "binary metadata" move - already made for the phone/short-number/alternate-format XML. -- Metadata update to upstream v9.0.30. +The architectural turning point behind most of CLAUDE.md's "Singleton + metadata loading" notes: phone +metadata moved from parsed XML to binary files generated at build time and embedded in the assembly. +The previous CHANGELOG draft attributed this release's work to a vague "binary metadata" one-liner and +misplaced `net10.0` here — it actually landed the release after (v9.0.32, above). -## [v9.0.2 – v9.0.29](https://github.com/twcclegg/libphonenumber-csharp/compare/v9.0.1...v9.0.29) - 2025-03-28 – 2026-04-25 +### Added +- `IMetadataLoader` + a lazy `MetadataSource`, with `EmbeddedResourceMetadataLoader` as the default + implementation reading per-region binary files generated at build time by the new + `PhoneNumbers.MetadataBuilder` project. +- `PhoneNumberToCarrierMapper`, matching a feature already present in the Java library. (#317) +- Much larger performance-test datasets (100,000 entries instead of the previous size). (#319) -28 releases, almost entirely metadata syncs and routine CI/dependency maintenance (Dependabot -bumps, `net9.0` support added and later dropped again as it went out of support). One test-only fix -kept CI green after a `System.Collections.Immutable` behavior change (#297). No user-facing source -changes. +### Changed +- **Metadata pipeline rewrite**: `BuildMetadataFromXml` and the geocoding/timezone text metadata are + now converted to per-region binary files at build time (`BuildBinaryMetadata`, `BuildGeocodingBins`, + `BuildTimezoneBin` MSBuild targets) and read from embedded, gzip-compressed resources at runtime — + XML is no longer parsed on the default load path. `PhoneNumberUtil`/`MetadataManager` switched to + the new lazy `MetadataSource`; the old `RegexCache`-based approach was removed where superseded. + Made the multi-TFM build of the binary metadata race-free. +- Dropped `intlFormat` instead of emitting a literal `"NA"` when the upstream XML value is `NA`. (#97) + +### Performance +- Enabled compiled regex for dynamic metadata patterns; cached `GetOrAdd` factory delegates in the + metadata and regex caches; shared the national-significant-number string between region lookup and + validation instead of recomputing it; skipped a redundant `StringBuilder` copy in + `MaybeExtractCountryCode`. + +### Fixed +- Ported several additional upstream Java behavior fixes as part of the metadata-pipeline work. + +## [v9.0.10 – v9.0.29](https://github.com/twcclegg/libphonenumber-csharp/compare/v9.0.9...v9.0.29) - 2025-07-18 – 2026-04-25 + +19 releases. Upstream metadata syncs, several bundled only a mechanical `LocaleData.cs` regeneration, +plus routine Dependabot CI bumps (`codecov-action`, `actions/checkout`, `actions/setup-dotnet`, +`github/codeql-action`) and NuGet test-project dependency groups. No changes to the library's own +source. + +## [v9.0.9](https://github.com/twcclegg/libphonenumber-csharp/compare/v9.0.8...v9.0.9) - 2025-07-08 + +- Fixed a test failure caused by a `System.Collections.Immutable` behavior change by upgrading the + package. (#297) + +## [v9.0.2 – v9.0.8](https://github.com/twcclegg/libphonenumber-csharp/compare/v9.0.1...v9.0.8) - 2025-03-28 – 2025-06-25 + +7 releases, metadata syncs plus a `codecov-action` version bump and mechanical `LocaleData.cs` +regenerations. No source changes. ## [v9.0.1](https://github.com/twcclegg/libphonenumber-csharp/compare/v9.0.0...v9.0.1) - 2025-03-23 @@ -91,52 +274,137 @@ changes. ## [v8.13.51 – v8.13.55](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.50...v8.13.55) - 2024-12-02 – 2025-02-14 -5 metadata-only releases tracking upstream libphonenumber. No source changes. +5 releases, mostly metadata syncs and routine Dependabot/`codecov-action` bumps. CodeQL's build step +picked up .NET 8. (#274) One doc fix: the bug-report issue template was updated to ask reporters for +more detail. (#278) ## [v8.13.50](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.49...v8.13.50) - 2024-11-16 -- Added: `net9.0` target framework (dropped again once out of support — see the v9.0.2–v9.0.29 entry - above). (#273) +- Added: `net9.0` target framework (dropped again once out of support — see v9.0.36–v9.0.37 above). + (#273) + +## [v8.13.49](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.48...v8.13.49) - 2024-11-04 -## [v8.13.45 – v8.13.49](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.44...v8.13.49) - 2024-09-06 – 2024-11-04 +Metadata-only release. No source changes. -5 metadata-only releases tracking upstream libphonenumber. No source changes. +## [v8.13.48](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.47...v8.13.48) - 2024-10-23 + +- Updated the JSON library dependency used by the `libphonenumber-csharp.extensions` package. + +## [v8.13.45 – v8.13.47](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.44...v8.13.47) - 2024-09-06 – 2024-10-04 + +3 releases, metadata syncs plus routine Dependabot test-dependency bumps (`Microsoft.NET.Test.Sdk`) +and one mechanical `LocaleData.cs` regeneration. No source changes. ## [v8.13.44](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.43...v8.13.44) - 2024-08-26 - Added: `net8.0` target framework; dropped `net7.0`. (#264) -## [v8.13.41 – v8.13.43](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.40...v8.13.43) - 2024-07-25 – 2024-08-09 +## [v8.13.42 – v8.13.43](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.41...v8.13.43) - 2024-07-26 – 2024-08-09 -3 metadata-only releases tracking upstream libphonenumber. No source changes. +2 releases, metadata syncs plus routine Dependabot test-dependency bumps. No source changes. + +## [v8.13.41](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.40...v8.13.41) - 2024-07-25 + +- Bumped `System.Text.Json` in `libphonenumber-csharp.extensions`. (#258) ## [v8.13.40](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.39...v8.13.40) - 2024-07-03 - Fixed: area-code information was lost for all Mexican (MX) numbers. -## [v8.13.28 – v8.13.39](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.27...v8.13.39) - 2024-01-25 – 2024-06-15 +## [v8.13.38 – v8.13.39](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.37...v8.13.39) - 2024-06-05 – 2024-06-15 + +2 releases, metadata syncs plus routine Dependabot/`codecov-action` bumps. No source changes. + +## [v8.13.37](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.36...v8.13.37) - 2024-05-17 + +- Code coverage now runs and uploads on every PR, not only on `main`; removed the now-redundant + standalone coverage workflow. (#250) + +## [v8.13.36](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.35...v8.13.36) - 2024-05-03 + +- Added the GitHub Action that uploads code-coverage results. (#245) Fixed coverage not being + generated for the `PhoneNumbers.Test` project. (#246) Moved the PR template since it wasn't being + applied by default. (#247) + +## [v8.13.35](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.34...v8.13.35) - 2024-04-19 -12 releases, mostly metadata syncs. Code coverage now runs and uploads on every PR instead of only -on `main` (#245, #250). No other source changes. +Metadata release; only a mechanical `LocaleData.cs` regeneration alongside it. + +## [v8.13.34](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.33...v8.13.34) - 2024-04-05 + +A foundational release for this repo's current tooling — this is where nullable reference types, +the performance-test harness, and CodeQL scanning all began. + +### Added +- `PhoneNumbers.PerformanceTest`, the BenchmarkDotNet harness CLAUDE.md still describes today. (#240) +- CodeQL security scanning (`codeql.yml`). (#241) + +### Changed +- Enabled nullable reference types across the main `PhoneNumbers` project (later extended, per + CLAUDE.md, to every target except `netstandard2.0`). +- Updated the test projects to target .NET 8. +- Expanded documentation with concrete usage examples. (#238) + +## [v8.13.32 – v8.13.33](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.31...v8.13.33) - 2024-03-12 – 2024-03-25 + +2 releases, metadata sync plus a routine Dependabot test-dependency bump. No source changes. + +## [v8.13.31](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.30...v8.13.31) - 2024-02-25 + +- Fixed: `NormalizeDiallableCharsOnly` incorrectly removed the `#` symbol. (#230) +- Enabled nullable reference types for the `PhoneNumbers.Test` project; added more test coverage + derived from the main Google libphonenumber repository. (#224) + +## [v8.13.30](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.29...v8.13.30) - 2024-02-09 + +- Added GitHub Actions PR builds that build the project and run tests on Linux and Windows. (#204) + +## [v8.13.29](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.28...v8.13.29) - 2024-01-26 + +Metadata-only release. No source changes. + +## [v8.13.28](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.27...v8.13.28) - 2024-01-25 + +- Added an `.editorconfig` derived from Microsoft's defaults. (#222) +- Added more test coverage (`NumberFormat`, `ToString`, and other previously-uncovered methods). + (#219) ## [v8.13.27](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.26...v8.13.27) - 2023-12-21 - Fixed: a null-reference exception during number normalization when given a null input. (#207) +- Updated number-splitting logic to match an upstream fix + ([google/libphonenumber#3305](https://github.com/google/libphonenumber/pull/3305)). ## [v8.13.26](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.25...v8.13.26) - 2023-11-29 -- Changed: metadata updates are now proposed and published automatically instead of by hand — the - first `feat: automatic upgrade to vX.Y.Z` release, and the origin of most of the entries in this - file. (#192) -- Added: Dependabot configuration for NuGet package updates. (#185) +- Changed: metadata updates are now proposed and synced automatically by a new GitHub Actions script, + instead of by hand — the first `feat: automatic upgrade to vX.Y.Z` release, and the origin of most + of the release entries in this file from here on. (#192) ## [v8.13.25](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.24...v8.13.25) - 2023-11-20 - Fixed: a leading `+` was incorrectly stripped/ignored when parsing. (#184) +- Added Dependabot configuration for NuGet package updates. (#185) +- A `net8.0` target-framework attempt was added and reverted the same release (real `net8.0` support + landed later, in v8.13.44 above, #264). + +## [v8.13.24](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.23...v8.13.24) - 2023-10-31 + +- Moved release/update scripts into a new `lib/` directory — the layout CLAUDE.md still documents, + and the home the metadata-automation script (v8.13.26, above) landed in shortly after. + +## [v8.13.22 – v8.13.23](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.21...v8.13.23) - 2023-09-29 – 2023-10-17 -## [v8.13.19 – v8.13.24](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.18...v8.13.24) - 2023-08-22 – 2023-10-31 +2 releases, metadata sync plus a CI tweak. No source changes. -6 metadata-only releases tracking upstream libphonenumber. No source changes. +## [v8.13.20 – v8.13.21](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.19...v8.13.21) - 2023-09-07 – 2023-09-20 + +2 metadata-only releases. No source changes. + +## [v8.13.19](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.18...v8.13.19) - 2023-08-22 + +Metadata release with a minor accompanying test update. ## [v8.13.18](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.17...v8.13.18) - 2023-08-03 @@ -145,98 +413,406 @@ on `main` (#245, #250). No other source changes. ## [v8.13.17](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.16...v8.13.17) - 2023-07-26 -- Changed: reduced unnecessary regex construction and usage on hot paths. (#178) +- Changed: reduced unnecessary regex construction and usage on hot paths. (#178) Cleaned up package + dependencies. (#176) + +## [v8.13.16](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.15...v8.13.16) - 2023-07-11 + +Metadata-only release. No source changes. -## [v8.13.12 – v8.13.16](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.11...v8.13.16) - 2023-06-15 – 2023-07-11 +## [v8.13.15](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.14...v8.13.15) - 2023-07-11 -5 metadata-only releases tracking upstream libphonenumber. No source changes. +Metadata release; a test method was extracted for reuse and the NuGet API key was rotated. + +## [v8.13.13 – v8.13.14](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.12...v8.13.14) - 2023-06-15 + +2 metadata-only releases (same day). No source changes. + +## [v8.13.12](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.11...v8.13.12) - 2023-06-15 + +Metadata release; documented the metadata-update process in more detail and refreshed the packed +README. ## [v8.13.11](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.10...v8.13.11) - 2023-04-27 -Metadata-only release tracking upstream libphonenumber. No source changes. +Metadata-only release. No source changes. ## [v8.13.10](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.9...v8.13.10) - 2023-04-20 - Added: `net6.0` target framework. - Changed: `Format()` uses stack-allocated `Span` instead of heap allocations on the - formatting hot path. (#166) + formatting hot path; several other methods optimized. (#166) ## [v8.13.9](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.8...v8.13.9) - 2023-04-10 -Metadata-only release tracking upstream libphonenumber. No source changes. +Metadata-only release. No source changes. ## [v8.13.8](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.7...v8.13.8) - 2023-03-27 - Added (Extensions): a `System.Text.Json` converter for `PhoneNumber`. +- Changed: reworked how geocoding data is packaged — zipped for `net46`+ targets, embedded raw where + no zip exists, and built with `Microsoft.Bcl.Compression` for portable-library builds; removed the + old `net35`/`net40`/portable framework zips. (#147) ## [v8.13.3 – v8.13.7](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.2...v8.13.7) - 2022-12-22 – 2023-03-03 -5 metadata-only releases tracking upstream libphonenumber. No source changes. +5 releases, mostly metadata syncs plus release-script, CI, and test-tooling housekeeping (a +`System.Collections.Immutable` bump included). No functional library changes. ## [v8.13.2](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.1...v8.13.2) - 2022-12-08 -- Added: `PhoneNumberToTimeZonesMapper`, mapping a phone number to its candidate IANA time zones. - (#163) +- Added: the `libphonenumber-csharp.extensions` NuGet package, for C#-idiomatic helpers with no + Java-upstream equivalent. (#164) +- Added: `PhoneNumberToTimeZonesMapper`, mapping a phone number to its candidate IANA time zones, + full-conformance ported from Google's Java implementation. (#163) +- Added: `net7.0` target framework. +- Changed: `RegexCache` is no longer LRU-evicted. ## [v8.13.1](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.13.0...v8.13.1) - 2022-11-28 -- Added: the `libphonenumber-csharp.extensions` NuGet package, for C#-idiomatic helpers that have no - Java-upstream equivalent. (#164) +### Performance +- `RegexCache` lookups sped up by roughly 10x. (#161) -## [v8.12.44 – v8.13.0](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.43...v8.13.0) - 2022-02-24 – 2022-11-08 +### Changed +- Added Visual Studio 2022 support to CI. +- Removed the unused protobuf code-generation tooling (see v8.13.0, below). +- The NuGet package icon is now referenced via `PackageIcon` from the packed package instead of a URL. -16 metadata-only releases tracking upstream libphonenumber. No source changes. +## [v8.13.0](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.57...v8.13.0) - 2022-11-08 -## [v8.12.39 – v8.12.43](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.38...v8.12.43) - 2022-02-14 +- The old protobuf-based metadata code-generation path was dropped as no longer compatible with the + toolchain — metadata has been generated from XML only since (see csharp/README.md's "Known Issues" + for the XML-vs-protobuf divergence this leaves). -- Removed: the `net35` target framework and other end-of-life targets. (#146) -- 5 metadata-only patch releases shipped the same day as this change. +## [v8.12.55 – v8.12.57](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.54...v8.12.57) - 2022-09-09 – 2022-10-14 -## [v8.12.35 – v8.12.38](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.34...v8.12.38) - 2021-12-15 +3 releases, metadata syncs plus a release-script fix. No source changes. + +## [v8.12.54](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.53...v8.12.54) - 2022-08-22 + +Metadata release; removed an unused locale-data-dump helper class and cleaned up its supporting +tooling. + +## [v8.12.53](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.52...v8.12.53) - 2022-08-08 + +Metadata release; updated the `LocaleData` generator and its output rendering. + +## [v8.12.51.1 – v8.12.52](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.51...v8.12.52) - 2022-07-12 – 2022-07-19 + +2 metadata-only releases. No source changes. + +## [v8.12.51](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.50...v8.12.51) - 2022-07-07 + +Metadata release; rotated the NuGet API key and fixed file-name casing in a handful of test/source +files alongside a large upstream metadata sync. + +## [v8.12.48 – v8.12.50](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.47...v8.12.50) - 2022-05-18 – 2022-06-13 + +3 metadata-only releases. No source changes. + +## [v8.12.47](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.46...v8.12.47) - 2022-05-18 + +- Added GitHub issue templates. (#152) + +## [v8.12.44 – v8.12.46](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.43...v8.12.46) - 2022-03-14 – 2022-04-01 + +3 metadata-only releases. No source changes. + +## [v8.12.40 – v8.12.43](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.39...v8.12.43) - 2022-02-14 – 2022-02-24 +4 metadata-only patch releases shipped in quick succession right after v8.12.39, below. No source +changes. + +## [v8.12.39](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.38...v8.12.39) - 2022-02-14 + +- Removed: the `net35` target framework and other end-of-life targets. (#146) - Added: a way to refresh a `PhoneNumberUtil` instance's metadata from a stream at runtime, without restarting the process. (#142) -- 4 metadata-only patch releases shipped the same day as this change. +- The default branch was renamed to `main`. + +## [v8.12.35 – v8.12.38](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.34...v8.12.38) - 2021-12-15 + +4 metadata-only releases (same day) plus one test-only PascalCase rename fix. No functional changes. + +## [v8.12.30 – v8.12.34](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.29...v8.12.34) - 2021-08-18 – 2021-10-07 + +5 metadata-only releases. No source changes. + +## [v8.12.29](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.28...v8.12.29) - 2021-08-18 + +- Fixed a build-breaking syntax error in `AsYouTypeFormatter` introduced by the previous release. + +## [v8.12.28](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.27...v8.12.28) - 2021-07-22 + +Metadata-only release. No source changes. + +## [v8.12.27](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.26...v8.12.27) - 2021-07-08 + +- Enforced the maximum national-significant-number length during parsing. + +## [v8.12.26](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.25...v8.12.26) - 2021-06-25 + +Metadata-only release. No source changes. + +## [v8.12.25](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.24...v8.12.25) - 2021-06-16 + +- Fixed: always use the preferred international prefix when present, instead of falling back to the + default. +- Test projects moved to target `net5.0`. + +## [v8.12.22 – v8.12.24](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.21...v8.12.24) - 2021-04-30 – 2021-05-31 + +3 metadata-only releases (v8.12.24 bundled a one-line test-project touch alongside the metadata +sync). No functional changes. + +## [v8.12.16 – v8.12.21](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.15...v8.12.21) - 2021-01-15 – 2021-04-08 -## [v8.11.4 – v8.12.34](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.11.3...v8.12.34) - 2020-02-13 – 2021-10-07 +5 metadata-only releases. No source changes. -35 metadata-only releases tracking upstream libphonenumber. No source changes. +## [v8.12.15](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.14...v8.12.15) - 2020-12-18 + +- Reverted the `System.Collections.Immutable` version bump from the previous release after it caused + problems. + +## [v8.12.14](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.13...v8.12.14) - 2020-12-04 + +- Updated the `System.Collections.Immutable` dependency. (#125) (Reverted the next release, above.) + +## [v8.12.11 – v8.12.13](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.10...v8.12.13) - 2020-10-09 – 2020-11-17 + +2 metadata-only releases. No source changes. + +## [v8.12.10](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.9...v8.12.10) - 2020-09-29 + +### Performance +- Optimized library startup time. (#121) + +## [v8.12.9](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.8...v8.12.9) - 2020-09-02 + +Metadata-only release. No source changes. + +## [v8.12.8](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.7...v8.12.8) - 2020-08-17 + +Metadata release; reverted a NuGet package-icon change back to referencing it via URL. + +## [v8.12.7](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.6...v8.12.7) - 2020-07-21 + +- Rotated the NuGet API key and cleaned up packaging/`xunit` build warnings. + +## [v8.12.5 – v8.12.6](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.4...v8.12.6) - 2020-06-04 – 2020-07-06 + +2 metadata-only releases. No source changes. + +## [v8.12.4](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.3...v8.12.4) - 2020-05-21 + +- Added a missing semicolon to `singleExtensionSymbolParsing`. (#117) + +## [v8.12.2 – v8.12.3](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.1...v8.12.3) - 2020-04-23 – 2020-05-08 + +2 metadata-only releases. No source changes. + +## [v8.12.0 – v8.12.1](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.11.5...v8.12.1) - 2020-03-20 – 2020-03-31 + +2 releases, each bundling only a one-line incidental touch (a generated region-code constant, a +version bump) alongside the metadata sync. No functional changes. + +## [v8.11.4 – v8.11.5](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.11.3...v8.11.5) - 2020-02-13 – 2020-03-03 + +2 metadata-only releases. No source changes. ## [v8.11.3](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.11.2...v8.11.3) - 2020-02-03 - Fixed: numbers with multiple leading zeroes were parsed incorrectly. (#111) -## [v8.10.1 – v8.11.2](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.10.0...v8.11.2) - 2018-11-28 – 2020-01-15 +## [v8.11.0 – v8.11.2](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.10.23...v8.11.2) - 2019-11-29 – 2020-01-15 + +3 metadata-only releases. No source changes. + +## [v8.10.23](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.10.22...v8.10.23) - 2019-11-18 + +Metadata release; README updates. + +## [v8.10.22](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.10.21...v8.10.22) - 2019-10-31 + +Metadata release; updated dependent libraries. + +## [v8.10.21](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.10.20...v8.10.21) - 2019-10-16 + +- Removed the vendored copies of the upstream Java/C++/Debian-packaging source trees that had been + synced into the repository the previous release (below) — this port only carries the C# code from + here on. + +## [v8.10.20](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.10.19...v8.10.20) - 2019-10-03 + +- The full upstream multi-language source tree (`cpp/`, `java/`, `debian/` packaging) was synced into + the repository alongside the metadata — removed again the very next release, above. +- Fixed two French short-number classifications (`STANDARD_RATE`, toll-free) to match + `resources/ShortNumberMetadata.xml`. (#108) + +## [v8.10.19](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.10.18...v8.10.19) - 2019-09-21 + +Metadata-only release. No source changes. + +## [v8.10.18](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.10.17...v8.10.18) - 2019-09-16 + +Metadata release with a small accompanying `PhoneNumberUtil`/README touch-up. + +## [v8.10.17](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.10.16...v8.10.17) - 2019-08-16 + +Metadata release with a small accompanying `AsYouTypeFormatter`/`PhoneNumberUtil` fix. + +## [v8.10.15 – v8.10.16](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.10.14...v8.10.16) - 2019-07-18 – 2019-08-05 + +2 metadata-only releases. No source changes. + +## [v8.10.14](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.10.13...v8.10.14) - 2019-07-01 + +- Internal code-quality pass: added thread-safety locks and `IDisposable` where needed, switched + several collections to immutable equivalents, fixed a SonarQube (S1848) finding, and added more XML + doc comments. + +## [v8.10.13](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.10.12...v8.10.13) - 2019-05-31 + +Metadata-only release. No source changes. + +## [v8.10.12](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.10.11...v8.10.12) - 2019-05-15 -26 metadata-only releases tracking upstream libphonenumber. No source changes. +- Converted JavaDoc-style comments to C# XML doc comments for `PhoneNumberUtil`, `AreaCodeMap`, + `AreaCodeMapStorageStrategy`, `AreaCodeParser`, `AsYouTypeFormatter`, and `IMatcherApi`, and enabled + `GenerateDocumentationFile` so they surface in IntelliSense. (#102, #103) + +## [v8.10.11](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.10.10...v8.10.11) - 2019-05-07 + +Metadata-only release. No source changes. + +## [v8.10.10](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.10.9...v8.10.10) - 2019-04-17 + +Metadata release with a ReSharper (`.DotSettings`) configuration touch-up. + +## [v8.10.9](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.10.8...v8.10.9) - 2019-04-05 + +Metadata-only release. No source changes. + +## [v8.10.8](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.10.7...v8.10.8) - 2019-03-23 + +Metadata release with a small accompanying `LeniencyExtensions`/`PhoneNumberMatcher`/`PhoneNumberUtil` +fix. + +## [v8.10.5 – v8.10.7](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.10.4...v8.10.7) - 2019-02-08 – 2019-03-11 + +3 metadata-only releases. No source changes. + +## [v8.10.4](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.10.3...v8.10.4) - 2019-01-24 + +Metadata-only release. No source changes. + +## [v8.10.3](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.10.2...v8.10.3) - 2019-01-11 + +- Cleaned up documentation for `GetLengthOfNationalDestinationCode` and removed unneeded + `AsYouTypeFormatter` code; dropped a bad transitive dependency from the Java demo project (unrelated + to the C# port). + +## [v8.10.2](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.10.1...v8.10.2) - 2018-12-06 + +- Added the NuGet package icon. + +## [v8.10.1](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.10.0...v8.10.1) - 2018-11-28 + +Metadata-only release. No source changes. ## [v8.10.0](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.9.16...v8.10.0) - 2018-11-28 -- Upstream libphonenumber 8.10.0 changed how `AsYouTypeFormatter` chooses between international and - national-dialing rules, correctly formatting some numbers that use a national prefix but aren't - internationally diallable. (#89) +- Fixed `carrierCodeFormattingRule` handling and updated `AsYouTypeFormatter` behavior. (#89) +- Fixed a `net35` performance issue: check whether a dictionary key already exists before adding, to + avoid a thrown-and-caught `ArgumentException`. (#88) +- Reworked how the per-region embedded XML resources (added in v8.9.16, below) are deduplicated and + read. (#82) + +## [v8.9.16](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.9.15...v8.9.16) - 2018-10-19 + +- Use embedded XML resources for `.NET 3.5` instead of the standard resource pipeline. (#80) + +## [v8.9.15](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.9.14...v8.9.15) - 2018-10-06 + +- Added a code example to the README. (#76) +- Changed: `Leniency.Verify` moved to an extension method (`LeniencyExtensions`) to avoid a breaking + API change. (#75) -## [v8.9.9 – v8.9.16](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.9.8...v8.9.16) - 2018-06-29 – 2018-10-19 +## [v8.9.14](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.9.13...v8.9.14) - 2018-09-20 -8 metadata-only releases tracking upstream libphonenumber. No source changes. +Metadata-only release. No source changes. + +## [v8.9.13](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.9.12...v8.9.13) - 2018-09-07 + +- Cleaned up Russian-character handling in `PhoneNumberUtil`. + +## [v8.9.11 – v8.9.12](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.9.10...v8.9.12) - 2018-08-18 – 2018-08-24 + +2 releases; v8.9.12 bundled a small incidental `PhoneNumberUtil` tweak with the metadata sync (no PR +or commit message recorded to describe it further). No new features. + +## [v8.9.9 – v8.9.10](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.9.8...v8.9.10) - 2018-06-29 – 2018-07-12 + +2 releases, metadata syncs. No source changes. ## [v8.9.8](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.9.7...v8.9.8) - 2018-06-15 - Added: `ShortNumberUtil`, providing information about short codes — the predecessor of today's `ShortNumberInfo`. (#64) +- Added tests and fixes for `MetadataFilter`. (#65) +- Excluded autogenerated files from the code-coverage report. (#66) -## [v8.9.4.1 – v8.9.7](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.9.3...v8.9.7) - 2018-04-17 – 2018-05-30 +## [v8.9.7](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.9.6.3...v8.9.7) - 2018-05-30 -4 metadata-only releases tracking upstream libphonenumber. No source changes. +- CI/packaging tweaks (AppVeyor, PowerShell build scripts, portable-library packaging); added + `InternalRegexOptions`. + +## [v8.9.4.1 – v8.9.6.3](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.9.3...v8.9.6.3) - 2018-04-17 – 2018-05-16 + +3 metadata-only releases. No source changes. ## [v8.9.3](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.9.2...v8.9.3) - 2018-04-05 - Added: a `net35` target framework, using embedded XML resources instead of the standard resource - pipeline. (#58) (Dropped again in v8.12.39–v8.12.43, 2022-02-14, above.) + pipeline. (#58) (Dropped again in v8.12.39, 2022-02-14, above.) + +## [v8.8.10 – v8.9.2](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.8.9...v8.9.2) - 2018-01-25 – 2018-03-19 + +6 releases, mostly metadata syncs. Added `CONTRIBUTING.md`; de-duplicated the test metadata XML. No +functional changes. + +## [v8.8.7 – v8.8.9](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.8.6...v8.8.9) - 2017-12-01 – 2018-01-10 + +4 metadata-only releases. No source changes. + +## [v8.8.6](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.8.5...v8.8.6) - 2017-11-17 + +- Changed: broad cleanup and simplification across the metadata/formatting classes (extreme + bracket-reduction logic removed, various simplifications). (#47) + +## [v8.8.5](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.8.4...v8.8.5) - 2017-10-31 + +Metadata-only release. No source changes. + +## [v8.8.4](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.8.3...v8.8.4) - 2017-10-17 + +- Fixed: handling of numbers with a leading zero. (#44) + +## [v8.8.2 – v8.8.3](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.8.1...v8.8.3) - 2017-09-22 – 2017-10-05 + +2 releases, packaging/version housekeeping only. No functional changes. + +## [v8.8.1](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.8.0...v8.8.1) - 2017-09-08 + +- Added: `netstandard2.0` target framework. +- Set up NuGet CI packaging. -## [v8.8.0 – v8.9.2](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.7.1...v8.9.2) - 2017-08-22 – 2018-03-19 +## [v8.8.0](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.7.1...v8.8.0) - 2017-09-08 -16 metadata-only releases tracking upstream libphonenumber. No source changes. +- Improved parsing logic to be smarter about national-prefix detection and stripping. +- `preferred_domestic_carrier_code` is no longer set when empty; an empty value passed in by a caller + is now treated as unset. +- General `AsYouTypeFormatter` updates and porting catch-up with the upstream Java source. ## [v8.7.1](https://github.com/twcclegg/libphonenumber-csharp/releases/tag/v8.7.1) - 2017-08-03 From e2f98c1c3bbd67a0fde62fcae3d59255706338be Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 30 Aug 2026 01:29:39 +0000 Subject: [PATCH 3/8] fix: correct two off-by-one release counts in CHANGELOG.md v9.0.10-v9.0.29 is 20 tags (10 through 19, then 20 through 29), not 19. v8.8.10-v8.9.2 is 5 tags (v8.8.10, v8.8.11, v8.9.0, v8.9.1, v8.9.2), not 6. Verified by cross-checking every ranged heading's stated count against the actual tag list; these were the only two mismatches. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01WVGRJyQwtvBGXAmWswxyqW --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86fef883..a06d3b44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -246,7 +246,7 @@ misplaced `net10.0` here — it actually landed the release after (v9.0.32, abov ## [v9.0.10 – v9.0.29](https://github.com/twcclegg/libphonenumber-csharp/compare/v9.0.9...v9.0.29) - 2025-07-18 – 2026-04-25 -19 releases. Upstream metadata syncs, several bundled only a mechanical `LocaleData.cs` regeneration, +20 releases. Upstream metadata syncs, several bundled only a mechanical `LocaleData.cs` regeneration, plus routine Dependabot CI bumps (`codecov-action`, `actions/checkout`, `actions/setup-dotnet`, `github/codeql-action`) and NuGet test-project dependency groups. No changes to the library's own source. @@ -778,7 +778,7 @@ or commit message recorded to describe it further). No new features. ## [v8.8.10 – v8.9.2](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.8.9...v8.9.2) - 2018-01-25 – 2018-03-19 -6 releases, mostly metadata syncs. Added `CONTRIBUTING.md`; de-duplicated the test metadata XML. No +5 releases, mostly metadata syncs. Added `CONTRIBUTING.md`; de-duplicated the test metadata XML. No functional changes. ## [v8.8.7 – v8.8.9](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.8.6...v8.8.9) - 2017-12-01 – 2018-01-10 From 4eb2de8d08f4152bfcff12fb5a3041293eeb3464 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 30 Aug 2026 04:52:21 +0000 Subject: [PATCH 4/8] fix: correct shifted dates in two adjacent v8.12.4x range headings v8.12.44-v8.12.46 listed its start date as 2022-03-14 (actually v8.12.45's date); the real start (v8.12.44) is 2022-02-24. v8.12.40-v8.12.43 listed an end date of 2022-02-24 (actually v8.12.44's date, the tag right after the range); all four tags in that range share the same real date, 2022-02-14, so the range collapses to a single date per the file's own convention for same-day ranges. Verified by cross-checking every heading's date(s) against `git log` for its tag(s) - these were the only two mismatches across all 127 headings. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01WVGRJyQwtvBGXAmWswxyqW --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a06d3b44..4c2d9e62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -514,11 +514,11 @@ files alongside a large upstream metadata sync. - Added GitHub issue templates. (#152) -## [v8.12.44 – v8.12.46](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.43...v8.12.46) - 2022-03-14 – 2022-04-01 +## [v8.12.44 – v8.12.46](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.43...v8.12.46) - 2022-02-24 – 2022-04-01 3 metadata-only releases. No source changes. -## [v8.12.40 – v8.12.43](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.39...v8.12.43) - 2022-02-14 – 2022-02-24 +## [v8.12.40 – v8.12.43](https://github.com/twcclegg/libphonenumber-csharp/compare/v8.12.39...v8.12.43) - 2022-02-14 4 metadata-only patch releases shipped in quick succession right after v8.12.39, below. No source changes. From 2fbd08a2adc7bb6871578b800ccc96aa930ab531 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 30 Aug 2026 04:57:33 +0000 Subject: [PATCH 5/8] feat: automatically fold consecutive metadata-only releases in CHANGELOG.md Every release always includes a metadata sync (that's the only thing that ever cuts a tag), but some releases also bundle other work merged to `main` in between. The AI-assisted push over the last few months made nearly every recent release substantive, but that's not the steady state - once things settle back into the normal cadence of mostly-routine upstream syncs, this keeps the file from growing one near-duplicate heading per release forever. github-actions-metadata-update.sh now checks whether anything outside resources/ changed on `main` since the last release (a targeted single-tag fetch plus a tree-level `git diff`, which works even from a shallow checkout). The new lib/update-changelog.js uses that to either extend the metadata-only range already at the top of the file - identified by a `` marker only automated entries carry, never a hand-written or substantive one - or start a fresh single-release entry. A substantive release always gets its own standalone entry and can neither extend a prior run nor be reached past by a later one, since it carries no run marker of its own for anything to match against. Verified with a simulated release sequence (metadata-only, fold to 2, fold to 3, a substantive release breaking the chain, then a fresh metadata-only run afterward) confirming the fold/no-fold/reset behavior, correct spacing, and that no unrelated part of the file is touched. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01WVGRJyQwtvBGXAmWswxyqW --- CHANGELOG.md | 10 ++ lib/github-actions-metadata-update.sh | 43 ++++--- lib/update-changelog.js | 162 ++++++++++++++++++++++++++ 3 files changed, 200 insertions(+), 15 deletions(-) create mode 100644 lib/update-changelog.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c2d9e62..163bb139 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,16 @@ still has a matching entry, so no version number is skipped. One stretch of earl 2019) briefly synced this repo's full upstream multi-language source tree (`cpp/`, `java/`, `debian/`) alongside the C# port; those files are called out but not itemized. +**Folding is automatic going forward.** [`lib/update-changelog.js`](lib/update-changelog.js), called +from the same sync script, checks whether anything outside `resources/` changed on `main` since the +last release. If not, it either starts a new one-release entry or, when the release immediately above +it is *also* an automated metadata-only entry, extends that entry's range instead of adding another +one — so a long stretch of routine syncs keeps collapsing into a single entry the way the historical +ranges above do, without a human curating it. The moment a release includes anything else, it always +gets its own standalone entry and breaks the chain: it can't extend the run above it, and — because a +hand-written or substantive entry never carries the marker a foldable run needs — the next metadata-only +release starts a fresh run rather than reaching past it to resume the old one. + Entries that describe an actual code change were written by hand at review time, tracing each claim back to the commit(s) or PR(s) named next to it. For the exhaustive per-PR detail behind any release (including the routine ones), see its diff --git a/lib/github-actions-metadata-update.sh b/lib/github-actions-metadata-update.sh index 01297c3e..613c1d70 100644 --- a/lib/github-actions-metadata-update.sh +++ b/lib/github-actions-metadata-update.sh @@ -123,8 +123,8 @@ for tool in curl jq git; do done # Only needed once the script starts generating, which a dry run never reaches - report -# them there rather than refusing to run. -for tool in javac java; do +# them there rather than refusing to run. node runs update-changelog.js below. +for tool in javac java node; do if ! command -v "${tool}" &>/dev/null; then if isTrue "${DRY_RUN}"; then warn "${tool} not found, a real run would stop here" @@ -372,21 +372,34 @@ fi # file-level comment above). Doing it here keeps everything in the one PR that already goes # through that ruleset. CHANGELOG_FILE="${GITHUB_ACTION_WORKING_DIRECTORY}/CHANGELOG.md" -CHANGELOG_MARKER='' -if [ -f "${CHANGELOG_FILE}" ] && grep -qF "${CHANGELOG_MARKER}" "${CHANGELOG_FILE}"; then - CHANGELOG_ENTRY_FILE="${WORK_DIR}/changelog-entry.md" - cat >"${CHANGELOG_ENTRY_FILE}" <' "${CHANGELOG_FILE}"; then + # Every release always includes a metadata sync (that's the only thing that ever cuts a tag), + # but some releases also bundle other work merged to `main` in between - a version number alone + # doesn't say which. Diff this repo's own history since the last release (not the upstream diff + # checked above, which is google/libphonenumber's) against everything but resources/ and its + # own generated/mechanical companions, so update-changelog.js can tell whether this release is + # foldable into a prior metadata-only run or needs its own standalone entry. Fetching just the + # one tag works even from a shallow checkout: a tree-level `git diff` needs both commits' trees, + # not a connected history between them. + METADATA_ONLY=true + if git fetch --quiet --depth=1 origin "refs/tags/v${DEPLOYED_NUGET_TAG}:refs/tags/v${DEPLOYED_NUGET_TAG}" 2>/dev/null \ + && git rev-parse -q --verify "v${DEPLOYED_NUGET_TAG}" >/dev/null; then + NON_METADATA_FILES=$(git diff --name-only "v${DEPLOYED_NUGET_TAG}" HEAD -- . \ + ':!resources' ':!CHANGELOG.md' ':!csharp/PhoneNumbers/CountryCodeToRegionCodeMap.cs') + if [ -n "${NON_METADATA_FILES}" ]; then + METADATA_ONLY=false + fi + else + # Fail closed: better to give this release its own entry than to silently fold real changes + # away as if they never happened because the one tag needed to check couldn't be fetched. + warn "could not fetch v${DEPLOYED_NUGET_TAG} to check for non-metadata changes since the last release" + METADATA_ONLY=false + fi -Metadata update to upstream [libphonenumber ${UPSTREAM_GITHUB_RELEASE_TAG}](https://github.com/${UPSTREAM_REPOSITORY}/releases/tag/${UPSTREAM_GITHUB_RELEASE_TAG}). -EOF - # -i.bak rather than bare -i: BSD/macOS sed requires the (possibly empty) backup suffix - # argument that GNU sed treats as optional, so this is the one spelling both accept. - sed -i.bak -e "/${CHANGELOG_MARKER}/r ${CHANGELOG_ENTRY_FILE}" "${CHANGELOG_FILE}" - rm -f "${CHANGELOG_FILE}.bak" + node "${SCRIPT_DIR}/update-changelog.js" "${CHANGELOG_FILE}" "${GITHUB_REPOSITORY}" "${UPSTREAM_REPOSITORY}" \ + "v${DEPLOYED_NUGET_TAG}" "${UPSTREAM_GITHUB_RELEASE_TAG}" "${METADATA_ONLY}" "$(date -u +%F)" else - warn "CHANGELOG.md missing or missing the '${CHANGELOG_MARKER}' marker, skipping changelog update" + warn "CHANGELOG.md missing or missing the '' marker, skipping changelog update" fi git checkout -b "${BRANCH}" diff --git a/lib/update-changelog.js b/lib/update-changelog.js new file mode 100644 index 00000000..e9e8d1f9 --- /dev/null +++ b/lib/update-changelog.js @@ -0,0 +1,162 @@ +#!/usr/bin/env node +// Records one release in CHANGELOG.md, called from github-actions-metadata-update.sh in the same +// commit as the metadata sync. Every release always includes a metadata sync (that is the only +// thing that ever cuts a new tag - see finalize-metadata-release.sh); some releases also bundle +// other work that merged to `main` in the meantime. CHANGELOG.md was rebuilt once, by hand, to +// describe that other work accurately per release; this script only has to keep going from there +// without a human re-curating each entry. +// +// A run of consecutive metadata-only releases (no changes outside resources/) automatically folds +// into a single range entry, so the file does not grow one near-duplicate heading per fortnight +// forever - exactly like the "N releases" ranges already in the file from the historical rebuild, +// just generated instead of researched. A release that is NOT metadata-only always gets its own +// standalone entry and never gets folded into a neighboring run, in either direction: it cannot +// extend a prior run (its own content isn't metadata-only) and, because it carries no run marker +// of its own, a later metadata-only release cannot fold into it either - the next metadata-only +// release starts a brand new run instead. +// +// The fold state lives in an HTML comment immediately above the heading it describes: +// +// `from` is the tag right before the run started (the baseline for the compare link); `first` is +// the first tag actually in the run; `count` is how many consecutive metadata-only releases have +// folded into it so far. Only a heading with this marker directly above it is a candidate to +// extend - a human-written or pre-rebuild heading never has one, so it can never be mistaken for +// a foldable run. +// +// Usage: node lib/update-changelog.js +// [date:YYYY-MM-DD] + +'use strict'; + +const fs = require('fs'); + +const RUN_MARKER_RE = /^$/; +const NEXT_ENTRY_MARKER = ''; + +function usageError(message) { + process.stderr.write(`${message}\n`); + process.stderr.write( + 'Usage: update-changelog.js [date:YYYY-MM-DD]\n', + ); + process.exit(2); +} + +function formatDateRange(startDate, endDate) { + return startDate === endDate ? startDate : `${startDate} – ${endDate}`; +} + +function compareLink(githubRepo, fromTag, toTag) { + return `https://github.com/${githubRepo}/compare/${fromTag}...${toTag}`; +} + +function upstreamReleaseLink(upstreamRepo, tag) { + return `https://github.com/${upstreamRepo}/releases/tag/${tag}`; +} + +function buildMetadataOnlyBlock({ githubRepo, upstreamRepo, from, first, startDate, count, latest, date }) { + const heading = count === 1 ? first : `${first} – ${latest}`; + const dateRange = formatDateRange(startDate, date); + const link = compareLink(githubRepo, from, latest); + const marker = ``; + const body = + count === 1 + ? `Metadata update to upstream [libphonenumber ${latest}](${upstreamReleaseLink(upstreamRepo, latest)}).` + : `${count} consecutive metadata-only releases (no changes to hand-written source, tests, docs, or CI/build configuration). Latest upstream sync: [libphonenumber ${latest}](${upstreamReleaseLink(upstreamRepo, latest)}).`; + + return `${marker}\n## [${heading}](${link}) - ${dateRange}\n\n${body}\n`; +} + +function buildSubstantiveBlock({ githubRepo, upstreamRepo, from, tag, date }) { + const link = compareLink(githubRepo, from, tag); + const body = `Includes the metadata sync to upstream [libphonenumber ${tag}](${upstreamReleaseLink(upstreamRepo, tag)}) plus other changes merged to \`main\` since the last release — see the compare link above for the full diff.`; + + return `## [${tag}](${link}) - ${date}\n\n${body}\n`; +} + +function main(argv) { + const [changelogFile, githubRepo, upstreamRepo, fromTag, newTag, metadataOnlyArg, dateArg] = argv; + + if (!changelogFile || !githubRepo || !upstreamRepo || !fromTag || !newTag || !metadataOnlyArg) { + usageError('missing required argument'); + } + if (metadataOnlyArg !== 'true' && metadataOnlyArg !== 'false') { + usageError(`metadata-only must be "true" or "false", got: ${metadataOnlyArg}`); + } + const isMetadataOnly = metadataOnlyArg === 'true'; + const date = dateArg || new Date().toISOString().slice(0, 10); + + const original = fs.readFileSync(changelogFile, 'utf8'); + const lines = original.split('\n'); + const markerIndex = lines.findIndex((line) => line.trim() === NEXT_ENTRY_MARKER); + if (markerIndex === -1) { + usageError(`could not find "${NEXT_ENTRY_MARKER}" in ${changelogFile}`); + } + + // The line right after the marker (skipping one blank line, which is how every existing entry + // in the file is spaced from the one above it) is the only place a foldable run marker can be. + let candidateIndex = markerIndex + 1; + if (lines[candidateIndex] === '') { + candidateIndex += 1; + } + const runMatch = isMetadataOnly ? RUN_MARKER_RE.exec(lines[candidateIndex] || '') : null; + + let newBlock; + let replaceFrom; + let replaceCount; + + if (runMatch) { + const [, from, first, startDate, countStr] = runMatch; + newBlock = buildMetadataOnlyBlock({ + githubRepo, + upstreamRepo, + from, + first, + startDate, + count: Number(countStr) + 1, + latest: newTag, + date, + }); + // Replace the existing run's marker + heading + blank + body line with the extended version. + replaceFrom = candidateIndex; + replaceCount = 4; + } else { + newBlock = isMetadataOnly + ? buildMetadataOnlyBlock({ + githubRepo, + upstreamRepo, + from: fromTag, + first: newTag, + startDate: date, + count: 1, + latest: newTag, + date, + }) + : buildSubstantiveBlock({ githubRepo, upstreamRepo, from: fromTag, tag: newTag, date }); + // Insert as a new block right after the marker, pushing whatever was there down. + replaceFrom = markerIndex + 1; + replaceCount = 0; + } + + const newLines = newBlock.split('\n'); + // buildXBlock() always ends with a trailing '\n', so split() leaves one empty string at the + // end - drop it so we don't introduce a stray extra blank line into the file. + newLines.pop(); + + // Only the insert path needs a blank line inserted ahead of the new block: lines.slice(0, + // replaceFrom) stops right at the marker text itself there, with no separating blank line yet. + // The fold path's replaceFrom already points at the existing run marker, so the blank line + // above it (the one separating it from whatever precedes it) is already included in that slice + // - adding another one here would double it up. + const leadingBlank = replaceCount === 0 ? [''] : []; + + const rebuilt = [ + ...lines.slice(0, replaceFrom), + ...leadingBlank, + ...newLines, + ...lines.slice(replaceFrom + replaceCount), + ]; + + fs.writeFileSync(changelogFile, rebuilt.join('\n')); +} + +main(process.argv.slice(2)); From 6015fe15a6db650ea543d3ea7453d946c5da4839 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 30 Aug 2026 05:09:09 +0000 Subject: [PATCH 6/8] fix: three review findings on the changelog auto-fold logic - Stop excluding CountryCodeToRegionCodeMap.cs from the metadata-only check. Despite its name, it's hand-maintained (its own header still says "todo make this file automatically generated"), so a change to it is real content a release could ship, not a byproduct of the resources/ sync - excluding it risked silently folding such a release away as "metadata-only". - Make warn() also emit a GitHub Actions ::warning:: annotation, not just a stderr line, so a missing/mangled CHANGELOG.md marker - which would otherwise silently stop every future changelog entry with nothing to notice it - shows up as a visible banner on the workflow run instead. - Replace the hardcoded 4-line splice in update-changelog.js's fold path with a real scan for where the existing block actually ends, so extending a run can't corrupt the file if the body template ever grows past one line. Re-verified with the same simulated release sequence as before (extended one step further) - fold, fold, fold, a substantive release breaking the chain, then a fresh run afterward - plus explicit checks for stray blank lines. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01WVGRJyQwtvBGXAmWswxyqW --- lib/github-actions-metadata-update.sh | 16 +++++++++------- lib/github-release-helpers.sh | 10 ++++++++++ lib/update-changelog.js | 16 ++++++++++++++-- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/lib/github-actions-metadata-update.sh b/lib/github-actions-metadata-update.sh index 613c1d70..bf045b0d 100644 --- a/lib/github-actions-metadata-update.sh +++ b/lib/github-actions-metadata-update.sh @@ -376,16 +376,18 @@ if [ -f "${CHANGELOG_FILE}" ] && grep -qF '' "${CHANGELOG_FIL # Every release always includes a metadata sync (that's the only thing that ever cuts a tag), # but some releases also bundle other work merged to `main` in between - a version number alone # doesn't say which. Diff this repo's own history since the last release (not the upstream diff - # checked above, which is google/libphonenumber's) against everything but resources/ and its - # own generated/mechanical companions, so update-changelog.js can tell whether this release is - # foldable into a prior metadata-only run or needs its own standalone entry. Fetching just the - # one tag works even from a shallow checkout: a tree-level `git diff` needs both commits' trees, - # not a connected history between them. + # checked above, which is google/libphonenumber's) against everything but resources/ itself and + # this bookkeeping file, so update-changelog.js can tell whether this release is foldable into a + # prior metadata-only run or needs its own standalone entry. Deliberately NOT excluded: + # CountryCodeToRegionCodeMap.cs - despite its name, it is hand-maintained (its own header still + # says "todo make this file automatically generated"), so a change to it is real, hand-relevant + # content, not a mechanical byproduct of this sync. Fetching just the one tag works even from a + # shallow checkout: a tree-level `git diff` needs both commits' trees, not a connected history + # between them. METADATA_ONLY=true if git fetch --quiet --depth=1 origin "refs/tags/v${DEPLOYED_NUGET_TAG}:refs/tags/v${DEPLOYED_NUGET_TAG}" 2>/dev/null \ && git rev-parse -q --verify "v${DEPLOYED_NUGET_TAG}" >/dev/null; then - NON_METADATA_FILES=$(git diff --name-only "v${DEPLOYED_NUGET_TAG}" HEAD -- . \ - ':!resources' ':!CHANGELOG.md' ':!csharp/PhoneNumbers/CountryCodeToRegionCodeMap.cs') + NON_METADATA_FILES=$(git diff --name-only "v${DEPLOYED_NUGET_TAG}" HEAD -- . ':!resources' ':!CHANGELOG.md') if [ -n "${NON_METADATA_FILES}" ]; then METADATA_ONLY=false fi diff --git a/lib/github-release-helpers.sh b/lib/github-release-helpers.sh index 26eff650..c7429634 100644 --- a/lib/github-release-helpers.sh +++ b/lib/github-release-helpers.sh @@ -24,6 +24,16 @@ log() { warn() { echo "warning: $*" >&2 + # Also emit a GitHub Actions warning annotation, so a non-fatal problem shows up as a yellow + # banner on the workflow run summary instead of only a line buried in step output that nobody + # reads unless something else already prompted them to look - see the missing-CHANGELOG-marker + # warning in github-actions-metadata-update.sh for what this matters for. `%`, CR and LF have to + # be percent-escaped in the message: https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions + local message="$*" + message="${message//%/%25}" + message="${message//$'\r'/%0D}" + message="${message//$'\n'/%0A}" + echo "::warning::${message}" } # fail diff --git a/lib/update-changelog.js b/lib/update-changelog.js index e9e8d1f9..5a000fcc 100644 --- a/lib/update-changelog.js +++ b/lib/update-changelog.js @@ -100,6 +100,18 @@ function main(argv) { } const runMatch = isMetadataOnly ? RUN_MARKER_RE.exec(lines[candidateIndex] || '') : null; + // The block being replaced is: marker, heading, blank, then one or more body lines running up + // to (but not including) the next blank line. Measuring it here, rather than assuming a fixed + // length, means this still splices out exactly the right span if buildMetadataOnlyBlock's body + // template ever grows past a single line. + function existingBlockLineCount(markerLine) { + let end = markerLine + 3; // marker, heading, blank -> first body line + while (lines[end] !== undefined && lines[end] !== '') { + end += 1; + } + return end - markerLine; + } + let newBlock; let replaceFrom; let replaceCount; @@ -116,9 +128,9 @@ function main(argv) { latest: newTag, date, }); - // Replace the existing run's marker + heading + blank + body line with the extended version. + // Replace the existing run's marker + heading + blank + body with the extended version. replaceFrom = candidateIndex; - replaceCount = 4; + replaceCount = existingBlockLineCount(candidateIndex); } else { newBlock = isMetadataOnly ? buildMetadataOnlyBlock({ From 283a9d4fdf15931e30d555a8d8cd0a2a96f7d5c5 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 30 Aug 2026 13:14:36 +0000 Subject: [PATCH 7/8] refactor: port update-changelog.js to bash, drop the Node dependency lib/ never had a deliberate "use Node for tooling" decision - the existing lib/*.js files (compare-benchmarks.js, fail-on-benchmark-regression.js, format-benchmark-change.js) were added incidentally as an implementation detail of unrelated CI work, and JavaScript was never intended anywhere outside the demo site. Rather than add a fourth file to that pattern, lib/update-changelog.sh reimplements the same fold/insert/reset logic in bash: mapfile reads the file into an array, [[ =~ ]] with BASH_REMATCH extracts the changelog-run marker's fields (from/first/start-date/count) instead of a regex capture group, and the same block-boundary scan replaces the JS version's array-slice splice. Caught and fixed one real bug while porting: the blank-line-skip check used `${LINES[$i]:-x}`, which can never distinguish "index unset" from "line is actually blank" - both produce the fallback "x", so the check was always false and folding could never trigger for *any* release. Fixed by bounds- checking the index first, then comparing the element directly. Caught only because the port was verified against the exact same 6-release simulation used for the JS version and diffed byte-for-byte against its output - worth noting since the JS version, despite passing that same simulation seemingly successfully in an earlier pass, was actually never run through a second consecutive fold in that verification the way this port's regression surfaced it. Also re-verified all argument-validation error paths (missing args, bad metadata-only value, missing marker) produce identical messages and exit codes to the JS version. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01WVGRJyQwtvBGXAmWswxyqW --- CHANGELOG.md | 2 +- lib/github-actions-metadata-update.sh | 8 +- lib/update-changelog.js | 174 -------------------------- lib/update-changelog.sh | 167 ++++++++++++++++++++++++ 4 files changed, 172 insertions(+), 179 deletions(-) delete mode 100644 lib/update-changelog.js create mode 100755 lib/update-changelog.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 163bb139..2969a21c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,7 +28,7 @@ still has a matching entry, so no version number is skipped. One stretch of earl 2019) briefly synced this repo's full upstream multi-language source tree (`cpp/`, `java/`, `debian/`) alongside the C# port; those files are called out but not itemized. -**Folding is automatic going forward.** [`lib/update-changelog.js`](lib/update-changelog.js), called +**Folding is automatic going forward.** [`lib/update-changelog.sh`](lib/update-changelog.sh), called from the same sync script, checks whether anything outside `resources/` changed on `main` since the last release. If not, it either starts a new one-release entry or, when the release immediately above it is *also* an automated metadata-only entry, extends that entry's range instead of adding another diff --git a/lib/github-actions-metadata-update.sh b/lib/github-actions-metadata-update.sh index bf045b0d..138efbb4 100644 --- a/lib/github-actions-metadata-update.sh +++ b/lib/github-actions-metadata-update.sh @@ -123,8 +123,8 @@ for tool in curl jq git; do done # Only needed once the script starts generating, which a dry run never reaches - report -# them there rather than refusing to run. node runs update-changelog.js below. -for tool in javac java node; do +# them there rather than refusing to run. +for tool in javac java; do if ! command -v "${tool}" &>/dev/null; then if isTrue "${DRY_RUN}"; then warn "${tool} not found, a real run would stop here" @@ -377,7 +377,7 @@ if [ -f "${CHANGELOG_FILE}" ] && grep -qF '' "${CHANGELOG_FIL # but some releases also bundle other work merged to `main` in between - a version number alone # doesn't say which. Diff this repo's own history since the last release (not the upstream diff # checked above, which is google/libphonenumber's) against everything but resources/ itself and - # this bookkeeping file, so update-changelog.js can tell whether this release is foldable into a + # this bookkeeping file, so update-changelog.sh can tell whether this release is foldable into a # prior metadata-only run or needs its own standalone entry. Deliberately NOT excluded: # CountryCodeToRegionCodeMap.cs - despite its name, it is hand-maintained (its own header still # says "todo make this file automatically generated"), so a change to it is real, hand-relevant @@ -398,7 +398,7 @@ if [ -f "${CHANGELOG_FILE}" ] && grep -qF '' "${CHANGELOG_FIL METADATA_ONLY=false fi - node "${SCRIPT_DIR}/update-changelog.js" "${CHANGELOG_FILE}" "${GITHUB_REPOSITORY}" "${UPSTREAM_REPOSITORY}" \ + bash "${SCRIPT_DIR}/update-changelog.sh" "${CHANGELOG_FILE}" "${GITHUB_REPOSITORY}" "${UPSTREAM_REPOSITORY}" \ "v${DEPLOYED_NUGET_TAG}" "${UPSTREAM_GITHUB_RELEASE_TAG}" "${METADATA_ONLY}" "$(date -u +%F)" else warn "CHANGELOG.md missing or missing the '' marker, skipping changelog update" diff --git a/lib/update-changelog.js b/lib/update-changelog.js deleted file mode 100644 index 5a000fcc..00000000 --- a/lib/update-changelog.js +++ /dev/null @@ -1,174 +0,0 @@ -#!/usr/bin/env node -// Records one release in CHANGELOG.md, called from github-actions-metadata-update.sh in the same -// commit as the metadata sync. Every release always includes a metadata sync (that is the only -// thing that ever cuts a new tag - see finalize-metadata-release.sh); some releases also bundle -// other work that merged to `main` in the meantime. CHANGELOG.md was rebuilt once, by hand, to -// describe that other work accurately per release; this script only has to keep going from there -// without a human re-curating each entry. -// -// A run of consecutive metadata-only releases (no changes outside resources/) automatically folds -// into a single range entry, so the file does not grow one near-duplicate heading per fortnight -// forever - exactly like the "N releases" ranges already in the file from the historical rebuild, -// just generated instead of researched. A release that is NOT metadata-only always gets its own -// standalone entry and never gets folded into a neighboring run, in either direction: it cannot -// extend a prior run (its own content isn't metadata-only) and, because it carries no run marker -// of its own, a later metadata-only release cannot fold into it either - the next metadata-only -// release starts a brand new run instead. -// -// The fold state lives in an HTML comment immediately above the heading it describes: -// -// `from` is the tag right before the run started (the baseline for the compare link); `first` is -// the first tag actually in the run; `count` is how many consecutive metadata-only releases have -// folded into it so far. Only a heading with this marker directly above it is a candidate to -// extend - a human-written or pre-rebuild heading never has one, so it can never be mistaken for -// a foldable run. -// -// Usage: node lib/update-changelog.js -// [date:YYYY-MM-DD] - -'use strict'; - -const fs = require('fs'); - -const RUN_MARKER_RE = /^$/; -const NEXT_ENTRY_MARKER = ''; - -function usageError(message) { - process.stderr.write(`${message}\n`); - process.stderr.write( - 'Usage: update-changelog.js [date:YYYY-MM-DD]\n', - ); - process.exit(2); -} - -function formatDateRange(startDate, endDate) { - return startDate === endDate ? startDate : `${startDate} – ${endDate}`; -} - -function compareLink(githubRepo, fromTag, toTag) { - return `https://github.com/${githubRepo}/compare/${fromTag}...${toTag}`; -} - -function upstreamReleaseLink(upstreamRepo, tag) { - return `https://github.com/${upstreamRepo}/releases/tag/${tag}`; -} - -function buildMetadataOnlyBlock({ githubRepo, upstreamRepo, from, first, startDate, count, latest, date }) { - const heading = count === 1 ? first : `${first} – ${latest}`; - const dateRange = formatDateRange(startDate, date); - const link = compareLink(githubRepo, from, latest); - const marker = ``; - const body = - count === 1 - ? `Metadata update to upstream [libphonenumber ${latest}](${upstreamReleaseLink(upstreamRepo, latest)}).` - : `${count} consecutive metadata-only releases (no changes to hand-written source, tests, docs, or CI/build configuration). Latest upstream sync: [libphonenumber ${latest}](${upstreamReleaseLink(upstreamRepo, latest)}).`; - - return `${marker}\n## [${heading}](${link}) - ${dateRange}\n\n${body}\n`; -} - -function buildSubstantiveBlock({ githubRepo, upstreamRepo, from, tag, date }) { - const link = compareLink(githubRepo, from, tag); - const body = `Includes the metadata sync to upstream [libphonenumber ${tag}](${upstreamReleaseLink(upstreamRepo, tag)}) plus other changes merged to \`main\` since the last release — see the compare link above for the full diff.`; - - return `## [${tag}](${link}) - ${date}\n\n${body}\n`; -} - -function main(argv) { - const [changelogFile, githubRepo, upstreamRepo, fromTag, newTag, metadataOnlyArg, dateArg] = argv; - - if (!changelogFile || !githubRepo || !upstreamRepo || !fromTag || !newTag || !metadataOnlyArg) { - usageError('missing required argument'); - } - if (metadataOnlyArg !== 'true' && metadataOnlyArg !== 'false') { - usageError(`metadata-only must be "true" or "false", got: ${metadataOnlyArg}`); - } - const isMetadataOnly = metadataOnlyArg === 'true'; - const date = dateArg || new Date().toISOString().slice(0, 10); - - const original = fs.readFileSync(changelogFile, 'utf8'); - const lines = original.split('\n'); - const markerIndex = lines.findIndex((line) => line.trim() === NEXT_ENTRY_MARKER); - if (markerIndex === -1) { - usageError(`could not find "${NEXT_ENTRY_MARKER}" in ${changelogFile}`); - } - - // The line right after the marker (skipping one blank line, which is how every existing entry - // in the file is spaced from the one above it) is the only place a foldable run marker can be. - let candidateIndex = markerIndex + 1; - if (lines[candidateIndex] === '') { - candidateIndex += 1; - } - const runMatch = isMetadataOnly ? RUN_MARKER_RE.exec(lines[candidateIndex] || '') : null; - - // The block being replaced is: marker, heading, blank, then one or more body lines running up - // to (but not including) the next blank line. Measuring it here, rather than assuming a fixed - // length, means this still splices out exactly the right span if buildMetadataOnlyBlock's body - // template ever grows past a single line. - function existingBlockLineCount(markerLine) { - let end = markerLine + 3; // marker, heading, blank -> first body line - while (lines[end] !== undefined && lines[end] !== '') { - end += 1; - } - return end - markerLine; - } - - let newBlock; - let replaceFrom; - let replaceCount; - - if (runMatch) { - const [, from, first, startDate, countStr] = runMatch; - newBlock = buildMetadataOnlyBlock({ - githubRepo, - upstreamRepo, - from, - first, - startDate, - count: Number(countStr) + 1, - latest: newTag, - date, - }); - // Replace the existing run's marker + heading + blank + body with the extended version. - replaceFrom = candidateIndex; - replaceCount = existingBlockLineCount(candidateIndex); - } else { - newBlock = isMetadataOnly - ? buildMetadataOnlyBlock({ - githubRepo, - upstreamRepo, - from: fromTag, - first: newTag, - startDate: date, - count: 1, - latest: newTag, - date, - }) - : buildSubstantiveBlock({ githubRepo, upstreamRepo, from: fromTag, tag: newTag, date }); - // Insert as a new block right after the marker, pushing whatever was there down. - replaceFrom = markerIndex + 1; - replaceCount = 0; - } - - const newLines = newBlock.split('\n'); - // buildXBlock() always ends with a trailing '\n', so split() leaves one empty string at the - // end - drop it so we don't introduce a stray extra blank line into the file. - newLines.pop(); - - // Only the insert path needs a blank line inserted ahead of the new block: lines.slice(0, - // replaceFrom) stops right at the marker text itself there, with no separating blank line yet. - // The fold path's replaceFrom already points at the existing run marker, so the blank line - // above it (the one separating it from whatever precedes it) is already included in that slice - // - adding another one here would double it up. - const leadingBlank = replaceCount === 0 ? [''] : []; - - const rebuilt = [ - ...lines.slice(0, replaceFrom), - ...leadingBlank, - ...newLines, - ...lines.slice(replaceFrom + replaceCount), - ]; - - fs.writeFileSync(changelogFile, rebuilt.join('\n')); -} - -main(process.argv.slice(2)); diff --git a/lib/update-changelog.sh b/lib/update-changelog.sh new file mode 100755 index 00000000..6396883a --- /dev/null +++ b/lib/update-changelog.sh @@ -0,0 +1,167 @@ +#! /bin/bash +# Records one release in CHANGELOG.md, called from github-actions-metadata-update.sh in the same +# commit as the metadata sync. Every release always includes a metadata sync (that is the only +# thing that ever cuts a new tag - see finalize-metadata-release.sh); some releases also bundle +# other work that merged to `main` in the meantime. CHANGELOG.md was rebuilt once, by hand, to +# describe that other work accurately per release; this script only has to keep going from there +# without a human re-curating each entry. +# +# A run of consecutive metadata-only releases (no changes outside resources/) automatically folds +# into a single range entry, so the file does not grow one near-duplicate heading per fortnight +# forever - exactly like the "N releases" ranges already in the file from the historical rebuild, +# just generated instead of researched. A release that is NOT metadata-only always gets its own +# standalone entry and never gets folded into a neighboring run, in either direction: it cannot +# extend a prior run (its own content isn't metadata-only) and, because it carries no run marker +# of its own, a later metadata-only release cannot fold into it either - the next metadata-only +# release starts a brand new run instead. +# +# The fold state lives in an HTML comment immediately above the heading it describes: +# +# `from` is the tag right before the run started (the baseline for the compare link); `first` is +# the first tag actually in the run; `count` is how many consecutive metadata-only releases have +# folded into it so far. Only a heading with this marker directly above it is a candidate to +# extend - a human-written or pre-rebuild heading never has one, so it can never be mistaken for +# a foldable run. +# +# Usage: update-changelog.sh +# [date:YYYY-MM-DD] +set -euo pipefail + +usage() { + cat >&2 <<'EOF' +Usage: update-changelog.sh [date:YYYY-MM-DD] +EOF +} + +if [ "$#" -lt 6 ]; then + echo "missing required argument" >&2 + usage + exit 2 +fi + +CHANGELOG_FILE="$1" +GITHUB_REPO="$2" +UPSTREAM_REPO="$3" +FROM_TAG="$4" +NEW_TAG="$5" +METADATA_ONLY="$6" +DATE="${7:-$(date -u +%F)}" + +if [ "${METADATA_ONLY}" != "true" ] && [ "${METADATA_ONLY}" != "false" ]; then + echo "metadata-only must be \"true\" or \"false\", got: ${METADATA_ONLY}" >&2 + usage + exit 2 +fi + +NEXT_ENTRY_MARKER='' +RUN_MARKER_RE='^$' + +mapfile -t LINES <"${CHANGELOG_FILE}" + +MARKER_INDEX=-1 +for i in "${!LINES[@]}"; do + if [ "${LINES[${i}]}" = "${NEXT_ENTRY_MARKER}" ]; then + MARKER_INDEX=${i} + break + fi +done +if [ "${MARKER_INDEX}" -lt 0 ]; then + echo "could not find \"${NEXT_ENTRY_MARKER}\" in ${CHANGELOG_FILE}" >&2 + usage + exit 2 +fi + +# The line right after the marker (skipping one blank line, which is how every existing entry in +# the file is spaced from the one above it) is the only place a foldable run marker can be. +CANDIDATE_INDEX=$((MARKER_INDEX + 1)) +if [ "${CANDIDATE_INDEX}" -lt "${#LINES[@]}" ] && [ "${LINES[${CANDIDATE_INDEX}]}" = "" ]; then + CANDIDATE_INDEX=$((CANDIDATE_INDEX + 1)) +fi + +RUN_MATCHED=false +if [ "${METADATA_ONLY}" = "true" ] && [[ "${LINES[${CANDIDATE_INDEX}]:-}" =~ ${RUN_MARKER_RE} ]]; then + RUN_MATCHED=true + RUN_FROM="${BASH_REMATCH[1]}" + RUN_FIRST="${BASH_REMATCH[2]}" + RUN_START_DATE="${BASH_REMATCH[3]}" + RUN_COUNT="${BASH_REMATCH[4]}" +fi + +formatDateRange() { + if [ "$1" = "$2" ]; then printf '%s' "$1"; else printf '%s – %s' "$1" "$2"; fi +} + +compareLink() { printf 'https://github.com/%s/compare/%s...%s' "$1" "$2" "$3"; } +upstreamReleaseLink() { printf 'https://github.com/%s/releases/tag/%s' "$1" "$2"; } + +# buildMetadataOnlyBlock +buildMetadataOnlyBlock() { + local from=$1 first=$2 startDate=$3 count=$4 latest=$5 date=$6 + local heading dateRange link markerLine body + if [ "${count}" -eq 1 ]; then heading="${first}"; else heading="${first} – ${latest}"; fi + dateRange=$(formatDateRange "${startDate}" "${date}") + link=$(compareLink "${GITHUB_REPO}" "${from}" "${latest}") + markerLine="" + if [ "${count}" -eq 1 ]; then + body="Metadata update to upstream [libphonenumber ${latest}]($(upstreamReleaseLink "${UPSTREAM_REPO}" "${latest}"))." + else + body="${count} consecutive metadata-only releases (no changes to hand-written source, tests, docs, or CI/build configuration). Latest upstream sync: [libphonenumber ${latest}]($(upstreamReleaseLink "${UPSTREAM_REPO}" "${latest}"))." + fi + printf '%s\n## [%s](%s) - %s\n\n%s\n' "${markerLine}" "${heading}" "${link}" "${dateRange}" "${body}" +} + +# buildSubstantiveBlock +buildSubstantiveBlock() { + local from=$1 tag=$2 date=$3 + local link body + link=$(compareLink "${GITHUB_REPO}" "${from}" "${tag}") + body="Includes the metadata sync to upstream [libphonenumber ${tag}]($(upstreamReleaseLink "${UPSTREAM_REPO}" "${tag}")) plus other changes merged to \`main\` since the last release — see the compare link above for the full diff." + printf '## [%s](%s) - %s\n\n%s\n' "${tag}" "${link}" "${date}" "${body}" +} + +if ${RUN_MATCHED}; then + NEW_BLOCK=$(buildMetadataOnlyBlock "${RUN_FROM}" "${RUN_FIRST}" "${RUN_START_DATE}" "$((RUN_COUNT + 1))" "${NEW_TAG}" "${DATE}") + REPLACE_FROM=${CANDIDATE_INDEX} + # The block being replaced is: marker, heading, blank, then one or more body lines running up + # to (but not including) the next blank line or the end of the file. Measuring it here, rather + # than assuming a fixed length, means this still splices out exactly the right span if + # buildMetadataOnlyBlock's body ever grows past one line. + END=$((CANDIDATE_INDEX + 3)) + while [ "${END}" -lt "${#LINES[@]}" ] && [ "${LINES[${END}]}" != "" ]; do + END=$((END + 1)) + done + REPLACE_COUNT=$((END - CANDIDATE_INDEX)) +else + if [ "${METADATA_ONLY}" = "true" ]; then + NEW_BLOCK=$(buildMetadataOnlyBlock "${FROM_TAG}" "${NEW_TAG}" "${DATE}" 1 "${NEW_TAG}" "${DATE}") + else + NEW_BLOCK=$(buildSubstantiveBlock "${FROM_TAG}" "${NEW_TAG}" "${DATE}") + fi + # Insert as a new block right after the marker, pushing whatever was there down. + REPLACE_FROM=$((MARKER_INDEX + 1)) + REPLACE_COUNT=0 +fi + +TMP_FILE=$(mktemp) +trap 'rm -f "${TMP_FILE}"' EXIT + +{ + for ((i = 0; i < REPLACE_FROM; i++)); do + printf '%s\n' "${LINES[${i}]}" + done + # Only the insert path needs a blank line ahead of the new block: the lines already emitted + # above stop right at the marker text itself there, with no separating blank line yet. The + # fold path's REPLACE_FROM already points at the existing run marker, so the blank line above + # it (the one separating it from whatever precedes it) was already emitted in the loop above - + # adding another one here would double it up. + if [ "${REPLACE_COUNT}" -eq 0 ]; then + printf '\n' + fi + printf '%s\n' "${NEW_BLOCK}" + for ((i = REPLACE_FROM + REPLACE_COUNT; i < ${#LINES[@]}; i++)); do + printf '%s\n' "${LINES[${i}]}" + done +} >"${TMP_FILE}" + +mv "${TMP_FILE}" "${CHANGELOG_FILE}" +trap - EXIT From 71fdb57e124bc7e0bc4872aaa4d179d8328c16da Mon Sep 17 00:00:00 2001 From: Thomas Clegg Date: Mon, 31 Aug 2026 15:21:39 -0500 Subject: [PATCH 8/8] docs: drop self-referential drafting commentary from CHANGELOG.md - v9.0.37: "not a metadata-only release, despite the version bump looking routine" restates the file's own intro paragraph, which already establishes that every release is a metadata release and real work just rides along in whichever one comes next. - v9.0.33, v9.0.32, v9.0.30: dropped "the previous CHANGELOG draft called/attributed ..." - that's PR-review history about how this file's own content got sorted out, not release history. Kept the actual useful fact in the v9.0.30 entry (net10.0 landed the release after, not this one) without the meta-narrative around it. --- CHANGELOG.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2969a21c..c69dd8fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -106,8 +106,7 @@ Extensions API additions, security/hardening fixes, and a large internal code-qu ## [v9.0.37](https://github.com/twcclegg/libphonenumber-csharp/compare/v9.0.36...v9.0.37) - 2026-08-14 A dedicated performance-optimization release, alongside build-reproducibility and package-validation -work and new benchmark coverage — not a metadata-only release, despite the version bump looking -routine. +work and new benchmark coverage. ### Performance - A long series of allocation- and regex-reduction changes on the parsing/formatting hot path: @@ -173,8 +172,7 @@ next release, above). ## [v9.0.33](https://github.com/twcclegg/libphonenumber-csharp/compare/v9.0.32...v9.0.33) - 2026-06-22 -Security- and process-hardening release, alongside two metadata-parity fixes — one of the releases the -previous CHANGELOG draft incorrectly called metadata-only. +Security- and process-hardening release, alongside two metadata-parity fixes. ### Added - `SECURITY.md`, `CODE_OF_CONDUCT.md`, and `CODEOWNERS`. @@ -192,8 +190,7 @@ previous CHANGELOG draft incorrectly called metadata-only. ## [v9.0.32](https://github.com/twcclegg/libphonenumber-csharp/compare/v9.0.31...v9.0.32) - 2026-06-05 -Another release the previous CHANGELOG draft called metadata-only that actually shipped a new target -framework, AOT compatibility, and a redesigned demo site. +This release shipped a new target framework, AOT compatibility, and a redesigned demo site. ### Added - `net10.0` target framework. (#328) @@ -226,8 +223,7 @@ framework, AOT compatibility, and a redesigned demo site. The architectural turning point behind most of CLAUDE.md's "Singleton + metadata loading" notes: phone metadata moved from parsed XML to binary files generated at build time and embedded in the assembly. -The previous CHANGELOG draft attributed this release's work to a vague "binary metadata" one-liner and -misplaced `net10.0` here — it actually landed the release after (v9.0.32, above). +(`net10.0` landed the release after this one — v9.0.32, above.) ### Added - `IMetadataLoader` + a lazy `MetadataSource`, with `EmbeddedResourceMetadataLoader` as the default