From f2eda998af3a7341ea8e5acd54595ed54c053ce7 Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Mon, 4 May 2026 14:14:31 +0200 Subject: [PATCH] Apply migration from 0.16.0 to 0.17.0 For some reason the dependabot PR updated the dependency to v0.17.0 but the PR and commit description said it was updated to v0.16.0, so the migration script ran for v0.15.0 and v0.16.0 but not v0.17.0. We now run the missing migration. Output: ``` ======================================================================== Updating generated CI workflows... Updated .github/workflows/ci-pr.yaml: updated CI pull-request workflow Updated .github/workflows/ci.yaml: updated main CI workflow ======================================================================== Fixing missed CI platform matrix migrations... Skipped .github/workflows/ci.yaml: platform matrix migration already fixed ======================================================================== Updating generated Dependabot workflows... Updated .github/workflows/auto-dependabot.yaml: updated Dependabot auto-merge workflow Updated .github/workflows/repo-config-migration.yaml: updated repo-config migration workflow ======================================================================== Creating black migration workflow... Created .github/workflows/black-migration.yaml: black formatting migration workflow ======================================================================== Updating auxiliary GitHub workflows... Updated .github/workflows/dco-merge-queue.yml: updated DCO merge queue workflow Updated .github/workflows/labeler.yml: updated labeler workflow Updated .github/workflows/release-notes-check.yml: updated release notes check workflow ======================================================================== ``` Signed-off-by: Leandro Lucarella --- .github/workflows/auto-dependabot.yaml | 11 ++- .github/workflows/black-migration.yaml | 88 ++++++++++++++++++++ .github/workflows/ci-pr.yaml | 21 +++-- .github/workflows/ci.yaml | 84 ++++++++++++------- .github/workflows/dco-merge-queue.yml | 3 + .github/workflows/labeler.yml | 2 + .github/workflows/release-notes-check.yml | 3 + .github/workflows/repo-config-migration.yaml | 11 +++ 8 files changed, 183 insertions(+), 40 deletions(-) create mode 100644 .github/workflows/black-migration.yaml diff --git a/.github/workflows/auto-dependabot.yaml b/.github/workflows/auto-dependabot.yaml index 65935f7b..f12d16a3 100644 --- a/.github/workflows/auto-dependabot.yaml +++ b/.github/workflows/auto-dependabot.yaml @@ -12,7 +12,9 @@ on: pull_request_target: permissions: + # Read repository contents and Dependabot metadata used by the nested action. contents: read + # The nested action also uses `github.token` internally for PR operations. pull-requests: write jobs: @@ -20,7 +22,8 @@ jobs: name: Auto-merge Dependabot PR if: > github.actor == 'dependabot[bot]' && - !contains(github.event.pull_request.title, 'the repo-config group') + !contains(github.event.pull_request.title, 'the repo-config group') && + !contains(github.event.pull_request.title, 'Bump black from ') runs-on: ubuntu-slim steps: - name: Generate GitHub App token @@ -29,6 +32,12 @@ jobs: with: app-id: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_ID }} private-key: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_PRIVATE_KEY }} + # Merge Dependabot PRs. + permission-contents: write + # Create the auto-merged label if it does not exist. + permission-issues: write + # Approve PRs, add labels, and enable auto-merge. + permission-pull-requests: write - name: Auto-merge Dependabot PR uses: frequenz-floss/dependabot-auto-approve@e943399cc9d76fbb6d7faae446cd57301d110165 # v1.5.0 diff --git a/.github/workflows/black-migration.yaml b/.github/workflows/black-migration.yaml new file mode 100644 index 00000000..1969c461 --- /dev/null +++ b/.github/workflows/black-migration.yaml @@ -0,0 +1,88 @@ +# Automatic black formatting migration for Dependabot PRs +# +# When Dependabot upgrades black, this workflow installs the new version +# and runs `black .` so the PR already contains any formatting changes +# introduced by the upgrade, while leaving the PR open for review. +# +# Black uses calendar versioning. Only the first release of a new calendar +# year may introduce formatting changes (major bump in Dependabot's terms). +# Minor and patch updates within a year keep formatting stable, so they stay +# in the regular Dependabot groups and are auto-merged normally. +# +# The companion auto-dependabot workflow skips major black PRs so they're +# handled exclusively by this 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 (the migration script is embedded +# in this workflow file on the base branch, not taken from the PR). +# - 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: Black Migration + +on: + merge_group: # To allow using this as a required check for merging + pull_request_target: + types: [opened, synchronize, reopened, labeled, unlabeled] + +permissions: + # Commit reformatted files back to the PR branch. + contents: write + # Create and normalize migration state labels. + issues: write + # Read/update pull request metadata and comments. + pull-requests: write + +jobs: + black-migration: + name: Migrate Black + # Skip if it was triggered by the merge queue. We only need the workflow to + # be executed to meet the "Required check" condition for merging, but we + # don't need to actually run the job, having the job present as Skipped is + # enough. + if: | + github.event_name == 'pull_request_target' && + github.actor == 'dependabot[bot]' && + contains(github.event.pull_request.title, 'Bump black from ') + runs-on: ubuntu-24.04 + steps: + - name: Generate token + id: create-app-token + uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0 + with: + app-id: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_ID }} + private-key: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_PRIVATE_KEY }} + # Push reformatted files to the PR branch. + permission-contents: write + # Create and normalize migration state labels. + permission-issues: write + # Read/update pull request metadata and labels. + permission-pull-requests: write + - name: Migrate + uses: frequenz-floss/gh-action-dependabot-migrate@b389f72f9282346920150a67495efbae450ac07b # v1.1.0 + with: + migration-script: | + import os + import subprocess + import sys + + version = os.environ["MIGRATION_VERSION"].lstrip("v") + subprocess.run( + [sys.executable, "-Im", "pip", "install", f"black=={version}"], + check=True, + ) + subprocess.run([sys.executable, "-Im", "black", "."], check=True) + token: ${{ steps.create-app-token.outputs.token }} + auto-merge-on-changes: "false" + sign-commits: "true" + auto-merged-label: "tool:auto-merged" + migrated-label: "tool:black:migration:executed" + intervention-pending-label: "tool:black:migration:intervention-pending" + intervention-done-label: "tool:black:migration:intervention-done" diff --git a/.github/workflows/ci-pr.yaml b/.github/workflows/ci-pr.yaml index ac6905d4..015d585c 100644 --- a/.github/workflows/ci-pr.yaml +++ b/.github/workflows/ci-pr.yaml @@ -3,6 +3,10 @@ name: Test PR on: pull_request: +permissions: + # Read repository contents for checkout and dependency resolution only. + contents: read + env: # Please make sure this version is included in the `matrix`, as the # `matrix` section can't use `env`, so it must be entered manually @@ -17,7 +21,7 @@ jobs: steps: - name: Run nox - uses: frequenz-floss/gh-action-nox@v1.1.1 + uses: frequenz-floss/gh-action-nox@e1351cf45e05e85afc1c79ab883e06322892d34c # v1.1.0 with: python-version: "3.11" nox-session: ci_checks_max @@ -27,15 +31,15 @@ jobs: runs-on: ubuntu-24.04 steps: - name: Setup Git - uses: frequenz-floss/gh-action-setup-git@v1.0.0 + uses: frequenz-floss/gh-action-setup-git@16952aac3ccc01d27412fe0dea3ea946530dcace # v1.0.0 - name: Fetch sources - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: submodules: true - name: Setup Python - uses: frequenz-floss/gh-action-setup-python-with-deps@v1.0.4 + uses: frequenz-floss/gh-action-setup-python-with-deps@0d0d77eac3b54799f31f25a1060ef2c6ebdf9299 # v1.0.2 with: python-version: ${{ env.DEFAULT_PYTHON_VERSION }} dependencies: .[dev-mkdocs] @@ -44,11 +48,14 @@ jobs: env: MIKE_VERSION: gh-${{ github.job }} run: | - mike deploy $MIKE_VERSION - mike set-default $MIKE_VERSION + # mike is installed as a console script, not a runnable module. + # Run the installed script under isolated mode to avoid importing from + # the workspace when building docs from checked-out code. + python -I "$(command -v mike)" deploy "$MIKE_VERSION" + python -I "$(command -v mike)" set-default "$MIKE_VERSION" - name: Upload site - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: docs-site path: site/ diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index aac4b34e..c3b4cfbf 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -15,6 +15,10 @@ on: - 'dependabot/**' workflow_dispatch: +permissions: + # Read repository contents for checkout and dependency resolution only. + contents: read + env: # Please make sure this version is included in the `matrix`, as the # `matrix` section can't use `env`, so it must be entered manually @@ -43,7 +47,7 @@ jobs: steps: - name: Run nox - uses: frequenz-floss/gh-action-nox@v1.1.1 + uses: frequenz-floss/gh-action-nox@e1351cf45e05e85afc1c79ab883e06322892d34c # v1.1.0 with: python-version: ${{ matrix.python }} nox-session: ${{ matrix.nox-session }} @@ -59,6 +63,8 @@ jobs: # We skip this job only if nox was also skipped if: always() && needs.nox.result != 'skipped' runs-on: ubuntu-slim + # Drop token permissions: this job only checks matrix status from `needs`. + permissions: {} env: DEPS_RESULT: ${{ needs.nox.result }} steps: @@ -74,24 +80,24 @@ jobs: steps: - name: Setup Git - uses: frequenz-floss/gh-action-setup-git@v1.0.0 + uses: frequenz-floss/gh-action-setup-git@16952aac3ccc01d27412fe0dea3ea946530dcace # v1.0.0 - name: Fetch sources - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: submodules: true - name: Setup Python - uses: frequenz-floss/gh-action-setup-python-with-deps@v1.0.4 + uses: frequenz-floss/gh-action-setup-python-with-deps@0d0d77eac3b54799f31f25a1060ef2c6ebdf9299 # v1.0.2 with: python-version: ${{ env.DEFAULT_PYTHON_VERSION }} dependencies: build - name: Build the source and binary distribution - run: python -m build + run: python -Im build - name: Upload distribution files - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: dist-packages path: dist/ @@ -113,13 +119,13 @@ jobs: steps: - name: Setup Git - uses: frequenz-floss/gh-action-setup-git@v1.0.0 + uses: frequenz-floss/gh-action-setup-git@16952aac3ccc01d27412fe0dea3ea946530dcace # v1.0.0 - name: Print environment (debug) run: env - name: Download package - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: dist-packages path: dist @@ -139,13 +145,13 @@ jobs: > pyproject.toml - name: Setup Python - uses: frequenz-floss/gh-action-setup-python-with-deps@v1.0.4 + uses: frequenz-floss/gh-action-setup-python-with-deps@0d0d77eac3b54799f31f25a1060ef2c6ebdf9299 # v1.0.2 with: python-version: ${{ matrix.python }} dependencies: dist/*.whl - name: Print installed packages (debug) - run: python -m pip freeze + run: python -Im pip freeze # This job runs if all the `test-installation` matrix jobs ran and succeeded. # It is only used to have a single job that we can require in branch @@ -158,6 +164,8 @@ jobs: # We skip this job only if test-installation was also skipped if: always() && needs.test-installation.result != 'skipped' runs-on: ubuntu-slim + # Drop token permissions: this job only checks matrix status from `needs`. + permissions: {} env: DEPS_RESULT: ${{ needs.test-installation.result }} steps: @@ -170,15 +178,15 @@ jobs: runs-on: ubuntu-24.04 steps: - name: Setup Git - uses: frequenz-floss/gh-action-setup-git@v1.0.0 + uses: frequenz-floss/gh-action-setup-git@16952aac3ccc01d27412fe0dea3ea946530dcace # v1.0.0 - name: Fetch sources - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: submodules: true - name: Setup Python - uses: frequenz-floss/gh-action-setup-python-with-deps@v1.0.4 + uses: frequenz-floss/gh-action-setup-python-with-deps@0d0d77eac3b54799f31f25a1060ef2c6ebdf9299 # v1.0.2 with: python-version: ${{ env.DEFAULT_PYTHON_VERSION }} dependencies: .[dev-mkdocs] @@ -187,11 +195,14 @@ jobs: env: MIKE_VERSION: gh-${{ github.job }} run: | - mike deploy $MIKE_VERSION - mike set-default $MIKE_VERSION + # mike is installed as a console script, not a runnable module. + # Run the installed script under isolated mode to avoid importing from + # the workspace when building docs from checked-out code. + python -I "$(command -v mike)" deploy "$MIKE_VERSION" + python -I "$(command -v mike)" set-default "$MIKE_VERSION" - name: Upload site - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: docs-site path: site/ @@ -203,18 +214,19 @@ jobs: if: github.event_name == 'push' runs-on: ubuntu-24.04 permissions: + # Push generated documentation updates to the `gh-pages` branch. contents: write steps: - name: Setup Git - uses: frequenz-floss/gh-action-setup-git@v1.0.0 + uses: frequenz-floss/gh-action-setup-git@16952aac3ccc01d27412fe0dea3ea946530dcace # v1.0.0 - name: Fetch sources - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: submodules: true - name: Setup Python - uses: frequenz-floss/gh-action-setup-python-with-deps@v1.0.4 + uses: frequenz-floss/gh-action-setup-python-with-deps@0d0d77eac3b54799f31f25a1060ef2c6ebdf9299 # v1.0.2 with: python-version: ${{ env.DEFAULT_PYTHON_VERSION }} dependencies: .[dev-mkdocs] @@ -227,7 +239,7 @@ jobs: GIT_REF: ${{ github.ref }} GIT_SHA: ${{ github.sha }} run: | - python -m frequenz.repo.config.cli.version.mike.info + python -Im frequenz.repo.config.cli.version.mike.info - name: Fetch the gh-pages branch if: steps.mike-version.outputs.version @@ -248,13 +260,23 @@ jobs: GIT_REF: ${{ github.ref }} GIT_SHA: ${{ github.sha }} run: | - mike deploy --update-aliases --title "$TITLE" "$VERSION" $ALIASES + # Collect aliases into an array to avoid accidental (or malicious) + # shell injection when passing them to mike. + aliases=() + if test -n "$ALIASES"; then + read -r -a aliases <<<"$ALIASES" + fi + # mike is installed as a console script, not a runnable module. + # Run the installed script under isolated mode to avoid importing from + # the workspace when building docs from checked-out code. + python -I "$(command -v mike)" \ + deploy --update-aliases --title "$TITLE" "$VERSION" "${aliases[@]}" - name: Sort site versions if: steps.mike-version.outputs.version run: | git checkout gh-pages - python -m frequenz.repo.config.cli.version.mike.sort versions.json + python -Im frequenz.repo.config.cli.version.mike.sort versions.json git commit -a -m "Sort versions.json" - name: Publish site @@ -268,14 +290,12 @@ jobs: # Create a release only on tags creation if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') permissions: - # We need write permissions on contents to create GitHub releases and on - # discussions to create the release announcement in the discussion forums + # Create GitHub releases and upload distribution artifacts. contents: write - discussions: write runs-on: ubuntu-slim steps: - name: Download distribution files - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: dist-packages path: dist @@ -297,14 +317,14 @@ jobs: - name: Create GitHub release run: | set -ux - extra_opts= - if echo "$REF_NAME" | grep -- -; then extra_opts=" --prerelease"; fi + extra_opts=() + if echo "$REF_NAME" | grep -- -; then extra_opts+=(--prerelease); fi gh release create \ -R "$REPOSITORY" \ --notes-file RELEASE_NOTES.md \ --generate-notes \ - $extra_opts \ - $REF_NAME \ + "${extra_opts[@]}" \ + "$REF_NAME" \ dist/* env: REF_NAME: ${{ github.ref_name }} @@ -321,10 +341,10 @@ jobs: id-token: write steps: - name: Download distribution files - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: dist-packages path: dist - name: Publish the Python distribution to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 + uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 diff --git a/.github/workflows/dco-merge-queue.yml b/.github/workflows/dco-merge-queue.yml index d9597ad0..7a4260de 100644 --- a/.github/workflows/dco-merge-queue.yml +++ b/.github/workflows/dco-merge-queue.yml @@ -3,6 +3,9 @@ name: DCO on: merge_group: +# Drop all token permissions: this workflow only runs a local echo command. +permissions: {} + jobs: DCO: runs-on: ubuntu-slim diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index c327e7f2..393ddfc2 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -5,7 +5,9 @@ on: [pull_request_target] jobs: Label: permissions: + # Read the labeler configuration from the repository. contents: read + # Add labels to pull requests. pull-requests: write runs-on: ubuntu-slim steps: diff --git a/.github/workflows/release-notes-check.yml b/.github/workflows/release-notes-check.yml index ab3017f8..5c8243b5 100644 --- a/.github/workflows/release-notes-check.yml +++ b/.github/workflows/release-notes-check.yml @@ -17,6 +17,9 @@ jobs: check-release-notes: name: Check release notes are updated runs-on: ubuntu-slim + permissions: + # Read pull request metadata to evaluate labels and changed files. + pull-requests: read steps: - name: Check for a release notes update if: github.event_name == 'pull_request' diff --git a/.github/workflows/repo-config-migration.yaml b/.github/workflows/repo-config-migration.yaml index 6ffaf602..086fda19 100644 --- a/.github/workflows/repo-config-migration.yaml +++ b/.github/workflows/repo-config-migration.yaml @@ -24,8 +24,11 @@ on: types: [opened, synchronize, reopened, labeled, unlabeled] permissions: + # Commit migration changes back to the PR branch. contents: write + # Create and normalize migration state labels. issues: write + # Read/update pull request metadata and comments. pull-requests: write jobs: @@ -46,6 +49,14 @@ jobs: with: app-id: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_ID }} private-key: ${{ secrets.FREQUENZ_AUTO_DEPENDABOT_APP_PRIVATE_KEY }} + # Push migration commits to the PR branch. + permission-contents: write + # Manage labels when auto-merging patch-only updates. + permission-issues: write + # Approve pull requests and enable auto-merge. + permission-pull-requests: write + # Allow pushes when migration changes workflow files. + permission-workflows: write - name: Migrate uses: frequenz-floss/gh-action-dependabot-migrate@45994e185a9040449304a470e8f02d0e197873b4 # v1.1.1 with: