Skip to content

Commit

Permalink
Add support for GitHub Actions
Browse files Browse the repository at this point in the history
  • Loading branch information
mijailr committed Nov 24, 2019
1 parent 7c90516 commit a26a09e
Show file tree
Hide file tree
Showing 10 changed files with 280 additions and 0 deletions.
49 changes: 49 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
name: build
env:
MIX_ENV: test
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

on:
push:
branches:
- master
pull_request:

jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Setup elixir
uses: actions/[email protected]
with:
elixir-version: 1.9.x
otp-version: 22.x

- name: Checkout repository
uses: actions/checkout@v1

- name: Get deps cache
uses: actions/cache@v1
with:
path: deps/
key: ${{ runner.os }}-deps-${{ hashFiles('**/mix.lock') }}
restore-keys: ${{ runner.os }}-deps-

- name: Get build cache
uses: actions/cache@v1
with:
path: _build/test/
key: ${{ runner.os }}-build-${{ hashFiles('**/mix.lock') }}
restore-keys: ${{ runner.os }}-build-

- name: Install Dependencies
run: |
mix local.rebar --force
mix local.hex --force
mix deps.get
mix compile
- name: Run Tests
run: |
mix coveralls.github
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ It uses Erlang's [cover](http://www.erlang.org/doc/man/cover.html) to generate c

The following are example projects.
- [coverage_sample](https://github.com/parroty/coverage_sample) is for Travis CI.
- [github_coverage_sample](https://github.com/mijailr/actions_sample) is for GitHub Actions.
- [circle_sample](https://github.com/parroty/circle_sample) is for CircleCI .
- [semaphore_sample](https://github.com/parroty/semaphore_sample) is for Semaphore CI.
- [excoveralls_umbrella](https://github.com/parroty/excoveralls_umbrella) is for umbrella project.
Expand Down Expand Up @@ -58,6 +59,8 @@ end
- [[mix coveralls] Show coverage](#mix-coveralls-show-coverage)
- [[mix coveralls.travis] Post coverage from travis](#mix-coverallstravis-post-coverage-from-travis)
- [.travis.yml](#travisyml)
- [[mix coveralls.github] Post coverage from GitHub Actions](#mix-coverallsgithub-post-coverage-from-github-actions)
- [.github/workflows/main.yml](#githubworkflowsexampleyml)
- [[mix coveralls.circle] Post coverage from circle](#mix-coverallscircle-post-coverage-from-circle)
- [circle.yml](#circleyml)
- [[mix coveralls.semaphore] Post coverage from semaphore](#mix-coverallssemaphore-post-coverage-from-semaphore)
Expand Down Expand Up @@ -120,6 +123,9 @@ Usage: mix coveralls.detail [--filter file-name-pattern]
Usage: mix coveralls.travis [--pro]
Used to post coverage from Travis CI server.

Usage: mix coveralls.github
Used to post coverage from [GitHub Actions](https://github.com/features/actions).

Usage: mix coveralls.post <Options>
Used to post coverage from local server using token.
The token should be specified in the argument or in COVERALLS_REPO_TOKEN
Expand Down Expand Up @@ -159,6 +165,38 @@ project, Use `coveralls.travis --pro` and ensure your coveralls.io
repo token is available via the `COVERALLS_REPO_TOKEN` environment
variable.

### [mix coveralls.github] Post coverage from [GitHub Actions](https://github.com/features/actions)
Specify `mix coveralls.github` as the build script in the GitHub action YML file and explicitly set the `MIX_ENV` environment to `test` and add `GITHUB_TOKEN` with the value of `{{ secrets.GITHUB_TOKEN }}`, this is required because is used internaly by coveralls.io to check the action and add statuses.

The value of `secrets.GITHUB_TOKEN` is added automatically inside every GitHub action, so you not need to assing that.

This task submits the result to Coveralls when the build is executed via GitHub actions and add statuses in the checks of github.

#### .github/workflows/example.yml
```yml
on: push
jobs:
test:
runs-on: ubuntu-latest
name: OTP ${{matrix.otp}} / Elixir ${{matrix.elixir}}
strategy:
matrix:
otp: [21.3.8.10, 22.1.7]
elixir: [1.8.2, 1.9.4]
env:
MIX_ENV: test
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/[email protected]
- uses: actions/[email protected]
with:
otp-version: ${{matrix.otp}}
elixir-version: ${{matrix.elixir}}
- run: mix deps.get
- run: mix coveralls.github
```

### [mix coveralls.circle] Post coverage from circle
Specify `mix coveralls.circle` in the `circle.yml`.
This task is for submitting the result to the coveralls server when Circle-CI build is executed.
Expand Down
9 changes: 9 additions & 0 deletions lib/excoveralls.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ defmodule ExCoveralls do
alias ExCoveralls.ConfServer
alias ExCoveralls.StatServer
alias ExCoveralls.Travis
alias ExCoveralls.Github
alias ExCoveralls.Circle
alias ExCoveralls.Semaphore
alias ExCoveralls.Drone
Expand All @@ -17,6 +18,7 @@ defmodule ExCoveralls do
alias ExCoveralls.Post

@type_travis "travis"
@type_github "github"
@type_circle "circle"
@type_semaphore "semaphore"
@type_drone "drone"
Expand Down Expand Up @@ -59,6 +61,13 @@ defmodule ExCoveralls do
Travis.execute(stats, options)
end

@doc """
Logic for posting from github action
"""
def analyze(stats, @type_github, options) do
Github.execute(stats, options)
end

@doc """
Logic for posting from circle-ci server
"""
Expand Down
103 changes: 103 additions & 0 deletions lib/excoveralls/github.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
defmodule ExCoveralls.Github do
@moduledoc """
Handles GitHub Actions integration with coveralls.
"""
alias ExCoveralls.Poster

def execute(stats, options) do
json = generate_json(stats, Enum.into(options, %{}))

if options[:verbose] do
IO.puts(json)
end

Poster.execute(json)
end

def generate_json(stats, options \\ %{})

def generate_json(stats, options) do
%{
repo_token: get_env("GITHUB_TOKEN"),
service_name: "github",
source_files: stats,
parallel: options[:parallel],
git: git_info()
}
|> Map.merge(job_data())
|> Jason.encode!
end

defp get_env(env) do
env
|> System.get_env
end

defp job_data() do
get_env("GITHUB_EVENT_NAME")
|> case do
"pull_request" ->
%{
service_pull_request: get_pr_id(),
service_job_id: "#{get_sha("pull_request")}-PR-#{get_pr_id()}",
}
event ->
%{service_job_id: get_sha(event)}
end
end

defp get_pr_id do
event_info()
|> Map.get("number")
|> Integer.to_string
end

defp get_committer_name do
event_info()
|> Map.get("sender")
|> Map.get("login")
end

defp get_sha("pull_request") do
event_info()
|> Map.get("pull_request")
|> Map.get("head")
|> Map.get("sha")
end

defp get_sha(_) do
get_env("GITHUB_SHA")
end

defp get_message("pull_request") do
{message, _} = System.cmd("git", ["log", get_sha("pull_request"), "-1", "--pretty=format:%s"])
message
end

defp get_message(_) do
{message, _} = System.cmd("git", ["log", "-1", "--pretty=format:%s"])
message
end

defp event_info do
get_env("GITHUB_EVENT_PATH")
|> File.read!()
|> Jason.decode!()
end

defp git_info do
event = get_env("GITHUB_EVENT_NAME")
%{
head: %{
id: get_sha(event),
committer_name: get_committer_name(),
message: get_message(event)
},
branch: get_branch()
}
end

defp get_branch do
get_env("GITHUB_REF")
end
end
3 changes: 3 additions & 0 deletions lib/excoveralls/task/util.ex
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ Usage: mix coveralls.html
Usage: mix coveralls.travis [--pro]
Used to post coverage from Travis CI server.
Usage: mix coveralls.github
Used to post coverage from a GitHub Action.
Usage: mix coveralls.post <Options>
Used to post coverage from local server using token.
The token should be specified in the argument or in COVERALLS_REPO_TOKEN
Expand Down
13 changes: 13 additions & 0 deletions lib/mix/tasks.ex
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,19 @@ defmodule Mix.Tasks.Coveralls do
end
end

defmodule Github do
@moduledoc """
Provides an entry point for github's script.
"""
use Mix.Task

@preferred_cli_env :test

def run(args) do
Mix.Tasks.Coveralls.do_run(args, [type: "github"])
end
end

defmodule Circle do
@moduledoc """
Provides an entry point for CircleCI's script.
Expand Down
5 changes: 5 additions & 0 deletions test/excoveralls_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ defmodule ExCoverallsTest do
assert called ExCoveralls.Circle.execute(@stats,[])
end

test_with_mock "analyze github", ExCoveralls.Github, [execute: fn(_,_) -> nil end] do
ExCoveralls.analyze(@stats, "github", [])
assert called ExCoveralls.Github.execute(@stats,[])
end

test_with_mock "analyze semaphore", ExCoveralls.Semaphore, [execute: fn(_,_) -> nil end] do
ExCoveralls.analyze(@stats, "semaphore", [])
assert called ExCoveralls.Semaphore.execute(@stats,[])
Expand Down
12 changes: 12 additions & 0 deletions test/fixtures/github_event.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"_comment": "Not used data was removed, see documentation",
"number": 206,
"pull_request": {
"head": {
"sha": "7c90516a3ac9f43ab6cf46ec5668b4430a3af103"
}
},
"sender": {
"login": "user"
}
}
42 changes: 42 additions & 0 deletions test/github_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
defmodule ExCoveralls.GithubTest do
use ExUnit.Case
import Mock
alias ExCoveralls.Github

@content "defmodule Test do\n def test do\n end\nend\n"
@counts [0, 1, nil, nil]
@source_info [%{name: "test/fixtures/test.ex", source: @content, coverage: @counts}]
setup do
# No additional context
System.put_env("GITHUB_EVENT_PATH", "test/fixtures/github_event.json")
System.put_env("GITHUB_SHA", "sha1")
System.put_env("GITHUB_EVENT_NAME", "pull_request")
System.put_env("GITHUB_REF", "branch")
System.put_env("GITHUB_TOKEN", "token")
{:ok, []}
end

test_with_mock "execute", ExCoveralls.Poster, execute: fn _ -> "result" end do
assert(Github.execute(@source_info, []) == "result")
end

test "when is not a pull request" do
System.put_env("GITHUB_EVENT_NAME", "anything")
{:ok, payload} = Jason.decode(Github.generate_json(@source_info))

assert(payload["repo_token"] == "token")
assert(payload["service_job_id"] == "sha1")
assert(payload["service_name"] == "github")
assert(payload["service_pull_request"] == nil)
end

test "generate from env vars" do
{:ok, payload} = Jason.decode(Github.generate_json(@source_info))


assert(payload["repo_token"] == "token")
assert(payload["service_job_id"] == "7c90516a3ac9f43ab6cf46ec5668b4430a3af103-PR-206")
assert(payload["service_name"] == "github")
assert(payload["service_pull_request"] == "206")
end
end
6 changes: 6 additions & 0 deletions test/mix/tasks_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,12 @@ defmodule Mix.Tasks.CoverallsTest do
assert(ExCoveralls.ConfServer.get == [type: "semaphore", args: []])
end

test_with_mock "github", Runner, [run: fn(_, _) -> nil end] do
Mix.Tasks.Coveralls.Github.run([])
assert(called Runner.run("test", ["--cover"]))
assert(ExCoveralls.ConfServer.get == [type: "github", args: []])
end

test_with_mock "post with env vars", Runner, [run: fn(_, _) -> nil end] do
org_token = System.get_env("COVERALLS_REPO_TOKEN") || ""
org_name = System.get_env("COVERALLS_SERVICE_NAME") || ""
Expand Down

0 comments on commit a26a09e

Please sign in to comment.