[Proposal] Run mix tasks from packages directly from the SCM

40 views
Skip to first unread message

Mitchell Hanberg

unread,
Jun 4, 2024, 2:04:42 PMJun 4
to elixir-lang-core
TLDR:

A builtin mix task to run mix tasks directly from hex or a git repository (an SCM).

`mix x phx_new phx.new`

Problem:

Currently if you would like to distribute a mix task or CLI tool, you either need to have the user first install an archive (like `mix archive.install hex phx_new`) or the user needs to  package and distribute it themselves.

The Node.js ecosystem has a solution to this called `npx`, that enables a developer to run a CLI tool without globally installing a package or creating a project first. This is useful for many scaffolding CLIs that generate new projects: e.g., `npx create-react-app my-app`.

In Elixir, we have the powerful `Mix.install/2` function that can enable installing packages in iex or scripts without installing them globally (not currently possible at all unless I'm mistaken) or creating a new project.

This still requires the user to write a script or boot up iex.

Solution:

A builtin mix task to run mix tasks directly from hex or a git repository (an SCM).

`mix x phx_new phx.new`

Advantages:

- Easier way to distribute CLI tooling
- Remove a step from projects like phoenix for installing the scaffolding CLI
- Building this into Elixir (vs having a 3rd party CLI people can install) ensures that users have it and library authors can take advantage of it reliably

Disadvantages:

- Another feature to maintain

Considerations:

- What to name the mix task? The above is just not the real proposed name, just the closed thing to the `npx` convention
- Should libraries be able to declare a "default" task, such that `mix x phx_new` works without explicitly declaring the task?

Notes:

I have a script in my dotfiles that as a proof of concept

```elixir
#!/usr/bin/env elixir

[package, task | args] = System.argv()

Mix.install([String.to_atom(package)])

Mix.Task.run(task, args)
```

Let me know what you think!

- Mitch

José Valim

unread,
Jun 4, 2024, 4:10:08 PMJun 4
to elixir-l...@googlegroups.com
I think this is potentially a good idea indeed! Especially because it would force people to always stay on the latest.

However, I don't think it needs to be part of install? For example, it could be included with archives:

mix archive.run hex phx_new ./my-app

phx_new could introduce a task called phx_new so you don't have to repeat it. Another option is to have:

mix archive.run hex phx.new ./my-app

And the name of the package comes from everything before the dot.

On the other hand, there are benefits in not depending on archives. Installed packages can have more dependencies. Maybe:

mix hex.run phx.new ./my-app

The question is: when do we check for updates? Maybe, if we make it part of Hex, we can always check the latest version before.
The command above will:

1. Download the package phx
2. Invoke the phx.new task with the remaining arguments

We will need to convince the Phoenix folks to publish phx though, probably as part of Phoenix v1.8. Definitely food for thought!



--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/d2e4ac74-8ef2-499b-88dc-3b0577fc1300n%40googlegroups.com.

Mitchell Hanberg

unread,
Jun 4, 2024, 4:39:58 PMJun 4
to elixir-lang-core
> However, I don't think it needs to be part of install? For example, it could be included with archives:

I only meant to imply that Mix.install could be used in the implementation, but not be part of Mix.install per say.

> On the other hand, there are benefits in not depending on archives. Installed packages can have more dependencies. Maybe:
>
>         mix hex.run phx.new ./my-app
>

Would including it in hex preclude it from running local or git/github tasks? i think that could be useful if you have some local code or just to avoid folks polluting hex.pm with little tasks.

> The question is: when do we check for updates? Maybe, if we make it part of Hex, we can always check the latest version before.

I think the way npx works is it caches whatever version it found at the time, but if you have a specifier like `latest`, it would always resolve it (I think).

So we could potentially include syntax like `mix x phx@latest phx.new my_app`, or merely have a `--force` flag that always checks to see if the current installation is the latest `mix x --force phx phx.new my_app`

Regarding the cli name, it could also be another binary that is installed like `mix`, `iex`, `elixir`, etc.

`hpx phx@latest phx.new my_app`

`hpx` being "hex package executor" (or whatever we want it to be)

Regarding the potential duplicative typing of something like `hpx phx_new phx.new my_app`, adding a new key to the Mix.Project to determine a default task would be useful (this is something else that `npx` does), leaving you with something like `hpx phx_new my_app`. That could leave some arg parsing ambiguity, so maybe something like `hpx phx_new:phx.new my_app` if you were to explicitly call a task.

Zach Allaun

unread,
Jun 5, 2024, 8:30:36 AMJun 5
to elixir-lang-core
I like this proposal a lot!

Personally, I'd like if I were always prompted in some way if a newer version that the cached one were available. So the behavior might be:

First run: install and run
Subsequent run: Check for latest, prompt to install latest and then run
Subsequent run with no internet connection: warn and use cached version

Perhaps a flag could be used to force only using a cached version without checking for latest, but in my opinion, if you want a specific version, you'd just install that version of the archive. 

Dave Cottlehuber

unread,
Jun 5, 2024, 2:37:10 PMJun 5
to elixir-lang-core
On Wed, 5 Jun 2024, at 14:30, Zach Allaun wrote:
> I like this proposal a lot!
>
> Personally, I'd like if I were always prompted in some way if a newer
> version that the cached one were available. So the behavior might be:
>
> First run: install and run
> Subsequent run: Check for latest, prompt to install latest and then run
> Subsequent run with no internet connection: warn and use cached version

I’d like to see this too.

Some considerations - as the maintainer of FreeBSD packages, I’d like to be able to distribute some of these scripts alongside elixir or Phoenix

This is so we give people a smooth out of the box experience, particularly when running in CI or Packaging mode without internet access.

If there is a way to namespace these checks perhaps, to have a private set of scripts that we can configure once and have each system/user fetch themselves from our private instance, that could be very handy.

Ideally disabling update checks for privacy paranoics is nice.

For naming “hex” is nice - it also can be used as a verb in English and German, to cast a spell.

A+
Dave
Reply all
Reply to author
Forward
0 new messages