Giter Site home page Giter Site logo

chevdor / tera-cli Goto Github PK

View Code? Open in Web Editor NEW
69.0 4.0 7.0 448 KB

A command line utility on top of the tera templating engine. Takes json|yaml|toml as input and can merge ENV in. You may see it as envsubst on steroid.

License: MIT License

Rust 76.40% Ruby 3.42% Dockerfile 4.90% Just 6.16% Fluent 0.21% Nix 8.92%
template tera rust chevdor 2021 ci automation template-engine stdin engine cli

tera-cli's Introduction

tera-cli

Intro

tera cli logo 256

tera is a template engine written in Rust and inspired by Jinja2. It allows merging some data called context data into a template and produces a new output. This project, tera-cli, is a command line for the tera template engine.

This project is called tera-cli but the command installed on your system is simply tera.

tera-ci offers powerful features related to your environment variables, allowing you to control the output both from the context data you pass but also from the ENV variables set on your system.

Example

Here is a basic example. For instance, you will pass data such as:

.data.json:
{
    "title": "Demo",
    "users": [
        {
            "username": "Alice",
            "url": "http://example.org/alice",
            "fav_colors": ["red", "green", "yellow"]
        },
        {
            "username": "Bob",
            "url": "http://example.org/bob",
            "fav_colors": ["orange"]
        }
    ]
}

as well as a template such as:

.template.tmpl
<title>{% block title %} {{title}} {% endblock title %}</title>

<ul>
{% for user in users -%}
    <li><a href="{{ user.url }}">{{ user.username }}
    {{ user.username }} likes {% for color in user.fav_colors -%}{{ color }} {% endfor %}
    </a></li>
{% endfor %}
</ul>

and a call such as tera --template template.tera data.json will produce:

.result
<title> Demo </title>

<ul>
<li><a href="http://example.org/alice">Alice
    Alice likes red green yellow
    </a></li>
<li><a href="http://example.org/bob">Bob
    Bob likes orange
    </a></li>

</ul>

The tera engine allows way more than the simple replacements shown above. You may check out the doc for more information. To name only a few, tera offers the following:

  • variables & expressions (you can do math…​)

  • comments

  • control structure & loops (if, for, …​)

  • filters

  • formatting functions (show a file size, format a date, etc…​)

  • inheritance, include, etc…​

  • built-ins: capitalize strings, replace, trim, etc…​

Install

cargo install --git https://github.com/chevdor/tera-cli

Hot reload

You may find it useful to watch a folder with your templates and run tera if a template changes. For this to work, it is recommended to name you template as foobar.md.tera if your template expands into a markdown file for instance. You may then use fswatch and watch a templates folder using:

fswatch templates -e ".*\.md$" | \
    xargs -n1 -I{} \
    tera --include-path templates \
        --template templates/template.md.tera context.json

Execute as Docker container

You can find a tera Docker image at chevdor/tera. The image is very small and should be less than 8MB.

You can test it with:

docker run --rm -it chevdor/tera --version

The Docker image mentioned above is not yet built by the CI so you may not find the very latest version from time to time.

Build container image

docker build --tag tera-cli .

Execute tera from the Docker container

Check the tera help

docker run -it --rm tera-cli --help

Parse a template

docker run -it --rm \
    --volume="$(pwd)/templates:/templates" \
    --read-only \
    --env=FOO=BAR \
    tera-cli --template /templates/env-debug.txt --env-only --env-key env

What can I do with that anyway ?

Well…​ if you have data and you want to format them, this tool will likely be a great companion.

  • You may generate beautiful changelogs in markdown, asciidoc, restructured text, etc…​

  • You may generate some more human views of your data

  • You may…​ make a blog with that…​

  • You may generate k8s config files…​.

Features

Supported formats

You may pass the context data either as file of into stdin.

Current stdin supports only json.

ENV support

There are several options related to the environment variables.

Enable ENV variables injection

By default, the environments variables are not merged in. You can turn this feature on with --env.

Collisions

Now that you enabled the merging of the ENV variables, it is important to understand that, in some cases, your ENV may collide with your context data. This can be convenient if you want your ENV to override the context data.

ENV injection priority

If you prefer the context data to overwrite the ENV, you may use --env-first. As a result, the ENV will be applied first to the context and your context data will be loaded afterward.

Collisions handling

You may perfer to consider collisions as failures. This is what --fail-on-collision is for. If a collision is detected, the program will exit with a status code of 1 and an appropriate message.

ENV only

You may also want to ONLY load ENV variables as context data. This is what --env-only does.

ENV sub key

By default, your ENV variables will be loaded at the root of the context data. For instance, the HOME ENV variable will be then available in your tera template as {{ HOME }}. As we just mentioned, collisions may be an issue. There is an easy to prevent them entirely: you may move the ENV into a sub key in the context data. This is allowed thanks to the --env-key <name> option. For instance, using --env-key env will make your HOME ENV variable available in the tera template as {{ env.HOME }}.

While the syntax is a little more verbose, paired with --fail-on-collision, this option allows ensuring that nothing happens in your back.

External files

Using the --include flag, the command will scan recursively for files that could be included, used as macros or for inheritance. By default, it will scan the folder where the main template is located, unless the --include-path option is given.

From this repository, you can test the include feature with the command:

USER="[YOURNAME]" tera --template data/include/hello.txt --include --env-only

and test the inheritance feature with:

USER="[YOURNAME]" tera --template data/inheritance/child.txt --inherit --env-only

Content escaping

Passing the -a | --escape flag allows escaping the content.

Usage

Command line utility for the tera templating engine. You need to provide a template using the tera syntax as well as some data (various format are supported)

Usage: tera [OPTIONS] --template <TEMPLATE> [CONTEXT]

Arguments:
  [CONTEXT]  Location of the context data. This file can be of the following type: json | toml | yaml. If you prefer to pass the data as stdin, use `--stdin`

Options:
  -t, --template <TEMPLATE>          Location of the template
  -i, --include                      This flag tells the command to parse all templates found in the same path where the given template is located [aliases: inherit]
      --include-path <INCLUDE_PATH>  Option to define a different path from which search and parse templates [aliases: inherit-path]
  -s, --stdin                        The context data can be passed using stdin
  -e, --env                          If true, the current ENV will be appended to the data under the --env-key key
      --env-key <ENV_KEY>            By default, if --env is set, the environment variables will be attached at the root of the context. This is convenient but may end up conflicting with your data. To prevent collisions, you can provide a custom key with this option
      --env-first                    By default, the context is made of the data you pass and the ENV is applied afterwards. Setting this option will apply the ENV first. This is interesting if you prefer your data to override the ENV
      --fail-on-collision            if you prefer your data to override the ENV
      --env-only                     If you want to solely use the ENV as context, you may pass this option. This will prevent an error about no context being passed to be raised
  -o, --out <OUT>                    Optional output file. If not passed, using stdout
  -a, --escape                       Auto-escape rendered content. This is useful for HTML output
  -h, --help                         Print help
  -V, --version                      Print version

tera-cli's People

Contributors

365tuwe avatar alerque avatar chevdor avatar exil0867 avatar koltesdigital avatar tandiljuan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

tera-cli's Issues

YAML/JSON support

It seems tera cannot handle YAML like

audit-events:
  sso.auth.success:
    description: Authentification success

Result

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error { kind: Msg("Failed to render '__tera_one_off'"), source: Some(Error { kind: Msg("Forloop containers have to be an ident or a function call (tried to iterate on 'Math(MathExpr { lhs: Expr { val: Ident(\"audit\"), negated: false, filters: [] }, rhs: Expr { val: Ident(\"events\"), negated: false, filters: [] }, operator: Sub })')"), source: None }) }', src/main.rs:64:66

Remove - from audit-events and you will get

Thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error { kind: Msg("Failed to render '__tera_one_off'"), source: Some(Error { kind: Msg("Tried to iterate using key value on variable `auditevents`, but it is missing a key"), source: None }) }', src/main.rs:64:66

Can one iterate over YAML map?

Extension not supported: Some("yml")

Hi,

I am getting the following error when I pass the yml context data as a file and not stdin

tera -t rules\namingConvention.tera convention.yml
thread 'main' panicked at 'Extension not supported: Some("yml")', src\wrapped_context.rs:167:21
note: run with RUST_BACKTRACE=1 environment variable to display a backtrace

Any ideas?

Thanks

Provide more prebuilt binaries

For CI systems where this tool might be used, I find it sometimes quite convenient to be able to just download a prebuilt binary instead of building it myself. I'd suggest building for all kinds of architectures and operating systems. For example, see the binary matrix and GitHub Action for it.

line breaks are not respected

this is the current template MD and rendered output:
image

This is how it should render it:
image

The wrongly formatted MD will be ignored by the mdbook and thus never rendered into the HTML nor served.

I think the issue is with the CLI, but i am not 100% sure. Better to have it reported here than there since the polkadot_network_discovery is only using this cli.

I've also looked into the code and couldn't find an obvious fix. I did find out that the tera has the linebreakbr utility but i couldn't make it work.

.DS_Store getting in the way

If you are using --include-path and this path happen to contain some invalid templates such as a .DS_Store file, you get served the following error:

* Failed to read template '"/.../scripts/changelog/.DS_Store"'

Consider enabling Fluent language functions by default

From #20:

I would kindly request you reconsider whether [the Fluent feature] should be enabled by default. My opinion and reasoning go like this: the people who care about the compile time and binary size (which are admittedly both heftier with the expanded dependency chain) are the ones most likely to have the tooling at hand to run cargo build --no-default-features themselves and get exactly what they want. On the other hand the people who [get their apps] by some other means such as prebuilt binaries, or more notably via distro package managers, are the least likely to be able to easily spin up their own tooling to get the full feature set added in [and the least likely to care how long it took to build]. I'm the one packaging for Arch Linux (and will likely promote this to the [extra] repository soon) and will be adding the feature flag by default, but what about people that install via Homebrew or apt or whatever? Those people will not care about the build time at all since it doesn't affect them and are unlikely to care much about the binary size either. On the other hand they might care that their app doesn't have the full feature set.

I think having features off by default makes a lot more sense for Rust libraries where the downstream compile time might be a significant consideration and the users (i.e. projects that spec dependencies) can much more easily add the features they need. I don't think it makes much sense to have them off by default for CLI tooling that people are likely to get in binary form from channels that don't provide options.

Issue templates seem wrong

The issue templates link to something weird entirely. Maybe just delete the issue templates. This is a fairly small project and it should be rather manageable even without templates. :)

Issue with --env-only

Using --env and --env-only fails:

tera --env --env-only --template templates/release.md
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/wrapped_context.rs:122:59
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Current workaround:

echo {} | tera --env --template templates/release.md --stdin

Include autotools setup to ease downstream packaging/installation

First of all, seeing the justfile here as a job runner for developer tooling made me happy!

I realize this is a somewhat unconventional ask in the Rust ecosystem, but I would like to propose including GNU AutoTools tooling in this project. If this project would be willing to accept it, I have all the necessary bits worked out already for other projects and could pretty easily contribute it here.

Pros:

  • No existing usage such as using cargo install directly to get a binary would change.
  • Packaging for downstream distros would be easier and more normalized. Building Rust binaries is pretty easy by themselves, but when a project comes with other support files it gets a lot harder and distros end up adhoc-ing their way through. Many distros end up without all the possible files.
  • Clap can easily be updated to provide shell completions and a man page matching the CLI usage. Getting these generated and installed these to the right locations is not something plain Cargo handles well, but almost every distro out there has built in support for autotools builds that do this right out of the box.
  • Notable for this project because of the conflicting project of the same name autotools has support for building and installing binaries under alternate names, either with a prefix, suffix, or name transformation. This tooling is standardized and distro packagers know how to use it, so for example if they have a tera binary from the other project they could easily configure this one to install as teracli (./configure --program-suffix=cli). This would be corrected for in not only the binary name but the completion files and man page as well.

Cons:

  • AutoTools is a bit arcane and can be obtuse, especially if you don't have a gray beard and haven't been hacking on Unix for decades. This would be mitigated by the fact that I have done this before and can contribute the whole thing and, once setup, it doesn't require much.
  • The release process will be a little different that your other projects. How to handle versions can be adapted to whatever workflow is desired, but once a release is cut the best end user experience will be with a released source tarball (made with make dist) rather than just a Git generated archive. This will need to be generated and attached to releases for the best experience (of course that can be automated too).

As an example you can check out my git-warp-time project with is tooled up this way. You can build the binary or install with cargo build and cargo install like any Rust project, but if you download a source release or clone the repo and use ./configure && make && make install you get all the goodies like shell completions and the man page taken care of.

Fix readme

Currently, we need to support 2 README files, moreover, github picks the asciidoc file by default, the one it cannot fully handle properly 🤦

This issue is blocked by github/markup#1095

Allow random extensions

We may want to pass a custom extension such as .foobar.
In that case, we could pass an extra flag to provide the format (such as --yaml) or be smart and guess.

Add Fluent language functions

I have a need for a CLI tool tool that not only implements data templating but also gives access to localizations through Fluent. There are several tools for the Handlebars ecosystem (at least JS and Rust ones that I'm aware of) that make this possible but I would prefer to use Tera if possible. There doesn't seem to be anything out there yet, but the fluent_templates crate already has support for use as a Tera function, so it shouldn't be hard to bring the existing library functionality to a CLI.

Is this project interested in adding this functionality (either as a default feature or behind a feature flag), or should I consider forking or creating an all new project for this?

c.f. guangie88/tera-cli#31

Allow multiple context(s)

Awesome library!

I have my data spread over multiple files. But I can specify only one value for context. This is blocking me from using this cli.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.