Merge branch 'feature/main-02-AddBuildHistory'
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
# CI/CD Workshop
|
||||
# Copyright (C) 2025 OpenBokeron
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
44
backend/app/data/build_history.json
Normal file
44
backend/app/data/build_history.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,8 +1,25 @@
|
||||
# CI/CD Workshop
|
||||
# Copyright (C) 2025 OpenBokeron
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import time
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
from app.services.builds import build_history
|
||||
from app.services.menu import build_menu
|
||||
from app.services.prices import prices_payload, random_price
|
||||
from app.settings import settings
|
||||
@@ -55,3 +72,8 @@ def prices():
|
||||
@app.get("/prices/{item}")
|
||||
def price_for_item(item: str):
|
||||
return random_price(item)
|
||||
|
||||
|
||||
@app.get("/builds")
|
||||
def builds():
|
||||
return build_history()
|
||||
|
||||
83
backend/app/services/builds.py
Normal file
83
backend/app/services/builds.py
Normal file
@@ -0,0 +1,83 @@
|
||||
# CI/CD Workshop
|
||||
# Copyright (C) 2025 OpenBokeron
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import base64
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Dict, List
|
||||
|
||||
import requests
|
||||
|
||||
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]:
|
||||
return sorted(builds, key=lambda build: build.get("number", 0), reverse=True)
|
||||
|
||||
def normalize_build(build: Dict) -> Dict:
|
||||
changes = build.get("changeSets", [])
|
||||
commits = []
|
||||
|
||||
for cs in changes:
|
||||
for item in cs.get("items", []):
|
||||
commits.append({
|
||||
"commit": item.get("commitId", "")[:7],
|
||||
"message": item.get("msg", ""),
|
||||
"author": item.get("author", {}).get("fullName", "unknown"),
|
||||
})
|
||||
|
||||
return {
|
||||
"number": build.get("number"),
|
||||
"status": (build.get("result") or "RUNNING").lower(),
|
||||
"finished_at": build.get("timestamp"),
|
||||
"duration_seconds": build.get("duration", 0) // 1000,
|
||||
"url": build.get("url"),
|
||||
"commits": commits,
|
||||
}
|
||||
|
||||
|
||||
def _auth_header() -> Dict[str, str]:
|
||||
token = f"{settings.jenkins_user}:{settings.jenkins_token}"
|
||||
encoded = base64.b64encode(token.encode()).decode()
|
||||
return {"Authorization": f"Basic {encoded}"}
|
||||
|
||||
def fetch_builds(limit: int = 5) -> List[Dict]:
|
||||
url = (
|
||||
f"{settings.jenkins_base_url}/job/{settings.jenkins_job_name}/api/json"
|
||||
"?tree=builds[number,url,result,timestamp,duration,"
|
||||
"changesets[items[commitId,msg,author[fullName]]]]"
|
||||
)
|
||||
|
||||
resp = requests.get(url, headers = _auth_header(), timeout=5)
|
||||
resp.raise_for_status()
|
||||
|
||||
builds = resp.json().get("builds", [])
|
||||
return builds[:limit]
|
||||
|
||||
def build_history() -> Dict:
|
||||
"""Return Jenkins build history data."""
|
||||
builds = fetch_builds()
|
||||
return {
|
||||
"builds": [normalize_build(b) for b in builds]
|
||||
}
|
||||
@@ -1,3 +1,19 @@
|
||||
# CI/CD Workshop
|
||||
# Copyright (C) 2025 OpenBokeron
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import json
|
||||
import random
|
||||
from datetime import datetime
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
# CI/CD Workshop
|
||||
# Copyright (C) 2025 OpenBokeron
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import json
|
||||
import random
|
||||
from datetime import datetime
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
# CI/CD Workshop
|
||||
# Copyright (C) 2025 OpenBokeron
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
|
||||
@@ -8,6 +24,10 @@ class RuntimeConfig:
|
||||
git_commit: str = os.getenv("GIT_COMMIT", "local")
|
||||
build_number: str = os.getenv("BUILD_NUMBER", "-")
|
||||
commit_author: str = os.getenv("COMMIT_AUTHOR", "local")
|
||||
jenkins_base_url: str = os.getenv("JENKINS_BASE_URL", "localhost:8080")
|
||||
jenkins_job_name: str = os.getenv("JENKINS_JOB_NAME", "")
|
||||
jenkins_user: str = os.getenv("JENKINS_USER", "")
|
||||
jenkins_token: str = os.getenv("JENKINS_TOKEN", "")
|
||||
|
||||
|
||||
settings = RuntimeConfig()
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
fastapi==0.111.0
|
||||
uvicorn[standard]==0.30.1
|
||||
requests==2.32.5
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
# CI/CD Workshop
|
||||
# Copyright (C) 2025 OpenBokeron
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.main import app
|
||||
@@ -34,3 +50,59 @@ def test_prices_random_list():
|
||||
first = items[0]
|
||||
assert "item" in first and "price" in first and "currency" in first
|
||||
assert all(item["price"] <= 3 for item in items)
|
||||
|
||||
|
||||
def test_build_history(monkeypatch):
|
||||
jenkins_builds = [
|
||||
{
|
||||
"number": 205,
|
||||
"result": "SUCCESS",
|
||||
"timestamp": 1719992400000,
|
||||
"duration": 75000,
|
||||
"url": "http://jenkins.local/job/demo/205",
|
||||
"changeSets": [
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"commitId": "9ac3f91d9bd0f0b7bfc5",
|
||||
"msg": "Anade la API de Jenkins",
|
||||
"author": {"fullName": "Dev One"},
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
"number": 204,
|
||||
"result": None,
|
||||
"timestamp": 1719988800000,
|
||||
"duration": 1500,
|
||||
"url": "http://jenkins.local/job/demo/204",
|
||||
"changeSets": [],
|
||||
},
|
||||
]
|
||||
|
||||
monkeypatch.setattr("app.services.builds.fetch_builds", lambda limit=5: jenkins_builds)
|
||||
|
||||
response = client.get("/builds")
|
||||
assert response.status_code == 200
|
||||
body = response.json()
|
||||
builds = body["builds"]
|
||||
assert isinstance(builds, list)
|
||||
assert len(builds) == 2
|
||||
|
||||
first = builds[0]
|
||||
assert first["number"] == 205
|
||||
assert first["status"] == "success"
|
||||
assert first["finished_at"] == 1719992400000
|
||||
assert first["duration_seconds"] == 75
|
||||
assert first["url"].endswith("/205")
|
||||
assert first["commits"] == [
|
||||
{"commit": "9ac3f91", "message": "Anade la API de Jenkins", "author": "Dev One"}
|
||||
]
|
||||
|
||||
second = builds[1]
|
||||
assert second["number"] == 204
|
||||
assert second["status"] == "running"
|
||||
assert second["duration_seconds"] == 1
|
||||
assert second["commits"] == []
|
||||
|
||||
Reference in New Issue
Block a user