Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/local ci #8

Merged
merged 8 commits into from
May 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
globaltypes.d.lua
Packages
DevPackages
sourcemap.json
*sourcemap.json
settings.json
32 changes: 31 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,5 +146,35 @@ You need to set the `FFlagEnableLoadModule` value to `true`. Be sure to restart

| Method | Instructions |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| CLI (recommended) | `lune run runTests` |
| CLI (recommended) | `lune run test` |
| Roblox Studio | Open the test place file `StateMachine-Test.rbxl` [built in the above step](#build-the-project) in Roblox Studio and run the place (server only). The output widget will show the test results. |


### Continuous Integration (CI)

CI checks are set up to run on pull requests. These checks must pass before merging, including:

1. `lint` with selene
1. `format check` with StyLua
1. `analyze` with luau-lsp
1. `test` with jest running in Lune

#### Running CI Locally

To run the same CI checks locally that would run on GitHub, a number of Lune scripts are provided.

From the project directory, you can run the following:

> lune run ci

This will run all the same checks that would run on GitHub.

Alternatively, you can run individual steps yourself:

> lune run lint

> lune run formatCheck

> lune run analyze

> lune run test
2 changes: 1 addition & 1 deletion lune/Context/Debug.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
On Roblox, it's locked behind a feature flag FFlagDebugLoadModule, which is not enabled by default.
In Lune, it doesn't exist at all, so this module creates the interface for it but is not implemented.
The _loader function needs to be overridden to be usable.
In this project, the `lune/runTests.lua` script implements and sets the _loader function.
In this project, the `lune/test.lua` script implements and sets the _loader function.
--]]

local Debug
Expand Down
2 changes: 1 addition & 1 deletion lune/Utils/Runtime.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

--[[
Provides a connection to a loop that runs every frame. This is used
in the custom Heartbeat implementation in lune/runTests.lua
in the custom Heartbeat implementation in lune/test.lua
--]]

local task = require("@lune/task")
Expand Down
93 changes: 93 additions & 0 deletions lune/analyze.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
--!strict

--[[
Analyzes all the same paths that get analyzed in the CI pipeline.

Since the parent folder is named `lune`, the `lune` cli will automatically look in this directory for scripts to run.

Usage (from project directory):
lune run analyze
--]]

local process = require("@lune/process")
local stdio = require("@lune/stdio")
local task = require("@lune/task")

local ANALYZE_PATHS: { { path: string, project: string?, sourceMap: string? } } = {
{
path = "src/StateMachine",
project = "default.project.json",
sourceMap = "stateMachineSourcemap.json",
},
{
path = "src/TestService",
project = "test.project.json",
sourceMap = "testSourcemap.json",
},
{
path = "lune",
project = nil,
sourceMap = nil,
},
}

local NUM_EXPECTED_TASKS = #ANALYZE_PATHS

local numErrors = 0
local numTasksCompleted = 0
local mainThread = coroutine.running()

local function buildSourceMap(projectFilePath: string, sourceMapFilePath: string)
local proc = process.spawn("./scripts/sourcemap.sh", { projectFilePath, sourceMapFilePath })
print(proc.stdout)
assert(proc.ok, proc.stderr)
end

local function analyzePath(path: string, sourceMap: string?)
local proc
if sourceMap then
proc = process.spawn("./scripts/analyze.sh", { sourceMap, path })
else
proc = process.spawn("./scripts/analyze.sh", { path })
end

print(proc.stdout)
assert(proc.ok, proc.stderr)
end

local function analyzeAllPaths()
print(`Analyzing {NUM_EXPECTED_TASKS} paths:`)
for _, path in ipairs(ANALYZE_PATHS) do
task.spawn(function()
local success, errorMessage: string? = pcall(function()
if path.project and path.sourceMap then
buildSourceMap(path.project, path.sourceMap)
end
analyzePath(path.path, path.sourceMap)
end)

if not success then
stdio.ewrite(errorMessage :: string)
numErrors += 1
end

numTasksCompleted += 1
if numTasksCompleted == NUM_EXPECTED_TASKS then
coroutine.resume(mainThread)
end
end)
end

if numTasksCompleted < NUM_EXPECTED_TASKS then
coroutine.yield()
end

if numErrors > 0 then
stdio.ewrite(`{numErrors} of {NUM_EXPECTED_TASKS} paths FAILED analysis\n`)
process.exit(1)
end

stdio.write(`All ({NUM_EXPECTED_TASKS}) paths analyzed successfully\n`)
end

analyzeAllPaths()
62 changes: 62 additions & 0 deletions lune/ci.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
--!strict

--[[
Runs all the same checks that CI steps run, including analysis, format check, linting, and testing.

Since the parent folder is named `lune`, the `lune` cli will automatically look in this directory for scripts to run.

Usage (from project directory):
lune run ci
--]]

local process = require("@lune/process")
local stdio = require("@lune/stdio")
local task = require("@lune/task")

local LUNE_COMMANDS = {
"lint",
"formatCheck",
"analyze",
"test",
}

local NUM_EXPECTED_TASKS = #LUNE_COMMANDS

local numErrors = 0
local numTasksCompleted = 0
local mainThread = coroutine.running()

local function runLuneCommand(command: string)
local home = process.env.HOME
local proc = process.spawn(`{home}/.aftman/bin/lune`, { "run", command })
print(proc.stdout)
if not proc.ok then
stdio.ewrite(proc.stderr)
numErrors += 1
end

numTasksCompleted += 1
if numTasksCompleted == NUM_EXPECTED_TASKS then
coroutine.resume(mainThread)
end
end

local function runAllLuneCommands()
print(`Fixing format for {NUM_EXPECTED_TASKS} paths:`)
for _, path in ipairs(LUNE_COMMANDS) do
task.spawn(runLuneCommand, path)
end

if numTasksCompleted < NUM_EXPECTED_TASKS then
coroutine.yield()
end

if numErrors > 0 then
stdio.ewrite(`{numErrors} of {NUM_EXPECTED_TASKS} commands FAILED\n`)
process.exit(1)
end

stdio.write(`All ({NUM_EXPECTED_TASKS}) commands succeeded\n`)
end

runAllLuneCommands()
62 changes: 62 additions & 0 deletions lune/formatCheck.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
--!strict

--[[
Checks the format for all the same paths that get checked in the CI pipeline.
This is just a check that prints problems to the output.
To fix the format, run `lune run formatFix` instead.

Since the parent folder is named `lune`, the `lune` cli will automatically look in this directory for scripts to run.

Usage (from project directory):
lune run formatCheck
--]]

local process = require("@lune/process")
local stdio = require("@lune/stdio")
local task = require("@lune/task")

local FORMAT_PATHS = {
"src/StateMachine",
"src/TestService",
"lune",
}

local NUM_EXPECTED_TASKS = #FORMAT_PATHS

local numErrors = 0
local numTasksCompleted = 0
local mainThread = coroutine.running()

local function checkFormatForPath(path: string)
local proc = process.spawn("./scripts/formatCheck.sh", { path })
print(proc.stdout)
if not proc.ok then
stdio.ewrite(proc.stderr)
numErrors += 1
end

numTasksCompleted += 1
if numTasksCompleted == NUM_EXPECTED_TASKS then
coroutine.resume(mainThread)
end
end

local function formatCheckAllPaths()
print(`Checking format for {NUM_EXPECTED_TASKS} paths:`)
for _, path in ipairs(FORMAT_PATHS) do
task.spawn(checkFormatForPath, path)
end

if numTasksCompleted < NUM_EXPECTED_TASKS then
coroutine.yield()
end

if numErrors > 0 then
stdio.ewrite(`{numErrors} of {NUM_EXPECTED_TASKS} paths FAILED formatting checks\n`)
process.exit(1)
end

stdio.write(`All ({NUM_EXPECTED_TASKS}) paths passed format checks successfully\n`)
end

formatCheckAllPaths()
63 changes: 63 additions & 0 deletions lune/formatFix.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
--!strict

--[[
Fixes the format for all the same paths that get fixed in the CI pipeline.
This changes the files in place.
To just check the format, run `lune run formatCheck` instead.

Since the parent folder is named `lune`, the `lune` cli will automatically look in this directory for scripts to run.

Usage (from project directory):
lune run formatFix
--]]

local process = require("@lune/process")
local stdio = require("@lune/stdio")
local task = require("@lune/task")

local FORMAT_PATHS = {
"src/StateMachine",
"src/TestService",
"lune",
}

local NUM_EXPECTED_TASKS = #FORMAT_PATHS

local numErrors = 0
local numTasksCompleted = 0
local mainThread = coroutine.running()

local function fixFormatForPath(path: string)
local home = process.env.HOME
local proc = process.spawn(`{home}/.aftman/bin/stylua`, { path })
print(proc.stdout)
if not proc.ok then
stdio.ewrite(proc.stderr)
numErrors += 1
end

numTasksCompleted += 1
if numTasksCompleted == NUM_EXPECTED_TASKS then
coroutine.resume(mainThread)
end
end

local function formatFixAllPaths()
print(`Fixing format for {NUM_EXPECTED_TASKS} paths:`)
for _, path in ipairs(FORMAT_PATHS) do
task.spawn(fixFormatForPath, path)
end

if numTasksCompleted < NUM_EXPECTED_TASKS then
coroutine.yield()
end

if numErrors > 0 then
stdio.ewrite(`{numErrors} of {NUM_EXPECTED_TASKS} paths FAILED to get format fixed\n`)
process.exit(1)
end

stdio.write(`All ({NUM_EXPECTED_TASKS}) paths have fixed formats\n`)
end

formatFixAllPaths()
Loading