From 264fcffb91eb2dced884236b8c872ec5eb036a52 Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Wed, 11 Feb 2026 18:53:13 +0000 Subject: [PATCH 1/3] docs: Rename migrating and updating sections The original names "Migrate an existing project" and "Update an existing project" are confusing, as it is not clear which refers to updating to a new repo-config version (for a project that already uses it) and which refers to converting a project that doesn't use repo-config to start using it. Specially since a migration script is included, using "migrating" for converting projects is particularly confusing and ambiguous. This commit renames them to "Update to a new version" and "Convert an existing project" to hopefully reduce the confusion. Signed-off-by: Leandro Lucarella --- docs/user-guide/SUMMARY.md | 4 ++-- ...existing-project.md => convert-an-existing-project.md} | 8 ++++---- ...-an-existing-project.md => update-to-a-new-version.md} | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) rename docs/user-guide/{migrate-an-existing-project.md => convert-an-existing-project.md} (84%) rename docs/user-guide/{update-an-existing-project.md => update-to-a-new-version.md} (97%) diff --git a/docs/user-guide/SUMMARY.md b/docs/user-guide/SUMMARY.md index b851adea..44a5eb5d 100644 --- a/docs/user-guide/SUMMARY.md +++ b/docs/user-guide/SUMMARY.md @@ -1,5 +1,5 @@ * [Introduction](index.md) * [Start a new project](start-a-new-project/) -* [Migrate an existing project](migrate-an-existing-project.md) -* [Update an existing project](update-an-existing-project.md) +* [Update to a new version](update-to-a-new-version.md) +* [Convert an existing project](convert-an-existing-project.md) * [Advanced usage](advanced-usage.md) diff --git a/docs/user-guide/migrate-an-existing-project.md b/docs/user-guide/convert-an-existing-project.md similarity index 84% rename from docs/user-guide/migrate-an-existing-project.md rename to docs/user-guide/convert-an-existing-project.md index 2cc92beb..173a5f96 100644 --- a/docs/user-guide/migrate-an-existing-project.md +++ b/docs/user-guide/convert-an-existing-project.md @@ -1,6 +1,6 @@ -# Migrate an existing project +# Convert an existing project -The easiest way to migrate an existing project is to generate a new one based +The easiest way to convert an existing project is to generate a new one based on the current project metadata and then overwrite the existing files. It is recommended to commit all changes before doing this, so you can then use @@ -31,5 +31,5 @@ git commit -a !!! Tip Please have a look at the follow-up steps listed in the [Start a new - project](#create-the-local-development-environment) section to finish the - setup. + project](start-a-new-project/#create-the-local-environment) section to + finish the setup. diff --git a/docs/user-guide/update-an-existing-project.md b/docs/user-guide/update-to-a-new-version.md similarity index 97% rename from docs/user-guide/update-an-existing-project.md rename to docs/user-guide/update-to-a-new-version.md index 44029407..6a88fc86 100644 --- a/docs/user-guide/update-an-existing-project.md +++ b/docs/user-guide/update-to-a-new-version.md @@ -1,5 +1,5 @@ {% set ref_name = version.ref if version else 'HEAD' %} -# Update an existing project +# Update to a new version To upgrade an existing project, there are two main approaches: use a migration script or re-run the [Cookiecutter] command. @@ -108,5 +108,5 @@ templates update commit. !!! Tip Please have a look at the follow-up steps listed in the [Start a new - project](#create-the-local-development-environment) section to finish the - setup. + project](start-a-new-project/#create-the-local-environment) section to + finish the setup. From 7d4aa0e69cbe6b27da8b61135cf85b4720dda861 Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Thu, 26 Feb 2026 11:54:46 +0000 Subject: [PATCH 2/3] Bump dependabot-auto-approve to v1.5.0 Signed-off-by: Leandro Lucarella --- cookiecutter/migrate.py | 2 +- .../.github/workflows/auto-dependabot.yaml | 2 +- .../frequenz-actor-test/.github/workflows/auto-dependabot.yaml | 2 +- .../frequenz-api-test/.github/workflows/auto-dependabot.yaml | 2 +- .../frequenz-app-test/.github/workflows/auto-dependabot.yaml | 2 +- .../frequenz-test-python/.github/workflows/auto-dependabot.yaml | 2 +- .../frequenz-model-test/.github/workflows/auto-dependabot.yaml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cookiecutter/migrate.py b/cookiecutter/migrate.py index 9067c726..9726c00c 100644 --- a/cookiecutter/migrate.py +++ b/cookiecutter/migrate.py @@ -442,7 +442,7 @@ def migrate_auto_dependabot_token() -> None: filepath = Path(".github") / "workflows" / "auto-dependabot.yaml" # This is separated only to avoid flake8 errors about line length dependabot_auto_approve_version = ( - "a115bc7e0194c08f876493f311ec6f4de53f984e # v1.4.0" + "e943399cc9d76fbb6d7faae446cd57301d110165 # v1.5.0" ) desired_content = ( r"""name: Auto-merge Dependabot PR diff --git a/cookiecutter/{{cookiecutter.github_repo_name}}/.github/workflows/auto-dependabot.yaml b/cookiecutter/{{cookiecutter.github_repo_name}}/.github/workflows/auto-dependabot.yaml index 2ad51a84..dc3715fa 100644 --- a/cookiecutter/{{cookiecutter.github_repo_name}}/.github/workflows/auto-dependabot.yaml +++ b/cookiecutter/{{cookiecutter.github_repo_name}}/.github/workflows/auto-dependabot.yaml @@ -30,7 +30,7 @@ jobs: private-key: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_PRIVATE_KEY }} - name: Auto-merge Dependabot PR - uses: frequenz-floss/dependabot-auto-approve@a115bc7e0194c08f876493f311ec6f4de53f984e # v1.4.0 + uses: frequenz-floss/dependabot-auto-approve@e943399cc9d76fbb6d7faae446cd57301d110165 # v1.5.0 with: github-token: ${{ steps.app-token.outputs.token }} dependency-type: 'all' diff --git a/tests_golden/integration/test_cookiecutter_generation/actor/frequenz-actor-test/.github/workflows/auto-dependabot.yaml b/tests_golden/integration/test_cookiecutter_generation/actor/frequenz-actor-test/.github/workflows/auto-dependabot.yaml index 696219b0..60309295 100644 --- a/tests_golden/integration/test_cookiecutter_generation/actor/frequenz-actor-test/.github/workflows/auto-dependabot.yaml +++ b/tests_golden/integration/test_cookiecutter_generation/actor/frequenz-actor-test/.github/workflows/auto-dependabot.yaml @@ -29,7 +29,7 @@ jobs: private-key: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_PRIVATE_KEY }} - name: Auto-merge Dependabot PR - uses: frequenz-floss/dependabot-auto-approve@a115bc7e0194c08f876493f311ec6f4de53f984e # v1.4.0 + uses: frequenz-floss/dependabot-auto-approve@e943399cc9d76fbb6d7faae446cd57301d110165 # v1.5.0 with: github-token: ${{ steps.app-token.outputs.token }} dependency-type: 'all' diff --git a/tests_golden/integration/test_cookiecutter_generation/api/frequenz-api-test/.github/workflows/auto-dependabot.yaml b/tests_golden/integration/test_cookiecutter_generation/api/frequenz-api-test/.github/workflows/auto-dependabot.yaml index 696219b0..60309295 100644 --- a/tests_golden/integration/test_cookiecutter_generation/api/frequenz-api-test/.github/workflows/auto-dependabot.yaml +++ b/tests_golden/integration/test_cookiecutter_generation/api/frequenz-api-test/.github/workflows/auto-dependabot.yaml @@ -29,7 +29,7 @@ jobs: private-key: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_PRIVATE_KEY }} - name: Auto-merge Dependabot PR - uses: frequenz-floss/dependabot-auto-approve@a115bc7e0194c08f876493f311ec6f4de53f984e # v1.4.0 + uses: frequenz-floss/dependabot-auto-approve@e943399cc9d76fbb6d7faae446cd57301d110165 # v1.5.0 with: github-token: ${{ steps.app-token.outputs.token }} dependency-type: 'all' diff --git a/tests_golden/integration/test_cookiecutter_generation/app/frequenz-app-test/.github/workflows/auto-dependabot.yaml b/tests_golden/integration/test_cookiecutter_generation/app/frequenz-app-test/.github/workflows/auto-dependabot.yaml index 696219b0..60309295 100644 --- a/tests_golden/integration/test_cookiecutter_generation/app/frequenz-app-test/.github/workflows/auto-dependabot.yaml +++ b/tests_golden/integration/test_cookiecutter_generation/app/frequenz-app-test/.github/workflows/auto-dependabot.yaml @@ -29,7 +29,7 @@ jobs: private-key: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_PRIVATE_KEY }} - name: Auto-merge Dependabot PR - uses: frequenz-floss/dependabot-auto-approve@a115bc7e0194c08f876493f311ec6f4de53f984e # v1.4.0 + uses: frequenz-floss/dependabot-auto-approve@e943399cc9d76fbb6d7faae446cd57301d110165 # v1.5.0 with: github-token: ${{ steps.app-token.outputs.token }} dependency-type: 'all' diff --git a/tests_golden/integration/test_cookiecutter_generation/lib/frequenz-test-python/.github/workflows/auto-dependabot.yaml b/tests_golden/integration/test_cookiecutter_generation/lib/frequenz-test-python/.github/workflows/auto-dependabot.yaml index 696219b0..60309295 100644 --- a/tests_golden/integration/test_cookiecutter_generation/lib/frequenz-test-python/.github/workflows/auto-dependabot.yaml +++ b/tests_golden/integration/test_cookiecutter_generation/lib/frequenz-test-python/.github/workflows/auto-dependabot.yaml @@ -29,7 +29,7 @@ jobs: private-key: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_PRIVATE_KEY }} - name: Auto-merge Dependabot PR - uses: frequenz-floss/dependabot-auto-approve@a115bc7e0194c08f876493f311ec6f4de53f984e # v1.4.0 + uses: frequenz-floss/dependabot-auto-approve@e943399cc9d76fbb6d7faae446cd57301d110165 # v1.5.0 with: github-token: ${{ steps.app-token.outputs.token }} dependency-type: 'all' diff --git a/tests_golden/integration/test_cookiecutter_generation/model/frequenz-model-test/.github/workflows/auto-dependabot.yaml b/tests_golden/integration/test_cookiecutter_generation/model/frequenz-model-test/.github/workflows/auto-dependabot.yaml index 696219b0..60309295 100644 --- a/tests_golden/integration/test_cookiecutter_generation/model/frequenz-model-test/.github/workflows/auto-dependabot.yaml +++ b/tests_golden/integration/test_cookiecutter_generation/model/frequenz-model-test/.github/workflows/auto-dependabot.yaml @@ -29,7 +29,7 @@ jobs: private-key: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_PRIVATE_KEY }} - name: Auto-merge Dependabot PR - uses: frequenz-floss/dependabot-auto-approve@a115bc7e0194c08f876493f311ec6f4de53f984e # v1.4.0 + uses: frequenz-floss/dependabot-auto-approve@e943399cc9d76fbb6d7faae446cd57301d110165 # v1.5.0 with: github-token: ${{ steps.app-token.outputs.token }} dependency-type: 'all' From 39421f519b1b9a886198445f4eecc8e53ab540eb Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Wed, 11 Feb 2026 18:53:13 +0000 Subject: [PATCH 3/3] Add repo-config migration workflow Add a new workflow that automatically runs the migration script when Dependabot opens a PR for the repo-config group. It handles multi-version jumps by running each intermediate migration in sequence, posts the migration output as a PR comment and in the job summary, and auto-approves/merges clean migrations. PRs that need manual intervention fail the job until a human signals resolution via the `tool:repo-config:migration:intervention-done` label (or by removing the `tool:repo-config:migration:intervention-pending` label). Signed-off-by: Leandro Lucarella --- RELEASE_NOTES.md | 7 + cookiecutter/migrate.py | 142 +++++++++++++++ .../.github/workflows/auto-dependabot.yaml | 4 +- .../workflows/repo-config-migration.yaml | 55 ++++++ docs/user-guide/advanced-usage.md | 166 ++++++++++++++++++ .../user-guide/convert-an-existing-project.md | 4 +- .../start-a-new-project/configure-github.md | 57 ++++-- docs/user-guide/update-to-a-new-version.md | 116 ++++++++++-- .../.github/workflows/auto-dependabot.yaml | 4 +- .../workflows/repo-config-migration.yaml | 53 ++++++ .../.github/workflows/auto-dependabot.yaml | 4 +- .../workflows/repo-config-migration.yaml | 53 ++++++ .../.github/workflows/auto-dependabot.yaml | 4 +- .../workflows/repo-config-migration.yaml | 53 ++++++ .../.github/workflows/auto-dependabot.yaml | 4 +- .../workflows/repo-config-migration.yaml | 53 ++++++ .../.github/workflows/auto-dependabot.yaml | 4 +- .../workflows/repo-config-migration.yaml | 53 ++++++ 18 files changed, 800 insertions(+), 36 deletions(-) create mode 100644 cookiecutter/{{cookiecutter.github_repo_name}}/.github/workflows/repo-config-migration.yaml create mode 100644 tests_golden/integration/test_cookiecutter_generation/actor/frequenz-actor-test/.github/workflows/repo-config-migration.yaml create mode 100644 tests_golden/integration/test_cookiecutter_generation/api/frequenz-api-test/.github/workflows/repo-config-migration.yaml create mode 100644 tests_golden/integration/test_cookiecutter_generation/app/frequenz-app-test/.github/workflows/repo-config-migration.yaml create mode 100644 tests_golden/integration/test_cookiecutter_generation/lib/frequenz-test-python/.github/workflows/repo-config-migration.yaml create mode 100644 tests_golden/integration/test_cookiecutter_generation/model/frequenz-model-test/.github/workflows/repo-config-migration.yaml diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 3d5cc008..0215809f 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -5,6 +5,7 @@ This release migrates lightweight GitHub Actions workflow jobs to use the new cost-effective `ubuntu-slim` runner. It also updates cookiecutter pyproject license metadata to SPDX expressions to avoid setuptools deprecation warnings. The auto-dependabot workflow now uses a GitHub App installation token instead of `GITHUB_TOKEN` to fix merge queue and auto-merge failures. +Finally, it adds an automated repo-config migration workflow that runs migration scripts on Dependabot PRs. ## Upgrading @@ -41,6 +42,12 @@ But you might still need to adapt your code: - The CI workflow now uses a simpler matrix. +- Added `repo-config-migration.yaml` workflow that automatically runs the migration script, commits changes, posts results, and auto-approves/merges only when no migration commit is created. + + The workflow handles multi-version jumps by running each intermediate migration in sequence. The migration script output is posted as a PR comment and in the job summary. PRs with migration commits stay open for manual approval and merge. PRs that need manual intervention fail the job until a human completes the steps and signals resolution by removing the `tool:repo-config:migration:intervention-pending` label or adding the `tool:repo-config:migration:intervention-done` label. + +- The `auto-dependabot.yaml` workflow now skips repo-config group PRs, which are handled by the new migration workflow instead. + ## Bug Fixes diff --git a/cookiecutter/migrate.py b/cookiecutter/migrate.py index 9726c00c..6b5f86a3 100644 --- a/cookiecutter/migrate.py +++ b/cookiecutter/migrate.py @@ -55,6 +55,9 @@ def main() -> None: print("Migrating the CI workflows to use a platform matrix...") migrate_platform_matrix() print("=" * 72) + print("Installing repo-config migration workflow...") + migrate_repo_config_workflow() + print("=" * 72) print() if _manual_steps: @@ -505,6 +508,145 @@ def migrate_auto_dependabot_token() -> None: print(f" Added {filepath}: installed updated workflow") +def migrate_repo_config_workflow() -> None: + """Install the repo-config migration workflow and update auto-dependabot. + + This installs the ``repo-config-migration.yaml`` workflow that uses the + ``frequenz-floss/gh-action-dependabot-migrate`` action. It also + updates ``auto-dependabot.yaml`` to skip repo-config group PRs (which + are handled by the migration workflow instead). + + The workflow file is created from scratch (overwriting any previous + version) to ensure it stays in sync with the latest template. + """ + workflows_dir = Path(".github") / "workflows" + if not workflows_dir.is_dir(): + print(" Skipping (no .github/workflows directory found)") + return + + # ── Install repo-config-migration.yaml ──────────────────────────── + migration_wf = workflows_dir / "repo-config-migration.yaml" + desired_content = ( + r"""# Automatic repo-config migrations for Dependabot PRs +# +# The companion auto-dependabot workflow skips repo-config group PRs so +# they're handled exclusively by the migration workflow. +# +# XXX: !!! SECURITY WARNING !!! +# pull_request_target has write access to the repo, and can read secrets. +# This is required because Dependabot PRs are treated as fork PRs: the +# GITHUB_TOKEN is read-only and secrets are unavailable with a plain +# pull_request trigger. The action mitigates the risk by: +# - Never executing code from the PR (migrate.py is fetched from an +# upstream tag, not from the checked-out branch). +# - Gating migration steps on github.actor == 'dependabot[bot]'. +# - Running checkout with persist-credentials: false and isolating +# push credentials from the migration script environment. +# For more details read: +# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ + +name: Repo Config Migration + +on: + pull_request_target: + types: [opened, synchronize, reopened, labeled, unlabeled] + +permissions: + contents: write + issues: write + pull-requests: write + +jobs: + repo-config-migration: + name: Migrate Repo Config + if: contains(github.event.pull_request.title, 'the repo-config group') + runs-on: ubuntu-24.04 + steps: + - name: Generate token + id: create-app-token + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 + with: + app-id: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_ID }} + private-key: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_PRIVATE_KEY }} + - name: Migrate + uses: frequenz-floss/gh-action-dependabot-migrate@07dc7e74726498c50726a80cc2167a04d896508f # v1.0.0 + with: + script-url-template: >- + https://raw.githubusercontent.com/frequenz-floss/frequenz-repo-config-python/{version}/cookiecutter/migrate.py""" # noqa: E501 + r""" + token: ${{ steps.create-app-token.outputs.token }} + migration-token: ${{ secrets.REPO_CONFIG_MIGRATION_TOKEN }} + sign-commits: "true" + auto-merged-label: "tool:auto-merged" + migrated-label: "tool:repo-config:migration:executed" + intervention-pending-label: "tool:repo-config:migration:intervention-pending" + intervention-done-label: "tool:repo-config:migration:intervention-done" +""" + ) + + if migration_wf.exists(): + content = migration_wf.read_text(encoding="utf-8").replace("\r\n", "\n") + if content == desired_content: + print(f" Skipped {migration_wf}: already up to date") + else: + print( + f" Replacing {migration_wf} with updated workflow" + " (overwriting any local changes)" + ) + replace_file_atomically(migration_wf, desired_content) + else: + workflows_dir.mkdir(parents=True, exist_ok=True) + replace_file_atomically(migration_wf, desired_content) + print(f" Installed {migration_wf}") + + # ── Update auto-dependabot.yaml ─────────────────────────────────── + # + # Add a condition to skip repo-config group PRs, which are now + # handled by the migration workflow instead. + auto_dep = workflows_dir / "auto-dependabot.yaml" + if not auto_dep.exists(): + print(f" Skipping {auto_dep} (file not found)") + return + + dep_content = auto_dep.read_text(encoding="utf-8") + + # Already has the exclusion condition. + if "the repo-config group" in dep_content: + print(f" Skipped {auto_dep} (already excludes repo-config group)") + return + + # Match both multi-line and single-line `if` formats, with any runner. + old_patterns = [ + # Multi-line if (e.g. from a previous migration that used ubuntu-slim) + (" if: github.actor == 'dependabot[bot]'\n runs-on: ubuntu-slim"), + (" if: github.actor == 'dependabot[bot]'\n runs-on: ubuntu-latest"), + (" if: github.actor == 'dependabot[bot]'\n runs-on: ubuntu-24.04"), + ] + + new_template = ( + " if: >\n" + " github.actor == 'dependabot[bot]' &&\n" + " !contains(github.event.pull_request.title, 'the repo-config group')\n" + " runs-on: {runner}" + ) + + for old_pattern in old_patterns: + if old_pattern in dep_content: + # Extract the runner from the old pattern. + runner = old_pattern.rsplit("runs-on: ", 1)[1] + new_block = new_template.format(runner=runner) + replace_file_contents_atomically(auto_dep, old_pattern, new_block) + print(f" Updated {auto_dep}: added repo-config group exclusion") + return + + # If we didn't match any known pattern, flag a manual step. + manual_step( + f"Could not update {auto_dep} automatically. Please add a condition " + "to skip repo-config group PRs: " + "`!contains(github.event.pull_request.title, 'the repo-config group')`" + ) + + def read_project_type() -> str | None: """Read the cookiecutter project type from the replay file.""" replay_path = Path(".cookiecutter-replay.json") diff --git a/cookiecutter/{{cookiecutter.github_repo_name}}/.github/workflows/auto-dependabot.yaml b/cookiecutter/{{cookiecutter.github_repo_name}}/.github/workflows/auto-dependabot.yaml index dc3715fa..94e4169d 100644 --- a/cookiecutter/{{cookiecutter.github_repo_name}}/.github/workflows/auto-dependabot.yaml +++ b/cookiecutter/{{cookiecutter.github_repo_name}}/.github/workflows/auto-dependabot.yaml @@ -19,7 +19,9 @@ permissions: jobs: auto-merge: name: Auto-merge Dependabot PR - if: github.actor == 'dependabot[bot]' + if: | + github.actor == 'dependabot[bot]' && + !contains(github.event.pull_request.title, 'the repo-config group') runs-on: ubuntu-slim steps: - name: Generate GitHub App token diff --git a/cookiecutter/{{cookiecutter.github_repo_name}}/.github/workflows/repo-config-migration.yaml b/cookiecutter/{{cookiecutter.github_repo_name}}/.github/workflows/repo-config-migration.yaml new file mode 100644 index 00000000..9b6101c4 --- /dev/null +++ b/cookiecutter/{{cookiecutter.github_repo_name}}/.github/workflows/repo-config-migration.yaml @@ -0,0 +1,55 @@ +{% raw -%} +# Automatic repo-config migrations for Dependabot PRs +# +# The companion auto-dependabot workflow skips repo-config group PRs so +# they're handled exclusively by the migration workflow. +# +# XXX: !!! SECURITY WARNING !!! +# pull_request_target has write access to the repo, and can read secrets. +# This is required because Dependabot PRs are treated as fork PRs: the +# GITHUB_TOKEN is read-only and secrets are unavailable with a plain +# pull_request trigger. The action mitigates the risk by: +# - Never executing code from the PR (migrate.py is fetched from an +# upstream tag, not from the checked-out branch). +# - Gating migration steps on github.actor == 'dependabot[bot]'. +# - Running checkout with persist-credentials: false and isolating +# push credentials from the migration script environment. +# For more details read: +# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ + +name: Repo Config Migration + +on: + pull_request_target: + types: [opened, synchronize, reopened, labeled, unlabeled] + +permissions: + contents: write + issues: write + pull-requests: write + +jobs: + repo-config-migration: + name: Migrate Repo Config + if: contains(github.event.pull_request.title, 'the repo-config group') + runs-on: ubuntu-24.04 + steps: + - name: Generate token + id: create-app-token + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 + with: + app-id: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_ID }} + private-key: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_PRIVATE_KEY }} + - name: Migrate + uses: frequenz-floss/gh-action-dependabot-migrate@07dc7e74726498c50726a80cc2167a04d896508f # v1.0.0 + with: + script-url-template: >- + https://raw.githubusercontent.com/frequenz-floss/frequenz-repo-config-python/{version}/cookiecutter/migrate.py + token: ${{ steps.create-app-token.outputs.token }} + migration-token: ${{ secrets.REPO_CONFIG_MIGRATION_TOKEN }} + sign-commits: "true" + auto-merged-label: "tool:auto-merged" + migrated-label: "tool:repo-config:migration:executed" + intervention-pending-label: "tool:repo-config:migration:intervention-pending" + intervention-done-label: "tool:repo-config:migration:intervention-done" +{%- endraw %} diff --git a/docs/user-guide/advanced-usage.md b/docs/user-guide/advanced-usage.md index 7dbe40c0..473b27da 100644 --- a/docs/user-guide/advanced-usage.md +++ b/docs/user-guide/advanced-usage.md @@ -1,3 +1,5 @@ +{% set ref_name = version.ref_name if version else default_branch %} + # Advanced usage The [Cookiecutter] template uses some tools provided as a library by this @@ -10,4 +12,168 @@ using different CLI options for some tools), then you'll need to. You can find information about the extra features in the [API reference](reference/frequenz/repo/config/). +## Migration workflow + +Projects generated from the template already include the automated migration +workflow (`repo-config-migration.yaml`). If you need to set it up in a +project that wasn't generated from the template (or need to recreate it), +follow the steps below. + +The migration workflow uses the +[`gh-action-dependabot-migrate`][gh-action-dependabot-migrate] GitHub Action, +which contains all the migration logic (running scripts, committing changes, +posting comments, managing labels, approving and merging). The workflow in +your repository triggers the action with repo-config-specific inputs. + +See the [`gh-action-dependabot-migrate` +documentation][gh-action-dependabot-migrate] for full details on the action's +inputs, authentication options, trust model, and security design. + +For general information about how the workflow works and how to interact with +it as a user, see the [automated migration workflow][migration-workflow] +section in the update guide. + +### Creating the caller workflow + +Create `.github/workflows/repo-config-migration.yaml` in your repository: + +{% raw %} +```yaml +name: Repo Config Migration + +on: + pull_request_target: + types: [opened, synchronize, reopened, labeled, unlabeled] + +permissions: + contents: write + issues: write + pull-requests: write + +jobs: + repo-config-migration: + name: Migrate Repo Config + if: contains(github.event.pull_request.title, 'the repo-config group') + runs-on: ubuntu-24.04 + steps: + - name: Generate token + id: create-app-token + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 + with: + app-id: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_ID }} + private-key: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_PRIVATE_KEY }} + - name: Migrate + uses: frequenz-floss/gh-action-dependabot-migrate@07dc7e74726498c50726a80cc2167a04d896508f # v1.0.0 + with: + script-url-template: >- + https://raw.githubusercontent.com/frequenz-floss/frequenz-repo-config-python/{version}/cookiecutter/migrate.py + token: ${{ steps.create-app-token.outputs.token }} + migration-token: ${{ secrets.REPO_CONFIG_MIGRATION_TOKEN }} + sign-commits: "true" + auto-merged-label: "tool:auto-merged" + migrated-label: "tool:repo-config:migration:executed" + intervention-pending-label: "tool:repo-config:migration:intervention-pending" + intervention-done-label: "tool:repo-config:migration:intervention-done" +``` +{% endraw %} + +!!! Note + + Keep third-party actions pinned to commit hashes. + [Dependabot] updates those references through the `github-actions` + ecosystem. + +The key repo-config-specific settings are: + +* **`script-url-template`** — points to the `migrate.py` script in this + repository, with `{version}` as a placeholder for the version tag (e.g. + `v0.15.0`). +* **`token`** — a [GitHub App][GitHub App] installation token used for + pushing migration commits and managing PR state (approval and auto-merge, + when applicable). + Because it is not `GITHUB_TOKEN`, API calls made with this token trigger + follow-up workflows (merge queue CI, status checks, etc.). +* **`migration-token`** — a token exposed to the migration script as + `GH_TOKEN` / `GITHUB_TOKEN` for authenticated GitHub API calls (e.g. + updating repository settings or branch rulesets). +* **`auto-merge-on-changes`** — intentionally omitted, so it uses the + action's default (`false`). If the migration produces file changes + (commits), the PR is left for manual review, approval, and merge. +* **`auto-merged-label`** — set to `tool:auto-merged` (shared with the + `auto-dependabot.yaml` workflow) so auto-merged PRs are consistently + labelled. +* **`migrated-label`**, **`intervention-pending-label`**, + **`intervention-done-label`** — custom label names following the + `tool:repo-config:migration:*` naming convention. +* **`if` condition** — matches PRs with `the repo-config group` in the + title, which is how [Dependabot] names PRs for the `repo-config` + dependency group. + +!!! Warning "Security" + + The workflow uses `pull_request_target` because [Dependabot] PRs are + treated as fork PRs: `GITHUB_TOKEN` is read-only and secrets are + unavailable with a plain `pull_request` trigger. The action mitigates + the risk by never executing code from the PR — the migration script is + fetched from an upstream tag. For details, see [Preventing pwn + requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/). + +### Requirements + +The workflow requires: + +* **A [GitHub App][GitHub App]** configured as described in the [GitHub App + for Dependabot + workflows](start-a-new-project/configure-github.md#github-app-for-dependabot-workflows) + section. + +* **A `REPO_CONFIG_MIGRATION_TOKEN` secret** — a token (e.g. a fine-grained PAT) that + gives the migration script access to the GitHub API. See the [GitHub App + for Dependabot + workflows](start-a-new-project/configure-github.md#github-app-for-dependabot-workflows) + section for details. + + **Only necessary if the migration script needs to make authenticated API + calls, it can be left empty otherwise.** + +* **Auto-merge enabled** in the repository settings (Settings > General > + Pull Requests > Allow auto-merge). + +* **A `repo-config` dependency group** in `.github/dependabot.yml` that + groups `frequenz-repo-config*` packages: + + ```yaml + groups: + repo-config: + patterns: + - "frequenz-repo-config*" + ``` + +* **A `github-actions` ecosystem** in `.github/dependabot.yml` so the + action version stays up to date: + + ```yaml + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + ``` + +### Interaction with other workflows + +The `auto-dependabot.yaml` workflow has a job-level `if` condition that +**skips** PRs containing `the repo-config group` in the title. This ensures +repo-config dependency updates are handled exclusively by the migration +workflow, while all other [Dependabot] PRs continue to be auto-approved and +merged by the existing workflow. + +The `github-actions` ecosystem updates to the *caller workflow itself* (i.e. +bumping the action's `@` reference) produce PRs with `the compatible +group` in the title, which does **not** match the migration workflow's `if` +condition. These PRs are handled normally by `auto-dependabot.yaml`. + [Cookiecutter]: https://cookiecutter.readthedocs.io/en/stable +[Dependabot]: https://docs.github.com/en/code-security/dependabot/dependabot-version-updates +[GitHub App]: https://docs.github.com/en/apps/creating-github-apps +[gh-action-dependabot-migrate]: https://github.com/frequenz-floss/gh-action-dependabot-migrate +[migration-workflow]: update-to-a-new-version.md#automated-migration-workflow diff --git a/docs/user-guide/convert-an-existing-project.md b/docs/user-guide/convert-an-existing-project.md index 173a5f96..61df8f4b 100644 --- a/docs/user-guide/convert-an-existing-project.md +++ b/docs/user-guide/convert-an-existing-project.md @@ -32,4 +32,6 @@ git commit -a Please have a look at the follow-up steps listed in the [Start a new project](start-a-new-project/#create-the-local-environment) section to - finish the setup. + finish the setup, including the [GitHub App + configuration](start-a-new-project/configure-github.md#github-app-for-dependabot-workflows) + required for the automated migration and auto-merge workflows. diff --git a/docs/user-guide/start-a-new-project/configure-github.md b/docs/user-guide/start-a-new-project/configure-github.md index 058f41ed..31275ea5 100644 --- a/docs/user-guide/start-a-new-project/configure-github.md +++ b/docs/user-guide/start-a-new-project/configure-github.md @@ -115,25 +115,6 @@ Import the following * Enable *Dependabot version updates* if relevant -#### Auto-merge Dependabot PRs (GitHub App) - -The templates include an `.github/workflows/auto-dependabot.yaml` workflow that -auto-approves and enables auto-merge for Dependabot PRs. - -This workflow uses a GitHub App installation token (not `GITHUB_TOKEN`). This is -intentional: actions performed with `GITHUB_TOKEN` do not trigger certain -follow-up workflow runs, which can prevent merge queue CI (`merge_group`) from -starting. - -To make it work, ensure: - -* The GitHub App is installed on the repository. -* The following secrets are available to the workflow (typically as org secrets): - `FREQUENZ_AUTO_DEPENDABOT_APP_ID` and `FREQUENZ_AUTO_DEPENDABOT_APP_PRIVATE_KEY`. -* The app installation has sufficient repository permissions to approve/label - and enable auto-merge. In practice, this means at least `Pull requests: write` - and `Contents: write`. - ## Code The basic code configuration should be generate using @@ -144,3 +125,41 @@ The basic code configuration should be generate using No special configuration is needed for GitHub Pages, but you need to initialize the `gh-pages` branch. You can read how to do this in the [Initialize GitHub Pages](index.md#initialize-github-pages.md) section. + +## GitHub Actions + +### GitHub App for Dependabot workflows + +The templates include two workflows that act on [Dependabot] PRs: + +* **`auto-dependabot.yaml`** — auto-approves and enables auto-merge for + routine dependency updates. +* **`repo-config-migration.yaml`** — runs the repo-config migration script + and auto-merges only when the migration does not create commits; otherwise + it requires manual approval and merge (see + [Automated migration workflow](../update-to-a-new-version.md#automated-migration-workflow) + for details). + +Both workflows use a [GitHub App][GitHub App] installation token instead of +`GITHUB_TOKEN`. This is intentional: actions performed with `GITHUB_TOKEN` do +not trigger certain follow-up workflow runs, which can prevent merge queue CI +(`merge_group`) from starting. + +To make them work, ensure a [GitHub App][GitHub App] is installed on the +repository with at least `Pull requests: write` and `Contents: write` +permissions (add `Workflows: write` if migrations can touch +`.github/workflows/*` files). + +If the migration script needs to make authenticated API calls (e.g. updating +repository settings or branch rulesets), you should also provide the +`REPO_CONFIG_MIGRATION_TOKEN` secret with a dedicated, least-privilege token +scoped to exactly what the script needs. The `REPO_CONFIG_MIGRATION_TOKEN` is +exposed to the migration script as `GH_TOKEN` / `GITHUB_TOKEN` + +> [!TIP] +> It is recommended to have this secret set only when needed, and remove it +> again as soon as the migration is done. This minimizes the risk of abuse in +> case of a security issue in the migration script. + +[Dependabot]: https://docs.github.com/en/code-security/dependabot/dependabot-version-updates +[GitHub App]: https://docs.github.com/en/apps/creating-github-apps diff --git a/docs/user-guide/update-to-a-new-version.md b/docs/user-guide/update-to-a-new-version.md index 6a88fc86..96b434d2 100644 --- a/docs/user-guide/update-to-a-new-version.md +++ b/docs/user-guide/update-to-a-new-version.md @@ -1,15 +1,111 @@ {% set ref_name = version.ref if version else 'HEAD' %} # Update to a new version -To upgrade an existing project, there are two main approaches: use a migration -script or re-run the [Cookiecutter] command. +To upgrade an existing project to a new repo-config version, there are three +approaches: let the automated migration workflow handle it (default and +recommended), run the migration script manually, or re-run the [Cookiecutter] +command. + +## Automated migration workflow + +Projects generated from the template include a GitHub Actions workflow +(`repo-config-migration.yaml`) that runs the migration script automatically +when [Dependabot] opens a pull request for the `repo-config` dependency group. + +For updates where the migration script does not create additional commits and +does not report manual steps, the workflow auto-approves and enables +auto-merge. If the migration creates commits or reports manual steps, the PR +always requires manual review, approval, and merge. + +```mermaid +flowchart TD + A((Start)) --> B{Migration} + + %% transitions out of Migration + B -->|succeeded without changes| C["Approved + auto-merge enabled"] + B -->|intervention needed| D["Pending intervention (merges blocked)"] + B -->|succeeded with changes| F["Awaiting review"] + + %% normal flow + C -->|checks pass| E((("Merged"))) + + %% intervention loop + D -->|intervention done| F + F -->|intervention undone| D + + %% review/merge + F -->|checks pass + approval/merge| E + + %% layout: enforce the desired rows + subgraph r1[ ] + direction LR + A + end + + subgraph r2[ ] + direction LR + B + end + + subgraph r3[ ] + direction LR + C --- D + end + + subgraph r4[ ] + direction LR + F + end + + subgraph r5[ ] + direction LR + E + end + + %% keep row boxes invisible + style r1 fill:transparent,stroke:transparent + style r2 fill:transparent,stroke:transparent + style r3 fill:transparent,stroke:transparent + style r4 fill:transparent,stroke:transparent + style r5 fill:transparent,stroke:transparent +``` + +If the migration script exits with a non-zero status, the workflow: + +* Posts a PR comment with the full migration output. +* Adds the `tool:repo-config:migration:intervention-pending` label. +* Fails the job, which blocks merging. + +After you complete the required manual steps, push your changes to the PR +branch and signal resolution in **either** of these two ways: + +* **Remove** the `tool:repo-config:migration:intervention-pending` label from + the PR. +* **Add** the `tool:repo-config:migration:intervention-done` label to the PR. + +Either action triggers the workflow again, which normalises the labels. +Whenever a migration produces commits or requires manual steps, you should +review, approve, and merge the PR yourself. -## Use a migration script +If intervention is marked as done and you later need more manual work, either +removing `tool:repo-config:migration:intervention-done` or adding +`tool:repo-config:migration:intervention-pending` marks intervention as +pending again. -This is the recommended approach, as it should be much less work. Only when -extremely deep changes are made to the template, or when the project is very -old, should you consider re-running the [Cookiecutter] command. Usually release -notes will warn you about the former. +!!! Note + + If you need to set up this workflow in a project that wasn't generated + from the template, see the + [migration workflow setup](advanced-usage.md#migration-workflow) + section in the advanced usage page. You will also need to configure the + [GitHub App for Dependabot + workflows](start-a-new-project/configure-github.md#github-app-for-dependabot-workflows). + +## Use a migration script manually + +If you prefer to run the migration yourself (or if your project doesn't use +the automated workflow), you can fetch the migration script from GitHub and +run it directly. The script can't always perform all the changes necessary to migrate to a new version. In this case, you will have to manually apply the changes. The script @@ -18,9 +114,6 @@ will guide you through the process, so please read the script output carefully. The script can also only migrate from one version to the next. If you are skipping versions, you will have to run the script multiple times. -The easiest way to run the migration script is to fetch it from GitHub and run -it directly. - ```sh curl -sSL https://raw.githubusercontent.com/frequenz-floss/frequenz-repo-config-python/{{ ref_name }}/cookiecutter/migrate.py \ | python3 @@ -110,3 +203,6 @@ templates update commit. Please have a look at the follow-up steps listed in the [Start a new project](start-a-new-project/#create-the-local-environment) section to finish the setup. + +[Cookiecutter]: https://cookiecutter.readthedocs.io/en/stable +[Dependabot]: https://docs.github.com/en/code-security/dependabot/dependabot-version-updates diff --git a/tests_golden/integration/test_cookiecutter_generation/actor/frequenz-actor-test/.github/workflows/auto-dependabot.yaml b/tests_golden/integration/test_cookiecutter_generation/actor/frequenz-actor-test/.github/workflows/auto-dependabot.yaml index 60309295..a6c76658 100644 --- a/tests_golden/integration/test_cookiecutter_generation/actor/frequenz-actor-test/.github/workflows/auto-dependabot.yaml +++ b/tests_golden/integration/test_cookiecutter_generation/actor/frequenz-actor-test/.github/workflows/auto-dependabot.yaml @@ -18,7 +18,9 @@ permissions: jobs: auto-merge: name: Auto-merge Dependabot PR - if: github.actor == 'dependabot[bot]' + if: | + github.actor == 'dependabot[bot]' && + !contains(github.event.pull_request.title, 'the repo-config group') runs-on: ubuntu-slim steps: - name: Generate GitHub App token diff --git a/tests_golden/integration/test_cookiecutter_generation/actor/frequenz-actor-test/.github/workflows/repo-config-migration.yaml b/tests_golden/integration/test_cookiecutter_generation/actor/frequenz-actor-test/.github/workflows/repo-config-migration.yaml new file mode 100644 index 00000000..c2d3d489 --- /dev/null +++ b/tests_golden/integration/test_cookiecutter_generation/actor/frequenz-actor-test/.github/workflows/repo-config-migration.yaml @@ -0,0 +1,53 @@ +# Automatic repo-config migrations for Dependabot PRs +# +# The companion auto-dependabot workflow skips repo-config group PRs so +# they're handled exclusively by the migration workflow. +# +# XXX: !!! SECURITY WARNING !!! +# pull_request_target has write access to the repo, and can read secrets. +# This is required because Dependabot PRs are treated as fork PRs: the +# GITHUB_TOKEN is read-only and secrets are unavailable with a plain +# pull_request trigger. The action mitigates the risk by: +# - Never executing code from the PR (migrate.py is fetched from an +# upstream tag, not from the checked-out branch). +# - Gating migration steps on github.actor == 'dependabot[bot]'. +# - Running checkout with persist-credentials: false and isolating +# push credentials from the migration script environment. +# For more details read: +# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ + +name: Repo Config Migration + +on: + pull_request_target: + types: [opened, synchronize, reopened, labeled, unlabeled] + +permissions: + contents: write + issues: write + pull-requests: write + +jobs: + repo-config-migration: + name: Migrate Repo Config + if: contains(github.event.pull_request.title, 'the repo-config group') + runs-on: ubuntu-24.04 + steps: + - name: Generate token + id: create-app-token + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 + with: + app-id: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_ID }} + private-key: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_PRIVATE_KEY }} + - name: Migrate + uses: frequenz-floss/gh-action-dependabot-migrate@07dc7e74726498c50726a80cc2167a04d896508f # v1.0.0 + with: + script-url-template: >- + https://raw.githubusercontent.com/frequenz-floss/frequenz-repo-config-python/{version}/cookiecutter/migrate.py + token: ${{ steps.create-app-token.outputs.token }} + migration-token: ${{ secrets.REPO_CONFIG_MIGRATION_TOKEN }} + sign-commits: "true" + auto-merged-label: "tool:auto-merged" + migrated-label: "tool:repo-config:migration:executed" + intervention-pending-label: "tool:repo-config:migration:intervention-pending" + intervention-done-label: "tool:repo-config:migration:intervention-done" diff --git a/tests_golden/integration/test_cookiecutter_generation/api/frequenz-api-test/.github/workflows/auto-dependabot.yaml b/tests_golden/integration/test_cookiecutter_generation/api/frequenz-api-test/.github/workflows/auto-dependabot.yaml index 60309295..a6c76658 100644 --- a/tests_golden/integration/test_cookiecutter_generation/api/frequenz-api-test/.github/workflows/auto-dependabot.yaml +++ b/tests_golden/integration/test_cookiecutter_generation/api/frequenz-api-test/.github/workflows/auto-dependabot.yaml @@ -18,7 +18,9 @@ permissions: jobs: auto-merge: name: Auto-merge Dependabot PR - if: github.actor == 'dependabot[bot]' + if: | + github.actor == 'dependabot[bot]' && + !contains(github.event.pull_request.title, 'the repo-config group') runs-on: ubuntu-slim steps: - name: Generate GitHub App token diff --git a/tests_golden/integration/test_cookiecutter_generation/api/frequenz-api-test/.github/workflows/repo-config-migration.yaml b/tests_golden/integration/test_cookiecutter_generation/api/frequenz-api-test/.github/workflows/repo-config-migration.yaml new file mode 100644 index 00000000..c2d3d489 --- /dev/null +++ b/tests_golden/integration/test_cookiecutter_generation/api/frequenz-api-test/.github/workflows/repo-config-migration.yaml @@ -0,0 +1,53 @@ +# Automatic repo-config migrations for Dependabot PRs +# +# The companion auto-dependabot workflow skips repo-config group PRs so +# they're handled exclusively by the migration workflow. +# +# XXX: !!! SECURITY WARNING !!! +# pull_request_target has write access to the repo, and can read secrets. +# This is required because Dependabot PRs are treated as fork PRs: the +# GITHUB_TOKEN is read-only and secrets are unavailable with a plain +# pull_request trigger. The action mitigates the risk by: +# - Never executing code from the PR (migrate.py is fetched from an +# upstream tag, not from the checked-out branch). +# - Gating migration steps on github.actor == 'dependabot[bot]'. +# - Running checkout with persist-credentials: false and isolating +# push credentials from the migration script environment. +# For more details read: +# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ + +name: Repo Config Migration + +on: + pull_request_target: + types: [opened, synchronize, reopened, labeled, unlabeled] + +permissions: + contents: write + issues: write + pull-requests: write + +jobs: + repo-config-migration: + name: Migrate Repo Config + if: contains(github.event.pull_request.title, 'the repo-config group') + runs-on: ubuntu-24.04 + steps: + - name: Generate token + id: create-app-token + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 + with: + app-id: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_ID }} + private-key: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_PRIVATE_KEY }} + - name: Migrate + uses: frequenz-floss/gh-action-dependabot-migrate@07dc7e74726498c50726a80cc2167a04d896508f # v1.0.0 + with: + script-url-template: >- + https://raw.githubusercontent.com/frequenz-floss/frequenz-repo-config-python/{version}/cookiecutter/migrate.py + token: ${{ steps.create-app-token.outputs.token }} + migration-token: ${{ secrets.REPO_CONFIG_MIGRATION_TOKEN }} + sign-commits: "true" + auto-merged-label: "tool:auto-merged" + migrated-label: "tool:repo-config:migration:executed" + intervention-pending-label: "tool:repo-config:migration:intervention-pending" + intervention-done-label: "tool:repo-config:migration:intervention-done" diff --git a/tests_golden/integration/test_cookiecutter_generation/app/frequenz-app-test/.github/workflows/auto-dependabot.yaml b/tests_golden/integration/test_cookiecutter_generation/app/frequenz-app-test/.github/workflows/auto-dependabot.yaml index 60309295..a6c76658 100644 --- a/tests_golden/integration/test_cookiecutter_generation/app/frequenz-app-test/.github/workflows/auto-dependabot.yaml +++ b/tests_golden/integration/test_cookiecutter_generation/app/frequenz-app-test/.github/workflows/auto-dependabot.yaml @@ -18,7 +18,9 @@ permissions: jobs: auto-merge: name: Auto-merge Dependabot PR - if: github.actor == 'dependabot[bot]' + if: | + github.actor == 'dependabot[bot]' && + !contains(github.event.pull_request.title, 'the repo-config group') runs-on: ubuntu-slim steps: - name: Generate GitHub App token diff --git a/tests_golden/integration/test_cookiecutter_generation/app/frequenz-app-test/.github/workflows/repo-config-migration.yaml b/tests_golden/integration/test_cookiecutter_generation/app/frequenz-app-test/.github/workflows/repo-config-migration.yaml new file mode 100644 index 00000000..c2d3d489 --- /dev/null +++ b/tests_golden/integration/test_cookiecutter_generation/app/frequenz-app-test/.github/workflows/repo-config-migration.yaml @@ -0,0 +1,53 @@ +# Automatic repo-config migrations for Dependabot PRs +# +# The companion auto-dependabot workflow skips repo-config group PRs so +# they're handled exclusively by the migration workflow. +# +# XXX: !!! SECURITY WARNING !!! +# pull_request_target has write access to the repo, and can read secrets. +# This is required because Dependabot PRs are treated as fork PRs: the +# GITHUB_TOKEN is read-only and secrets are unavailable with a plain +# pull_request trigger. The action mitigates the risk by: +# - Never executing code from the PR (migrate.py is fetched from an +# upstream tag, not from the checked-out branch). +# - Gating migration steps on github.actor == 'dependabot[bot]'. +# - Running checkout with persist-credentials: false and isolating +# push credentials from the migration script environment. +# For more details read: +# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ + +name: Repo Config Migration + +on: + pull_request_target: + types: [opened, synchronize, reopened, labeled, unlabeled] + +permissions: + contents: write + issues: write + pull-requests: write + +jobs: + repo-config-migration: + name: Migrate Repo Config + if: contains(github.event.pull_request.title, 'the repo-config group') + runs-on: ubuntu-24.04 + steps: + - name: Generate token + id: create-app-token + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 + with: + app-id: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_ID }} + private-key: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_PRIVATE_KEY }} + - name: Migrate + uses: frequenz-floss/gh-action-dependabot-migrate@07dc7e74726498c50726a80cc2167a04d896508f # v1.0.0 + with: + script-url-template: >- + https://raw.githubusercontent.com/frequenz-floss/frequenz-repo-config-python/{version}/cookiecutter/migrate.py + token: ${{ steps.create-app-token.outputs.token }} + migration-token: ${{ secrets.REPO_CONFIG_MIGRATION_TOKEN }} + sign-commits: "true" + auto-merged-label: "tool:auto-merged" + migrated-label: "tool:repo-config:migration:executed" + intervention-pending-label: "tool:repo-config:migration:intervention-pending" + intervention-done-label: "tool:repo-config:migration:intervention-done" diff --git a/tests_golden/integration/test_cookiecutter_generation/lib/frequenz-test-python/.github/workflows/auto-dependabot.yaml b/tests_golden/integration/test_cookiecutter_generation/lib/frequenz-test-python/.github/workflows/auto-dependabot.yaml index 60309295..a6c76658 100644 --- a/tests_golden/integration/test_cookiecutter_generation/lib/frequenz-test-python/.github/workflows/auto-dependabot.yaml +++ b/tests_golden/integration/test_cookiecutter_generation/lib/frequenz-test-python/.github/workflows/auto-dependabot.yaml @@ -18,7 +18,9 @@ permissions: jobs: auto-merge: name: Auto-merge Dependabot PR - if: github.actor == 'dependabot[bot]' + if: | + github.actor == 'dependabot[bot]' && + !contains(github.event.pull_request.title, 'the repo-config group') runs-on: ubuntu-slim steps: - name: Generate GitHub App token diff --git a/tests_golden/integration/test_cookiecutter_generation/lib/frequenz-test-python/.github/workflows/repo-config-migration.yaml b/tests_golden/integration/test_cookiecutter_generation/lib/frequenz-test-python/.github/workflows/repo-config-migration.yaml new file mode 100644 index 00000000..c2d3d489 --- /dev/null +++ b/tests_golden/integration/test_cookiecutter_generation/lib/frequenz-test-python/.github/workflows/repo-config-migration.yaml @@ -0,0 +1,53 @@ +# Automatic repo-config migrations for Dependabot PRs +# +# The companion auto-dependabot workflow skips repo-config group PRs so +# they're handled exclusively by the migration workflow. +# +# XXX: !!! SECURITY WARNING !!! +# pull_request_target has write access to the repo, and can read secrets. +# This is required because Dependabot PRs are treated as fork PRs: the +# GITHUB_TOKEN is read-only and secrets are unavailable with a plain +# pull_request trigger. The action mitigates the risk by: +# - Never executing code from the PR (migrate.py is fetched from an +# upstream tag, not from the checked-out branch). +# - Gating migration steps on github.actor == 'dependabot[bot]'. +# - Running checkout with persist-credentials: false and isolating +# push credentials from the migration script environment. +# For more details read: +# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ + +name: Repo Config Migration + +on: + pull_request_target: + types: [opened, synchronize, reopened, labeled, unlabeled] + +permissions: + contents: write + issues: write + pull-requests: write + +jobs: + repo-config-migration: + name: Migrate Repo Config + if: contains(github.event.pull_request.title, 'the repo-config group') + runs-on: ubuntu-24.04 + steps: + - name: Generate token + id: create-app-token + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 + with: + app-id: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_ID }} + private-key: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_PRIVATE_KEY }} + - name: Migrate + uses: frequenz-floss/gh-action-dependabot-migrate@07dc7e74726498c50726a80cc2167a04d896508f # v1.0.0 + with: + script-url-template: >- + https://raw.githubusercontent.com/frequenz-floss/frequenz-repo-config-python/{version}/cookiecutter/migrate.py + token: ${{ steps.create-app-token.outputs.token }} + migration-token: ${{ secrets.REPO_CONFIG_MIGRATION_TOKEN }} + sign-commits: "true" + auto-merged-label: "tool:auto-merged" + migrated-label: "tool:repo-config:migration:executed" + intervention-pending-label: "tool:repo-config:migration:intervention-pending" + intervention-done-label: "tool:repo-config:migration:intervention-done" diff --git a/tests_golden/integration/test_cookiecutter_generation/model/frequenz-model-test/.github/workflows/auto-dependabot.yaml b/tests_golden/integration/test_cookiecutter_generation/model/frequenz-model-test/.github/workflows/auto-dependabot.yaml index 60309295..a6c76658 100644 --- a/tests_golden/integration/test_cookiecutter_generation/model/frequenz-model-test/.github/workflows/auto-dependabot.yaml +++ b/tests_golden/integration/test_cookiecutter_generation/model/frequenz-model-test/.github/workflows/auto-dependabot.yaml @@ -18,7 +18,9 @@ permissions: jobs: auto-merge: name: Auto-merge Dependabot PR - if: github.actor == 'dependabot[bot]' + if: | + github.actor == 'dependabot[bot]' && + !contains(github.event.pull_request.title, 'the repo-config group') runs-on: ubuntu-slim steps: - name: Generate GitHub App token diff --git a/tests_golden/integration/test_cookiecutter_generation/model/frequenz-model-test/.github/workflows/repo-config-migration.yaml b/tests_golden/integration/test_cookiecutter_generation/model/frequenz-model-test/.github/workflows/repo-config-migration.yaml new file mode 100644 index 00000000..c2d3d489 --- /dev/null +++ b/tests_golden/integration/test_cookiecutter_generation/model/frequenz-model-test/.github/workflows/repo-config-migration.yaml @@ -0,0 +1,53 @@ +# Automatic repo-config migrations for Dependabot PRs +# +# The companion auto-dependabot workflow skips repo-config group PRs so +# they're handled exclusively by the migration workflow. +# +# XXX: !!! SECURITY WARNING !!! +# pull_request_target has write access to the repo, and can read secrets. +# This is required because Dependabot PRs are treated as fork PRs: the +# GITHUB_TOKEN is read-only and secrets are unavailable with a plain +# pull_request trigger. The action mitigates the risk by: +# - Never executing code from the PR (migrate.py is fetched from an +# upstream tag, not from the checked-out branch). +# - Gating migration steps on github.actor == 'dependabot[bot]'. +# - Running checkout with persist-credentials: false and isolating +# push credentials from the migration script environment. +# For more details read: +# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ + +name: Repo Config Migration + +on: + pull_request_target: + types: [opened, synchronize, reopened, labeled, unlabeled] + +permissions: + contents: write + issues: write + pull-requests: write + +jobs: + repo-config-migration: + name: Migrate Repo Config + if: contains(github.event.pull_request.title, 'the repo-config group') + runs-on: ubuntu-24.04 + steps: + - name: Generate token + id: create-app-token + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 + with: + app-id: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_ID }} + private-key: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_PRIVATE_KEY }} + - name: Migrate + uses: frequenz-floss/gh-action-dependabot-migrate@07dc7e74726498c50726a80cc2167a04d896508f # v1.0.0 + with: + script-url-template: >- + https://raw.githubusercontent.com/frequenz-floss/frequenz-repo-config-python/{version}/cookiecutter/migrate.py + token: ${{ steps.create-app-token.outputs.token }} + migration-token: ${{ secrets.REPO_CONFIG_MIGRATION_TOKEN }} + sign-commits: "true" + auto-merged-label: "tool:auto-merged" + migrated-label: "tool:repo-config:migration:executed" + intervention-pending-label: "tool:repo-config:migration:intervention-pending" + intervention-done-label: "tool:repo-config:migration:intervention-done"