# 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 ```bash 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: ```bash # 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 \ --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: ```yaml 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](https://pixi.sh) 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//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.