From 8ae829ad89ca2a2083ed56a0f7a3ba73f79be018 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 26 May 2026 16:54:39 -0500 Subject: [PATCH] feat: add live deploy target with multi-instance support to deploy-module.yml - Add 'live' and 'all' options to server selector - Live deploy reads LIVE_TARGETS JSON secret for multiple production instances - Move dev/demo host config from hardcoded env to vars.* - Add summary step for deploy reporting Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- templates/workflows/deploy-module.yml | 156 +++++++++++++++++++++----- 1 file changed, 126 insertions(+), 30 deletions(-) diff --git a/templates/workflows/deploy-module.yml b/templates/workflows/deploy-module.yml index de941ea..151a62b 100644 --- a/templates/workflows/deploy-module.yml +++ b/templates/workflows/deploy-module.yml @@ -1,4 +1,28 @@ -name: Deploy Dolibarr Module +# Copyright (C) 2026 Moko Consulting +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# FILE INFORMATION +# DEFGROUP: Gitea.Workflow +# INGROUP: moko-platform.Deploy +# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform +# PATH: /templates/workflows/deploy-module.yml +# VERSION: 02.00.00 +# BRIEF: Deploy Dolibarr module to dev, demo, or live environments +# +# Secrets required: +# GA_TOKEN - Gitea API token for repo access +# DEPLOY_SSH_KEY - SSH private key for server access +# LIVE_TARGETS - JSON array of live instances (optional), e.g.: +# [{"host":"client1.example.com","user":"deploy", +# "mods_dir":"/path/MokoDoliMods", +# "custom_dir":"/path/htdocs/custom"}] +# +# Variables required: +# DEV_HOST, DEV_USER, DEV_MODS_DIR, DEV_CUSTOM_DIR +# DEMO_HOST, DEMO_USER, DEMO_MODS_DIR, DEMO_CUSTOM_DIR + +name: "Dolibarr: Deploy Module" on: workflow_dispatch: @@ -7,30 +31,26 @@ on: description: 'Module repo name (e.g. MokoCRM, MokoDoliSign)' required: true server: - description: 'Target server' + description: 'Target environment' required: true default: 'dev' type: choice options: - dev - demo - - both + - live + - dev+demo + - all env: - GITEA_URL: https://git.mokoconsulting.tech - ORG: MokoConsulting - DEV_HOST: waas.dev.mokoconsulting.tech - DEV_USER: mokoconsulting_dev - DEV_MODS_DIR: /home/mokoconsulting_dev/MokoDoliMods - DEV_CUSTOM_DIR: /home/mokoconsulting_dev/crm.dev.mokoconsulting.tech/htdocs/custom - DEMO_HOST: waas.demo.mokoconsulting.tech - DEMO_USER: mokoconsulting_demo - DEMO_MODS_DIR: /home/mokoconsulting_demo/MokoDoliMods - DEMO_CUSTOM_DIR: /home/mokoconsulting_demo/crm.demo.mokoconsulting.tech/htdocs/custom + GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} + ORG: ${{ vars.GITEA_ORG || 'MokoConsulting' }} jobs: deploy: + name: Deploy ${{ inputs.module_repo }} to ${{ inputs.server }} runs-on: ubuntu-latest + steps: - name: Validate module repo run: | @@ -39,11 +59,11 @@ jobs: -H "Authorization: token ${{ secrets.GA_TOKEN }}" \ "${GITEA_URL}/api/v1/repos/${ORG}/${REPO}") if [ "$STATUS" != "200" ]; then - echo "ERROR: ${ORG}/${REPO} not found"; exit 1 + echo "::error::${ORG}/${REPO} not found (HTTP ${STATUS})" + exit 1 fi echo "REPO=${REPO}" >> $GITHUB_ENV - # Derive symlink name LINK_NAME=$(echo "$REPO" | sed 's/MokoDoli//;s/Moko//' | tr '[:upper:]' '[:lower:]') [ "$REPO" = "MokoCRM" ] && LINK_NAME="mokocrm" [ "$REPO" = "MokoDoliProjTemplate" ] && LINK_NAME="mokoprojtemplate" @@ -56,27 +76,27 @@ jobs: "${GITEA_URL}/api/v1/repos/${ORG}/${{ env.REPO }}/tags?limit=1") TAG=$(echo "$TAGS" | jq -r '.[0].name // empty') echo "TAG=${TAG}" >> $GITHUB_ENV - [ -n "$TAG" ] && echo "Deploying: $TAG" || echo "No tags — deploying main" + [ -n "$TAG" ] && echo "Deploying: $TAG" || echo "No tags - deploying main" - name: Deploy to dev - if: inputs.server == 'dev' || inputs.server == 'both' + if: inputs.server == 'dev' || inputs.server == 'dev+demo' || inputs.server == 'all' uses: appleboy/ssh-action@v1 with: - host: ${{ env.DEV_HOST }} - username: ${{ env.DEV_USER }} + host: ${{ vars.DEV_HOST }} + username: ${{ vars.DEV_USER }} key: ${{ secrets.DEPLOY_SSH_KEY }} script: | REPO="${{ env.REPO }}" LINK="${{ env.LINK_NAME }}" TAG="${{ env.TAG }}" - MODS="${{ env.DEV_MODS_DIR }}" - CUSTOM="${{ env.DEV_CUSTOM_DIR }}" + MODS="${{ vars.DEV_MODS_DIR }}" + CUSTOM="${{ vars.DEV_CUSTOM_DIR }}" mkdir -p "$MODS" && cd "$MODS" if [ -d "$REPO" ]; then cd "$REPO" && git fetch --tags origin else - git clone "https://git.mokoconsulting.tech/${{ env.ORG }}/${REPO}.git" + git clone "${{ env.GITEA_URL }}/${{ env.ORG }}/${REPO}.git" cd "$REPO" fi @@ -90,27 +110,27 @@ jobs: cd "$CUSTOM" [ -L "$LINK" ] || [ -d "$LINK" ] && rm -rf "$LINK" ln -sf "$MODS/$REPO/src" "$LINK" - echo "OK: $LINK → $MODS/$REPO/src (${TAG:-main})" + echo "OK: $LINK -> $MODS/$REPO/src (${TAG:-main})" - name: Deploy to demo - if: inputs.server == 'demo' || inputs.server == 'both' + if: inputs.server == 'demo' || inputs.server == 'dev+demo' || inputs.server == 'all' uses: appleboy/ssh-action@v1 with: - host: ${{ env.DEMO_HOST }} - username: ${{ env.DEMO_USER }} + host: ${{ vars.DEMO_HOST }} + username: ${{ vars.DEMO_USER }} key: ${{ secrets.DEPLOY_SSH_KEY }} script: | REPO="${{ env.REPO }}" LINK="${{ env.LINK_NAME }}" TAG="${{ env.TAG }}" - MODS="${{ env.DEMO_MODS_DIR }}" - CUSTOM="${{ env.DEMO_CUSTOM_DIR }}" + MODS="${{ vars.DEMO_MODS_DIR }}" + CUSTOM="${{ vars.DEMO_CUSTOM_DIR }}" mkdir -p "$MODS" && cd "$MODS" if [ -d "$REPO" ]; then cd "$REPO" && git fetch --tags origin else - git clone "https://git.mokoconsulting.tech/${{ env.ORG }}/${REPO}.git" + git clone "${{ env.GITEA_URL }}/${{ env.ORG }}/${REPO}.git" cd "$REPO" fi @@ -124,4 +144,80 @@ jobs: cd "$CUSTOM" [ -L "$LINK" ] || [ -d "$LINK" ] && rm -rf "$LINK" ln -sf "$MODS/$REPO/src" "$LINK" - echo "OK: $LINK → $MODS/$REPO/src (${TAG:-main})" + echo "OK: $LINK -> $MODS/$REPO/src (${TAG:-main})" + + - name: Deploy to live + if: inputs.server == 'live' || inputs.server == 'all' + env: + LIVE_TARGETS: ${{ secrets.LIVE_TARGETS }} + DEPLOY_KEY: ${{ secrets.DEPLOY_SSH_KEY }} + run: | + if [ -z "$LIVE_TARGETS" ] || [ "$LIVE_TARGETS" = "null" ]; then + echo "::error::LIVE_TARGETS secret is not configured." + echo "Set it to a JSON array of target objects." + exit 1 + fi + + COUNT=$(echo "$LIVE_TARGETS" | jq 'length') + echo "Deploying to ${COUNT} live instance(s)..." + + echo "$DEPLOY_KEY" > /tmp/deploy_key + chmod 600 /tmp/deploy_key + + FAILED=0 + for i in $(seq 0 $((COUNT - 1))); do + HOST=$(echo "$LIVE_TARGETS" | jq -r ".[$i].host") + USER=$(echo "$LIVE_TARGETS" | jq -r ".[$i].user") + MODS=$(echo "$LIVE_TARGETS" | jq -r ".[$i].mods_dir") + CUSTOM=$(echo "$LIVE_TARGETS" | jq -r ".[$i].custom_dir") + PORT=$(echo "$LIVE_TARGETS" | jq -r ".[$i].port // 22") + LABEL=$(echo "$LIVE_TARGETS" | jq -r ".[$i].label // empty") + [ -z "$LABEL" ] && LABEL="$HOST" + + echo "" + echo "=== Instance $((i+1))/${COUNT}: ${LABEL} (${USER}@${HOST}:${PORT}) ===" + + ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=15 \ + -i /tmp/deploy_key -p "$PORT" "${USER}@${HOST}" \ + "REPO='${{ env.REPO }}' LINK='${{ env.LINK_NAME }}' TAG='${{ env.TAG }}' MODS='${MODS}' CUSTOM='${CUSTOM}' GITEA_URL='${{ env.GITEA_URL }}' ORG='${{ env.ORG }}' bash" <<'REMOTE_SCRIPT' || { echo "::warning::Failed: ${LABEL}"; FAILED=$((FAILED+1)); continue; } + mkdir -p "$MODS" && cd "$MODS" + if [ -d "$REPO" ]; then + cd "$REPO" && git fetch --tags origin + else + git clone "${GITEA_URL}/${ORG}/${REPO}.git" + cd "$REPO" + fi + + if [ -n "$TAG" ]; then + git checkout "$TAG" --quiet + else + git checkout main --quiet + git pull --ff-only origin main --quiet + fi + + cd "$CUSTOM" + [ -L "$LINK" ] || [ -d "$LINK" ] && rm -rf "$LINK" + ln -sf "$MODS/$REPO/src" "$LINK" + echo "OK: $LINK -> $MODS/$REPO/src (${TAG:-main})" + REMOTE_SCRIPT + done + + rm -f /tmp/deploy_key + + if [ "$FAILED" -gt 0 ]; then + echo "::error::${FAILED} of ${COUNT} live deployment(s) failed" + exit 1 + fi + echo "All ${COUNT} live instance(s) deployed successfully." + + - name: Summary + if: always() + run: | + echo "## Deploy: ${{ env.REPO }} -> ${{ inputs.server }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY + echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY + echo "| Module | \`${{ env.REPO }}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Symlink | \`${{ env.LINK_NAME }}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Version | \`${{ env.TAG || 'main' }}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Target | \`${{ inputs.server }}\` |" >> $GITHUB_STEP_SUMMARY