// Package release provides release automation with changelog generation and publishing. package release import ( "context" "fmt" "forge.lthn.ai/core/go-devops/sdk" ) // SDKRelease holds the result of an SDK release. type SDKRelease struct { // Version is the SDK version. Version string // Languages that were generated. Languages []string // Output directory. Output string } // RunSDK executes SDK-only release: diff check + generate. // If dryRun is true, it shows what would be done without generating. func RunSDK(ctx context.Context, cfg *Config, dryRun bool) (*SDKRelease, error) { if cfg == nil { return nil, fmt.Errorf("release.RunSDK: config is nil") } if cfg.SDK == nil { return nil, fmt.Errorf("release.RunSDK: sdk not configured in .core/release.yaml") } projectDir := cfg.projectDir if projectDir == "" { projectDir = "." } // Determine version version := cfg.version if version == "" { var err error version, err = DetermineVersion(projectDir) if err != nil { return nil, fmt.Errorf("release.RunSDK: failed to determine version: %w", err) } } // Run diff check if enabled if cfg.SDK.Diff.Enabled { breaking, err := checkBreakingChanges(projectDir, cfg.SDK) if err != nil { // Non-fatal: warn and continue fmt.Printf("Warning: diff check failed: %v\n", err) } else if breaking { if cfg.SDK.Diff.FailOnBreaking { return nil, fmt.Errorf("release.RunSDK: breaking API changes detected") } fmt.Printf("Warning: breaking API changes detected\n") } } // Prepare result output := cfg.SDK.Output if output == "" { output = "sdk" } result := &SDKRelease{ Version: version, Languages: cfg.SDK.Languages, Output: output, } if dryRun { return result, nil } // Generate SDKs sdkCfg := toSDKConfig(cfg.SDK) s := sdk.New(projectDir, sdkCfg) s.SetVersion(version) if err := s.Generate(ctx); err != nil { return nil, fmt.Errorf("release.RunSDK: generation failed: %w", err) } return result, nil } // checkBreakingChanges runs oasdiff to detect breaking changes. func checkBreakingChanges(projectDir string, cfg *SDKConfig) (bool, error) { // Get previous tag for comparison (uses getPreviousTag from changelog.go) prevTag, err := getPreviousTag(projectDir, "HEAD") if err != nil { return false, fmt.Errorf("no previous tag found: %w", err) } // Detect spec path specPath := cfg.Spec if specPath == "" { s := sdk.New(projectDir, nil) specPath, err = s.DetectSpec() if err != nil { return false, err } } // Run diff result, err := sdk.Diff(prevTag, specPath) if err != nil { return false, err } return result.Breaking, nil } // toSDKConfig converts release.SDKConfig to sdk.Config. func toSDKConfig(cfg *SDKConfig) *sdk.Config { if cfg == nil { return nil } return &sdk.Config{ Spec: cfg.Spec, Languages: cfg.Languages, Output: cfg.Output, Package: sdk.PackageConfig{ Name: cfg.Package.Name, Version: cfg.Package.Version, }, Diff: sdk.DiffConfig{ Enabled: cfg.Diff.Enabled, FailOnBreaking: cfg.Diff.FailOnBreaking, }, } }