diff --git a/.emmyrc.json b/.emmyrc.json new file mode 100644 index 00000000..2d308c82 --- /dev/null +++ b/.emmyrc.json @@ -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" + ] + } +} diff --git a/.github/workflows/lua-lint.yml b/.github/workflows/lua-lint.yml new file mode 100644 index 00000000..35b51ad2 --- /dev/null +++ b/.github/workflows/lua-lint.yml @@ -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)
\(.message)"' \ + 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