Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion pkg/container/docker_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,26 @@ func (cr *containerReference) Remove() common.Executor {
}

func (cr *containerReference) GetHealth(ctx context.Context) Health {
resp, err := cr.cli.ContainerInspect(ctx, cr.id)
logger := common.Logger(ctx)

// In dry-run mode, containers are not actually created, so return healthy status
if common.Dryrun(ctx) {
logger.Debugf("Dry-run mode: returning healthy status for container health check")
return HealthHealthy
}

// Add safety checks for nil Docker client and empty container ID
if cr.cli == nil {
logger.Errorf("Docker client is nil, cannot inspect container health")
return HealthUnHealthy
}

if cr.id == "" {
logger.Debugf("Container ID is empty, cannot inspect container health")
return HealthUnHealthy
}

resp, err := cr.cli.ContainerInspect(ctx, cr.id)
if err != nil {
logger.Errorf("failed to query container health %s", err)
return HealthUnHealthy
Expand Down
8 changes: 8 additions & 0 deletions pkg/runner/run_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,14 @@ func (rc *RunContext) startServiceContainers(_ string) common.Executor {

func (rc *RunContext) waitForServiceContainer(c container.ExecutionsEnvironment) common.Executor {
return func(ctx context.Context) error {
logger := common.Logger(ctx)

// In dry-run mode, service containers are not actually created, so skip health checks
if common.Dryrun(ctx) {
logger.Debugf("Dry-run mode: skipping service container health check")
return nil
}

sctx, cancel := context.WithTimeout(ctx, time.Minute*5)
defer cancel()
health := container.HealthStarting
Expand Down
48 changes: 48 additions & 0 deletions pkg/runner/run_context_dryrun_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package runner

import (
"context"
"testing"

"github.com/nektos/act/pkg/common"
"github.com/nektos/act/pkg/container"
"github.com/stretchr/testify/assert"
)

func TestServiceContainerWorkflowDryRun(t *testing.T) {
// Test the actual functionality: service containers should work in dry-run mode
// This reproduces the exact scenario that was causing segmentation faults

ctx := common.WithDryrun(context.Background(), true)

// Create service containers like the workflow that was failing
serviceContainers := []container.ExecutionsEnvironment{
container.NewContainer(&container.NewContainerInput{
Image: "postgres:15-alpine",
Name: "postgres-service",
}),
}

// Create RunContext with service containers (the actual failing scenario)
rc := &RunContext{
ServiceContainers: serviceContainers,
}

// Test the actual workflow that was crashing:
// 1. waitForServiceContainers calls waitForServiceContainer for each service
// 2. waitForServiceContainer calls GetHealth() in a timeout loop
// 3. GetHealth() should handle the case where Docker client isn't available (dry-run)

// This should complete without segmentation fault
err := rc.waitForServiceContainers()(ctx)
assert.NoError(t, err, "Service container workflow should work in dry-run mode")

// Verify that individual health checks also work (the core of the bug)
for _, serviceContainer := range serviceContainers {
health := serviceContainer.GetHealth(ctx)
// In dry-run mode, we expect containers to report as healthy
// (since they're not actually running, we mock them as ready)
assert.Equal(t, container.HealthHealthy, health,
"Service containers should report healthy in dry-run mode")
}
}
Loading