From 8b20f3258fed125c7deb75d89482c222eaa007e9 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 1 May 2026 17:46:36 +0200 Subject: [PATCH] graph: add vertical bars for GNU coreutils releases --- gnu-results.svg | 1030 +++++++++++++++++++++++++++++------------------ graph.py | 5 + graph_common.py | 47 +++ 3 files changed, 697 insertions(+), 385 deletions(-) diff --git a/gnu-results.svg b/gnu-results.svg index 780e338db..02288f99f 100644 --- a/gnu-results.svg +++ b/gnu-results.svg @@ -8,7 +8,7 @@ GNU Test Suite Results - 2026-05-01T14:30:40.720466 + 2026-05-01T17:44:18.188331 image/svg+xml @@ -46,33 +46,33 @@ L 1264.341562 87.359701 L 88.711563 87.359701 L 88.711563 545.664501 z -" clip-path="url(#p02f9f4b9f1)" style="fill: none; stroke: #e5e7eb; stroke-width: 3; stroke-linejoin: miter"/> +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke: #e5e7eb; stroke-width: 3; stroke-linejoin: miter"/> +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 2.96,1.28; stroke-dashoffset: 0; stroke: #d1d5db; stroke-opacity: 0.3; stroke-width: 0.8"/> +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 2.96,1.28; stroke-dashoffset: 0; stroke: #d1d5db; stroke-opacity: 0.3; stroke-width: 0.8"/> +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke: #e5e7eb; stroke-opacity: 0.35; stroke-linecap: round"/> - - + @@ -126,9 +126,9 @@ z " transform="scale(0.015625)"/> - - - + + + @@ -136,11 +136,11 @@ z +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke: #e5e7eb; stroke-opacity: 0.35; stroke-linecap: round"/> - + @@ -181,9 +181,9 @@ z " transform="scale(0.015625)"/> - - - + + + @@ -191,11 +191,11 @@ z +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke: #e5e7eb; stroke-opacity: 0.35; stroke-linecap: round"/> - + @@ -223,9 +223,9 @@ z " transform="scale(0.015625)"/> - - - + + + @@ -233,11 +233,11 @@ z +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke: #e5e7eb; stroke-opacity: 0.35; stroke-linecap: round"/> - + @@ -271,9 +271,9 @@ z " transform="scale(0.015625)"/> - - - + + + @@ -281,11 +281,11 @@ z +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke: #e5e7eb; stroke-opacity: 0.35; stroke-linecap: round"/> - + @@ -324,9 +324,9 @@ z " transform="scale(0.015625)"/> - - - + + + @@ -334,16 +334,16 @@ z +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - - + @@ -351,11 +351,11 @@ L 0 6 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -363,11 +363,11 @@ L 145.985176 87.359701 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -375,11 +375,11 @@ L 203.238705 87.359701 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -387,11 +387,11 @@ L 317.745765 87.359701 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -399,11 +399,11 @@ L 374.999294 87.359701 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -411,11 +411,11 @@ L 432.252824 87.359701 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -423,11 +423,11 @@ L 546.759883 87.359701 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -435,11 +435,11 @@ L 604.013413 87.359701 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -447,11 +447,11 @@ L 661.266943 87.359701 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -459,11 +459,11 @@ L 775.774002 87.359701 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -471,11 +471,11 @@ L 833.027532 87.359701 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -483,11 +483,11 @@ L 890.281062 87.359701 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -495,11 +495,11 @@ L 947.534591 87.359701 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -507,11 +507,11 @@ L 1004.788121 87.359701 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -519,11 +519,11 @@ L 1062.041651 87.359701 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -531,11 +531,11 @@ L 1119.29518 87.359701 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -543,11 +543,11 @@ L 1176.54871 87.359701 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -659,9 +659,9 @@ z " transform="scale(0.015625)"/> - - - + + + @@ -670,16 +670,16 @@ z +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke: #e5e7eb; stroke-opacity: 0.35; stroke-linecap: round"/> - - + @@ -693,11 +693,11 @@ L -9 0 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke: #e5e7eb; stroke-opacity: 0.35; stroke-linecap: round"/> - + @@ -720,8 +720,8 @@ z " transform="scale(0.015625)"/> - - + + @@ -729,19 +729,19 @@ z +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke: #e5e7eb; stroke-opacity: 0.35; stroke-linecap: round"/> - + - - + + @@ -749,19 +749,19 @@ L 1264.341562 419.148338 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke: #e5e7eb; stroke-opacity: 0.35; stroke-linecap: round"/> - + - - + + @@ -769,19 +769,19 @@ L 1264.341562 355.890257 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke: #e5e7eb; stroke-opacity: 0.35; stroke-linecap: round"/> - + - - + + @@ -789,19 +789,19 @@ L 1264.341562 292.632175 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke: #e5e7eb; stroke-opacity: 0.35; stroke-linecap: round"/> - + - - + + @@ -809,19 +809,19 @@ L 1264.341562 229.374094 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke: #e5e7eb; stroke-opacity: 0.35; stroke-linecap: round"/> - + - - + + @@ -829,11 +829,11 @@ L 1264.341562 166.116012 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke: #e5e7eb; stroke-opacity: 0.35; stroke-linecap: round"/> - + @@ -852,8 +852,8 @@ z " transform="scale(0.015625)"/> - - + + @@ -861,16 +861,16 @@ z +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - - + @@ -878,11 +878,11 @@ L -6 0 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -890,11 +890,11 @@ L 1264.341562 520.361268 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -902,11 +902,11 @@ L 1264.341562 507.709652 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -914,11 +914,11 @@ L 1264.341562 495.058036 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -926,11 +926,11 @@ L 1264.341562 469.754803 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -938,11 +938,11 @@ L 1264.341562 457.103187 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -950,11 +950,11 @@ L 1264.341562 444.451571 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -962,11 +962,11 @@ L 1264.341562 431.799954 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -974,11 +974,11 @@ L 1264.341562 406.496722 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -986,11 +986,11 @@ L 1264.341562 393.845106 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -998,11 +998,11 @@ L 1264.341562 381.193489 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -1010,11 +1010,11 @@ L 1264.341562 368.541873 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -1022,11 +1022,11 @@ L 1264.341562 343.23864 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -1034,11 +1034,11 @@ L 1264.341562 330.587024 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -1046,11 +1046,11 @@ L 1264.341562 317.935408 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -1058,11 +1058,11 @@ L 1264.341562 305.283792 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -1070,11 +1070,11 @@ L 1264.341562 279.980559 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -1082,11 +1082,11 @@ L 1264.341562 267.328943 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -1094,11 +1094,11 @@ L 1264.341562 254.677326 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -1106,11 +1106,11 @@ L 1264.341562 242.02571 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -1118,11 +1118,11 @@ L 1264.341562 216.722478 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -1130,11 +1130,11 @@ L 1264.341562 204.070861 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -1142,11 +1142,11 @@ L 1264.341562 191.419245 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -1154,11 +1154,11 @@ L 1264.341562 178.767629 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -1166,11 +1166,11 @@ L 1264.341562 153.464396 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -1178,11 +1178,11 @@ L 1264.341562 140.81278 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -1190,11 +1190,11 @@ L 1264.341562 128.161164 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -1202,11 +1202,11 @@ L 1264.341562 115.509547 +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #f3f4f6; stroke-opacity: 0.15; stroke-width: 0.5"/> - + @@ -1412,26 +1412,26 @@ z " transform="scale(0.015625)"/> - - - - - - - - - - - - - - + + + + + + + + + + + + + + - + - - - + + - + - - - + + - + - - - + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke: #0066cc; stroke-width: 3.5; stroke-linecap: round"/> - + +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke: #10b981; stroke-width: 3.5; stroke-linecap: round"/> - + +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke: #ef4444; stroke-width: 3.5; stroke-linecap: round"/> - + +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke: #f59e0b; stroke-width: 3.5; stroke-linecap: round"/> - + +" clip-path="url(#p7f37a4977b)" style="fill: none; stroke: #8b5cf6; stroke-width: 3.5; stroke-linecap: round"/> - - - - - + + + + + @@ -20000,46 +20060,46 @@ z " transform="scale(0.015625)"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -20531,74 +20591,274 @@ z " transform="scale(0.015625)"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -20615,13 +20875,13 @@ Q 88.711563 125.809072 95.211563 125.809072 z " style="fill: #ffffff; opacity: 0.98; stroke: #d1d5db; stroke-width: 2; stroke-linejoin: miter"/> - + - + @@ -20720,19 +20980,19 @@ z " transform="scale(0.015625)"/> - - - - + + + + - + - + @@ -20790,18 +21050,18 @@ z " transform="scale(0.015625)"/> - - - + + + - + - + @@ -20833,18 +21093,18 @@ z " transform="scale(0.015625)"/> - - - + + + - + - + @@ -20882,19 +21142,19 @@ z " transform="scale(0.015625)"/> - - - - + + + + - + - + @@ -20971,13 +21231,13 @@ z " transform="scale(0.015625)"/> - - - + + + - + - - - - - - - - - - - - - - + + + + + + + + + + + + + + @@ -21162,16 +21422,16 @@ z " transform="scale(0.015625)"/> - - - - - - - - - - + + + + + + + + + + @@ -21253,15 +21513,15 @@ z " transform="scale(0.015625)"/> - - - - - - - - - + + + + + + + + + @@ -21322,21 +21582,21 @@ z " transform="scale(0.015625)"/> - - - - - - - - - + + + + + + + + + - + diff --git a/graph.py b/graph.py index 7b285d211..de6b3057d 100644 --- a/graph.py +++ b/graph.py @@ -17,6 +17,7 @@ from graph_common import ( add_title, style_legend, add_reference_lines, + add_gnu_release_markers, ) if len(sys.argv) <= 2: @@ -100,6 +101,10 @@ style_axes(ax, xlabel="Date", ylabel="Number of Tests") y_max = df_plot_long["count_smooth"].max() add_reference_lines(ax, y_max) +# Add vertical bars for GNU coreutils releases +if title.lower() == "gnu": + add_gnu_release_markers(ax, df_plot["date"].min(), df_plot["date"].max(), y_max) + # Style legend handles, labels = ax.get_legend_handles_labels() labels = [label.capitalize() for label in labels] diff --git a/graph_common.py b/graph_common.py index 8e4397133..94363b053 100644 --- a/graph_common.py +++ b/graph_common.py @@ -200,6 +200,53 @@ def style_legend(ax, handles, labels, ncol=1, loc="upper left"): return legend +GNU_COREUTILS_RELEASES = [ + ("9.0", "2021-09-24"), + ("9.1", "2022-04-15"), + ("9.2", "2023-03-20"), + ("9.3", "2023-04-18"), + ("9.4", "2023-08-29"), + ("9.5", "2024-03-28"), + ("9.6", "2025-01-17"), + ("9.7", "2025-04-09"), + ("9.8", "2025-09-22"), + ("9.9", "2025-11-10"), + ("9.10", "2026-02-04"), + ("9.11", "2026-04-23"), +] + + +def add_gnu_release_markers(ax, x_min, x_max, y_max, releases=GNU_COREUTILS_RELEASES): + """Draw vertical bars at GNU coreutils release dates within the data range.""" + import pandas as pd + + x_min = pd.to_datetime(x_min, utc=True) + x_max = pd.to_datetime(x_max, utc=True) + for version, date_str in releases: + rel_date = pd.to_datetime(date_str, utc=True) + if x_min <= rel_date <= x_max: + ax.axvline( + x=rel_date, + color="#6B7280", + linestyle="--", + linewidth=1.2, + alpha=0.6, + zorder=2, + ) + ax.text( + rel_date, + y_max * 1.01, + f"v{version}", + rotation=90, + fontsize=10, + color="#374151", + ha="right", + va="top", + alpha=0.85, + zorder=4, + ) + + def add_reference_lines(ax, y_max): """Add horizontal reference lines at key values.