[學習筆記] 為前端框架 Component 建立 Props Typing 的幾種方式,以 React 為例

前言

在開發 React 應用程式時(其它框架其實也適用),
使用 TypeScript 來強制定義 props 是一個常見的做法。
然而,有許多不同的方法可以實現這一目標。這篇文章討論了三種主要的方法。

比較

Inline Object Literals

在這個方法中,我們直接在函式的參數位置定義了 props 的型別,如下所示:

1
2
3
4
5
const Wrapper = (props: {
children?: ReactNode;
}) => {
return <div>{props.children}</div>;
};

優點:快速簡潔。
缺點:不太適合長期維護。

另一種解構的寫法會有更少的字數,
它會有一個 {}:{}特殊寫法,前者表示傳入的參數,後者表示要被解構的物件與型別
這樣的寫法最主要在 component 當中要使用傳入參數時可以省略 prop. 的語法,
但是理解上會不會更困難(對於不熟悉的開發者而言)就見團隊見智了…

1
2
3
4
5
6
7
8
 
const Wrapper = ({
children,
}: {
children?: ReactNode;
}) => {
return <div>{children}</div>;
};

Type Aliases

在這個方法中,我們把 props 的型別定義抽取到一個類型別名中:

1
2
3
4
5
6
7
export type WrapperProps = {
children?: ReactNode;
};

const Wrapper = (props: WrapperProps) => {
return <div>{props.children}</div>;
};

優點:可以在其他文件中重用。
缺點:在大型代碼庫中可能會讓 TypeScript 變慢。

Interfaces

在這個方法中,我們使用介面來定義 props:

1
2
3
4
5
6
7
export interface WrapperProps {
children?: ReactNode;
}

const Wrapper = (props: WrapperProps) => {
return <div>{props.children}</div>;
};

優點:性能較好,在大型代碼庫中表現較好。
缺點:需要較多的代碼。

小結

儘量使用 interface 會有較好的效能,同時可以共用這些代碼並幫助理解。

參考

(fin)

[學習筆記] Vim 學習資源

介紹

Vim 是一款強大高效的文本編輯器,強調效率和快速操作。
學習 Vim 能顯著提升編輯速度和效率,無需滑鼠,節省操作時間。
這裡記錄著我的學習資源,提供給未來的我複習使用。

TLDR Tips

  1. gg:移動到檔案的第一行
  2. G:移動到檔案的最後一行
  3. gg=G:重新縮排整個檔案
  4. gv:重新選取上一次的視覺選取
  5. `` <`:跳到上一次視覺選取的開始
  6. `` >`:跳到上一次視覺選取的結尾
  7. ^:移動到行的第一個非空白字符
  8. g_:移動到行的最後一個非空白字符
  9. g_lD:刪除行上的所有尾部空白
  10. ea:在當前單字的末尾插入
  11. gf:跳到游標下的文件名
  12. xp:向前交換字符
  13. Xp:向後交換字符
  14. yyp:複製當前行
  15. yapP:複製當前段落
  16. dat:刪除包括標籤在內的 HTML 標籤
  17. dit:刪除 HTML 標籤內的內容,但不包括標籤本身
  18. w:向右移動一個單字
  19. b:向左移動一個單字
  20. dd:刪除當前行
  21. zc:關閉當前摺疊
  22. zo:打開當前摺疊
  23. za:切換當前摺疊
  24. zi:完全切換摺疊
  25. <<:向左移動當前行的縮排
  26. >>:向右移動當前行的縮排
  27. z=:顯示拼寫更正
  28. zg:添加到拼寫字典
  29. zw:從拼寫字典中刪除
  30. ~:切換當前字符的大小寫
  31. gUw:將大小寫轉換到單字的末尾(u 用於小寫,~ 用於切換)
  32. gUiw:將整個單字轉換為大寫(u 用於小寫,~ 用於切換)
  33. gUU:將整行轉換為大寫
  34. gu$:將直到行尾的文本轉換為小寫
  35. da":刪除下一個雙引號括起來的字符串
  36. +:移動到下一行的第一個非空白字符
  37. S:刪除當前行並進入插入模式
  38. I:在行的開頭插入
  39. ci":更改下一個雙引號括起來的字符串內容
  40. ca{:更改大括號內的內容(也可以試試 [, ( 等)
  41. vaw:視覺選取單字
  42. dap:刪除整個段落
  43. r:替換字符
  44. ``[`:跳轉到上次複製的文本的開始
  45. ``]`:跳轉到上次複製的文本的結尾
  46. g;:跳轉到上次更改的位置
  47. g,:向前跳轉到更改列表
  48. &:在當前行上重複上次的替換
  49. g&:在所有行上重複上次的替換
  50. ZZ:儲存當前檔案並關閉

學習資源

內建學習工具

1
> vimtutor

(fin)

[實作筆記] 一些關於 Azure Resource Group 的冷知識

前情提要

Resource Group 是 Azure 上比較特別的一個設計,
這裡拿來記錄一些知道就知道,不知道就不知道的小事務。

本文

NetworkWatcherRG

注意

當您使用 Azure 入口網站 建立網路監看員實例時:

網路監看員實例的名稱會自動設定為NetworkWatcher_region,其中region會對應至 網路監看員 實例的 Azure 區域。
例如,在美國東部區域中啟用的網路監看員名為NetworkWatcher_eastus。
網路監看員實例會建立在名為NetworkWatcherRG的資源群組中。 若尚無該資源群組,將會加以建立。

Azure DevOps

當我們將 Azure DevOps 的 Billing 綁定之時,
會建立一組 VisualStudioOnline-XXXX 的 Resource Group

(fin)

[實作筆記] Macbook 壓縮檔案

前情提要

假設分享機密文件給同事或者發送文件到郵件或雲端儲存時,就必須手動處理壓縮和加密,
可以參考一些文章,大致有三個作法

  • 購買付款壓縮軟體
  • 使用雲端服務
  • 手動執行 terminal 指令

手動執行就可以處理的問題,我不會特別想要付款買一個軟體,
而雲端服務會擔心資訊安全,特別是要加密的資料代表有一定程度的重要性。
Terminal 大概是網路文章的主流解。
當我需要經常這樣做時,就太麻煩了,還要記得操作指令,額外增加心智負擔。
理想上,我希望只要選擇文件或資料夾,右鍵點擊”Zip with Password”,就能創建加密的 zip 文件。

實作

  1. 打開 Automator app。

  2. 創建一個新 Automator 文件:File > New (或按 ⌘N),選擇 “Quick Action” 類型。

  3. 將輸入類型更改為 “files or folders”。

  4. 在左側的 Actions library 中,雙擊 “Run AppleScript”,或拖放到右側工作區。刪除示例代碼,替換為第 5 步的腳本。

  5. 複製並粘貼下面的 AppleScript 代碼。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43

    set prompt_text to "請輸入壓縮密碼"

    repeat
    set zip_password to text returned of (display dialog prompt_text default answer "" with hidden answer)
    set verify_password to text returned of (display dialog "再次輸入壓縮密碼" buttons {"OK"} default button 1 default answer "" with hidden answer)
    considering case and diacriticals
    if (zip_password = verify_password) then
    exit repeat
    else
    set prompt_text to "密碼不一致,請重新設定"
    end if
    end considering
    end repeat

    tell application "Finder"
    set the_items to selection
    if ((class of the_items is list) and (count of the_items) > 0) then
    set items_to_zip to ""
    repeat with each_item in the_items
    set each_item_alias to each_item as alias
    set item_name to name of each_item_alias
    set item_name to quoted form of (item_name & "")
    set items_to_zip to items_to_zip & item_name & " "
    end repeat

    set first_item to (item 1 of the_items) as alias
    set containing_folder to POSIX path of (container of first_item as alias)
    set zip_name to text returned of (display dialog "輸入壓檔名" default answer "")
    set zip_file_name to quoted form of (zip_name & ".zip")

    if zip_password is not equal to "" then
    -- 如果存在密碼,執行加密壓縮
    do shell script "cd '" & containing_folder & "'; zip -x .DS_Store -r0 -P '" & zip_password & "' " & zip_file_name & " " & items_to_zip
    else
    -- 否則執行單純壓縮
    do shell script "cd '" & containing_folder & "'; zip -x .DS_Store -r0 " & zip_file_name & " " & items_to_zip
    end if
    else
    display dialog "你未選擇任何檔案。" buttons {"OK"} default button 1
    end if
    end tell

  6. 儲存工作流程:File > Save (或按 ⌘S),給它一個名字,如 “Zip with Password”

現在,選擇文件或文件夾,右擊,選擇 “Quick Actions” 中的 “Zip with Password”,
按照提示輸入密碼、驗證密碼和設置 zip 文件的名字。測試新創建的 zip 文件,確保一切運作正常。

參考

(fin)

[實作筆記] ClamAV 安裝

提要

因 ISO 需要在 GCP VM(Linux 系統)上安裝防毒。
ClamAV 是一款強大的開源防毒引擎,專為檢測和清除惡意程式而設計。
這篇用來記錄終端機中安裝和配置 ClamAV,包括系統更新、病毒庫更新、自動運行設定等步驟,
以確保系統有效抵禦各種威脅。

實作

首先,在終端機中輸入以下指令,展開系統更新的步驟:

1
sudo apt update && sudo apt upgrade

出現警示請按 Y

1
2
After this operation, 12.1 MB of additional disk space will be used.
Do you want to continue? [Y/n] Y

接著,透過以下指令安裝 ClamAV,一強大的防護工具,以確保系統免受惡意程式的侵害:

1
sudo apt install clamav clamav-daemon -y 

當安裝完成後

確保沒有其他 freshclam 進程在運行,您可以使用以下指令查看:

1
ps aux | grep freshclam

如果有其他 freshclam 進程在運行,請終止(kill)它們。

執行以下指令可更新 ClamAV 的病毒定義庫:

1
sudo freshclam

隨後,您可以進行系統掃描,以查找並清除潛在的威脅,請使用以下指令:

1
sudo clamscan -r /path/to/folder

若欲使 ClamAV 在系統啟動時自動運行,請執行以下指令:

1
sudo systemctl enable clamav-daemon

這將設定 ClamAV 在每次系統啟動時主動保護您的系統。

啟動 ClamAV 服務(如果它沒有在系統啟動時自動啟動):

1
sudo systemctl start clamav-daemon

檢查 ClamAV 服務的運行狀態:

1
sudo systemctl status clamav-daemon

這樣,您可以確保 ClamAV 已經更新、服務已經啟動,並檢查服務的當前運行狀態。

參考

(fin)

[生活筆記] 2023 的回顧與展望

引言

2023 年過去了, 作個回顧

工作相關

2023 年對我來說是個不穩定的一年,甚至考慮出國去唸語言學校,但最終留在了台灣。
T 社的本來想說點什麼,一年內提筆多次想想就又算了。
一年都過了還是作個簡單記錄。

T 社事件

本來想加入一個前輩 S 作為主管的團隊,
與這位前輩其實沒那麼多接觸,但知道技術實力不差,本來也是 N 社早期的創業員工之一。
預計是希望能近距離合作並學習,並以創業心態開發一款國際產品。
實際上加入 T 社後,S 桑進公司的次數屈指可數,
產品的發展也與預期不符,市場已有大量類似的競品,老闆吹的天花亂墜,但只是一個點餐系統
T 社本身到母公司 W 集團,完全缺乏軟體專案的管理經驗,隨便而缺乏專業開發團隊,
而缺乏軟體工程的管理專業,使得他們無法有效追蹤進度

需求與溝通

後續的結果是開發團隊與 W 集團互信不足,演員底的老闆,只能帶著兒子、特助或是咖啡甜點來信心喊話,
團隊內的產品部門變成了對講機與美工,唯一的功能就是轉達集團的需求與繪製 Figma。
整個產品團隊無法回答開發團隊的疑問,反而不斷的抱怨集團、特助或老闆,
整個溝通變得無效且變形

開發

反過來是招募時,餅畫得太大,導致 RD 明顯過度設計,
比如說,RD 跑了一個沒有 Domain Expert 的 DDD,
在沒有上線、沒有賺錢沒有用戶的情況下,用自已的腦補進行了領域的分工,
不到10人的團隊切分了 10 幾個微服務,並且濫用了 GraphQL。
這樣又帶來後續的問題,
第一、語言不一致,一開始的想法是讓各個開發者有開發的自主,但問題變成無法互相支援,各自畫地為王
第二、開發風格不一致,即使是使用相同的語言,使用的套件與寫作風格也未統一,所以要支援也有難度
第三、開發準備作業多,很多需求經討論後都需要新建成服務,RD 需要從建立 Repo,
到自行建置 GCP 相關服務(網路、資料庫、CI/CD 等),
第三、串接複雜,API 重重呼叫,為了要追蹤錯誤,需要延申很多額外的設計,
一個簡單的 CRUD,需要兩個 GraphQL 如果有要異動到呼叫的 API,更新一個 API 就要動到三個專案
第四,但是因為團隊缺乏領導者(S 桑)的情況下,開發者沒有有效的權責分工,GCP 的權限被一些早期入職的員工掌握,而需要等待。
第五,無溝通與不透明,晨會就形同虛設,整個團隊每個人變成一個一個穀倉

缺乏用戶

整個產品核心的兩個功能(點餐與收費)在兩個系統上,一個外部公司開發的軟體,一個在老舊的 POS。
而我們開發的系統比較像是一個單純的 App 加周邊整合。
這麼簡單的東西,卻用了一個複雜的架構,導致修改速度緩慢,遲遲無法上線,面對需求調整時也沒有彈性。
更進一步加深集團與開發團隊的不信任。

團隊狀況

  • 資料與雲端權限在一個菜鳥身上,不論有意無意,權限與知識不願意共享,除非有人踩到雷
  • 前端上班睡覺,開發品質低下的 React(完全沒有模組化的思維,不如用原生三本柱開發還比較快)
  • 產品設計的後台實務上沒有人會使用,主要還是在母公司的 ERP E
  • APP 組用了 Flutter 思維確停留在 IOS/Android/Web 的分工,到後來兩人也無法互相支援
  • 想學 C# 的 RD 整天吹噓 N 社的失敗專案(XmiERP),而代碼品質可讀、可測試性極差,讓我理解 Php 工程師的下限
  • 想學 Golang 的 RD 應該是整體過度設計的原兇,第一時間離職了。
  • 領頭 S 桑遲遲未進入團隊後,
  • 來了一個之前也在 N 社待過”幾天”的 R 主管
    • 缺乏開發能力(至少在這樣的穀倉下)
    • 也缺乏軟體開發管理的素養
    • 甚至連基本守法都作不到

  

勉強要說會作簡報,但是產品設計不合理,比如說,他花了 3 個月作簡報搞了一個動態標籤點餐,
然後跟廠商要了一個動態標籤機,丟在那裡再放3個月,然後拒絕承認有過這件事ಠ_ಠa,是在哈囉。
然後面試找來自已的樁腳。
整個公司與團隊缺乏互信

我的努力

試著找 S 桑與團隊開了一場大會,總算把 APP 推上架。
上架唯一可以收費的功能由我開發部署到實體店面驗收,並給了團隊與主管中實的逆耳忠言。
儘可能的 Change Your Company。
只可惜爛泥扶不上牆,石瓦塗黃泥也不成金。

以上是 T 社的故事概述,至於關於 R 與 W 集團的非法行為就另一個故事了(🍵

顧問

年初有找到很多的機會,例如在 C 社擔任顧問職,
或是接案,也算是個不錯的經驗,主要的發現如下。

  • 沒有識別專業能力的能力→如何確定軟體開發的產品如期如質?
  • 沒有正確分工的能力→大環境太偏向技能分工(ex:前後端、SRE、DBA etc…, 我認為應該更偏向需求端與實作端)
  • 沒有建立與管理流程的能力,版控流程、權限控管、需求流程

這剛好是我擅長且有經驗的,
資深的開發者的問題在停止學習,執著於自已的開發習慣,
資淺的開發太想炫技,而過度設計,兩者都需要透過管理手段去調控。

A 社相關

目前的事就先不多說,至少我開發的東西已經幫公司賺到 3000 萬以上,
遺憾的事是,幾個下屬想拉拉不起來,只好協議讓他離開,一切流程也合法合情,  
這也是我說的,有資金的人不見得有識別專業的能力,千里馬常有伯樂不常有。
其他一些甘苦還在進行式,至少比 T 社好多了,就不多談。

  • 主管技能學習中…
  • GCP 技術大躍進
  • ISO 認証學習中
  • Azure 技術學習中

生活

  • 潛水終於考到 AWO,下次想換個潛點了  
  • 跳舞方面
    • 跟知名的老師跳了一首,2023 足夠了
    • 上了幾個國際老師的課,不過還是沒有感到進步
      • Jo
      • Meti & Andante
      • Bianca and Nils (沒上課但是有跟 Bianca 跳到舞)
      • Lisa & Fabien
      • Dasha & Roman (有跟 Dasha 跳到舞)
      • Melanie & Jeremy
      • Chloe & Rico
      • Moondewti
      • Laura & Youngbo
    • 在誠品表演、嚐試一些空中動作 
    • 喜歡的樂手
      • Charlie Christian
      • YOASOBI
    • 喜歡的漫畫
      • 莽送的莉芙蓮
      • Völundio(休刊中)

     

  • 換了護照準備出國但是還不知道要去哪裡?
    • 打算再帶媽媽出國一次
    • 也想去倫敦看阿森納比賽
    • 也想再參加國外的 workshop
    • 不想變成只會希望而不行動的麵包師傅

展望

投資

Q1 總資產有望達到一個重大里程碑。下一個目標是持股資產達這個里程碑

英文

制定計劃中

工作

仍是目標全遠端/斜槓或接案

社交

毫無頭序,也應該制定計劃嗎???

健康

踢球?健身?跳舞

(fin)

[生活筆記] 修正 A 校的題目缺陷

導言

在 A 校擔任雲端助教一陣子,主要有幾個原因,
我也是資策會出身,所以我對轉職的學生可以感同身受,
我在教學的過程中,自已也會成長,很多時候可以學習到新知也得到成就感,
最後就是可以獲得額外的收入。

問題發現

這次是一個我有興趣的題目,簡單說 A 社利用 Replit 網站提供作業,
由學生進行撰寫,很聰明的整合方式,而且幾乎零成本。
而且我覺得最棒的是,他可以寫測試腳本,透過測試腳本,就可以驗收大部份的作業,加速批改的時間。

但是,這次同學的作業在執行時期,發生了異常掉入了一個無限廻圈的狀態
問題不難,是一個邊際值的問題。 應該可以加上測試保護,這是我的第一個直覺。
我去檢查了 A 校提供的標準答案,不出意外的也有相同的問題。

另外一個問題是,寫邊際值的測試案例不是基本的嗎?
查看了測試案例,竟然還真的沒有寫

問題排查

我選擇下載了 Replit 到地端開發,
在開始前先簡單描述題目,在一個限範圍內進行猜數字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const answer = Math.floor(Math.random() * 100) + 1
// start coding

let min = 1
let max = 100
let guess = Math.floor(Math.random() * 100) + 1
let count = 1

function getResult() {
while ( ) {
if ( ) {

} else if ( ) {

}
guess = Math.floor((max + min) / 2)
count ++
}
console.log( )
}

// 以下為測試檔,請勿更動
getResult()

module.exports = {
guess,
answer,
count,
getResult
}

這是很好的題目,可以同時使用到迴圈與判斷,也可以接觸 Math 模組。
我們來看一下測試案例,在 Replit 右下角的 Unit Test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
test("", async function() {
const spy = jest.spyOn(console, 'log')
const {
guess,
answer,
count,
getResult
} = index;

getResult()
if (guess === answer) {
expect(count).toBeLessThanOrEqual(10)
}
});

測試很單純,當答案在 1~100 之間時,執行迴圈的次數不應該超過 10 次(其實 7 次內應該都猜得出來)
我們應該加上一些邊際測試。
例如 1 與 100 的案例,

1
2
3
4
5
6
7
8
9
10
11
12
13
it("Answer_is_1", async function() {
const spy = jest.spyOn(console, 'log');
const originalMathRandom = Math.random;
Math.random = jest.fn()
.mockImplementationOnce(() => 0.001)
.mockImplementation(() => originalMathRandom());
//console.log('now random is', Math.random());
const index = require('./index');
index.getResult();

// Clean up
spy.mockRestore();
});
1
2
3
4
5
6
7
8
9
10
11
12
13
it("Answer_is_100", async function() {
const spy = jest.spyOn(console, 'log');
const originalMathRandom = Math.random;
Math.random = jest.fn()
.mockImplementationOnce(() => 0.999)
.mockImplementation(() => originalMathRandom());
//console.log('now random is', Math.random());
const index = require('./index');
index.getResult();

// Clean up
spy.mockRestore();
});

這裡注意到的是我們 mock 了Math.random,因為這才會影響我們的答案。

問題後的問題

當我們在測試案例為 100 時,會限入無窮迴圈,而 JavaScript 單緒的特性將無法離開這個測試,
雖然也不會報錯…但是測試將永遠跑不完。
這驅使我再加上一個測試案例,當執行次數超過 10 次時拋出例外。
而學生使用的範本,我希望儘可能不去修改它,
這裡要對 jest 與 javascript 要有足夠的理解才可以寫的好,
所幸在這個時代,有 AI 與 google 的加持下,很快就解決了。

最後的測試程式如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
describe('Guessing Game', () => {
let originalMathRandom;
let spy;

beforeEach(() => {
// Save the original functions
originalMathRandom = Math.random;
spy = jest.spyOn(console, 'log').mockImplementation(() => {});
});

afterEach(() => {
// Restore the original functions
Math.random = originalMathRandom;
spy.mockRestore();
jest.resetModules()
});

it("Answer_is_1", async function() {
Math.random = jest.fn().mockReturnValue(0.001);
const index = require('./index');
index.getResult();
expect(index.answer).toBe(1);
});

it("Answer_is_100", async function() {
Math.random = jest.fn().mockReturnValue(0.999);
const index = require('./index');
index.getResult();
expect(index.answer).toBe(100);
});

it('guess under 10 times', () => {
Math.random = jest.fn().mockReturnValue(0.5);
const index = require('./index');
for(let i=0; i<9; i++) {
index.getResult();
}
expect(() => index.getResult()).not.toThrow();
});

it('should throw error if count is more than 10', () => {
const originalMathFloor = Math.floor;
Math.floor = jest.spyOn(global.Math, 'floor')
.mockImplementationOnce(() => Math.floor(Math.random() * 100) + 1)
.mockImplementationOnce(() => 1000);
const index = require('./index');
expect(() => index.getResult()).toThrow('超過10次了,請重新開始');

});


// Add more tests as needed for the getResult function
});

給學生的出題程式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const answer = Math.floor(Math.random() * 100) + 1;

// start coding

let min = 1;
let max = 100;
let guess = Math.floor(Math.random() * 100) + 1;
let count = 1;

function getResult() {
console.log(`Guess number is ${guess}`);
while ( ) {
if ( ) {

} else if () {

}

count++;
if (count > 10) {
throw new Error('超過10次了,請重新開始');
}
}
console.log(`第${count}回合,您猜${guess},猜對了`);
}

// 以下為測試檔,請勿更動
module.exports = {
guess,
answer,
count,
getResult,
};

解答範本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
const answer = Math.floor(Math.random() * 100) + 1;

// start coding

let min = 1;
let max = 100;
let guess = Math.floor(Math.random() * 100) + 1;
let count = 1;

function getResult() {
console.log(`Guess number is ${guess}`);
while (guess !== answer) {
if (answer < guess) {
max = guess; //猜太大取代最大值
console.log(`第${count}回合,您猜${guess},太大了,請猜介於${min}~${max}之間的數字`);
} else if (answer > guess) {
min = guess; //猜太小取代最小值
console.log(`第${count}回合,您猜${guess},太小了,請猜介於${min}~${max}之間的數字`);
}
guess = (max === min + 1) ? max : Math.floor((max + min) / 2);

count++;
if (count > 10) {
throw new Error('超過10次了,請重新開始');
}
}
console.log(`第${count}回合,您猜${guess},猜對了`);
}

// 以下為測試檔,請勿更動
module.exports = {
guess,
answer,
count,
getResult,
};

小結

整個課程的調整與測試改寫,大概花了我 4~12 小時處理。
在改制後,互動環節變少了,計時制改為定時制的給薪,也讓無法花太多的時間幫學生排查問題。
這題目我有興趣就順手解決了。
可惜的是,我拿不到任何費用。

也再一次印証 AI 的強大,未來的人材需要有更高的整合能力,
寫測試寫程式,讀技術文章賺取資訊落的錢應該會越來越難賺。
但是能高度整合的人應該會更為搶手。

(fin)

[實作筆記] GCP Armor 設定

前情提要

在加入負載平衡(load balancer)後,我們發現防火牆的某些規則失效了,
尤其是那些僅允許公司內網存取站台的規則。
這是相當重要的規則,因為我們的部分開發資訊與半成品儲存在這些機器上。

之所以建立了負載平衡(load balancer),有兩個原因
首先,我需要作後端 API 的版本切換,而因為使用的程式語言不同並部署在不同主機,
而 load balancer 透過路由規則將流量導向不同的機器群的機制非常適合。
第二,我們的正式環境一直有 load balancer,而測試環境沒有,趁這個機會將沙盒/測試環境的配置調整成一致。

問題

本來有設定某些防火牆的規則,在掛載 load balancer 後就失效了。
原因是規則會識別流量的 IP,而掛載後,所有的流量對於機器來說,所有流量都來自 load balancer 的 IP 了。
這樣的規則形同虛設,這個時候 Google Armor 就是一個不錯的替代方案

Google Armor Tips

設定上十分簡單,在 Cloud Armor 頁面上選擇 policies,
我的情況是在建立 load balancer 後,就已經自動建立,
所以就直接修改。

參考規則如下

Action Type IP Addresses/Ranges Priority
Allow IP addresses/ranges 5*.1**.***.205/32 999
10.140.0.0/20
Deny IP addresses/ranges 0.0.0.0/0 1,000
(block all)

一個是對指定來源 IP 與內網允許流量進來,一個是拒絕所有流量,這裡的設定與防火牆蠻像的。
接下來是目標的部份,要設定你的機器群,設定好套用,大約 10 分鐘內就會生效(實測不到3分鐘就生效了)

小結

Google Armor的主要優勢之一是其簡單易用的設定。
在Cloud Armor頁面上,我們可以輕鬆地設定我們的安全策略,包括允許特定IP範圍的流量進入,同時拒絕不受歡迎的流量。
這些設定反映在我們的參考規則中,確保了對特定IP和內部網路的控制,同時阻止不受歡迎的流量。

特別是對於我們使用負載平衡器進行後端API版本切換和環境配置調整的需求,Google Armor提供了一個理想的解決方案。
其能夠在不同機器群之間巧妙地分發流量,確保我們的應用程式在不同版本之間平穩運作,同時維持良好的安全性。

這樣的調整不僅解決了我們遇到的具體問題,也提升了整體系統的安全性和可靠性。

(fin)

[實作筆記] Hexo 7 升級的錯誤處理心得

前情提要

最近在對Hexo進行升級至7.0版本的過程中,遇到了一些錯誤與問題。
這篇部落格將記錄下這次升級的過程中所遇到的問題以及解決方法,希望對有需要的讀者有所幫助。

錯誤記錄

Hexo Action 錯誤

升級後,遇到了Hexo Action報錯的問題,錯誤訊息如下:

1
2
3
4
5
6
node:internal/modules/cjs/loader:1051
throw err;
^

Error: Cannot find module 'hexo-util/lib/spawn'
Require stack:

解決方式是查找相應的模組,更新配置文件,在本案中,hexo-util/lib/spawn 路徑不存在,應該改為 hexo-util/dist/spawn。

地端執行 Hexo d 時 Permission Denied

在地端執行Hexo d時,遇到了權限問題,無法提交到GitHub的問題。
暫時解決方式是將repository的協議由ssh改為https,暫時繞過了問題。

1
2
3
4
deploy:
type: git
repository: https://github.com/marsen/marsen.github.io.git
branch: master

後來因 https 需要提供帳號密碼,而這些資訊不適合簽入版本控制,故改回了 ssh,
為了解決相應的 SSH 連線問題,需配置 SSH 連線 .ssh/config
參考:

1
2
3
4
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_rsa

YouTube Tag 無法處理

升級至Hexo 7.0後,發現內建的YouTube標籤無法使用,原因是Hexo 7.0中刪除了這些內建的標籤。
解決方式是引入安裝 hexo-tag-embed package。

Hexo 部署錯誤快取

在部署過程中,遇到了Hexo的快取問題,導致地端和CI都出現異常。
解決方式是清除 Hexo 的快取 hexo clean,完整 SOP 如下:

1
2
3
4
sudo rm -rf .deploy_git
hexo clean
hexo g
hexo d

使用 Docker 偵錯

Hexo Action本身是以容器去運行的,可以在本地端執行測試,不需推版
使用 Docker 進行 Hexo Action 時,進行偵錯的方法:

建置與執行

1
2
docker build -t hexo-action .
docker run -d hexo-action

觀察 logs 與 files 去偵錯,Docker Desktop 是很好的工具。

小結

這次Hexo 7.0的升級過程中,遇到了不少問題,但通過查找相應的解決方案,一一解決了這些問題。
在升級過程中,需要留意Hexo的官方文檔以及相關插件的更新,以確保能夠順利完成升級。

參考

(fin)

[實作筆記] Gitlab CI/CD 與 GCP - 錯誤處理

前情提要

在持續整合/持續交付 (CI/CD) 的過程中,我們常常會遇到一個問題:
當發生錯誤時,系統並不總是會拋出 exit 1 的錯誤碼。
這種情況下,即使發生錯誤,CI/CD 仍然會繼續執行,這可能導致部署了錯誤的版本上線,增加系統的不穩定性。

解決方法

為了解決這個問題,我們可以採取一些措施,確保在發生錯誤時 CI/CD 立即停止,並拋出 exit 1 錯誤碼。

1. 記錄錯誤信息到 output.log

首先,我們可以將所有的執行日誌輸出到一個文件,例如 output.log。這樣一來,
不管是哪個階段出現了問題,我們都能夠查閱這個文件,以便更好地理解錯誤的發生原因。

1
2
3
Copy code
# 在腳本中添加以下命令,將所有輸出寫入 output.log 文件
./your_script.sh > output.log 2>&1

2. 使用 grep 檢查錯誤

其次,我們可以使用 grep 命令來檢查 output.log 文件,查找關鍵字或錯誤模式。
如果發現了任何錯誤,我們可以採取相應的措施。

1
2
3
4
5
6
Copy code
# 使用 grep 查找關鍵字,並在發現錯誤時執行相應的操作
if grep -q "error" output.log; then
echo "Error found in output.log"
exit 1
fi

3. 發現錯誤時丟出 exit 1

最後,在檢查完錯誤後,如果發現了問題,我們應該明確地拋出 exit 1,這會通知 CI/CD 停止進一步的運行,
確保不會將有問題的版本部署上線。

1
2
3
4
5
6
Copy code
# 在發現錯誤時,明確地拋出 exit 1
if [ $? -ne 0 ]; then
echo "Error occurred. Exiting with code 1."
exit 1
fi

透過這些步驟,我們可以更好地管理 CI/CD 中的錯誤,確保系統的穩定性和可靠性。
同時,我們能夠更快速地響應和解決問題,減少部署錯誤版本的風險。

1
2
3
4
5
6
7
8
9
10
11
12
generate-qa:
stage: generate-qa
script:
- npm install
- npm run generate:qa | tee output.log # 可能出錯的命令,將輸出寫到文件
- grep -q "Error.*\[500\]" output.log && echo -e "\e[31m生成異常! Prerendering 發生 500 Error\e[0m" && exit 1
- echo "generated successfully"
artifacts:
paths:
- ./.output/public
rules:
- if: '$CI_COMMIT_BRANCH == "qa"'

小結

這些方法不僅有助於提高系統的穩定性和可靠性,還能夠加速問題的識別和解決過程,減少了部署錯誤版本的風險。
透過這些實踐,我們可以更加信心滿滿地運用 Gitlab CI/CD 與 GCP,確保順暢的開發和部署流程。

參考

(fin)

Please enable JavaScript to view the LikeCoin. :P