From 4e351b0b4a96eb1557b0dfdf512aad8ae26f460a Mon Sep 17 00:00:00 2001 From: kalidke Date: Mon, 28 Sep 2026 07:47:51 -0600 Subject: [PATCH 1/5] Caller template: run on pull requests and manual dispatch only Admiral decision 0025: CI tests run on pull requests only, plus workflow_dispatch; never on a push (main included) and never on a schedule. Drops the push block (branches, tags, paths-ignore) and the schedule block from templates/CI.yml, the same change as JuliaSMLM/GaussMLE c8d4a77, whose copy now differs from this one only in its with: lines. The README's trigger description follows. No file in this repository records the template's hash. Co-Authored-By: Claude Opus 5.5 --- README.md | 11 ++++++----- templates/CI.yml | 6 ------ 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 60ea8d5..6763435 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ that need a GPU, lab data, long run times or an instrument run on a lab machine | `test` | always | `GROUP=Core` tests at Julia `min` (the `julia` compat lower bound) and `1` | | `qa` | always | `GROUP=QA` tests (Aqua, ExplicitImports) at Julia `1` | | `downgrade` | `registered: true` | `GROUP=Core` at Julia `min`, the oldest supported Julia, with direct dependencies at the lowest versions `[compat]` allows | -| `pre` | `registered: true`, scheduled runs only | `GROUP=Core` at the Julia prerelease; allowed to fail | +| `pre` | `registered: true`, scheduled runs only (the lab caller has no schedule) | `GROUP=Core` at the Julia prerelease; allowed to fail | | `format` | `runic: true` | Runic formatting check | All jobs run on ubuntu-latest, x64, with a 30-minute timeout, and skip draft pull requests. There @@ -29,7 +29,7 @@ is no coverage upload and no docs job. The `GROUP` variable is read by the lab's | Input | Type | Default | Meaning | |---|---|---|---| -| `registered` | boolean | `false` | The package is in the General registry: adds `downgrade` and the monthly `pre` run. | +| `registered` | boolean | `false` | The package is in the General registry: adds `downgrade`, and `pre` on scheduled runs. | | `runic` | boolean | `false` | Adds the Runic formatting check. Runic checks every `.jl` file in the repository. | | `project` | string | `.` | Path to the package within the repository. | @@ -45,9 +45,10 @@ is no coverage upload and no docs job. The `GROUP` variable is read by the lab's 4. The package's tests must follow the lab's test layout: the standard `test/runtests.jl`, a `test/test_groups.toml`, and at least the `Core` and `QA` groups. -The caller runs on pushes to `main` or `master` and on tags, on pull requests (including when a -draft is marked ready), on manual dispatch, and monthly. Changes only to Markdown files, `dev/` -or `.claude/` do not start a run. A newer push to a pull request cancels the older run. +The caller runs on pull requests (including when a draft is marked ready) and on manual +dispatch, never on a push or a schedule (admiral decision 0025: the lab tests on its own machines, +and a pull request gets one confirming CI run). Changes only to Markdown files, `dev/` or +`.claude/` do not start a pull-request run. A newer push to a pull request cancels the older run. ## Versions diff --git a/templates/CI.yml b/templates/CI.yml index 4d8b719..516056c 100644 --- a/templates/CI.yml +++ b/templates/CI.yml @@ -3,16 +3,10 @@ name: CI on: - push: - branches: [main, master] - tags: ['*'] - paths-ignore: ['**.md', 'dev/**', '.claude/**'] pull_request: types: [opened, synchronize, reopened, ready_for_review] paths-ignore: ['**.md', 'dev/**', '.claude/**'] workflow_dispatch: - schedule: - - cron: '23 5 1 * *' # monthly concurrency: group: ${{ github.workflow }}-${{ github.ref }} From 5ebd32f169e0c8c2040cae3be44f5f6748503f9a Mon Sep 17 00:00:00 2001 From: kalidke Date: Mon, 28 Sep 2026 07:52:21 -0600 Subject: [PATCH 2/5] Self-test on pull requests and manual dispatch; drop the pre job Decision 0025 covers every test workflow, so the self-test also drops its push trigger and gains workflow_dispatch. The pre job ran only on a schedule, which decision 0025 rules out for test workflows and the caller template no longer has, so it is removed rather than kept as dead code; the registered input now adds only the downgrade job. The schedule clause in JULIA_CACHE_SAVE goes with it (a scheduled run is on the default branch anyway). The README follows, and its timeout now reads 60 minutes, as the jobs have been since PR 1. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/julia-ci.yml | 38 ++-------------------------------- .github/workflows/selftest.yml | 4 ++-- README.md | 9 ++++---- 3 files changed, 8 insertions(+), 43 deletions(-) diff --git a/.github/workflows/julia-ci.yml b/.github/workflows/julia-ci.yml index e443eaa..793143f 100644 --- a/.github/workflows/julia-ci.yml +++ b/.github/workflows/julia-ci.yml @@ -9,7 +9,7 @@ on: workflow_call: inputs: registered: - description: 'The package is registered: adds the downgrade job and the monthly Julia pre run.' + description: 'The package is registered: adds the downgrade job.' type: boolean default: false runic: @@ -31,8 +31,7 @@ env: JULIA_CPU_TARGET: 'generic;sandybridge,-xsaveopt,clone_all;haswell,-rdrnd,base(1);x86-64-v4,-rdrnd,base(1)' # Only the default branch saves Julia caches; PR and other branch runs restore the default # branch's cache and save nothing, so they cannot evict it from the 10 GB repository budget. - # Scheduled runs always run on the default branch. - JULIA_CACHE_SAVE: ${{ github.event_name == 'schedule' || github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} + JULIA_CACHE_SAVE: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} jobs: test: @@ -143,39 +142,6 @@ jobs: allow_reresolve: false force_latest_compatible_version: false - pre: - # Monthly look at the next Julia release; allowed to fail, reported by admiral's sweep. - name: Core - Julia pre - if: inputs.registered && github.event_name == 'schedule' - runs-on: ubuntu-latest - timeout-minutes: 60 # a cold portable-target build of a large package runs ~30 min - continue-on-error: true - permissions: - actions: write - contents: read - env: - GROUP: Core - steps: - - uses: actions/checkout@v7 - with: - persist-credentials: false - - uses: julia-actions/setup-julia@v3 - with: - version: pre - arch: x64 - - uses: julia-actions/cache@v3 - with: - cache-name: julia-cache;workflow=${{ github.workflow }};job=${{ github.job }};cpu=portable - save-always: ${{ env.JULIA_CACHE_SAVE }} - _job-status: ${{ env.JULIA_CACHE_SAVE == 'true' && job.status || 'not-saved-off-default-branch' }} - - uses: julia-actions/julia-buildpkg@v1 - with: - project: ${{ inputs.project }} - - uses: julia-actions/julia-runtest@v1 - with: - project: ${{ inputs.project }} - coverage: 'false' - format: name: Runic if: inputs.runic && (github.event_name != 'pull_request' || !github.event.pull_request.draft) diff --git a/.github/workflows/selftest.yml b/.github/workflows/selftest.yml index 62c01ec..ca72728 100644 --- a/.github/workflows/selftest.yml +++ b/.github/workflows/selftest.yml @@ -1,10 +1,10 @@ # Self-test: runs julia-ci.yml from this commit against the fixture package. registered and -# runic are on so the downgrade and Runic jobs run too; the pre job runs only on schedule. +# runic are on so the downgrade and Runic jobs run too. name: Self-test on: - push: pull_request: + workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.ref }} diff --git a/README.md b/README.md index 6763435..a869fc8 100644 --- a/README.md +++ b/README.md @@ -18,10 +18,9 @@ that need a GPU, lab data, long run times or an instrument run on a lab machine | `test` | always | `GROUP=Core` tests at Julia `min` (the `julia` compat lower bound) and `1` | | `qa` | always | `GROUP=QA` tests (Aqua, ExplicitImports) at Julia `1` | | `downgrade` | `registered: true` | `GROUP=Core` at Julia `min`, the oldest supported Julia, with direct dependencies at the lowest versions `[compat]` allows | -| `pre` | `registered: true`, scheduled runs only (the lab caller has no schedule) | `GROUP=Core` at the Julia prerelease; allowed to fail | | `format` | `runic: true` | Runic formatting check | -All jobs run on ubuntu-latest, x64, with a 30-minute timeout, and skip draft pull requests. There +All jobs run on ubuntu-latest, x64, with a 60-minute timeout, and skip draft pull requests. There is no coverage upload and no docs job. The `GROUP` variable is read by the lab's standard `test/runtests.jl`, which runs the test groups declared in `test/test_groups.toml`. @@ -29,7 +28,7 @@ is no coverage upload and no docs job. The `GROUP` variable is read by the lab's | Input | Type | Default | Meaning | |---|---|---|---| -| `registered` | boolean | `false` | The package is in the General registry: adds `downgrade`, and `pre` on scheduled runs. | +| `registered` | boolean | `false` | The package is in the General registry: adds `downgrade`. | | `runic` | boolean | `false` | Adds the Runic formatting check. Runic checks every `.jl` file in the repository. | | `project` | string | `.` | Path to the package within the repository. | @@ -60,5 +59,5 @@ move to it by editing their caller. ## Self-test `.github/workflows/selftest.yml` runs the workflow from the same commit, with `registered` and -`runic` on, against a small fixture package in `test/fixture/FixturePkg`, on every push and pull -request. +`runic` on, against a small fixture package in `test/fixture/FixturePkg`, on pull requests and +manual dispatch. From 423c3ad453155a52c9d526c651eff362924cc89e Mon Sep 17 00:00:00 2001 From: kalidke Date: Mon, 28 Sep 2026 07:59:22 -0600 Subject: [PATCH 3/5] Self-test: run when a draft pull request is marked ready; README cache note The self-test's pull_request trigger takes the template's types, so a draft marked ready for review gets a real run; without ready_for_review its only run is the draft one, whose skipped jobs count as success. The README notes that only a manual workflow_dispatch run on main seeds a Julia cache. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/selftest.yml | 1 + README.md | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.github/workflows/selftest.yml b/.github/workflows/selftest.yml index ca72728..297cb57 100644 --- a/.github/workflows/selftest.yml +++ b/.github/workflows/selftest.yml @@ -4,6 +4,7 @@ name: Self-test on: pull_request: + types: [opened, synchronize, reopened, ready_for_review] workflow_dispatch: concurrency: diff --git a/README.md b/README.md index a869fc8..a8f3013 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,8 @@ The caller runs on pull requests (including when a draft is marked ready) and on dispatch, never on a push or a schedule (admiral decision 0025: the lab tests on its own machines, and a pull request gets one confirming CI run). Changes only to Markdown files, `dev/` or `.claude/` do not start a pull-request run. A newer push to a pull request cancels the older run. +Only the default branch saves a Julia cache, so a cache is seeded only by a manual +`workflow_dispatch` run on `main`; until then, pull-request runs start cold. ## Versions From 7942c1ccb9600725242ba631f8a8fd1f15f85a15 Mon Sep 17 00:00:00 2001 From: kalidke Date: Mon, 28 Sep 2026 08:19:45 -0600 Subject: [PATCH 4/5] Scope the CI rule by visibility: private repositories skip push and schedule Keith revised decision 0025: Actions minutes are free on public repositories, so they keep push and scheduled CI and the warm cache; private repositories test on pull requests and by hand only. This replaces the trigger removal of the earlier commits on this branch: - templates/CI.yml is back to main's version, byte for byte, so no package copy has to change. - julia-ci.yml skips every job on a push or scheduled event in a private repository. Push, pull request and dispatch events carry github.event.repository.private; a scheduled event carries no repository, so a visibility job looks it up via the API, on scheduled runs only. - A private repository tests Julia 1 only (its floor is in the local record); downgrade and pre run in public repositories only. Public repositories keep the min/1 matrix. - The cache rule is unchanged: the default branch saves, including a public repository's push to main; the schedule clause stays because a scheduled event carries no default_branch. - The self-test keeps push and gains the template's pull_request types and workflow_dispatch. - README: the private-repository rule, how a private cache is seeded, and the 60-minute timeout. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/julia-ci.yml | 100 ++++++++++++++++++++++++++++++--- .github/workflows/selftest.yml | 3 +- README.md | 26 +++++---- templates/CI.yml | 6 ++ 4 files changed, 115 insertions(+), 20 deletions(-) diff --git a/.github/workflows/julia-ci.yml b/.github/workflows/julia-ci.yml index 793143f..b840c67 100644 --- a/.github/workflows/julia-ci.yml +++ b/.github/workflows/julia-ci.yml @@ -9,7 +9,7 @@ on: workflow_call: inputs: registered: - description: 'The package is registered: adds the downgrade job.' + description: 'The package is registered: adds the downgrade job and the monthly Julia pre run (public repositories).' type: boolean default: false runic: @@ -31,12 +31,41 @@ env: JULIA_CPU_TARGET: 'generic;sandybridge,-xsaveopt,clone_all;haswell,-rdrnd,base(1);x86-64-v4,-rdrnd,base(1)' # Only the default branch saves Julia caches; PR and other branch runs restore the default # branch's cache and save nothing, so they cannot evict it from the 10 GB repository budget. - JULIA_CACHE_SAVE: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} + # Scheduled runs always run on the default branch, but their event carries no repository. + # A private repository runs no push event, so only a manual dispatch on main seeds its cache. + JULIA_CACHE_SAVE: ${{ github.event_name == 'schedule' || github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} jobs: + # Admiral decision 0025: a public repository, whose Actions minutes are free, runs on every event + # its caller sends. A private one runs only on pull requests and manual dispatch, at Julia 1 + # only: its push and scheduled events skip every job. Push, pull request and dispatch events + # carry github.event.repository.private; a scheduled event carries no repository, so the + # visibility job looks it up, on scheduled runs only. + visibility: + name: Visibility + if: github.event_name == 'schedule' + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + contents: read + outputs: + public: ${{ steps.lookup.outputs.public }} + steps: + - id: lookup + env: + GH_TOKEN: ${{ github.token }} + run: | + visibility=$(gh api "repos/$GITHUB_REPOSITORY" --jq .visibility) + echo "public=$([ "$visibility" = public ] && echo true || echo false)" >> "$GITHUB_OUTPUT" + test: name: Core - Julia ${{ matrix.version }} - if: github.event_name != 'pull_request' || !github.event.pull_request.draft + needs: visibility + if: >- + !cancelled() + && (github.event_name != 'pull_request' || !github.event.pull_request.draft) + && (github.event_name != 'push' || !github.event.repository.private) + && (github.event_name != 'schedule' || needs.visibility.outputs.public == 'true') runs-on: ubuntu-latest timeout-minutes: 60 # a cold portable-target build of a large package runs ~30 min permissions: @@ -45,7 +74,8 @@ jobs: strategy: fail-fast: false matrix: - version: ['min', '1'] + # A private repository tests Julia 1 only; its floor is checked in the local record. + version: ${{ fromJSON(github.event.repository.private && '["1"]' || '["min", "1"]') }} env: GROUP: Core steps: @@ -72,7 +102,12 @@ jobs: qa: name: QA - if: github.event_name != 'pull_request' || !github.event.pull_request.draft + needs: visibility + if: >- + !cancelled() + && (github.event_name != 'pull_request' || !github.event.pull_request.draft) + && (github.event_name != 'push' || !github.event.repository.private) + && (github.event_name != 'schedule' || needs.visibility.outputs.public == 'true') runs-on: ubuntu-latest timeout-minutes: 60 # a cold portable-target build of a large package runs ~30 min permissions: @@ -104,9 +139,16 @@ jobs: downgrade: # Direct dependencies at the lowest versions [compat] allows. Weak dependencies are left # alone (mode deps): their extensions do not load in Core, and flooring them would install - # packages such as CUDA for nothing. + # packages such as CUDA for nothing. Public repositories only: it runs at the Julia floor, which + # a private repository checks in its local record. name: Core - lowest compat - if: inputs.registered && (github.event_name != 'pull_request' || !github.event.pull_request.draft) + needs: visibility + if: >- + !cancelled() + && (github.event_name != 'pull_request' || !github.event.pull_request.draft) + && (github.event_name != 'push' || !github.event.repository.private) + && (github.event_name != 'schedule' || needs.visibility.outputs.public == 'true') + && inputs.registered && !github.event.repository.private runs-on: ubuntu-latest timeout-minutes: 60 # a cold portable-target build of a large package runs ~30 min permissions: @@ -142,9 +184,51 @@ jobs: allow_reresolve: false force_latest_compatible_version: false + pre: + # Monthly look at the next Julia release; allowed to fail, reported by admiral's sweep. + name: Core - Julia pre + needs: visibility + if: >- + !cancelled() && inputs.registered + && github.event_name == 'schedule' && needs.visibility.outputs.public == 'true' + runs-on: ubuntu-latest + timeout-minutes: 60 # a cold portable-target build of a large package runs ~30 min + continue-on-error: true + permissions: + actions: write + contents: read + env: + GROUP: Core + steps: + - uses: actions/checkout@v7 + with: + persist-credentials: false + - uses: julia-actions/setup-julia@v3 + with: + version: pre + arch: x64 + - uses: julia-actions/cache@v3 + with: + cache-name: julia-cache;workflow=${{ github.workflow }};job=${{ github.job }};cpu=portable + save-always: ${{ env.JULIA_CACHE_SAVE }} + _job-status: ${{ env.JULIA_CACHE_SAVE == 'true' && job.status || 'not-saved-off-default-branch' }} + - uses: julia-actions/julia-buildpkg@v1 + with: + project: ${{ inputs.project }} + - uses: julia-actions/julia-runtest@v1 + with: + project: ${{ inputs.project }} + coverage: 'false' + format: name: Runic - if: inputs.runic && (github.event_name != 'pull_request' || !github.event.pull_request.draft) + needs: visibility + if: >- + !cancelled() + && (github.event_name != 'pull_request' || !github.event.pull_request.draft) + && (github.event_name != 'push' || !github.event.repository.private) + && (github.event_name != 'schedule' || needs.visibility.outputs.public == 'true') + && inputs.runic runs-on: ubuntu-latest timeout-minutes: 60 # a cold portable-target build of a large package runs ~30 min permissions: diff --git a/.github/workflows/selftest.yml b/.github/workflows/selftest.yml index 297cb57..d5df895 100644 --- a/.github/workflows/selftest.yml +++ b/.github/workflows/selftest.yml @@ -1,8 +1,9 @@ # Self-test: runs julia-ci.yml from this commit against the fixture package. registered and -# runic are on so the downgrade and Runic jobs run too. +# runic are on so the downgrade and Runic jobs run too; the pre job runs only on schedule. name: Self-test on: + push: pull_request: types: [opened, synchronize, reopened, ready_for_review] workflow_dispatch: diff --git a/README.md b/README.md index a8f3013..b925446 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,10 @@ that need a GPU, lab data, long run times or an instrument run on a lab machine | Job | When | What | |---|---|---| -| `test` | always | `GROUP=Core` tests at Julia `min` (the `julia` compat lower bound) and `1` | +| `test` | always | `GROUP=Core` tests at Julia `min` (the `julia` compat lower bound) and `1`; a private repository runs `1` only | | `qa` | always | `GROUP=QA` tests (Aqua, ExplicitImports) at Julia `1` | -| `downgrade` | `registered: true` | `GROUP=Core` at Julia `min`, the oldest supported Julia, with direct dependencies at the lowest versions `[compat]` allows | +| `downgrade` | `registered: true`, public repositories | `GROUP=Core` at Julia `min`, the oldest supported Julia, with direct dependencies at the lowest versions `[compat]` allows | +| `pre` | `registered: true`, public repositories, scheduled runs only | `GROUP=Core` at the Julia prerelease; allowed to fail | | `format` | `runic: true` | Runic formatting check | All jobs run on ubuntu-latest, x64, with a 60-minute timeout, and skip draft pull requests. There @@ -28,7 +29,7 @@ is no coverage upload and no docs job. The `GROUP` variable is read by the lab's | Input | Type | Default | Meaning | |---|---|---|---| -| `registered` | boolean | `false` | The package is in the General registry: adds `downgrade`. | +| `registered` | boolean | `false` | The package is in the General registry: adds `downgrade` and the monthly `pre` run, in a public repository. | | `runic` | boolean | `false` | Adds the Runic formatting check. Runic checks every `.jl` file in the repository. | | `project` | string | `.` | Path to the package within the repository. | @@ -44,12 +45,15 @@ is no coverage upload and no docs job. The `GROUP` variable is read by the lab's 4. The package's tests must follow the lab's test layout: the standard `test/runtests.jl`, a `test/test_groups.toml`, and at least the `Core` and `QA` groups. -The caller runs on pull requests (including when a draft is marked ready) and on manual -dispatch, never on a push or a schedule (admiral decision 0025: the lab tests on its own machines, -and a pull request gets one confirming CI run). Changes only to Markdown files, `dev/` or -`.claude/` do not start a pull-request run. A newer push to a pull request cancels the older run. -Only the default branch saves a Julia cache, so a cache is seeded only by a manual -`workflow_dispatch` run on `main`; until then, pull-request runs start cold. +The caller runs on pushes to `main` or `master` and on tags, on pull requests (including when a +draft is marked ready), on manual dispatch, and monthly. Changes only to Markdown files, `dev/` +or `.claude/` do not start a run. A newer push to a pull request cancels the older run. + +In a private repository, whose Actions minutes are paid, the jobs run only on pull requests and +manual dispatch, and skip push and scheduled events (admiral decision 0025: the lab tests on its +own machines, and its local record checks the Julia floor). Only the default branch saves a Julia +cache, so a private repository's cache is seeded only by a manual `workflow_dispatch` run on +`main`; until then its pull-request runs start cold. Public repositories run on every event. ## Versions @@ -61,5 +65,5 @@ move to it by editing their caller. ## Self-test `.github/workflows/selftest.yml` runs the workflow from the same commit, with `registered` and -`runic` on, against a small fixture package in `test/fixture/FixturePkg`, on pull requests and -manual dispatch. +`runic` on, against a small fixture package in `test/fixture/FixturePkg`, on every push and pull +request. diff --git a/templates/CI.yml b/templates/CI.yml index 516056c..4d8b719 100644 --- a/templates/CI.yml +++ b/templates/CI.yml @@ -3,10 +3,16 @@ name: CI on: + push: + branches: [main, master] + tags: ['*'] + paths-ignore: ['**.md', 'dev/**', '.claude/**'] pull_request: types: [opened, synchronize, reopened, ready_for_review] paths-ignore: ['**.md', 'dev/**', '.claude/**'] workflow_dispatch: + schedule: + - cron: '23 5 1 * *' # monthly concurrency: group: ${{ github.workflow }}-${{ github.ref }} From a5fabb7e681b53fef16f9b1c0006b5519c542fe1 Mon Sep 17 00:00:00 2001 From: kalidke Date: Mon, 28 Sep 2026 10:21:28 -0600 Subject: [PATCH 5/5] List the events that may run; private repositories save their caches Review of PR 2: the job conditions now list what may run (pull_request, workflow_dispatch, a public repository's event, or a scheduled run the visibility job found public) instead of what to skip, so an event a private caller adds later cannot run paid jobs. The downgrade job's gate is the public test alone, which makes its old push clause and private check redundant. A private repository has no push runs, so every one of its runs now saves the Julia cache; the Runic job gains actions: write for the old-cache deletion that follows a save off the default branch, and loses delete-old-caches: false. The README says how each kind is seeded and that GitHub drops a cache unused for 7 days. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/julia-ci.yml | 42 ++++++++++++++++++---------------- README.md | 12 ++++++---- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/.github/workflows/julia-ci.yml b/.github/workflows/julia-ci.yml index b840c67..3374a23 100644 --- a/.github/workflows/julia-ci.yml +++ b/.github/workflows/julia-ci.yml @@ -29,18 +29,19 @@ env: # mix AMD EPYC 7763, 9V74 and Intel Xeon 8370C, 8573C, and a job landing on an incompatible # one rejects the cache ("different system or CPU target") and rebuilds every dependency. JULIA_CPU_TARGET: 'generic;sandybridge,-xsaveopt,clone_all;haswell,-rdrnd,base(1);x86-64-v4,-rdrnd,base(1)' - # Only the default branch saves Julia caches; PR and other branch runs restore the default - # branch's cache and save nothing, so they cannot evict it from the 10 GB repository budget. - # Scheduled runs always run on the default branch, but their event carries no repository. - # A private repository runs no push event, so only a manual dispatch on main seeds its cache. - JULIA_CACHE_SAVE: ${{ github.event_name == 'schedule' || github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} + # In a public repository only the default branch saves Julia caches; PR and other branch runs + # restore the default branch's cache and save nothing, so they cannot evict it from the 10 GB + # repository budget. Scheduled runs always run on the default branch, but their event carries + # no repository. A private repository runs no push event, so every one of its runs saves. + JULIA_CACHE_SAVE: ${{ github.event_name == 'schedule' || github.event.repository.private || github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} jobs: # Admiral decision 0025: a public repository, whose Actions minutes are free, runs on every event # its caller sends. A private one runs only on pull requests and manual dispatch, at Julia 1 - # only: its push and scheduled events skip every job. Push, pull request and dispatch events - # carry github.event.repository.private; a scheduled event carries no repository, so the - # visibility job looks it up, on scheduled runs only. + # only; every other event skips every job. The conditions list what may run, so an event a + # caller adds later is covered. Most events carry github.event.repository.private; a scheduled + # event carries no repository, so the visibility job looks it up, on scheduled runs only. Never + # test private == false: on an event with no repository, null == false is true. visibility: name: Visibility if: github.event_name == 'schedule' @@ -64,8 +65,9 @@ jobs: if: >- !cancelled() && (github.event_name != 'pull_request' || !github.event.pull_request.draft) - && (github.event_name != 'push' || !github.event.repository.private) - && (github.event_name != 'schedule' || needs.visibility.outputs.public == 'true') + && (github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' + || (github.event.repository && !github.event.repository.private) + || needs.visibility.outputs.public == 'true') runs-on: ubuntu-latest timeout-minutes: 60 # a cold portable-target build of a large package runs ~30 min permissions: @@ -106,8 +108,9 @@ jobs: if: >- !cancelled() && (github.event_name != 'pull_request' || !github.event.pull_request.draft) - && (github.event_name != 'push' || !github.event.repository.private) - && (github.event_name != 'schedule' || needs.visibility.outputs.public == 'true') + && (github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' + || (github.event.repository && !github.event.repository.private) + || needs.visibility.outputs.public == 'true') runs-on: ubuntu-latest timeout-minutes: 60 # a cold portable-target build of a large package runs ~30 min permissions: @@ -146,9 +149,9 @@ jobs: if: >- !cancelled() && (github.event_name != 'pull_request' || !github.event.pull_request.draft) - && (github.event_name != 'push' || !github.event.repository.private) - && (github.event_name != 'schedule' || needs.visibility.outputs.public == 'true') - && inputs.registered && !github.event.repository.private + && ((github.event.repository && !github.event.repository.private) + || needs.visibility.outputs.public == 'true') + && inputs.registered runs-on: ubuntu-latest timeout-minutes: 60 # a cold portable-target build of a large package runs ~30 min permissions: @@ -226,12 +229,14 @@ jobs: if: >- !cancelled() && (github.event_name != 'pull_request' || !github.event.pull_request.draft) - && (github.event_name != 'push' || !github.event.repository.private) - && (github.event_name != 'schedule' || needs.visibility.outputs.public == 'true') + && (github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' + || (github.event.repository && !github.event.repository.private) + || needs.visibility.outputs.public == 'true') && inputs.runic runs-on: ubuntu-latest timeout-minutes: 60 # a cold portable-target build of a large package runs ~30 min permissions: + actions: write # private PR runs save, and the cache step deletes the caches they replace contents: read steps: - uses: actions/checkout@v7 @@ -246,9 +251,6 @@ jobs: cache-name: julia-cache;workflow=${{ github.workflow }};job=${{ github.job }};cpu=portable save-always: ${{ env.JULIA_CACHE_SAVE }} _job-status: ${{ env.JULIA_CACHE_SAVE == 'true' && job.status || 'not-saved-off-default-branch' }} - # Off the default branch nothing is saved (JULIA_CACHE_SAVE); on it, julia-actions/cache - # never deletes old caches, so deletion would need actions: write for nothing. - delete-old-caches: 'false' - uses: fredrikekre/runic-action@v1 with: version: '1' diff --git a/README.md b/README.md index b925446..759a09f 100644 --- a/README.md +++ b/README.md @@ -50,10 +50,14 @@ draft is marked ready), on manual dispatch, and monthly. Changes only to Markdow or `.claude/` do not start a run. A newer push to a pull request cancels the older run. In a private repository, whose Actions minutes are paid, the jobs run only on pull requests and -manual dispatch, and skip push and scheduled events (admiral decision 0025: the lab tests on its -own machines, and its local record checks the Julia floor). Only the default branch saves a Julia -cache, so a private repository's cache is seeded only by a manual `workflow_dispatch` run on -`main`; until then its pull-request runs start cold. Public repositories run on every event. +manual dispatch and skip every other event (admiral decision 0025: the lab tests on its own +machines, and its local record checks the Julia floor). Public repositories run on every event. + +A public repository saves its Julia cache only from the default branch, and pull requests restore +that cache. A private repository has no push runs, so every one of its runs saves: later runs on +the same pull request start warm, and a new pull request restores only the default branch's +cache, which a manual `workflow_dispatch` run on `main` seeds. GitHub drops a cache unused for 7 +days, so the first run after a quiet week starts cold. ## Versions