Skip to content

Commit

Permalink
prepared for release
Browse files Browse the repository at this point in the history
  • Loading branch information
illabo committed Oct 14, 2020
1 parent db94e79 commit eeb10b7
Show file tree
Hide file tree
Showing 11 changed files with 605 additions and 216 deletions.
36 changes: 36 additions & 0 deletions .github/workflows/build-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
on:
push:
tags:
- '*.**'

jobs:
build:
name: Create release, build and upload binary asset
runs-on: macos-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Build project
run: |
go build -o xcccr
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: ${{ github.ref }}
body_path: RELEASENOTES.md
draft: false
prerelease: false
- name: Upload Release Asset
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: ./xcccr
asset_name: xcccr
asset_content_type: application/octet-stream
8 changes: 8 additions & 0 deletions .xcccr.toml.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
ProjectPath = "/Users/home/" # Usualy ok to leave empty. If empty gets current working dir at runtime.
FilterTargets = ["CoreTests"] # If a target name contains one of these substrings the target would be filtered. Empty string "" matches everything. Please note that if regex is used targets filtering has no effect.
FilterPaths = ["Tests","UITests","Constants.swift"] # If a path contains one of these substrings the path (file, dir) would be filtered. Empty string "" matches everything. Please note that if regex is used path filtering has no effect.
FilterPattern = "[\\S\\s]*secret\\.json$" # Go flavored regex (non-PCRE!). Please check https://golang.org/pkg/regexp/ for more info. When used in config escape characters should be escaped twice for config parsing (unfortunately). Please note that if regex is used targets filtering has no effect.
InvertFilter = false # Set `false` to disallow targets and paths in filters. Set `true` to allow only targets and paths in filters. Default `false`.
IncludeMasked = false # Skip recalculating coverage, just read from xccov report regardless masked (filtered) targets and paths. Default `false`.
MeterLOC = false # Set `false` for coverage percent and `true` for number of LOCs e.g. 42/13 (now/was). Default `false`.
Tolerance = 5 # Percent. Default 0.
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2020 Ilya Yachin

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:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

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.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
## Xcode Coverage for Commit Reporter (xcccr)
This dead simple tool only works with Apple's `xccov` generated code coverage reports in json format. It takes two reports, diffs and spits out warnings and errors in GitHub Actions compatible format.

### CLI usage
Flag | Description
--------|------------
`-cur` | For path of "current report" file. A report to be checked.
`-lst` | For path of "last report" file. A report to be base for determining whether the coverage metrics is lowered or became higher in "current" report.
`-tol` | Percent to be tolerated before error. E.g. last is 70%, current is 65% and tol is 10 means no error.
`-proj` | Project root to get relative paths. If empty and missing from config working dir would be used. Usually in GitHub Actions you just want the working dir.
`-rg` | Regex filter, pass it in quotes e.g. `-rg="[\s\S]\.swift"`. Matching paths would be ignored. When used with `-i` the result is opposite: only matching paths would be visible to reporter. Please note that if regex is used targets and paths filtering set in config has no effect.
`-i` | Invert filter. Applied to -rg and to targets/paths filter lists in config. If no filters supplied prevents reporting allowing nothing.
`-m` | Include masked files coverage in total metrics. If true warnings would be produced only for unfiltered targets/paths, but total coverage would be reported for all the files as listed in original xccov json.
`-loc` | Count covered LOCs diff instead of percent coverage.
`-cfg` | Path to config. Parameters passed with the flags have precedence and overrides the values stored in config. Config passed with this flag overrides default config at `.xcccr.toml`. If there aren't any config default values are used.


If there aren't last report or it isn't needed it's ok to pass only the current one. Current report also may be piped in to xcccr like that `cat report.json | ./xcccr`

### Config values

Please check `.xcccr.toml.example` for reference.
2 changes: 2 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- This release only tested manually. Help wanted to cover with tests.
- Some minor bugs are expected.
200 changes: 200 additions & 0 deletions configurator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
package main

import (
"bufio"
"encoding/json"
"errors"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"regexp"
"strconv"

"github.com/BurntSushi/toml"
)

func readConfig(path string) (*Config, error) {
var cfg Config
_, err := toml.DecodeFile(path, &cfg)
return &cfg, err
}

func prepareRunConditions() (rnCnd *RunConditions, err error) {
rnCnd = &RunConditions{}

dumbValue := dumbValue()

lstPth := flag.String(
"lst",
"",
"Pass the path of last report to compare current report to.",
)
curPth := flag.String(
"cur",
"",
"Pass the path of current report.",
)
tolerance := flag.String("tol",
dumbValue,
"Percent to be tolerated before error. E.g. last is 70%, current is 65% and tol is 10 means no error.",
)
projRoot := flag.String(
"proj",
getWorkdir(),
"Project root to get relative paths. If empty and missing from config working dir would be used.",
)
filtr := flag.String("rg",
dumbValue,
"Regex filter. Applied to paths.",
)
fInvert := flag.Bool(
"i",
false,
"Invert filter. Applied to -rg and to targets/paths filter lists in config. If no filters supplied prevents reporting allowing nothing.",
)
includeMasked := flag.Bool(
"m",
false,
"Include masked files coverage in total metrics. If true warnings would be produced only for unfiltered targets/paths, but total coverage would be calculated for all the files.",
)
meterLOC := flag.Bool(
"loc",
false,
"Count covered LOCs diff instead of percent coverage.",
)
cfgPth := flag.String(
"cfg",
"",
"Path to config.",
)

flag.Parse()

if *cfgPth == "" {
*cfgPth = ".xcccr.toml"
}
cfg, err := readConfig(*cfgPth)
if err != nil && !os.IsNotExist(err) {
return
}
if *tolerance != dumbValue {
i, e := strconv.Atoi(*tolerance)
if i < 0 || i > 100 {
e = errors.New("out of allowed range")
}
if e != nil {
err = fmt.Errorf("tolerance -tol must be an int in range 0 to 100. %w", err)
return
}
cfg.Tolerance = i
}
if *filtr != dumbValue {
cfg.FilterPattern = *filtr
}
if *fInvert {
cfg.InvertFilter = true
}
if *includeMasked {
cfg.IncludeMasked = true
}
if *meterLOC {
cfg.MeterLOC = true
}
cfg.ProjectPath = *projRoot
if cfg.FilterPattern != "" {
re, e := regexp.Compile(cfg.FilterPattern)
if e != nil {
err = fmt.Errorf("filter pattern (aka regexp) is invalid: %w", e)
return
}
rnCnd.Regexp = re
}

rnCnd.Config = cfg
rnCnd.DiffUnitChan = make(chan DiffUnit)
rnCnd.TotalUnitChan = make(chan DiffUnit)

curXccRep, err := getCurrentCoverage(curPth)
if err != nil {
return
}
rnCnd.CurrentReport = curXccRep

lstXccRep, err := getLastCoverage(lstPth)
if err != nil {
return
}
rnCnd.LastReport = lstXccRep

return
}

func getCurrentCoverage(curPth *string) (curXccRep *XCCoverageReport, err error) {
var pipeIn []byte
info, _ := os.Stdin.Stat()
if info.Mode()&os.ModeNamedPipe != 0 {
reader := bufio.NewReader(os.Stdin)
for {
ln, _, err := reader.ReadLine()
if err != nil && err == io.EOF {
break
}
pipeIn = append(pipeIn, ln...)
}
}

if *curPth != "" {
curXccRep, err = readReportFile(*curPth)
if err != nil {
return
}
}
// piped in file content always overrides passed with a 'cur' flag
if len(pipeIn) > 0 {
if err = json.Unmarshal(pipeIn, &curXccRep); err != nil {
return
}
}
if *curPth == "" && len(pipeIn) == 0 {
log.Fatal("No data passed to programme to report coverage.")
}
return
}

func getLastCoverage(lstPth *string) (lstXccRep *XCCoverageReport, err error) {
lstXccRep = &XCCoverageReport{}
if *lstPth != "" {
lstXccRep, err = readReportFile(*lstPth)
notExist := os.IsNotExist(err)
if err != nil && notExist == false {
return
}
if notExist {
fmt.Println("Previous report file not found. Continuing with current report only.")
}
}
return
}

func readReportFile(filePath string) (report *XCCoverageReport, err error) {
report = &XCCoverageReport{}
filePath = fmt.Sprintf("%s%s", getWorkdir(), filePath)
_, err = os.Stat(filePath)
if err != nil {
return
}
fByt, err := ioutil.ReadFile(filePath)
if err != nil {
return
}
err = json.Unmarshal(fByt, report)

return
}

func dumbValue() string {
return randomStr(12)
}
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module xcccr

go 1.15

require github.com/BurntSushi/toml v0.3.1
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
Loading

0 comments on commit eeb10b7

Please sign in to comment.