Skip to content

add ecs web pre & post deploy commands#20

Merged
flybayer merged 2 commits into
mainfrom
bb-ecs-prepost
Jun 12, 2026
Merged

add ecs web pre & post deploy commands#20
flybayer merged 2 commits into
mainfrom
bb-ecs-prepost

Conversation

@flybayer

@flybayer flybayer commented Jun 11, 2026

Copy link
Copy Markdown
Member

Greptile Summary

This PR adds optional pre-deploy and post-deploy ECS task hooks to the rvn-ecs-web module (v0.3.0 → 0.4.0). When enabled, each hook runs a one-off ECS task using the same image, task definition, roles, networking, and capacity provider strategy as the web service.

  • New inputs (14 total): toggle, command array, extra environment variables, CPU, memory, ephemeral storage, and timeout fields for both pre- and post-deploy phases, each conditionally shown behind their respective enable toggle.
  • New outputs: pre_deploy and post_deploy expressions that construct the full RunTask override object (container command, CPU/memory fallback to the app task values, network config, capacity provider strategy) or return nil when disabled or the command is empty.
  • Documentation: reference table and prose in the module's UI section updated to describe the new fields and their semantics.

Confidence Score: 4/5

The pre/post deploy hooks are self-contained additions; existing behaviour is unchanged and the new code paths are guarded by both the enable toggle and a command-length check.

CPU/memory fallback logic, capacity provider strategy, and network configuration in the new pre_deploy/post_deploy output expressions all mirror the main task definition correctly. The one minor inconsistency is that pre_deploy_environment_variables and post_deploy_environment_variables are passed to the container override without the nil-guard used consistently elsewhere for optional array inputs.

compute/ecs_service/rvn-ecs-web-definition.yml — specifically the container override environment field for both hook expressions (lines 1515 and 1536).

Important Files Changed

Filename Overview
compute/ecs_service/rvn-ecs-web-definition.yml Adds pre- and post-deploy ECS task inputs and output expressions (version 0.3.0 → 0.4.0); CPU/memory/network logic is consistent with the main task definition, with a minor nil-guard inconsistency on the environment variable arrays in the container overrides.

Sequence Diagram

sequenceDiagram
    participant FC as Flightcontrol
    participant ECS as ECS RunTask (pre_deploy)
    participant SVC as ECS UpdateService
    participant ECS2 as ECS RunTask (post_deploy)

    FC->>ECS: "RunTask (pre_deploy_enabled && command.len > 0)"
    ECS-->>FC: task exits (success / failure)
    Note over FC,ECS: waits up to pre_deploy_timeout (default 1800s)
    FC->>SVC: UpdateService with new task definition
    SVC-->>FC: deployment stabilises
    FC->>ECS2: "RunTask (post_deploy_enabled && command.len > 0)"
    ECS2-->>FC: task exits (success / failure)
    Note over FC,ECS2: waits up to post_deploy_timeout (default 1800s)
Loading
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
compute/ecs_service/rvn-ecs-web-definition.yml:1515-1516
**Missing nil-guard on `environment` array in container override**

Throughout this file, optional array inputs that may be nil are wrapped with a nil-check before use (e.g. `module.input.security_group_ids != nil ? module.input.security_group_ids : []`). Both `pre_deploy_environment_variables` and `post_deploy_environment_variables` (line 1536) are passed directly as `"environment": module.input.pre_deploy_environment_variables` without that guard. While `default: []` makes nil unlikely in practice, it diverges from the defensive pattern in the rest of the file and could surface as a nil environment override if the field is ever unset via direct API input.

Reviews (2): Last reviewed commit: "add ecs web pre & post deploy commands" | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown

Ravion Module Publish Plan

Dry run only. No Ravion API mutations were made.

Module Current Version New Version Description
rvn-ecs-web 0.3.0 0.4.0 Add pre- and post-deploy commands.

Diffs

rvn-ecs-web 0.3.0 -> 0.4.0

--- remote
+++ compiled
       placeholder: sha256:... or latest or <run-id>-nixpacks
       required: true
       type: string
+  post_deploy: '<< module.input.post_deploy_enabled && len(module.input.post_deploy_command) > 0 ? {"container_overrides": [{"name": stack.output.container_name, "command": module.input.post_deploy_command, "environment": module.input.post_deploy_environment_variables, "cpu": module.input.post_deploy_cpu || nil, "memory": module.input.post_deploy_memory || nil}], "cpu": string(module.input.post_deploy_cpu || (module.input.capacity_provider == "ec2" ? int(float(module.input.task_cpu) * 1024) : int(float(module.input.fargate_size.vcpu) * 1024))), "memory": string(module.input.post_deploy_memory || (module.input.capacity_provider == "ec2" ? int(float(module.input.task_memory) * 1024) : int(float(module.input.fargate_size.memory_gb) * 1024))), "ephemeral_storage": (module.input.post_deploy_ephemeral_storage_size_gib ? {size_in_gib: module.input.post_deploy_ephemeral_storage_size_gib} : nil), "task_role_arn": stack.output.task_role_arn, "execution_role_arn": stack.output.execution_role_arn, "capacity_provider_strategy": ((module.input.capacity_provider == "fargate" || module.input.additional_fargate_capacity_enabled ? [{capacity_provider: module.input.fargate_capacity_provider_name, weight: 1, base: 0}] : []) | concat(module.input.capacity_provider == "fargate_spot" || module.input.additional_fargate_spot_capacity_enabled ? [{capacity_provider: module.input.fargate_spot_capacity_provider_name, weight: 1, base: 0}] : []) | concat(module.input.capacity_provider == "ec2" || module.input.additional_ec2_capacity_enabled ? [{capacity_provider: module.input.ec2_capacity_provider_name, weight: 1, base: 0}] : [])), "network_configuration": {"awsvpc_configuration": {"subnets": (module.input.private_subnet_placement_enabled ? module.input.private_subnet_ids : module.input.public_subnet_ids), "security_groups": ([stack.output.security_group_id] | concat(module.input.security_group_ids != nil ? module.input.security_group_ids : [])), "assign_public_ip": (module.input.private_subnet_placement_enabled ? "DISABLED" : "ENABLED")}}, "enable_execute_command": module.input.execute_command_enabled, "timeout": module.input.post_deploy_timeout} : nil >>'
+  pre_deploy: '<< module.input.pre_deploy_enabled && len(module.input.pre_deploy_command) > 0 ? {"container_overrides": [{"name": stack.output.container_name, "command": module.input.pre_deploy_command, "environment": module.input.pre_deploy_environment_variables, "cpu": module.input.pre_deploy_cpu || nil, "memory": module.input.pre_deploy_memory || nil}], "cpu": string(module.input.pre_deploy_cpu || (module.input.capacity_provider == "ec2" ? int(float(module.input.task_cpu) * 1024) : int(float(module.input.fargate_size.vcpu) * 1024))), "memory": string(module.input.pre_deploy_memory || (module.input.capacity_provider == "ec2" ? int(float(module.input.task_memory) * 1024) : int(float(module.input.fargate_size.memory_gb) * 1024))), "ephemeral_storage": (module.input.pre_deploy_ephemeral_storage_size_gib ? {size_in_gib: module.input.pre_deploy_ephemeral_storage_size_gib} : nil), "task_role_arn": stack.output.task_role_arn, "execution_role_arn": stack.output.execution_role_arn, "capacity_provider_strategy": ((module.input.capacity_provider == "fargate" || module.input.additional_fargate_capacity_enabled ? [{capacity_provider: module.input.fargate_capacity_provider_name, weight: 1, base: 0}] : []) | concat(module.input.capacity_provider == "fargate_spot" || module.input.additional_fargate_spot_capacity_enabled ? [{capacity_provider: module.input.fargate_spot_capacity_provider_name, weight: 1, base: 0}] : []) | concat(module.input.capacity_provider == "ec2" || module.input.additional_ec2_capacity_enabled ? [{capacity_provider: module.input.ec2_capacity_provider_name, weight: 1, base: 0}] : [])), "network_configuration": {"awsvpc_configuration": {"subnets": (module.input.private_subnet_placement_enabled ? module.input.private_subnet_ids : module.input.public_subnet_ids), "security_groups": ([stack.output.security_group_id] | concat(module.input.security_group_ids != nil ? module.input.security_group_ids : [])), "assign_public_ip": (module.input.private_subnet_placement_enabled ? "DISABLED" : "ENABLED")}}, "enable_execute_command": module.input.execute_command_enabled, "timeout": module.input.pre_deploy_timeout} : nil >>'
   task_definition:
     container_definitions: "<< (module.input.firelens_enabled ? [{\"cpu\": (module.input.capacity_provider == \"ec2\" ? int(float(module.input.task_cpu) * 1024) : int(float(module.input.fargate_size.vcpu) * 1024)), \"depends_on\": [{\"container_name\": \"log_router\", \"condition\": \"START\"}], \"environment\": ([{name: \"PORT\", value: string(module.input.container_port)}] | concat(module.input.environment_variables != nil ? module.input.environment_variables : [])), \"essential\": true, \"image\": (module.input.build_type == \"prebuilt_image\" ? (deploy.input.image_ref contains \"sha256:\" ? module.input.image_repository + \"@\" + deploy.input.image_ref : module.input.image_repository + \":\" + deploy.input.image_ref) : (deploy.input.image_ref contains \"sha256:\" ? stack.output.ecr_repository_url + \"@\" + deploy.input.image_ref : stack.output.ecr_repository_url + \":\" + deploy.input.image_ref)), \"linux_parameters\": {\"init_process_enabled\": true}, \"log_configuration\": {\"log_driver\": \"awsfirelens\"}, \"memory\": (module.input.capacity_provider == \"ec2\" ? int(float(module.input.task_memory) * 1024) : int(float(module.input.fargate_size.memory_gb) * 1024)), \"name\": (stack.output.container_name), \"port_mappings\": [{\"app_protocol\": \"http\", \"container_port\": (module.input.container_port), \"protocol\": \"tcp\"}], \"readonly_root_filesystem\": false, \"repository_credentials\": (module.input.image_registry_credentials_secret_arn ? {credentials_parameter: module.input.image_registry_credentials_secret_arn} : nil), \"secrets\": (module.input.secrets), \"stop_timeout\": 30}, {\"name\": \"log_router\", \"image\": module.input.firelens_image, \"cpu\": 0, \"memory_reservation\": 51, \"essential\": true, \"environment\": ([{name: \"FIRELENS_CONFIG_CONTENT\", value: \"[SERVICE]\\n    Flush 1\\n    Grace 30\\n\\n\" + (module.input.firelens_config ? module.input.firelens_config + \"\\n\" : \"\") + (module.input.firelens_cloudwatch_output_enabled ? \"\\n[OUTPUT]\\n    Name cloudwatch\\n    Match *\\n    region \" + stack.output.region + \"\\n    log_group_name \" + stack.output.log_group_name + \"\\n    auto_create_group true\\n    log_stream_prefix \" + stack.output.log_stream_prefix + \"/\\n    retry_limit 2\\n    log_key log\\n    log_format json/emf\\n\" : \"\")}] | concat(module.input.firelens_environment_variables != nil ? module.input.firelens_environment_variables : [])), \"secrets\": module.input.firelens_secrets != nil ? module.input.firelens_secrets : [], \"command\": [\"/bin/sh\", \"-c\", \"printf '%s' \\\"$FIRELENS_CONFIG_CONTENT\\\" > /flightcontrol-firelens.conf && exec /entrypoint.sh\"], \"user\": \"0\", \"log_configuration\": {\"log_driver\": \"awslogs\", \"options\": {\"awslogs-group\": stack.output.log_group_name, \"awslogs-region\": stack.output.region, \"awslogs-stream-prefix\": stack.output.log_stream_prefix + \"/firelens\"}}, \"firelens_configuration\": {\"type\": \"fluentbit\", \"options\": {\"config-file-type\": \"file\", \"config-file-value\": \"/flightcontrol-firelens.conf\", \"enable-ecs-log-metadata\": string(module.input.firelens_ecs_metadata_enabled)}}}] : [{\"cpu\": (module.input.capacity_provider == \"ec2\" ? int(float(module.input.task_cpu) * 1024) : int(float(module.input.fargate_size.vcpu) * 1024)), \"environment\": ([{name: \"PORT\", value: string(module.input.container_port)}] | concat(module.input.environment_variables != nil ? module.input.environment_variables : [])), \"essential\": true, \"image\": (module.input.build_type == \"prebuilt_image\" ? (deploy.input.image_ref contains \"sha256:\" ? module.input.image_repository + \"@\" + deploy.input.image_ref : module.input.image_repository + \":\" + deploy.input.image_ref) : (deploy.input.image_ref contains \"sha256:\" ? stack.output.ecr_repository_url + \"@\" + deploy.input.image_ref : stack.output.ecr_repository_url + \":\" + deploy.input.image_ref)), \"linux_parameters\": {\"init_process_enabled\": true}, \"log_configuration\": {\"log_driver\": \"awslogs\", \"options\": {\"awslogs-group\": stack.output.log_group_name, \"awslogs-region\": stack.output.region, \"awslogs-stream-prefix\": stack.output.log_stream_prefix}}, \"memory\": (module.input.capacity_provider == \"ec2\" ? int(float(module.input.task_memory) * 1024) : int(float(module.input.fargate_size.memory_gb) * 1024)), \"name\": (stack.output.container_name), \"port_mappings\": [{\"app_protocol\": \"http\", \"container_port\": (module.input.container_port), \"protocol\": \"tcp\"}], \"readonly_root_filesystem\": false, \"repository_credentials\": (module.input.image_registry_credentials_secret_arn ? {credentials_parameter: module.input.image_registry_credentials_secret_arn} : nil), \"secrets\": (module.input.secrets), \"stop_timeout\": 30}]) | concat(module.input.sidecars ? module.input.sidecars : []) >>"
     cpu: '<< string(module.input.capacity_provider == "ec2" ? int(float(module.input.task_cpu) * 1024) : int(float(module.input.fargate_size.vcpu) * 1024)) >>'
@@
       - name: DATABASE_URL
         valueFrom: arn:aws:ssm:...
     type: array
+  - description: Optional one-off commands that run in ECS before or after each deployment, using the same task image, roles, networking, and environment as the web container.
+    id: section_deploy_commands
+    label: Pre and post deploy
+    type: section
+  - default: false
+    description: Run a one-off ECS task before updating the ECS service.
+    id: pre_deploy_enabled
+    label: Run pre-deploy command
+    type: boolean
+  - add_button_label: Add cmd segment
+    default: []
+    description: Command arguments to run before updating the ECS service. For shell behavior, use `/bin/sh`, `-lc`, and your command string as separate arguments.
+    id: pre_deploy_command
+    label: Pre-deploy command
+    placeholder: npm
+    required: true
+    show_when:
+      pre_deploy_enabled: true
+    type: string_array
+  - collapsible: true
+    default: []
+    description: Additional environment variables for the pre-deploy task. Runtime environment variables and secrets are already inherited from the app container.
+    id: pre_deploy_environment_variables
+    label: Pre-deploy environment variables
+    placeholder: |-
+      - name: MIGRATION_MODE
+        value: online
+    required: false
+    show_when:
+      pre_deploy_enabled: true
+    type: array
+  - collapsible: true
+    description: Optional CPU units for the pre-deploy task. Leave blank to use the app task CPU setting.
+    id: pre_deploy_cpu
+    label: Pre-deploy CPU units
+    min: 1
+    required: false
+    show_when:
+      pre_deploy_enabled: true
+    type: number
+  - collapsible: true
+    description: Optional memory in MiB for the pre-deploy task. Leave blank to use the app task memory setting.
+    id: pre_deploy_memory
+    label: Pre-deploy memory (MiB)
+    min: 1
+    required: false
+    show_when:
+      pre_deploy_enabled: true
+    type: number
+  - collapsible: true
+    description: Optional ephemeral storage size for the pre-deploy task. Leave blank to use the task definition default.
+    id: pre_deploy_ephemeral_storage_size_gib
+    label: Pre-deploy ephemeral storage (GiB)
+    max: 200
+    min: 21
+    required: false
+    show_when:
+      pre_deploy_enabled: true
+    type: number
+  - collapsible: true
+    default: 1800
+    description: Maximum time to wait for the pre-deploy task to finish.
+    id: pre_deploy_timeout
+    label: Pre-deploy timeout (secs)
+    min: 1
+    show_when:
+     
... diff truncated ...

@flybayer

Copy link
Copy Markdown
Member Author

@greptile

Comment thread compute/ecs_service/rvn-ecs-web-definition.yml
Comment thread compute/ecs_service/rvn-ecs-web-definition.yml
@flybayer flybayer requested a review from mabadir June 12, 2026 00:31
@flybayer flybayer merged commit 3ab7fc6 into main Jun 12, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant