Skip to content
This repository has been archived by the owner on Sep 14, 2023. It is now read-only.

Commit

Permalink
feat: add cliffy (#946)
Browse files Browse the repository at this point in the history
Co-authored-by: Harry Solovay <[email protected]>
  • Loading branch information
kratico and harrysolovay authored May 16, 2023
1 parent e514a59 commit c673026
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 69 deletions.
21 changes: 14 additions & 7 deletions cli/bin.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,38 @@
import { CapiBinary } from "../deps/capi_binary_builds.ts"
import { Command } from "../deps/cliffy.ts"

export default async function(
export const bin = new Command()
.description("Execute <binary>@<version> with [args...]")
.arguments("<binary:string> <version:string> [...args:string]")
.stopEarly()
.example("run a polkadot node in dev mode", "capi bin polkadot v0.9.41 --dev")
.example(
"build a chain spec for Rococo local",
"capi bin polkadot v0.9.41 build-spec --chain rococo-local",
)
.action(runBin)

async function runBin(
_options: void,
binary: string,
version: string,
...args: string[]
) {
if (!binary || !version) throw new Error("Must specify binary and version")

const bin = new CapiBinary(binary, version)

if (!(await bin.exists())) {
console.error("Downloading", bin.key)
await bin.download()
}

const child = new Deno.Command(bin.path, {
args,
stdin: "inherit",
stdout: "inherit",
stderr: "inherit",
}).spawn()

for (const signal of ["SIGTERM", "SIGINT"] satisfies Deno.Signal[]) {
Deno.addSignalListener(signal, () => {
child.kill(signal)
})
}

Deno.exit((await child.status).code)
}
27 changes: 19 additions & 8 deletions cli/resolveNets.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
import * as $ from "../deps/scale.ts"
import * as flags from "../deps/std/flags.ts"
import * as path from "../deps/std/path.ts"
import { NetSpec } from "../nets/mod.ts"

const $nets = $.record($.instance(NetSpec as new() => NetSpec, $.tuple(), () => []))

export async function resolveNets(...args: string[]): Promise<Record<string, NetSpec>> {
const { nets: netsPathRaw } = flags.parse(args, {
string: ["nets"],
default: { nets: "./nets.ts" },
})
const netsPath = path.resolve(netsPathRaw)
await Deno.stat(netsPath)
export async function resolveNets(maybeNetsPath?: string): Promise<Record<string, NetSpec>> {
let netsPath
if (maybeNetsPath) {
netsPath = path.resolve(maybeNetsPath)
await Deno.stat(netsPath)
} else {
for (const p of ["nets.ts", "nets.js"]) {
try {
const resolved = path.resolve(p)
await Deno.stat(resolved)
netsPath = resolved
} catch (_e) {}
}
}
if (!netsPath) {
throw new Error(
"Could not resolve net specs path. Create a `nets.ts` file and export a net spec.",
)
}
const nets = await import(path.toFileUrl(netsPath).toString())
$.assert($nets, nets)
for (const key in nets) nets[key]!.name = key
Expand Down
46 changes: 27 additions & 19 deletions cli/serve.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as flags from "../deps/std/flags.ts"
import { Command } from "../deps/cliffy.ts"
import { blue, gray, yellow } from "../deps/std/fmt/colors.ts"
import { serve } from "../deps/std/http.ts"
import { serve as httpServe } from "../deps/std/http.ts"
import {
createCodegenHandler,
createCorsHandler,
Expand All @@ -12,33 +12,41 @@ import { gracefulExit } from "../util/mod.ts"
import { tempDir } from "../util/tempDir.ts"
import { resolveNets } from "./resolveNets.ts"

export default async function(...args: string[]) {
const { port, "--": cmd, out, target } = flags.parse(args, {
string: ["port", "out", "target"],
default: {
port: "4646",
out: "target/capi",
},
"--": true,
})
export const serve = new Command()
.description("Start the Capi server")
.option("-n, --nets <nets:file>", "nets.ts file path")
.option("-p, --port <port:number>", "", { default: 4646 })
.option(
"-o, --out <out:string>",
"Directory at which disk-related operations (such as storing devnet logs and caching metadata) can occur",
{ default: "target/capi" },
)
.option("--target <target:string>", "target name in net.ts")
.action(runServe)

const nets = await resolveNets(...args)
export interface RunServeOptions {
nets?: string
port: number
out: string
target?: string
}

async function runServe(
this: { getLiteralArgs(): string[] },
{ nets: netsPath, port, out, target }: RunServeOptions,
) {
const literalArgs = this.getLiteralArgs()
const nets = await resolveNets(netsPath)
const devnetTempDir = await tempDir(out, "devnet")

const href = `http://localhost:${port}/`

const controller = new AbortController()
const { signal } = controller

const dataCache = new FsCache(out, signal)
const tempCache = new InMemoryCache(signal)

const running = await fetch(`${href}capi_cwd`)
.then((r) => r.text())
.then((r) => r === Deno.cwd())
.catch(() => false)

if (!running) {
const devnetsHandler = createDevnetsHandler(devnetTempDir, nets, signal)
const codegenHandler = createCodegenHandler(dataCache, tempCache)
Expand All @@ -52,7 +60,7 @@ export default async function(...args: string[]) {
}
return await codegenHandler(request)
}))
await serve(handler, {
await httpServe(handler, {
hostname: "::",
port: +port,
signal,
Expand All @@ -70,7 +78,7 @@ export default async function(...args: string[]) {
}

async function onReady() {
const [bin, ...args] = cmd
const [bin, ...args] = literalArgs
if (bin) {
const command = new Deno.Command(bin, {
args,
Expand Down
60 changes: 36 additions & 24 deletions cli/sync.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,53 @@
import * as flags from "../deps/std/flags.ts"
import { Command, EnumType } from "../deps/cliffy.ts"
import { blue, gray } from "../deps/std/fmt/colors.ts"
import { assertEquals } from "../deps/std/testing/asserts.ts"
import { syncNets } from "../server/mod.ts"
import { normalizePackageName } from "../util/mod.ts"
import { tempDir } from "../util/tempDir.ts"
import { resolveNets } from "./resolveNets.ts"

export default async function(...args: string[]) {
const {
"import-map": importMapFile,
"package-json": packageJsonFile,
check,
out,
server,
} = flags.parse(args, {
string: ["config", "import-map", "out", "package-json", "server"],
boolean: ["check"],
default: {
server: "https://capi.dev/",
out: "target/capi",
},
export const sync = new Command()
.type("runtime", new EnumType(["deno", "node"]))
.description("Sync net specs and update your manifest")
.arguments("<runtime:runtime>")
.option("-n, --nets <nets:file>", "nets.ts file path", { default: "./nets.ts" })
.option("--check", "ensures that metadata and codegen are in sync")
.option("-o, --out <out:string>", "Metadata and codegen output directory", {
default: "target/capi",
})
.option("-s, --server <server:string>", "", { default: "https://capi.dev/" })
.option(
"--runtime-config <runtimeConfig:string>",
"the import_map.json or package.json file path",
)
.action(runSync)

const netSpecs = await resolveNets(...args)
export interface RunSyncOptions {
nets: string
check?: true
out: string
server: string
runtimeConfig?: string
}

async function runSync({
nets: netsFile,
check,
out,
server,
runtimeConfig,
}: RunSyncOptions, runtime: string) {
const netSpecs = await resolveNets(netsFile)
const devnetTempDir = await tempDir(out, "devnet")

const baseUrl = await syncNets(server, devnetTempDir, netSpecs)

if (importMapFile) {
syncFile(importMapFile, (importMap) => {
if (runtime === "deno") {
runtimeConfig ??= "import_map.json"
syncFile(runtimeConfig, (importMap) => {
importMap.imports["@capi/"] = baseUrl
})
}

if (packageJsonFile) {
syncFile(packageJsonFile, (packageJson) => {
} else if (runtime === "node") {
runtimeConfig ??= "package.json"
syncFile(runtimeConfig, (packageJson) => {
const addedPackages = new Set()
for (const rawName of Object.keys(netSpecs ?? {})) {
const name = normalizePackageName(rawName)
Expand Down
2 changes: 1 addition & 1 deletion deno.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"cache": "deno task capi serve -- deno cache -r=http://localhost:4646/",
"check": "deno task capi serve -- deno cache --check",
"star": "deno task run _tasks/star.ts && deno task check target/star.ts",
"sync": "mkdir -p target && deno task capi serve -- deno task capi sync --server http://localhost:4646/ --import-map import_map.json",
"sync": "mkdir -p target && deno task capi serve -- deno task capi sync deno --server http://localhost:4646/",
"run": "deno task capi serve -- deno run -A -r=http://localhost:4646/"
}
}
1 change: 1 addition & 0 deletions deps/cliffy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "https://deno.land/x/[email protected]/command/mod.ts"
21 changes: 11 additions & 10 deletions main.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import "./deps/shims/register.ts"
import { Command } from "./deps/cliffy.ts"

import bin from "./cli/bin.ts"
import serve from "./cli/serve.ts"
import sync from "./cli/sync.ts"
import { bin } from "./cli/bin.ts"
import { serve } from "./cli/serve.ts"
import { sync } from "./cli/sync.ts"

const commands: Record<string, (...args: string[]) => void> = { bin, serve, sync }

if (Deno.args[0]! in commands) {
commands[Deno.args[0]!]!(...Deno.args.slice(1))
} else {
throw new Error("Unrecognized command")
}
await new Command()
.name("capi")
.description("Capi is a framework for crafting interactions with Substrate chains")
.command("bin", bin)
.command("sync", sync)
.command("serve", serve)
.parse(Deno.args)

0 comments on commit c673026

Please sign in to comment.