name: TakeoutSaaS CI/CD on: push: branches: - main - dev pull_request: branches: - main workflow_dispatch: env: REGISTRY: ${{ secrets.REGISTRY }} REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }} REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }} DEPLOY_USER: ${{ secrets.DEPLOY_USER }} DEPLOY_PASSWORD: ${{ secrets.DEPLOY_PASSWORD }} REGISTRY_NAMESPACE: kjkj-saas jobs: detect: runs-on: ubuntu-latest outputs: services: ${{ steps.collect.outputs.services }} image_tag: ${{ steps.collect.outputs.image_tag }} steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - id: collect shell: bash run: | set -euo pipefail BASE="${{ github.event.before }}" if [ -z "$BASE" ] || [ "$BASE" = "0000000000000000000000000000000000000000" ]; then if git rev-parse HEAD^ >/dev/null 2>&1; then BASE="$(git rev-parse HEAD^)" else BASE="" fi fi if [ -z "$BASE" ]; then CHANGED=$(git ls-tree -r --name-only HEAD) else CHANGED=$(git diff --name-only "$BASE" HEAD || true) fi echo "本次变更文件:" echo "$CHANGED" deploy_all=false services=() hit() { echo "$CHANGED" | grep -qE "$1"; } if hit '^src/(Domain|Application|Infrastructure|Core|Modules)/'; then deploy_all=true; fi if hit '^Directory\.Build\.props$'; then deploy_all=true; fi if hit '^src/Api/TakeoutSaaS.AdminApi/'; then services+=("admin-api"); fi if hit '^src/Api/TakeoutSaaS.MiniApi/'; then services+=("mini-api"); fi if hit '^src/Api/TakeoutSaaS.UserApi/'; then services+=("user-api"); fi if $deploy_all || [ ${#services[@]} -eq 0 ]; then services=("admin-api" "mini-api" "user-api") fi printf '需要处理的服务: %s\n' "${services[*]}" SERVICES_LIST="${services[*]}" export SERVICES_LIST SERVICES_JSON=$(python -c "import json, os; print(json.dumps(os.environ.get('SERVICES_LIST','').split()))") echo "services=$SERVICES_JSON" >> "$GITHUB_OUTPUT" TAG=$(date +%Y%m%d%H%M%S) echo "image_tag=$TAG" >> "$GITHUB_OUTPUT" build: runs-on: ubuntu-latest needs: detect if: needs.detect.outputs.services != '[]' strategy: matrix: service: ${{ fromJson(needs.detect.outputs.services) }} steps: - uses: actions/checkout@v4 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ env.REGISTRY_USERNAME }} password: ${{ env.REGISTRY_PASSWORD }} - name: Build and push ${{ matrix.service }} env: SERVICE: ${{ matrix.service }} IMAGE_TAG: ${{ needs.detect.outputs.image_tag }} run: | set -euo pipefail case "$SERVICE" in admin-api) DOCKERFILE="src/Api/TakeoutSaaS.AdminApi/Dockerfile" IMAGE="$REGISTRY/$REGISTRY_NAMESPACE/admin-api:$IMAGE_TAG" ;; mini-api) DOCKERFILE="src/Api/TakeoutSaaS.MiniApi/Dockerfile" IMAGE="$REGISTRY/$REGISTRY_NAMESPACE/mini-api:$IMAGE_TAG" ;; user-api) DOCKERFILE="src/Api/TakeoutSaaS.UserApi/Dockerfile" IMAGE="$REGISTRY/$REGISTRY_NAMESPACE/user-api:$IMAGE_TAG" ;; *) echo "未知服务:$SERVICE" exit 1 ;; esac if [ ! -f "$DOCKERFILE" ]; then echo "未找到 Dockerfile: $DOCKERFILE" exit 1 fi docker build -f "$DOCKERFILE" -t "$IMAGE" . docker push "$IMAGE" deploy: runs-on: ubuntu-latest needs: - detect - build if: needs.detect.outputs.services != '[]' strategy: matrix: service: ${{ fromJson(needs.detect.outputs.services) }} steps: - name: Install sshpass run: sudo apt-get update && sudo apt-get install -y sshpass - name: Deploy ${{ matrix.service }} env: SERVICE: ${{ matrix.service }} IMAGE_TAG: ${{ needs.detect.outputs.image_tag }} run: | set -euo pipefail case "$SERVICE" in admin-api) IMAGE="$REGISTRY/$REGISTRY_NAMESPACE/admin-api:$IMAGE_TAG" PORT=7801 ;; mini-api) IMAGE="$REGISTRY/$REGISTRY_NAMESPACE/mini-api:$IMAGE_TAG" PORT=7701 ;; user-api) IMAGE="$REGISTRY/$REGISTRY_NAMESPACE/user-api:$IMAGE_TAG" PORT=7901 ;; *) echo "未知服务:$SERVICE" exit 1 ;; esac sshpass -p "$DEPLOY_PASSWORD" ssh -o StrictHostKeyChecking=no "$DEPLOY_USER@$DEPLOY_HOST" " set -e echo \"$REGISTRY_PASSWORD\" | docker login \"$REGISTRY\" -u \"$REGISTRY_USERNAME\" --password-stdin docker pull $IMAGE docker stop $SERVICE 2>/dev/null || true docker rm $SERVICE 2>/dev/null || true docker run -d --name $SERVICE --restart=always -p $PORT:$PORT $IMAGE "