前言
需要是:当 gitlab
我的项目,新建 tag 公布时。通过脚本主动打包成压缩包,并提供 http 下载。使得我的项目公布新版本时,用户可能通过 url 间接下载
流程图:
服务器配置
目前用于实现 http 服务的软件有许多,包含支流的 Apache、Nginx,还有微软的 IIS 等。这里应用 apache。
在学习 HTTP 协定的时候,咱们晓得 URL 实际上指定的就是服务器上特定的文件,所以应用 apche 中的 httpd 这个轻量的 HTTP server 实现一个简略的文件下载服务是再适合不过的了。
1. 装置 apt-get install apache2
若有子包抉择,则抉择 httpd
2. 启动
/etc/init.d/apache2 start
3. 查看启动状态:/etc/init.d/apache2 status
4. 尝试拜访
而后,拜访服务器的公网 ip 或域名,就能够 看到相似如下界面,此时阐明 Apache 失常工作:
若须要更改端口,能够看这篇 批改 apache2 端口
5. 下载
最初在 /var/www/html
门路下,删除 index.html
,上传本人想要被下载的文件,再次拜访,就能够进行下载了。
若不想所有人都能够通过该 url 拜访到服务器,还能够加上账号密码验证,能够参考账号密码验证
最初,能够通过 域名 / 文件名 的形式间接给他人一个链接,进行下载。
例如:xxxxx.com:port/20230214_myTestProject.zip, 拜访该 url 就能够下载。
脚本开发
首先须要借助 gitlab 的 CI/CD
GitLab CI/CD
CI(Continuous Integration),中文即继续集成。
CD(Continuous Delivery / Continuous Deployment)继续交付 或 继续布署。
官网文档
首先须要装置 gitlab-runner,官网装置文档
编写.gitlab-ci.yml
设置执行规定
workflow:
# 当有 tag 或 merge request 时执行
rules:
- if: '$CI_PIPELINE_SOURCE =="merge_request_event"|| $CI_COMMIT_TAG != null'
# 设置步骤(步骤间串行)stages:
- pack-web
命令行写法
最开始我是通过间接命令行的模式编写:
angular-pack:
tags:
- docker
stage: pack-web
# 找一个提供 node 的镜像
image: registry.cn-beijing.aliyuncs.com/mengyunzhi/node-chrome:14.16.0
variables:
# 定义压缩包名字,日期为前缀,项目名称为后缀
ZIP_NAME: "`date +%Y-%m-%d-%H-%M-%S`$CI_PROJECT_NAME"
before_script:
- cd web
script:
- env
- npm install -dd
- npm run build
- apt install zip
- zip -r $ZIP_NAME.zip dist
- apt install sshpass -y
- sshpass -p <password> scp -P 5022 -o StrictHostKeyChecking=no $ZIP_NAME.zip <username>@<Server IP>:/var/www/html
rules:
- if: $CI_COMMIT_TAG != null
- 通过 npm install 和 npm run build 打包构建,生成 dist 文件夹
- 装置 zip, 并应用 zip 命令将 dist 文件夹压缩成压缩包
- 装置 sshpass 包,使得 scp 不须要交互式输出明码。
- 应用 scp 命令,将文件夹发送到服务器上。填上服务器的 ip,ssh 端口,用户名明码,以及传输的指标地位
间接应用命令很不不便,咱们能够编写脚本
编写 node.js 脚本
1. 引入第三方模块
# 提供 ssh 连贯
const {NodeSSH} = require('node-ssh'); // 提供 ssh 连贯
const compressing = require('compressing'); // 文件压缩
const ChatBot = require('dingtalk-robot-sender'); // 提供钉钉推送
2. 初始化变量
// 初始化变量
const projectName = process.env.CI_PROJECT_NAME; // 由环境变量获取的工程名称
const appDir = `/var/www/html`; // 利用根地位
const host = process.env.HOST; // 服务器 ip
const sshPort = process.env.SSHPORT; // ssh 端口
const port = process.env.PORT; // http 下载端口
const username = process.env.USERNAME; // 服务器用户名
const password = process.env.PASSWORD; // 服务器明码
const bashUrl = "https://oapi.dingtalk.com/robot/send?access_token=";
const dingToken = process.env.DINGTOKEN; // 钉钉机器人 token
3. 连贯服务器
console.log('尝试连贯生产服务器');
const ssh = new NodeSSH();
await ssh.connect({host: `${host}`,
port: `${sshPort}`,
username: `${username}`,
password: `${password}`
});
4. 压缩文件
// 定义压缩包名字为 日期_项目名称
const d_t = new Date();
let year = d_t.getFullYear();
let month = ("0" + (d_t.getMonth() + 1)).slice(-2);
let day = ("0" + d_t.getDate()).slice(-2);
const zipName = year + month + day + "_" + projectName;
// 压缩文件,参数 1:要压缩的文件夹, 参数 2: 压缩后的名字
await compressing.zip.compressDir('dist', `${zipName}.zip`)
.then(() => {console.log('压缩胜利');
})
.catch(err => {console.error(err);
});
5. 上传到服务器
console.log('开始上传压缩包');
// 参数 1:为上传的文件名称,参数 2:服务器的门路以及文件名
await ssh.putFile(`${zipName}.zip`,`${appDir}/${zipName}.zip`)
6. 钉钉推送下载地址
await dingSendSuccess(zipName);
const dingSendSuccess = async function (zipName) {
try {
const robot = new ChatBot({webhook: bashUrl + dingToken});
let downloadUrl = host + ":" + port + "/" + zipName + ".zip";
let title = "#### 😀 😃 😄 😁 😆";
let text = "## 😀 😃 😄 😁 😆\n" +
`> ### ${projectName} 打包胜利! [下载地址](http://${downloadUrl}) \n`;
await robot.markdown(title, text, {}).then((res) => {console.log("响应信息:" + res.data);
});
} catch (e) {console.log('推送谬误', e);
} finally {process.exit(0);
}
}
下载地址为: 服务器 ip + http 端口 + 文件名
须要服务器提供 http 服务,上面说到如何配置。
7. 编写失败的钉钉推送
const dingSendError = async function (error) {
try {
const robot = new ChatBot({webhook: bashUrl + dingToken});
let title = "#### 😢 👿 😧 💔";
let text = "## 😢 👿 😧 💔\n" +
`> ### ${projectName} 打包失败! \n` +
`> #### 错误信息: ${error} \n`;
await robot.markdown(title, text, {}).then((res) => {console.log("响应信息:" + res);
});
} catch (e) {console.log('推送谬误', e);
} finally {process.exit(0);
}
}
8. 残缺代码
// 引入第三方模块
const {NodeSSH} = require('node-ssh');
const compressing = require('compressing');
const ChatBot = require('dingtalk-robot-sender');
// 初始化变量
const projectName = process.env.CI_PROJECT_NAME; // 由环境变量获取的工程名称
const appDir = `/var/www/html`; // 利用根地位
const host = process.env.HOST;
const sshPort = process.env.SSHPORT; // 下载端口
const port = process.env.PORT; // 下载端口
const username = process.env.USERNAME;
const password = process.env.PASSWORD;
const bashUrl = "https://oapi.dingtalk.com/robot/send?access_token=";
const dingToken = process.env.DINGTOKEN;
// 定义开始函数,在文件结尾调用
const start = async function () {
try {console.log('尝试连贯生产服务器');
const ssh = new NodeSSH();
await ssh.connect({host: `${host}`,
port: `${sshPort}`,
username: `${username}`,
password: `${password}`
});
const d_t = new Date();
let year = d_t.getFullYear();
let month = ("0" + (d_t.getMonth() + 1)).slice(-2);
let day = ("0" + d_t.getDate()).slice(-2);
const zipName = year + month + day + "_" + projectName;
await compressing.zip.compressDir('dist', `${zipName}.zip`)
.then(() => {console.log('压缩胜利');
})
.catch(err => {console.error(err);
});
console.log('开始上传压缩包');
await ssh.putFile(`${zipName}.zip`,
`${appDir}/${zipName}.zip`)
await dingSendSuccess(zipName);
} catch (e) {console.log('打包产生谬误', e);
await dingSendError(e.message);
} finally {process.exit(0);
}
}
const dingSendSuccess = async function (zipName) {
try {
const robot = new ChatBot({webhook: bashUrl + dingToken});
let downloadUrl = host + ":" + port + "/" + zipName + ".zip";
let title = "#### 😀 😃 😄 😁 😆";
let text = "## 😀 😃 😄 😁 😆\n" +
`> ### ${projectName} 打包胜利! [下载地址](http://${downloadUrl}) \n`;
await robot.markdown(title, text, {}).then((res) => {console.log("响应信息:" + res.data);
});
} catch (e) {console.log('推送谬误', e);
} finally {process.exit(0);
}
}
const dingSendError = async function (error) {
try {
const robot = new ChatBot({webhook: bashUrl + dingToken});
let title = "#### 😢 👿 😧 💔";
let text = "## 😢 👿 😧 💔\n" +
`> ### ${projectName} 打包失败! \n` +
`> #### 错误信息: ${error} \n`;
await robot.markdown(title, text, {}).then((res) => {console.log("响应信息:" + res);
});
} catch (e) {console.log('推送谬误', e);
} finally {process.exit(0);
}
}
start().then().catch(function (error) {console.log('产生谬误', error);
process.exit(1);
});
9. 效果图
期间遇到的问题
1. 文件名过长,无奈失常压缩
最开始文件名设置的是,年月日_时分秒_我的项目名字。
例如: 2023-2-13_16:55_myTestProject.zip
然而如图右边红色文件所示,文件名不全,而且后缀没了↑。
通过 console.log(zipName);
,却发现打印失常。最初归纳起因为 第三方包的问题。
解决: 缩小压缩包的名称字符
改为如下:年月日_我的项目名字
20230214__myTestProject.zip
2. 未提供文件名
过后找了很久,因为 报错信息并没有很好地显示问题。只报了 failure。
之后去看了报错的地位,尽管能晓得是 2699 行的起因,然而 代码曾经太底层了,剖析不出起因
最初通过比照网上的用法:
发现是因为:没有指出文件的名称,只指出了文件门路
如图,红框中的字段过后没有写。写上后报错隐没。
教训:理解好参数的含意
3.http 包的抉择
做到钉钉推送的时候,原本想用 request 包来发送申请
而后例如下载如下:
const request = require('request');
request('http://www.google.com', function (error, response, body) {console.error('error:', error); // Print the error if one occurred
console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
console.log('body:', body); // Print the HTML for the Google homepage.
});
然而发现:request 依赖包被弃用了
官网介绍说,2020 年 2 月 11 日,npm 依赖包就齐全被弃用了(即不会有任何新的扭转了);
代替办法找到了几个比拟举荐的:
- Axios,Axios 有更多的日 / 周 / 月下载量,github 上取得更多星;更多的 followers 和更多的 fork,反对在 browser 上应用,但进度展现仅反对在浏览器上显示
- Got,有更多的版本,更频繁的更新
- Node.js 规范库里的 HTTP 和 HTTPS 模块,无需装置内部软件包,然而同样地也会带来弊病就是应用起来并不敌对,会繁琐很多。
第一种用的人比拟多,比拟举荐,
然而仅仅为了钉钉推送的话,有个更不便的包 dingtalk-robot-sender
:文档
应用也很不便
let content = '我就是我, 是不一样的烟火';
let at = {
"atMobiles": [
"156xxxx8827",
"189xxxx8325"
],
"isAtAll": false
};
// 疾速发送文本音讯
robot.text(content, at);