This GitHub Action can be used to build and unittest TwinCAT PLCs that are contained in a Visual Studio Solution file (.sln) in a repository, This facilites CI/CD for TwinCAT by providing an up-to-date, virtualized build-infrastructure as well as proven developer tools. Use with an action such as publish-unit-test-result-action to publish the results of the unittests to GitHub.
The main goal of this GitHub action is to support open-source projects with CI/CD for TwinCAT. Many maintainers of open-source projects may not have the resources to implement, host, and continously upgrade a build system for TwinCAT. By registering to use this service for public repositories, you can run the action up to 30 times per month.
Additionally, we offer a commercial solution for companies seeking an enterprise-level service. This solution can be tailored to meet your specific needs, whether you prefer to use our build system with our servers, deploy our developer tools on your own servers, or any combination of both. For more information and to discuss your requirements, please contact us.
-
username
: Username of a Zeugwerk Useraccount obtained by registering (Required) -
password
: After registering, you'll receive an e-mail to set a password for your account, please check your spam folder if no e-mail appears in your inbox (Required) -
tcversion
: (Optional) This defaults to TC3.1 and should usually not be changed without contacting us. The setting can be used to overwrite the TwinCAT target that the PLC is compiled for.
We highly recommend to store the value for username
and password
in GitHub as secrets. GitHub Secrets are encrypted and allow you to store sensitive information, such as access tokens, in your repository. Do these steps for username
and password
- On GitHub, navigate to the main page of the repository.
- Under your repository name, click on the "Settings" tab.
- In the left sidebar, click Secrets.
- On the right bar, click on "Add a new secret"
- Type a name for your secret in the "Name" input box. (i.e.
ACTIONS_ZGWK_USERNAME
,ACTIONS_ZGWK_PASSWORD
) - Type the value for your secret.
- Click Add secret.
This action requires a configuration file that is places in the folder .Zeugwerk/config.json
. The simplest way to generate a configuration file is by using the Twinpack Package Manager.
A typcial configuration file for a solution with 1 PLC looks like this (Twinpack generates this for you automatically)
{
"fileversion": 1,
"solution": "TwinCAT Project1.sln",
"projects": [
{
"name": "TwinCAT Project1",
"plcs": [
{
"version": "1.0.0.0",
"name": "Untitled1",
"type": "Library",
"packages": [
{
"version": "1.2.19.0",
"repository": "bot",
"name": "ZCore",
"branch": "release/1.2",
"target": "TC3.1",
"configuration": "Distribution",
"distributor-name": "Zeugwerk GmbH"
}
],
"references": {
"*": [
"Tc2_Standard=*",
"Tc2_System=*",
"Tc3_Module=*"
]
}
}
]
}
]
}
zkbuild can also execute unittests. We support two variants how this can be achieved
- For this, it is mandatory to place your unittest solution in a subfolder called
tests
- It requires the usage of the latest release of TcUnit.
- Tests can be implemented as documented in TcUnit.
- A seperate configuration file is needed in
tests\.Zeugwerk\config.json
, which describes how to build the unittest PLC
- This is the perferred way for us to implement tests, because it puts the tests right next to the actual code.
- It requires the usage of the package
Zeugwerk.Core
(ZCore
), because zkbuild relies on an assertions interface that is defined in this library. - Creating tests is pretty straight forward: If you want to write testsuite for the function block
Valve
, implement the following function block - zkbuild automatically removes all function blocks that implement
ZCore.IUnittest
from the .library file that is returned by the action
FUNCTION_BLOCK ValveTest EXTENDS Valve IMPLEMENTS ZCore.IUnittest
METHOD Test_NameOfTest1
VAR_INPUT
assertions : ZCore.IAssertions;
END_VAR
assertions.IsTrue(TRUE, 'This test passes');
METHOD Test_NameOfTest2
VAR_INPUT
assertions : ZCore.IAssertions;
END_VAR
assertions.EqualsDint(5, 4, 'This test failes');
It is important that the function block implements the interface ZCore.IUnittest
, then every method with the signature above is regarded as a test.
You can implement as many testsuites and tests as you want. The assertions interface offers a lot of methods to write tests. Extending from the function block that is tested allows to manipulate private variables of your test object.
When the action is successfully finished and the configuration is set to type = Library
, there will be a library placed in the working directory of the GitHub running in the path archive/REPO_NAME/TC3.1/PLCNAME_VERSION.library
.
This library can then we uploaded to the Twinpack Package Manager via a seperate publish action. Of course it is also possible to process the library further in any other way (creating a GitHub Release, pushing it to a private server, ...)
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@v3
with:
name: artifact
path: |
**/*library
- name: Publish Unittest
uses: EnricoMi/publish-unit-test-result-action@v1
with:
files: archive/test/TcUnit_xUnit_results.xml
Zeugwerk CI supports to apply patches to the source code of the repository, which are applied before compiling the code. At the moment the following types of patches are supported
Platform patches
: are applied for specific TwinCAT VersionsArgument patches
: can be used for feature flags. At the moment this feature is not enabled for the free-to-use zkbuild, if you are interested in using it for your open source project, you may submit an issue.
To use patches, a config.json
file has to be used and patches have to be configured in the configuration file as follows
{
"fileversion": 1,
"solution": "TwinCAT Project1.sln",
"projects": [
{
"name": "TwinCAT Project1",
"plcs": [
{
...
"name": "Untitled1",
"patches":
"platform": {
"TC3.1.4024.56": [
"git-patch-for-specific-twincat-version.patch",
"search-and-replace.for-specific-twincat-version.replacement"
]
},
"argument": {
"": [
"git-patch.patch",
"search-and-replace.replacement"
]
},
...
}
]
}
]
}
Any file with the extension .patch
is applied as a Git patch. You can use git diff > some-changes.patch
to create the patch and the CI system, will use git apply -3 some-changes.patch
to apply the patch. If the patch can not be applied correctly, the pipeline will go to failure.
We support a mechanismn to do search-and-replace over many files in the source code before it is compiled. This is simpler than using a proper git patch. A replacement patch is a json file, which has the following format and has the file extension .replacement
{
"filter": "*.TcPOU",
"search": "pattern",
"replace": "replacement"
}
filter
can be used to filter for specific files, the examples filters for all POUs in the source codesearch
is a the regular expression pattern to match, special characters have to be escapedreplace
is the replacement string, which may contain identifiers to refer to captures of the regex ($1
...$N
). It is also possible to use enviornment variables here. Zeugwerk CI uses Jenkins, enviornment variables can be referred to with{{env.ENVIORNMENT_VARIABLE}}
. See here for a list of possible enviornmemt variables.