Skip to content
This repository was archived by the owner on Mar 13, 2026. It is now read-only.

Commit 5d3b26f

Browse files
committed
Initial function workflow_job processing
1 parent fe7aa3e commit 5d3b26f

3 files changed

Lines changed: 106 additions & 0 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__pycache__/

app.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
from flask import Flask, request, abort
2+
3+
import logging
4+
from const import GithubHeaders
5+
from datetime import datetime, timedelta
6+
7+
_LOGGER = logging.getLogger(__name__)
8+
9+
app = Flask(__name__)
10+
11+
jobs = dict()
12+
13+
def parse_datetime(date: str) -> datetime:
14+
exp = "%Y-%m-%dT%H:%M:%SZ"
15+
return datetime.strptime(date, exp)
16+
17+
def validate_origin_github() -> bool:
18+
userAgent = request.headers.get("User-Agent")
19+
if not userAgent.startswith("GitHub-Hookshot"):
20+
_LOGGER.warning("User-Agent is {userAgent}")
21+
return False
22+
23+
if request.headers.get("Content-Type") != "application/json":
24+
_LOGGER.warning("Content is not JSON")
25+
return False
26+
27+
if not request.headers.get(GithubHeaders.EVENT.value):
28+
_LOGGER.warning("No GitHub Event received!")
29+
return False
30+
31+
return True
32+
33+
def process_workflow_job():
34+
job = request.get_json()
35+
36+
job_id = job["workflow_job"]["run_id"]
37+
name = job["workflow_job"]["workflow_name"]
38+
time_start = parse_datetime(job["workflow_job"]["started_at"])
39+
repository = job["repository"]["full_name"]
40+
action = job["action"]
41+
NOW = datetime.now()
42+
43+
if action == "queued":
44+
# add to memory as timestamp
45+
jobs[job_id] = int(time_start.timestamp())
46+
msg = f"{NOW} {action=} {repository=} workflow={name} {job_id=} {time_start=}"
47+
48+
elif action == "in_progress":
49+
job_requested = jobs.get(job_id, None)
50+
if not job_requested:
51+
_LOGGER.warning(f"Job {job_id} is {action} but not stored!")
52+
time_to_start = 0
53+
else:
54+
time_to_start = (
55+
time_start - datetime.fromtimestamp(job_requested)
56+
).seconds
57+
msg = f"{NOW} {action=} {repository=} workflow={name} {job_id=} {time_to_start=}"
58+
59+
elif action == "completed":
60+
job_requested = jobs.get(job_id, None)
61+
if not job_requested:
62+
_LOGGER.warning(f"Job {job_id} is {action} but not stored!")
63+
time_to_finish = 0
64+
else:
65+
time_to_finish = (
66+
time_start - datetime.fromtimestamp(job_requested)
67+
).seconds
68+
# delete from memory
69+
del jobs[job_id]
70+
71+
msg = f"{NOW} {action=} {repository=} workflow={name} {job_id=} {time_to_finish=}"
72+
73+
print(msg)
74+
75+
return True
76+
77+
@app.route("/github-webhook", methods=["POST"])
78+
def github_webhook_process():
79+
if not validate_origin_github():
80+
return abort(401)
81+
82+
event = request.headers.get(GithubHeaders.EVENT.value)
83+
command = f"process_{event}"
84+
85+
#if hasattr(command) and callable(getattr(command)):
86+
if command == "process_workflow_job":
87+
_LOGGER.debug(f"Calling function {command}")
88+
#response = getattr(command)()
89+
response = process_workflow_job()
90+
91+
if not response:
92+
_LOGGER.error(f"Error calling {event} function")
93+
return abort(500)
94+
return "OK"
95+
96+
_LOGGER.error(f"Unknown event type {event}, can't handle")
97+
return abort(405)

const.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# StrEnum only in Python 3.11
2+
from enum import Enum
3+
4+
class GithubHeaders(str, Enum):
5+
# NOTE: Flask manipulates as Capital-Word-Per-Section
6+
EVENT = "X-Github-Event"
7+
HOOK_ID = "X-Github-Hook-Id"
8+
DELIVERY = "X-Github-Delivery"

0 commit comments

Comments
 (0)