Compare commits
3 Commits
1f0c10b458
...
512c1cea7b
| Author | SHA1 | Date | |
|---|---|---|---|
| 512c1cea7b | |||
| 5845fed88f | |||
| 938fd8170c |
@@ -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
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -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"},
|
||||||
|
)
|
||||||
|
|||||||
@@ -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]
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user