chore: migrate CI/CD pipeline from GitHub Actions to GitLab CI
This commit is contained in:
1 parent
82e16e2598
commit
e73b68e8d2
9 files changed
+106
-333
No files matched your search
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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 }}
|
|
||||||
@@ -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'
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
||||||
Generated
+8
-9
@@ -16,6 +16,7 @@
|
|||||||
"@angular/forms": "21.2.10",
|
"@angular/forms": "21.2.10",
|
||||||
"@angular/material": "^21.2.8",
|
"@angular/material": "^21.2.8",
|
||||||
"@angular/platform-browser": "21.2.10",
|
"@angular/platform-browser": "21.2.10",
|
||||||
|
"@angular/platform-browser-dynamic": "21.2.10",
|
||||||
"@angular/platform-server": "21.2.10",
|
"@angular/platform-server": "21.2.10",
|
||||||
"@angular/router": "21.2.10",
|
"@angular/router": "21.2.10",
|
||||||
"@angular/ssr": "21.2.8",
|
"@angular/ssr": "21.2.8",
|
||||||
@@ -658,12 +659,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular/platform-browser-dynamic": {
|
"node_modules/@angular/platform-browser-dynamic": {
|
||||||
"version": "21.1.5",
|
"version": "21.2.10",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-21.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-21.2.10.tgz",
|
||||||
"integrity": "sha512-Pd8nPbJSIONnze1WS9wLBAtaFw4TYIH+ZGjKHS9G1E9l09tDWtHWyB7dY82Sc//Nc8iR4V7dcsbUmFjOJHThww==",
|
"integrity": "sha512-+/HMJSLnF87EODkHj0AKE3Q8AfYO/8jpTfr731QmplqBtCoLlA/1XR8aYow2hB9YKL9HZWDb2qGkRtCXhrtt+w==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.3.0"
|
"tslib": "^2.3.0"
|
||||||
},
|
},
|
||||||
@@ -671,10 +670,10 @@
|
|||||||
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@angular/common": "21.1.5",
|
"@angular/common": "21.2.10",
|
||||||
"@angular/compiler": "21.1.5",
|
"@angular/compiler": "21.2.10",
|
||||||
"@angular/core": "21.1.5",
|
"@angular/core": "21.2.10",
|
||||||
"@angular/platform-browser": "21.1.5"
|
"@angular/platform-browser": "21.2.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular/platform-server": {
|
"node_modules/@angular/platform-server": {
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
"@angular/forms": "21.2.10",
|
"@angular/forms": "21.2.10",
|
||||||
"@angular/material": "^21.2.8",
|
"@angular/material": "^21.2.8",
|
||||||
"@angular/platform-browser": "21.2.10",
|
"@angular/platform-browser": "21.2.10",
|
||||||
|
"@angular/platform-browser-dynamic": "21.2.10",
|
||||||
"@angular/platform-server": "21.2.10",
|
"@angular/platform-server": "21.2.10",
|
||||||
"@angular/router": "21.2.10",
|
"@angular/router": "21.2.10",
|
||||||
"@angular/ssr": "21.2.8",
|
"@angular/ssr": "21.2.8",
|
||||||
|
|||||||
Reference in new issue
Block a user