- Update .gitmodules: ztools, silo, and OndselSolver now reference public git.kindred-systems.com URLs instead of internal Gitea - Merge OndselSolver numerical solver with ML solver scaffolding into unified kindred/solver repository - Rewrite README.md for conciseness - Add docs/CI_CD.md with full pipeline documentation - Rework CI/CD workflows for public dockerized runners - Add multi-platform release builds (Linux, macOS, Windows) - Release workflow triggers on v* tags only - Update docs/REPOSITORY_STATE.md and docs/INTEGRATION_PLAN.md
9.4 KiB
CI/CD
Kindred Create uses Gitea Actions for continuous integration and release builds. Workflows are defined in .gitea/workflows/.
Overview
| Workflow | Trigger | Purpose | Artifacts |
|---|---|---|---|
build.yml |
Push to main, pull requests |
Build + test | Linux tarball |
release.yml |
Tags matching v* |
Multi-platform release | AppImage, .deb, .dmg, .exe, .7z |
All builds run on public runners in dockerized mode. No host-mode or internal infrastructure is required.
Build workflow (build.yml)
Runs on every push to main and on pull requests. Builds the project in an Ubuntu 24.04 container and runs the test suite.
Steps
- Install system prerequisites (GL, X11, fontconfig headers)
- Checkout with recursive submodules
- Install pixi
- Restore ccache from prior builds
- Configure via
pixi run cmake --preset conda-linux-release - Build with
pixi run cmake --build build/release -j$(nproc) - Run C++ unit tests under xvfb (Assembly_tests excluded due to discovery timeouts)
- Install to
build/release/install - Run Python CLI tests (
FreeCADCmd -t 0) - Run GUI tests headless (
xvfb-run FreeCAD -t 0) - Package as
.tar.xzwith SHA256 checksum - Upload artifact (14-day retention)
Caching
ccache is persisted between builds using actions/cache. Cache keys are scoped by branch and commit SHA, with fallback to the branch key then main.
Key: ccache-build-{branch}-{sha}
Fallback: ccache-build-{branch}-
Fallback: ccache-build-main-
ccache configuration: 4 GB max, zlib compression level 6, sloppy mode for include timestamps and PCH.
Release workflow (release.yml)
Triggered by pushing a tag matching v* (e.g., v0.1.0, v1.0.0-rc1). Builds release packages for all platforms in parallel, then creates a Gitea release with all artifacts.
Triggering a release
git tag v0.1.0
git push origin v0.1.0
Or manually via workflow_dispatch with a tag input.
Tags containing rc, beta, or alpha are marked as pre-releases.
Platform matrix
| Job | Runner | Container | Preset | Output |
|---|---|---|---|---|
build-linux |
ubuntu-latest |
ubuntu:24.04 |
conda-linux-release |
AppImage, .deb |
build-macos (Intel) |
macos-13 |
native | conda-macos-release |
.dmg (x86_64) |
build-macos (Apple Silicon) |
macos-14 |
native | conda-macos-release |
.dmg (arm64) |
build-windows |
windows-latest |
native | conda-windows-release |
.exe (NSIS), .7z |
All four jobs run concurrently. After all succeed, publish-release collects artifacts and creates the Gitea release.
Linux build
Uses the rattler-build packaging pipeline:
pixi installinpackage/rattler-build/pixi run -e package create_bundle-- invokeslinux/create_bundle.sh- The bundle script:
- Copies the pixi conda environment to an AppDir
- Strips unnecessary files (includes, static libs, cmake files,
__pycache__) - Downloads
appimagetooland creates a squashfs AppImage (zstd compressed) - Generates SHA256 checksums
package/debian/build-deb.shbuilds a .deb from the AppDir- Installs to
/opt/kindred-create/with wrapper scripts in/usr/bin/ - Sets up LD_LIBRARY_PATH, QT_PLUGIN_PATH, PYTHONPATH in wrappers
- Creates desktop entry, MIME types, AppStream metainfo
- Installs to
macOS build
Builds natively on macOS runners (Intel via macos-13, Apple Silicon via macos-14):
pixi run -e package create_bundleinvokesosx/create_bundle.sh- The bundle script:
- Creates
FreeCAD.appbundle with conda environment inContents/Resources/ - Runs
fix_macos_lib_paths.pyto convert absolute rpaths to@loader_path - Builds native macOS launcher from
launcher/CMakeLists.txt - Patches
Info.plistwith version info - Creates DMG via
dmgbuild - If
SIGN_RELEASE=true: signs and notarizes viamacos_sign_and_notarize.zsh
- Creates
Windows build
Builds natively on Windows runner:
pixi run -e package create_bundleinvokeswindows/create_bundle.sh(bash via Git for Windows)- The bundle script:
- Copies conda environment DLLs, Python, and FreeCAD binaries to
FreeCAD_Windows/ - Applies SSL certificate patch (
ssl-patch.py) - Creates
qt6.conffor Qt6 plugin paths - Creates
.exewrapper shims via Chocolateyshimgen - Compresses to
.7z(compression level 9) - If
MAKE_INSTALLER=trueand NSIS available: builds NSIS installer
- Copies conda environment DLLs, Python, and FreeCAD binaries to
- NSIS installer (
package/WindowsInstaller/):- Multi-language support (28 languages)
- File associations for
.FCStd - Start menu and desktop shortcuts
- LZMA compression
Publish step
publish-release runs after all platform builds succeed:
- Downloads all artifacts from
build-linux,build-macos,build-windows - Collects release files (AppImage, .deb, .dmg, .7z, .exe, checksums)
- Creates a Gitea release via
gitea.com/actions/release-action - Requires
RELEASE_TOKENsecret with repository write permissions
Runner configuration
Public runner setup
Workflows target public Gitea-compatible runners. The Linux build job runs inside a Docker container (ubuntu:24.04) for isolation and reproducibility. macOS and Windows jobs run directly on hosted runners.
Required runner labels:
ubuntu-latest-- Linux builds (dockerized)macos-13-- macOS Intel buildsmacos-14-- macOS Apple Silicon buildswindows-latest-- Windows builds
Registering a self-hosted runner
If using self-hosted runners instead of hosted:
# Download the Gitea runner binary
curl -fsSL -o act_runner https://gitea.com/gitea/act_runner/releases/latest/download/act_runner-linux-amd64
chmod +x act_runner
# Register with your Gitea instance
./act_runner register \
--instance https://git.kindred-systems.com \
--token <RUNNER_REGISTRATION_TOKEN> \
--labels ubuntu-latest:docker://ubuntu:24.04
# Start the runner
./act_runner daemon
For dockerized mode, the runner executes jobs inside containers. Ensure Docker is installed on the runner host.
The runner config file (config.yaml) should set:
runner:
labels:
- "ubuntu-latest:docker://ubuntu:24.04"
container:
privileged: false
network: bridge
Secrets
| Secret | Used by | Purpose |
|---|---|---|
RELEASE_TOKEN |
release.yml (publish) |
Gitea API token for creating releases |
SIGNING_KEY_ID |
macOS build (optional) | Apple Developer signing identity |
Build tools
pixi
pixi manages all build dependencies via conda-forge. It ensures consistent toolchains (clang, cmake, Qt6, OpenCASCADE, etc.) across all platforms without relying on system packages.
Key pixi tasks (defined in root pixi.toml):
| Task | Description |
|---|---|
pixi run configure |
CMake configure (defaults to debug) |
pixi run build |
CMake build (defaults to debug) |
pixi run install |
CMake install |
pixi run test |
Run ctest |
pixi run freecad |
Launch built FreeCAD |
pixi run build-release |
CMake build (release) |
CMake presets
Defined in CMakePresets.json. Release builds use:
| Platform | Preset | Compiler | Linker |
|---|---|---|---|
| Linux | conda-linux-release |
clang/clang++ | mold |
| macOS | conda-macos-release |
clang/clang++ | default |
| Windows | conda-windows-release |
MSVC | default |
ccache
Compiler cache is used across all builds to speed up incremental compilation. Cache is persisted between CI runs via actions/cache. Configuration:
- Max size: 4 GB
- Compression: zlib level 6
- Sloppy mode: include timestamps, PCH defines, time macros
Adding a new platform or package format
- Create a bundle script at
package/rattler-build/<platform>/create_bundle.sh - Add the platform to
package/rattler-build/pixi.tomlif needed - Add a new job to
release.ymlfollowing the existing pattern - Add the new artifact pattern to the
publish-releasejob'sfindcommand - Update the release body template with the new download entry
Troubleshooting
Build fails with missing system libraries
The Docker container installs only minimal dependencies. If a new dependency is needed, add it to the apt-get install step in the workflow. Check the pixi environment first -- most dependencies should come from conda-forge, not system packages.
ccache misses are high
ccache misses spike when:
- The compiler version changes (pixi update)
- CMake presets change configuration flags
- The cache key doesn't match (new branch, force-pushed SHA)
Check pixi run ccache -s output for hit/miss ratios.
Submodule checkout fails
Submodules pointing to internal Gitea instances require either:
- Public mirrors of the submodule repositories
- Runner network access to the Gitea instance
- Submodule URLs updated to public-facing addresses in
.gitmodules
macOS signing
Code signing and notarization require:
SIGNING_KEY_IDsecret set to the Apple Developer identitySIGN_RELEASE=trueenvironment variable- Valid Apple Developer account credentials in the runner keychain
Without signing, the DMG is created unsigned. Users will see Gatekeeper warnings.
Windows NSIS installer not created
The NSIS installer requires:
- NSIS 3.x installed in the runner environment (available via conda or Chocolatey)
MAKE_INSTALLER=trueenvironment variable- Chocolatey
shimgen.exefor wrapper executables
If NSIS is unavailable, only the .7z portable archive is produced.