go-build/pkg/sdk/detect.go
Virgil fe0f528581 feat(sdk): always export scramble specs
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-01 21:55:31 +00:00

117 lines
3 KiB
Go

package sdk
import (
"context"
"dappco.re/go/core"
"dappco.re/go/core/build/internal/ax"
coreerr "dappco.re/go/core/log"
)
// commonSpecPaths are checked in order when no spec is configured.
var commonSpecPaths = []string{
"api/openapi.yaml",
"api/openapi.yml",
"api/openapi.json",
"openapi.yaml",
"openapi.yml",
"openapi.json",
"docs/api.yaml",
"docs/api.yml",
"docs/api.json",
"swagger.yaml",
"swagger.yml",
"swagger.json",
}
// DetectSpec finds the OpenAPI spec file.
// Priority: config path -> common paths -> Laravel Scramble.
//
// path, err := s.DetectSpec() // → "api/openapi.yaml", nil
func (s *SDK) DetectSpec() (string, error) {
// 1. Check configured path
if s.config.Spec != "" {
specPath := ax.Join(s.projectDir, s.config.Spec)
if ax.IsFile(specPath) {
return specPath, nil
}
return "", coreerr.E("sdk.DetectSpec", "configured spec not found: "+s.config.Spec, nil)
}
// 2. Check common paths
for _, p := range commonSpecPaths {
specPath := ax.Join(s.projectDir, p)
if ax.IsFile(specPath) {
return specPath, nil
}
}
// 3. Try Laravel Scramble detection
composerPath := ax.Join(s.projectDir, "composer.json")
if ax.IsFile(composerPath) {
data, err := ax.ReadFile(composerPath)
if err != nil {
return "", err
}
if containsScramble(string(data)) {
specPath, err := s.detectScramble()
if err != nil {
return "", err
}
return specPath, nil
}
}
return "", coreerr.E("sdk.DetectSpec", "no OpenAPI spec found (checked config, common paths, Scramble)", nil)
}
// detectScramble checks for Laravel Scramble and exports the spec.
func (s *SDK) detectScramble() (string, error) {
composerPath := ax.Join(s.projectDir, "composer.json")
if !ax.IsFile(composerPath) {
return "", coreerr.E("sdk.detectScramble", "no composer.json", nil)
}
// Check for scramble in composer.json
data, err := ax.ReadFile(composerPath)
if err != nil {
return "", err
}
// Simple check for scramble package
if !containsScramble(string(data)) {
return "", coreerr.E("sdk.detectScramble", "scramble not found in composer.json", nil)
}
scrambleSpecPath := ax.Join(s.projectDir, "api.json")
phpCommand, err := resolvePHPCli()
if err != nil {
return "", coreerr.E("sdk.detectScramble", "php CLI not found", err)
}
if err := ax.ExecDir(context.Background(), s.projectDir, phpCommand, "artisan", "scramble:export", "--path=api.json"); err != nil {
return "", coreerr.E("sdk.detectScramble", "scramble export failed", err)
}
if !ax.IsFile(scrambleSpecPath) {
return "", coreerr.E("sdk.detectScramble", "scramble export did not create api.json", nil)
}
return scrambleSpecPath, nil
}
// containsScramble checks if composer.json includes scramble.
func containsScramble(content string) bool {
return core.Contains(content, "dedoc/scramble") ||
core.Contains(content, "\"scramble\"")
}
func resolvePHPCli() (string, error) {
return ax.ResolveCommand("php",
"/usr/bin/php",
"/usr/local/bin/php",
"/opt/homebrew/bin/php",
)
}