This commit is contained in:
2025-07-28 15:46:38 +03:00
parent 828c6bb3e1
commit 54097ba74d
7 changed files with 314 additions and 26 deletions

1
.gitignore vendored
View File

@@ -26,3 +26,4 @@ logs
bin/*
!bin/.gitkeep
megalinter-reports
dist/*

79
.goreleaser.yml Normal file
View 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

View File

@@ -44,3 +44,27 @@ docker-build:
docker-run:
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
# 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

View File

@@ -1,25 +1,65 @@
// main package contains the cli functionality
package main
import (
"flag"
"fmt"
"io"
"os"
"github.com/ivuorinen/go-test-sarif-action/internal"
)
func main() {
if len(os.Args) < 3 {
fmt.Println("Usage: go-test-sarif <input.json> <output.sarif>")
os.Exit(1)
}
var (
version = "dev"
commit = "none"
date = "unknown"
builtBy = "unknown"
)
inputFile := os.Args[1]
outputFile := os.Args[2]
err := internal.ConvertToSARIF(inputFile, outputFile)
if err != nil {
fmt.Println("Error:", err)
os.Exit(1)
}
func printVersion(w io.Writer) {
_, _ = fmt.Fprintf(w, "go-test-sarif %s\n", version)
_, _ = fmt.Fprintf(w, " commit: %s\n", commit)
_, _ = fmt.Fprintf(w, " built at: %s\n", date)
_, _ = fmt.Fprintf(w, " built by: %s\n", builtBy)
}
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
View 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
}

View File

@@ -22,17 +22,13 @@ type TestEvent struct {
func ConvertToSARIF(inputFile, outputFile string) error {
f, err := os.Open(inputFile)
if err != nil {
return fmt.Errorf("failed to read input file: %w", err)
return err
}
defer func() {
if cerr := f.Close(); cerr != nil {
fmt.Fprintf(os.Stderr, "failed to close input file: %v\n", cerr)
}
}()
defer func() { _ = f.Close() }()
report, err := sarif.New(sarif.Version210)
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")
@@ -42,22 +38,23 @@ func ConvertToSARIF(inputFile, outputFile string) error {
for scanner.Scan() {
var event TestEvent
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 != "") {
res := sarif.NewRuleResult(rule.ID).
result := sarif.NewRuleResult(rule.ID).
WithLevel("error").
WithMessage(sarif.NewTextMessage(event.Output))
run.AddResult(res)
run.AddResult(result)
}
}
if err := scanner.Err(); err != nil {
return fmt.Errorf("failed to scan input file: %w", err)
return err
}
report.AddRun(run)
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)