6 releases
0.4.2 | Jan 25, 2024 |
---|---|
0.4.1 | Dec 29, 2023 |
0.3.4 | Aug 22, 2023 |
0.3.3 | Jul 29, 2022 |
0.3.2 | Feb 18, 2022 |
#253 in Command line utilities
115KB
2K
SLoC
Brix
Brix is a CLI tool written in Rust for scaffolding and code generation.
Attributions
Special thanks to Caleb Cushing for the original Java version, early interface design and internal architecture.
Installation
Brix is available on crates.io and the AUR for Arch Linux.
Install with cargo:
cargo install brix
Arch Linux (use an AUR helper like yay
or trizen
)
yay -S brix-git
Running
Usage:
brix [LANGUAGE] [CONFIG NAME] [PROJECT] [MODULE]
brix [OPTIONS] --config-dir | -d [CONFIG DIRECTORY]
brix [OPTIONS] --workdir | -w [WORKING DIRECTORY]
Building locally
Requirements
- Cargo and a minimum Rust version of 1.43.1
Running
- Run
cargo build
- Run
cargo run
Testing
Run cargo test --all
to test the entire workspace.
Generating Docs
Run cargo doc --no-deps --workspace --document-private-items --open
Using Brix
Language directory
To start using Brix in your project, create a .config/brix
directory in your project.
This directory will contain your configuration files. Specifying the first argument after running brix
will tell it which subdirectory to use. For example, starting Brix with brix java
will search the .config/brix/java
directory for configuration files in your project. This parameter is known as the language. If a config file isn't found, Brix will move up to the parent directory all the way up to HOME, until it finds a directory that contains a .config/brix/java
. This parameter doesn't necessarily have to be resitrcted to a programming language, it's there to group configuration files used in a similar way, perhaps for bootstraping a specific project.
Config name
Of course, running brix [language]
without pointing Brix to a specific config file is a bit useless. The second argument specifies the file name to use. Running brix java tutorial
will search for a file in .config/brix/java
named tutorial.brix.yml
or tutorial.brix.yaml
. As of right now the files must be written in YAML, but more options will be supported in the future, like JSON and TOML.
Project and module
These two arguments are specific to your config file, you can use them however you want, so let's take a look at how the config file is structured first.
Config file
Brix offers various commands for you to use to scaffold and generate your project. At the top-most level, the config file is just a list of commands, therefore we can start by just declaring the commands
property:
# example.brix.yml
commands:
- search_replace:
# ...
- exec:
# ...
The commands you list will be executed from top to bottom, one after the other. The following commands are supported:
copy
Copies a file or directory to a new location.
search_replace
Searches for a strin or regular expression in a file and replaces it with another string.
exec
Executes a list of commands.
mkdir
Creates a directory.
template
Templates a file to a new location.
Let's start with the most basic copy
command, and use Brix to simply copy a .gitignore file. Our config file would look something like this:
# .config/brix/js/gitignore.brix.yml
commands:
- copy:
source: .gitignore
destination: app/.gitignore
The source
directory is always relative to where the config file is located. In this case it would be .config/brix/js/.gitignore
. The destination
directory is relative to where you run brix
from, this is also known as the working directory, and can be overriden with the --workdir
or -w
flag.
Now, if we were to run brix js gitignore
in our project directory, we would actually get an error.
That's because Brix requires the project
and module
arguments to be specified.
In our case, we're not using them yet so we can specify anything. Now, running brix js gitignore project module
will run the command. If you didn't already have an app
directory, Brix will create one for you and copy the .gitignore
file to it.
You might notice though that this isn't that convenient for our project if we wan't to put the file somewhere other than app
. Let's say now we have a backend
folder in addition to app
. To copy the .gitignore file to backend, we would have to change the destination
in the config file each time. Instead, this is where the project and module arguments come in handy. Let's change the config file to:
# .config/brix/js/gitignore.brix.yml
commands:
- copy:
source: .gitignore
destination: {{project}}/.gitignore
Here, we are using the project
argument in the destination.
Running brix js gitignore backend module
will now copy the file to backend/.gitignore
. If we wanted to copy it to somewhere else, all we would have to do is run brix js gitignore somewhere_else module
.
We could also use the module parameter to, for example, sample a different gitignore file.
# .config/brix/js/gitignore.brix.yml
commands:
- copy:
source: {{module}}/.gitignore
destination: {{project}}/.gitignore
Running brix js gitignore dashboard default
will use .config/brix/js/default/.gitignore
and copy it to dashboard/.gitignore
.
Now, let's take a full look at all of the commands.
Copy
commands:
- copy:
source: file.txt
destination: output/file.txt
overwrite: true # Optional, will ask by default to overwrite if the file already exists
Search replace
Search replace uses fancy regex for regular expressions in the search
field and supports backreferences. The syntax is best explained here.
commands:
- search_replace:
destination: file-to-search-replace.txt
search: search string # fancy-regex supported
replace: replace string
Exec
Executes commands in order.
commands:
- exec:
commands:
- 'echo "Hello World!"'
- "prettier --write ."
- "cargo --version"
stdout: true # Optional
Mkdir
Creates a directory.
commands:
- mkdir:
destination: output/directory
Template
Templates a file.
commands:
- template:
source: file.ex.hbs
destination: output/file.ex
overwrite: true # Optional
context: # Optional
first: {{project}}Service
second: {{module}}
file.ex.hbs
could look something like:
defmodule App.{{first}.{{second}} do
# ...
end
And when templated, output/file.ex
:
defmodule App.UsersService.Store do
# ...
end
Context and Templating
Brix uses Handlebars, specifically the Rust version with both the template
command and config files in general. The context
parameter in the command isn't required, since {{project}}
and {{module}}
are automatically handled if specified in the template file.
There's also a way to specify global context in the config file:
context:
edition: 2022
name: {{project}}
commands:
- template:
source: file.txt.hbs
destination: output/file-{{edition}}.txt
- copy:
source: {{edition}}/notes.txt
destination: output/notes.txt
In this case, parameters edition
, name
, project
, and module
will all be available for use within the config file itself and all template files referenced in template
commands. Global context is also useful for declaring constants that are shared between multiple commands.
Templating helpers
Brix also provides useful helpers for manipulating these variables, specifically for altering capitalization and case. The following helpers are provided:
to-upper
to-lower
to-title
to-case
to-flat
to-java-package
to-java-package-path
All of these can be used to replace, for example, usage of {{project}}
:
context:
project: 'foo BAR 40'
{{to-upper project}} # FOO BAR 40
{{to-lower project}} # foo bar 40
{{to-title project}} # Foo Bar 40
{{to-flat project}} # foobar40
{{to-java-package project}} # foo.bar40
{{to-java-package-path project}} # foo/bar40
Reusing templates
Brix has the added benefit of not requiring a specific folder structure to be used inside the language directories. Templates and other files are completely independent from config files. This means that reusing templates is a lot easier, as everything is referenced with just a path. If you need to use the same template within two different config files, simply reference the path to the file in both and use a different context.
Full Example
Finally, let's take a look at a full example using Brix to bootstrap a Java project. The .config/brix
directory is conveniently located in HOME in order to be able to run brix
from anywhere and create a project like this.
commands:
- copy: # Copy all of the shared files to the project directory
source: project/shared
destination: .
overwrite: true
- template: # Template build.gradle.kts
source: module/build.gradle.kts.hbs
destination: "module/{{module}}/build.gradle.kts"
overwrite: false
- template: # Template module-info.java
source: module/src/main/java/module-info.java.hbs
destination: "module/{{module}}/src/main/java/module-info.java"
overwrite: false
- template: # Teplate package.json
source: project/templates/shared/package.json.hbs
destination: "package.json"
overwrite: false
- template: # Template settings.gradle.kts.hbs
source: project/templates/shared/settings.gradle.kts.hbs
destination: "settings.gradle.kts"
overwrite: false
- mkdir: # Create the main directory for the module
destination: "module/{{module}}/src/main/java/com/example/{{module}}"
- mkdir: # Create the test directory for the module
destination: "module/{{module}}/src/test/java/com/example/{{module}}"
- exec: # Run the following commands with no stdout
commands:
- git init
- git add .
- git commit -m initial
- yarn up
stdout: false
More Examples
There are a few extra examples located in ./config/brix/rust
.
- copy
cargo run -- rust copy brix foo
- exec
cargo run -- rust exec foo foo
- mkdir
cargo run -- rust mkdir brix foo
- search_replace
cargo run -- rust search_replace brix foo
- template
cargo run -- rust template brix foo
Dependencies
~12–22MB
~333K SLoC