Skip to content

Commit

Permalink
workflow works
Browse files Browse the repository at this point in the history
  • Loading branch information
chenyan-dfinity committed Sep 11, 2024
1 parent b568028 commit ee0892c
Show file tree
Hide file tree
Showing 6 changed files with 227 additions and 52 deletions.
15 changes: 13 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
} from "./contexts/WorkplaceState";
import { ProjectModal } from "./components/ProjectModal";
import { DeployModal, DeploySetter } from "./components/DeployModal";
import { FrontendDeployModal } from "./components/FrontendDeployModal";
import { backend, saved } from "./config/actor";
import { setupEditorIntegration } from "./integrations/editorIntegration";
import { Container } from "./webcontainer";
Expand Down Expand Up @@ -180,6 +181,7 @@ export function App() {

// States for deploy modal
const [showDeployModal, setShowDeployModal] = useState(false);
const [showFrontendDeployModal, setShowFrontendDeployModal] = useState(false);
const [isDeploying, setIsDeploying] = useState(false);
const [candidCode, setCandidCode] = useState("");
const [initTypes, setInitTypes] = useState([]);
Expand All @@ -190,6 +192,7 @@ export function App() {
setInitTypes,
setCandidCode,
setShowDeployModal,
setShowFrontendDeployModal,
setWasm,
};

Expand Down Expand Up @@ -258,9 +261,7 @@ export function App() {
await container.initFiles();
await container.run_cmd("npm", ["install"], {
cwd: "utils",
output: false,
});
//await container.run_cmd("node", ["uploadAsset.js", "dist"]);
// fetch code after loading base library
if (hasUrlParams) {
const files = await fetchFromUrlParams(workplaceDispatch);
Expand Down Expand Up @@ -327,6 +328,16 @@ export function App() {
logger={logger}
origin={workplaceState.origin}
/>
<FrontendDeployModal
state={workplaceState}
isOpen={showFrontendDeployModal}
close={() => setShowFrontendDeployModal(false)}
onDeploy={deployWorkplace}
isDeploy={setIsDeploying}
canisters={getDeployedCanisters(workplaceState.canisters)}
logger={logger}
origin={workplaceState.origin}
/>
<AppContainer
candidWidth={candidWidth}
consoleHeight={consoleHeight}
Expand Down
2 changes: 1 addition & 1 deletion src/components/Console.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const TerminalContainer = styled.div<{ isActive: boolean }>`
const fitAddon = new FitAddon();

export function Console({ setConsoleHeight, terminal }) {
const [activeTab, setActiveTab] = useState("terminal");
const [activeTab, setActiveTab] = useState("log");
const terminalRef = useRef<HTMLDivElement>(null);
const [isExpanded, setIsExpanded] = useState(true);
const lastRef = useRef<HTMLInputElement>(null);
Expand Down
51 changes: 2 additions & 49 deletions src/components/DeployModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ export interface DeploySetter {
setMainFile: (name: string) => void;
setCandidCode: (code: string) => void;
setInitTypes: (args: Array<IDL.Type>) => void;
setShowDeployModal: (boolean) => void;
setShowDeployModal: (arg: boolean) => void;
setShowFrontendDeployModal: (arg: boolean) => void;
setWasm: (file: Uint8Array | undefined) => void;
}

Expand Down Expand Up @@ -157,7 +158,6 @@ export function DeployModal({
const [deployMode, setDeployMode] = useState("");
const [startDeploy, setStartDeploy] = useState(false);
const worker = useContext(WorkerContext);
const container = useContext(ContainerContext);
const dispatch = useContext(WorkplaceDispatchContext);

const exceedsLimit = Object.keys(canisters).length >= MAX_CANISTERS;
Expand Down Expand Up @@ -315,52 +315,6 @@ export function DeployModal({
} catch (err) {}
}

async function deployFrontend() {
try {
await close();
await isDeploy(true);
const module_hash = assetWasmHash
.match(/.{2}/g)!
.map((byte) => parseInt(byte, 16));
const info = await deploy(
worker,
canisterName,
canisters[canisterName],
new Uint8Array(IDL.encode([], [])),
"install",
new Uint8Array(module_hash),
true,
false,
false,
logger,
origin,
);
const identity = Ed25519KeyIdentity.generate();
const principal = identity.getPrincipal();
const args = IDL.encode([IDL.Principal], [principal]);
await backend.callForward(info!, "authorize", args);
await container.container!.fs.writeFile(
"identity.json",
JSON.stringify(identity.toJSON()),
);
logger.log(`Authorized asset canister with ${principal}`);
await container.run_cmd("node", [
"uploadAsset.js",
info!.id.toText(),
"dist",
]);
await isDeploy(false);
setCompileResult({ wasm: undefined });
if (info) {
info.isFrontend = true;
onDeploy(info);
}
} catch (err) {
await isDeploy(false);
throw err;
}
}

async function handleDeploy(mode: string) {
const args = parse();
await addTags();
Expand Down Expand Up @@ -629,7 +583,6 @@ actor {
Install
</MyButton>
)}
<MyButton onClick={deployFrontend}>Frontend</MyButton>
<MyButton onClick={close}>Cancel</MyButton>
</ButtonContainer>
) : null}
Expand Down
2 changes: 2 additions & 0 deletions src/components/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,13 @@ export function Editor({
const deployClick = async () => {
if (selectFrontend) {
try {
logger.log("Building frontend... (see logs in the terminal tab)");
const { files, env } = generateNonMotokoFilesToWebContainer(state);
console.log(files);
await container.container!.mount(files, { mountPoint: "user" });
await container.run_cmd("npm", ["install"], { cwd: "user" });
await container.run_cmd("npm", ["run", "build"], { cwd: "user", env });
deploySetter.setShowFrontendDeployModal(true);
} catch (e) {
logger.log(e.message);
}
Expand Down
208 changes: 208 additions & 0 deletions src/components/FrontendDeployModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
import { useState, useCallback, useEffect, useContext } from "react";
import styled from "styled-components";
import { Ed25519KeyIdentity } from "@dfinity/identity";
import { IDL } from "@dfinity/candid";

import { Modal } from "./shared/Modal";
import { CanisterInfo, deploy } from "../build";
import { ILoggingStore } from "./Logger";
import { Button } from "./shared/Button";
import {
WorkerContext,
WorkplaceState,
ContainerContext,
Origin,
} from "../contexts/WorkplaceState";
import { didjs, backend } from "../config/actor";
import { Field } from "./shared/Field";

const assetWasmHash =
"3a533f511b3960b4186e76cf9abfbd8222a2c507456a66ec55671204ee70cae3";

const ModalContainer = styled.div`
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
width: 46rem;
`;

const FormContainer = styled.div`
width: 100%;
margin-top: 2rem;
`;

const ButtonContainer = styled.div`
display: flex;
justify-content: center;
width: 100%;
margin-top: 3rem;
`;

const MyButton = styled(Button)`
width: 12rem;
`;

interface FrontendDeployModalProps {
state: WorkplaceState;
isOpen: boolean;
close: () => void;
onDeploy: (string) => void;
isDeploy: (tag: boolean) => void;
canisters: Record<string, CanisterInfo>;
logger: ILoggingStore;
origin: Origin;
}

const MAX_CANISTERS = 3;

export function FrontendDeployModal({
state,
isOpen,
close,
onDeploy,
isDeploy,
canisters,
logger,
origin,
}: FrontendDeployModalProps) {
const [canisterName, setCanisterName] = useState("frontend");
const [buildDir, setBuildDir] = useState("dist");
const worker = useContext(WorkerContext);
const container = useContext(ContainerContext);

const exceedsLimit = Object.keys(canisters).length >= MAX_CANISTERS;

async function deployClick(mode: string) {
try {
await close();
await isDeploy(true);
var info: CanisterInfo | undefined = canisters[canisterName];
if (mode !== "upgrade") {
const module_hash = assetWasmHash
.match(/.{2}/g)!
.map((byte) => parseInt(byte, 16));
info = await deploy(
worker,
canisterName,
canisters[canisterName],
new Uint8Array(IDL.encode([], [])),
mode,
new Uint8Array(module_hash),
true,
false,
false,
logger,
origin,
);
const identity = Ed25519KeyIdentity.generate();
const principal = identity.getPrincipal();
const args = IDL.encode([IDL.Principal], [principal]);
await backend.callForward(info!, "authorize", args);
await container.container!.fs.writeFile(
"utils/identity.json",
JSON.stringify(identity.toJSON()),
);
logger.log(`Authorized asset canister with ${principal}`);
if (info) {
info.isFrontend = true;
onDeploy(info);
}
}
logger.log(`Uploading frontend...`);
await container.run_cmd(
"node",
["uploadAsset.js", info!.id.toText(), `../user/${buildDir}`],
{ cwd: "utils" },
);
logger.log(`Frontend uploaded`);
await isDeploy(false);
} catch (err) {
await isDeploy(false);
throw err;
}
}
const welcomeText = <p>Deploy your frontend to the IC</p>;
const deployLabelText = "Select a canister name";
const newDeploy = (
<>
<Field
type="text"
labelText={deployLabelText}
list="canisters"
value={canisterName}
onChange={(e) => setCanisterName(e.target.value)}
/>
<datalist id="canisters">
{Object.keys(canisters).map((canister, i) => (
<option key={`${canister}${i}`}>{canister}</option>
))}
</datalist>
</>
);
const selectDeploy = (
<Field
required
type="select"
labelText={deployLabelText}
value={canisterName}
onChange={(e) => setCanisterName(e.target.value)}
>
{Object.keys(canisters).map((canister) => (
<option value={canister}>{canister}</option>
))}
</Field>
);

return (
<>
<Modal
isOpen={isOpen}
close={close}
label="Deploy Canister"
shouldCloseOnEsc
shouldCloseOnOverlayClick
>
<ModalContainer>
<div style={{ maxHeight: 680, overflowY: "auto", width: "100%" }}>
{welcomeText}
<FormContainer>
{exceedsLimit ? selectDeploy : newDeploy}
<Field
type="text"
labelText="Build directory"
value={buildDir}
onChange={(e) => setBuildDir(e.target.value)}
/>
</FormContainer>
</div>
{canisterName ? (
<ButtonContainer>
{canisters.hasOwnProperty(canisterName) ? (
<>
<MyButton
variant="primary"
onClick={() => deployClick("upgrade")}
>
Upgrade
</MyButton>
<MyButton onClick={() => deployClick("reinstall")}>
Reinstall
</MyButton>
</>
) : (
<MyButton
variant="primary"
onClick={() => deployClick("install")}
>
Install
</MyButton>
)}
<MyButton onClick={close}>Cancel</MyButton>
</ButtonContainer>
) : null}
</ModalContainer>
</Modal>
</>
);
}
1 change: 1 addition & 0 deletions src/contexts/WorkplaceState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export function generateNonMotokoFilesToWebContainer(state: WorkplaceState) {
function generateEnv(state: WorkplaceState) {
const env: Record<string, string> = {
DFX_NETWORK: "ic",
NODE_ENV: "production",
};
const canister_ids = {};
Object.entries(state.canisters).forEach(([name, info]) => {
Expand Down

0 comments on commit ee0892c

Please sign in to comment.