fool-deploy
Version:
a fast deploy your web project in linux server
301 lines (286 loc) • 9.95 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
// src/index.ts
var import_fs3 = __toESM(require("fs"));
var import_shelljs3 = __toESM(require("shelljs"));
// src/lib/generateHash.ts
var import_crypto = __toESM(require("crypto"));
function generateHash(options2) {
const md5 = import_crypto.default.createHash("md5");
const str = md5.update(JSON.stringify(options2)).digest("hex");
return str;
}
var generateHash_default = generateHash;
// src/lib/generateOptions.ts
var import_fs = __toESM(require("fs"));
// src/lib/getPackageCommand.ts
function getPackageCommand() {
const managerInfo = process.env.npm_config_user_agent;
if (managerInfo == null ? void 0 : managerInfo.includes("yarn"))
return "yarn";
if (managerInfo == null ? void 0 : managerInfo.includes("pnpm"))
return "pnpm";
return "npm";
}
var getPackageCommand_default = getPackageCommand;
// src/lib/getValidPort.ts
var import_net = __toESM(require("net"));
function getValidPort(port) {
return new Promise(async (resolve) => {
let _port = port;
while (_port) {
const result = await probePort(_port);
if (result)
return resolve(_port);
_port++;
}
});
}
function probePort(port) {
const server = import_net.default.createServer().listen(port);
return new Promise(function(resolve) {
server.on("listening", function() {
server && server.close();
resolve(true);
});
server.on("error", function(err) {
resolve(err.code !== "EADDRINUSE");
});
});
}
var getValidPort_default = getValidPort;
// src/lib/generateOptions.ts
var options = {
cache: true,
port: 2333,
nodeVersion: "18.14-alpine",
nginxVersion: "1.22.1",
imageName: "fool-deploy:prod",
output: "dist"
};
async function generateOptions() {
const cwdPath = process.cwd();
const pkgPath = `${cwdPath}/package.json`;
if (import_fs.default.existsSync(pkgPath)) {
const str = import_fs.default.readFileSync(pkgPath).toString("utf-8");
const { name = "fool-deploy" } = JSON.parse(str);
options.imageName = `${name}:prod`;
} else {
throw new Error(
`Error: ${cwdPath} not found package.json\uFF0Cshell must running in projcet root dir.`
);
}
const jsonPath = `${cwdPath}/.foolrc`;
if (import_fs.default.existsSync(jsonPath)) {
const str = import_fs.default.readFileSync(jsonPath).toString("utf-8");
const externalOptions = JSON.parse(str);
options = { ...options, ...externalOptions };
}
const { imageName, packageCommand, buildCommand } = options;
if (imageName.split(":").length < 2) {
options.imageName = `${imageName}:prod`;
}
if (!packageCommand) {
options.packageCommand = getPackageCommand_default();
}
if (!buildCommand) {
const { packageCommand: packageCommand2 } = options;
options.buildCommand = packageCommand2 === "npm" ? `${packageCommand2} run build` : `${packageCommand2} build`;
}
options.port = await getValidPort_default(options.port);
return options;
}
var generateOptions_default = generateOptions;
// src/lib/isInstallDocker.ts
var import_shelljs = __toESM(require("shelljs"));
function isInstallDocker() {
return import_shelljs.default.exec("docker version").code === 0;
}
var isInstallDocker_default = isInstallDocker;
// src/lib/generateDockerfileContent.ts
var import_fs2 = __toESM(require("fs"));
function checkExist(filename) {
return import_fs2.default.existsSync(filename);
}
function generateDockerfileContent(options2, dirPath) {
const { nodeVersion, nginxVersion, packageCommand, output, buildCommand } = options2;
let deps = "";
let builder = "";
if (packageCommand === "pnpm") {
const existNpmrc = checkExist(".npmrc");
const existPnpmLock = checkExist("pnpm-lock.yaml");
deps = `
RUN npm install -g pnpm
COPY ${existPnpmLock ? "pnpm-lock.yaml" : ""} ${existNpmrc ? ".npmrc" : ""} ./
${existPnpmLock ? "RUN pnpm fetch" : ""}
COPY package.json ./
RUN pnpm install --frozen-lockfile --offline --ignore-scripts
`;
builder = `
RUN npm install -g pnpm
COPY --from=deps /workspace/node_modules ./node_modules
COPY . .
RUN ${buildCommand}
`;
}
if (packageCommand === "npm") {
const existNpmrc = checkExist(".npmrc");
const existPkgLock = checkExist("package-lock.json");
deps = `
COPY ${existPkgLock ? "package-lock.json" : ""} ${existNpmrc ? ".npmrc" : ""} package.json ./
RUN ${existPkgLock ? "npm ci" : "npm install"}
`;
builder = `
COPY --from=deps /workspace/node_modules ./node_modules
COPY . .
RUN ${buildCommand}
`;
}
if (packageCommand === "yarn") {
const existYarnrc = checkExist(".yarnrc");
const existYarnLock = checkExist("yarn.lock");
deps = `
COPY ${existYarnLock ? "yarn.lock" : ""} ${existYarnrc ? ".yarnrc" : ""} package.json ./
RUN ${existYarnLock ? "yarn install --frozen-lockfile" : "yarn install"}
`;
builder = `
COPY --from=deps /workspace/node_modules ./node_modules
COPY . .
RUN ${buildCommand}
`;
}
if (!deps || !builder) {
throw TypeError("TypeError: deps and builder cannot be null.");
}
const str = `
FROM node:${nodeVersion} as deps
WORKDIR /workspace
${deps}
FROM node:${nodeVersion} as builder
WORKDIR /workspace
${builder}
FROM nginx:${nginxVersion} as runer
WORKDIR /
COPY --from=builder /workspace/${output}/ /usr/share/nginx/html/
RUN rm /etc/nginx/conf.d/default.conf
COPY --from=builder /workspace/${dirPath}/nginx.conf /etc/nginx/conf.d/
`;
return str;
}
var generateDockerfileContent_default = generateDockerfileContent;
// src/config.ts
var config_default = {
cacheDir: ".fool-cache",
tempDir: ".fool-temp"
};
// src/lib/run.ts
var import_shelljs2 = __toESM(require("shelljs"));
function shellExec(result, msg) {
const { code } = result;
if (code !== 0) {
import_shelljs2.default.echo(`Error: ${msg}`);
import_shelljs2.default.exit(1);
}
}
function run(options2, filePath) {
const { imageName, port, cache } = options2;
shellExec(
import_shelljs2.default.exec(`docker rmi -f ${imageName}`),
`docker delete image ${imageName} failed.`
);
shellExec(
import_shelljs2.default.exec(`docker build --pull -t ${imageName} -f ${filePath} .`),
`docker build image ${imageName} failed.`
);
const [name] = imageName.split(":");
shellExec(
import_shelljs2.default.exec(`docker rm -f ${name}`),
`docker delete container ${name} failed.`
);
shellExec(
import_shelljs2.default.exec(
`docker run --name ${name} --restart=always -d -p ${port}:80 ${imageName}`
),
`docker run container ${name} failed.`
);
if (!cache)
import_shelljs2.default.rm("-rf", config_default.tempDir);
console.log(
`happy landing. deploy successful. running port is ${options2.port}.`
);
}
var run_default = run;
// src/lib/generateNginxContent.ts
function generateNginxContent() {
return `
server {
listen 80;
root /usr/share/nginx/html;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
`;
}
var generateNginxContent_default = generateNginxContent;
// src/index.ts
function create(options2, hash) {
const dirPath = options2.cache ? config_default.cacheDir : config_default.tempDir;
import_shelljs3.default.rm("-rf", config_default.cacheDir);
import_shelljs3.default.rm("-rf", config_default.tempDir);
import_fs3.default.mkdirSync(dirPath);
const nginxContent = generateNginxContent_default();
import_fs3.default.writeFileSync(`${dirPath}/nginx.conf`, nginxContent);
options2.cache && import_fs3.default.writeFileSync(`${dirPath}/.hash`, hash);
const dockerfileConent = generateDockerfileContent_default(options2, dirPath);
import_fs3.default.writeFileSync(`${dirPath}/Dockerfile`, dockerfileConent);
run_default(options2, `${dirPath}/Dockerfile`);
}
async function init() {
if (!isInstallDocker_default()) {
throw Error(
`Error: current environment not found docker, if you don't install docker, please see https://docs.docker.com/get-docker/ download docker.`
);
}
const options2 = await generateOptions_default();
const { cache } = options2;
if (!cache) {
return create(options2);
}
const curHash = generateHash_default(options2);
const hashFilePath = `${config_default.cacheDir}/.hash`;
if (!import_fs3.default.existsSync(config_default.cacheDir)) {
return create(options2, curHash);
}
const prevHash = import_fs3.default.existsSync(hashFilePath) ? import_fs3.default.readFileSync(hashFilePath).toString("utf-8") : "";
curHash === prevHash ? run_default(options2, `${config_default.cacheDir}/Dockerfile`) : create(options2, curHash);
}
init();