Skip to content

Commit f80577f

Browse files
authored
feat(ci): add LinkedIn and refactor to reusable workflows (#187)
1 parent 9d56eac commit f80577f

1 file changed

Lines changed: 78 additions & 201 deletions

File tree

.github/workflows/release.yml

Lines changed: 78 additions & 201 deletions
Original file line numberDiff line numberDiff line change
@@ -317,205 +317,82 @@ jobs:
317317
env:
318318
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
319319

320-
notify:
321-
name: Send Release Notifications
322-
runs-on: ubuntu-latest
320+
notify-discussion:
321+
name: Create GitHub Discussion
323322
needs: release
324-
325-
steps:
326-
- name: Checkout code
327-
uses: actions/checkout@v4
328-
329-
- name: Create GitHub Discussion Announcement
330-
id: discussion
331-
run: |
332-
VERSION="${{ github.event.inputs.version }}"
333-
RELEASE_URL="https://github.com/${{ github.repository }}/releases/tag/v$VERSION"
334-
REPO_URL="https://github.com/${{ github.repository }}"
335-
336-
# Get repository ID and Announcements category ID
337-
REPO_DATA=$(gh api graphql -f query='
338-
query($owner: String!, $repo: String!) {
339-
repository(owner: $owner, name: $repo) {
340-
id
341-
discussionCategories(first: 10) {
342-
nodes {
343-
id
344-
name
345-
}
346-
}
347-
}
348-
}
349-
' -f owner="${{ github.repository_owner }}" -f repo="${{ github.event.repository.name }}")
350-
351-
REPO_ID=$(echo "$REPO_DATA" | jq -r '.data.repository.id')
352-
CATEGORY_ID=$(echo "$REPO_DATA" | jq -r '.data.repository.discussionCategories.nodes[] | select(.name == "Announcements") | .id')
353-
354-
if [ -z "$CATEGORY_ID" ]; then
355-
echo "Error: Could not find Announcements category"
356-
exit 1
357-
fi
358-
359-
echo "Repository ID: $REPO_ID"
360-
echo "Announcements Category ID: $CATEGORY_ID"
361-
362-
# Create discussion body (build it with string concatenation to avoid heredoc issues)
363-
DISCUSSION_BODY="## Changes in this release"
364-
DISCUSSION_BODY="$DISCUSSION_BODY"$'\n\n'"See the [full changelog](RELEASE_URL_PLACEHOLDER) for details on what's new in this release."
365-
DISCUSSION_BODY="$DISCUSSION_BODY"$'\n\n'"## Installation"
366-
DISCUSSION_BODY="$DISCUSSION_BODY"$'\n\n'"### Quick Install (Recommended)"
367-
DISCUSSION_BODY="$DISCUSSION_BODY"$'\n\n'"**macOS / Linux:**"
368-
DISCUSSION_BODY="$DISCUSSION_BODY"$'\n''```bash'
369-
DISCUSSION_BODY="$DISCUSSION_BODY"$'\n'"curl -fsSL REPO_URL_PLACEHOLDER/releases/download/vVERSION_PLACEHOLDER/install.sh | bash"
370-
DISCUSSION_BODY="$DISCUSSION_BODY"$'\n''```'
371-
DISCUSSION_BODY="$DISCUSSION_BODY"$'\n\n'"**Windows (PowerShell):**"
372-
DISCUSSION_BODY="$DISCUSSION_BODY"$'\n''```powershell'
373-
DISCUSSION_BODY="$DISCUSSION_BODY"$'\n'"irm REPO_URL_PLACEHOLDER/releases/download/vVERSION_PLACEHOLDER/install.ps1 | iex"
374-
DISCUSSION_BODY="$DISCUSSION_BODY"$'\n''```'
375-
DISCUSSION_BODY="$DISCUSSION_BODY"$'\n\n'"### Manual Installation"
376-
DISCUSSION_BODY="$DISCUSSION_BODY"$'\n\n'"1. Download the appropriate archive for your platform from the [release page](RELEASE_URL_PLACEHOLDER)"
377-
DISCUSSION_BODY="$DISCUSSION_BODY"$'\n'"2. Extract the archive"
378-
DISCUSSION_BODY="$DISCUSSION_BODY"$'\n'"3. Move binaries to a directory in your PATH"
379-
DISCUSSION_BODY="$DISCUSSION_BODY"$'\n'"4. Run \`dtvem init\` to complete setup"
380-
DISCUSSION_BODY="$DISCUSSION_BODY"$'\n\n'"## Supported Platforms"
381-
DISCUSSION_BODY="$DISCUSSION_BODY"$'\n\n'"- ✅ Windows (amd64, arm64)"
382-
DISCUSSION_BODY="$DISCUSSION_BODY"$'\n'"- ✅ macOS (amd64, arm64/Apple Silicon)"
383-
DISCUSSION_BODY="$DISCUSSION_BODY"$'\n'"- ✅ Linux (amd64)"
384-
DISCUSSION_BODY="$DISCUSSION_BODY"$'\n\n'"---"
385-
DISCUSSION_BODY="$DISCUSSION_BODY"$'\n\n'"📦 [View Release](RELEASE_URL_PLACEHOLDER) | 📖 [Documentation](REPO_URL_PLACEHOLDER)"
386-
# Replace placeholders with actual values
387-
DISCUSSION_BODY="${DISCUSSION_BODY//RELEASE_URL_PLACEHOLDER/$RELEASE_URL}"
388-
DISCUSSION_BODY="${DISCUSSION_BODY//REPO_URL_PLACEHOLDER/$REPO_URL}"
389-
DISCUSSION_BODY="${DISCUSSION_BODY//VERSION_PLACEHOLDER/${{ github.event.inputs.version }}}"
390-
391-
# Create the discussion
392-
DISCUSSION_RESULT=$(gh api graphql -f query='
393-
mutation($repositoryId: ID!, $categoryId: ID!, $title: String!, $body: String!) {
394-
createDiscussion(input: {
395-
repositoryId: $repositoryId
396-
categoryId: $categoryId
397-
title: $title
398-
body: $body
399-
}) {
400-
discussion {
401-
url
402-
}
403-
}
404-
}
405-
' -f repositoryId="$REPO_ID" -f categoryId="$CATEGORY_ID" -f title="🎉 dtvem v$VERSION has been released!" -f body="$DISCUSSION_BODY")
406-
407-
DISCUSSION_URL=$(echo "$DISCUSSION_RESULT" | jq -r '.data.createDiscussion.discussion.url')
408-
echo "✓ Created discussion: $DISCUSSION_URL"
409-
echo "discussion_url=$DISCUSSION_URL" >> $GITHUB_OUTPUT
410-
shell: bash
411-
env:
412-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
413-
414-
- name: Post to BlueSky
415-
run: |
416-
VERSION="${{ github.event.inputs.version }}"
417-
RELEASE_URL="https://github.com/${{ github.repository }}/releases/tag/v$VERSION"
418-
DISCUSSION_URL="${{ steps.discussion.outputs.discussion_url }}"
419-
420-
# Discover runtimes from src/runtimes/ directory
421-
RUNTIME_DIRS=$(find src/runtimes -mindepth 1 -maxdepth 1 -type d -exec basename {} \; | sort)
422-
423-
# Capitalize runtime names
424-
RUNTIME_LIST=()
425-
for runtime in $RUNTIME_DIRS; do
426-
RUNTIME_LIST+=("${runtime^}") # Capitalize first letter
427-
done
428-
429-
# Format runtime names based on count (with hashtags)
430-
RUNTIME_COUNT=${#RUNTIME_LIST[@]}
431-
if [ $RUNTIME_COUNT -eq 1 ]; then
432-
RUNTIME_NAMES="#${RUNTIME_LIST[0]}"
433-
elif [ $RUNTIME_COUNT -eq 2 ]; then
434-
RUNTIME_NAMES="#${RUNTIME_LIST[0]} and #${RUNTIME_LIST[1]}"
435-
else
436-
# Three or more: "#A, #B, and #C"
437-
RUNTIME_NAMES=""
438-
for i in "${!RUNTIME_LIST[@]}"; do
439-
if [ $i -eq 0 ]; then
440-
RUNTIME_NAMES="#${RUNTIME_LIST[$i]}"
441-
elif [ $i -eq $((RUNTIME_COUNT - 1)) ]; then
442-
RUNTIME_NAMES="${RUNTIME_NAMES}, and #${RUNTIME_LIST[$i]}"
443-
else
444-
RUNTIME_NAMES="${RUNTIME_NAMES}, #${RUNTIME_LIST[$i]}"
445-
fi
446-
done
447-
fi
448-
449-
echo "Detected runtimes: $RUNTIME_NAMES"
450-
451-
# Authenticate with BlueSky
452-
echo "Authenticating with BlueSky..."
453-
AUTH_RESPONSE=$(curl -s -X POST https://bsky.social/xrpc/com.atproto.server.createSession \
454-
-H "Content-Type: application/json" \
455-
-d "{\"identifier\": \"${{ secrets.BLUESKY_USERNAME }}\", \"password\": \"${{ secrets.BLUESKY_APP_PASSWORD }}\"}")
456-
457-
ACCESS_TOKEN=$(echo "$AUTH_RESPONSE" | jq -r '.accessJwt')
458-
DID=$(echo "$AUTH_RESPONSE" | jq -r '.did')
459-
460-
if [ -z "$ACCESS_TOKEN" ] || [ "$ACCESS_TOKEN" == "null" ]; then
461-
echo "Error: Failed to authenticate with BlueSky"
462-
echo "Response: $AUTH_RESPONSE"
463-
exit 1
464-
fi
465-
466-
echo "✓ Authenticated as $DID"
467-
468-
# Create post text (must be under 300 graphemes)
469-
POST_TEXT="🚀 #dtvem v${VERSION} is now available!"
470-
POST_TEXT="${POST_TEXT}"$'\n\n'"Cross-platform version manager for ${RUNTIME_NAMES} - supports #Windows, #Linux, and #MacOS"
471-
POST_TEXT="${POST_TEXT}"$'\n\n'"Release: ${RELEASE_URL}"
472-
POST_TEXT="${POST_TEXT}"$'\n'"Discuss: ${DISCUSSION_URL}"
473-
474-
echo "Post text: $POST_TEXT"
475-
echo "Post length: $(echo -n "$POST_TEXT" | wc -c) characters"
476-
477-
# Get current timestamp in ISO 8601 format
478-
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
479-
480-
# Calculate facets (byte positions for hashtags and links)
481-
echo "Calculating facets for links and hashtags..."
482-
export POST_TEXT
483-
FACETS=$(python3 -c "import json; import re; import os; text = os.environ['POST_TEXT']; facets = []; [facets.append({'index': {'byteStart': len(text[:m.start()].encode('utf-8')), 'byteEnd': len(text[:m.start()+len(m.group(0))].encode('utf-8'))}, 'features': [{'\$type': 'app.bsky.richtext.facet#tag', 'tag': m.group(1)}]}) for m in re.finditer(r'#(\w+)', text)]; [facets.append({'index': {'byteStart': len(text[:m.start()].encode('utf-8')), 'byteEnd': len(text[:m.start()+len(m.group())].encode('utf-8'))}, 'features': [{'\$type': 'app.bsky.richtext.facet#link', 'uri': m.group()}]}) for m in re.finditer(r'https?://[^\s]+', text)]; print(json.dumps(facets))")
484-
485-
echo "Facets: $FACETS"
486-
487-
# Create the post using jq to properly escape JSON
488-
echo "Creating BlueSky post..."
489-
POST_RESPONSE=$(jq -n \
490-
--arg did "$DID" \
491-
--arg text "$POST_TEXT" \
492-
--arg timestamp "$TIMESTAMP" \
493-
--argjson facets "$FACETS" \
494-
'{
495-
repo: $did,
496-
collection: "app.bsky.feed.post",
497-
record: {
498-
text: $text,
499-
facets: $facets,
500-
createdAt: $timestamp,
501-
"$type": "app.bsky.feed.post"
502-
}
503-
}' | curl -s -X POST https://bsky.social/xrpc/com.atproto.repo.createRecord \
504-
-H "Content-Type: application/json" \
505-
-H "Authorization: Bearer $ACCESS_TOKEN" \
506-
-d @-)
507-
508-
POST_URI=$(echo "$POST_RESPONSE" | jq -r '.uri')
509-
510-
if [ -z "$POST_URI" ] || [ "$POST_URI" == "null" ]; then
511-
echo "Error: Failed to create BlueSky post"
512-
echo "Response: $POST_RESPONSE"
513-
exit 1
514-
fi
515-
516-
# Extract the post ID from the URI (format: at://did:plc:.../app.bsky.feed.post/POST_ID)
517-
POST_ID=$(echo "$POST_URI" | sed 's|.*/||')
518-
POST_URL="https://bsky.app/profile/${{ secrets.BLUESKY_USERNAME }}/post/$POST_ID"
519-
520-
echo "✓ Posted to BlueSky: $POST_URL"
521-
shell: bash
323+
uses: CodingWithCalvin/.github/.github/workflows/github-discussion.yml@main
324+
with:
325+
title: "🎉 dtvem v${{ github.event.inputs.version }} has been released!"
326+
body: |
327+
## Changes in this release
328+
329+
See the [full changelog](https://github.com/${{ github.repository }}/releases/tag/v${{ github.event.inputs.version }}) for details on what's new in this release.
330+
331+
## Installation
332+
333+
### Quick Install (Recommended)
334+
335+
**macOS / Linux:**
336+
```bash
337+
curl -fsSL https://github.com/${{ github.repository }}/releases/download/v${{ github.event.inputs.version }}/install.sh | bash
338+
```
339+
340+
**Windows (PowerShell):**
341+
```powershell
342+
irm https://github.com/${{ github.repository }}/releases/download/v${{ github.event.inputs.version }}/install.ps1 | iex
343+
```
344+
345+
### Manual Installation
346+
347+
1. Download the appropriate archive for your platform from the [release page](https://github.com/${{ github.repository }}/releases/tag/v${{ github.event.inputs.version }})
348+
2. Extract the archive
349+
3. Move binaries to a directory in your PATH
350+
4. Run `dtvem init` to complete setup
351+
352+
## Supported Platforms
353+
354+
- ✅ Windows (amd64, arm64)
355+
- ✅ macOS (amd64, arm64/Apple Silicon)
356+
- ✅ Linux (amd64)
357+
358+
---
359+
360+
📦 [View Release](https://github.com/${{ github.repository }}/releases/tag/v${{ github.event.inputs.version }}) | 📖 [Documentation](https://github.com/${{ github.repository }})
361+
362+
notify-bluesky:
363+
name: Post to Bluesky
364+
needs: notify-discussion
365+
uses: CodingWithCalvin/.github/.github/workflows/bluesky-post.yml@main
366+
with:
367+
post_text: |
368+
🚀 #dtvem v${{ github.event.inputs.version }} is now available!
369+
370+
Cross-platform version manager for #Node, #Python, and #Ruby - supports #Windows, #Linux, and #macOS
371+
372+
[Release Notes](https://github.com/${{ github.repository }}/releases/tag/v${{ github.event.inputs.version }})
373+
[Discussion](${{ needs.notify-discussion.outputs.discussion_url }})
374+
embed_url: https://github.com/${{ github.repository }}/releases/tag/v${{ github.event.inputs.version }}
375+
embed_title: dtvem v${{ github.event.inputs.version }}
376+
embed_description: Cross-platform runtime version manager for Node.js, Python, and Ruby
377+
secrets:
378+
BLUESKY_USERNAME: ${{ secrets.BLUESKY_USERNAME }}
379+
BLUESKY_APP_PASSWORD: ${{ secrets.BLUESKY_APP_PASSWORD }}
380+
381+
notify-linkedin:
382+
name: Post to LinkedIn
383+
needs: notify-discussion
384+
uses: CodingWithCalvin/.github/.github/workflows/linkedin-post.yml@main
385+
with:
386+
post_text: |
387+
🚀 #dtvem v${{ github.event.inputs.version }} is now available!
388+
389+
Cross-platform version manager for #Node, #Python, and #Ruby - supports #Windows, #Linux, and #macOS
390+
391+
Release Notes: https://github.com/${{ github.repository }}/releases/tag/v${{ github.event.inputs.version }}
392+
Discussion: ${{ needs.notify-discussion.outputs.discussion_url }}
393+
article_url: https://github.com/${{ github.repository }}/releases/tag/v${{ github.event.inputs.version }}
394+
article_title: dtvem v${{ github.event.inputs.version }}
395+
article_description: Cross-platform runtime version manager for Node.js, Python, and Ruby
396+
secrets:
397+
LINKEDIN_ACCESS_TOKEN: ${{ secrets.LINKEDIN_ACCESS_TOKEN }}
398+
LINKEDIN_CLIENT_ID: ${{ secrets.LINKEDIN_CLIENT_ID }}

0 commit comments

Comments
 (0)