cmd: add synthetic command validate-interfaces

The command is injected when running xcaddy with one of Caddy's commands, though `validate-interfaces` does not exist in Caddy itself. This is useful for [guest modules](https://caddyserver.com/docs/extending-caddy) developers to validate their implementation meets the expectation of the host module.
This commit is contained in:
Mohammed Al Sahaf 2023-12-14 23:35:37 +03:00
parent 18eadf4260
commit 971664d22f
No known key found for this signature in database
3 changed files with 44 additions and 4 deletions

View file

@ -45,6 +45,15 @@ type Builder struct {
Debug bool `json:"debug,omitempty"`
BuildFlags string `json:"build_flags,omitempty"`
ModFlags string `json:"mod_flags,omitempty"`
// Experimental: Inject extra imports into the build. These
// imports are to add Go modules that may be needed in the
// Prequels.
ExtraImports []string
// Experimental: Inject extra code before running Caddy's `main`.
// If any of the prequels requires an import, it's you responsibility
// to add the import to the ExtraImports slice.
Prequels []string
}
// Build builds Caddy at the configured version with the

View file

@ -223,6 +223,8 @@ func runDev(ctx context.Context, args []string) error {
SkipBuild: skipBuild,
SkipCleanup: skipCleanup,
Debug: buildDebugOutput,
ExtraImports: []string{`caddy "github.com/caddyserver/caddy/v2"`, `"fmt"`, `"github.com/spf13/cobra"`},
Prequels: []string{interfaceValidationCommand},
}
err = builder.Build(ctx, binOutput)
if err != nil {
@ -439,3 +441,22 @@ func goModule() *debug.Module {
}
return mod
}
const interfaceValidationCommand = `
caddycmd.RegisterCommand(caddycmd.Command{
Name: "validate-interfaces",
Short: "Validate that all modules conform to their namespaces",
CobraFunc: func(cmd *cobra.Command) {
cmd.RunE = caddycmd.WrapCommandFuncForCobra(func(f caddycmd.Flags) (int, error) {
for _, v := range caddy.Modules() {
mod, _ := caddy.GetModule(v)
if ok, err := caddy.ConformsToNamespace(mod.New(), mod.ID.Namespace()); !ok {
return caddy.ExitCodeFailedStartup, fmt.Errorf("module %s does not conform to its namespace: %v", mod.ID, err)
}
}
fmt.Println("All modules conform to their namespaces.")
return caddy.ExitCodeSuccess, nil
})
},
})
`

View file

@ -52,6 +52,8 @@ func (b Builder) newEnvironment(ctx context.Context) (*environment, error) {
// create the context for the main module template
tplCtx := goModTemplateContext{
CaddyModule: caddyModulePath,
ExtraImports: b.ExtraImports,
Prequels: b.Prequels,
}
for _, p := range b.Plugins {
tplCtx.Plugins = append(tplCtx.Plugins, p.PackagePath)
@ -86,7 +88,7 @@ func (b Builder) newEnvironment(ctx context.Context) (*environment, error) {
// write the main module file to temporary folder
mainPath := filepath.Join(tempFolder, "main.go")
log.Printf("[INFO] Writing main module: %s\n%s", mainPath, buf.Bytes())
err = os.WriteFile(mainPath, buf.Bytes(), 0644)
err = os.WriteFile(mainPath, buf.Bytes(), 0o644)
if err != nil {
return nil, err
}
@ -319,6 +321,8 @@ func (env environment) execGoGet(ctx context.Context, modulePath, moduleVersion,
type goModTemplateContext struct {
CaddyModule string
Plugins []string
ExtraImports []string
Prequels []string
}
const mainModuleTemplate = `package main
@ -331,9 +335,15 @@ import (
{{- range .Plugins}}
_ "{{.}}"
{{- end}}
{{- range .ExtraImports}}
{{.}}
{{- end}}
)
func main() {
{{- range .Prequels}}
{{.}}
{{- end}}
caddycmd.Main()
}
`