From db5932268dc937c13f258a1ed07ace704a899d6d Mon Sep 17 00:00:00 2001 From: Anthony Volk <14987227+anth-volk@users.noreply.github.com> Date: Mon, 21 Sep 2026 23:35:42 +0400 Subject: [PATCH 1/3] Run dependency updates daily --- .github/workflows/update-dependencies.yml | 97 ++++++++++++++++------- 1 file changed, 69 insertions(+), 28 deletions(-) diff --git a/.github/workflows/update-dependencies.yml b/.github/workflows/update-dependencies.yml index 0538560ed..8abc2adf5 100644 --- a/.github/workflows/update-dependencies.yml +++ b/.github/workflows/update-dependencies.yml @@ -2,10 +2,14 @@ name: Update dependencies on: schedule: - # Run every 15 minutes - - cron: '*/15 * * * *' + # Run daily at 06:00 UTC + - cron: '0 6 * * *' workflow_dispatch: # Allow manual triggering +concurrency: + group: update-dependencies + cancel-in-progress: false + jobs: update-dependencies: name: File update PR @@ -25,9 +29,9 @@ jobs: - name: Checkout repository uses: actions/checkout@v6 - # Checkout main branch with: ref: main + fetch-depth: 0 token: ${{ steps.app-token.outputs.token }} - name: Install uv @@ -37,6 +41,36 @@ jobs: uses: actions/setup-python@v6 with: python-version: "3.13" + + - name: Prepare update branch + id: branch + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + git config --global user.name "policyengine-auto" + git config --global user.email "policyengine-auto@users.noreply.github.com" + + open_pr=$(gh pr list \ + --repo "$GITHUB_REPOSITORY" \ + --base main \ + --head update-dependencies \ + --state open \ + --json number \ + --jq '.[0].number // empty') + + if [[ -n "$open_pr" ]]; then + echo "has_open_pr=true" >> "$GITHUB_OUTPUT" + git fetch origin update-dependencies + git checkout -B update-dependencies origin/update-dependencies + + # Preserve the open PR's commits while incorporating current main. + # Prefer main's version of a conflict because the generation and + # relocking steps below recreate the dependency-update changes. + git merge --no-edit -X theirs origin/main + else + echo "has_open_pr=false" >> "$GITHUB_OUTPUT" + git checkout -B update-dependencies origin/main + fi - name: Generate API clients run: | @@ -44,40 +78,47 @@ jobs: ./scripts/generate-clients.sh - name: Update dependencies - id: update run: | - # Run the update command + # Runs `uv lock --upgrade` in every library and project. make update - - # Check if there are changes + + - name: Commit and push update + id: push + run: | + changes_detected=false if [[ -z $(git status --porcelain) ]]; then echo "No changes detected" - echo "changes_detected=false" >> $GITHUB_OUTPUT else echo "Changes detected" - echo "changes_detected=true" >> $GITHUB_OUTPUT + git add . + git commit -m "Update dependencies ($(date -u +%Y-%m-%d))" + changes_detected=true fi - + + branch_pushed=false + if [[ "${{ steps.branch.outputs.has_open_pr }}" == "true" ]]; then + commits_to_push=$(git rev-list --count origin/update-dependencies..HEAD) + if [[ "$commits_to_push" -gt 0 ]]; then + git push origin HEAD:update-dependencies + branch_pushed=true + fi + elif [[ "$changes_detected" == "true" ]]; then + # No open PR owns this branch, so replacing a stale remote branch + # cannot rewrite active review history. + git push --force-with-lease origin HEAD:update-dependencies + branch_pushed=true + fi + + echo "branch_pushed=$branch_pushed" >> "$GITHUB_OUTPUT" + - name: Create pull request - if: steps.update.outputs.changes_detected == 'true' + if: steps.branch.outputs.has_open_pr == 'false' && steps.push.outputs.branch_pushed == 'true' + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} run: | - git config --global user.name "policyengine-auto" - git config --global user.email "policyengine-auto@users.noreply.github.com" - - git checkout -b update-dependencies - git add . - git commit -m "Update dependencies" - git push origin update-dependencies --force - - # Try to create PR, ignore if it already exists gh pr create \ + --repo "$GITHUB_REPOSITORY" \ --title "Update dependencies" \ - --body "Automated dependency updates - - This PR was automatically created by the dependency update workflow. - - Last updated: $(date)" \ + --body "Automated daily dependency updates. Subsequent runs append commits to this branch while the pull request remains open." \ --base main \ - --head update-dependencies || echo "PR may already exist, continuing..." - env: - GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} \ No newline at end of file + --head update-dependencies From c36b98f3d7a06f9ef2a7d6b866536e654ec3f82c Mon Sep 17 00:00:00 2001 From: Anthony Volk <14987227+anth-volk@users.noreply.github.com> Date: Mon, 21 Sep 2026 23:48:09 +0400 Subject: [PATCH 2/3] Run dependency updates at 3 PM Eastern --- .github/workflows/update-dependencies.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/update-dependencies.yml b/.github/workflows/update-dependencies.yml index 8abc2adf5..1c4662573 100644 --- a/.github/workflows/update-dependencies.yml +++ b/.github/workflows/update-dependencies.yml @@ -2,8 +2,9 @@ name: Update dependencies on: schedule: - # Run daily at 06:00 UTC - - cron: '0 6 * * *' + # Run daily at 3:00 PM US Eastern time. + - cron: '0 15 * * *' + timezone: 'America/New_York' workflow_dispatch: # Allow manual triggering concurrency: From 491cb8cf7cb666b60e60e21b550933bebc14f3f5 Mon Sep 17 00:00:00 2001 From: Anthony Volk <14987227+anth-volk@users.noreply.github.com> Date: Tue, 22 Sep 2026 00:00:38 +0400 Subject: [PATCH 3/3] Extract dependency update workflow scripts --- .../dependency-update-commit-and-push.sh | 39 ++++++++++ .../scripts/dependency-update-create-pr.sh | 14 ++++ .../dependency-update-prepare-branch.sh | 36 ++++++++++ .github/workflows/update-dependencies.yml | 71 ++----------------- 4 files changed, 96 insertions(+), 64 deletions(-) create mode 100755 .github/scripts/dependency-update-commit-and-push.sh create mode 100755 .github/scripts/dependency-update-create-pr.sh create mode 100755 .github/scripts/dependency-update-prepare-branch.sh diff --git a/.github/scripts/dependency-update-commit-and-push.sh b/.github/scripts/dependency-update-commit-and-push.sh new file mode 100755 index 000000000..86c935280 --- /dev/null +++ b/.github/scripts/dependency-update-commit-and-push.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash + +# Commit generated dependency changes and update the reusable PR branch. + +set -euo pipefail + +: "${GITHUB_OUTPUT:?GITHUB_OUTPUT is required}" +: "${HAS_OPEN_PR:?HAS_OPEN_PR is required}" + +if [[ "${HAS_OPEN_PR}" != "true" && "${HAS_OPEN_PR}" != "false" ]]; then + echo "HAS_OPEN_PR must be either true or false" >&2 + exit 1 +fi + +changes_detected=false +if [[ -z "$(git status --porcelain)" ]]; then + echo "No changes detected" +else + echo "Changes detected" + git add --all + git commit -m "Update dependencies ($(date -u +%Y-%m-%d))" + changes_detected=true +fi + +branch_pushed=false +if [[ "${HAS_OPEN_PR}" == "true" ]]; then + commits_to_push="$(git rev-list --count origin/update-dependencies..HEAD)" + if [[ "${commits_to_push}" -gt 0 ]]; then + git push origin HEAD:update-dependencies + branch_pushed=true + fi +elif [[ "${changes_detected}" == "true" ]]; then + # No open PR owns this branch, so replacing a stale remote branch cannot + # rewrite active review history. + git push --force-with-lease origin HEAD:update-dependencies + branch_pushed=true +fi + +echo "branch_pushed=${branch_pushed}" >> "${GITHUB_OUTPUT}" diff --git a/.github/scripts/dependency-update-create-pr.sh b/.github/scripts/dependency-update-create-pr.sh new file mode 100755 index 000000000..1084c03e8 --- /dev/null +++ b/.github/scripts/dependency-update-create-pr.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +# Open a dependency-update PR after a new update branch has been pushed. + +set -euo pipefail + +: "${GITHUB_REPOSITORY:?GITHUB_REPOSITORY is required}" + +gh pr create \ + --repo "${GITHUB_REPOSITORY}" \ + --title "Update dependencies" \ + --body "Automated daily dependency updates. Subsequent runs append commits to this branch while the pull request remains open." \ + --base main \ + --head update-dependencies diff --git a/.github/scripts/dependency-update-prepare-branch.sh b/.github/scripts/dependency-update-prepare-branch.sh new file mode 100755 index 000000000..ea63f00b0 --- /dev/null +++ b/.github/scripts/dependency-update-prepare-branch.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +# Reuse the branch for an open dependency-update PR, or start a new branch +# from current main when no update PR exists. + +set -euo pipefail + +: "${GITHUB_REPOSITORY:?GITHUB_REPOSITORY is required}" +: "${GITHUB_OUTPUT:?GITHUB_OUTPUT is required}" + +git config user.name "policyengine-auto" +git config user.email "policyengine-auto@users.noreply.github.com" + +open_pr="$( + gh pr list \ + --repo "${GITHUB_REPOSITORY}" \ + --base main \ + --head update-dependencies \ + --state open \ + --json number \ + --jq '.[0].number // empty' +)" + +if [[ -n "${open_pr}" ]]; then + echo "has_open_pr=true" >> "${GITHUB_OUTPUT}" + git fetch origin update-dependencies + git checkout -B update-dependencies origin/update-dependencies + + # Preserve the open PR's commits while incorporating current main. Prefer + # main's version of conflicts because later steps regenerate clients and + # recreate dependency lock changes. + git merge --no-edit -X theirs origin/main +else + echo "has_open_pr=false" >> "${GITHUB_OUTPUT}" + git checkout -B update-dependencies origin/main +fi diff --git a/.github/workflows/update-dependencies.yml b/.github/workflows/update-dependencies.yml index 1c4662573..caf7b0142 100644 --- a/.github/workflows/update-dependencies.yml +++ b/.github/workflows/update-dependencies.yml @@ -47,79 +47,22 @@ jobs: id: branch env: GH_TOKEN: ${{ steps.app-token.outputs.token }} - run: | - git config --global user.name "policyengine-auto" - git config --global user.email "policyengine-auto@users.noreply.github.com" - - open_pr=$(gh pr list \ - --repo "$GITHUB_REPOSITORY" \ - --base main \ - --head update-dependencies \ - --state open \ - --json number \ - --jq '.[0].number // empty') - - if [[ -n "$open_pr" ]]; then - echo "has_open_pr=true" >> "$GITHUB_OUTPUT" - git fetch origin update-dependencies - git checkout -B update-dependencies origin/update-dependencies - - # Preserve the open PR's commits while incorporating current main. - # Prefer main's version of a conflict because the generation and - # relocking steps below recreate the dependency-update changes. - git merge --no-edit -X theirs origin/main - else - echo "has_open_pr=false" >> "$GITHUB_OUTPUT" - git checkout -B update-dependencies origin/main - fi + run: .github/scripts/dependency-update-prepare-branch.sh - name: Generate API clients - run: | - # Generate clients with correct naming before updating dependencies - ./scripts/generate-clients.sh + run: ./scripts/generate-clients.sh - name: Update dependencies - run: | - # Runs `uv lock --upgrade` in every library and project. - make update + run: make update - name: Commit and push update id: push - run: | - changes_detected=false - if [[ -z $(git status --porcelain) ]]; then - echo "No changes detected" - else - echo "Changes detected" - git add . - git commit -m "Update dependencies ($(date -u +%Y-%m-%d))" - changes_detected=true - fi - - branch_pushed=false - if [[ "${{ steps.branch.outputs.has_open_pr }}" == "true" ]]; then - commits_to_push=$(git rev-list --count origin/update-dependencies..HEAD) - if [[ "$commits_to_push" -gt 0 ]]; then - git push origin HEAD:update-dependencies - branch_pushed=true - fi - elif [[ "$changes_detected" == "true" ]]; then - # No open PR owns this branch, so replacing a stale remote branch - # cannot rewrite active review history. - git push --force-with-lease origin HEAD:update-dependencies - branch_pushed=true - fi - - echo "branch_pushed=$branch_pushed" >> "$GITHUB_OUTPUT" + env: + HAS_OPEN_PR: ${{ steps.branch.outputs.has_open_pr }} + run: .github/scripts/dependency-update-commit-and-push.sh - name: Create pull request if: steps.branch.outputs.has_open_pr == 'false' && steps.push.outputs.branch_pushed == 'true' env: GH_TOKEN: ${{ steps.app-token.outputs.token }} - run: | - gh pr create \ - --repo "$GITHUB_REPOSITORY" \ - --title "Update dependencies" \ - --body "Automated daily dependency updates. Subsequent runs append commits to this branch while the pull request remains open." \ - --base main \ - --head update-dependencies + run: .github/scripts/dependency-update-create-pr.sh