mirror of
https://github.com/ivuorinen/go-test-sarif.git
synced 2026-01-26 03:04:09 +00:00
v1.0.0
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -26,3 +26,4 @@ logs
|
|||||||
bin/*
|
bin/*
|
||||||
!bin/.gitkeep
|
!bin/.gitkeep
|
||||||
megalinter-reports
|
megalinter-reports
|
||||||
|
dist/*
|
||||||
|
|||||||
79
.goreleaser.yml
Normal file
79
.goreleaser.yml
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
version: 2
|
||||||
|
|
||||||
|
before:
|
||||||
|
hooks:
|
||||||
|
- go mod tidy
|
||||||
|
- go generate ./...
|
||||||
|
|
||||||
|
builds:
|
||||||
|
- main: ./cmd/main.go
|
||||||
|
env:
|
||||||
|
- CGO_ENABLED=0
|
||||||
|
goos:
|
||||||
|
- linux
|
||||||
|
- windows
|
||||||
|
- darwin
|
||||||
|
goarch:
|
||||||
|
- amd64
|
||||||
|
- arm64
|
||||||
|
- arm
|
||||||
|
goarm:
|
||||||
|
- "7"
|
||||||
|
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||||
|
flags:
|
||||||
|
- -trimpath
|
||||||
|
ldflags:
|
||||||
|
- -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{ .CommitDate }} -X main.builtBy=goreleaser
|
||||||
|
|
||||||
|
archives:
|
||||||
|
- name_template: >-
|
||||||
|
{{ .ProjectName }}_
|
||||||
|
{{- title .Os }}_
|
||||||
|
{{- if eq .Arch "amd64" }}x86_64
|
||||||
|
{{- else if eq .Arch "386" }}i386
|
||||||
|
{{- else }}{{ .Arch }}{{ end }}
|
||||||
|
{{- if .Arm }}v{{ .Arm }}{{ end }}
|
||||||
|
builds_info:
|
||||||
|
group: root
|
||||||
|
owner: root
|
||||||
|
files:
|
||||||
|
- LICENSE*
|
||||||
|
- README*
|
||||||
|
- CHANGELOG*
|
||||||
|
|
||||||
|
checksum:
|
||||||
|
name_template: 'checksums.txt'
|
||||||
|
|
||||||
|
snapshot:
|
||||||
|
version_template: "{{ incpatch .Version }}-next"
|
||||||
|
|
||||||
|
changelog:
|
||||||
|
sort: asc
|
||||||
|
use: github
|
||||||
|
filters:
|
||||||
|
exclude:
|
||||||
|
- '^docs:'
|
||||||
|
- '^test:'
|
||||||
|
- '^chore:'
|
||||||
|
- Merge pull request
|
||||||
|
- Merge branch
|
||||||
|
- go mod tidy
|
||||||
|
groups:
|
||||||
|
- title: 'New Features'
|
||||||
|
regexp: "^.*feat[(\\w)]*:+.*$"
|
||||||
|
order: 0
|
||||||
|
- title: 'Bug fixes'
|
||||||
|
regexp: "^.*fix[(\\w)]*:+.*$"
|
||||||
|
order: 1
|
||||||
|
- title: Other work
|
||||||
|
order: 999
|
||||||
|
|
||||||
|
release:
|
||||||
|
github:
|
||||||
|
owner: ivuorinen
|
||||||
|
name: go-test-sarif
|
||||||
|
draft: true
|
||||||
|
prerelease: auto
|
||||||
|
mode: append
|
||||||
|
name_template: "{{.ProjectName}}-v{{.Version}}"
|
||||||
|
disable: false
|
||||||
24
Justfile
24
Justfile
@@ -44,3 +44,27 @@ docker-build:
|
|||||||
docker-run:
|
docker-run:
|
||||||
echo "Running {{app_name}} in Docker..."
|
echo "Running {{app_name}} in Docker..."
|
||||||
docker run --rm -v $(pwd):/workspace ghcr.io/ivuorinen/{{app_name}} go-test-results.json go-test-results.sarif
|
docker run --rm -v $(pwd):/workspace ghcr.io/ivuorinen/{{app_name}} go-test-results.json go-test-results.sarif
|
||||||
|
|
||||||
|
# Check if goreleaser is installed
|
||||||
|
check-goreleaser:
|
||||||
|
@which goreleaser > /dev/null || (echo "goreleaser not found. Please install from https://goreleaser.com/install/" && exit 1)
|
||||||
|
|
||||||
|
# Create a snapshot release (for testing)
|
||||||
|
release-snapshot: check-goreleaser
|
||||||
|
echo "Creating snapshot release..."
|
||||||
|
goreleaser release --snapshot --clean
|
||||||
|
|
||||||
|
# Create a local release (without publishing)
|
||||||
|
release-local: check-goreleaser
|
||||||
|
echo "Creating local release..."
|
||||||
|
goreleaser release --skip=publish --clean
|
||||||
|
|
||||||
|
# Create and publish a release (requires GITHUB_TOKEN)
|
||||||
|
release: check-goreleaser
|
||||||
|
echo "Creating and publishing release..."
|
||||||
|
GITHUB_TOKEN=$(gh auth token) goreleaser release --clean
|
||||||
|
|
||||||
|
# Validate goreleaser configuration
|
||||||
|
release-check: check-goreleaser
|
||||||
|
echo "Checking goreleaser configuration..."
|
||||||
|
goreleaser check
|
||||||
|
|||||||
68
cmd/main.go
68
cmd/main.go
@@ -1,25 +1,65 @@
|
|||||||
// main package contains the cli functionality
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/ivuorinen/go-test-sarif-action/internal"
|
"github.com/ivuorinen/go-test-sarif-action/internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
var (
|
||||||
if len(os.Args) < 3 {
|
version = "dev"
|
||||||
fmt.Println("Usage: go-test-sarif <input.json> <output.sarif>")
|
commit = "none"
|
||||||
os.Exit(1)
|
date = "unknown"
|
||||||
}
|
builtBy = "unknown"
|
||||||
|
)
|
||||||
|
|
||||||
inputFile := os.Args[1]
|
func printVersion(w io.Writer) {
|
||||||
outputFile := os.Args[2]
|
_, _ = fmt.Fprintf(w, "go-test-sarif %s\n", version)
|
||||||
|
_, _ = fmt.Fprintf(w, " commit: %s\n", commit)
|
||||||
err := internal.ConvertToSARIF(inputFile, outputFile)
|
_, _ = fmt.Fprintf(w, " built at: %s\n", date)
|
||||||
if err != nil {
|
_, _ = fmt.Fprintf(w, " built by: %s\n", builtBy)
|
||||||
fmt.Println("Error:", err)
|
}
|
||||||
os.Exit(1)
|
|
||||||
}
|
func printUsage(w io.Writer) {
|
||||||
|
_, _ = fmt.Fprintln(w, "Usage: go-test-sarif <input.json> <output.sarif>")
|
||||||
|
_, _ = fmt.Fprintln(w, " go-test-sarif --version")
|
||||||
|
}
|
||||||
|
|
||||||
|
func run(args []string, stdout, stderr io.Writer) int {
|
||||||
|
fs := flag.NewFlagSet("go-test-sarif", flag.ContinueOnError)
|
||||||
|
fs.SetOutput(stderr)
|
||||||
|
|
||||||
|
var versionFlag bool
|
||||||
|
fs.BoolVar(&versionFlag, "version", false, "Display version information")
|
||||||
|
fs.BoolVar(&versionFlag, "v", false, "Display version information (short)")
|
||||||
|
if err := fs.Parse(args[1:]); err != nil {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if versionFlag {
|
||||||
|
printVersion(stdout)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if fs.NArg() < 2 {
|
||||||
|
printUsage(stderr)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
inputFile := fs.Arg(0)
|
||||||
|
outputFile := fs.Arg(1)
|
||||||
|
|
||||||
|
if err := internal.ConvertToSARIF(inputFile, outputFile); err != nil {
|
||||||
|
_, _ = fmt.Fprintf(stderr, "Error: %v\n", err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
os.Exit(run(os.Args, os.Stdout, os.Stderr))
|
||||||
}
|
}
|
||||||
|
|||||||
147
cmd/main_test.go
Normal file
147
cmd/main_test.go
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRun(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
setupFunc func() (string, string, func())
|
||||||
|
wantExit int
|
||||||
|
wantStdout string
|
||||||
|
wantStderr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "version flag long",
|
||||||
|
args: []string{"go-test-sarif", "--version"},
|
||||||
|
wantExit: 0,
|
||||||
|
wantStdout: "go-test-sarif dev",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "version flag short",
|
||||||
|
args: []string{"go-test-sarif", "-v"},
|
||||||
|
wantExit: 0,
|
||||||
|
wantStdout: "go-test-sarif dev",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing arguments",
|
||||||
|
args: []string{"go-test-sarif"},
|
||||||
|
wantExit: 1,
|
||||||
|
wantStderr: "Usage: go-test-sarif",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only one argument",
|
||||||
|
args: []string{"go-test-sarif", "input.json"},
|
||||||
|
wantExit: 1,
|
||||||
|
wantStderr: "Usage: go-test-sarif",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid conversion",
|
||||||
|
args: []string{"go-test-sarif", "input.json", "output.sarif"},
|
||||||
|
setupFunc: setupValidTestFiles,
|
||||||
|
wantExit: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid input file",
|
||||||
|
args: []string{"go-test-sarif", "nonexistent.json", "output.sarif"},
|
||||||
|
wantExit: 1,
|
||||||
|
wantStderr: "Error:",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid flag",
|
||||||
|
args: []string{"go-test-sarif", "--invalid"},
|
||||||
|
wantExit: 1,
|
||||||
|
wantStderr: "flag provided but not defined",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
var cleanup func()
|
||||||
|
if tt.setupFunc != nil {
|
||||||
|
inputFile, outputFile, cleanupFunc := tt.setupFunc()
|
||||||
|
cleanup = cleanupFunc
|
||||||
|
// Replace placeholders with actual file paths
|
||||||
|
for i, arg := range tt.args {
|
||||||
|
switch arg {
|
||||||
|
case "input.json":
|
||||||
|
tt.args[i] = inputFile
|
||||||
|
case "output.sarif":
|
||||||
|
tt.args[i] = outputFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cleanup != nil {
|
||||||
|
defer cleanup()
|
||||||
|
}
|
||||||
|
|
||||||
|
stdout := &bytes.Buffer{}
|
||||||
|
stderr := &bytes.Buffer{}
|
||||||
|
|
||||||
|
exitCode := run(tt.args, stdout, stderr)
|
||||||
|
|
||||||
|
if exitCode != tt.wantExit {
|
||||||
|
t.Errorf("run() exit code = %v, want %v", exitCode, tt.wantExit)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.wantStdout != "" && !strings.Contains(stdout.String(), tt.wantStdout) {
|
||||||
|
t.Errorf("stdout = %q, want to contain %q", stdout.String(), tt.wantStdout)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.wantStderr != "" && !strings.Contains(stderr.String(), tt.wantStderr) {
|
||||||
|
t.Errorf("stderr = %q, want to contain %q", stderr.String(), tt.wantStderr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrintVersion(t *testing.T) {
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
printVersion(buf)
|
||||||
|
|
||||||
|
output := buf.String()
|
||||||
|
if !strings.Contains(output, "go-test-sarif dev") {
|
||||||
|
t.Errorf("printVersion() = %q, want to contain %q", output, "go-test-sarif dev")
|
||||||
|
}
|
||||||
|
if !strings.Contains(output, "commit: none") {
|
||||||
|
t.Errorf("printVersion() = %q, want to contain %q", output, "commit: none")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrintUsage(t *testing.T) {
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
printUsage(buf)
|
||||||
|
|
||||||
|
output := buf.String()
|
||||||
|
if !strings.Contains(output, "Usage: go-test-sarif <input.json> <output.sarif>") {
|
||||||
|
t.Errorf("printUsage() = %q, want to contain usage information", output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupValidTestFiles() (string, string, func()) {
|
||||||
|
tmpDir, err := os.MkdirTemp("", "go-test-sarif-test")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
inputFile := filepath.Join(tmpDir, "test-input.json")
|
||||||
|
outputFile := filepath.Join(tmpDir, "test-output.sarif")
|
||||||
|
|
||||||
|
// Create a valid test JSON file
|
||||||
|
testJSON := `{"Time":"2023-01-01T00:00:00Z","Action":"pass","Package":"example.com/test","Test":"TestExample","Elapsed":0.1}`
|
||||||
|
if err := os.WriteFile(inputFile, []byte(testJSON), 0644); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup := func() {
|
||||||
|
_ = os.RemoveAll(tmpDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
return inputFile, outputFile, cleanup
|
||||||
|
}
|
||||||
@@ -22,17 +22,13 @@ type TestEvent struct {
|
|||||||
func ConvertToSARIF(inputFile, outputFile string) error {
|
func ConvertToSARIF(inputFile, outputFile string) error {
|
||||||
f, err := os.Open(inputFile)
|
f, err := os.Open(inputFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to read input file: %w", err)
|
return err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() { _ = f.Close() }()
|
||||||
if cerr := f.Close(); cerr != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "failed to close input file: %v\n", cerr)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
report, err := sarif.New(sarif.Version210)
|
report, err := sarif.New(sarif.Version210)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create SARIF report: %w", err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
run := sarif.NewRunWithInformationURI("go-test-sarif", "https://golang.org/cmd/go/#hdr-Test_packages")
|
run := sarif.NewRunWithInformationURI("go-test-sarif", "https://golang.org/cmd/go/#hdr-Test_packages")
|
||||||
@@ -42,22 +38,23 @@ func ConvertToSARIF(inputFile, outputFile string) error {
|
|||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
var event TestEvent
|
var event TestEvent
|
||||||
if err := json.Unmarshal(scanner.Bytes(), &event); err != nil {
|
if err := json.Unmarshal(scanner.Bytes(), &event); err != nil {
|
||||||
return fmt.Errorf("invalid JSON format: %w", err)
|
return fmt.Errorf("invalid JSON: %w", err)
|
||||||
}
|
}
|
||||||
if event.Action == "fail" && (event.Test != "" || event.Package != "") {
|
if event.Action == "fail" && (event.Test != "" || event.Package != "") {
|
||||||
res := sarif.NewRuleResult(rule.ID).
|
result := sarif.NewRuleResult(rule.ID).
|
||||||
WithLevel("error").
|
WithLevel("error").
|
||||||
WithMessage(sarif.NewTextMessage(event.Output))
|
WithMessage(sarif.NewTextMessage(event.Output))
|
||||||
run.AddResult(res)
|
run.AddResult(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := scanner.Err(); err != nil {
|
if err := scanner.Err(); err != nil {
|
||||||
return fmt.Errorf("failed to scan input file: %w", err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
report.AddRun(run)
|
report.AddRun(run)
|
||||||
if err := report.WriteFile(outputFile); err != nil {
|
if err := report.WriteFile(outputFile); err != nil {
|
||||||
return fmt.Errorf("failed to write SARIF output file: %w", err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("SARIF report generated: %s\n", outputFile)
|
fmt.Printf("SARIF report generated: %s\n", outputFile)
|
||||||
|
|||||||
Reference in New Issue
Block a user