Skip to content

Commit 181539d

Browse files
committed
Fix gh --paginate mode; cache workflow files downloading
1 parent 2579bab commit 181539d

File tree

4 files changed

+49
-22
lines changed

4 files changed

+49
-22
lines changed

docker/ci-scaler/Dockerfile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ ENV DEBIAN_FRONTEND=noninteractive
1111
RUN true \
1212
&& apt-get update -y \
1313
&& apt-get install -y --no-install-recommends \
14-
awscli jq gh rsync python3 python3-yaml rsyslog systemctl tzdata gosu less mc git curl wget pv psmisc unzip vim nano telnet net-tools apt-transport-https ca-certificates locales gnupg lsb-release \
14+
awscli jq rsync python3 python3-yaml rsyslog systemctl tzdata gosu less mc git curl wget pv psmisc unzip vim nano telnet net-tools apt-transport-https ca-certificates locales gnupg lsb-release \
15+
&& curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | gpg --dearmor -o /usr/share/keyrings/githubcli-archive-keyring.gpg \
16+
&& echo "deb [signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" > /etc/apt/sources.list.d/github-cli.list \
17+
&& apt-get update -y \
18+
&& apt-get install -y --no-install-recommends gh \
1519
&& useradd -m guest -s /bin/bash \
1620
&& mkdir ~guest/.ssh && chmod 700 ~guest/.ssh \
1721
&& chown -R guest:guest ~guest

docker/ci-scaler/guest/scaler/api_gh.py

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22
import hashlib
33
import json
44
import os
5+
import random
6+
import shlex
57
import subprocess
68
import time
9+
import traceback
710
import yaml
811
from helpers import Runner, RateLimits, check_output
912
from typing import Any, cast
@@ -20,26 +23,35 @@ def gh_api(
2023
*args: str,
2124
input: Any | None = None,
2225
) -> Any:
23-
args = ("-H", "Accept: application/vnd.github.v3+json", *args)
24-
res = gh(
26+
args = (
2527
"api",
2628
*(["--input=-"] if input else []),
29+
"-H",
30+
"Accept: application/vnd.github.v3+json",
2731
*args,
28-
input=json.dumps(input) if input is not None else None,
2932
)
30-
return json.loads(res.strip()) if res != "" else None
33+
res = None
34+
try:
35+
res = gh(
36+
*args,
37+
input=json.dumps(input) if input is not None else None,
38+
).strip()
39+
return json.loads(res) if res else None
40+
except Exception:
41+
with open(f"/tmp/gh_api_error.{random.randint(0, 9)}.txt", "w") as f:
42+
f.write(f"$ gh {shlex.join(args).strip()}\n")
43+
f.write(f"{traceback.format_exc().rstrip()}\n")
44+
f.write(f"{res}\n")
45+
raise
3146

3247

3348
def gh_fetch_runners(
3449
*,
3550
repository: str,
3651
) -> list[Runner]:
37-
res = gh_api(f"repos/{repository}/actions/runners", "--paginate")
38-
if not isinstance(res, dict):
39-
raise ValueError(f"gh api returned a non-object: {res}")
40-
runners = cast(dict[str, Any], res).get("runners")
41-
if not isinstance(runners, list):
42-
raise ValueError(f'gh api returned a response with no "runners" key: {res}')
52+
res = gh_api(f"repos/{repository}/actions/runners", "--paginate", "--slurp")
53+
if not isinstance(res, list) or not res:
54+
raise ValueError(f"gh api returned a non-list of pages: {res}")
4355
return [
4456
Runner(
4557
id=str(runner["id"]),
@@ -53,7 +65,8 @@ def gh_fetch_runners(
5365
],
5466
loaded_at=int(time.time()),
5567
)
56-
for runner in cast(list[dict[str, Any]], runners)
68+
for page in cast(list[dict[str, Any]], res)
69+
for runner in cast(list[dict[str, Any]], page["runners"])
5770
]
5871

5972

@@ -114,7 +127,6 @@ def gh_webhook_ensure_absent(
114127
def gh_webhook_get_id(*, repository: str, url: str) -> str | None:
115128
ids: list[str] = gh_api(
116129
f"/repos/{repository}/hooks",
117-
"--paginate",
118130
"--jq",
119131
f"[.[] | select(.config.url=={json.dumps(url)}) | .id]",
120132
)

docker/ci-scaler/guest/scaler/handler_webhooks.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@
3030

3131

3232
DUPLICATED_EVENTS_TTL = 3600
33-
JOB_TIMING_TTL = 7200 # 2 hours to track job timing data
33+
JOB_TIMING_TTL = 3600 * 2
34+
WORKFLOW_TTL = 60 * 5
3435
WORKFLOW_RUN_EVENT = "workflow_run"
3536
WORKFLOW_JOB_EVENT = "workflow_job"
3637
IGNORE_KEYS = [
@@ -78,6 +79,7 @@ def __init__(self, *, domain: str, asg_specs: list[AsgSpec]):
7879
ttl=DUPLICATED_EVENTS_TTL
7980
)
8081
self.job_timings = ExpiringDict[int, JobTiming](ttl=JOB_TIMING_TTL)
82+
self.workflows = ExpiringDict[str, dict[str, Any]](ttl=WORKFLOW_TTL)
8183
this = self
8284

8385
class RequestHandler(PostJsonHttpRequestHandler):
@@ -225,15 +227,24 @@ def handle(
225227

226228
head_sha = str(run_payload["head_sha"])
227229
path = str(run_payload["path"])
228-
message = f"{repository}: downloading {os.path.basename(path)} and parsing jobs list..."
230+
message = f"{repository}{event_key}: downloading {os.path.basename(path)} and parsing jobs list"
229231
try:
230-
workflow = gh_fetch_workflow(
231-
repository=repository,
232-
sha=head_sha,
233-
path=path,
234-
)
232+
cache_key = f"{repository}:{path}"
233+
workflow = self.workflows.get(cache_key, None)
234+
if not workflow:
235+
workflow = gh_fetch_workflow(
236+
repository=repository,
237+
sha=head_sha,
238+
path=path,
239+
)
240+
self.workflows[cache_key] = workflow
241+
else:
242+
message += f" (cached)"
235243
labels = gh_predict_workflow_labels(workflow=workflow)
236-
log(f"{message} " + " ".join([f"{k}:+{v}" for k, v in labels.items()]))
244+
log(
245+
f"{message}... "
246+
+ " ".join([f"{k}:+{v}" for k, v in labels.items()])
247+
)
237248
except Exception as e:
238249
return handler.send_error(500, f"{message} failed: {e}")
239250

docker/ci-scaler/guest/scaler/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def main():
6969
parser.add_argument(
7070
"--poll-interval-sec",
7171
type=int,
72-
default=10,
72+
default=30,
7373
help="poll for the list of runners that often; it also determines the interval for publishing CloudWatch metrics",
7474
)
7575
parser.add_argument(

0 commit comments

Comments
 (0)