build: embed pac catalog in application bundles
This commit is contained in:
@@ -13,6 +13,7 @@
|
|||||||
- Package manifests are validated for identity, version, category, dependencies, conflicts, settings, Core/API compatibility, project contributions, and safe relative entry paths.
|
- Package manifests are validated for identity, version, category, dependencies, conflicts, settings, Core/API compatibility, project contributions, and safe relative entry paths.
|
||||||
- Native `.pac` files are ZIP-compressed Aurora packages with `manifest.json` at archive root.
|
- Native `.pac` files are ZIP-compressed Aurora packages with `manifest.json` at archive root.
|
||||||
- A `.pac` placed beside the AppImage or in its `modules` folder is discovered automatically and appears in the Modules screen.
|
- A `.pac` placed beside the AppImage or in its `modules` folder is discovered automatically and appears in the Modules screen.
|
||||||
|
- Packaged applications also carry a generated `.pac` catalog in their application resources. The macOS DMG layout exposes the same catalog as an **Aurora Modules** folder while the copied `.app` retains its own embedded catalog after the DMG is ejected.
|
||||||
- Linux packages use Aurora's 512×512 application icon and a synchronized `aurora-dockside` desktop filename, executable name, icon name, and `StartupWMClass`.
|
- Linux packages use Aurora's 512×512 application icon and a synchronized `aurora-dockside` desktop filename, executable name, icon name, and `StartupWMClass`.
|
||||||
- `.pac` inspection rejects encrypted entries, symbolic links, path traversal, absolute/drive paths, excessive entry counts, and expanded archives larger than 256 MiB before extraction.
|
- `.pac` inspection rejects encrypted entries, symbolic links, path traversal, absolute/drive paths, excessive entry counts, and expanded archives larger than 256 MiB before extraction.
|
||||||
- Installation uses staging plus rollback-safe replacement, so a failed update preserves the currently installed module.
|
- Installation uses staging plus rollback-safe replacement, so a failed update preserves the currently installed module.
|
||||||
@@ -45,7 +46,7 @@ npm run build
|
|||||||
npx electron-builder --linux AppImage
|
npx electron-builder --linux AppImage
|
||||||
```
|
```
|
||||||
|
|
||||||
Automated result: 6 test files and 26 tests passed. Coverage includes:
|
Automated result: 6 test files and 27 tests passed. Coverage includes:
|
||||||
|
|
||||||
- Empty registry
|
- Empty registry
|
||||||
- Available local packages
|
- Available local packages
|
||||||
@@ -78,7 +79,7 @@ Live project smoke result for project `24`:
|
|||||||
SHA-256:
|
SHA-256:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
faafa9555072e8de98db1ee65cf329804c0eb37d44cd3bf4e6b50c5216f9f6e6 aurora-dockside-2.0.0-alpha.24.AppImage
|
ad679c904204b7f8db52b209231a08eea6ba1baf34dd3f24eae90ffcdfae7e56 aurora-dockside-2.0.0-alpha.24.AppImage
|
||||||
d2165af4b75ab888c6d35a750a449205123866231ba5e98e1cbe9bcaa8738f46 aurora-module-wordpress-1.2.0.pac
|
d2165af4b75ab888c6d35a750a449205123866231ba5e98e1cbe9bcaa8738f46 aurora-module-wordpress-1.2.0.pac
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -86,3 +87,4 @@ d2165af4b75ab888c6d35a750a449205123866231ba5e98e1cbe9bcaa8738f46 aurora-module-
|
|||||||
|
|
||||||
- Clean-registry install/remove behavior was exercised through the real registry implementation in automated temporary-directory tests. The live GUI smoke used the already installed local WordPress package and project `24`.
|
- Clean-registry install/remove behavior was exercised through the real registry implementation in automated temporary-directory tests. The live GUI smoke used the already installed local WordPress package and project `24`.
|
||||||
- AppImage systems without working FUSE can use `--appimage-extract` and launch `squashfs-root/AppRun --no-sandbox`.
|
- AppImage systems without working FUSE can use `--appimage-extract` and launch `squashfs-root/AppRun --no-sandbox`.
|
||||||
|
- The embedded catalog and DMG layout are configured and the identical Linux application-resource layout was verified. A final signed/notarized `.app` and DMG must be built and inspected on macOS or a macOS CI runner.
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
appId: com.aurora-dockside.app
|
appId: com.aurora-dockside.app
|
||||||
productName: Aurora Dockside
|
productName: Aurora Dockside
|
||||||
|
beforePack: scripts/package-modules.cjs
|
||||||
directories:
|
directories:
|
||||||
buildResources: build
|
buildResources: build
|
||||||
files:
|
files:
|
||||||
@@ -11,6 +12,11 @@ files:
|
|||||||
- '!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}'
|
- '!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}'
|
||||||
asarUnpack:
|
asarUnpack:
|
||||||
- resources/**
|
- resources/**
|
||||||
|
extraResources:
|
||||||
|
- from: dist/module-catalog
|
||||||
|
to: module-catalog
|
||||||
|
filter:
|
||||||
|
- '*.pac'
|
||||||
win:
|
win:
|
||||||
executableName: aurora-dockside
|
executableName: aurora-dockside
|
||||||
nsis:
|
nsis:
|
||||||
@@ -23,6 +29,18 @@ mac:
|
|||||||
notarize: false
|
notarize: false
|
||||||
dmg:
|
dmg:
|
||||||
artifactName: ${name}-${version}.${ext}
|
artifactName: ${name}-${version}.${ext}
|
||||||
|
contents:
|
||||||
|
- x: 130
|
||||||
|
y: 220
|
||||||
|
- x: 410
|
||||||
|
y: 220
|
||||||
|
type: link
|
||||||
|
path: /Applications
|
||||||
|
- x: 270
|
||||||
|
y: 110
|
||||||
|
type: dir
|
||||||
|
name: Aurora Modules
|
||||||
|
path: dist/module-catalog
|
||||||
linux:
|
linux:
|
||||||
executableName: aurora-dockside
|
executableName: aurora-dockside
|
||||||
icon: resources/icon.png
|
icon: resources/icon.png
|
||||||
|
|||||||
+3
-2
@@ -19,11 +19,12 @@
|
|||||||
"start": "electron-vite preview",
|
"start": "electron-vite preview",
|
||||||
"dev": "electron-vite dev",
|
"dev": "electron-vite dev",
|
||||||
"build": "npm run typecheck && electron-vite build",
|
"build": "npm run typecheck && electron-vite build",
|
||||||
|
"build:modules": "node scripts/package-modules.cjs",
|
||||||
"postinstall": "electron-builder install-app-deps",
|
"postinstall": "electron-builder install-app-deps",
|
||||||
"build:unpack": "npm run build && electron-builder --dir",
|
"build:unpack": "npm run build && electron-builder --dir",
|
||||||
"build:win": "npm run build && electron-builder --win",
|
"build:win": "npm run build && electron-builder --win",
|
||||||
"build:mac": "electron-vite build && electron-builder --mac",
|
"build:mac": "npm run build && electron-builder --mac",
|
||||||
"build:linux": "electron-vite build && electron-builder --linux",
|
"build:linux": "npm run build && electron-builder --linux",
|
||||||
"test": "vitest",
|
"test": "vitest",
|
||||||
"test:run": "vitest run",
|
"test:run": "vitest run",
|
||||||
"test:coverage": "vitest run --coverage",
|
"test:coverage": "vitest run --coverage",
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
const { mkdirSync, readFileSync, readdirSync, rmSync } = require('fs')
|
||||||
|
const { join, resolve } = require('path')
|
||||||
|
const { spawnSync } = require('child_process')
|
||||||
|
|
||||||
|
function packageModules() {
|
||||||
|
const projectRoot = resolve(__dirname, '..')
|
||||||
|
const packagesRoot = join(projectRoot, 'packages')
|
||||||
|
const catalogRoot = join(projectRoot, 'dist', 'module-catalog')
|
||||||
|
mkdirSync(catalogRoot, { recursive: true })
|
||||||
|
|
||||||
|
for (const entry of readdirSync(packagesRoot, { withFileTypes: true })) {
|
||||||
|
if (!entry.isDirectory()) continue
|
||||||
|
const source = join(packagesRoot, entry.name)
|
||||||
|
let manifest
|
||||||
|
try {
|
||||||
|
manifest = JSON.parse(readFileSync(join(source, 'manifest.json'), 'utf8'))
|
||||||
|
} catch {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (!manifest.id || !manifest.version) throw new Error(`Invalid module manifest in ${source}`)
|
||||||
|
const output = join(catalogRoot, `${manifest.id}-${manifest.version}.pac`)
|
||||||
|
rmSync(output, { force: true })
|
||||||
|
const result = spawnSync('zip', ['-qr', output, '.'], { cwd: source, stdio: 'inherit' })
|
||||||
|
if (result.error) throw result.error
|
||||||
|
if (result.status !== 0) throw new Error(`zip failed for ${manifest.id} with exit code ${result.status}`)
|
||||||
|
process.stdout.write(` • packaged module ${manifest.id}@${manifest.version} → ${output}\n`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = packageModules
|
||||||
|
|
||||||
|
if (require.main === module) packageModules()
|
||||||
@@ -51,6 +51,21 @@ describe('external module registry', () => {
|
|||||||
expect((await getAvailableModulePackages()).map((item) => item.manifest.id)).toContain('sample-app')
|
expect((await getAvailableModulePackages()).map((item) => item.manifest.id)).toContain('sample-app')
|
||||||
delete process.env.APPIMAGE
|
delete process.env.APPIMAGE
|
||||||
})
|
})
|
||||||
|
it('discovers a .pac embedded in the packaged application resources', async () => {
|
||||||
|
const resources = await temp('aurora-resources-')
|
||||||
|
const catalog = join(resources, 'module-catalog')
|
||||||
|
await mkdir(catalog)
|
||||||
|
const pac = await pacFile()
|
||||||
|
await import('fs/promises').then(({ copyFile }) => copyFile(pac, join(catalog, 'sample-app.pac')))
|
||||||
|
const previous = Object.getOwnPropertyDescriptor(process, 'resourcesPath')
|
||||||
|
Object.defineProperty(process, 'resourcesPath', { value: resources, configurable: true })
|
||||||
|
try {
|
||||||
|
expect((await getAvailableModulePackages()).map((item) => item.manifest.id)).toContain('sample-app')
|
||||||
|
} finally {
|
||||||
|
if (previous) Object.defineProperty(process, 'resourcesPath', previous)
|
||||||
|
else Reflect.deleteProperty(process, 'resourcesPath')
|
||||||
|
}
|
||||||
|
})
|
||||||
it('installs a valid local package and refreshes after install', async () => {
|
it('installs a valid local package and refreshes after install', async () => {
|
||||||
const userData = await temp('aurora-user-'); const source = await packageDir()
|
const userData = await temp('aurora-user-'); const source = await packageDir()
|
||||||
await installModulePackage(source, userData)
|
await installModulePackage(source, userData)
|
||||||
|
|||||||
@@ -165,7 +165,10 @@ function catalogDirectories(): string[] {
|
|||||||
const besideImage = process.env.APPIMAGE
|
const besideImage = process.env.APPIMAGE
|
||||||
? [dirname(process.env.APPIMAGE), join(dirname(process.env.APPIMAGE), 'modules')]
|
? [dirname(process.env.APPIMAGE), join(dirname(process.env.APPIMAGE), 'modules')]
|
||||||
: []
|
: []
|
||||||
return [...configured, ...besideImage, join(process.cwd(), 'modules'), join(process.cwd(), 'packages'), join(app.getAppPath(), 'packages')]
|
const embedded = typeof process.resourcesPath === 'string'
|
||||||
|
? [join(process.resourcesPath, 'module-catalog')]
|
||||||
|
: []
|
||||||
|
return [...configured, ...besideImage, ...embedded, join(process.cwd(), 'modules'), join(process.cwd(), 'packages'), join(app.getAppPath(), 'packages')]
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getAvailableModulePackages(): Promise<AuroraAvailableModule[]> {
|
export async function getAvailableModulePackages(): Promise<AuroraAvailableModule[]> {
|
||||||
|
|||||||
Reference in New Issue
Block a user