UNPKG

fool-deploy

Version:

a fast deploy your web project in linux server

301 lines (286 loc) 9.95 kB
#!/usr/bin/env node "use strict"; 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();