diff --git a/.github/workflows/analyze-dependabot-reusable.yaml b/.github/workflows/analyze-dependabot-reusable.yaml new file mode 100644 index 00000000..a23fad51 --- /dev/null +++ b/.github/workflows/analyze-dependabot-reusable.yaml @@ -0,0 +1,55 @@ +# +# 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: + +jobs: + + analyze-pull-request: + # Skip this workflow on commits not pushed by Dependabot + if: ${{ github.actor == 'dependabot[bot]' }} + runs-on: ubuntu-latest + + steps: + + - name: Fetch Dependabot metadata + id: dependabot + uses: ppkarwasz/fetch-metadata@feat/multi-versions + with: + github-token: ${{ github.token }} + + # + # Stores the data required by the process-dependabot-reusable workflow as JSON files. + # + - name: Create artifacts + shell: bash + env: + PULL_REQUEST: ${{ toJSON(github.event.pull_request) }} + UPDATED_DEPENDENCIES: ${{ steps.dependabot.outputs.updated-dependencies-json }} + run: | + mkdir -p dependabot-metadata + echo "$PULL_REQUEST" > dependabot-metadata/pull_request.json + echo "$UPDATED_DEPENDENCIES" > dependabot-metadata/updated_dependencies.json + + - name: Upload artifacts + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # 4.6.2 + with: + name: dependabot-metadata + path: dependabot-metadata diff --git a/.github/workflows/process-dependabot-reusable.yaml b/.github/workflows/process-dependabot-reusable.yaml new file mode 100644 index 00000000..e2b18c9d --- /dev/null +++ b/.github/workflows/process-dependabot-reusable.yaml @@ -0,0 +1,169 @@ +# +# 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: + user-name: + description: The name of the user to use for the commit + default: 'ASF Logging Services RM' + type: string + user-email: + description: The email of the user to use for the commit + default: 'private@logging.apache.org' + type: string + analyze-workflow-run-id: + description: The ID of the workflow run that analyzed the PR + required: true + type: number + secrets: + RECURSIVE_TOKEN: + description: "A PAT with `contents: write` permission to push changes and trigger the next workflow run" + required: true + GPG_PASSPHRASE: + description: GPG passphrase for signing commits + required: false + GPG_PRIVATE_KEY: + description: GPG secret key for signing commits + required: true + +permissions: { } + +jobs: + + generate-changelog: + # Skip this workflow on commits not pushed by Dependabot + if: ${{ github.actor == 'dependabot[bot]' }} + runs-on: ubuntu-latest + + steps: + + - name: Fetch Dependabot metadata + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # 4.3.0 + with: + github-token: ${{ github.token }} + name: dependabot-metadata + path: ${{ runner.temp }}/dependabot-metadata + run-id: ${{ inputs.analyze-workflow-run-id }} + + - name: Process Dependabot metadata + shell: bash + run: | + # Extract the pull request metadata from the downloaded artifact + path="$RUNNER_TEMP/dependabot-metadata" + if [[ ! -f "$path/pull_request.json" ]]; then + echo "Pull request metadata not found at $path/pull_request.json" + exit 1 + fi + if [[ ! -f "$path/updated_dependencies.json" ]]; then + echo "Updated dependencies metadata not found at $path/updated_dependencies.json" + exit 1 + fi + # Extract the required metadata and set it as environment variables + pull_request="$path/pull_request.json" + echo "PR_ID=$(jq -r '.number' < "$pull_request")" >> $GITHUB_ENV + echo "PR_URL=$(jq -r '.html_url' < "$pull_request")" >> $GITHUB_ENV + echo "PR_HEAD_REF=$(jq -r '.head.ref' < "$pull_request")" >> $GITHUB_ENV + + - name: Check out repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2 + with: + ref: ${{ env.PR_HEAD_REF }} + token: ${{ secrets.RECURSIVE_TOKEN }} + + - name: Install `xmlstarlet` + shell: bash + run: sudo apt-get update && sudo apt-get install -y xmlstarlet + + - name: Find the release version major + shell: bash + run: | + revision=$( + xmlstarlet sel \ + -N m=http://maven.apache.org/POM/4.0.0 \ + --template --value-of /m:project/m:properties/m:revision \ + pom.xml + ) + if [[ ! $revision =~ ^[0-9]+\.[0-9]+\.[0-9]+(-SNAPSHOT)?$ ]]; then + echo "Invalid version format: $revision" + exit 1 + fi + parts=(${revision//./ }) + echo "RELEASE_VERSION_MAJOR=${parts[0]}" >> $GITHUB_ENV + + - name: Create changelog entries + shell: bash + run: | + PULL_REQUEST="$RUNNER_TEMP/dependabot-metadata/pull_request.json" + UPDATED_DEPENDENCIES="$RUNNER_TEMP/dependabot-metadata/updated_dependencies.json" + # Generates the content of a changelog entry + function generate_changelog_entry() { + local dependency="$1" + local issue_id=$(xmlstarlet esc "$PR_ID") + local issue_link=$(xmlstarlet esc "$PR_URL") + local dependency_name=$(echo "$dependency" | jq -r '.dependencyName' | xmlstarlet esc) + local new_version=$(echo "$dependency" | jq -r '.newVersion' | xmlstarlet esc) + cat << CHANGELOG_ENTRY + + + + + Update \`$dependency_name\` to version \`$new_version\`. + + CHANGELOG_ENTRY + } + # Ensure the changelog directory exists + release_changelog_path="src/changelog/.${RELEASE_VERSION_MAJOR}.x.x" + mkdir -p "$release_changelog_path" + cd "$release_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: Set up GPG + uses: crazy-max/ghaction-import-gpg@e89d40939c28e39f97cf32126055eeae86ba74ec # 6.3.0 + with: + gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} + passphrase: ${{ secrets.GPG_PASSPHRASE }} + + - name: Add & commit changes + shell: bash + env: + USER_NAME: ${{ inputs.user-name }} + USER_EMAIL: ${{ inputs.user-email }} + run: | + git add src/changelog + git config user.name "$USER_NAME" + git config user.email "$USER_EMAIL" + git commit -S -m "Generate changelog entries for PR #$PR_ID" + git push origin + + - name: Enable auto-merge on PR + shell: bash + env: + GH_TOKEN: ${{ github.token }} + run: | + gh pr merge --squash --auto "$PR_URL" 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..c4a850c7 100644 --- a/.github/workflows/merge-dependabot.yaml +++ b/src/site/antora/modules/ROOT/examples/analyze-dependabot.yaml @@ -15,28 +15,18 @@ # 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: + # Skip this workflow on commits not pushed by Dependabot + if: ${{ github.actor == '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..16503cab --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/process-dependabot.yaml @@ -0,0 +1,52 @@ +# +# 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.event.workflow_run.conclusion == 'success' && github.actor == 'dependabot[bot]' }} + 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 }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} + with: + # These are the default values. + # The e-mail address must match the one used in the GPG key. + user_name: "ASF Logging Services RM" + user_email: "private@logging.apache.org" + # The run ID of the workflow that analyzed the PR. + analyze-workflow-run-id: ${{ github.event.workflow_run.id }} +# 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`]