mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-08 13:06:15 +02:00
Plugins: Updated plugin repo script
This commit is contained in:
parent
0d2bf6d787
commit
52da072f9a
@ -6,4 +6,5 @@ packages/app-desktop
|
||||
packages/app-cli
|
||||
packages/app-mobile
|
||||
packages/app-clipper
|
||||
packages/generator-joplin
|
||||
packages/generator-joplin
|
||||
packages/plugin-repo-cli
|
@ -1371,6 +1371,12 @@ packages/lib/uuid.js.map
|
||||
packages/lib/versionInfo.d.ts
|
||||
packages/lib/versionInfo.js
|
||||
packages/lib/versionInfo.js.map
|
||||
packages/plugin-repo-cli/dummy.test.d.ts
|
||||
packages/plugin-repo-cli/dummy.test.js
|
||||
packages/plugin-repo-cli/dummy.test.js.map
|
||||
packages/plugin-repo-cli/index.d.ts
|
||||
packages/plugin-repo-cli/index.js
|
||||
packages/plugin-repo-cli/index.js.map
|
||||
packages/plugins/ToggleSidebars/api/index.d.ts
|
||||
packages/plugins/ToggleSidebars/api/index.js
|
||||
packages/plugins/ToggleSidebars/api/index.js.map
|
||||
@ -1461,21 +1467,6 @@ packages/renderer/utils.js.map
|
||||
packages/server/src/app.d.ts
|
||||
packages/server/src/app.js
|
||||
packages/server/src/app.js.map
|
||||
packages/server/src/config-base.d.ts
|
||||
packages/server/src/config-base.js
|
||||
packages/server/src/config-base.js.map
|
||||
packages/server/src/config-buildTypes.d.ts
|
||||
packages/server/src/config-buildTypes.js
|
||||
packages/server/src/config-buildTypes.js.map
|
||||
packages/server/src/config-dev.d.ts
|
||||
packages/server/src/config-dev.js
|
||||
packages/server/src/config-dev.js.map
|
||||
packages/server/src/config-prod.d.ts
|
||||
packages/server/src/config-prod.js
|
||||
packages/server/src/config-prod.js.map
|
||||
packages/server/src/config-tests.d.ts
|
||||
packages/server/src/config-tests.js
|
||||
packages/server/src/config-tests.js.map
|
||||
packages/server/src/config.d.ts
|
||||
packages/server/src/config.js
|
||||
packages/server/src/config.js.map
|
||||
@ -1686,10 +1677,13 @@ packages/server/src/utils/urlUtils.js.map
|
||||
packages/server/src/utils/uuidgen.d.ts
|
||||
packages/server/src/utils/uuidgen.js
|
||||
packages/server/src/utils/uuidgen.js.map
|
||||
packages/tools/build-plugin-repository.d.ts
|
||||
packages/tools/build-plugin-repository.js
|
||||
packages/tools/build-plugin-repository.js.map
|
||||
packages/tools/lerna-add.d.ts
|
||||
packages/tools/lerna-add.js
|
||||
packages/tools/lerna-add.js.map
|
||||
packages/tools/release-server.d.ts
|
||||
packages/tools/release-server.js
|
||||
packages/tools/release-server.js.map
|
||||
packages/tools/tool-utils.d.ts
|
||||
packages/tools/tool-utils.js
|
||||
packages/tools/tool-utils.js.map
|
||||
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
|
||||
|
30
.gitignore
vendored
30
.gitignore
vendored
@ -1359,6 +1359,12 @@ packages/lib/uuid.js.map
|
||||
packages/lib/versionInfo.d.ts
|
||||
packages/lib/versionInfo.js
|
||||
packages/lib/versionInfo.js.map
|
||||
packages/plugin-repo-cli/dummy.test.d.ts
|
||||
packages/plugin-repo-cli/dummy.test.js
|
||||
packages/plugin-repo-cli/dummy.test.js.map
|
||||
packages/plugin-repo-cli/index.d.ts
|
||||
packages/plugin-repo-cli/index.js
|
||||
packages/plugin-repo-cli/index.js.map
|
||||
packages/plugins/ToggleSidebars/api/index.d.ts
|
||||
packages/plugins/ToggleSidebars/api/index.js
|
||||
packages/plugins/ToggleSidebars/api/index.js.map
|
||||
@ -1449,21 +1455,6 @@ packages/renderer/utils.js.map
|
||||
packages/server/src/app.d.ts
|
||||
packages/server/src/app.js
|
||||
packages/server/src/app.js.map
|
||||
packages/server/src/config-base.d.ts
|
||||
packages/server/src/config-base.js
|
||||
packages/server/src/config-base.js.map
|
||||
packages/server/src/config-buildTypes.d.ts
|
||||
packages/server/src/config-buildTypes.js
|
||||
packages/server/src/config-buildTypes.js.map
|
||||
packages/server/src/config-dev.d.ts
|
||||
packages/server/src/config-dev.js
|
||||
packages/server/src/config-dev.js.map
|
||||
packages/server/src/config-prod.d.ts
|
||||
packages/server/src/config-prod.js
|
||||
packages/server/src/config-prod.js.map
|
||||
packages/server/src/config-tests.d.ts
|
||||
packages/server/src/config-tests.js
|
||||
packages/server/src/config-tests.js.map
|
||||
packages/server/src/config.d.ts
|
||||
packages/server/src/config.js
|
||||
packages/server/src/config.js.map
|
||||
@ -1674,10 +1665,13 @@ packages/server/src/utils/urlUtils.js.map
|
||||
packages/server/src/utils/uuidgen.d.ts
|
||||
packages/server/src/utils/uuidgen.js
|
||||
packages/server/src/utils/uuidgen.js.map
|
||||
packages/tools/build-plugin-repository.d.ts
|
||||
packages/tools/build-plugin-repository.js
|
||||
packages/tools/build-plugin-repository.js.map
|
||||
packages/tools/lerna-add.d.ts
|
||||
packages/tools/lerna-add.js
|
||||
packages/tools/lerna-add.js.map
|
||||
packages/tools/release-server.d.ts
|
||||
packages/tools/release-server.js
|
||||
packages/tools/release-server.js.map
|
||||
packages/tools/tool-utils.d.ts
|
||||
packages/tools/tool-utils.js
|
||||
packages/tools/tool-utils.js.map
|
||||
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
|
||||
|
@ -14,7 +14,6 @@
|
||||
"buildApiDoc": "npm start --prefix=packages/app-cli -- apidoc ../../readme/api/references/rest_api.md",
|
||||
"buildDoc": "./packages/tools/build-all.sh",
|
||||
"buildPluginDoc": "typedoc --name 'Joplin Plugin API Documentation' --mode file -theme './Assets/PluginDocTheme/' --readme './Assets/PluginDocTheme/index.md' --excludeNotExported --excludeExternals --excludePrivate --excludeProtected --out docs/api/references/plugin_api packages/lib/services/plugins/api/",
|
||||
"buildPluginRepo": "node packages/tools/build-plugin-repository.js",
|
||||
"buildTranslations": "npm run tsc && node packages/tools/build-translation.js",
|
||||
"buildTranslationsNoTsc": "node packages/tools/build-translation.js",
|
||||
"buildWebsite": "npm run buildApiDoc && node ./packages/tools/build-website.js && npm run buildPluginDoc",
|
||||
|
@ -52,7 +52,7 @@
|
||||
"coveralls": "^3.0.1",
|
||||
"eslint": "^6.0.0",
|
||||
"eslint-config-prettier": "^6.0.0",
|
||||
"jest": "^24.8.0",
|
||||
"jest": "^26.6.3",
|
||||
"prettier": "^1.18.2",
|
||||
"ts-jest": "^24.0.2",
|
||||
"typescript": "^3.5.3"
|
||||
|
9
packages/plugin-repo-cli/dummy.test.ts
Normal file
9
packages/plugin-repo-cli/dummy.test.ts
Normal file
@ -0,0 +1,9 @@
|
||||
// Dummy test because the Jest setup is done but there's for now no test.
|
||||
|
||||
describe('dummy', () => {
|
||||
|
||||
it('should pass', () => {
|
||||
expect(1).toBe(1);
|
||||
});
|
||||
|
||||
});
|
@ -3,7 +3,7 @@ import * as path from 'path';
|
||||
import * as process from 'process';
|
||||
import validatePluginId from '@joplin/lib/services/plugins/utils/validatePluginId';
|
||||
import markdownUtils, { MarkdownTableHeader, MarkdownTableRow } from '@joplin/lib/markdownUtils';
|
||||
const { execCommand, execCommandVerbose, rootDir, resolveRelativePathWithinDir, gitPullTry } = require('./tool-utils.js');
|
||||
import { execCommand2, resolveRelativePathWithinDir, gitPullTry, gitRepoCleanTry, gitRepoClean } from '@joplin/tools/tool-utils.js';
|
||||
|
||||
interface NpmPackage {
|
||||
name: string;
|
||||
@ -45,6 +45,7 @@ async function checkPluginRepository(dirPath: string) {
|
||||
|
||||
const previousDir = process.cwd();
|
||||
process.chdir(dirPath);
|
||||
await gitRepoCleanTry();
|
||||
await gitPullTry();
|
||||
process.chdir(previousDir);
|
||||
}
|
||||
@ -70,13 +71,13 @@ async function extractPluginFilesFromPackage(existingManifests: any, workDir: st
|
||||
const previousDir = process.cwd();
|
||||
process.chdir(workDir);
|
||||
|
||||
await execCommandVerbose('npm', ['install', packageName, '--save', '--ignore-scripts']);
|
||||
await execCommand2(`npm install ${packageName} --save --ignore-scripts`, { showOutput: false });
|
||||
|
||||
const pluginDir = resolveRelativePathWithinDir(workDir, 'node_modules', packageName, 'publish');
|
||||
|
||||
const files = await fs.readdir(pluginDir);
|
||||
const manifestFilePath = path.resolve(pluginDir, files.find(f => path.extname(f) === '.json'));
|
||||
const pluginFilePath = path.resolve(pluginDir, files.find(f => path.extname(f) === '.jpl'));
|
||||
const manifestFilePath = path.resolve(pluginDir, files.find((f: any) => path.extname(f) === '.json'));
|
||||
const pluginFilePath = path.resolve(pluginDir, files.find((f: any) => path.extname(f) === '.jpl'));
|
||||
|
||||
if (!(await fs.pathExists(manifestFilePath))) throw new Error(`Could not find manifest file at ${manifestFilePath}`);
|
||||
if (!(await fs.pathExists(pluginFilePath))) throw new Error(`Could not find plugin file at ${pluginFilePath}`);
|
||||
@ -155,23 +156,41 @@ async function updateReadme(readmePath: string, manifests: any) {
|
||||
|
||||
const tableRegex = /<!-- PLUGIN_LIST -->([^]*)<!-- PLUGIN_LIST -->/;
|
||||
|
||||
const content = await fs.readFile(readmePath, 'utf8');
|
||||
const content = await fs.pathExists(readmePath) ? await fs.readFile(readmePath, 'utf8') : '<!-- PLUGIN_LIST -->\n<!-- PLUGIN_LIST -->';
|
||||
const newContent = content.replace(tableRegex, `<!-- PLUGIN_LIST -->\n${mdTable}\n<!-- PLUGIN_LIST -->`);
|
||||
|
||||
await fs.writeFile(readmePath, newContent, 'utf8');
|
||||
}
|
||||
|
||||
async function main() {
|
||||
// We assume that the repository is located in a directory next to the main
|
||||
// Joplin monorepo.
|
||||
const repoDir = path.resolve(path.dirname(rootDir), 'joplin-plugins');
|
||||
interface CommandBuildArgs {
|
||||
pluginRepoDir: string;
|
||||
}
|
||||
|
||||
enum ProcessingActionType {
|
||||
Add = 1,
|
||||
Update = 2,
|
||||
}
|
||||
|
||||
function commitMessage(actionType: ProcessingActionType, npmPackage: NpmPackage): string {
|
||||
const output: string[] = [];
|
||||
|
||||
if (actionType === ProcessingActionType.Add) {
|
||||
output.push('New');
|
||||
} else {
|
||||
output.push('Update');
|
||||
}
|
||||
|
||||
output.push(`${npmPackage.name}@${npmPackage.version}`);
|
||||
|
||||
return output.join(': ');
|
||||
}
|
||||
|
||||
async function processNpmPackage(npmPackage: NpmPackage, repoDir: string) {
|
||||
const tempDir = `${repoDir}/temp`;
|
||||
const pluginManifestsPath = path.resolve(repoDir, 'manifests.json');
|
||||
const obsoleteManifestsPath = path.resolve(repoDir, 'obsoletes.json');
|
||||
const errorsPath = path.resolve(repoDir, 'errors.json');
|
||||
|
||||
await checkPluginRepository(repoDir);
|
||||
|
||||
await fs.mkdirp(tempDir);
|
||||
|
||||
const originalPluginManifests = await readJsonFile(pluginManifestsPath, {});
|
||||
@ -181,29 +200,31 @@ async function main() {
|
||||
...obsoleteManifests,
|
||||
};
|
||||
|
||||
const searchResults = (await execCommand('npm search joplin-plugin --searchlimit 5000 --json')).trim();
|
||||
const npmPackages = pluginInfoFromSearchResults(JSON.parse(searchResults));
|
||||
|
||||
const packageTempDir = `${tempDir}/packages`;
|
||||
|
||||
await fs.mkdirp(packageTempDir);
|
||||
const previousDir = process.cwd();
|
||||
process.chdir(packageTempDir);
|
||||
await execCommand('npm init --yes --loglevel silent');
|
||||
await execCommand2('npm init --yes --loglevel silent', { quiet: true });
|
||||
|
||||
const errors: any[] = [];
|
||||
const errors: any = await readJsonFile(errorsPath, {});
|
||||
delete errors[npmPackage.name];
|
||||
|
||||
let actionType: ProcessingActionType = ProcessingActionType.Update;
|
||||
let manifests: any = {};
|
||||
|
||||
for (const npmPackage of npmPackages) {
|
||||
try {
|
||||
const packageName = npmPackage.name;
|
||||
const destDir = `${repoDir}/plugins/`;
|
||||
const manifest = await extractPluginFilesFromPackage(existingManifests, packageTempDir, packageName, destDir);
|
||||
if (!obsoleteManifests[manifest.id]) manifests[manifest.id] = manifest;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
errors.push(error);
|
||||
try {
|
||||
const destDir = `${repoDir}/plugins/`;
|
||||
const manifest = await extractPluginFilesFromPackage(existingManifests, packageTempDir, npmPackage.name, destDir);
|
||||
|
||||
if (!existingManifests[manifest.id]) {
|
||||
actionType = ProcessingActionType.Add;
|
||||
}
|
||||
|
||||
if (!obsoleteManifests[manifest.id]) manifests[manifest.id] = manifest;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
errors[npmPackage.name] = error.message || '';
|
||||
}
|
||||
|
||||
// We preserve the original manifests so that if a plugin has been removed
|
||||
@ -217,20 +238,79 @@ async function main() {
|
||||
|
||||
await fs.writeFile(pluginManifestsPath, JSON.stringify(manifests, null, '\t'), 'utf8');
|
||||
|
||||
if (errors.length) {
|
||||
const toWrite = errors.map((e: any) => {
|
||||
return {
|
||||
message: e.message || '',
|
||||
};
|
||||
});
|
||||
await fs.writeFile(errorsPath, JSON.stringify(toWrite, null, '\t'), 'utf8');
|
||||
if (Object.keys(errors).length) {
|
||||
await fs.writeFile(errorsPath, JSON.stringify(errors, null, '\t'), 'utf8');
|
||||
} else {
|
||||
await fs.remove(errorsPath);
|
||||
}
|
||||
|
||||
await updateReadme(`${repoDir}/README.md`, manifests);
|
||||
|
||||
process.chdir(previousDir);
|
||||
await fs.remove(tempDir);
|
||||
|
||||
process.chdir(repoDir);
|
||||
|
||||
if (!(await gitRepoClean())) {
|
||||
await execCommand2('git add -A', { showOutput: false });
|
||||
await execCommand2(['git', 'commit', '-m', commitMessage(actionType, npmPackage)], { showOutput: false });
|
||||
} else {
|
||||
console.info('Nothing to commit');
|
||||
}
|
||||
}
|
||||
|
||||
async function commandBuild(args: CommandBuildArgs) {
|
||||
const repoDir = args.pluginRepoDir;
|
||||
await checkPluginRepository(repoDir);
|
||||
|
||||
const searchResults = (await execCommand2('npm search joplin-plugin --searchlimit 5000 --json', { showOutput: false })).trim();
|
||||
const npmPackages = pluginInfoFromSearchResults(JSON.parse(searchResults));
|
||||
|
||||
for (const npmPackage of npmPackages) {
|
||||
await processNpmPackage(npmPackage, repoDir);
|
||||
}
|
||||
|
||||
await execCommand2('git push');
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const scriptName: string = 'plugin-repo-cli';
|
||||
|
||||
const commands: Record<string, Function> = {
|
||||
build: commandBuild,
|
||||
};
|
||||
|
||||
let selectedCommand: string = '';
|
||||
let selectedCommandArgs: string = '';
|
||||
|
||||
function setSelectedCommand(name: string, args: any) {
|
||||
selectedCommand = name;
|
||||
selectedCommandArgs = args;
|
||||
}
|
||||
|
||||
require('yargs')
|
||||
.scriptName(scriptName)
|
||||
.usage('$0 <cmd> [args]')
|
||||
.command('build <plugin-repo-dir>', 'Build the plugin repository', (yargs: any) => {
|
||||
yargs.positional('plugin-repo-dir', {
|
||||
type: 'string',
|
||||
describe: 'Directory where the plugin repository is located',
|
||||
});
|
||||
}, (args: any) => setSelectedCommand('build', args))
|
||||
.help()
|
||||
.argv;
|
||||
|
||||
if (!selectedCommand) {
|
||||
console.error(`Please provide a command name or type \`${scriptName} --help\` for help`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!commands[selectedCommand]) {
|
||||
console.error(`No such command: ${selectedCommand}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
await commands[selectedCommand](selectedCommandArgs);
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
4894
packages/plugin-repo-cli/package-lock.json
generated
Normal file
4894
packages/plugin-repo-cli/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
31
packages/plugin-repo-cli/package.json
Normal file
31
packages/plugin-repo-cli/package.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "@joplin/plugin-repo-builder",
|
||||
"version": "1.7.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"scripts": {
|
||||
"tsc": "tsc --project tsconfig.json",
|
||||
"watch": "tsc --watch --project tsconfig.json",
|
||||
"prepare": "npm run tsc",
|
||||
"test": "jest",
|
||||
"test-ci": "npm run test"
|
||||
},
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@joplin/lib": "*",
|
||||
"@joplin/tools": "*",
|
||||
"fs-extra": "^9.0.1",
|
||||
"yargs": "^16.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^26.0.15",
|
||||
"@types/node": "^14.14.6",
|
||||
"@types/fs-extra": "^9.0.6",
|
||||
"jest": "^26.6.3",
|
||||
"typescript": "^4.1.3"
|
||||
}
|
||||
}
|
10
packages/plugin-repo-cli/tsconfig.json
Normal file
10
packages/plugin-repo-cli/tsconfig.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules",
|
||||
],
|
||||
}
|
40
packages/tools/lerna-add.ts
Normal file
40
packages/tools/lerna-add.ts
Normal file
@ -0,0 +1,40 @@
|
||||
// // npx lerna add --no-bootstrap --scope @joplin/plugin-repo-builder -D jest && npx lerna bootstrap --no-ci --include-dependents --include-dependencies --scope @joplin/plugin-repo-builder
|
||||
|
||||
// import { execCommand2, rootDir, gitPullTry } from './tool-utils.js';
|
||||
|
||||
// async function main() {
|
||||
// const argv = require('yargs').argv;
|
||||
|
||||
// console.info(process.argv);
|
||||
|
||||
// const args = [];
|
||||
// if (argv.D) args.push('-D');
|
||||
|
||||
// await execCommand2('npx lerna add --no-bootstrap --scope @joplin/plugin-repo-builder -D jest');
|
||||
|
||||
// //npx lerna add --no-bootstrap --scope @joplin/plugin-repo-builder -D jest && npx lerna bootstrap --no-ci --include-dependents --include-dependencies --scope @joplin/plugin-repo-builder
|
||||
// // process.chdir(rootDir);
|
||||
// // const version = (await execCommand2('npm version patch')).trim();
|
||||
// // const versionShort = version.substr(1);
|
||||
// // const tagName = `server-${version}`;
|
||||
|
||||
// // process.chdir(rootDir);
|
||||
// // console.info(`Running from: ${process.cwd()}`);
|
||||
|
||||
// // await execCommand2(`docker build -t "joplin/server:${versionShort}" -f Dockerfile.server .`);
|
||||
// // await execCommand2(`docker tag "joplin/server:${versionShort}" "joplin/server:latest"`);
|
||||
// // await execCommand2(`docker push joplin/server:${versionShort}`);
|
||||
// // await execCommand2('docker push joplin/server:latest');
|
||||
|
||||
// // await execCommand2('git add -A');
|
||||
// // await execCommand2(`git commit -m 'Server release ${version}'`);
|
||||
// // await execCommand2(`git tag ${tagName}`);
|
||||
// // await execCommand2('git push');
|
||||
// // await execCommand2('git push --tags');
|
||||
// }
|
||||
|
||||
// main().catch((error) => {
|
||||
// console.error('Fatal error');
|
||||
// console.error(error);
|
||||
// process.exit(1);
|
||||
// });
|
3336
packages/tools/package-lock.json
generated
3336
packages/tools/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
const { execCommand2, rootDir, gitPullTry } = require('./tool-utils.js');
|
||||
import { execCommand2, rootDir, gitPullTry } from './tool-utils';
|
||||
|
||||
const serverDir = `${rootDir}/packages/server`;
|
||||
|
||||
|
@ -80,6 +80,7 @@ async function main() {
|
||||
await updatePackageVersion(`${rootDir}/packages/app-cli/package.json`, majorMinorVersion);
|
||||
await updatePackageVersion(`${rootDir}/packages/generator-joplin/package.json`, majorMinorVersion);
|
||||
await updatePackageVersion(`${rootDir}/packages/server/package.json`, majorMinorVersion);
|
||||
await updatePackageVersion(`${rootDir}/packages/plugin-repo-cli/package.json`, majorMinorVersion);
|
||||
await updateGradleVersion(`${rootDir}/packages/app-mobile/android/app/build.gradle`, majorMinorVersion);
|
||||
await updateCodeProjVersion(`${rootDir}/packages/app-mobile/ios/Joplin.xcodeproj/project.pbxproj`, iosVersionHack(majorMinorVersion));
|
||||
await updateClipperManifestVersion(`${rootDir}/packages/app-clipper/manifest.json`, majorMinorVersion);
|
||||
|
@ -1,16 +1,51 @@
|
||||
import * as fs from 'fs-extra';
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
const fetch = require('node-fetch');
|
||||
const fs = require('fs-extra');
|
||||
const execa = require('execa');
|
||||
const { execSync } = require('child_process');
|
||||
const { splitCommandString } = require('@joplin/lib/string-utils');
|
||||
|
||||
const toolUtils = {};
|
||||
function quotePath(path: string) {
|
||||
if (!path) return '';
|
||||
if (path.indexOf('"') < 0 && path.indexOf(' ') < 0) return path;
|
||||
path = path.replace(/"/, '\\"');
|
||||
return `"${path}"`;
|
||||
}
|
||||
|
||||
toolUtils.execCommand = function(command) {
|
||||
function commandToString(commandName: string, args: string[] = []) {
|
||||
const output = [quotePath(commandName)];
|
||||
|
||||
for (const arg of args) {
|
||||
output.push(quotePath(arg));
|
||||
}
|
||||
|
||||
return output.join(' ');
|
||||
}
|
||||
|
||||
async function loadGitHubUsernameCache() {
|
||||
const path = `${__dirname}/github_username_cache.json`;
|
||||
|
||||
if (await fs.pathExists(path)) {
|
||||
const jsonString = await fs.readFile(path, 'utf8');
|
||||
return JSON.parse(jsonString);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
async function saveGitHubUsernameCache(cache: any) {
|
||||
const path = `${__dirname}/github_username_cache.json`;
|
||||
await fs.writeFile(path, JSON.stringify(cache));
|
||||
}
|
||||
|
||||
// Returns the project root dir
|
||||
export const rootDir = require('path').dirname(require('path').dirname(__dirname));
|
||||
|
||||
export function execCommand(command: string) {
|
||||
const exec = require('child_process').exec;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
exec(command, (error, stdout, stderr) => {
|
||||
exec(command, (error: any, stdout: any, stderr: any) => {
|
||||
if (error) {
|
||||
if (error.signal == 'SIGTERM') {
|
||||
resolve('Process was killed');
|
||||
@ -22,39 +57,28 @@ toolUtils.execCommand = function(command) {
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
function quotePath(path) {
|
||||
if (!path) return '';
|
||||
if (path.indexOf('"') < 0 && path.indexOf(' ') < 0) return path;
|
||||
path = path.replace(/"/, '\\"');
|
||||
return `"${path}"`;
|
||||
}
|
||||
|
||||
function commandToString(commandName, args = []) {
|
||||
const output = [quotePath(commandName)];
|
||||
|
||||
for (const arg of args) {
|
||||
output.push(quotePath(arg));
|
||||
}
|
||||
|
||||
return output.join(' ');
|
||||
}
|
||||
|
||||
toolUtils.resolveRelativePathWithinDir = function(baseDir, ...relativePath) {
|
||||
export function resolveRelativePathWithinDir(baseDir: string, ...relativePath: string[]) {
|
||||
const path = require('path');
|
||||
const resolvedBaseDir = path.resolve(baseDir);
|
||||
const resolvedPath = path.resolve(baseDir, ...relativePath);
|
||||
if (resolvedPath.indexOf(resolvedBaseDir) !== 0) throw new Error(`Resolved path for relative path "${JSON.stringify(relativePath)}" is not within base directory "${baseDir}" (Was resolved to ${resolvedPath})`);
|
||||
return resolvedPath;
|
||||
};
|
||||
}
|
||||
|
||||
toolUtils.execCommandVerbose = function(commandName, args = []) {
|
||||
export function execCommandVerbose(commandName: string, args: string[] = []) {
|
||||
console.info(`> ${commandToString(commandName, args)}`);
|
||||
const promise = execa(commandName, args);
|
||||
promise.stdout.pipe(process.stdout);
|
||||
return promise;
|
||||
};
|
||||
}
|
||||
|
||||
interface ExecCommandOptions {
|
||||
showInput?: boolean;
|
||||
showOutput?: boolean;
|
||||
quiet?: boolean;
|
||||
}
|
||||
|
||||
// There's lot of execCommandXXX functions, but eventually all scripts should
|
||||
// use the one below, which supports:
|
||||
@ -62,66 +86,79 @@ toolUtils.execCommandVerbose = function(commandName, args = []) {
|
||||
// - Printing the command being executed
|
||||
// - Printing the output in real time (piping to stdout)
|
||||
// - Returning the command result as string
|
||||
toolUtils.execCommand2 = async function(command, options = null) {
|
||||
export async function execCommand2(command: string | string[], options: ExecCommandOptions = null): Promise<string> {
|
||||
options = {
|
||||
showInput: true,
|
||||
showOutput: true,
|
||||
quiet: false,
|
||||
...options,
|
||||
};
|
||||
|
||||
if (options.showInput) console.info(`> ${command}`);
|
||||
const args = splitCommandString(command);
|
||||
if (options.quiet) {
|
||||
options.showInput = false;
|
||||
options.showOutput = false;
|
||||
}
|
||||
|
||||
if (options.showInput) {
|
||||
if (typeof command === 'string') {
|
||||
console.info(`> ${command}`);
|
||||
} else {
|
||||
console.info(`> ${commandToString(command[0], command.slice(1))}`);
|
||||
}
|
||||
}
|
||||
|
||||
const args: string[] = typeof command === 'string' ? splitCommandString(command) : command as string[];
|
||||
const executableName = args[0];
|
||||
args.splice(0, 1);
|
||||
const promise = execa(executableName, args);
|
||||
if (options.showOutput) promise.stdout.pipe(process.stdout);
|
||||
const result = await promise;
|
||||
return result.stdout;
|
||||
};
|
||||
return result.stdout.trim();
|
||||
}
|
||||
|
||||
toolUtils.execCommandWithPipes = function(executable, args) {
|
||||
export function execCommandWithPipes(executable: string, args: string[]) {
|
||||
const spawn = require('child_process').spawn;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const child = spawn(executable, args, { stdio: 'inherit' });
|
||||
|
||||
child.on('error', (error) => {
|
||||
child.on('error', (error: any) => {
|
||||
reject(error);
|
||||
});
|
||||
|
||||
child.on('close', (code) => {
|
||||
child.on('close', (code: any) => {
|
||||
if (code !== 0) {
|
||||
reject(`Ended with code ${code}`);
|
||||
} else {
|
||||
resolve();
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
toolUtils.toSystemSlashes = function(path) {
|
||||
export function toSystemSlashes(path: string) {
|
||||
const os = process.platform;
|
||||
if (os === 'win32') return path.replace(/\//g, '\\');
|
||||
return path.replace(/\\/g, '/');
|
||||
};
|
||||
}
|
||||
|
||||
toolUtils.deleteLink = async function(path) {
|
||||
if (toolUtils.isWindows()) {
|
||||
export function deleteLink(path: string) {
|
||||
if (isWindows()) {
|
||||
try {
|
||||
execSync(`rmdir "${toolUtils.toSystemSlashes(path)}"`, { stdio: 'pipe' });
|
||||
execSync(`rmdir "${toSystemSlashes(path)}"`, { stdio: 'pipe' });
|
||||
} catch (error) {
|
||||
// console.info('Error: ' + error.message);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
fs.unlinkSync(toolUtils.toSystemSlashes(path));
|
||||
fs.unlinkSync(toSystemSlashes(path));
|
||||
} catch (error) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
toolUtils.setPackagePrivateField = async function(filePath, value) {
|
||||
export async function setPackagePrivateField(filePath: string, value: any) {
|
||||
const text = await fs.readFile(filePath, 'utf8');
|
||||
const obj = JSON.parse(text);
|
||||
if (!value) {
|
||||
@ -130,9 +167,9 @@ toolUtils.setPackagePrivateField = async function(filePath, value) {
|
||||
obj.private = true;
|
||||
}
|
||||
await fs.writeFile(filePath, JSON.stringify(obj, null, 2), 'utf8');
|
||||
};
|
||||
}
|
||||
|
||||
toolUtils.credentialDir = async function() {
|
||||
export async function credentialDir() {
|
||||
const username = require('os').userInfo().username;
|
||||
|
||||
const toTry = [
|
||||
@ -143,48 +180,45 @@ toolUtils.credentialDir = async function() {
|
||||
];
|
||||
|
||||
for (const dirPath of toTry) {
|
||||
if (await fs.exists(dirPath)) return dirPath;
|
||||
if (await fs.pathExists(dirPath)) return dirPath;
|
||||
}
|
||||
|
||||
throw new Error(`Could not find credential directory in any of these paths: ${JSON.stringify(toTry)}`);
|
||||
};
|
||||
}
|
||||
|
||||
// Returns the project root dir
|
||||
toolUtils.rootDir = require('path').dirname(require('path').dirname(__dirname));
|
||||
|
||||
toolUtils.credentialFile = async function(filename) {
|
||||
const rootDir = await toolUtils.credentialDir();
|
||||
export async function credentialFile(filename: string) {
|
||||
const rootDir = await credentialDir();
|
||||
const output = `${rootDir}/${filename}`;
|
||||
if (!(await fs.exists(output))) throw new Error(`No such file: ${output}`);
|
||||
if (!(await fs.pathExists(output))) throw new Error(`No such file: ${output}`);
|
||||
return output;
|
||||
};
|
||||
}
|
||||
|
||||
toolUtils.readCredentialFile = async function(filename) {
|
||||
const filePath = await toolUtils.credentialFile(filename);
|
||||
export async function readCredentialFile(filename: string) {
|
||||
const filePath = await credentialFile(filename);
|
||||
const r = await fs.readFile(filePath);
|
||||
return r.toString();
|
||||
};
|
||||
}
|
||||
|
||||
toolUtils.downloadFile = function(url, targetPath) {
|
||||
export async function downloadFile(url: string, targetPath: string) {
|
||||
const https = require('https');
|
||||
const fs = require('fs');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const file = fs.createWriteStream(targetPath);
|
||||
https.get(url, function(response) {
|
||||
https.get(url, function(response: any) {
|
||||
if (response.statusCode !== 200) reject(new Error(`HTTP error ${response.statusCode}`));
|
||||
response.pipe(file);
|
||||
file.on('finish', function() {
|
||||
// file.close();
|
||||
resolve();
|
||||
resolve(null);
|
||||
});
|
||||
}).on('error', (error) => {
|
||||
}).on('error', (error: any) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
toolUtils.fileSha256 = function(filePath) {
|
||||
export function fileSha256(filePath: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const crypto = require('crypto');
|
||||
const fs = require('fs');
|
||||
@ -192,18 +226,18 @@ toolUtils.fileSha256 = function(filePath) {
|
||||
const shasum = crypto.createHash(algo);
|
||||
|
||||
const s = fs.ReadStream(filePath);
|
||||
s.on('data', function(d) { shasum.update(d); });
|
||||
s.on('data', function(d: any) { shasum.update(d); });
|
||||
s.on('end', function() {
|
||||
const d = shasum.digest('hex');
|
||||
resolve(d);
|
||||
});
|
||||
s.on('error', function(error) {
|
||||
s.on('error', function(error: any) {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
toolUtils.unlinkForce = async function(filePath) {
|
||||
export async function unlinkForce(filePath: string) {
|
||||
const fs = require('fs-extra');
|
||||
|
||||
try {
|
||||
@ -212,13 +246,13 @@ toolUtils.unlinkForce = async function(filePath) {
|
||||
if (error.code === 'ENOENT') return;
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
toolUtils.fileExists = async function(filePath) {
|
||||
export function fileExists(filePath: string) {
|
||||
const fs = require('fs-extra');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.stat(filePath, function(err) {
|
||||
fs.stat(filePath, function(err: any) {
|
||||
if (err == null) {
|
||||
resolve(true);
|
||||
} else if (err.code == 'ENOENT') {
|
||||
@ -228,27 +262,22 @@ toolUtils.fileExists = async function(filePath) {
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
async function loadGitHubUsernameCache() {
|
||||
const path = `${__dirname}/github_username_cache.json`;
|
||||
|
||||
if (await fs.exists(path)) {
|
||||
const jsonString = await fs.readFile(path);
|
||||
return JSON.parse(jsonString);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
async function saveGitHubUsernameCache(cache) {
|
||||
const path = `${__dirname}/github_username_cache.json`;
|
||||
await fs.writeFile(path, JSON.stringify(cache));
|
||||
|
||||
export async function gitRepoClean(): Promise<boolean> {
|
||||
const output = await execCommand2('git status --porcelain', { quiet: true });
|
||||
return !output.trim();
|
||||
}
|
||||
|
||||
toolUtils.gitPullTry = async function() {
|
||||
|
||||
export async function gitRepoCleanTry() {
|
||||
if (!(await gitRepoClean())) throw new Error(`There are pending changes in the repository: ${process.cwd()}`);
|
||||
}
|
||||
|
||||
export async function gitPullTry() {
|
||||
try {
|
||||
await toolUtils.execCommand('git pull');
|
||||
await execCommand('git pull');
|
||||
} catch (error) {
|
||||
if (error.message.includes('no tracking information for the current branch')) {
|
||||
console.info('Skipping git pull because no tracking information on current branch');
|
||||
@ -256,16 +285,16 @@ toolUtils.gitPullTry = async function() {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
toolUtils.githubUsername = async function(email, name) {
|
||||
export async function githubUsername(email: string, name: string) {
|
||||
const cache = await loadGitHubUsernameCache();
|
||||
const cacheKey = `${email}:${name}`;
|
||||
if (cacheKey in cache) return cache[cacheKey];
|
||||
|
||||
let output = null;
|
||||
|
||||
const oauthToken = await toolUtils.githubOauthToken();
|
||||
const oauthToken = await githubOauthToken();
|
||||
|
||||
const urlsToTry = [
|
||||
`https://api.github.com/search/users?q=${encodeURI(email)}+in:email`,
|
||||
@ -296,23 +325,23 @@ toolUtils.githubUsername = async function(email, name) {
|
||||
await saveGitHubUsernameCache(cache);
|
||||
|
||||
return output;
|
||||
};
|
||||
}
|
||||
|
||||
toolUtils.patreonOauthToken = async function() {
|
||||
return toolUtils.readCredentialFile('patreon_oauth_token.txt');
|
||||
};
|
||||
export function patreonOauthToken() {
|
||||
return readCredentialFile('patreon_oauth_token.txt');
|
||||
}
|
||||
|
||||
toolUtils.githubOauthToken = async function() {
|
||||
return toolUtils.readCredentialFile('github_oauth_token.txt');
|
||||
};
|
||||
export function githubOauthToken() {
|
||||
return readCredentialFile('github_oauth_token.txt');
|
||||
}
|
||||
|
||||
toolUtils.githubRelease = async function(project, tagName, options = null) {
|
||||
export async function githubRelease(project: string, tagName: string, options: any = null) {
|
||||
options = Object.assign({}, {
|
||||
isDraft: false,
|
||||
isPreRelease: false,
|
||||
}, options);
|
||||
|
||||
const oauthToken = await toolUtils.githubOauthToken();
|
||||
const oauthToken = await githubOauthToken();
|
||||
|
||||
const response = await fetch(`https://api.github.com/repos/laurent22/${project}/releases`, {
|
||||
method: 'POST',
|
||||
@ -336,9 +365,9 @@ toolUtils.githubRelease = async function(project, tagName, options = null) {
|
||||
if (!responseJson.url) throw new Error(`No URL for release: ${responseText}`);
|
||||
|
||||
return responseJson;
|
||||
};
|
||||
}
|
||||
|
||||
toolUtils.readline = question => {
|
||||
export function readline(question: string) {
|
||||
return new Promise((resolve) => {
|
||||
const readline = require('readline');
|
||||
|
||||
@ -347,63 +376,61 @@ toolUtils.readline = question => {
|
||||
output: process.stdout,
|
||||
});
|
||||
|
||||
rl.question(`${question} `, answer => {
|
||||
rl.question(`${question} `, (answer: string) => {
|
||||
resolve(answer);
|
||||
rl.close();
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
toolUtils.isLinux = () => {
|
||||
export function isLinux() {
|
||||
return process && process.platform === 'linux';
|
||||
};
|
||||
}
|
||||
|
||||
toolUtils.isWindows = () => {
|
||||
export function isWindows() {
|
||||
return process && process.platform === 'win32';
|
||||
};
|
||||
}
|
||||
|
||||
toolUtils.isMac = () => {
|
||||
export function isMac() {
|
||||
return process && process.platform === 'darwin';
|
||||
};
|
||||
}
|
||||
|
||||
toolUtils.insertContentIntoFile = async function(filePath, markerOpen, markerClose, contentToInsert) {
|
||||
export async function insertContentIntoFile(filePath: string, markerOpen: string, markerClose: string, contentToInsert: string) {
|
||||
const fs = require('fs-extra');
|
||||
let content = await fs.readFile(filePath, 'utf-8');
|
||||
// [^]* matches any character including new lines
|
||||
const regex = new RegExp(`${markerOpen}[^]*?${markerClose}`);
|
||||
content = content.replace(regex, markerOpen + contentToInsert + markerClose);
|
||||
await fs.writeFile(filePath, content);
|
||||
};
|
||||
}
|
||||
|
||||
toolUtils.dirname = (path) => {
|
||||
export function dirname(path: string) {
|
||||
if (!path) throw new Error('Path is empty');
|
||||
const s = path.split(/\/|\\/);
|
||||
s.pop();
|
||||
return s.join('/');
|
||||
};
|
||||
}
|
||||
|
||||
toolUtils.basename = (path) => {
|
||||
export function basename(path: string) {
|
||||
if (!path) throw new Error('Path is empty');
|
||||
const s = path.split(/\/|\\/);
|
||||
return s[s.length - 1];
|
||||
};
|
||||
}
|
||||
|
||||
toolUtils.filename = (path, includeDir = false) => {
|
||||
export function filename(path: string, includeDir = false) {
|
||||
if (!path) throw new Error('Path is empty');
|
||||
const output = includeDir ? path : toolUtils.basename(path);
|
||||
const output = includeDir ? path : basename(path);
|
||||
if (output.indexOf('.') < 0) return output;
|
||||
|
||||
const splitted = output.split('.');
|
||||
splitted.pop();
|
||||
return splitted.join('.');
|
||||
};
|
||||
}
|
||||
|
||||
toolUtils.fileExtension = (path) => {
|
||||
export function fileExtension(path: string) {
|
||||
if (!path) throw new Error('Path is empty');
|
||||
|
||||
const output = path.split('.');
|
||||
if (output.length <= 1) return '';
|
||||
return output[output.length - 1];
|
||||
};
|
||||
|
||||
module.exports = toolUtils;
|
||||
}
|
Loading…
Reference in New Issue
Block a user