Skip to content

ci: re-dispatch releases and image builds whose push events were dropped - #598

Open
bnsoni wants to merge 2 commits into
mainfrom
fix/ci/release-watchdog
Open

bnsoni wants to merge 2 commits into
mainfrom
fix/ci/release-watchdog

Conversation

@bnsoni

@bnsoni bnsoni commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Summary

GitHub occasionally drops the push event for a merge to main. The merge then gets no workflow runs at all, so nothing is released or built until someone notices. That happened to iblai/lms#281 on 2026-10-21: its merge commit got no Release run and no check suite. os uses the same chain (merge → Release → v* tag → Docker Build) with no manual trigger on either workflow, so the same dropped event here would leave a merge unreleased until the next merge.

  • release-watchdog.yml (new, every 10 min):
    • If main's head has no Release run after 10 min, it dispatches Release.
    • If the newest vX.Y.Z tag has no Docker Build run, it dispatches the build on that tag.
    • Hand-cut tags such as v0.171.0-patch.2 are ignored, because they aren't release builds.
    • Runs are matched by commit SHA, so the dispatched run satisfies the check and it never fires twice for the same commit.
    • Failed or cancelled runs are left alone. They still count as runs, and they need a person, not a retry loop.
  • release.yml:
    • Adds workflow_dispatch, limited to main.
    • Adds a step that skips release-it when main has nothing unreleased. Without it, a duplicate run would cut an empty patch release, because the conventionalcommits preset recommends patch for zero commits.
    • Hand-cut tags such as -patch.1 never count as the last release (--exclude).
    • The step counts the same commits as the job's if (ignoring chore(release): and chore(tauri):).
  • trigger-docker-build.yml:
    • Adds workflow_dispatch, for release tags only (the same patterns as the push filter). Any other ref's package.json still carries the last released version, so building it would overwrite that version's image.
    • Adds a per-ref concurrency group, so a late push event and a watchdog dispatch can't build and push the same tags at once.

Test plan

  • actionlint and shellcheck are clean on the new and changed steps. The one SC2086 note in trigger-docker-build.yml already exists on main.
  • The watchdog script was dry-run against this repo with dispatch stubbed out. It printed main@736795c58 has a release.yml run and v0.174.2@736795c58 has a trigger-docker-build.yml run.
  • The lms copy of the same script flagged the dropped lms merge exactly. It left release commits, regular merges and heads inside the grace period alone.
  • The unreleased-commit step was checked on real history: the release commit 736795c5 (v0.174.2) gives 0, and the Feat/iblai js bump 2.46.0 #596 merge 1f38f7ed gives 8.
  • After merge:
    • This PR's own merge goes through the normal path.
    • The next watchdog run should log both "has a … run" lines.
    • To try the dispatch path by hand, use Actions → Release Watchdog → Run workflow.

Notes: the repo is public, so the 10-minute schedule costs no Actions minutes. The same change for lms is iblai/lms#282.

Known limitations

  • Only the newest release tag is checked. If a tag's event is dropped and a newer release lands before the next tick, the older version gets no image; the newer one contains its changes. Rebuilding older tags automatically would also move :latest backwards.
  • The grace period is measured from the commit date. A commit pushed long after it was made can draw one redundant Release dispatch, which is a no-op because of the unreleased-commit check.
  • Desktop installers (tauri-autoversion.yml and the app-v* → DMG/Windows builds) have the same exposure and are not covered here. ensure_run could cover them in a follow-up.
  • The pre-push review flagged the tag lookup for pagination. matching-refs currently returns every tag on one page (183 lms, 492 os), but --paginate is added anyway.

🤖 Generated with Claude Code

GitHub occasionally drops the push event for a merge to main, so the merge gets
no workflow runs at all and nothing is released or built until someone notices
(iblai/lms#281 on 2026-10-21; os uses the same release chain).

- release-watchdog.yml runs every 10 min: when main's head has no Release run
  after 5 min it dispatches Release, and when the newest release tag has no
  Docker Build run it dispatches the build on that tag.
- release.yml gains workflow_dispatch and skips release-it when nothing is
  unreleased; release-it would otherwise cut an empty patch release.
- trigger-docker-build.yml gains workflow_dispatch, restricted to tag refs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@bnsoni
bnsoni requested a review from mamigot as a code owner October 21, 2026 15:29
- Docker Build: a manual dispatch builds only release tags (same patterns as
  the push filter), and a per-ref concurrency group stops a late push event and
  a watchdog dispatch from building and pushing the same tags at once.
- Release: hand-cut tags (e.g. -patch.1) never count as the last release.
- Watchdog: 10-minute grace before a dispatch, paginated tag lookup, and an
  explicit error when no release tag is found.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant