近日將幾個比較舊的 .NET 7 專案升級至 .NET 9,有些東西想記下來。
原由
之所以想到要把幾個舊的 .NET 7 專案升級至 .NET 9,主要是因為突然有個靈感,覺得其中一個 Windows Forms 應用程式很有機會能夠整合 AI agents 來實現某些用戶想要的功能。為了這個可能實現的點子,我覺得必須先把那個應用程式以及它依賴的第三方開源套件都升級至 .NET 9。
在那些需要升級的開源專案當中,有些是我自己寫的小工具,有的則是從既有的 GitHub 開源專案 fork 過來,包括:
那些放在 GitHub 上面的第三方開源專案大多已經沒有甚麼人在積極維護(這年頭誰還寫 Windows Forms 啊 XD),我也有 fork 一份自行維護,而且陸續改了一些 code。所以,這次把它們的 target framework 從 .NET 7 改為 .NET 9 之後,我決定把這些 fork repo 的關聯斷開(detach from the root repo),成為獨立的 repo。
以下是這次升級 .NET 版本過程中,我想記錄的幾個重點,主要是關於 GitHub 以及使用 Copilot 協助軟體開發的一點心得 。
GitHub fork
這裡要說兩件事(如果你已經知道便可跳過本節):
- GitHub 網站上有提供 detach from its roor repo 的功能了(以前沒有,至少我沒看到過)。
- GitHub 的 fork repo 能見度較低。
接著細說。
原本我是在
GitHub 的 online support 頁面提交 ticket 來請求協助將我的 fork repo 獨立出來。但是在最後一次提交 ticket 後,卻收到了跟之前不同的 email 回覆。信中告訴我:「我很幸運,因為現在 GitHub 網站有提供按鈕可以讓我自行完成這個操作了。」如下圖:
於是,我再去 GitHub 上查看,還真的有 detach 功能了 (到底...?! XD),如下圖。
此外,我這次也發現一個以前從來沒注意到的細節:在 GitHub 上,fork repo 能見度較低。具體來說,在 GitHub 上面以關鍵字搜尋 repository 時,搜尋結果會優先顯示 root repo,而 fork repo 往往被過濾掉而沒有出現在搜尋結果中(或排在很後面)。
GitHub Copilot + Claude Sonnet 3.7
我用的開發工具是 Visual Studio 2022,而且這次借助 GitHub Copilot (Agen mode) 搭配 Claude Sopnnet 3.7 來解決 .NET 9 breaking changes 所造成的編譯錯誤和警告。感覺很棒,因為節省了我許多自己摸索和找資料的時間。
建議的做法是繼承自 System.Exception 就好。除了這點,ApplicationException 類別有一個 constructor 是已經標示為 "過時",但 SourceGrird 專案用得挺多,導致編譯專案時出現一堆 warnings。於是,我把這類警告直接丟給 Copilot with Claude Sonnet 3.7 處理,而其回應總是侷限在「以 ApplicationException 為基礎類別的前提下」的解法來試圖消除編譯警告,但程式碼卻變得更複雜。
於是,我切換至工人智慧:把自訂 exception 改為繼承自 System.Exception,並刪除了那些過時的 constructor 的程式碼。花的時間不多,程式碼也更乾淨些。(這麼一來,我維護的這個 fork 大概也就我自己才敢用了 😂)
另外,也是靠著 AI agents 的幫助,不到半小時就把 CI/CD 工具從 AppVeyor 和 FlubuCore 改成 GitHub Actions。如果沒有 AI agents,我不知要花幾個小時才能搞定。不過,這當中有個小插曲,稍後說明原委,這裡先寫下重點:
在編寫 GitHub Actions 的 workflow(yaml 檔案)時,即使採用 Windows runner,只要碰到需要寫檔案路徑的時候,最好一律用 `/`,而不要用反斜線 `\`,以免 CI/CD pipeline 運行結果出現意外狀況。
這個小插曲的原委是這樣來的:我叫 Copilot 替我的 .NET 專案產生一個 GitHub Actions workflow。初次生成的檔案非常接近可用,只稍加修改就成功運行了。然而,當我測試其中的打包和 release 步驟時,卻發現失敗了。打包的步驟需要執行一個壓縮檔案的 PowerShell 命令,類似這樣:
Compress-Archive -Path .\publish\MyApp\* -DestinationPath .\publish\MyApp-v4.9.0.zip
GitHub workflow 執行到後面的 "Create GitHub Release" 工作時,會把上述命令產生的 .zip 檔案加入 GitHub repo 的 release 當中,供 user 下載。但這個建立 release 的步驟卻失敗了。錯誤訊息是:
Pattern 'publish\MyApp.zip' does not match any files.
我反覆檢查,明明 zip 壓縮檔案有產生,也放在正確的路徑底下。於是,我把這個錯誤訊息丟給 Copilot (Claude Sonnet 3.7),讓它幫我分析我的 worflow 配置檔案並找出修正的方法(我發現我好依賴 AI)。結果它一直繞圈子,問題始終未能解決。
後來,我問了 ChatGPT,問它這個錯誤訊息可能是甚麼原因造成的。ChatGPT 的答案頗詳細,總共給了四個建議,而第一個建議就對症下藥了:
原來,Copilot + Claude Sonnet 3.7 給我的解法一直都是用反斜線 `\` 來做為檔案路徑的分隔字元。這在某些 job 可以正常運行,但有的命令就會失敗,非得用 `/` 不可。(備註:這裡不是要說 Copilot 或 Claude 不好用,重點在於如何解決問題。)
使用 MinVer 自動管理版本
這次升級的幾個 .NET 專案都有使用 MinVer 來自動管理組件版本。我原本採用帶有前綴 "v" 的方式來替 repository 的 tag 命名,例如 "v4.2.0"。也就是說,每當要正式發布新版本時,就在 Git repository 中建立一個新的 tag,推送至遠端 GitHub 主機,使其觸發 GitHub Actions 來自動建置和打包,生成 NuGet 套件:
Run dotnet pack Src/SharpConfig.csproj --configuration Release --no-build --output ./artifacts /p:Version=4.2.0
8Successfully created package 'D:\a\SharpConfig\SharpConfig\artifacts\SharpConfig-huanlin.4.1.1-alpha.0.6.nupkg'.
但這麼一來,還得在 .NET 專案中額外加入 MinVer 的設定,才能讓這個帶有前綴 `v` 的 tag 命名方式正常運作。如下圖:
為了簡化配置,我後來決定不再使用前綴 `v`,而單純使用版本編號來替 tag 命名,例如 "4.2.0"。
結語
AI agents 用來學習和解決問題,經常可以節省大量時間。但要說完全直接使用 AI 工具來生成程式碼,現階段而言恐怕適用範圍和實用度都有待斟酌。故我心中不免疑問:「Vibe Coding 生成的 app 都是哪一類應用呢?」總覺得跟我實際工作要處理的問題還有不少距離(我脫節了嗎 XD)。
但,誰知道呢,也許在不久的將來,AI 真的能幫我們處理掉幾乎所有的 coding 工作,而且出錯了還自行診斷、反覆修正和測試。那就太棒了。(在那之前... back to work 🥹)
以上。要記錄的就這麼些小事。
Keep coding!
沒有留言: