Skip to content

Commit f49e795

Browse files
committed
chore(satellite): sync aimarket-protocol from aicom monorepo
Auto-generated by scripts/mirror_satellites.sh
1 parent 1d7600c commit f49e795

7 files changed

Lines changed: 211 additions & 2 deletions

File tree

.github/workflows/ci.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ permissions:
1313
jobs:
1414
validate:
1515
runs-on: ubuntu-latest
16+
permissions:
17+
contents: write
1618
steps:
1719
- uses: actions/checkout@v4
1820

@@ -38,3 +40,6 @@ jobs:
3840
- name: Assert package version matches VERSION file
3941
run: |
4042
test "$(cat VERSION)" = "0.1.0"
43+
44+
- name: Validation coverage badge
45+
run: bash scripts/ci_static_badge.sh validation ok

README.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,18 @@
77
88
# AIMarket Protocol v2 — Federation
99

10-
[![CI](https://github.com/alexar76/aimarket-protocol/actions/workflows/ci.yml/badge.svg)](https://github.com/alexar76/aimarket-protocol/actions/workflows/ci.yml)
11-
[![Release](https://img.shields.io/github/v/release/alexar76/aimarket-protocol?include_prereleases&label=release)](https://github.com/alexar76/aimarket-protocol/releases)
10+
<!-- aicom-readme-badges -->
11+
<p align="center">
12+
<a href="https://github.com/alexar76/aimarket-protocol/actions/workflows/ci.yml"><img src="https://github.com/alexar76/aimarket-protocol/actions/workflows/ci.yml/badge.svg" alt="CI" /></a>
13+
<a href="#testing--coverage"><img src="docs/badges/coverage.svg" alt="Test coverage" /></a>
14+
<a href="LICENSE"><img src="https://img.shields.io/badge/License-Apache--2.0-blue.svg" alt="License: Apache-2.0" /></a>
15+
</p>
16+
<!-- /aicom-readme-badges -->
17+
18+
19+
20+
21+
1222

1323
> **Ecosystem:** [AICOM overview & live demos](https://alexar76.github.io/aicom/) · **Package version:** `0.1.0` ([VERSION](VERSION))
1424

docs/badges/coverage.svg

Lines changed: 19 additions & 0 deletions
Loading

scripts/ci_coverage_badge.sh

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/usr/bin/env bash
2+
# Run pytest with coverage JSON + refresh docs/badges/coverage.svg; optional commit on main push.
3+
# Usage: scripts/ci_coverage_badge.sh [--] [pytest args...]
4+
set -euo pipefail
5+
6+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
7+
cd "$ROOT"
8+
9+
pip install -q pytest-cov
10+
11+
pytest_args=()
12+
if [[ "${1:-}" == "--" ]]; then
13+
shift
14+
fi
15+
if [[ $# -gt 0 ]]; then
16+
pytest_args=("$@")
17+
else
18+
pytest_args=(tests/ -q --tb=short --maxfail=5)
19+
fi
20+
21+
pytest "${pytest_args[@]}" --cov-report=json:coverage.json
22+
python scripts/generate_coverage_badge.py coverage.json docs/badges/coverage.svg
23+
24+
if [[ "${GITHUB_REF:-}" == "refs/heads/main" && "${GITHUB_EVENT_NAME:-}" == "push" ]]; then
25+
git config user.name "github-actions[bot]"
26+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
27+
git add docs/badges/coverage.svg
28+
git diff --cached --quiet && exit 0
29+
git commit -m "chore(ci): refresh coverage badge [skip ci]"
30+
git push
31+
fi

scripts/ci_static_badge.sh

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/usr/bin/env bash
2+
# Refresh docs/badges/coverage.svg for smoke / validation repos; optional commit on main push.
3+
set -euo pipefail
4+
5+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
6+
cd "$ROOT"
7+
8+
label="${1:-checks}"
9+
value="${2:-pass}"
10+
python scripts/generate_static_badge.py "$label" "$value" docs/badges/coverage.svg
11+
12+
if [[ "${GITHUB_REF:-}" == "refs/heads/main" && "${GITHUB_EVENT_NAME:-}" == "push" ]]; then
13+
git config user.name "github-actions[bot]"
14+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
15+
git add docs/badges/coverage.svg
16+
git diff --cached --quiet && exit 0
17+
git commit -m "chore(ci): refresh coverage badge [skip ci]"
18+
git push
19+
fi

scripts/generate_coverage_badge.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#!/usr/bin/env python3
2+
"""Write docs/badges/coverage.svg from coverage.json (pytest --cov-report=json)."""
3+
4+
from __future__ import annotations
5+
6+
import json
7+
import sys
8+
from pathlib import Path
9+
10+
ROOT = Path(__file__).resolve().parents[1]
11+
DEFAULT_IN = ROOT / "coverage.json"
12+
DEFAULT_OUT = ROOT / "docs" / "badges" / "coverage.svg"
13+
14+
15+
def _color(pct: float) -> str:
16+
if pct >= 80:
17+
return "#4c1"
18+
if pct >= 60:
19+
return "#97ca00"
20+
if pct >= 40:
21+
return "#dfb317"
22+
if pct >= 20:
23+
return "#fe7d37"
24+
return "#e05d44"
25+
26+
27+
def _svg(label: str, value: str, fill: str) -> str:
28+
lw = len(label) * 6.5 + 10
29+
vw = len(value) * 6.5 + 10
30+
tw = lw + vw
31+
return f"""<svg xmlns="http://www.w3.org/2000/svg" width="{tw:.0f}" height="20" role="img" aria-label="{label}: {value}">
32+
<title>{label}: {value}</title>
33+
<linearGradient id="s" x2="0" y2="100%">
34+
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
35+
<stop offset="1" stop-opacity=".1"/>
36+
</linearGradient>
37+
<clipPath id="r"><rect width="{tw:.0f}" height="20" rx="3" fill="#fff"/></clipPath>
38+
<g clip-path="url(#r)">
39+
<rect width="{lw:.0f}" height="20" fill="#555"/>
40+
<rect x="{lw:.0f}" width="{vw:.0f}" height="20" fill="{fill}"/>
41+
<rect width="{tw:.0f}" height="20" fill="url(#s)"/>
42+
</g>
43+
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
44+
<text x="{lw/2:.1f}" y="14" fill="#010101" fill-opacity=".3">{label}</text>
45+
<text x="{lw/2:.1f}" y="13">{label}</text>
46+
<text x="{lw + vw/2:.1f}" y="14" fill="#010101" fill-opacity=".3">{value}</text>
47+
<text x="{lw + vw/2:.1f}" y="13">{value}</text>
48+
</g>
49+
</svg>
50+
"""
51+
52+
53+
def main(argv: list[str] | None = None) -> int:
54+
argv = argv or sys.argv[1:]
55+
in_path = Path(argv[0]) if argv else DEFAULT_IN
56+
out_path = Path(argv[1]) if len(argv) > 1 else DEFAULT_OUT
57+
if not in_path.is_file():
58+
print(f"coverage.json not found: {in_path}", file=sys.stderr)
59+
print(
60+
"Run: USE_SQLITE=true pytest -q --cov --cov-report=json:coverage.json",
61+
file=sys.stderr,
62+
)
63+
return 1
64+
data = json.loads(in_path.read_text(encoding="utf-8"))
65+
pct = float(data["totals"]["percent_covered"])
66+
value = f"{pct:.0f}%"
67+
out_path.parent.mkdir(parents=True, exist_ok=True)
68+
out_path.write_text(_svg("coverage", value, _color(pct)), encoding="utf-8")
69+
print(f"Wrote {out_path} ({value})")
70+
return 0
71+
72+
73+
if __name__ == "__main__":
74+
raise SystemExit(main())

scripts/generate_static_badge.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/usr/bin/env python3
2+
"""Write docs/badges/coverage.svg for smoke / validation repos (no pytest %)."""
3+
4+
from __future__ import annotations
5+
6+
import sys
7+
from pathlib import Path
8+
9+
ROOT = Path(__file__).resolve().parents[1]
10+
DEFAULT_OUT = ROOT / "docs" / "badges" / "coverage.svg"
11+
12+
13+
def _svg(label: str, value: str, fill: str = "#4c1") -> str:
14+
lw = len(label) * 6.5 + 10
15+
vw = len(value) * 6.5 + 10
16+
tw = lw + vw
17+
return f"""<svg xmlns="http://www.w3.org/2000/svg" width="{tw:.0f}" height="20" role="img" aria-label="{label}: {value}">
18+
<title>{label}: {value}</title>
19+
<linearGradient id="s" x2="0" y2="100%">
20+
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
21+
<stop offset="1" stop-opacity=".1"/>
22+
</linearGradient>
23+
<clipPath id="r"><rect width="{tw:.0f}" height="20" rx="3" fill="#fff"/></clipPath>
24+
<g clip-path="url(#r)">
25+
<rect width="{lw:.0f}" height="20" fill="#555"/>
26+
<rect x="{lw:.0f}" width="{vw:.0f}" height="20" fill="{fill}"/>
27+
<rect width="{tw:.0f}" height="20" fill="url(#s)"/>
28+
</g>
29+
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
30+
<text x="{lw/2:.1f}" y="14" fill="#010101" fill-opacity=".3">{label}</text>
31+
<text x="{lw/2:.1f}" y="13">{label}</text>
32+
<text x="{lw + vw/2:.1f}" y="14" fill="#010101" fill-opacity=".3">{value}</text>
33+
<text x="{lw + vw/2:.1f}" y="13">{value}</text>
34+
</g>
35+
</svg>
36+
"""
37+
38+
39+
def main(argv: list[str] | None = None) -> int:
40+
argv = argv or sys.argv[1:]
41+
label = argv[0] if argv else "checks"
42+
value = argv[1] if len(argv) > 1 else "pass"
43+
out = Path(argv[2]) if len(argv) > 2 else DEFAULT_OUT
44+
out.parent.mkdir(parents=True, exist_ok=True)
45+
out.write_text(_svg(label, value), encoding="utf-8")
46+
print(f"Wrote {out} ({label}: {value})")
47+
return 0
48+
49+
50+
if __name__ == "__main__":
51+
raise SystemExit(main())

0 commit comments

Comments
 (0)