Running scripts
How a script is found
Section titled “How a script is found”luneblox run <path>One path, four candidates
Section titled “One path, four candidates”The path is treated as a Luau module path. A .luau or .lua extension is removed first, and a
trailing init segment is removed too, so these all name the same script:
luneblox run tools/buildluneblox run tools/build.luauluneblox run tools/build/init.luauFor the module path tools/build, these files are candidates:
tools/build.luautools/build.luatools/build/init.luautools/build/init.lua
Exactly one of them must exist. If two exist, such as build.luau next to a build/init.luau, the
path is ambiguous and the run fails. A directory with no init file fails with
The path is a directory without an init file.
Where it looks
Section titled “Where it looks”A relative path is tried in five places, in this order. The first place that resolves wins.
| Order | Location |
|---|---|
| 1 | The path itself, relative to the current directory |
| 2 | lune/<path> in the current directory |
| 3 | .lune/<path> in the current directory |
| 4 | lune/<path> in your home directory |
| 5 | .lune/<path> in your home directory |
So a file in the current directory shadows a script of the same name in a script folder, and a project script shadows a global one.
An absolute path is used as given. The script folders are never searched for it.
When nothing matches, the error names the first location:
Failed to resolve script at path /work/project/build (NotFound)Project scripts and global scripts
Section titled “Project scripts and global scripts”The lune and .lune folders in a project hold its scripts: build steps, code generation, release
tasks. Run them from the project root, since the folders are looked up in the current directory and
not in parent directories.
The same folders in your home directory hold global scripts, which are found from any directory:
~/.lune/ notes.luaucd /anywhereluneblox run notesluneblox list prints both sets. For each place it reads lune, or .lune when lune does not
exist, so keep the scripts of one place in one folder.
Shebang lines
Section titled “Shebang lines”If the file starts with #!, that first line is skipped. Line numbers in error messages still match
the file.
#!/usr/bin/env -S luneblox runprint("run me directly")Arguments
Section titled “Arguments”Everything after the script path is passed to the script. luneblox does not parse it, so flags
reach the script as written:
luneblox run deploy --env staging -v--!strictlocal process = require("@lune/process")
for index, arg in process.args do print(index, arg)end1 --env2 staging3 -vprocess.args is a read-only table of strings. It does not include luneblox, run or the script
path, nor flags written before the path, such as --coverage, which are
luneblox’s own. Environment variables are in process.env. See process.
Reading the script from stdin
Section titled “Reading the script from stdin”A script path of a single - reads the script from stdin until it closes, then runs it:
echo 'print(1 + 1)' | luneblox run -Arguments still work:
luneblox run - one two < script.luauA script run this way is named stdin in error messages. It has no location on disk, so it can
require the @lune/* libraries but not other files.
Exit codes
Section titled “Exit codes”| Situation | Exit code |
|---|---|
The script called process.exit(code) |
code |
The script called process.exit() with no code |
0 |
| A thread threw an error that nothing caught | 1 |
| Everything finished without an error | 0 |
| The script could not be found or read | 1 |
An explicit process.exit code wins over the automatic one: a script that logged an uncaught error
in a spawned task and then calls process.exit(0) exits with 0.
An uncaught error in any thread counts, not only in the main one. The error is printed to stderr,
the other threads keep running, and the exit code becomes 1 when the script ends:
--!strictlocal task = require("@lune/task")
task.spawn(function() error("background failure")end)
task.wait(1)print("still running")luneblox run example.luau; echo $?The last line printed is 1.
Exit codes are one byte, 0 to 255.
The REPL
Section titled “The REPL”luneblox with no command, or luneblox repl, starts an interactive session:
Lune v0.11.3> print(1 + 1)2> for i = 1, 2 do>> print(i)>> end12- The
>>prompt means the statement is incomplete and the next line continues it. - All input runs in one runtime, and the
@lune/*libraries can be required. - An error is printed and the session goes on.
- Ctrl+C prints
Interrupt: ^C again to exit; pressing it again exits. Ctrl+D exits at once. - Each line is saved to
.luneblox_historyin the state directory and is available with the up arrow in later sessions. See Directories.
The REPL needs a terminal. Do not start it from CI or a git hook.
Native code generation
Section titled “Native code generation”luneblox run turns on Luau’s native code generation for the script. To run it interpreted, set
LUNE_LUAU_JIT to 0, false or off:
LUNE_LUAU_JIT=0 luneblox run bench.luauOnly those three values, in lower case, turn it off. Any other value, or no value, leaves it on.
The variable is read by luneblox run only. The REPL and
standalone executables do not read it and run with native
code generation off.
Whatever the setting, a chunk loaded with luau.load is compiled to native code only when its
codegenEnabled option is set. See luau.
Line coverage
Section titled “Line coverage”--coverage writes which lines ran, and how often, to an LCOV
file that coverage tools and editors read. It goes before the script path:
luneblox run --coverage testsluneblox run --coverage=coverage/lcov.info testsWith no file named, the report is lcov.info in the current directory. A file in a directory that
does not exist creates the directory.
The report covers the script, every module it requires, and every chunk it loads with luau.load,
which is listed under its debugName. A file required more than once is one record. Paths inside the
current directory are written relative to it.
The report is written when the script ends, also when it ends with an error, so a test run that
fails still says what it reached. Chunks are compiled to count every statement, as
luau --coverage does, so a run with coverage can be slower than one without.
Fast flags
Section titled “Fast flags”Every way of running Luau here - run, the REPL, a standalone executable - sets Luau’s fast flags to
the live Roblox client’s values first. LUNE_ROBLOX_FFLAGS=0 skips that. See
Fast flags.
