@codacy/tools-sqlint-0 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +69 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +388 -0
- package/package.json +31 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Codacy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# @codacy/tools-sqlint-0
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
- [Overview](#overview)
|
|
6
|
+
- [Updating patterns](#updating-patterns)
|
|
7
|
+
- [Updating the SQLint version](#updating-the-sqlint-version)
|
|
8
|
+
- [Development](#development)
|
|
9
|
+
- [Notes for maintainers](#notes-for-maintainers)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Overview
|
|
14
|
+
|
|
15
|
+
SQL syntax checker using the [SQLint](https://github.com/purcell/sqlint) Ruby gem. Uses the CLI execution strategy -- spawns `sqlint` via `spawnTool()` and parses its text output.
|
|
16
|
+
|
|
17
|
+
| Property | Value |
|
|
18
|
+
| ------------- | -------------------------------------- |
|
|
19
|
+
| Tool ID | `SQLint` |
|
|
20
|
+
| Codacy UUID | `25b6766b-06c6-4625-9df1-561d28386b5f` |
|
|
21
|
+
| Strategy | CLI |
|
|
22
|
+
| Languages | SQL, PLSQL |
|
|
23
|
+
| Binary | `sqlint` (installed via `gem install`) |
|
|
24
|
+
| File patterns | `**/*.sql` |
|
|
25
|
+
|
|
26
|
+
## Updating patterns
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# Re-fetch pattern metadata from the Codacy API
|
|
30
|
+
pnpm prefetch
|
|
31
|
+
|
|
32
|
+
# Commit the result
|
|
33
|
+
git add src/patterns.json
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The `prefetch` script calls `packages/tools/scripts/fetch-patterns.mjs` with the tool UUID to download pattern metadata from the Codacy API.
|
|
37
|
+
|
|
38
|
+
SQLint has a single umbrella pattern: `SQLint_allIssues`. All syntax errors map to this one pattern.
|
|
39
|
+
|
|
40
|
+
## Updating the SQLint version
|
|
41
|
+
|
|
42
|
+
1. Update `SQLINT_VERSION` in `src/adapter.ts`
|
|
43
|
+
2. Run `pnpm prefetch` to check for any changes
|
|
44
|
+
3. Run `pnpm test` to verify compatibility
|
|
45
|
+
4. If the major version changes, create a new adapter package (`sqlint-1/`)
|
|
46
|
+
|
|
47
|
+
## Development
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pnpm build # Build with tsup
|
|
51
|
+
pnpm test # Run tests (requires ruby + sqlint in PATH for integration tests)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
To install sqlint locally for testing:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
gem install sqlint
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Notes for maintainers
|
|
61
|
+
|
|
62
|
+
- **PostgreSQL-only**: SQLint uses `pg_query` (libpg_query) under the hood, which means it only validates PostgreSQL dialect SQL. Non-PostgreSQL syntax may produce false positives.
|
|
63
|
+
- **Ruby gem dependency**: SQLint requires Ruby >= 2.7 as a runtime. The adapter uses `ensureRuby()` to download a portable Ruby if no system Ruby is found.
|
|
64
|
+
- **Native compilation**: The `pg_query` gem includes a native C extension that compiles libpg_query during installation. A C compiler (gcc/clang) and make are required on the system.
|
|
65
|
+
- Gems are installed in an isolated directory at `~/.codacy/runtimes/sqlint-0/gems/` via `GEM_HOME`/`GEM_PATH` environment variables.
|
|
66
|
+
- Output format is plain text, one finding per line: `file.sql:line:column:SEVERITY message`.
|
|
67
|
+
- Exit codes: 0 = no errors, 1 = errors found (both are success states for the adapter).
|
|
68
|
+
- SQLint has no configuration files -- it always checks all syntax.
|
|
69
|
+
- All findings map to the single umbrella pattern `SQLint_allIssues`.
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
default: () => index_default
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
var import_tooling2 = require("@codacy/tooling");
|
|
37
|
+
|
|
38
|
+
// src/adapter.ts
|
|
39
|
+
var import_tooling = require("@codacy/tooling");
|
|
40
|
+
var import_path = __toESM(require("path"));
|
|
41
|
+
var import_promises = __toESM(require("fs/promises"));
|
|
42
|
+
|
|
43
|
+
// src/patterns.json
|
|
44
|
+
var patterns_default = [
|
|
45
|
+
{
|
|
46
|
+
id: "SQLint_allIssues",
|
|
47
|
+
category: "Compatibility",
|
|
48
|
+
severityLevel: "High",
|
|
49
|
+
title: "Enforce ANSI SQL Syntax Compliance",
|
|
50
|
+
enabled: true,
|
|
51
|
+
parameters: []
|
|
52
|
+
}
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
// src/adapter.ts
|
|
56
|
+
var TOOL_ID = "SQLint";
|
|
57
|
+
var ADAPTER_ID = "sqlint-0";
|
|
58
|
+
var SQLINT_VERSION = "0.2.1";
|
|
59
|
+
var RUBY_VERSION_RANGE = ">=2.7.0";
|
|
60
|
+
var PATTERN_META = new Map(
|
|
61
|
+
patterns_default.map((p) => [
|
|
62
|
+
p.id,
|
|
63
|
+
{
|
|
64
|
+
category: p.category,
|
|
65
|
+
severity: p.severityLevel ?? "Info"
|
|
66
|
+
}
|
|
67
|
+
])
|
|
68
|
+
);
|
|
69
|
+
var UMBRELLA_PATTERN = "SQLint_allIssues";
|
|
70
|
+
function ruleMetadata() {
|
|
71
|
+
return PATTERN_META.get(UMBRELLA_PATTERN) ?? { category: "Compatibility", severity: "High" };
|
|
72
|
+
}
|
|
73
|
+
var OUTPUT_LINE_REGEX = /^(.+?):(\d+):(\d+):(ERROR|WARNING)\s+(.+)$/;
|
|
74
|
+
function getLineContent(lines, lineNumber) {
|
|
75
|
+
return lines[lineNumber - 1] ?? "";
|
|
76
|
+
}
|
|
77
|
+
async function readFileLines(filePath) {
|
|
78
|
+
try {
|
|
79
|
+
const content = await import_promises.default.readFile(filePath, "utf-8");
|
|
80
|
+
return content.split("\n");
|
|
81
|
+
} catch {
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function buildResult(startedAt, completedAt, issues, errors, filesAnalyzed) {
|
|
86
|
+
const durationMs = completedAt.getTime() - startedAt.getTime();
|
|
87
|
+
return {
|
|
88
|
+
metadata: {
|
|
89
|
+
startedAt: startedAt.toISOString(),
|
|
90
|
+
completedAt: completedAt.toISOString(),
|
|
91
|
+
durationMs
|
|
92
|
+
},
|
|
93
|
+
issues,
|
|
94
|
+
errors,
|
|
95
|
+
summary: {
|
|
96
|
+
toolId: TOOL_ID,
|
|
97
|
+
status: errors.some((e) => e.level === "error") ? "partial" : "success",
|
|
98
|
+
issueCount: issues.length,
|
|
99
|
+
errorCount: errors.length,
|
|
100
|
+
durationMs,
|
|
101
|
+
filesAnalyzed
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
async function resolveToolInvocation(ctx) {
|
|
106
|
+
const gemPaths = (0, import_tooling.getGemPaths)(ctx.globalDir, ADAPTER_ID);
|
|
107
|
+
const scriptPath = (0, import_tooling.resolveGemBinary)(gemPaths, "sqlint");
|
|
108
|
+
const portableRubyBin = (0, import_tooling.resolvePortableRubyBinary)(ctx.globalDir, "3.2.11");
|
|
109
|
+
try {
|
|
110
|
+
await import_promises.default.access(portableRubyBin);
|
|
111
|
+
await import_promises.default.access(scriptPath);
|
|
112
|
+
return { binary: portableRubyBin, prefixArgs: [scriptPath], rubyPath: portableRubyBin };
|
|
113
|
+
} catch {
|
|
114
|
+
}
|
|
115
|
+
const rubyInfo = await (0, import_tooling.findRuby)(RUBY_VERSION_RANGE).catch(() => null);
|
|
116
|
+
try {
|
|
117
|
+
await import_promises.default.access(scriptPath);
|
|
118
|
+
return {
|
|
119
|
+
binary: scriptPath,
|
|
120
|
+
prefixArgs: [],
|
|
121
|
+
rubyPath: rubyInfo?.path
|
|
122
|
+
};
|
|
123
|
+
} catch {
|
|
124
|
+
}
|
|
125
|
+
return { binary: "sqlint", prefixArgs: [], rubyPath: rubyInfo?.path };
|
|
126
|
+
}
|
|
127
|
+
var adapter = {
|
|
128
|
+
id: TOOL_ID,
|
|
129
|
+
displayName: "SQLint",
|
|
130
|
+
preferredVersion: SQLINT_VERSION,
|
|
131
|
+
compatibleVersions: ">=0.2.0",
|
|
132
|
+
website: "https://github.com/purcell/sqlint",
|
|
133
|
+
languages: ["SQL", "PLSQL"],
|
|
134
|
+
filePatterns: ["**/*.sql"],
|
|
135
|
+
supportedOS: ["linux", "darwin"],
|
|
136
|
+
supportedArch: ["x64", "arm64"],
|
|
137
|
+
supportedRunners: ["local", "container"],
|
|
138
|
+
prerequisites: [
|
|
139
|
+
{
|
|
140
|
+
name: "Ruby",
|
|
141
|
+
versionRange: RUBY_VERSION_RANGE,
|
|
142
|
+
preferredVersion: "3.2",
|
|
143
|
+
checkCommand: "ruby --version"
|
|
144
|
+
}
|
|
145
|
+
],
|
|
146
|
+
/**
|
|
147
|
+
* Checks whether SQLint is available (Ruby runtime + sqlint gem).
|
|
148
|
+
*
|
|
149
|
+
* Checks:
|
|
150
|
+
* 1. Ruby runtime via findRuby
|
|
151
|
+
* 2. sqlint binary in the Codacy-managed gem directory or on PATH
|
|
152
|
+
*/
|
|
153
|
+
async checkAvailability(ctx) {
|
|
154
|
+
ctx.logger.debug("Checking availability", { toolId: TOOL_ID });
|
|
155
|
+
const rubyInfo = await (0, import_tooling.findRuby)(RUBY_VERSION_RANGE, ctx.logger).catch(() => null);
|
|
156
|
+
let resolvedRubyPath = rubyInfo?.path;
|
|
157
|
+
const dependencies = rubyInfo ? [{ name: "Ruby", available: true, version: rubyInfo.version, installation: "global" }] : [{ name: "Ruby", available: false, reason: `Ruby (${RUBY_VERSION_RANGE}) not found` }];
|
|
158
|
+
if (!rubyInfo) {
|
|
159
|
+
const rubyBin = (0, import_tooling.resolvePortableRubyBinary)(ctx.globalDir, "3.2.11");
|
|
160
|
+
try {
|
|
161
|
+
await import_promises.default.access(rubyBin);
|
|
162
|
+
resolvedRubyPath = rubyBin;
|
|
163
|
+
dependencies[0] = {
|
|
164
|
+
name: "Ruby",
|
|
165
|
+
available: true,
|
|
166
|
+
version: "3.2.11",
|
|
167
|
+
installation: "codacy"
|
|
168
|
+
};
|
|
169
|
+
} catch {
|
|
170
|
+
return {
|
|
171
|
+
available: false,
|
|
172
|
+
reason: "Ruby runtime not found (required for SQLint)",
|
|
173
|
+
installHint: "Install Ruby 2.7+, or run with --install-dependencies for auto-download",
|
|
174
|
+
dependencies
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const gemPaths = (0, import_tooling.getGemPaths)(ctx.globalDir, ADAPTER_ID);
|
|
179
|
+
const sqlintVersion = await (0, import_tooling.checkGemTool)(
|
|
180
|
+
gemPaths,
|
|
181
|
+
"sqlint",
|
|
182
|
+
"--version",
|
|
183
|
+
/(\d+\.\d+\.\d+)/,
|
|
184
|
+
resolvedRubyPath,
|
|
185
|
+
ctx.logger
|
|
186
|
+
);
|
|
187
|
+
if (sqlintVersion) {
|
|
188
|
+
return {
|
|
189
|
+
available: true,
|
|
190
|
+
version: sqlintVersion,
|
|
191
|
+
path: (0, import_tooling.resolveGemBinary)(gemPaths, "sqlint"),
|
|
192
|
+
installation: "codacy",
|
|
193
|
+
dependencies
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
const { stdout, exitCode } = await (0, import_tooling.spawnTool)("sqlint", ["--version"], {
|
|
198
|
+
cwd: ctx.repositoryRoot,
|
|
199
|
+
logger: ctx.logger
|
|
200
|
+
});
|
|
201
|
+
if (exitCode === 0) {
|
|
202
|
+
const match = stdout.match(/(\d+\.\d+\.\d+)/);
|
|
203
|
+
if (match) {
|
|
204
|
+
return {
|
|
205
|
+
available: true,
|
|
206
|
+
version: match[1],
|
|
207
|
+
installation: "global",
|
|
208
|
+
dependencies
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
} catch {
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
available: false,
|
|
216
|
+
reason: "SQLint not found in Codacy gem directory or on PATH",
|
|
217
|
+
installHint: "Run with --install-dependencies to auto-install, or run: gem install sqlint",
|
|
218
|
+
dependencies
|
|
219
|
+
};
|
|
220
|
+
},
|
|
221
|
+
/**
|
|
222
|
+
* Installs SQLint by ensuring a Ruby runtime is available and installing
|
|
223
|
+
* the sqlint gem into an isolated gem directory.
|
|
224
|
+
*
|
|
225
|
+
* Note: The sqlint gem depends on pg_query, which includes a native C
|
|
226
|
+
* extension. A C compiler is required on the system for installation.
|
|
227
|
+
*/
|
|
228
|
+
async install(ctx) {
|
|
229
|
+
ctx.logger.info("Starting install", { toolId: TOOL_ID });
|
|
230
|
+
const ruby = await (0, import_tooling.ensureRuby)(ctx.globalDir, RUBY_VERSION_RANGE, ctx.logger);
|
|
231
|
+
if (!ruby) {
|
|
232
|
+
return {
|
|
233
|
+
success: false,
|
|
234
|
+
error: {
|
|
235
|
+
toolId: TOOL_ID,
|
|
236
|
+
phase: "requirementCheck",
|
|
237
|
+
kind: "MissingPrerequisite",
|
|
238
|
+
message: `Ruby (${RUBY_VERSION_RANGE}) could not be found or downloaded. SQLint requires a Ruby runtime.`,
|
|
239
|
+
level: "error"
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
try {
|
|
244
|
+
const gemPaths = (0, import_tooling.getGemPaths)(ctx.globalDir, ADAPTER_ID);
|
|
245
|
+
ctx.logger.info(`Installing SQLint ${SQLINT_VERSION} to ${gemPaths.gemHome}...`);
|
|
246
|
+
const prevConfigArgs = process.env.CONFIGURE_ARGS;
|
|
247
|
+
process.env.CONFIGURE_ARGS = `${prevConfigArgs ?? ""} --with-cflags=-DHAVE_STRCHRNUL`.trim();
|
|
248
|
+
try {
|
|
249
|
+
await (0, import_tooling.gemInstall)(gemPaths, ruby.path, ["google-protobuf:3.25.5"], ctx.logger);
|
|
250
|
+
await (0, import_tooling.gemInstall)(gemPaths, ruby.path, [`sqlint:${SQLINT_VERSION}`], ctx.logger);
|
|
251
|
+
} finally {
|
|
252
|
+
if (prevConfigArgs === void 0) {
|
|
253
|
+
delete process.env.CONFIGURE_ARGS;
|
|
254
|
+
} else {
|
|
255
|
+
process.env.CONFIGURE_ARGS = prevConfigArgs;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
const installedPath = (0, import_tooling.resolveGemBinary)(gemPaths, "sqlint");
|
|
259
|
+
ctx.logger.info(`Installed SQLint to ${installedPath}`);
|
|
260
|
+
return { success: true, installedPath, installedVersion: SQLINT_VERSION };
|
|
261
|
+
} catch (err) {
|
|
262
|
+
return {
|
|
263
|
+
success: false,
|
|
264
|
+
error: {
|
|
265
|
+
toolId: TOOL_ID,
|
|
266
|
+
phase: "toolInstall",
|
|
267
|
+
kind: "InstallError",
|
|
268
|
+
message: `Failed to install SQLint: ${err instanceof Error ? err.message : String(err)}`,
|
|
269
|
+
level: "error"
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
/**
|
|
275
|
+
* SQLint has no configuration files -- it is a purely syntax-based checker.
|
|
276
|
+
* Always returns { found: false }.
|
|
277
|
+
*/
|
|
278
|
+
async checkLocalConfigurationFile(_ctx) {
|
|
279
|
+
return { found: false };
|
|
280
|
+
},
|
|
281
|
+
/**
|
|
282
|
+
* Runs `sqlint` against the target SQL files and converts the text output
|
|
283
|
+
* into Codacy Issue objects.
|
|
284
|
+
*
|
|
285
|
+
* SQLint has no config generation and no rule selection -- it is a single
|
|
286
|
+
* umbrella pattern (`SQLint_allIssues`) that reports all syntax errors.
|
|
287
|
+
*
|
|
288
|
+
* Output format: one finding per line in the format:
|
|
289
|
+
* file.sql:line:column:SEVERITY message
|
|
290
|
+
*
|
|
291
|
+
* Exit codes:
|
|
292
|
+
* - 0: no errors found
|
|
293
|
+
* - 1: errors found (normal -- not treated as an error)
|
|
294
|
+
*
|
|
295
|
+
* Never throws -- all errors are captured in the returned `errors` array.
|
|
296
|
+
*/
|
|
297
|
+
async analyze(ctx, codacyConfig) {
|
|
298
|
+
const startMs = Date.now();
|
|
299
|
+
ctx.logger.info("Starting analysis", { toolId: TOOL_ID, fileCount: ctx.targetFiles.length });
|
|
300
|
+
const startedAt = /* @__PURE__ */ new Date();
|
|
301
|
+
const issues = [];
|
|
302
|
+
const errors = [];
|
|
303
|
+
if (ctx.targetFiles.length === 0) {
|
|
304
|
+
return buildResult(startedAt, /* @__PURE__ */ new Date(), issues, errors, 0);
|
|
305
|
+
}
|
|
306
|
+
const enabledPatterns = new Set(codacyConfig.patterns.map((p) => p.patternId));
|
|
307
|
+
if (!codacyConfig.useLocalConfigurationFile && codacyConfig.patterns.length === 0) {
|
|
308
|
+
ctx.logger.info("Skipping analysis: no patterns enabled", { toolId: TOOL_ID });
|
|
309
|
+
return buildResult(startedAt, /* @__PURE__ */ new Date(), issues, errors, ctx.targetFiles.length);
|
|
310
|
+
}
|
|
311
|
+
const invocation = await resolveToolInvocation(ctx);
|
|
312
|
+
const absoluteFiles = ctx.targetFiles.map((f) => import_path.default.resolve(ctx.repositoryRoot, f));
|
|
313
|
+
const args = [...invocation.prefixArgs, ...absoluteFiles];
|
|
314
|
+
try {
|
|
315
|
+
const gemPaths = (0, import_tooling.getGemPaths)(ctx.globalDir, ADAPTER_ID);
|
|
316
|
+
const env = (0, import_tooling.buildGemEnv)(gemPaths, invocation.rubyPath);
|
|
317
|
+
const { stdout, stderr, exitCode } = await (0, import_tooling.spawnTool)(invocation.binary, args, {
|
|
318
|
+
cwd: ctx.repositoryRoot,
|
|
319
|
+
env,
|
|
320
|
+
logger: ctx.logger
|
|
321
|
+
});
|
|
322
|
+
if (exitCode !== 0 && exitCode !== 1) {
|
|
323
|
+
errors.push({
|
|
324
|
+
toolId: TOOL_ID,
|
|
325
|
+
phase: "toolInvoke",
|
|
326
|
+
kind: "NonZeroExit",
|
|
327
|
+
message: `sqlint exited with code ${exitCode}${stderr ? `: ${stderr.trim()}` : ""}`,
|
|
328
|
+
level: "error"
|
|
329
|
+
});
|
|
330
|
+
return buildResult(startedAt, /* @__PURE__ */ new Date(), issues, errors, ctx.targetFiles.length);
|
|
331
|
+
}
|
|
332
|
+
const output = stdout + (stderr || "");
|
|
333
|
+
const outputLines = output.split("\n").filter((l) => l.trim());
|
|
334
|
+
const fileLineCache = /* @__PURE__ */ new Map();
|
|
335
|
+
const meta = ruleMetadata();
|
|
336
|
+
for (const outputLine of outputLines) {
|
|
337
|
+
const match = OUTPUT_LINE_REGEX.exec(outputLine);
|
|
338
|
+
if (!match) continue;
|
|
339
|
+
const [, rawFilePath, lineStr, colStr, , message] = match;
|
|
340
|
+
const line = parseInt(lineStr, 10);
|
|
341
|
+
const column = parseInt(colStr, 10);
|
|
342
|
+
let filePath = rawFilePath;
|
|
343
|
+
if (import_path.default.isAbsolute(filePath)) {
|
|
344
|
+
filePath = import_path.default.relative(ctx.repositoryRoot, filePath);
|
|
345
|
+
}
|
|
346
|
+
if (enabledPatterns.size > 0 && !enabledPatterns.has(UMBRELLA_PATTERN)) {
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
const absPath = import_path.default.resolve(ctx.repositoryRoot, filePath);
|
|
350
|
+
if (!fileLineCache.has(absPath)) {
|
|
351
|
+
fileLineCache.set(absPath, await readFileLines(absPath));
|
|
352
|
+
}
|
|
353
|
+
const fileLines = fileLineCache.get(absPath);
|
|
354
|
+
issues.push({
|
|
355
|
+
filePath,
|
|
356
|
+
patternId: UMBRELLA_PATTERN,
|
|
357
|
+
toolId: TOOL_ID,
|
|
358
|
+
line,
|
|
359
|
+
column,
|
|
360
|
+
message,
|
|
361
|
+
lineContent: getLineContent(fileLines, line),
|
|
362
|
+
multilineContent: (0, import_tooling.getMultilineContent)(fileLines, line, void 0),
|
|
363
|
+
category: meta.category,
|
|
364
|
+
severity: meta.severity
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
} catch (invokeErr) {
|
|
368
|
+
errors.push({
|
|
369
|
+
toolId: TOOL_ID,
|
|
370
|
+
phase: "toolInvoke",
|
|
371
|
+
kind: "InvocationError",
|
|
372
|
+
message: `Failed to run SQLint: ${invokeErr instanceof Error ? invokeErr.message : String(invokeErr)}`,
|
|
373
|
+
level: "error"
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
const durationMs = Date.now() - startMs;
|
|
377
|
+
ctx.logger.info("Analysis complete", {
|
|
378
|
+
toolId: TOOL_ID,
|
|
379
|
+
issueCount: issues.length,
|
|
380
|
+
errorCount: errors.length,
|
|
381
|
+
durationMs
|
|
382
|
+
});
|
|
383
|
+
return buildResult(startedAt, /* @__PURE__ */ new Date(), issues, errors, ctx.targetFiles.length);
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
// src/index.ts
|
|
388
|
+
var index_default = (0, import_tooling2.defineToolAdapter)(adapter);
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@codacy/tools-sqlint-0",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "SQLint adapter — CLI-mode SQL syntax checker",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@codacy/tooling": "0.3.0"
|
|
12
|
+
},
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"author": "Codacy <support@codacy.com> (https://www.codacy.com)",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/codacy/analysis-cli.git",
|
|
18
|
+
"directory": "packages/tools/sqlint-0"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://github.com/codacy/analysis-cli",
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=20.0.0"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsup src/index.ts --format cjs --dts --clean",
|
|
26
|
+
"test": "vitest run",
|
|
27
|
+
"lint": "eslint src/",
|
|
28
|
+
"clean": "rm -rf dist",
|
|
29
|
+
"prefetch": "node ../scripts/fetch-patterns.mjs 25b6766b-06c6-4625-9df1-561d28386b5f src/patterns.json"
|
|
30
|
+
}
|
|
31
|
+
}
|