(build): front back #169
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: 'Discord: repo activity' | |
| on: | |
| push: | |
| branches: ['**'] | |
| jobs: | |
| notify: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| # ---------------------------------------------------------------------- | |
| # Build the per-commit lines. | |
| # | |
| # Wire format per line (one commit): | |
| # N. **`type(scope):`** subject · [`abc1234`](url) · author | |
| # | |
| # Notes for future maintainers (and for whoever reads this from the | |
| # Discord side): | |
| # - We render the SUBJECT only. Bodies are intentionally omitted — | |
| # they fragment readability in chat and the hash link gives the | |
| # full git show one click away. | |
| # - The previous version stripped (), [], and ` from the message to | |
| # "protect" the [hash](url) link that follows. That was a false | |
| # positive: Discord's link parser only triggers on `]` IMMEDIATELY | |
| # followed by `(`, and our hash link sits behind a ` · ` separator | |
| # so subject-side punctuation is safe. Stripping parens turned | |
| # `fix(editor):` into `fixeditor:` and is the bug we are fixing. | |
| # - Conventional Commit prefix (type, optional scope, optional !) | |
| # is detected with a single capture and rendered as bold inline | |
| # code so it pops without breaking non-conventional commits. | |
| # - Control characters (\u0000-\u001f) are still stripped because | |
| # they break Discord rendering and have no legitimate place in a | |
| # commit subject. | |
| # - GitHub push webhooks include at most 20 commits in | |
| # github.event.commits. For larger pushes we load the full range | |
| # via the compare API (repos/.../compare/before...after). | |
| # ---------------------------------------------------------------------- | |
| - name: Build commit summary (for push) | |
| id: build | |
| env: | |
| EVENT_JSON: ${{ toJSON(github.event) }} | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| REF=$(jq -r '.ref' <<< "$EVENT_JSON" | sed 's|refs/heads/||') | |
| COMPARE=$(jq -r '.compare' <<< "$EVENT_JSON") | |
| BEFORE="${{ github.event.before }}" | |
| AFTER="${{ github.event.after }}" | |
| ZERO_SHA="0000000000000000000000000000000000000000" | |
| REPO="${{ github.repository }}" | |
| normalize_webhook() { | |
| jq '{ | |
| commits: [ | |
| .commits[]? | { | |
| id: .id, | |
| message: .message, | |
| author: {name: (.author.name // "unknown")} | |
| } | |
| ] | |
| }' <<< "$EVENT_JSON" | |
| } | |
| if [ "$BEFORE" = "$ZERO_SHA" ] || [ -z "$BEFORE" ]; then | |
| COMMITS_JSON=$(normalize_webhook) | |
| else | |
| API_PAGES=$(gh api "repos/${REPO}/compare/${BEFORE}...${AFTER}" --paginate 2>/dev/null) || API_PAGES="" | |
| if [ -n "$API_PAGES" ]; then | |
| COMMITS_JSON=$(jq -s ' | |
| [.[].commits[]?] | |
| | unique_by(.sha) | |
| | {commits: map({ | |
| id: .sha, | |
| message: .commit.message, | |
| author: {name: (.commit.author.name // "unknown")} | |
| })} | |
| ' <<< "$API_PAGES") | |
| if [ "$(jq -r '.commits | length' <<< "$COMMITS_JSON")" -eq 0 ]; then | |
| COMMITS_JSON=$(normalize_webhook) | |
| fi | |
| else | |
| echo "::warning::compare API failed; falling back to webhook commits (max 20)" | |
| COMMITS_JSON=$(normalize_webhook) | |
| fi | |
| fi | |
| COUNT=$(jq -r '.commits | length' <<< "$COMMITS_JSON") | |
| # Skip the synthetic "build front back" deploy-trigger commit so | |
| # the channel does not get a one-line notification with no signal. | |
| SKIP_NOTIFICATION="false" | |
| if [ "$COUNT" -eq 1 ]; then | |
| FIRST_MSG=$(jq -r '.commits[0].message // "" | split("\n")[0]' <<< "$COMMITS_JSON") | |
| if [ "$FIRST_MSG" = "build front back" ] || [ "$FIRST_MSG" = "(build): front back" ]; then | |
| SKIP_NOTIFICATION="true" | |
| echo "⏭️ Skipping Discord notification (build trigger commit)" | |
| fi | |
| fi | |
| REPO_URL="https://github.com/${REPO}" | |
| jq -r --arg repo "$REPO_URL" ' | |
| .commits | to_entries[] | | |
| ((1 + .key) | tostring) as $n | | |
| (.value.id[0:7]) as $sha | | |
| ($repo + "/commit/" + .value.id) as $url | | |
| (.value.message | |
| | split("\n")[0] | |
| | gsub("[\u0000-\u001f]"; " ") | |
| | gsub("\\s+"; " ") | |
| | gsub("^\\s+|\\s+$"; "") | |
| ) as $subject | | |
| ([$subject | |
| | capture("^(?<prefix>[a-zA-Z]+(?:\\([^)]+\\))?!?:)\\s*(?<rest>.*)$") | |
| ] | .[0]) as $cc | | |
| (if $cc != null then | |
| "**`" + $cc.prefix + "`** " + ($cc.rest | .[0:180]) | |
| else | |
| ($subject | .[0:200]) | |
| end) as $title | | |
| $n + ". " + $title + " · [`" + $sha + "`](" + $url + ") · " + .value.author.name | |
| ' <<< "$COMMITS_JSON" > /tmp/commits.txt | |
| { | |
| echo "COUNT=$COUNT" | |
| echo "REF=$REF" | |
| echo "COMPARE=$COMPARE" | |
| echo "SKIP_NOTIFICATION=$SKIP_NOTIFICATION" | |
| } >> "$GITHUB_ENV" | |
| # ---------------------------------------------------------------------- | |
| # Post to Discord. | |
| # | |
| # Splitting: | |
| # Discord caps content at 2000 chars. We pack as many commit lines as | |
| # fit under MAX_LEN (1900, leaving ~100 char buffer for trailing JSON | |
| # and a bit of slack), then flush. The HEADER is only included in the | |
| # first chunk so subsequent chunks read as continuations. | |
| # | |
| # flags: 4 == SUPPRESS_EMBEDS — keeps the message compact (no GitHub | |
| # link previews stacking under every commit). | |
| # ---------------------------------------------------------------------- | |
| - name: Post to Discord (smart split) | |
| if: env.SKIP_NOTIFICATION != 'true' | |
| run: | | |
| HEADER="🚀 **Push** to \`${{ env.REF }}\` by \`${{ github.actor }}\` | |
| 📦 \`${{ github.repository }}\` · 📝 ${{ env.COUNT }} commits · 🔗 [View Diff](${{ env.COMPARE }}) | |
| **Commits** | |
| " | |
| MAX_LEN=1900 | |
| CURRENT_CHUNK="" | |
| CHUNK_NUM=1 | |
| send_chunk() { | |
| local message="$1" | |
| curl -sS -H "Content-Type: application/json" \ | |
| -d "{\"content\": $(printf '%s' "$message" | jq -Rs .), \"flags\": 4}" \ | |
| "${{ secrets.DISCORD_WEBHOOK }}" | |
| sleep 1 | |
| } | |
| while IFS= read -r line || [ -n "$line" ]; do | |
| [ -z "$line" ] && continue | |
| TEST_MSG="${HEADER}${CURRENT_CHUNK}${line}"$'\n' | |
| if [ ${#TEST_MSG} -gt $MAX_LEN ] && [ -n "$CURRENT_CHUNK" ]; then | |
| if [ $CHUNK_NUM -eq 1 ]; then | |
| send_chunk "${HEADER}${CURRENT_CHUNK}" | |
| else | |
| send_chunk $'*(continued)*\n'"${CURRENT_CHUNK}" | |
| fi | |
| CHUNK_NUM=$((CHUNK_NUM + 1)) | |
| CURRENT_CHUNK="${line}"$'\n' | |
| else | |
| CURRENT_CHUNK="${CURRENT_CHUNK}${line}"$'\n' | |
| fi | |
| done < /tmp/commits.txt | |
| if [ -n "$CURRENT_CHUNK" ]; then | |
| if [ $CHUNK_NUM -eq 1 ]; then | |
| send_chunk "${HEADER}${CURRENT_CHUNK}" | |
| else | |
| send_chunk $'*(continued)*\n'"${CURRENT_CHUNK}" | |
| fi | |
| fi |