From d610095b716a0a3b1d5c04bd85986d5fe462d521 Mon Sep 17 00:00:00 2001 From: maltayyar2 Date: Thu, 15 Jan 2026 01:10:38 +0300 Subject: [PATCH] ci: setup odoo 18 scaffolding and workflows --- .github/workflows/delete-merged-branches.yml | 48 +++++++ .github/workflows/deploy-manager.yml | 130 ++++++++++++++++++ .../prevent-invalid-branch-merges.yml | 71 ++++++++++ .github/workflows/pull_code.yml | 35 ----- .github/workflows/upgrade_module.yml | 34 ----- 5 files changed, 249 insertions(+), 69 deletions(-) create mode 100644 .github/workflows/delete-merged-branches.yml create mode 100644 .github/workflows/deploy-manager.yml create mode 100644 .github/workflows/prevent-invalid-branch-merges.yml delete mode 100644 .github/workflows/pull_code.yml delete mode 100644 .github/workflows/upgrade_module.yml diff --git a/.github/workflows/delete-merged-branches.yml b/.github/workflows/delete-merged-branches.yml new file mode 100644 index 0000000..6e860cf --- /dev/null +++ b/.github/workflows/delete-merged-branches.yml @@ -0,0 +1,48 @@ +name: Auto Delete Branch After Merge + +on: + pull_request: + types: [closed] + +jobs: + delete-merged-branch: + if: github.event.pull_request.merged == true + runs-on: ubuntu-latest + steps: + - name: Delete merged branch + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GH_TOKEN }} + script: | + const base = context.payload.pull_request.base.ref; + const head = context.payload.pull_request.head.ref; + const owner = context.repo.owner; + const repo = context.repo.repo; + + core.info(`Check Deletion for: ${head} (merged into ${base})`); + + // Protected Patterns + const protectedPrefixes = ["dev_", "preprod_", "master_", "main"]; + + // Check if head branch is protected + const isProtected = protectedPrefixes.some(prefix => head.startsWith(prefix)); + + if (isProtected) { + core.info(`๐Ÿ›ก๏ธ Branch '${head}' is a protected environment branch. Skipping deletion.`); + return; + } + + // Additional Safety: Don't delete if it's not a standard feature/fix pattern? + // User wants to clean up users branches. + + try { + core.info(`๐Ÿงน Deleting branch: ${head}`); + await github.rest.git.deleteRef({ + owner, + repo, + ref: `heads/${head}` + }); + core.info("โœ… Branch deleted successfully."); + } catch (error) { + core.warning(`Failed to delete branch: ${error.message}`); + } \ No newline at end of file diff --git a/.github/workflows/deploy-manager.yml b/.github/workflows/deploy-manager.yml new file mode 100644 index 0000000..d06129c --- /dev/null +++ b/.github/workflows/deploy-manager.yml @@ -0,0 +1,130 @@ +name: Hydra Deployment Manager (Odoo 18) + +on: + push: + branches: + - 'dev_**' + - 'preprod_**' + # - 'master_**' + + workflow_dispatch: + inputs: + target_env: + description: 'Target Environment' + required: true + type: choice + options: + - dev + - preprod + - prod + default: 'dev' + force_restart: + description: 'Force Service Restart' + required: true + type: boolean + default: true + +jobs: + deploy: + runs-on: ubuntu-latest + name: Deploy + steps: + - name: Extract Context + id: context + run: | + REF_NAME=${{ github.ref_name }} + EVENT_NAME=${{ github.event_name }} + INPUT_ENV=${{ inputs.target_env }} + + echo "Processing Event: $EVENT_NAME on Ref: $REF_NAME" + + ENV="" + MODULE="" + PORT="" + + # Logic: + if [ "$EVENT_NAME" == "push" ]; then + if [[ "$REF_NAME" == dev_* ]]; then + ENV="dev" + MODULE=${REF_NAME#dev_} + elif [[ "$REF_NAME" == preprod_* ]]; then + ENV="preprod" + MODULE=${REF_NAME#preprod_} + fi + + elif [ "$EVENT_NAME" == "workflow_dispatch" ]; then + ENV="$INPUT_ENV" + if [[ "$REF_NAME" == dev_* ]]; then + MODULE=${REF_NAME#dev_} + elif [[ "$REF_NAME" == preprod_* ]]; then + MODULE=${REF_NAME#preprod_} + elif [[ "$REF_NAME" == master_* ]]; then + MODULE=${REF_NAME#master_} + else + # Fallback assuming valid module name in branch suffix + MODULE=${REF_NAME#*_} + fi + fi + + # ODOO 18 PORT CONFIGURATION + if [ "$ENV" == "dev" ]; then + PORT="18000" + elif [ "$ENV" == "preprod" ]; then + PORT="18010" + elif [ "$ENV" == "prod" ]; then + PORT="18069" + fi + + echo "ENV=$ENV" >> $GITHUB_OUTPUT + echo "MODULE=$MODULE" >> $GITHUB_OUTPUT + echo "PORT=$PORT" >> $GITHUB_OUTPUT + + - name: Deploy to Hydra Server (SSH) + if: steps.context.outputs.ENV != '' + uses: appleboy/ssh-action@v1.0.0 + with: + host: ${{ secrets.HYDRA_HOST }} + username: ${{ secrets.HYDRA_USER }} + key: ${{ secrets.HYDRA_SSH_KEY }} + port: 22 + script: | + MODULE="${{ steps.context.outputs.MODULE }}" + ENV="${{ steps.context.outputs.ENV }}" + BRANCH="${{ github.ref_name }}" + FORCE_RESTART="${{ inputs.force_restart }}" + + echo "๐Ÿš€ Deploying (Odoo 18) $MODULE to $ENV Environment..." + + if [ "$ENV" == "dev" ]; then + TARGET_ROOT="/root/odoo-infra/odoo18/addons/custom_modules" + SERVICE="odoo-18" + else + echo "๐Ÿšง Environment '$ENV' folder structure is not yet created on the server for Odoo 18. Deployment simulated." + exit 0 + fi + + TARGET_DIR="$TARGET_ROOT/$MODULE" + + # Auto-create directory if it doesn't exist (First run) + if [ ! -d "$TARGET_DIR" ]; then + echo "โœจ Creating new module directory: $TARGET_DIR" + mkdir -p "$TARGET_DIR" + # Initial clone logic handled by script usually, but here we can just git init if empty + # Better to assume the setup script handles it, or handle it here: + cd "$TARGET_DIR" + git init + git remote add origin git@github.com:hydracp9/odex30_standard.git + fi + + cd "$TARGET_DIR" + echo "โฌ‡๏ธ Pulling changes from $BRANCH..." + git fetch origin + git reset --hard origin/$BRANCH + + if [ "$FORCE_RESTART" == "true" ] || [ "${{ github.event_name }}" == "push" ]; then + echo "๐Ÿ”„ Restarting Service ($SERVICE)..." + cd /root/odoo-infra + docker compose restart $SERVICE + fi + + echo "โœ… Deployment Successful." diff --git a/.github/workflows/prevent-invalid-branch-merges.yml b/.github/workflows/prevent-invalid-branch-merges.yml new file mode 100644 index 0000000..d030937 --- /dev/null +++ b/.github/workflows/prevent-invalid-branch-merges.yml @@ -0,0 +1,71 @@ +name: Prevent Invalid Merges + +on: + pull_request: + types: [opened, reopened, synchronize, edited] + +jobs: + validate-merge-flow: + runs-on: ubuntu-latest + steps: + - name: Validate Branch Flow + uses: actions/github-script@v7 + with: + script: | + const base = context.payload.pull_request.base.ref; + const head = context.payload.pull_request.head.ref; + + core.info(`Checking Merge Flow: ${head} -> ${base}`); + + // 1. Parse module name from base branch + // Expected formats: dev_X, preprod_X, master_X + const tiers = ["dev", "preprod", "master"]; + + function parseBranch(branchName) { + for (const tier of tiers) { + if (branchName.startsWith(tier + "_")) { + return { tier: tier, module: branchName.substring(tier.length + 1) }; + } + } + return null; // Not a standard environment branch (maybe feature/fix) + } + + const baseInfo = parseBranch(base); + const headInfo = parseBranch(head); + + // If base is not a protected tier (dev/preprod/master), allow merge (feature -> feature) + if (!baseInfo) { + core.info("Base branch is not a protected environment tier. Merge allowed."); + return; + } + + // Logic for Protected Base Branches + + // โŒ Rule: Cannot merge directly into master from anywhere except preprod (of same module) + if (baseInfo.tier === "master") { + if (!headInfo || headInfo.tier !== "preprod" || headInfo.module !== baseInfo.module) { + core.setFailed(`โŒ Forbidden: You can ONLY merge into 'master_${baseInfo.module}' from 'preprod_${baseInfo.module}'. Detected: ${head}`); + return; + } + } + + // โŒ Rule: Cannot merge directly into preprod from anywhere except dev (of same module) + if (baseInfo.tier === "preprod") { + if (!headInfo || headInfo.tier !== "dev" || headInfo.module !== baseInfo.module) { + core.setFailed(`โŒ Forbidden: You can ONLY merge into 'preprod_${baseInfo.module}' from 'dev_${baseInfo.module}'. Detected: ${head}`); + return; + } + } + + // โŒ Rule: Cannot merge directly into dev from master or preprod (reverse flow) + // (Optional: You might allow hotfixes, but strictly strictly dev<-feature is best) + if (baseInfo.tier === "dev") { + // Allow feature branches to merge into dev + // Block upstream branches + if (headInfo && (headInfo.tier === "master" || headInfo.tier === "preprod")) { + core.setFailed(`โŒ Forbidden: Cannot merge upstream (${head}) back into dev.`); + return; + } + } + + core.info("โœ… Merge flow validation passed."); diff --git a/.github/workflows/pull_code.yml b/.github/workflows/pull_code.yml deleted file mode 100644 index 613d70d..0000000 --- a/.github/workflows/pull_code.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Pull Code - -on: - push: - branches: - - dev_odex_base - - dev_odex_hr - - dev_odex30_accounting - - workflow_dispatch: - inputs: - environment: - description: 'Select Server' - required: true - type: choice - options: - - dev - default: dev - -jobs: - - deploy_dev_server: - name: Deploy to dev server <159.89.22.77> - runs-on: odex30-runner - if: | - (github.ref == 'refs/heads/dev_odex_base' || github.ref == 'refs/heads/dev_odex_hr' || github.ref == 'refs/heads/dev_odex30_accounting') && - (github.event_name == 'push' || - (github.event_name == 'workflow_dispatch' && github.event.inputs.environment != '')) - - steps: - - name: Checkout And Restart Project - run: | - echo "** [INFO] Running on branch --> ${GITHUB_REF#refs/heads/}" - sudo chmod +x /home/${{ secrets.CLIENT_USER }}/scripts/pull/pull_code.sh - sudo /home/${{ secrets.CLIENT_USER }}/scripts/pull/pull_code.sh diff --git a/.github/workflows/upgrade_module.yml b/.github/workflows/upgrade_module.yml deleted file mode 100644 index 10e92bc..0000000 --- a/.github/workflows/upgrade_module.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: Upgrade Module - -on: - workflow_dispatch: - inputs: - database_name: - description: 'Database Name' - required: true - type: string - module_name: - description: 'Module Name' - required: true - type: string - environment: - description: 'Select Server' - required: true - type: choice - options: - - dev - default: dev - -jobs: - upgrade_master: - name: Upgrade Dev server - runs-on: odex30-runner - if: github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'dev' - steps: - - name: Upgrade Module - env: - DATABASE_NAME: ${{ github.event.inputs.database_name }} - MODULE_NAME: ${{ github.event.inputs.module_name }} - run: | - chmod +x /home/${{ secrets.CLIENT_USER }}/scripts/upgrade/upgrade-module.sh - /home/${{ secrets.CLIENT_USER }}/scripts/upgrade/upgrade-module.sh "$DATABASE_NAME" "$MODULE_NAME"