@synopackageland/cli 0.7.1 → 0.7.2

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.
Files changed (45) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/assets/default-icon/README.md +19 -0
  3. package/assets/default-icon/default-icon-small.svg +9 -0
  4. package/assets/default-icon/default-icon.svg +10 -0
  5. package/assets/default-icon/icon_16.png +0 -0
  6. package/assets/default-icon/icon_24.png +0 -0
  7. package/assets/default-icon/icon_256.png +0 -0
  8. package/assets/default-icon/icon_32.png +0 -0
  9. package/assets/default-icon/icon_48.png +0 -0
  10. package/assets/default-icon/icon_64.png +0 -0
  11. package/assets/default-icon/icon_72.png +0 -0
  12. package/dist/build-spk/dsm-icons.d.ts +31 -2
  13. package/dist/build-spk/dsm-icons.js +135 -56
  14. package/dist/build-spk/generator.js +17 -3
  15. package/dist/build-spk/png.d.ts +14 -0
  16. package/dist/build-spk/png.js +161 -0
  17. package/dist/build-spk/ui-texts.d.ts +20 -0
  18. package/dist/build-spk/ui-texts.js +42 -0
  19. package/dist/cli.js +26 -2
  20. package/dist/contract/i18n.d.ts +5 -0
  21. package/dist/contract/i18n.js +45 -0
  22. package/dist/contract/icon.d.ts +5 -0
  23. package/dist/contract/icon.js +27 -0
  24. package/dist/contract/validate.js +4 -0
  25. package/dist/contract/web-launch.js +6 -2
  26. package/dist/error-registry.js +3 -0
  27. package/dist/standalone/dsm-window.d.ts +2 -0
  28. package/dist/standalone/dsm-window.js +20 -6
  29. package/dist/standalone/i18n.d.ts +12 -0
  30. package/dist/standalone/i18n.js +30 -0
  31. package/dist/standalone/inspect-spk.d.ts +5 -0
  32. package/dist/standalone/inspect-spk.js +36 -1
  33. package/dist/standalone/manifest.d.ts +5 -6
  34. package/dist/standalone/manifest.js +21 -34
  35. package/dist/standalone/native-layout.d.ts +2 -0
  36. package/dist/standalone/native-layout.js +22 -1
  37. package/dist/standalone/spk.js +7 -4
  38. package/dist/templates/binary/dsm-package.yaml +3 -0
  39. package/dist/templates/compose/synology-app.yaml +3 -0
  40. package/dist/templates/native-go/dsm-package.yaml +3 -0
  41. package/package.json +2 -1
  42. package/schema/synology-app.schema.json +22 -0
  43. package/templates/binary/dsm-package.yaml +3 -0
  44. package/templates/compose/synology-app.yaml +3 -0
  45. package/templates/native-go/dsm-package.yaml +3 -0
@@ -8,6 +8,7 @@ import { parse } from 'yaml';
8
8
  import { createReproducibleTar } from '../build-spk/reproducible-archive.js';
9
9
  import { runCommand } from './build.js';
10
10
  import { resolveMinDsm } from '../build-spk/dsm-version.js';
11
+ import { renderPackageIcons, writePackageCenterIcons } from '../build-spk/dsm-icons.js';
11
12
  export const LAYOUT_ARCHES = { x86_64: [2, 62], aarch64: [2, 183], armv8: [2, 183], armv7: [1, 40] };
12
13
  function fail(message) { throw Object.assign(new Error(`NATIVE_LAYOUT_INVALID: ${message}`), { code: 'NATIVE_LAYOUT_INVALID' }); }
13
14
  const digest = (bytes) => createHash('sha256').update(bytes).digest('hex');
@@ -61,6 +62,18 @@ function hook(value) {
61
62
  fail('invalid hook environment');
62
63
  return { command: h.command, cwd: pathText(h.cwd ?? '.'), env };
63
64
  }
65
+ function icon(root, value) {
66
+ const path = pathText(value);
67
+ if (existsSync(resolve(root, path)))
68
+ inside(root, path);
69
+ try {
70
+ renderPackageIcons(resolve(root, path));
71
+ }
72
+ catch (error) {
73
+ fail(`icon: ${error.message}`);
74
+ }
75
+ return path;
76
+ }
64
77
  export function isNativeLayout(root) {
65
78
  try {
66
79
  return (() => { const raw = parse(readFileSync(join(root, 'dsm-package.yaml'), 'utf8')); return raw?.layout !== undefined || raw?.backend?.kind === 'native'; })();
@@ -71,7 +84,7 @@ export function isNativeLayout(root) {
71
84
  }
72
85
  export function loadNativeLayout(root) {
73
86
  try {
74
- const m = object(parse(readFileSync(join(root, 'dsm-package.yaml'), 'utf8')), ['schema', 'backend', 'package', 'version', 'build', 'displayname', 'description', 'maintainer', 'metadata', 'os_min_ver', 'privilege', 'arches', 'layout', 'native']);
87
+ const m = object(parse(readFileSync(join(root, 'dsm-package.yaml'), 'utf8')), ['schema', 'backend', 'package', 'version', 'build', 'displayname', 'description', 'maintainer', 'metadata', 'os_min_ver', 'privilege', 'arches', 'layout', 'native', 'icon']);
75
88
  if (m.schema !== 2)
76
89
  fail('schema 2 required; migrate schema: 1 to schema: 2 and add backend: {kind: native}');
77
90
  const backend = object(m.backend, ['kind']);
@@ -90,6 +103,8 @@ export function loadNativeLayout(root) {
90
103
  text(m[k]);
91
104
  if (!Array.isArray(m.arches) || !m.arches.length || new Set(m.arches).size !== m.arches.length || m.arches.some((a) => !Object.hasOwn(LAYOUT_ARCHES, a)))
92
105
  fail('invalid architecture list');
106
+ if (m.icon !== undefined)
107
+ m.icon = icon(root, m.icon);
93
108
  const layout = object(m.layout, ['payload', 'metadata', 'binaries', 'anchors']);
94
109
  layout.payload = pathText(layout.payload);
95
110
  layout.metadata = pathText(layout.metadata);
@@ -291,6 +306,7 @@ export async function buildNativeLayout(options) {
291
306
  const arches = options.arches ?? m.arches;
292
307
  if (!arches.length || new Set(arches).size !== arches.length || arches.some(a => !m.arches.includes(a)))
293
308
  fail('architecture not declared');
309
+ const icons = m.icon === undefined ? undefined : renderPackageIcons(inside(m.root, m.icon));
294
310
  const run = options.run ?? runCommand;
295
311
  const version = `${m.version}-${String(m.build).padStart(4, '0')}`;
296
312
  const env = { SYNOPKGLAND_PACKAGE: m.package, SYNOPKGLAND_VERSION: m.version, SYNOPKGLAND_BUILD: String(m.build), SYNOPKGLAND_SPK_VERSION: version, SYNOPKGLAND_PROJECT_DIR: m.root };
@@ -313,6 +329,11 @@ export async function buildNativeLayout(options) {
313
329
  for (const b of binaries)
314
330
  checkLayoutBinary(payload, b, arch);
315
331
  copyTree(metadata, stage);
332
+ if (icons) {
333
+ if (['PACKAGE_ICON.PNG', 'PACKAGE_ICON_256.PNG'].some(name => existsSync(join(metadata, name))))
334
+ fail('icon and PACKAGE_ICON files in the layout metadata are mutually exclusive; keep one');
335
+ writePackageCenterIcons(stage, icons);
336
+ }
316
337
  const target = join(stage, 'payload');
317
338
  mkdirSync(target);
318
339
  const files = copyTree(payload, target);
@@ -4,7 +4,7 @@ import { chmodSync, copyFileSync, existsSync, lstatSync, mkdirSync, mkdtempSync,
4
4
  import { tmpdir } from "node:os";
5
5
  import { dirname, join } from "node:path";
6
6
  import { infoArchValue } from "../build-spk/dsm-arch.js";
7
- import { writeDsmWindowIcons, writePackageCenterIcons } from "../build-spk/dsm-icons.js";
7
+ import { packageIcons, writeDsmWindowIcons, writePackageCenterIcons } from "../build-spk/dsm-icons.js";
8
8
  import { renderNativePrivilege } from "../build-spk/native-lifecycle.js";
9
9
  import { renderUninstallWizardFiles } from "../build-spk/uninstall-wizard.js";
10
10
  import { renderInstallWizardFiles, renderNativeInstallWizard } from "../build-spk/install-wizard.js";
@@ -13,7 +13,8 @@ import { renderDelegateScopeSteps } from '../build-spk/delegate-scope.js';
13
13
  import { createReproducibleTar } from "../build-spk/reproducible-archive.js";
14
14
  import { renderStandaloneScript } from "./lifecycle.js";
15
15
  import { DSM_LANGS, LOCALES } from "./i18n.js";
16
- import { CGI_PAYLOAD_PATH, renderUiConfig, renderWindowScript, windowScriptName } from "./dsm-window.js";
16
+ import { appTitle, CGI_PAYLOAD_PATH, renderUiConfig, renderWindowScript, windowScriptName } from "./dsm-window.js";
17
+ import { writeUiTexts } from "../build-spk/ui-texts.js";
17
18
  import { copyStandaloneNotices } from "../build-spk/notices.js";
18
19
  import { hostScriptName, renderHostAdapterScript } from "./host-adapter.js";
19
20
  import { copyDaemonPayload } from "./daemon.js";
@@ -54,6 +55,7 @@ export async function assembleStandaloneSpk(options) {
54
55
  try {
55
56
  if ((manifest.capabilities.rootOps || manifest.capabilities.delegate) && !options.executor)
56
57
  throw new StandaloneBuildError("EXECUTOR_PIN_REQUIRED", "Executor artifact required.");
58
+ const icons = packageIcons(manifest.icon);
57
59
  const uiRoot = join(staging, "package", "ui");
58
60
  const appDir = join(uiRoot, "app");
59
61
  copyUiTree(manifest.ui.dir, appDir);
@@ -61,7 +63,8 @@ export async function assembleStandaloneSpk(options) {
61
63
  writeFile(join(uiRoot, "config"), renderUiConfig(manifest));
62
64
  writeFile(join(uiRoot, windowScriptName(manifest)), renderWindowScript(manifest, assetVersion));
63
65
  writeFile(join(uiRoot, hostScriptName(manifest.build)), renderHostAdapterScript(manifest.package));
64
- writeDsmWindowIcons(uiRoot);
66
+ writeDsmWindowIcons(uiRoot, icons);
67
+ writeUiTexts(uiRoot, appTitle(manifest));
65
68
  const cgiTarget = join(staging, "package", CGI_PAYLOAD_PATH);
66
69
  mkdirSync(dirname(cgiTarget), { recursive: true });
67
70
  copyFileSync(options.cgiPath, cgiTarget);
@@ -129,7 +132,7 @@ export async function assembleStandaloneSpk(options) {
129
132
  ...(folder ? renderSharedFolderUpgradeWizardFiles(manifest.package, folder) : {}),
130
133
  ...renderUninstallWizardFiles(manifest.package, "native", { executor: Boolean(options.executor) }),
131
134
  });
132
- writePackageCenterIcons(staging);
135
+ writePackageCenterIcons(staging, icons);
133
136
  mkdirSync(dirname(options.outputPath), { recursive: true });
134
137
  await createReproducibleTar(staging, options.outputPath);
135
138
  }
@@ -5,6 +5,9 @@ version: 0.1.0
5
5
  build: 1
6
6
  displayname: __DISPLAY_NAME__
7
7
  description: A DSM package with a prebuilt binary CGI and DSM window UI.
8
+ # Package Center and DSM window icon: a square PNG of at least 256x256, or a
9
+ # folder of icon_<size>.png. Omit to use the SDK default icon.
10
+ # icon: assets/icon.png
8
11
  maintainer: Your Name
9
12
  # Optional INFO/website metadata. Omit fields you do not need.
10
13
  # metadata:
@@ -3,6 +3,9 @@ package: com.synology.hello.files
3
3
  version: 0.1.0
4
4
  displayname: Hello Files
5
5
  description: A minimal headless app with a Shared Folder mount.
6
+ # Package Center and DSM window icon: a square PNG of at least 256x256, or a
7
+ # folder of icon_<size>.png. Omit to use the SDK default icon.
8
+ # icon: assets/icon.png
6
9
  maintainer: Syno Package Land
7
10
  os_min_ver: "7.2.1-69057"
8
11
 
@@ -10,6 +10,9 @@ version: 0.1.0
10
10
  build: 1
11
11
  displayname: __DISPLAY_NAME__
12
12
  description: A native DSM package with a Go backend and a DSM window UI.
13
+ # Package Center and DSM window icon: a square PNG of at least 256x256, or a
14
+ # folder of icon_<size>.png. Omit to use the SDK default icon.
15
+ # icon: assets/icon.png
13
16
  maintainer: Your Name
14
17
  # Optional INFO/website metadata. Omit fields you do not need.
15
18
  # metadata:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synopackageland/cli",
3
- "version": "0.7.1",
3
+ "version": "0.7.2",
4
4
  "description": "Syno Package Land Developer Kit CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -11,6 +11,7 @@
11
11
  },
12
12
  "files": [
13
13
  "dist",
14
+ "assets",
14
15
  "CHANGELOG.md",
15
16
  "templates",
16
17
  "schema",
@@ -43,6 +43,28 @@
43
43
  "type": "string",
44
44
  "minLength": 1
45
45
  },
46
+ "icon": {
47
+ "type": "string",
48
+ "minLength": 1,
49
+ "description": "Package icon inside the project: a square PNG of at least 256x256, or a directory of icon_<size>.png (16, 24, 32, 48, 64, 72, 256; icon_256.png required). Omit to use the SDK default icon."
50
+ },
51
+ "i18n": {
52
+ "type": "object",
53
+ "description": "Translations; only info is allowed.",
54
+ "properties": {
55
+ "info": {
56
+ "type": "object",
57
+ "description": "Package Center INFO and DSM main menu and window title per locale (zh-TW, zh-CN, ja, ko, de, fr, es, it); English stays top level.",
58
+ "additionalProperties": {
59
+ "type": "object",
60
+ "properties": {
61
+ "displayname": { "type": "string", "minLength": 1 },
62
+ "description": { "type": "string", "minLength": 1 }
63
+ }
64
+ }
65
+ }
66
+ }
67
+ },
46
68
  "adminprotocol": {
47
69
  "type": "string"
48
70
  },
@@ -5,6 +5,9 @@ version: 0.1.0
5
5
  build: 1
6
6
  displayname: __DISPLAY_NAME__
7
7
  description: A DSM package with a prebuilt binary CGI and DSM window UI.
8
+ # Package Center and DSM window icon: a square PNG of at least 256x256, or a
9
+ # folder of icon_<size>.png. Omit to use the SDK default icon.
10
+ # icon: assets/icon.png
8
11
  maintainer: Your Name
9
12
  # Optional INFO/website metadata. Omit fields you do not need.
10
13
  # metadata:
@@ -3,6 +3,9 @@ package: com.synology.hello.files
3
3
  version: 0.1.0
4
4
  displayname: Hello Files
5
5
  description: A minimal headless app with a Shared Folder mount.
6
+ # Package Center and DSM window icon: a square PNG of at least 256x256, or a
7
+ # folder of icon_<size>.png. Omit to use the SDK default icon.
8
+ # icon: assets/icon.png
6
9
  maintainer: Syno Package Land
7
10
  os_min_ver: "7.2.1-69057"
8
11
 
@@ -10,6 +10,9 @@ version: 0.1.0
10
10
  build: 1
11
11
  displayname: __DISPLAY_NAME__
12
12
  description: A native DSM package with a Go backend and a DSM window UI.
13
+ # Package Center and DSM window icon: a square PNG of at least 256x256, or a
14
+ # folder of icon_<size>.png. Omit to use the SDK default icon.
15
+ # icon: assets/icon.png
13
16
  maintainer: Your Name
14
17
  # Optional INFO/website metadata. Omit fields you do not need.
15
18
  # metadata: