README.adoc

February 18, 2025 ยท View on GitHub

= porter

image:assets/porter.jpeg[porter]

= babashka library for creating config files

You have lots of configuration files that needs to be generated based on input. The input is spread over multiple files and changes depending on what environment you're producing the config files for. You end up using Makefile (good) and bash/python/ruby/insert-here and question your life choices.

You can now use porter and still question your life choices. But at least it'll be in EDN and Babashka.

== Usage

  • build-output takes [env, src, opts, ctx-paths] as arguments ** env is required can be things like :local, :staging, :production, etc ** src is the source input in the form of a string or a file ** opts is a map with optional keys :dest, :print, :namespaces, :classpaths, :exec and :injections ** ctx-paths is the context to be read from files to be put into the src

.Example usage [source, clojure]

(build-output :test "{{:test-data}}" {} ["build-resources/config.test.edn"])

=== opts

  • :dest - if we wish to send the output to a destination file
  • :print - if we wish to have the output printed to stdout
  • :print-ctx - if we wish to have the context map printed to stdout
  • :print-injections - if we wish to have the injection map printed to stdout
  • :namespaces - for evaling in ctx-paths
  • :classpaths - classpaths to add to the library. Used for the SCI interpreter
  • :injections - a map which will be merged with the context map
  • :exec - do we wish to execute the src as a command in a shell

=== ctx-paths

ctx-paths are deep-merged into each other, starting from the first entry.

== context (ctx)

When the context is created, a recursive lookup (up to 30 degrees) is done, allowing for building up a complete hashmap with all data required for building the output.

It's possible to manually skip processing by adding ^:skip as a metadata in front of any element inside the EDN ctx map. Main use is when you wish to protect arguments sent in to ctx functions from being processed (as lookup vectors).

.Lookup via vectors in ctx [source, clojure]

{:client/name "client-vip-name" :image.name/platform "platform" :version "1.0" :docker {:production {:platform {:image-namespace [:client/name] :image-name [:image.name/platform] :tag [:version]}}}}

The above would produce the following output being run through build-output

.Output from lookup via vectors [source, clojure]

{:client/name "client-vip-name" :image.name/platform "platform" :version "1.0" :docker {:production {:platform {:image-namespace "client-vip-name" :image-name "platform" :tag "1.0"}}}}

=== ctx functions

It's also possible to use functions in the context files to build the output. Porter supports the user namespace by default. Anything else has to be provided.

The user namespace has a ctx var injected into the SCI context and holds the context map created from ctx-paths.

The ctx var has the env var injected under the :env key and can be refered to as a key in the map.

.Example usage [source, clojure]

;; source code for get-namespace+tag in 'example-ns

(defn get-namespace+tag [ns n tag] (format "%s.%s:%s" ns n tag))

;; config.test.edn

{:client/name "client-vip-name" :image.name/platform "platform" :version "1.0" :docker {:production {:platform {:image-namespace [:client/name] :image-name [:image.name/platform] :tag [:version]}}} :docker.image/platform+tag (example-ns/get-namespace+tag [:docker [:env] :platform :image-namespace] [:docker [:env] :platform :image-name] [:docker [:env] :platform :tag])}

(build-output :test "config.test.edn" {:print true :classpaths ["classpath-example"] :namespaces ['example-ns]} ["build-resources/config.test.edn"]) => {:client/name "client-vip-name" :image.name/platform "platform" :version "1.0" :docker {:production {:platform {:image-namespace "client-vip-name" :image-name "platform" :tag "1.0"}}} :docker.image/platform+tag "client-vip-name.platform:1.0"}

== src paths

When referring paths from ctx in src there are two options available.

  • {{:my.path/here}} - this is equalivent to a Clojure keyword
  • {{[:my :path :here]}} - this is equalivent to a Clojure vector

Both options are paths to the context where data can be found.

= porter CLI tool

There is a link:porter[porter CLI tool] which automatically downloads and uses this library via the command line.

[source, shell]

curl -o /usr/local/bin/porter https://raw.githubusercontent.com/emil0r/porter/refs/heads/master/porter chmod +x /usr/local/bin/porter

.Example usage from local testing [source, shell]

./porter --env :local
--src "dev-resources/test.tester.yml"
--ctx-paths "dev-resources/test.tester.edn"
--print true
--dest testus.yml
--injections '{:version "1.0.0"}'
--classpaths "clj"
--namespaces tester

Gives the following output.

  • Adding classpath 'clj' is from the classpath specified
  • Requiring tester is a namespace used
  • This is from tester 1.0.0 is from a function called from the tester namespace
  • Invalid input gives output of either empty paths or broken paths for the src file ** Empty paths and Broken paths will be colored red with a white background

[source,shell]

Adding classpath 'clj' Requiring tester This is from tester 1.0.0 Invalid input Empty paths #{[:infra :local :postgres :user] [:infra :local :postgres :password]}, :broken-paths #{}

.Options [source, shell]

porter --help

-- Options --

--namespaces Namespaces to add --print-injections Print injections to stdout --injections Map of injections to the context map --exec Execute the src as a shell command --src Source of input. String or file. Can be multiple lines which will be joined together with a newline in between each entry. This is meant mainly for exec (blame Make) --env Which environment are we using --classpaths Classpaths to add --print Print the output to stdout --print-ctx Print ctx map to stdout --ctx-paths vector of strings to context edn files --dest Which file to write the output to

== Example

There is a basic example of how to use porter in the link:./example[example directory].

.Create the config file [source, bash]

cd example

make ENV=:production build-config-files-service

make ENV=:production build-docker-anchorpoint