From 6fdb7ffda50a67174d53bd24b01660de90c56900 Mon Sep 17 00:00:00 2001 From: Ayush8923 <80516839+Ayush8923@users.noreply.github.com> Date: Wed, 26 Aug 2026 15:35:08 +0530 Subject: [PATCH 1/5] feat(*): improve ci/cd pipeline --- .github/workflows/create-release.yml | 26 +++++++++++++ .github/workflows/deploy-staging.yml | 58 +++++++++++++++++++++------- .github/workflows/pr-title-check.yml | 49 +++++++++++++++++++++++ 3 files changed, 120 insertions(+), 13 deletions(-) create mode 100644 .github/workflows/pr-title-check.yml diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index 856fa8b05..fa15b6ab8 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -6,7 +6,33 @@ on: - "v[0-9]+.[0-9]+.[0-9]+" # Deploy only when tags like v1.0.0, v2.1.0, etc., are created jobs: + # A tag can point at any commit, so confirm CI passed on it before building. + verify-ci: + runs-on: ubuntu-latest + permissions: + contents: read + actions: read + steps: + - name: Confirm CI passed on the tagged commit + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + SHA="${{ github.sha }}" + echo "Checking Kaapi CI conclusion for ${SHA}" + CONCLUSION=$(gh api \ + "repos/${{ github.repository }}/actions/runs?head_sha=${SHA}" \ + --jq 'first(.workflow_runs[] + | select(.name == "Kaapi CI" and .status == "completed") + | .conclusion) // "missing"') + echo "Kaapi CI conclusion: ${CONCLUSION}" + if [ "${CONCLUSION}" != "success" ]; then + echo "::error::Kaapi CI has not passed for ${SHA} (conclusion: ${CONCLUSION}). Aborting release." + exit 1 + fi + build: + needs: verify-ci runs-on: ubuntu-latest environment: AWS_ENV_VARS diff --git a/.github/workflows/deploy-staging.yml b/.github/workflows/deploy-staging.yml index 90de78fe8..638297d8a 100644 --- a/.github/workflows/deploy-staging.yml +++ b/.github/workflows/deploy-staging.yml @@ -1,8 +1,10 @@ name: Deploy Kaapi staging to EC2 on: - push: + workflow_run: + workflows: ["Kaapi CI"] branches: [main] + types: [completed] workflow_dispatch: concurrency: @@ -13,6 +15,10 @@ jobs: deploy: runs-on: ubuntu-latest environment: AWS_STAGING_ENV + # workflow_dispatch runs unconditionally; workflow_run only on a green CI. + if: >- + ${{ github.event_name == 'workflow_dispatch' || + github.event.workflow_run.conclusion == 'success' }} permissions: id-token: write @@ -45,16 +51,42 @@ jobs: env: INSTANCE_ID: ${{ secrets.STAGING_EC2_INSTANCE_ID }} CMD_ID: ${{ steps.ssm.outputs.cmd_id }} + MAX_ATTEMPTS: "30" + SLEEP_SECONDS: "20" run: | - WAIT_EXIT=0 - aws ssm wait command-executed \ - --command-id "$CMD_ID" \ - --instance-id "$INSTANCE_ID" || WAIT_EXIT=$? - - aws ssm get-command-invocation \ - --command-id "$CMD_ID" \ - --instance-id "$INSTANCE_ID" \ - --query '{Status:Status,Stdout:StandardOutputContent,Stderr:StandardErrorContent}' \ - --output json - - exit $WAIT_EXIT + get_output() { + aws ssm get-command-invocation \ + --command-id "$CMD_ID" \ + --instance-id "$INSTANCE_ID" \ + --query '{Status:Status,Stdout:StandardOutputContent,Stderr:StandardErrorContent}' \ + --output json + } + + for i in $(seq 1 "$MAX_ATTEMPTS"); do + STATUS=$(aws ssm get-command-invocation \ + --command-id "$CMD_ID" \ + --instance-id "$INSTANCE_ID" \ + --query "Status" \ + --output text 2>/dev/null || echo "Pending") + + echo "Attempt $i/$MAX_ATTEMPTS | Status: $STATUS" + + case "$STATUS" in + Success) + echo "Deployment completed successfully." + get_output + exit 0 + ;; + Failed|Cancelled|TimedOut) + echo "Deployment ended with status: $STATUS" + get_output + exit 1 + ;; + esac + + sleep "$SLEEP_SECONDS" + done + + echo "Deployment did not finish within the wait budget." + get_output + exit 1 diff --git a/.github/workflows/pr-title-check.yml b/.github/workflows/pr-title-check.yml new file mode 100644 index 000000000..2b7784a98 --- /dev/null +++ b/.github/workflows/pr-title-check.yml @@ -0,0 +1,49 @@ +name: PR Title Check + +on: + pull_request_target: + types: [opened, edited, reopened, synchronize] + +permissions: + pull-requests: read + +concurrency: + group: pr-title-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - name: Validate PR title follows Conventional Commits + env: + PR_TITLE: ${{ github.event.pull_request.title }} + run: | + # type(scope): summary — scope optional, `!` marks a breaking change. + # Keep types in sync with what the PR formatter is allowed to emit. + PATTERN='^(feat|fix|chore|docs|refactor|perf|test|ci|build|style|revert)(\([a-z0-9 _/-]+\))?!?: .+' + + echo "Checking PR title: $PR_TITLE" + if echo "$PR_TITLE" | grep -qE "$PATTERN"; then + echo "PR title is valid." + exit 0 + fi + + echo "::error title=Invalid PR title::'$PR_TITLE' is not in Conventional Commits format" + { + echo "### ❌ PR title is not in Conventional Commits format" + echo "" + echo "**Your title:** \`$PR_TITLE\`" + echo "" + echo "**Required format:** \`type(scope): short summary\` (scope is optional)" + echo "" + echo "**Allowed types:** feat, fix, chore, docs, refactor, perf, test, ci, build, style, revert" + echo "" + echo "**Good examples:**" + echo "- \`feat(collection): Add new fields\`" + echo "- \`fix(auth): Correct token expiry check\`" + echo "- \`chore(ci): Add PR title check workflow\`" + echo "" + echo "Edit the PR title to match and this check re-runs automatically." + } >> "$GITHUB_STEP_SUMMARY" + exit 1 From 8e463604c8548880bbec3c2030d8f834af11ecd2 Mon Sep 17 00:00:00 2001 From: Ayush8923 <80516839+Ayush8923@users.noreply.github.com> Date: Wed, 26 Aug 2026 17:45:24 +0530 Subject: [PATCH 2/5] fix(*): update the deployment script --- .github/workflows/deploy-staging.yml | 49 ++++++++++------------------ 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/.github/workflows/deploy-staging.yml b/.github/workflows/deploy-staging.yml index 638297d8a..ee7b72217 100644 --- a/.github/workflows/deploy-staging.yml +++ b/.github/workflows/deploy-staging.yml @@ -48,45 +48,32 @@ jobs: echo "Sent SSM command: $CMD_ID" - name: Wait for SSM command to finish + timeout-minutes: 20 env: INSTANCE_ID: ${{ secrets.STAGING_EC2_INSTANCE_ID }} CMD_ID: ${{ steps.ssm.outputs.cmd_id }} - MAX_ATTEMPTS: "30" - SLEEP_SECONDS: "20" + POLL_INTERVAL: "20" run: | - get_output() { + poll_status() { aws ssm get-command-invocation \ - --command-id "$CMD_ID" \ - --instance-id "$INSTANCE_ID" \ - --query '{Status:Status,Stdout:StandardOutputContent,Stderr:StandardErrorContent}' \ - --output json - } - - for i in $(seq 1 "$MAX_ATTEMPTS"); do - STATUS=$(aws ssm get-command-invocation \ --command-id "$CMD_ID" \ --instance-id "$INSTANCE_ID" \ --query "Status" \ - --output text 2>/dev/null || echo "Pending") - - echo "Attempt $i/$MAX_ATTEMPTS | Status: $STATUS" - - case "$STATUS" in - Success) - echo "Deployment completed successfully." - get_output - exit 0 - ;; - Failed|Cancelled|TimedOut) - echo "Deployment ended with status: $STATUS" - get_output - exit 1 - ;; - esac + --output text 2>/dev/null || echo "Pending" + } - sleep "$SLEEP_SECONDS" + STATUS=$(poll_status) + until [[ "$STATUS" =~ ^(Success|Failed|Cancelled|TimedOut)$ ]]; do + echo "Status: $STATUS — waiting ${POLL_INTERVAL}s" + sleep "$POLL_INTERVAL" + STATUS=$(poll_status) done - echo "Deployment did not finish within the wait budget." - get_output - exit 1 + echo "Final status: $STATUS" + aws ssm get-command-invocation \ + --command-id "$CMD_ID" \ + --instance-id "$INSTANCE_ID" \ + --query '{Status:Status,Stdout:StandardOutputContent,Stderr:StandardErrorContent}' \ + --output json + + [[ "$STATUS" == "Success" ]] From c63123b0d4314b73f702c1e9b1387985154e0804 Mon Sep 17 00:00:00 2001 From: Ayush8923 <80516839+Ayush8923@users.noreply.github.com> Date: Wed, 26 Aug 2026 17:46:41 +0530 Subject: [PATCH 3/5] fix(*): update the deployment script --- .github/workflows/create-release.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index fa15b6ab8..8a21f4f39 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -6,7 +6,6 @@ on: - "v[0-9]+.[0-9]+.[0-9]+" # Deploy only when tags like v1.0.0, v2.1.0, etc., are created jobs: - # A tag can point at any commit, so confirm CI passed on it before building. verify-ci: runs-on: ubuntu-latest permissions: From daed54eca3784194395c03a0345049e6226c181c Mon Sep 17 00:00:00 2001 From: Ayush8923 <80516839+Ayush8923@users.noreply.github.com> Date: Wed, 26 Aug 2026 18:14:51 +0530 Subject: [PATCH 4/5] fix(*): added the docker build checks in the ci --- .github/workflows/continuous-integration.yml | 22 ++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 6befcd555..120e1ab2f 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -82,3 +82,25 @@ jobs: source .venv/bin/activate coverage report --fail-under=70 working-directory: backend + + docker: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + + - name: Build backend image + run: docker build -t kaapi-backend:ci ./backend + + - name: Prepare env for compose validation + run: cp .env.test.example .env + + - name: Validate compose files + env: + RABBITMQ_USER: ci + RABBITMQ_PASSWORD: ci + RABBITMQ_VHOST: ci + run: | + for f in docker-compose.yml docker-compose.dev.yml docker-compose.staging.yml; do + echo "Validating $f" + docker compose -f "$f" config -q + done From 0a17a75f63b78e6cf80c9fb2482100e9a146ce9f Mon Sep 17 00:00:00 2001 From: Ayush8923 <80516839+Ayush8923@users.noreply.github.com> Date: Wed, 26 Aug 2026 23:45:50 +0530 Subject: [PATCH 5/5] fix(*): update the timeout min --- .github/workflows/deploy-staging.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy-staging.yml b/.github/workflows/deploy-staging.yml index ee7b72217..671514939 100644 --- a/.github/workflows/deploy-staging.yml +++ b/.github/workflows/deploy-staging.yml @@ -48,11 +48,11 @@ jobs: echo "Sent SSM command: $CMD_ID" - name: Wait for SSM command to finish - timeout-minutes: 20 + timeout-minutes: 10 env: INSTANCE_ID: ${{ secrets.STAGING_EC2_INSTANCE_ID }} CMD_ID: ${{ steps.ssm.outputs.cmd_id }} - POLL_INTERVAL: "20" + POLL_INTERVAL: "10" run: | poll_status() { aws ssm get-command-invocation \