kintone のプラグイン管理画面の構成が変わって、plugin-uploader でエラーが発生したので、とりあえず暫定対策したメモ書き
※plugin-uploader で、本対策済みのようです。
概要
※plugin-uploader のバージョンアップで、本対策されます。
npm install @kintone/plugin-uploader@latest
or
npm install -g @kintone/plugin-uploader@latest
以下はメモ書きで、古い情報です。
2024/05/12 の kintone アップデートで、プラグイン管理画面の構成が変わって、plugin-uploader でエラーが発生。
※「エラーが発生しました kintone管理者権限のあるユーザーで実行してください」
クラス名等がかわっているので、とりあえず暫定対応した。
kintone 画面刷新の一環で、プラグイン管理画面が変わったと思われます。
プラグイン管理画面のクラス名は、自動生成されたものだと思われるので、今後のアップデートのタイミングで変更される可能性がある。
本対策としては、プラグインアップロードの REST API 対応が必要ですね。
error.log
field-format [master ≡] > npm run upload-prod
> field-format@0.1.0 upload-prod
> kintone-plugin-uploader dist/field-format-plugin8.zip --watch --waiting-dialog-ms 3000
Open https://xxxxxxx.cybozu.com/login?saml=off
Trying to log in...
Navigate to https://xxxxxxx.cybozu.com/k/admin/system/plugin/
エラーが発生しました kintone管理者権限のあるユーザーで実行してください
plugin-uploader 暫定対応
plugin-uploader がインストールされているパスを探して、エラー部分を修正
C:\Program Files (x86)\Nodist\bin\node_modules@kintone\plugin-uploader\dist\index.js
.index.js
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.run = void 0;
const chalk_1 = __importDefault(require("chalk"));
const fs_1 = __importDefault(require("fs"));
const puppeteer_1 = __importDefault(require("puppeteer"));
const messages_1 = require("./messages");
const TIMEOUT_MS = 10000;
const UPLOAD_TIMEOUT_MS = 60000;
const launchBrowser = (proxy, ignoreDefaultArgs) => {
const args = proxy ? [`--proxy-server=${proxy}`] : [];
return puppeteer_1.default.launch({ args, ignoreDefaultArgs, headless: "new" });
};
const readyForUpload = (browser, baseUrl, userName, password, lang, basicAuth) => __awaiter(void 0, void 0, void 0, function* () {
const m = (0, messages_1.getBoundMessage)(lang);
const page = yield browser.newPage();
const loginUrl = `${baseUrl}/login?saml=off`;
if (basicAuth) {
yield page.authenticate(basicAuth);
}
console.log(`Open ${loginUrl}`);
yield page.goto(loginUrl);
try {
yield page.waitForSelector(".form-username-slash", { timeout: TIMEOUT_MS });
}
catch (e) {
throw chalk_1.default.red(m("Error_cannotOpenLogin"));
}
console.log("Trying to log in...");
yield page.type(".form-username-slash > input.form-text", userName);
yield page.type(".form-password-slash > input.form-text", password);
yield page.click(".login-button");
try {
yield page.waitForNavigation({
timeout: TIMEOUT_MS,
waitUntil: "domcontentloaded",
});
}
catch (e) {
throw chalk_1.default.red(m("Error_failedLogin"));
}
const pluginUrl = `${baseUrl}/k/admin/system/plugin/`;
console.log(`Navigate to ${pluginUrl}`);
yield page.goto(pluginUrl);
try {
yield page.waitForSelector("#root button.sc-bZHSRq.fHDQvG", {
timeout: TIMEOUT_MS,
});
}
catch (e) {
throw chalk_1.default.red(m("Error_adminPrivilege"));
}
return page;
});
const upload = (page, pluginPath, lang) => __awaiter(void 0, void 0, void 0, function* () {
const m = (0, messages_1.getBoundMessage)(lang);
console.log(`Trying to upload ${pluginPath}`);
yield page.evaluate(() => {
const button1 = document.querySelector('#root button.sc-bZHSRq.fHDQvG');
if (button1) {
button1.click();
}
else {
throw new Error('button.sc-bZHSRq.fHDQvG is not found');
}
});
const file = yield page.$('input[type="file"]');
if (file == null) {
throw new Error('input[type="file"] is not found');
}
yield file.uploadFile(pluginPath);
// HACK: `page.click` does not work as expected, so we use `page.evaluate` instead.
// ref: https://github.com/puppeteer/puppeteer/pull/7097#issuecomment-850348366
yield page.evaluate(() => {
const button = document.querySelector('button.sc-ifyrAs.RhNPH');
if (button) {
button.click();
}
else {
throw new Error('button.sc-ifyrAs.RhNPH is not found');
}
});
yield page.waitForSelector(".sc-hABBmJ.fnsoHW", {
hidden: true,
timeout: UPLOAD_TIMEOUT_MS,
});
console.log(`${pluginPath} ${m("Uploaded")}`);
});
const run = (baseUrl, userName, password, pluginPath, options) => __awaiter(void 0, void 0, void 0, function* () {
let browser = yield launchBrowser(options.proxyServer, options.puppeteerIgnoreDefaultArgs);
let page;
const { lang, basicAuth } = options;
const m = (0, messages_1.getBoundMessage)(lang);
try {
page = yield readyForUpload(browser, baseUrl, userName, password, lang, basicAuth);
yield upload(page, pluginPath, lang);
if (options.watch) {
let uploading = false;
fs_1.default.watch(pluginPath, () => __awaiter(void 0, void 0, void 0, function* () {
if (uploading) {
return;
}
try {
uploading = true;
yield upload(page, pluginPath, lang);
}
catch (e) {
console.log(e);
console.log(m("Error_retry"));
yield browser.close();
browser = yield launchBrowser(options.proxyServer);
page = yield readyForUpload(browser, baseUrl, userName, password, lang, basicAuth);
yield upload(page, pluginPath, lang);
}
finally {
uploading = false;
}
}));
}
else {
yield browser.close();
}
}
catch (e) {
console.error(m("Error"), e);
yield browser.close();
}
});
exports.run = run;
//# sourceMappingURL=index.js.map