Current section

Files

Jump to
workspace lib mix tasks workspace.list.ex
Raw

lib/mix/tasks/workspace.list.ex

defmodule Mix.Tasks.Workspace.List do
opts = [
format: [
type: :string,
default: "pretty",
doc: """
The output format of the list. It can be one of the following:
* `json` - pretty prints the list as a json.
* `pretty` - pretty prints the list.
""",
allowed: ["json", "pretty"]
],
json: [
type: :boolean,
doc: """
If set a `json` file will be generated with the list of workspace projects and
associated metadata. By default it will be saved in `workspace.json` in the
current directory. You can override the output path by setting the `--output`
option.
""",
deprecated: "Use `--format json` with `--output` instead.",
doc_section: :export
],
output: [
type: :string,
doc: """
Save the list to a file. Applicable only if `--format` is set to `json`.
""",
doc_section: :export
],
relative_paths: [
type: :boolean,
default: false,
doc: """
If set the paths in the exported json file will be relative with respect to the
workspace path. Applicable only if `--format` is set to `json`.
""",
doc_section: :export
],
maintainer: [
type: :string,
doc: """
Search for projects with the given maintainer. A partial case insensitive string search
is performed so you can provide only part of the maintainer's name.
""",
doc_section: :filtering
]
]
@options_schema Workspace.Cli.options(
[
:workspace_path,
:config_path,
:project,
:exclude,
:paths,
:tags,
:excluded_tags,
:dependency,
:dependent,
:show_status,
:affected,
:modified,
:base,
:head
],
opts
)
@shortdoc "Display info about the workspace projects"
@moduledoc """
Shows workspace project info
$ mix workspace.list
By default the following are displayed:
- the project app name
- the project path with respect to workspace path
- the description if set
- the project tags
## Command line options
#{CliOptions.docs(@options_schema, sort: true, sections: Workspace.CliOptions.doc_sections())}
## Filtering projects
Several command line options can limit the returned projects and filter the
workspace.
You can list only projects with a specific tag:
$ mix workspace.list --tag core
Or exclude projects with a specific tag:
$ 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:
$ mix workspace.list --dependency foo
Additionally you can specify the `--dependent` flag to consider only dependencies of a
given project:
$ mix workspace.list --dependent foo
You can also filter by the project's maintainer. The search is case insensitive. The
maintainers are expected to be defined under `package`:
def project do
[
package: [
maintainers: ["Jack Sparrow"]
],
# rest project options
]
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
"""
use Mix.Task
alias Workspace.Cli
@impl Mix.Task
def run(args) do
{opts, _args, _extra} = CliOptions.parse!(args, @options_schema)
opts
|> Mix.WorkspaceUtils.load_and_filter_workspace()
|> maybe_search_projects(opts)
|> list_or_save_workspace_projects(opts)
end
defp maybe_search_projects(workspace, opts) do
search_by_maintainer(workspace, opts[:maintainer])
end
defp search_by_maintainer(workspace, nil), do: workspace
defp search_by_maintainer(workspace, maintainer) do
projects =
Enum.map(workspace.projects, fn {_name, project} ->
case matches_maintainer?(project, maintainer) do
true -> project
false -> Map.put(project, :skip, true)
end
end)
Workspace.State.set_projects(workspace, projects)
end
defp matches_maintainer?(project, maintainer) do
maintainers = get_in(project.config, [:package, :maintainers]) || []
maintainers = maintainers |> Enum.join(" ") |> String.downcase()
String.contains?(maintainers, String.downcase(maintainer))
end
defp list_or_save_workspace_projects(workspace, opts) do
# TODO: remove --json support in 0.4.0
opts =
if opts[:json] do
Keyword.merge(opts, format: "json", output: opts[:output] || "workspace.json")
else
opts
end
output_result(workspace, opts, opts[:format])
end
defp output_result(workspace, opts, "pretty") do
projects = Workspace.projects(workspace)
case Enum.count(projects, &(not &1.skip)) do
0 ->
Cli.log([:bright, :yellow, "No matching projects for the given options", :reset])
valid ->
Cli.log([
"Found ",
:bright,
:blue,
"#{valid} workspace projects",
:reset,
" matching the given options."
])
max_project_length = max_project_length(projects)
projects
|> Enum.sort_by(& &1.app)
|> Enum.each(&print_project_info(&1, max_project_length, opts[:show_status]))
end
end
defp output_result(workspace, opts, "json") do
json_data = Workspace.Export.to_json(workspace, sort: true, relative: opts[:relative_paths])
case opts[:output] do
nil ->
IO.puts(json_data)
output ->
File.write!(output, json_data)
Workspace.Cli.log([:green, "generated ", :reset, output], prefix: :header)
end
end
defp max_project_length([]), do: 0
defp max_project_length(projects) do
projects
|> Enum.map(fn project -> inspect(project.app) |> String.length() end)
|> Enum.max()
end
defp print_project_info(%Workspace.Project{skip: true}, _length, _show_status), do: :ok
defp print_project_info(project, max_project_length, show_status) do
indent_size = max_project_length - String.length(inspect(project.app))
indent = String.duplicate(" ", indent_size)
Cli.log([
" * ",
Cli.project_name(project, show_status: show_status, pretty: true),
indent,
:light_black,
" ",
Path.relative_to(project.mix_path, project.workspace_path),
:reset,
description(project.config[:description]),
tags(project.tags)
])
end
defp description(nil), do: ""
defp description(doc) when is_binary(doc), do: [" - ", doc]
defp tags([]), do: []
defp tags(tags) do
tags =
Enum.map(tags, fn tag -> [:tag, Workspace.Project.format_tag(tag), :reset] end)
|> Enum.intersperse(", ")
[" " | tags]
end
end