github地址:github.com/jinxuanzhen… 覺得有用的朋友幫忙給專案一個star,謝謝
接上文 《從0到1開發一個小程式cli腳手架(一)--建立頁面/元件模版篇》
上文大家應該大致學會了怎麼搭建一個cli腳手架,包括實現了一個快速生成啟動模版的功能,本質上作為腳手架應該可以做更多的事情,本篇文章會實現一些新的功能,例如:自動釋出體驗版,版本號控制,環境變數控制
痛點
不知道大家有沒有一天發多次版本或者一天給多個小程式發版的經歷,按照微信正常的釋出流程,需要:
- 修改版本號/版本描述
- 修改釋出環境
- 點選微信開發者工具上傳體驗版
- 提交稽核
- 確認環境/版本
- 點選發布
其中所有的1,2步為手工修改config檔案,第5步是確認手工修改config檔案的正確性,畢竟人總會犯錯,作者表示就幹過線下環境釋出到測試環境的事情,而且這是在做了第5步的情況下,很遺憾沒有仔細核對
為了不再次發生同樣的事情導致引咎辭職,那麼有沒有更好的方法呢 ?自然是有的,既然人不可靠,那麼直接把它流程化自然就可以了
準備工作
-
最好閱讀了上篇文章《從0到1開發一個小程式cli腳手架(一)--建立頁面/元件模版篇》,並搭建了一個簡單的demo
-
需要了解微信小程式提供的一些cli能力, 點選這裡
Let‘ go
後續的很多流程上的實操程式碼為了縮短篇幅會以虛擬碼的形式來進行描述,強烈推薦先閱讀上文,如果需要更詳細的實操程式碼請去github倉庫檢視
實際效果圖:
梳理流程
- 識別命令列
- 詢問問題,拿到版本號和版本描述
- 呼叫微信提供的cli能力,進行體驗版上傳
是不是發現非常簡單,事實也是如此,整個功能做下來也就60行程式碼 ~
目錄結構
專案結構分為入口檔案,配置檔案
- lib
- publish-weapp.js
- index.js
- config.js
- node_modules
- package.json
複製程式碼
config.js用來記錄一些基礎常量和預設項的配置,例如專案路徑,執行路徑等
module.exports = {
// 根目錄
root: __dirname,
// 執行命令目錄路徑
dir_root: process.cwd(),
// 小程式專案路徑
entry: './',
// 專案編譯輸出資料夾
output: './',
}
複製程式碼
識別命令列
在入口檔案(index.js)處利用第三方庫 commander 識別命令列引數,同時作為路由進行任務分發,
#!/usr/bin/env node
const version = require('./package').version; // 版本號
/* = package import
-------------------------------------------------------------- */
const program = require('commander'); // 命令列解析
/* = task events
-------------------------------------------------------------- */
const publishWeApp = require('./lib/publish-weapp'); // 釋出體驗版
program
.command('publish')
.description('釋出微信小程式體驗版')
.action((cmd, options) => publishWeApp();
/* = main entrance
-------------------------------------------------------------- */
program.parse(process.argv)
複製程式碼
建立互動命令
接下來是一個QA環節,這時候需要根據自己的實際需要安排自己需要的命令,可以回想一下要解決的問題:
- 修改版本號/版本描述
- 修改釋出環境
- 根據cli自動上傳體驗版
大概得出這樣一個佇列:
function getQuestion({version, versionDesc} = {}) {
return [
// 確定是否釋出正式版
{
type: 'confirm',
name: 'isRelease',
message: '是否為正式釋出版本?',
default: true
},
// 設定版本號
{
type: 'list',
name: 'version',
message: `設定上傳的版本號 (當前版本號: ${version}):`,
default: 1,
choices: getVersionChoices(version),
filter(opts) {
if (opts === 'no change') {
return version;
}
return opts.split(': ')[1];
},
when(answer) {
return !!answer.isRelease
}
},
// 設定上傳描述
{
type: 'input',
name: 'versionDesc',
message: `寫一個簡單的介紹來描述這個版本的改動過:`,
default: versionDesc
},
]
}
複製程式碼
最後獲得的json物件,是這個樣子:
{isRelease: true, version: '1.0.1', versionDesc: '這是一個體驗版'}
複製程式碼
確定是否釋出正式版
主要是因為體驗版並非完全是發行版,公司內部測試的時候也是需要釋出測試用體驗版的,但是又涉及只有正式版本修改本地版本檔案,那麼就只能多此一問作為區分了
設定版本號 / 設定版本資訊
設定上述如圖的 體驗版上的版本號和描述資訊,
這裡有個case是隻有第一個問題選擇釋出正式版才會讓你設定版本號,測試版本使用預設版本號0.0.0,這也是區分體驗版的一種方式
when(answer) {
return !!answer.isRelease
}
複製程式碼
這裡只設定了三個問題:是否釋出正式版,設定版本號,設定上傳描述,當然你也可以設定自己想做的其他事情
上傳體驗版
翻了下小程式的文件,大概猶如下幾個關鍵點:
找到cli工具
小程式cli並非全域性安裝,需要自己去索引路徑,命令列工具所在位置:
macOS: <安裝路徑>/Contents/MacOS/cli
Windows: <安裝路徑>/cli.bat
mac的 安裝路徑 如果是預設安裝的話,是/Applications/wechatwebdevtools.app/, 外加cli的位置是:
/Applications/wechatwebdevtools.app/Contents/Resources/app.nw/bin/cli
windows 的話作者表示沒有這個環境,只能大家自己探索了
拼湊上傳命令
官方文件給了非常詳細的描述:
# 上傳路徑 /Users/username/demo 下的專案,指定版本號為 1.0.0,版本備註為 initial release
cli -u 1.0.0@/Users/username/demo --upload-desc 'initial release'
# 上傳並將程式碼包大小等資訊存入 /Users/username/info.json
cli -u 1.0.0@/Users/username/demo --upload-desc 'initial release' --upload-info-output /Users/username/info.json
複製程式碼
編寫上傳邏輯
基本流程:
獲取cli -> 獲取當前版本配置 -> 問題佇列(獲取上傳資訊)-> 執行上傳(cli命令)-> 修改本地版本檔案 -> 成功提示
// ./lib/publish-weapp.js 檔案
module.exports = async function(userConf) {
// cli路徑
const cli = `/Applications/wechatwebdevtools.app/Contents/Resources/app.nw/bin/cli`;
// 版本配置檔案路徑
const versionConfPath = Config.dir_root + '/xdk.version.json';
// 獲取版本配置
const versionConf = require(versionConfPath);
// 開始執行問題佇列 anser case: {isRelease: true, version: '1.0.1', versionDesc: '這是一個體驗版'}
let answer = await inquirer.prompt(getQuestion(versionConf));
versionConf.version = answer.version || '0.0.0';
versionConf.versionDesc = answer.versionDesc;
//上傳體驗版
let res = spawn.sync(cli, ['-u', `${versionConf.version}@${Config.output}`, '--upload-desc', versionConf.versionDesc], { stdio: 'inherit' });
if (res.status !== 0) process.exit(1);
// 修改本地版本檔案 (當為發行版時)
!!answer.isRelease && await rewriteLocalVersionFile(versionConfPath, versionConf);
// success tips
Log.success(`上傳體驗版成功, 登入微信公眾平臺 https://mp.weixin.qq.com 獲取體驗版二維碼`);
}
// 修改本地版本檔案
function rewriteLocalVersionFile(filepath, versionConf) {
return new Promise((resolve, reject) => {
fs.writeFile(filepath, jsonFormat(versionConf), err => {
if(err){
Log.error(err);
process.exit(1);
}else {
resolve();
}
})
})
}
複製程式碼
注意這裡需要在專案根目錄下建立一個xdk.version.json的版本檔案,詳細配置說明見倉庫
大致是這樣的結構:
// xdk.version.json
{
"version": "0.12.2",
"versionDesc": "12.2版本"
}
複製程式碼
到這裡基本就完成了上傳的所有步驟,可以當前專案下鍵入xdk-cli publish
檢視一下程式是否正常執行,注意上傳出錯記得檢查是否處於登入狀態
擴充套件:關於版本號自增
可以看到在版本號環節使用的是list型別,而非input型別,這是因為手寫版本號有寫錯的風險, 還是讓我做選擇題吧 emmm...
最終效果:
就不在這裡展開了,可以下, 搜尋getVersionChoices
函式:
github.com/jinxuanzhen…
解決打包工具的問題
你會發現是執行cli命令就直接釋出了,那麼用gulp,webpack等工具專案因為需要上傳dist目錄而非src的原因,需要先進行打包再進行釋出: gulp -> xdk-cli publish ,將一個動作拆成了兩個
遺忘了一個怎麼辦?糾結了
不知道大家對微信開發者工具的上傳鉤子還有沒有印象,要實現的大概就是這麼個東西,
一個上傳前預處理的鉤子
這個實現起來也很簡單,首先給使用者的配置檔案(xdk.config.js)開一個可配置項:
// xdk.config.js
{
// 釋出鉤子
publishHook: {
async before(answer) {
this.spawnSync('gulp');
return Promise.resolve();
},
async after(answer) {
this.log('釋出後的鉤子執行了~');
return Promise.resolve();
}
}
}
複製程式碼
在publish-weapp.js中去識別鉤子即可:
// 前置鉤子函式
await userConf.publishHook.before.call(originPrototype, answer);
//上傳體驗版
let res = spawn.sync(cli, ['-u', `${versionConf.version}@${Config.output}`, '--upload-desc', versionConf.versionDesc], { stdio: 'inherit' });
// 後置鉤子函式
await userConf.publishHook.after.call(originPrototype, answer);
複製程式碼
當然,你需要判斷下鉤子是否存在,型別判斷,包括需要返回給使用者配置裡一些基本的方法和屬性,例如:日誌輸出,命令列執行等
我這邊以gulp為例,可以看到在publsih前先執行gulp
,後進行釋出,最後log出一行提示資訊
完全符合預期
解決環境變數切換問題
解決了自動上傳的問題,接下來就要解決環境變數切換的問題了,這裡還要借用下剛才寫的鉤子函式:
{
// 釋出鉤子
publishHook: {
async before(answer) {
this.spawnSync('gulp', [`--env=${answer.isRelease ? 'online' : 'stage'}`]);
return Promise.resolve();
},
async after(answer) {
this.log.success('釋出後的鉤子執行了~');
return Promise.resolve();
}
}
}
複製程式碼
以gulp為例,根據之前的回答的結果answer.isRelease,判斷是否為使用正式環境
如果你沒有使用編譯工具,也可以提出一個env的config檔案,使用fs模組直接重寫該環境變數
下面是一串虛擬碼:
目錄結構
- app (小程式專案)
- page
- app.json
...
- env.js
- xdk.config.js
- xdk.version.js
- envConf.js
// envConf.js
module.exports = {
['online']: {
appHost1: 'https://app.xxxxxxxxx.com',
appHost2: 'https://app.xxxxxxxxx.com',
},
['stage']: {
appHost1: 'https://stage.xxxxxxxxx.com',
appHost2: 'https://stage.xxxxxxxxx.com',
}
};
// xdk.config.js
publishHook: {
async before(answer) {
let config = require('envConf.js')[answer.isRelease ? 'online' : 'stage'];
fs.writeFile('./app/env.js', jsonFormat(jsonConf), (err) => {
if(err){
this.log.error('寫入失敗')
}else {
this.log.success('寫入成功);
}
});
return Promise.resolve();
}
複製程式碼
打包工具的問題還有環境變數的問題,我都沒有寫在cli裡面,是因為不同專案之間對環境變數的寫法,格式都不盡相同,具體要怎麼做還是要留給開發者自己來確定吧,這樣看起來更靈活一些
最後
總之利用腳手架解決了從釋出到上線的一連串問題,使得不再擔心因為切換環境導致線上bug,也不再擔心寫版本號寫錯的問題,確認線上環境這個環境也變成了一個非強需求的事情
整個上線流程也只需要:xdk-cli publish -> 提交稽核即可,而且整個程式碼也並不複雜,publish-weapp.js整個檔案算上註釋也就60行程式碼,何樂不為呢?
注:下篇會講一下如何做自定義指令,幫助小夥伴可以更自由的適配不同的專案~