Skip to content

feat: auto-update milestones on release#84

Merged
skjnldsv merged 10 commits into
mainfrom
feature/auto-milestones
Jun 8, 2026
Merged

feat: auto-update milestones on release#84
skjnldsv merged 10 commits into
mainfrom
feature/auto-milestones

Conversation

@skjnldsv

@skjnldsv skjnldsv commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Adds automatic milestone management to the release pipeline
  • On stable releases (e.g. v33.0.4): closes the released milestone, moves all open issues to the next patch milestone (Nextcloud 33.0.5), and ensures two open patch milestones always exist by creating the upcoming one (Nextcloud 33.0.6) — across all ~30 release repositories
  • On first beta (e.g. v35.0.0beta1): creates the next major milestone (Nextcloud 35) across all repos
  • Optional --due-date to set a due date on newly created milestones
  • A companion audit script detects orphaned/missing milestones and inconsistencies, and runs after every update
  • Script uses gh CLI, no PHP/composer needed in CI
  • Can be run standalone: bash .github/scripts/update-milestones.sh v33.0.4 stable33.json tag-only.json --dry-run
  • Replaces manual use of github_helper/milestoneupdater and github_helper/milestonemover

Changes

  • New: .github/scripts/update-milestones.sh — standalone milestone updater
  • New: .github/scripts/audit-milestones.sh — consistency audit (orphans, missing milestones, missing due dates)
  • New: .github/workflows/release-milestones.yml — reusable workflow (also manually triggerable with dry-run / audit-only)
  • New: tests/milestone-scripts/** + .github/workflows/test-milestone-scripts.yml — test harness and CI (see below)
  • Edit: .github/workflows/release.yml — wired as parallel job after tagging
  • Edit: README.md and .github/scripts/README.md — documentation

How it works

  1. Parses the release tag to determine milestone names
  2. Builds repo list from stableXX.json + tag-only.json (no separate config)
  3. For each repo: finds milestones via GitHub API, closes/creates/moves as needed
  4. Writes a summary table to GitHub Step Summary
  5. Best-effort: individual repo failures don't block others

Bugs found & fixed during testing

Writing the tests surfaced two real bugs in move_issues(), now fixed in this PR:

  1. Captured-return pollutionmove_issues() printed per-issue progress to stdout alongside the count it returned via command substitution. The caller's arithmetic on that polluted value failed, aborting the per-repo block before the milestone was closed or the upcoming one created whenever a milestone had open issues. Progress now goes to stderr.
  2. Pagination skip — the page counter was incremented while issues were being moved out of the source milestone, so after the first page was moved the remaining issues shifted onto page 1 and later pages returned empty, silently leaving issues behind on the milestone being closed. All open issue numbers are now gathered before any move.

Testing

The milestone scripts produce no files — they only call gh api — so the harness injects a stateful fake gh (tests/milestone-scripts/fake-gh.sh) that serves canned milestone/issue/tag responses from a JSON fixture and records every mutating call to a journal.

  • Snapshot scenarios (run.sh): 18 scenarios asserting on the journal of mutations + stdout + exit code — patch release, first stable (.0.0 alt name), first beta (+ idempotent), missing-next auto-create, existing-upcoming skip, --due-date, 150-issue pagination, pre-release no-op, bad due-date; audit: healthy, released-still-open, missing next/upcoming, no-due-date, orphan, master major-inference, no-stable.
  • Unit tests (unit.sh): argument validation, due-date format parsing, release-type routing, repo-list merge of both config formats.
  • CI (test-milestone-scripts.yml): shellcheck + both suites, fully offline (no real gh/network).

Run locally:

bash tests/milestone-scripts/run.sh     # snapshot suite
bash tests/milestone-scripts/unit.sh    # unit suite

@skjnldsv skjnldsv self-assigned this Jun 5, 2026
skjnldsv added 10 commits June 8, 2026 09:20
Close the released milestone, create the next patch milestone, and move
open issues across all release repositories. On first beta of a new
major version, create the major milestone instead.

The script uses gh CLI and can be run standalone outside of CI.

Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
When closing a released milestone (e.g. 33.0.4), create the milestone
two patches ahead (33.0.6) so there are always two open: the next
release (33.0.5, issues move here) and the upcoming one (33.0.6).

Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
New audit-milestones.sh script checks all repos against expected state:
- Orphaned milestones that should be closed (version already released)
- Missing milestones (next or upcoming patch not created)
- Naming issues (wrong casing or spacing)
- Missing due dates on upcoming milestones

Runs automatically after every milestone update. Can also be triggered
standalone via the audit_only workflow input.

Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
Release dates follow a fixed schedule (always Thursdays) and are set
manually. Auto-computing dates risks cascading wrong values if a
release slips. The audit script already flags missing due dates.

Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
Accept an optional --due-date YYYY-MM-DD flag to set due dates on newly
created milestones. Exposed as a workflow_dispatch input so it can be
set directly from the Actions UI. When omitted, milestones are created
without a due date.

Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
move_issues() wrote per-issue progress to stdout next to its return
value, so the caller captured both and the arithmetic on the moved count
failed - aborting the per-repo block before the milestone was closed or
the upcoming one created. It also bumped the page counter while moving
issues out of the source milestone, so after the first page was moved the
remaining issues shifted onto page 1 and later pages returned empty,
silently leaving issues behind on the closing milestone.

move_issues now gathers all open issue numbers first (stable pagination,
no mutation), then moves them, and writes progress to stderr so only the
count is returned. Also adds a GH wrapper variable so the scripts' gh
calls can be intercepted by tests, and drops two unused variables flagged
by shellcheck.

Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
The milestone scripts produce no files - they only call `gh api` - so a
stateful fake gh (fake-gh.sh) serves canned milestone/issue/tag responses
from a JSON fixture and records every mutating call to a journal. Snapshot
scenarios assert on that journal plus stdout and exit code; unit.sh covers
argument validation, due-date parsing, release-type routing and the
repo-list merge. A new workflow runs shellcheck and both suites on changes
to the scripts or tests. Everything is offline - no real gh or network.

Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
@skjnldsv skjnldsv force-pushed the feature/auto-milestones branch from e0179da to a9cf4f6 Compare June 8, 2026 11:20
@skjnldsv skjnldsv added the enhancement New feature or request label Jun 8, 2026
@skjnldsv skjnldsv merged commit 916536b into main Jun 8, 2026
1 check passed
@skjnldsv skjnldsv deleted the feature/auto-milestones branch June 8, 2026 12:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Development

Successfully merging this pull request may close these issues.

1 participant