-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
a8b11f5
commit 214361a
Showing
8 changed files
with
266 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,127 @@ | ||
import { BaseConfig } from "./config"; | ||
import path from "path"; | ||
import { exec, ExecException } from "child_process"; | ||
|
||
import { BaseConfig, readConfig } from "@/lib/config"; | ||
import { buildApp } from "@/lib/build"; | ||
import { readWorkspace } from "@/lib/workspace"; | ||
import { Log, Network } from "@/lib/types"; | ||
import { readdir, remove, move, pathExists } from "@/lib/utils/fs"; | ||
import { Logger } from "./logger"; | ||
|
||
const DEPLOY_DIST_FOLDER = "build"; | ||
|
||
export type DeployOptions = { | ||
deployAccountId?: string; | ||
signerAccountId?: string; | ||
signerPublicKey?: string; | ||
signerPrivateKey?: string; | ||
network?: Network; | ||
}; | ||
|
||
// translate files from src/widget to src | ||
export async function translateForBosCli(dist: string) { | ||
const srcDir = path.join(dist, "src", "widget"); | ||
const targetDir = path.join(dist, "src"); | ||
|
||
const new_files = await readdir(srcDir).catch(() => ([])); | ||
const original_files = await readdir(targetDir).catch(() => ([])); | ||
|
||
for (const file of new_files) { | ||
await move(path.join(srcDir, file), path.join(targetDir, file), { overwrite: true }); | ||
} | ||
|
||
for (const file of original_files) { | ||
if (new_files.includes(file)) | ||
continue; | ||
|
||
await remove(path.join(targetDir, file)); | ||
} | ||
} | ||
|
||
// deploy the app widgets and modules | ||
export async function deployAppCode(src: string, config: BaseConfig) { | ||
export async function deployAppCode(src: string, dist: string, opts: DeployOptions) { | ||
const fullSrc = path.resolve(src); | ||
const fullDist = path.resolve(dist); | ||
|
||
const deploying = log.loading(`[${fullSrc}] Deploying app`, LogLevels.BUILD); | ||
|
||
// Build | ||
await buildApp(src, dist, opts.network); | ||
|
||
// Translate for bos cli | ||
await log.wait( | ||
translateForBosCli(dist), | ||
`[${fullDist}] Translating files for bos cli`, | ||
`[${fullDist}] Translated successfully`, | ||
`[${fullDist}] Failed to translate`, | ||
LogLevels.BUILD | ||
); | ||
|
||
// Exec bos-cli; | ||
const config = await readConfig(path.join(src, "bos.config.json"), opts.network); | ||
|
||
const BOS_DEPLOY_ACCOUNT_ID = config.accounts.deploy || opts.deployAccountId; | ||
const BOS_SIGNER_ACCOUNT_ID = config.accounts.signer || opts.signerAccountId; | ||
const BOS_SIGNER_PUBLIC_KEY = opts.signerPublicKey; | ||
const BOS_SIGNER_PRIVATE_KEY = opts.signerPrivateKey; | ||
|
||
if (!BOS_DEPLOY_ACCOUNT_ID) { | ||
deploying.error(`Necessary values not provided, please provide Account ID for deploy`); | ||
return; | ||
} else if (!BOS_SIGNER_ACCOUNT_ID) { | ||
deploying.error(`Necessary values not provided, please provide Signer Account ID for deploy`); | ||
return; | ||
} else if (!BOS_SIGNER_PUBLIC_KEY || !BOS_SIGNER_PRIVATE_KEY) { | ||
deploying.error(`Necessary values not provided, please provide private & public key for deploy`); | ||
return; | ||
} | ||
|
||
exec( | ||
`cd ${dist} && npx bos components deploy "${BOS_DEPLOY_ACCOUNT_ID}" sign-as "${BOS_SIGNER_ACCOUNT_ID}" network-config "${opts.network}" sign-with-plaintext-private-key --signer-public-key "${BOS_SIGNER_PUBLIC_KEY}" --signer-private-key "${BOS_SIGNER_PRIVATE_KEY}" send`, | ||
(error: ExecException | null, stdout: string, stderr: string) => { | ||
if (!error) { | ||
deploying.finish(`[${fullSrc}] App deployed successfully`); | ||
return; | ||
} | ||
|
||
deploying.error(error.message); | ||
} | ||
); | ||
} | ||
|
||
// publish data.json to SocialDB | ||
export async function deployAppData(src: string, config: BaseConfig) { | ||
} | ||
|
||
export async function deploy(appName: string, opts: DeployOptions) { | ||
const src = '.'; | ||
|
||
// Deploy single project | ||
if (!appName) { | ||
if (await pathExists(path.join(src, "bos.config.json"))) { // Check if the directory has bos.config.json file | ||
await deployAppCode(src, path.join(src, DEPLOY_DIST_FOLDER), opts); | ||
return; | ||
} else { // Check if the directory has bos.workspace.json file | ||
if (await pathExists(path.join(src, "bos.workspace.json"))) { | ||
log.error(`Please provide app name`); | ||
return; | ||
} | ||
} | ||
|
||
log.error(`[${src}] bos.config.json file is not existing in the project`); | ||
return; | ||
} | ||
|
||
// Deploy workspace app | ||
const { apps } = await readWorkspace(src); | ||
|
||
const findingApp = log.loading(`Finding ${appName} in the workspace`, LogLevels.BUILD); | ||
const appSrc = apps.find((app) => app.includes(appName)); | ||
if (!appSrc) { | ||
findingApp.error(`Not found ${appName} in the workspace`); | ||
return; | ||
} | ||
findingApp.finish(`Found ${appName} in the workspace`); | ||
|
||
await deployAppCode(appSrc, path.join(DEPLOY_DIST_FOLDER, appSrc), opts); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
import * as process from "child_process"; | ||
import { deployAppCode } from '@/lib/deploy'; | ||
import { BaseConfig, DEFAULT_CONFIG } from '@/lib/config'; | ||
import * as fs from '@/lib/utils/fs'; | ||
import { LogLevel, Logger } from "@/lib/logger"; | ||
|
||
import { vol, } from 'memfs'; | ||
jest.mock('fs', () => require('memfs').fs); | ||
jest.mock('fs/promises', () => require('memfs').fs.promises); | ||
jest.mock('child_process', () => ({ | ||
exec: jest.fn((command: string) => { | ||
return command; | ||
}), | ||
})) | ||
|
||
const app_example = { | ||
"./bos.config.json": JSON.stringify({ | ||
...DEFAULT_CONFIG, | ||
account: "test.near", | ||
ipfs: { | ||
gateway: "https://testipfs/ipfs", | ||
}, | ||
format: true, | ||
}), | ||
"./aliases.json": JSON.stringify({ | ||
"name": "world", | ||
}), | ||
"./ipfs/logo.svg": "<svg viewBox='0 0 100 100'><circle cx='50' cy='50' r='50' fill='red' /></svg>", | ||
"./module/hello/utils.ts": "const hello = (name: string) => `Hello, ${name}!`; export default { hello };", | ||
"./widget/index.tsx": "type Hello = {}; const hello: Hello = 'hi'; export default hello;", | ||
"./widget/index.metadata.json": JSON.stringify({ | ||
name: "Hello", | ||
description: "Hello world widget", | ||
}), | ||
"./widget/nested/index.tsx": "type Hello = {}; const hello: Hello = 'hi'; export default hello;", | ||
"./widget/nested/index.metadata.json": JSON.stringify({ | ||
name: "Nested Hello", | ||
description: "Nested Hello world widget", | ||
}), | ||
"./widget/module.tsx": "VM.require('${module_hello_utils}'); export default hello('world');", | ||
"./widget/config.jsx": "return <h1>${config_account}${config_account_deploy}</h1>;", | ||
"./widget/alias.tsx": "export default <h1>Hello ${alias_name}!</h1>;", | ||
"./widget/ipfs.tsx": "export default <img height='100' src='${ipfs_logo.svg}' />;", | ||
"./data/thing/data.json": JSON.stringify({ | ||
"type": "efiz.near/type/thing", | ||
}), | ||
"./data/thing/datastring.jsonc": JSON.stringify({ | ||
name: "Thing", | ||
}), | ||
}; | ||
|
||
const app_example_output = { | ||
"/build/ipfs.json": JSON.stringify({ | ||
"logo.svg": "QmHash", | ||
}, null, 2) + "\n", | ||
"/build/src/hello.utils.module.js": "const hello = (name) => `Hello, ${name}!`;\nreturn { hello };\n", | ||
"/build/src/index.jsx": "const hello = \"hi\";\nreturn hello(props);\n", | ||
"/build/src/nested.index.jsx": "const hello = \"hi\";\nreturn hello(props);\n", | ||
"/build/src/module.jsx": "VM.require(\"test.near/widget/hello.utils.module\");\nreturn hello(\"world\");\n", | ||
"/build/src/config.jsx": "return <h1>test.neartest.near</h1>;\n", | ||
"/build/src/alias.jsx": "return <h1>Hello world!</h1>;\n", | ||
"/build/src/ipfs.jsx": "return <img height=\"100\" src=\"https://testipfs/ipfs/QmHash\" />;\n", | ||
"/build/data.json": JSON.stringify({ | ||
"test.near": { | ||
thing: { | ||
data: { | ||
"type": "efiz.near/type/thing", | ||
}, | ||
datastring: JSON.stringify({ | ||
name: "Thing", | ||
}) | ||
}, | ||
widget: { | ||
index: { | ||
metadata: { | ||
name: "Hello", | ||
description: "Hello world widget", | ||
} | ||
}, | ||
"nested.index": { | ||
metadata: { | ||
name: "Nested Hello", | ||
description: "Nested Hello world widget", | ||
} | ||
|
||
} | ||
} | ||
} | ||
}, null, 2) + "\n", | ||
}; | ||
|
||
const unmockedFetch = global.fetch; | ||
const unmockedLog = global.log; | ||
|
||
describe('deploy', () => { | ||
beforeEach(() => { | ||
vol.reset(); | ||
vol.fromJSON(app_example, '/app_example'); | ||
|
||
global.fetch = (() => { | ||
return Promise.resolve({ | ||
json: () => Promise.resolve({ | ||
cid: "QmHash", | ||
}) | ||
}) | ||
}) as any; | ||
global.log = new Logger(LogLevel.ERROR); | ||
}) | ||
afterAll(() => { | ||
global.fetch = unmockedFetch; | ||
global.log = unmockedLog; | ||
}) | ||
|
||
it('should match expected input for bos-cli-rs', async () => { | ||
await deployAppCode('/app_example', '/build', {}); | ||
expect(vol.toJSON('/build')).toEqual(app_example_output); | ||
}) | ||
}) |
How come this was replaced?
A change in this file introduced a bug where deeply nested widget paths don't work anymore. See bug
I've started a branch with a good test that is currently failing, make the fix on this branch: #89