-
Notifications
You must be signed in to change notification settings - Fork 585
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
[rush] deferredInstallationScripts & --ignore-scripts #3537
base: main
Are you sure you want to change the base?
[rush] deferredInstallationScripts & --ignore-scripts #3537
Conversation
libraries/rush-lib/src/logic/installManager/WorkspaceInstallManager.ts
Outdated
Show resolved
Hide resolved
} else { | ||
// full install | ||
if (newState.selectedProjectNames === oldState.selectedProjectNames) { | ||
return true; |
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.
It's bad for maintenance to have return true
statements short circuiting the "does this object match" checker. The only early returns should be return false
, and return true
is the result when all the individual checks have passed.
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 agree with you. but i have no idea how to do it here. Could you give some suggestions on how to change this part of code for better maintenance.
libraries/rush-lib/src/logic/installManager/WorkspaceInstallManager.ts
Outdated
Show resolved
Hide resolved
libraries/rush-lib/src/logic/installManager/WorkspaceInstallManager.ts
Outdated
Show resolved
Hide resolved
libraries/rush-lib/src/logic/installManager/WorkspaceInstallManager.ts
Outdated
Show resolved
Hide resolved
@@ -372,6 +376,10 @@ export class WorkspaceInstallManager extends BaseInstallManager { | |||
} | |||
|
|||
protected async postInstallAsync(): Promise<void> { | |||
if (this.twoStageInstall) { | |||
this.commonTempInstallFlag.saveIfModified(); |
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 use of this flag file feels like it's getting muddled by this change. It used to mean "everything in the repo is installed and ready to use", now it means "some subset of the repo is installed and possibly the post-install scripts have been run".
It would make sense to use this file to do more specific state tracking (I like that you're including the scope in the file now). We should also care about running the post-install scripts though since not running them could leave your repo in a less-usable state. Maybe we should check this file, and if ignoreScripts
is true
and this.options.ignoreScripts
is false, we execute this stage, then write ignoreScripts: false
in the flag file?
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.
It kind of complicated. Say we have run rush install --ignore-scripts --to rush
, the flag file records:
"selectedProjects": ["<other_deps>", ..., "rush-lib", "rush"]
"ignoreScripts": true
Now, if i run rush install --to rush-lib
, the stage 1 pnpm install --ignore-scripts
should be skipped, since --to rush-lib
is a subset of --to rush
. And Rush just run stage 2 pnpm rebuild --filter rush-lib...
. Now we can not mark ignoreScripts: false
in the flag file. Since the selectedProjects
contains "rush", which is out of range of --filter rush-lib...
One possible solution maybe is to record more information in one selected project in the flag file, use the above case as an example, the flag file will be
"selectedProjects": [
// ... other deps
{
packageName: 'rush-lib',
ignoreScripts: false,
},
{
packageName: 'rush',
ignoreScripts: true, // <--- because we run "pnpm rebuild --pending --filter rush-lib..." which excludes "rush"
}
]
In this way, in postinstallAsync
we need calculate ignoreScripts
based on all selectedProjects to figure out whether we can skip "pnpm rebuild --pending"...
Do you think it's worth the efforts to implement this logic? For now, i choose an easy implementation which run "pnpm rebuild --pending [--filter <project...>]" everytime. --pending
makes pnpm to decide whether there is any script we need to run.
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 question. I think keeping more detailed info on the selected projects does make sense... it allows you to be very specific about the work you're performing and when you want to perform that work. Another option IMO would be to execute the install from scratch again (ie. running pnpm install --ignore-scripts ...
then pnpm rebuild ...
) whenever non-scoping arguments to the install have changed.
Basically, I feel like we either need to commit to tracking each project install on a per-project basis, or we need to treat all other options as something that could affect all projects and start from scratch.
throw new Error(`Encounter an error when running install lifecycle scripts. error: ${err.message}`); | ||
} finally { | ||
// Always save after pnpm rebuild to update timestamp of last install flag file. | ||
this.commonTempInstallFlag.create(); |
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.
Previously, this flag would only exist if there was a successful install. Use should be updated here to keep in line with what I describe above (update the flag for ignoreScripts
in the case that the post-install scripts run + succeed, or leave as-is and use it to skip the install but use to execute post-install 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.
The reason why invoking creating last install flag here: canSkipInstall
compares a timestamp(mtime of this flag file) with mtime of node_modules folder. And no matter pnpm rebuild --pending
runs successfully or not, node_modules
folder is touched, i.e. mtime updated. So we need to update the mtime of this flag file to make the canSkipInstall
works again.
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.
Right, but skipping an install in the event of an error doesn't make sense. It should be updated only after the command is successfully executed. If the command fails, then the state of the repo is unknown and the last-install flag should probably be deleted.
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.
It's kindof fight against the main point of the proposal, faster retries on postinstall scripts.
Imagine
pnpm install --ignore-scripts --filter <a...>
👆 success
pnpm rebuild --pending --filter <a...>
👆 fail
In this case, if Rush.js doesn't update the last-install.flag. The "canSkipInstall" will never return true in this case. which triggers a "pnpm install --ignore-scripts --filter <a...>" again in next "rush install/update"...
but, the main point is to make "canSkipInstall" returns true in this case, and only the stage 2 command("pnpm rebuild") runs, which is the "faster retires" part...
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.
Then in that case it should write the flag file twice, once after performing the install (with ignoreScripts: true
), then once after running the post-install scripts (with ignoreScripts: false
). We shouldn't be saying "yes, everything is installed correctly" regardless of success or failure, that defeats the entire purpose of the flag file.
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.
in that case it should write the flag file twice, once after performing the install
+1 Rush install is meant to be transactionally safe. It is a common optimization for CI agents to reuse the same git clone
working folder from a previous job. And it is very common for the previous rush install
to have been terminated because someone pushed an update to their PR branch, which causes the previous CI job to get immediately cancelled and restarted.
0392f2e
to
d1529e9
Compare
* If true, rush install or rush update implicitly specify --ignore-scripts during pnpm install, | ||
* and run install lifecycle scripts by pnpm rebuild --pending after pnpm install successfully. | ||
*/ | ||
/*[LINE "HYPOTHETICAL"]*/ "deferredInstallationScripts": true |
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.
Will this eventually be our recommended approach?
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 think so
libraries/rush-lib/src/logic/installManager/WorkspaceInstallManager.ts
Outdated
Show resolved
Hide resolved
libraries/rush-lib/src/logic/installManager/WorkspaceInstallManager.ts
Outdated
Show resolved
Hide resolved
d1529e9
to
7fa7ee2
Compare
Updates 2022/8/7:
|
Update: Sync this branch with the latest main branch |
"changes": [ | ||
{ | ||
"packageName": "@microsoft/rush", | ||
"comment": "Supports deferredInstallationScripts behind a experimental flag, which makes faster retires for install lifecycle 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.
"comment": "Supports deferredInstallationScripts behind a experimental flag, which makes faster retires for install lifecycle scripts", | |
"comment": "Add support for a `--deferredInstallationScripts` CLI flag behind an experimental flag, which allows for faster retires of dependency install lifecycle 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.
Actually, there is no --defereredInstallScripts
CLI flag. When turn on the experimental flag, post install scripts are invoked after pnpm install
during "rush install/update"...
Dose "Add support for deferred running installation scripts behind an experimental flag, which allows for faster retires of dependency install lifecycle scripts."
work for you?
Update:
|
Summary
This PR implements "two stage install" proposal.
close #3530
Details
This feature is now behind a flag in
experiments.json
:"deferredInstallationScripts": true
Workflow now:
Goal: faster retries for install lifecycle scripts:
If step3 fails, such as encountering an error in a "postinstall" scripts, rerun "rush install" will skip step2, which gets a faster retry on failed scripts.
How it was tested
Use rushstack repo for example
rush --debug install -t rush
two stage install works :)
rush --debug install -t rush-lib
Because
-t rush-lib
is a subset of-t rush
, stage1(pnpm install w/ --ignore-scripts) was skipped, and stage2(pnpm rebuild --pending) was run. At the same time, there is no pending rebuild scripts to run. rebuild finished in seconds.