CD: uat.yml ไม่ build image เอง reuse ของ build.yml + ขึ้น UAT ผ่าน Forgejo UI ได้ — ลบ job build ทั้ง job (เดิม build ซ้ำทุกครั้ง) แทนด้วย steps ใหม่ใน deploy job: Resolve image tag map deploy1.2.3/1.2.3/v1.2.3 → v1.2.3 เสมอ (ตัด prefix deploy ตัด v เติม v กลับ — ใช้ image เดียวกับที่ build.yml push ตอน tag v*) + login registry + docker manifest inspect เช็ค image มีจริงก่อน SSH (fail-fast พร้อมข้อความบอกให้ push tag v* ก่อน ถ้ายังไม่มี) + workflow_dispatch เพิ่ม input version (required) — กด Run workflow ใน UI ใส่เวอร์ชันได้เลยไม่ต้องสร้าง tag deploy* (ทางเดิมยังใช้ได้) — deploy job เปิด if ให้รันกรณ้ dispatch (always() && !failure() && !cancelled() — ผ่านเมื่อ validate-schema success หรือ skip แต่ยัง block ถ้า fail เหมือนเดิม) + notify ถอด needs.build ที่ลบไป + field Tag โชว์เวอร์ชันจาก input ตอน dispatch แทนชื่อ branch + field Image Tag ดึงจาก output ของ deploy job — กติกาคงเดิม: image เกิดจาก build.yml ตอน push tag v* (deploy Test ไปด้วย) เท่านั้น UAT แค่ reuse — verify: js-yaml parse ผ่าน + ทดสอบ normalize ครบ 3 รูปแบบ (deploy1.2.3/v1.2.3/1.2.3 → v1.2.3)

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
Your Name 2026-09-17 16:56:38 +07:00
parent fa880b314e
commit 8ba911a119

View file

@ -5,6 +5,10 @@ on:
tags: tags:
- "deploy*.*.*" - "deploy*.*.*"
workflow_dispatch: workflow_dispatch:
inputs:
version:
description: "เวอร์ชันที่จะ deploy เช่น 1.2.3 / v1.2.3 / deploy1.2.3 — จะ map เป็น image v1.2.3 (ต้อง push tag v1.2.3 ให้ build.yml build ไว้ก่อน)"
required: true
env: env:
# Container Registry # Container Registry
@ -12,11 +16,11 @@ env:
REGISTRY_USERNAME: ${{ vars.CONTAINER_REGISTRY_USERNAME }} REGISTRY_USERNAME: ${{ vars.CONTAINER_REGISTRY_USERNAME }}
REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }}
# Docker Image # Docker Image — ไม่ build เอง ใช้ image เดียวกับที่ build.yml push ไว้
# (push tag deploy1.2.3 หรือ Run workflow จาก UI ใส่เวอร์ชัน → map เป็น image v1.2.3 เสมอ)
WEB_IMAGE_NAME: chamomind/ecourt-note WEB_IMAGE_NAME: chamomind/ecourt-note
IMAGE_TAG: ${{ github.ref_name }}
# SSH Deployment — ต่างจาก build.yml ตัวเดียว: ชี้เครื่อง UAT (ตั้ง vars.SSH_UAT_DEPLOY_HOST เพิ่มใน Forgejo) # SSH Deployment — ต่างจาก build.yml: ชี้เครื่อง UAT (ตั้ง vars.SSH_UAT_DEPLOY_HOST เพิ่มใน Forgejo) + ไม่ build image เอง
DEPLOY_HOST: ${{ vars.SSH_UAT_DEPLOY_HOST }} DEPLOY_HOST: ${{ vars.SSH_UAT_DEPLOY_HOST }}
DEPLOY_USER: ${{ vars.SSH_DEPLOY_USER }} DEPLOY_USER: ${{ vars.SSH_DEPLOY_USER }}
@ -85,14 +89,28 @@ jobs:
echo "" echo ""
echo "📚 Reference: README.md / docs/PLAN.md §3" echo "📚 Reference: README.md / docs/PLAN.md §3"
build: deploy:
name: Build Web Docker Image name: Deploy eCourt Note Web to UAT Server
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: validate-schema needs: validate-schema
if: startsWith(github.ref, 'refs/tags/') # รันได้ 2 ทาง: push tag deploy*.*.* หรือกด Run workflow จาก UI (ใส่เวอร์ชันใน input)
# !failure() && !cancelled() = ผ่านได้เมื่อ validate-schema success หรือถูก skip (กรณี dispatch)
# แต่ยังไม่รันถ้า validate-schema fail (tag ไม่อยู่บน dev)
if: >-
always() && !failure() && !cancelled() &&
(startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch')
outputs:
image_tag: ${{ steps.image-tag.outputs.tag }}
steps: steps:
- name: Checkout code - name: Resolve image tag (deploy1.2.3 / v1.2.3 → v1.2.3)
uses: actions/checkout@v4 id: image-tag
run: |
RAW="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version || github.ref_name }}"
TAG="${RAW#deploy}"
TAG="${TAG#v}"
TAG="v$TAG"
echo "tag=$TAG" >> $GITHUB_OUTPUT
echo "Reuse image จาก build.yml: ${{ env.REGISTRY_URL }}/${{ env.WEB_IMAGE_NAME }}:$TAG"
- name: Log in to Container Registry - name: Log in to Container Registry
uses: docker/login-action@v3 uses: docker/login-action@v3
@ -101,39 +119,15 @@ jobs:
username: ${{ env.REGISTRY_USERNAME }} username: ${{ env.REGISTRY_USERNAME }}
password: ${{ env.REGISTRY_PASSWORD }} password: ${{ env.REGISTRY_PASSWORD }}
- name: Build Docker image (no buildx) - name: Verify image exists in registry
run: | run: |
# Build using standard docker build (not buildx) if ! docker manifest inspect "${{ env.REGISTRY_URL }}/${{ env.WEB_IMAGE_NAME }}:${{ steps.image-tag.outputs.tag }}" > /dev/null; then
git reset --hard HEAD echo "❌ Image ${{ env.REGISTRY_URL }}/${{ env.WEB_IMAGE_NAME }}:${{ steps.image-tag.outputs.tag }} ไม่มีใน registry"
cd ${{ github.workspace }} echo "workflow นี้ไม่ build เอง — push tag ${{ steps.image-tag.outputs.tag }} ก่อน เพื่อให้ build.yml build + push image ให้"
docker build \ exit 1
--no-cache \ fi
--pull \ echo "✅ Image found — deploy ด้วย image เดียวกับ build.yml"
--file ./docker/web.Dockerfile \
--tag ${{ env.REGISTRY_URL }}/${{ env.WEB_IMAGE_NAME }}:${{ env.IMAGE_TAG }} \
--tag ${{ env.REGISTRY_URL }}/${{ env.WEB_IMAGE_NAME }}:latest \
.
echo "✅ Build successful!"
docker images | grep ${{ env.WEB_IMAGE_NAME }}
- name: Push Docker image
run: |
docker push ${{ env.REGISTRY_URL }}/${{ env.WEB_IMAGE_NAME }}:${{ env.IMAGE_TAG }}
docker push ${{ env.REGISTRY_URL }}/${{ env.WEB_IMAGE_NAME }}:latest
echo "✅ Image pushed to registry"
- name: Test image can run
run: |
# Quick test that image can start
docker run --rm ${{ env.REGISTRY_URL }}/${{ env.WEB_IMAGE_NAME }}:${{ env.IMAGE_TAG }} node --version
deploy:
name: Deploy eCourt Note Web to UAT Server
runs-on: ubuntu-latest
needs: build
if: startsWith(github.ref, 'refs/tags/')
steps:
- name: Deploy web on UAT server - name: Deploy web on UAT server
uses: appleboy/ssh-action@v1.2.1 uses: appleboy/ssh-action@v1.2.1
with: with:
@ -145,13 +139,13 @@ jobs:
cd ~/repo cd ~/repo
chmod +x ./replace-env.sh ./deploy.sh chmod +x ./replace-env.sh ./deploy.sh
./replace-env.sh ECOURT_NOTE_TAG "${{ env.IMAGE_TAG }}" ./replace-env.sh ECOURT_NOTE_TAG "${{ steps.image-tag.outputs.tag }}"
./deploy.sh ./deploy.sh
notify: notify:
name: Notify Deployment Status name: Notify Deployment Status
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [build, deploy] needs: [deploy]
if: always() if: always()
steps: steps:
- name: Send Discord notification - Success - name: Send Discord notification - Success
@ -171,7 +165,7 @@ jobs:
}, },
{ {
"name": "Tag", "name": "Tag",
"value": "${{ github.ref_name }}", "value": "${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version || github.ref_name }}",
"inline": true "inline": true
}, },
{ {
@ -181,7 +175,7 @@ jobs:
}, },
{ {
"name": "Image Tag", "name": "Image Tag",
"value": "`${{ env.IMAGE_TAG }}`", "value": "`${{ needs.deploy.outputs.image_tag }}`",
"inline": true "inline": true
}, },
{ {
@ -204,7 +198,7 @@ jobs:
${{ env.DISCORD_WEBHOOK_URL }} ${{ env.DISCORD_WEBHOOK_URL }}
- name: Send Discord notification - Failure - name: Send Discord notification - Failure
if: ${{ needs.deploy.result == 'failure' || needs.build.result == 'failure' }} if: ${{ needs.deploy.result == 'failure' }}
run: | run: |
curl -H "Content-Type: application/json" \ curl -H "Content-Type: application/json" \
-d '{ -d '{
@ -230,7 +224,7 @@ jobs:
}, },
{ {
"name": "Failed Job", "name": "Failed Job",
"value": "${{ needs.build.result == 'failure' && 'Build' || 'Deploy' }}", "value": "Deploy",
"inline": true "inline": true
}, },
{ {