zkbuild (zkbuild-action)
One of Zeugwerk’s CI/CD tools for TwinCAT. Manual PLC builds in the IDE don’t scale and aren’t reproducible. zkbuild lets you compile your PLCs and run unit tests in the cloud -no Jenkins or build servers. zkbuild-action is available for all major Git hosts (GitHub, GitLab, Bitbucket) and calls our CI/CD backend. Push code; get reproducible .library artifacts and test results.
Free tier for public repos (30 builds/month). For private repos or higher volume, contact us.
On-premises / self-hosted builds - If you need to build on your own infrastructure (private network, air-gapped, custom CI), use zkmake instead. It provides the same build and test capabilities using a local TwinCAT XAE installation.
Why use it
- Reproducible builds - Same output every time, independent of your machine.
- Automated testing - Run TcUnit or Framework unit tests in CI; the build fails if tests fail.
- No manual builds - Avoid “forgot to rebuild” and “works on my machine” issues.
- Audit trail - Every build is logged and traceable (useful for compliance).
- Zero DevOps - No Jenkins or build servers to maintain.
Quick start
- Register - Register here. Public repos get 30 free builds/month.
- Add a workflow - The example below is for GitHub; zkbuild-action also works with GitLab CI and Bitbucket Pipelines. Create
.github/workflows/build.ymlin your repo (or the equivalent in your Git host):
name: Build/Test
on:
push:
branches: [main, 'release/**']
pull_request_target:
workflow_dispatch:
jobs:
Build:
name: Build/Test
runs-on: ubuntu-latest
steps:
- name: Build
uses: Zeugwerk/zkbuild-action@1.0.0
with:
username: ${{ secrets.ACTIONS_ZGWK_USERNAME }}
password: ${{ secrets.ACTIONS_ZGWK_PASSWORD }}
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: artifact
path: "**/*.library"
- name: Publish Unittest
uses: EnricoMi/publish-unit-test-result-action@v1
with:
files: archive/test/TcUnit_xUnit_results.xml
- Store credentials - Add
ACTIONS_ZGWK_USERNAMEandACTIONS_ZGWK_PASSWORDas GitHub Secrets. Do not commit them. - Configure the build - Add a
.Zeugwerk/config.jsonin your repo (see Configuration). You can generate it with Twinpack.
How it works
Your push triggers the action → the action calls Zeugwerk CI/CD → our server builds the solution, runs unit tests, and returns artifacts (e.g. .library files, test results). The action uploads them to the workflow run. No Jenkins or self-hosted runner required.
Configuration
Inputs
| Input | Required | Default | Description |
|---|---|---|---|
username |
Yes | Zeugwerk account username. Store as a GitHub Actions secret and reference via secrets.ZEUGWERK_USERNAME. |
|
password |
Yes | Zeugwerk account password. Store as a GitHub Actions secret and reference via secrets.ZEUGWERK_PASSWORD. |
|
tcversion |
No | (auto) | TwinCAT version for compiling and testing (e.g. TC3.1.4024.22). Must be available on the Zeugwerk CI/CD server. If omitted, an available version is selected automatically. |
platform |
No | TwinCAT RT (x64) |
Target platform for the build. Must match a platform supported by the Zeugwerk CI/CD server. |
variant-build |
No | (default) | Name of the PLC project variant to build. Useful when the project defines multiple variants for different hardware configurations. |
variant-test |
No | (default) | Name of the PLC project variant to use when running unit tests. Typically a variant without hardware dependencies so tests can run in simulation. |
static-analysis |
No | false |
Enable TwinCAT static analysis during the build. true runs light analysis for open-source users and full analysis for Zeugwerk customers (full analysis requires a Beckhoff license). Uses the static analysis settings from the .plcproj files. |
installer |
No | false |
When true, generates an installer package that allows the PLC to be deployed to a target system from a Windows PC. See PLC deployment installer. |
installer-name |
No | Setup-<REPO> <VERSION>.exe |
Override the installer file name. |
workspace |
No | ./ |
Path to the directory containing the .Zeugwerk folder. Use this when your project is not at the repository root (e.g. src/MyPlc/). |
version |
No | (from latest tag) | Override the version number used when naming build artifacts. Accepts 1.2.3.4 or 1.2.3-alpha style strings. |
skip-build |
No | false |
Skip compiling the project and producing build artifacts. Useful when you only want to run unit tests. |
skip-test |
No | false |
Skip compiling and executing unit tests. Useful for faster feedback on draft PRs. |
force-checks |
No | false |
Force a "Check All Objects" pass even for application PLCs. By default this is skipped for applications since "Build Solution" is sufficient. |
artifact-name |
No | artifact.zip |
File name for the downloaded build artifact archive. Override to avoid collisions when multiple build jobs run in the same workflow. |
Outputs
| Artifact | Location |
|---|---|
.library file(s) |
archive/<repo>/<tcversion>/<plc>_<version>.library |
| Test results (JUnit) | archive/test/TcUnit_xUnit_results.xml (embedded / tests/) · archive/tests/<TestProjectFolder>/TcUnit_xUnit_results.xml (in-solution) |
Installer (.exe) |
archive/<installer-name> (when installer: true) |
| Build logs | GitHub Actions run summary |
Config file
zkbuild-action reads .Zeugwerk/config.json from the repository root (or from the directory set by workspace). Use Twinpack to generate and maintain it. For the full schema including packages, references, modules, and patches: config.json reference.
Unit tests
zkbuild-action discovers and runs unit tests automatically. Three layouts are supported: embedded test FBs (ZCore.IUnittest / Testbench.IUnittest, Option A), a standalone project under tests/ (Option B), or an in-solution UnitTestApplication in the main config.json (Option C, native TcUnit). Results are written as JUnit XML under archive/test/ or archive/tests/<TestProjectFolder>/ depending on the layout.
For setup, folder layout, in-solution configuration, and CI reporting examples: Unit tests.
Input examples
Pin a TwinCAT version
- uses: Zeugwerk/zkbuild-action@1.0.0
with:
username: ${{ secrets.ZEUGWERK_USERNAME }}
password: ${{ secrets.ZEUGWERK_PASSWORD }}
tcversion: TC3.1.4024.56
Build and test different variants
Use variant-build for the release build and variant-test for a simulation variant that runs without real hardware:
- uses: Zeugwerk/zkbuild-action@1.0.0
with:
username: ${{ secrets.ZEUGWERK_USERNAME }}
password: ${{ secrets.ZEUGWERK_PASSWORD }}
variant-build: Release
variant-test: Simulation
Enable static analysis
- uses: Zeugwerk/zkbuild-action@1.0.0
with:
username: ${{ secrets.ZEUGWERK_USERNAME }}
password: ${{ secrets.ZEUGWERK_PASSWORD }}
static-analysis: true
Generate an installer package
- uses: Zeugwerk/zkbuild-action@1.0.0
with:
username: ${{ secrets.ZEUGWERK_USERNAME }}
password: ${{ secrets.ZEUGWERK_PASSWORD }}
installer: true
installer-name: Setup-MyPLC ${{ github.ref_name }}.exe
Project not at repository root
Use workspace when .Zeugwerk/config.json is in a subdirectory:
- uses: Zeugwerk/zkbuild-action@1.0.0
with:
username: ${{ secrets.ZEUGWERK_USERNAME }}
password: ${{ secrets.ZEUGWERK_PASSWORD }}
workspace: src/MyPlc/
Run tests only (skip build artifacts)
Useful on pull requests where you want fast feedback without producing .library files:
- uses: Zeugwerk/zkbuild-action@1.0.0
with:
username: ${{ secrets.ZEUGWERK_USERNAME }}
password: ${{ secrets.ZEUGWERK_PASSWORD }}
skip-build: true
PLC deployment installer
When installer: true, zkbuild-action produces a Windows .exe (by default Setup-<REPO> <VERSION>.exe in archive/). Download it from your build artifacts and run it on a Windows engineering or service PC to deploy the compiled PLC boot project to a TwinCAT target.
The installer packages the boot project from your CI build. On the target PC it asks for deployment settings, then deploys to the PLC identified by its AMS NetID. Deployment can take several minutes.
Prerequisites
- Windows PC with network access to the TwinCAT target
- AMS NetID of the target PLC (for example
127.0.0.1.1.1for a local runtime) - Administrator rights recommended when running silently in automated scripts
Interactive installation
- Download the
.exefrom your CI build artifacts. - Run the installer and follow the wizard.
- On TwinCAT Deployment Options, enter the AMS NetID of the target PLC. The default for a local runtime is
127.0.0.1.1.1. - On TwinCAT Restart Option, choose whether TwinCAT should be restarted after deployment.
- Restarting may cause mechanical crashes or damage if the machine is still running. Only enable this when the system is in a safe state.
- Complete the wizard. The installer runs until deployment has finished.
Silent installation
For unattended deployment (scripts, MDM, remote maintenance), run the installer directly from an elevated command prompt. The process blocks until setup exits.
"Setup-MyPLC 1.0.0.0.exe" /SILENT /NETID=127.0.0.1.1.1
"Setup-MyPLC 1.0.0.0.exe" /VERYSILENT /NETID=192.168.0.1.1.1 /RESTART=1
Parameters
| Parameter | Required in silent mode | Description |
|---|---|---|
/SILENT |
— | Runs without the wizard. No user interaction is required when /NETID is supplied. |
/VERYSILENT |
— | Same as /SILENT, but also suppresses the standard setup progress window. |
/NETID=<ams net id> |
Yes | AMS NetID of the target PLC (same value as in the wizard). Example: /NETID=127.0.0.1.1.1 |
/RESTART |
No | Restart the TwinCAT runtime after deployment. Equivalent to checking the restart option in the wizard. |
/RESTART=1, /RESTART=true, /RESTART=yes |
No | Same as /RESTART. |
Warning
In silent mode there is no confirmation dialog for /RESTART. Use it only when the machine is in a safe state.
Pricing
- Free - 30 builds/month for public repositories.
- Commercial - Custom pricing for private repos and higher volume. contact us.
Examples
struckig, DeviceInfo, Demo-Twincat-Application-CI, rplc. See the Actions tab in any of these repos for build results.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Credentials not found | Secrets not configured | Ensure ZEUGWERK_USERNAME and ZEUGWERK_PASSWORD are set in GitHub Secrets. |
| TwinCAT version mismatch | Requested version not available on CI server | Set tcversion explicitly, e.g. tcversion: TC3.1.4024.56. |
| Tests not running | Test FBs / config not detected | Use embedded FBs, a tests/ project, or UnitTestApplication in config.json. See Unit tests. |
| Build skipped unexpectedly | skip-build: true set |
Check your workflow inputs; remove skip-build if you need artifacts. |
| Variant build failing | Variant name mismatch | Verify the variant name in variant-build exactly matches what is defined in the TwinCAT project. |
| Installer not produced | installer not set |
Add installer: true to the action inputs. |
| Wrong artifact picked up | Multiple builds in same run | Set a unique artifact-name per job to avoid collisions. |
| Project not found | Monorepo with non-root project | Set workspace to the subdirectory containing .Zeugwerk/config.json. |
Advanced: config with dependencies
For declaring packages and references in .Zeugwerk/config.json, see the config.json reference.
Advanced: patches
zkbuild-action supports platform patches (applied automatically per TwinCAT version) and argument patches (applied when you pass the patch input). For the full patch configuration format, see the config.json reference - Patches.
Note
Argument patches are not available on the free zkbuild tier. Open an issue if you need them for an open source project.
Comparison: zkbuild-action vs. zkmake
| zkbuild-action (cloud) | zkmake (on-premises) | |
|---|---|---|
| Infrastructure | Zeugwerk CI/CD (no setup) | Your Windows build server |
| TwinCAT XAE required | No | Yes (on the build machine) |
| Pipeline support | GitHub, GitLab, Bitbucket | Any CI that runs Windows .exe |
| Private repos | Commercial plan | License required |
| Free tier | 30 builds/month (public repos) | - |
| Config format | .Zeugwerk/config.json |
.Zeugwerk/config.json |