Skip to content

Update dependency @eslint-react/eslint-plugin to v5.9.1 #1063

Update dependency @eslint-react/eslint-plugin to v5.9.1

Update dependency @eslint-react/eslint-plugin to v5.9.1 #1063

Workflow file for this run

name: Refresh SBOM for Dependency PRs
# Auto-refreshes the cached CycloneDX SBOMs (`client/sbom/`, `server/sbom/`)
# on dependency-update PRs opened by Renovate or Dependabot. Without this,
# every bot PR fails the `Verify ... SBOM is up-to-date` checks in
# `run_tests.yml` because the bots only update `package.json` /
# `pnpm-lock.yaml` (or `build.gradle` / `gradle.properties` /
# `settings.gradle`) — they don't run `pnpm run sbom:force` or
# `./gradlew cyclonedxBom`.
#
# Event choice: pull_request_target
# ---------------------------------
# We use `pull_request_target` (not `pull_request`) so this workflow can
# access repo secrets when triggered by Dependabot — `pull_request` events
# from Dependabot don't expose Actions secrets by default. The workflow
# definition is read from the BASE branch (safe from PR tampering), but the
# checked-out code is the PR head, so we add a same-repo guard
# (`head.repo.full_name == github.repository`) to ensure we only run on
# branches Renovate/Dependabot pushed into THIS repo (they never use forks).
#
# Token requirement
# -----------------
# We deliberately do NOT push with the default `GITHUB_TOKEN`: pushes signed
# by `GITHUB_TOKEN` are excluded from triggering further workflow runs, so
# the failed test workflow on the PR would stay red even after the SBOM
# commit lands. Instead we either:
# 1. Mint a least-privilege installation token from a GitHub App
# (preferred), via the `SBOM_BOT_APP_ID` repo/org variable and the
# `SBOM_BOT_PRIVATE_KEY` secret. Scoped to `contents: write` on the
# single PR head repo.
# 2. Fall back to a fine-grained PAT in `SBOM_BOT_PAT`.
#
# Credential hygiene
# ------------------
# Checkout runs with the default (read-only) `GITHUB_TOKEN` and
# `persist-credentials: false`, so the write-capable App/PAT token is
# never written into the runner's git config. The push step authenticates
# explicitly via a one-shot URL so the token can't be picked up by
# subsequent steps or build-script lifecycle hooks.
on:
pull_request_target:
branches: [develop, main]
types: [opened, synchronize, reopened]
paths:
- 'client/package.json'
- 'client/pnpm-lock.yaml'
- 'server/build.gradle'
- 'server/gradle.properties'
- 'server/settings.gradle'
permissions:
contents: read
concurrency:
group: refresh-sbom-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
refresh:
# Only run on bot-opened PRs that target THIS repo (no forks). The
# same-repo check is defense-in-depth: Renovate and Dependabot always
# push into the base repo on their own branches.
if: >
(github.event.pull_request.user.login == 'renovate[bot]' ||
github.event.pull_request.user.login == 'dependabot[bot]') &&
github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
steps:
# GitHub disallows the `secrets` context inside step-level `if:`
# expressions (only env/github/inputs/job/matrix/needs/runner/steps/
# strategy/vars are permitted there) — referencing it directly causes
# the entire workflow to fail to load. Bounce the secret presence
# check through an env var so the gate is expressible.
- name: Detect App token availability
id: app-token-config
env:
APP_PRIVATE_KEY: ${{ secrets.SBOM_BOT_PRIVATE_KEY }}
APP_ID: ${{ vars.SBOM_BOT_APP_ID }}
run: |
if [ -n "${APP_ID:-}" ] && [ -n "${APP_PRIVATE_KEY:-}" ]; then
echo "available=true" >> "$GITHUB_OUTPUT"
else
echo "available=false" >> "$GITHUB_OUTPUT"
fi
- name: Mint GitHub App token (preferred)
id: app-token
if: steps.app-token-config.outputs.available == 'true'
uses: actions/create-github-app-token@v3
with:
app-id: ${{ vars.SBOM_BOT_APP_ID }}
private-key: ${{ secrets.SBOM_BOT_PRIVATE_KEY }}
# Scope to least privilege: only `contents: write` on the PR head repo.
owner: ${{ github.event.pull_request.head.repo.owner.login }}
repositories: ${{ github.event.pull_request.head.repo.name }}
permission-contents: write
- name: Pick push token (App token > PAT fallback)
id: token
env:
APP_TOKEN: ${{ steps.app-token.outputs.token }}
PAT_TOKEN: ${{ secrets.SBOM_BOT_PAT }}
run: |
if [ -n "${APP_TOKEN:-}" ]; then
echo "source=app" >> "$GITHUB_OUTPUT"
echo "::add-mask::$APP_TOKEN"
echo "value=$APP_TOKEN" >> "$GITHUB_OUTPUT"
elif [ -n "${PAT_TOKEN:-}" ]; then
echo "source=pat" >> "$GITHUB_OUTPUT"
echo "::add-mask::$PAT_TOKEN"
echo "value=$PAT_TOKEN" >> "$GITHUB_OUTPUT"
else
echo "::error::Neither SBOM_BOT_APP_ID/SBOM_BOT_PRIVATE_KEY nor SBOM_BOT_PAT is configured. See .github/workflows/refresh_sbom.yml header for setup details."
exit 1
fi
- name: Checkout PR branch (read-only, no token persisted)
uses: actions/checkout@v7
with:
# pull_request_target defaults to the base ref; we want the PR head.
ref: ${{ github.event.pull_request.head.sha }}
# No `token:` here — default GITHUB_TOKEN is sufficient for reading
# this same repo, and `persist-credentials: false` ensures nothing
# writable is left in the git config for later steps (e.g. Gradle
# build scripts or pnpm lifecycle hooks) to pick up.
persist-credentials: false
# Full history so `git diff "$BASE_SHA"...HEAD` always resolves the
# PR base, even on long-lived branches where a shallow clone might
# not include it.
fetch-depth: 0
- name: Detect which SBOM(s) need refreshing
id: scopes
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: |
changed=$(git diff --name-only "$BASE_SHA"...HEAD)
client=false
server=false
if echo "$changed" | grep -qE '^client/(package\.json|pnpm-lock\.yaml)$'; then
client=true
fi
if echo "$changed" | grep -qE '^server/(build\.gradle|gradle\.properties|settings\.gradle)$'; then
server=true
fi
echo "client=$client" >> "$GITHUB_OUTPUT"
echo "server=$server" >> "$GITHUB_OUTPUT"
echo "Client SBOM refresh needed: $client"
echo "Server SBOM refresh needed: $server"
# ---------- Server SBOM ----------
- name: Set up JDK 25
if: steps.scopes.outputs.server == 'true'
uses: actions/setup-java@v5
with:
java-version: '25'
distribution: 'zulu'
cache: 'gradle'
- name: Regenerate server SBOM
if: steps.scopes.outputs.server == 'true'
working-directory: ./server
run: |
chmod +x ./gradlew
./gradlew cyclonedxBom
# ---------- Client SBOM ----------
# `pnpm sbom` reads the lockfile directly, so we deliberately do NOT run
# `pnpm install` — skipping it both saves time and avoids running
# dependency lifecycle scripts. Corepack provides pnpm itself.
- name: Enable Corepack
if: steps.scopes.outputs.client == 'true'
run: corepack enable
- name: Set up Node.js
if: steps.scopes.outputs.client == 'true'
uses: actions/setup-node@v6
with:
node-version: '24.17.0'
- name: Regenerate client SBOM
if: steps.scopes.outputs.client == 'true'
working-directory: ./client
run: pnpm run sbom:force
- name: Commit and push if SBOM changed
env:
PUSH_TOKEN: ${{ steps.token.outputs.value }}
PR_BRANCH: ${{ github.event.pull_request.head.ref }}
PR_REPO: ${{ github.event.pull_request.head.repo.full_name }}
run: |
if git diff --quiet -- client/sbom server/sbom; then
echo "SBOM already current — nothing to commit. This typically means the workflow re-ran on its own commit; safe to skip."
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add client/sbom server/sbom
git commit -m "chore: refresh SBOM after dependency update"
# Authenticate the push via a one-shot URL rather than persisting
# the write token into git config. The token is masked in logs.
git push "https://x-access-token:${PUSH_TOKEN}@github.com/${PR_REPO}.git" "HEAD:${PR_BRANCH}"