From 53f2c09ccadc2e7b8ad25500f05fdcbb6167c303 Mon Sep 17 00:00:00 2001 From: yhaliaw Date: Fri, 22 May 2026 15:54:41 +0800 Subject: [PATCH 1/6] Add PPA for dotnet backport during image building --- .../github_runner_image_builder/templates/cloud-init.sh.j2 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/github_runner_image_builder/templates/cloud-init.sh.j2 b/app/src/github_runner_image_builder/templates/cloud-init.sh.j2 index 2b4ebcfa..389e47dd 100644 --- a/app/src/github_runner_image_builder/templates/cloud-init.sh.j2 +++ b/app/src/github_runner_image_builder/templates/cloud-init.sh.j2 @@ -65,7 +65,9 @@ function install_apt_packages() { chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" > /etc/apt/sources.list.d/github-cli.list fi - + + echo "Adding dotnet backports PPA" + DEBIAN_FRONTEND=noninteractive /usr/bin/add-apt-repository -y ppa:dotnet/backports echo "Updating apt packages" DEBIAN_FRONTEND=noninteractive /usr/bin/apt-get update -y echo "Upgrading apt packages" From 7976709398347b21eb3582903d468f4322513bf6 Mon Sep 17 00:00:00 2001 From: yhaliaw Date: Fri, 22 May 2026 16:39:03 +0800 Subject: [PATCH 2/6] Fix unit tests --- app/src/github_runner_image_builder/templates/cloud-init.sh.j2 | 2 +- app/tests/unit/test_openstack_builder.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/github_runner_image_builder/templates/cloud-init.sh.j2 b/app/src/github_runner_image_builder/templates/cloud-init.sh.j2 index 389e47dd..6c399299 100644 --- a/app/src/github_runner_image_builder/templates/cloud-init.sh.j2 +++ b/app/src/github_runner_image_builder/templates/cloud-init.sh.j2 @@ -65,7 +65,7 @@ function install_apt_packages() { chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" > /etc/apt/sources.list.d/github-cli.list fi - + echo "Adding dotnet backports PPA" DEBIAN_FRONTEND=noninteractive /usr/bin/add-apt-repository -y ppa:dotnet/backports echo "Updating apt packages" diff --git a/app/tests/unit/test_openstack_builder.py b/app/tests/unit/test_openstack_builder.py index 97350053..57594415 100644 --- a/app/tests/unit/test_openstack_builder.py +++ b/app/tests/unit/test_openstack_builder.py @@ -777,6 +777,8 @@ def test__generate_cloud_init_script( main" > /etc/apt/sources.list.d/github-cli.list fi + echo "Adding dotnet backports PPA" + DEBIAN_FRONTEND=noninteractive /usr/bin/add-apt-repository -y ppa:dotnet/backports echo "Updating apt packages" DEBIAN_FRONTEND=noninteractive /usr/bin/apt-get update -y echo "Upgrading apt packages" From 3671dbd21f68d6f1c0228e2dbfaf52cf7241fa1a Mon Sep 17 00:00:00 2001 From: yhaliaw Date: Fri, 22 May 2026 16:44:11 +0800 Subject: [PATCH 3/6] Update the version number --- app/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/pyproject.toml b/app/pyproject.toml index 1deff3ce..01406142 100644 --- a/app/pyproject.toml +++ b/app/pyproject.toml @@ -3,7 +3,7 @@ [project] name = "github-runner-image-builder" -version = "0.13.0" +version = "0.14.0" authors = [ { name = "Canonical IS DevOps", email = "is-devops-team@canonical.com" }, ] From 15e21b41eb1023c7a23a1cdd50bebe958db64d03 Mon Sep 17 00:00:00 2001 From: yhaliaw Date: Fri, 22 May 2026 16:47:02 +0800 Subject: [PATCH 4/6] Add version number --- docs/changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index ef6c8a0c..cb9a7eed 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ +## [#222 Add PPA for dotnet backport during image building](https://github.com/canonical/github-runner-image-builder-operator/pull/222) (2026-05-22) + +- Add dotnet PPA in image building. This allows wider range of dotnet version to be installed. + ## [#219 Use Juju secrets](https://github.com/canonical/github-runner-image-builder-operator/pull/219) (2026-04-17) - Require `openstack-password-secret` configuration option to securely store OpenStack passwords using Juju secrets. From a5294e063700bfdef83e0145086e8ecaa322f5ad Mon Sep 17 00:00:00 2001 From: yhaliaw Date: Fri, 22 May 2026 16:51:47 +0800 Subject: [PATCH 5/6] Fix spelling --- docs/changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index cb9a7eed..0c85acc9 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,8 +1,8 @@ -## [#222 Add PPA for dotnet backport during image building](https://github.com/canonical/github-runner-image-builder-operator/pull/222) (2026-05-22) +## [#222 Add PPA for .NET backport during image building](https://github.com/canonical/github-runner-image-builder-operator/pull/222) (2026-05-22) -- Add dotnet PPA in image building. This allows wider range of dotnet version to be installed. +- Add .NET PPA in image building. This allows wider range of .NET version to be installed. ## [#219 Use Juju secrets](https://github.com/canonical/github-runner-image-builder-operator/pull/219) (2026-04-17) From 752d4162dee9e8c5b0003059418a79de5df5b66e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 22 May 2026 08:55:34 +0000 Subject: [PATCH 6/6] chore: sync grafana cos_agent charm library to latest patch Agent-Logs-Url: https://github.com/canonical/github-runner-image-builder-operator/sessions/02d4c4d6-96ed-47eb-b259-25a46b167b20 Co-authored-by: yhaliaw <43424755+yhaliaw@users.noreply.github.com> --- lib/charms/grafana_agent/v0/cos_agent.py | 45 ++++++++++++++++++------ 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/lib/charms/grafana_agent/v0/cos_agent.py b/lib/charms/grafana_agent/v0/cos_agent.py index 228550af..d3944207 100644 --- a/lib/charms/grafana_agent/v0/cos_agent.py +++ b/lib/charms/grafana_agent/v0/cos_agent.py @@ -211,7 +211,9 @@ def __init__(self, *args): ``` """ +import copy import enum +import hashlib import json import logging import socket @@ -254,7 +256,7 @@ class _MetricsEndpointDict(TypedDict): LIBID = "dc15fa84cef84ce58155fb84f6c6213a" LIBAPI = 0 -LIBPATCH = 24 +LIBPATCH = 25 PYDEPS = ["cosl >= 0.0.50", "pydantic"] @@ -308,6 +310,13 @@ def _dedupe_list(items: List[Dict[str, Any]]) -> List[Dict[str, Any]]: return unique_items +def _dict_hash_except_key(scrape_config: Dict[str, Any], key: Optional[str]): + """Get a hash of the scrape_config dict, except for the specified key.""" + cfg_for_hash = {k: v for k, v in scrape_config.items() if k != key} + serialized = json.dumps(cfg_for_hash, sort_keys=True) + return hashlib.blake2b(serialized.encode(), digest_size=4).hexdigest() + + class TracingError(Exception): """Base class for custom errors raised by tracing.""" @@ -697,6 +706,27 @@ def _on_refresh(self, event): ) as e: logger.error("Invalid relation data provided: %s", e) + def _deterministic_scrape_configs( + self, scrape_configs: List[Dict[str, Any]] + ) -> List[Dict[str, Any]]: + """Get deterministic scrape_configs with stable job names. + + For stability across serializations, compute a short per-config hash + and append it to the existing job name (or 'default'). Keep the app + name as a prefix: __<8hex-hash>. + + Hash the whole scrape_config (except any existing job_name) so the + suffix is sensitive to all stable fields. Use deterministic JSON + serialization. + """ + local_scrape_configs = copy.deepcopy(scrape_configs) + for scrape_config in local_scrape_configs: + name = scrape_config.get("job_name", "default") + short_id = _dict_hash_except_key(scrape_config, "job_name") + scrape_config["job_name"] = f"{self._charm.app.name}_{name}_{short_id}" + + return sorted(local_scrape_configs, key=lambda c: c.get("job_name", "")) + @property def _scrape_jobs(self) -> List[Dict]: """Return a list of scrape_configs. @@ -711,22 +741,17 @@ def _scrape_jobs(self) -> List[Dict]: scrape_configs = self._scrape_configs.copy() # Convert "metrics_endpoints" to standard scrape_configs, and add them in - unit_name = self._charm.unit.name.replace("/", "_") for endpoint in self._metrics_endpoints: - port = endpoint["port"] - path = endpoint["path"] - sanitized_path = path.strip("/").replace("/", "_") scrape_configs.append( { - "job_name": f"{unit_name}_localhost_{port}_{sanitized_path}", - "metrics_path": path, - "static_configs": [{"targets": [f"localhost:{port}"]}], + "metrics_path": endpoint["path"], + "static_configs": [{"targets": [f"localhost:{endpoint['port']}"]}], } ) scrape_configs = scrape_configs or [] - return scrape_configs + return self._deterministic_scrape_configs(scrape_configs) @property def _metrics_alert_rules(self) -> Dict: @@ -742,7 +767,7 @@ def _metrics_alert_rules(self) -> Dict: ) alert_rules.add_path(self._metrics_rules, recursive=self._recursive) alert_rules.add( - generic_alert_groups.application_rules, + copy.deepcopy(generic_alert_groups.application_rules), group_name_prefix=JujuTopology.from_charm(self._charm).identifier, )