前言

需要是:当 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
  1. 通过npm install 和 npm run build 打包构建,生成dist文件夹
  2. 装置zip, 并应用zip命令将dist文件夹压缩成压缩包
  3. 装置sshpass包,使得scp不须要交互式输出明码。
  4. 应用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;        // 服务器ipconst 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);