Skip to content

Commit e524f20

Browse files
committed
fix(cli): quiet update and sort links
1 parent 845706a commit e524f20

4 files changed

Lines changed: 135 additions & 23 deletions

File tree

defaults/v8s-links.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# slug|target|state|title|description|tags|owner|expires_at|notes
2-
home|https://example.com|permanent|Home|Primary website|core|owner||
2+
33
contact|https://www.youtube.com/watch?v=dQw4w9WgXcQ|permanent|Contact|Scheduled contact example|contact,schedule|owner||
44
@schedule timezone=America/New_York
55
@schedule 9to5=https://www.youtube.com/watch?v=UbxUSsFXYo4
66
docs|https://www.vanityurls.link/en/docs/|permanent|Docs|vanityURLs documentation|docs|owner||
7+
home|https://example.com|permanent|Home|Primary website|core|owner||

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"test:registry": "node scripts/registry.test.mjs",
3333
"test:worker": "node scripts/workers/worker.test.mjs",
3434
"upgrade": "node scripts/upgrade.mjs",
35-
"update": "npm run upgrade",
35+
"update": "node scripts/upgrade.mjs",
3636
"validate": "npm run validate:all",
3737
"validate:all": "npm run validate:registry",
3838
"validate:registry": "node scripts/validate-registry.mjs build/v8s.json",

scripts/lint.mjs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ function walk(directory) {
2424
}
2525

2626
function lintFile(filePath) {
27+
if (path.basename(filePath) === "v8s-links.txt") {
28+
sortV8sLinksFile(filePath);
29+
}
30+
2731
const text = fs.readFileSync(filePath, "utf8");
2832
const relative = path.relative(ROOT, filePath);
2933

@@ -63,6 +67,71 @@ function lintFile(filePath) {
6367
}
6468
}
6569

70+
function sortV8sLinksFile(filePath) {
71+
const text = fs.readFileSync(filePath, "utf8");
72+
const sorted = sortedV8sLinksText(text);
73+
if (sorted !== text) {
74+
fs.writeFileSync(filePath, sorted);
75+
}
76+
}
77+
78+
function sortedV8sLinksText(text) {
79+
const lines = text.replace(/\r\n/g, "\n").split("\n");
80+
if (lines.at(-1) === "") lines.pop();
81+
82+
const header = [];
83+
const blocks = [];
84+
let current = null;
85+
let seenLink = false;
86+
87+
for (const line of lines) {
88+
const trimmed = line.trim();
89+
90+
if (!seenLink && (!trimmed || trimmed.startsWith("#"))) {
91+
header.push(line);
92+
continue;
93+
}
94+
95+
if (!trimmed) continue;
96+
97+
if (/^\s+@schedule\b/.test(line) && current) {
98+
current.lines.push(line);
99+
continue;
100+
}
101+
102+
const slug = trimmed.startsWith("#") ? `~${blocks.length}` : normalizeLinkSlug(line.split("|")[0]);
103+
current = {
104+
slug,
105+
lines: [line]
106+
};
107+
blocks.push(current);
108+
if (!trimmed.startsWith("#")) seenLink = true;
109+
}
110+
111+
const sortedBlocks = blocks.sort((left, right) => left.slug.localeCompare(right.slug));
112+
const output = [...trimTrailingBlankLines(header)];
113+
if (output.length && sortedBlocks.length) output.push("");
114+
115+
for (const block of sortedBlocks) {
116+
output.push(...block.lines);
117+
}
118+
119+
return `${output.join("\n")}\n`;
120+
}
121+
122+
function normalizeLinkSlug(value) {
123+
return String(value || "")
124+
.trim()
125+
.replace(/^\/+/, "")
126+
.replace(/\/+$/, "");
127+
}
128+
129+
function trimTrailingBlankLines(lines) {
130+
const trimmed = [...lines];
131+
while (trimmed.length && !trimmed.at(-1).trim()) trimmed.pop();
132+
return trimmed;
133+
}
134+
66135
function lintWrangler() {
67136
const wranglerPath = path.join(ROOT, "wrangler.toml");
68137
const text = fs.readFileSync(wranglerPath, "utf8");

scripts/upgrade.mjs

Lines changed: 63 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33
import fs from "node:fs";
44
import os from "node:os";
55
import path from "node:path";
6-
import { execFileSync, spawnSync } from "node:child_process";
6+
import { spawnSync } from "node:child_process";
77

88
const ROOT = process.cwd();
99
const DEFAULT_REMOTE = "https://github.com/vanityurls/code.git";
1010
const DEFAULT_REF = "main";
1111
const DEFAULT_PATHS = ["defaults", "scripts", "package.json", "package-lock.json", "LICENSE"];
1212
const PROTECTED_PATHS = ["custom", "wrangler.toml", ".dev.vars", "README.md"];
13+
const GENERATED_PATHS = ["build", "functions", "src"];
1314

1415
function parseArgs(argv) {
1516
const args = {
@@ -95,7 +96,7 @@ Options:
9596
--paths <a,b> Product-owned paths to replace. Default: ${DEFAULT_PATHS.join(",")}
9697
--path <path> Add one product-owned path to replace
9798
--dry-run Show what would happen without changing files
98-
--no-check Skip npm run check after syncing
99+
--no-check Skip upgrade verification after syncing
99100
--no-clean Skip npm run clean before syncing
100101
--allow-dirty Allow a dirty worktree before upgrade
101102
`);
@@ -113,13 +114,18 @@ function run(command, args, options = {}) {
113114
});
114115

115116
if (result.status !== 0) {
116-
const stderr = result.stderr ? `\n${result.stderr.trim()}` : "";
117-
throw new Error(`${command} ${args.join(" ")} failed${stderr}`);
117+
throw commandError(command, args, result);
118118
}
119119

120120
return result.stdout || "";
121121
}
122122

123+
function commandError(command, args, result) {
124+
const stdout = result.stdout ? `\nstdout:\n${result.stdout.trim()}` : "";
125+
const stderr = result.stderr ? `\nstderr:\n${result.stderr.trim()}` : "";
126+
return new Error(`${command} ${args.join(" ")} failed${stdout}${stderr}`);
127+
}
128+
123129
function git(args, options) {
124130
return run("git", args, options);
125131
}
@@ -162,21 +168,52 @@ function resolveSource(args) {
162168
return "HEAD";
163169
}
164170

165-
git(["fetch", "--depth=1", remote, args.ref]);
171+
console.log(`[fetch] ${remoteLabel(remote)} ${args.ref}`);
172+
git(["fetch", "--depth=1", remote, args.ref], { capture: true });
166173
return "FETCH_HEAD";
167174
}
168175

169176
function clean(args) {
170177
if (!args.clean) return;
171178
if (args.dryRun) {
172-
console.log("[dry-run] would run npm run clean");
179+
console.log(`[dry-run] would remove ${GENERATED_PATHS.map((entry) => `${entry}/`).join(", ")}`);
173180
return;
174181
}
175182

176-
execFileSync("npm", ["run", "clean"], {
183+
for (const relativePath of GENERATED_PATHS) {
184+
fs.rmSync(path.join(ROOT, relativePath), {
185+
recursive: true,
186+
force: true
187+
});
188+
}
189+
console.log(`[clean] Removed ${GENERATED_PATHS.map((entry) => `${entry}/`).join(", ")}`);
190+
}
191+
192+
function remoteLabel(remote) {
193+
const cleanRemote = String(remote || "").replace(/\.git$/i, "");
194+
const githubHttps = cleanRemote.match(/^https?:\/\/github\.com\/(.+)$/i);
195+
if (githubHttps) return `github.com/${githubHttps[1]}`;
196+
const githubSsh = cleanRemote.match(/^git@github\.com:(.+)$/i);
197+
if (githubSsh) return `github.com/${githubSsh[1]}`;
198+
return remote;
199+
}
200+
201+
function runQuiet(command, args) {
202+
const result = spawnSync(command, args, {
177203
cwd: ROOT,
178-
stdio: "inherit"
204+
encoding: "utf8",
205+
env: {
206+
...process.env,
207+
LC_ALL: "C"
208+
},
209+
stdio: "pipe"
179210
});
211+
212+
if (result.status !== 0) {
213+
throw commandError(command, args, result);
214+
}
215+
216+
return result.stdout || "";
180217
}
181218

182219
function extractSource(source, paths) {
@@ -228,28 +265,29 @@ function syncPaths(args, source) {
228265
function runCheck(args) {
229266
if (!args.check) return;
230267
if (args.dryRun) {
231-
console.log("[dry-run] would run npm run check");
268+
console.log("[dry-run] would run upgrade verification");
232269
return;
233270
}
234271

235-
execFileSync("npm", ["run", "check"], {
236-
cwd: ROOT,
237-
stdio: "inherit"
238-
});
272+
const buildOutput = runQuiet("npm", ["run", "build"], "build");
273+
const linkCount = buildOutput.match(/Wrote build\/v8s\.json with (\d+) links/)?.[1] || "unknown";
274+
console.log(`[build] Built v8s-blocklist.json and v8s.json with ${linkCount} links`);
275+
276+
runQuiet(process.execPath, ["scripts/registry.test.mjs"], "registry tests");
277+
console.log("[test] registry tests ok");
278+
runQuiet(process.execPath, ["scripts/install.test.mjs"], "install tests");
279+
console.log("[test] install tests ok");
280+
runQuiet(process.execPath, ["scripts/maintenance.test.mjs"], "maintenance tests");
281+
console.log("[test] maintenance tests ok");
239282
}
240283

241284
function printSummary(args, source, result) {
242-
console.log("\nUpgrade summary");
243-
console.log(`Source: ${source}`);
244-
console.log(`Synced: ${result.synced.length ? result.synced.join(", ") : "none"}`);
245-
if (result.missing.length) console.log(`Missing upstream paths: ${result.missing.join(", ")}`);
285+
console.log(`Summary: Synced ${result.synced.length ? result.synced.join(", ") : "none"}`);
286+
if (result.missing.length) console.log(`Summary: Missing upstream paths: ${result.missing.join(", ")}`);
246287

247288
if (!args.dryRun) {
248289
const status = worktreeStatus();
249-
console.log("\nReview with:");
250-
console.log(" git status --short");
251-
console.log(" git diff");
252-
if (status) console.log("\nCommit after review and successful checks.");
290+
if (status) console.log("Review with git status --short and git diff, then commit and push.");
253291
}
254292
}
255293

@@ -262,6 +300,10 @@ async function main() {
262300

263301
const source = resolveSource(args);
264302
const result = syncPaths(args, source);
303+
if (!args.dryRun) {
304+
console.log(`[sync] Synced ${result.synced.length ? result.synced.join(", ") : "none"}`);
305+
if (result.missing.length) console.log(`[sync] Missing upstream paths: ${result.missing.join(", ")}`);
306+
}
265307

266308
runCheck(args);
267309
printSummary(args, source, result);

0 commit comments

Comments
 (0)