mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-11 15:04:52 +09:00
Merge pull request #979 from bbassie/feature/emmylua-lint
Add emmylua lint CI for module Lua
This commit is contained in:
11 files changed
+175
-19
No files matched your search
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/EmmyLuaLs/emmylua-analyzer-rust/refs/heads/main/crates/emmylua_code_analysis/resources/schema.json",
|
||||
"runtime": {
|
||||
"version": "Lua5.2",
|
||||
"requirePattern": [ "?", "?.lua" ]
|
||||
},
|
||||
"workspace": {
|
||||
"$comment-moduleMap": "Rewrites each file's own module path rather than the require string",
|
||||
"$comment-library": "The factorio typedefs are provided by a second config, generated with fmtk luals-addon in ci",
|
||||
"$comment-templates": "The create templates contain __plugin_name__ placeholders and are not valid lua",
|
||||
"ignoreGlobs": [
|
||||
"**/node_modules/**",
|
||||
"**/dist/**",
|
||||
"database/**",
|
||||
"docs/**",
|
||||
"external_plugins/**",
|
||||
"factorio/**",
|
||||
"instances/**",
|
||||
"logs/**",
|
||||
"mods/**",
|
||||
"modules/**",
|
||||
"temp/**",
|
||||
"packages/create/templates/**"
|
||||
],
|
||||
"moduleMap": [
|
||||
{ "pattern": "^packages\\.host\\.modules\\.(.+)$", "replace": "modules.$1" },
|
||||
{ "pattern": "^plugins\\.([^.]+)\\.module\\.(.+)$", "replace": "modules.$1.$2" },
|
||||
{ "pattern": "^packages\\.host\\.lua\\.export\\.(.+)$", "replace": "$1" },
|
||||
{ "pattern": "^packages\\.host\\.lua\\.clusterio_lib\\.(.+)$", "replace": "$1" }
|
||||
]
|
||||
},
|
||||
"doc": {
|
||||
"privateName": [ "__*" ]
|
||||
},
|
||||
"$comment-disable": "Same set as ExpCluster, these are editor level hints rather than ci checks",
|
||||
"$comment-unnecessary-if": "Disabled by the config fmtk generates for itself, the api union types make it unreliable",
|
||||
"diagnostics": {
|
||||
"globals": [ "__DebugAdapter", "__Profiler" ],
|
||||
"disable": [
|
||||
"assign-type-mismatch",
|
||||
"param-type-mismatch",
|
||||
"preferred-local-alias",
|
||||
"unnecessary-assert",
|
||||
"unnecessary-if",
|
||||
"unused"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
name: Lua Lint
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
EMMYLUA_CHECK_VERSION: "0.24.0"
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/*
|
||||
- name: Generate Factorio typedefs
|
||||
run: |
|
||||
npm install --prefix "$RUNNER_TEMP/fmtk" factoriomod-debug
|
||||
"$RUNNER_TEMP/fmtk/node_modules/.bin/fmtk" luals-addon "$RUNNER_TEMP"
|
||||
- name: Install emmylua_check
|
||||
run: |
|
||||
curl -fsSL "https://github.com/EmmyLuaLs/emmylua-analyzer-rust/releases/download/${EMMYLUA_CHECK_VERSION}/emmylua_check-linux-x64.tar.gz" \
|
||||
| tar -xz -C "$RUNNER_TEMP"
|
||||
- name: Run Lint Report
|
||||
shell: bash
|
||||
run: |
|
||||
# The typedefs are generated per run, so their path is passed via a second config
|
||||
jq -n --arg lib "$RUNNER_TEMP/factorio/library" \
|
||||
'{ workspace: { library: [ $lib ] } }' \
|
||||
> "$RUNNER_TEMP/ci.emmyrc.json"
|
||||
|
||||
# Exits non zero on error severity, so the gate below is used instead
|
||||
"$RUNNER_TEMP/emmylua_check" . --config ".emmyrc.json,$RUNNER_TEMP/ci.emmyrc.json" \
|
||||
--output-format json --output check.json || true
|
||||
|
||||
# Credit to https://github.com/Krealle/luals-check-action/blob/main/action.yml
|
||||
# Although some minor fixes were needed
|
||||
|
||||
# Format and print the messages (would be nice to format as a table, but that's too complex for jq)
|
||||
jq -r --arg root "$PWD/" \
|
||||
'.[] | (.file | ltrimstr($root)) as $file | .diagnostics[] |
|
||||
{
|
||||
file: $file,
|
||||
code: .code,
|
||||
line: .range.start.line,
|
||||
message: (.message | split("\n")
|
||||
| map(if startswith("- ") then .[2:] else . end)
|
||||
| join("\n")),
|
||||
severity: (if .severity <= 1 then "ERR" else "WARN" end)
|
||||
} |
|
||||
"**[\(.severity)] \(.code):** \(.file)#L\(.line + 1)<br><code>\(.message)</code>"' \
|
||||
check.json > "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
echo ""
|
||||
|
||||
# Github Annotations (limited to 10)
|
||||
jq -r --arg root "$PWD/" \
|
||||
'.[] | (.file | ltrimstr($root)) as $file | .diagnostics[] |
|
||||
{
|
||||
file: $file,
|
||||
title: .code,
|
||||
line: .range.start.line,
|
||||
endLine: .range.end.line,
|
||||
col: .range.start.character,
|
||||
endColumn: .range.end.character,
|
||||
message: (.message | gsub("\n"; "%0A"; "m")),
|
||||
level: (if .severity <= 1 then "error" else "warning" end)
|
||||
} |
|
||||
"::\(.level) file=\(.file),line=\(.line + 1),endLine=\(.endLine + 1),col=\(.col + 1),endColumn=\(.endColumn + 1),title=\(.title)::\(.message) (\(.title))"' \
|
||||
check.json
|
||||
|
||||
# An entry is emitted for every file checked, including clean ones, so
|
||||
# the gate counts diagnostics and asserts the workspace was actually read
|
||||
if [[ $(jq 'length' check.json) -lt 20 ]] ; then
|
||||
echo "::error::Only $(jq 'length' check.json) files were checked, expected the whole workspace"
|
||||
exit 1
|
||||
elif [[ $(jq '[.[].diagnostics[]] | length' check.json) -gt 0 ]] ; then
|
||||
exit 1
|
||||
else
|
||||
echo "✅ All checks succeeded!" | tee "$GITHUB_STEP_SUMMARY"
|
||||
fi
|
||||
@@ -38,6 +38,7 @@ node_modules
|
||||
/config-*.json.lock
|
||||
pnpm-lock.yaml
|
||||
mod_cache.json
|
||||
.luarc.json
|
||||
/static/
|
||||
saves
|
||||
/temp/
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
--- Undefined fields are expected, the typedefs only cover the latest version
|
||||
--- @diagnostic disable: undefined-field
|
||||
|
||||
local function send_json(channel, data)
|
||||
data = helpers and helpers.table_to_json(data) or game.table_to_json(data)
|
||||
print("\f$ipc:" .. channel .. "?j" .. data)
|
||||
|
||||
@@ -10,7 +10,8 @@ Implementation Guidelines
|
||||
|
||||
local lib_json = require("json")
|
||||
|
||||
--- @diagnostic disable: deprecated
|
||||
--- Undefined fields are expected, the typedefs only cover the latest version
|
||||
--- @diagnostic disable: deprecated, undefined-field
|
||||
|
||||
--- @class LibCompat
|
||||
--- @field script_data table
|
||||
@@ -238,4 +239,6 @@ set_script_data()
|
||||
compat.on_init = set_script_data --- @package
|
||||
compat.on_load = set_script_data --- @package
|
||||
|
||||
return setmetatable(compat, compat_mt)
|
||||
-- Not returned directly because that would infer the metatable type rather than LibCompat
|
||||
setmetatable(compat, compat_mt)
|
||||
return compat
|
||||
@@ -22,6 +22,9 @@
|
||||
-- SOFTWARE.
|
||||
--
|
||||
|
||||
--- Vendored file, the forward declared recursion is not worth annotating
|
||||
--- @diagnostic disable: need-check-nil
|
||||
|
||||
local json = { _version = "0.1.2" }
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
@@ -21,9 +21,10 @@ return function(player, record)
|
||||
player.driving = true
|
||||
|
||||
-- Teleport to safe location if unable to enter vehicle
|
||||
if not player.driving and player.controller_type == defines.controllers.character then
|
||||
local character = player.character
|
||||
if not player.driving and character and player.controller_type == defines.controllers.character then
|
||||
local safe_position = record.vehicle.surface.find_non_colliding_position(
|
||||
player.character.name, player.position, 32, 1/8
|
||||
character.name, player.position, 32, 1/8
|
||||
)
|
||||
if safe_position then
|
||||
player.teleport(safe_position, player.surface)
|
||||
|
||||
@@ -207,14 +207,14 @@ end
|
||||
--- @return QuickBarSlotEncoded
|
||||
local function serialize_filter(filter)
|
||||
if filter.quality == "normal" and filter.comparator == "=" then
|
||||
return filter.name
|
||||
return filter.name --[[@as string]]
|
||||
end
|
||||
|
||||
return {
|
||||
t = "f",
|
||||
n = filter.name,
|
||||
q = filter.quality,
|
||||
c = filter.comparator,
|
||||
n = filter.name --[[@as string]],
|
||||
q = filter.quality --[[@as string]],
|
||||
c = filter.comparator --[[@as string]],
|
||||
}
|
||||
end
|
||||
|
||||
@@ -234,7 +234,7 @@ function serialize.serialize_quick_bar_slot(slot)
|
||||
|
||||
if v2_0_quick_bar_api then
|
||||
-- 2.0 gives us an item filter which supports quality
|
||||
--- @cast slot ItemFilter
|
||||
--- @cast slot -QuickBarSlot, -LuaItemPrototype
|
||||
return serialize_filter(slot)
|
||||
end
|
||||
|
||||
@@ -267,7 +267,7 @@ function serialize.deserialize_quick_bar_slot(entry)
|
||||
quality = entry.q,
|
||||
comparator = entry.c,
|
||||
},
|
||||
}
|
||||
} --[[@as QuickBarSlot]]
|
||||
end
|
||||
|
||||
print("Warning: Unsupported serialized quick bar slot type '" .. tostring(entry.t) .. "'")
|
||||
@@ -292,7 +292,7 @@ function serialize.deserialize_quick_bar_slot(entry)
|
||||
|
||||
-- Return a string (member of ItemPrototypeIdentification)
|
||||
-- Pre 2.0 this was the only method of encoding used
|
||||
return entry
|
||||
return entry --[[@as string]]
|
||||
end
|
||||
|
||||
--- @param player LuaPlayer
|
||||
@@ -709,7 +709,8 @@ local function ensure_character(player)
|
||||
local surface = player.surface
|
||||
if v2_space_platform and surface.platform then
|
||||
local planet_surface = find_planet_surface(surface.platform)
|
||||
player.teleport(player.force.get_spawn_position(planet_surface), planet_surface)
|
||||
local force = player.force --[[@as LuaForce]]
|
||||
player.teleport(force.get_spawn_position(planet_surface), planet_surface)
|
||||
end
|
||||
|
||||
-- Create and return the character
|
||||
@@ -721,7 +722,7 @@ local function ensure_character(player)
|
||||
player.surface.name, tostring(player.connected), tostring(player.driving)
|
||||
))
|
||||
end
|
||||
return player.character
|
||||
return assert(player.character)
|
||||
end
|
||||
|
||||
--- @class FailedDeserializationPlayerData
|
||||
|
||||
@@ -132,6 +132,7 @@ function auth.add_commands()
|
||||
end
|
||||
|
||||
if event.parameter then
|
||||
--- @type string[]
|
||||
local args = {}
|
||||
for w in string.gmatch(event.parameter, "[^ ]+") do
|
||||
args[#args + 1] = w
|
||||
@@ -149,7 +150,7 @@ function auth.add_commands()
|
||||
end
|
||||
|
||||
local player_name, url, code = table.unpack(args)
|
||||
local player = game.players[player_name]
|
||||
local player = game.get_player(player_name)
|
||||
if not player then
|
||||
rcon.print("Player " .. player_name .. " does not exist")
|
||||
return
|
||||
@@ -159,7 +160,7 @@ function auth.add_commands()
|
||||
|
||||
elseif command == "code_set" then
|
||||
local player_name = args[1]
|
||||
local player = game.players[player_name]
|
||||
local player = game.get_player(player_name)
|
||||
if not player then
|
||||
rcon.print("Player " .. player_name .. " does not exist")
|
||||
return
|
||||
@@ -171,7 +172,7 @@ function auth.add_commands()
|
||||
|
||||
elseif command == "error" then
|
||||
local player_name = table.remove(args, 1)
|
||||
local player = game.players[player_name]
|
||||
local player = game.get_player(player_name)
|
||||
if not player then
|
||||
rcon.print("Player " .. player_name .. " does not exist")
|
||||
return
|
||||
@@ -192,10 +193,12 @@ auth.events[defines.events.on_gui_click] = function(event)
|
||||
end
|
||||
|
||||
if event.element.name == "player_auth_dialog_close_button" then
|
||||
event.element.parent.parent.destroy()
|
||||
local parent = assert(event.element.parent)
|
||||
assert(parent.parent).destroy()
|
||||
|
||||
elseif event.element.name == "player_auth_verify_code_button" then
|
||||
local verify_code = event.element.parent.player_auth_verify_code_input.text
|
||||
local parent = assert(event.element.parent)
|
||||
local verify_code = assert(parent.player_auth_verify_code_input).text
|
||||
local player_name = game.players[event.player_index].name
|
||||
clusterio_api.send_json("player_auth", {
|
||||
type = "set_verify_code",
|
||||
|
||||
@@ -30,8 +30,17 @@ local function set_technology_progress(tech, progress)
|
||||
end
|
||||
end
|
||||
|
||||
--- @class ResearchSyncTechnology
|
||||
--- @field level uint
|
||||
--- @field researched boolean?
|
||||
--- @field progress double?
|
||||
|
||||
--- @class ResearchSyncData
|
||||
--- @field technologies table<string, ResearchSyncTechnology>
|
||||
--- @field ignore_research_finished boolean
|
||||
|
||||
--- @param no_early_return boolean?
|
||||
--- @return table
|
||||
--- @return ResearchSyncData
|
||||
local function get_script_data(no_early_return)
|
||||
local research_sync = compat.script_data.research_sync
|
||||
if research_sync and not no_early_return then
|
||||
|
||||
@@ -32,6 +32,7 @@ function statistics_exporter.export()
|
||||
for _, force in pairs(game.forces) do
|
||||
local flow_statistics = {}
|
||||
for _, statName in pairs(statistics) do
|
||||
--- @type LuaFlowStatistics
|
||||
local stat = v2_stats and force["get_" .. statName](surface.index) or force[statName]
|
||||
flow_statistics[statName] = {
|
||||
input = stat.input_counts,
|
||||
|
||||
Reference in new issue
Block a user