From bef8e667e33ff37d7573ce882bc35e1a72e54260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Fri, 18 Sep 2026 18:37:55 +0200 Subject: [PATCH 1/3] ci(release): skip the nightly submodule resolver outside latest mode `nightly-submodules` ran on every trigger, including PRs and release tags, purely so that its dependents' `needs:` would not short-circuit. On a pinned build it checks out the repo, hits its own guard, writes latest=false and exits, so every ordinary run queued a runner and made four build jobs wait on a job that had nothing to do. Gate the job on the same condition its guard step already uses, and let the dependents survive a skipped dependency with `!cancelled()`. Their conditions and `name:` expressions already treat empty outputs as pinned mode, so a skipped resolver reads exactly like the latest=false it used to write. Nightly and `submodules=latest` behaviour is unchanged. A failed resolver in latest mode still cannot pull the build jobs in: their remaining condition requires latest == 'true' && moved == 'true', which empty outputs never satisfy. --- .github/workflows/release.yml | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d8f82be00..8eb75143f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -275,13 +275,21 @@ jobs: } >> "$GITHUB_STEP_SUMMARY" nightly-submodules: - # Always runs (cheap) so the build jobs' `needs:` never short-circuits - # them. Only does real work on the nightly cron or `submodules=latest`: - # resolves every submodule, recursively, to its upstream master and + # Only runs in latest mode: the nightly cron or `submodules=latest`. + # Resolves every submodule, recursively, to its upstream master and # reports whether anything moved relative to the committed pointers. # Build jobs check out exactly these SHAs so every leg tests the same # tips even when they start minutes apart. Nothing is committed. + # + # On every other trigger this job is skipped, so ordinary pushes, PRs and + # release tags do not queue a runner to do nothing. Its dependents carry + # `!cancelled()` so a skipped dependency does not cascade into skipping + # them, and they read its outputs as empty, which their conditions and + # `name:` expressions already treat as pinned mode. name: Resolve latest submodule tips (nightly) + if: >- + (github.event_name == 'schedule' && github.event.schedule == '0 3 * * *') + || inputs.submodules == 'latest' runs-on: ubuntu-latest outputs: latest: ${{ steps.resolve.outputs.latest }} @@ -379,8 +387,9 @@ jobs: # Normal triggers as before; additionally the nightly cron, but only when # a submodule actually moved off its committed pointer. if: >- - github.event_name == 'push' || github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' - || (needs.nightly-submodules.outputs.latest == 'true' && needs.nightly-submodules.outputs.moved == 'true') + !cancelled() + && (github.event_name == 'push' || github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' + || (needs.nightly-submodules.outputs.latest == 'true' && needs.nightly-submodules.outputs.moved == 'true')) runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v7 @@ -451,8 +460,9 @@ jobs: # Normal triggers as before; additionally the nightly cron, but only when # a submodule actually moved off its committed pointer. if: >- - github.event_name == 'push' || github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' - || (needs.nightly-submodules.outputs.latest == 'true' && needs.nightly-submodules.outputs.moved == 'true') + !cancelled() + && (github.event_name == 'push' || github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' + || (needs.nightly-submodules.outputs.latest == 'true' && needs.nightly-submodules.outputs.moved == 'true')) runs-on: ${{ matrix.os }} continue-on-error: ${{ matrix.experimental }} env: @@ -809,8 +819,9 @@ jobs: # Normal triggers as before; additionally the nightly cron, but only when # a submodule actually moved off its committed pointer. if: >- - github.event_name == 'push' || github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' - || (needs.nightly-submodules.outputs.latest == 'true' && needs.nightly-submodules.outputs.moved == 'true') + !cancelled() + && (github.event_name == 'push' || github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' + || (needs.nightly-submodules.outputs.latest == 'true' && needs.nightly-submodules.outputs.moved == 'true')) runs-on: ubuntu-22.04 container: image: quay.io/pypa/manylinux_2_28_x86_64 @@ -1115,8 +1126,9 @@ jobs: # Normal triggers as before; additionally the nightly cron, but only when # a submodule actually moved off its committed pointer. if: >- - github.event_name == 'push' || github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' - || (needs.nightly-submodules.outputs.latest == 'true' && needs.nightly-submodules.outputs.moved == 'true') + !cancelled() + && (github.event_name == 'push' || github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' + || (needs.nightly-submodules.outputs.latest == 'true' && needs.nightly-submodules.outputs.moved == 'true')) runs-on: ${{ matrix.os }} continue-on-error: ${{ matrix.experimental }} env: From 932fe28cab0513ce4e4904b53de0b55faf522ad4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Fri, 18 Sep 2026 18:38:06 +0200 Subject: [PATCH 2/3] ci(release): put the matrix leg back in the build job names Since #1448 gave build-qt and build-tauri an explicit `name:` for the `[latest submodules]` suffix, GitHub stopped appending the matrix values it adds only to unnamed jobs. Every leg rendered identically: four "Build Qt artifacts" and six "Build Tauri artifacts", so a red run did not say which platform failed without opening jobs one by one. Name the OS explicitly, plus the research flag, reusing the idiom the artifact upload steps already use. os and research fully disambiguate: python_version, node_version and the skip flags are single-valued in both matrices. --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8eb75143f..007c9d8a3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -455,7 +455,7 @@ jobs: run: python3 -m pytest scripts/tests/test_generate_latest_json.py scripts/tests/test_configure_tauri_release.py -q build-qt: - name: Build Qt artifacts${{ needs.nightly-submodules.outputs.latest == 'true' && ' [latest submodules]' || '' }} + name: Build Qt artifacts (${{ matrix.os }}${{ matrix.research && ', research' || '' }})${{ needs.nightly-submodules.outputs.latest == 'true' && ' [latest submodules]' || '' }} needs: nightly-submodules # Normal triggers as before; additionally the nightly cron, but only when # a submodule actually moved off its committed pointer. @@ -1121,7 +1121,7 @@ jobs: path: dist/activitywatch-*.* build-tauri: - name: Build Tauri artifacts${{ needs.nightly-submodules.outputs.latest == 'true' && ' [latest submodules]' || '' }} + name: Build Tauri artifacts (${{ matrix.os }}${{ matrix.research && ', research' || '' }})${{ needs.nightly-submodules.outputs.latest == 'true' && ' [latest submodules]' || '' }} needs: nightly-submodules # Normal triggers as before; additionally the nightly cron, but only when # a submodule actually moved off its committed pointer. From 5414cffdcff751b10666609c18aea8d06e201349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Fri, 18 Sep 2026 21:17:31 +0200 Subject: [PATCH 3/3] ci(release): keep a failed resolver blocking, and label the effective edition Greptile P1: with only !cancelled(), a workflow_dispatch run with submodules=latest whose resolver FAILED would still satisfy the workflow_dispatch disjunct, so the full matrix would build the pinned tree and report success for a run whose whole point was to validate the latest tips. The PR description claimed otherwise; it was wrong, because that disjunct short-circuits before the latest/moved check. Gate the dependents on the resolver's result being success or skipped, which restores the old failure semantics while keeping the new skip behaviour. Greptile P2: research tag pushes and edition=research dispatches enable Research Edition through AW_RESEARCH_EDITION while matrix.research stays false, so those legs were labelled as ordinary builds. Label the effective edition, mirroring each job's own AW_RESEARCH_EDITION expression. Applied to the manylinux job too, which Greptile did not flag but which builds the research edition on the same triggers. --- .github/workflows/release.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 007c9d8a3..be6cbc9c1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -388,6 +388,7 @@ jobs: # a submodule actually moved off its committed pointer. if: >- !cancelled() + && contains(fromJSON('["success", "skipped"]'), needs.nightly-submodules.result) && (github.event_name == 'push' || github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' || (needs.nightly-submodules.outputs.latest == 'true' && needs.nightly-submodules.outputs.moved == 'true')) runs-on: ubuntu-22.04 @@ -455,12 +456,13 @@ jobs: run: python3 -m pytest scripts/tests/test_generate_latest_json.py scripts/tests/test_configure_tauri_release.py -q build-qt: - name: Build Qt artifacts (${{ matrix.os }}${{ matrix.research && ', research' || '' }})${{ needs.nightly-submodules.outputs.latest == 'true' && ' [latest submodules]' || '' }} + name: Build Qt artifacts (${{ matrix.os }}${{ (matrix.research || (github.event_name == 'workflow_dispatch' && inputs.edition == 'research') || endsWith(github.ref_name, '-research')) && ', research' || '' }})${{ needs.nightly-submodules.outputs.latest == 'true' && ' [latest submodules]' || '' }} needs: nightly-submodules # Normal triggers as before; additionally the nightly cron, but only when # a submodule actually moved off its committed pointer. if: >- !cancelled() + && contains(fromJSON('["success", "skipped"]'), needs.nightly-submodules.result) && (github.event_name == 'push' || github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' || (needs.nightly-submodules.outputs.latest == 'true' && needs.nightly-submodules.outputs.moved == 'true')) runs-on: ${{ matrix.os }} @@ -814,12 +816,13 @@ jobs: path: dist/activitywatch-*.* build-qt-manylinux-2-28: - name: Build Qt artifacts (manylinux_2_28 — glibc 2.28 ABI floor)${{ needs.nightly-submodules.outputs.latest == 'true' && ' [latest submodules]' || '' }} + name: Build Qt artifacts (manylinux_2_28 — glibc 2.28 ABI floor${{ ((github.event_name == 'workflow_dispatch' && inputs.edition == 'research') || endsWith(github.ref_name, '-research')) && ', research' || '' }})${{ needs.nightly-submodules.outputs.latest == 'true' && ' [latest submodules]' || '' }} needs: nightly-submodules # Normal triggers as before; additionally the nightly cron, but only when # a submodule actually moved off its committed pointer. if: >- !cancelled() + && contains(fromJSON('["success", "skipped"]'), needs.nightly-submodules.result) && (github.event_name == 'push' || github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' || (needs.nightly-submodules.outputs.latest == 'true' && needs.nightly-submodules.outputs.moved == 'true')) runs-on: ubuntu-22.04 @@ -1121,12 +1124,13 @@ jobs: path: dist/activitywatch-*.* build-tauri: - name: Build Tauri artifacts (${{ matrix.os }}${{ matrix.research && ', research' || '' }})${{ needs.nightly-submodules.outputs.latest == 'true' && ' [latest submodules]' || '' }} + name: Build Tauri artifacts (${{ matrix.os }}${{ (matrix.research || (github.event_name == 'workflow_dispatch' && inputs.edition == 'research') || endsWith(github.ref_name, '-research')) && ', research' || '' }})${{ needs.nightly-submodules.outputs.latest == 'true' && ' [latest submodules]' || '' }} needs: nightly-submodules # Normal triggers as before; additionally the nightly cron, but only when # a submodule actually moved off its committed pointer. if: >- !cancelled() + && contains(fromJSON('["success", "skipped"]'), needs.nightly-submodules.result) && (github.event_name == 'push' || github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' || (needs.nightly-submodules.outputs.latest == 'true' && needs.nightly-submodules.outputs.moved == 'true')) runs-on: ${{ matrix.os }}