Repository tools
This commit is contained in:
parent
d63409494c
commit
df17cb9cba
@ -62,6 +62,8 @@ export class Squeebot {
|
||||
// Start channels
|
||||
this.channelManager.initialize(this.config.get('channels'));
|
||||
|
||||
logger.debug(this.channelManager);
|
||||
|
||||
if (autostart) {
|
||||
// Start enabled plugins
|
||||
await this.startPlugins();
|
||||
@ -79,12 +81,15 @@ export class Squeebot {
|
||||
try {
|
||||
await this.pluginManager.load(getManifest);
|
||||
} catch (e) {
|
||||
logger.error(new Error(`Failed to start ${pluginName}: ${e.message}`));
|
||||
logger.error('Failed to start %s:', pluginName, e.stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public attachReadline(rl: any): void {
|
||||
// Pass readline to logger
|
||||
logger.setReadline(rl);
|
||||
|
||||
// Send readline on request
|
||||
this.stream.on('core', 'request-cli', () =>
|
||||
this.stream.emit('cli', rl));
|
||||
@ -104,7 +109,7 @@ export class Squeebot {
|
||||
this.shuttingDown = true;
|
||||
this.stream.on('core', 'shutdown', (state: number) => {
|
||||
if (state > 0) {
|
||||
this.config.save(true).then((x) => process.exit(0));
|
||||
this.config.save().then((x) => process.exit(0));
|
||||
}
|
||||
});
|
||||
this.stream.emitTo('core', 'shutdown', 0);
|
||||
|
334
src/squeebot.ts
334
src/squeebot.ts
@ -1,6 +1,334 @@
|
||||
import child_process from 'child_process';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import readline from 'readline';
|
||||
import yargs from 'yargs';
|
||||
|
||||
function call(test: string): void {
|
||||
console.log('it works!', test);
|
||||
import { logger } from '@squeebot/core/lib/core';
|
||||
import { NPMExecutor } from '@squeebot/core/lib/npm';
|
||||
import { IEnvironment } from '@squeebot/core/lib/types';
|
||||
|
||||
import { loadEnvironment } from '@squeebot/core/lib/core';
|
||||
import { PluginMetaLoader } from '@squeebot/core/lib/plugin';
|
||||
|
||||
const tsConfig = {
|
||||
compilerOptions: {
|
||||
downlevelIteration: true,
|
||||
esModuleInterop: true,
|
||||
experimentalDecorators: true,
|
||||
forceConsistentCasingInFileNames: true,
|
||||
skipLibCheck: true,
|
||||
sourceMap: false,
|
||||
strict: true,
|
||||
target: 'es5',
|
||||
},
|
||||
};
|
||||
|
||||
const gitignore = `/node_modules/
|
||||
deployment.json`;
|
||||
|
||||
function dummyEnvironment(location: string): IEnvironment {
|
||||
return {
|
||||
configurationPath: path.join(location, 'plugins'),
|
||||
environment: 'development',
|
||||
path: location,
|
||||
pluginsPath: path.join(location, 'plugins'),
|
||||
repositoryPath: path.join(location, 'plugins'),
|
||||
};
|
||||
}
|
||||
|
||||
call('hi');
|
||||
function execute(cmd: any[], loc: string): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const cproc = child_process.spawn(cmd[0], cmd.slice(1), {
|
||||
cwd: loc,
|
||||
stdio: 'pipe',
|
||||
});
|
||||
|
||||
cproc.on('exit', (code: number) => {
|
||||
if (code !== 0) {
|
||||
return reject(new Error('Build failed!'));
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function newEnvironment(
|
||||
name?: string,
|
||||
location?: string): Promise<void> {
|
||||
if (!name) {
|
||||
name = 'squeebot';
|
||||
}
|
||||
|
||||
if (!location) {
|
||||
location = name;
|
||||
}
|
||||
|
||||
location = path.resolve(process.cwd(), location);
|
||||
|
||||
const envFile = path.join(location, name + '.json');
|
||||
await fs.ensureDir(location);
|
||||
await fs.writeJson(envFile, {
|
||||
environment: 'production',
|
||||
path: location,
|
||||
});
|
||||
|
||||
console.log('\nNew environment file:\t', envFile);
|
||||
console.log('Environment path:\t', location);
|
||||
}
|
||||
|
||||
async function newRepository(
|
||||
name: string,
|
||||
location?: string,
|
||||
typescript = true): Promise<void> {
|
||||
if (!name) {
|
||||
throw new Error('Repository needs a name!');
|
||||
}
|
||||
|
||||
if (!location) {
|
||||
location = name;
|
||||
}
|
||||
|
||||
location = path.resolve(process.cwd(), location);
|
||||
console.log('Creating a new repository development environment at', location);
|
||||
|
||||
await fs.ensureDir(location);
|
||||
const env = dummyEnvironment(location);
|
||||
|
||||
console.log('Creating package.json and installing base requirements');
|
||||
const executor = new NPMExecutor(env, '/home/evert/Squeebot/core');
|
||||
await executor.loadPackageFile();
|
||||
|
||||
let gitIgnore = gitignore + '';
|
||||
|
||||
if (typescript) {
|
||||
console.log('Installing TypeScript support');
|
||||
await executor.installPackage('typescript');
|
||||
await fs.writeJson(path.join(location, 'tsconfig.json'), tsConfig);
|
||||
gitIgnore += '\n*.js';
|
||||
gitIgnore += '\n*.d.ts';
|
||||
|
||||
console.log('Adding TypeScript scripts to package.json');
|
||||
const pkgjson = path.join(location, 'package.json');
|
||||
const contents = await fs.readJson(pkgjson);
|
||||
contents.scripts = {
|
||||
build: 'tsc',
|
||||
watch: 'tsc -w',
|
||||
};
|
||||
await fs.writeJson(pkgjson, contents, { spaces: 2 });
|
||||
}
|
||||
|
||||
console.log('Creating .gitignore');
|
||||
await fs.writeFile(path.join(location, '.gitignore'), gitIgnore);
|
||||
|
||||
console.log('Writing build metadata');
|
||||
await fs.writeJson(path.join(location, 'squeebot.repo.json'), {
|
||||
name,
|
||||
plugins: [],
|
||||
typescript,
|
||||
}, { spaces: 2 });
|
||||
|
||||
console.log('\nDone! Your repository "%s" lives at:', name, location);
|
||||
}
|
||||
|
||||
async function deploy(
|
||||
name: string,
|
||||
location: string,
|
||||
outDir: string,
|
||||
deployment: string): Promise<void> {
|
||||
const deployFile = path.join(location, 'deployment.json');
|
||||
|
||||
if (!await fs.pathExists(deployFile)) {
|
||||
throw new Error('deployment.json file is missing! Can\'t deploy!');
|
||||
}
|
||||
|
||||
const meta = await fs.readJson(deployFile);
|
||||
|
||||
// If we have a path to a metadata file, we copy the output directory to
|
||||
// the plugins directory, overwriting existing plugins with the same name!
|
||||
if ((deployment.indexOf('dev') === 0 || deployment.indexOf('squee') === 0) &&
|
||||
meta.devSqueebot) {
|
||||
console.log('Deploying to a configured Squeebot development environment');
|
||||
const devbotPath = path.resolve(location, meta.devSqueebot);
|
||||
|
||||
console.log('Path:', devbotPath);
|
||||
|
||||
if (!await fs.pathExists(devbotPath)) {
|
||||
throw new Error('Development Squeebot environment file doesn\'t exist!');
|
||||
}
|
||||
|
||||
const devEnv = await loadEnvironment(devbotPath);
|
||||
if (!devEnv.pluginsPath) {
|
||||
throw new Error('Bad development Squeebot environment file!');
|
||||
}
|
||||
|
||||
const pluginsPath = path.resolve(devEnv.path, devEnv.pluginsPath);
|
||||
|
||||
console.log('Copying plugins to', pluginsPath);
|
||||
const listAllFiles = await fs.readdir(outDir);
|
||||
for (const f of listAllFiles) {
|
||||
if (f === 'repository') {
|
||||
continue;
|
||||
}
|
||||
const dst = path.join(pluginsPath, f);
|
||||
await fs.copy(path.join(outDir, f), dst, {
|
||||
overwrite: true,
|
||||
});
|
||||
}
|
||||
|
||||
console.log('Done!');
|
||||
}
|
||||
}
|
||||
|
||||
async function buildRepository(
|
||||
location?: string,
|
||||
out = true,
|
||||
doDeploy?: string,
|
||||
onlyDeploy = false): Promise<void> {
|
||||
if (!location) {
|
||||
location = process.cwd();
|
||||
}
|
||||
location = path.resolve(process.cwd(), location);
|
||||
const outDir = path.join(location, '.out');
|
||||
|
||||
const buildMetaFile = path.join(location, 'squeebot.repo.json');
|
||||
if (!await fs.pathExists(buildMetaFile)) {
|
||||
throw new Error(`${location} is not a valid squeebot repository development environment!`);
|
||||
}
|
||||
|
||||
const meta = await fs.readJson(buildMetaFile);
|
||||
const env = dummyEnvironment(location);
|
||||
env.pluginsPath = location;
|
||||
|
||||
if (!meta.name) {
|
||||
throw new Error(`${location} is not a valid squeebot repository development environment!`);
|
||||
}
|
||||
|
||||
console.log('Detected repository "%s"!', meta.name);
|
||||
|
||||
if (onlyDeploy) {
|
||||
if (!await fs.pathExists(outDir)) {
|
||||
throw new Error(`You need to build before deploying!`);
|
||||
} else {
|
||||
return deploy(meta.name, location, outDir, doDeploy as string);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Detecting plugins in this environment..');
|
||||
|
||||
const loader = new PluginMetaLoader(env);
|
||||
const plugins = await loader.loadAll(false);
|
||||
const savedList: any[] = [];
|
||||
let indexFileContents = 'name=' + meta.name + '\n';
|
||||
indexFileContents += 'created=' + Math.floor(Date.now() / 1000) + '\n';
|
||||
|
||||
console.log('Found the following plugins:', plugins.map((plugin) => {
|
||||
return `${plugin.name}@${plugin.version}`;
|
||||
}).join(', '));
|
||||
|
||||
plugins.forEach((plugin) => {
|
||||
savedList.push({
|
||||
name: plugin.name,
|
||||
version: plugin.version,
|
||||
});
|
||||
indexFileContents += `${plugin.name} ${plugin.version} "${plugin.description}"\n`;
|
||||
});
|
||||
|
||||
meta.plugins = savedList;
|
||||
await fs.writeJson(buildMetaFile, meta, { spaces: 2 });
|
||||
|
||||
if (meta.typescript) {
|
||||
console.log('Running build task..');
|
||||
await execute(['npm', 'run', 'build'], location);
|
||||
}
|
||||
|
||||
if (!out) {
|
||||
console.log('Done!');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Creating repository index');
|
||||
await fs.remove(outDir);
|
||||
await fs.ensureDir(outDir);
|
||||
await fs.writeFile(path.join(outDir, 'repository'), indexFileContents);
|
||||
|
||||
console.log('Copying plugins');
|
||||
for (const plugin of plugins) {
|
||||
const src = path.join(location, plugin.name);
|
||||
const dst = path.join(outDir, plugin.name);
|
||||
await fs.copy(src, dst);
|
||||
}
|
||||
|
||||
if (meta.typescript) {
|
||||
console.log('Stripping source files');
|
||||
for (const plugin of plugins) {
|
||||
const plOut = path.join(outDir, plugin.name);
|
||||
const listAllFiles = await fs.readdir(plOut);
|
||||
for (const f of listAllFiles) {
|
||||
if (f.match(/(\.d)?\.ts$/i) != null) {
|
||||
await fs.remove(path.join(plOut, f));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (doDeploy == null) {
|
||||
console.log('Done!');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('\n [!!!] DEPLOYING REPOSITORY [!!!]\n');
|
||||
|
||||
deploy(meta.name, location, outDir, doDeploy);
|
||||
}
|
||||
|
||||
const yar = yargs.scriptName('squeebot')
|
||||
.command('new [name] [path]', 'create a new Squeebot environment', (y) => {
|
||||
y.positional('name', {
|
||||
describe: 'The name of the new environment',
|
||||
})
|
||||
.positional('path', {
|
||||
describe: 'The path to create a new Squeebot environment at (default: working directory)',
|
||||
});
|
||||
}, (v) => newEnvironment(v.name as string, v.path as string))
|
||||
.command('repository', 'manage repositories', (y) => {
|
||||
y.command('new [name] [path]', 'create a new repository', (yi) => {
|
||||
yi.positional('name', {
|
||||
demandOption: 'The repository requires a name',
|
||||
describe: 'The name of the new repository',
|
||||
})
|
||||
.positional('path', {
|
||||
describe: 'The path to create the new Squeebot plugin repository at (default: working directory)',
|
||||
})
|
||||
.option('t', {
|
||||
alias: 'no-typescript',
|
||||
describe: 'Do not include typescript in the development environment',
|
||||
type: 'boolean',
|
||||
});
|
||||
}, (v) => newRepository(v.name as string, v.path as string, v.t !== true));
|
||||
y.command('build [path]', 'build a repository of plugins and generate the index file', (yi) => {
|
||||
yi.positional('path', {
|
||||
describe: 'The path of the repository',
|
||||
})
|
||||
.option('p', {
|
||||
alias: 'no-output',
|
||||
describe: 'Do not create an output directory, just build',
|
||||
type: 'boolean',
|
||||
})
|
||||
.option('d', {
|
||||
alias: 'deploy',
|
||||
describe: 'Deploy the output directory as configured',
|
||||
nargs: 1,
|
||||
type: 'string',
|
||||
})
|
||||
.option('o', {
|
||||
alias: 'deploy-only',
|
||||
describe: 'Deploy only, without rebuilding',
|
||||
type: 'boolean',
|
||||
});
|
||||
}, (v) => buildRepository(v.path as string,
|
||||
v.p !== true,
|
||||
v.d as string,
|
||||
v.o === true));
|
||||
})
|
||||
.argv;
|
||||
|
@ -29,8 +29,6 @@ async function start(argv: any): Promise<void> {
|
||||
|
||||
const sb = new Squeebot(env);
|
||||
|
||||
sb.stream.on('logger', 'debug', (data: string) => console.log('DEBUG', data));
|
||||
|
||||
await sb.initialize();
|
||||
|
||||
sb.attachReadline(rl);
|
||||
|
Loading…
Reference in New Issue
Block a user