Skip to content

Commit

Permalink
Supports lcov files generated from partitioned test runs (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
brianberlin authored Mar 8, 2024
1 parent 1393654 commit 7b0bee0
Show file tree
Hide file tree
Showing 8 changed files with 215 additions and 87 deletions.
19 changes: 10 additions & 9 deletions .github/workflows/push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ on:
pull_request:

env:
OTP_VERSION: 26.1.1
ELIXIR_VERSION: 1.15.6-otp-26
ELIXIR_VERSION: 1.16.1-otp-26
OTP_VERSION: 26.2.2
MIX_ENV: test

jobs:
test:
Expand All @@ -18,7 +19,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v2

- name: Elixir
uses: erlef/setup-beam@v1
with:
Expand All @@ -31,7 +32,7 @@ jobs:
with:
path: _build
key: build-${{ runner.os }}-${{ env.OTP_VERSION }}-${{ env.ELIXIR_VERSION }}-${{ hashFiles('mix.lock') }}

- name: Deps Cache
uses: actions/cache/restore@v3
id: deps-cache
Expand All @@ -42,7 +43,7 @@ jobs:
- name: Install Mix Dependencies
if: steps.deps-cache.outputs.cache-hit != 'true'
run: mix deps.get

- name: Compile
if: steps.build-cache.outputs.cache-hit != 'true'
run: mix compile
Expand All @@ -54,17 +55,17 @@ jobs:
run: mix credo --strict

- name: Run Tests
run: mix lcov
run: mix test --cover

- name: Coverage Reporter
uses: peek-travel/coverage-reporter@main
id: coverage-reporter
if: github.event_name == 'pull_request'
continue-on-error: true
with:
with:
lcov_path: cover/lcov.info
coverage_threshold: 90

- name: Restore PLT cache
uses: actions/cache@v2
id: plt-cache
Expand Down
38 changes: 38 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Main

on:
push:
branches:
- master
env:
MIX_ENV: prod
ELIXIR_VERSION: 1.16.1-otp-26
OTP_VERSION: 26.2.2

jobs:
build:
runs-on: ubuntu-latest
steps:

- name: Checkout
uses: actions/checkout@v4

- name: Elixir
uses: erlef/setup-beam@v1
with:
otp-version: ${{ env.OTP_VERSION }}
elixir-version: ${{ env.ELIXIR_VERSION }}

- name: Install Mix Dependencies
run: mix deps.get

- name: Escript Build
run: mix escript.build

- name: Commit Coverage Reporter Binary
uses: EndBug/[email protected]
with:
add: "coverage_reporter"
author_name: github-actions[bot]
author_email: github-actions[bot]@users.noreply.github.com
message: "[auto-commit] Update coverage_reporter binary"
2 changes: 2 additions & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
elixir 1.16.1-otp-26
erlang 26.2.2
20 changes: 5 additions & 15 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,17 +1,7 @@
ARG ELIXIR_VERSION=1.14.4
ARG OTP_VERSION=25.3.1
ARG DEBIAN_VERSION=bullseye-20230227-slim
FROM hexpm/elixir:1.16.1-erlang-26.2.2-ubuntu-jammy-20240125

ARG BUILDER_IMAGE="hexpm/elixir:${ELIXIR_VERSION}-erlang-${OTP_VERSION}-debian-${DEBIAN_VERSION}"
ARG RUNNER_IMAGE="debian:${DEBIAN_VERSION}"
COPY coverage_reporter /
COPY entrypoint.sh /
COPY deps/castore/priv/cacerts.pem /

FROM ${BUILDER_IMAGE} as builder

RUN apt-get update -y && apt-get install -y build-essential git \
&& apt-get clean && rm -f /var/lib/apt/lists/*_*

WORKDIR /app
COPY mix.exs mix.lock entrypoint.sh ./
COPY lib ./lib

ENTRYPOINT ["/app/entrypoint.sh"]
ENTRYPOINT ["/entrypoint.sh"]
10 changes: 3 additions & 7 deletions entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@ set -e
set -o pipefail
echo ">>> Running command"
echo ""
# bash -c "set -e; set -o pipefail; $1"
cd /app
mix local.hex --force
mix local.rebar --force
mix deps.get
mix compile
mix run --no-mix-exs -e "CoverageReporter.run()"


/coverage_reporter
59 changes: 28 additions & 31 deletions lib/coverage_reporter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@ defmodule CoverageReporter do
However, The LCOV files produced by excoveralls only include SF, DA, LF, LH, and end_of_record lines.
"""

def run(opts \\ []) do
def main(opts) do
config = get_config(opts)

%{pull_number: pull_number, head_branch: head_branch, repository: repository} = config
changed_files = get_changed_files(config)
{total, module_results} = get_coverage_from_lcov_files(config)
Expand Down Expand Up @@ -96,14 +95,15 @@ defmodule CoverageReporter do
table = :ets.new(__MODULE__, [:set, :private])

module_results =
Enum.flat_map(lcov_paths, fn path ->
Enum.reduce(lcov_paths, %{}, fn path, acc ->
path
|> File.stream!()
|> Stream.map(&String.trim(&1))
|> Stream.chunk_by(&(&1 == "end_of_record"))
|> Stream.reject(&(&1 == ["end_of_record"]))
|> Stream.map(fn record -> process_lcov_record(table, record) end)
|> Enum.reduce(acc, fn record, acc -> process_lcov_record(table, record, acc) end)
end)
|> Map.values()

covered = :ets.select_count(table, [{{{:_, :_}, true}, [], [true]}])
not_covered = :ets.select_count(table, [{{{:_, :_}, false}, [], [true]}])
Expand All @@ -112,7 +112,7 @@ defmodule CoverageReporter do
{total, module_results}
end

defp process_lcov_record(table, record) do
defp process_lcov_record(table, record, acc) do
"SF:" <> path = Enum.find(record, &String.starts_with?(&1, "SF:"))

coverage_by_line =
Expand All @@ -124,19 +124,22 @@ defmodule CoverageReporter do
|> String.split(",")
|> Enum.map(&String.to_integer(&1))

covered = :ets.select_count(table, [{{{path, line_number}, true}, [], [true]}])
insert_line_coverage(table, count, path, line_number)

{line_number, count}
{line_number, count + covered}
end)

covered = :ets.select_count(table, [{{{path, :_}, true}, [], [true]}])
not_covered = :ets.select_count(table, [{{{path, :_}, false}, [], [true]}])
{percentage(covered, not_covered), path, coverage_by_line}
Map.put(acc, path, {percentage(covered, not_covered), path, coverage_by_line})
end

defp insert_line_coverage(table, count, path, line_number) do
if count == 0 do
:ets.insert(table, {{path, line_number}, false})
covered_count = :ets.select_count(table, [{{{path, line_number}, true}, [], [true]}])

if count == 0 and covered_count == 0 do
:ets.insert_new(table, {{path, line_number}, false})
else
Enum.each(1..count, fn _ -> :ets.insert(table, {{path, line_number}, true}) end)
end
Expand Down Expand Up @@ -262,12 +265,12 @@ defmodule CoverageReporter do
|> Enum.reduce(_groups = [], &add_line_to_groups/2)
|> Enum.reduce(
_annotations = [],
&create_annotations(&1, &2, changed_lines, file, source_code)
&do_create_annotations(&1, &2, changed_lines, file, source_code)
)
end)
end

defp create_annotations(line_number_group, annotations, changed_lines, file, source_code) do
defp do_create_annotations(line_number_group, annotations, changed_lines, file, source_code) do
end_line = List.first(line_number_group)
start_line = List.last(line_number_group)

Expand Down Expand Up @@ -372,18 +375,12 @@ defmodule CoverageReporter do

defp github_request_all(config, path, params \\ %{page: 1}, accumulator \\ []) do
case github_request(config, method: :get, url: path, params: params) do
{:ok, 200, []} ->
{200, []} ->
{:ok, 200, accumulator}

{:ok, 200, results} ->
{200, results} ->
params = Map.put(params, :page, params[:page] + 1)
github_request_all(config, path, params, results ++ accumulator)

{:ok, status_code, result} ->
{:ok, status_code, result}

{:error, reason} ->
{:error, reason}
end
end

Expand All @@ -400,16 +397,18 @@ defmodule CoverageReporter do
{:user_agent, "CoverageReporter"}
]

options = Keyword.merge(opts, base_url: github_api_url, headers: headers)
options =
Keyword.merge(opts,
base_url: github_api_url,
headers: headers,
connect_options: [transport_opts: [cacertfile: "/cacerts.pem"]]
)

request = Req.new(options)

case Req.request(request) do
{_request, %{status: status, body: body}} ->
{:ok, status, body}
{_request, %{status: status, body: body}} = Req.request(request)

{:error, reason} ->
{:error, reason}
end
{status, body}
end

defp get_config(opts) do
Expand Down Expand Up @@ -442,11 +441,9 @@ defmodule CoverageReporter do
do: opts[:lcov_path_prefix] || System.get_env("INPUT_LCOV_PATH_PREFIX")

defp pull_number(opts) do
github_ref = (opts[:github_ref] || System.get_env("GITHUB_REF")) |> String.split("/")
["refs", "pull", pr_number, "merge"] =
(opts[:github_ref] || System.get_env("GITHUB_REF")) |> String.split("/")

case github_ref do
["refs", "pull", pr_number, "merge"] -> pr_number
_ -> raise "Could not find pull request number."
end
pr_number
end
end
9 changes: 6 additions & 3 deletions mix.exs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
defmodule CoverageReporter.MixProject do
use Mix.Project

@app :coverage_reporter

def project do
[
app: :coverage_reporter,
app: @app,
version: "0.1.0",
elixir: "~> 1.14",
start_permanent: Mix.env() == :prod,
deps: deps()
deps: deps(),
test_coverage: [tool: LcovEx],
escript: [main_module: CoverageReporter]
]
end

Expand Down
Loading

0 comments on commit 7b0bee0

Please sign in to comment.