Skip to content

Commit b530a64

Browse files
authored
Use ubuntu-slim for lightweight jobs (#496)
Fixes #492.
2 parents 9587b9f + bfeebf5 commit b530a64

40 files changed

Lines changed: 277 additions & 63 deletions

.github/cookiecutter-migrate.template.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
""" # noqa: E501
2222

2323
import hashlib
24+
import json
2425
import os
2526
import subprocess
2627
import tempfile
@@ -36,6 +37,28 @@ def main() -> None:
3637
print("=" * 72)
3738

3839

40+
def read_project_type() -> str | None:
41+
"""Read the cookiecutter project type from the replay file."""
42+
replay_path = Path(".cookiecutter-replay.json")
43+
if not replay_path.exists():
44+
return None
45+
46+
try:
47+
data = json.loads(replay_path.read_text(encoding="utf-8"))
48+
except (json.JSONDecodeError, OSError):
49+
return None
50+
51+
cookiecutter_data = data.get("cookiecutter")
52+
if not isinstance(cookiecutter_data, dict):
53+
return None
54+
55+
project_type = cookiecutter_data.get("type")
56+
if not isinstance(project_type, str):
57+
return None
58+
59+
return project_type
60+
61+
3962
def apply_patch(patch_content: str) -> None:
4063
"""Apply a patch using the patch utility."""
4164
subprocess.run(["patch", "-p1"], input=patch_content.encode(), check=True)

.github/workflows/auto-dependabot.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ permissions:
1010
jobs:
1111
auto-merge:
1212
if: github.actor == 'dependabot[bot]'
13-
runs-on: ubuntu-latest
13+
runs-on: ubuntu-slim
1414
steps:
1515
- name: Auto-merge Dependabot PR
1616
uses: frequenz-floss/dependabot-auto-approve@3cad5f42e79296505473325ac6636be897c8b8a1 # v1.3.2

.github/workflows/ci.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ jobs:
6060
needs: ["nox"]
6161
# We skip this job only if nox was also skipped
6262
if: always() && needs.nox.result != 'skipped'
63-
runs-on: ubuntu-24.04
63+
runs-on: ubuntu-slim
6464
env:
6565
DEPS_RESULT: ${{ needs.nox.result }}
6666
steps:
@@ -161,7 +161,7 @@ jobs:
161161
needs: ["test-installation"]
162162
# We skip this job only if test-installation was also skipped
163163
if: always() && needs.test-installation.result != 'skipped'
164-
runs-on: ubuntu-24.04
164+
runs-on: ubuntu-slim
165165
env:
166166
DEPS_RESULT: ${{ needs.test-installation.result }}
167167
steps:
@@ -276,7 +276,7 @@ jobs:
276276
# discussions to create the release announcement in the discussion forums
277277
contents: write
278278
discussions: write
279-
runs-on: ubuntu-24.04
279+
runs-on: ubuntu-slim
280280
steps:
281281
- name: Download distribution files
282282
uses: actions/download-artifact@v6
@@ -318,7 +318,7 @@ jobs:
318318
publish-to-pypi:
319319
name: Publish packages to PyPI
320320
needs: ["create-github-release"]
321-
runs-on: ubuntu-24.04
321+
runs-on: ubuntu-slim
322322
permissions:
323323
# For trusted publishing. See:
324324
# https://blog.pypi.org/posts/2023-04-20-introducing-trusted-publishers/

.github/workflows/dco-merge-queue.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ on:
55

66
jobs:
77
DCO:
8-
runs-on: ubuntu-latest
8+
runs-on: ubuntu-slim
99
if: ${{ github.actor != 'dependabot[bot]' }}
1010
steps:
1111
- run: echo "This DCO job runs on merge_queue event and doesn't check PR contents"

.github/workflows/labeler.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ jobs:
77
permissions:
88
contents: read
99
pull-requests: write
10-
runs-on: ubuntu-latest
10+
runs-on: ubuntu-slim
1111
steps:
1212
- name: Labeler
1313
# XXX: !!! SECURITY WARNING !!!

.github/workflows/release-notes-check.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ on:
1616
jobs:
1717
check-release-notes:
1818
name: Check release notes are updated
19-
runs-on: ubuntu-latest
19+
runs-on: ubuntu-slim
2020
permissions:
2121
pull-requests: read
2222
steps:

RELEASE_NOTES.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Summary
44

5-
<!-- Here goes a general summary of what this release is about -->
5+
This release migrates lightweight GitHub Actions workflow jobs to use the new cost-effective `ubuntu-slim` runner.
66

77
## Upgrading
88

@@ -13,7 +13,7 @@
1313
All upgrading should be done via the migration script or regenerating the templates.
1414

1515
```bash
16-
curl -sSL https://raw.githubusercontent.com/frequenz-floss/frequenz-repo-config-python/v0.12/cookiecutter/migrate.py | python3
16+
curl -sSL https://raw.githubusercontent.com/frequenz-floss/frequenz-repo-config-python/v0.14.0/cookiecutter/migrate.py | python3
1717
```
1818

1919
But you might still need to adapt your code:
@@ -26,7 +26,14 @@ But you might still need to adapt your code:
2626

2727
### Cookiecutter template
2828

29-
<!-- Here new features for cookiecutter specifically -->
29+
- Migrated lightweight workflow jobs to use the new `ubuntu-slim` runner for cost savings.
30+
The following jobs now use `ubuntu-slim`:
31+
- `ci.yaml`: `protolint`, `nox-all`, `test-installation-all`, `create-github-release`, `publish-to-pypi`
32+
- `ci-pr.yaml`: `protolint`
33+
- `auto-dependabot.yaml`: `auto-merge`
34+
- `release-notes-check.yml`: `check-release-notes`
35+
- `dco-merge-queue.yml`: `DCO`
36+
- `labeler.yml`: `Label`
3037

3138
## Bug Fixes
3239

cookiecutter/migrate.py

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
""" # noqa: E501
2222

2323
import hashlib
24+
import json
2425
import os
2526
import subprocess
2627
import tempfile
@@ -32,10 +33,193 @@ def main() -> None:
3233
"""Run the migration steps."""
3334
# Add a separation line like this one after each migration step.
3435
print("=" * 72)
36+
print("Migrating workflows to use ubuntu-slim runner for lightweight jobs...")
37+
migrate_to_ubuntu_slim()
38+
print("=" * 72)
3539
print("Migration script finished. Remember to follow any manual instructions.")
3640
print("=" * 72)
3741

3842

43+
def migrate_to_ubuntu_slim() -> None:
44+
"""Migrate workflow files to use ubuntu-slim runner for lightweight jobs.
45+
46+
This updates several workflow files to use the new cost-effective ubuntu-slim
47+
runner for jobs that are lightweight (e.g., labeling, release notes checks,
48+
simple API calls).
49+
"""
50+
workflows_dir = Path(".github") / "workflows"
51+
project_type = read_project_type()
52+
include_protolint = project_type == "api"
53+
if project_type is None:
54+
include_protolint = True
55+
manual_step(
56+
"Unable to detect the cookiecutter project type from "
57+
".cookiecutter-replay.json; protolint migrations will run anyway. "
58+
"Please verify any protolint jobs and keep them only if this is an api "
59+
"project."
60+
)
61+
62+
migrations = {
63+
"ci.yaml": [
64+
{
65+
"job": "nox-all",
66+
"old": (
67+
" if: always() && needs.nox.result != 'skipped'\n"
68+
" runs-on: ubuntu-24.04"
69+
),
70+
"new": (
71+
" if: always() && needs.nox.result != 'skipped'\n"
72+
" runs-on: ubuntu-slim"
73+
),
74+
},
75+
{
76+
"job": "test-installation-all",
77+
"old": (
78+
" if: always() && needs.test-installation.result != 'skipped'\n"
79+
" runs-on: ubuntu-24.04"
80+
),
81+
"new": (
82+
" if: always() && needs.test-installation.result != 'skipped'\n"
83+
" runs-on: ubuntu-slim"
84+
),
85+
},
86+
{
87+
"job": "create-github-release",
88+
"old": " discussions: write\n runs-on: ubuntu-24.04",
89+
"new": " discussions: write\n runs-on: ubuntu-slim",
90+
},
91+
{
92+
"job": "publish-to-pypi",
93+
"old": ' needs: ["create-github-release"]\n runs-on: ubuntu-24.04',
94+
"new": ' needs: ["create-github-release"]\n runs-on: ubuntu-slim',
95+
},
96+
],
97+
"auto-dependabot.yaml": [
98+
{
99+
"job": "auto-merge",
100+
"old": (
101+
" auto-merge:\n"
102+
" if: github.actor == 'dependabot[bot]'\n"
103+
" runs-on: ubuntu-latest"
104+
),
105+
"new": (
106+
" auto-merge:\n"
107+
" if: github.actor == 'dependabot[bot]'\n"
108+
" runs-on: ubuntu-slim"
109+
),
110+
}
111+
],
112+
"release-notes-check.yml": [
113+
{
114+
"job": "check-release-notes",
115+
"old": (
116+
" check-release-notes:\n"
117+
" name: Check release notes are updated\n"
118+
" runs-on: ubuntu-latest"
119+
),
120+
"new": (
121+
" check-release-notes:\n"
122+
" name: Check release notes are updated\n"
123+
" runs-on: ubuntu-slim"
124+
),
125+
}
126+
],
127+
"dco-merge-queue.yml": [
128+
{
129+
"job": "DCO",
130+
"old": "jobs:\n DCO:\n runs-on: ubuntu-latest",
131+
"new": "jobs:\n DCO:\n runs-on: ubuntu-slim",
132+
}
133+
],
134+
"labeler.yml": [
135+
{
136+
"job": "Label",
137+
"old": (
138+
" Label:\n"
139+
" permissions:\n"
140+
" contents: read\n"
141+
" pull-requests: write\n"
142+
" runs-on: ubuntu-latest"
143+
),
144+
"new": (
145+
" Label:\n"
146+
" permissions:\n"
147+
" contents: read\n"
148+
" pull-requests: write\n"
149+
" runs-on: ubuntu-slim"
150+
),
151+
}
152+
],
153+
}
154+
if include_protolint:
155+
protolint_rule = {
156+
"job": "protolint",
157+
"old": (
158+
" protolint:\n"
159+
" name: Check proto files with protolint\n"
160+
" runs-on: ubuntu-24.04"
161+
),
162+
"new": (
163+
" protolint:\n"
164+
" name: Check proto files with protolint\n"
165+
" runs-on: ubuntu-slim"
166+
),
167+
}
168+
migrations.setdefault("ci-pr.yaml", []).append(protolint_rule)
169+
migrations.setdefault("ci.yaml", []).append(protolint_rule)
170+
171+
for filename, rules in migrations.items():
172+
filepath = workflows_dir / filename
173+
if not filepath.exists():
174+
print(f" Skipping {filepath} (file not found)")
175+
continue
176+
177+
for rule in rules:
178+
job = rule["job"]
179+
old = rule["old"]
180+
new = rule["new"]
181+
try:
182+
content = filepath.read_text(encoding="utf-8")
183+
except FileNotFoundError:
184+
continue
185+
186+
if old in content:
187+
replace_file_contents_atomically(filepath, old, new)
188+
print(f" Updated {filepath}: migrated job {job} to ubuntu-slim")
189+
continue
190+
191+
if new in content:
192+
print(f" Skipped {filepath}: already uses ubuntu-slim for job {job}")
193+
continue
194+
195+
manual_step(
196+
f" Pattern not found in {filepath}: please switch job {job} to use "
197+
"`runs-on: ubuntu-slim` where appropriate."
198+
)
199+
200+
201+
def read_project_type() -> str | None:
202+
"""Read the cookiecutter project type from the replay file."""
203+
replay_path = Path(".cookiecutter-replay.json")
204+
if not replay_path.exists():
205+
return None
206+
207+
try:
208+
data = json.loads(replay_path.read_text(encoding="utf-8"))
209+
except (json.JSONDecodeError, OSError):
210+
return None
211+
212+
cookiecutter_data = data.get("cookiecutter")
213+
if not isinstance(cookiecutter_data, dict):
214+
return None
215+
216+
project_type = cookiecutter_data.get("type")
217+
if not isinstance(project_type, str):
218+
return None
219+
220+
return project_type
221+
222+
39223
def apply_patch(patch_content: str) -> None:
40224
"""Apply a patch using the patch utility."""
41225
subprocess.run(["patch", "-p1"], input=patch_content.encode(), check=True)

cookiecutter/{{cookiecutter.github_repo_name}}/.github/workflows/auto-dependabot.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ permissions:
1010
jobs:
1111
auto-merge:
1212
if: github.actor == 'dependabot[bot]'
13-
runs-on: ubuntu-latest
13+
runs-on: ubuntu-slim
1414
steps:
1515
- name: Auto-merge Dependabot PR
1616
uses: frequenz-floss/dependabot-auto-approve@3cad5f42e79296505473325ac6636be897c8b8a1 # v1.3.2

cookiecutter/{{cookiecutter.github_repo_name}}/.github/workflows/ci-pr.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
{% endraw %}{% if cookiecutter.type == "api" %}{% raw -%}
1616
protolint:
1717
name: Check proto files with protolint
18-
runs-on: ubuntu-24.04
18+
runs-on: ubuntu-slim
1919

2020
steps:
2121
- name: Setup Git

0 commit comments

Comments
 (0)