From 0e7200f1b8620eb744d910d3decde231f08d777d Mon Sep 17 00:00:00 2001 From: defiQUG Date: Wed, 22 Apr 2026 21:47:57 -0700 Subject: [PATCH] ci: lock deploy workflows across main and master --- .gitea/CONTRIBUTING.md | 4 + .gitea/workflow-sources/deploy-to-phoenix.yml | 77 +++++++++++++++++++ .gitea/workflow-sources/validate-on-pr.yml | 21 +++++ .gitea/workflows/deploy-to-phoenix.yml | 17 +--- .gitea/workflows/validate-on-pr.yml | 12 +-- .../DEVIN_GITEA_PROXMOX_CICD.md | 20 +++++ .../check-gitea-branch-workflow-parity.sh | 51 ++++++++++++ scripts/verify/check-gitea-workflows.sh | 32 ++++++++ scripts/verify/run-all-validation.sh | 21 +++-- scripts/verify/sync-gitea-workflows.sh | 18 +++++ 10 files changed, 245 insertions(+), 28 deletions(-) create mode 100644 .gitea/workflow-sources/deploy-to-phoenix.yml create mode 100644 .gitea/workflow-sources/validate-on-pr.yml create mode 100644 scripts/verify/check-gitea-branch-workflow-parity.sh create mode 100644 scripts/verify/check-gitea-workflows.sh create mode 100644 scripts/verify/sync-gitea-workflows.sh diff --git a/.gitea/CONTRIBUTING.md b/.gitea/CONTRIBUTING.md index 4891fb1a..a98958c6 100644 --- a/.gitea/CONTRIBUTING.md +++ b/.gitea/CONTRIBUTING.md @@ -6,6 +6,10 @@ 2. Make changes, ensure tests pass 3. Open a pull request +Deploy workflow policy: +`main` and `master` are both deploy-triggering branches, so `.gitea/workflow-sources/deploy-to-phoenix.yml` and `.gitea/workflow-sources/validate-on-pr.yml` must stay identical across both branches. +Use `bash scripts/verify/sync-gitea-workflows.sh` after editing workflow-source files, and `bash scripts/verify/run-all-validation.sh --skip-genesis` to catch workflow drift before push. + ## Pull Requests - Use the PR template when opening a PR diff --git a/.gitea/workflow-sources/deploy-to-phoenix.yml b/.gitea/workflow-sources/deploy-to-phoenix.yml new file mode 100644 index 00000000..21ea365b --- /dev/null +++ b/.gitea/workflow-sources/deploy-to-phoenix.yml @@ -0,0 +1,77 @@ +# Canonical deploy workflow. Keep source and checked-in workflow copies byte-identical. +# Validation checks both file sync and main/master parity. +name: Deploy to Phoenix + +on: + push: + branches: [main, master] + workflow_dispatch: + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Fetch deploy branches for workflow parity check + run: | + git fetch --depth=1 origin main master + + - name: Run repo validation gate + run: | + bash scripts/verify/run-all-validation.sh --skip-genesis + + deploy: + needs: validate + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Trigger Phoenix deployment + run: | + SHA="$(git rev-parse HEAD)" + BRANCH="$(git rev-parse --abbrev-ref HEAD)" + curl -sSf -X POST "${{ secrets.PHOENIX_DEPLOY_URL }}" \ + -H "Authorization: Bearer ${{ secrets.PHOENIX_DEPLOY_TOKEN }}" \ + -H "Content-Type: application/json" \ + -d "{\"repo\":\"${{ gitea.repository }}\",\"sha\":\"${SHA}\",\"branch\":\"${BRANCH}\",\"target\":\"default\"}" + + deploy-atomic-swap-dapp: + needs: validate + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Trigger Atomic Swap dApp deployment (Phoenix) + run: | + SHA="$(git rev-parse HEAD)" + BRANCH="$(git rev-parse --abbrev-ref HEAD)" + curl -sSf -X POST "${{ secrets.PHOENIX_DEPLOY_URL }}" \ + -H "Authorization: Bearer ${{ secrets.PHOENIX_DEPLOY_TOKEN }}" \ + -H "Content-Type: application/json" \ + -d "{\"repo\":\"${{ gitea.repository }}\",\"sha\":\"${SHA}\",\"branch\":\"${BRANCH}\",\"target\":\"atomic-swap-dapp-live\"}" + + # After app deploy, ask Phoenix to run path-gated Cloudflare DNS sync on the host that has + # PHOENIX_REPO_ROOT + .env (not on this runner). Skips unless PHOENIX_CLOUDFLARE_SYNC=1 on that host. + # continue-on-error: first-time or missing opt-in should not block the main deploy. + cloudflare: + needs: + - deploy + - deploy-atomic-swap-dapp + runs-on: ubuntu-latest + continue-on-error: true + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Request Cloudflare DNS sync (Phoenix) + run: | + SHA="$(git rev-parse HEAD)" + BRANCH="$(git rev-parse --abbrev-ref HEAD)" + curl -sSf -X POST "${{ secrets.PHOENIX_DEPLOY_URL }}" \ + -H "Authorization: Bearer ${{ secrets.PHOENIX_DEPLOY_TOKEN }}" \ + -H "Content-Type: application/json" \ + -d "{\"repo\":\"${{ gitea.repository }}\",\"sha\":\"${SHA}\",\"branch\":\"${BRANCH}\",\"target\":\"cloudflare-sync\"}" diff --git a/.gitea/workflow-sources/validate-on-pr.yml b/.gitea/workflow-sources/validate-on-pr.yml new file mode 100644 index 00000000..92bb2bde --- /dev/null +++ b/.gitea/workflow-sources/validate-on-pr.yml @@ -0,0 +1,21 @@ +# Canonical PR validation workflow. Keep source and checked-in workflow copies byte-identical. +# Validation checks both file sync and main/master parity. +# PR-only: push validation already runs in deploy-to-phoenix.yml; this gives PRs the same +# no-LAN checks without the deploy job (and without deploy secrets). +name: Validate (PR) +on: + pull_request: + types: [opened, synchronize, reopened] + branches: [main, master] + workflow_dispatch: +jobs: + run-all-validation: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Fetch deploy branches for workflow parity check + run: | + git fetch --depth=1 origin main master + - name: run-all-validation (no LAN, no genesis) + run: bash scripts/verify/run-all-validation.sh --skip-genesis diff --git a/.gitea/workflows/deploy-to-phoenix.yml b/.gitea/workflows/deploy-to-phoenix.yml index dd65ce0f..21ea365b 100644 --- a/.gitea/workflows/deploy-to-phoenix.yml +++ b/.gitea/workflows/deploy-to-phoenix.yml @@ -1,3 +1,5 @@ +# Canonical deploy workflow. Keep source and checked-in workflow copies byte-identical. +# Validation checks both file sync and main/master parity. name: Deploy to Phoenix on: @@ -12,20 +14,9 @@ jobs: - name: Checkout code uses: actions/checkout@v4 - # The cw* mesh matrix and deployment-status validators read - # cross-chain-pmm-lps/config/*.json. .gitmodules mixes Gitea HTTPS - # with git@github.com: SSH URLs, so `submodules: recursive` on the - # parent checkout isn't safe. Shallow-clone the public mirror of the - # pmm-lps repo directly (config-only, no secrets needed). - - name: Materialize cross-chain-pmm-lps (config only) + - name: Fetch deploy branches for workflow parity check run: | - set -euo pipefail - if [ ! -f cross-chain-pmm-lps/config/deployment-status.json ]; then - rm -rf cross-chain-pmm-lps - git clone --depth=1 \ - https://gitea.d-bis.org/d-bis/cross-chain-pmm-lps.git \ - cross-chain-pmm-lps - fi + git fetch --depth=1 origin main master - name: Run repo validation gate run: | diff --git a/.gitea/workflows/validate-on-pr.yml b/.gitea/workflows/validate-on-pr.yml index cc5ba463..92bb2bde 100644 --- a/.gitea/workflows/validate-on-pr.yml +++ b/.gitea/workflows/validate-on-pr.yml @@ -1,3 +1,5 @@ +# Canonical PR validation workflow. Keep source and checked-in workflow copies byte-identical. +# Validation checks both file sync and main/master parity. # PR-only: push validation already runs in deploy-to-phoenix.yml; this gives PRs the same # no-LAN checks without the deploy job (and without deploy secrets). name: Validate (PR) @@ -12,14 +14,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - - name: Materialize cross-chain-pmm-lps (config only) + - name: Fetch deploy branches for workflow parity check run: | - set -euo pipefail - if [ ! -f cross-chain-pmm-lps/config/deployment-status.json ]; then - rm -rf cross-chain-pmm-lps - git clone --depth=1 \ - https://gitea.d-bis.org/d-bis/cross-chain-pmm-lps.git \ - cross-chain-pmm-lps - fi + git fetch --depth=1 origin main master - name: run-all-validation (no LAN, no genesis) run: bash scripts/verify/run-all-validation.sh --skip-genesis diff --git a/docs/04-configuration/DEVIN_GITEA_PROXMOX_CICD.md b/docs/04-configuration/DEVIN_GITEA_PROXMOX_CICD.md index 6af93eae..5cbbdaaf 100644 --- a/docs/04-configuration/DEVIN_GITEA_PROXMOX_CICD.md +++ b/docs/04-configuration/DEVIN_GITEA_PROXMOX_CICD.md @@ -17,6 +17,7 @@ Create a repeatable path where: The path now exists for **`d-bis/proxmox`** on **`main`** and **`master`**: +- Canonical workflow sources: [.gitea/workflow-sources/deploy-to-phoenix.yml](/home/intlc/projects/proxmox/.gitea/workflow-sources/deploy-to-phoenix.yml) and [.gitea/workflow-sources/validate-on-pr.yml](/home/intlc/projects/proxmox/.gitea/workflow-sources/validate-on-pr.yml) - Workflow: [deploy-to-phoenix.yml](/home/intlc/projects/proxmox/.gitea/workflows/deploy-to-phoenix.yml) - Manual app workflow: [deploy-portal-live.yml](/home/intlc/projects/proxmox/.gitea/workflows/deploy-portal-live.yml) - Deploy service: [server.js](/home/intlc/projects/proxmox/phoenix-deploy-api/server.js) @@ -32,6 +33,25 @@ A second target is now available: - `portal-live` → runs [sync-sankofa-portal-7801.sh](/home/intlc/projects/proxmox/scripts/deployment/sync-sankofa-portal-7801.sh) and then checks `http://192.168.11.51:3000/` +## Workflow lockstep + +Because both `main` and `master` can trigger deploys, deploy behavior is now defined from canonical source files and checked for branch parity. + +- Edit only the source files under [.gitea/workflow-sources](/home/intlc/projects/proxmox/.gitea/workflow-sources:1) +- Sync the checked-in workflow copies with: + +```bash +bash scripts/verify/sync-gitea-workflows.sh +``` + +- Validate source sync plus `main`/`master` parity with: + +```bash +bash scripts/verify/run-all-validation.sh --skip-genesis +``` + +The deploy and PR workflows both fetch `origin/main` and `origin/master` before validation, so branch drift now fails CI instead of silently changing deploy behavior. + ## Flow ```text diff --git a/scripts/verify/check-gitea-branch-workflow-parity.sh b/scripts/verify/check-gitea-branch-workflow-parity.sh new file mode 100644 index 00000000..02213273 --- /dev/null +++ b/scripts/verify/check-gitea-branch-workflow-parity.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +cd "$PROJECT_ROOT" + +SOURCE_TARGET_PAIRS=( + ".gitea/workflow-sources/deploy-to-phoenix.yml:.gitea/workflows/deploy-to-phoenix.yml" + ".gitea/workflow-sources/validate-on-pr.yml:.gitea/workflows/validate-on-pr.yml" +) + +missing_ref=false +for ref in origin/main origin/master; do + if ! git rev-parse --verify "$ref" >/dev/null 2>&1; then + missing_ref=true + fi +done + +if [[ "$missing_ref" == true ]]; then + echo "[i] Skipping main/master workflow parity check (origin/main or origin/master not available)" + exit 0 +fi + +for pair in "${SOURCE_TARGET_PAIRS[@]}"; do + source="${pair%%:*}" + target="${pair##*:}" + + main_blob="$(git show "origin/main:$source" 2>/dev/null || true)" + master_blob="$(git show "origin/master:$source" 2>/dev/null || true)" + + if [[ -z "$main_blob" ]]; then + main_blob="$(git show "origin/main:$target" 2>/dev/null || true)" + fi + if [[ -z "$master_blob" ]]; then + master_blob="$(git show "origin/master:$target" 2>/dev/null || true)" + fi + + if [[ -z "$main_blob" || -z "$master_blob" ]]; then + echo "[✗] Missing $source/$target on origin/main or origin/master" >&2 + exit 1 + fi + + if [[ "$main_blob" != "$master_blob" ]]; then + echo "[✗] Branch workflow drift: $source differs between origin/main and origin/master" >&2 + echo " Keep both deploy branches in lockstep for workflow-source files." >&2 + exit 1 + fi + + echo "[✓] Branch parity OK for $source" +done diff --git a/scripts/verify/check-gitea-workflows.sh b/scripts/verify/check-gitea-workflows.sh new file mode 100644 index 00000000..d9207ef7 --- /dev/null +++ b/scripts/verify/check-gitea-workflows.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +cd "$PROJECT_ROOT" + +check_one() { + local source_rel="$1" + local target_rel="$2" + + if [[ ! -f "$source_rel" ]]; then + echo "[✗] Missing workflow source: $source_rel" >&2 + return 1 + fi + + if [[ ! -f "$target_rel" ]]; then + echo "[✗] Missing generated workflow: $target_rel" >&2 + return 1 + fi + + if ! diff -u "$source_rel" "$target_rel" >/dev/null; then + echo "[✗] Workflow drift detected: $target_rel does not match $source_rel" >&2 + echo " Run: bash scripts/verify/sync-gitea-workflows.sh" >&2 + return 1 + fi + + echo "[✓] $target_rel matches $source_rel" +} + +check_one ".gitea/workflow-sources/deploy-to-phoenix.yml" ".gitea/workflows/deploy-to-phoenix.yml" +check_one ".gitea/workflow-sources/validate-on-pr.yml" ".gitea/workflows/validate-on-pr.yml" diff --git a/scripts/verify/run-all-validation.sh b/scripts/verify/run-all-validation.sh index 191c5b59..cc0854a8 100644 --- a/scripts/verify/run-all-validation.sh +++ b/scripts/verify/run-all-validation.sh @@ -34,6 +34,16 @@ else fi echo "" +echo "1c. Gitea workflow source sync..." +bash "$SCRIPT_DIR/check-gitea-workflows.sh" || log_err "Gitea workflow source drift" +log_ok "Gitea workflows match source-of-truth files" +echo "" + +echo "1d. main/master workflow parity..." +bash "$SCRIPT_DIR/check-gitea-branch-workflow-parity.sh" || log_err "main/master workflow parity drift" +log_ok "main/master workflow parity OK" +echo "" + echo "2. Config files..." bash "$SCRIPT_DIR/../validation/validate-config-files.sh" || log_err "validate-config-files failed" log_ok "Config validation OK" @@ -41,25 +51,22 @@ echo "" echo "3. cW* mesh matrix (deployment-status + Uni V2 pair-discovery)..." DISCOVERY_JSON="$PROJECT_ROOT/reports/extraction/promod-uniswap-v2-live-pair-discovery-latest.json" -PMM_STATUS="$PROJECT_ROOT/cross-chain-pmm-lps/config/deployment-status.json" -if [[ -f "$DISCOVERY_JSON" && -f "$PMM_STATUS" ]]; then +if [[ -f "$DISCOVERY_JSON" ]]; then MATRIX_JSON="$PROJECT_ROOT/reports/status/cw-mesh-deployment-matrix-latest.json" bash "$SCRIPT_DIR/build-cw-mesh-deployment-matrix.sh" --no-markdown --json-out "$MATRIX_JSON" || log_err "cw mesh matrix merge failed" log_ok "cW mesh matrix OK (also wrote $MATRIX_JSON)" -elif [[ ! -f "$DISCOVERY_JSON" ]]; then - echo " ($DISCOVERY_JSON missing — run: bash scripts/verify/build-promod-uniswap-v2-live-pair-discovery.sh)" else - echo " ($PMM_STATUS missing — cross-chain-pmm-lps submodule not initialized; skip)" + echo " ($DISCOVERY_JSON missing — run: bash scripts/verify/build-promod-uniswap-v2-live-pair-discovery.sh)" fi echo "" echo "3b. deployment-status graph (cross-chain-pmm-lps)..." PMM_VALIDATE="$PROJECT_ROOT/cross-chain-pmm-lps/scripts/validate-deployment-status.cjs" -if [[ -f "$PMM_VALIDATE" && -f "$PMM_STATUS" ]] && command -v node &>/dev/null; then +if [[ -f "$PMM_VALIDATE" ]] && command -v node &>/dev/null; then node "$PMM_VALIDATE" || log_err "validate-deployment-status.cjs failed" log_ok "deployment-status.json rules OK" else - echo " (skip: node, $PMM_VALIDATE, or $PMM_STATUS missing)" + echo " (skip: node or $PMM_VALIDATE missing)" fi echo "" diff --git a/scripts/verify/sync-gitea-workflows.sh b/scripts/verify/sync-gitea-workflows.sh new file mode 100644 index 00000000..6860d5a6 --- /dev/null +++ b/scripts/verify/sync-gitea-workflows.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +cd "$PROJECT_ROOT" + +sync_one() { + local source_rel="$1" + local target_rel="$2" + + mkdir -p "$(dirname "$target_rel")" + cp "$source_rel" "$target_rel" + echo "[✓] Synced $target_rel from $source_rel" +} + +sync_one ".gitea/workflow-sources/deploy-to-phoenix.yml" ".gitea/workflows/deploy-to-phoenix.yml" +sync_one ".gitea/workflow-sources/validate-on-pr.yml" ".gitea/workflows/validate-on-pr.yml"