Skip to content
Open
96 changes: 91 additions & 5 deletions .github/workflows/deploy-prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ jobs:
sync-dev-to-main:
runs-on: ubuntu-latest
if: ${{ contains(fromJson(vars.PROD_DEPLOYMENT_ALLOWED_USERS), github.actor) }}
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
Expand All @@ -33,17 +35,101 @@ jobs:
git merge --ff-only origin/dev || {
echo "❌ Fast-forward merge failed. Manual conflict resolution required."
echo "Please ensure dev branch is ahead of main with no conflicts."
git merge --abort
exit 1
}

- name: Push updated main branch
run: git push origin main

# Bump minor version on main
bump-minor-version:
runs-on: ubuntu-latest
needs: sync-dev-to-main
if: ${{ contains(fromJson(vars.PROD_DEPLOYMENT_ALLOWED_USERS), github.actor) }}
Comment thread
aditeyabaral marked this conversation as resolved.
permissions:
contents: write
steps:
- name: Checkout main
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
ref: main
fetch-depth: 0

- name: Configure Git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

- name: Bump minor version
run: |
python3 - <<'EOF'
import re

with open("pyproject.toml") as f:
content = f.read()

match = re.search(r'^version = "(\d+)\.(\d+)\.(\d+)"', content, re.MULTILINE)
Comment thread
aditeyabaral marked this conversation as resolved.
Outdated
if not match:
raise SystemExit('version = "x.y.z" not found in pyproject.toml')
major, minor, patch = int(match.group(1)), int(match.group(2)), int(match.group(3))
new_version = f"{major}.{minor + 1}.0"

new_content = re.sub(
r'^version = "\d+\.\d+\.\d+"',
f'version = "{new_version}"',
content,
count=1,
flags=re.MULTILINE,
)

with open("pyproject.toml", "w") as f:
f.write(new_content)

print(f"Bumped: {major}.{minor}.{patch} -> {new_version}")
EOF

- name: Commit and push
run: |
git add pyproject.toml
Comment thread
aditeyabaral marked this conversation as resolved.
Outdated
git diff --cached --quiet && exit 0
git commit -m "chore: bump minor version [skip ci]"
Comment thread
aditeyabaral marked this conversation as resolved.
Comment thread
aditeyabaral marked this conversation as resolved.
git push origin main
Comment thread
aditeyabaral marked this conversation as resolved.

# Sync minor version bump back to dev so dev and main stay on the same commit
sync-minor-to-dev:
runs-on: ubuntu-latest
needs: bump-minor-version
if: ${{ contains(fromJson(vars.PROD_DEPLOYMENT_ALLOWED_USERS), github.actor) }}
permissions:
contents: write
steps:
- name: Checkout dev
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
ref: dev
fetch-depth: 0

- name: Configure Git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

- name: Fast-forward dev to main
run: |
git fetch origin main
git merge --ff-only origin/main || {
echo "❌ Fast-forward merge of main into dev failed. dev has diverged from main."
echo "Please manually fast-forward dev to main before retrying the production deployment."
exit 1
}
git push origin dev

# Build and push Docker images
push-to-dockerhub:
runs-on: ubuntu-latest
needs: sync-dev-to-main
needs: bump-minor-version
if: ${{ contains(fromJson(vars.PROD_DEPLOYMENT_ALLOWED_USERS), github.actor) }}
env:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
Expand Down Expand Up @@ -84,7 +170,7 @@ jobs:
# Push to GitHub Container Registry
push-to-ghcr:
runs-on: ubuntu-latest
needs: sync-dev-to-main
needs: bump-minor-version
if: ${{ contains(fromJson(vars.PROD_DEPLOYMENT_ALLOWED_USERS), github.actor) }}
permissions:
contents: read
Expand Down Expand Up @@ -119,7 +205,7 @@ jobs:
# Deploy to Staging
deploy-to-staging:
runs-on: ubuntu-latest
needs: [sync-dev-to-main, push-to-dockerhub, push-to-ghcr]
needs: [bump-minor-version, push-to-dockerhub, push-to-ghcr]
Comment thread
aditeyabaral marked this conversation as resolved.
Outdated
if: ${{ contains(fromJson(vars.PROD_DEPLOYMENT_ALLOWED_USERS), github.actor) }}
env:
RENDER_DEPLOY_HOOK_URL_DEV: ${{ secrets.RENDER_DEPLOY_HOOK_URL_DEV }}
Expand All @@ -143,7 +229,7 @@ jobs:
# Deploy to Production
deploy-to-prod:
runs-on: ubuntu-latest
needs: [sync-dev-to-main, push-to-dockerhub, push-to-ghcr, deploy-to-staging]
needs: [bump-minor-version, push-to-dockerhub, push-to-ghcr, deploy-to-staging, sync-minor-to-dev]
if: ${{ contains(fromJson(vars.PROD_DEPLOYMENT_ALLOWED_USERS), github.actor) }}
env:
RENDER_DEPLOY_HOOK_URL_PROD: ${{ secrets.RENDER_DEPLOY_HOOK_URL_PROD }}
Expand Down
66 changes: 65 additions & 1 deletion .github/workflows/deploy-staging.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,17 @@ on:
types:
- completed

concurrency:
group: deploy-staging-${{ github.event.workflow_run.head_branch }}
cancel-in-progress: false

jobs:
# Deploy to staging environment
deploy-staging:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_branch == 'dev' }}
if: "${{ github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_branch == 'dev' && !startsWith(github.event.workflow_run.head_commit.message, 'chore: bump') }}"
permissions:
contents: write
env:
RENDER_DEPLOY_HOOK_URL_DEV: ${{ secrets.RENDER_DEPLOY_HOOK_URL_DEV }}
steps:
Expand All @@ -21,6 +27,64 @@ jobs:
exit 1
fi

- name: Checkout validated commit
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
ref: ${{ github.event.workflow_run.head_sha }}
fetch-depth: 0

- name: Configure Git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

- name: Verify dev HEAD matches validated SHA
run: |
git fetch origin dev
DEV_HEAD=$(git rev-parse origin/dev)
VALIDATED_SHA="${{ github.event.workflow_run.head_sha }}"
if [ "$DEV_HEAD" != "$VALIDATED_SHA" ]; then
echo "❌ dev has advanced to $DEV_HEAD beyond the validated SHA $VALIDATED_SHA."
echo "Skipping deployment to avoid deploying unvalidated commits."
exit 1
fi
Comment thread
aditeyabaral marked this conversation as resolved.
Comment thread
aditeyabaral marked this conversation as resolved.

- name: Bump patch version
run: |
python3 - <<'EOF'
Comment thread
aditeyabaral marked this conversation as resolved.
Outdated
import re

with open("pyproject.toml") as f:
content = f.read()

match = re.search(r'^version = "(\d+)\.(\d+)\.(\d+)"', content, re.MULTILINE)
Comment thread
aditeyabaral marked this conversation as resolved.
Outdated
if not match:
raise SystemExit('version = "x.y.z" not found in pyproject.toml')
major, minor, patch = int(match.group(1)), int(match.group(2)), int(match.group(3))
new_version = f"{major}.{minor}.{patch + 1}"

new_content = re.sub(
r'^version = "\d+\.\d+\.\d+"',
f'version = "{new_version}"',
content,
count=1,
flags=re.MULTILINE,
)

with open("pyproject.toml", "w") as f:
f.write(new_content)

print(f"Bumped: {major}.{minor}.{patch} -> {new_version}")
EOF

Comment thread
aditeyabaral marked this conversation as resolved.
Outdated
- name: Commit and push
run: |
git add pyproject.toml
Comment thread
aditeyabaral marked this conversation as resolved.
Outdated
git diff --cached --quiet && exit 0
git commit -m "chore: bump patch version [skip ci]"
Comment thread
aditeyabaral marked this conversation as resolved.
git push origin HEAD:dev
Comment thread
aditeyabaral marked this conversation as resolved.
Comment thread
aditeyabaral marked this conversation as resolved.

- name: Deploy to Staging Environment
Comment thread
aditeyabaral marked this conversation as resolved.
run: |
echo "🚀 Deploying to Staging..."
Expand Down
Loading