1+ name : CI - Code Quality
2+
3+ # This workflow runs on every pull request and push to main branches
4+ # It performs formatting checks, linting, building, and testing
5+ on :
6+ push :
7+ branches : [main, develop]
8+ pull_request :
9+ branches : [main, develop]
10+ types : [opened, synchronize, reopened]
11+
12+ # Cancel in-progress runs when a new commit is pushed
13+ concurrency :
14+ group : ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
15+ cancel-in-progress : true
16+
17+ jobs :
18+ # Job 1: Code Quality Checks
19+ quality :
20+ name : 🎨 Code Quality
21+ runs-on : ubuntu-latest
22+ steps :
23+ - name : 📥 Checkout code
24+ uses : actions/checkout@v4
25+ with :
26+ fetch-depth : 0 # Full history for better analysis
27+
28+ - name : 📦 Setup Node.js
29+ uses : actions/setup-node@v4
30+ with :
31+ node-version : 22
32+ cache : ' npm'
33+
34+ - name : 📚 Install dependencies
35+ run : |
36+ echo "📦 Installing dependencies..."
37+ npm ci
38+ echo "✅ Dependencies installed"
39+
40+ - name : 🎨 Check formatting
41+ id : format
42+ run : |
43+ echo "🎨 Checking code formatting with Biome..."
44+ npm run format
45+
46+ # Check if there are any changes
47+ if [[ -n $(git status --porcelain) ]]; then
48+ echo "❌ Code formatting issues found!"
49+ echo "formatting_issues=true" >> $GITHUB_OUTPUT
50+
51+ # Show what files would be formatted
52+ echo "📝 Files with formatting issues:"
53+ git status --porcelain
54+
55+ # Create a comment body
56+ echo "## 🎨 Formatting Issues Found" >> format-comment.md
57+ echo "" >> format-comment.md
58+ echo "Please run \`npm run format\` locally to fix formatting issues." >> format-comment.md
59+ echo "" >> format-comment.md
60+ echo "Files that need formatting:" >> format-comment.md
61+ echo "\`\`\`" >> format-comment.md
62+ git status --porcelain | sed 's/^ M //' >> format-comment.md
63+ echo "\`\`\`" >> format-comment.md
64+
65+ exit 1
66+ else
67+ echo "✅ Code formatting is perfect!"
68+ echo "formatting_issues=false" >> $GITHUB_OUTPUT
69+ fi
70+
71+ - name : 🔍 Run linter
72+ id : lint
73+ run : |
74+ echo "🔍 Running linter..."
75+ npm run lint || {
76+ echo "❌ Linting issues found!"
77+ echo "lint_issues=true" >> $GITHUB_OUTPUT
78+ exit 1
79+ }
80+ echo "✅ No linting issues found!"
81+ echo "lint_issues=false" >> $GITHUB_OUTPUT
82+
83+ # Job 2: Build
84+ build :
85+ name : 🔨 Build
86+ runs-on : ubuntu-latest
87+ needs : quality # Only run if quality checks pass
88+ steps :
89+ - name : 📥 Checkout code
90+ uses : actions/checkout@v4
91+
92+ - name : 📦 Setup Node.js
93+ uses : actions/setup-node@v4
94+ with :
95+ node-version : 22
96+ cache : ' npm'
97+
98+ - name : 📚 Install dependencies
99+ run : |
100+ echo "📦 Installing dependencies..."
101+ npm ci
102+ echo "✅ Dependencies installed"
103+
104+ - name : 🔨 Build project
105+ run : |
106+ echo "🔨 Building project..."
107+ npm run build
108+ echo "✅ Build successful!"
109+
110+ - name : 📊 Check build output
111+ run : |
112+ echo "📊 Build output summary:"
113+ echo "Total files in dist: $(find dist -type f | wc -l)"
114+ echo "Build size: $(du -sh dist | cut -f1)"
115+
116+ # Ensure executable permissions
117+ chmod +x dist/index.js
118+ echo "✅ Executable permissions set"
119+
120+ - name : 💾 Upload build artifacts
121+ uses : actions/upload-artifact@v4
122+ with :
123+ name : build-artifacts
124+ path : dist/
125+ retention-days : 7
126+
127+ # Job 3: Test
128+ test :
129+ name : 🧪 Test
130+ runs-on : ubuntu-latest
131+ needs : quality # Only run if quality checks pass
132+ strategy :
133+ matrix :
134+ node-version : [18, 20, 22] # Test on multiple Node versions
135+ steps :
136+ - name : 📥 Checkout code
137+ uses : actions/checkout@v4
138+
139+ - name : 📦 Setup Node.js ${{ matrix.node-version }}
140+ uses : actions/setup-node@v4
141+ with :
142+ node-version : ${{ matrix.node-version }}
143+ cache : ' npm'
144+
145+ - name : 📚 Install dependencies
146+ run : |
147+ echo "📦 Installing dependencies..."
148+ npm ci
149+ echo "✅ Dependencies installed"
150+
151+ - name : 🧪 Run tests
152+ run : |
153+ echo "🧪 Running tests on Node.js ${{ matrix.node-version }}..."
154+ npm test
155+ echo "✅ All tests passed!"
156+
157+ - name : 📊 Generate coverage report
158+ if : matrix.node-version == 22 # Only on latest Node
159+ run : |
160+ echo "📊 Generating coverage report..."
161+ npm run test:coverage || true
162+
163+ # Display coverage summary
164+ if [ -f coverage/coverage-summary.json ]; then
165+ echo "📈 Coverage Summary:"
166+ node -e "
167+ const coverage = require('./coverage/coverage-summary.json');
168+ const total = coverage.total;
169+ console.log('Lines:', total.lines.pct + '%');
170+ console.log('Statements:', total.statements.pct + '%');
171+ console.log('Functions:', total.functions.pct + '%');
172+ console.log('Branches:', total.branches.pct + '%');
173+ "
174+ fi
175+
176+ - name : 📤 Upload coverage reports
177+ if : matrix.node-version == 22
178+ uses : actions/upload-artifact@v4
179+ with :
180+ name : coverage-report
181+ path : coverage/
182+ retention-days : 30
183+
184+ # Job 4: Summary
185+ summary :
186+ name : 📋 PR Summary
187+ runs-on : ubuntu-latest
188+ needs : [quality, build, test]
189+ if : always() # Run even if other jobs fail
190+ steps :
191+ - name : 📋 Create summary
192+ run : |
193+ if [[ "${{ github.event_name }}" == "pull_request" ]]; then
194+ echo "# 📋 Pull Request Check Summary" >> $GITHUB_STEP_SUMMARY
195+ else
196+ echo "# 📋 Branch Check Summary (${{ github.ref_name }})" >> $GITHUB_STEP_SUMMARY
197+ fi
198+ echo "" >> $GITHUB_STEP_SUMMARY
199+
200+ # Quality checks
201+ if [[ "${{ needs.quality.result }}" == "success" ]]; then
202+ echo "✅ **Code Quality**: Passed" >> $GITHUB_STEP_SUMMARY
203+ else
204+ echo "❌ **Code Quality**: Failed" >> $GITHUB_STEP_SUMMARY
205+ fi
206+
207+ # Build
208+ if [[ "${{ needs.build.result }}" == "success" ]]; then
209+ echo "✅ **Build**: Successful" >> $GITHUB_STEP_SUMMARY
210+ elif [[ "${{ needs.build.result }}" == "skipped" ]]; then
211+ echo "⏭️ **Build**: Skipped (quality checks failed)" >> $GITHUB_STEP_SUMMARY
212+ else
213+ echo "❌ **Build**: Failed" >> $GITHUB_STEP_SUMMARY
214+ fi
215+
216+ # Tests
217+ if [[ "${{ needs.test.result }}" == "success" ]]; then
218+ echo "✅ **Tests**: All passed" >> $GITHUB_STEP_SUMMARY
219+ elif [[ "${{ needs.test.result }}" == "skipped" ]]; then
220+ echo "⏭️ **Tests**: Skipped (quality checks failed)" >> $GITHUB_STEP_SUMMARY
221+ else
222+ echo "❌ **Tests**: Failed" >> $GITHUB_STEP_SUMMARY
223+ fi
224+
225+ echo "" >> $GITHUB_STEP_SUMMARY
226+ echo "## 📝 Next Steps" >> $GITHUB_STEP_SUMMARY
227+
228+ if [[ "${{ needs.quality.result }}" != "success" ]]; then
229+ echo "1. Fix code quality issues (run \`npm run format\` and \`npm run lint\`)" >> $GITHUB_STEP_SUMMARY
230+ elif [[ "${{ needs.build.result }}" != "success" ]] && [[ "${{ needs.build.result }}" != "skipped" ]]; then
231+ echo "1. Fix build errors" >> $GITHUB_STEP_SUMMARY
232+ elif [[ "${{ needs.test.result }}" != "success" ]] && [[ "${{ needs.test.result }}" != "skipped" ]]; then
233+ echo "1. Fix failing tests" >> $GITHUB_STEP_SUMMARY
234+ else
235+ if [[ "${{ github.event_name }}" == "pull_request" ]]; then
236+ echo "🎉 All checks passed! Your PR is ready for review." >> $GITHUB_STEP_SUMMARY
237+ else
238+ echo "🎉 All checks passed! The ${{ github.ref_name }} branch is healthy." >> $GITHUB_STEP_SUMMARY
239+ fi
240+ fi
241+
242+ - name : 💬 Comment on PR
243+ if : (failure() || cancelled()) && github.event_name == 'pull_request'
244+ uses : actions/github-script@v7
245+ with :
246+ github-token : ${{ secrets.GITHUB_TOKEN }}
247+ script : |
248+ const summary = [];
249+
250+ // Add header
251+ summary.push('## 🔍 PR Check Results\n');
252+
253+ // Quality checks
254+ if ('${{ needs.quality.result }}' === 'failure') {
255+ summary.push('### ❌ Code Quality Issues\n');
256+ summary.push('Please fix the following:');
257+ summary.push('- Run `npm run format` to fix formatting issues');
258+ summary.push('- Run `npm run lint` to see linting errors\n');
259+ }
260+
261+ // Build
262+ if ('${{ needs.build.result }}' === 'failure') {
263+ summary.push('### ❌ Build Failed\n');
264+ summary.push('The TypeScript build failed. Please check the build logs.\n');
265+ }
266+
267+ // Tests
268+ if ('${{ needs.test.result }}' === 'failure') {
269+ summary.push('### ❌ Tests Failed\n');
270+ summary.push('Some tests are failing. Please check the test logs.\n');
271+ }
272+
273+ if (summary.length > 1) {
274+ summary.push('---');
275+ summary.push('💡 **Tip**: Click on the job names above to see detailed logs.');
276+
277+ github.rest.issues.createComment({
278+ issue_number: context.issue.number,
279+ owner: context.repo.owner,
280+ repo: context.repo.repo,
281+ body: summary.join('\n')
282+ });
283+ }
0 commit comments