60c5cc40b2
Implements a new `relspec assets` command (list/execute subcommands) that
loads local binary and text asset files into PostgreSQL by binding file bytes
as native pgx query parameters — never as SQL text literals — so binary data
stays byte-exact with no escaping overhead.
Key design points:
- YAML manifest (assets.yaml) colocated with files describes each entry:
file path, SQL call with :bytes/:filename/:param named placeholders, and
optional static params map.
- Placeholder substitution converts :name to positional $N params; PostgreSQL
::cast syntax is protected before substitution to avoid false matches.
- Directory scan follows the existing {priority}_{sequence}_{name} naming
convention, enabling asset-loading steps to be correctly interleaved with
relspec scripts execute in a migrate-apply pipeline.
- Symlink components and path traversal (../) are silently skipped to prevent
directory escape attacks.
- 14 unit tests cover manifest loading, directory scanning, ordering, symlink
skipping, path traversal rejection, placeholder substitution edge cases
(repeated, cast protection, binary byte-exact, unknown).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
75 lines
1.9 KiB
Go
75 lines
1.9 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"runtime/debug"
|
|
"time"
|
|
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var (
|
|
// Version information, set via ldflags during build
|
|
version = "dev"
|
|
buildDate = "unknown"
|
|
prisma7 bool
|
|
)
|
|
|
|
func init() {
|
|
// If version wasn't set via ldflags, try to get it from build info
|
|
if version == "dev" {
|
|
if info, ok := debug.ReadBuildInfo(); ok {
|
|
// Try to get version from VCS
|
|
var vcsRevision, vcsTime string
|
|
for _, setting := range info.Settings {
|
|
switch setting.Key {
|
|
case "vcs.revision":
|
|
if len(setting.Value) >= 7 {
|
|
vcsRevision = setting.Value[:7]
|
|
}
|
|
case "vcs.time":
|
|
vcsTime = setting.Value
|
|
}
|
|
}
|
|
|
|
if vcsRevision != "" {
|
|
version = vcsRevision
|
|
}
|
|
|
|
if vcsTime != "" {
|
|
if t, err := time.Parse(time.RFC3339, vcsTime); err == nil {
|
|
buildDate = t.UTC().Format("2006-01-02 15:04:05 UTC")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var rootCmd = &cobra.Command{
|
|
Use: "relspec",
|
|
Short: "RelSpec - Database schema conversion and analysis tool",
|
|
Long: `RelSpec is a database relations specification tool that provides
|
|
bidirectional conversion between various database schema formats.
|
|
|
|
It reads database schemas from multiple sources (live databases, DBML,
|
|
DCTX, DrawDB, etc.) and writes them to various formats (GORM, Bun,
|
|
JSON, YAML, SQL, etc.).`,
|
|
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
|
fmt.Printf("RelSpec %s (built: %s)\n\n", version, buildDate)
|
|
},
|
|
}
|
|
|
|
func init() {
|
|
rootCmd.AddCommand(convertCmd)
|
|
rootCmd.AddCommand(diffCmd)
|
|
rootCmd.AddCommand(inspectCmd)
|
|
rootCmd.AddCommand(scriptsCmd)
|
|
rootCmd.AddCommand(assetsCmd)
|
|
rootCmd.AddCommand(templCmd)
|
|
rootCmd.AddCommand(editCmd)
|
|
rootCmd.AddCommand(mergeCmd)
|
|
rootCmd.AddCommand(splitCmd)
|
|
rootCmd.AddCommand(versionCmd)
|
|
rootCmd.PersistentFlags().BoolVar(&prisma7, "prisma7", false, "Use Prisma 7 generator conventions when reading/writing Prisma schemas")
|
|
}
|