Skip to content

Commit 108b798

Browse files
authored
Merge branch 'master' into add-flags
2 parents 2625ef2 + 2c8097b commit 108b798

11 files changed

Lines changed: 944 additions & 84 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ jobs:
4646
uses: actions/checkout@v6
4747

4848
- name: 🏗️ Setup Terraform
49-
uses: hashicorp/setup-terraform@v3
49+
uses: hashicorp/setup-terraform@v4
5050

5151
- name: 🔍 Terraform Format
5252
run: terraform fmt -check -recursive

app/clients/gcloud_client.py

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,14 @@ def _get_template_name(self, template_name):
5555
except Exception:
5656
return None
5757

58-
def create_runner_instance(self, registration_token, repo_url, template_name, instance_label=None):
58+
def create_runner_instance(
59+
self,
60+
registration_token,
61+
repo_url,
62+
template_name,
63+
instance_label=None,
64+
delivery_id=None,
65+
):
5966
"""
6067
Create a new GCE instance for a GitHub Actions runner.
6168
@@ -64,16 +71,26 @@ def create_runner_instance(self, registration_token, repo_url, template_name, in
6471
repo_url (str): The URL of the repository or organization.
6572
template_name (str): The name of the instance template to use.
6673
instance_label (str): Label to add to the Instance for Cost Tracking.
74+
delivery_id (str): The GitHub webhook delivery ID for log correlation.
6775
6876
Returns:
6977
str: The name of the created instance.
7078
"""
7179
instance_template_resource = self._get_template_name(template_name)
7280
if instance_template_resource:
73-
logger.info(f"Found matching instance template: {instance_template_resource.name}")
81+
logger.info(
82+
"Found matching instance template: %s, delivery_id: %s",
83+
instance_template_resource.name,
84+
delivery_id,
85+
)
7486
else:
75-
logger.warning(f"No matching instance template found for label '{template_name}' in region {self.region}. "
76-
"Skipping instance creation.")
87+
logger.warning(
88+
"No matching instance template found for label '%s' in region %s. "
89+
"Skipping instance creation. delivery_id: %s",
90+
template_name,
91+
self.region,
92+
delivery_id,
93+
)
7794
return None
7895

7996
# Name must start with a lowercase letter followed by up to 62 lowercase letters,
@@ -84,7 +101,12 @@ def create_runner_instance(self, registration_token, repo_url, template_name, in
84101
else:
85102
instance_name = f"gcp-runner-{instance_uuid}"
86103

87-
logger.info(f"Creating GCE instance {instance_name} with template {instance_template_resource.self_link}")
104+
logger.info(
105+
"Creating GCE instance %s with template %s, delivery_id: %s",
106+
instance_name,
107+
instance_template_resource.self_link,
108+
delivery_id,
109+
)
88110

89111
# Set instance name
90112
instance_resource = compute_v1.Instance() # google.cloud.compute_v1.types.Instance
@@ -135,30 +157,46 @@ def create_runner_instance(self, registration_token, repo_url, template_name, in
135157

136158
try:
137159
# https://docs.cloud.google.com/compute/docs/reference/rest/v1/instances/insert
138-
operation = self.instance_client.insert(
139-
request=request
160+
operation = self.instance_client.insert(request=request)
161+
logger.info(
162+
"Instance creation operation started: %s, delivery_id: %s",
163+
operation.name,
164+
delivery_id,
140165
)
141-
logger.info(f"Instance creation operation started: {operation.name}")
142166
return instance_name
143167
except Exception as e:
144-
logger.error(f"Failed to create instance: {e}")
168+
logger.error(
169+
"Failed to create instance: %s, delivery_id: %s", e, delivery_id
170+
)
145171
raise
146172

147-
def delete_runner_instance(self, instance_name):
173+
def delete_runner_instance(self, instance_name, delivery_id=None):
148174
"""
149175
Delete a GCE instance.
150176
151177
Args:
152178
instance_name (str): The name of the instance to delete.
179+
delivery_id (str): The GitHub webhook delivery ID for log correlation.
153180
"""
154-
logger.info(f"Deleting GCE instance {instance_name}")
181+
logger.info(
182+
"Deleting GCE instance %s, delivery_id: %s", instance_name, delivery_id
183+
)
155184
try:
156185
operation = self.instance_client.delete(
157186
project=self.project_id,
158187
zone=self.zone,
159188
instance=instance_name
160189
)
161-
logger.info(f"Instance deletion operation started: {operation.name}")
190+
logger.info(
191+
"Instance deletion operation started: %s, delivery_id: %s",
192+
operation.name,
193+
delivery_id,
194+
)
162195
except Exception as e:
163-
logger.error(f"Failed to delete instance {instance_name}: {e}")
196+
logger.error(
197+
"Failed to delete instance %s: %s, delivery_id: %s",
198+
instance_name,
199+
e,
200+
delivery_id,
201+
)
164202
raise

app/clients/github_client.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def get_installation_access_token(self):
7979
# The installation access token will expire after 1 hour.
8080
return response.json()['token']
8181

82-
def get_registration_token(self, org_name=None, repo_name=None):
82+
def get_registration_token(self, org_name=None, repo_name=None, delivery_id=None):
8383
"""Gets a runner registration token."""
8484
# https://docs.github.com/en/rest/actions/self-hosted-runners
8585
token = self.get_installation_access_token()
@@ -90,12 +90,20 @@ def get_registration_token(self, org_name=None, repo_name=None):
9090
}
9191
if org_name:
9292
# GitHub Docs: https://t.ly/dAyGK
93-
url = f'https://api.github.com/orgs/{org_name}/actions/runners/registration-token'
94-
logger.info(f"Create registration token for organization: {org_name}")
93+
url = f"https://api.github.com/orgs/{org_name}/actions/runners/registration-token"
94+
logger.info(
95+
"Create registration token for organization: %s, delivery_id: %s",
96+
org_name,
97+
delivery_id,
98+
)
9599
elif repo_name:
96100
# GitHub Docs: https://t.ly/n0w2a
97-
url = f'https://api.github.com/repos/{repo_name}/actions/runners/registration-token'
98-
logger.info(f"Create registration token for repository: {repo_name}")
101+
url = f"https://api.github.com/repos/{repo_name}/actions/runners/registration-token"
102+
logger.info(
103+
"Create registration token for repository: %s, delivery_id: %s",
104+
repo_name,
105+
delivery_id,
106+
)
99107
else:
100108
raise ValueError("Either org_name or repo_name must be provided")
101109

app/routes/webhook.py

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@ def webhook():
1818
"""Handle incoming GitHub webhook events."""
1919
# https://docs.github.com/en/webhooks/webhook-events-and-payloads
2020
event_type = request.headers.get('X-GitHub-Event')
21+
# https://docs.github.com/en/webhooks/webhook-events-and-payloads#delivery-headers
22+
delivery_id = request.headers.get('X-GitHub-Delivery')
2123

2224
# Validate event type
2325
if not event_type or not isinstance(event_type, str):
2426
logger.error("Missing or invalid X-GitHub-Event header")
2527
return jsonify({'status': 'error', 'message': 'Invalid event type'}), 400
2628

27-
logger.info("Received webhook event: %s", event_type)
29+
logger.info("Received webhook event: %s, delivery_id: %s", event_type, delivery_id)
2830

2931
# Handle ping event
3032
if event_type == 'ping':
@@ -34,36 +36,72 @@ def webhook():
3436
signature = request.headers.get('X-Hub-Signature-256')
3537

3638
if not verify_github_signature(request.data, signature):
37-
logger.error("GitHub webhook signature not successfully verified! Ignoring webhook event.")
39+
logger.error(
40+
"GitHub webhook signature not successfully verified! "
41+
"Ignoring webhook event. delivery_id: %s",
42+
delivery_id,
43+
)
3844
return jsonify({'status': 'forbidden', 'message': 'Invalid signature'}), 403
3945

4046
# Validate JSON payload
4147
try:
4248
payload = request.json
4349
if not payload:
44-
logger.error("Empty or invalid JSON payload")
50+
logger.error("Empty or invalid JSON payload, delivery_id: %s", delivery_id)
4551
return jsonify({'status': 'error', 'message': 'Invalid JSON payload'}), 400
4652
except Exception as e:
47-
logger.error("Failed to parse JSON payload: %s", str(e))
53+
logger.error(
54+
"Failed to parse JSON payload: %s, delivery_id: %s",
55+
str(e),
56+
delivery_id,
57+
)
4858
return jsonify({'status': 'error', 'message': 'Invalid JSON'}), 400
4959

5060
# https://docs.github.com/en/webhooks/webhook-events-and-payloads#workflow_job
5161
if event_type == 'workflow_job':
52-
return handle_workflow_job_event(payload)
62+
return handle_workflow_job_event(payload, delivery_id)
5363
else:
54-
logger.warning("Received unknown event type: %s", event_type)
64+
logger.warning(
65+
"Received unknown event type: %s, delivery_id: %s",
66+
event_type,
67+
delivery_id,
68+
)
5569
return jsonify({'status': 'ignored'}), 200
5670

5771

58-
def handle_workflow_job_event(payload):
72+
def handle_workflow_job_event(payload, delivery_id=None):
5973
"""Handle workflow_job event."""
6074
try:
6175
webhook_service = WebhookService()
62-
webhook_service.handle_workflow_job(payload)
63-
return jsonify({'status': 'success'}), 200
76+
result = webhook_service.handle_workflow_job(payload, delivery_id=delivery_id)
77+
logger.info(
78+
"Webhook processed successfully, action: %s, runner_name: %s, "
79+
"delivery_id: %s",
80+
result.get("action"),
81+
result.get("runner_name"),
82+
delivery_id,
83+
)
84+
return (
85+
jsonify(
86+
{
87+
'status': 'success',
88+
'action': result.get('action'),
89+
'runner_name': result.get('runner_name'),
90+
}
91+
),
92+
200,
93+
)
6494
except ValueError as e:
65-
logger.error("[Webhook] Validation error: %s", str(e))
95+
logger.error(
96+
"[Webhook] Validation error: %s, delivery_id: %s",
97+
str(e),
98+
delivery_id,
99+
)
66100
return jsonify({'status': 'error', 'message': 'Invalid payload'}), 400
67101
except Exception as e:
68-
logger.error("[Webhook] Error handling webhook: %s", str(e))
102+
logger.error(
103+
"[Webhook] Error handling webhook: %s, delivery_id: %s",
104+
str(e),
105+
delivery_id,
106+
)
69107
return jsonify({'status': 'error', 'message': 'Internal error'}), 500

0 commit comments

Comments
 (0)