-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix how native solc is checked after download #3284
Fix how native solc is checked after download #3284
Conversation
HH-1188 Hardhat 2.11 fails in docker environments [NomicFoundation/hardhat#3195]
Reproduction: https://github.com/DefiCake/hardhat-211-docker-bug-report I am finding troubles when running hardhat inside CI pipelines. I am not sure where this was introduced, suspecting the latest 2.11 where compile time optimizations happened, but not really sure. Got a personal project at v.2.9.9 where it does work well. It boils down to it failing when running in dockerized envs with either one of these two errors: Either it will exit early during solc download and not run the test:
Or it will fail with an EPIPE error:
Docker info: This issue was automatically created, do not edit its description. GH-ID: #3195 |
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
🦋 Changeset detectedLatest commit: 145b12c The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for fixing this @fvictorio!
@alifarooq0 my pleasure! This will need a review from @alcuadrado, who is on vacation this week. Hopefully we can release it mid next week. |
I wonder if we should prevent this from ever happening, at least in the CLI. There doesn't seem to be a very good way of doing it though. One option: let finished = false;
process.on("exit", () => {
if (!finished) {
console.error("shouldn't happen");
process.exitCode = 1;
}
});
main()
.then(() => {
finished = true;
process.exit(process.exitCode)
})
.catch((error) => {
finished = true;
console.error(error);
process.exit(1);
}); |
@fvictorio to better understand the calling code, is the issue that we need to bubble up the error from |
Not really bubble up, it was just about handling it. We were using |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approving it.
Note that this works because child_process.execFile
has a custom util.promisify
behavior https://nodejs.org/docs/latest-v14.x/api/child_process.html#child_process_child_process_execfile_file_args_options_callback
The build was broken due to more recent `solc` requiring libc compat files in Alpine and Hardhat ignoring errors from the execution of `solc`. This led to deadlocked compilation attempts. Hardhat fix: NomicFoundation/hardhat#3284 This changes adds the required compat files in our Alpine image as well.
The build was broken due to more recent `solc` requiring libc compat files in Alpine and Hardhat ignoring errors from the execution of `solc`. This led to deadlocked compilation attempts. Hardhat fix: NomicFoundation/hardhat#3284 This changes adds the required compat files in our Alpine image as well. (cherry picked from commit 583f41c)
* Fix compilation of smart contracts in Docker containers The build was broken due to more recent `solc` requiring libc compat files in Alpine and Hardhat ignoring errors from the execution of `solc`. This led to deadlocked compilation attempts. Hardhat fix: NomicFoundation/hardhat#3284 This changes adds the required compat files in our Alpine image as well. (cherry picked from commit 583f41c) * pluto: Remove now obsolete link in Docker file (cherry picked from commit 093c48a) * Add missing flag in the CTD (#4329) (cherry picked from commit c54f94c) * Use proper upstream version tag for Avado on `master` (#4330) * Use proper upstream version tag for Avado on `master` * Print upstream version info as well (cherry picked from commit c2a680a) * Fix typo in the deployment-gatet script (#4331) (cherry picked from commit fc1c764) * Revert timeouts Co-authored-by: Tino Breddin <tino@hoprnet.org> Co-authored-by: ausias-armesto <ausiasarmesto@gmail.com>
Use
util.promisify
instead of implementing our own handling of theexecFile
result. This fixes #3195 because of some very fun reasons.tl;dr
The previous implementation wasn't handling the case were the
ChildProcess
instance emitted an"error"
event. You would expect that this would hang the process forever, but you would be wrong (I know I was).The reason it was erroring is that, in an alpine-based docker image, the solc binary couldn't even be executed. This caused an error instead of emitting an
"exit"
event with a non-zero code.Explanation
The problem here is a combination of two weird things I had no idea about.
First, when you download a solc native binary in an alpine-based docker container, the file cannot be executed. The fun part is that
execFile
errors with aENOENT
error, and running it in the shell fails with a "file not found" error. This was already misleading enough. Here's a good explanation of why that happens.Second, and most important: in that scenario the
ChildProcess
instance returned byexecFile
would emit anerror
event, and we weren't handling that. In my mental model of how node.js works, this should've caused the Hardhat process to hang forever, but my mental model was wrong. Unresolved promises don't prevent the node process from finishing if there's no pending IO or timeouts or whatever, as explained here.In other words:
hh compile
was downloading a binary that didn't work and finishing with a successful exit code. After that, successive executions would try to use that compiler and fail.The lesson I take from this is that we should be super careful when we convert something like an event emitter into a promise.