jlg23 3 days ago

Did this a few times, devs loved it: all in posix-shell, the main script was a rather ugly thing but devs would never touch it. You'd code your command as a simple shell script in cli-lib/$username/$command, it would be available to you as 'cli $command', to others as 'cli $username.$command'.

Useful commands would be copied over into the directory that contains the commands accessible to all. Usefulness was determined at the coffee machine, the smokers' room or in conversations after presentations on their work given by devs.

Also, interested interns would be tasked to spot "optimization potential" - so they read through these commands, learned to read and write shell scripts and they had to learn to efficiently address senior devs.

Everytime this was more a social thing than actual workflow optimization: Tooling became a permant side project, people could (and would) look their peers' scripts, one could take a short break from the main project by working on this. One observation I made: the more senior the devs, the less interested they were in this "playground" - after all good devs are always faster at hacking solutions to these simple problems than evaluating and adapting existing ones.

jasonpeacock 3 days ago

I'm not worried about matching the build script language to the project language.

I am worried when I have to install a full dependency graph to run your build scripts...that's where things become brittle and annoying.

Python with Typer[0] and Plumbum[1] is great for quickly cranking out ergonomic build scripts, or Invoke[3] which is intended for exactly this purpose.

[0] https://typer.tiangolo.com/ [1] https://plumbum.readthedocs.io/en/latest/ [3] https://www.pyinvoke.org/

But then I've gone from "any Python 3.x can be used as-is" to "setup a Python venv and install these dependencies" as soon as I add any dependency :(

So you either have to constrain yourself to Argparse and Subprocess in the Python stdlib, or accept that your build scripts are a project unto themselves.

This applies similarly to Ruby, TS/JS/Node, Perl, etc.

  • athrun 2 days ago

    I feel this is where uv's PEP 723 support of inline dependencies in a single-file Python script shines.

    Just use something like this as a shebang, and you can have your cake and eat it too!

    #!/usr/bin/env -S uv run

    previous HN thread: https://news.ycombinator.com/item?id=43097006

    • rednafi 2 days ago

      It’s much better with uv, but nothing beats the ergonomics of running go mod tidy && go run script.go.

      I wish Python had a native way to build self-contained executables like Go. The binary would be larger since it’s a dynamically interpreted language, but as long as it’s self-contained, I wouldn’t mind.

      • lgas 2 days ago

        I mean... at first blush it seems like `./scriptname` which is what the `uv` shebang provides beats having to remember two commands and && them together.

zanderhill 2 days ago

I've successfully used a pattern like this for groups of 10 to 20 devs where you designate a folder containing scripts and subfolders as a clu for team.

I started off using Basecamp sub, then a rust remake called tome and finally simplified it as a golang project https://github.com/zph/tome-cli.

The elegance of this solution is having a helper binary that wraps a folder of sub folders and scripts as a cohesive sdk.

Copied from readme, the features are: - Organize a folder of scripts into a CLI sdk - Any language is supported via normal script #! - Usage text extracted from script header if USAGE: is included in leading comments - Full help text extracted as lines from USAGE: to first empty line - Builtin alias generator allows for embedding configuration flags via tome-cli alias Auto completion of: subcommands (exec, help, etc) root folder's folder names root folder's scripts root script's flags and arguments (when they satisfy the --complete and TOME_COMPLETION interface) Gitignore like syntax for ignoring scripts by using a .tomeignore file at base of root folder example

The trick to allowing any script language is to only allow self contained dependency languages like deno, python with uv inline deps or golang with bash wrapper. And then you need to vendor those runtime references in repo as well using hermit or dotslash so that the toolkit is self contained.

It saves a good bit of time to have a central cli andale it easy to extend

fortruce 3 days ago

This is a gripe I have with my current work project. All project scripts are written as bash functions and the convention is to just source a huge bash script to have them all available.

As the project has grown these bash functions have become more and more esoteric. Discoverability is not as good as could be expected from a cli tool with subcommands and simple help strings.

Additionally, I've found this setup has caused issues with various tooling that an executable cli tool would not have. For example, zellij layout commands do not run through a shell so you cannot specify a bash function/alias as a pane command.

  • jasonpeacock 3 days ago

    Sourcing bash functions is the worst experience, I loathe it.

    Many people think of Bash as "special", but it's just another interpreted language like Ruby or Python or Perl. It's that many people also use a Bash repl for their interactive shell that they get confused.

Sirikon 3 days ago

I prefer to have a “scripts” directory with each script being a separate executable, maybe a comment on top of each script. Discoverability is just looking at the file system with any tool you’re used to.

Both Bash and Python are a given anywhere. I use Bash for simple stuff and Python for the complex stuff. Thanks to each script being a separate file, I can mix Bash and Python, but using only Python is very valid.

rednafi 2 days ago

It’s easier to do in some languages than in others. I write all my scripts in Python for Python projects and in Go for Go projects. Then, the scripts are called from Make targets.

But doing it in Kotlin is pushing it too far in my book. Kotlin compilation is slow, and running the scripts is nowhere near as ergonomic as running python -m script or go run script.go.

I prefer using a Makefile over creating a CLI. Adding and removing Make targets is trivial and doesn’t require me to fiddle with CLI entry point code. But Make syntax is fugly, so I keep the targets lean, and they mostly just invoke the scripts.

tyleo 2 days ago

I did something similar to this at my company. I made a little executable that runs on the command line you can easily plug new functionality into using C#. I did it out of frustration at not having good command line tools. Tons of folks use it now and I’d do it again anywhere I go that doesn’t have something similar.

hoten 3 days ago

+1 avoid bash scripting yourself into a unmaintainable mess

Don't think you necessarily need to use the same language as the rest of the project. Python scripts can do the job just fine, given you can wrangle the appropriate program entry points via the CLI interface.

eternityforest 2 days ago

I use a makefile in all my projects that need scripts, you get autocomplete for free that way.