Quick Start

Rule” is an active component that is receiving control every minute and does something with your project on your behalf. A simple scenario would be to monitor your Git master branch and run automated build every time a new commit appears there (aka continuous integration).

Let’s create a rule for said continuous integration mechanism, with the following “spec”:

urn:github:526301:git-on-commit(
  "ssh://git@github.com/jcabi/jcabi-aspects.git",
  "mvn test -e -Pci --settings ../settings.xml",
  ["me@example.com"],
  "m1.small",
  {
    "settings.xml": """
    <settings>
      <profiles>
        <profile>
          <id>ci</id>
          <properties>
            <my.secret>hello, everybody!</my.secret>
          </properties>
        </profile>
      </profiles>
    </settings>
    """
  }
)

Save it and in a few minutes you will see its first result. To see the result, just click its name. You will see every-minute “pulses”.

The spec defines how to construct an object that should receive control (see full syntax description). In this example, the object will be constructed from a template defined by user urn:github:526301 with git-on-commit name. The template requires five parameters. The first one is the URL of Git repository, as a Java string:

"ssh://git@github.com/jcabi/jcabi-aspects.git"

The second one is a bash command that will be executed if a new commit is found in master branch of the repository:

"mvn test -e -Pci --settings ../settings.xml"

The third one is a list of email addresses that will receive notifications when build is complete (either with success or failure). In the example, the list contains just one element, but you can use it with many, by means of specifying a collection:

["me@example.com", "you@example.com"]

The fourth one is an EC2 instance type, which will be used for execution of the build. When a new commit is found in your master branch, the rule will create a new AWS EC2 instance, execute your script, and terminate the instance. Your account will be charged for the price of the instance, approximately. Prices and instance types are listed in AWS documention.

The last parameter is a map of files to be uploaded to the EC2 instance before running the build script. They will be uploaded into /home/ubuntu directory, which is a home directory of the EC2 user for Ubuntu images. The map may contain more elements, for example:

{
  "secret.txt": "... content of the file ...",
  "settings.xml": """
  <settings/>
  """,
  "file-2.txt": "... another text file ..."
}

Triple quotes are used to specify a long text that constitues more than one line, similar to Groovy multi-line strings.

What if my repository is private?

If your git repository requires authentication, we would recommend to use SSH private key, that’s how:

urn:github:526301:git-on-commit(
  ...
  {
    "~/.ssh/id_rsa": """
    -----BEGIN RSA PRIVATE KEY-----
    MIIEowIBAAKCAQEAw93hQPucIa0D5nVhKHkHuyeBptzG6U7Lig4BbUAPjtSSsC2n
    2RCJwO6ToaVnG8IHMiGRkXgFqKr1rd9unuPLZFi/HTYMkf0SYdoJc3GL9Ywt0pam
    ... skipped ...
    a/pLWlmOF47jYTpc6j1Ws1JsxX+x6qrnN72P93dwjdNBgcgR3H3XtYVDSm3myMt6
    N9d/UY1o9c4kuhwbe9KlJGW8KBrZAS4j1RlvrBS8DVPjx18+xPyN
    -----END RSA PRIVATE KEY-----
    """
  }
)

Your private key will be uploaded as ~/.ssh/id_rsa file and picked up by Git while cloning of your repository.

If you are planning to use your private key for other rules, we recommend to move it to its own “passive” rule named, say, github-ssh-key:

"""
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAw93hQPucIa0D5nVhKHkHuyeBptzG6U7Lig4BbUAPjtSSsC2n
2RCJwO6ToaVnG8IHMiGRkXgFqKr1rd9unuPLZFi/HTYMkf0SYdoJc3GL9Ywt0pam
... skipped ...
2g2QQQKBgAQBp0FuT2o0KbxGnejYiU9T4SKMW3X3n2UWipx+TbntNNlGDP55NNYY
N9d/UY1o9c4kuhwbe9KlJGW8KBrZAS4j1RlvrBS8DVPjx18+xPyN
-----END RSA PRIVATE KEY-----
"""

Yes, it’s possible to define a rule of just one plain text value. It won’t get a pulse every minute, but can be used in other rules:

urn:github:526301:git-on-commit(
  ...
  {
    "~/.ssh/id_rsa": github-ssh-key()
  }
)

That’s how you avoid duplication and keep data in one place - by defining your own “passive” rules that don’t get every-minute pulses, but hold some data shared between other rules.

How much does it cost?

Rultor.com is an open source and completely free service. It is sponsored by TPC2.com. You don’t pay anything for creating an account and defining your rules and stands.

However, you can fund your account using PayPal and use the money to compensate other users for rule templates they provide to you. Together with rule templates, they provide you an access to their computing and storage resources (like AWS EC2 instances or FTP logging). Rultor.com charges your account automatically and transfers money to their accounts. A detailed statistics of your expenses is available at account page.

There is an allowance of $5.00 automatically added to your account every 30 days, if you don’t fund it otherwise. Thus, if you have just one continuous integration rule which runs, say three times per working day, using m1.small type of EC2 instance, your total cost will be $3.60 per month. This is less than the allowance you get automatically from us and you don’t need to fund your account at all.

Drain

A “drain” is where your rules log their events. Every rule has its own drain configured with its own spec. For example, you can configure it like:

urn:github:526301:ftp-drain("example", "secret-code")

In this example we’re using a template ftp-drain provided by urn:github:526301. This template logs all events to a private FTP server of that user and filters out non-important pulses. This template requires two parameters. The first one is the name of a “stand” and the second one is it authentication code.

Stand

Now it’s time to create a “stand”, where project members will see the progress of your rules. Stand spec is much simplier, for example this one can be used for a public project:

com.rultor.acl.Either(
  [
    com.rultor.acl.OpenView(),
    com.rultor.acl.MD5Keyed("0682f007844a0266990df1b2912f95bc")
  ]
)

An instantiated stand spec implements ACL interface, which decides who is allowed to read your stand and who is allowed to post into it. Your rules of work post into the stand and your project members read the stand at http://www.rultor.com/s/example (if the name of the stand is example).

In this spec we instantiate Either class with one argument of its constructor, which is a list of instances of class ACL. As the name implies, the class allows access if any of the encapsulated ACLs do so.

OpenView allows read access to everybody, which means that page http://www.rultor.com/s/example will be readable in public web and indexable by Google.

MD5Keyed allows write access only for providers of super-secret authentication key. As you already understood, 0682f007844a0266990df1b2912f95bc is its MD5 hash.

If you want to close access to your stand, use Prohibited without arguments:

com.rultor.acl.Prohibited()

This is a link to a stand we use to publish the progress of development of Rultor itself: www.rultor.com/s/rultor.

Widgets

On top of the stand of rultor you see a few nice “widgets” that summarize the information collected by running instances. You can add similar widgets to your own stand. Their spec is a collection of classes that inherit Widget interface, for example:

[
  com.rultor.widget.BuildHealth(),
  com.rultor.widget.BuildHistory()
]

These two will be enough to start with. For a full description of existing collection of widgets see com.rultor.widget package documentation.

Continuous Integration of Master Branch

In the example above, every new commit made to your Git master branch triggers a script, which builds the product and validates its consistency (you should use unit tests, integration tests, and static analysis as validating instruments).

Template git-on-commit provided by user urn:github:526301 does it all in a smart way. Let’s break it down into components.

As you already know, a rule receives control every minute. Technically it’s even more interesting. Every minute a rule is instantiated as a Java object, according to its spec and then receives control. We call this object an “instance”. The module that instantiates a rule and controls instances is called a “conveyer”.

When the instance’s lifespan comes to end, it looses control and gets destroyed by Java Virtual Machine. More to this, all instances are stateless immutable objects, which are not aware about each others existence. This models gives a lot of benefits in concurrency and traceability. However, it requires more accurate and precise configuration.

The first problem that git-on-commit template solves is a possibility of concurrent execution of multiple instances. When a new commit appears in Git branch, Conveyer creates an instance and lets it do its job, which is building the product. This process may take more than a minute. We don’t want to start a new build until the first one is finished. This problem is solved by git-on-commit by using semaphores in AWS SimpleDB. When using the template you pay for its usage of SimpleDB, but the price is very small (less than $0.00001 per request).

As you have probably already understood, there is no way to manually “trigger” execution of a rule. This is where Rultor differs from other continuous integration systems, like Jenkins. A new instance of every rule is created and executed every minute, 24 hours per day, 7 days per week. The only way to stop this process is to either delete the rule or enclose its spec into Empty, for example:

com.rultor.base.Empty(
  urn:github:526301:git-on-commit(
    "https://github.com/jcabi/jcabi.git",
    "mvn test -e -Pci --settings /home/ubuntu/settings.xml",
    ... skipped ...
  )
)

In order to detect new commits, git-on-commit clones your repository to a supplementary server and runs git pull on every execution (every minute). In most cases, sensitivy of one minute is not required, that’s why git-on-commit ignores four executions out of five.

Let’s say, you want to execute it even less often, every hour instead of every five minutes, to save money. You can use the same class Crontab that is used by git-on-commit:

com.rultor.base.Crontab(
  ${work}, "0 * * * *",
  urn:github:526301:git-on-commit(
    "https://github.com/jcabi/jcabi.git",
    "mvn test -e -Pci --settings /home/ubuntu/settings.xml",
    ... skipped ...
  )
)

Constructor of class Crontab accepts three parameters, where the first one is the Work being executed at the moment, the second one is crontab spec, and the third one is your original instance.

This is how Crontab is designed (it is a Decorator pattern):

public class Crontab implements Instance {
  private Work work;
  private String spec;
  private Instance origin;
  public Crontab(Work wrk, String spc, Instance org) {
     this.work = wrk
     this.spec = spc;
     this.origin = org;
  }
  @Override
  public void pulse() {
    if (this.allowed()) {
      this.origin.pulse();
    }
  }
  private boolean allowed() {
    // return TRUE if the time of Work#scheduled()
    // satisfies the requirements of the encapsulated
    // crontab spec, otherwise FALSE
  }
}

Most of the classes that you will use in your specs are using Decorator pattern, so get used to it.

When a new commit is detected, build script is executed in a newly created EC2 instance. When execution is finished and build succeeded or failed we don’t want to execute the same operation on the same commit. We want our rule to remember that a given commit has already been seen and should be ignored in the future. This check is also implemented by git-on-commit.

When execution is finished, git-on-commit uploads full output of the script to Amazon S3 and publishes a link to the file in the stand.

Release Every New Tag

Another popular scenario in almost any software project should be releasing of the next version. When your next version is stable enough to reach the market you tag its latest commit and document it in release notes. You can specify a rule in Rultor that will find such tags and run build script on them:

urn:github:526301:git-on-tag(
  "ssh://git@github.com/jcabi/jcabi-aspects.git",
  "mvn deploy -e -Pci --settings ../settings.xml",
  ["me@example.com"],
  "m1.small",
  {
    "settings.xml": """
    <settings>
      <profiles>
        <profile>
          <id>ci</id>
          <properties>
            <my.secret>hello, everybody!</my.secret>
          </properties>
        </profile>
      </profiles>
    </settings>
    """
  }
)

The spec looks almost identical to the one discussed above, with two important differences. First, we’re using git-on-tag template provided by user urn:github:526301. Second, we’re instructing Maven to deploy our artifacts, not just test them:

"mvn deploy -e -Pci --settings ../settings.xml"

Template git-on-tag expects you to name your tags according to Semantic Versioning convention and prefix them with a single word, for example:

rultor-0.1.5
rultor-5.3-beta
example-2.0.3.GA

Guard Your Master Branch

It is a very good practice to test changes submitted to your code base before merging. When you receive a pull request from one of your project contributor, you should review the code for its logical validity and then run build automation script with all quality checks turned on. If build fails, you should reject this pull request and explain why. This technology is also known as pre-flight build.

Read this article published by Yegor Bugayenko in php|Architect managazine on August 2010: “Prevent Conflicts in Distributed Agile PHP Projects”

The process can be automated using github-on-pull-request provided by user urn:github:526301:

urn:github:526301:github-on-pull-request(
  "rultor/rultor",
  "rultor-guard",
  "... our secret password to Github ...",
  "m1.small",
  "mvn clean install -B -C",
  "rultor",
  ["yegor256"],
  ".*good to merge.*",
  {".ssh/id_rsa": github-ssh-key()}
)

Once a new pull request is ready user yegor256 has to review the code and post a message that matches .*good to merge.* regular expression to its issue. The rule will pick this pull request up, will merge it with master its destination branch and merge, if succeeded. Otherwise it will post a negative message to the Github issue and won’t try again, until a new comment “good” comment is posted by yegor256.

How to Contribute?

Rultor is an open source project hosted at Gihub. You’re very welcome to suggest new features, report bugs, and ask questions in Github Issue Tracker. The project is still in “pre-beta” state.

Moreover, you’re welcome to fork our repository and submit your changes and/or improvements.