diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy-prod.yaml similarity index 57% rename from .github/workflows/deploy.yaml rename to .github/workflows/deploy-prod.yaml index 9c63594..6ec1078 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy-prod.yaml @@ -1,75 +1,130 @@ -name: Deploy +name: Deploy to Production on: - workflow_run: - workflows: [ "Pre-Commit Checks" ] - types: - - completed + workflow_dispatch: + +env: + # Array of usernames allowed to trigger production deploys + ALLOWED_USERS: '["aditeyabaral", "achyu-dev", "ndigvijay"]' jobs: - # Docker build and push (main only) + # Sync dev branch to main before deployment + sync-dev-to-main: + runs-on: ubuntu-latest + if: ${{ contains(fromJson(env.ALLOWED_USERS), github.actor) }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + 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: Fetch all branches + run: | + git fetch origin dev + git fetch origin main + + - name: Checkout main branch + run: git checkout main + + - name: Merge dev into main + run: | + 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 + + # Build and push Docker images push-to-dockerhub: runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.head_branch == 'main' && github.event.workflow_run.conclusion == 'success' }} + needs: sync-dev-to-main + if: ${{ contains(fromJson(env.ALLOWED_USERS), github.actor) }} env: DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} steps: - - uses: actions/checkout@v3 + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: main + - name: Check Docker credentials run: | if [ -z "${{ secrets.DOCKER_USERNAME }}" ] || [ -z "${{ secrets.DOCKER_PASSWORD }}" ]; then - echo "Secrets missing, skipping push" + echo "❌ Docker credentials missing, skipping Docker Hub push" exit 1 fi + - name: Get short commit hash id: vars run: echo "tag=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT" + - name: Log in to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} + - name: Build and tag image run: | docker build . --tag ${{ secrets.DOCKER_USERNAME }}/pesu-auth:${{ steps.vars.outputs.tag }} docker tag ${{ secrets.DOCKER_USERNAME }}/pesu-auth:${{ steps.vars.outputs.tag }} ${{ secrets.DOCKER_USERNAME }}/pesu-auth:latest + - name: Push image to Docker Hub run: | docker push ${{ secrets.DOCKER_USERNAME }}/pesu-auth:${{ steps.vars.outputs.tag }} docker push ${{ secrets.DOCKER_USERNAME }}/pesu-auth:latest - # GHCR build and push (main only) + # Push to GitHub Container Registry push-to-ghcr: runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.head_branch == 'main' && github.event.workflow_run.conclusion == 'success' }} + needs: sync-dev-to-main + if: ${{ contains(fromJson(env.ALLOWED_USERS), github.actor) }} permissions: contents: read packages: write steps: - - uses: actions/checkout@v3 + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: main + - name: Get short commit hash id: vars run: echo "tag=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT" + - name: Log in to GitHub Container Registry uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and tag image for GHCR run: | docker build . --tag ghcr.io/${{ github.repository_owner }}/pesu-auth:${{ steps.vars.outputs.tag }} docker tag ghcr.io/${{ github.repository_owner }}/pesu-auth:${{ steps.vars.outputs.tag }} ghcr.io/${{ github.repository_owner }}/pesu-auth:latest + - name: Push image to GitHub Container Registry run: | docker push ghcr.io/${{ github.repository_owner }}/pesu-auth:${{ steps.vars.outputs.tag }} docker push ghcr.io/${{ github.repository_owner }}/pesu-auth:latest - # Deploy both environments on main + # Deploy to both Production and Staging deploy-prod-and-staging: runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.head_branch == 'main' && github.event.workflow_run.conclusion == 'success' }} + needs: [sync-dev-to-main, push-to-dockerhub, push-to-ghcr] + if: ${{ contains(fromJson(env.ALLOWED_USERS), github.actor) }} env: RENDER_DEPLOY_HOOK_URL_PROD: ${{ secrets.RENDER_DEPLOY_HOOK_URL_PROD }} RENDER_DEPLOY_HOOK_URL_DEV: ${{ secrets.RENDER_DEPLOY_HOOK_URL_DEV }} @@ -77,13 +132,14 @@ jobs: - name: Check Deploy Hook URLs run: | if [ -z "${{ secrets.RENDER_DEPLOY_HOOK_URL_PROD }}" ]; then - echo "Production deploy hook missing!" + echo "❌ Production deploy hook missing!" exit 1 fi if [ -z "${{ secrets.RENDER_DEPLOY_HOOK_URL_DEV }}" ]; then - echo "Staging deploy hook missing!" + echo "❌ Staging deploy hook missing!" exit 1 fi + - name: Deploy to Production run: | echo "🚀 Deploying to Production..." @@ -91,31 +147,21 @@ jobs: echo "❌ Production deploy failed!" exit 1 } - - name: Sync Staging with Production - run: | - echo "🚀 Deploying to Staging (mirror prod)..." - curl -X POST ${{ secrets.RENDER_DEPLOY_HOOK_URL_DEV }} || { - echo "❌ Staging deploy failed!" - exit 1 - } + echo "✅ Production deployment completed successfully!" - # Deploy only to staging on dev - deploy-staging-only: - runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.head_branch == 'dev' && github.event.workflow_run.conclusion == 'success' }} - env: - RENDER_DEPLOY_HOOK_URL_DEV: ${{ secrets.RENDER_DEPLOY_HOOK_URL_DEV }} - steps: - - name: Check Staging Deploy Hook URL - run: | - if [ -z "${{ secrets.RENDER_DEPLOY_HOOK_URL_DEV }}" ]; then - echo "Staging deploy hook missing!" - exit 1 - fi - - name: Deploy to Staging (Dev branch) + - name: Deploy to Staging run: | echo "🚀 Deploying to Staging..." curl -X POST ${{ secrets.RENDER_DEPLOY_HOOK_URL_DEV }} || { echo "❌ Staging deploy failed!" exit 1 } + echo "✅ Staging deployment completed successfully!" + + - name: Deployment Summary + run: | + echo "🎉 All deployments completed successfully!" + echo "✅ Branch sync: dev → main" + echo "✅ Docker images: pushed to Docker Hub and GHCR" + echo "✅ Production: deployed" + echo "✅ Staging: deployed" diff --git a/.github/workflows/deploy-staging.yaml b/.github/workflows/deploy-staging.yaml new file mode 100644 index 0000000..10813b8 --- /dev/null +++ b/.github/workflows/deploy-staging.yaml @@ -0,0 +1,31 @@ +name: Deploy to Staging + +on: + workflow_run: + workflows: [ "Pre-Commit Checks" ] + types: + - completed + +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' }} + env: + RENDER_DEPLOY_HOOK_URL_DEV: ${{ secrets.RENDER_DEPLOY_HOOK_URL_DEV }} + steps: + - name: Check Staging Deploy Hook URL + run: | + if [ -z "${{ secrets.RENDER_DEPLOY_HOOK_URL_DEV }}" ]; then + echo "❌ Staging deploy hook missing!" + exit 1 + fi + + - name: Deploy to Staging Environment + run: | + echo "🚀 Deploying to Staging..." + curl -X POST ${{ secrets.RENDER_DEPLOY_HOOK_URL_DEV }} || { + echo "❌ Staging deploy failed!" + exit 1 + } + echo "✅ Staging deployment completed successfully!" diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 30b69fb..2f0e9f6 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -1,15 +1,22 @@ name: Docker Image Build -on: [ push, pull_request ] +on: + push: + branches-ignore: + - main + - dev + pull_request: + types: + - opened + - synchronize + - reopened jobs: - build: - runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Get short commit hash id: vars diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index c70cdda..ccb485f 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -1,6 +1,15 @@ name: Lint -on: [ push, pull_request ] +on: + push: + branches-ignore: + - main + - dev + pull_request: + types: + - opened + - synchronize + - reopened jobs: lint: @@ -11,14 +20,11 @@ jobs: python-version: [ "3.11", "3.12", "3.13" ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - pip install -r requirements.txt - name: Install Ruff run: pip install ruff diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index 10898ba..c4e7349 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -3,7 +3,7 @@ name: Pre-Commit Checks on: [ push, pull_request ] jobs: - pre-commit: + pre-commit-checks: runs-on: ubuntu-latest strategy: max-parallel: 1 @@ -16,7 +16,6 @@ jobs: TEST_SRN: ${{ secrets.TEST_SRN }} TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }} TEST_BRANCH: ${{ secrets.TEST_BRANCH }} - TEST_BRANCH_SHORT_CODE: ${{ secrets.TEST_BRANCH_SHORT_CODE }} TEST_PROGRAM: ${{ secrets.TEST_PROGRAM }} TEST_SEMESTER: ${{ secrets.TEST_SEMESTER }} TEST_SECTION: ${{ secrets.TEST_SECTION }} @@ -26,18 +25,17 @@ jobs: TEST_CAMPUS_CODE: ${{ secrets.TEST_CAMPUS_CODE }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -r requirements.txt - pip install pre-commit pytest pytest-cov httpx python-dotenv pytest-asyncio + pip install .[dev] - name: Run pre-commit hooks run: pre-commit run --all-files diff --git a/.github/workflows/source.yaml b/.github/workflows/source.yaml index d3bcadf..b637698 100644 --- a/.github/workflows/source.yaml +++ b/.github/workflows/source.yaml @@ -26,25 +26,21 @@ jobs: - name: Validate PR origin and target branch run: | - # Allow dev → main PRs, but only if it's within the same repo (not a fork) - if [ "$SOURCE_REPO" = "$TARGET_REPO" ] && [ "$SOURCE_BRANCH" = "dev" ] && [ "$TARGET_BRANCH" = "main" ]; then - echo "✅ Dev to Main PR (release) allowed (same repo)." - exit 0 - fi - - # Otherwise: PRs must come from a fork, non-main branch, targeting dev + # PRs must come from a fork if [ "$SOURCE_REPO" = "$TARGET_REPO" ]; then - echo "❌ PR must come from a fork (not the main repo)." + echo "❌ PR must come from a fork." exit 1 fi + # PRs cannot come from the 'main' branch of a fork if [ "$SOURCE_BRANCH" = "main" ]; then echo "❌ PR cannot come from the 'main' branch of a fork." exit 1 fi + # PRs must target the 'dev' branch only if [ "$TARGET_BRANCH" != "dev" ]; then - echo "❌ PR must target the 'dev' branch (unless it's a dev → main release)." + echo "❌ PR must target the 'dev' branch only. PRs to 'main' are not allowed." exit 1 fi