隨著專案逐漸成形,我們需要執行的指令也越來越多:編譯、執行、測試、程式碼檢查...。
如果每位開發者都手動輸入這些指令,不僅繁瑣,而且容易因參數不同而導致結果不一致。
為了解決這個問題,我們引入一個古老而強大的工具:make
和 Makefile
。
Makefile
就如同專案的「命令中心」,它為所有常見任務提供了一個統一、簡潔的入口。開發者不再需要記住複雜的指令,只需執行 make build
或 make test
即可。
Makefile
的核心語法非常簡單,由一系列的「規則」組成:
<target>: <prerequisites>
<TAB><command>
build
。⚠️ 極其重要:command
行的開頭必須是一個 Tab 字元,而不是四個或八個空格。這是 Makefile
最容易出錯的地方。
在大多數 Unix-like 系統(如 Linux 和 macOS)中,make
通常已預裝。你可以在終端機中輸入以下指令來確認:
make --version
如果顯示版本資訊,表示 make
已安裝。如果沒有,請根據你的作業系統安裝 make
。
在你的專案根目錄下,建立一個名為 Makefile
的檔案。我們將逐步填充內容。
為了讓 Makefile
更易於維護,我們會先定義一些變數。
這裡我們會讀取 .env 檔案中的變數,並在 Makefile
中使用。
這樣就不用重複定義相同的變數,確保一致性。
# 讀取 .env 檔案
include .env
# 定義變數
# ===================================================================================
# Variables
# ===================================================================================
pes_parent_dir:=$(shell pwd)/$(lastword $(MAKEFILE_LIST))
pes_parent_dir:=$(shell dirname $(pes_parent_dir))
DockerImageNameMigrate='migrate/migrate:v4.19.0'
MigrationFilePath=$(pes_parent_dir)/deployments/migrations
LocalDatabase='mysql://$(MYSQL_USER):$(MYSQL_PASSWORD)@tcp($(MYSQL_HOST):$(MYSQL_PORT))/$(MYSQL_DATABASE)'
接下來,我們會撰寫一些常用的指令,例如 run
等等。
# ==============================================================================
# Development
# ==============================================================================
.PHONY: run
run: ## Run the application
@go run ./cmd/api/main.go
JWT (JSON Web Token) 的生成與解析
內容,我們會建立以下的 Makefile
:# ==============================================================================
# JWT Secret
# ==============================================================================
.PHONY: genJWTSecretToEnv
genJWTSecretToEnv: ## Generate a new JWT secret key and append it to the .env file
@openssl ecparam -genkey -name secp521r1 -noout | tee -a | awk '{printf "%s""\\n",$$0}' | rev | cut -c3- | rev | awk '{printf "\nTOKEN_SECRET=\"%s\"\n",$$0}' >> .env
資料庫遷移 (Migration)
內容,我們會建立以下的 Makefile
:# ==============================================================================
# Database
# ==============================================================================
.PHONY: databaseMigrateCreate databaseMigrateUp databaseMigrateRollback
databaseMigrateCreate: ## Create a new database migration file. Usage: make databaseMigrateCreate name="migration_name"
ifndef name
$(error name is undefined. Usage: make databaseMigrateCreate name="migration_name")
endif
@mkdir -p $(MigrationFilePath)
@docker run --rm -v $(MigrationFilePath):/migrations --network host $(DockerImageNameMigrate) create -seq -ext sql -dir /migrations $(name)
databaseMigrateUp: ## Migrate database to the latest version
@docker run --rm -v $(MigrationFilePath):/migrations --network host $(DockerImageNameMigrate) -path=/migrations/ -database $(LocalDatabase) up
databaseMigrateRollback: ## Rollback database by one version
@docker run --rm -v $(MigrationFilePath):/migrations --network host $(DockerImageNameMigrate) -path=/migrations/ -database $(LocalDatabase) down 1
@
符號:加在指令前,表示在執行時不要將該行指令本身印出來,讓輸出更乾淨。.PHONY
:宣告這些目標不是實際的檔案名稱,避免與檔案名稱衝突。help
指令一個優秀的 Makefile
應該是自解釋的。我們可以寫一個 help
target,它會自動掃描 Makefile
中所有包含 ##
註解的行,並將它們格式化輸出。
# ==============================================================================
# Help
# ==============================================================================
.PHONY: help
help: ## Show this help message
@echo "Usage: make <target>"
@echo ""
@echo "Targets:"
@awk 'BEGIN {FS = ":.*?## " } /^[a-zA-Z_-]+:.*?## / {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
現在,只要在終端機執行 make help
,你就能看到一份格式精美、包含所有指令說明的列表。
$ make help
Usage: make <target>
Targets:
help Show this help message
run Run the application
databaseMigrateCreate Create a new database migration file. Usage: make databaseMigrateCreate name="migration_name"
databaseMigrateUp Migrate database to the latest version
databaseMigrateRollback Rollback database by one version
genJWTSecretToEnv Generate a new JWT secret key and append it to the .env file
在 Makefile
中,我們可以指定一個預設目標,當使用者執行 make
而不帶任何參數時,將會執行這個目標。通常,我們會將 help
目標設為預設目標,這樣使用者可以快速查看所有可用的指令。
# ==============================================================================
# Default
# ==============================================================================
.DEFAULT_GOAL := help
我們已經成功建立了一個功能完備的 Makefile
。它不僅簡化了我們的日常工作,更重要的是,它為團隊提供了一套標準化的工作流程,大大提升了開發體驗和效率。
從現在開始,無論是新加入的成員還是資深的開發者,都可以透過 make
這一個簡單的入口,來執行專案的所有關鍵任務。
本文添加的完整內容可以到 Github 觀看