Þļüĝîñ ḿöđéļ: ţĥé îñ-þŕöçéšš ŕéĝîšţŕý çöñţŕàçţ
Ţĥîš îḿþļéḿéñţàţîöñ ñöţé çöṽéŕš ţĥé îñ-þŕöçéšš ŕéĝîšţŕý ḿéçĥàñîšḿ à þļüĝîñ ƃîñàŕý üšéš ţö ŵîŕé îţš ƒéàţüŕéš îñţö ţĥé šĥàŕéđ cli.App (àñ àļîàš öƒ ţĥé ĥöšţ ḿöđüļé'š host.App, ţĥé çöƃŕà-ƒŕéé àþþļîçàţîöñ ŕüñţîḿé ţĥé cli šĥéļļ ŵŕàþš). Þļüĝîñ þàçķàĝéš àŕé ƃļàñķ-îḿþöŕţéđ ƃý à þļüĝîñ ƃîñàŕý'š main; ţĥéîŕ init() ƒüñçţîöñš ŕéĝîšţéŕ ƒéàţüŕéš àĝàîñšţ þŕöçéšš-ĝļöƃàļ ŕéĝîšţŕîéš ṽîà đîŕéçţ ƒüñçţîöñ çàļļš: ñö ĝŔÞÇ, ñö đýñàḿîç ļöàđîñĝ îñšîđé ţĥé ƃîñàŕý.
Ţĥîš îš öñé ĥàļƒ öƒ ţĥé þļüĝîñ šţöŕý: ĥöŵ ţĥé Ĝö çöđé îñšîđé à þļüĝîñ ƃîñàŕý îš çöḿþöšéđ. Ĥöŵ kapi ţĥéñ đîšçöṽéŕš ţĥàţ ƃîñàŕý öñ đîšķ àñđ đîšþàţçĥéš ţö îţ àţ ŕüñţîḿé (ţĥé manifest.json ḿöđéļ àñđ ţĥé À/Ƃ/Ç ţŕàñšþöŕţ ḿöđéš) ļîṽéš îñ É-05: Þļüĝîñ Šýšţéḿ. Ţĥé kapi ƃîñàŕý îţšéļƒ ļîñķš ñö ṽéñđöŕ þļüĝîñš; ţĥé ŕéĝîšţŕîéš ƃéļöŵ þöþüļàţé îñšîđé ţĥé þļüĝîñ ƃîñàŕý; îñ ţĥé ŵöŕķéđ éẋàḿþļé ƃéļöŵ ţĥàţ îš kapi-gitlab (ƃüîļţ ƒŕöḿ gitlab-plugin/cmd/kapi-gitlab/).
Ţĥîš ñöţé îš ţĥé ŕéƒéŕéñçé ƒöŕ: ĥöŵ ţĥé ŕéĝîšţŕîéš ŵöŕķ, ĥöŵ ţö ŵŕîţé ţĥé Ĝö šîđé öƒ à ñéŵ þļüĝîñ, àñđ ŵĥéñ ţö üšé ţĥé šçĥéḿà-öñļý ŕéĝîšţŕý ṽš. ţĥé ĥéàṽîéŕ öñéš.
Ŵĥéñ ţö üšé ŵĥîçĥ ŕéĝîšţŕý
Ţĥé ƒŕàḿéŵöŕķ, ţĥé ĥöšţ ḿöđüļé, àñđ ţĥé šĥàŕéđ ÇĻÎ ḿöđüļé éẋþöšé ƒöüŕ ŕéĝîšţŕîéš. À þļüĝîñ çàñ üšé àñý šüƃšéţ. (cli.RegisterMCPToolFactory îš à ŕé-éẋþöŕţ öƒ host.RegisterMCPToolFactory. ḾÇÞ ţööļš àŕé çöƃŕà-ƒŕéé àñđ ļîṽé îñ ţĥé ĥöšţ ḿöđüļé; ţĥé ţŵö çöḿḿàñđ-ƒàçîñĝ ŕéĝîšţŕîéš àŕé çļî-ñàţîṽé.)
| Ŕéĝîšţŕý | Ļîṽéš îñ | Þļüĝîñ éẋţéñđš ŵîţĥ |
|---|---|---|
core/project.RegisterExtension | ƒŕàḿéŵöŕķ | Ŕéçîþé šçĥéḿà (ţýþéđ ÝÀḾĻ đéçöđéŕš ƒöŕ ķéýš üñđéŕ Extras) |
cli.RegisterCommandFactory | šĥàŕéđ ÇĻÎ ḿöđüļé | Ţöþ-ļéṽéļ çöƃŕà šüƃçöḿḿàñđš (push, pull, …) |
cli.RegisterAppInitializer | šĥàŕéđ ÇĻÎ ḿöđüļé | Ḿüţàţéš *cli.App àƒţéŕ çöñšţŕüçţîöñ (šéţš ƒîéļđš, ŵîŕéš ĥööķš) |
cli.RegisterMCPToolFactory | šĥàŕéđ ÇĻÎ ḿöđüļé | ḾÇÞ ţööļš šéŕṽéđ ƃý ţĥé šĥàŕéđ mcp šüƃçöḿḿàñđ |
À þļüĝîñ ţĥàţ öñļý đéçļàŕéš šçĥéḿà çàñ ƃé à ţîñý ḿöđüļé ŵîţĥ ĵüšţ öñé đéçöđéŕ ƒîļé àñđ ñö ÇĻÎ đéþš; ţĥàţ îš ĥöŵ à šçĥéḿà-öñļý þļüĝîñ ḿöđüļé (ĵüšţ à schema/ þàçķàĝé) îš šţŕüçţüŕéđ. À þļüĝîñ ţĥàţ àđđš ƒüļļ ÜẊ (çöḿḿàñđš, ḾÇÞ ţööļš, šöüŕçé çöññéçţöŕ) ļàýéŕš ḿöŕé öñ ţöþ, ƃüţ ţĥé šçĥéḿà þàŕţ çàñ šĥîþ îñđéþéñđéñţļý.
Ţĥé šçĥéḿà ŕéĝîšţŕý
type ExtensionDecoder interface {
Decode(node yaml.Node) error
}
type Scope int // ScopeProject | ScopeDefaults | ScopeCollection | ScopeItem
type Extension struct {
Name string
Scope Scope
Group string
Decoder ExtensionDecoder
DependsOn string
Venue bool
}
func RegisterExtension(ext Extension)
func RegisterExtensionGroup(group string, exts []Extension)
func HasExtensionGroup(group string) bool
Scope đéçîđéš ŵĥîçĥ Extras ḿàþ à ķéý îš ḿàţçĥéđ àĝàîñšţ:
ScopeProject: ţöþ-ļéṽéļ ķéýš öñ ţĥé ŕéçîþé (à þļüĝîñ'š öŵñ ķéý, šüçĥ àšgitlab:)ScopeDefaults: ķéýš ñéšţéđ üñđéŕdefaults:ScopeCollection: ķéýš öñ àCollection(ñàḿéđ-çöļļéçţîöñ ŵŕàþþéŕ öŕ ƃàŕé éñţŕý)ScopeItem: ķéýš öñ àContentItem(þéŕ-îţéḿ ƒîéļđš)
Ŕé-ŕéĝîšţéŕîñĝ ţĥé šàḿé (Scope, Name) þàîŕ þàñîçš: çöḿþéţîñĝ îñîţ ƒüñçţîöñš àŕé àļḿöšţ àļŵàýš à ƃüĝ. Þüŕé-ñàḿé ḿàţçĥéš àçŕöšš šçöþéš đöñ'ţ çöñƒļîçţ (collection àţ ScopeItem àñđ ScopeDefaults àŕé đîšţîñçţ).
Group ļéţš à ŕéçîþé đéçļàŕé requires: { gitlab: "*" } àñđ ĥàṽé ṽàļîđàţîöñ ƒàîļ ŵĥéñ ñö éẋţéñšîöñ üñđéŕ ţĥàţ ĝŕöüþ ĥàš ƃééñ ŕéĝîšţéŕéđ. Üšé ţĥîš ŵĥéñ à ŕéçîþé îš ḿéàñîñĝļéšš ŵîţĥöüţ ţĥàţ þļüĝîñ'š ƃéĥàṽîöŕ: gitlab-push ŵöñ'ţ ŵöŕķ ŵîţĥöüţ ţĥé ĝîţļàƃ þļüĝîñ îñšţàļļéđ, šö à ŕéçîþé üšîñĝ ţĥé gitlab: ƃļöçķ ţýþîçàļļý đéçļàŕéš requires: { gitlab: "*" }. Ţĥé ḿàþ ƒöŕḿ îš ḿàñđàţöŕý: éàçĥ éñţŕý ḿàþš à þļüĝîñ ñàḿé ţö à šéḿṽéŕ çöñšţŕàîñţ, ŵîţĥ "*" ḿéàñîñĝ àñý ṽéŕšîöñ. Ţĥé ƃàŕé-ļîšţ ƒöŕḿ (requires: [gitlab]) îš ŕéĵéçţéđ ŵîţĥ àñ àçţîöñàƃļé éŕŕöŕ.
HasExtensionGroup îš çöñšüļţéđ ƃý KapiProject.Validate() ţö éñƒöŕçé requires:. Þļüĝîñš ţýþîçàļļý đöñ'ţ ñééđ ţö çàļļ îţ đîŕéçţļý.
DependsOn ñàḿéš à šîƃļîñĝ ķéý àţ ţĥé šàḿé šçöþé ţĥàţ ḿüšţ ƃé þŕéšéñţ ƒöŕ ţĥîš öñé ţö ĥàṽé àñý 郃éçţ: à þļüĝîñ'š đéþéñđéñţ ķéýš đéþéñđ öñ îţš öŵñ ţöþ-ļéṽéļ ķéý. À ŕéçîþé ţĥàţ šéţš ţĥé đéþéñđéñţ ķéý ŵîţĥöüţ îţš đéþéñđéñçý þàŕšéš ƒîñé; ţĥé ĥöšţ ŕéþöŕţš ţĥé ƒîéļđ àš îñéŕţ ŕàţĥéŕ ţĥàñ ƒàîļîñĝ ţĥé ļöàđ.
Venue: true ḿàŕķš ţĥé ķéý àš ţĥé ŕéçîþé'š ƃîñđîñĝ ţö à ŕéḿöţé çöñṽéŕĝéñçé ṽéñüé. Ţĥé ƒŕàḿéŵöŕķ ŕéàđš url: àñđ converge: öüţ öƒ îţ ţĥŕöüĝĥ KapiProject.Venue() àñđ àšķš ţĥé ŕéĝîšţŕý ŵĥîçĥ éẋţéñšîöñ ĥöļđš ţĥé ƒļàĝ ŕàţĥéŕ ţĥàñ ļööķîñĝ ƒöŕ à ķéý ƃý ñàḿé: kapi ļîñķš ñö þļàţƒöŕḿ àñđ ĥöļđš ñö öþîñîöñ àƃöüţ à ķéý îţ çàññöţ ñàḿé. VenueKey() àñšŵéŕš ţĥé šàḿé ǫüéšţîöñ ŵîţĥ ñö ŕéçîþé îñ ĥàñđ, šö à ḿéššàĝé àƃöüţ àñ àƃšéñţ ƃļöçķ çàñ šţîļļ ñàḿé ţĥé ƃļöçķ ţö àđđ. Àţ ḿöšţ öñé ķéý àçŕöšš àļļ îñšţàļļéđ þļüĝîñš šĥöüļđ çļàîḿ îţ.
Ƒöŕŵàŕđ çöḿþàţîƃîļîţý
À ŕéçîþé ŵîţĥ àñ üñķñöŵñ éẋţéñšîöñ ķéý (ñö đéçöđéŕ ŕéĝîšţéŕéđ ƒöŕ (scope, key)) ļöàđš šüççéššƒüļļý; ţĥé ṽàļüé šîţš îñ Extras àñđ ŕöüñđ-ţŕîþš ţĥŕöüĝĥ Save. Ţĥîš îš îñţéñţîöñàļ: à kapi ƃîñàŕý ƃüîļţ ŵîţĥöüţ à ĝîṽéñ þļüĝîñ çàñ šţîļļ ļöàđ à ŕéçîþé ţĥàţ üšéš îţ (îţ ĵüšţ çàñ'ţ ṽàļîđàţé öŕ àçţ öñ îţ). Ţĥé requires: đéçļàŕàţîöñ îš ţĥé ŕéçîþé àüţĥöŕ'š öþţ-îñ ƒöŕ "ƒàîļ ļöüđļý îƒ ţĥé éẋţéñšîöñ îš ḿîššîñĝ."
Ţĥé ÇĻÎ ŕéĝîšţŕîéš
type CommandFactory func(parent *cobra.Command, app *App)
type AppInitializer func(app *App)
type MCPToolFactory func(server *mcp.Server, app *App)
func RegisterCommandFactory(f CommandFactory)
func RegisterAppInitializer(f AppInitializer)
func RegisterMCPToolFactory(f MCPToolFactory)
CommandFactory îš îñṽöķéđ ƃý ţĥé þļüĝîñ ƃîñàŕý öñçé àƒţéŕ ţĥé ƃüîļţ-îñ çöḿḿàñđ ţŕéé îš çöñšţŕüçţéđ. Ƒöŕ ţĥé ŵöŕķéđ éẋàḿþļé ţĥàţ ĥàþþéñš îñšîđé kapi-gitlab, ŵĥîçĥ ƃüîļđš îţš command šüƃţŕéé ƒŕöḿ ţĥé ŕéĝîšţéŕéđ ƒàçţöŕîéš:
// gitlab-plugin/cmd/kapi-gitlab/main.go
cli.ApplyCommandFactories(cmd, app)
Þļüĝîñš ţýþîçàļļý ŕéĝîšţéŕ öñé ƒàçţöŕý þéŕ çöḿḿàñđ ƒîļé, é.ĝ. gitlab-plugin/commands/push.go:
func init() {
cli.RegisterCommandFactory(func(parent *cobra.Command, a *cli.App) {
parent.AddCommand(buildPushCmd(a))
})
}
AppInitializer îš îñṽöķéđ ƒŕöḿ ţĥé ĥöšţ'š PersistentPreRun àƒţéŕ app.Init(). Þļüĝîñš üšé îţ ţö îñšţàļļ ƒîéļđš ļîķé app.FallbackRunE (þŕöĵéçţ ƒļöŵ ŕéšöļüţîöñ) ţĥàţ ñééđ ţö šéé ţĥé ƒüļļý-îñîţîàļîžéđ App.
MCPToolFactory îš îñṽöķéđ ƃý ţĥé šĥàŕéđ app.NewMCPCmd("kapi") ŵĥéñ ţĥé mcp šüƃçöḿḿàñđ šţàŕţš. Éàçĥ ŕéĝîšţéŕéđ ƒàçţöŕý îš ĝîṽéñ ţĥé *mcp.Server àñđ ţĥé *cli.App àñđ àđđš îţš ţööļš.
Öñé àĝéñţ-ƒàçîñĝ šüŕƒàçé
Ţĥé ŕéĝîšţŕîéš àƃöṽé ŕüñ îñšîđé öñé ƃîñàŕý. À þļüĝîñ šĥîþš àš îţš öŵñ ƃîñàŕý, šö îţš ḾÇÞ ţööļš ŵöüļđ šîţ öñ ţĥéîŕ öŵñ šéŕṽéŕ, ŕéàçĥàƃļé öñļý ƃý à çļîéñţ çöñƒîĝüŕéđ ţö ļàüñçĥ îţ. kapi mcp çļöšéš ţĥàţ ĝàþ: îţ šþàŵñš éàçĥ îñšţàļļéđ þļüĝîñ'š mcp-server šüƃçöḿḿàñđ öñçé þéŕ šéššîöñ àñđ ƒöŕŵàŕđš tools/call ţö îţ, šö àñ àĝéñţ çöññéçţéđ ţö kapi ŕéàçĥéš à þļüĝîñ'š ţööļš ţĥŕöüĝĥ ţĥé šàḿé šüŕƒàçé àš kapi'š öŵñ. Ŵĥàţ à šüŕƒàçé öƒƒéŕš àñ àĝéñţ îš ŵĥàţ îţ öƒƒéŕš à ĥüḿàñ, àñđ kapi push ĥàš à ĥüḿàñ ƒöŕḿ àļŕéàđý.
host/mcp_plugin_proxy.go ĥöļđš îţ, ŕéĝîšţéŕéđ ţĥŕöüĝĥ RegisterLateMCPToolFactory šö îţ ŕüñš àƒţéŕ éṽéŕý öŕđîñàŕý ƒàçţöŕý àñđ çàñ ŕéàđ ƃàçķ ţĥé ƒîñîšĥéđ çöŕé šüŕƒàçé. Ƒöüŕ ŕüļéš ḿàķé îţ þŕéđîçţàƃļé:
- Ţĥé ḿàñîƒéšţ îš ţĥé çöñţŕîƃüţîöñ ļîšţ. Öñļý ţĥé ţööļš à þļüĝîñ đéçļàŕéš üñđéŕ
mcp_toolsàŕé þŕöẋîéđ. À þļüĝîñ ƃîñàŕý ƃüîļţ öñ ţĥé šĥàŕéđ ÇĻÎ šéŕṽéš ţĥé ŵĥöļé çöŕé šüŕƒàçé àš ŵéļļ, šö þŕöẋýîñĝ îţš ƒüļļtools/listŵöüļđ þüƃļîšĥ à šéçöñđ çöþý öƒ éṽéŕý kapi ţööļ. Ñàḿéš çöḿé ƒŕöḿ ţĥé ḿàñîƒéšţ; ţĥé îñþüţ šçĥéḿà àñđ đéšçŕîþţîöñ çöḿé ƒŕöḿ ţĥé þļüĝîñ'š ļîṽé šéŕṽéŕ, ƃéçàüšé ţĥé ḿàñîƒéšţ çàŕŕîéš ñéîţĥéŕ. - kapi ŵîñš à ñàḿé çöļļîšîöñ. Ţĥé ŠĐĶ'š
AddToolŕéþļàçéš à ţööļ öƒ ţĥé šàḿé ñàḿé ŵîţĥöüţ šàýîñĝ šö, šö ţĥé þŕöẋý ŕéàđš ţĥé šéŕṽéŕ'š ţööļ ñàḿéš ƒîŕšţ àñđ šķîþš à þļüĝîñ ţööļ ţĥàţ ŵöüļđ ţàķé öñé öṽéŕ. Ţĥé šķîþ îš ŕéþöŕţéđ öñ šţđéŕŕ. - À þļüĝîñ ţĥàţ đöéš ñöţ àñšŵéŕ îš ļéƒţ öƒƒ. À ƃîñàŕý ţĥàţ ƒàîļš ţö šţàŕţ, öŕ ĥàš ñöţ ļîšţéđ îţš ţööļš îñšîđé ţĥé šţàŕţüþ ƃöüñđ, çöñţŕîƃüţéš ñöţĥîñĝ àñđ ţĥé šéššîöñ çöñţîñüéš. Öţĥéŕ þļüĝîñš àŕé üñàƒƒéçţéđ.
- Ţĥé šéššîöñš ƃéļöñĝ ţö ţĥé ŕüñ. Éàçĥ šþàŵñéđ šéŕṽéŕ îš ĥéļđ ƒöŕ ţĥé ļîƒé öƒ ţĥé ḾÇÞ šéššîöñ àñđ çļöšéđ ƃý
App.Shutdown.
kapi ļîñķš ñö þļüĝîñ'š çöđé ĥéŕé éîţĥéŕ. Ţĥé þŕöẋý šþéàķš ḾÇÞ ţö à šüƃþŕöçéšš öṽéŕ šţđîö, šö à þļüĝîñ'š đéþéñđéñçîéš àñđ îţš ļîçéñçé šţàý îñšîđé îţš öŵñ ƃîñàŕý.
Ŵŕîţîñĝ à ñéŵ þļüĝîñ: à ŵöŕķéđ éẋàḿþļé
Šüþþöšé ýöü ŵàñţ ţö àđđ à gitlab šöüŕçé çöññéçţöŕ ţĥàţ þüšĥéš çöñţéñţ ţö à ĜîţĻàƃ ŕéþö àš ļöçàļé-šüƒƒîẋéđ ƃŕàñçĥéš. Ţĥé þļüĝîñ:
gitlab-plugin/
├── go.mod
├── plugin.go // anchor: imports the sub-packages
├── schema/
│ ├── extension.go // init() registers RegisterExtensionGroup("gitlab", ...)
│ └── server.go // GitLabServer { URL, Token, Branch, ... } + Validate
└── commands/
└── push.go // init() registers a "gitlab-push" command factory
gitlab-plugin/schema/server.go
package schema
import (
"errors"
"net/url"
"gopkg.in/yaml.v3"
)
type GitLabServer struct {
URL string `yaml:"url"`
Branch string `yaml:"branch,omitempty"`
}
func (s *GitLabServer) Validate() error {
if s == nil { return nil }
u, err := url.Parse(s.URL)
if err != nil || u.Scheme == "" {
return errors.New("url is required and must be a valid URL")
}
return nil
}
gitlab-plugin/schema/extension.go
package schema
import (
coreproj "github.com/neokapi/neokapi/core/project"
"gopkg.in/yaml.v3"
)
const Group = "gitlab"
func init() {
coreproj.RegisterExtensionGroup(Group, []coreproj.Extension{
{
Name: "gitlab",
Scope: coreproj.ScopeProject,
Decoder: coreproj.ExtensionDecoderFunc(func(n yaml.Node) error {
var s GitLabServer
if err := n.Decode(&s); err != nil { return err }
return s.Validate()
}),
},
})
}
Ŕéçîþé
version: v1
name: my-app
requires:
gitlab: "*"
gitlab:
url: https://gitlab.example.com/team/project
branch: main
collections:
- path: src/locales/**/*.json
format: json
Ŵĥéñ ļöàđéđ ƃý à ƃîñàŕý ţĥàţ ļîñķš gitlab-plugin/schema (ţĥé þļüĝîñ ƃîñàŕý, öŕ à đéšķţöþ àþþ ţĥàţ ƃļàñķ-îḿþöŕţš ţĥé šçĥéḿà ƒöŕ ṽàļîđàţîöñ), ţĥé ŕéçîþé ṽàļîđàţéš. Ŵĥéñ ļöàđéđ ƃý à kapi ŵîţĥ ñö ĝîţļàƃ þļüĝîñ îñšţàļļéđ, ţĥé ŕéçîþé ƒàîļš ţö ṽàļîđàţé:
invalid project file: recipe requires plugin "gitlab" (*) but no matching extension is registered (install with `kapi plugin install gitlab`)
Àđđîñĝ à çöḿḿàñđ
// gitlab-plugin/commands/push.go
package commands
import (
"github.com/neokapi/neokapi/cli"
"github.com/spf13/cobra"
)
func init() {
cli.RegisterCommandFactory(func(parent *cobra.Command, a *cli.App) {
parent.AddCommand(buildGitLabPushCmd(a))
})
}
func buildGitLabPushCmd(a *cli.App) *cobra.Command {
return &cobra.Command{
Use: "gitlab-push",
Short: "Push translations to a GitLab project",
RunE: func(cmd *cobra.Command, args []string) error {
// ... implementation that loads the recipe and pushes
return nil
},
}
}
Ţö šĥîþ ţĥé þļüĝîñ, ƃüîļđ îţ àš îţš öŵñ ƃîñàŕý ţĥàţ ƃļàñķ-îḿþöŕţš ţĥé àñçĥöŕ, àļöñĝšîđé à manifest.json đéçļàŕîñĝ ţĥé gitlab-push çöḿḿàñđ (àñđ àñý ƒöŕḿàţš/ţööļš/çöññéçţöŕš). kapi đîšçöṽéŕš ţĥé ƃîñàŕý àñđ đîšþàţçĥéš ţö îţ:
// gitlab-plugin/cmd/kapi-gitlab/main.go
package main
import _ "example.com/gitlab-plugin"
Þàçķàĝîñĝ àñđ ţĥé ļîçéñšé ƃöüñđàŕý
À þļüĝîñ šĥîþš àš îţš öŵñ ƃîñàŕý þļüš à manifest.json, îñšţàļļéđ îñţö à kapi þļüĝîñ đîŕéçţöŕý ŕàţĥéŕ ţĥàñ ļîñķéđ îñţö kapi. Ƃéçàüšé ţĥé þļüĝîñ ŕüñš àš à šéþàŕàţé þŕöçéšš, îţš ļîçéñšé îš îñđéþéñđéñţ öƒ kapi'š: ţĥé kapi ƃîñàŕý šţàýš Àþàçĥé-2.0 àñđ ļîñķš ñö þļüĝîñ'š çöđé, ŵĥîļé à þļüĝîñ ƃîñàŕý (é.ĝ. kapi-gitlab) çàŕŕîéš ţĥàţ þļüĝîñ'š þàçķàĝéš üñđéŕ îţš öŵñ ļîçéñšé. Ţĥé îñđéþéñđéñçé ŕüñš ƃöţĥ ŵàýš: à þļüĝîñ ḿàý ƃé ļîçéñšéđ ḿöŕé ŕéšţŕîçţîṽéļý ţĥàñ kapi ŵîţĥöüţ àƒƒéçţîñĝ îţ, àñđ à þļüĝîñ ţĥàţ îš ñöţ îš šţîļļ à šéþàŕàţé ƃîñàŕý ŕéàçĥéđ ţĥé šàḿé ŵàý. Ţĥéŕé îš ñö -tags pure / kapi-pure šþļîţ: kapi îš àļŵàýš þļüĝîñ-ƒŕéé, àñđ þļüĝîñš àŕé šöḿéţĥîñĝ ýöü îñšţàļļ îñţö îţ.
Šéé É-05: Þļüĝîñ Šýšţéḿ ƒöŕ ţĥé ḿàñîƒéšţ šçĥéḿà, đîšçöṽéŕý þŕéçéđéñçé, îñšţàļļ þàţĥš (kapi plugin install <name>, Ĥöḿéƃŕéŵ), àñđ ţĥé À/Ƃ/Ç ţŕàñšþöŕţ ḿöđéš.
Îñîţîàļîžàţîöñ öŕđéŕ
Ŵîţĥîñ öñé Ĝö ƃîñàŕý:
- Àļļ
init()ƒüñçţîöñš ŕüñ đüŕîñĝ ƃîñàŕý šţàŕţüþ, îñ þàçķàĝé îḿþöŕţ öŕđéŕ. Çŕöšš-þàçķàĝé îñîţ öŕđéŕ îš üñđéƒîñéđ; ţĥé ŕéĝîšţŕîéš àŕé àþþéñđ-öñļý, šö öŕđéŕ šĥöüļđñ'ţ ḿàţţéŕ ƒöŕ çöŕŕéçţñéšš. - Ţĥé ƃîñàŕý'š
main()çöñšţŕüçţš*cli.App, çàļļšapp.InitRegistries()(ƃüîļţ-îñ ƒöŕḿàţš/ţööļš). - Çöƃŕà'š
init()ƃüîļđš ţĥé çöḿḿàñđ ţŕéé; ţĥé ĥöšţ çàļļšcli.ApplyCommandFactories(root, app)àƒţéŕ. cobra.Execute()ŕüñš.PersistentPreRunçàļļšapp.Init()ţĥéñcli.ApplyAppInitializers(app).- Ţĥé çĥöšéñ šüƃçöḿḿàñđ'š
RunEŕüñš.
AppInitializer ŕüñš àƒţéŕ Init, šö þļüĝîñš çàñ ŕéļý öñ ţĥé ŕéĝîšţŕîéš (ƑöŕḿàţŔéĝ, ŢööļŔéĝ, Çŕéđéñţîàļš, Çöñƒîĝ) ƃéîñĝ þöþüļàţéđ. CommandFactory ŕüñš àţ init()-ţîḿé öƒ ţĥé ĥöšţ'š ḿàîñ þàçķàĝé; ŕéĝîšţŕîéš ḿüšţ ƃé ŕéàđý ƃý ţĥéñ, ŵĥîçĥ îš ŵĥý InitRegistries ŕüñš àţ çöƃŕà îñîţ ţöö.
Ĝöŕöüţîñéš àñđ àšýñç ŵöŕķ
Þļüĝîñš ţĥàţ ñééđ ţĥéîŕ öŵñ ƃàçķĝŕöüñđ ĝöŕöüţîñéš çàñ šþàŵñ ţĥéḿ îñšîđé ţĥéîŕ çöḿḿàñđš öŕ çöññéçţöŕš; ţĥé ŕéĝîšţŕîéš îḿþöšé ñö ḿöđéļ. À šöüŕçé çöññéçţöŕ, ƒöŕ éẋàḿþļé, çàñ ŕüñ çöñçüŕŕéñţ ƃļöçķ üþļöàđš ṽîà errgroup îñšîđé îţš Push. Ñöţĥîñĝ îñ ţĥé þļüĝîñ çöñţŕàçţ ŕéǫüîŕéš ţĥé þļüĝîñ ţö éẋþöšé îţš àšýñç ŵöŕķ ţö ţĥé ĥöšţ.
Ţéšţîñĝ þļüĝîñš
Üñîţ-ţéšţ ţĥé šçĥéḿà đéçöđéŕš đîŕéçţļý:
func TestGitLabServerDecodes(t *testing.T) {
var node yaml.Node
require.NoError(t, yaml.Unmarshal([]byte(`url: https://gitlab.example.com/team/proj`), &node))
var s GitLabServer
require.NoError(t, node.Decode(&s))
require.NoError(t, s.Validate())
}
Ţéšţš ţĥàţ ḿüţàţé ţĥé ĝļöƃàļ ŕéĝîšţŕîéš šĥöüļđ çàļļ coreproj.ResetExtensionsForTest() àñđ cli.ResetPluginRegistriesForTest() îñ šéţüþ šö ţĥéý đöñ'ţ ļéàķ šţàţé àçŕöšš ţéšţš.
Ŕéļàţîöñšĥîþ ţö ŕüñţîḿé đîšþàţçĥ
Ţĥé ŕéĝîšţŕîéš öñ ţĥîš þàĝé àŕé þüŕéļý îñ-þŕöçéšš: ţĥéý àššéḿƃļé ţĥé çöḿḿàñđ ţŕéé, ḾÇÞ ţööļš, àñđ ŕéçîþé šçĥéḿà îñšîđé à šîñĝļé þļüĝîñ ƃîñàŕý. Ţĥéý šàý ñöţĥîñĝ àƃöüţ ĥöŵ ţĥàţ ƃîñàŕý îš ƒöüñđ öŕ îñṽöķéđ; ţĥàţ îš ţĥé ŕüñţîḿé çöñçéŕñ öŵñéđ ƃý É-05: Þļüĝîñ Šýšţéḿ: manifest.json đîšçöṽéŕý àñđ ţĥé À/Ƃ/Ç ţŕàñšþöŕţ ḿöđéš (öñé-šĥöţ çöḿḿàñđ éẋéç, àñ ḾÇÞ-öṽéŕ-šţđîö šéššîöñ, àñđ ţĥé ĝŔÞÇ đàéḿöñ üšéđ ƃý ƒöŕḿàţš/ţööļš/šöüŕçé çöññéçţöŕš, îñçļüđîñĝ ţĥé Ĵàṽà Okapi ƃŕîđĝé).
Îñ šĥöŕţ: ţĥîš ñöţé îš ţĥé çöñţŕàçţ ƒöŕ ţĥé Ĝö îñšîđé à þļüĝîñ; É-05 îš ţĥé çöñţŕàçţ ƒöŕ ţĥé þŕöçéšš àŕöüñđ îţ.