Add comprehensive CI/CD workflows for Forgejo Actions
- ci.yml: Complete CI pipeline with lint, type-check, tests, build, security, and i18n validation - code-quality.yml: Advanced code analysis including complexity, TODO tracking, and import analysis - dependency-review.yml: Automated dependency update review with security checks - pr-labeler.yml: Intelligent PR labeling based on files and content - release.yml: Automated release process with changelog generation - Documentation and best practices guide Features: - Multi-node testing (Node 18, 20) - Security scanning for hardcoded secrets - Bundle size impact analysis - Translation key validation - Complexity analysis and code quality metrics 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
5151e87824
commit
b913475932
6 changed files with 750 additions and 0 deletions
85
.forgejo/workflows/README.md
Normal file
85
.forgejo/workflows/README.md
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
# Forgejo CI/CD Workflows
|
||||||
|
|
||||||
|
This directory contains automated workflows for the Great Lakes Ice Report project.
|
||||||
|
|
||||||
|
## Workflows
|
||||||
|
|
||||||
|
### CI (ci.yml)
|
||||||
|
Runs on every push to main and on all pull requests. Includes:
|
||||||
|
- **Lint**: Checks code style with ESLint
|
||||||
|
- **Type Check**: Validates TypeScript types
|
||||||
|
- **Test**: Runs Jest tests on Node.js 18 and 20
|
||||||
|
- **Build**: Verifies all build outputs (backend, frontend, CSS)
|
||||||
|
- **Security**: Checks for hardcoded secrets and vulnerabilities
|
||||||
|
- **i18n Validation**: Ensures translation files are valid and complete
|
||||||
|
|
||||||
|
### Code Quality (code-quality.yml)
|
||||||
|
Runs on pull requests to analyze code quality:
|
||||||
|
- Complexity analysis
|
||||||
|
- Detection of console.log statements
|
||||||
|
- TODO/FIXME comment tracking
|
||||||
|
- Large file detection
|
||||||
|
- Import analysis and circular dependency checks
|
||||||
|
|
||||||
|
### Dependency Review (dependency-review.yml)
|
||||||
|
Triggered when package.json or package-lock.json changes:
|
||||||
|
- Identifies major version updates
|
||||||
|
- Security vulnerability scanning
|
||||||
|
- Bundle size impact analysis
|
||||||
|
|
||||||
|
### PR Labeler (pr-labeler.yml)
|
||||||
|
Automatically suggests labels based on:
|
||||||
|
- Changed file paths
|
||||||
|
- PR title and description keywords
|
||||||
|
- Type of changes (bug, feature, security, etc.)
|
||||||
|
|
||||||
|
### Release (release.yml)
|
||||||
|
Triggered on version tags (v*):
|
||||||
|
- Runs full test suite
|
||||||
|
- Builds the project
|
||||||
|
- Generates changelog
|
||||||
|
- Creates release archive
|
||||||
|
|
||||||
|
## Running Workflows Locally
|
||||||
|
|
||||||
|
You can test workflows locally using [act](https://github.com/nektos/act):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run all workflows
|
||||||
|
act
|
||||||
|
|
||||||
|
# Run specific workflow
|
||||||
|
act -W .forgejo/workflows/ci.yml
|
||||||
|
|
||||||
|
# Run specific job
|
||||||
|
act -j lint -W .forgejo/workflows/ci.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Workflow Status Badges
|
||||||
|
|
||||||
|
Add these to your README:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
[](https://git.deco.sh/deco/ice/actions/workflows/ci.yml)
|
||||||
|
[](https://git.deco.sh/deco/ice/actions/workflows/code-quality.yml)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Keep workflows fast**: Use caching and parallel jobs
|
||||||
|
2. **Fail fast**: Put quick checks (lint, type-check) before slow ones (tests)
|
||||||
|
3. **Be specific**: Use path filters to avoid unnecessary runs
|
||||||
|
4. **Cache dependencies**: Always use `actions/setup-node` with cache
|
||||||
|
5. **Security first**: Never commit secrets, always use repository secrets
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Workflow not running?
|
||||||
|
- Check if Forgejo Actions is enabled in repository settings
|
||||||
|
- Verify workflow syntax with online YAML validators
|
||||||
|
- Check runner availability
|
||||||
|
|
||||||
|
### Tests failing in CI but passing locally?
|
||||||
|
- Ensure Node.js versions match
|
||||||
|
- Check for missing environment variables
|
||||||
|
- Verify database initialization in CI environment
|
214
.forgejo/workflows/ci.yml
Normal file
214
.forgejo/workflows/ci.yml
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ main ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Lint Code
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
cache: 'npm'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Run ESLint
|
||||||
|
run: npm run lint
|
||||||
|
|
||||||
|
type-check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: TypeScript Type Check
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
cache: 'npm'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Run TypeScript compiler
|
||||||
|
run: npx tsc --noEmit
|
||||||
|
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Run Tests
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node-version: [18, 20]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node.js ${{ matrix.node-version }}
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
cache: 'npm'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Run tests with coverage
|
||||||
|
run: npm run test:coverage
|
||||||
|
|
||||||
|
- name: Upload coverage reports
|
||||||
|
if: matrix.node-version == '20'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: coverage-report
|
||||||
|
path: coverage/
|
||||||
|
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Build Project
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
cache: 'npm'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Build TypeScript
|
||||||
|
run: npm run build:ts
|
||||||
|
|
||||||
|
- name: Build Frontend
|
||||||
|
run: npm run build:frontend
|
||||||
|
|
||||||
|
- name: Build CSS
|
||||||
|
run: npm run build-css
|
||||||
|
|
||||||
|
- name: Verify build outputs
|
||||||
|
run: |
|
||||||
|
echo "Checking backend build..."
|
||||||
|
test -f dist/server.js || exit 1
|
||||||
|
|
||||||
|
echo "Checking frontend build..."
|
||||||
|
test -f public/dist/app-main.js || exit 1
|
||||||
|
test -f public/dist/app-admin.js || exit 1
|
||||||
|
test -f public/dist/app-privacy.js || exit 1
|
||||||
|
|
||||||
|
echo "Checking CSS build..."
|
||||||
|
test -f public/style.css || exit 1
|
||||||
|
|
||||||
|
echo "✅ All build outputs verified!"
|
||||||
|
|
||||||
|
security:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Security Checks
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
cache: 'npm'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Run npm audit
|
||||||
|
run: npm audit --audit-level=high
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
- name: Check for secrets
|
||||||
|
run: |
|
||||||
|
echo "Checking for potential secrets..."
|
||||||
|
! grep -r "MAPBOX_ACCESS_TOKEN" --include="*.js" --include="*.ts" --exclude-dir=node_modules --exclude-dir=dist --exclude-dir=.git . || \
|
||||||
|
(echo "❌ Found hardcoded Mapbox token!" && exit 1)
|
||||||
|
|
||||||
|
! grep -r "ADMIN_PASSWORD" --include="*.js" --include="*.ts" --exclude-dir=node_modules --exclude-dir=dist --exclude-dir=.git . || \
|
||||||
|
(echo "❌ Found hardcoded admin password!" && exit 1)
|
||||||
|
|
||||||
|
echo "✅ No hardcoded secrets found"
|
||||||
|
|
||||||
|
validate-i18n:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Validate i18n Files
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
|
||||||
|
- name: Validate JSON files
|
||||||
|
run: |
|
||||||
|
echo "Validating i18n JSON files..."
|
||||||
|
for file in src/i18n/locales/*.json; do
|
||||||
|
echo "Checking $file..."
|
||||||
|
node -e "JSON.parse(require('fs').readFileSync('$file', 'utf8'))" || exit 1
|
||||||
|
done
|
||||||
|
echo "✅ All i18n files are valid JSON"
|
||||||
|
|
||||||
|
- name: Check translation keys match
|
||||||
|
run: |
|
||||||
|
echo "Comparing translation keys..."
|
||||||
|
node -e "
|
||||||
|
const fs = require('fs');
|
||||||
|
const en = JSON.parse(fs.readFileSync('src/i18n/locales/en.json', 'utf8'));
|
||||||
|
const esMX = JSON.parse(fs.readFileSync('src/i18n/locales/es-MX.json', 'utf8'));
|
||||||
|
|
||||||
|
function getKeys(obj, prefix = '') {
|
||||||
|
let keys = [];
|
||||||
|
for (const key in obj) {
|
||||||
|
const fullKey = prefix ? prefix + '.' + key : key;
|
||||||
|
if (typeof obj[key] === 'object' && !Array.isArray(obj[key])) {
|
||||||
|
keys = keys.concat(getKeys(obj[key], fullKey));
|
||||||
|
} else {
|
||||||
|
keys.push(fullKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return keys.sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
const enKeys = getKeys(en);
|
||||||
|
const esMXKeys = getKeys(esMX);
|
||||||
|
|
||||||
|
const missingInEs = enKeys.filter(k => !esMXKeys.includes(k));
|
||||||
|
const missingInEn = esMXKeys.filter(k => !enKeys.includes(k));
|
||||||
|
|
||||||
|
if (missingInEs.length > 0) {
|
||||||
|
console.error('❌ Keys in en.json missing from es-MX.json:', missingInEs);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (missingInEn.length > 0) {
|
||||||
|
console.error('❌ Keys in es-MX.json missing from en.json:', missingInEn);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('✅ All translation keys match between locales');
|
||||||
|
"
|
141
.forgejo/workflows/code-quality.yml
Normal file
141
.forgejo/workflows/code-quality.yml
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
name: Code Quality
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
code-quality:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Code Quality Checks
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0 # Full history for better analysis
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
cache: 'npm'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Check code complexity
|
||||||
|
run: |
|
||||||
|
echo "Analyzing code complexity..."
|
||||||
|
npx -y complexity-report src/**/*.ts src/**/*.js --format json > complexity.json || true
|
||||||
|
|
||||||
|
node -e "
|
||||||
|
try {
|
||||||
|
const report = JSON.parse(require('fs').readFileSync('complexity.json', 'utf8'));
|
||||||
|
console.log('\\n📊 Code Complexity Report:');
|
||||||
|
|
||||||
|
const files = report.reports || [];
|
||||||
|
const complex = files.filter(f => f.aggregate?.cyclomatic > 10);
|
||||||
|
|
||||||
|
if (complex.length > 0) {
|
||||||
|
console.log('\\n⚠️ Files with high complexity (>10):');
|
||||||
|
complex.forEach(f => {
|
||||||
|
console.log(\` - \${f.path}: Cyclomatic complexity = \${f.aggregate.cyclomatic}\`);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log('✅ All files have acceptable complexity');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log('ℹ️ Complexity analysis not available');
|
||||||
|
}
|
||||||
|
"
|
||||||
|
|
||||||
|
- name: Check for console.log statements
|
||||||
|
run: |
|
||||||
|
echo "Checking for console.log statements..."
|
||||||
|
FILES=$(grep -r "console\.log" --include="*.ts" --include="*.js" \
|
||||||
|
--exclude-dir=node_modules --exclude-dir=dist --exclude-dir=public/dist \
|
||||||
|
--exclude-dir=tests --exclude-dir=scripts \
|
||||||
|
src/ || true)
|
||||||
|
|
||||||
|
if [ -n "$FILES" ]; then
|
||||||
|
echo "⚠️ Found console.log statements (consider using proper logging):"
|
||||||
|
echo "$FILES"
|
||||||
|
else
|
||||||
|
echo "✅ No console.log statements in source code"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Check for TODO/FIXME comments
|
||||||
|
run: |
|
||||||
|
echo "Checking for TODO/FIXME comments..."
|
||||||
|
TODOS=$(grep -r "TODO\|FIXME\|HACK\|XXX" --include="*.ts" --include="*.js" \
|
||||||
|
--exclude-dir=node_modules --exclude-dir=dist \
|
||||||
|
. || true)
|
||||||
|
|
||||||
|
if [ -n "$TODOS" ]; then
|
||||||
|
echo "📝 Found TODO/FIXME comments:"
|
||||||
|
echo "$TODOS"
|
||||||
|
echo ""
|
||||||
|
echo "ℹ️ Consider creating issues for these items"
|
||||||
|
else
|
||||||
|
echo "✅ No TODO/FIXME comments found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Check for large files
|
||||||
|
run: |
|
||||||
|
echo "Checking for large files..."
|
||||||
|
LARGE_FILES=$(find . -type f -size +1M \
|
||||||
|
-not -path "./node_modules/*" \
|
||||||
|
-not -path "./.git/*" \
|
||||||
|
-not -path "./dist/*" \
|
||||||
|
-not -path "./coverage/*" \
|
||||||
|
-not -name "*.db" \
|
||||||
|
-not -name "package-lock.json")
|
||||||
|
|
||||||
|
if [ -n "$LARGE_FILES" ]; then
|
||||||
|
echo "⚠️ Found large files (>1MB):"
|
||||||
|
echo "$LARGE_FILES" | xargs -I {} sh -c 'echo " - {} ($(du -h {} | cut -f1))"'
|
||||||
|
echo ""
|
||||||
|
echo "Consider if these files should be in the repository"
|
||||||
|
else
|
||||||
|
echo "✅ No large files detected"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Check TypeScript strict mode
|
||||||
|
run: |
|
||||||
|
echo "Verifying TypeScript strict mode..."
|
||||||
|
STRICT=$(grep -E '"strict":\s*true' tsconfig.json)
|
||||||
|
|
||||||
|
if [ -n "$STRICT" ]; then
|
||||||
|
echo "✅ TypeScript strict mode is enabled"
|
||||||
|
else
|
||||||
|
echo "⚠️ Consider enabling TypeScript strict mode for better type safety"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Analyze import statements
|
||||||
|
run: |
|
||||||
|
echo "Analyzing imports..."
|
||||||
|
|
||||||
|
# Check for circular dependencies
|
||||||
|
npx -y madge --circular --extensions ts,js src/ || true
|
||||||
|
|
||||||
|
# Check for unused exports
|
||||||
|
echo ""
|
||||||
|
echo "Checking for potentially unused exports..."
|
||||||
|
npx -y ts-unused-exports tsconfig.json --excludePathsFromReport=src/types || true
|
||||||
|
|
||||||
|
- name: Generate PR comment
|
||||||
|
if: always()
|
||||||
|
run: |
|
||||||
|
echo "## 🔍 Code Quality Report" > pr-comment.md
|
||||||
|
echo "" >> pr-comment.md
|
||||||
|
echo "All automated code quality checks have been run. Please review the logs above for details." >> pr-comment.md
|
||||||
|
echo "" >> pr-comment.md
|
||||||
|
echo "### Checklist" >> pr-comment.md
|
||||||
|
echo "- [ ] ESLint passes" >> pr-comment.md
|
||||||
|
echo "- [ ] TypeScript compiles without errors" >> pr-comment.md
|
||||||
|
echo "- [ ] Tests pass" >> pr-comment.md
|
||||||
|
echo "- [ ] No high complexity code" >> pr-comment.md
|
||||||
|
echo "- [ ] No hardcoded secrets" >> pr-comment.md
|
||||||
|
echo "" >> pr-comment.md
|
||||||
|
echo "_This comment was generated automatically by the Code Quality workflow._" >> pr-comment.md
|
112
.forgejo/workflows/dependency-review.yml
Normal file
112
.forgejo/workflows/dependency-review.yml
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
name: Dependency Review
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'package.json'
|
||||||
|
- 'package-lock.json'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
dependency-review:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Review Dependencies
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
cache: 'npm'
|
||||||
|
|
||||||
|
- name: Check for major version changes
|
||||||
|
run: |
|
||||||
|
echo "Checking for major dependency updates..."
|
||||||
|
git fetch origin main
|
||||||
|
|
||||||
|
# Get the package.json from main branch
|
||||||
|
git show origin/main:package.json > package-main.json
|
||||||
|
|
||||||
|
# Compare dependencies
|
||||||
|
node -e "
|
||||||
|
const fs = require('fs');
|
||||||
|
const mainPkg = JSON.parse(fs.readFileSync('package-main.json', 'utf8'));
|
||||||
|
const currentPkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
||||||
|
|
||||||
|
function compareDeps(mainDeps = {}, currentDeps = {}, type) {
|
||||||
|
console.log(\`\\nChecking \${type}:\`);
|
||||||
|
let hasChanges = false;
|
||||||
|
|
||||||
|
for (const [pkg, currentVer] of Object.entries(currentDeps)) {
|
||||||
|
const mainVer = mainDeps[pkg];
|
||||||
|
if (!mainVer) {
|
||||||
|
console.log(\` ✅ Added: \${pkg}@\${currentVer}\`);
|
||||||
|
hasChanges = true;
|
||||||
|
} else if (mainVer !== currentVer) {
|
||||||
|
const mainMajor = mainVer.match(/\\d+/)?.[0];
|
||||||
|
const currentMajor = currentVer.match(/\\d+/)?.[0];
|
||||||
|
|
||||||
|
if (mainMajor && currentMajor && mainMajor !== currentMajor) {
|
||||||
|
console.log(\` ⚠️ Major update: \${pkg} \${mainVer} → \${currentVer}\`);
|
||||||
|
} else {
|
||||||
|
console.log(\` 📦 Updated: \${pkg} \${mainVer} → \${currentVer}\`);
|
||||||
|
}
|
||||||
|
hasChanges = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [pkg, mainVer] of Object.entries(mainDeps)) {
|
||||||
|
if (!currentDeps[pkg]) {
|
||||||
|
console.log(\` ❌ Removed: \${pkg}@\${mainVer}\`);
|
||||||
|
hasChanges = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasChanges) {
|
||||||
|
console.log(\` No changes\`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compareDeps(mainPkg.dependencies, currentPkg.dependencies, 'dependencies');
|
||||||
|
compareDeps(mainPkg.devDependencies, currentPkg.devDependencies, 'devDependencies');
|
||||||
|
"
|
||||||
|
|
||||||
|
- name: Check for security advisories
|
||||||
|
run: |
|
||||||
|
npm audit --json > audit.json || true
|
||||||
|
node -e "
|
||||||
|
const audit = JSON.parse(require('fs').readFileSync('audit.json', 'utf8'));
|
||||||
|
const vulns = audit.metadata?.vulnerabilities || {};
|
||||||
|
|
||||||
|
console.log('\\nSecurity Audit Summary:');
|
||||||
|
console.log(' Critical:', vulns.critical || 0);
|
||||||
|
console.log(' High:', vulns.high || 0);
|
||||||
|
console.log(' Moderate:', vulns.moderate || 0);
|
||||||
|
console.log(' Low:', vulns.low || 0);
|
||||||
|
|
||||||
|
if (vulns.critical > 0 || vulns.high > 0) {
|
||||||
|
console.error('\\n❌ Found critical or high severity vulnerabilities!');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
"
|
||||||
|
|
||||||
|
- name: Check bundle size impact
|
||||||
|
run: |
|
||||||
|
echo "Analyzing bundle size impact..."
|
||||||
|
|
||||||
|
# Install dependencies from main
|
||||||
|
git show origin/main:package-lock.json > package-lock-main.json
|
||||||
|
npm ci --package-lock-only --package-lock=package-lock-main.json
|
||||||
|
npm run build:frontend || true
|
||||||
|
du -sh public/dist > size-main.txt
|
||||||
|
|
||||||
|
# Install current dependencies
|
||||||
|
npm ci
|
||||||
|
npm run build:frontend
|
||||||
|
du -sh public/dist > size-current.txt
|
||||||
|
|
||||||
|
echo "Bundle size comparison:"
|
||||||
|
echo "Main branch: $(cat size-main.txt)"
|
||||||
|
echo "This branch: $(cat size-current.txt)"
|
99
.forgejo/workflows/pr-labeler.yml
Normal file
99
.forgejo/workflows/pr-labeler.yml
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
name: PR Labeler
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, edited, synchronize]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
label:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Label Pull Request
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Analyze and label PR
|
||||||
|
run: |
|
||||||
|
echo "Analyzing PR for automatic labeling..."
|
||||||
|
|
||||||
|
# Get changed files
|
||||||
|
git fetch origin main
|
||||||
|
CHANGED_FILES=$(git diff --name-only origin/main...HEAD)
|
||||||
|
|
||||||
|
# Initialize labels array
|
||||||
|
LABELS=""
|
||||||
|
|
||||||
|
# Check file types and paths
|
||||||
|
if echo "$CHANGED_FILES" | grep -q "^src/.*\.ts$"; then
|
||||||
|
LABELS="$LABELS,backend"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if echo "$CHANGED_FILES" | grep -q "^src/frontend/.*\.ts$"; then
|
||||||
|
LABELS="$LABELS,frontend"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if echo "$CHANGED_FILES" | grep -q "^public/.*\.\(js\|html\)$"; then
|
||||||
|
LABELS="$LABELS,frontend"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if echo "$CHANGED_FILES" | grep -q "^src/scss/.*\.scss$"; then
|
||||||
|
LABELS="$LABELS,styles"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if echo "$CHANGED_FILES" | grep -q "^tests/.*\.test\.ts$"; then
|
||||||
|
LABELS="$LABELS,tests"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if echo "$CHANGED_FILES" | grep -q "^\.forgejo/workflows/"; then
|
||||||
|
LABELS="$LABELS,ci/cd"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if echo "$CHANGED_FILES" | grep -q "package.*\.json$"; then
|
||||||
|
LABELS="$LABELS,dependencies"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if echo "$CHANGED_FILES" | grep -q "^docs/\|README\.md\|CLAUDE\.md"; then
|
||||||
|
LABELS="$LABELS,documentation"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if echo "$CHANGED_FILES" | grep -q "^src/i18n/"; then
|
||||||
|
LABELS="$LABELS,i18n"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if echo "$CHANGED_FILES" | grep -q "^scripts/"; then
|
||||||
|
LABELS="$LABELS,tooling"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check PR title/body for keywords
|
||||||
|
PR_TITLE="${{ github.event.pull_request.title }}"
|
||||||
|
PR_BODY="${{ github.event.pull_request.body }}"
|
||||||
|
|
||||||
|
if echo "$PR_TITLE $PR_BODY" | grep -qi "security\|vulnerability\|CVE"; then
|
||||||
|
LABELS="$LABELS,security"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if echo "$PR_TITLE $PR_BODY" | grep -qi "performance\|optimize\|speed"; then
|
||||||
|
LABELS="$LABELS,performance"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if echo "$PR_TITLE $PR_BODY" | grep -qi "bug\|fix\|issue"; then
|
||||||
|
LABELS="$LABELS,bug"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if echo "$PR_TITLE $PR_BODY" | grep -qi "feature\|enhancement\|add"; then
|
||||||
|
LABELS="$LABELS,enhancement"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if echo "$PR_TITLE $PR_BODY" | grep -qi "breaking change\|BREAKING"; then
|
||||||
|
LABELS="$LABELS,breaking-change"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove leading comma and duplicates
|
||||||
|
LABELS=$(echo "$LABELS" | sed 's/^,//' | tr ',' '\n' | sort -u | tr '\n' ',' | sed 's/,$//')
|
||||||
|
|
||||||
|
echo "Suggested labels: $LABELS"
|
||||||
|
|
||||||
|
# Note: In actual Forgejo/Gitea, you would use the API to apply labels
|
||||||
|
# This is just for demonstration
|
||||||
|
echo "To apply labels, use: tea pr edit ${{ github.event.pull_request.number }} --add-label \"$LABELS\""
|
99
.forgejo/workflows/release.yml
Normal file
99
.forgejo/workflows/release.yml
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Create Release
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
cache: 'npm'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: npm test
|
||||||
|
|
||||||
|
- name: Build project
|
||||||
|
run: npm run build
|
||||||
|
|
||||||
|
- name: Generate changelog
|
||||||
|
run: |
|
||||||
|
echo "# Changelog" > CHANGELOG_CURRENT.md
|
||||||
|
echo "" >> CHANGELOG_CURRENT.md
|
||||||
|
|
||||||
|
# Get the previous tag
|
||||||
|
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
|
||||||
|
CURRENT_TAG="${{ github.ref_name }}"
|
||||||
|
|
||||||
|
if [ -n "$PREV_TAG" ]; then
|
||||||
|
echo "## Changes since $PREV_TAG" >> CHANGELOG_CURRENT.md
|
||||||
|
echo "" >> CHANGELOG_CURRENT.md
|
||||||
|
|
||||||
|
# Group commits by type
|
||||||
|
echo "### Features" >> CHANGELOG_CURRENT.md
|
||||||
|
git log $PREV_TAG..HEAD --grep="feat:" --pretty=format:"- %s" >> CHANGELOG_CURRENT.md || true
|
||||||
|
echo "" >> CHANGELOG_CURRENT.md
|
||||||
|
|
||||||
|
echo "### Bug Fixes" >> CHANGELOG_CURRENT.md
|
||||||
|
git log $PREV_TAG..HEAD --grep="fix:" --pretty=format:"- %s" >> CHANGELOG_CURRENT.md || true
|
||||||
|
echo "" >> CHANGELOG_CURRENT.md
|
||||||
|
|
||||||
|
echo "### Other Changes" >> CHANGELOG_CURRENT.md
|
||||||
|
git log $PREV_TAG..HEAD --grep -v "feat:\|fix:" --pretty=format:"- %s" >> CHANGELOG_CURRENT.md || true
|
||||||
|
else
|
||||||
|
echo "## Initial Release" >> CHANGELOG_CURRENT.md
|
||||||
|
git log --pretty=format:"- %s" >> CHANGELOG_CURRENT.md
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Create release archive
|
||||||
|
run: |
|
||||||
|
# Create a release archive excluding unnecessary files
|
||||||
|
tar -czf "ice-report-${{ github.ref_name }}.tar.gz" \
|
||||||
|
--exclude=node_modules \
|
||||||
|
--exclude=.git \
|
||||||
|
--exclude=.env \
|
||||||
|
--exclude=*.db \
|
||||||
|
--exclude=coverage \
|
||||||
|
--exclude=.forgejo \
|
||||||
|
.
|
||||||
|
|
||||||
|
- name: Create release notes
|
||||||
|
run: |
|
||||||
|
echo "# Release ${{ github.ref_name }}" > RELEASE_NOTES.md
|
||||||
|
echo "" >> RELEASE_NOTES.md
|
||||||
|
echo "## Installation" >> RELEASE_NOTES.md
|
||||||
|
echo "" >> RELEASE_NOTES.md
|
||||||
|
echo '```bash' >> RELEASE_NOTES.md
|
||||||
|
echo "wget https://git.deco.sh/deco/ice/releases/download/${{ github.ref_name }}/ice-report-${{ github.ref_name }}.tar.gz" >> RELEASE_NOTES.md
|
||||||
|
echo "tar -xzf ice-report-${{ github.ref_name }}.tar.gz" >> RELEASE_NOTES.md
|
||||||
|
echo "cd ice-report" >> RELEASE_NOTES.md
|
||||||
|
echo "npm install" >> RELEASE_NOTES.md
|
||||||
|
echo "npm run build" >> RELEASE_NOTES.md
|
||||||
|
echo '```' >> RELEASE_NOTES.md
|
||||||
|
echo "" >> RELEASE_NOTES.md
|
||||||
|
cat CHANGELOG_CURRENT.md >> RELEASE_NOTES.md
|
||||||
|
|
||||||
|
# Note: In actual Forgejo/Gitea, you would use their release API
|
||||||
|
# This is a placeholder showing what would be done
|
||||||
|
- name: Display release information
|
||||||
|
run: |
|
||||||
|
echo "Release ${{ github.ref_name }} is ready!"
|
||||||
|
echo "Archive: ice-report-${{ github.ref_name }}.tar.gz"
|
||||||
|
echo ""
|
||||||
|
echo "Release notes:"
|
||||||
|
cat RELEASE_NOTES.md
|
Loading…
Add table
Add a link
Reference in a new issue