From d12418b251cd01bfb033f500c8281181fdcdb257 Mon Sep 17 00:00:00 2001 From: gaymeowing <62822174+gaymeowing@users.noreply.github.com> Date: Tue, 28 Apr 2026 09:43:02 -0400 Subject: [PATCH 1/8] spring cleaning * remove ipairs and pairs usage * cleanup load option code a bit by using ternary with a macro function * cleanup log.pretty by using table.concat and removing excessive concat usage * make log use the nocolor argument on richterm formatters * fix log.update not removing the previous line if colorsEnabled is false * stringify varadics correctly in log, as using `or` would lead to only the first value of the varadic being printed * freeze log module --- src/commands/run.luau | 12 +++-- src/util/log.luau | 113 +++++++++++++++--------------------------- 2 files changed, 49 insertions(+), 76 deletions(-) diff --git a/src/commands/run.luau b/src/commands/run.luau index 9e27afd..e803f86 100644 --- a/src/commands/run.luau +++ b/src/commands/run.luau @@ -34,6 +34,10 @@ FLAGS: ]]) end +local function toBoolNum(v: any): number + return if v then 1 else 0 +end + return function(...) local fs = require("@std/fs") local process = require("@lute/process") @@ -83,7 +87,7 @@ return function(...) local globals = args:get("lua.globals") assert(globals, "Expected globals to be a comma-separated list of key=value pairs") - for _, pair in ipairs(string.split(globals, ",")) do + for _, pair in string.split(globals, ",") do local key, value = pair:match("([^=]+)=([^=]+)") if key and value then luaGlobals[key] = value @@ -100,9 +104,9 @@ return function(...) if ( - (args:get("load.place") and 1 or 0) - + (args:get("load.version") and 1 or 0) - + (args:get("load.project") and 1 or 0) + toBoolNum(args:get("load.place")) + + toBoolNum(args:get("load.version")) + + toBoolNum(args:get("load.project")) ) > 1 then log.fatal("Cannot specify more than one --load option") diff --git a/src/util/log.luau b/src/util/log.luau index ae4e93f..cf1dc9d 100644 --- a/src/util/log.luau +++ b/src/util/log.luau @@ -9,6 +9,7 @@ local log = {} type Level = "trace" | "debug" | "info" | "warn" | "error" +local dimBlueFormatter = richterm.combine(richterm.blue, richterm.dim) local currentLevel: Level = "info" local colorsEnabled = true -- set to false to disable colors @@ -18,13 +19,17 @@ end local showCaller = true -- set to false to disable caller information -local levels = { +local levels = table.freeze({ trace = 1, debug = 2, info = 3, warn = 4, error = 5, -} :: { [Level]: number } +}) :: { [Level]: number } + +local function stringifyVararg(...: any): string + return table.concat({ ... }, " ") +end function log.should(level: Level) return levels[level] >= levels[currentLevel] @@ -39,7 +44,7 @@ local function getCallerInfo() local lines = string.split(traceback, "\n") -- Look for the first line that's not in this logger module. - for _, line in ipairs(lines) do + for _, line in lines do if line:find("util/log") then continue end @@ -52,88 +57,50 @@ end function log.trace(...) if log.should("trace") then - local l: string = "" - if colorsEnabled then - l = richterm.combine(richterm.blue, richterm.dim)("[TRACE] ") - print(l .. richterm.dim(... or "")) - else - l = "[TRACE] " - print(l .. (... or "")) - end + local prefix = dimBlueFormatter("[TRACE] ") + print(prefix .. richterm.dim(stringifyVararg(...), colorsEnabled)) end end function log.debug(...) if log.should("debug") then - local l: string = "" - if colorsEnabled then - l = richterm.cyan("[DEBUG] ") - print(l .. richterm.dim(... or "")) - else - l = "[DEBUG] " - print(l .. (... or "")) - end + local prefix = richterm.cyan("[DEBUG] ", colorsEnabled) + print(prefix .. richterm.dim(stringifyVararg(...), colorsEnabled)) end end function log.info(...) if log.should("info") then - local l: string = "" - if colorsEnabled then - l = richterm.green("[INFO] ") - else - l = "[INFO] " - end - print(l .. (... or "")) + local prefix = richterm.green("[INFO] ", colorsEnabled) + print(prefix .. stringifyVararg(...)) end end function log.update(...) if log.should("info") then - local l: string = "" - if colorsEnabled then - l = richterm.green("[INFO] ") - print("\x1b[1A\x1b[2K" .. l .. (... or "")) - else - l = "[INFO] " - print(l .. (... or "")) - end + local prefix = richterm.green("[INFO] ", colorsEnabled) + print("\x1b[1A\x1b[2K" .. prefix .. stringifyVararg(...)) end end function log.warn(...) if log.should("warn") then - local l: string = "" - if colorsEnabled then - l = richterm.yellow("[WARN] ") - else - l = "[WARN] " - end - print(l .. (... or "")) + local prefix = richterm.yellow("[WARN] ", colorsEnabled) + print(prefix .. stringifyVararg(...)) end end function log.error(...) if log.should("error") then - local l: string = "" - if colorsEnabled then - l = richterm.red("[ERROR] ") - else - l = "[ERROR] " - end - print(l .. (... or "")) + local prefix = richterm.red("[ERROR] ", colorsEnabled) + print(prefix .. stringifyVararg(...)) end end function log.fatal(...): never + local prefix = richterm.red("[FATAL] ", colorsEnabled) local caller = getCallerInfo() - local l: string = "" - if colorsEnabled then - l = richterm.red("[FATAL] ") - else - l = "[FATAL] " - end - print(caller .. " | " .. l .. (... or "") .. "\n") + print(caller .. " | " .. prefix .. stringifyVararg(...)) process.exit(1) return nil :: any end @@ -167,26 +134,28 @@ function log.noColor() end function log.pretty(tbl: any, indent: number?): string - local i = indent or 0 - local spacing = string.rep(" ", i) if type(tbl) ~= "table" then return tostring(tbl) end - local result = "{\n" - for key, value in pairs(tbl) do - local keyStr = type(key) == "string" and key or "[" .. tostring(key) .. "]" - result = result .. spacing .. " " .. keyStr .. ": " - if type(value) == "table" then - result = result .. log.pretty(value, i + 1) - elseif type(value) == "string" then - result = result .. '"' .. value .. '"' - else - result = result .. tostring(value) - end - result = result .. "\n" + + indent = indent or 0 + local spacing = string.rep(" ", indent) + local result = { "{" } + + for key, value in tbl do + local keyStr = if type(key) == "string" then key else `["{key}"]` + --stylua: ignore + local valueStr = if type(value) == "table" then + log.pretty(value, indent + 1) + elseif type(value) == "string" then + `"{value}"` + else + value + + table.insert(result, `{keyStr}: {valueStr}`) end - result = result .. spacing .. "}" - return result + table.insert(result, "}") + return table.concat(result, `\n{spacing}`) end -return log +return table.freeze(log) From 3770c6c92388f665d1ad34097906a9bea52ee9dc Mon Sep 17 00:00:00 2001 From: gaymeowing <62822174+gaymeowing@users.noreply.github.com> Date: Tue, 28 Apr 2026 09:46:06 -0400 Subject: [PATCH 2/8] stylua formatting --- src/commands/run.luau | 1 + src/util/log.luau | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/commands/run.luau b/src/commands/run.luau index e803f86..17955df 100644 --- a/src/commands/run.luau +++ b/src/commands/run.luau @@ -102,6 +102,7 @@ return function(...) placeId = tonumber(args:get("placeId")) or log.fatal("Missing a place ID"), } + --stylua: ignore if ( toBoolNum(args:get("load.place")) diff --git a/src/util/log.luau b/src/util/log.luau index cf1dc9d..cb42c23 100644 --- a/src/util/log.luau +++ b/src/util/log.luau @@ -98,7 +98,7 @@ function log.error(...) end function log.fatal(...): never - local prefix = richterm.red("[FATAL] ", colorsEnabled) + local prefix = richterm.red("[FATAL] ", colorsEnabled) local caller = getCallerInfo() print(caller .. " | " .. prefix .. stringifyVararg(...)) process.exit(1) From 7e1e7871f233d3c29597f2d663ceb74163127364 Mon Sep 17 00:00:00 2001 From: gaymeowing <62822174+gaymeowing@users.noreply.github.com> Date: Tue, 28 Apr 2026 10:14:03 -0400 Subject: [PATCH 3/8] * remove pairs usage in ocaleSdk * improve spinner code via using constant modulus instead of `#spinnerChars`, using richterm.combine for spinner coloring, using a macro function for seconds formatting, and making spinnerChars a constant table --- src/core/ocaleSdk.luau | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/core/ocaleSdk.luau b/src/core/ocaleSdk.luau index 16adc56..0ba10dc 100644 --- a/src/core/ocaleSdk.luau +++ b/src/core/ocaleSdk.luau @@ -6,6 +6,13 @@ local task = require("@lute/task") local log = require("../util/log") local richterm = require("@batteries/richterm") +local boldYellowFormatter = richterm.combine(richterm.yellow, richterm.bold) +local spinnerChars = table.freeze({ "|", "/", "-", "\\" }) + +local function formatSeconds(s: number): string + return string.format("%.1fs", s) +end + local OcaleSDK = {} export type BinaryInputResponse = { path: string, size: number, uploadUri: string } @@ -104,7 +111,7 @@ function OcaleSDK.createTask( if luaGlobals and next(luaGlobals) then local keys: { string } = {} local values: { string } = {} - for key, value in pairs(luaGlobals) do + for key, value in luaGlobals do table.insert(keys, `_G[{string.format("%q", key)}]`) table.insert(values, string.format("%q", value)) end @@ -141,21 +148,19 @@ function OcaleSDK.pollTaskCompletion( local timeRemaining = timeout or 300 local interval = pollInterval or 2 - local spinnerChars = { "|", "/", "-", "\\" } - local spinnerIndex = 1 - local spinner = richterm.yellow(spinnerChars[spinnerIndex], log.noColor()) - local startTime = os.clock() - local elapsed = string.format("%.1f", os.clock() - startTime) log.info(`Polling for OCALE task completion`) if not log.noColor() then task.spawn(function() + local spinnerIndex = 1 + while timeRemaining > 0 do - spinner = richterm.bold(richterm.yellow(spinnerChars[spinnerIndex], log.noColor())) - elapsed = string.format("%.1f", os.clock() - startTime) - log.update(`{spinner} {richterm.dim(`[{elapsed}s]`, log.noColor())} Polling for OCALE task completion`) - spinnerIndex = spinnerIndex % #spinnerChars + 1 + local spinner = boldYellowFormatter(spinnerChars[spinnerIndex], log.noColor()) + local elapsed = formatSeconds(os.clock() - startTime) + + log.update(`{spinner} {richterm.dim(`[{elapsed}]`, log.noColor())} Polling for OCALE task completion`) + spinnerIndex = (spinnerIndex % 4) + 1 task.wait(0.1) end end) @@ -177,9 +182,9 @@ function OcaleSDK.pollTaskCompletion( if body.state ~= "PROCESSING" then timeRemaining = 0 task.wait(0.1) - elapsed = string.format("%.1f", os.clock() - startTime) + local elapsed = formatSeconds(os.clock() - startTime) log.info( - `{richterm.green("✓", log.noColor())} {richterm.dim(`[{elapsed}s]`, log.noColor())} OCALE task completed` + `{richterm.green("✓", log.noColor())} {richterm.dim(`[{elapsed}]`, log.noColor())} OCALE task completed` ) return body end @@ -188,9 +193,10 @@ function OcaleSDK.pollTaskCompletion( end timeRemaining = 0 - elapsed = string.format("%.1f", os.clock() - startTime) + local elapsed = formatSeconds(os.clock() - startTime) + --stylua: ignore return log.fatal( - `{richterm.red("✗", log.noColor())} {richterm.dim(`[{elapsed}s]`, log.noColor())} Task timed out` + `{richterm.red("✗", log.noColor())} {richterm.dim(`[{elapsed}]`, log.noColor())} Task timed out` ) end From 4bcaea8a1a831d7323f25a14da8079f7916ab5b4 Mon Sep 17 00:00:00 2001 From: gaymeowing <62822174+gaymeowing@users.noreply.github.com> Date: Thu, 30 Apr 2026 10:45:29 -0400 Subject: [PATCH 4/8] simplify global assignment code --- src/core/ocaleSdk.luau | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/core/ocaleSdk.luau b/src/core/ocaleSdk.luau index 0ba10dc..0b1a5c3 100644 --- a/src/core/ocaleSdk.luau +++ b/src/core/ocaleSdk.luau @@ -109,14 +109,12 @@ function OcaleSDK.createTask( -- inject globals at the top of the entry script if luaGlobals and next(luaGlobals) then - local keys: { string } = {} - local values: { string } = {} + local globalAssignments: { string } = {} + for key, value in luaGlobals do - table.insert(keys, `_G[{string.format("%q", key)}]`) - table.insert(values, string.format("%q", value)) + table.insert(globalAssignments, `_G["{key}"]="{value}";`) end - local assignGlobals = table.concat(keys, ",") .. "=" .. table.concat(values, ",") .. " " - entryScript = assignGlobals .. entryScript + entryScript = table.concat(globalAssignments) .. entryScript end local req: net.Metadata = { From d896561918c48e4d59044eac214f19626f8d4ec8 Mon Sep 17 00:00:00 2001 From: gaymeowing <62822174+gaymeowing@users.noreply.github.com> Date: Thu, 30 Apr 2026 13:52:15 -0400 Subject: [PATCH 5/8] freeze ocaleSdk module table --- src/core/ocaleSdk.luau | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/ocaleSdk.luau b/src/core/ocaleSdk.luau index 0b1a5c3..4185a65 100644 --- a/src/core/ocaleSdk.luau +++ b/src/core/ocaleSdk.luau @@ -221,4 +221,4 @@ function OcaleSDK.getTaskLogs(apiKey: string, taskPath: string): { string } return logs end -return OcaleSDK +return table.freeze(OcaleSDK) From e9cfd418695c0e556bf1a8e122660d5a9a823df2 Mon Sep 17 00:00:00 2001 From: gaymeowing <62822174+gaymeowing@users.noreply.github.com> Date: Thu, 30 Apr 2026 13:52:59 -0400 Subject: [PATCH 6/8] remove getCallerInfo as the format of debug.traceback shouldn't be relied on as per luau documentation --- src/util/log.luau | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/src/util/log.luau b/src/util/log.luau index cb42c23..5938d1d 100644 --- a/src/util/log.luau +++ b/src/util/log.luau @@ -35,26 +35,6 @@ function log.should(level: Level) return levels[level] >= levels[currentLevel] end --- Get caller information from debug traceback. -local function getCallerInfo() - if not showCaller then - return "" - end - local traceback = debug.traceback() - local lines = string.split(traceback, "\n") - - -- Look for the first line that's not in this logger module. - for _, line in lines do - if line:find("util/log") then - continue - end - - return line - end - - return "[unknown]" -end - function log.trace(...) if log.should("trace") then local prefix = dimBlueFormatter("[TRACE] ") @@ -99,8 +79,9 @@ end function log.fatal(...): never local prefix = richterm.red("[FATAL] ", colorsEnabled) - local caller = getCallerInfo() - print(caller .. " | " .. prefix .. stringifyVararg(...)) + local message = prefix .. stringifyVararg(...) + + print(if showCaller then debug.traceback(message, 2) else message) process.exit(1) return nil :: any end From 2ef96bc540bd66d40efd2ba806a3a5351b91730d Mon Sep 17 00:00:00 2001 From: gaymeowing <62822174+gaymeowing@users.noreply.github.com> Date: Tue, 12 May 2026 09:22:53 -0400 Subject: [PATCH 7/8] * Remove pcall in runProcess, as process.run wont error * Make runProcess take in a varadic, as it makes for a nicer API --- scripts/build.luau | 2 +- scripts/test.luau | 6 +++--- src/commands/run.luau | 8 ++++---- src/util/runProcess.luau | 31 ++++++++++++++++++------------- 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/scripts/build.luau b/scripts/build.luau index 0994d4d..2867616 100644 --- a/scripts/build.luau +++ b/scripts/build.luau @@ -27,5 +27,5 @@ fs.write(versionFile, `return \{ version="{version}", hash="{hash}" \}`) fs.close(versionFile) log.info("Building rocale-cli...") -runProcess({ "lute", "compile", "src/cli.luau", "--output", output_path, "--show-require-graph", "--bundle-stats" }) +runProcess("lute", "compile", "src/cli.luau", "--output", output_path, "--show-require-graph", "--bundle-stats") log.info("Build complete: " .. output_path) diff --git a/scripts/test.luau b/scripts/test.luau index bfc0602..2508075 100644 --- a/scripts/test.luau +++ b/scripts/test.luau @@ -6,7 +6,7 @@ log.setLevel("debug") local runProcess = require("../src/util/runProcess") -runProcess({ +runProcess( "build/rocale-cli", "run", "--script", @@ -21,8 +21,8 @@ runProcess({ "HELLO_GLOBAL=hello,WORLD_GLOBAL=world!", "--binaryOutput", "build/testOutput.txt", - "--verbose", -}) + "--verbose" +) local testOutput = fs.readfiletostring("build/testOutput.txt") fs.remove("build/testOutput.txt") diff --git a/src/commands/run.luau b/src/commands/run.luau index 17955df..a49966a 100644 --- a/src/commands/run.luau +++ b/src/commands/run.luau @@ -131,16 +131,16 @@ return function(...) assert(outputPath, "Missing output file path") if projectFile:match("%.project%.json$") then - runProcess({ "rojo", "build", projectFile, "--output", outputPath }) + runProcess("rojo", "build", projectFile, "--output", outputPath) elseif projectFile:match("%.rbxp$") then - runProcess({ + runProcess( "robloxdev-cli", "pack", "--input", projectFile, "--output", - outputPath, - }) + outputPath + ) else log.fatal(`Unsupported project file: '{projectFile}'. Must be a 'project.json' or '.rbxp' file.`) end diff --git a/src/util/runProcess.luau b/src/util/runProcess.luau index 2bf51bb..1e856b6 100644 --- a/src/util/runProcess.luau +++ b/src/util/runProcess.luau @@ -1,26 +1,31 @@ -local process = require("@lute/process") -local system = require("@lute/system") +local process = require("@std/process") +local system = require("@std/system") local log = require("../util/log") -return function(cmd: { string }) - local isWin32 = string.lower(system.os) == "windows_nt" +local isWin32 = string.lower(system.os) == "windows_nt" + +local function runProcess(...: string): string + local cmd = { ... } + if isWin32 then - cmd[1] = cmd[1] .. ".exe" + cmd[1] ..= ".exe" end local cmdStr = table.concat(cmd, " ") log.debug(`Running command: {cmdStr}`) - local ok, result = pcall(function() - return process.run( - cmd, - { stdio = "inherit", env = { PATH = `{process.cwd()}{if isWin32 then ";" else ":"}{process.env.PATH}` } } - ) - end) + local result = process.run(cmd, { + stdio = "inherit", + env = { + PATH = `{process.cwd()}{if isWin32 then ";" else ":"}{process.env.PATH}`, + }, + }) - if ok and result.ok then + if result.ok then return result.stdout else - return log.fatal(`Error while running command: {cmdStr}\n{result.stderr or result}`) + return log.fatal(`Error while running command: {cmdStr}\n{result.stderr}`) end end + +return runProcess From c154de8138186f90ce7495fa02834bdef09c4088 Mon Sep 17 00:00:00 2001 From: gaymeowing <62822174+gaymeowing@users.noreply.github.com> Date: Thu, 14 May 2026 09:11:35 -0400 Subject: [PATCH 8/8] fix build failing --- scripts/test.luau | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/test.luau b/scripts/test.luau index f5ba81e..920e225 100644 --- a/scripts/test.luau +++ b/scripts/test.luau @@ -30,7 +30,7 @@ assert(testOutput == "hello world!", `Expected binary output to be "hello world! log.info("Rerunning tests using load.version and without optional variables") -runProcess({ +runProcess( "build/rocale-cli", "run", "--script", @@ -41,5 +41,5 @@ runProcess({ process.env.TEST_UNIVERSE_ID, "--load.version", "0", - "--verbose", -}) + "--verbose" +)