-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
Ability to store a built package in offline mirror #5314
Ability to store a built package in offline mirror #5314
Conversation
This change will increase the build size from 10.46 MB to 10.48 MB, an increase of 22.83 KB (0%)
|
dd4bb39
to
d6a1646
Compare
src/package-install-scripts.js
Outdated
|
||
} | ||
|
||
static getPrebuiltName(pkg: Manifest): string { |
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.
todo: put in a separate file
@@ -145,22 +143,6 @@ test('reading a lockfile should not optimize it', async () => { | |||
}); | |||
}); | |||
|
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.
moved offline mirror tests out
}); | ||
}); | ||
|
||
test('creates the file in the mirror when fetching a git repository', async () => { |
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.
following tests are copied from integration.js file
/* @flow */ | ||
|
||
export function getPlatformSpecificPackageFilename(pkg: {name: string, version: string}): string { | ||
// TODO support hash for all subdependencies that have installs scripts |
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 will be problematic, yeah :/ Something I had in mind was that maybe we could implement nested lockfiles to solve this issue. Given a nested lockfile, we could guarantee that a package would always be built against a specific set of dependencies, and we could cache it more efficiently. Wdyt?
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 think nested lockfile is an overkill and may be confusing considering that we don't support nested lockfiles in general.
We might generate something in between - a lock-like file that will only have dependencies
section because we don't really need "version" and "resolved" fields as they are already defined in the parent yarn.lock
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.
On the other hand having that file inside the .tgz file requires us to unzip it before checking whether the archive works for us, we need to encode all dependencies into the file suffix and hashing them seems a reasonable way to shorten the suffix
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.
Anyway, we'll discuss it in V2 of this feature, we need to see how it will be used by the users if it will be :)
src/util/package-name-utils.js
Outdated
// TODO support hash for all subdependencies that have installs scripts | ||
const normaliseScope = name => (name[0] === '@' ? name.substr(1).replace('/', '-') : name); | ||
const suffix = getSystemParams(); | ||
return `${normaliseScope(pkg.name)}-v${pkg.version}-${suffix}`; |
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: normalize
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.
done
src/config.js
Outdated
@@ -328,6 +331,7 @@ export default class Config { | |||
this.enableMetaFolder = Boolean(this.getOption('enable-meta-folder')); | |||
this.enableLockfileVersions = Boolean(this.getOption('yarn-enable-lockfile-versions')); | |||
this.linkFileDependencies = Boolean(this.getOption('yarn-link-file-dependencies')); | |||
this.packBuiltPackages = Boolean(this.getOption('pack-built-packages')); |
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.
Shouldn't the option be called experimental-build-mirror
or something like this, to clearly state that it's experimental?
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, makes sense.
How about experimental-pack-script-packages-in-mirror
?
src/package-install-scripts.js
Outdated
if (this.packageCanBeInstalled(pkg)) { | ||
const offlineMirrorPath = this.config.getOfflineMirrorPath(); | ||
if (!offlineMirrorPath) { | ||
break; |
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 packBuiltPackages option is set but no offline mirror has been set, the saveBuildArtifacts
function won't be called. Is it expected?
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.
Good point!
Not inteded
src/package-install-scripts.js
Outdated
}, | ||
this.reporter, | ||
); | ||
const stream = await pack(pkgConfig, loc); |
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.
Does pack
also includes the build artifacts? :o
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 pass loc
which from local node_modules
with built artifacts, pack just generates a .tgz from a folder
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 name loc better to be clear
a3a0ab4
to
c6fc640
Compare
@arcanis, ready for another round |
src/package-install-scripts.js
Outdated
|
||
const fs2 = require('fs'); |
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 find that a bit confusing - maybe createWriteStream should be simply reexported from util/fs
(or maybe we should use fs-extra
which takes care of exporting promises when we need them).
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, fs2 is a poor choice for naming this variable.
Let's do it like in other places the Node.js fs module should be fs
but out promise wrappers should be named fsUtil
.
A year ago there was a proposal to reexport all exports from Node.js fs
module there but we decided not to do it because it means we would have to manually list all functions of Node.js fs
. Could be just extra trouble to worry when Node.js introduces a new function or breaks API
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.
fs-extra
automatically exports all the fs
Node functions in such a way that they automatically support promises. Using it would allow us to let someone else care about keeping the list updated! :) Ok, not urgent, let's keep this for later
src/package-install-scripts.js
Outdated
}); | ||
pkg.prebuiltVariants = pkg.prebuiltVariants || {}; | ||
pkg.prebuiltVariants[prebuiltFilename] = hash; | ||
// this.artifacts[`${pkg.name}@${pkg.version}`] = [prebuiltFilename]; |
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.
Can we remove this line if it isn't used?
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.
Doh, debugging code
const offlineMirrorPath = this.config.getOfflineMirrorPath(); | ||
if (prebuiltVariants[prebuiltName] && offlineMirrorPath) { | ||
const filename = path.join(offlineMirrorPath, 'prebuilt', prebuiltName + '.tgz'); | ||
if (shrunk._remote && (await fs.exists(filename))) { |
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.
Fwiw the Node documentation recommends using fs.existsSync
instead of fs.exists
to prevent race condition as much as possible (fs.exists
is actually deprecated).
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 have 59 places where this wrapper is used.
If we migrate away from it we should do it in a separate PR, right?
src/package-install-scripts.js
Outdated
for (const pkg of pkgs) { | ||
if (this.packageCanBeInstalled(pkg)) { | ||
let prebuiltPath = path.join(offlineMirrorPath, 'prebuilt'); | ||
if (!await fs.exists(prebuiltPath)) { |
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 don't have to check it exists since we're using mkdirp
which will do it for us, right?
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, makes sense, fixed
@arcanis, ready for another round |
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! Can't wait to see how people will use it!
Question: does this depend on the engine being used? because some dependencies need to be rebuilt if installed for node or electron |
@hoodie, currently platform, architecture and modules (Node libs version) are taken into account Feel free to send a PR to add another parameter. |
@bestander that seems to be thorough enough, thank you :D |
I absolutely love this feature. This adds a "supported" way to bring hermetic builds across platforms and environments. |
For those wishing to enable this feature from a clean yarn installation, simply to speed up local development rebuilds - add the following in your project root: .yarnrc
.gitignore
Loving this feature already! |
We're having issues with this feature, as it saves references to prebuilt packages to |
@bestander: Looks like |
No :)
Could you send a PR to fix that if you have time?
…On 28 March 2018 at 11:57, Andrew Lane ***@***.***> wrote:
Looks like yarn-offline-mirror-pruning true deletes./<yarn-offline-cache>/
prebuilt/*, is this as designed?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#5314 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/ACBdWI9tPa6SkDUr4v6d6egLlvL7xIG3ks5ti90SgaJpZM4R4GRn>
.
|
@bestander - if you can confirm that the |
Please do and thank you! |
@theandrewlane I have the same problem, that |
Summary
Packages with install scripts will be saved into a .tgz file into offline mirror if
experimental-pack-script-packages-in-mirror
is set in .yarnrc.Next time you run
yarn install
instead of unpacking the original .tgz package and running scripts yarn unpacks the previously built one and does not run any scripts.Good for CI systems, slow machines and when you don't trust packages to do run shady download scripts.
Of course it adds platform specific suffixes to the .tgz file name, so that you can safely share the prebuilt offline mirror folder with other OS and Node versions.
Follow up: blog post and docs.
Test plan
Added tests for all common cases: rerunning install, switching Node version etc.