Dozer, a simple continuous integration / deployment / delivery / task runner (CI/CD).
Useful if:
- You don't have your own server and/or existing cloud CI/CD options seem like overkill.
- Existing cloud CI/CD options are not suitable for some reason (e.g. too hardware demanding runs).
- You want to use command line tools that are hard to install on cloud runners.
- Your environment is already set up and you want to save time installing all tools on each CI/CD run.
- You want to develop and test your CI/CD steps on your localhost before uploading the scripts to a cloud CI/CD.
The GUI is optional. Dozer can also just run as a CLI command.
Instructions for Dozer v3 and earlier.
- If you have Dozer 3 or earlier installed, uninstall it first (find Dozer in the Apps & Features settings dialog, click it and select Uninstall).
- Download a release archive for your platform from the Releases page and unzip it to a safe folder.
- Add that folder to your PATH. Then make sure to open a fresh terminal before using Dozer.
Save a YAML file in your project's directory, for example, ci.yaml:
runtimeDirectory: C:/somewhere # optional, default: current working directory
steps:
- title: Print Node Version # any display name
command: node -v # CLI command to run
- title: Another step
workingDirectory: C:/Program Files/Java/jdk1.8.0_211/bin
skippable: true # the execution will continue on failure of this step
command: java.exe
-version # can be multi-line
- title: Downloaded Script # This step automatically downloads the linked code and saves it under the temporary directory.
command: node ${TMP}/delayed.js 5
code: https://raw.githubusercontent.com/kasp1/Dozer/v4/examples/dev/delayed.js
Then in your project's directory, run the following command:
dozer ci.yaml # [--gui|--webui|--no-api]
--gui
opens a native user interface window by running thedozerui
command.--gui <command|path>
opens a native user interface window with the specified command or path to binary.--webui
starts an internal web server and opens the web user interface in a browser tab.--webui-port
defines the port for the internal web user interface server to start on,8221
by default.--webui <url>
will open an external web user interface by the specified URL in a browser tab, won't start an internal web server.--no-browser
will prevent--webui
from opening the browser tab automatically, while--webui
will still start the web server (so if the WebUI address is input in a browser tab manually, it will work).--no-api
disables the Websockets API, no UI will be able to connect and display the progress and outputs.--api-port <number>
changes the port that the user interfaces should connect to,8220
by default.--root <dir>
changes the starting working directory. Overrides the YAMLruntimeDirectory
option. Both\
and/
can be specified as path separators.--key=<value>
specifies any environment variable calledkey
for the pipeline steps with the value of<value>
. Examples:--foo=bar
,--CI_MY_VAR=Hi
,"--CI_MY_VAR=Hi there!"
(Dozer 4.2.0+).
Warning: it is a bad practice and a security risk to have your pipeline steps output sensitive data. If you don't specify the
--no-api
argument while running Dozer, and if the machine's IP is public, anyone in the world can connect to the Dozer runner and see the output logs. For ease of use, Dozer will open the UI API by default.
Dozer steps don't need to be written in any particular language. Create an executable script/binary in your favorite programming language. Test it with regular command line calls, then add it as a Dozer step in a similar manner.
Sometimes you need to pass data from one step to another. With Dozer, you would set an environment variable by outputting a regular standard output line with the syntax ##varName=value#
.
Node.js:
console.log('##NEXT_VERSION=1.0.1#')
Python:
print('##NEXT_VERSION=1.0.1#')
PHP:
print '##NEXT_VERSION=1.0.1#';
Dart:
print('##NEXT_VERSION=1.0.1#');
Please note that ##
cannot be prepended with another output on the same line.
Note: the values of variables containing any of the following words will be automatically hidden in the NativeUI and WebUI interfaces:
secret
,password
,pwd
,passwd
,token
, case-insensitive. For example,CI_SECRET_LOGIN
would be considered sensitive information and hidden.
All environment variables, including the ones you set in the previous steps, are obtained the standard way for your language of choice.
Node.js
process.env['NEXT_VERSION']
Python
import os
print(os.environ['NEXT_VERSION'])
PHP
getenv('NEXT_VERSION');
Dart
import 'dart:io';
Platform.environment['NEXT_VERSION']
Assuming you have semantic-release set up for your project, add the following semantic-release-env-version
plugin to your semantic-release configuration, e.g:
"release": {
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/changelog",
[ "./semantic-release-env-version.js", {
"varName": "CI_VERSION",
"setOnlyOnRelease": false
}]
]
}
Don't forget to update the path to the plugin's file semantic-release-env-version.js
, which should contain the following code:
const { spawnSync } = require("child_process")
function setDozerCiVersion(varName, version) {
version = version || '1.0.0' // 1.0.0 if there is no tag history in the repo
varName = varName || 'CI_VERSION'
console.log(`##${varName}=${version}#`)
}
let mod = {
async analyzeCommits (pluginConfig, { lastRelease: { version }, logger }) {
const setOnlyOnRelease = pluginConfig.setOnlyOnRelease === undefined ? true : !!pluginConfig.setOnlyOnRelease
if (!setOnlyOnRelease) {
setDozerCiVersion(pluginConfig.varName, version)
}
},
async prepare (pluginConfig, { nextRelease: { version }, logger }) {
setDozerCiVersion(pluginConfig.varName, version)
}
}
module.exports = mod
Step to run semantic release:
steps:
- displayName: 'Semantic Release'
comand: npx semantic-release --no-ci
Discord: https://discord.gg/TwnpfKVzGH
- Make sure you have Node.js installed.
- Clone the repository.
- In a terminal, change to the
runner
directory. - Run
npm i
to install dependencies.
The source code of the runner is in the runner
folder. Each time you want to test, execute node runner/main.js ci.yaml
the same way as you would execute dozer ci.yaml
.
The examples/dev
contains a few pipelines handy for development.
Before using the --webui
argument, you need to make sure the Web UI is built under dist/webui
(please see how to build it below). Alternatively, you can specify another webui directory for Dozer Runner to serve the Web UI files from by setting the DOZER_DEV_WEBUI_DIR
to a directory containing the files (e.g. in the existing Dozer installation in your system). Another alternative would be specifying the URL to a third-party Web UI page, e.g. --webui http://localhost:8080/#localhost:8220
, which won't start the Web UI file server but will just open a browser tab for you.
Before using --gui
, you need to make sure the dozerui
command exists on your system. If the command is not present in your system PATH
environment variable, you can specify a direct path to the Native UI binary as --gui path/to/binary
. The same way you can also guide the Dozer Runner to start any other command present on your system, e.g. --gui customui
.
Install pkg
and ncc
, the commands which compile the Runner into binaries... npm i -g pkg @vercel/ncc
.
In the project root folder, execute ncc build runner/main.js -o dist && pkg dist/index.js -o dist/dozer-win -t latest-win
. The built binary should appear as ../dist/dozer-win.exe
.
- Make sure you have Node.js and Vue CLI installed.
- Clone the repository.
- In a terminal, change to the
webui
directory. - Run
npm i
to install dependencies.
The source code of the WebUI is in the webui
folder. Each time you want to test, execute node run serve
, which will make the Vue CLI open the WebUI in a browser window in a hot-reload mode.
In the webui
folder, execute node run serve
. The built files should appear under ../dist/webui
.
- Make sure you have Visual Studio Community and Flutter SDK installed.
- Clone the repository.
- In a terminal, change to the
nativeui
directory. - Run
flutter pub get
to install dependencies.
The source code of the NativeUI is in the nativeui
folder. Each time you want to test, execute flutter run windows
, which will start the UI app in a hot-reload mode. If there is a Dozer process running in the background, the UI will automatically connect to it and display progress.
In the nativeui
folder, execute flutter build windows
. The built files should appear under nativeui/build/windows/runner/Release
.
Before packaging for distribution, the development and build requirements for each Runner, WebUI, and NativeUI have to be met first.
The DOZER_VC_DLLS
environment variable needs to be set to the path where msvcp140.dll
, vcruntime140.dll
, vcruntime140_1.dll
are located, e.g. C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Redist\MSVC\14.32.31326\x64\Microsoft.VC143.CRT
.
Run build-dist-win.bat
. When finished, the Dozer distributable should be present as dist/Dozer_<version>_Windows_x64.zip
.
Run build-dist-linux.bat
. When finished, the Dozer distributable should be present as dist/Dozer_<version>_Linux_x64.zip
.
Run build-dist-mac.bat
. When finished, the Dozer distributable should be present as dist/Dozer_<version>_MacOS.zip
.
Unless the --no-api
command line argument is specified on the Dozer Runner, it will open the Websockets API for any custom UI or scripts to connect and get information about the pipeline's progress.
The communication messages are stringified JSONs. Any newly connected client will automatically receive the recap
message, which contains the list of pipeline steps and their up-to-date statuses and logs, and environment variables before and after their execution. Afterward, there will be automatically coming messages when a step will change its status when a step has a new output and periodic updates about environment variables (between each step).
If the incoming message contains the { "recap": ... }
key, it will contain information about each step in the pipeline and its up-to-date history.
If the incoming message contains { "step": ..., "output": ... }
keys, it will contain a new output
content to be attached to the output history of the specified step
.
In the event of a step finishing, a message will arrive containing the { "step": ..., "status": ..., "totalTime": ..., "vars": ... }
keys. If total totalTime
is present, it is the time that the step took to execute in milliseconds.