Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 196 additions & 0 deletions .github/workflows/changeset-bot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
name: Changeset Bot

on:
issue_comment:
types: [created]

jobs:
changeset:
name: Create Changeset
if: |
github.event.issue.pull_request &&
startsWith(github.event.comment.body, '/changeset')
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write

steps:
- name: React to comment
uses: actions/github-script@v7
with:
script: |
await github.rest.reactions.createForIssueComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: context.payload.comment.id,
content: 'eyes'
});

- name: Get PR branch
id: pr
uses: actions/github-script@v7
with:
script: |
const pr = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number
});
core.setOutput('branch', pr.data.head.ref);
core.setOutput('title', pr.data.title);

- name: Checkout PR branch
uses: actions/checkout@v4
with:
ref: ${{ steps.pr.outputs.branch }}
fetch-depth: 0

- name: Install pnpm
uses: pnpm/action-setup@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Parse command
id: parse
uses: actions/github-script@v7
with:
script: |
const comment = context.payload.comment.body.trim();
const lines = comment.split('\n');
const firstLine = lines[0].trim();

// Parse: /changeset [patch|minor|major] [package1,package2]
const match = firstLine.match(/^\/changeset\s+(patch|minor|major)(?:\s+(.+))?$/i);

if (!match) {
core.setFailed('Invalid format. Use: /changeset <patch|minor|major> [package1,package2]');
return;
}

const bump = match[1].toLowerCase();
const packagesArg = match[2];

// Get summary from rest of comment or PR title
let summary = lines.slice(1).join('\n').trim();
if (!summary) {
summary = '${{ steps.pr.outputs.title }}';
}

core.setOutput('bump', bump);
core.setOutput('packages', packagesArg || '');
core.setOutput('summary', summary);

- name: Get changed packages
id: packages
run: |
if [ -n "${{ steps.parse.outputs.packages }}" ]; then
echo "packages=${{ steps.parse.outputs.packages }}" >> $GITHUB_OUTPUT
else
# Auto-detect from changed files
CHANGED=$(git diff --name-only origin/main...HEAD | grep -E "^(ts-cache|storages)/" | cut -d'/' -f1-2 | sort -u | tr '\n' ',' | sed 's/,$//')
if [ -z "$CHANGED" ]; then
CHANGED="ts-cache"
fi
echo "packages=$CHANGED" >> $GITHUB_OUTPUT
fi

- name: Create changeset
run: |
BUMP="${{ steps.parse.outputs.bump }}"
PACKAGES="${{ steps.packages.outputs.packages }}"
SUMMARY="${{ steps.parse.outputs.summary }}"

# Generate random filename
FILENAME=".changeset/$(echo $RANDOM | md5sum | head -c 12).md"

# Map folder names to package names
get_package_name() {
case "$1" in
ts-cache) echo "@node-ts-cache/core" ;;
storages/lru) echo "@node-ts-cache/lru-storage" ;;
storages/redis) echo "@node-ts-cache/redis-storage" ;;
storages/redisio) echo "@node-ts-cache/ioredis-storage" ;;
storages/node-cache) echo "@node-ts-cache/node-cache-storage" ;;
storages/lru-redis) echo "@node-ts-cache/lru-redis-storage" ;;
*) echo "$1" ;;
esac
}

# Create changeset content
echo "---" > "$FILENAME"

IFS=',' read -ra PKGS <<< "$PACKAGES"
for pkg in "${PKGS[@]}"; do
pkg=$(echo "$pkg" | xargs) # trim whitespace
pkg_name=$(get_package_name "$pkg")
echo "\"$pkg_name\": $BUMP" >> "$FILENAME"
done

echo "---" >> "$FILENAME"
echo "" >> "$FILENAME"
echo "$SUMMARY" >> "$FILENAME"

echo "Created changeset:"
cat "$FILENAME"

- name: Commit and push
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git add .changeset/
git commit -m "chore: add changeset"
git push

- name: React success
uses: actions/github-script@v7
with:
script: |
await github.rest.reactions.createForIssueComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: context.payload.comment.id,
content: 'rocket'
});

- name: Comment success
uses: actions/github-script@v7
with:
script: |
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `✅ Changeset created!\n\n**Type:** ${{ steps.parse.outputs.bump }}\n**Packages:** ${{ steps.packages.outputs.packages }}\n**Summary:** ${{ steps.parse.outputs.summary }}`
});

- name: React failure
if: failure()
uses: actions/github-script@v7
with:
script: |
await github.rest.reactions.createForIssueComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: context.payload.comment.id,
content: 'confused'
});

- name: Comment failure
if: failure()
uses: actions/github-script@v7
with:
script: |
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `❌ Failed to create changeset. Please check the [workflow run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details.\n\n**Usage:**\n\`\`\`\n/changeset patch\n/changeset minor\n/changeset major @node-ts-cache/core,@node-ts-cache/lru-storage\n\`\`\`\n\nAdd a summary on the next line:\n\`\`\`\n/changeset patch\nFixed a bug in cache expiration\n\`\`\``
});
86 changes: 86 additions & 0 deletions .github/workflows/changeset-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: Changeset Check

on:
pull_request:
branches:
- main

jobs:
check:
name: Check for changeset
runs-on: ubuntu-latest
permissions:
pull-requests: write

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Check for changeset
id: check
run: |
# Skip check for release PRs
if [[ "${{ github.head_ref }}" == "changeset-release/"* ]]; then
echo "skip=true" >> $GITHUB_OUTPUT
exit 0
fi

# Check if any changeset files exist (excluding config.json and README)
CHANGESETS=$(find .changeset -name "*.md" -type f ! -name "README.md" 2>/dev/null | wc -l)

if [ "$CHANGESETS" -eq 0 ]; then
echo "found=false" >> $GITHUB_OUTPUT
exit 0
fi

echo "found=true" >> $GITHUB_OUTPUT
echo "count=$CHANGESETS" >> $GITHUB_OUTPUT

- name: Post or update comment
if: steps.check.outputs.skip != 'true'
uses: actions/github-script@v7
with:
script: |
const found = '${{ steps.check.outputs.found }}' === 'true';
const count = '${{ steps.check.outputs.count }}';

const marker = '<!-- changeset-check-comment -->';

let body;
if (found) {
body = `${marker}\n✅ **Changeset found** (${count} changeset file(s))\n\nThis PR will be included in the next release.`;
} else {
body = `${marker}\n⚠️ **No changeset found**\n\nThis PR doesn't have a changeset. If this PR should trigger a release, please add one:\n\n### Option 1: Comment command\nComment on this PR:\n\`\`\`\n/changeset patch\nYour change description here\n\`\`\`\n\nOr specify packages:\n\`\`\`\n/changeset minor @node-ts-cache/core\nAdded new caching feature\n\`\`\`\n\n### Option 2: Manual\n\`\`\`bash\npnpm changeset\ngit add .changeset && git commit -m "chore: add changeset" && git push\n\`\`\`\n\n---\n*If this PR doesn't need a release (e.g., docs, CI changes), you can ignore this message.*`;
}

// Find existing comment
const comments = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number
});

const existing = comments.data.find(c => c.body.includes(marker));

if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body
});
}

// Fail the check if no changeset found
if (!found) {
core.setFailed('No changeset found. See comment for instructions.');
}
7 changes: 3 additions & 4 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,10 @@ jobs:
id: changesets
uses: changesets/action@v1
with:
publish: pnpm release
version: pnpm version
publish: pnpm run release
version: pnpm run version
title: 'chore: release packages'
commit: 'chore: release packages'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_CONFIG_PROVENANCE: true
Loading