bod-cli 0.5.2 → 0.5.5
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/cli.js +80 -2
- package/package.json +1 -1
- package/src/commands/deploy.ts +88 -2
package/dist/cli.js
CHANGED
|
@@ -18976,7 +18976,77 @@ async function uploadDeploy(client, appId, branch) {
|
|
|
18976
18976
|
console.log(source_default.dim(`Including siblings: ${siblingPaths.join(", ")}`));
|
|
18977
18977
|
console.log(source_default.dim(`Packing source (excluding: ${allExcludes.join(", ")})...`));
|
|
18978
18978
|
const tmpFile = `${Bun.env.TMPDIR || "/tmp"}/bod-deploy-${Date.now()}.tar.gz`;
|
|
18979
|
-
const
|
|
18979
|
+
const env3 = { ...process.env, COPYFILE_DISABLE: "1" };
|
|
18980
|
+
const fs = __require("fs");
|
|
18981
|
+
const { resolve, basename, join: join3 } = __require("path");
|
|
18982
|
+
const siblingMap = new Map;
|
|
18983
|
+
const siblingArgs = [];
|
|
18984
|
+
for (const sp of siblingPaths) {
|
|
18985
|
+
const abs = resolve(sp);
|
|
18986
|
+
const name = basename(abs);
|
|
18987
|
+
siblingMap.set(sp, name);
|
|
18988
|
+
siblingArgs.push("-C", abs.slice(0, abs.lastIndexOf("/")), name);
|
|
18989
|
+
}
|
|
18990
|
+
const rewritten = [];
|
|
18991
|
+
const rewritePkg = (pkgPath) => {
|
|
18992
|
+
if (!fs.existsSync(pkgPath))
|
|
18993
|
+
return;
|
|
18994
|
+
const original = fs.readFileSync(pkgPath, "utf8");
|
|
18995
|
+
let content = original;
|
|
18996
|
+
for (const [rel, name] of siblingMap) {
|
|
18997
|
+
content = content.replace(new RegExp(`"file:[^"]*${name}"`, "g"), `"file:./${name}"`);
|
|
18998
|
+
}
|
|
18999
|
+
if (content !== original) {
|
|
19000
|
+
rewritten.push({ path: pkgPath, original });
|
|
19001
|
+
fs.writeFileSync(pkgPath, content);
|
|
19002
|
+
}
|
|
19003
|
+
};
|
|
19004
|
+
rewritePkg(join3(process.cwd(), "package.json"));
|
|
19005
|
+
try {
|
|
19006
|
+
const rootPkg = JSON.parse(fs.readFileSync(join3(process.cwd(), "package.json"), "utf8"));
|
|
19007
|
+
for (const pattern of rootPkg.workspaces ?? []) {
|
|
19008
|
+
const base = join3(process.cwd(), pattern.replace(/\/?\*$/, ""));
|
|
19009
|
+
if (!fs.existsSync(base))
|
|
19010
|
+
continue;
|
|
19011
|
+
for (const sub of fs.readdirSync(base)) {
|
|
19012
|
+
rewritePkg(join3(base, sub, "package.json"));
|
|
19013
|
+
}
|
|
19014
|
+
}
|
|
19015
|
+
} catch {}
|
|
19016
|
+
const includedSiblings = new Set([...siblingMap.values()]);
|
|
19017
|
+
for (const sp of siblingPaths) {
|
|
19018
|
+
const abs = resolve(sp);
|
|
19019
|
+
const sibPkgPath = join3(abs, "package.json");
|
|
19020
|
+
if (!fs.existsSync(sibPkgPath))
|
|
19021
|
+
continue;
|
|
19022
|
+
const original = fs.readFileSync(sibPkgPath, "utf8");
|
|
19023
|
+
const sibPkg = JSON.parse(original);
|
|
19024
|
+
let changed = false;
|
|
19025
|
+
for (const field of ["dependencies", "devDependencies"]) {
|
|
19026
|
+
if (!sibPkg[field])
|
|
19027
|
+
continue;
|
|
19028
|
+
for (const [name, ver] of Object.entries(sibPkg[field])) {
|
|
19029
|
+
if (!ver.startsWith("file:") || !ver.includes(".."))
|
|
19030
|
+
continue;
|
|
19031
|
+
const depName = basename(resolve(abs, ver.slice(5)));
|
|
19032
|
+
if (!includedSiblings.has(depName)) {
|
|
19033
|
+
sibPkg[field][name] = "*";
|
|
19034
|
+
changed = true;
|
|
19035
|
+
}
|
|
19036
|
+
}
|
|
19037
|
+
}
|
|
19038
|
+
if (changed) {
|
|
19039
|
+
rewritten.push({ path: sibPkgPath, original });
|
|
19040
|
+
fs.writeFileSync(sibPkgPath, JSON.stringify(sibPkg, null, 2) + `
|
|
19041
|
+
`);
|
|
19042
|
+
}
|
|
19043
|
+
}
|
|
19044
|
+
const tar = Bun.spawnSync(["tar", "czh", ...excludeFlags, "-f", tmpFile, ".", ...siblingArgs], {
|
|
19045
|
+
stderr: "pipe",
|
|
19046
|
+
env: env3
|
|
19047
|
+
});
|
|
19048
|
+
for (const { path, original } of rewritten)
|
|
19049
|
+
fs.writeFileSync(path, original);
|
|
18980
19050
|
if (tar.exitCode !== 0) {
|
|
18981
19051
|
const errText = tar.stderr.toString();
|
|
18982
19052
|
throw new Error(`tar failed: ${errText}`);
|
|
@@ -19047,6 +19117,14 @@ async function pollDeploy(client, appId, since, instanceCaps, localConfig) {
|
|
|
19047
19117
|
} else {
|
|
19048
19118
|
displayUrl = `https://${host}`;
|
|
19049
19119
|
}
|
|
19120
|
+
if (!isPreview) {
|
|
19121
|
+
try {
|
|
19122
|
+
const domains = await client.get(`/apps/${appId}/domains`);
|
|
19123
|
+
const verified = domains?.find((d2) => d2.status === "active" || d2.verified);
|
|
19124
|
+
if (verified)
|
|
19125
|
+
displayUrl = `https://${verified.domain}`;
|
|
19126
|
+
} catch {}
|
|
19127
|
+
}
|
|
19050
19128
|
console.log(` → ${displayUrl}`);
|
|
19051
19129
|
if (isPreview)
|
|
19052
19130
|
console.log(source_default.yellow(` ⚠ Preview deployment (branch: ${latest.branch})`));
|
|
@@ -19073,7 +19151,7 @@ var deploy_default = defineCommand2({
|
|
|
19073
19151
|
const { url, apiKey, name: instanceName, instance } = getResolvedInstance(loadConfig(), readInstanceFromYaml());
|
|
19074
19152
|
console.log(source_default.dim(`Using instance "${instanceName}"`));
|
|
19075
19153
|
const client = new BodClient(url, apiKey);
|
|
19076
|
-
const isLocal = url.startsWith("
|
|
19154
|
+
const isLocal = !url.startsWith("https://");
|
|
19077
19155
|
let localConfig = null;
|
|
19078
19156
|
if (isLocal) {
|
|
19079
19157
|
try {
|
package/package.json
CHANGED
package/src/commands/deploy.ts
CHANGED
|
@@ -29,7 +29,83 @@ async function uploadDeploy(client: BodClient, appId: string, branch: string) {
|
|
|
29
29
|
|
|
30
30
|
console.log(chalk.dim(`Packing source (excluding: ${allExcludes.join(', ')})...`))
|
|
31
31
|
const tmpFile = `${Bun.env.TMPDIR || '/tmp'}/bod-deploy-${Date.now()}.tar.gz`
|
|
32
|
-
const
|
|
32
|
+
const env = { ...process.env, COPYFILE_DISABLE: '1' } // suppress macOS ._ resource forks
|
|
33
|
+
const fs = require('fs')
|
|
34
|
+
const { resolve, basename, join } = require('path')
|
|
35
|
+
|
|
36
|
+
// Siblings use `../` relative paths which GNU tar on the server rejects.
|
|
37
|
+
// Strategy: pack siblings at root level (no `..`), rewrite file:../X → file:./X in package.jsons.
|
|
38
|
+
const siblingMap = new Map<string, string>() // originalRelPath → basename
|
|
39
|
+
const siblingArgs: string[] = []
|
|
40
|
+
for (const sp of siblingPaths) {
|
|
41
|
+
const abs = resolve(sp)
|
|
42
|
+
const name = basename(abs)
|
|
43
|
+
siblingMap.set(sp, name)
|
|
44
|
+
siblingArgs.push('-C', abs.slice(0, abs.lastIndexOf('/')), name)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Rewrite file:.. deps in package.json files so the server finds siblings at ./
|
|
48
|
+
const rewritten: { path: string; original: string }[] = []
|
|
49
|
+
const rewritePkg = (pkgPath: string) => {
|
|
50
|
+
if (!fs.existsSync(pkgPath)) return
|
|
51
|
+
const original = fs.readFileSync(pkgPath, 'utf8')
|
|
52
|
+
let content = original
|
|
53
|
+
for (const [rel, name] of siblingMap) {
|
|
54
|
+
// Rewrite file:../X to file:./X (X is the sibling basename)
|
|
55
|
+
content = content.replace(new RegExp(`"file:[^"]*${name}"`, 'g'), `"file:./${name}"`)
|
|
56
|
+
}
|
|
57
|
+
if (content !== original) {
|
|
58
|
+
rewritten.push({ path: pkgPath, original })
|
|
59
|
+
fs.writeFileSync(pkgPath, content)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Rewrite root + workspace package.jsons (file:../X → file:./X for included siblings)
|
|
64
|
+
rewritePkg(join(process.cwd(), 'package.json'))
|
|
65
|
+
try {
|
|
66
|
+
const rootPkg = JSON.parse(fs.readFileSync(join(process.cwd(), 'package.json'), 'utf8'))
|
|
67
|
+
for (const pattern of rootPkg.workspaces ?? []) {
|
|
68
|
+
const base = join(process.cwd(), pattern.replace(/\/?\*$/, ''))
|
|
69
|
+
if (!fs.existsSync(base)) continue
|
|
70
|
+
for (const sub of fs.readdirSync(base)) {
|
|
71
|
+
rewritePkg(join(base, sub, 'package.json'))
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
} catch {}
|
|
75
|
+
|
|
76
|
+
// Rewrite sibling package.jsons: file:.. deps that aren't included become "*" (npm fallback)
|
|
77
|
+
const includedSiblings = new Set([...siblingMap.values()])
|
|
78
|
+
for (const sp of siblingPaths) {
|
|
79
|
+
const abs = resolve(sp)
|
|
80
|
+
const sibPkgPath = join(abs, 'package.json')
|
|
81
|
+
if (!fs.existsSync(sibPkgPath)) continue
|
|
82
|
+
const original = fs.readFileSync(sibPkgPath, 'utf8')
|
|
83
|
+
const sibPkg = JSON.parse(original)
|
|
84
|
+
let changed = false
|
|
85
|
+
for (const field of ['dependencies', 'devDependencies']) {
|
|
86
|
+
if (!sibPkg[field]) continue
|
|
87
|
+
for (const [name, ver] of Object.entries(sibPkg[field]) as [string, string][]) {
|
|
88
|
+
if (!ver.startsWith('file:') || !ver.includes('..')) continue
|
|
89
|
+
const depName = basename(resolve(abs, ver.slice(5)))
|
|
90
|
+
if (!includedSiblings.has(depName)) {
|
|
91
|
+
sibPkg[field][name] = '*'
|
|
92
|
+
changed = true
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (changed) {
|
|
97
|
+
rewritten.push({ path: sibPkgPath, original })
|
|
98
|
+
fs.writeFileSync(sibPkgPath, JSON.stringify(sibPkg, null, 2) + '\n')
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const tar = Bun.spawnSync(['tar', 'czh', ...excludeFlags, '-f', tmpFile, '.', ...siblingArgs], {
|
|
103
|
+
stderr: 'pipe',
|
|
104
|
+
env,
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
// Restore original package.json files
|
|
108
|
+
for (const { path, original } of rewritten) fs.writeFileSync(path, original)
|
|
33
109
|
if (tar.exitCode !== 0) {
|
|
34
110
|
const errText = tar.stderr.toString()
|
|
35
111
|
throw new Error(`tar failed: ${errText}`)
|
|
@@ -112,6 +188,16 @@ async function pollDeploy(client: BodClient, appId: string, since: number, insta
|
|
|
112
188
|
} else {
|
|
113
189
|
displayUrl = `https://${host}`
|
|
114
190
|
}
|
|
191
|
+
|
|
192
|
+
// Show custom domain if configured (production branch only)
|
|
193
|
+
if (!isPreview) {
|
|
194
|
+
try {
|
|
195
|
+
const domains = await client.get<any[]>(`/apps/${appId}/domains`)
|
|
196
|
+
const verified = domains?.find((d: any) => d.status === 'active' || d.verified)
|
|
197
|
+
if (verified) displayUrl = `https://${verified.domain}`
|
|
198
|
+
} catch {}
|
|
199
|
+
}
|
|
200
|
+
|
|
115
201
|
console.log(` → ${displayUrl}`)
|
|
116
202
|
if (isPreview) console.log(chalk.yellow(` ⚠ Preview deployment (branch: ${latest.branch})`))
|
|
117
203
|
}
|
|
@@ -138,7 +224,7 @@ export default defineCommand({
|
|
|
138
224
|
console.log(chalk.dim(`Using instance "${instanceName}"`))
|
|
139
225
|
const client = new BodClient(url, apiKey)
|
|
140
226
|
// Fetch local config for URL generation
|
|
141
|
-
const isLocal = url.startsWith('
|
|
227
|
+
const isLocal = !url.startsWith('https://')
|
|
142
228
|
let localConfig: { localDomain?: string; caddyPort?: number } | null = null
|
|
143
229
|
if (isLocal) {
|
|
144
230
|
try { localConfig = await client.get<any>('/config/public') } catch {}
|