Croni is a simple cron based CI server with a responsive bootstrap powered web client. It enables Continuous Integration with almost no setup, just add your build and test scripts an trigger them periodically to get a notification about errors beforehand and not while compiling.web testing ci
Croni shall help persons and small teams, who are having the need for a build server, which at least runs daily/hourly. But don’t want to setup and maintain a fully blown build setup like Jenkins, Bamboo, et cetera.
What functionalities does croni provide?
Basically, croni adds a bootstrap powered front end to cron for better overview and forces one to put each cronjob in a repository, which makes it easy to share, keep track of changes and deploy croni in seconds.
The croni front end does not provide any interaction like triggering a build or the like. Furthermore, it only provides information about finished builds, i.e. no informaton about running jobs are shown.
List of functionalities/requirements:
- separate workspaces for each build
- declaring global/job-specific timeout, recipients and failure message depending on exit code
- log and workspace rotation of jobs (gc)
- provide update automation for jobs repository
- provide manual croni submodule update
- ‘sendmail’ for failed builds
- expose build information on local HTTP server (static web pages)
- show console log for each job run in pop up window
- show croni.log in pop up window
- expose job workspaces
- python < 3
Dependencies in bold has to be met! Python is only necessary for HTTP server and firefox opens croni’s index.html locally without one. Croni will run fine without sendmail, it simply just doesn’t send mails.
Give it a try!
First, back up your current crontab - it will be replaced! Then execute following commands:
git clone https://github.com/boddenberg-it/croni-test cd croni-test ./init.sh
Now, you should see your croni instance on http://localhost:8080.
> Firefox should open the index.html file locally.
The first table shows information about jobs and submodule repositories. The second table is a build timeline.
Some rows provide links: * duration opens workspace. * name opens job page. * build number opens console log as seen below.
> Clicking ‘welcome to croni’ will show croni.log in same pop up.
Each project page is linked in the navigation bar and shows a table holding latest results of jobs within the project.
Each job page holds a job-specific timeline similar to timelines above.
After explaining the front end let’s take a look at the structure of the example jobs repository:
. ├── croni ├── croni.cfg ├── init.sh ├── jobs │ └── hello_project │ └── hello_world.sh └── README.md
Btw, croni creates ~/.croni while initialising. It is not listed above and holds following parameter:
croni_run=true croni_update=false croni_sendmail=false
The croni.cfg file holds following parameter:
croni_update_expression="0 5,17 * * *" croni_port="8080" croni_server_check_expression="0 * * * *" croni_workspace_rotation="14" croni_build_rotation="28" # all default_xyz parameters can be overridden by job. default_croni_timeout="1800" default_croni_mail_recipients="firstname.lastname@example.org" default_croni_reason_87="This is the global (croni.cfg) reason message for exit code 87"
How to create jobs?
A job can be any executable script, which declares following parameters in code or comment.
#!/usr/bin/python croni="0 * * * *" print('Hello World!')
Here is an example with all available job parameters.
#!/bin/sh croni="0 * * * *" croni_mail_recipients="email@example.com" croni_timeout="90" croni_reason_87="job failed with exit code 87" echo "foobar"
Additionally, creating a folder e.g. ‘scripts’ will allow you to call scripts within this folder and pass arguments as follows:
#!/bin/sh croni="0 * * * *" $base/scripts/example_script.sh "foo" "bar"
The ‘initialised’ branch of croni-test repository holds the test suite. It should give a good overview.
How to maintain croni?
Croni automatically updates the jobs repository as long as croni_update in ~/.croni is true.
> Any local changes will be stashed in order to fulfill the update - nothing is lost!
Furthermore, you can use following commands:
# deploys changes ./croni.sh deploy # fetches changes of jobs repository ./croni.sh update # fetches changes of croni submodule ./croni.sh upgrade # ensures server is running if croni_port is declared ./croni.sh start_server ./croni.sh run $project $jobfile # runs although croni_run is "false" in ~/.croni ./croni.sh test $project $jobfile
Moreover, an alias in ~/.bashrc à la:
alias croni="[PATH_JOBS_REPO]/croni.sh $@"
will be useful to execute croni from any directory.
Okay, how do I keep this example?
You can simply fork the croni-test repository on github or create an empty repository on any arbitrary git server. Then do steps in “Give it a try!” section and change the remote-url to the one of your repository and push.
git remote set-url origin $URL git push -u origin master
Congratulations, you have just created your croni build server!
Basically, it’s a hacky prototype. It would be interesting to (re)write croni properly in python to build a basis to go towards a “mature” build server, but this depends on the feedback. Personally, it’s a handy cronjob presenter and handler.