1
0
Fork 0
mirror of https://github.com/imjasonh/apidiff-action synced 2026-07-06 22:52:43 +00:00

Initial commit of goapidiff

A Go tool that analyzes API changes between two versions and reports breaking changes.

Features:
- Detects breaking API changes in Go code
- Compares git refs or directories
- Ignores internal/ packages by default
- Multiple output formats: text, JSON, markdown
- CI-friendly exit codes

Built on top of golang.org/x/exp/apidiff which provides the core API analysis.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Jason Hall 2025-07-29 10:25:22 -04:00
commit 449d0c57e6
Failed to extract signature
19 changed files with 1830 additions and 0 deletions

45
testdata/break/new/example.go vendored Normal file
View file

@ -0,0 +1,45 @@
package example
import "fmt"
// Greeter provides greeting functionality
type Greeter struct {
Name string
Language string // Added field - compatible change
}
// Greet returns a greeting message
// Breaking change: added parameter
func (g *Greeter) Greet(formal bool) string {
if formal {
return fmt.Sprintf("Good day, %s!", g.Name)
}
return fmt.Sprintf("Hello, %s!", g.Name)
}
// Add adds two integers
func Add(a, b int) int {
return a + b
}
// Multiply multiplies two integers (new function - compatible)
func Multiply(a, b int) int {
return a * b
}
// Config holds configuration
type Config struct {
Host string
Port int
Timeout int // Added field - compatible change
}
// Status represents service status
type Status int
const (
StatusUnknown Status = iota
StatusReady
StatusError
StatusStarting // Added constant - compatible change
)

33
testdata/break/old/example.go vendored Normal file
View file

@ -0,0 +1,33 @@
package example
import "fmt"
// Greeter provides greeting functionality
type Greeter struct {
Name string
}
// Greet returns a greeting message
func (g *Greeter) Greet() string {
return fmt.Sprintf("Hello, %s!", g.Name)
}
// Add adds two integers
func Add(a, b int) int {
return a + b
}
// Config holds configuration
type Config struct {
Host string
Port int
}
// Status represents service status
type Status int
const (
StatusUnknown Status = iota
StatusReady
StatusError
)

41
testdata/clean/new/math.go vendored Normal file
View file

@ -0,0 +1,41 @@
package math
// Add adds two integers
func Add(a, b int) int {
// Implementation changed but API is the same
result := a + b
return result
}
// Subtract subtracts b from a
func Subtract(a, b int) int {
// Refactored implementation
diff := a - b
return diff
}
// Calculator provides basic math operations
type Calculator struct {
precision int
}
// NewCalculator creates a new calculator
func NewCalculator() *Calculator {
// Changed internal default but API is the same
return &Calculator{precision: 4}
}
// Compute performs a calculation
func (c *Calculator) Compute(operation string, a, b float64) float64 {
// Refactored with better performance
var result float64
switch operation {
case "add":
result = a + b
case "subtract":
result = a - b
default:
result = 0
}
return result
}

33
testdata/clean/old/math.go vendored Normal file
View file

@ -0,0 +1,33 @@
package math
// Add adds two integers
func Add(a, b int) int {
return a + b
}
// Subtract subtracts b from a
func Subtract(a, b int) int {
return a - b
}
// Calculator provides basic math operations
type Calculator struct {
precision int
}
// NewCalculator creates a new calculator
func NewCalculator() *Calculator {
return &Calculator{precision: 2}
}
// Compute performs a calculation
func (c *Calculator) Compute(operation string, a, b float64) float64 {
switch operation {
case "add":
return a + b
case "subtract":
return a - b
default:
return 0
}
}

View file

@ -0,0 +1,19 @@
package internal
// Helper is an internal helper function
// BREAKING: Changed return type (but should be ignored)
func Helper(s string) (string, error) {
return s + "!", nil
}
// InternalType is an internal type
// BREAKING: Removed field (but should be ignored)
type InternalType struct {
NewValue string
}
// Process does internal processing
// BREAKING: Changed signature (but should be ignored)
func (i *InternalType) Process(multiplier int) string {
return i.NewValue
}

16
testdata/internal/new/public.go vendored Normal file
View file

@ -0,0 +1,16 @@
package example
// PublicFunc is a public function
// BREAKING: Added required parameter
func PublicFunc(s string, uppercase bool) string {
if uppercase {
return s
}
return s
}
// PublicType is a public type
type PublicType struct {
Name string
Email string // Added field - compatible
}

View file

@ -0,0 +1,16 @@
package internal
// Helper is an internal helper function
func Helper(s string) string {
return s + "!"
}
// InternalType is an internal type
type InternalType struct {
Value int
}
// Process does internal processing
func (i *InternalType) Process() int {
return i.Value * 2
}

11
testdata/internal/old/public.go vendored Normal file
View file

@ -0,0 +1,11 @@
package example
// PublicFunc is a public function
func PublicFunc(s string) string {
return s
}
// PublicType is a public type
type PublicType struct {
Name string
}

78
testdata/safe/new/api.go vendored Normal file
View file

@ -0,0 +1,78 @@
package api
import "time"
// User represents a user in the system
type User struct {
ID string
Name string
Email string // Added field - compatible change
CreatedAt time.Time // Added field - compatible change
}
// GetUser retrieves a user by ID
func GetUser(id string) (*User, error) {
// Mock implementation
return &User{ID: id, Name: "John Doe"}, nil
}
// GetUsers retrieves all users (new function - compatible)
func GetUsers() ([]*User, error) {
return []*User{}, nil
}
// UpdateUser updates a user (new function - compatible)
func UpdateUser(user *User) error {
return nil
}
// Status represents the service status
type Status string
const (
StatusOK Status = "ok"
StatusError Status = "error"
StatusPending Status = "pending" // Added constant - compatible change
StatusUnknown Status = "unknown" // Added constant - compatible change
)
// Service provides API functionality
type Service struct {
endpoint string
timeout time.Duration // Added field - compatible change
}
// NewService creates a new service
func NewService(endpoint string) *Service {
return &Service{endpoint: endpoint}
}
// NewServiceWithTimeout creates a new service with timeout (new function - compatible)
func NewServiceWithTimeout(endpoint string, timeout time.Duration) *Service {
return &Service{endpoint: endpoint, timeout: timeout}
}
// Call makes an API call
func (s *Service) Call(method string) error {
// Mock implementation
return nil
}
// CallWithContext makes an API call with context (new method - compatible)
func (s *Service) CallWithContext(method string, timeout time.Duration) error {
return nil
}
// GetStatus returns the service status (new method - compatible)
func (s *Service) GetStatus() Status {
return StatusOK
}
// ErrorCode represents error codes (new type - compatible)
type ErrorCode int
const (
ErrNone ErrorCode = 0 // New constant - compatible
ErrInvalid ErrorCode = 1 // New constant - compatible
ErrTimeout ErrorCode = 2 // New constant - compatible
)

37
testdata/safe/old/api.go vendored Normal file
View file

@ -0,0 +1,37 @@
package api
// User represents a user in the system
type User struct {
ID string
Name string
}
// GetUser retrieves a user by ID
func GetUser(id string) (*User, error) {
// Mock implementation
return &User{ID: id, Name: "John Doe"}, nil
}
// Status represents the service status
type Status string
const (
StatusOK Status = "ok"
StatusError Status = "error"
)
// Service provides API functionality
type Service struct {
endpoint string
}
// NewService creates a new service
func NewService(endpoint string) *Service {
return &Service{endpoint: endpoint}
}
// Call makes an API call
func (s *Service) Call(method string) error {
// Mock implementation
return nil
}