From ac2a5a67e9f63a232fbe4595e50d668d6216109f Mon Sep 17 00:00:00 2001 From: connor rigby Date: Tue, 7 May 2019 11:33:37 -0700 Subject: [PATCH] Add coveralls aggregator --- .circleci/config.yml | 117 ++++++++++++++++++++----- farmbot_ext/mix.exs | 7 ++ farmbot_os/mix.exs | 7 ++ farmbot_os/test/dummy_test.exs | 7 ++ farmbot_os/test/test_helper.exs | 1 + mix.exs | 49 +++++++++++ mix.lock | 19 ++++ support/mix.tasks.farmbot.coveralls.ex | 55 ++++++++++++ 8 files changed, 241 insertions(+), 21 deletions(-) create mode 100644 farmbot_os/test/dummy_test.exs create mode 100644 farmbot_os/test/test_helper.exs create mode 100644 mix.exs create mode 100644 mix.lock create mode 100644 support/mix.tasks.farmbot.coveralls.ex diff --git a/.circleci/config.yml b/.circleci/config.yml index ec28f11b..91d0dc67 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -59,9 +59,9 @@ build_firmware_steps: &build_firmware_steps echo "$MIX_TARGET" > MIX_TARGET echo "$MIX_ENV" > MIX_ENV - restore_cache: - key: v11-fbos-{{ checksum "MIX_TARGET" }}-{{ checksum "MIX_ENV" }}-dependency-cache-{{ checksum "farmbot_os/mix.lock" }} + key: v12-fbos-{{ checksum "MIX_TARGET" }}-{{ checksum "MIX_ENV" }}-dependency-cache-{{ checksum "farmbot_os/mix.lock" }} - restore_cache: - key: v11-fbos-host-test-dependency-cache-{{ checksum "farmbot_os/mix.lock" }} + key: v12-fbos-host-test-dependency-cache-{{ checksum "farmbot_os/mix.lock" }} - <<: *install_elixir - <<: *install_hex_archives - run: @@ -79,7 +79,7 @@ build_firmware_steps: &build_firmware_steps command: | cp /nerves/build/farmbot_os/_build/${MIX_TARGET}/${MIX_TARGET}_${MIX_ENV}/nerves/images/farmbot.fw /nerves/deploy/system/artifacts/farmbot-${MIX_TARGET}-$(cat VERSION).fw - save_cache: - key: v11-fbos-{{ checksum "MIX_TARGET" }}-{{ checksum "MIX_ENV" }}-dependency-cache-{{ checksum "farmbot_os/mix.lock" }} + key: v12-fbos-{{ checksum "MIX_TARGET" }}-{{ checksum "MIX_ENV" }}-dependency-cache-{{ checksum "farmbot_os/mix.lock" }} paths: - /nerves/build/farmbot_os/_build/ - /nerves/build/farmbot_os/deps/ @@ -99,7 +99,7 @@ deploy_nerves_hub_firmware_steps: &deploy_nerves_hub_firmware_steps echo "$MIX_TARGET" > MIX_TARGET echo "$MIX_ENV" > MIX_ENV - restore_cache: - key: v11-fbos-{{ checksum "MIX_TARGET" }}-{{ checksum "MIX_ENV" }}-dependency-cache-{{ checksum "farmbot_os/mix.lock" }} + key: v12-fbos-{{ checksum "MIX_TARGET" }}-{{ checksum "MIX_ENV" }}-dependency-cache-{{ checksum "farmbot_os/mix.lock" }} - restore_cache: key: nerves/deploy/system-{{ checksum "MIX_TARGET" }}-{{ .Branch }}-{{ .Revision }}-{{ .Environment.CIRCLE_TAG }} - <<: *install_elixir @@ -135,7 +135,7 @@ jobs: - checkout - restore_cache: keys: - - v11-fbcs-test-dependency-cache-{{ checksum "farmbot_celery_script/mix.lock" }} + - v12-fbcs-test-dependency-cache-{{ checksum "farmbot_celery_script/mix.lock" }} - <<: *install_elixir - <<: *install_hex_archives - run: @@ -145,12 +145,13 @@ jobs: mix deps.get mix compile mix format --check-formatted - mix test + mix coveralls.json - save_cache: - key: v11-fbcs-test-dependency-cache-{{ checksum "farmbot_celery_script/mix.lock" }} + key: v12-fbcs-test-dependency-cache-{{ checksum "farmbot_celery_script/mix.lock" }} paths: - farmbot_celery_script/_build/test - farmbot_celery_script/deps + - farmbot_celery_script/cover test_farmbot_firmware: <<: *defaults @@ -163,7 +164,7 @@ jobs: - checkout - restore_cache: keys: - - v11-fbfw-test-dependency-cache-{{ checksum "farmbot_firmware/mix.lock" }} + - v12-fbfw-test-dependency-cache-{{ checksum "farmbot_firmware/mix.lock" }} - <<: *install_elixir - <<: *install_hex_archives - run: @@ -173,12 +174,13 @@ jobs: mix deps.get mix compile mix format --check-formatted - mix test + mix coveralls.json - save_cache: - key: v11-fbfw-test-dependency-cache-{{ checksum "farmbot_firmware/mix.lock" }} + key: v12-fbfw-test-dependency-cache-{{ checksum "farmbot_firmware/mix.lock" }} paths: - farmbot_firmware/_build/test - farmbot_firmware/deps + - farmbot_firmware/cover test_farmbot_core: <<: *defaults @@ -193,10 +195,10 @@ jobs: - run: git submodule update --init --recursive - restore_cache: keys: - - v11-fbcore-test-dependency-cache-{{ checksum "farmbot_core/mix.lock" }} + - v12-fbcore-test-dependency-cache-{{ checksum "farmbot_core/mix.lock" }} - restore_cache: keys: - - v11-fbcore-test-arduino-dependency-cache-{{ checksum ".circleci/setup-arduino.sh" }} + - v12-fbcore-test-arduino-dependency-cache-{{ checksum ".circleci/setup-arduino.sh" }} - <<: *install_elixir - <<: *install_hex_archives - <<: *install_arduino @@ -211,15 +213,16 @@ jobs: mix deps.get mix compile mix format --check-formatted - mix test --trace + mix coveralls.json --trace - save_cache: - key: v11-fbcore-test-dependency-cache-{{ checksum "farmbot_core/mix.lock" }} + key: v12-fbcore-test-dependency-cache-{{ checksum "farmbot_core/mix.lock" }} paths: - farmbot_core/_build/test - farmbot_core/deps - farmbot_core/arduino + - farmbot_core/cover - save_cache: - key: v11-fbcore-test-arduino-dependency-cache-{{ checksum ".circleci/setup-arduino.sh" }} + key: v12-fbcore-test-arduino-dependency-cache-{{ checksum ".circleci/setup-arduino.sh" }} paths: - ~/arduino-1.8.5 - farmbot_core/_build/core @@ -241,7 +244,7 @@ jobs: - run: git submodule update --init --recursive - restore_cache: keys: - - v11-fbext-test-dependency-cache-{{ checksum "farmbot_core/mix.lock" }} + - v12-fbext-test-dependency-cache-{{ checksum "farmbot_ext/mix.lock" }} - <<: *install_elixir - <<: *install_hex_archives - run: @@ -253,12 +256,13 @@ jobs: mix format --check-formatted mix ecto.create mix ecto.migrate - mix test + mix coveralls.json - save_cache: - key: v11-fbext-test-dependency-cache-{{ checksum "farmbot_core/mix.lock" }} + key: v12-fbext-test-dependency-cache-{{ checksum "farmbot_ext/mix.lock" }} paths: - farmbot_ext/_build/test - farmbot_ext/deps + - farmbot_ext/cover test_farmbot_os: <<: *defaults @@ -271,7 +275,8 @@ jobs: - checkout - run: git submodule update --init --recursive - restore_cache: - key: v11-fbos-host-test-dependency-cache-{{ checksum "farmbot_os/mix.lock" }} + keys: + - v12-fbos-host-test-dependency-cache-{{ checksum "farmbot_os/mix.lock" }} - <<: *install_elixir - <<: *install_hex_archives - run: @@ -281,12 +286,57 @@ jobs: mix deps.get mix compile mix format --check-formatted - mix test + mix coveralls.json - save_cache: - key: v11-fbos-host-test-dependency-cache-{{ checksum "farmbot_os/mix.lock" }} + key: v12-fbos-host-test-dependency-cache-{{ checksum "farmbot_os/mix.lock" }} paths: - farmbot_os/_build/host - farmbot_os/deps/host + - farmbot_os/cover + + report_coverage: + <<: *defaults + environment: + MIX_ENV: test + MIX_TARGET: host + NERVES_LOG_DISABLE_PROGRESS_BAR: "yes" + ELIXIR_VERSION: 1.8.0 + steps: + - checkout + - run: git submodule update --init --recursive + - <<: *install_elixir + - <<: *install_hex_archives + - restore_cache: + keys: + - v12-fbsupport-test-dependency-cache-{{ checksum "mix.lock" }} + - restore_cache: + keys: + - v12-fbcs-test-dependency-cache-{{ checksum "farmbot_celery_script/mix.lock" }} + - restore_cache: + keys: + - v12-fbfw-test-dependency-cache-{{ checksum "farmbot_firmware/mix.lock" }} + - restore_cache: + keys: + - v12-fbcore-test-dependency-cache-{{ checksum "farmbot_core/mix.lock" }} + - restore_cache: + keys: + - v12-fbext-test-dependency-cache-{{ checksum "farmbot_ext/mix.lock" }} + - restore_cache: + keys: + - v12-fbos-host-test-dependency-cache-{{ checksum "farmbot_os/mix.lock" }} + - run: + name: Report Coverage + working_directory: /nerves/build/ + command: | + mix deps.get + mix compile + mix format --check-formatted + mix farmbot.coveralls circle + - save_cache: + key: v12-fbsupport-test-dependency-cache-{{ checksum "mix.lock" }} + paths: + - deps/ + - _build ################################################################################ # target=rpi app_env=prod # @@ -570,6 +620,21 @@ workflows: - staging - beta - next + - report_coverage: + context: org-global + requires: + - test_farmbot_celery_script + - test_farmbot_firmware + - test_farmbot_core + - test_farmbot_ext + - test_farmbot_os + filters: + branches: + ignore: + - master + - staging + - beta + - next # master branch to staging.farmbot.io nerves_hub_prod_stable_staging: @@ -734,6 +799,14 @@ workflows: branches: only: - next + - report_coverage: + context: org-global + requires: + - test_farmbot_celery_script + - test_farmbot_firmware + - test_farmbot_core + - test_farmbot_ext + - test_farmbot_os - build_rpi3_prod: context: farmbot-staging @@ -769,6 +842,7 @@ workflows: - next requires: - build_rpi3_prod + - report_coverage # - deploy_rpi_prod_next: # context: farmbot-staging @@ -778,3 +852,4 @@ workflows: # - next # requires: # - build_rpi_prod + # - report_coverage diff --git a/farmbot_ext/mix.exs b/farmbot_ext/mix.exs index 22a7eca4..e6c5e92a 100644 --- a/farmbot_ext/mix.exs +++ b/farmbot_ext/mix.exs @@ -10,6 +10,13 @@ defmodule FarmbotExt.MixProject do elixir: @elixir_version, start_permanent: Mix.env() == :prod, elixirc_paths: ["lib", "vendor"], + test_coverage: [tool: ExCoveralls], + preferred_cli_env: [ + coveralls: :test, + "coveralls.detail": :test, + "coveralls.post": :test, + "coveralls.html": :test + ], deps: deps() ] end diff --git a/farmbot_os/mix.exs b/farmbot_os/mix.exs index 386f0e51..2b5efbd6 100644 --- a/farmbot_os/mix.exs +++ b/farmbot_os/mix.exs @@ -26,6 +26,13 @@ defmodule FarmbotOS.MixProject do elixirc_paths: elixirc_paths(Mix.env(), Mix.target()), deps_path: "deps/#{Mix.target()}", build_path: "_build/#{Mix.target()}", + test_coverage: [tool: ExCoveralls], + preferred_cli_env: [ + coveralls: :test, + "coveralls.detail": :test, + "coveralls.post": :test, + "coveralls.html": :test + ], deps: deps() ] end diff --git a/farmbot_os/test/dummy_test.exs b/farmbot_os/test/dummy_test.exs new file mode 100644 index 00000000..cfcec06d --- /dev/null +++ b/farmbot_os/test/dummy_test.exs @@ -0,0 +1,7 @@ +defmodule DummyTest do + use ExUnit.Case + + test "delete me" do + assert true + end +end diff --git a/farmbot_os/test/test_helper.exs b/farmbot_os/test/test_helper.exs new file mode 100644 index 00000000..869559e7 --- /dev/null +++ b/farmbot_os/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start() diff --git a/mix.exs b/mix.exs new file mode 100644 index 00000000..90f207fd --- /dev/null +++ b/mix.exs @@ -0,0 +1,49 @@ +defmodule FarmbotSupport.MixProject do + ################################################################################ + ###### WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ###### + ###### This is not the root mix.exs the farmbot application ###### + ###### This OTP application is for test support only. ###### + ###### WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ###### + ################################################################################ + + use Mix.Project + @version Path.join([__DIR__, "VERSION"]) |> File.read!() |> String.trim() + @elixir_version Path.join([__DIR__, "ELIXIR_VERSION"]) |> File.read!() |> String.trim() + + def project do + [ + app: :farmbot_support, + version: @version, + elixir: @elixir_version, + start_permanent: Mix.env() == :prod, + elixirc_paths: ["support"], + test_coverage: [tool: ExCoveralls], + preferred_cli_env: [ + test: :test, + coveralls: :test, + "coveralls.circle": :test, + "coveralls.detail": :test, + "coveralls.post": :test, + "coveralls.html": :test + ], + deps: deps() + ] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [ + extra_applications: [:logger] + ] + end + + # Run "mix help deps" to learn about dependencies. + defp deps do + [ + {:jason, "~> 1.1"}, + {:excoveralls, "~> 0.10", only: [:test], targets: [:host]}, + {:dialyxir, "~> 1.0.0-rc.3", only: [:dev], targets: [:host], runtime: false}, + {:ex_doc, "~> 0.19", only: [:dev], targets: [:host], runtime: false} + ] + end +end diff --git a/mix.lock b/mix.lock new file mode 100644 index 00000000..0e120cd2 --- /dev/null +++ b/mix.lock @@ -0,0 +1,19 @@ +%{ + "certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"}, + "dialyxir": {:hex, :dialyxir, "1.0.0-rc.6", "78e97d9c0ff1b5521dd68041193891aebebce52fc3b93463c0a6806874557d7d", [:mix], [{:erlex, "~> 0.2.1", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm"}, + "earmark": {:hex, :earmark, "1.3.2", "b840562ea3d67795ffbb5bd88940b1bed0ed9fa32834915125ea7d02e35888a5", [:mix], [], "hexpm"}, + "erlex": {:hex, :erlex, "0.2.1", "cee02918660807cbba9a7229cae9b42d1c6143b768c781fa6cee1eaf03ad860b", [:mix], [], "hexpm"}, + "ex_doc": {:hex, :ex_doc, "0.20.2", "1bd0dfb0304bade58beb77f20f21ee3558cc3c753743ae0ddbb0fd7ba2912331", [:mix], [{:earmark, "~> 1.3", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.10", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"}, + "excoveralls": {:hex, :excoveralls, "0.11.0", "1427780f327902f099ef948bbc107b06a200a14a80e5badc1f9624d0f059235f", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"}, + "hackney": {:hex, :hackney, "1.15.1", "9f8f471c844b8ce395f7b6d8398139e26ddca9ebc171a8b91342ee15a19963f4", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"}, + "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"}, + "jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, + "makeup": {:hex, :makeup, "0.8.0", "9cf32aea71c7fe0a4b2e9246c2c4978f9070257e5c9ce6d4a28ec450a839b55f", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.13.0", "be7a477997dcac2e48a9d695ec730b2d22418292675c75aa2d34ba0909dcdeda", [:mix], [{:makeup, "~> 0.8", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"}, + "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"}, + "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm"}, + "nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm"}, + "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"}, + "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"}, +} diff --git a/support/mix.tasks.farmbot.coveralls.ex b/support/mix.tasks.farmbot.coveralls.ex new file mode 100644 index 00000000..7b6fc581 --- /dev/null +++ b/support/mix.tasks.farmbot.coveralls.ex @@ -0,0 +1,55 @@ +defmodule Mix.Tasks.Farmbot.Coveralls do + @moduledoc """ + Mix Task to report the coverage for all of the individual projects that make + up the repository. + """ + + use Mix.Task + Module.register_attribute(__MODULE__, :projects, accumulate: true) + @projects :farmbot_celery_script + @projects :farmbot_core + @projects :farmbot_ext + @projects :farmbot_firmware + @projects :farmbot_os + + def run(args) do + @projects + |> pmap(&read_coverage_json!/1) + |> List.flatten() + |> run_task(args) + end + + def run_task(stats, []) do + run_task(stats, ["local"]) + end + + def run_task(stats, ["local"]) do + ExCoveralls.Local.execute(stats, []) + end + + def run_task(stats, ["circle"]) do + ExCoveralls.Circle.execute(stats, []) + end + + def pmap(data, func) do + data + |> Enum.map(&(Task.async(fn -> func.(&1) end))) + |> Enum.map(&Task.await/1) + end + + def read_coverage_json!(project) do + coverage_file = Path.join([to_string(project), "cover", "excoveralls.json"]) + with {:ok, bin} <- File.read(coverage_file), + {:ok, json} <- Jason.decode(bin) do + Enum.map(json["source_files"], fn(%{"name" => name, "source" => source, "coverage" => coverage}) -> + %{name: Path.join([to_string(project), name]), source: source, coverage: coverage} + end) + else + _ -> Mix.raise(""" + Could not read coverage JSON from #{coverage_file}. + Make sure to run `mix coveralls.json` in each project's parent + directory. + """) + end + end +end \ No newline at end of file