From 23a2a2cd18389321d2f608531623e218b82b9c88 Mon Sep 17 00:00:00 2001 From: Miguel Dalberto Pedro Date: Thu, 24 Sep 2026 18:31:39 -0300 Subject: [PATCH] Send who requested the build in the metrics payload Adds --buildUserEmail and --changeAuthorEmail to uploadMetadata.py, sent as the top-level build_user_email and change_author_email keys on every schema version, v1 per-design fallback included. Blank values are left out. The dashboard stores them on the build and attributes the build to the user who started it, so a "my branches" view can include builds a user started by hand. They describe the build rather than the payload's shape, so they do not bump payload_schema_version. The backend must already register them as metadata before this lands: a v1 message carries metrics at its root, and an older backend would ingest the keys as metrics. Signed-off-by: Miguel Dalberto Pedro --- flow/util/uploadMetadata.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/flow/util/uploadMetadata.py b/flow/util/uploadMetadata.py index 6918ccf2b4..4cd7a71854 100755 --- a/flow/util/uploadMetadata.py +++ b/flow/util/uploadMetadata.py @@ -51,6 +51,21 @@ "pinned commit rather than the built one. When supplied alongside " "--jobName the payload is emitted as schema v4.", ) +parser.add_argument( + "--buildUserEmail", + type=str, + default=None, + help="Email of the Jenkins user who started the build by hand " + "(BUILD_USER_EMAIL). Omitted for SCM, timer and branch-scan triggers. " + "The dashboard attributes the build to this user.", +) +parser.add_argument( + "--changeAuthorEmail", + type=str, + default=None, + help="Email of the pull request's author on a PR build " + "(CHANGE_AUTHOR_EMAIL). Empty when the author's GitHub email is private.", +) # --- PUBSUB args --- parser.add_argument( @@ -193,6 +208,23 @@ def resolve_schema_version(args, provenance): return 4 if provenance else 3 +def resolve_requester(args): + """Who asked for the build, as the payload keys the backend reads. + + Blank values are left out rather than sent empty. The keys ride on every + schema version: the backend reads them by presence, and they describe the + build rather than the payload's shape, so they do not bump the version. + """ + requester = {} + for key, value in ( + ("build_user_email", args.buildUserEmail), + ("change_author_email", args.changeAuthorEmail), + ): + if value and value.strip(): + requester[key] = value.strip() + return requester + + def build_pipeline_payload(design_records, args, provenance=None): """Return the pipeline-level payload dict. @@ -220,6 +252,7 @@ def build_pipeline_payload(design_records, args, provenance=None): # the payload. if schema_version == 4: payload.update(provenance) + payload.update(resolve_requester(args)) return payload @@ -261,6 +294,7 @@ def publish_v1_per_design(publisher, topic_path, design_records, args, provenanc key presence rather than by version. """ futures = [] + requester = resolve_requester(args) for d in design_records: payload = { "build_id": args.buildID, @@ -273,6 +307,7 @@ def publish_v1_per_design(publisher, topic_path, design_records, args, provenanc } if provenance: payload.update(provenance) + payload.update(requester) payload.update(d["metrics"]) message_data = json.dumps(payload, default=str).encode("utf-8")