Skip to content

Commit

Permalink
feat(bitrisescript): scan bitrise.log for PERFHERDER_DATA and dump it…
Browse files Browse the repository at this point in the history
… to stdout

Perfherder is hardcoded to scan `public/live.log` for PERFHERDER_DATA.
Since this shows up in `<workflow>/bitrise.log`, we need logic to
forward these lines over to the main log.
  • Loading branch information
ahal committed Apr 19, 2024
1 parent d43ac3c commit da2aad9
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 1 deletion.
26 changes: 26 additions & 0 deletions bitrisescript/src/bitrisescript/bitrise.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import asyncio
import logging
import os
from pathlib import Path
from pprint import pformat
from typing import Any

Expand Down Expand Up @@ -183,6 +184,30 @@ async def download_log(build_slug: str, artifacts_dir: str, poll_interval: int =
log.error(f"Bitrise has no log for build '{build_slug}'. Please check https://app.bitrise.io/build/{build_slug}")


async def dump_perfherder_data(artifacts_dir: str) -> None:
"""Dumps any detected PERFHERDER_DATA log lines to stdout.
Perfherder is hardcoded to parse `public/live_backing.log` for lines that
start with PERFHERDER_DATA. Scan the bitrise log and forward any such lines
to stdout so Perfherder can find them.
Once bug 1646502 is fixed and all tasks relying on this function have been
migrated over to the new ingestion format, we can remove this.
Args:
artifacts_dir (str): Path to the artifact directory.
"""
path = Path(artifacts_dir) / "bitrise.log"
if not path.is_file():
log.warning(f"Not scanning for Perfherder data, {path} does not exist!")
return

perfherder_data_lines = [line for line in path.read_text().splitlines() if line.startswith("PERFHERDER_DATA")]
if perfherder_data_lines:
perfherder_data_str = "\n".join(perfherder_data_lines)
log.info(f"Found Perfherder data in {path}:\n{perfherder_data_str}")


async def download_file(download_url: str, file_destination: str, chunk_size: int = 512) -> None:
"""Download a file.
Expand Down Expand Up @@ -240,3 +265,4 @@ async def run_build(artifacts_dir: str, workflow_id: str, **build_params: Any) -
finally:
log.info(f"Retrieving bitrise log for '{build_slug}'...")
await download_log(build_slug, artifacts_dir)
await dump_perfherder_data(artifacts_dir)
53 changes: 52 additions & 1 deletion bitrisescript/tests/test_bitrise.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from asyncio import Future
import inspect
import logging
from asyncio import Future
from contextlib import nullcontext as does_not_raise
from textwrap import dedent

import pytest
import pytest_asyncio
Expand Down Expand Up @@ -228,6 +230,55 @@ async def test_download_log(mocker, client):
assert m_download.call_args == ((log_url, f"{artifacts_dir}/bitrise.log"),)


@pytest.mark.asyncio
async def test_dump_perfherder_data(mocker, caplog):
caplog.set_level(logging.INFO)
artifacts_dir = "artifacts"

# bitrise.log doesn't exist
mock_is_file = mocker.patch.object(bitrise.Path, "is_file")
mock_is_file.return_value = False
await bitrise.dump_perfherder_data(artifacts_dir)
assert "Not scanning for Perfherder data" in caplog.text

# bitrise.log doesn't contain Perfherder data
caplog.clear()
mock_is_file.return_value = True
mock_read_text = mocker.patch.object(bitrise.Path, "read_text")
mock_read_text.return_value = dedent(
"""
INFO does not contain
DEBUG any perfherder data
"""
).strip()
await bitrise.dump_perfherder_data(artifacts_dir)
assert "Not scanning for Perfherder data" not in caplog.text
assert "Found Perfherder data in" not in caplog.text

# bitrise.log contains Perfherder data
caplog.clear()
mock_read_text.return_value = dedent(
"""
INFO does contain
PERFHERDER_DATA {"foo": "bar"}
DEBUG perfherder data
PERFHERDER_DATA {"baz": 1}
"""
).strip()
await bitrise.dump_perfherder_data(artifacts_dir)
assert "Not scanning for Perfherder data" not in caplog.text
assert (
dedent(
"""
Found Perfherder data in artifacts/bitrise.log:
PERFHERDER_DATA {"foo": "bar"}
PERFHERDER_DATA {"baz": 1}
"""
).strip()
in caplog.text
)


@pytest.mark.asyncio
async def test_download_file(responses, tmp_path):
url = "https://example.com/log.txt"
Expand Down

0 comments on commit da2aad9

Please sign in to comment.