1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Monorepo 倉(cāng)庫(kù)代碼質(zhì)量提升實(shí)踐

        共 7777字,需瀏覽 16分鐘

         ·

        2024-04-11 17:42

        一、背景

        Monorepo 是一種項(xiàng)目代碼管理方式,指單個(gè)倉(cāng)庫(kù)中管理多個(gè)項(xiàng)目,有助于簡(jiǎn)化代碼共享、版本控制、構(gòu)建和部署等方面的復(fù)雜性,并提供更好的可重用性和協(xié)作性。

        Monorepo 倉(cāng)庫(kù)包含多個(gè)項(xiàng)目,可能涉及跨業(yè)務(wù)方向乃至跨部門的開(kāi)發(fā)者,迭代頻繁且代碼量通常較大。公共代碼的調(diào)用方往往涉及多個(gè)項(xiàng)目,修改公共代碼的影響范圍廣,回歸成本高。因此可以從以下兩個(gè)角度出發(fā),建立代碼質(zhì)量保障機(jī)制,將代碼變更的風(fēng)險(xiǎn)攔截在開(kāi)發(fā)階段:

        • 自動(dòng)化測(cè)試:創(chuàng)建及更新 MR 時(shí)自動(dòng)運(yùn)行測(cè)試用例,確認(rèn)代碼變更對(duì)于歷史功能的影響
        • 人工 review:基于 Code Owner 機(jī)制,根據(jù)代碼修改范圍指定對(duì)應(yīng)的代碼負(fù)責(zé)人進(jìn)行 review。

        二、以 Git Submodule 的形式引入單元測(cè)試

        2.1 名詞解釋

        2.2 使用 Git Submodule 引入單測(cè)的原因

        從代碼倉(cāng)庫(kù)維度上隔離生產(chǎn)環(huán)境的業(yè)務(wù)代碼和開(kāi)發(fā)環(huán)境的測(cè)試代碼,各自獨(dú)立維護(hù)。一方面避免測(cè)試代碼影響生產(chǎn)環(huán)境,另一方面測(cè)試倉(cāng)庫(kù)的變更風(fēng)險(xiǎn)可控,可以采取更寬松的變更規(guī)則(如無(wú)需 review),降低測(cè)試代碼的變更成本。

        2.3 接入步驟

        2.3.1 初始化測(cè)試倉(cāng)庫(kù)

        測(cè)試倉(cāng)庫(kù)主要用于承載測(cè)試用例、jest 配置文件。

        2.3.2 主倉(cāng)庫(kù)引入測(cè)試模塊

        git submodule add xxx.git(測(cè)試倉(cāng)庫(kù)git地址) test(本地目錄)

        2.3.3 配置測(cè)試環(huán)境

        1. 待測(cè)項(xiàng)目下安裝測(cè)試依賴
        npm i -D @ies/eden-test
        1. 根據(jù)測(cè)試模塊(子模塊)是否放在待測(cè)項(xiàng)目下,有兩種配置方案
        • 方案一:子模塊放在待測(cè)項(xiàng)目的目錄下,在待測(cè)項(xiàng)目中運(yùn)行測(cè)試命令,即可執(zhí)行子模塊中的測(cè)試用例

          • 在待測(cè)項(xiàng)目中配置測(cè)試命令,將子模塊中的 jest 配置文件軟鏈到待測(cè)項(xiàng)目根目錄下,并執(zhí)行測(cè)試用例

            // src/infrastructure/package.json
            {
              "scripts": {
              // 首次使用需要初始化submodule
              "test:init""git submodule update --init && git submodule foreach git checkout origin/master && ln submodule/jest.config.js(子模塊中jest配置文件的路徑) jest.config.js && eden-test",
              // 已有子模塊后運(yùn)行單測(cè)
              "test""ln submodule/jest.config.js(子模塊中jest配置文件的路徑) jest.config.js && eden-test"
              }
            }
        • 方案二:子模塊不在待測(cè)項(xiàng)目的目錄下,需要將子模塊中的測(cè)試用例和測(cè)試配置文件軟鏈到待測(cè)項(xiàng)目下,確保測(cè)試過(guò)程中找到子模塊的測(cè)試用例

          • 編寫測(cè)試腳本 src/infrastructure/link.sh ,把測(cè)試子模塊的內(nèi)容(測(cè)試用例和 jest 配置)遞歸地軟鏈到待測(cè)項(xiàng)目下。

            由于 jest 支持文件軟鏈而暫不支持目錄軟鏈,因此此處采用遞歸創(chuàng)建文件軟鏈的方式,而非直接創(chuàng)建目錄軟鏈。

            #!/bin/bash
            # 遞歸創(chuàng)建軟鏈腳本

            # 軟鏈的源目錄
            source_dir=${1:-../../test/infrastructure/src/utils}

            # 軟鏈的目標(biāo)目錄
            target_dir=${2:-test/utils}

            # jest.config.js目錄
            config_dir='../../test/infrastructure/jest.config.js'
            if [ ! -f 'jest.config.js' ]; then
                ln -s $config_dir jest.config.js
            fi

            # 遍歷源目錄下的所有文件和目錄
            for file in $source_dir/*
            do
                # 獲取文件或目錄名
                filename=$(basename $file)

                # 如果是目錄,則遞歸調(diào)用腳本創(chuàng)建同名目錄
                if [ -d $file ]; then
                    mkdir -p "$target_dir/$filename"
                    bash $0 "$file" "$target_dir/$filename"
                fi

                # 如果是文件,則在目標(biāo)目錄下創(chuàng)建軟鏈
                if [ -f $file ]; then
                # echo "===from===" "$PWD/$file" "===to===" "$PWD/$target_dir/$filename"
                    ln -s "$PWD/$file" "$PWD/$target_dir/$filename"
                fi
            done

            echo "軟鏈創(chuàng)建完成!"
          • 配置測(cè)試命令,運(yùn)行創(chuàng)建軟鏈的腳本并運(yùn)行測(cè)試用例

            // package.json
            {
                "scripts": {
                    // 首次使用需要初始化submodule
                    "test:init""git submodule update --init && git submodule foreach git checkout origin/master && ./link.sh && eden-test",
                    // 已有子模塊后運(yùn)行單測(cè)
                    "test""./link.sh && eden-test"
                }
            }
          • 修改子模塊的 jest 配置,在運(yùn)行測(cè)試用例的過(guò)程中支持抓取軟鏈接

            // submodule/jest.config.js

            module.exports = {
                ...,
                watchmanfalse,
                  haste: {
                    enableSymlinkstrue,
                  },
            }

        2.3.4 CI 環(huán)境自動(dòng)運(yùn)行

        創(chuàng)建 CI 配置文件,定義兩個(gè)執(zhí)行步驟,分別為 Unit Test 和 codecov,其中 Unit Test 用于運(yùn)行單測(cè)、生成測(cè)試覆蓋率文件,codecov 用于定義前端頁(yè)面如何顯示測(cè)試覆蓋率。

        測(cè)試模塊的版本管理

        • 默認(rèn)的版本管理方式:

          子模塊被提交到主倉(cāng)庫(kù)時(shí),會(huì)記錄子模塊的 commit id。在下一次拉取子模塊時(shí)自動(dòng)切換到對(duì)應(yīng)的 commit id,從而實(shí)現(xiàn)子模塊的版本管理。因此理論上更新測(cè)試用例時(shí),需要去主倉(cāng)庫(kù)更新 commit id。

        • 預(yù)期效果&解決思路:

          為了達(dá)到主倉(cāng)庫(kù)無(wú)感知的效果,本方案在 CI 環(huán)境下會(huì)忽略 commit id,執(zhí)行切換分支的操作。切換原則為若測(cè)試倉(cāng)庫(kù)存在與當(dāng)前 MR 的源分支同名的分支,則使用同名分支的測(cè)試用例,否則使用測(cè)試倉(cāng)庫(kù) master 的測(cè)試用例。

        • 使用流程:

          在主倉(cāng)庫(kù) A 分支開(kāi)發(fā)時(shí)新增了公共方法(待測(cè)代碼),則需要在測(cè)試倉(cāng)庫(kù)起一個(gè)同名的 A 分支編寫對(duì)應(yīng)的測(cè)試用例,提交 MR 后觸發(fā)測(cè)試流水線,CI 環(huán)境下會(huì)拉取子模塊并切換到 A 分支運(yùn)行測(cè)試用例。在完成開(kāi)發(fā)后再分別將主倉(cāng)庫(kù)和測(cè)試倉(cāng)庫(kù)的 A 分支合入 master。

        運(yùn)行單測(cè)的核心命令

        # 初始化子模塊
        - git submodule update --init
        # 進(jìn)入子模塊目錄下切換分支
        - cd test/infrastructure
        # 優(yōu)先使用同名分支的測(cè)試用例,master的測(cè)試用例作為兜底
        - git checkout master
        - git show-branch $CI_EVENT_CHANGE_SOURCE_BRANCH &>/dev/null && git checkout $CI_EVENT_CHANGE_SOURCE_BRANCH || echo $CI_EVENT_CHANGE_SOURCE_BRANCH not exist
        # 回到待測(cè)項(xiàng)目下執(zhí)行測(cè)試用例
        - cd ../../src/infrastructure
        - mkdir -p test
        - cp -r ../../test/infrastructure/src/utils test
        - npm i
        - npm run test

        2.4 接入效果

        2.3.1 本地開(kāi)發(fā)

        主倉(cāng)庫(kù)和測(cè)試倉(cāng)庫(kù)的代碼獨(dú)立提交

        2.3.2 CI 環(huán)境自動(dòng)運(yùn)行

        創(chuàng)建 MR 后自動(dòng)運(yùn)行流水線,在 MR 界面查看測(cè)試用例運(yùn)行結(jié)果及覆蓋率。當(dāng)測(cè)試用例執(zhí)行失敗,或測(cè)試覆蓋率不達(dá)標(biāo)時(shí),阻塞 merge 操作。

        三、人工 review

        3.1 引入 Code Owner 機(jī)制

        3.1.1 為什么需要 Code Owner 機(jī)制

        我們?cè)谌粘i_(kāi)發(fā)時(shí),通常會(huì)約定一些 review 規(guī)則,如:

        • 修改公共代碼時(shí),存在一定風(fēng)險(xiǎn),需要對(duì)應(yīng)的模塊負(fù)責(zé)人 review。
        • 在代碼按領(lǐng)域組織的場(chǎng)景下,修改領(lǐng)域?qū)哟a需要對(duì)應(yīng)的領(lǐng)域負(fù)責(zé)人 review。

        在落地過(guò)程中,存在的問(wèn)題可能包括:忘記自己修改了公共代碼、不確定模塊負(fù)責(zé)人從而增加溝通成本等,因此需要從流程上規(guī)范 review 規(guī)則,對(duì)公共代碼的修改進(jìn)行嚴(yán)格把關(guān)。

        為了滿足更細(xì)粒度的準(zhǔn)入控制,引入 CODE OWNERS 機(jī)制,基于 change 的代碼變更添加 reviewer 并要求這些 reviewer approve 后才能合入。

        3.1.2 接入步驟

        在 CODEOWNERS 文件中定義 review 規(guī)則,每條規(guī)則包含匹配路徑及 reviewer。每行一個(gè)規(guī)則,從上到下匹配,后匹配到的規(guī)則會(huì)覆蓋先匹配到的規(guī)則。

        # 每行一個(gè)規(guī)則,從上到下匹配,后匹配到的規(guī)則會(huì)覆蓋先匹配到的規(guī)則
        # 例如:
        /sample_feature/ @user1 @user2
        *.js @user3
        /sample_feature/*.js @group1

        路徑語(yǔ)法同 gitignore:

        • 所有空行或者以 # 開(kāi)頭的行都會(huì)被 Git 忽略。

        • 可以使用標(biāo)準(zhǔn)的 glob 模式匹配。

          • 星號(hào)(*)匹配零個(gè)或多個(gè)任意字符;
          • [abc]匹配任何一個(gè)列在方括號(hào)中的字符(這個(gè)例子要么匹配一個(gè) a,要么匹配一個(gè) b,要么匹配一個(gè) c);
          • 問(wèn)號(hào)(?)只匹配一個(gè)任意字符;
          • 如果在方括號(hào)中使用短劃線分隔兩個(gè)字符,表示所有在這兩個(gè)字符范圍內(nèi)的都可以匹配(比如 [0-9] 表示匹配所有 0 到 9 的數(shù)字)。
          • 使用兩個(gè)星號(hào)(**)表示匹配任意中間目錄,比如a/**/z可以匹配 a/z, a/b/z 或 a/b/c/z 等。
        • 匹配模式可以以(/)開(kāi)頭防止遞歸。

        • 匹配模式可以以(/)結(jié)尾指定目錄。

        • 要忽略指定模式以外的文件或目錄,可以在模式前加上驚嘆號(hào)(!)取反。

        3.1.3 接入效果

        創(chuàng)建 MR 后,在 MR 界面會(huì)查看命中的各個(gè)代碼路徑匹配規(guī)則、對(duì)應(yīng)的 owner 及滿足情況。(需要確保目標(biāo)分支下已有 OWNERS 文件)

        3.2 CR 通知收斂到飛書話題群

        3.2.1 解決的問(wèn)題

        • 部分 MR 命中的 owner 規(guī)則比較多,需要在群里一一@或者私聊,存在一定的觸達(dá)成本
        • 現(xiàn)有的觸達(dá)方案中,通知內(nèi)容包含 MR 所有的狀態(tài)變更(發(fā)起、approve、評(píng)論、更新、合入等),因此通知比較頻繁、觸達(dá)效率較低

        3.2.2 解決思路

        • 將發(fā)送群通知提醒 review 的能力集成到 MR 流水線中,并支持通過(guò) MR 標(biāo)題中的 WIP 標(biāo)識(shí)來(lái)決定是否發(fā)送群通知。
        • 結(jié)合飛書話題群的功能,在發(fā)送群通知時(shí)@相關(guān)的 reviewers 及發(fā)起人,相關(guān)人員自動(dòng)訂閱話題,在話題下回復(fù)的消息均會(huì)以消息卡片的形式推送給相關(guān)人??梢杂糜谠?MR 狀態(tài)變更時(shí)提醒相關(guān)人員,同時(shí)將 MR 相關(guān)的討論收斂到一個(gè)話題下,提升溝通效率。

        四、總結(jié)

        在代碼評(píng)審階段,根據(jù)代碼修改范圍邀請(qǐng)?jiān)u審人、運(yùn)行單元測(cè)試,可以在較大程度上保障代碼功能符合預(yù)期,降低代碼修改帶來(lái)的質(zhì)量風(fēng)險(xiǎn)。后續(xù)可以嘗試與 AI 結(jié)合,例如集成大模型自動(dòng)生成測(cè)試用例、review 代碼的能力,進(jìn)一步降低質(zhì)量保障方案的實(shí)現(xiàn)成本,從而提升研發(fā)效率。


        瀏覽 38
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            欧美三级片中文字幕在线观看 | 亚洲婷婷网 | 簧片网站在线观看 | 靠逼视频免费的 | 日日人人 | 欧产国产韩产日产不卡成人网站 | 国产传媒一区 | 日韩女同毛片区二区三区五区 | 狠狠躁夜夜躁人人爽天天高潮 | 亚州AV无码 |