Files
create/docs/CI_CD.md
forbes-0023 1b3f780aa1
Some checks failed
Build and Test / build (push) Has been cancelled
chore: migrate submodules to public repos, rework docs and CI/CD
- 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
2026-02-03 10:54:47 -06:00

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

  1. Install system prerequisites (GL, X11, fontconfig headers)
  2. Checkout with recursive submodules
  3. Install pixi
  4. Restore ccache from prior builds
  5. Configure via pixi run cmake --preset conda-linux-release
  6. Build with pixi run cmake --build build/release -j$(nproc)
  7. Run C++ unit tests under xvfb (Assembly_tests excluded due to discovery timeouts)
  8. Install to build/release/install
  9. Run Python CLI tests (FreeCADCmd -t 0)
  10. Run GUI tests headless (xvfb-run FreeCAD -t 0)
  11. Package as .tar.xz with SHA256 checksum
  12. 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:

  1. pixi install in package/rattler-build/
  2. pixi run -e package create_bundle -- invokes linux/create_bundle.sh
  3. The bundle script:
    • Copies the pixi conda environment to an AppDir
    • Strips unnecessary files (includes, static libs, cmake files, __pycache__)
    • Downloads appimagetool and creates a squashfs AppImage (zstd compressed)
    • Generates SHA256 checksums
  4. package/debian/build-deb.sh builds 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

macOS build

Builds natively on macOS runners (Intel via macos-13, Apple Silicon via macos-14):

  1. pixi run -e package create_bundle invokes osx/create_bundle.sh
  2. The bundle script:
    • Creates FreeCAD.app bundle with conda environment in Contents/Resources/
    • Runs fix_macos_lib_paths.py to convert absolute rpaths to @loader_path
    • Builds native macOS launcher from launcher/CMakeLists.txt
    • Patches Info.plist with version info
    • Creates DMG via dmgbuild
    • If SIGN_RELEASE=true: signs and notarizes via macos_sign_and_notarize.zsh

Windows build

Builds natively on Windows runner:

  1. pixi run -e package create_bundle invokes windows/create_bundle.sh (bash via Git for Windows)
  2. The bundle script:
    • Copies conda environment DLLs, Python, and FreeCAD binaries to FreeCAD_Windows/
    • Applies SSL certificate patch (ssl-patch.py)
    • Creates qt6.conf for Qt6 plugin paths
    • Creates .exe wrapper shims via Chocolatey shimgen
    • Compresses to .7z (compression level 9)
    • If MAKE_INSTALLER=true and NSIS available: builds NSIS installer
  3. 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:

  1. Downloads all artifacts from build-linux, build-macos, build-windows
  2. Collects release files (AppImage, .deb, .dmg, .7z, .exe, checksums)
  3. Creates a Gitea release via gitea.com/actions/release-action
  4. Requires RELEASE_TOKEN secret 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 builds
  • macos-14 -- macOS Apple Silicon builds
  • windows-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

  1. Create a bundle script at package/rattler-build/<platform>/create_bundle.sh
  2. Add the platform to package/rattler-build/pixi.toml if needed
  3. Add a new job to release.yml following the existing pattern
  4. Add the new artifact pattern to the publish-release job's find command
  5. 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_ID secret set to the Apple Developer identity
  • SIGN_RELEASE=true environment 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=true environment variable
  • Chocolatey shimgen.exe for wrapper executables

If NSIS is unavailable, only the .7z portable archive is produced.