Skip to content

Commit 00a4ba7

Browse files
committed
Update workflow
1 parent 0840aae commit 00a4ba7

File tree

3 files changed

+201
-2
lines changed

3 files changed

+201
-2
lines changed

.github/models/trafic.schema.json

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"title": "Trafic configuration schema",
4+
"description": "Schema describing the structure of a trafic.json file used by the project.",
5+
"type": "object",
6+
"required": ["companyId", "companyLogo", "lines"],
7+
"additionalProperties": false,
8+
"properties": {
9+
"companyId": {
10+
"type": "string",
11+
"description": "Unique identifier for the company (e.g. 'fr-idf')."
12+
},
13+
"companyLogo": {
14+
"type": "string",
15+
"format": "uri",
16+
"description": "URL to the company logo (absolute URL)."
17+
},
18+
"gtfsRTUrl": {
19+
"type": ["string", "null"],
20+
"format": "uri",
21+
"description": "Optional GTFS-RT URL for real-time alerts. Can be null or omitted.",
22+
"default": null
23+
},
24+
"lines": {
25+
"type": "array",
26+
"description": "Array of transport groups. Each group is an array where the first item is the transport header and the following items are line objects. Example: [[{transportLogo: \"...\"}, {lineId: \"...\", lineName: \"...\"}, ...], [...]]",
27+
"items": {
28+
"type": "array",
29+
"minItems": 1,
30+
"items": {
31+
"type": "object",
32+
"oneOf": [
33+
{
34+
"title": "Transport header",
35+
"required": ["transportLogo"],
36+
"properties": {
37+
"transportLogo": {
38+
"type": "string",
39+
"description": "Relative path to an icon used as transport mode header (e.g. 'assets/icons/transportMode/metro_white.png')."
40+
}
41+
},
42+
"additionalProperties": true
43+
},
44+
{
45+
"title": "Line object",
46+
"required": ["lineName", "lineLogo"],
47+
"properties": {
48+
"lineId": { "type": "string", "description": "GTFS or internal line identifier; may be empty string when not available." },
49+
"lineName": { "type": "string", "description": "Human-readable line name (e.g. 'Métro 1')." },
50+
"lineLogo": { "type": "string", "format": "uri", "description": "URL to the line logo image." },
51+
"lineLogoDark": { "type": ["string","null"], "format": "uri", "description": "Optional dark-mode logo URL.", "default": null },
52+
"isDisabled": { "type": "boolean", "description": "Optional flag to mark a line as disabled." }
53+
},
54+
"additionalProperties": false
55+
}
56+
]
57+
}
58+
}
59+
}
60+
},
61+
"examples": [
62+
{
63+
"companyId": "fr-idf",
64+
"companyLogo": "https://hexatransit.fr/assets/logo/fr-idf/idfm.png",
65+
"gtfsRTUrl": "http://gtfsidfm.example/gtfs-rt-alerts",
66+
"lines": [
67+
[
68+
{ "transportLogo": "assets/icons/transportMode/metro_white.png" },
69+
{ "lineId": "IDFM:C01371", "lineName": "Métro 1", "lineLogo": "https://.../metro_1.png" },
70+
{ "lineId": "", "lineName": "Métro 15", "lineLogo": "https://.../metro_15.png", "isDisabled": true }
71+
],
72+
[
73+
{ "transportLogo": "assets/icons/transportMode/tram_white.png" },
74+
{ "lineId": "IDFM:C01389", "lineName": "Tramway T1", "lineLogo": "https://.../tram_1.png", "lineLogoDark": "https://.../tram_1_dark.png" }
75+
]
76+
]
77+
}
78+
]
79+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
#!/usr/bin/env python3
2+
"""Validate trafic.json files under `logo/` against the JSON Schema in `.github/models/trafic.schema.json`.
3+
4+
Usage:
5+
python .github/scripts/check_structure_trafic.py [--schema PATH] [--logo-dir PATH]
6+
7+
Exits with code 0 when all files are valid, otherwise exits with 1.
8+
"""
9+
from __future__ import annotations
10+
11+
import argparse
12+
import json
13+
import sys
14+
from pathlib import Path
15+
from typing import List
16+
17+
try:
18+
import jsonschema
19+
from jsonschema import Draft7Validator, FormatChecker
20+
except Exception: # pragma: no cover - helpful message when module missing
21+
print("ERROR: the 'jsonschema' package is required. Install with: pip install jsonschema", file=sys.stderr)
22+
raise
23+
24+
25+
def find_trafic_files(root: Path) -> List[Path]:
26+
"""Return list of paths named 'trafic.json' under root/logo (recursively)."""
27+
logo_dir = root / "logo"
28+
if not logo_dir.exists():
29+
return []
30+
return list(logo_dir.rglob("trafic.json"))
31+
32+
33+
def load_json(path: Path):
34+
with path.open("r", encoding="utf-8") as fh:
35+
return json.load(fh)
36+
37+
38+
def validate_instance(schema: dict, instance: dict, instance_path: Path) -> List[str]:
39+
validator = Draft7Validator(schema, format_checker=FormatChecker())
40+
errors = []
41+
for err in sorted(validator.iter_errors(instance), key=lambda e: e.path):
42+
# build a readable location
43+
location = "".join(f"/{p}" for p in err.absolute_path) or "/"
44+
errors.append(f"{instance_path}: {location} -> {err.message}")
45+
return errors
46+
47+
48+
def main(argv: List[str] | None = None) -> int:
49+
parser = argparse.ArgumentParser(prog="check_structure_trafic.py", description="Validate trafic.json files against JSON Schema")
50+
parser.add_argument("--schema", "-s", type=Path, default=Path(__file__).resolve().parents[2] / ".github" / "models" / "trafic.schema.json", help="Path to trafic.schema.json")
51+
parser.add_argument("--root", "-r", type=Path, default=Path(__file__).resolve().parents[2], help="Repository root (default: repo root)")
52+
parser.add_argument("--logo-dir", type=Path, help="Optional explicit logo directory (overrides --root/logo)")
53+
parser.add_argument("--quiet", "-q", action="store_true", help="Only print errors and exit code")
54+
args = parser.parse_args(argv)
55+
56+
schema_path = args.schema
57+
repo_root = args.root
58+
if args.logo_dir:
59+
logo_root = args.logo_dir
60+
else:
61+
logo_root = repo_root / "logo"
62+
63+
if not schema_path.exists():
64+
print(f"ERROR: schema file not found at {schema_path}", file=sys.stderr)
65+
return 2
66+
67+
try:
68+
schema = load_json(schema_path)
69+
except Exception as exc:
70+
print(f"ERROR: failed to load schema {schema_path}: {exc}", file=sys.stderr)
71+
return 2
72+
73+
files = find_trafic_files(repo_root) if not args.logo_dir else list(Path(args.logo_dir).rglob("trafic.json"))
74+
if not files:
75+
print(f"No trafic.json files found under {logo_root}")
76+
return 0
77+
78+
total = 0
79+
invalid = 0
80+
all_errors: List[str] = []
81+
82+
for f in sorted(files):
83+
total += 1
84+
try:
85+
instance = load_json(f)
86+
except Exception as exc:
87+
invalid += 1
88+
all_errors.append(f"{f}: JSON parse error: {exc}")
89+
continue
90+
91+
errors = validate_instance(schema, instance, f)
92+
if errors:
93+
invalid += 1
94+
all_errors.extend(errors)
95+
else:
96+
if not args.quiet:
97+
print(f"OK: {f}")
98+
99+
if all_errors:
100+
print("\nValidation errors:")
101+
for e in all_errors:
102+
print(" -", e)
103+
104+
print(f"\nChecked {total} files: {total - invalid} valid, {invalid} invalid")
105+
return 1 if invalid else 0
106+
107+
108+
if __name__ == "__main__":
109+
raise SystemExit(main())

.github/workflows/deploy.yml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,21 @@ jobs:
2020
- name: Check logo paths existence
2121
run: python .github/scripts/check_logo_path_existence.py
2222

23+
architecture-check:
24+
name: Architecture check
25+
runs-on: ubuntu-latest
26+
needs: syntax-check
27+
steps:
28+
- name: Checkout repository
29+
uses: actions/checkout@v6
30+
31+
- name: Run architecture checks for trafic.json files
32+
run: python .github/scripts/check_structure_trafic.py
33+
2334
gtfs-check:
2435
name: GTFS routes check
2536
runs-on: ubuntu-latest
26-
needs: syntax-check
37+
needs: architecture-check
2738
steps:
2839
- name: Checkout repository
2940
uses: actions/checkout@v6
@@ -36,7 +47,7 @@ jobs:
3647

3748
deploy:
3849
if: github.ref == 'refs/heads/main'
39-
needs: [syntax-check, gtfs-check]
50+
needs: [syntax-check, architecture-check, gtfs-check]
4051
runs-on: ubuntu-latest
4152
steps:
4253
- name: Checkout repository

0 commit comments

Comments
 (0)