Skip to content

Commit

Permalink
Support filtering by dependency
Browse files Browse the repository at this point in the history
  • Loading branch information
pnezis committed Sep 30, 2024
1 parent df1a015 commit c32910e
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 6 deletions.
1 change: 1 addition & 0 deletions workspace/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [Unreleased]

* `workspace.list`: support filtering by dependency
* `workspace.list`: add `--maintainer` for filtering projects with the given maintainer

## [v0.1.1](https://github.com/sportradar/elixir-workspace/tree/workspace/v0.1.1) (2024-07-04)
Expand Down
6 changes: 6 additions & 0 deletions workspace/lib/mix/tasks/workspace.list.ex
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ defmodule Mix.Tasks.Workspace.List do
:exclude,
:tags,
:excluded_tags,
:dependency,
:show_status
],
opts
Expand Down Expand Up @@ -82,6 +83,11 @@ defmodule Mix.Tasks.Workspace.List do
$ mix workspace.list --exclude-tag deprecated
In large monorepos you may want to get all projects depending on a specific package. You
can achieve this through the `--dependency` option:
$ mix workspace.list --dependency foo
You can also filter by the project's maintainer. The search is case insensitive. The
maintainers are expected to be defined under `package`:
Expand Down
8 changes: 8 additions & 0 deletions workspace/lib/workspace/cli_options.ex
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ defmodule Workspace.CliOptions do
""",
doc_section: :filtering
],
dependency: [
type: :string,
multiple: false,
doc: """
If set, only projects that have the given dependency will be considered.
""",
doc_section: :filtering
],
excluded_tags: [
type: :string,
multiple: true,
Expand Down
17 changes: 13 additions & 4 deletions workspace/lib/workspace/filtering.ex
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ 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.
* `:dependency` (`t:atom/0`) - keeps only projects that have the given dependency.
* `:affected` (`t:boolean/0`) - if set only the affected projects will be
included and everything else will be skipped. Defaults to `false`.
* `:modified` (`t:boolean/0`) - if set only the modified projects will be
Expand Down Expand Up @@ -75,14 +76,16 @@ defmodule Workspace.Filtering do
modified: opts[:modified] || false,
only_roots: opts[:only_roots] || false,
excluded_tags: opts[:excluded_tags] || [],
tags: Enum.map(opts[:tags] || [], &maybe_to_tag/1)
tags: Enum.map(opts[:tags] || [], &maybe_to_tag/1),
dependency: maybe_to_atom(opts[:dependency])
]

Enum.map(workspace.projects, fn {_name, project} ->
Map.put(project, :skip, skippable?(project, opts))
Map.put(project, :skip, skippable?(workspace, project, opts))
end)
end

defp maybe_to_atom(nil), do: nil
defp maybe_to_atom(value) when is_atom(value), do: value
defp maybe_to_atom(value) when is_binary(value), do: String.to_atom(value)

Expand All @@ -107,14 +110,15 @@ defmodule Workspace.Filtering do
do:
raise(ArgumentError, "invalid tag, it should be a string of the form `tag` or `scope:tag`")

defp skippable?(project, opts) do
defp skippable?(workspace, project, opts) do
excluded_project?(project, opts[:excluded]) or
excluded_tag?(project, opts[:excluded_tags]) or
not_selected_project?(project, opts[:selected]) or
not_selected_tag?(project, opts[:tags]) or
not_root?(project, opts[:only_roots]) or
not_affected?(project, opts[:affected]) or
not_modified?(project, opts[:modified])
not_modified?(project, opts[:modified]) or
not_dependency?(workspace, project, opts[:dependency])
end

defp excluded_project?(project, excluded), do: project.app in excluded
Expand All @@ -138,4 +142,9 @@ defmodule Workspace.Filtering do

defp not_modified?(_project, false), do: false
defp not_modified?(project, true), do: not Workspace.Project.modified?(project)

defp not_dependency?(_workspace, _project, nil), do: false

defp not_dependency?(workspace, project, dependency),
do: dependency not in Workspace.Graph.dependencies(workspace, project.app)
end
17 changes: 17 additions & 0 deletions workspace/test/mix/tasks/workspace.list_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,23 @@ defmodule Mix.Tasks.Workspace.ListTest do
end) == expected
end

test "filtering by --dependency" do
expected = """
Found 2 workspace projects matching the given options.
* :package_default_a package_default_a/mix.exs :shared, area:core
* :package_default_h package_default_h/mix.exs
"""

assert capture_io(fn ->
ListTask.run([
"--workspace-path",
@sample_workspace_default_path,
"--dependency",
"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 @@ -26,7 +26,8 @@ defmodule Workspace.CliTest do
:base,
:head,
:tags,
:excluded_tags
:excluded_tags,
:dependency
]

test "valid options sanity check" do
Expand Down
18 changes: 17 additions & 1 deletion workspace/test/workspace/filtering_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ defmodule Workspace.FilteringTest do
import Workspace.TestUtils

setup do
project_a = project_fixture(app: :foo, workspace: [tags: [:foo, {:scope, :ui}]])
project_a =
project_fixture(app: :foo, workspace: [tags: [:foo, {:scope, :ui}]], deps: [{:bar}])

project_b = project_fixture(app: :bar, workspace: [tags: [:bar]])
project_c = project_fixture(app: :baz, workspace: [tags: [:foo, :bar]])

Expand Down Expand Up @@ -115,5 +117,19 @@ defmodule Workspace.FilteringTest do
Workspace.Filtering.run(workspace, tags: [1])
end
end

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

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

workspace = Workspace.Filtering.run(workspace, dependency: :bar)

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

0 comments on commit c32910e

Please sign in to comment.