活動:Laravel x Vue Conf Taiwan 2022
名稱:從 Legacy 到 Lovely 用 CI/CD 改建二十年祖產全記錄
基本介紹:
曾經一個數十萬行規模的 PHP4 專案,沒有文件、沒有測試甚至沒有原始碼版本控制的 Legacy,到現在已轉化為以 Laravel 為核心運行,開發的過程中陸續導入單元測試、Lint、持續整合(CI)、持續部署(CD)甚至是上線後的 系統監控。這段轉化的過程中,這個團隊怎麼做技術管理?怎麼邊擴增功能又邊重構?導入了哪些工具、套件又做了關鍵抉擇與取捨?
在這個議程中你可以聽到一些故事、技術管理的抉擇、一些技術與工具選擇的原因以及得到一個使用 Laravel 可以參考 DevOps 流程工具框架。
簡報連結:20221202 從 Legacy 到 Lovely,用 CI/CD 改建二十年祖產全記錄 - Speaker Deck
在系統開發的過程中,難免在無意間製造 bug,但在發現系統有問題之後,該怎麼樣快速的找到產生問題的原始碼,這就是另外一門學問了。
現在開發者通常使用 GIT 作為版本控制工具,而 GIT 可以怎麼樣提供開發者快速找到問題呢?大家可以參考 GIT 的 bisect 這個指令。以下提供一個在 PHP 開發的環境下,使用 Bisect 的案例來供大家參考。
GIT 的 Bisect
指令是一個幫助開發者從儲存庫中,原本正確的 Commit 點到發現錯誤的 Commit 點的範圍內,以二分法找尋問題點的方法,以底下的例子:
以上圖範例中,假設在開發的過程中,原本在 v2.0 版本確認沒有問題,但突然發現,最新的開發版本上測試發現問題,因此我們要找出從釋出 v2.0 版本後,是在哪個 commit 之後才發生這問題的,這時候就可以利用 bisect 來進行尋找錯誤的動作。
1 | git bisect start HEAD v2.0 |
如上指令,其指令意思是「開始使用 bisect 指令除錯,定義的範圍從目前的 HEAD 一直到 refs v2.0 的位置」,使用指令務必要確定,一定要有一端測試是正常的。
當開始使用 bisect 除錯後,首先 GIT 會幫忙先把目前所在 GIT HEAD 的位置挪移到中間的 commit 上,這時候可以再次地進行「測試」,當在這次測試的過程中確認目前的 commit 還是錯的,則:
1 | git bisect bad |
可以使用 git bisect bad
來標注目前的 commit 依然是有問題的,這代表,目前所在的 commit 直到開始點的所有 commit 都是有問題的。接著 GIT 會協助繼續幫忙把 HEAD 指標移動到距離目前確認測試是正確的 commit 的一半位置。
到了目前的 commit 位置後,如果發現測試是正確的,則可以使用 git bisect good
來標注目前所在的 commit 測試是正常的,則代表從目前的 commit 到正確端的 commit (v2.0) 都是正確的。接著 GIT 會把 HEAD 的位置移動到下一個 commit 上。
最後,在這個 commit 上再次的進行測試,發現,其還是正確的,同樣的使用 git bisect good
進行標注。
則最終可以找到第一個造成問題的 commit 上。
在找到有問題的 commit 之後,GIT 會提示有問題的這個 commit 的 SHA1,此時 HEAD 會停留在原本的位置。這時候如果要回到該分支的最原始位置,則可以使用指令:
1 | git bisect reset |
一般來說,如果有進行單元測試,又有進行 CICD 的流程的話,有問題的 commit 在推上 GIT Server 的第一瞬間就會被發現了,並不應該出現上述的案例。
在 GIT Bisect Command Demo 這個 GitLab 的 Repo 上,我建立了一個以 PHP 搭配 PHPUnit 做單元測試的簡單範例。
1 | git clone git@gitlab.com:mouson-gitlab-playground/git-feature-demo/git-bisect-command-demo.git |
1 | ./vendor/bin/phpunit |
執行畫面如下:
1 | git bisect start HEAD v2.0 |
執行後可以看到,目前 GIT HEAD 所在的位置挪移到了 48c7706
的這個 SHA1 上。
48c7706
這個位置,也就是原本的 master
到 v2.0 之間的「中間」所在的位置。
1 | ./vendor/bin/phpunit |
在執行 git bisect bad
宣告該 commit 為錯誤點之後,GIT 會自動的挪移 HEAD 位置到 3526104
的 SHA1 位置,如下圖。
1 | ./vendor/bin/phpunit |
在執行 git bisect good
宣告該 commit 為正確點之後,GIT 會自動的挪移 HEAD 位置到 8f3dca5
的 SHA1 位置,如下圖。
目前 HEAD 所在的 SHA1 8f3dca5
位置,已經就在第一次剖半的 SHA1 48c7706
旁邊,意味著,當目前所在的位置如測試「正確」,則代表第一次錯誤的位置為 SHA1 48c7706
,而如果「錯誤」則表示目前位置就是第一次出現錯誤的位置。
1 | ./vendor/bin/phpunit |
同樣的透過單元測試,進行驗證,在這個步驟測試結果是正確的。因此錯誤點是第一次剖半的 SHA1 48c7706
,此時標注目前所在的位置 SHA1 為測試正確。
1 | git bisect good |
在執行 git bisect good
指令後 GIT 如同上面所描述的,會顯示第一次出現錯誤的 commit 細節資訊,如 commit 內容、作者、時間及對應修改的原始碼內容等。
在找到錯誤的位置之後,還需要透過 git bisect reset
指令來進行「恢復環境」的動作,其執行後 GIT 會幫忙將 HEAD 移回 master 的 HEAD 位置。這時候就可以針對所看到的 commit 位置進行除錯的決策及判斷與修改。
1 | git bisect reset |
上面所描述的是使用「手動」透過 git bisect bad
以及 git bisect good
的方式設定目前所在位置是否正確的方法,但 bisect
指令還有提供自動執行測試的功能,只要使用的執行方法,當執行測試發生錯誤的時候,可以產生錯誤訊號,讓 GIT 判斷,那就可以自動找到 commit 的位置。
其自動測試的過程如下:
自動化檢測的開始流程與手動的方法是一樣的,同樣是執行 git bisect start HEAD v2.0
。
1 | git bisect start HEAD v2.0 |
主要是底下的指令,需要宣告要執行測試的指令為何,這邊以 php 的環境來說,使用的是 PHPUnit 單元測試,因此其執行路徑在專案目錄下的 ./vendor/bin/phpunit
,因此可以直接執行:
1 | git bisect run ./vendor/bin/phpunit |
在自動測試之後 GIT 同樣的會將錯誤的位置停在最後的 commit 並顯示第一個發生錯誤的 commit 位置及資訊,因此在取得錯誤之後,還是需要自己手動執行 git bisect reset
回到原本的位置進行後續的動作。
使用 GIT 的 bisect 指令進行除錯,可以減少自行執行 GIT 指令做 HEAD 位置移動的麻煩,可以加速找到有問題的位置,但平時還是要養成好習慣,如果 commit 的原始碼內容太過龐大或雜亂,對於除錯依然沒有太大幫助,所以,養成好的 commit 習慣還是方便除錯的最重要根本。
GitLab CI 對於入門者相當的友善,但由於版本變化快速,一段時間沒關注,可能就會錯過 GitLab 在 gitlab-ci.yml 上新的或更方便的特性,如 GitLab CI 的平行處理、平行處理矩陣、繼承、參考等等的用法,在這場 Workshop 中,將帶給參加者認識進階的 GitLab CI 使用及一些除錯、降低 GitLab 執行時間的小技巧。
透過工作坊了解如何分析 GitLab CI 執行期間的瓶頸點、除錯技巧以及 GitLab CI 提供的進階語法。
對於 GitLab CI 有一些基本的認識,但還不夠熟悉,想要更了解 GitLab CI 的使用族群。
以上影片取自 n8n.io - Free and Open Workflow Automation Tool 的介紹,n8n 是近期發現超好用的自動化工具。
onedev:超簡單的一體化 DevOps 開源平台 - Soft & Share
網路產業如何搜尋與追蹤競爭者? 10 招競品分析實用工具與技巧大公開! | by Anne Hsiao | 3PM LAB 產品三眼怪實驗室 | Medium
產品名稱 vs
、形容這個產品的名詞
、site:{網站網址}
、related:網站網址
(找跟這網站相關的網站)Business Name Generator - free AI-powered naming tool - Namelix
輕鬆架一套類 IFTTT 的自動化工作流「n8n」 | jkgtw’s blog
n8n.io - Free and Open Workflow Automation Tool
Introduction | Docs
Server Setup | Docs
fsantini/KoboCloud: A set of scripts to synchronize a kobo reader with popular cloud services
這份簡報主要是搭配 GitLab 10 週年活動的分享,針對 GitLab 13.0 ~ 14.3 其中的 GitLab CICD 設定檔新增功能及變更所做的亮點整理。
在 GitLab 13.0 ~ 14.3 的版本更新及變更中,橫跨了 17 個版本,其關於 CICD 的設定變更幅度非常的多,因此在整理之後,又再次過濾及挑選一次。
這次整理的亮點主要包誇五大方向,分別是 Pipeline Editor、Include、Reference、Parallel With Matrix 以及 Directed Acyclic Graph (DAG) 的 Needs 功能。
在 GitLab 13.8 版開始,GitLab 在介面上增加了 Pipeline 的編輯器,在這個編輯器上,除了可以做 CICD 設定檔的文字編輯外,也可以直接在編輯器上看到編輯後的 Pipeline 視覺化關卡規劃,更可以直接線上做語法檢查 (Lint),甚至,如果有使用到 extends, include 等語法時,還可以透過最後一個「View merged YAML」來查看合併之後的語法。到了 14.1 版,也開始支援可以在編輯器上選擇要編輯 CICD 設定檔的專案分支。
有 Pipeline Editor 線上編輯器之後,大幅度的改善以往在 local 端透過編輯器撰寫後,還必須複製到線上透過 Lint 工具才能知道語法是否正確的繁瑣,因為這個功能的新增,也改變了我個人的開發流程。
在 GitLab CICD 設定檔的語法中 Include 是很早就提供的功能,只是早期必須要付費版本才能使用,直到 GitLab 11.4 版開始下放到了免費版,從此讓 GitLab 變得很好用,也讓團隊的共用變得很有可能。
在最初開放給免費版本的 Include 語法中,包含四種格式,分別是 include:local:
、include:remote
、include:project
以及 include:template
。
include:local:
:載入同專案中的其他 YAML 檔案,如果只有一個也可以直接簡短為 include: 'file-name.yml'
include:remote
:載入遠端公開路徑的 YAML 檔案include:project
:載入其他專案中的 YAML 檔,可以透過 ref
設定是什麼分支以及透過 file
設定要載入的檔案路徑include:template
:載入 GitLab 官方提供的 GitLab CICD Template以上的功能在後續的版本中有一系列的變化,首先是載入其他專案的 YAML 檔,以往的語法一次只能載入一個專案中的一個檔案,在 13.6 之後開始,允許開發者在同一個專案宣告中,載入多個 YAML 檔案,範例語法如下:
1 | include: |
到了 13.8 以及 14.2 甚至開始提供在專案路徑上可以使用變數。這些變數可以是專案、群組甚至是 GitLab 服務層級。範例語法:
1 | include: |
以往載入同專案底下的專案必須要一個一個的載入,到了 13.11 版本後,可以使用 wildcard 的特性如:
1 | include: 'configs/*.yml' |
到了 14.2 版之後,開始提供可以針對同專案底下的 include 撰寫 rules
條件語法,透過條件來決定要不要載入,如:
1 | include: |
以為,提供重複使用的語法僅有 include、extends 以及 anchors 語法,在 GitLab 13.9 之後,提供了 !reference
tags 的語法工載入特定工作中的特定內容,如底下的範例:
1 | # setup.yml |
1 | # .gitlab-ci.yml |
以上的 !reference 語法,經過 GitLab 的語法解析之後,會轉變為:
1 | # .gitlab-ci.yml |
其中需要特別注意到的地方是 test
的這個工作。script
及 after_script
兩個部分,除了同一份 yaml 檔案的重複使用外,連 include 進來的 yaml 也可以透過 !reference tags 進行重複使用。
到了 GitLab 14.3 版中,甚是也提供了 rules 條件式的重複使用。
parallel
語法是在 GitLab 11.5 版本中提供的,其讓工作可以透過平行化處理加快工作完成的時間,但並不是大部分的工作都可以支援平行化,因此對於大部分的使用者來說,幫助似乎不大。但到了 13.3 的版本,開始提供 parallel:matrix
工作,這個支援就讓很多只是變數不一樣的工作得以更加的精簡,如下的範例:
1 | deploystacks: |
如上一個工作,在 GitLab 解析之後就可以變成三個工作,其三個工作的變數分別是為:
而到了 13.5 版,調整了規格變成可以支援一維陣列的支援。在到了 13.10 版本,則開始支援把 parallel:matrix
變數,帶到預計要 trigger 的工作中。
Directed Acyclic Graph 中文通常被翻譯為「有向無環圖」,在 wiki 的解釋中是這樣說明的:在圖論中,如果一個有向圖從任意頂點出發無法經過若干條邊回到該點,則這個圖是一個有向無環圖。而在 GitLab 12.2 版本中開始支援 Pipeline 的有向無環圖,代表著 Pipeline 不必再一個一個關卡的進行,當一個工作完成,後續需要該工作完成後才能進行的工作可以直接開始進行
。
以底下的範例中,設定結尾為 a 的個關卡工作,後續的工作都需要前面工作先完成,如 test_a 需要 build_a,test_b 需要 build_b:
在還沒有支援 DAG 的環境中,如過 Build 的這個關卡中,build_b 這個工作會執行的特別慢,則會導致 Test 關卡中的工作一直無法進行。
當有 DAG 的機制導入後,因為後續的工作可以直接開始進行,可能就會看到如上的範例,a 系列的工作一路執行到 Deploy 關卡了 build_b 工作還在進行。
這項特性,到了 GitLab 13.12 的 Pipeline 圖片中,讓 Pipeline 的檢視中,更加的有感覺,如下影片:
GitLab 14.2 版中,提供了新的 Stageless pipelines 的特性,這可以讓 GitLab CICD 設定檔的內容更加的精簡,如下範例的變化:
以往必須要特別宣告 stage 才能更進行,現在透過 needs 的語法,只需要把需求相依設定好,也就可以進行了。
簡報連結:GitLab 13~14CICD功能亮點提示-GitLab 10週年線上聚會 - Speaker Deck
GitLab 每個月的 22 日都會釋出一個版本,從 13.0 到 14.3 中共經歷了 17 次的釋出,期間新增的功能其實非常的多,在這次的簡報中,我取了其中我個人認為比較特別的特性,主要是可以幫助讓 GitLab CICD 設定檔的內容更加簡潔、更有重複使用性的特性,也希望這分享對於近期了解 GitLab CICD 的特性發展有幫助。
圖片取自:🦊GitLab Cheatsheet - 1 - Basics of Stages and Jobs - DEV Community
How to become more productive with Gitlab CI | GitLab
parallel.matrix
的機制.gitlab-ci.yml
可能越來越胖,也因此 .gitlab-ci.yml 可能因為業務需求內容越來越複雜。這時候就可以把一些功能拆出作為子項工作,利用 trigger.include
子工作的 .gitlab-ci.yml 檔案來減少衝突。multiple caches
的機制,讓一個 Job 可以針對多個目錄建立快取,並且使用不一樣的特徵值。個人覺得這功能在 mono-repo 的專案中會特別有效益。How Orange made a first step toward CI/CD standardization with GitLab | GitLab
Orange 這家公司推出了他們維護的 GitLab CI/CD 模板供社群直接引入使用,裡頭提供了大量的支援,如 docker, terraform, k8s, php, helm, python, angular, k6 openshift, s3, anbible, maven 等等,對於想更深入學習 GitLab CI/CD 的人還蠻值得一看的。
這一系列共八張 Cheatsheet ,對於剛開始使用 GitLab CI 學著撰寫 .gitlab-ci.yml 的使用者挺有幫助的。值得一看。
Commit Virtual 2021: Using CI Templates for Speed and Consistency
🌳🚀 CS Visualized: Useful Git Commands - DEV Community
超多知名網站的 pitch 簡報,包含 YouTube, Foursquare, facebook 等:The Best Pitch Deck Examples | Pitch Deck Hunt
FIDO
【文思不藏私】ORID 面試技巧. 最近正好想整理一下如何用 ORID… | by Vince Huang | 【文思不藏私】 | Medium
Impressive download | SourceForge.net
Impressive is a program that displays PDF presentation slides with style. Smooth alpha-blended slide transitions are provided for the sake of eye candy, but in addition to this, Impressive offers some unique tools that are very useful for presentations.
在簡報播放時 Highlight 畫面
bitwarden/cli: The command line vault (Windows, macOS, & Linux).
Alfred Spotify Mini Player #Alfred #Spotify
一套可以快速掌握網站數據的服務(付費的),KKDay 團隊有在使用:Amplitude | The Digital Optimization System
1 | A=ABCDEFGHIJK |
透過 PHP 產出 Excel 格式檔案的幾個 Library
當代 PHP 開發者在除錯時必須要知道的工具
當在必須要知道的 12 個 PHP 測試工具:Ultimate List of Best PHP Testing Tools of 2021
PHP 版本相容測試工具:PHPCompatibility/PHPCompatibility: PHP Compatibility check for PHP_CodeSniffer
Laravel 在 Console 底下呈現 spinner 的方法及 Library:Laravel Console Spinner | Laravel News
模組化 Laravel Modular Laravel - DEV Community
從 Laravel 的一個 SQL injection 漏洞查看如何追底層的 code:CVE-2021-39165: 从一个Laravel SQL注入漏洞开始的Bug Bounty之旅 | 离别歌
使用 Laravel 製作的 CRM 工具:krayin/laravel-crm: Free & Opensource Laravel CRM solution for SMEs and Enterprises for complete customer lifecycle management.
Larger Laravel Projects: 12 Things to Take Care Of - Laravel Daily
本次選圖取自:GitLab CI CD Note
裡頭放了很多好用的 Laravel Snippets 及 Method,像 wasRecentlyCreated()
可以用在 firstOrCreate
之後做判斷到底是新增還是修改。
1 | - /^https:\/\/example.com\/path/i |
1 | - preg_match('/^https:\/\/example.com\/path/i', $uri); |
分享一些 PHP 中有用的知识和坑 - SegmentFault 思否
performance - List of Big-O for PHP functions - Stack Overflow
PHP 原生的 function 的效能上 Big-O 列表
GitLab 官方認證的講者所建立的 GitLab Workshop,是個入門 GitLab CI/CD 可以嘗試參考的內容。
這這篇文章中幫 htop 這個好用的工具做了一次完整的介紹,包含畫面中的每個欄位的意義。
從作業系統的角度來談為什麼需要「虛擬記憶體」. 上個月的專欄「你一定用過… | by Larry Lu | Jun, 2021 | Medium
這篇延伸自上一篇,增加描述了許多什麼是虛擬記憶體,為什麼需要虛擬記憶體的說明。
於GitLab CI/CD 手冊上發現 artifacts:reports:cobertura
在 GitLab 15.0 中移除,替換為 artifacts:reports:coverage_report
,因此更新底下內容。
在上一篇 在 Pipeline 檢視 PHPUnit 單元測試報告,透過 artifacts:reports:junit
的機制,使每次 Pipeline 執行後,就能馬上看到單元測試報告,有了測試報告之後,可能就會開始思考,那 Code Coverage 測試代碼覆蓋的狀況,是不是也可以在 Merge Request 的過程中就看到呢?
答案是可以的,而且在 GitLab CI/CD 的免費版本中就有這個功能。在 GitLab 12.9 版後,只需要透過 artifacts:reports:coverage_report
並作相關設定,就可以搜集 Cobertura XML 格式的報表就可以完成。這部分與 artifacts:reports:junit
的原理是一樣的,只需要讓 Pipeline 的工作可以產出所需要的格式的檔案,透過 artifacts:reports:coverage_report
將檔案搜集起來,就可以在 Merge Request 的畫面中看到視覺化的測試代碼覆蓋情況。
那麼,在 PHP 該怎麼做出符合Cobertura XML格式的測試程式碼覆蓋狀況檔案呢?
如上圖(取自官方Test coverage visualization文件圖片),在圖片中,呈現綠色
線條的部分,代表測試有執行到的部分,而橘色
的部分則代表沒有測試程式覆蓋到。
在 GitLab 官方文件上提到了幾個語言及其支援的工具或外掛,如:
在 PHP 搭配 PHPUnit 該如何產出 Cobertura XML 呢?
在 PHPUnit 9.5 版的手冊中,關於 coverage 的章節中,可以看到,目前手冊上顯示如果要輸出報表,總共支援了底下的六種格式:
其中,並沒有提到 GitLab CI/CD 要使用的 Cobertura XML 格式,因此甚至有人寫了轉換工具soyhuce/phpunit-to-cobertura,但仔細探究 PHPUnit 的原始碼,搜尋「cobertura」後發現了一道曙光phpunit/Builder.php at sebastianbergmann/phpunit),在 phpunit 的指令中,支援了 coverage-cobertura
關鍵字。
於是實際驗證,在已經開啟 xdebug 的 PHP 環境中,執行:
1 | php vendor/bin/phpunit --coverage-cobertura=cobertura.xml |
真的順利產出 Cobertura XML 格式的報表,樣式如下:
1 |
|
這代表至少在 PHPUnit 9.x 的版本中,可以支援 --coverage-cobertura
來產出 Cobertura XML 格式報表。
同樣的,在 PHPUnit 的官方手冊中,是沒辦法找到在 PHPUnit 設定檔中應該如何產出 Cobertura XML 格式報表的,但如同在第一章中驗證的方法,在 coverage 的 report 做了 cobertura 的宣告:
1 | <coverage processUncoveredFiles="true"> |
同樣產出了 Cobertura XML 格式報表,因此這在 phpunit.xml
或 phpunit.xml.dist
是可行的。
在這次的範例,直接取用中上一篇在 Pipeline 檢視 PHPUnit 單元測試報告相同的原始碼,但由於 PHPUnit 要產出 Code Coverage 相關的資訊時,必須搭配 xdebug 使用,因此這邊改採 jorge07/alpine-php:8.0-dev
docker image 作為執行環境,同樣以 PHP 8.0 作為 PHP 的版本。
1 | .test_template: |
在底下的範例中,使用的模板都使用以上的共用安裝過程及共用的 docker image jorge07/alpine-php:8.0-dev
。值得一提的地方是,在 PHP 8.0 搭配 XDebug 3.0 後,在取得 Coverage 時,必須讓 XDebug 知道 XDEBUG_MODE
為 coverage
。
如前面所提到的,在 Command Line 中要得到 Cobertura XML 格式的報表,必須在 PHPUnit 執行的指令中加上 --coverage-cobertura filePath
參數,在 .gitlab-ci.yml
中,根據 上一篇 的範例原始碼,script
的部分必須再增加 --coverage-cobertura
,增加後的 .gitlab-ci.yml 如下:
1 | test:phpunit-with-command-line: |
如上,再增加後,可以在對應的 Merge Request 馬上看到成果:
如上圖,在範例的 PHP 程式碼中,刻意的不測試 isFalse
這個方法,因此方法中的原始碼呈現 No test coverage。
phpunit.xml.dist
或 phpunit.xml
設定 Cobertura XML 格式 Coverage 報表同樣的在第二段中,有提到如果要將 Cobertura XML 設定在 phpunit.xml
中,則 Coverage 的部分必須增加 report 並且增加 cobertura 的段落,範例如下:
1 | <coverage processUncoveredFiles="true"> |
這邊的 phpunit.xml 將 Cobertura 格式的檔案輸出到 build/report.cobertura.xml
這個檔案路徑。因此對應的 GitLab CI Pipeline 描述,也需要針對報表的路徑做調整,如下:
1 | test:phpunit-with-phpunit-file: |
在 PHP 語言中,為了讓 PHPUnit 可以執行的更快,開始有一些加強的方案,如很多人常用的平行化處理方案,透過多執行序來同時執行 PHP 的單元測試以加快完成速度,其中 ParaTest paratestphp/paratest: Parallel testing for PHPUnit 就是這中間很有名的一個工具。如上一篇提到 ParaTest 大部分的指令都可以與 PHPUnit 支援的相容,在經過測試後,發現 Coverage Report 產出 Cobertura XML 格式報表的部分,是相容的,因此幾乎不太需要更動。如下是在 ParaTest 的指令模式中產出 Cobertura XML 格式報表:
1 | test:paratest-with-phpunit-file: |
上面內容與上一篇一樣 mkdir -p build
是為了避免無法寫入檔案造成無法產出報表。其他的如 PHPUnit 產出 Cobertura XML 格式報表,僅加入了 artifacts:reports:cobertura
來取得 Cobertura XML 格式報表對應的檔案。
1 | test:paratest-with-command-line: |
同上一個範例中,也同樣有 mkdir -p build
這段產出 build 資料夾的部分。其它的如 PHPUnit 在 Command Line 一樣,要產出 Cobertura XML 格式的報表必須要透過 --coverage-cobertura build/cobertura.xml
參數。
在 GitLab CI 中使 PHP 的測試工作 PHPUnit 執行後產出 JUnit XML 格式的報表,主要倚靠 PHPUnit 自身的功能搭配 GitLab CI artifacts:reports:junit
機制來完成,而,如果需要更有效的看到視覺化的 Code Coverage 則可以透過 artifacts:reports:cobertura
,在 PHP 的環境中,透過小祕技產出 Cobertura XML 其實只是增加一兩段指令,不會太難,最難的部分反而是從 PHPUnit 原始法中找到 coverage-cobertura
這個宣告。
希望這篇文章,希望對大家有幫助。
許多使用 GitLab 作為版本控制工具的使用者,要開始搭配 GitLab CI/CD 建立專案的持續整合流程時,一開始通常是從專案既有的測試開始著手,讓「測試」在每次原始碼 commit 後就自動執行,進而增加原始碼的品質。
在 GitLab 版本 11.2 之後,GitLab CI 開始提供了讓使用者可以簡便的在 Pipeline 中就看到 Pipeline 過程中產出的報表功能。其主要是透過 artifacts
夾帶 Pipeline 工作中產出的報表。
在 GitLab CI 提供的眾多報表功能中,artifacts:reports:junit
主要是用來搜集並呈現「單元測試」執行結果的報表,使用JUnit report format XML files,看到 JUnit 熟悉的朋友可能知道,這是 Java 語言在撰寫單元測試常用的工具。那麼,在 PHP 語言中,如果也要產出同樣的報表,應該要怎麼產生呢?
artifacts:reports:junit
在 GitLab 的 artifacts:reports:junit
的文件中,提供了如下的 JUnit 報表範例:
1 | rspec: |
在這段範例中,主要看兩段關鍵重點:
1 | script: |
首先是 Script 的部分,這段範例中以 Ruby 的 rspce
為產出 JUnit 格式的展示,在 rspec
中,使用 --format
來宣告要輸出的報表格式,這邊使用 RspecJunitFormatter
也就是 artifacts:reports:junit
需要的 JUnit XML
格式,並透過 --out
設定要輸出的檔案格式為何,這邊使用 rspec.xml
,因此執行後,會在本地端產出一個名稱為 rspec.xml
的檔案。
1 | artifacts: |
在 GitLab CI 中 artifacts
通常是用來搜集 GitLab CI Job (工作),所產出的產出物,在 artifacts:reports:junit
裡,即透過宣告為 reports
中的 junit
格式,來取得上一段 rspec
所產出的 rspec.xml
檔案,使其作為 JUnit 報表。
在這次的範例中,直接使用 composer 官方所提供的 docker image 作為執行環境,因此預設使用了 PHP 8.0。
1 | .test_template: |
在底下的範例中,使用的模板都使用以上的共用安裝過程及共用的 docker image composer
。
在第一段中有提到要在 GitLab CI 的 Pipeline 顯示報表,最重要的是要在測試的過程中,產出 JUnit 格式的報表,在PHPUnit 官方手冊 中,提到如果要產出 JUnit 格式的紀錄,可以在執行 PHPUnit 的過程中加上 --log-junit <file>
,那麼,在 GitLab CI 的 Pipeline 設定中,要怎麼設定呢?
1 | test:phpunit-with-command-line: |
如上原始碼,在 script 的區段中,使用 --log-junit <file>
語法,使其產出 JUnit 格式的報表到檔案路徑 build/junit.xml
,接著透過 artifacts:reports:junit
語法,直接取得產出物 build/junit.xml
作為報表。執行後,可以在 Pipeline 中看到 Tests 的內容已經有一些數字呈現,點入後可以看到直接顯示在這個測試中有多少執行錯誤、跳過及通過等資訊:
點入後,可以在看到更詳細的內容描述,如每個測試案例的執行狀況、細節等:
一般在使用 PHPUnit 的時候,會把常常需要使用的指令透過 phpunit.xml
或 phpunit.xml.dist
作為設定值,這樣每次在執行 PHPUnit 的時候,只要直接執行 php vendor/bin/phpunit
而不用再透過指令參數做設定。那麼在 phpunix.xml
中,要怎麼設定 JUnit 格式的報表呢?
在 PHPUnit 的手冊中關於 XML 設定檔的部分有提到,可以透過 <junit>
及其參數 outputFile
來做設定。如底下的範例:
1 | <logging> |
上面的原始碼宣告將 JUnit 格式的檔案輸出到 build/report.junit.xml
這個檔案路徑。因此對應的 GitLab CI Pipeline 描述,也只需要如底下原始碼:
1 | test:phpunit-with-phpunit-file: |
在上面的原始碼中,因為已經將 JUnit 格式的報表宣告放在 build/report.junit.xml
,因此 script
中執行 PHPUnit 的部分僅剩下 php vendor/bin/phpunit
,而 artifacts:reports:junit
的部分則維持不變。
在 PHP 語言中,為了讓 PHPUnit 可以執行的更快,開始有一些加強的方案,如很多人常用的平行化處理方案,透過多執行序來同時執行 PHP 的單元測試以加快完成速度,其中 ParaTest paratestphp/paratest: Parallel testing for PHPUnit 就是這中間很有名的一個工具。
在 ParaTest 中,要怎麼樣宣告產出 JUnit XML 格式的報表呢?在 ParaTest 的手冊中提到,原本在 PHPUnit 中使用的語法,在 ParaTest 基本上都是可以直接相容的,其中 JUnit XML 格式的報表就是相容的項目之一。因此無論是使用 phpunit.xml
宣告,或者是直接使用 Command Line 搭配參數指令執行的方法都是一樣的。如下,首先是使用 phpunit.xml
宣告的方法:
1 | test:paratest-with-phpunit-file: |
上面的原始碼中,比較需要注意的地方是 mkdir -p build
產出資料夾的部分,在目前範例中使用的 ParaTest 6.3 版本中,如果宣告產出報表所在的「資料夾不存在」,那麼在執行 ParaTest 的過程中換產生一段警告,顯示無法寫入檔案。(如下)
1 | Warning: file_put_contents(/builds/mouson-gitlab-playground/feature-demo/gitlab-phpunit-gen-junit-report-demo/build/report.junit.xml): Failed to open stream: No such file or directory in /builds/mouson-gitlab-playground/feature-demo/gitlab-phpunit-gen-junit-report-demo/vendor/brianium/paratest/src/Logging/JUnit/Writer.php on line 99 |
進而造成 GitLab 工作完成後,要打包產出物時,找不到檔案而產出另外一個警告。(如下)
1 | Uploading artifacts... |
因此在範例中,需要特別執行 mkdir -p build
來建立 build 資料夾。
接下來就是使用 Command Line 產出報表的範例:
1 | test:paratest-with-command-line: |
同上一個範例中,也同樣有 mkdir -p build
這段產出 build 資料夾的部分。其它的如 PHPUnit 在 Command Line 一樣,要產出 JUnit XML 格式的報表必須要透過 --log-junit <file>
參數。
在 GitLab CI 中使 PHP 的測試工作 PHPUnit 執行後產出 JUnit XML 格式的報表,主要倚靠 PHPUnit 自身的功能搭配 GitLab CI artifacts:reports:junit
機制來完成,其實不難,但因為可以直接在 Pipeline 中直接查看到執行結果,在單元測試發生錯誤時,就可以馬上透過 Pipeline 中提供的報表直接查看到出錯的位置,減少了好幾個步驟,是個值得增加的設定。
GitLab 官方固定在每個月 22 日釋出新的版本,也會針對該次的釋出提供一則圖文並茂的說明;而由於墨嗓我個人對於 GitLab CI/CD 的部分相對有興趣,因此對於每次釋出的內容中,關於 GitLab CI/CD 的部分會看得特別仔細,甚至會做一些實驗。
既然做了,那就多做一點點,記錄下來,跟大家一起分享,希望對大家有些幫助。這是第一次的嘗試,沒意外的話會持續做下去。
這個月釋出的是 GitLab 13.12 版。
在 pipeline 編輯器裡頭增加更多有用的 GitLab CI/CD 的資訊。如下圖,在 GitLab CI/CD 的 Editor 畫面裡,增加了右邊的這個區塊,看起來對於剛開始接觸 GitLab CI/CD 的使用者來說,提供了很多怎麼開始的訊息。
在 include
語法中,支援 wildcards,原本類似的名稱需要寫很多次,現在可以透過 *
號直接一次性的載入,讓 .gitlab-ci.yml 更加的簡潔。範例如下圖:
更多的細節可以參考官方的 include 說明文件。
DAG 的功能讓 GitLab CI/CD 的工作可以不用一個 stage 一個 stage 的執行工作,讓不同 stage 之間的 job 彼此有了關聯。但在觀看 Pipeline 執行的時候,卻沒辦法看到這些工作中的關聯。在 GitLab 13.12 版中,讓 Pipeline 執行的時候也可以看到彼此的關聯性了。如下圖或影片的呈現。
在 GitLab CI 的過程中,當測試發生失敗時,如果可以從 Pipeline 的執行畫面中,就可以看到什麼地方錯誤,就可以減少一些查看錯誤點的時間。但如果是執行 E2E 的測試時,可能就沒辦法直接看到是什麼樣的測試情境、動作下發生錯誤。
在 GitLab 13.12 的這版本中,延伸 test report 的功能,開始支援 JUnit report 中使用 attachment
這個標籤,讓縮圖等圖像檔案可以包含在其中。
1 | <testcase time="1.00" name="Test"> |
讓 test report 的內容可以直接呈現發生失敗時的畫面,使開發者可以快速的掌握到出錯的原因,加速確認問題。
Ref:
在 Ultimate 版本的 GitLab 中,在 Pipeline 執行過程中可以直接產出 Code Quality Report 並且呈現在 Pipeline 上,而有許多的功能,現在又更近一步的在觀看 MR 的過程中,就讓使用者可以直接看到違反原則的地方。
直接呈現 Group 層級的部署頻率的圖表。如下圖:
在 .gitlab-ci.yml 中,開始支援 workflow
語法裡頭建立自訂變數。用法如下:
1 | workflow: |
如上,當 Merge Request 的目標分支是 master
的時候,變數 MY_VAR
的值為 hello
否則為 workflow 中初始的 initial value
。
Ref:
原本 pipeline 編輯器中,無法很明確地顯示執行狀況,現在可以直接向 MR 裡頭一樣,呈現該 pipeline 的執行狀態了
在 .gitlab-ci.yml 中 release
這個關鍵字開始支援輸出連結。範例語法如下:
1 | release: |
Ref: release:assets:links
建立 Laravel Migration 時,有時候必須要根據現有資料庫中的資料逐筆更新,像是資料庫欄位轉移、變換等等的,當資料庫數據數量不多時,這樣的更新可能一下就完成了,但如果資料庫的資料很多時,會發現執行 php artisan migrate
時,會出現「等待該 migration
執行,但卻不知道目前執行狀況」的情境。
這時候如果這個資料庫更新,可以加上「進度條」呈現,就可以讓維運人員可以得到回應,相對了解當下的狀況,進而做出更好的狀態判斷。
那麼,該怎麼在 Laravel Migration 的過程中加上進度條 (Progress Bar) 呢?
如下為完整的範例原始碼:
1 | use Illuminate\Database\Migrations\Migration; |
1 | $output = new ConsoleOutput(); |
在原始碼中的 Line 11-12 行使用了兩個 Symfony
Framework 的工具,分別是 ConsoleOutput
以及 ProgressBar
。
在 Line 11 中 new ConsoleOutput()
在取得 CLI Console
畫面輸出的資源,而 Line 12 的 new ProgressBar($output, $count)
則是在畫面輸出的資源上,建立一個進度條物件,並且宣告這個物件預計會有 $count
個「步驟」。
另外 ProgressBar
這個物件還提供了第三個參數 $minSecondsBetweenRedraws
預設為 1/25
秒(0.25秒),如果遇到需要調整進度更新速度的情境,可以透過第三個參數做調整。
1 | $progressBar->start(); |
這三行分別是進度條的「開始」、「前進」及「結束」:
$progressBar->start()
進度條開始。進度條開始呈現,這邊可以再次的設定這個進度條步驟總數,預設為 null
也就是沿用 ProgressBar
宣告時的進度步驟數,開始後 ProgressBar
本身的 timer
即開始作用,並在 Console 畫面上呈現進度條。
$progressBar->advance()
進度條步驟前進讓進度前進並呈現在畫面上。預設為增加一個步驟,也可以透過參數調整,如 $progressBar->advance(4)
就是前進四個步驟。
$progressBar->finish()
進度條完成完成整個進度條進度。就算目前進度尚未達到設定的最大值,依然直接呈現進度完成 100% 並且結束整個進度條。
1 | $output->write(PHP_EOL); |
讓進度條完成後可以換到下一行繼續顯示。如果沒有在 ConsoleOutput
輸出一個換行字元,則會發現後續的 Migrated: SomeMigrate
換接續在進度條後面直接輸出。如下圖:
在 Symfony 提供的 Progress Bar 元件中,其實還有更多不一樣的用法,如自訂輸出格式,可以做出各種華麗的進度顯示,也可以同時呈現多條進度條,但對於一般的資料庫更新來說,預設值都還算夠用,有興趣也可以參考 Symfony 提供的 Progress Bar 文件 看更進階的使用方法。
這陣子在進行手邊 PHP 專案的版本升級,從 PHP 5.x 升級到 PHP 7.x ,由於專案裡大量的使用了 while(list($key, $value) = each($items)) {}
這樣的語法,而在 PHP 7.2 之後,官方直接標注不建議使用,因此升級的過程中,順帶轉換為 foreach
結構,在過程中踩到一些 PHP 5.x 升級到 PHP 7.x 之後的特性雷,因此寫下這篇作為紀錄。
PS. 現行 PHP 7.2 當未關閉警告的情況下,會提醒 PHP: each - Manual 不建議使用¶:
1 | Deprecated: The each() function is deprecated. |
首先,測試資料的部分,全程使用底下的陣列內容。
1 | $items = [1, 2, 3, 4, 5, 6]; |
在 Migrating from PHP 5.6.x to PHP 7.0.x 的文件 PHP: Backward incompatible changes - Manual 中,提到 foreach no longer changes the internal array pointer
在 PHP 7 之後 foreach
不會再改變 array
的內部指標。如下驗證測試碼,current 可以取得目前 items 這個 array 的指標位置,當超出範圍則顯示為 boolean 的 false 否則為 integer:
1 | $items = [1, 2, 3, 4, 5, 6]; |
1 | PHP version: 7.1.8 |
1 | PHP version: 5.6.30 |
平常我們會如此的使用 foreach 做迴圈內容的操作,這邊為單純整個測試及紀錄,就不變動 array 的內容。
1 | $items = [1, 2, 3, 4, 5, 6]; |
輸出:
1 | PHP version: 5.6.30 |
1 | $items = [1, 2, 3, 4, 5, 6]; |
輸出:
1 | PHP version: 5.6.30 |
目前為止無論是 each 或是 foreach 的操作,不論是 PHP 5.6 或是 PHP 7.1 兩者的輸出內容是一致的。
1 | $items = [1, 2, 3, 4, 5, 6]; |
1 | PHP version: 7.1.8 |
1 | PHP version: 5.6.30 |
從上面的結果看到,PHP 5.6 的執行結果,在進行完 foreach
迴圈之後,再做一次同一陣列的 while each
迴圈,已經不會再印出任何內容。 each
迴圈在操作時,可以透過 PHP: current - Manual 來取得目前陣列的 pointer
指到 array 的哪一個 element,稍作修改,使上面的程式印出陣列目前所在的 pointer 位置。
1 | $items = [1, 2, 3, 4, 5, 6]; |
1 | PHP version: 7.1.8 |
1 | PHP version: 5.6.30 |
發現,在 PHP 7 版本中,pointer 並沒有轉變,而 PHP 5.x 版本則會隨著 foreach
操作,連帶變動到 each 使用的 pointer 指標。
1 | $items = [1, 2, 3, 4, 5, 6]; |
1 | PHP version: 7.1.8 |
1 | PHP version: 5.6.30 |
特性如同上一個小實驗,在 PHP 7 版本中,pointer 並沒有轉變,而 PHP 5 版本則會隨著 foreach
操作,連帶變動到 each 使用的 pointer 指標。
先一次 each 操作,而後 foreach,接著使用 while each 迴圈,程式碼:
1 | $items = [1, 2, 3, 4, 5, 6]; |
1 | PHP version: 7.1.8 |
1 | PHP version: 5.6.30 |
這邊是第二個需要特別注意的地方,在 PHP 7 版本中,因為不再操作 array 內部的 pointer,因此就算在 foreach 操作前做了一次 each 操作,也不影響 foreach 的初始值,且來到 while each 迴圈中時,會從陣列的第二個位置開始。但…在 PHP 5 中,foreach 會重置 pointer,而後在結束 foreach 迴圈離開後, while each 直接接 foreach 結束之後的 pointer 繼續進行。
foreach by-reference
操作到 array 的內容時,那下一個地雷將會是這個:foreach by-reference has improved iteration behaviour ,在 PHP 5 時,會操作到的指標僅陣列的原始範圍,但在 PHP 7 開始,則會動態的調整需要走訪的陣列範圍,如官方的範例:1 | $array = [0]; |
1 | int(0) |
1 | int(0) |
影片連結:影片連結
]]>這份簡報分為兩個部分:
第一部分怎麼讓一個完全沒有原始碼版本控制的大型專案,逐步的轉變,陸續導入 GitLab 的各種功能,直到現在專案已經有了自己的 CI/CD 流程。這期間期間當然包含了一些政治面、商業面的因素,但整體經歷了哪些事情呢?在這部分會跟大家聊聊這段故事。
第二部分,在使用及導入 GitLab 的過程中,隨著專案演進 .gitlab.yml 定義的流水線 pipeline 上有越來越多工作,怎麼讓工作流程進行的更快一些呢?又,可以怎麼整理 .gitlab.yml 檔,讓他更簡潔一些?在這部分將跟大家分享一些小技巧。
內容中所用到的 source code 如下:
https://gitlab.com/mouson-gitlab-playground/gitlab-ci-demo-sample
可透過切換分支進行查看
在這場分享裡,您可以聽到大型系統重構的技巧,包括如何消滅 Global 變數、整合 PHPUnit、用 Xdebug 追蹤程式碼,以及搭配使用 PhpStorm 的祕技。
工程師都不喜歡碰別人寫得程式碼,因為那些所謂的 Legacy Code 往往寫法過時、沒有文件,維護起來特別痛苦。但這些 Legacy 專案之所以能持續存在,往往代表其具有絕對的商業價值。身為成熟的開發者,我們應該格守童軍法則,除了讓 Legacy 專案能持續運作外,更應該在每一次的迭代裡往更好的方向前進。在這一場線上技術分享裡,Mouson 以面對它、接受它、處理它、放下它等四個階段,提示大家如何用正確的心態面對 Legacy 專案。
在本次的分享裡,Mouson 先跟大家討論何謂 Legacy 專案?在一般的認知裡,都是指那些年代久遠、結構龐大、經歷多次迭代、沒有文件的程式碼。不過,Mouson 曾面對更棘手的情況。他所處理的 Legacy 專案,是跑在 Apache 1.3、PHP 4.1.2 的古老專案。不僅如此,這些 PHP 程式高達 1000 隻、逼近 20 萬行。除了經歷 5 個年代的開發風格且沒留下文件外,專案還使用了不常見的商用資料庫,甚至還包括用 C 寫的 PHP Extension。最頭大的是,一些 Extension 是連原始碼都沒有!
即便面對這種史詩級的專案,Mouson 鼓勵大家以正確的心態面對,並謹記那些不能殺死你的,終將使你更強大的格言,依鐵哥(Jace)在「重構或重寫 Legacy code 的幾個階段」一文裡提到的四個階段 - 面對它、接受它、處理它、放下它,逐一說明他們團隊與 Legacy 專案博鬥的故事與從中累積出來的經驗及方法。
首先,團隊需正視自己手上的 Legacy 專案,綜覽全局,並花時間理解 Legacy Code 的架構,做為後續重構的基礎。接著,他與團隊合力改變工作流程,包括建立 Issue Tracking、更換版本控制系統成 Git、導入 CI/CD 自動化。而在撰寫程式的工作上,他們選擇使用 PhpStorm 來協助檢查可能在升版過程中出錯的程式碼,並以內建的重構工具輔助他們修改程式碼。另外,團隊也開始施行 TDD,針對核心演算法進行重構、補上測試,並定期 Code Review。在逐步改善的過程中建立研發團隊的信譽,讓業務及客服團隊對產品的信心提升。
除了分享團隊升級的心路歷程外,Mouson 也以 PhpStorm 實際示範了幾個重構情境,包括如何用 PhpStorm 搭配 Xdebug 的中斷點、Step Into 等功能追蹤原始碼,加上幾個常用的快速鍵,就可以在眾多 Class 及 Method 間跳躍。以及 PhpStorm 在撰寫 TDD 的測試案例時,可以運用 IdeaVim 的客製化設定一秒複製測試案例,搭配執行測試的快速鍵,讓 TDD 開發更順暢。從 Mouson 示範過程中,能夠發現只要能夠掌握 PhpStorm、Xdebug 的操作技巧,重構程式似乎就沒有這麼複雜,用 TDD 來開發也沒想像中的困難。
在台日技術社群交流會所做的分享,這份簡報主要介紹三個 GIT 的指令,分別是 bisect、rerere 以及 worktree:
簡報連結:20170701 Eloquent 核心解構 讓 Laravel 支援更多資料庫 - Speaker Deck
影片連結:[LaravelConf Taiwan 2017] 陳佑竹 - Eloquent 資料庫層核心解構,讓 Laravel 支援更多資料庫 - YouTube
]]>找了好一陣子,一直沒能弄到自己喜歡&順手的網站備份工具,想一想自己想要的需求應該不會太難,主要應該要有以下需求:
但,一直沒能找到自己順手的…於是,就自己動手了一個基本符合自己需求的工具 - WebSiteBackupTools , 這個工具使用了 Dropbox-Uploader 這個 Dropbox Uploader is a BASH script 也不用再自己額外寫 Dropbox API 的功能,且持續的更新中,不致於發生功能壞掉沒能修的問題。
以下就繼續說明該如何使用囉!
1 | wget https://github.com/mouson/WebSiteBackupTools/releases/download/v0.1/WebSiteBackupTools.zip |
1 | git clone https://github.com/mouson/WebSiteBackupTools.git |
1 | cd /root/WebSiteBackupTools |
其中
1 | #!/bin/bash |
執行以下指令:
1 | sh /root/WebSiteBackupTools/Scripts/Backup2Dropbox.sh config.sh |
當你有多組網站需要備份,也可以這樣使用
1 | sh /root/WebSiteBackupTools/Scripts/Backup2Dropbox.sh website1_config.sh |
如果需要設定 cronjob 排程,也可以進行以下設定:
1 | crontab -e |
1 | 0 2 * * * sh /root/WebSiteBackupTools/Scripts/Backup2Dropbox.sh website1_config.sh > /dev/null 2>&1 |
這份工具目前還不是我心目中最完美的備份工具,但堪用,之後會持續的更新它,讓它變成我最愛的工具。生日的這天,完成這小工具,還蠻有意思的。
有任何需求會任何 bug 也歡迎回報。
]]>影片連結:GIT從入門到應用-線上讀書會
]]>