Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 46 additions & 1 deletion .go-covercheck.history.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,50 @@
{
"entries": [
{
"commit": "a33d6daaa0cea52472df889a1e6a04490fa41151",
"branch": "copilot/fix-42",
"timestamp": "2025-08-02T06:01:01.917674791Z",
"results": {
"byFile": [
{
"statementCoverage": "1/2",
"blockCoverage": "1/2",
"statementPercentage": 50,
"blockPercentage": 50,
"statementThreshold": 65,
"blockThreshold": 50,
"failed": true,
"file": "github.com/mach6/go-covercheck/pkg/math/math.go"
}
],
"byPackage": [
{
"statementCoverage": "1/2",
"blockCoverage": "1/2",
"statementPercentage": 50,
"blockPercentage": 50,
"statementThreshold": 65,
"blockThreshold": 50,
"failed": true,
"package": "github.com/mach6/go-covercheck/pkg/math"
}
],
"byTotal": {
"statements": {
"coverage": "1/2",
"threshold": 80,
"percentage": 50,
"failed": true
},
"blocks": {
"coverage": "1/2",
"threshold": 75,
"percentage": 50,
"failed": true
}
}
}
},
{
"commit": "ef613d597be47cdeaa401aebee56ef0605eb4f8b",
"branch": "detached",
Expand Down Expand Up @@ -542,4 +587,4 @@
}
}
]
}
}
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ A fast, flexible CLI tool for enforcing test coverage thresholds in Go projects.
- Enforce minimum coverage thresholds for files, packages, and the entire project.
- Supports statement and block coverage separately.
- Native `table`|`json`|`yaml`|`md`|`html`|`csv`|`tsv` output.
- Configurable table styles (`default`|`light`|`bold`|`rounded`|`double`).
- Configurable via a `.go-covercheck.yml` or CLI flags.
- Sorting and colored table output.
- Colored `json` and `yaml` output.
Expand All @@ -27,7 +28,6 @@ The following items are noteworthy and not (currently) supported.

- Does not support configurable profile block count (how many times a section of code was hit) thresholds. The assumption
is any value `>=1` is enough.
- Table style is not configurable.
- Color codes (see [Color Legend](#🎨-color-legend)) are not configurable.
- Severity weights (see [Color Legend](#🎨-color-legend)) are not configurable.

Expand Down Expand Up @@ -111,6 +111,9 @@ Here is a sample `.go-covercheck.yml` configuration file:
statementThreshold: 65.0
blockThreshold: 60.0

# Optional, table style for table output format (default: light)
tableStyle: bold

# Optional, by total thresholds overriding the global values above
total:
statements: 75.0
Expand Down Expand Up @@ -202,6 +205,26 @@ Flags:
-v, --version version for go-covercheck
```

### 🎨 Table Styles

You can customize the appearance of table output using the `--table-style` flag or by configuring `tableStyle` in your `.go-covercheck.yml` file.

Available table styles:
- `default` - ASCII characters (compatible with all terminals)
- `light` - Unicode light box drawing characters (default)
- `bold` - Unicode bold box drawing characters
- `rounded` - Unicode rounded corner characters
- `double` - Unicode double line characters

**Example:**
```shell
# Use bold table style via CLI
go-covercheck --table-style=bold

# Use rounded table style via config file
echo "tableStyle: rounded" >> .go-covercheck.yml
```

## 🕰️ History

History is a feature that allows you to save and compare coverage results against previous runs.
Expand Down
13 changes: 13 additions & 0 deletions cmd/go-covercheck/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ const (
FormatFlag = "format"
FormatFlagShort = "f"

TableStyleFlag = "table-style"
TableStyleFlagUsage = "table style [default|light|bold|rounded|double]"

StatementThresholdFlag = "statement-threshold"
StatementThresholdFlagShort = "s"
StatementThresholdFlagUsage = "global statement threshold to enforce [0=disabled]"
Expand Down Expand Up @@ -453,6 +456,10 @@ func applyConfigOverrides(cfg *config.Config, cmd *cobra.Command, noConfigFile b
noConfigFile {
cfg.Format = v
}
if v, _ := cmd.Flags().GetString(TableStyleFlag); cmd.Flags().Changed(TableStyleFlag) ||
noConfigFile {
cfg.TableStyle = v
}
if v, _ := cmd.Flags().GetBool(NoTableFlag); cmd.Flags().Changed(NoTableFlag) ||
noConfigFile {
cfg.NoTable = v
Expand Down Expand Up @@ -538,6 +545,12 @@ func initFlags(cmd *cobra.Command) {
FormatFlagUsage,
)

cmd.Flags().String(
TableStyleFlag,
config.TableStyleDefValue,
TableStyleFlagUsage,
)

cmd.Flags().Float64P(
StatementThresholdFlag,
StatementThresholdFlagShort,
Expand Down
17 changes: 17 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ const (
FormatMD = "md"
FormatDefault = FormatTable

TableStyleDefault = "default"
TableStyleLight = "light"
TableStyleBold = "bold"
TableStyleRounded = "rounded"
TableStyleDouble = "double"
TableStyleDefValue = TableStyleLight

thresholdOff = 0
thresholdMax = 100

Expand Down Expand Up @@ -78,6 +85,7 @@ type Config struct {
NoSummary bool `yaml:"noSummary,omitempty"`
NoColor bool `yaml:"noColor,omitempty"`
Format string `yaml:"format,omitempty"`
TableStyle string `yaml:"tableStyle,omitempty"`
TerminalWidth int `yaml:"terminalWidth,omitempty"`
ModuleName string `yaml:"moduleName,omitempty"`
}
Expand Down Expand Up @@ -110,6 +118,7 @@ func (c *Config) ApplyDefaults() {
c.SortOrder = SortOrderDefault
c.Skip = []string{}
c.Format = FormatDefault
c.TableStyle = TableStyleDefValue

c.initPerFileWhenNil()
c.initPerPackageWhenNil()
Expand Down Expand Up @@ -148,6 +157,14 @@ func (c *Config) Validate() error { //nolint:cyclop
FormatJSON, FormatYAML, FormatTable, FormatCSV, FormatHTML, FormatTSV, FormatMD)
}

switch c.TableStyle {
case TableStyleDefault, TableStyleLight, TableStyleBold, TableStyleRounded, TableStyleDouble:
break
default:
return fmt.Errorf("table-style must be one of %s|%s|%s|%s|%s",
TableStyleDefault, TableStyleLight, TableStyleBold, TableStyleRounded, TableStyleDouble)
}

if c.NoSummary && c.NoTable && c.Format != FormatJSON && c.Format != FormatYAML {
return fmt.Errorf("cannot specify both no-summary and no-table with format %s", c.Format)
}
Expand Down
61 changes: 61 additions & 0 deletions pkg/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,64 @@ func TestLoad_InvalidYAML(t *testing.T) {
require.Error(t, err)
require.Nil(t, cfg)
}

func TestLoad_ValidYAML_WithTableStyle(t *testing.T) {
yaml := `
statementThreshold: 75.0
tableStyle: bold
`
tmpFile := path.Join(t.TempDir(), "test_config_table_style.yaml")
err := os.WriteFile(tmpFile, []byte(yaml), 0600)
require.NoError(t, err)
defer os.Remove(tmpFile) //nolint:errcheck

cfg, err := config.Load(tmpFile)
require.NoError(t, err)
require.Equal(t, "bold", cfg.TableStyle)
}

func TestApplyDefaults_TableStyle(t *testing.T) {
cfg := &config.Config{}
cfg.ApplyDefaults()
require.Equal(t, config.TableStyleDefValue, cfg.TableStyle)
require.Equal(t, "light", cfg.TableStyle) // Should be light by default
}

func TestValidate_InvalidTableStyle(t *testing.T) {
cfg := &config.Config{
StatementThreshold: 70,
BlockThreshold: 50,
SortBy: "file",
SortOrder: "asc",
Format: "table",
TableStyle: "invalid",
}
err := cfg.Validate()
require.Error(t, err)
require.Contains(t, err.Error(), "table-style must be one of")
}

func TestValidate_ValidTableStyles(t *testing.T) {
validStyles := []string{
config.TableStyleDefault,
config.TableStyleLight,
config.TableStyleBold,
config.TableStyleRounded,
config.TableStyleDouble,
}

for _, style := range validStyles {
t.Run(style, func(t *testing.T) {
cfg := &config.Config{
StatementThreshold: 70,
BlockThreshold: 50,
SortBy: "file",
SortOrder: "asc",
Format: "table",
TableStyle: style,
}
err := cfg.Validate()
require.NoError(t, err)
})
}
}
82 changes: 50 additions & 32 deletions pkg/output/history.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,55 @@ import (
"github.com/mach6/go-covercheck/pkg/history"
)

// getHistoryTableStyle returns the appropriate table.Style for history tables.
func getHistoryTableStyle(cfg *config.Config) table.Style {
var boxStyle table.BoxStyle

switch cfg.TableStyle {
case config.TableStyleDefault:
boxStyle = table.StyleBoxDefault
case config.TableStyleBold:
boxStyle = table.StyleBoxBold
case config.TableStyleRounded:
boxStyle = table.StyleBoxRounded
case config.TableStyleDouble:
boxStyle = table.StyleBoxDouble
default: // config.TableStyleLight or any other value
boxStyle = table.StyleBoxLight
}

return table.Style{
Name: "Custom",
Box: boxStyle,
Color: table.ColorOptionsDefault,
Format: table.FormatOptions{
Footer: text.FormatDefault,
FooterAlign: text.AlignRight,
FooterVAlign: text.VAlignDefault,
Header: text.FormatUpper,
HeaderAlign: text.AlignCenter,
HeaderVAlign: text.VAlignDefault,
Row: text.FormatDefault,
RowAlign: text.AlignRight,
RowVAlign: text.VAlignDefault,
},
HTML: table.DefaultHTMLOptions,
Options: table.Options{
DoNotColorBordersAndSeparators: false,
DrawBorder: true,
SeparateColumns: true,
SeparateFooter: true,
SeparateHeader: true,
SeparateRows: true,
},
Size: table.SizeOptions{
WidthMax: cfg.TerminalWidth,
WidthMin: 0,
},
Title: table.TitleOptionsDefault,
}
}

// CompareHistory shows the comparison output for a ref: and the results.
func CompareHistory(ref string, refEntry *history.Entry, results compute.Results) {
fmt.Printf("\n≡ Comparing against ref: %s [commit %s]\n",
Expand Down Expand Up @@ -122,38 +171,7 @@ func ShowHistory(h *history.History, limit int, cfg *config.Config) {

t := table.NewWriter()
t.SetOutputMirror(os.Stdout)
t.SetStyle(
table.Style{
Name: "Custom",
Box: table.StyleBoxLight,
Color: table.ColorOptionsDefault,
Format: table.FormatOptions{
Footer: text.FormatDefault,
FooterAlign: text.AlignRight,
FooterVAlign: text.VAlignDefault,
Header: text.FormatUpper,
HeaderAlign: text.AlignCenter,
HeaderVAlign: text.VAlignDefault,
Row: text.FormatDefault,
RowAlign: text.AlignRight,
RowVAlign: text.VAlignDefault,
},
HTML: table.DefaultHTMLOptions,
Options: table.Options{
DoNotColorBordersAndSeparators: false,
DrawBorder: true,
SeparateColumns: true,
SeparateFooter: true,
SeparateHeader: true,
SeparateRows: true,
},
Size: table.SizeOptions{
WidthMax: cfg.TerminalWidth,
WidthMin: 0,
},
Title: table.TitleOptionsDefault,
},
)
t.SetStyle(getHistoryTableStyle(cfg))

t.AppendHeader(table.Row{"Timestamp", "Commit", "Branch", "Tags", "Label", "Coverage"})

Expand Down
Loading