Skip to content

Commit c10009a

Browse files
authored
chore(cicd): setup github actions and release workflow based in release publishing (#31)
1 parent c4662bb commit c10009a

File tree

5 files changed

+352
-0
lines changed

5 files changed

+352
-0
lines changed

.github/workflows/ci.yml

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
build:
11+
runs-on: ubuntu-latest
12+
13+
strategy:
14+
matrix:
15+
node-version: [18.x, 20.x]
16+
17+
steps:
18+
- uses: actions/checkout@v4
19+
20+
- name: Use Node.js ${{ matrix.node-version }}
21+
uses: actions/setup-node@v4
22+
with:
23+
node-version: ${{ matrix.node-version }}
24+
25+
- name: Setup pnpm
26+
uses: pnpm/action-setup@v3
27+
with:
28+
version: 10.8.0
29+
30+
- name: Get pnpm store directory
31+
id: pnpm-cache
32+
shell: bash
33+
run: |
34+
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
35+
36+
- name: Setup pnpm cache
37+
uses: actions/cache@v4
38+
with:
39+
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
40+
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
41+
restore-keys: |
42+
${{ runner.os }}-pnpm-store-
43+
44+
- name: Install dependencies
45+
run: pnpm install
46+
47+
- name: Lint
48+
run: pnpm lint
49+
50+
- name: Build
51+
run: pnpm build
52+
53+
- name: Test
54+
run: pnpm test:ci
55+
56+
- name: Upload coverage reports to Codecov
57+
uses: codecov/codecov-action@v4
58+
with:
59+
token: ${{ secrets.CODECOV_TOKEN }}
60+
fail_ci_if_error: false

.github/workflows/pr-changelog.yml

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
name: PR Changelog
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, reopened, edited]
6+
branches: [ main ]
7+
8+
jobs:
9+
validate-pr:
10+
name: Validate PR Description
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Check PR Description
15+
id: check-pr
16+
uses: actions/github-script@v7
17+
with:
18+
script: |
19+
const pr = context.payload.pull_request;
20+
const body = pr.body || '';
21+
22+
// Check if PR has a changelog section
23+
const hasChangelog = body.includes('## Changelog') ||
24+
body.includes('## Changes') ||
25+
body.includes('## What Changed');
26+
27+
if (!hasChangelog) {
28+
core.setFailed('PR description should include a changelog section (## Changelog, ## Changes, or ## What Changed)');
29+
await github.rest.issues.createComment({
30+
owner: context.repo.owner,
31+
repo: context.repo.repo,
32+
issue_number: pr.number,
33+
body: '⚠️ Please add a changelog section to your PR description. This will be used in release notes.\n\nAdd one of these sections:\n- `## Changelog`\n- `## Changes`\n- `## What Changed`\n\nAnd describe the changes in a user-friendly way.'
34+
});
35+
return;
36+
}
37+
38+
core.info('PR has a valid changelog section');
39+
40+
- name: Add Label
41+
if: success()
42+
uses: actions/github-script@v7
43+
with:
44+
script: |
45+
const pr = context.payload.pull_request;
46+
47+
// Determine PR type based on title or labels
48+
let prType = 'other';
49+
const title = pr.title.toLowerCase();
50+
51+
if (title.startsWith('fix:') || title.includes('bug') || title.includes('fix')) {
52+
prType = 'fix';
53+
} else if (title.startsWith('feat:') || title.includes('feature')) {
54+
prType = 'feature';
55+
} else if (title.includes('breaking') || title.includes('!:')) {
56+
prType = 'breaking';
57+
} else if (title.startsWith('docs:') || title.includes('documentation')) {
58+
prType = 'docs';
59+
} else if (title.startsWith('chore:') || title.includes('chore')) {
60+
prType = 'chore';
61+
}
62+
63+
// Add appropriate label
64+
try {
65+
await github.rest.issues.addLabels({
66+
owner: context.repo.owner,
67+
repo: context.repo.repo,
68+
issue_number: pr.number,
69+
labels: [`type: ${prType}`]
70+
});
71+
} catch (error) {
72+
core.warning(`Failed to add label: ${error.message}`);
73+
}

.github/workflows/release.yml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: Release
2+
3+
on:
4+
release:
5+
types: [published]
6+
7+
jobs:
8+
release:
9+
name: Release
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- name: Checkout
14+
uses: actions/checkout@v4
15+
with:
16+
fetch-depth: 0
17+
18+
- name: Setup Node.js
19+
uses: actions/setup-node@v4
20+
with:
21+
node-version: 20
22+
registry-url: 'https://registry.npmjs.org'
23+
24+
- name: Setup pnpm
25+
uses: pnpm/action-setup@v3
26+
with:
27+
version: 10.8.0
28+
29+
- name: Get pnpm store directory
30+
id: pnpm-cache
31+
shell: bash
32+
run: |
33+
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
34+
35+
- name: Setup pnpm cache
36+
uses: actions/cache@v4
37+
with:
38+
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
39+
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
40+
restore-keys: |
41+
${{ runner.os }}-pnpm-store-
42+
43+
- name: Install dependencies
44+
run: pnpm install
45+
46+
- name: Lint
47+
run: pnpm lint
48+
49+
- name: Build
50+
run: pnpm build
51+
52+
- name: Test
53+
run: pnpm test:ci
54+
55+
- name: Publish to NPM
56+
env:
57+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
58+
run: pnpm publish --no-git-checks

.github/workflows/version-bump.yml

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
name: Version Bump
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
7+
jobs:
8+
version-bump:
9+
name: Version Bump
10+
runs-on: ubuntu-latest
11+
if: "!contains(github.event.head_commit.message, 'skip ci') && !contains(github.event.head_commit.message, 'chore(release)')"
12+
13+
steps:
14+
- name: Checkout
15+
uses: actions/checkout@v4
16+
with:
17+
fetch-depth: 0
18+
token: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }}
19+
20+
- name: Setup Node.js
21+
uses: actions/setup-node@v4
22+
with:
23+
node-version: 20
24+
25+
- name: Setup pnpm
26+
uses: pnpm/action-setup@v3
27+
with:
28+
version: 10.8.0
29+
30+
- name: Get pnpm store directory
31+
id: pnpm-cache
32+
shell: bash
33+
run: |
34+
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
35+
36+
- name: Setup pnpm cache
37+
uses: actions/cache@v4
38+
with:
39+
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
40+
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
41+
restore-keys: |
42+
${{ runner.os }}-pnpm-store-
43+
44+
- name: Install dependencies
45+
run: pnpm install
46+
47+
- name: Lint
48+
run: pnpm lint
49+
50+
- name: Build
51+
run: pnpm build
52+
53+
- name: Test
54+
run: pnpm test:ci
55+
56+
- name: Test Release (Dry Run)
57+
env:
58+
GITHUB_TOKEN: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }}
59+
run: pnpm release:dry-run
60+
61+
- name: Update or Create GitHub Release Draft
62+
env:
63+
GITHUB_TOKEN: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }}
64+
run: |
65+
# Get the next version from semantic-release
66+
VERSION=$(npx semantic-release --dry-run | grep -oP 'The next release version is \K[0-9]+\.[0-9]+\.[0-9]+' || echo "")
67+
68+
if [ -z "$VERSION" ]; then
69+
echo "No version change detected, skipping release creation"
70+
exit 0
71+
fi
72+
73+
echo "Next version will be: $VERSION"
74+
75+
# Update package.json version
76+
npm version $VERSION --no-git-tag-version
77+
78+
# Generate changelog for this version
79+
npx semantic-release --dry-run --no-ci > release-notes.md
80+
81+
# Extract just the release notes section
82+
sed -n '/# \[/,/^$/p' release-notes.md > changelog-extract.md
83+
84+
# Get PR information
85+
PR_NUMBER=$(echo "${{ github.event.head_commit.message }}" | grep -oP '#\K[0-9]+' || echo "")
86+
PR_TITLE=""
87+
PR_BODY=""
88+
89+
if [ ! -z "$PR_NUMBER" ]; then
90+
PR_INFO=$(gh pr view $PR_NUMBER --json title,body || echo "{}")
91+
PR_TITLE=$(echo "$PR_INFO" | jq -r '.title // ""')
92+
PR_BODY=$(echo "$PR_INFO" | jq -r '.body // ""')
93+
fi
94+
95+
# Check if draft release already exists
96+
RELEASE_EXISTS=$(gh release view v$VERSION --json isDraft 2>/dev/null || echo "{}")
97+
IS_DRAFT=$(echo "$RELEASE_EXISTS" | jq -r '.isDraft // false')
98+
99+
if [ "$IS_DRAFT" = "true" ]; then
100+
echo "Updating existing draft release v$VERSION"
101+
102+
# Get existing release notes
103+
gh release view v$VERSION --json body | jq -r '.body' > existing-notes.md
104+
105+
# Add new PR information if available
106+
if [ ! -z "$PR_NUMBER" ] && [ ! -z "$PR_TITLE" ]; then
107+
echo -e "\n### PR #$PR_NUMBER: $PR_TITLE\n" >> existing-notes.md
108+
if [ ! -z "$PR_BODY" ]; then
109+
echo -e "$PR_BODY\n" >> existing-notes.md
110+
fi
111+
fi
112+
113+
# Update the release
114+
gh release edit v$VERSION --notes-file existing-notes.md
115+
else
116+
echo "Creating new draft release v$VERSION"
117+
118+
# Create initial release notes
119+
echo -e "# Release v$VERSION\n" > release-notes.md
120+
cat changelog-extract.md >> release-notes.md
121+
122+
# Add PR information if available
123+
if [ ! -z "$PR_NUMBER" ] && [ ! -z "$PR_TITLE" ]; then
124+
echo -e "\n## Pull Requests\n" >> release-notes.md
125+
echo -e "### PR #$PR_NUMBER: $PR_TITLE\n" >> release-notes.md
126+
if [ ! -z "$PR_BODY" ]; then
127+
echo -e "$PR_BODY\n" >> release-notes.md
128+
fi
129+
fi
130+
131+
# Create a draft release
132+
gh release create v$VERSION \
133+
--draft \
134+
--title "v$VERSION" \
135+
--notes-file release-notes.md
136+
fi
137+
138+
# Commit the version change
139+
git config --global user.name "GitHub Actions"
140+
git config --global user.email "[email protected]"
141+
git add package.json
142+
git commit -m "chore(release): bump version to $VERSION [skip ci]"
143+
git push

.releaserc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"branches": ["main"],
3+
"plugins": [
4+
"@semantic-release/commit-analyzer",
5+
"@semantic-release/release-notes-generator",
6+
"@semantic-release/changelog",
7+
"@semantic-release/npm",
8+
["@semantic-release/github", {
9+
"assets": [
10+
{"path": "dist/index.js", "label": "MCP Server Bundle"}
11+
]
12+
}],
13+
["@semantic-release/git", {
14+
"assets": ["package.json", "CHANGELOG.md"],
15+
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
16+
}]
17+
]
18+
}

0 commit comments

Comments
 (0)