Commit 9dd3f26ae5
9dd3f26ae58c3dd2da05cbe2ae8e9a6ce13e14c3
parent: d206b14ff8
Verified · cmc ci/build: success ci/lint: success ci/test: success
cmc <hello@cleberg.net> · 2026-09-11 14:38 UTC
Check listed instances daily
scripts/check-instances.py fetches /api/instance from every clearnet
instance in instances.json and exits 1 if any is down; a scheduled
gitbay job runs it each morning.
Closes #32
Layout: unified · split
.gitbay/ci.yml
+7
| @@ -17,3 +17,10 @@ jobs: |
| 17 | image: localhost/gitbay-ci:2 |
17 | image: localhost/gitbay-ci:2 |
| 18 | steps: |
18 | steps: |
| 19 | - go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.13.2 run ./... |
19 | - go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.13.2 run ./... |
| |
20 | # Daily check that every listed instance answers, so INSTANCES.md does not |
| |
21 | # keep dead links. `build trigger krz/skunky-art instances` runs it now. |
| |
22 | instances: |
| |
23 | image: localhost/gitbay-ci:2 |
| |
24 | schedule: "0 6 * * *" |
| |
25 | steps: |
| |
26 | - python3 scripts/check-instances.py |
README.org
+2 −1
| @@ -85,5 +85,6 @@ go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.13.2 run ./... |
| 85 | ci runs both on every push. the roadmap is [[file:ROADMAP.md][ROADMAP.md]]. |
85 | ci runs both on every push. the roadmap is [[file:ROADMAP.md][ROADMAP.md]]. |
| 86 | |
86 | |
| 87 | * instances |
87 | * instances |
| 88 | add yours by merge request to [[file:instances.json][instances.json]] and [[file:INSTANCES.md][INSTANCES.md]] (or use |
88 | instances are checked daily by ci (~scripts/check-instances.py~); a dead one |
| |
89 | turns the build red. add yours by merge request to [[file:instances.json][instances.json]] and [[file:INSTANCES.md][INSTANCES.md]] (or use |
| 89 | ~--add-instance~ to write both), or open an issue. |
90 | ~--add-instance~ to write both), or open an issue. |
scripts/check-instances.py
added
+55
| @@ -0,0 +1,55 @@ |
| |
1 | #!/usr/bin/env python3 |
| |
2 | """Check every clearnet instance in instances.json. |
| |
3 | |
| |
4 | Fetches /api/instance from each and prints one line per instance. Exits 1 if |
| |
5 | any is down, so a scheduled CI job turns red and INSTANCES.md does not keep |
| |
6 | listing dead links. Tor, I2P and Yggdrasil addresses are skipped: the runner |
| |
7 | cannot reach them. |
| |
8 | """ |
| |
9 | |
| |
10 | import json |
| |
11 | import sys |
| |
12 | import urllib.error |
| |
13 | import urllib.request |
| |
14 | |
| |
15 | TIMEOUT = 15 |
| |
16 | |
| |
17 | |
| |
18 | def check(url): |
| |
19 | req = urllib.request.Request( |
| |
20 | url.rstrip("/") + "/api/instance", |
| |
21 | headers={"User-Agent": "skunkyart-instance-check"}, |
| |
22 | ) |
| |
23 | try: |
| |
24 | with urllib.request.urlopen(req, timeout=TIMEOUT) as resp: |
| |
25 | info = json.load(resp) |
| |
26 | except (urllib.error.URLError, json.JSONDecodeError, TimeoutError) as err: |
| |
27 | return None, str(err) |
| |
28 | return info.get("version", "?"), None |
| |
29 | |
| |
30 | |
| |
31 | def main(): |
| |
32 | with open("instances.json", encoding="utf-8") as f: |
| |
33 | instances = json.load(f)["instances"] |
| |
34 | |
| |
35 | failed = 0 |
| |
36 | for inst in instances: |
| |
37 | url = inst.get("urls", {}).get("clearnet") |
| |
38 | if not url: |
| |
39 | print(f"{inst['title']}: no clearnet url, skipped") |
| |
40 | continue |
| |
41 | version, err = check(url) |
| |
42 | if err: |
| |
43 | failed += 1 |
| |
44 | print(f"{inst['title']}: DOWN {url} ({err})") |
| |
45 | else: |
| |
46 | print(f"{inst['title']}: ok {url} v{version}") |
| |
47 | |
| |
48 | if failed: |
| |
49 | print(f"{failed} instance(s) down") |
| |
50 | return 1 |
| |
51 | return 0 |
| |
52 | |
| |
53 | |
| |
54 | if __name__ == "__main__": |
| |
55 | sys.exit(main()) |