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..fc71d20d --- /dev/null +++ b/.gitea/workflow-sources/deploy-to-phoenix.yml @@ -0,0 +1,81 @@ +# 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: | + REMOTE="${GITEA_WORKFLOW_REMOTE:-origin}" + if git remote | grep -qx gitea; then + REMOTE="${GITEA_WORKFLOW_REMOTE:-gitea}" + fi + git fetch --depth=1 "$REMOTE" 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..05db708e --- /dev/null +++ b/.gitea/workflow-sources/validate-on-pr.yml @@ -0,0 +1,25 @@ +# 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: | + REMOTE="${GITEA_WORKFLOW_REMOTE:-origin}" + if git remote | grep -qx gitea; then + REMOTE="${GITEA_WORKFLOW_REMOTE:-gitea}" + fi + git fetch --depth=1 "$REMOTE" 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 f0f8a3d5..fc71d20d 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,6 +14,14 @@ jobs: - name: Checkout code uses: actions/checkout@v4 + - name: Fetch deploy branches for workflow parity check + run: | + REMOTE="${GITEA_WORKFLOW_REMOTE:-origin}" + if git remote | grep -qx gitea; then + REMOTE="${GITEA_WORKFLOW_REMOTE:-gitea}" + fi + git fetch --depth=1 "$REMOTE" main master + - name: Run repo validation gate run: | bash scripts/verify/run-all-validation.sh --skip-genesis diff --git a/.gitea/workflows/validate-on-pr.yml b/.gitea/workflows/validate-on-pr.yml index 3cb7b9d9..05db708e 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,5 +14,12 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + - name: Fetch deploy branches for workflow parity check + run: | + REMOTE="${GITEA_WORKFLOW_REMOTE:-origin}" + if git remote | grep -qx gitea; then + REMOTE="${GITEA_WORKFLOW_REMOTE:-gitea}" + fi + git fetch --depth=1 "$REMOTE" main master - name: run-all-validation (no LAN, no genesis) run: bash scripts/verify/run-all-validation.sh --skip-genesis 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..d190ed93 --- /dev/null +++ b/scripts/verify/check-gitea-branch-workflow-parity.sh @@ -0,0 +1,56 @@ +#!/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" +) + +REMOTE="${GITEA_WORKFLOW_REMOTE:-origin}" +if git remote | grep -qx gitea; then + REMOTE="${GITEA_WORKFLOW_REMOTE:-gitea}" +fi + +missing_ref=false +for ref in "$REMOTE/main" "$REMOTE/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 ($REMOTE/main or $REMOTE/master not available)" + exit 0 +fi + +for pair in "${SOURCE_TARGET_PAIRS[@]}"; do + source="${pair%%:*}" + target="${pair##*:}" + + main_blob="$(git show "$REMOTE/main:$source" 2>/dev/null || true)" + master_blob="$(git show "$REMOTE/master:$source" 2>/dev/null || true)" + + if [[ -z "$main_blob" ]]; then + main_blob="$(git show "$REMOTE/main:$target" 2>/dev/null || true)" + fi + if [[ -z "$master_blob" ]]; then + master_blob="$(git show "$REMOTE/master:$target" 2>/dev/null || true)" + fi + + if [[ -z "$main_blob" || -z "$master_blob" ]]; then + echo "[✗] Missing $source/$target on $REMOTE/main or $REMOTE/master" >&2 + exit 1 + fi + + if [[ "$main_blob" != "$master_blob" ]]; then + echo "[✗] Branch workflow drift: $source differs between $REMOTE/main and $REMOTE/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/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"