Last active
May 9, 2024 11:58
-
-
Save cxmeel/d24eb096ebfb563f6e4267beb349c907 to your computer and use it in GitHub Desktop.
A Lune script for building plugins and watching for file changes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--!strict | |
--[[ | |
Usage: | |
Build production release: | |
> `lune run build` | |
Build dev release to Roblox/Plugins directory: | |
> `lune run build -- --dev` | |
Build dev release to plugins dir AND watch for changes: | |
> `lune run build -- --dev --watch` | |
Modify the `PROJECT_JSON`, `WATCHED_DIRS` and `COMMAND_SEQUENCE` | |
variables as required. | |
]] | |
local DateTime = require("@lune/datetime") | |
local fs = require("@lune/fs") | |
local process = require("@lune/process") | |
local serde = require("@lune/serde") | |
local task = require("@lune/task") | |
local ARGS = process.args | |
local isDev = table.find(ARGS, "--dev") ~= nil | |
local isWatch = table.find(ARGS, "--watch") ~= nil | |
type Command = { | |
label: string?, | |
exec: string | ({ string }) -> (), | |
args: { string }?, | |
when: (() -> boolean)?, | |
} | |
local PROJECT_JSON = serde.decode("json", fs.readFile("build.project.json")) | |
local POLL_INTERVAL = 2 | |
local WATCHED_DIRS: { string } = { | |
"src", | |
"packages", | |
} | |
local COMMAND_SEQUENCE: { Command } = { | |
{ | |
exec = "lune", | |
args = { "run", "wally-install" }, | |
when = function() | |
return not isDev and not isWatch | |
end, | |
}, | |
{ | |
exec = "rojo", | |
args = { "sourcemap", "-o", "sourcemap.json" }, | |
}, | |
{ | |
label = "remove build directory", | |
exec = function() | |
if fs.isDir("build") then | |
fs.removeDir("build") | |
end | |
end, | |
}, | |
{ | |
exec = "darklua", | |
args = { "process", "src", "build", "-c", "darklua.json" }, | |
}, | |
{ | |
exec = "rojo", | |
args = { "build", "build.project.json", "--plugin", `{PROJECT_JSON.name}.rbxm` }, | |
when = function() | |
return isDev | |
end, | |
}, | |
{ | |
exec = "rojo", | |
args = { "build", "build.project.json", "-o", `{PROJECT_JSON.name}.rbxm` }, | |
when = function() | |
return not isDev | |
end, | |
}, | |
} | |
local SESSION_START = DateTime.now() | |
local function generateUuid() | |
local template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx" | |
return string.gsub(template, "[xy]", function(c) | |
local v = (c == "x") and math.random(0, 0xf) or math.random(8, 0xb) | |
return string.format("%x", v) | |
end) | |
end | |
local function shortenUuid(uuid: string) | |
return uuid:sub(1, 7) | |
end | |
local function runCommandSequence() | |
local RUN_SESSION_ID = generateUuid() | |
local now = DateTime.now() | |
local formattedTime = now:formatLocalTime("%X") | |
print(`Running build sequence: {shortenUuid(RUN_SESSION_ID)} ({formattedTime})`) | |
for _, command in COMMAND_SEQUENCE do | |
if not command.when or command.when() then | |
local label = command.label or tostring(command.exec) | |
local isFunction = typeof(command.exec) == "function" | |
print( | |
`> Running {isFunction and "function" or "command"}: {label} {table.concat( | |
command.args or {}, | |
" " | |
)}` | |
) | |
local result: any | |
if isFunction then | |
local ok, err = pcall(command.exec :: any, command.args) | |
result = { ok = ok, stderr = err } | |
else | |
result = process.spawn(command.exec :: string, command.args) | |
end | |
if not result.ok then | |
error(`Failed to run command: {command.exec}\n> {result.stderr}`) | |
end | |
end | |
end | |
if isWatch then | |
print("\nWaiting for changes. Press CTRL+C to stop.\n") | |
end | |
end | |
local function powershellGetLastModified(dir: string) | |
local dirModifiedAt = (fs.metadata(dir).modifiedAt or SESSION_START) :: any | |
local lastModified = dirModifiedAt.unixTimestamp | |
local result = process.spawn("powershell", { | |
"-Command", | |
`Get-ChildItem -Recurse -Path {dir} | Sort-Object LastWriteTime -Descending | Select-Object -First 1 | Get-Date -UFormat %s -Millisecond 0`, | |
}) | |
if result.ok then | |
local psLastModified = tonumber(result.stdout) | |
if psLastModified and psLastModified > lastModified then | |
lastModified = psLastModified | |
end | |
end | |
return lastModified | |
end | |
local function watch(dir: string, callback: () -> ()) | |
local lastModified = powershellGetLastModified(dir) | |
local thread | |
thread = task.spawn(function() | |
while task.wait(POLL_INTERVAL) do | |
if not fs.isDir(dir) then | |
if thread then | |
task.cancel(thread) | |
end | |
return | |
end | |
local modifiedAt = powershellGetLastModified(dir) | |
if modifiedAt ~= lastModified then | |
lastModified = modifiedAt | |
callback() | |
end | |
end | |
end) | |
return function() | |
if thread then | |
task.cancel(thread) | |
end | |
end | |
end | |
local function setReactDevMode() | |
if not isDev then | |
return | |
end | |
local reactPath = "Packages/React.lua" | |
local hasReact = fs.isFile(reactPath) | |
if not hasReact then | |
return | |
end | |
local reactContent = fs.readFile(reactPath) | |
if reactContent:find("_G.__DEV__") then | |
print("> React is running in dev mode\n") | |
return | |
end | |
reactContent = `-- selene: allow(global_usage)\n_G.__DEV__ = true\n\n{reactContent}` | |
fs.writeFile(reactPath, reactContent) | |
print("> Enabled React's dev mode due to `--dev` flag\n") | |
end | |
local function main() | |
setReactDevMode() | |
runCommandSequence() | |
if not isWatch then | |
return | |
end | |
local _watchThreads = {} | |
local thread: thread? | |
for _, dir in WATCHED_DIRS do | |
if not fs.isDir(dir) then | |
continue | |
end | |
local watchThread = watch(dir, function() | |
if thread then | |
task.cancel(thread) | |
end | |
thread = task.spawn(runCommandSequence) | |
end) | |
table.insert(_watchThreads, watchThread) | |
end | |
end | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment