Skip to content

Commit 8d2a327

Browse files
committed
feat: implement comprehensive CI/CD pipeline with quality checks and automated releases
1 parent db7d989 commit 8d2a327

File tree

5 files changed

+1733
-22
lines changed

5 files changed

+1733
-22
lines changed

.eslintrc.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"env": {
3+
"node": true,
4+
"es2022": true
5+
},
6+
"extends": [
7+
"eslint:recommended",
8+
"plugin:@typescript-eslint/recommended"
9+
],
10+
"parser": "@typescript-eslint/parser",
11+
"plugins": ["@typescript-eslint"],
12+
"rules": {
13+
"@typescript-eslint/no-explicit-any": "off",
14+
"@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }]
15+
}
16+
}

.github/workflows/ci-cd.yml

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
name: CI/CD Pipeline
2+
3+
on:
4+
push:
5+
branches: [main, master, develop]
6+
tags:
7+
- 'v*'
8+
pull_request:
9+
branches: [main, master, develop]
10+
types: [opened, synchronize, reopened]
11+
workflow_dispatch:
12+
inputs:
13+
environment:
14+
description: 'Deployment environment'
15+
required: true
16+
default: 'development'
17+
type: choice
18+
options:
19+
- development
20+
- production
21+
22+
env:
23+
NODE_VERSION: '22' # Node 22+ for SEA support
24+
25+
jobs:
26+
# ============================================================
27+
# PR Validation (on PRs and main pushes)
28+
# ============================================================
29+
pr-validation:
30+
name: 🔍 PR Validation
31+
runs-on: ubuntu-latest
32+
timeout-minutes: 5
33+
if: github.event_name == 'pull_request' || (github.ref == 'refs/heads/main' && github.event_name == 'push')
34+
35+
steps:
36+
- name: Validate PR Format
37+
uses: actions/github-script@v7
38+
with:
39+
script: |
40+
if (context.eventName === 'pull_request') {
41+
const title = context.payload.pull_request.title;
42+
const validPrefixes = ['feat:', 'fix:', 'docs:', 'style:', 'refactor:', 'perf:', 'test:', 'chore:', 'ci:', 'build:'];
43+
const isValid = validPrefixes.some(prefix => title.toLowerCase().startsWith(prefix));
44+
45+
if (!isValid) {
46+
core.setFailed(`PR title should start with one of: ${validPrefixes.join(', ')}`);
47+
} else {
48+
console.log('✅ PR title format is valid');
49+
}
50+
}
51+
52+
- name: Check PR Size
53+
uses: actions/github-script@v7
54+
with:
55+
script: |
56+
if (context.eventName === 'pull_request') {
57+
const additions = context.payload.pull_request.additions;
58+
const deletions = context.payload.pull_request.deletions;
59+
const totalChanges = additions + deletions;
60+
61+
console.log(`PR Size: +${additions} -${deletions} (~${totalChanges} total)`);
62+
63+
if (totalChanges > 1500) {
64+
core.setFailed(`Very large PR detected (${totalChanges} changes). Please break into smaller PRs.`);
65+
} else if (totalChanges > 800) {
66+
core.warning(`Large PR detected (${totalChanges} changes). Consider breaking into smaller PRs.`);
67+
}
68+
}
69+
70+
# ============================================================
71+
# Code Quality Checks
72+
# ============================================================
73+
quality-checks:
74+
name: 🔬 Code Quality
75+
runs-on: ubuntu-latest
76+
timeout-minutes: 10
77+
78+
steps:
79+
- name: Checkout code
80+
uses: actions/checkout@v4
81+
with:
82+
fetch-depth: 0
83+
84+
- name: Setup Node.js
85+
uses: actions/setup-node@v4
86+
with:
87+
node-version: ${{ env.NODE_VERSION }}
88+
cache: 'npm'
89+
90+
- name: Install dependencies
91+
run: npm ci
92+
93+
- name: Run ESLint
94+
id: lint
95+
run: npm run lint
96+
continue-on-error: true
97+
98+
- name: TypeScript Check
99+
id: ts-check
100+
run: npm run type-check
101+
continue-on-error: true
102+
103+
- name: Prettier Check
104+
id: format
105+
run: npm run format:check
106+
continue-on-error: true
107+
108+
- name: Quality Summary
109+
run: |
110+
echo "## 🔬 Code Quality Results" >> $GITHUB_STEP_SUMMARY
111+
echo "" >> $GITHUB_STEP_SUMMARY
112+
echo "| Check | Status |" >> $GITHUB_STEP_SUMMARY
113+
echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY
114+
echo "| ESLint | ${{ steps.lint.outcome == 'success' && '✅ Passed' || '⚠️ Issues Found' }} |" >> $GITHUB_STEP_SUMMARY
115+
echo "| TypeScript | ${{ steps.ts-check.outcome == 'success' && '✅ Passed' || '⚠️ Issues Found' }} |" >> $GITHUB_STEP_SUMMARY
116+
echo "| Prettier Format | ${{ steps.format.outcome == 'success' && '✅ Passed' || '⚠️ Issues Found' }} |" >> $GITHUB_STEP_SUMMARY
117+
118+
# ============================================================
119+
# Unit Tests
120+
# ============================================================
121+
unit-tests:
122+
name: 🧪 Unit Tests
123+
runs-on: ubuntu-latest
124+
timeout-minutes: 10
125+
126+
steps:
127+
- name: Checkout code
128+
uses: actions/checkout@v4
129+
130+
- name: Setup Node.js
131+
uses: actions/setup-node@v4
132+
with:
133+
node-version: ${{ env.NODE_VERSION }}
134+
cache: 'npm'
135+
136+
- name: Install dependencies
137+
run: npm ci
138+
139+
- name: Run Tests
140+
id: tests
141+
run: npm test
142+
143+
- name: Test Summary
144+
if: always()
145+
run: |
146+
echo "## 🧪 Unit Test Results" >> $GITHUB_STEP_SUMMARY
147+
echo "" >> $GITHUB_STEP_SUMMARY
148+
echo "| Test Suite | Status |" >> $GITHUB_STEP_SUMMARY
149+
echo "|------------|--------|" >> $GITHUB_STEP_SUMMARY
150+
echo "| Vitest | ${{ steps.tests.outcome == 'success' && '✅ Passed' || '❌ Failed' }} |" >> $GITHUB_STEP_SUMMARY
151+
152+
# ============================================================
153+
# Build Standalone EXE
154+
# ============================================================
155+
build:
156+
name: 🏗️ Build Standalone EXE
157+
runs-on: windows-latest
158+
timeout-minutes: 15
159+
needs: [quality-checks, unit-tests]
160+
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request'
161+
162+
steps:
163+
- name: Checkout code
164+
uses: actions/checkout@v4
165+
166+
- name: Setup Node.js
167+
uses: actions/setup-node@v4
168+
with:
169+
node-version: ${{ env.NODE_VERSION }}
170+
cache: 'npm'
171+
172+
- name: Install dependencies
173+
run: npm ci
174+
175+
- name: Build Standalone EXE
176+
run: npm run build:sea
177+
178+
- name: Upload Artifact
179+
uses: actions/upload-artifact@v4
180+
with:
181+
name: remote-opencode-windows
182+
path: dist/remote-opencode.exe
183+
retention-days: 7
184+
185+
# ============================================================
186+
# Release (Only on Tags)
187+
# ============================================================
188+
release:
189+
name: 🚀 Create Release
190+
runs-on: ubuntu-latest
191+
needs: build
192+
if: startsWith(github.ref, 'refs/tags/v')
193+
permissions:
194+
contents: write
195+
196+
steps:
197+
- name: Download Artifact
198+
uses: actions/download-artifact@v4
199+
with:
200+
name: remote-opencode-windows
201+
path: .
202+
203+
- name: Create GitHub Release
204+
uses: softprops/action-gh-release@v2
205+
with:
206+
files: remote-opencode.exe
207+
name: Release ${{ github.ref_name }}
208+
draft: false
209+
prerelease: false
210+
generate_release_notes: true
211+
env:
212+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
213+
214+
# ============================================================
215+
# Quality Gate - Final Summary
216+
# ============================================================
217+
quality-gate:
218+
name: 📊 Quality Gate
219+
needs: [quality-checks, unit-tests, build]
220+
runs-on: ubuntu-latest
221+
if: always()
222+
permissions:
223+
pull-requests: write
224+
issues: write
225+
contents: read
226+
227+
steps:
228+
- name: Pipeline Summary
229+
run: |
230+
echo "## 🚀 CI/CD Pipeline Summary" >> $GITHUB_STEP_SUMMARY
231+
echo "" >> $GITHUB_STEP_SUMMARY
232+
echo "| Stage | Status |" >> $GITHUB_STEP_SUMMARY
233+
echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY
234+
echo "| 🔬 Code Quality | ${{ needs.quality-checks.result == 'success' && '✅ Passed' || '❌ Failed' }} |" >> $GITHUB_STEP_SUMMARY
235+
echo "| 🧪 Unit Tests | ${{ needs.unit-tests.result == 'success' && '✅ Passed' || '❌ Failed' }} |" >> $GITHUB_STEP_SUMMARY
236+
echo "| 🏗️ Build | ${{ needs.build.result == 'success' && '✅ Passed' || '❌ Failed' }} |" >> $GITHUB_STEP_SUMMARY
237+
238+
- name: Check Quality Gate
239+
run: |
240+
if [ "${{ needs.build.result }}" != "success" ]; then
241+
echo "❌ Quality Gate FAILED: Build did not succeed"
242+
exit 1
243+
fi
244+
echo "✅ Quality Gate PASSED"
245+
246+
- name: Comment on PR
247+
if: github.event_name == 'pull_request' && always()
248+
uses: actions/github-script@v7
249+
with:
250+
script: |
251+
const buildStatus = "${{ needs.build.result }}";
252+
const qualityStatus = "${{ needs.quality-checks.result }}";
253+
const testStatus = "${{ needs.unit-tests.result }}";
254+
255+
const body = `## ${buildStatus === 'success' ? '✅' : '❌'} CI/CD Pipeline ${buildStatus === 'success' ? 'Passed' : 'Failed'}
256+
257+
| Stage | Status |
258+
|-------|--------|
259+
| 🔬 Code Quality | ${qualityStatus === 'success' ? '✅ Passed' : '⚠️ Issues'} |
260+
| 🧪 Unit Tests | ${testStatus === 'success' ? '✅ Passed' : '❌ Failed'} |
261+
| 🏗️ Build | ${buildStatus === 'success' ? '✅ Passed' : '❌ Failed'} |
262+
263+
${buildStatus === 'success' ? 'PR is ready for review! 🎉' : 'Please fix the issues above before merging.'}`;
264+
265+
try {
266+
github.rest.issues.createComment({
267+
issue_number: context.issue.number,
268+
owner: context.repo.owner,
269+
repo: context.repo.repo,
270+
body: body
271+
});
272+
} catch (error) {
273+
console.log('Could not post comment:', error);
274+
}

.prettierrc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"semi": true,
3+
"trailingComma": "all",
4+
"singleQuote": true,
5+
"printWidth": 100,
6+
"tabWidth": 2
7+
}

0 commit comments

Comments
 (0)