From 346b249b3352189b19293758e36ab16bb438d61c Mon Sep 17 00:00:00 2001 From: nameless-mc Date: Tue, 26 May 2026 20:13:10 +0900 Subject: [PATCH 1/3] feat(plugin): add hidden --skip-manifest-validation option to pack and upload --- src/cli/plugin/pack.ts | 6 ++++++ src/cli/plugin/upload.ts | 6 ++++++ src/plugin/core/contents/index.ts | 9 +++++++-- src/plugin/core/plugin/index.ts | 10 ++++++---- src/plugin/packer/pack.ts | 27 +++++++++++++++------------ src/plugin/upload/index.ts | 5 ++++- 6 files changed, 44 insertions(+), 19 deletions(-) diff --git a/src/cli/plugin/pack.ts b/src/cli/plugin/pack.ts index f02d768fe65..113c7d1fa43 100644 --- a/src/cli/plugin/pack.ts +++ b/src/cli/plugin/pack.ts @@ -35,6 +35,11 @@ const builder = (args: yargs.Argv) => .option("watch", { describe: "Run in watch mode", type: "boolean", + }) + .option("skip-manifest-validation", { + type: "boolean", + default: false, + hidden: true, }); type Args = yargs.Arguments< @@ -48,6 +53,7 @@ const handler = async (args: Args) => { output: args.output, ppkFilePath: args["private-key"], watch: args.watch, + skipManifestValidation: args["skip-manifest-validation"], }; if (process.env.NODE_ENV === "test") { console.log(JSON.stringify(params)); diff --git a/src/cli/plugin/upload.ts b/src/cli/plugin/upload.ts index 727b69416c4..25a162777c4 100644 --- a/src/cli/plugin/upload.ts +++ b/src/cli/plugin/upload.ts @@ -30,6 +30,11 @@ const builder = (args: yargs.Argv) => describe: "Run in watch mode", type: "boolean", default: false, + }) + .option("skip-manifest-validation", { + type: "boolean", + default: false, + hidden: true, }); type Args = yargs.Arguments< @@ -43,6 +48,7 @@ const handler = async (args: Args) => { pluginFilePath: args.input, force: args.yes, watch: args.watch, + skipManifestValidation: args["skip-manifest-validation"], }; const apiClientOptions: RestAPIClientOptions = { baseUrl: args["base-url"], diff --git a/src/plugin/core/contents/index.ts b/src/plugin/core/contents/index.ts index 8e944f59563..9ce57cea320 100644 --- a/src/plugin/core/contents/index.ts +++ b/src/plugin/core/contents/index.ts @@ -39,10 +39,15 @@ export class ContentsZip extends ZipFileDriver implements ContentsInterface { return ContentsZip.fromBuffer(buffer); } - public static async fromBuffer(buffer: Buffer) { + public static async fromBuffer( + buffer: Buffer, + options: { skipValidation?: boolean } = {}, + ) { const contentsZip = new ContentsZip(buffer); await contentsZip.cacheEntries(); - await contentsZip.validate(); + if (!options.skipValidation) { + await contentsZip.validate(); + } return contentsZip; } diff --git a/src/plugin/core/plugin/index.ts b/src/plugin/core/plugin/index.ts index 22ae5b3fec2..c4c50130ba7 100644 --- a/src/plugin/core/plugin/index.ts +++ b/src/plugin/core/plugin/index.ts @@ -47,14 +47,16 @@ export class PluginZip extends ZipFileDriver implements PluginInterface { return new PluginZip(buffer); } - public async manifest() { - const contentsZip = await this.contentsZip(); + public async manifest(options: { skipValidation?: boolean } = {}) { + const contentsZip = await this.contentsZip(options); return contentsZip.manifest(); } - private async contentsZip(): Promise { + private async contentsZip( + options: { skipValidation?: boolean } = {}, + ): Promise { const buffer = await this.readFile("contents.zip"); - return ContentsZip.fromBuffer(buffer); + return ContentsZip.fromBuffer(buffer, options); } async getPluginID(): Promise { diff --git a/src/plugin/packer/pack.ts b/src/plugin/packer/pack.ts index c6f07305311..5f8b4b2dd7b 100644 --- a/src/plugin/packer/pack.ts +++ b/src/plugin/packer/pack.ts @@ -11,6 +11,7 @@ type Params = { ppkFilePath: string; output?: string; watch?: boolean; + skipManifestValidation?: boolean; }; // TODO: Reduce statements in this func @@ -44,20 +45,22 @@ export const pack = async (params: Params) => { } // 3. Validate manifest.json - const result = await manifest.validate(new LocalFSDriver(sourceRootDir)); + if (!params.skipManifestValidation) { + const result = await manifest.validate(new LocalFSDriver(sourceRootDir)); - if (result.warnings.length > 0) { - result.warnings.forEach((warning) => { - logger.warn(warning); - }); - } + if (result.warnings.length > 0) { + result.warnings.forEach((warning) => { + logger.warn(warning); + }); + } - if (!result.valid) { - logger.error("Invalid manifest.json:"); - result.errors.forEach((msg) => { - logger.error(`- ${msg}`); - }); - throw new Error("Invalid manifest.json"); + if (!result.valid) { + logger.error("Invalid manifest.json:"); + result.errors.forEach((msg) => { + logger.error(`- ${msg}`); + }); + throw new Error("Invalid manifest.json"); + } } // 4. Prepare output directory diff --git a/src/plugin/upload/index.ts b/src/plugin/upload/index.ts index d1b858c487d..cfaeea509f2 100644 --- a/src/plugin/upload/index.ts +++ b/src/plugin/upload/index.ts @@ -15,6 +15,7 @@ export type Params = { pluginFilePath: string; force?: boolean; watch?: boolean; + skipManifestValidation?: boolean; }; export const upload = async ( @@ -33,7 +34,9 @@ export const upload = async ( const buffer = await fs.readFile(pluginFilePath); const pluginZip = await PluginZip.fromBuffer(buffer); const pluginId = await pluginZip.getPluginID(); - const pluginManifest = await pluginZip.manifest(); + const pluginManifest = await pluginZip.manifest({ + skipValidation: params.skipManifestValidation, + }); // Read plugin info from kintone const { plugins: installedPlugins } = await apiClient.plugin.getPlugins( From 882a4a16d08dc7aa97728673f768420cb946baa1 Mon Sep 17 00:00:00 2001 From: nameless-mc Date: Tue, 26 May 2026 20:27:29 +0900 Subject: [PATCH 2/3] fix(plugin): skip installation summary when manifest validation is skipped --- src/plugin/core/contents/index.ts | 9 ++------- src/plugin/core/plugin/index.ts | 10 ++++------ src/plugin/upload/index.ts | 15 ++++++++------- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/plugin/core/contents/index.ts b/src/plugin/core/contents/index.ts index 9ce57cea320..8e944f59563 100644 --- a/src/plugin/core/contents/index.ts +++ b/src/plugin/core/contents/index.ts @@ -39,15 +39,10 @@ export class ContentsZip extends ZipFileDriver implements ContentsInterface { return ContentsZip.fromBuffer(buffer); } - public static async fromBuffer( - buffer: Buffer, - options: { skipValidation?: boolean } = {}, - ) { + public static async fromBuffer(buffer: Buffer) { const contentsZip = new ContentsZip(buffer); await contentsZip.cacheEntries(); - if (!options.skipValidation) { - await contentsZip.validate(); - } + await contentsZip.validate(); return contentsZip; } diff --git a/src/plugin/core/plugin/index.ts b/src/plugin/core/plugin/index.ts index c4c50130ba7..22ae5b3fec2 100644 --- a/src/plugin/core/plugin/index.ts +++ b/src/plugin/core/plugin/index.ts @@ -47,16 +47,14 @@ export class PluginZip extends ZipFileDriver implements PluginInterface { return new PluginZip(buffer); } - public async manifest(options: { skipValidation?: boolean } = {}) { - const contentsZip = await this.contentsZip(options); + public async manifest() { + const contentsZip = await this.contentsZip(); return contentsZip.manifest(); } - private async contentsZip( - options: { skipValidation?: boolean } = {}, - ): Promise { + private async contentsZip(): Promise { const buffer = await this.readFile("contents.zip"); - return ContentsZip.fromBuffer(buffer, options); + return ContentsZip.fromBuffer(buffer); } async getPluginID(): Promise { diff --git a/src/plugin/upload/index.ts b/src/plugin/upload/index.ts index cfaeea509f2..436c28e9c5f 100644 --- a/src/plugin/upload/index.ts +++ b/src/plugin/upload/index.ts @@ -34,9 +34,6 @@ export const upload = async ( const buffer = await fs.readFile(pluginFilePath); const pluginZip = await PluginZip.fromBuffer(buffer); const pluginId = await pluginZip.getPluginID(); - const pluginManifest = await pluginZip.manifest({ - skipValidation: params.skipManifestValidation, - }); // Read plugin info from kintone const { plugins: installedPlugins } = await apiClient.plugin.getPlugins( @@ -48,10 +45,13 @@ export const upload = async ( const installedPlugin = installedPlugins.find((p) => p.id === pluginId); const isInstalled = installedPlugin !== undefined; - const isSameVersion = installedPlugin?.version === pluginManifest.version; - // Show installation summary - const installationSummary = ` + // Reading the manifest requires a well-formed manifest.json, so we skip the + // summary when validation is intentionally bypassed. + if (!params.skipManifestValidation) { + const pluginManifest = await pluginZip.manifest(); + const isSameVersion = installedPlugin?.version === pluginManifest.version; + const installationSummary = ` Installation Summary: Destination: ${restApiClientOptions.baseUrl} File Path: ${pluginFilePath} @@ -59,7 +59,8 @@ export const upload = async ( Plugin Name: ${pluginManifest.name} Current version: ${installedPlugin?.version ?? "(not installed)"} Target version: ${pluginManifest.version}${isSameVersion ? " (reinstall)" : ""}`; - logger.info(installationSummary); + logger.info(installationSummary); + } // Get confirmation from user if required if (!params.force && !params.watch) { From 1168eeb22ece808b94c59a074c2222b6111eb370 Mon Sep 17 00:00:00 2001 From: nameless-mc Date: Wed, 27 May 2026 18:44:26 +0900 Subject: [PATCH 3/3] fix: fix bugs --- src/plugin/core/contents/index.ts | 9 ++++++--- src/plugin/core/manifest/interface.ts | 5 ++++- src/plugin/core/manifest/v1/index.ts | 7 +++++-- src/plugin/core/manifest/v2/index.ts | 7 +++++-- src/plugin/core/plugin/index.ts | 6 +++++- src/plugin/packer/pack.ts | 1 + 6 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/plugin/core/contents/index.ts b/src/plugin/core/contents/index.ts index 8e944f59563..a94f36ecf5e 100644 --- a/src/plugin/core/contents/index.ts +++ b/src/plugin/core/contents/index.ts @@ -33,16 +33,19 @@ export class ContentsZip extends ZipFileDriver implements ContentsInterface { public static async buildFromManifest( manifest: ManifestInterface, driver: DriverInterface, + skipManifestValidation = false, ): Promise { const buffer = await buildContentsZip(manifest, driver); // const buffer = await _createContentsZipStream(manifest, driver); - return ContentsZip.fromBuffer(buffer); + return ContentsZip.fromBuffer(buffer, skipManifestValidation); } - public static async fromBuffer(buffer: Buffer) { + public static async fromBuffer(buffer: Buffer, skipManifestValidation = false) { const contentsZip = new ContentsZip(buffer); await contentsZip.cacheEntries(); - await contentsZip.validate(); + if (!skipManifestValidation) { + await contentsZip.validate(); + } return contentsZip; } diff --git a/src/plugin/core/manifest/interface.ts b/src/plugin/core/manifest/interface.ts index f6d5441d475..628d2600a9e 100644 --- a/src/plugin/core/manifest/interface.ts +++ b/src/plugin/core/manifest/interface.ts @@ -35,7 +35,10 @@ export interface ManifestInterface { * Generate contents.zip from Manifest and Driver * @param driver */ - generateContentsZip(driver: DriverInterface): Promise; + generateContentsZip( + driver: DriverInterface, + skipManifestValidation?: boolean, + ): Promise; // Accessor get manifestVersion(): 1 | 2; diff --git a/src/plugin/core/manifest/v1/index.ts b/src/plugin/core/manifest/v1/index.ts index ebd699be295..19f0f945904 100644 --- a/src/plugin/core/manifest/v1/index.ts +++ b/src/plugin/core/manifest/v1/index.ts @@ -60,8 +60,11 @@ export class ManifestV1 implements ManifestInterface { return sourceList(this.manifest); } - async generateContentsZip(driver: DriverInterface): Promise { - return ContentsZip.buildFromManifest(this, driver); + async generateContentsZip( + driver: DriverInterface, + skipManifestValidation = false, + ): Promise { + return ContentsZip.buildFromManifest(this, driver, skipManifestValidation); } } diff --git a/src/plugin/core/manifest/v2/index.ts b/src/plugin/core/manifest/v2/index.ts index a6c46fa0168..b8cf70a081b 100644 --- a/src/plugin/core/manifest/v2/index.ts +++ b/src/plugin/core/manifest/v2/index.ts @@ -60,8 +60,11 @@ export class ManifestV2 implements ManifestInterface { return sourceListV2(this.manifest); } - async generateContentsZip(driver: DriverInterface): Promise { - return ContentsZip.buildFromManifest(this, driver); + async generateContentsZip( + driver: DriverInterface, + skipManifestValidation = false, + ): Promise { + return ContentsZip.buildFromManifest(this, driver, skipManifestValidation); } } diff --git a/src/plugin/core/plugin/index.ts b/src/plugin/core/plugin/index.ts index 22ae5b3fec2..92c1ea36a9f 100644 --- a/src/plugin/core/plugin/index.ts +++ b/src/plugin/core/plugin/index.ts @@ -31,8 +31,12 @@ export class PluginZip extends ZipFileDriver implements PluginInterface { manifest: ManifestInterface, privateKey: PrivateKeyInterface, driver: DriverInterface, + skipManifestValidation = false, ): Promise { - const contentsZip = await manifest.generateContentsZip(driver); + const contentsZip = await manifest.generateContentsZip( + driver, + skipManifestValidation, + ); return this.buildFromContentsZip(contentsZip, privateKey); } diff --git a/src/plugin/packer/pack.ts b/src/plugin/packer/pack.ts index 5f8b4b2dd7b..18fb817c012 100644 --- a/src/plugin/packer/pack.ts +++ b/src/plugin/packer/pack.ts @@ -80,6 +80,7 @@ export const pack = async (params: Params) => { manifest, privateKey, new LocalFSDriver(sourceRootDir), + params.skipManifestValidation, ); // 7. Start watch mode if watch option is given