├── .dockerignore ├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── app ├── .babelrc ├── .dockerignore ├── .env ├── .gitignore ├── Dockerfile ├── README.md ├── components │ ├── footer.js │ ├── header.js │ ├── layout.js │ ├── menu.js │ ├── report.js │ └── reportlink.js ├── jest.config.js ├── next.config.js ├── package.json ├── pages │ ├── [org] │ │ └── repos │ │ │ └── [repo] │ │ │ ├── commits │ │ │ └── [commit] │ │ │ │ ├── file.js │ │ │ │ └── report.js │ │ │ ├── index.js │ │ │ └── pulls │ │ │ └── [pull] │ │ │ └── [commit] │ │ │ ├── file.js │ │ │ └── report.js │ ├── _app.js │ ├── api │ │ └── hello.js │ ├── docs │ │ ├── contributing │ │ │ └── index.js │ │ ├── hosting │ │ │ └── index.js │ │ ├── index.js │ │ └── integration │ │ │ └── index.js │ ├── index.js │ └── reports │ │ └── [org] │ │ ├── [repo].js │ │ └── [repo] │ │ └── [commit].js ├── public │ ├── docs │ │ ├── install-id.png │ │ └── private-key.png │ ├── favicon.ico │ ├── logo │ │ ├── logo-trns.png │ │ ├── logo.ai │ │ ├── logo.pdf │ │ ├── logo.psd │ │ ├── logo1.jpg │ │ ├── logo10.jpg │ │ ├── logo11.png │ │ ├── logo2-crp.png │ │ ├── logo2-o.png │ │ ├── logo2-trns.png │ │ ├── logo2.png │ │ ├── logo3.jpg │ │ ├── logo4-trns.png │ │ ├── logo4.png │ │ ├── logo5.png │ │ ├── logo6.png │ │ ├── logo7.png │ │ ├── logo8.png │ │ └── logo9.jpg │ ├── robots.txt │ ├── screenshots │ │ ├── check.png │ │ ├── comment.png │ │ ├── coverage.png │ │ └── filelist.png │ ├── sitemap.xml │ └── vercel.svg ├── setupTests.js ├── styles │ ├── File.module.css │ ├── Home.module.css │ ├── globals.sass │ └── hl.css ├── tests │ └── index.test.js ├── utils │ └── index.js └── yarn.lock ├── assets └── opencoverage-ui.png ├── conf └── nginx-templates │ └── default.conf.template ├── cov.yaml ├── docker-compose.yaml ├── opencoverage ├── __init__.py ├── api │ ├── __init__.py │ ├── api.py │ ├── app.py │ ├── badge.py │ └── upload.py ├── clients │ ├── __init__.py │ └── scm │ │ ├── __init__.py │ │ ├── base.py │ │ ├── exceptions.py │ │ └── github.py ├── commands.py ├── database.py ├── models.py ├── parser.py ├── reporter.py ├── settings.py ├── taskrunner.py ├── tasks.py ├── types.py └── utils.py ├── poetry.lock ├── pyproject.toml ├── setup.cfg ├── stubs ├── lxml │ ├── __init__.py │ └── etree.py └── unidiff │ └── __init__.py └── tests ├── __init__.py ├── acceptance ├── __init__.py ├── fixtures.py └── test_api.py ├── conftest.py ├── data ├── guillotina.cov ├── guillotina.pr ├── lcov └── test.pem ├── fixtures.py ├── integration ├── __init__.py └── test_github.py ├── unit ├── __init__.py ├── test_api.py ├── test_commands.py ├── test_database.py ├── test_parser.py ├── test_reporter.py ├── test_scm.py └── test_taskrunner.py └── utils.py /.dockerignore: -------------------------------------------------------------------------------- 1 | app 2 | lib 3 | bin 4 | .pytest-cache 5 | .vscode 6 | cov.pem 7 | cov2.pem 8 | coverage.xml 9 | .env.dev 10 | htmlcov 11 | conf -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: opencoverage 2 | 3 | on: [push] 4 | 5 | jobs: 6 | 7 | # Job to run pre-checks 8 | pre-checks: 9 | runs-on: ubuntu-latest 10 | strategy: 11 | matrix: 12 | python-version: [3.8] 13 | 14 | steps: 15 | - name: Checkout the repository 16 | uses: actions/checkout@v2 17 | 18 | - name: Setup Python 19 | uses: actions/setup-python@v1 20 | with: 21 | python-version: ${{ matrix.python-version }} 22 | 23 | - name: Install package 24 | run: | 25 | curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python - --version=1.1.4 26 | source $HOME/.poetry/env 27 | make install-dev 28 | - name: Run pre-checks 29 | run: | 30 | source $HOME/.poetry/env 31 | make lint 32 | make mypy 33 | # Job to run tests 34 | tests-api: 35 | runs-on: ubuntu-latest 36 | 37 | services: 38 | # Label used to access the service container 39 | postgres: 40 | # Docker Hub image 41 | image: postgres:12 42 | # Provide the password for postgres 43 | env: 44 | POSTGRES_PASSWORD: postgres 45 | # Set health checks to wait until postgres has started 46 | options: >- 47 | --health-cmd pg_isready 48 | --health-interval 10s 49 | --health-timeout 5s 50 | --health-retries 5 51 | ports: 52 | # Maps tcp port 5432 on service container to the host 53 | - 5432:5432 54 | 55 | strategy: 56 | matrix: 57 | python-version: [3.8] 58 | database: ["postgres"] 59 | 60 | # Set environment variables 61 | env: 62 | DSN: "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable" 63 | SCM: github 64 | GITHUB_APP_PEM_FILE: "cov.pem" 65 | GITHUB_APP_ID: "97375" 66 | GITHUB_DEFAULT_INSTALLATION_ID: "14245515" 67 | 68 | steps: 69 | - name: Checkout the repository 70 | uses: actions/checkout@v2 71 | 72 | - name: Setup Python 73 | uses: actions/setup-python@v1 74 | with: 75 | python-version: ${{ matrix.python-version }} 76 | 77 | - name: Install the package 78 | run: | 79 | curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python - --version=1.1.4 80 | source $HOME/.poetry/env 81 | make install-dev 82 | 83 | - name: Copy github api pem 84 | run: 'echo "$COV_PEM" > cov.pem' 85 | shell: bash 86 | env: 87 | COV_PEM: ${{secrets.COV_PEM}} 88 | 89 | - name: Run tests 90 | run: | 91 | source $HOME/.poetry/env 92 | make coverage 93 | 94 | - name: Upload coverage to Open Coverage 95 | run: | 96 | source $HOME/.poetry/env 97 | poetry run codecov --url="https://open-coverage.org/api" --token=- --slug=vangheem/opencoverage --file=coverage.xml -F project:api 98 | 99 | tests-frontend: 100 | runs-on: ubuntu-latest 101 | steps: 102 | - name: Checkout the repository 103 | uses: actions/checkout@v2 104 | - name: Use Node.js ${{ matrix.node-version }} 105 | uses: actions/setup-node@v1 106 | with: 107 | node-version: 14.x 108 | - run: npm install -g yarn 109 | working-directory: ./app 110 | - run: yarn install 111 | working-directory: ./app 112 | - run: yarn coverage > coverage.lcov 113 | working-directory: ./app 114 | # don't report now, not serious about it as it's so little 115 | # - run: yarn report-coverage 116 | # working-directory: ./app 117 | 118 | 119 | build-docker-api: 120 | if: ${{ github.ref == 'refs/heads/master' }} 121 | runs-on: ubuntu-latest 122 | needs: 123 | - pre-checks 124 | - tests-api 125 | steps: 126 | - name: Checkout the repository 127 | uses: actions/checkout@v2 128 | 129 | - name: Login to DockerHub 130 | uses: docker/login-action@v1 131 | with: 132 | username: ${{ secrets.DOCKERHUB_USERNAME }} 133 | password: ${{ secrets.DOCKERHUB_TOKEN }} 134 | 135 | - name: Get version 136 | run: echo "VERSION=$(awk '/^version =/{gsub(/"/,"",$3);print $3}' pyproject.toml)" >> $GITHUB_ENV 137 | 138 | - name: Build docker for API 139 | run: | 140 | docker build . -t opencoverage/api:$VERSION 141 | docker tag opencoverage/api:$VERSION opencoverage/api:latest 142 | docker push opencoverage/api:$VERSION 143 | docker push opencoverage/api:latest 144 | 145 | build-docker-frontend: 146 | if: ${{ github.ref == 'refs/heads/master' }} 147 | runs-on: ubuntu-latest 148 | needs: 149 | - pre-checks 150 | - tests-frontend 151 | steps: 152 | - name: Checkout the repository 153 | uses: actions/checkout@v2 154 | 155 | - name: Login to DockerHub 156 | uses: docker/login-action@v1 157 | with: 158 | username: ${{ secrets.DOCKERHUB_USERNAME }} 159 | password: ${{ secrets.DOCKERHUB_TOKEN }} 160 | 161 | - name: Get version 162 | run: echo "VERSION=$(awk '/^version =/{gsub(/"/,"",$3);print $3}' pyproject.toml)" >> $GITHUB_ENV 163 | 164 | - name: Build docker for Frontend 165 | run: | 166 | cd app 167 | docker build . -t opencoverage/frontend:$VERSION 168 | docker tag opencoverage/frontend:$VERSION opencoverage/frontend:latest 169 | docker push opencoverage/frontend:$VERSION 170 | docker push opencoverage/frontend:latest 171 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .coverage 2 | build/ 3 | fs/ 4 | *.swp 5 | .mypy_cache/ 6 | .env.dev 7 | cov.pem 8 | .vscode 9 | coverage.xml 10 | htmlcov -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8.7-buster 2 | LABEL maintainer="." 3 | 4 | WORKDIR /app 5 | 6 | RUN apt-get update && apt-get install -y vim netcat curl 7 | 8 | # Utilizing poetry's install script sandboxes poetry's dependencies away from the system 9 | # Otherwise applications may trample a package poetry requires, breaking poetry 10 | RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python - --version=1.1.4 11 | 12 | # This is a bit of a hack ATM as $HOME is not set when docker is built 13 | # This will need to be updated if we install poetry as a non-root user 14 | ENV PATH="/root/.poetry/bin:$PATH" 15 | 16 | RUN poetry config virtualenvs.create false 17 | 18 | COPY . /app 19 | 20 | RUN make install 21 | 22 | EXPOSE 5000 23 | 24 | CMD ["make", "run"] 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 2 | 3 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 4 | 5 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 6 | 7 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # We like colors 2 | # From: https://coderwall.com/p/izxssa/colored-makefile-for-golang-projects 3 | RED=`tput setaf 1` 4 | GREEN=`tput setaf 2` 5 | RESET=`tput sgr0` 6 | YELLOW=`tput setaf 3` 7 | 8 | # Vars 9 | POETRY ?= poetry 10 | 11 | help: ## This help message 12 | @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' 13 | 14 | install: ## Install dependencies 15 | $(POETRY) install --no-dev 16 | 17 | install-dev: ## Install DEV dependencies 18 | $(POETRY) install 19 | 20 | format: ## Format with isort and black 21 | $(POETRY) run isort opencoverage tests 22 | $(POETRY) run black opencoverage tests 23 | 24 | lint: ## Run isort, black and flake8 25 | $(POETRY) run isort --check-only opencoverage tests 26 | $(POETRY) run black --check opencoverage tests 27 | $(POETRY) run flake8 --config setup.cfg opencoverage tests 28 | 29 | mypy: 30 | $(POETRY) run mypy -p opencoverage 31 | 32 | test-dev: ## Run pytest against DEV environment 33 | $(POETRY) run pytest tests --env=.env.dev 34 | 35 | test: ## Run pytest 36 | $(POETRY) run pytest tests 37 | 38 | coverage: ## Create coverage report 39 | $(POETRY) run pytest -v tests -s --tb=native -v --cov=opencoverage --cov-report xml 40 | 41 | coverage-dev: ## Create coverage report for DEV environment 42 | $(POETRY) run pytest -v tests -s --tb=native -v --cov=opencoverage --cov-report xml --env=.env.dev 43 | 44 | send-codecov: 45 | $(POETRY) run codecov --url="https://open-coverage.org/api" --token=- --slug=vangheem/opencoverage --file=coverage.xml -F project:api 46 | 47 | run: ## Run Open Coverage 48 | $(POETRY) run opencoverage 49 | 50 | run-dev: ## Run Open Coverage DEV environment 51 | $(POETRY) run opencoverage -e .env.dev 52 | 53 | 54 | .PHONY: clean install test coverage run run-dev help 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Open Coverage 2 | 3 | [![opencoverage](https://open-coverage.org/api/vangheem/repos/opencoverage/badge.svg?branch=master&project=api)](https://open-coverage.org/vangheem/repos/opencoverage) 4 | [![build](https://img.shields.io/github/checks-status/vangheem/opencoverage/master?label=Build)](https://github.com/vangheem/opencoverage/actions) 5 | [![license](https://img.shields.io/github/license/vangheem/opencoverage)](https://github.com/vangheem/opencoverage/blob/master/LICENSE) 6 | [![dockerapi](https://img.shields.io/docker/v/opencoverage/api?label=Docker%28API%29)](https://hub.docker.com/r/opencoverage/api) 7 | [![dockerfront](https://img.shields.io/docker/v/opencoverage/frontend?label=Docker%28Frontend%29)](https://hub.docker.com/r/opencoverage/frontend) 8 | 9 | Free and open source alternative providing coverage reporting and diff coverage reporting. 10 | 11 | The project can be simple replacement for [Codecov](https://about.codecov.io/) or [Coveralls](https://coveralls.io/) for teams working on private repositories. 12 | 13 | (some of the enterprise option pricing seemed a little unreasonable) 14 | 15 | Features: 16 | 17 | - Coverage reporting 18 | - Diff coverage reporting 19 | - GitHub integration: PRs, comments 20 | - Codecov CLI compatible 21 | 22 | Requirements: 23 | 24 | - [SQLAlchemy](https://www.sqlalchemy.org/ "Link to SQLAlchemy site") compatible backend(PostgreSQL, SQLite, MySQL, etc) 25 | - Open Coverage backend 26 | - Open Coverage frontend 27 | 28 | SCM integrations: 29 | 30 | - [x] GitHub 31 | - [ ] Bitbucket 32 | - [ ] GitLab 33 | 34 | ## Prerequisites 35 | 36 | Please make sure that you have the following dependencies installed 37 | 38 | - [Docker](https://www.docker.com/) 39 | - [Docker Compose](https://docs.docker.com/compose/) 40 | 41 | ### Development 42 | 43 | - [Poetry](https://python-poetry.org/) 44 | 45 | ## Getting started 46 | 47 | You can use `docker-compose` to start Open Coverage. 48 | 49 | ```sh 50 | docker-compose up 51 | ``` 52 | 53 | This will start the backend, frontend, PostgreSQL and NGINX. 54 | If you want to change to another supported database you have to adjust the *docker-compose.yaml* file. 55 | 56 | Depending on your machine and internet connection is can take a moment till everything is running. 57 | Once `docker-compose` is ready you can browse to http://localhost:3000/ to checkout the frontend and documentation. 58 | 59 | ![Open Coverage Frontend Picture](assets/opencoverage-ui.png) 60 | 61 | ## Configuration 62 | 63 | To run the server yourself, you need to create a GitHub application and install 64 | it for your organization. 65 | 66 | All configuration is done with env variables. 67 | 68 | - host 69 | - port 70 | - public_url 71 | - root_path: root path api is served from 72 | - dsn: connection string for backend database 73 | - cors: hosts frontend runs on 74 | - scm: enum(`github`) 75 | - github_app_id: ID of app 76 | - github_app_pem_file: pem file for application you created 77 | - github_default_installation_id: ID of org this app is installed on 78 | 79 | ## Backend development 80 | 81 | Develop: 82 | 83 | ```sh 84 | make install-dev 85 | ``` 86 | 87 | Tests: 88 | 89 | Run docker compose first: 90 | 91 | ```sh 92 | docker-compose up postgres 93 | ``` 94 | 95 | ```sh 96 | make test 97 | ``` 98 | 99 | Run: 100 | 101 | ```sh 102 | make run 103 | ``` 104 | 105 | ## Frontend development 106 | 107 | This project uses [Next.js](https://nextjs.org/ "Link to Next.js site"). 108 | See the `app` directory for details. 109 | 110 | ## Send report from CI 111 | 112 | The backend is compatible with the Codecov CLI. 113 | 114 | You need to provide the installation id of your org as the `--token` value 115 | or `dummy` if you are using the `github_default_installation_id` setting 116 | and only using the server for a single org. 117 | 118 | ```sh 119 | codecov --url="http://:8000" --token= --slug=vangheem/opencoverage 120 | ``` 121 | -------------------------------------------------------------------------------- /app/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["next/babel"] 3 | } -------------------------------------------------------------------------------- /app/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /app/.env: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_API_URL=http://localhost:8000 2 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | -------------------------------------------------------------------------------- /app/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14 2 | 3 | WORKDIR /usr/src/app 4 | COPY ./ ./ 5 | 6 | RUN yarn install 7 | RUN yarn build 8 | 9 | CMD ["bash", "-c", "yarn build && yarn start --hostname 0.0.0.0"] -------------------------------------------------------------------------------- /app/README.md: -------------------------------------------------------------------------------- 1 | # Open Coverage Frontend 2 | 3 | The frontend of Open Coverage is written in [Next.js](https://nextjs.org/). 4 | 5 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 6 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 7 | 8 | ## Requirements 9 | 10 | [Node.js](https://nodejs.org/en/) or [Yarn](https://yarnpkg.com/) 11 | 12 | ## Getting Started 13 | 14 | First, install all packages with NPM or Yarn: 15 | 16 | ```shell 17 | npm install 18 | # or 19 | yarn install 20 | ``` 21 | 22 | Second, run the development server: 23 | 24 | ```shell 25 | npm run dev 26 | # or 27 | yarn dev 28 | ``` 29 | 30 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 31 | 32 | You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. 33 | 34 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`. 35 | 36 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. 37 | 38 | -------------------------------------------------------------------------------- /app/components/footer.js: -------------------------------------------------------------------------------- 1 | export default function Footer () { 2 | return ( 3 |
4 |
5 | 50 |
51 |
52 | ) 53 | } 54 | -------------------------------------------------------------------------------- /app/components/header.js: -------------------------------------------------------------------------------- 1 | import Menu from './menu.js' 2 | import Head from 'next/head' 3 | 4 | export default function Header ({ title, description }) { 5 | if (!title) { 6 | title = 'Open Coverage' 7 | } 8 | if (!description) { 9 | description = 10 | 'Free and open source code coverage reporting for public and private projects. Install on-prem or use the public service.' 11 | } 12 | return ( 13 | <> 14 | 15 | {title} 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /app/components/layout.js: -------------------------------------------------------------------------------- 1 | // components/Layout.js 2 | import React, { Component } from 'react' 3 | import Header from './header' 4 | import Footer from './footer' 5 | 6 | export default class Layout extends Component { 7 | render () { 8 | const { children } = this.props 9 | return ( 10 | <> 11 |
12 |
16 | {children} 17 |
18 |