diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b428a0..b1e638f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,12 +6,14 @@ ## Fixes and improvements - -# NEXT: -## Breaking changes -- +# 4.4.0 ## Added features +- Basic local `sim`-ulator +- `delete` service groups - Can crete service user with `role` ## Fixes and improvements +- `deploy` asks for a message if there are changed files and colors the deployment output to make it easier to read +- hide 'pending' users from main view - `build` skips library install if up-to-date # 4.2.0 diff --git a/Execute.js b/Execute.js new file mode 100644 index 0000000..7aeae3f --- /dev/null +++ b/Execute.js @@ -0,0 +1,400 @@ +import { detectProjectType, RUN_COMMAND } from "@merrymake/detect-project-type"; +import { spawn, } from "child_process"; +import cookieParser from "cookie-parser"; +import express from "express"; +import fs from "fs"; +import net from "net"; +import { GRAY, NORMAL_COLOR, RED, WHITE, YELLOW } from "./prompt.js"; +import { addToExecuteQueue, all, finish, generateString, printWithPrefix, } from "./utils.js"; +let spacerTimer; +function timedOutput(str, prefix) { + if (spacerTimer !== undefined) + clearTimeout(spacerTimer); + printWithPrefix(str, prefix); + spacerTimer = setTimeout(() => console.log(""), 10000); +} +function prep(folder, runCommand, env, displayFolder) { + const runCmd = runCommand(folder); + const [cmd, ...args] = runCmd.split(" "); + const options = { + cwd: folder, + env, + shell: "sh", + }; + const p = spawn(cmd, args, options); + p.stdout.on("data", (data) => { + timedOutput(`${data.toString()}`, `${GRAY}${displayFolder}: ${NORMAL_COLOR}`); + }); + p.stderr.on("data", (data) => { + timedOutput(`${data.toString()}${NORMAL_COLOR}`, `${GRAY}${displayFolder}: ${RED}`); + }); + return p; +} +function run(p, action, envelope, payload) { + return new Promise((resolve) => { + p.stdin.write(pack(Buffer.from(action), Buffer.from(JSON.stringify(envelope)), payload)); + p.stdin.end(); + p.on("close", () => { + resolve(); + }); + }); +} +function numberToBuffer(n) { + return Buffer.from([n >> 16, n >> 8, n >> 0]); +} +function bufferToNumber(buffers) { + return (buffers.at(0) << 16) | (buffers.at(1) << 8) | buffers.at(2); +} +function pack(...buffers) { + const result = []; + buffers.forEach((x) => result.push(numberToBuffer(x.length), x)); + return Buffer.concat(result); +} +function execute(handle, pathToRoot, group, repo, action, envelope, payload) { + const server = net.createServer((socket) => { + socket.on("end", () => { + socket.end(); + }); + socket.on("close", () => { + server.close(); + }); + let missing = 0; + const parsed = []; + let buffer = Buffer.alloc(0); + socket.on("data", (buf) => { + buffer = Buffer.concat([buffer, buf]); + while (true) { + if (missing === 0) { + if (buffer.length < 3) { + return; + } + missing = bufferToNumber(buffer); + buffer = buffer.subarray(3); + } + if (missing === 0) { + parsed.push(Buffer.alloc(0)); + if (parsed.length === 2) { + const [event, payload] = parsed.splice(0, 2); + handle(event.toString(), payload); + } + continue; + } + if (buffer.length >= missing) { + parsed.push(buffer.subarray(0, missing)); + buffer = buffer.subarray(missing); + missing = 0; + if (parsed.length === 2) { + const [event, payload] = parsed.splice(0, 2); + handle(event.toString(), payload); + } + } + else { + return; + } + } + }); + }); + server.listen(() => { }); + const env = process.env || {}; + env.RAPIDS = `localhost:${server.address().port}`; + if (fs.existsSync(pathToRoot + "/" + group + "/env.kv")) { + fs.readFileSync(pathToRoot + "/" + group + "/env.kv", "utf-8") + .split(/\r?\n/) + .forEach((x) => { + if (!x.includes("=")) + return; + const b = x.split("="); + env[b[0]] = b[1]; + }); + } + const folder = `${pathToRoot}/${group}/${repo}`; + const type = detectProjectType(folder); + const runCommand = RUN_COMMAND[type]; + const p = prep(folder, runCommand, env, `${envelope.traceId}:${group}/${repo}`); + return run(p, action, envelope, payload); +} +function parseMerrymakeJson(folder, event) { + if (!fs.existsSync(`${folder}/merrymake.json`)) + throw "Missing merrymake.json"; + const config = JSON.parse(fs.readFileSync(`${folder}/merrymake.json`, "utf-8")); + return Object.keys(config.hooks) + .filter((x) => x.endsWith(`/${event}`)) + .map((x) => { + const hook = config.hooks[x]; + const action = typeof hook === "object" ? hook.action : hook; + return [x.split("/")[0], action]; + }); +} +function processFolders(pathToRoot, event) { + const rivers = {}; + fs.readdirSync(pathToRoot) + .filter((x) => !x.startsWith("(deleted) ") && !x.endsWith(".DS_Store")) + .forEach((group) => { + fs.readdirSync(`${pathToRoot}/${group}`) + .filter((x) => !x.startsWith("(deleted) ") && !x.endsWith(".DS_Store")) + .forEach((repo) => { + if (!fs.existsSync(`${pathToRoot}/${group}/${repo}/merrymake.json`)) + return; + parseMerrymakeJson(`${pathToRoot}/${group}/${repo}`, event).forEach(([river, action]) => { + if (rivers[river] === undefined) + rivers[river] = []; + rivers[river].push({ group, repo, action }); + }); + }); + }); + return rivers; +} +var Mode; +(function (Mode) { + Mode[Mode["RECORDING"] = 0] = "RECORDING"; + Mode[Mode["PLAYING"] = 1] = "PLAYING"; + Mode[Mode["NORMAL"] = 2] = "NORMAL"; +})(Mode || (Mode = {})); +const mode = Mode.NORMAL; +const USUAL_HEADERS = new Set([ + "accept", + "accept-language", + "accept-patch", + "accept-ranges", + "access-control-allow-credentials", + "access-control-allow-headers", + "access-control-allow-methods", + "access-control-allow-origin", + "access-control-expose-headers", + "access-control-max-age", + "access-control-request-headers", + "access-control-request-method", + "age", + "allow", + "alt-svc", + "authorization", + "cache-control", + "connection", + "content-disposition", + "content-encoding", + "content-language", + "content-length", + "content-location", + "content-range", + "content-type", + "cookie", + "date", + "etag", + "expect", + "expires", + "forwarded", + "from", + "host", + "if-match", + "if-modified-since", + "if-none-match", + "if-unmodified-since", + "last-modified", + "location", + "origin", + "pragma", + "proxy-authenticate", + "proxy-authorization", + "public-key-pins", + "range", + "referer", + "retry-after", + "sec-websocket-accept", + "sec-websocket-extensions", + "sec-websocket-key", + "sec-websocket-protocol", + "sec-websocket-version", + "set-cookie", + "strict-transport-security", + "tk", + "trailer", + "transfer-encoding", + "upgrade", + "user-agent", + "vary", + "via", + "warning", + "www-authenticate", + "x-forwarded-for", + "x-forwarded-host", + "x-forwarded-port", + "x-forwarded-proto", + "x-forwarded-scheme", + "x-real-ip", + "x-request-id", + "x-scheme", + "merrymake-key", +]); +function reply(resp, payload, contentType, statusCode, headers) { + Object.keys(headers).forEach((k) => { + const v = headers[k]; + v !== undefined && resp.setHeader(k, v); + }); + if (contentType !== undefined) + resp.contentType(contentType); + resp.status(statusCode).send(payload); +} +class Simulator { + pathToRoot; + pendingReplies = {}; + channels = {}; + constructor(pathToRoot) { + this.pathToRoot = pathToRoot; + } + start() { + return new Promise((resolve) => { + const app = express(); + const withSession = cookieParser(); + app.get("/:event", withSession, (req, res) => { + let event = req.params.event; + let payload = Buffer.from(JSON.stringify(req.query)); + this.handleEndpoint(req, res, event, payload); + }); + app.all("/:event", withSession, (req, res) => { + let event = req.params.event; + let payload = !Buffer.isBuffer(req.body) + ? typeof req.body === "object" + ? Buffer.from(JSON.stringify(req.body)) + : Buffer.from(req.body) + : req.body; + this.handleEndpoint(req, res, event, payload); + }); + app.all("/", (req, res) => { + res.send("Simulator ready."); + }); + const port = 3000; + app.listen(port, () => { + console.log(` +${WHITE}███${GRAY}╗ ${WHITE}███${GRAY}╗${WHITE}███████${GRAY}╗${WHITE}██████${GRAY}╗ ${WHITE}██████${GRAY}╗ ${WHITE}██${GRAY}╗ ${WHITE}██${GRAY}╗${WHITE}███${GRAY}╗ ${WHITE}███${GRAY}╗ ${WHITE}█████${GRAY}╗ ${WHITE}██${GRAY}╗ ${WHITE}██${GRAY}╗${WHITE}███████${GRAY}╗ +${WHITE}████${GRAY}╗ ${WHITE}████${GRAY}║${WHITE}██${GRAY}╔════╝${WHITE}██${GRAY}╔══${WHITE}██${GRAY}╗${WHITE}██${GRAY}╔══${WHITE}██${GRAY}╗╚${WHITE}██${GRAY}╗ ${WHITE}██${GRAY}╔╝${WHITE}████${GRAY}╗ ${WHITE}████${GRAY}║${WHITE}██${GRAY}╔══${WHITE}██${GRAY}╗${WHITE}██${GRAY}║ ${WHITE}██${GRAY}╔╝${WHITE}██${GRAY}╔════╝ +${WHITE}██${GRAY}╔${WHITE}████${GRAY}╔${WHITE}██${GRAY}║${WHITE}█████${GRAY}╗ ${WHITE}██████${GRAY}╔╝${WHITE}██████${GRAY}╔╝ ╚${WHITE}████${GRAY}╔╝ ${WHITE}██${GRAY}╔${WHITE}████${GRAY}╔${WHITE}██${GRAY}║${WHITE}███████${GRAY}║${WHITE}█████${GRAY}╔╝ ${WHITE}█████${GRAY}╗ +${WHITE}██${GRAY}║╚${WHITE}██${GRAY}╔╝${WHITE}██${GRAY}║${WHITE}██${GRAY}╔══╝ ${WHITE}██${GRAY}╔══${WHITE}██${GRAY}╗${WHITE}██${GRAY}╔══${WHITE}██${GRAY}╗ ╚${WHITE}██${GRAY}╔╝ ${WHITE}██${GRAY}║╚${WHITE}██${GRAY}╔╝${WHITE}██${GRAY}║${WHITE}██${GRAY}╔══${WHITE}██${GRAY}║${WHITE}██${GRAY}╔═${WHITE}██${GRAY}╗ ${WHITE}██${GRAY}╔══╝ +${WHITE}██${GRAY}║ ╚═╝ ${WHITE}██${GRAY}║${WHITE}███████${GRAY}╗${WHITE}██${GRAY}║ ${WHITE}██${GRAY}║${WHITE}██${GRAY}║ ${WHITE}██${GRAY}║ ${WHITE}██${GRAY}║ ${WHITE}██${GRAY}║ ╚═╝ ${WHITE}██${GRAY}║${WHITE}██${GRAY}║ ${WHITE}██${GRAY}║${WHITE}██${GRAY}║ ${WHITE}██${GRAY}╗${WHITE}███████${GRAY}╗ +${GRAY}╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ + +Local simulator running on http://localhost:${port}/ +Use ctrl+c to exit +${NORMAL_COLOR}`); + }); + }); + } + reply(traceId, body) { + const rs = this.pendingReplies[traceId]; + if (rs !== undefined) { + delete this.pendingReplies[traceId]; + reply(rs.resp, body.content, body["content-type"], body["status-code"] || 200, body.headers || {}); + } + } + processEvent(evnt, payload, envelope) { + const [spawn, event] = evnt[0] === "<" ? [true, evnt.substring(1)] : [false, evnt]; + // TODO spawn + const traceId = envelope.traceId; + if (event === "$reply") { + const body = JSON.parse(payload.toString()); + this.reply(traceId, body); + } + else if (event === "$join") { + const to = payload.toString(); + const rs = this.pendingReplies[traceId]; + if (rs !== undefined) { + if (this.channels[to] === undefined) + this.channels[to] = new Set(); + this.channels[to].add(rs.resp); + rs.channels.add(to); + } + } + else if (event === "$broadcast") { + const p = JSON.parse(payload.toString()); + const cs = this.channels[p.to] || []; + cs.forEach((c) => { + c.write(`event: ${p.event}\n`); + p.payload.split("\n").forEach((x) => c.write(`data: ${x}\n`)); + c.write(`\n`); + }); + } + const riverConfigs = processFolders(this.pathToRoot, event); + const rivers = Object.keys(riverConfigs); + if (rivers.length === 0) { + timedOutput(`${YELLOW}Warning: No hooks for '${event}'${NORMAL_COLOR}`, `${GRAY}${traceId}: `); + } + rivers.forEach((r) => { + const actions = riverConfigs[r]; + const action = (() => { + if (actions.length === 1) { + return actions[0]; + // } else if (mode === Mode.RECORDING) { + // TODO Ask user which to choose + // } else if (mode === Mode.PLAYING) { + // TODO Choose same as recording + } + else { + return actions[~~(actions.length * Math.random())]; + } + })(); + const subEventCount = {}; + execute((event, payload) => { + this.processEvent(event, payload, { + ...envelope, + messageId: envelope.messageId + + event + + (subEventCount[event] = (subEventCount[event] || -1) + 1), + }); + }, this.pathToRoot, action.group, action.repo, action.action, envelope, payload); + }); + } + async handleEndpoint(req, resp, event, payload) { + resp.set("Access-Control-Allow-Origin", "*"); + const headers = (() => { + const filtered = Object.keys(req.headersDistinct).filter((k) => !USUAL_HEADERS.has(k)); + if (filtered.length === 0) + return undefined; + const result = {}; + filtered.forEach((k) => (result[k] = req.headersDistinct[k][0])); + return result; + })(); + let sessionId = req.cookies.sessionId; + if (!sessionId) { + sessionId = "s" + Math.random(); + resp.cookie("sessionId", sessionId); + } + const api_json = JSON.parse(fs.readFileSync(`${this.pathToRoot}/event-catalogue/api.json`, "utf-8")); + const conf = api_json[event]; + const traceId = generateString(3, all); + this.pendingReplies[traceId] = { + resp, + channels: new Set(), + }; + if (conf !== undefined && conf.waitFor !== undefined) { + setTimeout(() => { + timedOutput(`Reply timeout for trace${NORMAL_COLOR}`, `${GRAY}${traceId}: `); + }, conf.waitFor); + } + if (conf !== undefined && conf.streaming === true) { + req.on("close", () => { + const rep = this.pendingReplies[traceId]; + rep.channels.forEach((c) => { + this.channels[c].delete(rep.resp); + if (this.channels[c].size === 0) { + delete this.channels[c]; + } + }); + }); + resp.set("Content-Type", "text/event-stream"); + resp.set("Cache-Control", "no-cache"); + resp.set("Connection", "keep-alive"); + resp.flushHeaders(); + } + const envelope = { + messageId: "m" + Math.random(), + traceId, + sessionId, + headers, + }; + this.processEvent(event, payload, envelope); + } +} +export function do_startSimulator(pathToRoot) { + const sim = new Simulator(pathToRoot); + addToExecuteQueue(() => sim.start()); + return finish(); +} diff --git a/Execute.ts b/Execute.ts new file mode 100644 index 0000000..f5a18fc --- /dev/null +++ b/Execute.ts @@ -0,0 +1,513 @@ +import { detectProjectType, RUN_COMMAND } from "@merrymake/detect-project-type"; +import { + ChildProcessWithoutNullStreams, + ExecOptions, + spawn, +} from "child_process"; +import cookieParser from "cookie-parser"; +import express, { Request, RequestHandler, Response } from "express"; +import fs from "fs"; +import net from "net"; +import { GRAY, NORMAL_COLOR, RED, WHITE, YELLOW } from "./prompt.js"; +import { + addToExecuteQueue, + all, + finish, + generateString, + printWithPrefix, +} from "./utils.js"; + +interface Envelope { + messageId: string; + traceId: string; + sessionId?: string; + headers?: { [key: string]: string }; +} +interface MerrymakeJson { + hooks: { [key: string]: string | { action: string; timeout?: number } }; +} +interface ApiJson { + [key: string]: { waitFor?: number; streaming?: boolean }; +} +interface PendingReplies { + [traceId: string]: { resp: Response; channels: Set }; +} +interface Channels { + [channel: string]: Set; +} +interface ReplyBody { + "status-code"?: number; + headers?: { [key: string]: string }; + content: Buffer; + "content-type"?: string; +} + +let spacerTimer: undefined | NodeJS.Timeout; +function timedOutput(str: string, prefix?: string) { + if (spacerTimer !== undefined) clearTimeout(spacerTimer); + printWithPrefix(str, prefix); + spacerTimer = setTimeout(() => console.log(""), 10000); +} + +function prep( + folder: string, + runCommand: (folder: string) => string, + env: NodeJS.ProcessEnv, + displayFolder: string +) { + const runCmd = runCommand(folder); + const [cmd, ...args] = runCmd.split(" "); + const options: ExecOptions = { + cwd: folder, + env, + shell: "sh", + }; + const p = spawn(cmd, args, options); + p.stdout.on("data", (data: Buffer) => { + timedOutput( + `${data.toString()}`, + `${GRAY}${displayFolder}: ${NORMAL_COLOR}` + ); + }); + p.stderr.on("data", (data: Buffer) => { + timedOutput( + `${data.toString()}${NORMAL_COLOR}`, + `${GRAY}${displayFolder}: ${RED}` + ); + }); + return p; +} + +function run( + p: ChildProcessWithoutNullStreams, + action: string, + envelope: Envelope, + payload: Buffer +) { + return new Promise((resolve) => { + p.stdin.write( + pack(Buffer.from(action), Buffer.from(JSON.stringify(envelope)), payload) + ); + p.stdin.end(); + p.on("close", () => { + resolve(); + }); + }); +} + +function numberToBuffer(n: number) { + return Buffer.from([n >> 16, n >> 8, n >> 0]); +} +function bufferToNumber(buffers: Buffer) { + return (buffers.at(0)! << 16) | (buffers.at(1)! << 8) | buffers.at(2)!; +} +function pack(...buffers: Buffer[]) { + const result: Buffer[] = []; + buffers.forEach((x) => result.push(numberToBuffer(x.length), x)); + return Buffer.concat(result); +} + +function execute( + handle: (event: string, payload: Buffer) => void, + pathToRoot: string, + group: string, + repo: string, + action: string, + envelope: Envelope, + payload: Buffer +) { + const server = net.createServer((socket: net.Socket) => { + socket.on("end", () => { + socket.end(); + }); + socket.on("close", () => { + server.close(); + }); + let missing = 0; + const parsed: Buffer[] = []; + let buffer = Buffer.alloc(0); + socket.on("data", (buf: Buffer) => { + buffer = Buffer.concat([buffer, buf]); + while (true) { + if (missing === 0) { + if (buffer.length < 3) { + return; + } + missing = bufferToNumber(buffer); + buffer = buffer.subarray(3); + } + if (missing === 0) { + parsed.push(Buffer.alloc(0)); + if (parsed.length === 2) { + const [event, payload] = parsed.splice(0, 2); + handle(event.toString(), payload); + } + continue; + } + if (buffer.length >= missing) { + parsed.push(buffer.subarray(0, missing)); + buffer = buffer.subarray(missing); + missing = 0; + if (parsed.length === 2) { + const [event, payload] = parsed.splice(0, 2); + handle(event.toString(), payload); + } + } else { + return; + } + } + }); + }); + server.listen(() => {}); + const env = process.env || {}; + env.RAPIDS = `localhost:${(server.address() as net.AddressInfo).port}`; + if (fs.existsSync(pathToRoot + "/" + group + "/env.kv")) { + fs.readFileSync(pathToRoot + "/" + group + "/env.kv", "utf-8") + .split(/\r?\n/) + .forEach((x) => { + if (!x.includes("=")) return; + const b = x.split("="); + env[b[0]] = b[1]; + }); + } + const folder = `${pathToRoot}/${group}/${repo}`; + const type = detectProjectType(folder); + const runCommand = RUN_COMMAND[type]; + const p = prep( + folder, + runCommand, + env, + `${envelope.traceId}:${group}/${repo}` + ); + return run(p, action, envelope, payload); +} + +function parseMerrymakeJson(folder: string, event: string) { + if (!fs.existsSync(`${folder}/merrymake.json`)) + throw "Missing merrymake.json"; + const config: MerrymakeJson = JSON.parse( + fs.readFileSync(`${folder}/merrymake.json`, "utf-8") + ); + return Object.keys(config.hooks) + .filter((x) => x.endsWith(`/${event}`)) + .map((x) => { + const hook = config.hooks[x]; + const action = typeof hook === "object" ? hook.action : hook; + return [x.split("/")[0], action]; + }); +} + +function processFolders(pathToRoot: string, event: string) { + const rivers: { + [river: string]: { group: string; repo: string; action: string }[]; + } = {}; + fs.readdirSync(pathToRoot) + .filter((x) => !x.startsWith("(deleted) ") && !x.endsWith(".DS_Store")) + .forEach((group) => { + fs.readdirSync(`${pathToRoot}/${group}`) + .filter((x) => !x.startsWith("(deleted) ") && !x.endsWith(".DS_Store")) + .forEach((repo) => { + if (!fs.existsSync(`${pathToRoot}/${group}/${repo}/merrymake.json`)) + return; + parseMerrymakeJson(`${pathToRoot}/${group}/${repo}`, event).forEach( + ([river, action]) => { + if (rivers[river] === undefined) rivers[river] = []; + rivers[river].push({ group, repo, action }); + } + ); + }); + }); + return rivers; +} + +enum Mode { + RECORDING, + PLAYING, + NORMAL, +} +const mode: Mode = Mode.NORMAL; + +const USUAL_HEADERS = new Set([ + "accept", + "accept-language", + "accept-patch", + "accept-ranges", + "access-control-allow-credentials", + "access-control-allow-headers", + "access-control-allow-methods", + "access-control-allow-origin", + "access-control-expose-headers", + "access-control-max-age", + "access-control-request-headers", + "access-control-request-method", + "age", + "allow", + "alt-svc", + "authorization", + "cache-control", + "connection", + "content-disposition", + "content-encoding", + "content-language", + "content-length", + "content-location", + "content-range", + "content-type", + "cookie", + "date", + "etag", + "expect", + "expires", + "forwarded", + "from", + "host", + "if-match", + "if-modified-since", + "if-none-match", + "if-unmodified-since", + "last-modified", + "location", + "origin", + "pragma", + "proxy-authenticate", + "proxy-authorization", + "public-key-pins", + "range", + "referer", + "retry-after", + "sec-websocket-accept", + "sec-websocket-extensions", + "sec-websocket-key", + "sec-websocket-protocol", + "sec-websocket-version", + "set-cookie", + "strict-transport-security", + "tk", + "trailer", + "transfer-encoding", + "upgrade", + "user-agent", + "vary", + "via", + "warning", + "www-authenticate", + "x-forwarded-for", + "x-forwarded-host", + "x-forwarded-port", + "x-forwarded-proto", + "x-forwarded-scheme", + "x-real-ip", + "x-request-id", + "x-scheme", + "merrymake-key", +]); + +function reply( + resp: Response, + payload: Buffer, + contentType: string | undefined, + statusCode: number, + headers: { [key: string]: string } +) { + Object.keys(headers).forEach((k) => { + const v = headers[k]; + v !== undefined && resp.setHeader(k, v); + }); + if (contentType !== undefined) resp.contentType(contentType); + resp.status(statusCode).send(payload); +} + +class Simulator { + private pendingReplies: PendingReplies = {}; + private channels: Channels = {}; + constructor(private pathToRoot: string) {} + start() { + return new Promise((resolve) => { + const app = express(); + const withSession: RequestHandler<{ event: string }> = cookieParser(); + app.get("/:event", withSession, (req, res) => { + let event = req.params.event; + let payload: Buffer = Buffer.from(JSON.stringify(req.query)); + this.handleEndpoint(req, res, event, payload); + }); + app.all("/:event", withSession, (req, res) => { + let event = req.params.event; + let payload = !Buffer.isBuffer(req.body) + ? typeof req.body === "object" + ? Buffer.from(JSON.stringify(req.body)) + : Buffer.from(req.body) + : req.body; + this.handleEndpoint(req, res, event, payload); + }); + app.all("/", (req, res) => { + res.send("Simulator ready."); + }); + const port = 3000; + app.listen(port, () => { + console.log(` +${WHITE}███${GRAY}╗ ${WHITE}███${GRAY}╗${WHITE}███████${GRAY}╗${WHITE}██████${GRAY}╗ ${WHITE}██████${GRAY}╗ ${WHITE}██${GRAY}╗ ${WHITE}██${GRAY}╗${WHITE}███${GRAY}╗ ${WHITE}███${GRAY}╗ ${WHITE}█████${GRAY}╗ ${WHITE}██${GRAY}╗ ${WHITE}██${GRAY}╗${WHITE}███████${GRAY}╗ +${WHITE}████${GRAY}╗ ${WHITE}████${GRAY}║${WHITE}██${GRAY}╔════╝${WHITE}██${GRAY}╔══${WHITE}██${GRAY}╗${WHITE}██${GRAY}╔══${WHITE}██${GRAY}╗╚${WHITE}██${GRAY}╗ ${WHITE}██${GRAY}╔╝${WHITE}████${GRAY}╗ ${WHITE}████${GRAY}║${WHITE}██${GRAY}╔══${WHITE}██${GRAY}╗${WHITE}██${GRAY}║ ${WHITE}██${GRAY}╔╝${WHITE}██${GRAY}╔════╝ +${WHITE}██${GRAY}╔${WHITE}████${GRAY}╔${WHITE}██${GRAY}║${WHITE}█████${GRAY}╗ ${WHITE}██████${GRAY}╔╝${WHITE}██████${GRAY}╔╝ ╚${WHITE}████${GRAY}╔╝ ${WHITE}██${GRAY}╔${WHITE}████${GRAY}╔${WHITE}██${GRAY}║${WHITE}███████${GRAY}║${WHITE}█████${GRAY}╔╝ ${WHITE}█████${GRAY}╗ +${WHITE}██${GRAY}║╚${WHITE}██${GRAY}╔╝${WHITE}██${GRAY}║${WHITE}██${GRAY}╔══╝ ${WHITE}██${GRAY}╔══${WHITE}██${GRAY}╗${WHITE}██${GRAY}╔══${WHITE}██${GRAY}╗ ╚${WHITE}██${GRAY}╔╝ ${WHITE}██${GRAY}║╚${WHITE}██${GRAY}╔╝${WHITE}██${GRAY}║${WHITE}██${GRAY}╔══${WHITE}██${GRAY}║${WHITE}██${GRAY}╔═${WHITE}██${GRAY}╗ ${WHITE}██${GRAY}╔══╝ +${WHITE}██${GRAY}║ ╚═╝ ${WHITE}██${GRAY}║${WHITE}███████${GRAY}╗${WHITE}██${GRAY}║ ${WHITE}██${GRAY}║${WHITE}██${GRAY}║ ${WHITE}██${GRAY}║ ${WHITE}██${GRAY}║ ${WHITE}██${GRAY}║ ╚═╝ ${WHITE}██${GRAY}║${WHITE}██${GRAY}║ ${WHITE}██${GRAY}║${WHITE}██${GRAY}║ ${WHITE}██${GRAY}╗${WHITE}███████${GRAY}╗ +${GRAY}╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ + +Local simulator running on http://localhost:${port}/ +Use ctrl+c to exit +${NORMAL_COLOR}`); + }); + }); + } + + reply(traceId: string, body: ReplyBody) { + const rs = this.pendingReplies[traceId]; + if (rs !== undefined) { + delete this.pendingReplies[traceId]; + reply( + rs.resp, + body.content, + body["content-type"], + body["status-code"] || 200, + body.headers || {} + ); + } + } + + processEvent(evnt: string, payload: Buffer, envelope: Envelope) { + const [spawn, event] = + evnt[0] === "<" ? [true, evnt.substring(1)] : [false, evnt]; + // TODO spawn + const traceId = envelope.traceId; + if (event === "$reply") { + const body: ReplyBody = JSON.parse(payload.toString()); + this.reply(traceId, body); + } else if (event === "$join") { + const to = payload.toString(); + const rs = this.pendingReplies[traceId]; + if (rs !== undefined) { + if (this.channels[to] === undefined) this.channels[to] = new Set(); + this.channels[to].add(rs.resp); + rs.channels.add(to); + } + } else if (event === "$broadcast") { + const p: { event: string; to: string; payload: string } = JSON.parse( + payload.toString() + ); + const cs = this.channels[p.to] || []; + cs.forEach((c) => { + c.write(`event: ${p.event}\n`); + p.payload.split("\n").forEach((x) => c.write(`data: ${x}\n`)); + c.write(`\n`); + }); + } + const riverConfigs = processFolders(this.pathToRoot, event); + const rivers = Object.keys(riverConfigs); + if (rivers.length === 0) { + timedOutput( + `${YELLOW}Warning: No hooks for '${event}'${NORMAL_COLOR}`, + `${GRAY}${traceId}: ` + ); + } + + rivers.forEach((r) => { + const actions = riverConfigs[r]; + const action = (() => { + if (actions.length === 1) { + return actions[0]; + // } else if (mode === Mode.RECORDING) { + // TODO Ask user which to choose + // } else if (mode === Mode.PLAYING) { + // TODO Choose same as recording + } else { + return actions[~~(actions.length * Math.random())]; + } + })(); + const subEventCount: { [event: string]: number } = {}; + execute( + (event, payload) => { + this.processEvent(event, payload, { + ...envelope, + messageId: + envelope.messageId + + event + + (subEventCount[event] = (subEventCount[event] || -1) + 1), + }); + }, + this.pathToRoot, + action.group, + action.repo, + action.action, + envelope, + payload + ); + }); + } + + async handleEndpoint( + req: Request, + resp: Response, + event: string, + payload: Buffer + ) { + resp.set("Access-Control-Allow-Origin", "*"); + const headers = (() => { + const filtered = Object.keys(req.headersDistinct).filter( + (k) => !USUAL_HEADERS.has(k) + ); + if (filtered.length === 0) return undefined; + const result: { [key: string]: string } = {}; + filtered.forEach((k) => (result[k] = req.headersDistinct[k]![0])); + return result; + })(); + let sessionId = req.cookies.sessionId; + if (!sessionId) { + sessionId = "s" + Math.random(); + resp.cookie("sessionId", sessionId); + } + const api_json: ApiJson = JSON.parse( + fs.readFileSync(`${this.pathToRoot}/event-catalogue/api.json`, "utf-8") + ); + const conf = api_json[event]; + const traceId = generateString(3, all); + this.pendingReplies[traceId] = { + resp, + channels: new Set(), + }; + if (conf !== undefined && conf.waitFor !== undefined) { + setTimeout(() => { + timedOutput( + `Reply timeout for trace${NORMAL_COLOR}`, + `${GRAY}${traceId}: ` + ); + }, conf.waitFor); + } + if (conf !== undefined && conf.streaming === true) { + req.on("close", () => { + const rep = this.pendingReplies[traceId]; + rep.channels.forEach((c) => { + this.channels[c].delete(rep.resp); + if (this.channels[c].size === 0) { + delete this.channels[c]; + } + }); + }); + resp.set("Content-Type", "text/event-stream"); + resp.set("Cache-Control", "no-cache"); + resp.set("Connection", "keep-alive"); + resp.flushHeaders(); + } + const envelope: Envelope = { + messageId: "m" + Math.random(), + traceId, + sessionId, + headers, + }; + this.processEvent(event, payload, envelope); + } +} + +export function do_startSimulator(pathToRoot: string) { + const sim = new Simulator(pathToRoot); + addToExecuteQueue(() => sim.start()); + return finish(); +} diff --git a/args.js b/args.js index 23dddff..a237c74 100644 --- a/args.js +++ b/args.js @@ -1,11 +1,7 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.initializeArgs = initializeArgs; -exports.getArgs = getArgs; let args; -function initializeArgs(strs) { +export function initializeArgs(strs) { args = strs; } -function getArgs() { +export function getArgs() { return args; } diff --git a/commands/quickstart.js b/commands/quickstart.js index b384146..ffb1b55 100644 --- a/commands/quickstart.js +++ b/commands/quickstart.js @@ -1,4 +1,4 @@ -"use strict"; +export {}; // import { Path, addToExecuteQueue, getCache } from "../utils"; // import { do_key } from "../newCommands/apikey"; // import { createServiceGroup } from "./group"; diff --git a/config.js b/config.js index 91bc858..fe4d2bf 100644 --- a/config.js +++ b/config.js @@ -1,12 +1,9 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.SPECIAL_FOLDERS = exports.GIT_HOST = exports.SSH_USER = exports.SSH_HOST = exports.HTTP_HOST = exports.RAPIDS_HOST = exports.API_URL = exports.MERRYMAKE_IO = exports.FINGERPRINT = void 0; -exports.FINGERPRINT = `AAAAC3NzaC1lZDI1NTE5AAAAIPLSjAn8YwNclgEEk8fgyNb1pbhn9X7bMKwFUweaoPzn`; -exports.MERRYMAKE_IO = `merrymake.io`; -exports.API_URL = `api.${exports.MERRYMAKE_IO}`; -exports.RAPIDS_HOST = `https://rapids.${exports.MERRYMAKE_IO}`; -exports.HTTP_HOST = `https://${exports.API_URL}`; -exports.SSH_HOST = `${exports.API_URL}`; -exports.SSH_USER = `mist`; -exports.GIT_HOST = `ssh://${exports.SSH_USER}@${exports.API_URL}`; -exports.SPECIAL_FOLDERS = ["event-catalogue", "public"]; +export const FINGERPRINT = `AAAAC3NzaC1lZDI1NTE5AAAAIPLSjAn8YwNclgEEk8fgyNb1pbhn9X7bMKwFUweaoPzn`; +export const MERRYMAKE_IO = `merrymake.io`; +export const API_URL = `api.${MERRYMAKE_IO}`; +export const RAPIDS_HOST = `https://rapids.${MERRYMAKE_IO}`; +export const HTTP_HOST = `https://${API_URL}`; +export const SSH_HOST = `${API_URL}`; +export const SSH_USER = `mist`; +export const GIT_HOST = `ssh://${SSH_USER}@${API_URL}`; +export const SPECIAL_FOLDERS = ["event-catalogue", "public"]; diff --git a/contexts.js b/contexts.js index 0791bdc..322a684 100644 --- a/contexts.js +++ b/contexts.js @@ -1,26 +1,20 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.CONTEXTS = void 0; -const fs_1 = __importDefault(require("fs")); -const path_1 = __importDefault(require("path")); -const prompt_1 = require("./prompt"); -const utils_1 = require("./utils"); +import fs from "fs"; +import path from "path"; +import { GREEN, NORMAL_COLOR } from "./prompt.js"; +import { Path, directoryNames, fetchOrgRaw } from "./utils.js"; function downOrg(cmd) { - const folders = fs_1.default.readdirSync("."); + const folders = fs.readdirSync("."); let org = undefined; for (let i = 0; i < folders.length; i++) { const folder = folders[i]; - if (fs_1.default.existsSync(path_1.default.join(folder, ".merrymake"))) { + if (fs.existsSync(path.join(folder, ".merrymake"))) { org = folder; break; } } let hint = `You can only run '${cmd}' from inside an organization.`; if (org !== undefined) { - hint += `\nUse: '${prompt_1.GREEN}cd ${org}${prompt_1.NORMAL_COLOR}' or one of these:`; + hint += `\nUse: '${GREEN}cd ${org}${NORMAL_COLOR}' or one of these:`; } else { hint += `\nUse one of these:`; @@ -28,12 +22,12 @@ function downOrg(cmd) { return hint; } function upOrg(cmd) { - const struct = (0, utils_1.fetchOrgRaw)(); + const struct = fetchOrgRaw(); if (struct.org === null) return downOrg(cmd); else { let hint = `You can only run '${cmd}' from the organization root folder.`; - hint += `\nUse '${prompt_1.GREEN}cd ${struct.pathToRoot.substring(0, struct.pathToRoot.length - 1)}${prompt_1.NORMAL_COLOR}' or one of these:`; + hint += `\nUse '${GREEN}cd ${struct.pathToRoot.substring(0, struct.pathToRoot.length - 1)}${NORMAL_COLOR}' or one of these:`; return hint; } } @@ -42,14 +36,14 @@ const SERVICE_CONTEXT = (cmd) => { const bfs = ["."]; while (bfs.length !== 0) { const cur = bfs.shift(); - if (fs_1.default.existsSync(path_1.default.join(cur, "merrymake.json"))) { - hint += `\nUse '${prompt_1.GREEN}cd ${cur.replace(/\\/g, "\\\\")}${prompt_1.NORMAL_COLOR}' or one of these:`; + if (fs.existsSync(path.join(cur, "merrymake.json"))) { + hint += `\nUse '${GREEN}cd ${cur.replace(/\\/g, "\\\\")}${NORMAL_COLOR}' or one of these:`; break; } else { try { - if (fs_1.default.lstatSync(cur).isDirectory()) - bfs.push(...fs_1.default.readdirSync(cur).map((x) => path_1.default.join(cur, x))); + if (fs.lstatSync(cur).isDirectory()) + bfs.push(...fs.readdirSync(cur).map((x) => path.join(cur, x))); } catch (e) { } } @@ -58,40 +52,44 @@ const SERVICE_CONTEXT = (cmd) => { }; const NOT_SERVICE_CONTEXT = (cmd) => { let hint = `You cannot run '${cmd}' from inside a service folder.`; - hint += `\nUse '${prompt_1.GREEN}cd ..${prompt_1.NORMAL_COLOR}' or one of these:`; + hint += `\nUse '${GREEN}cd ..${NORMAL_COLOR}' or one of these:`; return hint; }; const SERVICE_GROUP_CONTEXT = (cmd) => { - const struct = (0, utils_1.fetchOrgRaw)(); - const serviceGroups = (0, utils_1.directoryNames)(new utils_1.Path(struct.pathToRoot), [ + const struct = fetchOrgRaw(); + const serviceGroups = directoryNames(new Path(struct.pathToRoot), [ "event-catalogue", "public", ]); let hint = `You can only run '${cmd}' from inside a service group.`; - hint += `\nUse '${prompt_1.GREEN}cd ${path_1.default + hint += `\nUse '${GREEN}cd ${path .join(struct.pathToRoot, serviceGroups[0].name) - .replace(/\\/g, "\\\\")}${prompt_1.NORMAL_COLOR}' or one of these:`; + .replace(/\\/g, "\\\\")}${NORMAL_COLOR}' or one of these:`; return hint; }; const NOT_SERVICE_GROUP_CONTEXT = upOrg; const ORGANIZATION_CONTEXT = downOrg; const NOT_ORGANIZATION_CONTEXT = (cmd) => { - const struct = (0, utils_1.fetchOrgRaw)(); + const struct = fetchOrgRaw(); let hint = `You can only run '${cmd}' from outside an organization.`; - hint += `\nUse '${prompt_1.GREEN}cd ${struct.pathToRoot.replace(/\\/g, "\\\\")}..${prompt_1.NORMAL_COLOR}' or one of these:`; + hint += `\nUse '${GREEN}cd ${struct.pathToRoot.replace(/\\/g, "\\\\")}..${NORMAL_COLOR}' or one of these:`; return hint; }; -exports.CONTEXTS = { - rapids: ORGANIZATION_CONTEXT, - role: ORGANIZATION_CONTEXT, +export const CONTEXTS = { + clone: NOT_ORGANIZATION_CONTEXT, + clean: SERVICE_CONTEXT, + build: SERVICE_CONTEXT, deploy: SERVICE_CONTEXT, - fetch: NOT_SERVICE_CONTEXT, - repo: SERVICE_GROUP_CONTEXT, envvar: SERVICE_GROUP_CONTEXT, + event: ORGANIZATION_CONTEXT, + fetch: NOT_SERVICE_CONTEXT, group: NOT_SERVICE_GROUP_CONTEXT, + hosting: NOT_SERVICE_GROUP_CONTEXT, + join: NOT_ORGANIZATION_CONTEXT, key: ORGANIZATION_CONTEXT, - event: ORGANIZATION_CONTEXT, org: NOT_ORGANIZATION_CONTEXT, - clone: NOT_ORGANIZATION_CONTEXT, - join: NOT_ORGANIZATION_CONTEXT, + rapids: ORGANIZATION_CONTEXT, + rename: NOT_SERVICE_GROUP_CONTEXT, + repo: SERVICE_GROUP_CONTEXT, + role: NOT_SERVICE_GROUP_CONTEXT, }; diff --git a/contexts.ts b/contexts.ts index 214bb7b..4261c7c 100644 --- a/contexts.ts +++ b/contexts.ts @@ -1,7 +1,7 @@ import fs from "fs"; import path from "path"; -import { GREEN, NORMAL_COLOR } from "./prompt"; -import { Path, directoryNames, fetchOrgRaw } from "./utils"; +import { GREEN, NORMAL_COLOR } from "./prompt.js"; +import { Path, directoryNames, fetchOrgRaw } from "./utils.js"; function downOrg(cmd: string) { const folders = fs.readdirSync("."); @@ -84,16 +84,20 @@ const NOT_ORGANIZATION_CONTEXT = (cmd: string) => { }; export const CONTEXTS: { [cmd: string]: (cmd: string) => string } = { - rapids: ORGANIZATION_CONTEXT, - role: ORGANIZATION_CONTEXT, + clone: NOT_ORGANIZATION_CONTEXT, + clean: SERVICE_CONTEXT, + build: SERVICE_CONTEXT, deploy: SERVICE_CONTEXT, - fetch: NOT_SERVICE_CONTEXT, - repo: SERVICE_GROUP_CONTEXT, envvar: SERVICE_GROUP_CONTEXT, + event: ORGANIZATION_CONTEXT, + fetch: NOT_SERVICE_CONTEXT, group: NOT_SERVICE_GROUP_CONTEXT, + hosting: NOT_SERVICE_GROUP_CONTEXT, + join: NOT_ORGANIZATION_CONTEXT, key: ORGANIZATION_CONTEXT, - event: ORGANIZATION_CONTEXT, org: NOT_ORGANIZATION_CONTEXT, - clone: NOT_ORGANIZATION_CONTEXT, - join: NOT_ORGANIZATION_CONTEXT, + rapids: ORGANIZATION_CONTEXT, + rename: NOT_SERVICE_GROUP_CONTEXT, + repo: SERVICE_GROUP_CONTEXT, + role: NOT_SERVICE_GROUP_CONTEXT, }; diff --git a/dist/windows.zip b/dist/windows.zip index 38c3c49..42b97cf 100644 Binary files a/dist/windows.zip and b/dist/windows.zip differ diff --git a/executors.js b/executors.js index 1827f7c..6236848 100644 --- a/executors.js +++ b/executors.js @@ -1,30 +1,9 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.do_build = do_build; -exports.alignRight = alignRight; -exports.alignLeft = alignLeft; -exports.printTableHeader = printTableHeader; -exports.do_spending = do_spending; -exports.do_delete_group = do_delete_group; -exports.do_delete_org = do_delete_org; -const detect_project_type_1 = require("@merrymake/detect-project-type"); -const child_process_1 = require("child_process"); -const fs_1 = __importDefault(require("fs")); -const process_1 = require("process"); -const args_1 = require("./args"); -const utils_1 = require("./utils"); +import { BUILD_SCRIPT_MAKERS, detectProjectType, } from "@merrymake/detect-project-type"; +import { spawn } from "child_process"; +import fs from "fs"; +import { stdout } from "process"; +import { getArgs } from "./args.js"; +import { outputGit, sshReq } from "./utils.js"; function spawnPromise(str) { return new Promise((resolve, reject) => { const [cmd, ...args] = str.split(" "); @@ -32,12 +11,12 @@ function spawnPromise(str) { cwd: ".", shell: "sh", }; - const ls = (0, child_process_1.spawn)(cmd, args, options); + const ls = spawn(cmd, args, options); ls.stdout.on("data", (data) => { - (0, utils_1.output2)(data.toString()); + outputGit(data.toString()); }); ls.stderr.on("data", (data) => { - (0, utils_1.output2)(data.toString()); + outputGit(data.toString()); }); ls.on("close", (code) => { if (code === 0) @@ -47,38 +26,36 @@ function spawnPromise(str) { }); }); } -function do_build() { - return __awaiter(this, void 0, void 0, function* () { - try { - const projectType = (0, detect_project_type_1.detectProjectType)("."); - (0, utils_1.output2)(`Building ${projectType} project...`); - const buildCommands = detect_project_type_1.BUILD_SCRIPT_MAKERS[projectType]("."); - for (let i = 0; i < buildCommands.length; i++) { - const x = buildCommands[i]; - yield spawnPromise(x); - } - } - catch (e) { - throw e; +export async function do_build() { + try { + const projectType = detectProjectType("."); + outputGit(`Building ${projectType} project...`); + const buildCommands = BUILD_SCRIPT_MAKERS[projectType]("."); + for (let i = 0; i < buildCommands.length; i++) { + const x = buildCommands[i]; + await spawnPromise(x); } - }); + } + catch (e) { + throw e; + } } -function alignRight(str, width) { +export function alignRight(str, width) { return str.length > width ? str.substring(0, width - 3) + "..." : str.padStart(width, " "); } -function alignLeft(str, width) { +export function alignLeft(str, width) { return str.length > width ? str.substring(0, width - 3) + "..." : str.padEnd(width, " "); } -function printTableHeader(prefix, widths) { - if ((0, args_1.getArgs)().length > 0) +export function printTableHeader(prefix, widths) { + if (getArgs().length > 0) return ""; - const totalWidth = (typeof process_1.stdout.getWindowSize !== "function" + const totalWidth = (typeof stdout.getWindowSize !== "function" ? 80 - : process_1.stdout.getWindowSize()[0]) - prefix.length; + : stdout.getWindowSize()[0]) - prefix.length; const vals = Object.values(widths); const rest = totalWidth - vals.reduce((acc, x) => acc + Math.max(x, 0)) - @@ -149,111 +126,105 @@ const MONTHS = [ "November", "December", ]; -function do_spending(org) { - return __awaiter(this, void 0, void 0, function* () { - try { - const rows = JSON.parse(yield (0, utils_1.sshReq)(`spending`, `--org`, org)); - let mth = 0; - let grp = ""; - let srv = ""; - rows.forEach((x) => { - if (x.mth === null) - return; - const nmth = +x.mth; - if (mth !== nmth) { - if (mth !== 0) - (0, utils_1.output2)(""); - mth = nmth; - (0, utils_1.output2)(`Month: ${MONTHS[mth - 1]}`); - printTableHeader("", { - Group: 11, - Service: 11, - Hook: 20, - Count: 7, - Time: 7, - "Est. Cost": 9, - }); - } - const group = x.grp === null ? "= Total" : x.grp === grp ? "" : x.grp; - grp = x.grp; - const service = x.grp === null - ? "" - : x.srv === null - ? "= Total" - : x.srv === srv - ? "" - : x.srv; - srv = x.srv; - let count = +x.cnt; - let count_unit = " "; - let count_str = "" + count + " "; +export async function do_spending(org) { + try { + const rows = JSON.parse(await sshReq(`spending`, `--org`, org)); + let mth = 0; + let grp = ""; + let srv = ""; + rows.forEach((x) => { + if (x.mth === null) + return; + const nmth = +x.mth; + if (mth !== nmth) { + if (mth !== 0) + outputGit(""); + mth = nmth; + outputGit(`Month: ${MONTHS[mth - 1]}`); + printTableHeader("", { + Group: 11, + Service: 11, + Hook: 20, + Count: 7, + Time: 7, + "Est. Cost": 9, + }); + } + const group = x.grp === null ? "= Total" : x.grp === grp ? "" : x.grp; + grp = x.grp; + const service = x.grp === null + ? "" + : x.srv === null + ? "= Total" + : x.srv === srv + ? "" + : x.srv; + srv = x.srv; + let count = +x.cnt; + let count_unit = " "; + let count_str = "" + count + " "; + if (count > 1000) { + count /= 1000; + count_unit = "k"; if (count > 1000) { count /= 1000; - count_unit = "k"; + count_unit = "M"; if (count > 1000) { count /= 1000; - count_unit = "M"; - if (count > 1000) { - count /= 1000; - count_unit = "B"; - } + count_unit = "B"; } - count_str = count.toFixed(1); } - let time = x.time_ms; - let time_unit = "ms"; - let time_str = "" + time + " "; - if (time > 1000) { - time /= 1000; - time_unit = "s"; + count_str = count.toFixed(1); + } + let time = x.time_ms; + let time_unit = "ms"; + let time_str = "" + time + " "; + if (time > 1000) { + time /= 1000; + time_unit = "s"; + if (time > 60) { + time /= 60; + time_unit = "m"; if (time > 60) { time /= 60; - time_unit = "m"; - if (time > 60) { - time /= 60; - time_unit = "h"; - if (time > 24) { - time /= 24; - time_unit = "d"; - if (time > 30) { - time /= 30; - time_unit = "M"; - } + time_unit = "h"; + if (time > 24) { + time /= 24; + time_unit = "d"; + if (time > 30) { + time /= 30; + time_unit = "M"; } } } - time_str = time.toFixed(1); } - const hook = x.srv === null ? "" : x.hook === null ? "= Total" : x.hook; - (0, utils_1.output2)(`${alignLeft(group, 11)} │ ${alignLeft(service, 11)} │ ${alignLeft(hook, 20)} │ ${alignRight("" + count_str + " " + count_unit, 7)} │ ${alignRight("" + time_str + " " + time_unit, 7)} │ € ${alignRight(x.cost_eur, 7)}`); - }); - } - catch (e) { - throw e; - } - }); + time_str = time.toFixed(1); + } + const hook = x.srv === null ? "" : x.hook === null ? "= Total" : x.hook; + outputGit(`${alignLeft(group, 11)} │ ${alignLeft(service, 11)} │ ${alignLeft(hook, 20)} │ ${alignRight("" + count_str + " " + count_unit, 7)} │ ${alignRight("" + time_str + " " + time_unit, 7)} │ € ${alignRight(x.cost_eur, 7)}`); + }); + } + catch (e) { + throw e; + } } -function do_delete_group(org, group) { - return __awaiter(this, void 0, void 0, function* () { - try { - (0, utils_1.output2)(yield (0, utils_1.sshReq)(`team`, `--delete`, `--org`, org, group)); - if (fs_1.default.existsSync(group)) - fs_1.default.renameSync(group, `(deleted) ${group}`); - } - catch (e) { - throw e; - } - }); +export async function do_delete_group(org, group) { + try { + outputGit(await sshReq(`team`, `--delete`, `--org`, org, group)); + if (fs.existsSync(group)) + fs.renameSync(group, `(deleted) ${group}`); + } + catch (e) { + throw e; + } } -function do_delete_org(org) { - return __awaiter(this, void 0, void 0, function* () { - try { - (0, utils_1.output2)(yield (0, utils_1.sshReq)(`org`, `--delete`, org)); - if (fs_1.default.existsSync(org)) - fs_1.default.renameSync(org, `(deleted) ${org}`); - } - catch (e) { - throw e; - } - }); +export async function do_delete_org(org) { + try { + outputGit(await sshReq(`org`, `--delete`, org)); + if (fs.existsSync(org)) + fs.renameSync(org, `(deleted) ${org}`); + } + catch (e) { + throw e; + } } diff --git a/executors.ts b/executors.ts index 9af26bc..cf85f23 100644 --- a/executors.ts +++ b/executors.ts @@ -5,9 +5,8 @@ import { import { ExecOptions, spawn } from "child_process"; import fs from "fs"; import { stdout } from "process"; -import { getArgs } from "./args"; -import { GREEN, NORMAL_COLOR, RED, YELLOW } from "./prompt"; -import { fetchOrgRaw, getCache, output2, sshReq, urlReq } from "./utils"; +import { getArgs } from "./args.js"; +import { outputGit, sshReq } from "./utils.js"; function spawnPromise(str: string) { return new Promise((resolve, reject) => { @@ -18,10 +17,10 @@ function spawnPromise(str: string) { }; const ls = spawn(cmd, args, options); ls.stdout.on("data", (data: Buffer | string) => { - output2(data.toString()); + outputGit(data.toString()); }); ls.stderr.on("data", (data: Buffer | string) => { - output2(data.toString()); + outputGit(data.toString()); }); ls.on("close", (code) => { if (code === 0) resolve(); @@ -33,7 +32,7 @@ function spawnPromise(str: string) { export async function do_build() { try { const projectType = detectProjectType("."); - output2(`Building ${projectType} project...`); + outputGit(`Building ${projectType} project...`); const buildCommands = BUILD_SCRIPT_MAKERS[projectType]("."); for (let i = 0; i < buildCommands.length; i++) { const x = buildCommands[i]; @@ -163,9 +162,9 @@ export async function do_spending(org: string) { if (x.mth === null) return; const nmth = +x.mth; if (mth !== nmth) { - if (mth !== 0) output2(""); + if (mth !== 0) outputGit(""); mth = nmth; - output2(`Month: ${MONTHS[mth - 1]}`); + outputGit(`Month: ${MONTHS[mth - 1]}`); printTableHeader("", { Group: 11, Service: 11, @@ -227,7 +226,7 @@ export async function do_spending(org: string) { time_str = time.toFixed(1); } const hook = x.srv === null ? "" : x.hook === null ? "= Total" : x.hook; - output2( + outputGit( `${alignLeft(group, 11)} │ ${alignLeft(service, 11)} │ ${alignLeft( hook, 20 @@ -244,7 +243,7 @@ export async function do_spending(org: string) { export async function do_delete_group(org: string, group: string) { try { - output2(await sshReq(`team`, `--delete`, `--org`, org, group)); + outputGit(await sshReq(`team`, `--delete`, `--org`, org, group)); if (fs.existsSync(group)) fs.renameSync(group, `(deleted) ${group}`); } catch (e) { throw e; @@ -253,7 +252,7 @@ export async function do_delete_group(org: string, group: string) { export async function do_delete_org(org: string) { try { - output2(await sshReq(`org`, `--delete`, org)); + outputGit(await sshReq(`org`, `--delete`, org)); if (fs.existsSync(org)) fs.renameSync(org, `(deleted) ${org}`); } catch (e) { throw e; diff --git a/index.js b/index.js index d35cc29..6c410d3 100644 --- a/index.js +++ b/index.js @@ -1,71 +1,53 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const node_process_1 = require("node:process"); -const args_1 = require("./args"); -const prompt_1 = require("./prompt"); -const utils_1 = require("./utils"); -const register_1 = require("./newCommands/register"); -const newCommands_1 = require("./newCommands"); -// if (!stdin.isTTY || stdin.setRawMode === undefined) { -// console.log( -// "This console does not support TTY, please use 'winpty mm' or the 'mmk'-command instead." -// ); -// process.exit(1); -// } -// TODO make type for command -// if ( -// process.argv[0] -// .substring(process.argv[0].lastIndexOf(path.sep) + 1) -// .startsWith("node") -// ) -process.argv.splice(0, 1); -process.argv.splice(0, 1); +import { stdin } from "node:process"; +import { initializeArgs } from "./args.js"; +import { index } from "./newCommands/index.js"; +import { addKnownHost } from "./newCommands/register.js"; +import { CTRL_C, exit } from "./prompt.js"; +import { abort, checkVersion, package_json, setDryrun } from "./utils.js"; +process.argv.splice(0, 1); // Remove node +process.argv.splice(0, 1); // Remove index +if (process.argv[0].endsWith("version")) { + console.log(package_json.version); + process.exit(0); +} if (process.argv[0] === "dryrun") { - (0, utils_1.setDryrun)(); + setDryrun(); process.argv.splice(0, 1); } -(0, args_1.initializeArgs)(process.argv.flatMap((x) => x.startsWith("-") +initializeArgs(process.argv.flatMap((x) => x.startsWith("-") ? x .substring(1) .split("") .map((x) => `-${x}`) : [x])); -if (node_process_1.stdin.isTTY) { +if (stdin.isTTY) { // without this, we would only get streams once enter is pressed - node_process_1.stdin.setRawMode(true); + stdin.setRawMode(true); // resume stdin in the parent process (node app won't quit all by itself // unless an error or process.exit() happens) - node_process_1.stdin.resume(); + stdin.resume(); // i don't want binary, do you? - node_process_1.stdin.setEncoding("utf8"); + stdin.setEncoding("utf8"); // You can always exit with crtl-c - node_process_1.stdin.on("data", (key) => { + stdin.on("data", (key) => { const k = key.toString(); - if (k === prompt_1.CTRL_C) { - (0, utils_1.abort)(); + if (k === CTRL_C) { + abort(); } }); } // TODO Change join to invite // TODO roles -(() => __awaiter(void 0, void 0, void 0, function* () { - (0, utils_1.checkVersion)(); - const token = yield (0, newCommands_1.index)(); -}))().catch((e) => { - (0, prompt_1.exit)(); - if (("" + e).includes("Permission denied")) { - (0, register_1.addKnownHost)(); +(async () => { + checkVersion(); + const token = await index(); +})().catch((e) => { + exit(); + const eStr = "" + e; + if (eStr.includes("Permission denied")) { + addKnownHost(); console.log("\x1b[31mAn error occurred, please try again. If the problem persists reach out on http://discord.merrymake.eu \x1b[0m", e); } - console.log(`\x1b[31mERROR ${e.toString().trimEnd()}\x1b[0m`); + console.log(`\x1b[31mERROR ${eStr.trimEnd()}\x1b[0m`); process.exit(0); }); diff --git a/index.ts b/index.ts index e8e476b..22c4194 100644 --- a/index.ts +++ b/index.ts @@ -1,26 +1,16 @@ import { stdin } from "node:process"; -import { initializeArgs } from "./args"; -import { CTRL_C, exit } from "./prompt"; -import { abort, checkVersion, setDryrun } from "./utils"; -import { addKnownHost } from "./newCommands/register"; -import { index } from "./newCommands"; +import { initializeArgs } from "./args.js"; +import { index } from "./newCommands/index.js"; +import { addKnownHost } from "./newCommands/register.js"; +import { CTRL_C, exit } from "./prompt.js"; +import { abort, checkVersion, package_json, setDryrun } from "./utils.js"; -// if (!stdin.isTTY || stdin.setRawMode === undefined) { -// console.log( -// "This console does not support TTY, please use 'winpty mm' or the 'mmk'-command instead." -// ); -// process.exit(1); -// } - -// TODO make type for command - -// if ( -// process.argv[0] -// .substring(process.argv[0].lastIndexOf(path.sep) + 1) -// .startsWith("node") -// ) -process.argv.splice(0, 1); -process.argv.splice(0, 1); +process.argv.splice(0, 1); // Remove node +process.argv.splice(0, 1); // Remove index +if (process.argv[0].endsWith("version")) { + console.log(package_json.version); + process.exit(0); +} if (process.argv[0] === "dryrun") { setDryrun(); process.argv.splice(0, 1); @@ -62,13 +52,14 @@ if (stdin.isTTY) { const token: never = await index(); })().catch((e) => { exit(); - if (("" + e).includes("Permission denied")) { + const eStr = "" + e; + if (eStr.includes("Permission denied")) { addKnownHost(); console.log( "\x1b[31mAn error occurred, please try again. If the problem persists reach out on http://discord.merrymake.eu \x1b[0m", e ); } - console.log(`\x1b[31mERROR ${e.toString().trimEnd()}\x1b[0m`); + console.log(`\x1b[31mERROR ${eStr.trimEnd()}\x1b[0m`); process.exit(0); }); diff --git a/mm.js b/mm.js index 19d7995..40cf98e 100644 --- a/mm.js +++ b/mm.js @@ -1,8 +1,6 @@ #!/usr/bin/env node -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); process.env["COMMAND"] = "mm"; -const prompt_1 = require("./prompt"); +import { YELLOW, NORMAL_COLOR } from "./prompt.js"; process.env["UPDATE_MESSAGE"] = `to update run the command: -${prompt_1.YELLOW}npm install --global @merrymake/cli@latest${prompt_1.NORMAL_COLOR}`; -require("./index"); +${YELLOW}npm install --global @merrymake/cli@latest${NORMAL_COLOR}`; +import "./index.js"; diff --git a/mm.ts b/mm.ts index a405814..40cf98e 100644 --- a/mm.ts +++ b/mm.ts @@ -1,6 +1,6 @@ #!/usr/bin/env node process.env["COMMAND"] = "mm"; -import { YELLOW, NORMAL_COLOR } from "./prompt"; +import { YELLOW, NORMAL_COLOR } from "./prompt.js"; process.env["UPDATE_MESSAGE"] = `to update run the command: ${YELLOW}npm install --global @merrymake/cli@latest${NORMAL_COLOR}`; -import "./index"; +import "./index.js"; diff --git a/mmk.js b/mmk.js index 7a9b4fe..ec71a71 100644 --- a/mmk.js +++ b/mmk.js @@ -1,8 +1,6 @@ #!/usr/bin/env winpty node -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); process.env["COMMAND"] = "mmk"; -const prompt_1 = require("./prompt"); +import { YELLOW, NORMAL_COLOR } from "./prompt.js"; process.env["UPDATE_MESSAGE"] = `to update run the command: -${prompt_1.YELLOW}npm install --global @merrymake/cli@latest${prompt_1.NORMAL_COLOR}`; -require("./index"); +${YELLOW}npm install --global @merrymake/cli@latest${NORMAL_COLOR}`; +import "./index.js"; diff --git a/mmk.ts b/mmk.ts index b57bd50..ec71a71 100644 --- a/mmk.ts +++ b/mmk.ts @@ -1,6 +1,6 @@ #!/usr/bin/env winpty node process.env["COMMAND"] = "mmk"; -import { YELLOW, NORMAL_COLOR } from "./prompt"; +import { YELLOW, NORMAL_COLOR } from "./prompt.js"; process.env["UPDATE_MESSAGE"] = `to update run the command: ${YELLOW}npm install --global @merrymake/cli@latest${NORMAL_COLOR}`; -import "./index"; +import "./index.js"; diff --git a/newCommands/apikey.js b/newCommands/apikey.js index 031c362..f2b5369 100644 --- a/newCommands/apikey.js +++ b/newCommands/apikey.js @@ -1,145 +1,116 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.do_key_create = do_key_create; -exports.do_key_modify = do_key_modify; -exports.key_create = key_create; -exports.key = key; -const process_1 = require("process"); -const executors_1 = require("../executors"); -const prompt_1 = require("../prompt"); -const utils_1 = require("../utils"); -function do_key_create(organizationId, description, duration) { - return __awaiter(this, void 0, void 0, function* () { - try { - const cmd = [ - `apikey-create`, - duration, - `--organizationId`, - organizationId.toString(), - ]; - if (description !== "") - cmd.push(`--description`, description); - const reply = yield (0, utils_1.sshReq)(...cmd); - if (reply.length !== 8) - throw reply; - (0, prompt_1.output)(`Created apikey ${description !== "" ? `'${description}'` : ""}: ${prompt_1.YELLOW}${reply}${prompt_1.NORMAL_COLOR}\n`); - const apikeyId = reply; - return apikeyId; - } - catch (e) { - throw e; - } - }); +import { stdout } from "process"; +import { alignLeft, printTableHeader } from "../executors.js"; +import { NORMAL_COLOR, RED, YELLOW, choice, output, shortText, } from "../prompt.js"; +import { finish, outputGit, sshReq } from "../utils.js"; +export async function do_key_create(organizationId, description, duration) { + try { + const cmd = [ + `apikey-create`, + duration, + `--organizationId`, + organizationId.toString(), + ]; + if (description !== "") + cmd.push(`--description`, description); + const reply = await sshReq(...cmd); + if (reply.length !== 8) + throw reply; + output(`Created apikey ${description !== "" ? `'${description}'` : ""}: ${YELLOW}${reply}${NORMAL_COLOR}\n`); + const apikeyId = reply; + return apikeyId; + } + catch (e) { + throw e; + } } -function do_key_modify(apikeyId, description, duration) { - return __awaiter(this, void 0, void 0, function* () { - try { - const cmd = [`apikey-modify`, `--duration`, duration, apikeyId]; - if (description !== "") - cmd.push(`--description`, description); - yield (0, utils_1.sshReq)(...cmd); - (0, utils_1.output2)(`Updated key.`); - } - catch (e) { - throw e; - } - }); +export async function do_key_modify(apikeyId, description, duration) { + try { + const cmd = [`apikey-modify`, `--duration`, duration, apikeyId]; + if (description !== "") + cmd.push(`--description`, description); + await sshReq(...cmd); + outputGit(`Updated key.`); + } + catch (e) { + throw e; + } } -function key_key_name(description, continuation) { - return __awaiter(this, void 0, void 0, function* () { - try { - const duration = yield (0, prompt_1.shortText)("Duration", "How long should the key be active? Ex. 1 hour", "14 days"); - return continuation(description, duration); - } - catch (e) { - throw e; - } - }); +async function key_key_name(description, continuation) { + try { + const duration = await shortText("Duration", "How long should the key be active? Ex. 1 hour", "14 days"); + return continuation(description, duration); + } + catch (e) { + throw e; + } } -function key_key(currentName, continuation) { - return __awaiter(this, void 0, void 0, function* () { - try { - const description = yield (0, prompt_1.shortText)("Apikey display name", "Used to identify this key", currentName); - return key_key_name(description, continuation); - } - catch (e) { - throw e; - } - }); +async function key_key(currentName, continuation) { + try { + const description = await shortText("Apikey display name", "Used to identify this key", currentName); + return key_key_name(description, continuation); + } + catch (e) { + throw e; + } } -function composeAwait(f, g) { - return __awaiter(this, void 0, void 0, function* () { - try { - const b = yield g; - return f(b); - } - catch (e) { - throw e; - } - }); +async function composeAwait(f, g) { + try { + const b = await g; + return f(b); + } + catch (e) { + throw e; + } } -function key_create(organizationId, continuation) { - return __awaiter(this, void 0, void 0, function* () { - try { - const description = yield (0, prompt_1.shortText)("Apikey display name", "Used to identify this key", ""); - return key_key_name(description, (description, duration) => composeAwait(continuation, do_key_create(organizationId, description, duration))); - } - catch (e) { - throw e; - } - }); +export async function key_create(organizationId, continuation) { + try { + const description = await shortText("Apikey display name", "Used to identify this key", ""); + return key_key_name(description, (description, duration) => composeAwait(continuation, do_key_create(organizationId, description, duration))); + } + catch (e) { + throw e; + } } -function key(organizationId) { - return __awaiter(this, void 0, void 0, function* () { - try { - const resp = yield (0, utils_1.sshReq)(`apikey-list`, organizationId.toString()); - const keys = JSON.parse(resp); - const options = keys.map((x) => { - const d = new Date(x.expiresOn); - const ds = d.getTime() < Date.now() - ? `${prompt_1.RED}${d.toLocaleString()}${prompt_1.NORMAL_COLOR}` - : d.toLocaleString(); - const n = x.name || ""; - return { - long: x.id, - text: `${x.id} │ ${(0, executors_1.alignLeft)(n, Math.max((typeof process_1.stdout.getWindowSize !== "function" - ? 80 - : process_1.stdout.getWindowSize()[0]) - - 8 - - 23 - - "─┼──┼─".length - - " ".length, 12))} │ ${ds}`, - action: () => key_key(x.name, (description, duration) => composeAwait(utils_1.finish, do_key_modify(x.id, description, duration))), - }; - }); - options.push({ - long: `new`, - short: `n`, - text: `add a new apikey`, - action: () => key_create(organizationId, utils_1.finish), - }); - let tableHeader = ""; - if (options.length > 1) - tableHeader = - "\n" + - (0, executors_1.printTableHeader)(" ", { - Key: 8, - "Display name": -12, - "Expiry time": 23, - }); - return yield (0, prompt_1.choice)("Which apikey would you like to edit?" + tableHeader, options).then(); - } - catch (e) { - throw e; - } - }); +export async function key(organizationId) { + try { + const resp = await sshReq(`apikey-list`, organizationId.toString()); + const keys = JSON.parse(resp); + const options = keys.map((x) => { + const d = new Date(x.expiresOn); + const ds = d.getTime() < Date.now() + ? `${RED}${d.toLocaleString()}${NORMAL_COLOR}` + : d.toLocaleString(); + const n = x.name || ""; + return { + long: x.id, + text: `${x.id} │ ${alignLeft(n, Math.max((typeof stdout.getWindowSize !== "function" + ? 80 + : stdout.getWindowSize()[0]) - + 8 - + 23 - + "─┼──┼─".length - + " ".length, 12))} │ ${ds}`, + action: () => key_key(x.name, (description, duration) => composeAwait(finish, do_key_modify(x.id, description, duration))), + }; + }); + options.push({ + long: `new`, + short: `n`, + text: `add a new apikey`, + action: () => key_create(organizationId, finish), + }); + let tableHeader = ""; + if (options.length > 1) + tableHeader = + "\n" + + printTableHeader(" ", { + Key: 8, + "Display name": -12, + "Expiry time": 23, + }); + return await choice("Which apikey would you like to edit?" + tableHeader, options).then(); + } + catch (e) { + throw e; + } } diff --git a/newCommands/apikey.ts b/newCommands/apikey.ts index 2799b32..2202f83 100644 --- a/newCommands/apikey.ts +++ b/newCommands/apikey.ts @@ -1,5 +1,5 @@ import { stdout } from "process"; -import { alignLeft, printTableHeader } from "../executors"; +import { alignLeft, printTableHeader } from "../executors.js"; import { NORMAL_COLOR, Option, @@ -8,9 +8,9 @@ import { choice, output, shortText, -} from "../prompt"; -import { OrganizationId } from "../types"; -import { finish, output2, sshReq } from "../utils"; +} from "../prompt.js"; +import { OrganizationId } from "../types.js"; +import { finish, outputGit, sshReq } from "../utils.js"; export async function do_key_create( organizationId: OrganizationId, @@ -48,7 +48,7 @@ export async function do_key_modify( const cmd = [`apikey-modify`, `--duration`, duration, apikeyId]; if (description !== "") cmd.push(`--description`, description); await sshReq(...cmd); - output2(`Updated key.`); + outputGit(`Updated key.`); } catch (e) { throw e; } diff --git a/newCommands/build.js b/newCommands/build.js index 20b5197..dda7aaf 100644 --- a/newCommands/build.js +++ b/newCommands/build.js @@ -1,35 +1,20 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.do_build = do_build; -exports.build = build; -const detect_project_type_1 = require("@merrymake/detect-project-type"); -const utils_1 = require("../utils"); -function do_build() { - return __awaiter(this, void 0, void 0, function* () { - try { - const projectType = (0, detect_project_type_1.detectProjectType)("."); - (0, utils_1.output2)(`Building ${projectType} project...`); - const buildCommands = detect_project_type_1.BUILD_SCRIPT_MAKERS[projectType]("."); - for (let i = 0; i < buildCommands.length; i++) { - const x = buildCommands[i]; - yield (0, utils_1.spawnPromise)(x); - } +import { BUILD_SCRIPT_MAKERS, detectProjectType, } from "@merrymake/detect-project-type"; +import { addToExecuteQueue, finish, outputGit, spawnPromise, } from "../utils.js"; +export async function do_build() { + try { + const projectType = detectProjectType("."); + outputGit(`Building ${projectType} project...`); + const buildCommands = BUILD_SCRIPT_MAKERS[projectType]("."); + for (let i = 0; i < buildCommands.length; i++) { + const x = buildCommands[i]; + await spawnPromise(x); } - catch (e) { - throw e; - } - }); + } + catch (e) { + throw e; + } } -function build() { - (0, utils_1.addToExecuteQueue)(() => do_build()); - return (0, utils_1.finish)(); +export function build() { + addToExecuteQueue(() => do_build()); + return finish(); } diff --git a/newCommands/build.ts b/newCommands/build.ts index 972cbb3..29a0520 100644 --- a/newCommands/build.ts +++ b/newCommands/build.ts @@ -2,12 +2,17 @@ import { BUILD_SCRIPT_MAKERS, detectProjectType, } from "@merrymake/detect-project-type"; -import { addToExecuteQueue, finish, output2, spawnPromise } from "../utils"; +import { + addToExecuteQueue, + finish, + outputGit, + spawnPromise, +} from "../utils.js"; export async function do_build() { try { const projectType = detectProjectType("."); - output2(`Building ${projectType} project...`); + outputGit(`Building ${projectType} project...`); const buildCommands = BUILD_SCRIPT_MAKERS[projectType]("."); for (let i = 0; i < buildCommands.length; i++) { const x = buildCommands[i]; diff --git a/newCommands/clone.js b/newCommands/clone.js index 8da8916..b668d76 100644 --- a/newCommands/clone.js +++ b/newCommands/clone.js @@ -1,89 +1,63 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.do_clone = do_clone; -exports.do_fetch_clone = do_fetch_clone; -exports.checkout_org = checkout_org; -exports.checkout = checkout; -const fs_1 = __importDefault(require("fs")); -const config_1 = require("../config"); -const prompt_1 = require("../prompt"); -const utils_1 = require("../utils"); -const fetch_1 = require("./fetch"); -const org_1 = require("./org"); -const types_1 = require("../types"); -function do_clone(struct, folderName, displayName, organizationId) { - return __awaiter(this, void 0, void 0, function* () { - try { - (0, utils_1.output2)(`Cloning ${displayName}...`); - fs_1.default.mkdirSync(`${folderName}/.merrymake`, { recursive: true }); - const orgFile = { organizationId: organizationId.toString() }; - fs_1.default.writeFileSync(`${folderName}/.merrymake/conf.json`, JSON.stringify(orgFile)); - const eventsDir = `${folderName}/event-catalogue`; - fs_1.default.mkdirSync(eventsDir, { recursive: true }); - yield (0, utils_1.execPromise)(`git init --initial-branch=main`, eventsDir); - yield (0, utils_1.execPromise)(`git remote add origin "${config_1.GIT_HOST}/o${organizationId}/event-catalogue"`, eventsDir); - fs_1.default.writeFileSync(eventsDir + "/api.json", "{}"); - fs_1.default.writeFileSync(eventsDir + "/cron.json", "{}"); - const publicDir = `${folderName}/public`; - fs_1.default.mkdirSync(publicDir, { recursive: true }); - yield (0, utils_1.execPromise)(`git init --initial-branch=main`, publicDir); - yield (0, utils_1.execPromise)(`git remote add origin "${config_1.GIT_HOST}/o${organizationId}/public"`, publicDir); - fs_1.default.writeFileSync(publicDir + "/index.html", "Hello, World!"); - yield (0, fetch_1.ensureGroupStructure)({ pathTo: new types_1.PathToOrganization(folderName), id: organizationId }, struct); - } - catch (e) { - throw e; - } - }); +import fs from "fs"; +import { GIT_HOST } from "../config.js"; +import { choice } from "../prompt.js"; +import { OrganizationId, PathToOrganization } from "../types.js"; +import { addToExecuteQueue, execPromise, finish, outputGit, sshReq, toFolderName, } from "../utils.js"; +import { ensureGroupStructure } from "./fetch.js"; +import { listOrgs } from "./org.js"; +export async function do_clone(struct, folderName, displayName, organizationId) { + try { + outputGit(`Cloning ${displayName}...`); + fs.mkdirSync(`${folderName}/.merrymake`, { recursive: true }); + const orgFile = { organizationId: organizationId.toString() }; + fs.writeFileSync(`${folderName}/.merrymake/conf.json`, JSON.stringify(orgFile)); + const eventsDir = `${folderName}/event-catalogue`; + fs.mkdirSync(eventsDir, { recursive: true }); + await execPromise(`git init --initial-branch=main`, eventsDir); + await execPromise(`git remote add origin "${GIT_HOST}/o${organizationId}/event-catalogue"`, eventsDir); + fs.writeFileSync(eventsDir + "/api.json", "{}"); + fs.writeFileSync(eventsDir + "/cron.json", "{}"); + const publicDir = `${folderName}/public`; + fs.mkdirSync(publicDir, { recursive: true }); + await execPromise(`git init --initial-branch=main`, publicDir); + await execPromise(`git remote add origin "${GIT_HOST}/o${organizationId}/public"`, publicDir); + fs.writeFileSync(publicDir + "/index.html", "Hello, World!"); + await ensureGroupStructure({ pathTo: new PathToOrganization(folderName), id: organizationId }, struct); + } + catch (e) { + throw e; + } } -function do_fetch_clone(displayName, folderName, organizationId) { - return __awaiter(this, void 0, void 0, function* () { - try { - const reply = yield (0, utils_1.sshReq)(`organization-fetch`, organizationId.toString()); - if (!reply.startsWith("{")) - throw reply; - const structure = JSON.parse(reply); - yield do_clone(structure, folderName, displayName, organizationId); - } - catch (e) { - throw e; - } - }); +export async function do_fetch_clone(displayName, folderName, organizationId) { + try { + const reply = await sshReq(`organization-fetch`, organizationId.toString()); + if (!reply.startsWith("{")) + throw reply; + const structure = JSON.parse(reply); + await do_clone(structure, folderName, displayName, organizationId); + } + catch (e) { + throw e; + } } -function checkout_org(displayName, organizationId) { - return __awaiter(this, void 0, void 0, function* () { - const folderName = (0, utils_1.toFolderName)(displayName); - if (fs_1.default.existsSync(folderName)) { - throw `Folder '${folderName}' already exists.`; - } - (0, utils_1.addToExecuteQueue)(() => do_fetch_clone(displayName, folderName, organizationId)); - return (0, utils_1.finish)(); - }); +export async function checkout_org(displayName, organizationId) { + const folderName = toFolderName(displayName); + if (fs.existsSync(folderName)) { + throw `Folder '${folderName}' already exists.`; + } + addToExecuteQueue(() => do_fetch_clone(displayName, folderName, organizationId)); + return finish(); } -function checkout() { - return __awaiter(this, void 0, void 0, function* () { - try { - const orgs = yield (0, org_1.listOrgs)(); - return (0, prompt_1.choice)("Which organization would you like to clone?", orgs.map((org) => ({ - long: org.id, - text: `${org.name} (${org.id})`, - action: () => checkout_org(org.name, new types_1.OrganizationId(org.id)), - }))); - } - catch (e) { - throw e; - } - }); +export async function checkout() { + try { + const orgs = await listOrgs(); + return choice("Which organization would you like to clone?", orgs.map((org) => ({ + long: org.id, + text: `${org.name} (${org.id})`, + action: () => checkout_org(org.name, new OrganizationId(org.id)), + }))); + } + catch (e) { + throw e; + } } diff --git a/newCommands/clone.ts b/newCommands/clone.ts index 639db1f..9c2284c 100644 --- a/newCommands/clone.ts +++ b/newCommands/clone.ts @@ -1,19 +1,18 @@ import fs from "fs"; -import { GIT_HOST } from "../config"; -import { choice } from "../prompt"; +import { GIT_HOST } from "../config.js"; +import { choice } from "../prompt.js"; +import { OrganizationId, PathToOrganization } from "../types.js"; import { OrgFile, - Path, addToExecuteQueue, execPromise, finish, - output2, + outputGit, sshReq, toFolderName, -} from "../utils"; -import { ToBeStructure, ensureGroupStructure } from "./fetch"; -import { listOrgs } from "./org"; -import { OrganizationId, PathToOrganization } from "../types"; +} from "../utils.js"; +import { ToBeStructure, ensureGroupStructure } from "./fetch.js"; +import { listOrgs } from "./org.js"; export async function do_clone( struct: ToBeStructure, @@ -22,7 +21,7 @@ export async function do_clone( organizationId: OrganizationId ) { try { - output2(`Cloning ${displayName}...`); + outputGit(`Cloning ${displayName}...`); fs.mkdirSync(`${folderName}/.merrymake`, { recursive: true }); const orgFile: OrgFile = { organizationId: organizationId.toString() }; fs.writeFileSync( diff --git a/newCommands/deploy.js b/newCommands/deploy.js index c12d348..2634d6a 100644 --- a/newCommands/deploy.js +++ b/newCommands/deploy.js @@ -1,75 +1,127 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.do_deploy = do_deploy; -exports.do_redeploy = do_redeploy; -exports.deploy = deploy; -exports.redeploy = redeploy; -const prompt_1 = require("../prompt"); -const types_1 = require("../types"); -const utils_1 = require("../utils"); -function do_deploy_internal(commit) { - return __awaiter(this, void 0, void 0, function* () { - try { - const result = []; - const onData = (s) => { - result.push(s); - (0, utils_1.output2)(s); - }; - yield (0, utils_1.execStreamPromise)(`git add -A && ${commit} && git push origin HEAD 2>&1`, onData); - return result.join(""); - } - catch (e) { - throw e; - } - }); +import { choice, Formatting, shortText } from "../prompt.js"; +import { addToExecuteQueue, execStreamPromise, finish, outputGit, spawnPromise, } from "../utils.js"; +/* +[remove .gitignored files] +[clean workspace] +*/ +/* +git add -A +git diff-index --quiet HEAD 2>/dev/null [exitCode to detect dirty working tree] +if dirty + read MSG + git commit -m "MSG" +git fetch +git rebase origin/main +git push origin HEAD:main 2>&1 +if "Everything up to date" + ask redeploy? + git commit --allow-empty -m "Redeploy (empty)" +*/ +async function do_deploy_internal(commit) { + try { + const result = []; + const onData = (s) => { + result.push(s); + outputGit(s); + }; + await execStreamPromise(`git add -A && ${commit} && git push origin HEAD 2>&1`, onData); + return result.join(""); + } + catch (e) { + throw e; + } } -function do_deploy(pathToService) { - return __awaiter(this, void 0, void 0, function* () { - try { - const before = process.cwd(); - process.chdir(pathToService.toString()); - const output = yield do_deploy_internal("(git diff-index --quiet HEAD 2>/dev/null || git commit -m 'Deploy')"); - process.chdir(before); - return !output.startsWith("Everything up-to-date"); - } - catch (e) { - throw e; - } - }); +async function executeAndPrint(command) { + try { + const result = []; + const onData = (s) => { + result.push(s); + outputGit(s); + }; + await execStreamPromise(command, onData); + return result.join(""); + } + catch (e) { + throw e; + } } -function do_redeploy() { - return do_deploy_internal("git commit --allow-empty -m 'Redeploy'"); +export async function do_deploy(pathToService) { + try { + const before = process.cwd(); + process.chdir(pathToService.toString()); + const output = await do_deploy_internal("(git diff-index --quiet HEAD 2>/dev/null || git commit -m 'Deploy with Merrymake')"); + process.chdir(before); + return !output.startsWith("Everything up-to-date"); + } + catch (e) { + throw e; + } } -function deploy() { - return __awaiter(this, void 0, void 0, function* () { - try { - const didDeploy = yield do_deploy(new types_1.PathToRepository(".")); - if (didDeploy) - return (0, utils_1.finish)(); - else - return (0, prompt_1.choice)("Would you like to redeploy?", [ - { - long: "again", - text: "deploy again", - action: () => redeploy(), - }, - ], { disableAutoPick: true }); - } - catch (e) { - throw e; - } - }); +function do_redeploy() { + return spawnPromise("git commit --allow-empty -m 'Redeploy with Merrymake' && git push origin HEAD 2>&1"); } function redeploy() { - (0, utils_1.addToExecuteQueue)(() => do_redeploy()); - return (0, utils_1.finish)(); + addToExecuteQueue(() => do_redeploy()); + return finish(); +} +async function rebaseOntoMain() { + try { + const output = await executeAndPrint(`git fetch && git rebase origin/main && git push origin HEAD:main 2>&1`); + if (output.trimEnd().endsWith("Everything up-to-date")) + return choice("Would you like to redeploy?", [ + { + long: "again", + text: "deploy again", + action: () => redeploy(), + }, + ], { disableAutoPick: true }); + return finish(); + } + catch (e) { + throw e; + } +} +async function getMessage() { + try { + const message = await shortText("Describe your changes: This commit will ", "Write in future tense 'refactor module X'", null, { formatting: Formatting.Minimal }); + const msg = message.length === 0 + ? "[No message]" + : message[0].toUpperCase() + message.substring(1); + await spawnPromise(`git commit -m "${msg}"`); + return rebaseOntoMain(); + } + catch (e) { + throw e; + } +} +export async function deploy() { + try { + const dirty = await (async () => { + try { + await spawnPromise(`git add -A && git diff-index --quiet HEAD 2>/dev/null`); + return false; + } + catch (e) { + return true; + } + })(); + return dirty ? getMessage() : rebaseOntoMain(); + // const didDeploy = await do_deploy(new PathToRepository(".")); + // if (didDeploy) return finish(); + // else + // return choice( + // "Would you like to redeploy?", + // [ + // { + // long: "again", + // text: "deploy again", + // action: () => redeploy(), + // }, + // ], + // { disableAutoPick: true } + // ); + } + catch (e) { + throw e; + } } diff --git a/newCommands/deploy.ts b/newCommands/deploy.ts index f217bef..aa8301c 100644 --- a/newCommands/deploy.ts +++ b/newCommands/deploy.ts @@ -1,18 +1,38 @@ -import { choice } from "../prompt"; -import { PathToRepository } from "../types"; +import { choice, Formatting, shortText } from "../prompt.js"; +import { PathToRepository } from "../types.js"; import { addToExecuteQueue, execStreamPromise, finish, - output2, -} from "../utils"; + outputGit, + spawnPromise, +} from "../utils.js"; + +/* +[remove .gitignored files] +[clean workspace] +*/ + +/* +git add -A +git diff-index --quiet HEAD 2>/dev/null [exitCode to detect dirty working tree] +if dirty + read MSG + git commit -m "MSG" +git fetch +git rebase origin/main +git push origin HEAD:main 2>&1 +if "Everything up to date" + ask redeploy? + git commit --allow-empty -m "Redeploy (empty)" +*/ async function do_deploy_internal(commit: string) { try { const result: string[] = []; const onData = (s: string) => { result.push(s); - output2(s); + outputGit(s); }; await execStreamPromise( `git add -A && ${commit} && git push origin HEAD 2>&1`, @@ -23,13 +43,26 @@ async function do_deploy_internal(commit: string) { throw e; } } +async function executeAndPrint(command: string) { + try { + const result: string[] = []; + const onData = (s: string) => { + result.push(s); + outputGit(s); + }; + await execStreamPromise(command, onData); + return result.join(""); + } catch (e) { + throw e; + } +} export async function do_deploy(pathToService: PathToRepository) { try { const before = process.cwd(); process.chdir(pathToService.toString()); const output = await do_deploy_internal( - "(git diff-index --quiet HEAD 2>/dev/null || git commit -m 'Deploy')" + "(git diff-index --quiet HEAD 2>/dev/null || git commit -m 'Deploy with Merrymake')" ); process.chdir(before); return !output.startsWith("Everything up-to-date"); @@ -37,15 +70,23 @@ export async function do_deploy(pathToService: PathToRepository) { throw e; } } -export function do_redeploy() { - return do_deploy_internal("git commit --allow-empty -m 'Redeploy'"); +function do_redeploy() { + return spawnPromise( + "git commit --allow-empty -m 'Redeploy with Merrymake' && git push origin HEAD 2>&1" + ); } -export async function deploy() { +function redeploy() { + addToExecuteQueue(() => do_redeploy()); + return finish(); +} + +async function rebaseOntoMain() { try { - const didDeploy = await do_deploy(new PathToRepository(".")); - if (didDeploy) return finish(); - else + const output = await executeAndPrint( + `git fetch && git rebase origin/main && git push origin HEAD:main 2>&1` + ); + if (output.trimEnd().endsWith("Everything up-to-date")) return choice( "Would you like to redeploy?", [ @@ -57,12 +98,59 @@ export async function deploy() { ], { disableAutoPick: true } ); + return finish(); } catch (e) { throw e; } } -export function redeploy() { - addToExecuteQueue(() => do_redeploy()); - return finish(); +async function getMessage() { + try { + const message = await shortText( + "Describe your changes: This commit will ", + "Write in future tense 'refactor module X'", + null, + { formatting: Formatting.Minimal } + ); + const msg = + message.length === 0 + ? "[No message]" + : message[0].toUpperCase() + message.substring(1); + await spawnPromise(`git commit -m "${msg}"`); + return rebaseOntoMain(); + } catch (e) { + throw e; + } +} + +export async function deploy() { + try { + const dirty = await (async () => { + try { + await spawnPromise( + `git add -A && git diff-index --quiet HEAD 2>/dev/null` + ); + return false; + } catch (e) { + return true; + } + })(); + return dirty ? getMessage() : rebaseOntoMain(); + // const didDeploy = await do_deploy(new PathToRepository(".")); + // if (didDeploy) return finish(); + // else + // return choice( + // "Would you like to redeploy?", + // [ + // { + // long: "again", + // text: "deploy again", + // action: () => redeploy(), + // }, + // ], + // { disableAutoPick: true } + // ); + } catch (e) { + throw e; + } } diff --git a/newCommands/envvar.js b/newCommands/envvar.js index ae14ce1..80f27f0 100644 --- a/newCommands/envvar.js +++ b/newCommands/envvar.js @@ -1,59 +1,42 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.envvar = envvar; -const secret_lib_1 = require("@merrymake/secret-lib"); -const fs_1 = __importDefault(require("fs")); -const config_1 = require("../config"); -const prompt_1 = require("../prompt"); -const utils_1 = require("../utils"); -function do_envvar(pathToOrganization, organizationId, serviceGroupId, key, value, access, encrypted) { - return __awaiter(this, void 0, void 0, function* () { - const keyFolder = pathToOrganization.with(".merrymake").with(".key"); - try { - let val; - if (encrypted === true) { - const repoBase = `${config_1.GIT_HOST}/o${organizationId.toString()}/g${serviceGroupId.toString()}/.key`; - fs_1.default.rmSync(keyFolder.toString(), { force: true, recursive: true }); - yield (0, utils_1.execPromise)(`git clone -q "${repoBase}"`, pathToOrganization.with(".merrymake").toString()); - const key = fs_1.default.readFileSync(keyFolder.with("merrymake.key").toString()); - val = new secret_lib_1.MerrymakeCrypto() - .encrypt(Buffer.from(value), key) - .toString("base64"); - } - else { - val = value; - } - (0, utils_1.output2)(yield (0, utils_1.sshReq)(`envvar-set`, key, ...access, `--serviceGroupId`, serviceGroupId.toString(), `--value`, val, ...(encrypted ? ["--encrypted"] : []))); +import { MerrymakeCrypto } from "@merrymake/secret-lib"; +import fs from "fs"; +import { GIT_HOST } from "../config.js"; +import { Visibility, choice, shortText } from "../prompt.js"; +import { addToExecuteQueue, execPromise, finish, outputGit, sshReq, } from "../utils.js"; +async function do_envvar(pathToOrganization, organizationId, serviceGroupId, key, value, access, encrypted) { + const keyFolder = pathToOrganization.with(".merrymake").with(".key"); + try { + let val; + if (encrypted === true) { + const repoBase = `${GIT_HOST}/o${organizationId.toString()}/g${serviceGroupId.toString()}/.key`; + fs.rmSync(keyFolder.toString(), { force: true, recursive: true }); + await execPromise(`git clone -q "${repoBase}"`, pathToOrganization.with(".merrymake").toString()); + const key = fs.readFileSync(keyFolder.with("merrymake.key").toString()); + val = new MerrymakeCrypto() + .encrypt(Buffer.from(value), key) + .toString("base64"); } - catch (e) { - throw e; + else { + val = value; } - finally { - fs_1.default.rmSync(keyFolder.toString(), { - force: true, - recursive: true, - }); - } - }); + outputGit(await sshReq(`envvar-set`, key, ...access, `--serviceGroupId`, serviceGroupId.toString(), `--value`, val, ...(encrypted ? ["--encrypted"] : []))); + } + catch (e) { + throw e; + } + finally { + fs.rmSync(keyFolder.toString(), { + force: true, + recursive: true, + }); + } } function envvar_key_value_access_visible(pathToOrganization, organizationId, serviceGroupId, key, value, access, secret) { - (0, utils_1.addToExecuteQueue)(() => do_envvar(pathToOrganization, organizationId, serviceGroupId, key, value, access, secret)); - return (0, utils_1.finish)(); + addToExecuteQueue(() => do_envvar(pathToOrganization, organizationId, serviceGroupId, key, value, access, secret)); + return finish(); } function envvar_key_visible_value(pathToOrganization, organizationId, serviceGroupId, key, value, secret, init, prod) { - return (0, prompt_1.choice)("Where would you like the variable to be visible?", [ + return choice("Where would you like the variable to be visible?", [ { long: "both", short: "b", @@ -74,22 +57,20 @@ function envvar_key_visible_value(pathToOrganization, organizationId, serviceGro }, ], { def: init ? (prod ? 0 : 2) : 1 }); } -function envvar_key_visible(pathToOrganization, organizationId, serviceGroupId, key, secret, init, prod) { - return __awaiter(this, void 0, void 0, function* () { - try { - const value = yield (0, prompt_1.shortText)("Value", "The value...", "", secret === true ? prompt_1.Visibility.Secret : prompt_1.Visibility.Public).then(); - if (value !== "") - return envvar_key_visible_value(pathToOrganization, organizationId, serviceGroupId, key, value, secret, init, prod); - else - return envvar_key_value_access_visible(pathToOrganization, organizationId, serviceGroupId, key, value, ["--inProduction", "--inInitRun"], false); - } - catch (e) { - throw e; - } - }); +async function envvar_key_visible(pathToOrganization, organizationId, serviceGroupId, key, secret, init, prod) { + try { + const value = await shortText("Value", "The value...", "", secret === true ? { hide: Visibility.Secret } : undefined).then(); + if (value !== "") + return envvar_key_visible_value(pathToOrganization, organizationId, serviceGroupId, key, value, secret, init, prod); + else + return envvar_key_value_access_visible(pathToOrganization, organizationId, serviceGroupId, key, value, ["--inProduction", "--inInitRun"], false); + } + catch (e) { + throw e; + } } function envvar_key(pathToOrganization, organizationId, serviceGroupId, key, secret, init, prod) { - return (0, prompt_1.choice)("What is the visibility of the variable?", [ + return choice("What is the visibility of the variable?", [ { long: "secret", short: "s", @@ -110,37 +91,33 @@ function envvar_key(pathToOrganization, organizationId, serviceGroupId, key, sec }, ], { def: secret ? 0 : 1 }); } -function envvar_new(pathToOrganization, organizationId, serviceGroupId) { - return __awaiter(this, void 0, void 0, function* () { - try { - const key = yield (0, prompt_1.shortText)("Key", "Key for the key-value pair", "key").then(); - return envvar_key(pathToOrganization, organizationId, serviceGroupId, key, true, true, true); - } - catch (e) { - throw e; - } - }); +async function envvar_new(pathToOrganization, organizationId, serviceGroupId) { + try { + const key = await shortText("Key", "Key for the key-value pair", "key").then(); + return envvar_key(pathToOrganization, organizationId, serviceGroupId, key, true, true, true); + } + catch (e) { + throw e; + } } -function envvar(pathToOrganization, organizationId, serviceGroupId) { - return __awaiter(this, void 0, void 0, function* () { - try { - const resp = yield (0, utils_1.sshReq)(`envvar-list`, serviceGroupId.toString()); - const orgs = JSON.parse(resp); - const options = orgs.map((x) => ({ - long: x.k, - text: `[${x.i ? "I" : " "}${x.p ? "P" : " "}] ${x.k}: ${x.v}`, - action: () => envvar_key(pathToOrganization, organizationId, serviceGroupId, x.k, x.s, x.i, x.p), - })); - options.push({ - long: `new`, - short: `n`, - text: `add a new environment variable`, - action: () => envvar_new(pathToOrganization, organizationId, serviceGroupId), - }); - return yield (0, prompt_1.choice)("Which environment variable do you want to edit?", options).then(); - } - catch (e) { - throw e; - } - }); +export async function envvar(pathToOrganization, organizationId, serviceGroupId) { + try { + const resp = await sshReq(`envvar-list`, serviceGroupId.toString()); + const orgs = JSON.parse(resp); + const options = orgs.map((x) => ({ + long: x.k, + text: `[${x.i ? "I" : " "}${x.p ? "P" : " "}] ${x.k}: ${x.v}`, + action: () => envvar_key(pathToOrganization, organizationId, serviceGroupId, x.k, x.s, x.i, x.p), + })); + options.push({ + long: `new`, + short: `n`, + text: `add a new environment variable`, + action: () => envvar_new(pathToOrganization, organizationId, serviceGroupId), + }); + return await choice("Which environment variable do you want to edit?", options).then(); + } + catch (e) { + throw e; + } } diff --git a/newCommands/envvar.ts b/newCommands/envvar.ts index f77fe08..9ca7df8 100644 --- a/newCommands/envvar.ts +++ b/newCommands/envvar.ts @@ -1,16 +1,19 @@ import { MerrymakeCrypto } from "@merrymake/secret-lib"; import fs from "fs"; -import path from "path"; -import { GIT_HOST } from "../config"; -import { Option, Visibility, choice, shortText } from "../prompt"; -import { OrganizationId, PathToOrganization, ServiceGroupId } from "../types"; +import { GIT_HOST } from "../config.js"; +import { Option, Visibility, choice, shortText } from "../prompt.js"; +import { + OrganizationId, + PathToOrganization, + ServiceGroupId, +} from "../types.js"; import { addToExecuteQueue, execPromise, finish, - output2, + outputGit, sshReq, -} from "../utils"; +} from "../utils.js"; async function do_envvar( pathToOrganization: PathToOrganization, @@ -38,7 +41,7 @@ async function do_envvar( } else { val = value; } - output2( + outputGit( await sshReq( `envvar-set`, key, @@ -160,7 +163,7 @@ async function envvar_key_visible( "Value", "The value...", "", - secret === true ? Visibility.Secret : Visibility.Public + secret === true ? { hide: Visibility.Secret } : undefined ).then(); if (value !== "") return envvar_key_visible_value( diff --git a/newCommands/event.js b/newCommands/event.js index fffffd7..cb8f97c 100644 --- a/newCommands/event.js +++ b/newCommands/event.js @@ -1,72 +1,53 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.do_event = do_event; -exports.event = event; -const prompt_1 = require("../prompt"); -const utils_1 = require("../utils"); -const apikey_1 = require("./apikey"); -function do_event(apikeyId, events) { - return __awaiter(this, void 0, void 0, function* () { - try { - const selected = Object.keys(events).filter((x) => events[x]); - yield (0, utils_1.sshReq)(`events-allow`, apikeyId, `--events`, selected.join(",")); - (0, utils_1.output2)(`Allowed event${selected.length > 1 ? "s" : ""} ${selected.join(", ")} on key ${apikeyId}.`); - } - catch (e) { - throw e; - } - }); +import { choice, multiSelect } from "../prompt.js"; +import { addToExecuteQueue, finish, outputGit, sshReq } from "../utils.js"; +import { key_create } from "./apikey.js"; +export async function do_event(apikeyId, events) { + try { + const selected = Object.keys(events).filter((x) => events[x]); + await sshReq(`events-allow`, apikeyId, `--events`, selected.join(",")); + outputGit(`Allowed event${selected.length > 1 ? "s" : ""} ${selected.join(", ")} on key ${apikeyId}.`); + } + catch (e) { + throw e; + } } function event_key_events(apikeyId, events) { - (0, utils_1.addToExecuteQueue)(() => do_event(apikeyId, events)); - return (0, utils_1.finish)(); + addToExecuteQueue(() => do_event(apikeyId, events)); + return finish(); } -function event_key(apikeyId) { - return __awaiter(this, void 0, void 0, function* () { - try { - const resp = yield (0, utils_1.sshReq)(`events-list`, apikeyId); - const parsed = JSON.parse(resp); - const events = {}; - parsed.forEach((x) => (events[x.event] = x.allowed)); - return yield (0, prompt_1.multiSelect)(events, (s) => event_key_events(apikeyId, s), "No events in event-catalogue. Make sure you have added events to the event-catalogue and deployed it."); - } - catch (e) { - throw e; - } - }); +async function event_key(apikeyId) { + try { + const resp = await sshReq(`events-list`, apikeyId); + const parsed = JSON.parse(resp); + const events = {}; + parsed.forEach((x) => (events[x.event] = x.allowed)); + return await multiSelect(events, (s) => event_key_events(apikeyId, s), "No events in event-catalogue. Make sure you have added events to the event-catalogue and deployed it."); + } + catch (e) { + throw e; + } } -function event(organizationId) { - return __awaiter(this, void 0, void 0, function* () { - try { - const resp = yield (0, utils_1.sshReq)(`apikey-list`, organizationId.toString()); - const keys = JSON.parse(resp); - const options = keys.map((x) => { - const n = x.name || ""; - return { - long: x.id, - text: x.name === null ? x.id : `${x.name} (${x.id})`, - action: () => event_key(x.id), - }; - }); - options.push({ - long: `new`, - short: `n`, - text: `add a new apikey`, - action: () => (0, apikey_1.key_create)(organizationId, event_key), - }); - return yield (0, prompt_1.choice)("Which key to allow events through?", options).then(); - } - catch (e) { - throw e; - } - }); +export async function event(organizationId) { + try { + const resp = await sshReq(`apikey-list`, organizationId.toString()); + const keys = JSON.parse(resp); + const options = keys.map((x) => { + const n = x.name || ""; + return { + long: x.id, + text: x.name === null ? x.id : `${x.name} (${x.id})`, + action: () => event_key(x.id), + }; + }); + options.push({ + long: `new`, + short: `n`, + text: `add a new apikey`, + action: () => key_create(organizationId, event_key), + }); + return await choice("Which key to allow events through?", options).then(); + } + catch (e) { + throw e; + } } diff --git a/newCommands/event.ts b/newCommands/event.ts index 5559ccb..9cf4a85 100644 --- a/newCommands/event.ts +++ b/newCommands/event.ts @@ -1,9 +1,7 @@ -import { stdout } from "process"; -import { alignLeft, printTableHeader } from "../executors"; -import { Option, choice, multiSelect } from "../prompt"; -import { OrganizationId } from "../types"; -import { addToExecuteQueue, finish, output2, sshReq } from "../utils"; -import { key_create } from "./apikey"; +import { Option, choice, multiSelect } from "../prompt.js"; +import { OrganizationId } from "../types.js"; +import { addToExecuteQueue, finish, outputGit, sshReq } from "../utils.js"; +import { key_create } from "./apikey.js"; export async function do_event( apikeyId: string, @@ -12,7 +10,7 @@ export async function do_event( try { const selected = Object.keys(events).filter((x) => events[x]); await sshReq(`events-allow`, apikeyId, `--events`, selected.join(",")); - output2( + outputGit( `Allowed event${selected.length > 1 ? "s" : ""} ${selected.join( ", " )} on key ${apikeyId}.` diff --git a/newCommands/fetch.js b/newCommands/fetch.js index a9c94a8..0cb2cb4 100644 --- a/newCommands/fetch.js +++ b/newCommands/fetch.js @@ -1,158 +1,131 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ensureGroupStructure = ensureGroupStructure; -exports.do_fetch = do_fetch; -exports.fetch = fetch; -const fs_1 = __importDefault(require("fs")); -const config_1 = require("../config"); -const types_1 = require("../types"); -const utils_1 = require("../utils"); -function getCurrentStructure(pathToOrganization) { - return __awaiter(this, void 0, void 0, function* () { - const folders = (0, utils_1.directoryNames)(pathToOrganization, [ - "event-catalogue", - "public", - ]); - const groups = {}; - yield Promise.all(folders.map((f) => { - const pathToGroup = pathToOrganization.with(f.name); - if (fs_1.default.existsSync(pathToGroup.with(".group-id").toString())) { - const groupId = fs_1.default - .readFileSync(pathToGroup.with(".group-id").toString()) - .toString(); - const repositories = {}; - groups[groupId] = { name: f.name, repositories }; - const folders = (0, utils_1.directoryNames)(pathToGroup, []); - return Promise.all(folders.map((f) => __awaiter(this, void 0, void 0, function* () { - if (fs_1.default.existsSync(pathToGroup.with(f.name).with(".git").toString())) { - const repositoryUrl = yield (0, utils_1.execPromise)(`git ls-remote --get-url origin`, pathToGroup.with(f.name).toString()); - const repositoryId = repositoryUrl - .trim() - .substring(repositoryUrl.lastIndexOf("/") + "/r".length); - repositories[repositoryId] = f.name; - } - }))); - } - })); - return groups; - }); +import fs from "fs"; +import { GIT_HOST } from "../config.js"; +import { RepositoryId, ServiceGroupId, } from "../types.js"; +import { addToExecuteQueue, directoryNames, execPromise, finish, outputGit, sshReq, toFolderName, } from "../utils.js"; +async function getCurrentStructure(pathToOrganization) { + const folders = directoryNames(pathToOrganization, [ + "event-catalogue", + "public", + ]); + const groups = {}; + await Promise.all(folders.map((f) => { + const pathToGroup = pathToOrganization.with(f.name); + if (fs.existsSync(pathToGroup.with(".group-id").toString())) { + const groupId = fs + .readFileSync(pathToGroup.with(".group-id").toString()) + .toString(); + const repositories = {}; + groups[groupId] = { name: f.name, repositories }; + const folders = directoryNames(pathToGroup, []); + return Promise.all(folders.map(async (f) => { + if (fs.existsSync(pathToGroup.with(f.name).with(".git").toString())) { + const repositoryUrl = await execPromise(`git ls-remote --get-url origin`, pathToGroup.with(f.name).toString()); + const repositoryId = repositoryUrl + .trim() + .substring(repositoryUrl.lastIndexOf("/") + "/r".length); + repositories[repositoryId] = f.name; + } + })); + } + })); + return groups; } -function ensureRepositoryStructure(organizationId, serviceGroup, toBe, asIs) { - return __awaiter(this, void 0, void 0, function* () { - yield Promise.all(Object.keys(toBe).map((repositoryId) => __awaiter(this, void 0, void 0, function* () { - const repositoryDisplayName = toBe[repositoryId]; - const folderName = (0, utils_1.toFolderName)(repositoryDisplayName); - const pathToRepository = serviceGroup.pathTo.with(folderName); - if (asIs[repositoryId] !== undefined && - asIs[repositoryId] !== folderName) { - fs_1.default.renameSync(serviceGroup.pathTo.with(asIs[repositoryId]).toString(), pathToRepository.toString()); - } - yield ensureServiceFolder(organizationId, serviceGroup.id, { - pathTo: pathToRepository, - id: new types_1.RepositoryId(repositoryId), - }).then(); - delete asIs[repositoryId]; - }))); - yield Promise.all(Object.keys(asIs).map((repositoryId) => { - const folderName = asIs[repositoryId]; - // TODO Delete - console.log("Delete", serviceGroup.pathTo.with(folderName).toString()); - })); - }); +async function ensureRepositoryStructure(organizationId, serviceGroup, toBe, asIs) { + await Promise.all(Object.keys(toBe).map(async (repositoryId) => { + const repositoryDisplayName = toBe[repositoryId]; + const folderName = toFolderName(repositoryDisplayName); + const pathToRepository = serviceGroup.pathTo.with(folderName); + if (asIs[repositoryId] !== undefined && + asIs[repositoryId] !== folderName) { + fs.renameSync(serviceGroup.pathTo.with(asIs[repositoryId]).toString(), pathToRepository.toString()); + } + await ensureServiceFolder(organizationId, serviceGroup.id, { + pathTo: pathToRepository, + id: new RepositoryId(repositoryId), + }).then(); + delete asIs[repositoryId]; + })); + await Promise.all(Object.keys(asIs).map((repositoryId) => { + const folderName = asIs[repositoryId]; + // TODO Delete + console.log("Delete", serviceGroup.pathTo.with(folderName).toString()); + })); } -function ensureGroupStructure(organization, toBe) { - return __awaiter(this, void 0, void 0, function* () { - const asIs = yield getCurrentStructure(organization.pathTo); - yield Promise.all(Object.keys(toBe).map((serviceGroupId) => __awaiter(this, void 0, void 0, function* () { - const group = toBe[serviceGroupId]; - const folderName = (0, utils_1.toFolderName)(group.displayName); - const pathToGroup = organization.pathTo.with(folderName); - let asIsRepos; - if (asIs[serviceGroupId] === undefined) { - fs_1.default.mkdirSync(pathToGroup.toString(), { recursive: true }); - fs_1.default.writeFileSync(pathToGroup.with(".group-id").toString(), serviceGroupId); - asIsRepos = {}; - } - else { - if (asIs[serviceGroupId].name !== folderName) { - fs_1.default.renameSync(organization.pathTo.with(asIs[serviceGroupId].name).toString(), pathToGroup.toString()); - } - asIsRepos = asIs[serviceGroupId].repositories; +export async function ensureGroupStructure(organization, toBe) { + const asIs = await getCurrentStructure(organization.pathTo); + await Promise.all(Object.keys(toBe).map(async (serviceGroupId) => { + const group = toBe[serviceGroupId]; + const folderName = toFolderName(group.displayName); + const pathToGroup = organization.pathTo.with(folderName); + let asIsRepos; + if (asIs[serviceGroupId] === undefined) { + fs.mkdirSync(pathToGroup.toString(), { recursive: true }); + fs.writeFileSync(pathToGroup.with(".group-id").toString(), serviceGroupId); + asIsRepos = {}; + } + else { + if (asIs[serviceGroupId].name !== folderName) { + fs.renameSync(organization.pathTo.with(asIs[serviceGroupId].name).toString(), pathToGroup.toString()); } - yield ensureRepositoryStructure(organization.id, { pathTo: pathToGroup, id: new types_1.ServiceGroupId(serviceGroupId) }, group.repositories, asIsRepos); - delete asIs[serviceGroupId]; - }))); - yield Promise.all(Object.keys(asIs).map((groupId) => { - const group = asIs[groupId]; - const folderName = group.name; - // TODO Delete - console.log("Delete", organization.pathTo.with(folderName).toString()); - })); - }); + asIsRepos = asIs[serviceGroupId].repositories; + } + await ensureRepositoryStructure(organization.id, { pathTo: pathToGroup, id: new ServiceGroupId(serviceGroupId) }, group.repositories, asIsRepos); + delete asIs[serviceGroupId]; + })); + await Promise.all(Object.keys(asIs).map((groupId) => { + const group = asIs[groupId]; + const folderName = group.name; + // TODO Delete + console.log("Delete", organization.pathTo.with(folderName).toString()); + })); } -function ensureServiceFolder(organizationId, groupId, repository) { - return __awaiter(this, void 0, void 0, function* () { - process.stdout.write("."); - const dir = repository.pathTo.toString(); - const repo = `"${config_1.GIT_HOST}/o${organizationId}/g${groupId}/r${repository.id}"`; - try { - if (!fs_1.default.existsSync(dir)) { - fs_1.default.mkdirSync(dir, { recursive: true }); - } - if (!fs_1.default.existsSync(dir + "/.git")) { - yield (0, utils_1.execPromise)(`git init --initial-branch=main`, dir); - yield (0, utils_1.execPromise)(`git remote add origin ${repo}`, dir); - fs_1.default.writeFileSync(dir + "/fetch.bat", `@echo off +async function ensureServiceFolder(organizationId, groupId, repository) { + process.stdout.write("."); + const dir = repository.pathTo.toString(); + const repo = `"${GIT_HOST}/o${organizationId}/g${groupId}/r${repository.id}"`; + try { + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + if (!fs.existsSync(dir + "/.git")) { + await execPromise(`git init --initial-branch=main`, dir); + await execPromise(`git remote add origin ${repo}`, dir); + fs.writeFileSync(dir + "/fetch.bat", `@echo off git fetch git reset --hard origin/main del fetch.sh (goto) 2>nul & del fetch.bat`); - fs_1.default.writeFileSync(dir + "/fetch.sh", `#!/bin/sh + fs.writeFileSync(dir + "/fetch.sh", `#!/bin/sh git fetch git reset --hard origin/main rm fetch.bat fetch.sh`, {}); - fs_1.default.chmodSync(dir + "/fetch.sh", "755"); - } - else { - yield (0, utils_1.execPromise)(`git remote set-url origin ${repo}`, dir); - } + fs.chmodSync(dir + "/fetch.sh", "755"); } - catch (e) { - console.log(e); + else { + await execPromise(`git remote set-url origin ${repo}`, dir); } - }); + } + catch (e) { + console.log(e); + } } -function do_fetch(organization) { - return __awaiter(this, void 0, void 0, function* () { - try { - (0, utils_1.output2)(`Fetching...`); - const reply = yield (0, utils_1.sshReq)(`organization-fetch`, organization.id.toString()); - if (!reply.startsWith("{")) - throw reply; - const structure = JSON.parse(reply); - process.stdout.write(`Consolidating`); - yield ensureGroupStructure(organization, structure); - process.stdout.write("\n"); - return structure; - } - catch (e) { - throw e; - } - }); +export async function do_fetch(organization) { + try { + outputGit(`Fetching...`); + const reply = await sshReq(`organization-fetch`, organization.id.toString()); + if (!reply.startsWith("{")) + throw reply; + const structure = JSON.parse(reply); + process.stdout.write(`Consolidating`); + await ensureGroupStructure(organization, structure); + process.stdout.write("\n"); + return structure; + } + catch (e) { + throw e; + } } -function fetch(organization) { - (0, utils_1.addToExecuteQueue)(() => do_fetch(organization)); - return (0, utils_1.finish)(); +export function fetch(organization) { + addToExecuteQueue(() => do_fetch(organization)); + return finish(); } diff --git a/newCommands/fetch.ts b/newCommands/fetch.ts index a9e2935..5f75576 100644 --- a/newCommands/fetch.ts +++ b/newCommands/fetch.ts @@ -1,23 +1,23 @@ import fs from "fs"; -import { GIT_HOST } from "../config"; +import { GIT_HOST } from "../config.js"; import { Organization, OrganizationId, PathToOrganization, - Repository, RepositoryId, + RepositoryWithId, ServiceGroup, ServiceGroupId, -} from "../types"; +} from "../types.js"; import { addToExecuteQueue, directoryNames, execPromise, finish, - output2, + outputGit, sshReq, toFolderName, -} from "../utils"; +} from "../utils.js"; type DisplayName = string; type RepositoryStructure = { [repositoryId: string]: DisplayName }; @@ -159,7 +159,7 @@ export async function ensureGroupStructure( async function ensureServiceFolder( organizationId: OrganizationId, groupId: ServiceGroupId, - repository: Repository + repository: RepositoryWithId ) { process.stdout.write("."); const dir = repository.pathTo.toString(); @@ -198,7 +198,7 @@ rm fetch.bat fetch.sh`, export async function do_fetch(organization: Organization) { try { - output2(`Fetching...`); + outputGit(`Fetching...`); const reply = await sshReq( `organization-fetch`, organization.id.toString() diff --git a/newCommands/group.js b/newCommands/group.js index 79dd6e7..e6e8572 100644 --- a/newCommands/group.js +++ b/newCommands/group.js @@ -1,58 +1,77 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.do_createServiceGroup = do_createServiceGroup; -exports.group = group; -const fs_1 = __importDefault(require("fs")); -const prompt_1 = require("../prompt"); -const types_1 = require("../types"); -const utils_1 = require("../utils"); -const repo_1 = require("./repo"); -function do_createServiceGroup(path, organizationId, displayName) { - return __awaiter(this, void 0, void 0, function* () { - try { - console.log(`Creating service group '${displayName}'...`); - const reply = yield (0, utils_1.sshReq)(`group-create`, displayName, `--organizationId`, organizationId.toString()); - if (reply.length !== 8) - throw reply; - fs_1.default.mkdirSync(path.toString(), { recursive: true }); - const serviceGroupId = new types_1.ServiceGroupId(reply); - fs_1.default.writeFileSync(path.with(".group-id").toString(), serviceGroupId.toString()); - return serviceGroupId; - } - catch (e) { - throw e; - } - }); +import fs from "fs"; +import { choice, shortText } from "../prompt.js"; +import { PathToServiceGroup, ServiceGroupId, } from "../types.js"; +import { addToExecuteQueue, finish, sshReq, toFolderName } from "../utils.js"; +import { repo_create } from "./repo.js"; +export async function do_deleteServiceGroup(serviceGroup, displayName) { + try { + console.log(`Deleting service group '${displayName}'...`); + const reply = await sshReq(`group-delete`, serviceGroup.id.toString()); + console.log(reply); + if (fs.existsSync(serviceGroup.pathTo.toString())) + fs.renameSync(serviceGroup.pathTo.toString(), `(deleted) ${serviceGroup.pathTo}`); + } + catch (e) { + throw e; + } } -function group(organization) { - return __awaiter(this, void 0, void 0, function* () { - try { - let num = 1; - while (fs_1.default.existsSync(organization.pathTo.with("service-group-" + num).toString())) - num++; - const displayName = yield (0, prompt_1.shortText)("Service group name", "Used to share envvars.", "service-group-" + num).then(); - const folderName = (0, utils_1.toFolderName)(displayName); - const pathToServiceGroup = organization.pathTo.with(folderName); - const serviceGroupId = yield do_createServiceGroup(pathToServiceGroup, organization.id, displayName); - return (0, repo_1.repo_create)(organization, { - pathTo: pathToServiceGroup, - id: serviceGroupId, - }); - } - catch (e) { - throw e; - } - }); +function deleteServiceGroupId(serviceGroup, displayName) { + addToExecuteQueue(async () => do_deleteServiceGroup(serviceGroup, displayName)); + return finish(); +} +export async function deleteServiceGroup(organizationId) { + try { + const resp = await sshReq(`group-list`, organizationId.toString()); + if (!resp.startsWith("[")) + throw resp; + const groups = JSON.parse(resp); + const options = groups.map((group) => { + const folderName = toFolderName(group.name); + return { + long: folderName, + text: `Delete ${group.name} (${folderName})`, + action: () => deleteServiceGroupId({ + id: new ServiceGroupId(group.id), + pathTo: new PathToServiceGroup(folderName), + }, group.name), + }; + }); + return await choice("Which service group would you like to delete?", options).then(); + } + catch (e) { + throw e; + } +} +export async function do_createServiceGroup(path, organizationId, displayName) { + try { + console.log(`Creating service group '${displayName}'...`); + const reply = await sshReq(`group-create`, displayName, `--organizationId`, organizationId.toString()); + if (reply.length !== 8) + throw reply; + fs.mkdirSync(path.toString(), { recursive: true }); + const serviceGroupId = new ServiceGroupId(reply); + fs.writeFileSync(path.with(".group-id").toString(), serviceGroupId.toString()); + return serviceGroupId; + } + catch (e) { + throw e; + } +} +export async function group(organization) { + try { + let num = 1; + while (fs.existsSync(organization.pathTo.with("service-group-" + num).toString())) + num++; + const displayName = await shortText("Service group name", "Used to share envvars.", "service-group-" + num).then(); + const folderName = toFolderName(displayName); + const pathToServiceGroup = organization.pathTo.with(folderName); + const serviceGroupId = await do_createServiceGroup(pathToServiceGroup, organization.id, displayName); + return repo_create(organization, { + pathTo: pathToServiceGroup, + id: serviceGroupId, + }); + } + catch (e) { + throw e; + } } diff --git a/newCommands/group.ts b/newCommands/group.ts index 51e1c8c..d50a436 100644 --- a/newCommands/group.ts +++ b/newCommands/group.ts @@ -1,13 +1,68 @@ import fs from "fs"; -import { shortText } from "../prompt"; +import { choice, Option, shortText } from "../prompt.js"; import { Organization, OrganizationId, PathToServiceGroup, + ServiceGroup, ServiceGroupId, -} from "../types"; -import { sshReq, toFolderName } from "../utils"; -import { repo_create } from "./repo"; +} from "../types.js"; +import { addToExecuteQueue, finish, sshReq, toFolderName } from "../utils.js"; +import { repo_create } from "./repo.js"; + +export async function do_deleteServiceGroup( + serviceGroup: ServiceGroup, + displayName: string +) { + try { + console.log(`Deleting service group '${displayName}'...`); + const reply = await sshReq(`group-delete`, serviceGroup.id.toString()); + console.log(reply); + if (fs.existsSync(serviceGroup.pathTo.toString())) + fs.renameSync( + serviceGroup.pathTo.toString(), + `(deleted) ${serviceGroup.pathTo}` + ); + } catch (e) { + throw e; + } +} + +function deleteServiceGroupId(serviceGroup: ServiceGroup, displayName: string) { + addToExecuteQueue(async () => + do_deleteServiceGroup(serviceGroup, displayName) + ); + return finish(); +} + +export async function deleteServiceGroup(organizationId: OrganizationId) { + try { + const resp = await sshReq(`group-list`, organizationId.toString()); + if (!resp.startsWith("[")) throw resp; + const groups: { id: string; name: string }[] = JSON.parse(resp); + const options: Option[] = groups.map((group) => { + const folderName = toFolderName(group.name); + return { + long: folderName, + text: `Delete ${group.name} (${folderName})`, + action: () => + deleteServiceGroupId( + { + id: new ServiceGroupId(group.id), + pathTo: new PathToServiceGroup(folderName), + }, + group.name + ), + }; + }); + return await choice( + "Which service group would you like to delete?", + options + ).then(); + } catch (e) { + throw e; + } +} export async function do_createServiceGroup( path: PathToServiceGroup, diff --git a/newCommands/hosting.js b/newCommands/hosting.js index 1910b02..a7f460a 100644 --- a/newCommands/hosting.js +++ b/newCommands/hosting.js @@ -1,45 +1,24 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.BITBUCKET_FILE = void 0; -exports.do_create_deployment_agent = do_create_deployment_agent; -exports.bitbucketStep = bitbucketStep; -exports.do_bitbucket = do_bitbucket; -exports.hosting = hosting; -const fs_1 = __importDefault(require("fs")); -const config_1 = require("../config"); -const prompt_1 = require("../prompt"); -const types_1 = require("../types"); -const utils_1 = require("../utils"); -const fetch_1 = require("./fetch"); -function do_create_deployment_agent(organization, name, file) { - return __awaiter(this, void 0, void 0, function* () { - try { - (0, utils_1.output2)("Creating service user..."); - const cmd = [`user-create-service`, organization.id.toString()]; - if (name !== "") - cmd.push(`--name`, name); - const key = yield (0, utils_1.sshReq)(...cmd); - fs_1.default.writeFileSync(file, key); - } - catch (e) { - throw e; - } - }); +import fs from "fs"; +import { API_URL, FINGERPRINT, GIT_HOST, SPECIAL_FOLDERS } from "../config.js"; +import { choice, shortText } from "../prompt.js"; +import { Path, RepositoryId, ServiceGroupId, } from "../types.js"; +import { addToExecuteQueue, execPromise, finish, getFiles, outputGit, sshReq, toFolderName, } from "../utils.js"; +import { do_fetch } from "./fetch.js"; +export async function do_create_deployment_agent(organization, name, file) { + try { + outputGit("Creating service user..."); + const cmd = [`user-create-service`, organization.id.toString()]; + if (name !== "") + cmd.push(`--name`, name); + const key = await sshReq(...cmd); + fs.writeFileSync(file, key); + } + catch (e) { + throw e; + } } -exports.BITBUCKET_FILE = "bitbucket-pipelines.yml"; -function bitbucketStep(group_service, repo) { +export const BITBUCKET_FILE = "bitbucket-pipelines.yml"; +export function bitbucketStep(group_service, repo) { return ` - step: name: ${group_service.toString().replace(/\\/g, "/")} script: @@ -47,133 +26,128 @@ function bitbucketStep(group_service, repo) { .toString() .replace(/\\/g, "/")} ${repo}`; } -function do_bitbucket(organization, host, key) { - return __awaiter(this, void 0, void 0, function* () { - try { - const structure = yield (0, fetch_1.do_fetch)(organization); - fs_1.default.writeFileSync(organization.pathTo.with(".merrymake").with("deploy.sh").toString(), `set -o errexit +export async function do_bitbucket(organization, host, key) { + try { + const structure = await do_fetch(organization); + fs.writeFileSync(organization.pathTo.with(".merrymake").with("deploy.sh").toString(), `set -o errexit chmod 600 ${key} eval \`ssh-agent\` ssh-add ${key} -echo "${config_1.API_URL} ssh-ed25519 ${config_1.FINGERPRINT}" >> ~/.ssh/known_hosts +echo "${API_URL} ssh-ed25519 ${FINGERPRINT}" >> ~/.ssh/known_hosts cd $1 git init -git remote add merrymake "${config_1.GIT_HOST}/o${organization.id.toString()}/$2" +git remote add merrymake "${GIT_HOST}/o${organization.id.toString()}/$2" git fetch merrymake git reset merrymake/main || echo "No previous deployment" git config --global user.email "support@merrymake.eu" git config --global user.name "Merrymake" git add -A && (git diff-index --quiet HEAD || git commit -m 'Deploy from BitBucket') export RES=$(git push merrymake HEAD:main --force 2>&1); echo "\${RES}" -case $RES in "Everything up-to-date"*) exit 0 ;; *"if/when the smoke test succeeds"*) exit 0 ;; *"Processed events"*) exit 0 ;; *) echo "Deployment failed"; exit -1 ;; esac`); - const pipelineFile = [ - `pipelines: +case $RES in "Everything up-to-date"*) exit 0 ;; *"Releasing service"*) exit 0 ;; *"Processed events"*) exit 0 ;; *) echo "Deployment failed"; exit -1 ;; esac`); + const pipelineFile = [ + `pipelines: branches: master: - parallel: # SERVICES ARE AUTOMATICALLY ADDED BELOW`, - ]; - const folders = config_1.SPECIAL_FOLDERS.map((x) => ({ localPath: new types_1.Path(x), remotePath: x })); - Object.keys(structure).forEach((serviceGroupId) => { - const group = structure[serviceGroupId]; - const folderName = (0, utils_1.toFolderName)(group.displayName); - const serviceGroup = { - pathTo: organization.pathTo.with(folderName), - id: new types_1.ServiceGroupId(serviceGroupId), + ]; + const folders = SPECIAL_FOLDERS.map((x) => ({ localPath: new Path(x), remotePath: x })); + Object.keys(structure).forEach((serviceGroupId) => { + const group = structure[serviceGroupId]; + const folderName = toFolderName(group.displayName); + const serviceGroup = { + pathTo: organization.pathTo.with(folderName), + id: new ServiceGroupId(serviceGroupId), + }; + Object.keys(group.repositories).forEach((repositoryId) => { + const repositoryDisplayName = group.repositories[repositoryId]; + const folderName = toFolderName(repositoryDisplayName); + const repository = { + pathTo: serviceGroup.pathTo.with(folderName), + id: new RepositoryId(repositoryId), }; - Object.keys(group.repositories).forEach((repositoryId) => { - const repositoryDisplayName = group.repositories[repositoryId]; - const folderName = (0, utils_1.toFolderName)(repositoryDisplayName); - const repository = { - pathTo: serviceGroup.pathTo.with(folderName), - id: new types_1.RepositoryId(repositoryId), - }; - const localPath = repository.pathTo; - const remotePath = `g${serviceGroup.id.toString()}/r${repository.id.toString()}`; - folders.push({ localPath, remotePath }); - }); + const localPath = repository.pathTo; + const remotePath = `g${serviceGroup.id.toString()}/r${repository.id.toString()}`; + folders.push({ localPath, remotePath }); }); - for (let i = 0; i < folders.length; i++) { - const { localPath, remotePath } = folders[i]; - (0, utils_1.output2)(`Processing ${localPath}`); - try { - yield (0, utils_1.execPromise)(`git fetch`, localPath.toString()); - yield (0, utils_1.execPromise)(`git reset origin/main`, localPath.toString()); - } - catch (e) { } - fs_1.default.rmSync(localPath.with(".git").toString(), { - recursive: true, - force: true, - }); - pipelineFile.push(bitbucketStep(localPath, remotePath)); + }); + for (let i = 0; i < folders.length; i++) { + const { localPath, remotePath } = folders[i]; + outputGit(`Processing ${localPath}`); + try { + await execPromise(`git fetch`, localPath.toString()); + await execPromise(`git reset origin/main`, localPath.toString()); } - fs_1.default.writeFileSync(organization.pathTo.with(exports.BITBUCKET_FILE).toString(), pipelineFile.join("\n")); - yield (0, utils_1.execPromise)(`git init`, organization.pathTo.toString()); - yield (0, utils_1.execPromise)(`git update-index --add --chmod=+x .merrymake/deploy.sh`, organization.pathTo.toString()); - yield (0, utils_1.execPromise)(`git remote add origin ${host}`, organization.pathTo.toString()); - } - catch (e) { - throw e; + catch (e) { } + fs.rmSync(localPath.with(".git").toString(), { + recursive: true, + force: true, + }); + pipelineFile.push(bitbucketStep(localPath, remotePath)); } - }); + fs.writeFileSync(organization.pathTo.with(BITBUCKET_FILE).toString(), pipelineFile.join("\n")); + await execPromise(`git init`, organization.pathTo.toString()); + // For mac + await execPromise(`chmod +x .merrymake/deploy.sh`, organization.pathTo.toString()); + // For windows + await execPromise(`git update-index --add --chmod=+x .merrymake/deploy.sh`, organization.pathTo.toString()); + await execPromise(`git remote add origin ${host}`, organization.pathTo.toString()); + } + catch (e) { + throw e; + } } function hosting_bitbucket_key_host(organization, host, key) { - (0, utils_1.addToExecuteQueue)(() => do_bitbucket(organization, host, key)); - return (0, utils_1.finish)(); + addToExecuteQueue(() => do_bitbucket(organization, host, key)); + return finish(); } -function hosting_bitbucket_key(organization, file) { - return __awaiter(this, void 0, void 0, function* () { - try { - const host = yield (0, prompt_1.shortText)("Bitbucket repo", "The URL to the bitbucket mono-repository.", `https://...`).then(); - return hosting_bitbucket_key_host(organization, host, file); - } - catch (e) { - throw e; - } - }); +async function hosting_bitbucket_key(organization, file) { + try { + const host = await shortText("Bitbucket repo", "The URL to the bitbucket mono-repository.", `https://...`).then(); + return hosting_bitbucket_key_host(organization, host, file); + } + catch (e) { + throw e; + } } -function hosting_bitbucket_create(organization) { - return __awaiter(this, void 0, void 0, function* () { - try { - const name = yield (0, prompt_1.shortText)("Name", "Display name for the service user", `Service User`).then(); - const file = ".merrymake/" + (0, utils_1.toFolderName)(name) + ".key"; - (0, utils_1.addToExecuteQueue)(() => do_create_deployment_agent(organization, name, file)); - return hosting_bitbucket_key(organization, file); - } - catch (e) { - throw e; - } - }); +async function hosting_bitbucket_create(organization) { + try { + const name = await shortText("Name", "Display name for the service user", `Service User`).then(); + const file = ".merrymake/" + toFolderName(name) + ".key"; + addToExecuteQueue(() => do_create_deployment_agent(organization, name, file)); + return hosting_bitbucket_key(organization, file); + } + catch (e) { + throw e; + } } -function hosting_bitbucket(organization) { - return __awaiter(this, void 0, void 0, function* () { - try { - const keyfiles = (0, utils_1.getFiles)(organization.pathTo.with(`.merrymake`)).filter((x) => x.endsWith(".key")); - const options = keyfiles.map((x) => { - const f = x.substring(0, x.length - ".key".length); - return { - long: f, - text: `use service user ${f}`, - action: () => hosting_bitbucket_key(organization, `.merrymake/${f}.key`), - }; - }); - options.push({ - long: `create`, - short: `c`, - text: `create service user`, - action: () => hosting_bitbucket_create(organization), - }); - return yield (0, prompt_1.choice)("Which service user would you like to use?", options, { - invertedQuiet: { cmd: false, select: true }, - }).then(); - } - catch (e) { - throw e; - } - }); +async function hosting_bitbucket(organization) { + try { + const keyfiles = getFiles(organization.pathTo.with(`.merrymake`)).filter((x) => x.endsWith(".key")); + const options = keyfiles.map((x) => { + const f = x.substring(0, x.length - ".key".length); + return { + long: f, + text: `use service user ${f}`, + action: () => hosting_bitbucket_key(organization, `.merrymake/${f}.key`), + }; + }); + options.push({ + long: `create`, + short: `c`, + text: `create service user`, + action: () => hosting_bitbucket_create(organization), + }); + return await choice("Which service user would you like to use?", options, { + invertedQuiet: { cmd: false, select: true }, + }).then(); + } + catch (e) { + throw e; + } } -function hosting(organization) { - return (0, prompt_1.choice)("Which host would you like to use?", [ +export function hosting(organization) { + return choice("Which host would you like to use?", [ { long: "bitbucket", short: "b", diff --git a/newCommands/hosting.ts b/newCommands/hosting.ts index c8efab3..a25690e 100644 --- a/newCommands/hosting.ts +++ b/newCommands/hosting.ts @@ -1,31 +1,25 @@ import fs from "fs"; -import { - API_URL, - FINGERPRINT, - GIT_HOST, - MERRYMAKE_IO, - SPECIAL_FOLDERS, -} from "../config"; -import { Option, choice, shortText } from "../prompt"; +import { API_URL, FINGERPRINT, GIT_HOST, SPECIAL_FOLDERS } from "../config.js"; +import { Option, choice, shortText } from "../prompt.js"; import { Organization, Path, PathTo, - Repository, RepositoryId, + RepositoryWithId, ServiceGroup, ServiceGroupId, -} from "../types"; +} from "../types.js"; import { addToExecuteQueue, execPromise, finish, getFiles, - output2, + outputGit, sshReq, toFolderName, -} from "../utils"; -import { ToBeStructure, do_fetch, ensureGroupStructure } from "./fetch"; +} from "../utils.js"; +import { do_fetch } from "./fetch.js"; export async function do_create_deployment_agent( organization: Organization, @@ -33,7 +27,7 @@ export async function do_create_deployment_agent( file: string ) { try { - output2("Creating service user..."); + outputGit("Creating service user..."); const cmd = [`user-create-service`, organization.id.toString()]; if (name !== "") cmd.push(`--name`, name); const key = await sshReq(...cmd); @@ -76,7 +70,7 @@ git config --global user.email "support@merrymake.eu" git config --global user.name "Merrymake" git add -A && (git diff-index --quiet HEAD || git commit -m 'Deploy from BitBucket') export RES=$(git push merrymake HEAD:main --force 2>&1); echo "\${RES}" -case $RES in "Everything up-to-date"*) exit 0 ;; *"if/when the smoke test succeeds"*) exit 0 ;; *"Processed events"*) exit 0 ;; *) echo "Deployment failed"; exit -1 ;; esac` +case $RES in "Everything up-to-date"*) exit 0 ;; *"Releasing service"*) exit 0 ;; *"Processed events"*) exit 0 ;; *) echo "Deployment failed"; exit -1 ;; esac` ); const pipelineFile = [ `pipelines: @@ -97,7 +91,7 @@ case $RES in "Everything up-to-date"*) exit 0 ;; *"if/when the smoke test succee Object.keys(group.repositories).forEach((repositoryId) => { const repositoryDisplayName = group.repositories[repositoryId]; const folderName = toFolderName(repositoryDisplayName); - const repository: Repository = { + const repository: RepositoryWithId = { pathTo: serviceGroup.pathTo.with(folderName), id: new RepositoryId(repositoryId), }; @@ -108,7 +102,7 @@ case $RES in "Everything up-to-date"*) exit 0 ;; *"if/when the smoke test succee }); for (let i = 0; i < folders.length; i++) { const { localPath, remotePath } = folders[i]; - output2(`Processing ${localPath}`); + outputGit(`Processing ${localPath}`); try { await execPromise(`git fetch`, localPath.toString()); await execPromise(`git reset origin/main`, localPath.toString()); @@ -124,6 +118,12 @@ case $RES in "Everything up-to-date"*) exit 0 ;; *"if/when the smoke test succee pipelineFile.join("\n") ); await execPromise(`git init`, organization.pathTo.toString()); + // For mac + await execPromise( + `chmod +x .merrymake/deploy.sh`, + organization.pathTo.toString() + ); + // For windows await execPromise( `git update-index --add --chmod=+x .merrymake/deploy.sh`, organization.pathTo.toString() diff --git a/newCommands/index.js b/newCommands/index.js index 64f0fb5..dcb2006 100644 --- a/newCommands/index.js +++ b/newCommands/index.js @@ -1,193 +1,197 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.index = index; -const fs_1 = __importDefault(require("fs")); -const path_1 = __importDefault(require("path")); -const prompt_1 = require("../prompt"); -const types_1 = require("../types"); -const utils_1 = require("../utils"); -const apikey_1 = require("./apikey"); -const deploy_1 = require("./deploy"); -const envvar_1 = require("./envvar"); -const event_1 = require("./event"); -const fetch_1 = require("./fetch"); -const group_1 = require("./group"); -const hosting_1 = require("./hosting"); -const org_1 = require("./org"); -const queue_1 = require("./queue"); -const register_1 = require("./register"); -const repo_1 = require("./repo"); -const role_1 = require("./role"); -const build_1 = require("./build"); -function getContext() { - return __awaiter(this, void 0, void 0, function* () { - let repository; - let serviceGroup; - let organization; - const cwd = process.cwd().split(/\/|\\/); - let out = "."; - for (let i = cwd.length - 1; i >= 0; i--) { - if (fs_1.default.existsSync(path_1.default.join(out, "merrymake.json"))) { - if (fs_1.default.existsSync(path_1.default.join(out, ".git"))) { - const repositoryUrl = yield (0, utils_1.execPromise)(`git ls-remote --get-url origin`); - const buffer = repositoryUrl.split("/"); - repository = { - id: new types_1.RepositoryId(buffer[buffer.length - 1]), - pathTo: new types_1.PathToRepository(out), - }; - } - // TODO bitbucket - } - else if (fs_1.default.existsSync(path_1.default.join(out, ".group-id"))) { - serviceGroup = { - id: new types_1.ServiceGroupId(fs_1.default.readFileSync(path_1.default.join(out, ".group-id")).toString()), - pathTo: new types_1.PathToServiceGroup(out), +import fs from "fs"; +import path from "path"; +import { do_startSimulator } from "../Execute.js"; +import { choice } from "../prompt.js"; +import { OrganizationId, PathToOrganization, PathToRepository, PathToServiceGroup, RepositoryId, ServiceGroupId, } from "../types.js"; +import { execPromise } from "../utils.js"; +import { key } from "./apikey.js"; +import { build } from "./build.js"; +import { deploy } from "./deploy.js"; +import { envvar } from "./envvar.js"; +import { event } from "./event.js"; +import { fetch } from "./fetch.js"; +import { group } from "./group.js"; +import { BITBUCKET_FILE, hosting } from "./hosting.js"; +import { orgAction, rename } from "./org.js"; +import { queue } from "./queue.js"; +import { register } from "./register.js"; +import { repo } from "./repo.js"; +import { role } from "./role.js"; +async function getContext() { + let repository; + let serviceGroup; + let organization; + let inGit = false; + const cwd = process.cwd().split(/\/|\\/); + let out = "."; + for (let i = cwd.length - 1; i >= 0; i--) { + if (fs.existsSync(path.join(out, ".git"))) + inGit = true; + if (fs.existsSync(path.join(out, "merrymake.json"))) { + if (fs.existsSync(path.join(out, ".git"))) { + const repositoryUrl = await execPromise(`git ls-remote --get-url origin`); + const buffer = repositoryUrl.split("/"); + repository = { + id: new RepositoryId(buffer[buffer.length - 1]), + pathTo: new PathToRepository(out), }; } - else if (fs_1.default.existsSync(path_1.default.join(out, ".merrymake", "conf.json"))) { - const orgFile = JSON.parse(fs_1.default.readFileSync(path_1.default.join(out, ".merrymake", "conf.json")).toString()); - organization = { - id: new types_1.OrganizationId(orgFile.organizationId), - pathTo: new types_1.PathToOrganization(out), + else { + repository = { + pathTo: new PathToRepository(out), }; - return { repository, serviceGroup, organization }; } - out += ".." + path_1.default.sep; + // TODO bitbucket + } + else if (fs.existsSync(path.join(out, ".group-id"))) { + serviceGroup = { + id: new ServiceGroupId(fs.readFileSync(path.join(out, ".group-id")).toString()), + pathTo: new PathToServiceGroup(out), + }; + } + else if (fs.existsSync(path.join(out, ".merrymake", "conf.json"))) { + const orgFile = JSON.parse(fs.readFileSync(path.join(out, ".merrymake", "conf.json")).toString()); + organization = { + id: new OrganizationId(orgFile.organizationId), + pathTo: new PathToOrganization(out), + }; + return { repository, serviceGroup, organization, inGit }; } - return { - repository, - serviceGroup, - organization, - }; - }); + out += ".." + path.sep; + } + return { + repository, + serviceGroup, + organization, + }; } -function index() { - return __awaiter(this, void 0, void 0, function* () { - try { - const options = []; - const { repository, serviceGroup, organization } = yield getContext(); - if (fs_1.default.existsSync(`.git`)) { - options.push({ - long: "deploy", - short: "d", - text: "deploy service to the cloud", - weight: 900, - action: () => (0, deploy_1.deploy)(), - }); - } - if (repository !== undefined) { - options.push({ - long: "build", - short: "b", - text: "build service locally", - weight: 800, - action: () => (0, build_1.build)(), - }); - } - if (serviceGroup !== undefined) { +export async function index() { + try { + const options = []; + const { repository, serviceGroup, organization, inGit } = await getContext(); + if (inGit) { + options.push({ + long: "deploy", + short: "d", + text: "deploy service with git", + weight: 900, + action: () => deploy(), + }); + } + if (repository !== undefined) { + options.push({ + long: "build", + short: "b", + text: "build service locally", + weight: 800, + action: () => build(), + }); + } + if (serviceGroup !== undefined) { + options.push({ + long: "envvar", + short: "e", + text: "add or edit envvar for service group", + weight: 800, + action: () => envvar(organization.pathTo, organization.id, serviceGroup.id), + }, { + long: "repo", + short: "r", + text: "add or edit repository", + weight: 700, + action: () => repo(organization, serviceGroup), + }); + } + if (organization !== undefined) { + if (!fs.existsSync(organization.pathTo.with(BITBUCKET_FILE).toString())) { options.push({ - long: "envvar", - short: "e", - text: "add or edit envvar for service group", - weight: 800, - action: () => (0, envvar_1.envvar)(organization.pathTo, organization.id, serviceGroup.id), - }, { - long: "repo", - short: "r", - text: "add or edit repository", - weight: 700, - action: () => (0, repo_1.repo)(organization, serviceGroup), + long: "fetch", + short: "f", + text: "fetch updates to service groups and repos", + weight: 600, + action: () => fetch(organization), }); } - if (organization !== undefined) { - if (!fs_1.default.existsSync(organization.pathTo.with(hosting_1.BITBUCKET_FILE).toString())) { - options.push({ - long: "fetch", - short: "f", - text: "fetch updates to service groups and repos", - weight: 600, - action: () => (0, fetch_1.fetch)(organization), - }); + if (serviceGroup === undefined) { + if (!fs.existsSync(organization.pathTo.with(BITBUCKET_FILE).toString())) { options.push({ long: "hosting", short: "h", text: "configure git hosting with bitbucket", weight: 100, - action: () => (0, hosting_1.hosting)(organization), - }); - } - if (serviceGroup === undefined) { - options.push({ - long: "group", - short: "g", - text: "create a service group", - weight: 500, - action: () => (0, group_1.group)(organization), - }, { - long: "role", - short: "o", - text: "add or assign roles to users in the organization", - weight: 200, - action: () => (0, role_1.role)(organization), - }, { - long: "rename", - short: "_", - text: "rename the organization", - weight: 1, - action: () => (0, org_1.rename)(organization.id), + action: () => hosting(organization), }); } options.push({ - long: "rapids", - short: "q", - text: "view or post messages to the rapids", - weight: 1000, - action: () => (0, queue_1.queue)(organization.id), - }, { - long: "key", - short: "k", - text: "add or edit api-keys for the organization", - weight: 400, - action: () => (0, apikey_1.key)(organization.id), + long: "delete", + short: "d", + text: "delete a service group", + weight: 100, + action: () => hosting(organization), }, { - long: "event", - short: "v", - text: "allow or disallow events through api-keys for the organization", - weight: 300, - action: () => (0, event_1.event)(organization.id), - }); - } - else if (organization === undefined) { - options.push({ - long: "start", - text: "start for new user or new device", - weight: 900, - action: () => (0, register_1.register)(), + long: "group", + short: "g", + text: "create a service group", + weight: 500, + action: () => group(organization), }, { - long: "org", + long: "role", short: "o", - text: "manage or checkout organizations", - weight: 500, - action: () => (0, org_1.orgAction)(), + text: "add or assign roles to users in the organization", + weight: 200, + action: () => role(organization), + }, { + long: "rename", + short: "_", + text: "rename the organization", + weight: 1, + action: () => rename(organization.id), }); } - options.sort((a, b) => b.weight - a.weight); - return (0, prompt_1.choice)("What would you like to do?", options); + options.push({ + long: "rapids", + short: "q", + text: "view or post messages to the rapids", + weight: 1000, + action: () => queue(organization.id), + }, { + long: "sim", + short: "s", + text: "simulate your system locally", + weight: 700, + action: () => do_startSimulator(organization.pathTo.toString()), + }, { + long: "key", + short: "k", + text: "add or edit api-keys for the organization", + weight: 400, + action: () => key(organization.id), + }, { + long: "event", + short: "v", + text: "allow or disallow events through api-keys for the organization", + weight: 300, + action: () => event(organization.id), + }); } - catch (e) { - throw e; + else if (organization === undefined) { + options.push({ + long: "start", + text: "start for new user or new device", + weight: 900, + action: () => register(), + }, { + long: "org", + short: "o", + text: "manage or checkout organizations", + weight: 500, + action: () => orgAction(), + }); } - }); + options.sort((a, b) => b.weight - a.weight); + return choice("What would you like to do?", options); + } + catch (e) { + throw e; + } } diff --git a/newCommands/index.ts b/newCommands/index.ts index 239d0be..9a5c8a0 100644 --- a/newCommands/index.ts +++ b/newCommands/index.ts @@ -1,6 +1,7 @@ import fs from "fs"; import path from "path"; -import { Option, choice } from "../prompt"; +import { do_startSimulator } from "../Execute.js"; +import { Option, choice } from "../prompt.js"; import { Organization, OrganizationId, @@ -9,31 +10,34 @@ import { PathToServiceGroup, Repository, RepositoryId, + RepositoryWithId, ServiceGroup, ServiceGroupId, -} from "../types"; -import { OrgFile, execPromise } from "../utils"; -import { key } from "./apikey"; -import { deploy } from "./deploy"; -import { envvar } from "./envvar"; -import { event } from "./event"; -import { fetch } from "./fetch"; -import { group } from "./group"; -import { BITBUCKET_FILE, hosting } from "./hosting"; -import { orgAction, rename } from "./org"; -import { queue } from "./queue"; -import { register } from "./register"; -import { repo } from "./repo"; -import { role } from "./role"; -import { build } from "./build"; +} from "../types.js"; +import { OrgFile, execPromise } from "../utils.js"; +import { key } from "./apikey.js"; +import { build } from "./build.js"; +import { deploy } from "./deploy.js"; +import { envvar } from "./envvar.js"; +import { event } from "./event.js"; +import { fetch } from "./fetch.js"; +import { group } from "./group.js"; +import { BITBUCKET_FILE, hosting } from "./hosting.js"; +import { orgAction, rename } from "./org.js"; +import { queue } from "./queue.js"; +import { register } from "./register.js"; +import { repo } from "./repo.js"; +import { role } from "./role.js"; async function getContext() { - let repository: Repository | undefined; + let repository: Repository | RepositoryWithId | undefined; let serviceGroup: ServiceGroup | undefined; let organization: Organization | undefined; + let inGit = false; const cwd = process.cwd().split(/\/|\\/); let out = "."; for (let i = cwd.length - 1; i >= 0; i--) { + if (fs.existsSync(path.join(out, ".git"))) inGit = true; if (fs.existsSync(path.join(out, "merrymake.json"))) { if (fs.existsSync(path.join(out, ".git"))) { const repositoryUrl = await execPromise( @@ -44,6 +48,10 @@ async function getContext() { id: new RepositoryId(buffer[buffer.length - 1]), pathTo: new PathToRepository(out), }; + } else { + repository = { + pathTo: new PathToRepository(out), + }; } // TODO bitbucket } else if (fs.existsSync(path.join(out, ".group-id"))) { @@ -61,7 +69,7 @@ async function getContext() { id: new OrganizationId(orgFile.organizationId), pathTo: new PathToOrganization(out), }; - return { repository, serviceGroup, organization }; + return { repository, serviceGroup, organization, inGit }; } out += ".." + path.sep; } @@ -75,12 +83,13 @@ async function getContext() { export async function index() { try { const options: (Option & { weight: number })[] = []; - const { repository, serviceGroup, organization } = await getContext(); - if (fs.existsSync(`.git`)) { + const { repository, serviceGroup, organization, inGit } = + await getContext(); + if (inGit) { options.push({ long: "deploy", short: "d", - text: "deploy service to the cloud", + text: "deploy service with git", weight: 900, action: () => deploy(), }); @@ -122,16 +131,27 @@ export async function index() { weight: 600, action: () => fetch(organization), }); - options.push({ - long: "hosting", - short: "h", - text: "configure git hosting with bitbucket", - weight: 100, - action: () => hosting(organization), - }); } if (serviceGroup === undefined) { + if ( + !fs.existsSync(organization.pathTo.with(BITBUCKET_FILE).toString()) + ) { + options.push({ + long: "hosting", + short: "h", + text: "configure git hosting with bitbucket", + weight: 100, + action: () => hosting(organization), + }); + } options.push( + { + long: "delete", + short: "d", + text: "delete a service group", + weight: 100, + action: () => hosting(organization), + }, { long: "group", short: "g", @@ -163,6 +183,13 @@ export async function index() { weight: 1000, action: () => queue(organization.id), }, + { + long: "sim", + short: "s", + text: "simulate your system locally", + weight: 700, + action: () => do_startSimulator(organization.pathTo.toString()), + }, { long: "key", short: "k", diff --git a/newCommands/org.js b/newCommands/org.js index 5e2aed2..fadd10a 100644 --- a/newCommands/org.js +++ b/newCommands/org.js @@ -1,177 +1,135 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.do_createOrganization = do_createOrganization; -exports.do_renameOrganization = do_renameOrganization; -exports.rename = rename; -exports.generateOrgName = generateOrgName; -exports.org = org; -exports.do_join = do_join; -exports.listOrgs = listOrgs; -exports.orgAction = orgAction; -const prompt_1 = require("../prompt"); -const types_1 = require("../types"); -const utils_1 = require("../utils"); -const words_1 = require("../words"); -const clone_1 = require("./clone"); -const group_1 = require("./group"); -function do_createOrganization(folderName, displayName) { - return __awaiter(this, void 0, void 0, function* () { - try { - const reply = yield (0, utils_1.sshReq)(`organization-create`, displayName); - if (reply.length !== 8) - throw reply; - const organizationId = new types_1.OrganizationId(reply); - yield (0, clone_1.do_clone)({}, folderName, displayName, organizationId); - return organizationId; - } - catch (e) { - throw e; - } - }); -} -function do_renameOrganization(organizationId, displayName) { - return __awaiter(this, void 0, void 0, function* () { - try { - const reply = yield (0, utils_1.sshReq)(`organization-rename`, displayName, `--organizationId`, organizationId.toString()); - } - catch (e) { - throw e; - } - }); +import { choice, shortText } from "../prompt.js"; +import { OrganizationId, PathToOrganization } from "../types.js"; +import { addToExecuteQueue, digits, finish, generateString, lowercase, outputGit, sshReq, toFolderName, } from "../utils.js"; +import { ADJECTIVE, NOUN } from "../words.js"; +import { checkout, checkout_org, do_clone } from "./clone.js"; +import { group } from "./group.js"; +export async function do_createOrganization(folderName, displayName) { + try { + const reply = await sshReq(`organization-create`, displayName); + if (reply.length !== 8) + throw reply; + const organizationId = new OrganizationId(reply); + await do_clone({}, folderName, displayName, organizationId); + return organizationId; + } + catch (e) { + throw e; + } } -function rename(organizationId) { - return __awaiter(this, void 0, void 0, function* () { - try { - const displayName = yield (0, prompt_1.shortText)("Organization name", "Used when collaborating with others.", "Acme Anvils").then(); - (0, utils_1.addToExecuteQueue)(() => do_renameOrganization(organizationId, displayName)); - return (0, utils_1.finish)(); - } - catch (e) { - throw e; - } - }); +export async function do_renameOrganization(organizationId, displayName) { + try { + const reply = await sshReq(`organization-rename`, displayName, `--organizationId`, organizationId.toString()); + } + catch (e) { + throw e; + } } -const characters = "abcdefghijklmnopqrstuvwxyz0123456789"; -function generateString(length) { - let result = ""; - for (let i = 0; i < length; i++) { - result += characters.charAt(Math.floor(Math.random() * characters.length)); +export async function rename(organizationId) { + try { + const displayName = await shortText("Organization name", "Used when collaborating with others.", "Acme Anvils").then(); + addToExecuteQueue(() => do_renameOrganization(organizationId, displayName)); + return finish(); + } + catch (e) { + throw e; } - return result; } -function generateOrgName() { +export function generateOrgName() { if (process.env["MERRYMAKE_NAME_LENGTH"] !== undefined && !Number.isNaN(+process.env["MERRYMAKE_NAME_LENGTH"])) { const base = `org-${new Date().toLocaleDateString().replace(/\//g, "-")}-`; - return (base + generateString(+process.env["MERRYMAKE_NAME_LENGTH"] - base.length)); + return (base + + generateString(+process.env["MERRYMAKE_NAME_LENGTH"] - base.length, lowercase + digits)); } else - return (words_1.ADJECTIVE[~~(words_1.ADJECTIVE.length * Math.random())] + + return (ADJECTIVE[~~(ADJECTIVE.length * Math.random())] + "-" + - words_1.NOUN[~~(words_1.NOUN.length * Math.random())] + + NOUN[~~(NOUN.length * Math.random())] + "-" + - words_1.NOUN[~~(words_1.NOUN.length * Math.random())]); + NOUN[~~(NOUN.length * Math.random())]); } -function org() { - return __awaiter(this, void 0, void 0, function* () { - try { - const orgName = generateOrgName(); - const displayName = yield (0, prompt_1.shortText)("Organization name", "Used when collaborating with others.", orgName).then(); - const folderName = (0, utils_1.toFolderName)(displayName); - const organizationId = yield do_createOrganization(folderName, displayName); - return (0, group_1.group)({ - pathTo: new types_1.PathToOrganization(folderName), - id: organizationId, - }); - } - catch (e) { - throw e; - } - }); +export async function org() { + try { + const orgName = generateOrgName(); + const displayName = await shortText("Organization name", "Used when collaborating with others.", orgName).then(); + const folderName = toFolderName(displayName); + const organizationId = await do_createOrganization(folderName, displayName); + return group({ + pathTo: new PathToOrganization(folderName), + id: organizationId, + }); + } + catch (e) { + throw e; + } } -function do_join(org) { - return __awaiter(this, void 0, void 0, function* () { - try { - (0, utils_1.output2)(yield (0, utils_1.sshReq)(`me-join`, org)); - } - catch (e) { - throw e; - } - }); +export async function do_join(org) { + try { + outputGit(await sshReq(`me-join`, org)); + } + catch (e) { + throw e; + } } function join_org(name) { // TODO join, wait, then checkout - (0, utils_1.addToExecuteQueue)(() => do_join(name)); - return (0, utils_1.finish)(); + addToExecuteQueue(() => do_join(name)); + return finish(); } -function join() { - return __awaiter(this, void 0, void 0, function* () { - try { - const name = yield (0, prompt_1.shortText)("Organization to join", "Name of the organization you wish to request access to.", null).then(); - return join_org(name); - } - catch (e) { - throw e; - } - }); +async function join() { + try { + const name = await shortText("Organization to join", "Name of the organization you wish to request access to.", null).then(); + return join_org(name); + } + catch (e) { + throw e; + } } let orgListCache; -function listOrgs() { - return __awaiter(this, void 0, void 0, function* () { - if (orgListCache === undefined) { - const resp = yield (0, utils_1.sshReq)(`organization-list`); - if (!resp.startsWith("[")) - throw resp; - orgListCache = JSON.parse(resp); - } - return orgListCache; - }); +export async function listOrgs() { + if (orgListCache === undefined) { + const resp = await sshReq(`organization-list`); + if (!resp.startsWith("[")) + throw resp; + orgListCache = JSON.parse(resp); + } + return orgListCache; } -function orgAction() { - return __awaiter(this, void 0, void 0, function* () { - try { - const orgs = yield listOrgs(); - const options = []; - if (orgs.length > 0) { - options.push({ - long: orgs[0].id, - text: `checkout ${orgs[0].name} (${orgs[0].id})`, - action: () => (0, clone_1.checkout_org)(orgs[0].name, new types_1.OrganizationId(orgs[0].id)), - }); - } - if (orgs.length > 1) { - options.push({ - long: "checkout", - short: "c", - text: `checkout another organization`, - action: () => (0, clone_1.checkout)(), - }); - } +export async function orgAction() { + try { + const orgs = await listOrgs(); + const options = []; + if (orgs.length > 0) { options.push({ - long: "new", - short: "n", - text: `create new organization`, - action: () => org(), + long: orgs[0].id, + text: `checkout ${orgs[0].name} (${orgs[0].id})`, + action: () => checkout_org(orgs[0].name, new OrganizationId(orgs[0].id)), }); + } + if (orgs.length > 1) { options.push({ - long: "join", - short: "j", - text: `join an existing organization`, - action: () => join(), + long: "checkout", + short: "c", + text: `checkout another organization`, + action: () => checkout(), }); - return (0, prompt_1.choice)("Which organization will you work with?", options); } - catch (e) { - throw e; - } - }); + options.push({ + long: "new", + short: "n", + text: `create new organization`, + action: () => org(), + }); + options.push({ + long: "join", + short: "j", + text: `join an existing organization`, + action: () => join(), + }); + return choice("Which organization will you work with?", options); + } + catch (e) { + throw e; + } } diff --git a/newCommands/org.ts b/newCommands/org.ts index 5b5501e..a0c4589 100644 --- a/newCommands/org.ts +++ b/newCommands/org.ts @@ -1,16 +1,18 @@ -import { Option, choice, shortText } from "../prompt"; -import { OrganizationId, PathToOrganization } from "../types"; +import { Option, choice, shortText } from "../prompt.js"; +import { OrganizationId, PathToOrganization } from "../types.js"; import { - Path, addToExecuteQueue, + digits, finish, - output2, + generateString, + lowercase, + outputGit, sshReq, toFolderName, -} from "../utils"; -import { ADJECTIVE, NOUN } from "../words"; -import { checkout, checkout_org, do_clone } from "./clone"; -import { group } from "./group"; +} from "../utils.js"; +import { ADJECTIVE, NOUN } from "../words.js"; +import { checkout, checkout_org, do_clone } from "./clone.js"; +import { group } from "./group.js"; export async function do_createOrganization( folderName: string, @@ -57,17 +59,6 @@ export async function rename(organizationId: OrganizationId) { } } -const characters = "abcdefghijklmnopqrstuvwxyz0123456789"; - -function generateString(length: number) { - let result = ""; - for (let i = 0; i < length; i++) { - result += characters.charAt(Math.floor(Math.random() * characters.length)); - } - - return result; -} - export function generateOrgName() { if ( process.env["MERRYMAKE_NAME_LENGTH"] !== undefined && @@ -75,7 +66,11 @@ export function generateOrgName() { ) { const base = `org-${new Date().toLocaleDateString().replace(/\//g, "-")}-`; return ( - base + generateString(+process.env["MERRYMAKE_NAME_LENGTH"] - base.length) + base + + generateString( + +process.env["MERRYMAKE_NAME_LENGTH"] - base.length, + lowercase + digits + ) ); } else return ( @@ -108,7 +103,7 @@ export async function org() { export async function do_join(org: string) { try { - output2(await sshReq(`me-join`, org)); + outputGit(await sshReq(`me-join`, org)); } catch (e) { throw e; } diff --git a/newCommands/post.js b/newCommands/post.js index 3ad3620..17c077c 100644 --- a/newCommands/post.js +++ b/newCommands/post.js @@ -1,145 +1,88 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; +import { optimisticMimeTypeOf } from "@merrymake/ext2mime"; +import fs, { readdirSync } from "fs"; +import { RAPIDS_HOST } from "../config.js"; +import { choice, shortText } from "../prompt.js"; +import { addToExecuteQueue, finish, outputGit, sshReq, urlReq, } from "../utils.js"; +import { key_create } from "./apikey.js"; +export async function do_post(eventType, key, contentType, payload) { + try { + outputGit(`Sending POST request to ${RAPIDS_HOST}/${key}/${eventType}`); + const resp = await urlReq(`${RAPIDS_HOST}/${key}/${eventType}`, "POST", payload, contentType); + outputGit(resp.body); + } + catch (e) { + throw e; } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.do_post = do_post; -exports.do_post_file = do_post_file; -exports.post = post; -const ext2mime_1 = require("@merrymake/ext2mime"); -const fs_1 = __importStar(require("fs")); -const config_1 = require("../config"); -const prompt_1 = require("../prompt"); -const utils_1 = require("../utils"); -const apikey_1 = require("./apikey"); -function do_post(eventType, key, contentType, payload) { - return __awaiter(this, void 0, void 0, function* () { - try { - (0, utils_1.output2)(`Sending POST request to ${config_1.RAPIDS_HOST}/${key}/${eventType}`); - const resp = yield (0, utils_1.urlReq)(`${config_1.RAPIDS_HOST}/${key}/${eventType}`, "POST", payload, contentType); - (0, utils_1.output2)(resp.body); - } - catch (e) { - throw e; - } - }); } -function do_post_file(eventType, key, filename) { - return __awaiter(this, void 0, void 0, function* () { - try { - const content = fs_1.default.readFileSync(filename).toString(); - const type = (0, ext2mime_1.optimisticMimeTypeOf)(filename.substring(filename.lastIndexOf(".") + 1)); - if (type === null) - throw "Could not determine content type"; - (0, utils_1.output2)(`Sending POST request to ${config_1.RAPIDS_HOST}/${key}/${eventType}`); - const resp = yield (0, utils_1.urlReq)(`${config_1.RAPIDS_HOST}/${key}/${eventType}`, "POST", content, type.toString()); - (0, utils_1.output2)(resp.body); - } - catch (e) { - throw e; - } - }); +export async function do_post_file(eventType, key, filename) { + try { + const content = fs.readFileSync(filename).toString(); + const type = optimisticMimeTypeOf(filename.substring(filename.lastIndexOf(".") + 1)); + if (type === null) + throw "Could not determine content type"; + outputGit(`Sending POST request to ${RAPIDS_HOST}/${key}/${eventType}`); + const resp = await urlReq(`${RAPIDS_HOST}/${key}/${eventType}`, "POST", content, type.toString()); + outputGit(resp.body); + } + catch (e) { + throw e; + } } function post_event_payload_key(foo) { - (0, utils_1.addToExecuteQueue)(foo); - return (0, utils_1.finish)(); + addToExecuteQueue(foo); + return finish(); } -function post_key(organizationId, foo) { - return __awaiter(this, void 0, void 0, function* () { - try { - const resp = yield (0, utils_1.sshReq)(`apikey-list`, organizationId.toString()); - const keys = JSON.parse(resp); - const options = keys.map((x) => { - const n = x.name ? ` (${x.name})` : ""; - return { - long: x.id, - text: `${x.id}${n}`, - action: () => post_event_payload_key(() => foo(x.id)), - }; - }); - options.push({ - long: `new`, - short: `n`, - text: `add a new apikey`, - action: () => (0, apikey_1.key_create)(organizationId, (key) => post_event_payload_key(() => foo(key))), - }); - return yield (0, prompt_1.choice)("Which key to post through?", options).then(); - } - catch (e) { - throw e; - } - }); +async function post_key(organizationId, foo) { + try { + const resp = await sshReq(`apikey-list`, organizationId.toString()); + const keys = JSON.parse(resp); + const options = keys.map((x) => { + const n = x.name ? ` (${x.name})` : ""; + return { + long: x.id, + text: `${x.id}${n}`, + action: () => post_event_payload_key(() => foo(x.id)), + }; + }); + options.push({ + long: `new`, + short: `n`, + text: `add a new apikey`, + action: () => key_create(organizationId, (key) => post_event_payload_key(() => foo(key))), + }); + return await choice("Which key to post through?", options).then(); + } + catch (e) { + throw e; + } } -function post_event_payload_type(organizationId, eventType, contentType) { - return __awaiter(this, void 0, void 0, function* () { - try { - const payload = yield (0, prompt_1.shortText)("Payload", "The data to be attached to the request", "").then(); - return post_key(organizationId, (key) => do_post(eventType, key, contentType, payload)); - } - catch (e) { - throw e; - } - }); +async function post_event_payload_type(organizationId, eventType, contentType) { + try { + const payload = await shortText("Payload", "The data to be attached to the request", "").then(); + return post_key(organizationId, (key) => do_post(eventType, key, contentType, payload)); + } + catch (e) { + throw e; + } } -function post_event_payload_file(organizationId, eventType) { - return __awaiter(this, void 0, void 0, function* () { - try { - const files = (0, fs_1.readdirSync)(".", { withFileTypes: true }).flatMap((x) => x.isDirectory() ? [] : [x.name]); - const options = files.map((x) => { - return { - long: x, - text: x, - action: () => post_key(organizationId, (key) => do_post_file(eventType, key, x)), - }; - }); - return yield (0, prompt_1.choice)("Which file would you like to send?", options, {}).then((x) => x); - } - catch (e) { - throw e; - } - }); +async function post_event_payload_file(organizationId, eventType) { + try { + const files = readdirSync(".", { withFileTypes: true }).flatMap((x) => x.isDirectory() ? [] : [x.name]); + const options = files.map((x) => { + return { + long: x, + text: x, + action: () => post_key(organizationId, (key) => do_post_file(eventType, key, x)), + }; + }); + return await choice("Which file would you like to send?", options, {}).then((x) => x); + } + catch (e) { + throw e; + } } function post_event(organizationId, eventType) { - return (0, prompt_1.choice)("What type of payload should the event use?", [ + return choice("What type of payload should the event use?", [ { long: "empty", short: "e", @@ -166,14 +109,12 @@ function post_event(organizationId, eventType) { }, ]); } -function post(organizationId) { - return __awaiter(this, void 0, void 0, function* () { - try { - const eventType = yield (0, prompt_1.shortText)("Event type", "The type of event to post", "hello").then(); - return post_event(organizationId, eventType); - } - catch (e) { - throw e; - } - }); +export async function post(organizationId) { + try { + const eventType = await shortText("Event type", "The type of event to post", "hello").then(); + return post_event(organizationId, eventType); + } + catch (e) { + throw e; + } } diff --git a/newCommands/post.ts b/newCommands/post.ts index 899c28b..f2c37b4 100644 --- a/newCommands/post.ts +++ b/newCommands/post.ts @@ -1,10 +1,16 @@ import { optimisticMimeTypeOf } from "@merrymake/ext2mime"; import fs, { readdirSync } from "fs"; -import { RAPIDS_HOST } from "../config"; -import { Option, choice, shortText } from "../prompt"; -import { OrganizationId } from "../types"; -import { addToExecuteQueue, finish, output2, sshReq, urlReq } from "../utils"; -import { key_create } from "./apikey"; +import { RAPIDS_HOST } from "../config.js"; +import { Option, choice, shortText } from "../prompt.js"; +import { OrganizationId } from "../types.js"; +import { + addToExecuteQueue, + finish, + outputGit, + sshReq, + urlReq, +} from "../utils.js"; +import { key_create } from "./apikey.js"; export async function do_post( eventType: string, @@ -13,14 +19,14 @@ export async function do_post( payload: string ) { try { - output2(`Sending POST request to ${RAPIDS_HOST}/${key}/${eventType}`); + outputGit(`Sending POST request to ${RAPIDS_HOST}/${key}/${eventType}`); const resp = await urlReq( `${RAPIDS_HOST}/${key}/${eventType}`, "POST", payload, contentType ); - output2(resp.body); + outputGit(resp.body); } catch (e) { throw e; } @@ -37,14 +43,14 @@ export async function do_post_file( filename.substring(filename.lastIndexOf(".") + 1) ); if (type === null) throw "Could not determine content type"; - output2(`Sending POST request to ${RAPIDS_HOST}/${key}/${eventType}`); + outputGit(`Sending POST request to ${RAPIDS_HOST}/${key}/${eventType}`); const resp = await urlReq( `${RAPIDS_HOST}/${key}/${eventType}`, "POST", content, type.toString() ); - output2(resp.body); + outputGit(resp.body); } catch (e) { throw e; } diff --git a/newCommands/queue.js b/newCommands/queue.js index 21ad702..2b23ea9 100644 --- a/newCommands/queue.js +++ b/newCommands/queue.js @@ -1,137 +1,116 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.do_queue_time = do_queue_time; -exports.queue = queue; -const args_1 = require("../args"); -const executors_1 = require("../executors"); -const prompt_1 = require("../prompt"); -const utils_1 = require("../utils"); -const post_1 = require("../newCommands/post"); -function do_queue_time(org, time) { - return __awaiter(this, void 0, void 0, function* () { - try { - const resp = yield (0, utils_1.sshReq)(`queue`, `--org`, org, `--time`, "" + time); - const queue = JSON.parse(resp); - (0, utils_1.output2)((0, executors_1.printTableHeader)("", { - Id: 6, - River: 12, - Event: 12, - Status: 7, - "Queue time": 20, - })); - queue.forEach((x) => (0, utils_1.output2)(`${x.id} │ ${(0, executors_1.alignRight)(x.r, 12)} │ ${(0, executors_1.alignLeft)(x.e, 12)} │ ${(0, executors_1.alignLeft)(x.s, 7)} │ ${new Date(x.q).toLocaleString()}`)); - } - catch (e) { - throw e; - } - }); +import { getArgs, initializeArgs } from "../args.js"; +import { alignLeft, alignRight, printTableHeader } from "../executors.js"; +import { post } from "../newCommands/post.js"; +import { choice, shortText } from "../prompt.js"; +import { addToExecuteQueue, finish, outputGit, sshReq } from "../utils.js"; +export async function do_queue_time(org, time) { + try { + const resp = await sshReq(`queue`, `--org`, org, `--time`, "" + time); + const queue = JSON.parse(resp); + outputGit(printTableHeader("", { + Id: 6, + River: 12, + Event: 12, + Status: 7, + "Queue time": 20, + })); + queue.forEach((x) => outputGit(`${x.id} │ ${alignRight(x.r, 12)} │ ${alignLeft(x.e, 12)} │ ${alignLeft(x.s, 7)} │ ${new Date(x.q).toLocaleString()}`)); + } + catch (e) { + throw e; + } } -function queue_event(id, river) { - return __awaiter(this, void 0, void 0, function* () { - try { - const res = JSON.parse(yield (0, utils_1.sshReq)(`rapids-inspect`, `\\"${id}\\"`, `--river`, river)); - const resout = res.output; - delete res.output; - console.log(res); - (0, utils_1.output2)("Output:"); - (0, utils_1.output2)(resout); - return (0, utils_1.finish)(); - // return choice( - // "Do you want to replay this service invocation?", - // [ - // { - // long: "replay", - // text: "replay service invocation", - // action: () => queue_event_replay(org, id, river), - // }, - // ], - // { disableAutoPick: true } - // ); - } - catch (e) { - throw e; - } - }); +async function queue_event(id, river) { + try { + const res = JSON.parse(await sshReq(`rapids-inspect`, `\\"${id}\\"`, `--river`, river)); + const resout = res.output; + delete res.output; + console.log(res); + outputGit("Output:"); + outputGit(resout); + return finish(); + // return choice( + // "Do you want to replay this service invocation?", + // [ + // { + // long: "replay", + // text: "replay service invocation", + // action: () => queue_event_replay(org, id, river), + // }, + // ], + // { disableAutoPick: true } + // ); + } + catch (e) { + throw e; + } } let cache_queue; function queue_id(id) { - const tableHeader = (0, executors_1.printTableHeader)(" ", { + const tableHeader = printTableHeader(" ", { River: 12, Event: 12, Status: 7, "Queue time": 23, }); - return (0, prompt_1.choice)("Which event would you like to inspect?\n" + tableHeader, cache_queue + return choice("Which event would you like to inspect?\n" + tableHeader, cache_queue .filter((x) => x.id === id) .map((x) => ({ long: x.r, - text: `${(0, executors_1.alignRight)(x.r, 12)} │ ${(0, executors_1.alignLeft)(x.e, 12)} │ ${(0, executors_1.alignLeft)(x.s, 7)} │ ${new Date(x.q).toLocaleString()}`, + text: `${alignRight(x.r, 12)} │ ${alignLeft(x.e, 12)} │ ${alignLeft(x.s, 7)} │ ${new Date(x.q).toLocaleString()}`, action: () => queue_event(x.id, x.r), })), { invertedQuiet: { cmd: true, select: true } }).then(); } function queue_time_value(org, time) { - (0, utils_1.addToExecuteQueue)(() => do_queue_time(org, time)); - return (0, utils_1.finish)(); + addToExecuteQueue(() => do_queue_time(org, time)); + return finish(); } -function queue_time(org) { - return __awaiter(this, void 0, void 0, function* () { - try { - let d = new Date(yield (0, prompt_1.shortText)("Time", "Displays events _around_ specified time.", "1995-12-17T03:24:00")).getTime(); - while (isNaN(d)) { - (0, utils_1.output2)("Invalid date, please try again."); - d = new Date(yield (0, prompt_1.shortText)("Time", "Displays events _around_ specified time.", "1995-12-17T03:24:00")).getTime(); - } - return queue_time_value(org, d); - } - catch (e) { - throw e; +async function queue_time(org) { + try { + let d = new Date(await shortText("Time", "Displays events _around_ specified time.", "1995-12-17T03:24:00")).getTime(); + while (isNaN(d)) { + outputGit("Invalid date, please try again."); + d = new Date(await shortText("Time", "Displays events _around_ specified time.", "1995-12-17T03:24:00")).getTime(); } - }); + return queue_time_value(org, d); + } + catch (e) { + throw e; + } } const QUEUE_COUNT = 15; -function queue(organizationId) { - return __awaiter(this, void 0, void 0, function* () { - try { - const options = []; - const resp = yield (0, utils_1.sshReq)(`rapids-view`, organizationId.toString()); - cache_queue = JSON.parse(resp); - const tableHeader = "\n" + - (0, executors_1.printTableHeader)(" ", { - Id: 21, - "River/Event": 19, - Stat: 4, - "Queue time": 20, - }); - options.push(...cache_queue.map((x) => ({ - long: x.id, - text: `${x.id} │ ${(0, executors_1.alignLeft)(x.r + "/" + x.e, 19)} │ ${(0, executors_1.alignLeft)(x.s.substring(0, 4), 4)} │ ${new Date(x.q).toLocaleString()}`, - action: () => { - if ((0, args_1.getArgs)().length === 0) - (0, args_1.initializeArgs)([x.r]); - return queue_id(x.id); - }, - }))); - options.push({ - long: "post", - short: "p", - text: "post message to rapids using an api-key", - action: () => (0, post_1.post)(organizationId), +export async function queue(organizationId) { + try { + const options = []; + const resp = await sshReq(`rapids-view`, organizationId.toString()); + cache_queue = JSON.parse(resp); + const tableHeader = "\n" + + printTableHeader(" ", { + Id: 21, + "River/Event": 19, + Stat: 4, + "Queue time": 20, }); - return yield (0, prompt_1.choice)("Which event would you like to inspect?" + tableHeader, options, { - invertedQuiet: { cmd: false, select: false }, - }).then(); - } - catch (e) { - throw e; - } - }); + options.push(...cache_queue.map((x) => ({ + long: x.id, + text: `${x.id} │ ${alignLeft(x.r + "/" + x.e, 19)} │ ${alignLeft(x.s.substring(0, 4), 4)} │ ${new Date(x.q).toLocaleString()}`, + action: () => { + if (getArgs().length === 0) + initializeArgs([x.r]); + return queue_id(x.id); + }, + }))); + options.push({ + long: "post", + short: "p", + text: "post message to rapids using an api-key", + action: () => post(organizationId), + }); + return await choice("Which event would you like to inspect?" + tableHeader, options, { + invertedQuiet: { cmd: false, select: false }, + }).then(); + } + catch (e) { + throw e; + } } diff --git a/newCommands/queue.ts b/newCommands/queue.ts index 5303a19..2db1e15 100644 --- a/newCommands/queue.ts +++ b/newCommands/queue.ts @@ -1,9 +1,9 @@ -import { getArgs, initializeArgs } from "../args"; -import { alignLeft, alignRight, printTableHeader } from "../executors"; -import { Option, choice, shortText } from "../prompt"; -import { addToExecuteQueue, finish, output2, sshReq } from "../utils"; -import { post } from "../newCommands/post"; -import { OrganizationId } from "../types"; +import { getArgs, initializeArgs } from "../args.js"; +import { alignLeft, alignRight, printTableHeader } from "../executors.js"; +import { post } from "../newCommands/post.js"; +import { Option, choice, shortText } from "../prompt.js"; +import { OrganizationId } from "../types.js"; +import { addToExecuteQueue, finish, outputGit, sshReq } from "../utils.js"; export async function do_queue_time(org: string, time: number) { try { @@ -15,7 +15,7 @@ export async function do_queue_time(org: string, time: number) { r: string; s: string; }[] = JSON.parse(resp); - output2( + outputGit( printTableHeader("", { Id: 6, River: 12, @@ -25,7 +25,7 @@ export async function do_queue_time(org: string, time: number) { }) ); queue.forEach((x) => - output2( + outputGit( `${x.id} │ ${alignRight(x.r, 12)} │ ${alignLeft(x.e, 12)} │ ${alignLeft( x.s, 7 @@ -45,8 +45,8 @@ async function queue_event(id: string, river: string) { const resout = res.output; delete res.output; console.log(res); - output2("Output:"); - output2(resout); + outputGit("Output:"); + outputGit(resout); return finish(); // return choice( // "Do you want to replay this service invocation?", @@ -110,7 +110,7 @@ async function queue_time(org: string) { ) ).getTime(); while (isNaN(d)) { - output2("Invalid date, please try again."); + outputGit("Invalid date, please try again."); d = new Date( await shortText( "Time", diff --git a/newCommands/register.js b/newCommands/register.js index 8a9816c..26635d4 100644 --- a/newCommands/register.js +++ b/newCommands/register.js @@ -1,45 +1,26 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.useExistingKey = useExistingKey; -exports.generateNewKey = generateNewKey; -exports.addKnownHost = addKnownHost; -exports.do_register = do_register; -exports.register = register; -const fs_1 = __importDefault(require("fs")); -const os_1 = __importDefault(require("os")); -const utils_1 = require("../utils"); -const config_1 = require("../config"); -const prompt_1 = require("../prompt"); -const wait_1 = require("./wait"); -const org_1 = require("./org"); +import fs from "fs"; +import os from "os"; +import { API_URL, FINGERPRINT, HTTP_HOST, SSH_USER } from "../config.js"; +import { choice, NORMAL_COLOR, output, shortText, YELLOW, } from "../prompt.js"; +import { addExitMessage, execPromise, getFiles, Path, urlReq, } from "../utils.js"; +import { orgAction } from "./org.js"; +import { wait } from "./wait.js"; function saveSSHConfig(path) { let lines = []; let changed = false; let foundHost = false; - if (fs_1.default.existsSync(`${os_1.default.homedir()}/.ssh/config`)) { - lines = fs_1.default - .readFileSync(`${os_1.default.homedir()}/.ssh/config`) + if (fs.existsSync(`${os.homedir()}/.ssh/config`)) { + lines = fs + .readFileSync(`${os.homedir()}/.ssh/config`) .toString() .split("\n"); let inHost = false; for (let i = 0; i < lines.length; i++) { const line = lines[i]; if ((line.startsWith("\t") || line.startsWith(" ")) && inHost) { - if (line.includes("User ") && !line.includes(`User ${config_1.SSH_USER}`)) { + if (line.includes("User ") && !line.includes(`User ${SSH_USER}`)) { lines[i] = - line.substring(0, line.indexOf("User ")) + `User ${config_1.SSH_USER}`; + line.substring(0, line.indexOf("User ")) + `User ${SSH_USER}`; changed = true; } else if (line.includes("IdentityFile ") && @@ -52,7 +33,7 @@ function saveSSHConfig(path) { } else if (line.startsWith("\t") || line.startsWith(" ")) { } - else if (line.startsWith(`Host ${config_1.API_URL}`)) { + else if (line.startsWith(`Host ${API_URL}`)) { inHost = true; foundHost = true; } @@ -62,146 +43,134 @@ function saveSSHConfig(path) { } } if (!foundHost) { - lines.unshift(`Host ${config_1.API_URL}`, `\tUser ${config_1.SSH_USER}`, `\tHostName ${config_1.API_URL}`, `\tPreferredAuthentications publickey`, `\tIdentityFile ~/.ssh/${path}\n`); + lines.unshift(`Host ${API_URL}`, `\tUser ${SSH_USER}`, `\tHostName ${API_URL}`, `\tPreferredAuthentications publickey`, `\tIdentityFile ~/.ssh/${path}\n`); changed = true; } if (changed) { - (0, prompt_1.output)(`Saving preference...\n`); - fs_1.default.writeFileSync(`${os_1.default.homedir()}/.ssh/config`, lines.join("\n")); + output(`Saving preference...\n`); + fs.writeFileSync(`${os.homedir()}/.ssh/config`, lines.join("\n")); } } -function useExistingKey(path) { - return __awaiter(this, void 0, void 0, function* () { - try { - saveSSHConfig(path); - (0, prompt_1.output)(`Reading ${path}.pub...\n`); - return { - key: "" + fs_1.default.readFileSync(os_1.default.homedir() + `/.ssh/${path}.pub`), - keyFile: path, - }; - } - catch (e) { - throw e; - } - }); +export async function useExistingKey(path) { + try { + saveSSHConfig(path); + output(`Reading ${path}.pub...\n`); + return { + key: "" + fs.readFileSync(os.homedir() + `/.ssh/${path}.pub`), + keyFile: path, + }; + } + catch (e) { + throw e; + } } -function generateNewKey() { - return __awaiter(this, void 0, void 0, function* () { - try { - (0, prompt_1.output)(`Generating new ssh key...\n`); - if (!fs_1.default.existsSync(os_1.default.homedir() + "/.ssh")) - fs_1.default.mkdirSync(os_1.default.homedir() + "/.ssh"); - yield (0, utils_1.execPromise)(`ssh-keygen -t rsa -b 4096 -f "${os_1.default.homedir()}/.ssh/merrymake" -N ""`); - saveSSHConfig("merrymake"); - return { - key: "" + fs_1.default.readFileSync(os_1.default.homedir() + "/.ssh/merrymake.pub"), - keyFile: "merrymake", - }; - } - catch (e) { - throw e; - } - }); +export async function generateNewKey() { + try { + output(`Generating new ssh key...\n`); + if (!fs.existsSync(os.homedir() + "/.ssh")) + fs.mkdirSync(os.homedir() + "/.ssh"); + await execPromise(`ssh-keygen -t rsa -b 4096 -f "${os.homedir()}/.ssh/merrymake" -N ""`); + saveSSHConfig("merrymake"); + return { + key: "" + fs.readFileSync(os.homedir() + "/.ssh/merrymake.pub"), + keyFile: "merrymake", + }; + } + catch (e) { + throw e; + } } -function addKnownHost() { +export function addKnownHost() { let isKnownHost = false; - if (fs_1.default.existsSync(`${os_1.default.homedir()}/.ssh/known_hosts`)) { - const lines = ("" + fs_1.default.readFileSync(`${os_1.default.homedir()}/.ssh/known_hosts`)).split("\n"); - isKnownHost = lines.some((x) => x.includes(`${config_1.API_URL} ssh-ed25519 ${config_1.FINGERPRINT}`)); + if (fs.existsSync(`${os.homedir()}/.ssh/known_hosts`)) { + const lines = ("" + fs.readFileSync(`${os.homedir()}/.ssh/known_hosts`)).split("\n"); + isKnownHost = lines.some((x) => x.includes(`${API_URL} ssh-ed25519 ${FINGERPRINT}`)); } if (!isKnownHost) { - (0, prompt_1.output)("Adding fingerprint...\n"); - if (!fs_1.default.existsSync(os_1.default.homedir() + "/.ssh")) - fs_1.default.mkdirSync(os_1.default.homedir() + "/.ssh"); - fs_1.default.appendFileSync(`${os_1.default.homedir()}/.ssh/known_hosts`, `\n${config_1.API_URL} ssh-ed25519 ${config_1.FINGERPRINT}\n`); + output("Adding fingerprint...\n"); + if (!fs.existsSync(os.homedir() + "/.ssh")) + fs.mkdirSync(os.homedir() + "/.ssh"); + fs.appendFileSync(`${os.homedir()}/.ssh/known_hosts`, `\n${API_URL} ssh-ed25519 ${FINGERPRINT}\n`); } } -function do_register(keyAction, email) { - return __awaiter(this, void 0, void 0, function* () { - try { - const { key, keyFile } = yield keyAction(); - (0, prompt_1.output)(`Registering ${email === "" ? "anonymous account" : email}...\n`); - addKnownHost(); - if (email === "") { - (0, utils_1.addExitMessage)(`Notice: Anonymous accounts are automatically deleted permanently after ~2 weeks, without warning. To add an email and avoid automatic deletion, run the command: - ${prompt_1.YELLOW}${process.env["COMMAND"]} register ${keyFile}${prompt_1.NORMAL_COLOR}`); - } - const result = yield (0, utils_1.urlReq)(`${config_1.HTTP_HOST}/admin/user`, "POST", JSON.stringify({ - email, - key, - })); - if (result.code !== 200) - throw result.body; - const needsVerify = result.body === "true"; - return needsVerify - ? (0, wait_1.wait)("Click the button in the email before continuing", org_1.orgAction) - : (0, org_1.orgAction)(); - } - catch (e) { - throw e; +export async function do_register(keyAction, email) { + try { + const { key, keyFile } = await keyAction(); + output(`Registering ${email === "" ? "anonymous account" : email}...\n`); + addKnownHost(); + if (email === "") { + addExitMessage(`Notice: Anonymous accounts are automatically deleted permanently after ~2 weeks, without warning. To add an email and avoid automatic deletion, run the command: + ${YELLOW}${process.env["COMMAND"]} register ${keyFile}${NORMAL_COLOR}`); } - }); + const result = await urlReq(`${HTTP_HOST}/admin/user`, "POST", JSON.stringify({ + email, + key, + })); + if (result.code !== 200) + throw result.body; + const needsVerify = result.body === "true"; + return needsVerify + ? wait("Click the button in the email before continuing", orgAction) + : orgAction(); + } + catch (e) { + throw e; + } } -function register_key(keyAction) { - return __awaiter(this, void 0, void 0, function* () { - try { - const email = yield (0, prompt_1.shortText)("Email", "By attaching an email you'll be notified in case of changes for your organizations.", "").then(); - return do_register(keyAction, email); - } - catch (e) { - throw e; - } - }); +async function register_key(keyAction) { + try { + const email = await shortText("Email", "By attaching an email you'll be notified in case of changes for your organizations.", "").then(); + return do_register(keyAction, email); + } + catch (e) { + throw e; + } } -function register_manual() { - return __awaiter(this, void 0, void 0, function* () { - try { - const key = yield (0, prompt_1.shortText)("Public key", "", "ssh-rsa ...").then(); - return register_key(() => Promise.resolve({ - key, - keyFile: `add "${key}"`, - })); - } - catch (e) { - throw e; - } - }); +async function register_manual() { + try { + const key = await shortText("Public key", "", "ssh-rsa ...").then(); + return register_key(() => Promise.resolve({ + key, + keyFile: `add "${key}"`, + })); + } + catch (e) { + throw e; + } } -function register() { - return __awaiter(this, void 0, void 0, function* () { - try { - const keyfiles = (0, utils_1.getFiles)(new utils_1.Path(`${os_1.default.homedir()}/.ssh`)).filter((x) => x.endsWith(".pub")); - const keys = keyfiles.map((x) => { - const f = x.substring(0, x.length - ".pub".length); - return { - long: f, - text: `use key ${f}`, - action: () => register_key(() => useExistingKey(f)), - }; - }); +export async function register() { + try { + const keyfiles = getFiles(new Path(`${os.homedir()}/.ssh`)).filter((x) => x.endsWith(".pub")); + const keys = keyfiles.map((x) => { + const f = x.substring(0, x.length - ".pub".length); + return { + long: f, + text: `use key ${f}`, + action: () => register_key(() => useExistingKey(f)), + }; + }); + keys.push({ + long: "add", + short: "a", + text: "manually add key", + action: () => register_manual(), + }); + let def = keyfiles.indexOf("merrymake.pub"); + if (def < 0) { keys.push({ - long: "add", - short: "a", - text: "manually add key", - action: () => register_manual(), + long: "new", + short: "n", + text: "setup new key specifically for Merrymake", + action: () => register_key(generateNewKey), }); - let def = keyfiles.indexOf("merrymake.pub"); - if (def < 0) { - keys.push({ - long: "new", - short: "n", - text: "setup new key specifically for Merrymake", - action: () => register_key(generateNewKey), - }); - def = keys.length - 1; - } - return yield (0, prompt_1.choice)("Which SSH key would you like to use?", keys, { - invertedQuiet: { cmd: false, select: true }, - def, - }).then(); + def = keys.length - 1; } - catch (e) { - throw e; - } - }); + return await choice("Which SSH key would you like to use?", keys, { + invertedQuiet: { cmd: false, select: true }, + def, + }).then(); + } + catch (e) { + throw e; + } } diff --git a/newCommands/register.ts b/newCommands/register.ts index 910c9b3..e2b69ab 100644 --- a/newCommands/register.ts +++ b/newCommands/register.ts @@ -1,27 +1,23 @@ import fs from "fs"; import os from "os"; +import { API_URL, FINGERPRINT, HTTP_HOST, SSH_USER } from "../config.js"; import { - output2, - addExitMessage, - urlReq, - saveCache, - addToExecuteQueue, - finish, - getFiles, - Path, - execPromise, -} from "../utils"; -import { API_URL, FINGERPRINT, HTTP_HOST, SSH_USER } from "../config"; -import { - YELLOW, - NORMAL_COLOR, choice, + NORMAL_COLOR, Option, - shortText, output, -} from "../prompt"; -import { wait } from "./wait"; -import { orgAction } from "./org"; + shortText, + YELLOW, +} from "../prompt.js"; +import { + addExitMessage, + execPromise, + getFiles, + Path, + urlReq, +} from "../utils.js"; +import { orgAction } from "./org.js"; +import { wait } from "./wait.js"; function saveSSHConfig(path: string) { let lines: string[] = []; diff --git a/newCommands/repo.js b/newCommands/repo.js index 00c11e7..adf89aa 100644 --- a/newCommands/repo.js +++ b/newCommands/repo.js @@ -1,149 +1,115 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.do_fetch_template = do_fetch_template; -exports.do_duplicate = do_duplicate; -exports.do_createService = do_createService; -exports.service_template = service_template; -exports.listRepos = listRepos; -exports.repo_create = repo_create; -exports.repo = repo; -const detect_project_type_1 = require("@merrymake/detect-project-type"); -const fs_1 = __importDefault(require("fs")); -const config_1 = require("../config"); -const prompt_1 = require("../prompt"); -const templates_1 = require("../templates"); -const utils_1 = require("../utils"); -const deploy_1 = require("./deploy"); -const post_1 = require("./post"); -const types_1 = require("../types"); -const hosting_1 = require("./hosting"); -function do_pull(pth, repo) { - return __awaiter(this, void 0, void 0, function* () { - try { - const before = process.cwd(); - process.chdir(pth.toString()); - if (fs_1.default.existsSync(".git")) - yield (0, utils_1.execPromise)(`git pull -q "${repo}"`); - else { - yield (0, utils_1.execPromise)(`git clone -q "${repo}" .`); - fs_1.default.rmSync(".git", { recursive: true, force: true }); - } - process.chdir(before); - } - catch (e) { - throw e; - } - }); +import { VERSION_CMD } from "@merrymake/detect-project-type"; +import fs from "fs"; +import { GIT_HOST } from "../config.js"; +import { choice, shortText } from "../prompt.js"; +import { languages, templates } from "../templates.js"; +import { RepositoryId, } from "../types.js"; +import { Path, TODO, execPromise, finish, sshReq, toFolderName, } from "../utils.js"; +import { do_deploy } from "./deploy.js"; +import { BITBUCKET_FILE, bitbucketStep } from "./hosting.js"; +import { post } from "./post.js"; +async function do_pull(pth, repo) { + try { + const before = process.cwd(); + process.chdir(pth.toString()); + if (fs.existsSync(".git")) + await execPromise(`git pull -q "${repo}"`); + else { + await execPromise(`git clone -q "${repo}" .`); + fs.rmSync(".git", { recursive: true, force: true }); + } + process.chdir(before); + } + catch (e) { + throw e; + } } -function do_fetch_template(pth, template, projectType) { +export function do_fetch_template(pth, template, projectType) { console.log(`Fetching ${projectType} template...`); return do_pull(pth, `https://github.com/merrymake/${projectType}-${template}-template`); } -function do_duplicate(pth, organizationId, groupId, repositoryId) { +export function do_duplicate(pth, organizationId, groupId, repositoryId) { console.log(`Duplicating ${"local folder"} service...`); - return do_pull(pth, `${config_1.GIT_HOST}/o${organizationId}/g${groupId}/r${repositoryId}`); + return do_pull(pth, `${GIT_HOST}/o${organizationId}/g${groupId}/r${repositoryId}`); } -function service_template_language(pathToService, organizationId, template, projectType) { - return __awaiter(this, void 0, void 0, function* () { - try { - yield do_fetch_template(pathToService, template, projectType); - return after_service(pathToService, organizationId); - } - catch (e) { - throw e; - } - }); +async function service_template_language(pathToService, organizationId, template, projectType) { + try { + await do_fetch_template(pathToService, template, projectType); + return after_service(pathToService, organizationId); + } + catch (e) { + throw e; + } } -function do_createService(organization, serviceGroup, folderName, displayName) { - return __awaiter(this, void 0, void 0, function* () { - try { - const repositoryPath = serviceGroup.pathTo.with(folderName); - console.log(`Creating service '${displayName}'...`); - const reply = yield (0, utils_1.sshReq)(`repository-create`, displayName, `--serviceGroupId`, serviceGroup.id.toString()); - if (reply.length !== 8) - throw reply; - const repositoryId = new types_1.RepositoryId(reply); - const repoBase = `g${serviceGroup.id.toString()}/r${repositoryId}`; - if (fs_1.default.existsSync(organization.pathTo.with(hosting_1.BITBUCKET_FILE).toString())) { - fs_1.default.mkdirSync(repositoryPath.toString(), { recursive: true }); - fs_1.default.appendFileSync(organization.pathTo.with(hosting_1.BITBUCKET_FILE).toString(), "\n" + - (0, hosting_1.bitbucketStep)(new utils_1.Path(serviceGroup.pathTo.last()).with(folderName), repoBase)); +export async function do_createService(organization, serviceGroup, folderName, displayName) { + try { + const repositoryPath = serviceGroup.pathTo.with(folderName); + console.log(`Creating service '${displayName}'...`); + const reply = await sshReq(`repository-create`, displayName, `--serviceGroupId`, serviceGroup.id.toString()); + if (reply.length !== 8) + throw reply; + const repositoryId = new RepositoryId(reply); + const repoBase = `g${serviceGroup.id.toString()}/r${repositoryId}`; + if (fs.existsSync(organization.pathTo.with(BITBUCKET_FILE).toString())) { + fs.mkdirSync(repositoryPath.toString(), { recursive: true }); + fs.appendFileSync(organization.pathTo.with(BITBUCKET_FILE).toString(), "\n" + + bitbucketStep(new Path(serviceGroup.pathTo.last()).with(folderName), repoBase)); + } + else { + try { + await execPromise(`git clone -q "${GIT_HOST}/o${organization.id.toString()}/${repoBase}" ${folderName}`, serviceGroup.pathTo.toString()); } - else { - try { - yield (0, utils_1.execPromise)(`git clone -q "${config_1.GIT_HOST}/o${organization.id.toString()}/${repoBase}" ${folderName}`, serviceGroup.pathTo.toString()); - } - catch (e) { - if (("" + e).startsWith("warning: You appear to have cloned an empty repository.")) { - } - else - throw e; + catch (e) { + if (("" + e).startsWith("warning: You appear to have cloned an empty repository.")) { } - yield (0, utils_1.execPromise)(`git symbolic-ref HEAD refs/heads/main`, repositoryPath.toString()); + else + throw e; } - return repositoryId; - } - catch (e) { - throw e; + await execPromise(`git symbolic-ref HEAD refs/heads/main`, repositoryPath.toString()); } - }); + return repositoryId; + } + catch (e) { + throw e; + } } -function service_template(pathToService, organizationId, template) { - return __awaiter(this, void 0, void 0, function* () { - try { - const langs = yield Promise.all(templates_1.templates[template].languages.map((x, i) => (() => __awaiter(this, void 0, void 0, function* () { - return (Object.assign(Object.assign({}, templates_1.languages[x]), { weight: yield (0, utils_1.execPromise)(detect_project_type_1.VERSION_CMD[templates_1.languages[x].projectType]) - .then((r) => { - return templates_1.templates[template].languages.length + 1 - i; - }) - .catch((e) => { - return -i; - }) })); - }))())); - langs.sort((a, b) => b.weight - a.weight); - return yield (0, prompt_1.choice)("Which programming language would you like to use?", langs.map((x) => ({ - long: x.long, - short: x.short, - text: x.long, - action: () => service_template_language(pathToService, organizationId, template, x.projectType), - }))).then(); - } - catch (e) { - throw e; - } - }); +export async function service_template(pathToService, organizationId, template) { + try { + const langs = await Promise.all(templates[template].languages.map((x, i) => (async () => ({ + ...languages[x], + weight: (await Promise.all(Object.keys(VERSION_CMD[languages[x].projectType]).map((k) => execPromise(VERSION_CMD[languages[x].projectType][k]) + .then((r) => 1) + .catch((e) => -1)))).reduce((a, x) => a * x, i), + }))())); + langs.sort((a, b) => b.weight - a.weight); + return await choice("Which programming language would you like to use?", langs.map((x) => ({ + long: x.long, + short: x.short, + text: x.long, + action: () => service_template_language(pathToService, organizationId, template, x.projectType), + }))).then(); + } + catch (e) { + throw e; + } } -function after_service_deploy(pathToService, organizationId) { - return __awaiter(this, void 0, void 0, function* () { - try { - yield (0, deploy_1.do_deploy)(pathToService); - return (0, prompt_1.choice)("Would you like to post and event to the Rapids? (Trigger the service)", [ - { - long: "post", - text: "post an event to the rapids", - action: () => (0, post_1.post)(organizationId), - }, - ], { disableAutoPick: true }); - } - catch (e) { - throw e; - } - }); +async function after_service_deploy(pathToService, organizationId) { + try { + await do_deploy(pathToService); + return choice("Would you like to post and event to the Rapids? (Trigger the service)", [ + { + long: "post", + text: "post an event to the rapids", + action: () => post(organizationId), + }, + ], { disableAutoPick: true }); + } + catch (e) { + throw e; + } } function after_service(pathToService, organizationId) { - return (0, prompt_1.choice)("Would you like to deploy the new service?", [ + return choice("Would you like to deploy the new service?", [ { long: "deploy", short: "d", @@ -152,126 +118,114 @@ function after_service(pathToService, organizationId) { }, ], { disableAutoPick: true }); } -function duplicate_then(pathToService, organizationId, groupId, repositoryId) { - return __awaiter(this, void 0, void 0, function* () { - try { - yield do_duplicate(pathToService, organizationId, groupId, repositoryId); - return after_service(pathToService, organizationId); - } - catch (e) { - throw e; - } - }); +async function duplicate_then(pathToService, organizationId, groupId, repositoryId) { + try { + await do_duplicate(pathToService, organizationId, groupId, repositoryId); + return after_service(pathToService, organizationId); + } + catch (e) { + throw e; + } } -function duplicate(pathToService, organizationId, serviceGroupId) { - return __awaiter(this, void 0, void 0, function* () { - try { - const repos = yield listRepos(serviceGroupId); - return yield (0, prompt_1.choice)("Which service would you like to duplicate?", repos.map((x) => ({ - long: x.id, - text: `${x.name} (${x.id})`, - action: () => duplicate_then(pathToService, organizationId, serviceGroupId, new types_1.RepositoryId(x.id)), - }))).then(); - } - catch (e) { - throw e; - } - }); +async function duplicate(pathToService, organizationId, serviceGroupId) { + try { + const repos = await listRepos(serviceGroupId); + return await choice("Which service would you like to duplicate?", repos.map((x) => ({ + long: x.id, + text: `${x.name} (${x.id})`, + action: () => duplicate_then(pathToService, organizationId, serviceGroupId, new RepositoryId(x.id)), + }))).then(); + } + catch (e) { + throw e; + } } let repoListCache; -function listRepos(serviceGroupId) { - return __awaiter(this, void 0, void 0, function* () { - if (repoListCache === undefined) { - const resp = yield (0, utils_1.sshReq)(`repository-list`, serviceGroupId.toString()); - if (!resp.startsWith("[")) - throw resp; - repoListCache = JSON.parse(resp); - } - return repoListCache; - }); +export async function listRepos(serviceGroupId) { + if (repoListCache === undefined) { + const resp = await sshReq(`repository-list`, serviceGroupId.toString()); + if (!resp.startsWith("[")) + throw resp; + repoListCache = JSON.parse(resp); + } + return repoListCache; } -function repo_create(organization, serviceGroup) { - return __awaiter(this, void 0, void 0, function* () { - try { - let num = 1; - while (fs_1.default.existsSync(serviceGroup.pathTo.with("repo-" + num).toString())) - num++; - const displayName = yield (0, prompt_1.shortText)("Repository name", "This is where the code lives.", "repo-" + num).then(); - const folderName = (0, utils_1.toFolderName)(displayName); - const pathToRepository = serviceGroup.pathTo.with(folderName); - const repositoryId = yield do_createService(organization, serviceGroup, folderName, displayName); - const options = []; - // const repositories = await listRepos(serviceGroupId); - // if (repositories.length > 0) { - // options.push({ - // long: "duplicate", - // short: "d", - // text: "duplicate an existing service", - // action: () => duplicate(pathToRepository, org, group), - // }); - // } - Object.keys(templates_1.templates).forEach((x) => options.push({ - long: templates_1.templates[x].long, - short: templates_1.templates[x].short, - text: templates_1.templates[x].text, - action: () => service_template(pathToRepository, organization.id, x), - })); - options.push({ - long: "empty", - short: "e", - text: "nothing, just an empty repo", - action: () => (0, utils_1.finish)(), - }); - return yield (0, prompt_1.choice)("What would you like the new repository to contain?", options).then(); - } - catch (e) { - throw e; - } - }); +export async function repo_create(organization, serviceGroup) { + try { + let num = 1; + while (fs.existsSync(serviceGroup.pathTo.with("repo-" + num).toString())) + num++; + const displayName = await shortText("Repository name", "This is where the code lives.", "repo-" + num).then(); + const folderName = toFolderName(displayName); + const pathToRepository = serviceGroup.pathTo.with(folderName); + const repositoryId = await do_createService(organization, serviceGroup, folderName, displayName); + const options = []; + // const repositories = await listRepos(serviceGroupId); + // if (repositories.length > 0) { + // options.push({ + // long: "duplicate", + // short: "d", + // text: "duplicate an existing service", + // action: () => duplicate(pathToRepository, org, group), + // }); + // } + Object.keys(templates).forEach((x) => options.push({ + long: templates[x].long, + short: templates[x].short, + text: templates[x].text, + action: () => service_template(pathToRepository, organization.id, x), + })); + options.push({ + long: "empty", + short: "e", + text: "nothing, just an empty repo", + action: () => finish(), + }); + return await choice("What would you like the new repository to contain?", options).then(); + } + catch (e) { + throw e; + } } -function repo_edit(pathToGroup, displayName, repositoryId) { - return __awaiter(this, void 0, void 0, function* () { - try { - const options = [ - { - long: "rename", - text: `rename repository`, - action: utils_1.TODO, - }, - { - long: "delete", - text: `delete repository '${displayName}' permanently`, - action: utils_1.TODO, - }, - ]; - return yield (0, prompt_1.choice)("How would you like to edit the repo?", options).then((x) => x); - } - catch (e) { - throw e; - } - }); +async function repo_edit(pathToGroup, displayName, repositoryId) { + try { + const options = [ + { + long: "rename", + text: `rename repository`, + action: TODO, + }, + { + long: "delete", + text: `delete repository '${displayName}' permanently`, + action: TODO, + }, + ]; + return await choice("How would you like to edit the repo?", options).then((x) => x); + } + catch (e) { + throw e; + } } -function repo(organization, serviceGroup) { - return __awaiter(this, void 0, void 0, function* () { - try { - const repos = yield listRepos(serviceGroup.id); - const options = []; +export async function repo(organization, serviceGroup) { + try { + const repos = await listRepos(serviceGroup.id); + const options = []; + options.push({ + long: "create", + text: `create new repository`, + action: () => repo_create(organization, serviceGroup), + }); + repos.forEach((x) => { options.push({ - long: "create", - text: `create new repository`, - action: () => repo_create(organization, serviceGroup), - }); - repos.forEach((x) => { - options.push({ - long: x.id, - text: `edit ${x.name} (${x.id})`, - action: () => repo_edit(serviceGroup.pathTo, x.name, x.id), - }); + long: x.id, + text: `edit ${x.name} (${x.id})`, + action: () => repo_edit(serviceGroup.pathTo, x.name, x.id), }); - return yield (0, prompt_1.choice)("Which repository would you like to manage?", options).then(); - } - catch (e) { - throw e; - } - }); + }); + return await choice("Which repository would you like to manage?", options).then(); + } + catch (e) { + throw e; + } } diff --git a/newCommands/repo.ts b/newCommands/repo.ts index 44cdf4d..2bea1d9 100644 --- a/newCommands/repo.ts +++ b/newCommands/repo.ts @@ -1,18 +1,8 @@ import { VERSION_CMD } from "@merrymake/detect-project-type"; import fs from "fs"; -import { GIT_HOST } from "../config"; -import { Option, choice, shortText } from "../prompt"; -import { languages, templates } from "../templates"; -import { - Path, - TODO, - execPromise, - finish, - sshReq, - toFolderName, -} from "../utils"; -import { do_deploy } from "./deploy"; -import { post } from "./post"; +import { GIT_HOST } from "../config.js"; +import { Option, choice, shortText } from "../prompt.js"; +import { languages, templates } from "../templates.js"; import { Organization, OrganizationId, @@ -21,8 +11,18 @@ import { RepositoryId, ServiceGroup, ServiceGroupId, -} from "../types"; -import { BITBUCKET_FILE, bitbucketStep } from "./hosting"; +} from "../types.js"; +import { + Path, + TODO, + execPromise, + finish, + sshReq, + toFolderName, +} from "../utils.js"; +import { do_deploy } from "./deploy.js"; +import { BITBUCKET_FILE, bitbucketStep } from "./hosting.js"; +import { post } from "./post.js"; async function do_pull(pth: PathToRepository, repo: string) { try { @@ -141,13 +141,15 @@ export async function service_template( templates[template].languages.map((x, i) => (async () => ({ ...languages[x], - weight: await execPromise(VERSION_CMD[languages[x].projectType]) - .then((r) => { - return templates[template].languages.length + 1 - i; - }) - .catch((e) => { - return -i; - }), + weight: ( + await Promise.all( + Object.keys(VERSION_CMD[languages[x].projectType]).map((k) => + execPromise(VERSION_CMD[languages[x].projectType][k]) + .then((r) => 1) + .catch((e) => -1) + ) + ) + ).reduce((a, x) => a * x, i), }))() ) ); diff --git a/newCommands/role.js b/newCommands/role.js index c2dd2ff..6037879 100644 --- a/newCommands/role.js +++ b/newCommands/role.js @@ -1,232 +1,229 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.do_attach_role = do_attach_role; -exports.do_auto_approve = do_auto_approve; -exports.do_remove_auto_approve = do_remove_auto_approve; -exports.listRoles = listRoles; -exports.role = role; -const prompt_1 = require("../prompt"); -const types_1 = require("../types"); -const utils_1 = require("../utils"); -const hosting_1 = require("./hosting"); +import { choice, shortText } from "../prompt.js"; +import { AccessId } from "../types.js"; +import { addToExecuteQueue, finish, outputGit, sshReq, toFolderName, } from "../utils.js"; +import { do_create_deployment_agent } from "./hosting.js"; const SPECIAL_ROLES = ["Pending", "Build agent", "Deployment agent"]; -function do_attach_role(user, accessId) { - return __awaiter(this, void 0, void 0, function* () { - try { - (0, utils_1.output2)(yield (0, utils_1.sshReq)(`user-assign`, user, `--accessId`, accessId.toString())); - } - catch (e) { - throw e; - } - }); -} -function do_auto_approve(domain, accessId) { - return __awaiter(this, void 0, void 0, function* () { - try { - (0, utils_1.output2)(yield (0, utils_1.sshReq)(`preapprove-add`, `--accessId`, accessId.toString(), domain)); - } - catch (e) { - throw e; - } - }); -} -function do_remove_auto_approve(organizationId, domain) { - return __awaiter(this, void 0, void 0, function* () { - try { - (0, utils_1.output2)(yield (0, utils_1.sshReq)(`preapprove-remove`, `--organizationId`, organizationId.toString(), domain)); - } - catch (e) { - throw e; - } - }); +export async function do_attach_role(user, accessId) { + try { + outputGit(await sshReq(`user-assign`, user, `--accessId`, accessId.toString())); + } + catch (e) { + throw e; + } +} +export async function do_auto_approve(domain, accessId) { + try { + outputGit(await sshReq(`preapprove-add`, `--accessId`, accessId.toString(), domain)); + } + catch (e) { + throw e; + } +} +export async function do_remove_auto_approve(organizationId, domain) { + try { + outputGit(await sshReq(`preapprove-remove`, `--organizationId`, organizationId.toString(), domain)); + } + catch (e) { + throw e; + } } function role_user_attach_role(user, accessId) { - (0, utils_1.addToExecuteQueue)(() => do_attach_role(user, accessId)); - return (0, utils_1.finish)(); + addToExecuteQueue(() => do_attach_role(user, accessId)); + return finish(); } function role_auto_domain_role(domain, accessId) { - (0, utils_1.addToExecuteQueue)(() => do_auto_approve(domain, accessId)); - return (0, utils_1.finish)(); + addToExecuteQueue(() => do_auto_approve(domain, accessId)); + return finish(); } function role_auto_remove(organizationId, domain) { - (0, utils_1.addToExecuteQueue)(() => do_remove_auto_approve(organizationId, domain)); - return (0, utils_1.finish)(); + addToExecuteQueue(() => do_remove_auto_approve(organizationId, domain)); + return finish(); } let roleListCache; -function listRoles(organizationId) { - return __awaiter(this, void 0, void 0, function* () { - if (roleListCache === undefined) { - const resp = yield (0, utils_1.sshReq)(`role-list`, organizationId.toString()); - if (!resp.startsWith("[")) - throw resp; - roleListCache = JSON.parse(resp); - } - return roleListCache; - }); -} -function role_user_attach(organizationId, user) { - return __awaiter(this, void 0, void 0, function* () { - try { - const roles = yield listRoles(organizationId); - const options = roles - .filter((role) => !SPECIAL_ROLES.includes(role.name)) - .map((role) => { - return { - long: role.id, - text: `assign ${role.name} (${role.id})`, - action: () => role_user_attach_role(user, new types_1.AccessId(role.id)), - }; - }); - return yield (0, prompt_1.choice)("Which role would you like to assign?", options).then((x) => x); - } - catch (e) { - throw e; - } - }); -} -function role_user(organizationId, user) { - return __awaiter(this, void 0, void 0, function* () { - try { - const roles = yield listRoles(organizationId); - const pendingId = roles.find((x) => x.name === "Pending").id; - const options = []; - options.push({ - long: `assign`, - short: `a`, - text: `assign an additional role to user`, - action: () => role_user_attach(organizationId, user), - }); - options.push({ - long: `remove`, - short: `r`, - text: `remove all roles and access`, - action: () => role_user_attach_role(user, new types_1.AccessId(pendingId)), - }); - return yield (0, prompt_1.choice)("What would you like to do?", options).then(); - } - catch (e) { - throw e; - } - }); -} -function role_auto_new_domain(organizationId, domain) { - return __awaiter(this, void 0, void 0, function* () { - try { - const roles = yield listRoles(organizationId); - const options = roles - .filter((role) => !SPECIAL_ROLES.includes(role.name)) - .map((role) => { - return { - long: role.id, - text: `auto assign ${role.name} (${role.id})`, - action: () => role_auto_domain_role(domain, new types_1.AccessId(role.id)), - }; - }); - return yield (0, prompt_1.choice)("Which role should new users get?", options).then((x) => x); - } - catch (e) { - throw e; - } - }); -} -function role_auto_new(organizationId) { - return __awaiter(this, void 0, void 0, function* () { - try { - const domain = yield (0, prompt_1.shortText)("Domain", "Email domain to auto approve.", `@example.com`).then(); - return role_auto_new_domain(organizationId, domain); - } - catch (e) { - throw e; - } - }); -} -function role_auto(organizationId) { - return __awaiter(this, void 0, void 0, function* () { - try { - const resp = yield (0, utils_1.sshReq)(`preapprove-list`, organizationId.toString()); - const domains = JSON.parse(resp); - const doms = {}; - domains.forEach((x) => { - if (doms[x.domain] === undefined) - doms[x.domain] = []; - doms[x.domain].push(x.access); - }); - const options = Object.keys(doms).map((domain) => { - return { - long: domain, - text: `remove ${domain} (${doms[domain].join(", ")})`, - action: () => role_auto_remove(organizationId, domain), - }; - }); - options.push({ - long: `new`, - short: `n`, - text: `setup a new domain rule`, - action: () => role_auto_new(organizationId), - }); - return yield (0, prompt_1.choice)("What would you like to do?", options).then(); - } - catch (e) { - throw e; - } - }); -} -function service_user(organization) { - return __awaiter(this, void 0, void 0, function* () { - try { - const name = yield (0, prompt_1.shortText)("Name", "Display name for the service user", `Service User`).then(); - const file = ".merrymake/" + (0, utils_1.toFolderName)(name) + ".key"; - (0, utils_1.addToExecuteQueue)(() => (0, hosting_1.do_create_deployment_agent)(organization, name, file)); - return (0, utils_1.finish)(); - } - catch (e) { - throw e; - } - }); -} -function role(organization) { - return __awaiter(this, void 0, void 0, function* () { - try { - const resp = yield (0, utils_1.sshReq)(`user-list`, organization.id.toString()); - const users = JSON.parse(resp); - const options = users.map((user) => { - return { - long: user.email, - text: `${user.email}: ${user.roles}`, - action: () => role_user(organization.id, user.id), - }; - }); - // options.push({ - // long: `new`, - // short: `n`, - // text: `create a new role`, - // action: () => role_new(org), - // }); - options.push({ - long: `service`, - short: `s`, - text: `create a new service user`, - action: () => service_user(organization), - }); - options.push({ - long: `auto`, - short: `a`, - text: `configure domain auto approval`, - action: () => role_auto(organization.id), - }); - return yield (0, prompt_1.choice)("Which user do you want to manage?", options).then((x) => x); - } - catch (e) { - throw e; - } - }); +export async function listRoles(organizationId) { + if (roleListCache === undefined) { + const resp = await sshReq(`role-list`, organizationId.toString()); + if (!resp.startsWith("[")) + throw resp; + roleListCache = JSON.parse(resp); + } + return roleListCache; +} +async function role_user_attach(organizationId, user) { + try { + const roles = await listRoles(organizationId); + const options = roles + .filter((role) => !SPECIAL_ROLES.includes(role.name)) + .map((role) => { + return { + long: role.id, + text: `assign ${role.name} (${role.id})`, + action: () => role_user_attach_role(user, new AccessId(role.id)), + }; + }); + return await choice("Which role would you like to assign?", options).then((x) => x); + } + catch (e) { + throw e; + } +} +async function role_user(organizationId, user) { + try { + const roles = await listRoles(organizationId); + const pendingId = roles.find((x) => x.name === "Pending").id; + const options = []; + options.push({ + long: `assign`, + short: `a`, + text: `assign an additional role to user`, + action: () => role_user_attach(organizationId, user), + }); + options.push({ + long: `remove`, + short: `r`, + text: `remove all roles and access`, + action: () => role_user_attach_role(user, new AccessId(pendingId)), + }); + return await choice("What would you like to do?", options).then(); + } + catch (e) { + throw e; + } +} +async function role_auto_new_domain(organizationId, domain) { + try { + const roles = await listRoles(organizationId); + const options = roles + .filter((role) => !SPECIAL_ROLES.includes(role.name)) + .map((role) => { + return { + long: role.id, + text: `auto assign ${role.name} (${role.id})`, + action: () => role_auto_domain_role(domain, new AccessId(role.id)), + }; + }); + return await choice("Which role should new users get?", options).then((x) => x); + } + catch (e) { + throw e; + } +} +async function role_auto_new(organizationId) { + try { + const domain = await shortText("Domain", "Email domain to auto approve.", `@example.com`).then(); + return role_auto_new_domain(organizationId, domain); + } + catch (e) { + throw e; + } +} +async function role_auto(organizationId) { + try { + const resp = await sshReq(`preapprove-list`, organizationId.toString()); + const domains = JSON.parse(resp); + const doms = {}; + domains.forEach((x) => { + if (doms[x.domain] === undefined) + doms[x.domain] = []; + doms[x.domain].push(x.access); + }); + const options = Object.keys(doms).map((domain) => { + return { + long: domain, + text: `remove ${domain} (${doms[domain].join(", ")})`, + action: () => role_auto_remove(organizationId, domain), + }; + }); + options.push({ + long: `new`, + short: `n`, + text: `setup a new domain rule`, + action: () => role_auto_new(organizationId), + }); + return await choice("What would you like to do?", options).then(); + } + catch (e) { + throw e; + } +} +async function service_user(organization) { + try { + const name = await shortText("Name", "Display name for the service user", `Service User`).then(); + const file = ".merrymake/" + toFolderName(name) + ".key"; + addToExecuteQueue(() => do_create_deployment_agent(organization, name, file)); + return finish(); + } + catch (e) { + throw e; + } +} +let userListCache; +export async function listUsers(organizationId) { + if (userListCache === undefined) { + const resp = await sshReq(`user-list`, organizationId.toString()); + if (!resp.startsWith("[")) + throw resp; + userListCache = JSON.parse(resp); + } + return userListCache; +} +export async function pending(organization) { + try { + const users = await listUsers(organization.id); + const options = users + .filter((u) => u.roles[0] === "Pending") + .map((user) => { + return { + long: user.email, + text: `${user.email}: ${user.roles}`, + action: () => role_user(organization.id, user.id), + }; + }); + return await choice("Which user do you want to allow?", options).then(); + } + catch (e) { + throw e; + } +} +export async function role(organization) { + try { + const users = await listUsers(organization.id); + const options = users + .filter((u) => u.roles[0] !== "Pending") + .map((user) => { + return { + long: user.email, + text: `${user.email}: ${user.roles}`, + action: () => role_user(organization.id, user.id), + }; + }); + // options.push({ + // long: `new`, + // short: `n`, + // text: `create a new role`, + // action: () => role_new(org), + // }); + options.push({ + long: `pending`, + short: `p`, + text: `see or allow pending users`, + action: () => pending(organization), + }); + options.push({ + long: `service`, + short: `s`, + text: `create a new service user`, + action: () => service_user(organization), + }); + options.push({ + long: `auto`, + short: `a`, + text: `configure domain auto approval`, + action: () => role_auto(organization.id), + }); + return await choice("Which user do you want to manage?", options).then(); + } + catch (e) { + throw e; + } } diff --git a/newCommands/role.ts b/newCommands/role.ts index 9b05794..ad8fae8 100644 --- a/newCommands/role.ts +++ b/newCommands/role.ts @@ -1,19 +1,18 @@ -import { getArgs } from "../args"; -import { Option, choice, shortText } from "../prompt"; -import { AccessId, Organization, OrganizationId } from "../types"; +import { Option, choice, shortText } from "../prompt.js"; +import { AccessId, Organization, OrganizationId } from "../types.js"; import { addToExecuteQueue, finish, - output2, + outputGit, sshReq, toFolderName, -} from "../utils"; -import { do_create_deployment_agent } from "./hosting"; +} from "../utils.js"; +import { do_create_deployment_agent } from "./hosting.js"; const SPECIAL_ROLES = ["Pending", "Build agent", "Deployment agent"]; export async function do_attach_role(user: string, accessId: AccessId) { try { - output2( + outputGit( await sshReq(`user-assign`, user, `--accessId`, accessId.toString()) ); } catch (e) { @@ -23,7 +22,7 @@ export async function do_attach_role(user: string, accessId: AccessId) { export async function do_auto_approve(domain: string, accessId: AccessId) { try { - output2( + outputGit( await sshReq(`preapprove-add`, `--accessId`, accessId.toString(), domain) ); } catch (e) { @@ -36,7 +35,7 @@ export async function do_remove_auto_approve( domain: string ) { try { - output2( + outputGit( await sshReq( `preapprove-remove`, `--organizationId`, @@ -198,24 +197,58 @@ async function service_user(organization: Organization) { } } +let userListCache: { email: string; id: string; roles: string }[] | undefined; +export async function listUsers(organizationId: OrganizationId) { + if (userListCache === undefined) { + const resp = await sshReq(`user-list`, organizationId.toString()); + if (!resp.startsWith("[")) throw resp; + userListCache = JSON.parse(resp); + } + return userListCache!; +} + +export async function pending(organization: Organization) { + try { + const users = await listUsers(organization.id); + const options: Option[] = users + .filter((u) => u.roles[0] === "Pending") + .map((user) => { + return { + long: user.email, + text: `${user.email}: ${user.roles}`, + action: () => role_user(organization.id, user.id), + }; + }); + return await choice("Which user do you want to allow?", options).then(); + } catch (e) { + throw e; + } +} + export async function role(organization: Organization) { try { - const resp = await sshReq(`user-list`, organization.id.toString()); - const users: { email: string; id: string; roles: string }[] = - JSON.parse(resp); - const options: Option[] = users.map((user) => { - return { - long: user.email, - text: `${user.email}: ${user.roles}`, - action: () => role_user(organization.id, user.id), - }; - }); + const users = await listUsers(organization.id); + const options: Option[] = users + .filter((u) => u.roles[0] !== "Pending") + .map((user) => { + return { + long: user.email, + text: `${user.email}: ${user.roles}`, + action: () => role_user(organization.id, user.id), + }; + }); // options.push({ // long: `new`, // short: `n`, // text: `create a new role`, // action: () => role_new(org), // }); + options.push({ + long: `pending`, + short: `p`, + text: `see or allow pending users`, + action: () => pending(organization), + }); options.push({ long: `service`, short: `s`, @@ -228,9 +261,7 @@ export async function role(organization: Organization) { text: `configure domain auto approval`, action: () => role_auto(organization.id), }); - return await choice("Which user do you want to manage?", options).then( - (x) => x - ); + return await choice("Which user do you want to manage?", options).then(); } catch (e) { throw e; } diff --git a/newCommands/start.js b/newCommands/start.js index 4f13a2e..edad361 100644 --- a/newCommands/start.js +++ b/newCommands/start.js @@ -1,21 +1,7 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.start = start; -function start() { - return __awaiter(this, void 0, void 0, function* () { - try { - } - catch (e) { - throw e; - } - }); +export async function start() { + try { + } + catch (e) { + throw e; + } } diff --git a/newCommands/wait.js b/newCommands/wait.js index 701d896..ce364f1 100644 --- a/newCommands/wait.js +++ b/newCommands/wait.js @@ -1,25 +1,11 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.wait = wait; -const prompt_1 = require("../prompt"); -function wait(text, action) { - return __awaiter(this, void 0, void 0, function* () { - try { - return (0, prompt_1.choice)(text, [{ long: "continue", text: "continue", action }], { - disableAutoPick: true, - }); - } - catch (e) { - throw e; - } - }); +import { choice } from "../prompt.js"; +export async function wait(text, action) { + try { + return choice(text, [{ long: "continue", text: "continue", action }], { + disableAutoPick: true, + }); + } + catch (e) { + throw e; + } } diff --git a/newCommands/wait.ts b/newCommands/wait.ts index 25cebc2..487c948 100644 --- a/newCommands/wait.ts +++ b/newCommands/wait.ts @@ -1,4 +1,4 @@ -import { choice } from "../prompt"; +import { choice } from "../prompt.js"; export async function wait(text: string, action: () => Promise) { try { diff --git a/package-lock.json b/package-lock.json index 9a41dcd..7b11e2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,29 +1,30 @@ { "name": "@merrymake/cli", - "version": "4.2.0", + "version": "4.3.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@merrymake/cli", - "version": "4.2.0", + "version": "4.3.1", "license": "ISC", "dependencies": { - "@merrymake/detect-project-type": "^1.2.0", - "@merrymake/ext2mime": "^1.0.3", - "@merrymake/secret-lib": "^1.0.0", - "cookie-parser": "^1.4.6", - "express": "^4.17.3" + "@merrymake/detect-project-type": "latest", + "@merrymake/ext2mime": "latest", + "@merrymake/secret-lib": "latest", + "cookie-parser": "latest", + "express": "latest" }, "bin": { "mm": "mm.js", "mmk": "mmk.js" }, "devDependencies": { - "@types/cookie-parser": "^1.4.6", - "@types/express": "^4.17.13", - "@types/node": "^16.7.4", - "pkg": "^5.8.1" + "@tsconfig/node-lts": "latest", + "@types/cookie-parser": "latest", + "@types/express": "latest", + "@types/node": "latest", + "pkg": "latest" } }, "node_modules/@babel/generator": { @@ -133,9 +134,12 @@ } }, "node_modules/@merrymake/detect-project-type": { - "version": "1.5.7", - "resolved": "https://registry.npmjs.org/@merrymake/detect-project-type/-/detect-project-type-1.5.7.tgz", - "integrity": "sha512-VkC6RTpazfABHLaXqixFbZNvBVRBD/CgtZwu4+CXSY+2c0CPn56wHV7Bk+wuaC3IbJuVmWO9DCtWzdB3TfdVHA==" + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@merrymake/detect-project-type/-/detect-project-type-2.0.2.tgz", + "integrity": "sha512-YD8c2kKMOrjNm6u5rtIAs3kmvFG/RwY8h89etmWyiAR2OUM7wFWW8XYTLtER9EMD505haR6ggjcr5VPIkRUygw==", + "dependencies": { + "strip-json-comments": "^5.0.1" + } }, "node_modules/@merrymake/ext2mime": { "version": "1.0.3", @@ -182,6 +186,12 @@ "node": ">= 8" } }, + "node_modules/@tsconfig/node-lts": { + "version": "22.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node-lts/-/node-lts-22.0.1.tgz", + "integrity": "sha512-BwlbLiYurZKrj+Pa6etSE1jXmr3VEDdgJto1jEYKcpBVwZZSWVkCPyFEFYbHdIOaFMlSTtV206DYPlT109aqug==", + "dev": true + }, "node_modules/@types/body-parser": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", @@ -211,21 +221,21 @@ } }, "node_modules/@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.0.tgz", + "integrity": "sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==", "dev": true, "dependencies": { "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", + "@types/express-serve-static-core": "^5.0.0", "@types/qs": "*", "@types/serve-static": "*" } }, "node_modules/@types/express-serve-static-core": { - "version": "4.19.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", - "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.2.tgz", + "integrity": "sha512-vluaspfvWEtE4vcSDlKRNer52DvOGrB2xv6diXy6UKyKW0lqZiWHGNApSyxOv+8DE5Z27IzVvE7hNkxg7EXIcg==", "dev": true, "dependencies": { "@types/node": "*", @@ -247,10 +257,13 @@ "dev": true }, "node_modules/@types/node": { - "version": "16.18.121", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.121.tgz", - "integrity": "sha512-Gk/pOy8H0cvX8qNrwzElYIECpcUn87w4EAEFXFvPJ8qsP9QR/YqukUORSy0zmyDyvdo149idPpy4W6iC5aSbQA==", - "dev": true + "version": "22.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.1.tgz", + "integrity": "sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==", + "dev": true, + "dependencies": { + "undici-types": "~6.20.0" + } }, "node_modules/@types/qs": { "version": "6.9.17", @@ -1741,6 +1754,15 @@ "rc": "cli.js" } }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", @@ -2073,12 +2095,14 @@ } }, "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true, + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.1.tgz", + "integrity": "sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==", "engines": { - "node": ">=0.10.0" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/supports-color": { @@ -2206,6 +2230,12 @@ "node": ">= 0.6" } }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", diff --git a/package.json b/package.json index 0bddb9d..03ccb3f 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,9 @@ { "name": "@merrymake/cli", - "version": "4.3.1", + "version": "4.4.0", "description": "", "main": "index.js", + "type": "module", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "pack": "pkg -c pkg.json pkg.js" @@ -23,16 +24,17 @@ "author": "", "license": "ISC", "dependencies": { - "@merrymake/detect-project-type": "^1.2.0", - "@merrymake/ext2mime": "^1.0.3", - "@merrymake/secret-lib": "^1.0.0", - "cookie-parser": "^1.4.6", - "express": "^4.17.3" + "@merrymake/detect-project-type": "latest", + "@merrymake/ext2mime": "latest", + "@merrymake/secret-lib": "latest", + "cookie-parser": "latest", + "express": "latest" }, "devDependencies": { - "@types/cookie-parser": "^1.4.6", - "@types/express": "^4.17.13", - "@types/node": "^16.7.4", - "pkg": "^5.8.1" + "@types/cookie-parser": "latest", + "@types/express": "latest", + "@types/node": "latest", + "pkg": "latest", + "@tsconfig/node-lts": "latest" } } diff --git a/pkg.js b/pkg.js index b1a5848..0f62614 100644 --- a/pkg.js +++ b/pkg.js @@ -1,13 +1,11 @@ #!/usr/bin/env node -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); process.env["COMMAND"] = "mm"; -const node_process_1 = require("node:process"); -if (!node_process_1.stdin.isTTY || node_process_1.stdin.setRawMode === undefined) { +import { stdin } from "node:process"; +if (!stdin.isTTY || stdin.setRawMode === undefined) { console.log("This console does not support TTY, please use 'winpty mm' instead."); process.exit(1); } -const prompt_1 = require("./prompt"); +import { YELLOW, NORMAL_COLOR } from "./prompt.js"; process.env["UPDATE_MESSAGE"] = `get the latest version from: -${prompt_1.YELLOW}https://github.com/merrymake/cli/releases${prompt_1.NORMAL_COLOR}`; -require("./index"); +${YELLOW}https://github.com/merrymake/cli/releases${NORMAL_COLOR}`; +import "./index.js"; diff --git a/pkg.ts b/pkg.ts index 55ed8a2..4fbe8bb 100644 --- a/pkg.ts +++ b/pkg.ts @@ -7,7 +7,7 @@ if (!stdin.isTTY || stdin.setRawMode === undefined) { ); process.exit(1); } -import { YELLOW, NORMAL_COLOR } from "./prompt"; +import { YELLOW, NORMAL_COLOR } from "./prompt.js"; process.env["UPDATE_MESSAGE"] = `get the latest version from: ${YELLOW}https://github.com/merrymake/cli/releases${NORMAL_COLOR}`; -import "./index"; +import "./index.js"; diff --git a/prompt.js b/prompt.js index ff6b495..35229b5 100644 --- a/prompt.js +++ b/prompt.js @@ -1,63 +1,86 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Visibility = exports.INVISIBLE = exports.YELLOW = exports.GREEN = exports.BLUE = exports.RED = exports.NORMAL_COLOR = exports.SHOW_CURSOR = exports.HIDE_CURSOR = exports.RIGHT = exports.LEFT = exports.DOWN = exports.UP = exports.ENTER = exports.DELETE = exports.ESCAPE = exports.BACKSPACE = exports.CTRL_C = void 0; -exports.output = output; -exports.choice = choice; -exports.multiSelect = multiSelect; -exports.spinner_start = spinner_start; -exports.spinner_stop = spinner_stop; -exports.shortText = shortText; -exports.exit = exit; -const node_process_1 = require("node:process"); -const args_1 = require("./args"); -const utils_1 = require("./utils"); -const contexts_1 = require("./contexts"); -exports.CTRL_C = "\u0003"; +import { stdin, stdout } from "node:process"; +import { getArgs } from "./args.js"; +import { CONTEXTS } from "./contexts.js"; +import { abort } from "./utils.js"; +export const CTRL_C = "\u0003"; // const CR = "\u000D"; -exports.BACKSPACE = "\b"; -exports.ESCAPE = "\u001b"; -exports.DELETE = "\u001b[3~"; -exports.ENTER = "\r"; -exports.UP = "\u001b[A"; -exports.DOWN = "\u001b[B"; -exports.LEFT = "\u001b[D"; -exports.RIGHT = "\u001b[C"; -exports.HIDE_CURSOR = "\u001B[?25l"; -exports.SHOW_CURSOR = "\u001B[?25h"; -exports.NORMAL_COLOR = "\u001B[0m"; -exports.RED = "\u001B[0;31m"; -exports.BLUE = "\u001B[0;34m"; -exports.GREEN = "\u001B[0;32m"; -exports.YELLOW = "\u001B[0;93m"; -exports.INVISIBLE = [ - exports.HIDE_CURSOR, - exports.SHOW_CURSOR, - exports.NORMAL_COLOR, - exports.RED, - exports.BLUE, - exports.GREEN, - exports.YELLOW, +export const BACKSPACE = "\b"; +export const ESCAPE = "\u001b"; +export const DELETE = "\u001b[3~"; +export const ENTER = "\r"; +export const UP = "\u001b[A"; +export const DOWN = "\u001b[B"; +export const LEFT = "\u001b[D"; +export const RIGHT = "\u001b[C"; +export const HIDE_CURSOR = "\u001B[?25l"; +export const SHOW_CURSOR = "\u001B[?25h"; +export const NORMAL_COLOR = "\u001B[0m"; +export const BLACK = "\x1b[30m"; +export const RED = "\x1b[31m"; +export const GREEN = "\x1b[32m"; +export const YELLOW = "\x1b[33m"; +export const BLUE = "\x1b[34m"; +export const PURPLE = "\x1b[35m"; +export const CYAN = "\x1b[36m"; +export const WHITE = "\x1b[37m"; +export const GRAY = "\x1b[90m"; +export const BgBlack = "\x1b[40m"; +export const BgRed = "\x1b[41m"; +export const BgGreen = "\x1b[42m"; +export const BgYellow = "\x1b[43m"; +export const BgBlue = "\x1b[44m"; +export const BgPurple = "\x1b[45m"; +export const BgCyan = "\x1b[46m"; +export const BgWhite = "\x1b[47m"; +export const BgGray = "\x1b[100m"; +export const STRIKE = "\x1b[9m"; +export const NOSTRIKE = "\x1b[29m"; +export const INVISIBLE = [ + HIDE_CURSOR, + SHOW_CURSOR, + NORMAL_COLOR, + BLACK, + RED, + GREEN, + YELLOW, + BLUE, + PURPLE, + CYAN, + WHITE, + GRAY, + BgBlack, + BgRed, + BgGreen, + BgYellow, + BgBlue, + BgPurple, + BgCyan, + BgWhite, + BgGray, + STRIKE, + NOSTRIKE, ]; +export const REMOVE_INVISIBLE = new RegExp(INVISIBLE.map((x) => x.replace(/\[/, "\\[").replace(/\?/, "\\?")).join("|"), "gi"); let xOffset = 0; let yOffset = 0; let maxYOffset = 0; -function output(str) { - const cleanStr = str.replace(new RegExp(exports.INVISIBLE.map((x) => x.replace(/\[/, "\\[").replace(/\?/, "\\?")).join("|"), "gi"), ""); - node_process_1.stdout.write(node_process_1.stdout.isTTY ? str : cleanStr); - if (!node_process_1.stdout.isTTY) +export function output(str) { + const cleanStr = str.replace(REMOVE_INVISIBLE, ""); + stdout.write(stdout.isTTY ? str : cleanStr); + if (!stdout.isTTY) return; const lines = cleanStr.split("\n"); const newXOffset = xOffset + lines[0].length; // TODO handle (split on) \r xOffset = newXOffset % - (typeof node_process_1.stdout.getWindowSize !== "function" + (typeof stdout.getWindowSize !== "function" ? 80 - : node_process_1.stdout.getWindowSize()[0]); + : stdout.getWindowSize()[0]); yOffset += ~~(newXOffset / - (typeof node_process_1.stdout.getWindowSize !== "function" + (typeof stdout.getWindowSize !== "function" ? 80 - : node_process_1.stdout.getWindowSize()[0])); + : stdout.getWindowSize()[0])); if (maxYOffset < yOffset) maxYOffset = yOffset; for (let i = 1; i < lines.length; i++) { @@ -65,16 +88,16 @@ function output(str) { yOffset += 1 + ~~(line.length / - (typeof node_process_1.stdout.getWindowSize !== "function" + (typeof stdout.getWindowSize !== "function" ? 80 - : node_process_1.stdout.getWindowSize()[0])); + : stdout.getWindowSize()[0])); if (maxYOffset < yOffset) maxYOffset = yOffset; xOffset = line.length % - (typeof node_process_1.stdout.getWindowSize !== "function" + (typeof stdout.getWindowSize !== "function" ? 80 - : node_process_1.stdout.getWindowSize()[0]); + : stdout.getWindowSize()[0]); } // stdout.moveCursor(-xOffset, -yOffset); // const pos = "" + xOffset + "," + yOffset; @@ -82,13 +105,13 @@ function output(str) { // stdout.moveCursor(xOffset - pos.length, yOffset); } function moveCursor(x, y) { - if (!node_process_1.stdout.isTTY) + if (!stdout.isTTY) return; xOffset += x; yOffset += y; if (maxYOffset < yOffset) maxYOffset = yOffset; - node_process_1.stdout.moveCursor(x, y); + stdout.moveCursor(x, y); } function moveCursorTo(x, y) { moveCursor(x - xOffset, y - yOffset); @@ -106,7 +129,7 @@ function makeSelectionSuperInternal(action, extra = () => { }) { cleanup(); extra(); if (listener !== undefined) - node_process_1.stdin.removeListener("data", listener); + stdin.removeListener("data", listener); return action(); } function makeSelectionInternal(option, extra) { @@ -132,22 +155,21 @@ function makeSelectionQuietly(option) { } let listener; function cleanup() { - output(exports.NORMAL_COLOR); - output(exports.SHOW_CURSOR); + output(NORMAL_COLOR); + output(SHOW_CURSOR); } -function choice(heading, options, opts) { +export function choice(heading, options, opts) { return new Promise((resolve, reject) => { - var _a, _b, _c; if (options.length === 0) { - console.log((opts === null || opts === void 0 ? void 0 : opts.errorMessage) || "There are no options."); + console.log(opts?.errorMessage || "There are no options."); process.exit(1); } - if (options.length === 1 && (opts === null || opts === void 0 ? void 0 : opts.disableAutoPick) !== true) { - if ((0, args_1.getArgs)().length > 0 && - ((0, args_1.getArgs)()[0] === options[0].long || - (0, args_1.getArgs)()[0] === `-${options[0].short}`)) - (0, args_1.getArgs)().splice(0, 1); - const prom = ((_a = opts === null || opts === void 0 ? void 0 : opts.invertedQuiet) === null || _a === void 0 ? void 0 : _a.cmd) === true + if (options.length === 1 && opts?.disableAutoPick !== true) { + if (getArgs().length > 0 && + (getArgs()[0] === options[0].long || + getArgs()[0] === `-${options[0].short}`)) + getArgs().splice(0, 1); + const prom = opts?.invertedQuiet?.cmd === true ? makeSelection(options[0]) : makeSelectionQuietly(options[0]); prom.then(resolve); @@ -158,15 +180,15 @@ function choice(heading, options, opts) { short: "x", long: "x", text: "exit", - action: () => (0, utils_1.abort)(), + action: () => abort(), }); const quick = {}; const str = [heading + "\n"]; for (let i = 0; i < options.length; i++) { const o = options[i]; - if ((0, args_1.getArgs)()[0] === o.long || (0, args_1.getArgs)()[0] === `-${o.short}`) { - (0, args_1.getArgs)().splice(0, 1); - const prom = ((_b = opts === null || opts === void 0 ? void 0 : opts.invertedQuiet) === null || _b === void 0 ? void 0 : _b.cmd) === true + if (getArgs()[0] === o.long || getArgs()[0] === `-${o.short}`) { + getArgs().splice(0, 1); + const prom = opts?.invertedQuiet?.cmd === true ? makeSelection(o) : makeSelectionQuietly(o); prom.then(resolve); @@ -182,71 +204,70 @@ function choice(heading, options, opts) { const before = o.text.substring(0, index); const after = o.text.substring(index + o.long.length); str.push(before); - str.push(exports.YELLOW); + str.push(YELLOW); str.push(o.long); - str.push(exports.NORMAL_COLOR); + str.push(NORMAL_COLOR); str.push(after); str.push("\n"); } - let pos = (opts === null || opts === void 0 ? void 0 : opts.def) || 0; - if ((0, args_1.getArgs)().length > 0) { - const arg = (0, args_1.getArgs)().splice(0, 1)[0]; + let pos = opts?.def || 0; + if (getArgs().length > 0) { + const arg = getArgs().splice(0, 1)[0]; if (arg === "_") { - const prom = ((_c = opts === null || opts === void 0 ? void 0 : opts.invertedQuiet) === null || _c === void 0 ? void 0 : _c.cmd) === true + const prom = opts?.invertedQuiet?.cmd === true ? makeSelection(options[pos]) : makeSelectionQuietly(options[pos]); prom.then(resolve); prom.catch(reject); return; } - else if (contexts_1.CONTEXTS[arg] !== undefined) - output(contexts_1.CONTEXTS[arg](arg) + "\n"); + else if (CONTEXTS[arg] !== undefined) + output(CONTEXTS[arg](arg) + "\n"); else - output(`${exports.RED}Invalid argument in the current context: ${arg}${exports.NORMAL_COLOR}\n`); - (0, args_1.getArgs)().splice(0, (0, args_1.getArgs)().length); + output(`${RED}Invalid argument in the current context: ${arg}${NORMAL_COLOR}\n`); + getArgs().splice(0, getArgs().length); } output(" \n"); - output(exports.HIDE_CURSOR); + output(HIDE_CURSOR); output(str.join("")); - if (!node_process_1.stdin.isTTY || node_process_1.stdin.setRawMode === undefined) { + if (!stdin.isTTY || stdin.setRawMode === undefined) { console.log("This console does not support TTY, please use the 'mmk'-command instead."); process.exit(1); } - output(exports.YELLOW); + output(YELLOW); moveCursor(0, -options.length + pos); output(`>`); moveCursor(-1, 0); // on any data into stdin - node_process_1.stdin.on("data", (listener = (key) => { - var _a; + stdin.on("data", (listener = (key) => { const k = key.toString(); // moveCursor(0, options.length - pos); // //let l = JSON.stringify(key); // //output(l); // stdout.write("" + yOffset); // moveCursor(-("" + yOffset).length, -options.length + pos); - if (k === exports.ENTER) { - const prom = ((_a = opts === null || opts === void 0 ? void 0 : opts.invertedQuiet) === null || _a === void 0 ? void 0 : _a.cmd) !== false + if (k === ENTER) { + const prom = opts?.invertedQuiet?.cmd !== false ? makeSelection(options[pos]) : makeSelectionQuietly(options[pos]); prom.then(resolve); prom.catch(reject); return; } - else if (k === exports.UP && pos <= 0) { + else if (k === UP && pos <= 0) { return; } - else if (k === exports.UP) { + else if (k === UP) { pos--; output(` `); moveCursor(-1, -1); output(`>`); moveCursor(-1, 0); } - else if (k === exports.DOWN && pos >= options.length - 1) { + else if (k === DOWN && pos >= options.length - 1) { return; } - else if (k === exports.DOWN) { + else if (k === DOWN) { pos++; output(` `); moveCursor(-1, 1); @@ -266,7 +287,7 @@ function choice(heading, options, opts) { } const SELECTED_MARK = "✔"; const NOT_SELECTED_MARK = "_"; -function multiSelect(selection, after, errorMessage) { +export function multiSelect(selection, after, errorMessage) { return new Promise((resolve, reject) => { // options.push({ // short: "x", @@ -279,19 +300,19 @@ function multiSelect(selection, after, errorMessage) { console.log(errorMessage); process.exit(1); } - if ((0, args_1.getArgs)().length > 0) { - const arg = (0, args_1.getArgs)()[0]; + if (getArgs().length > 0) { + const arg = getArgs()[0]; const es = arg.split(","); const result = {}; keys.forEach((e) => (result[e] = false)); const illegal = es.filter((e) => !keys.includes(e)); if (illegal.length > 0) { - output(`${exports.RED}Invalid arguments in the current context: ${illegal.join(", ")}${exports.NORMAL_COLOR}\n`); + output(`${RED}Invalid arguments in the current context: ${illegal.join(", ")}${NORMAL_COLOR}\n`); } else { es.forEach((e) => (result[e] = true)); } - (0, args_1.getArgs)().splice(0, (0, args_1.getArgs)().length); + getArgs().splice(0, getArgs().length); const prom = makeSelectionSuperInternal(() => after(selection)); prom.then(resolve); prom.catch(reject); @@ -309,26 +330,26 @@ function multiSelect(selection, after, errorMessage) { str.push(` [s] submit\n`); str.push(` [x] exit\n`); output(" \n"); - output(exports.HIDE_CURSOR); + output(HIDE_CURSOR); output(str.join("")); - if (!node_process_1.stdin.isTTY || node_process_1.stdin.setRawMode === undefined) { + if (!stdin.isTTY || stdin.setRawMode === undefined) { console.log("This console does not support TTY, please use the 'mmk'-command instead."); process.exit(1); } let pos = 0; - output(exports.YELLOW); + output(YELLOW); moveCursor(0, -(keys.length + 2) + pos); output(`>`); moveCursor(-1, 0); // on any data into stdin - node_process_1.stdin.on("data", (listener = (key) => { + stdin.on("data", (listener = (key) => { const k = key.toString(); // moveCursor(0, options.length - pos); // //let l = JSON.stringify(key); // //output(l); // stdout.write("" + yOffset); // moveCursor(-("" + yOffset).length, -options.length + pos); - if (k === exports.ENTER) { + if (k === ENTER) { if (pos === keys.length) { // Submit const selected = keys @@ -353,7 +374,7 @@ function multiSelect(selection, after, errorMessage) { short: "x", long: "x", text: "exit", - action: () => (0, utils_1.abort)(), + action: () => abort(), }); prom.then(resolve); prom.catch(reject); @@ -362,27 +383,27 @@ function multiSelect(selection, after, errorMessage) { else { const sel = (selection[keys[pos]] = !selection[keys[pos]]); moveCursor(2, 0); - output(exports.NORMAL_COLOR); + output(NORMAL_COLOR); output(sel ? SELECTED_MARK : NOT_SELECTED_MARK); - output(exports.YELLOW); + output(YELLOW); moveCursor(-3, 0); } return; } - else if (k === exports.UP && pos <= 0) { + else if (k === UP && pos <= 0) { return; } - else if (k === exports.UP) { + else if (k === UP) { pos--; output(` `); moveCursor(-1, -1); output(`>`); moveCursor(-1, 0); } - else if (k === exports.DOWN && pos >= keys.length + 2 - 1) { + else if (k === DOWN && pos >= keys.length + 2 - 1) { return; } - else if (k === exports.DOWN) { + else if (k === DOWN) { pos++; output(` `); moveCursor(-1, 1); @@ -394,7 +415,7 @@ function multiSelect(selection, after, errorMessage) { short: "x", long: "x", text: "exit", - action: () => (0, utils_1.abort)(), + action: () => abort(), }); prom.then(resolve); prom.catch(reject); @@ -423,8 +444,8 @@ function multiSelect(selection, after, errorMessage) { let interval; let spinnerIndex = 0; const SPINNER = ["│", "/", "─", "\\"]; -function spinner_start() { - if (!node_process_1.stdout.isTTY) +export function spinner_start() { + if (!stdout.isTTY) return; interval = setInterval(spin, 200); } @@ -432,24 +453,33 @@ function spin() { output(SPINNER[(spinnerIndex = (spinnerIndex + 1) % SPINNER.length)]); moveCursor(-1, 0); } -function spinner_stop() { +export function spinner_stop() { if (interval !== undefined) { clearInterval(interval); interval = undefined; } } -var Visibility; +export var Visibility; (function (Visibility) { Visibility[Visibility["Secret"] = 0] = "Secret"; Visibility[Visibility["Public"] = 1] = "Public"; -})(Visibility || (exports.Visibility = Visibility = {})); -function shortText(prompt, description, defaultValueArg, hide = Visibility.Public) { +})(Visibility || (Visibility = {})); +export var Formatting; +(function (Formatting) { + Formatting[Formatting["Normal"] = 0] = "Normal"; + Formatting[Formatting["Minimal"] = 1] = "Minimal"; +})(Formatting || (Formatting = {})); +export function shortText(prompt, description, defaultValueArg, options) { return new Promise((resolve) => { + const hide = options?.hide === undefined ? Visibility.Public : options.hide; + const formatting = options?.formatting === undefined + ? Formatting.Normal + : options.formatting; if (hide === Visibility.Secret) hasSecret = true; const defaultValue = defaultValueArg === null ? "" : defaultValueArg; - if ((0, args_1.getArgs)()[0] !== undefined) { - const result = (0, args_1.getArgs)()[0] === "_" ? defaultValue : (0, args_1.getArgs)()[0]; + if (getArgs()[0] !== undefined) { + const result = getArgs()[0] === "_" ? defaultValue : getArgs()[0]; command += " " + (result.includes(" ") @@ -457,33 +487,35 @@ function shortText(prompt, description, defaultValueArg, hide = Visibility.Publi : result.length === 0 ? "_" : result); - (0, args_1.getArgs)().splice(0, 1); + getArgs().splice(0, 1); moveToBottom(); cleanup(); if (listener !== undefined) - node_process_1.stdin.removeListener("data", listener); + stdin.removeListener("data", listener); resolve(result); return; } let str = prompt; - if (defaultValue === "") - str += ` (${exports.YELLOW}optional${exports.NORMAL_COLOR})`; - else - str += ` (suggestion: ${exports.YELLOW}${defaultValue}${exports.NORMAL_COLOR})`; - str += ": "; + if (formatting === Formatting.Normal) { + if (defaultValue === "") + str += ` (${YELLOW}optional${NORMAL_COLOR})`; + else + str += ` (suggestion: ${YELLOW}${defaultValue}${NORMAL_COLOR})`; + str += ": "; + } output(" \n"); output(str); const [prevX, prevY] = getCursorPosition(); output("\n"); moveCursorTo(prevX, prevY); - if (!node_process_1.stdin.isTTY || node_process_1.stdin.setRawMode === undefined) { + if (!stdin.isTTY || stdin.setRawMode === undefined) { console.log("This console does not support TTY, please use the 'mmk'-command instead."); process.exit(1); } let beforeCursor = ""; let afterCursor = ""; // on any data into stdin - node_process_1.stdin.on("data", (listener = (key) => { + stdin.on("data", (listener = (key) => { const k = key.toString(); // moveCursor(-str.length, 0); // const l = JSON.stringify(key); @@ -491,7 +523,7 @@ function shortText(prompt, description, defaultValueArg, hide = Visibility.Publi // output("" + afterCursor.length); // moveCursor(str.length - l.length, 0); // moveCursor(-l.length, -options.length + pos); - if (k === exports.ENTER) { + if (k === ENTER) { moveToBottom(); cleanup(); const combinedStr = beforeCursor + afterCursor; @@ -508,55 +540,55 @@ function shortText(prompt, description, defaultValueArg, hide = Visibility.Publi } output("\n"); if (listener !== undefined) - node_process_1.stdin.removeListener("data", listener); + stdin.removeListener("data", listener); resolve(result); return; } - else if (k === exports.UP || k === exports.DOWN) { + else if (k === UP || k === DOWN) { } - else if (k === exports.ESCAPE) { + else if (k === ESCAPE) { const [prevX, prevY] = getCursorPosition(); moveCursor(-str.length - beforeCursor.length, 1); output(description); moveCursorTo(prevX, prevY); } - else if (k === exports.LEFT && beforeCursor.length > 0) { + else if (k === LEFT && beforeCursor.length > 0) { moveCursor(-beforeCursor.length, 0); afterCursor = beforeCursor.charAt(beforeCursor.length - 1) + afterCursor; beforeCursor = beforeCursor.substring(0, beforeCursor.length - 1); - node_process_1.stdout.clearLine(1); + stdout.clearLine(1); output(hide === Visibility.Secret ? (beforeCursor + afterCursor).replace(/./g, "*") : beforeCursor + afterCursor); moveCursor(-afterCursor.length, 0); } - else if (k === exports.RIGHT && afterCursor.length > 0) { + else if (k === RIGHT && afterCursor.length > 0) { moveCursor(-beforeCursor.length, 0); beforeCursor += afterCursor.charAt(0); afterCursor = afterCursor.substring(1); - node_process_1.stdout.clearLine(1); + stdout.clearLine(1); output(hide === Visibility.Secret ? (beforeCursor + afterCursor).replace(/./g, "*") : beforeCursor + afterCursor); moveCursor(-afterCursor.length, 0); } - else if (k === exports.DELETE && afterCursor.length > 0) { + else if (k === DELETE && afterCursor.length > 0) { moveCursor(-beforeCursor.length, 0); afterCursor = afterCursor.substring(1); - node_process_1.stdout.clearLine(1); + stdout.clearLine(1); output(hide === Visibility.Secret ? (beforeCursor + afterCursor).replace(/./g, "*") : beforeCursor + afterCursor); moveCursor(-afterCursor.length, 0); } - else if ((k === exports.BACKSPACE || + else if ((k === BACKSPACE || k.charCodeAt(0) === 8 || k.charCodeAt(0) === 127) && beforeCursor.length > 0) { moveCursor(-beforeCursor.length, 0); beforeCursor = beforeCursor.substring(0, beforeCursor.length - 1); - node_process_1.stdout.clearLine(1); + stdout.clearLine(1); output(hide === Visibility.Secret ? (beforeCursor + afterCursor).replace(/./g, "*") : beforeCursor + afterCursor); @@ -565,7 +597,7 @@ function shortText(prompt, description, defaultValueArg, hide = Visibility.Publi else if (/^[A-Za-z0-9@_, .\-/:;#=&*?!"'`^%£$€+<>()\[\]{}\\]+$/.test(k)) { moveCursor(-beforeCursor.length, 0); beforeCursor += k; - node_process_1.stdout.clearLine(1); + stdout.clearLine(1); output(hide === Visibility.Secret ? (beforeCursor + afterCursor).replace(/./g, "*") : beforeCursor + afterCursor); @@ -576,7 +608,30 @@ function shortText(prompt, description, defaultValueArg, hide = Visibility.Publi })); }); } -function exit() { +let seconds = 0; +export function timer_start(suffix = "") { + if (!stdout.isTTY) + return; + seconds = 0; + const out = seconds.toString().padStart(3, " ") + suffix; + output(out); + interval = setInterval(() => time(suffix), 1000); +} +function time(suffix) { + seconds++; + const out = seconds.toString().padStart(3, " ") + suffix; + moveCursor(-out.length, 0); + output(out); +} +export function timer_stop() { + if (interval !== undefined) { + clearInterval(interval); + interval = undefined; + return true; + } + return false; +} +export function exit() { moveToBottom(); cleanup(); } diff --git a/prompt.ts b/prompt.ts index db2ee71..597ae52 100644 --- a/prompt.ts +++ b/prompt.ts @@ -1,7 +1,7 @@ import { stdin, stdout } from "node:process"; -import { getArgs } from "./args"; -import { abort } from "./utils"; -import { CONTEXTS } from "./contexts"; +import { getArgs } from "./args.js"; +import { CONTEXTS } from "./contexts.js"; +import { abort } from "./utils.js"; export const CTRL_C = "\u0003"; // const CR = "\u000D"; @@ -16,33 +16,66 @@ export const RIGHT = "\u001b[C"; export const HIDE_CURSOR = "\u001B[?25l"; export const SHOW_CURSOR = "\u001B[?25h"; export const NORMAL_COLOR = "\u001B[0m"; -export const RED = "\u001B[0;31m"; -export const BLUE = "\u001B[0;34m"; -export const GREEN = "\u001B[0;32m"; -export const YELLOW = "\u001B[0;93m"; + +export const BLACK = "\x1b[30m"; +export const RED = "\x1b[31m"; +export const GREEN = "\x1b[32m"; +export const YELLOW = "\x1b[33m"; +export const BLUE = "\x1b[34m"; +export const PURPLE = "\x1b[35m"; +export const CYAN = "\x1b[36m"; +export const WHITE = "\x1b[37m"; +export const GRAY = "\x1b[90m"; + +export const BgBlack = "\x1b[40m"; +export const BgRed = "\x1b[41m"; +export const BgGreen = "\x1b[42m"; +export const BgYellow = "\x1b[43m"; +export const BgBlue = "\x1b[44m"; +export const BgPurple = "\x1b[45m"; +export const BgCyan = "\x1b[46m"; +export const BgWhite = "\x1b[47m"; +export const BgGray = "\x1b[100m"; + +export const STRIKE = "\x1b[9m"; +export const NOSTRIKE = "\x1b[29m"; + export const INVISIBLE = [ HIDE_CURSOR, SHOW_CURSOR, NORMAL_COLOR, + BLACK, RED, - BLUE, GREEN, YELLOW, + BLUE, + PURPLE, + CYAN, + WHITE, + GRAY, + BgBlack, + BgRed, + BgGreen, + BgYellow, + BgBlue, + BgPurple, + BgCyan, + BgWhite, + BgGray, + STRIKE, + NOSTRIKE, ]; +export const REMOVE_INVISIBLE = new RegExp( + INVISIBLE.map((x) => x.replace(/\[/, "\\[").replace(/\?/, "\\?")).join("|"), + "gi" +); + let xOffset = 0; let yOffset = 0; let maxYOffset = 0; export function output(str: string) { - const cleanStr = str.replace( - new RegExp( - INVISIBLE.map((x) => x.replace(/\[/, "\\[").replace(/\?/, "\\?")).join( - "|" - ), - "gi" - ), - "" - ); + const cleanStr = str.replace(REMOVE_INVISIBLE, ""); stdout.write(stdout.isTTY ? str : cleanStr); if (!stdout.isTTY) return; const lines = cleanStr.split("\n"); @@ -480,7 +513,7 @@ export function multiSelect( }); } -let interval: NodeJS.Timer | undefined; +let interval: NodeJS.Timeout | undefined; let spinnerIndex = 0; const SPINNER = ["│", "/", "─", "\\"]; export function spinner_start() { @@ -502,13 +535,22 @@ export enum Visibility { Secret, Public, } +export enum Formatting { + Normal, + Minimal, +} export function shortText( prompt: string, description: string, defaultValueArg: string | null, - hide = Visibility.Public + options?: { hide?: Visibility; formatting?: Formatting } ) { return new Promise((resolve) => { + const hide = options?.hide === undefined ? Visibility.Public : options.hide; + const formatting = + options?.formatting === undefined + ? Formatting.Normal + : options.formatting; if (hide === Visibility.Secret) hasSecret = true; const defaultValue = defaultValueArg === null ? "" : defaultValueArg; if (getArgs()[0] !== undefined) { @@ -529,9 +571,11 @@ export function shortText( } let str = prompt; - if (defaultValue === "") str += ` (${YELLOW}optional${NORMAL_COLOR})`; - else str += ` (suggestion: ${YELLOW}${defaultValue}${NORMAL_COLOR})`; - str += ": "; + if (formatting === Formatting.Normal) { + if (defaultValue === "") str += ` (${YELLOW}optional${NORMAL_COLOR})`; + else str += ` (suggestion: ${YELLOW}${defaultValue}${NORMAL_COLOR})`; + str += ": "; + } output(" \n"); output(str); @@ -654,6 +698,29 @@ export function shortText( }); } +let seconds = 0; +export function timer_start(suffix = "") { + if (!stdout.isTTY) return; + seconds = 0; + const out = seconds.toString().padStart(3, " ") + suffix; + output(out); + interval = setInterval(() => time(suffix), 1000); +} +function time(suffix: string) { + seconds++; + const out = seconds.toString().padStart(3, " ") + suffix; + moveCursor(-out.length, 0); + output(out); +} +export function timer_stop() { + if (interval !== undefined) { + clearInterval(interval); + interval = undefined; + return true; + } + return false; +} + export function exit() { moveToBottom(); cleanup(); diff --git a/questions.js b/questions.js index 8e93f73..28a2eae 100644 --- a/questions.js +++ b/questions.js @@ -1,4 +1,4 @@ -"use strict"; +export {}; // import fs from "fs"; // import { key } from "./newCommands/apikey"; // import { checkout } from "./commands/clone"; diff --git a/simulator.js b/simulator.js index 2a9b49e..4d36c8a 100644 --- a/simulator.js +++ b/simulator.js @@ -1,52 +1,40 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Run = void 0; -const utils_1 = require("./utils"); -const express_1 = __importDefault(require("express")); -const fs_1 = __importDefault(require("fs")); -const child_process_1 = require("child_process"); -const detect_project_type_1 = require("@merrymake/detect-project-type"); -const http_1 = __importDefault(require("http")); -const prompt_1 = require("./prompt"); -const cookie_parser_1 = __importDefault(require("cookie-parser")); +import { detectProjectType, RUN_COMMAND, } from "@merrymake/detect-project-type"; +import { spawn } from "child_process"; +import cookieParser from "cookie-parser"; +import express from "express"; +import fs from "fs"; +import http from "http"; +import { NORMAL_COLOR, YELLOW } from "./prompt.js"; +import { directoryNames, fetchOrg, outputGit, Path } from "./utils.js"; const MILLISECONDS = 1; const SECONDS = 1000 * MILLISECONDS; const MINUTES = 60 * SECONDS; const HOURS = 60 * MINUTES; const DEFAULT_TIMEOUT = 5 * MINUTES; -class Run { +export class Run { + port; + hooks; + pathToRoot; constructor(port) { this.port = port; - const { pathToRoot } = (0, utils_1.fetchOrg)(); + const { pathToRoot } = fetchOrg(); this.pathToRoot = pathToRoot; } execute() { return new Promise((resolve) => { - const app = (0, express_1.default)(); - const server = http_1.default.createServer(app); - const withSession = (0, cookie_parser_1.default)(); + const app = express(); + const server = http.createServer(app); + const withSession = cookieParser(); app.use((req, res, next) => { if (req.is("multipart/form-data") || req.is("application/x-www-form-urlencoded")) { - express_1.default.urlencoded({ extended: true, limit: "10mb" })(req, res, next); + express.urlencoded({ extended: true, limit: "10mb" })(req, res, next); } else { - express_1.default.raw({ type: "*/*", limit: "10mb" })(req, res, next); + express.raw({ type: "*/*", limit: "10mb" })(req, res, next); } }); - app.post("/trace/:sessionId/:traceId/:event", (req, res) => __awaiter(this, void 0, void 0, function* () { + app.post("/trace/:sessionId/:traceId/:event", async (req, res) => { try { const traceId = req.params.traceId; const sessionId = req.params.sessionId; @@ -61,11 +49,11 @@ class Run { else throw e; } - })); - app.get("/rapids/:event", withSession, (req, res) => __awaiter(this, void 0, void 0, function* () { + }); + app.get("/rapids/:event", withSession, async (req, res) => { try { const payload = Buffer.from(JSON.stringify(req.query)); - yield this.processEvent(req, res, payload); + await this.processEvent(req, res, payload); } catch (e) { if (e.data !== undefined) @@ -73,15 +61,15 @@ class Run { else throw e; } - })); - app.all("/rapids/:event", withSession, (req, res) => __awaiter(this, void 0, void 0, function* () { + }); + app.all("/rapids/:event", withSession, async (req, res) => { try { const payload = !Buffer.isBuffer(req.body) ? typeof req.body === "object" ? Buffer.from(JSON.stringify(req.body)) : Buffer.from(req.body) : req.body; - yield this.processEvent(req, res, payload); + await this.processEvent(req, res, payload); } catch (e) { if (e.data !== undefined) @@ -89,74 +77,71 @@ class Run { else throw e; } - })); + }); app.get("/rapids", (req, res) => { - res.send("Running..."); + res.send("Ready."); }); server.listen(this.port, () => { - (0, utils_1.output2)(""); - (0, utils_1.output2)(`88. .88 88 `); - (0, utils_1.output2)(`888. .888 88 `); - (0, utils_1.output2)(`88Y8. .8P88 88 `); - (0, utils_1.output2)(`88 Y8o8P 88 .88. 88.d8 88.d8 Yb dP 8888bd88b .88.8 88 .8P .88. `); - (0, utils_1.output2)(`88 Y8P 88 d" "b 88" 88" Yb dP 88 '88 '8b d" "8 88 .8P d" "b`); - (0, utils_1.output2)(`88 " 88 888888 88 88 Yb dP 88 88 88 8 8 88d8P 888888`); - (0, utils_1.output2)(`88 88 Y. 88 88 Y8P 88 88 88 Y. .8 88" 8b Y. `); - (0, utils_1.output2)(`88 88 "88P 88 88 dP 88 88 88 "88"8 88 "8b "88P `); - (0, utils_1.output2)(` dP `); - (0, utils_1.output2)(""); - (0, utils_1.output2)(`Running local Rapids on ${prompt_1.YELLOW}http://localhost:${this.port}/rapids${prompt_1.NORMAL_COLOR}`); - (0, utils_1.output2)(`To exit, press ctrl+c`); - (0, utils_1.output2)(""); + outputGit(""); + outputGit(`88. .88 88 `); + outputGit(`888. .888 88 `); + outputGit(`88Y8. .8P88 88 `); + outputGit(`88 Y8o8P 88 .88. 88.d8 88.d8 Yb dP 8888bd88b .88.8 88 .8P .88. `); + outputGit(`88 Y8P 88 d" "b 88" 88" Yb dP 88 '88 '8b d" "8 88 .8P d" "b`); + outputGit(`88 " 88 888888 88 88 Yb dP 88 88 88 8 8 88d8P 888888`); + outputGit(`88 88 Y. 88 88 Y8P 88 88 88 Y. .8 88" 8b Y. `); + outputGit(`88 88 "88P 88 88 dP 88 88 88 "88"8 88 "8b "88P `); + outputGit(` dP `); + outputGit(""); + outputGit(`Running local Rapids on ${YELLOW}http://localhost:${this.port}/rapids${NORMAL_COLOR}`); + outputGit(`To exit, press ctrl+c`); + outputGit(""); }); }); } - processEvent(req, res, payload) { - return __awaiter(this, void 0, void 0, function* () { - try { - let sessionId = req.cookies.sessionId; - if (!sessionId) { - sessionId = "s" + Math.random(); - res.cookie("sessionId", sessionId); - } - res.set("Access-Control-Allow-Origin", "*"); - const event = req.params.event; - this.hooks = new PublicHooks(this.pathToRoot); - const conf = this.hooks.getApiConfig(event); - const traceId = "t" + Math.random(); - pendingReplies[traceId] = { - resp: res, - channels: new Set(), - }; - if (conf !== undefined && conf.streaming === true) { - req.on("close", () => { - const rep = pendingReplies[traceId]; - rep.channels.forEach((c) => { - channels[c].delete(rep.resp); - if (channels[c].size === 0) { - delete channels[c]; - } - }); - }); - res.set("Content-Type", "text/event-stream"); - res.set("Cache-Control", "no-cache"); - res.set("Connection", "keep-alive"); - res.flushHeaders(); - } - const teams = (0, utils_1.directoryNames)(new utils_1.Path(this.pathToRoot), [ - "event-catalogue", - ]).map((x) => x.name); - processFolders(this.pathToRoot, this.pathToRoot, null, teams, this.hooks); - loadLocalEnvvars(this.pathToRoot); - const response = yield this.runWithReply(this.pathToRoot, this.port, res, event, payload, traceId, sessionId, this.hooks, req.headers["content-type"]); + async processEvent(req, res, payload) { + try { + let sessionId = req.cookies.sessionId; + if (!sessionId) { + sessionId = "s" + Math.random(); + res.cookie("sessionId", sessionId); } - catch (e) { - throw e; + res.set("Access-Control-Allow-Origin", "*"); + const event = req.params.event; + this.hooks = new PublicHooks(this.pathToRoot); + const conf = this.hooks.getApiConfig(event); + const traceId = "t" + Math.random(); + pendingReplies[traceId] = { + resp: res, + channels: new Set(), + }; + if (conf !== undefined && conf.streaming === true) { + req.on("close", () => { + const rep = pendingReplies[traceId]; + rep.channels.forEach((c) => { + channels[c].delete(rep.resp); + if (channels[c].size === 0) { + delete channels[c]; + } + }); + }); + res.set("Content-Type", "text/event-stream"); + res.set("Cache-Control", "no-cache"); + res.set("Connection", "keep-alive"); + res.flushHeaders(); } - }); + const teams = directoryNames(new Path(this.pathToRoot), [ + "event-catalogue", + ]).map((x) => x.name); + processFolders(this.pathToRoot, this.pathToRoot, null, teams, this.hooks); + loadLocalEnvvars(this.pathToRoot); + const response = await this.runWithReply(this.pathToRoot, this.port, res, event, payload, traceId, sessionId, this.hooks, req.headers["content-type"]); + } + catch (e) { + throw e; + } } runService(pathToRoot, port, event, payload, traceId, sessionId, hooks, contentType) { - var _a; if (event === "$reply") { const rs = pendingReplies[traceId]; if (rs !== undefined) { @@ -183,7 +168,7 @@ class Run { c.write(`\n`); }); } - const rivers = (_a = hooks.riversFor(event)) === null || _a === void 0 ? void 0 : _a.hooks; + const rivers = hooks.riversFor(event)?.hooks; if (rivers === undefined) return; const messageId = "m" + Math.random(); @@ -199,12 +184,16 @@ class Run { const args = [...rest, `'${service.action}'`, `'${envelope}'`]; const options = { cwd: service.dir, - env: Object.assign(Object.assign(Object.assign({}, process.env), (envvars[service.group] || {})), { RAPIDS: `http://localhost:${port}/trace/${sessionId}/${traceId}` }), + env: { + ...process.env, + ...(envvars[service.group] || {}), + RAPIDS: `http://localhost:${port}/trace/${sessionId}/${traceId}`, + }, shell: "sh", }; if (process.env["DEBUG"]) console.log(cmd, args); - const ls = (0, child_process_1.spawn)(cmd, args, options); + const ls = spawn(cmd, args, options); ls.stdin.write(payload); ls.stdin.end(); ls.stdout.on("data", (data) => { @@ -228,30 +217,27 @@ class Run { // }); }); } - runWithReply(pathToRoot, port, resp, event, payload, traceId, sessionId, hooks, contentType) { - return __awaiter(this, void 0, void 0, function* () { - try { - const rivers = hooks.riversFor(event); - if (rivers === undefined) - return reply(resp, HTTP.CLIENT_ERROR.NO_HOOKS, "text/plain"); - const conf = hooks.getApiConfig(event); - this.runService(pathToRoot, port, event, payload, traceId, sessionId, hooks, contentType); - if (conf === undefined || conf.streaming !== true) { - yield sleep((conf === null || conf === void 0 ? void 0 : conf.waitFor) || MAX_WAIT); - const pending = pendingReplies[traceId]; - if (pending !== undefined) { - delete pendingReplies[traceId]; - reply(resp, HTTP.SUCCESS.QUEUE_JOB, "text/plain"); - } + async runWithReply(pathToRoot, port, resp, event, payload, traceId, sessionId, hooks, contentType) { + try { + const rivers = hooks.riversFor(event); + if (rivers === undefined) + return reply(resp, HTTP.CLIENT_ERROR.NO_HOOKS, "text/plain"); + const conf = hooks.getApiConfig(event); + this.runService(pathToRoot, port, event, payload, traceId, sessionId, hooks, contentType); + if (conf === undefined || conf.streaming !== true) { + await sleep(conf?.waitFor || MAX_WAIT); + const pending = pendingReplies[traceId]; + if (pending !== undefined) { + delete pendingReplies[traceId]; + reply(resp, HTTP.SUCCESS.QUEUE_JOB, "text/plain"); } } - catch (e) { - throw e; - } - }); + } + catch (e) { + throw e; + } } } -exports.Run = Run; const MAX_WAIT = 5000; const Reset = "\x1b[0m"; const FgRed = "\x1b[31m"; @@ -259,18 +245,18 @@ let envvars = {}; let pendingReplies = {}; let channels = {}; class PublicHooks { + publicEvents; + hooks = {}; constructor(pathToRoot) { - this.hooks = {}; - this.publicEvents = JSON.parse("" + fs_1.default.readFileSync(`${pathToRoot}/event-catalogue/api.json`)); + this.publicEvents = JSON.parse("" + fs.readFileSync(`${pathToRoot}/event-catalogue/api.json`)); } getApiConfig(event) { return this.publicEvents[event]; } register(event, river, hook) { - var _a; const evt = this.hooks[event] || (this.hooks[event] = { - waitFor: (_a = this.publicEvents[event]) === null || _a === void 0 ? void 0 : _a.waitFor, + waitFor: this.publicEvents[event]?.waitFor, hooks: {}, }); const rvr = evt.hooks[river] || (evt.hooks[river] = []); @@ -282,20 +268,20 @@ class PublicHooks { } function isDirectory(folder) { try { - return fs_1.default.lstatSync(folder).isDirectory(); + return fs.lstatSync(folder).isDirectory(); } catch (e) { return false; } } function processFolder(pathToRoot, group, folder, hooks) { - if (fs_1.default.existsSync(`${folder}/merrymake.json`)) { + if (fs.existsSync(`${folder}/merrymake.json`)) { let projectType; let cmd; const dir = folder.replace(/\/\//g, "/"); try { - projectType = (0, detect_project_type_1.detectProjectType)(folder); - cmd = detect_project_type_1.RUN_COMMAND[projectType](folder); + projectType = detectProjectType(folder); + cmd = RUN_COMMAND[projectType](folder); } catch (e) { timedOutput(FgRed + @@ -303,7 +289,7 @@ function processFolder(pathToRoot, group, folder, hooks) { Reset); return; } - const config = JSON.parse("" + fs_1.default.readFileSync(`${folder}/merrymake.json`)); + const config = JSON.parse("" + fs.readFileSync(`${folder}/merrymake.json`)); Object.keys(config.hooks).forEach((k) => { const [river, event] = k.split("/"); const hook = config.hooks[k]; @@ -325,7 +311,7 @@ function processFolder(pathToRoot, group, folder, hooks) { }); } else if (isDirectory(folder)) { - processFolders(pathToRoot, folder, group, fs_1.default.readdirSync(folder), hooks); + processFolders(pathToRoot, folder, group, fs.readdirSync(folder), hooks); } } function processFolders(pathToRoot, prefix, group, folders, hooks) { @@ -334,12 +320,12 @@ function processFolders(pathToRoot, prefix, group, folders, hooks) { .forEach((folder) => processFolder(pathToRoot, group || folder, prefix + folder + "/", hooks)); } function loadLocalEnvvars(pathToRoot) { - fs_1.default.readdirSync(pathToRoot) + fs.readdirSync(pathToRoot) .filter((x) => !x.startsWith("(deleted) ") && !x.endsWith(".DS_Store")) .forEach((group) => { - if (fs_1.default.existsSync(pathToRoot + "/" + group + "/env.kv")) { + if (fs.existsSync(pathToRoot + "/" + group + "/env.kv")) { envvars[group] = {}; - fs_1.default.readFileSync(pathToRoot + "/" + group + "/env.kv") + fs.readFileSync(pathToRoot + "/" + group + "/env.kv") .toString() .split(/\r?\n/) .forEach((x) => { @@ -355,8 +341,8 @@ let spacerTimer; function timedOutput(str) { if (spacerTimer !== undefined) clearTimeout(spacerTimer); - (0, utils_1.output2)(str); - spacerTimer = setTimeout(() => (0, utils_1.output2)(""), 10000); + outputGit(str); + spacerTimer = setTimeout(() => outputGit(""), 10000); } var HTTP; (function (HTTP) { diff --git a/simulator.ts b/simulator.ts index 3db7b81..f37c169 100644 --- a/simulator.ts +++ b/simulator.ts @@ -1,16 +1,15 @@ -import { output2, fetchOrg, directoryNames, Path } from "./utils"; -import express, { Request, RequestHandler } from "express"; -import { Response } from "express"; -import fs from "fs"; -import { spawn, ExecOptions } from "child_process"; import { detectProjectType, ProjectType, RUN_COMMAND, } from "@merrymake/detect-project-type"; -import http from "http"; -import { YELLOW, NORMAL_COLOR } from "./prompt"; +import { ExecOptions, spawn } from "child_process"; import cookieParser from "cookie-parser"; +import express, { Request, RequestHandler, Response } from "express"; +import fs from "fs"; +import http from "http"; +import { NORMAL_COLOR, YELLOW } from "./prompt.js"; +import { directoryNames, fetchOrg, outputGit, Path } from "./utils.js"; const MILLISECONDS = 1; const SECONDS = 1000 * MILLISECONDS; @@ -90,44 +89,44 @@ export class Run { }); app.get("/rapids", (req, res) => { - res.send("Running..."); + res.send("Ready."); }); server.listen(this.port, () => { - output2(""); - output2( + outputGit(""); + outputGit( `88. .88 88 ` ); - output2( + outputGit( `888. .888 88 ` ); - output2( + outputGit( `88Y8. .8P88 88 ` ); - output2( + outputGit( `88 Y8o8P 88 .88. 88.d8 88.d8 Yb dP 8888bd88b .88.8 88 .8P .88. ` ); - output2( + outputGit( `88 Y8P 88 d" "b 88" 88" Yb dP 88 '88 '8b d" "8 88 .8P d" "b` ); - output2( + outputGit( `88 " 88 888888 88 88 Yb dP 88 88 88 8 8 88d8P 888888` ); - output2( + outputGit( `88 88 Y. 88 88 Y8P 88 88 88 Y. .8 88" 8b Y. ` ); - output2( + outputGit( `88 88 "88P 88 88 dP 88 88 88 "88"8 88 "8b "88P ` ); - output2( + outputGit( ` dP ` ); - output2(""); - output2( + outputGit(""); + outputGit( `Running local Rapids on ${YELLOW}http://localhost:${this.port}/rapids${NORMAL_COLOR}` ); - output2(`To exit, press ctrl+c`); - output2(""); + outputGit(`To exit, press ctrl+c`); + outputGit(""); }); }); } @@ -465,8 +464,8 @@ function loadLocalEnvvars(pathToRoot: string) { let spacerTimer: undefined | NodeJS.Timeout; function timedOutput(str: string) { if (spacerTimer !== undefined) clearTimeout(spacerTimer); - output2(str); - spacerTimer = setTimeout(() => output2(""), 10000); + outputGit(str); + spacerTimer = setTimeout(() => outputGit(""), 10000); } module HTTP { diff --git a/templates.js b/templates.js index 3bcc7eb..ce4fbfd 100644 --- a/templates.js +++ b/templates.js @@ -1,7 +1,4 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.templates = exports.languages = void 0; -exports.languages = { +export const languages = { python: { long: "python", short: "p", projectType: "python" }, csharp: { long: "c#", short: "#", projectType: "csharp" }, java: { long: "java", short: "j", projectType: "gradle" }, @@ -10,7 +7,7 @@ exports.languages = { go: { long: "go", short: "g", projectType: "go" }, javascript: { long: "javascript", short: "¤", projectType: "nodejs" }, }; -exports.templates = { +export const templates = { basic: { long: "basic", short: "b", diff --git a/tsconfig.json b/tsconfig.json index 9879c19..6c46c4c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,109 +1,6 @@ { + "extends": "@tsconfig/node-lts/tsconfig.json", "compilerOptions": { - /* Visit https://aka.ms/tsconfig to read more about this file */ - - /* Projects */ - // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - - /* Language and Environment */ - "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, - // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ - // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ - - /* Modules */ - "module": "CommonJS" /* Specify what module code is generated. */, - // "rootDir": "./", /* Specify the root folder within your source files. */ - // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ - // "types": [], /* Specify type package names to be included without being referenced in a source file. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ - // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ - // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ - // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ - // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ - "resolveJsonModule": true /* Enable importing .json files. */, - // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ - // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ - - /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ - - /* Emit */ - // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - // "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - // "outDir": "./", /* Specify an output folder for all emitted files. */ - // "removeComments": true, /* Disable emitting comments. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, - - /* Type Checking */ - "strict": true /* Enable all strict type-checking options. */, - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ - // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ - // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ + "resolveJsonModule": true } } diff --git a/types.js b/types.js index d5f30b8..794432d 100644 --- a/types.js +++ b/types.js @@ -1,11 +1,6 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Path = exports.PathToRepository = exports.PathToServiceGroup = exports.PathToOrganization = exports.AccessId = exports.RepositoryId = exports.ServiceGroupId = exports.OrganizationId = void 0; -const path_1 = __importDefault(require("path")); -class OrganizationId { +import path from "path"; +export class OrganizationId { + organizationId; constructor(organizationId) { this.organizationId = organizationId; } @@ -13,8 +8,8 @@ class OrganizationId { return this.organizationId; } } -exports.OrganizationId = OrganizationId; -class ServiceGroupId { +export class ServiceGroupId { + serviceGroupId; constructor(serviceGroupId) { this.serviceGroupId = serviceGroupId; } @@ -22,8 +17,8 @@ class ServiceGroupId { return this.serviceGroupId; } } -exports.ServiceGroupId = ServiceGroupId; -class RepositoryId { +export class RepositoryId { + repositoryId; constructor(repositoryId) { this.repositoryId = repositoryId; } @@ -31,8 +26,8 @@ class RepositoryId { return this.repositoryId; } } -exports.RepositoryId = RepositoryId; -class AccessId { +export class AccessId { + accessId; constructor(accessId) { this.accessId = accessId; } @@ -40,25 +35,25 @@ class AccessId { return this.accessId; } } -exports.AccessId = AccessId; -class PathToOrganization { +export class PathToOrganization { + pathToOrganization; constructor(pathToOrganization) { this.pathToOrganization = pathToOrganization; } with(folder) { - return new PathToServiceGroup(path_1.default.join(this.pathToOrganization, folder)); + return new PathToServiceGroup(path.join(this.pathToOrganization, folder)); } toString() { return this.pathToOrganization; } } -exports.PathToOrganization = PathToOrganization; -class PathToServiceGroup { +export class PathToServiceGroup { + pathToServiceGroup; constructor(pathToServiceGroup) { this.pathToServiceGroup = pathToServiceGroup; } with(folder) { - return new PathToRepository(path_1.default.join(this.pathToServiceGroup, folder)); + return new PathToRepository(path.join(this.pathToServiceGroup, folder)); } last() { return this.pathToServiceGroup.substring(this.pathToServiceGroup.lastIndexOf("/")); @@ -67,28 +62,27 @@ class PathToServiceGroup { return this.pathToServiceGroup; } } -exports.PathToServiceGroup = PathToServiceGroup; -class PathToRepository { +export class PathToRepository { + pathToRepository; constructor(pathToRepository) { this.pathToRepository = pathToRepository; } with(folder) { - return new Path(path_1.default.join(this.pathToRepository, folder)); + return new Path(path.join(this.pathToRepository, folder)); } toString() { return this.pathToRepository; } } -exports.PathToRepository = PathToRepository; -class Path { +export class Path { + path; constructor(path) { this.path = path; } with(folder) { - return new Path(path_1.default.join(this.path, folder)); + return new Path(path.join(this.path, folder)); } toString() { return this.path; } } -exports.Path = Path; diff --git a/types.ts b/types.ts index 65be073..7a5a2f6 100644 --- a/types.ts +++ b/types.ts @@ -78,6 +78,8 @@ export interface ServiceGroup { pathTo: PathToServiceGroup; } export interface Repository { - id: RepositoryId; pathTo: PathToRepository; } +export interface RepositoryWithId extends Repository { + id: RepositoryId; +} diff --git a/utils.js b/utils.js index 0d6648a..8593f5a 100644 --- a/utils.js +++ b/utils.js @@ -1,84 +1,32 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; +import { exec, spawn } from "child_process"; +import fs from "fs"; +import http from "http"; +import https from "https"; +import { readdirSync } from "node:fs"; +import os from "os"; +import path from "path"; +import { SSH_HOST } from "./config.js"; +// IN THE FUTURE: import conf from "./package.json" with {type:"json"}; +import { BLUE, GRAY, GREEN, NORMAL_COLOR, RED, REMOVE_INVISIBLE, YELLOW, exit, output, spinner_start, spinner_stop, timer_start, timer_stop, } from "./prompt.js"; +import { createRequire } from "node:module"; +import { stdout } from "process"; +const require = createRequire(import.meta.url); +export const package_json = require("./package.json"); +export const lowercase = "abcdefghijklmnopqrstuvwxyz"; +export const uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +export const digits = "0123456789"; +export const underscore = "_"; +export const dash = "-"; +export const all = lowercase + uppercase + digits + underscore + dash; +export function generateString(length, alphabet) { + let result = ""; + for (let i = 0; i < length; i++) { + result += alphabet.charAt(Math.floor(Math.random() * alphabet.length)); } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Path = void 0; -exports.getFiles = getFiles; -exports.setDryrun = setDryrun; -exports.addToExecuteQueue = addToExecuteQueue; -exports.addExitMessage = addExitMessage; -exports.abort = abort; -exports.finish = finish; -exports.TODO = TODO; -exports.getCache = getCache; -exports.saveCache = saveCache; -exports.fetchOrgRaw = fetchOrgRaw; -exports.fetchOrg = fetchOrg; -exports.output2 = output2; -exports.execPromise = execPromise; -exports.checkVersion = checkVersion; -exports.typedKeys = typedKeys; -exports.execStreamPromise = execStreamPromise; -exports.spawnPromise = spawnPromise; -exports.sshReq = sshReq; -exports.partition = partition; -exports.urlReq = urlReq; -exports.directoryNames = directoryNames; -exports.toFolderName = toFolderName; -const child_process_1 = require("child_process"); -const fs_1 = __importDefault(require("fs")); -const http_1 = __importDefault(require("http")); -const https_1 = __importDefault(require("https")); -const node_fs_1 = require("node:fs"); -const os_1 = __importDefault(require("os")); -const path_1 = __importDefault(require("path")); -const config_1 = require("./config"); -const conf = __importStar(require("./package.json")); -const prompt_1 = require("./prompt"); -class Path { + return result; +} +export class Path { + offset; constructor(offset = ".") { this.offset = offset; let end = this.offset.length; @@ -89,7 +37,7 @@ class Path { this.offset = "."; } with(next) { - return new Path(path_1.default.join(this.offset, next)); + return new Path(path.join(this.offset, next)); } withoutLastUp() { return new Path(this.offset.substring(0, this.offset.lastIndexOf(".."))); @@ -98,101 +46,140 @@ class Path { return this.offset; } } -exports.Path = Path; -function getFiles(path) { +export function getFiles(path) { return getFiles_internal(path, ""); } function getFiles_internal(path, prefix) { - if (!fs_1.default.existsSync(path.toString())) + if (!fs.existsSync(path.toString())) return []; - return (0, node_fs_1.readdirSync)(path.toString(), { withFileTypes: true }).flatMap((x) => x.isDirectory() + return readdirSync(path.toString(), { withFileTypes: true }).flatMap((x) => x.isDirectory() ? getFiles_internal(path.with(x.name), prefix + x.name + "/") : [prefix + x.name]); } const toExecute = []; let dryrun = false; -function setDryrun() { - output2(`${prompt_1.BLUE}Dryrun mode, changes will not be performed.${prompt_1.NORMAL_COLOR}`); +export function setDryrun() { + outputGit(`${BLUE}Dryrun mode, changes will not be performed.${NORMAL_COLOR}`); dryrun = true; } -function addToExecuteQueue(f) { +export function addToExecuteQueue(f) { if (!dryrun) toExecute.push(f); } let printOnExit = []; -function addExitMessage(str) { +export function addExitMessage(str) { printOnExit.push(str); } function printExitMessages() { - printOnExit.forEach((x) => (0, prompt_1.output)(x + "\n")); + printOnExit.forEach((x) => output(x + "\n")); } -function abort() { - (0, prompt_1.exit)(); +export function abort() { + exit(); printExitMessages(); process.exit(0); } -function finish() { - return __awaiter(this, void 0, void 0, function* () { - try { - (0, prompt_1.exit)(); - for (let i = 0; i < toExecute.length; i++) { - yield toExecute[i](); - } - printExitMessages(); - process.exit(0); - } - catch (e) { - console.log("finish"); - throw e; +export async function finish() { + try { + exit(); + for (let i = 0; i < toExecute.length; i++) { + await toExecute[i](); } - }); + printExitMessages(); + process.exit(0); + } + catch (e) { + throw e; + } } -function TODO() { +export function TODO() { console.log("TODO"); - (0, prompt_1.exit)(); + exit(); process.exit(0); } -function getCache() { - if (!fs_1.default.existsSync(`${historyFolder}cache`)) { +export function getCache() { + if (!fs.existsSync(`${historyFolder}cache`)) { return { registered: false, hasOrgs: false }; } - return JSON.parse(fs_1.default.readFileSync(`${historyFolder}cache`).toString()); + return JSON.parse(fs.readFileSync(`${historyFolder}cache`).toString()); } -function saveCache(cache) { - fs_1.default.writeFileSync(`${historyFolder}cache`, JSON.stringify(cache)); +export function saveCache(cache) { + fs.writeFileSync(`${historyFolder}cache`, JSON.stringify(cache)); } -function fetchOrgRaw() { - if (fs_1.default.existsSync(path_1.default.join(".merrymake", "conf.json"))) { - const org = JSON.parse("" + fs_1.default.readFileSync(path_1.default.join(".merrymake", "conf.json"))); - return { org, serviceGroup: null, pathToRoot: "." + path_1.default.sep }; +export function fetchOrgRaw() { + if (fs.existsSync(path.join(".merrymake", "conf.json"))) { + const org = JSON.parse("" + fs.readFileSync(path.join(".merrymake", "conf.json"))); + return { org, serviceGroup: null, pathToRoot: "." + path.sep }; } const cwd = process.cwd().split(/\/|\\/); let out = ""; - let folder = path_1.default.sep; + let folder = path.sep; let serviceGroup = null; for (let i = cwd.length - 1; i >= 0; i--) { - if (fs_1.default.existsSync(out + path_1.default.join("..", ".merrymake", "conf.json"))) { + if (fs.existsSync(out + path.join("..", ".merrymake", "conf.json"))) { serviceGroup = cwd[i]; - const org = (JSON.parse("" + fs_1.default.readFileSync(path_1.default.join(`${out}..`, `.merrymake`, `conf.json`)))); - return { org, serviceGroup, pathToRoot: out + ".." + path_1.default.sep }; + const org = (JSON.parse("" + fs.readFileSync(path.join(`${out}..`, `.merrymake`, `conf.json`)))); + return { org, serviceGroup, pathToRoot: out + ".." + path.sep }; } - folder = path_1.default.sep + cwd[i] + folder; - out += ".." + path_1.default.sep; + folder = path.sep + cwd[i] + folder; + out += ".." + path.sep; } return { org: null, serviceGroup: null, pathToRoot: null }; } -function fetchOrg() { +export function fetchOrg() { const res = fetchOrgRaw(); if (res.org === null) throw "Not inside a Merrymake organization"; return res; } -function output2(str) { - console.log((str || "") - .trimEnd() +export function printWithPrefix(str, prefix = "") { + const prefixLength = prefix.replace(REMOVE_INVISIBLE, "").length; + console.log(prefix + + str + .trimEnd() + .split("\n") + .flatMap((x) => x + .match(new RegExp(`.{1,${stdout.getWindowSize()[0] - prefixLength}}( |$)|.{1,${stdout.getWindowSize()[0] - prefixLength}}`, "g")) + .map((x) => x.trimEnd())) + .join(`\n${prefix}`)); +} +export function outputGit(str) { + const st = (str || "").trimEnd(); + if (st.endsWith("elapsed")) { + return; + } + else { + const wasRunning = timer_stop(); + if (wasRunning) + process.stdout.write(`\n`); + } + console.log(st .split("\n") - .map((x) => x.trimEnd()) + .map((x) => { + const lineParts = x.trimEnd().split("remote: "); + const line = lineParts[lineParts.length - 1]; + const color = line.match(/fail|error|fatal/i) !== null + ? RED + : line.match(/warn/i) !== null + ? YELLOW + : line.match(/succe/i) !== null + ? GREEN + : NORMAL_COLOR; + const commands = line.split("'mm"); + for (let i = 1; i < commands.length; i++) { + const ind = commands[i].indexOf("'"); + const cmd = commands[i].substring(0, ind); + const rest = commands[i].substring(ind); + commands[i] = `'${YELLOW}mm ${cmd}${color}${rest}`; + } + lineParts[lineParts.length - 1] = + color + commands.join("") + NORMAL_COLOR; + return lineParts.join(`${GRAY}remote: `); + }) .join("\n")); + if (st.endsWith("(this may take a few minutes)...")) { + process.stdout.write(`${GRAY}remote: ${NORMAL_COLOR} `); + timer_start("s elapsed"); + } } function versionIsOlder(old, new_) { const os = old.split("."); @@ -209,10 +196,10 @@ function versionIsOlder(old, new_) { return true; return false; } -function execPromise(cmd, cwd) { +export function execPromise(cmd, cwd) { return new Promise((resolve, reject) => { - const a = (0, child_process_1.exec)(cmd, { cwd }, (error, stdout, stderr) => { - const err = (error === null || error === void 0 ? void 0 : error.message) || stderr; + const a = exec(cmd, { cwd }, (error, stdout, stderr) => { + const err = error?.message || stderr; if (err) { reject(stderr || stdout); } @@ -222,37 +209,35 @@ function execPromise(cmd, cwd) { }); }); } -const historyFolder = os_1.default.homedir() + "/.merrymake/"; +const historyFolder = os.homedir() + "/.merrymake/"; const historyFile = "history"; const updateFile = "last_update_check"; -function checkVersion() { - return __awaiter(this, void 0, void 0, function* () { - if (!fs_1.default.existsSync(historyFolder)) - fs_1.default.mkdirSync(historyFolder); - const lastCheck = fs_1.default.existsSync(historyFolder + updateFile) - ? +fs_1.default.readFileSync(historyFolder + updateFile).toString() - : 0; - if (Date.now() - lastCheck > 4 * 60 * 60 * 1000) { - try { - const call = yield execPromise("npm show @merrymake/cli dist-tags --json"); - const version = JSON.parse(call); - if (versionIsOlder(conf.version, version.latest)) { - addExitMessage(` +export async function checkVersion() { + if (!fs.existsSync(historyFolder)) + fs.mkdirSync(historyFolder); + const lastCheck = fs.existsSync(historyFolder + updateFile) + ? +fs.readFileSync(historyFolder + updateFile).toString() + : 0; + if (Date.now() - lastCheck > 4 * 60 * 60 * 1000) { + try { + const call = await execPromise("npm show @merrymake/cli dist-tags --json"); + const version = JSON.parse(call); + if (versionIsOlder(package_json.version, version.latest)) { + addExitMessage(` New version of merrymake-cli available, ${process.env["UPDATE_MESSAGE"]}`); - } } - catch (e) { } - fs_1.default.writeFileSync(historyFolder + updateFile, "" + Date.now()); } - }); + catch (e) { } + fs.writeFileSync(historyFolder + updateFile, "" + Date.now()); + } } -function typedKeys(o) { +export function typedKeys(o) { return Object.keys(o); } -function execStreamPromise(full, onData, cwd) { +export function execStreamPromise(full, onData, cwd) { return new Promise((resolve, reject) => { const [cmd, ...args] = full.split(" "); - const p = (0, child_process_1.spawn)(cmd, args, { cwd, shell: "sh" }); + const p = spawn(cmd, args, { cwd, shell: "sh" }); p.stdout.on("data", (data) => { onData(data.toString()); }); @@ -261,25 +246,25 @@ function execStreamPromise(full, onData, cwd) { }); p.on("exit", (code) => { if (code !== 0) - reject(); + reject("subprocess failed"); else resolve(); }); }); } -function spawnPromise(str) { +export function spawnPromise(str) { return new Promise((resolve, reject) => { const [cmd, ...args] = str.split(" "); const options = { cwd: ".", shell: "sh", }; - const ls = (0, child_process_1.spawn)(cmd, args, options); + const ls = spawn(cmd, args, options); ls.stdout.on("data", (data) => { - output2(data.toString()); + outputGit(data.toString()); }); ls.stderr.on("data", (data) => { - output2(data.toString()); + outputGit(data.toString()); }); ls.on("close", (code) => { if (code === 0) @@ -290,30 +275,28 @@ function spawnPromise(str) { }); } function sshReqInternal(cmd) { - return execPromise(`ssh -o ConnectTimeout=10 mist@${config_1.SSH_HOST} "${cmd}"`); + return execPromise(`ssh -o ConnectTimeout=10 mist@${SSH_HOST} "${cmd}"`); } -function sshReq(...cmd) { - return __awaiter(this, void 0, void 0, function* () { - try { - (0, prompt_1.spinner_start)(); - const result = yield sshReqInternal(cmd - .map((x) => (x.length === 0 || x.includes(" ") ? `\\"${x}\\"` : x)) - .join(" ")); - (0, prompt_1.spinner_stop)(); - return result; - } - catch (e) { - throw e; - } - }); +export async function sshReq(...cmd) { + try { + spinner_start(); + const result = await sshReqInternal(cmd + .map((x) => (x.length === 0 || x.includes(" ") ? `\\"${x}\\"` : x)) + .join(" ")); + spinner_stop(); + return result; + } + catch (e) { + throw e; + } } -function partition(str, radix) { +export function partition(str, radix) { const index = str.indexOf(radix); if (index < 0) return [str, ""]; return [str.substring(0, index), str.substring(index + radix.length)]; } -function urlReq(url, method = "GET", data, contentType = "application/json") { +export function urlReq(url, method = "GET", data, contentType = "application/json") { return new Promise((resolve, reject) => { const [protocol, fullPath] = url.indexOf("://") >= 0 ? partition(url, "://") : ["http", url]; const [base, path] = partition(fullPath, "/"); @@ -324,7 +307,7 @@ function urlReq(url, method = "GET", data, contentType = "application/json") { "Content-Type": contentType, "Content-Length": data.length, }; - const sender = protocol === "http" ? http_1.default : https_1.default; + const sender = protocol === "http" ? http : https; const req = sender.request({ host, port, @@ -348,13 +331,13 @@ function urlReq(url, method = "GET", data, contentType = "application/json") { req.end(); }); } -function directoryNames(path, exclude) { - if (!fs_1.default.existsSync(path.toString())) +export function directoryNames(path, exclude) { + if (!fs.existsSync(path.toString())) return []; - return fs_1.default + return fs .readdirSync(path.toString(), { withFileTypes: true }) .filter((x) => x.isDirectory() && !exclude.includes(x.name) && !x.name.startsWith(".")); } -function toFolderName(str) { +export function toFolderName(str) { return str.toLowerCase().replace(/[^a-z0-9\-_]/g, "-"); } diff --git a/utils.ts b/utils.ts index b8a2871..ca843f1 100644 --- a/utils.ts +++ b/utils.ts @@ -5,18 +5,43 @@ import https from "https"; import { readdirSync } from "node:fs"; import os from "os"; import path from "path"; -import { SSH_HOST } from "./config"; -import * as conf from "./package.json"; +import { SSH_HOST } from "./config.js"; +// IN THE FUTURE: import conf from "./package.json" with {type:"json"}; import { BLUE, + GRAY, + GREEN, NORMAL_COLOR, + RED, + REMOVE_INVISIBLE, + YELLOW, exit, output, spinner_start, spinner_stop, -} from "./prompt"; -import { PathTo } from "./types"; + timer_start, + timer_stop, +} from "./prompt.js"; +import { PathTo } from "./types.js"; +import { createRequire } from "node:module"; +import { stdout } from "process"; +const require = createRequire(import.meta.url); +export const package_json = require("./package.json"); + +export const lowercase = "abcdefghijklmnopqrstuvwxyz"; +export const uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +export const digits = "0123456789"; +export const underscore = "_"; +export const dash = "-"; +export const all = lowercase + uppercase + digits + underscore + dash; +export function generateString(length: number, alphabet: string) { + let result = ""; + for (let i = 0; i < length; i++) { + result += alphabet.charAt(Math.floor(Math.random() * alphabet.length)); + } + return result; +} export class Path { constructor(private offset = ".") { let end = this.offset.length; @@ -52,7 +77,9 @@ const toExecute: (() => Promise)[] = []; let dryrun = false; export function setDryrun() { - output2(`${BLUE}Dryrun mode, changes will not be performed.${NORMAL_COLOR}`); + outputGit( + `${BLUE}Dryrun mode, changes will not be performed.${NORMAL_COLOR}` + ); dryrun = true; } export function addToExecuteQueue(f: () => Promise) { @@ -80,7 +107,6 @@ export async function finish(): Promise { printExitMessages(); process.exit(0); } catch (e) { - console.log("finish"); throw e; } } @@ -140,14 +166,69 @@ export function fetchOrg() { if (res.org === null) throw "Not inside a Merrymake organization"; return res; } -export function output2(str: string) { + +export function printWithPrefix(str: string, prefix: string = "") { + const prefixLength = prefix.replace(REMOVE_INVISIBLE, "").length; console.log( - (str || "") - .trimEnd() + prefix + + str + .trimEnd() + .split("\n") + .flatMap((x) => + x + .match( + new RegExp( + `.{1,${stdout.getWindowSize()[0] - prefixLength}}( |$)|.{1,${ + stdout.getWindowSize()[0] - prefixLength + }}`, + "g" + ) + )! + .map((x) => x.trimEnd()) + ) + .join(`\n${prefix}`) + ); +} + +export function outputGit(str: string) { + const st = (str || "").trimEnd(); + if (st.endsWith("elapsed")) { + return; + } else { + const wasRunning = timer_stop(); + if (wasRunning) process.stdout.write(`\n`); + } + console.log( + st .split("\n") - .map((x) => x.trimEnd()) + .map((x) => { + const lineParts = x.trimEnd().split("remote: "); + const line = lineParts[lineParts.length - 1]; + const color = + line.match(/fail|error|fatal/i) !== null + ? RED + : line.match(/warn/i) !== null + ? YELLOW + : line.match(/succe/i) !== null + ? GREEN + : NORMAL_COLOR; + const commands = line.split("'mm"); + for (let i = 1; i < commands.length; i++) { + const ind = commands[i].indexOf("'"); + const cmd = commands[i].substring(0, ind); + const rest = commands[i].substring(ind); + commands[i] = `'${YELLOW}mm ${cmd}${color}${rest}`; + } + lineParts[lineParts.length - 1] = + color + commands.join("") + NORMAL_COLOR; + return lineParts.join(`${GRAY}remote: `); + }) .join("\n") ); + if (st.endsWith("(this may take a few minutes)...")) { + process.stdout.write(`${GRAY}remote: ${NORMAL_COLOR} `); + timer_start("s elapsed"); + } } function versionIsOlder(old: string, new_: string) { @@ -188,7 +269,7 @@ export async function checkVersion() { "npm show @merrymake/cli dist-tags --json" ); const version: { latest: string } = JSON.parse(call); - if (versionIsOlder(conf.version, version.latest)) { + if (versionIsOlder(package_json.version, version.latest)) { addExitMessage(` New version of merrymake-cli available, ${process.env["UPDATE_MESSAGE"]}`); } @@ -216,7 +297,7 @@ export function execStreamPromise( console.log(data.toString()); }); p.on("exit", (code) => { - if (code !== 0) reject(); + if (code !== 0) reject("subprocess failed"); else resolve(); }); }); @@ -231,10 +312,10 @@ export function spawnPromise(str: string) { }; const ls = spawn(cmd, args, options); ls.stdout.on("data", (data: Buffer | string) => { - output2(data.toString()); + outputGit(data.toString()); }); ls.stderr.on("data", (data: Buffer | string) => { - output2(data.toString()); + outputGit(data.toString()); }); ls.on("close", (code) => { if (code === 0) resolve(); diff --git a/words.js b/words.js index 2eea27a..11cdd96 100644 --- a/words.js +++ b/words.js @@ -1,7 +1,4 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ADJECTIVE = exports.NOUN = void 0; -exports.NOUN = [ +export const NOUN = [ "advantage", "advertisement", "advice", @@ -103,7 +100,7 @@ exports.NOUN = [ "transport", "turnover", ]; -exports.ADJECTIVE = [ +export const ADJECTIVE = [ "absent-minded", "abstruse", "absurd",