Compare commits

...

17 Commits

13 changed files with 149 additions and 57 deletions

7
.env.example Normal file
View File

@@ -0,0 +1,7 @@
BACKEND_TAG=latest
FRONTEND_TAG=latest
VITE_API_BASE=/taller/api
JENKINS_BASE_URL=http://host.docker.internal:8080
JENKINS_JOB_NAME=TallerCiCd
JENKINS_USER=
JENKINS_TOKEN=

View File

@@ -2,13 +2,36 @@ pipeline {
agent none agent none
options { options {
disableConcurrentBuilds()
timestamps() timestamps()
} }
parameters {
string(
name: 'JENKINS_BASE_URL',
defaultValue: 'https://openbokeron.org/jenkins',
description: 'Base URL del Jenkins objetivo'
)
string(
name: 'JENKINS_JOB_NAME',
defaultValue: 'CD',
description: 'Nombre del job que se consulta en Jenkins'
)
string(
name: 'VITE_API_BASE',
defaultValue: '/taller/api',
description: 'Base path/API para el frontend (build time)'
)
}
environment { environment {
NODE_OPTIONS = '--max_old_space_size=2048' NODE_OPTIONS = '--max_old_space_size=2048'
APP_VERSION = "1.0.${BUILD_NUMBER}" APP_VERSION = "1.0.${BUILD_NUMBER}"
DOCKER_BUILDKIT = '1' DOCKER_BUILDKIT = '1'
JENKINS_BASE_URL = "${params.JENKINS_BASE_URL}"
JENKINS_JOB_NAME = "${params.JENKINS_JOB_NAME}"
VITE_API_BASE = "${params.VITE_API_BASE}"
PYTHONDONTWRITEBYTECODE = 1
} }
stages { stages {
@@ -18,9 +41,6 @@ pipeline {
========================= */ ========================= */
stage('Backend: test (main)') { stage('Backend: test (main)') {
when {
branch 'main'
}
agent { agent {
docker { docker {
image 'python:3.11-slim' image 'python:3.11-slim'
@@ -47,9 +67,6 @@ pipeline {
========================= */ ========================= */
stage('Docker: build images') { stage('Docker: build images') {
when {
branch 'main'
}
agent any agent any
steps { steps {
@@ -72,13 +89,12 @@ pipeline {
--build-arg GIT_COMMIT=${COMMIT_SHORT} \ --build-arg GIT_COMMIT=${COMMIT_SHORT} \
--build-arg COMMIT_AUTHOR="${COMMIT_AUTHOR}" \ --build-arg COMMIT_AUTHOR="${COMMIT_AUTHOR}" \
--build-arg BUILD_NUMBER=${BUILD_NUMBER} \ --build-arg BUILD_NUMBER=${BUILD_NUMBER} \
-t cafeteria-backend:${BUILD_NUMBER} \ -t cafeteria-backend:${APP_VERSION} \
-t cafeteria-backend:latest \
./backend ./backend
docker build \ docker build \
-t cafeteria-frontend:${BUILD_NUMBER} \ --build-arg VITE_API_BASE=${VITE_API_BASE} \
-t cafeteria-frontend:latest \ -t cafeteria-frontend:${APP_VERSION} \
./frontend ./frontend
''' '''
} }
@@ -89,14 +105,7 @@ pipeline {
========================= */ ========================= */
stage('Deploy (docker compose)') { stage('Deploy (docker compose)') {
when {
branch 'main'
}
agent any agent any
environment {
JENKINS_BASE_URL = 'http://jenkins:8080'
JENKINS_JOB_NAME = 'Espetos'
}
steps { steps {
withCredentials([ withCredentials([
usernamePassword( usernamePassword(
@@ -108,17 +117,16 @@ pipeline {
sh ''' sh '''
set -e set -e
echo "Deploying backend ${BUILD_NUMBER}" echo "Deploying ${APP_VERSION}"
echo "BACKEND_TAG=${BUILD_NUMBER}" > .env BACKEND_TAG=${APP_VERSION} FRONTEND_TAG=${APP_VERSION} docker compose up -d
echo "FRONTEND_TAG=${BUILD_NUMBER}" >> .env
docker-compose up -d
''' '''
} }
} }
} }
stage('Cleanup') { stage('Cleanup') {
agent any agent any
steps { steps {

View File

@@ -41,6 +41,11 @@ pipeline {
args '-u root' args '-u root'
} }
} }
environment {
PYTHONDONTWRITEBYTECODE = 1
}
steps { steps {
dir('backend') { dir('backend') {
sh ''' sh '''

View File

@@ -7,6 +7,16 @@
- Python 3.11+ y `pip` - Python 3.11+ y `pip`
- Node 18+ y `npm` - Node 18+ y `npm`
## Configuracion de entorno (local/prod)
Variables que usa `docker-compose` y el frontend:
```bash
cp .env.example .env
```
Notas:
- `VITE_API_BASE` por defecto apunta a `/taller/api` y el frontend proxya a la API.
- Para Jenkins local en contenedor: `JENKINS_BASE_URL=http://jenkins:8080`. Se hace necesario que back y jenkins estén en la misma red de Docker si se quiere probar en local.
- Para VPS: `JENKINS_BASE_URL=https://openbokeron.org/jenkins`.
## Backend (FastAPI) ## Backend (FastAPI)
```bash ```bash
cd backend cd backend
@@ -42,6 +52,7 @@ cd frontend
npm install npm install
npm run dev -- --host --port 5173 npm run dev -- --host --port 5173
``` ```
Abrir `http://localhost:5173/taller/`.
Tests, lint/check: Tests, lint/check:
```bash ```bash
cd frontend cd frontend

View File

@@ -1,4 +1,4 @@
FROM python:3.11-slim AS builder FROM docker.io/library/python:3.11-slim AS builder
WORKDIR /build WORKDIR /build
RUN apt-get update && apt-get install -y --no-install-recommends \ RUN apt-get update && apt-get install -y --no-install-recommends \
@@ -9,7 +9,7 @@ COPY requirements.txt .
RUN pip wheel --no-cache-dir --no-deps -r requirements.txt -w /build/wheels RUN pip wheel --no-cache-dir --no-deps -r requirements.txt -w /build/wheels
FROM python:3.11-slim FROM docker.io/library/python:3.11-slim
WORKDIR /app WORKDIR /app
# ---- Build args (desde Jenkins) ---- # ---- Build args (desde Jenkins) ----

View File

@@ -24,7 +24,10 @@ class RuntimeConfig:
git_commit: str = os.getenv("GIT_COMMIT", "local") git_commit: str = os.getenv("GIT_COMMIT", "local")
build_number: str = os.getenv("BUILD_NUMBER", "-") build_number: str = os.getenv("BUILD_NUMBER", "-")
commit_author: str = os.getenv("COMMIT_AUTHOR", "local") commit_author: str = os.getenv("COMMIT_AUTHOR", "local")
jenkins_base_url: str = os.getenv("JENKINS_BASE_URL", "localhost:8080") jenkins_base_url: str = os.getenv(
"JENKINS_BASE_URL",
"http://localhost:8080"
).rstrip("/")
jenkins_job_name: str = os.getenv("JENKINS_JOB_NAME", "") jenkins_job_name: str = os.getenv("JENKINS_JOB_NAME", "")
jenkins_user: str = os.getenv("JENKINS_USER", "") jenkins_user: str = os.getenv("JENKINS_USER", "")
jenkins_token: str = os.getenv("JENKINS_TOKEN", "") jenkins_token: str = os.getenv("JENKINS_TOKEN", "")

View File

@@ -2,14 +2,16 @@ services:
backend: backend:
build: build:
context: ./backend context: ./backend
image: cafeteria-backend:${BACKEND_TAG} image: cafeteria-backend:${BACKEND_TAG:-latest}
networks:
- cafeteria
ports: ports:
- "8000:8000" - "8000:8000"
environment: environment:
JENKINS_BASE_URL: ${JENKINS_BASE_URL} JENKINS_BASE_URL: ${JENKINS_BASE_URL:-http://jenkins:8080}
JENKINS_JOB_NAME: ${JENKINS_JOB_NAME} JENKINS_JOB_NAME: ${JENKINS_JOB_NAME:-TallerCiCd}
JENKINS_USER: ${JENKINS_USER} JENKINS_USER: ${JENKINS_USER:-}
JENKINS_TOKEN: ${JENKINS_TOKEN} JENKINS_TOKEN: ${JENKINS_TOKEN:-}
restart: unless-stopped restart: unless-stopped
healthcheck: healthcheck:
test: test:
@@ -26,10 +28,21 @@ services:
frontend: frontend:
build: build:
context: ./frontend context: ./frontend
image: cafeteria-frontend:${FRONTEND_TAG} args:
VITE_API_BASE: ${VITE_API_BASE:-/taller/api}
image: cafeteria-frontend:${FRONTEND_TAG:-latest}
networks:
- cafeteria
ports: ports:
- "80:80" - "8081:8081"
depends_on: depends_on:
backend: backend:
condition: service_healthy condition: service_healthy
restart: unless-stopped restart: unless-stopped
networks:
cafeteria:
enable_ipv6: true
ipam:
config:
- subnet: 2001:db8::/64

View File

@@ -1,12 +1,14 @@
FROM node:20-slim AS build FROM docker.io/library/node:20-slim AS build
WORKDIR /app WORKDIR /app
COPY package*.json ./ COPY package*.json ./
ARG VITE_API_BASE
ENV VITE_API_BASE=$VITE_API_BASE
RUN npm install --no-progress RUN npm install --no-progress
COPY . . COPY . .
RUN npm run build RUN npm run build
FROM nginx:1.27-alpine AS final FROM docker.io/library/nginx:1.27-alpine AS final
COPY nginx.conf /etc/nginx/conf.d/default.conf COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/dist /usr/share/nginx/html COPY --from=build /app/dist /usr/share/nginx/html/taller
EXPOSE 80 EXPOSE 8081
CMD ["nginx", "-g", "daemon off;"] CMD ["nginx", "-g", "daemon off;"]

View File

@@ -1,11 +1,24 @@
server { server {
listen 80; listen 8081;
server_name _; server_name _;
root /usr/share/nginx/html; root /usr/share/nginx/html;
index index.html; index index.html;
location / { location = /taller {
try_files $uri /index.html; return 301 /taller/;
}
location /taller/api/ {
proxy_pass http://backend:8000/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /taller/ {
try_files $uri $uri/ /taller/index.html;
} }
} }

View File

@@ -96,8 +96,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
if (!ENABLE_POLLING) return; if (!ENABLE_POLLING) return;
const pricesInterval = setInterval(fetchPrices, 8000); const pricesInterval = setInterval(fetchPrices, 180000);
const ciInterval = setInterval(fetchCiStatus, 10000); const ciInterval = setInterval(fetchCiStatus, 300000);
return () => { return () => {
clearInterval(pricesInterval); clearInterval(pricesInterval);
@@ -467,7 +467,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
<div class="openbokeron-logo"> <div class="openbokeron-logo">
<div class="logo-bubble"> <div class="logo-bubble">
<img <img
src="/open-bokeron-logo.png" src={`${import.meta.env.BASE_URL}/open-bokeron-logo.png`}
alt="Logo de Open Bokeron" alt="Logo de Open Bokeron"
loading="lazy" loading="lazy"
/> />

View File

@@ -16,5 +16,9 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
export const API_BASE = import.meta.env.VITE_API_BASE || 'http://localhost:8000'; const baseUrl = import.meta.env.BASE_URL || '/';
const defaultApiBase = `${baseUrl.replace(/\/$/, '')}/api`;
const rawApiBase = import.meta.env.VITE_API_BASE || defaultApiBase;
export const API_BASE = rawApiBase.replace(/\/$/, '');
export const ENABLE_POLLING = import.meta.env.MODE !== 'test'; export const ENABLE_POLLING = import.meta.env.MODE !== 'test';

View File

@@ -1,7 +1,15 @@
import { defineConfig } from 'vite'; import { defineConfig, loadEnv } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte'; import { svelte } from '@sveltejs/vite-plugin-svelte';
export default defineConfig({ export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), 'VITE_');
const basePath = '/taller/';
const defaultApiBase = `${basePath.replace(/\/$/, '')}/api`;
const apiBase = (env.VITE_API_BASE || defaultApiBase).replace(/\/$/, '');
const apiProxyPath = apiBase.startsWith('http') ? null : apiBase;
return {
base: basePath,
plugins: [svelte()], plugins: [svelte()],
test: { test: {
environment: 'jsdom', environment: 'jsdom',
@@ -9,5 +17,18 @@ export default defineConfig({
}, },
server: { server: {
port: 5173, port: 5173,
proxy: apiProxyPath
? {
[apiProxyPath]: {
target: 'http://localhost:8000',
changeOrigin: true,
rewrite: (path) =>
path.startsWith(apiProxyPath)
? path.slice(apiProxyPath.length) || '/'
: path,
}, },
}
: undefined,
},
};
}); });

View File

@@ -3,10 +3,15 @@ FROM jenkins/jenkins:lts
USER root USER root
RUN apt-get update \ RUN apt-get update \
&& apt-get install -y docker.io curl \ && apt-get install -y ca-certificates curl gnupg \
&& curl -L https://github.com/docker/compose/releases/download/v2.27.0/docker-compose-linux-x86_64 \ && install -m 0755 -d /etc/apt/keyrings \
-o /usr/local/bin/docker-compose \ && curl -fsSL https://download.docker.com/linux/debian/gpg \
&& chmod +x /usr/local/bin/docker-compose \ | gpg --dearmor -o /etc/apt/keyrings/docker.gpg \
&& chmod a+r /etc/apt/keyrings/docker.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian $(. /etc/os-release && echo \"$VERSION_CODENAME\") stable" \
> /etc/apt/sources.list.d/docker.list \
&& apt-get update \
&& apt-get install -y docker-ce-cli docker-compose-plugin \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
USER jenkins USER jenkins