<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>鳴人， 作者 泰克哪裡去</title>
	<atom:link href="https://tech.uccu.website/author/naruto/feed" rel="self" type="application/rss+xml" />
	<link>https://tech.uccu.website/author/naruto</link>
	<description>一個科技相關的隨手記錄網站</description>
	<lastBuildDate>Sat, 15 Oct 2022 14:36:45 +0000</lastBuildDate>
	<language>zh-TW</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.1</generator>
<site xmlns="com-wordpress:feed-additions:1">119574712</site>	<item>
		<title>【2022鐵人賽】2022 iThome鐵人賽結尾</title>
		<link>https://tech.uccu.website/2022ironman-day30-ending.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=2022ironman-day30-ending</link>
					<comments>https://tech.uccu.website/2022ironman-day30-ending.html#respond</comments>
		
		<dc:creator><![CDATA[鳴人]]></dc:creator>
		<pubDate>Sat, 15 Oct 2022 14:36:40 +0000</pubDate>
				<category><![CDATA[2022鐵人賽]]></category>
		<category><![CDATA[iThome鐵人賽]]></category>
		<category><![CDATA[2022ironman]]></category>
		<guid isPermaLink="false">https://tech.uccu.website/?p=2105</guid>

					<description><![CDATA[<p>時光飛逝，轉眼間已經來到30天連續發文的最後一天，這篇文章成功發送出去之後，就代表今年的鐵人賽30天不中斷發表 ... <a title="【2022鐵人賽】2022 iThome鐵人賽結尾" class="read-more" href="https://tech.uccu.website/2022ironman-day30-ending.html" aria-label="Read more about 【2022鐵人賽】2022 iThome鐵人賽結尾">閱讀全文</a></p>
<p>這篇文章 <a href="https://tech.uccu.website/2022ironman-day30-ending.html">【2022鐵人賽】2022 iThome鐵人賽結尾</a> 最早出現於 <a href="https://tech.uccu.website">泰克哪裡去</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>時光飛逝，轉眼間已經來到30天連續發文的最後一天，這篇文章成功發送出去之後，就代表今年的鐵人賽30天不中斷發表文章的挑戰也成功了，連續兩年參加iThome的鐵人賽都能夠完賽也是一件滿令人感動的事情。</p>



<p>回想今年一直拖到最後一刻才決定報名參加，隔天就是必須發表第一篇文章的最後期限，當時都還沒有想好這30天的文章內容要如何安排，只能且戰且走的方式先報名出去，接著花了幾天的時間邊寫文章邊思考一下以今年報名的題目有哪些內容可以寫，先列出了大概的文章標題，每天再按照已經列出來的文章標題目錄去著手。就這樣，一天接著一天過去了，最終來到了最後一天…</p>



<p>今年和去年都是以Azure DevOps為主題來寫文章，而這兩年所寫的內容也都是我實際在工作上使用到的東西，只是文章中將工作相關的內容去除掉了，每篇文章的內容和截圖幾乎都是完全重新再做一遍，有些地方太複雜還要想辦法簡化一下，不然每天要寫一篇文章實在是有點困難…(這是稱之為「挑戰」的原因吧！)</p>



<p>其實走向DevOps的領域最困難也最具挑戰性的應該就是需要面對與熟悉很多不同的系統與工具，各家的DevOps系統搭配上每間公司的產品、政策與流程，就像是玩積木一般，每個人都有他的創意，組合出來的東西和樣子都長得不一樣。同樣類似的需求，在設計上也有許多的不同之處，如同烹飪一般，大家都是做同一道菜，但是選用的材料也有差異，這就像今年其中一篇文章「<a href="https://tech.uccu.website/2022ironman-day22-task-or-cli.html" target="_blank" rel="noreferrer noopener">使用Task與CLI的抉擇</a>」所提到的概念一樣，同樣的需求可以透過很多不同的方式達成，可以用內建的Task也可以用Script來完成，各自的色、香、味又是如何，真正品嚐的人才會知道了。</p>



<p>回頭想想，參加鐵人賽的時期總是我個人的部落格產出文章最多的時期，平常下班回家沒事大概只想耍廢，上班又不可能拿來寫文章，所以總是很佩服固定有內容產出的大大們。</p>



<p>這兩年以Azure DevOps為主題所寫的文章內容，同樣有發佈在我的部落格，2013年的鐵人賽雖然也有發佈在部落格，不過搬家沒有回復資料，所以就看不到了。也因為那時候是從部落格貼上內容到鐵人賽，圖片的連結也是取自部落格放圖片的位置，現在從當年的鐵人賽文章內容也會發現破圖的現象，所以這兩年寫在鐵人賽文章中的圖片都是獨立另外上傳在iThome的系統中，這樣就不會再發生部落格掛掉卻在鐵人賽文章中破圖的情況了。</p>



<p>如果各位對於部落格的文章有興趣，或是想在鐵人賽結束之後看看有沒有「第31天」之後的內容，也歡迎過段時間來到部落格逛逛。</p>



<p>對了，因為DevOps牽涉到的面向很廣，公司在轉型的過程中也要列出明確的Roadmap，近期也開了一個新的職缺，想要找人來和我一起在公司推動相關的工作，如果您有興趣，也可以私訊給我聊聊進一步的資訊。</p>



<p>最後，祝大家工作都順利、愉快！</p>



<p>終於完結啦~</p>
<p>這篇文章 <a href="https://tech.uccu.website/2022ironman-day30-ending.html">【2022鐵人賽】2022 iThome鐵人賽結尾</a> 最早出現於 <a href="https://tech.uccu.website">泰克哪裡去</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://tech.uccu.website/2022ironman-day30-ending.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2105</post-id>	</item>
		<item>
		<title>【2022鐵人賽】Power Automate整合Microsoft Forms：Teams通知</title>
		<link>https://tech.uccu.website/2022ironman-day29-power-automate-with-forms.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=2022ironman-day29-power-automate-with-forms</link>
					<comments>https://tech.uccu.website/2022ironman-day29-power-automate-with-forms.html#respond</comments>
		
		<dc:creator><![CDATA[鳴人]]></dc:creator>
		<pubDate>Fri, 14 Oct 2022 10:27:13 +0000</pubDate>
				<category><![CDATA[2022鐵人賽]]></category>
		<category><![CDATA[Azure DevOps]]></category>
		<category><![CDATA[iThome鐵人賽]]></category>
		<category><![CDATA[2022ironman]]></category>
		<category><![CDATA[azure devops]]></category>
		<category><![CDATA[microsoft forms]]></category>
		<category><![CDATA[microsoft teams]]></category>
		<guid isPermaLink="false">https://tech.uccu.website/?p=2129</guid>

					<description><![CDATA[<p>昨天我們利用Microsoft Forms建立了新專案的申請表單，在文章中提到選擇微軟的Forms而不是Goo ... <a title="【2022鐵人賽】Power Automate整合Microsoft Forms：Teams通知" class="read-more" href="https://tech.uccu.website/2022ironman-day29-power-automate-with-forms.html" aria-label="Read more about 【2022鐵人賽】Power Automate整合Microsoft Forms：Teams通知">閱讀全文</a></p>
<p>這篇文章 <a href="https://tech.uccu.website/2022ironman-day29-power-automate-with-forms.html">【2022鐵人賽】Power Automate整合Microsoft Forms：Teams通知</a> 最早出現於 <a href="https://tech.uccu.website">泰克哪裡去</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>昨天我們利用Microsoft Forms建立了新專案的申請表單，在<a href="https://tech.uccu.website/2022ironman-day28-microsoft-forms.html" target="_blank" rel="noreferrer noopener">文章中</a>提到選擇微軟的Forms而不是Google表單的原因，主要就是為了透過Power Automate建立自動化的流程，把表單的資料自動發送到Microsoft Teams的頻道中，這樣就不用時時刻刻留意有沒有人填寫了表單，或是通知的Email被歸到垃圾信件匣當中而沒有看到。</p>



<p>先來看看成果的範例吧！</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-recalc-dims="1" fetchpriority="high" decoding="async" width="445" height="642" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/PowerAutomateNotifyTeams.png?resize=445%2C642&#038;ssl=1" alt="" class="wp-image-2133" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/PowerAutomateNotifyTeams.png?w=445&amp;ssl=1 445w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/PowerAutomateNotifyTeams.png?resize=208%2C300&amp;ssl=1 208w" sizes="(max-width: 445px) 100vw, 445px" /><figcaption>表單填寫資料自動發送到Teams</figcaption></figure></div>


<p>訊息的格式或排版是可以自己定義調整的，至於欄位的部份就是看當初表單是怎麼設計的囉！</p>



<p>首先，我們進入<a href="https://powerautomate.microsoft.com/zh-tw/" target="_blank" rel="noreferrer noopener">Power Automate的頁面</a>，登入帳號進入到管理介面之後，在左邊的選單選擇「範本」，然後在搜尋框裡面輸入「teams」，就可以找到和Microsoft Teams相關的範本可以拿來修改使用。</p>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" decoding="async" width="1024" height="768" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/PowreAutomateSamples-1024x768.png?resize=1024%2C768&#038;ssl=1" alt="" class="wp-image-2134" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/PowreAutomateSamples.png?resize=1024%2C768&amp;ssl=1 1024w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/PowreAutomateSamples.png?resize=300%2C225&amp;ssl=1 300w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/PowreAutomateSamples.png?resize=768%2C576&amp;ssl=1 768w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/PowreAutomateSamples.png?w=1058&amp;ssl=1 1058w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>從篩選出來的Teams範本中，選擇「在提交新回應時通知小組」，也就是在上圖中右下角的那個項目，可以稍微看一下它的小icon是不是Microsoft Forms和Microsoft Teams的圖樣。點擊進入之後會如下圖，系統會在兩個系統中使用你的帳號進行作業，點擊「繼續」按鈕授權之後就可以進入到設計畫面了。</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-recalc-dims="1" decoding="async" width="660" height="719" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/SendMsgToTeamsWhenFormReceiveData.png?resize=660%2C719&#038;ssl=1" alt="" class="wp-image-2135" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/SendMsgToTeamsWhenFormReceiveData.png?w=660&amp;ssl=1 660w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/SendMsgToTeamsWhenFormReceiveData.png?resize=275%2C300&amp;ssl=1 275w" sizes="(max-width: 660px) 100vw, 660px" /></figure></div>


<p>下面這張圖就是設計的頁面，都是簡單的挑選幾個選項就可以完成的事情，主要重點就是選擇要將內容發送到Teams的哪一個頻道，然後在最下面要傳送到Teams中的訊息呈現樣式，可以把它弄得很整齊漂亮，不過我個人是覺得資料有呈現出來就可以了。</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="500" height="1024" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/PowerAutomateFlowDesignSample-500x1024.png?resize=500%2C1024&#038;ssl=1" alt="" class="wp-image-2137" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/PowerAutomateFlowDesignSample.png?resize=500%2C1024&amp;ssl=1 500w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/PowerAutomateFlowDesignSample.png?resize=146%2C300&amp;ssl=1 146w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/PowerAutomateFlowDesignSample.png?resize=768%2C1574&amp;ssl=1 768w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/PowerAutomateFlowDesignSample.png?resize=749%2C1536&amp;ssl=1 749w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/PowerAutomateFlowDesignSample.png?w=909&amp;ssl=1 909w" sizes="auto, (max-width: 500px) 100vw, 500px" /></figure></div>


<p>訊息呈現的樣式和內容搞定之後，選擇下面或右上角的儲存就搞定了，這時候就可以回到表單去填寫一筆資料看看，如果有在設定的頻道中收到訊息的話，那就是成功了。</p>



<p>透過這樣的方式就可以讓申請者直接進入表單填寫的頁面，當他們填寫完畢之後直接就會在Teams上面通知，若是有流程上需要配合的人員，例如某些步驟需要別的同事協助作業，完成之後再繼續其它的動作，這時候也可以直接在機器人發送的訊息下面使用回覆訊息的功能，然後@ 那位同事，整個搞定之後也可以回覆該訊息串記錄一下那張申請表已經完成了。</p>
<p>這篇文章 <a href="https://tech.uccu.website/2022ironman-day29-power-automate-with-forms.html">【2022鐵人賽】Power Automate整合Microsoft Forms：Teams通知</a> 最早出現於 <a href="https://tech.uccu.website">泰克哪裡去</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://tech.uccu.website/2022ironman-day29-power-automate-with-forms.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2129</post-id>	</item>
		<item>
		<title>【2022鐵人賽】Microsoft Forms：建立Azure DevOps專案申請表</title>
		<link>https://tech.uccu.website/2022ironman-day28-microsoft-forms.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=2022ironman-day28-microsoft-forms</link>
					<comments>https://tech.uccu.website/2022ironman-day28-microsoft-forms.html#respond</comments>
		
		<dc:creator><![CDATA[鳴人]]></dc:creator>
		<pubDate>Thu, 13 Oct 2022 18:18:19 +0000</pubDate>
				<category><![CDATA[2022鐵人賽]]></category>
		<category><![CDATA[Azure DevOps]]></category>
		<category><![CDATA[iThome鐵人賽]]></category>
		<category><![CDATA[2022ironman]]></category>
		<category><![CDATA[azure devops]]></category>
		<guid isPermaLink="false">https://tech.uccu.website/?p=2120</guid>

					<description><![CDATA[<p>雖然預期範本和架構設計好之後會陸續有新的專案申請開設，但是如果都是人員透過電話、Email或是親自來找你建立專 ... <a title="【2022鐵人賽】Microsoft Forms：建立Azure DevOps專案申請表" class="read-more" href="https://tech.uccu.website/2022ironman-day28-microsoft-forms.html" aria-label="Read more about 【2022鐵人賽】Microsoft Forms：建立Azure DevOps專案申請表">閱讀全文</a></p>
<p>這篇文章 <a href="https://tech.uccu.website/2022ironman-day28-microsoft-forms.html">【2022鐵人賽】Microsoft Forms：建立Azure DevOps專案申請表</a> 最早出現於 <a href="https://tech.uccu.website">泰克哪裡去</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>雖然預期範本和架構設計好之後會陸續有新的專案申請開設，但是如果都是人員透過電話、Email或是親自來找你建立專案似乎太麻煩了一點，而且可能每次都會被詢問需要提供哪些資料，所以如果有個簡易的表單能夠讓人填寫的話就是再好不過的事了。</p>



<p>從這個簡單的想法為出發點，其實也很容易找到一些現成的服務，Google的表單也是一個不錯的選項，不過如果公司的員工帳號是儲存在AD(Active Directory)，那麼選用微軟的Forms應該會是一個更好的選擇，所以這邊就以Microsoft Forms為例子(雖然還有另一個主要原因，不過下一篇再說)。</p>



<p>首先，搜尋Microsoft Forms進入Microsoft Forms官網頁面之後，登入公司的帳號(如果有在使用Office365)，選擇新增表單。</p>



<figure class="wp-block-image size-full"><img data-recalc-dims="1" loading="lazy" decoding="async" width="897" height="285" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/CreateNewForm.png?resize=897%2C285&#038;ssl=1" alt="" class="wp-image-2121" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/CreateNewForm.png?w=897&amp;ssl=1 897w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/CreateNewForm.png?resize=300%2C95&amp;ssl=1 300w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/CreateNewForm.png?resize=768%2C244&amp;ssl=1 768w" sizes="auto, (max-width: 897px) 100vw, 897px" /></figure>



<p>接著會是下面的設計畫面，透過幾個基本的元件組合就可以設計出一個簡易的表單(設計組合的部份就不一個個截圖了)。</p>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="536" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/FormDesigner-1024x536.png?resize=1024%2C536&#038;ssl=1" alt="" class="wp-image-2122" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/FormDesigner.png?resize=1024%2C536&amp;ssl=1 1024w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/FormDesigner.png?resize=300%2C157&amp;ssl=1 300w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/FormDesigner.png?resize=768%2C402&amp;ssl=1 768w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/FormDesigner.png?resize=1536%2C804&amp;ssl=1 1536w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/FormDesigner.png?w=1675&amp;ssl=1 1675w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>大概設計出下面這樣的表單內容：</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="479" height="1024" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/FormPage1-479x1024.png?resize=479%2C1024&#038;ssl=1" alt="" class="wp-image-2123" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/FormPage1.png?resize=479%2C1024&amp;ssl=1 479w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/FormPage1.png?resize=140%2C300&amp;ssl=1 140w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/FormPage1.png?resize=768%2C1643&amp;ssl=1 768w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/FormPage1.png?resize=718%2C1536&amp;ssl=1 718w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/FormPage1.png?w=936&amp;ssl=1 936w" sizes="auto, (max-width: 479px) 100vw, 479px" /></figure></div>

<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="532" height="1024" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/FormPage2-532x1024.png?resize=532%2C1024&#038;ssl=1" alt="" class="wp-image-2124" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/FormPage2.png?resize=532%2C1024&amp;ssl=1 532w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/FormPage2.png?resize=156%2C300&amp;ssl=1 156w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/FormPage2.png?resize=768%2C1479&amp;ssl=1 768w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/FormPage2.png?resize=798%2C1536&amp;ssl=1 798w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/FormPage2.png?w=936&amp;ssl=1 936w" sizes="auto, (max-width: 532px) 100vw, 532px" /></figure></div>


<p>如果有人填寫了資料，就可以在「回應」的頁籤中看到填寫的內容，不過因為並不是要做問卷統計，所以下面的圖表結果不是我們關心的重點，直接選擇「在Excel中開啟」就可以把結果的資料下載到本機中透過Excel打開，這樣就可以看到有哪些人填寫了哪些資料了。</p>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="751" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/FormDataResponses-1024x751.png?resize=1024%2C751&#038;ssl=1" alt="" class="wp-image-2125" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/FormDataResponses.png?resize=1024%2C751&amp;ssl=1 1024w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/FormDataResponses.png?resize=300%2C220&amp;ssl=1 300w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/FormDataResponses.png?resize=768%2C563&amp;ssl=1 768w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/FormDataResponses.png?resize=1536%2C1126&amp;ssl=1 1536w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/FormDataResponses.png?w=1815&amp;ssl=1 1815w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="183" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/FormDataInExcel-1024x183.png?resize=1024%2C183&#038;ssl=1" alt="" class="wp-image-2126" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/FormDataInExcel.png?resize=1024%2C183&amp;ssl=1 1024w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/FormDataInExcel.png?resize=300%2C54&amp;ssl=1 300w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/FormDataInExcel.png?resize=768%2C138&amp;ssl=1 768w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/FormDataInExcel.png?resize=1536%2C275&amp;ssl=1 1536w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/FormDataInExcel.png?resize=2048%2C367&amp;ssl=1 2048w, https://i0.wp.com/tech.uccu.website/wp-content/uploads/2022/10/FormDataInExcel.png?w=2250&amp;ssl=1 2250w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>透過這樣簡單的設計就可以有一個申請表單的機制，可以省下一些流程與麻煩事，也方便保存這些歷程資料。</p>
<p>這篇文章 <a href="https://tech.uccu.website/2022ironman-day28-microsoft-forms.html">【2022鐵人賽】Microsoft Forms：建立Azure DevOps專案申請表</a> 最早出現於 <a href="https://tech.uccu.website">泰克哪裡去</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://tech.uccu.website/2022ironman-day28-microsoft-forms.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2120</post-id>	</item>
		<item>
		<title>【2022鐵人賽】Project Wiki：隱藏的Git Repository</title>
		<link>https://tech.uccu.website/2022ironman-day27-hidden-project-wiki-repository.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=2022ironman-day27-hidden-project-wiki-repository</link>
					<comments>https://tech.uccu.website/2022ironman-day27-hidden-project-wiki-repository.html#respond</comments>
		
		<dc:creator><![CDATA[鳴人]]></dc:creator>
		<pubDate>Wed, 12 Oct 2022 14:54:44 +0000</pubDate>
				<category><![CDATA[2022鐵人賽]]></category>
		<category><![CDATA[Azure DevOps]]></category>
		<category><![CDATA[iThome鐵人賽]]></category>
		<category><![CDATA[2022ironman]]></category>
		<category><![CDATA[azure devops]]></category>
		<guid isPermaLink="false">https://tech.uccu.website/?p=2107</guid>

					<description><![CDATA[<p>距離鐵人賽結束的時間剩沒幾天，後面幾個篇幅來些比較清淡的項目。 前面提到過將Pipeline範本化設計就是為了 ... <a title="【2022鐵人賽】Project Wiki：隱藏的Git Repository" class="read-more" href="https://tech.uccu.website/2022ironman-day27-hidden-project-wiki-repository.html" aria-label="Read more about 【2022鐵人賽】Project Wiki：隱藏的Git Repository">閱讀全文</a></p>
<p>這篇文章 <a href="https://tech.uccu.website/2022ironman-day27-hidden-project-wiki-repository.html">【2022鐵人賽】Project Wiki：隱藏的Git Repository</a> 最早出現於 <a href="https://tech.uccu.website">泰克哪裡去</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>距離鐵人賽結束的時間剩沒幾天，後面幾個篇幅來些比較清淡的項目。</p>



<p>前面提到過將Pipeline範本化設計就是為了建立多個Azure DevOps專案的時候可以快速方便的把基本的架構弄起來，而每一個新建立的專案都有可能是給不同的開發團隊使用的，這些夥伴可能甚至沒有使用過Azure DevOps、對<a href="https://tech.uccu.website/2022ironman-day3-flow-plan.html" target="_blank" rel="noreferrer noopener">我們規劃的流程</a>沒那麼熟悉等等。</p>



<p>這時候當我們將新的DevOps專案建立好之後，要把相關資訊移轉給他們時，若是有個地方可以將一些資訊集中放在那裡提供團隊成員參考，我想應該是一個滿不錯的方式。而這個地方，我覺得最適合的就是每個Azure DevOps專案內的Summary頁面與Wiki，就像下面這張範例圖。</p>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="628" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/01-ProjectSummary-1024x628.png?resize=1024%2C628&#038;ssl=1" alt="" class="wp-image-2108" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/01-ProjectSummary.png?resize=1024%2C628&amp;ssl=1 1024w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/01-ProjectSummary.png?resize=300%2C184&amp;ssl=1 300w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/01-ProjectSummary.png?resize=768%2C471&amp;ssl=1 768w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/01-ProjectSummary.png?w=1359&amp;ssl=1 1359w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>一個新專案剛建立的時候應該是長得像下面這個樣子，Summary的頁面並沒有什麼內容，如果我們可以利用這個版面，就可以讓團隊成員一開始進入專案首頁的時候就可以看到我們希望傳遞給他們的資訊。</p>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="496" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/02-EmptyProjectSummary-1024x496.png?resize=1024%2C496&#038;ssl=1" alt="" class="wp-image-2109" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/02-EmptyProjectSummary.png?resize=1024%2C496&amp;ssl=1 1024w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/02-EmptyProjectSummary.png?resize=300%2C145&amp;ssl=1 300w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/02-EmptyProjectSummary.png?resize=768%2C372&amp;ssl=1 768w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/02-EmptyProjectSummary.png?w=1388&amp;ssl=1 1388w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>要達到這個目的，我們只需要透過Markdown格式來寫一些內容就可以達成，而存放Markdown檔案的空間就是Project的Wiki。</p>



<figure class="wp-block-image size-full"><img data-recalc-dims="1" loading="lazy" decoding="async" width="910" height="495" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/03-CreateProjectWiki.png?resize=910%2C495&#038;ssl=1" alt="" class="wp-image-2110" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/03-CreateProjectWiki.png?w=910&amp;ssl=1 910w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/03-CreateProjectWiki.png?resize=300%2C163&amp;ssl=1 300w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/03-CreateProjectWiki.png?resize=768%2C418&amp;ssl=1 768w" sizes="auto, (max-width: 910px) 100vw, 910px" /></figure>



<p>從Wiki的選項進入之後，你可以看到有兩個按鈕選項，分別是Create project wiki、Publish code as wiki，兩個差別在於前者會建立一個隱藏的wiki Git Repository，也就是這篇文章的主題，後者則是使用你已經建立好的Git Repository。在這邊我們選擇Create project wiki。</p>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="457" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/04-CreateSummaryMarkdown-1024x457.png?resize=1024%2C457&#038;ssl=1" alt="" class="wp-image-2111" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/04-CreateSummaryMarkdown.png?resize=1024%2C457&amp;ssl=1 1024w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/04-CreateSummaryMarkdown.png?resize=300%2C134&amp;ssl=1 300w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/04-CreateSummaryMarkdown.png?resize=768%2C343&amp;ssl=1 768w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/04-CreateSummaryMarkdown.png?w=1076&amp;ssl=1 1076w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>系統建麼了wiki Git Repository之後就會帶到上面的這個編輯文件的畫面，而這個文件保存的格式就是不折不扣的Markdown文件(.md)，利用Markdown格式寫完之後按下Save按鈕之後，在它的左邊就會自動出現Close按鈕，按下之後你就會看到下面的這個總覽畫面：</p>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="606" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/05-ProjectWiki-1024x606.png?resize=1024%2C606&#038;ssl=1" alt="" class="wp-image-2112" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/05-ProjectWiki.png?resize=1024%2C606&amp;ssl=1 1024w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/05-ProjectWiki.png?resize=300%2C177&amp;ssl=1 300w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/05-ProjectWiki.png?resize=768%2C454&amp;ssl=1 768w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/05-ProjectWiki.png?w=1192&amp;ssl=1 1192w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>建立完Summary.md的內容之後，再點回去專案的Summary頁面，接著按下Add Project Description按鈕，右邊彈出來的畫面上就可以選擇Wiki，它會自動認到剛剛上面新增的Summary.md內容。</p>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="617" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/06-SetProjectSummary-1024x617.png?resize=1024%2C617&#038;ssl=1" alt="" class="wp-image-2113" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/06-SetProjectSummary.png?resize=1024%2C617&amp;ssl=1 1024w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/06-SetProjectSummary.png?resize=300%2C181&amp;ssl=1 300w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/06-SetProjectSummary.png?resize=768%2C463&amp;ssl=1 768w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/06-SetProjectSummary.png?w=1174&amp;ssl=1 1174w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>儲存之後重新看到的畫面就會像下面這樣：</p>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="503" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/07-SummaryOverview-1024x503.png?resize=1024%2C503&#038;ssl=1" alt="" class="wp-image-2114" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/07-SummaryOverview.png?resize=1024%2C503&amp;ssl=1 1024w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/07-SummaryOverview.png?resize=300%2C147&amp;ssl=1 300w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/07-SummaryOverview.png?resize=768%2C378&amp;ssl=1 768w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/07-SummaryOverview.png?w=1359&amp;ssl=1 1359w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>前面已經說過，這個Project wiki實際上就是一個Git Repository，但是…，如果你直接點進去Repos功能中想要去找它，會發現…它根本不存在？</p>



<figure class="wp-block-image size-full"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1009" height="626" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/08-NotFoundWikiRepository.png?resize=1009%2C626&#038;ssl=1" alt="" class="wp-image-2115" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/08-NotFoundWikiRepository.png?w=1009&amp;ssl=1 1009w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/08-NotFoundWikiRepository.png?resize=300%2C186&amp;ssl=1 300w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/08-NotFoundWikiRepository.png?resize=768%2C476&amp;ssl=1 768w" sizes="auto, (max-width: 1009px) 100vw, 1009px" /></figure>



<p>甚至你跑到專案的設定裡面想要去找找看有沒有它的蹤跡卻也無功而返？</p>



<figure class="wp-block-image size-full"><img data-recalc-dims="1" loading="lazy" decoding="async" width="637" height="775" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/09-NotFoundWikiRepository.png?resize=637%2C775&#038;ssl=1" alt="" class="wp-image-2116" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/09-NotFoundWikiRepository.png?w=637&amp;ssl=1 637w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/09-NotFoundWikiRepository.png?resize=247%2C300&amp;ssl=1 247w" sizes="auto, (max-width: 637px) 100vw, 637px" /></figure>



<p>其實呢！直接從網址輸入它的名字就可以進去了喔！</p>



<figure class="wp-block-image size-full"><img data-recalc-dims="1" loading="lazy" decoding="async" width="679" height="432" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/10-InvisibleWikiRepository.png?resize=679%2C432&#038;ssl=1" alt="" class="wp-image-2117" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/10-InvisibleWikiRepository.png?w=679&amp;ssl=1 679w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/10-InvisibleWikiRepository.png?resize=300%2C191&amp;ssl=1 300w" sizes="auto, (max-width: 679px) 100vw, 679px" /></figure>



<p>很神奇對吧！也因為它就是和一般的Git Repository一樣，所以你可以把它Clone下來，透過你習慣的IDE來編輯Markdown檔案，完成之後再重新把它commit上來。畫面中的.order檔案則是用來設定Markdown在上面的Wiki總覽畫面顯示的順序。</p>



<p>透過這個方式，也可以將專案的Summary Markdown檔案弄一個範本，放在templates Git Repo或是另外建立一個Docs Git Repo，之後建立新的專案的時候就直接從這個地方複製過去就可以了，節省每一個專案要重新在介面上輸入同樣內容的時間。</p>
<p>這篇文章 <a href="https://tech.uccu.website/2022ironman-day27-hidden-project-wiki-repository.html">【2022鐵人賽】Project Wiki：隱藏的Git Repository</a> 最早出現於 <a href="https://tech.uccu.website">泰克哪裡去</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://tech.uccu.website/2022ironman-day27-hidden-project-wiki-repository.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2107</post-id>	</item>
		<item>
		<title>【2022鐵人賽】調整CD Release Pipeline與匯出Json檔案當作範本</title>
		<link>https://tech.uccu.website/2022ironman-day26-adjust-cd-release-pipeline-and-export-as-template.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=2022ironman-day26-adjust-cd-release-pipeline-and-export-as-template</link>
					<comments>https://tech.uccu.website/2022ironman-day26-adjust-cd-release-pipeline-and-export-as-template.html#respond</comments>
		
		<dc:creator><![CDATA[鳴人]]></dc:creator>
		<pubDate>Tue, 11 Oct 2022 16:20:51 +0000</pubDate>
				<category><![CDATA[2022鐵人賽]]></category>
		<category><![CDATA[Azure DevOps]]></category>
		<category><![CDATA[iThome鐵人賽]]></category>
		<category><![CDATA[2022ironman]]></category>
		<category><![CDATA[azure devops]]></category>
		<guid isPermaLink="false">https://tech.uccu.website/?p=2093</guid>

					<description><![CDATA[<p>CI Pipeline的YAML搞定之後，別忘了接續它動作的是CD Release Pipeline，之前在基 ... <a title="【2022鐵人賽】調整CD Release Pipeline與匯出Json檔案當作範本" class="read-more" href="https://tech.uccu.website/2022ironman-day26-adjust-cd-release-pipeline-and-export-as-template.html" aria-label="Read more about 【2022鐵人賽】調整CD Release Pipeline與匯出Json檔案當作範本">閱讀全文</a></p>
<p>這篇文章 <a href="https://tech.uccu.website/2022ironman-day26-adjust-cd-release-pipeline-and-export-as-template.html">【2022鐵人賽】調整CD Release Pipeline與匯出Json檔案當作範本</a> 最早出現於 <a href="https://tech.uccu.website">泰克哪裡去</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>CI Pipeline的YAML搞定之後，別忘了接續它動作的是CD Release Pipeline，之前在<a href="https://tech.uccu.website/2022ironman-day12-create-cd-pipeline.html" target="_blank" rel="noreferrer noopener">基本版-建立CD Release Pipeline</a>這篇文章建立了CD Pipeline，但是那時候還沒有設計YAML範本，並且GCP的Json檔案和Dockerfile檔案都還沒有搬移到新的templates Git Repo，這兩個檔案在之後已經搬家了，所以自然就需要調整更新一下CD Pipeline囉！</p>



<p>為了之後建立多個Azure DevOps方便，所以調整完後也需要將它匯出成Json檔案當作後續建立的範本來匯入，這樣大部份的項目就會先建立好，我們只需要針對一些地方進行調整修改就可以了。</p>



<p>首先，先來調整原本的CD Pipeline，先增加templates的Repo來源，這部份在後續新的Azure DevOps專案應該不會更動，別名就使用PipelineTemplates吧！</p>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="686" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/01-AddPipelineTemplateRepo-1024x686.png?resize=1024%2C686&#038;ssl=1" alt="" class="wp-image-2094" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/01-AddPipelineTemplateRepo.png?resize=1024%2C686&amp;ssl=1 1024w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/01-AddPipelineTemplateRepo.png?resize=300%2C201&amp;ssl=1 300w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/01-AddPipelineTemplateRepo.png?resize=768%2C514&amp;ssl=1 768w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/01-AddPipelineTemplateRepo.png?w=1211&amp;ssl=1 1211w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>接著我們需要修改Stage和Production的Agent job，要新增一個Bash task，就是針對環境變數檔案內容合併的部份，Script裡面的內容參照先前CI Pipeline寫的內容，只是路徑和變數需要調整一下，可以對照下圖的Script和YAML內的差異。</p>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="597" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/02-AddMergeScripts-1024x597.png?resize=1024%2C597&#038;ssl=1" alt="" class="wp-image-2095" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/02-AddMergeScripts.png?resize=1024%2C597&amp;ssl=1 1024w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/02-AddMergeScripts.png?resize=300%2C175&amp;ssl=1 300w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/02-AddMergeScripts.png?resize=768%2C448&amp;ssl=1 768w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/02-AddMergeScripts.png?w=1034&amp;ssl=1 1034w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>改完之後還需要調整一下Deploy CloudRun的Script，圖中的PipelineRepo和PipelineTemplates都是對應上面第一張圖所設的別名，應該沒忘吧？</p>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="406" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/03-UpdateDeployScripts-1024x406.png?resize=1024%2C406&#038;ssl=1" alt="" class="wp-image-2096" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/03-UpdateDeployScripts.png?resize=1024%2C406&amp;ssl=1 1024w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/03-UpdateDeployScripts.png?resize=300%2C119&amp;ssl=1 300w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/03-UpdateDeployScripts.png?resize=768%2C304&amp;ssl=1 768w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/03-UpdateDeployScripts.png?w=1188&amp;ssl=1 1188w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>Production的部份也是一樣新增Bash把合併環境變數檔案的Script加進去，可以直接複製Stage內的Script。同時也別忘了要修改Deploy CloudRun的Script。</p>



<p>Stage跟Production的Script都調整好了之後，就來到Variables頁籤來新增使用到的變數。這邊跟之前比較不一樣的地方在於綠框的內容，因為我們已經學習到了更多的東西，所以可以調整一下一些變數的設計，這樣之後在新的Azure DevOps專案匯入CD Pipeline的時候才不會有漏改的地方。(參考<a href="https://tech.uccu.website/2022ironman-day24-projectname-and-variables-template.html" target="_blank" rel="noreferrer noopener">多專案命名規則與變數範本</a>這篇文章)</p>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="567" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/04-AddNewVariables-1024x567.png?resize=1024%2C567&#038;ssl=1" alt="" class="wp-image-2097" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/04-AddNewVariables.png?resize=1024%2C567&amp;ssl=1 1024w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/04-AddNewVariables.png?resize=300%2C166&amp;ssl=1 300w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/04-AddNewVariables.png?resize=768%2C425&amp;ssl=1 768w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/04-AddNewVariables.png?w=1354&amp;ssl=1 1354w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>都改完存檔之後，就可以回到外面一層，在CD Pipeline的部份叫出選單，把它匯出(Export)為Json檔案保存下來，之後在新建立Azure DevOps專案的時候方便匯入。</p>



<figure class="wp-block-image size-full"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1021" height="367" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/05-ExportCDPipeline.png?resize=1021%2C367&#038;ssl=1" alt="" class="wp-image-2102" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/05-ExportCDPipeline.png?w=1021&amp;ssl=1 1021w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/05-ExportCDPipeline.png?resize=300%2C108&amp;ssl=1 300w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/05-ExportCDPipeline.png?resize=768%2C276&amp;ssl=1 768w" sizes="auto, (max-width: 1021px) 100vw, 1021px" /></figure>



<figure class="wp-block-image size-full"><img data-recalc-dims="1" loading="lazy" decoding="async" width="313" height="177" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/06-ImportCDPipeline.png?resize=313%2C177&#038;ssl=1" alt="" class="wp-image-2098" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/06-ImportCDPipeline.png?w=313&amp;ssl=1 313w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/06-ImportCDPipeline.png?resize=300%2C170&amp;ssl=1 300w" sizes="auto, (max-width: 313px) 100vw, 313px" /></figure>



<p>上圖就是匯入的選項位置，新專案可能因為沒有任何一個Release Pipeline而找不到這個選項，只要隨便建立一個空的Release Pipeline再來找這個匯入的功能就可以了。(建立的空Release Pipeline再刪掉就好)</p>



<figure class="wp-block-image size-full"><img data-recalc-dims="1" loading="lazy" decoding="async" width="513" height="255" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/07-SelectImportJsonFile.png?resize=513%2C255&#038;ssl=1" alt="" class="wp-image-2099" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/07-SelectImportJsonFile.png?w=513&amp;ssl=1 513w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/07-SelectImportJsonFile.png?resize=300%2C149&amp;ssl=1 300w" sizes="auto, (max-width: 513px) 100vw, 513px" /></figure>



<p>選擇前面匯出的Json檔案按下Ok就會匯入成為一個新的CD Release Pipeline了。</p>



<p>匯入成為新的CD Release Pipeline之後，根據先前在<a href="https://tech.uccu.website/2022ironman-day12-create-cd-pipeline.html" target="_blank" rel="noreferrer noopener">基本版-建立CD Release Pipeline</a>這篇文章建立的步驟檢查一下各項設定與變數內容(記得更改專案名稱)，然後有兩個地方特別要留意一下，第一個就是除了PipelineTemplates那個Artifacts來源之外，其它的都要刪掉重新加，因為專案不同。第二個就是在Production的Create Pull Request的部份要針對新的專案重新選擇，不然Git commit SHA會對不起來，無法建立PR也無法合併。</p>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="517" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/08-CheckCreatePRTaskProjectAndRepo-1024x517.png?resize=1024%2C517&#038;ssl=1" alt="" class="wp-image-2100" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/08-CheckCreatePRTaskProjectAndRepo.png?resize=1024%2C517&amp;ssl=1 1024w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/08-CheckCreatePRTaskProjectAndRepo.png?resize=300%2C152&amp;ssl=1 300w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/08-CheckCreatePRTaskProjectAndRepo.png?resize=768%2C388&amp;ssl=1 768w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/08-CheckCreatePRTaskProjectAndRepo.png?w=1225&amp;ssl=1 1225w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
<p>這篇文章 <a href="https://tech.uccu.website/2022ironman-day26-adjust-cd-release-pipeline-and-export-as-template.html">【2022鐵人賽】調整CD Release Pipeline與匯出Json檔案當作範本</a> 最早出現於 <a href="https://tech.uccu.website">泰克哪裡去</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://tech.uccu.website/2022ironman-day26-adjust-cd-release-pipeline-and-export-as-template.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2093</post-id>	</item>
		<item>
		<title>【2022鐵人賽】重構YAML範本：加入更多彈性</title>
		<link>https://tech.uccu.website/2022ironman-day25-refactor-yaml-add-more-extensibility.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=2022ironman-day25-refactor-yaml-add-more-extensibility</link>
					<comments>https://tech.uccu.website/2022ironman-day25-refactor-yaml-add-more-extensibility.html#respond</comments>
		
		<dc:creator><![CDATA[鳴人]]></dc:creator>
		<pubDate>Mon, 10 Oct 2022 14:58:29 +0000</pubDate>
				<category><![CDATA[2022鐵人賽]]></category>
		<category><![CDATA[Azure DevOps]]></category>
		<category><![CDATA[iThome鐵人賽]]></category>
		<category><![CDATA[2022ironman]]></category>
		<category><![CDATA[azure devops]]></category>
		<guid isPermaLink="false">https://tech.uccu.website/?p=2059</guid>

					<description><![CDATA[<p>YAML範本設計終於來到了尾聲，前一篇連變數都把它範本化了，還有什麼地方可以優化的嗎？ 有的！如果回頭檢視之前 ... <a title="【2022鐵人賽】重構YAML範本：加入更多彈性" class="read-more" href="https://tech.uccu.website/2022ironman-day25-refactor-yaml-add-more-extensibility.html" aria-label="Read more about 【2022鐵人賽】重構YAML範本：加入更多彈性">閱讀全文</a></p>
<p>這篇文章 <a href="https://tech.uccu.website/2022ironman-day25-refactor-yaml-add-more-extensibility.html">【2022鐵人賽】重構YAML範本：加入更多彈性</a> 最早出現於 <a href="https://tech.uccu.website">泰克哪裡去</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>YAML範本設計終於來到了尾聲，前一篇連變數都把它範本化了，還有什麼地方可以優化的嗎？</p>



<p>有的！如果回頭檢視之前設計的YAML內容，肯定還是可以發現有什麼地方可以改得更好，只是一開始沒發現或是暫時沒時間弄而已。然而實際在我的工作上其實已經針對使用的YAML重構到了第三個版本，雖然沒有完全寫在鐵人賽的文章內容中，但是大致上的精髓也是十之八九，就來看看還有什麼東西可以調整的吧！</p>



<h2 class="wp-block-heading">加入stepList型態參數</h2>



<p>之前在<a href="https://tech.uccu.website/2022ironman-day15-variables-and-parameters.html" target="_blank" rel="noreferrer noopener">認識Build Pipeline的參數(Parameters)與變數(Variables)</a>這篇文章中有提到參數的型態，除了常用的string、boolean這種常值類型的以外，還有對應stage、job、step與它們的集合類型的參數，而stepList就是可以加入許多額外的step的參數類型。</p>



<p>如果你是寫過程式的開發者，我想應該都有事件驅動的概念，如果你曾經寫過.Net WinForm的程式，應該更能理解我在這裡要加入的概念。</p>



<p>還記得我們的Job有分成三個吧？分別是BuildCode、BuildImage、DeployCloudRun，在這邊我希望能夠在這些Job實際執行動作「之前」與「之後」加入一些能夠讓外部動態增加一些step的彈性。這裡的外部不一定指的是範本以外的專案，也可以是範本中不同層級的YAML範本。</p>



<h3 class="wp-block-heading">BuildCode增加的彈性</h3>



<p>在BuildCode的範本裡面我打算加入codePreBuildSteps、codePostBuildSteps這兩個參數，分別代表程式碼在編譯之前與編譯之後可以額外加入step的區段。</p>



<p>為了這麼做，首先必須在BuildCode.yaml中加入對應的參數設定如下：</p>



<pre class="wp-block-prismatic-blocks"><code class="language-yaml"># jobs/buildCode.yaml

  #======= Extensibility ========
  - name: codePreBuildSteps
    type: stepList
    default: []
  - name: codePostBuildSteps
    type: stepList
    default: []</code></pre>



<p>接著在使用dotnet-sdk-in-linux-container.yaml之前加入codePreBuildSteps的參數，在結尾的部份加入codePostBuildSteps參數，YAML內容部份如下：</p>



<pre class="wp-block-prismatic-blocks"><code class="language-yaml"># jobs/buildCode.yaml

      - ${{ parameters.codePreBuildSteps }}
      - template: ../steps/dotnet-sdk-in-linux-container.yaml
        parameters:
          srcPath: ${{ parameters.sourcePath }}
          slnOrCsprojName: ${{ parameters.slnOrCsprojName }}
          dotnetCommand: publish
          dotnetCommandArgs: &#039;-c ${{ parameters.buildConfiguration }}&#039;
      - ${{ parameters.codePostBuildSteps }}</code></pre>



<p>因為buildCode.yaml是job層級，上面還有個stage template，所以在stages/dotnet-build-stage.yaml中也必須同樣加上上面的參數設定，然後在使用buildCode.yaml的部份加上對應的參數值。</p>



<pre class="wp-block-prismatic-blocks"><code class="language-yaml"># stages/dotnet-build-stage.yaml

    - template: ../jobs/buildCode.yaml
      parameters:
        sourcePath: ${{ parameters.sourcePath }}
        slnOrCsprojName: ${{ parameters.slnOrCsprojName }}
        codePreBuildSteps: ${{ parameters.codePreBuildSteps }}
        codePostBuildSteps: ${{ parameters.codePostBuildSteps }}</code></pre>



<h3 class="wp-block-heading">buildImage增加的彈性</h3>



<p>以相同的概念在buildImage的部份則是加上imagePreBuildSteps、imagePostBuildSteps，不過這邊要加入的檔案有三個，分別是stages/dotnet-build-stage.yaml、jobs/buildImage.yaml、steps/docker-build.yaml。</p>



<p>參數設定如下：</p>



<pre class="wp-block-prismatic-blocks"><code class="language-yaml">  #======= Extensibility ========
  - name: imagePreBuildSteps
    type: stepList
    default: []
  - name: imagePostBuildSteps
    type: stepList
    default: []</code></pre>



<p>加了參數設定之後，接下來稍微有點不太一樣，在buildImage.yaml加入了上面的參數之後，是在使用docker-build.yaml的地方設定參數值，真正使用到參數值的地方則是在docker-build.yaml裡面，會不一樣主要還是裡面的steps差異，如果不影響就可以放外面，會影響就放裡面。</p>



<pre class="wp-block-prismatic-blocks"><code class="language-yaml"># steps/docker-build.yaml

  - ${{ parameters.imagePreBuildSteps }}
  - task: Docker@2
    displayName: Build image
    inputs:
      repository: &#039;${{ parameters.imgRepository }}&#039;
      command: &#039;build&#039;
      Dockerfile: ${{ parameters.buildDockerfile }}
      buildContext: ${{ parameters.buildContext }}
      ${{ if eq(parameters.buildArgs, &#039;&#039;) }}:
        arguments: &#039;--no-cache&#039;
      ${{ else }}:
        arguments: &#039;--no-cache --build-arg ${{ parameters.buildArgs }}&#039;
      tags: ${{ parameters.imgTags }}
  - ${{ parameters.imagePostBuildSteps }}</code></pre>



<h3 class="wp-block-heading">deployCloudRun增加的彈性</h3>



<p>在deployCloudRun的部份因為沒有細到step template，所以直接就是在deployCloudRun.yaml中加入參數設定與使用參數值了(同一份檔案就直接全貼內容了)。不過參數設定的部份還是要另外複製一份到stages/deploy-cloudrun-stage.yaml檔案中。</p>



<pre class="wp-block-prismatic-blocks"><code class="language-yaml"># jobs/deployCloudRun.yaml

parameters:
  - name: cloudRunProjectId
    type: string
  - name: imgRepository
    type: string
  - name: cloudRunRegion
    type: string
  - name: cloudRunServiceName
    type: string
  - name: templateResourceName
    type: string
    default: templates
  - name: pipelineResourceName
    type: string
    default: pipelines

  #======= Extensibility ========
  - name: preDeploySteps
    type: stepList
    default: []
  - name: postDeploySteps
    type: stepList
    default: []

  - name: jobName
    type: string
    default: DeployCloudRun

jobs:
  - job: ${{ parameters.jobName }}
    steps:
      - script: |    
          echo &#039;${{ convertToJson(parameters) }}&#039; &gt;&gt; parameters.json
          cat parameters.json
        displayName: Print template parameters
      - checkout: ${{ parameters.templateResourceName }}
        path: ${{ parameters.templateResourceName }}
        clean: true
      - checkout: ${{ parameters.pipelineResourceName }}
        path: ${{ parameters.pipelineResourceName }}
        clean: true
      - ${{ parameters.preDeploySteps }}
      - template: ../steps/merge-cloudRun-envVars.yaml
        parameters:
          templateResourceName: ${{ parameters.templateResourceName }}
          pipelineResourceName: ${{ parameters.pipelineResourceName }}
          projectEnvVarsFile: variables/cloudrun-envVars.yaml
          sharedEnvVarsFile: variables/cloudrun-envVars.yaml
          mergedNewFileFullPath: $(Agent.BuildDirectory)/mergedEnvVars.yaml
      - task: Bash@3
        displayName: Deploy docker image to cloudrun
        inputs:
          targetType: &#039;inline&#039;
          script: |
            docker run --rm \
            -v $(Agent.BuildDirectory)/${{ parameters.templateResourceName }}/ironman2022-gcp-key.json:/gcp/cloudKey.json \
            -v $(Agent.BuildDirectory)/mergedEnvVars.yaml:/gcp/env-vars.yaml \
            asia.gcr.io/google.com/cloudsdktool/google-cloud-cli:latest \
            bash -c &quot;gcloud auth login --cred-file=/gcp/cloudKey.json &amp;&amp; gcloud run deploy ${{ parameters.cloudRunServiceName }} --env-vars-file /gcp/env-vars.yaml --image ${{ parameters.imgRepository }} --region ${{ parameters.cloudRunRegion }} --project ${{ parameters.cloudRunProjectId }} --allow-unauthenticated&quot;
      - ${{ parameters.postDeploySteps }}</code></pre>



<p>stages/deploy-cloudrun-stage.yaml檔案中的內容則是下面這段：</p>



<pre class="wp-block-prismatic-blocks"><code class="language-yaml"># stages/deploy-cloudrun-stage.yaml

parameters:
  # deployCloudRun.yaml parameters
  - name: cloudRunProjectId
    type: string
  - name: imgRepository
    type: string
  - name: cloudRunRegion
    type: string
  - name: cloudRunServiceName
    type: string
  - name: templateResourceName
    type: string
    default: templates
  - name: pipelineResourceName
    type: string
    default: pipelines

  #======= Extensibility ========
  - name: preDeploySteps
    type: stepList
    default: []
  - name: postDeploySteps
    type: stepList
    default: []

  - name: stageName
    type: string
    default: DeployCloudRun
    
stages:
- stage: ${{ parameters.stageName }}
  displayName: 佈署Cloud Run
  jobs:
    - template: ../jobs/deployCloudRun.yaml
      parameters:
        cloudRunProjectId: ${{ parameters.cloudRunProjectId }}
        imgRepository: ${{ parameters.imgRepository }}
        cloudRunRegion: ${{ parameters.cloudRunRegion }}
        cloudRunServiceName: ${{ parameters.cloudRunServiceName }}
        templateResourceName: ${{ parameters.templateResourceName }}
        pipelineResourceName: ${{ parameters.pipelineResourceName }}
        preDeploySteps: ${{ parameters.preDeploySteps }}
        postDeploySteps: ${{ parameters.postDeploySteps }}</code></pre>



<h2 class="wp-block-heading">將jobName、stageName、dependsOn設為參數</h2>



<p>如果你有仔細的看deployCloudRun.yaml和deploy-cloudrun-stage.yaml這兩個檔案的內容的話，應該會發現除了前面提到的stepList類型的參數之外，還另外增加了jobName或stageName參數。</p>



<p>這個修改就是讓外部可以重新設定job或stage的名稱(也就是識別ID)，如果沒有特別設定的話則是使用預設值，也就是原本的名稱。這麼做的用意在哪呢？</p>



<p>主要是為了dependsOn屬性，也就是Job的依賴關係。</p>



<p>其實在鐵人賽的文章中，我只有以.Net的程式為範例來舉例，所以編譯程式碼的Job YAML直接就叫作buildCode.yaml，但是實際上若不是完全只有使用一種程式語言的環境，那麼應該會分成好幾種不同程式語言的YAML檔，例如：dotnet-build.yaml、ios-build.yaml、node-build.yaml之類的，也就是在buildImage的部份如果dependsOn固定設定一個名稱，可能在範本使用上不太妥當，所以我將jobName、stageName和dependsOn都設為參數。</p>



<h2 class="wp-block-heading">如何使用？</h2>



<p>要使用到參數設定的stepList，就是在引用範本的parameters底下對應的參數直接使用task類型的YAML語法就可以了，如果是在範本中使用到之後又要開放給外部(上一層)使用，則是繼續使用參數值即可。</p>



<p>例如在buildImage之前要做一些事，像是額外下載需要的檔案，或是更改檔名或設定值之類的…，用stages/dotnet-build-stage.yaml的部份內容來舉例如下：</p>



<pre class="wp-block-prismatic-blocks"><code class="language-yaml">    - template: ../jobs/buildImage.yaml
      parameters:
        templateResourceName: ${{ parameters.templateResourceName }}
        dependsOn: BuildCode
        artifactName: ${{ parameters.artifactName }}
        unzip: ${{ parameters.unzip }}
        zipFileName: ${{ parameters.zipFileName }}
        unzipToFolderPath: ${{ parameters.unzipToFolderPath }}
        imgRepository: ${{ parameters.imgRepository }}
        imgTags: ${{ parameters.imgTags }}
        buildDockerfile: ${{ parameters.buildDockerfile }}
        buildContext: ${{ parameters.buildContext }}
        containerRegistry: ${{ parameters.containerRegistry }}
        buildArgs: &quot;TARGET_FILE=${{ parameters.startFileName }}&quot;
        imagePreBuildSteps: 
        - script: |
            echo &quot;這是使用的範例1&quot;
          displayName: 範例啦
        - task: Bash@3
          displayName: 還是範例啦
          inputs:
            targetType: &#039;inline&#039;
            script: |
              echo &quot;這是使用的範例2&quot;
        - ${{ parameters.imagePreBuildSteps }}
        imagePostBuildSteps: ${{ parameters.imagePostBuildSteps }}</code></pre>



<p>細部來看的話，就只有下面這段：</p>



<pre class="wp-block-prismatic-blocks"><code class="language-yaml">        imagePreBuildSteps: 
        - script: |
            echo &quot;這是使用的範例1&quot;
          displayName: 範例啦
        - task: Bash@3
          displayName: 還是範例啦
          inputs:
            targetType: &#039;inline&#039;
            script: |
              echo &quot;這是使用的範例2&quot;
        - ${{ parameters.imagePreBuildSteps }}</code></pre>



<p>最後的 &#8211; ${{ parameters.imagePreBuildSteps }}是讓範本設定同名的參數能延續使用，也就是不會讓外部設定的內容失效，簡單來說就是接下去的意思。</p>



<h2 class="wp-block-heading">這次修改過的YAML檔案內容</h2>



<p>下面就是這篇文章修改過的完整YAML檔案內容，</p>



<pre class="wp-block-prismatic-blocks"><code class="language-yaml"># steps/docker-build.yaml

parameters:
  - name: imgRepository
    type: string
  - name: imgTags
    type: object
  - name: buildDockerfile
    type: string
    default: Build.Dockerfile
  - name: buildContext
    type: string
  - name: containerRegistry
    type: string
  - name: buildArgs
    type: string
    default: &#039;&#039;

  #======= Extensibility ========
  - name: imagePreBuildSteps
    type: stepList
    default: []
  - name: imagePostBuildSteps
    type: stepList
    default: []

steps:
  - ${{ parameters.imagePreBuildSteps }}
  - task: Docker@2
    displayName: Build image
    inputs:
      repository: &#039;${{ parameters.imgRepository }}&#039;
      command: &#039;build&#039;
      Dockerfile: ${{ parameters.buildDockerfile }}
      buildContext: ${{ parameters.buildContext }}
      ${{ if eq(parameters.buildArgs, &#039;&#039;) }}:
        arguments: &#039;--no-cache&#039;
      ${{ else }}:
        arguments: &#039;--no-cache --build-arg ${{ parameters.buildArgs }}&#039;
      tags: ${{ parameters.imgTags }}
  - ${{ parameters.imagePostBuildSteps }}
  - task: Docker@2
    displayName: &quot;Login to Container Registry&quot;
    inputs:
      command: login
      containerRegistry: ${{ parameters.containerRegistry }}
  - task: Bash@3
    displayName: Push docker image
    inputs:
      targetType: &#039;inline&#039;
      script: |
        docker push -a ${{ parameters.imgRepository }}</code></pre>



<pre class="wp-block-prismatic-blocks"><code class="language-yaml"># jobs/buildCode.yaml

parameters:
  - name: sourcePath
    type: string
    default: $(Build.SourcesDirectory)
  - name: slnOrCsprojName
    type: string
    default: Pipeline.sln
  - name: buildConfiguration
    type: string
    default: release
    values:
    - release
    - debug

  - name: jobName
    type: string
    default: BuildCode

  #======= Extensibility ========
  - name: codePreBuildSteps
    type: stepList
    default: []
  - name: codePostBuildSteps
    type: stepList
    default: []

jobs:
  - job: ${{ parameters.jobName }}
    steps:
      - checkout: sources
        clean: true
      - template: ../steps/publish-commit-sha.yaml
        parameters:
          sourcePath: ${{ parameters.sourcePath }}
      - ${{ parameters.codePreBuildSteps }}
      - template: ../steps/dotnet-sdk-in-linux-container.yaml
        parameters:
          srcPath: ${{ parameters.sourcePath }}
          slnOrCsprojName: ${{ parameters.slnOrCsprojName }}
          dotnetCommand: publish
          dotnetCommandArgs: &#039;-c ${{ parameters.buildConfiguration }}&#039;
      - ${{ parameters.codePostBuildSteps }}
      - template: ../steps/publish-pipeline-artifacts.yaml
        parameters:
          publishPath: &#039;$(Build.ArtifactStagingDirectory)/zipFiles&#039;
          artifactName: &#039;$(pipelineArtifact)&#039;</code></pre>



<pre class="wp-block-prismatic-blocks"><code class="language-yaml"># jobs/buildImage.yaml

parameters:
  - name: artifactName
    type: string
    default: OutputFiles
  - name: unzip
    type: boolean
    default: false
  - name: zipFileName
    type: string
    default: &#039;&#039;
  - name: unzipToFolderPath
    type: string
    default: &#039;&#039;

  # - name: pipelineResourceName
  #   type: string
  #   default: pipelines
  - name: templateResourceName
    type: string
    default: templates

  - name: imgRepository
    type: string
  - name: imgTags
    type: object
  - name: buildDockerfile
    type: string
    default: Build.Dockerfile
  - name: buildContext
    type: string
  - name: containerRegistry
    type: string
  - name: buildArgs
    type: string
    default: &#039;&#039;

  #======= Extensibility ========
  - name: imagePreBuildSteps
    type: stepList
    default: []
  - name: imagePostBuildSteps
    type: stepList
    default: []
  - name: dependsOn
    type: object
    default: []

  - name: jobName
    type: string
    default: BuildImage

jobs:
  - job: ${{ parameters.jobName }}
    dependsOn: ${{ parameters.dependsOn }}
    steps:
    - checkout: ${{ parameters.templateResourceName }}
      path: ${{ parameters.templateResourceName }}
      clean: true
    - template: ../steps/download-pipeline-artifacts.yaml
      parameters:
        artifactName: ${{ parameters.artifactName }}
        unzip: ${{ parameters.unzip }}
        zipFileName: ${{ parameters.zipFileName }}
        unzipToFolderPath: ${{ parameters.unzipToFolderPath }}
    - template: ../steps/docker-build.yaml
      parameters:
        imgRepository: ${{ parameters.imgRepository }}
        imgTags: ${{ parameters.imgTags }}
        buildDockerfile: $(Agent.BuildDirectory)/${{ parameters.templateResourceName }}/Dockerfile
        buildContext: ${{ parameters.buildContext }}
        containerRegistry: ${{ parameters.containerRegistry }}
        ${{ if ne(parameters.buildArgs, &#039;&#039;) }}:
          buildArgs: ${{ parameters.buildArgs }}
        imagePreBuildSteps: ${{ parameters.imagePreBuildSteps }}
        imagePostBuildSteps: ${{ parameters.imagePostBuildSteps }}</code></pre>



<pre class="wp-block-prismatic-blocks"><code class="language-yaml"># jobs/deployCloudRun.yaml

parameters:
  - name: cloudRunProjectId
    type: string
  - name: imgRepository
    type: string
  - name: cloudRunRegion
    type: string
  - name: cloudRunServiceName
    type: string
  - name: templateResourceName
    type: string
    default: templates
  - name: pipelineResourceName
    type: string
    default: pipelines

  #======= Extensibility ========
  - name: preDeploySteps
    type: stepList
    default: []
  - name: postDeploySteps
    type: stepList
    default: []

  - name: jobName
    type: string
    default: DeployCloudRun

jobs:
  - job: ${{ parameters.jobName }}
    steps:
      - script: |    
          echo &#039;${{ convertToJson(parameters) }}&#039; &gt;&gt; parameters.json
          cat parameters.json
        displayName: Print template parameters
      - checkout: ${{ parameters.templateResourceName }}
        path: ${{ parameters.templateResourceName }}
        clean: true
      - checkout: ${{ parameters.pipelineResourceName }}
        path: ${{ parameters.pipelineResourceName }}
        clean: true
      - ${{ parameters.preDeploySteps }}
      - template: ../steps/merge-cloudRun-envVars.yaml
        parameters:
          templateResourceName: ${{ parameters.templateResourceName }}
          pipelineResourceName: ${{ parameters.pipelineResourceName }}
          projectEnvVarsFile: variables/cloudrun-envVars.yaml
          sharedEnvVarsFile: variables/cloudrun-envVars.yaml
          mergedNewFileFullPath: $(Agent.BuildDirectory)/mergedEnvVars.yaml
      - task: Bash@3
        displayName: Deploy docker image to cloudrun
        inputs:
          targetType: &#039;inline&#039;
          script: |
            docker run --rm \
            -v $(Agent.BuildDirectory)/${{ parameters.templateResourceName }}/ironman2022-gcp-key.json:/gcp/cloudKey.json \
            -v $(Agent.BuildDirectory)/mergedEnvVars.yaml:/gcp/env-vars.yaml \
            asia.gcr.io/google.com/cloudsdktool/google-cloud-cli:latest \
            bash -c &quot;gcloud auth login --cred-file=/gcp/cloudKey.json &amp;&amp; gcloud run deploy ${{ parameters.cloudRunServiceName }} --env-vars-file /gcp/env-vars.yaml --image ${{ parameters.imgRepository }} --region ${{ parameters.cloudRunRegion }} --project ${{ parameters.cloudRunProjectId }} --allow-unauthenticated&quot;
      - ${{ parameters.postDeploySteps }}</code></pre>



<pre class="wp-block-prismatic-blocks"><code class="language-yaml"># stages/deploy-cloudrun-stage.yaml

parameters:
  # deployCloudRun.yaml parameters
  - name: cloudRunProjectId
    type: string
  - name: imgRepository
    type: string
  - name: cloudRunRegion
    type: string
  - name: cloudRunServiceName
    type: string
  - name: templateResourceName
    type: string
    default: templates
  - name: pipelineResourceName
    type: string
    default: pipelines

  #======= Extensibility ========
  - name: preDeploySteps
    type: stepList
    default: []
  - name: postDeploySteps
    type: stepList
    default: []

  - name: stageName
    type: string
    default: DeployCloudRun
    
stages:
- stage: ${{ parameters.stageName }}
  displayName: 佈署Cloud Run
  jobs:
    - template: ../jobs/deployCloudRun.yaml
      parameters:
        cloudRunProjectId: ${{ parameters.cloudRunProjectId }}
        imgRepository: ${{ parameters.imgRepository }}
        cloudRunRegion: ${{ parameters.cloudRunRegion }}
        cloudRunServiceName: ${{ parameters.cloudRunServiceName }}
        templateResourceName: ${{ parameters.templateResourceName }}
        pipelineResourceName: ${{ parameters.pipelineResourceName }}
        preDeploySteps: ${{ parameters.preDeploySteps }}
        postDeploySteps: ${{ parameters.postDeploySteps }}</code></pre>



<pre class="wp-block-prismatic-blocks"><code class="language-yaml"># stages/dotnet-build-stage.yaml

parameters:
  # buildCode.yaml parameters
  - name: sourcePath
    type: string
    default: $(Build.SourcesDirectory)
  - name: slnOrCsprojName
    type: string
    default: Pipeline.sln

  # buildImage.yaml parameters
  - name: artifactName
    type: string
    default: OutputFiles
  - name: unzip
    type: boolean
    default: false
  - name: zipFileName
    type: string
    default: &#039;&#039;
  - name: unzipToFolderPath
    type: string
    default: &#039;&#039;
  - name: imgRepository
    type: string
  - name: imgTags
    type: object
  - name: buildDockerfile
    type: string
    default: Build.Dockerfile
  - name: buildContext
    type: string
  - name: containerRegistry
    type: string
  - name: startFileName
    type: string
  - name: templateResourceName
    type: string
    default: templates

  #======= Extensibility ========
  - name: imagePreBuildSteps
    type: stepList
    default: []
  - name: imagePostBuildSteps
    type: stepList
    default: []
  
  - name: codePreBuildSteps
    type: stepList
    default: []
  - name: codePostBuildSteps
    type: stepList
    default: []

  - name: stageName
    type: string
    default: BuildSourceAndImage

stages:
- stage: ${{ parameters.stageName }}
  displayName: 編譯程式碼和建立Docker Image
  jobs:
    - template: ../jobs/buildCode.yaml
      parameters:
        sourcePath: ${{ parameters.sourcePath }}
        slnOrCsprojName: ${{ parameters.slnOrCsprojName }}
        codePreBuildSteps: ${{ parameters.codePreBuildSteps }}
        codePostBuildSteps: ${{ parameters.codePostBuildSteps }}
    - template: ../jobs/buildImage.yaml
      parameters:
        templateResourceName: ${{ parameters.templateResourceName }}
        dependsOn: BuildCode
        artifactName: ${{ parameters.artifactName }}
        unzip: ${{ parameters.unzip }}
        zipFileName: ${{ parameters.zipFileName }}
        unzipToFolderPath: ${{ parameters.unzipToFolderPath }}
        imgRepository: ${{ parameters.imgRepository }}
        imgTags: ${{ parameters.imgTags }}
        buildDockerfile: ${{ parameters.buildDockerfile }}
        buildContext: ${{ parameters.buildContext }}
        containerRegistry: ${{ parameters.containerRegistry }}
        buildArgs: &quot;TARGET_FILE=${{ parameters.startFileName }}&quot;
        imagePreBuildSteps: ${{ parameters.imagePreBuildSteps }}
        # - script: |
        #     echo &quot;這是使用的範例1&quot;
        #   displayName: 範例啦
        # - task: Bash@3
        #   displayName: 還是範例啦
        #   inputs:
        #     targetType: &#039;inline&#039;
        #     script: |
        #       echo &quot;這是使用的範例2&quot;
        # - ${{ parameters.imagePreBuildSteps }}
        imagePostBuildSteps: ${{ parameters.imagePostBuildSteps }}</code></pre>
<p>這篇文章 <a href="https://tech.uccu.website/2022ironman-day25-refactor-yaml-add-more-extensibility.html">【2022鐵人賽】重構YAML範本：加入更多彈性</a> 最早出現於 <a href="https://tech.uccu.website">泰克哪裡去</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://tech.uccu.website/2022ironman-day25-refactor-yaml-add-more-extensibility.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2059</post-id>	</item>
		<item>
		<title>【2022鐵人賽】多專案命名規則與變數範本</title>
		<link>https://tech.uccu.website/2022ironman-day24-projectname-and-variables-template.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=2022ironman-day24-projectname-and-variables-template</link>
					<comments>https://tech.uccu.website/2022ironman-day24-projectname-and-variables-template.html#respond</comments>
		
		<dc:creator><![CDATA[鳴人]]></dc:creator>
		<pubDate>Sun, 09 Oct 2022 15:15:13 +0000</pubDate>
				<category><![CDATA[2022鐵人賽]]></category>
		<category><![CDATA[Azure DevOps]]></category>
		<category><![CDATA[iThome鐵人賽]]></category>
		<category><![CDATA[2022ironman]]></category>
		<category><![CDATA[azure devops]]></category>
		<guid isPermaLink="false">https://tech.uccu.website/?p=2084</guid>

					<description><![CDATA[<p>我們前面會將大部份的YAML設計都改成範本的主要原因就是為了大部份的內容可以重複使用，並且在需要調整需修改的時 ... <a title="【2022鐵人賽】多專案命名規則與變數範本" class="read-more" href="https://tech.uccu.website/2022ironman-day24-projectname-and-variables-template.html" aria-label="Read more about 【2022鐵人賽】多專案命名規則與變數範本">閱讀全文</a></p>
<p>這篇文章 <a href="https://tech.uccu.website/2022ironman-day24-projectname-and-variables-template.html">【2022鐵人賽】多專案命名規則與變數範本</a> 最早出現於 <a href="https://tech.uccu.website">泰克哪裡去</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>我們前面會將大部份的YAML設計都改成範本的主要原因就是為了大部份的內容可以重複使用，並且在需要調整需修改的時候不需要每一個專案都去修改，所以會建立多個Azure DevOps專案應該是可以預期的。</p>



<p>不過每一個專案名稱除了使用在Azure DevOps專案上之外，還有程式碼的專案/方案名稱、GCP Artifact Registry的名稱、GCP CloudRun的名稱，所以命名規則儘可能一致是必要的，名稱一致在很多事情上也比較方便。</p>



<p>有些人可能會使用像命名空間的方式來取名，利用點符號「.」來區隔階層，有些地方無法使用點符號卻可以使用減符號「-」或底線符號「_」，甚至有些服務只允許小寫名稱。使用什麼符號這些其實都還好，只要不是DevOps專案叫AAA，程式碼專案叫BBB，使用到的服務又用CCC就沒那麼麻煩。</p>



<p>一旦規則清楚，許多東西其實就不見得需要放在專案的設定內，可以將共同會有的設定值提取到範本專案中來設定，這跟程式設計進行抽象化把共同的東西放到最底層(最上層)的意思是一樣的。</p>



<p>這時候再回頭來看一遍前一篇的YAML截圖，會發現其實有很多東西應該是可以被提取出來的，也就是變數Variables的那一個區段，很多其實在每一個專案可能都會去設定它，但是內容卻是幾乎一樣的。</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="711" height="1024" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/00-NewProjectYAML-711x1024.png?resize=711%2C1024&#038;ssl=1" alt="" class="wp-image-2061" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/00-NewProjectYAML.png?resize=711%2C1024&amp;ssl=1 711w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/00-NewProjectYAML.png?resize=208%2C300&amp;ssl=1 208w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/00-NewProjectYAML.png?resize=768%2C1107&amp;ssl=1 768w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/00-NewProjectYAML.png?resize=1066%2C1536&amp;ssl=1 1066w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/00-NewProjectYAML.png?w=1211&amp;ssl=1 1211w" sizes="auto, (max-width: 711px) 100vw, 711px" /></figure></div>


<p>像是CloudRun的Region、ProjectId就一定是不會變的，所以這一篇我們就要來將這些內容抽出來做成變數範本(variables template)。</p>



<p>變數改成範本的規則和其它的範本差不多，就是從Variables開始下手，在Pipeline YAML原本的Variables底下也是使用template關鍵字來引用已改為範本的內容。</p>



<p>不過這邊我想要把它分為三個部份，分別是：</p>



<ol class="wp-block-list"><li>與程式語言無關的最上層共用變數範本</li><li>與程式語言相關的變數範本</li><li>專案專用的變數範本</li></ol>



<p>因為實際上可能碰到的情況是Azure DevOps內有許多不同的專案，每一個專案使用的程式語言可能都不一樣，至少如果有開發手持裝置App的需求的話，就一定會有後端語言和App語言不同的情況。碰到這種情況，有些變數就不會是另一種程式語言/範本需要設定的內容，像是圖中的slnOrCsprojName這個變數，就是為了.Net語言而設定的變數。</p>



<p>所以在範本專案的Git Repo裡面我會建立下面的目錄結構與檔案</p>



<pre class="wp-block-prismatic-blocks"><code class="language-">variables
  variables-template.yaml
  dotnet
    variables-template.yaml
  ios
    variables-template.yaml
</code></pre>



<p>在variables目錄底下會有一個variables-template.yaml檔案，這裡面是存放共用的變數設定。同樣的在variables目錄底下也會有不同語言的目錄，例如dotnet、ios，然後裡面再放一個variables-template.yaml。</p>



<p>上面這樣子就完成了1和2的設計了，剩下的3當然就是要放在專案自己的Git Repo裡面啦！所以在專案的Pipelines Git Repo裡面也會有個variables目錄，不過底下的檔案名稱就不會有template字樣了，畢竟它不是範本專案嘛！</p>



<pre class="wp-block-prismatic-blocks"><code class="language-">variables
  dotnet-variables.yaml
  ios-variables.yaml</code></pre>



<p>這邊我是直接以程式語言的名稱為前綴命名，方便一眼就識別出來是哪一個程式語言使用的變數檔，如果要分得再細一點的話，其實應該會再區分是給PR Pipeline使用的？還是給CI Pipeline使用的？例如：dotnet-ci-variables.yaml</p>



<p>知道檔案怎麼區分之後，接下來就直接把現有的變數移到各自的檔案中吧！</p>



<pre class="wp-block-prismatic-blocks"><code class="language-yaml"># variables/variables-template.yaml

variables:
  templateResourceName: templates
  pipelineResourceName: pipelines
  sourceResourceName: sources
  lowerCaseProjectName: $[lower(variables[&#039;normalProjectName&#039;])]
  hyphenLowerCaseProjectName: $[replace(variables[&#039;lowerCaseProjectName&#039;], &#039;.&#039;, &#039;-&#039;)]
  pipelineArtifact: output
  buildResultZipName: buildResult.zip

  #==== docker image configuration ====
  imgRepository: asia-east1-docker.pkg.dev/feisty-mechanic-363012/$(lowerCaseProjectName)/web

  #==== if deploy to Cloud Run ====
  imgRegistryService: GCPArtifactRegistry-$(normalProjectName)
  cloudRunServiceName: $(lowerCaseProjectName)
  cloudRunRegion: asia-east1
  cloudRunProjectId: feisty-mechanic-363012</code></pre>



<pre class="wp-block-prismatic-blocks"><code class="language-yaml"># variables/dotnet/variables-template.yaml

variables:
  codeType: net
  slnOrCsprojName: $(normalProjectName).sln
  buildConfiguration: &#039;release&#039; # debug/release
  startFileName: $(normalProjectName).dll</code></pre>



<pre class="wp-block-prismatic-blocks"><code class="language-yaml"># (專案的) variables/dotnet-variables.yaml

variables:
  normalProjectName: DemoProject</code></pre>



<p>上面的YAML內容剛好依照了1、2、3的順序列出來，但是如果要看懂的話，可能要從3開始看，也就是在專案內的變數檔最後只剩下設定了它的專案名稱，也就是normalProjectName這個變數。normalProjectName這個變數會在共用的變數範本檔(最上面的那個)裡面被lower函數用來轉為全小寫的lowerCaseProjectName變數，另外還有一個hyphenLowerCaseProjectName變數則是將lowerCaseProjectName變數中的點符號「.」轉為減符號「-」(如果有)，也就是如果專案名稱是使用命名空間的格式來取名稱，像是AAA.BBB.CCC這樣，那在hyphenLowerCaseProjectName變數中就會變成aaa-bbb-ccc，在某些服務上比較適合這樣的格式。</p>



<p>也因為已經將專案名稱進行了一些轉換，所以後續的其它變數就會依照需要來取用適合的變數，例如imgRepository的設定中就使用到了lowerCaseProjectName變數，而imgRegistryService則是使用了normalProjectName變數(就是Service Connection分享給其它專案時預設的格式)，cloudRunServiceName則是佈署的時候使用小寫的專案名稱。</p>



<p>在語言差異的共用變數範本中則是將normalProjectName變數用在了sln與dll檔案名稱上，在這個變數範本中另外有一個codeType變數，是為了可以在處理程式語言的範本變多的時候，利用條件式判斷來決定要插入使用哪一個範本。</p>



<p>上面三個變數檔都各自就位之後，最後就是修改最上面截圖的CI Pipeline YAML內容了，修改後的內容如下：</p>



<pre class="wp-block-prismatic-blocks"><code class="language-yaml">trigger:
- none

resources:
  repositories:
  - repository: sources
    type: git
    name: DemoProject/NetApp
    ref: Develop
    trigger:
      branches:
        include:
          - Develop
  - repository: pipelines
    type: git
    name: DemoProject/Pipelines
    ref: main
  - repository: templates
    type: git
    name: ironman2022/templates
    ref: main

variables:
  - template: variables/variables-template.yaml@templates
  - template: variables/dotnet/variables-template.yaml@templates
  - template: ../variables/dotnet-variables.yaml

stages:
  - template: stages/dotnet-build-stage.yaml@templates
    parameters:
      sourcePath: $(Build.SourcesDirectory)
      slnOrCsprojName: $(slnOrCsprojName)
      artifactName: $(pipelineArtifact)
      unzip: true
      zipFileName: $(buildResultZipName)
      unzipToFolderPath: $(System.ArtifactsDirectory)/buildImage
      imgRepository: $(imgRepository)
      imgTags: |
        latest
        $(Build.BuildId)
      buildDockerfile: $(buildDockerfile)
      buildContext: $(System.ArtifactsDirectory)/buildImage
      containerRegistry: ${{ variables.imgRegistryService }}
      startFileName: $(startFileName)
      templateResourceName: ${{ variables.templateResourceName }}
  - template: stages/deploy-cloudrun-stage.yaml@templates
    parameters:
      cloudRunProjectId: $(cloudRunProjectId)
      imgRepository: $(imgRepository)
      cloudRunRegion: $(cloudRunRegion)
      cloudRunServiceName: $(cloudRunServiceName)
      templateResourceName: ${{ variables.templateResourceName }}
      pipelineResourceName: ${{ variables.pipelineResourceName }}</code></pre>



<p>上面在variables區段就是全部改用了變數範本，順序就是前面提到的1、2、3，要留意的是只有第1個和第2個最後有@符號，代表引用範本專案內的檔案。</p>



<p>仔細看上面的YAML內容的話，你可能會覺得不太對勁，怎麼會有些地方是用$(變數名稱)，有些地方卻是用${{ variables.變數名稱 }}呢？</p>



<p>如果你還記得<a href="https://tech.uccu.website/2022ironman-day14-expressions-and-functions.html" target="_blank" rel="noreferrer noopener">認識Build Pipeline的運算式Expressions與函數</a>這篇內容提到的巨集運算式與編譯時期的運算式的話，就會知道前者是常用的巨集式用法，後者是編譯時期的用法。因為我們定義的有些變數是內嵌式還有引用到變數(imgRegistryService變數內用到其它變數)，或是其它原因，所以無法使用巨集式的用法，這時候改用後者就可以解決這樣的問題。</p>



<p>通常這是改完要執行的時候碰到錯誤訊息，上面會告訴你哪一些變數它當時無法解析，可以先改列出來的那些項目，或是全部都改用後者(因為目前到這裡沒有動態改到變數的地方，所以沒差)。</p>
<p>這篇文章 <a href="https://tech.uccu.website/2022ironman-day24-projectname-and-variables-template.html">【2022鐵人賽】多專案命名規則與變數範本</a> 最早出現於 <a href="https://tech.uccu.website">泰克哪裡去</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://tech.uccu.website/2022ironman-day24-projectname-and-variables-template.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2084</post-id>	</item>
		<item>
		<title>【2022鐵人賽】進階版-建立CI Pipeline</title>
		<link>https://tech.uccu.website/2022ironman-day23-newproject-ci-pipeline-issues.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=2022ironman-day23-newproject-ci-pipeline-issues</link>
					<comments>https://tech.uccu.website/2022ironman-day23-newproject-ci-pipeline-issues.html#respond</comments>
		
		<dc:creator><![CDATA[鳴人]]></dc:creator>
		<pubDate>Sat, 08 Oct 2022 16:02:15 +0000</pubDate>
				<category><![CDATA[2022鐵人賽]]></category>
		<category><![CDATA[Azure DevOps]]></category>
		<category><![CDATA[iThome鐵人賽]]></category>
		<category><![CDATA[2022ironman]]></category>
		<category><![CDATA[azure devops]]></category>
		<guid isPermaLink="false">https://tech.uccu.website/?p=2060</guid>

					<description><![CDATA[<p>這一篇的標題雖然和前面的「基本版-建立CI Pipeline(1)」有點像，但是並不是打算相同的內容再來一次， ... <a title="【2022鐵人賽】進階版-建立CI Pipeline" class="read-more" href="https://tech.uccu.website/2022ironman-day23-newproject-ci-pipeline-issues.html" aria-label="Read more about 【2022鐵人賽】進階版-建立CI Pipeline">閱讀全文</a></p>
<p>這篇文章 <a href="https://tech.uccu.website/2022ironman-day23-newproject-ci-pipeline-issues.html">【2022鐵人賽】進階版-建立CI Pipeline</a> 最早出現於 <a href="https://tech.uccu.website">泰克哪裡去</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>這一篇的標題雖然和前面的「<a href="https://tech.uccu.website/2022ironman-day8-ci-pipeline-buildcode.html" target="_blank" rel="noreferrer noopener">基本版-建立CI Pipeline(1)</a>」有點像，但是並不是打算相同的內容再來一次，或是有太多在YAML的部份進階的修改，因為YAML內容的修改部份都已經打散在前幾篇的內容當中了，這一篇是來談談建立新的DevOps Project之後再建立CI Pipeline的時候會碰到的問題。</p>



<p>因為大部份的規則都一樣，加上使用YAML範本，所以前面文章建立的CI Pipeline YAML檔案(放在Pipelines Git Repo)可以直接複製一份到到新的Azure DevOps專案(當然也同樣是叫Pipelines Git Repo，這樣才方便)，只是有些地方要稍微修改，這邊直接貼截圖不貼YAML內容了(因為圖可以框紅線)。</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="711" height="1024" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/00-NewProjectYAML-711x1024.png?resize=711%2C1024&#038;ssl=1" alt="" class="wp-image-2061" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/00-NewProjectYAML.png?resize=711%2C1024&amp;ssl=1 711w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/00-NewProjectYAML.png?resize=208%2C300&amp;ssl=1 208w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/00-NewProjectYAML.png?resize=768%2C1107&amp;ssl=1 768w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/00-NewProjectYAML.png?resize=1066%2C1536&amp;ssl=1 1066w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/00-NewProjectYAML.png?w=1211&amp;ssl=1 1211w" sizes="auto, (max-width: 711px) 100vw, 711px" /></figure></div>


<p>圖中可以看到紅線的地方就是要調整的地方，就是把名稱改成新建立的DevOps專案名稱而已。</p>



<p>建立CI Pipeline的步驟就直接省略了，直接看看建立完成之後會碰到哪些問題吧！</p>



<p>首先是GCP的Service Connection授權問題，如下圖：</p>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="488" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/01-ServiceConnectionAuthorization-1024x488.png?resize=1024%2C488&#038;ssl=1" alt="" class="wp-image-2062" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/01-ServiceConnectionAuthorization.png?resize=1024%2C488&amp;ssl=1 1024w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/01-ServiceConnectionAuthorization.png?resize=300%2C143&amp;ssl=1 300w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/01-ServiceConnectionAuthorization.png?resize=768%2C366&amp;ssl=1 768w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/01-ServiceConnectionAuthorization.png?w=1236&amp;ssl=1 1236w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>就算你直接去按了「Authorize resources」按鈕，會發現也沒有什麼效果，因為真正的動作步驟應該是下面這樣：</p>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="472" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/02-ServiceConnectionSecuritySettings-1024x472.png?resize=1024%2C472&#038;ssl=1" alt="" class="wp-image-2063" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/02-ServiceConnectionSecuritySettings.png?resize=1024%2C472&amp;ssl=1 1024w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/02-ServiceConnectionSecuritySettings.png?resize=300%2C138&amp;ssl=1 300w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/02-ServiceConnectionSecuritySettings.png?resize=768%2C354&amp;ssl=1 768w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/02-ServiceConnectionSecuritySettings.png?w=1184&amp;ssl=1 1184w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>因為Service Connection是建立在前面一開始建立Service Connection的專案，也是我們存放YAML範本的專案，所以要到那個專案的Service Connection裡面去Share它，動作如上圖所示。</p>



<figure class="wp-block-image size-full"><img data-recalc-dims="1" loading="lazy" decoding="async" width="888" height="246" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/03-ServiceConnectionSecuritySettings-ProjectPermissions1.png?resize=888%2C246&#038;ssl=1" alt="" class="wp-image-2064" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/03-ServiceConnectionSecuritySettings-ProjectPermissions1.png?w=888&amp;ssl=1 888w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/03-ServiceConnectionSecuritySettings-ProjectPermissions1.png?resize=300%2C83&amp;ssl=1 300w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/03-ServiceConnectionSecuritySettings-ProjectPermissions1.png?resize=768%2C213&amp;ssl=1 768w" sizes="auto, (max-width: 888px) 100vw, 888px" /></figure>



<p>在Project permissions的區段按下加號，把剛剛建立的新專案加進來，然後就會看到下圖的結果，會使用原本的名稱再加上「-新專案名稱」，這個會同時加在新專案的Service Connection，或是說它只是在這裡是個關聯，在新專案那邊改了名稱之後，這邊的名稱也會變的。</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-recalc-dims="1" loading="lazy" decoding="async" width="417" height="169" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/04-ServiceConnectionSecuritySettings-ProjectPermissions2.png?resize=417%2C169&#038;ssl=1" alt="" class="wp-image-2065" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/04-ServiceConnectionSecuritySettings-ProjectPermissions2.png?w=417&amp;ssl=1 417w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/04-ServiceConnectionSecuritySettings-ProjectPermissions2.png?resize=300%2C122&amp;ssl=1 300w" sizes="auto, (max-width: 417px) 100vw, 417px" /></figure></div>


<p>上面紅框的名稱同時也就是在最上面YAML截圖中的imgRegistryService變數的值。</p>



<p>接著再重新執行CI Pipeline，會發現它開始跑了(中間需要授權相關資源的類似截圖請看後面的圖，這邊忘了截圖)，不過跑到BuildImage這個Job的時候卻發現它又失敗了。</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="543" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/05-TemplateRepoPermissions-TF401019-1024x543.png?resize=1024%2C543&#038;ssl=1" alt="" class="wp-image-2066" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/05-TemplateRepoPermissions-TF401019.png?resize=1024%2C543&amp;ssl=1 1024w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/05-TemplateRepoPermissions-TF401019.png?resize=300%2C159&amp;ssl=1 300w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/05-TemplateRepoPermissions-TF401019.png?resize=768%2C408&amp;ssl=1 768w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/05-TemplateRepoPermissions-TF401019.png?w=1402&amp;ssl=1 1402w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure></div>


<p>這是怎麼回事呢？其實是要跨專案使用YAML範本的Git Repo所發生的授權問題，在Log中也可以發現關鍵字是TF401019這個問題。</p>



<p>要解決這個問題，其實是要在專案設定中把兩個選項關閉，不過一開始進入專案設定的時候會發現它是灰色無法更改的…</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-recalc-dims="1" loading="lazy" decoding="async" width="809" height="659" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/06-TemplateProjectSettings.png?resize=809%2C659&#038;ssl=1" alt="" class="wp-image-2067" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/06-TemplateProjectSettings.png?w=809&amp;ssl=1 809w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/06-TemplateProjectSettings.png?resize=300%2C244&amp;ssl=1 300w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/06-TemplateProjectSettings.png?resize=768%2C626&amp;ssl=1 768w" sizes="auto, (max-width: 809px) 100vw, 809px" /></figure></div>


<p>專案的設定會是這樣灰色無法更改的狀態，通常就代表它是從更上層繼承下來的設定，所以只要往上追到組織層級的設定就可以看到可以更改的狀態了：</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-recalc-dims="1" loading="lazy" decoding="async" width="800" height="660" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/07-OrgSettings.png?resize=800%2C660&#038;ssl=1" alt="" class="wp-image-2068" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/07-OrgSettings.png?w=800&amp;ssl=1 800w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/07-OrgSettings.png?resize=300%2C248&amp;ssl=1 300w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/07-OrgSettings.png?resize=768%2C634&amp;ssl=1 768w" sizes="auto, (max-width: 800px) 100vw, 800px" /></figure></div>


<p>改完組織層級的設定之後，再回到專案設定中去關閉那兩個選項就行了。</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-recalc-dims="1" loading="lazy" decoding="async" width="783" height="629" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/08-TemplateProjectSettings.png?resize=783%2C629&#038;ssl=1" alt="" class="wp-image-2069" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/08-TemplateProjectSettings.png?w=783&amp;ssl=1 783w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/08-TemplateProjectSettings.png?resize=300%2C241&amp;ssl=1 300w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/08-TemplateProjectSettings.png?resize=768%2C617&amp;ssl=1 768w" sizes="auto, (max-width: 783px) 100vw, 783px" /></figure></div>


<p>再重新執行CI Pipeline，雖然在Checkout範本的那關過了，不過卻發現在Push Docker Image的階段失敗了，因為在GCP的Artifact Registry裡面並沒有建立新專案的存放區，這部份請參考<a href="https://tech.uccu.website/2022ironman-day4-create-registry-and-service-account.html" target="_blank" rel="noreferrer noopener">前面的文章</a>來建立。</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="434" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/09-PushDockerImage-RepositoryNotFound-1024x434.png?resize=1024%2C434&#038;ssl=1" alt="" class="wp-image-2070" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/09-PushDockerImage-RepositoryNotFound.png?resize=1024%2C434&amp;ssl=1 1024w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/09-PushDockerImage-RepositoryNotFound.png?resize=300%2C127&amp;ssl=1 300w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/09-PushDockerImage-RepositoryNotFound.png?resize=768%2C326&amp;ssl=1 768w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/09-PushDockerImage-RepositoryNotFound.png?w=1059&amp;ssl=1 1059w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure></div>


<p>建立完GCP的Artifact Registry之後再重新執行CI Pipeline，如果是在執行的Log畫面等待，可能會發現怎麼好像不會動？這時候就有可能是有資源需要授權，正等待著去Permit它(只有第一次會這樣)，這在一開始建立CI Pipeline要執行的時候應該就會碰到，只是前面我忘了截圖了。</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="517" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/10-ResourcesPermit-1024x517.png?resize=1024%2C517&#038;ssl=1" alt="" class="wp-image-2071" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/10-ResourcesPermit.png?resize=1024%2C517&amp;ssl=1 1024w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/10-ResourcesPermit.png?resize=300%2C151&amp;ssl=1 300w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/10-ResourcesPermit.png?resize=768%2C388&amp;ssl=1 768w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/10/10-ResourcesPermit.png?w=1303&amp;ssl=1 1303w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure></div>


<p>按下Permit之後應該就可以正常執行完所有的動作，在Log中也可以看得到CloudRun被正確佈署了。</p>
<p>這篇文章 <a href="https://tech.uccu.website/2022ironman-day23-newproject-ci-pipeline-issues.html">【2022鐵人賽】進階版-建立CI Pipeline</a> 最早出現於 <a href="https://tech.uccu.website">泰克哪裡去</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://tech.uccu.website/2022ironman-day23-newproject-ci-pipeline-issues.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2060</post-id>	</item>
		<item>
		<title>【2022鐵人賽】使用Task與CLI的抉擇</title>
		<link>https://tech.uccu.website/2022ironman-day22-task-or-cli.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=2022ironman-day22-task-or-cli</link>
					<comments>https://tech.uccu.website/2022ironman-day22-task-or-cli.html#respond</comments>
		
		<dc:creator><![CDATA[鳴人]]></dc:creator>
		<pubDate>Sat, 08 Oct 2022 14:37:38 +0000</pubDate>
				<category><![CDATA[2022鐵人賽]]></category>
		<category><![CDATA[Azure DevOps]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[iThome鐵人賽]]></category>
		<category><![CDATA[2022ironman]]></category>
		<category><![CDATA[azure devops]]></category>
		<category><![CDATA[container]]></category>
		<category><![CDATA[linux]]></category>
		<guid isPermaLink="false">https://tech.uccu.website/?p=2055</guid>

					<description><![CDATA[<p>透過CLI就是組Scripts來達成目的，所以彈性相對高很多。除了可以Build Source Code之外，要Pack Nuget Package也是同樣透過Sdk CLI就可以達成。但是如果是用Task的話，可能就會需要分成不同的Task。</p>
<p>這篇文章 <a href="https://tech.uccu.website/2022ironman-day22-task-or-cli.html">【2022鐵人賽】使用Task與CLI的抉擇</a> 最早出現於 <a href="https://tech.uccu.website">泰克哪裡去</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>這篇要來聊聊Azure DevOps Pipeline的Task與CLI的抉擇，為什麼會這樣說呢？</p>



<p>因為其實在Azure DevOps Pipeline中已經有提供許多不錯的Task設計，尤其.Net的程式在Azure DevOps Pipeline中有各式各樣對應的Task可以使用，像去年的文章「<a href="https://tech.uccu.website/2021ironman-day7-first-pipeline-template-and-editor.html">CI/CD從這裡：設定第一個Pipeline(範本與編輯介面介紹)</a>」裡面就是使用內建的VSBuild Task來編譯.Net的程式碼，但是今年卻在「<a href="https://tech.uccu.website/2022ironman-day8-ci-pipeline-buildcode.html" target="_blank" rel="noreferrer noopener">基本版-建立CI Pipeline(1)</a>」文章內一開始就使用.Net Sdk的Container來編譯.Net的程式碼…</p>



<p>除了今年的文章屬於進階應用之外，還有其它幾個不同的因素考量：</p>



<ul class="wp-block-list"><li>Task要透過GUI才能快速方便知道有什麼參數可以選擇與設定</li><li>不同功能可能要選用不同task</li><li>可以達到相同功能的Task可能不只一個</li><li>想要測試看看執行結果如何無法在Pipeline以外的環境執行</li><li>用Container或直接用Sdk CLI可以在本機執行看看結果</li><li>新功能或指令在Task可能沒有設計或支援</li><li>如果是自管的Agent只要裝了Docker就可以</li></ul>



<p>因為透過CLI就是組Scripts來達成目的，所以彈性相對高很多。例如.Net Core Sdk CLI除了可以Build Source Code之外，要Pack Nuget Package也是同樣透過Sdk CLI就可以達成，只是指令的組成不太一樣。但是如果是用Task的話，就會需要分成Nuget Task與.Net Core Task，或是pack也用.Net Core Task而不是用Nuget Task。</p>



<p>另外還有像是我們使用Azure DevOps Artifacts功能來存放私有的Nuget Packages，不過要Push上去是需要身份驗證授權的，在Task的選擇部份就有Nuget authenticate Task(搭配設定Service Connection)，或是有個nuget.config檔案，裡面寫入PAT然後放在特定的位置來自動解決身份驗證的問題。</p>



<p>所以原本的dotnet-build-in-linux-container.yaml就可以重新做一份step template：</p>



<pre class="wp-block-prismatic-blocks"><code class="language-yaml"># steps/dotnet-sdk-in-linux-container.yaml

parameters:
  - name: dotnetSdkImgRepository
    type: string
    default: mcr.microsoft.com/dotnet/sdk:6.0-alpine
  - name: srcPath
    type: string
  - name: outputPath
    type: string
    default: $(Build.ArtifactStagingDirectory)/outputFiles/
  - name: slnOrCsprojName
    type: string
    default: Pipeline.sln
  - name: dotnetCommand
    type: string
  - name: dotnetCommandArgs
    type: string

steps:
  - script: |    
      echo &quot;Dotnet sdk in linux container template&quot;
      echo &quot;${{ convertToJson(parameters) }}&quot;
    displayName: Print template parameters
  - task: Bash@3
    displayName: Run dotnet sdk command in container
    inputs:
      targetType: &#039;inline&#039;
      script: |
        if [ -d ${{ parameters.outputPath }} ]; then echo &quot;${{ parameters.outputPath }} exist&quot;; else mkdir -p ${{ parameters.outputPath }}; fi
        export UID=$(id -u)
        export GID=$(id -g)
        docker run --user $UID:$GID --rm \
        -v ${{ parameters.srcPath }}:/tmp/source \
        -v ${{ parameters.outputPath }}:/tmp/publish \
        -e DOTNET_CLI_HOME=/tmp/.dotnet \
        ${{ parameters.dotnetSdkImgRepository }} \
        dotnet ${{ parameters.dotnetCommand }} /tmp/source/${{ parameters.slnOrCsprojName }} \
        -o /tmp/publish ${{ parameters.dotnetCommandArgs }} \
  - task: ArchiveFiles@2
    displayName: 壓縮成zip
    inputs:
      rootFolderOrFile: $(Build.BinariesDirectory)
      includeRootFolder: false
      archiveType: &#039;zip&#039;
      archiveFile: &#039;$(Build.ArtifactStagingDirectory)/zipFiles/buildResult.zip&#039;
      replaceExistingArchive: true</code></pre>



<p>這樣就可以用同樣的一個範本搞定Build Source Code、Pack Nuget Package之類的事情，對應在jobs/buildCode.yaml就是把引用的template檔案改成新的，然後加上dotnetCommand、dotnetCommandArgs參數的設定就行了。</p>



<pre class="wp-block-prismatic-blocks"><code class="language-yaml">      - template: ../steps/dotnet-sdk-in-linux-container.yaml
        parameters:
          srcPath: ${{ parameters.sourcePath }}
          slnOrCsprojName: ${{ parameters.slnOrCsprojName }}
          dotnetCommand: publish
          dotnetCommandArgs: &#039;-c ${{ parameters.buildConfiguration }}&#039;</code></pre>
<p>這篇文章 <a href="https://tech.uccu.website/2022ironman-day22-task-or-cli.html">【2022鐵人賽】使用Task與CLI的抉擇</a> 最早出現於 <a href="https://tech.uccu.website">泰克哪裡去</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://tech.uccu.website/2022ironman-day22-task-or-cli.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2055</post-id>	</item>
		<item>
		<title>【2022鐵人賽】不同.Net Docker Image共用Dockerfile</title>
		<link>https://tech.uccu.website/2022ironman-day21-dotnet-startfile-in-dockerfile.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=2022ironman-day21-dotnet-startfile-in-dockerfile</link>
					<comments>https://tech.uccu.website/2022ironman-day21-dotnet-startfile-in-dockerfile.html#respond</comments>
		
		<dc:creator><![CDATA[鳴人]]></dc:creator>
		<pubDate>Thu, 06 Oct 2022 15:52:38 +0000</pubDate>
				<category><![CDATA[2022鐵人賽]]></category>
		<category><![CDATA[Azure DevOps]]></category>
		<category><![CDATA[iThome鐵人賽]]></category>
		<category><![CDATA[2022ironman]]></category>
		<category><![CDATA[azure devops]]></category>
		<guid isPermaLink="false">https://tech.uccu.website/?p=2048</guid>

					<description><![CDATA[<p>.Net的Docker Image裡面我們是使用dotnet指令加上要執行的DLL檔名去啟動，但是在多個不同的專案就會有不同的DLL檔名，原本在Dockerfile裡面是寫死固定的DLL檔名，這篇透過使用ARG和ENV的方式讓.Net使用的Dockerfile也只需要一份公版範本。</p>
<p>這篇文章 <a href="https://tech.uccu.website/2022ironman-day21-dotnet-startfile-in-dockerfile.html">【2022鐵人賽】不同.Net Docker Image共用Dockerfile</a> 最早出現於 <a href="https://tech.uccu.website">泰克哪裡去</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>終於在前面把該拆成範本的YAML差不多都搞定了。</p>



<p>不過如果這樣子就要開始應用在多個不同的Azure DevOps專案，並且是.Net開發的系統而且使用Docker Image的話，可能就會碰到一個需要思考的問題，那就是每個.Net系統都要有一個自己的Dockerfile檔案嗎？</p>



<p>為什麼會這樣子說呢？如果你還記得前面「<a href="https://tech.uccu.website/2022ironman-day9-ci-pipeline-buildimage.html" target="_blank" rel="noreferrer noopener">基本版-建立CI Pipeline(2)</a>」這篇文章裡面使用到的Dockerfile，最後一行的內容是下面這樣：</p>



<pre class="wp-block-prismatic-blocks"><code class="language-">ENTRYPOINT [&quot;dotnet&quot;, &quot;IronmanWeb.dll&quot;]</code></pre>



<p>也就是說在EntryPoint裡面使用dotnet指令傳入的是.Net DLL的名稱，不同的系統會編譯出不同的DLL名稱，所以這個內容如果是寫死在Dockerfile裡面，那麼每一個.Net系統都需要有一份自己的Dockerfile…</p>



<p>我知道現在新版本的Visual Studio已經有功能可以自動從專案或方案自動產生出來相對應的Dockerfile，不過…其實不少開發人員是搞不懂裡面的內容是在做什麼的。</p>



<p>而且每一個系統需要一份Dockerfile，那如果忘了產生，或是內容忘了更新，或是檔案沒有傳到版控亦或是被不小心刪掉了呢？</p>



<p>或許你會說…檔案不是放在各別專案的Pipelines Git Repo嗎？開發人員不是碰不到這個Repository？</p>



<p>對…，放在各別專案的Pipelines Git Repo是不歸開發人員管，但是檔案是開發人員產生之後給你？還是你幫每一個專案都寫一份Dockerfile？弄一個範本的Dockerfile然後新專案再根據DLL名稱去更改？</p>



<p>上面不管是哪一個方法都可行，放在Source Code的Git Repo裡面，從YAML範本中去checkout sources找到指定檔名的Dockerfile，或是放在Pipelines Git Repo裡面，檔案由開發人員提供或是用範本改DLL名稱都可以。</p>



<p>不過更好的方式是在Templates Git Repo裡面有一份.Net用的Dockerfile，不同的系統傳入不同的DLL名稱，也就是在Dockerfile裡面加上ARG和ENV的使用，下面比較一下修改前的Dockerfile和修改後的Dockerfile差異吧！</p>



<pre class="wp-block-prismatic-blocks"><code class="language-"># 原本的Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:6.0-alpine

WORKDIR /app
COPY . .

ENTRYPOINT [&quot;dotnet&quot;, &quot;IronmanWeb.dll&quot;]</code></pre>



<pre class="wp-block-prismatic-blocks"><code class="language-"># 修改後的Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:6.0-alpine

# 加上ARG，名稱為TARGET_FILE
ARG TARGET_FILE

WORKDIR /app
COPY . .

# 加上ENV，名稱為APP，設定值是/app/${TARGET_FILE}
ENV APP=/app/${TARGET_FILE}

# 改用CMD執行dotnet並且使用APP環境變數
CMD dotnet ${APP}

# 加上EXPOSE對外開放80 Port
EXPOSE 80</code></pre>



<p>改成新的內容之後，對應的steps/docker-build.yaml step template也要修改一下：</p>



<pre class="wp-block-prismatic-blocks"><code class="language-yaml">parameters:
  - name: imgRepository
    type: string
  - name: imgTags
    type: object
  - name: buildDockerfile
    type: string
    default: Build.Dockerfile
  - name: buildContext
    type: string
  - name: containerRegistry
    type: string
  # 增加buildArgs參數
  - name: buildArgs
    type: string
    default: &#039;&#039;

steps:
  - task: Docker@2
    displayName: Build image
    inputs:
      repository: &#039;${{ parameters.imgRepository }}&#039;
      command: &#039;build&#039;
      Dockerfile: ${{ parameters.buildDockerfile }}
      buildContext: ${{ parameters.buildContext }}
      # 原本是arguments: &#039;--no-cache&#039;，利用條件判斷決定要不要傳入--build-arg
      ${{ if eq(parameters.buildArgs, &#039;&#039;) }}:
        arguments: &#039;--no-cache&#039;
      ${{ else }}:
        arguments: &#039;--no-cache --build-arg ${{ parameters.buildArgs }}&#039;
      tags: ${{ parameters.imgTags }}
  - task: Docker@2
    displayName: &quot;Login to Container Registry&quot;
    inputs:
      command: login
      containerRegistry: ${{ parameters.containerRegistry }}
  - task: Bash@3
    displayName: Push docker image
    inputs:
      targetType: &#039;inline&#039;
      script: |
        docker push -a ${{ parameters.imgRepository }}</code></pre>



<p>當然相對應的jobs/buildImage.yaml job template也要加上對應的參數設定：</p>



<pre class="wp-block-prismatic-blocks"><code class="language-yaml">parameters:
  - name: artifactName
    type: string
    default: OutputFiles
  - name: unzip
    type: boolean
    default: false
  - name: zipFileName
    type: string
    default: &#039;&#039;
  - name: unzipToFolderPath
    type: string
    default: &#039;&#039;
  # 因為Dockerfile從範本的Git Repo取得，所以要加上定義的名稱
  - name: templateResourceName
    type: string
    default: templates

  - name: imgRepository
    type: string
  - name: imgTags
    type: object
  - name: buildDockerfile
    type: string
    default: Build.Dockerfile
  - name: buildContext
    type: string
  - name: containerRegistry
    type: string
  # 加上buildArgs參數
  - name: buildArgs
    type: string
    default: &#039;&#039;

jobs:
  - job: BuildImage
    dependsOn: BuildCode
    steps:
    # 加上checkout從範本所在的Git Repo取得Dockerfile
    - checkout: ${{ parameters.templateResourceName }}
      path: ${{ parameters.templateResourceName }}
      clean: true
    - template: ../steps/download-pipeline-artifacts.yaml
      parameters:
        artifactName: ${{ parameters.artifactName }}
        unzip: ${{ parameters.unzip }}
        zipFileName: ${{ parameters.zipFileName }}
        unzipToFolderPath: ${{ parameters.unzipToFolderPath }}
    - template: ../steps/docker-build.yaml
      parameters:
        imgRepository: ${{ parameters.imgRepository }}
        imgTags: ${{ parameters.imgTags }}
        buildDockerfile: ${{ parameters.buildDockerfile }}
        buildContext: ${{ parameters.buildContext }}
        containerRegistry: ${{ parameters.containerRegistry }}
        # 用條件判斷要不要加上buildArgs參數設定
        ${{ if ne(parameters.buildArgs, &#039;&#039;) }}:
          buildArgs: ${{ parameters.buildArgs }}
</code></pre>



<p>在stages/dotnet-build-stage.yaml stage template的部份則是增加startFileName參數，讓專案的Pipeline YAML傳入真正要執行的檔名：</p>



<pre class="wp-block-prismatic-blocks"><code class="language-">parameters:
  # buildCode.yaml parameters
  - name: sourcePath
    type: string
    default: $(Build.SourcesDirectory)
  - name: slnOrCsprojName
    type: string
    default: Pipeline.sln

  # buildImage.yaml parameters
  - name: artifactName
    type: string
    default: OutputFiles
  - name: unzip
    type: boolean
    default: false
  - name: zipFileName
    type: string
    default: &#039;&#039;
  - name: unzipToFolderPath
    type: string
    default: &#039;&#039;
  - name: imgRepository
    type: string
  - name: imgTags
    type: object
  - name: buildDockerfile
    type: string
    default: Build.Dockerfile
  - name: buildContext
    type: string
  - name: containerRegistry
    type: string
  # 讓專案的YAML傳入真正要執行的DLL檔名
  - name: startFileName
    type: string
  # 因為Dockerfile從範本的Git Repo取得，所以要加上定義的名稱
  - name: templateResourceName
    type: string
    default: templates    

stages:
- stage: BuildSourceAndImage
  displayName: 編譯程式碼和建立Docker Image
  jobs:
    - template: ../jobs/buildCode.yaml
      parameters:
        sourcePath: ${{ parameters.sourcePath }}
        slnOrCsprojName: ${{ parameters.slnOrCsprojName }}
    - template: ../jobs/buildImage.yaml
      parameters:
        # 加上templateResourceName參數
        templateResourceName: ${{ parameters.templateResourceName }}
        artifactName: ${{ parameters.artifactName }}
        unzip: ${{ parameters.unzip }}
        zipFileName: ${{ parameters.zipFileName }}
        unzipToFolderPath: ${{ parameters.unzipToFolderPath }}
        imgRepository: ${{ parameters.imgRepository }}
        imgTags: ${{ parameters.imgTags }}
        buildDockerfile: ${{ parameters.buildDockerfile }}
        buildContext: ${{ parameters.buildContext }}
        containerRegistry: ${{ parameters.containerRegistry }}
        # 因為在.Net系統一定會使用buildArgs，並且設定TARGET_FILE這個在Dockerfile內設定的ARG，所以不用條件判斷
        buildArgs: &quot;TARGET_FILE=${{ parameters.startFileName }}&quot;</code></pre>



<p>最後就是在專案的CI Pipeline YAML內增加startFileName的參數設定就行了，這部份只貼有修改的部份YAML內容：</p>



<pre class="wp-block-prismatic-blocks"><code class="language-yaml">stages:
  - template: stages/dotnet-build-stage.yaml@templates
    parameters:
      sourcePath: $(Build.SourcesDirectory)
      slnOrCsprojName: $(slnOrCsprojName)
      artifactName: &#039;$(pipelineArtifact)&#039;
      unzip: true
      zipFileName: buildResult.zip
      unzipToFolderPath: $(System.ArtifactsDirectory)/buildImage
      imgRepository: $(imgRepository)
      imgTags: |
        latest
      buildDockerfile: $(buildDockerfile)
      buildContext: $(System.ArtifactsDirectory)/buildImage
      containerRegistry: $(imgRegistryService)
      # 加上下面這兩行
      startFileName: IronmanWeb.dll
      templateResourceName: templates</code></pre>



<p>上面加上startFileName的部份更好的方式應該是透過變數來設定，因為別的地方可能也會用到專案的名稱(以這邊的例子就是IronmanWeb)，所以或許會是下面這樣：</p>



<pre class="wp-block-prismatic-blocks"><code class="language-">variables:
  projectName: IronmanWeb
  startFileName: $(projectName).dll</code></pre>



<p>這樣和projectName一樣的地方就只需要設定一次，修改也不會漏改其它地方。</p>
<p>這篇文章 <a href="https://tech.uccu.website/2022ironman-day21-dotnet-startfile-in-dockerfile.html">【2022鐵人賽】不同.Net Docker Image共用Dockerfile</a> 最早出現於 <a href="https://tech.uccu.website">泰克哪裡去</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://tech.uccu.website/2022ironman-day21-dotnet-startfile-in-dockerfile.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2048</post-id>	</item>
	</channel>
</rss>
