UzumakiItachi
首页
  • JavaSript
  • Vue

    • Vue2
    • Vue3
  • React

    • React_18
  • WebPack
  • 浏览器相关
  • 工程化相关
  • 工作中遇到的问题以及解决方案
  • Git
  • 面试
  • 学习
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
  • 个人产出
  • 实用工具
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

UzumakiItachi

起风了,唯有努力生存。
首页
  • JavaSript
  • Vue

    • Vue2
    • Vue3
  • React

    • React_18
  • WebPack
  • 浏览器相关
  • 工程化相关
  • 工作中遇到的问题以及解决方案
  • Git
  • 面试
  • 学习
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
  • 个人产出
  • 实用工具
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • WebPack

  • 浏览器相关

  • 工程化相关

    • 执行npm run dev的时候发生了什么
    • 仓库版本管理-standard-version
    • git版本管理以及生成changelog
    • 发布一个npm包
    • gitlab runner 免密码登录服务器
    • 实现一个小程序持续集成工具
      • 前言
      • 小程序持续集成
      • 初始化
      • lib
      • index
      • 使用
      • 注意
      • 构建
      • 关于构建
      • 结语
    • 使用changeset管理monorepo项目
    • pnpm+trubo打造一个极致丝滑的monorepo工程
    • 编写一个生成git提交信息的vite插件
    • 关于构建SPA项目的一些优化
    • 实现一个简易的脚手架
    • vite搭建多页面项目
    • 实现一个可自定义模板内容的脚手架
  • 工作中遇到的问题以及解决方案

  • Git

  • Vite

  • 一些小工具

  • 算法

  • 服务器

  • HTTP

  • 技术
  • 工程化相关
hanhanbuku
2023-05-23
目录

实现一个小程序持续集成工具

# 前言

在开发小程序的过程中会遇到发布版本需要提交代码这个步骤。那有没有什么办法可以像正常的前端项目的流水线自动化的去执行这一步呢?微信官方是有提供一个ci的工具库的,下面我们就基于这个工具库封装一个自己的ci工具,发布到npm上

# 小程序持续集成

⏱ 小程序持续集成 ci,基于 miniprogram-ci (opens new window) 实现

具体的文档可以自行查阅,下面来简单描述一下我们自己的工具包应该如何开发

# 初始化

首先初始化一个项目,并建立好目录结构 img.png

把一些流程化的工具安装好,比如commitlint,standard-version,husky,rollup等等。还有其他需求的可以自己安装。

下面就开始去写构建脚本

# lib

先安装一下微信的ci (注意,这里安装包需要严格区分一下devDependencies和dependencies,作为一个工具,被用户安装的时候是只会下载dependencies里的依赖的,所以你的包运行必须要的依赖请卸载dependencies里)

pnpm install miniprogram-ci
1

miniprogram-ci的使用非常简单,看文档就行。我们这里主要是对他进行一个二次封装,使用起来更加的方便

在我们的ci文件里写上如下内容

const ci = require('miniprogram-ci')
const utils = require('./utils')

export default class mpCi {
    constructor(options) {
        this.options = options
        this.project = null
    }

    init() {
        this.project = new ci.Project({...this.options.config()})
        const baseConfig = () => ({
            project: this.project,
            version: this.options.baseConfig().version || utils.getVersion(),
            desc: this.options.baseConfig().desc,
            robot: this.options.baseConfig().desc.robot || 30,
            setting: this.options.baseConfig().setting ?? {
                es6: true,
                es7: true,
            },
        })
        this.upload(baseConfig)
        this.preview(baseConfig)
    }

    upload(baseConfig) {
        ci.upload({
            ...baseConfig(),
            onProgressUpdate: ({_status, _msg}) => {
                if (_status === 'done') {
                    console.log(`🚁  ${_msg}\n`)
                }
            },
        }).then(({subPackageInfo}) => {
            console.log(
                '\n🌈 体验版上传成功\n\n' +
                '分包信息:\n' +
                subPackageInfo.reduce((pre, curr) => {
                    return pre + `${curr.name}: ${curr.size / 1000}kb\n`
                }, ''),
            )
        }).catch((e) => {
            console.log('\n🛠 体验版上传失败\n', e)
            process.exit(1)
        })
    }

    preview(baseConfig) {
        ci.preview({
            ...baseConfig(),
            onProgressUpdate: () => {
            },
            qrcodeFormat: 'base64',
            qrcodeOutputDest: 'qrcode.text',
        }).then(({subPackageInfo}) => {
            console.log(
                '\n🌈 预览上传成功\n\n' +
                '分包信息:\n' +
                subPackageInfo.reduce((pre, curr) => {
                    return pre + `${curr.name}: ${curr.size / 1000}kb\n`
                }, ''),
            )
        }).catch((e) => {
            console.log('\n🛠 预览上传失败\n', e)
            process.exit(1)
        })
    }
}

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

# index

import mpCi from './lib/ci'

function tyrGet(target){
    if(!target){
        return null
    }
    if(target instanceof Function){
        return target()
    }
    return target
}
export const run = function (options){
    const {config,baseConfig} = options
    try {
        const ci = new mpCi({config:tyrGet(config),baseConfig:tyrGet(baseConfig)})
        ci.init()
    }catch (err){
        console.log('初始化失败:'+ err)
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 使用

run({
    config(){
        return{
            appid: "wxsomeappid",
            type: "miniProgram",
            projectPath: path.resolve(process.cwd(), "./dist/"),
            privateKeyPath: path.resolve(process.cwd(), "./dist/"),
            ignores: ["node_modules/**/*"],  
        }
    },
    baseConfig(){
        return{
            version:"版本号",
            desc: "更新描述",
            robot: "机器人序号",
            setting: {
                es6: true,
                es7: true,
            },
        }
    }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 注意

其他没什么好说的主要提一下privateKeyPath,在初始化ci的时候可以选择传入privateKeyPath或者privateKey,二者选择其一就行。这个密钥是在微信公众平台下载的,下载方法可以看文章开头贴的ci的官方文档。 。如果有流水线的话推荐使用密钥的值的方式。我们可以在仓库流水线的变量里将密钥配置进去。然后在ci脚本里执行我们这个upload脚本的时候通过`````--xxx的形式把密钥输入上去。在脚本中可以通过process.argv```获取到这个--xxx参数。 这种办法既安全有省力。

下面简单演示一下

//假设我们的准备工作都已经做好了,这个时候该怎么去获取这个privateKey呢

/**
 * 通过这函数,去获取在执行shell脚本的时候的参数
 * @param key
 * @returns {string}
 */
function getCliArg(key) {
    const args = process.argv
    const index = args.lastIndexOf(key)
    if (index !== -1) {
        return args[index + 1]
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

在流水线脚本里这样写

 script:
    - yarn
    - yarn build:mp-weixin:test
    - yarn mp-ci --key $MP_CLI_KEY --branch ${CI_COMMIT_BRANCH}
1
2
3
4

注意到上面脚本中的--key没有,他后面跟的$MP_CLI_KEY 就是我们预先在流水线变量里配置好的key了。 这样一来只要运行我们写的ci脚本并且传入--key。我们就只需要在代码中去获取这个--key就可以了

run({
    config(){
        return{
             ···
            privateKey:getCliArg('--key') ,
        }
    },
    baseConfig(){
        ...
    }
})
1
2
3
4
5
6
7
8
9
10
11

# 构建

在构建这一块选择使用rollup,因为他比较轻量级,简单好用,并且天然支持esm 安装好rollup以及rollup-plugin-terser后,写个rollup.config.js,并写入如下配置

import { terser } from "rollup-plugin-terser";

export default {
    input: 'src/index.js',
    output: {
        file: 'dist/index.js',
        format: 'esm'
    },
    plugins: [
        terser({ compress: { drop_console: true } })    
    ]
};

1
2
3
4
5
6
7
8
9
10
11
12
13
 "build": "npx rollup -c",
1

执行一下就可以看到打包后的结果了。

# 关于构建

我们把代码托管到github上,交由github action来去完成这一些列的动作。 具体的yml脚本可以这样写

# 1
name: mp-ic Package

on:
  push:
    branches:
      - master

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
#      采用pnpm的方式去安装依赖
      - uses: pnpm/action-setup@v2
        with:
          #      指定pnpm版本
          version: 7
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'pnpm'
      - name: install
        run: pnpm install
      - name: build
        run: pnpm run build
      - name: publish
        run: npm publish
        env :
          NODE_AUTH_TOKEN: ${{ secrets.MPCI_NPM_TOKEN }}
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

MPCI_NPM_TOKEN是在npm里生成的,据说把他配到github 的action里就可以直接发布。但我试过了好像不太行。最终还是采用手动的形式去发布。如果要自动化的话也不是不行。网上还是有很多免登录的方案的。这里就不去做过多的纠结了。直接采用本地发。

注意在流水线里每一个jobs的环境都是独立的,也就是说,你在上一个jobs里安装的东西。在下一个jobs里是不能用的。所以最好把有关联的步骤都放在同一个jobs里去执行。

# 结语

整体的开发就是这样了,剩下就是将这个项目发布到npm上。具体的过程这里就不多说了。有需要可以自行移步博客里的npm教程

编辑 (opens new window)
上次更新: 2023/05/29, 17:30:33
gitlab runner 免密码登录服务器
使用changeset管理monorepo项目

← gitlab runner 免密码登录服务器 使用changeset管理monorepo项目→

最近更新
01
小程序实现全局状态管理
07-09
02
前端检测更新,自动刷新网页
06-09
03
swiper渲染大量数据的优化方案
06-06
更多文章>
Theme by Vdoing | Copyright © 2023-2025 UzumakiItachi | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式