Versioning Package
pkg/versioning compares OpenAPI specs across versions, detects breaking changes, and generates changelogs and migration guides.
import "github.com/andrianprasetya/open-swag-go/pkg/versioning"Differ
Compares two OpenAPI specs and produces a structured diff.
type Differ struct {
// unexported fields
}
func NewDiffer() *DifferMethods
func (d *Differ) Diff(old, new []byte) (*Diff, error)Accepts two JSON-encoded OpenAPI specs and returns a Diff describing every change. Returns an error if either spec is malformed.
func (d *Differ) DiffFromFiles(oldPath, newPath string) (*Diff, error)Convenience wrapper that reads specs from file paths before diffing.
Usage
differ := versioning.NewDiffer()
diff, err := differ.Diff(oldSpec, newSpec)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Total changes: %d\n", len(diff.Changes))
fmt.Printf("Breaking: %d\n", len(diff.BreakingChanges))Diff
The result of comparing two specs.
type Diff struct {
Changes []Change // All detected changes
BreakingChanges []BreakingChange // Subset of changes that are breaking
Summary string // Human-readable summary
}Change
A single difference between two spec versions.
type Change struct {
Type ChangeType // added, removed, modified
Path string // JSON pointer to the changed element, e.g. "/paths/~1users/get"
Description string // Human-readable description of the change
Breaking 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 {
Change // Embeds Change
Severity Severity // low, medium, high
Migration string // Suggested migration step
}Severity Constants
type Severity string
const (
SeverityLow Severity = "low"
SeverityMedium Severity = "medium"
SeverityHigh Severity = "high"
)Breaking changes are classified by severity:
| Severity | Examples |
|---|---|
| High | Endpoint removed, required parameter added, response schema field removed |
| Medium | Parameter type changed, response status code changed |
| Low | Description changed, example updated, optional field added |
ChangelogGenerator
Produces a human-readable changelog from a Diff.
type ChangelogGenerator struct {
// unexported fields
}
func NewChangelogGenerator() *ChangelogGeneratorMethods
func (g *ChangelogGenerator) Generate(diff *Diff) stringReturns a Markdown-formatted changelog string.
func (g *ChangelogGenerator) GenerateToFile(diff *Diff, path string) errorWrites the changelog to a file.
Usage
gen := versioning.NewChangelogGenerator()
changelog := gen.Generate(diff)
fmt.Println(changelog)Output:
## Changelog
### Breaking Changes
- **DELETE** `/users/{id}` — Endpoint removed (high severity)
- **PUT** `/users/{id}` — Required field `email` added to request body (medium severity)
### Non-Breaking Changes
- **POST** `/users` — Added optional `nickname` field to request body
- **GET** `/products` — New `category` query parameterMigrationGenerator
Produces step-by-step migration instructions from breaking changes.
type MigrationGenerator struct {
// unexported fields
}
func NewMigrationGenerator() *MigrationGeneratorMethods
func (g *MigrationGenerator) Generate(diff *Diff) stringReturns a Markdown-formatted migration guide covering only breaking changes.
func (g *MigrationGenerator) GenerateToFile(diff *Diff, path string) errorWrites the migration guide to a file.
Usage
mgen := versioning.NewMigrationGenerator()
guide := mgen.Generate(diff)
fmt.Println(guide)Quick Example
package main
import (
"fmt"
"log"
"os"
"github.com/andrianprasetya/open-swag-go/pkg/versioning"
)
func main() {
oldSpec, _ := os.ReadFile("spec-v1.json")
newSpec, _ := os.ReadFile("spec-v2.json")
differ := versioning.NewDiffer()
diff, err := differ.Diff(oldSpec, newSpec)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Changes: %d, Breaking: %d\n",
len(diff.Changes), len(diff.BreakingChanges))
changelog := versioning.NewChangelogGenerator().Generate(diff)
fmt.Println(changelog)
if len(diff.BreakingChanges) > 0 {
guide := versioning.NewMigrationGenerator().Generate(diff)
os.WriteFile("MIGRATION.md", []byte(guide), 0644)
}
}