Probe for the actual Danger.swiftmodule instead of trusting the compiler that built danger-swift - #663
Conversation
…ler that built danger-swift moduleFolder (and the equivalent Script.artifactsPath) picked between .build/debug and .build/debug/Modules using a compile-time #if compiler(<6.0) check on whichever Swift built the danger-swift binary itself. That's a proxy for a runtime property of the toolchain/build system building the target package, and it's wrong whenever those two differ — which is the normal case for a distributed binary (Homebrew, Docker, the new prebuilt universal binary), and is why the swiftbuild build system (Xcode 16.3+'s new default) breaks it. Replace the compile-time check with a runtime probe for the exact Danger.swiftmodule artifact at both candidate locations, falling back to today's compiled-in default whenever the probe is ambiguous (both or neither present) so no currently-working configuration changes behavior.
The Marathon inline-dependency path's Script.artifactsPath had the same compile-time #if compiler(<6.0) defect as SPMDanger.moduleFolder, but an initial fix probed FileManager.default against paths relative to the process's current directory. Runner.swift's only caller resolves the returned paths relative to the script's own folder (a separate directory where the build actually ran), not the process cwd, so the probe could pick the wrong candidate based on unrelated filesystem state. Resolve the probe against the script's own folder instead, with an injectable FileManager so this is actually testable, and add coverage mirroring the SPMDangerTests cases plus a regression test pinning that the probe ignores the process cwd.
|
The macOS CI failures here are unrelated to this diff. Every macOS job fails identically before Danger ever gets to run the compiled Swift: This happens inside the Root cause looks environmental: the macOS jobs install This is reproducible on #662 as well (identical stack trace, identical point of failure), so it predates this PR and isn't something this diff can fix — flagging in case it's useful context, but not blocking review of the actual change. |
|
Correction to my earlier note here about the macOS CI failure: I'd said "a recent Opened a fix at #664: install |
Problem
SPMDanger.moduleFolder(used to build the-Iflag when compiling a Dangerfile against aDangerDepsSwiftPM product) picks between.build/debugand.build/debug/Modulesusing a compile-time check on the Swift version that built thedanger-swiftbinary itself:That's a proxy for a runtime property — the toolchain and build system used to build the target package — and it's wrong whenever those two differ, which is the normal case for a distributed
danger-swiftbinary (Homebrew, Docker, or the prebuilt universal binary from #660): the binary's own compile-time Swift version has nothing to do with which build system/toolchain later builds the Dangerfile's dependencies.Concretely, this breaks on a toolchain that defaults
swift buildto the newerswiftbuildbuild system (observed on Xcode 27 Beta 4 / Swift 6.4; not claiming a specific version threshold here, just that this is a real, shipping build system that behaves differently fromnative). Underswiftbuild, SwiftPM produces flat*.swiftmodulefiles with noModules/subdirectory at all — the inverse of whatnative+Swift 6 produces. Adanger-swiftbinary built with Swift ≥6.0 (true of every distributed binary today) then looks inModules/, finds nothing, and fails:Measured layouts (building only
--product DangerDeps<X>, exactly whatbuildDependenciesruns):.build/debugarm64-apple-macosx/debugout/Products/Debug*.swiftmodulein bin path<bin>/Modules/The two layouts are disjoint.
.build/debugitself stays a valid symlink under both — SwiftPM repoints it on every build — so only the module-search path (-I) is wrong, never-L.Sources/DangerDependenciesResolver/Script.swift'sartifactsPathhas the identical#if compiler(<6.0)pattern for the Marathon-based inline-dependency path (import ... package:Dangerfiles with noDangerDepslibrary).Fix
Replace the compile-time check with a runtime probe for the actual
Danger.swiftmoduleartifact at both candidate locations, falling back to today's compiled-in default whenever the probe is ambiguous (both or neither candidate present) so no currently-working configuration changes behavior:Probing for the exact artifact (not mere
Modules/directory existence) avoids a false positive from an empty/partialModules/left over from a prior build under a different toolchain.Backwards compatibility is testable, not just asserted: both unambiguous branches are byte-identical to today's two
#if compilerbranches, and the only behavior change is in states that are broken today (native <6.0 binary vs. ≥6.0 package or vice versa, andswiftbuild). The ambiguous fallback keeps every currently-working binary — including the official Docker image, which defaults to Swift 5.9 — on its existing compiled-in behavior.The same fix is applied to
Script.artifactsPath, resolved against the script's ownfolder(not the process's working directory, since the build for that path runs in a separate scratch folder).Testing
swift test --filter "SPMDangerTests|ScriptTests"— 20/20 passing, including new coverage for all four probe states (flat-only, nested-only, both-present, neither-present) on both call sites, plus a regression test pinning thatScript.artifactsPathprobes under its ownfolder, not the process cwd.swiftcflags:swiftbuildbuild system (Xcode 27 Beta 4, Swift 6.4): the unpatched behavior (-I <bin>/Modules) fails withno such module 'Danger'; the patched behavior (-I <bin>) compiles and runs.native(Xcode 26.6, Swift 6.3.3): unchanged — the probe unambiguously selects the nestedModules/path, byte-identical to today, and the flat path correctly fails (proving the two layouts are genuinely disjoint, not just "either works").swift build -c release --product danger-swiftsucceeds under both toolchains.Related
buildFolderto a hardcoded.build/out/Products/Debugwhen present): after a toolchain downgrade without a clean, the stale directory survives and gets selected, producingerror: module compiled with Swift 6.4 cannot be imported by the Swift 6.3.3 compiler. This PR instead keepsbuildFolderuntouched and only changes the module-search path, following the.build/debugsymlink SwiftPM already maintains rather than hardcoding an absolute layout path.