Basic polyglot plugin crap
This commit is contained in:
parent
65c69d58d4
commit
34a85f0a0c
38
manifest.yml
38
manifest.yml
|
@ -6,13 +6,47 @@ plugins:
|
||||||
run: '{plugin_dir}/echo.sh'
|
run: '{plugin_dir}/echo.sh'
|
||||||
# this is the default installCommand, so we don't really need to specify it
|
# this is the default installCommand, so we don't really need to specify it
|
||||||
installCommand: ['git', 'clone', '{remote}', '{plugin_dir}']
|
installCommand: ['git', 'clone', '{remote}', '{plugin_dir}']
|
||||||
|
preInstallCommand: ['bash', '-c', 'echo "is this thing on?"']
|
||||||
|
# postInstallCommand:
|
||||||
# none of these work, but wouldn't be tough to implement if needed
|
# none of these work, but wouldn't be tough to implement if needed
|
||||||
# preInstallCommand:
|
|
||||||
# may also want to checkout a provided version tag and copy contents
|
# may also want to checkout a provided version tag and copy contents
|
||||||
# and only clone to a "plugin-repos" dir
|
# and only clone to a "plugin-repos" dir
|
||||||
# might want to omit git history, too?
|
# might want to omit git history, too?
|
||||||
# postInstallCommand:
|
|
||||||
|
|
||||||
shutup:
|
shutup:
|
||||||
remote: 'https://git.lyte.dev/lytedev/shutup-cli-plugin'
|
remote: 'https://git.lyte.dev/lytedev/shutup-cli-plugin'
|
||||||
run: '{plugin_dir}/shutup.sh'
|
run: '{plugin_dir}/shutup.sh'
|
||||||
|
|
||||||
|
'go-hello-world':
|
||||||
|
preInstallCommand:
|
||||||
|
# install golang using asdf
|
||||||
|
# if we wanted, we could have an asdf plugin and install it here like
|
||||||
|
# poc-cli install-plugin asdf
|
||||||
|
# and it would make sure the shell and everything is ready to go
|
||||||
|
- 'bash'
|
||||||
|
- '-c'
|
||||||
|
- 'asdf plugin-add golang; asdf install golang latest; asdf global golang latest; curl -o /tmp/hello-world.go https://raw.githubusercontent.com/mmcgrana/gobyexample/master/examples/hello-world/hello-world.go'
|
||||||
|
installCommand:
|
||||||
|
- bash
|
||||||
|
- '-c'
|
||||||
|
- 'cd {plugin_dir} && go build /tmp/hello-world.go'
|
||||||
|
run: '{plugin_dir}/hello-world'
|
||||||
|
|
||||||
|
'elixir-hello-jason':
|
||||||
|
preInstallCommand:
|
||||||
|
# obviously, installing elixir and erlang through asdf can be a little
|
||||||
|
# hairy in the real world, but a contrived example suits this
|
||||||
|
# proof-of-concept just fine
|
||||||
|
- 'bash'
|
||||||
|
- '-c'
|
||||||
|
- 'asdf plugin-add erlang; asdf plugin-add elixir; asdf install erlang latest; asdf install elixir latest; asdf global erlang latest; asdf global elixir latest'
|
||||||
|
installCommand:
|
||||||
|
- bash
|
||||||
|
- '-c'
|
||||||
|
# remember, instead of installing :jason, we might install internal libraries!
|
||||||
|
- 'printf "%s\n%s" "Mix.install([:jason])" "IO.puts(Jason.encode!(%{hello: :world}))" > {plugin_dir}/script.exs'
|
||||||
|
run:
|
||||||
|
- elixir
|
||||||
|
- '{plugin_dir}/script.exs'
|
||||||
|
|
||||||
|
# in the real world, most of these scripts would probably live within the plugin themselves to be easier to write
|
||||||
|
|
38
readme.md
38
readme.md
|
@ -1,3 +1,41 @@
|
||||||
|
# CLI Proof-of-Concept
|
||||||
|
|
||||||
|
## Known Issues
|
||||||
|
|
||||||
|
- The plugin directory being present doesn't _really_ mean it's "installed" ;P
|
||||||
|
|
||||||
|
## Compile
|
||||||
|
|
||||||
|
```sh
|
||||||
|
deno compile -A -o poc-cli src/core.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
You can cross-compile, too. See `deno compile --help` for `--target`.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
I dunno, but probably something like this:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo mv poc-cli /usr/bin
|
||||||
|
sudo chown root:root /usr/bin/poc-cli
|
||||||
|
sudo chmod 755 /usr/bin/poc-cli
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
It's self-documenting!
|
||||||
|
|
||||||
|
```
|
||||||
|
poc-cli
|
||||||
|
```
|
||||||
|
|
||||||
|
-----
|
||||||
|
|
||||||
|
# Notes Below!
|
||||||
|
|
||||||
|
-----
|
||||||
|
|
||||||
# Pluggable CLI
|
# Pluggable CLI
|
||||||
|
|
||||||
- Each subcommand is a plugin?
|
- Each subcommand is a plugin?
|
||||||
|
|
70
src/core.ts
70
src/core.ts
|
@ -1,7 +1,8 @@
|
||||||
import {yaml, fs, path, xdg} from "./deps.ts"
|
import {yaml, fs, path, xdg} from "./deps.ts"
|
||||||
|
|
||||||
const MANIFEST_URL = "https://git.lyte.dev/lytedev/pluggable-cli-deno/raw/branch/master/manifest.yml"
|
// const MANIFEST_URL = "https://git.lyte.dev/lytedev/pluggable-cli-deno/raw/branch/master/manifest.yml"
|
||||||
const PLUGINS_DIR = path.join(xdg.cache(), "/cli-poc-installed-plugins")
|
const MANIFEST_URL = "file:///home/daniel/code/pluggable-cli/manifest.yml"
|
||||||
|
const PLUGINS_DIR = path.join(xdg.cache(), "/poc-cli-installed-plugins")
|
||||||
|
|
||||||
fs.ensureDir(PLUGINS_DIR)
|
fs.ensureDir(PLUGINS_DIR)
|
||||||
|
|
||||||
|
@ -19,14 +20,20 @@ for await (const dir of Deno.readDir(PLUGINS_DIR)) {
|
||||||
}
|
}
|
||||||
// console.debug(manifest)
|
// console.debug(manifest)
|
||||||
|
|
||||||
|
async function showConfig() {
|
||||||
|
console.info("MANIFEST_URL:", MANIFEST_URL)
|
||||||
|
console.info("PLUGINS_DIR:", PLUGINS_DIR)
|
||||||
|
}
|
||||||
|
|
||||||
async function usage() {
|
async function usage() {
|
||||||
console.info("cli-poc: a CLI proof-of-concept")
|
console.info("poc-cli: a CLI proof-of-concept")
|
||||||
console.info(" -h, --help: Show usage")
|
console.info(" -h, --help: Show usage")
|
||||||
console.info(" install-plugin <plugin-name>")
|
console.info(" install-plugin <plugin-name>")
|
||||||
console.info(" update-plugin <plugin-name>")
|
console.info(" update-plugin <plugin-name>")
|
||||||
console.info(" remove-plugin <plugin-name>")
|
console.info(" remove-plugin <plugin-name>")
|
||||||
console.info(" ensure-plugin <plugin-name>")
|
console.info(" ensure-plugin <plugin-name>")
|
||||||
console.info(" list-plugins")
|
console.info(" list-plugins")
|
||||||
|
console.info(" show-config")
|
||||||
}
|
}
|
||||||
|
|
||||||
async function listPlugins() {
|
async function listPlugins() {
|
||||||
|
@ -37,8 +44,23 @@ async function listPlugins() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function installedPlugins() {
|
async function runCommandAndMaybeExit(cmd: string[] | string, additionalOptions?: Partial<Deno.RunOptions>) {
|
||||||
|
if (typeof cmd === 'string') {
|
||||||
|
cmd = [cmd]
|
||||||
|
}
|
||||||
|
const opts = Object.assign({
|
||||||
|
stdout: "piped",
|
||||||
|
stderr: "piped",
|
||||||
|
cmd
|
||||||
|
}, additionalOptions)
|
||||||
|
// console.debug(opts)
|
||||||
|
const runner = Deno.run(opts)
|
||||||
|
const status = await runner.status()
|
||||||
|
if (status.code != 0) {
|
||||||
|
console.error(`Command ${JSON.stringify(cmd)} failed:\n${new TextDecoder().decode(await runner.output())}\n${new TextDecoder().decode(await runner.stderrOutput())}`)
|
||||||
|
Deno.exit(status.code)
|
||||||
|
}
|
||||||
|
return runner
|
||||||
}
|
}
|
||||||
|
|
||||||
async function installPlugin(pluginName: string) {
|
async function installPlugin(pluginName: string) {
|
||||||
|
@ -47,25 +69,25 @@ async function installPlugin(pluginName: string) {
|
||||||
console.error(`plugin ${pluginName} has no entry`)
|
console.error(`plugin ${pluginName} has no entry`)
|
||||||
Deno.exit(1)
|
Deno.exit(1)
|
||||||
}
|
}
|
||||||
const cmd = (pluginManifestData.installCommand || ["git", "clone", pluginManifestData.remote, pluginName]).map((segment: string) =>
|
|
||||||
|
if (pluginManifestData.preInstallCommand) {
|
||||||
|
await runCommandAndMaybeExit(pluginManifestData.preInstallCommand)
|
||||||
|
}
|
||||||
|
|
||||||
|
const cmd = (pluginManifestData.installCommand || ["git", "clone", "{remote}", "{plugin_dir}"]).map((segment: string) =>
|
||||||
segment
|
segment
|
||||||
.replace('{remote}', pluginManifestData.remote)
|
.replace('{remote}', pluginManifestData.remote)
|
||||||
.replace('{plugin_dir}', path.join(PLUGINS_DIR, pluginName))
|
.replace('{plugin_dir}', path.join(PLUGINS_DIR, pluginName))
|
||||||
)
|
)
|
||||||
|
|
||||||
console.debug(cmd)
|
fs.ensureDir(path.join(PLUGINS_DIR, pluginName))
|
||||||
|
const result = await runCommandAndMaybeExit(cmd)
|
||||||
|
|
||||||
const installCommand = Deno.run({
|
if (pluginManifestData.postInstallCommand) {
|
||||||
stdout: "piped",
|
await runCommandAndMaybeExit(pluginManifestData.postInstallCommand)
|
||||||
stderr: "piped",
|
|
||||||
cmd
|
|
||||||
})
|
|
||||||
const status = await installCommand.status()
|
|
||||||
if (status.code != 0) {
|
|
||||||
console.error(`Installing plugin using command ${cmd} failed:\n${new TextDecoder().decode(await installCommand.output())}\n${new TextDecoder().decode(await installCommand.stderrOutput())}`)
|
|
||||||
Deno.exit(status.code)
|
|
||||||
}
|
}
|
||||||
return installCommand
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deletePlugin(pluginName: string) {
|
async function deletePlugin(pluginName: string) {
|
||||||
|
@ -79,14 +101,14 @@ async function updatePlugin(pluginName: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ensurePlugin(pluginName: string) {
|
async function ensurePlugin(pluginName: string) {
|
||||||
if (manifest.plugins[subcommand].installed !== true) {
|
if (!manifest.plugins[subcommand].installed) {
|
||||||
// console.warn(`Installing missing ${subcommand} plugin...`)
|
// console.warn(`Installing missing ${subcommand} plugin...`)
|
||||||
await installPlugin(subcommand)
|
await installPlugin(subcommand)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const subcommand = Deno.args[0]
|
const subcommand = Deno.args[0]
|
||||||
if (Deno.args.includes("-h") || subcommand == "help" || subcommand == "" || Deno.args.includes("--help")) {
|
if (Deno.args.includes("-h") || subcommand == "help" || subcommand == "" || Deno.args.includes("--help") || Deno.args.length < 1) {
|
||||||
usage()
|
usage()
|
||||||
} else if (subcommand == "update-plugin") {
|
} else if (subcommand == "update-plugin") {
|
||||||
await updatePlugin(Deno.args[1])
|
await updatePlugin(Deno.args[1])
|
||||||
|
@ -98,11 +120,11 @@ if (Deno.args.includes("-h") || subcommand == "help" || subcommand == "" || Deno
|
||||||
await installPlugin(Deno.args[1])
|
await installPlugin(Deno.args[1])
|
||||||
} else if (subcommand == "list-plugins") {
|
} else if (subcommand == "list-plugins") {
|
||||||
listPlugins()
|
listPlugins()
|
||||||
|
} else if (subcommand == "show-config") {
|
||||||
|
showConfig()
|
||||||
} else {
|
} else {
|
||||||
await ensurePlugin(subcommand)
|
await ensurePlugin(subcommand)
|
||||||
const subcommandCommand = Deno.run({
|
const cmd = (typeof manifest.plugins[subcommand].run === 'string' ?
|
||||||
cmd: [manifest.plugins[subcommand].run.replace("{plugin_dir}", path.join(PLUGINS_DIR, subcommand))].concat(Deno.args.slice(1))
|
[manifest.plugins[subcommand].run] : manifest.plugins[subcommand].run).map((segment: string) => segment.replace("{plugin_dir}", path.join(PLUGINS_DIR, subcommand)))
|
||||||
})
|
await runCommandAndMaybeExit(cmd.concat(Deno.args.slice(1)), {stdout: "inherit", stderr: "inherit"})
|
||||||
const status = await subcommandCommand.status()
|
|
||||||
if (status.code != 0) Deno.exit(status.code)
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue