Compare commits

...

93 Commits

Author SHA1 Message Date
0ac0d4dd8e Merge pull request 'chore(git): normalize author identities with .mailmap' (#380) from chore/git-identity-cleanup into main
All checks were successful
Build and Test / build (push) Successful in 30m2s
Reviewed-on: #380
2026-03-03 20:22:13 +00:00
forbes
9ca8809b55 chore(git): normalize author identities with .mailmap
All checks were successful
Build and Test / build (pull_request) Successful in 28m58s
Add .mailmap to parent repo and all submodules (silo, gears, datums,
solver) to normalize historical author/committer display.

Maps stale identities (forbes-0023, josephforbes23, Zoe Forbes, admin)
and old emails to: forbes <contact@kindred-systems.com>
2026-03-03 14:19:20 -06:00
d84049b905 Merge pull request 'docs: comprehensive documentation refresh' (#379) from docs/user-docs-refresh into main
Some checks failed
Deploy Docs / build-and-deploy (push) Failing after 46s
Build and Test / build (push) Has been cancelled
Reviewed-on: #379
2026-03-03 19:56:15 +00:00
forbes
7f02fd182e docs: comprehensive documentation refresh — remove stale references, add missing content
All checks were successful
Build and Test / build (pull_request) Successful in 24m46s
Root documentation:
- README.md: add Datums description, update addon load order and SDK references,
  fix project structure tree, update issue reporting guidance
- CONTRIBUTING.md: update submodule table (remove ztools, add gears/datums/solver),
  fix QSS guidance (single canonical source, not three copies)
- docs/ARCHITECTURE.md: update bootstrap flow (5 addons, split deferred timers
  between Create core and Silo addon), update load order and source layout
- docs/COMPONENTS.md: add Datums and Solver sections, update Gears description,
  fix Silo origin registration reference
- docs/KNOWN_ISSUES.md: create missing file referenced by CLAUDE.md and CONTRIBUTING.md
- docs/INTEGRATION_PLAN.md: update layer 5 diagram, fix load order references,
  update Phase 6 install rules, fix Layer 2/3/5 descriptions
- docs/OVERVIEW.md: add datums submodule entry
- docs/UPSTREAM.md: update Phase 1 directory table and Phase 4 submodule list

mdBook documentation (docs/src/):
- SUMMARY.md: replace dead architecture/ links with existing reference pages,
  remove deleted silo-server files, add new silo-server pages
- introduction.md: rewrite — replace ztools with current addons (Silo, Gears,
  Datums, KCSDK), update version to v0.1.5/FreeCAD 1.2.0
- guide/getting-started.md: update first-run addon list
- guide/installation.md: update verification console output
- guide/workbenches.md: rewrite — replace ztools with Gears, Datums, Solver
- guide/building.md: update submodule table, fix error message guidance
- development/contributing.md: fix scope example and issue reporting
- development/repo-structure.md: rewrite — add SDK, datums, gears, solver,
  reference/ folder; update submodule and key files tables
- development/writing-an-addon.md: fix priority range table
- reference/create-module-bootstrap.md: rewrite — reflect addon_loader system,
  split deferred timers between Create core and Silo addon
- reference/datum-creator.md: update from ZTools to datums addon paths and naming
- reference/glossary.md: add KCSDK entry, update FreeCAD version, remove ztools
  entry, update repository URLs table
2026-03-03 13:52:53 -06:00
ce2da47d5b Merge pull request 'docs: update CLAUDE.md with current repository context' (#378) from docs/claude-md-update into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #378
2026-03-03 19:43:16 +00:00
forbes
5336cde0de docs: update CLAUDE.md with current repository context
All checks were successful
Build and Test / build (pull_request) Successful in 25m3s
- Update project overview: five addon modules, KCSDK C++ layer
- Add KCSDK C++ architecture section (SDK/, IPanelProvider, ThemeEngine, etc.)
- Update repository structure tree (SDK/, datums, gears, reference/ for archived addons)
- Update addon load order: sdk(0) → solver(10) → gears(40) → datums(45) → silo(60)
- Update deferred init: Create core now only handles kc_format + update checker
- Add detailed Gears, Datums, Solver addon module descriptions
- Update Silo section to reflect self-contained deferred setup
- Replace ztools/quicknav references with datums/gears/sdk throughout
- Update submodules table (remove archived, add gears/datums)
- Update commit scopes list
- Update known issues (remove stale ztools references, add archived addons note)
- Point editing context and origin APIs to kindred_sdk wrappers
2026-03-03 13:42:20 -06:00
53f39d6368 Merge pull request 'docs: update server docs' (#377) from fix/rattler-build-submodules into main
Some checks are pending
Deploy Docs / build-and-deploy (push) Successful in 36s
Build and Test / build (push) Has started running
Reviewed-on: #377
2026-03-03 19:36:39 +00:00
forbes
ceaa3acffe docs: update server docs
Some checks failed
Build and Test / build (pull_request) Failing after 2m18s
2026-03-03 13:35:35 -06:00
95142a0b0a Merge pull request 'fix(ci): clone submodule content in rattler-build package' (#376) from fix/rattler-build-submodules into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #376
2026-03-03 14:30:16 +00:00
79e2929cc1 Merge pull request 'chore(datums): convert datums addon to submodule' (#375) from chore/datums-submodule into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #375
2026-03-03 14:29:47 +00:00
forbes
784d172a61 fix(ci): clone submodule content in rattler-build package
Some checks failed
Build and Test / build (pull_request) Has been cancelled
rattler-build's source.path copy does not include submodule contents —
directories like mods/silo/, mods/solver/, mods/gears/, and mods/datums/
end up empty in the work directory, causing cmake --install to fail
when it tries to install files from those paths.

Add a clone_if_empty helper to build.sh that detects empty submodule
directories and shallow-clones them from their remotes before CMake
runs. Add git to build requirements in recipe.yaml.

This fixes the release build (AppImage) failure where cmake --install
failed at the silo addon install step after successfully installing
the silo tree-node icons from src/Mod/Create/resources/.
2026-03-03 08:28:17 -06:00
forbes
3f688873c6 chore(datums): convert datums addon to submodule
Some checks failed
Build and Test / build (pull_request) Has been cancelled
Convert mods/datums/ from tracked files to a git submodule pointing
to https://git.kindred-systems.com/kindred/datums.git (branch: main).

Follows the same submodule pattern as silo, solver, and gears.
The datums remote repo was initialized from the existing tracked files
with the package.xml repository URL updated to point to the new repo.

CMake install targets in src/Mod/Create/CMakeLists.txt continue to
work unchanged since the files remain at the same paths.
2026-03-03 08:27:31 -06:00
c60b4dbee1 Merge pull request 'chore(silo): add root package.xml and migrate setup to silo addon (#373)' (#374) from chore/silo-sdk-standardize into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #374
2026-03-03 14:25:45 +00:00
forbes
4d94857883 chore(silo): add root package.xml and migrate setup to silo addon (#373)
Some checks failed
Build and Test / build (pull_request) Has been cancelled
Update silo submodule to include root-level package.xml, Init.py, and
InitGui.py following the standard addon layout used by sdk, gears,
datums, and solver.

Move five Silo-specific deferred setup functions from
src/Mod/Create/InitGui.py into the silo addon's own InitGui.py:
- Document observer registration (600ms)
- Auth dock panel via kindred_sdk.register_dock_panel() (2000ms)
- First-start settings check (3000ms)
- Activity dock panel via kindred_sdk.register_dock_panel() (4000ms)
- Remove duplicate origin registration (was 1500ms, already done in
  SiloWorkbench.Initialize())

Update CMake install targets to include root-level silo files.

Create core InitGui.py now only handles kc_format (500ms) and
update_checker (10000ms).
2026-03-03 08:24:45 -06:00
4e1e2ca46d Merge pull request 'fix(silo): pull assembly dependencies on all open paths (#337)' (#371) from fix/silo-assembly-deps into main
Some checks failed
Build and Test / build (push) Failing after 1m42s
Sync Silo Server Docs / sync (push) Successful in 35s
Reviewed-on: #371
2026-03-02 19:22:29 +00:00
0a86a93e1b fix(silo): update silo submodule — pull assembly deps on all open paths (#337)
Some checks failed
Build and Test / build (pull_request) Failing after 1m47s
2026-03-02 13:14:10 -06:00
10dd66fc29 Merge pull request 'feat(datums): add unified datum creator addon' (#370) from feat/unified-datum-creator into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #370
2026-03-02 19:04:08 +00:00
177a5eec67 Merge pull request 'docs: remove stale documentation and update for post-ztools architecture' (#369) from docs/stale-doc-cleanup into main
Some checks failed
Build and Test / build (push) Has been cancelled
Deploy Docs / build-and-deploy (push) Successful in 43s
Reviewed-on: #369
2026-03-02 19:03:50 +00:00
e160b07b00 Merge pull request 'feat(sdk): KCSDK v1.0.0 — complete SDK implementation (#346)' (#366) from feat/kcsdk-finalize into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #366
2026-03-02 19:03:15 +00:00
470e6f3cf6 Merge remote-tracking branch 'origin/main' into feat/kcsdk-finalize
All checks were successful
Build and Test / build (pull_request) Successful in 29m7s
# Conflicts:
#	mods/sdk/kindred_sdk/context.py
#	mods/sdk/kindred_sdk/dock.py
#	mods/sdk/kindred_sdk/menu.py
#	mods/sdk/kindred_sdk/origin.py
#	mods/sdk/kindred_sdk/toolbar.py
2026-03-02 12:32:48 -06:00
e3de2c0e71 feat(datums): add unified datum creator addon
All checks were successful
Build and Test / build (pull_request) Successful in 29m22s
Introduces mods/datums/ — a standalone addon that replaces the three
stock PartDesign datum commands (Plane, Line, Point) with a single
unified Create_DatumCreator command.

Features:
- 16 smart creation modes (7 plane, 4 axis, 5 point)
- Auto-detection engine: selects best mode from geometry selection
- Mode override combo for manual selection
- Dynamic parameter UI (offset, angle, position, XYZ)
- Datums_Type/Params/SourceRefs metadata for edit panel
- Edit panel with real-time parameter updates via AttachExtension
- Catppuccin Mocha themed plane styling via SDK theme tokens
- Injected into partdesign.body and partdesign.feature contexts

Also adds CMake install targets for gears and datums addons.

Ported from archived ztools with key changes:
- Property prefix: ZTools_ -> Datums_
- No document-level datums (Body-only)
- PySide -> PySide6
- SDK integration (register_command, inject_commands, get_theme_tokens)
2026-03-02 12:15:18 -06:00
de1b79255c docs: remove stale documentation and update for post-ztools architecture
All checks were successful
Build and Test / build (pull_request) Successful in 28m56s
Remove critically stale docs:
- docs/REPOSITORY_STATE.md — snapshot from 2026-01-31, all data outdated
- docs/MULTI_USER_CLIENT.md — duplicate of DAG_CLIENT_INTEGRATION.md
- docs/src/guide/ztools.md — describes removed addon
- docs/KNOWN_ISSUES.md — replaced by Gitea issue tracker

Update docs for ztools removal (#344), gears addition, and KCSDK progress:
- OVERVIEW.md: fix version (v0.1.5), FreeCAD base (1.2.0), submodule table
- ARCHITECTURE.md: replace ztools with gears in bootstrap flow and source layout
- COMPONENTS.md: remove ztools section, add gears, remove stale missing-icons note
- INTEGRATION_PLAN.md: mark Phase 7 #353-#356 as DONE, update Layer 5 diagram
- README.md: update project structure, load order, feature descriptions
- CI_CD.md: add context for disabled macOS/Windows builds (#164)
- SUMMARY.md: remove ztools guide from mdBook navigation
2026-03-02 11:48:37 -06:00
b0621f9731 Merge pull request 'chore: native repository conversion — remove FreeCAD/GitHub artifacts (#367)' (#368) from chore/repo-cleanup into main
All checks were successful
Build and Test / build (push) Successful in 28m57s
Reviewed-on: #368
2026-03-02 14:08:39 +00:00
forbes
1b01762a1c chore: rebrand XDGData and icon installs to Kindred Create (#367)
All checks were successful
Build and Test / build (pull_request) Successful in 29m26s
- Replace org.freecad.FreeCAD.desktop with kindred-create.desktop
- Replace org.freecad.FreeCAD.metainfo.xml.in with Kindred Create
  metainfo (new description, Kindred URLs, kindred-create app ID)
- Replace org.freecad.FreeCAD.xml with kindred-create.xml (adds .kc
  MIME type alongside .fcstd)
- Replace FreeCAD.thumbnailer.in with kindred-create.thumbnailer.in
- Update XDGData/CMakeLists.txt for new filenames
- Update src/Gui/CMakeLists.txt icon installs: use kindred-create
  icons from resources/ (all sizes 16-512 + SVG) instead of renaming
  freecad-icon-*.png to org.freecad.FreeCAD.png
- Update freecad-thumbnailer.in default icon to kindred-create.png
- Update AppImage create_bundle.sh to use kindred-create desktop/icon

Remaining org.freecad.FreeCAD references are macOS bundle IDs
(MainWindow.cpp, MacAppBundle/) and packaging fallbacks — separate
rebranding tasks.
2026-03-02 08:06:36 -06:00
forbes
fa9c9ba761 chore: native repository conversion — remove FreeCAD/GitHub artifacts (#367)
All checks were successful
Build and Test / build (pull_request) Successful in 29m36s
- Delete .github/ directory (60 files, ~4600 lines of unused GitHub
  Actions workflows, FreeCAD-branded issue/PR templates, problem
  matchers, scripts, and images)
- Delete .packit.yaml (Fedora COPR packaging, unused)
- Delete SECURITY.md (FreeCAD-specific security policy)
- Update .pre-commit-config.yaml to remove .github file pattern
- Update .gitignore to remove .github/ whitelist
- Rewrite CODE_OF_CONDUCT.md for Kindred Create community
- Rewrite PRIVACY_POLICY.md covering Silo integration, update
  checker, origin system, and local storage

Gitea equivalents (.gitea/workflows/, .gitea/ISSUE_TEMPLATE/,
.gitea/pull_request_template.md) are already in place.

Closes #367
2026-03-02 08:01:38 -06:00
forbes
79f69e2856 feat(sdk): remove fallbacks, add deprecation warnings, bump v1.0.0 (#357)
All checks were successful
Build and Test / build (pull_request) Successful in 28m54s
- Bump SDK_VERSION to 1.0.0 in version.py and package.xml
- Remove <pure_python> tag from package.xml (kcsdk is C++)
- Remove all FreeCADGui.* fallback paths in context.py, dock.py,
  toolbar.py, menu.py; require kcsdk module
- Remove query fallbacks in origin.py (keep register/unregister
  which still need FreeCADGui.addOrigin/removeOrigin)
- Add deprecation warnings to 11 superseded FreeCADGui methods
  in ApplicationPy.cpp (not addOrigin/removeOrigin)
- All 39 Tier 1 tests pass
2026-03-01 14:42:00 -06:00
forbes
a6a6cefc16 feat(sdk): add status bar widget wrapper and origin query bindings (#356)
register_status_widget(): pure Python wrapper that adds a live widget
to the main window status bar with context menu discoverability.

Origin query bindings (kcsdk.list_origins, active_origin, get_origin,
set_active_origin): thin C++ forwarding to OriginManager with Python
wrappers using kcsdk-first routing.

IOriginProvider and IStatusBarProvider C++ interfaces dropped — existing
FileOrigin stack is already complete, and status bar widgets don't need
C++ lifecycle management.
2026-03-01 14:42:00 -06:00
forbes
747c458e23 feat(sdk): add IMenuProvider interface and register_command wrapper (#355)
IMenuProvider: declarative menu placement with optional context awareness.
C++ interface with pybind11 bindings + GIL-safe holder. SDKMenuManipulator
(shared WorkbenchManipulator) injects menu items on workbench switch,
filtered by editing context when context_ids() is non-empty.

register_command(): thin Python wrapper around FreeCADGui.addCommand()
that standardizes the calling convention within the SDK contract.

Python wrappers (kindred_sdk.register_menu, kindred_sdk.register_command)
use kcsdk-first routing with FreeCADGui fallback.
2026-03-01 14:42:00 -06:00
forbes
4eb643a26f feat(sdk): add IToolbarProvider interface to kcsdk (#354) 2026-03-01 14:42:00 -06:00
forbes
ab4054eb9e docs(sdk): add KCSDK API reference and addon developer guide
- docs/src/reference/kcsdk-python.md: full kcsdk Python API reference
- docs/src/development/writing-an-addon.md: step-by-step addon guide
- docs/INTEGRATION_PLAN.md: add Phase 7 KCSDK section
- docs/ARCHITECTURE.md: add src/Gui/SDK/ to source layout
- docs/src/SUMMARY.md: add new pages to mdBook navigation
2026-03-01 14:42:00 -06:00
forbes
5f8557fc83 fix(editing-context): resolve initial context on construction
EditingContextResolver constructor did not call refresh(), leaving
d->current as a default empty EditingContext. When BreadcrumbToolBar
queried currentContext() on creation, it received an empty context
with no breadcrumb segments, causing the navbar to appear blank.

Add refresh() at end of constructor so the initial state is resolved
before any View3DInventor queries it.
2026-03-01 14:42:00 -06:00
forbes
64644eb623 feat(sdk): add panel provider and theme engine to kcsdk (#352, #353)
- IPanelProvider: abstract interface for dock panels with PySide widget bridging
- PyIPanelProvider/PyProviderHolder: pybind11 trampoline + GIL-safe holder
- WidgetBridge: PySide QWidget → C++ QWidget* conversion via Shiboken
- SDKRegistry: panel registration, creation, and lifecycle management
- ThemeEngine: C++ singleton with minimal YAML parser, palette cache,
  getColor/allTokens/formatQss matching Python Palette API
- kcsdk bindings: DockArea, PanelPersistence enums, panel functions,
  theme_color, theme_tokens, format_qss, load_palette
- dock.py: kcsdk delegation with FreeCADGui fallback
- theme.py: kcsdk delegation with Python YAML fallback
2026-03-01 14:42:00 -06:00
forbes
60c0489d73 feat(sdk): migrate editing context API to kcsdk (#351)
Add context/overlay registration, injection, query, and refresh to the
KCSDK C++ library and kcsdk pybind11 module.

New files:
- src/Gui/SDK/Types.h — ContextDef, OverlayDef, ContextSnapshot structs
  (plain C++, no Qt in public API)

Modified:
- src/Gui/SDK/SDKRegistry.h/.cpp — register_context/overlay, unregister,
  inject_commands, current_context, refresh (delegates to
  EditingContextResolver with std↔Qt conversion)
- src/Gui/SDK/CMakeLists.txt — add Types.h, link FreeCADGui
- src/Gui/SDK/bindings/kcsdk_py.cpp — bind all context functions with
  GIL-safe match callable wrapping and dict-based snapshot return
- mods/sdk/kindred_sdk/context.py — try kcsdk first, fall back to
  FreeCADGui for backwards compatibility
2026-03-01 14:42:00 -06:00
forbes
63e591a626 feat(sdk): scaffold KCSDK library + kcsdk pybind11 module (#350)
Add the KCSDK C++ shared library and kcsdk pybind11 module, establishing
the build infrastructure for the C++-backed addon SDK.

New files:
- src/Gui/SDK/KCSDKGlobal.h — DLL export macros
- src/Gui/SDK/SDKRegistry.h/.cpp — empty singleton with API_VERSION_MAJOR=1
- src/Gui/SDK/CMakeLists.txt — builds KCSDK shared library
- src/Gui/SDK/bindings/kcsdk_py.cpp — pybind11 module exposing version + available()
- src/Gui/SDK/bindings/CMakeLists.txt — builds kcsdk pybind11 module

Modified:
- src/Gui/CMakeLists.txt — add_subdirectory(SDK)

Verified: import kcsdk; kcsdk.API_VERSION_MAJOR == 1; kcsdk.available() == []
2026-03-01 14:42:00 -06:00
9778d0dd1b Merge pull request 'feat(sdk): add status bar widget wrapper and origin query bindings (#356)' (#364) from feat/gears-addon into main
All checks were successful
Build and Test / build (push) Successful in 29m22s
Sync Silo Server Docs / sync (push) Successful in 37s
Reviewed-on: #364
2026-03-01 20:16:45 +00:00
forbes
ab8519c272 feat(sdk): add status bar widget wrapper and origin query bindings (#356)
All checks were successful
Build and Test / build (pull_request) Successful in 29m13s
register_status_widget(): pure Python wrapper that adds a live widget
to the main window status bar with context menu discoverability.

Origin query bindings (kcsdk.list_origins, active_origin, get_origin,
set_active_origin): thin C++ forwarding to OriginManager with Python
wrappers using kcsdk-first routing.

IOriginProvider and IStatusBarProvider C++ interfaces dropped — existing
FileOrigin stack is already complete, and status bar widgets don't need
C++ lifecycle management.
2026-03-01 14:13:31 -06:00
ec5b3850e9 Merge pull request 'feat(sdk): add IMenuProvider interface and register_command wrapper (#355)' (#363) from feat/gears-addon into main
All checks were successful
Build and Test / build (push) Successful in 29m14s
Reviewed-on: #363
2026-03-01 19:39:11 +00:00
forbes
2bf083609d feat(sdk): add IMenuProvider interface and register_command wrapper (#355)
All checks were successful
Build and Test / build (pull_request) Successful in 28m41s
IMenuProvider: declarative menu placement with optional context awareness.
C++ interface with pybind11 bindings + GIL-safe holder. SDKMenuManipulator
(shared WorkbenchManipulator) injects menu items on workbench switch,
filtered by editing context when context_ids() is non-empty.

register_command(): thin Python wrapper around FreeCADGui.addCommand()
that standardizes the calling convention within the SDK contract.

Python wrappers (kindred_sdk.register_menu, kindred_sdk.register_command)
use kcsdk-first routing with FreeCADGui fallback.
2026-03-01 13:13:44 -06:00
3759f707cf Merge pull request 'feat/gears-addon' (#362) from feat/gears-addon into main
All checks were successful
Build and Test / build (push) Successful in 28m30s
Reviewed-on: #362
2026-03-01 15:37:17 +00:00
forbes
76e448d0d7 feat(sdk): add IToolbarProvider interface to kcsdk (#354)
All checks were successful
Build and Test / build (pull_request) Successful in 30m28s
2026-03-01 09:32:25 -06:00
forbes
51f7635ceb chore: update silo and solver submodules 2026-03-01 09:31:59 -06:00
forbes
ecdc9c2067 chore: set gears submodule default branch to main 2026-03-01 09:31:46 -06:00
153f75e6e7 Merge pull request 'feat: add gears workbench as default addon' (#361) from feat/gears-addon into main
All checks were successful
Build and Test / build (push) Successful in 30m15s
Sync Silo Server Docs / sync (push) Successful in 38s
Reviewed-on: #361
2026-02-28 23:42:12 +00:00
forbes
04065e83f3 feat: add gears workbench as default addon
All checks were successful
Build and Test / build (pull_request) Successful in 29m39s
Add kindred/gears as a submodule under mods/gears. The gears workbench
provides 13 parametric gear commands (involute, cycloid, bevel, worm,
crown, timing, lantern, hypocycloid gears + connectors).

Integration:
- Kindred package.xml with load_priority 40 (sdk -> solver -> gears -> silo)
- Init.py adds repo root to sys.path for pygears + namespace package
- InitGui.py imports upstream GearWorkbench and injects gear commands
  into partdesign.body and partdesign.feature editing contexts
- context.py kcsdk routing already complete from #351
2026-02-28 15:17:06 -06:00
abfd939d2c Merge pull request 'feat(sdk): KCSDK panel provider, theme engine, and breadcrumb fix (#352, #353)' (#360) from feat/kcsdk-scaffold into main
All checks were successful
Deploy Docs / build-and-deploy (push) Successful in 50s
Build and Test / build (push) Successful in 29m40s
Reviewed-on: #360
2026-02-28 21:00:56 +00:00
forbes
5a0be2804d docs(sdk): add KCSDK API reference and addon developer guide
All checks were successful
Build and Test / build (pull_request) Successful in 29m17s
- docs/src/reference/kcsdk-python.md: full kcsdk Python API reference
- docs/src/development/writing-an-addon.md: step-by-step addon guide
- docs/INTEGRATION_PLAN.md: add Phase 7 KCSDK section
- docs/ARCHITECTURE.md: add src/Gui/SDK/ to source layout
- docs/src/SUMMARY.md: add new pages to mdBook navigation
2026-02-28 14:53:51 -06:00
forbes
1b38d7b24b fix(editing-context): resolve initial context on construction
EditingContextResolver constructor did not call refresh(), leaving
d->current as a default empty EditingContext. When BreadcrumbToolBar
queried currentContext() on creation, it received an empty context
with no breadcrumb segments, causing the navbar to appear blank.

Add refresh() at end of constructor so the initial state is resolved
before any View3DInventor queries it.
2026-02-28 14:53:44 -06:00
forbes
18532e3bd7 feat(sdk): add panel provider and theme engine to kcsdk (#352, #353)
- IPanelProvider: abstract interface for dock panels with PySide widget bridging
- PyIPanelProvider/PyProviderHolder: pybind11 trampoline + GIL-safe holder
- WidgetBridge: PySide QWidget → C++ QWidget* conversion via Shiboken
- SDKRegistry: panel registration, creation, and lifecycle management
- ThemeEngine: C++ singleton with minimal YAML parser, palette cache,
  getColor/allTokens/formatQss matching Python Palette API
- kcsdk bindings: DockArea, PanelPersistence enums, panel functions,
  theme_color, theme_tokens, format_qss, load_palette
- dock.py: kcsdk delegation with FreeCADGui fallback
- theme.py: kcsdk delegation with Python YAML fallback
2026-02-28 14:53:38 -06:00
9fc0d064f8 Merge pull request 'feat(sdk): migrate editing context API to kcsdk (#351)' (#359) from feat/kcsdk-scaffold into main
All checks were successful
Build and Test / build (push) Successful in 29m31s
Reviewed-on: #359
2026-02-28 18:50:54 +00:00
81a2e75477 Merge branch 'main' into feat/kcsdk-scaffold
All checks were successful
Build and Test / build (pull_request) Successful in 30m14s
2026-02-28 18:50:42 +00:00
forbes
f44aba7388 feat(sdk): migrate editing context API to kcsdk (#351)
All checks were successful
Build and Test / build (pull_request) Successful in 29m14s
Add context/overlay registration, injection, query, and refresh to the
KCSDK C++ library and kcsdk pybind11 module.

New files:
- src/Gui/SDK/Types.h — ContextDef, OverlayDef, ContextSnapshot structs
  (plain C++, no Qt in public API)

Modified:
- src/Gui/SDK/SDKRegistry.h/.cpp — register_context/overlay, unregister,
  inject_commands, current_context, refresh (delegates to
  EditingContextResolver with std↔Qt conversion)
- src/Gui/SDK/CMakeLists.txt — add Types.h, link FreeCADGui
- src/Gui/SDK/bindings/kcsdk_py.cpp — bind all context functions with
  GIL-safe match callable wrapping and dict-based snapshot return
- mods/sdk/kindred_sdk/context.py — try kcsdk first, fall back to
  FreeCADGui for backwards compatibility
2026-02-27 14:05:07 -06:00
169c177b69 Merge pull request 'feat(sdk): scaffold KCSDK library + kcsdk pybind11 module (#350)' (#358) from feat/kcsdk-scaffold into main
All checks were successful
Sync Silo Server Docs / sync (push) Successful in 1m3s
Build and Test / build (push) Successful in 29m51s
Reviewed-on: #358
2026-02-27 19:51:37 +00:00
forbes
9b28feb8ca feat(sdk): scaffold KCSDK library + kcsdk pybind11 module (#350)
All checks were successful
Build and Test / build (pull_request) Successful in 29m2s
Add the KCSDK C++ shared library and kcsdk pybind11 module, establishing
the build infrastructure for the C++-backed addon SDK.

New files:
- src/Gui/SDK/KCSDKGlobal.h — DLL export macros
- src/Gui/SDK/SDKRegistry.h/.cpp — empty singleton with API_VERSION_MAJOR=1
- src/Gui/SDK/CMakeLists.txt — builds KCSDK shared library
- src/Gui/SDK/bindings/kcsdk_py.cpp — pybind11 module exposing version + available()
- src/Gui/SDK/bindings/CMakeLists.txt — builds kcsdk pybind11 module

Modified:
- src/Gui/CMakeLists.txt — add_subdirectory(SDK)

Verified: import kcsdk; kcsdk.API_VERSION_MAJOR == 1; kcsdk.available() == []
2026-02-27 13:36:48 -06:00
fcb5c2b00f Merge pull request 'chore: archive QuickNav and ZTools into reference folder (#345)' (#349) from chore/archive-addons-reference into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #349
2026-02-27 18:59:37 +00:00
forbes
c8b0706a1d chore: archive QuickNav and ZTools into reference folder (#345)
All checks were successful
Build and Test / build (pull_request) Successful in 32m17s
Copy QuickNav and ZTools source trees into reference/ for developer
reference during the UI/UX rework. These are plain directories (not
submodules) and are not included in the build.

- reference/quicknav/ — QuickNav addon source
- reference/ztools/ — ZTools addon source

Part of the UI/UX rework preparation. See #346.
2026-02-27 12:54:40 -06:00
a623f280da Merge pull request 'chore: remove ZTools addon from build and loader (#344)' (#348) from chore/remove-ztools into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #348
2026-02-27 18:51:53 +00:00
forbes
485f69f257 chore: remove ZTools addon from build and loader (#344)
All checks were successful
Build and Test / build (pull_request) Successful in 29m28s
- Remove ZTools install block from src/Mod/Create/CMakeLists.txt
- Remove mods/ztools submodule entry from .gitmodules
- Remove 'ztools' from legacy fallback order in addon_loader.py
- Remove ztools imports and test classes from test_kindred_pure.py
  (TestTypeMatches, TestMatchScore, TestSelectionItemProperties,
   TestColumnToIndex, TestDatumModes)
- Remove 'ztools Workbench' from issue template component lists
- Remove mods/ztools submodule from git tracking

ZTools will be archived to a reference folder in a separate step (#345).
This is part of the UI/UX rework epic (#346).
2026-02-27 12:46:32 -06:00
a32f405990 Merge pull request 'chore: remove QuickNav addon from build and loader (#343)' (#347) from chore/remove-quicknav into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #347
2026-02-27 18:29:18 +00:00
forbes
d47c94af8d chore: remove QuickNav addon from build and loader (#343)
All checks were successful
Build and Test / build (pull_request) Successful in 30m57s
- Remove QuickNav install block from src/Mod/Create/CMakeLists.txt
- Remove mods/quicknav submodule entry from .gitmodules
- Remove quicknav imports and TestWorkbenchMap tests from test_kindred_pure.py
- Remove mods/quicknav submodule from git tracking

QuickNav will be archived to a reference folder in a separate step (#345).
This is part of the UI/UX rework epic (#346).
2026-02-27 12:27:33 -06:00
b1d75fa237 Merge pull request 'fix(assembly): enforce drag quaternion continuity + short-arc validation (#338)' (#342) from fix/drag-quat-continuity into main
All checks were successful
Build and Test / build (push) Successful in 29m40s
Reviewed-on: #342
2026-02-27 16:41:57 +00:00
forbes
d174ef7a8d fix(assembly): use short-arc angle in validateNewPlacements (#338)
All checks were successful
Build and Test / build (pull_request) Successful in 30m5s
Rotation::evaluateVector() computes angle = 2*acos(w) which gives
values in [0, 2*pi].  When the relative quaternion has w < 0 (opposite
hemisphere), the angle exceeds pi even though q and -q represent the
same rotation.  This caused the validator to report ~350 degree 'flips'
and reject valid solver output.

Fix: map the angle to [0, pi] before comparing against the 91-degree
threshold.  This is the short-arc equivalent — the minimum rotation
angle between two orientations regardless of quaternion sign convention.
2026-02-27 10:38:37 -06:00
bc157ddb72 Merge pull request 'fix(assembly): update solver submodule — enforce drag quaternion continuity (#338)' (#341) from fix/drag-quat-continuity into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #341
2026-02-27 15:41:19 +00:00
forbes
07a51aefb1 fix(assembly): update solver submodule — enforce drag quaternion continuity (#338)
Some checks failed
Build and Test / build (pull_request) Has been cancelled
Points solver to fix/drag-quat-continuity (solver#40) which fixes
the planar constraint drift during interactive drag by enforcing
quaternion continuity on dragged parts and detecting branch jumps
beyond simple hemisphere negation.
2026-02-27 09:40:51 -06:00
9f644294c5 Merge pull request 'chore: update solver submodule — add planar drag console test (#338)' (#340) from test/planar-drag-console-test into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #340
2026-02-27 15:32:03 +00:00
forbes
983e211f12 chore: update solver submodule — add planar drag console test (#338)
Some checks failed
Build and Test / build (pull_request) Has been cancelled
2026-02-27 09:31:31 -06:00
forbes
1788b5778a Add CLAUDE.md to .gitignore
All checks were successful
Build and Test / build (push) Successful in 29m54s
2026-02-27 08:19:52 -06:00
418e947cbd Merge pull request 'fix(solver): world-anchored reference normal prevents planar axial drift' (#336) from fix/planar-halfspace-drag-flip into main
All checks were successful
Build and Test / build (push) Successful in 29m17s
Sync Silo Server Docs / sync (push) Successful in 34s
Reviewed-on: #336
2026-02-26 17:10:39 +00:00
4c9ff957e3 Merge branch 'main' into fix/planar-halfspace-drag-flip
All checks were successful
Build and Test / build (pull_request) Successful in 28m48s
2026-02-26 17:10:27 +00:00
9aaf244179 fix(solver): update solver submodule — world-anchored planar reference normal
All checks were successful
Build and Test / build (pull_request) Successful in 29m11s
Updates solver to include the fix for PlanarConstraint axial drift
when combined with CylindricalConstraint. The distance residual now
uses a world-frame reference normal (Const nodes) instead of the
body-attached normal that rotates with the body.
2026-02-26 11:07:06 -06:00
69ccdbf742 Merge pull request 'fix(assembly): use instance suffixes for duplicate part labels' (#335) from fix/assembly-part-number-suffix into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #335
2026-02-26 16:34:34 +00:00
4acd09171e Merge pull request 'fix(theme): prevent panel element headings from being clipped' (#334) from fix/theme-panel-heading-clipping into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #334
2026-02-26 16:34:17 +00:00
7f909f166f Merge pull request 'fix(ci): add target_commitish to release payload to fix HTTP 500' (#333) from fix/ci-release-target-commitish into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #333
2026-02-26 16:34:04 +00:00
f69e0efec7 Merge pull request 'docs: add CLAUDE.md for developer context' (#332) from docs/claude-md into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #332
2026-02-26 16:33:36 +00:00
forbes
559a240799 fix(assembly): update solver submodule — fix planar half-space drag flip
Some checks failed
Build and Test / build (pull_request) Has been cancelled
Updates mods/solver to include fix for the planar half-space correction
that caused 'flipped orientation' rejections when dragging a body
connected by a Cylindrical joint + distance=0 Planar constraint.

The solver's PlanarConstraint half-space tracker was reflecting the body
through the plane when the face normal dot product crossed zero during
legitimate rotation about the cylindrical axis. Now returns a tracking-
only HalfSpace (no correction) for on-plane constraints, matching the
pattern used by Cylindrical/Revolute/Concentric trackers.

See: kindred/solver#38
2026-02-26 09:04:14 -06:00
7c85b2ad93 fix(assembly): use instance suffixes for duplicate part labels
All checks were successful
Build and Test / build (pull_request) Successful in 29m11s
When parts with structured part numbers (e.g., P03-0001) are inserted
into an assembly multiple times, UniqueNameManager::decomposeName()
treats the trailing digits as an auto-generated suffix and increments
them (P03-0002, P03-0003), corrupting the part number.

Add a makeInstanceLabel() helper in AssemblyLink.cpp that appends -N
instance suffixes instead (P03-0001-1, P03-0001-2). All instances get
a suffix starting at -1 so the original part number is never modified.

Applied at all three Label.setValue() sites in
synchronizeComponents() (AssemblyLink, link group, and regular link
creation paths).

Also add a UniqueNameManager test documenting the trailing-digit
decomposition behavior for structured part numbers.

Closes #327
2026-02-26 09:00:13 -06:00
311d911cfa fix(theme): prevent panel element headings from being clipped
All checks were successful
Build and Test / build (pull_request) Successful in 29m23s
Three QSS issues caused headings to render with only the top ~60%
visible:

- QGroupBox: margin-top 12px was insufficient for the title rendered
  in subcontrol-origin: margin. Increased to 16px and added 2px
  vertical padding to the title.
- QDockWidget::title: min-height 18px conflicted with padding 8px 6px,
  constraining the content area. Removed min-height to let Qt auto-size
  from padding + font metrics.
- QSint--ActionGroup QToolButton: min-height 18px forced a height that
  was then clipped by the C++ setFixedHeight(headerSize) calculation.
  Set min-height to 0px so the C++ layout controls sizing.

Closes #325
2026-02-26 08:48:01 -06:00
4ef8e64a7c fix(ci): add target_commitish to release payload to fix HTTP 500
All checks were successful
Build and Test / build (pull_request) Successful in 29m30s
The publish-release job was missing the target_commitish field in the
Gitea release creation API payload. Without it, Gitea cannot resolve
the tag to a git object and returns HTTP 500 with 'object does not
exist'.

Add COMMIT_SHA (from github.sha) to the job env and pass it as
target_commitish in the JSON payload.

Closes #326
2026-02-26 08:45:58 -06:00
d94e8c8294 docs: add CLAUDE.md for developer context
All checks were successful
Build and Test / build (pull_request) Successful in 29m9s
2026-02-26 08:40:34 -06:00
3550d916bd Merge pull request 'docs(solver): update drag protocol docs to reflect implemented caching' (#330) from docs/solver-drag-cache into main
All checks were successful
Deploy Docs / build-and-deploy (push) Successful in 40s
Build and Test / build (push) Successful in 29m53s
Reviewed-on: #330
2026-02-26 14:25:17 +00:00
6e15b25134 docs(solver): update drag protocol docs to reflect implemented caching
All checks were successful
Build and Test / build (pull_request) Successful in 30m27s
The interactive drag section described the original naive implementation
(re-solve from scratch each step) and called the caching layer a
'planned future optimization'. In reality _DragCache is fully
implemented: pre_drag() builds the system, Jacobian, and compiled
evaluator once, and drag_step() reuses them.

Update code snippets, add _DragCache field table, and document the
single_equation_pass exclusion from the drag path.
2026-02-25 13:23:41 -06:00
82f2422285 Merge pull request 'fix(solver): skip single_equation_pass during drag to prevent stale constraints' (#329) from fix/planar-drag-prepass into main
Some checks failed
Build and Test / build (push) Has been cancelled
Sync Silo Server Docs / sync (push) Successful in 40s
Reviewed-on: #329
2026-02-25 19:03:16 +00:00
314955c3ef fix(solver): update solver submodule — skip prepass during drag
Some checks failed
Build and Test / build (pull_request) Has been cancelled
Updates solver submodule to include fix for planar distance=0
constraints not holding during drag operations.
2026-02-25 12:58:03 -06:00
c7a7436e7b Merge pull request 'chore: update solver submodule' (#328) from chore/update-solver-submodule into main
All checks were successful
Sync Silo Server Docs / sync (push) Successful in 38s
Build and Test / build (push) Successful in 30m20s
Reviewed-on: #328
2026-02-25 02:49:57 +00:00
forbes
40dd8e09d7 chore: update solver submodule
All checks were successful
Build and Test / build (pull_request) Successful in 29m42s
Picks up fix/drag-orientation-stability (kindred/solver#36):
- Half-space tracking for all compound constraints with branch ambiguity
- Quaternion continuity enforcement during interactive drag
2026-02-24 20:49:27 -06:00
1fd52ccf1c Merge pull request 'feat: add QuickNav addon — Phase 1 core infrastructure (#320)' (#324) from feat/quicknav-phase1 into main
All checks were successful
Deploy Docs / build-and-deploy (push) Successful in 46s
Build and Test / build (push) Successful in 29m41s
Reviewed-on: #324
2026-02-24 18:37:19 +00:00
e73c5fc750 feat: add QuickNav addon — Phase 1 core infrastructure (#320)
All checks were successful
Build and Test / build (pull_request) Successful in 29m58s
Add quicknav submodule and create-side integration for keyboard-driven
command navigation.

Submodule: mods/quicknav (https://git.kindred-systems.com/kindred/quicknav)

Create-side changes:
- CMakeLists.txt: add quicknav install rules
- test_kindred_pure.py: add 16 workbench_map validation tests
- docs/src/quicknav/SPEC.md: QuickNav specification

QuickNav provides numbered-key access to workbenches (Ctrl+1-5),
command groupings (Shift+1-9), and individual commands (1-9), with
a navigation bar toolbar and input-widget safety guards.
2026-02-23 14:12:02 -06:00
f652d6ccf8 Merge pull request 'fix(assembly): handle non-standard datum element types in Distance joint classification' (#319) from fix/datum-plane-classification-all-hierarchies into main
Some checks failed
Build and Test / build (push) Has been cancelled
Sync Silo Server Docs / sync (push) Successful in 37s
Reviewed-on: #319
2026-02-23 03:20:04 +00:00
forbes
14ee8c673f fix(assembly): classify datum planes from all class hierarchies in Distance joints
Some checks failed
Build and Test / build (pull_request) Has been cancelled
The datum plane detection in getDistanceType() only checked for
App::Plane (origin planes). This missed two other class hierarchies:

  - PartDesign::Plane (inherits Part::Datum, NOT App::Plane)
  - Part::Plane primitive referenced bare (no Face element)

Both produce empty element types (sub-name ends with ".") but failed
the isDerivedFrom<App::Plane>() check, falling through to
DistanceType::Other and the Planar fallback. This caused incorrect
constraint geometry, leading to conflicting/unsatisfiable constraints
and solver failures.

Add shape-based isDatumPlane/Line/Point helpers that cover all three
hierarchies by inspecting the actual OCCT geometry rather than relying
on class identity alone. Extend getDistanceType() to use these helpers
for all datum-vs-datum and datum-vs-element combinations.

Adds TestDatumClassification.py with tests for PartDesign::Plane,
Part::Plane (bare ref), and cross-hierarchy datum combinations.
2026-02-22 21:18:34 -06:00
a6a5db11f8 Merge pull request 'fix(assembly): classify datum planes from all class hierarchies in Distance joints' (#318) from fix/datum-plane-classification-all-hierarchies into main
All checks were successful
Build and Test / build (push) Successful in 31m20s
Reviewed-on: #318
2026-02-23 00:56:20 +00:00
forbes
962b521f5c fix(assembly): classify datum planes from all class hierarchies in Distance joints
All checks were successful
Build and Test / build (pull_request) Successful in 30m13s
The datum plane detection in getDistanceType() only checked for
App::Plane (origin planes). This missed two other class hierarchies:

  - PartDesign::Plane (inherits Part::Datum, NOT App::Plane)
  - Part::Plane primitive referenced bare (no Face element)

Both produce empty element types (sub-name ends with ".") but failed
the isDerivedFrom<App::Plane>() check, falling through to
DistanceType::Other and the Planar fallback. This caused incorrect
constraint geometry, leading to conflicting/unsatisfiable constraints
and solver failures.

Add shape-based isDatumPlane/Line/Point helpers that cover all three
hierarchies by inspecting the actual OCCT geometry rather than relying
on class identity alone. Extend getDistanceType() to use these helpers
for all datum-vs-datum and datum-vs-element combinations.

Adds TestDatumClassification.py with tests for PartDesign::Plane,
Part::Plane (bare ref), and cross-hierarchy datum combinations.
2026-02-22 18:55:39 -06:00
5c9212247a Merge pull request 'fix(assembly): add datum plane logging + fix cross-product singularity' (#317) from fix/distance-datum-plane-classification into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #317
2026-02-22 22:05:48 +00:00
50dc8c8ea1 Merge pull request 'fix(assembly): classify datum plane references in Distance joints' (#316) from fix/distance-datum-plane-classification into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #316
2026-02-22 20:21:07 +00:00
forbes
b4835e1b05 fix(assembly): classify datum plane references in Distance joints
Some checks failed
Build and Test / build (pull_request) Has been cancelled
When a Distance joint references a datum plane (XY_Plane, XZ_Plane,
YZ_Plane), getDistanceType() failed to recognize it because datum
plane sub-names yield an empty element type. This caused the fallback
to DistanceType::Other → BaseJointKind::Planar, which adds spurious
parallel-normal residuals that overconstrain the system.

For example, three vertex-to-datum-plane Distance joints produced
10 residuals (3×Planar) with 6 mutually contradictory orientation
constraints, causing the solver to find garbage least-squares
solutions.

Add early detection of App::Plane datum objects before the main
geometry classification chain. Datum planes are now correctly mapped:
- Vertex + DatumPlane → PointPlane → PointInPlane (1 residual)
- Edge + DatumPlane → LinePlane → LineInPlane
- Face/DatumPlane + DatumPlane → PlanePlane → Planar
2026-02-22 14:19:11 -06:00
245 changed files with 14833 additions and 8065 deletions

View File

@@ -47,7 +47,6 @@ body:
description: Which part of Kindred Create is affected?
options:
- General / Core
- ztools Workbench
- Silo (Parts Database)
- Theme / QSS
- Assembly

View File

@@ -29,7 +29,7 @@ body:
attributes:
label: Location
description: Where should this documentation live? Link to existing pages if applicable.
placeholder: e.g. docs/src/guide/ztools.md, or "new page under Reference"
placeholder: e.g. docs/src/guide/assembly.md, or "new page under Reference"
validations:
required: false

View File

@@ -27,7 +27,6 @@ body:
description: Which part of Kindred Create does this relate to?
options:
- General / Core
- ztools Workbench
- Silo (Parts Database)
- Theme / QSS
- Assembly

View File

@@ -322,6 +322,7 @@ jobs:
env:
BUILD_TAG: ${{ github.ref_name || inputs.tag }}
COMMIT_SHA: ${{ github.sha }}
NODE_EXTRA_CA_CERTS: /etc/ssl/certs/ca-certificates.crt
steps:
@@ -386,6 +387,7 @@ jobs:
'name': f'Kindred Create {tag}',
'body': body,
'prerelease': prerelease,
'target_commitish': '${COMMIT_SHA}',
}))
")

View File

@@ -1,92 +0,0 @@
name: Report a Problem
description: Have you found something that does not work well, is too hard to do or is missing altogether? Please create a Problem Report.
labels: ["Status: Needs triage","Status: Needs confirmation"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this problem report! Please [search](https://github.com/FreeCAD/FreeCAD/issues) if a similar issue already exists and check out [how to report issues](https://github.com/FreeCAD/FreeCAD?tab=readme-ov-file#reporting-issues). By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/FreeCAD/FreeCAD/blob/main/CODE_OF_CONDUCT.md).
- type: textarea
id: description
attributes:
label: Problem description
description: Describe the problem and how it impacts user experience, workflow, maintainability or performance. You can attach images or log files by clicking this area to highlight it and then dragging files in. To attach a FCStd file, ZIP it first.
placeholder: Describe your problem briefly.
validations:
required: true
- type: dropdown
id: wb
attributes:
label: Workbench affected?
options:
- Assembly
- BIM
- CAM
- Core (App, Gui,...)
- Draft
- FEM
- Material
- Measurement
- Mesh
- Part
- Part Design
- Sketcher
- Spreadsheet
- TechDraw
- Other (specify in description)
- type: textarea
id: steps_to_reproduce
attributes:
label: Steps to reproduce
description: If the problem appears to be a bug with the current functionality, provide a test case or recipe that reliably reproduces the issue. Ideally [record a macro](https://wiki.freecad.org/Std_DlgMacroRecord) and attach it. Please also add an example file as ZIP.
placeholder: |
Drag an example file as ZIP into this textbox to attach it.
Steps to reproduce the behavior:
1. Open my example file
2. Go to '...'
3. Click on '...'
validations:
required: true
- type: textarea
id: expected_behavior
attributes:
label: Expected behavior
description: A clear and concise description of what you expected to happen when following the provided steps above.
placeholder: What is the expected behavior?
validations:
required: true
- type: textarea
id: actual_behavior
attributes:
label: Actual behavior
description: A clear and concise description of what actually happens. If applicable, add screenshots to help explain the problem.
placeholder: What is actually happening? You can add screenshorts or videos by dragging them into this textbox.
validations:
required: true
- type: textarea
id: dev_version
attributes:
label: Development version About Info (in Safe Mode)
description: Download the latest weekly [development release](https://github.com/FreeCAD/FreeCAD/releases) and try reproducing the issue in safe mode (Help → Restart in Safe Mode). Use the [About FreeCAD](https://wiki.freecad.org/About) dialog to copy your full version information and paste it here.
placeholder: |
1. Download the latest weekly development version (link above).
2. Make sure to run in Safe Mode (Help -> Restart in Safe Mode) to exclude interference from 3rd party addons.
3. If the issue is still reproducible, open the About FreeCAD dialog and copy the full version info here.
render: shell
validations:
required: true
- type: textarea
id: good_version
attributes:
label: Last known good version (optional)
description: If this is a regression, paste the [About FreeCAD](https://wiki.freecad.org/About) info from the last known working version.
placeholder: If the problem did not exist in a previous version, paste the About FreeCAD info from that version here.
render: shell

View File

@@ -1,103 +0,0 @@
name: Report a Code Quality Issue
description: Report problems related to code structure, maintainability, performance, correctness, or technical debt that do not directly affect end-user behavior.
labels: ["Status: Needs triage", "Status: Needs confirmation", "Type: Code Quality"]
type: "Code Quality"
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to help improve FreeCAD's code quality!
> [!NOTE]
> This form is intended **only for code quality issues**, such as:
> - Architectural or design problems
> - Maintainability or readability issues
> - Performance or scalability concerns
> - Incorrect abstractions, layering violations, or technical debt
>
> If the issue affects user-visible behavior, workflows, or UI, please use the regular **Report Problem** form instead.
Please [search existing issues](https://github.com/FreeCAD/FreeCAD/issues) before submitting.
By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/FreeCAD/FreeCAD/blob/main/CODE_OF_CONDUCT.md).
- type: dropdown
id: wb
attributes:
label: Area / Workbench affected
description: Select the primary area affected by the code quality issue.
options:
- Assembly
- BIM
- CAM
- Core (App, Gui, Base, ...)
- Draft
- FEM
- Material
- Measurement
- Mesh
- Part
- Part Design
- Sketcher
- Spreadsheet
- TechDraw
- Build system / CI
- Documentation
- Other (specify below)
- type: textarea
id: description
attributes:
label: Problem description
description: |
Describe the code quality issue clearly.
Focus on *why* the current implementation is problematic (e.g. maintainability, performance, correctness, extensibility).
Reference files, classes, functions, or modules where applicable.
placeholder: |
Example:
- Class X violates dependency rules by including Y
- Function Z mixes responsibilities and is hard to reason about
- Algorithm has unnecessary complexity or poor performance characteristics
validations:
required: true
- type: textarea
id: impact
attributes:
label: Impact
description: |
Explain the practical impact of this issue.
For example: increased maintenance cost, higher bug risk, performance degradation, difficulty extending functionality, or CI/build issues.
placeholder: Describe how this affects the codebase long-term.
validations:
required: true
- type: textarea
id: proposed_solution
attributes:
label: Suggested improvement (optional)
description: |
If you have ideas on how to address the issue, outline them here.
This can include refactoring suggestions, alternative designs, or references to best practices.
placeholder: Optional — leave empty if unsure.
- type: textarea
id: references
attributes:
label: References / evidence
description: |
Add relevant references such as:
- File paths or code snippets
- Related issues or pull requests
- Benchmarks, logs, or static analysis results
placeholder: Links, snippets, or related issues.
- type: textarea
id: dev_version
attributes:
label: Development version (if relevant)
description: |
If the issue is version-specific or recently introduced, paste the output from the
**About FreeCAD** dialog of a development build.
Otherwise, this can be left empty.
placeholder: Paste About FreeCAD information here.
render: shell

View File

@@ -1,19 +0,0 @@
name: Request a Backport
description: Suggest that a commit from the main development branch be backported to the next point release
title: "[Backport] "
labels: ["needs triage", "backport"]
body:
- type: input
id: pull_request
attributes:
label: Original PR
description: A link to the PR that contains the commits to be backported
placeholder: https://github.com/FreeCAD/FreeCAD/pulls/1234
validations:
required: true
- type: textarea
id: description
attributes:
label: Description and Justification
description: Why should this PR (or individual commit) be backported?
placeholder: Please explain why this PR or commit should be backported to the next point release.

View File

@@ -1,5 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: 💬 START HERE -- FreeCAD Forum
url: https://forum.freecad.org/
about: You are encouraged to discuss the problem you are seeing on the FreeCAD Forums before opening an issue on GitHub

View File

@@ -1,102 +0,0 @@
aci
addmin
afile
ake
aline
alle
alledges
alocation
anid
anormal
anull
apoints
aply
appy
ascript
ba
beginn
bloaded
bottome
brushin
childrens
childs
connexion
currenty
curvelinear
detet
documentin
doubleclick
dum
eiter
ende
extaction
finde
findn
fle
fo
fpt
freez
froms
graphin
hist
incrementin
indexin
indicies
indx
inout
invertin
isnt
ist
itsel
leadin
linez
localy
lod
mantatory
mata
methode
mke
modell
nd
nin
normale
normaly
numer
oce
ontop
ot
pard
parm
parms
pointin
programm
propt
rady
recurrance
rin
rougly
sectionin
seh
ser
sergent
serie
shs
siz
som
strack
strin
substraction
sur
te
textin
thist
tread
ue
upto
uptodate
usind
vas
vertexes
vew
wallthickness
zuser

View File

@@ -1,11 +0,0 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: daily
- package-ecosystem: pip
directory: /
schedule:
interval: daily

Binary file not shown.

Before

Width:  |  Height:  |  Size: 431 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 416 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 540 KiB

BIN
.github/images/bim.png vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 570 KiB

BIN
.github/images/fem.png vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 464 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 459 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 457 KiB

113
.github/labels.yml vendored
View File

@@ -1,113 +0,0 @@
# Add 'source' label to any change to src files within the source dir EXCEPT for the docs sub-folder
# source:
# - any: ['src/**/*', '!src/docs/*']
# 'Mod' related labels
"Mod: Core":
- changed-files:
- any-glob-to-any-file: ['src/App/**/*', 'src/Base/**/*', 'src/Gui/**/*']
:octocat::
- changed-files:
- any-glob-to-any-file: ['.github/**/*']
"Mod: Addon Manager":
- changed-files:
- any-glob-to-any-file: ['src/Mod/AddonManager/**/*']
"Mod: Materials":
- changed-files:
- any-glob-to-any-file: ['src/Mod/Material/**/*']
"Mod: Measurement":
- changed-files:
- any-glob-to-any-file: ['src/Mod/Measure/**/*']
"Mod: BIM":
- changed-files:
- any-glob-to-any-file: ['src/Mod/BIM/**/*']
"Mod: Assembly":
- changed-files:
- any-glob-to-any-file: ['src/Mod/Assembly/**/*']
"Mod: Draft":
- changed-files:
- any-glob-to-any-file: ['src/Mod/Draft/**/*']
"Mod: FEM":
- changed-files:
- any-glob-to-any-file: ['src/Mod/Fem/**/*']
"Mod: Mesh":
- changed-files:
- any-glob-to-any-file: ['src/Mod/Mesh/**/*']
"Mod: MeshPart":
- changed-files:
- any-glob-to-any-file: ['src/Mod/MeshPart/**/*']
"Mod: OpenSCAD":
- changed-files:
- any-glob-to-any-file: ['src/Mod/OpenSCAD/**/*']
"Mod: Part":
- changed-files:
- any-glob-to-any-file: ['src/Mod/Part/**/*']
"Mod: Part Design":
- changed-files:
- any-glob-to-any-file: ['src/Mod/PartDesign/**/*']
"Mod: CAM":
- changed-files:
- any-glob-to-any-file: ['src/Mod/CAM/**/*']
"Mod: Points":
- changed-files:
- any-glob-to-any-file: ['src/Mod/Points/**/*']
"Mod: Reverse Engineering":
- changed-files:
- any-glob-to-any-file: ['src/Mod/ReverseEngineering/**/*']
"Mod: Sketcher":
- changed-files:
- any-glob-to-any-file: ['src/Mod/Sketcher/**/*']
"Mod: Spreadsheet":
- changed-files:
- any-glob-to-any-file: ['src/Mod/Spreadsheet/**/*']
"Mod: Start":
- changed-files:
- any-glob-to-any-file: ['src/Mod/Start/**/*']
"Mod: Surface":
- changed-files:
- any-glob-to-any-file: ['src/Mod/Surface/**/*']
"Mod: TechDraw":
- changed-files:
- any-glob-to-any-file: ['src/Mod/TechDraw/**/*']
"Mod: Test":
- changed-files:
- any-glob-to-any-file: ['src/Mod/Test/**/*']
# 'Packaging' related labels
"Packaging/building":
- changed-files:
- any-glob-to-any-file: ['cMake/**/*']
"3rd party component":
- changed-files:
- any-glob-to-any-file: ['src/3rdParty/**/*']
# 'Topic' related labels
"Topic: Stylesheets":
- changed-files:
- any-glob-to-any-file: ['src/Gui/Stylesheets/**/*']

View File

@@ -1,15 +0,0 @@
{
"problemMatcher": [
{
"owner": "black-warning",
"severity": "warning",
"pattern": [
{
"regexp": "^(would reformat (.*))$",
"file": 2,
"message": 1
}
]
}
]
}

View File

@@ -1,17 +0,0 @@
{
"problemMatcher": [
{
"owner": "clang",
"pattern": [
{
"regexp": "^(.+):([0-9]+):([0-9]+): (error|warning|note): (.+)$",
"file": 1,
"line": 2,
"column": 3,
"severity": 4,
"message": 5
}
]
}
]
}

View File

@@ -1,16 +0,0 @@
{
"problemMatcher": [
{
"owner": "codespell-matcher",
"severity": "warning",
"pattern": [
{
"regexp": "^(.+):(\\d+):\\s+(.+)$",
"file": 1,
"line": 2,
"message": 3
}
]
}
]
}

View File

@@ -1,16 +0,0 @@
{
"problemMatcher": [
{
"owner": "cpplint",
"severity": "warning",
"pattern": [
{
"regexp": "^(.+):([0-9]+): (.+)$",
"file": 1,
"line": 2,
"message": 3
}
]
}
]
}

View File

@@ -1,18 +0,0 @@
{
"__comment": "Taken from vscode-cpptools's Extension/package.json gcc rule",
"problemMatcher": [
{
"owner": "gcc-problem-matcher",
"pattern": [
{
"regexp": "^(.*):(\\d+):(\\d+):\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$",
"file": 1,
"line": 2,
"column": 3,
"severity": 4,
"message": 5
}
]
}
]
}

View File

@@ -1,16 +0,0 @@
{
"problemMatcher": [
{
"owner": "grepMatcher-warning",
"severity": "warning",
"pattern": [
{
"regexp": "^(.+):([0-9]+):(.+)$",
"file": 1,
"line": 2,
"message": 3
}
]
}
]
}

View File

@@ -1,20 +0,0 @@
{
"__comment":
"Taken from vscode's vs/workbench/contrib/tasks/common/problemMatcher.ts msCompile rule",
"problemMatcher": [
{
"owner": "msvc-problem-matcher",
"pattern": [
{
"regexp": "^(?:\\s+\\d+\\>)?([^\\s].*)\\((\\d+),?(\\d+)?(?:,\\d+,\\d+)?\\)\\s*:\\s+(error|warning|info)\\s+(\\w{1,2}\\d+)\\s*:\\s*(.*)$",
"file": 1,
"line": 2,
"column": 3,
"severity": 4,
"code": 5,
"message": 6
}
]
}
]
}

View File

@@ -1,18 +0,0 @@
{
"problemMatcher": [
{
"owner": "pylint-error",
"severity": "error",
"pattern": [
{
"regexp": "^(.+?):([0-9]+):([0-9]+): ([([E|F][0-9]+): (.*)$",
"file": 1,
"line": 2,
"column": 3,
"code": 4,
"message": 5
}
]
}
]
}

View File

@@ -1,18 +0,0 @@
{
"problemMatcher": [
{
"owner": "pylint-warning",
"severity": "warning",
"pattern": [
{
"regexp": "^(.+?):([0-9]+):([0-9]+): ([^E|F][0-9]+): (.*)$",
"file": 1,
"line": 2,
"column": 3,
"code": 4,
"message": 5
}
]
}
]
}

View File

@@ -1,46 +0,0 @@
<!-- Include a brief summary of the changes. -->
<!--
The FreeCAD community thanks you for your contribution!
By creating a Pull Request you agree to the contributing policy. The complete policy can be found in the root of the source tree (CONTRIBUTING.md) or at https://github.com/FreeCAD/FreeCAD/blob/main/CONTRIBUTING.md
This template provides guidance on creating a PR that can be reviewed and approved as quickly as possible. Comments may be safely deleted.
Unless you know exactly what you're doing, please leave the checkbox 'Allow edits by maintainers' enabled. This will allow maintainers to help you.
-->
## Issues
<!-- link to individual issues this PR closes by referencing the issue number (e.g., fixes #1234, closes #4321). -->
## Before and After Images
<!-- If your proposed changes affect the FreeCAD GUI, add before and after screenshots -->
<!-- Notes on the PR Review Process
The following section describes what the maintainers consider when reviewing your Pull Request. These items may not require you to take any action. This information is provided for context. Understanding what we consider will help you prepare your request for speedy approval.
You can find additional documentation about these guidelines in the [Developers handbook](https://freecad.github.io/DevelopersHandbook).
Alignment (Does the PR align with the goals and interests of the project?)
- Does the PR have at least one issue linked, which this PR closes?
- Has the conversation on the PR and related issue(s) reached consensus?
- If the PR affects the GUI, is the Design Working Group (DWG) aware and have they had time to review and comment?
- If the PR affects the GUI, did the contributor include before/after images?
- If the PR affects standards and workflow, is the CAD Working Group (CWG) aware and have they had time to review/comment?
Impact (Does the change affect other parts of the project?)
- Has the impact on documentation been considered and appropriate action taken?
- Has the impact on translation been considered appropriate action taken?
- Will the PR affect existing user documents?
Code Quality (Is code well-written and maintainable?)
- Does the PR warrant a review by the Code Quality Working Group (CQWG)?
- Does the change include tests?
- Is the PR rebased on the current main branch with unnecessary commits squashed?
Release (Are there considerations related to release timing?)
- Has the PR been considered for backporting to the latest release branch?
- Have the release notes been considered/updated?
-->

View File

@@ -1,168 +0,0 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: LGPL-2.1-or-later
"""
run_gui_tests.py
List registered tests via `FreeCAD -t`, filter for GUI tests (names containing 'Gui'), and run each
GUI test module using the specified FreeCAD executable.
Usage:
run_gui_tests.py [FREECAD_EXEC]
If FREECAD_EXEC is omitted the script falls back to 'FreeCAD' on PATH.
If FREECAD_EXEC is a directory containing bin/FreeCAD, that binary is used.
If FREECAD_EXEC is an executable path, it is used directly.
This script returns 0 if all GUI modules run successfully. Otherwise it returns the last non-zero
exit code.
"""
from __future__ import annotations
import sys
import subprocess
import os
from pathlib import Path
def find_executable(arg: str | None) -> str:
"""Return the FreeCAD executable path to use.
If `arg` is None or empty, returns the plain name 'FreeCAD' which will be looked up on PATH. If
`arg` is a directory and contains `bin/FreeCAD` that binary will be returned. If `arg` is a file
path it is returned as-is. Otherwise the original argument is returned.
Common use cases: use the FreeCAD binary from a build directory or an installed FreeCAD prefix.
"""
if not arg:
return "FreeCAD"
p = Path(arg)
if p.is_dir():
candidate = p / "bin" / "FreeCAD"
if candidate.exists():
return str(candidate)
if p.is_file():
return str(p)
# fallback: return as-is (may be on PATH)
return arg
def validate_executable(path: str) -> tuple[bool, str]:
"""Return (ok, message). Checks if the executable exists or is likely on PATH.
This is best effort: if a bare name is given (e.g. 'FreeCAD') we can't stat it here, so we
accept it but warn. If the path points to a file, we check executability. Also warn if the name
looks like the CLI-only 'FreeCADCmd'.
"""
p = Path(path)
if p.is_file():
if not os.access(str(p), os.X_OK):
return False, f"File exists but is not executable: {path}"
if p.name.endswith("FreeCADCmd"):
return (
True,
(
"Warning: executable looks like 'FreeCADCmd' (CLI); GUI tests require the GUI "
"binary 'FreeCAD'."
),
)
return True, ""
# Bare name or non-existent path: accept but warn
if p.name.endswith("FreeCADCmd"):
return (
True,
(
"Warning: executable name looks like 'FreeCADCmd' (CLI); GUI tests require the GUI "
"binary 'FreeCAD'."
),
)
return True, ""
def run_and_capture(cmd: list[str]) -> tuple[int, str]:
"""Run `cmd` and return (returncode, combined stdout+stderr string).
If the executable is not found a return code of 127 is returned and a small error string is
provided as output to aid diagnosis.
"""
try:
proc = subprocess.run(
cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, check=False
)
return proc.returncode, proc.stdout
except FileNotFoundError:
return 127, f"Executable not found: {cmd[0]}\n"
def parse_registered_tests(output: str) -> list[str]:
"""Parse output from `FreeCAD -t` and return a list of registered test unit names.
The function looks for the section starting with the literal 'Registered test units:' and
then collects non-empty, stripped lines from that point onwards as test names.
"""
lines = output.splitlines()
tests: list[str] = []
started = False
for ln in lines:
if not started:
if "Registered test units:" in ln:
started = True
continue
s = ln.strip()
if not s:
# allow blank lines but keep going
continue
tests.append(s)
return tests
def main(argv: list[str]) -> int:
"""Entry point: run GUI test modules registered in the FreeCAD executable.
Returns the last non-zero exit code from any GUI test module, or 0 on success.
"""
exec_arg = argv[1] if len(argv) > 1 else None
freecad_exec = find_executable(exec_arg)
print(f"Using FreeCAD executable: {freecad_exec}")
ok, msg = validate_executable(freecad_exec)
if msg:
print(msg, file=sys.stderr)
if not ok:
print(f"Aborting: invalid FreeCAD executable: {freecad_exec}", file=sys.stderr)
return 3
code, out = run_and_capture([freecad_exec, "-t"])
if code != 0:
print(
f"Warning: listing tests returned exit code {code}; attempting to parse output anyway",
file=sys.stderr,
)
tests = parse_registered_tests(out)
if not tests:
print("No registered tests found; exiting with success.")
return 0
gui_tests = [t for t in tests if "Gui" in t]
if not gui_tests:
print("No GUI tests found in registered tests; nothing to run.")
return 0
print("Found GUI test modules:")
for t in gui_tests:
print(" ", t)
last_rc = 0
for mod in gui_tests:
print(f"\nRunning GUI tests for module: {mod}")
rc, out = run_and_capture([freecad_exec, "-t", mod])
print(out)
if rc != 0:
print(f"Module {mod} exited with code {rc}", file=sys.stderr)
last_rc = rc
return last_rc
if __name__ == "__main__":
sys.exit(main(sys.argv))

View File

@@ -1,115 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * *
# * Copyright (c) 2023 0penBrain. *
# * *
# * This file is part of FreeCAD. *
# * *
# * FreeCAD is free software: you can redistribute it and/or modify it *
# * under the terms of the GNU Lesser General Public License as *
# * published by the Free Software Foundation, either version 2.1 of the *
# * License, or (at your option) any later version. *
# * *
# * FreeCAD is distributed in the hope that it will be useful, but *
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
# * Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public *
# * License along with FreeCAD. If not, see *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
# This workflow is a complementary one to the master CI.
# It aims at doing cleanup operations after a CI workflow ran.
# Being triggered when the master workflow ends allows it to run with necessary privileges.
# Indeed it always run with push-like rights even for PR events.
# In order to work, this cleanup workflow imposes name formatting for caches
# Caches that have to be cleaned (typically compiler caches) shall be named as below :
# ${MARK}-${CONTEXT}-${REF}-${ID}
# with :
# ${MARK} => A mark identifying a cache to be cleaned, defined as being "FC" (without quotes)
# ${CONTEXT} => A string identifying cache saving context, typically OS name or compiler name
# ${REF} => The full reference of the branch owning the cache (starting with "/refs/pull/" or "/refs/heads/")
# ${ID} => A cache unique identifier, generally an ascending number, in no case containing a '-' (hyphen) sign
name: FreeCAD CI cleaner
on:
workflow_run:
workflows: [FreeCAD master CI]
types:
- completed
env:
dryrun: false
concurrency:
group: FC-CI-cleaner
cancel-in-progress: false
jobs:
CachesCleanup:
runs-on: ubuntu-latest
env:
logdir: /tmp/log/
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2
with:
egress-policy: audit
- name: Make needed directories
run: |
mkdir -p ${{ env.logdir }}
- name: Get existing caches for the repo
run: |
curl -H "Accept: application/vnd.github+json" -H "authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" $GITHUB_API_URL/repos/$GITHUB_REPOSITORY/actions/caches > ${{ env.logdir }}caches.json
- name: Extract pull request caches
run: |
# Extract caches of which names starts with MARK and contains "/refs/pull/"
jq ".actions_caches | map(select(.key | startswith(\"FC-\"))) | map(select(.key | contains(\"refs/pull/\")))" ${{ env.logdir }}caches.json > ${{ env.logdir }}pulls.json
- name: Extract and delete pull request obsolete cache IDs
run: |
# Group the caches by MARK-CONTEXT-REF, sort by ascending last access datetime and keep all but the last as to be deleted
# As a consequence, for pull requests, only the most recent cache is kept (one for each context and for each PR)
PRID=$(jq "group_by(.key | .[:rindex(\"-\")]) | .[] | sort_by(.last_accessed_at) | .[:-1][].id" ${{ env.logdir }}pulls.json)
for id in $PRID
do
echo "Trying to delete pull request obsolete cache ID : $id"
if [ ${{ env.dryrun }} == "false" ]
then
curl -X DELETE -H "Accept: application/vnd.github+json" -H "authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" $GITHUB_API_URL/repos/$GITHUB_REPOSITORY/actions/caches/$id
else
echo "DRYRUN: executing : curl -X DELETE -H \"Accept: application/vnd.github+json\" $GITHUB_API_URL/repos/$GITHUB_REPOSITORY/actions/caches/$id"
fi
done
- name: Extract push caches
run: |
# Extract caches of which names starts with MARK and contains "/refs/heads/"
jq ".actions_caches | map(select(.key | startswith(\"FC-\"))) | map(select(.key | contains(\"refs/heads/\")))" ${{ env.logdir }}caches.json > ${{ env.logdir }}pushes.json
- name: Extract and delete push obsolete cache IDs
run: |
# Group the caches by MARK-CONTEXT-REF, sort by ascending last access datetime, keep all but the last 2 and keep all accessed for more than 1 hour as to be deleted
# As a consequence, for pushes (repo branches), at least 2 caches (for each context and for each branch) are kept, others are deleted if they have been useless for more than 1 hour
PSID=$(jq "group_by(.key | .[:rindex(\"-\")]) | .[] | sort_by(.last_accessed_at) | .[:-2][] | select((.last_accessed_at | if contains(\".\") then .[:rindex(\".\")]+\"Z\" else . end | fromdateiso8601) < (now | floor - 3600)) | .id" ${{ env.logdir }}pushes.json)
for id in $PSID
do
echo "Trying to delete push obsolete cache ID : $id"
if [ ${{ env.dryrun }} == "false" ]
then
curl -X DELETE -H "Accept: application/vnd.github+json" -H "authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" $GITHUB_API_URL/repos/$GITHUB_REPOSITORY/actions/caches/$id
else
echo "DRYRUN: executing : curl -X DELETE -H \"Accept: application/vnd.github+json\" $GITHUB_API_URL/repos/$GITHUB_REPOSITORY/actions/caches/$id"
fi
done
- name: Upload logs
if: always()
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: ${{ github.job }}-Logs
path: |
${{ env.logdir }}

View File

@@ -1,93 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * *
# * Copyright (c) 2023 0penBrain. *
# * *
# * This file is part of FreeCAD. *
# * *
# * FreeCAD is free software: you can redistribute it and/or modify it *
# * under the terms of the GNU Lesser General Public License as *
# * published by the Free Software Foundation, either version 2.1 of the *
# * License, or (at your option) any later version. *
# * *
# * FreeCAD is distributed in the hope that it will be useful, but *
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
# * Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public *
# * License along with FreeCAD. If not, see *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
# This is the master workflow for CI of FreeCAD.
# It (only) aims at properly organizing the sub-workflows.
name: FreeCAD master CI
on:
workflow_dispatch: ~
push:
branches:
- main
- releases/**
pull_request: ~
merge_group: ~
concurrency:
group: FC-CI-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
Prepare:
uses: ./.github/workflows/sub_prepare.yml
with:
artifactBasename: Prepare-${{ github.run_id }}
Pixi:
needs: [Prepare]
uses: ./.github/workflows/sub_buildPixi.yml
with:
artifactBasename: Pixi-${{ github.run_id }}
Ubuntu:
needs: [Prepare]
if: "!startsWith(github.head_ref, 'refs/heads/backport-')"
uses: ./.github/workflows/sub_buildUbuntu.yml
with:
artifactBasename: Ubuntu-${{ github.run_id }}
Windows:
needs: [Prepare]
if: "!startsWith(github.head_ref, 'refs/heads/backport-')"
uses: ./.github/workflows/sub_buildWindows.yml
with:
artifactBasename: Windows-${{ github.run_id }}
Lint:
needs: [Prepare]
if: "!startsWith(github.head_ref, 'refs/heads/backport-')"
uses: ./.github/workflows/sub_lint.yml
with:
artifactBasename: Lint-${{ github.run_id }}
changedFiles: ${{ needs.Prepare.outputs.changedFiles }}
changedLines: ${{ needs.Prepare.outputs.changedLines }}
changedCppFiles: ${{ needs.Prepare.outputs.changedCppFiles }}
changedCppLines: ${{ needs.Prepare.outputs.changedCppLines }}
changedPythonFiles: ${{ needs.Prepare.outputs.changedPythonFiles }}
changedPythonLines: ${{ needs.Prepare.outputs.changedPythonLines }}
WrapUp:
needs: [
Prepare,
Pixi,
Ubuntu,
Windows,
Lint
]
if: always()
uses: ./.github/workflows/sub_wrapup.yml
with:
previousSteps: ${{ toJSON(needs) }}

View File

@@ -1,74 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * *
# * Copyright (c) 2023 0penBrain. *
# * *
# * This file is part of FreeCAD. *
# * *
# * FreeCAD is free software: you can redistribute it and/or modify it *
# * under the terms of the GNU Lesser General Public License as *
# * published by the Free Software Foundation, either version 2.1 of the *
# * License, or (at your option) any later version. *
# * *
# * FreeCAD is distributed in the hope that it will be useful, but *
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
# * Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public *
# * License along with FreeCAD. If not, see *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
name: build
description: "Linux: build application"
inputs:
builddir:
description: "Directory where build will happen"
required: true
logFile:
description: "Path for log file"
required: true
errorFile:
description: "Path to error file"
required: true
reportFile:
description: "Path for report file"
required: true
extraParameters:
description: "Extra parameters to CMake build"
required: false
runs:
using: "composite"
steps:
- name: Build
id: build
shell: bash -l {0}
run: |
set -o pipefail
(stdbuf -oL -eL cmake --build ${{ inputs.builddir }} -j$(nproc) ${{ inputs.extraParameters }}) \
2> >(tee -a ${{ inputs.errorFile }}) | tee -a ${{ inputs.logFile }}
- name: Write report
shell: bash -l {0}
if: always()
run: |
if [ ${{ steps.build.outcome }} == 'success' ]
then
echo "<details><summary>:heavy_check_mark: CMake build succeeded</summary>" >> ${{ inputs.reportFile }}
else
echo "<details><summary>:fire: CMake build failed</summary>" >> ${{ inputs.reportFile }}
fi
echo "" >> ${{ inputs.reportFile }}
echo "Build Error Log (stderr output):" >> ${{ inputs.reportFile }}
echo '```' >> ${{ inputs.reportFile }}
cat ${{ inputs.errorFile }} >> ${{ inputs.reportFile }}
echo '```' >> ${{ inputs.reportFile }}
echo "Build Log (only built targets reported):" >> ${{ inputs.reportFile }}
echo '```' >> ${{ inputs.reportFile }}
cat ${{ inputs.logFile }} | sed -ne "/Built target/p" >> ${{ inputs.reportFile }}
echo '```' >> ${{ inputs.reportFile }}
echo "</details>">> ${{ inputs.reportFile }}
echo "" >> ${{ inputs.reportFile }}

View File

@@ -1,79 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * *
# * Copyright (c) 2023 0penBrain. *
# * *
# * This file is part of FreeCAD. *
# * *
# * FreeCAD is free software: you can redistribute it and/or modify it *
# * under the terms of the GNU Lesser General Public License as *
# * published by the Free Software Foundation, either version 2.1 of the *
# * License, or (at your option) any later version. *
# * *
# * FreeCAD is distributed in the hope that it will be useful, but *
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
# * Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public *
# * License along with FreeCAD. If not, see *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
name: configure
description: "Linux: configure CMake"
inputs:
sourcedir:
description: "Directory where sources are stored"
required: false
default: ./
builddir:
description: "Directory where build will happen"
required: true
logFile:
description: "Path for log file"
required: true
errorFile:
description: "Path to error file"
required: true
reportFile:
description: "Path for report file"
required: true
extraParameters:
description: "Extra parameters to CMake configure"
required: false
runs:
using: "composite"
steps:
- name: Configure CMake
id: configure
shell: bash -l {0}
run: |
set -o pipefail
(stdbuf -oL -eL cmake -S ${{ inputs.sourcedir }} -B ${{ inputs.builddir }} -D CMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE ${{inputs.extraParameters }}) \
2> >(tee -a ${{ inputs.errorFile }}) | tee -a ${{ inputs.logFile }}
- name: Write report
shell: bash -l {0}
if: always()
run: |
if [ ${{ steps.configure.outcome }} == 'success' ]
then
echo "<details><summary>:heavy_check_mark: CMake configure succeeded</summary>" >> ${{ inputs.reportFile }}
echo "" >> ${{ inputs.reportFile }}
echo "Configure Error Log (stderr output):" >> ${{ inputs.reportFile }}
echo '```' >> ${{ inputs.reportFile }}
cat ${{ inputs.errorFile }} >> ${{ inputs.reportFile }}
echo '```' >> ${{ inputs.reportFile }}
else
echo "<details><summary>:fire: CMake configure failed</summary>" >> ${{ inputs.reportFile }}
fi
echo "" >> ${{ inputs.reportFile }}
echo "Configure Log (only final configuration values reported):" >> ${{ inputs.reportFile }}
echo '```' >> ${{ inputs.reportFile }}
cat ${{ inputs.logFile }} | sed -ne "/^ *==/,/^====/p" >> ${{ inputs.reportFile }}
echo '```' >> ${{ inputs.reportFile }}
echo "</details>">> ${{ inputs.reportFile }}
echo "" >> ${{ inputs.reportFile }}

View File

@@ -1,47 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * *
# * Copyright (c) 2023 0penBrain. *
# * *
# * This file is part of FreeCAD. *
# * *
# * FreeCAD is free software: you can redistribute it and/or modify it *
# * under the terms of the GNU Lesser General Public License as *
# * published by the Free Software Foundation, either version 2.1 of the *
# * License, or (at your option) any later version. *
# * *
# * FreeCAD is distributed in the hope that it will be useful, but *
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
# * Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public *
# * License along with FreeCAD. If not, see *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
name: generateCacheKey
description: "Linux: generates a cache key taking into account distro and compiler"
inputs:
compiler:
description: "Binary name/path of compiler to be used"
required: true
qt_major_version:
description: "Major version number of qt to be used"
required: true
outputs:
cacheKey:
description: "Cache key with distro and compiler version"
value: ${{ steps.generateCacheKey.outputs.cacheKey }}
runs:
using: "composite"
steps:
- id: generateCacheKey
shell: bash -l {0}
run: |
cacheKey=$(lsb_release -ds | tr -d ' ')-$(basename ${{ inputs.compiler }})$(${{ inputs.compiler }} -dumpfullversion -dumpversion)-qt${{ inputs.qt_major_version }}
echo "Generated cache key : $cacheKey"
echo "cacheKey=$cacheKey" >> $GITHUB_OUTPUT

View File

@@ -1,74 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * *
# * Copyright (c) 2023 0penBrain. *
# * *
# * This file is part of FreeCAD. *
# * *
# * FreeCAD is free software: you can redistribute it and/or modify it *
# * under the terms of the GNU Lesser General Public License as *
# * published by the Free Software Foundation, either version 2.1 of the *
# * License, or (at your option) any later version. *
# * *
# * FreeCAD is distributed in the hope that it will be useful, but *
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
# * Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public *
# * License along with FreeCAD. If not, see *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
name: install
description: "Linux: install application"
inputs:
builddir:
description: "Directory where build is stored"
required: true
logFile:
description: "Path for log file"
required: true
errorFile:
description: "Path to error file"
required: true
reportFile:
description: "Path for report file"
required: true
extraParameters:
description: "Extra parameters to CMake install"
required: false
runs:
using: "composite"
steps:
- name: Install
id: install
shell: bash -l {0}
run: |
set -o pipefail
(stdbuf -oL -eL sudo cmake --install ${{ inputs.builddir }} ${{ inputs.extraParameters }}) \
2> >(tee -a ${{ inputs.errorFile }}) | tee -a ${{ inputs.logFile }}
- name: Write report
shell: bash -l {0}
if: always()
run: |
if [ ${{ steps.install.outcome }} == 'success' ]
then
echo "<details><summary>:heavy_check_mark: CMake install succeeded</summary>" >> ${{ inputs.reportFile }}
else
echo "<details><summary>:fire: CMake install failed</summary>" >> ${{ inputs.reportFile }}
fi
echo "" >> ${{ inputs.reportFile }}
echo "Install Error Log (stderr output):" >> ${{ inputs.reportFile }}
echo '```' >> ${{ inputs.reportFile }}
cat ${{ inputs.errorFile }} >> ${{ inputs.reportFile }}
echo '```' >> ${{ inputs.reportFile }}
echo "Install Error Log (stdout output trimmed to the last 100 Lines):" >> ${{ inputs.reportFile }}
echo '```' >> ${{ inputs.reportFile }}
tail -n 100 ${{ inputs.logFile }} >> ${{ inputs.reportFile }}
echo '```' >> ${{ inputs.reportFile }}
echo "</details>">> ${{ inputs.reportFile }}
echo "" >> ${{ inputs.reportFile }}

View File

@@ -1,73 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * *
# * Copyright (c) 2023 0penBrain. *
# * *
# * This file is part of FreeCAD. *
# * *
# * FreeCAD is free software: you can redistribute it and/or modify it *
# * under the terms of the GNU Lesser General Public License as *
# * published by the Free Software Foundation, either version 2.1 of the *
# * License, or (at your option) any later version. *
# * *
# * FreeCAD is distributed in the hope that it will be useful, but *
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
# * Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public *
# * License along with FreeCAD. If not, see *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
name: build
description: "macOS: build application"
inputs:
builddir:
description: "Directory where build will happen"
required: true
logFile:
description: "Path for log file"
required: true
errorFile:
description: "Path to error file"
required: true
reportFile:
description: "Path for report file"
required: true
extraParameters:
description: "Extra parameters to CMake build"
required: false
runs:
using: "composite"
steps:
- name: Build
id: build
shell: bash -l {0}
run: |
(cmake --build ${{ inputs.builddir }} -j$(nproc) ${{ inputs.extraParameters }}) \
2> >(tee -a ${{ inputs.errorFile }}) | tee -a ${{ inputs.logFile }}
- name: Write report
shell: bash -l {0}
if: always()
run: |
if [ ${{ steps.build.outcome }} == 'success' ]
then
echo "<details><summary>:heavy_check_mark: CMake build succeeded</summary>" >> ${{ inputs.reportFile }}
else
echo "<details><summary>:fire: CMake build failed</summary>" >> ${{ inputs.reportFile }}
fi
echo "" >> ${{ inputs.reportFile }}
echo "Build Error Log (stderr output):" >> ${{ inputs.reportFile }}
echo '```' >> ${{ inputs.reportFile }}
cat ${{ inputs.errorFile }} >> ${{ inputs.reportFile }}
echo '```' >> ${{ inputs.reportFile }}
echo "Build Log (only built targets reported):" >> ${{ inputs.reportFile }}
echo '```' >> ${{ inputs.reportFile }}
cat ${{ inputs.logFile }} | sed -ne "/Built target/p" >> ${{ inputs.reportFile }}
echo '```' >> ${{ inputs.reportFile }}
echo "</details>">> ${{ inputs.reportFile }}
echo "" >> ${{ inputs.reportFile }}

View File

@@ -1,78 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * *
# * Copyright (c) 2023 0penBrain. *
# * *
# * This file is part of FreeCAD. *
# * *
# * FreeCAD is free software: you can redistribute it and/or modify it *
# * under the terms of the GNU Lesser General Public License as *
# * published by the Free Software Foundation, either version 2.1 of the *
# * License, or (at your option) any later version. *
# * *
# * FreeCAD is distributed in the hope that it will be useful, but *
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
# * Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public *
# * License along with FreeCAD. If not, see *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
name: configure
description: "macOS: configure CMake"
inputs:
sourcedir:
description: "Directory where sources are stored"
required: false
default: ./
builddir:
description: "Directory where build will happen"
required: true
logFile:
description: "Path for log file"
required: true
errorFile:
description: "Path to error file"
required: true
reportFile:
description: "Path for report file"
required: true
extraParameters:
description: "Extra parameters to CMake configure"
required: false
runs:
using: "composite"
steps:
- name: Configure CMake
id: configure
shell: bash -l {0}
run: |
(cmake -S ${{ inputs.sourcedir }} -B ${{ inputs.builddir }} -D CMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE ${{inputs.extraParameters }}) \
2> >(tee -a ${{ inputs.errorFile }}) | tee -a ${{ inputs.logFile }}
- name: Write report
shell: bash -l {0}
if: always()
run: |
if [ ${{ steps.configure.outcome }} == 'success' ]
then
echo "<details><summary>:heavy_check_mark: CMake configure succeeded</summary>" >> ${{ inputs.reportFile }}
echo "" >> ${{ inputs.reportFile }}
echo "Configure Error Log (stderr output):" >> ${{ inputs.reportFile }}
echo '```' >> ${{ inputs.reportFile }}
cat ${{ inputs.errorFile }} >> ${{ inputs.reportFile }}
echo '```' >> ${{ inputs.reportFile }}
else
echo "<details><summary>:fire: CMake configure failed</summary>" >> ${{ inputs.reportFile }}
fi
echo "" >> ${{ inputs.reportFile }}
echo "Configure Log (only final configuration values reported):" >> ${{ inputs.reportFile }}
echo '```' >> ${{ inputs.reportFile }}
cat ${{ inputs.logFile }} | sed -ne "/^ *==/,/^====/p" >> ${{ inputs.reportFile }}
echo '```' >> ${{ inputs.reportFile }}
echo "</details>">> ${{ inputs.reportFile }}
echo "" >> ${{ inputs.reportFile }}

View File

@@ -1,44 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * *
# * Copyright (c) 2023 0penBrain. *
# * *
# * This file is part of FreeCAD. *
# * *
# * FreeCAD is free software: you can redistribute it and/or modify it *
# * under the terms of the GNU Lesser General Public License as *
# * published by the Free Software Foundation, either version 2.1 of the *
# * License, or (at your option) any later version. *
# * *
# * FreeCAD is distributed in the hope that it will be useful, but *
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
# * Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public *
# * License along with FreeCAD. If not, see *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
name: generateCacheKey
description: "macOS: generates a cache key taking into account distro and compiler"
inputs:
compiler:
description: "Binary name/path of compiler to be used"
required: true
outputs:
cacheKey:
description: "Cache key with distro and compiler version"
value: ${{ steps.generateCacheKey.outputs.cacheKey }}
runs:
using: "composite"
steps:
- id: generateCacheKey
shell: bash -l {0}
run: |
cacheKey=$(sw_vers --productName)-$(sw_vers --productVersion)-$(basename ${{ inputs.compiler }})$(${{ inputs.compiler }} -dumpfullversion -dumpversion)
echo "Generated cache key : $cacheKey"
echo "cacheKey=$cacheKey" >> $GITHUB_OUTPUT

View File

@@ -1,73 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * *
# * Copyright (c) 2023 0penBrain. *
# * *
# * This file is part of FreeCAD. *
# * *
# * FreeCAD is free software: you can redistribute it and/or modify it *
# * under the terms of the GNU Lesser General Public License as *
# * published by the Free Software Foundation, either version 2.1 of the *
# * License, or (at your option) any later version. *
# * *
# * FreeCAD is distributed in the hope that it will be useful, but *
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
# * Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public *
# * License along with FreeCAD. If not, see *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
name: install
description: "macOS: install application"
inputs:
builddir:
description: "Directory where build is stored"
required: true
logFile:
description: "Path for log file"
required: true
errorFile:
description: "Path to error file"
required: true
reportFile:
description: "Path for report file"
required: true
extraParameters:
description: "Extra parameters to CMake install"
required: false
runs:
using: "composite"
steps:
- name: Install
id: install
shell: bash -l {0}
run: |
(sudo cmake --install ${{ inputs.builddir }} ${{ inputs.extraParameters }}) \
2> >(tee -a ${{ inputs.errorFile }}) | tee -a ${{ inputs.logFile }}
- name: Write report
shell: bash -l {0}
if: always()
run: |
if [ ${{ steps.install.outcome }} == 'success' ]
then
echo "<details><summary>:heavy_check_mark: CMake install succeeded</summary>" >> ${{ inputs.reportFile }}
else
echo "<details><summary>:fire: CMake install failed</summary>" >> ${{ inputs.reportFile }}
fi
echo "" >> ${{ inputs.reportFile }}
echo "Install Error Log (stderr output):" >> ${{ inputs.reportFile }}
echo '```' >> ${{ inputs.reportFile }}
cat ${{ inputs.errorFile }} >> ${{ inputs.reportFile }}
echo '```' >> ${{ inputs.reportFile }}
echo "Install Error Log (stdout output trimmed to the last 100 Lines):" >> ${{ inputs.reportFile }}
echo '```' >> ${{ inputs.reportFile }}
tail -n 100 ${{ inputs.logFile }} >> ${{ inputs.reportFile }}
echo '```' >> ${{ inputs.reportFile }}
echo "</details>">> ${{ inputs.reportFile }}
echo "" >> ${{ inputs.reportFile }}

View File

@@ -1,169 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * *
# * Copyright (c) 2023 The FreeCAD Project Association *
# * *
# * This file is part of FreeCAD. *
# * *
# * FreeCAD is free software: you can redistribute it and/or modify it *
# * under the terms of the GNU Lesser General Public License as *
# * published by the Free Software Foundation, either version 2.1 of the *
# * License, or (at your option) any later version. *
# * *
# * FreeCAD is distributed in the hope that it will be useful, but *
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
# * Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public *
# * License along with FreeCAD. If not, see *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
# !!! Each step running a single test shall have an'id' defined so its report will be output
name: Run all C++ Tests
description: "Run all C++ tests, generating logs and reports for each"
inputs:
reportdir:
description: "Report directory where logs will be stored"
required: true
builddir:
description: "Build directory where tests are located"
required: true
reportFile:
description: "Path for report file"
required: true
runs:
using: "composite"
steps:
- name: C++ Assembly tests
id: assembly
uses: ./.github/workflows/actions/runCPPTests/runSingleTest
with:
testCommand: ${{ inputs.builddir }}/tests/Assembly_tests_run --gtest_output=json:${{ inputs.reportdir }}assembly_gtest_results.json
testLogFile: ${{ inputs.reportdir }}assembly_gtest_test_log.txt
testName: Assembly
- name: C++ app tests
id: app
uses: ./.github/workflows/actions/runCPPTests/runSingleTest
with:
testCommand: ${{ inputs.builddir }}/tests/App_tests_run --gtest_output=json:${{ inputs.reportdir }}app_gtest_results.json
testLogFile: ${{ inputs.reportdir }}app_gtest_test_log.txt
testName: App
- name: C++ base tests
id: base
uses: ./.github/workflows/actions/runCPPTests/runSingleTest
with:
testCommand: ${{ inputs.builddir }}/tests/Base_tests_run --gtest_output=json:${{ inputs.reportdir }}base_gtest_results.json
testLogFile: ${{ inputs.reportdir }}base_gtest_test_log.txt
testName: Base
- name: C++ Gui tests
id: gui
uses: ./.github/workflows/actions/runCPPTests/runSingleTest
with:
testCommand: ${{ inputs.builddir }}/tests/Gui_tests_run --gtest_output=json:${{ inputs.reportdir }}gui_gtest_results.json
testLogFile: ${{ inputs.reportdir }}gui_gtest_test_log.txt
testName: Gui
- name: C++ Material tests
id: material
uses: ./.github/workflows/actions/runCPPTests/runSingleTest
with:
testCommand: ${{ inputs.builddir }}/tests/Material_tests_run --gtest_output=json:${{ inputs.reportdir }}material_gtest_results.json
testLogFile: ${{ inputs.reportdir }}material_gtest_test_log.txt
testName: Material
- name: C++ Measure tests
id: measure
uses: ./.github/workflows/actions/runCPPTests/runSingleTest
with:
testCommand: ${{ inputs.builddir }}/tests/Material_tests_run --gtest_output=json:${{ inputs.reportdir }}measure_gtest_results.json
testLogFile: ${{ inputs.reportdir }}measure_gtest_test_log.txt
testName: Measure
- name: C++ Mesh tests
id: mesh
uses: ./.github/workflows/actions/runCPPTests/runSingleTest
with:
testCommand: ${{ inputs.builddir }}/tests/Mesh_tests_run --gtest_output=json:${{ inputs.reportdir }}mesh_gtest_results.json
testLogFile: ${{ inputs.reportdir }}mesh_gtest_test_log.txt
testName: Mesh
- name: C++ MeshPart tests
id: meshpart
uses: ./.github/workflows/actions/runCPPTests/runSingleTest
with:
testCommand: ${{ inputs.builddir }}/tests/Mesh_tests_run --gtest_output=json:${{ inputs.reportdir }}meshpart_gtest_results.json
testLogFile: ${{ inputs.reportdir }}meshpart_gtest_test_log.txt
testName: MeshPart
- name: C++ Misc tests
id: misc
uses: ./.github/workflows/actions/runCPPTests/runSingleTest
with:
testCommand: ${{ inputs.builddir }}/tests/Misc_tests_run --gtest_output=json:${{ inputs.reportdir }}misc_gtest_results.json
testLogFile: ${{ inputs.reportdir }}misc_gtest_test_log.txt
testName: Misc
- name: C++ Part tests
id: part
uses: ./.github/workflows/actions/runCPPTests/runSingleTest
with:
testCommand: ${{ inputs.builddir }}/tests/Part_tests_run --gtest_output=json:${{ inputs.reportdir }}part_gtest_results.json
testLogFile: ${{ inputs.reportdir }}Part_gtest_test_log.txt
testName: Part
- name: C++ PartDesign tests
id: partdesign
uses: ./.github/workflows/actions/runCPPTests/runSingleTest
with:
testCommand: ${{ inputs.builddir }}/tests/PartDesign_tests_run --gtest_output=json:${{ inputs.reportdir }}partdesign_gtest_results.json
testLogFile: ${{ inputs.reportdir }}PartDesign_gtest_test_log.txt
testName: PartDesign
- name: C++ Points tests
id: points
uses: ./.github/workflows/actions/runCPPTests/runSingleTest
with:
testCommand: ${{ inputs.builddir }}/tests/Points_tests_run --gtest_output=json:${{ inputs.reportdir }}points_gtest_results.json
testLogFile: ${{ inputs.reportdir }}points_gtest_test_log.txt
testName: Points
- name: C++ Sketcher tests
id: sketcher
uses: ./.github/workflows/actions/runCPPTests/runSingleTest
with:
testCommand: ${{ inputs.builddir }}/tests/Sketcher_tests_run --gtest_output=json:${{ inputs.reportdir }}sketcher_gtest_results.json
testLogFile: ${{ inputs.reportdir }}sketcher_gtest_test_log.txt
testName: Sketcher
- name: C++ Spreadsheet tests
id: spreadsheet
uses: ./.github/workflows/actions/runCPPTests/runSingleTest
with:
testCommand: ${{ inputs.builddir }}/tests/Sketcher_tests_run --gtest_output=json:${{ inputs.reportdir }}spreadsheet_gtest_results.json
testLogFile: ${{ inputs.reportdir }}spreadsheet_gtest_test_log.txt
testName: Spreadsheet
- name: C++ Start tests
id: start
uses: ./.github/workflows/actions/runCPPTests/runSingleTest
with:
testCommand: ${{ inputs.builddir }}/tests/Start_tests_run --gtest_output=json:${{ inputs.reportdir }}start_gtest_results.json
testLogFile: ${{ inputs.reportdir }}start_gtest_test_log.txt
testName: Start
- name: Compose summary report based on test results
if: always()
shell: bash -l {0}
run: |
# Print global result
if [ ${{ job.status }} != "success" ]
then
echo "<details><summary>:fire: C++ tests failed</summary>" >> ${{ inputs.reportFile }}
else
echo "<details><summary>:heavy_check_mark: C++ tests succeeded</summary>" >> ${{ inputs.reportFile }}
fi
echo "" >> ${{ inputs.reportFile }}
echo "<blockquote>" >> ${{ inputs.reportFile }}
#Extract individual results
cat > /tmp/data << "EOD"
${{ toJSON(steps) }}
EOD
echo "$(jq -r ".[].outputs.reportText" /tmp/data)" >> ${{ inputs.reportFile }}
# Close report
echo "</blockquote>" >> ${{ inputs.reportFile }}
echo "</details>" >> ${{ inputs.reportFile }}
echo "" >> ${{ inputs.reportFile }}

View File

@@ -1,74 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * *
# * Copyright (c) 2023 0penBrain. *
# * Copyright (c) 2023 FreeCAD Project Association *
# * *
# * This file is part of FreeCAD. *
# * *
# * FreeCAD is free software: you can redistribute it and/or modify it *
# * under the terms of the GNU Lesser General Public License as *
# * published by the Free Software Foundation, either version 2.1 of the *
# * License, or (at your option) any later version. *
# * *
# * FreeCAD is distributed in the hope that it will be useful, but *
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
# * Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public *
# * License along with FreeCAD. If not, see *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
name: runCPPTests
description: "Run C++ tests, generate log and report"
inputs:
testCommand:
description: "Test command to be run"
required: true
testLogFile:
description: "Path for the command-line output of the tests"
required: true
testName:
description: "A descriptive name for the test suite"
required: true
outputs:
reportText:
description: "Report text"
value: ${{ steps.report.outputs.reportText }}
runs:
using: "composite"
steps:
- name: Run C++ tests
shell: bash -l {0}
run: |
set -o pipefail
${{ inputs.testCommand }} | tee -a ${{ inputs.testLogFile }}
- name: Parse test results
if: always()
id: report
shell: bash -l {0}
run: |
result=$(sed -ne "/Global test environment tear-down/,/^$/{/^$/d;p}" ${{ inputs.testLogFile }})
if grep -qF "[ FAILED ]" <<< $result
then
reportText="<details><summary>:fire: GTest C++ test suite '${{ inputs.testName }}' failed</summary>\n"
else
reportText="<details><summary>:heavy_check_mark: GTest C++ test suite '${{ inputs.testName }}' succeeded</summary>\n"
fi
reportText+="\n"
reportText+="Results\n"
reportText+="\n"
reportText+='```\n'
reportText+="$result\n"
reportText+='```\n'
reportText+="</details>\n"
reportText+="\n"
echo "reportText<<EOD" >> $GITHUB_OUTPUT
echo -e "$reportText" >> $GITHUB_OUTPUT
echo "EOD" >> $GITHUB_OUTPUT
echo -e "$reportText"

View File

@@ -1,82 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * *
# * Copyright (c) 2023 0penBrain. *
# * *
# * This file is part of FreeCAD. *
# * *
# * FreeCAD is free software: you can redistribute it and/or modify it *
# * under the terms of the GNU Lesser General Public License as *
# * published by the Free Software Foundation, either version 2.1 of the *
# * License, or (at your option) any later version. *
# * *
# * FreeCAD is distributed in the hope that it will be useful, but *
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
# * Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public *
# * License along with FreeCAD. If not, see *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
name: runPythonTests
description: "Run Python tests, generate log and report"
inputs:
testDescription:
description: "Test description text, will be used on report"
required: true
testCommand:
description: "Test command to be run"
required: true
logFile:
description: "Path for log file"
required: true
reportFile:
description: "Path for report file"
required: true
runs:
using: "composite"
steps:
- name: Run tests
id: runTests
shell: bash -l {0}
run: |
set -o pipefail
${{ inputs.testCommand }} | sed -Ee "/[[:blank:]]*\([[:digit:]]{1,3} %\)[[:blank:]]*/d" | tee -a ${{ inputs.logFile }}
- name: Write report
shell: bash -l {0}
if: always()
run: |
sed -ne "/^\(FAILED\|ERROR\):/,/^[[:blank:]]*$/bF; /^Traceback/,/^[^[:blank:]]/{/^Traceback/bT; /^[^[:blank:]]/G; bT}; b; :T w ${{ inputs.logFile }}_tracebacks" -e "b; :F w ${{ inputs.logFile }}_failedtests" ${{ inputs.logFile }}
icon=""
if [ $( cat ${{ inputs.logFile }}_tracebacks | wc -l ) -gt 0 ]
then
icon=" :fire:"
fi
if [ ${{ steps.runTests.outcome }} == 'success' ]
then
echo "<details><summary>:heavy_check_mark: ${{ inputs.testDescription }} succeeded$icon</summary>" >> ${{ inputs.reportFile }}
else
echo "<details><summary>:fire: ${{ inputs.testDescription }} failed$icon</summary>" >> ${{ inputs.reportFile }}
echo "" >> ${{ inputs.reportFile }}
echo "Failed tests" >> ${{ inputs.reportFile }}
echo "" >> ${{ inputs.reportFile }}
echo '```' >> ${{ inputs.reportFile }}
cat ${{ inputs.logFile }}_failedtests >> ${{ inputs.reportFile }}
echo '```' >> ${{ inputs.reportFile }}
fi
echo "" >> ${{ inputs.reportFile }}
if [ $( cat ${{ inputs.logFile }}_tracebacks | wc -l ) -gt 0 ]
then
echo "Uncaught tracebacks -- these tracebacks appeared during test but didn't fail a test --" >> ${{ inputs.reportFile }}
echo "" >> ${{ inputs.reportFile }}
echo '```' >> ${{ inputs.reportFile }}
cat ${{ inputs.logFile }}_tracebacks >> ${{ inputs.reportFile }}
echo '```' >> ${{ inputs.reportFile }}
fi
echo "</details>">> ${{ inputs.reportFile }}
echo "" >> ${{ inputs.reportFile }}

View File

@@ -1,78 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * *
# * Copyright (c) 2023 0penBrain. *
# * *
# * This file is part of FreeCAD. *
# * *
# * FreeCAD is free software: you can redistribute it and/or modify it *
# * under the terms of the GNU Lesser General Public License as *
# * published by the Free Software Foundation, either version 2.1 of the *
# * License, or (at your option) any later version. *
# * *
# * FreeCAD is distributed in the hope that it will be useful, but *
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
# * Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public *
# * License along with FreeCAD. If not, see *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
# This action aims at speeding up CI and reduce dependency to external resources
# by creating a cache of Ccache needed binaries then using it for CI runs rather
# than downloading every time.
#
# If it needs to be updated to another version, the process it to change
# 'downloadpath' and 'version' inputs below then delete the existing cache
# from Github interface so a new one is generated using new values.
name: getCcache
description: "Windows: tries to get a cached version of Ccache and create one if fails"
inputs:
ccachebindir:
description: "Directory where ccache binaries shall be stored"
required: true
# Below inputs shall generally not be provided as they won't be used if a cached version exists
# They are mainly used because Github do not support adding env variables in a composite action
ccachedownloadpath:
description: "Path where to download ccache"
required: false
default: https://github.com/ccache/ccache/releases/download/v4.9/
ccacheversion:
description: "Ccache version to be downloaded"
required: false
default: ccache-4.9-windows-x86_64
runs:
using: "composite"
steps:
- name: Create destination directory
shell: bash
run: |
mkdir -p ${{ inputs.ccachebindir }}
- name: Get cached version
uses: actions/cache/restore@v4
id: getCached
with:
path: ${{ inputs.ccachebindir }}
key: ccacheforwin-${{ inputs.ccacheversion }}
- name: Download ccache
shell: bash
if: steps.getCached.outputs.cache-hit != 'true'
run: |
curl -L -o ccache.zip ${{ inputs.ccachedownloadpath }}${{ inputs.ccacheversion }}.zip
7z x ccache.zip -o"ccachetemp" -r -y
cp -a ccachetemp/${{ inputs.ccacheversion }}/ccache.exe ${{ inputs.ccachebindir }}
cp -a ccachetemp/${{ inputs.ccacheversion }}/ccache.exe ${{ inputs.ccachebindir }}/cl.exe
rm ccache.zip
rm -rf ccachetemp
- name: Save version to cache
if: steps.getCached.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: ${{ inputs.ccachebindir }}
key: ${{ steps.getCached.outputs.cache-primary-key }}

View File

@@ -1,76 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * *
# * Copyright (c) 2023 0penBrain. *
# * *
# * This file is part of FreeCAD. *
# * *
# * FreeCAD is free software: you can redistribute it and/or modify it *
# * under the terms of the GNU Lesser General Public License as *
# * published by the Free Software Foundation, either version 2.1 of the *
# * License, or (at your option) any later version. *
# * *
# * FreeCAD is distributed in the hope that it will be useful, but *
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
# * Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public *
# * License along with FreeCAD. If not, see *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
# This action aims at speeding up CI and reduce dependency to external resources
# by creating a cache of Libpack needed files then using it for CI runs rather
# than downloading every time.
#
# If it needs to be updated to another version, the process it to change
# 'downloadpath' and 'version' inputs below then delete the existing cache
# from Github interface so a new one is generated using new values.
name: getLibpack
description: "Windows: tries to get a cached version of Libpack and create one if fails"
inputs:
libpackdir:
description: "Directory where libpack files shall be stored"
required: true
# Below inputs shall generally not be provided as they won't be used if a cached version exists
# They are mainly used because Github do not support adding env variables in a composite action
libpackdownloadurl:
description: "URL where to download libpack"
required: false
default: https://github.com/FreeCAD/FreeCAD-LibPack/releases/download/3.1.1.3/LibPack-1.1.0-v3.1.1.3-Release.7z
libpackname:
description: "Libpack name (once downloaded)"
required: false
default: LibPack-1.1.0-v3.1.1.3-Release
runs:
using: "composite"
steps:
- name: Create destination directory
shell: bash
run: |
mkdir -p ${{ inputs.libpackdir }}
- name: Get cached version
uses: actions/cache/restore@v4
id: getCached
with:
path: ${{ inputs.libpackdir }}
key: libpackforwin-${{ inputs.libpackname }}
- name: Download libpack
shell: bash
if: steps.getCached.outputs.cache-hit != 'true'
run: |
curl -L -o libpack.7z ${{ inputs.libpackdownloadurl }}
7z x libpack.7z -o"libpacktemp" -r -y
mv libpacktemp/${{ inputs.libpackname }}/* ${{ inputs.libpackdir }}
rm -rf libpacktemp
- name: Save version to cache
if: steps.getCached.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: ${{ inputs.libpackdir }}
key: ${{ steps.getCached.outputs.cache-primary-key }}

View File

@@ -1,133 +0,0 @@
# This workflow warns and then closes issues that have had no activity for a
# specified amount of time. You can adjust the behavior by modifying this file.
# For more information, see:
# https://github.com/marketplace/actions/close-stale-issues
# https://github.com/actions/stale/blob/master/action.yml
# https://github.com/actions/stale
---
name: 'Stale Issues'
on: # yamllint disable-line rule:truthy
schedule:
- cron: '0 0 * * *' # Run at 00:00 UTC every day
workflow_dispatch:
permissions:
contents: read
issues: write # for actions/stale to close stale issues
pull-requests: write # for actions/stale to close stale pull requests
jobs:
stale:
runs-on: ubuntu-latest
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2
with:
egress-policy: audit
- name: '🧹 Tag & close stale unconfirmed bugs'
id: stale_issues
uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-issue-stale: 90
days-before-issue-close: 14
days-before-pr-stale: -1
days-before-pr-close: -1
operations-per-run: 200 # max num of ops per run
stale-issue-label: 'Status: Stale'
close-issue-label: 'Status: Auto-closing'
exempt-issue-labels: 'Status: Confirmed,Priority: High,Priority: Critical,Blocker,Type: Feature,no-auto-close,3rd party: OCC'
remove-stale-when-updated: true
ascending: true
stale-issue-message: |
Hi! This issue hasnt seen activity in a while. If its still relevant, please update to the latest FreeCAD weekly build [download here](https://github.com/FreeCAD/FreeCAD/releases/) to see if the problem is resolved.
If the issue persists, let us know by adding a comment with any updates or details. Otherwise, well close this issue automatically in 14 days to keep our backlog tidy. Feel free to comment anytime to keep it open. Closed issues can always be reopened.
Thanks for helping improve FreeCAD!
Access additional [FreeCAD](https://freecad.org) resources:
- **Forum**: https://forum.freecad.org
- **Blog**: https://blog.freecad.org
- **Wiki**: https://wiki.freecad.org
- name: '🧹 Close stale requested feedback issues'
id: awaiting_issues
uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-issue-stale: 20
days-before-issue-close: 14
days-before-pr-stale: -1
days-before-pr-close: -1
operations-per-run: 100 # max num of ops per run
stale-issue-label: 'Status: Stale'
close-issue-label: 'Status: Auto-closing'
only-labels: 'Status: Needs feedback,Status: Needs test on dev version,Status: Needs steps to reproduce'
remove-stale-when-updated: true
ascending: true
stale-issue-message: |
Hi! This issue hasnt seen activity in a while despite the need for further feedback.
If its still relevant, please update to the latest FreeCAD weekly build [download here](https://github.com/FreeCAD/FreeCAD/releases/) to see if the problem is resolved.
If the issue persists, let us know by adding a comment with any updates or details. Otherwise, well close this issue automatically in 14 days to keep our backlog tidy. Feel free to comment anytime to keep it open. Closed issues can always be reopened.
Thanks for helping improve FreeCAD!
Access additional [FreeCAD](https://freecad.org) resources:
- **Forum**: https://forum.freecad.org
- **Blog**: https://blog.freecad.org
- **Wiki**: https://wiki.freecad.org
- name: '🧹 Tag & close inactive issues'
id: inactive_issues
uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-issue-stale: 190
days-before-issue-close: 60
days-before-pr-stale: -1
days-before-pr-close: -1
operations-per-run: 100 # max num of ops per run
stale-issue-label: 'Status: Stale'
close-issue-label: 'Status: Auto-closing'
exempt-issue-labels: 'Priority: High,Priority: Critical,Blocker,Type: Feature,no-auto-close,3rd party: OCC'
remove-stale-when-updated: true
ascending: true
stale-issue-message: |
Hi! This issue hasnt seen activity in a while. We automatically check each issue after 190 days without activity to keep the backlog tidy.
If its still relevant, please update to the latest FreeCAD weekly build [download here](https://github.com/FreeCAD/FreeCAD/releases/) to see if the issue is already resolved.
If the issue is still relevant, let us know by adding a comment.
Otherwise, well close this issue automatically in 60 days.
Feel free to comment anytime to keep it open. Closed issues can always be reopened.
Thanks for helping improve FreeCAD!
Access additional [FreeCAD](https://freecad.org) resources:
- **Forum**: https://forum.freecad.org
- **Blog**: https://blog.freecad.org
- **Wiki**: https://wiki.freecad.org
- name: '🧹 Tag & close inactive PRs'
id: inactive_pr
uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-issue-stale: -1
days-before-issue-close: -1
days-before-pr-stale: 120
days-before-pr-close: 60
operations-per-run: 30 # max num of ops per run
stale-pr-label: 'Status: Stale'
close-pr-label: 'Status: Auto-closing'
exempt-pr-labels: 'Needs backport,Priority: High,Priority: Critical,no-auto-close'
remove-stale-when-updated: true
ascending: true
stale-pr-message: |
Thanks for helping improve FreeCAD!
This pull request hasnt seen activity in a while. We automatically check each PR after 120 days without activity to keep the repository tidy.
If the PR is still relevant, let us know by adding a comment.
Otherwise, well close this PR automatically in 60 days.
If you would like to keep working on this pull request, we advice to rebase it on current main branch, ask feedback from users or maintainers and engage with the community to get it forward.

View File

@@ -1,52 +0,0 @@
name: Backport merged pull request
on:
pull_request_target:
types: [closed, labeled]
branches: [main, releases/*]
permissions:
contents: write
pull-requests: write
jobs:
backport:
name: Create backport pull request
runs-on: ubuntu-latest
# Run the action if a PR is merged with backport labels
# OR
# when already merged PR is labeled with backport labels
if: >
github.event.pull_request.merged
&& (
github.event.action == 'closed'
|| (
github.event.action == 'labeled'
&& startsWith(github.event.label.name, 'backport ')
)
)
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
token: ${{ secrets.GH_TOKEN_FOR_CI_RUNNERS }}
- name: Create backport pull requests
uses: korthout/backport-action@d07416681cab29bf2661702f925f020aaa962997 # v3.4.1
with:
# Inputs documented here: https://github.com/korthout/backport-action?tab=readme-ov-file#inputs
github_token: ${{ secrets.GH_TOKEN_FOR_CI_RUNNERS }}
github_workspace: ${{ github.workspace }}
# permit PRs with merge commits to be backported
merge_commits: 'skip'
# copy labels to backport to identify affected systems and priorities
copy_labels_pattern: '.*'
# Regex pattern to match github labels
# The capture group catches the target branch
# i.e. label "backport releases/FreeCAD-1-0" will create backport
# PR for branch releases/FreeCAD-1-0
label_pattern: ^backport ([^ ]+)$

View File

@@ -1,213 +0,0 @@
name: Build Release
on:
release:
types: [created]
schedule:
- cron: "0 0 * * 3"
workflow_dispatch:
permissions:
contents: write
actions: write
jobs:
upload_src:
runs-on: ubuntu-latest
outputs:
build_tag: ${{ steps.get_tag.outputs.build_tag }}
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2
with:
egress-policy: audit
- name: Checkout Source
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.sha }}
fetch-depth: 2
fetch-tags: true
submodules: 'recursive'
- name: get tag and create release if weekly
id: get_tag
shell: bash -l {0}
env:
GH_TOKEN: ${{ github.token }}
run: |
if [ "${{ github.event_name }}" = "release" ]; then
export BUILD_TAG="${{ github.event.release.tag_name }}"
else
export BUILD_TAG=weekly-$(date "+%Y.%m.%d")
gh release create ${BUILD_TAG} --title "Development Build ${BUILD_TAG}" -F .github/workflows/weekly-build-notes.md --prerelease || true
fi
echo "BUILD_TAG=${BUILD_TAG}" >> "$GITHUB_ENV"
echo "build_tag=${BUILD_TAG}" >> "$GITHUB_OUTPUT"
- name: Trigger notes updater workflow (only for weekly)
if: startsWith(steps.get_tag.outputs.build_tag, 'weekly-')
uses: actions/github-script@v7
env:
WEEKLY_TAG: ${{ steps.get_tag.outputs.build_tag }}
with:
script: |
const owner = context.repo.owner;
const repo = context.repo.repo;
// Reusable/dispatchable updater workflow file in .github/workflows/
const workflow_id = 'weekly-compare-link.yml';
// Use the default branch so the workflow file is available
const ref = (context.payload?.repository?.default_branch) || 'main';
const current_tag = process.env.WEEKLY_TAG || '';
await github.rest.actions.createWorkflowDispatch({
owner, repo, workflow_id, ref,
inputs: { current_tag }
});
core.info(`Dispatched ${workflow_id} on ${ref} with current_tag='${current_tag}'.`)
- name: Upload Source
id: upload_source
shell: bash -l {0}
env:
GH_TOKEN: ${{ github.token }}
run: |
python3 package/scripts/write_version_info.py ../freecad_version.txt
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
git config user.name 'github-actions[bot]'
git apply package/disable_git_info.patch
git commit -a -m "Disable git info write to Version.h"
git archive HEAD -o freecad_source_${BUILD_TAG}.tar
git submodule foreach --recursive \
"git archive HEAD --prefix=\$path/ -o \$sha1.tar && \
tar -A -f \$toplevel/freecad_source_${BUILD_TAG}.tar \$sha1.tar && \
rm \$sha1.tar"
gzip freecad_source_${BUILD_TAG}.tar
sha256sum freecad_source_${BUILD_TAG}.tar.gz > freecad_source_${BUILD_TAG}.tar.gz-SHA256.txt
gh release upload --clobber ${BUILD_TAG} "freecad_source_${BUILD_TAG}.tar.gz" "freecad_source_${BUILD_TAG}.tar.gz-SHA256.txt"
build:
needs: upload_src
strategy:
matrix:
include:
- { target: linux-64, os: ubuntu-22.04 }
- { target: linux-arm64, os: ubuntu-22.04-arm }
- { target: osx-64, os: macos-15-intel, deploy_target: "10.13" }
- { target: osx-arm64, os: macos-latest, deploy_target: "11.0" }
- { target: osx-arm64, os: macos-latest, deploy_target: "15.0" }
- { target: win-64, os: windows-latest }
fail-fast: false
runs-on: ${{ matrix.os }}
environment: weekly-build
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2
with:
egress-policy: audit
# prevent running out of disk space on Ubuntu runners.
- name: Maximize build space
if: runner.os == 'Linux'
uses: AdityaGarg8/remove-unwanted-software@90e01b21170618765a73370fcc3abbd1684a7793 # v5
with:
verbose: 'true'
remove-android: 'true' # (frees ~9 GB)
remove-cached-tools: 'true' # (frees ~8.3 GB)
- name: Set Platform Environment Variables
shell: bash -l {0}
env:
OPERATING_SYSTEM: ${{ runner.os }}
run: |
if [[ $OPERATING_SYSTEM == 'Windows' ]]; then
echo 'PIXI_CACHE_DIR=D:\rattler' >> "$GITHUB_ENV"
echo 'RATTLER_CACHE_DIR=D:\rattler' >> "$GITHUB_ENV"
fi
- name: Checkout Source
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.sha }}
fetch-depth: 2
fetch-tags: true
submodules: 'recursive'
- uses: prefix-dev/setup-pixi@a0af7a228712d6121d37aba47adf55c1332c9c2e # v0.9.4
with:
pixi-version: v0.59.0
cache: false
- name: Install the Apple certificate and provisioning profile
id: get_cert
if: runner.os == 'macOS'
env:
APP_SPECIFIC_PASSWORD: ${{ secrets.APP_SPECIFIC_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }}
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }}
DEVELOPER_TEAM_ID: ${{ secrets.DEVELOPER_TEAM_ID }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
run: |
if [ -z "$BUILD_CERTIFICATE_BASE64" ]; then
echo "has_cert=false" >> $GITHUB_OUTPUT
echo "No certificate avalable... skipping" && exit 0
else
echo "has_cert=true" >> $GITHUB_OUTPUT
fi
# create variables
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
PP_PATH=$RUNNER_TEMP/FreeCAD_bundle.provisionprofile
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
export KEYCHAIN_PASSWORD=$(openssl rand -base64 8)
# import certificate and provisioning profile from secrets
echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH
echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o $PP_PATH
# create temporary keychain
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
# import certificate to keychain
security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH
# apply provisioning profile
mkdir -p ~/Library/Provisioning\ Profiles
cp $PP_PATH ~/Library/Provisioning\ Profiles
xcrun notarytool store-credentials "FreeCAD" --keychain "$KEYCHAIN_PATH" --apple-id "${APPLE_ID}" --password "${APP_SPECIFIC_PASSWORD}" --team-id "${DEVELOPER_TEAM_ID}"
- name: Build and Release Packages
shell: bash
env:
GH_TOKEN: ${{ github.token }}
SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }}
SIGN_RELEASE: ${{ steps.get_cert.outputs.has_cert }}
TARGET_PLATFORM: ${{ matrix.target }}
MAKE_INSTALLER: "true"
UPLOAD_RELEASE: "true"
BUILD_TAG: ${{ needs.upload_src.outputs.build_tag }}
run: |
if [[ "${{ runner.os }}" == "macOS" ]]; then
export MACOS_DEPLOYMENT_TARGET="${{ matrix.deploy_target }}"
fi
python3 package/scripts/write_version_info.py ../freecad_version.txt
cd package/rattler-build
pixi install
pixi run -e package create_bundle
## Needed if running on a self-hosted runner:
# - name: Clean up keychain and provisioning profile
# if: ${{ always() }}
# run: |
# security delete-keychain $RUNNER_TEMP/app-signing.keychain-db
# rm ~/Library/MobileDevice/Provisioning\ Profiles/build_pp.mobileprovision

View File

@@ -1,127 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL Advanced"
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
schedule:
- cron: '28 12 * * 6'
permissions:
contents: read
jobs:
analyze:
name: Analyze (${{ matrix.language }})
# Runner size impacts CodeQL analysis time. To learn more, please see:
# - https://gh.io/recommended-hardware-resources-for-running-codeql
# - https://gh.io/supported-runners-and-hardware-resources
# - https://gh.io/using-larger-runners (GitHub.com only)
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
permissions:
# required for all workflows
security-events: write
# required to fetch internal or private CodeQL packs
packages: read
# only required for workflows in private repositories
actions: read
contents: read
strategy:
fail-fast: false
matrix:
include:
- language: actions
build-mode: none
# - language: c-cpp
# build-mode: autobuild
# - language: javascript-typescript
# build-mode: none
- language: python
build-mode: none
# CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
# Use `c-cpp` to analyze code written in C, C++ or both
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2
with:
egress-policy: audit
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 2
submodules: recursive
# Add any setup steps before running the `github/codeql-action/init` action.
# This includes steps like installing compilers or runtimes (`actions/setup-node`
# or others). This is typically only required for manual builds.
# - name: Setup runtime (example)
# uses: actions/setup-example@v1
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended
queries: security-and-quality
# Change the CodeQL Bundle version
# tools: https://github.com/github/codeql-action/releases/download/codeql-bundle-v2.20.7/codeql-bundle-linux64.tar.gz
# Add exclusions
config: |
paths-ignore:
- src/Mod/Import/App/SCL_output/**
query-filters:
- exclude:
id: py/file-not-closed
# If the analyze step fails for one of the languages you are analyzing with
# "We were unable to automatically build your code", modify the matrix above
# to set the build mode to "manual" for that language. Then modify this step
# to build your code.
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
- if: matrix.build-mode == 'manual'
shell: bash
run: |
echo 'If you are using a "manual" build mode for one or more of the' \
'languages you are analyzing, replace this with the commands to build' \
'your code, for example:'
echo ' make bootstrap'
echo ' make release'
exit 1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
with:
category: "/language:${{matrix.language}}"

View File

@@ -1,163 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL Advanced (c-cpp)"
on:
# push:
# branches: [ "main" ]
# pull_request:
# branches: [ "main" ]
schedule:
- cron: '28 12 * * 6'
workflow_dispatch: # Allow manual triggers
env:
CODEQL_EXTRACTOR_CPP_AUTOINSTALL_DEPENDENCIES: false
permissions:
contents: read
jobs:
analyze:
name: Analyze (${{ matrix.language }})
# Runner size impacts CodeQL analysis time. To learn more, please see:
# - https://gh.io/recommended-hardware-resources-for-running-codeql
# - https://gh.io/supported-runners-and-hardware-resources
# - https://gh.io/using-larger-runners (GitHub.com only)
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
permissions:
# required for all workflows
security-events: write
# required to fetch internal or private CodeQL packs
packages: read
# only required for workflows in private repositories
actions: read
contents: read
strategy:
fail-fast: false
matrix:
include:
# - language: actions
# build-mode: none
- language: c-cpp
build-mode: autobuild
# - language: javascript-typescript
# build-mode: none
# - language: python
# build-mode: none
# CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
# Use `c-cpp` to analyze code written in C, C++ or both
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2
with:
egress-policy: audit
# prevent running out of disk space on Ubuntu runners.
- name: Maximize build space
uses: AdityaGarg8/remove-unwanted-software@90e01b21170618765a73370fcc3abbd1684a7793 # v5
with:
verbose: 'true'
remove-android: 'true' # (frees ~9 GB)
remove-cached-tools: 'true' # (frees ~8.3 GB)
remove-haskell: 'true' # (frees ~5.2 GB)
remove-swapfile: 'true' # (frees ~4 GB)
remove-docker-images: 'true' # (frees ~3.2 GB)
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 2
submodules: recursive
# Install FreeCAD dependencies (cpp)
- name: Setup build environment
run: ./package/ubuntu/install-apt-packages.sh
# Add any setup steps before running the `github/codeql-action/init` action.
# This includes steps like installing compilers or runtimes (`actions/setup-node`
# or others). This is typically only required for manual builds.
# - name: Setup runtime (example)
# uses: actions/setup-example@v1
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended
queries: security-and-quality
# Change the CodeQL Bundle version
# tools: https://github.com/github/codeql-action/releases/download/codeql-bundle-v2.20.7/codeql-bundle-linux64.tar.gz
# If the analyze step fails for one of the languages you are analyzing with
# "We were unable to automatically build your code", modify the matrix above
# to set the build mode to "manual" for that language. Then modify this step
# to build your code.
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
- if: matrix.build-mode == 'manual'
shell: bash
run: |
echo 'If you are using a "manual" build mode for one or more of the' \
'languages you are analyzing, replace this with the commands to build' \
'your code, for example:'
echo ' make bootstrap'
echo ' make release'
exit 1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
with:
category: "/language:${{matrix.language}}"
output: sarif-results
upload: failure-only
- name: filter-sarif
uses: advanced-security/filter-sarif@v1
with:
patterns: |
-tests/**/*
-src/3rdParty/**/*
-**/ui_*.h
-**/moc_*.cpp
input: sarif-results/cpp.sarif
output: sarif-results/cpp.sarif
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: sarif-results/cpp.sarif
- name: Upload loc as a Build Artifact
uses: actions/upload-artifact@v5
with:
name: sarif-results
path: sarif-results
retention-days: 1

View File

@@ -1,27 +0,0 @@
# Dependency Review Action
#
# This Action will scan dependency manifest files that change as part of a Pull Request,
# surfacing known-vulnerable versions of the packages declared or updated in the PR.
# Once installed, if the workflow run is marked as required,
# PRs introducing known-vulnerable packages will be blocked from merging.
#
# Source repository: https://github.com/actions/dependency-review-action
name: 'Dependency Review'
on: [pull_request]
permissions:
contents: read
jobs:
dependency-review:
runs-on: ubuntu-latest
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2
with:
egress-policy: audit
- name: 'Checkout Repository'
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: 'Dependency Review'
uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4

View File

@@ -1,39 +0,0 @@
name: Fedora Daily Build
permissions:
contents: read
on:
schedule:
- cron: "00 00 * * *"
workflow_dispatch:
jobs:
trigger-copr-build:
environment: fedora-daily
env:
copr_login: ${{ secrets.COPR_LOGIN }}
copr_username: ${{ secrets.COPR_USERNAME }}
copr_token: ${{ secrets.COPR_TOKEN }}
runs-on: ubuntu-latest
container: quay.io/packit/packit
steps:
- name: setup copr token
run: |
mkdir -p ~/.config
echo \
"[copr-cli]
login = $copr_login
username = $copr_username
token = $copr_token
copr_url = https://copr.fedorainfracloud.org
" > ~/.config/copr
- name: checkout sources
uses: actions/checkout@v6
with:
submodules: recursive
fetch-depth: 500
fetch-tags: true
- name: Setup safe Git directory
run: git config --global --add safe.directory $GITHUB_WORKSPACE
- name: trigger copr build
run: |
packit build in-copr --project freecad

View File

@@ -1,68 +0,0 @@
name: Fetch Crowdin Translations
on:
workflow_dispatch:
schedule:
- cron: "0 0 * * 1"
jobs:
update-crowdin:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
fetch-depth: 0
token: ${{ secrets.GH_TOKEN_FOR_CROWDIN_SYNC }}
- name: Install Qt ≥ 6.8
uses: jurplel/install-qt-action@d325aaf2a8baeeda41ad0b5d39f84a6af9bcf005
with:
aqtversion: "==3.1.*"
version: "6.8.3"
host: "linux"
target: "desktop"
arch: "linux_gcc_64"
- name: Setup Python & dependencies
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
with:
python-version: "3.13"
- name: Install Python packages
run: |
python3 -m pip install --upgrade pip
pip install pyside6
- name: Gather translations from crowdin
run: |
./updatecrowdin.py build
while ./updatecrowdin.py build-status | grep -q "status: inProgress"; do sleep 10; done
./updatecrowdin.py download
./updatecrowdin.py install
working-directory: ./src/Tools
env:
CROWDIN_TOKEN: ${{ secrets.CROWDIN_TOKEN }}
CROWDIN_PROJECT_ID: freecad
- name: Commit changes
run: |
git config --global user.name "freecad-gh-actions-translation-bot"
git config --global user.email "freecad-gh-actions-translation-bot@github.com"
git add src
git commit -m "Update translations from Crowdin" || echo "No changes to commit"
- name: Push changes to a new branch
run: |
git branch update-crowdin-translations
git push origin update-crowdin-translations --force
- name: Create Pull Request
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0
with:
branch: update-crowdin-translations
title: "Update translations from Crowdin"
body: "Automatic Crowdin update."
token: ${{ secrets.GH_TOKEN_FOR_CROWDIN_SYNC }}
delete-branch: true
add-paths: |
src

View File

@@ -1,49 +0,0 @@
name: Monthly issue metrics
on:
workflow_dispatch:
schedule:
- cron: '0 0 15 * *'
permissions:
issues: write
pull-requests: read
jobs:
build:
name: issue metrics
runs-on: ubuntu-latest
if: github.repository_owner == 'FreeCAD'
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2
with:
egress-policy: audit
- name: Get dates for last month
shell: bash
run: |
# Calculate the first day of the previous month
first_day=$(date -d "last month" +%Y-%m-15)
# Calculate the last day of the previous month
last_day=$(date -d "$first_day +1 month -1 day" +%Y-%m-%d)
#Set an environment variable with the date range
echo "$first_day..$last_day"
echo "last_month=$first_day..$last_day" >> "$GITHUB_ENV"
- name: Run issue-metrics tool
uses: github/issue-metrics@67526e7bd8100b870f10b1c120780a8375777b43 # v3.25.5
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SEARCH_QUERY: 'repo:FreeCAD/FreeCAD is:issue created:${{ env.last_month }}'
- name: Create issue
uses: peter-evans/create-issue-from-file@fca9117c27cdc29c6c4db3b86c48e4115a786710 # v6.0.0
with:
title: Monthly issue metrics report
token: ${{ secrets.GITHUB_TOKEN }}
content-filepath: ./issue_metrics.md
assignees: maxwxyz

View File

@@ -1,32 +0,0 @@
# This workflow will triage pull requests and apply a label based on the
# paths that are modified in the pull request.
#
# For more information, see:
# https://github.com/actions/labeler
name: Labeler
on:
pull_request_target:
types: [opened, reopened]
permissions:
contents: read
jobs:
label:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2
with:
egress-policy: audit
- uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
configuration-path: ".github/labels.yml"
sync-labels: false

View File

@@ -1,43 +0,0 @@
name: Push Crowdin Translations
on:
workflow_dispatch:
schedule:
- cron: "0 0 * * 1"
jobs:
update-crowdin:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
fetch-depth: 0
ref: releases/FreeCAD-1-1
- name: Install Qt ≥ 6.8
uses: jurplel/install-qt-action@d325aaf2a8baeeda41ad0b5d39f84a6af9bcf005
with:
aqtversion: "==3.1.*"
version: "6.8.3"
host: "linux"
target: "desktop"
arch: "linux_gcc_64"
- name: Setup Python & dependencies
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
with:
python-version: "3.13"
- name: Install Python packages
run: |
python3 -m pip install --upgrade pip
pip install pyside6
- name: Push translations to Crowdin
run: |
./updatecrowdin.py gather
./updatecrowdin.py upload
working-directory: ./src/Tools
env:
CROWDIN_TOKEN: ${{ secrets.CROWDIN_TOKEN }}
CROWDIN_PROJECT_ID: freecad

View File

@@ -1,81 +0,0 @@
# This workflow uses actions that are not certified by GitHub. They are provided
# by a third-party and are governed by separate terms of service, privacy
# policy, and support documentation.
name: Scorecard supply-chain security
on:
# For Branch-Protection check. Only the default branch is supported. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
branch_protection_rule:
# To guarantee Maintained check is occasionally updated. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
schedule:
- cron: '20 7 * * 2'
push:
branches: ["main"]
# Declare default permissions as read only.
permissions: read-all
jobs:
analysis:
name: Scorecard analysis
runs-on: ubuntu-latest
permissions:
# Needed to upload the results to code-scanning dashboard.
security-events: write
# Needed to publish results and get a badge (see publish_results below).
id-token: write
contents: read
actions: read
# To allow GraphQL ListCommits to work
issues: read
pull-requests: read
# To detect SAST tools
checks: read
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2
with:
egress-policy: audit
- name: "Checkout code"
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
with:
results_file: results.sarif
results_format: sarif
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
# - you want to enable the Branch-Protection check on a *public* repository, or
# - you are installing Scorecards on a *private* repository
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
# repo_token: ${{ secrets.SCORECARD_TOKEN }}
# Public repositories:
# - Publish results to OpenSSF REST API for easy access by consumers
# - Allows the repository to include the Scorecard badge.
# - See https://github.com/ossf/scorecard-action#publishing-results.
# For private repositories:
# - `publish_results` will always be set to `false`, regardless
# of the value entered here.
publish_results: true
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: SARIF file
path: results.sarif
retention-days: 5
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
with:
sarif_file: results.sarif

View File

@@ -1,232 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * *
# * Copyright (c) 2024 0penBrain, Lorenz Lechner and Jacob Oursland. *
# * *
# * This file is part of FreeCAD. *
# * *
# * FreeCAD is free software: you can redistribute it and/or modify it *
# * under the terms of the GNU Lesser General Public License as *
# * published by the Free Software Foundation, either version 2.1 of the *
# * License, or (at your option) any later version. *
# * *
# * FreeCAD is distributed in the hope that it will be useful, but *
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
# * Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public *
# * License along with FreeCAD. If not, see *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
# This is a build and test workflow for CI of FreeCAD.
# This workflow aims at building and testing FreeCAD on a Conda environment on macOS.
name: Pixi Builds
on:
workflow_call:
inputs:
artifactBasename:
type: string
required: true
testOnBuildDir:
default: false
type: boolean
required: false
allowedToFail:
default: false
type: boolean
required: false
outputs:
reportFile:
value: ${{ jobs.Build.outputs.reportFile }}
jobs:
build_with_pixi:
runs-on: ${{ matrix.os }}
continue-on-error: ${{ inputs.allowedToFail }}
env:
CCACHE_COMPRESS: true
CCACHE_COMPRESSLEVEL: 5
CCACHE_CONFIGPATH: ${{ github.workspace }}/ccache/config
CCACHE_DIR: ${{ github.workspace }}/ccache
CCACHE_MAXSIZE: 1G
CCACHE_NODIRECT: true
CCACHE_NOHASHDIR: true
CCACHE_NOINODECACHE: true
CCACHE_SLOPPINESS: "include_file_ctime,include_file_mtime,pch_defines,time_macros"
builddir: ${{ github.workspace }}/build/release/
cacheKey: pixi-${{ matrix.os }}
config: release
logdir: ${{ github.workspace }}/logs/
reportdir: ${{ github.workspace }}/report/
reportfilename: ${{ inputs.artifactBasename }}-${{ matrix.os }}-report.md
outputs:
reportFile: ${{ steps.Init.outputs.reportFile }}
strategy:
max-parallel: 6
fail-fast: false
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2
with:
egress-policy: audit
- name: Set Platform Environment Variables
shell: bash -l {0}
env:
OPERATING_SYSTEM: ${{ runner.os }}
run: |
if [[ $OPERATING_SYSTEM == 'Windows' ]]; then
echo 'CCACHE_COMPILERCHECK=%compiler%' >> "$GITHUB_ENV"
else
echo 'CCACHE_COMPILERCHECK=%compiler% -dumpfullversion -dumpversion' >> "$GITHUB_ENV"
fi
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Add GCC Problem Matcher
if: runner.os == 'Linux'
run: |
echo "::add-matcher::${{ runner.workspace }}/FreeCAD/.github/problemMatcher/gcc.json"
- name: Add Clang Problem Matcher
if: runner.os == 'macOS'
run: |
echo "::add-matcher::${{ runner.workspace }}/FreeCAD/.github/problemMatcher/clang.json"
- name: Add MSVC++ Problem Matcher
if: runner.os == 'Windows'
run: |
echo "::add-matcher::${{ runner.workspace }}/FreeCAD/.github/problemMatcher/msvc.json"
- name: Make needed directories, files and initializations
id: Init
run: |
mkdir -p ${{ env.builddir }}
mkdir -p ${{ env.logdir }}
mkdir -p ${{ env.reportdir }}
echo "reportFile=${{ env.reportfilename }}" >> $GITHUB_OUTPUT
- uses: prefix-dev/setup-pixi@a0af7a228712d6121d37aba47adf55c1332c9c2e # v0.9.4
with:
pixi-version: v0.59.0
cache: false
- name: Restore Compiler Cache
id: cache-restore
if: always()
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ env.CCACHE_DIR }}
key: FC-${{ env.cacheKey }}-${{ github.ref }}-${{ github.run_id }}
restore-keys: |
FC-${{ env.cacheKey }}-${{ github.ref }}-
FC-${{ env.cacheKey }}-
- name: Print CCache statistics before build, reset stats and print config
run: |
pixi run ccache -s
pixi run ccache -z
pixi run ccache -p
- name: CMake Configure
run: |
pixi run configure-${{ env.config }}
- name: CMake Build
run: |
pixi run build-${{ env.config }}
- name: Print ccache statistics after Build
if: always()
run: |
pixi run ccache -s
- name: FreeCAD CLI tests on build dir
if: inputs.testOnBuildDir
timeout-minutes: 10
uses: ./.github/workflows/actions/runPythonTests
with:
testDescription: "CLI tests on build dir"
testCommand: pixi run ${{ env.builddir }}/bin/FreeCADCmd -t 0
logFile: ${{ env.logdir }}TestCLIBuild.log
reportFile: ${{env.reportdir}}${{ env.reportfilename }}
- name: FreeCAD GUI tests on build dir
if: runner.os == 'Linux' && inputs.testOnBuildDir
timeout-minutes: 15
uses: ./.github/workflows/actions/runPythonTests
with:
testDescription: "GUI tests on build dir"
testCommand: pixi run xvfb-run ${{ env.builddir }}/bin/FreeCAD -t 0
logFile: ${{ env.logdir }}TestGUIBuild.log
reportFile: ${{env.reportdir}}${{ env.reportfilename }}
- name: C++ tests
timeout-minutes: 10
if: runner.os != 'Windows'
uses: ./.github/workflows/actions/runCPPTests/runAllTests
with:
reportdir: ${{ env.reportdir }}
builddir: ${{ env.builddir }}
reportFile: ${{ env.reportdir }}${{ env.reportfilename }}
- name: CMake Install
run: |
pixi run install-${{ env.config }}
- name: FreeCAD CLI tests on install
if: runner.os != 'Windows'
timeout-minutes: 10
uses: ./.github/workflows/actions/runPythonTests
with:
testDescription: "CLI tests on install"
testCommand: pixi run FreeCADCmd -t 0
logFile: ${{ env.logdir }}TestCLIInstall.log
reportFile: ${{env.reportdir}}${{ env.reportfilename }}
- name: FreeCAD GUI tests on install
# if: runner.os == 'Linux'
# currently broken on Qt6 builds
if: false
timeout-minutes: 15
uses: ./.github/workflows/actions/runPythonTests
with:
testDescription: "GUI tests on install"
testCommand: pixi run xvfb-run FreeCAD -t 0
logFile: ${{ env.logdir }}TestGUIInstall.log
reportFile: ${{env.reportdir}}${{ env.reportfilename }}
- name: Save Compiler Cache
id: cache-save
if: always()
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ env.CCACHE_DIR }}
key: FC-${{ env.cacheKey }}-${{ github.ref }}-${{ github.run_id }}
- name: Upload logs
if: always()
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: ${{ inputs.artifactBasename }}-${{ matrix.os }}-Logs
path: |
${{ env.logdir }}
/var/crash/*FreeCAD*
- name: Upload report
if: always()
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: ${{ env.reportfilename }}
path: |
${{env.reportdir}}${{ env.reportfilename }}

View File

@@ -1,239 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * *
# * Copyright (c) 2023 0penBrain. *
# * *
# * This file is part of FreeCAD. *
# * *
# * FreeCAD is free software: you can redistribute it and/or modify it *
# * under the terms of the GNU Lesser General Public License as *
# * published by the Free Software Foundation, either version 2.1 of the *
# * License, or (at your option) any later version. *
# * *
# * FreeCAD is distributed in the hope that it will be useful, but *
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
# * Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public *
# * License along with FreeCAD. If not, see *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
# This is a build and test workflow for CI of FreeCAD.
# This workflow aims at building and testing FreeCAD on Ubuntu 24.04 using GCC.
name: Build Ubuntu 24.04
on:
workflow_call:
inputs:
artifactBasename:
type: string
required: true
testOnBuildDir:
default: false
type: boolean
required: false
allowedToFail:
default: false
type: boolean
required: false
outputs:
reportFile:
value: ${{ jobs.Build.outputs.reportFile }}
jobs:
Build:
runs-on: ubuntu-24.04
continue-on-error: ${{ inputs.allowedToFail }}
env:
CCACHE_COMPILERCHECK: "%compiler% -dumpfullversion -dumpversion" # default:mtime
CCACHE_COMPRESS: true
CCACHE_COMPRESSLEVEL: 5
CCACHE_CONFIGPATH: ${{ github.workspace }}/ccache/config
CCACHE_DIR: ${{ github.workspace }}/ccache
CCACHE_MAXSIZE: 1G
CCACHE_NODIRECT: true
CCACHE_NOHASHDIR: true
CCACHE_NOINODECACHE: true
CCACHE_SLOPPINESS: "include_file_ctime,include_file_mtime,pch_defines,time_macros"
CC: /usr/bin/gcc
CXX: /usr/bin/g++
#CC: /usr/bin/clang
#CXX: /usr/bin/clang++
builddir: ${{ github.workspace }}/build/release/
config: release
logdir: /tmp/logs/
reportdir: /tmp/report/
reportfilename: ${{ inputs.artifactBasename }}-report.md
defaults:
run:
shell: bash
outputs:
reportFile: ${{ steps.Init.outputs.reportFile }}
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2
with:
egress-policy: audit
- name: Checking out source code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
submodules: true
- name: Install FreeCAD dependencies
run: ./package/ubuntu/install-apt-packages.sh
- name: Install FreeCAD Python test dependencies
run: |
sudo apt-get update -y -qq
sudo apt-get install -y -qq python3-pip
python3 -m pip install --upgrade pip
python3 -m pip install ifcopenshell==0.8.2 --break-system-packages
- name: Make needed directories, files and initializations
id: Init
run: |
mkdir -p ${{ env.CCACHE_DIR }}
mkdir -p ${{ env.CCACHE_CONFIGPATH }}
mkdir -p ${{ env.builddir }}
mkdir -p ${{ env.logdir }}
mkdir -p ${{ env.reportdir }}
echo "reportFile=${{ env.reportfilename }}" >> $GITHUB_OUTPUT
- name: Generate cache key
id: genCacheKey
uses: ./.github/workflows/actions/linux/generateCacheKey
with:
compiler: ${{ env.CXX }}
qt_major_version: 5
- name: Restore Compiler Cache
id: cache-restore
if: always()
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ env.CCACHE_DIR }}
key: FC-${{ steps.genCacheKey.outputs.cacheKey }}-${{ github.ref }}-${{ github.run_id }}
restore-keys: |
FC-${{ steps.genCacheKey.outputs.cacheKey }}-${{ github.ref }}-
FC-${{ steps.genCacheKey.outputs.cacheKey }}-
- name: Print CCache statistics before build, reset stats and print config
run: |
ccache -s
ccache -z
ccache -p
- name: Install cmake
uses: jwlawson/actions-setup-cmake@3a6cbe35ba64df7ca70c51365c4aff65db9a9037 # v2.1.1
with:
cmake-version: '3.31.6'
- name: CMake Configure
uses: ./.github/workflows/actions/linux/configure
with:
extraParameters: -G Ninja --preset ${{ env.config }}
builddir: ${{ env.builddir }}
logFile: ${{ env.logdir }}Cmake.log
errorFile: ${{ env.logdir }}CmakeErrors.log
reportFile: ${{env.reportdir}}${{ env.reportfilename }}
- name: CMake Build
uses: ./.github/workflows/actions/linux/build
with:
builddir: ${{ env.builddir }}
logFile: ${{ env.logdir }}Build.log
errorFile: ${{ env.logdir }}BuildErrors.log
reportFile: ${{env.reportdir}}${{ env.reportfilename }}
- name: Print ccache statistics after Build
if: always()
run: |
ccache -s
- name: FreeCAD CLI tests on build dir
if: inputs.testOnBuildDir
timeout-minutes: 10
uses: ./.github/workflows/actions/runPythonTests
with:
testDescription: "CLI tests on build dir"
testCommand: ${{ env.builddir }}/bin/FreeCADCmd -t 0
logFile: ${{ env.logdir }}TestCLIBuild.log
reportFile: ${{env.reportdir}}${{ env.reportfilename }}
- name: FreeCAD GUI tests on build dir
if: inputs.testOnBuildDir
timeout-minutes: 15
uses: ./.github/workflows/actions/runPythonTests
with:
testDescription: "GUI tests on build dir"
# Use Python wrapper to run only GUI-registered tests with the built FreeCAD executable
testCommand: xvfb-run python3 ./.github/scripts/run_gui_tests.py "${{ env.builddir }}"
logFile: ${{ env.logdir }}TestGUIBuild.log
reportFile: ${{env.reportdir}}${{ env.reportfilename }}
- name: C++ tests
timeout-minutes: 1
uses: ./.github/workflows/actions/runCPPTests/runAllTests
with:
reportdir: ${{ env.reportdir }}
builddir: ${{ env.builddir }}
reportFile: ${{ env.reportdir }}${{ env.reportfilename }}
- name: CMake Install
uses: ./.github/workflows/actions/linux/install
with:
builddir: ${{ env.builddir }}
logFile: ${{ env.logdir }}Install.log
errorFile: ${{ env.logdir }}InstallErrors.log
reportFile: ${{env.reportdir}}${{ env.reportfilename }}
- name: FreeCAD CLI tests on install
timeout-minutes: 10
uses: ./.github/workflows/actions/runPythonTests
with:
testDescription: "CLI tests on install"
testCommand: FreeCADCmd -t 0
logFile: ${{ env.logdir }}TestCLIInstall.log
reportFile: ${{env.reportdir}}${{ env.reportfilename }}
- name: FreeCAD GUI tests on install
timeout-minutes: 15
uses: ./.github/workflows/actions/runPythonTests
with:
testDescription: "GUI tests on install"
# Use Python wrapper to run only GUI-registered tests using the installed FreeCAD
testCommand: xvfb-run python3 ./.github/scripts/run_gui_tests.py "FreeCAD"
logFile: ${{ env.logdir }}TestGUIInstall.log
reportFile: ${{env.reportdir}}${{ env.reportfilename }}
- name: Save Compiler Cache
id: cache-save
if: always()
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ env.CCACHE_DIR }}
key: FC-${{ steps.genCacheKey.outputs.cacheKey }}-${{ github.ref }}-${{ github.run_id }}
- name: Upload logs
if: always()
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: ${{ inputs.artifactBasename }}-Logs
path: |
${{ env.logdir }}
/var/crash/*FreeCAD*
- name: Upload report
if: always()
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: ${{ env.reportfilename }}
path: |
${{env.reportdir}}${{ env.reportfilename }}

View File

@@ -1,183 +0,0 @@
# ***************************************************************************
# * Copyright (c) 2023 0penBrain *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
# * as published by the Free Software Foundation; either version 2 of *
# * the License, or (at your option) any later version. *
# * for detail see the LICENCE text file. *
# * *
# * This program is distributed in the hope that it will be useful, *
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
# * GNU Library General Public License for more details. *
# * *
# * You should have received a copy of the GNU Library General Public *
# * License along with this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
# This is a build and test workflow for CI of FreeCAD.
# This workflow aims at building and testing FreeCAD on Windows using MSVC.
name: Build Windows
on:
workflow_dispatch:
inputs:
artifactBasename:
description: "Base name for artifact"
required: false
type: string
default: "FreeCAD"
allowedToFail:
description: "whether a failed build is allowed"
required: false
type: boolean
default: false
workflow_call:
inputs:
artifactBasename:
required: true
type: string
allowedToFail:
required: false
default: false
type: boolean
outputs:
reportFile:
value: ${{ jobs.Build.outputs.reportFile }}
jobs:
Build:
runs-on: windows-latest
continue-on-error: ${{ inputs.allowedToFail }}
env:
CCACHE_COMPILERCHECK: "%compiler%" # default:mtime
CCACHE_COMPRESS: true
CCACHE_COMPRESSLEVEL: 5
CCACHE_DIR: C:/FC/cache/
CCACHE_LOGFILE: C:/logs/ccache.log
CCACHE_MAXSIZE: 1G
CCACHE_NODIRECT: true
CCACHE_NOHASHDIR: true
CCACHE_NOINODECACHE: true
CCACHE_SLOPPINESS: "include_file_ctime,include_file_mtime,pch_defines,time_macros" # Can't get PCH to work on Windows
## Have to use C:\ because not enough space on workspace drive
builddir: C:/FC/build/release/
cacheKey: Windows
ccachebindir: C:/FC/ccache/
config: release
libpackdir: C:/FC/libpack/
logdir: C:/logs/
reportdir: C:/report/
reportfilename: ${{ inputs.artifactBasename }}-report.md
outputs:
reportFile: ${{ steps.Init.outputs.reportFile }}
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2
with:
egress-policy: audit
- name: Checking out source code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
submodules: true
- name: Make needed directories, files and initializations
id: Init
run: |
mkdir ${{ env.CCACHE_DIR }}
mkdir ${{ env.ccachebindir }}
mkdir ${{ env.libpackdir }}
mkdir ${{ env.builddir }}
mkdir ${{ env.logdir }}
mkdir ${{ env.reportdir }}
echo "reportFile=${{ env.reportfilename }}" >> $GITHUB_OUTPUT
- name: Get Ccache
uses: ./.github/workflows/actions/windows/getCcache
with:
ccachebindir: ${{ env.ccachebindir }}
- name: Get Libpack
uses: ./.github/workflows/actions/windows/getLibpack
with:
libpackdir: ${{ env.libpackdir }}
- name: Restore compiler cache
id: cache-restore
if: always()
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ env.CCACHE_DIR }}
key: FC-${{ env.cacheKey }}-${{ github.ref }}-${{ github.run_id }}
restore-keys: |
FC-${{ env.cacheKey }}-${{ github.ref }}-
FC-${{ env.cacheKey }}-
- name: Print Ccache statistics before build, reset stats and print config
run: |
. $env:ccachebindir\ccache -s
. $env:ccachebindir\ccache -z
. $env:ccachebindir\ccache -p
- name: Install cmake
uses: jwlawson/actions-setup-cmake@3a6cbe35ba64df7ca70c51365c4aff65db9a9037 # v2.1.1
with:
cmake-version: '3.31.6'
- name: Configuring CMake
run: >
cmake -B"${{ env.builddir }}" .
--preset ${{ env.config }}
-DCMAKE_VS_NO_COMPILE_BATCHING=ON
-DCMAKE_BUILD_TYPE=Release
-DFREECAD_USE_PCH=OFF
-DFREECAD_RELEASE_PDB=OFF
-DFREECAD_LIBPACK_DIR="${{ env.libpackdir }}"
-DFREECAD_COPY_DEPEND_DIRS_TO_BUILD=ON
-DFREECAD_COPY_LIBPACK_BIN_TO_BUILD=ON
-DFREECAD_COPY_PLUGINS_BIN_TO_BUILD=ON
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@6fb02220983dee41ce7ae257b6f4d8f9bf5ed4ce # v2.0.0
- name: Compiling sources
run: |
cd $env:builddir
msbuild ALL_BUILD.vcxproj /m /p:Configuration=Release /p:TrackFileAccess=false /p:CLToolPath=${{ env.ccachebindir }}
- name: Print Ccache statistics after build
if: always()
run: |
. $env:ccachebindir\ccache -s
- name: C++ unit tests
if: false # Disabled because seems to not function on Windows build
timeout-minutes: 1
run: |
. ${{ env.builddir }}\tests\Release\Tests_run --gtest_output=json:${{ env.reportdir }}gtest_results.json # 2>&1 | tee -filepath ${{ env.logdir }}\unitTests.log
- name: FreeCAD CLI tests
run: |
. ${{ env.builddir }}\bin\FreeCADCmd -t 0 # 2>&1 | tee -filepath ${{ env.logdir }}\integrationTests.log
- name: Save Compiler Cache
id: cache-save
if: always()
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ env.CCACHE_DIR }}
key: FC-${{ env.cacheKey }}-${{ github.ref }}-${{ github.run_id }}
- name: Upload logs
if: always()
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: ${{ inputs.artifactBasename }}-Logs
path: |
${{ env.logdir }}

View File

@@ -1,371 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * *
# * Copyright (c) 2023 0penBrain. *
# * *
# * This file is part of FreeCAD. *
# * *
# * FreeCAD is free software: you can redistribute it and/or modify it *
# * under the terms of the GNU Lesser General Public License as *
# * published by the Free Software Foundation, either version 2.1 of the *
# * License, or (at your option) any later version. *
# * *
# * FreeCAD is distributed in the hope that it will be useful, but *
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
# * Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public *
# * License along with FreeCAD. If not, see *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
# This is the lint workflow for CI of FreeCAD
name: Lint
on:
workflow_call:
inputs:
artifactBasename:
type: string
required: true
changedFiles:
type: string
required: true
changedLines:
type: string
required: false
changedCppFiles:
type: string
required: true
changedCppLines:
type: string
required: false
changedPythonFiles:
type: string
required: true
changedPythonLines:
type: string
required: false
checkLineendings:
default: false
type: boolean
required: false
lineendingsFailSilent:
default: true
type: boolean
required: false
checkWhitespace:
default: true
type: boolean
required: false
whitespaceFailSilent:
default: true
type: boolean
required: false
checkTabs:
default: true
type: boolean
required: false
tabsFailSilent:
default: true
type: boolean
required: false
checkQtConnections:
default: true
type: boolean
required: false
qtConnectionsFailSilent:
default: true
type: boolean
required: false
checkCpplint:
default: true
type: boolean
required: false
cpplintFailSilent:
default: true
type: boolean
required: false
checkPylint:
default: true
type: boolean
required: false
pylintDisable:
default: C0302,C0303 # Trailing whitespaces (C0303) are already checked
type: string
required: false
pylintFailSilent:
default: true
type: boolean
required: false
checkBlack:
default: true
type: boolean
required: false
blackFailSilent:
default: true
type: boolean
required: false
checkClangFormat:
default: false
type: boolean
required: false
clangStyle:
default: file # for .clang-format file
type: string
required: false
clangFormatFailSilent:
default: true
type: boolean
required: false
checkSpelling:
default: true
type: boolean
required: false
listIgnoredMisspelling:
default: .github/codespellignore
type: string
required: false
spellingIgnore:
default: ./.git*,*.po,*.ts,*.svg,./src/3rdParty,./src/Base/swig*,./src/Mod/Robot/App/kdl_cp,./src/Mod/Import/App/SCL*,./src/Doc/FreeCAD.uml,./build/
type: string
required: false
codespellFailSilent:
default: true
type: boolean
required: false
checkClangTidy:
default: true
type: boolean
required: false
clangTidyFailSilent:
default: true # warnings or notes will never fail the CI, only errors
type: boolean
required: false
checkClazy: # for the Message codes see: https://invent.kde.org/sdk/clazy#list-of-checks
default: false
type: boolean
required: false
clazyChecks:
default: level2,no-non-pod-global-static,no-copyable-polymorphic
type: string
required: false
clazyFailSilent:
default: true # warnings or notes will never fail the CI, only errors
type: boolean
required: false
checkClazyQT6:
default: false
type: boolean
required: false
clazyQT6Checks:
default: qt6-deprecated-api-fixes,qt6-header-fixes,qt6-qhash-signature,qt6-fwd-fixes,missing-qobject-macro # for QT6 Porting https://invent.kde.org/sdk/clazy#list-of-checks
type: string
required: false
QT6Branch: # branch to check for QT6 Porting
default: main
type: string
required: false
clazyQT6FailSilent:
default: true # warnings or notes will never fail the CI, only errors
type: boolean
required: false
outputs:
reportFile:
value: ${{ jobs.Lint.outputs.reportFile }}
permissions:
contents: read
jobs:
Lint:
if: inputs.changedFiles != ''
runs-on: ubuntu-latest
env:
logdir: /tmp/logs/
fixesdir: /tmp/fixes/
reportdir: /tmp/report/
reportfilename: ${{ inputs.artifactBasename }}-report.md
defaults:
run:
shell: bash
outputs:
reportFile: ${{ steps.Init.outputs.reportFile }}
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2
with:
egress-policy: audit
- name: Check out code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
submodules: true
- name: Make needed directories, files and initializations
id: Init
run: |
mkdir -p ${{ env.logdir }}
mkdir -p ${{ env.fixesdir }}
mkdir -p ${{ env.reportdir }}
echo "reportFile=${{ env.reportfilename }}" >> $GITHUB_OUTPUT
# Generic lints steps
- name: Check File Case Sensitivity
uses: credfeto/action-case-checker@cb652aeab29ed363bbdb7d9ee1bfcc010c46cac5 # v1.3.0
- name: Check for non Unix line ending
if: inputs.checkLineendings && always()
continue-on-error: ${{ inputs.lineendingsFailSilent }}
run: |
python3 tools/lint/generic_checks.py \
--lineendings-check \
--files "${{ inputs.changedFiles }}" \
--report-file "${{ env.reportdir }}${{ env.reportfilename }}" \
--log-dir "${{ env.logdir }}"
- name: Check for trailing whitespaces
if: inputs.checkWhitespace && always()
continue-on-error: ${{ inputs.whitespaceFailSilent }}
run: |
python3 tools/lint/generic_checks.py \
--whitespace-check \
--files "${{ inputs.changedFiles }}" \
--report-file "${{ env.reportdir }}${{ env.reportfilename }}" \
--log-dir "${{ env.logdir }}"
- name: Check for Tab usage
if: inputs.checkTabs && always()
continue-on-error: ${{ inputs.tabsFailSilent }}
run: |
python3 tools/lint/generic_checks.py \
--tabs-check \
--files "${{ inputs.changedFiles }}" \
--report-file "${{ env.reportdir }}${{ env.reportfilename }}" \
--log-dir "${{ env.logdir }}"
# Python linting steps
- name: Pylint
if: inputs.checkPylint && inputs.changedPythonFiles != '' && always()
continue-on-error: ${{ inputs.pylintFailSilent }}
run: |
python3 tools/lint/pylint.py \
--files "${{ inputs.changedPythonFiles }}" \
--disable "${{ inputs.pylintDisable }}" \
--log-dir "${{ env.logdir }}" \
--report-file "${{ env.reportdir }}${{ env.reportfilename }}"
- name: Black
if: inputs.checkBlack && inputs.changedPythonFiles != '' && always()
continue-on-error: ${{ inputs.blackFailSilent }}
run: |
python3 tools/lint/black.py \
--files "${{ inputs.changedPythonFiles }}" \
--log-dir "${{ env.logdir }}" \
--report-file "${{ env.reportdir }}${{ env.reportfilename }}"
# C++ linting steps
- name: Install FreeCAD dependencies
if: inputs.changedCppFiles != '' && always()
run: ./package/ubuntu/install-apt-packages.sh
- name: Run CMake # This is needed for Clang tools to work correctly
if: inputs.changedCppFiles != '' && always()
run: |
mkdir build && cmake -S ./ -B ./build/ -D CMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE
- name: Check old Qt string-based connections (https://wiki.qt.io/New_Signal_Slot_Syntax)
if: inputs.checkQtConnections && inputs.changedCppFiles != '' && always()
continue-on-error: ${{ inputs.qtConnectionsFailSilent }}
run: |
python3 tools/lint/qt_connections.py \
--files "${{ inputs.changedFiles }}" \
--log-dir "${{ env.logdir }}" \
--report-file "${{ env.reportdir }}${{ env.reportfilename }}"
- name: Cpplint
if: inputs.checkCpplint && inputs.changedCppFiles != '' && always()
continue-on-error: ${{ inputs.cpplintFailSilent }}
run: |
python3 tools/lint/cpplint.py \
--files "${{ inputs.changedCppFiles }}" \
--log-dir "${{ env.logdir }}" \
--report-file "${{ env.reportdir }}${{ env.reportfilename }}"
- name: Clang-format
if: inputs.checkClangFormat && inputs.changedCppFiles != '' && always()
continue-on-error: ${{ inputs.clangFormatFailSilent }}
run: |
python3 tools/lint/clang_format.py \
--files "${{ inputs.changedCppFiles }}" \
--clang-style "${{ inputs.clangStyle }}" \
--log-dir "${{ env.logdir }}" \
--report-file "${{ env.reportdir }}${{ env.reportfilename }}"
- name: Codespell
if: inputs.checkSpelling && always()
continue-on-error: ${{ inputs.codespellFailSilent }}
run: |
python3 tools/lint/codespell.py \
--files ${{ inputs.changedFiles }} \
--ignore-words "${{ inputs.listIgnoredMisspelling }}" \
--skip "${{ inputs.spellingIgnore }}" \
--log-dir "${{ env.logdir }}" \
--report-file "${{ env.reportdir }}${{ env.reportfilename }}"
- name: Clang-tidy
if: inputs.checkClangTidy && inputs.changedCppFiles != '' && always()
continue-on-error: ${{ inputs.clangTidyFailSilent }}
run: |
python3 tools/lint/clang_tidy.py \
--files "${{ inputs.changedCppFiles }}" \
--line-filter '${{ inputs.changedCppLines }}' \
--clang-style "${{ inputs.clangStyle }}" \
--log-dir "${{ env.logdir }}" \
--report-file "${{ env.reportdir }}${{ env.reportfilename }}"
- name: Clazy
if: inputs.checkClazy && inputs.changedCppFiles != '' && always()
continue-on-error: ${{ inputs.clazyFailSilent }}
run: |
python3 tools/lint/clazy.py \
--files "${{ inputs.changedCppFiles }}" \
--clazy-checks "${{ inputs.clazyChecks }}" \
--log-dir "${{ env.logdir }}" \
--report-file "${{ env.reportdir }}${{ env.reportfilename }}"
- name: Clazy-QT6
if: inputs.checkClazyQT6 && inputs.changedCppFiles != '' && github.ref == inputs.QT6Branch && always()
continue-on-error: ${{ inputs.clazyQT6FailSilent }}
run: |
python3 tools/lint/clazy_qt6.py \
--files "${{ inputs.changedCppFiles }}" \
--clazy-qt6-checks "${{ inputs.clazyQT6Checks }}" \
--log-dir "${{ env.logdir }}" \
--report-file "${{ env.reportdir }}${{ env.reportfilename }}"
# Upload steps
- name: Upload logs and fixes
if: always()
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: ${{ inputs.artifactBasename }}-Logs
path: |
${{ env.logdir }}
${{ env.fixesdir }}
- name: Upload report
if: always()
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: ${{ env.reportfilename }}
path: |
${{env.reportdir}}${{ env.reportfilename }}

View File

@@ -1,194 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * *
# * Copyright (c) 2023 0penBrain. *
# * *
# * This file is part of FreeCAD. *
# * *
# * FreeCAD is free software: you can redistribute it and/or modify it *
# * under the terms of the GNU Lesser General Public License as *
# * published by the Free Software Foundation, either version 2.1 of the *
# * License, or (at your option) any later version. *
# * *
# * FreeCAD is distributed in the hope that it will be useful, but *
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
# * Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public *
# * License along with FreeCAD. If not, see *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
# This is the pre-check workflow for CI of FreeCAD.
# It aims at running some basic checks about the workflow run ...
# ... and gathering some data needed for the next steps.
name: Prepare
on:
workflow_call:
inputs:
artifactBasename:
type: string
required: true
dontFailOnOldRebase:
default: true
type: boolean
required: false
maxRebaseHours:
default: "48"
type: string
required: false
outputs:
reportFile:
value: ${{ jobs.Prepare.outputs.reportFile }}
changedFiles:
value: ${{ jobs.Prepare.outputs.changedFiles }}
changedLines:
value: ${{ jobs.Prepare.outputs.changedLines }}
changedPythonFiles:
value: ${{ jobs.Prepare.outputs.changedPythonFiles }}
changedPythonLines:
value: ${{ jobs.Prepare.outputs.changedPythonLines }}
changedCppFiles:
value: ${{ jobs.Prepare.outputs.changedCppFiles }}
changedCppLines:
value: ${{ jobs.Prepare.outputs.changedCppLines }}
jobs:
Prepare:
env:
isPR: ${{ github.event_name == 'pull_request' }}
isPush: ${{ github.event_name == 'push' }}
logdir: /tmp/logs/
reportdir: /tmp/report/
reportfilename: ${{ inputs.artifactBasename }}-report.md
runs-on: ubuntu-latest
defaults:
run:
shell: bash
outputs:
reportFile: ${{ steps.Init.outputs.reportFile }}
changedFiles: ${{ steps.Output.outputs.changedFiles }}
changedLines: ${{ steps.Output.outputs.changedLines }}
changedPythonFiles: ${{ steps.Output.outputs.changedPythonFiles }}
changedPythonLines: ${{ steps.Output.outputs.changedPythonLines }}
changedCppFiles: ${{ steps.Output.outputs.changedCppFiles }}
changedCppLines: ${{ steps.Output.outputs.changedCppLines }}
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2
with:
egress-policy: audit
- name: Make needed directories, files and initializations
id: Init
run: |
mkdir -p ${{ env.logdir }}
mkdir -p ${{ env.reportdir }}
commitCnt=0
touch ${{ env.logdir }}changedFiles.lst ${{ env.logdir }}changedCppFiles.lst ${{ env.logdir }}changedPythonFiles.lst
echo "reportFile=${{ env.reportfilename }}" >> $GITHUB_OUTPUT
- name: Check out code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
submodules: true
- name: Determine base and head SHA in case of PR
if: env.isPR == 'true'
run: |
baseSha=${{ github.event.pull_request.base.sha }}
headSha=${{ github.event.pull_request.head.sha }}
echo "baseSha=$baseSha" >> $GITHUB_ENV
echo "headSha=$headSha" >> $GITHUB_ENV
echo "This CI run is performed on a Pull Request" | tee -a ${{env.reportdir}}${{ env.reportfilename }}
echo "Base SHA is $baseSha, Head SHA is $headSha" | tee -a ${{env.reportdir}}${{ env.reportfilename }}
- name: Check if PR has been recently rebased
if: env.isPR == 'true'
continue-on-error: ${{ inputs.dontFailOnOldRebase }}
run: |
baseDate=$(curl -H "Accept: application/vnd.github+json" -H "authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" $GITHUB_API_URL/repos/$GITHUB_REPOSITORY/commits/$baseSha | jq -r '.commit.committer.date')
dateDiff=$(( ( $(date +%s) - $(date -d $baseDate +%s) ) / 3600 ))
echo "Pull request is based on a $dateDiff hour-old commit" | tee -a ${{env.reportdir}}${{ env.reportfilename }}
# Exit the step with appropriate code
if [ $dateDiff -gt ${{ inputs.maxRebaseHours }} ]
then
echo -n ":warning: Pull request should be rebased" | tee -a ${{env.reportdir}}${{ env.reportfilename }}
exit 1
fi
- name: Determine base and head SHA in case of push
if: env.isPush == 'true'
run: |
baseSha=${{ github.event.before }}
headSha=${{ github.event.after }}
echo "headSha=$headSha" >> $GITHUB_ENV
if [ $baseSha -eq 0 ]
then
echo "This CI run is performed on a Push that created a new branch : files changed will be ignored" | tee -a ${{env.reportdir}}${{ env.reportfilename }}
echo "Head SHA is $headSha" | tee -a ${{env.reportdir}}${{ env.reportfilename }}
echo "isPush='false'" >> $GITHUB_ENV
else
echo "This CI run is performed on a Push" | tee -a ${{env.reportdir}}${{ env.reportfilename }}
echo "Base SHA is $baseSha, Head SHA is $headSha" | tee -a ${{env.reportdir}}${{ env.reportfilename }}
echo "baseSha=$baseSha" >> $GITHUB_ENV
fi
- name: Get compare between head and base
if: env.isPR == 'true' || env.isPush == 'true'
run: |
echo "$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/compare/$baseSha...$headSha"
curl -H "Accept: application/vnd.github+json" -H "authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" $GITHUB_API_URL/repos/$GITHUB_REPOSITORY/compare/$baseSha...$headSha > ${{ env.logdir }}compare.json
- name: Get number of commits in the changeset
if: env.isPR == 'true' || env.isPush == 'true'
run: |
commitCnt=$(jq -re '.ahead_by' ${{ env.logdir }}compare.json)
echo "Changeset is composed of $commitCnt commit(s)" | tee -a ${{env.reportdir}}${{ env.reportfilename }}
- name: Get files modified in changeset #TODO check what happens with deleted file in the subsequent process
if: env.isPR == 'true'
env:
API_URL: ${{ github.api_url }}
TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
REF: ${{ github.ref_name }}
PR: ${{ github.event.number }}
run: |
# could reduce this to a single
python3 tools/lint/changed_lines.py --api-url ${API_URL} --token ${TOKEN} --repo ${REPO} --ref=${REF} --pr=${PR} > ${{ env.logdir }}changedLines.lst
cat ${{ env.logdir }}changedLines.lst | jq '.[].name' > ${{ env.logdir }}changedFiles.lst
python3 tools/lint/changed_lines.py --api-url ${API_URL} --token ${TOKEN} --repo ${REPO} --ref=${REF} --pr=${PR} --file-filter '.py, .pyi' > ${{ env.logdir }}changedPythonLines.lst
cat ${{ env.logdir }}changedPythonLines.lst | jq '.[].name' > ${{ env.logdir }}changedPythonFiles.lst
python3 tools/lint/changed_lines.py --api-url ${API_URL} --token ${TOKEN} --repo ${REPO} --ref=${REF} --pr=${PR} --file-filter '.c, .cc, .cu, .cuh, .c++, .cpp, .cxx, .h, .hh, .h++, .hpp, .hxx' > ${{ env.logdir }}changedCppLines.lst
cat ${{ env.logdir }}changedCppLines.lst | jq '.[].name' > ${{ env.logdir }}changedCppFiles.lst
# Write the report
echo "::group::Modified files in changeset (removed files are ignored) :" ; cat ${{ env.logdir }}changedFiles.lst ; echo "::endgroup::"
echo "<details><summary>Modified files (removed files are ignored):</summary>" >> ${{env.reportdir}}${{ env.reportfilename }}
cat ${{ env.logdir }}changedFiles.lst >> ${{env.reportdir}}${{ env.reportfilename }}
echo "</details>" >> ${{env.reportdir}}${{ env.reportfilename }}
echo "" >> ${{env.reportdir}}${{ env.reportfilename }}
- name: Transmitting outputs
id: Output
run: |
echo "changedFiles=$(cat ${{ env.logdir }}changedFiles.lst | tr '\n' ' ')" >> $GITHUB_OUTPUT
echo "changedLines=$(cat ${{ env.logdir }}changedLines.lst | tr '\n' ' ')" >> $GITHUB_OUTPUT
echo "changedPythonFiles=$(cat ${{ env.logdir }}changedPythonFiles.lst | tr '\n' ' ')" >> $GITHUB_OUTPUT
echo "changedPythonLines=$(cat ${{ env.logdir }}changedPythonLines.lst | tr '\n' ' ')" >> $GITHUB_OUTPUT
echo "changedCppFiles=$(cat ${{ env.logdir }}changedCppFiles.lst | tr '\n' ' ')" >> $GITHUB_OUTPUT
echo "changedCppLines=$(cat ${{ env.logdir }}changedCppLines.lst | tr '\n' ' ')" >> $GITHUB_OUTPUT
echo "" >> $GITHUB_OUTPUT
- name: Upload logs
if: always()
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: ${{ inputs.artifactBasename }}-Logs
path: |
${{ env.logdir }}
- name: Upload report
if: always()
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: ${{ env.reportfilename }}
path: |
${{env.reportdir}}${{ env.reportfilename }}

View File

@@ -1,125 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * *
# * Copyright (c) 2023 0penBrain. *
# * *
# * This file is part of FreeCAD. *
# * *
# * FreeCAD is free software: you can redistribute it and/or modify it *
# * under the terms of the GNU Lesser General Public License as *
# * published by the Free Software Foundation, either version 2.1 of the *
# * License, or (at your option) any later version. *
# * *
# * FreeCAD is distributed in the hope that it will be useful, but *
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
# * Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public *
# * License along with FreeCAD. If not, see *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
# This is a generic wrapup workflow that only aims at gathering reports and ...
# ... presenting them as a unified summary
#
# It expects steps to be summarized to be presented a JSON input formatted ...
# ... as the "needs" context of Github :
# https://docs.github.com/en/actions/learn-github-actions/contexts#needs-context
# In addition to standard "result", each step shall have a string entry in "outputs" ...
# ... named "reportFile" containing the name of the corresponding report. The ...
# ... report file shall be available in an artifact with the same name.
name: WrapUp
on:
workflow_call:
inputs:
previousSteps:
type: string
required: true
permissions:
contents: read
jobs:
WrapUp:
runs-on: ubuntu-latest
env:
artifactsDownloadDir: /tmp/artifacts/
defaults:
run:
shell: bash
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2
with:
egress-policy: audit
- name: Make needed directories, files and initializations
run: |
mkdir -p ${{ env.artifactsDownloadDir }}
- name: Download artifacts
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
path: ${{ env.artifactsDownloadDir }}
- name: Save input data to file
run: |
cat > data << "EOD"
${{ inputs.previousSteps }}
EOD
- name: Compute the report
run: |
echo "usedArtifacts<<EOD" >> $GITHUB_ENV
for step in $(jq -r "keys_unsorted | .[]" data)
do
echo "Processing step $step"
result=$(jq -r ".\"$step\".result" data)
icon=":heavy_check_mark:"
if [ $result == 'failure' ]
then
icon=":x:"
elif [ $result == 'cancelled' ]
then
icon=":no_entry_sign:"
elif [ $result == 'skipped' ]
then
icon=":white_check_mark:"
fi
echo "### $icon $step step" >> report.md
if [ $result == 'skipped' ]
then
echo "Step was skipped, no report was generated" | tee -a report.md
continue
elif [ $result == 'cancelled' ]
then
echo "Step was cancelled when executing, report may be incomplete" | tee -a report.md
fi
report=$(jq -r ".\"$step\".outputs.reportFile" data)
if [ $report ]
then
echo "Report for step $step is $report"
echo "$report" >> $GITHUB_ENV
if [ $(find ${{ env.artifactsDownloadDir }} -type f -name $report | wc -l) -eq 1 ]
then
find ${{ env.artifactsDownloadDir }} -type f -name $report -exec cat {} \; >> report.md
else
echo "No or several files found for report $report, not printing" | tee -a report.md
echo "Below files found :"
find ${{ env.artifactsDownloadDir }} -type f -name $report
fi
else
echo "Report file was not set by step $step" | tee -a report.md
fi
echo "" >> report.md
done
echo "EOD" >> $GITHUB_ENV
cat report.md >> $GITHUB_STEP_SUMMARY
- name: Delete used artifacts
continue-on-error: true
uses: geekyeggo/delete-artifact@f275313e70c08f6120db482d7a6b98377786765b # v5.1.0
with:
name: |
${{ env.usedArtifacts }}

View File

@@ -1,16 +0,0 @@
> [!IMPORTANT]
> Bleeding edge FreeCAD development builds for testing bugfixes, regressions, and recently implemented features. Do not use in a production environment.
**Changes since last weekly:** <!--DIFF_LINK-->
### How-to use
1. Download the appropriate asset for your OS below
2. Unpack the bundle to any folder on your system
3. Launch the application
- **Windows**
Run `\FreeCAD.exe` in the extracted directory
- **macOS**
Launch `/FreeCAD.app` in the extracted directory
- **Linux**
Open the `*.AppImage`

View File

@@ -1,140 +0,0 @@
name: Weekly compare link to release notes
on:
release:
types: [published] # run automatically when a (pre-)release is published
workflow_dispatch: # allow manual runs too
inputs:
current_tag:
description: "Weekly tag (e.g., weekly-2026.01.07). Leave empty to auto-detect latest weekly pre-release."
required: false
dry_run:
description: "Only compute; do not update the release body."
required: false
type: boolean
default: false
permissions:
contents: write # required to PATCH the release body
jobs:
update-notes:
runs-on: ubuntu-latest
steps:
- name: Inject compare link into weekly release notes
uses: actions/github-script@v7
env:
# Pass manual inputs via env for convenience
CURRENT_TAG: ${{ github.event.inputs.current_tag }}
DRY_RUN: ${{ github.event.inputs.dry_run }}
PLACEHOLDER: "<!--DIFF_LINK-->"
with:
script: |
// Updates the current weekly release notes with a compare link to the previous weekly.
// Works for both release-published events and manual (workflow_dispatch) runs.
const owner = context.repo.owner;
const repo = context.repo.repo;
const rx = /^weekly-(\d{4})\.(\d{2})\.(\d{2})$/;
// Determine currentTag:
// 1) Manual input via workflow_dispatch (env.CURRENT_TAG)
// 2) Tag from release event payload
// 3) Fallback: newest weekly pre-release
let currentTag = process.env.CURRENT_TAG || (context.payload?.release?.tag_name) || null;
async function detectLatestWeeklyTag() {
const releases = await github.paginate(github.rest.repos.listReleases, { owner, repo, per_page: 100 });
const cand = releases.find(r => r.prerelease && typeof r.tag_name === 'string' && rx.test(r.tag_name));
return cand?.tag_name || null;
}
if (!currentTag) {
currentTag = await detectLatestWeeklyTag();
}
if (!currentTag || !rx.test(currentTag)) {
core.info(`No weekly tag detected or tag format mismatch ('${currentTag}'). Skipping.`);
return;
}
// Resolve the current release object
let curRel;
try {
const { data } = await github.rest.repos.getReleaseByTag({ owner, repo, tag: currentTag });
curRel = data;
} catch (e) {
core.setFailed(`No release for tag ${currentTag}: ${e.message}`);
return;
}
// If event is a normal release (not pre-release), skip automatically-run case;
// but allow manual override (manual runs can patch any weekly tag).
const isManual = context.eventName === 'workflow_dispatch';
if (!isManual && !curRel.prerelease) {
core.info('Current release is not a pre-release; skipping (auto run).');
return;
}
// Helpers
const toPrevWeeklyTag = (tag) => {
const [, y, m, d] = tag.match(rx);
const dt = new Date(Date.UTC(+y, +m - 1, +d));
const prev = new Date(dt.getTime() - 7 * 24 * 3600 * 1000); // minus 7 days
const iso = prev.toISOString().slice(0, 10); // YYYY-MM-DD
return `weekly-${iso.replace(/-/g, '.')}`; // weekly-YYYY.MM.DD
};
const ymdKey = (t) => t.replace(rx, '$1$2$3'); // YYYYMMDD
async function tagExists(tag) {
try {
await github.rest.git.getRef({ owner, repo, ref: `tags/${tag}` });
return true;
} catch {
return false;
}
}
// Compute previous weekly deterministically, then fall back if needed
let prevTag = toPrevWeeklyTag(currentTag);
if (!(await tagExists(prevTag))) {
core.info(`Computed previous tag ${prevTag} not found; scanning older weeklies...`);
const releases = await github.paginate(github.rest.repos.listReleases, { owner, repo, per_page: 100 });
const curKey = ymdKey(currentTag);
const older = releases
.filter(r => r.prerelease && typeof r.tag_name === 'string' && rx.test(r.tag_name))
.map(r => ({ tag: r.tag_name, key: ymdKey(r.tag_name) }))
.filter(x => x.key < curKey)
.sort((a, b) => b.key.localeCompare(a.key)); // newest older first
if (!older.length) {
core.info('No older weekly found; nothing to do.');
return;
}
prevTag = older[0].tag;
}
const compareUrl = `https://github.com/${owner}/${repo}/compare/${prevTag}...${currentTag}`;
const head = `**Changes since last weekly (${prevTag} → ${currentTag}):**\n${compareUrl}\n`;
if (process.env.DRY_RUN === 'true') {
core.info(`[DRY RUN] Would update release ${currentTag} with: ${compareUrl}`);
return;
}
// Idempotent body update
let body = curRel.body || '';
if (body.includes(compareUrl)) {
core.info('Compare URL already present; done.');
return;
}
const placeholder = process.env.PLACEHOLDER || '<!--DIFF_LINK-->';
if (body.includes(placeholder)) {
body = body.replace(placeholder, compareUrl);
} else if (/\*\*Changes since last weekly:/i.test(body)) {
body = body.replace(/\*\*Changes since last weekly:[^\n]*\n?/i, head + '\n');
} else {
body += (body.endsWith('\n') ? '\n' : '\n\n') + head;
}
await github.rest.repos.updateRelease({ owner, repo, release_id: curRel.id, body });
core.info(`Release notes updated with compare link: ${compareUrl}`);

3
.gitignore vendored
View File

@@ -9,7 +9,6 @@
!/.gitmodules
!/.pre-commit-config.yaml
!/.pylintrc
!/.github/
!/.gitea/
!/contrib/.vscode/
!*.gitattributes
@@ -77,3 +76,5 @@ docs/book/
# To regenerate themed icons: python3 icons/retheme.py
# icons/themed/ is tracked (committed) so CI builds include them
CLAUDE.md

12
.gitmodules vendored
View File

@@ -10,10 +10,6 @@
[submodule "src/Mod/AddonManager"]
path = src/Mod/AddonManager
url = https://github.com/FreeCAD/AddonManager.git
[submodule "mods/ztools"]
path = mods/ztools
url = https://git.kindred-systems.com/forbes/ztools.git
branch = main
[submodule "mods/silo"]
path = mods/silo
url = https://git.kindred-systems.com/kindred/silo-mod.git
@@ -22,3 +18,11 @@
path = mods/solver
url = https://git.kindred-systems.com/kindred/solver.git
branch = main
[submodule "mods/gears"]
path = mods/gears
url = https://git.kindred-systems.com/kindred/gears.git
branch = main
[submodule "mods/datums"]
path = mods/datums
url = https://git.kindred-systems.com/kindred/datums.git
branch = main

5
.mailmap Normal file
View File

@@ -0,0 +1,5 @@
forbes <contact@kindred-systems.com> forbes <joseph.forbes@kindred-systems.com>
forbes <contact@kindred-systems.com> forbes <zoe.forbes@kindred-systems.com>
forbes <contact@kindred-systems.com> forbes-0023 <joseph.forbes@kindred-systems.com>
forbes <contact@kindred-systems.com> josephforbes23 <joseph.forbes@kindred-systems.com>
forbes <contact@kindred-systems.com> Zoe Forbes <forbes@copernicus-9.kindred.internal>

View File

@@ -1,68 +0,0 @@
specfile_path: package/fedora/freecad.spec
files_to_sync:
- .packit.yaml
actions:
post-upstream-clone:
- bash -c '/usr/bin/python3 package/scripts/write_version_info.py freecad_version.txt'
- rm -f freecad-sources.tar.gz
changelog-entry:
- bash -c 'git log --no-merges --pretty="format:- %s (%an)" $(git describe --tags --abbrev=0 )..HEAD -- |sed 's/%/%%/g''
create-archive:
- git submodule update --init
- bash -c 'git ls-files --recurse-submodules | tar -caf freecad-sources.tar.gz -T-'
- echo -n 'freecad-sources.tar.gz'
downstream_package_name: freecad
additional_packages:
- python3
jobs:
- job: copr_build
identifier: pull_request
trigger: pull_request
notifications:
pull_request:
successful_build: True
branch: main
additional_repos:
- copr://bpostle/IfcOpenShell
manual_trigger: true
targets:
fedora-stable:
without_opts:
- debug_info
fedora-development:
without_opts:
- debug_info
- job: tests
identifier: pull_request
trigger: pull_request
branch: main
manual_trigger: true
targets:
fedora-latest-stable:
without_opts:
- debug_info
fmf_path: package/fedora/tests
- job: copr_build
identifier: main
trigger: commit
manual_trigger: true
branch: main
additional_repos:
- copr://bpostle/IfcOpenShell
owner: freecad
project: nightly
- job: copr_build
identifier: release
trigger: release
owner: freecad
project: freecad
additional_repos:
- copr://bpostle/IfcOpenShell
targets:
fedora-all:
without_opts:
- tests

View File

@@ -4,7 +4,6 @@
# See https://pre-commit.com/hooks.html for more hooks
files: |
(?x)^(
.github|
src/Base|
src/Gui|
src/Main|
@@ -60,7 +59,7 @@ exclude: |
.*\.brep$
)
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0
hooks:
- id: trailing-whitespace
@@ -68,12 +67,12 @@ repos:
- id: check-yaml
- id: check-added-large-files
- id: mixed-line-ending
- repo: https://github.com/psf/black-pre-commit-mirror
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 2892f1f81088477370d4fbc56545c05d33d2493f # frozen: 25.11.0
hooks:
- id: black
args: ['--line-length', '100']
- repo: https://github.com/pre-commit/mirrors-clang-format
args: ["--line-length", "100"]
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: 317810f3c6a0ad3572367dc86cb6e41863e16e08 # frozen: v21.1.5
hooks:
- id: clang-format

347
CLAUDE.md Normal file
View File

@@ -0,0 +1,347 @@
# CLAUDE.md — Developer Context for Kindred Create
## Project Overview
Kindred Create is a fork of FreeCAD 1.0+ that adds integrated tooling for professional engineering workflows. It ships a context-aware UI system, five addon modules (SDK, Gears, Datums, Silo, Solver), a KCSDK C++ extension layer, a Catppuccin Mocha dark theme, and a pluggable file origin layer on top of FreeCAD's parametric modeling core.
- **Kindred Create version:** 0.1.5
- **FreeCAD base version:** 1.2.0
- **License:** LGPL-2.1-or-later
- **Repository:** `git.kindred-systems.com/kindred/create` (Gitea)
- **Main branch:** `main`
## Quick Start
```bash
git clone --recursive ssh://git@git.kindred-systems.com:2222/kindred/create.git
cd create
pixi run configure # CMake configure (debug by default)
pixi run build # Build
pixi run install # Install to build dir
pixi run freecad # Launch
pixi run test # Run C++ tests (ctest)
pixi run test-kindred # Run Python/Kindred tests
```
Build variants: append `-debug` or `-release` (e.g., `pixi run build-release`). See `CMakePresets.json` for platform-specific presets (Linux x86_64/aarch64, macOS Intel/ARM, Windows x64).
## Repository Structure
```
create/
├── src/
│ ├── App/ Core application (C++)
│ ├── Base/ Base classes, type system, persistence (C++)
│ ├── Gui/ GUI framework (C++)
│ │ ├── SDK/ KCSDK C++ library (Kindred feature)
│ │ │ ├── bindings/ pybind11 module (kcsdk.so)
│ │ │ ├── IPanelProvider.h Dock panel interface
│ │ │ ├── IToolbarProvider.h Toolbar interface
│ │ │ ├── IMenuProvider.h Menu interface
│ │ │ ├── SDKRegistry.h Provider registry
│ │ │ ├── ThemeEngine.h Runtime theme token access
│ │ │ └── WidgetBridge.h Qt widget creation bridge
│ │ ├── EditingContext.h Editing context resolver (Kindred feature)
│ │ ├── BreadcrumbToolBar.h Breadcrumb navigation widget (Kindred feature)
│ │ ├── FileOrigin.h Abstract origin interface (Kindred feature)
│ │ ├── OriginManager.h Origin lifecycle management
│ │ ├── CommandOrigin.cpp Origin_Commit/Pull/Push/Info/BOM commands
│ │ ├── ApplicationPy.h All FreeCADGui.* Python bindings
│ │ ├── Application.h App signals (fastsignals)
│ │ ├── Stylesheets/ QSS theme files
│ │ └── PreferencePacks/ Preference configurations (build-time generated)
│ ├── Mod/ FreeCAD modules (PartDesign, Assembly, Sketcher, etc.)
│ │ └── Create/ Kindred Create module
│ │ ├── App/ C++ app-side module (AppCreate)
│ │ ├── Gui/ C++ GUI-side module (AppCreateGui)
│ │ ├── Init.py Console bootstrap — loads addons
│ │ ├── InitGui.py GUI bootstrap — loads addons, kc_format, update checker
│ │ ├── addon_loader.py Manifest-driven loader with dependency resolution
│ │ ├── kc_format.py .kc file format preservation
│ │ ├── silo_document.py .kc tree building observer
│ │ ├── silo_tree.py Silo metadata tree builder
│ │ ├── silo_objects.py Silo tree FeaturePython objects
│ │ ├── silo_viewers.py Silo tree MDI viewers
│ │ └── silo_viewproviders.py Silo tree ViewProvider icons
│ └── 3rdParty/ Vendored dependencies
│ ├── OndselSolver/ [submodule] Assembly constraint solver (forked)
│ ├── FastSignals/ Signal/slot library (NOT Boost)
│ └── GSL/ [submodule] Microsoft Guidelines Support Library
├── mods/ Kindred addon modules
│ ├── sdk/ Addon SDK — stable API contract (priority 0)
│ ├── gears/ [submodule] Parametric gear workbench (priority 40)
│ ├── datums/ [submodule] Unified datum creator (priority 45)
│ ├── silo/ [submodule] PLM workbench (priority 60)
│ └── solver/ [submodule] Assembly solver research (priority 10)
├── reference/ Archived addons (not built)
│ ├── ztools/ Archived — commands migrated to datums + core
│ └── quicknav/ Archived — navigation addon
├── docs/ mdBook documentation + architecture docs
├── tests/ C++ unit tests (GoogleTest)
├── package/ Packaging (debian/, rattler-build/)
├── resources/ Branding, icons, desktop integration
├── cMake/ CMake helper modules
├── .gitea/workflows/ CI/CD pipelines
├── CMakeLists.txt Root build configuration (CMake 3.22.0+)
├── CMakePresets.json Platform build presets
└── pixi.toml Pixi environment and build tasks
```
## Build System
- **Primary:** CMake 3.22.0+ with Ninja generator
- **Environment:** [Pixi](https://pixi.sh) (conda-forge) manages all dependencies
- **Key deps:** Qt 6.8.x, Python 3.11.x, OpenCASCADE 7.8.x, PySide6, Boost, VTK, SMESH
- **Presets:** `conda-linux-debug`, `conda-linux-release`, `conda-macos-debug`, `conda-macos-release`, `conda-windows-debug`, `conda-windows-release`
- **Tasks summary:**
| Task | Description |
|------|-------------|
| `pixi run configure` | CMake configure (debug) |
| `pixi run build` | Build (debug) |
| `pixi run install` | Install to build dir |
| `pixi run freecad` | Launch FreeCAD |
| `pixi run test` | C++ tests via ctest |
| `pixi run test-kindred` | Python/Kindred test suite |
## Architecture Patterns
### Signals — Use FastSignals, NOT Boost
```cpp
#include <fastsignals/signal.h>
// See src/Gui/Application.h:121-155 for signal declarations
```
All signals in `src/Gui/` use `fastsignals::signal`. Never use Boost.Signals2.
### Type Checking Across Modules
Avoid header dependencies between `src/Gui/` and `src/Mod/` by using runtime type checks:
```cpp
auto type = Base::Type::fromName("Sketcher::SketchObject");
if (obj->isDerivedFrom(type)) { ... }
```
### Python Bindings
All `FreeCADGui.*` functions go in `src/Gui/ApplicationPy.h` and `src/Gui/ApplicationPy.cpp`. Use `METH_VARARGS` only (no `METH_KEYWORDS` in this file). Do not create separate files for new Python bindings.
### KCSDK C++ Layer
The `src/Gui/SDK/` directory provides a C++ shared library (`libKCSDK`) with pybind11 bindings (`kcsdk` Python module). This is the stable C++ API for Kindred features:
- **`IPanelProvider`** — interface for registering dock panels
- **`IToolbarProvider`** — interface for toolbar registration
- **`IMenuProvider`** — interface for menu registration
- **`SDKRegistry`** — central registry for all providers
- **`ThemeEngine`** — runtime access to Catppuccin Mocha theme tokens
- **`WidgetBridge`** — Qt widget creation from Python factories
Python addons access these through `kindred_sdk.*` wrappers (e.g., `kindred_sdk.register_dock_panel()` routes through `kcsdk.register_panel()` + `kcsdk.create_panel()`). Do not use `kcsdk` directly from addons — always use `kindred_sdk`.
### Toolbar Visibility
Use `ToolBarItem::DefaultVisibility::Unavailable` to hide toolbars by default, then `ToolBarManager::setState(ForceAvailable)` to show them contextually. This pattern is proven by the Sketcher module.
The `appendToolbar` Python API accepts an optional 3rd argument: `"Visible"`, `"Hidden"`, or `"Unavailable"`.
### Editing Context System
The `EditingContextResolver` singleton (`src/Gui/EditingContext.h/.cpp`) drives the context-aware UI. It evaluates registered context definitions in priority order and activates the matching one, setting toolbar visibility and updating the `BreadcrumbToolBar`.
Built-in contexts: `sketcher.edit`, `assembly.edit`, `partdesign.feature`, `partdesign.body`, `assembly.idle`, `spreadsheet`, `empty_document`, `no_document`.
Python API (prefer `kindred_sdk` wrappers over direct `FreeCADGui` calls):
- `kindred_sdk.register_context()` — register a new context
- `kindred_sdk.register_overlay()` — conditional toolbar overlay
- `kindred_sdk.inject_commands()` — add commands to existing contexts
- `kindred_sdk.current_context()` — query active context
- `kindred_sdk.refresh_context()` — force re-evaluation
### Addon Loading
Addons in `mods/` are loaded by `src/Mod/Create/addon_loader.py`. Each addon provides a `package.xml` at the mod root with `<kindred>` extensions declaring version bounds, load priority, and dependencies. The loader discovers manifests, validates version bounds, and resolves load order via topological sort on dependencies, breaking ties by `(load_priority, name)`.
Current load order: **sdk** (0) → **solver** (10) → **gears** (40) → **datums** (45) → **silo** (60).
A `<workbench>` tag in `package.xml` is required for `InitGui.py` to be loaded, even if no actual workbench is registered. The `<subdirectory>` element within `<workbench>` specifies where `Init.py`/`InitGui.py` live relative to the mod root (e.g., `freecad` for silo, `datums` for datums, `./` for sdk).
### Deferred Initialization
GUI setup uses `QTimer.singleShot` with staggered delays. Create core (`src/Mod/Create/InitGui.py`) handles:
- 500ms: `.kc` file format registration
- 10000ms: Update checker
Each addon manages its own deferred setup in its `InitGui.py`. For example, Silo (`mods/silo/InitGui.py`) defers:
- 500ms: kindred:// URL handler
- 600ms: Document observer for .kc tree building
- 2000ms: Auth dock panel (via `kindred_sdk.register_dock_panel`)
- 2500ms: Silo overlay context
- 3000ms: First-start settings check
- 4000ms: Activity dock panel (via `kindred_sdk.register_dock_panel`)
### Unified Origin System
File operations (New, Open, Save, Commit, Pull, Push) are abstracted behind `FileOrigin` (`src/Gui/FileOrigin.h`). `LocalFileOrigin` handles local files; `SiloOrigin` (`mods/silo/freecad/silo_origin.py`) backs Silo-tracked documents. The active origin is selected automatically based on document properties (`SiloItemId`, `SiloPartNumber`).
Origins are registered via `kindred_sdk.register_origin()`. Query functions (`list_origins`, `active_origin`, `get_origin`, `set_active_origin`) route through the `kcsdk` C++ module.
## Submodules
| Path | Repository | Branch | Purpose |
|------|------------|--------|---------|
| `mods/silo` | `git.kindred-systems.com/kindred/silo-mod` | `main` | PLM workbench (includes silo-client submodule) |
| `mods/gears` | `git.kindred-systems.com/kindred/gears` | `main` | Parametric gear generation workbench |
| `mods/datums` | `git.kindred-systems.com/kindred/datums` | `main` | Unified datum creator (plane, axis, point) |
| `mods/solver` | `git.kindred-systems.com/kindred/solver` | `main` | Assembly solver research (GNN-based) |
| `src/3rdParty/OndselSolver` | `git.kindred-systems.com/kindred/solver` | — | Constraint solver (forked with NR fix) |
| `src/3rdParty/GSL` | `github.com/microsoft/GSL` | — | Guidelines Support Library |
| `src/Mod/AddonManager` | `github.com/FreeCAD/AddonManager` | — | FreeCAD addon manager |
| `tests/lib` | `github.com/google/googletest` | — | C++ test framework |
Update a submodule:
```bash
cd mods/silo
git checkout main && git pull
cd ../..
git add mods/silo
git commit -m "chore: update silo submodule"
```
Initialize all submodules: `git submodule update --init --recursive`
## Key Addon Modules
### SDK (`mods/sdk/`)
Stable API contract for addons. Python package `kindred_sdk` wraps the KCSDK C++ module, providing:
- **Editing contexts:** `register_context()`, `register_overlay()`, `inject_commands()`, `refresh_context()`
- **Origins:** `register_origin()`, `unregister_origin()`, `list_origins()`, `active_origin()`
- **Dock panels:** `register_dock_panel(object_name, title, factory, area, delay_ms)`
- **Commands:** `register_command(cmd_id, classname, pixmap, tooltip)`
- **Theme:** `get_theme_tokens()`, `load_palette()` (Catppuccin Mocha YAML palette)
- **Toolbars/Menus:** `register_toolbar()`, `register_menu()`
- **Version:** `create_version()`, `freecad_version()`
Addons should use `kindred_sdk.*` instead of `FreeCADGui.*` internals where possible.
### Gears (`mods/gears/`)
Parametric gear workbench. Provides `GearWorkbench` with gear generation and analysis tools. Code lives in `freecad/gears/` (workbench) and `pygears/` (computational library). Priority 40, depends on SDK.
### Datums (`mods/datums/`)
Unified datum creator that replaces the three stock PartDesign datum commands (Plane, Line, Point) with a single `Create_DatumCreator` command. Features:
- 16 smart creation modes (7 plane, 4 axis, 5 point)
- Auto-detection engine: selects best mode from geometry selection
- Mode override combo for manual selection
- Dynamic parameter UI (offset, angle, position, XYZ)
- Edit panel with real-time parameter updates via AttachExtension
- Injected into `partdesign.body` and `partdesign.feature` contexts via SDK
Source: `mods/datums/datums/` (command.py, core.py, panel.py, detection.py, edit_panel.py). Priority 45, depends on SDK.
### Silo (`mods/silo/`)
PLM workbench with 14 commands for parts lifecycle management. Go REST API server + PostgreSQL + MinIO backend. FreeCAD client communicates via shared `silo-client` submodule.
Structure: root-level `package.xml`/`Init.py`/`InitGui.py`, workbench code in `freecad/`, client library in `silo-client/`. All deferred setup (origin, overlay, dock panels, document observer) is self-contained in `mods/silo/InitGui.py`.
Silo origin detection: `silo_origin.py:ownsDocument()` checks for `SiloItemId`/`SiloPartNumber` properties on the active document. Origin registered via `kindred_sdk.register_origin()`.
### Solver (`mods/solver/`)
Assembly solver research addon (GNN-based). Pure Python, priority 10. Provides `KindredSolverBackend` for experimental assembly constraint solving.
## Theme
- **Canonical source:** `src/Gui/Stylesheets/KindredCreate.qss`
- The PreferencePacks copy at `src/Gui/PreferencePacks/KindredCreate/KindredCreate.qss` is **generated at build time** via `configure_file()`. Only edit the Stylesheets copy.
- Color palette: Catppuccin Mocha (26 colors + 14 semantic roles, defined in `mods/sdk/kindred_sdk/palettes/catppuccin-mocha.yaml`)
- Runtime access: `kindred_sdk.get_theme_tokens()` returns palette dict; C++ side via `ThemeEngine` in `src/Gui/SDK/`
- Default preferences: `src/Gui/PreferencePacks/KindredCreate/KindredCreate.cfg`
## Git Conventions
### Branch Names
`type/kebab-case-description`
Types: `feat/`, `fix/`, `chore/`, `docs/`, `refactor/`, `art/`
### Commit Messages
[Conventional Commits](https://www.conventionalcommits.org/):
```
type(scope): lowercase imperative description
```
| Prefix | Purpose |
|--------|---------|
| `feat:` | New feature |
| `fix:` | Bug fix |
| `chore:` | Maintenance, dependencies |
| `docs:` | Documentation only |
| `art:` | Icons, theme, visual assets |
| `refactor:` | Code restructuring |
Scopes: `solver`, `sketcher`, `editing-context`, `toolbar`, `datums`, `silo`, `gears`, `sdk`, `breadcrumb`, `gui`, `assembly`, `ci`, `theme`, or omitted.
### PR Workflow
1. Create a branch from `main`: `git checkout -b feat/my-feature main`
2. Commit with conventional commit messages
3. Push and open a PR against `main` via Gitea (or `tea pulls create`)
4. CI runs automatically on PRs
### Code Style
- **C++:** clang-format (`.clang-format`), clang-tidy (`.clang-tidy`)
- **Python:** black (100-char line length), pylint (`.pylintrc`)
- **Pre-commit hooks:** `pre-commit install` (runs clang-format, black, trailing-whitespace, etc.)
## CI/CD
- **Build:** `.gitea/workflows/build.yml` — runs on pushes to `main` and on PRs
- **Release:** `.gitea/workflows/release.yml` — triggered by `v*` tags, builds AppImage and .deb
- **Platform:** Currently Linux x86_64 only in CI; other platforms have presets but no runners yet
## Documentation
| Document | Content |
|----------|---------|
| `README.md` | Project overview, installation, usage |
| `CONTRIBUTING.md` | Branch workflow, commit conventions, code style |
| `docs/ARCHITECTURE.md` | Bootstrap flow, addon lifecycle, source layout |
| `docs/COMPONENTS.md` | Feature inventory (Silo, origin, theme, icons) |
| `docs/KNOWN_ISSUES.md` | Known issues, incomplete features, next steps |
| `docs/INTEGRATION_PLAN.md` | 5-layer architecture, phase status |
| `docs/CI_CD.md` | Build and release workflows |
| `docs/KC_SPECIFICATION.md` | .kc file format specification |
| `docs/UPSTREAM.md` | FreeCAD upstream merge strategy |
| `docs/INTER_SOLVER.md` | Assembly solver integration |
| `docs/BOM_MERGE.md` | BOM-Assembly bridge specification |
The `docs/src/` directory contains an mdBook site with detailed guides organized by topic (architecture, development, guide, reference, silo-server, solver).
## Issue Tracker
Issues are tracked on Gitea at `git.kindred-systems.com/kindred/create/issues`. Use the `tea` CLI for local interaction:
```bash
tea issues # List open issues
tea issues 123 # View issue #123 details
tea pulls create # Create a PR
```
## Known Issues and Pitfalls
1. **Silo auth not production-hardened** — LDAP/OIDC backends are coded but need infrastructure deployment
2. **No unit tests** for Silo FreeCAD commands or Go backend
3. **Assembly solver datum handling is minimal** — joints referencing datum planes/points may produce incorrect placement
4. **`Silo_BOM` requires Silo-tracked document** — unregistered documents show a warning with no registration path
5. **QSS edits** — only edit `src/Gui/Stylesheets/KindredCreate.qss`; the PreferencePacks copy is auto-generated
6. **Archived addons**`reference/ztools/` and `reference/quicknav/` are archived source for reference only; they are not built or loaded

View File

@@ -1,8 +1,8 @@
# FreeCAD Project Code of Conduct
# Kindred Create Code of Conduct
## Statement
The FreeCAD Project community covers people from a wide variety of countries,
The Kindred Create community includes people from a wide variety of countries,
backgrounds and positions. This global diversity is a great strength of the
project, but can also lead to communication issues, which may in turn cause
unhappiness. To maximise happiness of the project community taken as a whole,
@@ -55,12 +55,13 @@ further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting any of the community admins or moderators at
https://forum.freecad.org/memberlist.php?mode=team . All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
reported by opening an issue at
https://git.kindred-systems.com/kindred/create/issues or by contacting the
Kindred Systems project maintainers directly. All complaints will be reviewed
and investigated and will result in a response that is deemed necessary and
appropriate to the circumstances. The project team is obligated to maintain
confidentiality with regard to the reporter of an incident. Further details
of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other

View File

@@ -66,8 +66,10 @@ Kindred Create uses git submodules for addon workbenches:
| Submodule | Path | Repository |
|-----------|------|------------|
| ztools | `mods/ztools` | `git.kindred-systems.com/forbes/ztools` |
| silo-mod | `mods/silo` | `git.kindred-systems.com/kindred/silo-mod` |
| gears | `mods/gears` | `git.kindred-systems.com/kindred/gears` |
| datums | `mods/datums` | `git.kindred-systems.com/kindred/datums` |
| solver | `mods/solver` | `git.kindred-systems.com/kindred/solver` |
To update a submodule:
@@ -87,15 +89,9 @@ git submodule update --init --recursive
## Theme and QSS changes
The Catppuccin Mocha theme has **three QSS copies** that must be kept in sync:
The Catppuccin Mocha theme canonical source is `src/Gui/Stylesheets/KindredCreate.qss`. The copy at `src/Gui/PreferencePacks/KindredCreate/KindredCreate.qss` is **generated at build time** via `configure_file()` in CMake -- do not edit it directly.
1. `resources/preferences/KindredCreate/KindredCreate.qss` (canonical)
2. `src/Gui/Stylesheets/KindredCreate.qss`
3. `src/Gui/PreferencePacks/KindredCreate/KindredCreate.qss`
When modifying the theme, apply changes to all three files. Note that the copies have intentional differences (e.g., tree branch rendering style), so do not blindly copy between them -- apply edits individually.
See [KNOWN_ISSUES.md](docs/KNOWN_ISSUES.md) for the planned QSS consolidation.
Only edit `src/Gui/Stylesheets/KindredCreate.qss` when modifying the theme.
## Preference pack

View File

@@ -1,28 +1,37 @@
# FreeCAD Privacy Policy
# Kindred Create Privacy Policy
The FreeCAD application does not collect, transmit, share or use any Personal Data.
Kindred Create is built on FreeCAD and does not collect, transmit, share or use any Personal Data by default.
FreeCAD is community-developed Free Software. The community does not condone the unauthorized usage of private data, so our software does not gather or send personal data.
The software does not contain advertisements or trackers.
The FreeCAD website is mostly static, it does not contain any trackers, neither ours nor third-party. The website uses cookies to remember logged in status, timezone and other data related to navigating the site. The website does not contain advertisements.
## Update Checker
The software does not contain advertisements or trackers either.
Kindred Create includes an optional update checker that periodically contacts a Kindred Systems server to determine whether a newer version is available. This request transmits only the current application version. No personally identifiable information is sent. The update checker can be disabled in Preferences.
## Silo Integration
Kindred Create includes optional integration with Silo, a product lifecycle management (PLM) system developed by Kindred Systems. When connected to a Silo server:
* Authentication credentials (username and password or token) are transmitted to the configured Silo server over HTTPS.
* Document metadata (part numbers, revision history, bill of materials data) is exchanged with the Silo server as part of normal PLM operations.
* File contents are uploaded to and downloaded from the Silo server's storage backend.
All Silo communication occurs only with servers you explicitly configure. No data is sent to Kindred Systems or any third party unless you choose to connect to such a server.
## File Origins
Kindred Create's origin system tracks where documents were opened from or saved to. This metadata (file paths, origin identifiers) is stored locally within the document and in application preferences. It is not transmitted externally unless you use a remote origin such as Silo.
## Local Storage
While running and for subsequent runs, Kindred Create uses local persistent storage for logs, configuration files, cache, thumbnails, recently accessed files and other information which may contain private data. This stays on local storage.
## Caveats
FreeCAD is able to load or save files to/from remote servers (for some protocols and platforms). If you choose to load or save a remote file, your IP or other private data might be shared as part of the normal connection flow for the given protocol. This is out of our control and it is up to you to decide whether you trust a remote host.
Kindred Create is able to load or save files to/from remote servers. If you choose to load or save a remote file, your IP or other private data might be shared as part of the normal connection flow for the given protocol. This is out of our control and it is up to you to decide whether you trust a remote host.
The FreeCAD eco system includes user developed workbenches. These workbenches can be installed/updated using the Add-on Manager. The Add-on Manager retrieves workbenches from remote servers across the internet. Add-on workbenches are not checked for malicious content. It is your responsibility to decide whether you trust an add-on workbench.
Kindred Create can be used to create and run macros. These are Python scripts that can perform any action that the user can perform on a system. When running a macro from an outside source, it is your responsibility to ensure you trust the author.
FreeCAD is meant to manipulate CAD files which may contain metadata. It is your responsibility to verify the metadata contained in your files before you share them with others. These files may contain local directory paths which could reveal user names if the user name forms part of the path - as in “C:\MrsCAD\Documents\myFreeCADFile.FCstd”.
CAD files may contain metadata including local directory paths which could reveal user names if the user name forms part of the path. It is your responsibility to verify the metadata contained in your files before you share them with others.
FreeCAD can also be used to create and run macros. These are Python scripts that can perform any action that the user can perform on a system. When running a macro from an outside source, it is your responsibility to ensure you trust the author.
While running and for subsequent runs, FreeCAD uses local persistent storage for logs, configuration files, cache, thumbnails, recently accessed files and other information which may contain private data. This stays on local storage.
When reading the online version of the User Manual within FreeCAD, manual contents is requested through HTTPS connections.
FreeCAD is Free Software and therefore may be packaged by other people, who may include additional software or modify the source code. We do not vouch for these third-party packages and cannot tell you what they contain and what they do regarding your privacy. The official packages are explicitly listed in our download page.
*The above privacy policy is based on the [GIMP privacy policy](https://www.gimp.org/about/privacy.html).*
Kindred Create is Free Software and therefore may be packaged by other people, who may include additional software or modify the source code. We do not vouch for these third-party packages and cannot tell you what they contain and what they do regarding your privacy.

File diff suppressed because one or more lines are too long

View File

@@ -1,32 +0,0 @@
# Security Policy
The FreeCAD project is a FOSS (Free and Open-Source Software) project that has a community of thousands of users and
hundreds of developers worldwide. We encourage responsible reporting of security vulnerabilities that may affect users
of this software, and will endeavor to address these vulnerabilities when they are discovered.
## Bounties
FreeCAD does not have a program to pay bounties for security bugs. If you discover a vulnerability that affects a part
of the FreeCAD project (either directly in FreeCAD, in a library it depends on, or in any of the various other
subprojects such as our website, forums, etc.) we ask you to join the large community of volunteer contributors and
file a report about the issue.
Note that funds may be available from the [FreeCAD Project Association (FPA)](https://fpa.freecad.org) to pursue
security research and/or the development of fixes to any vulnerabilities discovered. However, vulnerabilities held as
hostage in demands for "bounties" will not be entertained. Contact the FPA at fpa@freecad.org for more information.
## Supported Versions
FreeCAD implements security fixes to the current release series, and to the current development on the main branch.
| Version | Supported |
|---------| ------------------ |
| 1.1dev | :white_check_mark: |
| 1.0 | :white_check_mark: |
| < 1.0 | :x: |
## Reporting a Vulnerability
To report a vulnerability use GitHub's security reporting tool:
https://github.com/FreeCAD/FreeCAD/security/advisories/new

View File

@@ -20,25 +20,26 @@ FreeCAD startup
│ └─ for each addon in order:
│ └─ exec(InitGui.py)
│ ├─ sdk (priority 0): logs "SDK loaded"
│ ├─ ztools (priority 50): schedules deferred _register() (2000ms)
│ ├─ imports ZTools commands
│ ├─ installs _ZToolsManipulator (global)
│ │ └─ injects commands into editing contexts
│ ├─ solver (priority 10): registers KindredSolverBackend
├─ gears (priority 40): registers GearsWorkbench
├─ datums (priority 45): registers Create_DatumCreator
│ └─ silo (priority 60): registers SiloWorkbench
│ └─ schedules deferred Silo overlay registration (2500ms)
│ └─ schedules deferred Silo setup (overlay, panels, observer)
├─ EditingContextResolver singleton created (MainWindow constructor)
│ ├─ registers built-in contexts (PartDesign, Sketcher, Assembly, Spreadsheet)
│ ├─ connects to signalInEdit/signalResetEdit/signalActiveDocument/signalActivateView
│ └─ BreadcrumbToolBar connected to contextChanged signal
└─ Deferred setup (QTimer):
Create core (src/Mod/Create/InitGui.py):
├─ 500ms: _register_kc_format() → .kc file format
├─ 1500ms: _register_silo_origin() → registers Silo FileOrigin
├─ 2000ms: _setup_silo_auth_panel() → "Database Auth" dock
├─ 2000ms: ZTools _register() → commands + manipulator
├─ 2500ms: Silo overlay registration → "Silo Origin" toolbar overlay
├─ 3000ms: _check_silo_first_start() → settings prompt
├─ 4000ms: _setup_silo_activity_panel() → "Database Activity" dock
└─ 10000ms: _check_for_updates() → update checker (Gitea API)
Silo addon (mods/silo/InitGui.py):
├─ 500ms: kindred:// URL handler
├─ 600ms: document observer (.kc tree building)
├─ 2000ms: auth dock panel (via kindred_sdk.register_dock_panel)
├─ 2500ms: Silo overlay registration → "Silo Origin" toolbar overlay
├─ 3000ms: first-start settings check
└─ 4000ms: activity dock panel (via kindred_sdk.register_dock_panel)
```
### Addon lifecycle
@@ -65,7 +66,7 @@ The loader (`addon_loader.py`) processes addons in this order:
5. **Load** — execute `Init.py` (console) then `InitGui.py` (GUI) for each addon
6. **Register** — populate `FreeCAD.KindredAddons` registry with addon state
Current load order: **sdk** (0) → **ztools** (50) → **silo** (60)
Current load order: **sdk** (0) → **solver** (10) → **gears** (40) → **datums** (45) → **silo** (60)
## Key source layout
@@ -98,24 +99,39 @@ mods/sdk/ [dir] Kindred addon SDK — stable API contract
│ └── catppuccin-mocha.yaml 26 colors + 14 semantic roles
└── Init.py / InitGui.py Minimal log messages
mods/ztools/ [submodule] command provider (not a workbench)
├── package.xml Manifest (priority 50, depends on sdk)
── ztools/InitGui.py Deferred command registration + _ZToolsManipulator
├── ztools/ztools/
│ ├── commands/ Datum, pattern, pocket, assembly, spreadsheet
│ ├── datums/core.py Datum creation via Part::AttachExtension
│ └── resources/ Icons (SDK theme tokens), theme utilities
└── CatppuccinMocha/ Theme preference pack (QSS)
mods/gears/ [submodule → gears.git] Gears workbench
├── package.xml Manifest (priority 40, depends on sdk)
── ... Parametric gear generation
mods/silo/ [submodule → silo-mod.git] FreeCAD workbench
── freecad/package.xml Manifest (priority 60, depends on sdk)
mods/solver/ [submodule → solver.git] Solver addon
── ... Solver plugin support
mods/datums/ [submodule → datums.git] Unified datum creator
├── package.xml Manifest (priority 45, depends on sdk)
├── Init.py / InitGui.py Bootstrap
└── datums/ Python package (command, core, panel, detection, edit_panel)
mods/silo/ [submodule → silo-mod.git] PLM workbench
├── package.xml Manifest (priority 60, depends on sdk)
├── Init.py sys.path setup for silo-client
├── InitGui.py Consolidated GUI bootstrap (workbench, panels, observer, overlay)
├── silo-client/ [submodule → silo-client.git] shared API client
│ └── silo_client/ SiloClient, SiloSettings, CATEGORY_NAMES
└── freecad/ FreeCAD workbench (Python)
├── InitGui.py SiloWorkbench + overlay registration (via SDK)
├── silo_commands.py Commands + FreeCADSiloSettings adapter
└── silo_origin.py FileOrigin backend for Silo (via SDK)
src/Gui/SDK/ KCSDK C++ shared library (libKCSDK.so)
├── KCSDKGlobal.h DLL export macros
├── Types.h Plain C++ types (ContextDef, DockArea, PanelPersistence)
├── IPanelProvider.h Abstract dock panel interface
├── WidgetBridge.h/.cpp PySide QWidget <-> C++ QWidget* (via Gui::PythonWrapper)
├── SDKRegistry.h/.cpp Singleton registry — contexts, panels, providers
└── bindings/ pybind11 module (kcsdk.so)
├── kcsdk_py.cpp Module definition — enums, functions, classes
├── PyIPanelProvider.h Trampoline for Python subclassing
└── PyProviderHolder.h GIL-safe forwarding wrapper
src/Gui/EditingContext.h/.cpp EditingContextResolver singleton + context registry
src/Gui/BreadcrumbToolBar.h/.cpp Color-coded breadcrumb toolbar (Catppuccin Mocha)
src/Gui/FileOrigin.h/.cpp FileOrigin base class + LocalFileOrigin

File diff suppressed because one or more lines are too long

View File

@@ -1,31 +1,29 @@
# Components
## ztools (command provider)
## Gears workbench
ZTools is no longer a standalone workbench. It registers as a command provider that
injects tools into the appropriate editing contexts via `WorkbenchManipulator` and
the `EditingContextResolver` system.
Gears (`mods/gears/`) is a parametric gear generation addon. Provides `GearWorkbench` with gear generation and analysis tools. Code lives in `freecad/gears/` (workbench) and `pygears/` (computational library). Priority 40, depends on SDK.
**Registered commands (9):**
---
| Command | Function | Injected Into |
|---------|----------|---------------|
| `ZTools_DatumCreator` | Create datum planes, axes, points (16 modes) | PartDesign Helper Features |
| `ZTools_DatumManager` | Manage existing datum objects | PartDesign Helper Features |
| `ZTools_EnhancedPocket` | Flip-side pocket (cut outside sketch profile) | PartDesign Modeling Features |
| `ZTools_RotatedLinearPattern` | Linear pattern with incremental rotation | PartDesign Transformation Features |
| `ZTools_AssemblyLinearPattern` | Pattern assembly components linearly | Assembly |
| `ZTools_AssemblyPolarPattern` | Pattern assembly components around axis | Assembly |
| `ZTools_SpreadsheetStyle{Bold,Italic,Underline}` | Text style toggles | Spreadsheet |
| `ZTools_SpreadsheetAlign{Left,Center,Right}` | Cell alignment | Spreadsheet |
| `ZTools_Spreadsheet{BgColor,TextColor,QuickAlias}` | Colors and alias creation | Spreadsheet |
## Datums
**Integration** via `_ZToolsManipulator` (WorkbenchManipulator) and `injectEditingCommands()`:
- Commands are injected into native workbench toolbars (PartDesign, Assembly, Spreadsheet)
- Context toolbar injections ensure commands appear when the relevant editing context is active
- PartDesign menu items inserted after `PartDesign_Boolean`
Datums (`mods/datums/`) is a unified datum creator that replaces the three stock PartDesign datum commands (Plane, Line, Point) with a single `Create_DatumCreator` command. Features:
**Datum types (7):** offset_from_face, offset_from_plane, midplane, 3_points, normal_to_edge, angled, tangent_to_cylinder. All except tangent_to_cylinder use `Part::AttachExtension` for automatic parametric updates.
- 16 smart creation modes (7 plane, 4 axis, 5 point)
- Auto-detection engine that selects the best mode from geometry selection
- Mode override combo for manual selection
- Dynamic parameter UI (offset, angle, position, XYZ)
- Edit panel with real-time parameter updates via AttachExtension
- Injected into `partdesign.body` and `partdesign.feature` contexts via SDK
Source: `mods/datums/datums/` (command.py, core.py, panel.py, detection.py, edit_panel.py). Priority 45, depends on SDK.
---
## Solver
Solver (`mods/solver/`) is an assembly solver research addon (GNN-based). Pure Python, priority 10. Provides `KindredSolverBackend` for experimental assembly constraint solving.
---
@@ -70,7 +68,7 @@ These appear in the File menu and "Origin Tools" toolbar across all workbenches
**Global integration** via the origin system in `src/Gui/`:
- File toolbar: `Std_Origin` selector widget + `Std_New`/`Std_Open`/`Std_Save` (delegate to current origin)
- Origin Tools toolbar: `Origin_Commit`/`Origin_Pull`/`Origin_Push`/`Origin_Info`/`Origin_BOM` (auto-disable by capability)
- Silo origin registered at startup by `src/Mod/Create/InitGui.py`
- Silo origin registered at startup by `mods/silo/InitGui.py` via `kindred_sdk.register_origin()`
**Dock panels:**
- Database Auth (2000ms) -- Login/logout and API token management
@@ -110,10 +108,6 @@ Notable theme customizations beyond standard Catppuccin colors:
`silo-auth.svg`, `silo-bom.svg`, `silo-commit.svg`, `silo-info.svg`, `silo-new.svg`, `silo-open.svg`, `silo-pull.svg`, `silo-push.svg`, `silo-save.svg`, `silo.svg`
### Missing icons
3 command icon names have no corresponding SVG file: `silo-tag`, `silo-rollback`, `silo-status`. The `_icon()` function returns an empty string for these, so `Silo_TagProjects`, `Silo_Rollback`, and `Silo_SetStatus` render without toolbar icons.
### Palette
All silo-* icons use the Catppuccin Mocha color scheme. See `icons/kindred/README.md` for palette specification and icon design standards.

View File

@@ -1,10 +1,10 @@
# Integration Plan
Strategy for integrating ztools and Silo as built-in addons while maintaining clear boundaries with FreeCAD core.
Strategy for integrating Kindred addons as built-in components while maintaining clear boundaries with FreeCAD core.
## Goals
1. **Native feel** -- ztools and Silo behave as first-class citizens, not bolted-on addons
1. **Native feel** -- Addons behave as first-class citizens, not bolted-on extras
2. **Clean boundaries** -- Clear separation between FreeCAD core, Kindred extensions, and addon code
3. **Minimal core modifications** -- Preserve FreeCAD's container models (Part, Body, Assembly)
4. **Maintainability** -- Easy to pull upstream FreeCAD changes without merge conflicts
@@ -18,12 +18,11 @@ Strategy for integrating ztools and Silo as built-in addons while maintaining cl
├─────────────────────────────────────────────────────────────┤
│ Layer 5: Kindred Addons (mods/) │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ ztools │ │ Silo │
│ │ Datum Creator Open/Save/Commit
│ │ Enhanced Pocket Part numbering │
│ │ Assembly Patterns│ │ Revision control│
│ Spreadsheet fmt │ │ BOM management │
│ └──────────────────┘ └──────────────────┘ │
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ │ Silo │ │ Gears Datums │ │ Solver
│ │ PLM/VCS │ │ Gear gen │ │ Datum │ │ Assembly │
│ │ BOM mgmt │ │ │ │ creator │ │ research │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
├─────────────────────────────────────────────────────────────┤
│ Layer 4: Kindred Addon SDK (mods/sdk/) │
│ ┌─────────────────────────────────────────────────────────┐│
@@ -58,7 +57,7 @@ FreeCAD's fundamental data structures: `PartDesign::Body`, `PartDesign::Pocket`,
### Layer 2: FreeCAD Python API -- use as-is
The Python API provides everything needed for feature creation, command registration, and geometry access. ZTools and Silo operate entirely through this layer. Stable FreeCAD APIs (`Gui.addCommand()`, `Gui.addWorkbench()`, `Gui.addWorkbenchManipulator()`) are called directly they do not need SDK wrappers.
The Python API provides everything needed for feature creation, command registration, and geometry access. Addons operate through this layer. Stable FreeCAD APIs (`Gui.addCommand()`, `Gui.addWorkbench()`, `Gui.addWorkbenchManipulator()`) are called directly -- they do not need SDK wrappers.
### Layer 3: Kindred Create Module -- `src/Mod/Create/`
@@ -67,9 +66,9 @@ The Create module serves two roles:
**Python bootstrap** (`Init.py`, `InitGui.py`, `addon_loader.py`):
- Scans `mods/` for `package.xml` manifests with `<kindred>` extensions
- Validates version compatibility, resolves dependencies via topological sort
- Loads addons in priority order (sdk → ztools → silo)
- Loads addons in priority order (sdk → solver → gears → datums → silo)
- Populates `FreeCAD.KindredAddons` registry for runtime introspection
- Sets up deferred Silo dock panels, origin registration, and update checker
- Sets up deferred .kc format registration and update checker
**C++ module scaffold** (`App/`, `Gui/`, `CreateGlobal.h`):
- `CreateApp.so` and `CreateGui.so` shared libraries
@@ -91,7 +90,7 @@ The SDK is loaded first (priority 0) and has no dependencies.
### Layer 5: Kindred Addons -- `mods/`
Pure Python addons with `package.xml` manifests. Self-contained with `Init.py`, `InitGui.py`, and `<kindred>` metadata. Developed and versioned independently as git submodules. Must declare `<dependency>sdk</dependency>` to use SDK APIs.
Pure Python addons with `package.xml` manifests. Self-contained with `Init.py`, `InitGui.py`, and `<kindred>` metadata. Developed and versioned independently as git submodules. Must declare `<dependency>sdk</dependency>` to use SDK APIs. Current addons: **solver** (assembly research), **gears** (parametric gear generation), **datums** (unified datum creator), **silo** (PLM).
---
@@ -101,7 +100,7 @@ Pure Python addons with `package.xml` manifests. Self-contained with `Init.py`,
**Implementation:** `src/Mod/Create/addon_loader.py` implements manifest-driven loading with dependency resolution. Replaces the original `exec()`-based approach. Each addon provides a `package.xml` with `<kindred>` extensions specifying version bounds, load priority, and dependencies.
**Default workbench:** `PartDesignWorkbench` (set in `PreferencePacks/KindredCreate/KindredCreate.cfg`). ZTools is no longer a workbench — its commands are injected into native workbench toolbars via the `EditingContextResolver` and `WorkbenchManipulator` systems.
**Default workbench:** `PartDesignWorkbench` (set in `PreferencePacks/KindredCreate/KindredCreate.cfg`).
**Registry:** `FreeCAD.KindredAddons` provides runtime introspection — `.loaded()` returns addon names and states, `.get(name)` returns manifest data, `.contexts()` lists registered editing contexts.
@@ -111,7 +110,7 @@ Pure Python addons with `package.xml` manifests. Self-contained with `Init.py`,
**Implementation:** `mods/sdk/kindred_sdk/` package with wrappers for editing contexts, theme tokens, FileOrigin registration, and dock panel creation. YAML-driven palette system (`palettes/catppuccin-mocha.yaml`) replaces hardcoded color dicts that were duplicated across 5+ locations.
**Migration:** Silo has been migrated to use the SDK (#250) — it declares `<dependency>sdk</dependency>` and uses `kindred_sdk` for overlay registration and theme tokens. ztools has **not yet been migrated**: its `package.xml` lacks `<kindred>` metadata and its `InitGui.py` does not import from `kindred_sdk`. See KNOWN_ISSUES.md next step #9.
**Migration:** Silo has been migrated to use the SDK (#250) — it declares `<dependency>sdk</dependency>` and uses `kindred_sdk` for overlay registration and theme tokens. Gears also depends on the SDK. The former ztools addon was removed from the build (#344) and archived to `reference/ztools/` (#345) as part of the UI/UX rework (#346); its commands will be reimplemented through the KCSDK system.
### Phase 1.75: C++ module scaffold -- DONE
@@ -121,17 +120,16 @@ Pure Python addons with `package.xml` manifests. Self-contained with `Init.py`,
### Phase 2: Enhanced Pocket as C++ feature -- NOT STARTED
**Goal:** Replace the Python boolean-operation workaround in `ZTools_EnhancedPocket` with a proper `Create::FlipPocket` C++ feature inheriting from `PartDesign::ProfileBased`.
**Goal:** Implement a proper `Create::FlipPocket` C++ feature inheriting from `PartDesign::ProfileBased`.
**Rationale:** The current Python implementation creates a standard Pocket then applies a boolean Common. A native feature would integrate properly with the feature tree, support undo/redo correctly, and perform better on complex geometry.
**Rationale:** The former ztools Python implementation created a standard Pocket then applied a boolean Common. A native feature would integrate properly with the feature tree, support undo/redo correctly, and perform better on complex geometry.
**Scope:**
- `src/Mod/Create/App/FeatureFlipPocket.cpp` -- Feature implementation
- `src/Mod/Create/Gui/TaskFlipPocket.cpp` -- Task panel
- `src/Mod/Create/Gui/ViewProviderFlipPocket.cpp` -- View provider
- Update `ZTools_EnhancedPocket` to create `Create::FlipPocket` instead of the workaround
**Decision:** Deferred. The Python approach is functional for current needs. Revisit when performance or feature-tree integration becomes a problem.
**Decision:** Deferred. The former Python approach was functional. Revisit as part of the KCSDK-driven command reimplementation (#346).
### Phase 3: Datum C++ helpers -- NOT STARTED (SUPERSEDED)
@@ -159,14 +157,60 @@ Theme colors are now centralized in the SDK's YAML palette (`mods/sdk/kindred_sd
### Phase 6: Build system integration -- DONE
**Goal:** CMake install rules for `mods/` submodules so packages include ztools, Silo, and SDK automatically.
**Goal:** CMake install rules for `mods/` submodules so packages include all addons automatically.
**Implementation:** `src/Mod/Create/CMakeLists.txt` includes install rules for all addons (`mods/ztools`, `mods/silo`, `mods/sdk`) and the C++ module scaffold (`App/`, `Gui/`). Build-time code generation: `version.py.in``version.py` via `configure_file()`.
**Implementation:** `src/Mod/Create/CMakeLists.txt` includes install rules for all addons (`mods/sdk`, `mods/solver`, `mods/gears`, `mods/datums`, `mods/silo`) and the C++ module scaffold (`App/`, `Gui/`). Build-time code generation: `version.py.in``version.py` via `configure_file()`.
**CI/CD status:** Release workflows (`.gitea/workflows/release.yml`) build for Linux (AppImage + .deb), macOS (DMG for Intel + Apple Silicon), and Windows (.exe NSIS installer + .7z archive). Builds run on public runners in dockerized mode. Releases are triggered by `v*` tags. See `docs/CI_CD.md` for details.
---
### Phase 7: KCSDK — C++-backed SDK module -- IN PROGRESS
**Goal:** Replace the pure-Python SDK wrappers with a C++ shared library (`libKCSDK.so`) and pybind11 bindings (`kcsdk.so`). This gives addons a stable, typed API with proper GIL safety and enables future C++ addon development without Python.
**Architecture:**
```
Python Addons (silo, future addons, ...)
|
kindred_sdk (mods/sdk/) <- convenience layer (try kcsdk, fallback FreeCADGui)
|
kcsdk.so (pybind11 module) <- C++ API bindings
|
KCSDK (C++ shared library) <- SDKRegistry + provider interfaces
|
FreeCADGui (EditingContextResolver, DockWindowManager, OriginManager, ...)
```
**Sub-phases:**
| # | Issue | Status | Description |
|---|-------|--------|-------------|
| 1 | #350 | DONE | Scaffold KCSDK library + kcsdk pybind11 module |
| 2 | #351 | DONE | Migrate editing context API to kcsdk |
| 3 | #352 | DONE | Panel provider system (IPanelProvider) |
| 4 | #353 | DONE | C++ theme engine |
| 5 | #354 | DONE | Toolbar provider system (IToolbarProvider) |
| 6 | #355 | DONE | Menu and action system |
| 7 | #356 | DONE | Status bar provider + origin migration |
| 8 | #357 | — | Deprecation cleanup + SDK v1.0.0 |
**Key files:**
- `src/Gui/SDK/` — C++ library (KCSDKGlobal.h, Types.h, SDKRegistry, IPanelProvider, WidgetBridge)
- `src/Gui/SDK/bindings/` — pybind11 module (kcsdk_py.cpp, PyIPanelProvider, PyProviderHolder)
- `mods/sdk/kindred_sdk/` — Python wrappers with kcsdk/legacy fallback
**Design decisions:**
- **No Qt in public C++ API** — `Types.h` uses `std::string`, `std::vector`, `std::function`. Qt conversion happens internally in `SDKRegistry.cpp`.
- **GIL-safe Python callables** — Python callbacks stored via `std::make_shared<py::object>` with `py::gil_scoped_acquire` before every invocation.
- **PySide widget bridging** — `WidgetBridge::toQWidget()` converts PySide QWidget objects to C++ `QWidget*` via `Gui::PythonWrapper` (Shiboken).
- **Provider pattern** — Interfaces like `IPanelProvider` enable addons to register factories. The registry calls `create_widget()` once and manages the lifecycle through `DockWindowManager`.
---
## Design decisions
1. **`Create::` namespace prefix.** All Kindred Create C++ features use this prefix to distinguish them from FreeCAD core.
@@ -175,15 +219,15 @@ Theme colors are now centralized in the SDK's YAML palette (`mods/sdk/kindred_sd
3. **Silo server distributed separately.** Users deploy the Silo server independently. Setup instructions live in `mods/silo/README.md`.
4. **Version synchronization via submodule pins.** ztools and Silo versions are pinned git submodule commits. Updates are deliberate:
4. **Version synchronization via submodule pins.** Addon versions are pinned git submodule commits. Updates are deliberate:
```bash
cd mods/ztools && git pull origin main && cd ../..
git add mods/ztools && git commit -m "Update ztools submodule"
cd mods/silo && git pull origin main && cd ../..
git add mods/silo && git commit -m "Update silo submodule"
```
5. **Python-first approach.** C++ extensions are deferred until Python cannot achieve the requirement. The AttachExtension approach for datums validated this strategy.
5. **Python-first approach.** C++ extensions are deferred until Python cannot achieve the requirement.
6. **Graceful degradation.** The addon loader skips addons that fail validation or loading. Each addon load is wrapped in try/except with console logging. The SDK, ztools, and Silo can all be absent without breaking the bootstrap.
6. **Graceful degradation.** The addon loader skips addons that fail validation or loading. Each addon load is wrapped in try/except with console logging. Any addon can be absent without breaking the bootstrap.
7. **SDK as adaptation layer.** Addons call `kindred_sdk.*` instead of `FreeCADGui.*` platform internals. This creates a single point of adaptation during upstream rebases — update the SDK wrappers, not every addon.

View File

@@ -1,90 +1,26 @@
# Known Issues
## Issues
**Last updated:** 2026-03-03
### Critical
## Silo
1. ~~**QSS duplication.**~~ Resolved. The canonical QSS lives in `src/Gui/Stylesheets/KindredCreate.qss`. The PreferencePacks copy is now generated at build time via `configure_file()` in `src/Gui/PreferencePacks/CMakeLists.txt`. The unused `resources/preferences/KindredCreate/` directory has been removed.
1. **Auth not production-hardened** -- LDAP/OIDC backends are coded but need infrastructure deployment. Currently only local (bcrypt) auth is tested in production.
2. **No unit tests** for Silo FreeCAD commands or Go backend.
3. **`Silo_BOM` requires Silo-tracked document** -- Unregistered documents show a warning with no inline registration path.
2. ~~**WorkbenchManipulator timing.**~~ Resolved. ZTools is no longer a workbench. Commands are registered via a deferred `QTimer.singleShot(2000ms)` in `InitGui.py`, which activates dependent workbenches first, then imports ZTools commands and installs the `_ZToolsManipulator`. The `EditingContextResolver` handles toolbar visibility based on editing context.
## Assembly
3. ~~**Silo shortcut persistence.**~~ Resolved. `Silo_ToggleMode` removed; file operations now delegate to the selected origin via the unified origin system.
4. **Solver datum handling is minimal** -- Joints referencing datum planes/points may produce incorrect placement. The Kindred fork carries patches to `findPlacement()` for `PartDesign::Plane`, `PartDesign::Point`, `App::Plane`, and `App::Point`, but coverage is incomplete for complex assemblies.
### High
## Theme
4. **Silo authentication not production-hardened.** Local auth (bcrypt) works end-to-end. LDAP (FreeIPA) and OIDC (Keycloak) backends are coded but depend on infrastructure not yet deployed. FreeCAD client has `Silo_Auth` dock panel for login and API token management. Server has session middleware (`alexedwards/scs`), CSRF protection (`nosurf`), and role-based access control (admin/editor/viewer). Migration `009_auth.sql` adds users, api_tokens, and sessions tables.
5. **QSS edits** -- Only edit `src/Gui/Stylesheets/KindredCreate.qss`. The PreferencePacks copy at `src/Gui/PreferencePacks/KindredCreate/KindredCreate.qss` is generated at build time via `configure_file()`.
5. **No unit tests.** Zero test coverage for ztools and Silo FreeCAD commands. Silo Go backend also lacks tests.
## Platform
6. **Assembly solver datum handling is minimal.** `UtilsAssembly.findPlacement()` handles standard shapes (faces, edges, vertices) and `App::Line` origin objects. It does not extract placement from `PartDesign::Plane` or `PartDesign::Point` datum objects — when no element is selected, it returns a default `App.Placement()`. This means assembly joints referencing datum planes/points may produce incorrect placement.
6. **CI/CD is Linux x86_64 only** -- macOS and Windows build presets are defined but no CI runners are configured.
7. **Wayland fractional scaling** -- Some HiDPI scaling issues remain on Wayland with fractional scaling factors.
### Medium
## Archived Addons
7. **`Silo_BOM` requires Silo-tracked document.** Depends on `SiloPartNumber` property. Unregistered documents show a warning with no registration path.
8. **PartDesign menu insertion fragility.** `_ZToolsPartDesignManipulator.modifyMenuBar()` inserts after `PartDesign_Boolean`. If upstream renames this command, insertions silently fail.
9. **tangent_to_cylinder falls back to manual placement.** TangentPlane MapMode requires a vertex reference not collected by the current UI.
10. ~~**`delete_bom_entry()` bypasses error normalization.**~~ Resolved. `delete_bom_entry()` uses `self._request("DELETE", ...)` which routes through `SiloClient._request()` with proper error handling.
11. ~~**Missing Silo icons.**~~ Resolved. All three icons now exist: `silo-tag.svg`, `silo-rollback.svg`, `silo-status.svg` in `mods/silo/freecad/resources/icons/`.
### Fixed (retain for reference)
12. **OndselSolver Newton-Raphson convergence.** `NewtonRaphson::isConvergedToNumericalLimit()` compared `dxNorms->at(iterNo)` to itself instead of `dxNorms->at(iterNo - 1)`. This prevented convergence detection on complex assemblies, causing solver exhaustion and "grounded object moved" warnings. Fixed in Kindred fork (`src/3rdParty/OndselSolver`). Needs upstreaming to `FreeCAD/OndselSolver`.
13. **Assembly solver crash on document restore.** `AssemblyObject::onChanged()` called `updateSolveStatus()` when the Group property changed during document restore, triggering the solver while child objects were still deserializing (SIGSEGV). Fixed with `isRestoring()` and `isPerformingTransaction()` guards at `src/Mod/Assembly/App/AssemblyObject.cpp:143`.
14. **`DlgSettingsGeneral::applyMenuIconSize` visibility.** The method was `private` but called from `StartupProcess.cpp`. Fixed by moving to `public` (PR #49). Also required `Dialog::` namespace qualifier in `StartupProcess.cpp`.
---
## Incomplete features
### Silo
| Feature | Status | Notes |
|---------|--------|-------|
| Authentication | Local auth complete | LDAP/OIDC backends coded, pending infrastructure. Auth dock panel available. |
| CSRF protection | Implemented | `nosurf` library on web form routes |
| File locking | Not implemented | Needed to prevent concurrent edits |
| Odoo ERP integration | Stub only | Returns "not yet implemented" |
| Part number date segments | Unknown | `formatDate()` reference is stale — function not found in codebase |
| Location/inventory APIs | Tables exist, no handlers | |
| CSV import rollback | Not implemented | `bom_handlers.go` |
| SSE event streaming | Implemented | Reconnect logic with exponential backoff |
| Database Activity panel | Implemented | Dock panel showing real-time server events |
| Start panel | Implemented | In-viewport start page with recent files and Silo integration |
### ztools
| Feature | Status | Notes |
|---------|--------|-------|
| Tangent-to-cylinder attachment | Manual fallback | No vertex ref in UI |
| Angled datum live editing | Incomplete | AttachmentOffset not updated in panel |
| Assembly pattern undo | Not implemented | |
---
## Next steps
1. **Authentication hardening** -- Deploy FreeIPA and Keycloak infrastructure. End-to-end test LDAP and OIDC flows. Harden token rotation and session expiry.
2. **BOM-Assembly bridge** -- Auto-populate Silo BOM from Assembly component links on save. See `docs/BOM_MERGE.md` for specification.
3. **File locking** -- Pessimistic locks on `Silo_Open` to prevent concurrent edits. Requires server-side lock table and client-side lock display.
4. ~~**Build system**~~ Done. CMake install rules in `src/Mod/Create/CMakeLists.txt` handle all `mods/` submodules.
5. **Test coverage** -- Unit tests for ztools datum creation, Silo FreeCAD commands, and Go API endpoints.
6. ~~**QSS consolidation**~~ Done. Canonical QSS is `src/Gui/Stylesheets/KindredCreate.qss`; PreferencePacks copy generated at build time via `configure_file()`.
7. **Update notification UI** -- Display in-app notification when a new release is available (issue #30). The update checker backend (`update_checker.py`) runs at startup; notification UI still needed.
8. **KC file format completion** -- Populate `silo_instance` and `revision_hash` in manifest.json. Implement write-back for history.json, approvals.json, dependencies.json. See `docs/KC_SPECIFICATION.md`.
9. **ztools SDK migration** -- Add `<kindred>` metadata to `mods/ztools/package.xml` (load priority, version bounds, SDK dependency). Migrate `InitGui.py` to use `kindred_sdk` APIs for context/overlay registration.
10. **DAG cross-item edges** -- Assembly constraints referencing geometry in child parts should populate `dag_cross_edges`. Deferred until assembly constraint model is finalized.
8. **ztools and QuickNav archived** -- These addons are in `reference/ztools/` and `reference/quicknav/` for reference only. They are not built or loaded. ZTools commands are being reimplemented through the KCSDK system and the datums addon.

View File

@@ -1,395 +0,0 @@
# DAG Client Integration Contract
**Status:** Draft
**Last Updated:** 2026-02-13
This document describes what silo-mod and Headless Create runners need to implement to integrate with the Silo dependency DAG and worker system.
---
## 1. Overview
The DAG system has two client-side integration points:
1. **silo-mod workbench** (desktop) -- pushes DAG data to Silo on save or revision create.
2. **silorunner + silo-mod** (headless) -- extracts DAGs, validates features, and exports geometry as compute jobs.
Both share the same Python codebase in the silo-mod repository. Desktop users call the code interactively; runners call it headlessly via `create --console`.
---
## 2. DAG Sync Payload
Clients push feature trees to Silo via:
```
PUT /api/items/{partNumber}/dag
Authorization: Bearer <user_token or runner_token>
Content-Type: application/json
```
### 2.1 Request Body
```json
{
"revision_number": 3,
"nodes": [
{
"node_key": "Sketch001",
"node_type": "sketch",
"properties_hash": "a1b2c3d4e5f6...",
"metadata": {
"label": "Base Profile",
"constraint_count": 12
}
},
{
"node_key": "Pad001",
"node_type": "pad",
"properties_hash": "f6e5d4c3b2a1...",
"metadata": {
"label": "Main Extrusion",
"length": 25.0
}
}
],
"edges": [
{
"source_key": "Sketch001",
"target_key": "Pad001",
"edge_type": "depends_on"
}
]
}
```
### 2.2 Field Reference
**Nodes:**
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `node_key` | string | yes | Unique within item+revision. Use Create's internal object name (e.g. `Sketch001`, `Pad003`). |
| `node_type` | string | yes | One of: `sketch`, `pad`, `pocket`, `fillet`, `chamfer`, `constraint`, `body`, `part`, `datum`. |
| `properties_hash` | string | no | SHA-256 hex digest of the node's parametric inputs. Used for memoization. |
| `validation_state` | string | no | One of: `clean`, `dirty`, `validating`, `failed`. Defaults to `clean`. |
| `metadata` | object | no | Arbitrary key-value pairs for display or debugging. |
**Edges:**
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `source_key` | string | yes | The node that is depended upon. |
| `target_key` | string | yes | The node that depends on the source. |
| `edge_type` | string | no | One of: `depends_on` (default), `references`, `constrains`. |
**Direction convention:** Edges point from dependency to dependent. If Pad001 depends on Sketch001, the edge is `source_key: "Sketch001"`, `target_key: "Pad001"`.
### 2.3 Response
```json
{
"synced": true,
"node_count": 15,
"edge_count": 14
}
```
---
## 3. Computing properties_hash
The `properties_hash` enables memoization -- if a node's inputs haven't changed since the last validation, it can be skipped. Computing it:
```python
import hashlib
import json
def compute_properties_hash(feature_obj):
"""Hash the parametric inputs of a Create feature."""
inputs = {}
if feature_obj.TypeId == "Sketcher::SketchObject":
# Hash geometry + constraints
inputs["geometry_count"] = feature_obj.GeometryCount
inputs["constraint_count"] = feature_obj.ConstraintCount
inputs["geometry"] = str(feature_obj.Shape.exportBrep())
elif feature_obj.TypeId == "PartDesign::Pad":
inputs["length"] = feature_obj.Length.Value
inputs["type"] = str(feature_obj.Type)
inputs["reversed"] = feature_obj.Reversed
inputs["sketch"] = feature_obj.Profile[0].Name
# ... other feature types
canonical = json.dumps(inputs, sort_keys=True)
return hashlib.sha256(canonical.encode()).hexdigest()
```
The exact inputs per feature type are determined by what parametric values affect the feature's geometry. Include anything that, if changed, would require recomputation.
---
## 4. Feature Tree Walking
To extract the DAG from a Create document:
```python
import FreeCAD
def extract_dag(doc):
"""Walk a Create document and return nodes + edges."""
nodes = []
edges = []
for obj in doc.Objects:
# Skip non-feature objects
if not hasattr(obj, "TypeId"):
continue
node_type = classify_type(obj.TypeId)
if node_type is None:
continue
nodes.append({
"node_key": obj.Name,
"node_type": node_type,
"properties_hash": compute_properties_hash(obj),
"metadata": {
"label": obj.Label,
"type_id": obj.TypeId,
}
})
# Walk dependencies via InList (objects this one depends on)
for dep in obj.InList:
if hasattr(dep, "TypeId") and classify_type(dep.TypeId):
edges.append({
"source_key": dep.Name,
"target_key": obj.Name,
"edge_type": "depends_on",
})
return nodes, edges
def classify_type(type_id):
"""Map Create TypeIds to DAG node types."""
mapping = {
"Sketcher::SketchObject": "sketch",
"PartDesign::Pad": "pad",
"PartDesign::Pocket": "pocket",
"PartDesign::Fillet": "fillet",
"PartDesign::Chamfer": "chamfer",
"PartDesign::Body": "body",
"Part::Feature": "part",
"Sketcher::SketchConstraint": "constraint",
}
return mapping.get(type_id)
```
---
## 5. When to Push DAG Data
Push the DAG to Silo in these scenarios:
| Event | Trigger | Who |
|-------|---------|-----|
| User saves in silo-mod | On save callback | Desktop silo-mod workbench |
| User creates a revision | After `POST /api/items/{pn}/revisions` succeeds | Desktop silo-mod workbench |
| Runner extracts DAG | After `create-dag-extract` job completes | silorunner via `PUT /api/runner/jobs/{id}/dag` |
| Runner validates | After `create-validate` job, push updated validation states | silorunner via `PUT /api/runner/jobs/{id}/dag` |
---
## 6. Runner Entry Points
silo-mod must provide these Python entry points for headless invocation:
### 6.1 silo.runner.dag_extract
Extracts the feature DAG from a Create file and writes it as JSON.
```python
# silo/runner.py
def dag_extract(input_path, output_path):
"""
Extract feature DAG from a Create file.
Args:
input_path: Path to the .kc (Kindred Create) file.
output_path: Path to write the JSON output.
Output JSON format:
{
"nodes": [...], // Same format as DAG sync payload
"edges": [...]
}
"""
doc = FreeCAD.openDocument(input_path)
nodes, edges = extract_dag(doc)
with open(output_path, 'w') as f:
json.dump({"nodes": nodes, "edges": edges}, f)
FreeCAD.closeDocument(doc.Name)
```
### 6.2 silo.runner.validate
Rebuilds all features and reports pass/fail per node.
```python
def validate(input_path, output_path):
"""
Validate a Create file by rebuilding all features.
Output JSON format:
{
"valid": true/false,
"nodes": [
{
"node_key": "Pad001",
"state": "clean", // or "failed"
"message": null, // error message if failed
"properties_hash": "..."
}
]
}
"""
doc = FreeCAD.openDocument(input_path)
doc.recompute()
results = []
all_valid = True
for obj in doc.Objects:
if not hasattr(obj, "TypeId"):
continue
node_type = classify_type(obj.TypeId)
if node_type is None:
continue
state = "clean"
message = None
if hasattr(obj, "isValid") and not obj.isValid():
state = "failed"
message = f"Feature {obj.Label} failed to recompute"
all_valid = False
results.append({
"node_key": obj.Name,
"state": state,
"message": message,
"properties_hash": compute_properties_hash(obj),
})
with open(output_path, 'w') as f:
json.dump({"valid": all_valid, "nodes": results}, f)
FreeCAD.closeDocument(doc.Name)
```
### 6.3 silo.runner.export
Exports geometry to STEP, IGES, or other formats.
```python
def export(input_path, output_path, format="step"):
"""
Export a Create file to an external format.
Args:
input_path: Path to the .kc file.
output_path: Path to write the exported file.
format: Export format ("step", "iges", "stl", "obj").
"""
doc = FreeCAD.openDocument(input_path)
import Part
shapes = [obj.Shape for obj in doc.Objects if hasattr(obj, "Shape")]
compound = Part.makeCompound(shapes)
format_map = {
"step": "STEP",
"iges": "IGES",
"stl": "STL",
"obj": "OBJ",
}
Part.export([compound], output_path)
FreeCAD.closeDocument(doc.Name)
```
---
## 7. Headless Invocation
The `silorunner` binary shells out to Create (with silo-mod installed):
```bash
# DAG extraction
create --console -e "from silo.runner import dag_extract; dag_extract('/tmp/job/part.kc', '/tmp/job/dag.json')"
# Validation
create --console -e "from silo.runner import validate; validate('/tmp/job/part.kc', '/tmp/job/result.json')"
# Export
create --console -e "from silo.runner import export; export('/tmp/job/part.kc', '/tmp/job/output.step', 'step')"
```
**Prerequisites:** The runner host must have:
- Headless Create installed (Kindred's fork of FreeCAD)
- silo-mod installed as a Create addon (so `from silo.runner import ...` works)
- No display server required -- `--console` mode is headless
---
## 8. Validation Result Handling
After a runner completes a `create-validate` job, it should:
1. Read the result JSON.
2. Push updated validation states via `PUT /api/runner/jobs/{jobID}/dag`:
```json
{
"revision_number": 3,
"nodes": [
{"node_key": "Sketch001", "node_type": "sketch", "validation_state": "clean", "properties_hash": "abc..."},
{"node_key": "Pad001", "node_type": "pad", "validation_state": "failed", "properties_hash": "def..."}
],
"edges": [
{"source_key": "Sketch001", "target_key": "Pad001"}
]
}
```
3. Complete the job via `POST /api/runner/jobs/{jobID}/complete` with the summary result.
---
## 9. SSE Events
Clients should listen for these events on `GET /api/events`:
| Event | Payload | When |
|-------|---------|------|
| `dag.updated` | `{item_id, part_number, revision_number, node_count, edge_count}` | After any DAG sync |
| `dag.validated` | `{item_id, part_number, valid, failed_count}` | After validation completes |
| `job.created` | `{job_id, definition_name, trigger, item_id}` | Job auto-triggered or manually created |
| `job.claimed` | `{job_id, runner_id, runner}` | Runner claims a job |
| `job.progress` | `{job_id, progress, message}` | Runner reports progress |
| `job.completed` | `{job_id, runner_id}` | Job finishes successfully |
| `job.failed` | `{job_id, runner_id, error}` | Job fails |
| `job.cancelled` | `{job_id, cancelled_by}` | Job cancelled by user |
---
## 10. Cross-Item Edges
For assembly constraints that reference geometry in child parts (e.g. a mate constraint between two parts), use the `dag_cross_edges` table. These edges bridge the BOM DAG and the feature DAG.
Cross-item edges are **not** included in the standard `PUT /dag` sync. They will be managed through a dedicated endpoint in a future iteration once the assembly constraint model in Create/silo-mod is finalized.
For now, the DAG sync covers intra-item dependencies only. Assembly-level interference detection uses the BOM DAG (`relationships` table) combined with per-item feature DAGs.

View File

@@ -1,26 +1,31 @@
# Kindred Create
**Last updated:** 2026-02-08
**Branch:** main @ `cf523f1d87a`
**Kindred Create:** v0.1.0
**FreeCAD base:** v1.0.0
**Last updated:** 2026-03-02
**Branch:** main @ `b0621f9731c`
**Kindred Create:** v0.1.5
**FreeCAD base:** v1.2.0
## Documentation
| Document | Contents |
|----------|----------|
| [ARCHITECTURE.md](ARCHITECTURE.md) | Bootstrap flow, source layout, submodules |
| [COMPONENTS.md](COMPONENTS.md) | ztools, Silo, Origin commands, theme, icons |
| [KNOWN_ISSUES.md](KNOWN_ISSUES.md) | Bugs, incomplete features, next steps |
| [COMPONENTS.md](COMPONENTS.md) | Silo, Gears, Origin commands, theme, icons |
| [INTEGRATION_PLAN.md](INTEGRATION_PLAN.md) | Architecture layers, integration phases |
| [CI_CD.md](CI_CD.md) | Build and release workflows |
| [INTER_SOLVER.md](INTER_SOLVER.md) | Inter-solver communication |
| [DAG_CLIENT_INTEGRATION.md](DAG_CLIENT_INTEGRATION.md) | DAG client integration (draft) |
| [BOM_MERGE.md](BOM_MERGE.md) | Bill of materials merge specification |
| [KC_SPECIFICATION.md](KC_SPECIFICATION.md) | .kc file format specification |
## Submodules
| Submodule | Path | Source | Pinned commit |
|-----------|------|--------|---------------|
| ztools | `mods/ztools` | `git.kindred-systems.com/forbes/ztools` | `3298d1c` |
| silo-mod | `mods/silo` | `git.kindred-systems.com/kindred/silo-mod` | `f9924d3` |
| silo-mod | `mods/silo` | `git.kindred-systems.com/kindred/silo-mod` | `cc6a79f` |
| gears | `mods/gears` | `git.kindred-systems.com/kindred/gears` | `1e26c39` |
| datums | `mods/datums` | `git.kindred-systems.com/kindred/datums` | — |
| solver | `mods/solver` | `git.kindred-systems.com/kindred/solver` | `cd7f66f` |
| OndselSolver | `src/3rdParty/OndselSolver` | `git.kindred-systems.com/kindred/solver` | `fe41fa3` |
| GSL | `src/3rdParty/GSL` | `github.com/microsoft/GSL` | `756c91a` |
| AddonManager | `src/Mod/AddonManager` | `github.com/FreeCAD/AddonManager` | `01e242e` |
@@ -28,4 +33,6 @@
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)).
OndselSolver is forked from `github.com/FreeCAD/OndselSolver` to carry a Newton-Raphson convergence fix.
The ztools addon was removed from the build (#344) and archived to `reference/ztools/` (#345) as part of the UI/UX rework (#346). Its commands will be reimplemented through the KCSDK system.

View File

@@ -1,332 +0,0 @@
# Kindred Create Repository State Report
**Generated:** 2026-01-31
**Branch:** main
**Parent repo:** create-0070 @ `364a7057ef`
**Submodules:**
- ztools @ `8d1f195` (ztools-0065, main)
- silo @ `c778825` (silo-0062, main)
---
## Recent Changes (This Session)
### Assembly Solver Fix
- `610fd43` — PartDesign datum planes (including ZTools datums) now work
correctly as joint references in Assembly. `findPlacement()` in
`src/Mod/Assembly/UtilsAssembly.py` extracts geometry from
`PartDesign::Plane` and `PartDesign::Point` objects instead of returning
zero placements.
### ZTools Bug Fixes
- `665bdb2` — Three fixes in the ztools submodule:
- Removed `int()` wrapper from `getStandardButtons()` (Qt6/PySide6
compatibility — `StandardButton` enum is not int-castable).
- Guarded `_setup_ztools_viewprovider()` against C++ ViewProviders
that lack a `Proxy` attribute (`PartDesign::Plane` uses
`ViewProviderDatumPlane`, pure C++).
- Changed `setEditorMode(prop, 2)` to `setPropertyStatus(prop, "Hidden")`
for persistent attachment property hiding across save/reload.
### ZTools Parametric Datums via AttachExtension
- `2c716b4` — ZTools datum planes now leverage FreeCAD's built-in
`Part::AttachExtension` for automatic parametric updates. Instead of
`MapMode = "Deactivated"` with manual placement, each datum type maps to a
vanilla `MapMode` (`FlatFace`, `ThreePointsPlane`, `NormalToEdge`, etc.)
with appropriate `AttachmentSupport` and `AttachmentOffset`. Datums update
automatically when source geometry changes on recompute.
### Stylesheet Fixes (Uncommitted — Staged)
- **Tree branch indicators**: Created `branch_closed.svg` and
`branch_open.svg` in `images_dark-light/` with Catppuccin `#cdd6f4`
chevrons. Added `QTreeView::branch` image rules for expand/collapse states.
- **Spinbox/combobox arrows**: Synced all three QSS copies so the CSS
border-triangle arrow fix (previously only in `resources/preferences/`)
now applies at runtime via `src/Gui/Stylesheets/KindredCreate.qss`.
- **Header clipping**: Added `min-height: 20px` to `QHeaderView::section`.
- **QSS sync**: All four copies (`resources/preferences/`, `src/Gui/Stylesheets/`,
`src/Gui/PreferencePacks/`, `mods/ztools/CatppuccinMocha/`) are now
byte-identical. Merged dock widget padding and spreadsheet cell editor
styling improvements that existed only in the Stylesheets copy.
### ZTools-PartDesign Merge (Uncommitted — Staged)
- `_ZToolsPartDesignManipulator` in `mods/ztools/ztools/InitGui.py` uses
`Gui.addWorkbenchManipulator()` to inject ZTools commands into PartDesign's
C++ toolbars:
- `ZTools_DatumCreator` + `ZTools_DatumManager` → "Part Design Helper Features"
- `ZTools_EnhancedPocket` → "Part Design Modeling Features"
- `ZTools_RotatedLinearPattern` → "Part Design Transformation Features"
- Same commands also inserted into the Part Design menu after `PartDesign_Boolean`.
### Silo Enhancements (Uncommitted — Staged)
- **File menu integration**: `SiloMenuManipulator` in `src/Mod/Create/InitGui.py`
injects `Silo_New`, `Silo_Open`, `Silo_Save`, `Silo_Commit`, `Silo_Pull`,
`Silo_Push`, and `Silo_BOM` into the File menu across all workbenches.
- **Toolbar toggle**: `Silo_ToggleMode` checkable command swaps `Ctrl+O/S/N`
between standard FreeCAD file commands and Silo equivalents. Appended to the
global File toolbar via the manipulator.
- **SSL certificate browsing**: Settings dialog now includes a file browser for
custom CA certificates (`SslCertPath` preference). `_get_ssl_context()` loads
the custom cert before system CAs.
- **BOM integration**: Upstream BOM feature (`Silo_BOM` command with tabbed
dialog — BOM + Where Used) merged with local changes and added to the
Create File menu manipulator.
---
## Architecture Overview
```
create-0070/ Kindred Create (FreeCAD 1.0+ fork)
├── src/
│ ├── Mod/
│ │ ├── Assembly/ Assembly solver (OndselSolver)
│ │ ├── PartDesign/ Part modeling (C++ workbench)
│ │ ├── Sketcher/ 2D constraint sketcher
│ │ ├── Create/ Kindred bootstrap module (Python)
│ │ │ └── InitGui.py Loads ztools+silo, installs manipulators
│ │ └── ... Other FreeCAD modules
│ └── Gui/
│ ├── Stylesheets/ QSS themes + arrow/branch SVGs
│ ├── PreferencePacks/ Preference pack bundles
│ └── WorkbenchManipulatorPython.cpp Menu/toolbar injection API
├── mods/
│ ├── ztools/ [submodule] ztools-0065
│ │ └── ztools/
│ │ ├── InitGui.py ZToolsWorkbench + PartDesign manipulator
│ │ └── ztools/
│ │ ├── commands/ Datum, pattern, pocket, assembly, spreadsheet
│ │ ├── datums/core.py Datum creation with AttachExtension
│ │ └── resources/ Icons, theme utilities
│ └── silo/ [submodule] silo-0062
│ └── pkg/freecad/
│ ├── InitGui.py SiloWorkbench
│ └── silo_commands.py 12 commands + SiloClient API
└── resources/
└── preferences/KindredCreate/ Canonical QSS + preference pack
```
### Integration Flow
```
FreeCAD startup
└─ src/Mod/Create/InitGui.py exec()
├─ exec(mods/ztools/ztools/InitGui.py)
│ ├─ registers ZToolsWorkbench
│ └─ installs _ZToolsPartDesignManipulator (global)
├─ exec(mods/silo/pkg/freecad/InitGui.py)
│ └─ registers SiloWorkbench
└─ QTimer.singleShot(2000):
├─ _setup_silo_menu()
│ └─ installs SiloMenuManipulator (global)
│ ├─ modifyMenuBar: Silo commands in File menu
│ └─ modifyToolBars: Silo_ToggleMode in File toolbar
├─ _check_silo_first_start()
└─ _setup_silo_activity_panel()
```
---
## Submodule Status
### ztools (ztools-0065)
| Commit | Description |
|--------|-------------|
| `8d1f195` | Add PartDesign WorkbenchManipulator and sync Catppuccin theme |
| `005348b` | Leverage FreeCAD AttachExtension for parametric datum updates |
| `0e95d1c` | Fix Qt6 StandardButton TypeError and C++ ViewProvider Proxy errors |
| `98bd444` | Fix workbench init and spreadsheet syntax errors |
**Custom commands**: 9 registered
- `ZTools_DatumCreator`, `ZTools_DatumManager`
- `ZTools_RotatedLinearPattern`
- `ZTools_EnhancedPocket`
- `ZTools_AssemblyLinearPattern`, `ZTools_AssemblyPolarPattern`
- `ZTools_SpreadsheetStyle{Bold,Italic,Underline}`, `ZTools_SpreadsheetAlign{Left,Center,Right}`, `ZTools_Spreadsheet{BgColor,TextColor,QuickAlias}`
**Datum types supported**: offset_from_face, offset_from_plane, midplane,
3_points, normal_to_edge, angled, tangent_to_cylinder
### silo (silo-0062)
| Commit | Description |
|--------|-------------|
| `c778825` | Add Silo mode toggle, SSL cert browsing, and BOM menu integration |
| `8c06899` | (upstream) Various fixes |
| `bce7d5a` | Add BOM handling and routes to API and web UI |
| `3a79d89` | feat: add BOM system with API, database repository, and FreeCAD command |
| `8e44ed2` | Fix SIGSEGV: defer document open after dialog close |
**FreeCAD commands**: 12 registered
- `Silo_Open`, `Silo_New`, `Silo_Save`, `Silo_Commit`
- `Silo_Pull`, `Silo_Push`, `Silo_Info`, `Silo_BOM`
- `Silo_TagProjects`, `Silo_Rollback`, `Silo_SetStatus`
- `Silo_Settings`, `Silo_ToggleMode`
**API surface**: 38 REST routes covering items, revisions, files, BOM,
projects, schemas, and Odoo integration stubs. See
`mods/silo/docs/REPOSITORY_STATUS.md` for full route table.
**BOM API methods** (new):
- `get_bom()`, `get_bom_expanded()`, `get_bom_where_used()`
- `add_bom_entry()`, `update_bom_entry()`, `delete_bom_entry()`
---
## Potential Issues
### Critical
1. **Three QSS copies can drift again.** The canonical source is
`resources/preferences/KindredCreate/KindredCreate.qss`. The other copies
(`src/Gui/Stylesheets/`, `src/Gui/PreferencePacks/`, `mods/ztools/CatppuccinMocha/`)
must be kept in sync manually. Consider a build step or symlinks to
eliminate duplication.
2. **WorkbenchManipulator command registration timing.** The
`_ZToolsPartDesignManipulator` appends commands by name (e.g.
`ZTools_DatumCreator`). If the ZTools workbench has not been initialized
(first activation) when the user switches to PartDesign, the commands may
not be registered yet. The `Create/InitGui.py` `exec()` loads the
workbench class but `Initialize()` only runs on first activation. The
manipulator API tolerates missing commands silently (toolbar append
returns without error), but the buttons won't appear until ZTools
initializes. Mitigation: force-activate ZToolsWorkbench during Create
module load, or defer manipulator installation.
3. **Silo toggle mode shortcut persistence.** `_swap_shortcuts()` stores
original shortcuts in a module-level dict (`_original_shortcuts`). If
FreeCAD crashes with Silo mode ON, the original shortcuts are lost and
standard commands will have no keyboard shortcuts on next launch. Consider
persisting original shortcuts to preferences.
### High
4. **Silo has no authentication.** All API endpoints are publicly accessible.
Required before multi-user deployment. See
`mods/silo/docs/REPOSITORY_STATUS.md` for full list.
5. **No unit tests for silo FreeCAD commands or ztools.** Python code has zero
test coverage. The Go backend also lacks tests per the silo status report.
6. **Assembly solver `findPlacement()` geometry extraction is minimal.** The
fix at `UtilsAssembly.py:1006` extracts placement from `obj.Shape.Faces[0]`
for `PartDesign::Plane`. This works for planar datums but does not handle
edge cases like empty shapes or non-planar datum objects.
### Medium
7. **`Silo_BOM` depends on `get_tracked_object()`** which looks for a
`SiloPartNumber` property on document objects. Documents not registered
with Silo show a warning dialog. The UX could be improved with a
one-click registration flow from the BOM dialog.
8. **`_ZToolsPartDesignManipulator.modifyMenuBar()` inserts after
`PartDesign_Boolean`.** If upstream FreeCAD renames or removes this
command, the menu insertions silently fail. The toolbar appends (by
toolbar name) are more robust.
9. **ZTools `plane_tangent_to_cylinder` falls back to manual placement**
because the TangentPlane MapMode requires a vertex reference not currently
collected by the UI. This is the only datum type that does not benefit
from automatic parametric updates.
10. **Silo `delete_bom_entry()` uses raw `urllib.request`** instead of the
`_request()` helper method on `SiloClient`. This bypasses error
normalization. Should be refactored to use `self._request("DELETE", ...)`.
---
## Feature Stubs and TODOs
### From silo REPOSITORY_STATUS
| Feature | Status | Location |
|---------|--------|----------|
| Odoo ERP integration | Stub (returns "not yet implemented") | `internal/odoo/` |
| Part number date segments | Broken (`formatDate()` returns error) | `internal/partnum/generator.go:102` |
| Location/Inventory APIs | Tables exist, no handlers | `migrations/001_initial.sql` |
| File locking | Not implemented | — |
| Authentication/Authorization | Not implemented | — |
| CSRF protection | Not implemented | Web UI only |
| CSV import transaction rollback | Not implemented | `bom_handlers.go` |
### From ztools
| Feature | Status | Location |
|---------|--------|----------|
| Tangent-to-cylinder attachment | Manual fallback (no vertex ref) | `datums/core.py` |
| Angled datum live editing | AttachmentOffset rotation not updated in panel | `datum_viewprovider.py` |
| Assembly pattern undo support | Not implemented | `assembly_pattern_commands.py` |
### From Create integration plan
| Phase | Feature | Status |
|-------|---------|--------|
| 1 | Addon auto-loading | Done (`src/Mod/Create/InitGui.py`) |
| 2 | Enhanced Pocket as C++ feature | Not started |
| 3 | Datum C++ helpers | Not started (Python AttachExtension approach used instead) |
| 4 | Theme system refinement | Partially done (QSS synced, not moved to Create module) |
| 5 | Silo deep integration | Done (manipulator-based menu/toolbar injection) |
| 6 | Build system integration | Not started |
---
## File Change Summary (Uncommitted)
### Parent repo (create-0070)
| File | Change |
|------|--------|
| `resources/preferences/KindredCreate/KindredCreate.qss` | Branch indicators, header min-height, dock/actiongroup improvements, spreadsheet cell editor |
| `src/Gui/Stylesheets/KindredCreate.qss` | Synced with canonical |
| `src/Gui/PreferencePacks/KindredCreate/KindredCreate.qss` | Synced with canonical |
| `src/Gui/Stylesheets/images_dark-light/branch_closed.svg` | New: right-pointing chevron |
| `src/Gui/Stylesheets/images_dark-light/branch_open.svg` | New: down-pointing chevron |
| `src/Mod/Create/InitGui.py` | Expanded SiloMenuManipulator (BOM, full commands, toolbar toggle) |
| `mods/silo` | Submodule pointer updated |
| `mods/ztools` | Submodule pointer updated |
### ztools submodule (committed + pushed)
| File | Change |
|------|--------|
| `ztools/InitGui.py` | Added `_ZToolsPartDesignManipulator` |
| `CatppuccinMocha/CatppuccinMocha.qss` | Synced with canonical KindredCreate.qss |
### silo submodule (committed + pushed)
| File | Change |
|------|--------|
| `pkg/freecad/silo_commands.py` | SSL cert browsing, `Silo_ToggleMode`, BOM merge |
| `pkg/freecad/InitGui.py` | Added `Silo_ToggleMode` to toolbar |
---
## Silo Integration Path
Based on `mods/silo/docs/REPOSITORY_STATUS.md` and the existing
`docs/INTEGRATION_PLAN.md`, the integration path forward is:
### Completed
- Addon auto-loading via `src/Mod/Create/InitGui.py`
- Global File menu injection via `WorkbenchManipulator`
- Toolbar toggle for Silo mode (shortcut swapping)
- SSL certificate configuration for internal CAs
- BOM command integrated into File menu
### Next Steps
1. **Authentication** — LDAP/FreeIPA integration for multi-user. Silo server
needs auth middleware; FreeCAD client needs credential storage in preferences.
2. **BOM-Assembly bridge** — Auto-populate Silo BOM from FreeCAD Assembly
component links. When a user saves an assembly, extract child part numbers
from `Assembly_InsertLink` objects and sync to Silo BOM.
3. **File locking** — Pessimistic locks on checkout (`Silo_Open`) to prevent
concurrent edits. Requires server-side lock table and client-side
lock-status display.
4. **Status bar integration** — Show current Silo item part number, revision,
and sync status in FreeCAD's status bar. Use
`FreeCADGui.getMainWindow().statusBar()`.
5. **Build system** — CMake install rules for `mods/` submodules so `.deb`
packages include ztools and silo without manual intervention.

View File

@@ -37,9 +37,11 @@ These files/directories exist only in Kindred Create and can be copied directly
| Directory | Description |
|-----------|-------------|
| `icons/kindred/` | 1444 Catppuccin Mocha SVG icon overrides |
| `mods/silo/` | Silo workbench submodule (`silo-mod`) |
| `mods/ztools/` | ZTools workbench submodule |
| `src/Mod/Create/` | Kindred Create workbench (InitGui.py, kc_format.py, update_checker.py, version.py.in) |
| `mods/silo/` | Silo PLM workbench submodule (`silo-mod`) |
| `mods/gears/` | Gears workbench submodule |
| `mods/datums/` | Datums addon submodule |
| `mods/solver/` | Solver addon submodule |
| `src/Mod/Create/` | Kindred Create module (InitGui.py, kc_format.py, update_checker.py, version.py.in) |
| `package/` | rattler-build packaging recipe |
| `docs/` | Kindred documentation (architecture, specifications, guides) |
| `.gitea/` | CI workflows, issue templates, runner configs |
@@ -194,7 +196,7 @@ Files touched: `src/Mod/Assembly/App/AssemblyObject.cpp`, `src/Mod/Assembly/Util
| `CMakeLists.txt` | Re-apply Kindred version variables on top of upstream 1.2.0-dev |
| `package/rattler-build/recipe.yaml` | Update for new version, verify dependency pins |
| `.gitea/workflows/` | Verify CI still works with updated source |
| Submodule pins | Re-add `mods/silo`, `mods/ztools` submodules |
| Submodule pins | Re-add `mods/silo`, `mods/gears`, `mods/datums`, `mods/solver` submodules |
| `src/3rdParty/OndselSolver` | Verify our fork is still needed or if upstream fixed the issues |
---

View File

@@ -10,17 +10,14 @@
- [Installation](./guide/installation.md)
- [Building from Source](./guide/building.md)
- [Workbenches](./guide/workbenches.md)
- [ztools](./guide/ztools.md)
- [Silo](./guide/silo.md)
- [Document Templates](./guide/templates.md)
# Architecture
- [Overview](./architecture/overview.md)
- [Python as Source of Truth](./architecture/python-source-of-truth.md)
- [Silo Server](./architecture/silo-server.md)
- [Signal Architecture](./architecture/signal-architecture.md)
- [KCSolve: Pluggable Solver](./architecture/ondsel-solver.md)
- [Bootstrap and Addon Loading](./reference/create-module-bootstrap.md)
- [KCSDK C++ Layer](./reference/kcsdk-python.md)
- [KCSolve: Pluggable Solver](./reference/kcsolve-python.md)
# Development
@@ -30,10 +27,10 @@
- [Build System](./development/build-system.md)
- [Gui Module Build](./development/gui-build-integration.md)
- [Package.xml Schema Extensions](./development/package-xml-schema.md)
- [Writing an Addon](./development/writing-an-addon.md)
# Silo Server
- [Overview](./silo-server/overview.md)
- [Specification](./silo-server/SPECIFICATION.md)
- [Configuration](./silo-server/CONFIGURATION.md)
- [Deployment](./silo-server/DEPLOYMENT.md)
@@ -45,7 +42,9 @@
- [Component Audit](./silo-server/COMPONENT_AUDIT.md)
- [Status System](./silo-server/STATUS.md)
- [Gap Analysis](./silo-server/GAP_ANALYSIS.md)
- [Frontend Spec](./silo-server/frontend-spec.md)
- [Modules](./silo-server/MODULES.md)
- [Projects and Approvals](./silo-server/PROJECTS_AND_APPROVALS.md)
- [Workers](./silo-server/WORKERS.md)
- [Installation](./silo-server/INSTALL.md)
- [Solver Service](./silo-server/SOLVER.md)
- [Roadmap](./silo-server/ROADMAP.md)
@@ -76,4 +75,5 @@
- [OriginSelectorWidget](./reference/cpp-origin-selector-widget.md)
- [FileOriginPython Bridge](./reference/cpp-file-origin-python.md)
- [Creating a Custom Origin (C++)](./reference/cpp-custom-origin-guide.md)
- [KCSDK Python API](./reference/kcsdk-python.md)
- [KCSolve Python API](./reference/kcsolve-python.md)

View File

@@ -1,132 +0,0 @@
# KCSolve: Pluggable Solver Architecture
KCSolve is the pluggable assembly constraint solver framework for Kindred Create. It defines an abstract solver interface (`IKCSolver`) and a runtime registry (`SolverRegistry`) that lets the Assembly module work with any conforming solver backend. The default backend wraps OndselSolver via `OndselAdapter`.
- **Library:** `src/Mod/Assembly/Solver/` (builds `libKCSolve.so`)
- **Python module:** `src/Mod/Assembly/Solver/bindings/` (builds `kcsolve.so`)
- **Tests:** `tests/src/Mod/Assembly/Solver/` (C++), `src/Mod/Assembly/AssemblyTests/TestKCSolvePy.py` (Python)
## Architecture
```
┌──────────────────────────────────────────────────┐
│ Assembly Module (AssemblyObject.cpp) │
│ Builds SolveContext from FreeCAD document, │
│ calls solver via SolverRegistry │
├──────────────────────────────────────────────────┤
│ SolverRegistry (singleton) │
│ register_solver(), get(), available() │
│ Plugin discovery via scan() / scan_default_paths │
├──────────────┬───────────────────────────────────┤
│ OndselAdapter │ Python solvers │ Future plugins │
│ (C++ built-in)│ (via kcsolve) │ (.so plugins) │
└──────────────┴───────────────────────────────────┘
```
The Assembly module never references OndselSolver directly. All solver access goes through `SolverRegistry::instance().get()`, which returns a `std::unique_ptr<IKCSolver>`.
## IKCSolver interface
A solver backend implements `IKCSolver` (defined in `IKCSolver.h`). Only three methods are pure virtual; all others have sensible defaults:
| Method | Required | Purpose |
|--------|----------|---------|
| `name()` | yes | Human-readable solver name |
| `supported_joints()` | yes | List of `BaseJointKind` values the solver handles |
| `solve(ctx)` | yes | Solve for static equilibrium |
| `update(ctx)` | no | Incremental re-solve after parameter changes |
| `pre_drag(ctx, parts)` | no | Begin interactive drag session |
| `drag_step(placements)` | no | One mouse-move during drag |
| `post_drag()` | no | End drag session |
| `run_kinematic(ctx)` | no | Run kinematic simulation |
| `num_frames()` | no | Frame count after simulation |
| `update_for_frame(i)` | no | Retrieve frame placements |
| `diagnose(ctx)` | no | Detect redundant/conflicting constraints |
| `is_deterministic()` | no | Whether output is reproducible (default: true) |
| `export_native(path)` | no | Write solver-native debug file (e.g. ASMT) |
| `supports_bundle_fixed()` | no | Whether solver handles Fixed-joint bundling internally |
## Core types
All types live in `Types.h` with no FreeCAD dependencies, making the header standalone for future server/worker use.
**Transform** -- position `[x, y, z]` + unit quaternion `[w, x, y, z]`. Equivalent to `Base::Placement` but independent. Note the quaternion convention differs from `Base::Rotation` which uses `(x, y, z, w)` ordering; the adapter layer handles the swap.
**BaseJointKind** -- 24 primitive constraint types decomposed from FreeCAD's `JointType` and `DistanceType` enums. Covers point constraints (Coincident, PointOnLine, PointInPlane), axis/surface constraints (Concentric, Tangent, Planar), kinematic joints (Fixed, Revolute, Cylindrical, Slider, Ball, Screw, Universal), mechanical elements (Gear, RackPinion), distance variants, and a `Custom` extension point.
**SolveContext** -- complete solver input: parts (with placements, mass, grounded flag), constraints (with markers, parameters, limits), optional motion definitions and simulation parameters.
**SolveResult** -- solver output: status code, updated part placements, DOF count, constraint diagnostics, and simulation frame count.
## SolverRegistry
Thread-safe singleton managing solver backends:
```cpp
auto& reg = SolverRegistry::instance();
// Registration (at module init)
reg.register_solver("ondsel", []() {
return std::make_unique<OndselAdapter>();
});
// Retrieval
auto solver = reg.get(); // default solver
auto solver = reg.get("ondsel"); // by name
// Queries
reg.available(); // ["ondsel", ...]
reg.joints_for("ondsel"); // [Fixed, Revolute, ...]
reg.set_default("ondsel");
```
Plugin discovery scans directories for shared libraries exporting `kcsolve_api_version()` and `kcsolve_create()`. Default paths: `KCSOLVE_PLUGIN_PATH` env var and `<prefix>/lib/kcsolve/`.
## OndselAdapter
The built-in solver backend wrapping OndselSolver's Lagrangian constraint formulation. Registered as `"ondsel"` at Assembly module initialization.
Supports all 24 joint types. The adapter translates between `SolveContext`/`SolveResult` and OndselSolver's internal `ASMTAssembly` representation, including:
- Part placement conversion (Transform <-> Base::Placement quaternion ordering)
- Constraint parameter mapping (BaseJointKind -> OndselSolver joint classes)
- Interactive drag protocol (pre_drag/drag_step/post_drag)
- Kinematic simulation (run_kinematic/num_frames/update_for_frame)
- Constraint diagnostics (redundancy detection via MbD system)
## Python bindings (kcsolve module)
The `kcsolve` pybind11 module exposes the full C++ API to Python. See [KCSolve Python API](../reference/kcsolve-python.md) for details.
Key capabilities:
- All enums, structs, and classes accessible from Python
- Subclass `IKCSolver` in pure Python to create new solver backends
- Register Python solvers at runtime via `kcsolve.register_solver()`
- Query the registry from the FreeCAD console
## File layout
```
src/Mod/Assembly/Solver/
├── Types.h # Enums and structs (no FreeCAD deps)
├── IKCSolver.h # Abstract solver interface
├── SolverRegistry.h/cpp # Singleton registry + plugin loading
├── OndselAdapter.h/cpp # OndselSolver wrapper
├── KCSolveGlobal.h # DLL export macros
├── CMakeLists.txt # Builds libKCSolve.so
└── bindings/
├── PyIKCSolver.h # pybind11 trampoline for Python subclasses
├── kcsolve_py.cpp # Module definition (enums, structs, classes)
└── CMakeLists.txt # Builds kcsolve.so (pybind11 module)
```
## Testing
- **18 C++ tests** (`KCSolve_tests_run`) covering SolverRegistry (8 tests) and OndselAdapter (10 tests including drag protocol and redundancy diagnosis)
- **16 Python tests** (`TestKCSolvePy`) covering module import, type bindings, registry functions, Python solver subclassing, and full register/load/solve round-trips
- **6 Python integration tests** (`TestSolverIntegration`) testing solver behavior through FreeCAD document objects
## Related
- [KCSolve Python API Reference](../reference/kcsolve-python.md)
- [INTER_SOLVER.md](../../INTER_SOLVER.md) -- full architecture specification

View File

@@ -1,78 +0,0 @@
# Architecture Overview
Kindred Create is structured as a thin integration layer on top of FreeCAD. The design follows three principles:
1. **Minimal core modifications** — prefer submodule addons over patching FreeCAD internals
2. **Graceful degradation** — Create runs without ztools or Silo if submodules are missing
3. **Pure Python addons** — workbenches follow FreeCAD's standard addon pattern
## Three-layer model
```
┌─────────────────────────────────┐
│ FreeCAD Documents (.FCStd) │ Python source of truth
│ Workbench logic (Python) │
├─────────────────────────────────┤
│ PostgreSQL │ Silo metadata, revisions, BOM
├─────────────────────────────────┤
│ MinIO (S3-compatible) │ Binary file storage cache
└─────────────────────────────────┘
```
FreeCAD documents are the authoritative representation of CAD data. Silo's PostgreSQL database stores metadata (part numbers, revisions, BOM relationships) and MinIO stores the binary `.FCStd` files. The FreeCAD workbench synchronizes between local files and the server.
## Source layout
```
create/
├── src/App/ # Core application (C++)
├── src/Base/ # Foundation classes (C++)
├── src/Gui/ # GUI framework (C++ + Qt6 + QSS)
│ ├── Stylesheets/ # KindredCreate.qss theme
│ ├── PreferencePacks/ # Theme preference pack
│ ├── Icons/ # silo-*.svg origin icons
│ ├── FileOrigin.* # Abstract file origin interface
│ └── OriginManager.* # Origin lifecycle management
├── src/Mod/ # ~37 FreeCAD modules
│ ├── Create/ # Kindred bootstrap module
│ ├── Assembly/ # Assembly workbench (Kindred patches)
│ ├── PartDesign/ # Part Design (stock + ztools injection)
│ └── ... # Other stock FreeCAD modules
├── mods/
│ ├── ztools/ # Datum/pattern/pocket workbench (submodule)
│ └── silo/ # Parts database workbench (submodule)
└── src/3rdParty/
├── OndselSolver/ # Assembly solver (submodule)
└── GSL/ # Guidelines Support Library (submodule)
```
## Bootstrap sequence
1. FreeCAD core initializes, discovers `src/Mod/Create/`
2. `Init.py` runs `setup_kindred_addons()` — adds `mods/ztools/ztools` and `mods/silo/freecad` to `sys.path`, executes their `Init.py`
3. GUI phase: `InitGui.py` runs `setup_kindred_workbenches()` — executes addon `InitGui.py` files to register workbenches
4. Deferred QTimer cascade:
- **1500ms** — Register Silo as a file origin
- **2000ms** — Dock the Silo auth panel
- **3000ms** — Check for Silo first-start configuration
- **4000ms** — Dock the Silo activity panel
- **10000ms** — Check for application updates
The QTimer cascade exists because FreeCAD's startup is not fully synchronous — Silo registration must wait for the GUI framework to be ready.
## Origin system
The origin system is Kindred's primary addition to FreeCAD's GUI layer:
- **`FileOrigin`** — abstract C++ interface for file backends
- **`LocalFileOrigin`** — default implementation (local filesystem)
- **`SiloOrigin`** — Silo database backend (registered by the Python addon)
- **`OriginManager`** — manages origin lifecycle, switching, capability queries
- **`OriginSelectorWidget`** — dropdown in the File toolbar
- **`CommandOrigin.cpp`** — Commit / Pull / Push / Info / BOM commands that delegate to the active origin
## Module interaction
- **ztools** injects commands into PartDesign via `_ZToolsPartDesignManipulator`
- **Silo** registers as a `FileOrigin` backend via `silo_origin.register_silo_origin()`
- **Create module** is glue only — no feature code, just bootstrap and version management

View File

@@ -1,26 +0,0 @@
# Python as Source of Truth
In Kindred Create's architecture, FreeCAD documents (`.FCStd` files) are the authoritative representation of all CAD data. The Silo database and MinIO storage are caches and metadata indexes — they do not define the model.
## How it works
An `.FCStd` file is a ZIP archive containing:
- XML documents describing the parametric model tree
- BREP geometry files for each shape
- Thumbnail images
- Embedded spreadsheets and metadata
When a user runs **Commit**, the workbench uploads the entire `.FCStd` file to MinIO and records metadata (part number, revision, timestamp, commit message) in PostgreSQL. When a user runs **Pull**, the workbench downloads the `.FCStd` from MinIO and opens it locally.
## Why this design
- **No data loss** — the complete model is always in the `.FCStd` file, never split across systems
- **Offline capability** — engineers can work without a server connection
- **FreeCAD compatibility** — files are standard FreeCAD documents, openable in stock FreeCAD
- **Simple sync model** — the unit of transfer is always a whole file, avoiding merge conflicts in binary geometry
## Trade-offs
- **Large files** — `.FCStd` files can grow large with complex assemblies; every commit stores the full file
- **No partial sync** — you cannot pull a single feature or component; it is all or nothing
- **Conflict resolution** — two users editing the same file must resolve conflicts manually (last-commit-wins by default)

View File

@@ -1,274 +0,0 @@
# Signal Architecture
Kindred Create uses two signal systems side by side: **fastsignals** for domain and application events, and **Qt signals** for UI framework integration. This page explains why both exist, where each is used, and the patterns for working with them.
## Why two signal systems
Qt signals require the `Q_OBJECT` macro, MOC preprocessing, and a `QObject` base class. This makes them the right tool for widget-to-widget communication but a poor fit for domain classes like `FileOrigin`, `OriginManager`, and `App::Document` that are not `QObject` subclasses and should not depend on the Qt meta-object system.
fastsignals is a header-only C++ library that provides type-safe signals with lambda support, RAII connection management, and no build-tool preprocessing. It lives at `src/3rdParty/FastSignals/` and is linked into the Gui module via CMake.
| | Qt signals | fastsignals |
|---|-----------|-------------|
| **Requires** | `Q_OBJECT`, MOC, `QObject` base | Nothing (header-only) |
| **Dispatch** | Event loop (can be queued) | Synchronous, immediate |
| **Connection** | `QObject::connect()` | `signal.connect(lambda)` |
| **Lifetime** | Tied to `QObject` parent-child tree | `scoped_connection` RAII |
| **Thread model** | Can queue across threads | Fires on emitter's thread |
| **Slot types** | Slots, lambdas, `std::function` | Lambdas, `std::function`, function pointers |
## Where each is used
### fastsignals — domain events
These are events about application state that multiple independent listeners may care about. The emitting class is not a `QObject`.
**FileOrigin** (`src/Gui/FileOrigin.h`):
```cpp
fastsignals::signal<void(ConnectionState)> signalConnectionStateChanged;
```
**OriginManager** (`src/Gui/OriginManager.h`):
```cpp
fastsignals::signal<void(const std::string&)> signalOriginRegistered;
fastsignals::signal<void(const std::string&)> signalOriginUnregistered;
fastsignals::signal<void(const std::string&)> signalCurrentOriginChanged;
fastsignals::signal<void(App::Document*, const std::string&)> signalDocumentOriginChanged;
```
**Gui::Document** (`src/Gui/Document.h`):
```cpp
mutable fastsignals::signal<void(const ViewProviderDocumentObject&)> signalNewObject;
mutable fastsignals::signal<void(const ViewProviderDocumentObject&)> signalDeletedObject;
mutable fastsignals::signal<void(const ViewProviderDocumentObject&,
const App::Property&)> signalChangedObject;
// ~9 more document-level signals
```
**Gui::Application** (`src/Gui/Application.h`):
```cpp
fastsignals::signal<void(const Gui::Document&, bool)> signalNewDocument;
fastsignals::signal<void(const Gui::Document&)> signalDeleteDocument;
fastsignals::signal<void(const Gui::ViewProvider&)> signalNewObject;
// ~7 more application-level signals
```
### Qt signals — UI interaction
These are events between Qt widgets, actions, and framework components where `QObject` is already the base class and the Qt event loop handles dispatch.
**OriginSelectorWidget** (a `QToolButton` subclass):
```cpp
// QActionGroup::triggered → onOriginActionTriggered
connect(m_originActions, &QActionGroup::triggered,
this, &OriginSelectorWidget::onOriginActionTriggered);
// QAction::triggered → onManageOriginsClicked
connect(m_manageAction, &QAction::triggered,
this, &OriginSelectorWidget::onManageOriginsClicked);
```
### The boundary
The pattern is consistent: **fastsignals for observable state changes in domain classes, Qt signals for UI framework plumbing**. A typical flow crosses the boundary once:
```
OriginManager emits signalCurrentOriginChanged (fastsignals)
→ OriginSelectorWidget::onCurrentOriginChanged (lambda listener)
→ updates QToolButton text/icon (Qt API calls)
```
## fastsignals API
### Declaring a signal
Signals are public member variables with a template signature:
```cpp
fastsignals::signal<void(const std::string&)> signalSomethingHappened;
```
Use `mutable` if the signal needs to fire from `const` methods (as `Gui::Document` does):
```cpp
mutable fastsignals::signal<void(int)> signalReadOnlyEvent;
```
Name signals with the `signal` prefix by convention.
### Emitting
Call the signal like a function:
```cpp
signalSomethingHappened("hello");
```
All connected slots execute **synchronously**, in connection order, before the call returns. There is no event loop queuing.
### Connecting
`signal.connect()` accepts any callable and returns a connection object:
```cpp
auto conn = mySignal.connect([](const std::string& s) {
Base::Console().log("Got: %s\n", s.c_str());
});
```
### Connection types
| Type | RAII | Copyable | Use case |
|------|------|----------|----------|
| `fastsignals::connection` | No | Yes | Long-lived, manually managed |
| `fastsignals::scoped_connection` | Yes | No (move only) | Member variable, automatic cleanup |
| `fastsignals::advanced_connection` | No | No | Temporary blocking via `shared_connection_block` |
`scoped_connection` is the standard choice for class members. It disconnects automatically when destroyed.
### Disconnecting
```cpp
conn.disconnect(); // Explicit
// or let scoped_connection destructor handle it
```
## Connection patterns
### Pattern 1: Scoped member connections
The most common pattern. Store `scoped_connection` as a class member and connect in the constructor or an `attach()` method.
```cpp
class MyListener {
fastsignals::scoped_connection m_conn;
public:
MyListener(OriginManager* mgr)
{
m_conn = mgr->signalOriginRegistered.connect(
[this](const std::string& id) { onRegistered(id); }
);
}
// Destructor auto-disconnects via m_conn
~MyListener() = default;
private:
void onRegistered(const std::string& id) { /* ... */ }
};
```
### Pattern 2: Explicit disconnect in destructor
`OriginSelectorWidget` disconnects explicitly before destruction to prevent any signal delivery during teardown:
```cpp
OriginSelectorWidget::~OriginSelectorWidget()
{
disconnectSignals(); // m_conn*.disconnect()
}
```
This is defensive — `scoped_connection` would disconnect on its own, but explicit disconnection avoids edge cases where a signal fires between member destruction order.
### Pattern 3: DocumentObserver base class
`Gui::DocumentObserver` wraps ~10 document signals behind virtual methods:
```cpp
class DocumentObserver {
using Connection = fastsignals::scoped_connection;
Connection connectDocumentCreatedObject;
Connection connectDocumentDeletedObject;
// ...
void attachDocument(Document* doc); // connects all
void detachDocument(); // disconnects all
};
```
Subclasses override `slotCreatedObject()`, `slotDeletedObject()`, etc. This pattern avoids repeating connection boilerplate in every document listener.
### Pattern 4: Temporary blocking
`advanced_connection` supports blocking a slot without disconnecting:
```cpp
auto conn = signal.connect(handler, fastsignals::advanced_tag());
fastsignals::shared_connection_block block(conn, true); // blocked
signal(); // handler does NOT execute
block.unblock();
signal(); // handler executes
```
Use this to prevent recursive signal handling.
## Thread safety
### What is thread-safe
- **Emitting** a signal from any thread (internal spin mutex protects the slot list).
- **Connecting and disconnecting** from any thread, even while slots are executing on another thread.
### What is not thread-safe
- **Accessing the same `connection` object** from multiple threads. Protect with your own mutex or keep connection objects thread-local.
- **Slot execution context.** Slots run on the emitter's thread. If a fastsignal fires on a background thread and the slot touches Qt widgets, you must marshal to the main thread:
```cpp
mgr->signalOriginRegistered.connect([this](const std::string& id) {
QMetaObject::invokeMethod(this, [this, id]() {
// Now on the main thread — safe to update UI
updateUI(id);
}, Qt::QueuedConnection);
});
```
In practice, all origin and document signals in Kindred Create fire on the main thread, so this marshalling is not currently needed. It would become necessary if background workers emitted signals.
## Performance
- **Emission:** O(n) where n = connected slots. No allocation, no event loop overhead.
- **Connection:** O(1) with spin mutex.
- **Memory:** Each signal stores a shared pointer to a slot vector. Each `scoped_connection` is ~16 bytes.
- fastsignals is rarely a bottleneck. Profile before optimising signal infrastructure.
## Adding a new signal
1. Declare the signal as a public member (or `mutable` if emitting from const methods).
2. Name it with the `signal` prefix.
3. Emit it at the appropriate point in your code.
4. Listeners store `scoped_connection` members and connect via lambdas.
5. Document the signal's signature and when it fires.
Do not create a fastsignal for single-listener scenarios — a direct method call is simpler.
## Common mistakes
**Dangling `this` capture.** If a lambda captures `this` and the object is destroyed before the connection, the next emission crashes. Always store the connection as a `scoped_connection` member so it disconnects on destruction.
**Assuming queued dispatch.** fastsignals are synchronous. A slot that blocks will block the emitter. Keep slots fast or offload work to a background thread.
**Forgetting `mutable`.** If you need to emit from a `const` method, the signal member must be `mutable`. Otherwise the compiler rejects the call.
**Copying `scoped_connection`.** It is move-only. Use `std::move()` when putting connections into containers:
```cpp
std::vector<fastsignals::scoped_connection> conns;
conns.push_back(std::move(conn)); // OK
```
## See also
- [OriginManager](../reference/cpp-origin-manager.md) — signal catalog for origin lifecycle events
- [FileOrigin Interface](../reference/cpp-file-origin.md) — `signalConnectionStateChanged`
- [OriginSelectorWidget](../reference/cpp-origin-selector-widget.md) — listener patterns in practice
- `src/3rdParty/FastSignals/` — library source and headers

View File

@@ -1,11 +0,0 @@
# Silo Server
The Silo server architecture is documented in the dedicated [Silo Server](../silo-server/overview.md) section.
- [Overview](../silo-server/overview.md) — Architecture, stack, and key features
- [Specification](../silo-server/SPECIFICATION.md) — Full API specification with 38+ routes
- [Configuration](../silo-server/CONFIGURATION.md) — YAML config reference
- [Deployment](../silo-server/DEPLOYMENT.md) — Docker Compose, systemd, production setup
- [Authentication](../silo-server/AUTH.md) — LDAP, OIDC, and local auth backends
- [Status System](../silo-server/STATUS.md) — Revision lifecycle states
- [Gap Analysis](../silo-server/GAP_ANALYSIS.md) — Current gaps and planned improvements

View File

@@ -37,7 +37,7 @@ Use [Conventional Commits](https://www.conventionalcommits.org/):
| `art:` | Icons, theme, visual assets |
Scope is optional but encouraged:
- `feat(ztools): add datum point creation mode`
- `feat(datums): add datum point creation mode`
- `fix(gui): correct menu icon size on Wayland`
- `chore: update silo submodule`
@@ -45,7 +45,7 @@ Scope is optional but encouraged:
Report issues at the [issue tracker](https://git.kindred-systems.com/kindred/create/issues). When reporting:
1. Note whether the issue involves Kindred Create additions (ztools, Silo, theme) or base FreeCAD
1. Note whether the issue involves Kindred Create additions (Silo, Gears, Datums, theme) or base FreeCAD
2. Include version info from **Help > About FreeCAD > Copy to clipboard**
3. Provide reproduction steps and attach example files (FCStd as ZIP) if applicable

View File

@@ -6,6 +6,7 @@ create/
│ ├── App/ # Core application (C++)
│ ├── Base/ # Base classes (C++)
│ ├── Gui/ # GUI framework and stylesheets (C++)
│ │ └── SDK/ # KCSDK C++ library (libKCSDK)
│ ├── Main/ # Application entry points
│ ├── Mod/ # FreeCAD modules (~37)
│ │ ├── Create/ # Kindred bootstrap module
@@ -17,9 +18,15 @@ create/
│ └── 3rdParty/
│ ├── OndselSolver/ # Assembly solver (submodule)
│ └── GSL/ # Guidelines Support Library (submodule)
├── mods/ # Kindred addon workbenches (submodules)
│ ├── ztools/ # ztools workbench
── silo/ # Silo parts database
├── mods/ # Kindred addon modules
│ ├── sdk/ # Addon SDK — stable API contract (priority 0)
── solver/ # Assembly solver research (submodule, priority 10)
│ ├── gears/ # Gears workbench (submodule, priority 40)
│ ├── datums/ # Unified datum creator (submodule, priority 45)
│ └── silo/ # Silo PLM workbench (submodule, priority 60)
├── reference/ # Archived addons (not built)
│ ├── ztools/ # Archived — commands migrated to datums + core
│ └── quicknav/ # Archived — navigation addon
├── icons/ # Icon theming (kindred overrides, palettes, retheme script)
│ ├── kindred/ # Hand-crafted Catppuccin Mocha SVG overrides (~1444 icons)
│ ├── mappings/ # Color palette CSVs (FCAD.csv, kindred.csv)
@@ -29,10 +36,7 @@ create/
│ └── icons/ # Platform icons (.ico, .icns, hicolor)
├── package/ # Packaging scripts
│ ├── debian/ # Debian package
── ubuntu/ # Ubuntu-specific
│ ├── fedora/ # RPM package
│ ├── rattler-build/ # Cross-platform bundles (AppImage, DMG, NSIS)
│ └── WindowsInstaller/ # NSIS installer definition
── rattler-build/ # Cross-platform bundles (AppImage, DMG, NSIS)
├── .gitea/workflows/ # CI/CD pipelines
│ ├── build.yml # Build + test on push/PR
│ └── release.yml # Release on tag push
@@ -41,9 +45,6 @@ create/
│ └── lib/ # Google Test framework (submodule)
├── cMake/ # CMake helper modules
├── docs/ # Documentation (this book)
├── tools/ # Dev utilities (build, lint, profile)
├── contrib/ # IDE configs (VSCode, CLion, debugger)
├── data/ # Example and test data
├── CMakeLists.txt # Root build configuration
├── CMakePresets.json # Platform build presets
├── pixi.toml # Pixi environment and tasks
@@ -57,8 +58,10 @@ create/
| Submodule | Path | Source | Purpose |
|-----------|------|--------|---------|
| ztools | `mods/ztools` | `git.kindred-systems.com/forbes/ztools` | Unified workbench |
| silo-mod | `mods/silo` | `git.kindred-systems.com/kindred/silo-mod` | Parts database |
| silo-mod | `mods/silo` | `git.kindred-systems.com/kindred/silo-mod` | PLM workbench |
| gears | `mods/gears` | `git.kindred-systems.com/kindred/gears` | Parametric gear generation |
| datums | `mods/datums` | `git.kindred-systems.com/kindred/datums` | Unified datum creator |
| solver | `mods/solver` | `git.kindred-systems.com/kindred/solver` | Assembly solver research |
| OndselSolver | `src/3rdParty/OndselSolver` | `git.kindred-systems.com/kindred/solver` | Assembly solver |
| GSL | `src/3rdParty/GSL` | `github.com/microsoft/GSL` | C++ guidelines library |
| AddonManager | `src/Mod/AddonManager` | `github.com/FreeCAD/AddonManager` | Extension manager |
@@ -69,8 +72,11 @@ create/
| File | Purpose |
|------|---------|
| `src/Mod/Create/Init.py` | Console-phase bootstrap — loads addons |
| `src/Mod/Create/InitGui.py` | GUI-phase bootstrap — registers workbenches, deferred setup |
| `src/Mod/Create/InitGui.py` | GUI-phase bootstrap — kc_format, update checker |
| `src/Mod/Create/addon_loader.py` | Manifest-driven addon loader with dependency resolution |
| `src/Gui/FileOrigin.h` | Abstract file origin interface (Kindred addition) |
| `src/Gui/EditingContext.h` | Editing context resolver (Kindred addition) |
| `src/Gui/SDK/` | KCSDK C++ library — stable addon API |
| `src/Gui/Stylesheets/KindredCreate.qss` | Catppuccin Mocha theme |
| `pixi.toml` | Build tasks and dependencies |
| `CMakeLists.txt` | Root CMake configuration |

View File

@@ -0,0 +1,283 @@
# Writing an Addon
This guide walks through creating a Kindred Create addon from scratch. Addons are Python packages in the `mods/` directory that extend Create with commands, panels, and UI modifications through the SDK.
## Addon structure
A minimal addon has this layout:
```
mods/my-addon/
├── package.xml # Manifest (required)
├── Init.py # Console-phase bootstrap
├── InitGui.py # GUI-phase bootstrap
└── my_addon/
├── __init__.py
└── commands.py # Your commands
```
## Step 1: Create the manifest
Every addon needs a `package.xml` with a `<kindred>` extension block. The `<workbench>` tag is required for `InitGui.py` to be loaded, even if your addon doesn't register a workbench.
```xml
<?xml version="1.0" encoding="UTF-8"?>
<package format="1">
<name>my-addon</name>
<description>My custom addon for Kindred Create.</description>
<version>0.1.0</version>
<maintainer email="you@example.com">Your Name</maintainer>
<license>LGPL-2.1-or-later</license>
<!-- Required for InitGui.py loading -->
<workbench>
<classname>MyAddonWorkbench</classname>
</workbench>
<kindred>
<min_create_version>0.1.5</min_create_version>
<load_priority>70</load_priority>
<pure_python>true</pure_python>
<dependencies>
<dependency>sdk</dependency>
</dependencies>
</kindred>
</package>
```
### Priority ranges
| Range | Use |
|-------|-----|
| 0-9 | SDK and core infrastructure |
| 10-49 | Foundation addons (solver, gears, datums) |
| 50-99 | Standard addons (silo) |
| 100+ | Optional/user addons |
See [Package.xml Schema Extensions](./package-xml-schema.md) for the full schema.
## Step 2: Console bootstrap (Init.py)
`Init.py` runs during FreeCAD's console initialization, before the GUI exists. Use it for non-GUI setup.
```python
import FreeCAD
FreeCAD.Console.PrintLog("my-addon: loaded (console)\n")
```
## Step 3: GUI bootstrap (InitGui.py)
`InitGui.py` runs when the GUI is ready. This is where you register commands, contexts, panels, and overlays.
```python
import FreeCAD
import FreeCADGui
FreeCAD.Console.PrintLog("my-addon: loaded (GUI)\n")
def _deferred_setup():
"""Register commands and UI after the main window is ready."""
from my_addon import commands
commands.register()
from PySide.QtCore import QTimer
QTimer.singleShot(2000, _deferred_setup)
```
Deferred setup via `QTimer.singleShot()` avoids timing issues during startup. See [Create Module Bootstrap](../reference/create-module-bootstrap.md) for the full timer cascade.
## Step 4: Register commands
FreeCAD commands use `Gui.addCommand()`. This is a stable FreeCAD API and does not need SDK wrappers.
```python
# my_addon/commands.py
import FreeCAD
import FreeCADGui
class MyCommand:
def GetResources(self):
return {
"MenuText": "My Command",
"ToolTip": "Does something useful",
}
def Activated(self):
FreeCAD.Console.PrintMessage("My command activated\n")
def IsActive(self):
return FreeCAD.ActiveDocument is not None
def register():
FreeCADGui.addCommand("MyAddon_MyCommand", MyCommand())
```
## Step 5: Inject into editing contexts
Use the SDK to add your commands to existing toolbar contexts, rather than creating a standalone workbench.
```python
from kindred_sdk import inject_commands
# Add your command to the PartDesign body context toolbar
inject_commands("partdesign.body", "PartDesign", ["MyAddon_MyCommand"])
```
Built-in contexts you can inject into: `sketcher.edit`, `assembly.edit`, `partdesign.feature`, `partdesign.body`, `assembly.idle`, `spreadsheet`, `empty_document`, `no_document`.
## Step 6: Register a custom context
If your addon has its own editing mode, register a context to control which toolbars are visible.
```python
from kindred_sdk import register_context
def _is_my_object_in_edit():
import FreeCADGui
doc = FreeCADGui.activeDocument()
if doc and doc.getInEdit():
obj = doc.getInEdit().Object
return obj.isDerivedFrom("App::FeaturePython") and hasattr(obj, "MyAddonType")
return False
register_context(
"myaddon.edit",
"Editing {name}",
"#f9e2af", # Catppuccin yellow
["MyAddonToolbar", "StandardViews"],
_is_my_object_in_edit,
priority=55,
)
```
## Step 7: Register a dock panel
For panels that live in the dock area (like Silo's database panels), use the SDK panel registration.
### Simple approach (recommended for most addons)
```python
from kindred_sdk import register_dock_panel
def _create_my_panel():
from PySide import QtWidgets
widget = QtWidgets.QTreeWidget()
widget.setHeaderLabels(["Name", "Value"])
return widget
register_dock_panel(
"MyAddonPanel", # unique object name
"My Addon", # title bar text
_create_my_panel,
area="right",
delay_ms=3000, # create 3 seconds after startup
)
```
### Advanced approach (IPanelProvider)
For full control over panel behavior, implement the `IPanelProvider` interface directly:
```python
import kcsdk
class MyPanelProvider(kcsdk.IPanelProvider):
def id(self):
return "myaddon.inspector"
def title(self):
return "Inspector"
def create_widget(self):
from PySide import QtWidgets
tree = QtWidgets.QTreeWidget()
tree.setHeaderLabels(["Property", "Value"])
return tree
def preferred_area(self):
return kcsdk.DockArea.Left
def context_affinity(self):
return "myaddon.edit" # only visible in your custom context
# Register and create
kcsdk.register_panel(MyPanelProvider())
kcsdk.create_panel("myaddon.inspector")
```
## Step 8: Use theme colors
The SDK provides the Catppuccin Mocha palette for consistent theming.
```python
from kindred_sdk import get_theme_tokens, load_palette
# Quick lookup
tokens = get_theme_tokens()
blue = tokens["blue"] # "#89b4fa"
error = tokens["error"] # mapped from semantic role
# Full palette object
palette = load_palette()
palette.get("accent.primary") # semantic role lookup
palette.get("mauve") # direct color lookup
# Format QSS templates
qss = palette.format_qss("background: {base}; color: {text};")
```
## Complete example
Putting it all together, here's a minimal addon that adds a command and a dock panel:
```
mods/my-addon/
├── package.xml
├── Init.py
├── InitGui.py
└── my_addon/
├── __init__.py
└── commands.py
```
**InitGui.py:**
```python
import FreeCAD
def _setup():
from my_addon.commands import register
from kindred_sdk import inject_commands, register_dock_panel
register()
inject_commands("partdesign.body", "PartDesign", ["MyAddon_MyCommand"])
from PySide import QtWidgets
register_dock_panel(
"MyAddonPanel", "My Addon",
lambda: QtWidgets.QLabel("Hello from my addon"),
area="right", delay_ms=0,
)
from PySide.QtCore import QTimer
QTimer.singleShot(2500, _setup)
```
## Key patterns
- **Use `kindred_sdk` wrappers** instead of `FreeCADGui.*` internals. The SDK handles fallback and error logging.
- **Defer initialization** with `QTimer.singleShot()` to avoid startup timing issues.
- **Declare `<dependency>sdk</dependency>`** in your manifest to ensure the SDK loads before your addon.
- **Inject commands into existing contexts** rather than creating standalone workbenches. This gives users a unified toolbar experience.
- **Use theme tokens** from the palette for colors. Don't hardcode hex values.
## Related
- [KCSDK Python API Reference](../reference/kcsdk-python.md)
- [Package.xml Schema Extensions](./package-xml-schema.md)
- [Create Module Bootstrap](../reference/create-module-bootstrap.md)

View File

@@ -19,12 +19,14 @@ If cloned without `--recursive`:
git submodule update --init --recursive
```
The repository includes six submodules:
The repository includes several submodules:
| Submodule | Path | Source |
|-----------|------|--------|
| ztools | `mods/ztools` | `git.kindred-systems.com/forbes/ztools` |
| silo-mod | `mods/silo` | `git.kindred-systems.com/kindred/silo-mod` |
| gears | `mods/gears` | `git.kindred-systems.com/kindred/gears` |
| datums | `mods/datums` | `git.kindred-systems.com/kindred/datums` |
| solver | `mods/solver` | `git.kindred-systems.com/kindred/solver` |
| OndselSolver | `src/3rdParty/OndselSolver` | `git.kindred-systems.com/kindred/solver` |
| GSL | `src/3rdParty/GSL` | `github.com/microsoft/GSL` |
| AddonManager | `src/Mod/AddonManager` | `github.com/FreeCAD/AddonManager` |
@@ -98,7 +100,7 @@ ccache is auto-detected by CMake at configure time.
## Common problems
**Submodules not initialized:** If you see missing file errors for ztools or Silo, run `pixi run initialize` or `git submodule update --init --recursive`.
**Submodules not initialized:** If you see missing file errors for addon modules, run `pixi run initialize` or `git submodule update --init --recursive`.
**Pixi not found:** Install pixi from <https://pixi.sh>.

View File

@@ -33,7 +33,7 @@ See [Installation](./installation.md) for prebuilt package details and [Building
On first launch, Kindred Create:
1. Loads **ztools** commands and the **Silo** workbench via the Create bootstrap module
1. Loads addon modules (SDK, Solver, Gears, Datums, Silo) via the Create bootstrap module
2. Opens the **PartDesign** workbench as the default (with context-driven toolbars)
3. Prompts for Silo server configuration if not yet set up
4. Checks for application updates in the background (after ~10 seconds)

View File

@@ -32,12 +32,14 @@ The AppImage is a self-contained bundle using squashfs with zstd compression. No
Launch Kindred Create and check the console output (View > Report View) for:
```
Create: Loaded ztools Init.py
Create: Loaded silo Init.py
Create module initialized
addon_loader: loading sdk (priority 0)
addon_loader: loading solver (priority 10)
addon_loader: loading gears (priority 40)
addon_loader: loading datums (priority 45)
addon_loader: loading silo (priority 60)
```
This confirms the bootstrap module loaded both workbenches. If Silo is not configured, you will see a settings prompt on first launch.
This confirms the bootstrap module loaded all addons. If Silo is not configured, you will see a settings prompt on first launch.
## Uninstalling

View File

@@ -1,23 +1,27 @@
# Workbenches
Kindred Create ships two custom workbenches on top of FreeCAD's standard set.
Kindred Create ships several addon modules on top of FreeCAD's standard set.
## ztools
## Gears
A unified workbench that consolidates part design, assembly, and sketcher tools into a single interface. It is the **default workbench** when Kindred Create launches.
A parametric gear generation workbench for creating involute spur gears, helical gears, and other gear profiles directly within FreeCAD assemblies.
ztools commands are also injected into the PartDesign workbench menus and toolbars via a manipulator mechanism, so they are accessible even when working in stock PartDesign.
## Datums
See the [ztools guide](./ztools.md) for details.
A unified datum creator that replaces the three stock PartDesign datum commands (Plane, Line, Point) with a single `Create_DatumCreator` command. Provides 16 smart creation modes with auto-detection from geometry selection. Injected into PartDesign body and feature contexts via the SDK.
## Silo
A parts database workbench for managing CAD files, part numbers, revisions, and bills of materials across teams. Silo commands (New, Open, Save, Commit, Pull, Push, Info, BOM) are integrated into the File menu and toolbar across **all** workbenches via the origin system.
A parts lifecycle management workbench for managing CAD files, part numbers, revisions, and bills of materials across teams. Silo commands (New, Open, Save, Commit, Pull, Push, Info, BOM) are integrated into the File menu and toolbar across **all** workbenches via the origin system.
Silo requires a running server instance. On first launch, Kindred Create prompts for server configuration.
See the [Silo guide](./silo.md) for details.
## Solver
An experimental assembly solver research addon using GNN-based constraint solving.
## Stock FreeCAD workbenches
All standard FreeCAD workbenches are available: PartDesign, Sketcher, Assembly, TechDraw, Draft, BIM, CAM, FEM, Mesh, Spreadsheet, and others. Kindred Create does not remove or disable any stock functionality.

View File

@@ -1,133 +0,0 @@
# ztools
ztools is a pure-Python FreeCAD workbench that consolidates part design, assembly, and sketcher tools into a single unified interface. It is the **default workbench** when Kindred Create launches.
- **Submodule path:** `mods/ztools/`
- **Source:** `git.kindred-systems.com/forbes/ztools`
- **Stats:** 6,400+ lines of code, 24+ command classes, 33 custom icons, 17 toolbars
## Commands
### Datum Creator (`ZTools_DatumCreator`)
Creates datum geometry (planes, axes, points) with 16 creation modes. The task panel auto-detects the geometry type from your selection and offers appropriate modes. Supports custom naming, spreadsheet linking, and body- or document-level creation.
### Datum Manager (`ZTools_DatumManager`)
Manages existing datums. (Stub — planned for Phase 1, Q1 2026.)
### Enhanced Pocket (`ZTools_EnhancedPocket`)
Extends FreeCAD's Pocket feature with **Flip Side to Cut** — a SOLIDWORKS-style feature that removes material *outside* the sketch profile rather than inside. Uses a Boolean Common operation internally. Supports all standard pocket types: Dimension, Through All, To First, Up To Face, Two Dimensions. Taper angle is supported for standard pockets (disabled for flipped).
### Rotated Linear Pattern (`ZTools_RotatedLinearPattern`)
Creates a linear pattern with incremental rotation per instance. Configure direction, spacing, number of occurrences, and cumulative or per-instance rotation. Source components are automatically hidden.
### Assembly Linear Pattern (`ZTools_AssemblyLinearPattern`)
Creates linear patterns of assembly components. Supports multi-component selection, direction vectors, total length or spacing modes, and creation as Links (recommended) or copies. Auto-detects the parent assembly.
### Assembly Polar Pattern (`ZTools_AssemblyPolarPattern`)
Creates polar (circular) patterns of assembly components. Supports custom or preset axes (X/Y/Z), full circle or custom angle, center point definition, and creation as Links or copies.
### Spreadsheet Formatting (9 commands)
| Command | Action |
|---------|--------|
| `ZTools_SpreadsheetStyleBold` | Toggle bold |
| `ZTools_SpreadsheetStyleItalic` | Toggle italic |
| `ZTools_SpreadsheetStyleUnderline` | Toggle underline |
| `ZTools_SpreadsheetAlignLeft` | Left align |
| `ZTools_SpreadsheetAlignCenter` | Center align |
| `ZTools_SpreadsheetAlignRight` | Right align |
| `ZTools_SpreadsheetBgColor` | Background color picker |
| `ZTools_SpreadsheetTextColor` | Text color picker |
| `ZTools_SpreadsheetQuickAlias` | Auto-create aliases from row/column labels |
## Datum creation modes
### Planes (7 modes)
| Mode | Description | Input |
|------|-------------|-------|
| Offset from Face | Offsets a planar face along its normal | Face + distance (mm) |
| Offset from Plane | Offsets an existing datum plane | Datum plane + distance (mm) |
| Midplane | Plane halfway between two parallel faces | Two parallel faces |
| 3 Points | Plane through three non-collinear points | Three vertices |
| Normal to Edge | Plane perpendicular to an edge at a parameter location | Edge + parameter (0.01.0) |
| Angled | Rotates a plane about an edge by a specified angle | Face + edge + angle (degrees) |
| Tangent to Cylinder | Plane tangent to a cylindrical face at an angular position | Cylindrical face + angle (degrees) |
### Axes (4 modes)
| Mode | Description | Input |
|------|-------------|-------|
| 2 Points | Axis through two points | Two vertices |
| From Edge | Axis along a linear edge | Linear edge |
| Cylinder Center | Axis along the centerline of a cylinder | Cylindrical face |
| Plane Intersection | Axis at the intersection of two planes | Two non-parallel planes |
### Points (5 modes)
| Mode | Description | Input |
|------|-------------|-------|
| At Vertex | Point at a vertex location | Vertex |
| XYZ Coordinates | Point at explicit coordinates | x, y, z (mm) |
| On Edge | Point at a location along an edge | Edge + parameter (0.01.0) |
| Face Center | Point at the center of mass of a face | Face |
| Circle Center | Point at the center of a circular or arc edge | Circular edge |
## PartDesign injection
ztools registers a `_ZToolsPartDesignManipulator` that hooks into the PartDesign workbench at startup. This injects the following commands into PartDesign's toolbars and menus:
| PartDesign toolbar | Injected command |
|--------------------|-----------------|
| Part Design Helper Features | `ZTools_DatumCreator`, `ZTools_DatumManager` |
| Part Design Modeling Features | `ZTools_EnhancedPocket` |
| Part Design Transformation Features | `ZTools_RotatedLinearPattern` |
The manipulator is registered in `InitGui.py` when the Create bootstrap module loads addon workbenches.
## Directory structure
```
mods/ztools/
├── ztools/ztools/
│ ├── InitGui.py # Workbench registration + manipulator
│ ├── Init.py # Console initialization
│ ├── commands/
│ │ ├── datum_commands.py # DatumCreator + DatumManager
│ │ ├── datum_viewprovider.py # ViewProvider + edit panel
│ │ ├── pocket_commands.py # EnhancedPocket + FlippedPocket
│ │ ├── pattern_commands.py # RotatedLinearPattern
│ │ ├── assembly_pattern_commands.py # Linear + Polar assembly patterns
│ │ └── spreadsheet_commands.py # 9 formatting commands
│ ├── datums/
│ │ └── core.py # 16 datum creation functions
│ └── resources/ # Icons and theme
└── CatppuccinMocha/ # Theme preference pack
```
## Internal properties
ztools stores metadata on feature objects using these properties (preserved for backward compatibility):
| Property | Purpose |
|----------|---------|
| `ZTools_Type` | Feature type identifier |
| `ZTools_Params` | JSON creation parameters |
| `ZTools_SourceRefs` | JSON source geometry references |
## Known gaps
- Datum Manager is a stub — full implementation planned for Q1 2026
- Datum parameter changes don't recalculate from source geometry yet
- Enhanced Pocket taper angle is disabled for flipped pockets
## Further reading
- `mods/ztools/KINDRED_INTEGRATION.md` — integration architecture and migration options
- `mods/ztools/ROADMAP.md` — phased development plan (Q1Q4 2026)

View File

@@ -1,30 +1,36 @@
# Kindred Create
Kindred Create is a fork of [FreeCAD](https://www.freecad.org) 1.0+ that adds integrated tooling for professional engineering workflows. It ships custom workbenches and a dark theme on top of FreeCAD's parametric modeling core.
Kindred Create is a fork of [FreeCAD](https://www.freecad.org) 1.0+ that adds integrated tooling for professional engineering workflows. It ships custom workbenches, a C++ SDK extension layer, and a dark theme on top of FreeCAD's parametric modeling core.
- **License:** LGPL 2.1+
- **Organization:** [Kindred Systems LLC](https://www.kindred-systems.com)
- **Build system:** CMake + [pixi](https://pixi.sh)
- **Current version:** Kindred Create v0.1.0 (FreeCAD base v1.0.0)
- **Current version:** Kindred Create v0.1.5 (FreeCAD base v1.2.0)
## Key features
**[ztools](./guide/ztools.md)** A unified workbench that consolidates part design, assembly, and sketcher tools into a single interface. Adds custom datum creation (planes, axes, points with 16 creation modes), pattern tools for assemblies, an enhanced pocket with flip-side cutting, and spreadsheet formatting commands.
**[Silo](./guide/silo.md)** -- A parts lifecycle management system for managing CAD files, part numbers, revisions, and bills of materials across teams. Includes a Go REST API server backed by PostgreSQL and MinIO, with FreeCAD commands for opening, saving, and syncing files directly from the application.
**[Silo](./guide/silo.md)** — A parts database system for managing CAD files, part numbers, revisions, and bills of materials across teams. Includes a Go REST API server backed by PostgreSQL and MinIO, with FreeCAD commands for opening, saving, and syncing files directly from the application.
**Gears** -- A parametric gear generation workbench for creating involute spur gears, helical gears, and other gear profiles directly within FreeCAD assemblies.
**Catppuccin Mocha theme** A dark theme applied across the entire application, including the 3D viewport, sketch editor, spreadsheet view, and tree view. Uses spanning-line branch indicators instead of disclosure arrows, with tuned preference defaults for document handling, selection behavior, and notifications.
**Datums** -- A unified datum creator that replaces the three stock PartDesign datum commands (Plane, Line, Point) with a single command providing 16 smart creation modes with auto-detection from geometry selection.
**Origin system** A pluggable file backend abstraction. The origin selector in the File toolbar lets you switch between local filesystem operations and Silo database operations. Silo commands (Commit, Pull, Push, Info, BOM) are available across all workbenches when Silo is the active origin.
**KCSDK** -- A C++ shared library (`libKCSDK`) with pybind11 bindings providing stable interfaces for dock panels, toolbars, menus, themes, and editing contexts. Python addons access it through the `kindred_sdk` wrapper package.
**Update checker** — On startup, Kindred Create checks the Gitea releases API for newer versions and logs the result. Configurable check interval and skip-version preferences.
**Catppuccin Mocha theme** -- A dark theme applied across the entire application, including the 3D viewport, sketch editor, spreadsheet view, and tree view. Uses spanning-line branch indicators instead of disclosure arrows, with tuned preference defaults for document handling, selection behavior, and notifications.
**Origin system** -- A pluggable file backend abstraction. The origin selector in the File toolbar lets you switch between local filesystem operations and Silo database operations. Silo commands (Commit, Pull, Push, Info, BOM) are available across all workbenches when Silo is the active origin.
**Update checker** -- On startup, Kindred Create checks the Gitea releases API for newer versions and logs the result. Configurable check interval and skip-version preferences.
## How it relates to FreeCAD
Kindred Create is a fork/distribution of FreeCAD 1.0+. The design minimizes core modifications custom functionality is delivered through submodule addons (ztools, Silo) that follow FreeCAD's standard workbench pattern. If the addon submodules are missing, Kindred Create still functions as a themed FreeCAD.
Kindred Create is a fork/distribution of FreeCAD 1.0+. The design minimizes core modifications -- custom functionality is delivered through submodule addons (Silo, Gears, Datums, Solver) that follow FreeCAD's standard workbench pattern. If the addon submodules are missing, Kindred Create still functions as a themed FreeCAD.
The primary additions to FreeCAD's core are:
- The **origin system** (`FileOrigin` interface in `src/Gui/`) for pluggable file backends
- The **editing context system** (`EditingContextResolver` in `src/Gui/`) for context-driven UI
- The **KCSDK** C++ library (`src/Gui/SDK/`) for addon integration
- The **Create bootstrap module** (`src/Mod/Create/`) that loads addons at startup
- The **Catppuccin Mocha theme** (`KindredCreate.qss`) and preference pack
- Patches to **Assembly** (`findPlacement()` datum/origin handling)

View File

@@ -5,41 +5,42 @@ The Create module (`src/Mod/Create/`) is the integration layer that bootstraps K
**Source files:**
- `src/Mod/Create/Init.py` -- console-phase addon loading
- `src/Mod/Create/InitGui.py` -- GUI-phase workbench loading and deferred timers
- `src/Mod/Create/InitGui.py` -- GUI-phase addon loading and deferred timers
- `src/Mod/Create/addon_loader.py` -- manifest-driven loader with dependency resolution
- `src/Mod/Create/update_checker.py` -- Gitea releases API polling
- `src/Mod/Create/kc_format.py` -- .kc file format round-trip preservation
## Loading Mechanism
FreeCAD loads module `Init.py` and `InitGui.py` files using **direct `exec()`**, not Python's import system. The C++ startup code scans `Mod/` directories, reads each file as text, compiles it, and executes it in an isolated namespace:
FreeCAD loads module `Init.py` and `InitGui.py` files using **direct `exec()`**, not Python's import system. The C++ startup code scans `Mod/` directories, reads each file as text, compiles it, and executes it in an isolated namespace.
```python
source = init_py.read_text(encoding="utf-8")
code = compile(source, init_py, "exec")
exec(code)
```
Modules loaded this way are **not** added to `sys.modules`. Each execution gets a fresh globals dict with no caching or dependency resolution.
The addon loader (`addon_loader.py`) then discovers addons in `mods/` via their `package.xml` manifests, resolves dependencies via topological sort, and loads them in priority order.
## Phase 1: Console (`Init.py`)
Runs immediately at application startup, before any GUI is available.
`setup_kindred_addons()` discovers and loads two built-in addons from `mods/`:
`addon_loader.load_addons(gui=False)` discovers and loads addons from `mods/`:
| Addon | Path | Purpose |
|-------|------|---------|
| ztools | `mods/ztools/ztools/` | Part design, assembly, and sketcher tools |
| silo | `mods/silo/freecad/` | Database integration and version control |
| Addon | Priority | Purpose |
|-------|----------|---------|
| sdk | 0 | Stable API contract |
| solver | 10 | Assembly solver research |
| gears | 40 | Parametric gear generation |
| datums | 45 | Unified datum creator |
| silo | 60 | PLM workbench |
For each addon:
1. Build the path from `FreeCAD.getHomePath() + "mods/" + addon_path`
2. Add to `sys.path` if not already present
3. Find `Init.py` in the addon directory
4. Execute it with `exec(compile(source, init_file, "exec"), exec_globals)`
1. Discover `package.xml` manifest (depth 1 or 2)
2. Parse `<kindred>` extensions (version bounds, priority, dependencies)
3. Validate version compatibility
4. Resolve load order via topological sort on dependencies
5. Add addon dir to `sys.path`
6. Execute `Init.py`
7. Register in `FreeCAD.KindredAddons`
Failures are logged to `FreeCAD.Console.PrintWarning()` and do not prevent other addons from loading.
Failures are logged to `FreeCAD.Console` and do not prevent other addons from loading.
## Phase 2: GUI (`InitGui.py`)
@@ -47,35 +48,36 @@ Runs after the console phase, when `FreeCADGui` is available.
### Synchronous
`setup_kindred_workbenches()` loads `InitGui.py` from the same two addons (ztools, silo), registering their workbenches and GUI commands.
`addon_loader.load_addons(gui=True)` loads `InitGui.py` from each addon in the same resolved order, registering workbenches and GUI commands.
### Deferred Timers
### Deferred Timers (Create core)
Six `QTimer.singleShot` calls stagger initialization to let the GUI event loop settle:
Two `QTimer.singleShot` calls in `src/Mod/Create/InitGui.py`:
| Delay | Function | Purpose |
|-------|----------|---------|
| 500 ms | `_register_kc_format()` | Register .kc file format observer |
| 1500 ms | `_register_silo_origin()` | Register Silo as a file origin |
| 2000 ms | `_setup_silo_auth_panel()` | Dock the Database Auth panel |
| 3000 ms | `_check_silo_first_start()` | Show settings dialog on first run |
| 4000 ms | `_setup_silo_activity_panel()` | Dock the Database Activity panel |
| 10000 ms | `_check_for_updates()` | Poll Gitea for new releases |
Every timer callback is wrapped in `try/except`. Failures log to `FreeCAD.Console.PrintLog()` and do not crash the application.
### Deferred Timers (Silo addon)
The Silo addon manages its own deferred setup in `mods/silo/InitGui.py`:
| Delay | Function | Purpose |
|-------|----------|---------|
| 500 ms | `_handle_startup_urls()` | Process kindred:// URLs |
| 600 ms | `_register_silo_document_observer()` | .kc tree building observer |
| 2000 ms | `_setup_silo_auth_panel()` | "Database Auth" dock panel |
| 2500 ms | Silo overlay registration | "Silo Origin" toolbar overlay |
| 3000 ms | `_check_silo_first_start()` | Settings prompt on first run |
| 4000 ms | `_setup_silo_activity_panel()` | "Database Activity" dock panel |
Every timer callback is wrapped in `try/except`. Failures log to `FreeCAD.Console` and do not crash the application.
### Timer Details
**_register_kc_format (500 ms)** -- Imports `kc_format` and calls `kc_format.register()`, which installs a `DocumentObserver` with `slotStartSaveDocument` / `slotFinishSaveDocument` hooks. These cache `silo/` ZIP entries before FreeCAD's C++ save rewrites the archive, then re-inject them afterwards. Only processes `.kc` files.
**_register_silo_origin (1500 ms)** -- Imports `silo_origin` and calls `silo_origin.register_silo_origin()`, making Silo available in the origin selector dropdown alongside LocalFileOrigin.
**_setup_silo_auth_panel (2000 ms)** -- Creates a `QDockWidget` titled "Database Auth" containing a `SiloAuthDockWidget` from `silo_commands`. Docked on the right side of the main window. Guards against duplicates by checking if the widget already exists.
**_check_silo_first_start (3000 ms)** -- Reads `User parameter:BaseApp/Preferences/Mod/KindredSilo`. On the very first launch (when `FirstStartChecked` is not set), runs the `Silo_Settings` command if no API URL is configured.
**_setup_silo_activity_panel (4000 ms)** -- Creates a "Database Activity" dock widget showing the 20 most recent Silo items (part number, description, date). Falls back to "(Unable to connect to Silo database)" on connection failure.
**_check_for_updates (10000 ms)** -- Calls `update_checker._run_update_check()` in the background.
## Update Checker
@@ -112,21 +114,32 @@ The checker never shows a dialog -- it only logs to `FreeCAD.Console`.
FreeCAD startup
|
Init.py (exec'd, immediate)
+-- setup_kindred_addons()
| +-- ztools/Init.py (exec'd)
| +-- silo/freecad/Init.py (exec'd)
+-- addon_loader.load_addons(gui=False)
| +-- sdk/Init.py
| +-- solver/Init.py
| +-- gears/Init.py
| +-- datums/Init.py
| +-- silo/Init.py
|
[GUI startup]
|
InitGui.py (exec'd, immediate)
+-- setup_kindred_workbenches()
| +-- ztools/InitGui.py (exec'd)
| +-- silo/freecad/InitGui.py (exec'd)
+-- addon_loader.load_addons(gui=True)
| +-- sdk/InitGui.py
| +-- solver/InitGui.py
| +-- gears/InitGui.py
| +-- datums/InitGui.py
| +-- silo/InitGui.py (schedules its own deferred timers)
|
Create core timers:
+-- QTimer 500ms --> kc_format.register()
+-- QTimer 1500ms --> silo_origin.register_silo_origin()
+-- QTimer 2000ms --> SiloAuthDockWidget
+-- QTimer 3000ms --> Silo first-start check
+-- QTimer 4000ms --> Database Activity panel
+-- QTimer 10000ms --> update_checker._run_update_check()
Silo addon timers (scheduled from silo/InitGui.py):
+-- QTimer 500ms --> kindred:// URL handler
+-- QTimer 600ms --> document observer
+-- QTimer 2000ms --> auth dock panel
+-- QTimer 2500ms --> Silo overlay
+-- QTimer 3000ms --> first-start check
+-- QTimer 4000ms --> activity dock panel
```

Some files were not shown because too many files have changed in this diff Show More