Compare commits
79 Commits
559a240799
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 0ac0d4dd8e | |||
|
|
9ca8809b55 | ||
| d84049b905 | |||
|
|
7f02fd182e | ||
| ce2da47d5b | |||
|
|
5336cde0de | ||
| 53f39d6368 | |||
|
|
ceaa3acffe | ||
| 95142a0b0a | |||
| 79e2929cc1 | |||
|
|
784d172a61 | ||
|
|
3f688873c6 | ||
| c60b4dbee1 | |||
|
|
4d94857883 | ||
| 4e1e2ca46d | |||
| 0a86a93e1b | |||
| 10dd66fc29 | |||
| 177a5eec67 | |||
| e160b07b00 | |||
| 470e6f3cf6 | |||
| e3de2c0e71 | |||
| de1b79255c | |||
| b0621f9731 | |||
|
|
1b01762a1c | ||
|
|
fa9c9ba761 | ||
|
|
79f69e2856 | ||
|
|
a6a6cefc16 | ||
|
|
747c458e23 | ||
|
|
4eb643a26f | ||
|
|
ab4054eb9e | ||
|
|
5f8557fc83 | ||
|
|
64644eb623 | ||
|
|
60c0489d73 | ||
|
|
63e591a626 | ||
| 9778d0dd1b | |||
|
|
ab8519c272 | ||
| ec5b3850e9 | |||
|
|
2bf083609d | ||
| 3759f707cf | |||
|
|
76e448d0d7 | ||
|
|
51f7635ceb | ||
|
|
ecdc9c2067 | ||
| 153f75e6e7 | |||
|
|
04065e83f3 | ||
| abfd939d2c | |||
|
|
5a0be2804d | ||
|
|
1b38d7b24b | ||
|
|
18532e3bd7 | ||
| 9fc0d064f8 | |||
| 81a2e75477 | |||
|
|
f44aba7388 | ||
| 169c177b69 | |||
|
|
9b28feb8ca | ||
| fcb5c2b00f | |||
|
|
c8b0706a1d | ||
| a623f280da | |||
|
|
485f69f257 | ||
| a32f405990 | |||
|
|
d47c94af8d | ||
| b1d75fa237 | |||
|
|
d174ef7a8d | ||
| bc157ddb72 | |||
|
|
07a51aefb1 | ||
| 9f644294c5 | |||
|
|
983e211f12 | ||
|
|
1788b5778a | ||
| 418e947cbd | |||
| 4c9ff957e3 | |||
| 9aaf244179 | |||
| 69ccdbf742 | |||
| 4acd09171e | |||
| 7f909f166f | |||
| f69e0efec7 | |||
| 7c85b2ad93 | |||
| 311d911cfa | |||
| 4ef8e64a7c | |||
| d94e8c8294 | |||
| 3550d916bd | |||
| 6e15b25134 |
@@ -47,7 +47,6 @@ body:
|
||||
description: Which part of Kindred Create is affected?
|
||||
options:
|
||||
- General / Core
|
||||
- ztools Workbench
|
||||
- Silo (Parts Database)
|
||||
- Theme / QSS
|
||||
- Assembly
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}',
|
||||
}))
|
||||
")
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
19
.github/ISSUE_TEMPLATE/3-BACKPORT_REQUEST.yml
vendored
@@ -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.
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -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
|
||||
102
.github/codespellignore
vendored
@@ -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
|
||||
11
.github/dependabot.yml
vendored
@@ -1,11 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: daily
|
||||
|
||||
- package-ecosystem: pip
|
||||
directory: /
|
||||
schedule:
|
||||
interval: daily
|
||||
BIN
.github/images/assembly-dark.png
vendored
|
Before Width: | Height: | Size: 431 KiB |
BIN
.github/images/assembly.png
vendored
|
Before Width: | Height: | Size: 416 KiB |
BIN
.github/images/bim-dark.png
vendored
|
Before Width: | Height: | Size: 540 KiB |
BIN
.github/images/bim.png
vendored
|
Before Width: | Height: | Size: 570 KiB |
BIN
.github/images/fem.png
vendored
|
Before Width: | Height: | Size: 464 KiB |
BIN
.github/images/partdesign-dark.png
vendored
|
Before Width: | Height: | Size: 459 KiB |
BIN
.github/images/partdesign.png
vendored
|
Before Width: | Height: | Size: 457 KiB |
113
.github/labels.yml
vendored
@@ -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/**/*']
|
||||
15
.github/problemMatcher/blackWarning.json
vendored
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"problemMatcher": [
|
||||
{
|
||||
"owner": "black-warning",
|
||||
"severity": "warning",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^(would reformat (.*))$",
|
||||
"file": 2,
|
||||
"message": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
17
.github/problemMatcher/clang.json
vendored
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
16
.github/problemMatcher/codespell.json
vendored
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"problemMatcher": [
|
||||
{
|
||||
"owner": "codespell-matcher",
|
||||
"severity": "warning",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^(.+):(\\d+):\\s+(.+)$",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"message": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
16
.github/problemMatcher/cpplint.json
vendored
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"problemMatcher": [
|
||||
{
|
||||
"owner": "cpplint",
|
||||
"severity": "warning",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^(.+):([0-9]+): (.+)$",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"message": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
18
.github/problemMatcher/gcc.json
vendored
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
16
.github/problemMatcher/grepMatcherWarning.json
vendored
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"problemMatcher": [
|
||||
{
|
||||
"owner": "grepMatcher-warning",
|
||||
"severity": "warning",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^(.+):([0-9]+):(.+)$",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"message": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
20
.github/problemMatcher/msvc.json
vendored
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
18
.github/problemMatcher/pylintError.json
vendored
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
18
.github/problemMatcher/pylintWarning.json
vendored
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
46
.github/pull_request_template.md
vendored
@@ -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?
|
||||
-->
|
||||
168
.github/scripts/run_gui_tests.py
vendored
@@ -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))
|
||||
115
.github/workflows/CI_cleanup.yml
vendored
@@ -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 }}
|
||||
93
.github/workflows/CI_master.yml
vendored
@@ -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) }}
|
||||
74
.github/workflows/actions/linux/build/action.yml
vendored
@@ -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 }}
|
||||
@@ -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 }}
|
||||
@@ -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
|
||||
@@ -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 }}
|
||||
73
.github/workflows/actions/macos/build/action.yml
vendored
@@ -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 }}
|
||||
@@ -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 }}
|
||||
@@ -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
|
||||
@@ -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 }}
|
||||
@@ -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 }}
|
||||
@@ -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"
|
||||
@@ -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 }}
|
||||
@@ -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 }}
|
||||
@@ -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 }}
|
||||
@@ -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 hasn’t seen activity in a while. If it’s 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, we’ll 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 hasn’t seen activity in a while despite the need for further feedback.
|
||||
If it’s 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, we’ll 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 hasn’t seen activity in a while. We automatically check each issue after 190 days without activity to keep the backlog tidy.
|
||||
If it’s 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, we’ll 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 hasn’t 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, we’ll 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.
|
||||
52
.github/workflows/backport.yml
vendored
@@ -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 ([^ ]+)$
|
||||
213
.github/workflows/build_release.yml
vendored
@@ -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
|
||||
127
.github/workflows/codeql.yml
vendored
@@ -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}}"
|
||||
163
.github/workflows/codeql_cpp.yml
vendored
@@ -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
|
||||
27
.github/workflows/dependency-review.yml
vendored
@@ -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
|
||||
39
.github/workflows/fedora-daily.yml
vendored
@@ -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
|
||||
68
.github/workflows/fetch_crowdin_translations.yml
vendored
@@ -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
|
||||
49
.github/workflows/issue-metrics.yml
vendored
@@ -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
|
||||
32
.github/workflows/labeler.yml
vendored
@@ -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
|
||||
43
.github/workflows/push_crowdin_translations.yml
vendored
@@ -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
|
||||
81
.github/workflows/scorecards.yml
vendored
@@ -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
|
||||
232
.github/workflows/sub_buildPixi.yml
vendored
@@ -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 }}
|
||||
239
.github/workflows/sub_buildUbuntu.yml
vendored
@@ -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 }}
|
||||
183
.github/workflows/sub_buildWindows.yml
vendored
@@ -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 }}
|
||||
371
.github/workflows/sub_lint.yml
vendored
@@ -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 }}
|
||||
194
.github/workflows/sub_prepare.yml
vendored
@@ -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 }}
|
||||
125
.github/workflows/sub_wrapup.yml
vendored
@@ -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 }}
|
||||
16
.github/workflows/weekly-build-notes.md
vendored
@@ -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`
|
||||
140
.github/workflows/weekly-compare-link.yml
vendored
@@ -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
@@ -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
|
||||
|
||||
15
.gitmodules
vendored
@@ -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,6 +18,11 @@
|
||||
path = mods/solver
|
||||
url = https://git.kindred-systems.com/kindred/solver.git
|
||||
branch = main
|
||||
[submodule "mods/quicknav"]
|
||||
path = mods/quicknav
|
||||
url = https://git.kindred-systems.com/kindred/quicknav.git
|
||||
[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
@@ -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>
|
||||
68
.packit.yaml
@@ -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
|
||||
@@ -3,77 +3,76 @@
|
||||
# See https://pre-commit.com for more information
|
||||
# See https://pre-commit.com/hooks.html for more hooks
|
||||
files: |
|
||||
(?x)^(
|
||||
.github|
|
||||
src/Base|
|
||||
src/Gui|
|
||||
src/Main|
|
||||
src/Tools|
|
||||
src/Mod/Assembly|
|
||||
src/Mod/BIM|
|
||||
src/Mod/CAM|
|
||||
src/Mod/Cloud|
|
||||
src/Mod/Draft|
|
||||
src/Mod/Fem|
|
||||
src/Mod/Help|
|
||||
src/Mod/Import|
|
||||
src/Mod/Inspection|
|
||||
src/Mod/JtReader|
|
||||
src/Mod/Measure|
|
||||
src/Mod/MeshPart|
|
||||
src/Mod/Mesh|
|
||||
src/Mod/PartDesign|
|
||||
src/Mod/Part|
|
||||
src/Mod/Plot|
|
||||
src/Mod/Points|
|
||||
src/Mod/ReverseEngineering|
|
||||
src/Mod/Robot|
|
||||
src/Mod/Show|
|
||||
src/Mod/Sketcher|
|
||||
src/Mod/Spreadsheet|
|
||||
src/Mod/Start|
|
||||
src/Mod/Surface|
|
||||
src/Mod/Test|
|
||||
src/Mod/Tux|
|
||||
src/Mod/Web|
|
||||
tests/src
|
||||
)
|
||||
(?x)^(
|
||||
src/Base|
|
||||
src/Gui|
|
||||
src/Main|
|
||||
src/Tools|
|
||||
src/Mod/Assembly|
|
||||
src/Mod/BIM|
|
||||
src/Mod/CAM|
|
||||
src/Mod/Cloud|
|
||||
src/Mod/Draft|
|
||||
src/Mod/Fem|
|
||||
src/Mod/Help|
|
||||
src/Mod/Import|
|
||||
src/Mod/Inspection|
|
||||
src/Mod/JtReader|
|
||||
src/Mod/Measure|
|
||||
src/Mod/MeshPart|
|
||||
src/Mod/Mesh|
|
||||
src/Mod/PartDesign|
|
||||
src/Mod/Part|
|
||||
src/Mod/Plot|
|
||||
src/Mod/Points|
|
||||
src/Mod/ReverseEngineering|
|
||||
src/Mod/Robot|
|
||||
src/Mod/Show|
|
||||
src/Mod/Sketcher|
|
||||
src/Mod/Spreadsheet|
|
||||
src/Mod/Start|
|
||||
src/Mod/Surface|
|
||||
src/Mod/Test|
|
||||
src/Mod/Tux|
|
||||
src/Mod/Web|
|
||||
tests/src
|
||||
)
|
||||
exclude: |
|
||||
(?x)^(
|
||||
.*vcproj.*|
|
||||
.*vcxproj.*|
|
||||
src/App/ExpressionParser.tab.c|
|
||||
src/App/ExpressionParser.tab.h|
|
||||
src/App/ExpressionParser.y|
|
||||
src/App/lex.ExpressionParser.c|
|
||||
src/Gui/3Dconnexion/navlib|
|
||||
src/Gui/QSint|
|
||||
src/Gui/Quarter|
|
||||
src/Mod/Fem/femexamples|
|
||||
src/Mod/Import/App/SCL|
|
||||
src/Mod/Import/App/SCL_output|
|
||||
src/Mod/Mesh/App/TestData|
|
||||
src/Mod/Mesh/App/WildMagic4|
|
||||
src/Mod/Robot/App/kdl_cp|
|
||||
src/Mod/Robot/Lib|
|
||||
.*\.ts$|
|
||||
.*\.brep$
|
||||
)
|
||||
(?x)^(
|
||||
.*vcproj.*|
|
||||
.*vcxproj.*|
|
||||
src/App/ExpressionParser.tab.c|
|
||||
src/App/ExpressionParser.tab.h|
|
||||
src/App/ExpressionParser.y|
|
||||
src/App/lex.ExpressionParser.c|
|
||||
src/Gui/3Dconnexion/navlib|
|
||||
src/Gui/QSint|
|
||||
src/Gui/Quarter|
|
||||
src/Mod/Fem/femexamples|
|
||||
src/Mod/Import/App/SCL|
|
||||
src/Mod/Import/App/SCL_output|
|
||||
src/Mod/Mesh/App/TestData|
|
||||
src/Mod/Mesh/App/WildMagic4|
|
||||
src/Mod/Robot/App/kdl_cp|
|
||||
src/Mod/Robot/Lib|
|
||||
.*\.ts$|
|
||||
.*\.brep$
|
||||
)
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-yaml
|
||||
- id: check-added-large-files
|
||||
- id: mixed-line-ending
|
||||
- repo: https://github.com/psf/black-pre-commit-mirror
|
||||
rev: 2892f1f81088477370d4fbc56545c05d33d2493f # frozen: 25.11.0
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-yaml
|
||||
- id: check-added-large-files
|
||||
- id: mixed-line-ending
|
||||
- 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
|
||||
rev: 317810f3c6a0ad3572367dc86cb6e41863e16e08 # frozen: v21.1.5
|
||||
- id: black
|
||||
args: ["--line-length", "100"]
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
rev: 317810f3c6a0ad3572367dc86cb6e41863e16e08 # frozen: v21.1.5
|
||||
hooks:
|
||||
- id: clang-format
|
||||
- id: clang-format
|
||||
|
||||
347
CLAUDE.md
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
32
SECURITY.md
@@ -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
|
||||
|
||||
@@ -19,26 +19,27 @@ FreeCAD startup
|
||||
├─ addon_loader.load_addons(gui=True)
|
||||
│ └─ 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
|
||||
│ └─ silo (priority 60): registers SiloWorkbench
|
||||
│ └─ schedules deferred Silo overlay registration (2500ms)
|
||||
│ ├─ sdk (priority 0): logs "SDK loaded"
|
||||
│ ├─ solver (priority 10): registers KindredSolverBackend
|
||||
│ ├─ gears (priority 40): registers GearsWorkbench
|
||||
│ ├─ datums (priority 45): registers Create_DatumCreator
|
||||
│ └─ silo (priority 60): registers SiloWorkbench
|
||||
│ └─ 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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
@@ -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 |
|
||||
|
||||
---
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 |
|
||||
|
||||
283
docs/src/development/writing-an-addon.md
Normal 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)
|
||||
@@ -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>.
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.0–1.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.0–1.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 (Q1–Q4 2026)
|
||||
@@ -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)
|
||||
|
||||
@@ -1,441 +0,0 @@
|
||||
# QuickNav — Keyboard Navigation Addon Specification
|
||||
|
||||
**Addon name:** QuickNav
|
||||
**Type:** Pure Python FreeCAD addon (no C++ required)
|
||||
**Compatibility:** FreeCAD 1.0+, Kindred Create 0.1+
|
||||
**Location:** `mods/quicknav/`
|
||||
|
||||
---
|
||||
|
||||
## 1. Overview
|
||||
|
||||
QuickNav provides keyboard-driven command access for FreeCAD and Kindred Create. It replaces mouse-heavy toolbar navigation with a numbered key system organized by workbench and command grouping. The addon is activated by loading its workbench and toggled on/off with the `0` key.
|
||||
|
||||
### Design Goals
|
||||
|
||||
- Numbers `1-9` execute commands within the active command grouping
|
||||
- `Shift+1-9` switches command grouping within the active workbench
|
||||
- `Ctrl+1-9` switches workbench context
|
||||
- All groupings and workbenches are ordered by most-recently-used (MRU) history
|
||||
- History is unlimited internally, top 9 shown, remainder scrollable/clickable
|
||||
- Mouse interaction remains fully functional — QuickNav is purely additive
|
||||
- Configuration persisted via `FreeCAD.ParamGet()`
|
||||
|
||||
---
|
||||
|
||||
## 2. Terminology
|
||||
|
||||
| Term | Definition |
|
||||
|------|-----------|
|
||||
| **Workbench** | A FreeCAD workbench (Sketcher, PartDesign, Assembly, etc.). Fixed assignment to Ctrl+N slots. |
|
||||
| **Command Grouping** | A logical group of commands within a workbench, mapped from existing FreeCAD toolbar groupings. Max 9 per tier. |
|
||||
| **Active Grouping** | The left-most visible grouping in the navigation bar. Its commands are accessible via `1-9`. |
|
||||
| **Navigation Bar** | Bottom toolbar displaying the current state: active workbench, groupings, and numbered commands. |
|
||||
| **MRU Stack** | Most-recently-used ordering. Position 0 = currently active, 1 = previously active, etc. |
|
||||
| **Tier** | When a workbench has >9 command groupings, they are split: Tier 1 (most common 9), Tier 2 (next 9). |
|
||||
|
||||
---
|
||||
|
||||
## 3. Key Bindings
|
||||
|
||||
### 3.1 Mode Toggle
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| `0` | Toggle QuickNav on/off. When off, all QuickNav key interception is disabled and the navigation bar hides. |
|
||||
|
||||
### 3.2 Command Execution
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| `1-9` | Execute the Nth command in the active grouping. If the command is auto-executable (e.g., Pad after closed sketch), execute immediately. Otherwise, enter tool mode (same as clicking the toolbar button). |
|
||||
|
||||
### 3.3 Grouping Navigation
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| `Shift+1-9` | Switch to the Nth command grouping (MRU ordered) within the current workbench. The newly activated grouping moves to position 0 in the MRU stack. |
|
||||
| `Shift+Left/Right` | Scroll through groupings beyond the visible 9. |
|
||||
|
||||
### 3.4 Workbench Navigation
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| `Ctrl+1` | Sketcher |
|
||||
| `Ctrl+2` | Part Design |
|
||||
| `Ctrl+3` | Assembly |
|
||||
| `Ctrl+4` | Spreadsheet |
|
||||
| `Ctrl+5` | TechDraw |
|
||||
| `Ctrl+6-9` | User-configurable / additional workbenches |
|
||||
|
||||
Switching workbench via `Ctrl+N` also restores that workbench's last-active command grouping.
|
||||
|
||||
---
|
||||
|
||||
## 4. Navigation Bar
|
||||
|
||||
The navigation bar is a `QToolBar` positioned at the bottom of the main window (replacing or sitting alongside FreeCAD's default bottom toolbar area).
|
||||
|
||||
### 4.1 Layout
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ [WB: Sketcher] │ ❶ Primitives │ ② Constraints │ ③ Dimensions │ ◀▶ │
|
||||
│ │ 1:Line 2:Rect 3:Circle 4:Arc 5:Point 6:Slot ... │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
- **Left section:** Current workbench name with Ctrl+N hint
|
||||
- **Middle section (top row):** Command groupings, MRU ordered. Active grouping is ❶ (filled circle), others are ②③ etc. Scrollable horizontally if >9.
|
||||
- **Middle section (bottom row):** Commands within the active grouping, numbered 1-9
|
||||
- **Right section:** Scroll arrows for overflow groupings
|
||||
|
||||
### 4.2 Visual States
|
||||
|
||||
- **Active grouping:** Bold text, filled number badge, Catppuccin Mocha `blue` (#89b4fa) accent
|
||||
- **Inactive groupings:** Normal text, outlined number badge, `surface1` (#45475a) text
|
||||
- **Hovered command:** `surface2` (#585b70) background highlight
|
||||
- **Active command (tool in use):** `green` (#a6e3a1) underline indicator
|
||||
|
||||
### 4.3 Mouse Interaction
|
||||
|
||||
- Click any grouping to activate it (equivalent to Shift+N)
|
||||
- Click any command to execute it (equivalent to pressing N)
|
||||
- Scroll wheel on grouping area to cycle through overflow groupings
|
||||
- Click scroll arrows to page through overflow
|
||||
|
||||
---
|
||||
|
||||
## 5. Workbench Command Groupings
|
||||
|
||||
Each workbench's existing FreeCAD toolbars map to command groupings. Where a workbench has >9 toolbars, split into Tier 1 (default, most common) and Tier 2 (accessible via scrolling or `Shift+Left/Right`).
|
||||
|
||||
### 5.1 Sketcher (Ctrl+1)
|
||||
|
||||
| Grouping | Commands (1-9) |
|
||||
|----------|---------------|
|
||||
| Primitives | Line, Rectangle, Circle, Arc, Point, Slot, B-Spline, Polyline, Ellipse |
|
||||
| Constraints | Coincident, Horizontal, Vertical, Parallel, Perpendicular, Tangent, Equal, Symmetric, Block |
|
||||
| Dimensions | Distance, Horizontal Distance, Vertical Distance, Radius, Diameter, Angle, Lock, Constrain Refraction |
|
||||
| Construction | Toggle Construction, External Geometry, Carbon Copy, Offset, Trim, Extend, Split |
|
||||
| Tools | Mirror, Array (Linear), Array (Polar), Move, Rotate, Scale, Close Shape, Connect Edges |
|
||||
|
||||
### 5.2 Part Design (Ctrl+2)
|
||||
|
||||
| Grouping | Commands (1-9) |
|
||||
|----------|---------------|
|
||||
| Additive | Pad, Revolution, Additive Loft, Additive Pipe, Additive Helix, Additive Box, Additive Cylinder, Additive Sphere, Additive Cone |
|
||||
| Subtractive | Pocket, Hole, Groove, Subtractive Loft, Subtractive Pipe, Subtractive Helix, Subtractive Box, Subtractive Cylinder, Subtractive Sphere |
|
||||
| Datums | New Sketch, Datum Plane, Datum Line, Datum Point, Shape Binder, Sub-Shape Binder, ZTools Datum Creator, ZTools Datum Manager |
|
||||
| Transformations | Mirrored, Linear Pattern, Polar Pattern, MultiTransform, ZTools Rotated Linear Pattern |
|
||||
| Modeling | Fillet, Chamfer, Draft, Thickness, Boolean, ZTools Enhanced Pocket |
|
||||
|
||||
### 5.3 Assembly (Ctrl+3)
|
||||
|
||||
| Grouping | Commands (1-9) |
|
||||
|----------|---------------|
|
||||
| Components | Insert Component, Create Part, Create Assembly, Ground, BOM |
|
||||
| Joints | Fixed, Revolute, Cylindrical, Slider, Ball, Planar, Distance, Angle, Parallel |
|
||||
| Patterns | ZTools Linear Pattern, ZTools Polar Pattern |
|
||||
|
||||
### 5.4 Spreadsheet (Ctrl+4)
|
||||
|
||||
| Grouping | Commands (1-9) |
|
||||
|----------|---------------|
|
||||
| Editing | Merge Cells, Split Cell, Alias, Import CSV, Export CSV |
|
||||
| Formatting | Bold, Italic, Underline, Align Left, Align Center, Align Right, BG Color, Text Color, Quick Alias |
|
||||
|
||||
### 5.5 TechDraw (Ctrl+5)
|
||||
|
||||
Groupings derived from TechDraw's existing toolbars at runtime.
|
||||
|
||||
> **Note:** The exact command lists above are initial defaults. The addon discovers available commands from each workbench's toolbar structure at activation time and falls back to these defaults only if discovery fails.
|
||||
|
||||
---
|
||||
|
||||
## 6. MRU History Behavior
|
||||
|
||||
### 6.1 Grouping History (per workbench)
|
||||
|
||||
Each workbench maintains its own grouping MRU stack.
|
||||
|
||||
- When a grouping is activated (via `Shift+N` or mouse click), it moves to position 0
|
||||
- The previously active grouping moves to position 1, everything else shifts down
|
||||
- Position 0 is always the active grouping (already selected, shown leftmost)
|
||||
- `Shift+1` is a no-op (already active), `Shift+2` activates the previous grouping, etc.
|
||||
|
||||
### 6.2 Workbench History
|
||||
|
||||
- Workbenches have fixed Ctrl+N assignments (not MRU ordered)
|
||||
- However, each workbench remembers its last-active grouping
|
||||
- Switching to a workbench restores its last-active grouping as position 0
|
||||
|
||||
### 6.3 Persistence
|
||||
|
||||
Stored in `FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/QuickNav")`:
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `Enabled` | Bool | Whether QuickNav is currently active |
|
||||
| `GroupHistory/<Workbench>` | String | Semicolon-delimited list of grouping names in MRU order |
|
||||
| `LastGrouping/<Workbench>` | String | Name of the last-active grouping per workbench |
|
||||
| `CustomSlots/Ctrl6` through `Ctrl9` | String | Workbench names for user-configurable slots |
|
||||
|
||||
---
|
||||
|
||||
## 7. Auto-Execution Logic
|
||||
|
||||
When a command is invoked via number key, QuickNav checks if the command can be auto-executed:
|
||||
|
||||
### 7.1 Auto-Execute Conditions
|
||||
|
||||
A command auto-executes (runs and completes without entering a persistent mode) when:
|
||||
|
||||
1. **Pad/Pocket after closed sketch:** If the active body has a sketch that was just closed (sketch edit mode exited with a closed profile), pressing the Pad or Pocket command key creates the feature with default parameters. The task panel still opens for parameter adjustment.
|
||||
2. **Boolean operations:** If exactly two bodies/shapes are selected, boolean commands execute with defaults.
|
||||
3. **Constraint application:** If appropriate geometry is pre-selected in Sketcher, constraint commands apply immediately.
|
||||
|
||||
### 7.2 Mode-Entry (Default)
|
||||
|
||||
All other commands enter their standard FreeCAD tool mode — identical to clicking the toolbar button. The user interacts with the 3D view and/or task panel as normal.
|
||||
|
||||
---
|
||||
|
||||
## 8. Key Event Handling
|
||||
|
||||
### 8.1 Event Filter Architecture
|
||||
|
||||
```python
|
||||
class QuickNavEventFilter(QObject):
|
||||
"""Installed on FreeCAD's main window via installEventFilter().
|
||||
|
||||
Intercepts KeyPress events when QuickNav is active.
|
||||
Passes through all events when QuickNav is inactive.
|
||||
"""
|
||||
|
||||
def eventFilter(self, obj, event):
|
||||
if event.type() != QEvent.KeyPress:
|
||||
return False
|
||||
if not self._active:
|
||||
return False
|
||||
|
||||
# Don't intercept when a text input widget has focus
|
||||
focused = QApplication.focusWidget()
|
||||
if isinstance(focused, (QLineEdit, QTextEdit, QPlainTextEdit, QSpinBox, QDoubleSpinBox)):
|
||||
return False
|
||||
|
||||
# Don't intercept when task panel input fields are focused
|
||||
if self._is_task_panel_input(focused):
|
||||
return False
|
||||
|
||||
key = event.key()
|
||||
modifiers = event.modifiers()
|
||||
|
||||
if key == Qt.Key_0 and modifiers == Qt.NoModifier:
|
||||
self.toggle_active()
|
||||
return True
|
||||
|
||||
if key >= Qt.Key_1 and key <= Qt.Key_9:
|
||||
n = key - Qt.Key_0
|
||||
if modifiers == Qt.ControlModifier:
|
||||
self.switch_workbench(n)
|
||||
return True
|
||||
elif modifiers == Qt.ShiftModifier:
|
||||
self.switch_grouping(n)
|
||||
return True
|
||||
elif modifiers == Qt.NoModifier:
|
||||
self.execute_command(n)
|
||||
return True
|
||||
|
||||
return False # Pass through all other keys
|
||||
```
|
||||
|
||||
### 8.2 Conflict Resolution
|
||||
|
||||
QuickNav's event filter takes priority when active. FreeCAD's existing keybindings for `Ctrl+1` through `Ctrl+9` (if any) are overridden while QuickNav is enabled. The original bindings are restored when QuickNav is toggled off or unloaded.
|
||||
|
||||
Existing `Shift+` and bare number key bindings in FreeCAD are similarly overridden only while QuickNav is active. This is safe because:
|
||||
- FreeCAD does not use bare number keys as shortcuts by default
|
||||
- Shift+number is not commonly bound in default FreeCAD
|
||||
|
||||
### 8.3 Input Widget Safety
|
||||
|
||||
The event filter must NOT intercept keys when the user is:
|
||||
- Typing in the Python console
|
||||
- Entering values in the task panel (dimensions, parameters)
|
||||
- Editing spreadsheet cells
|
||||
- Typing in any `QLineEdit`, `QTextEdit`, `QSpinBox`, or `QDoubleSpinBox`
|
||||
- Using the Sketcher's inline dimension input
|
||||
|
||||
---
|
||||
|
||||
## 9. Addon Structure
|
||||
|
||||
```
|
||||
mods/quicknav/
|
||||
├── package.xml # FreeCAD addon manifest with <kindred> extension
|
||||
├── Init.py # Non-GUI initialization (no-op)
|
||||
├── InitGui.py # Registers QuickNavWorkbench
|
||||
├── quicknav/
|
||||
│ ├── __init__.py
|
||||
│ ├── core.py # QuickNavManager singleton — orchestrates state
|
||||
│ ├── event_filter.py # QuickNavEventFilter (QObject)
|
||||
│ ├── nav_bar.py # NavigationBar (QToolBar subclass)
|
||||
│ ├── workbench_map.py # Fixed workbench → Ctrl+N mapping + grouping discovery
|
||||
│ ├── history.py # MRU stack with ParamGet persistence
|
||||
│ ├── auto_exec.py # Auto-execution condition checks
|
||||
│ ├── commands.py # FreeCAD command wrappers (QuickNav_Toggle, etc.)
|
||||
│ └── resources/
|
||||
│ ├── icons/ # Number badge SVGs, QuickNav icon
|
||||
│ └── theme.py # Catppuccin Mocha color tokens
|
||||
└── tests/
|
||||
└── test_history.py # MRU stack unit tests
|
||||
```
|
||||
|
||||
### 9.1 Manifest
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<package format="1">
|
||||
<name>QuickNav</name>
|
||||
<description>Keyboard-driven toolbar navigation</description>
|
||||
<version>0.1.0</version>
|
||||
<maintainer email="dev@kindred-systems.com">Kindred Systems</maintainer>
|
||||
<license>LGPL-2.1</license>
|
||||
<content>
|
||||
<workbench>
|
||||
<classname>QuickNavWorkbench</classname>
|
||||
</workbench>
|
||||
</content>
|
||||
<kindred>
|
||||
<min_create_version>0.1.0</min_create_version>
|
||||
<load_priority>10</load_priority>
|
||||
<pure_python>true</pure_python>
|
||||
<dependencies>
|
||||
<dependency>sdk</dependency>
|
||||
</dependencies>
|
||||
</kindred>
|
||||
</package>
|
||||
```
|
||||
|
||||
### 9.2 Activation
|
||||
|
||||
QuickNav activates when its workbench is loaded (via the addon loader or manual activation). It installs the event filter on the main window and creates the navigation bar. The workbench itself is invisible — it does not add its own toolbars or menus beyond the navigation bar. It acts as a transparent overlay on whatever workbench the user is actually working in.
|
||||
|
||||
```python
|
||||
class QuickNavWorkbench(Gui.Workbench):
|
||||
"""Invisible workbench that installs QuickNav on load.
|
||||
|
||||
QuickNav doesn't replace the active workbench — it layers on top.
|
||||
Loading QuickNav installs the event filter and nav bar, then
|
||||
immediately re-activates the previously active workbench.
|
||||
"""
|
||||
|
||||
def Initialize(self):
|
||||
QuickNavManager.instance().install()
|
||||
|
||||
def Activated(self):
|
||||
# Re-activate the previous workbench so QuickNav is transparent
|
||||
prev = QuickNavManager.instance().previous_workbench
|
||||
if prev:
|
||||
Gui.activateWorkbench(prev)
|
||||
|
||||
def Deactivated(self):
|
||||
pass
|
||||
|
||||
def GetClassName(self):
|
||||
return "Gui::PythonWorkbench"
|
||||
```
|
||||
|
||||
**Alternative (preferred for Create):** Instead of a workbench, QuickNav can be activated directly from `Create/InitGui.py` at boot, gated by the `Enabled` preference. This avoids the workbench-switching dance entirely. The `QuickNavWorkbench` registration is kept for standalone FreeCAD compatibility.
|
||||
|
||||
---
|
||||
|
||||
## 10. Command Discovery
|
||||
|
||||
At activation time, QuickNav introspects each workbench's toolbars to build the command grouping map.
|
||||
|
||||
```python
|
||||
def discover_groupings(workbench_name: str) -> list[CommandGrouping]:
|
||||
"""Discover command groupings from a workbench's toolbar structure.
|
||||
|
||||
1. Temporarily activate the workbench (if not already active)
|
||||
2. Enumerate QToolBars from the main window
|
||||
3. Map toolbar name → list of QAction names
|
||||
4. Filter out non-command actions (separators, widgets)
|
||||
5. Split into tiers if >9 groupings
|
||||
6. Restore the previously active workbench
|
||||
"""
|
||||
```
|
||||
|
||||
### 10.1 Fallback Defaults
|
||||
|
||||
If toolbar discovery fails (workbench not initialized, empty toolbars), QuickNav falls back to the hardcoded groupings in Section 5. These are stored as a Python dict in `workbench_map.py`.
|
||||
|
||||
### 10.2 ZTools Integration
|
||||
|
||||
ZTools commands injected via `WorkbenchManipulator` appear in the discovered toolbars and are automatically included in the relevant groupings. No special handling is needed — QuickNav discovers commands after all manipulators have run.
|
||||
|
||||
---
|
||||
|
||||
## 11. FreeCAD Compatibility
|
||||
|
||||
QuickNav is designed as a standalone FreeCAD addon that works without Kindred Create or the SDK.
|
||||
|
||||
| Feature | FreeCAD | Kindred Create |
|
||||
|---------|---------|----------------|
|
||||
| Core navigation (keys, nav bar) | ✅ | ✅ |
|
||||
| Catppuccin Mocha theming | ❌ (uses Qt defaults) | ✅ (via SDK theme tokens) |
|
||||
| Auto-boot on startup | ❌ (manual workbench activation) | ✅ (via addon loader) |
|
||||
| ZTools commands in groupings | ❌ (not present) | ✅ (discovered from manipulated toolbars) |
|
||||
|
||||
The SDK dependency is optional — QuickNav checks for `kindred_sdk` availability and degrades gracefully:
|
||||
|
||||
```python
|
||||
try:
|
||||
from kindred_sdk.theme import get_theme_tokens
|
||||
THEME = get_theme_tokens()
|
||||
except ImportError:
|
||||
THEME = None # Use Qt default palette
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. Implementation Phases
|
||||
|
||||
### Phase 1: Core Infrastructure
|
||||
- Event filter with key interception and input widget safety
|
||||
- QuickNavManager singleton with toggle on/off
|
||||
- Navigation bar widget (QToolBar) with basic layout
|
||||
- Hardcoded workbench/grouping maps from Section 5
|
||||
- ParamGet persistence for enabled state
|
||||
|
||||
### Phase 2: Dynamic Discovery
|
||||
- Toolbar introspection for command grouping discovery
|
||||
- MRU history with persistence
|
||||
- Grouping overflow scrolling
|
||||
- Workbench restore (last-active grouping per workbench)
|
||||
|
||||
### Phase 3: Auto-Execution
|
||||
- Context-aware auto-execute logic
|
||||
- Sketcher closed-profile detection for Pad/Pocket
|
||||
- Pre-selection constraint application
|
||||
|
||||
### Phase 4: Polish
|
||||
- Number badge SVG icons
|
||||
- Catppuccin Mocha theming (conditional on SDK)
|
||||
- Scroll animations
|
||||
- Settings dialog (custom Ctrl+6-9 assignments)
|
||||
- FreeCAD standalone packaging
|
||||
|
||||
---
|
||||
|
||||
## 13. Open Questions
|
||||
|
||||
1. **Tier switching UX:** When a workbench has >9 groupings split into tiers, should `Shift+0` toggle between tiers, or should tiers be purely a scroll/mouse concept?
|
||||
|
||||
2. **Visual number badges:** Should the commands in the nav bar show keycap-style badges (like `⌨ 1`) or just prepend the number (`1: Line`)?
|
||||
|
||||
3. **Sketcher inline dimension input:** FreeCAD's Sketcher has an inline dimension entry that isn't a standard QLineEdit. Need to verify the event filter correctly identifies and skips this widget.
|
||||
|
||||
4. **Ctrl+N conflicts with Create shortcuts:** Verify that Create/Silo don't already bind Ctrl+1 through Ctrl+9. The Silo toggle uses Ctrl+O/S/N, so these should be clear.
|
||||