Skip to content

no message

no message #151

Workflow file for this run

name: CI Build and Push
#
# 必須在 GitHub 專案設定 → Settings → Secrets and variables → Actions 中建立:
# 1. DOCKERHUB_USERNAME:可推送映像到 Docker Hub 的帳號。
# 2. DOCKERHUB_TOKEN:對應帳號的存取 token/密碼。
# 3. GH_PAT:至少擁有 repo 權限的 Personal Access Token,用來更新 GitOps 儲庫。
#
on:
push:
# branches: [ "disable" ]
branches: ["**"]
# 任何分支推送都觸發流程
# 集中管理各微服務目錄與對應的變更偵測
env:
FORCE_BUILD_ALL: "false" # 設為 true 時視為所有服務皆有變動
SERVICE_DIRS: | # 需要監控的微服務資料夾
service/service-test
service/service-template
SERVICE_GLOBS: | # 用於變更偵測的檔案範圍
service/service-test/**
service/service-template/**
jobs:
detect-changes:
runs-on: ubuntu-latest
# 以 Ubuntu Runner 執行變更偵測
outputs:
matrix: ${{ steps.build-matrix.outputs.matrix }}
has_changes: ${{ steps.build-matrix.outputs.has_changes }}
# 將偵測結果輸出給後續 Job 使用
steps:
# 取得完整 Git 歷史作後續比較
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
# 判斷有哪些微服務有變動,後續工作只處理必要項目。
- name: Detect changed microservices
id: changed
uses: tj-actions/changed-files@v45
with:
files: ${{ env.SERVICE_GLOBS }}
# 列出所有變更檔案供除錯參考
- name: Log changed files
if: steps.changed.outputs.all_changed_files != ''
run: |
echo "::group::Changed files"
echo "${{ steps.changed.outputs.all_changed_files }}" | tr ' ' '\n'
echo "::endgroup::"
# 將變動檔案列表轉成矩陣格式,供後續 Job 動態決策。
- name: Build service matrix
id: build-matrix
run: |
set -euo pipefail
# 確保腳本遇到錯誤立即停止
echo "Building matrix from detected changes..."
# 依據設定的服務清單建立陣列
readarray -t services <<< "${SERVICE_DIRS}"
declare -A seen=()
matrix_entries=()
# seen 用來去除重複服務
if [[ "${FORCE_BUILD_ALL}" == "true" ]]; then
echo "::notice::FORCE_BUILD_ALL is true; scheduling all services."
for svc in "${services[@]}"; do
[[ -z "$svc" ]] && continue
if [[ -z ${seen[$svc]:-} ]]; then
seen[$svc]=1
name=$(basename "$svc")
slug=$(echo "$name" | tr '[:upper:]' '[:lower:]')
matrix_entries+=("{\"service\":\"$svc\",\"name\":\"$name\",\"image_slug\":\"$slug\"}")
fi
done
else
# 掃描所有變動檔案並比對服務前綴
while IFS= read -r file; do
for svc in "${services[@]}"; do
[[ -z "$svc" ]] && continue
prefix="$svc/"
if [[ $file == ${prefix}* && -z ${seen[$svc]:-} ]]; then
seen[$svc]=1
name=$(basename "$svc")
slug=$(echo "$name" | tr '[:upper:]' '[:lower:]')
matrix_entries+=("{\"service\":\"$svc\",\"name\":\"$name\",\"image_slug\":\"$slug\"}")
# 收集後續矩陣所需欄位
fi
done
done < <(printf "%s\n%s\n" "${{ steps.changed.outputs.modified_files }}" "${{ steps.changed.outputs.added_files }}" | tr ' ' '\n' | sort -u)
fi
if [ ${#matrix_entries[@]} -eq 0 ]; then
# 無服務變更
echo "has_changes=false" >> "$GITHUB_OUTPUT"
echo 'matrix={"include":[]}' >> "$GITHUB_OUTPUT"
echo "::notice::No microservice changes detected; downstream jobs will be skipped."
else
# 有至少一個服務需要建置
echo "has_changes=true" >> "$GITHUB_OUTPUT"
joined_entries=$(IFS=, ; echo "${matrix_entries[*]}")
matrix_json="{\"include\":[${joined_entries}]}"
echo "matrix=${matrix_json}" >> "$GITHUB_OUTPUT"
echo "Matrix definition: ${matrix_json}"
fi
build-and-push:
needs: detect-changes
if: needs.detect-changes.outputs.has_changes == 'true'
# 僅在前段偵測到變更時才執行
runs-on: ubuntu-latest
# 使用預設 Ubuntu Runner 進行建置
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.detect-changes.outputs.matrix) }}
# 對每個受影響服務個別建置,互不阻塞
env:
TZ: Asia/Taipei
# 固定時區;mvnd native 版本不支援停用 daemon
steps:
# 取得變更服務的原始碼
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
# 準備 JDK,並啟用 Maven 依賴快取
- name: Provision Java toolchain
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '23'
cache: maven
# 快取 mvnd 發行版,避免每次重新下載
- name: Cache mvnd distribution
uses: actions/cache@v4
with:
path: ${{ env.HOME }}/.mvnd
key: mvnd-${{ runner.os }}-2.0.0-rc-3
# 下載並安裝 mvnd,用於加速 Maven 構建
- name: Install mvnd
run: |
set -euo pipefail
# 嚴格模式確保安裝流程安全
MVND_VERSION=2.0.0-rc-3
if [ ! -x "$HOME/.mvnd/bin/mvnd" ]; then
ARCHIVE_BASE="maven-mvnd-${MVND_VERSION}-linux-amd64"
ARCHIVE_FILE="${ARCHIVE_BASE}.tar.gz"
DOWNLOAD_URL="https://downloads.apache.org/maven/mvnd/${MVND_VERSION}/${ARCHIVE_FILE}"
curl -fsSL "$DOWNLOAD_URL" -o mvnd.tar.gz
tar -xzf mvnd.tar.gz
rm -rf "$HOME/.mvnd"
mv "$ARCHIVE_BASE" "$HOME/.mvnd"
rm mvnd.tar.gz
fi
echo "$HOME/.mvnd/bin" >> $GITHUB_PATH
# 僅為目標微服務建置,`-am` 會帶入依賴模組。
# 使用 mvnd 並行打包,提高 CI 構建速度
- name: Build service artifact
env:
IMAGE_TAG: ${{ github.sha }}
run: |
set -euo pipefail
# 確保 mvnd 指令出錯時立即失敗
svc="${{ matrix.service }}"
echo "::group::Building Maven artifacts for ${svc}"
mvnd -B -T 1C -Dmaven.compiler.useIncrementalCompilation=false -DskipTests -Duser.timezone=Asia/Taipei -Dimage.tag=${IMAGE_TAG} -pl "$svc" -am clean package
# -T 1C 會依 CPU 自動分派執行緒
echo "::endgroup::"
# 啟用 QEMU 以支援跨架構建置
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
with:
platforms: linux/arm64
# 建立 Docker Buildx Builder(container driver 支援多架構)
- name: Set up Buildx
uses: docker/setup-buildx-action@v3
with:
install: true
cleanup: false
# 登入 Docker Hub 推送映像
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# 建置並推送映像,使用短 SHA 與 latest 雙標籤。
- name: Build and push image
env:
IMAGE_REPO: ${{ secrets.DOCKERHUB_USERNAME }}
run: |
set -euo pipefail
# Docker 建置失敗時立即終止
svc="${{ matrix.service }}"
name="${{ matrix.name }}"
slug="${{ matrix.image_slug }}"
image="${IMAGE_REPO}/$slug"
short_sha="${GITHUB_SHA::7}"
cache_scope="svc-${slug}"
echo "Building docker image ${image}:${short_sha} from ${svc}"
echo "::group::Docker build logs"
docker buildx build "$svc" \
--file "$svc/Dockerfile" \
--platform linux/amd64,linux/arm64 \
--tag "$image:$short_sha" \
--tag "$image:latest" \
--cache-from "type=gha,scope=${cache_scope}" \
--cache-to "type=gha,scope=${cache_scope},mode=max" \
--push
echo "::endgroup::"
# 拉取 GitOps 倉庫準備更新映像標籤
- name: Checkout GitOps repo
if: github.event_name == 'push'
uses: actions/checkout@v4
with:
repository: ${{ github.repository_owner }}/GitOps
token: ${{ secrets.GH_PAT }}
path: gitops
# 更新對應服務的部署檔,讓 ArgoCD 拉取最新映像
- name: Update image tag in manifests
if: github.event_name == 'push'
id: manifest
working-directory: gitops
env:
IMAGE_REPO: ${{ secrets.DOCKERHUB_USERNAME }}
SERVICE_NAME: ${{ matrix.name }}
IMAGE_SLUG: ${{ matrix.image_slug }}
run: |
set -eo pipefail
# 確保更新 manifest 過程出錯時即中止
short_sha="${GITHUB_SHA::7}"
image="${IMAGE_REPO}/${IMAGE_SLUG}:$short_sha"
deployment_path="k8s/${SERVICE_NAME}/deployment.yaml"
if [ ! -f "$deployment_path" ]; then
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "Missing deployment file for ${SERVICE_NAME}, nothing to update."
exit 0
fi
echo "Updating ${deployment_path} to image ${image}"
sed -i "s|image: .*${IMAGE_SLUG}:.*|image: ${image}|" "$deployment_path"
echo "deployment_path=$deployment_path" >> "$GITHUB_OUTPUT"
echo "skip=false" >> "$GITHUB_OUTPUT"
# 將更新後的 manifests 提交回 GitOps 倉庫
- name: Commit and push manifest changes
if: github.event_name == 'push' && steps.manifest.outputs.skip == 'false'
uses: EndBug/add-and-commit@v9
with:
cwd: gitops
add: ${{ steps.manifest.outputs.deployment_path }}
message: "Update ${{ matrix.name }} image to ${{ secrets.DOCKERHUB_USERNAME }}/${{ matrix.image_slug }}:${{ github.sha }}"
push: true
github_token: ${{ secrets.GH_PAT }}