@coreframe/scripts 0.1.0 → 0.1.2
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/dist/config.d.ts +18 -4
- package/dist/config.js +1 -3
- package/dist/coreframe.d.ts +243 -241
- package/dist/coreframe.js +1 -239
- package/dist/db/client.d.ts +26 -18
- package/dist/db/client.js +1 -29
- package/dist/db/config.d.ts +22 -19
- package/dist/db/config.js +1 -32
- package/dist/db/drizzle-kit.d.ts +6 -5
- package/dist/db/drizzle-kit.js +1 -21
- package/dist/db/ensure.d.ts +20 -2
- package/dist/db/ensure.js +1 -39
- package/dist/db/migrate-held.d.ts +16 -10
- package/dist/db/migrate-held.js +3 -112
- package/dist/db/migrate.d.ts +4 -2
- package/dist/db/migrate.js +1 -24
- package/dist/db/project.d.ts +19 -7
- package/dist/db/project.js +1 -9
- package/dist/db/reset.d.ts +4 -2
- package/dist/db/reset.js +1 -18
- package/dist/db/schema-push.d.ts +22 -0
- package/dist/db/schema-push.js +1 -0
- package/dist/db/seed.d.ts +6 -3
- package/dist/db/seed.js +1 -22
- package/dist/db/setup.d.ts +6 -3
- package/dist/db/setup.js +1 -14
- package/dist/deploy/check-migrations.d.ts +4 -2
- package/dist/deploy/check-migrations.js +1 -17
- package/dist/deploy/checks.d.ts +6 -2
- package/dist/deploy/checks.js +1 -27
- package/dist/deploy/config.d.ts +47 -44
- package/dist/deploy/config.js +1 -9
- package/dist/deploy/migrations.d.ts +21 -18
- package/dist/deploy/migrations.js +4 -203
- package/dist/deploy/pending-migrations.d.ts +9 -5
- package/dist/deploy/pending-migrations.js +1 -30
- package/dist/deploy/tauri-release.d.ts +93 -73
- package/dist/deploy/tauri-release.js +1 -512
- package/dist/deploy/version.d.ts +15 -13
- package/dist/deploy/version.js +1 -41
- package/dist/deploy.d.ts +16 -15
- package/dist/deploy.js +1 -246
- package/dist/dev.d.ts +23 -12
- package/dist/dev.js +1 -95
- package/dist/image-generator.d.ts +18 -2
- package/dist/image-generator.js +1 -59
- package/dist/project.d.ts +4 -1
- package/dist/project.js +1 -13
- package/dist/typecheck.d.ts +4 -2
- package/dist/typecheck.js +1 -44
- package/dist/upgrade/check-skill-current.d.ts +10 -5
- package/dist/upgrade/check-skill-current.js +1 -87
- package/dist/upgrade/ensure-template-remote.d.ts +10 -5
- package/dist/upgrade/ensure-template-remote.js +1 -56
- package/dist/upgrade/fast-forward-template-files.d.ts +8 -6
- package/dist/upgrade/fast-forward-template-files.js +1 -279
- package/package.json +7 -2
- package/config.test.ts +0 -15
- package/config.ts +0 -9
- package/coreframe.ts +0 -272
- package/db/client.ts +0 -72
- package/db/config.ts +0 -41
- package/db/drizzle-kit.test.ts +0 -27
- package/db/drizzle-kit.ts +0 -39
- package/db/ensure.ts +0 -67
- package/db/migrate-held.test.ts +0 -81
- package/db/migrate-held.ts +0 -166
- package/db/migrate.ts +0 -39
- package/db/project.ts +0 -28
- package/db/reset.ts +0 -24
- package/db/seed.ts +0 -38
- package/db/setup.ts +0 -18
- package/deploy/check-migrations.ts +0 -31
- package/deploy/checks.ts +0 -38
- package/deploy/config.test.ts +0 -24
- package/deploy/config.ts +0 -68
- package/deploy/migrations.test.ts +0 -110
- package/deploy/migrations.ts +0 -317
- package/deploy/pending-migrations.test.ts +0 -93
- package/deploy/pending-migrations.ts +0 -45
- package/deploy/tauri-release.test.ts +0 -196
- package/deploy/tauri-release.ts +0 -816
- package/deploy/version.test.ts +0 -29
- package/deploy/version.ts +0 -65
- package/deploy.test.ts +0 -101
- package/deploy.ts +0 -346
- package/dev.test.ts +0 -153
- package/dev.ts +0 -155
- package/image-generator.ts +0 -84
- package/project.ts +0 -6
- package/tsconfig.build.json +0 -12
- package/tsconfig.json +0 -20
- package/typecheck.ts +0 -55
- package/upgrade/check-skill-current.ts +0 -112
- package/upgrade/ensure-template-remote.ts +0 -79
- package/upgrade/fast-forward-template-files.ts +0 -408
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
//#region upgrade/ensure-template-remote.d.ts
|
|
2
|
+
type EnsureTemplateRemoteOptions = {
|
|
3
|
+
fetch: boolean;
|
|
4
|
+
remote: string;
|
|
5
5
|
};
|
|
6
|
-
|
|
6
|
+
declare function ensureTemplateRemote({
|
|
7
|
+
fetch,
|
|
8
|
+
remote
|
|
9
|
+
}?: Partial<EnsureTemplateRemoteOptions>): void;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { EnsureTemplateRemoteOptions, ensureTemplateRemote };
|
|
@@ -1,57 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
3
|
-
const defaultRemote = "template";
|
|
4
|
-
const defaultUrl = "https://github.com/airlock-labs/coreframe.git";
|
|
5
|
-
function git(args) {
|
|
6
|
-
return spawnSync("git", args, { encoding: "utf8" });
|
|
7
|
-
}
|
|
8
|
-
function runGit(args) {
|
|
9
|
-
const result = git(args);
|
|
10
|
-
if (result.status !== 0) {
|
|
11
|
-
const detail = result.stderr.trim() || result.stdout.trim();
|
|
12
|
-
throw new Error(`Git command failed: git ${args.join(" ")}${detail ? `\n${detail}` : ""}`);
|
|
13
|
-
}
|
|
14
|
-
return result.stdout;
|
|
15
|
-
}
|
|
16
|
-
function getRemoteUrl(remote) {
|
|
17
|
-
const result = git(["remote", "get-url", remote]);
|
|
18
|
-
if (result.status === 2) {
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
if (result.status !== 0) {
|
|
22
|
-
const detail = result.stderr.trim() || result.stdout.trim();
|
|
23
|
-
throw new Error(`Could not inspect remote ${remote}.${detail ? `\n${detail}` : ""}`);
|
|
24
|
-
}
|
|
25
|
-
return result.stdout.trim();
|
|
26
|
-
}
|
|
27
|
-
export function ensureTemplateRemote({ fetch = true, remote = defaultRemote, } = {}) {
|
|
28
|
-
if (remote.length === 0) {
|
|
29
|
-
throw new Error("Remote name must not be empty.");
|
|
30
|
-
}
|
|
31
|
-
runGit(["rev-parse", "--show-toplevel"]);
|
|
32
|
-
const existingUrl = getRemoteUrl(remote);
|
|
33
|
-
if (existingUrl === null) {
|
|
34
|
-
runGit(["remote", "add", remote, defaultUrl]);
|
|
35
|
-
console.log(`Added ${remote} remote: ${defaultUrl}`);
|
|
36
|
-
}
|
|
37
|
-
else if (existingUrl === defaultUrl) {
|
|
38
|
-
console.log(`${remote} remote already points at ${defaultUrl}`);
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
throw new Error(`${remote} remote points at ${existingUrl}, not ${defaultUrl}.\n` +
|
|
42
|
-
"Choose the intended template source before continuing, then update or rename the remote manually.");
|
|
43
|
-
}
|
|
44
|
-
if (fetch) {
|
|
45
|
-
runGit(["fetch", remote, "--tags", "--prune"]);
|
|
46
|
-
console.log(`Fetched ${remote}.`);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
if (import.meta.main) {
|
|
50
|
-
try {
|
|
51
|
-
ensureTemplateRemote({ fetch: !process.argv.includes("--no-fetch") });
|
|
52
|
-
}
|
|
53
|
-
catch (error) {
|
|
54
|
-
console.error(error instanceof Error ? error.message : String(error));
|
|
55
|
-
process.exit(65);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
2
|
+
import{spawnSync as e}from"node:child_process";const t=`https://github.com/airlock-labs/coreframe.git`;function n(t){return e(`git`,t,{encoding:`utf8`})}function r(e){let t=n(e);if(t.status!==0){let n=t.stderr.trim()||t.stdout.trim();throw Error(`Git command failed: git ${e.join(` `)}${n?`\n${n}`:``}`)}return t.stdout}function i(e){let t=n([`remote`,`get-url`,e]);if(t.status===2)return null;if(t.status!==0){let n=t.stderr.trim()||t.stdout.trim();throw Error(`Could not inspect remote ${e}.${n?`\n${n}`:``}`)}return t.stdout.trim()}function a({fetch:e=!0,remote:n=`template`}={}){if(n.length===0)throw Error(`Remote name must not be empty.`);r([`rev-parse`,`--show-toplevel`]);let a=i(n);if(a===null)r([`remote`,`add`,n,t]),console.log(`Added ${n} remote: ${t}`);else if(a===t)console.log(`${n} remote already points at ${t}`);else throw Error(`${n} remote points at ${a}, not ${t}.\nChoose the intended template source before continuing, then update or rename the remote manually.`);e&&(r([`fetch`,n,`--tags`,`--prune`]),console.log(`Fetched ${n}.`))}if(import.meta.main)try{a({fetch:!process.argv.includes(`--no-fetch`)})}catch(e){console.error(e instanceof Error?e.message:String(e)),process.exit(65)}export{a as ensureTemplateRemote};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
//#region upgrade/fast-forward-template-files.d.ts
|
|
2
|
+
type FastForwardTemplateFilesOptions = {
|
|
3
|
+
apply: boolean;
|
|
4
|
+
base?: string;
|
|
5
|
+
target: string;
|
|
6
6
|
};
|
|
7
|
-
|
|
7
|
+
declare function fastForwardTemplateFiles(args: FastForwardTemplateFilesOptions): void;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { FastForwardTemplateFilesOptions, fastForwardTemplateFiles };
|
|
@@ -1,280 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { spawnSync }
|
|
3
|
-
import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, readlinkSync, rmSync, symlinkSync, writeFileSync, } from "node:fs";
|
|
4
|
-
import { dirname, join, resolve, sep } from "node:path";
|
|
5
|
-
const projectOwnedPathPrefixes = ["drizzle/", "drizzle-hold/"];
|
|
6
|
-
const supportedModes = new Set(["100644", "100755", "120000"]);
|
|
7
|
-
function git(args, options = {}) {
|
|
8
|
-
return spawnSync("git", args, options);
|
|
9
|
-
}
|
|
10
|
-
function gitText(args) {
|
|
11
|
-
const result = git(args, { encoding: "utf8" });
|
|
12
|
-
if (result.status !== 0) {
|
|
13
|
-
const detail = result.stderr.toString().trim() || result.stdout.toString().trim();
|
|
14
|
-
throw new Error(`Git command failed: git ${args.join(" ")}${detail ? `\n${detail}` : ""}`);
|
|
15
|
-
}
|
|
16
|
-
return result.stdout.toString();
|
|
17
|
-
}
|
|
18
|
-
function gitBuffer(args) {
|
|
19
|
-
const result = git(args);
|
|
20
|
-
if (result.status !== 0) {
|
|
21
|
-
const detail = result.stderr.toString("utf8").trim() || result.stdout.toString("utf8").trim();
|
|
22
|
-
throw new Error(`Git command failed: git ${args.join(" ")}${detail ? `\n${detail}` : ""}`);
|
|
23
|
-
}
|
|
24
|
-
return result.stdout;
|
|
25
|
-
}
|
|
26
|
-
function getRepoRoot() {
|
|
27
|
-
return gitText(["rev-parse", "--show-toplevel"]).trim();
|
|
28
|
-
}
|
|
29
|
-
function readPackageCoreframeVersion(repoRoot) {
|
|
30
|
-
const packagePath = join(repoRoot, "package.json");
|
|
31
|
-
if (!existsSync(packagePath)) {
|
|
32
|
-
throw new Error("Missing --base <git-ref>, and package.json does not exist.");
|
|
33
|
-
}
|
|
34
|
-
const packageJson = JSON.parse(readFileSync(packagePath, "utf8"));
|
|
35
|
-
const version = packageJson.engines?.coreframe;
|
|
36
|
-
if (typeof version !== "string" || version.length === 0) {
|
|
37
|
-
throw new Error("Missing --base <git-ref>, and package.json engines.coreframe is not set.");
|
|
38
|
-
}
|
|
39
|
-
return version;
|
|
40
|
-
}
|
|
41
|
-
function splitNullTerminated(buffer) {
|
|
42
|
-
const fields = [];
|
|
43
|
-
let start = 0;
|
|
44
|
-
for (let index = 0; index < buffer.length; index += 1) {
|
|
45
|
-
if (buffer[index] !== 0) {
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
fields.push(buffer.subarray(start, index).toString("utf8"));
|
|
49
|
-
start = index + 1;
|
|
50
|
-
}
|
|
51
|
-
return fields.filter((field) => field.length > 0);
|
|
52
|
-
}
|
|
53
|
-
function listChangedPaths(base, target) {
|
|
54
|
-
const output = gitBuffer(["diff", "--name-status", "-z", "--no-renames", base, target, "--"]);
|
|
55
|
-
const fields = splitNullTerminated(output);
|
|
56
|
-
const changes = [];
|
|
57
|
-
for (let index = 0; index < fields.length; index += 2) {
|
|
58
|
-
const status = fields[index];
|
|
59
|
-
const path = fields[index + 1];
|
|
60
|
-
if (!status || !path) {
|
|
61
|
-
throw new Error("Could not parse git diff --name-status output.");
|
|
62
|
-
}
|
|
63
|
-
changes.push({ path, status });
|
|
64
|
-
}
|
|
65
|
-
return changes;
|
|
66
|
-
}
|
|
67
|
-
function getGitEntry(ref, path) {
|
|
68
|
-
const output = gitBuffer(["ls-tree", "-z", ref, "--", path]);
|
|
69
|
-
if (output.length === 0) {
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
const record = splitNullTerminated(output)[0];
|
|
73
|
-
const tabIndex = record.indexOf("\t");
|
|
74
|
-
if (tabIndex === -1) {
|
|
75
|
-
throw new Error(`Could not parse git ls-tree output for ${ref}:${path}.`);
|
|
76
|
-
}
|
|
77
|
-
const [mode, type, object] = record.slice(0, tabIndex).split(" ");
|
|
78
|
-
if (!mode || !type || !object) {
|
|
79
|
-
throw new Error(`Could not parse git ls-tree metadata for ${ref}:${path}.`);
|
|
80
|
-
}
|
|
81
|
-
return { mode, object, path, type };
|
|
82
|
-
}
|
|
83
|
-
function readGitBlob(entry) {
|
|
84
|
-
return gitBuffer(["cat-file", "blob", entry.object]);
|
|
85
|
-
}
|
|
86
|
-
function safeWorktreePath(repoRoot, path) {
|
|
87
|
-
const absolutePath = resolve(repoRoot, path);
|
|
88
|
-
const rootPrefix = repoRoot.endsWith(sep) ? repoRoot : `${repoRoot}${sep}`;
|
|
89
|
-
if (absolutePath !== repoRoot && !absolutePath.startsWith(rootPrefix)) {
|
|
90
|
-
throw new Error(`Refusing to access path outside repository: ${path}`);
|
|
91
|
-
}
|
|
92
|
-
return absolutePath;
|
|
93
|
-
}
|
|
94
|
-
function pathExists(path) {
|
|
95
|
-
try {
|
|
96
|
-
lstatSync(path);
|
|
97
|
-
return true;
|
|
98
|
-
}
|
|
99
|
-
catch (error) {
|
|
100
|
-
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
throw error;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
function isExecutable(stat) {
|
|
107
|
-
return (stat.mode & 0o111) !== 0;
|
|
108
|
-
}
|
|
109
|
-
function entryIsSupported(entry) {
|
|
110
|
-
return entry === null || (entry.type === "blob" && supportedModes.has(entry.mode));
|
|
111
|
-
}
|
|
112
|
-
function isProjectOwnedPath(path) {
|
|
113
|
-
return projectOwnedPathPrefixes.some((prefix) => path === prefix.slice(0, -1) || path.startsWith(prefix));
|
|
114
|
-
}
|
|
115
|
-
function currentMatchesEntry(repoRoot, path, entry) {
|
|
116
|
-
const worktreePath = safeWorktreePath(repoRoot, path);
|
|
117
|
-
if (entry === null) {
|
|
118
|
-
return { matches: !pathExists(worktreePath) };
|
|
119
|
-
}
|
|
120
|
-
if (!entryIsSupported(entry)) {
|
|
121
|
-
return { matches: false, reason: `unsupported template entry mode ${entry.mode} ${entry.type}` };
|
|
122
|
-
}
|
|
123
|
-
let stat;
|
|
124
|
-
try {
|
|
125
|
-
stat = lstatSync(worktreePath);
|
|
126
|
-
}
|
|
127
|
-
catch (error) {
|
|
128
|
-
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
129
|
-
return { matches: false, reason: "path is missing from worktree" };
|
|
130
|
-
}
|
|
131
|
-
throw error;
|
|
132
|
-
}
|
|
133
|
-
const expected = readGitBlob(entry);
|
|
134
|
-
if (entry.mode === "120000") {
|
|
135
|
-
if (!stat.isSymbolicLink()) {
|
|
136
|
-
return { matches: false, reason: "worktree path is not a symlink" };
|
|
137
|
-
}
|
|
138
|
-
const linkTarget = Buffer.from(readlinkSync(worktreePath), "utf8");
|
|
139
|
-
return { matches: linkTarget.equals(expected) };
|
|
140
|
-
}
|
|
141
|
-
if (!stat.isFile()) {
|
|
142
|
-
return { matches: false, reason: "worktree path is not a regular file" };
|
|
143
|
-
}
|
|
144
|
-
const executableMatches = entry.mode === "100755" ? isExecutable(stat) : !isExecutable(stat);
|
|
145
|
-
if (!executableMatches) {
|
|
146
|
-
return { matches: false, reason: "worktree executable bit differs from template" };
|
|
147
|
-
}
|
|
148
|
-
return { matches: readFileSync(worktreePath).equals(expected) };
|
|
149
|
-
}
|
|
150
|
-
function removeExistingPath(worktreePath) {
|
|
151
|
-
if (!pathExists(worktreePath)) {
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
rmSync(worktreePath, { force: true, recursive: false });
|
|
155
|
-
}
|
|
156
|
-
function applyEntry(repoRoot, path, entry) {
|
|
157
|
-
const worktreePath = safeWorktreePath(repoRoot, path);
|
|
158
|
-
if (entry === null) {
|
|
159
|
-
removeExistingPath(worktreePath);
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
mkdirSync(dirname(worktreePath), { recursive: true });
|
|
163
|
-
if (entry.mode === "120000") {
|
|
164
|
-
removeExistingPath(worktreePath);
|
|
165
|
-
symlinkSync(readGitBlob(entry).toString("utf8"), worktreePath);
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
const stat = pathExists(worktreePath) ? lstatSync(worktreePath) : null;
|
|
169
|
-
if (stat && !stat.isFile()) {
|
|
170
|
-
removeExistingPath(worktreePath);
|
|
171
|
-
}
|
|
172
|
-
writeFileSync(worktreePath, readGitBlob(entry));
|
|
173
|
-
chmodSync(worktreePath, entry.mode === "100755" ? 0o755 : 0o644);
|
|
174
|
-
}
|
|
175
|
-
function actionFor(baseEntry, targetEntry) {
|
|
176
|
-
if (baseEntry === null && targetEntry !== null) {
|
|
177
|
-
return "add";
|
|
178
|
-
}
|
|
179
|
-
if (baseEntry !== null && targetEntry === null) {
|
|
180
|
-
return "delete";
|
|
181
|
-
}
|
|
182
|
-
return "update";
|
|
183
|
-
}
|
|
184
|
-
function printEntries(title, entries) {
|
|
185
|
-
if (entries.length === 0) {
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
console.log(`${title}:`);
|
|
189
|
-
for (const entry of entries) {
|
|
190
|
-
const detail = entry.reason ? ` (${entry.reason})` : "";
|
|
191
|
-
console.log(` ${entry.action}: ${entry.path}${detail}`);
|
|
192
|
-
}
|
|
193
|
-
console.log("");
|
|
194
|
-
}
|
|
195
|
-
export function fastForwardTemplateFiles(args) {
|
|
196
|
-
const repoRoot = getRepoRoot();
|
|
197
|
-
const base = args.base ?? readPackageCoreframeVersion(repoRoot);
|
|
198
|
-
const target = args.target;
|
|
199
|
-
const changes = listChangedPaths(base, target);
|
|
200
|
-
const clean = [];
|
|
201
|
-
const ignored = [];
|
|
202
|
-
const skipped = [];
|
|
203
|
-
const unchanged = [];
|
|
204
|
-
for (const change of changes) {
|
|
205
|
-
if (isProjectOwnedPath(change.path)) {
|
|
206
|
-
ignored.push({
|
|
207
|
-
action: "ignored",
|
|
208
|
-
path: change.path,
|
|
209
|
-
reason: "project-owned migration history",
|
|
210
|
-
});
|
|
211
|
-
continue;
|
|
212
|
-
}
|
|
213
|
-
const baseEntry = getGitEntry(base, change.path);
|
|
214
|
-
const targetEntry = getGitEntry(target, change.path);
|
|
215
|
-
const action = actionFor(baseEntry, targetEntry);
|
|
216
|
-
if (!entryIsSupported(baseEntry) || !entryIsSupported(targetEntry)) {
|
|
217
|
-
skipped.push({
|
|
218
|
-
action,
|
|
219
|
-
path: change.path,
|
|
220
|
-
reason: "unsupported template entry type",
|
|
221
|
-
});
|
|
222
|
-
continue;
|
|
223
|
-
}
|
|
224
|
-
const targetMatch = currentMatchesEntry(repoRoot, change.path, targetEntry);
|
|
225
|
-
if (targetMatch.matches) {
|
|
226
|
-
unchanged.push({ action: "unchanged", path: change.path });
|
|
227
|
-
continue;
|
|
228
|
-
}
|
|
229
|
-
const baseMatch = currentMatchesEntry(repoRoot, change.path, baseEntry);
|
|
230
|
-
if (!baseMatch.matches) {
|
|
231
|
-
skipped.push({
|
|
232
|
-
action,
|
|
233
|
-
path: change.path,
|
|
234
|
-
reason: baseMatch.reason ?? "downstream differs from base template",
|
|
235
|
-
});
|
|
236
|
-
continue;
|
|
237
|
-
}
|
|
238
|
-
clean.push({ action, path: change.path, targetEntry });
|
|
239
|
-
}
|
|
240
|
-
if (args.apply) {
|
|
241
|
-
for (const entry of clean) {
|
|
242
|
-
applyEntry(repoRoot, entry.path, entry.targetEntry ?? null);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
console.log(`Coreframe template fast-forward: ${base} -> ${target}`);
|
|
246
|
-
console.log("");
|
|
247
|
-
printEntries(args.apply ? "Applied clean template updates" : "Would apply clean template updates", clean);
|
|
248
|
-
printEntries("Ignored project-owned paths", ignored);
|
|
249
|
-
printEntries("Skipped for agent review", skipped);
|
|
250
|
-
if (unchanged.length > 0) {
|
|
251
|
-
console.log(`${unchanged.length} changed template path(s) already matched the target state.`);
|
|
252
|
-
}
|
|
253
|
-
if (changes.length === 0) {
|
|
254
|
-
console.log("No template file changes found in the requested range.");
|
|
255
|
-
}
|
|
256
|
-
if (!args.apply && clean.length > 0) {
|
|
257
|
-
console.log("");
|
|
258
|
-
console.log(`Run with --apply to write the ${clean.length} clean update(s).`);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
if (import.meta.main) {
|
|
262
|
-
const targetIndex = process.argv.indexOf("--target");
|
|
263
|
-
const target = targetIndex === -1 ? null : process.argv[targetIndex + 1];
|
|
264
|
-
if (!target) {
|
|
265
|
-
console.error("Usage: coreframe upgrade fast-forward-template-files --target <git-ref> [--base <git-ref>] [--apply]");
|
|
266
|
-
process.exit(64);
|
|
267
|
-
}
|
|
268
|
-
try {
|
|
269
|
-
const baseIndex = process.argv.indexOf("--base");
|
|
270
|
-
fastForwardTemplateFiles({
|
|
271
|
-
apply: process.argv.includes("--apply"),
|
|
272
|
-
base: baseIndex === -1 ? undefined : process.argv[baseIndex + 1],
|
|
273
|
-
target,
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
catch (error) {
|
|
277
|
-
console.error(error instanceof Error ? error.message : String(error));
|
|
278
|
-
process.exit(65);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
2
|
+
import{dirname as e,join as t,resolve as n,sep as r}from"node:path";import{chmodSync as i,existsSync as a,lstatSync as o,mkdirSync as s,readFileSync as c,readlinkSync as l,rmSync as u,symlinkSync as d,writeFileSync as f}from"node:fs";import{spawnSync as p}from"node:child_process";const m=[`drizzle/`,`drizzle-hold/`],h=new Set([`100644`,`100755`,`120000`]);function g(e,t={}){return p(`git`,e,t)}function _(e){let t=g(e,{encoding:`utf8`});if(t.status!==0){let n=t.stderr.toString().trim()||t.stdout.toString().trim();throw Error(`Git command failed: git ${e.join(` `)}${n?`\n${n}`:``}`)}return t.stdout.toString()}function v(e){let t=g(e);if(t.status!==0){let n=t.stderr.toString(`utf8`).trim()||t.stdout.toString(`utf8`).trim();throw Error(`Git command failed: git ${e.join(` `)}${n?`\n${n}`:``}`)}return t.stdout}function y(){return _([`rev-parse`,`--show-toplevel`]).trim()}function b(e){let n=t(e,`package.json`);if(!a(n))throw Error(`Missing --base <git-ref>, and package.json does not exist.`);let r=JSON.parse(c(n,`utf8`)).engines?.coreframe;if(typeof r!=`string`||r.length===0)throw Error(`Missing --base <git-ref>, and package.json engines.coreframe is not set.`);return r}function x(e){let t=[],n=0;for(let r=0;r<e.length;r+=1)e[r]===0&&(t.push(e.subarray(n,r).toString(`utf8`)),n=r+1);return t.filter(e=>e.length>0)}function S(e,t){let n=x(v([`diff`,`--name-status`,`-z`,`--no-renames`,e,t,`--`])),r=[];for(let e=0;e<n.length;e+=2){let t=n[e],i=n[e+1];if(!t||!i)throw Error(`Could not parse git diff --name-status output.`);r.push({path:i,status:t})}return r}function C(e,t){let n=v([`ls-tree`,`-z`,e,`--`,t]);if(n.length===0)return null;let r=x(n)[0],i=r.indexOf(` `);if(i===-1)throw Error(`Could not parse git ls-tree output for ${e}:${t}.`);let[a,o,s]=r.slice(0,i).split(` `);if(!a||!o||!s)throw Error(`Could not parse git ls-tree metadata for ${e}:${t}.`);return{mode:a,object:s,path:t,type:o}}function w(e){return v([`cat-file`,`blob`,e.object])}function T(e,t){let i=n(e,t),a=e.endsWith(r)?e:`${e}${r}`;if(i!==e&&!i.startsWith(a))throw Error(`Refusing to access path outside repository: ${t}`);return i}function E(e){try{return o(e),!0}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return!1;throw e}}function D(e){return(e.mode&73)!=0}function O(e){return e===null||e.type===`blob`&&h.has(e.mode)}function k(e){return m.some(t=>e===t.slice(0,-1)||e.startsWith(t))}function A(e,t,n){let r=T(e,t);if(n===null)return{matches:!E(r)};if(!O(n))return{matches:!1,reason:`unsupported template entry mode ${n.mode} ${n.type}`};let i;try{i=o(r)}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return{matches:!1,reason:`path is missing from worktree`};throw e}let a=w(n);return n.mode===`120000`?i.isSymbolicLink()?{matches:Buffer.from(l(r),`utf8`).equals(a)}:{matches:!1,reason:`worktree path is not a symlink`}:i.isFile()?(n.mode===`100755`?D(i):!D(i))?{matches:c(r).equals(a)}:{matches:!1,reason:`worktree executable bit differs from template`}:{matches:!1,reason:`worktree path is not a regular file`}}function j(e){E(e)&&u(e,{force:!0,recursive:!1})}function M(t,n,r){let a=T(t,n);if(r===null){j(a);return}if(s(e(a),{recursive:!0}),r.mode===`120000`){j(a),d(w(r).toString(`utf8`),a);return}let c=E(a)?o(a):null;c&&!c.isFile()&&j(a),f(a,w(r)),i(a,r.mode===`100755`?493:420)}function N(e,t){return e===null&&t!==null?`add`:e!==null&&t===null?`delete`:`update`}function P(e,t){if(t.length!==0){console.log(`${e}:`);for(let e of t){let t=e.reason?` (${e.reason})`:``;console.log(` ${e.action}: ${e.path}${t}`)}console.log(``)}}function F(e){let t=y(),n=e.base??b(t),r=e.target,i=S(n,r),a=[],o=[],s=[],c=[];for(let e of i){if(k(e.path)){o.push({action:`ignored`,path:e.path,reason:`project-owned migration history`});continue}let i=C(n,e.path),l=C(r,e.path),u=N(i,l);if(!O(i)||!O(l)){s.push({action:u,path:e.path,reason:`unsupported template entry type`});continue}if(A(t,e.path,l).matches){c.push({action:`unchanged`,path:e.path});continue}let d=A(t,e.path,i);if(!d.matches){s.push({action:u,path:e.path,reason:d.reason??`downstream differs from base template`});continue}a.push({action:u,path:e.path,targetEntry:l})}if(e.apply)for(let e of a)M(t,e.path,e.targetEntry??null);console.log(`Coreframe template fast-forward: ${n} -> ${r}`),console.log(``),P(e.apply?`Applied clean template updates`:`Would apply clean template updates`,a),P(`Ignored project-owned paths`,o),P(`Skipped for agent review`,s),c.length>0&&console.log(`${c.length} changed template path(s) already matched the target state.`),i.length===0&&console.log(`No template file changes found in the requested range.`),!e.apply&&a.length>0&&(console.log(``),console.log(`Run with --apply to write the ${a.length} clean update(s).`))}if(import.meta.main){let e=process.argv.indexOf(`--target`),t=e===-1?null:process.argv[e+1];t||(console.error(`Usage: coreframe upgrade fast-forward-template-files --target <git-ref> [--base <git-ref>] [--apply]`),process.exit(64));try{let e=process.argv.indexOf(`--base`);F({apply:process.argv.includes(`--apply`),base:e===-1?void 0:process.argv[e+1],target:t})}catch(e){console.error(e instanceof Error?e.message:String(e)),process.exit(65)}}export{F as fastForwardTemplateFiles};
|
package/package.json
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coreframe/scripts",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"bin": {
|
|
5
5
|
"coreframe": "./bin/coreframe.js"
|
|
6
6
|
},
|
|
7
|
+
"files": [
|
|
8
|
+
"bin/",
|
|
9
|
+
"dist/"
|
|
10
|
+
],
|
|
7
11
|
"type": "module",
|
|
8
12
|
"repository": {
|
|
9
13
|
"type": "git",
|
|
@@ -17,7 +21,7 @@
|
|
|
17
21
|
}
|
|
18
22
|
},
|
|
19
23
|
"scripts": {
|
|
20
|
-
"build": "
|
|
24
|
+
"build": "tsdown",
|
|
21
25
|
"test": "vitest run",
|
|
22
26
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
23
27
|
"format": "oxfmt",
|
|
@@ -34,6 +38,7 @@
|
|
|
34
38
|
"devDependencies": {
|
|
35
39
|
"@types/node": "^26.1.0",
|
|
36
40
|
"sharp": "^0.34.5",
|
|
41
|
+
"tsdown": "0.22.3",
|
|
37
42
|
"typescript": "6.0.3",
|
|
38
43
|
"vite": "^8.1.0",
|
|
39
44
|
"vitest": "^4.1.9"
|
package/config.test.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest"
|
|
2
|
-
|
|
3
|
-
import { defineConfig } from "./config.ts"
|
|
4
|
-
|
|
5
|
-
describe("defineConfig", () => {
|
|
6
|
-
it("returns a typed Coreframe config object", () => {
|
|
7
|
-
const config = defineConfig({
|
|
8
|
-
deploy: {
|
|
9
|
-
smokeChecks: [{ name: "home", path: "/" }],
|
|
10
|
-
},
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
expect(config.deploy?.smokeChecks).toEqual([{ name: "home", path: "/" }])
|
|
14
|
-
})
|
|
15
|
-
})
|