fix(hooks): propagate lint-staged exit code from husky pre-commit #143
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 | |
| 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. | |
| # ---------------------------------------------------------------------- | |
| - name: Build commit summary (for push) | |
| id: build | |
| env: | |
| EVENT_JSON: ${{ toJSON(github.event) }} | |
| run: | | |
| COUNT=$(jq -r '.commits | length' <<< "$EVENT_JSON") | |
| REF=$(jq -r '.ref' <<< "$EVENT_JSON" | sed 's|refs/heads/||') | |
| COMPARE=$(jq -r '.compare' <<< "$EVENT_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]' <<< "$EVENT_JSON") | |
| if [ "$FIRST_MSG" = "build front back" ]; then | |
| SKIP_NOTIFICATION="true" | |
| echo "⏭️ Skipping Discord notification (build trigger commit)" | |
| fi | |
| fi | |
| REPO_URL="https://github.com/${{ github.repository }}" | |
| 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 | | |
| # capture() returns an empty stream (not null) on no match, | |
| # which would silently drop the entire commit line. Wrap it in | |
| # an array so non-conventional commits resolve to null and fall | |
| # through to the plain-subject branch below. | |
| ([$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 | |
| ' <<< "$EVENT_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 "${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 "${CURRENT_CHUNK}" | |
| fi | |
| fi |