fix: add nil check before writing to dest in handshake handler (#86) #28
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build and Deploy Proxy Router | |
| on: | |
| push: | |
| branches: [ main, stg, dev, cicd/* ] | |
| paths: [ | |
| 'build.sh', | |
| 'docker-compose.yml', | |
| 'Dockerfile', | |
| '**/*.go', | |
| '**/*.ts', | |
| '**/*.js', | |
| '.github/workflows/build-deploy-proxy-router.yml'] | |
| pull_request: | |
| types: [opened, reopened, synchronize] | |
| branches: | |
| - dev | |
| paths: | |
| - 'build.sh' | |
| - 'docker-compose.yml' | |
| - 'Dockerfile' | |
| - '**/*.go' | |
| - '**/*.ts' | |
| - '**/*.js' | |
| - '.github/workflows/build-deploy-proxy-router.yml' | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: true | |
| defaults: | |
| run: | |
| shell: bash | |
| jobs: | |
| Generate-Tag: | |
| runs-on: ubuntu-latest | |
| name: Generate Tag Name | |
| outputs: | |
| tag_name: ${{ steps.gen_tag.outputs.tag_name }} | |
| vtag: ${{ steps.gen_tag.outputs.vtag }} | |
| vfull: ${{ steps.gen_tag.outputs.vfull }} | |
| version: ${{ steps.gen_tag.outputs.version }} | |
| image_name: ${{ steps.config.outputs.image_name }} | |
| environment: ${{ steps.gen_tag.outputs.environment }} | |
| is_cicd_branch: ${{ steps.gen_tag.outputs.is_cicd_branch }} | |
| steps: | |
| - name: Clone repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| - name: Generate version tag | |
| id: gen_tag | |
| uses: ./.github/actions/gen-tag | |
| with: | |
| component: proxy-router | |
| major_version: '3' | |
| - name: Config | |
| id: config | |
| run: | | |
| IMAGE_NAME="ghcr.io/lumerin-protocol/proxy-router" | |
| echo "image_name=${IMAGE_NAME}" >> $GITHUB_OUTPUT | |
| echo "✅ Docker Image: ${IMAGE_NAME}:${{ steps.gen_tag.outputs.tag_name }}" >> $GITHUB_STEP_SUMMARY | |
| if [ "${{ steps.gen_tag.outputs.is_cicd_branch }}" == "true" ]; then | |
| echo "🔧 CI/CD Test Mode: Build test only" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| Build-Test: | |
| if: | | |
| (github.event_name == 'pull_request' && (github.base_ref == 'main' || github.base_ref == 'stg' || github.base_ref == 'dev')) || | |
| (github.event_name == 'push' && (startsWith(github.ref, 'refs/heads/cicd/') || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/stg' || github.ref == 'refs/heads/dev')) | |
| runs-on: ubuntu-latest | |
| needs: Generate-Tag | |
| environment: ${{ github.ref == 'refs/heads/main' && 'main' || '' }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| - name: Build Docker Image to Test | |
| run: | | |
| BUILDTAG=${{ needs.Generate-Tag.outputs.tag_name }} | |
| BUILDIMAGE=${{ needs.Generate-Tag.outputs.image_name }} | |
| BUILDCOMMIT=${{ github.sha }} | |
| docker build \ | |
| --platform linux/amd64 \ | |
| --build-arg TAG_NAME=$BUILDTAG \ | |
| --build-arg COMMIT=$BUILDCOMMIT \ | |
| --load \ | |
| -t $BUILDIMAGE:$BUILDTAG \ | |
| . || (echo "❌ Failed to build image with tag: $BUILDIMAGE:$BUILDTAG" && exit 1) | |
| echo "✅ Proxy-Router Test Build of $BUILDIMAGE:$BUILDTAG Successful!" | |
| - name: Run Docker Image to Test | |
| run: | | |
| BUILDTAG=${{ needs.Generate-Tag.outputs.tag_name }} | |
| BUILDIMAGE=${{ needs.Generate-Tag.outputs.image_name }} | |
| # Construct Graph URL from org secrets/vars (using DEV for CI testing) | |
| FUTURES_SUBGRAPH_URL="https://gateway.thegraph.com/api/${{ secrets.DEV_GRAPH_APIKEY }}/subgraphs/id/${{ vars.DEV_FUTURES_SUBGRAPH_ID }}" | |
| docker run -d --name proxy-router-test \ | |
| -e FUTURES_SUBGRAPH_URL="${FUTURES_SUBGRAPH_URL}" \ | |
| -e MULTICALL_ADDRESS=${{ vars.MULTICALL_ADDRESS }} \ | |
| -e FUTURES_ADDRESS=${{ vars.FUTURES_ADDRESS }} \ | |
| -e FUTURES_VALIDATOR_URL_OVERRIDE=${{ vars.FUTURES_VALIDATOR_URL_OVERRIDE }} \ | |
| -e CLONE_FACTORY_ADDRESS=${{ vars.CLONE_FACTORY_ADDRESS }} \ | |
| -e ETH_NODE_ADDRESS=${{ vars.ETH_NODE_ADDRESS }} \ | |
| -e POOL_ADDRESS=${{ vars.POOL_ADDRESS }} \ | |
| -e WEB_ADDRESS=0.0.0.0:8888 \ | |
| -e WEB_PUBLIC_URL=http://localhost:8888 \ | |
| -e WALLET_PRIVATE_KEY=${{ secrets.WALLET_PRIVATE_KEY }} \ | |
| -e LOG_IS_PROD=false \ | |
| -e LOG_LEVEL=debug \ | |
| -e LOG_JSON=true \ | |
| -e ENVIRONMENT=production \ | |
| -e HASHRATE_SHARE_TIMEOUT=20m \ | |
| -p 8888:8888 \ | |
| $BUILDIMAGE:$BUILDTAG || (echo "❌ Failed to start image with tag: $BUILDIMAGE:$BUILDTAG" && exit 1) | |
| timeout=10 | |
| while ! curl -s http://localhost:8888/healthcheck >/dev/null; do | |
| ((timeout--)) | |
| if ((timeout <= 0)); then | |
| echo "📜 Fetching container logs:" | |
| docker logs proxy-router-test | |
| docker stop proxy-router-test | |
| docker rm proxy-router-test | |
| echo "❌ Proxy-Router $BUILDIMAGE:$BUILDTAG failed to start in time" | |
| exit 1 | |
| fi | |
| sleep 1 | |
| done | |
| echo "✅ Proxy-Router $BUILDIMAGE:$BUILDTAG started successfully!" | |
| VERSION_TAG=$(curl -s http://localhost:8888/healthcheck | jq -r '.version') | |
| echo "📜 Fetching container logs:" | |
| docker logs proxy-router-test | |
| if [ "$VERSION_TAG" == "$BUILDTAG" ]; then | |
| docker stop proxy-router-test | |
| docker rm proxy-router-test | |
| echo "✅ Version tag in container matches the expected tag: $VERSION_TAG" | |
| else | |
| docker stop proxy-router-test | |
| docker rm proxy-router-test | |
| echo "❌ Version tag mismatch: Expected $BUILDTAG, but got $VERSION_TAG" | |
| exit 1 # Prevent failure and continue | |
| fi | |
| echo "✅ Proxy-Router Test Run of $BUILDIMAGE:$BUILDTAG Successful!" | |
| OS-Build: | |
| if: | | |
| needs.Generate-Tag.outputs.is_cicd_branch != 'true' && ( | |
| (github.event_name == 'pull_request' && (github.base_ref == 'main' || github.base_ref == 'stg' || github.base_ref == 'dev')) || | |
| (github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/stg' || github.ref == 'refs/heads/dev')) | |
| ) | |
| runs-on: ubuntu-latest | |
| needs: | |
| - Generate-Tag | |
| - Build-Test | |
| strategy: | |
| matrix: | |
| goos: [linux, darwin, windows] | |
| goarch: [amd64, arm64] | |
| exclude: | |
| - goos: windows | |
| goarch: arm64 # Exclude windows/arm64 | |
| fail-fast: false | |
| steps: | |
| - name: Clone repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| - name: Setup Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: '1.22.x' | |
| cache-dependency-path: | | |
| go.sum | |
| - name: Install dependencies | |
| run: | | |
| go mod download | |
| - name: Set GOOS and GOARCH for cross-compilation | |
| run: | | |
| echo "GOOS=${{ matrix.goos }}" >> $GITHUB_ENV | |
| echo "GOARCH=${{ matrix.goarch }}" >> $GITHUB_ENV | |
| - name: Build | |
| run: | | |
| BUILDTAG=${{ needs.Generate-Tag.outputs.tag_name }} | |
| COMMIT=${{ github.sha }} | |
| go mod tidy | |
| go build \ | |
| -ldflags="-s -w \ | |
| -X 'github.com/Lumerin-protocol/proxy-router/internal/config.BuildVersion=$BUILDTAG' \ | |
| -X 'github.com/Lumerin-protocol/proxy-router/internal/config.Commit=$COMMIT' \ | |
| " \ | |
| -o ./proxy-router ./cmd || (echo "❌ Failed to build binary" && exit 1) | |
| echo "✅ Proxy-Router Build Successful!" | |
| - name: Pack artifacts | |
| run: | | |
| BUILDTAG="proxy-router-${{ needs.Generate-Tag.outputs.tag_name }}-${{ matrix.goos }}-${{ matrix.goarch }}" | |
| # Adjust artifact extension based on GOOS | |
| if [ "${{ matrix.goos }}" == "windows" ]; then | |
| ARTIFACT="$BUILDTAG.exe" | |
| else | |
| ARTIFACT="$BUILDTAG" | |
| fi | |
| echo "Artifact: $ARTIFACT" | |
| mv ./proxy-router $ARTIFACT # Rename to match OS/arch | |
| chmod +x $ARTIFACT # Ensure it's executable | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| path: ./proxy-router-${{ needs.Generate-Tag.outputs.tag_name }}-${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.goos == 'windows' && '.exe' || '' }} | |
| name: proxy-router-${{ matrix.goos }}-${{ matrix.goarch }} | |
| Release: | |
| if: | | |
| needs.Generate-Tag.outputs.is_cicd_branch != 'true' && | |
| (github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/stg' || github.ref == 'refs/heads/dev')) | |
| needs: | |
| - Generate-Tag | |
| - OS-Build | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Clone repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| - name: Download artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: ./artifact | |
| - name: Prepare artifacts | |
| run: | | |
| mkdir -p ./artifact/release | |
| mv ./artifact/*/* ./artifact/release | |
| ls -lah ./artifact/release | |
| - name: Create release | |
| id: create_release | |
| uses: anzz1/action-create-release@v1 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| with: | |
| tag_name: ${{ needs.Generate-Tag.outputs.tag_name }} | |
| prerelease: ${{ github.ref != 'refs/heads/main' }} | |
| - name: Upload release assets | |
| uses: softprops/action-gh-release@v1 | |
| with: | |
| tag_name: ${{ needs.Generate-Tag.outputs.tag_name }} | |
| files: ./artifact/release/* | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GHCR-Build-and-Push: | |
| if: | | |
| needs.Generate-Tag.outputs.is_cicd_branch != 'true' && | |
| (github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/stg' || github.ref == 'refs/heads/dev')) | |
| needs: | |
| - Generate-Tag | |
| - Build-Test | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v2 | |
| - 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 Push Multi-Platform Docker Image | |
| run: | | |
| BUILDTAG=${{ needs.Generate-Tag.outputs.tag_name }} | |
| BUILDIMAGE=${{ needs.Generate-Tag.outputs.image_name }} | |
| BUILDCOMMIT=${{ github.sha }} | |
| docker buildx build \ | |
| --platform linux/amd64,linux/arm64 \ | |
| --build-arg TAG_NAME=$BUILDTAG \ | |
| --build-arg COMMIT=$BUILDCOMMIT \ | |
| --push \ | |
| -t $BUILDIMAGE:$BUILDTAG \ | |
| . || (echo "❌ Failed to push image with tag: $BUILDIMAGE:$BUILDTAG" && exit 1) | |
| echo "✅ Proxy-Router Build and Push of $BUILDIMAGE:$BUILDTAG Successful!" | |
| - name: Optionally Push Latest Tag | |
| if: ${{ github.ref == 'refs/heads/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 "✅ Proxy-Router Push $BUILDIMAGE:latest Tag Successful!" | |
| AWS-Deploy: | |
| if: | | |
| needs.Generate-Tag.outputs.is_cicd_branch != 'true' && | |
| (github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/stg' || github.ref == 'refs/heads/dev')) | |
| needs: | |
| - Generate-Tag | |
| - GHCR-Build-and-Push | |
| runs-on: ubuntu-latest | |
| environment: ${{ github.ref_name == 'main' && 'main' || github.ref_name }} | |
| permissions: | |
| id-token: write | |
| contents: read | |
| steps: | |
| - name: Clone | |
| id: checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| - name: Install dependencies | |
| run: | | |
| sudo apt-get update && sudo apt-get install -y jq | |
| - name: Determine AWS Environment | |
| id: set_environment | |
| run: | | |
| if [ "${{ github.ref_name }}" == "dev" ]; then | |
| echo "aws_env=dev" >> $GITHUB_ENV | |
| elif [ "${{ github.ref_name }}" == "stg" ]; then | |
| echo "aws_env=stg" >> $GITHUB_ENV | |
| elif [ "${{ github.ref_name }}" == "main" ]; then | |
| echo "aws_env=lmn" >> $GITHUB_ENV | |
| else | |
| echo "This branch is not configured for AWS deployment." | |
| exit 1 | |
| fi | |
| echo "aws_region=us-east-1" >> $GITHUB_ENV | |
| - name: Configure AWS Credentials | |
| uses: aws-actions/configure-aws-credentials@v4 | |
| with: | |
| role-to-assume: ${{ secrets.AWS_ROLE_ARN }} | |
| aws-region: ${{ env.aws_region }} | |
| role-session-name: GitHubActions-ProxyRouter-Deploy | |
| - name: Deploy Proxy Router to ECS | |
| run: | | |
| BUILDTAG="${{ needs.Generate-Tag.outputs.tag_name }}" | |
| BUILDIMAGE="${{ needs.Generate-Tag.outputs.image_name }}" | |
| AWS_ENV="${{ env.aws_env }}" | |
| AWS_REGION="${{ env.aws_region }}" | |
| CLUSTER_NAME="ecs-proxy-router-${AWS_ENV}-use1" | |
| SERVICE_NAME="svc-proxy-router-${AWS_ENV}-use1" | |
| TASK_FAMILY="tsk-proxy-router" | |
| echo "🚀 Deploying ${BUILDIMAGE}:${BUILDTAG} to ${AWS_ENV} environment" | |
| echo "Cluster: ${CLUSTER_NAME}" | |
| echo "Service: ${SERVICE_NAME}" | |
| # Get current task definition | |
| aws ecs describe-task-definition \ | |
| --task-definition ${TASK_FAMILY} \ | |
| --region ${AWS_REGION} \ | |
| --query 'taskDefinition' > task-def.json | |
| # Update task definition with new image only | |
| # Secrets are pulled at runtime by ECS from Secrets Manager | |
| jq --arg IMAGE "${BUILDIMAGE}:${BUILDTAG}" \ | |
| '.containerDefinitions[0].image = $IMAGE | | |
| del(.taskDefinitionArn, .revision, .status, .requiresAttributes, .compatibilities, .registeredAt, .registeredBy)' \ | |
| task-def.json > new-task-def.json | |
| # Register new task definition | |
| NEW_TASK_DEF=$(aws ecs register-task-definition \ | |
| --cli-input-json file://new-task-def.json \ | |
| --region ${AWS_REGION} \ | |
| --query 'taskDefinition.taskDefinitionArn' --output text) | |
| echo "✅ Registered new task definition: ${NEW_TASK_DEF}" | |
| # Update ECS service | |
| aws ecs update-service \ | |
| --cluster ${CLUSTER_NAME} \ | |
| --service ${SERVICE_NAME} \ | |
| --task-definition ${NEW_TASK_DEF} \ | |
| --region ${AWS_REGION} \ | |
| --force-new-deployment | |
| echo "✅ Proxy Router service updated successfully!" | |
| - name: Deploy Proxy Validator to ECS | |
| run: | | |
| BUILDTAG="${{ needs.Generate-Tag.outputs.tag_name }}" | |
| BUILDIMAGE="${{ needs.Generate-Tag.outputs.image_name }}" | |
| AWS_ENV="${{ env.aws_env }}" | |
| AWS_REGION="${{ env.aws_region }}" | |
| CLUSTER_NAME="ecs-proxy-router-${AWS_ENV}-use1" | |
| SERVICE_NAME="svc-proxy-validator-${AWS_ENV}-use1" | |
| TASK_FAMILY="tsk-proxy-validator" | |
| echo "🚀 Deploying ${BUILDIMAGE}:${BUILDTAG} to ${AWS_ENV} validator" | |
| echo "Cluster: ${CLUSTER_NAME}" | |
| echo "Service: ${SERVICE_NAME}" | |
| # Get current task definition | |
| aws ecs describe-task-definition \ | |
| --task-definition ${TASK_FAMILY} \ | |
| --region ${AWS_REGION} \ | |
| --query 'taskDefinition' > validator-task-def.json | |
| # Update task definition with new image only | |
| # Secrets are pulled at runtime by ECS from Secrets Manager | |
| jq --arg IMAGE "${BUILDIMAGE}:${BUILDTAG}" \ | |
| '.containerDefinitions[0].image = $IMAGE | | |
| del(.taskDefinitionArn, .revision, .status, .requiresAttributes, .compatibilities, .registeredAt, .registeredBy)' \ | |
| validator-task-def.json > new-validator-task-def.json | |
| # Register new task definition | |
| NEW_TASK_DEF=$(aws ecs register-task-definition \ | |
| --cli-input-json file://new-validator-task-def.json \ | |
| --region ${AWS_REGION} \ | |
| --query 'taskDefinition.taskDefinitionArn' --output text) | |
| echo "✅ Registered new task definition: ${NEW_TASK_DEF}" | |
| # Wait 5 minutes for router to stabilize before updating validator | |
| echo "⏳ Waiting 5 minutes before deploying validator..." | |
| sleep 300 | |
| # Update ECS service | |
| aws ecs update-service \ | |
| --cluster ${CLUSTER_NAME} \ | |
| --service ${SERVICE_NAME} \ | |
| --task-definition ${NEW_TASK_DEF} \ | |
| --region ${AWS_REGION} \ | |
| --force-new-deployment | |
| echo "✅ Proxy Validator service updated successfully!" | |
| echo "🎉 Deployment to ${AWS_ENV} complete!" | |
| Notify: | |
| name: 📢 Notify | |
| runs-on: ubuntu-latest | |
| needs: [Generate-Tag, Build-Test, OS-Build, Release, GHCR-Build-and-Push, AWS-Deploy] | |
| if: always() && github.event_name != 'pull_request' | |
| steps: | |
| - name: Checkout (for composite action) | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 2 | |
| - name: Determine status | |
| id: status | |
| run: | | |
| if [ "${{ needs.Generate-Tag.outputs.is_cicd_branch }}" == "true" ]; then | |
| # For cicd branches, success if Build-Test passed | |
| if [ "${{ needs.Build-Test.result }}" == "success" ]; then | |
| echo "status=success" >> $GITHUB_OUTPUT | |
| else | |
| echo "status=failure" >> $GITHUB_OUTPUT | |
| fi | |
| elif [ "${{ needs.AWS-Deploy.result }}" == "success" ]; then | |
| echo "status=success" >> $GITHUB_OUTPUT | |
| elif [ "${{ needs.AWS-Deploy.result }}" == "failure" ] || [ "${{ needs.GHCR-Build-and-Push.result }}" == "failure" ] || [ "${{ needs.Build-Test.result }}" == "failure" ]; then | |
| echo "status=failure" >> $GITHUB_OUTPUT | |
| elif [ "${{ needs.AWS-Deploy.result }}" == "cancelled" ]; then | |
| echo "status=cancelled" >> $GITHUB_OUTPUT | |
| else | |
| echo "status=skipped" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Send Slack notification | |
| uses: ./.github/actions/slack-notify | |
| with: | |
| status: ${{ steps.status.outputs.status }} | |
| environment: ${{ needs.Generate-Tag.outputs.environment }} | |
| service_name: 'Proxy Seller & Validator' | |
| version: ${{ needs.Generate-Tag.outputs.tag_name }} | |
| slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }} | |
| github_token: ${{ secrets.GITHUB_TOKEN }} | |
| image_tag: ${{ needs.Generate-Tag.outputs.is_cicd_branch != 'true' && format('{0}:{1}', needs.Generate-Tag.outputs.image_name, needs.Generate-Tag.outputs.tag_name) || '' }} | |
| additional_info: '${{ needs.Generate-Tag.outputs.is_cicd_branch == ''true'' && ''*Mode:* CI/CD Test (build test only)'' || ''*Services:* Proxy Router + Proxy Validator'' }}' |