Compare commits
8 Commits
465678f3e4
...
5a2da916fa
| Author | SHA1 | Date | |
|---|---|---|---|
| 5a2da916fa | |||
| 82d1cf2c71 | |||
| 85d7315e30 | |||
| 179ba2b85c | |||
| ac8135aec8 | |||
| 74f040fa50 | |||
| 73fa36f9ec | |||
| 7fafabad42 |
@@ -2,136 +2,127 @@ name: Build, Test and Deploy
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches: [main, int, dev]
|
||||||
- main
|
|
||||||
- int
|
concurrency:
|
||||||
- dev
|
group: print-calculator-${{ gitea.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test-backend:
|
test-backend:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Python
|
# Evito actions/setup-python (spesso fragile su act_runner)
|
||||||
uses: actions/setup-python@v4
|
- name: Install Python deps + run tests
|
||||||
with:
|
shell: bash
|
||||||
python-version: '3.10'
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
run: |
|
||||||
pip install -r backend/requirements.txt
|
apt-get update
|
||||||
pip install pytest httpx
|
apt-get install -y --no-install-recommends python3 python3-pip
|
||||||
|
python3 -m pip install --upgrade pip
|
||||||
- name: Run Backend Tests
|
python3 -m pip install -r backend/requirements.txt
|
||||||
run: |
|
python3 -m pip install pytest httpx
|
||||||
export PYTHONPATH=$PYTHONPATH:$(pwd)/backend
|
export PYTHONPATH="${PYTHONPATH}:$(pwd)/backend"
|
||||||
pytest backend/tests
|
pytest backend/tests
|
||||||
|
|
||||||
build-and-push:
|
build-and-push:
|
||||||
needs: test-backend
|
needs: test-backend
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set Environment Variables
|
- name: Set TAG + OWNER lowercase
|
||||||
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
if [[ "${{ gitea.ref }}" == "refs/heads/main" ]]; then
|
if [[ "${{ gitea.ref }}" == "refs/heads/main" ]]; then
|
||||||
echo "TAG=prod" >> $GITHUB_ENV
|
echo "TAG=prod" >> "$GITHUB_ENV"
|
||||||
elif [[ "${{ gitea.ref }}" == "refs/heads/int" ]]; then
|
elif [[ "${{ gitea.ref }}" == "refs/heads/int" ]]; then
|
||||||
echo "TAG=int" >> $GITHUB_ENV
|
echo "TAG=int" >> "$GITHUB_ENV"
|
||||||
else
|
else
|
||||||
echo "TAG=dev" >> $GITHUB_ENV
|
echo "TAG=dev" >> "$GITHUB_ENV"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo "OWNER_LOWER=$(echo '${{ gitea.repository_owner }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
|
- name: Ensure docker CLI exists
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
if ! command -v docker >/dev/null 2>&1; then
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y --no-install-recommends docker.io
|
||||||
|
fi
|
||||||
|
docker version
|
||||||
|
|
||||||
- name: Login to Gitea Registry
|
- name: Login to Gitea Registry
|
||||||
uses: docker/login-action@v2
|
shell: bash
|
||||||
with:
|
run: |
|
||||||
registry: ${{ secrets.REGISTRY_URL }}
|
set -euo pipefail
|
||||||
username: ${{ secrets.GITEA_USER }}
|
printf '%s' "${{ secrets.REGISTRY_TOKEN }}" | docker login "${{ secrets.REGISTRY_URL }}" \
|
||||||
password: ${{ secrets.GITEA_TOKEN }}
|
-u "${{ secrets.REGISTRY_USER }}" --password-stdin
|
||||||
|
|
||||||
- name: Build and Push Backend
|
- name: Build & Push Backend
|
||||||
uses: docker/build-push-action@v4
|
shell: bash
|
||||||
with:
|
run: |
|
||||||
context: ./backend
|
BACKEND_IMAGE="${{ secrets.REGISTRY_URL }}/${{ env.OWNER_LOWER }}/print-calculator-backend:${{ env.TAG }}"
|
||||||
push: true
|
docker build -t "$BACKEND_IMAGE" ./backend
|
||||||
tags: ${{ secrets.REGISTRY_URL }}/${{ gitea.repository_owner }}/print-calculator-backend:${{ env.TAG }}
|
docker push "$BACKEND_IMAGE"
|
||||||
|
|
||||||
- name: Build and Push Frontend
|
- name: Build & Push Frontend
|
||||||
uses: docker/build-push-action@v4
|
shell: bash
|
||||||
with:
|
run: |
|
||||||
context: ./frontend
|
FRONTEND_IMAGE="${{ secrets.REGISTRY_URL }}/${{ env.OWNER_LOWER }}/print-calculator-frontend:${{ env.TAG }}"
|
||||||
push: true
|
docker build -t "$FRONTEND_IMAGE" ./frontend
|
||||||
tags: ${{ secrets.REGISTRY_URL }}/${{ gitea.repository_owner }}/print-calculator-frontend:${{ env.TAG }}
|
docker push "$FRONTEND_IMAGE"
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
needs: build-and-push
|
needs: build-and-push
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Set ENV
|
||||||
uses: actions/checkout@v3
|
shell: bash
|
||||||
|
|
||||||
- name: Set Deployment Vars
|
|
||||||
run: |
|
run: |
|
||||||
if [[ "${{ gitea.ref }}" == "refs/heads/main" ]]; then
|
if [[ "${{ gitea.ref }}" == "refs/heads/main" ]]; then
|
||||||
echo "ENV=prod" >> $GITHUB_ENV
|
echo "ENV=prod" >> "$GITHUB_ENV"
|
||||||
echo "TAG=prod" >> $GITHUB_ENV
|
|
||||||
elif [[ "${{ gitea.ref }}" == "refs/heads/int" ]]; then
|
elif [[ "${{ gitea.ref }}" == "refs/heads/int" ]]; then
|
||||||
echo "ENV=int" >> $GITHUB_ENV
|
echo "ENV=int" >> "$GITHUB_ENV"
|
||||||
echo "TAG=int" >> $GITHUB_ENV
|
|
||||||
else
|
else
|
||||||
echo "ENV=dev" >> $GITHUB_ENV
|
echo "ENV=dev" >> "$GITHUB_ENV"
|
||||||
echo "TAG=dev" >> $GITHUB_ENV
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Create Remote Directory
|
- name: Trigger deploy on Unraid (forced command key)
|
||||||
uses: appleboy/ssh-action@v0.1.10
|
shell: bash
|
||||||
with:
|
run: |
|
||||||
host: ${{ secrets.SERVER_HOST }}
|
set -euo pipefail
|
||||||
username: ${{ secrets.SERVER_USER }}
|
|
||||||
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
||||||
script: mkdir -p /mnt/user/appdata/print-calculator/${{ env.ENV }}/
|
|
||||||
|
|
||||||
- name: Copy Compose File to Server
|
apt-get update
|
||||||
uses: appleboy/scp-action@v0.1.4
|
apt-get install -y --no-install-recommends openssh-client
|
||||||
with:
|
|
||||||
host: ${{ secrets.SERVER_HOST }}
|
|
||||||
username: ${{ secrets.SERVER_USER }}
|
|
||||||
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
||||||
source: "docker-compose.deploy.yml"
|
|
||||||
target: "/mnt/user/appdata/print-calculator/${{ env.ENV }}/"
|
|
||||||
|
|
||||||
- name: Copy Env File to Server
|
mkdir -p ~/.ssh
|
||||||
uses: appleboy/scp-action@v0.1.4
|
chmod 700 ~/.ssh
|
||||||
with:
|
|
||||||
host: ${{ secrets.SERVER_HOST }}
|
|
||||||
username: ${{ secrets.SERVER_USER }}
|
|
||||||
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
||||||
source: "deploy/envs/${{ env.ENV }}.env"
|
|
||||||
target: "/mnt/user/appdata/print-calculator/${{ env.ENV }}/.env"
|
|
||||||
|
|
||||||
- name: Execute Remote Deployment
|
# 1) Prende il secret base64 e rimuove spazi/newline/CR
|
||||||
uses: appleboy/ssh-action@v0.1.10
|
printf '%s' "${{ secrets.SSH_PRIVATE_KEY_B64 }}" | tr -d '\r\n\t ' > /tmp/key.b64
|
||||||
with:
|
|
||||||
host: ${{ secrets.SERVER_HOST }}
|
# 2) (debug sicuro) stampa solo la lunghezza della base64
|
||||||
username: ${{ secrets.SERVER_USER }}
|
echo "b64_len=$(wc -c < /tmp/key.b64)"
|
||||||
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
||||||
script: |
|
# 3) Decodifica in chiave privata
|
||||||
cd /mnt/user/appdata/print-calculator/${{ env.ENV }}/
|
base64 -d /tmp/key.b64 > ~/.ssh/id_ed25519
|
||||||
|
|
||||||
# Rename the copied env file to strictly '.env' so docker compose picks it up automatically
|
# 4) Rimuove eventuali CRLF dentro la chiave (se proviene da Windows)
|
||||||
mv ${{ env.ENV }}.env .env
|
tr -d '\r' < ~/.ssh/id_ed25519 > ~/.ssh/id_ed25519.clean
|
||||||
|
mv ~/.ssh/id_ed25519.clean ~/.ssh/id_ed25519
|
||||||
# Login to registry
|
chmod 600 ~/.ssh/id_ed25519
|
||||||
echo ${{ secrets.GITEA_TOKEN }} | docker login ${{ secrets.REGISTRY_URL }} -u ${{ secrets.GITEA_USER }} --password-stdin
|
|
||||||
|
# 5) Validazione: se fallisce qui, la chiave NON è valida/corrotta
|
||||||
# Pull new images
|
ssh-keygen -y -f ~/.ssh/id_ed25519 >/dev/null
|
||||||
# We force reading from .env just to be safe, though default behavior does it too.
|
|
||||||
docker compose --env-file .env -f docker-compose.deploy.yml pull
|
# ... (resto del codice uguale)
|
||||||
|
ssh-keyscan -H "${{ secrets.SERVER_HOST }}" >> ~/.ssh/known_hosts 2>/dev/null
|
||||||
# Start/Update services
|
|
||||||
# TAG is inside .env now, so we don't even need to pass it explicitly!
|
# Aggiungiamo le opzioni di verbosità se dovesse fallire ancora,
|
||||||
docker compose --env-file .env -f docker-compose.deploy.yml up -d --remove-orphans
|
# e assicuriamoci che l'input sia pulito
|
||||||
|
ssh -i ~/.ssh/id_ed25519 -o BatchMode=yes "${{ secrets.SERVER_USER }}@${{ secrets.SERVER_HOST }}" "${{ env.ENV }}"
|
||||||
@@ -23,7 +23,7 @@ services:
|
|||||||
image: ${REGISTRY_URL}/${REPO_OWNER}/print-calculator-frontend:${TAG}
|
image: ${REGISTRY_URL}/${REPO_OWNER}/print-calculator-frontend:${TAG}
|
||||||
container_name: print-calculator-frontend-${ENV}
|
container_name: print-calculator-frontend-${ENV}
|
||||||
ports:
|
ports:
|
||||||
- "${FRONTEND_PORT}:80"
|
- "${FRONTEND_PORT}:8008"
|
||||||
depends_on:
|
depends_on:
|
||||||
- backend
|
- backend
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|||||||
@@ -39,6 +39,19 @@
|
|||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"production": {
|
"production": {
|
||||||
|
"fileReplacements": [
|
||||||
|
{
|
||||||
|
"replace": "src/environments/environment.ts",
|
||||||
|
"with": "src/environments/environment.prod.ts"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"optimization": true,
|
||||||
|
"outputHashing": "all",
|
||||||
|
"sourceMap": false,
|
||||||
|
"namedChunks": false,
|
||||||
|
"aot": true,
|
||||||
|
"extractLicenses": true,
|
||||||
|
|
||||||
"budgets": [
|
"budgets": [
|
||||||
{
|
{
|
||||||
"type": "initial",
|
"type": "initial",
|
||||||
@@ -50,8 +63,7 @@
|
|||||||
"maximumWarning": "4kB",
|
"maximumWarning": "4kB",
|
||||||
"maximumError": "8kB"
|
"maximumError": "8kB"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"outputHashing": "all"
|
|
||||||
},
|
},
|
||||||
"development": {
|
"development": {
|
||||||
"optimization": false,
|
"optimization": false,
|
||||||
@@ -59,6 +71,7 @@
|
|||||||
"sourceMap": true
|
"sourceMap": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"defaultConfiguration": "production"
|
"defaultConfiguration": "production"
|
||||||
},
|
},
|
||||||
"serve": {
|
"serve": {
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ interface QuoteResponse {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import { environment } from '../../environments/environment';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-calculator',
|
selector: 'app-calculator',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
@@ -61,7 +63,7 @@ export class CalculatorComponent {
|
|||||||
this.error = '';
|
this.error = '';
|
||||||
this.results = null;
|
this.results = null;
|
||||||
|
|
||||||
this.http.post<QuoteResponse>('http://localhost:8000/calculate/stl', formData)
|
this.http.post<QuoteResponse>(`${environment.apiUrl}/calculate/stl`, formData)
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: res => {
|
next: res => {
|
||||||
this.results = res;
|
this.results = res;
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import { Injectable, inject } from '@angular/core';
|
import { Injectable, inject } from '@angular/core';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
import { environment } from '../environments/environment';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class PrintService {
|
export class PrintService {
|
||||||
private http = inject(HttpClient);
|
private http = inject(HttpClient);
|
||||||
private apiUrl = 'http://127.0.0.1:8000'; // Should be in environment
|
private apiUrl = environment.apiUrl;
|
||||||
|
|
||||||
calculateQuote(file: File, params?: any): Observable<any> {
|
calculateQuote(file: File, params?: any): Observable<any> {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
|
|||||||
4
frontend/src/environments/environment.prod.ts
Normal file
4
frontend/src/environments/environment.prod.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export const environment = {
|
||||||
|
production: true,
|
||||||
|
apiUrl: 'https://3d-fab.ch'
|
||||||
|
};
|
||||||
4
frontend/src/environments/environment.ts
Normal file
4
frontend/src/environments/environment.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export const environment = {
|
||||||
|
production: false,
|
||||||
|
apiUrl: 'http://localhost:8000'
|
||||||
|
};
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
|
<meta name="robots" content="noindex, nofollow">
|
||||||
<title>Frontend</title>
|
<title>Frontend</title>
|
||||||
<base href="/">
|
<base href="/">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|||||||
Reference in New Issue
Block a user