{"id":16634889,"url":"https://github.com/ruochuan12/mini-ci","last_synced_at":"2025-07-29T02:04:53.936Z","repository":{"id":43748616,"uuid":"511569760","full_name":"ruochuan12/mini-ci","owner":"ruochuan12","description":"基于微信小程序 miniprogram-ci 开发的更快速、更方便且支持多选、批量等功能的小程序上传、预览自动化工具","archived":false,"fork":false,"pushed_at":"2024-08-09T14:02:13.000Z","size":4034,"stargazers_count":217,"open_issues_count":3,"forks_count":29,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-07-20T16:27:39.679Z","etag":null,"topics":["ci","cli","miniprogram","miniprogram-ci","node","nodejs"],"latest_commit_sha":null,"homepage":"https://juejin.cn/post/7124467547163852808","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ruochuan12.png","metadata":{"files":{"readme":"README-2.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2022-07-07T14:56:03.000Z","updated_at":"2025-07-19T00:20:19.000Z","dependencies_parsed_at":"2022-09-05T04:51:48.725Z","dependency_job_id":null,"html_url":"https://github.com/ruochuan12/mini-ci","commit_stats":null,"previous_names":["lxchuan12/mp-cli","ruochuan12/mini-ci","lxchuan12/mini-ci"],"tags_count":20,"template":false,"template_full_name":null,"purl":"pkg:github/ruochuan12/mini-ci","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruochuan12%2Fmini-ci","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruochuan12%2Fmini-ci/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruochuan12%2Fmini-ci/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruochuan12%2Fmini-ci/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ruochuan12","download_url":"https://codeload.github.com/ruochuan12/mini-ci/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruochuan12%2Fmini-ci/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267617643,"owners_count":24116208,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-29T02:00:12.549Z","response_time":2574,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ci","cli","miniprogram","miniprogram-ci","node","nodejs"],"created_at":"2024-10-12T05:48:49.708Z","updated_at":"2025-07-29T02:04:53.906Z","avatar_url":"https://github.com/ruochuan12.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"---\nhighlight: darcula\ntheme: smartblue\n---\n\n# 还在用开发者工具上传小程序? 快来试试 miniprogram-ci 提效摸鱼\n\n## 1. 前言\n\n大家好，我是[若川](https://lxchuan12.gitee.io)。我倾力持续组织了一年[每周大家一起学习200行左右的源码共读活动](https://juejin.cn/post/7079706017579139102)，感兴趣的可以[点此扫码加我微信 `ruochuan02` 参与](https://juejin.cn/pin/7217386885793595453)。另外，想学源码，极力推荐关注我写的专栏[《学习源码整体架构系列》](https://juejin.cn/column/6960551178908205093)，目前是掘金关注人数（4.3k+人）第一的专栏，写有20余篇源码文章。\n\n## 2. 前情回顾\n\n**注意：文章是基于 [`tag v0.7.0`](https://github.com/lxchuan12/mini-ci/tree/0.7.0) 撰写。目前工具是 `0.12.0` 版本，后续 `mini-ci` 会持续更新，文章应该暂时不会更新**。\n\n[本文提到的工具 mini-ci 已开源，求个 star^\\_^](https://github.com/lxchuan12/mini-ci.git)\n\n```bash\n# 可全局安装\nnpm i @ruochuan/mini-ci -g\nmini-ci -h\n# 也可以不全局安装\nnpx @ruochuan/mini-ci -h\n# 查看帮助信息，或者查看文档：https://github.com/lxchuan12/mini-ci.git，\n```\n\n估计有很多开发小程序的同学，还在使用微信开发者工具上传小程序。如果你是，那么这篇文章非常适合你。如果不是，同样也很适合你。\n\n早在 2021 年 08 月，我写过一篇文章 [Vue 3.2 发布了，那尤雨溪是怎么发布 Vue.js 的？](https://juejin.cn/post/6997943192851054606)\n\n`Vue 2.7` 如何发布跟`Vue 3.2`这篇文章类似，所以就不赘述了。\n\n\u003e `vuejs`发布的文件很多代码我们可以直接复制粘贴修改，优化我们自己发布的流程。比如写小程序，相对可能发布频繁，完全可以使用这套代码，配合[miniprogram-ci](https://developers.weixin.qq.com/miniprogram/dev/devtools/ci.html 'miniprogram-ci')，再加上一些自定义，加以优化。\n\n于是今天我们来开发这样的脚手架工具。\n\n看完本文，你将学到：\n\n```bash\n1. 如何利用 release-it 提升版本号，自动打 tag，生成 changelog 等\n2. npm init 原理\n3. 如何写一个脚手架工具\n - 如何解析 Nodejs 命令行参数 minimist\n - 如何选择单选、多选 enquirer(prompt, MultiSelect)\n - 等等\n```\n\n先看看最终开发的效果。\n\n支持的功能\n\n![支持的功能](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/600f4161b25c4d62b47c2fffb8f267fd~tplv-k3u1fbpfcp-watermark.image?)\n\n显示帮助信息\n\n![显示帮助信息](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8bb61b337b4a4065ae669ba3738c8d53~tplv-k3u1fbpfcp-watermark.image?)\n\n上传效果\n\n![上传效果](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3fdedad23161405b9381daf272443f1f~tplv-k3u1fbpfcp-watermark.image?)\n\n## 3. 关于为啥要开发这样的工具\n\n\u003e 关于小程序 `ci` 上传，再分享两篇文章。\n\n\u003e [基于 CI 实现微信小程序的持续构建](https://help.coding.net/docs/best-practices/ci/1minute/wechat-mini-program.html)\n\n\u003e [小打卡小程序自动化构建及发布的工程化实践](https://www.yuque.com/jinxuanzheng/gvhmm5/uy4qu9#8yQ8M) 虽然文章里不是最新的 `miniprogram-ci`，但这篇场景写得比较全面。\n\n接着，我们先来看看 miniprogram-ci 官方文档。\n\n## 4. miniprogram-ci 官方文档\n\n[miniprogram-ci 文档](https://developers.weixin.qq.com/miniprogram/dev/devtools/ci.html)\n\n### 4.1 上传\n\n```js\nconst ci = require('miniprogram-ci');\n(async () =\u003e {\n const project = new ci.Project({\n  appid: 'wxsomeappid',\n  type: 'miniProgram',\n  projectPath: 'the/project/path',\n  privateKeyPath: 'the/path/to/privatekey',\n  ignores: ['node_modules/**/*'],\n });\n const uploadResult = await ci.upload({\n  project,\n  version: '1.1.1',\n  desc: 'hello',\n  setting: {\n   es6: true,\n  },\n  onProgressUpdate: console.log,\n });\n console.log(uploadResult);\n})();\n```\n\n### 4.2 预览\n\n```js\nconst ci = require('miniprogram-ci');\n(async () =\u003e {\n const project = new ci.Project({\n  appid: 'wxsomeappid',\n  type: 'miniProgram',\n  projectPath: 'the/project/path',\n  privateKeyPath: 'the/path/to/privatekey',\n  ignores: ['node_modules/**/*'],\n });\n const previewResult = await ci.preview({\n  project,\n  desc: 'hello', // 此备注将显示在“小程序助手”开发版列表中\n  setting: {\n   es6: true,\n  },\n  qrcodeFormat: 'image',\n  qrcodeOutputDest: '/path/to/qrcode/file/destination.jpg',\n  onProgressUpdate: console.log,\n  // pagePath: 'pages/index/index', // 预览页面\n  // searchQuery: 'a=1\u0026b=2',  // 预览参数 [注意!]这里的`\u0026`字符在命令行中应写成转义字符`\\\u0026`\n });\n console.log(previewResult);\n})();\n```\n\n## 5. Taro 小程序插件 @tarojs/plugin-mini-ci\n\n如果使用 `Taro` 开发的小程序，可以直接使用。\n\n具体如何使用参考文档，我在本文中就不赘述了。\n\n[小程序持续集成 @tarojs/plugin-mini-ci](https://taro-docs.jd.com/taro/docs/plugin-mini-ci/)\n\n我组织的[源码共读第 30 期](https://juejin.cn/post/7082662027143053342)读的就是这个插件，非常值得学习。[@tarojs/plugin-mini-ci 源码解读可以参考 @NewName 的源码文章](https://juejin.cn/post/7089819849257385997)\n\n我体验下来的感觉有以下几点可以优化。\n\n- 不支持指定机器人\n- 不支持不打包时上传\n- 不支持官方提供的更多配置\n- 不支持选择多个小程序批量上传等等\n\n如果有时间我可能给 `Taro` 提 `PR`，当然不一定会被合并。\n\n## 6. uni-app 好像没有提供类似的插件\n\nuni-app 好像没有提供类似的插件。需要自己动手，丰衣足食。\n\n## 7. release-it 自动提升版本、打 tag、生成 changelog 等\n\n于是我们自己动手，丰衣足食，写一个工具解决上面提到的问题，**支持 `Taro` 打包后的小程序和 `uni-app` 打包后的，还有原生小程序上传和预览**。\n\n开发小工具之前，先介绍一些好用的工具。\n\n据说很多小伙伴的项目，没有打 `tag`、没有版本的概念，没有生成 `changelog`，没有配置 `eslint`、`prettier`，没有 `commit` 等规范。\n\n这些其实不难，`commit` 规范一般简单做法是安装 `npm i git-cz -D`，\n在`package.json` 中加入如下脚本。\n\n```json\n{\n \"scripts\": {\n  \"commit\": \"git-cz\"\n }\n}\n```\n\n`git` 提交时使用 `npm run commit` 即可，其他就不赘述了。\n\nrelease-it，自动提升版本号，自动打 `tag`，生成 `changelog` 等\n\n[release-it 官网仓库](https://github.com/release-it/release-it)\n\n```bash\nnpm init release-it\n# 选择 .release-it.json 用下面的配置，复制粘贴到 .release-it.json 中。\n# 再安装 changelog 插件\nnpm i @release-it/conventional-changelog -D\n```\n\n```json\n{\n \"github\": {\n  \"release\": false\n },\n \"git\": {\n  \"commitMessage\": \"release: v${version}\"\n },\n \"npm\": {\n  \"publish\": false\n },\n \"hooks\": {\n  \"after:bump\": \"echo 更新版本成功\"\n },\n \"plugins\": {\n  \"@release-it/conventional-changelog\": {\n   \"preset\": \"angular\",\n   \"infile\": \"CHANGELOG.md\"\n  }\n }\n}\n```\n\n这样配置后，可以 `npm run release` 执行 `release-it` 版本。\n还支持 hooks 钩子，比如提升版本号后`\"after:bump\": \"echo 更新版本成功\"`，更多功能可以查看[release-it 官网仓库](https://github.com/release-it/release-it)。\n\n### 7.1 npm init release-it 原理\n\n为啥 `npm init` 也可以直接初始化一个项目，带着疑问，我们翻看 npm 文档。\n\n[`npm init`](https://docs.npmjs.com/cli/v6/commands/npm-init)\n\n`npm init` 用法：\n\n```bash\nnpm init [--force|-f|--yes|-y|--scope]\nnpm init \u003c@scope\u003e (same as `npx \u003c@scope\u003e/create`)\nnpm init [\u003c@scope\u003e/]\u003cname\u003e (same as `npx [\u003c@scope\u003e/]create-\u003cname\u003e`)\n```\n\n`npm init \u003cinitializer\u003e` 时转换成 `npx` 命令：\n\n```bash\nnpm init foo -\u003e npx create-foo\nnpm init @usr/foo -\u003e npx @usr/create-foo\nnpm init @usr -\u003e npx @usr/create\n```\n\n看完文档，我们也就理解了：\n\n**运行 `npm init release-it` =\u003e 相当于 `npx create-release-it`**\n\n[create-release-it](https://github.com/release-it/create-release-it)\n\n`npm init release-it` 原理其实就是 `npx create-release-it`\n选择一些配置，生成 `.release-it.json` 或者 `package.json` 的 `release-it` 配置。\n\n再写入命令`release` 配置到 `package.json`。\n\n```json\n{\n \"scripts\": {\n  \"release\": \"release-it\"\n }\n}\n```\n\n最后执行 `npm install release-it --save-dev`\n也就是源码里的 `await execa('npm', ['install', 'release-it', '--save-dev'], { stdio: 'inherit' });`。\n\n[这行源码位置](https://github.com/release-it/create-release-it/blob/master/index.js#L120)\n\n## 8. 小程序上传工具实现主流程\n\n需要支持多选，那肯定得遍历数组。\n\n```js\n// 代码只是关键代码，完整的可以查看 https://github.com/lxchuan12/mini-ci/blob/0.7.0/src/index.js\n(async () =\u003e {\n for (const mpConfigItem of mpConfigList) {\n  try {\n   const res = await main({});\n  } catch (err) {\n   console.log('执行失败', err);\n  }\n }\n})();\n```\n\n`main` 函数\n\n```js\nconst { green, bold } = require('kolorist');\nconst step = (msg) =\u003e console.log(bold(green(`[step] ${msg}`)));\nasync function main(options = {}) {\n const project = new ci.Project(lastProjectOptions);\n if (upload) {\n  step('开始上传小程序...');\n  const uploadResult = await ci.upload(lastUploadOptions);\n  console.log('uploadResult', uploadResult);\n }\n if (preview) {\n  step('开始生成预览二维码...');\n  const previewResult = await ci.preview(lastPreviewOptions);\n  console.log('previewResult', previewResult);\n }\n}\n```\n\n### 8.1 添加功能支持指定参数\n\n使用 [minimist](https://github.com/minimist/minimist) 解析命令行参数。\n\n```js\nconst getParams = () =\u003e {\n const params = process.argv.slice(2);\n const paramsDefault = {\n  default: {\n   robot: 1,\n   preview: false,\n   upload: false,\n   // 空跑，不执行\n   dry: false,\n   // 根据配置，单选还是多选来上传小程序\n   useSelect: false,\n   useMultiSelect: false,\n   help: false,\n   version: false,\n  },\n  alias: {\n   u: 'upload',\n   r: 'robot',\n   v: 'version',\n   d: 'dry',\n   s: 'useSelect',\n   m: 'useMultiSelect',\n   p: 'preview',\n   h: 'help',\n  },\n };\n return require('minimist')(params, paramsDefault);\n};\n\nmodule.exports = {\n getParams,\n};\n```\n\n### 8.2 支持读取项目的 `package.json` 的 `version`，也支持读取自定义`version`\n\n[kolorist](https://github.com/marvinhagemeister/kolorist) 颜色输出。\n\n```js\nconst { red, bold } = require('kolorist');\nconst getVersion = () =\u003e {\n let version;\n try {\n  version = require(`${packageJsonPath}/package.json`).version;\n } catch (e) {\n  console.log(e);\n  console.log(\n   red(\n    bold(\n     '未设置 version , 并且未设置 package.json 路径，无法读取 version',\n    ),\n   ),\n  );\n }\n return version;\n};\n\nmodule.exports = {\n getVersion,\n};\n```\n\n### 8.3 版本描述 支持指定 git commit hash 和作者\n\n`git rev-parse --short HEAD` 读取 `git` 仓库最近一次的 `commit hash`。\n\n`parse-git-config` 可以读取 `.git/config` 配置。\n\n```js\n// const path = require('path');\nconst { execSync } = require('child_process');\nconst parseGitConfig = require('parse-git-config');\nconst getDesc = (projectPath, version) =\u003e {\n // 获取最新 git 记录 7位的 commit hash\n let gitCommitHash = 'git commit hash 为空';\n try {\n  gitCommitHash = execSync('git rev-parse --short HEAD', {\n   cwd: projectPath,\n  })\n   .toString()\n   .trim();\n } catch (e) {\n  console.warn('获取 git commit hash 失败');\n  console.warn(e);\n }\n\n // 获取项目的git仓库的 user.name\n let userName = '默认';\n try {\n  const {\n   user: { name = '默认' },\n  } = parseGitConfig.sync({\n   cwd: projectPath,\n   path: '.git/config',\n  });\n  userName = name;\n } catch (e) {\n  console.warn('获取 .git/config user.name 失败');\n  console.warn(e);\n }\n\n const desc = `v${version} - ${gitCommitHash} - by@${userName}`;\n return desc;\n};\n\nmodule.exports = getDesc;\n```\n\n### 8.4 读取配置 wx.config.js 配置（更推荐）\n\n当前也支持读取 `.env` 配置。读取 `.env` 配置，可以采用 [`dotenv`](https://github.com/motdotla/dotenv)。关于 dotenv 的原理，可以看我之前写过的文章[面试官：项目中常用的 .env 文件原理是什么？如何实现？](https://juejin.cn/post/7045057475845816357)\n\n但 `wx.config.js` 可以配置更多东西而且更灵活。所以更推荐。\n\n感兴趣的可以研究 `vue-cli` 是如何读取 `vue.config.js` 配置的。围绕工作相关的学习，往往收益更大。\n\n```js\n// 读取 wx.config.js 配置\nconst loadWxconfig = (cwd) =\u003e {\n try {\n  return require(path.join(cwd, 'wx.config.js'));\n } catch (e) {\n  return {\n   error: '未配置 wx.config.js 文件',\n  };\n }\n};\n\nconst parseEnv = () =\u003e {\n const cwd = process.cwd();\n\n let parsed = {};\n let wxconfig = loadWxconfig(cwd);\n if (wxconfig.error) {\n  let dotenvResult = require('dotenv').config({\n   path: path.join(cwd, './.env'),\n  });\n\n  parsed = dotenvResult.parsed;\n  if (dotenvResult.error) {\n   throw error;\n  }\n } else {\n  parsed = wxconfig;\n }\n // 代码有省略\n};\n```\n\n### 8.5 支持选择多个小程序\n\n我们可以用 [enquirer](https://github.com/enquirer/enquirer) 来实现单选或者多选的功能。以下只是关键代码。\n完整代码可以查看 [mini-ci/src/utils/getConfig.js 文件](https://github.com/lxchuan12/mini-ci/blob/0.7.0/src/utils/getConfig.js)。\n\n```js\n// 只是关键代码\nconst { prompt, MultiSelect } = require('enquirer');\nconst configPathList = fs.readdirSync(configPath);\nconst configPathListJson = configPathList.map((el) =\u003e {\n return require(`${configPath}/${el}`);\n});\nconst { name } = await prompt({\n type: 'select',\n name: 'name',\n message: '请选择一个小程序配置',\n choices: configPathListJson,\n});\nresult = configPathListJson.filter((el) =\u003e el.name === name);\nreturn result;\n```\n\n### 8.6 支持多个批量上传\n\n```js\n// 只是关键代码\nconst { prompt, MultiSelect } = require('enquirer');\nconst configPathList = fs.readdirSync(configPath);\nconst configPathListJson = configPathList.map((el) =\u003e {\n return require(`${configPath}/${el}`);\n});\nconst multiSelectPrompt = new MultiSelect({\n name: 'value',\n message: '可选择多个小程序配置',\n limit: 7,\n choices: configPathListJson,\n});\n\ntry {\n const answer = await multiSelectPrompt.run();\n console.log('Answer:', answer);\n result = configPathListJson.filter((el) =\u003e answer.includes(el.name));\n return result;\n} catch (err) {\n console.log('您已经取消');\n console.log(err);\n process.exit(1);\n}\n```\n\n**后续可能接入 CI/CD、接入邮件提醒、接入钉钉、支持可视化操作等等**\n\n### 8.7 更多如何使用可以参考文档\n\n```bash\n# 全局安装 mini-ci 工具，也可以不全局安装\nnpm i @ruochuan/mini-ci -g\nmini-ci -h\nnpx @ruochuan/mini-ci -h\n# 文档：https://github.com/lxchuan12/mini-ci.git\n# 克隆腾讯开源的电商小程序\ngit clone https://github.com/lxchuan12/tdesign-miniprogram-starter-retail.git\n# 切到分支 feature/release-it\ngit checkout feature/release-it\n```\n\n可以克隆我的另外一个小程序（腾讯开源的电商小程序）。比如 `projects` 中。\n\n按照[微信小程序文档](https://developers.weixin.qq.com/miniprogram/dev/devtools/ci.html)配置小程序密钥等，这样就能上传和预览了。如果没有微信小程序，可以自行免费开通个人的[微信小程序](https://mp.weixin.qq.com/)。\n\n## 9. 总结\n\n通过本文的学习，我们知道了以下知识。\n\n```bash\n1. 如何利用 release-it 提升版本号，自动打 tag，生成 changelog 等\n2. npm init 原理\n3. 如何写一个脚手架工具\n - 如何解析 Nodejs 命令行参数 minimist\n - 如何选择单选、多选 enquirer(prompt, MultiSelect)\n - 等等\n```\n\n我相信大家也能够自己动手实现公司类似要求的脚手架工具，减少发版时间，降本提效。\n\n[本文提到的工具 mini-ci 已开源，求个 star^\\_^](https://github.com/lxchuan12/mini-ci.git)\n\n```bash\n# 可全局安装\nnpm i @ruochuan/mini-ci -g\nmini-ci -h\n# 也可以不全局安装\nnpx @ruochuan/mini-ci -h\n# 查看帮助信息，或者查看文档：https://github.com/lxchuan12/mini-ci.git，\n```\n\n**注意：文章是基于 [`tag v0.7.0`](https://github.com/lxchuan12/mini-ci/tree/0.7.0) 撰写。目前工具是 `0.12.0` 版本，后续 `mini-ci` 会持续更新，文章应该暂时不会更新**。\n\n---\n\n最后可以持续关注我[@若川](https://juejin.cn/user/1415826704971918)。我倾力持续组织了一年[每周大家一起学习200行左右的源码共读活动](https://juejin.cn/post/7079706017579139102)，感兴趣的可以[点此扫码加我微信 `ruochuan02` 参与](https://juejin.cn/pin/7217386885793595453)。\n\n另外，想学源码，极力推荐关注我写的专栏[《学习源码整体架构系列》](https://juejin.cn/column/6960551178908205093)，目前是掘金关注人数（4.2k+人）第一的专栏，写有20余篇源码文章。包含`jQuery`、`underscore`、`lodash`、`vuex`、`sentry`、`axios`、`redux`、`koa`、`vue-devtools`、`vuex4`、`koa-compose`、`vue 3.2 发布`、`vue-this`、`create-vue`、`玩具vite`、`create-vite` 等20余篇源码文章。\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fruochuan12%2Fmini-ci","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fruochuan12%2Fmini-ci","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fruochuan12%2Fmini-ci/lists"}