# 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<> $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