Quick guide to COMA the COnfiguration MAnagement tool

Readers familiar with autoconf will find it easier to get a grasp of
coma, since they are quite similar in some respects.


Terminology
-----------

- ITEM is a bundle of stuff (code, doc, ...) that is the basic unit of
configuration management (version handling, dependency tracking,
...). It is called configuration item in CM terminology.

- SLOT is a parameter of an item. In OO terms, it is an attribute of
the object that represents the configuration of an item.

- KIT is a kind of item that defines a set of items that make it
up. It is called configuration in CM terminology.


Introduction
------------

Coma provides a uniform configuration interface to items.  Through
coma, parameters can be defined, queried, set and validated by the
item itself or even by other items thanks to the simple object
persistency employed.

One can also define kits that contain other items and set their
parameters.

Example
-------

An item has slots (i.e. attributes, parameters) that can be set
concrete values. The item can then use these slot values to control
arbitrary aspects of its behaviour such as building and running. A
very simple item definition may look like this:

  (defitem simple-item
      (log-level
       c-compiler)
    (:config-files "Makefile"))

This says that SIMPLE-ITEM has two slots, LOG-LEVEL and C-COMPILER and
the Makefile shall be created by coma. One way to set a slot is
through the command line. Let's set to LOG-LEVEL to 3 and tell the
item to use gcc:

  coma.sh set-slot log-level 3 c-compiler \"gcc\"

To create Makefile coma needs the template file Makefile.coma:

  all:
          @c-compiler@ -DLOG_LEVEL=@log-level@ my-prog.c

Now, if the item is told to activate its configuration:

  coma.sh configure

then the template file is processed the values of the appropriate
slots are substitited for @c-compiler@ and @log-level@:

  all:
          gcc -DLOG_LEVEL=3 my-prog.c




  (defitem example-item
      (i-am-a-boolean
       run-speed
       log-level
       (debug nil)
       (do-not-save-me "'cos I'm not supposed to change"))
    (:transient do-not-save-me)
    ;; rules for every occasion
    (:rule ()
           (member i-am-a-boolean '(t nil)))
    ;; rules that should be true for running to succeed
    (:rule (:run)
            (member run-speed '("slow" "medium" "fast"))
            (>= log-level 0)
            (<= log-level 5))
    ;; rules that should be true for build to succeed
    (:rule (:build)
           (member debug '(t nil)))
    (:config-files "file1" "dir/file2.txt"))

Here, we defined five new slots for our item called example-item:
i-am-a-boolean, run-speed, log-level, debug and do-not-save-me. The
slots debug and do-not-save-me have default values: nil and the string
"'cos I'm not supposed to change".

Think about an item definition as if it was a class definition.


Transient slots
---------------

Items store their current value in the filesystem persistently. The
:TRANSIENT tells the item not to save the do-not-save-me slot. Slots
containing values not intended to be changed by someone configuring
the item should probably be declared transient. A typical example is
the vendor-version slot of third party libraries.


Rule sets and validation
------------------------

One can also define RULE SETS that ... !!! FIXME LATER !!!


Template processing
-------------------

Template files are coma's way of communicating with the outside
world. The template file contains variant sections between two @
chars. When the template file is processed the lisp code in the
variant section is evaluated and its value inserted at the point of
the variant section. Also, everything printed to the stream
*standard-output* goes to the output file.

To escape @ use @@.

In the :CONFIG-FILES section we list all files that should be produced
by processing their corresponding template files. For "file1" the
template file is "file1.coma" for "dir/file2.txt" it is
"dir/file2.txt.coma".

Template files are processed with the current directory set to the
root of the item. The slot symbols are bound to their respective
values in the item. The symbol 'self' is bound to the item itself.

Functions, variables, constants from the item definition file are also
available in variant sections. For example let's define a function
that returns "true" or "false" according to the boolean value of its
argument:

  (defun ->bool (v)
    (if v
        "true"
        "false"))


In the template file one can write:

  boolean_property=@(->bool i-am-a-boolean)@

instead of directly writing:

  boolean_property=@(if i-am-a-boolean "true" "false")@

Note that the output file not written if it already exists and has
identical content.

Accessing slots
---------------

With the -> one can access slots of an item. By writing in a template
file:

  (-> self i-am-a-boolean)

one accesses the i-am-a-boolean slot of the current item. There is no
need to do that since i-am-a-boolean refers directly to the
appropriate slot for convenience. But if the value of a slot is
another item that has a slot we are interested in:

  (-> other-item path)

or even

  (-> other-item slot-of-other-item path)


Standard slots
--------------

Items defined with defitem have two standard slots: name and
path. Name is a string containing the uppercase version of the name of
the item ("EXAMPLE-ITEM" in the example). Path is an absolute path to
the root directory of the item.

Kits have two more standard slots neither of which is important here.


Files 
-----

Usually an item has a directory named "cm" directly under its root. In
this directory there are a few files:

  coma.lisp - loader script for coma
  coma.sh - shell script to start coma
  .coma-sources.bundle - the bulk of the code packaged in one file
  .coma-build/ - directory that contains unpacked and compiled sources
  item.lisp - the item definition
  config.lisp - if exists it contains the current configuration of the item
  kit-items.lisp - the items in this kit and their configs (only for kits)
  map.lisp - mapping from the item names to files (not intended for the public)


Using coma.sh
-------------

  Generating files
  ----------------

    coma.sh [generate | g] [ <TARGET-DIR1> [ <TARGET-DIR2> ... ] ]

  Installs coma to all directories given as arguments. When invoked
  without arguments it defaults to the current directory.

  Configuring
  -----------

    coma.sh [configure | c]      or simply      coma.sh

  Activates the configuration typically by doing template processing
  for the configuration files. Configuration files are defined in the
  :config-files section of the item definition.

  Validating
  ----------

    coma.sh [validate | v] [ <RULE-SET1> [ <RULE-SET2> ... ] ]

  Runs all validation rules for the rule sets given as arguments. When
  invoked without arguments it runs all rule sets. Rule sets are
  defined in the :rule section of the item definition.


  Iterating over all items in a kit
  ---------------------------------

    coma.sh do <command> <args ...>

  Issues <command> with <args ...> in the root dir of each item in the
  kit. For example:

    coma.sh do hoc fw

  moves to the latest version on the current branch for each item in the kit,

    coma.sh do hoc ci

  commits all items. Remember that do does not include the kit itself.


Working with kits
-----------------

  Checking out the kit
  --------------------

    Check out a kit:

      hoc co e6-kit

    Let the kit check out its items:
      cd e6-kit
      cm/coma.sh

  Updating the kit
  ----------------

    In the kit sandbox (i.e. under the kit's root dir, but not under
    any other item's root dir) issue:

       hoc up

    Now, that the all important the kit-items file is updated, let's
    make the kit update its items accordingly:

       cm/coma.sh

  Changing the version of an item in the kit
  ------------------------------------------

  There are two ways to do it: edit kit-items.lisp and change the
  version by hand or go to the checked out item and issue the
  appropriate hoc command. If you take the latter aproach then
  kit-item.lisp is automatically updated for you.

  Committing
  ----------

    One commits the modified items one by one, writes the release note
    and supplies hoc with the information about the severity of the
    change. After every item is commited one may also commit the kit
    as well. Before commiting one should always do a 'hoc fw', just
    like one had to do a 'cvs up' in the old times.


    Writing the release note
    ------------------------

    The RN is written from the item's point of view. One does not refer
    to the kits the item is (or could be in) since it is not always
    known at the that time.

    Severity of change
    ------------------

      The hoc versions are of the form <major>.<minor>.<patch>,
      e.g. 0.0.0 and 1.2.3. At commit time one needs to tell hoc which
      version of the three to bump. The conventions governing this
      choice are:

        - if compatibility is broken then bump major

        - else if the item is enhanced in a way that it is upward
        compatible with the previous version, then bump minor

        - else it is a bugfix, so bump patch

      Let's say that item A is a conforming client of item B, if A
      uses B according to the rules set up by B. In other words, B
      defines what parts of it can be used by clients and how, and
      comforming clients must respect that. Most of the time
      conformance is not totally ensured and is governed by
      conventions.

      What effects compatibility? Any change to an item that can
      potentionally break a conforming client is a major one. A change
      that allows more (or the same) for a conforming item is a minor
      one. Otherwise, it is a patch.

      Typically, for libraries it means that changes to public
      interfaces of the item (including the item definition!) must be
      monitored.

      Consider an item that contains a property file that is read by
      another item. For this item its interface is defined by the keys
      in the property file. Any change to those keys can potentionally
      break its clients.
