為了避免開發人員開發完畢之後未正確簽入版控,因此實作一個檢查機制,當版控的檔案與產生出來的檔案不一致的時候,透過通知的機制告知開發人員
透過計算檔案的 checksum,比對兩個檔案的 checksum 即可得知是否相同,為了達到這個目的,需要做到下列事項
- 從 Git 取得程式原始碼
- 將前端編譯出來的程式複製到暫存的目錄
- 重新編譯前端程式,輸出至原來的路徑
- 比對暫存目錄、輸出目錄的檔案是否一致
- 若比對結果不一致,則發出通知
複製檔案
利用 node.js 的 fs-extra
套件來複製檔案,好處是透過 CLI 執行該程式,不管在專案的根目錄或是網站的目錄,都可以正確執行複製目錄的行為,所以 jenkins 的 cli 指令也不需要固定寫死
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| var fs = require("fs-extra"); const path = require("path"); const sourceDir = path.join(__dirname, "../Resource/Source"); const generateDir = path.join(__dirname, "../Resource/Bundle");
fs.copy(generateDir, sourceDir, function(err) { if (err) { console.log("An error occured while copying the folder."); return console.error(err); } console.log("Copy completed!"); });
|
重新編譯前端程式
透過已設定好的指令執行即可
比對目錄檔案 checksum 是否一致
透過 fs-magic
這個 node.js 的外掛來處理檔案 hash,並比對是否一致,依據最終的結果,透過回傳 EXIT Code 來告知 jenkins 任務的執行是否成功
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
| const _fs = require("fs-magic");
async function compareDirectories(sourceDir, generateDir) { let result = true; let errMsg = [];
const [sourceFiles, sourceDirs] = await _fs.scandir(sourceDir, true, true); const [generateFiles, generateDirs] = await _fs.scandir(generateDir, true, true);
if (sourceFiles.length !== generateFiles.length) { errMsg.push(`版控:[${sourceFiles.length}] 產出:[${generateFiles.length}]:目錄內檔案數量不同 `); result = false; } if (sourceDirs.length !== generateDirs.length) { errMsg.push(`版控:[${sourceDirs.length}] 產出:[${generateDirs.length}]:子目錄數量不同`); result = false; }
const hashes1 = await Promise.all(sourceFiles.map(f => _fs.sha256file(f))); const hashes2 = await Promise.all(generateFiles.map(f => _fs.sha256file(f)));
const lookup = {}; for (let i = 0; i < hashes2.length; i++) { const f2 = generateFiles[i].substr(generateDir.length);
lookup[f2] = hashes2[i]; }
for (let i = 0; i < hashes1.length; i++) { const f1 = sourceFiles[i].substr(sourceDir.length); if (!lookup[f1]) { errMsg.push(`[ERROR] ${generateDir} 目錄內 ${f1} 檔案不存在`); result = false; } if (lookup[f1] !== hashes1[i]) { errMsg.push(`[ERROR] [${f1}] checksum not match!`); errMsg.push(`[產 出]:[${lookup[f1]}]`); errMsg.push(`[版 控]:[${hashes1[i]}]`); result = false; } } return { result, errMsg }; }
module.exports = compareDirectories;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const compareDirectories = require("./compare.js"); const path = require("path"); const sourceDir = path.join(__dirname, "../Resource/Source"); const generateDir = path.join(__dirname, "../Resource/Bundle");
async function compareFiles() { let { result, errMsg } = await compareDirectories(sourceDir, generateDir); console.log(`result:${result}`); if (result) { process.exit(0); } else { process.exit(1); } }
compareFiles();
|
透過 exit code 回應執行結果成功或失敗,藉此控制 Jenkins Job 任務結果,可再接續其他下游專案運作
Jenkins 設定範例
新增一個 freeStyle 專案,透過 git 下載 source 完畢後,再新增執行 Windows 批次命令
1 2 3 4 5 6 7 8 9 10 11 12 13
| # STEP1 cd MyProject yarn
# STEP2 node MyProject\test\copyFiles.js
# STEP3 cd MyProject yarn build
# STEP4 node MyProject\test\compareFiles.js
|
之所以分開四個步驟,是因為放在同一個 shell script 區塊,執行 yarn 就會卡住後面的指令。