refactor(cli): separate build and publish concerns
- Move SDK generation to `core build sdk` subcommand - Make `core ci` publish-only (expects artifacts in dist/) - Add release.Publish() for publishing pre-built artifacts - Keep `core sdk diff` and `core sdk validate` for API validation - Update SKILL.md documentation This separation prevents accidental releases - running `core ci` without first building will fail safely. Workflow: core build # Build binaries core build sdk # Build SDKs core ci # Publish what's in dist/ Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
148670fd82
commit
331032cd57
5 changed files with 274 additions and 116 deletions
|
|
@ -25,7 +25,8 @@ The `core` command provides a unified interface for Go/Wails development, multi-
|
||||||
| Deploy PHP app | `core php deploy` | Coolify deployment |
|
| Deploy PHP app | `core php deploy` | Coolify deployment |
|
||||||
| Build project | `core build` | Auto-detects project type |
|
| Build project | `core build` | Auto-detects project type |
|
||||||
| Build for targets | `core build --targets linux/amd64,darwin/arm64` | Cross-compile |
|
| Build for targets | `core build --targets linux/amd64,darwin/arm64` | Cross-compile |
|
||||||
| Release | `core ci` | Build + publish to GitHub/npm/Homebrew |
|
| Build SDK | `core build sdk` | Generate API clients from OpenAPI |
|
||||||
|
| Publish release | `core ci` | Publish pre-built artifacts |
|
||||||
| Check environment | `core doctor` | Verify tools installed |
|
| Check environment | `core doctor` | Verify tools installed |
|
||||||
| Multi-repo status | `core dev health` | Quick summary across repos |
|
| Multi-repo status | `core dev health` | Quick summary across repos |
|
||||||
| Multi-repo workflow | `core dev work` | Status + commit + push |
|
| Multi-repo workflow | `core dev work` | Status + commit + push |
|
||||||
|
|
@ -35,7 +36,8 @@ The `core` command provides a unified interface for Go/Wails development, multi-
|
||||||
| List issues | `core dev issues` | Open issues across repos |
|
| List issues | `core dev issues` | Open issues across repos |
|
||||||
| List PRs | `core dev reviews` | PRs needing review |
|
| List PRs | `core dev reviews` | PRs needing review |
|
||||||
| Check CI | `core dev ci` | GitHub Actions status |
|
| Check CI | `core dev ci` | GitHub Actions status |
|
||||||
| Generate SDK | `core sdk` | Generate API clients from OpenAPI |
|
| Validate OpenAPI | `core sdk validate` | Validate OpenAPI spec |
|
||||||
|
| Check API changes | `core sdk diff` | Detect breaking API changes |
|
||||||
| Sync docs | `core docs sync` | Sync docs across repos |
|
| Sync docs | `core docs sync` | Sync docs across repos |
|
||||||
| Search packages | `core pkg search <query>` | GitHub search for core-* repos |
|
| Search packages | `core pkg search <query>` | GitHub search for core-* repos |
|
||||||
| Install package | `core pkg install <name>` | Clone and register package |
|
| Install package | `core pkg install <name>` | Clone and register package |
|
||||||
|
|
@ -65,6 +67,31 @@ core build --ci
|
||||||
|
|
||||||
**Why:** Handles cross-compilation, code signing, archiving, checksums, and CI output formatting.
|
**Why:** Handles cross-compilation, code signing, archiving, checksums, and CI output formatting.
|
||||||
|
|
||||||
|
## Releasing
|
||||||
|
|
||||||
|
Build and publish are **separated** to prevent accidental releases:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Step 1: Build artifacts (safe - no publishing)
|
||||||
|
core build
|
||||||
|
core build sdk
|
||||||
|
|
||||||
|
# Step 2: Publish to configured targets (requires pre-built artifacts)
|
||||||
|
core ci # Publish what's in dist/
|
||||||
|
core ci --dry-run # Preview what would be published
|
||||||
|
core ci --draft # Create as draft release
|
||||||
|
core ci --prerelease # Mark as prerelease
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why separate?** Running `core ci` without first building will fail safely - no accidental publishes.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Release workflow utilities
|
||||||
|
core ci init # Initialize .core/release.yaml
|
||||||
|
core ci changelog # Generate changelog from commits
|
||||||
|
core ci version # Show determined version
|
||||||
|
```
|
||||||
|
|
||||||
## Multi-Repo Workflow
|
## Multi-Repo Workflow
|
||||||
|
|
||||||
When working across host-uk repositories:
|
When working across host-uk repositories:
|
||||||
|
|
@ -121,14 +148,31 @@ Generate API clients from OpenAPI specs:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Generate all configured SDKs
|
# Generate all configured SDKs
|
||||||
core sdk
|
core build sdk
|
||||||
|
|
||||||
# Generate specific language
|
# Generate specific language
|
||||||
core sdk --lang typescript
|
core build sdk --lang typescript
|
||||||
core sdk --lang php
|
core build sdk --lang php
|
||||||
|
|
||||||
# Specify OpenAPI spec
|
# Specify OpenAPI spec
|
||||||
core sdk --spec ./openapi.yaml
|
core build sdk --spec ./openapi.yaml
|
||||||
|
|
||||||
|
# Preview without generating
|
||||||
|
core build sdk --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
## SDK Validation
|
||||||
|
|
||||||
|
Validate specs and check for breaking changes:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Validate OpenAPI spec
|
||||||
|
core sdk validate
|
||||||
|
core sdk validate --spec ./api.yaml
|
||||||
|
|
||||||
|
# Check for breaking API changes
|
||||||
|
core sdk diff --base v1.0.0
|
||||||
|
core sdk diff --base ./old-api.yaml --spec ./new-api.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
@ -476,7 +520,8 @@ Go project?
|
||||||
└── Lint: core go lint
|
└── Lint: core go lint
|
||||||
└── Tidy modules: core go mod tidy
|
└── Tidy modules: core go mod tidy
|
||||||
└── Build: core build [--targets <os/arch>]
|
└── Build: core build [--targets <os/arch>]
|
||||||
└── Release: core ci
|
└── Build SDK: core build sdk
|
||||||
|
└── Publish: core ci [--dry-run]
|
||||||
|
|
||||||
PHP/Laravel project?
|
PHP/Laravel project?
|
||||||
└── Start dev: core php dev [--https]
|
└── Start dev: core php dev [--https]
|
||||||
|
|
@ -526,6 +571,8 @@ Managing packages?
|
||||||
| Raw `linuxkit run` | `core vm run` | Unified interface, templates |
|
| Raw `linuxkit run` | `core vm run` | Unified interface, templates |
|
||||||
| `gh repo clone` | `core pkg install` | Auto-detects org, adds to registry |
|
| `gh repo clone` | `core pkg install` | Auto-detects org, adds to registry |
|
||||||
| Manual GitHub search | `core pkg search` | Filtered to org, formatted output |
|
| Manual GitHub search | `core pkg search` | Filtered to org, formatted output |
|
||||||
|
| `core ci` without build | `core build && core ci` | Build first, then publish |
|
||||||
|
| `core sdk generate` | `core build sdk` | SDK generation moved to build |
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"github.com/host-uk/core/pkg/build"
|
"github.com/host-uk/core/pkg/build"
|
||||||
"github.com/host-uk/core/pkg/build/builders"
|
"github.com/host-uk/core/pkg/build/builders"
|
||||||
"github.com/host-uk/core/pkg/build/signing"
|
"github.com/host-uk/core/pkg/build/signing"
|
||||||
|
"github.com/host-uk/core/pkg/sdk"
|
||||||
"github.com/leaanthony/clir"
|
"github.com/leaanthony/clir"
|
||||||
"github.com/leaanthony/debme"
|
"github.com/leaanthony/debme"
|
||||||
"github.com/leaanthony/gosod"
|
"github.com/leaanthony/gosod"
|
||||||
|
|
@ -125,6 +126,25 @@ func AddBuildCommand(app *clir.Cli) {
|
||||||
}
|
}
|
||||||
return runPwaBuild(pwaURL)
|
return runPwaBuild(pwaURL)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// --- `build sdk` command ---
|
||||||
|
sdkBuildCmd := buildCmd.NewSubCommand("sdk", "Generate API SDKs from OpenAPI spec")
|
||||||
|
sdkBuildCmd.LongDescription("Generates typed API clients from OpenAPI specifications.\n" +
|
||||||
|
"Supports TypeScript, Python, Go, and PHP.\n\n" +
|
||||||
|
"Examples:\n" +
|
||||||
|
" core build sdk # Generate all configured SDKs\n" +
|
||||||
|
" core build sdk --lang typescript # Generate only TypeScript SDK\n" +
|
||||||
|
" core build sdk --spec api.yaml # Use specific OpenAPI spec")
|
||||||
|
|
||||||
|
var sdkSpec, sdkLang, sdkVersion string
|
||||||
|
var sdkDryRun bool
|
||||||
|
sdkBuildCmd.StringFlag("spec", "Path to OpenAPI spec file", &sdkSpec)
|
||||||
|
sdkBuildCmd.StringFlag("lang", "Generate only this language (typescript, python, go, php)", &sdkLang)
|
||||||
|
sdkBuildCmd.StringFlag("version", "Version to embed in generated SDKs", &sdkVersion)
|
||||||
|
sdkBuildCmd.BoolFlag("dry-run", "Show what would be generated without writing files", &sdkDryRun)
|
||||||
|
sdkBuildCmd.Action(func() error {
|
||||||
|
return runBuildSDK(sdkSpec, sdkLang, sdkVersion, sdkDryRun)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// runProjectBuild handles the main `core build` command with auto-detection.
|
// runProjectBuild handles the main `core build` command with auto-detection.
|
||||||
|
|
@ -516,6 +536,73 @@ func getBuilder(projectType build.ProjectType) (build.Builder, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- SDK Build Logic ---
|
||||||
|
|
||||||
|
func runBuildSDK(specPath, lang, version string, dryRun bool) error {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
projectDir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get working directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load config
|
||||||
|
config := sdk.DefaultConfig()
|
||||||
|
if specPath != "" {
|
||||||
|
config.Spec = specPath
|
||||||
|
}
|
||||||
|
|
||||||
|
s := sdk.New(projectDir, config)
|
||||||
|
if version != "" {
|
||||||
|
s.SetVersion(version)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%s Generating SDKs\n", buildHeaderStyle.Render("Build SDK:"))
|
||||||
|
if dryRun {
|
||||||
|
fmt.Printf(" %s\n", buildDimStyle.Render("(dry-run mode)"))
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
// Detect spec
|
||||||
|
detectedSpec, err := s.DetectSpec()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("%s %v\n", buildErrorStyle.Render("Error:"), err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf(" Spec: %s\n", buildTargetStyle.Render(detectedSpec))
|
||||||
|
|
||||||
|
if dryRun {
|
||||||
|
if lang != "" {
|
||||||
|
fmt.Printf(" Language: %s\n", buildTargetStyle.Render(lang))
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" Languages: %s\n", buildTargetStyle.Render(strings.Join(config.Languages, ", ")))
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Printf("%s Would generate SDKs (dry-run)\n", buildSuccessStyle.Render("OK:"))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if lang != "" {
|
||||||
|
// Generate single language
|
||||||
|
if err := s.GenerateLanguage(ctx, lang); err != nil {
|
||||||
|
fmt.Printf("%s %v\n", buildErrorStyle.Render("Error:"), err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf(" Generated: %s\n", buildTargetStyle.Render(lang))
|
||||||
|
} else {
|
||||||
|
// Generate all
|
||||||
|
if err := s.Generate(ctx); err != nil {
|
||||||
|
fmt.Printf("%s %v\n", buildErrorStyle.Render("Error:"), err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf(" Generated: %s\n", buildTargetStyle.Render(strings.Join(config.Languages, ", ")))
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Printf("%s SDK generation complete\n", buildSuccessStyle.Render("Success:"))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// --- PWA Build Logic ---
|
// --- PWA Build Logic ---
|
||||||
|
|
||||||
func runPwaBuild(pwaURL string) error {
|
func runPwaBuild(pwaURL string) error {
|
||||||
|
|
|
||||||
|
|
@ -45,20 +45,15 @@ func AddCIReleaseCommand(app *clir.Cli) {
|
||||||
var version string
|
var version string
|
||||||
var draft bool
|
var draft bool
|
||||||
var prerelease bool
|
var prerelease bool
|
||||||
var target string
|
|
||||||
|
|
||||||
releaseCmd.BoolFlag("dry-run", "Preview release without publishing", &dryRun)
|
releaseCmd.BoolFlag("dry-run", "Preview release without publishing", &dryRun)
|
||||||
releaseCmd.StringFlag("version", "Version to release (e.g., v1.2.3)", &version)
|
releaseCmd.StringFlag("version", "Version to release (e.g., v1.2.3)", &version)
|
||||||
releaseCmd.BoolFlag("draft", "Create release as a draft", &draft)
|
releaseCmd.BoolFlag("draft", "Create release as a draft", &draft)
|
||||||
releaseCmd.BoolFlag("prerelease", "Mark release as a prerelease", &prerelease)
|
releaseCmd.BoolFlag("prerelease", "Mark release as a prerelease", &prerelease)
|
||||||
releaseCmd.StringFlag("target", "CIRelease target (sdk)", &target)
|
|
||||||
|
|
||||||
// Default action for `core release`
|
// Default action for `core ci` - publish only (expects artifacts in dist/)
|
||||||
releaseCmd.Action(func() error {
|
releaseCmd.Action(func() error {
|
||||||
if target == "sdk" {
|
return runCIPublish(dryRun, version, draft, prerelease)
|
||||||
return runCIReleaseSDK(dryRun, version)
|
|
||||||
}
|
|
||||||
return runCIRelease(dryRun, version, draft, prerelease)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// `release init` subcommand
|
// `release init` subcommand
|
||||||
|
|
@ -86,8 +81,9 @@ func AddCIReleaseCommand(app *clir.Cli) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// runCIRelease executes the main release workflow.
|
// runCIPublish publishes pre-built artifacts from dist/.
|
||||||
func runCIRelease(dryRun bool, version string, draft, prerelease bool) error {
|
// It does NOT build - use `core build` first.
|
||||||
|
func runCIPublish(dryRun bool, version string, draft, prerelease bool) error {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
// Get current directory
|
// Get current directory
|
||||||
|
|
@ -120,14 +116,19 @@ func runCIRelease(dryRun bool, version string, draft, prerelease bool) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print header
|
// Print header
|
||||||
fmt.Printf("%s Starting release process\n", releaseHeaderStyle.Render("CIRelease:"))
|
fmt.Printf("%s Publishing release\n", releaseHeaderStyle.Render("CI:"))
|
||||||
if dryRun {
|
if dryRun {
|
||||||
fmt.Printf(" %s\n", releaseDimStyle.Render("(dry-run mode)"))
|
fmt.Printf(" %s\n", releaseDimStyle.Render("(dry-run mode)"))
|
||||||
}
|
}
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
|
|
||||||
// Run the release
|
// Check for publishers
|
||||||
rel, err := release.Run(ctx, cfg, dryRun)
|
if len(cfg.Publishers) == 0 {
|
||||||
|
return fmt.Errorf("no publishers configured in .core/release.yaml")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Publish pre-built artifacts
|
||||||
|
rel, err := release.Publish(ctx, cfg, dryRun)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("%s %v\n", releaseErrorStyle.Render("Error:"), err)
|
fmt.Printf("%s %v\n", releaseErrorStyle.Render("Error:"), err)
|
||||||
return err
|
return err
|
||||||
|
|
@ -135,11 +136,11 @@ func runCIRelease(dryRun bool, version string, draft, prerelease bool) error {
|
||||||
|
|
||||||
// Print summary
|
// Print summary
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Printf("%s CIRelease completed!\n", releaseSuccessStyle.Render("Success:"))
|
fmt.Printf("%s Publish completed!\n", releaseSuccessStyle.Render("Success:"))
|
||||||
fmt.Printf(" Version: %s\n", releaseValueStyle.Render(rel.Version))
|
fmt.Printf(" Version: %s\n", releaseValueStyle.Render(rel.Version))
|
||||||
fmt.Printf(" Artifacts: %d\n", len(rel.Artifacts))
|
fmt.Printf(" Artifacts: %d\n", len(rel.Artifacts))
|
||||||
|
|
||||||
if !dryRun && len(cfg.Publishers) > 0 {
|
if !dryRun {
|
||||||
for _, pub := range cfg.Publishers {
|
for _, pub := range cfg.Publishers {
|
||||||
fmt.Printf(" Published: %s\n", releaseValueStyle.Render(pub.Type))
|
fmt.Printf(" Published: %s\n", releaseValueStyle.Render(pub.Type))
|
||||||
}
|
}
|
||||||
|
|
@ -148,50 +149,6 @@ func runCIRelease(dryRun bool, version string, draft, prerelease bool) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// runCIReleaseSDK executes SDK-only release.
|
|
||||||
func runCIReleaseSDK(dryRun bool, version string) error {
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
projectDir, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to get working directory: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load configuration
|
|
||||||
cfg, err := release.LoadConfig(projectDir)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to load config: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply CLI overrides
|
|
||||||
if version != "" {
|
|
||||||
cfg.SetVersion(version)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print header
|
|
||||||
fmt.Printf("%s Generating SDKs\n", releaseHeaderStyle.Render("SDK CIRelease:"))
|
|
||||||
if dryRun {
|
|
||||||
fmt.Printf(" %s\n", releaseDimStyle.Render("(dry-run mode)"))
|
|
||||||
}
|
|
||||||
fmt.Println()
|
|
||||||
|
|
||||||
// Run SDK release
|
|
||||||
result, err := release.RunSDK(ctx, cfg, dryRun)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("%s %v\n", releaseErrorStyle.Render("Error:"), err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print summary
|
|
||||||
fmt.Println()
|
|
||||||
fmt.Printf("%s SDK generation complete!\n", releaseSuccessStyle.Render("Success:"))
|
|
||||||
fmt.Printf(" Version: %s\n", releaseValueStyle.Render(result.Version))
|
|
||||||
fmt.Printf(" Languages: %v\n", result.Languages)
|
|
||||||
fmt.Printf(" Output: %s/\n", releaseValueStyle.Render(result.Output))
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// runCIReleaseInit creates a release configuration interactively.
|
// runCIReleaseInit creates a release configuration interactively.
|
||||||
func runCIReleaseInit() error {
|
func runCIReleaseInit() error {
|
||||||
projectDir, err := os.Getwd()
|
projectDir, err := os.Getwd()
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
|
@ -29,22 +28,16 @@ var (
|
||||||
|
|
||||||
// AddSDKCommand adds the sdk command and its subcommands.
|
// AddSDKCommand adds the sdk command and its subcommands.
|
||||||
func AddSDKCommand(app *clir.Cli) {
|
func AddSDKCommand(app *clir.Cli) {
|
||||||
sdkCmd := app.NewSubCommand("sdk", "Generate and manage API SDKs")
|
sdkCmd := app.NewSubCommand("sdk", "SDK validation and API compatibility tools")
|
||||||
sdkCmd.LongDescription("Generate typed API clients from OpenAPI specs.\n" +
|
sdkCmd.LongDescription("Tools for validating OpenAPI specs and checking API compatibility.\n" +
|
||||||
"Supports TypeScript, Python, Go, and PHP.")
|
"To generate SDKs, use: core build sdk\n\n" +
|
||||||
|
"Commands:\n" +
|
||||||
// sdk generate
|
" diff Check for breaking API changes\n" +
|
||||||
genCmd := sdkCmd.NewSubCommand("generate", "Generate SDKs from OpenAPI spec")
|
" validate Validate OpenAPI spec syntax")
|
||||||
var specPath, lang string
|
|
||||||
genCmd.StringFlag("spec", "Path to OpenAPI spec file", &specPath)
|
|
||||||
genCmd.StringFlag("lang", "Generate only this language", &lang)
|
|
||||||
genCmd.Action(func() error {
|
|
||||||
return runSDKGenerate(specPath, lang)
|
|
||||||
})
|
|
||||||
|
|
||||||
// sdk diff
|
// sdk diff
|
||||||
diffCmd := sdkCmd.NewSubCommand("diff", "Check for breaking API changes")
|
diffCmd := sdkCmd.NewSubCommand("diff", "Check for breaking API changes")
|
||||||
var basePath string
|
var basePath, specPath string
|
||||||
diffCmd.StringFlag("base", "Base spec (version tag or file)", &basePath)
|
diffCmd.StringFlag("base", "Base spec (version tag or file)", &basePath)
|
||||||
diffCmd.StringFlag("spec", "Current spec file", &specPath)
|
diffCmd.StringFlag("spec", "Current spec file", &specPath)
|
||||||
diffCmd.Action(func() error {
|
diffCmd.Action(func() error {
|
||||||
|
|
@ -59,42 +52,6 @@ func AddSDKCommand(app *clir.Cli) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func runSDKGenerate(specPath, lang string) error {
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
projectDir, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to get working directory: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load config
|
|
||||||
config := sdk.DefaultConfig()
|
|
||||||
if specPath != "" {
|
|
||||||
config.Spec = specPath
|
|
||||||
}
|
|
||||||
|
|
||||||
s := sdk.New(projectDir, config)
|
|
||||||
|
|
||||||
fmt.Printf("%s Generating SDKs\n", sdkHeaderStyle.Render("SDK:"))
|
|
||||||
|
|
||||||
if lang != "" {
|
|
||||||
// Generate single language
|
|
||||||
if err := s.GenerateLanguage(ctx, lang); err != nil {
|
|
||||||
fmt.Printf("%s %v\n", sdkErrorStyle.Render("Error:"), err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Generate all
|
|
||||||
if err := s.Generate(ctx); err != nil {
|
|
||||||
fmt.Printf("%s %v\n", sdkErrorStyle.Render("Error:"), err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("%s SDK generation complete\n", sdkSuccessStyle.Render("Success:"))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func runSDKDiff(basePath, specPath string) error {
|
func runSDKDiff(basePath, specPath string) error {
|
||||||
projectDir, err := os.Getwd()
|
projectDir, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,9 @@ package release
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/host-uk/core/pkg/build"
|
"github.com/host-uk/core/pkg/build"
|
||||||
"github.com/host-uk/core/pkg/build/builders"
|
"github.com/host-uk/core/pkg/build/builders"
|
||||||
|
|
@ -25,8 +27,116 @@ type Release struct {
|
||||||
ProjectDir string
|
ProjectDir string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run executes the release process: determine version, build artifacts,
|
// Publish publishes pre-built artifacts from dist/ to configured targets.
|
||||||
|
// Use this after `core build` to separate build and publish concerns.
|
||||||
|
// If dryRun is true, it will show what would be done without actually publishing.
|
||||||
|
func Publish(ctx context.Context, cfg *Config, dryRun bool) (*Release, error) {
|
||||||
|
if cfg == nil {
|
||||||
|
return nil, fmt.Errorf("release.Publish: config is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
projectDir := cfg.projectDir
|
||||||
|
if projectDir == "" {
|
||||||
|
projectDir = "."
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve to absolute path
|
||||||
|
absProjectDir, err := filepath.Abs(projectDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("release.Publish: failed to resolve project directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 1: Determine version
|
||||||
|
version := cfg.version
|
||||||
|
if version == "" {
|
||||||
|
version, err = DetermineVersion(absProjectDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("release.Publish: failed to determine version: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Find pre-built artifacts in dist/
|
||||||
|
distDir := filepath.Join(absProjectDir, "dist")
|
||||||
|
artifacts, err := findArtifacts(distDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("release.Publish: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(artifacts) == 0 {
|
||||||
|
return nil, fmt.Errorf("release.Publish: no artifacts found in dist/\nRun 'core build' first to create artifacts")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: Generate changelog
|
||||||
|
changelog, err := Generate(absProjectDir, "", version)
|
||||||
|
if err != nil {
|
||||||
|
// Non-fatal: continue with empty changelog
|
||||||
|
changelog = fmt.Sprintf("Release %s", version)
|
||||||
|
}
|
||||||
|
|
||||||
|
release := &Release{
|
||||||
|
Version: version,
|
||||||
|
Artifacts: artifacts,
|
||||||
|
Changelog: changelog,
|
||||||
|
ProjectDir: absProjectDir,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 4: Publish to configured targets
|
||||||
|
if len(cfg.Publishers) > 0 {
|
||||||
|
pubRelease := publishers.NewRelease(release.Version, release.Artifacts, release.Changelog, release.ProjectDir)
|
||||||
|
|
||||||
|
for _, pubCfg := range cfg.Publishers {
|
||||||
|
publisher, err := getPublisher(pubCfg.Type)
|
||||||
|
if err != nil {
|
||||||
|
return release, fmt.Errorf("release.Publish: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
extendedCfg := buildExtendedConfig(pubCfg)
|
||||||
|
publisherCfg := publishers.NewPublisherConfig(pubCfg.Type, pubCfg.Prerelease, pubCfg.Draft, extendedCfg)
|
||||||
|
if err := publisher.Publish(ctx, pubRelease, publisherCfg, cfg, dryRun); err != nil {
|
||||||
|
return release, fmt.Errorf("release.Publish: publish to %s failed: %w", pubCfg.Type, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return release, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// findArtifacts discovers pre-built artifacts in the dist directory.
|
||||||
|
func findArtifacts(distDir string) ([]build.Artifact, error) {
|
||||||
|
if _, err := os.Stat(distDir); os.IsNotExist(err) {
|
||||||
|
return nil, fmt.Errorf("dist/ directory not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
var artifacts []build.Artifact
|
||||||
|
|
||||||
|
entries, err := os.ReadDir(distDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read dist/: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
if entry.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
name := entry.Name()
|
||||||
|
path := filepath.Join(distDir, name)
|
||||||
|
|
||||||
|
// Include archives and checksums
|
||||||
|
if strings.HasSuffix(name, ".tar.gz") ||
|
||||||
|
strings.HasSuffix(name, ".zip") ||
|
||||||
|
strings.HasSuffix(name, ".txt") ||
|
||||||
|
strings.HasSuffix(name, ".sig") {
|
||||||
|
artifacts = append(artifacts, build.Artifact{Path: path})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return artifacts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run executes the full release process: determine version, build artifacts,
|
||||||
// generate changelog, and publish to configured targets.
|
// generate changelog, and publish to configured targets.
|
||||||
|
// For separated concerns, prefer using `core build` then `core ci` (Publish).
|
||||||
// If dryRun is true, it will show what would be done without actually publishing.
|
// If dryRun is true, it will show what would be done without actually publishing.
|
||||||
func Run(ctx context.Context, cfg *Config, dryRun bool) (*Release, error) {
|
func Run(ctx context.Context, cfg *Config, dryRun bool) (*Release, error) {
|
||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue