From 715d8b5c5648c3e7bd85256abb105b199dbe9b69 Mon Sep 17 00:00:00 2001 From: krisbitney Date: Tue, 18 Oct 2022 12:51:25 +0500 Subject: [PATCH 1/4] added deployWrapper, awaitPing, initInfra, and stopInfra methods to test-env interface. Added strategy option to buildWrapper --- packages/js/test-env/src/index.ts | 112 +++++++++++++++++++++++++++++- 1 file changed, 111 insertions(+), 1 deletion(-) diff --git a/packages/js/test-env/src/index.ts b/packages/js/test-env/src/index.ts index 1ca2f6a320..38a63685a2 100644 --- a/packages/js/test-env/src/index.ts +++ b/packages/js/test-env/src/index.ts @@ -143,6 +143,83 @@ export const stopTestEnvironment = async (cli?: string): Promise => { return Promise.resolve(); }; +async function awaitPing(url: string, timeout: number, maxTimeout: number) { + let time = 0; + + while (time < maxTimeout) { + const request = axios.get(url, { timeout }); + const success = await request + .then(() => true) + .catch((e) => e.code !== "ECONNRESET"); + + if (success) { + return true; + } + + await new Promise(function (resolve) { + setTimeout(() => resolve(), timeout); + }); + + time += timeout; + } + + return false; +} + +/** + * Initializes infra + * + * @param awaitResponseUris: an array of URI's from which to await responses + * @param manifestAbsPath: the absolute path of the infra manifest to use + * */ +export async function initInfra( + awaitResponseUris?: string[], + manifestAbsPath?: string +): Promise { + const pathArgs = manifestAbsPath ? ["--manifest-file", manifestAbsPath] : []; + + const { exitCode, stderr, stdout } = await runCLI({ + args: ["infra", "up", "--verbose", ...pathArgs], + }); + + if (exitCode) { + throw Error( + `initInfra failed to start test environment.\nExit Code: ${exitCode}\nStdErr: ${stderr}\nStdOut: ${stdout}` + ); + } + + for (const uri of awaitResponseUris ?? []) { + const success = await awaitPing(uri, 2000, 20000); + + if (!success) { + throw Error(`test-env: resource located at ${uri} failed to start`); + } + } + + return Promise.resolve(); +} + +/** + * Stops infra + * + * @param manifestAbsPath: the absolute path of the infra manifest to use + * */ +export async function stopInfra(manifestAbsPath?: string): Promise { + const pathArgs = manifestAbsPath ? ["--manifest-file", manifestAbsPath] : []; + + const { exitCode, stderr, stdout } = await runCLI({ + args: ["infra", "down", "--verbose", ...pathArgs], + }); + + if (exitCode) { + throw Error( + `initInfra failed to stop test environment.\nExit Code: ${exitCode}\nStdErr: ${stderr}\nStdOut: ${stdout}` + ); + } + + return Promise.resolve(); +} + export const runCLI = async (options: { args: string[]; cwd?: string; @@ -203,11 +280,15 @@ export const runCLI = async (options: { export async function buildWrapper( wrapperAbsPath: string, - manifestPathOverride?: string + manifestPathOverride?: string, + strategy: "vm" | "image" | "local" = "vm" ): Promise { const manifestPath = manifestPathOverride ? path.join(wrapperAbsPath, manifestPathOverride) : `${wrapperAbsPath}/polywrap.yaml`; + + const strategyArgs = strategy ? ["--strategy", strategy] : []; + const { exitCode: buildExitCode, stdout: buildStdout, @@ -219,6 +300,7 @@ export async function buildWrapper( manifestPath, "--output-dir", `${wrapperAbsPath}/build`, + ...strategyArgs, ], }); @@ -230,6 +312,34 @@ export async function buildWrapper( } } +export async function deployWrapper( + wrapperAbsPath: string, + manifestPathOverride?: string, + outputPath?: string +): Promise { + const manifestPath = manifestPathOverride + ? path.join(wrapperAbsPath, manifestPathOverride) + : `${wrapperAbsPath}/polywrap.yaml`; + + const outputArgs = outputPath ? ["--output-file", outputPath] : []; + + const { + exitCode: buildExitCode, + stdout: buildStdout, + stderr: buildStderr, + } = await runCLI({ + args: ["deploy", "--manifest-file", manifestPath, ...outputArgs], + cwd: wrapperAbsPath, + }); + + if (buildExitCode !== 0) { + console.error(`polywrap exited with code: ${buildExitCode}`); + console.log(`stderr:\n${buildStderr}`); + console.log(`stdout:\n${buildStdout}`); + throw Error("polywrap CLI failed"); + } +} + export async function buildAndDeployWrapper({ wrapperAbsPath, ipfsProvider, From 773d9d9128213d77ab3237f61bd951f4a113575c Mon Sep 17 00:00:00 2001 From: krisbitney Date: Tue, 18 Oct 2022 18:52:56 +0500 Subject: [PATCH 2/4] added cli api (new methods) to test-env-js and did minor refactoring --- packages/js/test-env/src/build-and-deploy.ts | 228 +++++++++ packages/js/test-env/src/cli-api.ts | 267 +++++++++++ packages/js/test-env/src/index.ts | 478 +------------------ packages/js/test-env/src/test-environment.ts | 76 +++ packages/js/test-env/src/util.ts | 63 +++ 5 files changed, 638 insertions(+), 474 deletions(-) create mode 100644 packages/js/test-env/src/build-and-deploy.ts create mode 100644 packages/js/test-env/src/cli-api.ts create mode 100644 packages/js/test-env/src/test-environment.ts create mode 100644 packages/js/test-env/src/util.ts diff --git a/packages/js/test-env/src/build-and-deploy.ts b/packages/js/test-env/src/build-and-deploy.ts new file mode 100644 index 0000000000..0b1524bda3 --- /dev/null +++ b/packages/js/test-env/src/build-and-deploy.ts @@ -0,0 +1,228 @@ +import { generateName } from "./generate-name"; +import { build } from "./cli-api"; +import { ensAddresses, runCLI } from "./index"; + +import fs from "fs"; +import path from "path"; +import yaml from "yaml"; +import { Uri } from "@polywrap/core-js"; +import { + DeployManifest, + deserializePolywrapManifest, +} from "@polywrap/polywrap-manifest-types-js"; + +export async function buildAndDeployWrapper({ + wrapperAbsPath, + ipfsProvider, + ethereumProvider, + ensName, +}: { + wrapperAbsPath: string; + ipfsProvider: string; + ethereumProvider: string; + ensName?: string; +}): Promise<{ + ensDomain: string; + ipfsCid: string; +}> { + const manifestPath = `${wrapperAbsPath}/polywrap.yaml`; + const tempManifestFilename = `polywrap-temp.yaml`; + const tempDeployManifestFilename = `polywrap.deploy-temp.yaml`; + const tempManifestPath = path.join(wrapperAbsPath, tempManifestFilename); + const tempDeployManifestPath = path.join( + wrapperAbsPath, + tempDeployManifestFilename + ); + + // create a new ENS domain + const wrapperEns = ensName ?? `${generateName()}.eth`; + + await build(wrapperAbsPath); + + // manually configure manifests + const { __type, ...polywrapManifest } = deserializePolywrapManifest( + fs.readFileSync(manifestPath, "utf-8") + ); + + fs.writeFileSync( + tempManifestPath, + yaml.stringify( + { + ...polywrapManifest, + extensions: { + ...polywrapManifest.extensions, + deploy: `./${tempDeployManifestFilename}`, + }, + }, + null, + 2 + ) + ); + + const deployManifest: Omit = { + format: "0.2.0", + jobs: { + buildAndDeployWrapper: { + config: { + provider: ethereumProvider, + ensRegistryAddress: ensAddresses.ensAddress, + ensRegistrarAddress: ensAddresses.registrarAddress, + ensResolverAddress: ensAddresses.resolverAddress, + }, + steps: [ + { + name: "registerName", + package: "ens-recursive-name-register", + uri: `wrap://ens/${wrapperEns}`, + }, + { + name: "ipfsDeploy", + package: "ipfs", + uri: `fs/${wrapperAbsPath}/build`, + config: { + gatewayUri: ipfsProvider, + }, + }, + { + name: "ensPublish", + package: "ens", + uri: "$$ipfsDeploy", + config: { + domainName: wrapperEns, + }, + }, + ], + }, + }, + }; + fs.writeFileSync( + tempDeployManifestPath, + yaml.stringify(deployManifest, null, 2) + ); + + // deploy Wrapper + + const { + exitCode: deployExitCode, + stdout: deployStdout, + stderr: deployStderr, + } = await runCLI({ + args: ["deploy", "--manifest-file", tempManifestPath], + }); + + if (deployExitCode !== 0) { + console.error(`polywrap exited with code: ${deployExitCode}`); + console.log(`stderr:\n${deployStderr}`); + console.log(`stdout:\n${deployStdout}`); + throw Error("polywrap CLI failed"); + } + + // remove manually configured manifests + + fs.unlinkSync(tempManifestPath); + fs.unlinkSync(tempDeployManifestPath); + + // get the IPFS CID of the published package + const extractCID = /(wrap:\/\/ipfs\/[A-Za-z0-9]+)/; + const result = deployStdout.match(extractCID); + + if (!result) { + throw Error( + `polywrap CLI output missing IPFS CID.\nOutput: ${deployStdout}` + ); + } + + const wrapperCid = new Uri(result[1]).path; + + return { + ensDomain: wrapperEns, + ipfsCid: wrapperCid, + }; +} + +export async function buildAndDeployWrapperToHttp({ + wrapperAbsPath, + httpProvider, + name, +}: { + wrapperAbsPath: string; + httpProvider: string; + name?: string; +}): Promise<{ uri: string }> { + const manifestPath = `${wrapperAbsPath}/polywrap.yaml`; + const tempManifestFilename = `polywrap-temp.yaml`; + const tempDeployManifestFilename = `polywrap.deploy-temp.yaml`; + const tempManifestPath = path.join(wrapperAbsPath, tempManifestFilename); + const tempDeployManifestPath = path.join( + wrapperAbsPath, + tempDeployManifestFilename + ); + + const wrapperName = name ?? generateName(); + const postUrl = `${httpProvider}/wrappers/local/${wrapperName}`; + + await build(wrapperAbsPath); + + // manually configure manifests + + const { __type, ...polywrapManifest } = deserializePolywrapManifest( + fs.readFileSync(manifestPath, "utf-8") + ); + + polywrapManifest.extensions = { + ...polywrapManifest.extensions, + deploy: `./${tempDeployManifestFilename}`, + }; + fs.writeFileSync( + tempManifestPath, + yaml.stringify({ ...polywrapManifest }, null, 2) + ); + + const deployManifest: Omit = { + format: "0.2.0", + jobs: { + buildAndDeployWrapperToHttp: { + steps: [ + { + name: "httpDeploy", + package: "http", + uri: `fs/${wrapperAbsPath}/build`, + config: { + postUrl, + }, + }, + ], + }, + }, + }; + fs.writeFileSync( + tempDeployManifestPath, + yaml.stringify(deployManifest, null, 2) + ); + + // deploy Wrapper + + const { + exitCode: deployExitCode, + stdout: deployStdout, + stderr: deployStderr, + } = await runCLI({ + args: ["deploy", "--manifest-file", tempManifestPath], + }); + + if (deployExitCode !== 0) { + console.error(`polywrap exited with code: ${deployExitCode}`); + console.log(`stderr:\n${deployStderr}`); + console.log(`stdout:\n${deployStdout}`); + throw Error("polywrap CLI failed"); + } + + // remove manually configured manifests + + fs.unlinkSync(tempManifestPath); + fs.unlinkSync(tempDeployManifestPath); + + return { + uri: postUrl, + }; +} diff --git a/packages/js/test-env/src/cli-api.ts b/packages/js/test-env/src/cli-api.ts new file mode 100644 index 0000000000..e26b8e62e3 --- /dev/null +++ b/packages/js/test-env/src/cli-api.ts @@ -0,0 +1,267 @@ +import { runCLI } from "./index"; +import { awaitPing } from "./util"; + +import path from "path"; + +type InfraCommandOptions = { + infraManifest?: string; + modules?: string[]; + verbose?: boolean; + quiet?: boolean; +}; + +type CodegenCommandOptions = { + projectManifest?: string; + codegenDir?: string; + publishDir?: string; + clientConfig?: string; + verbose?: boolean; + quiet?: boolean; +}; + +type BuildCommandOptions = { + projectManifest?: string; + outputDir?: string; + clientConfig?: string; + noCodegen?: boolean; + strategy?: "vm" | "image" | "local"; + verbose?: boolean; + quiet?: boolean; +}; + +type DeployCommandOptions = { + projectManifest?: string; + outputFile?: string; + verbose?: boolean; + quiet?: boolean; +}; + +/** + * Initialize test environment declared in infra manifest + * + * @param awaitResponseUris: an array of URI's from which to await responses + * @param options: options for Polywrap CLI 'infra' command + * @param cwd: the current working directory for the CLI call + * */ +export async function infraUp( + awaitResponseUris?: string[], + options?: InfraCommandOptions, + cwd?: string +): Promise { + const modules = options?.modules ? ["--modules", ...options.modules] : []; + const manifest = options?.infraManifest + ? ["--manifest-file", options.infraManifest] + : []; + + const { exitCode, stderr, stdout } = await runCLI({ + args: [ + "infra", + "up", + ...manifest, + ...modules, + `--verbose ${!!options?.verbose}`, + `--quiet ${!!options?.quiet}`, + ], + cwd, + }); + + if (exitCode) { + throw Error( + `initInfra failed to start test environment.\nExit Code: ${exitCode}\nStdErr: ${stderr}\nStdOut: ${stdout}` + ); + } + + for (const uri of awaitResponseUris ?? []) { + const success = await awaitPing(uri, 2000, 20000); + + if (!success) { + throw Error(`test-env: resource located at ${uri} failed to start`); + } + } + + return Promise.resolve(); +} + +/** + * Stop test environment declared in infra manifest + * + * @param options: options for Polywrap CLI 'infra' command + * @param cwd: the current working directory for the CLI call + * */ +export async function infraDown( + options?: InfraCommandOptions, + cwd?: string +): Promise { + const modules = options?.modules ? ["--modules", ...options.modules] : []; + const manifest = options?.infraManifest + ? ["--manifest-file", options.infraManifest] + : []; + + const { exitCode, stderr, stdout } = await runCLI({ + args: [ + "infra", + "down", + ...manifest, + ...modules, + `--verbose ${!!options?.verbose}`, + `--quiet ${!!options?.quiet}`, + ], + cwd, + }); + + if (exitCode) { + throw Error( + `initInfra failed to stop test environment.\nExit Code: ${exitCode}\nStdErr: ${stderr}\nStdOut: ${stdout}` + ); + } + + return Promise.resolve(); +} + +/** + * Generate code for a Polywrap project + * + * @param wrapperAbsPath: absolute path of the wrapper root folder + * @param options: options for Polywrap CLI 'codegen' command + * @param cwd: the current working directory for the CLI call + * */ +export async function codegen( + wrapperAbsPath: string, + options?: CodegenCommandOptions, + cwd: string = wrapperAbsPath +): Promise { + const manifestFile = options?.projectManifest + ? ["--manifest-file", path.join(wrapperAbsPath, options.projectManifest)] + : []; + + const codegenDir = options?.codegenDir + ? ["--codegen-dir", options.codegenDir] + : []; + + const publishDir = options?.publishDir + ? ["--output-dir", options.publishDir] + : []; + + const clientConfig = options?.clientConfig + ? ["--client-config", options.clientConfig] + : []; + + const { + exitCode: buildExitCode, + stdout: buildStdout, + stderr: buildStderr, + } = await runCLI({ + args: [ + "build", + ...manifestFile, + ...codegenDir, + ...publishDir, + ...clientConfig, + `--verbose ${!!options?.verbose}`, + `--quiet ${!!options?.quiet}`, + ], + cwd, + }); + + if (buildExitCode !== 0) { + console.error(`polywrap exited with code: ${buildExitCode}`); + console.log(`stderr:\n${buildStderr}`); + console.log(`stdout:\n${buildStdout}`); + throw Error("polywrap CLI failed"); + } +} + +/** + * Build a wrapper + * + * @param wrapperAbsPath: absolute path of the wrapper root folder + * @param options: options for Polywrap CLI 'build' command + * @param cwd: the current working directory for the CLI call + * */ +export async function build( + wrapperAbsPath: string, + options?: BuildCommandOptions, + cwd: string = wrapperAbsPath +): Promise { + const manifestFile = options?.projectManifest + ? ["--manifest-file", path.join(wrapperAbsPath, options.projectManifest)] + : []; + + const outputDir = options?.outputDir + ? ["--output-dir", options.outputDir] + : []; + + const strategy = options?.strategy ? ["--strategy", options.strategy] : []; + + const clientConfig = options?.clientConfig + ? ["--client-config", options.clientConfig] + : []; + + const { + exitCode: buildExitCode, + stdout: buildStdout, + stderr: buildStderr, + } = await runCLI({ + args: [ + "build", + ...manifestFile, + ...outputDir, + ...strategy, + ...clientConfig, + `--noCodegen ${!!options?.noCodegen}`, + `--verbose ${!!options?.verbose}`, + `--quiet ${!!options?.quiet}`, + cwd, + ], + }); + + if (buildExitCode !== 0) { + console.error(`polywrap exited with code: ${buildExitCode}`); + console.log(`stderr:\n${buildStderr}`); + console.log(`stdout:\n${buildStdout}`); + throw Error("polywrap CLI failed"); + } +} + +/** + * Deploy a wrapper using a deploy manifest + * + * @param wrapperAbsPath: absolute path of the wrapper root folder + * @param options: options for Polywrap CLI 'deploy' command + * @param cwd: the current working directory for the CLI call + * */ +export async function deploy( + wrapperAbsPath: string, + options?: DeployCommandOptions, + cwd: string = wrapperAbsPath +): Promise { + const manifestFile = options?.projectManifest + ? ["--manifest-file", path.join(wrapperAbsPath, options.projectManifest)] + : []; + + const outputArgs = options?.outputFile + ? ["--output-file", options.outputFile] + : []; + + const { + exitCode: buildExitCode, + stdout: buildStdout, + stderr: buildStderr, + } = await runCLI({ + args: [ + "deploy", + ...manifestFile, + ...outputArgs, + `--verbose ${!!options?.verbose}`, + `--quiet ${!!options?.quiet}`, + ], + cwd, + }); + + if (buildExitCode !== 0) { + console.error(`polywrap exited with code: ${buildExitCode}`); + console.log(`stderr:\n${buildStderr}`); + console.log(`stdout:\n${buildStdout}`); + throw Error("polywrap CLI failed"); + } +} diff --git a/packages/js/test-env/src/index.ts b/packages/js/test-env/src/index.ts index 38a63685a2..83d46aec28 100644 --- a/packages/js/test-env/src/index.ts +++ b/packages/js/test-env/src/index.ts @@ -1,16 +1,10 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import { generateName } from "./generate-name"; - import path from "path"; import spawn from "spawn-command"; -import axios from "axios"; import fs from "fs"; -import yaml from "yaml"; -import { Uri } from "@polywrap/core-js"; -import { - DeployManifest, - deserializePolywrapManifest, -} from "@polywrap/polywrap-manifest-types-js"; + +export * from "./test-environment"; +export * from "./build-and-deploy"; +export * from "./cli-api"; export const ensAddresses = { ensAddress: "0xe78A0F7E598Cc8b0Bb87894B0F60dD2a88d6a8Ab", @@ -34,192 +28,6 @@ export const embeddedWrappers = { const monorepoCli = `${__dirname}/../../../cli/bin/polywrap`; const npmCli = `${__dirname}/../../../polywrap/bin/polywrap`; -async function awaitResponse( - url: string, - expectedRes: string, - getPost: "get" | "post", - timeout: number, - maxTimeout: number, - data?: string -) { - let time = 0; - - while (time < maxTimeout) { - const request = getPost === "get" ? axios.get(url) : axios.post(url, data); - const success = await request - .then(function (response) { - const responseData = JSON.stringify(response.data); - return responseData.indexOf(expectedRes) > -1; - }) - .catch(function () { - return false; - }); - - if (success) { - return true; - } - - await new Promise(function (resolve) { - setTimeout(() => resolve(), timeout); - }); - - time += timeout; - } - - return false; -} - -export const initTestEnvironment = async (cli?: string): Promise => { - // Start the test environment - const { exitCode, stderr, stdout } = await runCLI({ - args: ["infra", "up", "--modules=eth-ens-ipfs", "--verbose"], - cli, - }); - - if (exitCode) { - throw Error( - `initTestEnvironment failed to start test environment.\nExit Code: ${exitCode}\nStdErr: ${stderr}\nStdOut: ${stdout}` - ); - } - - // Wait for all endpoints to become available - let success = false; - - // IPFS - success = await awaitResponse( - `http://localhost:5001/api/v0/version`, - '"Version":', - "get", - 2000, - 20000 - ); - - if (!success) { - throw Error("test-env: IPFS failed to start"); - } - - // Ganache - success = await awaitResponse( - `http://localhost:8545`, - '"jsonrpc":', - "post", - 2000, - 20000, - '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":83}' - ); - - if (!success) { - throw Error("test-env: Ganache failed to start"); - } - - // ENS - success = await awaitResponse( - "http://localhost:8545", - '"result":"0x', - "post", - 2000, - 20000, - `{"jsonrpc":"2.0","method":"eth_getCode","params":["${ensAddresses.ensAddress}", "0x2"],"id":1}` - ); - - if (!success) { - throw Error("test-env: ENS failed to deploy"); - } -}; - -export const stopTestEnvironment = async (cli?: string): Promise => { - // Stop the test environment - const { exitCode, stderr } = await runCLI({ - args: ["infra", "down", "--modules=eth-ens-ipfs"], - cli, - }); - - if (exitCode) { - throw Error( - `stopTestEnvironment failed to stop test environment.\nExit Code: ${exitCode}\nStdErr: ${stderr}` - ); - } - - return Promise.resolve(); -}; - -async function awaitPing(url: string, timeout: number, maxTimeout: number) { - let time = 0; - - while (time < maxTimeout) { - const request = axios.get(url, { timeout }); - const success = await request - .then(() => true) - .catch((e) => e.code !== "ECONNRESET"); - - if (success) { - return true; - } - - await new Promise(function (resolve) { - setTimeout(() => resolve(), timeout); - }); - - time += timeout; - } - - return false; -} - -/** - * Initializes infra - * - * @param awaitResponseUris: an array of URI's from which to await responses - * @param manifestAbsPath: the absolute path of the infra manifest to use - * */ -export async function initInfra( - awaitResponseUris?: string[], - manifestAbsPath?: string -): Promise { - const pathArgs = manifestAbsPath ? ["--manifest-file", manifestAbsPath] : []; - - const { exitCode, stderr, stdout } = await runCLI({ - args: ["infra", "up", "--verbose", ...pathArgs], - }); - - if (exitCode) { - throw Error( - `initInfra failed to start test environment.\nExit Code: ${exitCode}\nStdErr: ${stderr}\nStdOut: ${stdout}` - ); - } - - for (const uri of awaitResponseUris ?? []) { - const success = await awaitPing(uri, 2000, 20000); - - if (!success) { - throw Error(`test-env: resource located at ${uri} failed to start`); - } - } - - return Promise.resolve(); -} - -/** - * Stops infra - * - * @param manifestAbsPath: the absolute path of the infra manifest to use - * */ -export async function stopInfra(manifestAbsPath?: string): Promise { - const pathArgs = manifestAbsPath ? ["--manifest-file", manifestAbsPath] : []; - - const { exitCode, stderr, stdout } = await runCLI({ - args: ["infra", "down", "--verbose", ...pathArgs], - }); - - if (exitCode) { - throw Error( - `initInfra failed to stop test environment.\nExit Code: ${exitCode}\nStdErr: ${stderr}\nStdOut: ${stdout}` - ); - } - - return Promise.resolve(); -} - export const runCLI = async (options: { args: string[]; cwd?: string; @@ -277,281 +85,3 @@ export const runCLI = async (options: { stderr, }; }; - -export async function buildWrapper( - wrapperAbsPath: string, - manifestPathOverride?: string, - strategy: "vm" | "image" | "local" = "vm" -): Promise { - const manifestPath = manifestPathOverride - ? path.join(wrapperAbsPath, manifestPathOverride) - : `${wrapperAbsPath}/polywrap.yaml`; - - const strategyArgs = strategy ? ["--strategy", strategy] : []; - - const { - exitCode: buildExitCode, - stdout: buildStdout, - stderr: buildStderr, - } = await runCLI({ - args: [ - "build", - "--manifest-file", - manifestPath, - "--output-dir", - `${wrapperAbsPath}/build`, - ...strategyArgs, - ], - }); - - if (buildExitCode !== 0) { - console.error(`polywrap exited with code: ${buildExitCode}`); - console.log(`stderr:\n${buildStderr}`); - console.log(`stdout:\n${buildStdout}`); - throw Error("polywrap CLI failed"); - } -} - -export async function deployWrapper( - wrapperAbsPath: string, - manifestPathOverride?: string, - outputPath?: string -): Promise { - const manifestPath = manifestPathOverride - ? path.join(wrapperAbsPath, manifestPathOverride) - : `${wrapperAbsPath}/polywrap.yaml`; - - const outputArgs = outputPath ? ["--output-file", outputPath] : []; - - const { - exitCode: buildExitCode, - stdout: buildStdout, - stderr: buildStderr, - } = await runCLI({ - args: ["deploy", "--manifest-file", manifestPath, ...outputArgs], - cwd: wrapperAbsPath, - }); - - if (buildExitCode !== 0) { - console.error(`polywrap exited with code: ${buildExitCode}`); - console.log(`stderr:\n${buildStderr}`); - console.log(`stdout:\n${buildStdout}`); - throw Error("polywrap CLI failed"); - } -} - -export async function buildAndDeployWrapper({ - wrapperAbsPath, - ipfsProvider, - ethereumProvider, - ensName, -}: { - wrapperAbsPath: string; - ipfsProvider: string; - ethereumProvider: string; - ensName?: string; -}): Promise<{ - ensDomain: string; - ipfsCid: string; -}> { - const manifestPath = `${wrapperAbsPath}/polywrap.yaml`; - const tempManifestFilename = `polywrap-temp.yaml`; - const tempDeployManifestFilename = `polywrap.deploy-temp.yaml`; - const tempManifestPath = path.join(wrapperAbsPath, tempManifestFilename); - const tempDeployManifestPath = path.join( - wrapperAbsPath, - tempDeployManifestFilename - ); - - // create a new ENS domain - const wrapperEns = ensName ?? `${generateName()}.eth`; - - await buildWrapper(wrapperAbsPath); - - // manually configure manifests - const { __type, ...polywrapManifest } = deserializePolywrapManifest( - fs.readFileSync(manifestPath, "utf-8") - ); - - fs.writeFileSync( - tempManifestPath, - yaml.stringify( - { - ...polywrapManifest, - extensions: { - ...polywrapManifest.extensions, - deploy: `./${tempDeployManifestFilename}`, - }, - }, - null, - 2 - ) - ); - - const deployManifest: Omit = { - format: "0.2.0", - jobs: { - buildAndDeployWrapper: { - config: { - provider: ethereumProvider, - ensRegistryAddress: ensAddresses.ensAddress, - ensRegistrarAddress: ensAddresses.registrarAddress, - ensResolverAddress: ensAddresses.resolverAddress, - }, - steps: [ - { - name: "registerName", - package: "ens-recursive-name-register", - uri: `wrap://ens/${wrapperEns}`, - }, - { - name: "ipfsDeploy", - package: "ipfs", - uri: `fs/${wrapperAbsPath}/build`, - config: { - gatewayUri: ipfsProvider, - }, - }, - { - name: "ensPublish", - package: "ens", - uri: "$$ipfsDeploy", - config: { - domainName: wrapperEns, - }, - }, - ], - }, - }, - }; - fs.writeFileSync( - tempDeployManifestPath, - yaml.stringify(deployManifest, null, 2) - ); - - // deploy Wrapper - - const { - exitCode: deployExitCode, - stdout: deployStdout, - stderr: deployStderr, - } = await runCLI({ - args: ["deploy", "--manifest-file", tempManifestPath], - }); - - if (deployExitCode !== 0) { - console.error(`polywrap exited with code: ${deployExitCode}`); - console.log(`stderr:\n${deployStderr}`); - console.log(`stdout:\n${deployStdout}`); - throw Error("polywrap CLI failed"); - } - - // remove manually configured manifests - - fs.unlinkSync(tempManifestPath); - fs.unlinkSync(tempDeployManifestPath); - - // get the IPFS CID of the published package - const extractCID = /(wrap:\/\/ipfs\/[A-Za-z0-9]+)/; - const result = deployStdout.match(extractCID); - - if (!result) { - throw Error( - `polywrap CLI output missing IPFS CID.\nOutput: ${deployStdout}` - ); - } - - const wrapperCid = new Uri(result[1]).path; - - return { - ensDomain: wrapperEns, - ipfsCid: wrapperCid, - }; -} - -export async function buildAndDeployWrapperToHttp({ - wrapperAbsPath, - httpProvider, - name, -}: { - wrapperAbsPath: string; - httpProvider: string; - name?: string; -}): Promise<{ uri: string }> { - const manifestPath = `${wrapperAbsPath}/polywrap.yaml`; - const tempManifestFilename = `polywrap-temp.yaml`; - const tempDeployManifestFilename = `polywrap.deploy-temp.yaml`; - const tempManifestPath = path.join(wrapperAbsPath, tempManifestFilename); - const tempDeployManifestPath = path.join( - wrapperAbsPath, - tempDeployManifestFilename - ); - - const wrapperName = name ?? generateName(); - const postUrl = `${httpProvider}/wrappers/local/${wrapperName}`; - - await buildWrapper(wrapperAbsPath); - - // manually configure manifests - - const { __type, ...polywrapManifest } = deserializePolywrapManifest( - fs.readFileSync(manifestPath, "utf-8") - ); - - polywrapManifest.extensions = { - ...polywrapManifest.extensions, - deploy: `./${tempDeployManifestFilename}`, - }; - fs.writeFileSync( - tempManifestPath, - yaml.stringify({ ...polywrapManifest }, null, 2) - ); - - const deployManifest: Omit = { - format: "0.2.0", - jobs: { - buildAndDeployWrapperToHttp: { - steps: [ - { - name: "httpDeploy", - package: "http", - uri: `fs/${wrapperAbsPath}/build`, - config: { - postUrl, - }, - }, - ], - }, - }, - }; - fs.writeFileSync( - tempDeployManifestPath, - yaml.stringify(deployManifest, null, 2) - ); - - // deploy Wrapper - - const { - exitCode: deployExitCode, - stdout: deployStdout, - stderr: deployStderr, - } = await runCLI({ - args: ["deploy", "--manifest-file", tempManifestPath], - }); - - if (deployExitCode !== 0) { - console.error(`polywrap exited with code: ${deployExitCode}`); - console.log(`stderr:\n${deployStderr}`); - console.log(`stdout:\n${deployStdout}`); - throw Error("polywrap CLI failed"); - } - - // remove manually configured manifests - - fs.unlinkSync(tempManifestPath); - fs.unlinkSync(tempDeployManifestPath); - - return { - uri: postUrl, - }; -} diff --git a/packages/js/test-env/src/test-environment.ts b/packages/js/test-env/src/test-environment.ts new file mode 100644 index 0000000000..272ea5a45f --- /dev/null +++ b/packages/js/test-env/src/test-environment.ts @@ -0,0 +1,76 @@ +import { awaitResponse } from "./util"; +import { ensAddresses, runCLI } from "./index"; + +export const initTestEnvironment = async (cli?: string): Promise => { + // Start the test environment + const { exitCode, stderr, stdout } = await runCLI({ + args: ["infra", "up", "--modules=eth-ens-ipfs", "--verbose"], + cli, + }); + + if (exitCode) { + throw Error( + `initTestEnvironment failed to start test environment.\nExit Code: ${exitCode}\nStdErr: ${stderr}\nStdOut: ${stdout}` + ); + } + + // Wait for all endpoints to become available + let success = false; + + // IPFS + success = await awaitResponse( + `http://localhost:5001/api/v0/version`, + '"Version":', + "get", + 2000, + 20000 + ); + + if (!success) { + throw Error("test-env: IPFS failed to start"); + } + + // Ganache + success = await awaitResponse( + `http://localhost:8545`, + '"jsonrpc":', + "post", + 2000, + 20000, + '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":83}' + ); + + if (!success) { + throw Error("test-env: Ganache failed to start"); + } + + // ENS + success = await awaitResponse( + "http://localhost:8545", + '"result":"0x', + "post", + 2000, + 20000, + `{"jsonrpc":"2.0","method":"eth_getCode","params":["${ensAddresses.ensAddress}", "0x2"],"id":1}` + ); + + if (!success) { + throw Error("test-env: ENS failed to deploy"); + } +}; + +export const stopTestEnvironment = async (cli?: string): Promise => { + // Stop the test environment + const { exitCode, stderr } = await runCLI({ + args: ["infra", "down", "--modules=eth-ens-ipfs"], + cli, + }); + + if (exitCode) { + throw Error( + `stopTestEnvironment failed to stop test environment.\nExit Code: ${exitCode}\nStdErr: ${stderr}` + ); + } + + return Promise.resolve(); +}; diff --git a/packages/js/test-env/src/util.ts b/packages/js/test-env/src/util.ts new file mode 100644 index 0000000000..af3904f5b9 --- /dev/null +++ b/packages/js/test-env/src/util.ts @@ -0,0 +1,63 @@ +import axios from "axios"; + +export async function awaitResponse( + url: string, + expectedRes: string, + getPost: "get" | "post", + timeout: number, + maxTimeout: number, + data?: string +): Promise { + let time = 0; + + while (time < maxTimeout) { + const request = getPost === "get" ? axios.get(url) : axios.post(url, data); + const success = await request + .then(function (response) { + const responseData = JSON.stringify(response.data); + return responseData.indexOf(expectedRes) > -1; + }) + .catch(function () { + return false; + }); + + if (success) { + return true; + } + + await new Promise(function (resolve) { + setTimeout(() => resolve(), timeout); + }); + + time += timeout; + } + + return false; +} + +export async function awaitPing( + url: string, + timeout: number, + maxTimeout: number +): Promise { + let time = 0; + + while (time < maxTimeout) { + const request = axios.get(url, { timeout }); + const success = await request + .then(() => true) + .catch((e) => e.code !== "ECONNRESET"); + + if (success) { + return true; + } + + await new Promise(function (resolve) { + setTimeout(() => resolve(), timeout); + }); + + time += timeout; + } + + return false; +} From b4ff47ab721f36bdc7b440dcb8c0baf625bedd9b Mon Sep 17 00:00:00 2001 From: krisbitney Date: Tue, 18 Oct 2022 19:06:49 +0500 Subject: [PATCH 3/4] minor refactoring of test-env-js directories --- packages/js/test-env/src/build-and-deploy.ts | 2 +- packages/js/test-env/src/cli-api.ts | 2 +- packages/js/test-env/src/test-environment.ts | 2 +- packages/js/test-env/src/{util.ts => utils/await.ts} | 0 packages/js/test-env/src/{ => utils}/declarations.d.ts | 0 packages/js/test-env/src/{ => utils}/generate-name.ts | 0 packages/js/test-env/src/utils/index.ts | 2 ++ 7 files changed, 5 insertions(+), 3 deletions(-) rename packages/js/test-env/src/{util.ts => utils/await.ts} (100%) rename packages/js/test-env/src/{ => utils}/declarations.d.ts (100%) rename packages/js/test-env/src/{ => utils}/generate-name.ts (100%) create mode 100644 packages/js/test-env/src/utils/index.ts diff --git a/packages/js/test-env/src/build-and-deploy.ts b/packages/js/test-env/src/build-and-deploy.ts index 0b1524bda3..fbd5c50582 100644 --- a/packages/js/test-env/src/build-and-deploy.ts +++ b/packages/js/test-env/src/build-and-deploy.ts @@ -1,4 +1,4 @@ -import { generateName } from "./generate-name"; +import { generateName } from "./utils"; import { build } from "./cli-api"; import { ensAddresses, runCLI } from "./index"; diff --git a/packages/js/test-env/src/cli-api.ts b/packages/js/test-env/src/cli-api.ts index e26b8e62e3..7f972587ab 100644 --- a/packages/js/test-env/src/cli-api.ts +++ b/packages/js/test-env/src/cli-api.ts @@ -1,5 +1,5 @@ import { runCLI } from "./index"; -import { awaitPing } from "./util"; +import { awaitPing } from "./utils"; import path from "path"; diff --git a/packages/js/test-env/src/test-environment.ts b/packages/js/test-env/src/test-environment.ts index 272ea5a45f..f04f6f340a 100644 --- a/packages/js/test-env/src/test-environment.ts +++ b/packages/js/test-env/src/test-environment.ts @@ -1,4 +1,4 @@ -import { awaitResponse } from "./util"; +import { awaitResponse } from "./utils"; import { ensAddresses, runCLI } from "./index"; export const initTestEnvironment = async (cli?: string): Promise => { diff --git a/packages/js/test-env/src/util.ts b/packages/js/test-env/src/utils/await.ts similarity index 100% rename from packages/js/test-env/src/util.ts rename to packages/js/test-env/src/utils/await.ts diff --git a/packages/js/test-env/src/declarations.d.ts b/packages/js/test-env/src/utils/declarations.d.ts similarity index 100% rename from packages/js/test-env/src/declarations.d.ts rename to packages/js/test-env/src/utils/declarations.d.ts diff --git a/packages/js/test-env/src/generate-name.ts b/packages/js/test-env/src/utils/generate-name.ts similarity index 100% rename from packages/js/test-env/src/generate-name.ts rename to packages/js/test-env/src/utils/generate-name.ts diff --git a/packages/js/test-env/src/utils/index.ts b/packages/js/test-env/src/utils/index.ts new file mode 100644 index 0000000000..b9e40895e2 --- /dev/null +++ b/packages/js/test-env/src/utils/index.ts @@ -0,0 +1,2 @@ +export * from "./await"; +export * from "./generate-name"; From d2309a6ebd641130f3fa4d564434c49cf5c32b20 Mon Sep 17 00:00:00 2001 From: krisbitney Date: Tue, 18 Oct 2022 21:34:46 +0500 Subject: [PATCH 4/4] updated cwd for cli-api.ts methods --- packages/js/test-env/src/cli-api.ts | 34 +++++++++++------------------ 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/packages/js/test-env/src/cli-api.ts b/packages/js/test-env/src/cli-api.ts index 7f972587ab..e02a445352 100644 --- a/packages/js/test-env/src/cli-api.ts +++ b/packages/js/test-env/src/cli-api.ts @@ -1,8 +1,6 @@ import { runCLI } from "./index"; import { awaitPing } from "./utils"; -import path from "path"; - type InfraCommandOptions = { infraManifest?: string; modules?: string[]; @@ -121,17 +119,15 @@ export async function infraDown( /** * Generate code for a Polywrap project * - * @param wrapperAbsPath: absolute path of the wrapper root folder + * @param wrapperAbsPath: absolute path of the wrapper root folder, used as the cwd * @param options: options for Polywrap CLI 'codegen' command - * @param cwd: the current working directory for the CLI call * */ export async function codegen( wrapperAbsPath: string, - options?: CodegenCommandOptions, - cwd: string = wrapperAbsPath + options?: CodegenCommandOptions ): Promise { const manifestFile = options?.projectManifest - ? ["--manifest-file", path.join(wrapperAbsPath, options.projectManifest)] + ? ["--manifest-file", options.projectManifest] : []; const codegenDir = options?.codegenDir @@ -160,7 +156,7 @@ export async function codegen( `--verbose ${!!options?.verbose}`, `--quiet ${!!options?.quiet}`, ], - cwd, + cwd: wrapperAbsPath, }); if (buildExitCode !== 0) { @@ -174,17 +170,15 @@ export async function codegen( /** * Build a wrapper * - * @param wrapperAbsPath: absolute path of the wrapper root folder + * @param wrapperAbsPath: absolute path of the wrapper root folder, used as the cwd * @param options: options for Polywrap CLI 'build' command - * @param cwd: the current working directory for the CLI call * */ export async function build( wrapperAbsPath: string, - options?: BuildCommandOptions, - cwd: string = wrapperAbsPath + options?: BuildCommandOptions ): Promise { const manifestFile = options?.projectManifest - ? ["--manifest-file", path.join(wrapperAbsPath, options.projectManifest)] + ? ["--manifest-file", options.projectManifest] : []; const outputDir = options?.outputDir @@ -208,11 +202,11 @@ export async function build( ...outputDir, ...strategy, ...clientConfig, - `--noCodegen ${!!options?.noCodegen}`, + `--no-codegen ${!!options?.noCodegen}`, `--verbose ${!!options?.verbose}`, `--quiet ${!!options?.quiet}`, - cwd, ], + cwd: wrapperAbsPath, }); if (buildExitCode !== 0) { @@ -226,17 +220,15 @@ export async function build( /** * Deploy a wrapper using a deploy manifest * - * @param wrapperAbsPath: absolute path of the wrapper root folder + * @param wrapperAbsPath: absolute path of the wrapper root folder, used as the cwd * @param options: options for Polywrap CLI 'deploy' command - * @param cwd: the current working directory for the CLI call * */ export async function deploy( wrapperAbsPath: string, - options?: DeployCommandOptions, - cwd: string = wrapperAbsPath + options?: DeployCommandOptions ): Promise { const manifestFile = options?.projectManifest - ? ["--manifest-file", path.join(wrapperAbsPath, options.projectManifest)] + ? ["--manifest-file", options.projectManifest] : []; const outputArgs = options?.outputFile @@ -255,7 +247,7 @@ export async function deploy( `--verbose ${!!options?.verbose}`, `--quiet ${!!options?.quiet}`, ], - cwd, + cwd: wrapperAbsPath, }); if (buildExitCode !== 0) {