fix(tui): stop saying "openboot" twice on the boot screen (#153) #73
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Auto Release | |
| # Sensor: when unreleased commits on main trip a threshold, either auto-tag | |
| # (patch fast lane) or open a release-ready issue (feat threshold). | |
| # See docs/HARNESS.md. | |
| # | |
| # Triggers (any one fires): | |
| # - >=5 feat/fix commits since last stable tag | |
| # - >=7 days since last stable tag AND new commits exist | |
| # - any fix: present (patch fast lane) | |
| # | |
| # Version bump: | |
| # - any feat: present -> minor (X.Y+1.0) — opens issue, does NOT tag | |
| # - else -> patch (X.Y.Z+1) — auto-tags + dispatches release.yml | |
| # | |
| # Why the split: feat: changes carry more risk than fix: patches and benefit | |
| # from a human running the local Tart VM e2e suite before tag. The issue is | |
| # a nudge, not a hard gate — a human can tag without running it. | |
| # | |
| # Why we dispatch release.yml on patch instead of relying on the tag-push | |
| # trigger: GitHub deliberately suppresses workflow events fired by | |
| # GITHUB_TOKEN, so a tag pushed from here would NOT start release.yml. | |
| # We push the tag and then explicitly | |
| # `gh workflow run release.yml -f version=<tag>`. | |
| on: | |
| push: | |
| branches: [main] | |
| workflow_dispatch: | |
| inputs: | |
| dry_run: | |
| description: 'Dry-run: compute decision and exit without tagging.' | |
| type: boolean | |
| default: true | |
| concurrency: | |
| group: auto-release | |
| cancel-in-progress: false | |
| permissions: | |
| contents: write | |
| actions: write | |
| issues: write | |
| jobs: | |
| evaluate: | |
| name: auto-release sensor | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Fetch tags | |
| run: git fetch --tags --force | |
| - name: Decide | |
| id: decide | |
| env: | |
| DRY_RUN: ${{ inputs.dry_run }} | |
| run: | | |
| set -euo pipefail | |
| last_tag=$(git tag --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname \ | |
| | grep -Ev -- '-(rc|beta|alpha)' \ | |
| | head -n1 || true) | |
| if [ -z "$last_tag" ]; then | |
| echo "::warning::no stable tag found — skipping auto-release." | |
| echo "release=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| echo "last_tag=$last_tag" | |
| commits=$(git log --format='%s' "${last_tag}..HEAD") | |
| if [ -z "$commits" ]; then | |
| echo "no commits since $last_tag — skip" | |
| echo "release=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| feat_count=$(printf '%s\n' "$commits" | grep -cE '^feat(\(.+\))?!?:' || true) | |
| fix_count=$(printf '%s\n' "$commits" | grep -cE '^fix(\(.+\))?!?:' || true) | |
| combined=$((feat_count + fix_count)) | |
| last_ts=$(git log -1 --format=%ct "$last_tag") | |
| now=$(date -u +%s) | |
| days=$(( (now - last_ts) / 86400 )) | |
| echo "feat=$feat_count fix=$fix_count combined=$combined days_since=$days" | |
| reason="" | |
| if [ "$combined" -ge 5 ]; then | |
| reason="${combined} feat/fix commits since ${last_tag}" | |
| elif [ "$days" -ge 7 ]; then | |
| reason="${days} days since ${last_tag} with new commits" | |
| elif [ "$fix_count" -ge 1 ]; then | |
| reason="fix present (patch fast lane)" | |
| else | |
| echo "no threshold tripped — skip" | |
| echo "release=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| IFS='.' read -r major minor patch <<<"${last_tag#v}" | |
| if [ "$feat_count" -ge 1 ]; then | |
| minor=$((minor + 1)) | |
| patch=0 | |
| bump="minor" | |
| else | |
| patch=$((patch + 1)) | |
| bump="patch" | |
| fi | |
| new_tag="v${major}.${minor}.${patch}" | |
| echo "decision: release ${new_tag} (${bump}) — ${reason}" | |
| { | |
| echo "release=true" | |
| echo "new_tag=${new_tag}" | |
| echo "bump=${bump}" | |
| echo "reason=${reason}" | |
| } >> "$GITHUB_OUTPUT" | |
| if [ "${DRY_RUN:-false}" = "true" ]; then | |
| echo "::notice::DRY_RUN — would tag ${new_tag} (${reason})" | |
| echo "dry_run=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Tag and dispatch release (patch fast lane) | |
| if: | | |
| steps.decide.outputs.release == 'true' | |
| && steps.decide.outputs.dry_run != 'true' | |
| && steps.decide.outputs.bump == 'patch' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| NEW_TAG: ${{ steps.decide.outputs.new_tag }} | |
| REASON: ${{ steps.decide.outputs.reason }} | |
| run: | | |
| set -euo pipefail | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git tag -a "$NEW_TAG" -m "Auto-release: $REASON" | |
| git push origin "$NEW_TAG" | |
| gh workflow run release.yml -f version="$NEW_TAG" | |
| echo "::notice::tagged $NEW_TAG and dispatched release.yml — $REASON" | |
| - name: Open release-ready issue (feat threshold) | |
| if: | | |
| steps.decide.outputs.release == 'true' | |
| && steps.decide.outputs.dry_run != 'true' | |
| && steps.decide.outputs.bump == 'minor' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| NEW_TAG: ${{ steps.decide.outputs.new_tag }} | |
| REASON: ${{ steps.decide.outputs.reason }} | |
| run: | | |
| set -euo pipefail | |
| # If an open release-ready issue already exists for this tag, skip | |
| # (we don't want to spam the queue every push to main). | |
| existing=$(gh issue list \ | |
| --label release-ready \ | |
| --state open \ | |
| --search "in:title ${NEW_TAG}" \ | |
| --json number --jq '.[0].number' || true) | |
| if [ -n "$existing" ]; then | |
| echo "::notice::release-ready issue #${existing} already open for ${NEW_TAG} — skipping" | |
| exit 0 | |
| fi | |
| body=$(cat <<EOF | |
| Auto-release sensor wants to cut **${NEW_TAG}** (minor bump). | |
| **Why:** ${REASON} | |
| Because this release includes \`feat:\` changes, the sensor did | |
| **not** auto-tag. Run the destructive e2e suite locally, then | |
| cut the release manually: | |
| - [ ] L4 CI (\`vm-e2e-spike.yml\`) is green on the latest commit on \`main\` | |
| - [ ] sanity-check the curl|bash smoke and cli-compat results in the most recent test.yml run on main | |
| - [ ] \`git tag -a ${NEW_TAG} -m "..."\` and \`git push origin ${NEW_TAG}\` | |
| - [ ] close this issue | |
| L4 CI is not yet a hard merge gate, but \`feat:\` changes carry | |
| more risk than \`fix:\` patches — verify it before tagging. | |
| EOF | |
| ) | |
| gh issue create \ | |
| --title "Release ready: ${NEW_TAG}" \ | |
| --label release-ready \ | |
| --body "$body" | |
| echo "::notice::opened release-ready issue for ${NEW_TAG} — ${REASON}" |