From 2ea3524e62ee45860d98a83b67acb76069ef1175 Mon Sep 17 00:00:00 2001 From: Jeff Dickey Date: Tue, 7 Jul 2026 14:58:30 -0500 Subject: [PATCH 1/6] gate autorelease on fix and feat commits --- .github/workflows/auto-merge-release.yml | 14 +++++++++++ .github/workflows/release-plz.yml | 31 ++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/.github/workflows/auto-merge-release.yml b/.github/workflows/auto-merge-release.yml index ef174d99..52a30206 100644 --- a/.github/workflows/auto-merge-release.yml +++ b/.github/workflows/auto-merge-release.yml @@ -17,10 +17,24 @@ jobs: if: github.repository == 'jdx/usage' runs-on: ubuntu-latest steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + fetch-depth: 0 - name: Merge release into main run: | set -euo pipefail + latest_tag=$(git tag --list 'v[0-9]*' --sort=-v:refname | head -1 || true) + if [ -n "$latest_tag" ]; then + range="${latest_tag}..HEAD" + else + range="HEAD" + fi + if ! git log --format=%s "$range" | grep -Eq '^(fix|feat)(\([^)]+\))?!?: '; then + echo "No pending fix/feat commits since the latest release; not auto-merging release PRs" + exit 0 + fi + PR_NUMBER=$(gh pr list -R jdx/usage --base main --head release --state open --limit 100 --json number,headRefName,headRepositoryOwner \ --jq '[.[] | select(.headRefName == "release") | select(.headRepositoryOwner.login == "jdx")][0].number // empty') if [ -z "$PR_NUMBER" ]; then diff --git a/.github/workflows/release-plz.yml b/.github/workflows/release-plz.yml index d23bf805..df875dc7 100644 --- a/.github/workflows/release-plz.yml +++ b/.github/workflows/release-plz.yml @@ -20,7 +20,38 @@ concurrency: group: release-plz jobs: + pending-release: + runs-on: ubuntu-latest + outputs: + has_release_commits: ${{ steps.commits.outputs.has_release_commits }} + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + fetch-depth: 0 + - name: Check for fix/feat commits since the latest release + id: commits + run: | + set -euo pipefail + + latest_tag=$(git tag --list 'v[0-9]*' --sort=-v:refname | head -1 || true) + if [ -n "$latest_tag" ]; then + range="${latest_tag}..HEAD" + echo "Checking pending commits since ${latest_tag}" + else + range="HEAD" + echo "No v* release tag found; checking all commits" + fi + + if git log --format=%s "$range" | grep -Eq '^(fix|feat)(\([^)]+\))?!?: '; then + echo "has_release_commits=true" >> "$GITHUB_OUTPUT" + else + echo "No pending fix/feat commits; skipping autorelease." + echo "has_release_commits=false" >> "$GITHUB_OUTPUT" + fi + release-plz: + needs: pending-release + if: needs.pending-release.outputs.has_release_commits == 'true' runs-on: ubuntu-latest steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 From 29dc63cfc88319bdd1b13221b55c7ac3c9448773 Mon Sep 17 00:00:00 2001 From: Jeff Dickey Date: Tue, 7 Jul 2026 15:03:42 -0500 Subject: [PATCH 2/6] ci: run autorelease daily after seven days --- .github/workflows/auto-merge-release.yml | 13 ++++++++++--- .github/workflows/release-plz.yml | 13 ++++++++++++- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/.github/workflows/auto-merge-release.yml b/.github/workflows/auto-merge-release.yml index 52a30206..45ad26d7 100644 --- a/.github/workflows/auto-merge-release.yml +++ b/.github/workflows/auto-merge-release.yml @@ -2,8 +2,7 @@ name: auto-merge-release on: schedule: - # 10:00 UTC = 4am CST (winter) / 5am CDT (summer) - - cron: "0 10 * * 1" + - cron: "0 10 * * *" workflow_dispatch: permissions: {} @@ -27,6 +26,14 @@ jobs: latest_tag=$(git tag --list 'v[0-9]*' --sort=-v:refname | head -1 || true) if [ -n "$latest_tag" ]; then range="${latest_tag}..HEAD" + tag_time=$(git log -1 --format=%ct "$latest_tag") + now=$(date +%s) + min_age=$((7 * 24 * 60 * 60)) + age=$((now - tag_time)) + if [ "$age" -lt "$min_age" ]; then + echo "Latest release ${latest_tag} is less than 7 days old; not auto-merging release PRs" + exit 0 + fi else range="HEAD" fi @@ -49,7 +56,7 @@ jobs: exit 0 fi - echo "PR #$PR_NUMBER is ready for the Monday morning release window" + echo "PR #$PR_NUMBER is ready for the daily release window" gh pr merge "$PR_NUMBER" -R jdx/usage --squash --auto env: GH_TOKEN: ${{ secrets.MY_RELEASE_PLEASE_TOKEN }} diff --git a/.github/workflows/release-plz.yml b/.github/workflows/release-plz.yml index df875dc7..f5963a1d 100644 --- a/.github/workflows/release-plz.yml +++ b/.github/workflows/release-plz.yml @@ -11,6 +11,8 @@ on: branches: - main - release-plz + schedule: + - cron: "0 0 * * *" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -28,7 +30,7 @@ jobs: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 0 - - name: Check for fix/feat commits since the latest release + - name: Check release age and fix/feat commits id: commits run: | set -euo pipefail @@ -37,6 +39,15 @@ jobs: if [ -n "$latest_tag" ]; then range="${latest_tag}..HEAD" echo "Checking pending commits since ${latest_tag}" + tag_time=$(git log -1 --format=%ct "$latest_tag") + now=$(date +%s) + min_age=$((7 * 24 * 60 * 60)) + age=$((now - tag_time)) + if [ "$age" -lt "$min_age" ]; then + echo "Latest release ${latest_tag} is less than 7 days old; skipping autorelease." + echo "has_release_commits=false" >> "$GITHUB_OUTPUT" + exit 0 + fi else range="HEAD" echo "No v* release tag found; checking all commits" From d637dfaeeac77cd1f390acefe6138ffd93fab114 Mon Sep 17 00:00:00 2001 From: Jeff Dickey Date: Tue, 7 Jul 2026 15:06:14 -0500 Subject: [PATCH 3/6] ci: move autorelease cadence gate to auto-merge --- .github/workflows/release-plz.yml | 42 ------------------------------- 1 file changed, 42 deletions(-) diff --git a/.github/workflows/release-plz.yml b/.github/workflows/release-plz.yml index f5963a1d..d23bf805 100644 --- a/.github/workflows/release-plz.yml +++ b/.github/workflows/release-plz.yml @@ -11,8 +11,6 @@ on: branches: - main - release-plz - schedule: - - cron: "0 0 * * *" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -22,47 +20,7 @@ concurrency: group: release-plz jobs: - pending-release: - runs-on: ubuntu-latest - outputs: - has_release_commits: ${{ steps.commits.outputs.has_release_commits }} - steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - fetch-depth: 0 - - name: Check release age and fix/feat commits - id: commits - run: | - set -euo pipefail - - latest_tag=$(git tag --list 'v[0-9]*' --sort=-v:refname | head -1 || true) - if [ -n "$latest_tag" ]; then - range="${latest_tag}..HEAD" - echo "Checking pending commits since ${latest_tag}" - tag_time=$(git log -1 --format=%ct "$latest_tag") - now=$(date +%s) - min_age=$((7 * 24 * 60 * 60)) - age=$((now - tag_time)) - if [ "$age" -lt "$min_age" ]; then - echo "Latest release ${latest_tag} is less than 7 days old; skipping autorelease." - echo "has_release_commits=false" >> "$GITHUB_OUTPUT" - exit 0 - fi - else - range="HEAD" - echo "No v* release tag found; checking all commits" - fi - - if git log --format=%s "$range" | grep -Eq '^(fix|feat)(\([^)]+\))?!?: '; then - echo "has_release_commits=true" >> "$GITHUB_OUTPUT" - else - echo "No pending fix/feat commits; skipping autorelease." - echo "has_release_commits=false" >> "$GITHUB_OUTPUT" - fi - release-plz: - needs: pending-release - if: needs.pending-release.outputs.has_release_commits == 'true' runs-on: ubuntu-latest steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 From 2247edcebd305f2b9637a540b67c23b5fb42a0f5 Mon Sep 17 00:00:00 2001 From: Jeff Dickey Date: Tue, 7 Jul 2026 15:13:24 -0500 Subject: [PATCH 4/6] ci: fix auto-merge checkout permissions --- .github/workflows/auto-merge-release.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/auto-merge-release.yml b/.github/workflows/auto-merge-release.yml index 45ad26d7..233a3cde 100644 --- a/.github/workflows/auto-merge-release.yml +++ b/.github/workflows/auto-merge-release.yml @@ -15,18 +15,21 @@ jobs: merge: if: github.repository == 'jdx/usage' runs-on: ubuntu-latest + permissions: + contents: read steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 0 + persist-credentials: false - name: Merge release into main run: | set -euo pipefail - latest_tag=$(git tag --list 'v[0-9]*' --sort=-v:refname | head -1 || true) + latest_tag=$(git describe --tags --match 'v[0-9]*' --abbrev=0 || true) if [ -n "$latest_tag" ]; then range="${latest_tag}..HEAD" - tag_time=$(git log -1 --format=%ct "$latest_tag") + tag_time=$(git for-each-ref --format='%(creatordate:unix)' "refs/tags/$latest_tag") now=$(date +%s) min_age=$((7 * 24 * 60 * 60)) age=$((now - tag_time)) From 478f0ab26a21b43becbfeacfead1d6f8dd572c76 Mon Sep 17 00:00:00 2001 From: Jeff Dickey Date: Tue, 7 Jul 2026 15:23:44 -0500 Subject: [PATCH 5/6] ci: harden release auto-merge guard --- .github/workflows/auto-merge-release.yml | 39 +++++++++++++++--------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/.github/workflows/auto-merge-release.yml b/.github/workflows/auto-merge-release.yml index 233a3cde..98aa95f2 100644 --- a/.github/workflows/auto-merge-release.yml +++ b/.github/workflows/auto-merge-release.yml @@ -21,28 +21,37 @@ jobs: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 0 + fetch-tags: true persist-credentials: false - name: Merge release into main run: | set -euo pipefail - latest_tag=$(git describe --tags --match 'v[0-9]*' --abbrev=0 || true) - if [ -n "$latest_tag" ]; then - range="${latest_tag}..HEAD" - tag_time=$(git for-each-ref --format='%(creatordate:unix)' "refs/tags/$latest_tag") - now=$(date +%s) - min_age=$((7 * 24 * 60 * 60)) - age=$((now - tag_time)) - if [ "$age" -lt "$min_age" ]; then - echo "Latest release ${latest_tag} is less than 7 days old; not auto-merging release PRs" - exit 0 + if [ "${GITHUB_EVENT_NAME:-}" != "workflow_dispatch" ]; then + latest_tag=$(git describe --tags --match 'v[0-9]*' --abbrev=0 || true) + if [ -n "$latest_tag" ]; then + range="${latest_tag}..HEAD" + tag_time=$(git for-each-ref --format='%(creatordate:unix)' "refs/tags/$latest_tag") + now=$(date +%s) + min_age=$((7 * 24 * 60 * 60)) + age=$((now - tag_time)) + if [ "$age" -lt "$min_age" ]; then + echo "Latest release ${latest_tag} is less than 7 days old; not auto-merging release PRs" + exit 0 + fi + else + echo "No v[0-9]* release tag found; allowing bootstrap release" + range="" + fi + if [ -n "$range" ]; then + commit_subjects=$(git log --format=%s "$range") + if ! grep -Eq '^(fix|feat)(\([^)]+\))?!?: ' <<<"$commit_subjects"; then + echo "No pending fix/feat commits since the latest release; not auto-merging release PRs" + exit 0 + fi fi else - range="HEAD" - fi - if ! git log --format=%s "$range" | grep -Eq '^(fix|feat)(\([^)]+\))?!?: '; then - echo "No pending fix/feat commits since the latest release; not auto-merging release PRs" - exit 0 + echo "Manual dispatch; skipping release cadence guards" fi PR_NUMBER=$(gh pr list -R jdx/usage --base main --head release --state open --limit 100 --json number,headRefName,headRepositoryOwner \ From 6b33db1be6658cb78854170b8010d84efe014ab2 Mon Sep 17 00:00:00 2001 From: Jeff Dickey Date: Tue, 7 Jul 2026 15:26:41 -0500 Subject: [PATCH 6/6] ci: retry setup-dependent checks