Skip to content

Commit

Permalink
chore: plugin pack command for 2 (#916)
Browse files Browse the repository at this point in the history
Release-As: 1.12.2
  • Loading branch information
tasshi-me authored Nov 6, 2024
1 parent 6da3174 commit 268817c
Show file tree
Hide file tree
Showing 82 changed files with 1,872 additions and 1,307 deletions.
4 changes: 0 additions & 4 deletions license-manager.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,6 @@ const OVERRIDE_LICENSES_TEXT = {
// License text is written in README.md
licenseText: "See https://github.com/rzcoder/node-rsa#license",
},
"stream-buffers": {
licensePageUrl:
"https://raw.githubusercontent.com/samcday/node-stream-buffer/refs/heads/main/UNLICENSE",
},
};

module.exports = config;
6 changes: 0 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,10 @@
"@cybozu/eslint-config": "^24.0.0-beta.0",
"@cybozu/license-manager": "^1.2.1",
"@octokit/rest": "^20.1.1",
"@types/debug": "^4.1.12",
"@types/jest": "^29.5.14",
"@types/node": "^18.19.64",
"@types/node-rsa": "^1.1.4",
"@types/rollup-plugin-auto-external": "^2.0.5",
"@types/stream-buffers": "^3.0.7",
"@types/yargs": "^17.0.33",
"@types/yauzl": "^2.10.3",
"@types/yazl": "^2.4.5",
Expand Down Expand Up @@ -112,13 +110,9 @@
"chokidar": "^4.0.1",
"csv-parse": "^4.16.3",
"csv-stringify": "5.6.5",
"debug": "^4.3.7",
"execa": "^9.4.1",
"https-proxy-agent": "^7.0.5",
"iconv-lite": "^0.6.3",
"mkdirp": "^3.0.1",
"node-rsa": "^1.1.1",
"stream-buffers": "^3.0.3",
"yargs": "^17.7.2",
"yauzl": "^3.1.3",
"yazl": "^3.1.0"
Expand Down
268 changes: 67 additions & 201 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

25 changes: 15 additions & 10 deletions src/cli/plugin/index.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
import type { CommandModule } from "yargs";
import type yargs from "yargs";
import { packCommand } from "./pack";
import { emitExperimentalWarning } from "../../utils/stability";
import { infoCommand } from "./info";
import { setStability } from "../stability";

const command = "plugin";

const describe = "[Experimental] Commands for kintone plugin";
const describe = "Commands for kintone plugin";

const builder = (args: yargs.Argv) => args.command(packCommand).demandCommand();
const builder = (args: yargs.Argv) =>
args.command(infoCommand).command(packCommand).demandCommand();

const handler = () => {
emitExperimentalWarning("This feature is under early development");
/** noop **/
};

export const pluginCommand: CommandModule = {
command,
describe,
builder,
handler,
};
export const pluginCommand: CommandModule = setStability(
{
command,
describe,
builder,
handler,
},
"experimental",
"This feature is under early development",
);
53 changes: 53 additions & 0 deletions src/cli/plugin/info.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import type yargs from "yargs";
import type { CommandModule } from "yargs";
import type { OutputFormat } from "../../plugin/info/";
import { run } from "../../plugin/info/";
import { logger } from "../../utils/log";
import { RunError } from "../../record/error";
import { setStability } from "../stability";

const command = "info";

const describe = "Show information from plugin file";

const outputFormats: OutputFormat[] = ["plain", "json"];

const builder = (args: yargs.Argv) =>
args
.option("input", {
describe: "The input plugin zip",
type: "string",
demandOption: true,
requiresArg: true,
})
.option("format", {
describe: "Format",
default: "plain" satisfies OutputFormat as OutputFormat,
choices: outputFormats,
requiresArg: true,
});

type Args = yargs.Arguments<
ReturnType<typeof builder> extends yargs.Argv<infer U> ? U : never
>;

const handler = async (args: Args) => {
try {
await run(args.input, args.format);
} catch (error) {
logger.error(new RunError(error));
// eslint-disable-next-line n/no-process-exit
process.exit(1);
}
};

export const infoCommand: CommandModule<{}, Args> = setStability(
{
command,
describe,
builder,
handler,
},
"experimental",
"This feature is under early development",
);
49 changes: 30 additions & 19 deletions src/cli/plugin/pack.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import type yargs from "yargs";
import type { CommandModule } from "yargs";
import cli from "../../plugin/packer/cli";
import { emitExperimentalWarning } from "../../utils/stability";
import { run } from "../../plugin/packer/";
import { logger } from "../../utils/log";
import { RunError } from "../../record/error";
import { setStability } from "../stability";

const command = "pack";

const describe = "[Experimental] Packaging plugin project to a zip file";
const describe = "Packaging plugin project to a zip file";

const builder = (args: yargs.Argv) =>
args
Expand Down Expand Up @@ -38,22 +40,31 @@ type Args = yargs.Arguments<
>;

const handler = async (args: Args) => {
emitExperimentalWarning("This feature is under early development");
const flags = {
ppk: args["private-key"],
out: args.output,
watch: args.watch,
};
if (process.env.NODE_ENV === "test") {
console.log(JSON.stringify({ pluginDir: args.input, flags: flags }));
} else {
await cli(args.input, flags);
try {
const flags = {
ppk: args["private-key"],
output: args.output,
watch: args.watch,
};
if (process.env.NODE_ENV === "test") {
console.log(JSON.stringify({ pluginDir: args.input, flags: flags }));
} else {
await run(args.input, flags);
}
} catch (error) {
logger.error(new RunError(error));
// eslint-disable-next-line n/no-process-exit
process.exit(1);
}
};

export const packCommand: CommandModule<{}, Args> = {
command,
describe,
builder,
handler,
};
export const packCommand: CommandModule<{}, Args> = setStability(
{
command,
describe,
builder,
handler,
},
"experimental",
"This feature is under early development",
);
69 changes: 69 additions & 0 deletions src/cli/stability.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import type { ArgumentsCamelCase, CommandModule } from "yargs";
import {
emitDeprecationWarning,
emitExperimentalWarning,
} from "../utils/stability";

/**
* Set stability index to a command.
* - Show stability on help message
* - Emit warning on execution
* @param cmd Command module
* @param stability "experimental" or "deprecated"
* @param message additional information
*/
export const setStability = <T = {}, U = {}>(
cmd: CommandModule<T, U>,
stability: "experimental" | "deprecated",
message: string,
): CommandModule<T, U> => {
const { describe, handler, ...restCmd } = cmd;
const newDescribe = buildDescriptionWithStability(
describe,
message,
stability,
);

const newHandler = async (args: ArgumentsCamelCase<U>) => {
switch (stability) {
case "experimental":
emitExperimentalWarning(message);
break;
case "deprecated":
emitDeprecationWarning(message);
break;
}
await handler(args);
};

return {
...restCmd,
describe: newDescribe,
handler: newHandler,
};
};

const buildDescriptionWithStability = (
description: string | false | undefined,
message: string,
stability: "experimental" | "deprecated",
): string => {
const labels = {
experimental: "Experimental",
deprecated: "Deprecated",
};
const label = labels[stability];
const msgLines = message.split("\n");

let output = "";
if (description) {
output += description + "\n\n";
}

output += `[${label}: ${msgLines[0]}]`;
if (msgLines.length > 1) {
output += `${"\n" + msgLines.slice(1)}`;
}

return output;
};
Binary file not shown.
Empty file.
Empty file.
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"$schema": "https://example.com/plugin-manifest-schema.json",
"manifest_version": 2,
"version": 1,
"type": "APP",
"name": {
"en": "sample extension"
},
"description": {
"en": "This is sample extension."
},
"icon": "image/icon.png",
"components": [
{
"type": "APP_INDEX_HEADER_SPACE",
"js": ["js/customize.js", "https://example.com/js/customize.js"],
"css": ["https://example.com/css/customize.css", "css/customize.css"],
"html": "html/customize.html"
}
],
"config": {
"html": "html/config.html",
"js": ["https://example.com/js/config.js", "js/config.js"],
"css": ["css/config.css", "https://example.com/css/config.css"],
"required_params": ["Param1", "Param2"]
},
"allowed_hosts": ["https://example.com"],
"permissions": {
"js_api": [
"rest_api:execute",
"kintone.app.getId",
"kintone.plugin.app.getConfig"
],
"rest_api": ["app_record:read", "/k/v1/record.json:put"]
}
}
71 changes: 71 additions & 0 deletions src/plugin/core/contents/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import path from "path";
import { ManifestFactory } from "../../manifest";
import { ContentsZip } from "../index";
import { LocalFSDriver } from "../../driver";
import fs from "fs";

const fixturesDir = path.join(__dirname, "fixtures");

describe("ContentsZip", () => {
describe("should be able to create ContentsZip from a plugin directory", () => {
it("manifest v1", async () => {
const pluginDir = path.join(fixturesDir, "plugin-manifest-v1");

const manifestJSONPath = path.join(pluginDir, "manifest.json");
const manifest = await ManifestFactory.loadJsonFile(manifestJSONPath);

const contentsZip = await ContentsZip.buildFromManifest(
manifest,
new LocalFSDriver(pluginDir),
);
const files = await contentsZip.fileList();
expect(files).toStrictEqual(["manifest.json", "image/icon.png"]);
expect(contentsZip).toBeInstanceOf(ContentsZip);
expect(contentsZip.buffer).toBeInstanceOf(Buffer);
});

it("manifest v2", async () => {
const pluginDir = path.join(fixturesDir, "plugin-manifest-v2");

const manifestJSONPath = path.join(pluginDir, "manifest.json");
const manifest = await ManifestFactory.loadJsonFile(manifestJSONPath);

const contentsZip = await ContentsZip.buildFromManifest(
manifest,
new LocalFSDriver(pluginDir),
);

const expectedFiles = [
"manifest.json",
"image/icon.png",
"js/customize.js",
"css/customize.css",
"html/customize.html",
"html/config.html",
"js/config.js",
"css/config.css",
];
const files = await contentsZip.fileList();
expect(files).toStrictEqual(expectedFiles);
expect(contentsZip).toBeInstanceOf(ContentsZip);
expect(contentsZip.buffer).toBeInstanceOf(Buffer);
});
});

describe("invalid contents.zip", () => {
const invalidMaxFileSizeContentsZipPath = path.join(
__dirname,
"fixtures",
"invalid-maxFileSize",
"invalid-maxFileSize-contents.zip",
);

// TODO: This test must be in contents-zip module
it("throws an error if the contents.zip is invalid", async () => {
const buffer = fs.readFileSync(invalidMaxFileSizeContentsZipPath);
await expect(ContentsZip.fromBuffer(buffer)).rejects.toThrow(
'"/icon" file size should be <= 20MB',
);
});
});
});
Loading

0 comments on commit 268817c

Please sign in to comment.