feat: Add performance reporting for release tracking
- Add scripts/generate-perf-report.sh to fetch CI timing data - Add make perf-report TAG=X.Y.Z target - Generate per-language timing reports (JSON + Markdown) - Track performance history across releases - Prefer fully passing pipelines over warnings 4.2.0: 638 tests, 100% pass, avg 58s, slowest commonlisp (119s)
This commit is contained in:
parent
078a530361
commit
a0ee191dd7
5 changed files with 765 additions and 1 deletions
15
Makefile
15
Makefile
|
|
@ -1,4 +1,4 @@
|
|||
.PHONY: help test test-all test-c test-python test-go test-javascript test-ruby test-php test-rust test-java test-bash test-perl test-lua set-version
|
||||
.PHONY: help test test-all test-c test-python test-go test-javascript test-ruby test-php test-rust test-java test-bash test-perl test-lua set-version perf-report
|
||||
|
||||
# Client directories with their own Makefiles
|
||||
CLIENTS_WITH_MAKEFILE := $(wildcard clients/*/Makefile)
|
||||
|
|
@ -33,6 +33,7 @@ help:
|
|||
@echo " make lint # Lint all clients"
|
||||
@echo " make clean # Clean build artifacts"
|
||||
@echo " make set-version VERSION=X.Y.Z # Set version in all files"
|
||||
@echo " make perf-report TAG=X.Y.Z # Generate performance report for release"
|
||||
@echo ""
|
||||
@echo "Available client Makefiles:"
|
||||
@for dir in $(CLIENT_DIRS); do echo " $$dir"; done
|
||||
|
|
@ -308,3 +309,15 @@ ifndef VERSION
|
|||
@exit 1
|
||||
endif
|
||||
@bash scripts/set-version.sh $(VERSION)
|
||||
|
||||
# ============================================================================
|
||||
# Performance Reporting
|
||||
# ============================================================================
|
||||
|
||||
.PHONY: perf-report
|
||||
perf-report:
|
||||
ifdef TAG
|
||||
@bash scripts/generate-perf-report.sh $(TAG)
|
||||
else
|
||||
@bash scripts/generate-perf-report.sh
|
||||
endif
|
||||
|
|
|
|||
9
reports/PERFORMANCE-HISTORY.md
Normal file
9
reports/PERFORMANCE-HISTORY.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# Performance History
|
||||
|
||||
Tracking compile and test execution times across releases.
|
||||
|
||||
## Releases
|
||||
|
||||
| Version | Date | Tests | Pass Rate | Avg Duration | Slowest | Fastest |
|
||||
|---------|------|-------|-----------|--------------|---------|---------|
|
||||
| 4.2.0 | 2026-01-18 | 638 | 100.0% | 58s | commonlisp | ocaml |
|
||||
398
reports/perf-4.2.0.json
Normal file
398
reports/perf-4.2.0.json
Normal file
|
|
@ -0,0 +1,398 @@
|
|||
{
|
||||
"tag": "4.2.0",
|
||||
"generated_at": "2026-01-18T18:44:55Z",
|
||||
"parent_pipeline_id": 10827,
|
||||
"child_pipeline_id": 10829,
|
||||
"summary": {
|
||||
"total_tests": 638,
|
||||
"passed_tests": 638,
|
||||
"failed_tests": 0,
|
||||
"total_languages": 42,
|
||||
"passed_languages": 42,
|
||||
"avg_duration_seconds": 58,
|
||||
"max_duration_seconds": 119,
|
||||
"min_duration_seconds": 42,
|
||||
"slowest_language": "commonlisp",
|
||||
"fastest_language": "ocaml"
|
||||
},
|
||||
"languages": [
|
||||
{
|
||||
"language": "commonlisp",
|
||||
"status": "Passed",
|
||||
"queued_duration": 130.733107,
|
||||
"started_at": "2026-01-17T23:27:44Z",
|
||||
"updated_at": "2026-01-17T23:29:43Z",
|
||||
"job_id": 23846,
|
||||
"duration_seconds": 119
|
||||
},
|
||||
{
|
||||
"language": "forth",
|
||||
"status": "Passed",
|
||||
"queued_duration": 226.822778,
|
||||
"started_at": "2026-01-17T23:29:36Z",
|
||||
"updated_at": "2026-01-17T23:30:57Z",
|
||||
"job_id": 23872,
|
||||
"duration_seconds": 81
|
||||
},
|
||||
{
|
||||
"language": "csharp",
|
||||
"status": "Passed",
|
||||
"queued_duration": 221.494055,
|
||||
"started_at": "2026-01-17T23:29:31Z",
|
||||
"updated_at": "2026-01-17T23:30:51Z",
|
||||
"job_id": 23870,
|
||||
"duration_seconds": 80
|
||||
},
|
||||
{
|
||||
"language": "fsharp",
|
||||
"status": "Passed",
|
||||
"queued_duration": 215.969817,
|
||||
"started_at": "2026-01-17T23:29:25Z",
|
||||
"updated_at": "2026-01-17T23:30:43Z",
|
||||
"job_id": 23869,
|
||||
"duration_seconds": 78
|
||||
},
|
||||
{
|
||||
"language": "go",
|
||||
"status": "Passed",
|
||||
"queued_duration": 182.801565,
|
||||
"started_at": "2026-01-17T23:28:38Z",
|
||||
"updated_at": "2026-01-17T23:29:49Z",
|
||||
"job_id": 23854,
|
||||
"duration_seconds": 71
|
||||
},
|
||||
{
|
||||
"language": "prolog",
|
||||
"status": "Passed",
|
||||
"queued_duration": 224.618985,
|
||||
"started_at": "2026-01-17T23:29:34Z",
|
||||
"updated_at": "2026-01-17T23:30:43Z",
|
||||
"job_id": 23871,
|
||||
"duration_seconds": 69
|
||||
},
|
||||
{
|
||||
"language": "perl",
|
||||
"status": "Passed",
|
||||
"queued_duration": 124.483959,
|
||||
"started_at": "2026-01-17T23:27:35Z",
|
||||
"updated_at": "2026-01-17T23:28:43Z",
|
||||
"job_id": 23832,
|
||||
"duration_seconds": 68
|
||||
},
|
||||
{
|
||||
"language": "php",
|
||||
"status": "Passed",
|
||||
"queued_duration": 126.024306,
|
||||
"started_at": "2026-01-17T23:27:37Z",
|
||||
"updated_at": "2026-01-17T23:28:43Z",
|
||||
"job_id": 23834,
|
||||
"duration_seconds": 66
|
||||
},
|
||||
{
|
||||
"language": "raku",
|
||||
"status": "Passed",
|
||||
"queued_duration": 181.812216,
|
||||
"started_at": "2026-01-17T23:28:36Z",
|
||||
"updated_at": "2026-01-17T23:29:40Z",
|
||||
"job_id": 23851,
|
||||
"duration_seconds": 64
|
||||
},
|
||||
{
|
||||
"language": "elixir",
|
||||
"status": "Passed",
|
||||
"queued_duration": 128.33839,
|
||||
"started_at": "2026-01-17T23:27:40Z",
|
||||
"updated_at": "2026-01-17T23:28:44Z",
|
||||
"job_id": 23840,
|
||||
"duration_seconds": 64
|
||||
},
|
||||
{
|
||||
"language": "cpp",
|
||||
"status": "Passed",
|
||||
"queued_duration": 182.401442,
|
||||
"started_at": "2026-01-17T23:28:37Z",
|
||||
"updated_at": "2026-01-17T23:29:41Z",
|
||||
"job_id": 23853,
|
||||
"duration_seconds": 64
|
||||
},
|
||||
{
|
||||
"language": "r",
|
||||
"status": "Passed",
|
||||
"queued_duration": 128.039533,
|
||||
"started_at": "2026-01-17T23:27:40Z",
|
||||
"updated_at": "2026-01-17T23:28:43Z",
|
||||
"job_id": 23839,
|
||||
"duration_seconds": 63
|
||||
},
|
||||
{
|
||||
"language": "bash",
|
||||
"status": "Passed",
|
||||
"queued_duration": 127.648122,
|
||||
"started_at": "2026-01-17T23:27:39Z",
|
||||
"updated_at": "2026-01-17T23:28:42Z",
|
||||
"job_id": 23838,
|
||||
"duration_seconds": 63
|
||||
},
|
||||
{
|
||||
"language": "rust",
|
||||
"status": "Passed",
|
||||
"queued_duration": 184.558244,
|
||||
"started_at": "2026-01-17T23:28:40Z",
|
||||
"updated_at": "2026-01-17T23:29:42Z",
|
||||
"job_id": 23855,
|
||||
"duration_seconds": 62
|
||||
},
|
||||
{
|
||||
"language": "deno",
|
||||
"status": "Passed",
|
||||
"queued_duration": 175.795332,
|
||||
"started_at": "2026-01-17T23:28:30Z",
|
||||
"updated_at": "2026-01-17T23:29:31Z",
|
||||
"job_id": 23849,
|
||||
"duration_seconds": 61
|
||||
},
|
||||
{
|
||||
"language": "c",
|
||||
"status": "Passed",
|
||||
"queued_duration": 182.150823,
|
||||
"started_at": "2026-01-17T23:28:37Z",
|
||||
"updated_at": "2026-01-17T23:29:36Z",
|
||||
"job_id": 23852,
|
||||
"duration_seconds": 59
|
||||
},
|
||||
{
|
||||
"language": "lua",
|
||||
"status": "Passed",
|
||||
"queued_duration": 126.849415,
|
||||
"started_at": "2026-01-17T23:27:38Z",
|
||||
"updated_at": "2026-01-17T23:28:35Z",
|
||||
"job_id": 23836,
|
||||
"duration_seconds": 57
|
||||
},
|
||||
{
|
||||
"language": "zig",
|
||||
"status": "Passed",
|
||||
"queued_duration": 189.744268,
|
||||
"started_at": "2026-01-17T23:28:47Z",
|
||||
"updated_at": "2026-01-17T23:29:43Z",
|
||||
"job_id": 23861,
|
||||
"duration_seconds": 56
|
||||
},
|
||||
{
|
||||
"language": "erlang",
|
||||
"status": "Passed",
|
||||
"queued_duration": 128.85146,
|
||||
"started_at": "2026-01-17T23:27:41Z",
|
||||
"updated_at": "2026-01-17T23:28:37Z",
|
||||
"job_id": 23841,
|
||||
"duration_seconds": 56
|
||||
},
|
||||
{
|
||||
"language": "scheme",
|
||||
"status": "Passed",
|
||||
"queued_duration": 129.75745,
|
||||
"started_at": "2026-01-17T23:27:42Z",
|
||||
"updated_at": "2026-01-17T23:28:37Z",
|
||||
"job_id": 23843,
|
||||
"duration_seconds": 55
|
||||
},
|
||||
{
|
||||
"language": "groovy",
|
||||
"status": "Passed",
|
||||
"queued_duration": 134.87264,
|
||||
"started_at": "2026-01-17T23:27:49Z",
|
||||
"updated_at": "2026-01-17T23:28:43Z",
|
||||
"job_id": 23848,
|
||||
"duration_seconds": 54
|
||||
},
|
||||
{
|
||||
"language": "d",
|
||||
"status": "Passed",
|
||||
"queued_duration": 189.224944,
|
||||
"started_at": "2026-01-17T23:28:46Z",
|
||||
"updated_at": "2026-01-17T23:29:40Z",
|
||||
"job_id": 23860,
|
||||
"duration_seconds": 54
|
||||
},
|
||||
{
|
||||
"language": "tcl",
|
||||
"status": "Passed",
|
||||
"queued_duration": 129.562818,
|
||||
"started_at": "2026-01-17T23:27:42Z",
|
||||
"updated_at": "2026-01-17T23:28:35Z",
|
||||
"job_id": 23842,
|
||||
"duration_seconds": 53
|
||||
},
|
||||
{
|
||||
"language": "kotlin",
|
||||
"status": "Passed",
|
||||
"queued_duration": 187.476238,
|
||||
"started_at": "2026-01-17T23:28:44Z",
|
||||
"updated_at": "2026-01-17T23:29:37Z",
|
||||
"job_id": 23857,
|
||||
"duration_seconds": 53
|
||||
},
|
||||
{
|
||||
"language": "java",
|
||||
"status": "Passed",
|
||||
"queued_duration": 186.306987,
|
||||
"started_at": "2026-01-17T23:28:42Z",
|
||||
"updated_at": "2026-01-17T23:29:35Z",
|
||||
"job_id": 23856,
|
||||
"duration_seconds": 53
|
||||
},
|
||||
{
|
||||
"language": "v",
|
||||
"status": "Passed",
|
||||
"queued_duration": 190.541247,
|
||||
"started_at": "2026-01-17T23:28:48Z",
|
||||
"updated_at": "2026-01-17T23:29:40Z",
|
||||
"job_id": 23863,
|
||||
"duration_seconds": 52
|
||||
},
|
||||
{
|
||||
"language": "typescript",
|
||||
"status": "Passed",
|
||||
"queued_duration": 74.207827,
|
||||
"started_at": "2026-01-17T23:26:44Z",
|
||||
"updated_at": "2026-01-17T23:27:36Z",
|
||||
"job_id": 23827,
|
||||
"duration_seconds": 52
|
||||
},
|
||||
{
|
||||
"language": "python",
|
||||
"status": "Passed",
|
||||
"queued_duration": 72.915966,
|
||||
"started_at": "2026-01-17T23:26:43Z",
|
||||
"updated_at": "2026-01-17T23:27:35Z",
|
||||
"job_id": 23824,
|
||||
"duration_seconds": 52
|
||||
},
|
||||
{
|
||||
"language": "objc",
|
||||
"status": "Passed",
|
||||
"queued_duration": 187.135332,
|
||||
"started_at": "2026-01-17T23:28:48Z",
|
||||
"updated_at": "2026-01-17T23:29:40Z",
|
||||
"job_id": 23864,
|
||||
"duration_seconds": 52
|
||||
},
|
||||
{
|
||||
"language": "javascript",
|
||||
"status": "Passed",
|
||||
"queued_duration": 73.805034,
|
||||
"started_at": "2026-01-17T23:26:44Z",
|
||||
"updated_at": "2026-01-17T23:27:36Z",
|
||||
"job_id": 23826,
|
||||
"duration_seconds": 52
|
||||
},
|
||||
{
|
||||
"language": "fortran",
|
||||
"status": "Passed",
|
||||
"queued_duration": 188.920887,
|
||||
"started_at": "2026-01-17T23:28:45Z",
|
||||
"updated_at": "2026-01-17T23:29:37Z",
|
||||
"job_id": 23859,
|
||||
"duration_seconds": 52
|
||||
},
|
||||
{
|
||||
"language": "ruby",
|
||||
"status": "Passed",
|
||||
"queued_duration": 75.205723,
|
||||
"started_at": "2026-01-17T23:26:46Z",
|
||||
"updated_at": "2026-01-17T23:27:37Z",
|
||||
"job_id": 23829,
|
||||
"duration_seconds": 51
|
||||
},
|
||||
{
|
||||
"language": "dart",
|
||||
"status": "Passed",
|
||||
"queued_duration": 184.421648,
|
||||
"started_at": "2026-01-17T23:28:49Z",
|
||||
"updated_at": "2026-01-17T23:29:40Z",
|
||||
"job_id": 23865,
|
||||
"duration_seconds": 51
|
||||
},
|
||||
{
|
||||
"language": "crystal",
|
||||
"status": "Passed",
|
||||
"queued_duration": 131.235438,
|
||||
"started_at": "2026-01-17T23:27:45Z",
|
||||
"updated_at": "2026-01-17T23:28:36Z",
|
||||
"job_id": 23847,
|
||||
"duration_seconds": 51
|
||||
},
|
||||
{
|
||||
"language": "clojure",
|
||||
"status": "Passed",
|
||||
"queued_duration": 130.414948,
|
||||
"started_at": "2026-01-17T23:27:44Z",
|
||||
"updated_at": "2026-01-17T23:28:35Z",
|
||||
"job_id": 23845,
|
||||
"duration_seconds": 51
|
||||
},
|
||||
{
|
||||
"language": "awk",
|
||||
"status": "Passed",
|
||||
"queued_duration": 184.885825,
|
||||
"started_at": "2026-01-17T23:28:35Z",
|
||||
"updated_at": "2026-01-17T23:29:25Z",
|
||||
"job_id": 23850,
|
||||
"duration_seconds": 50
|
||||
},
|
||||
{
|
||||
"language": "cobol",
|
||||
"status": "Passed",
|
||||
"queued_duration": 188.376226,
|
||||
"started_at": "2026-01-17T23:28:45Z",
|
||||
"updated_at": "2026-01-17T23:29:34Z",
|
||||
"job_id": 23858,
|
||||
"duration_seconds": 49
|
||||
},
|
||||
{
|
||||
"language": "powershell",
|
||||
"status": "Passed",
|
||||
"queued_duration": 130.055351,
|
||||
"started_at": "2026-01-17T23:27:43Z",
|
||||
"updated_at": "2026-01-17T23:28:30Z",
|
||||
"job_id": 23844,
|
||||
"duration_seconds": 47
|
||||
},
|
||||
{
|
||||
"language": "julia",
|
||||
"status": "Passed",
|
||||
"queued_duration": 181.87361,
|
||||
"started_at": "2026-01-17T23:28:50Z",
|
||||
"updated_at": "2026-01-17T23:29:36Z",
|
||||
"job_id": 23866,
|
||||
"duration_seconds": 46
|
||||
},
|
||||
{
|
||||
"language": "nim",
|
||||
"status": "Passed",
|
||||
"queued_duration": 190.067576,
|
||||
"started_at": "2026-01-17T23:28:47Z",
|
||||
"updated_at": "2026-01-17T23:29:31Z",
|
||||
"job_id": 23862,
|
||||
"duration_seconds": 44
|
||||
},
|
||||
{
|
||||
"language": "haskell",
|
||||
"status": "Passed",
|
||||
"queued_duration": 188.242852,
|
||||
"started_at": "2026-01-17T23:28:57Z",
|
||||
"updated_at": "2026-01-17T23:29:40Z",
|
||||
"job_id": 23867,
|
||||
"duration_seconds": 43
|
||||
},
|
||||
{
|
||||
"language": "ocaml",
|
||||
"status": "Passed",
|
||||
"queued_duration": 188.729273,
|
||||
"started_at": "2026-01-17T23:28:57Z",
|
||||
"updated_at": "2026-01-17T23:29:39Z",
|
||||
"job_id": 23868,
|
||||
"duration_seconds": 42
|
||||
}
|
||||
]
|
||||
}
|
||||
114
reports/perf-4.2.0.md
Normal file
114
reports/perf-4.2.0.md
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
# Performance Report: 4.2.0
|
||||
|
||||
**Generated:** 2026-01-18T18:44:55Z
|
||||
**Pipeline:** [10829](https://git.unturf.com/engineering/unturf/un-inception/-/pipelines/10829)
|
||||
|
||||
## Summary
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Total Tests | 638 |
|
||||
| Passed | 638 |
|
||||
| Failed | 0 |
|
||||
| Pass Rate | 100.0% |
|
||||
| Languages | 42 |
|
||||
| Avg Duration | 58s |
|
||||
| Slowest | commonlisp (119s) |
|
||||
| Fastest | ocaml (42s) |
|
||||
|
||||
## Per-Language Performance
|
||||
|
||||
| Language | Status | Duration | Tests |
|
||||
|----------|--------|----------|-------|
|
||||
| commonlisp | Passed | 119s | - |
|
||||
| forth | Passed | 81s | - |
|
||||
| csharp | Passed | 80s | - |
|
||||
| fsharp | Passed | 78s | - |
|
||||
| go | Passed | 71s | - |
|
||||
| prolog | Passed | 69s | - |
|
||||
| perl | Passed | 68s | - |
|
||||
| php | Passed | 66s | - |
|
||||
| raku | Passed | 64s | - |
|
||||
| elixir | Passed | 64s | - |
|
||||
| cpp | Passed | 64s | - |
|
||||
| r | Passed | 63s | - |
|
||||
| bash | Passed | 63s | - |
|
||||
| rust | Passed | 62s | - |
|
||||
| deno | Passed | 61s | - |
|
||||
| c | Passed | 59s | - |
|
||||
| lua | Passed | 57s | - |
|
||||
| zig | Passed | 56s | - |
|
||||
| erlang | Passed | 56s | - |
|
||||
| scheme | Passed | 55s | - |
|
||||
| groovy | Passed | 54s | - |
|
||||
| d | Passed | 54s | - |
|
||||
| tcl | Passed | 53s | - |
|
||||
| kotlin | Passed | 53s | - |
|
||||
| java | Passed | 53s | - |
|
||||
| v | Passed | 52s | - |
|
||||
| typescript | Passed | 52s | - |
|
||||
| python | Passed | 52s | - |
|
||||
| objc | Passed | 52s | - |
|
||||
| javascript | Passed | 52s | - |
|
||||
| fortran | Passed | 52s | - |
|
||||
| ruby | Passed | 51s | - |
|
||||
| dart | Passed | 51s | - |
|
||||
| crystal | Passed | 51s | - |
|
||||
| clojure | Passed | 51s | - |
|
||||
| awk | Passed | 50s | - |
|
||||
| cobol | Passed | 49s | - |
|
||||
| powershell | Passed | 47s | - |
|
||||
| julia | Passed | 46s | - |
|
||||
| nim | Passed | 44s | - |
|
||||
| haskell | Passed | 43s | - |
|
||||
| ocaml | Passed | 42s | - |
|
||||
|
||||
## Timing Distribution
|
||||
|
||||
```
|
||||
commonlisp 119s ███████████████████████
|
||||
forth 81s ████████████████
|
||||
csharp 80s ████████████████
|
||||
fsharp 78s ███████████████
|
||||
go 71s ██████████████
|
||||
prolog 69s █████████████
|
||||
perl 68s █████████████
|
||||
php 66s █████████████
|
||||
raku 64s ████████████
|
||||
elixir 64s ████████████
|
||||
cpp 64s ████████████
|
||||
r 63s ████████████
|
||||
bash 63s ████████████
|
||||
rust 62s ████████████
|
||||
deno 61s ████████████
|
||||
c 59s ███████████
|
||||
lua 57s ███████████
|
||||
zig 56s ███████████
|
||||
erlang 56s ███████████
|
||||
scheme 55s ███████████
|
||||
groovy 54s ██████████
|
||||
d 54s ██████████
|
||||
tcl 53s ██████████
|
||||
kotlin 53s ██████████
|
||||
java 53s ██████████
|
||||
v 52s ██████████
|
||||
typescript 52s ██████████
|
||||
python 52s ██████████
|
||||
objc 52s ██████████
|
||||
javascript 52s ██████████
|
||||
fortran 52s ██████████
|
||||
ruby 51s ██████████
|
||||
dart 51s ██████████
|
||||
crystal 51s ██████████
|
||||
clojure 51s ██████████
|
||||
awk 50s ██████████
|
||||
cobol 49s █████████
|
||||
powershell 47s █████████
|
||||
julia 46s █████████
|
||||
nim 44s ████████
|
||||
haskell 43s ████████
|
||||
ocaml 42s ████████
|
||||
```
|
||||
|
||||
---
|
||||
*Report generated by `make perf-report`*
|
||||
230
scripts/generate-perf-report.sh
Executable file
230
scripts/generate-perf-report.sh
Executable file
|
|
@ -0,0 +1,230 @@
|
|||
#!/bin/bash
|
||||
# Generate performance report for a tagged release
|
||||
# Fetches job timing data from GitLab CI and creates a historical record
|
||||
#
|
||||
# Usage: scripts/generate-perf-report.sh [TAG]
|
||||
# Example: scripts/generate-perf-report.sh 4.2.0
|
||||
#
|
||||
# Output: reports/perf-{TAG}.md and reports/perf-{TAG}.json
|
||||
|
||||
set -e
|
||||
|
||||
TAG="${1:-}"
|
||||
GITLAB_URL="https://git.unturf.com"
|
||||
PROJECT_PATH="engineering/unturf/un-inception"
|
||||
|
||||
if [ -z "$TAG" ]; then
|
||||
# Default to current VERSION
|
||||
if [ -f VERSION ]; then
|
||||
TAG=$(cat VERSION)
|
||||
else
|
||||
echo "Usage: $0 <tag>"
|
||||
echo "Example: $0 4.2.0"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Generating performance report for tag: $TAG"
|
||||
|
||||
# Create reports directory
|
||||
mkdir -p reports
|
||||
|
||||
# Find pipeline for this tag (prefer passing pipelines)
|
||||
echo "Finding pipeline for tag $TAG..."
|
||||
PIPELINES=$(curl -s "$GITLAB_URL/$PROJECT_PATH/-/pipelines.json" | jq -r "[.pipelines[] | select(.ref.name == \"$TAG\" and .ref.tag == true)]")
|
||||
|
||||
if [ "$(echo "$PIPELINES" | jq 'length')" -eq 0 ]; then
|
||||
echo "Error: No pipeline found for tag $TAG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Try to find a pipeline with fully passing child
|
||||
PARENT_PIPELINE_ID=""
|
||||
CHILD_PIPELINE_ID=""
|
||||
|
||||
for pid in $(echo "$PIPELINES" | jq -r '.[].id'); do
|
||||
CHILD_DATA=$(curl -s "$GITLAB_URL/$PROJECT_PATH/-/pipelines/$pid.json" | jq '.triggered[0]')
|
||||
CHILD_ID=$(echo "$CHILD_DATA" | jq -r '.id // empty')
|
||||
CHILD_STATUS=$(echo "$CHILD_DATA" | jq -r '.details.status.group // empty')
|
||||
|
||||
if [ -n "$CHILD_ID" ]; then
|
||||
PARENT_PIPELINE_ID="$pid"
|
||||
CHILD_PIPELINE_ID="$CHILD_ID"
|
||||
# Prefer "success" over "success-with-warnings"
|
||||
if [ "$CHILD_STATUS" = "success" ]; then
|
||||
echo "Found fully passing pipeline: $pid -> $CHILD_ID"
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$CHILD_PIPELINE_ID" ]; then
|
||||
echo "Error: No child pipeline found for tag $TAG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Using parent pipeline: $PARENT_PIPELINE_ID"
|
||||
echo "Using child pipeline: $CHILD_PIPELINE_ID"
|
||||
|
||||
if [ -z "$CHILD_PIPELINE_ID" ] || [ "$CHILD_PIPELINE_ID" = "null" ]; then
|
||||
echo "Error: No child pipeline found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Found child pipeline: $CHILD_PIPELINE_ID"
|
||||
|
||||
# Get all test jobs
|
||||
echo "Fetching job data..."
|
||||
JOBS_JSON=$(curl -s "$GITLAB_URL/$PROJECT_PATH/-/pipelines/$CHILD_PIPELINE_ID/stage.json?stage=test" | jq '.latest_statuses')
|
||||
|
||||
# Get test report summary
|
||||
TEST_REPORT=$(curl -s "$GITLAB_URL/$PROJECT_PATH/-/pipelines/$CHILD_PIPELINE_ID/test_report.json")
|
||||
TOTAL_TESTS=$(echo "$TEST_REPORT" | jq -r '.total_count // 0')
|
||||
PASSED_TESTS=$(echo "$TEST_REPORT" | jq -r '.success_count // 0')
|
||||
FAILED_TESTS=$(echo "$TEST_REPORT" | jq -r '.failed_count // 0')
|
||||
|
||||
# Parse job timing data
|
||||
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||
REPORT_DATE=$(date -u +"%Y-%m-%d")
|
||||
|
||||
# Create JSON report with per-language timing
|
||||
echo "Parsing timing data..."
|
||||
|
||||
# Build JSON array of language performance
|
||||
# Note: We strip milliseconds from timestamps for jq compatibility
|
||||
PERF_JSON=$(echo "$JOBS_JSON" | jq -r '
|
||||
[.[] |
|
||||
select(.name | startswith("test: [")) |
|
||||
{
|
||||
language: (.name | gsub("test: \\[|\\]"; "")),
|
||||
status: .status.text,
|
||||
queued_duration: (.queued_duration // 0),
|
||||
started_at: (.started_at // "" | gsub("\\.[0-9]+Z$"; "Z")),
|
||||
updated_at: (.updated_at // "" | gsub("\\.[0-9]+Z$"; "Z")),
|
||||
job_id: .id
|
||||
}
|
||||
] |
|
||||
map(. + {
|
||||
duration_seconds: (
|
||||
if .started_at != "" and .updated_at != "" then
|
||||
(((.updated_at | fromdateiso8601) - (.started_at | fromdateiso8601)) | floor)
|
||||
else 0 end
|
||||
)
|
||||
}) |
|
||||
sort_by(.duration_seconds) | reverse
|
||||
')
|
||||
|
||||
# Calculate stats
|
||||
TOTAL_LANGUAGES=$(echo "$PERF_JSON" | jq 'length')
|
||||
PASSED_LANGUAGES=$(echo "$PERF_JSON" | jq '[.[] | select(.status == "Passed")] | length')
|
||||
AVG_DURATION=$(echo "$PERF_JSON" | jq '[.[].duration_seconds] | add / length | floor')
|
||||
MAX_DURATION=$(echo "$PERF_JSON" | jq '[.[].duration_seconds] | max')
|
||||
MIN_DURATION=$(echo "$PERF_JSON" | jq '[.[].duration_seconds] | min')
|
||||
SLOWEST_LANG=$(echo "$PERF_JSON" | jq -r '.[0].language')
|
||||
FASTEST_LANG=$(echo "$PERF_JSON" | jq -r '.[-1].language')
|
||||
|
||||
# Write JSON report
|
||||
cat > "reports/perf-$TAG.json" << EOF
|
||||
{
|
||||
"tag": "$TAG",
|
||||
"generated_at": "$TIMESTAMP",
|
||||
"parent_pipeline_id": $PARENT_PIPELINE_ID,
|
||||
"child_pipeline_id": $CHILD_PIPELINE_ID,
|
||||
"summary": {
|
||||
"total_tests": $TOTAL_TESTS,
|
||||
"passed_tests": $PASSED_TESTS,
|
||||
"failed_tests": $FAILED_TESTS,
|
||||
"total_languages": $TOTAL_LANGUAGES,
|
||||
"passed_languages": $PASSED_LANGUAGES,
|
||||
"avg_duration_seconds": $AVG_DURATION,
|
||||
"max_duration_seconds": $MAX_DURATION,
|
||||
"min_duration_seconds": $MIN_DURATION,
|
||||
"slowest_language": "$SLOWEST_LANG",
|
||||
"fastest_language": "$FASTEST_LANG"
|
||||
},
|
||||
"languages": $PERF_JSON
|
||||
}
|
||||
EOF
|
||||
|
||||
echo " ✓ reports/perf-$TAG.json"
|
||||
|
||||
# Write Markdown report
|
||||
cat > "reports/perf-$TAG.md" << EOF
|
||||
# Performance Report: $TAG
|
||||
|
||||
**Generated:** $TIMESTAMP
|
||||
**Pipeline:** [$CHILD_PIPELINE_ID]($GITLAB_URL/$PROJECT_PATH/-/pipelines/$CHILD_PIPELINE_ID)
|
||||
|
||||
## Summary
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Total Tests | $TOTAL_TESTS |
|
||||
| Passed | $PASSED_TESTS |
|
||||
| Failed | $FAILED_TESTS |
|
||||
| Pass Rate | $(echo "scale=1; $PASSED_TESTS * 100 / $TOTAL_TESTS" | bc)% |
|
||||
| Languages | $TOTAL_LANGUAGES |
|
||||
| Avg Duration | ${AVG_DURATION}s |
|
||||
| Slowest | $SLOWEST_LANG (${MAX_DURATION}s) |
|
||||
| Fastest | $FASTEST_LANG (${MIN_DURATION}s) |
|
||||
|
||||
## Per-Language Performance
|
||||
|
||||
| Language | Status | Duration | Tests |
|
||||
|----------|--------|----------|-------|
|
||||
EOF
|
||||
|
||||
# Add per-language rows
|
||||
echo "$PERF_JSON" | jq -r '.[] | "| \(.language) | \(.status) | \(.duration_seconds)s | - |"' >> "reports/perf-$TAG.md"
|
||||
|
||||
cat >> "reports/perf-$TAG.md" << EOF
|
||||
|
||||
## Timing Distribution
|
||||
|
||||
\`\`\`
|
||||
EOF
|
||||
|
||||
# Add a simple ASCII histogram
|
||||
echo "$PERF_JSON" | jq -r '.[] | "\(.language)|\(.duration_seconds)"' | while IFS='|' read -r lang dur; do
|
||||
bar=""
|
||||
bars=$((dur / 5))
|
||||
for ((i=0; i<bars && i<40; i++)); do bar+="█"; done
|
||||
printf "%-12s %3ds %s\n" "$lang" "$dur" "$bar" >> "reports/perf-$TAG.md"
|
||||
done
|
||||
|
||||
cat >> "reports/perf-$TAG.md" << EOF
|
||||
\`\`\`
|
||||
|
||||
---
|
||||
*Report generated by \`make perf-report\`*
|
||||
EOF
|
||||
|
||||
echo " ✓ reports/perf-$TAG.md"
|
||||
|
||||
# Update or create historical index
|
||||
if [ ! -f "reports/PERFORMANCE-HISTORY.md" ]; then
|
||||
cat > "reports/PERFORMANCE-HISTORY.md" << EOF
|
||||
# Performance History
|
||||
|
||||
Tracking compile and test execution times across releases.
|
||||
|
||||
## Releases
|
||||
|
||||
| Version | Date | Tests | Pass Rate | Avg Duration | Slowest | Fastest |
|
||||
|---------|------|-------|-----------|--------------|---------|---------|
|
||||
EOF
|
||||
fi
|
||||
|
||||
# Add this release to history (avoid duplicates)
|
||||
if ! grep -q "| $TAG |" "reports/PERFORMANCE-HISTORY.md"; then
|
||||
echo "| $TAG | $REPORT_DATE | $TOTAL_TESTS | $(echo "scale=1; $PASSED_TESTS * 100 / $TOTAL_TESTS" | bc)% | ${AVG_DURATION}s | $SLOWEST_LANG | $FASTEST_LANG |" >> "reports/PERFORMANCE-HISTORY.md"
|
||||
echo " ✓ Updated reports/PERFORMANCE-HISTORY.md"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Done! Performance report for $TAG:"
|
||||
echo " - reports/perf-$TAG.json"
|
||||
echo " - reports/perf-$TAG.md"
|
||||
echo " - reports/PERFORMANCE-HISTORY.md"
|
||||
echo ""
|
||||
echo "To commit: git add reports/ && git commit -m 'perf: Add performance report for $TAG'"
|
||||
Loading…
Add table
Add a link
Reference in a new issue