quip-cli
Version:
A Command Line Interface for the Quip Live Apps platform
158 lines (157 loc) • 6.11 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.doPublish = exports.createBundle = void 0;
const tslib_1 = require("tslib");
const command_1 = require("@oclif/command");
const chalk_1 = tslib_1.__importDefault(require("chalk"));
const form_data_1 = tslib_1.__importDefault(require("form-data"));
const fs_1 = tslib_1.__importDefault(require("fs"));
const minimatch_1 = tslib_1.__importDefault(require("minimatch"));
const path_1 = tslib_1.__importDefault(require("path"));
const crypto_1 = tslib_1.__importDefault(require("crypto"));
const cli_api_1 = tslib_1.__importStar(require("../lib/cli-api"));
const config_1 = require("../lib/config");
const manifest_1 = require("../lib/manifest");
const print_1 = require("../lib/print");
const types_1 = require("../lib/types");
const util_1 = require("../lib/util");
exports.createBundle = async (manifest, manifestPath, ignore) => {
const root = path_1.default.dirname(manifestPath);
const allFiles = new Set(await util_1.readRecursive(root, ignore));
const missing = new Map();
const bundle = [];
const addToFiles = (matcher, source) => {
if (!matcher) {
return;
}
let found = false;
for (let file of allFiles) {
if (minimatch_1.default(file, matcher)) {
bundle.push(file);
found = true;
}
}
if (!found) {
missing.set(source, missing.get(source) || new Set());
missing.get(source).add(matcher);
}
};
const addAll = (files, source) => {
if (!files) {
return;
}
files.forEach(matcher => {
if (types_1.isMigration(matcher)) {
addToFiles(matcher.js_file, source);
}
else {
addToFiles(matcher, source);
}
});
};
addToFiles("manifest.json", "manifest file");
addToFiles(manifest.thumbnail, "thumbnail");
addToFiles(manifest.toolbar_icon, "toolbar_icon");
addAll(manifest.js_files, "js_files");
addAll(manifest.css_files, "css_file");
addAll(manifest.other_resources, "other_resources");
addAll(manifest.migrations, "migrations");
return { root, bundle, missing };
};
exports.doPublish = async (manifest, manifestPath, ignore, config, site, printJson) => {
let gitsha = null;
try {
gitsha = await util_1.runCmdPromise(path_1.default.dirname(manifestPath), "git", "rev-parse", "HEAD");
}
catch (e) {
/* swallow this error, this is just a best effort. */
}
const form = new form_data_1.default();
const { root, bundle, missing } = await exports.createBundle(manifest, manifestPath, ignore);
if (missing.size > 0) {
print_1.println(chalk_1.default `{red WARNING: the following files were defined in your manifest, but were not found.}
{red This bundle may be incomplete, you should include these files or remove them from your manifest.}`);
for (let [source, files] of missing) {
print_1.println(chalk_1.default `{red === ${source} ===}`);
files.forEach(f => print_1.println(chalk_1.default `{red ${f}}`));
}
}
const files = await Promise.all(bundle.map(async (name) => {
const fileBuffer = await fs_1.default.promises.readFile(path_1.default.join(root, name));
return [
name,
fileBuffer,
crypto_1.default
.createHash("md5")
.update(fileBuffer)
.digest("hex"),
];
}));
const bundlemd5 = crypto_1.default.createHash("md5");
// Sort by md5 alphabetically so the md5 of these md5s is stable
files
.sort(([_na, _fa, a], [_nb, _fb, b]) => (a === b ? 0 : a < b ? -1 : 1))
.forEach(([name, data, md5]) => {
form.append("bundle", data, {
filepath: name,
});
bundlemd5.update(md5);
});
form.append("md5", bundlemd5.digest("hex"));
if (gitsha) {
form.append("gitsha", gitsha);
}
const fetch = await cli_api_1.default(config, site);
const response = await cli_api_1.successOnly(fetch(`app/${manifest.id}`, "post", form), printJson);
if (!response) {
return null;
}
return response;
};
class Publish extends command_1.Command {
async run() {
const { args, flags } = this.parse(Publish);
const manifestPath = await manifest_1.findManifest(process.cwd());
if (!manifestPath) {
throw new Error(`Could not find a manifest.json file.`);
}
const manifest = await manifest_1.getManifest(manifestPath);
const success = await exports.doPublish(manifest, manifestPath, flags.ignore, flags.config, flags.site, flags.json);
if (success) {
if (!flags.json) {
print_1.println(chalk_1.default `{magenta Successfully published ${manifest.name} v${manifest.version_name} (${manifest.version_number})}`);
}
}
else {
if (!flags.json) {
print_1.println(chalk_1.default `{red Publishing failed.}`);
}
process.exit(1);
}
}
}
exports.default = Publish;
Publish.description = "Uploads this bundle to the developer console, and sets it as the latest development version.";
Publish.flags = {
help: command_1.flags.help({ char: "h" }),
json: command_1.flags.boolean({
char: "j",
description: "output responses in JSON",
}),
ignore: command_1.flags.string({
char: "i",
description: "blob to ignore. Defaults to 'node_modules'",
default: "node_modules",
}),
site: command_1.flags.string({
char: "s",
description: "use a specific quip site rather than the standard quip.com login",
default: config_1.DEFAULT_SITE,
}),
config: command_1.flags.string({
hidden: true,
description: "Use a custom config file (default ~/.quiprc)",
default: () => config_1.defaultConfigPath(),
}),
};
Publish.args = [];