diff --git a/server/config.go b/server/config.go index 27705f2c26..87c9c7f185 100644 --- a/server/config.go +++ b/server/config.go @@ -825,6 +825,7 @@ type RuntimeConfig struct { LuaReadOnlyGlobals bool `yaml:"lua_read_only_globals" json:"lua_read_only_globals" usage:"When enabled marks all Lua runtime global tables as read-only to reduce memory footprint. Default true."` JsReadOnlyGlobals bool `yaml:"js_read_only_globals" json:"js_read_only_globals" usage:"When enabled marks all Javascript runtime globals as read-only to reduce memory footprint. Default true."` LuaApiStacktrace bool `yaml:"lua_api_stacktrace" json:"lua_api_stacktrace" usage:"Include the Lua stacktrace in error responses returned to the client. Default false."` + LuaPaths []string `yaml:"lua_path" json:"lua_path" usage:"Lua default path directories."` JsEntrypoint string `yaml:"js_entrypoint" json:"js_entrypoint" usage:"Specifies the location of the bundled JavaScript runtime source code."` } @@ -886,6 +887,7 @@ func NewRuntimeConfig() *RuntimeConfig { LuaReadOnlyGlobals: true, JsReadOnlyGlobals: true, LuaApiStacktrace: false, + LuaPaths: make([]string, 0), } } diff --git a/server/runtime_lua.go b/server/runtime_lua.go index e3d2153485..3d1c0fa4f1 100644 --- a/server/runtime_lua.go +++ b/server/runtime_lua.go @@ -113,7 +113,8 @@ func NewRuntimeProviderLua(logger, startupLogger *zap.Logger, db *sql.DB, protoj startupLogger.Info("Initialising Lua runtime provider", zap.String("path", rootPath)) // Load Lua modules into memory by reading the file contents. No evaluation/execution at this stage. - moduleCache, modulePaths, stdLibs, err := openLuaModules(startupLogger, rootPath, paths) + moduleCache, modulePaths, stdLibs, err := openLuaModules(startupLogger, + append([]string{rootPath}, config.GetRuntime().LuaPaths...), paths) if err != nil { // Errors already logged in the function call above. return nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, err @@ -1215,7 +1216,8 @@ func NewRuntimeProviderLua(logger, startupLogger *zap.Logger, db *sql.DB, protoj func CheckRuntimeProviderLua(logger *zap.Logger, config Config, version string, paths []string) error { // Load Lua modules into memory by reading the file contents. No evaluation/execution at this stage. - moduleCache, _, stdLibs, err := openLuaModules(logger, config.GetRuntime().Path, paths) + moduleCache, _, stdLibs, err := openLuaModules(logger, + append([]string{config.GetRuntime().Path}, config.GetRuntime().LuaPaths...), paths) if err != nil { // Errors already logged in the function call above. return err @@ -1231,7 +1233,7 @@ func CheckRuntimeProviderLua(logger *zap.Logger, config Config, version string, return nil } -func openLuaModules(logger *zap.Logger, rootPath string, paths []string) (*RuntimeLuaModuleCache, []string, map[string]lua.LGFunction, error) { +func openLuaModules(logger *zap.Logger, rootPaths []string, paths []string) (*RuntimeLuaModuleCache, []string, map[string]lua.LGFunction, error) { moduleCache := &RuntimeLuaModuleCache{ Names: make([]string, 0), Modules: make(map[string]*RuntimeLuaModule, 0), @@ -1239,8 +1241,12 @@ func openLuaModules(logger *zap.Logger, rootPath string, paths []string) (*Runti modulePaths := make([]string, 0) // Override before Package library is invoked. - lua.LuaLDir = rootPath - lua.LuaPathDefault = lua.LuaLDir + string(os.PathSeparator) + "?.lua;" + lua.LuaLDir + string(os.PathSeparator) + "?" + string(os.PathSeparator) + "init.lua" + lua.LuaLDir = rootPaths[0] + lua.LuaPathDefault = lua.LuaLDir + lua.LuaDirSep + "?.lua;" + lua.LuaPathDefault += lua.LuaLDir + lua.LuaDirSep + "?" + lua.LuaDirSep + "init.lua;" + for i := 1; i < len(rootPaths); i++ { + lua.LuaPathDefault += filepath.Join(lua.LuaLDir, rootPaths[i]) + ";" + } if err := os.Setenv(lua.LuaPath, lua.LuaPathDefault); err != nil { logger.Error("Could not set Lua module path", zap.Error(err)) return nil, nil, nil, err @@ -1259,7 +1265,7 @@ func openLuaModules(logger *zap.Logger, rootPath string, paths []string) (*Runti return nil, nil, nil, err } - relPath, _ := filepath.Rel(rootPath, path) + relPath, _ := filepath.Rel(rootPaths[0], path) name := strings.TrimSuffix(relPath, filepath.Ext(relPath)) // Make paths Lua friendly. name = strings.Replace(name, string(os.PathSeparator), ".", -1) diff --git a/server/runtime_lua_loadlib.go b/server/runtime_lua_loadlib.go index e3686c1662..201156de77 100644 --- a/server/runtime_lua_loadlib.go +++ b/server/runtime_lua_loadlib.go @@ -54,7 +54,18 @@ func OpenPackage(moduleCache *RuntimeLuaModuleCache) func(L *lua.LState) int { return func(L *lua.LState) int { loLoaderCache := func(L *lua.LState) int { name := L.CheckString(1) - module, ok := moduleCache.Modules[name] + var ok bool + var module *RuntimeLuaModule + dir := lua.LuaLDir + lua.LuaDirSep + for _, path := range strings.Split(lua.LuaPathDefault, ";") { + path = strings.TrimPrefix(path, dir) + path = strings.TrimSuffix(path, ".lua") + path = strings.ReplaceAll(path, "?", name) + path = strings.ReplaceAll(path, lua.LuaDirSep, ".") + if module, ok = moduleCache.Modules[path]; ok { + break + } + } if !ok { L.Push(lua.LString(fmt.Sprintf("no cached module '%s'", name))) return 1