chore: migrate CI/CD pipeline from GitHub Actions to GitLab CI

This commit is contained in:
nessar committed 2026-05-05 21:07:25 +02:00
1 parent 82e16e2598
commit e73b68e8d2
9 files changed
+106 -333

No files matched your search

-47
View File
@@ -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
-34
View File
@@ -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
-41
View File
@@ -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}}'
@@ -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 }}
-69
View File
@@ -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'
-105
View File
@@ -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
+97
View File
@@ -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
+8 -9
View File
@@ -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": {
+1
View File
@@ -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",