From e73b68e8d27c739177f994b2d00d2e88579a3eb4 Mon Sep 17 00:00:00 2001
From: TO
Date: Tue, 5 May 2026 21:07:25 +0200
Subject: [PATCH] chore: migrate CI/CD pipeline from GitHub Actions to GitLab
CI
---
.github/copilot-instructions.md | 47 ---------
.github/dependabot.yml | 34 -------
.github/workflows/codeql.yml | 41 --------
.github/workflows/dependabot-auto-merge.yml | 28 ------
.github/workflows/docker-build-push.yml | 69 -------------
.github/workflows/pr-validation.yml | 105 --------------------
.gitlab-ci.yml | 97 ++++++++++++++++++
package-lock.json | 17 ++--
package.json | 1 +
9 files changed, 106 insertions(+), 333 deletions(-)
delete mode 100644 .github/copilot-instructions.md
delete mode 100644 .github/dependabot.yml
delete mode 100644 .github/workflows/codeql.yml
delete mode 100644 .github/workflows/dependabot-auto-merge.yml
delete mode 100644 .github/workflows/docker-build-push.yml
delete mode 100644 .github/workflows/pr-validation.yml
create mode 100644 .gitlab-ci.yml
diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
deleted file mode 100644
index 9cb8b7f..0000000
--- a/.github/copilot-instructions.md
+++ /dev/null
@@ -1,47 +0,0 @@
-You are an expert in TypeScript, Angular, and scalable web application development. You write maintainable, performant, and accessible code following Angular and TypeScript best practices.
-
-## TypeScript Best Practices
-
-- Use strict type checking
-- Prefer type inference when the type is obvious
-- Avoid the `any` type; use `unknown` when type is uncertain
-
-## Angular Best Practices
-
-- Always use standalone components over NgModules
-- Must NOT set `standalone: true` inside Angular decorators. It's the default.
-- Use signals for state management
-- Implement lazy loading for feature routes
-- Do NOT use the `@HostBinding` and `@HostListener` decorators. Put host bindings inside the `host` object of the `@Component` or `@Directive` decorator instead
-- Use `NgOptimizedImage` for all static images.
- - `NgOptimizedImage` does not work for inline base64 images.
-
-## Components
-
-- Keep components small and focused on a single responsibility
-- Use `input()` and `output()` functions instead of decorators
-- Use `computed()` for derived state
-- Set `changeDetection: ChangeDetectionStrategy.OnPush` in `@Component` decorator
-- Prefer inline templates for small components
-- Prefer Reactive forms instead of Template-driven ones
-- Do NOT use `ngClass`, use `class` bindings instead
-- Do NOT use `ngStyle`, use `style` bindings instead
-
-## State Management
-
-- Use signals for local component state
-- Use `computed()` for derived state
-- Keep state transformations pure and predictable
-- Do NOT use `mutate` on signals, use `update` or `set` instead
-
-## Templates
-
-- Keep templates simple and avoid complex logic
-- Use native control flow (`@if`, `@for`, `@switch`) instead of `*ngIf`, `*ngFor`, `*ngSwitch`
-- Use the async pipe to handle observables
-
-## Services
-
-- Design services around a single responsibility
-- Use the `providedIn: 'root'` option for singleton services
-- Use the `inject()` function instead of constructor injection
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
deleted file mode 100644
index 4feaae7..0000000
--- a/.github/dependabot.yml
+++ /dev/null
@@ -1,34 +0,0 @@
-version: 2
-updates:
- # Enable version updates for npm
- - package-ecosystem: 'npm'
- directory: '/'
- schedule:
- interval: 'weekly'
- day: 'monday'
- open-pull-requests-limit: 10
- groups:
- angular:
- patterns:
- - '@angular/*'
- development-dependencies:
- dependency-type: 'development'
- update-types:
- - 'minor'
- - 'patch'
-
- # Enable version updates for GitHub Actions
- - package-ecosystem: 'github-actions'
- directory: '/'
- schedule:
- interval: 'weekly'
- day: 'monday'
- open-pull-requests-limit: 5
-
- # Enable version updates for Docker
- - package-ecosystem: 'docker'
- directory: '/'
- schedule:
- interval: 'weekly'
- day: 'monday'
- open-pull-requests-limit: 5
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
deleted file mode 100644
index c21ed48..0000000
--- a/.github/workflows/codeql.yml
+++ /dev/null
@@ -1,41 +0,0 @@
-name: CodeQL Security Analysis
-
-on:
- push:
- branches: [main]
- pull_request:
- branches: [main]
- schedule:
- - cron: '0 0 * * 1' # Run every Monday at midnight UTC
-
-jobs:
- analyze:
- name: Analyze Code
- runs-on: ubuntu-latest
- permissions:
- security-events: write
- actions: read
- contents: read
-
- strategy:
- fail-fast: false
- matrix:
- language: ['javascript-typescript']
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@v6
-
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v4
- with:
- languages: ${{ matrix.language }}
- queries: security-extended,security-and-quality
-
- - name: Autobuild
- uses: github/codeql-action/autobuild@v4
-
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v4
- with:
- category: '/language:${{matrix.language}}'
diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml
deleted file mode 100644
index e2c6b86..0000000
--- a/.github/workflows/dependabot-auto-merge.yml
+++ /dev/null
@@ -1,28 +0,0 @@
-name: Dependabot Auto-merge
-
-on:
- pull_request:
- branches: [main]
-
-permissions:
- contents: write
- pull-requests: write
-
-jobs:
- auto-merge:
- name: Auto-merge Dependabot minor and patch updates
- runs-on: ubuntu-latest
- if: github.actor == 'dependabot[bot]'
- steps:
- - name: Fetch Dependabot metadata
- id: metadata
- uses: dependabot/fetch-metadata@v2.5.0
-
- - name: Auto-merge minor and patch updates
- if: |
- steps.metadata.outputs.update-type == 'version-update:semver-minor' ||
- steps.metadata.outputs.update-type == 'version-update:semver-patch'
- run: gh pr merge --auto --squash "$PR_URL"
- env:
- PR_URL: ${{ github.event.pull_request.html_url }}
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/docker-build-push.yml b/.github/workflows/docker-build-push.yml
deleted file mode 100644
index 2894225..0000000
--- a/.github/workflows/docker-build-push.yml
+++ /dev/null
@@ -1,69 +0,0 @@
-name: Docker Build and Push
-
-on:
- push:
- branches: [main]
- pull_request:
- branches: [main]
- types: [closed]
- release:
- types: [published]
- workflow_dispatch:
-
-jobs:
- build-and-push:
- if: github.event_name != 'pull_request' || github.event.pull_request.merged == true
- runs-on: ubuntu-latest
- permissions:
- contents: read
- packages: write
- security-events: write
-
- steps:
- - uses: actions/checkout@v6
-
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v3
-
- - name: Log in to GitHub Container Registry
- uses: docker/login-action@v3
- with:
- registry: ghcr.io
- username: ${{ github.actor }}
- password: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Extract metadata
- id: meta
- uses: docker/metadata-action@v5
- with:
- images: ghcr.io/${{ github.repository }}
- tags: |
- type=ref,event=branch
- type=semver,pattern={{version}}
- type=semver,pattern={{major}}.{{minor}}
- type=sha
- type=raw,value=latest,enable={{is_default_branch}}
-
- - name: Build and push
- id: build
- uses: docker/build-push-action@v6
- with:
- context: .
- push: true
- tags: ${{ steps.meta.outputs.tags }}
- labels: ${{ steps.meta.outputs.labels }}
- cache-from: type=gha
- cache-to: type=gha,mode=max
-
- - name: Run Trivy vulnerability scanner
- uses: aquasecurity/trivy-action@0.35.0
- with:
- image-ref: ghcr.io/thoguet/vac-optimizer@${{ steps.build.outputs.digest }}
- format: 'sarif'
- output: 'trivy-results.sarif'
-
- - name: Upload Trivy results to GitHub Security tab
- uses: github/codeql-action/upload-sarif@v4
- if: always()
- with:
- sarif_file: 'trivy-results.sarif'
diff --git a/.github/workflows/pr-validation.yml b/.github/workflows/pr-validation.yml
deleted file mode 100644
index ac91c2b..0000000
--- a/.github/workflows/pr-validation.yml
+++ /dev/null
@@ -1,105 +0,0 @@
-name: PR Validation
-
-on:
- pull_request:
- branches: [main]
- push:
- branches: [main]
-
-jobs:
- lint:
- name: Lint Check
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v6
-
- - name: Setup Node.js
- uses: actions/setup-node@v6
- with:
- node-version: '20'
- cache: 'npm'
-
- - name: Install dependencies
- run: npm ci --legacy-peer-deps
-
- - name: Check code formatting with Prettier
- run: npx prettier --check "src/**/*.{ts,html,scss,css,json}"
-
- - name: Run ESLint
- run: npx eslint "src/**/*.ts"
-
- type-check:
- name: TypeScript Type Check
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v6
-
- - name: Setup Node.js
- uses: actions/setup-node@v6
- with:
- node-version: '20'
- cache: 'npm'
-
- - name: Install dependencies
- run: npm ci --legacy-peer-deps
-
- - name: Run TypeScript compiler
- run: npx tsc --noEmit
-
- test:
- name: Unit Tests
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v6
-
- - name: Setup Node.js
- uses: actions/setup-node@v6
- with:
- node-version: '20'
- cache: 'npm'
-
- - name: Install dependencies
- run: npm ci --legacy-peer-deps
-
- - name: Run unit tests
- run: npm test -- --watch=false --browsers=ChromeHeadless --code-coverage
-
- - name: Upload coverage reports
- uses: codecov/codecov-action@v5
- if: always()
- with:
- token: ${{ secrets.CODECOV_TOKEN }}
- files: ./coverage/vac-optimizer/lcov.info
- fail_ci_if_error: false
-
- build:
- name: Build Application
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v6
-
- - name: Setup Node.js
- uses: actions/setup-node@v6
- with:
- node-version: '20'
- cache: 'npm'
-
- - name: Install dependencies
- run: npm ci --legacy-peer-deps
-
- - name: Build production bundle
- run: npm run build -- --configuration production
- env:
- NG_BUILD_CACHE: '.angular/cache'
-
- - name: Check bundle size
- run: |
- du -sh dist/vac-optimizer/browser
- echo "Build completed successfully"
-
- - name: Upload build artifacts
- uses: actions/upload-artifact@v6
- with:
- name: build-artifacts
- path: dist/
- retention-days: 7
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000..19279be
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,97 @@
+image: node:20
+
+stages:
+ - validate
+ - test
+ - build
+ - security
+ - docker
+
+variables:
+ DOCKER_DRIVER: overlay2
+ DOCKER_TLS_CERTDIR: ""
+
+cache:
+ key:
+ files:
+ - package-lock.json
+ paths:
+ - .npm/
+
+before_script:
+ - npm ci --cache .npm --prefer-offline
+
+# -- Validate Stage --
+
+lint:
+ stage: validate
+ script:
+ - npx eslint "src/**/*.ts"
+
+prettier:
+ stage: validate
+ script:
+ - npx prettier --check "src/**/*.{ts,html,scss,css,json}"
+
+type-check:
+ stage: validate
+ script:
+ - npx tsc --noEmit
+
+# -- Test Stage --
+
+test:
+ stage: test
+ script:
+ - npm run test -- --watch=false --browsers=ChromeHeadless --code-coverage
+ artifacts:
+ when: always
+ paths:
+ - coverage/vac-optimizer/
+ expire_in: 7 days
+
+# -- Build Stage --
+
+build:
+ stage: build
+ script:
+ - npm run build -- --configuration production
+ artifacts:
+ paths:
+ - dist/
+ expire_in: 7 days
+
+# -- Security Stage --
+
+include:
+ - template: Security/SAST.gitlab-ci.yml
+
+trivy_scan:
+ stage: security
+ image:
+ name: aquasec/trivy:latest
+ entrypoint: [""]
+ before_script: [] # override global before_script
+ script:
+ # Trivy will scan the project directory
+ - trivy fs --format sarif --output trivy-results.sarif .
+ artifacts:
+ reports:
+ sast: trivy-results.sarif
+ expire_in: 7 days
+
+# -- Docker Stage --
+
+docker-build:
+ stage: docker
+ image: docker:24.0.5
+ services:
+ - docker:24.0.5-dind
+ before_script:
+ - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
+ script:
+ - docker build -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG" -t "$CI_REGISTRY_IMAGE:latest" .
+ - docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG"
+ - docker push "$CI_REGISTRY_IMAGE:latest"
+ rules:
+ - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
diff --git a/package-lock.json b/package-lock.json
index 618b4d4..5906803 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,6 +16,7 @@
"@angular/forms": "21.2.10",
"@angular/material": "^21.2.8",
"@angular/platform-browser": "21.2.10",
+ "@angular/platform-browser-dynamic": "21.2.10",
"@angular/platform-server": "21.2.10",
"@angular/router": "21.2.10",
"@angular/ssr": "21.2.8",
@@ -658,12 +659,10 @@
}
},
"node_modules/@angular/platform-browser-dynamic": {
- "version": "21.1.5",
- "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-21.1.5.tgz",
- "integrity": "sha512-Pd8nPbJSIONnze1WS9wLBAtaFw4TYIH+ZGjKHS9G1E9l09tDWtHWyB7dY82Sc//Nc8iR4V7dcsbUmFjOJHThww==",
- "dev": true,
+ "version": "21.2.10",
+ "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-21.2.10.tgz",
+ "integrity": "sha512-+/HMJSLnF87EODkHj0AKE3Q8AfYO/8jpTfr731QmplqBtCoLlA/1XR8aYow2hB9YKL9HZWDb2qGkRtCXhrtt+w==",
"license": "MIT",
- "peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
@@ -671,10 +670,10 @@
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
},
"peerDependencies": {
- "@angular/common": "21.1.5",
- "@angular/compiler": "21.1.5",
- "@angular/core": "21.1.5",
- "@angular/platform-browser": "21.1.5"
+ "@angular/common": "21.2.10",
+ "@angular/compiler": "21.2.10",
+ "@angular/core": "21.2.10",
+ "@angular/platform-browser": "21.2.10"
}
},
"node_modules/@angular/platform-server": {
diff --git a/package.json b/package.json
index dda476b..91fdc59 100644
--- a/package.json
+++ b/package.json
@@ -35,6 +35,7 @@
"@angular/forms": "21.2.10",
"@angular/material": "^21.2.8",
"@angular/platform-browser": "21.2.10",
+ "@angular/platform-browser-dynamic": "21.2.10",
"@angular/platform-server": "21.2.10",
"@angular/router": "21.2.10",
"@angular/ssr": "21.2.8",