diff --git a/.github/workflows/notify-telegram.yml b/.github/workflows/notify-telegram.yml index d9e5bfb..70032e6 100644 --- a/.github/workflows/notify-telegram.yml +++ b/.github/workflows/notify-telegram.yml @@ -1,17 +1,34 @@ name: Notify Telegram +# tauri-action creates the release with the default GITHUB_TOKEN, and GitHub +# Actions deliberately suppresses `release: published` events for releases +# made by GITHUB_TOKEN (to prevent recursive workflow chains). So we can't +# listen for `release: published` — it will never fire on stable releases. +# +# Instead, chain off the Release workflow via `workflow_run`, the same way +# `publish-repos.yml` does. `workflow_dispatch` is kept so a missed +# announcement can be replayed by hand. on: - release: - types: [published] + workflow_dispatch: + inputs: + tag: + description: "Release tag to announce (e.g. v0.23.0). Leave empty for latest stable." + required: false + type: string + workflow_run: + workflows: ["Release"] + types: + - completed permissions: contents: read jobs: notify: - # Only post for stable releases on the canonical repo. Pre-releases - # (rolling builds, RCs) are skipped so the channel stays low-noise. - if: github.repository == 'zhom/donutbrowser' && !github.event.release.prerelease + if: > + github.repository == 'zhom/donutbrowser' && + (github.event_name == 'workflow_dispatch' || + github.event.workflow_run.conclusion == 'success') runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -19,11 +36,68 @@ jobs: ref: main fetch-depth: 0 + - name: Resolve release tag + id: tag + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + INPUT_TAG: ${{ inputs.tag }} + run: | + if [[ -n "${INPUT_TAG:-}" ]]; then + TAG="${INPUT_TAG}" + elif [[ "${{ github.event_name }}" == "workflow_run" ]]; then + # The Release workflow runs on `push: tags: v*` so head_branch + # of the triggering run is the tag name. + TAG="${{ github.event.workflow_run.head_branch }}" + else + TAG=$(gh release view --repo "${{ github.repository }}" --json tagName -q .tagName) + fi + echo "tag=${TAG}" >> "$GITHUB_OUTPUT" + echo "Resolved tag: ${TAG}" + + - name: Skip pre-releases / missing releases + id: gate + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ steps.tag.outputs.tag }} + run: | + # Tag like `nightly-…` or `nightly` is never an announceable + # stable release. Short-circuit before hitting the API. + if [[ "${TAG}" == nightly* ]]; then + echo "Tag '${TAG}' is a rolling/nightly build, skipping Telegram post." + echo "skip=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + + # Only stable semver tags vX.Y.Z are eligible. Reject anything + # with a pre-release suffix (`-rc1`, `-beta`, etc.). + if [[ ! "${TAG}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Tag '${TAG}' is not a stable semver tag, skipping." + echo "skip=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + + # Confirm the release exists and isn't marked prerelease in the + # GitHub UI — guards against someone manually flipping the flag. + RELEASE_JSON=$(gh release view "${TAG}" --repo "${{ github.repository }}" --json isPrerelease,tagName 2>/dev/null || echo "") + if [[ -z "${RELEASE_JSON}" ]]; then + echo "Release ${TAG} not found via gh — skipping." + echo "skip=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + IS_PRE=$(jq -r .isPrerelease <<< "${RELEASE_JSON}") + if [[ "${IS_PRE}" == "true" ]]; then + echo "Release ${TAG} is marked prerelease, skipping." + echo "skip=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + echo "skip=false" >> "$GITHUB_OUTPUT" + - name: Post release announcement to Telegram + if: steps.gate.outputs.skip != 'true' env: TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }} TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }} - TAG: ${{ github.event.release.tag_name }} + TAG: ${{ steps.tag.outputs.tag }} REPO: ${{ github.repository }} run: | if [ -z "$TELEGRAM_BOT_TOKEN" ] || [ -z "$TELEGRAM_CHAT_ID" ]; then @@ -31,8 +105,8 @@ jobs: exit 0 fi - # Resolve the previous stable tag the same way notify-discord does - # so the changelog ranges line up. + # Find the previous stable tag (skip the current one) so the + # changelog range is well-defined. PREV_TAG=$(git tag --sort=-version:refname \ | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' \ | grep -v "^${TAG}$" \ @@ -45,7 +119,7 @@ jobs: # Build a plain bullet list from feat / fix / refactor commits. # Other commit types (chore, docs, ci, test, deps) are intentionally - # filtered out — same convention as the Discord embed. + # filtered out to keep the channel focused on user-visible changes. CHANGES="" while IFS= read -r msg; do [ -z "$msg" ] && continue @@ -62,7 +136,6 @@ jobs: # HTML-escape the changelog before injecting into Telegram HTML # mode — commit messages can legitimately contain `<`, `>`, `&`. - # The static markup around it (we control it) is left as-is. ESCAPED_CHANGES=$(printf '%s' "$CHANGES" \ | python3 -c "import html, sys; sys.stdout.write(html.escape(sys.stdin.read()))")