View Source SayCheezEx (say_cheez_ex v0.3.6)

This module is used to retrieve assorted pieces of configuration from a release's build environment.

  • Which build is this?
  • Who built this release?
  • When was this built?
  • What was the Git SHA for this build?

Usage

Make sure that you capture all elements you need to a module attribute - e.g.

  module Foo do
    import SayCheezEx, only: [cheez!: 1]
    # e.g. "v 0.1.5/d9a87c3 137 on server.local"
    @version cheez!("v {:project_version}/{:git_commit_id} {:build_number} on {:build_on}")
    ...
  end

Data gathering must be done at compile time and will simply create a string once and for all that matches your informational need.

This can be done in multiple ways:

  • You can call the cheez!/1 or cheez/1 functions with a format string, as described in their docs
  • You can call info/1 and get_env/1 to extract the specific parameters you need.

See info/1 for list of allowed attributes, or all/0 for a map with all pre-defined attributes.

Graphs

What is better than Elixir's own documentation? adding graphs to it, and having them embedded in your documentation.

To try them, use:

  module Foo do
    import SayCheezEx, only: [uml: 1, graphviz: 1]

    @moduledoc """
    Here goes a Graphviz graph:

    #{graphviz("digraph { Sup -> GenServ }")}

    Here a PlantUML graph:

    #{uml("""
      Bob -> Alice : I do love UML in documentation
      Alice -> Bob : I love it too!
    """)}

    """

    ...
  end

See for yourself some examples at:

Graphs are cached under _build/img_cache so a full render happens only when they change.

Link to this section Summary

Functions

Dumps a map of all known build/env configuration keys for this environment.

Prints a map of only some elements.

Captures the environment from a definition string.

Captures the environment from a definition string, and prints it out so it is shown in the compile logs.

Creates a date out of an ISO date.

Expands a sequence of tokens into a string.

Given a list of candidates, returs the first that is not unknown.

As the C compiler is formatted differently on Win and Unix, we handle both cases with a related unit test, so we can add (and test) more.

Reads the first env variable that is not empty.

Reads an ISO date from Git.

Runs current Git command.

Runs a local Graphviz command and embeds the resulting SVG.

Reads a hostname.

Gets assorted pieces of system information.

We want to rewrite ELixir module names so we can remove the Elixir prefix.

Runs a command.

When should we print on STDOUT?

Checks whether we should output the results of SayCheezEx on STDOUT.

Tokenizes a string into a list.

Breaks up a list of keywords into tuples.

Runs a command though PlantUml, either local or on-line.

Link to this section Functions

@spec all() :: map()

Dumps a map of all known build/env configuration keys for this environment.

If you want a map of only some elements, see all/1.

An example output might be:

%{
  build_at: "230411.1528",
  build_at_day: "2023-04-11",
  build_at_full: "2023-04-11.15:28:47",
  build_by: "lenz",
  build_number: "87",
  build_on: "Lenzs-MacBook-Pro.local",
  build_mix_env: "dev",
  git_all: "b204919/230411.1509",
  git_commit_id: "b204919",
  git_commit_id_full: "b2049190312ef810875476398978c2b0387251d3",
  git_date: "2023-04-11.15:09:50",
  git_date_compact: "230411.1509",
  git_last_committer: "Lenz",
  project_full_version: "0.2.1/b204919/230411.1509",
  project_name: "SayCheezEx",
  project_version: "0.2.2",
  sysinfo_arch: "aarch64-apple-darwin22.3.0",
  sysinfo_beam: "BEAM jit 13.2",
  sysinfo_c_compiler: "gnuc 4.2.1",
  sysinfo_compat: "25",
  sysinfo_driver: "3.3",
  sysinfo_nif: "2.16",
  sysinfo_ptr: "64bit",
  sysinfo_word: "64bit",
  system: "1.14.3/OTP25",
  system_elixir: "1.14.3",
  system_otp: "25",
  ...
}

but the right place to check all properties and their meaning is info/1.

@spec all(maybe_improper_list()) :: map()

Prints a map of only some elements.

If you want a map of all elements, see all/0.

Captures the environment from a definition string.

Same as cheez!/1 but it does not print the captured string.

Captures the environment from a definition string, and prints it out so it is shown in the compile logs.

Usage:

  > cheez!("v {:project_version}/{:git_commit_id} {:build_number} on {:build_on}")
  "v 0.1.5/d9a87c3 137 on server.local"

definition-strings

Definition strings

This function will interpolate attributes set between brackets, with the following rules:

  • {:project_version} is an info tag. These is a long list of those - see all/0.
  • {$HOST} is an environment variable - in this case, HOST
  • {=HELLO} is a default value, in this case the literal string "HELLO"

If multiple attributes are specified in a comma-separated string, they all are expanded, and the first one that is defined will be output. So e.g. {$FOO,$BAR,=BAZ} will first try to interpolate the variable FOO; if that is undefined, it will try BAR, and if that too is undefined, it will output "BAZ" (that is always defined)

As it is quite a common thing to include a module name in the string as it appears in the MODULE attribute, and that will print out the module with an "Elixir" prefix (e.g. module "Foo.Bar" will become "Elixir.Foo.Bar"), the "Elixir" prefix is stripped when found.

controlling-output

Controlling output

In general, on every capture, we would love to print out the current captured variables on STDOUT. This is quite handy for server builds, as in the build log there are the same pieces of information that the build embeds.

This also gets in the way during development and testing, cluttering the output.

So the default is that is printing only happens when building in the :prod environment, unless you force it on or off using the CHEEZ environment variable. So, say....

  CHEEZ=1 mix test

Will print captured output (see should_print?/0 for further details). Of course this only happens when compiling classes, so when working interactively you may sometimes see the output and sometimes won't, as the compiler decides what is in need of being rebuilt.

If you don't want this fuction to print out the captured environment, just use cheez/1.

Link to this function

date_from_iso_date(iso_date, fmt_list)

View Source

Creates a date out of an ISO date.

We need to do this instead of giving git format options, as date formatting is not supported well by ancient git versions (e.g. Centos7 still has git 1.8).

So we basically break a date of the format 2023-02-15 08:50:19 +0100 into a set of tokens, and reassemble them based on a list of input tokens or constant strings.

Expands a sequence of tokens into a string.

Link to this function

first_non_empty(vars, def_val \\ "?")

View Source

Given a list of candidates, returs the first that is not unknown.

If all of them are unknown, return the default.

Link to this function

format_sysinfo_c_compiler(arg)

View Source

As the C compiler is formatted differently on Win and Unix, we handle both cases with a related unit test, so we can add (and test) more.

@spec get_env(binary() | maybe_improper_list()) :: binary()

Reads the first env variable that is not empty.

Reads an ISO date from Git.

See date_from_iso_date/2

@spec git_run([binary()]) :: binary()

Runs current Git command.

CWD is the root of the repo.

Runs a local Graphviz command and embeds the resulting SVG.

Sup SupGenServ GenServSup->GenServ OtherGenServer OtherGenServerSup->OtherGenServer

notes

Notes

Reads a hostname.

@spec info(atom()) :: binary()

Gets assorted pieces of system information.

project

Project

  • project_name: "SayCheezEx" - the project will be named with an atom as :say_cheez_ex, but we return a camelized string
  • project_version: "0.1.0-dev",
  • project_full_version: "0.1.0-dev/7ea2260/230212.1425",

elixir-erlang

Elixir - Erlang

  • system: "1.13.4/OTP25",
  • system_elixir: "1.13.4",
  • system_otp: "25"

git

Git

  • git_all: "7ea2260/230212.1425" - a recap of commit id and date compact
  • git_commit_id: "7ea2260" - the short commit-id
  • git_commit_id_full: "7ea2260895f35fc46976a2fdbc4d8faeaad09467" - the full commit-id
  • git_date: "2023-02-12.14:25:47" - the date if last commit
  • git_date_compact: "230212.1425" - the compact date of last commit
  • git_last_committer: "Lenz" - the author of last commit

build-information

Build information

  • build_at: "230213.1545" - a short date of when the release was built
  • build_at_day: "2023-02-13" - the day a release was built
  • build_at_full: "2023-02-13.15:45:08" - the exact time a release was built
  • build_by: "jenkins" - the user that was running on the build server
  • build_on: "intserver03" - the server the release was built on. First checks hostname then the environment variable HOST
  • build_mix_env: the mix build environament, as a string (e.g. "dev" or "prod")

jenkins-specific

Jenkins-specific

  • build_number: "86" - the value of the BUILD_NUMBER attribute

system-environment

System environment

  • sysinfo_arch: the system architecture (e.g. "aarch64-apple-darwin22.3.0")
  • sysinfo_beam: the type of VM, whether it's a JIT or interpreter, and its version (e.g. "BEAM jit 13.2")
  • sysinfo_banner: the "welcome banner" that the VM prints on startup (e.g. "Erlang/OTP 25 [erts-13.2] [source] [64-bit] [smp:10:10] [ds:10:10:10] [async-threads:1] [jit]")
  • sysinfo_c_compiler: the system C compiler used to build the VM (e.g. "gnuc 4.2.1")
  • sysinfo_compat: the Erlang/OTP release that the current emulator has been set to be backward compatible with (e.g. "25")
  • sysinfo_driver: Erlang driver version used by the runtime system (e.g. "3.3")
  • sysinfo_nif: version of the Erlang NIF interface (e.g. "2.16")
  • sysinfo_ptr: the size of Erlang term words in bits (e.g. "64bit")
  • sysinfo_word: the size of an emulator pointer in bits (e.g. "64bit")
Link to this function

replace_elixir_modules(s)

View Source

We want to rewrite ELixir module names so we can remove the Elixir prefix.

From

"{:abc,=NONE} Elixir.My.Module {:abc,=NONE}"

To

"{:abc,=NONE} My.Module {:abc,=NONE}"
Link to this function

run_cmd(cmd, parameters)

View Source

Runs a command.

Returns the output, only if return code is zero.

Otherwise returns "@unknown_entry"

When should we print on STDOUT?

  • in environment :prod only (so for final build)
  • unless environment variable CHEEZ is set to 1, that forces printing in all environments
  • unless environment variable CHEEZ is set to 0, that silences printing in all environments
Link to this function

should_print?(env_cheez, build_mix_env)

View Source

Checks whether we should output the results of SayCheezEx on STDOUT.

In a separate function for ease of testing.

Tokenizes a string into a list.

Breaks up a list of keywords into tuples.

Runs a command though PlantUml, either local or on-line.

This is pretty handy to document, for example, a set of GenServers and how they depend on each other:

QMLiveLocal DBOperator 1Operator NQMLTable of ServicesInstance stateOperator 1UpdaterDynamicSupervisorPuller XPuller YPusher APusher BCmds AService NApplicationPoolActivatorLoggerWeb acceptor

Or how multiple GenServers send messages to each other and which responses/replies are used:

Shared feedback actionsLogFwdPBXPBXUniloaderUniloaderPullerPullerPusher 1Pusher 1Commands 1Commands 1Pusher 2Pusher 2Commands 2Commands 2QMLive 1QMLive 1QMLive 2QMLive 2Log upload...Actions pendingCheck actionsAny news?new command,id=123new cmd,id=C1/P1/123Command storedTimer wake-upAny news?"new command, id=42""new cmd, id=C2/P1/42"2 cmds stored

And there is way more - only limited by your imagination. For example, this is a mind-map (stolen from https://www.drawio.com/blog/plantuml-mindmaps-from-text) It ain't' half bad, right?

Currently under reviewOnboarding and offboarding tasksOnboardingPrior to first dayContract signedEmployee handbookIT equipment reservedFirst dayOffice tourTeam introsAccount setupFirst weekShadow team membersSoftware trainingFirst monthAssign projects/tasksSet goalsGet team feedbackOffboardingFeedback and reviewExit interviewTasks/projects reassignedHandoverAccount deactivation/deletionIT hardware returnprioritymeetingsTasksLast updated: May

And as all of this is text-based, it plays well with your source files, version control and whatnot.

notes

Notes

  • You can find full documentation of PlantUML and the gazillion graphs it produces at https://plantuml.com/
  • At the moment, all images are processed through the on-line server
  • Nothing stops you from using the module SayCheezEx.Graphs.Plantuml to generate SVG images at runtime, not only in documentation.
  • See also graphviz/1.