Skip to content

Commit

Permalink
Add --path filtering option
Browse files Browse the repository at this point in the history
You can now filter by one or more paths under which the projects should be
  • Loading branch information
pnezis committed Oct 4, 2024
1 parent f45466f commit bc5187b
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 3 deletions.
8 changes: 7 additions & 1 deletion workspace/lib/mix/tasks/workspace.list.ex
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ defmodule Mix.Tasks.Workspace.List do
:config_path,
:project,
:exclude,
:paths,
:tags,
:excluded_tags,
:dependency,
Expand Down Expand Up @@ -84,6 +85,11 @@ defmodule Mix.Tasks.Workspace.List do
$ mix workspace.list --exclude-tag deprecated
If your monorepo has a nested structure you can list projects only under one or more
specific paths.
$ mix workspace.list --path packages/shared --path packages/infra
In large monorepos you may want to get all projects depending on a specific package. You
can achieve this through the `--dependency` option:
Expand All @@ -108,7 +114,7 @@ defmodule Mix.Tasks.Workspace.List do
In order to get all projects associated with a specific maintainer:
$ mix workspace.list --maintainer "Jack Sparrow"
# notice that the search is case insensitive, this works as well
$ mix workspace.list --maintainer sparrow
Expand Down
1 change: 1 addition & 0 deletions workspace/lib/mix/tasks/workspace.run.ex
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ defmodule Mix.Tasks.Workspace.Run do
:project,
:exclude,
:tags,
:paths,
:excluded_tags,
:affected,
:modified,
Expand Down
10 changes: 10 additions & 0 deletions workspace/lib/workspace/cli_options.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ defmodule Workspace.CliOptions do
doc: "Ignore the given projects",
doc_section: :filtering
],
paths: [
type: :string,
multiple: true,
long: "path",
doc: """
A path under which projects will be considered. Paths should be relative with respect to
the workspace root. All other projects can be ignored. Can be set multiple times.
""",
doc_section: :filtering
],
tags: [
type: :string,
multiple: true,
Expand Down
16 changes: 15 additions & 1 deletion workspace/lib/workspace/filtering.ex
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ defmodule Workspace.Filtering do
* `:tags` (list of `t:Workspace.Project.tag()`) - a list of tags to be
considered. All projects that do not have at least one the specified tags will
be skipped.
* `:paths` (list of `t:String.t()`) - a list of project paths to be considered.
Only projects located under any of the provided path will be considered.
* `:dependency` (`t:atom/0`) - keeps only projects that have the given dependency.
* `:dependent` (`t:atom/0`) - keeps only dependencies of the given project.
* `:affected` (`t:boolean/0`) - if set only the affected projects will be
Expand Down Expand Up @@ -79,7 +81,8 @@ defmodule Workspace.Filtering do
excluded_tags: opts[:excluded_tags] || [],
tags: Enum.map(opts[:tags] || [], &maybe_to_tag/1),
dependency: maybe_to_atom(opts[:dependency]),
dependent: maybe_to_atom(opts[:dependent])
dependent: maybe_to_atom(opts[:dependent]),
paths: opts[:paths]
]

Enum.map(workspace.projects, fn {_name, project} ->
Expand Down Expand Up @@ -117,6 +120,7 @@ defmodule Workspace.Filtering do
excluded_tag?(project, opts[:excluded_tags]) or
not_selected_project?(project, opts[:selected]) or
not_selected_tag?(project, opts[:tags]) or
not_in_paths?(project, opts[:paths]) or
not_root?(project, opts[:only_roots]) or
not_affected?(project, opts[:affected]) or
not_modified?(project, opts[:modified]) or
Expand All @@ -137,6 +141,16 @@ defmodule Workspace.Filtering do
defp not_selected_tag?(project, tags),
do: Enum.all?(project.tags, fn tag -> tag not in tags end)

defp not_in_paths?(_project, nil), do: false

defp not_in_paths?(project, paths) do
Enum.all?(paths, fn path ->
path = Path.join(project.workspace_path, path) |> Path.expand()

not String.starts_with?(project.mix_path, path <> "/")
end)
end

defp not_root?(_project, false), do: false
defp not_root?(project, true), do: not project.root?

Expand Down
19 changes: 19 additions & 0 deletions workspace/test/mix/tasks/workspace.list_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,25 @@ defmodule Mix.Tasks.Workspace.ListTest do
end) == expected
end

test "filtering by --path" do
expected = """
Found 2 workspace projects matching the given options.
* :package_default_c package_default_c/mix.exs
* :package_default_d package_default_d/mix.exs
"""

assert capture_io(fn ->
ListTask.run([
"--workspace-path",
@sample_workspace_default_path,
"--path",
"package_default_c",
"--path",
"package_default_d"
])
end) == expected
end

test "with --json option set" do
output = Path.join(@sample_workspace_changed_path, "workspace.json")

Expand Down
3 changes: 2 additions & 1 deletion workspace/test/workspace/cli_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ defmodule Workspace.CliTest do
:tags,
:excluded_tags,
:dependency,
:dependent
:dependent,
:paths
]

test "valid options sanity check" do
Expand Down
20 changes: 20 additions & 0 deletions workspace/test/workspace/filtering_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,26 @@ defmodule Workspace.FilteringTest do
end
end

test "with paths set", %{workspace: workspace} do
workspace = Workspace.Filtering.run(workspace, paths: ["packages"])

refute Workspace.project!(workspace, :bar).skip
refute Workspace.project!(workspace, :baz).skip
refute Workspace.project!(workspace, :foo).skip

workspace = Workspace.Filtering.run(workspace, paths: ["packages/foo", "packages/baz"])

assert Workspace.project!(workspace, :bar).skip
refute Workspace.project!(workspace, :baz).skip
refute Workspace.project!(workspace, :foo).skip

workspace = Workspace.Filtering.run(workspace, paths: ["packages/food", "packages/baze"])

assert Workspace.project!(workspace, :bar).skip
assert Workspace.project!(workspace, :baz).skip
assert Workspace.project!(workspace, :foo).skip
end

test "with dependency set", %{workspace: workspace} do
workspace = Workspace.Filtering.run(workspace, dependency: :invalid)

Expand Down

0 comments on commit bc5187b

Please sign in to comment.