Versioning Package
pkg/versioning compares OpenAPI specs across versions, detects breaking changes, and generates changelogs and migration guides.
import "github.com/gopackx/open-swag-go/pkg/versioning"Differ
Compares two OpenAPI specs and produces a structured diff.
type Differ struct{}
func NewDiffer() *DifferMethods
func (d *Differ) Compare(oldSpec, newSpec map[string]interface{}) (*Diff, error)
func (d *Differ) CompareFiles(oldPath, newPath string) (*Diff, error)Compare takes two decoded OpenAPI specs (e.g. from json.Unmarshal into a map[string]interface{}). CompareFiles reads and decodes two spec files first.
Usage
differ := versioning.NewDiffer()
diff, err := differ.CompareFiles("spec-v1.json", "spec-v2.json")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Total changes: %d\n", len(diff.Changes))
fmt.Printf("Breaking: %d\n", len(diff.Breaking))Diff
The result of comparing two specs.
type Diff struct {
OldVersion string // Version of the old spec
NewVersion string // Version of the new spec
Changes []Change // All detected changes
Breaking []BreakingChange // Subset of changes that are breaking
Summary Summary // Aggregate counts
}
func (d *Diff) HasBreakingChanges() boolSummary
type Summary struct {
AddedEndpoints int
RemovedEndpoints int
ModifiedEndpoints int
BreakingChanges int
}Change
A single difference between two spec versions.
type Change struct {
Type ChangeType // added, removed, modified
Path string // API path, e.g. "/users/{id}"
Method string // HTTP method (when applicable)
Description string // Human-readable description
IsBreaking bool // Whether this change is breaking
}ChangeType constants
type ChangeType string
const (
ChangeAdded ChangeType = "added"
ChangeRemoved ChangeType = "removed"
ChangeModified ChangeType = "modified"
)BreakingChange
A change that may break existing API consumers.
type BreakingChange struct {
Path string // API path
Method string // HTTP method
Reason string // Why the change is breaking
Migration string // Suggested migration step
}What the differ detects
Compare and CompareFiles walk the two specs and flag the following as
breaking changes:
- Endpoint removed — a path+method that existed in old but not new.
- Request body removed — for an endpoint where it existed before.
- Required request body added — body added with
required: true. - New required field in a request body schema.
- Field type changed — JSON
typeorformatof an existing request-body property differs. v1.2.0 added this — earlier versions definedBreakingTypeChangedbut never produced entries for it. - Parameter removed — query/path/header/cookie parameter dropped.
- Parameter type changed — schema
type/formatof an existing parameter differs (also new in v1.2.0). - New required parameter added.
- Response code removed from an existing operation.
Each detected entry includes a migration hint via the auto-generated
BreakingChange.Migration field, suitable for surfacing in changelogs.
Breaking change rules
type BreakingChangeType string
const (
BreakingEndpointRemoved BreakingChangeType = "endpoint_removed"
BreakingParameterRemoved BreakingChangeType = "parameter_removed"
BreakingRequiredAdded BreakingChangeType = "required_field_added"
BreakingResponseRemoved BreakingChangeType = "response_removed"
BreakingTypeChanged BreakingChangeType = "type_changed"
BreakingRequestBodyRemoved BreakingChangeType = "request_body_removed"
BreakingSecurityAdded BreakingChangeType = "security_added"
)
type BreakingChangeRule struct {
Type BreakingChangeType
Description string
Severity string // "error" or "warning"
}
func DefaultBreakingRules() []BreakingChangeRule
func IsBreaking(changeType BreakingChangeType) boolChangelogGenerator
Produces a structured changelog from a Diff.
type ChangelogGenerator struct{}
func NewChangelogGenerator() *ChangelogGenerator
func (g *ChangelogGenerator) Generate(diff *Diff) *ChangelogEntry
type ChangelogEntry struct {
Version string
Date time.Time
Added []string
Changed []string
Removed []string
Fixed []string
Breaking []string
}
func (e *ChangelogEntry) ToMarkdown() string
// Convenience: build a Markdown changelog in one call.
func GenerateChangelog(diff *Diff) stringUsage
entry := versioning.NewChangelogGenerator().Generate(diff)
fmt.Println(entry.ToMarkdown())
// or, in one step:
fmt.Println(versioning.GenerateChangelog(diff))MigrationGenerator
Produces a step-by-step migration guide from breaking changes.
type MigrationGenerator struct{}
func NewMigrationGenerator() *MigrationGenerator
func (g *MigrationGenerator) Generate(diff *Diff) *MigrationGuide
type MigrationGuide struct {
FromVersion string
ToVersion string
Steps []MigrationStep
}
func (g *MigrationGuide) ToMarkdown() string
type MigrationStep struct {
Title string
Description string
Before string
After string
Endpoint string
Method string
}Quick Example
package main
import (
"fmt"
"log"
"os"
"github.com/gopackx/open-swag-go/pkg/versioning"
)
func main() {
differ := versioning.NewDiffer()
diff, err := differ.CompareFiles("spec-v1.json", "spec-v2.json")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Changes: %d, Breaking: %d\n", len(diff.Changes), len(diff.Breaking))
fmt.Println(versioning.GenerateChangelog(diff))
if diff.HasBreakingChanges() {
guide := versioning.NewMigrationGenerator().Generate(diff)
os.WriteFile("MIGRATION.md", []byte(guide.ToMarkdown()), 0644)
}
}