Skip to content

Commit 20c8d87

Browse files
engalarclaude
andcommitted
fix: docker run reliability and workflow DEFAULT outcome handling
- Re-download stale runtime cache when PAD files are missing (fixes builds that fail with missing StudioPro.conf.hbs after interrupted downloads) - Clean up partial MxBuild/runtime cache before re-downloading (fixes silent re-download when kill interrupts extraction) - Drop DEFAULT outcomes from boolean workflow decisions at write time; Mendix 11 runtime rejects VoidConditionOutcome for boolean conditions - Guard against nil-panic in visitor when DROP OUTCOME/PATH has invalid syntax - Use %PROGRAMFILES% env vars for MxBuild/JDK search on Windows instead of hardcoded C:\Program Files Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent ce1442f commit 20c8d87

5 files changed

Lines changed: 68 additions & 9 deletions

File tree

cmd/mxcli/docker/build.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -641,9 +641,18 @@ func ensurePADFiles(productVersion string, w io.Writer) error {
641641
return nil
642642
}
643643

644-
// Check that the runtime PAD source exists
644+
// Check that the runtime PAD source exists; if not, the cached runtime is stale
645+
// (downloaded before PAD files were included). Invalidate and re-download.
645646
if _, err := os.Stat(runtimePAD); err != nil {
646-
return fmt.Errorf("runtime PAD files not found at %s", runtimePAD)
647+
fmt.Fprintf(w, " PAD files missing from cached runtime, re-downloading...\n")
648+
os.RemoveAll(runtimeDir)
649+
if _, err := DownloadRuntime(productVersion, w); err != nil {
650+
return fmt.Errorf("re-downloading runtime for PAD files: %w", err)
651+
}
652+
// Check again — some versions simply don't ship PAD files.
653+
if _, err := os.Stat(runtimePAD); err != nil {
654+
return fmt.Errorf("runtime PAD files not found at %s (not included in this Mendix version)", runtimePAD)
655+
}
647656
}
648657

649658
// Create parent directory if needed

cmd/mxcli/docker/detect.go

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,18 @@ func resolveMxBuild(explicitPath string) (string, error) {
9494
func mxbuildSearchPaths() []string {
9595
switch runtime.GOOS {
9696
case "windows":
97-
return []string{
98-
`C:\Program Files\Mendix\*\modeler\mxbuild.exe`,
99-
`C:\Program Files (x86)\Mendix\*\modeler\mxbuild.exe`,
97+
var paths []string
98+
// Use environment variables — the system drive is not always C:.
99+
for _, env := range []string{"PROGRAMFILES", "PROGRAMW6432", "PROGRAMFILES(X86)"} {
100+
if dir := os.Getenv(env); dir != "" {
101+
paths = append(paths, filepath.Join(dir, "Mendix", "*", "modeler", "mxbuild.exe"))
102+
}
100103
}
104+
if len(paths) == 0 {
105+
// Fallback if env vars are missing (unlikely but safe).
106+
paths = []string{`C:\Program Files\Mendix\*\modeler\mxbuild.exe`}
107+
}
108+
return paths
101109
case "darwin":
102110
return []string{
103111
"/Applications/Mendix/*/modeler/mxbuild",
@@ -174,11 +182,20 @@ func resolveMacOSJavaHome() (string, error) {
174182
func jdkSearchPaths() []string {
175183
switch runtime.GOOS {
176184
case "windows":
177-
return []string{
178-
`C:\Program Files\Eclipse Adoptium\jdk-21*`,
179-
`C:\Program Files\Java\jdk-21*`,
180-
`C:\Program Files\Microsoft\jdk-21*`,
185+
var paths []string
186+
for _, env := range []string{"PROGRAMFILES", "PROGRAMW6432"} {
187+
if dir := os.Getenv(env); dir != "" {
188+
paths = append(paths,
189+
filepath.Join(dir, "Eclipse Adoptium", "jdk-21*"),
190+
filepath.Join(dir, "Java", "jdk-21*"),
191+
filepath.Join(dir, "Microsoft", "jdk-21*"),
192+
)
193+
}
181194
}
195+
if len(paths) == 0 {
196+
paths = []string{`C:\Program Files\Eclipse Adoptium\jdk-21*`}
197+
}
198+
return paths
182199
case "darwin":
183200
return []string{
184201
"/Library/Java/JavaVirtualMachines/temurin-21*/Contents/Home",

cmd/mxcli/docker/download.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ func DownloadMxBuild(version string, w io.Writer) (string, error) {
8686
return "", err
8787
}
8888

89+
// Remove any partial cache from a previously interrupted download.
90+
if _, err := os.Stat(cacheDir); err == nil {
91+
fmt.Fprintf(w, " Removing incomplete cache at %s...\n", cacheDir)
92+
os.RemoveAll(cacheDir)
93+
}
94+
8995
url := MxBuildCDNURL(version, runtime.GOARCH)
9096
fmt.Fprintf(w, " Downloading MxBuild %s for %s...\n", version, runtime.GOARCH)
9197
fmt.Fprintf(w, " URL: %s\n", url)
@@ -182,6 +188,12 @@ func DownloadRuntime(version string, w io.Writer) (string, error) {
182188
return "", err
183189
}
184190

191+
// Remove any partial cache from a previously interrupted download.
192+
if _, err := os.Stat(cacheDir); err == nil {
193+
fmt.Fprintf(w, " Removing incomplete cache at %s...\n", cacheDir)
194+
os.RemoveAll(cacheDir)
195+
}
196+
185197
url := RuntimeCDNURL(version)
186198
fmt.Fprintf(w, " Downloading Mendix runtime %s...\n", version)
187199
fmt.Fprintf(w, " URL: %s\n", url)

mdl/executor/cmd_workflows_write.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,22 @@ func buildExclusiveSplit(n *ast.WorkflowDecisionNode) *workflows.ExclusiveSplitA
351351
}
352352
act.Name = act.Caption
353353

354+
// Detect boolean decision (has TRUE or FALSE outcomes).
355+
// The Mendix 11 runtime only supports BooleanConditionOutcome and
356+
// EnumerationValueConditionOutcome — VoidConditionOutcome (DEFAULT) is rejected.
357+
isBooleanDecision := false
358+
for _, o := range n.Outcomes {
359+
if o.Value == "True" || o.Value == "False" {
360+
isBooleanDecision = true
361+
break
362+
}
363+
}
364+
354365
for _, outcomeNode := range n.Outcomes {
366+
if isBooleanDecision && outcomeNode.Value == "Default" {
367+
// Skip DEFAULT on boolean decisions — runtime rejects VoidConditionOutcome.
368+
continue
369+
}
355370
outcome := buildConditionOutcome(outcomeNode)
356371
if outcome != nil {
357372
act.Outcomes = append(act.Outcomes, outcome)

mdl/visitor/visitor_workflow.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,9 @@ func buildAlterWorkflowAction(ctx *parser.AlterWorkflowActionContext) ast.AlterW
195195

196196
// DROP OUTCOME 'name' ON alterActivityRef
197197
if ctx.DROP() != nil && ctx.OUTCOME() != nil {
198+
if ctx.AlterActivityRef() == nil || ctx.STRING_LITERAL() == nil {
199+
return nil
200+
}
198201
ref, atPos := parseAlterActivityRef(ctx.AlterActivityRef().(*parser.AlterActivityRefContext))
199202
outcomeName := unquoteString(ctx.STRING_LITERAL().GetText())
200203
return &ast.DropOutcomeOp{
@@ -206,6 +209,9 @@ func buildAlterWorkflowAction(ctx *parser.AlterWorkflowActionContext) ast.AlterW
206209

207210
// DROP PATH 'caption' ON alterActivityRef
208211
if ctx.DROP() != nil && ctx.PATH() != nil && ctx.BOUNDARY() == nil {
212+
if ctx.AlterActivityRef() == nil || ctx.STRING_LITERAL() == nil {
213+
return nil
214+
}
209215
ref, atPos := parseAlterActivityRef(ctx.AlterActivityRef().(*parser.AlterActivityRefContext))
210216
pathCaption := unquoteString(ctx.STRING_LITERAL().GetText())
211217
return &ast.DropPathOp{

0 commit comments

Comments
 (0)