Lint Check Workflow (lint-check.yaml)
The Lint Check Workflow provides comprehensive code quality validation for both Node.js and Deno services. It features intelligent change detection, advanced ESLint configuration with ignore pattern matching, Deno formatting and linting, and detailed PR reporting with actionable feedback.
🕵️♂️ Overview
- File:
.github/workflows/lint-check.yaml - Purpose: Code quality and formatting validation
- Triggers: Pull requests and manual dispatch
- Services: Node.js (ESLint) and Deno (native tools)
- Features: Smart file detection, detailed PR comments, artifact preservation
🔄 Trigger Events
on:
pull_request:
branches: ['**']
types: [opened, reopened, synchronize]
workflow_dispatch:
🔍 Advanced Change Detection
File Analysis Process
# Get merge base for accurate diff
git fetch origin ${{ github.base_ref }}
BASE_SHA=$(git merge-base origin/${{ github.base_ref }} HEAD)
# Get changed files excluding deletions
git diff --name-only --diff-filter=d $BASE_SHA HEAD > files.txt
Service Type Detection
# Identify Node.js changes (exclude ai-service)
if grep -qv '^ai-service/' files.txt; then
echo "node_changed=true" >> $GITHUB_OUTPUT
fi
# Identify Deno changes
if grep -q '^ai-service/' files.txt; then
echo "deno_changed=true" >> $GITHUB_OUTPUT
fi
🟢 Node.js Linting
Environment Setup
# Node.js 20 with caching
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
# Install dependencies
npm i
Advanced ESLint Integration
Dynamic Ignore Pattern Matching
The workflow includes a sophisticated ESLint ignore matcher:
// scripts/eslint-ignore-matcher.js
const minimatch = require('minimatch');
function isFileIgnored(filePath, ignorePatterns) {
const normalizedPath = filePath.replace(/\\/g, '/');
return ignorePatterns.some(pattern => {
const isNegation = pattern.startsWith('!');
const actualPattern = isNegation ? pattern.slice(1) : pattern;
const matches = minimatch(normalizedPath, actualPattern, {
dot: true,
matchBase: !actualPattern.includes('/'),
});
return isNegation ? !matches : matches;
});
}
Ignore Pattern Processing
# Process files against .eslintignore
cat files.txt | node scripts/eslint-ignore-matcher.js > ignore_status.json
# Extract files to lint
jq -r '.[] | select(.ignored == false) | .file' ignore_status.json > files_to_lint.txt
# Extract ignored files for reporting
jq -r '.[] | select(.ignored == true) | .file' ignore_status.json > files_ignored.txt
ESLint Execution
# Run ESLint with JSON output
npx eslint \
--debug \
--format json \
--output-file eslint_report.json \
$(cat files_to_lint.txt)
Result Processing
# Clean and format ESLint output
jq '[.[] | select(.messages != null and .filePath != null) | . as $parent | {
filePath: .filePath,
messages: [.messages[] | select(.message != null and .line != null) | . + {filePath: $parent.filePath}]
}]' eslint_report.json > eslint_report_cleaned.json
🦕 Deno Linting
Environment Setup
# Deno 2.1.7
uses: denoland/setup-deno@v2
with:
deno-version: 2.1.7
Deno Format Check
cd ai-service
CHANGED_DENO_FILES=$(cat ../files.txt | grep -E '^ai-service/.*\.(ts|tsx)$' | sed 's/^ai-service\///')
if [ -n "$CHANGED_DENO_FILES" ]; then
if NO_COLOR=true deno fmt --check $CHANGED_DENO_FILES; then
echo "✅ Deno format check passed" >> ../lint_results.txt
else
echo "❌ Deno format check failed" >> ../lint_results.txt
fi
fi
Deno Lint Check
if NO_COLOR=true deno lint $CHANGED_DENO_FILES; then
echo "✅ Deno lint check passed" >> ../lint_results.txt
else
echo "❌ Deno lint check failed" >> ../lint_results.txt
fi
Deno Type Check
if NO_COLOR=true deno check $CHANGED_DENO_FILES; then
echo "✅ Deno type check passed" >> ../lint_results.txt
else
echo "❌ Deno type check failed" >> ../lint_results.txt
fi
📊 Result Analysis & Reporting
Error Categorization
# Extract ESLint errors (severity 2)
jq -r '.[] | .messages[] | select(.severity == 2) | "\(.filePath):\(.line) - \(.message) (\(.ruleId))"' eslint_report.json > eslint_errors.txt
# Extract ESLint warnings (severity 1)
jq -r '.[] | .messages[] | select(.severity == 1) | "\(.filePath):\(.line) - \(.message) (\(.ruleId))"' eslint_report.json > eslint_warnings.txt
Comprehensive Reporting
# Generate detailed lint report
{
echo "=== Run Information ==="
echo "Timestamp: $(date -u +'%Y-%m-%dT%H:%M:%SZ')"
echo "Runner: ${{ runner.os }}"
echo "Node Version: $(node --version)"
echo "Deno Version: $(deno --version)"
echo "=== Changed Files ==="
cat files.txt
echo "=== Service Detection ==="
echo "Node.js changes detected: ${{ steps.node-changes.outputs.node_changed }}"
echo "Deno changes detected: ${{ steps.deno-changes.outputs.deno_changed }}"
echo "=== ESLint Results ==="
if [ -s eslint_errors.txt ]; then
echo "Errors:"
cat eslint_errors.txt
else
echo "✅ No errors found!"
fi
if [ -s eslint_warnings.txt ]; then
echo "Warnings:"
cat eslint_warnings.txt
else
echo "✅ No warnings found!"
fi
echo "=== Deno Check Results ==="
cat lint_results.txt
} > lint_report.txt
📦 Artifact Management
Artifact Upload
- name: 📝 Upload Workflow Artifacts
uses: actions/upload-artifact@v4
with:
name: lint-check-artifacts
path: |
files.txt
lint_report.txt
eslint_report.json
deno-lint-results.txt
retention-days: 7
Artifact Contents
- files.txt: List of all changed files
- lint_report.txt: Comprehensive linting summary
- eslint_report.json: Raw ESLint output in JSON format
- deno-lint-results.txt: Deno-specific lint results
❌ Failure Conditions
Blocking Errors
The workflow fails on:
# ESLint errors (severity 2) - blocking
if [ -f "eslint_errors.txt" ] && [ -s "eslint_errors.txt" ]; then
FAIL=true
echo "❌ ESLint errors found - failing the workflow"
fi
# Deno check failures - blocking
if grep -q "❌" lint_results.txt; then
FAIL=true
echo "❌ Deno checks failed - failing the workflow"
fi
Non-blocking Warnings
ESLint warnings (severity 1) do not fail the workflow but are reported:
# Warnings are reported but don't block merge
if [ -s eslint_warnings.txt ]; then
echo "⚠️ ESLint warnings found (non-blocking)"
fi
💬 Pull Request Integration
Automated PR Comments
// Status determination
let statusEmoji = '✅';
let statusMessage = 'All checks passed!';
if (hasEslintErrors || hasFailedDenoChecks) {
statusEmoji = '❌';
statusMessage = 'Some checks failed. Action required!';
} else if (hasEslintWarnings) {
statusEmoji = '⚠️';
statusMessage = 'Checks passed with warnings';
}
// Comment structure
const commentBody = `## 🕵️♂️ Lint Check Results
### ${statusEmoji} Status: ${statusMessage}
### 📊 Run Information
- **Run ID**: [\`${process.env.RUN_ID}\`](${context.serverUrl}/${context.repo.owner}/${
context.repo.repo
}/actions/runs/${process.env.RUN_ID})
- **Started**: ${process.env.TIMESTAMP}
### 🔍 Changed Files
${process.env.FILES}
### 🛠 Service Detection
- **Node.js Changes**: ${process.env.NODE_CHANGES === 'true' ? '✅' : 'No'}
- **Deno Changes**: ${process.env.DENO_CHANGES === 'true' ? '🦕' : 'No'}
${
hasEslintErrors
? `### ❌ Errors
\`\`\`
${fs.readFileSync('eslint_errors.txt', 'utf8')}
\`\`\`
`
: ''
}
${
hasEslintWarnings
? `### ⚠️ Warnings
<details>
<summary>Click to see warnings</summary>
\`\`\`
${fs.readFileSync('eslint_warnings.txt', 'utf8')}
\`\`\`
</details>
`
: ''
}
### 📝 Detailed Results
<details>
<summary>Click to view full results</summary>
\`\`\`
${results}
\`\`\`
</details>
`;
Comment Features
- Status indicators: Clear visual status with emojis
- Collapsible sections: Detailed information in expandable sections
- Direct links: Links to workflow runs for debugging
- File tracking: Shows exactly which files were analyzed
- Service detection: Indicates which tools were used
🔧 Configuration
ESLint Configuration
The workflow respects standard ESLint configurations:
// .eslintrc.js or package.json
{
"extends": ["eslint:recommended"],
"rules": {
"no-unused-vars": "error",
"semi": ["error", "always"]
},
"ignorePatterns": [
"node_modules/",
"dist/",
"build/"
]
}
ESLint Ignore Patterns
# .eslintignore
node_modules/**
dist/**
build/**
coverage/**
**/*.min.js
*.config.js
Deno Configuration
// deno.json in ai-service/
{
"lint": {
"rules": {
"tags": ["recommended"]
}
},
"fmt": {
"useTabs": false,
"lineWidth": 80,
"indentWidth": 2
}
}
📋 Linting Configuration
Tool Versions
The workflow uses:
- Node.js 20: Runtime environment for ESLint
- Deno 2.1.7: Runtime for AI service linting
- ESLint: Code quality checking with JSON output
- Minimatch 5.1.0: Pattern matching for ignore files
Error Classification
- ESLint errors (severity 2): Block workflow execution
- ESLint warnings (severity 1): Reported but non-blocking
- Deno format failures: Block workflow execution
- Deno lint failures: Block workflow execution
- Deno type check failures: Block workflow execution
🔗 Related Workflows
- unit-tests.yml - Run tests after successful linting
- pr-review-reminder.yaml - Team notification workflow