feat: dist building
This commit is contained in:
20
README.md
20
README.md
@@ -120,10 +120,26 @@ npm run test:coverage
|
|||||||
# Generates drizzle artifacts, builds main process and renderer
|
# Generates drizzle artifacts, builds main process and renderer
|
||||||
npm run build
|
npm run build
|
||||||
|
|
||||||
# Package distributables
|
# Package app directory only (no installer)
|
||||||
npx electron-builder
|
npm run package
|
||||||
|
|
||||||
|
# Package distributables for all supported systems
|
||||||
|
npm run dist
|
||||||
|
|
||||||
|
# Package by target platform
|
||||||
|
npm run dist:mac
|
||||||
|
npm run dist:win
|
||||||
|
npm run dist:linux
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Distribution Notes
|
||||||
|
|
||||||
|
- Custom protocol `bds://` is declared in electron-builder metadata for packaged apps.
|
||||||
|
- macOS signing/notarization is scaffolded via `scripts/notarize.mjs` and runs when these env vars are set:
|
||||||
|
- `APPLE_ID`
|
||||||
|
- `APPLE_APP_SPECIFIC_PASSWORD`
|
||||||
|
- `APPLE_TEAM_ID`
|
||||||
|
|
||||||
### Database Utilities
|
### Database Utilities
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
108
package-lock.json
generated
108
package-lock.json
generated
@@ -45,11 +45,13 @@
|
|||||||
"simple-git": "^3.31.1",
|
"simple-git": "^3.31.1",
|
||||||
"snowball-stemmers": "^0.6.0",
|
"snowball-stemmers": "^0.6.0",
|
||||||
"turndown": "^7.2.2",
|
"turndown": "^7.2.2",
|
||||||
|
"uuid": "^13.0.0",
|
||||||
"vanilla-calendar-pro": "^3.1.0",
|
"vanilla-calendar-pro": "^3.1.0",
|
||||||
"zod": "^4.3.6",
|
"zod": "^4.3.6",
|
||||||
"zustand": "^5.0.11"
|
"zustand": "^5.0.11"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@electron/notarize": "^3.1.0",
|
||||||
"@testing-library/jest-dom": "^6.9.1",
|
"@testing-library/jest-dom": "^6.9.1",
|
||||||
"@testing-library/react": "^16.3.2",
|
"@testing-library/react": "^16.3.2",
|
||||||
"@testing-library/user-event": "^14.6.1",
|
"@testing-library/user-event": "^14.6.1",
|
||||||
@@ -75,7 +77,6 @@
|
|||||||
"memfs": "^4.6.0",
|
"memfs": "^4.6.0",
|
||||||
"tsx": "^4.6.0",
|
"tsx": "^4.6.0",
|
||||||
"typescript": "^5.3.0",
|
"typescript": "^5.3.0",
|
||||||
"uuid": "^13.0.0",
|
|
||||||
"vite": "^7.3.1",
|
"vite": "^7.3.1",
|
||||||
"vitest": "^4.0.18",
|
"vitest": "^4.0.18",
|
||||||
"wait-on": "^9.0.3"
|
"wait-on": "^9.0.3"
|
||||||
@@ -1134,57 +1135,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@electron/notarize": {
|
"node_modules/@electron/notarize": {
|
||||||
"version": "2.5.0",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-3.1.1.tgz",
|
||||||
"integrity": "sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A==",
|
"integrity": "sha512-uQQSlOiJnqRkTL1wlEBAxe90nVN/Fc/hEmk0bqpKk8nKjV1if/tXLHKUPePtv9Xsx90PtZU8aidx5lAiOpjkQQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": "^4.1.1",
|
"debug": "^4.4.0",
|
||||||
"fs-extra": "^9.0.1",
|
|
||||||
"promise-retry": "^2.0.1"
|
"promise-retry": "^2.0.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10.0.0"
|
"node": ">= 22.12.0"
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@electron/notarize/node_modules/fs-extra": {
|
|
||||||
"version": "9.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
|
|
||||||
"integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"at-least-node": "^1.0.0",
|
|
||||||
"graceful-fs": "^4.2.0",
|
|
||||||
"jsonfile": "^6.0.1",
|
|
||||||
"universalify": "^2.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@electron/notarize/node_modules/jsonfile": {
|
|
||||||
"version": "6.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
|
|
||||||
"integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"universalify": "^2.0.0"
|
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"graceful-fs": "^4.1.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@electron/notarize/node_modules/universalify": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 10.0.0"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@electron/osx-sign": {
|
"node_modules/@electron/osx-sign": {
|
||||||
@@ -6350,6 +6311,60 @@
|
|||||||
"semver": "bin/semver.js"
|
"semver": "bin/semver.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/app-builder-lib/node_modules/@electron/notarize": {
|
||||||
|
"version": "2.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.5.0.tgz",
|
||||||
|
"integrity": "sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "^4.1.1",
|
||||||
|
"fs-extra": "^9.0.1",
|
||||||
|
"promise-retry": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/app-builder-lib/node_modules/@electron/notarize/node_modules/fs-extra": {
|
||||||
|
"version": "9.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
|
||||||
|
"integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"at-least-node": "^1.0.0",
|
||||||
|
"graceful-fs": "^4.2.0",
|
||||||
|
"jsonfile": "^6.0.1",
|
||||||
|
"universalify": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/app-builder-lib/node_modules/@electron/notarize/node_modules/jsonfile": {
|
||||||
|
"version": "6.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
|
||||||
|
"integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"universalify": "^2.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"graceful-fs": "^4.1.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/app-builder-lib/node_modules/@electron/notarize/node_modules/universalify": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/app-builder-lib/node_modules/ci-info": {
|
"node_modules/app-builder-lib/node_modules/ci-info": {
|
||||||
"version": "4.3.1",
|
"version": "4.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz",
|
||||||
@@ -14913,7 +14928,6 @@
|
|||||||
"version": "13.0.0",
|
"version": "13.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz",
|
||||||
"integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==",
|
"integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
"https://github.com/sponsors/broofa",
|
"https://github.com/sponsors/broofa",
|
||||||
"https://github.com/sponsors/ctavan"
|
"https://github.com/sponsors/ctavan"
|
||||||
|
|||||||
45
package.json
45
package.json
@@ -12,6 +12,11 @@
|
|||||||
"start": "concurrently --kill-others \"npm run dev:renderer\" \"npm run start:electron\"",
|
"start": "concurrently --kill-others \"npm run dev:renderer\" \"npm run start:electron\"",
|
||||||
"start:electron": "wait-on http://localhost:5173 && cross-env NODE_ENV=development node ./node_modules/electron/cli.js .",
|
"start:electron": "wait-on http://localhost:5173 && cross-env NODE_ENV=development node ./node_modules/electron/cli.js .",
|
||||||
"build": "npm run lint && npm run db:generate && npm run build:main && npm run build:renderer",
|
"build": "npm run lint && npm run db:generate && npm run build:main && npm run build:renderer",
|
||||||
|
"package": "npm run build && electron-builder --dir",
|
||||||
|
"dist": "npm run build && electron-builder",
|
||||||
|
"dist:mac": "npm run build && electron-builder --mac",
|
||||||
|
"dist:win": "npm run build && electron-builder --win",
|
||||||
|
"dist:linux": "npm run build && electron-builder --linux",
|
||||||
"build:main": "node ./node_modules/typescript/bin/tsc -p tsconfig.main.json",
|
"build:main": "node ./node_modules/typescript/bin/tsc -p tsconfig.main.json",
|
||||||
"build:renderer": "node ./node_modules/vite/bin/vite.js build",
|
"build:renderer": "node ./node_modules/vite/bin/vite.js build",
|
||||||
"start:prod": "node ./node_modules/electron/cli.js .",
|
"start:prod": "node ./node_modules/electron/cli.js .",
|
||||||
@@ -29,6 +34,7 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@electron/notarize": "^3.1.0",
|
||||||
"@testing-library/jest-dom": "^6.9.1",
|
"@testing-library/jest-dom": "^6.9.1",
|
||||||
"@testing-library/react": "^16.3.2",
|
"@testing-library/react": "^16.3.2",
|
||||||
"@testing-library/user-event": "^14.6.1",
|
"@testing-library/user-event": "^14.6.1",
|
||||||
@@ -54,7 +60,6 @@
|
|||||||
"memfs": "^4.6.0",
|
"memfs": "^4.6.0",
|
||||||
"tsx": "^4.6.0",
|
"tsx": "^4.6.0",
|
||||||
"typescript": "^5.3.0",
|
"typescript": "^5.3.0",
|
||||||
"uuid": "^13.0.0",
|
|
||||||
"vite": "^7.3.1",
|
"vite": "^7.3.1",
|
||||||
"vitest": "^4.0.18",
|
"vitest": "^4.0.18",
|
||||||
"wait-on": "^9.0.3"
|
"wait-on": "^9.0.3"
|
||||||
@@ -96,6 +101,7 @@
|
|||||||
"simple-git": "^3.31.1",
|
"simple-git": "^3.31.1",
|
||||||
"snowball-stemmers": "^0.6.0",
|
"snowball-stemmers": "^0.6.0",
|
||||||
"turndown": "^7.2.2",
|
"turndown": "^7.2.2",
|
||||||
|
"uuid": "^13.0.0",
|
||||||
"vanilla-calendar-pro": "^3.1.0",
|
"vanilla-calendar-pro": "^3.1.0",
|
||||||
"zod": "^4.3.6",
|
"zod": "^4.3.6",
|
||||||
"zustand": "^5.0.11"
|
"zustand": "^5.0.11"
|
||||||
@@ -108,6 +114,8 @@
|
|||||||
"build": {
|
"build": {
|
||||||
"appId": "com.bds.blogging-desktop-server",
|
"appId": "com.bds.blogging-desktop-server",
|
||||||
"productName": "Blogging Desktop Server",
|
"productName": "Blogging Desktop Server",
|
||||||
|
"asar": true,
|
||||||
|
"afterSign": "scripts/notarize.mjs",
|
||||||
"directories": {
|
"directories": {
|
||||||
"output": "release"
|
"output": "release"
|
||||||
},
|
},
|
||||||
@@ -122,14 +130,43 @@
|
|||||||
"to": "drizzle"
|
"to": "drizzle"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"protocols": [
|
||||||
|
{
|
||||||
|
"name": "bDS Blogmark Links",
|
||||||
|
"schemes": [
|
||||||
|
"bds"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"win": {
|
"win": {
|
||||||
"target": "nsis"
|
"target": [
|
||||||
|
{
|
||||||
|
"target": "nsis",
|
||||||
|
"arch": [
|
||||||
|
"x64",
|
||||||
|
"arm64"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"mac": {
|
"mac": {
|
||||||
"target": "dmg"
|
"target": [
|
||||||
|
"dmg",
|
||||||
|
"zip"
|
||||||
|
],
|
||||||
|
"category": "public.app-category.productivity",
|
||||||
|
"hardenedRuntime": true,
|
||||||
|
"gatekeeperAssess": false,
|
||||||
|
"entitlements": "build/entitlements.mac.plist",
|
||||||
|
"entitlementsInherit": "build/entitlements.mac.inherit.plist"
|
||||||
},
|
},
|
||||||
"linux": {
|
"linux": {
|
||||||
"target": "AppImage"
|
"target": [
|
||||||
|
"AppImage",
|
||||||
|
"deb",
|
||||||
|
"rpm"
|
||||||
|
],
|
||||||
|
"category": "Utility"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
30
scripts/notarize.mjs
Normal file
30
scripts/notarize.mjs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { notarize } from '@electron/notarize';
|
||||||
|
|
||||||
|
export default async function notarizeIfConfigured(context) {
|
||||||
|
const { electronPlatformName, appOutDir, packager } = context;
|
||||||
|
|
||||||
|
if (electronPlatformName !== 'darwin') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const appleId = process.env.APPLE_ID;
|
||||||
|
const appleIdPassword = process.env.APPLE_APP_SPECIFIC_PASSWORD;
|
||||||
|
const teamId = process.env.APPLE_TEAM_ID;
|
||||||
|
|
||||||
|
if (!appleId || !appleIdPassword || !teamId) {
|
||||||
|
console.log('[notarize] Skipped: APPLE_ID / APPLE_APP_SPECIFIC_PASSWORD / APPLE_TEAM_ID not set');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const appName = packager.appInfo.productFilename;
|
||||||
|
const appPath = `${appOutDir}/${appName}.app`;
|
||||||
|
|
||||||
|
console.log(`[notarize] Submitting ${appPath}`);
|
||||||
|
await notarize({
|
||||||
|
appPath,
|
||||||
|
appleId,
|
||||||
|
appleIdPassword,
|
||||||
|
teamId,
|
||||||
|
});
|
||||||
|
console.log('[notarize] Completed');
|
||||||
|
}
|
||||||
45
tests/engine/PackagingConfig.test.ts
Normal file
45
tests/engine/PackagingConfig.test.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
describe('package.json packaging configuration', () => {
|
||||||
|
const packageJsonPath = path.resolve(process.cwd(), 'package.json');
|
||||||
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')) as Record<string, any>;
|
||||||
|
|
||||||
|
it('defines cross-platform distribution scripts', () => {
|
||||||
|
const scripts = packageJson.scripts as Record<string, string>;
|
||||||
|
|
||||||
|
expect(scripts['dist']).toBeTypeOf('string');
|
||||||
|
expect(scripts['dist:mac']).toBeTypeOf('string');
|
||||||
|
expect(scripts['dist:win']).toBeTypeOf('string');
|
||||||
|
expect(scripts['dist:linux']).toBeTypeOf('string');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('registers bds protocol for packaged apps', () => {
|
||||||
|
const build = packageJson.build as Record<string, any>;
|
||||||
|
const protocols = build.protocols as Array<{ name: string; schemes: string[] }>;
|
||||||
|
|
||||||
|
expect(Array.isArray(protocols)).toBe(true);
|
||||||
|
expect(protocols.some((entry) => Array.isArray(entry.schemes) && entry.schemes.includes('bds'))).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('configures macOS, Windows, and Linux targets', () => {
|
||||||
|
const build = packageJson.build as Record<string, any>;
|
||||||
|
|
||||||
|
expect(build.mac).toBeTruthy();
|
||||||
|
expect(build.win).toBeTruthy();
|
||||||
|
expect(build.linux).toBeTruthy();
|
||||||
|
|
||||||
|
expect(build.mac.target).toBeTruthy();
|
||||||
|
expect(build.win.target).toBeTruthy();
|
||||||
|
expect(build.linux.target).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keeps runtime modules in dependencies (not devDependencies)', () => {
|
||||||
|
const dependencies = packageJson.dependencies as Record<string, string>;
|
||||||
|
const devDependencies = packageJson.devDependencies as Record<string, string>;
|
||||||
|
|
||||||
|
expect(dependencies.uuid).toBeTypeOf('string');
|
||||||
|
expect(devDependencies.uuid).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user