diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 543bb6c..68714b5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,23 +1,34 @@ -name: CI-CD +name: Deploy Spot Indexer on: workflow_dispatch: inputs: - build_container: - description: "Build container image" - required: true + environment: + description: 'Target environment (dev=DEV, stg=STG, main=LMN/PROD)' + required: false + type: choice + options: + - dev + - stg + - main + build_only: + description: 'Build and push only (skip deployment)' + required: false type: boolean + default: false + # Full pipeline on push to main branches push: branches: - main - stg - dev - - cicd/* - paths: [".github/**", "src/**", "Dockerfile", "package.json", "package-lock.json", "LICENSE", "README.md"] + # Build test only on PRs targeting dev pull_request: + branches: + - dev types: [opened, reopened, synchronize] paths: [".github/**", "src/**", "Dockerfile", "package.json", "package-lock.json", "LICENSE", "README.md"] @@ -29,19 +40,29 @@ defaults: run: shell: bash +permissions: + id-token: write # Required for OIDC + contents: write # Required for creating tags + packages: write # For GHCR + env: - TEST_ENV_VAR: "test" + SERVICE_NAME: spot-indexer + GHCR_REGISTRY: ghcr.io + GHCR_IMAGE: ghcr.io/lumerin-protocol/proxy-indexer jobs: - Generate-Tag: + generate-tag: runs-on: ubuntu-latest - name: Generate Tag Name + name: ๐Ÿ“ฆ Generate Tag outputs: tag_name: ${{ steps.gen_tag_name.outputs.tag_name }} vtag: ${{ steps.gen_tag_name.outputs.vtag }} vfull: ${{ steps.gen_tag_name.outputs.vfull }} - image_name: ${{ steps.gen_tag_name.outputs.image_name }} - artifacts_base_url: ${{ github.server_url }}/${{ github.repository }}/releases/download/${{ steps.gen_tag_name.outputs.tag_name }} + environment: ${{ steps.determine_env.outputs.environment }} + should_deploy: ${{ steps.determine_env.outputs.should_deploy }} + ecs_cluster: ${{ steps.determine_env.outputs.ecs_cluster }} + ecs_service: ${{ steps.determine_env.outputs.ecs_service }} + aws_region: ${{ steps.determine_env.outputs.aws_region }} steps: - name: Clone repository uses: actions/checkout@v4 @@ -49,11 +70,54 @@ jobs: fetch-depth: 0 fetch-tags: true + - name: Determine environment + id: determine_env + run: | + # Determine environment based on branch or manual input + if [ "${{ github.event_name }}" == "workflow_dispatch" ] && [ -n "${{ github.event.inputs.environment }}" ]; then + ENV="${{ github.event.inputs.environment }}" + else + BRANCH="${{ github.ref_name }}" + case "$BRANCH" in + main) ENV="main" ;; + stg) ENV="stg" ;; + *) ENV="dev" ;; + esac + fi + echo "environment=${ENV}" >> $GITHUB_OUTPUT + + # Determine if we should deploy (not a PR, not build_only) + if [ "${{ github.event_name }}" == "pull_request" ]; then + echo "should_deploy=false" >> $GITHUB_OUTPUT + elif [ "${{ github.event.inputs.build_only }}" == "true" ]; then + echo "should_deploy=false" >> $GITHUB_OUTPUT + else + echo "should_deploy=true" >> $GITHUB_OUTPUT + fi + + # Set ECS cluster and service names based on environment + case $ENV in + dev) + echo "aws_region=us-east-1" >> $GITHUB_OUTPUT + echo "ecs_cluster=ecs-lumerin-marketplace-dev-use1" >> $GITHUB_OUTPUT + echo "ecs_service=svc-lumerin-indexer-dev-use1" >> $GITHUB_OUTPUT + ;; + stg) + echo "aws_region=us-east-1" >> $GITHUB_OUTPUT + echo "ecs_cluster=ecs-lumerin-marketplace-stg-use1" >> $GITHUB_OUTPUT + echo "ecs_service=svc-lumerin-indexer-stg-use1" >> $GITHUB_OUTPUT + ;; + main) + echo "aws_region=us-east-1" >> $GITHUB_OUTPUT + echo "ecs_cluster=ecs-lumerin-marketplace-lmn-use1" >> $GITHUB_OUTPUT + echo "ecs_service=svc-lumerin-indexer-lmn-use1" >> $GITHUB_OUTPUT + ;; + esac + - name: Determine tag name id: gen_tag_name shell: bash run: | - IMAGE_NAME="ghcr.io/lumerin-protocol/proxy-indexer" VMAJ_NEW=2 VMIN_NEW=0 VPAT_NEW=0 @@ -68,7 +132,9 @@ jobs: VPAT=0 fi - if [ "$GITHUB_REF_NAME" = "main" ]; then + ENV="${{ steps.determine_env.outputs.environment }}" + + if [ "$GITHUB_REF_NAME" = "main" ] || [ "$ENV" = "main" ]; then if [ "$VMAJ_NEW" -gt "$VMAJ" ]; then VMAJ=$VMAJ_NEW VMIN=$VMIN_NEW @@ -83,178 +149,233 @@ jobs: MB=$(git merge-base refs/remotes/origin/main HEAD) VPAT=$(git rev-list --count --no-merges ${MB}..HEAD) VFULL=${VMAJ}.${VMIN}.${VPAT} - RNAME=${GITHUB_REF_NAME##*/} - [ "$GITHUB_EVENT_NAME" = "pull_request" ] && RNAME=pr${GITHUB_REF_NAME%/merge} - VTAG=v${VFULL}-${RNAME} + VTAG=v${VFULL}-${ENV} fi - # Output variables for use in subsequent jobs environment - echo "::set-output name=tag_name::${VTAG}" - echo "::set-output name=vtag::${VTAG}" - echo "::set-output name=vfull::${VFULL}" - echo "::set-output name=image_name::${IMAGE_NAME}" - echo "โœ… New Build Tag: $VTAG" >> $GITHUB_STEP_SUMMARY - echo "โœ… Docker Image: ${IMAGE_NAME}:${VTAG}" >> $GITHUB_STEP_SUMMARY - echo "โŒ Old Major Tag: $VLAST" >> $GITHUB_STEP_SUMMARY + # Output variables for use in subsequent jobs + echo "tag_name=${VTAG}" >> $GITHUB_OUTPUT + echo "vtag=${VTAG}" >> $GITHUB_OUTPUT + echo "vfull=${VFULL}" >> $GITHUB_OUTPUT + echo "โœ… Build Tag: $VTAG" >> $GITHUB_STEP_SUMMARY + echo "๐Ÿ“ฆ Docker Image: ${{ env.GHCR_IMAGE }}:${VTAG}" >> $GITHUB_STEP_SUMMARY + echo "๐ŸŒ Environment: ${ENV}" >> $GITHUB_STEP_SUMMARY - Test-Build-Indexer-Container: - name: Test Build Indexer Container - if: - ( - (github.event_name == 'pull_request' && (github.base_ref == 'main' || github.base_ref == 'dev' || github.base_ref == 'stg')) || - (github.event_name == 'workflow_dispatch' && (github.event.inputs.build_container == 'true')) - ) + test-build: + name: ๐Ÿงช Test Build + if: github.event_name == 'pull_request' runs-on: ubuntu-latest - needs: Generate-Tag + needs: generate-tag steps: - name: Checkout repository uses: actions/checkout@v4 - with: - fetch-depth: 0 - fetch-tags: true - - name: Build Docker Image to Test + - name: Build Docker Image (test only) run: | - # Ensure we're in the workspace directory - cd $GITHUB_WORKSPACE - - BUILDTAG=${{ needs.Generate-Tag.outputs.tag_name }} - BUILDIMAGE=${{ needs.Generate-Tag.outputs.image_name }} + BUILDTAG=${{ needs.generate-tag.outputs.tag_name }} BUILDCOMMIT=${{ github.sha }} docker build \ --platform linux/amd64 \ --build-arg TAG_NAME=$BUILDTAG \ --build-arg COMMIT=$BUILDCOMMIT \ - --load \ - -t $BUILDIMAGE:$BUILDTAG \ + -t ${{ env.GHCR_IMAGE }}:$BUILDTAG \ -f ./Dockerfile \ - . || (echo "โŒ Failed to build image with tag: $BUILDIMAGE:$BUILDTAG" && exit 1) - echo "โœ… Indexer Container Build Successful!" + . || (echo "โŒ Failed to build image" && exit 1) + echo "โœ… Test build successful!" - GHCR-Build-and-Push: - name: Build & Push Docker Image - if: | - ( - (github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/stg')) || - (github.event_name == 'workflow_dispatch' && (github.event.inputs.build_container == 'true')) - ) - needs: - - Generate-Tag + build-and-push: + name: ๐Ÿ”จ Build & Push + if: github.event_name != 'pull_request' runs-on: ubuntu-latest + needs: generate-tag + outputs: + version: ${{ needs.generate-tag.outputs.tag_name }} + environment: ${{ needs.generate-tag.outputs.environment }} steps: - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 fetch-tags: true + token: ${{ secrets.GITHUB_TOKEN }} - name: Log in to GitHub Container Registry uses: docker/login-action@v3 with: - registry: ghcr.io + registry: ${{ env.GHCR_REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Build and Push Multi-Platform Docker Image + - name: Inject version into package.json run: | - # Ensure we're in the workspace directory - cd $GITHUB_WORKSPACE - - BUILDTAG=${{ needs.Generate-Tag.outputs.tag_name }} - BUILDIMAGE=${{ needs.Generate-Tag.outputs.image_name }} - BUILDCOMMIT=${{ github.sha }} - FULLTAG=${{ needs.Generate-Tag.outputs.vfull }} - - # Debug: Show current directory and file existence - pwd - ls -la - echo "Checking if package.json exists..." - [ -f ./package.json ] && echo "package.json exists" || echo "package.json does not exist" + FULLTAG=${{ needs.generate-tag.outputs.vfull }} echo "Injecting version $FULLTAG into package.json" sed -i "s/\"version\": \".*\"/\"version\": \"$FULLTAG\"/" ./package.json - cat ./package.json | grep '"version"' # Optional: Verify the change + cat ./package.json | grep '"version"' + + - name: Build and Push Docker Image + run: | + BUILDTAG=${{ needs.generate-tag.outputs.tag_name }} + BUILDCOMMIT=${{ github.sha }} + ENV=${{ needs.generate-tag.outputs.environment }} + docker build \ --platform linux/amd64 \ --build-arg TAG_NAME=$BUILDTAG \ --build-arg COMMIT=$BUILDCOMMIT \ --push \ - -t $BUILDIMAGE:$BUILDTAG \ + -t ${{ env.GHCR_IMAGE }}:$BUILDTAG \ -f ./Dockerfile \ - . || (echo "โŒ Failed to push image with tag: $BUILDIMAGE:$BUILDTAG" && exit 1) - echo "โœ… Indexer Container Build and Push of $BUILDIMAGE:$BUILDTAG Successful!" + . || (echo "โŒ Failed to push image" && exit 1) + + echo "โœ… Pushed ${{ env.GHCR_IMAGE }}:$BUILDTAG" - - name: Optionally Push Latest Tag - if: ${{ github.ref == 'refs/heads/main' }} + - name: Push environment-latest tag (dev/stg only) + if: needs.generate-tag.outputs.environment != 'main' run: | - BUILDIMAGE=${{ needs.Generate-Tag.outputs.image_name }} - BUILDTAG=${{ needs.Generate-Tag.outputs.tag_name }} - docker pull $BUILDIMAGE:$BUILDTAG || (echo "โŒ Failed to pull image: $BUILDIMAGE:$BUILDTAG" && exit 1) - docker tag $BUILDIMAGE:$BUILDTAG $BUILDIMAGE:latest || (echo "โŒ Failed to tag image as :latest" && exit 1) - docker push $BUILDIMAGE:latest || (echo "โŒ Failed to push image as :latest" && exit 1) - echo "โœ… Indexer Container Push $BUILDIMAGE:latest Tag Successful!" + BUILDTAG=${{ needs.generate-tag.outputs.tag_name }} + ENV=${{ needs.generate-tag.outputs.environment }} + + docker pull ${{ env.GHCR_IMAGE }}:$BUILDTAG + docker tag ${{ env.GHCR_IMAGE }}:$BUILDTAG ${{ env.GHCR_IMAGE }}:latest-${ENV} + docker push ${{ env.GHCR_IMAGE }}:latest-${ENV} + + echo "โœ… Pushed ${{ env.GHCR_IMAGE }}:latest-${ENV}" - GitLab-Deploy-Indexer: - name: Deploy Indexer to GitLab - if: | - ( - (github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/stg')) - ) - needs: - - Generate-Tag - - GHCR-Build-and-Push - runs-on: ubuntu-latest - steps: - - name: Clone - id: checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - fetch-tags: true + - name: Push latest tag (main only) + if: needs.generate-tag.outputs.environment == 'main' + run: | + BUILDTAG=${{ needs.generate-tag.outputs.tag_name }} + docker pull ${{ env.GHCR_IMAGE }}:$BUILDTAG + docker tag ${{ env.GHCR_IMAGE }}:$BUILDTAG ${{ env.GHCR_IMAGE }}:latest + docker push ${{ env.GHCR_IMAGE }}:latest + echo "โœ… Pushed ${{ env.GHCR_IMAGE }}:latest" - - name: Install dependencies + # Create git tag immediately after successful GHCR push + # This enables Terraform auto-lookup to find the latest version + - name: Configure Git run: | - sudo apt-get update && sudo apt-get install -y jq + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" - - name: Trigger GitLab Pipeline + - name: Create and push git tag run: | - BUILDTAG=${{ needs.Generate-Tag.outputs.tag_name }} - FULLTAG=${{ needs.Generate-Tag.outputs.vfull }} - echo "Triggering GitLab Deploy for branch ${{ github.ref_name }} with tag $BUILDTAG" - # Determine branch - if [ "${{ github.ref_name }}" == "dev" ]; then - GITLABBRANCH="dev" - elif [ "${{ github.ref_name }}" == "stg" ]; then - GITLABBRANCH="stg" - elif [ "${{ github.ref_name }}" == "main" ]; then - GITLABBRANCH="main" + TAG_NAME=${{ needs.generate-tag.outputs.tag_name }} + echo "๐Ÿท๏ธ Creating git tag: $TAG_NAME" + + if git rev-parse "$TAG_NAME" >/dev/null 2>&1; then + echo "โš ๏ธ Tag $TAG_NAME already exists, skipping" else - echo "โŒ This branch is not configured to trigger GitLab pipelines." - exit 1 + git tag -a "$TAG_NAME" -m "Release $TAG_NAME - Container pushed to GHCR" + git push origin "$TAG_NAME" + echo "โœ… Git tag $TAG_NAME created and pushed" fi - echo "โœ… Sending Request to GitLab branch: $GITLABBRANCH" - response=$(curl --silent \ - --request POST \ - --url "${{ vars.GITLAB_TRIGGER_URL }}" \ - --form "token=${{ secrets.GITLAB_TRIGGER_TOKEN }}" \ - --form "ref=$GITLABBRANCH" \ - --form "variables[SOURCE_REPO]=${{ github.repository }}" \ - --form "variables[SOURCE_BRANCH]=${{ github.ref_name }}" \ - --form "variables[GITHUB_VFULL]=$FULLTAG" \ - --form "variables[GITHUB_TAG]=$BUILDTAG") - # Parse JSON response using jq - gitlab_status=$(echo "$response" | jq -r '.status // "unknown"') - gitlab_web_url=$(echo "$response" | jq -r '.web_url // "N/A"') + - name: Build summary + run: | + echo "## ๐Ÿ“ฆ Build Complete" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Container Tags:**" >> $GITHUB_STEP_SUMMARY + echo "- \`${{ env.GHCR_IMAGE }}:${{ needs.generate-tag.outputs.tag_name }}\` (semver)" >> $GITHUB_STEP_SUMMARY + if [ "${{ needs.generate-tag.outputs.environment }}" == "main" ]; then + echo "- \`${{ env.GHCR_IMAGE }}:latest\`" >> $GITHUB_STEP_SUMMARY + else + echo "- \`${{ env.GHCR_IMAGE }}:latest-${{ needs.generate-tag.outputs.environment }}\`" >> $GITHUB_STEP_SUMMARY + fi + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Git Tag:** \`${{ needs.generate-tag.outputs.tag_name }}\`" >> $GITHUB_STEP_SUMMARY - # Log the response - echo "GitLab Response: $response" + deploy: + name: ๐Ÿš€ Deploy + if: needs.generate-tag.outputs.should_deploy == 'true' + runs-on: ubuntu-latest + needs: [generate-tag, build-and-push] + + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ needs.generate-tag.outputs.environment == 'dev' && secrets.AWS_ROLE_ARN_DEV || needs.generate-tag.outputs.environment == 'stg' && secrets.AWS_ROLE_ARN_STG || secrets.AWS_ROLE_ARN_LMN }} + aws-region: ${{ needs.generate-tag.outputs.aws_region }} + role-session-name: GitHubActions-SpotIndexer-${{ github.run_id }} - # Validate the status field - if [[ "$gitlab_status" =~ ^(created|preparing|success|running|scheduled)$ ]]; then - echo "โœ… GitLab pipeline triggered successfully! Status: $gitlab_status" - echo "Pipeline details: $gitlab_web_url" + - name: Update ECS service + run: | + echo "๐Ÿš€ Deploying to ${{ needs.generate-tag.outputs.environment }} environment" + echo " Cluster: ${{ needs.generate-tag.outputs.ecs_cluster }}" + echo " Service: ${{ needs.generate-tag.outputs.ecs_service }}" + echo " Image: ${{ env.GHCR_IMAGE }}:${{ needs.generate-tag.outputs.tag_name }}" + + aws ecs update-service \ + --cluster ${{ needs.generate-tag.outputs.ecs_cluster }} \ + --service ${{ needs.generate-tag.outputs.ecs_service }} \ + --force-new-deployment \ + --region ${{ needs.generate-tag.outputs.aws_region }} + + echo "โœ… Deployment triggered" + + verify: + name: ๐Ÿ” Verify + if: needs.generate-tag.outputs.should_deploy == 'true' + runs-on: ubuntu-latest + needs: [generate-tag, deploy] + + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ needs.generate-tag.outputs.environment == 'dev' && secrets.AWS_ROLE_ARN_DEV || needs.generate-tag.outputs.environment == 'stg' && secrets.AWS_ROLE_ARN_STG || secrets.AWS_ROLE_ARN_LMN }} + aws-region: ${{ needs.generate-tag.outputs.aws_region }} + role-session-name: GitHubActions-SpotIndexer-Verify-${{ github.run_id }} + + - name: Wait for service to stabilize + run: | + echo "โณ Waiting for service to stabilize..." + aws ecs wait services-stable \ + --cluster ${{ needs.generate-tag.outputs.ecs_cluster }} \ + --services ${{ needs.generate-tag.outputs.ecs_service }} \ + --region ${{ needs.generate-tag.outputs.aws_region }} + + echo "โœ… Service is stable" + + summary: + name: ๐Ÿ“Š Summary + runs-on: ubuntu-latest + needs: [generate-tag, build-and-push, deploy, verify] + if: always() && needs.generate-tag.outputs.should_deploy == 'true' + + steps: + - name: Deployment summary + if: needs.verify.result == 'success' + run: | + echo "## ๐ŸŽ‰ Deployment Complete" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Service:** Spot Indexer" >> $GITHUB_STEP_SUMMARY + echo "**Environment:** ${{ needs.generate-tag.outputs.environment }}" >> $GITHUB_STEP_SUMMARY + echo "**Version:** ${{ needs.generate-tag.outputs.tag_name }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Container Tags:**" >> $GITHUB_STEP_SUMMARY + echo "- \`${{ env.GHCR_IMAGE }}:${{ needs.generate-tag.outputs.tag_name }}\`" >> $GITHUB_STEP_SUMMARY + if [ "${{ needs.generate-tag.outputs.environment }}" == "main" ]; then + echo "- \`${{ env.GHCR_IMAGE }}:latest\`" >> $GITHUB_STEP_SUMMARY else - echo "โŒ GitLab pipeline FAILED. Invalid status: $gitlab_status" - echo "Pipeline details: $gitlab_web_url" - exit 1 + echo "- \`${{ env.GHCR_IMAGE }}:latest-${{ needs.generate-tag.outputs.environment }}\`" >> $GITHUB_STEP_SUMMARY fi + echo "" >> $GITHUB_STEP_SUMMARY + echo "**ECS:**" >> $GITHUB_STEP_SUMMARY + echo "- Cluster: \`${{ needs.generate-tag.outputs.ecs_cluster }}\`" >> $GITHUB_STEP_SUMMARY + echo "- Service: \`${{ needs.generate-tag.outputs.ecs_service }}\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "โœ… Service deployed and stabilized successfully" >> $GITHUB_STEP_SUMMARY + echo "๐Ÿท๏ธ Git tag \`${{ needs.generate-tag.outputs.tag_name }}\` created" >> $GITHUB_STEP_SUMMARY + + - name: Failure summary + if: needs.verify.result == 'failure' + run: | + echo "## โŒ Deployment Failed" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Service:** Spot Indexer" >> $GITHUB_STEP_SUMMARY + echo "**Environment:** ${{ needs.generate-tag.outputs.environment }}" >> $GITHUB_STEP_SUMMARY + echo "**Version:** ${{ needs.generate-tag.outputs.tag_name }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "โš ๏ธ Deployment or verification failed. Check logs above for details." >> $GITHUB_STEP_SUMMARY + echo "๐Ÿ“ฆ Note: Container was pushed and git tag was created before deployment failed." >> $GITHUB_STEP_SUMMARY