From f81180732e620c544ed05113dedbcd2500207e85 Mon Sep 17 00:00:00 2001 From: Marnix van Lieshout Date: Mon, 28 Sep 2026 09:55:45 +0200 Subject: [PATCH 01/13] ci: add uv audit workflow for automated vulnerability patching Assisted-by: GitHub Copilot Signed-off-by: Marnix van Lieshout --- .github/workflows/_job_uv_audit.yaml | 104 +++++++++++++++++++++++++++ .github/workflows/uv-audit.yaml | 20 ++++++ 2 files changed, 124 insertions(+) create mode 100644 .github/workflows/_job_uv_audit.yaml create mode 100644 .github/workflows/uv-audit.yaml diff --git a/.github/workflows/_job_uv_audit.yaml b/.github/workflows/_job_uv_audit.yaml new file mode 100644 index 000000000..62674ec46 --- /dev/null +++ b/.github/workflows/_job_uv_audit.yaml @@ -0,0 +1,104 @@ +# SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project +# +# SPDX-License-Identifier: MPL-2.0 + +name: uv Audit and Targeted Upgrade +on: + workflow_call: + secrets: + token: + description: "Token used to push the fix branch and open the PR. Defaults to GITHUB_TOKEN." + required: false + +jobs: + uv-audit: + name: uv Audit + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - name: Checkout # Must be done before using composite actions + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + show-progress: false # very verbose for not much + token: ${{ secrets.token || secrets.GITHUB_TOKEN }} + + - name: Install uv + uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0 + with: + activate-environment: true + enable-cache: true + + - name: Install dependencies + run: uv sync --frozen --all-groups + + - name: Run uv audit + id: audit + shell: bash + run: | + uv audit --preview-features audit-command --preview-features malware-check \ + --output-format json > uv_audit_report.json || true + + UPGRADE=$(jq -r '[.vulnerabilities[]? | select(.fix_versions | length > 0) | .dependency.name] | unique | .[]' uv_audit_report.json) + NO_FIX=$(jq -r '[.vulnerabilities[]? | select(.fix_versions | length == 0) | .dependency.name] | unique | .[]' uv_audit_report.json) + + echo "Upgrade: $UPGRADE" + echo "No fix available: $NO_FIX" + + if [ -z "$UPGRADE" ]; then + echo "No packages to upgrade." + echo "changed=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + ARGS=() + for pkg in $UPGRADE; do + ARGS+=(--upgrade-package "$pkg") + done + + uv lock "${ARGS[@]}" | tee uv_audit_upgrade.txt + + if git diff --quiet -- uv.lock; then + echo "changed=false" >> "$GITHUB_OUTPUT" + else + echo "changed=true" >> "$GITHUB_OUTPUT" + fi + + - name: Build PR body + if: steps.audit.outputs.changed == 'true' + run: | + { + echo "## uv audit — targeted dependency fixes" + if [ -f uv_audit_upgrade.txt ]; then + echo "### uv lock output" + echo '```' + cat uv_audit_upgrade.txt + echo '```' + fi + } > pr_body.md + + - name: Commit and push branch + if: steps.audit.outputs.changed == 'true' + run: | + BRANCH="automated/uv-audit-fix-$(date +%Y%m%d%H%M%S)" + echo "BRANCH=$BRANCH" >> "$GITHUB_ENV" + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git checkout -b "$BRANCH" + git add uv.lock uv_audit_report.json + [ -f uv_audit_upgrade.txt ] && git add uv_audit_upgrade.txt + git commit -m "fix(deps): patch vulnerable packages found by uv audit" + git push origin "$BRANCH" + + - name: Create pull request + if: steps.audit.outputs.changed == 'true' + env: + GH_TOKEN: ${{ secrets.token || secrets.GITHUB_TOKEN }} + run: | + gh pr create \ + --title "security: uv audit targeted dependency fixes" \ + --body-file pr_body.md \ + --base "${{ github.ref_name }}" \ + --head "$BRANCH" \ + --label security --label dependencies --label automated diff --git a/.github/workflows/uv-audit.yaml b/.github/workflows/uv-audit.yaml new file mode 100644 index 000000000..35435a08d --- /dev/null +++ b/.github/workflows/uv-audit.yaml @@ -0,0 +1,20 @@ +# SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project +# +# SPDX-License-Identifier: MPL-2.0 + +name: uv Audit + +on: + schedule: + - cron: '0 6 * * *' # Every day at 06:00 UTC + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +jobs: + uv-audit: + name: uv Audit + uses: ./.github/workflows/_job_uv_audit.yaml + secrets: inherit From 2b44a2bbbd8e182bf1b7c661e5a45ccdf6a9eb7e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Sep 2026 10:05:47 +0200 Subject: [PATCH 02/13] chore(deps)(deps): bump holidays from 0.104 to 0.105 in the python-versions group across 1 directory (#1104) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the python-versions group with 1 update in the / directory: [holidays](https://github.com/vacanza/holidays). Updates `holidays` from 0.104 to 0.105
Release notes

Sourced from holidays's releases.

v0.105

Version 0.105

Released September 21, 2026

New Contributors:

Full Changelog: https://github.com/vacanza/holidays/compare/v0.104...v0.105

Changelog

Sourced from holidays's changelog.

Version 0.105

Released September 21, 2026

Commits
  • 532c3ce Merge pull request #3833 from vacanza/dev
  • 4ca8b83 Finalize v0.105
  • cc9c9bc Add support for special holidays inheritance from parent entity (#3830)
  • 636ac52 chore: Update snapshots (#3831)
  • bd3114d Update India holidays: subdiv holidays improvement (East, North-East & South ...
  • 9490dfe Update Indonesia holidays: add 2027 special holidays (#3828)
  • ba5f6b9 Bump the version-updates group with 3 updates (#3824)
  • a85303a Bump the version-updates group with 2 updates (#3825)
  • 899cfe4 Bump the version-updates group with 2 updates (#3826)
  • efab764 chore: Update snapshots (#3823)
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=holidays&package-manager=uv&previous-version=0.104&new-version=0.105)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- uv.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uv.lock b/uv.lock index 9e610753c..348dac2b4 100644 --- a/uv.lock +++ b/uv.lock @@ -2154,14 +2154,14 @@ wheels = [ [[package]] name = "holidays" -version = "0.104" +version = "0.105" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/71/23/9d0f6c31dda49b50efecdac7b76d979fa37b6267e55bab4907a58f02e8ac/holidays-0.104.tar.gz", hash = "sha256:7e327b255dee3de3c8f7fef58f835ccb7685881ea72176697ff60269c57743b2", size = 1021650, upload-time = "2026-09-07T17:49:30.506Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/b7/b0563bf091ff8089a8a4299068236d277c299e13b5809afeaa99b0c11280/holidays-0.105.tar.gz", hash = "sha256:fc9abc0c187b62e955f92aa12dbe7ed1998cc94712f295ec9244db788594d662", size = 1039659, upload-time = "2026-09-21T21:32:01.423Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/63/63/e6d7af3e7cd7ce095aee210204fce64b2ce5164c0c3c79d70e335837886c/holidays-0.104-py3-none-any.whl", hash = "sha256:e0db9ff1d5588e3349bf2de6a597421e39cb7299b64a015170a0a0886940c384", size = 1619592, upload-time = "2026-09-07T17:49:28.243Z" }, + { url = "https://files.pythonhosted.org/packages/9e/d0/dbc6c50f190ed0c32e4ebca037bff252280b0707049ee95825f4e8c3cc19/holidays-0.105-py3-none-any.whl", hash = "sha256:7fbd87770f1128eb24514fbb4c3ec6afa1c72d65c346713dbc13c2d0297df481", size = 1637790, upload-time = "2026-09-21T21:31:59.345Z" }, ] [[package]] From b8ef7d1dbd39b4dc16ba0cea005797d777f61143 Mon Sep 17 00:00:00 2001 From: Marnix van Lieshout Date: Mon, 28 Sep 2026 10:13:29 +0200 Subject: [PATCH 03/13] fix: use correct uv audit preview features, drop nonexistent malware-check flag Assisted-by: GitHub Copilot Signed-off-by: Marnix van Lieshout --- .github/workflows/_job_uv_audit.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/_job_uv_audit.yaml b/.github/workflows/_job_uv_audit.yaml index 62674ec46..499acb725 100644 --- a/.github/workflows/_job_uv_audit.yaml +++ b/.github/workflows/_job_uv_audit.yaml @@ -37,8 +37,7 @@ jobs: id: audit shell: bash run: | - uv audit --preview-features audit-command --preview-features malware-check \ - --output-format json > uv_audit_report.json || true + uv audit --preview-features audit,json-output --output-format json > uv_audit_report.json || true UPGRADE=$(jq -r '[.vulnerabilities[]? | select(.fix_versions | length > 0) | .dependency.name] | unique | .[]' uv_audit_report.json) NO_FIX=$(jq -r '[.vulnerabilities[]? | select(.fix_versions | length == 0) | .dependency.name] | unique | .[]' uv_audit_report.json) From 58c58e129cb1a50e27aaa11c894a5786626cf72d Mon Sep 17 00:00:00 2001 From: Marnix van Lieshout Date: Mon, 28 Sep 2026 10:21:09 +0200 Subject: [PATCH 04/13] fix: reuse a stable branch/PR for uv audit fixes instead of flooding with new PRs Assisted-by: GitHub Copilot Signed-off-by: Marnix van Lieshout --- .github/workflows/_job_uv_audit.yaml | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/.github/workflows/_job_uv_audit.yaml b/.github/workflows/_job_uv_audit.yaml index 499acb725..8b4eedf09 100644 --- a/.github/workflows/_job_uv_audit.yaml +++ b/.github/workflows/_job_uv_audit.yaml @@ -80,24 +80,33 @@ jobs: - name: Commit and push branch if: steps.audit.outputs.changed == 'true' run: | - BRANCH="automated/uv-audit-fix-$(date +%Y%m%d%H%M%S)" + # Fixed branch name: reused/force-pushed each run so we update one PR instead of piling up new ones. + BRANCH="automated/uv-audit-fix" echo "BRANCH=$BRANCH" >> "$GITHUB_ENV" git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - git checkout -b "$BRANCH" + git checkout -B "$BRANCH" git add uv.lock uv_audit_report.json [ -f uv_audit_upgrade.txt ] && git add uv_audit_upgrade.txt git commit -m "fix(deps): patch vulnerable packages found by uv audit" - git push origin "$BRANCH" + git push --force origin "$BRANCH" - name: Create pull request if: steps.audit.outputs.changed == 'true' env: GH_TOKEN: ${{ secrets.token || secrets.GITHUB_TOKEN }} run: | - gh pr create \ - --title "security: uv audit targeted dependency fixes" \ - --body-file pr_body.md \ - --base "${{ github.ref_name }}" \ - --head "$BRANCH" \ - --label security --label dependencies --label automated + PR_NUMBER=$(gh pr list --head "$BRANCH" --state open --json number --jq '.[0].number // empty') + + if [ -n "$PR_NUMBER" ]; then + gh pr edit "$PR_NUMBER" \ + --title "security: uv audit targeted dependency fixes" \ + --body-file pr_body.md + else + gh pr create \ + --title "security: uv audit targeted dependency fixes" \ + --body-file pr_body.md \ + --base "${{ github.ref_name }}" \ + --head "$BRANCH" \ + --label security --label dependencies --label automated + fi From a93e1ee1c25f631a85a2de10d118281cbd36e264 Mon Sep 17 00:00:00 2001 From: Marnix van Lieshout Date: Mon, 28 Sep 2026 10:27:15 +0200 Subject: [PATCH 05/13] fix: pause automation if the audit branch was manually edited (Dependabot-style) Assisted-by: GitHub Copilot Signed-off-by: Marnix van Lieshout --- .github/workflows/_job_uv_audit.yaml | 30 ++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/.github/workflows/_job_uv_audit.yaml b/.github/workflows/_job_uv_audit.yaml index 8b4eedf09..f6d70e019 100644 --- a/.github/workflows/_job_uv_audit.yaml +++ b/.github/workflows/_job_uv_audit.yaml @@ -64,8 +64,29 @@ jobs: echo "changed=true" >> "$GITHUB_OUTPUT" fi - - name: Build PR body + - name: Check for manual edits on automated branch + id: guard if: steps.audit.outputs.changed == 'true' + run: | + # Fixed branch name: reused/force-pushed each run so we update one PR instead of piling up new ones. + BRANCH="automated/uv-audit-fix" + echo "BRANCH=$BRANCH" >> "$GITHUB_ENV" + + git fetch origin "$BRANCH" 2>/dev/null || true + + if git rev-parse --verify "origin/$BRANCH" >/dev/null 2>&1; then + LAST_AUTHOR=$(git log -1 --format='%ae' "origin/$BRANCH") + if [ "$LAST_AUTHOR" != "github-actions[bot]@users.noreply.github.com" ]; then + echo "::warning::$BRANCH was manually edited (last commit by $LAST_AUTHOR); skipping automated update so the manual changes aren't overwritten. Merge or close the PR to resume automation." + echo "skip=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + fi + + echo "skip=false" >> "$GITHUB_OUTPUT" + + - name: Build PR body + if: steps.audit.outputs.changed == 'true' && steps.guard.outputs.skip != 'true' run: | { echo "## uv audit — targeted dependency fixes" @@ -78,11 +99,8 @@ jobs: } > pr_body.md - name: Commit and push branch - if: steps.audit.outputs.changed == 'true' + if: steps.audit.outputs.changed == 'true' && steps.guard.outputs.skip != 'true' run: | - # Fixed branch name: reused/force-pushed each run so we update one PR instead of piling up new ones. - BRANCH="automated/uv-audit-fix" - echo "BRANCH=$BRANCH" >> "$GITHUB_ENV" git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git checkout -B "$BRANCH" @@ -92,7 +110,7 @@ jobs: git push --force origin "$BRANCH" - name: Create pull request - if: steps.audit.outputs.changed == 'true' + if: steps.audit.outputs.changed == 'true' && steps.guard.outputs.skip != 'true' env: GH_TOKEN: ${{ secrets.token || secrets.GITHUB_TOKEN }} run: | From 9ccc921d7d6258a3daddbe9d2b2ee5b90eaab8cb Mon Sep 17 00:00:00 2001 From: Marnix van Lieshout Date: Mon, 28 Sep 2026 10:30:07 +0200 Subject: [PATCH 06/13] feat: add /uv-audit rebase comment command to resume paused automation Assisted-by: GitHub Copilot Signed-off-by: Marnix van Lieshout --- .github/workflows/_job_uv_audit.yaml | 12 ++++++ .github/workflows/uv-audit-rebase.yaml | 51 ++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 .github/workflows/uv-audit-rebase.yaml diff --git a/.github/workflows/_job_uv_audit.yaml b/.github/workflows/_job_uv_audit.yaml index f6d70e019..71385b8a3 100644 --- a/.github/workflows/_job_uv_audit.yaml +++ b/.github/workflows/_job_uv_audit.yaml @@ -5,6 +5,12 @@ name: uv Audit and Targeted Upgrade on: workflow_call: + inputs: + force: + description: "Skip the manual-edit guard and overwrite the automated branch/PR." + type: boolean + required: false + default: false secrets: token: description: "Token used to push the fix branch and open the PR. Defaults to GITHUB_TOKEN." @@ -72,6 +78,12 @@ jobs: BRANCH="automated/uv-audit-fix" echo "BRANCH=$BRANCH" >> "$GITHUB_ENV" + if [ "${{ inputs.force }}" = "true" ]; then + echo "Force requested; skipping the manual-edit check." + echo "skip=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + git fetch origin "$BRANCH" 2>/dev/null || true if git rev-parse --verify "origin/$BRANCH" >/dev/null 2>&1; then diff --git a/.github/workflows/uv-audit-rebase.yaml b/.github/workflows/uv-audit-rebase.yaml new file mode 100644 index 000000000..f1a48ae9e --- /dev/null +++ b/.github/workflows/uv-audit-rebase.yaml @@ -0,0 +1,51 @@ +# SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project +# +# SPDX-License-Identifier: MPL-2.0 + +name: uv Audit Rebase + +on: + issue_comment: + types: [created] + +permissions: + contents: write + pull-requests: write + +jobs: + rebase: + name: Resume uv audit automation + # Only maintainers can trigger this, since it force-pushes and re-runs a job with write access. + if: > + github.event.issue.pull_request != null && + contains(github.event.comment.body, '/uv-audit rebase') && + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) + runs-on: ubuntu-latest + permissions: + pull-requests: read + issues: write + steps: + - name: Verify comment targets the automated branch + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + HEAD_REF=$(gh pr view "${{ github.event.issue.number }}" --repo "${{ github.repository }}" --json headRefName --jq .headRefName) + if [ "$HEAD_REF" != "automated/uv-audit-fix" ]; then + echo "::error::/uv-audit rebase only works on the automated uv-audit PR (head ref was '$HEAD_REF')." + exit 1 + fi + + - name: Acknowledge + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh api "repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions" \ + -f content='+1' --silent + + uv-audit: + name: uv Audit + needs: rebase + uses: ./.github/workflows/_job_uv_audit.yaml + with: + force: true + secrets: inherit From 5757aa7f2566e642856a541a6141ed49d3a847de Mon Sep 17 00:00:00 2001 From: Marnix van Lieshout Date: Mon, 28 Sep 2026 10:34:28 +0200 Subject: [PATCH 07/13] feat: document /uv-audit rebase command in PR body; temporarily trigger on push for branch testing Assisted-by: GitHub Copilot Signed-off-by: Marnix van Lieshout --- .github/workflows/_job_uv_audit.yaml | 2 ++ .github/workflows/uv-audit.yaml | 3 +++ 2 files changed, 5 insertions(+) diff --git a/.github/workflows/_job_uv_audit.yaml b/.github/workflows/_job_uv_audit.yaml index 71385b8a3..c57a3f6d0 100644 --- a/.github/workflows/_job_uv_audit.yaml +++ b/.github/workflows/_job_uv_audit.yaml @@ -108,6 +108,8 @@ jobs: cat uv_audit_upgrade.txt echo '```' fi + echo "### Commands" + echo "This PR is regenerated automatically and safe to edit — if you push a commit here, automation pauses so your changes aren't overwritten. Comment \`/uv-audit rebase\` to discard your edits and let automation take over again." } > pr_body.md - name: Commit and push branch diff --git a/.github/workflows/uv-audit.yaml b/.github/workflows/uv-audit.yaml index 35435a08d..fed351025 100644 --- a/.github/workflows/uv-audit.yaml +++ b/.github/workflows/uv-audit.yaml @@ -8,6 +8,9 @@ on: schedule: - cron: '0 6 * * *' # Every day at 06:00 UTC workflow_dispatch: + push: # TEMPORARY: lets us trigger real runs from this PR branch, since workflow_dispatch only works once merged to the default branch. Remove before merging. + branches: + - ci/uv-audit-workflow permissions: contents: write From b920030797b12d6e59623f562e477105c5655ede Mon Sep 17 00:00:00 2001 From: Marnix van Lieshout Date: Mon, 28 Sep 2026 10:39:00 +0200 Subject: [PATCH 08/13] fix: remove unnecessary uv sync --all-groups step, which conflicts with mutually exclusive cpu/gpu extras uv audit and uv lock both work directly from uv.lock/pyproject.toml without needing a synced venv. Assisted-by: GitHub Copilot Signed-off-by: Marnix van Lieshout --- .github/workflows/_job_uv_audit.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/_job_uv_audit.yaml b/.github/workflows/_job_uv_audit.yaml index c57a3f6d0..e70e999ab 100644 --- a/.github/workflows/_job_uv_audit.yaml +++ b/.github/workflows/_job_uv_audit.yaml @@ -36,9 +36,6 @@ jobs: activate-environment: true enable-cache: true - - name: Install dependencies - run: uv sync --frozen --all-groups - - name: Run uv audit id: audit shell: bash From 21369fbfdf6249a72395979923076fd6dd882699 Mon Sep 17 00:00:00 2001 From: Marnix van Lieshout Date: Mon, 28 Sep 2026 10:41:08 +0200 Subject: [PATCH 09/13] fix: only use the dependencies label, which is the only one that exists in this repo security/automated labels don't exist in OpenSTEF/openstef, causing gh pr create to fail. Assisted-by: GitHub Copilot Signed-off-by: Marnix van Lieshout --- .github/workflows/_job_uv_audit.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_job_uv_audit.yaml b/.github/workflows/_job_uv_audit.yaml index e70e999ab..1fd57085a 100644 --- a/.github/workflows/_job_uv_audit.yaml +++ b/.github/workflows/_job_uv_audit.yaml @@ -137,5 +137,5 @@ jobs: --body-file pr_body.md \ --base "${{ github.ref_name }}" \ --head "$BRANCH" \ - --label security --label dependencies --label automated + --label dependencies fi From 14fdd53a28b8faee33f08adbdcbe2abf2d9a0ff6 Mon Sep 17 00:00:00 2001 From: Marnix van Lieshout Date: Mon, 28 Sep 2026 10:49:35 +0200 Subject: [PATCH 10/13] fix: rename PR/commit to deps(security), stop committing audit report/log files, capture uv lock output uv lock prints progress to stderr, so uv_audit_upgrade.txt was ending up empty. Also uv_audit_report.json and uv_audit_upgrade.txt are transient working files and shouldn't be committed to the repo alongside uv.lock. Assisted-by: GitHub Copilot Signed-off-by: Marnix van Lieshout --- .github/workflows/_job_uv_audit.yaml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/_job_uv_audit.yaml b/.github/workflows/_job_uv_audit.yaml index 1fd57085a..7b89e0f4d 100644 --- a/.github/workflows/_job_uv_audit.yaml +++ b/.github/workflows/_job_uv_audit.yaml @@ -59,7 +59,7 @@ jobs: ARGS+=(--upgrade-package "$pkg") done - uv lock "${ARGS[@]}" | tee uv_audit_upgrade.txt + uv lock "${ARGS[@]}" 2>&1 | tee uv_audit_upgrade.txt if git diff --quiet -- uv.lock; then echo "changed=false" >> "$GITHUB_OUTPUT" @@ -115,9 +115,8 @@ jobs: git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git checkout -B "$BRANCH" - git add uv.lock uv_audit_report.json - [ -f uv_audit_upgrade.txt ] && git add uv_audit_upgrade.txt - git commit -m "fix(deps): patch vulnerable packages found by uv audit" + git add uv.lock + git commit -m "deps(security): patch vulnerable packages found by uv audit" git push --force origin "$BRANCH" - name: Create pull request @@ -129,11 +128,11 @@ jobs: if [ -n "$PR_NUMBER" ]; then gh pr edit "$PR_NUMBER" \ - --title "security: uv audit targeted dependency fixes" \ + --title "deps(security): uv audit targeted dependency fixes" \ --body-file pr_body.md else gh pr create \ - --title "security: uv audit targeted dependency fixes" \ + --title "deps(security): uv audit targeted dependency fixes" \ --body-file pr_body.md \ --base "${{ github.ref_name }}" \ --head "$BRANCH" \ From d37f667b64603fbc3f34d31b4d9e708df472aa08 Mon Sep 17 00:00:00 2001 From: Marnix van Lieshout Date: Mon, 28 Sep 2026 11:01:06 +0200 Subject: [PATCH 11/13] feat: add real /uv-audit rebase command alongside /uv-audit recreate - /uv-audit rebase: regenerates the fix and replays any manual commits on top (git rebase --onto), matching @dependabot rebase semantics. - /uv-audit recreate: discards manual edits and regenerates from scratch, matching @dependabot recreate semantics. Assisted-by: GitHub Copilot Signed-off-by: Marnix van Lieshout --- .github/workflows/_job_uv_audit.yaml | 43 ++++++++++++++++++-- .github/workflows/uv-audit-rebase.yaml | 2 +- .github/workflows/uv-audit-recreate.yaml | 51 ++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/uv-audit-recreate.yaml diff --git a/.github/workflows/_job_uv_audit.yaml b/.github/workflows/_job_uv_audit.yaml index 7b89e0f4d..9c1cd4b00 100644 --- a/.github/workflows/_job_uv_audit.yaml +++ b/.github/workflows/_job_uv_audit.yaml @@ -11,6 +11,11 @@ on: type: boolean required: false default: false + rebase: + description: "Skip the manual-edit guard and replay manual commits on top of a freshly regenerated fix, instead of discarding them." + type: boolean + required: false + default: false secrets: token: description: "Token used to push the fix branch and open the PR. Defaults to GITHUB_TOKEN." @@ -75,8 +80,8 @@ jobs: BRANCH="automated/uv-audit-fix" echo "BRANCH=$BRANCH" >> "$GITHUB_ENV" - if [ "${{ inputs.force }}" = "true" ]; then - echo "Force requested; skipping the manual-edit check." + if [ "${{ inputs.force }}" = "true" ] || [ "${{ inputs.rebase }}" = "true" ]; then + echo "Force/rebase requested; skipping the manual-edit check." echo "skip=false" >> "$GITHUB_OUTPUT" exit 0 fi @@ -106,11 +111,11 @@ jobs: echo '```' fi echo "### Commands" - echo "This PR is regenerated automatically and safe to edit — if you push a commit here, automation pauses so your changes aren't overwritten. Comment \`/uv-audit rebase\` to discard your edits and let automation take over again." + echo "This PR is regenerated automatically and safe to edit — if you push a commit here, automation pauses so your changes aren't overwritten. Comment \`/uv-audit rebase\` to replay your edits on top of a refreshed fix, or \`/uv-audit recreate\` to discard your edits and let automation take over again." } > pr_body.md - name: Commit and push branch - if: steps.audit.outputs.changed == 'true' && steps.guard.outputs.skip != 'true' + if: steps.audit.outputs.changed == 'true' && steps.guard.outputs.skip != 'true' && inputs.rebase != true run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" @@ -119,6 +124,36 @@ jobs: git commit -m "deps(security): patch vulnerable packages found by uv audit" git push --force origin "$BRANCH" + - name: Rebase manual edits onto refreshed fix + if: steps.audit.outputs.changed == 'true' && steps.guard.outputs.skip != 'true' && inputs.rebase == true + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + # The default checkout is shallow; rebase needs the branch's full history. + git fetch --unshallow origin 2>/dev/null || git fetch origin + git fetch origin "$BRANCH" + + git checkout -b uv-audit-fix-new + git add uv.lock + git commit -m "deps(security): patch vulnerable packages found by uv audit" + NEW_COMMIT=$(git rev-parse HEAD) + + OLD_COMMIT=$(git log "origin/$BRANCH" --author='github-actions[bot]' -1 --format=%H) + if [ -z "$OLD_COMMIT" ]; then + echo "::error::No prior automated commit found on $BRANCH to rebase onto. Comment /uv-audit recreate instead." + exit 1 + fi + + git checkout -B "$BRANCH" "origin/$BRANCH" + if ! git rebase --onto "$NEW_COMMIT" "$OLD_COMMIT" "$BRANCH"; then + git rebase --abort + echo "::error::Rebase failed: your edits conflict with the refreshed fix. Resolve manually, or comment /uv-audit recreate to discard your edits." + exit 1 + fi + + git push --force origin "$BRANCH" + - name: Create pull request if: steps.audit.outputs.changed == 'true' && steps.guard.outputs.skip != 'true' env: diff --git a/.github/workflows/uv-audit-rebase.yaml b/.github/workflows/uv-audit-rebase.yaml index f1a48ae9e..173fdd258 100644 --- a/.github/workflows/uv-audit-rebase.yaml +++ b/.github/workflows/uv-audit-rebase.yaml @@ -47,5 +47,5 @@ jobs: needs: rebase uses: ./.github/workflows/_job_uv_audit.yaml with: - force: true + rebase: true secrets: inherit diff --git a/.github/workflows/uv-audit-recreate.yaml b/.github/workflows/uv-audit-recreate.yaml new file mode 100644 index 000000000..9a4dbc009 --- /dev/null +++ b/.github/workflows/uv-audit-recreate.yaml @@ -0,0 +1,51 @@ +# SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project +# +# SPDX-License-Identifier: MPL-2.0 + +name: uv Audit Recreate + +on: + issue_comment: + types: [created] + +permissions: + contents: write + pull-requests: write + +jobs: + recreate: + name: Resume uv audit automation + # Only maintainers can trigger this, since it force-pushes and re-runs a job with write access. + if: > + github.event.issue.pull_request != null && + contains(github.event.comment.body, '/uv-audit recreate') && + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) + runs-on: ubuntu-latest + permissions: + pull-requests: read + issues: write + steps: + - name: Verify comment targets the automated branch + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + HEAD_REF=$(gh pr view "${{ github.event.issue.number }}" --repo "${{ github.repository }}" --json headRefName --jq .headRefName) + if [ "$HEAD_REF" != "automated/uv-audit-fix" ]; then + echo "::error::/uv-audit recreate only works on the automated uv-audit PR (head ref was '$HEAD_REF')." + exit 1 + fi + + - name: Acknowledge + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh api "repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions" \ + -f content='+1' --silent + + uv-audit: + name: uv Audit + needs: recreate + uses: ./.github/workflows/_job_uv_audit.yaml + with: + force: true + secrets: inherit From 7d6a83557e50a8c64573413ddf22f6cc9080f3cc Mon Sep 17 00:00:00 2001 From: Marnix van Lieshout Date: Mon, 28 Sep 2026 11:07:52 +0200 Subject: [PATCH 12/13] test: trigger uv audit workflow run From 5c6431e086db8b3fd8f61cb05d2282a659803ef5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 28 Sep 2026 09:08:26 +0000 Subject: [PATCH 13/13] deps(security): patch vulnerable packages found by uv audit --- uv.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uv.lock b/uv.lock index 348dac2b4..d4309d6ec 100644 --- a/uv.lock +++ b/uv.lock @@ -2687,7 +2687,7 @@ wheels = [ [[package]] name = "jupyter-server" -version = "2.20.0" +version = "2.21.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -2709,9 +2709,9 @@ dependencies = [ { name = "traitlets" }, { name = "websocket-client" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6b/dc/db3a582633170186f8c8b31298d7eb26ad0eb031a1f53476c258b64eed05/jupyter_server-2.20.0.tar.gz", hash = "sha256:b5778ba337d8015a3dc2b80803ecdd5ac18d3797fddf61a50ea5fb472b4ebe14", size = 756523, upload-time = "2026-06-17T12:09:09.435Z" } +sdist = { url = "https://files.pythonhosted.org/packages/65/3d/3c9f8bce5d5448107bd285cd80185e76c9c871d076b580dfd90997e52904/jupyter_server-2.21.1.tar.gz", hash = "sha256:a8960aa29263f6041283e97d4756b099fb49b767baab6371891ecb1bd40a63df", size = 761651, upload-time = "2026-09-15T16:00:07.853Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/71/8c002223e873a870f5c41dc69b0a7c922301123e4a31d5d01ecb700aef77/jupyter_server-2.20.0-py3-none-any.whl", hash = "sha256:c3b67c93c471e947c18b5026f04f21614218adb706df8f48227d3ee8e0a7cdcc", size = 393143, upload-time = "2026-06-17T12:09:07.234Z" }, + { url = "https://files.pythonhosted.org/packages/1d/39/91bc08650cc8e3efeb7a83c25e20ebddcc9b70d28b4eb6f51a4fb55da7a4/jupyter_server-2.21.1-py3-none-any.whl", hash = "sha256:2a6467606af7dbae2e7e31640030025969e15db6a649eae334af90415dc71dca", size = 394641, upload-time = "2026-09-15T16:00:05.526Z" }, ] [[package]]