rushstack
rushstack copied to clipboard
[rush] Allow ProjectChangeAnalyzer to acccept an ignore glob
Summary
When writing tools that use ProjectChangeAnalyzer, it would be very convenient to be able to pass a custom list of ignore globs that can augment (or override) the existing project-specific ignore globs.
Add an option that can be used by monorepo tooling (but isn't exposed in any way to the rush command-line) to support this.
Details
Here's the scenario that spawned this PR: in pull requests, we want to run terraform plan on all projects that have impacted infrastructure. However, due to the way terraforming has been designed across the organization, we need to do so by using a reusable workflow that we don't control. This means it's really important to us not to run plan unless we think there's a good chance that infrastructure has been impacted.
Our criteria for this is as follows:
- List all projects that have git diffs (compared to same target e.g.
main) - Filter this list to only projects that have touched files in the relative folder
infra/terraform - From this list, calculate "impacted by" (project and all recursive dependents)
- From this list, filter to only projects tagged
terraform.
(We use the inherit Rush project structure to list infrastructure components as "dependencies" of consuming projects; this allows us to find all projects that might need to plan infrastructure changes, if any project they depend on has changes in the folder where infrastructure lives.)
How it was tested
Example usage in our monorepo:
// Find all projects changed in the specified folder
await changeAnalyzer.getChangedProjectsAsync({
targetBranchName: 'origin/main',
enableFiltering: true,
includeExternalDependencies: false,
terminal: terminal,
ignoreGlobs: ['*', '!*/', '!infra/terraform/**']
})
I'd be more a fan of removing the glob handling from ProjectChangeAnalyzer altogether and instead take as input a filterByProject?: Map<RushConfigurationProject, (relativePath: string) => boolean> parameter (if not provided, default filter is (relativePath: string) => true). Said map could get constructed by reading the RushProjectConfiguration files, but reading config files shouldn't really be in the scope of ProjectChangeAnalyzer.
It also comes up because for the existing filtering, the ignore/additional globbing will need to be scoped to a specific phase, not an entire project.
@dmichon-msft Makes sense to me!
Maybe even filterByProject?: (project: RushConfigurationProject, relativePath: string) => boolean?
I realize either approach would allow Rush to lazy-lookup the project configuration, but I think a single function that gets passed both args will be a lot easier for casual tool developers to reason about.
@elliot-nelson The filterByProject?: (project: RushConfigurationProject, relativePath: string) => boolean syntax would work fine for this particular API. Since the per-operation deps scenario is going to need to support querying the same project data multiple times with different filters, it's probably not worth worrying about optimizing for the ability to query project, then filter, and skip the filter calls if the project didn't return a filter. For getChangedProjectsAsync the filter will only be passed changed files and only needs to be called on files from a particular project until it finds at least one match.
I can go either way between both arguments in one function or having a function/map that takes the project and returns a filter for later querying.