diff --git a/.github/workflows/analyze-dependabot-reusable.yaml b/.github/workflows/analyze-dependabot-reusable.yaml new file mode 100644 index 00000000..326656d4 --- /dev/null +++ b/.github/workflows/analyze-dependabot-reusable.yaml @@ -0,0 +1,59 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to you under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +name: Dependabot Analyze PR + +on: + workflow_call: { } + +# Explicitly drop all permissions inherited from the caller for security. +# Reference: https://docs.github.com/en/actions/sharing-automations/reusing-workflows#access-and-permissions +permissions: { } + +jobs: + + analyze-pull-request: + # Defense-in-depth (in case the caller forgets): + # `github.actor` prevents recursive calls when `github-actions[bot]` pushes to the PR; + # `github.event.pull_request.user.login` skips PRs not opened by Dependabot. + if: ${{ + github.actor == 'dependabot[bot]' + && github.event.pull_request.user.login == 'dependabot[bot]' + }} + runs-on: ubuntu-latest + + steps: + + - name: Fetch Dependabot metadata + id: dependabot + uses: dependabot/fetch-metadata@ffa630c65fa7e0ecfa0625b5ceda64399aea1b36 # 3.0.0 + with: + github-token: ${{ github.token }} + + # Creates the data required by the `process-dependabot-reusable` workflow as a JSON file. + - name: Create artifact + shell: bash + env: + UPDATED_DEPENDENCIES: ${{ steps.dependabot.outputs.updated-dependencies-json }} + run: | + echo "$UPDATED_DEPENDENCIES" > updated_dependencies.json + + - name: Upload artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # 7.0.1 + with: + name: dependabot-metadata + path: updated_dependencies.json diff --git a/.github/workflows/process-dependabot-reusable.yaml b/.github/workflows/process-dependabot-reusable.yaml new file mode 100644 index 00000000..b46491a0 --- /dev/null +++ b/.github/workflows/process-dependabot-reusable.yaml @@ -0,0 +1,142 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to you under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +name: Dependabot Process PR + +on: + workflow_call: + inputs: + changelog-path: + description: The path to the changelog directory (e.g. `src/changelog/.2.x.x`) + required: true + type: string + secrets: + RECURSIVE_TOKEN: + description: "A PAT with `contents: write` permission to push changes and trigger the next workflow run" + required: true + +# Explicitly drop all permissions inherited from the caller for security. +# Reference: https://docs.github.com/en/actions/sharing-automations/reusing-workflows#access-and-permissions +permissions: { } + +jobs: + + generate-changelog: + # Defense-in-depth (in case the caller forgets): + # `github.actor` prevents recursive calls when `github-actions[bot]` pushes to the PR; + # `github.event.workflow_run.conclusion` only runs after a successful analysis workflow. + if: ${{ + github.actor == 'dependabot[bot]' + && github.event.workflow_run.conclusion == 'success' + }} + runs-on: ubuntu-latest + permissions: + # The default GITHUB_TOKEN will be used to enable the "auto-merge" on the PR + # This requires the following two permissions: + contents: write + pull-requests: write + + steps: + + - name: Get pull request metadata + id: pr + env: + # Reference of the payload: https://docs.github.com/en/webhooks/webhook-events-and-payloads#workflow_run + # + # The structure of `pull_requests` is not documented, so we'll dump it for debugging purposes. + PULL_REQUESTS: ${{ toJSON(github.event.workflow_run.pull_requests) }} + run: | + # Print payload for debugging + jq <<< "$PULL_REQUESTS" + echo "id=$(echo "$PULL_REQUESTS" | jq -r '.[0].number')" >> "$GITHUB_OUTPUT" + echo "head-ref=$(echo "$PULL_REQUESTS" | jq -r '.[0].head.ref')" >> "$GITHUB_OUTPUT" + + - name: Fetch Dependabot metadata + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # 8.0.1 + with: + github-token: ${{ github.token }} + name: dependabot-metadata + path: ${{ runner.temp }} + run-id: ${{ github.event.workflow_run.id }} + + - name: Check out repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2 + with: + ref: ${{ steps.pr.outputs.head-ref }} + token: ${{ secrets.RECURSIVE_TOKEN }} + + - name: Create changelog entries + shell: bash + env: + PR_ID: ${{ steps.pr.outputs.id }} + PR_URL: ${{ github.server_url }}/${{ github.repository }}/pull/${{ steps.pr.outputs.id }} + CHANGELOG_PATH: ${{ inputs.changelog-path }} + UPDATED_DEPENDENCIES: ${{ runner.temp }}/updated_dependencies.json + run: | + # Escapes special XML characters in a string + xml_escape() { sed 's/&/\&/g; s//\>/g; s/"/\"/g'; } + + # Generates the content of a changelog entry + function generate_changelog_entry() { + local dependency="$1" + local issue_id=$(xml_escape <<< "$PR_ID") + local issue_link=$(xml_escape <<< "$PR_URL") + local dependency_name=$(echo "$dependency" | jq -r '.dependencyName' | xml_escape) + local new_version=$(echo "$dependency" | jq -r '.newVersion' | xml_escape) + cat << CHANGELOG_ENTRY + + + + Update \`$dependency_name\` to version \`$new_version\` + + CHANGELOG_ENTRY + } + + # Ensure the changelog directory exists + mkdir -p "$CHANGELOG_PATH" + cd "$CHANGELOG_PATH" + + # Generate the changelog entries for each updated dependency + cat "$UPDATED_DEPENDENCIES" | jq --compact-output '.[]' | while read -r dependency; do + # Extract the dependency name and version + dependency_name=$(echo "$dependency" | jq -r '.dependencyName') + changelog_file_name=$(echo "update_${dependency_name,,}.xml" | sed -r -e 's/[^a-z0-9.-]/_/g' -e 's/_+/_/g') + generate_changelog_entry "$dependency" > "$changelog_file_name" + done + + - name: Add & commit changes + shell: bash + env: + CHANGELOG_PATH: ${{ inputs.changelog-path }} + PR_ID: ${{ steps.pr.outputs.id }} + run: | + git add "$CHANGELOG_PATH" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git commit -m "Generate changelog entries for #$PR_ID" + git push origin + + - name: Enable auto-merge on PR + shell: bash + env: + GH_TOKEN: ${{ github.token }} + PR_ID: ${{ steps.pr.outputs.id }} + run: | + gh pr merge --squash --auto "$PR_ID" diff --git a/src/changelog/.12.x.x/add-deploy-profile.xml b/src/changelog/.12.x.x/add-deploy-profile.xml new file mode 100644 index 00000000..a34fffff --- /dev/null +++ b/src/changelog/.12.x.x/add-deploy-profile.xml @@ -0,0 +1,10 @@ + + + + + Added `process-dependabot-reusable` to handle Dependabot PRs under RTC restrictions. + + diff --git a/.github/workflows/merge-dependabot.yaml b/src/site/antora/modules/ROOT/examples/analyze-dependabot.yaml similarity index 52% rename from .github/workflows/merge-dependabot.yaml rename to src/site/antora/modules/ROOT/examples/analyze-dependabot.yaml index 2d611cc1..81aefb12 100644 --- a/.github/workflows/merge-dependabot.yaml +++ b/src/site/antora/modules/ROOT/examples/analyze-dependabot.yaml @@ -15,28 +15,23 @@ # limitations under the License. # -name: merge-dependabot +name: "Dependabot Analyze PR" on: - pull_request_target: - paths-ignore: - - "**.adoc" - - "**.md" - - "**.txt" + pull_request: -permissions: read-all +permissions: { } jobs: - build: - if: github.repository == 'apache/logging-parent' && github.event_name == 'pull_request_target' && github.actor == 'dependabot[bot]' - uses: ./.github/workflows/build-reusable.yaml - - merge-dependabot: - needs: build - uses: ./.github/workflows/merge-dependabot-reusable.yaml - permissions: - contents: write # to push changelog commits - pull-requests: write # to close the PR - secrets: - GPG_SECRET_KEY: ${{ secrets.LOGGING_GPG_SECRET_KEY }} # to sign commits +# tag::analyze-dependabot[] + analyze-dependabot: + # `github.actor` prevents recursive calls when `github-actions[bot]` pushes to the PR; + # `github.event.pull_request.user.login` skips PRs not opened by Dependabot. + if: ${{ + github.repository == 'apache/logging-parent' + && github.actor == 'dependabot[bot]' + && github.event.pull_request.user.login == 'dependabot[bot]' + }} + uses: apache/logging-parent/.github/workflows/analyze-dependabot-reusable.yaml@rel/{project-version} +# end::analyze-dependabot[] diff --git a/src/site/antora/modules/ROOT/examples/process-dependabot.yaml b/src/site/antora/modules/ROOT/examples/process-dependabot.yaml new file mode 100644 index 00000000..f40be8fc --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/process-dependabot.yaml @@ -0,0 +1,50 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to you under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +name: "Dependabot Process PR" + +on: + workflow_run: + workflows: + - "Dependabot Analyze PR" + types: + - completed + +permissions: { } + +jobs: + +# tag::process-dependabot[] + process-dependabot: + # Skip this workflow on commits not pushed by Dependabot + if: ${{ + github.repository == 'apache/logging-parent' + && github.actor == 'dependabot[bot]' + && github.event.workflow_run.conclusion == 'success' + }} + uses: apache/logging-parent/.github/workflows/process-dependabot-reusable.yaml@rel/{project-version} + permissions: + # The default GITHUB_TOKEN will be used to enable the "auto-merge" on the PR + # This requires the following two permissions: + contents: write + pull-requests: write + secrets: + RECURSIVE_TOKEN: ${{ secrets.DEPENDABOT_TOKEN }} + with: + # The path to the changelog directory for the current development branch. + changelog-path: src/changelog/.2.x.x +# end::process-dependabot[] diff --git a/src/site/antora/modules/ROOT/pages/workflows.adoc b/src/site/antora/modules/ROOT/pages/workflows.adoc index e9ed9f90..62c64ca2 100644 --- a/src/site/antora/modules/ROOT/pages/workflows.adoc +++ b/src/site/antora/modules/ROOT/pages/workflows.adoc @@ -104,10 +104,64 @@ To verify the reproducibility of a release, you can use: include::example$build.yaml[tag=verify-reproducibility-release,indent=0] ---- -[#merge-dependabot] -== {project-github-url}/blob/main/.github/workflows/merge-dependabot-reusable.yaml[`merge-dependabot-reusable.yaml`] +[#analyze-dependabot] +== {project-github-url}/blob/main/.github/workflows/analyze-dependabot-reusable.yaml[`analyze-dependabot-reusable.yaml`] -Merges Dependabot PRs along with changelog entries. +Analyzes Dependabot pull requests to collect detailed information about updated dependencies. +Stores the results in the `dependabot-metadata` artifact, +which is later consumed by the <> workflow to automate changelog generation and PR processing. + +[NOTE] +==== +This workflow must be triggered by an event that includes the `pull_request` payload and does not require any privileges. +It can then be used in a `pull_request` workflow. +==== + +.Snippet from an {examples-base-link}/analyze-dependabot.yaml[example `analyze-dependabot.yaml`] using this workflow +[source,yaml,subs=+attributes] +---- +include::example$analyze-dependabot.yaml[tag=analyze-dependabot,indent=0] +---- + +[#process-dependabot] +== {project-github-url}/blob/main/.github/workflows/process-dependabot-reusable.yaml[`process-dependabot-reusable.yaml`] + +Helps to process Dependabot pull requests by: + +* Generating changelog entries for the updated dependencies. +* Enabling the "auto-merge" option for the pull request. + +The workflow needs the following privileged tokens: + +`GITHUB_TOKEN`:: +The default GitHub token with `contents:write` and `pull_requests:write` permissions, +used to enable auto-merge on pull requests. ++ +This token is automatically provided by GitHub Actions, but needs to be configured in the `permissions` property. + +`RECURSIVE_TOKEN`:: +A GitHub token required to push generated changelog files as a new commit to the repository. +The default `GITHUB_TOKEN` can **not** be used, +as it will not trigger required check runs and will prevent the pull request from being merged. +A Personal Access Token (PAT) with `contents:write` permission must be provided instead. ++ +The token must be passed as a secret named `RECURSIVE_TOKEN`. + +This workflow is designed to be triggered by the `workflow_run` event, +as soon as the <> workflow completes. + +[NOTE] +==== +When this workflow is triggered by `workflow_run`, +GitHub Actions uses the "Actions" secret context instead of "Dependabot" secrets, +even if the `github.actor` is `dependabot[bot]`. +==== + +.Snippet from an {examples-base-link}/process-dependabot.yaml[example `process-dependabot.yaml`] using this workflow +[source,yaml,subs=+attributes] +---- +include::example$process-dependabot.yaml[tag=process-dependabot,indent=0] +---- [#deploy-site] == {project-github-url}/blob/main/.github/workflows/deploy-site-reusable.yaml[`deploy-site-reusable.yaml`]