From f6c1dfda7840f8e1d535e9c0b9f6dc69aa4afe6c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 4 Sep 2026 08:16:39 +0000 Subject: [PATCH 1/5] Bump frequenz-repo-config in the repo-config group across 1 directory Bumps the repo-config group with 1 update in the / directory: [frequenz-repo-config](https://github.com/frequenz-floss/frequenz-repo-config-python). Updates `frequenz-repo-config` from 0.18.0 to 0.19.0 - [Release notes](https://github.com/frequenz-floss/frequenz-repo-config-python/releases) - [Changelog](https://github.com/frequenz-floss/frequenz-repo-config-python/blob/v0.x.x/RELEASE_NOTES.md) - [Commits](https://github.com/frequenz-floss/frequenz-repo-config-python/compare/v0.18.0...v0.19.0) --- updated-dependencies: - dependency-name: frequenz-repo-config dependency-version: 0.19.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: repo-config ... Signed-off-by: dependabot[bot] --- pyproject.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 475884b..db0f566 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ requires = [ "setuptools == 84.0.0", "setuptools_scm[toml] == 10.2.1", - "frequenz-repo-config[lib] == 0.18.0", + "frequenz-repo-config[lib] == 0.19.0", ] build-backend = "setuptools.build_meta" @@ -80,7 +80,7 @@ dev-mkdocs = [ "mkdocs-macros-plugin == 1.5.0", "mkdocs-material == 9.7.7", "mkdocstrings[python] == 1.0.6", - "frequenz-repo-config[lib] == 0.18.0", + "frequenz-repo-config[lib] == 0.19.0", ] dev-mypy = [ "mypy == 2.3.1", @@ -91,7 +91,7 @@ dev-mypy = [ "types-protobuf == 7.35.1.20260827", "types-python-dateutil == 2.9.0.20260807", ] -dev-noxfile = ["nox == 2026.8.17", "frequenz-repo-config[lib] == 0.18.0"] +dev-noxfile = ["nox == 2026.8.17", "frequenz-repo-config[lib] == 0.19.0"] dev-pylint = [ "pylint == 4.0.8", # For checking the noxfile, docs/ script, and tests @@ -100,7 +100,7 @@ dev-pylint = [ ] dev-pytest = [ "pytest == 9.1.1", - "frequenz-repo-config[extra-lint-examples] == 0.18.0", + "frequenz-repo-config[extra-lint-examples] == 0.19.0", "pytest-mock == 3.15.1", "pytest-asyncio == 1.4.0", "async-solipsism == 0.9", From 778cd541c0a5aaa2fd8beb22a3f86e44ccccb131 Mon Sep 17 00:00:00 2001 From: "frequenz-auto-dependabot[bot]" <261417025+frequenz-auto-dependabot[bot]@users.noreply.github.com> Date: Fri, 4 Sep 2026 08:21:19 +0000 Subject: [PATCH 2/5] Apply migration from 0.18.0 to 0.19.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit === v0.19.0 ========================================================= Script URL: https://raw.githubusercontent.com/frequenz-floss/frequenz-repo-config-python/v0.19.0/cookiecutter/migrate.py ======================================================================== Enabling exhaustive match checks for mypy... Updated pyproject.toml: enabled exhaustive-match ======================================================================== ======================================================================== Removing default `-vv` from pytest addopts... Updated pyproject.toml: removed default `-vv` from [tool.pytest.ini_options] ======================================================================== Removing the dummy DCO merge-queue workflow... Removed .github/workflows/dco-merge-queue.yml ======================================================================== Pinning the DCO check in the 'Protect version branches' ruleset... Ruleset 'Protect version branches' is already up to date ======================================================================== ======================================================================== Updating pinned build dependencies... Skipped pyproject.toml: build dependencies already up to date ======================================================================== ======================================================================== Enabling asyncio debug mode for pytest... Updated pyproject.toml: enabled asyncio debug mode under [tool.pytest.ini_options] ======================================================================== ======================================================================== Updating the release instructions in CONTRIBUTING.md... Updated CONTRIBUTING.md: refreshed the release instructions ======================================================================== ✅ Migration script finished successfully ✅ The migration completed successfully. Applied-by: frequenz-floss/gh-action-dependabot-migrate --- .github/workflows/dco-merge-queue.yml | 14 -------------- CONTRIBUTING.md | 20 +++++++++++++++++--- pyproject.toml | 3 ++- 3 files changed, 19 insertions(+), 18 deletions(-) delete mode 100644 .github/workflows/dco-merge-queue.yml diff --git a/.github/workflows/dco-merge-queue.yml b/.github/workflows/dco-merge-queue.yml deleted file mode 100644 index 7a4260d..0000000 --- a/.github/workflows/dco-merge-queue.yml +++ /dev/null @@ -1,14 +0,0 @@ -# Based on https://github.com/hyperledger/besu/pull/5207/files -name: DCO -on: - merge_group: - -# Drop all token permissions: this workflow only runs a local echo command. -permissions: {} - -jobs: - DCO: - runs-on: ubuntu-slim - if: ${{ github.actor != 'dependabot[bot]' }} - steps: - - run: echo "This DCO job runs on merge_queue event and doesn't check PR contents" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 840dbac..7bc5567 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -53,7 +53,7 @@ Or you can use `nox`: nox -R -s pytest -- tests/test_*.py ``` -The same appliest to `pylint` or `mypy` for example: +The same applies to `pylint` or `mypy` for example: ```sh nox -R -s pylint -- tests/test_*.py @@ -121,12 +121,19 @@ These are the steps to create a new release: 1. Get the latest head you want to create a release from. + If you use milestones, check that the milestone for this release has no open + issues left, and move whatever won't make it to a later milestone. + 2. Update the `RELEASE_NOTES.md` file if it is not complete, up to date, and - remove template comments (``) and empty sections. Submit a pull request if an update is needed, wait until it is merged, and update the latest head you want to create a release from to get the new merged pull request. + This can't be left for later: the notes published with the release are read + from the `RELEASE_NOTES.md` committed at the tagged commit, not from the tag + message. + 3. Create a new signed tag using the release notes and a [semver](https://semver.org/) compatible version number with a `v` prefix, for example: @@ -135,6 +142,10 @@ These are the steps to create a new release: git tag -s --cleanup=whitespace -F RELEASE_NOTES.md v0.0.1 ``` + The signature is not optional: the *Protect released tags* ruleset rejects + unsigned tags. It also forbids updating or deleting a released tag, so make + sure you are tagging the commit you want. + 4. Push the new tag. 5. A GitHub action will test the tag and if all goes well it will create @@ -153,4 +164,7 @@ These are the steps to create a new release: Commit the new release notes and create a PR (this step should be automated eventually too). -7. Celebrate! +7. If you use milestones, close the milestone for this release, and create the + next one if it doesn't exist yet. + +8. Celebrate! diff --git a/pyproject.toml b/pyproject.toml index db0f566..4a4d19f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -185,13 +185,14 @@ filterwarnings = [ # chars as this is a regex 'ignore:Protobuf gencode version .*exactly one major version older.*:UserWarning', ] -addopts = "-vv" testpaths = ["tests", "src"] +asyncio_debug = true asyncio_mode = "auto" asyncio_default_fixture_loop_scope = "function" required_plugins = ["pytest-asyncio", "pytest-mock"] [tool.mypy] +enable_error_code = ["exhaustive-match"] explicit_package_bases = true namespace_packages = true # This option disables mypy cache, and it is sometimes useful to enable it if From 912a6777c02f1fa0ec9dda7a875cfb35140a10be Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Tue, 8 Sep 2026 12:28:47 +0000 Subject: [PATCH 3/5] Handle unset count_or_until in EndCriteria.from_protobuf The frequenz-repo-config 0.19.0 migration enabled mypy's exhaustive-match check, which flagged this match statement: it only handled the "count" and "until_time" cases of the count_or_until oneof, not the case where neither is set. When neither is set, `instance` is correctly left with its `count` and `until` defaults (both `None`), so add an explicit `case None` that does nothing, documenting that this is intentional rather than an oversight. Signed-off-by: Leandro Lucarella --- src/frequenz/client/dispatch/recurrence.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/frequenz/client/dispatch/recurrence.py b/src/frequenz/client/dispatch/recurrence.py index 2c62bd2..4a482d3 100644 --- a/src/frequenz/client/dispatch/recurrence.py +++ b/src/frequenz/client/dispatch/recurrence.py @@ -90,6 +90,10 @@ def from_protobuf(cls, pb_criteria: PBRecurrenceRule.EndCriteria) -> "EndCriteri instance.count = pb_criteria.count case "until_time": instance.until = to_datetime(pb_criteria.until_time) + case None: + # Neither `count` nor `until_time` is set, so this recurrence has no + # end criteria, and `instance` is left with its defaults. + pass return instance def to_protobuf(self) -> PBRecurrenceRule.EndCriteria: From ee1e619bd24e9f99c7e0b451579c0eabf1d7e8eb Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Tue, 8 Sep 2026 12:28:51 +0000 Subject: [PATCH 4/5] Handle typeless TargetCategory in _target_components_to_protobuf The frequenz-repo-config 0.19.0 migration enabled mypy's exhaustive-match check, which flagged this match statement: it only handled the three concrete subtypes of `category.type` (BatteryType, EvChargerType, InverterType), not the case where the category has no specific type at all. When `category.type` is `None`, the target is a plain `ComponentCategory` or `ElectricalComponentCategory` without a narrower type, and `pb_category.category`, set right above the match, is all that's needed. Add an explicit `case None` that does nothing, documenting that this is intentional rather than an oversight. Signed-off-by: Leandro Lucarella --- src/frequenz/client/dispatch/types.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/frequenz/client/dispatch/types.py b/src/frequenz/client/dispatch/types.py index af9a3c2..be95437 100644 --- a/src/frequenz/client/dispatch/types.py +++ b/src/frequenz/client/dispatch/types.py @@ -370,6 +370,11 @@ def _target_components_to_protobuf( pb_category.ev_charger = category.type.value case InverterType(): pb_category.inverter = category.type.value + case None: + # The category has no specific type (it's a plain + # `ComponentCategory` or `ElectricalComponentCategory`), so + # only `pb_category.category`, already set above, is needed. + pass case _: raise ValueError(f"Invalid target components: {target}") From 2807b30af8edc7ec772655aab45b4f21f6ee2ed5 Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Tue, 8 Sep 2026 12:28:56 +0000 Subject: [PATCH 5/5] FakeService: Handle unknown paths in update field mask The frequenz-repo-config 0.19.0 migration enabled mypy's exhaustive-match check, which flagged both match statements in UpdateMicrogridDispatch that switch on a path segment from the update mask: since these segments are arbitrary strings coming from `path.split(".")`, not a fixed set of literals, mypy can never treat a fixed list of `case` patterns as exhaustive over them. Both matches already left unrecognized paths silently unhandled, so add a `case _: pass` to each, documenting that this is intentional: an update mask may legitimately reference fields this fake service doesn't special-case, and those are ignored rather than erroring. Signed-off-by: Leandro Lucarella --- src/frequenz/client/dispatch/test/_service.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/frequenz/client/dispatch/test/_service.py b/src/frequenz/client/dispatch/test/_service.py index 6a57c9f..14fe86f 100644 --- a/src/frequenz/client/dispatch/test/_service.py +++ b/src/frequenz/client/dispatch/test/_service.py @@ -323,6 +323,17 @@ async def UpdateMicrogridDispatch( getattr(pb_dispatch.data.recurrence, split_path[1])[:] = ( getattr(request.update.recurrence, split_path[1])[:] ) + case _: + # `split_path[1]` is an arbitrary string, so mypy can + # never consider the cases above exhaustive. Paths that + # don't match a known recurrence field are ignored, same + # as unrecognized top-level paths below. + pass + case _: + # `split_path[0]` is an arbitrary string, so mypy can never + # consider the cases above exhaustive. Unrecognized top-level + # paths are ignored. + pass dispatch = Dispatch.from_protobuf(pb_dispatch) dispatch = replace(