2019年9月20日 星期五

[教學]如何把Github上面的.netStandard專案自動部屬到Nuget.org(使用Appveyor和build.cake)

# 前言 : 最近在寫專案 想說趁機試一下如何把自動化佈署 把`.netStandard`的專案部屬到`nuget`上 . 結果找了好久發現大家教學寫得都不一樣 三洨 . 找了找最後覺得[這篇](https://dev.to/joaofbantunes/creating-a-cicd-pipeline-for-a-net-library-part-1---intro-2j61?fbclid=IwAR2P8wr3x75airwMQLkpyFC73DZFDXF1l1JZna_XfvG_7L2mdXa45kQo674)寫得最好 就整理一下心得好了 . # 佈署方式 : 簡單來說,CI(持續整合) CD(自動佈署)有好幾種方法 最基本的兩種 1. CI/CD 供應商(例如`Appveyor`)會提供自己的設定檔和格式,通常是`appveyor.yml ` 可以在裡面設定一些參數 優點是快速上手,通常自動測試設定很快就完成了 但缺點是不同的自動化平台的設定方式會有些差別 2. 叫CI跑自己設定好的腳本 雖然說多了幾個不明所以的檔案會有點礙眼 但如果哪天要從`appveyor`換到`Tr@vis CI`,只要讓`travis.yml`跑自己寫好的腳本就好了 . 還有本地也可以執行腳本,不需要等到push後才知道跑出來的結果 . 內建的參數有點玩膩了(並沒有 就來玩玩看用腳本自動佈署的方式好了 . # 執行流程 先簡單說明一下執行過程好了,以`Appveyor`為例 在跑CI/CD時 . 執行的流程大概會是 : 1. `Appveyor`會抓取專案內的`appveyor.yml`設定 2. 然後讓`Appveyor`執行`build.ps1` 並且帶入參數,例如`目前執行的branch`,還有`nuget上傳的金鑰`等等 3. `build.ps1`再執行`build.cake`,會把剛剛的那些參數傳過去 4. `build.cake`會定義一些流程,然後可以根據`不同參數`執行`不同動作` 假如在`master`上,就會自動佈署 如果是`develop`或是其他Branch,就只跑測試就好 . 所以說,跑腳本可以做的事情,會比`appveyor.yml`上面設定參數的自由度更高 . # 腳本(build.cake) 這邊的說明方式是直接把整份`build.cake`直接翻開來加上註解做說明 也可以從[這邊](https://github.com/osu-Karaoke/LyricMaker/blob/develop/build.cake)看 . 首先,先產生`build.ps1`檔 詳細的方法可以參考[這邊](https://dotblogs.com.tw/mileslin/2016/04/23/124859) . 自動產生出來的`build.ps1`不用動他 . 接下來新增`build.cake` 然後複製[這邊](https://github.com/osu-Karaoke/LyricMaker/blob/develop/build.cake)的內容過去 然後在修改成自己想要的樣子 . 這邊簡單說明一下這份`build.cake`工作方式是 平常PR時跑測試 如果在`master`上做事或是有東西合併進來,就佈署一份到`nuget.org`上 . 如果大家要做的事情也差不多 就只要修改`上面的參數名稱`就好了 ```csharp // 這邊不用管他,簡單來說當作要引用nuget的感覺 #tool "nuget:?package=coveralls.io&version=1.4.2" #addin Cake.Git #addin nuget:?package=Nuget.Core #addin "nuget:?package=Cake.Coveralls&version=0.9.0" // 因為要丟到nuget上所以using這個package using NuGet; // 預設要執行的項目 var target = Argument("target", "Default"); // 產生出來的nuget要放這個目錄 var artifactsDir = "./artifacts/"; // 方案位置,要改成自己的方案名稱 var solutionPath = "./LyricMaker.sln"; // 要包成nuget的專案,要改成自己的方案名稱 var project = "./LyricMaker/LyricMaker.csproj"; // 測試目錄,和對應需要測試的方案名稱,也是要改 var testFolder = "./LyricMaker.Tests/"; var testProject = testFolder + "LyricMaker.Tests.csproj"; // CodeCoverage清單,但不才暫時沒需要所以有註解調 var coverageResultsFileName = "coverage.xml"; // 從丟進來的參數中取得目前Branch名稱 var currentBranch = Argument("currentBranch", GitBranchCurrent("./").FriendlyName); // 判斷是不是master var isReleaseBuild = string.Equals(currentBranch, "master", StringComparison.OrdinalIgnoreCase); // 預設是Release var configuration = "Release"; // 從丟進來的參數中取得nuget金鑰和codecoverage(目前沒用到)的金鑰 var nugetApiKey = Argument("nugetApiKey", null); var coverallsToken = Argument("coverallsToken", null); // nuget.org網址 var nugetSource = "https://api.nuget.org/v3/index.json"; // 定義任務,把空間清乾淨 Task("Clean") .Does(() => { if (DirectoryExists(artifactsDir)) { DeleteDirectory( artifactsDir, new DeleteDirectorySettings { Recursive = true, Force = true } ); } CreateDirectory(artifactsDir); DotNetCoreClean(solutionPath); }); // 定義任務,載入nuget Task("Restore") .Does(() => { DotNetCoreRestore(solutionPath); }); // 定義任務,建置 Task("Build") .IsDependentOn("Clean") .IsDependentOn("Restore") .Does(() => { DotNetCoreBuild( solutionPath, new DotNetCoreBuildSettings { Configuration = configuration } ); }); // 定義任務,跑測試 Task("Test") .Does(() => { var settings = new DotNetCoreTestSettings { ArgumentCustomization = args => args.Append("/p:CollectCoverage=true") .Append("/p:CoverletOutputFormat=opencover") //.Append("/p:CoverletOutput=./" + coverageResultsFileName) }; DotNetCoreTest(testProject, settings); //MoveFile(testFolder + coverageResultsFileName, artifactsDir + coverageResultsFileName); }); // 定義任務,上傳跑任務時產生的Code coverage結果 Task("UploadCoverage") .IsDependentOn("Test") .Does(() => { CoverallsIo(artifactsDir + coverageResultsFileName, new CoverallsIoSettings() { RepoToken = coverallsToken }); }); // 定義任務,包成nuget Task("Package") .Does(() => { var settings = new DotNetCorePackSettings { OutputDirectory = artifactsDir, NoBuild = true }; DotNetCorePack(project, settings); }); // 定義任務,把nuget上傳 Task("Publish") .IsDependentOn("Package") .Does(() => { var pushSettings = new DotNetCoreNuGetPushSettings { Source = nugetSource, ApiKey = nugetApiKey }; var pkgs = GetFiles(artifactsDir + "*.nupkg"); foreach(var pkg in pkgs) { if(!IsNuGetPublished(pkg)) { Information($"Publishing \"{pkg}\"."); DotNetCoreNuGetPush(pkg.FullPath, pushSettings); } else { // 如果看到這個訊息,代表專案的nuget版本號在nuget.org上已經有了 Information($"Bypassing publishing \"{pkg}\" as it is already published."); } } }); // Check nuget package is published private bool IsNuGetPublished(FilePath packagePath) { var package = new ZipPackage(packagePath.FullPath); var latestPublishedVersions = NuGetList( package.Id, new NuGetListSettings { Prerelease = true } ); return latestPublishedVersions.Any(p => package.Version.Equals(new SemanticVersion(p.Version))); } // 定義任務,這個任務要完成編譯和測試 Task("BuildAndTest") .IsDependentOn("Build") .IsDependentOn("Test"); // 定義任務,這個任務要完成編譯,測試和上傳結果 Task("CompleteWithoutPublish") .IsDependentOn("Build") .IsDependentOn("Test"); //.IsDependentOn("UploadCoverage"); if(isReleaseBuild) { // 定義完成任務 // 玩成任務就是執行編譯,測試和佈署到nuget Information("Release build"); Task("Complete") .IsDependentOn("Build") .IsDependentOn("Test") //.IsDependentOn("UploadCoverage") .IsDependentOn("Publish"); } else { // 定義完成任務 // 完成任務就只需要跑編譯和測試 Information("Development build"); Task("Complete") .IsDependentOn("Build") .IsDependentOn("Test"); //.IsDependentOn("UploadCoverage"); } // 定義預設任務是 完成任務 Task("Default") .IsDependentOn("Complete"); // 執行預設任務 RunTarget(target); ``` # 腳本(appveyor.yml) 接下來新增`build.cake` 然後複製[這邊](https://github.com/osu-Karaoke/LyricMaker/blob/develop/build.cake)的內容過去 也是在修改成自己想要的樣子 . 也是說明一下專案格式 這樣比較好懂 ```yml # 建置版本 version: "{build}" # 使用的環境 image: Visual Studio 2017 # 定義只有在那些Branch上Appveyor才做事 # 因為不才希望丟Pull Request也要跑CI,所以就註解掉了 #branches: # only: # - master # - develop # 環境 environment: DOTNET_CLI_TELEMETRY_OPTOUT: 1 DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 NUGET_API_KEY: # 金藥需要加密,下面會說明 secure: SCmDP3tOoIfO4zexfzu07xjBnv1KDMNDljU1iKZqU1VCTwyZ8pcAi5aLa5SMmwzh # Appveyor要做的事情就是執行build.ps1,build.ps1就會執行build.cake # 然後帶入參數 build_script: - ps: .\build.ps1 --currentBranch=$env:APPVEYOR_REPO_BRANCH --nugetApiKey=$env:NUGET_API_KEY --coverallsToken=$env:COVERALLS_TOKEN # 把內建的測試和佈署關掉 test: off #tests handled by cake script deploy: off #deploy handled by cake script ``` . 在把Nuget自動上傳到`nuget.org`時,會需要一組金鑰 取得金鑰的教學在[這邊](https://docs.microsoft.com/zh-tw/nuget/quickstart/create-and-publish-a-package-using-the-dotnet-cli#acquire-your-api-key)可以看到 . 直接把取得到的金鑰放在`appveyor.yml` 會讓所有取得金鑰的人 能夠直接用金鑰佈署專案,相當危險,所以需要加密 . 需要在[這邊](https://ci.appveyor.com/tools/encrypt) 把從`nuget.org`拿到的金鑰貼上後 會得到(比較長)加密後的金鑰 . 這組(比較長)加密後的金鑰 就可以任意丟在網路上沒問題了 . # 後言 在摸了半天,知道整個運作流程後 就沒那麼難了 . 大家加油ㄅ .

2019年9月2日 星期一

[開箱]少女終末旅行 千都 黏土人

前言 :
之前被問到要不要先到博弈遊戲公司幫忙
雖然算是有點逼不得已(?)結果不才居然答應了
三洨
.
然後第一次領到超級厚的薪水袋時
阿斯
.
之後就看著辦吧ODO
那邊好像是多角經營,想點辦法盡量不要去做博弈遊戲部分吧
.
是說就算真的只是普通的遊戲公司
好像也沒好到哪就是了
不才討厭做遊戲
.
寫遊戲又不好賺
薪水低
沒加班還要感謝公司感謝得五體投地
.
來寫後端多輕鬆
是不是呢(#
.
然後這次的黏土人一次買了兩隻
所以就分兩次開箱文吧
.

正文 :


開箱
一次開兩隻
.
有錢人的快樂
往往就是這麽樸實無華
且枯燥
.
ㄍ你角川
.
雖然說有兩隻
但這次開的是左邊的千都
.

.
正面配件
.
背面的頭盔
.
沒戴頭盔的正面
.
側面
.
背面
.
另外一個側面
.
戴上帽子的正面
看起來可愛多了
.
側面
.
背面
.
另外一個側面
.
不知道為什麼
支架很難安裝上去
.
背包
.
經過了幾百隻黏土人的進化
和喪女的背包比起來
這個好裝多了
.
只要往後卡就可以了
而且材質是軟的
不用像喪女的還要擔心會不會把背包的肩帶弄斷
.
然後有付上一根塑膠棒
插在背包上就可以固定黏土人了
.
但很詭異的,黏土人會懸空
.
最後還是決定打開說明書看看
確認之前安裝的方式沒有問題
.
除了原本的臉,還有另外付上兩張表情
.
不是不才在吐槽
把表情遮住的話
前面兩張臉根本看不出差別阿
.
原本的臉
.
嘴巴表情加上幾個pixel後的笑臉
.
額...好吧
.
然後上看智障的臉
.
這時候千續就派上用場了
.
ㄌㄨㄝ
.
哦幹幹幹最好不要過來喔
.
 (千都)乾我以後絕對不要成為這種人
.
下一個就換你了
.
幹到的相機超讚的啦
.
 千都的日記
.
 腳配件
.
 ......
.
 應該是這樣插下去吧?
.
雖然說說明書沒寫但不才直覺好像不是這樣
這個腳部配件應該是配合(DLC)半履帶型摩托車用的
.
然後意外的發現可以不需要配件就可以直立(!!!!!)
.
只是不知道為什麼
總覺得
突然想到植物大戰殭屍裡面的殭屍
.
換上喪女臉後
更像僵屍了
.
走開
.

總之今天的黏土人開箱就寫到這邊
下一篇是尤莉的黏土人開箱

2019年7月16日 星期二

[Azure] asp.net core 專案出現 set 'CopyRefAssembliesToPublishDirectory' to true 錯誤

前言 :
最近被認識的人問到
給我原本薪水的1.5倍
然後過去他們那邊工作
.
如果只是跳槽的話
不才當然馬上去問老闆明天能不能不要來了
.
但很不巧的
是做博弈
.
有點好奇
如果可以增加一點五倍的薪水,大家會改去做博弈(的遊戲)嗎?
.
雖然站在不才的角度來想
不才只是提供你遊戲玩
恩,娛樂的一種
雖然不太好
.
嘛,就跟垃圾食物一樣嘛
雖然傷身體但大家都愛吃
.
但單把進來玩的人當羊宰
說真的有實在有點過意不去
而且會把手弄髒
.
後來和朋友聊到後
他提到了課金手游....
.
:thinking: :thinking: :thinking:
.
究竟賭博
和課金手游
到底有什麼不同呢
為啥不才會排斥博弈
卻不會排斥做手游
.

正文 :

回到正題
在使用azure的時候有時候會因為懶惰
直接把檔案按下右鍵publish到azure上
.
在某些程度上不才用得挺開心的
尤其在修改.cshtml時
真是方便(菸
.
但很不巧地今天踢到了鐵板
出現了這個畫面
.
恩,上面簡單是說
把 CopyRefAssembliesToPublishDirectory 打開
.
但不才試過根本沒用RRR
為啥只是在cshtml加上css然後按下publish就會變成這樣
.
後來發現
在有些專案中
例如這次不才改的grandnode
https://grandnode.com/
.
有些.cshtml是直接以檔案形式丟到azure上面的server
例如前台的佈景主題
.
另外有些則是包在dll內
例如Area裡面後台管理上的.cshtml
.
......
.

解決方式就是進到server內
把剛才丟上去的檔案(.cshtml)砍掉
就正常了
.

結論 :
白生氣了
.

2019年5月19日 星期日

[開箱] 980 邪神與廚二病少女 邪神醬黏土人

前言 :
終於狠心來換哀鳳了
換完後整個順到不行
感覺就像是從雨衣變成短裙
還是真空的那種
.
真不虧是蘋果
除了購買價格和維修價格和哀鳳難用到死的鍵盤
和2016年後的Macbook Pro系列外

沒讓我失望過
.
還有下次寫部落格換個地方好了
便利商店有夠吵
.

正文 :
開箱箱
.

這次月子大人表示有點興趣
希望能夠一起開箱
.
月子 : 關我屁事
.


首先是這個箱子
.


被打開後
哇,裡面有黏土人誒
讚讚
.

正面
.

側面
.

背面
.

另外一個側面
.
月子 : 啊所以是好了沒?
.

好ㄅ
月子去其他地方玩了
只能放在桌上開了
.

開開
.

將將
.
先看一下本體
.

架子方面除了老樣子外
多出根東西
.

可以把它插上去
然後黏土人就立在上面了
.
讚讚
.

黏土人正面
.

側面
.

背面
.

另外一個側面
.
不才發現打這幾段字的速度最快
.

然後其他的配件
.

臉有這兩個
.

不知道即將尾巴要被切掉的憨臉
.

還有meme臉
就是那個
很難描述的但第二格還是第三格的臉看起來87%像的meme
.
翻了半小時的好色龍還是沒有找到
算了,自己拍好了
.


還有這個尾巴是可以左右擺動的喔 <3
.

然後是奶子(轉頭看看FB在不在旁邊
.
雖然從一開始都很巧妙的被頭髮遮住
但實際上是有的喔
真的可以露出來的喔
.
而且感覺比動畫中還大
幾乎是たわわ等級的
.
唯一美中不足的
沒有設計得像たわわ一樣有觸感
就只是片
比較浮誇的塑膠
.

然後身體是可拆的
.

裝上另外一片尾巴
.

將將
.

然後
把原本的尾巴加上配件
.

恭喜
Buy one and get one free.
.
物超所值
感謝好微笑,讚嘆好微笑
.
反正都是被砍
哪邊噴血都沒差啦(X
.

尾巴正面
.

側面
.

背面
.

另外一個側面(誒
.

裝上另外一個尾巴的正面
.

側面
.

背面
.

另外一個側面
.



噴點血吧
.

然後很神奇的
這個噴血的配件
.

可以被拆開
.

應該是為了塑造那種
噴血但其實沒有真的噴很多
的那種感覺吧
.
額大概
.

(乾還是很痛好不好
.

蛇蛇 <3
.

幹幹幹幹幹這不是裙子RRRR
沒看到我痛死了嗎
.

快死掉了RRRR
.






然後是一些手手的動作
.

最後是那個一直都找不到的meme
.
恩....
到底是多少R
.

轉頭
.

(2啊,北漆)
.

應該是這樣吧
.

...好喔
.
以上
.
2019/5/21:
額終於找到了



後文 :

買一送一
物超所值
高雄發大財
.
然後發現圖被部落格壓糊了

難怪不才以前寫文章時總覺得照片不管怎麼拍都覺得不夠銳利
雞掰
.
工程師 :
沒辦法,有一個奇怪的用戶每次寫文章都上傳好幾百MB的相片上來
.
好啦原圖就順便附上連結好了
感謝Google相片,讚嘆Google相片
https://photos.app.goo.gl/3TLoHw7qjPQE66tg6
.

最後新做好的逐格動畫專用拍攝櫃(半廢棄中)
終於有那麼一點點用處了
可喜可賀
.
這份轉載至 http://blog.udn.com/andy840119/126789987
先測試看看效果好不好ODO
.