oss版本管理——ossVersion

313 阅读5分钟

在我们的日常开发中,前端打包后的静态资源都放在哪里呢?有没有放到oss上的呢? 我们在做工业品项目的时候,前端打包后的静态资源存放在oss上,文件夹带版本号,这样每次打包上传不同的版本,后端去抓到最新版本的静态资源作部署测试,上线的时候选择最新版本的文件部署。

听着还不错,那么会遇到这两个问题:

  1. 因为涉及到多人开发项目,每次需要手动更改版本号再上传。
  2. 如果有人打包后没有提交package.json文件,那么其他人在打包上传的时候会出现版本不正确,覆盖的情况。 带着问题我们就要着手去解决,以我们的PC端的项目为例,采用的脚手架为codmi(npm.m.jd.com/package/@jd…

目前我们的打包上传的流程是:

standard-version && codmi build && codmi upload

standard-version: 是可以本地更新版本号,增加changelog和创建 git tags,我们暂且使用本地版本号更改递增。 当standard-version改变 package.json文件的 version字段,为最新的版本号,之后执行codmi build,这里在项目的根目录的codmi.config.js配置文件里面设置webpack打包配置,读取到最新的版本: require('./package.json').version, 然后执行codmi upload上传操作。 这就是遇到开头说的问题,多人开发项目,版本容易错乱。所以在执行standard-version之前我们 oss上拿到最新的版本然后再去改变package.json文件。所以就着手开始npm报的开发吧....

  1. 首先我们先来看一下在oss上我们保存的前端静态资源目录是怎样的:

oss

在这层目录可以看到,在文件夹ispText下面有很多版本文件夹,在这层之前就是bucket空间,所以我们需要知道oss提供的Api 调取当前目录下所有的文件目录,然后进行比较最大的版本出来。先来看下oss的Api,拿到我们需要的方法。

1.bucket.listObject -- 列出 指定Bucket中的Object信息列表 请求时可以通过一些查询条件来限制返回的结果。

{ Name: 'pro-test',
 Prefix: null,
 Marker: null,
 Delimiter: null,
 MaxKeys: 2,
 HasNext: true,
 Contents:
  [ { Key: 'pro-a.txt',
      LastModified: 'Mon, 11 Sep 2017 17:47:04 GMT',
      ETag: '0cc175b9c0f1b6a831c399e269772661',
      Size: 1 },
    { Key: 'pro-b.txt',
      LastModified: 'Mon, 11 Sep 2017 17:47:15 GMT',
      ETag: '92eb5ffee6ae2fec3ad71c777531578f',
      Size: 1 } ],
 CommonPrefixes: null }

这是方法返回的结果,Contents就是返回的扁平化数据,这个后面会有讲解。

注意:开始之前,我们还是把npm包放到jnpm上(npm.jd.com/)京东的npm包托管。… npm init 初始化,npm publish`发包,这里就不多赘述,不过在遇到 发包失败,状态码为403的时候,先看看自己的erp是否正常登陆,还有是不是被锁住了,如果锁住了就重新在erp.jd.com重新登录下。

然后我们再看下npm包的结构:

oss

bin文件夹下的cli.js为入口文件:

#!/usr/bin/env node

const ossVersion = require("../oss.js");
var argv = require("yargs").argv;
ossVersion(argv);

/usr/bin/env就是告诉系统可以在PATH目录中查找。 所以配置#!/usr/bin/env node, 就是解决了不同的用户node路径不同的问题,可以让系统动态的去查找node来执行你的脚本文件。

shelljs 只解决了如何调用 shell 命令,而 yargs 模块能够解决如何处理命令行参数。

npm install --save yargs

argv就是在执行命令的会后拿到的参数,这里我们后面会用到,因为可以传参版本号来之定最新版本。

进入主体内容,在根目录的oss.js文件,导出ossVersion方法,参数为argv,这里面存着执行命令带过来的参数。这里我们在头部初始化所需要的变量和方法。

module.exports = async function ossVersion(argv) {
 let directory = ""; //指定目录
  let versionArr = []; //读取全部目录的版本
  let __setting = {}; //保存ossVersion配置文件参数
  //添加原型方法去重
  Array.prototype.pushNoRepeat = function () {
    for (var i = 0; i < arguments.length; i++) {
      var ele = arguments[i];
      if (this.indexOf(ele) == -1) {
        this.push(ele);
      }
    }
  };
  Array.prototype.max = function () {
    return Math.max.apply({}, this);
  };
  Array.prototype.min = function () {
    return Math.min.apply({}, this);
  };
}

然后,我们是需要比较目录最新版本的,那在这之前我们需要获取到当前的全部目录,在oss上需要实例化new Jss()方法。

new Jss(endpoint, accessKey, secretKey, useHttps)
  • endpoint 所要连接的 endpoint 地址
  • accessKey 字符串 accessKey
  • secretKey 字符串 secretKey
  • useHttps string 是否使用 https

我们会在项目的根目录存放json格式配置文件,存放关于 oss bucket等信息。所以默认需要把这个配置文件的参数取出来再实例化。

//读取ossVersion.json 配置文件
let readOssVersion = () => {
let _setting = fs.readFileSync("./ossVersion.json", "utf-8");
   return JSON.parse(_setting);
};
 __setting = readOssVersion();

//初始化Jss   oss api实例
var jss = new Jss( __setting.endpoint , __setting.accessKey ,__setting.secretKey,__setting.useHttps);

bucket.listObject就是真正取目录结构的方法,需要传几个参数:

jss.bucket(bucketName)
.listObject(marker, maxKeys, prefix, delimiter)
.then((res) => {
}
  • marker 指定的Object的Key的起始标志
  • MaxKeys 指定的Object的数量
  • Prefix 指定的Object Key的前缀
  • delimiter 指定的Delimiter分组符

这里注意一点:marker参数指的是在查询的时候开始的位置,每次查询数量最大会返回1000条,那么如果几千条的话,当前返回结果的最后一个就是下次开始的marker,前提是返回的数据HasNext为true时才会出现。所以这里需要处理一下:

let marker = "";
if (HasNext && JSON.parse(res).Contents.length > 2) {
  marker = JSON.parse(res).Contents[JSON.parse(res).Contents.length - 1].Key;
}

然后请求到的数据可以先存到一个数组里面,然后我们再去循环数组找出版本最大的一个:

所有版本存储到versionArr,循环找出最大值,然后递增,修改package.json文件的参数。
JSON.parse(res).Contents.forEach((item, index) => {
   if (item.Key.split("/")[0] == directory) {
     versionArr.pushNoRepeat(item.Key.split("/")[1]);
   }
});
let arr = versionArr.sort(function (a, b) {
  var sources = a.split(".");
  var dests = b.split(".");
  var maxL = Math.max(sources.length, dests.length);
  var result = 0;
  for (let i = 0; i < maxL; i++) {
     ......
  }
  return result;(-1 or 1)
}

找到最新的版本,就需要修改package.json 文件的参数,这里我们是结合standard-version来做的,所以我们只需要把最新的版本给到standard-version的prebump字段,后面的继续打包上传。

定义writePackageJson方法:

function writePackageJson(cbDataPackage, wholeVersion) {
    cbDataPackage["standard-version"]["scripts"][
      "prebump"
    ] = `echo ${wholeVersion}`;
    fs.writeFile( "./package.json", JSON.stringify(cbDataPackage, "", "\t"), function (err) {
        if (err) console.error(err);
        console.log( "package.json文件修改完成,prebump修改为:",cbDataPackage["standard-version"]["scripts"]["prebump"]
        );
      }
    );
  }

这样的话,主体的逻辑就已经实现了,下面贴一张整体的流程图。

配置文件

我们可以在项目的根目录放上一个配置文件ossVersion.json,里面放上oss相关的参数。

{
  "endpoint": "storage.jd.local",
  "accessKey": "rm9RhqAtor1UbGMR",
  "secretKey": "YwYKZZgvCTgKQUYgBZyWGDYFrflEeIAdabV3OJLs",
  "useHttps": "false",
  "bucket": "safequkuaifang-musiclib",
  "folderName": "ispText",
  "directoryType": "ispText"
}

命令

package.json 文件中 script 增加执行命令 -d 指定名录名称 -v 指定版本(需要手动设置版本)。script 脚本命令行使用:

"start": "codmi start",
"build": "codmi build",
"upload": "codmi upload",
"prepub": "ossVersion && standard-version && codmi build && codmi upload",
"preenv": "ossVersion --d=ispText",

以自己的项目为例:

prepub命令在原来的基础上增加了ossVersion命令,默认会拉去最新的版本号,在执行后面的操作。

preenv则可以针对自己选择要指定的版本号进行创建

codmi配置文件的修改

我们项目使用的是codmi的脚手架,配置文件里面有用到ossUpload插件,就可以和版本控制供给结合使用了。需要注意的一点,在配置文件中,ossUpload的配置需要使用 空间文件夹目录字段,此时,需要动态从我们根目录创建的ossVersion.json中获取,这样的话修改了配置文件也可以指定目录。

folderName = `${ossVersion.directoryType}/${version}`;

在我们的日常开发中总会遇到个各种的难点和困难,那就克服,并且找到好的方法去解决。这次的功能介绍就到这里吧,欢迎各位来交流沟通。