-
Notifications
You must be signed in to change notification settings - Fork 20
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
chore: Download browsers for all platforms to release builds from single machine #165
chore: Download browsers for all platforms to release builds from single machine #165
Conversation
Thanks for taking this on, it represents a huge accomplishment. In my process of smoke testing binaries I built with this branch, I encountered a problem running tests on my recorded steps for Linux and Windows. This problem doesn't happen on the Mac build, so I think we are probably seeing the same issue here as we had with the browsers. Sharp isn't getting copied over for non-native builds, and thus it seems to be causing the test feature to fail. |
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.
Great work @kyungeunni 🎉
electron-builder.env
Outdated
@@ -0,0 +1 @@ | |||
BROWSER_REVISION=907428 |
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.
We can automate this with ENV variable. We need to get it from
synthetics-recorder/scripts/install-pw.js
Line 29 in 13a1463
const SYNTHETICS_BROWSER_REVISIONS = require("@elastic/synthetics/node_modules/playwright-chromium/browsers.json"); |
scripts/download-browsers.js
Outdated
const { getChromeVersion } = require("./install-pw"); | ||
|
||
const SUPPORTED_PLATFORMS = ["linux", "mac", "win"]; | ||
const EXECUTABLE_PATHS = { |
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.
Need ffmpeg
as well if i remember correctly for all platforms. Please double check if thats not the case.
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 reminding me this!! I've updated the script to also download ffmpeg
too.
scripts/download-browsers.js
Outdated
} | ||
} | ||
// eslint-disable-next-line no-console | ||
downloadAll().then(() => console.log("Download succeeded")); |
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.
Move them inside this check if we want to execute only when running as main module and not while importing -
synthetics-recorder/scripts/install-pw.js
Line 50 in 13a1463
if (require.main === module) { |
package.json
Outdated
"release": "npm run pack -- -mwl --publish=always", | ||
"pack": "run-s prepack electron-builder", | ||
"prepack": "run-p build prepack:browsers", | ||
"prepack:browsers": "node scripts/download-browsers", |
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.
As @justinkambic experienced, this would still break for native deps, We can fix it by running the preparation and package script serially for all platforms and uploading the pack one by one.
scripts/download-browsers.js
Outdated
// 'mac-arm64': '%s/builds/chromium/%s/chromium-mac-arm64.zip', | ||
}; | ||
|
||
async function download(platform, revision, directory) { |
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.
Did you try to check the Registry in Playwright if there was any other way to do this?
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.
Yes I went through all the Registry in Playwright and it seemed to be no work way around in there, especially I couldn't mock the platform as they would check the host machine's platform in various steps. So I used one of the functions that Registry uses to download the browser which is not tied to the host machine's platform(you pass it via parameter)
c33b1d4
to
2eed943
Compare
This PR is ready for re-review. What I've fixed since the last review:
_The latest build works well on Mac and Windows. When running it on Linux(Ubuntu 18.04) it had an issue launching the playback(test) but that is out of the scope as it doesn't seem to be related to the build. (I will create another ticket regarding that shortly). As the binary for Linux doesn't work at the moment, I excluded Linux from the build configs, we will re-enable it once we make it work on Linux._ |
Fantastic update @kyungeunni and thank you for the advice @emilioalvap ❤️ I will review as soon as possible. |
0ed182f
to
163c887
Compare
Hey @lucasfcosta 👋 @justinkambic mentioned that it would be great if you could test running the build script from your machine and see if it works well just to make sure it is not customed to work only on my machine. When you have a minute, could you try running |
@kyungeunni I'm still seeing an issue on Linux unfortunately 😞 |
Context - |
Thanks, @vigneshshanmugam !! I'm wondering if it was a permission issue, shouldn't the error code be |
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 this @kyungeunni!
My only comments were around env vars and small nits.
Btw, I did build the runner on my Mac and ran the built recorder. I was able to record a multi-step journey and replay it just fine :)
@@ -245,6 +245,7 @@ async function onTest(data, browserWindow) { | |||
*/ | |||
synthCliProcess = fork(`${SYNTHETICS_CLI}`, args, { | |||
env: { | |||
...process.env, |
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.
I know we're invoking our own cli
here, but I'd usually be a bit concerned with passing the entire env
to it because of the secrets it may contain. Can we detect which parts of process.env
do we need and pass only those?
Not a big deal, but just thought I'd add the comment for us to discuss.
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.
This is something I'm uncertain about as I'm not accustomed to the security implications of changes to CLI interaction. From speaking offline it seems like this is not a large risk, but I'd like to hear others' opinions.
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.
As @emilioalvap mentioned in another comment, I think there will be various types of env values as well as platform specific ones, so it can be a bit tricky to track all of them. We should measure the effort vs worth by doing so (if we decide to do it).
Another note, when env option is not specified when spawning, Node assigns parent's process.env
as a default
scripts/beforePack.js
Outdated
stdio: "inherit", | ||
shell: true, | ||
env: { | ||
...process.env, |
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.
Here too, I'm not sure if this is a big deal, but I'd imagine that if anyone pushes a malicious update to libvips
or any other library we use in the fix-sharp
command, they could have an environment variable containing our secrets used for signing the app or other credentials.
Maybe I'm being paranoid or there are mechanisms to prevent this already, but I'd appreciate more thoughts from others here.
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.
@lucasfcosta Good point. I'm not sure we would like to follow an allow list approach here, since we are taking into consideration multiple types (X, chromium, playwright, distro, etc...) and user-defined variables.
IMO, it should suffice to filter those variables that are only meant to be used in the recorder itself: user auth, tokens and such.
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.
Yeah, that's a valid point 👍 At the same time, I also thought would we have the same security concerns if we were to run the same script in the main process. Let me know if I'm being so naive!! Anyways It is always to be precautious about it, and I really like what @emilioalvap suggested. We should consider auditing some of the sensitive env vars and filter them out before passing to the child process.
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.
Agreed, a filter out should be enough rather than a filter in just to be sure we're not passing our own signing keys to a malicious package downwards.
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.
Just so I can better understand here, isn't there a whole stack of dependencies we already use, given that we use an OSS toolchain? I guess my question is why are we particularly concerned here? I spoke briefly with @emilioalvap , and he pointed out that NPM is perhaps weaker than a linux distro in terms of supply chain (though probably not for many attacks).
CC @levinebw, your thoughts here?
scripts/beforePack.js
Outdated
npmInstall.on("error", error => { | ||
reject(error); | ||
}); |
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.
nit:
npmInstall.on("error", error => { | |
reject(error); | |
}); | |
npmInstall.on("error", reject); |
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 updated!(The less code the better 🧹)
scripts/download-browsers.js
Outdated
|
||
exports.downloadForPlatform = async function downloadForPlatform(platform) { | ||
platform = translatePlatform(platform); | ||
for (const browser of ["chromium", "ffmpeg"]) { |
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.
Nit: should we change browser
to software
here and in the references within the function like getBrowserVersion
given we're not only dealing with browsers here?
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.
That's a good point, will amend 👍
It seems like we are trying to access it for the screenshots, In that case, can you try with |
@vigneshshanmugam I think even if we turn it off, the runner would still try to create a folder as specified here though, wouldn't it? Or am I missing something? |
++ As you mentioned there is no way to turn it off, turning off screenshots wont just look for it. |
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.
I'm going to comment here and leave a passive LGTM - we should make sure that we don't leave these threads questioning security topics open before merging. I'll reach out to the other reviewers to see if they have any further thoughts on revisions we can make here, or if they're alright to merge as-is.
@@ -245,6 +245,7 @@ async function onTest(data, browserWindow) { | |||
*/ | |||
synthCliProcess = fork(`${SYNTHETICS_CLI}`, args, { | |||
env: { | |||
...process.env, |
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.
This is something I'm uncertain about as I'm not accustomed to the security implications of changes to CLI interaction. From speaking offline it seems like this is not a large risk, but I'd like to hear others' opinions.
scripts/beforePack.js
Outdated
return Promise.all([downloadForPlatform(platform), fixSharp(arch, platform)]); | ||
}; | ||
|
||
const { Arch } = require("electron-builder"); |
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.
Is there a functional reason that Arch
needs to be required after beforePack
is initialized?
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.
Oh good catch @justinkambic, I will move it to the top of the file as it should be 👍
abee6f0
to
77c005d
Compare
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.
I'll follow @justinkambic's on this and leave this as a passive LGTM considering that filtering out the environment variables with our keys should be enough to prevent it from being passed downwards to other packages, although I do think we should just consult with security to be sure before merging.
As a note, I've also tested E2E the build for my MacOS and it worked fine for recording, replaying, and generating code for a journey.
Works fine on windows! |
Currently, we do not store any secrets in env variables. When we add then, we should remember and filter those secrets out when spawning the child process. We probably need to consider naming convention for it so we don't need to manually add what to filter. For example, we can name sensitive env variables with |
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.
LGTM, Great work @kyungeunni
scripts/afterPack.js
Outdated
const platform = ctx.electronPlatformName; | ||
if (platform === "linux") { | ||
const resourcesPath = path.join(ctx.appOutDir, "resources"); | ||
changePermission(resourcesPath); |
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.
Did you miss the return statement here?
77c005d
to
63c4ae9
Compare
scripts/beforePack.js
Outdated
exports.default = function beforePack(ctx) { | ||
const arch = Arch[ctx.arch]; | ||
const platform = ctx.electronPlatformName; | ||
return Promise.all([downloadForPlatform(platform), fixSharp(arch, platform)]); |
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.
I just noted this today, which function would be responsible for handling the error when one of the promises break? Does electron builder takes care of this?
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.
The behavior I observed is that when one of the promises breaks, the electron-builder would throw an error and abort the building process, which is what I expect it to do so. (downloading a browser and sharp lib for each platform is a prerequisite prior to bundling)
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.
If the error handling is taken care of the electron-builder, then all good 👍🏽 I just wanted us to avoid UnhandledPromiseRejection errors.
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.
Just adding an approval here considering the conversation we've just had with someone representing the security team (meaning we won't do any signing at this stage and thus not pass credentials down through |
…-in-the-ci * upstream/main: Disable record journey button when URL input is empty. (elastic#218) Bump EUI version. (elastic#219) fix: use action.url instead of frameUrl in header (elastic#212) fix: Reset default values when action/assertion edit view closes (elastic#208) Fix Header layout when paused. (elastic#206) Make Test button disabled during recording/paused states. (elastic#207) chore: Add update to `README.md` to explain undefined reference error (elastic#198) chore: update .prettierrc to inherit Kibana's config (elastic#197) 0.0.1-alpha.3 Revert release version in `package.json` to reflect current version. feat: Step divider UI (elastic#175) chore: Remove unused exports (elastic#189) chore: Download browsers for all platforms to release builds from single machine (elastic#165) fix: wrap control button with tooltip when necessary only (elastic#184) fix: URL input expands for longer value (elastic#178) ci: release automation for unsigned artifacts (stage 1) (elastic#183) feat: switch `Steps` data structure from nested array to map (elastic#171) fix: reference the correct pw fork (elastic#186)
Summary
fixes #150
fixes #172 (fixed by 891b9f6)
This PR adds an additional pre-build step. When running
npm run release
script, the pre-build script will download browsers for Mac Windows, and Linux (if the browsers are already downloaded, it will skip downloading steps. The path contains the revision number so it also prevents packaging the stale browser.). Then electron builder will include the right browser for each platform as specified inelectron-builder.yml
.Implementation details
I couldn't utilize the playwright's registry library, as they are hardly coupled with the host machine's OS. Instead, I've used fetchBrowser library that is used by the registry's install function.
How to validate this change
npm run pack -- -mwl
. It will digest builds in your localdist
directory without publishing it..deb
for the time being)Next Steps