From 7259977bd38465908d8b4debe800d23bb0ba6e69 Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Fri, 18 Sep 2026 17:27:32 +1000 Subject: [PATCH] ci: move GitHub Actions to their Node 24 majors Node 20 reached end-of-life on 2026-04-30, and these pins are all node20 (upload-pages-artifact v3 wraps node20 upload-artifact): actions/checkout v4 -> v7 (6 uses, 4 files) actions/setup-python v5 -> v7 (6 uses, 4 files) actions/upload-artifact v4 -> v7 (refresh-snapshots) actions/download-artifact v4 -> v8 (refresh-snapshots) actions/upload-pages-artifact v3 -> v5 (audit-dashboard) actions/configure-pages v5 -> v6 (audit-dashboard) actions/deploy-pages v4 -> v5 (audit-dashboard) refresh-snapshots notify: download-artifact v5+ unpacks a download that resolves to exactly one artifact straight into path/, with no / folder, and that includes pattern: downloads. When one canary leg fails (the usual case, as on 2026-09-01) failure.json and builder.log land in failures/ itself, `for dir in failures/*/` finds nothing, and the upstream-break issue is filed without its details, with nothing erroring. The loop now finds each failure.json wherever it landed and reads builder.log beside it, so it no longer depends on the layout. upload-pages-artifact v4+ drops dotfiles by default; the last deployed Pages artifact (94 entries) had none, so nothing is lost. Also adds a grouped weekly github-actions Dependabot config (the repo had none), in the form QuantEcon/actions uses. Co-Authored-By: Claude Opus 5 (1M context) --- .github/dependabot.yml | 23 +++++++++++++++++++ .github/workflows/audit-dashboard.yml | 10 ++++----- .github/workflows/consumed-file-check.yml | 4 ++-- .github/workflows/refresh-snapshots.yml | 27 ++++++++++++++--------- .github/workflows/validate-datasets.yml | 4 ++-- 5 files changed, 48 insertions(+), 20 deletions(-) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..0cd7497 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,23 @@ +# Dependabot: keep the workflow pins on current majors, so hand-written +# workflows cannot drift back onto end-of-life runtimes unnoticed. +# +# Two groups, the form QuantEcon/actions uses, so updates land as at most two +# PRs a week: "github-actions" for minor/patch (low risk, merge together) and +# "github-actions-major" for majors (read the release notes first). +# `directory: /` covers .github/workflows/. +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + open-pull-requests-limit: 10 + commit-message: + prefix: "⬆️" + groups: + github-actions: + patterns: ["*"] + update-types: [minor, patch] + github-actions-major: + patterns: ["*"] + update-types: [major] diff --git a/.github/workflows/audit-dashboard.yml b/.github/workflows/audit-dashboard.yml index f4879e6..34dae6d 100644 --- a/.github/workflows/audit-dashboard.yml +++ b/.github/workflows/audit-dashboard.yml @@ -38,7 +38,7 @@ jobs: outputs: warnings: ${{ steps.warnings.outputs.text }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: # Nothing under `lectures/` may be an LFS object (.gitattributes # scopes LFS to `sources/**`), and this job publishes `lectures/` to @@ -49,7 +49,7 @@ jobs: # as pointer text everywhere else. It also keeps `sources/` — a 99 MiB # LFS object — off every run of this workflow. lfs: false - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v7 with: python-version: "3.12" - run: pip install pyyaml @@ -85,7 +85,7 @@ jobs: cp -r site/. _site/ cp -r lectures _site/lectures cp audit.json _site/audit.json - - uses: actions/upload-pages-artifact@v3 + - uses: actions/upload-pages-artifact@v5 with: path: _site @@ -100,11 +100,11 @@ jobs: name: github-pages url: ${{ steps.deployment.outputs.page_url }} steps: - - uses: actions/configure-pages@v5 + - uses: actions/configure-pages@v6 with: enablement: true - id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@v5 # The strict build is this repo's drift alarm for 8 upstream lecture repos, # and the weekly schedule fires when nobody is watching. It worked exactly as diff --git a/.github/workflows/consumed-file-check.yml b/.github/workflows/consumed-file-check.yml index 977babd..6a364b0 100644 --- a/.github/workflows/consumed-file-check.yml +++ b/.github/workflows/consumed-file-check.yml @@ -15,7 +15,7 @@ jobs: consumed-files: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: # `lfs: false` is the assertion, not a saving. A manifest records the # sha256 of the real bytes, so if a `lectures/` file is ever @@ -34,7 +34,7 @@ jobs: # by mistake arrives as real bytes, hashes to something the README # does not record, and goes red. lfs: false - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v7 with: python-version: "3.12" - run: pip install pyyaml diff --git a/.github/workflows/refresh-snapshots.yml b/.github/workflows/refresh-snapshots.yml index 94cccfd..7920602 100644 --- a/.github/workflows/refresh-snapshots.yml +++ b/.github/workflows/refresh-snapshots.yml @@ -58,10 +58,10 @@ jobs: all: ${{ steps.plan.outputs.all }} due: ${{ steps.plan.outputs.due }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: lfs: false - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v7 with: python-version: "3.12" - run: pip install pyyaml @@ -99,10 +99,10 @@ jobs: # A matrix cannot expose one output per leg, so a failing leg leaves its # failure.json + log as an artifact and the notify job reads those. steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: lfs: false - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v7 with: python-version: "3.12" - run: pip install -r requirements.txt @@ -130,7 +130,7 @@ jobs: exit "$code" - name: Keep the failure for the notifier if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: canary-failure-${{ matrix.snapshot.stem }} path: | @@ -145,7 +145,7 @@ jobs: permissions: issues: write steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: pattern: canary-failure-* path: failures @@ -161,8 +161,13 @@ jobs: { echo "The weekly sources-alive canary failed. Nothing was written: every consumer keeps reading the last-good snapshot, so no lecture is affected — this is the alarm moving from the lecture repos' CI to here, doing its job." echo - for dir in failures/*/; do - [ -f "$dir/failure.json" ] || continue + # download-artifact v5+ unpacks a lone artifact straight into + # failures/ (one leg failed, the usual case) and several into + # failures//, so find each failure.json wherever it landed + # and read builder.log beside it. If nothing was uploaded (plan + # failed, or a leg failed before its builder ran) there is no + # failures/ and the loop finds nothing, as the old glob did. + while IFS= read -r dir; do dataset=$(jq -r .dataset "$dir/failure.json") builder=$(jq -r .builder "$dir/failure.json") kind=$(jq -r .kind "$dir/failure.json") @@ -179,7 +184,7 @@ jobs: tail -n 15 "$dir/builder.log" echo '```' echo - done + done < <(find failures -type f -name failure.json -exec dirname {} \; 2>/dev/null | sort) echo "Run: $RUN_URL" echo echo "_Posted automatically. Later failures comment here rather than opening new issues, so close this once the canary is green._" @@ -208,13 +213,13 @@ jobs: snapshot: ${{ fromJson(needs.plan.outputs.due) }} name: refresh (${{ matrix.snapshot.dataset }}) steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: lfs: false # The PAT, when present, is what lets the PR trigger `consumed-files` # (see the header). `persist-credentials` keeps it for the push. token: ${{ secrets.QUANTECON_SERVICES_PAT || github.token }} - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v7 with: python-version: "3.12" - run: pip install -r requirements.txt diff --git a/.github/workflows/validate-datasets.yml b/.github/workflows/validate-datasets.yml index 9338967..413cec2 100644 --- a/.github/workflows/validate-datasets.yml +++ b/.github/workflows/validate-datasets.yml @@ -42,10 +42,10 @@ jobs: pandas: ["2.3.3", "3.0.5"] name: validate (pandas ${{ matrix.pandas }}) steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: lfs: false # lectures/ is plain git; a pointer here should fail (see consumed-file-check.yml) - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v7 with: python-version: "3.12" # wbgapi: builders/business_cycle.py imports it at module level, and the