DetectRuntimeRequirements analyzes workflow data to detect required runtimes
(workflowData *WorkflowData)
| 13 | |
| 14 | // DetectRuntimeRequirements analyzes workflow data to detect required runtimes |
| 15 | func DetectRuntimeRequirements(workflowData *WorkflowData) []RuntimeRequirement { |
| 16 | runtimeSetupLog.Print("Detecting runtime requirements from workflow data") |
| 17 | requirements := make(map[string]*RuntimeRequirement) // map of runtime ID -> requirement |
| 18 | |
| 19 | // Detect from custom steps |
| 20 | if workflowData.CustomSteps != "" { |
| 21 | detectFromCustomSteps(workflowData.CustomSteps, requirements) |
| 22 | } |
| 23 | |
| 24 | // Detect from MCP server configurations |
| 25 | if workflowData.ParsedTools != nil { |
| 26 | detectFromMCPConfigs(workflowData.ParsedTools, requirements) |
| 27 | } |
| 28 | if workflowData.MCPScripts != nil { |
| 29 | detectFromMCPScripts(workflowData.MCPScripts, requirements) |
| 30 | } |
| 31 | |
| 32 | // When using a custom image runner, ensure Node.js is set up. |
| 33 | // Standard GitHub-hosted runners (ubuntu-*, windows-*) have Node.js pre-installed, |
| 34 | // but custom image runners (self-hosted, enterprise runners, non-standard labels) may not. |
| 35 | // Node.js is required for gh-aw scripts such as start_safe_outputs_server.sh and |
| 36 | // start_mcp_scripts_server.sh that invoke `node` directly. |
| 37 | if isCustomImageRunner(workflowData.RunsOn) { |
| 38 | runtimeSetupLog.Printf("Custom image runner detected (%q), ensuring Node.js is set up", workflowData.RunsOn) |
| 39 | nodeRuntime := findRuntimeByID("node") |
| 40 | if nodeRuntime != nil { |
| 41 | updateRequiredRuntime(nodeRuntime, "", requirements) |
| 42 | } |
| 43 | } |
| 44 | |
| 45 | // When a custom harness script is configured for an engine that currently supports |
| 46 | // harness wrappers, require Node.js runtime setup with the default version so workflows |
| 47 | // consistently execute the harness with Node 24. |
| 48 | if requiresNodeForEngineHarness(workflowData) { |
| 49 | nodeRuntime := findRuntimeByID("node") |
| 50 | if nodeRuntime != nil { |
| 51 | updateRequiredRuntime(nodeRuntime, string(constants.DefaultNodeVersion), requirements) |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | // Detect runtimes required by LSP server configurations. |
| 56 | // Each known LSP server declares the runtime it needs (e.g. "go" for gopls, |
| 57 | // "ruby" for solargraph). Feeding these through the runtime manager ensures |
| 58 | // the runtime is set up via a properly SHA-pinned setup action rather than |
| 59 | // relying on whatever version happens to be pre-installed on the runner. |
| 60 | if len(workflowData.LSP) > 0 { |
| 61 | detectFromLSPServers(workflowData.LSP, requirements) |
| 62 | } |
| 63 | |
| 64 | // Apply runtime overrides from frontmatter |
| 65 | if workflowData.Runtimes != nil { |
| 66 | applyRuntimeOverrides(workflowData.Runtimes, requirements) |
| 67 | } |
| 68 | |
| 69 | // Add Python as dependency when uv is detected (uv requires Python) |
| 70 | if _, hasUV := requirements["uv"]; hasUV { |
| 71 | if _, hasPython := requirements["python"]; !hasPython { |
| 72 | runtimeSetupLog.Print("UV detected without Python, automatically adding Python runtime") |