Skip to content

Commit 0c3a9fe

Browse files
authored
Integrate trace reports into build and test actions
1 parent faf4ae4 commit 0c3a9fe

8 files changed

Lines changed: 218 additions & 14 deletions

File tree

.github/actions/build/action.yaml

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,15 @@ outputs:
5959
build_duration_seconds:
6060
description: "Elapsed time of the ya make build step in seconds"
6161
value: ${{ steps.build.outputs.build_duration_seconds }}
62+
trace_report_url:
63+
description: "URL to the static ya build trace"
64+
value: ${{ steps.build-trace.outputs.html_url }}
65+
trace_otlp_url:
66+
description: "URL to the raw ya build OTLP bundle"
67+
value: ${{ steps.build-trace.outputs.otlp_url }}
68+
trace_inputs_url:
69+
description: "URL to the archived raw ya build trace inputs"
70+
value: ${{ steps.build-trace.outputs.inputs_url }}
6271

6372
runs:
6473
using: composite
@@ -264,6 +273,7 @@ runs:
264273
while [ "$attempt" -lt "$RETRIES" ]; do
265274
attempt=$((attempt + 1))
266275
echo "ya build attempt ${attempt}/${RETRIES}"
276+
TRACE_START_TIME_NS=$(date +%s%N)
267277
date
268278
EXIT_CODE_FILE=$(mktemp)
269279
set +e
@@ -273,6 +283,7 @@ runs:
273283
} |& tee "$TMP_DIR/ya_make_output.txt"
274284
set -e
275285
YA_MAKE_EXIT_CODE=$(< "$EXIT_CODE_FILE")
286+
TRACE_END_TIME_NS=$(date +%s%N)
276287
rm -f "$EXIT_CODE_FILE"
277288
if [ "$YA_MAKE_EXIT_CODE" = "0" ]; then
278289
break
@@ -288,8 +299,60 @@ runs:
288299
BUILD_DURATION_SECONDS=$((BUILD_END_TIME - BUILD_START_TIME))
289300
echo "BUILD_DURATION_SECONDS=$BUILD_DURATION_SECONDS" | tee -a "$GITHUB_ENV"
290301
echo "build_duration_seconds=$BUILD_DURATION_SECONDS" | tee -a "$GITHUB_OUTPUT"
291-
echo "YA_MAKE_EXIT_CODE=$YA_MAKE_EXIT_CODE" >> "$GITHUB_ENV"
302+
{
303+
echo "YA_MAKE_EXIT_CODE=$YA_MAKE_EXIT_CODE"
304+
echo "YA_BUILD_ATTEMPT=$attempt"
305+
echo "TRACE_START_TIME_NS=$TRACE_START_TIME_NS"
306+
echo "TRACE_END_TIME_NS=$TRACE_END_TIME_NS"
307+
} >> "$GITHUB_ENV"
292308
date
309+
- name: Install trace dependencies
310+
if: always() && env.YA_MAKE_EXIT_CODE != ''
311+
continue-on-error: true
312+
shell: bash
313+
run: |
314+
pip install -r .github/scripts/requirements.txt
315+
- name: Render build trace
316+
id: build-trace
317+
if: always() && env.YA_MAKE_EXIT_CODE != ''
318+
continue-on-error: true
319+
shell: bash
320+
env:
321+
BUILD_PRESET: ${{ inputs.build_preset }}
322+
BUILD_TARGET: ${{ inputs.build_target }}
323+
run: |
324+
set -euo pipefail
325+
export PYTHONPATH="${PYTHONPATH:-}:$GITHUB_WORKSPACE/.github"
326+
BUILD_TARGET=${BUILD_TARGET//\"/}
327+
TRACE_REPORT_URL="${S3_WEBSITE_PREFIX}/build_logs/${COMPONENT_DIR}trace.html"
328+
TRACE_OTLP_URL="${S3_WEBSITE_PREFIX}/build_logs/${COMPONENT_DIR}trace.otlp.jsonl.gz"
329+
TRACE_INPUTS_URL="${S3_WEBSITE_PREFIX}/build_logs/${COMPONENT_DIR}trace-inputs.tar.gz"
330+
if [ -n "${S3_REPORTS_WEBSITE_PREFIX:-}" ]; then
331+
TRACE_REPORT_URL="${S3_REPORTS_WEBSITE_PREFIX}/${COMPONENT_DIR}build/trace.html"
332+
TRACE_OTLP_URL="${S3_REPORTS_WEBSITE_PREFIX}/${COMPONENT_DIR}build/trace.otlp.jsonl.gz"
333+
TRACE_INPUTS_URL="${S3_REPORTS_WEBSITE_PREFIX}/${COMPONENT_DIR}build/trace-inputs.tar.gz"
334+
fi
335+
bash "$GITHUB_WORKSPACE/.github/scripts/tracing/render_ya_trace_bundle.sh" \
336+
--warning-title "Build trace" \
337+
--report-dir "$TMP_DIR" \
338+
--ya-out "$TMP_DIR" \
339+
--evlog "$TMP_DIR/ya_evlog.jsonl" \
340+
--html-url "$TRACE_REPORT_URL" \
341+
--otlp-url "$TRACE_OTLP_URL" \
342+
--inputs-url "$TRACE_INPUTS_URL" \
343+
--summary "$GITHUB_STEP_SUMMARY" \
344+
--summary-heading "Build trace" \
345+
--github-output "$GITHUB_OUTPUT" \
346+
-- \
347+
--attempt-start-ns "$TRACE_START_TIME_NS" \
348+
--attempt-end-ns "$TRACE_END_TIME_NS" \
349+
--exit-code "$YA_MAKE_EXIT_CODE" \
350+
--result-code "$YA_MAKE_EXIT_CODE" \
351+
--component "${COMPONENT_DIR%/}" \
352+
--build-preset "$BUILD_PRESET" \
353+
--build-target "$BUILD_TARGET" \
354+
--retry "$YA_BUILD_ATTEMPT" \
355+
--operation build
293356
- name: Summarize build errors
294357
id: build-errors
295358
if: env.YA_MAKE_EXIT_CODE != '' && env.YA_MAKE_EXIT_CODE != '0'
@@ -321,7 +384,21 @@ runs:
321384
shell: bash
322385
run: |
323386
echo "::group::s3-sync"
324-
aws s3 sync --acl public-read --no-progress "$TMP_DIR/" "$S3_BUCKET_PATH/build_logs/${COMPONENT_DIR}"
387+
aws s3 sync --acl public-read --no-progress \
388+
--exclude ".trace-inputs.*.tar.gz.tmp" \
389+
--exclude "trace-inputs.files" \
390+
"$TMP_DIR/" \
391+
"$S3_BUCKET_PATH/build_logs/${COMPONENT_DIR}"
392+
if [ -n "${S3_REPORTS_BUCKET_PATH:-}" ]; then
393+
aws s3 sync --acl public-read --follow-symlinks --no-progress \
394+
--exclude "*" \
395+
--include "trace.html" \
396+
--include "trace.manifest.json" \
397+
--include "trace.otlp.jsonl.gz" \
398+
--include "trace-inputs.tar.gz" \
399+
"$TMP_DIR/" \
400+
"$S3_REPORTS_BUCKET_PATH/${COMPONENT_DIR}build/"
401+
fi
325402
echo "::endgroup::"
326403
- name: Create directory listing on s3
327404
if: always()

.github/actions/test/action.yaml

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,11 @@ runs:
159159
echo -n "$GITHUB_TOKEN" > "$GITHUB_TOKEN_FILE"
160160
echo "GITHUB_TOKEN_FILE=$GITHUB_TOKEN_FILE" >> "$GITHUB_ENV"
161161
chmod 600 "$GITHUB_TOKEN_FILE"
162+
- name: Install trace dependencies
163+
continue-on-error: true
164+
shell: bash
165+
run: |
166+
pip install -r .github/scripts/requirements.txt
162167
- name: ya test
163168
id: check_test_results
164169
shell: bash --noprofile --norc -eo pipefail -x {0}
@@ -299,13 +304,14 @@ runs:
299304
300305
echo "RETRIES=${RETRIES}" | tee -a "$GITHUB_ENV"
301306
echo "report_generated=no" >> "$GITHUB_OUTPUT"
302-
RETRY_FLAG=""
307+
retry_params=()
303308
i=0
304309
echo "LAST_STEP=0" | tee -a "$GITHUB_ENV"
305310
# setting variable in case that test will be interrupter by OOM killer that
306311
# will kill VM with runner
307312
echo "failed_tests=yes" >> $GITHUB_OUTPUT
308313
while [ "$RETRIES" -gt 0 ]; do
314+
TRACE_START_TIME_NS=$(date +%s%N)
309315
TEST_START_TIME=$(date +%s)
310316
echo TEST_START_TIME=$TEST_START_TIME | tee -a "$GITHUB_ENV"
311317
RETRIES=$((RETRIES - 1))
@@ -338,7 +344,7 @@ runs:
338344
--junit "${JUNIT_REPORT_XML}.${i}" \
339345
--log-file "$RETRY_LOG_DIR/ya_log.txt" \
340346
--evlog-file "$RETRY_LOG_DIR/ya_evlog.jsonl" \
341-
"$RETRY_FLAG"
347+
"${retry_params[@]}"
342348
echo $? > "$EXIT_CODE_FILE"
343349
} |& tee "$RETRY_LOG_DIR/ya_make_output.txt"
344350
set -e
@@ -376,7 +382,7 @@ runs:
376382
echo "ya test returned $RC after timeout killed it with SIGKILL"
377383
ATTEMPT_TIMED_OUT=1
378384
DO_RETRY=1
379-
RETRY_FLAG="-X"
385+
retry_params=(-X)
380386
else
381387
echo "ya test returned $RC it was killed by OOM killer"
382388
if [ $OOM_CAN_BE_RETRIED -eq 0 ]; then
@@ -402,11 +408,12 @@ runs:
402408
# disabling retries here for now until we will be able to
403409
# process junit with retried runs
404410
DO_RETRY=1
405-
RETRY_FLAG="-X"
411+
retry_params=(-X)
406412
;;
407413
esac
408414
409415
touch "$LOG_DIR/${i}_build_finished"
416+
TRACE_END_TIME_NS=$(date +%s%N)
410417
TEST_END_TIME=$(date +%s)
411418
echo TEST_END_TIME=$TEST_END_TIME | tee -a "$GITHUB_ENV"
412419
@@ -573,6 +580,15 @@ runs:
573580
fi
574581
fi
575582
583+
REPORT_DIR="$ARTIFACTS_DIR/summary/${COMPONENT_DIR}${i}"
584+
TRACE_REPORT_URL="$S3_WEBSITE_PREFIX/summary/${COMPONENT_DIR}${i}/trace.html"
585+
TRACE_OTLP_URL="$S3_WEBSITE_PREFIX/summary/${COMPONENT_DIR}${i}/trace.otlp.jsonl.gz"
586+
TRACE_INPUTS_URL="$S3_WEBSITE_PREFIX/summary/${COMPONENT_DIR}${i}/trace-inputs.tar.gz"
587+
if [ -n "${S3_REPORTS_WEBSITE_PREFIX:-}" ]; then
588+
TRACE_REPORT_URL="$S3_REPORTS_WEBSITE_PREFIX/${COMPONENT_DIR}${i}/trace.html"
589+
TRACE_OTLP_URL="$S3_REPORTS_WEBSITE_PREFIX/${COMPONENT_DIR}${i}/trace.otlp.jsonl.gz"
590+
TRACE_INPUTS_URL="$S3_REPORTS_WEBSITE_PREFIX/${COMPONENT_DIR}${i}/trace-inputs.tar.gz"
591+
fi
576592
export SUMMARY_OUT_ENV_PATH=$(mktemp -p /home/github)
577593
set +x
578594
GITHUB_TOKEN="$(cat "$GITHUB_TOKEN_FILE")"
@@ -588,6 +604,7 @@ runs:
588604
--test-target "${TEST_TARGET//\"/}" \
589605
--test-time $((TEST_END_TIME - TEST_START_TIME)) \
590606
--build-error-log-url "$BUILD_ERROR_LOG_URL_FOR_ITERATION" \
607+
--trace-report-url "$TRACE_REPORT_URL" \
591608
"Tests" ya-test.html "${JUNIT_REPORT_XML}.${i}"
592609
unset GITHUB_TOKEN
593610
echo "report_generated=yes" >> "$GITHUB_OUTPUT"
@@ -598,6 +615,29 @@ runs:
598615
echo "[Extracted build errors]($BUILD_ERROR_LOG_URL_FOR_ITERATION)"
599616
} >> "$SUMMARY_OUT_ENV_PATH"
600617
fi
618+
PYTHONPATH="$PYTHONPATH:$GITHUB_WORKSPACE/.github" \
619+
bash "$GITHUB_WORKSPACE/.github/scripts/tracing/render_ya_trace_bundle.sh" \
620+
--warning-title "Trace report" \
621+
--report-dir "$REPORT_DIR" \
622+
--ya-out "$OUT_DIR" \
623+
--evlog "$LOG_DIR/${i}/ya_evlog.jsonl" \
624+
--html-url "$TRACE_REPORT_URL" \
625+
--otlp-url "$TRACE_OTLP_URL" \
626+
--inputs-url "$TRACE_INPUTS_URL" \
627+
--summary "$SUMMARY_OUT_ENV_PATH" \
628+
-- \
629+
--attempt-start-ns "$TRACE_START_TIME_NS" \
630+
--attempt-end-ns "$TRACE_END_TIME_NS" \
631+
--exit-code "$RC" \
632+
--result-code "$FAIL_CHECKER_RC" \
633+
--component "${COMPONENT_DIR%/}" \
634+
--build-preset "$BUILD_PRESET" \
635+
--test-target "$TEST_TARGET" \
636+
--test-type "$TEST_TYPE" \
637+
--test-size "$TEST_SIZE" \
638+
--retry "$i" \
639+
--test-log-url-prefix "$S3_WEBSITE_PREFIX/logs/${COMPONENT_DIR}${i}/" \
640+
--test-data-url-prefix "$S3_WEBSITE_PREFIX/test_data/${COMPONENT_DIR}${i}$GITHUB_WORKSPACE/"
601641
cat $SUMMARY_OUT_ENV_PATH | tee -a $GITHUB_STEP_SUMMARY
602642
echo "::endgroup::"
603643
@@ -613,11 +653,21 @@ runs:
613653
fi
614654
615655
echo "::group::s3-sync"
616-
aws s3 sync --acl public-read --follow-symlinks --no-progress "$ARTIFACTS_DIR/" "$S3_BUCKET_PATH/"
617-
if [ -n "${S3_REPORTS_BUCKET_PATH:-}" ] && [ -f "$ARTIFACTS_DIR/summary/${COMPONENT_DIR}${i}/summary.json" ]; then
618-
aws s3 cp --acl public-read --follow-symlinks --no-progress \
619-
"$ARTIFACTS_DIR/summary/${COMPONENT_DIR}${i}/summary.json" \
620-
"$S3_REPORTS_BUCKET_PATH/${COMPONENT_DIR}${i}/summary.json"
656+
aws s3 sync --acl public-read --follow-symlinks --no-progress \
657+
--exclude "*.trace-inputs.*.tar.gz.tmp" \
658+
--exclude "*trace-inputs.files" \
659+
"$ARTIFACTS_DIR/" \
660+
"$S3_BUCKET_PATH/"
661+
if [ -n "${S3_REPORTS_BUCKET_PATH:-}" ]; then
662+
aws s3 sync --acl public-read --follow-symlinks --no-progress \
663+
--exclude "*" \
664+
--include "summary.json" \
665+
--include "trace.html" \
666+
--include "trace.manifest.json" \
667+
--include "trace.otlp.jsonl.gz" \
668+
--include "trace-inputs.tar.gz" \
669+
"$REPORT_DIR/" \
670+
"$S3_REPORTS_BUCKET_PATH/${COMPONENT_DIR}${i}/"
621671
fi
622672
touch "$LOG_DIR/${i}_artifacts_synced"
623673
echo "::endgroup::"

.github/packer/scripts/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@ PyGithub==2.9.1
2626
nebius==0.3.59
2727
tabulate
2828
numpy
29+
opentelemetry-proto-json==0.65b0

.github/scripts/tests/generate_summary.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,7 @@ def render_testlist_html(
454454
fn: str,
455455
summary_url: str,
456456
build_error_log_url: str = "",
457+
trace_report_url: str = "",
457458
) -> None:
458459
templates_path = Path(__file__).with_name("templates")
459460

@@ -482,6 +483,7 @@ def render_testlist_html(
482483
summary_url=summary_url,
483484
build_error_log_url=build_error_log_url,
484485
build_error_target_url=build_error_target_url,
486+
trace_report_url=trace_report_url,
485487
)
486488

487489
with open(fn, "w") as fp:
@@ -557,6 +559,7 @@ def gen_summary(
557559
summary_out_folder: str,
558560
paths: list[TitlePathTriplet],
559561
build_error_log_url: str = "",
562+
trace_report_url: str = "",
560563
) -> TestSummary:
561564
summary = TestSummary()
562565

@@ -577,6 +580,7 @@ def gen_summary(
577580
os.path.join(summary_out_folder, html_fn),
578581
summary_url=summary_url_prefix,
579582
build_error_log_url=build_error_log_url,
583+
trace_report_url=trace_report_url,
580584
)
581585
summary_line.add_report(html_fn, report_url)
582586
summary.add_line(summary_line)
@@ -1377,6 +1381,7 @@ def main() -> None:
13771381
)
13781382
parser.add_argument("--test-time", default="0", required=False)
13791383
parser.add_argument("--build-error-log-url", default="", required=False)
1384+
parser.add_argument("--trace-report-url", default="", required=False)
13801385
parser.add_argument(
13811386
"--workload-status",
13821387
choices=("in_progress", "completed"),
@@ -1405,6 +1410,7 @@ def main() -> None:
14051410
args.summary_out_path,
14061411
title_path,
14071412
build_error_log_url=args.build_error_log_url,
1413+
trace_report_url=args.trace_report_url,
14081414
)
14091415
write_summary(
14101416
summary,

.github/scripts/tests/generate_summary_test.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ def test_gen_summary_creates_html_and_aggregates_counters(
4545
"https://summary/",
4646
str(tmp_path),
4747
[("Tests", "ya-test.html", str(xml_path))],
48+
trace_report_url="https://reports.example/trace.html",
4849
)
4950

5051
assert summary.is_empty is False
@@ -60,6 +61,8 @@ def test_gen_summary_creates_html_and_aggregates_counters(
6061
assert 'id="FAIL"' in html
6162
assert "a/fail" in html
6263
assert "Summary dir file listing" in html
64+
assert 'href="https://reports.example/trace.html"' in html
65+
assert "best-effort basis and may be missing" in html
6366

6467

6568
def test_gen_summary_links_build_errors_in_fail_build_html(

.github/scripts/tests/templates/summary.html

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,13 @@
4040
</head>
4141

4242
<body>
43-
<p><a href="{{ summary_url }}index.html">Summary dir file listing</a></p>
43+
<p>
44+
<a href="{{ summary_url }}index.html">Summary dir file listing</a>
45+
{% if trace_report_url %}
46+
| <a href="{{ trace_report_url }}"
47+
title="The trace report is generated on a best-effort basis and may be missing">Ya make trace</a>
48+
{% endif %}
49+
</p>
4450
{% for status in status_order %}
4551
<h1 id="{{ status.name }}">{{ status.name }} ({{ tests[status] | length }})</h1>
4652
{% if status.name == "FAIL_BUILD" and build_error_log_url %}

.github/workflows/README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,48 @@ For validating syntax use action-validator
44
find .github/workflows .github/actions -type f \( -iname \*.yaml -o -iname \*.yml \) -print | while read path; do echo Checking $path; action-validator --verbose $path; done
55
```
66

7+
## Static execution traces
8+
9+
Each `ya make` test attempt writes these files beside `summary.json`:
10+
11+
- `trace.otlp.jsonl.gz`: canonical OTLP/JSON Lines spans;
12+
- `trace.manifest.json`: bundle metadata and span counts;
13+
- `trace.html`: a self-contained, searchable waterfall.
14+
15+
The HTML test summary links to `trace.html`; the link warns that trace
16+
generation is best-effort and its target may be missing.
17+
18+
The standalone `build` action writes the same three files beside its build
19+
logs and copies them to the workflow trace reports prefix. Its bundle has a
20+
`ya make build` root with graph, cache, build-operation, and critical-path
21+
spans, but no test chunks.
22+
23+
Observed `subtest-started`/`subtest-finished` events become test spans. Older
24+
finish-only events use the reported test duration and are marked with
25+
`test.timing.inferred=true`. Ya recipe-stage durations do not have absolute
26+
timestamps, so their `ya.test.stage` spans are positioned from the chunk start
27+
and marked as inferred while retaining the reported duration.
28+
29+
The per-attempt ya event log supplies absolute timing for graph generation,
30+
execution, and report-finalization phase spans. Completed worker nodes become
31+
searchable build spans for compilation, linking, archive creation, cache
32+
restores, and result materialization. Test worker nodes are matched to their
33+
logical chunks and provide worker and worker-phase spans around the test tree.
34+
The `build operations` span reports both its wall-clock execution envelope and
35+
the cumulative worker-node time; the latter can be larger because nodes run in
36+
parallel. It also carries ya's authoritative considered-task cache statistics,
37+
observed test-excluded worker-node cache ratios (including per-tool `CC`, `AR`,
38+
`LD`, and similar breakdowns), total task reuse/avoidance, execution-stage wall
39+
times, distributed-cache I/O, and the build-only portion of ya's reported
40+
critical path. Critical-path build nodes are marked on their individual spans.
41+
42+
To render a saved OTLP bundle locally:
43+
44+
```bash
45+
PYTHONPATH=.github python3 -m scripts.tracing.trace_report \
46+
trace.otlp.jsonl.gz -o trace.html
47+
```
48+
749
You can use [act](https://github.com/nektos/act) as a debugging tool for pipelines it acts as a GitHub runner of some sort, using docker.
850

951
It is not 100% replacement for GitHub actions altogether (i.e. you can't run self-hosted GitHub runners), but you can use it to debug some of your changes before committing

0 commit comments

Comments
 (0)