# 1、自动部署git项目

/*
 * 快速初始化本地仓库,并且推送到远程仓库
 * @Author: lxx
 * @Date: 2024-11-21 15:50:32
 * @Last Modified by: lxx
 * @Last Modified time: 2024-11-23 10:18:55
 */

const { exec } = require("child_process");
const readline = require("readline");
const fs = require("fs");
const path = require("path");

const gitUrl = `http://192.168.xx.xx:5001`;
const gitGroup = `h5`;

let fileName = `com.line.light.frcpbd`;
let commitTx = `feat init`;

// 定义目标目录
const targetDir = path.join(__dirname, fileName);

// 创建 readline 接口用于获取用户输入
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
});

// 定义执行 Git 命令的函数
function executeCommand(command) {
    return new Promise((resolve, reject) => {
        exec(command, (error, stdout, stderr) => {
            if (error) {
                reject(`执行命令时出错: ${error.message}`);
                return;
            }
            resolve(stdout);
        });
    });
}

// 主函数
async function main() {
    // 检查目标目录是否存在
    if (fs.existsSync(targetDir)) {
        // 改变当前工作目录
        try {
            process.chdir(targetDir);
            console.log(`当前工作目录已更改为: ${process.cwd()}`);
            try {
                // 初始化 Git 仓库
                console.log("初始化 Git 仓库...");
                await executeCommand("git init");

                // 添加远程仓库
                console.log("添加远程仓库...");
                const remoteUrl = `${gitUrl}/${gitGroup}/${fileName}.git`;
                await executeCommand(`git remote add origin ${remoteUrl}`);

                // 获取提交信息
                rl.question("请输入提交信息: ", async (commitMessage) => {
                    try {
                        // 添加所有文件到暂存区
                        console.log("添加所有文件到暂存区...");
                        await executeCommand("git add .");

                        // 提交代码
                        console.log("提交代码...");
                        await executeCommand(
                            `git commit -m "${commitMessage}"`
                        );

                        // 推送到远程仓库
                        console.log("推送到远程仓库...");
                        await executeCommand("git push -u origin master");

                        console.log(`${fileName} 代码提交完成!`);
                    } catch (error) {
                        console.error(`提交或推送时出错: ${error}`);
                    } finally {
                        rl.close();
                    }
                });
            } catch (error) {
                console.error(`初始化 Git 仓库时出错: ${error}`);
            }
        } catch (err) {
            console.error(`无法改变工作目录: ${err}`);
        }
    } else {
        console.error("目标目录不存在");
    }
}

// 执行主函数
main();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96

# 2、快速复制文件夹,替换指定内容,并且执行打包命令

/*
 * 快速复制文件夹,并且打包
 * @Author: lxx
 * @Date: 2024-09-19 14:54:49
 * @Last Modified by: lxx
 * @Last Modified time: 2024-11-21 15:51:13
 */

const fs = require("fs").promises;
const path = require("path");
const { spawn } = require("child_process");

/**
 * 修改list即可
 * @param sourceName 源model
 * @param targetName 目标model
 */
const list = [
  {
    sourceName: "com.honyar.switch.sw3a01",
    targetName: "com.tnv.switch.sw3e01",
  },
  // {
  //   sourceName: "com.honyar.switch.sw1a01",
  //   targetName: "com.haoyi.switch.sw1a01",
  // },
];

const commandList = list.map((item) => `npm run publish ${item.targetName}`);

// 复制文件夹
async function copyFolder(src, dest) {
  try {
    // 创建目标文件夹
    await fs.mkdir(dest, { recursive: true });

    // 读取源文件夹中的文件和文件夹
    const items = await fs.readdir(src);

    for (const item of items) {
      const srcPath = path.join(src, item);
      const destPath = path.join(dest, item);
      const stat = await fs.stat(srcPath);

      if (stat.isDirectory()) {
        // 如果是文件夹,递归调用 copyFolder
        await copyFolder(srcPath, destPath);
      } else {
        // 如果是文件,复制文件
        await fs.copyFile(srcPath, destPath);
      }
    }

    // console.log(`复制完成: ${src} 到 ${dest}`);
  } catch (error) {
    console.error(`复制失败: ${error.message}`);
  }
}

// 替换文件的内容
async function replaceInFile(filePath, target, replacement) {
  try {
    // 读取文件内容
    let data = await fs.readFile(filePath, "utf8");

    // 替换目标字符
    const updatedData = data.replace(new RegExp(target, "g"), replacement);

    // 将更新后的内容写回文件
    await fs.writeFile(filePath, updatedData, "utf8");

    // console.log(`成功将 "${target}" 替换为 "${replacement}"`);
  } catch (error) {
    console.error(`文件内容替换失败: ${error.message}`);
  }
}

(async () => {
  for (let i = 0; i < list.length; i++) {
    const sourceName = list[i].sourceName;
    const targetName = list[i].targetName;

    const sourceFolder = `./projects/${sourceName}`; // 源文件夹路径
    const destinationFolder = `./projects/${targetName}`; // 目标文件夹路径

    const filePath = `./projects/${targetName}/project.json`; // 指定文件路径
    const sourceStr = sourceName; // 要替换的字符
    const newStr = targetName; // 替换成的新字符

    const filePath2 = `./projects/${targetName}/package.json`; // 指定文件路径
    const sourceStr2 = sourceName.replace(/\./g, "-");
    const newStr2 = targetName.replace(/\./g, "-");

    await copyFolder(sourceFolder, destinationFolder);

    await replaceInFile(filePath, sourceStr, newStr);

    await replaceInFile(filePath2, sourceStr2, newStr2);

    const child = spawn(commandList[i], { shell: true });

    child.stdout.on("data", (data) => {
      if (data.includes("eslint fix report")) {
        console.log(`${targetName} 没有error`);
      }
    });

    child.stderr.on("data", (data) => {
      console.log(`warn: ${data}`);
    });

    child.on("close", (code) => {
      console.log(`${targetName} 打包完成,代码: ${code}`);
    });
  }
})();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117

# 3、前端项目自动化部署

  • 所用核心库:scp2
/* eslint-disable */
// 引入scp2
var client = require('scp2');
// 下面三个插件是部署的时候控制台美化所用 可有可无
const ora = require('ora');
const chalk = require('chalk');
const spinner = ora(chalk.green('正在发布到正式服务器...'));
spinner.start();
const projectName = 'gmevue';
client.scp(
  './dist/',
  {
    // 本地打包文件的位置
    host: '', // 服务器的IP地址
    port: '22', // 服务器端口, 一般为 22
    username: 'root', // 用户名
    password: '', // 密码
    path: '/usr/share/nginx/html/' + projectName, // 项目部署的服务器目标位置
  },
  (err) => {
    spinner.stop();
    if (!err) {
      console.log(chalk.green('项目部署完毕!'));
    } else {
      console.log(chalk.red('发布失败!'));
      console.log('err', err);
    }
  }
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  • 备份+部署(利用ssh2执行命令)
let Client = require('ssh2').Client;
const chalk = require('chalk');
const conn = new Client();
const ora = require('ora');
const scpClient = require('scp2');
const serverConfig = {
    dev: {
        id: 'dev',
        name: '测试环境',
        host: '', // 服务器的IP地址
        port: '22', // 服务器端口, 一般为 22
        username: 'root', // 用户名
        password: '', // 密码
        path: `/usr/share/nginx/html`, // 项目部署的服务器目标位置
    },
    prod: {
        id: 'preview',
        name: '预发布环境',
        host: '', // 服务器的IP地址
        port: '22', // 服务器端口, 一般为 22
        username: 'root', // 用户名
        password: '', // 密码
        path: `/usr/share/nginx/html`, // 项目部署的服务器目标位置
    }
};
const curConfig = serverConfig['dev'] // 当前配置
const fileName = `abc` // 文件夹名字
// 备份文件
function backupsFile() {
    const { host, port, username, password, path } = curConfig
    const cmd = `
        cd ${path}\n
        cp -r ${fileName} ./${fileName}_${new Date().toLocaleDateString()}
    `;
    conn.on('ready', function () {
        conn.exec(cmd,
            function (err, stream) {
                console.log(chalk.green(`已备份文件 ${fileName}`));
                if (err) throw err;
                stream.on('close', function (code, signal) {
                    uploadFile()
                    conn.end();
                })
            });
    })
        .on('error', function (err) {
            console.log(chalk.red('Fail! 服务器连接失败.\n'));
            throw err;
        })
        .connect({
            host,
            port,
            username,
            password
        });
}
// 上传文件
function uploadFile() {
    const { host, port, username, password, path, name } = curConfig
    const spinner = ora(
        chalk.green(`正在发布到 ${name} 项目名为${fileName}...`)
    );
    spinner.start();
    scpClient.scp(
        `../dist/${fileName}`,
        {
            // 本地打包文件的位置
            host,
            port,
            username,
            password,
            path: `${path}/${fileName}`, // 项目部署的服务器目标位置
        },
        (err) => {
            spinner.stop();
            if (!err) {
                console.log(chalk.green('项目发布完毕!'));
            } else {
                console.log(chalk.red(`项目发布失败!`));
                console.log('err', err);
            }
        }
    );
}
/**
 * @param {Boolean} isBackupsFile 是否备份文件
 */
function main(isBackupsFile = true) {
    isBackupsFile ? backupsFile() : uploadFile()
}
main()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91

# 4、两个远程仓库进行同步

const { exec } = require("child_process");
const fs = require("fs");
const path = require("path");
const https = require("https");

const gitUrl = "https://gitee.com";
const gitOrg = "you_org_name";

// 给定的目录数组,当前目录下,需要同步的目录名
const directoryArray = [
    "fishtanklight",
];

// 定义执行 Git 命令的函数
function executeCommand(command) {
    return new Promise((resolve, reject) => {
        exec(command, (error, stdout, stderr) => {
            if (error) {
                reject(`执行命令时出错: ${error.message}`);
                return;
            }
            resolve(stdout);
        });
    });
}

// 判断是否为git仓库
async function isGitRepo(dir) {
    try {
        // 方法1:检查 .git 目录是否存在
        const gitDir = path.join(dir, ".git");
        if (fs.existsSync(gitDir)) {
            return true;
        }

        // 方法2:执行 git 命令检测(更准确)
        const { stdout } = await executeCommand(
            "git rev-parse --is-inside-work-tree",
            { cwd: dir }
        );
        return stdout.trim() === "true";
    } catch (error) {
        return false; // 不是 Git 仓库
    }
}

// 主函数
async function main() {
    for (const item of directoryArray) {
        const targetDir = path.join(__dirname, item);

        try {
            process.chdir(targetDir);
            console.log(`当前工作目录已更改为: ${process.cwd()}`);

            await createStash(item);

            const remoteUrl = `${gitUrl}/${gitOrg}/${item}.git`;

            try {
                // 检查远程仓库是否已存在
                const remotes = await executeCommand("git remote -v");
                const remoteExists = remotes.includes(`secGit\t${remoteUrl}`);

                if (!remoteExists) {
                    console.log("添加远程仓库...", remoteUrl);
                    await executeCommand(`git remote add secGit ${remoteUrl}`);
                }

                await executeCommand("git push secGit master");

                console.log(`${targetDir} 代码已同步到 secGit`);
            } catch (error) {
                console.error(`Git 操作出错: ${error}`);
            }
        } catch (err) {
            console.error(`无法改变工作目录: ${err}`);
        }
    }
}

async function createStash(name) {
    return await new Promise((resolve, reject) => {
        // 目标服务器的URL和端口
        const options = {
            hostname: "gitee.com",
            port: 443,
            path: `/api/v5/orgs/${gitOrg}/repos`,
            method: "POST",
            headers: {
                "Content-Type": "application/json", // 或者根据需要设置其他类型,如 'application/x-www-form-urlencoded'
            },
        };

        // 要发送的数据
        const postData = JSON.stringify({
            name: name,
            access_token: "access_token",
        });

        // 创建HTTPS请求
        const req = https.request(options, (res) => {
            console.log(`状态码: ${res.statusCode}`);

            res.on("data", (d) => {
                process.stdout.write(d);
            });

            res.on("end", () => {
                resolve("end");
            });
        });

        // 发送请求数据
        req.write(postData);

        // 处理请求错误
        req.on("error", (e) => {
            console.error(`请求遇到问题: ${e.message}`);
        });

        // 结束请求
        req.end();
    });
}

// 执行主函数
main();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
lastUpdate: 4/18/2025, 2:05:53 PM