MCPcopy Create free account
hub / github.com/github/gh-aw / renderStepFromMap

Method renderStepFromMap

pkg/workflow/compiler_yaml_step_conversion.go:108–208  ·  view source on GitHub ↗

renderStepFromMap renders a GitHub Actions step from a map to YAML

(out *strings.Builder, step map[string]any, data *WorkflowData, indent string)

Source from the content-addressed store, hash-verified

106
107// renderStepFromMap renders a GitHub Actions step from a map to YAML
108func (c *Compiler) renderStepFromMap(out *strings.Builder, step map[string]any, data *WorkflowData, indent string) {
109 // Before rendering, extract any ${{ ... }} expressions from the run: field into
110 // env: variables to prevent shell injection attacks. A compiler warning is emitted
111 // for every expression that is moved so that authors know their script was changed.
112 if sanitized, warnings, changed := sanitizeRunStepExpressions(step); changed {
113 stepConversionLog.Printf("Sanitized run-step expressions: %d warning(s) emitted", len(warnings))
114 for _, w := range warnings {
115 fmt.Fprintln(os.Stderr, console.FormatWarningMessage(w))
116 c.IncrementWarningCount()
117 }
118 step = sanitized
119 }
120
121 stepName, _ := step["name"].(string)
122 stepConversionLog.Printf("Rendering step from map: name=%q, fields=%d", stepName, len(step))
123 // Start the step with a dash
124 out.WriteString(indent + "- ")
125
126 // Track if we've written the first line
127 firstField := true
128
129 // Order of fields to write (matches GitHub Actions convention)
130 fieldOrder := []string{"name", "id", "if", "uses", "with", "run", "env", "working-directory", "continue-on-error", "timeout-minutes", "shell"}
131
132 for _, field := range fieldOrder {
133 if value, exists := step[field]; exists {
134 // Add proper indentation for non-first fields
135 if !firstField {
136 out.WriteString(indent + " ")
137 }
138 firstField = false
139
140 // Render the field based on its type
141 switch v := value.(type) {
142 case string:
143 // Handle multi-line strings (especially for 'run' field)
144 if field == "run" && strings.Contains(v, "\n") {
145 fmt.Fprintf(out, "%s: |\n", field)
146 lines := strings.SplitSeq(v, "\n")
147 for line := range lines {
148 fmt.Fprintf(out, "%s %s\n", indent, line)
149 }
150 } else {
151 fmt.Fprintf(out, "%s: %s\n", field, v)
152 }
153 case map[string]any:
154 // For complex fields like "with" or "env" — sort keys for stable output.
155 fmt.Fprintf(out, "%s:\n", field)
156 for _, key := range sliceutil.SortedKeys(v) {
157 if field == "env" {
158 fmt.Fprintf(out, "%s %s: %s\n", indent, key, formatStepEnvValueForYAML(v[key]))
159 } else {
160 fmt.Fprintf(out, "%s %s: %v\n", indent, key, v[key])
161 }
162 }
163 default:
164 fmt.Fprintf(out, "%s: %v\n", field, v)
165 }

Callers 1

Calls 6

IncrementWarningCountMethod · 0.95
FormatWarningMessageFunction · 0.92
SortedKeysFunction · 0.92
PrintfMethod · 0.45

Tested by

no test coverage detected