Skip to content

Commit ce994a3

Browse files
authored
Add black auto-migration workflow (#556)
This workflow runs black on all files on major black updates, so if there is any changes in formatting, they are applied automatically.
2 parents fb63f2b + 5800adf commit ce994a3

17 files changed

Lines changed: 805 additions & 13 deletions

File tree

.github/workflows/auto-dependabot.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ permissions:
2020
jobs:
2121
auto-merge:
2222
name: Auto-merge Dependabot PR
23-
if: github.actor == 'dependabot[bot]'
23+
if: |
24+
github.actor == 'dependabot[bot]' &&
25+
!contains(github.event.pull_request.title, 'Bump black from ')
2426
runs-on: ubuntu-slim
2527
steps:
2628
- name: Generate GitHub App token
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# Automatic black formatting migration for Dependabot PRs
2+
#
3+
# When Dependabot upgrades black, this workflow installs the new version
4+
# and runs `black .` so the PR already contains any formatting changes
5+
# introduced by the upgrade, while leaving the PR open for review.
6+
#
7+
# Black uses calendar versioning. Only the first release of a new calendar
8+
# year may introduce formatting changes (major bump in Dependabot's terms).
9+
# Minor and patch updates within a year keep formatting stable, so they stay
10+
# in the regular Dependabot groups and are auto-merged normally.
11+
#
12+
# The companion auto-dependabot workflow skips major black PRs so they're
13+
# handled exclusively by this migration workflow.
14+
#
15+
# XXX: !!! SECURITY WARNING !!!
16+
# pull_request_target has write access to the repo, and can read secrets.
17+
# This is required because Dependabot PRs are treated as fork PRs: the
18+
# GITHUB_TOKEN is read-only and secrets are unavailable with a plain
19+
# pull_request trigger. The action mitigates the risk by:
20+
# - Never executing code from the PR (the migration script is embedded
21+
# in this workflow file on the base branch, not taken from the PR).
22+
# - Gating migration steps on github.actor == 'dependabot[bot]'.
23+
# - Running checkout with persist-credentials: false and isolating
24+
# push credentials from the migration script environment.
25+
# For more details read:
26+
# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
27+
28+
name: Black Migration
29+
30+
on:
31+
merge_group: # To allow using this as a required check for merging
32+
pull_request_target:
33+
types: [opened, synchronize, reopened, labeled, unlabeled]
34+
35+
permissions:
36+
# Commit reformatted files back to the PR branch.
37+
contents: write
38+
# Create and normalize migration state labels.
39+
issues: write
40+
# Read/update pull request metadata and comments.
41+
pull-requests: write
42+
43+
jobs:
44+
black-migration:
45+
name: Migrate Black
46+
# Skip if it was triggered by the merge queue. We only need the workflow to
47+
# be executed to meet the "Required check" condition for merging, but we
48+
# don't need to actually run the job, having the job present as Skipped is
49+
# enough.
50+
if: |
51+
github.event_name == 'pull_request_target' &&
52+
github.actor == 'dependabot[bot]' &&
53+
contains(github.event.pull_request.title, 'Bump black from ')
54+
runs-on: ubuntu-24.04
55+
steps:
56+
- name: Generate token
57+
id: create-app-token
58+
uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0
59+
with:
60+
app-id: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_ID }}
61+
private-key: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_PRIVATE_KEY }}
62+
# Push reformatted files to the PR branch.
63+
permission-contents: write
64+
# Create and normalize migration state labels.
65+
permission-issues: write
66+
# Read/update pull request metadata and labels.
67+
permission-pull-requests: write
68+
- name: Migrate
69+
uses: frequenz-floss/gh-action-dependabot-migrate@b389f72f9282346920150a67495efbae450ac07b # v1.1.0
70+
with:
71+
migration-script: |
72+
import os
73+
import subprocess
74+
import sys
75+
76+
version = os.environ["MIGRATION_VERSION"].lstrip("v")
77+
subprocess.run(
78+
[sys.executable, "-Im", "pip", "install", f"black=={version}"],
79+
check=True,
80+
)
81+
subprocess.run([sys.executable, "-Im", "black", "."], check=True)
82+
token: ${{ steps.create-app-token.outputs.token }}
83+
auto-merge-on-changes: "false"
84+
sign-commits: "true"
85+
auto-merged-label: "tool:auto-merged"
86+
migrated-label: "tool:black:migration:executed"
87+
intervention-pending-label: "tool:black:migration:intervention-pending"
88+
intervention-done-label: "tool:black:migration:intervention-done"

RELEASE_NOTES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ But you might still need to adapt your code:
2626

2727
### Cookiecutter template
2828

29-
<!-- Here new features for cookiecutter specifically -->
29+
- Add a `black-migration.yaml` workflow that automatically reformats code when Dependabot upgrades `black`.
3030

3131
## Bug Fixes
3232

cookiecutter/migrate.py

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,102 @@
3737
_manual_steps: list[str] = [] # pylint: disable=invalid-name
3838

3939

40+
_BLACK_MIGRATION_WORKFLOW = (
41+
"""\
42+
# Automatic black formatting migration for Dependabot PRs
43+
#
44+
# When Dependabot upgrades black, this workflow installs the new version
45+
# and runs `black .` so the PR already contains any formatting changes
46+
# introduced by the upgrade, while leaving the PR open for review.
47+
#
48+
# Black uses calendar versioning. Only the first release of a new calendar
49+
# year may introduce formatting changes (major bump in Dependabot's terms).
50+
# Minor and patch updates within a year keep formatting stable, so they stay
51+
# in the regular Dependabot groups and are auto-merged normally.
52+
#
53+
# The companion auto-dependabot workflow skips major black PRs so they're
54+
# handled exclusively by this migration workflow.
55+
#
56+
# XXX: !!! SECURITY WARNING !!!
57+
# pull_request_target has write access to the repo, and can read secrets.
58+
# This is required because Dependabot PRs are treated as fork PRs: the
59+
# GITHUB_TOKEN is read-only and secrets are unavailable with a plain
60+
# pull_request trigger. The action mitigates the risk by:
61+
# - Never executing code from the PR (the migration script is embedded
62+
# in this workflow file on the base branch, not taken from the PR).
63+
# - Gating migration steps on github.actor == 'dependabot[bot]'.
64+
# - Running checkout with persist-credentials: false and isolating
65+
# push credentials from the migration script environment.
66+
# For more details read:
67+
# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
68+
69+
name: Black Migration
70+
71+
on:
72+
merge_group: # To allow using this as a required check for merging
73+
pull_request_target:
74+
types: [opened, synchronize, reopened, labeled, unlabeled]
75+
76+
permissions:
77+
# Commit reformatted files back to the PR branch.
78+
contents: write
79+
# Create and normalize migration state labels.
80+
issues: write
81+
# Read/update pull request metadata and comments.
82+
pull-requests: write
83+
84+
jobs:
85+
black-migration:
86+
name: Migrate Black
87+
# Skip if it was triggered by the merge queue. We only need the workflow to
88+
# be executed to meet the "Required check" condition for merging, but we
89+
# don't need to actually run the job, having the job present as Skipped is
90+
# enough.
91+
if: |
92+
github.event_name == 'pull_request_target' &&
93+
github.actor == 'dependabot[bot]' &&
94+
contains(github.event.pull_request.title, 'Bump black from ')
95+
runs-on: ubuntu-24.04
96+
steps:
97+
- name: Generate token
98+
id: create-app-token
99+
uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0
100+
with:
101+
app-id: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_ID }}
102+
private-key: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_PRIVATE_KEY }}
103+
# Push reformatted files to the PR branch.
104+
permission-contents: write
105+
# Create and normalize migration state labels.
106+
permission-issues: write
107+
# Read/update pull request metadata and labels.
108+
permission-pull-requests: write
109+
- name: Migrate
110+
uses: frequenz-floss/gh-action-dependabot-migrate@"""
111+
# Broken just to avoid flake8 maximum line length check
112+
"""b389f72f9282346920150a67495efbae450ac07b # v1.1.0"
113+
with:
114+
migration-script: |
115+
import os
116+
import subprocess
117+
import sys
118+
119+
version = os.environ["MIGRATION_VERSION"].lstrip("v")
120+
subprocess.run(
121+
[sys.executable, "-Im", "pip", "install", f"black=={version}"],
122+
check=True,
123+
)
124+
subprocess.run([sys.executable, "-Im", "black", "."], check=True)
125+
token: ${{ steps.create-app-token.outputs.token }}
126+
auto-merge-on-changes: "false"
127+
sign-commits: "true"
128+
auto-merged-label: "tool:auto-merged"
129+
migrated-label: "tool:black:migration:executed"
130+
intervention-pending-label: "tool:black:migration:intervention-pending"
131+
intervention-done-label: "tool:black:migration:intervention-done"
132+
"""
133+
)
134+
135+
40136
_RELEASE_NOTES_CHECK_REPLACEMENTS = [
41137
(
42138
" permissions:\n pull-requests: read\n",
@@ -65,6 +161,9 @@ def main() -> None:
65161
print("Updating generated Dependabot workflows...")
66162
migrate_dependabot_workflows()
67163
print("=" * 72)
164+
print("Creating black migration workflow...")
165+
_migrate_black_migration_workflow()
166+
print("=" * 72)
68167
print("Updating auxiliary GitHub workflows...")
69168
migrate_auxiliary_workflows()
70169
print("=" * 72)
@@ -279,6 +378,16 @@ def migrate_dependabot_workflows() -> None:
279378
" # Approve PRs, add labels, and enable auto-merge.\n"
280379
" permission-pull-requests: write\n",
281380
),
381+
(
382+
" !contains(github.event.pull_request.title, "
383+
"'the repo-config group')\n"
384+
" runs-on:",
385+
" !contains(github.event.pull_request.title, "
386+
"'the repo-config group') &&\n"
387+
" !contains(github.event.pull_request.title, "
388+
"'Bump black from ')\n"
389+
" runs-on:",
390+
),
282391
],
283392
description="updated Dependabot auto-merge workflow",
284393
)
@@ -358,6 +467,14 @@ def migrate_auxiliary_workflows() -> None:
358467
)
359468

360469

470+
def _migrate_black_migration_workflow() -> None:
471+
"""Create or replace the black formatting migration workflow."""
472+
filepath = Path(".github/workflows/black-migration.yaml")
473+
action = "Updated" if filepath.exists() else "Created"
474+
replace_file_atomically(filepath, _BLACK_MIGRATION_WORKFLOW)
475+
print(f" {action} {filepath}: black formatting migration workflow")
476+
477+
361478
_WORKFLOW_ACTION_PINS: dict[str, str] = {
362479
"frequenz-floss/gh-action-setup-git": (
363480
"16952aac3ccc01d27412fe0dea3ea946530dcace # v1.0.0"

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ jobs:
2323
name: Auto-merge Dependabot PR
2424
if: |
2525
github.actor == 'dependabot[bot]' &&
26-
!contains(github.event.pull_request.title, 'the repo-config group')
26+
!contains(github.event.pull_request.title, 'the repo-config group') &&
27+
!contains(github.event.pull_request.title, 'Bump black from ')
2728
runs-on: ubuntu-slim
2829
steps:
2930
- name: Generate GitHub App token
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
{% raw -%}
2+
# Automatic black formatting migration for Dependabot PRs
3+
#
4+
# When Dependabot upgrades black, this workflow installs the new version
5+
# and runs `black .` so the PR already contains any formatting changes
6+
# introduced by the upgrade, while leaving the PR open for review.
7+
#
8+
# Black uses calendar versioning. Only the first release of a new calendar
9+
# year may introduce formatting changes (major bump in Dependabot's terms).
10+
# Minor and patch updates within a year keep formatting stable, so they stay
11+
# in the regular Dependabot groups and are auto-merged normally.
12+
#
13+
# The companion auto-dependabot workflow skips major black PRs so they're
14+
# handled exclusively by this migration workflow.
15+
#
16+
# XXX: !!! SECURITY WARNING !!!
17+
# pull_request_target has write access to the repo, and can read secrets.
18+
# This is required because Dependabot PRs are treated as fork PRs: the
19+
# GITHUB_TOKEN is read-only and secrets are unavailable with a plain
20+
# pull_request trigger. The action mitigates the risk by:
21+
# - Never executing code from the PR (the migration script is embedded
22+
# in this workflow file on the base branch, not taken from the PR).
23+
# - Gating migration steps on github.actor == 'dependabot[bot]'.
24+
# - Running checkout with persist-credentials: false and isolating
25+
# push credentials from the migration script environment.
26+
# For more details read:
27+
# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
28+
29+
name: Black Migration
30+
31+
on:
32+
merge_group: # To allow using this as a required check for merging
33+
pull_request_target:
34+
types: [opened, synchronize, reopened, labeled, unlabeled]
35+
36+
permissions:
37+
# Commit reformatted files back to the PR branch.
38+
contents: write
39+
# Create and normalize migration state labels.
40+
issues: write
41+
# Read/update pull request metadata and comments.
42+
pull-requests: write
43+
44+
jobs:
45+
black-migration:
46+
name: Migrate Black
47+
# Skip if it was triggered by the merge queue. We only need the workflow to
48+
# be executed to meet the "Required check" condition for merging, but we
49+
# don't need to actually run the job, having the job present as Skipped is
50+
# enough.
51+
if: |
52+
github.event_name == 'pull_request_target' &&
53+
github.actor == 'dependabot[bot]' &&
54+
contains(github.event.pull_request.title, 'Bump black from ')
55+
runs-on: ubuntu-24.04
56+
steps:
57+
- name: Generate token
58+
id: create-app-token
59+
uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0
60+
with:
61+
app-id: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_ID }}
62+
private-key: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_PRIVATE_KEY }}
63+
# Push reformatted files to the PR branch.
64+
permission-contents: write
65+
# Create and normalize migration state labels.
66+
permission-issues: write
67+
# Read/update pull request metadata and labels.
68+
permission-pull-requests: write
69+
- name: Migrate
70+
uses: frequenz-floss/gh-action-dependabot-migrate@b389f72f9282346920150a67495efbae450ac07b # v1.1.0
71+
with:
72+
migration-script: |
73+
import os
74+
import subprocess
75+
import sys
76+
77+
version = os.environ["MIGRATION_VERSION"].lstrip("v")
78+
subprocess.run(
79+
[sys.executable, "-Im", "pip", "install", f"black=={version}"],
80+
check=True,
81+
)
82+
subprocess.run([sys.executable, "-Im", "black", "."], check=True)
83+
token: ${{ steps.create-app-token.outputs.token }}
84+
auto-merge-on-changes: "false"
85+
sign-commits: "true"
86+
auto-merged-label: "tool:auto-merged"
87+
migrated-label: "tool:black:migration:executed"
88+
intervention-pending-label: "tool:black:migration:intervention-pending"
89+
intervention-done-label: "tool:black:migration:intervention-done"
90+
{%- endraw %}

0 commit comments

Comments
 (0)