7 Commits

Author SHA1 Message Date
e232b78366 Update
All checks were successful
OB/TallerCiCd/pipeline/pr-main This commit looks good
Tests / Declarative: Post Actions passed: 14
CI-Multi/pipeline/pr-main This commit looks good
2026-01-17 11:44:57 +01:00
512c1cea7b Merge pull request 'bugfix/main-16-PreventException' (#18) from bugfix/main-16-PreventException into main
Reviewed-on: #18
2025-12-28 11:35:40 +01:00
5845fed88f Ruff fix 2025-12-28 11:34:30 +01:00
938fd8170c Return error status 2025-12-28 11:34:01 +01:00
1f0c10b458 Merge pull request 'bugfix/main-13-UpdateConfigurations' (#17) from bugfix/main-13-UpdateConfigurations into main
Reviewed-on: #17
2025-12-28 11:15:36 +01:00
b15630c7ea Delete trigger 2025-12-28 11:05:24 +01:00
bc044a10c9 Change base images 2025-12-28 11:01:55 +01:00
8 changed files with 36 additions and 72 deletions

View File

@@ -34,14 +34,6 @@ pipeline {
PYTHONDONTWRITEBYTECODE = 1 PYTHONDONTWRITEBYTECODE = 1
} }
triggers {
gitea(
branchFilterType: 'Include',
branchFilter: 'main',
secret: ''
)
}
stages { stages {
/* ========================= /* =========================

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

@@ -1,44 +0,0 @@
{
"builds": [
{
"number": 205,
"status": "success",
"branch": "main",
"commit": "9ac3f91",
"author": "Miau",
"finished_at": "2024-05-04T10:20:00Z",
"duration_seconds": 312
},
{
"number": 204,
"status": "failed",
"branch": "feature/nosetioestoesunmock",
"commit": "75c4ba2",
"author": "Miau",
"finished_at": "2024-05-04T09:50:00Z",
"duration_seconds": 188,
"failed_stage": "tests",
"fun_message": "woops"
},
{
"number": 203,
"status": "failed",
"branch": "main",
"commit": "512ca7e",
"author": "Miau",
"finished_at": "2024-05-04T09:10:00Z",
"duration_seconds": 140,
"failed_stage": "lint",
"fun_message": "Nadie pasa en local el linter"
},
{
"number": 202,
"status": "success",
"branch": "hotfix/tehedichoqueestoesunmock?",
"commit": "c73d8ab",
"author": "Miau",
"finished_at": "2024-05-03T18:30:00Z",
"duration_seconds": 276
}
]
}

View File

@@ -16,8 +16,10 @@
import time import time
from fastapi import FastAPI import requests
from fastapi import FastAPI, status
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from app.services.builds import build_history from app.services.builds import build_history
from app.services.menu import build_menu from app.services.menu import build_menu
@@ -76,4 +78,10 @@ def price_for_item(item: str):
@app.get("/builds") @app.get("/builds")
def builds(): def builds():
return build_history() try:
return build_history()
except (requests.RequestException, ValueError):
return JSONResponse(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
content={"builds": [], "error": "jenkins_unavailable"},
)

View File

@@ -15,26 +15,17 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
import base64 import base64
import json
from pathlib import Path
from typing import Dict, List from typing import Dict, List
import requests import requests
from app.settings import settings from app.settings import settings
DATA_DIR = Path(__file__).resolve().parent.parent / "data"
def _load_json(filename: str) -> Dict:
path = DATA_DIR / filename
with open(path, encoding="utf-8") as file:
return json.load(file)
def _sort_builds(builds: List[Dict]) -> List[Dict]: def _sort_builds(builds: List[Dict]) -> List[Dict]:
return sorted(builds, key=lambda build: build.get("number", 0), reverse=True) return sorted(builds, key=lambda build: build.get("number", 0), reverse=True)
def normalize_build(build: Dict) -> Dict: def normalize_build(build: Dict) -> Dict:
changes = build.get("changeSets", []) changes = build.get("changeSets", [])
commits = [] commits = []
@@ -58,11 +49,16 @@ def normalize_build(build: Dict) -> Dict:
def _auth_header() -> Dict[str, str]: def _auth_header() -> Dict[str, str]:
if not settings.jenkins_user or not settings.jenkins_token:
return {}
token = f"{settings.jenkins_user}:{settings.jenkins_token}" token = f"{settings.jenkins_user}:{settings.jenkins_token}"
encoded = base64.b64encode(token.encode()).decode() encoded = base64.b64encode(token.encode()).decode()
return {"Authorization": f"Basic {encoded}"} return {"Authorization": f"Basic {encoded}"}
def fetch_builds(limit: int = 5) -> List[Dict]: def fetch_builds(limit: int = 5) -> List[Dict]:
if not settings.jenkins_job_name:
raise ValueError("JENKINS_JOB_NAME not configured")
url = ( url = (
f"{settings.jenkins_base_url}/job/{settings.jenkins_job_name}/api/json" f"{settings.jenkins_base_url}/job/{settings.jenkins_job_name}/api/json"
"?tree=builds[number,url,result,timestamp,duration," "?tree=builds[number,url,result,timestamp,duration,"
@@ -78,6 +74,4 @@ def fetch_builds(limit: int = 5) -> List[Dict]:
def build_history() -> Dict: def build_history() -> Dict:
"""Return Jenkins build history data.""" """Return Jenkins build history data."""
builds = fetch_builds() builds = fetch_builds()
return { return {"builds": [normalize_build(b) for b in builds]}
"builds": [normalize_build(b) for b in builds]
}

View File

@@ -46,5 +46,5 @@ def random_price(item: str) -> Dict:
def prices_payload() -> Dict: def prices_payload() -> Dict:
return { return {
"items": [random_price(item) for item in PRICE_RANGES.keys()], "items": [random_price(item) for item in PRICE_RANGES.keys()],
"disclaimer": "Depende de como pilles al de cafete.", "disclaimer": "Este es un cambio menor a fin de probar Webhooks.",
} }

View File

@@ -14,6 +14,7 @@
# You should have received a copy of the GNU General Public License # 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/>.
import requests
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from app.main import app from app.main import app
@@ -106,3 +107,16 @@ def test_build_history(monkeypatch):
assert second["status"] == "running" assert second["status"] == "running"
assert second["duration_seconds"] == 1 assert second["duration_seconds"] == 1
assert second["commits"] == [] assert second["commits"] == []
def test_build_history_error_returns_empty(monkeypatch):
def raise_error(limit=5):
raise requests.RequestException("boom")
monkeypatch.setattr("app.services.builds.fetch_builds", raise_error)
response = client.get("/builds")
assert response.status_code == 503
body = response.json()
assert body["builds"] == []
assert body["error"] == "jenkins_unavailable"

View File

@@ -1,4 +1,4 @@
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 ARG VITE_API_BASE
@@ -7,7 +7,7 @@ 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/taller COPY --from=build /app/dist /usr/share/nginx/html/taller
EXPOSE 8081 EXPOSE 8081