This commit is contained in:
Murat Özkorkmaz
2025-10-17 15:51:04 +02:00
parent 61ee0b6e89
commit 1077322477
10 changed files with 408 additions and 0 deletions

70
.gitea-ci.yml Normal file
View File

@@ -0,0 +1,70 @@
version: "1"
stages:
- build
- push
- deploy
variables:
IMAGE_NAME: angular-web-app
TAG: ${CI_COMMIT_TAG:-latest}
GITEA_REGISTRY: ${CI_REGISTRY}
build-image:
stage: build
image: docker:latest
services:
- docker:dind
before_script:
- apk add --no-cache bash
- chmod +x ./build.sh ./push-to-gitea.sh
script:
- echo "🏗️ Baue Docker-Image..."
- ./build.sh "$IMAGE_NAME" "$TAG"
artifacts:
paths:
- Dockerfile
push-image:
stage: push
image: docker:latest
services:
- docker:dind
before_script:
- apk add --no-cache bash
- chmod +x ./push-to-gitea.sh
script:
- echo "⬆️ Push zu Gitea Registry..."
- |
export GITEA_REGISTRY="${CI_REGISTRY}"
export GITEA_USER="${CI_REGISTRY_USER}"
export GITEA_TOKEN="${CI_REGISTRY_TOKEN}"
./push-to-gitea.sh "$IMAGE_NAME" "$TAG"
only:
- main
- tags
deploy:
stage: deploy
image: alpine:latest
needs:
- push-image
only:
- main
- tags
script:
- echo "🚀 Deployment auf Zielserver starten..."
- apk add --no-cache openssh-client docker-cli
- echo "${DEPLOY_KEY}" > /tmp/deploy_key.pem
- chmod 600 /tmp/deploy_key.pem
- |
ssh -i /tmp/deploy_key.pem -o StrictHostKeyChecking=no ${DEPLOY_USER}@${DEPLOY_HOST} <<EOF
echo "📦 Pull aktuelles Image aus Registry..."
docker login ${GITEA_REGISTRY} -u "${CI_REGISTRY_USER}" -p "${CI_REGISTRY_TOKEN}"
docker pull ${GITEA_REGISTRY}/${CI_REGISTRY_USER}/${IMAGE_NAME}:${TAG}
echo "🛑 Stoppe alten Container (falls vorhanden)..."
docker stop ${IMAGE_NAME} || true
docker rm ${IMAGE_NAME} || true
echo "🚀 Starte neuen Container..."
docker run -d --name ${IMAGE_NAME} -p 80:80 ${GITEA_REGISTRY}/${CI_REGISTRY_USER}/${IMAGE_NAME}:${TAG}
EOF

1
.gitignore vendored
View File

@@ -41,3 +41,4 @@ testem.log
# System files
.DS_Store
Thumbs.db
/docker/.env

61
docker/.dockerignore Normal file
View File

@@ -0,0 +1,61 @@
# ================================
# Node and build output
# ================================
node_modules
dist
out-tsc
.angular
.cache
.tmp
# ================================
# Testing & Coverage
# ================================
coverage
jest
cypress
cypress/screenshots
cypress/videos
reports
playwright-report
.vite
.vitepress
# ================================
# Environment & log files
# ================================
*.env*
!*.env.production
*.log
*.tsbuildinfo
# ================================
# IDE & OS-specific files
# ================================
.vscode
.idea
.DS_Store
Thumbs.db
*.swp
# ================================
# Version control & CI files
# ================================
.git
.gitignore
# ================================
# Docker & local orchestration
# ================================
Dockerfile
Dockerfile.*
.dockerignore
docker-compose.yml
docker-compose*.yml
# ================================
# Miscellaneous
# ================================
*.bak
*.old
*.tmp

3
docker/.env.dist Normal file
View File

@@ -0,0 +1,3 @@
GITEA_REGISTRY=gitea.example.com
GITEA_USER=dein-benutzername
GITEA_TOKEN=ghp_oder_personal_access_token

46
docker/Dockerfile Normal file
View File

@@ -0,0 +1,46 @@
# =========================================
# Stage 1: Build the Angular Application
# =========================================
ARG NODE_VERSION=24.7.0-alpine
ARG NGINX_VERSION=alpine3.22
# Use a lightweight Node.js image for building (customizable via ARG)
FROM node:${NODE_VERSION} AS builder
# Set the working directory inside the container
WORKDIR /app
# Copy package-related files first to leverage Docker's caching mechanism
COPY package.json package-lock.json ./
# Install project dependencies using npm ci (ensures a clean, reproducible install)
RUN --mount=type=cache,target=/root/.npm npm ci
# Copy the rest of the application source code into the container
COPY . .
# Build the Angular application
RUN npm run build
# =========================================
# Stage 2: Prepare Nginx to Serve Static Files
# =========================================
FROM nginxinc/nginx-unprivileged:${NGINX_VERSION} AS runner
# Use a built-in non-root user for security best practices
USER nginx
# Copy custom Nginx config
COPY docker/nginx.conf /etc/nginx/nginx.conf
# Copy the static build output from the build stage to Nginx's default HTML serving directory
COPY --chown=nginx:nginx --from=builder /app/dist/*/browser /usr/share/nginx/html
# Expose port 8080 to allow HTTP traffic
# Note: The default NGINX container now listens on port 8080 instead of 80
EXPOSE 8080
# Start Nginx directly with custom config
ENTRYPOINT ["nginx", "-c", "/etc/nginx/nginx.conf"]
CMD ["-g", "daemon off;"]

17
docker/README.Docker.md Normal file
View File

@@ -0,0 +1,17 @@
### Building and running your application
When you're ready, start your application by running:
`docker compose up --build`.
### Deploying your application to the cloud
First, build your image, e.g.: `docker build -t myapp .`.
If your cloud uses a different CPU architecture than your development
machine (e.g., you are on a Mac M1 and your cloud provider is amd64),
you'll want to build the image for that platform, e.g.:
`docker build --platform=linux/amd64 -t myapp .`.
Then, push it to your registry, e.g. `docker push myregistry.com/myapp`.
Consult Docker's [getting started](https://docs.docker.com/go/get-started-sharing/)
docs for more detail on building and pushing.

21
docker/build.sh Executable file
View File

@@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -e
# Bild- und Tag-Parameter
IMAGE="${1:-angular-web-app}"
TAG="${2:-latest}"
# Pfad zum Skript selbst
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Root-Verzeichnis (eine Ebene über docker/)
ROOT_DIR="$(realpath "$SCRIPT_DIR/..")"
# Dockerfile-Pfad
DOCKERFILE="$SCRIPT_DIR/Dockerfile"
echo "🏗️ Baue Docker-Image..."
echo "🔹 Dockerfile: $DOCKERFILE"
echo "🔹 Build-Kontext: $ROOT_DIR"
docker build -f "$DOCKERFILE" -t "${IMAGE}:${TAG}" "$ROOT_DIR"

52
docker/compose.yaml Normal file
View File

@@ -0,0 +1,52 @@
# Comments are provided throughout this file to help you get started.
# If you need more help, visit the Docker Compose reference guide at
# https://docs.docker.com/go/compose-spec-reference/
# Here the instructions define your application as a service called "app".
# This service is built from the Dockerfile in the current directory.
# You can add other services your application may depend on here, such as a
# database or a cache. For examples, see the Awesome Compose repository:
# https://github.com/docker/awesome-compose
services:
app:
build:
context: .
target: final
# If your application exposes a port, uncomment the following lines and change
# the port numbers as needed. The first number is the host port and the second
# is the port inside the container.
# ports:
# - 8080:8080
# The commented out section below is an example of how to define a PostgreSQL
# database that your application can use. `depends_on` tells Docker Compose to
# start the database before your application. The `db-data` volume persists the
# database data between container restarts. The `db-password` secret is used
# to set the database password. You must create `db/password.txt` and add
# a password of your choosing to it before running `docker compose up`.
# depends_on:
# db:
# condition: service_healthy
# db:
# image: postgres
# restart: always
# user: postgres
# secrets:
# - db-password
# volumes:
# - db-data:/var/lib/postgresql/data
# environment:
# - POSTGRES_DB=example
# - POSTGRES_PASSWORD_FILE=/run/secrets/db-password
# expose:
# - 5432
# healthcheck:
# test: [ "CMD", "pg_isready" ]
# interval: 10s
# timeout: 5s
# retries: 5
# volumes:
# db-data:
# secrets:
# db-password:
# file: db/password.txt

69
docker/nginx.conf Normal file
View File

@@ -0,0 +1,69 @@
worker_processes auto;
pid /tmp/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Logging
access_log off;
error_log /dev/stderr warn;
# Performance
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
keepalive_requests 1000;
# Compression
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_min_length 256;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/javascript
application/x-javascript
application/json
application/xml
application/xml+rss
font/ttf
font/otf
image/svg+xml;
server {
listen 8080;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# Angular Routing
location / {
try_files $uri $uri/ /index.html;
}
# Static Assets Caching
location ~* \.(?:ico|css|js|gif|jpe?g|png|woff2?|eot|ttf|svg|map)$ {
expires 1y;
access_log off;
add_header Cache-Control "public, immutable";
}
# Optional: Explicit asset route
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
}

68
docker/push-to-gitea.sh Executable file
View File

@@ -0,0 +1,68 @@
#!/usr/bin/env bash
set -e # Skript bei Fehlern sofort beenden
# === .env-Datei laden ===
if [ -f .env ]; then
echo "📄 Lade Umgebungsvariablen aus .env..."
export $(grep -v '^#' .env | xargs)
else
echo "⚠️ Keine .env-Datei gefunden verwende Umgebungsvariablen oder Defaults."
fi
# === KONFIGURATION ===
GITEA_REGISTRY="${GITEA_REGISTRY:-gitea.example.com}"
GITEA_USER="${GITEA_USER:-}"
GITEA_TOKEN="${GITEA_TOKEN:-}"
DEFAULT_IMAGE="angular-web-app"
DEFAULT_TAG="latest"
# === PARAMETER ===
IMAGE_NAME="${1:-$DEFAULT_IMAGE}"
TAG="${2:-$DEFAULT_TAG}"
# === BUILD AUSFÜHREN ===
echo "🏗️ Baue Docker-Image..."
./build.sh "$IMAGE_NAME" "$TAG"
# === IMAGE-TAGGING ===
FULL_IMAGE="${GITEA_REGISTRY}/${GITEA_USER}/${IMAGE_NAME}:${TAG}"
echo "🏷️ Tagge Image für Gitea-Registry: ${FULL_IMAGE}"
docker tag "${IMAGE_NAME}:${TAG}" "${FULL_IMAGE}"
# === LOGIN ===
if [ -n "$GITEA_USER" ] && [ -n "$GITEA_TOKEN" ]; then
echo "🔐 Melde bei Gitea Registry an (${GITEA_REGISTRY})..."
echo "$GITEA_TOKEN" | docker login "$GITEA_REGISTRY" -u "$GITEA_USER" --password-stdin
else
echo "⚠️ Kein Benutzername oder Token angegeben überspringe Login."
fi
# === PRÜFEN, OB IMAGE SCHON EXISTIERT ===
echo "🔎 Prüfe, ob Image bereits in Registry existiert..."
EXISTS=false
if docker manifest inspect "${FULL_IMAGE}" >/dev/null 2>&1; then
EXISTS=true
echo "🟡 Image ${FULL_IMAGE} existiert bereits in der Registry."
else
echo "🟢 Image ${FULL_IMAGE} ist neu wird gepusht."
fi
# === DIGESTS VERGLEICHEN (wenn vorhanden) ===
if [ "$EXISTS" = true ]; then
LOCAL_DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' "${FULL_IMAGE}" 2>/dev/null | cut -d'@' -f2)
REMOTE_DIGEST=$(docker manifest inspect "${FULL_IMAGE}" 2>/dev/null | sha256sum | awk '{print $1}')
if [ -n "$LOCAL_DIGEST" ] && [ -n "$REMOTE_DIGEST" ] && [ "$LOCAL_DIGEST" = "$REMOTE_DIGEST" ]; then
echo "✅ Lokales und entferntes Image sind identisch Push wird übersprungen."
exit 0
else
echo "🆕 Unterschiede festgestellt Push wird ausgeführt."
fi
fi
# === PUSH ===
echo "⬆️ Push zum Gitea-Repository..."
docker push "${FULL_IMAGE}"
echo "✅ Upload abgeschlossen: ${FULL_IMAGE}"