乐趣区

关于前端:初级FE也能懂前端架构师如何设计并开发一个脚手架解决日常CICD问题的上

大家好,我是一只不守妇道的花喵。

本文你能够学到:

  • 相熟 cli 开发的全流程,能够开发出相似 @babel/core@babel/preset-env 的 npm 包(是不是很装逼)
  • 相熟 npm 公布流程
  • 相熟 lerna 开发 cli 工具,疾速入门lerna
  • 理解架构师如何残缺设计、开发一个脚手架
  • 如何利用脚手架高效解决日常开发中的 CI/CD 流程

注:本文老衲在 mac 下测试,应用 win 的同学目录本人改下哈

1. 筹备

为什么要开发脚手架(像个架构师一样思考)

这就须要说到CI/CD

工厂里的装配线以疾速、自动化、可反复的形式从原材料生产出消费品。同样,软件交付管道以疾速、自动化和可反复的形式从源代码生成公布版本。如何实现这项工作的总体设计称为“继续交付”(CD)。启动装配线的过程称为“继续集成”(CI)。确保品质的过程称为“继续测试”,将最终产品提供给用户的过程称为“继续部署”。一些专家让这所有简略、顺畅、高效地运行,这些人被称为运维开发(DevOps)践行者。[1]

解决的问题:

传统(事实上大一点点的公司都会思考 CI/CD 自动化)手动操作低效、出错率高、依赖某些特定人员等。尤其在我的项目越来越大的时候,这些问题将更为严重,更不必谈“灰度公布”,这些点有一个就有足够的理由接着看本文。作为前端架构师就是要解决各种“效力”问题。

解决的场景:

  • 对立 CI/CD 标准
  • 进步 CI/CD 效率
  • 缩小 CI/CD 出错率
  • 不依赖任何人,按流程走
  • ……

前置常识

  • 相熟js,理解nodejs
  • 理解过一种或几种命令行工具,如:vue-clivitecreate-react-appgit
  • 理解 npmjs 注册、登录、公布等流程(上面有起码常识教程)

需要

  • 分包,一个脚手架如果较为简单就必然波及分包,分包能够无效升高工程复杂度、进步开发 / 保护效率、并且每个分包绝对独立,能够独自应用,babel就有将近 150 的包,点击查看,相互依赖,设想下你怎么治理这么多包?每次更新版本、装置依赖怎么操作?分包的依赖降级又要怎么整?
  • 命令 + 子命令,日常用的 gitvue-cli 都有子命令,脚手架如果足够简单也会波及子命令
  • 日志
  • 命令行交互
  • 网络申请
  • 本地文件解决
  • ……

注:咱们做一个我的项目的需要调研到技术选型,不会只思考当下须要什么,都会再需要下“多思考一步”,会思考其扩展性,如下面的分包、命令 + 子命令。当然如果的确是一个很小的脚手架,是能够不思考分包以升高设计的复杂度(当然相熟了 lerna 没啥复杂度),比如说只有一两个功能模块。然而如果像 vue-clibabel,波及的模块 / 插件几十上百个,那就必须要思考。作为预留,咱们这边应用能分包(Multirepo)的扩大计划。

Monorepo vs Multirepo

选型

  • 分包,lerna,业界闻名的 Multirepo 计划解决方案
  • 命令 + 子命令,commanderyargs
  • 日志,npmlogcolors
  • 命令行交互
  • 网络申请
  • 本地文件解决,fs-extra

其余

  • 版本比拟:semver
  • 解析参数选项:minimist
  • ……
    <!–
  • .env 文件中加载环境变量:dotenv
  • npm 装置:npminstall
  • root 权限查看:root-check
  • 获取用户主目录的门路:user-home
    –>

2. 开始

2.1. 初始化

  • 创立 mkdir ~/huamiao-cli_all 文件夹,前面有后端服务
  • 创立mkdir ~/huamiao-cli_all/huamiao-cli
  • 切换目录:cd ~/huamiao-cli_all/huamiao-cli
  • 初始化为 npm 我的项目:npm init -y
  • 装置 lerna:npm i -D lerna,为了使用方便(应用的时候不必应用 npx,)全局也装置 lerna:npm i -g lerna
  • lerna 初始化:lerna init(如果不想全局装置 lerna,能够应用 npx lerna init),生成lerna.json 并批改版本为1.0.0

    {
    "packages": ["packages/*"],
    "version": "1.0.0"
    }
    
  • lerna init后会默认执行git init,创立.gitignore,疏忽以下文件:

    **/node_modules
    .vscode
    .DS_Store
    lerna-debug.log
  • git 暂存:git add . && git status

    git add . && git status
    位于分支 master
    
    尚无提交
    
    要提交的变更:(应用 "git rm --cached < 文件 >..." 以勾销暂存)新文件:.gitignore
          新文件:lerna.json
          新文件:package-lock.json
          新文件:package.json
  • 提交到本地仓库:git commit -m 'init'

注:npm 官网源如果太慢,能够切换为淘宝源或应用cnpm/yarn

npm config set registry https://registry.npm.taobao.org/

2.2. 创立包 & 测试公布

  • 创立 core 外围包,输出 lerna create core,一路回车
    特地留神:package name: (core) @huamiao-cli/core,这边须要蕴含组织名称@huamiao-cli

    lerna create core
    
    package name: (core) @huamiao-cli/core
    
    {
    "name": "@huamiao-cli/core",
    "version": "1.0.0",
    "description": "> TODO: description",
    "homepage": "","license":"ISC","main":"lib/core.js","directories": {"lib":"lib","test":"__tests__"},"files": ["lib"],"publishConfig": {"registry":"https://registry.npm.taobao.org"},"scripts": {"test":"echo \"Error: run tests from root\" && exit 1"}
    }
    
    
    Is this OK? (yes) 
    lerna success create New package @huamiao-cli/core created at ./packages/core
  • 创立 utils 工具包,同上
  • npmjs创立组织:@huamiao-cli

点击 Create

点击 Skip

当初能够在 npmjs-> 个人头像 ->packages 查看到

  • 公布,lerna publish,公布组织的包须要配置 publishConfig
    如果你用淘宝源,可能会呈现上面的状况:

    {
    "name": "@huamiao-cli/core",
    "version": "1.0.6",
    "description": "> TODO: description",
    "homepage": "","license":"ISC","main":"lib/core.js","directories": {"lib":"lib","test":"__tests__"},"files": ["lib"],"publishConfig": {"registry":"https://registry.npm.taobao.org"},"scripts": {"test":"echo 'run utils test'"}
    }

    看这边

    "publishConfig": {"registry": "https://registry.npm.taobao.org"},

    packages中两个包的 package.json 都改为:

    "publishConfig": {"access": "public"}

    公布前要 git 提交,并且要绑定近程仓库,否则:

    lerna publish     
    info cli using local version of lerna
    lerna notice cli v4.0.0
    lerna info current version 1.0.0
    lerna ERR! ENOREMOTEBRANCH Branch 'master' doesn't exist in remote'origin'.
    lerna ERR! ENOREMOTEBRANCH If this is a new branch, please make sure you push it to the remote first.

    咱们在 gitee 创立一个公开库,这边不赘述了,有疑难留言。

配置近程仓库并推送:

git remote add origin https://gitee.com/xxx/huamiao-cli.git
git push -u origin master

切换到 npm 官网源:

npm config set registry https://registry.npmjs.org

注:如果这边不慎没切换到官网源,再提交一个 git,再公布,版本累加即可公布

公布:lerna publish选第一个先:

❯ Patch (1.0.1) 
  Minor (1.1.0) 
  Major (2.0.0) 
  Prepatch (1.0.1-alpha.0) 
  Preminor (1.1.0-alpha.0) 
  Premajor (2.0.0-alpha.0) 
  Custom Prerelease 
  Custom Version 

输出 y

lerna publish                                                
info cli using local version of lerna
lerna notice cli v4.0.0
lerna info current version 1.0.0
lerna info Assuming all packages changed
? Select a new version (currently 1.0.0) Patch (1.0.1)

Changes:
 - @huamiao-cli/core: 1.0.0 => 1.0.1
 - @huamiao-cli/utils: 1.0.0 => 1.0.1

? Are you sure you want to publish these packages? (ynH) y
>> Yes

残缺日志:

info cli using local version of lerna
lerna notice cli v4.0.0
lerna info current version 1.0.4
lerna info Looking for changed packages since v1.0.4
? Select a new version (currently 1.0.4) Patch (1.0.5)

Changes:
 - @huamiao-cli-dev/core: 1.0.4 => 1.0.5
 - @huamiao-cli-dev/utils: 1.0.4 => 1.0.5

? Are you sure you want to publish these packages? Yes
lerna info execute Skipping releases
lerna info git Pushing tags...
lerna info publish Publishing packages to npm...
lerna notice Skipping all user and access validation due to third-party registry
lerna notice Make sure you're authenticated properly ¯\_(ツ)_/¯
lerna WARN ENOLICENSE Packages @huamiao-cli-dev/core and @huamiao-cli-dev/utils are missing a license.
lerna WARN ENOLICENSE One way to fix this is to add a LICENSE.md file to the root of this repository.
lerna WARN ENOLICENSE See https://choosealicense.com for additional guidance.
lerna http fetch PUT 200 https://registry.npmjs.org/@huamiao-cli-dev%2futils 4431ms
lerna success published @huamiao-cli-dev/utils 1.0.5
lerna notice 
lerna notice 📦  @huamiao-cli-dev/utils@1.0.5
lerna notice === Tarball Contents === 
lerna notice 73B  lib/utils.js
lerna notice 487B package.json
lerna notice 108B README.md   
lerna notice === Tarball Details === 
lerna notice name:          @huamiao-cli-dev/utils                  
lerna notice version:       1.0.5                                   
lerna notice filename:      huamiao-cli-dev-utils-1.0.5.tgz         
lerna notice package size:  565 B                                   
lerna notice unpacked size: 668 B                                   
lerna notice shasum:        9af0a5740f88b0a6829ea4a90c7107435d0f5489
lerna notice integrity:     sha512-8kKZVoH1sF0WB[...]1aTA3UQiBl70g==
lerna notice total files:   3                                       
lerna notice 
lerna http fetch PUT 200 https://registry.npmjs.org/@huamiao-cli-dev%2fcore 7474ms
lerna success published @huamiao-cli-dev/core 1.0.5
lerna notice 
lerna notice 📦  @huamiao-cli-dev/core@1.0.5
lerna notice === Tarball Contents === 
lerna notice 71B  lib/core.js 
lerna notice 485B package.json
lerna notice 105B README.md   
lerna notice === Tarball Details === 
lerna notice name:          @huamiao-cli-dev/core                   
lerna notice version:       1.0.5                                   
lerna notice filename:      huamiao-cli-dev-core-1.0.5.tgz          
lerna notice package size:  566 B                                   
lerna notice unpacked size: 661 B                                   
lerna notice shasum:        a26f686a3951de2df1ef2196a1b82c9d87e299ec
lerna notice integrity:     sha512-rP/WQo7oROLD3[...]7LZKGyzZ9vHYQ==
lerna notice total files:   3                                       
lerna notice 
Successfully published:
 - @huamiao-cli-dev/core@1.0.5
 - @huamiao-cli-dev/utils@1.0.5
lerna success published 2 packages

期待胜利后,在 npmjs packages中的 huamiao-cli 组织,查看是否公布胜利。

这边公布在 huamiao-cli-dev 这个组织外面


npmjs 偶然会不稳固,并且对于国内网络互联互通问题常常拜访较慢,如果失败,判断不是咱们的问题,能够从新公布,就是 learn 公布每次都会要求加个版本,这点有点不合理

注:这边老手会遇到各种问题,如有问题,能够在留言,老衲帮掘友解决。

强烈建议老手测试公布胜利后再进行开发。

3. 起码必要常识

3.1.learn

3.1.1. 初始化

  • lerna init,初始化我的项目,创立 lerna.json 存储 version,并查看package.jsondevDependency有无lerna,没有则增加

3.1.2. 创立 package

  • lerna create,创立 package

    以下 <> 为必须,[]为可选
    Usage:lerna create <name> [loc]
    name:惟一、蕴含作用域名(本文为 @huamiao-cli,eg:@huamiao-cli/core)loc:包和包能够嵌套,抉择包的绝对地址,默认为第一个配置的包
    地位
  • lerna add,装置依赖,eg:lerna add module-1 --scope=module-2scope指定包,不指定会装置到每个包里,每次只能装置一个依赖
Usage:lerna add <package>[@version] [--dev] [--exact] [--peer]
  • lerna link,链接依赖,本地调试包的相互依赖,多个包一起开发

3.1.3. 开发和测试

  • lerna exec,对 packages 中每个包执行命令,eg:lerna exec -- rm -rf ./node_modules
  • lerna run,对 packages 中每个包执行npm script,eg:lerna run test
  • lerna clean,删除所有 packages 中的 node_module,约等于lerna exec -- rm -rf ./node_modules
  • lerna bootstrap,对 packages 中每个包从新按装置依赖

3.2. npm公布繁难流程

3.2.1. npmjs注册账号

3.2.2. 切换为官网源

关上终端,切换为官网源(如果之前没切换过源,默认是官网的,不须要切换)

npm config set registry https://registry.npmjs.org

3.2.3. npm 登录

npm login

Username: <username>
Password: 
Email: (this IS public) <username>@qq.com
Logged in as <username> on https://registry.npmjs.org/.

3.2.4. npm 公布

npm publish

Successfully published:
 - @huamiao-cli/core@1.0.0
 - @huamiao-cli/utils@1.0.0
lerna success published 2 packages

4. cli 筹备工作

4.1. 配置命令

packages/core将作为 cli 入口

切换到 packages/core 目录

新增文件bin/index.js

#!/usr/bin/env node

require('../lib/index')();

package.json增加:

"bin": {"miao": "bin/index.js"},

lib/index.js

console.log('hello world!');

npm link装置到全局,全局就能够调用 miao 命令了

关上终端测试下:miao,测试胜利,失常打印出:

hello world!

4.2. 版本号、欢送语

接着开发,获取版本号

const packageJson = require('../package');

下面的欢送语,比拟个别,咱们来个有逼格的:

const packageJson = require('../package');

console.log('欢送应用');
console.log(` _   _             __  __ _                    ____ _ _ 
| | | |_   _  __ _|  \\/  (_) __ _  ___        / ___| (_)
| |_| | | | |/ _\` | |\\/| | |/ _\` |/ _ \\  ___ | |   | | |
|  _  | |_| | (_| | |  | | | (_| | (_) ||___|| |___| | |
|_| |_|\\__,_|\\__,_|_|  |_|_|\\__,_|\\___/       \\____|_|_|  Version ${packageJson.version}
`);

4.3. 日志,分包开发

下面的 console.log('欢送应用'); 改色彩下,晋升逼格:

const {log} = require('@huamiao-cli/utils');

这边 log 咱们还没开发

core/package.json增加

"dependencies": {"@huamiao-cli/utils": "^1.0.0"},

lerna link

当初就能够用 packages/utils 包的办法了

日志咱们用的是 npmlog, 切换到packages/utils 须要装置依赖:npm i npmlog

packages/utils/package.json批改:

  "main": "lib/index.js",

lib/index.js文件:

'use strict';

const log = require('./log');

// 对立导出,前面还有很多工具
module.exports = {log};

lib/log.js文件:

const log = require('npmlog')

log.level = 'info'

log.heading = 'huamiao-cli' // 自定义头部
log.addLevel('success', 2000, { fg: 'green', bold: true}) // 自定义 success 日志
log.addLevel('notice', 2000, { fg: 'blue', bg: 'black'}) // 自定义 notice 日志

module.exports = log

回到packages/core/lib/index.js

"use strict";

module.exports = core;

const packageJson = require("../package");
const {log} = require("@huamiao-cli/utils");

function core() {
  console.log(` _   _             __  __ _                    ____ _ _ 
| | | |_   _  __ _|  \\/  (_) __ _  ___        / ___| (_)
| |_| | | | |/ _\` | |\\/| | |/ _\` |/ _ \\  ___ | |   | | |
|  _  | |_| | (_| | |  | | | (_| | (_) ||___|| |___| | |
|_| |_|\\__,_|\\__,_|_|  |_|_|\\__,_|\\___/       \\____|_|_|  Version ${packageJson.version}
`);
  log.info("欢送应用");
}

这样版本号、欢送语曾经 ok 了

4.4. nodejs 最低版本判断

咱们这个 cli 应用到的一些库是须要 nodejs version 12+,所以这边咱们须要查看下以后nodejs 的版本,这边须要 semver 包,装置下:npm i semver

packages/core/lib/index.js减少:

  const semver = require("semver");
  const MINIMUM_NODEJS_VERSION = "14.0.0";

  if (semver.lte(process.version, MINIMUM_NODEJS_VERSION)) {console.log(`huamiao-cli 最低要求 Node.js 版本 v${MINIMUM_NODEJS_VERSION}`);
    process.exit();}

这个提醒咱们想让他变成红色,能够用 log:

  const semver = require("semver");
  const MINIMUM_NODEJS_VERSION = "14.0.0";

  if (semver.lte(process.version, MINIMUM_NODEJS_VERSION)) {log.error(`huamiao-cli 最低要求 Node.js 版本 v${MINIMUM_NODEJS_VERSION}`);
    process.exit();}

最低 nodejs 的版本号咱们抽离常量进去,不便前面批改

packages/core/lib/index.js当初咱们残缺的代码如下:

"use strict";

module.exports = core;

const packageJson = require("../package");
const {log} = require("@huamiao-cli/utils");

function core() {console.log();
  console.log(` _   _             __  __ _                    ____ _ _ 
| | | |_   _  __ _|  \\/  (_) __ _  ___        / ___| (_)
| |_| | | | |/ _\` | |\\/| | |/ _\` |/ _ \\  ___ | |   | | |
|  _  | |_| | (_| | |  | | | (_| | (_) ||___|| |___| | |
|_| |_|\\__,_|\\__,_|_|  |_|_|\\__,_|\\___/       \\____|_|_|  Version ${packageJson.version}
`);
  log.info("欢送应用");
  console.log();

  const semver = require("semver");
  const MINIMUM_NODEJS_VERSION = "14.0.0";

  if (semver.lte(process.version, MINIMUM_NODEJS_VERSION)) {log.error(`huamiao-cli 最低要求 Node.js 版本 v${MINIMUM_NODEJS_VERSION}`);
    process.exit();}
}

执行下miao

4.5. 剖析参数是否无效,是否 debug

接下来剖析下参数

为了调试不便 packages/utils/lib/log.js 能够批改日志等级为verbose,这个是调试用的等级:

log.level = 'verbose'

校验入参,这边须要,minimist包,装置下依赖:npm i minimist

  const minimist = require("minimist");

  log.info("校验入参:");
  let args = minimist(process.argv.slice(2));
  log.info("args:", args);
  
  if (args["_"].length === 0) {log.warn('请输出参数')
  }

测试下
miao cmdChild cmdChildParams -a aParams -b bParams

huamiao-cli info 欢送应用 
huamiao-cli verb 校验入参:huamiao-cli verb args:  {_: [ 'cmdChild', 'cmdChildParams'], a: 'aParams', b: 'bParams' }

咱们常常须要调试,所以咱们加个 debug 参数,miao --debugminimist默认反对

  const minimist = require("minimist");

  log.info("校验入参:");
  let args = minimist(process.argv.slice(2));
  log.info("args:", args);

  if (args["_"].length === 0 && !args.debug) {log.warn("请输出参数");
  } else {
    // 能够在这配置 debug 相干设置,比方批改 log 的等级为 verbose 来打印调试日志
    log.level = "verbose";
    log.verbose("debug");
  }

增加下环境变量process.env.LOG_LEVEL,并设置 log 的日志等级

  const minimist = require("minimist");

  log.verbose("校验入参:");
  let args = minimist(process.argv.slice(2));
  log.verbose("args:", args);

  if (args["_"].length === 0 && !args.debug) {log.warn("请输出参数");
  } else {
    // 能够在这配置 debug 相干设置,比方批改 log 的等级为 verbose 来打印调试日志
    if (args.debug) process.env.DEBUGGING = "verbose";
    else process.env.DEBUGGING = "info";
    log.level = process.env.DEBUGGING;
  }

筹备工作算完结了

4.6. 注册命令

应用 commander 包,装置依赖:npm i commander

const program = require("commander");

// 设置版本号、自定义用法说明
program.version(packageJson.version).usage("<command> [options] 其余阐明");

// 增加命令能够在这里增加
// ……

// 注册命令
program.parse(process.argv);

咱们测试下,miao -h

增加个简略的命令:

const program = require("commander");

  // 设置版本号、自定义用法说明
  program.version(packageJson.version).usage("<command> [options] 其余阐明");

  // 增加命令能够在这里增加
  program
    .command("test")
    .description("形容")
    .option("-a, --all", "清空全副")
    .action((cmd, options) => {// cmd.all 主动由下面配置 .option("-a, --all", "清空全副") 创立
      console.log("cmd:", cmd.all);
      log.success("测试", "一只花喵");
    });

  // 其余子命令
  program.command("*").action(function (cmd, options) {console.log("cmd:", cmd);
    console.log("options:", options.args);
    console.log("没有匹配到命令:miao", args['_']);
  });

  // 注册命令
  program.option("--debug", "关上调试模式").parse(process.argv);

试试 miao test -a aaaamiao test --all bbbbmiao diy 吧!

内容有点多,第一节先这样吧!

FAQ

  • 依赖谬误或未知谬误,清理所有依赖lerna clean,重装依赖lerna bootstrap

    Loadmap

  • init【重磅】:抉择模板、自定义模板等
  • 公布【重磅】:公布、git 操作、构建等
  • 更多实现细节

参考

  • [1] 什么是 CI/CD:https://linux.cn/article-9926…
  • lerna doc:https://github.com/lerna/lerna
  • CI/CD 是什么:https://www.redhat.com/zh/top…

点赞、关注越多更新越快哦~

退出移动版