@mirinjs/cli 0.0.1-alpha.1 → 0.0.1-alpha.11
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/package.json +3 -3
- package/src/artifacts.ts +10 -6
- package/src/build.ts +41 -4
- package/src/bundle.ts +80 -21
- package/src/dev.ts +5 -0
- package/src/index.ts +8 -1
- package/src/release.ts +85 -0
- package/src/temps.ts +24 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mirinjs/cli",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.11",
|
|
4
4
|
"description": "CLI for mirin apps: dev, build, init.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
"bun": ">=1.2.0"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"create-mirinjs": "0.0.1-alpha.
|
|
30
|
+
"create-mirinjs": "0.0.1-alpha.11"
|
|
31
31
|
},
|
|
32
32
|
"optionalDependencies": {
|
|
33
|
-
"@mirinjs/darwin-arm64": "0.0.1-alpha.
|
|
33
|
+
"@mirinjs/darwin-arm64": "0.0.1-alpha.11"
|
|
34
34
|
},
|
|
35
35
|
"publishConfig": {
|
|
36
36
|
"access": "public"
|
package/src/artifacts.ts
CHANGED
|
@@ -118,13 +118,17 @@ async function ensureCef(): Promise<string> {
|
|
|
118
118
|
const url = `https://github.com/${REPO_SLUG}/releases/download/v${version}/${asset}`;
|
|
119
119
|
console.log(`[mirin] downloading CEF for ${platformTag()} (one-time, ~hundreds of MB)…`);
|
|
120
120
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
121
|
+
// Download with curl, not `Bun.write(file, await fetch(url))`: the latter
|
|
122
|
+
// pins a core at 100% CPU and never completes on large gzip responses
|
|
123
|
+
// (the streaming write path). curl is fast and ubiquitous.
|
|
125
124
|
const archive = join(tmpdir(), asset);
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
try {
|
|
126
|
+
await $`curl -fSL --retry 3 -o ${archive} ${url}`;
|
|
127
|
+
await $`tar -xzf ${archive} -C ${cacheDir}`;
|
|
128
|
+
} catch (err) {
|
|
129
|
+
rmSync(archive, { force: true });
|
|
130
|
+
throw new Error(`mirin: failed to download CEF from ${url} (${err}).`);
|
|
131
|
+
}
|
|
128
132
|
rmSync(archive, { force: true });
|
|
129
133
|
return cacheDir;
|
|
130
134
|
}
|
package/src/build.ts
CHANGED
|
@@ -16,23 +16,52 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import { $ } from "bun";
|
|
19
|
-
import { mkdirSync, rmSync } from "node:fs";
|
|
19
|
+
import { mkdirSync, rmSync, readFileSync, existsSync } from "node:fs";
|
|
20
20
|
import { join } from "node:path";
|
|
21
21
|
import { buildAppBundle } from "./bundle.ts";
|
|
22
22
|
import { resolveArtifacts } from "./artifacts.ts";
|
|
23
|
+
import { sweepBuildTemps } from "./temps.ts";
|
|
23
24
|
|
|
24
|
-
export
|
|
25
|
+
export interface BuildResult {
|
|
26
|
+
/** Path to the assembled .app. */
|
|
27
|
+
app: string;
|
|
28
|
+
appName: string;
|
|
29
|
+
bundleId: string;
|
|
30
|
+
/** App version (from the project's package.json). */
|
|
31
|
+
version: string;
|
|
32
|
+
/** Update channel (config.release.channel ?? "stable"). */
|
|
33
|
+
channel: string;
|
|
34
|
+
/** Update baseUrl, if `release` is configured. */
|
|
35
|
+
baseUrl?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Read the project's package.json version (the single source of app version). */
|
|
39
|
+
function appVersion(projectDir: string): string {
|
|
40
|
+
const pkgPath = join(projectDir, "package.json");
|
|
41
|
+
if (!existsSync(pkgPath)) return "0.0.0";
|
|
42
|
+
try {
|
|
43
|
+
return JSON.parse(readFileSync(pkgPath, "utf8")).version ?? "0.0.0";
|
|
44
|
+
} catch {
|
|
45
|
+
return "0.0.0";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function build(projectDir = process.cwd()): Promise<BuildResult> {
|
|
25
50
|
const outDir = join(projectDir, "build");
|
|
26
51
|
const work = join(projectDir, ".mirin");
|
|
27
52
|
mkdirSync(outDir, { recursive: true });
|
|
28
53
|
mkdirSync(work, { recursive: true });
|
|
54
|
+
sweepBuildTemps(projectDir);
|
|
29
55
|
|
|
30
56
|
const config = (await import(join(projectDir, "mirin.config.ts"))).default;
|
|
31
57
|
const appName: string = config.name ?? "Mirin App";
|
|
32
58
|
const bundleId: string = config.id ?? "dev.mirin.app";
|
|
33
59
|
const mainEntry = join(projectDir, config.main ?? "main/main.ts");
|
|
60
|
+
const version = appVersion(projectDir);
|
|
61
|
+
const channel: string = config.release?.channel ?? "stable";
|
|
62
|
+
const baseUrl: string | undefined = config.release?.baseUrl;
|
|
34
63
|
|
|
35
|
-
console.log(`[mirin build] ${appName}`);
|
|
64
|
+
console.log(`[mirin build] ${appName} ${version}`);
|
|
36
65
|
|
|
37
66
|
// 1. production UI
|
|
38
67
|
console.log("[mirin build] vite build…");
|
|
@@ -51,6 +80,11 @@ export async function build(projectDir = process.cwd()): Promise<number> {
|
|
|
51
80
|
// 5. assemble + sign
|
|
52
81
|
console.log("[mirin build] assembling .app…");
|
|
53
82
|
rmSync(join(outDir, `${appName}.app`), { recursive: true, force: true });
|
|
83
|
+
// version.json embeds the running app's update identity (read by app.updater).
|
|
84
|
+
// Only when `release` is configured — otherwise the app has no updater.
|
|
85
|
+
const versionJson = baseUrl
|
|
86
|
+
? JSON.stringify({ version, channel, baseUrl, name: appName, identifier: bundleId })
|
|
87
|
+
: undefined;
|
|
54
88
|
const { app } = await buildAppBundle({
|
|
55
89
|
appName,
|
|
56
90
|
bundleId,
|
|
@@ -59,15 +93,18 @@ export async function build(projectDir = process.cwd()): Promise<number> {
|
|
|
59
93
|
coreDylib: artifacts.coreDylib,
|
|
60
94
|
helperBin: artifacts.helperBin,
|
|
61
95
|
cefPath: artifacts.cefPath,
|
|
96
|
+
version,
|
|
97
|
+
icon: config.icon ? join(projectDir, config.icon) : undefined,
|
|
62
98
|
signIdentity: process.env.MIRIN_SIGN_IDENTITY,
|
|
63
99
|
resources: {
|
|
64
100
|
uiDir: join(projectDir, "dist"),
|
|
65
101
|
workerJs,
|
|
66
102
|
manifestJson: JSON.stringify({ windows: config.windows }),
|
|
103
|
+
versionJson,
|
|
67
104
|
},
|
|
68
105
|
});
|
|
69
106
|
|
|
70
107
|
console.log(`\n[mirin build] done → ${app}`);
|
|
71
108
|
console.log(` open "${app}"`);
|
|
72
|
-
return
|
|
109
|
+
return { app, appName, bundleId, version, channel, baseUrl };
|
|
73
110
|
}
|
package/src/bundle.ts
CHANGED
|
@@ -33,6 +33,10 @@ export interface BundleOptions {
|
|
|
33
33
|
coreDylib: string; // libmirin_core.dylib
|
|
34
34
|
helperBin: string; // compiled mirin-helper binary
|
|
35
35
|
cefPath: string; // dir containing the CEF framework (vendor/cef)
|
|
36
|
+
/** App version → Info.plist CFBundleShortVersionString/CFBundleVersion. */
|
|
37
|
+
version?: string;
|
|
38
|
+
/** App icon source: a .icns, a .iconset dir, or a square .png. Optional. */
|
|
39
|
+
icon?: string;
|
|
36
40
|
/** Codesign identity; "-" (default) is ad-hoc. Set to a Developer ID to ship. */
|
|
37
41
|
signIdentity?: string;
|
|
38
42
|
/** Production-only resources placed under Contents/Resources. */
|
|
@@ -40,9 +44,54 @@ export interface BundleOptions {
|
|
|
40
44
|
uiDir?: string; // Vite dist/, copied to Resources/ui (served via app://ui)
|
|
41
45
|
workerJs?: string; // bundled main-process Worker entry -> Resources/worker.js
|
|
42
46
|
manifestJson?: string; // serialized manifest -> Resources/mirin.manifest.json
|
|
47
|
+
versionJson?: string; // serialized version.json -> Resources/version.json (updater)
|
|
43
48
|
};
|
|
44
49
|
}
|
|
45
50
|
|
|
51
|
+
/** The 10 standard iconset renditions (point size + @1x/@2x pixel size). */
|
|
52
|
+
const ICONSET_RENDITIONS = [
|
|
53
|
+
{ name: "icon_16x16.png", px: 16 },
|
|
54
|
+
{ name: "icon_16x16@2x.png", px: 32 },
|
|
55
|
+
{ name: "icon_32x32.png", px: 32 },
|
|
56
|
+
{ name: "icon_32x32@2x.png", px: 64 },
|
|
57
|
+
{ name: "icon_128x128.png", px: 128 },
|
|
58
|
+
{ name: "icon_128x128@2x.png", px: 256 },
|
|
59
|
+
{ name: "icon_256x256.png", px: 256 },
|
|
60
|
+
{ name: "icon_256x256@2x.png", px: 512 },
|
|
61
|
+
{ name: "icon_512x512.png", px: 512 },
|
|
62
|
+
{ name: "icon_512x512@2x.png", px: 1024 },
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Render the app icon to `Resources/icon.icns` and return the CFBundleIconFile
|
|
67
|
+
* stem ("icon"), or undefined if there's no usable source. Accepts a `.icns`
|
|
68
|
+
* (copied), a `.iconset` directory (iconutil), or a square `.png` (rendered to
|
|
69
|
+
* a full iconset via sips, then iconutil).
|
|
70
|
+
*/
|
|
71
|
+
async function writeIcon(iconSrc: string, resources: string): Promise<string | undefined> {
|
|
72
|
+
if (!existsSync(iconSrc)) {
|
|
73
|
+
console.warn(`[mirin] icon not found, skipping: ${iconSrc}`);
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
const icns = join(resources, "icon.icns");
|
|
77
|
+
|
|
78
|
+
if (iconSrc.endsWith(".icns")) {
|
|
79
|
+
cpSync(iconSrc, icns);
|
|
80
|
+
} else if (iconSrc.endsWith(".iconset")) {
|
|
81
|
+
await $`iconutil -c icns ${iconSrc} -o ${icns}`.quiet();
|
|
82
|
+
} else {
|
|
83
|
+
const iconset = join(resources, "icon.iconset");
|
|
84
|
+
rmSync(iconset, { recursive: true, force: true });
|
|
85
|
+
mkdirSync(iconset, { recursive: true });
|
|
86
|
+
for (const { name, px } of ICONSET_RENDITIONS) {
|
|
87
|
+
await $`sips -z ${px} ${px} ${iconSrc} --out ${join(iconset, name)}`.quiet();
|
|
88
|
+
}
|
|
89
|
+
await $`iconutil -c icns ${iconset} -o ${icns}`.quiet();
|
|
90
|
+
rmSync(iconset, { recursive: true, force: true });
|
|
91
|
+
}
|
|
92
|
+
return "icon";
|
|
93
|
+
}
|
|
94
|
+
|
|
46
95
|
type PlistValue = string | boolean | Record<string, string>;
|
|
47
96
|
|
|
48
97
|
function plist(entries: Record<string, PlistValue>): string {
|
|
@@ -87,25 +136,30 @@ export async function buildAppBundle(opts: BundleOptions): Promise<{ app: string
|
|
|
87
136
|
cpSync(opts.hostExe, join(macos, appName));
|
|
88
137
|
cpSync(opts.coreDylib, join(macos, "libmirin_core.dylib"));
|
|
89
138
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
139
|
+
// Render the icon (if any) into Resources before writing the plist, so we
|
|
140
|
+
// only set CFBundleIconFile when an icon was actually produced.
|
|
141
|
+
const iconFile = opts.icon ? await writeIcon(opts.icon, join(contents, "Resources")) : undefined;
|
|
142
|
+
const version = opts.version ?? "0.0.1";
|
|
143
|
+
|
|
144
|
+
const info: Record<string, PlistValue> = {
|
|
145
|
+
CFBundleDevelopmentRegion: "en",
|
|
146
|
+
CFBundleDisplayName: appName,
|
|
147
|
+
CFBundleExecutable: appName,
|
|
148
|
+
CFBundleIdentifier: bundleId,
|
|
149
|
+
CFBundleInfoDictionaryVersion: "6.0",
|
|
150
|
+
CFBundleName: appName,
|
|
151
|
+
CFBundlePackageType: "APPL",
|
|
152
|
+
CFBundleShortVersionString: version,
|
|
153
|
+
CFBundleVersion: version,
|
|
154
|
+
LSMinimumSystemVersion: "13.0",
|
|
155
|
+
NSHighResolutionCapable: true,
|
|
156
|
+
NSSupportsAutomaticGraphicsSwitching: true,
|
|
157
|
+
LSFileQuarantineEnabled: true,
|
|
158
|
+
LSEnvironment: { MallocNanoZone: "0" },
|
|
159
|
+
};
|
|
160
|
+
if (iconFile) info.CFBundleIconFile = iconFile;
|
|
161
|
+
|
|
162
|
+
writeFileSync(join(contents, "Info.plist"), plist(info));
|
|
109
163
|
|
|
110
164
|
cpSync(join(cefPath, FRAMEWORK), join(frameworks, FRAMEWORK), {
|
|
111
165
|
recursive: true,
|
|
@@ -123,6 +177,11 @@ export async function buildAppBundle(opts: BundleOptions): Promise<{ app: string
|
|
|
123
177
|
if (opts.resources?.manifestJson != null) {
|
|
124
178
|
writeFileSync(join(resources, "mirin.manifest.json"), opts.resources.manifestJson);
|
|
125
179
|
}
|
|
180
|
+
// version.json is the running app's self-knowledge for the updater. Written
|
|
181
|
+
// before the codesign loop below so the signature covers it.
|
|
182
|
+
if (opts.resources?.versionJson != null) {
|
|
183
|
+
writeFileSync(join(resources, "version.json"), opts.resources.versionJson);
|
|
184
|
+
}
|
|
126
185
|
|
|
127
186
|
for (const { suffix, id } of HELPER_TYPES) {
|
|
128
187
|
const name = `${appName} Helper${suffix}`;
|
|
@@ -139,8 +198,8 @@ export async function buildAppBundle(opts: BundleOptions): Promise<{ app: string
|
|
|
139
198
|
CFBundleInfoDictionaryVersion: "6.0",
|
|
140
199
|
CFBundleName: name,
|
|
141
200
|
CFBundlePackageType: "APPL",
|
|
142
|
-
CFBundleShortVersionString:
|
|
143
|
-
CFBundleVersion:
|
|
201
|
+
CFBundleShortVersionString: version,
|
|
202
|
+
CFBundleVersion: version,
|
|
144
203
|
LSMinimumSystemVersion: "13.0",
|
|
145
204
|
LSUIElement: "1",
|
|
146
205
|
NSHighResolutionCapable: true,
|
package/src/dev.ts
CHANGED
|
@@ -13,12 +13,16 @@ import { mkdirSync } from "node:fs";
|
|
|
13
13
|
import { join } from "node:path";
|
|
14
14
|
import { buildAppBundle } from "./bundle.ts";
|
|
15
15
|
import { resolveArtifacts } from "./artifacts.ts";
|
|
16
|
+
import { sweepBuildTemps } from "./temps.ts";
|
|
16
17
|
|
|
17
18
|
const DEV_URL = "http://localhost:5173";
|
|
18
19
|
|
|
19
20
|
export async function dev(projectDir = process.cwd()): Promise<number> {
|
|
20
21
|
const work = join(projectDir, ".mirin");
|
|
21
22
|
mkdirSync(work, { recursive: true });
|
|
23
|
+
// `bun build --compile` drops temp *.bun-build files in the cwd; clear any left
|
|
24
|
+
// behind by a previously interrupted run so they don't pile up in the project.
|
|
25
|
+
sweepBuildTemps(projectDir);
|
|
22
26
|
|
|
23
27
|
// --- load the manifest ---
|
|
24
28
|
const config = (await import(join(projectDir, "mirin.config.ts"))).default;
|
|
@@ -46,6 +50,7 @@ export async function dev(projectDir = process.cwd()): Promise<number> {
|
|
|
46
50
|
coreDylib: artifacts.coreDylib,
|
|
47
51
|
helperBin: artifacts.helperBin,
|
|
48
52
|
cefPath: artifacts.cefPath,
|
|
53
|
+
icon: config.icon ? join(projectDir, config.icon) : undefined,
|
|
49
54
|
});
|
|
50
55
|
|
|
51
56
|
// --- start Vite ---
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { resolve } from "node:path";
|
|
7
7
|
import { dev } from "./dev.ts";
|
|
8
8
|
import { build } from "./build.ts";
|
|
9
|
+
import { release } from "./release.ts";
|
|
9
10
|
import { scaffold } from "create-mirinjs";
|
|
10
11
|
|
|
11
12
|
const [command, arg] = Bun.argv.slice(2);
|
|
@@ -15,6 +16,7 @@ const USAGE = `mirin — build desktop apps with Bun + Chromium
|
|
|
15
16
|
Usage:
|
|
16
17
|
mirin dev run the app against the Vite dev server (HMR + typed RPC)
|
|
17
18
|
mirin build package a standalone, signed .app (output: ./build)
|
|
19
|
+
mirin release build + emit update artifacts (output: ./build/release)
|
|
18
20
|
mirin init [dir] scaffold a new app
|
|
19
21
|
`;
|
|
20
22
|
|
|
@@ -24,7 +26,12 @@ switch (command) {
|
|
|
24
26
|
break;
|
|
25
27
|
}
|
|
26
28
|
case "build": {
|
|
27
|
-
|
|
29
|
+
await build();
|
|
30
|
+
process.exit(0);
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
case "release": {
|
|
34
|
+
process.exit(await release());
|
|
28
35
|
break;
|
|
29
36
|
}
|
|
30
37
|
case "init": {
|
package/src/release.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `mirin release` — build the app and emit flat-named update artifacts.
|
|
3
|
+
*
|
|
4
|
+
* Produces, under `build/release/`:
|
|
5
|
+
* {channel}-{platform}-{arch}-update.json the manifest the app polls
|
|
6
|
+
* {channel}-{platform}-{arch}-{Name}.app.tar.gz the full bundle
|
|
7
|
+
*
|
|
8
|
+
* These names are flat (no folders) so they upload as-is to GitHub Releases,
|
|
9
|
+
* S3/R2, or any static host — whatever `release.baseUrl` points at. Channels
|
|
10
|
+
* coexist at the same baseUrl because the channel is part of every filename.
|
|
11
|
+
*
|
|
12
|
+
* The app's `app.updater` polls `{baseUrl}/{prefix}-update.json`, compares the
|
|
13
|
+
* advertised version to its own, downloads `url`, verifies `sha256`, then swaps
|
|
14
|
+
* the whole `.app` and relaunches. (A signed/notarized `.app` must be replaced
|
|
15
|
+
* whole — never modified in place — so the artifact is the entire bundle.)
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { $ } from "bun";
|
|
19
|
+
import { mkdirSync, rmSync, readFileSync } from "node:fs";
|
|
20
|
+
import { join } from "node:path";
|
|
21
|
+
import { build } from "./build.ts";
|
|
22
|
+
|
|
23
|
+
export async function release(projectDir = process.cwd()): Promise<number> {
|
|
24
|
+
const result = await build(projectDir);
|
|
25
|
+
if (!result.baseUrl) {
|
|
26
|
+
console.error(
|
|
27
|
+
"[mirin release] no `release.baseUrl` in mirin.config.ts — nothing to publish.",
|
|
28
|
+
);
|
|
29
|
+
return 1;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const platform = "darwin";
|
|
33
|
+
const arch = process.arch; // "arm64" | "x64"
|
|
34
|
+
const prefix = `${result.channel}-${platform}-${arch}`;
|
|
35
|
+
// Sanitize the app name for a URL-safe artifact filename (spaces break hosts).
|
|
36
|
+
const safeName = result.appName.replace(/[^A-Za-z0-9._-]/g, "");
|
|
37
|
+
|
|
38
|
+
const buildDir = join(projectDir, "build");
|
|
39
|
+
const outDir = join(buildDir, "release");
|
|
40
|
+
rmSync(outDir, { recursive: true, force: true });
|
|
41
|
+
mkdirSync(outDir, { recursive: true });
|
|
42
|
+
|
|
43
|
+
// Notarize + staple before packing (so the shipped bundle passes Gatekeeper on
|
|
44
|
+
// end-user machines), when notary credentials are present in the environment.
|
|
45
|
+
const apple = process.env.MIRIN_NOTARY_APPLE_ID;
|
|
46
|
+
const pw = process.env.MIRIN_NOTARY_PASSWORD;
|
|
47
|
+
const team = process.env.MIRIN_NOTARY_TEAM_ID;
|
|
48
|
+
if (apple && pw && team) {
|
|
49
|
+
console.log("[mirin release] notarizing (this can take a few minutes)…");
|
|
50
|
+
const zip = join(buildDir, "_notarize.zip");
|
|
51
|
+
await $`ditto -c -k --keepParent ${result.app} ${zip}`;
|
|
52
|
+
await $`xcrun notarytool submit ${zip} --apple-id ${apple} --password ${pw} --team-id ${team} --wait`;
|
|
53
|
+
await $`xcrun stapler staple ${result.app}`;
|
|
54
|
+
rmSync(zip, { force: true });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const tarName = `${prefix}-${safeName}.app.tar.gz`;
|
|
58
|
+
const tarPath = join(outDir, tarName);
|
|
59
|
+
|
|
60
|
+
console.log(`[mirin release] packing ${result.appName}.app → ${tarName}`);
|
|
61
|
+
// BSD tar preserves the CEF framework's symlinks + executable bits.
|
|
62
|
+
await $`tar -czf ${tarPath} -C ${buildDir} ${`${result.appName}.app`}`;
|
|
63
|
+
|
|
64
|
+
const bytes = readFileSync(tarPath);
|
|
65
|
+
const sha256 = new Bun.CryptoHasher("sha256").update(bytes).digest("hex");
|
|
66
|
+
|
|
67
|
+
const manifest = {
|
|
68
|
+
version: result.version,
|
|
69
|
+
channel: result.channel,
|
|
70
|
+
platform,
|
|
71
|
+
arch,
|
|
72
|
+
url: tarName,
|
|
73
|
+
sha256,
|
|
74
|
+
size: bytes.byteLength,
|
|
75
|
+
};
|
|
76
|
+
const manifestName = `${prefix}-update.json`;
|
|
77
|
+
await Bun.write(join(outDir, manifestName), `${JSON.stringify(manifest, null, 2)}\n`);
|
|
78
|
+
|
|
79
|
+
const mb = (bytes.byteLength / 1e6).toFixed(1);
|
|
80
|
+
console.log(`\n[mirin release] done → build/release/`);
|
|
81
|
+
console.log(` ${manifestName}`);
|
|
82
|
+
console.log(` ${tarName} (${mb} MB)`);
|
|
83
|
+
console.log(`\nUpload both to: ${result.baseUrl}`);
|
|
84
|
+
return 0;
|
|
85
|
+
}
|
package/src/temps.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remove `bun build --compile` scratch files (`.*.bun-build`) from a directory.
|
|
3
|
+
*
|
|
4
|
+
* `bun build --compile` writes these temp files into the working directory and
|
|
5
|
+
* normally cleans them up — but a hard-killed build leaves them behind, where
|
|
6
|
+
* they accumulate in the project root. The dev/build commands sweep them first.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { readdirSync, rmSync } from "node:fs";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
|
|
12
|
+
export function sweepBuildTemps(dir: string): void {
|
|
13
|
+
let entries: string[];
|
|
14
|
+
try {
|
|
15
|
+
entries = readdirSync(dir);
|
|
16
|
+
} catch {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
for (const name of entries) {
|
|
20
|
+
if (name.endsWith(".bun-build")) {
|
|
21
|
+
rmSync(join(dir, name), { force: true });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|