mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 09:01:03 -08:00
chore(nx): update Nx to 22.7.1 (#923)
* chore(nx): update Nx to 22.7.1 Entire-Checkpoint: f957cd9849e0 * fix(ci): patch nx-electron package metadata copy Entire-Checkpoint: f957cd9849e0 * fix(packaging): preserve electron package metadata Entire-Checkpoint: f957cd9849e0 * fix(packaging): address package verifier review Entire-Checkpoint: f957cd9849e0
This commit is contained in:
1 parent
6305dcf00e
commit
1ca291d35c
13 files changed
+2608
-1975
No files matched your search
@@ -78,6 +78,7 @@ Thumbs.db
|
||||
|
||||
# Native Electron addons
|
||||
apps/electron-backend/native/build/
|
||||
apps/electron-backend/src/app/options/electron-builder.metadata.generated.json
|
||||
vendor/embedded-mpv/darwin-*/include/
|
||||
vendor/embedded-mpv/darwin-*/lib/
|
||||
vendor/embedded-mpv/darwin-*/runtime-manifest.json
|
||||
@@ -4,3 +4,5 @@ build
|
||||
coverage
|
||||
release
|
||||
.angular
|
||||
|
||||
.nx/self-healing
|
||||
@@ -125,7 +125,10 @@
|
||||
}
|
||||
},
|
||||
"package": {
|
||||
"dependsOn": ["electron-backend:build"],
|
||||
"dependsOn": [
|
||||
"electron-backend:build",
|
||||
"electron-backend:generate-builder-metadata"
|
||||
],
|
||||
"executor": "nx-electron:package",
|
||||
"options": {
|
||||
"name": "electron-backend",
|
||||
@@ -136,7 +139,10 @@
|
||||
}
|
||||
},
|
||||
"make": {
|
||||
"dependsOn": ["electron-backend:build"],
|
||||
"dependsOn": [
|
||||
"electron-backend:build",
|
||||
"electron-backend:generate-builder-metadata"
|
||||
],
|
||||
"executor": "nx-electron:make",
|
||||
"options": {
|
||||
"name": "electron-backend",
|
||||
@@ -145,6 +151,24 @@
|
||||
"outputPath": "dist/executables"
|
||||
}
|
||||
},
|
||||
"generate-builder-metadata": {
|
||||
"dependsOn": ["electron-backend:build"],
|
||||
"executor": "nx:run-commands",
|
||||
"cache": true,
|
||||
"inputs": [
|
||||
"{workspaceRoot}/package.json",
|
||||
"{workspaceRoot}/electron-builder.json",
|
||||
"{workspaceRoot}/tools/packaging/generate-electron-builder-metadata.mjs"
|
||||
],
|
||||
"outputs": [
|
||||
"{workspaceRoot}/apps/electron-backend/src/app/options/electron-builder.metadata.generated.json",
|
||||
"{workspaceRoot}/dist/apps/electron-backend/package.json"
|
||||
],
|
||||
"options": {
|
||||
"command": "node tools/packaging/generate-electron-builder-metadata.mjs",
|
||||
"cwd": "{workspaceRoot}"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"command": "eslint apps/electron-backend/**/*.ts"
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
{
|
||||
"$schema": "../../../../../node_modules/nx-electron/src/validation/maker.schema.json"
|
||||
"$schema": "../../../../../node_modules/nx-electron/src/validation/maker.schema.json",
|
||||
"extends": "apps/electron-backend/src/app/options/electron-builder.metadata.generated.json"
|
||||
}
|
||||
@@ -526,9 +526,9 @@ export async function initDatabase(
|
||||
sqlite = new Database(filePath, {
|
||||
readonly,
|
||||
verbose: isSqlTraceEnabled()
|
||||
? (sql: string) => {
|
||||
? (message?: unknown) => {
|
||||
traceSql('sql-main', 'query', {
|
||||
sql: compactSqlForTrace(sql),
|
||||
sql: compactSqlForTrace(String(message ?? '')),
|
||||
});
|
||||
}
|
||||
: undefined,
|
||||
|
||||
@@ -24,9 +24,11 @@
|
||||
"cache": true,
|
||||
"inputs": [
|
||||
"default",
|
||||
"^default",
|
||||
"{workspaceRoot}/.eslintrc.json",
|
||||
"{workspaceRoot}/.eslintignore",
|
||||
"{workspaceRoot}/eslint.config.mjs"
|
||||
"{workspaceRoot}/eslint.config.mjs",
|
||||
"{workspaceRoot}/tools/eslint-rules/**/*"
|
||||
]
|
||||
},
|
||||
"@nx/jest:jest": {
|
||||
|
||||
+47
-42
@@ -58,16 +58,16 @@
|
||||
"private": true,
|
||||
"packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319",
|
||||
"dependencies": {
|
||||
"@angular/animations": "21.1.5",
|
||||
"@angular/cdk": "^21.1.5",
|
||||
"@angular/common": "~21.1.5",
|
||||
"@angular/compiler": "~21.1.5",
|
||||
"@angular/core": "~21.1.5",
|
||||
"@angular/forms": "~21.1.5",
|
||||
"@angular/material": "^21.1.5",
|
||||
"@angular/platform-browser": "~21.1.5",
|
||||
"@angular/platform-browser-dynamic": "~21.1.5",
|
||||
"@angular/router": "~21.1.5",
|
||||
"@angular/animations": "21.2.9",
|
||||
"@angular/cdk": "21.2.9",
|
||||
"@angular/common": "21.2.9",
|
||||
"@angular/compiler": "21.2.9",
|
||||
"@angular/core": "21.2.9",
|
||||
"@angular/forms": "21.2.9",
|
||||
"@angular/material": "21.2.9",
|
||||
"@angular/platform-browser": "21.2.9",
|
||||
"@angular/platform-browser-dynamic": "21.2.9",
|
||||
"@angular/router": "21.2.9",
|
||||
"@ngrx/component-store": "21.0.1",
|
||||
"@ngrx/effects": "21.0.1",
|
||||
"@ngrx/entity": "21.0.1",
|
||||
@@ -103,28 +103,29 @@
|
||||
"zone.js": "~0.15.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/core": "~21.1.4",
|
||||
"@angular-devkit/schematics": "~21.1.4",
|
||||
"@angular-eslint/builder": "21.2.0",
|
||||
"@angular-eslint/eslint-plugin": "21.2.0",
|
||||
"@angular-eslint/eslint-plugin-template": "21.2.0",
|
||||
"@angular-eslint/schematics": "21.2.0",
|
||||
"@angular-eslint/template-parser": "21.2.0",
|
||||
"@angular/build": "~21.1.4",
|
||||
"@angular/cli": "~21.1.4",
|
||||
"@angular/common": "21.1.5",
|
||||
"@angular/compiler": "21.1.5",
|
||||
"@angular/compiler-cli": "21.1.5",
|
||||
"@angular/core": "21.1.5",
|
||||
"@angular/forms": "21.1.5",
|
||||
"@angular/language-service": "21.1.5",
|
||||
"@angular/platform-browser": "21.1.5",
|
||||
"@angular/platform-browser-dynamic": "21.1.5",
|
||||
"@angular/router": "21.1.5",
|
||||
"@angular/service-worker": "21.1.5",
|
||||
"@angular-devkit/core": "21.2.9",
|
||||
"@angular-devkit/schematics": "21.2.9",
|
||||
"@angular-eslint/builder": "21.3.1",
|
||||
"@angular-eslint/eslint-plugin": "21.3.1",
|
||||
"@angular-eslint/eslint-plugin-template": "21.3.1",
|
||||
"@angular-eslint/schematics": "21.3.1",
|
||||
"@angular-eslint/template-parser": "21.3.1",
|
||||
"@angular/build": "21.2.9",
|
||||
"@angular/cli": "21.2.10",
|
||||
"@angular/common": "21.2.9",
|
||||
"@angular/compiler": "21.2.9",
|
||||
"@angular/compiler-cli": "21.2.9",
|
||||
"@angular/core": "21.2.9",
|
||||
"@angular/forms": "21.2.9",
|
||||
"@angular/language-service": "21.2.9",
|
||||
"@angular/platform-browser": "21.2.9",
|
||||
"@angular/platform-browser-dynamic": "21.2.9",
|
||||
"@angular/router": "21.2.9",
|
||||
"@angular/service-worker": "21.2.9",
|
||||
"@astrojs/mdx": "4.3.13",
|
||||
"@astrojs/sitemap": "3.7.0",
|
||||
"@astrojs/tailwind": "6.0.2",
|
||||
"@electron/asar": "3.4.1",
|
||||
"@eslint/eslintrc": "3.3.1",
|
||||
"@eslint/js": "^9.38.0",
|
||||
"@faker-js/faker": "10.3.0",
|
||||
@@ -135,17 +136,18 @@
|
||||
"@ngrx/eslint-plugin": "^21.0.1",
|
||||
"@ngx-translate/core": "16.0.4",
|
||||
"@ngx-translate/http-loader": "16.0.1",
|
||||
"@nx/angular": "22.5.1",
|
||||
"@nx/devkit": "22.5.1",
|
||||
"@nx/eslint": "22.5.1",
|
||||
"@nx/eslint-plugin": "22.5.1",
|
||||
"@nx/jest": "22.5.1",
|
||||
"@nx/js": "22.5.1",
|
||||
"@nx/playwright": "22.5.1",
|
||||
"@nx/web": "22.5.1",
|
||||
"@nx/workspace": "22.5.1",
|
||||
"@nx/angular": "22.7.1",
|
||||
"@nx/devkit": "22.7.1",
|
||||
"@nx/esbuild": "22.7.1",
|
||||
"@nx/eslint": "22.7.1",
|
||||
"@nx/eslint-plugin": "22.7.1",
|
||||
"@nx/jest": "22.7.1",
|
||||
"@nx/js": "22.7.1",
|
||||
"@nx/playwright": "22.7.1",
|
||||
"@nx/web": "22.7.1",
|
||||
"@nx/workspace": "22.7.1",
|
||||
"@playwright/test": "^1.36.0",
|
||||
"@schematics/angular": "~21.1.4",
|
||||
"@schematics/angular": "21.2.9",
|
||||
"@semantic-release/changelog": "6.0.3",
|
||||
"@semantic-release/git": "10.0.1",
|
||||
"@semantic-release/npm": "12.0.1",
|
||||
@@ -165,7 +167,7 @@
|
||||
"@typescript-eslint/eslint-plugin": "^8.46.2",
|
||||
"@typescript-eslint/parser": "^8.46.2",
|
||||
"@typescript-eslint/utils": "^8.46.2",
|
||||
"angular-eslint": "20.5.2",
|
||||
"angular-eslint": "21.3.1",
|
||||
"astro": "5.18.1",
|
||||
"conventional-changelog-cli": "5.0.0",
|
||||
"cors": "2.8.6",
|
||||
@@ -188,8 +190,8 @@
|
||||
"jsonc-eslint-parser": "^2.1.0",
|
||||
"material-design-icons-iconfont": "6.7.0",
|
||||
"ng-mocks": "14.15.1",
|
||||
"nx": "22.5.1",
|
||||
"nx-electron": "^21.0.1",
|
||||
"nx": "22.7.1",
|
||||
"nx-electron": "22.0.0",
|
||||
"prettier": "^3.8.1",
|
||||
"semantic-release": "24.2.9",
|
||||
"sharp": "0.34.5",
|
||||
@@ -222,6 +224,9 @@
|
||||
"smol-toml@1.6.0": "1.6.1",
|
||||
"svgo@3.3.2": "3.3.3",
|
||||
"yaml@1.10.2": "1.10.3"
|
||||
},
|
||||
"patchedDependencies": {
|
||||
"nx-electron@22.0.0": "patches/nx-electron@22.0.0.patch"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
diff --git a/src/executors/package/executor.js b/src/executors/package/executor.js
|
||||
index e36f61a825a280c26ec5064f1ba086ac170fb1b6..17a351dd4b91bf3b72743036f79c8300aba7f79e 100644
|
||||
--- a/src/executors/package/executor.js
|
||||
+++ b/src/executors/package/executor.js
|
||||
@@ -111,7 +111,6 @@ function _createBaseConfig(options, context) {
|
||||
return {
|
||||
directories: Object.assign(Object.assign({}, options.directories), { output: (0, path_1.join)(context.root, outputPath) }),
|
||||
files: files.concat([
|
||||
- './package.json',
|
||||
{
|
||||
from: (0, path_1.resolve)(options.sourcePath, options.name),
|
||||
to: options.name,
|
||||
Generated
+2114
-1908
File diff suppressed because it is too large.
Load diff
@@ -1,16 +1,49 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import { createRequire } from 'node:module';
|
||||
import { dirname, join } from 'node:path';
|
||||
import test from 'node:test';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const currentDir = dirname(fileURLToPath(import.meta.url));
|
||||
const require = createRequire(import.meta.url);
|
||||
const packageMetadata = JSON.parse(
|
||||
fs.readFileSync(join(currentDir, '..', '..', 'package.json'), 'utf8')
|
||||
);
|
||||
const electronBuilderConfig = JSON.parse(
|
||||
fs.readFileSync(
|
||||
join(currentDir, '..', '..', 'electron-builder.json'),
|
||||
'utf8'
|
||||
)
|
||||
);
|
||||
const electronProjectConfig = JSON.parse(
|
||||
fs.readFileSync(
|
||||
join(currentDir, '..', '..', 'apps', 'electron-backend', 'project.json'),
|
||||
'utf8'
|
||||
)
|
||||
);
|
||||
const makerOptions = JSON.parse(
|
||||
fs.readFileSync(
|
||||
join(
|
||||
currentDir,
|
||||
'..',
|
||||
'..',
|
||||
'apps',
|
||||
'electron-backend',
|
||||
'src',
|
||||
'app',
|
||||
'options',
|
||||
'maker.options.json'
|
||||
),
|
||||
'utf8'
|
||||
)
|
||||
);
|
||||
const generatedMetadataConfigPath =
|
||||
'apps/electron-backend/src/app/options/electron-builder.metadata.generated.json';
|
||||
const packageLayoutVerifier = fs.readFileSync(
|
||||
join(currentDir, 'verify-electron-package-layout.mjs'),
|
||||
'utf8'
|
||||
);
|
||||
|
||||
test('Linux package identity does not expose the internal Electron backend project name', () => {
|
||||
assert.equal(electronBuilderConfig.productName, 'IPTVnator');
|
||||
@@ -22,3 +55,110 @@ test('Linux package identity does not expose the internal Electron backend proje
|
||||
'iptvnator'
|
||||
);
|
||||
});
|
||||
|
||||
test('generated Electron package metadata mirrors the root package identity', async () => {
|
||||
const {
|
||||
buildElectronBuilderMetadata,
|
||||
buildElectronPackageMetadata,
|
||||
} = await import(
|
||||
'./generate-electron-builder-metadata.mjs'
|
||||
);
|
||||
const generatedElectronPackage = buildElectronPackageMetadata(
|
||||
packageMetadata,
|
||||
electronBuilderConfig,
|
||||
{
|
||||
name: 'electron-backend',
|
||||
version: '0.0.1',
|
||||
dependencies: {
|
||||
'better-sqlite3': '12.5.0',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
buildElectronBuilderMetadata(packageMetadata, electronBuilderConfig)
|
||||
.extraMetadata,
|
||||
{
|
||||
name: packageMetadata.name,
|
||||
productName: electronBuilderConfig.productName,
|
||||
version: packageMetadata.version,
|
||||
description: packageMetadata.description,
|
||||
author: packageMetadata.author,
|
||||
homepage: packageMetadata.homepage,
|
||||
license: packageMetadata.license,
|
||||
main: electronBuilderConfig.extraMetadata.main,
|
||||
}
|
||||
);
|
||||
assert.deepEqual(generatedElectronPackage, {
|
||||
name: packageMetadata.name,
|
||||
productName: electronBuilderConfig.productName,
|
||||
version: packageMetadata.version,
|
||||
description: packageMetadata.description,
|
||||
author: packageMetadata.author,
|
||||
homepage: packageMetadata.homepage,
|
||||
license: packageMetadata.license,
|
||||
main: electronBuilderConfig.extraMetadata.main,
|
||||
dependencies: {
|
||||
'better-sqlite3': '12.5.0',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('nx-electron packaging prepares metadata before make/package', () => {
|
||||
assert.equal(makerOptions.extends, generatedMetadataConfigPath);
|
||||
assert.deepEqual(
|
||||
electronProjectConfig.targets['generate-builder-metadata'].dependsOn,
|
||||
['electron-backend:build']
|
||||
);
|
||||
assert.deepEqual(
|
||||
electronProjectConfig.targets['generate-builder-metadata'].outputs,
|
||||
[
|
||||
`{workspaceRoot}/${generatedMetadataConfigPath}`,
|
||||
'{workspaceRoot}/dist/apps/electron-backend/package.json',
|
||||
]
|
||||
);
|
||||
assert.ok(
|
||||
electronProjectConfig.targets.make.dependsOn.includes(
|
||||
'electron-backend:generate-builder-metadata'
|
||||
)
|
||||
);
|
||||
assert.ok(
|
||||
electronProjectConfig.targets.package.dependsOn.includes(
|
||||
'electron-backend:generate-builder-metadata'
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
test('package layout verifier uses canonical helpers and direct dependencies', () => {
|
||||
assert.ok(packageMetadata.devDependencies?.['@electron/asar']);
|
||||
assert.match(packageLayoutVerifier, /require\(['"]@electron\/asar['"]\)/);
|
||||
assert.doesNotMatch(packageLayoutVerifier, /electronBuilderRequire/);
|
||||
assert.match(
|
||||
packageLayoutVerifier,
|
||||
/buildElectronBuilderMetadata\(\s*packageMetadata,\s*electronBuilderConfig\s*\)\.extraMetadata/s
|
||||
);
|
||||
assert.doesNotMatch(
|
||||
packageLayoutVerifier,
|
||||
/const packagedPackageMetadata = \{\s*name:/s
|
||||
);
|
||||
assert.doesNotMatch(
|
||||
packageLayoutVerifier,
|
||||
/builderEffectiveConfigPath && fileExists\(builderEffectiveConfigPath\)/
|
||||
);
|
||||
});
|
||||
|
||||
test('nx-electron packaging does not copy duplicate root package metadata', () => {
|
||||
const nxElectronExecutorPath = require.resolve(
|
||||
'nx-electron/src/executors/package/executor.js'
|
||||
);
|
||||
const nxElectronExecutor = fs.readFileSync(
|
||||
nxElectronExecutorPath,
|
||||
'utf8'
|
||||
);
|
||||
|
||||
assert.doesNotMatch(nxElectronExecutor, /['"]\.\/package\.json['"]/);
|
||||
assert.match(
|
||||
nxElectronExecutor,
|
||||
/filter:\s*\[['"]index\.js['"],\s*['"]package\.json['"]\]/
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,156 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
export const generatedMetadataConfigPath = path.join(
|
||||
'apps',
|
||||
'electron-backend',
|
||||
'src',
|
||||
'app',
|
||||
'options',
|
||||
'electron-builder.metadata.generated.json'
|
||||
);
|
||||
export const electronBackendPackageJsonPath = path.join(
|
||||
'dist',
|
||||
'apps',
|
||||
'electron-backend',
|
||||
'package.json'
|
||||
);
|
||||
|
||||
function readJson(filePath) {
|
||||
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||||
}
|
||||
|
||||
function removeUndefinedValues(value) {
|
||||
return Object.fromEntries(
|
||||
Object.entries(value).filter(([, entryValue]) => entryValue !== undefined)
|
||||
);
|
||||
}
|
||||
|
||||
function requirePackageField(packageMetadata, fieldName) {
|
||||
const value = packageMetadata[fieldName];
|
||||
|
||||
if (typeof value !== 'string' || value.length === 0) {
|
||||
throw new Error(`Root package.json must define a non-empty ${fieldName}.`);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
export function buildElectronBuilderMetadata(
|
||||
packageMetadata,
|
||||
electronBuilderConfig = {}
|
||||
) {
|
||||
const extraMetadata = removeUndefinedValues({
|
||||
name: requirePackageField(packageMetadata, 'name'),
|
||||
productName:
|
||||
electronBuilderConfig.productName ??
|
||||
electronBuilderConfig.extraMetadata?.productName,
|
||||
version: requirePackageField(packageMetadata, 'version'),
|
||||
description: packageMetadata.description,
|
||||
author: packageMetadata.author,
|
||||
homepage: packageMetadata.homepage,
|
||||
license: packageMetadata.license,
|
||||
main:
|
||||
electronBuilderConfig.extraMetadata?.main ??
|
||||
'electron-backend/main.js',
|
||||
});
|
||||
|
||||
return {
|
||||
extends: 'electron-builder.json',
|
||||
extraMetadata,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildElectronPackageMetadata(
|
||||
packageMetadata,
|
||||
electronBuilderConfig = {},
|
||||
currentElectronPackageMetadata = {}
|
||||
) {
|
||||
return removeUndefinedValues({
|
||||
...currentElectronPackageMetadata,
|
||||
...buildElectronBuilderMetadata(packageMetadata, electronBuilderConfig)
|
||||
.extraMetadata,
|
||||
});
|
||||
}
|
||||
|
||||
export function writeElectronPackageMetadata({
|
||||
workspaceRoot = process.cwd(),
|
||||
packageMetadata,
|
||||
electronBuilderConfig,
|
||||
packageJsonPath = electronBackendPackageJsonPath,
|
||||
} = {}) {
|
||||
const absolutePackageJsonPath = path.join(workspaceRoot, packageJsonPath);
|
||||
|
||||
if (!fs.existsSync(absolutePackageJsonPath)) {
|
||||
return {
|
||||
outputPath: absolutePackageJsonPath,
|
||||
updated: false,
|
||||
};
|
||||
}
|
||||
|
||||
const currentElectronPackageMetadata = readJson(absolutePackageJsonPath);
|
||||
const nextElectronPackageMetadata = buildElectronPackageMetadata(
|
||||
packageMetadata,
|
||||
electronBuilderConfig,
|
||||
currentElectronPackageMetadata
|
||||
);
|
||||
|
||||
fs.writeFileSync(
|
||||
absolutePackageJsonPath,
|
||||
`${JSON.stringify(nextElectronPackageMetadata, null, 2)}\n`
|
||||
);
|
||||
|
||||
return {
|
||||
outputPath: absolutePackageJsonPath,
|
||||
updated: true,
|
||||
};
|
||||
}
|
||||
|
||||
export function writeElectronBuilderMetadata({
|
||||
workspaceRoot = process.cwd(),
|
||||
outputPath = generatedMetadataConfigPath,
|
||||
} = {}) {
|
||||
const packageMetadata = readJson(path.join(workspaceRoot, 'package.json'));
|
||||
const electronBuilderConfig = readJson(
|
||||
path.join(workspaceRoot, 'electron-builder.json')
|
||||
);
|
||||
const metadataConfig = buildElectronBuilderMetadata(
|
||||
packageMetadata,
|
||||
electronBuilderConfig
|
||||
);
|
||||
const absoluteOutputPath = path.join(workspaceRoot, outputPath);
|
||||
const packageMetadataResult = writeElectronPackageMetadata({
|
||||
workspaceRoot,
|
||||
packageMetadata,
|
||||
electronBuilderConfig,
|
||||
});
|
||||
|
||||
fs.mkdirSync(path.dirname(absoluteOutputPath), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
absoluteOutputPath,
|
||||
`${JSON.stringify(metadataConfig, null, 4)}\n`
|
||||
);
|
||||
|
||||
return {
|
||||
outputPath: absoluteOutputPath,
|
||||
metadataConfig,
|
||||
packageMetadataResult,
|
||||
};
|
||||
}
|
||||
|
||||
const currentFilePath = fileURLToPath(import.meta.url);
|
||||
|
||||
if (process.argv[1] && path.resolve(process.argv[1]) === currentFilePath) {
|
||||
const { outputPath, packageMetadataResult } = writeElectronBuilderMetadata();
|
||||
console.log(`Wrote Electron builder metadata: ${outputPath}`);
|
||||
if (packageMetadataResult.updated) {
|
||||
console.log(
|
||||
`Wrote Electron package metadata: ${packageMetadataResult.outputPath}`
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
`Skipped Electron package metadata because ${packageMetadataResult.outputPath} does not exist yet.`
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,14 @@
|
||||
"test": {
|
||||
"executor": "nx:run-commands",
|
||||
"cache": true,
|
||||
"inputs": ["default", "{workspaceRoot}/electron-builder.json"],
|
||||
"inputs": [
|
||||
"default",
|
||||
"{workspaceRoot}/package.json",
|
||||
"{workspaceRoot}/electron-builder.json",
|
||||
"{workspaceRoot}/apps/electron-backend/project.json",
|
||||
"{workspaceRoot}/apps/electron-backend/src/app/options/maker.options.json",
|
||||
"{workspaceRoot}/tools/packaging/generate-electron-builder-metadata.mjs"
|
||||
],
|
||||
"options": {
|
||||
"command": "node --test tools/packaging/electron-package-identity.test.mjs",
|
||||
"cwd": "{workspaceRoot}"
|
||||
|
||||
@@ -2,7 +2,10 @@ import fs from 'fs';
|
||||
import { createRequire } from 'module';
|
||||
import path from 'path';
|
||||
|
||||
import { buildElectronBuilderMetadata } from './generate-electron-builder-metadata.mjs';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const { extractFile } = require('@electron/asar');
|
||||
const { validatePackagedEmbeddedMpv } = require('./embedded-mpv-macos.cjs');
|
||||
const args = process.argv.slice(2);
|
||||
const normalizedArgs = args[0] === '--' ? args.slice(1) : args;
|
||||
@@ -16,15 +19,17 @@ if (!platform) {
|
||||
}
|
||||
|
||||
const workspaceRoot = process.cwd();
|
||||
const executablesRoot = path.join(workspaceRoot, 'dist', 'executables');
|
||||
const packageOutputRoots = [
|
||||
path.join(workspaceRoot, 'dist', 'executables'),
|
||||
path.join(workspaceRoot, 'dist', 'packages'),
|
||||
];
|
||||
const packageJsonPath = path.join(workspaceRoot, 'package.json');
|
||||
const electronBuilderConfigPath = path.join(
|
||||
workspaceRoot,
|
||||
'electron-builder.json'
|
||||
);
|
||||
const builderEffectiveConfigPath = path.join(
|
||||
executablesRoot,
|
||||
'builder-effective-config.yaml'
|
||||
const builderEffectiveConfigPaths = packageOutputRoots.map((outputRoot) =>
|
||||
path.join(outputRoot, 'builder-effective-config.yaml')
|
||||
);
|
||||
const flatpakMetainfoPath = path.join(
|
||||
workspaceRoot,
|
||||
@@ -47,6 +52,10 @@ const workerRelativeDir = path.join(
|
||||
'workers'
|
||||
);
|
||||
const workerFiles = ['epg-parser.worker.js', 'database.worker.js'];
|
||||
const packagedPackageMetadata = buildElectronBuilderMetadata(
|
||||
packageMetadata,
|
||||
electronBuilderConfig
|
||||
).extraMetadata;
|
||||
const nativeModuleRelativeDirs = [
|
||||
path.join('app.asar.unpacked', 'node_modules'),
|
||||
path.join('app.asar.unpacked', 'electron-backend', 'node_modules'),
|
||||
@@ -79,11 +88,11 @@ function isTruthy(value) {
|
||||
}
|
||||
|
||||
function getMacResourceDirs() {
|
||||
const candidates = [
|
||||
const candidates = packageOutputRoots.flatMap((outputRoot) => [
|
||||
{
|
||||
arch: 'x64',
|
||||
directory: path.join(
|
||||
executablesRoot,
|
||||
outputRoot,
|
||||
'mac',
|
||||
'IPTVnator.app',
|
||||
'Contents',
|
||||
@@ -93,14 +102,14 @@ function getMacResourceDirs() {
|
||||
{
|
||||
arch: 'arm64',
|
||||
directory: path.join(
|
||||
executablesRoot,
|
||||
outputRoot,
|
||||
'mac-arm64',
|
||||
'IPTVnator.app',
|
||||
'Contents',
|
||||
'Resources'
|
||||
),
|
||||
},
|
||||
];
|
||||
]);
|
||||
|
||||
return candidates
|
||||
.filter((candidate) => !arch || candidate.arch === arch)
|
||||
@@ -109,20 +118,20 @@ function getMacResourceDirs() {
|
||||
}
|
||||
|
||||
function getUnpackedResourceDirs(prefix) {
|
||||
if (!directoryExists(executablesRoot)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return fs
|
||||
.readdirSync(executablesRoot, { withFileTypes: true })
|
||||
return packageOutputRoots
|
||||
.filter(directoryExists)
|
||||
.flatMap((outputRoot) =>
|
||||
fs
|
||||
.readdirSync(outputRoot, { withFileTypes: true })
|
||||
.filter(
|
||||
(entry) =>
|
||||
entry.isDirectory() &&
|
||||
entry.name.startsWith(prefix) &&
|
||||
entry.name.endsWith('-unpacked')
|
||||
)
|
||||
.map((entry) => path.join(executablesRoot, entry.name, 'resources'))
|
||||
.filter(directoryExists);
|
||||
.map((entry) => path.join(outputRoot, entry.name, 'resources'))
|
||||
.filter(directoryExists)
|
||||
);
|
||||
}
|
||||
|
||||
function getResourceDirs() {
|
||||
@@ -140,7 +149,15 @@ function getResourceDirs() {
|
||||
}
|
||||
|
||||
function sanitizeExecutableName(value) {
|
||||
return value.replace(/[<>:"/\\|?*\u0000-\u001f]/g, '');
|
||||
const invalidCharacters = new Set(['<', '>', ':', '"', '/', '\\', '|', '?', '*']);
|
||||
|
||||
return [...value]
|
||||
.filter(
|
||||
(character) =>
|
||||
character.charCodeAt(0) >= 32 &&
|
||||
!invalidCharacters.has(character)
|
||||
)
|
||||
.join('');
|
||||
}
|
||||
|
||||
function getLinuxExecutableName() {
|
||||
@@ -345,7 +362,9 @@ function parseEffectiveSnapConfig(yamlContent) {
|
||||
}
|
||||
|
||||
function loadSnapConfigInspection() {
|
||||
if (fileExists(builderEffectiveConfigPath)) {
|
||||
const builderEffectiveConfigPath = builderEffectiveConfigPaths.find(fileExists);
|
||||
|
||||
if (builderEffectiveConfigPath) {
|
||||
const effectiveConfigContent = fs.readFileSync(
|
||||
builderEffectiveConfigPath,
|
||||
'utf8'
|
||||
@@ -368,6 +387,62 @@ function loadSnapConfigInspection() {
|
||||
};
|
||||
}
|
||||
|
||||
function formatJsonValue(value) {
|
||||
return JSON.stringify(value);
|
||||
}
|
||||
|
||||
function packageMetadataMatches(actualValue, expectedValue) {
|
||||
return formatJsonValue(actualValue) === formatJsonValue(expectedValue);
|
||||
}
|
||||
|
||||
function readAsarPackageMetadata(asarPath) {
|
||||
return JSON.parse(extractFile(asarPath, 'package.json').toString('utf8'));
|
||||
}
|
||||
|
||||
function verifyPackagedPackageMetadata(resourceDir, errors) {
|
||||
const asarPath = path.join(resourceDir, 'app.asar');
|
||||
|
||||
if (!fileExists(asarPath)) {
|
||||
errors.push(`Missing packaged app archive: ${asarPath}`);
|
||||
return;
|
||||
}
|
||||
|
||||
let appPackageMetadata;
|
||||
|
||||
try {
|
||||
appPackageMetadata = readAsarPackageMetadata(asarPath);
|
||||
} catch (error) {
|
||||
errors.push(
|
||||
`Unable to read package.json from ${asarPath}: ${error.message}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const mismatches = Object.entries(packagedPackageMetadata)
|
||||
.filter(([, expectedValue]) => expectedValue !== undefined)
|
||||
.filter(
|
||||
([fieldName, expectedValue]) =>
|
||||
!packageMetadataMatches(
|
||||
appPackageMetadata[fieldName],
|
||||
expectedValue
|
||||
)
|
||||
)
|
||||
.map(([fieldName, expectedValue]) => {
|
||||
const actualValue = appPackageMetadata[fieldName];
|
||||
|
||||
return `- ${fieldName}: expected ${formatJsonValue(expectedValue)}, received ${formatJsonValue(actualValue)}`;
|
||||
});
|
||||
|
||||
if (mismatches.length > 0) {
|
||||
errors.push(
|
||||
[
|
||||
`Packaged app package.json metadata does not match root package identity in ${asarPath}.`,
|
||||
...mismatches,
|
||||
].join('\n')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function verifyLinuxLauncher(resourceDir, errors) {
|
||||
const appDir = path.dirname(resourceDir);
|
||||
const launcherPath = path.join(appDir, linuxExecutableName);
|
||||
@@ -462,6 +537,8 @@ function verifyResourceDir(resourceDir) {
|
||||
|
||||
const errors = [];
|
||||
|
||||
verifyPackagedPackageMetadata(resourceDir, errors);
|
||||
|
||||
if (missingWorkers.length > 0) {
|
||||
errors.push(
|
||||
`Missing worker artifacts in ${resourceDir}: ${missingWorkers.join(', ')}`
|
||||
|
||||
Reference in new issue
Block a user