Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add Docker setup #9

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,13 @@ Directory tree should look like:

## Requirements

If you are comfortable managing your environment, we suggest the following dependencies:

- Node.js 18.0.0 or later
- npm 6.0.0 or later
- Python 3.10 or later

Otherwise, just use [Docker](docker.com) and choose it during project setup.

## License

Expand Down
10 changes: 5 additions & 5 deletions commitlint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export default {
extends: ["@commitlint/config-conventional"],
rules: {
"body-max-line-length": [0, "always", Infinity],
},
};
extends: ["@commitlint/config-conventional"],
rules: {
"body-max-line-length": [0, "always", Infinity],
},
};
24 changes: 24 additions & 0 deletions examples/nextjs/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
FROM node:23-alpine3.19 AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install

FROM node:23-alpine3.19 AS dev
RUN apk add "python3>3.10"
RUN apk add curl
RUN curl -fsSL https://nilup.nilogy.xyz/install.sh | sh
ENV PATH="$PATH:/root/.nilup/bin"
RUN nilup use latest
RUN nilup -V
WORKDIR /app
RUN nada init nada
RUN cd nada && python3 -m venv .venv
COPY . .

COPY --from=deps /app/node_modules ./node_modules
COPY --from=deps /app/package.json ./package.json

EXPOSE 3000
ENV PORT=3000
CMD ["sh"]
Copy link
Contributor Author

@cyberglot cyberglot Oct 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In some case, users may want to run commands inside the Docker container, now they easily can.

15 changes: 14 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@
"test": "jest",
"format": "prettier --write \"**/*.{js,ts,json,md}\"",
"prepare": "husky",
"lint": "eslint . --fix",
"lint": "eslint src --fix",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reason, it was trying to lint the code generated in dist/ ?

"type-check": "tsc --noEmit",
"check": "npm run type-check && npm run lint",
"pre-commit": "lint-staged && npm run type-check"
},
"lint-staged": {
"*.{js,ts, mjs}": [
"eslint --fix",
"eslint src --fix",
"prettier --write"
],
"*.{json,md}": "prettier --write"
Expand Down Expand Up @@ -69,6 +69,7 @@
"dependencies": {
"@nillion/client-core": "0.1.0-rc.16",
"@nillion/client-react-hooks": "0.1.0-rc.16",
"@nillion/client-vms": "0.1.0-rc.16"
"@nillion/client-vms": "0.1.0-rc.16",
"eta": "^3.5.0"
}
}
105 changes: 105 additions & 0 deletions src/functions/dockerInstead.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import readline from "readline";
import fs from "fs";
import fsp from "node:fs/promises";
import path from "path";
import { fileURLToPath } from "url";
import { Eta } from "eta";
import { execSync } from "child_process";

export function isDockerInstalled(): boolean {
try {
const dockerVer = execSync("docker --version", { stdio: "pipe" }).toString().trim();
execSync("docker --version", { stdio: "pipe" });
console.log(`Docker is already installed: ${dockerVer}`);
return true;
} catch (error: unknown) {
console.log(error);
return false;
}
}

export function noDocker(): string {
return "Docker is not installed. Please install Docker (https://www.docker.com) and try again.";
process.exit();
}

export async function wouldPreferDocker(): Promise<boolean> {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});

const dockerMessage =
"Would you prefer to use Docker to run the project? Recommended if you are not comfortable managing your own dependencies. (default: no) ";

return new Promise((resolve) => {
rl.question(dockerMessage, (answer) => {
rl.close();
const yesOrNo = answer.trim() || "nillion-quickstart";
if (/y|yes|n|no/.test(yesOrNo)) {
resolve(yesOrNo === "y" || yesOrNo === "yes");
} else {
console.log("Using default: no Docker container.");
resolve(false);
}
});
Comment on lines +36 to +45
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to replace this part of the code with something more robust like prompt.js or inquirer.js!

});
}

export function createProject(useDocker: boolean, projectName: string, rootDir: string): void {
console.log("--------------------");
console.log("Creating Docker project... please, wait a moment.");
const nextAppPath: string = path.join(rootDir);

const currentFilePath = fileURLToPath(import.meta.url);
const currentDir = path.dirname(currentFilePath);
const examplesPath = path.join(currentDir, "..", "..", "examples", "nextjs");

if (!fs.existsSync(examplesPath)) {
throw new Error(`Next.js example not found at ${examplesPath}`);
}

fs.cpSync(examplesPath, nextAppPath, { recursive: true });
const root = path.join(currentDir, "..", "..");
instantiateTemplates(useDocker, projectName, root, nextAppPath);

console.log(`Your project has been created successfully at ${nextAppPath}!`);
console.log("--------------------");
}

const npm = { name: "package.json" };
const docker = { name: "docker-compose.yaml" };

function instantiateTemplates(
useDocker: boolean,
projectName: string,
rootDir: string,
projectDir: string,
) {
const info = [npm];
if (useDocker) info.push(docker);

const eta = new Eta({ views: path.join(rootDir, "templates") });

info
.map(({ name }) => {
return {
content: eta.render(`./${name}.eta`, { useDocker, projectName }),
fullNamePath: path.join(projectDir, name),
};
})
.forEach(writeFile);
}

interface FileInfo {
fullNamePath: string;
content: string;
}

async function writeFile({ fullNamePath, content }: FileInfo) {
try {
await fsp.writeFile(fullNamePath, content);
} catch (err) {
console.log(`Error Writing file ${fullNamePath}: ${err}`);
}
}
10 changes: 6 additions & 4 deletions src/functions/installRepoPackage.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
export const installDependencies = async () => {
// NPM install part
export const installDependencies = (cmd: string, args: string[]) => async () => {
// abstracted to use either npm or docker
console.log("Installing project dependencies...");
await new Promise((resolve, reject) => {
const installProcess = import("child_process").then((childProcess) =>
childProcess.spawn("npm", ["install"], { stdio: "inherit" }),
childProcess.spawn(cmd, args, { stdio: "inherit" }),
);
installProcess
.then((process) => {
process.on("close", (code: number) => {
if (code !== 0) {
reject(new Error(`npm install failed with code ${code}`));
reject(new Error(`installation failed with code ${code}`));
} else {
resolve(undefined);
}
Expand All @@ -19,3 +19,5 @@ export const installDependencies = async () => {
});
console.log("Dependencies installed successfully.");
};

export const installRepoPackage = installDependencies("npm", ["install"]);
49 changes: 35 additions & 14 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
#!/usr/bin/env node

import fs from "fs";
import open from "open";

import { displayLogo } from "./functions/displayLogo.js";
import { displayWelcomeMessage } from "./functions/displayWelcomeMessage.js";
import { isNilupInstalled } from "./functions/isNilupInstalled.js";
import { installNilup } from "./functions/installNilup.js";
import { createNextJsProject } from "./functions/createNextJsProject.js";
import { promptForProjectName } from "./functions/nameRepo.js";
import { installDependencies } from "./functions/installRepoPackage.js";
import open from "open";
import { installRepoPackage } from "./functions/installRepoPackage.js";
import {
isDockerInstalled,
wouldPreferDocker,
noDocker,
createProject,
} from "./functions/dockerInstead.js";

async function main() {
displayLogo();
Expand All @@ -17,25 +24,39 @@ async function main() {
const projectName = await promptForProjectName();
console.log("--------------------");
console.log(`Creating project: ${projectName}`);
console.log("--------------------");
console.log("Checking if Nilup is installed...");
const useDocker = await wouldPreferDocker();
let instructions;

if (!isNilupInstalled()) {
installNilup();
}
if (useDocker) {
console.log("--------------------");
console.log("Checking if Docker is installed...");
if (!isDockerInstalled()) noDocker();

fs.mkdirSync(projectName, { recursive: true });
process.chdir(projectName);
fs.mkdirSync(projectName, { recursive: true });
process.chdir(projectName);
createProject(useDocker, projectName, process.cwd());
console.log("Docker build completed!");
instructions =
"Run `npm run docker:build` followed by `npm run docker:up`, then open `localhost:3000` in your browser to see your new project.\nPlease check https://github.com/NillionNetwork/create-nillion-app/blob/main/docker.md to learn more how to use Docker in this project.";
} else {
console.log("--------------------");
console.log("Checking if Nilup is installed...");

createNextJsProject(process.cwd());
if (!isNilupInstalled()) {
installNilup();
}

await installDependencies();
fs.mkdirSync(projectName, { recursive: true });
process.chdir(projectName);
createNextJsProject(process.cwd());
await installRepoPackage();
instructions =
"Run `npm run dev` inside your project's folder and open `localhost:3000` in your browser to see your new project.";
}

console.log("--------------------");
console.log(`Nillion quickstart has been created successfully! 🚀`);
console.log(
`Cd into your repo + run "npm run dev" and open localhost:3000 in your browser to see your new project.`,
);
console.log(instructions);
console.log("--------------------");
console.log(`Follow the rest of the Quickstart Guide to get started!`);
console.log("Opening the Nillion Quickstart Guide in your browser...");
Expand Down
21 changes: 21 additions & 0 deletions templates/docker-compose.yaml.eta
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
services:
<%= it.projectName %>:
restart: always
environment:
- WATCHPACK_POLLING=true
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is for Windows users, but I am not sure if it works because I don't have a Windows machine.

- NODE_ENV=development
build:
context: ./
target: dev
stdin_open: true
ports:
- "3000:3000"
volumes:
- ./:/nillion
command: npx next dev
healthcheck:
test: curl -fSs http://127.0.0.1:3000 || exit 1
start_period: 180s
timeout: 10s
interval: 10s
retries: 3
35 changes: 35 additions & 0 deletions templates/package.json.eta
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "<%= it.projectName %>",
"version": "0.1.0",
"private": true,
"scripts": {
"format": "npx prettier . --write",
<% if (it.useDocker) { %>
"docker:build": "docker compose build",
"docker:up": "docker compose up -d",
"docker:down": "docker compose down"
<% } else { %>
"dev": "npx next dev",
"build": "npx next build",
"start": "npx next start",
"lint": "npx next lint"
<% } %>
},
"dependencies": {
"@nillion/client-react-hooks": "^0.1.0",
"next": "14.2.14",
"react": "^18",
"react-dom": "^18"
},
"devDependencies": {
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"eslint": "^8",
"eslint-config-next": "14.2.14",
"postcss": "^8",
"prettier": "3.3.3",
"tailwindcss": "^3.4.1",
"typescript": "^5"
}
}
Loading