@praxisflux/gates 0.59.6 → 0.60.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.
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
// These helpers make that dotfile-safe, idempotent, and honest (verify on disk, never clobber
|
|
5
5
|
// what the user has grown). Zero-dependency.
|
|
6
6
|
|
|
7
|
-
import { cpSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
8
|
-
import { join, dirname } from "node:path";
|
|
7
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
8
|
+
import { join, dirname, resolve } from "node:path";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Recursively copy `src` → `dest`. Uses node:fs cp, which copies dotfiles (e.g. `.template/`)
|
|
@@ -38,6 +38,43 @@ export function ensureGitignore(root, entry) {
|
|
|
38
38
|
return true;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Resolve the real git dir for `root`: the `.git` directory itself, or — when `.git` is a
|
|
43
|
+
* worktree pointer file (`gitdir: <path>`) — the path it points at. `null` when `.git` is
|
|
44
|
+
* absent (pre-`git init`, spec 060 R6) or unreadable.
|
|
45
|
+
*/
|
|
46
|
+
function resolveGitDir(root) {
|
|
47
|
+
const p = join(root, ".git");
|
|
48
|
+
if (!existsSync(p)) return null;
|
|
49
|
+
if (statSync(p).isDirectory()) return p;
|
|
50
|
+
try {
|
|
51
|
+
const m = /^gitdir:\s*(.+?)\s*$/m.exec(readFileSync(p, "utf8"));
|
|
52
|
+
return m ? resolve(root, m[1]) : null;
|
|
53
|
+
} catch { return null; }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Ensure `<gitdir>/info/exclude` contains every line in `entries` (local-only planting,
|
|
58
|
+
* spec 060 R1/R2), creating `info/` as needed and appending only lines not already present.
|
|
59
|
+
* Never touches `.gitignore`. Returns `{ status: "no-git" }` (nothing written — R6) when
|
|
60
|
+
* `root` has no `.git`, else `{ status: "added"|"unchanged", added: string[] }`.
|
|
61
|
+
* `dryRun: true` (plant's `--check`) reports what would happen without writing.
|
|
62
|
+
*/
|
|
63
|
+
export function ensureExclude(root, entries, { dryRun = false } = {}) {
|
|
64
|
+
const gitDir = resolveGitDir(root);
|
|
65
|
+
if (!gitDir) return { status: "no-git", added: [] };
|
|
66
|
+
const p = join(gitDir, "info", "exclude");
|
|
67
|
+
const existing = existsSync(p) ? readFileSync(p, "utf8") : "";
|
|
68
|
+
const have = new Set(existing.split("\n").map((l) => l.trim()));
|
|
69
|
+
const toAdd = entries.map((e) => e.trim()).filter((e) => !have.has(e));
|
|
70
|
+
if (toAdd.length === 0) return { status: "unchanged", added: [] };
|
|
71
|
+
if (dryRun) return { status: "added", added: toAdd };
|
|
72
|
+
mkdirSync(dirname(p), { recursive: true });
|
|
73
|
+
const prefix = existing === "" ? "" : existing.replace(/\n*$/, "\n");
|
|
74
|
+
writeFileSync(p, prefix + toAdd.join("\n") + "\n");
|
|
75
|
+
return { status: "added", added: toAdd };
|
|
76
|
+
}
|
|
77
|
+
|
|
41
78
|
/** Which of `relPaths` (relative to `root`) are missing on disk. Empty = all present. */
|
|
42
79
|
export function verifyPresent(root, relPaths) {
|
|
43
80
|
return relPaths.filter((r) => !existsSync(join(root, r)));
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
// These helpers make that dotfile-safe, idempotent, and honest (verify on disk, never clobber
|
|
5
5
|
// what the user has grown). Zero-dependency.
|
|
6
6
|
|
|
7
|
-
import { cpSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
8
|
-
import { join, dirname } from "node:path";
|
|
7
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
8
|
+
import { join, dirname, resolve } from "node:path";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Recursively copy `src` → `dest`. Uses node:fs cp, which copies dotfiles (e.g. `.template/`)
|
|
@@ -38,6 +38,43 @@ export function ensureGitignore(root, entry) {
|
|
|
38
38
|
return true;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Resolve the real git dir for `root`: the `.git` directory itself, or — when `.git` is a
|
|
43
|
+
* worktree pointer file (`gitdir: <path>`) — the path it points at. `null` when `.git` is
|
|
44
|
+
* absent (pre-`git init`, spec 060 R6) or unreadable.
|
|
45
|
+
*/
|
|
46
|
+
function resolveGitDir(root) {
|
|
47
|
+
const p = join(root, ".git");
|
|
48
|
+
if (!existsSync(p)) return null;
|
|
49
|
+
if (statSync(p).isDirectory()) return p;
|
|
50
|
+
try {
|
|
51
|
+
const m = /^gitdir:\s*(.+?)\s*$/m.exec(readFileSync(p, "utf8"));
|
|
52
|
+
return m ? resolve(root, m[1]) : null;
|
|
53
|
+
} catch { return null; }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Ensure `<gitdir>/info/exclude` contains every line in `entries` (local-only planting,
|
|
58
|
+
* spec 060 R1/R2), creating `info/` as needed and appending only lines not already present.
|
|
59
|
+
* Never touches `.gitignore`. Returns `{ status: "no-git" }` (nothing written — R6) when
|
|
60
|
+
* `root` has no `.git`, else `{ status: "added"|"unchanged", added: string[] }`.
|
|
61
|
+
* `dryRun: true` (plant's `--check`) reports what would happen without writing.
|
|
62
|
+
*/
|
|
63
|
+
export function ensureExclude(root, entries, { dryRun = false } = {}) {
|
|
64
|
+
const gitDir = resolveGitDir(root);
|
|
65
|
+
if (!gitDir) return { status: "no-git", added: [] };
|
|
66
|
+
const p = join(gitDir, "info", "exclude");
|
|
67
|
+
const existing = existsSync(p) ? readFileSync(p, "utf8") : "";
|
|
68
|
+
const have = new Set(existing.split("\n").map((l) => l.trim()));
|
|
69
|
+
const toAdd = entries.map((e) => e.trim()).filter((e) => !have.has(e));
|
|
70
|
+
if (toAdd.length === 0) return { status: "unchanged", added: [] };
|
|
71
|
+
if (dryRun) return { status: "added", added: toAdd };
|
|
72
|
+
mkdirSync(dirname(p), { recursive: true });
|
|
73
|
+
const prefix = existing === "" ? "" : existing.replace(/\n*$/, "\n");
|
|
74
|
+
writeFileSync(p, prefix + toAdd.join("\n") + "\n");
|
|
75
|
+
return { status: "added", added: toAdd };
|
|
76
|
+
}
|
|
77
|
+
|
|
41
78
|
/** Which of `relPaths` (relative to `root`) are missing on disk. Empty = all present. */
|
|
42
79
|
export function verifyPresent(root, relPaths) {
|
|
43
80
|
return relPaths.filter((r) => !existsSync(join(root, r)));
|
package/lib/installer.mjs
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
// These helpers make that dotfile-safe, idempotent, and honest (verify on disk, never clobber
|
|
5
5
|
// what the user has grown). Zero-dependency.
|
|
6
6
|
|
|
7
|
-
import { cpSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
8
|
-
import { join, dirname } from "node:path";
|
|
7
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
8
|
+
import { join, dirname, resolve } from "node:path";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Recursively copy `src` → `dest`. Uses node:fs cp, which copies dotfiles (e.g. `.template/`)
|
|
@@ -38,6 +38,43 @@ export function ensureGitignore(root, entry) {
|
|
|
38
38
|
return true;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Resolve the real git dir for `root`: the `.git` directory itself, or — when `.git` is a
|
|
43
|
+
* worktree pointer file (`gitdir: <path>`) — the path it points at. `null` when `.git` is
|
|
44
|
+
* absent (pre-`git init`, spec 060 R6) or unreadable.
|
|
45
|
+
*/
|
|
46
|
+
function resolveGitDir(root) {
|
|
47
|
+
const p = join(root, ".git");
|
|
48
|
+
if (!existsSync(p)) return null;
|
|
49
|
+
if (statSync(p).isDirectory()) return p;
|
|
50
|
+
try {
|
|
51
|
+
const m = /^gitdir:\s*(.+?)\s*$/m.exec(readFileSync(p, "utf8"));
|
|
52
|
+
return m ? resolve(root, m[1]) : null;
|
|
53
|
+
} catch { return null; }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Ensure `<gitdir>/info/exclude` contains every line in `entries` (local-only planting,
|
|
58
|
+
* spec 060 R1/R2), creating `info/` as needed and appending only lines not already present.
|
|
59
|
+
* Never touches `.gitignore`. Returns `{ status: "no-git" }` (nothing written — R6) when
|
|
60
|
+
* `root` has no `.git`, else `{ status: "added"|"unchanged", added: string[] }`.
|
|
61
|
+
* `dryRun: true` (plant's `--check`) reports what would happen without writing.
|
|
62
|
+
*/
|
|
63
|
+
export function ensureExclude(root, entries, { dryRun = false } = {}) {
|
|
64
|
+
const gitDir = resolveGitDir(root);
|
|
65
|
+
if (!gitDir) return { status: "no-git", added: [] };
|
|
66
|
+
const p = join(gitDir, "info", "exclude");
|
|
67
|
+
const existing = existsSync(p) ? readFileSync(p, "utf8") : "";
|
|
68
|
+
const have = new Set(existing.split("\n").map((l) => l.trim()));
|
|
69
|
+
const toAdd = entries.map((e) => e.trim()).filter((e) => !have.has(e));
|
|
70
|
+
if (toAdd.length === 0) return { status: "unchanged", added: [] };
|
|
71
|
+
if (dryRun) return { status: "added", added: toAdd };
|
|
72
|
+
mkdirSync(dirname(p), { recursive: true });
|
|
73
|
+
const prefix = existing === "" ? "" : existing.replace(/\n*$/, "\n");
|
|
74
|
+
writeFileSync(p, prefix + toAdd.join("\n") + "\n");
|
|
75
|
+
return { status: "added", added: toAdd };
|
|
76
|
+
}
|
|
77
|
+
|
|
41
78
|
/** Which of `relPaths` (relative to `root`) are missing on disk. Empty = all present. */
|
|
42
79
|
export function verifyPresent(root, relPaths) {
|
|
43
80
|
return relPaths.filter((r) => !existsSync(join(root, r)));
|
package/package.json
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
// These helpers make that dotfile-safe, idempotent, and honest (verify on disk, never clobber
|
|
5
5
|
// what the user has grown). Zero-dependency.
|
|
6
6
|
|
|
7
|
-
import { cpSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
8
|
-
import { join, dirname } from "node:path";
|
|
7
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
8
|
+
import { join, dirname, resolve } from "node:path";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Recursively copy `src` → `dest`. Uses node:fs cp, which copies dotfiles (e.g. `.template/`)
|
|
@@ -38,6 +38,43 @@ export function ensureGitignore(root, entry) {
|
|
|
38
38
|
return true;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Resolve the real git dir for `root`: the `.git` directory itself, or — when `.git` is a
|
|
43
|
+
* worktree pointer file (`gitdir: <path>`) — the path it points at. `null` when `.git` is
|
|
44
|
+
* absent (pre-`git init`, spec 060 R6) or unreadable.
|
|
45
|
+
*/
|
|
46
|
+
function resolveGitDir(root) {
|
|
47
|
+
const p = join(root, ".git");
|
|
48
|
+
if (!existsSync(p)) return null;
|
|
49
|
+
if (statSync(p).isDirectory()) return p;
|
|
50
|
+
try {
|
|
51
|
+
const m = /^gitdir:\s*(.+?)\s*$/m.exec(readFileSync(p, "utf8"));
|
|
52
|
+
return m ? resolve(root, m[1]) : null;
|
|
53
|
+
} catch { return null; }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Ensure `<gitdir>/info/exclude` contains every line in `entries` (local-only planting,
|
|
58
|
+
* spec 060 R1/R2), creating `info/` as needed and appending only lines not already present.
|
|
59
|
+
* Never touches `.gitignore`. Returns `{ status: "no-git" }` (nothing written — R6) when
|
|
60
|
+
* `root` has no `.git`, else `{ status: "added"|"unchanged", added: string[] }`.
|
|
61
|
+
* `dryRun: true` (plant's `--check`) reports what would happen without writing.
|
|
62
|
+
*/
|
|
63
|
+
export function ensureExclude(root, entries, { dryRun = false } = {}) {
|
|
64
|
+
const gitDir = resolveGitDir(root);
|
|
65
|
+
if (!gitDir) return { status: "no-git", added: [] };
|
|
66
|
+
const p = join(gitDir, "info", "exclude");
|
|
67
|
+
const existing = existsSync(p) ? readFileSync(p, "utf8") : "";
|
|
68
|
+
const have = new Set(existing.split("\n").map((l) => l.trim()));
|
|
69
|
+
const toAdd = entries.map((e) => e.trim()).filter((e) => !have.has(e));
|
|
70
|
+
if (toAdd.length === 0) return { status: "unchanged", added: [] };
|
|
71
|
+
if (dryRun) return { status: "added", added: toAdd };
|
|
72
|
+
mkdirSync(dirname(p), { recursive: true });
|
|
73
|
+
const prefix = existing === "" ? "" : existing.replace(/\n*$/, "\n");
|
|
74
|
+
writeFileSync(p, prefix + toAdd.join("\n") + "\n");
|
|
75
|
+
return { status: "added", added: toAdd };
|
|
76
|
+
}
|
|
77
|
+
|
|
41
78
|
/** Which of `relPaths` (relative to `root`) are missing on disk. Empty = all present. */
|
|
42
79
|
export function verifyPresent(root, relPaths) {
|
|
43
80
|
return relPaths.filter((r) => !existsSync(join(root, r)));
|