From 3b4887983119de0ec4fa44164674e85d150dfaab Mon Sep 17 00:00:00 2001 From: Jon Bartels Date: Mon, 20 Jul 2026 22:02:42 -0400 Subject: [PATCH 1/3] Add Renovate config (Gradle + Docker) + metadata regen workflow Configure Renovate for dependency updates instead of Dependabot: monthly schedule, 2-day minimumReleaseAge, coherent-project grouping (keyed deep so unrelated org.apache.* projects stay separate), ecosystem labels, PR limit, and OSV vulnerability alerts. Because this repo enforces Gradle dependency verification and the metadata file cannot always be regenerated by the bot, add a split-privilege workflow that regenerates gradle/verification-metadata.xml on renovate/** branches: an untrusted build job with a read-only token and no secrets produces the file, and a separate trusted job that runs no dependency code commits it. This isolates the writable token from untrusted execution. Co-Authored-By: Claude Opus 4.8 (1M context) Signed-off-by: Jon Bartels --- .../renovate-verification-metadata.yaml | 87 +++++++++++++++ renovate.json | 100 ++++++++++++++++++ 2 files changed, 187 insertions(+) create mode 100644 .github/workflows/renovate-verification-metadata.yaml create mode 100644 renovate.json diff --git a/.github/workflows/renovate-verification-metadata.yaml b/.github/workflows/renovate-verification-metadata.yaml new file mode 100644 index 000000000..30f3bdff7 --- /dev/null +++ b/.github/workflows/renovate-verification-metadata.yaml @@ -0,0 +1,87 @@ +name: Renovate Verification Metadata + +# Fallback for Renovate Gradle PRs: if Renovate (Mend-hosted) does not itself regenerate +# gradle/verification-metadata.xml (its command allowlist / task coverage / PGP handling is +# unconfirmed for this repo), this workflow does it. See the Phase 3 validation gate in the +# migration plan — delete this file once Renovate is confirmed to regenerate the metadata +# (incl. PGP + full config coverage) on its own. +# +# Split privilege: the untrusted build runs in a read-only job with no secrets (it executes +# newly-bumped third-party code), and a separate trusted job that runs NO dependency code +# commits the result. The writable token never coexists with untrusted execution. + +on: + push: + branches: + - "renovate/**" + +# No workflow-level permissions; each job requests the minimum it needs. +permissions: {} + +concurrency: + group: renovate-metadata-${{ github.ref }} + cancel-in-progress: true + +jobs: + regen: + # Runs the untrusted, newly-bumped dependency code (build + tests + plugins). + # Read-only token, no secrets: nothing to steal, cannot push. + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + java-version: '17' + java-package: 'jdk+fx' + distribution: 'zulu' + + - name: Set up Gradle + uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4.4.3 + with: + validate-wrappers: true + + # Cold cache + build dist for full config coverage (a lighter task under-resolves + # configs; see CONTRIBUTING.md). + - name: Regenerate verification metadata (cold cache) + run: | + GRADLE_USER_HOME="$(mktemp -d)" ./gradlew --write-verification-metadata sha256 \ + build dist -PdisableSigning=true -Pcoverage=true + + - name: Upload refreshed metadata + uses: actions/upload-artifact@v4 + with: + name: verification-metadata + path: gradle/verification-metadata.xml + if-no-files-found: error + + commit: + # Trusted: runs NO dependency code. Only takes the artifact produced above and pushes it. + needs: regen + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + + - name: Download refreshed metadata + uses: actions/download-artifact@v4 + with: + name: verification-metadata + path: ${{ runner.temp }}/metadata + + - name: Commit if changed + run: | + cp "${RUNNER_TEMP}/metadata/verification-metadata.xml" gradle/verification-metadata.xml + if git diff --quiet -- gradle/verification-metadata.xml; then + echo "No verification metadata changes to commit." + exit 0 + fi + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git commit -m "Regenerate Gradle dependency verification metadata" \ + -- gradle/verification-metadata.xml + git push origin "HEAD:${GITHUB_REF_NAME}" diff --git a/renovate.json b/renovate.json new file mode 100644 index 000000000..d0fe35676 --- /dev/null +++ b/renovate.json @@ -0,0 +1,100 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ], + "schedule": [ + "before 6am on the first day of the month" + ], + "minimumReleaseAge": "2 days", + "prConcurrentLimit": 10, + "dependencyDashboard": true, + "semanticCommits": "enabled", + "labels": [ + "dependencies" + ], + "osvVulnerabilityAlerts": true, + "packageRules": [ + { + "description": "Ecosystem labels", + "matchManagers": [ + "gradle" + ], + "addLabels": [ + "java" + ] + }, + { + "matchManagers": [ + "dockerfile" + ], + "addLabels": [ + "docker" + ] + }, + { + "description": "Catch-all: bundle minor/patch of ungrouped deps; majors stay individual. Listed BEFORE the named groups so a family's own minor/patch is overridden back into its family group (Renovate merges rules, last match wins for groupName).", + "matchUpdateTypes": [ + "minor", + "patch" + ], + "groupName": "all-minor-and-patch" + }, + { + "groupName": "aws-sdk", + "matchPackageNames": [ + "software.amazon.awssdk:*", + "software.amazon.eventstream:*" + ] + }, + { + "groupName": "jetty", + "matchPackageNames": [ + "org.eclipse.jetty*" + ] + }, + { + "groupName": "jersey", + "matchPackageNames": [ + "org.glassfish.jersey*", + "org.glassfish.hk2*" + ] + }, + { + "groupName": "jackson", + "matchPackageNames": [ + "com.fasterxml.jackson*" + ] + }, + { + "groupName": "netty", + "matchPackageNames": [ + "io.netty*", + "com.typesafe.netty*" + ] + }, + { + "groupName": "log4j", + "matchPackageNames": [ + "org.apache.logging.log4j*" + ] + }, + { + "groupName": "apache-commons", + "matchPackageNames": [ + "org.apache.commons:*", + "commons-*" + ] + }, + { + "groupName": "test-tooling", + "matchPackageNames": [ + "org.mockito*", + "net.bytebuddy*", + "org.objenesis:*", + "org.hamcrest:*", + "junit:*" + ] + } + ] +} From e53d7d3d2558c4b3d6cc148e07142d10e24b63fe Mon Sep 17 00:00:00 2001 From: Jon Bartels Date: Wed, 22 Jul 2026 09:24:32 -0400 Subject: [PATCH 2/3] Tune Renovate: PR hourly limit, major-update approval, timezone Add prHourlyLimit to avoid PR bursts, require Dependency Dashboard approval before major-version PRs open, and set the schedule timezone. Explicitly set automerge=false; we never want automerge. Co-Authored-By: Claude Opus 4.8 (1M context) Signed-off-by: Jon Bartels --- renovate.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/renovate.json b/renovate.json index d0fe35676..51449912d 100644 --- a/renovate.json +++ b/renovate.json @@ -6,8 +6,14 @@ "schedule": [ "before 6am on the first day of the month" ], + "timezone": "America/New_York", "minimumReleaseAge": "2 days", "prConcurrentLimit": 10, + "prHourlyLimit": 2, + "automerge": false, + "major": { + "dependencyDashboardApproval": true + }, "dependencyDashboard": true, "semanticCommits": "enabled", "labels": [ From 73213dd195ed2afc9d06fa9c2bbc4a98812e8022 Mon Sep 17 00:00:00 2001 From: Jon Bartels Date: Wed, 22 Jul 2026 16:48:51 -0400 Subject: [PATCH 3/3] Drop custom verification-metadata workflow; Renovate maintains it Verified empirically on a live Mend-hosted fork: when its PR branch is not externally modified, Renovate runs ./gradlew --write-verification-metadata and commits the refreshed gradle/verification-metadata.xml itself (jetty-http 10.0.0: jar+pom+parent checksums authored by renovate[bot], no workflow present). The custom workflow only appeared necessary because its commit raced Renovate's on push; removing it lets Renovate self-maintain. Co-Authored-By: Claude Opus 4.8 (1M context) Signed-off-by: Jon Bartels --- .../renovate-verification-metadata.yaml | 87 ------------------- 1 file changed, 87 deletions(-) delete mode 100644 .github/workflows/renovate-verification-metadata.yaml diff --git a/.github/workflows/renovate-verification-metadata.yaml b/.github/workflows/renovate-verification-metadata.yaml deleted file mode 100644 index 30f3bdff7..000000000 --- a/.github/workflows/renovate-verification-metadata.yaml +++ /dev/null @@ -1,87 +0,0 @@ -name: Renovate Verification Metadata - -# Fallback for Renovate Gradle PRs: if Renovate (Mend-hosted) does not itself regenerate -# gradle/verification-metadata.xml (its command allowlist / task coverage / PGP handling is -# unconfirmed for this repo), this workflow does it. See the Phase 3 validation gate in the -# migration plan — delete this file once Renovate is confirmed to regenerate the metadata -# (incl. PGP + full config coverage) on its own. -# -# Split privilege: the untrusted build runs in a read-only job with no secrets (it executes -# newly-bumped third-party code), and a separate trusted job that runs NO dependency code -# commits the result. The writable token never coexists with untrusted execution. - -on: - push: - branches: - - "renovate/**" - -# No workflow-level permissions; each job requests the minimum it needs. -permissions: {} - -concurrency: - group: renovate-metadata-${{ github.ref }} - cancel-in-progress: true - -jobs: - regen: - # Runs the untrusted, newly-bumped dependency code (build + tests + plugins). - # Read-only token, no secrets: nothing to steal, cannot push. - runs-on: ubuntu-latest - permissions: - contents: read - steps: - - uses: actions/checkout@v4 - - - name: Set up JDK - uses: actions/setup-java@v4 - with: - java-version: '17' - java-package: 'jdk+fx' - distribution: 'zulu' - - - name: Set up Gradle - uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4.4.3 - with: - validate-wrappers: true - - # Cold cache + build dist for full config coverage (a lighter task under-resolves - # configs; see CONTRIBUTING.md). - - name: Regenerate verification metadata (cold cache) - run: | - GRADLE_USER_HOME="$(mktemp -d)" ./gradlew --write-verification-metadata sha256 \ - build dist -PdisableSigning=true -Pcoverage=true - - - name: Upload refreshed metadata - uses: actions/upload-artifact@v4 - with: - name: verification-metadata - path: gradle/verification-metadata.xml - if-no-files-found: error - - commit: - # Trusted: runs NO dependency code. Only takes the artifact produced above and pushes it. - needs: regen - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - uses: actions/checkout@v4 - - - name: Download refreshed metadata - uses: actions/download-artifact@v4 - with: - name: verification-metadata - path: ${{ runner.temp }}/metadata - - - name: Commit if changed - run: | - cp "${RUNNER_TEMP}/metadata/verification-metadata.xml" gradle/verification-metadata.xml - if git diff --quiet -- gradle/verification-metadata.xml; then - echo "No verification metadata changes to commit." - exit 0 - fi - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git commit -m "Regenerate Gradle dependency verification metadata" \ - -- gradle/verification-metadata.xml - git push origin "HEAD:${GITHUB_REF_NAME}"