In a previous post, I mentioned a command line tool that I built which you can point at a Swift package, and which will generate a GitHub Actions workflow file for it.
I also built a SwiftPM plugin which invokes the tool, but at some point it stopped working. Instead of running, it would just hang, for reasons unknown.
The Deadlock
The root cause turned out to be nested SwiftPM invocations.
The plugin launches ActionBuilderTool, and the tool calls swift package dump-package to inspect metadata and infer sensible workflow defaults.
I think that did work, at some point in the past, but changes in Swift Package Manager appear to have broken it. When the swift command line tool runs the plugin, which runs the tool, which runs swift, you end up with lock contention around index.lock, and everything grinds to a halt.
The fix I found was to make the tool’s swift package dump-package use an isolated scratch path inside a subfolder of the .build folder. This means that the inner invocation doesn’t fight with the outer one, and it’s still in a place that we can write to.
The workaround isn’t ideal, since building to a different scratch path means doing a lot of work again, but this isn’t a command that you run that often.
I’d like to find a better fix, but for now, it works.
To make the behavior explicit, I added a --called-from-plugin flag. The plugin uses this, but if you just want to use the tool manually, you don’t need to.
Unexpected Lack Of Items In The Bagging Area…
I thought I was done at this point, and made a new release, only to discover that it was broken and not doing anything at all.
🤔
I eventually tracked that down to an issue with nested sandboxes. The tool invocation was silently failing when the plugin called it, but the plugin wasn’t reporting anything. The failure was because it was trying to sandbox inside a sandbox. Invoking the inner swift package dump-package without sandboxing seems to sort that out - and hopefully it’s still safe since the plugin itself is running sandboxed.
Smoke Em If You Got Em
I fixed the problem, eventually, after a lot of head scratching.
In doing so I also improved the plugin diagnostics so it now reports a proper error if the tool exits non-zero, and explicitly checks that the expected workflow file was actually produced.
I then added smoke tests for:
- direct tool workflow generation
- tool generation in plugin-mode
- actual
swift packageplugin invocation
Obviously I then used the tool on the plugin source code itself, to generate a workflow file which runs these tests in an action.
Hopefully that will stop me from releasing a broken plugin next time…
Other Fixes In Passing
While I was in there, I also tidied up a few other things in ActionBuilderCore, which is the underlying library that the tool uses to do the generation:
- bumped the supported Swift versions and moved the minimum up to 5.10
- updated the GH images that the workflow uses; some of them had become obsolete
- reduced generated workflow noise and improved log handling
- removed some stale dependencies and legacy code paths
There’s a bunch more polishing I’d like to do, but at least the “plugin hangs forever” issue is no longer one of the mysteries of the universe.
Part of the reason I started doing all of this in the first place is that I decided to revive and update Action Status, and I needed some actual running workflows to point it at. Which led me to my discovery of the broken plugin. And thus another rabbit hole was entered.
More about the Action Status refresh anon…
Oh, if you’re interested, you can find the plugin here.