Deploy Staging #453
Workflow file for this run
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: Deploy Staging | |
| permissions: | |
| contents: read | |
| deployments: write | |
| id-token: write | |
| security-events: write # Required for Trivy SARIF upload in test workflow | |
| concurrency: | |
| group: robosystems-deploy | |
| cancel-in-progress: false | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| graph_container_refresh: | |
| description: "Refresh graph containers via SSM after deploy" | |
| required: false | |
| type: boolean | |
| default: true | |
| graph_refresh_max_wait_minutes: | |
| description: "Minutes to wait for in-flight destructive ops (materialization, SEC stage, ext mat) before cycling" | |
| required: false | |
| type: string | |
| default: "30" | |
| graph_refresh_force_ignore_busy: | |
| description: "Skip busy-counter check and cycle graph containers immediately (emergency override)" | |
| required: false | |
| type: boolean | |
| default: false | |
| graph_writer_az: | |
| description: "Move ALL LadybugDB writer tiers to this AZ (e.g. us-east-1d). Recovery/capacity only — replaces every writer instance onto an empty volume. Leave empty for normal deploys; after a move, set GRAPH_WRITER_AZ_STAGING to the same value so later releases stay there." | |
| required: false | |
| type: string | |
| default: "" | |
| jobs: | |
| # Guard: Verify staging environment is enabled | |
| check-staging-enabled: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check if staging is enabled | |
| run: | | |
| if [ "${{ vars.ENVIRONMENT_STAGING }}" != "staging" ]; then | |
| echo "❌ Staging environment is not enabled" | |
| echo "" | |
| echo "To enable staging deployments, set the GitHub variable:" | |
| echo " gh variable set ENVIRONMENT_STAGING --body staging" | |
| echo "" | |
| echo "Or run: just setup-gha (select 'Production + Staging')" | |
| exit 1 | |
| fi | |
| echo "✅ Staging environment is enabled" | |
| runner: | |
| needs: [check-staging-enabled] | |
| runs-on: ubuntu-latest | |
| outputs: | |
| runners_available: ${{ steps.select.outputs.runners_available }} | |
| runner_type: ${{ steps.select.outputs.runner_type }} | |
| runner_config: ${{ steps.select.outputs.runner_config }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| with: | |
| repository: ${{ github.repository }} | |
| ref: ${{ github.ref }} | |
| token: ${{ github.token }} | |
| - name: Select runner | |
| id: select | |
| uses: ./.github/actions/select-runner | |
| with: | |
| runner_labels: ${{ vars.RUNNER_LABELS || 'github-hosted' }} | |
| runner_scope: ${{ vars.RUNNER_SCOPE || 'both' }} | |
| github_token: ${{ github.token }} | |
| test: | |
| needs: [runner] | |
| uses: ./.github/workflows/test.yml | |
| with: | |
| runner_config: ${{ needs.runner.outputs.runner_config }} | |
| secrets: inherit | |
| stack-config: | |
| needs: [runner] | |
| runs-on: ${{ fromJSON(needs.runner.outputs.runner_config) }} | |
| permissions: | |
| id-token: write | |
| contents: read | |
| outputs: | |
| vpc_stack: ${{ steps.load.outputs.vpc_stack }} | |
| s3_stack: ${{ steps.load.outputs.s3_stack }} | |
| postgres_stack: ${{ steps.load.outputs.postgres_stack }} | |
| valkey_stack: ${{ steps.load.outputs.valkey_stack }} | |
| api_stack: ${{ steps.load.outputs.api_stack }} | |
| graph_infra_stack: ${{ steps.load.outputs.graph_infra_stack }} | |
| graph_volumes_stack: ${{ steps.load.outputs.graph_volumes_stack }} | |
| prometheus_stack: ${{ steps.load.outputs.prometheus_stack }} | |
| grafana_stack: ${{ steps.load.outputs.grafana_stack }} | |
| bastion_stack: ${{ steps.load.outputs.bastion_stack }} | |
| waf_stack: ${{ steps.load.outputs.waf_stack }} | |
| audit_stack: ${{ steps.load.outputs.audit_stack }} | |
| cloudtrail_stack: ${{ steps.load.outputs.cloudtrail_stack }} | |
| security_stack: ${{ steps.load.outputs.security_stack }} | |
| dagster_stack: ${{ steps.load.outputs.dagster_stack }} | |
| worker_stack: ${{ steps.load.outputs.worker_stack }} | |
| opensearch_stack: ${{ steps.load.outputs.opensearch_stack }} | |
| domain_name_root: ${{ steps.load.outputs.domain_name_root }} | |
| graph_ami_id: ${{ steps.ami.outputs.ami_id }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| with: | |
| repository: ${{ github.repository }} | |
| ref: ${{ github.ref }} | |
| token: ${{ github.token }} | |
| - name: Configure AWS credentials | |
| uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c # v6 | |
| with: | |
| role-to-assume: ${{ vars.AWS_ROLE_ARN }} | |
| aws-region: ${{ vars.AWS_REGION || 'us-east-1' }} | |
| - name: Get Graph AMI ID | |
| id: ami | |
| uses: ./.github/actions/get-graph-ami | |
| with: | |
| environment: staging | |
| - name: Install PyYAML | |
| shell: bash | |
| run: pip install --user pyyaml | |
| - name: Load Stack Configuration | |
| id: load | |
| shell: bash | |
| run: | | |
| chmod +x bin/tools/stack-config.sh | |
| ENV="staging" | |
| # Load all stack names from config | |
| echo "vpc_stack=$(./bin/tools/stack-config.sh $ENV vpc)" >> $GITHUB_OUTPUT | |
| echo "s3_stack=$(./bin/tools/stack-config.sh $ENV s3)" >> $GITHUB_OUTPUT | |
| echo "postgres_stack=$(./bin/tools/stack-config.sh $ENV postgres)" >> $GITHUB_OUTPUT | |
| echo "valkey_stack=$(./bin/tools/stack-config.sh $ENV valkey)" >> $GITHUB_OUTPUT | |
| echo "api_stack=$(./bin/tools/stack-config.sh $ENV api)" >> $GITHUB_OUTPUT | |
| echo "graph_infra_stack=$(./bin/tools/stack-config.sh $ENV graph.infra)" >> $GITHUB_OUTPUT | |
| echo "graph_volumes_stack=$(./bin/tools/stack-config.sh $ENV graph.volumes)" >> $GITHUB_OUTPUT | |
| echo "prometheus_stack=$(./bin/tools/stack-config.sh $ENV monitoring.prometheus)" >> $GITHUB_OUTPUT | |
| echo "grafana_stack=$(./bin/tools/stack-config.sh $ENV monitoring.grafana)" >> $GITHUB_OUTPUT | |
| echo "bastion_stack=$(./bin/tools/stack-config.sh $ENV bastion)" >> $GITHUB_OUTPUT | |
| echo "waf_stack=$(./bin/tools/stack-config.sh $ENV waf)" >> $GITHUB_OUTPUT | |
| echo "audit_stack=$(./bin/tools/stack-config.sh $ENV audit)" >> $GITHUB_OUTPUT | |
| echo "cloudtrail_stack=$(./bin/tools/stack-config.sh $ENV cloudtrail)" >> $GITHUB_OUTPUT | |
| echo "security_stack=$(./bin/tools/stack-config.sh $ENV security)" >> $GITHUB_OUTPUT | |
| echo "dagster_stack=$(./bin/tools/stack-config.sh $ENV dagster)" >> $GITHUB_OUTPUT | |
| echo "worker_stack=$(./bin/tools/stack-config.sh $ENV workers)" >> $GITHUB_OUTPUT | |
| echo "opensearch_stack=$(./bin/tools/stack-config.sh $ENV opensearch)" >> $GITHUB_OUTPUT | |
| # Domain configuration - leave empty for isolated deployment (no custom domains) | |
| echo "domain_name_root=${{ vars.API_DOMAIN_NAME_ROOT || '' }}" >> $GITHUB_OUTPUT | |
| build: | |
| needs: [runner] | |
| uses: ./.github/workflows/build.yml | |
| with: | |
| environment: ${{ vars.ENVIRONMENT_STAGING || 'staging' }} | |
| aws_region: ${{ vars.AWS_REGION || 'us-east-1' }} | |
| ecr_repository: ${{ vars.AWS_ECR_REPOSITORY || 'robosystems' }} | |
| # Docker Hub publishing for staging: disabled (staging builds should not go to Docker Hub) | |
| publish_to_dockerhub: false | |
| secrets: | |
| DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} | |
| DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} | |
| create-deployment: | |
| needs: [runner, test, build, stack-config] | |
| runs-on: ${{ fromJSON(needs.runner.outputs.runner_config) }} | |
| outputs: | |
| deployment_id: ${{ steps.deployment.outputs.deployment_id }} | |
| steps: | |
| - name: Create GitHub Deployment | |
| id: deployment | |
| uses: chrnorm/deployment-action@500aa6a23c81ffa1acf71072aee3cfa2cc2e556a # v2 | |
| with: | |
| token: ${{ github.token }} | |
| environment: ${{ vars.ENVIRONMENT_STAGING }} | |
| description: "Staging Deployment Created" | |
| - name: Update Deployment Status | |
| uses: chrnorm/deployment-status@6df8d036fd2fee9eb82936733953da1f8382b41e # v2 | |
| with: | |
| token: ${{ github.token }} | |
| state: in_progress | |
| deployment-id: ${{ steps.deployment.outputs.deployment_id }} | |
| description: "Staging Deployment In Progress" | |
| deploy-vpc: | |
| needs: [runner, create-deployment, stack-config] | |
| uses: ./.github/workflows/deploy-vpc.yml | |
| with: | |
| # Stack & Repository Configuration | |
| stack_name: ${{ needs.stack-config.outputs.vpc_stack }} | |
| cloudtrail_stack_name: ${{ needs.stack-config.outputs.cloudtrail_stack }} | |
| environment: shared | |
| # GHA Runner Configuration | |
| runner_config: ${{ needs.runner.outputs.runner_config }} | |
| # AWS Configuration | |
| aws_region: ${{ vars.AWS_REGION || 'us-east-1' }} | |
| # VPC Configuration | |
| endpoint_mode: ${{ vars.VPC_ENDPOINT_MODE || 'none' }} | |
| max_availability_zones: ${{ vars.VPC_MAX_AVAILABILITY_ZONES || '2' }} | |
| vpc_second_octet: ${{ vars.VPC_SECOND_OCTET || '0' }} | |
| # VPC Flow Logs Configuration (VPC-level, not environment-specific) | |
| vpc_flow_logs_enabled: ${{ vars.VPC_FLOW_LOGS_ENABLED || 'false' }} | |
| vpc_flow_logs_retention_days: ${{ vars.VPC_FLOW_LOGS_RETENTION_DAYS || '30' }} | |
| vpc_flow_logs_traffic_type: ${{ vars.VPC_FLOW_LOGS_TRAFFIC_TYPE || 'REJECT' }} | |
| # CloudTrail Configuration (account-level, not environment-specific) | |
| cloudtrail_enabled: ${{ vars.CLOUDTRAIL_ENABLED || 'false' }} | |
| cloudtrail_log_retention_days: ${{ vars.CLOUDTRAIL_LOG_RETENTION_DAYS || '90' }} | |
| cloudtrail_data_events_enabled: ${{ vars.CLOUDTRAIL_DATA_EVENTS_ENABLED || 'false' }} | |
| # Security Baseline Configuration (account-level, not environment-specific) | |
| security_enabled: ${{ vars.SECURITY_ENABLED || 'false' }} | |
| security_stack_name: ${{ needs.stack-config.outputs.security_stack }} | |
| security_config_enabled: ${{ vars.SECURITY_CONFIG_ENABLED || 'false' }} | |
| secrets: inherit | |
| deploy-s3: | |
| needs: [runner, create-deployment, stack-config] | |
| uses: ./.github/workflows/deploy-s3.yml | |
| with: | |
| # Stack & Repository Configuration | |
| stack_name: ${{ needs.stack-config.outputs.s3_stack }} | |
| environment: ${{ vars.ENVIRONMENT_STAGING || 'staging' }} | |
| # GHA Runner Configuration | |
| runner_config: ${{ needs.runner.outputs.runner_config }} | |
| # AWS Configuration | |
| aws_region: ${{ vars.AWS_REGION || 'us-east-1' }} | |
| # Namespace Configuration (always use account ID for globally unique bucket names) | |
| namespace: ${{ vars.AWS_ACCOUNT_ID }} | |
| # Domain Configuration | |
| # Leave PUBLIC_DOMAIN_NAME_STAGING empty for default CloudFront domain (isolated deployment) | |
| public_domain_name: ${{ vars.PUBLIC_DOMAIN_NAME_STAGING || '' }} | |
| domain_name_root: ${{ vars.API_DOMAIN_NAME_ROOT || '' }} | |
| secrets: inherit | |
| package-scripts: | |
| needs: [runner, deploy-s3] | |
| runs-on: ${{ fromJSON(needs.runner.outputs.runner_config) }} | |
| permissions: | |
| id-token: write | |
| contents: read | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| with: | |
| repository: ${{ github.repository }} | |
| ref: ${{ github.ref }} | |
| token: ${{ github.token }} | |
| - name: Configure AWS credentials | |
| uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c # v6 | |
| with: | |
| role-to-assume: ${{ vars.AWS_ROLE_ARN }} | |
| aws-region: ${{ vars.AWS_REGION || 'us-east-1' }} | |
| - name: Package and upload scripts | |
| run: | | |
| echo "📦 Packaging UserData scripts..." | |
| ./bin/tools/package-scripts.sh ${{ vars.ENVIRONMENT_STAGING || 'staging' }} | |
| echo "✅ Scripts packaged and uploaded successfully" | |
| deploy-postgres: | |
| needs: [runner, create-deployment, stack-config, build, deploy-vpc] | |
| uses: ./.github/workflows/deploy-postgres.yml | |
| with: | |
| # Stack & Repository Configuration | |
| stack_name: ${{ needs.stack-config.outputs.postgres_stack }} | |
| environment: ${{ vars.ENVIRONMENT_STAGING || 'staging' }} | |
| # GHA Runner Configuration | |
| runner_config: ${{ needs.runner.outputs.runner_config }} | |
| # AWS Configuration | |
| aws_region: ${{ vars.AWS_REGION || 'us-east-1' }} | |
| vpc_id: ${{ needs.deploy-vpc.outputs.vpc_id }} | |
| vpc_cidr: ${{ needs.deploy-vpc.outputs.vpc_cidr }} | |
| subnet_ids: ${{ needs.deploy-vpc.outputs.private_subnet_ids }} | |
| # Database Version Configuration | |
| postgres_version: ${{ vars.DATABASE_POSTGRES_VERSION_STAGING || '16.11' }} | |
| # Instance Configuration | |
| instance_size: ${{ vars.DATABASE_INSTANCE_SIZE_STAGING || 'db.t4g.small' }} | |
| allocated_storage: ${{ vars.DATABASE_ALLOCATED_STORAGE_STAGING || '20' }} | |
| allocated_max_storage: ${{ vars.DATABASE_MAX_ALLOCATED_STORAGE_STAGING || '100' }} | |
| # High Availability Configuration | |
| multi_az_enabled: ${{ vars.DATABASE_MULTI_AZ_ENABLED_STAGING || 'false' }} | |
| # RDS Proxy Configuration (off by default - flip RDS_PROXY_ENABLED_STAGING to 'true' to turn on) | |
| rds_proxy_enabled: ${{ vars.RDS_PROXY_ENABLED_STAGING || 'false' }} | |
| rds_proxy_max_connections_percent: ${{ vars.RDS_PROXY_MAX_CONNECTIONS_PERCENT_STAGING || '100' }} | |
| rds_proxy_connection_borrow_timeout: ${{ vars.RDS_PROXY_CONNECTION_BORROW_TIMEOUT_STAGING || '120' }} | |
| # Performance Insights (Database Insights Standard mode - off by default in staging) | |
| performance_insights_enabled: ${{ vars.DATABASE_PI_ENABLED_STAGING || 'false' }} | |
| performance_insights_retention_days: ${{ vars.DATABASE_PI_RETENTION_DAYS_STAGING || '7' }} | |
| # Lambda Configuration (container-based) | |
| lambda_image_uri: ${{ needs.build.outputs.lambda_image }} | |
| # Other Configuration | |
| aws_sns_alert_email: ${{ vars.AWS_SNS_ALERT_EMAIL }} | |
| secrets: inherit | |
| deploy-valkey: | |
| needs: [runner, create-deployment, stack-config, build, deploy-vpc] | |
| uses: ./.github/workflows/deploy-valkey.yml | |
| with: | |
| # Stack & Repository Configuration | |
| stack_name: ${{ needs.stack-config.outputs.valkey_stack }} | |
| environment: ${{ vars.ENVIRONMENT_STAGING || 'staging' }} | |
| # GHA Runner Configuration | |
| runner_config: ${{ needs.runner.outputs.runner_config }} | |
| # AWS Configuration | |
| aws_region: ${{ vars.AWS_REGION || 'us-east-1' }} | |
| vpc_id: ${{ needs.deploy-vpc.outputs.vpc_id }} | |
| subnet_ids: ${{ needs.deploy-vpc.outputs.private_subnet_ids }} | |
| # Instance Configuration | |
| node_type: ${{ vars.VALKEY_NODE_TYPE_STAGING || 'cache.t4g.micro' }} | |
| num_cache_nodes: ${{ vars.VALKEY_NUM_NODES_STAGING || '1' }} | |
| # Valkey Version Configuration | |
| valkey_version: ${{ vars.VALKEY_VERSION_STAGING || '8.1' }} | |
| # Security Configuration | |
| encryption_enabled: ${{ vars.VALKEY_ENCRYPTION_ENABLED_STAGING || 'true' }} | |
| # Backup Configuration | |
| snapshot_retention_days: ${{ vars.VALKEY_SNAPSHOT_RETENTION_DAYS_STAGING || '0' }} | |
| # Lambda Configuration (container-based) | |
| lambda_image_uri: ${{ needs.build.outputs.lambda_image }} | |
| # Other Configuration | |
| aws_sns_alert_email: ${{ vars.AWS_SNS_ALERT_EMAIL }} | |
| secrets: inherit | |
| deploy-opensearch: | |
| # Text search disabled by default - set OPENSEARCH_ENABLED_STAGING=true to enable | |
| if: vars.OPENSEARCH_ENABLED_STAGING == 'true' | |
| needs: [runner, create-deployment, stack-config, deploy-vpc, deploy-bastion] | |
| uses: ./.github/workflows/deploy-opensearch.yml | |
| with: | |
| # Stack & Repository Configuration | |
| stack_name: ${{ needs.stack-config.outputs.opensearch_stack }} | |
| environment: ${{ vars.ENVIRONMENT_STAGING || 'staging' }} | |
| # GHA Runner Configuration | |
| runner_config: ${{ needs.runner.outputs.runner_config }} | |
| # AWS Configuration | |
| aws_region: ${{ vars.AWS_REGION || 'us-east-1' }} | |
| vpc_id: ${{ needs.deploy-vpc.outputs.vpc_id }} | |
| subnet_ids: ${{ needs.deploy-vpc.outputs.private_subnet_ids }} | |
| # Instance Configuration | |
| instance_type: ${{ vars.OPENSEARCH_INSTANCE_TYPE_STAGING || 't3.medium.search' }} | |
| instance_count: ${{ vars.OPENSEARCH_INSTANCE_COUNT_STAGING || '1' }} | |
| zone_awareness_enabled: ${{ vars.OPENSEARCH_ZONE_AWARENESS_STAGING || 'false' }} | |
| availability_zone_count: ${{ vars.OPENSEARCH_AZ_COUNT_STAGING || '2' }} | |
| dedicated_master_enabled: ${{ vars.OPENSEARCH_DEDICATED_MASTER_STAGING || 'false' }} | |
| dedicated_master_type: ${{ vars.OPENSEARCH_MASTER_TYPE_STAGING || 't3.small.search' }} | |
| dedicated_master_count: ${{ vars.OPENSEARCH_MASTER_COUNT_STAGING || '3' }} | |
| ebs_volume_size: ${{ vars.OPENSEARCH_EBS_SIZE_STAGING || '100' }} | |
| # Engine Version Configuration | |
| engine_version: ${{ vars.OPENSEARCH_VERSION_STAGING || '2.19' }} | |
| # Bastion Access | |
| bastion_sg_id: ${{ needs.deploy-bastion.outputs.bastion_sg_id }} | |
| # Other Configuration | |
| aws_sns_alert_email: ${{ vars.AWS_SNS_ALERT_EMAIL }} | |
| secrets: inherit | |
| deploy-prometheus: | |
| # Observability disabled by default - set OBSERVABILITY_ENABLED_STAGING=true to enable | |
| if: vars.OBSERVABILITY_ENABLED_STAGING == 'true' | |
| needs: [runner, create-deployment, stack-config, deploy-vpc] | |
| uses: ./.github/workflows/deploy-prometheus.yml | |
| with: | |
| # Stack & Repository Configuration | |
| stack_name: ${{ needs.stack-config.outputs.prometheus_stack }} | |
| environment: ${{ vars.ENVIRONMENT_STAGING || 'staging' }} | |
| # GHA Runner Configuration | |
| runner_config: ${{ needs.runner.outputs.runner_config }} | |
| # AWS Configuration | |
| aws_region: ${{ vars.AWS_REGION || 'us-east-1' }} | |
| secrets: inherit | |
| deploy-grafana: | |
| # Observability disabled by default - set OBSERVABILITY_ENABLED_STAGING=true to enable | |
| if: vars.OBSERVABILITY_ENABLED_STAGING == 'true' | |
| needs: [runner, create-deployment, stack-config] | |
| uses: ./.github/workflows/deploy-grafana.yml | |
| with: | |
| # Stack & Repository Configuration | |
| stack_name: ${{ needs.stack-config.outputs.grafana_stack }} | |
| environment: shared | |
| # GHA Runner Configuration | |
| runner_config: ${{ needs.runner.outputs.runner_config }} | |
| # AWS Configuration | |
| aws_region: ${{ vars.AWS_REGION || 'us-east-1' }} | |
| secrets: inherit | |
| deploy-bastion: | |
| needs: | |
| [ | |
| runner, | |
| create-deployment, | |
| stack-config, | |
| deploy-vpc, | |
| deploy-postgres, | |
| deploy-valkey, | |
| deploy-s3, | |
| ] | |
| uses: ./.github/workflows/deploy-bastion.yml | |
| with: | |
| # Stack & Repository Configuration | |
| stack_name: ${{ needs.stack-config.outputs.bastion_stack }} | |
| environment: ${{ vars.ENVIRONMENT_STAGING || 'staging' }} | |
| # GHA Runner Configuration | |
| runner_config: ${{ needs.runner.outputs.runner_config }} | |
| # AWS Configuration | |
| aws_region: ${{ vars.AWS_REGION || 'us-east-1' }} | |
| vpc_id: ${{ needs.deploy-vpc.outputs.vpc_id }} | |
| public_subnet_ids: ${{ needs.deploy-vpc.outputs.public_subnet_ids }} | |
| # Security Groups (SSM-only access - no SSH parameters needed) | |
| valkey_sg_id: ${{ needs.deploy-valkey.outputs.valkey_sg_id }} | |
| # S3 Bucket Configuration (from S3 stack outputs) | |
| deployment_bucket_arn: ${{ needs.deploy-s3.outputs.deployment_bucket_arn }} | |
| secrets: inherit | |
| # Graph Infrastructure - Step 1: Deploy DynamoDB, Secrets, Lambda monitoring | |
| deploy-graph-infra: | |
| needs: | |
| [runner, create-deployment, stack-config, build, deploy-vpc, deploy-s3] | |
| uses: ./.github/workflows/deploy-graph-infra.yml | |
| with: | |
| # Stack & Repository Configuration | |
| stack_name: ${{ needs.stack-config.outputs.graph_infra_stack }} | |
| environment: ${{ vars.ENVIRONMENT_STAGING || 'staging' }} | |
| # GHA Runner Configuration | |
| runner_config: ${{ needs.runner.outputs.runner_config }} | |
| # AWS Configuration | |
| aws_region: ${{ vars.AWS_REGION || 'us-east-1' }} | |
| # Lambda Configuration (container-based) | |
| lambda_image_uri: ${{ needs.build.outputs.lambda_image }} | |
| # Other Configuration | |
| aws_sns_alert_email: ${{ vars.AWS_SNS_ALERT_EMAIL }} | |
| secrets: inherit | |
| # Graph Infrastructure - Step 2: Deploy EBS volume management | |
| deploy-graph-volumes: | |
| needs: [runner, stack-config, build, deploy-vpc, deploy-graph-infra] | |
| uses: ./.github/workflows/deploy-graph-volumes.yml | |
| with: | |
| # Stack & Repository Configuration | |
| stack_name: ${{ needs.stack-config.outputs.graph_volumes_stack }} | |
| environment: ${{ vars.ENVIRONMENT_STAGING || 'staging' }} | |
| # GHA Runner Configuration | |
| runner_config: ${{ needs.runner.outputs.runner_config }} | |
| # AWS Configuration | |
| aws_region: ${{ vars.AWS_REGION || 'us-east-1' }} | |
| vpc_id: ${{ needs.deploy-vpc.outputs.vpc_id }} | |
| subnet_ids: ${{ needs.deploy-vpc.outputs.private_subnet_ids }} | |
| # Registry Configuration | |
| volume_registry_table: ${{ needs.deploy-graph-infra.outputs.volume_registry_table }} | |
| # Lambda Configuration (container-based) | |
| lambda_image_uri: ${{ needs.build.outputs.lambda_image }} | |
| # Secrets Configuration | |
| graph_api_secret_arn: ${{ needs.deploy-graph-infra.outputs.secret_arn }} | |
| # Other Configuration | |
| aws_sns_alert_email: ${{ vars.AWS_SNS_ALERT_EMAIL }} | |
| secrets: inherit | |
| deploy-graph: | |
| needs: | |
| [ | |
| runner, | |
| create-deployment, | |
| stack-config, | |
| build, | |
| deploy-vpc, | |
| deploy-postgres, | |
| deploy-valkey, | |
| deploy-s3, | |
| deploy-graph-infra, | |
| deploy-graph-volumes, | |
| package-scripts, | |
| ] | |
| uses: ./.github/workflows/deploy-graph.yml | |
| with: | |
| # Environment Configuration | |
| environment: ${{ vars.ENVIRONMENT_STAGING || 'staging' }} | |
| # GHA Runner Configuration | |
| runner_config: ${{ needs.runner.outputs.runner_config }} | |
| # AWS Configuration | |
| aws_region: ${{ vars.AWS_REGION || 'us-east-1' }} | |
| vpc_id: ${{ needs.deploy-vpc.outputs.vpc_id }} | |
| subnet_ids: ${{ needs.deploy-vpc.outputs.private_subnet_ids }} | |
| public_subnet_ids: ${{ needs.deploy-vpc.outputs.public_subnet_ids }} | |
| valkey_sg_id: ${{ needs.deploy-valkey.outputs.valkey_sg_id }} | |
| valkey_url: ${{ needs.deploy-valkey.outputs.valkey_url }} | |
| database_endpoint: ${{ needs.deploy-postgres.outputs.database_endpoint }} | |
| database_port: ${{ needs.deploy-postgres.outputs.database_port }} | |
| ami_id: ${{ needs.stack-config.outputs.graph_ami_id }} | |
| # Container Configuration - use staging tag for EC2/ASG (container refresh handles updates) | |
| ecr_image_tag: staging | |
| # SEC shared replicas track the moving staging tag by default (no per-deploy | |
| # ECRImageTag change → CloudFormation doesn't cycle the fleet; the daily | |
| # shared_replicas_refresh is the only routine boot). Leave | |
| # shared_replica_image_tag unset for that. Pin it ONLY during a | |
| # storage-format-breaking engine upgrade, so a boot can't pull a new engine | |
| # before the new-format sec.lbug is published — set a | |
| # SHARED_REPLICA_IMAGE_TAG_STAGING var for the migration window, then clear it. | |
| shared_replica_image_tag: ${{ vars.SHARED_REPLICA_IMAGE_TAG_STAGING || '' }} | |
| # Infrastructure inputs from graph infrastructure jobs | |
| secret_arn: ${{ needs.deploy-graph-infra.outputs.secret_arn }} | |
| volume_registry_table: ${{ needs.deploy-graph-infra.outputs.volume_registry_table }} | |
| volume_manager_function_arn: ${{ needs.deploy-graph-volumes.outputs.volume_manager_function_arn }} | |
| volume_detachment_topic_arn: ${{ needs.deploy-graph-volumes.outputs.volume_detachment_topic_arn }} | |
| # Writer placement. Every writer tier sits in ONE AZ (EBS volume affinity). | |
| # The graph_writer_az dispatch input MOVES the tiers — a deliberate, per-run | |
| # act. The GRAPH_WRITER_AZ_STAGING var SUSTAINS the AZ across the input-less | |
| # dispatches from create-release / graph-maintenance and may only name the | |
| # AZ the tiers already occupy (a mismatch fails the deploy). Both unset → | |
| # <region>c, unchanged behaviour. | |
| writer_az: ${{ inputs.graph_writer_az || vars.GRAPH_WRITER_AZ_STAGING || '' }} | |
| writer_az_change_allowed: ${{ (inputs.graph_writer_az != '' && 'true') || 'false' }} | |
| # Graph Tier Configuration | |
| lbug_large_enabled: ${{ vars.LBUG_LARGE_ENABLED_STAGING || 'false' }} | |
| lbug_xlarge_enabled: ${{ vars.LBUG_XLARGE_ENABLED_STAGING || 'false' }} | |
| lbug_shared_enabled: ${{ vars.LBUG_SHARED_ENABLED_STAGING || 'false' }} | |
| # Graph Scaling Configuration | |
| lbug_standard_min_instances: ${{ vars.LBUG_STANDARD_MIN_INSTANCES_STAGING }} | |
| lbug_standard_max_instances: ${{ vars.LBUG_STANDARD_MAX_INSTANCES_STAGING }} | |
| lbug_large_min_instances: ${{ vars.LBUG_LARGE_MIN_INSTANCES_STAGING }} | |
| lbug_large_max_instances: ${{ vars.LBUG_LARGE_MAX_INSTANCES_STAGING }} | |
| lbug_xlarge_min_instances: ${{ vars.LBUG_XLARGE_MIN_INSTANCES_STAGING }} | |
| lbug_xlarge_max_instances: ${{ vars.LBUG_XLARGE_MAX_INSTANCES_STAGING }} | |
| lbug_shared_min_instances: ${{ vars.LBUG_SHARED_MIN_INSTANCES_STAGING }} | |
| lbug_shared_max_instances: ${{ vars.LBUG_SHARED_MAX_INSTANCES_STAGING }} | |
| # Shared Replicas Configuration | |
| shared_replicas_enabled: ${{ vars.SHARED_REPLICAS_ENABLED_STAGING || 'false' }} | |
| shared_replicas_min_instances: ${{ vars.SHARED_REPLICAS_MIN_INSTANCES_STAGING || '1' }} | |
| shared_replicas_max_instances: ${{ vars.SHARED_REPLICAS_MAX_INSTANCES_STAGING || '3' }} | |
| shared_replicas_desired_capacity: ${{ vars.SHARED_REPLICAS_DESIRED_CAPACITY_STAGING || '1' }} | |
| shared_replicas_root_volume_size: ${{ vars.SHARED_REPLICAS_ROOT_VOLUME_SIZE_STAGING || '100' }} | |
| shared_repositories: ${{ vars.SHARED_REPOSITORIES_STAGING || 'sec' }} | |
| shared_replicas_cpu_target: ${{ vars.SHARED_REPLICAS_CPU_TARGET_STAGING || '70' }} | |
| shared_replicas_memory_target: ${{ vars.SHARED_REPLICAS_MEMORY_TARGET_STAGING || '80' }} | |
| shared_replicas_enable_response_time_scaling: ${{ vars.SHARED_REPLICAS_ENABLE_RESPONSE_TIME_SCALING_STAGING || 'false' }} | |
| shared_replicas_response_time_target: ${{ vars.SHARED_REPLICAS_RESPONSE_TIME_TARGET_STAGING || '5' }} | |
| shared_replicas_instance_warmup: ${{ vars.SHARED_REPLICAS_INSTANCE_WARMUP_STAGING || '900' }} | |
| shared_replicas_health_check_grace_period: ${{ vars.SHARED_REPLICAS_HEALTH_CHECK_GRACE_PERIOD_STAGING || '900' }} | |
| # Shared Replicas Spot Configuration | |
| shared_replicas_spot_enabled: ${{ vars.SHARED_REPLICAS_SPOT_ENABLED_STAGING || 'false' }} | |
| shared_replicas_od_base: ${{ vars.SHARED_REPLICAS_OD_BASE_STAGING || '0' }} | |
| shared_replicas_spot_weight: ${{ vars.SHARED_REPLICAS_SPOT_WEIGHT_STAGING || '0' }} | |
| # Other Configuration | |
| aws_sns_alert_email: ${{ vars.AWS_SNS_ALERT_EMAIL }} | |
| secrets: inherit | |
| deploy-api: | |
| needs: | |
| [ | |
| runner, | |
| create-deployment, | |
| stack-config, | |
| build, | |
| deploy-vpc, | |
| deploy-valkey, | |
| deploy-postgres, | |
| deploy-s3, | |
| deploy-grafana, | |
| deploy-prometheus, | |
| deploy-opensearch, | |
| deploy-bastion, | |
| ] | |
| if: ${{ !cancelled() && !contains(needs.*.result, 'cancelled') && !contains(needs.*.result, 'failure') }} | |
| uses: ./.github/workflows/deploy-api.yml | |
| with: | |
| # Stack & Repository Configuration | |
| stack_name: ${{ needs.stack-config.outputs.api_stack }} | |
| environment: ${{ vars.ENVIRONMENT_STAGING || 'staging' }} | |
| # GHA Runner Configuration | |
| runner_config: ${{ needs.runner.outputs.runner_config }} | |
| # AWS Configuration | |
| aws_region: ${{ vars.AWS_REGION || 'us-east-1' }} | |
| vpc_id: ${{ needs.deploy-vpc.outputs.vpc_id }} | |
| subnet_ids: ${{ needs.deploy-vpc.outputs.private_subnet_ids }} | |
| public_subnet_ids: ${{ needs.deploy-vpc.outputs.public_subnet_ids }} | |
| # Container & Application Configuration | |
| ecr_repository_url: ${{ needs.build.outputs.ecr_repository_url }} | |
| ecr_image_tag: ${{ needs.build.outputs.image_ref }} | |
| # ECS & Compute Configuration | |
| cpu: ${{ vars.API_CPU_STAGING || '512' }} | |
| memory: ${{ vars.API_MEMORY_STAGING || '1024' }} | |
| fargate_spot_weight: ${{ vars.API_FARGATE_SPOT_WEIGHT_STAGING || '90' }} | |
| fargate_base: ${{ vars.API_FARGATE_BASE_STAGING || '0' }} | |
| # Auto-scaling Configuration | |
| min_capacity: ${{ vars.API_MIN_CAPACITY_STAGING || '1' }} | |
| max_capacity: ${{ vars.API_MAX_CAPACITY_STAGING || '2' }} | |
| cpu_target_value: ${{ vars.API_CPU_TARGET_STAGING || '70' }} | |
| memory_target_value: ${{ vars.API_MEMORY_TARGET_STAGING || '80' }} | |
| # Access Mode & Domain Configuration | |
| # API_ACCESS_MODE_STAGING: 'public' (HTTPS with domain) or 'internal' (SSM tunnel) | |
| api_access_mode: ${{ vars.API_ACCESS_MODE_STAGING || 'internal' }} | |
| domain_name: ${{ vars.API_DOMAIN_NAME_STAGING || '' }} | |
| domain_name_root: ${{ needs.stack-config.outputs.domain_name_root }} | |
| # Database Configuration (from Postgres stack outputs) | |
| # Uses RDS Proxy endpoint when RDS_PROXY_ENABLED_STAGING=true, direct endpoint otherwise. | |
| # The proxy output is an empty string when the flag is off, which GHA's || coalesce treats as falsy. | |
| database_endpoint: ${{ needs.deploy-postgres.outputs.database_proxy_endpoint || needs.deploy-postgres.outputs.database_endpoint }} | |
| database_port: ${{ needs.deploy-postgres.outputs.database_port }} | |
| # Lambda Configuration (container-based) | |
| lambda_image_uri: ${{ needs.build.outputs.lambda_image }} | |
| # Cache Configuration | |
| valkey_url: ${{ needs.deploy-valkey.outputs.valkey_url }} | |
| valkey_sg_id: ${{ needs.deploy-valkey.outputs.valkey_sg_id }} | |
| bastion_sg_id: ${{ needs.deploy-bastion.outputs.bastion_sg_id }} | |
| # OpenSearch Configuration (optional, empty when OPENSEARCH_ENABLED_STAGING=false) | |
| opensearch_endpoint: ${{ needs.deploy-opensearch.outputs.opensearch_endpoint || '' }} | |
| opensearch_sg_id: ${{ needs.deploy-opensearch.outputs.opensearch_sg_id || '' }} | |
| # WAF Configuration (environment-specific, skipped for internal-only deployments) | |
| waf_enabled: ${{ vars.WAF_ENABLED_STAGING || 'false' }} | |
| waf_stack_name: ${{ needs.stack-config.outputs.waf_stack }} | |
| waf_rate_limit_per_ip: ${{ vars.WAF_RATE_LIMIT_PER_IP || '3000' }} | |
| waf_enable_geo_blocking: ${{ vars.WAF_GEO_BLOCKING_ENABLED || 'false' }} | |
| waf_enable_aws_managed_rules: ${{ vars.WAF_AWS_MANAGED_RULES_ENABLED || 'true' }} | |
| audit_enabled: ${{ vars.AUDIT_ENABLED_STAGING || 'false' }} | |
| audit_stack_name: ${{ needs.stack-config.outputs.audit_stack }} | |
| audit_retention_days: ${{ vars.AUDIT_RETENTION_DAYS || '400' }} | |
| # S3 Bucket Configuration (from S3 stack outputs) | |
| shared_processed_bucket_arn: ${{ needs.deploy-s3.outputs.shared_processed_bucket_arn }} | |
| deployment_bucket_arn: ${{ needs.deploy-s3.outputs.deployment_bucket_arn }} | |
| user_data_bucket_arn: ${{ needs.deploy-s3.outputs.user_data_bucket_arn }} | |
| public_data_bucket_arn: ${{ needs.deploy-s3.outputs.public_data_bucket_arn }} | |
| # Other Configuration | |
| prometheus_stack_name: ${{ needs.deploy-prometheus.outputs.prometheus_stack_name || '' }} | |
| aws_sns_alert_email: ${{ vars.AWS_SNS_ALERT_EMAIL }} | |
| robosystems_app_url: ${{ vars.ROBOSYSTEMS_APP_URL_STAGING || 'https://staging.robosystems.ai' }} | |
| roboledger_app_url: ${{ vars.ROBOLEDGER_APP_URL_STAGING || 'https://staging.roboledger.ai' }} | |
| roboinvestor_app_url: ${{ vars.ROBOINVESTOR_APP_URL_STAGING || 'https://staging.roboinvestor.ai' }} | |
| secrets: inherit | |
| deploy-dagster: | |
| needs: | |
| [ | |
| runner, | |
| create-deployment, | |
| stack-config, | |
| build, | |
| deploy-vpc, | |
| deploy-valkey, | |
| deploy-postgres, | |
| deploy-s3, | |
| deploy-grafana, | |
| deploy-prometheus, | |
| deploy-opensearch, | |
| deploy-bastion, | |
| ] | |
| if: ${{ !cancelled() && !contains(needs.*.result, 'cancelled') && !contains(needs.*.result, 'failure') }} | |
| uses: ./.github/workflows/deploy-dagster.yml | |
| with: | |
| # Stack & Repository Configuration | |
| stack_name: ${{ needs.stack-config.outputs.dagster_stack }} | |
| environment: ${{ vars.ENVIRONMENT_STAGING || 'staging' }} | |
| # GHA Runner Configuration | |
| runner_config: ${{ needs.runner.outputs.runner_config }} | |
| # AWS Configuration | |
| aws_region: ${{ vars.AWS_REGION || 'us-east-1' }} | |
| vpc_id: ${{ needs.deploy-vpc.outputs.vpc_id }} | |
| subnet_ids: ${{ needs.deploy-vpc.outputs.private_subnet_ids }} | |
| # Container & Application Configuration | |
| ecr_repository_url: ${{ needs.build.outputs.ecr_repository_url }} | |
| ecr_image_tag: ${{ needs.build.outputs.image_ref }} | |
| # Fargate Configuration | |
| daemon_cpu: ${{ vars.DAGSTER_DAEMON_CPU_STAGING || '1024' }} | |
| daemon_memory: ${{ vars.DAGSTER_DAEMON_MEMORY_STAGING || '2048' }} | |
| webserver_cpu: ${{ vars.DAGSTER_WEBSERVER_CPU_STAGING || '512' }} | |
| webserver_memory: ${{ vars.DAGSTER_WEBSERVER_MEMORY_STAGING || '1024' }} | |
| webserver_desired_count: ${{ vars.DAGSTER_WEBSERVER_DESIRED_COUNT_STAGING || '0' }} | |
| # Run Job Profiles: hardcoded in deploy-dagster.yml (Light/Standard/Compute/Heavy) | |
| max_concurrent_runs: ${{ vars.DAGSTER_MAX_CONCURRENT_RUNS_STAGING || '20' }} | |
| # Worker Configuration | |
| worker_stack_name: ${{ needs.stack-config.outputs.worker_stack }} | |
| graph_volumes_stack_name: ${{ needs.stack-config.outputs.graph_volumes_stack }} | |
| worker_enabled: ${{ vars.WORKER_ENABLED_STAGING || 'false' }} | |
| worker_cpu: ${{ vars.WORKER_CPU_STAGING || '512' }} | |
| worker_memory: ${{ vars.WORKER_MEMORY_STAGING || '1024' }} | |
| worker_desired_count: ${{ vars.WORKER_DESIRED_COUNT_STAGING || '1' }} | |
| worker_min_count: ${{ vars.WORKER_MIN_COUNT_STAGING || '1' }} | |
| worker_max_count: ${{ vars.WORKER_MAX_COUNT_STAGING || '5' }} | |
| worker_autoscaling_enabled: ${{ vars.WORKER_AUTOSCALING_ENABLED_STAGING || 'false' }} | |
| aws_sns_alert_email: ${{ vars.AWS_SNS_ALERT_EMAIL }} | |
| # Capacity Provider Configuration (Spot vs On-Demand) | |
| daemon_fargate_spot_weight: ${{ vars.DAGSTER_DAEMON_FARGATE_SPOT_WEIGHT_STAGING || '80' }} | |
| webserver_fargate_spot_weight: ${{ vars.DAGSTER_WEBSERVER_FARGATE_SPOT_WEIGHT_STAGING || '80' }} | |
| daemon_fargate_base: ${{ vars.DAGSTER_DAEMON_FARGATE_BASE_STAGING || '0' }} | |
| webserver_fargate_base: ${{ vars.DAGSTER_WEBSERVER_FARGATE_BASE_STAGING || '0' }} | |
| # Database Configuration (from Postgres stack outputs) | |
| # Uses RDS Proxy endpoint when RDS_PROXY_ENABLED_STAGING=true, direct endpoint otherwise. | |
| # Dagster daemon connections will pin on the proxy (LISTEN/NOTIFY, advisory locks) so | |
| # they get no proxy benefit but pinning is neutral. The embedded worker stack benefits. | |
| database_endpoint: ${{ needs.deploy-postgres.outputs.database_proxy_endpoint || needs.deploy-postgres.outputs.database_endpoint }} | |
| database_port: ${{ needs.deploy-postgres.outputs.database_port }} | |
| # Cache Configuration | |
| valkey_url: ${{ needs.deploy-valkey.outputs.valkey_url }} | |
| valkey_sg_id: ${{ needs.deploy-valkey.outputs.valkey_sg_id }} | |
| # Database Security Group | |
| database_sg_id: ${{ needs.deploy-postgres.outputs.database_sg_id }} | |
| # OpenSearch Configuration (optional, empty when OPENSEARCH_ENABLED_STAGING=false) | |
| opensearch_endpoint: ${{ needs.deploy-opensearch.outputs.opensearch_endpoint || '' }} | |
| opensearch_sg_id: ${{ needs.deploy-opensearch.outputs.opensearch_sg_id || '' }} | |
| # Bastion Access (for internal UI access) | |
| bastion_sg_id: ${{ needs.deploy-bastion.outputs.bastion_sg_id }} | |
| # Monitoring Configuration | |
| container_insights_enabled: ${{ vars.DAGSTER_CONTAINER_INSIGHTS_STAGING || 'disabled' }} | |
| # S3 Bucket Configuration (from S3 stack outputs) | |
| shared_raw_bucket_arn: ${{ needs.deploy-s3.outputs.shared_raw_bucket_arn }} | |
| shared_processed_bucket_arn: ${{ needs.deploy-s3.outputs.shared_processed_bucket_arn }} | |
| deployment_bucket_arn: ${{ needs.deploy-s3.outputs.deployment_bucket_arn }} | |
| user_data_bucket_arn: ${{ needs.deploy-s3.outputs.user_data_bucket_arn }} | |
| public_data_bucket_arn: ${{ needs.deploy-s3.outputs.public_data_bucket_arn }} | |
| logs_bucket_arn: ${{ needs.deploy-s3.outputs.logs_bucket_arn }} | |
| public_data_cdn_url: ${{ needs.deploy-s3.outputs.public_data_cdn_url }} | |
| robosystems_app_url: ${{ vars.ROBOSYSTEMS_APP_URL_STAGING || 'https://staging.robosystems.ai' }} | |
| roboledger_app_url: ${{ vars.ROBOLEDGER_APP_URL_STAGING || 'https://staging.roboledger.ai' }} | |
| roboinvestor_app_url: ${{ vars.ROBOINVESTOR_APP_URL_STAGING || 'https://staging.roboinvestor.ai' }} | |
| secrets: inherit | |
| # Service refresh - currently only graph containers (ECS services auto-deploy via version tags) | |
| service-refresh: | |
| needs: [runner, create-deployment, stack-config, deploy-graph] | |
| if: ${{ !cancelled() && needs.deploy-graph.result == 'success' }} | |
| uses: ./.github/workflows/service-refresh.yml | |
| with: | |
| environment: ${{ vars.ENVIRONMENT_STAGING || 'staging' }} | |
| runner_config: ${{ needs.runner.outputs.runner_config }} | |
| aws_region: ${{ vars.AWS_REGION || 'us-east-1' }} | |
| # Graph refresh (EC2 containers via SSM) | |
| graph_refresh_enabled: ${{ inputs.graph_container_refresh != false }} | |
| graph_node_types: "writer" | |
| # Pre-refresh busy-counter wait (protects in-flight materialization | |
| # on the shared-tier SEC master from mid-op cycling) | |
| max_wait_minutes: ${{ inputs.graph_refresh_max_wait_minutes || '30' }} | |
| force_ignore_busy: ${{ (inputs.graph_refresh_force_ignore_busy && 'true') || 'false' }} | |
| # API/Dagster/Worker refresh disabled - ECS auto-deploys via version tags in task definitions | |
| api_refresh_enabled: "false" | |
| api_stack_name: ${{ needs.stack-config.outputs.api_stack }} | |
| dagster_refresh_enabled: "false" | |
| worker_refresh_enabled: "false" | |
| secrets: inherit | |
| deployment-successful: | |
| needs: | |
| [ | |
| runner, | |
| build, | |
| stack-config, | |
| create-deployment, | |
| deploy-vpc, | |
| deploy-graph, | |
| deploy-valkey, | |
| deploy-postgres, | |
| deploy-s3, | |
| deploy-grafana, | |
| deploy-prometheus, | |
| deploy-opensearch, | |
| deploy-bastion, | |
| deploy-api, | |
| deploy-dagster, | |
| service-refresh, | |
| ] | |
| if: | | |
| always() && | |
| needs.runner.result == 'success' && | |
| needs.build.result == 'success' && | |
| needs.stack-config.result == 'success' && | |
| needs.create-deployment.result == 'success' && | |
| needs.deploy-vpc.result == 'success' && | |
| needs.deploy-graph.result == 'success' && | |
| needs.deploy-valkey.result == 'success' && | |
| needs.deploy-postgres.result == 'success' && | |
| needs.deploy-s3.result == 'success' && | |
| (needs.deploy-grafana.result == 'success' || needs.deploy-grafana.result == 'skipped') && | |
| (needs.deploy-prometheus.result == 'success' || needs.deploy-prometheus.result == 'skipped') && | |
| (needs.deploy-opensearch.result == 'success' || needs.deploy-opensearch.result == 'skipped') && | |
| needs.deploy-bastion.result == 'success' && | |
| needs.deploy-api.result == 'success' && | |
| needs.deploy-dagster.result == 'success' && | |
| (needs.service-refresh.result == 'success' || needs.service-refresh.result == 'skipped') | |
| runs-on: ${{ fromJSON(needs.runner.outputs.runner_config) }} | |
| outputs: | |
| deployment_id: ${{ needs.create-deployment.outputs.deployment_id }} | |
| steps: | |
| - name: Checkout Code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| with: | |
| repository: ${{ github.repository }} | |
| ref: ${{ github.ref }} | |
| token: ${{ github.token }} | |
| - name: Generate Deployment Summary | |
| env: | |
| API_ACCESS_MODE: ${{ vars.API_ACCESS_MODE_STAGING || 'internal' }} | |
| API_DOMAIN: ${{ vars.API_DOMAIN_NAME_STAGING || '' }} | |
| LBUG_LARGE_ENABLED: ${{ vars.LBUG_LARGE_ENABLED_STAGING || 'false' }} | |
| LBUG_XLARGE_ENABLED: ${{ vars.LBUG_XLARGE_ENABLED_STAGING || 'false' }} | |
| LBUG_SHARED_ENABLED: ${{ vars.LBUG_SHARED_ENABLED_STAGING || 'false' }} | |
| run: | | |
| cat >> $GITHUB_STEP_SUMMARY << EOF | |
| ## Staging Deployment Successful | |
| | Component | Details | | |
| |-----------|---------| | |
| | Environment | staging | | |
| | Image Tag | \`${{ needs.build.outputs.image_tag }}\` | | |
| | Image | \`${{ needs.build.outputs.image }}\` | | |
| | Commit | \`${{ github.sha }}\` | | |
| | Ref | \`${{ github.ref_name }}\` | | |
| | Triggered by | @${{ github.actor }} | | |
| ### API Access | |
| EOF | |
| if [ "$API_ACCESS_MODE" = "public" ] && [ -n "$API_DOMAIN" ]; then | |
| cat >> $GITHUB_STEP_SUMMARY << EOF | |
| - **Mode**: Public (HTTPS) | |
| - **URL**: https://${API_DOMAIN} | |
| - **Admin CLI**: \`just admin staging <command>\` (requires \`just tunnel staging api-internal\`; the CLI targets the tunnel on :18000) | |
| EOF | |
| else | |
| cat >> $GITHUB_STEP_SUMMARY << EOF | |
| - **Mode**: Internal (SSM tunnel required) | |
| - **Connect**: \`just tunnel staging all\` | |
| - **API**: http://localhost:18000 | |
| - **Admin CLI**: \`just admin staging <command>\` | |
| EOF | |
| fi | |
| cat >> $GITHUB_STEP_SUMMARY << EOF | |
| ### LadybugDB Graph Clusters | |
| | Tier | Status | | |
| |------|--------| | |
| | Standard | Enabled | | |
| EOF | |
| if [ "$LBUG_LARGE_ENABLED" = "true" ]; then | |
| echo "| Large | Enabled |" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| if [ "$LBUG_XLARGE_ENABLED" = "true" ]; then | |
| echo "| XLarge | Enabled |" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| if [ "$LBUG_SHARED_ENABLED" = "true" ]; then | |
| echo "| Shared | Enabled |" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| cat >> $GITHUB_STEP_SUMMARY << EOF | |
| ### All Services | |
| Connect to all services with one command: | |
| \`\`\` | |
| just tunnel staging all | |
| \`\`\` | |
| | Service | Local Port | | |
| |---------|------------| | |
| | API | http://localhost:18000 | | |
| | Dagster UI | http://localhost:18002 | | |
| | PostgreSQL | localhost:15432 | | |
| | Valkey | localhost:16379 | | |
| ### Next Steps | |
| - [Bootstrap Guide](https://robosystems.ai/docs/technical/bootstrap-guide) - Initial setup | |
| - [Architecture Overview](https://robosystems.ai/docs/technical/architecture-overview) - System architecture | |
| EOF | |
| - name: Update Deployment Status | |
| uses: chrnorm/deployment-status@6df8d036fd2fee9eb82936733953da1f8382b41e # v2 | |
| with: | |
| token: ${{ github.token }} | |
| state: success | |
| deployment-id: ${{ needs.create-deployment.outputs.deployment_id }} | |
| description: "Staging Deployment Succeeded" | |
| handle-deployment-failure: | |
| needs: | |
| [ | |
| runner, | |
| create-deployment, | |
| deploy-vpc, | |
| deploy-graph, | |
| deploy-valkey, | |
| deploy-postgres, | |
| deploy-s3, | |
| deploy-grafana, | |
| deploy-prometheus, | |
| deploy-bastion, | |
| deploy-api, | |
| deploy-dagster, | |
| service-refresh, | |
| ] | |
| if: always() && contains(needs.*.result, 'failure') | |
| runs-on: ${{ fromJSON(needs.runner.outputs.runner_config) }} | |
| steps: | |
| - name: Checkout Code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| with: | |
| repository: ${{ github.repository }} | |
| ref: ${{ github.ref }} | |
| token: ${{ github.token }} | |
| - name: Handle Deployment Failure | |
| uses: ./.github/actions/handle-deployment-failure | |
| with: | |
| deployment-id: ${{ needs.create-deployment.outputs.deployment_id }} | |
| environment: staging | |
| github-token: ${{ github.token }} |