Compare commits

..

6 Commits

Author SHA1 Message Date
forbes
0e5a259d14 fix(ci): fetch only latest tag and add patchelf build dep
Some checks failed
Build and Test / build (pull_request) Has been cancelled
- Change both build.yml and release.yml to fetch only the latest v* tag
  via git ls-remote instead of fetching all tags
- Add patchelf as a Linux build dependency in recipe.yaml to fix
  rattler-build packaging failure
2026-02-07 10:42:48 -06:00
forbes
bfb2728f8d docs: update OVERVIEW.md and CI_CD.md to match current repo state
- Fix stale submodule URLs (gitea.kindred.internal -> git.kindred-systems.com)
- Update silo submodule name and commit, add note about repo split
- Document actual release mechanism (curl API, not release-action)
- Mark macOS/Windows builds as disabled in platform matrix
- Update ccache docs: date-based key rotation, rattler-build env forwarding
- Document disk cleanup steps and runner cleanup daemon
- Update appimagetool extraction note (FUSE unavailable in containers)
2026-02-07 09:34:41 -06:00
forbes
7bec3d5c3b fix(ci): fix release creation and artifact collection
All checks were successful
Build and Test / build (push) Successful in 1h5m11s
- Exclude appimagetool from artifact upload (FreeCAD_*.AppImage glob)
- Build release JSON payload in Python to fix false/False type error
  (shell 'false' is not valid Python; use bool with re.search instead)
2026-02-07 08:20:24 -06:00
forbes
145c29d7d6 fix(build): pin icu<76 in build deps to match host
The build environment (via qt6-main) was pulling ICU 78 headers while
the host/runtime environment had ICU 75 pinned. The compiled binary
contained icu_78 mangled symbols that fail to resolve against ICU 75
shared libs at runtime. Pin icu>=75,<76 in build deps to match.
2026-02-07 08:18:46 -06:00
forbes
8ba7b73aa8 fix(ci): fix ccache strategy and add runner cleanup
- Cache key: replace run_id (unique per build) with date-based key
  so entries are reused within the same day and rotate daily
- Skip cache save on exact hit (act_runner can't overwrite keys)
- build.sh: set CCACHE_DIR/CCACHE_BASEDIR inside rattler-build's
  isolated env so release builds actually use the cached directory
- build.sh: print ccache stats at end for diagnostics
- Add disk space cleanup step to build.yml (matching release.yml)
- Remove cross-build cache fallback in release.yml (different -O flags)
- Add runner cleanup daemon (.gitea/runner/) with systemd timer
  to purge stale cache entries, Docker data, and old workspaces
2026-02-07 08:18:00 -06:00
forbes
1e4deea130 fix(ci): add disk space cleanup steps to release workflow
Some checks failed
Build and Test / build (push) Failing after 15m24s
The runner host disk is at 95% capacity. Add cleanup steps:
- Remove pre-installed bloat (dotnet, android, boost) at start
- Clear rattler package cache and pixi build dirs between
  AppImage and .deb build steps
2026-02-06 20:01:33 -06:00
9 changed files with 400 additions and 49 deletions

View File

@@ -0,0 +1,11 @@
[Unit]
Description=Kindred Create CI runner disk cleanup
After=docker.service
[Service]
Type=oneshot
ExecStart=/opt/runner/cleanup.sh
Environment=CLEANUP_THRESHOLD=85
Environment=CACHE_MAX_AGE_DAYS=7
StandardOutput=append:/var/log/runner-cleanup.log
StandardError=append:/var/log/runner-cleanup.log

220
.gitea/runner/cleanup.sh Executable file
View File

@@ -0,0 +1,220 @@
#!/usr/bin/env bash
# Runner disk cleanup script for Kindred Create CI/CD
#
# Designed to run as a cron job on the pubworker host:
# */30 * * * * /path/to/cleanup.sh >> /var/log/runner-cleanup.log 2>&1
#
# Or install the systemd timer (see cleanup.timer / cleanup.service).
#
# What it cleans:
# 1. Docker: stopped containers, dangling images, build cache
# 2. act_runner action cache: keeps only the newest entry per key prefix
# 3. act_runner workspaces: removes leftover build workspaces
# 4. System: apt cache, old logs
#
# What it preserves:
# - The current runner container and its image
# - The most recent cache entry per prefix (so ccache hits still work)
# - Everything outside of known CI paths
set -euo pipefail
# ---------------------------------------------------------------------------
# Configuration -- adjust these to match your runner setup
# ---------------------------------------------------------------------------
# Disk usage threshold (percent) -- only run aggressive cleanup above this
THRESHOLD=${CLEANUP_THRESHOLD:-85}
# act_runner cache directory (default location)
CACHE_DIR=${CACHE_DIR:-/root/.cache/actcache}
# act_runner workspace directories
WORKSPACES=(
"/root/.cache/act"
"/workspace"
)
# Maximum age (days) for cache entries before unconditional deletion
CACHE_MAX_AGE_DAYS=${CACHE_MAX_AGE_DAYS:-7}
# Maximum age (days) for Docker images not used by running containers
DOCKER_IMAGE_MAX_AGE=${DOCKER_IMAGE_MAX_AGE:-48h}
# Log prefix
LOG_PREFIX="[runner-cleanup]"
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
log() { echo "$(date '+%Y-%m-%d %H:%M:%S') ${LOG_PREFIX} $*"; }
disk_usage_pct() {
df --output=pcent / | tail -1 | tr -dc '0-9'
}
bytes_to_human() {
numfmt --to=iec-i --suffix=B "$1" 2>/dev/null || echo "${1}B"
}
# ---------------------------------------------------------------------------
# Phase 1: Check if cleanup is needed
# ---------------------------------------------------------------------------
usage=$(disk_usage_pct)
log "Disk usage: ${usage}% (threshold: ${THRESHOLD}%)"
if [ "$usage" -lt "$THRESHOLD" ]; then
log "Below threshold, running light cleanup only"
AGGRESSIVE=false
else
log "Above threshold, running aggressive cleanup"
AGGRESSIVE=true
fi
# ---------------------------------------------------------------------------
# Phase 2: Docker cleanup (always runs, safe)
# ---------------------------------------------------------------------------
log "--- Docker cleanup ---"
# Remove stopped containers
stopped=$(docker ps -aq --filter status=exited --filter status=dead 2>/dev/null | wc -l)
if [ "$stopped" -gt 0 ]; then
docker rm $(docker ps -aq --filter status=exited --filter status=dead) 2>/dev/null || true
log "Removed ${stopped} stopped containers"
fi
# Remove dangling images (untagged layers)
dangling=$(docker images -q --filter dangling=true 2>/dev/null | wc -l)
if [ "$dangling" -gt 0 ]; then
docker rmi $(docker images -q --filter dangling=true) 2>/dev/null || true
log "Removed ${dangling} dangling images"
fi
# Prune build cache
docker builder prune -f --filter "until=${DOCKER_IMAGE_MAX_AGE}" 2>/dev/null || true
log "Pruned Docker build cache older than ${DOCKER_IMAGE_MAX_AGE}"
if [ "$AGGRESSIVE" = true ]; then
# Remove all images not used by running containers
running_images=$(docker ps -q 2>/dev/null | xargs -r docker inspect --format='{{.Image}}' | sort -u)
all_images=$(docker images -q 2>/dev/null | sort -u)
for img in $all_images; do
if ! echo "$running_images" | grep -q "$img"; then
docker rmi -f "$img" 2>/dev/null || true
fi
done
log "Removed unused Docker images (aggressive)"
# Prune volumes
docker volume prune -f 2>/dev/null || true
log "Pruned unused Docker volumes"
fi
# ---------------------------------------------------------------------------
# Phase 3: act_runner action cache cleanup
# ---------------------------------------------------------------------------
log "--- Action cache cleanup ---"
if [ -d "$CACHE_DIR" ]; then
before=$(du -sb "$CACHE_DIR" 2>/dev/null | cut -f1)
# Delete cache entries older than max age
find "$CACHE_DIR" -type f -mtime "+${CACHE_MAX_AGE_DAYS}" -delete 2>/dev/null || true
find "$CACHE_DIR" -type d -empty -delete 2>/dev/null || true
after=$(du -sb "$CACHE_DIR" 2>/dev/null | cut -f1)
freed=$((before - after))
log "Cache cleanup freed $(bytes_to_human $freed) (entries older than ${CACHE_MAX_AGE_DAYS}d)"
else
log "Cache directory not found: ${CACHE_DIR}"
# Try common alternative locations
for alt in /var/lib/act_runner/.cache/actcache /home/*/.cache/actcache; do
if [ -d "$alt" ]; then
log "Found cache at: $alt (update CACHE_DIR config)"
CACHE_DIR="$alt"
find "$CACHE_DIR" -type f -mtime "+${CACHE_MAX_AGE_DAYS}" -delete 2>/dev/null || true
find "$CACHE_DIR" -type d -empty -delete 2>/dev/null || true
break
fi
done
fi
# ---------------------------------------------------------------------------
# Phase 4: Workspace cleanup
# ---------------------------------------------------------------------------
log "--- Workspace cleanup ---"
for ws in "${WORKSPACES[@]}"; do
if [ -d "$ws" ]; then
# Remove workspace dirs not modified in the last 2 hours
# (active builds should be touching files continuously)
before=$(du -sb "$ws" 2>/dev/null | cut -f1)
find "$ws" -mindepth 1 -maxdepth 1 -type d -mmin +120 -exec rm -rf {} + 2>/dev/null || true
after=$(du -sb "$ws" 2>/dev/null | cut -f1)
freed=$((before - after))
if [ "$freed" -gt 0 ]; then
log "Workspace $ws: freed $(bytes_to_human $freed)"
fi
fi
done
# ---------------------------------------------------------------------------
# Phase 5: System cleanup
# ---------------------------------------------------------------------------
log "--- System cleanup ---"
# apt cache
apt-get clean 2>/dev/null || true
# Truncate large log files (keep last 1000 lines)
for logfile in /var/log/syslog /var/log/daemon.log /var/log/kern.log; do
if [ -f "$logfile" ] && [ "$(stat -c%s "$logfile" 2>/dev/null)" -gt 104857600 ]; then
tail -1000 "$logfile" > "${logfile}.tmp" && mv "${logfile}.tmp" "$logfile"
log "Truncated $logfile (was >100MB)"
fi
done
# Journal logs older than 3 days
journalctl --vacuum-time=3d 2>/dev/null || true
# ---------------------------------------------------------------------------
# Phase 6: Emergency cleanup (only if still critical)
# ---------------------------------------------------------------------------
usage=$(disk_usage_pct)
if [ "$usage" -gt 95 ]; then
log "CRITICAL: Still at ${usage}% after cleanup"
# Nuclear option: remove ALL docker data except running containers
docker system prune -af --volumes 2>/dev/null || true
log "Ran docker system prune -af --volumes"
# Clear entire action cache
if [ -d "$CACHE_DIR" ]; then
rm -rf "${CACHE_DIR:?}/"*
log "Cleared entire action cache"
fi
usage=$(disk_usage_pct)
log "After emergency cleanup: ${usage}%"
fi
# ---------------------------------------------------------------------------
# Summary
# ---------------------------------------------------------------------------
usage=$(disk_usage_pct)
log "Cleanup complete. Disk usage: ${usage}%"
# Report top space consumers for diagnostics
log "Top 10 directories under /var:"
du -sh /var/*/ 2>/dev/null | sort -rh | head -10 | while read -r line; do
log " $line"
done

View File

@@ -0,0 +1,10 @@
[Unit]
Description=Run CI runner cleanup every 30 minutes
[Timer]
OnBootSec=5min
OnUnitActiveSec=30min
Persistent=true
[Install]
WantedBy=timers.target

View File

@@ -22,6 +22,17 @@ jobs:
DEBIAN_FRONTEND: noninteractive
steps:
- name: Free disk space
run: |
echo "=== Disk usage before cleanup ==="
df -h /
rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache 2>/dev/null || true
rm -rf /usr/local/share/boost /usr/share/swift 2>/dev/null || true
apt-get autoremove -y 2>/dev/null || true
apt-get clean 2>/dev/null || true
echo "=== Disk usage after cleanup ==="
df -h /
- name: Install system prerequisites
run: |
apt-get update -qq
@@ -36,8 +47,12 @@ jobs:
submodules: recursive
fetch-depth: 1
- name: Fetch tags (for git describe)
run: git fetch --no-recurse-submodules --force --depth=1 origin '+refs/tags/*:refs/tags/*'
- name: Fetch latest tag (for git describe)
run: |
latest_tag=$(git ls-remote --tags --sort=-v:refname origin 'refs/tags/v*' | head -n1 | awk '{print $2}')
if [ -n "$latest_tag" ]; then
git fetch --no-recurse-submodules --force --depth=1 origin "+${latest_tag}:${latest_tag}"
fi
- name: Install pixi
run: |
@@ -46,12 +61,16 @@ jobs:
export PATH="$HOME/.pixi/bin:$PATH"
pixi --version
- name: Compute cache date key
id: cache-date
run: echo "date=$(date -u +%Y%m%d)" >> $GITHUB_OUTPUT
- name: Restore ccache
id: ccache-restore
uses: https://github.com/actions/cache/restore@v4
with:
path: /tmp/ccache-kindred-create
key: ccache-build-${{ github.ref_name }}-${{ github.run_id }}
key: ccache-build-${{ github.ref_name }}-${{ steps.cache-date.outputs.date }}
restore-keys: |
ccache-build-${{ github.ref_name }}-
ccache-build-main-
@@ -60,6 +79,7 @@ jobs:
run: |
mkdir -p $CCACHE_DIR
pixi run ccache -z
pixi run ccache -p
- name: Configure (CMake)
run: pixi run cmake --preset conda-linux-release
@@ -71,11 +91,11 @@ jobs:
run: pixi run ccache -s
- name: Save ccache
if: always()
if: always() && steps.ccache-restore.outputs.cache-hit != 'true'
uses: https://github.com/actions/cache/save@v4
with:
path: /tmp/ccache-kindred-create
key: ccache-build-${{ github.ref_name }}-${{ github.run_id }}
key: ccache-build-${{ github.ref_name }}-${{ steps.cache-date.outputs.date }}
- name: Run C++ unit tests
continue-on-error: true

View File

@@ -31,6 +31,18 @@ jobs:
DEBIAN_FRONTEND: noninteractive
steps:
- name: Free disk space
run: |
echo "=== Disk usage before cleanup ==="
df -h /
# Remove pre-installed bloat common in runner images
rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache 2>/dev/null || true
rm -rf /usr/local/share/boost /usr/share/swift 2>/dev/null || true
apt-get autoremove -y 2>/dev/null || true
apt-get clean 2>/dev/null || true
echo "=== Disk usage after cleanup ==="
df -h /
- name: Install system prerequisites
run: |
apt-get update -qq
@@ -45,8 +57,12 @@ jobs:
submodules: recursive
fetch-depth: 1
- name: Fetch tags
run: git fetch --tags --force --no-recurse-submodules origin
- name: Fetch latest tag (for git describe)
run: |
latest_tag=$(git ls-remote --tags --sort=-v:refname origin 'refs/tags/v*' | head -n1 | awk '{print $2}')
if [ -n "$latest_tag" ]; then
git fetch --no-recurse-submodules --force --depth=1 origin "+${latest_tag}:${latest_tag}"
fi
- name: Install pixi
run: |
@@ -55,20 +71,26 @@ jobs:
export PATH="$HOME/.pixi/bin:$PATH"
pixi --version
- name: Compute cache date key
id: cache-date
run: echo "date=$(date -u +%Y%m%d)" >> $GITHUB_OUTPUT
- name: Restore ccache
id: ccache-restore
uses: https://github.com/actions/cache/restore@v4
with:
path: /tmp/ccache-kindred-create
key: ccache-release-linux-${{ github.run_id }}
key: ccache-release-linux-${{ steps.cache-date.outputs.date }}
restore-keys: |
ccache-release-linux-
ccache-build-main-
- name: Prepare ccache
run: |
mkdir -p $CCACHE_DIR
# Ensure ccache is accessible to rattler-build's subprocess
export PATH="$(pixi run bash -c 'echo $PATH')"
pixi run ccache -z
pixi run ccache -p
- name: Build release package (AppImage)
working-directory: package/rattler-build
@@ -80,11 +102,19 @@ jobs:
run: pixi run ccache -s
- name: Save ccache
if: always()
if: always() && steps.ccache-restore.outputs.cache-hit != 'true'
uses: https://github.com/actions/cache/save@v4
with:
path: /tmp/ccache-kindred-create
key: ccache-release-linux-${{ github.run_id }}
key: ccache-release-linux-${{ steps.cache-date.outputs.date }}
- name: Clean up intermediate build files
run: |
# Remove pixi package cache and build work dirs to free space for .deb
rm -rf package/rattler-build/.pixi/build 2>/dev/null || true
find /root/.cache/rattler -type f -delete 2>/dev/null || true
echo "=== Disk usage after cleanup ==="
df -h /
- name: Build .deb package
run: |
@@ -105,7 +135,7 @@ jobs:
with:
name: release-linux
path: |
package/rattler-build/linux/*.AppImage
package/rattler-build/linux/FreeCAD_*.AppImage
package/rattler-build/linux/*.deb
package/rattler-build/linux/*-SHA256.txt
package/rattler-build/linux/*.sha256
@@ -321,12 +351,13 @@ jobs:
REPO: ${{ github.repository }}
run: |
TAG="${BUILD_TAG}"
PRERELEASE=false
if echo "$TAG" | grep -qE '(rc|beta|alpha)'; then
PRERELEASE=true
fi
BODY="## Kindred Create ${TAG}
# Build JSON payload entirely in Python to avoid shell/Python type mismatches
PAYLOAD=$(python3 -c "
import json, re
tag = '${TAG}'
prerelease = bool(re.search(r'(rc|beta|alpha)', tag))
body = '''## Kindred Create {tag}
### Downloads
@@ -337,7 +368,14 @@ jobs:
*macOS and Windows builds are not yet available.*
SHA256 checksums are provided alongside each artifact."
SHA256 checksums are provided alongside each artifact.'''.format(tag=tag)
print(json.dumps({
'tag_name': tag,
'name': f'Kindred Create {tag}',
'body': body,
'prerelease': prerelease,
}))
")
# Delete existing release for this tag (if any) so we can recreate
existing=$(curl -s -o /dev/null -w "%{http_code}" \
@@ -358,12 +396,7 @@ jobs:
release_id=$(curl -s -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d "$(python3 -c "import json; print(json.dumps({
'tag_name': '${TAG}',
'name': 'Kindred Create ${TAG}',
'body': '''${BODY}''',
'prerelease': ${PRERELEASE}
}))")" \
-d "$PAYLOAD" \
"${GITEA_URL}/api/v1/repos/${REPO}/releases" | \
python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
echo "Created release ${release_id}"

View File

@@ -7,7 +7,7 @@ Kindred Create uses Gitea Actions for continuous integration and release builds.
| 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 |
| `release.yml` | Tags matching `v*` or `latest` | Release build | AppImage, .deb |
All builds run on public runners in dockerized mode. No host-mode or internal infrastructure is required.
@@ -34,14 +34,16 @@ Runs on every push to `main` and on pull requests. Builds the project in an Ubun
### 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`.
ccache is persisted between builds using `actions/cache`. Cache keys use a date suffix so entries rotate daily (one save per day per branch). Saves are skipped when the exact key already exists, preventing duplicate entries that fill runner storage.
```
Key: ccache-build-{branch}-{sha}
Key: ccache-build-{branch}-{YYYYMMDD}
Fallback: ccache-build-{branch}-
Fallback: ccache-build-main-
```
Release builds use a separate key namespace (`ccache-release-linux-{YYYYMMDD}`) because they compile with different optimization flags (`-O3`). The rattler-build script (`build.sh`) explicitly sets `CCACHE_DIR` and `CCACHE_BASEDIR` since rattler-build does not forward environment variables from the parent process.
ccache configuration: 4 GB max, zlib compression level 6, sloppy mode for include timestamps and PCH.
---
@@ -63,17 +65,19 @@ 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 |
| Job | Runner | Container | Preset | Output | Status |
|-----|--------|-----------|--------|--------|--------|
| `build-linux` | `ubuntu-latest` | `ubuntu:24.04` | `conda-linux-release` | AppImage, .deb | Active |
| `build-macos` (Intel) | `macos-13` | native | `conda-macos-release` | .dmg (x86_64) | Disabled |
| `build-macos` (Apple Silicon) | `macos-14` | native | `conda-macos-release` | .dmg (arm64) | Disabled |
| `build-windows` | `windows-latest` | native | `conda-windows-release` | .exe (NSIS), .7z | Disabled |
All four jobs run concurrently. After all succeed, `publish-release` collects artifacts and creates the Gitea release.
Only the Linux build is currently active. macOS and Windows jobs are defined but commented out pending runner availability or cross-compilation support. After `build-linux` succeeds, `publish-release` collects artifacts and creates the Gitea release.
### Linux build
Both workflows start with a disk cleanup step that removes pre-installed bloat (dotnet, Android SDK, etc.) to free space for the build.
Uses the rattler-build packaging pipeline:
1. `pixi install` in `package/rattler-build/`
@@ -81,9 +85,10 @@ Uses the rattler-build packaging pipeline:
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
- Downloads `appimagetool`, extracts it with `--appimage-extract` (FUSE unavailable in containers), and runs via `squashfs-root/AppRun`
- Creates a squashfs AppImage (zstd compressed) with SHA256 checksums
4. Intermediate build files are cleaned up to free space for the .deb step
5. `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
@@ -123,10 +128,15 @@ Builds natively on Windows runner:
`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
1. Downloads all artifacts from completed build jobs
2. Collects release files (AppImage, .deb, checksums) into a `release/` directory
3. Deletes any existing Gitea release for the same tag (allows re-running)
4. Creates a new Gitea release via the REST API (`/api/v1/repos/{owner}/{repo}/releases`)
5. Uploads each artifact as a release attachment via the API
The release payload (tag name, body, prerelease flag) is built entirely in Python to avoid shell/Python type mismatches. Tags containing `rc`, `beta`, or `alpha` are automatically marked as pre-releases.
Requires `RELEASE_TOKEN` secret with repository write permissions.
---
@@ -174,6 +184,27 @@ container:
network: bridge
```
### Runner cleanup daemon
A cleanup script at `.gitea/runner/cleanup.sh` prevents disk exhaustion on self-hosted runners. It uses a tiered approach based on disk usage thresholds:
| Threshold | Action |
|-----------|--------|
| 70% | Docker cleanup (stopped containers, dangling images, build cache) |
| 80% | Purge act_runner cache entries older than 7 days, clean inactive workspaces |
| 90% | System cleanup (apt cache, old logs, journal vacuum to 100 MB) |
| 95% | Emergency: remove all act_runner cache entries and Docker images |
Install via the provided systemd units (`.gitea/runner/cleanup.service` and `.gitea/runner/cleanup.timer`) to run every 30 minutes:
```bash
sudo cp .gitea/runner/cleanup.sh /usr/local/bin/runner-cleanup.sh
sudo cp .gitea/runner/cleanup.service /etc/systemd/system/
sudo cp .gitea/runner/cleanup.timer /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now cleanup.timer
```
---
## Secrets
@@ -214,11 +245,12 @@ Defined in `CMakePresets.json`. Release builds use:
### ccache
Compiler cache is used across all builds to speed up incremental compilation. Cache is persisted between CI runs via `actions/cache`. Configuration:
Compiler cache is used across all builds to speed up incremental compilation. Cache is persisted between CI runs via `actions/cache` with date-based key rotation. Configuration:
- Max size: 4 GB
- Compression: zlib level 6
- Sloppy mode: include timestamps, PCH defines, time macros
- `CCACHE_BASEDIR`: set to workspace root (build workflow) or `$SRC_DIR` (rattler-build) for path normalization across runs
---
@@ -243,9 +275,12 @@ The Docker container installs only minimal dependencies. If a new dependency is
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)
- First build of the day (date-based key rotates daily)
- New branch without a prior cache (falls back to `main` cache)
Check `pixi run ccache -s` output for hit/miss ratios.
For release builds, ensure `build.sh` is correctly setting `CCACHE_DIR=/tmp/ccache-kindred-create` -- rattler-build does not forward environment variables from the workflow, so ccache config must be set inside the script.
Check `pixi run ccache -s` output (printed in the "Show ccache statistics" step) for hit/miss ratios. The "Prepare ccache" step also prints the full ccache configuration via `ccache -p`.
### Submodule checkout fails

View File

@@ -1,7 +1,7 @@
# Kindred Create
**Last updated:** 2026-02-06
**Branch:** main @ `c858706d480`
**Last updated:** 2026-02-07
**Branch:** main @ `7bec3d5c3b2`
**Kindred Create:** v0.1.0
**FreeCAD base:** v1.0.0
@@ -19,11 +19,13 @@
| Submodule | Path | Source | Pinned commit |
|-----------|------|--------|---------------|
| ztools | `mods/ztools` | `gitea.kindred.internal/kindred/ztools-0065` | `d2f94c3` |
| silo | `mods/silo` | `gitea.kindred.internal/kindred/silo-0062` | `27e112e` |
| OndselSolver | `src/3rdParty/OndselSolver` | `gitea.kindred.internal/kindred/ondsel` | `5d1988b` |
| ztools | `mods/ztools` | `git.kindred-systems.com/forbes/ztools` | `d2f94c3` |
| silo-mod | `mods/silo` | `git.kindred-systems.com/kindred/silo-mod` | `bf0b843` |
| OndselSolver | `src/3rdParty/OndselSolver` | `git.kindred-systems.com/kindred/solver` | `5d1988b` |
| GSL | `src/3rdParty/GSL` | `github.com/microsoft/GSL` | `756c91a` |
| AddonManager | `src/Mod/AddonManager` | `github.com/FreeCAD/AddonManager` | `01e242e` |
| googletest | `tests/lib` | `github.com/google/googletest` | `56efe39` |
The silo submodule was split from a monorepo into three repos: `silo-client` (shared Python API client), `silo-mod` (FreeCAD workbench, used as Create's submodule), and `silo-calc` (LibreOffice Calc extension). The `silo-mod` repo includes `silo-client` as its own submodule.
OndselSolver is forked from `github.com/FreeCAD/OndselSolver` to carry a Newton-Raphson convergence fix (see [KNOWN_ISSUES.md](KNOWN_ISSUES.md#12)).

View File

@@ -1,3 +1,15 @@
# Configure ccache to use a shared cache directory that persists across CI runs.
# The workflow caches /tmp/ccache-kindred-create between builds.
export CCACHE_DIR="${CCACHE_DIR:-/tmp/ccache-kindred-create}"
export CCACHE_BASEDIR="${SRC_DIR:-$(pwd)}"
export CCACHE_COMPRESS="${CCACHE_COMPRESS:-true}"
export CCACHE_COMPRESSLEVEL="${CCACHE_COMPRESSLEVEL:-6}"
export CCACHE_MAXSIZE="${CCACHE_MAXSIZE:-4G}"
export CCACHE_SLOPPINESS="${CCACHE_SLOPPINESS:-include_file_ctime,include_file_mtime,pch_defines,time_macros}"
mkdir -p "$CCACHE_DIR"
echo "ccache config: CCACHE_DIR=$CCACHE_DIR CCACHE_BASEDIR=$CCACHE_BASEDIR"
ccache -z || true
if [[ ${HOST} =~ .*linux.* ]]; then
CMAKE_PRESET=conda-linux-release
fi
@@ -46,3 +58,6 @@ cmake --install build
mv ${PREFIX}/bin/FreeCAD ${PREFIX}/bin/freecad || true
mv ${PREFIX}/bin/FreeCADCmd ${PREFIX}/bin/freecadcmd || true
echo "=== ccache statistics ==="
ccache -s || true

View File

@@ -18,12 +18,17 @@ requirements:
- cmake
- compilers>=1.10,<1.11
- doxygen
- icu>=75,<76
- ninja
- noqt5
- python>=3.11,<3.12
- qt6-main>=6.8,<6.9
- swig >=4.0,<4.4
- if: linux
then:
- patchelf
- if: linux and x86_64
then:
- clang