Files
gh-secondsky-sap-skills-ski…/references/advanced-ci-cd.md
2025-11-30 08:55:38 +08:00

833 lines
16 KiB
Markdown

# UI5 Linter - Advanced CI/CD Integration
**Source**: [https://github.com/UI5/linter/blob/main/.github/workflows/ci.yml](https://github.com/UI5/linter/blob/main/.github/workflows/ci.yml)
**Last Updated**: 2025-11-21
**UI5 Linter Version**: 1.20.5
---
## Overview
This guide covers advanced CI/CD integration patterns for UI5 Linter, including the UI5 Linter project's own CI workflow, coverage reporting, license checking, dependency analysis, and multi-environment strategies.
---
## UI5 Linter's Own CI Workflow (Real-World Example)
The UI5 Linter project itself uses a comprehensive CI pipeline that demonstrates best practices.
### Complete Workflow
**File**: `.github/workflows/ci.yml`
**Note**: This is the actual CI workflow used by the UI5 Linter project itself, demonstrating production best practices.
```yaml
name: CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
permissions: {}
jobs:
test:
name: Test
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'npm'
- name: Install dependencies
run: npm ci --engine-strict
- name: Run linter
run: npm run lint
- name: Check licenses
run: npm run check-licenses
- name: Check dependencies
run: npm run depcheck
- name: Build
run: npm run build-test
- name: Run tests with coverage
run: npm run coverage
- name: Report coverage
uses: coverallsapp/github-action@v2.3.6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
continue-on-error: true
```
### Key Patterns Demonstrated
**1. Engine-Strict Installation**
```yaml
- name: Install dependencies
run: npm ci --engine-strict
```
**Why**: Fails fast if Node.js version is incompatible, preventing hidden issues.
---
**2. Multi-Step Quality Checks**
```yaml
- run: npm run lint # Code quality
- run: npm run check-licenses # Legal compliance
- run: npm run depcheck # Dependency health
- run: npm run build-test # Build verification
- run: npm run coverage # Test coverage
```
**Why**: Layered validation catches different types of issues.
---
**3. Coverage Reporting**
```yaml
- uses: coverallsapp/github-action@v2.3.6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
continue-on-error: true
```
**Why**: `continue-on-error` prevents workflow failure if Coveralls is down.
---
**4. Minimal Permissions**
```yaml
permissions: {}
```
**Why**: Security best practice - grant only necessary permissions.
---
## Advanced GitHub Actions Patterns
### Multi-Platform Testing
Test across operating systems:
```yaml
name: Cross-Platform Lint
on: [push, pull_request]
jobs:
lint:
strategy:
matrix:
os: [ubuntu-24.04, macos-latest, windows-latest]
node: ['24', '22']
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node }}
cache: 'npm'
- run: npm ci
- name: Run UI5 Linter
run: npm run lint
```
**Use Cases**:
- Ensure linter works on all platforms
- Catch platform-specific path issues
- Verify compatibility with multiple Node versions
---
### Multi-Job Workflow with Dependencies
Organize checks into separate jobs:
```yaml
name: Complete Validation
on: [push, pull_request]
jobs:
lint-ui5:
name: UI5 Linter
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'npm'
- run: npm ci
- run: npm run lint
lint-js:
name: ESLint
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'npm'
- run: npm ci
- run: npm run eslint
test:
name: Tests
needs: [lint-ui5, lint-js] # Only run if linting passes
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'npm'
- run: npm ci
- run: npm test
deploy:
name: Deploy
needs: test # Only deploy if tests pass
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- run: echo "Deploy to production"
```
**Benefits**:
- Parallel execution (faster)
- Clear separation of concerns
- Conditional deployment
---
### Caching for Performance
Optimize workflow performance with caching:
```yaml
jobs:
lint:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'npm' # Automatic npm cache
- name: Cache UI5 Linter results
uses: actions/cache@v4
with:
path: .ui5lint-cache
key: ui5lint-${{ hashFiles('webapp/**/*.js', 'webapp/**/*.xml', 'ui5lint.config.*') }}
restore-keys: |
ui5lint-
- run: npm ci
- name: Run UI5 Linter
run: npm run lint
```
**Performance Gain**: 30-50% faster on cache hit
---
### Diff-Based Linting (Lint Only Changed Files)
Lint only files changed in PR:
```yaml
name: Incremental Lint
on: [pull_request]
jobs:
lint-changed:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0 # Fetch all history for diff
- uses: actions/setup-node@v6
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- name: Get changed files
id: changed-files
run: |
echo "files=$(git diff --name-only --diff-filter=ACMRT ${{ github.event.pull_request.base.sha }} ${{ github.sha }} | grep -E '\.(js|xml|json)$' | tr '\n' ' ')" >> $GITHUB_OUTPUT
- name: Lint changed files
if: steps.changed-files.outputs.files != ''
run: |
npx ui5lint ${{ steps.changed-files.outputs.files }}
```
**Benefits**:
- Faster on large codebases
- Immediate feedback on changes
---
## Coverage Reporting Integration
### Coveralls Integration
**Setup**:
```bash
npm install --save-dev nyc
```
**package.json**:
```json
{
"scripts": {
"test": "ava",
"coverage": "nyc npm test",
"lint": "ui5lint"
},
"nyc": {
"reporter": ["lcov", "text"],
"include": ["src/**/*.js"],
"exclude": ["test/**"]
}
}
```
**GitHub Actions**:
```yaml
- name: Run tests with coverage
run: npm run coverage
- name: Upload to Coveralls
uses: coverallsapp/github-action@v2.3.6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
continue-on-error: true
```
---
### Codecov Integration
```yaml
- name: Run tests with coverage
run: npm run coverage
- name: Upload to Codecov
uses: codecov/codecov-action@v3
with:
files: ./coverage/lcov.info
flags: unittests
name: codecov-umbrella
fail_ci_if_error: true
```
---
## License Checking
Ensure all dependencies have acceptable licenses:
**package.json**:
```json
{
"scripts": {
"check-licenses": "license-checker --onlyAllow 'MIT;Apache-2.0;BSD-2-Clause;BSD-3-Clause;ISC'"
},
"devDependencies": {
"license-checker": "^25.0.1"
}
}
```
**GitHub Actions**:
```yaml
- name: Check licenses
run: npm run check-licenses
```
**Why**: Legal compliance, prevent GPL contamination
---
## Dependency Checking
Identify unused or missing dependencies:
**package.json**:
```json
{
"scripts": {
"depcheck": "depcheck --ignores='@types/*,eslint-*'"
},
"devDependencies": {
"depcheck": "^1.4.3"
}
}
```
**GitHub Actions**:
```yaml
- name: Check dependencies
run: npm run depcheck
```
**Why**: Keep dependencies clean, reduce bundle size
---
## Security Scanning
### npm audit
```yaml
- name: Security audit
run: npm audit --audit-level=moderate
continue-on-error: true # Don't fail on low/moderate
```
---
### Snyk Integration
```yaml
- name: Run Snyk
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high
```
---
## Artifact Management
Save lint results as artifacts:
```yaml
jobs:
lint:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'npm'
- run: npm ci
- name: Run UI5 Linter (JSON output)
run: npm run lint -- --format json 2> lint-diagnostics.log | tee lint-results.json
continue-on-error: true
- name: Run UI5 Linter (HTML report)
run: npm run lint -- --format html 2> lint-diagnostics.log | tee lint-report.html
continue-on-error: true
- name: Upload JSON results
uses: actions/upload-artifact@v5
if: always()
with:
name: lint-results-json
path: lint-results.json
retention-days: 30
- name: Upload HTML report
uses: actions/upload-artifact@v5
if: always()
with:
name: lint-report-html
path: lint-report.html
retention-days: 30
- name: Check for errors
run: |
if [ ! -f lint-results.json ]; then
echo "❌ Lint results file not found"
exit 1
fi
if ! jq '[.[].errorCount] | add' lint-results.json > /tmp/error_count 2>/dev/null; then
echo "❌ Failed to parse lint-results.json"
exit 1
fi
ERROR_COUNT=$(cat /tmp/error_count)
ERROR_COUNT=${ERROR_COUNT:-0}
if [ "$ERROR_COUNT" -gt 0 ]; then
echo "❌ Found $ERROR_COUNT linting errors"
exit 1
fi
```
**Benefits**:
- Download reports for review
- Historical tracking
- Share with non-technical stakeholders (HTML)
---
## Pull Request Comments
Add lint results as PR comments:
```yaml
jobs:
lint-pr:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- name: Run UI5 Linter
id: lint
run: |
npm run lint -- --format markdown > lint-results.md || true
echo "results<<EOF" >> $GITHUB_OUTPUT
cat lint-results.md >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Comment PR
uses: actions/github-script@v6
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.name,
body: '## UI5 Lint Results\n\n${{ steps.lint.outputs.results }}'
})
```
---
## GitLab CI Integration
**File**: `.gitlab-ci.yml`
```yaml
stages:
- lint
- test
- deploy
variables:
npm_config_cache: "$CI_PROJECT_DIR/.npm"
cache:
paths:
- .npm
- node_modules
lint:ui5:
stage: lint
image: node:20
script:
- npm ci
- npm run lint -- --format json > lint-results.json
- npm run lint -- --format html > lint-report.html
artifacts:
when: always
reports:
junit: lint-results.json
paths:
- lint-report.html
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH == "main"'
lint:ui5:fix:
stage: lint
image: node:20
script:
- npm ci
- npm run lint:fix
- git diff --exit-code || (echo "Autofix available" && exit 1)
allow_failure: true
only:
- merge_requests
```
---
## Jenkins Pipeline
**File**: `Jenkinsfile`
```groovy
pipeline {
agent {
docker {
image 'node:20'
}
}
stages {
stage('Install') {
steps {
sh 'npm ci'
}
}
stage('Lint') {
steps {
sh 'npm run lint -- --format json > lint-results.json'
sh 'npm run lint -- --format html > lint-report.html'
}
post {
always {
archiveArtifacts artifacts: 'lint-*.json,lint-*.html', allowEmptyArchive: true
publishHTML([
reportDir: '.',
reportFiles: 'lint-report.html',
reportName: 'UI5 Lint Report'
])
}
}
}
stage('Test') {
when {
expression {
def results = readJSON file: 'lint-results.json'
return results.sum { it.errorCount } == 0
}
}
steps {
sh 'npm test'
}
}
}
}
```
---
## Pre-Commit Hooks (Advanced)
### Lint-Staged with Auto-Fix
**package.json**:
```json
{
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"webapp/**/*.{js,xml}": [
"ui5lint --fix",
"ui5lint",
"git add"
],
"webapp/manifest.json": [
"ui5lint --fix",
"ui5lint"
]
}
}
```
**Setup**:
```bash
npm install --save-dev husky lint-staged
npx husky install
npx husky add .husky/pre-commit "npx lint-staged"
```
---
### Commit Message Linting
Enforce conventional commits (like UI5 Linter):
**package.json**:
```json
{
"devDependencies": {
"@commitlint/cli": "^17.0.0",
"@commitlint/config-conventional": "^17.0.0"
}
}
```
**commitlint.config.mjs**:
```javascript
export default {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [2, 'always', [
'feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore'
]],
'scope-enum': [2, 'always', [
'linter', 'autofix', 'docs', 'ci'
]]
}
};
```
**Husky Hook**:
```bash
npx husky add .husky/commit-msg "npx commitlint --edit $1"
```
---
## Monorepo Integration
For monorepos with multiple UI5 apps:
```yaml
name: Monorepo Lint
on: [push, pull_request]
jobs:
find-apps:
runs-on: ubuntu-24.04
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- uses: actions/checkout@v6
- id: set-matrix
run: |
APPS=$(find apps -name "ui5.yaml" -exec dirname {} \; | jq -R -s -c 'split("\n")[:-1]')
echo "matrix=$APPS" >> $GITHUB_OUTPUT
lint:
needs: find-apps
runs-on: ubuntu-24.04
strategy:
matrix:
app: ${{ fromJson(needs.find-apps.outputs.matrix) }}
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'npm'
cache-dependency-path: '${{ matrix.app }}/package-lock.json'
- name: Install dependencies
run: npm ci
working-directory: ${{ matrix.app }}
- name: Lint ${{ matrix.app }}
run: npm run lint
working-directory: ${{ matrix.app }}
```
---
## Environment-Specific Configurations
Use different configs for dev vs CI:
**ui5lint.config.js** (dev):
```javascript
export default {
ignores: ["webapp/thirdparty/**"]
};
```
**.ui5lint.ci.config.js** (CI):
```javascript
export default {
ignores: [
"webapp/thirdparty/**",
"webapp/test/**" // More aggressive ignores for CI
]
};
```
**GitHub Actions**:
```yaml
- name: Lint (CI config)
run: npm run lint -- --config .ui5lint.ci.config.js
```
---
## Summary Checklist
**Basic CI/CD** (✅ Covered in templates):
- [ ] Run linter on push/PR
- [ ] Fail build on errors
- [ ] Cache dependencies
**Advanced Patterns**:
- [ ] Multi-platform testing
- [ ] Coverage reporting
- [ ] License checking
- [ ] Dependency analysis
- [ ] Security scanning
- [ ] Artifact management
- [ ] PR comments
- [ ] Pre-commit hooks
**Production-Ready**:
- [ ] Environment-specific configs
- [ ] Monorepo support
- [ ] Performance optimization (caching)
- [ ] Historical tracking (artifacts)
---
## Resources
**UI5 Linter CI Workflow**: [https://github.com/UI5/linter/blob/main/.github/workflows/ci.yml](https://github.com/UI5/linter/blob/main/.github/workflows/ci.yml)
**GitHub Actions Docs**: [https://docs.github.com/en/actions](https://docs.github.com/en/actions)
**GitLab CI Docs**: [https://docs.gitlab.com/ee/ci/](https://docs.gitlab.com/ee/ci/)
**Jenkins Docs**: [https://www.jenkins.io/doc/](https://www.jenkins.io/doc/)
---
**Document Version**: 1.0
**Last Verified**: 2025-11-21
**Next Review**: 2026-02-21