2022年1月17日 星期一

Azure DevOps in Action - 在CI Pipeline中發佈NuGet套件

我們在前面的章節曾經介紹過,軟體重用性提升一個很大的要素就是套件化。時至今日,不管你用哪一種軟體開發語言,大概都離不開各式各樣的套件庫。

因此,我們在前面談到Azure Repos的章節中,曾經約略介紹過如何建立套件,也介紹過利用Azure DevOps的Artifacts功能來建立的私有(Private)套件庫:
enter image description here

你大概已經知道,我們可以將組件(.dll)封裝成具有版號的套件,以便於分享給團隊甚至網際網路上所有開發人員來使用。只是先前,我們封裝好的套件(.unpkg)都是以手動方式上傳到 nuget.org,我們接著來看看,如何自動化完成這件事情。

設計自動發佈套件的Pipeline

一個自動化建置出NuGet套件並且上傳的CI Pipeline並不難設計,就建立 .net core的套件而言,你可以直接使用 ASP.NET Core的範本來修改即可:
enter image description here

接著將範本中的Publish改為(Pack):
enter image description here

因為套件本身不是網頁,因此沒必要『Publish』出什麼,倒是需要產生出 .unpkg檔案,因此我們需要執行dotnet的Pack command。

然而光Pack還不夠,你還得上傳到 nuget.org,這部分則可以透過 NuGet push task(下圖B)來完成:
enter image description here

上圖中的NuGet push task,其實是在運行NuGet的push command(上圖D),該task會在預設(上圖E)的位置嘗試尋找我們先前透過 Pack Nuget命令所建置出的 .nupkg 檔案,然後將其上傳發送到指定的套件庫。

因此,我們需要進行上傳位置的設定。

第一次連線時你必須點選(上圖F)右方的『+New』按鈕,接著會出現底下畫面:
enter image description here

在出現的畫面中,我們必須輸入ApiKey(上圖C)以便於具有連線到NuGet的權限,後續可以讓Pipeline把套件檔案上傳到Nuget。

一般來說,公開的NuGet套件庫位於:
https://api.nuget.org/v3/index.json

如同先前介紹過的,你只需要有Microsoft Account即可登入並且上傳套件。請先用你的Microsoft Account登入nuget.org
enter image description here

登入後你會發現右上角的頭像圖示選單中,就有一個API Keys選項,透過該選項,你就可以取得一組API Key。

取得API Key之後,把該Key填入下圖C的位置,設定好連線名稱(下圖D):
enter image description here

按下Save鈕之後即可。

建立好連線,並且整個Pipeline被運行之後,你應當會看到自動建置出的套件被上傳到nuget了:
enter image description here

如果要上傳到Azure DevOps的Artifacts,其實也很簡單:
enter image description here

只需要把Task的Target feed選項設為『This organization/collection』即可。

備註:

如果讀者想自己試試看,您可以參考筆者在Ggithub上的source code,位於:https://github.com/isdaviddong/HealthMgrPackageDemo.git
將其Clone到您的專案後,請修改底下檔案:
HealthMgrPackageDemo/HealthMgr.csproj
將其中程式碼 HealthMgr-[yourName] 中的 [yourName] 換掉,成為您的姓名(必須是全球唯一值),因為這個值會影響套件的ID,而該ID在整個nuget平台必須是唯一的,否則將無法上傳。

2022年1月10日 星期一

頻繁交付已不是問題,真正的挑戰在於品質

DevOps Pipeline image from Microsoft

上面這張圖,是我在講 Azure DevOps 的時候,時常跟學員分享的Pipeline,裡面粗略的談到了CI/CD Pipeline及其前段(版控)與後段(持續監控)所涉及的相關技術,可以讓對於DevOps完全沒概念的學員得以稍微一窺究竟。

但每當我繼續問學員:『你現在知道有這些內容了,那…CI/CD的目的到底是什麼? 實現CI/CD對於企業來說究竟有何好處呢? 』學員突然被問到,有時一下子會無法立刻反應過來。

我繼續說到:『倘若,CI Pipeline的主要目的是為了實現持續整合,而實現持續整合的主要目的則是為了持續交付。那…為何需要頻繁交付呢?』

過去一年,你一定有上網預約疫苗的經驗,如果沒有,你大概也有上網登記五倍券還是某種OO券的經驗,再沒有,你疫情期間總有過上網購物吧。

倘若,你上網登記或購物時,網站突然有問題,或是購物車的金額計算不太正確,你通知了網站營運單位,他們也告知您會立刻著手處理,這時候的你,會希望網站多久可以更新或修復?

一小時? 一天? 還是一兩個月?

同學幾乎都跟我說:『立刻,不然我就換一家網站購物囉~』。

是啊,立刻。

既然我們都這麼要求其他人,那我們自己所建立起來的網站呢? 能不能經得起這樣的要求? 當我們的網站有問題,或是客戶提出新的需求時,我們能夠多快的將新功能或修正交付到用戶手上?

而且你知道的,緊急狀況下,往往兵荒馬亂,即便你知道程式碼在某個地方可能有錯,但這段程式碼最初不是你寫的,你敢立刻改嗎? 你怎麼知道不會改了這邊,就壞了另一邊? 你怎麼知道這個修正會不會引起其他額外的副作用? 有些時候,程式碼明明在你的電腦上是好的,但整合了團隊中其他人開發的程式碼之後,佈署到測試機上,就是無法運行,怎麼回事呢!?

就算開發人員真的把所有問題都在測試機上改好了,是不是還要經過QA的人工測試把關才能交付給客戶呢? 但測試需要時間啊,如何才能實現『立刻』將成品交付給客戶?

上面這些,都是CI/CD想幫助你解決的問題。

持續交付,聽來很容易。也確實,因為技術的進步,現在要快速的把更新後的成品佈署到正式機上讓用戶使用,也許真的也不難。但你有把握在『快速』的同時,還能保有高品質與安全性嗎? 你有勇氣讓開發人員把程式碼修改完之後,透過Pipeline『全自動』的直接上版到正式機上嗎?

從你收到bugs或需求,一直到交付到用戶手上的這一刻,你能夠多快呢? 你的交付頻率,可以達到一周數次甚至一天數次嗎?

沒有持續整合,就沒有頻繁交付

也是,一周數次或一天數次的高品質交付,在幾年前聽起來似乎有點不可思議,但現在市場上很多網站或是應用服務,正以這樣的速度在和你的產品競爭,而你心中理想的交付,想要多頻繁呢?

然而,頻繁交付不是問題,真正的挑戰其實在於品質

現在很多網站或軟體服務的廠商,更新bugs也是挺『頻繁』的。只是這個頻繁,完全是『人工』所堆積出來的。當碰到問題,要求工程師加班熬夜立刻解決,一旦改好程式碼,開發人員自己在電腦上隨便按兩下測試一下,接著就直接把成品手動複製貼上到正式機,然後就…祈禱不要再出問題。這也難怪綠色包裝的乖乖會成為長銷商品了。

吃燒餅哪有不掉芝麻的,有bugs在所難免,特別是高壓又加班的緊張環境下,品質肯定會大打折扣,但工程師就在這樣的輪迴下一天天的過著日子。是啊,大夥拚著新鮮的肝,快速地把一堆含有潛在問題的產品交付到用戶手中,然後再快速的修復bugs,然後再快速的收到用戶傳來的新bug,然後…就這樣日復一日、年復一年,你覺得很有趣嗎?

頻繁交付的基礎,是頻繁的程式碼整合

而且,我們必須要在CI Pipeline當中,設法加入各種快速的檢查,以確保持續維持著高品質的產出,前面提到的單元測試,就是其中之一。但單元測試只是基本,除了單元測試之外,我們還應該要做靜態程式碼掃描,還應該要做套件的安全性掃描,我們要讓團隊適當地做Code Review,並且對程式碼的品質有方法、有步驟的持續進行提升,這些都是持續整合要做的事情,都做到了,你『才』算是有了一個持續交付的基礎。

沒找到真正的需求,就沒有有價值的成果

是不是這樣就夠了,差不多,但還缺一個重點,就是『需求』。

軟體開發的一切都是從需求來的,如果我們對需求沒有好的管理,我們的快速交付只是徒勞,有點像是薛西弗斯那個巨大的石頭,我們只是一次又一次,一再一再的奮力把成品快速的交到用戶手上,但卻沒辦法讓用戶滿意 – 如果你忘了需求才是一切的核心。

而需求是得要被探索和釐清的,特別是這個快速變化的時代。時間有限、資源有限,當我們想要快速將成果交付到用戶手上的同時,我們必須和時間賽跑,如何能夠快速地將用戶最需要的功能,在第一時間『先』交付到用戶手上,然後『再』持續慢慢地補齊用戶想要的其他功能,如何在交付功能之後,持續的蒐集用戶的真實反饋,調整開發的優先順序,把用戶真正需要的功能先實做出來,這其實是一門藝術。困難,但必須。因為找到真正能幫用戶產生價值的需求,才是軟體開發一切的根本。

持續高品質、同時快速的交付,是近代軟體開發一個很大的挑戰。現在我們已經有相當好用的工具(像是Azure DevOps)來幫助我們,作為頻繁交付的基礎。如果你還沒有開始,那如何善用工具來實現高品質的持續交付與整合,是你今年肯定該面對的議題。

2021年12月19日 星期日

Azure Cognitive Services - Speech to Text Demo 語音轉文字功能

昨天,在 .net conf 2021,明明講的主題是框架設計的延續 - 套件設計,但其中六七個demo當中,大家看起來最有反應和效果的反而是底下這個語音轉文字的CLI Demo。
enter image description here

影片:

這是一個透過CLI呼叫的語音辨識的服務,挺有趣吧,我只是把它變成command line tools, 也就是CLI工具,這個demo只是一個我想做的語音助理的一半,後半段沒demo出來的是一個語音助理的雛型,也就是透過語言來控制電腦。(還需要整合LUIS以及掛上特定 intent 的 actions)

若想要用語音來控制電腦,第一步當然是辨識語音,你可能沒想到過,現在即便 command line, console app也可以辨識語音(Speech to Text)。

拜 azure cognitive 所賜,如今這已經是簡單到不行的技術。主要的程式碼只有底下這幾行:

async static Task FromMic(SpeechConfig speechConfig)
{
	using var audioConfig = AudioConfig.FromDefaultMicrophoneInput();
	using var recognizer = new SpeechRecognizer(speechConfig, "zh-tw", audioConfig);
	Console.WriteLine("嗨~ 請透過語音下達指令...");
	var result = await recognizer.RecognizeOnceAsync();
	Console.WriteLine($"語音命令 = '{result.Text}' ");
}

我把整套 CLI tools的source code放 github上了:
https://github.com/isdaviddong/demo-listenup-VoiceCommand
當然你得自己換掉 Namespace 與 Azure Cognitive Services 的key。

如果要申請 Azure Cognitive Services 的 語音轉文字服務,可以參考:
https://portal.azure.com/#create/Microsoft.CognitiveServicesSpeechServices

enjoy it~

設定 unit test的code coverage report

在預設的 Azure DevOps Pipeline範本當中,針對 .net core的專案,你可以透過 dotnet task來運行單元測試,使用的指令是 dotnet test。底下是Yaml code, 這是一個很標準的做法:

steps:
- task: DotNetCoreCLI@2
  displayName: Test
  inputs:
    command: test
    projects: '$(Parameters.TestProjects)'
    arguments: '--configuration $(BuildConfiguration)'

但使用預設的dotnet rest,並不會產生code coverage報表,雖然我們並沒有想追求很高的unit test code coverage,但出一下報表對我們來說還是很具有參考價值的。

如果你也想要檢視code coverage報表,可以做底下調整。

step 1: 修改 dotnet test task

steps:
- task: DotNetCoreCLI@2
  displayName: Test
  inputs:
    command: test
    projects: '$(Parameters.TestProjects)'
    arguments: '--configuration $(BuildConfiguration) --collect:"XPlat Code Coverage" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=cobertura'

你會發現在參數的地方,我們加上了 --collect 等敍述,主要是告知 dotnet task運行test的時候,要蒐集相關資訊。

step 2: 增加Publish code coverage results task

steps:
- task: PublishCodeCoverageResults@1
  displayName: 'Publish code coverage from $(Agent.TempDirectory)/**/coverage.cobertura.xml'
  inputs:
    codeCoverageTool: Cobertura
    summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml'

Publish code coverage results task可以幫助我們把報表跟pipeline的GUI做一個整合,呈現出底下這樣的視覺效果:
enter image description here

如此一來,就有可以參考的 code coverage report囉。

底下是操作影片:

===========

相關課程:

敏捷開發專案管理與Azure DevOps實戰
https://www.studyhost.tw/NewCourses/ALM

2021年11月28日 星期日

敏捷,其實很簡單

enter image description here

DevOpsDays 2021的會場,有個活動叫專家面對面好像(具體叫啥我忘了),主要是讓與會者能夠和現場講師有更多互動。當天分享結束後,主辦單位請我去那坐一下(我當然也從善如流的照做了),正當我回答完幾位學員的問題,準備離開的時候,有一位年紀比我稍長的先生坐了下來,有條不紊的介紹完自己的團隊與專案後,開口問了幾個問題。

其中,有一個很關鍵,大概是這樣的內容:『如果團隊手上非常多插單的情況,導致每個迭代都無法正常進行,這樣適合 run scrum嗎?』根據多年的經驗,這個問題肯定只是表象,背後一定有著其他沒說出來的狀況。

我想了想,反問他:『你們的迭代設定多長?』
『一周或兩周,每個團隊有點不同』他回答。

我疑惑的問:『所以用戶的需求等不及一周的時間? 非得立刻插單不可?』『因為都是一些非常嚴重bug,不立刻處理不行』他說

『那你們真正該面對的不是插單的問題,而是整個專案已經快失控了,這樣等於每天都在救火啊?』我看著他。
他有點不好意思的說:『的確是這樣的。』

『那我的建議會是…』我認真的回答他道:『你們應該停止任何新功能的開發,先把所有的bugs解決,然後找出系統架構上或開發上的核心問題,到底~是什麼原因導致系統bugs層出不窮? 先著手解決它,別讓團隊陷在持續救火的處境裡…』

如果團隊每天都必須救火,那不管你 run 什麼方法都是對專案沒有幫助的,敏捷或scrum的導入不會立刻改變團隊的體質,如果大家的習慣不改變,團隊面對的困境還是會持續僵在那裏。

『但是,如果不開發新功能,這樣專案就不會有進度了啊?』他似乎有些擔憂這個。

如果你不停止開發新功能,去面對團隊本該處理的核心問題,這樣你看到新功能的進度,也只是個假象,終有一天,層出不窮的bugs會在專案的後期吃掉你所有的進展,最終,你會無法交出任何一個功能給客戶。

『恩,我知道』他面有難色地回答。說完謝謝之後,就皺著眉頭離開了。

我知道他心裡的考量和難處。

回到根本,敏捷其實很簡單–就是務實的面對問題,努力交付出用戶真正可以用的軟體。但,在這世上,總是有很多事情,特別是技術面以外的事情,並非那麼的務實。有時候我們的思慮,可能還包含了人情、績效、政治…等等各種其他方面的考量。這時候,問題就被我們搞複雜了。

就如同我在DevOpsDays中說的,敏捷一直都很簡單,但讓一切複雜的,是人。如果我們把專案的成果建築在不穩固的基礎上,不管你的專案看似多麼地 on schedule, 一夕之間都有可能歸回無有。

相關課程:

敏捷開發專案管理與Azure DevOps實戰
https://www.studyhost.tw/NewCourses/ALM

2021年11月25日 星期四

Azure DevOps in Action - 實現PR觸發的CI自動化建置

在上Azure DevOps課程的時候,學員問了一個很好的問題。

如果我們採用 Feature Branch,那你會走一個底下這樣的團隊合作流程:
enter image description here

上圖中有一個很重要的部分是,在PR之後所觸發的自動化佈署。

也就是,在feature branch分支被commit/push準備合併到主線前,我們會透過PR進行code review和discussion,過程中當然應該要先針對分支進行build才對呀。如果 build fail 了,那或許根本沒啥好討論了,整個PR直接給個comment然後reject掉就得了。

所以,分支(特別是feature branch)在走PR合併回主線前,針對分支的auto build非常重要,但,這要如何實現?

在Azure DevOps中,是透過 branch policy來實現的:
enter image description here
你只需要設定特定分支(例如master)的branch policy,把開關打開,設定任何從只分支建立出的分支(像是feature branch),PR後都會觸發個定的build pipeline就行了。

enter image description here

如此一來,圖中PR就會自動觸發該分支的build,這樣,我們的repo owner或是reviewer就可以在code review之前,看看build是否成功:
enter image description here
當然,在build pipeline中,我們也可以先做像是 SonarCloud / Checkmarx 之類的程式碼掃描,如此一來,整個團隊的開發協同運作流程,就更加迷人了。

相關課程:

敏捷開發專案管理與Azure DevOps實戰
https://www.studyhost.tw/NewCourses/ALM

2021年10月25日 星期一

+壓力-身段×堅強÷任性

#一起看廣告

昨天晚上,做了個夢。
我夢到在駕駛座上睡著了,然後被後座的其他乘客叫醒,乘客急急忙忙地跟我說:『ㄟㄟ,David,你睡著了啦!! 車還在開耶…』

我回答:『對耶,啊…有自動駕駛啦…』
然後我就繼續睡了…

早上起床突然想到,跟老婆說。
她說:『好啦,收到,我知道你有多想買自動駕駛的車了…』

古人說:『日有所思,夜有所夢。』可能真的是因為,這陣子看了好多汽車廣告。

最近看到 Ford 的廣告拍得很好,分別是林依晨的 New Focus (很值得看)

還有先前張鈞甯的 KUGA: (超好看)

後來又補一個:

上面這兩個廣告我都超推,不看你會後悔。但某天,我看著看著,卻讓我回憶起十多年前的這一支廣告。總覺得 Ford 的車怎麼樣我不知道(沒開過),但廣告總是拍得很有意境:

+壓力-身段×堅強÷任性
換算之後,你是否還記得真實的自己?

當初看上面這則廣告時,自己還是個帶著無限衝勁,剛站穩在職場上的少年。

但,這十幾年(也才十幾年),很多事情都改變了。環境變了,世界也變了。傲氣收斂成淡淡的執著,衝動則被多慮的思緒給撫平了。

年輕人總是想『做自己』(相信我,這並非時下青年的特色,每一個世代都是)。但,到了最近這個年紀,才發現,原來做自己之前,還得先找到自己。很顯然的,年輕時不顧後果地去凸顯自己的任性,並不是做自己。

當你找到並認識自己是誰,明白自己的能力和限制,還能無畏著外界的眼光,放下纏累自己的包袱,不跟他人比較、不被世俗的成功定義,還有勇氣和餘力踏上自己想走的路。持續走下去…這樣,才是做自己。

『做自己』要的不單單只是一股衝勁,而是認真的認識並面對自己的個性和缺點、堅持著持續超越和挑戰自己的毅力。

熱門文章