feat: unify generated configuration loading and extensible decoders
This commit is contained in:
@@ -14,7 +14,7 @@ v0.14 新增 `api format [--check]`、`api upgrade [--dry-run|--preview <空目
|
||||
|
||||
```sh
|
||||
# 仓库读取权限与 PATH 配置见根目录 README
|
||||
go install git.yuntuo.hk/k2safe/go-omni/cmd/go-omni@v0.19.0-preview
|
||||
go install git.yuntuo.hk/k2safe/go-omni/cmd/go-omni@v0.20.0-preview
|
||||
go-omni new --module example.com/hello ./hello
|
||||
|
||||
# 从自己的契约生成;service.api 及其相对 import 文件需要在本地存在
|
||||
@@ -49,14 +49,14 @@ main、Gen.go、OpenAPI 和 manifest 由生成器维护;业务文件按字节
|
||||
|
||||
## 版本与迁移
|
||||
|
||||
CLI 与框架依赖固定使用 v0.19.0-preview。远程安装的 CLI 默认把自身版本写入 go.mod,不添加本地 replace;`--framework-version` 可显式指定已发布的兼容版本。仅在开发框架源码时使用本地替换:
|
||||
CLI 与框架依赖固定使用 v0.20.0-preview。远程安装的 CLI 默认把自身版本写入 go.mod,不添加本地 replace;`--framework-version` 可显式指定已发布的兼容版本。仅在开发框架源码时使用本地替换:
|
||||
|
||||
```sh
|
||||
# 在 go-omni 源码仓库中执行
|
||||
go run ./cmd/go-omni new --module example.com/hello --framework-dir . ../hello
|
||||
```
|
||||
|
||||
当前模板与依赖以 v0.19.0-preview 为基线,继续使用 manifest v2;GORM 可选适配器从 v0.14.1-preview 提供。已有项目使用 [定点升级与预览](../../DEVELOPMENT.md#v013-项目升级):原始模板可自动替换,含业务修改的基础文件按预览合并;通用清理由 Runtime 负责。独立工具命令不会被删除。受管理项目通过 `.api` 模块导入增加业务,不使用旧 `add` 命令的手动路由装配。
|
||||
当前模板与依赖以 v0.20.0-preview 为基线,继续使用 manifest v2;GORM 可选适配器从 v0.14.1-preview 提供。已有项目使用 [定点升级与预览](../../DEVELOPMENT.md#v013-项目升级):原始模板可自动替换,含业务修改的基础文件按预览合并;通用清理由 Runtime 负责。独立工具命令不会被删除。受管理项目通过 `.api` 模块导入增加业务,不使用旧 `add` 命令的手动路由装配。
|
||||
|
||||
## 数据库迁移命令
|
||||
|
||||
@@ -69,3 +69,7 @@ go-omni migrate --dir migrations --apply
|
||||
```
|
||||
|
||||
SQL 命名示例 `0001_createUsers.up.sql`;预览只显示版本和 SHA-256,不连接数据库。失败保留 dirty 状态,不自动 down/force。细节见 [扩展模块](../../ADVANCED.md)。
|
||||
|
||||
### v0.20 配置入口
|
||||
|
||||
新生成 `internal/config/loadGen.go` 调用统一加载,纳入生成检查;项目 config.go 只维护字段、默认值和校验,loader.go 用于注册额外格式。JSON/YAML/XML 内置。旧手写 Load 会阻止生成,请先按 [迁移步骤](../../CONFIG.md#v019-及更早项目迁移) 整理,不会自动覆盖业务代码。
|
||||
|
||||
113
cmd/go-omni/apigen/configLoader_test.go
Normal file
113
cmd/go-omni/apigen/configLoader_test.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package apigen
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestGeneratedConfigurationConsumer(t *testing.T) {
|
||||
if os.Getenv("GO_OMNI_SCAFFOLD_INTEGRATION") != "1" {
|
||||
t.Skip("set GO_OMNI_SCAFFOLD_INTEGRATION=1")
|
||||
}
|
||||
dir := filepath.Join(t.TempDir(), "app")
|
||||
api := filepath.Join(t.TempDir(), "service.api")
|
||||
writeFile(t, api, "service configuration {\n@handler ping\nget /ping\n}\n")
|
||||
o := Options{API: api, Dir: dir, Module: "example.com/configuration", FrameworkDir: frameworkRoot(t)}
|
||||
if err := generateManagedTestProject(t, o); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
main := readFile(t, filepath.Join(dir, "main.go"))
|
||||
// The business only changes fields/defaults/validation, not the loading flow.
|
||||
writeFile(t, filepath.Join(dir, "internal/config/config.go"), `package config
|
||||
import("git.yuntuo.hk/k2safe/go-omni/config";"git.yuntuo.hk/k2safe/go-omni/logx")
|
||||
type Config struct { Server config.Config `+"`json:\"server\"`"+`;Limit int `+"`json:\"limit\" envDefault:\"10\"`"+` }
|
||||
func Default()Config{ c:=config.Default();c.Log.File="./var/log/app.log";c.Log.Rotation=logx.RotationConfig{Enabled:true,MaxSizeMB:10};return Config{Server:c} }
|
||||
func(c Config)Validate()error{if err:=c.Server.Validate();err!=nil{return config.AtField("server",err)};if c.Limit<0{return config.Invalid("limit","must not be negative")};return nil}
|
||||
`)
|
||||
writeFile(t, filepath.Join(dir, "internal/config/loader.go"), `package config
|
||||
import omniConfig "git.yuntuo.hk/k2safe/go-omni/config"
|
||||
func NewLoader()(*omniConfig.Loader,error){return omniConfig.NewLoader(omniConfig.WithDecoder("custom",omniConfig.JSONDecoder{}))}
|
||||
`)
|
||||
svc := filepath.Join(dir, "internal/svc/serviceContext.go")
|
||||
writeFile(t, svc, strings.Replace(readFile(t, svc), "return nil", "panic(\"dependencies must not initialize on invalid config\")", 1))
|
||||
writeFile(t, filepath.Join(dir, "consumer_test.go"), readFile(t, "testdata/configuration/consumer_test.go"))
|
||||
runConsumerGo(t, dir, "mod", "tidy")
|
||||
runConsumerGo(t, dir, "test", "-race", "-count=1", "./...")
|
||||
runConsumerGo(t, dir, "build", ".")
|
||||
o.API = filepath.Join(dir, "api/service.api")
|
||||
before := snapshot(t, dir)
|
||||
for _, mode := range []string{"dry", "generate", "check"} {
|
||||
o.DryRun = mode == "dry"
|
||||
o.Check = mode == "check"
|
||||
if err := generateManagedTestProject(t, o); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if !sameFiles(before, snapshot(t, dir)) {
|
||||
t.Fatal("business files overwritten")
|
||||
}
|
||||
if main != readFile(t, filepath.Join(dir, "main.go")) {
|
||||
t.Fatal("main changed")
|
||||
}
|
||||
load := filepath.Join(dir, "internal/config/loadGen.go")
|
||||
original := readFile(t, load)
|
||||
writeFile(t, load, original+"\n// drift\n")
|
||||
if err := generateManagedTestProject(t, o); err == nil {
|
||||
t.Fatal("loadGen drift accepted")
|
||||
}
|
||||
writeFile(t, load, original)
|
||||
if os.Getenv("GO_OMNI_RELEASE_CLI") != "" {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
cmd := exec.CommandContext(ctx, "go", "list", "-m", "-json", frameworkModule)
|
||||
cmd.Dir = dir
|
||||
cmd.Env = append(os.Environ(), "GOWORK=off")
|
||||
data, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatal(err, string(data))
|
||||
}
|
||||
var dependency struct {
|
||||
Version, Sum string
|
||||
Replace any
|
||||
}
|
||||
if err = json.Unmarshal(data, &dependency); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if dependency.Version != os.Getenv("GO_OMNI_RELEASE_VERSION") || dependency.Replace != nil || dependency.Sum == "" {
|
||||
t.Fatal("not a remote consumer")
|
||||
}
|
||||
t.Logf("remote framework %s checksum %s; no local replace", dependency.Version, dependency.Sum)
|
||||
}
|
||||
}
|
||||
func TestConfigLoadMigrationGuard(t *testing.T) {
|
||||
dir := filepath.Join(t.TempDir(), "app")
|
||||
o := Options{Dir: dir, Module: "example.com/upgrade", FrameworkDir: frameworkRoot(t)}
|
||||
if _, err := New(o); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
o.API = filepath.Join(dir, "api/service.api")
|
||||
matches, err := filepath.Glob(filepath.Join(dir, "api/*.api"))
|
||||
if err != nil || len(matches) != 1 {
|
||||
t.Fatal(matches, err)
|
||||
}
|
||||
o.API = matches[0]
|
||||
writeFile(t, filepath.Join(dir, "internal/config/legacy.go"), "package config\nfunc Load(path string)(Config,error){return Default(),nil}\n")
|
||||
before := snapshot(t, dir)
|
||||
for _, mode := range []string{"dry", "generate", "check"} {
|
||||
o.DryRun = mode == "dry"
|
||||
o.Check = mode == "check"
|
||||
_, err := Generate(o)
|
||||
if err == nil || !strings.Contains(err.Error(), "CONFIG.md") {
|
||||
t.Fatal("missing migration diagnostic", err)
|
||||
}
|
||||
if !sameFiles(before, snapshot(t, dir)) {
|
||||
t.Fatal("migration guard changed files")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
)
|
||||
|
||||
const frameworkModule = "git.yuntuo.hk/k2safe/go-omni"
|
||||
const minimumVersion = "v0.19.0-preview"
|
||||
const minimumVersion = "v0.20.0-preview"
|
||||
const generatedHeader = "// Code generated by go-omni api. DO NOT EDIT.\n"
|
||||
|
||||
type Options struct {
|
||||
@@ -106,6 +106,9 @@ func generate(o Options, c *Compiled, starter bool) (Output, error) {
|
||||
if err = checkRuntimeExtension(directory); err != nil {
|
||||
return Output{}, err
|
||||
}
|
||||
if err = checkConfigLoadExtension(directory); err != nil {
|
||||
return Output{}, err
|
||||
}
|
||||
for _, r := range c.Spec.Routes {
|
||||
if !r.Manual {
|
||||
path := filepath.Join(directory, "internal/handler", r.Group, lower(r.Handler)+"Manual.go")
|
||||
@@ -152,7 +155,7 @@ func generate(o Options, c *Compiled, starter bool) (Output, error) {
|
||||
if !filepath.IsAbs(path) {
|
||||
path = filepath.Join(directory, path)
|
||||
}
|
||||
if source, err := os.ReadFile(filepath.Join(path, "config", "env.go")); err == nil && strings.Contains(string(source), "func ServerEnvAliases(") {
|
||||
if source, err := os.ReadFile(filepath.Join(path, "config", "loader.go")); err == nil && strings.Contains(string(source), "func NewLoader(") {
|
||||
localCurrent = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,8 @@ Unimplemented/nil factories fail startup and clean registered dependencies.
|
||||
Existing projects retain middleware: handlers until explicitly migrated.
|
||||
Ordinary Logic uses the generated Request/Response types directly. Where an
|
||||
independent business type is needed, use ordinary Go or the optional copyx package.
|
||||
Configuration loading is generator-managed in internal/config/loadGen.go. Extend fields/defaults/Validate in config.go; additional decoders belong in loader.go/NewLoader. JSON/YAML/XML use one pipeline. See the framework CONFIG.md.
|
||||
|
||||
There are no mappings/mappingFiles settings or generated mapping files in v0.19.
|
||||
See COPY.md in the framework repository for copy policies and migration steps,
|
||||
and GENERATION.md for field composition and middleware factories.
|
||||
|
||||
59
cmd/go-omni/apigen/testdata/configuration/consumer_test.go
vendored
Normal file
59
cmd/go-omni/apigen/testdata/configuration/consumer_test.go
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"example.com/configuration/internal/config"
|
||||
omniConfig "git.yuntuo.hk/k2safe/go-omni/config"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGeneratedConfiguration(t *testing.T) {
|
||||
for ext, source := range map[string]string{
|
||||
"json": `{"limit":7}`, "yaml": "limit: 7\n", "xml": "<config><limit>7</limit></config>", "custom": `{"limit":7}`,
|
||||
} {
|
||||
t.Run(ext, func(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "app."+ext)
|
||||
if err := os.WriteFile(path, []byte(source), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cfg, err := config.Load(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if cfg.Limit != 7 {
|
||||
t.Fatal(cfg.Limit)
|
||||
}
|
||||
t.Setenv("GO_OMNI_LIMIT", "0")
|
||||
cfg, err = config.Load(path)
|
||||
if err != nil || cfg.Limit != 0 {
|
||||
t.Fatal(cfg.Limit, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
t.Setenv("GO_OMNI_LOG_OUTPUT", "stderr")
|
||||
cfg, err := config.Load("")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if cfg.Limit != 10 || cfg.Server.Log.File != "" || cfg.Server.Log.Rotation.Enabled {
|
||||
t.Fatal("defaults/log normalization", cfg, err)
|
||||
}
|
||||
path := filepath.Join(t.TempDir(), "bad.yaml")
|
||||
os.WriteFile(path, []byte("limit: -1\n"), 0600)
|
||||
// Validation must run before svc.Initialize; the project initializer panics
|
||||
// below if reached, so this also detects accidentally starting dependencies.
|
||||
err = run(context.Background(), path)
|
||||
var diagnostic *omniConfig.Error
|
||||
if !errors.As(err, &diagnostic) || diagnostic.Field != "limit" || diagnostic.File != path || diagnostic.Line != 1 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Setenv("GO_OMNI_LIMIT", "private-not-a-number")
|
||||
_, err = config.Load(path)
|
||||
if !errors.As(err, &diagnostic) || diagnostic.Env != "GO_OMNI_LIMIT" || strings.Contains(err.Error(), "private-not-a-number") {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,39 @@ type UpgradePlan struct {
|
||||
Module, Entry string
|
||||
}
|
||||
|
||||
// Handwritten old loaders must be migrated explicitly, never silently replaced
|
||||
// or left competing with the generated entry point.
|
||||
func checkConfigLoadExtension(root string) error {
|
||||
dir := filepath.Join(root, "internal/config")
|
||||
entries, err := os.ReadDir(dir)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, entry := range entries {
|
||||
name := entry.Name()
|
||||
if entry.IsDir() || !strings.HasSuffix(name, ".go") || strings.HasSuffix(name, "_test.go") || name == "loadGen.go" {
|
||||
continue
|
||||
}
|
||||
rel := "internal/config/" + name
|
||||
if err := safePath(root, rel); err != nil {
|
||||
return err
|
||||
}
|
||||
f, err := goparser.ParseFile(gotoken.NewFileSet(), filepath.Join(root, rel), nil, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parse %s: %w", rel, err)
|
||||
}
|
||||
for _, decl := range f.Decls {
|
||||
if fn, ok := decl.(*ast.FuncDecl); ok && fn.Recv == nil && fn.Name.Name == "Load" {
|
||||
return fmt.Errorf("%s: migrate the project-owned Load function before generating internal/config/loadGen.go; retain Config/Default/Validate and move decoder options to NewLoader; see CONFIG.md", rel)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PlanUpgrade compares infrastructure against the exact previous release seeds.
|
||||
// A custom file is never classified as safe to replace. Logic, middleware,
|
||||
// options and repositories outside the changed templates are left untouched.
|
||||
|
||||
@@ -284,6 +284,9 @@ func apply(root, module, entry string, generated, seeds map[string][]byte, check
|
||||
}
|
||||
func digest(data []byte) string { sum := sha256.Sum256(data); return hex.EncodeToString(sum[:]) }
|
||||
func ownedPath(name string) bool {
|
||||
if name == "internal/config/loadGen.go" {
|
||||
return true
|
||||
}
|
||||
if strings.HasPrefix(name, "internal/types/dto") && strings.HasSuffix(name, "Gen.go") && goName.MatchString(strings.TrimSuffix(filepath.Base(name), ".go")) {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
)
|
||||
|
||||
func TestNewWithRemoteFrameworkVersion(t *testing.T) {
|
||||
for _, flag := range [][]string{{"--framework-version", "v0.19.0-preview"}, {"--framework-version=v0.19.0-preview"}} {
|
||||
for _, flag := range [][]string{{"--framework-version", "v0.20.0-preview"}, {"--framework-version=v0.20.0-preview"}} {
|
||||
project := filepath.Join(t.TempDir(), "billing")
|
||||
args := append([]string{"--module", "example.com/billing"}, flag...)
|
||||
if err := runNew(append(args, project)); err != nil {
|
||||
@@ -18,7 +18,7 @@ func TestNewWithRemoteFrameworkVersion(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(string(content), "go-omni v0.19.0-preview") || strings.Contains(string(content), "replace ") {
|
||||
if !strings.Contains(string(content), "go-omni v0.20.0-preview") || strings.Contains(string(content), "replace ") {
|
||||
t.Fatalf("CLI did not pin remote dependency: %s", content)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ func apiInfrastructure(module, database, storage, logOutput, logFormat string, l
|
||||
"internal/svc/runtimeGen.go": "templates/api-runtime.go.tmpl",
|
||||
"internal/svc/options.go": "templates/api-options.go.tmpl",
|
||||
"internal/config/config.go": "templates/api-config.go.tmpl",
|
||||
"internal/config/loadGen.go": "templates/api-load.go.tmpl",
|
||||
"internal/config/loader.go": "templates/api-loader.go.tmpl",
|
||||
"etc/config.yaml": "templates/modules-config.yaml.tmpl",
|
||||
"internal/svc/serviceContext.go": "templates/api-context.go.tmpl",
|
||||
}
|
||||
@@ -31,6 +33,8 @@ func apiInfrastructure(module, database, storage, logOutput, logFormat string, l
|
||||
files["internal/svc/storage.go"] = "templates/modules-storage.go.tmpl"
|
||||
}
|
||||
if legacy {
|
||||
delete(files, "internal/config/loadGen.go")
|
||||
delete(files, "internal/config/loader.go")
|
||||
delete(files, "internal/svc/runtimeGen.go")
|
||||
delete(files, "internal/svc/databaseOptions.go")
|
||||
files["internal/svc/close.go"] = "templates/api-close.go.tmpl"
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
package settings
|
||||
import (
|
||||
"os"
|
||||
{{if and (not .API) (ne .Storage "none")}}"strings"{{end}}
|
||||
"time"
|
||||
"git.yuntuo.hk/k2safe/go-omni/config"
|
||||
"git.yuntuo.hk/k2safe/go-omni/logx"
|
||||
{{if eq .LogOutput "file"}}"git.yuntuo.hk/k2safe/go-omni/logx"{{end}}
|
||||
{{if eq .Database "mysql"}}mysqladapter "git.yuntuo.hk/k2safe/go-omni/database/mysql"{{end}}
|
||||
{{if ne .Storage "none"}}"errors"
|
||||
{{if ne .Storage "r2"}}"net/url"{{else}}"regexp"{{end}}
|
||||
@@ -35,22 +34,10 @@ func Default() Config {
|
||||
}{{end}}}
|
||||
}
|
||||
|
||||
// Load applies defaults -> file -> typed environment -> validation.
|
||||
// New fields automatically use GO_OMNI_<JSON_PATH>, or an explicit env tag.
|
||||
func Load(path string)(Config,error){
|
||||
cfg:=Default();var err error
|
||||
cfg,err=config.ApplyDefaults(cfg);if err!=nil{return Config{},err}
|
||||
if path!="" {cfg,err=config.DecodeFile(path,cfg);if err!=nil{return Config{},err}}
|
||||
cfg,err=config.OverlayEnv(cfg,config.EnvOptions{Prefix:"GO_OMNI_",Empty:"ignore",Aliases:config.ServerEnvAliases("GO_OMNI_","server")});if err!=nil{return Config{},err}
|
||||
output,outputSet:=os.LookupEnv("GO_OMNI_SERVER_LOG_OUTPUT");if !outputSet {output=os.Getenv("GO_OMNI_LOG_OUTPUT")}
|
||||
file,fileSet:=os.LookupEnv("GO_OMNI_SERVER_LOG_FILE");if !fileSet {file=os.Getenv("GO_OMNI_LOG_FILE")}
|
||||
if output!="" && file=="" {cfg.Server.Log.File="";cfg.Server.Log.Rotation=logx.RotationConfig{}}
|
||||
return cfg,cfg.Validate()
|
||||
}
|
||||
func(c Config)Validate()error{
|
||||
if err:=c.Server.Validate();err!=nil{return err}
|
||||
{{if eq .Database "mysql"}}if err:=c.Database.Validate();err!=nil{return err}{{end}}
|
||||
{{if ne .Storage "none"}}if err:=c.Storage.Validate();err!=nil{return err}{{end}}
|
||||
if err:=c.Server.Validate();err!=nil{return config.AtField("server",err)}
|
||||
{{if eq .Database "mysql"}}if err:=c.Database.Validate();err!=nil{return config.AtField("database",err)}{{end}}
|
||||
{{if ne .Storage "none"}}if err:=c.Storage.Validate();err!=nil{return config.AtField("storage",err)}{{end}}
|
||||
return nil
|
||||
}
|
||||
{{if ne .Storage "none"}}
|
||||
|
||||
13
cmd/go-omni/scaffold/templates/api-load.go.tmpl
Normal file
13
cmd/go-omni/scaffold/templates/api-load.go.tmpl
Normal file
@@ -0,0 +1,13 @@
|
||||
package config
|
||||
|
||||
import omniConfig "git.yuntuo.hk/k2safe/go-omni/config"
|
||||
|
||||
// Load is maintained by the generator. Extend Config/Default/Validate in
|
||||
// config.go, or NewLoader in loader.go; do not duplicate the loading pipeline.
|
||||
func Load(path string) (Config, error) {
|
||||
loader,err:=NewLoader()
|
||||
if err!=nil{return Config{},err}
|
||||
return omniConfig.LoadProject(path,Default(),omniConfig.EnvOptions{
|
||||
Prefix:"GO_OMNI_",Empty:"ignore",Aliases:omniConfig.ServerEnvAliases("GO_OMNI_","server"),
|
||||
},loader)
|
||||
}
|
||||
7
cmd/go-omni/scaffold/templates/api-loader.go.tmpl
Normal file
7
cmd/go-omni/scaffold/templates/api-loader.go.tmpl
Normal file
@@ -0,0 +1,7 @@
|
||||
package config
|
||||
|
||||
import omniConfig "git.yuntuo.hk/k2safe/go-omni/config"
|
||||
|
||||
// NewLoader is project-owned. JSON/YAML/XML work without changes. Register
|
||||
// additional format decoders here only when the project needs them.
|
||||
func NewLoader() (*omniConfig.Loader,error) { return omniConfig.NewLoader() }
|
||||
@@ -10,7 +10,7 @@ const frameworkModule = "git.yuntuo.hk/k2safe/go-omni"
|
||||
// Source builds can carry an unpublished VCS version (including +dirty).
|
||||
// Keep this fallback in sync with the next release; installed CLIs use their
|
||||
// downloaded module version, which also has a module checksum.
|
||||
const defaultFrameworkVersion = "v0.19.0-preview"
|
||||
const defaultFrameworkVersion = "v0.20.0-preview"
|
||||
|
||||
// This target is not a claim that the release already exists. Source users
|
||||
// select a local framework checkout until the module templates are published.
|
||||
|
||||
Reference in New Issue
Block a user