-
Notifications
You must be signed in to change notification settings - Fork 317
feat: auto-generate --allow-host-service-ports from services: port mappings #23756
Description
Problem
When a workflow uses GitHub Actions services: with port mappings (e.g., PostgreSQL, Redis), the compiled workflow runs the agent inside AWF's isolated network. The agent cannot reach service containers without explicit --allow-host-service-ports configuration.
Currently, there is no way for the compiler to automatically detect service ports and pass them to AWF. Users would need to manually configure port access, which defeats the purpose of the declarative services: block.
Why host port mappings are required
The agent runs inside AWF's agent container on an isolated Docker network (awf-net). GitHub Actions services: containers run on the runner's Docker network. These are separate networks — the agent cannot reach services by hostname. The only path is through host port mappings, where the runner maps service container ports to host ports, and AWF allows traffic to the host gateway on those specific ports.
Services declared without ports: are unreachable from the agent:
services:
redis:
image: redis
# No ports: — unreachable from AWF agentServices with ports: are reachable via the host gateway:
services:
redis:
image: redis
ports:
- 6379:6379 # Reachable via host gatewayProposed Solution
Runtime port resolution via GitHub Actions expressions
Instead of parsing port numbers at compile time, the compiler should emit ${{ job.services.<service_id>.ports['<container_port>'] }} expressions. These are resolved by the GitHub Actions runner at runtime, before the shell executes the command.
This handles all port mapping styles:
"5432:5432"(explicit) — expression resolves to5432"5432"(dynamic host port) — expression resolves to the runtime-assigned port"49152:5432"(remapped) — expression resolves to49152
Compiler behavior
- During compilation, parse the
services:block to extract service IDs and container ports - For each service with
ports:, generate expressions:${{ job.services.<id>.ports['<container_port>'] }} - Emit
--allow-host-service-ports "<comma-separated expressions>"in the AWF command - For services without
ports:, emit a compile-time warning
Example
Input workflow:
services:
postgres:
image: postgres:15
ports:
- 5432:5432
redis:
image: redis:7
ports:
- 6379:6379Compiled AWF command includes:
--allow-host-service-ports "${{ job.services.postgres.ports['5432'] }},${{ job.services.redis.ports['6379'] }}"
At runtime, resolves to:
--allow-host-service-ports "5432,6379"
Port parsing rules
| Port spec | Container port | Notes |
|---|---|---|
"5432:5432" |
5432 |
Explicit mapping |
"5432" |
5432 |
Dynamic host port |
"49152:5432" |
5432 |
Remapped host port |
"5432/tcp" |
5432 |
TCP (default) |
"5432/udp" |
skip | UDP not supported, warn |
"6000-6010:6000-6010" |
6000-6010 |
Range expansion (with cap) |
5432 (integer) |
5432 |
YAML may parse as int |
Shell quoting consideration
The ${{ }} expressions contain single quotes (e.g., ports['5432']). These must be emitted as raw strings in the compiled YAML, not passed through shell escaping functions. The GitHub Actions runner substitutes ${{ }} expressions before the shell interprets the command, so the single quotes are part of the expression syntax, not shell syntax. The value should be double-quoted in the output to protect the comma-separated result from word splitting.
Implementation Notes
Files to modify
- New file for port extraction logic (parsing port specs, building expressions)
WorkflowDatastruct — add field to carry the extracted port expressions- Service processing — call port extraction after services are merged (handles imports)
- AWF command builder — add
--allow-host-service-portsas a raw (non-shell-escaped) arg
Edge cases to handle
- Multiple services with multiple ports each → comma-separated expressions
- Services without ports → compile-time warning
- No services at all → no-op
- UDP-only ports → skip with warning
- Port ranges → expand with reasonable cap
- Integer port values in YAML → handle type coercion
- Services from imports → already handled by existing merge logic
Test plan
- Unit tests: port spec parsing for all syntaxes
- Unit tests: expression generation for single/multi service+port combinations
- Integration test: compiled YAML contains correct
--allow-host-service-portsexpressions - Negative tests: services without ports emit warnings, UDP ports skipped
- Smoke test: add a Redis service to an existing smoke workflow (e.g.,
services: { redis: { image: redis:7, ports: ["6379:6379"] } }). The smoke agent should be instructed to connect to Redis onlocalhost:6379(e.g.,redis-cli pingorecho PING | nc localhost 6379) and verify it gets aPONGresponse. The agent receives no special context about AWF or port configuration — it just tries to use Redis as any normal workflow would. This validates the full end-to-end path: compiler extracts ports → expressions resolve at runtime → AWF allows traffic → agent reaches the service.