next-start-cli
Version:
This is a CLI tool to create a nextjs project and add the necessary dependencies and configurations
226 lines (201 loc) • 7.71 kB
JavaScript
import { createSpinner } from "nanospinner";
import chalk from "chalk";
import fs from "fs/promises";
import path from "path";
import { execa } from "execa";
import inquirer from "inquirer";
import { copyTemplateFiles } from "../utils/templateCopier.js";
export const setupInfra = async (project, noGit = false) => {
console.log(chalk.gray("─".repeat(50)));
console.log(chalk.green(`Setting up Docker infrastructure for ${project}`));
// Get node and yarn versions
const nodeVersionSpinner = createSpinner("Detecting Node version").start();
let nodeVersion;
try {
// Use exact version from user's computer
const { stdout } = await execa("node", ["--version"]);
nodeVersion = stdout.trim().replace("v", "");
nodeVersionSpinner.success({
text: `Detected Node version: ${nodeVersion}`,
});
} catch (error) {
nodeVersionSpinner.error({ text: "Failed to detect Node version" });
const { version } = await inquirer.prompt({
type: "input",
name: "version",
message: "Enter Node version to use:",
default: "20.11.0",
});
nodeVersion = version;
}
const yarnVersionSpinner = createSpinner("Detecting Yarn version").start();
let yarnVersion;
try {
const { stdout } = await execa("yarn", ["--version"]);
yarnVersion = stdout.trim();
yarnVersionSpinner.success({
text: `Detected Yarn version: ${yarnVersion}`,
});
} catch (error) {
yarnVersionSpinner.error({ text: "Failed to detect Yarn version" });
const { version } = await inquirer.prompt({
type: "input",
name: "version",
message: "Enter Yarn version to use:",
default: "1.22.22",
});
yarnVersion = version;
}
try {
// Copy template files first
const templateSpinner = createSpinner(
"Copying Docker infrastructure templates"
).start();
await copyTemplateFiles("infra", process.cwd());
templateSpinner.success({
text: "Docker infrastructure templates copied successfully",
});
// Update Dockerfiles with proper versions
const updateDockerfilesSpinner = createSpinner(
"Updating Docker configurations"
).start();
const scriptPath = path.join(process.cwd(), "scripts", "start-docker.sh");
await fs.chmod(scriptPath, 0o755);
// Update Dockerfile.dev
const dockerfileDev = path.join(process.cwd(), "Dockerfile.dev");
let dockerfileDevContent = await fs.readFile(dockerfileDev, "utf8");
dockerfileDevContent = dockerfileDevContent
.replace(/FROM node:.*-alpine/, `FROM node:${nodeVersion}-alpine`)
.replace(
/RUN npm install -g yarn@.*/,
`RUN npm install -g yarn@${yarnVersion} --force`
);
await fs.writeFile(dockerfileDev, dockerfileDevContent, "utf8");
// Update Dockerfile.prod
const dockerfileProd = path.join(process.cwd(), "Dockerfile.prod");
let dockerfileProdContent = await fs.readFile(dockerfileProd, "utf8");
dockerfileProdContent = dockerfileProdContent
.replace(
/FROM node:.*-alpine AS base/,
`FROM node:${nodeVersion}-alpine AS base`
)
.replace(
/RUN npm install -g yarn@.*/,
`RUN npm install -g yarn@${yarnVersion} --force`
);
await fs.writeFile(dockerfileProd, dockerfileProdContent, "utf8");
// Update docker-compose.yml with project name for services
const dockerCompose = path.join(process.cwd(), "docker-compose.yml");
let dockerComposeContent = await fs.readFile(dockerCompose, "utf8");
// Add container_name and image name as properties INSIDE each service
dockerComposeContent = dockerComposeContent
.replace(
/ # Development service\n ${project}-dev:/,
` # Development service\n ${project}-dev:\n container_name: ${project}-dev\n image: ${project}-dev`
)
.replace(
/ # Production service\n ${project}-prod:/,
` # Production service\n ${project}-prod:\n container_name: ${project}-prod\n image: ${project}-prod`
);
await fs.writeFile(dockerCompose, dockerComposeContent, "utf8");
updateDockerfilesSpinner.success({
text: "Docker configurations updated with detected versions and project name",
});
// Also make the start-docker.sh script executable
try {
const scriptPath = path.join(process.cwd(), "scripts", "start-docker.sh");
await fs.chmod(scriptPath, 0o755); // rwxr-xr-x permission
console.log(chalk.green("✔ Made start-docker.sh executable"));
} catch (scriptError) {
console.log(
chalk.yellow(
"Note: Couldn't make start-docker.sh executable. You may need to run: chmod +x scripts/start-docker.sh"
)
);
}
if (!noGit) {
const gitSpinner = createSpinner(
"Creating git commit for Docker setup"
).start();
try {
// Check if git is initialized
await execa("git", ["rev-parse", "--is-inside-work-tree"]);
// Stage the Docker files
await execa("git", [
"add",
"Dockerfile.dev",
"Dockerfile.prod",
"docker-compose.yml",
".dockerignore",
".env.local",
"scripts/start-docker.sh",
]);
// Create conventional commit
await execa("git", [
"commit",
"-m",
"feat(docker): add Docker infrastructure",
]);
gitSpinner.success({ text: "Created git commit for Docker setup" });
} catch (gitError) {
gitSpinner.error({
text: `Could not create git commit: ${gitError.message}`,
});
console.log(
chalk.yellow(
"Note: Git repository may not be initialized or git is not installed."
)
);
}
}
const readmeSpinner = createSpinner(
"Updating README.md with Docker instructions"
).start();
try {
const readmePath = path.join(process.cwd(), "README.md");
let readmeContent = await fs.readFile(readmePath, "utf8");
// Check if Docker instructions already exist
if (!readmeContent.includes("./scripts/start-docker.sh")) {
// Find the "Getting Started" section and add Docker instructions after the run commands
readmeContent = readmeContent.replace(
/```bash\nnpm run dev\n# or\nyarn dev\n# or\npnpm dev\n# or\nbun dev\n```/,
"```bash\nyarn dev```\n\nTo start the project using Docker (dev or prod), use the helper script:\n\n```bash\n./scripts/start-docker.sh\n```"
);
await fs.writeFile(readmePath, readmeContent, "utf8");
readmeSpinner.success({
text: "README.md updated with Docker instructions",
});
// Add README.md to git commit if not noGit
if (!noGit) {
try {
await execa("git", ["add", "README.md"]);
} catch (gitError) {
// Ignore git errors here, they will be handled in the main git section
}
}
} else {
readmeSpinner.success({
text: "README.md already contains Docker instructions",
});
}
} catch (readmeError) {
readmeSpinner.error({
text: `Could not update README.md: ${readmeError.message}`,
});
console.log(
chalk.yellow(
"Note: You may need to manually update README.md with Docker instructions"
)
);
}
} catch (error) {
console.error(
chalk.red(`Failed to set up Docker infrastructure: ${error.message}`)
);
process.exit(1);
}
console.log(chalk.gray("─".repeat(50)));
console.log(
chalk.green("\n✔ Docker infrastructure setup completed successfully")
);
};