@pgsql/lint 18.2.0 → 18.2.1

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/README.md CHANGED
@@ -62,8 +62,11 @@ request's base branch (`$GITHUB_BASE_REF`) and otherwise to the repository's
62
62
  default branch; the diff is taken against `git merge-base HEAD <base>`, so
63
63
  commits landed on the base branch afterwards don't widen the set. Uncommitted and
64
64
  untracked changes are included, deleted/renamed-away paths are dropped, and a
65
- shallow clone or detached checkout (no resolvable merge base) falls back to the
66
- working-tree diff against `HEAD`. Nothing changed is an exit-0 pass.
65
+ shallow clone or detached checkout (no resolvable base) falls back to working-tree
66
+ changes only. Nothing changed is an exit-0 pass.
67
+
68
+ Detection is [`git-changed`](https://npmjs.com/package/git-changed), shared with
69
+ `pgpm package --check`, so the two agree on what "changed" means.
67
70
 
68
71
  ### Config file
69
72
 
package/changed.d.ts CHANGED
@@ -2,12 +2,12 @@
2
2
  * Changed-file detection for `--changed`, so a CI gate (or a human before a
3
3
  * commit) pays only for the SQL that a branch actually touched.
4
4
  *
5
- * Modelled on pgpm's bundle-drift check (`pgpm/core/src/packaging/check.ts` in
6
- * constructive): resolve a base ref (explicit → `origin/$GITHUB_BASE_REF` in a
7
- * PR → the repository's default branch), diff `HEAD` against the **merge base**
8
- * so unrelated commits on the base branch don't widen the set, and union that
9
- * with uncommitted/untracked working-tree changes. Deleted paths are dropped —
10
- * there is nothing left on disk to lint.
5
+ * The git plumbing — base resolution, merge-base diff, working-tree union,
6
+ * rename targets, dropping paths that no longer exist — lives in `git-changed`,
7
+ * which is shared with pgpm's bundle-drift check. This module is the `.sql`
8
+ * filter over it, and the place where "no base" stays non-fatal: a lint gate
9
+ * that refuses to run on a shallow clone lints nothing, which is worse than
10
+ * linting the working tree.
11
11
  */
12
12
  /**
13
13
  * Resolve the ref to diff against. An explicit `base` wins; otherwise the PR
@@ -26,9 +26,7 @@ export interface ChangedFilesResult {
26
26
  }
27
27
  /**
28
28
  * Collect the files that differ from `base` (via `git merge-base`) plus any
29
- * uncommitted/untracked working-tree changes. Falls back to `git diff HEAD`
30
- * when no base is resolvable or no merge base exists — a shallow clone or a
31
- * detached CI checkout — rather than failing the run.
29
+ * uncommitted/untracked working-tree changes.
32
30
  */
33
31
  export declare function changedFiles(options?: {
34
32
  cwd?: string;
package/changed.js CHANGED
@@ -3,80 +3,18 @@
3
3
  * Changed-file detection for `--changed`, so a CI gate (or a human before a
4
4
  * commit) pays only for the SQL that a branch actually touched.
5
5
  *
6
- * Modelled on pgpm's bundle-drift check (`pgpm/core/src/packaging/check.ts` in
7
- * constructive): resolve a base ref (explicit → `origin/$GITHUB_BASE_REF` in a
8
- * PR → the repository's default branch), diff `HEAD` against the **merge base**
9
- * so unrelated commits on the base branch don't widen the set, and union that
10
- * with uncommitted/untracked working-tree changes. Deleted paths are dropped —
11
- * there is nothing left on disk to lint.
6
+ * The git plumbing — base resolution, merge-base diff, working-tree union,
7
+ * rename targets, dropping paths that no longer exist — lives in `git-changed`,
8
+ * which is shared with pgpm's bundle-drift check. This module is the `.sql`
9
+ * filter over it, and the place where "no base" stays non-fatal: a lint gate
10
+ * that refuses to run on a shallow clone lints nothing, which is worse than
11
+ * linting the working tree.
12
12
  */
13
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
- if (k2 === undefined) k2 = k;
15
- var desc = Object.getOwnPropertyDescriptor(m, k);
16
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
17
- desc = { enumerable: true, get: function() { return m[k]; } };
18
- }
19
- Object.defineProperty(o, k2, desc);
20
- }) : (function(o, m, k, k2) {
21
- if (k2 === undefined) k2 = k;
22
- o[k2] = m[k];
23
- }));
24
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
25
- Object.defineProperty(o, "default", { enumerable: true, value: v });
26
- }) : function(o, v) {
27
- o["default"] = v;
28
- });
29
- var __importStar = (this && this.__importStar) || (function () {
30
- var ownKeys = function(o) {
31
- ownKeys = Object.getOwnPropertyNames || function (o) {
32
- var ar = [];
33
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
34
- return ar;
35
- };
36
- return ownKeys(o);
37
- };
38
- return function (mod) {
39
- if (mod && mod.__esModule) return mod;
40
- var result = {};
41
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
42
- __setModuleDefault(result, mod);
43
- return result;
44
- };
45
- })();
46
13
  Object.defineProperty(exports, "__esModule", { value: true });
47
14
  exports.resolveChangedBase = resolveChangedBase;
48
15
  exports.changedFiles = changedFiles;
49
16
  exports.changedSqlFiles = changedSqlFiles;
50
- const child_process_1 = require("child_process");
51
- const fs_1 = require("fs");
52
- const path = __importStar(require("path"));
53
- function git(args, cwd) {
54
- return (0, child_process_1.execFileSync)('git', args, {
55
- cwd,
56
- encoding: 'utf-8',
57
- stdio: ['ignore', 'pipe', 'ignore'],
58
- maxBuffer: 64 * 1024 * 1024
59
- });
60
- }
61
- function tryGit(args, cwd) {
62
- try {
63
- return git(args, cwd);
64
- }
65
- catch {
66
- return null;
67
- }
68
- }
69
- /** The repository's default branch as a remote-tracking ref, when discoverable. */
70
- function defaultBranch(cwd) {
71
- const head = tryGit(['symbolic-ref', '--short', 'refs/remotes/origin/HEAD'], cwd);
72
- if (head && head.trim())
73
- return head.trim();
74
- for (const candidate of ['origin/main', 'origin/master', 'main', 'master']) {
75
- if (tryGit(['rev-parse', '--verify', '--quiet', candidate], cwd))
76
- return candidate;
77
- }
78
- return undefined;
79
- }
17
+ const git_changed_1 = require("git-changed");
80
18
  /**
81
19
  * Resolve the ref to diff against. An explicit `base` wins; otherwise the PR
82
20
  * base branch (`origin/$GITHUB_BASE_REF`) when running in GitHub Actions;
@@ -84,76 +22,23 @@ function defaultBranch(cwd) {
84
22
  * the caller falls back to working-tree changes only.
85
23
  */
86
24
  function resolveChangedBase(base, cwd = process.cwd()) {
87
- if (base && base.trim())
88
- return base.trim();
89
- const prBase = process.env.GITHUB_BASE_REF;
90
- if (prBase && prBase.trim()) {
91
- const ref = `origin/${prBase.trim()}`;
92
- if (tryGit(['rev-parse', '--verify', '--quiet', ref], cwd))
93
- return ref;
94
- return prBase.trim();
95
- }
96
- return defaultBranch(cwd);
25
+ return (0, git_changed_1.resolveBase)(base, cwd);
97
26
  }
98
- /** Parse `git status --porcelain` into paths (rename target wins). */
99
- function workingTreePaths(cwd) {
100
- const out = [];
101
- // `-uall` lists untracked *files*; the default collapses a new directory to
102
- // the directory name, which would hide every file a new module adds.
103
- const status = tryGit(['status', '--porcelain', '-uall'], cwd) ?? '';
104
- for (const rawLine of status.split('\n')) {
105
- const line = rawLine.trimEnd();
106
- if (!line)
107
- continue;
108
- let p = line.slice(3);
109
- const arrow = p.indexOf(' -> ');
110
- if (arrow !== -1)
111
- p = p.slice(arrow + 4);
112
- p = p.replace(/^"|"$/g, '');
113
- if (p)
114
- out.push(p);
27
+ function collect(cwd, base, ext) {
28
+ if (!(0, git_changed_1.isRepo)(cwd)) {
29
+ throw new Error(`--changed needs a git repository; ${cwd} is not inside one`);
115
30
  }
116
- return out;
31
+ const result = (0, git_changed_1.changedFiles)({ cwd, base, ext });
32
+ return { files: result.paths, base: result.base, mergeBase: result.mergeBase };
117
33
  }
118
34
  /**
119
35
  * Collect the files that differ from `base` (via `git merge-base`) plus any
120
- * uncommitted/untracked working-tree changes. Falls back to `git diff HEAD`
121
- * when no base is resolvable or no merge base exists — a shallow clone or a
122
- * detached CI checkout — rather than failing the run.
36
+ * uncommitted/untracked working-tree changes.
123
37
  */
124
38
  function changedFiles(options = {}) {
125
- const cwd = options.cwd ?? process.cwd();
126
- if (!tryGit(['rev-parse', '--git-dir'], cwd)) {
127
- throw new Error(`--changed needs a git repository; ${cwd} is not inside one`);
128
- }
129
- const files = new Set(workingTreePaths(cwd));
130
- const base = resolveChangedBase(options.base, cwd);
131
- let mergeBase;
132
- if (base) {
133
- const found = tryGit(['merge-base', 'HEAD', base], cwd);
134
- mergeBase = found?.trim() || undefined;
135
- }
136
- // No base, or no common ancestor (shallow clone / detached checkout): the
137
- // uncommitted diff against HEAD is all the history we can see.
138
- const diffArgs = mergeBase
139
- ? ['diff', '--name-only', '--diff-filter=ACMR', mergeBase, 'HEAD']
140
- : ['diff', '--name-only', '--diff-filter=ACMR', 'HEAD'];
141
- for (const rawLine of (tryGit(diffArgs, cwd) ?? '').split('\n')) {
142
- const p = rawLine.trim();
143
- if (p)
144
- files.add(p);
145
- }
146
- const abs = [];
147
- for (const rel of files) {
148
- const full = path.resolve(cwd, rel);
149
- // Deleted or renamed-away paths have nothing left to lint.
150
- if ((0, fs_1.existsSync)(full) && (0, fs_1.statSync)(full).isFile())
151
- abs.push(full);
152
- }
153
- return { files: abs.sort(), base, mergeBase };
39
+ return collect(options.cwd ?? process.cwd(), options.base);
154
40
  }
155
41
  /** {@link changedFiles}, narrowed to `.sql`. */
156
42
  function changedSqlFiles(options = {}) {
157
- const result = changedFiles(options);
158
- return { ...result, files: result.files.filter((f) => f.toLowerCase().endsWith('.sql')) };
43
+ return collect(options.cwd ?? process.cwd(), options.base, '.sql');
159
44
  }
package/esm/changed.js CHANGED
@@ -2,43 +2,14 @@
2
2
  * Changed-file detection for `--changed`, so a CI gate (or a human before a
3
3
  * commit) pays only for the SQL that a branch actually touched.
4
4
  *
5
- * Modelled on pgpm's bundle-drift check (`pgpm/core/src/packaging/check.ts` in
6
- * constructive): resolve a base ref (explicit → `origin/$GITHUB_BASE_REF` in a
7
- * PR → the repository's default branch), diff `HEAD` against the **merge base**
8
- * so unrelated commits on the base branch don't widen the set, and union that
9
- * with uncommitted/untracked working-tree changes. Deleted paths are dropped —
10
- * there is nothing left on disk to lint.
5
+ * The git plumbing — base resolution, merge-base diff, working-tree union,
6
+ * rename targets, dropping paths that no longer exist — lives in `git-changed`,
7
+ * which is shared with pgpm's bundle-drift check. This module is the `.sql`
8
+ * filter over it, and the place where "no base" stays non-fatal: a lint gate
9
+ * that refuses to run on a shallow clone lints nothing, which is worse than
10
+ * linting the working tree.
11
11
  */
12
- import { execFileSync } from 'child_process';
13
- import { existsSync, statSync } from 'fs';
14
- import * as path from 'path';
15
- function git(args, cwd) {
16
- return execFileSync('git', args, {
17
- cwd,
18
- encoding: 'utf-8',
19
- stdio: ['ignore', 'pipe', 'ignore'],
20
- maxBuffer: 64 * 1024 * 1024
21
- });
22
- }
23
- function tryGit(args, cwd) {
24
- try {
25
- return git(args, cwd);
26
- }
27
- catch {
28
- return null;
29
- }
30
- }
31
- /** The repository's default branch as a remote-tracking ref, when discoverable. */
32
- function defaultBranch(cwd) {
33
- const head = tryGit(['symbolic-ref', '--short', 'refs/remotes/origin/HEAD'], cwd);
34
- if (head && head.trim())
35
- return head.trim();
36
- for (const candidate of ['origin/main', 'origin/master', 'main', 'master']) {
37
- if (tryGit(['rev-parse', '--verify', '--quiet', candidate], cwd))
38
- return candidate;
39
- }
40
- return undefined;
41
- }
12
+ import { changedFiles as gitChangedFiles, isRepo, resolveBase } from 'git-changed';
42
13
  /**
43
14
  * Resolve the ref to diff against. An explicit `base` wins; otherwise the PR
44
15
  * base branch (`origin/$GITHUB_BASE_REF`) when running in GitHub Actions;
@@ -46,76 +17,23 @@ function defaultBranch(cwd) {
46
17
  * the caller falls back to working-tree changes only.
47
18
  */
48
19
  export function resolveChangedBase(base, cwd = process.cwd()) {
49
- if (base && base.trim())
50
- return base.trim();
51
- const prBase = process.env.GITHUB_BASE_REF;
52
- if (prBase && prBase.trim()) {
53
- const ref = `origin/${prBase.trim()}`;
54
- if (tryGit(['rev-parse', '--verify', '--quiet', ref], cwd))
55
- return ref;
56
- return prBase.trim();
57
- }
58
- return defaultBranch(cwd);
20
+ return resolveBase(base, cwd);
59
21
  }
60
- /** Parse `git status --porcelain` into paths (rename target wins). */
61
- function workingTreePaths(cwd) {
62
- const out = [];
63
- // `-uall` lists untracked *files*; the default collapses a new directory to
64
- // the directory name, which would hide every file a new module adds.
65
- const status = tryGit(['status', '--porcelain', '-uall'], cwd) ?? '';
66
- for (const rawLine of status.split('\n')) {
67
- const line = rawLine.trimEnd();
68
- if (!line)
69
- continue;
70
- let p = line.slice(3);
71
- const arrow = p.indexOf(' -> ');
72
- if (arrow !== -1)
73
- p = p.slice(arrow + 4);
74
- p = p.replace(/^"|"$/g, '');
75
- if (p)
76
- out.push(p);
22
+ function collect(cwd, base, ext) {
23
+ if (!isRepo(cwd)) {
24
+ throw new Error(`--changed needs a git repository; ${cwd} is not inside one`);
77
25
  }
78
- return out;
26
+ const result = gitChangedFiles({ cwd, base, ext });
27
+ return { files: result.paths, base: result.base, mergeBase: result.mergeBase };
79
28
  }
80
29
  /**
81
30
  * Collect the files that differ from `base` (via `git merge-base`) plus any
82
- * uncommitted/untracked working-tree changes. Falls back to `git diff HEAD`
83
- * when no base is resolvable or no merge base exists — a shallow clone or a
84
- * detached CI checkout — rather than failing the run.
31
+ * uncommitted/untracked working-tree changes.
85
32
  */
86
33
  export function changedFiles(options = {}) {
87
- const cwd = options.cwd ?? process.cwd();
88
- if (!tryGit(['rev-parse', '--git-dir'], cwd)) {
89
- throw new Error(`--changed needs a git repository; ${cwd} is not inside one`);
90
- }
91
- const files = new Set(workingTreePaths(cwd));
92
- const base = resolveChangedBase(options.base, cwd);
93
- let mergeBase;
94
- if (base) {
95
- const found = tryGit(['merge-base', 'HEAD', base], cwd);
96
- mergeBase = found?.trim() || undefined;
97
- }
98
- // No base, or no common ancestor (shallow clone / detached checkout): the
99
- // uncommitted diff against HEAD is all the history we can see.
100
- const diffArgs = mergeBase
101
- ? ['diff', '--name-only', '--diff-filter=ACMR', mergeBase, 'HEAD']
102
- : ['diff', '--name-only', '--diff-filter=ACMR', 'HEAD'];
103
- for (const rawLine of (tryGit(diffArgs, cwd) ?? '').split('\n')) {
104
- const p = rawLine.trim();
105
- if (p)
106
- files.add(p);
107
- }
108
- const abs = [];
109
- for (const rel of files) {
110
- const full = path.resolve(cwd, rel);
111
- // Deleted or renamed-away paths have nothing left to lint.
112
- if (existsSync(full) && statSync(full).isFile())
113
- abs.push(full);
114
- }
115
- return { files: abs.sort(), base, mergeBase };
34
+ return collect(options.cwd ?? process.cwd(), options.base);
116
35
  }
117
36
  /** {@link changedFiles}, narrowed to `.sql`. */
118
37
  export function changedSqlFiles(options = {}) {
119
- const result = changedFiles(options);
120
- return { ...result, files: result.files.filter((f) => f.toLowerCase().endsWith('.sql')) };
38
+ return collect(options.cwd ?? process.cwd(), options.base, '.sql');
121
39
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pgsql/lint",
3
- "version": "18.2.0",
3
+ "version": "18.2.1",
4
4
  "author": "Constructive <developers@constructive.io>",
5
5
  "description": "Source-level SQL/PL/pgSQL convention linter: fully-qualified references, no search_path, no dynamic SQL, with ESLint-style inline waivers. Pure AST, no database.",
6
6
  "main": "index.js",
@@ -36,6 +36,7 @@
36
36
  "dependencies": {
37
37
  "@pgsql/traverse": "18.7.3",
38
38
  "chalk": "^4.1.0",
39
+ "git-changed": "^0.3.0",
39
40
  "libpg-query": "18.1.4",
40
41
  "minimist": "1.2.8",
41
42
  "pgsql-parser": "18.2.3"
@@ -56,5 +57,5 @@
56
57
  "search_path",
57
58
  "dynamic-sql"
58
59
  ],
59
- "gitHead": "fa4ea3167cadd5eb0e9a216a2abfa564f6c62364"
60
+ "gitHead": "69cdce673db035fb1ee3b529480d39fac01512ab"
60
61
  }