CI: improve release workflow

adapt weekly build workflow to do normal releases too, rename accordingly
skip macos singning setup if certificate not available (useful to run on forks)
add missing dmgbuild dependency for badge icons on macos
build windows installer in workflow, add needed dependencies to pixi.toml
reorganize packaging scripts that can be useful outside rattler-build too
do some cleanup
add .gitignore to rattler-build
Properly configure appimage updating depending on release type and upload zsync file
This commit is contained in:
Adrian Insaurralde Avalos
2025-11-20 00:30:43 -03:00
parent d466ba037b
commit 46f3cf2f19
19 changed files with 2285 additions and 2235 deletions

View File

@@ -1,5 +1,7 @@
name: Weekly Build
name: Build Release
on:
release:
types: [created]
schedule:
- cron: "0 0 * * 3"
workflow_dispatch:
@@ -8,10 +10,10 @@ permissions:
contents: write
jobs:
tag_build:
upload_src:
runs-on: ubuntu-latest
outputs:
build_tag: ${{ steps.tag_build.outputs.build_tag }}
build_tag: ${{ steps.get_tag.outputs.build_tag }}
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
@@ -21,20 +23,25 @@ jobs:
- name: Checkout Source
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: ${{ github.sha }}
fetch-depth: 2
fetch-tags: true
submodules: 'recursive'
- name: Tag Build
id: tag_build
- name: get tag and create release if weekly
id: get_tag
shell: bash -l {0}
env:
GH_TOKEN: ${{ github.token }}
run: |
export BUILD_TAG=weekly-$(date "+%Y.%m.%d")
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"
gh release create ${BUILD_TAG} --title "Development Build ${BUILD_TAG}" -F .github/workflows/weekly-build-notes.md --prerelease || true
- name: Upload Source
id: upload_source
@@ -42,10 +49,10 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}
run: |
python3 package/rattler-build/scripts/make_version_file.py ../freecad_version.txt
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/rattler-build/scripts/disable_git_info.patch
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 \
@@ -57,7 +64,7 @@ jobs:
gh release upload --clobber ${BUILD_TAG} "freecad_source_${BUILD_TAG}.tar.gz" "freecad_source_${BUILD_TAG}.tar.gz-SHA256.txt"
build:
needs: tag_build
needs: upload_src
strategy:
matrix:
include:
@@ -88,10 +95,8 @@ jobs:
- name: Set Platform Environment Variables
shell: bash -l {0}
env:
BUILD_TAG: ${{ needs.tag_build.outputs.build_tag }}
OPERATING_SYSTEM: ${{ runner.os }}
run: |
echo "BUILD_TAG=${BUILD_TAG}" >> "$GITHUB_ENV"
if [[ $OPERATING_SYSTEM == 'Windows' ]]; then
echo 'PIXI_CACHE_DIR=D:\rattler' >> "$GITHUB_ENV"
echo 'RATTLER_CACHE_DIR=D:\rattler' >> "$GITHUB_ENV"
@@ -100,6 +105,7 @@ jobs:
- name: Checkout Source
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: ${{ github.sha }}
fetch-depth: 2
fetch-tags: true
submodules: 'recursive'
@@ -110,6 +116,7 @@ jobs:
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 }}
@@ -120,9 +127,15 @@ jobs:
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_Weekly.provisionprofile
PP_PATH=$RUNNER_TEMP/FreeCAD_bundle.provisionprofile
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
export KEYCHAIN_PASSWORD=$(openssl rand -base64 8)
@@ -152,12 +165,13 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}
SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }}
SIGN_RELEASE: "true"
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: |
python3 package/rattler-build/scripts/make_version_file.py ../freecad_version.txt
git apply package/rattler-build/scripts/disable_git_info.patch
python3 package/scripts/write_version_info.py ../freecad_version.txt
cd package/rattler-build
pixi install
pixi run -e package create_bundle

View File

@@ -3,7 +3,7 @@ files_to_sync:
- .packit.yaml
actions:
post-upstream-clone:
- bash -c 'BUILD_TAG=dev /usr/bin/python3 package/rattler-build/scripts/make_version_file.py freecad_version.txt'
- 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''

12
package/rattler-build/.gitignore vendored Normal file
View File

@@ -0,0 +1,12 @@
linux/AppDir/usr
linux/AppDir/*.desktop
linux/AppDir/*.svg
linux/AppDir/packages.txt
windows/FreeCAD_*Windows*
osx/FreeCAD.app
**.AppImage
**.AppImage.zsync
**.dmg
**.7z
**.exe
**-SHA256.txt

View File

@@ -1,7 +1,7 @@
#!/bin/bash
set -e
set -x
set -x
conda_env="AppDir/usr"
@@ -9,9 +9,6 @@ mkdir -p ${conda_env}
cp -a ../.pixi/envs/default/* ${conda_env}
export PATH="${PWD}/${conda_env}/bin:${PATH}"
export CONDA_PREFIX="${PWD}/${conda_env}"
echo -e "\nDelete unnecessary stuff"
rm -rf ${conda_env}/include
find ${conda_env} -name \*.a -delete
@@ -49,7 +46,7 @@ rm -rf ${conda_env}/lib/cmake/
find . -name "*.h" -type f -delete
find . -name "*.cmake" -type f -delete
python_version=$(python -c 'import platform; print("py" + platform.python_version_tuple()[0] + platform.python_version_tuple()[1])')
python_version=$(${conda_env}/bin/python -c 'import platform; print("py" + platform.python_version_tuple()[0] + platform.python_version_tuple()[1])')
version_name="FreeCAD_${BUILD_TAG}-Linux-$(uname -m)-${python_version}"
echo -e "\################"
@@ -62,6 +59,20 @@ sed -i "1s/.*/\nLIST OF PACKAGES:/" AppDir/packages.txt
curl -LO https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-$(uname -m).AppImage
chmod a+x appimagetool-$(uname -m).AppImage
if [ "${UPLOAD_RELEASE}" == "true" ]; then
case "${BUILD_TAG}" in
*weekly*)
GH_UPDATE_TAG="weeklies"
;;
*rc*)
GH_UPDATE_TAG="${BUILD_TAG}"
;;
*)
GH_UPDATE_TAG="latest"
;;
esac
fi
echo -e "\nCreate the appimage"
# export GPG_TTY=$(tty)
chmod a+x ./AppDir/AppRun
@@ -69,7 +80,7 @@ chmod a+x ./AppDir/AppRun
--comp zstd \
--mksquashfs-opt -Xcompression-level \
--mksquashfs-opt 22 \
-u "gh-releases-zsync|FreeCAD|FreeCAD|${BUILD_TAG}|FreeCAD*$(uname -m)*.AppImage.zsync" \
-u "gh-releases-zsync|FreeCAD|FreeCAD|${GH_UPDATE_TAG}|FreeCAD*$(uname -m)*.AppImage.zsync" \
AppDir ${version_name}.AppImage
# -s --sign-key ${GPG_KEY_ID} \
@@ -77,5 +88,13 @@ echo -e "\nCreate hash"
sha256sum ${version_name}.AppImage > ${version_name}.AppImage-SHA256.txt
if [ "${UPLOAD_RELEASE}" == "true" ]; then
gh release upload --clobber ${BUILD_TAG} "${version_name}.AppImage" "${version_name}.AppImage-SHA256.txt"
gh release upload --clobber ${BUILD_TAG} "${version_name}.AppImage" "${version_name}.AppImage.zsync" "${version_name}.AppImage-SHA256.txt"
if [ "${GH_UPDATE_TAG}" == "weeklies" ]; then
generic_name="FreeCAD_weekly-Linux-$(uname -m)"
mv "${version_name}.AppImage" "${generic_name}.AppImage"
mv "${version_name}.AppImage.zsync" "${generic_name}.AppImage.zsync"
mv "${version_name}.AppImage-SHA256.txt" "${generic_name}.AppImage-SHA256.txt"
gh release create weeklies --prerelease | true
gh release upload --clobber weeklies "${generic_name}.AppImage" "${generic_name}.AppImage.zsync" "${generic_name}.AppImage-SHA256.txt"
fi
fi

View File

@@ -9,9 +9,6 @@ mkdir -p ${conda_env}
cp -a ../.pixi/envs/default/* ${conda_env}
export PATH="${PWD}/${conda_env}/bin:${PATH}"
export CONDA_PREFIX="${PWD}/${conda_env}"
# delete unnecessary stuff
rm -rf ${conda_env}/include
find ${conda_env} -name \*.a -delete
@@ -50,7 +47,7 @@ cmake --build build
mkdir -p FreeCAD.app/Contents/MacOS
cp build/FreeCAD FreeCAD.app/Contents/MacOS/FreeCAD
python_version=$(python -c 'import platform; print("py" + platform.python_version_tuple()[0] + platform.python_version_tuple()[1])')
python_version=$(${conda_env}/bin/python -c 'import platform; print("py" + platform.python_version_tuple()[0] + platform.python_version_tuple()[1])')
version_name="FreeCAD_${BUILD_TAG}-macOS-$(uname -m)-${python_version}"
application_menu_name="FreeCAD_${BUILD_TAG}"
@@ -71,7 +68,7 @@ rm -rf ${conda_env}/Library
if [[ "${SIGN_RELEASE}" == "true" ]]; then
# create the signed dmg
./macos_sign_and_notarize.zsh -p "FreeCAD" -k ${SIGNING_KEY_ID} -o "${version_name}.dmg"
../../scripts/macos_sign_and_notarize.zsh -p "FreeCAD" -k ${SIGNING_KEY_ID} -o "${version_name}.dmg"
else
# create the dmg
dmgbuild -s dmg_settings.py "FreeCAD" "${version_name}.dmg"

View File

@@ -1,3 +1,5 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# Ensure default values are set in defines if they are not already provided
defines.setdefault('containing_folder', '.')
defines.setdefault('app_name', 'FreeCAD.app')

File diff suppressed because it is too large Load Diff

View File

@@ -22,44 +22,37 @@ freecad = { path = "." }
[feature.package.dependencies]
python = ">=3.11,<3.12"
[feature.package.tasks]
create_bundle = 'bash -c "cd $(bash scripts/get_os.bash) && bash create_bundle.sh"'
## Linux (x86-64)
[feature.package.target.linux-64.dependencies]
coreutils = "*"
[feature.package.target.linux-64.tasks]
create_bundle = 'bash -c "cd linux && bash create_bundle.sh"'
## Linux (aarch64)
[feature.package.target.linux-aarch64.dependencies]
coreutils = "*"
[feature.package.target.linux-aarch64.tasks]
create_bundle = 'bash -c "cd linux && bash create_bundle.sh"'
## macOS (Intel)
[feature.package.target.osx-64.dependencies]
coreutils = "*"
dmgbuild = "*"
pyobjc-framework-Quartz = "*"
sed = "*"
[feature.package.target.osx-64.tasks]
create_bundle = 'bash -c "cd osx && bash create_bundle.sh"'
## macOS (Apple Silicon)
[feature.package.target.osx-arm64.dependencies]
coreutils = "*"
dmgbuild = "*"
pyobjc-framework-Quartz = "*"
sed = "*"
[feature.package.target.osx-arm64.tasks]
create_bundle = 'bash -c "cd osx && bash create_bundle.sh"'
## Windows dependencies (x86-64)
[feature.package.target.win-64.dependencies]
git = "*"
[feature.package.target.win-64.tasks]
create_bundle = 'bash -c "cd windows && bash create_bundle.sh"'
nsis = { version = "*", build = "*_log*" }
7zip = "*"
vs2022_win-64 = "*"
[environments]
default = ["freecad"]

View File

@@ -145,7 +145,6 @@ requirements:
- gmsh
- graphviz
- ifcopenshell
- jinja2
- lark
- lxml
- matplotlib-base

View File

@@ -1,13 +0,0 @@
diff --git a/src/Tools/SubWCRev.py b/src/Tools/SubWCRev.py
index 1f3f0a436343..c314003736f1 100644
--- a/src/Tools/SubWCRev.py
+++ b/src/Tools/SubWCRev.py
@@ -523,7 +523,7 @@ def main():
inp = open("%s/src/Build/Version.h.in" % (bindir))
lines = inp.readlines()
inp.close()
- lines = i.writeVersion(lines)
+ #lines = i.writeVersion(lines)
out = open("%s/src/Build/Version.h.out" % (bindir), "w")
out.writelines(lines)
out.write("\n")

View File

@@ -1,53 +0,0 @@
import sys
import os
import subprocess
import platform
from datetime import datetime
import freecad
import FreeCAD
package_manager = "conda"
system = platform.platform().split("-")[0]
arch = platform.machine()
# Windows uses a different syntax
if arch == "AMD64":
arch = "x86_64"
if "ARCH" in os.environ:
if os.environ["ARCH"] != "":
arch = os.environ["ARCH"]
python_version = platform.python_version().split(".")
python_version = "py" + python_version[0] + python_version[1]
date = str(datetime.now()).split(" ")[0]
version_info = FreeCAD.Version()
build_version_suffix = FreeCAD.ConfigGet("BuildVersionSuffix")
dev_version = version_info[0] + "." + version_info[1] + "." + version_info[2] + build_version_suffix
revision = version_info[3].split(" ")[0]
if system == "macOS":
import jinja2
print("create plist from template")
osx_directory = os.path.join(os.path.dirname(__file__), "..", "osx")
with open(os.path.join(osx_directory, "Info.plist.template")) as template_file:
template_str = template_file.read()
template = jinja2.Template(template_str)
rendered_str = template.render( FREECAD_VERSION="{}-{}".format(dev_version, revision),
APPLICATION_MENU_NAME="FreeCAD-{}-{}".format(dev_version, revision) )
with open(os.path.join(osx_directory, "FreeCAD.app", "Contents", "Info.plist"), "w") as rendered_file:
rendered_file.write(rendered_str)
if "DEPLOY_RELEASE" in os.environ and os.environ["DEPLOY_RELEASE"] == "weekly-builds":
dev_version = "weekly-builds"
revision_separator = "-"
else:
revision_separator = ""
revision = ""
bundle_name = f"FreeCAD_{dev_version}{revision_separator}{revision}-{package_manager}-{system}-{arch}-{python_version}"
with open("bundle_name.txt", "w") as bundle_name_file:
bundle_name_file.write(bundle_name)

View File

@@ -0,0 +1,8 @@
#!/usr/bin/env bash
if [[ "$OSTYPE" =~ (msys*|cygwin*|mingw*) ]]; then
echo windows
elif [[ "$OSTYPE" == darwin* ]]; then
echo osx
elif [[ "$OSTYPE" == linux* ]]; then
echo linux
fi

View File

@@ -1,56 +0,0 @@
set conda_env="fc_env"
robocopy ..\.pixi\envs\default\* %conda_env% /S /MT:%NUMBER_OF_PROCESSORS% > nul
%conda_env%\python ..\scripts\get_freecad_version.py
set /p freecad_version_name= <bundle_name.txt
echo **********************
echo %freecad_version_name%
echo **********************
REM remove arm binaries that fail to extract unless using latest 7zip
for /r %conda_env% %%i in (*arm*.exe) do (@echo "%%i will be removed" & @del "%%i")
set copy_dir="FreeCAD_Conda_Build"
mkdir %copy_dir%
REM Copy Conda's Python and (U)CRT to FreeCAD/bin
robocopy %conda_env%\DLLs %copy_dir%\bin\DLLs /S /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Lib %copy_dir%\bin\Lib /XD __pycache__ /S /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Scripts %copy_dir%\bin\Scripts /S /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\python*.* %copy_dir%\bin\ /XF *.pdb /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\msvc*.* %copy_dir%\bin\ /XF *.pdb /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\ucrt*.* %copy_dir%\bin\ /XF *.pdb /MT:%NUMBER_OF_PROCESSORS% > nul
REM Copy meaningful executables
robocopy %conda_env%\Library\bin %copy_dir%\bin\ ccx.exe /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\bin %copy_dir%\bin\ gmsh.exe /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\bin %copy_dir%\bin\ dot.exe /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\bin %copy_dir%\bin\ unflatten.exe /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\mingw-w64\bin * %copy_dir%\bin\ /MT:%NUMBER_OF_PROCESSORS% > nul
REM Copy Conda's QT5/plugins to FreeCAD/bin
robocopy %conda_env%\Library\plugins %copy_dir%\bin\ /S /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\resources %copy_dir%\resources /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\translations %copy_dir%\translations /MT:%NUMBER_OF_PROCESSORS% > nul
REM get all the dependency .dlls
robocopy %conda_env%\Library\bin *.dll %copy_dir%\bin /XF *.pdb /XF api*.* /MT:%NUMBER_OF_PROCESSORS% > nul
REM Copy FreeCAD build
robocopy %conda_env%\Library\bin FreeCAD* %copy_dir%\bin /XF *.pdb /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\data %copy_dir%\data /XF *.txt /S /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\Ext %copy_dir%\Ext /S /XD __pycache__ /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\lib %copy_dir%\lib /XF *.lib /XF *.prl /XF *.sh /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\Mod %copy_dir%\Mod /S /XD __pycache__ /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\doc %copy_dir%\doc ThirdPartyLibraries.html LICENSE.html /MT:%NUMBER_OF_PROCESSORS% > nul
REM Apply Patches
rename %copy_dir%\bin\Lib\ssl.py ssl-orig.py
copy ssl-patch.py %copy_dir%\bin\Lib\ssl.py
cd %copy_dir%\..
ren %copy_dir% %freecad_version_name%
dir
REM if errorlevel1 exit 1
"%ProgramFiles%\7-Zip\7z.exe" a -t7z -mx9 -mmt=%NUMBER_OF_PROCESSORS% %freecad_version_name%.7z %freecad_version_name%\ -bb
certutil -hashfile "%freecad_version_name%.7z" SHA256 > "%freecad_version_name%.7z"-SHA256.txt
echo %date%-%time% >>"%freecad_version_name%.7z"-SHA256.txt

View File

@@ -3,23 +3,9 @@
set -e
set -x
conda_env="fc_env"
conda_env="$(pwd)/../.pixi/envs/default/"
mkdir -p ${conda_env}
cp -a ../.pixi/envs/default/* ${conda_env}
export PATH="${PWD}/${conda_env}/bin:${PATH}"
export CONDA_PREFIX="${PWD}/${conda_env}"
# remove arm binaries that fail to extract unless using latest 7zip
rm $(find ${conda_env} -name \*arm\*.exe)
# delete unnecessary stuff
rm -rf ${conda_env}/include
find ${conda_env} -name \*.a -delete
copy_dir="FreeCAD_Conda_Build"
copy_dir="FreeCAD_Windows"
mkdir -p ${copy_dir}/bin
# Copy Conda's Python and (U)CRT to FreeCAD/bin
@@ -48,16 +34,28 @@ cp -a ${conda_env}/Library/lib ${copy_dir}/lib
cp -a ${conda_env}/Library/Mod ${copy_dir}/Mod
mkdir -p ${copy_dir}/doc
cp -a ${conda_env}/Library/doc/{ThirdPartyLibraries.html,LICENSE.html} ${copy_dir}/doc
rm -rf ${conda_env}/bin_tmp
# delete unnecessary stuff
find ${copy_dir} -name \*.a -delete
find ${copy_dir} -name \*.lib -delete
find ${copy_dir} -name \*arm\*.exe -delete # arm binaries that fail to extract unless using latest 7zip
# Apply Patches
mv ${copy_dir}/bin/Lib/ssl.py ssl-orig.py
mv ${copy_dir}/bin/Lib/ssl.py .ssl-orig.py
cp ssl-patch.py ${copy_dir}/bin/Lib/ssl.py
echo '[Paths]' >> ${copy_dir}/bin/qt6.conf
echo 'Prefix = ../lib/qt6' >> ${copy_dir}/bin/qt6.conf
python_version=$(python -c 'import platform; print("py" + platform.python_version_tuple()[0] + platform.python_version_tuple()[1])')
# convenient shortcuts to run the binaries
if [ -x /c/ProgramData/chocolatey/tools/shimgen.exe ]; then
pushd ${copy_dir}
/c/ProgramData/chocolatey/tools/shimgen.exe -p bin/freecadcmd.exe -i "$(pwd)/../../../WindowsInstaller/icons/FreeCAD.ico" -o "$(pwd)/FreeCADCmd.exe"
/c/ProgramData/chocolatey/tools/shimgen.exe --gui -p bin/freecad.exe -i "$(pwd)/../../../WindowsInstaller/icons/FreeCAD.ico" -o "$(pwd)/FreeCAD.exe"
popd
fi
python_version=$("${copy_dir}"/bin/python.exe -c 'import platform; print("py" + platform.python_version_tuple()[0] + platform.python_version_tuple()[1])')
version_name="FreeCAD_${BUILD_TAG}-Windows-$(uname -m)-${python_version}"
echo -e "################"
@@ -69,11 +67,37 @@ sed -i '1s/.*/\nLIST OF PACKAGES:/' ${copy_dir}/packages.txt
mv ${copy_dir} ${version_name}
"${PROGRAMFILES}/7-Zip/7z.exe" a -t7z -mx9 -mmt=${NUMBER_OF_PROCESSORS} ${version_name}.7z ${version_name} -bb
7z a -t7z -mx9 -mmt=${NUMBER_OF_PROCESSORS} ${version_name}.7z ${version_name} -bb
# create hash
sha256sum ${version_name}.7z > ${version_name}.7z-SHA256.txt
if [ "${MAKE_INSTALLER}" == "true" ]; then
FILES_FREECAD="$(cygpath -w $(pwd))\\${version_name}"
nsis_cpdir=$(pwd)/.nsis_tmp
cp -r "${CONDA_PREFIX}/NSIS" "${nsis_cpdir}"
# curl -L -o ".nsis-log.zip" http://prdownloads.sourceforge.net/nsis/nsis-3.11-log.zip # we use the log variant of the package already
# curl -L -o ".nsis-strlen_8192.zip" "http://prdownloads.sourceforge.net/nsis/nsis-3.11-strlen_8192.zip"
curl -L -o ".NsProcess.7z" "https://nsis.sourceforge.io/mediawiki/images/1/18/NsProcess.zip"
if [ ! $(echo fc19fc66a5219a233570fafd5daeb0c9b85387b379f6df5ac8898159a57c5944 .NsProcess.7z | sha256sum --check --status) ]; then
7z x .NsProcess.7z -o"${nsis_cpdir}" -y
mv "${nsis_cpdir}"/Plugin/nsProcess.dll "${nsis_cpdir}"/Plugins/x86-ansi/nsProcess.dll
mv "${nsis_cpdir}"/Plugin/nsProcessW.dll "${nsis_cpdir}"/Plugins/x86-unicode/nsProcess.dll
"${nsis_cpdir}"/makensis.exe -V4 \
-D"ExeFile=${version_name}-installer.exe" \
-D"FILES_FREECAD=${FILES_FREECAD}" \
-X'SetCompressor /FINAL lzma' \
../../WindowsInstaller/FreeCAD-installer.nsi
mv ../../WindowsInstaller/${version_name}-installer.exe .
sha256sum ${version_name}-installer.exe > ${version_name}-installer.exe-SHA256.txt
else
echo "Error: Failed to get NsProcess plugin. Aborting installer creation..."
fi
rm -rf "${nsis_cpdir}"
fi
if [ "${UPLOAD_RELEASE}" == "true" ]; then
gh release upload --clobber ${BUILD_TAG} "${version_name}.7z" "${version_name}.7z-SHA256.txt"
if [ "${MAKE_INSTALLER}" == "true" ]; then
gh release upload --clobber ${BUILD_TAG} "${version_name}-installer.exe" "${version_name}-installer.exe-SHA256.txt"
fi
fi

View File

@@ -83,8 +83,11 @@ fi
# Check for dmgbuild executable
if ! command -v dmgbuild &> /dev/null; then
echo "Error: dmgbuild not installed. Please install it for example using pip:"
echo 'pip3 install "dmgbuild[badge_icons]>=1.6.0,<1.7.0"'
echo 'Error: dmgbuild not installed. Please install it'
echo '- using pixi:'
echo 'pixi g install dmgbuild --with pyobjc-framework-Quartz'
echo '- using pip:'
echo 'pip3 install dmgbuild pyobjc-framework-Quartz'
exit 1
fi
@@ -125,13 +128,13 @@ ID_FILE="${DMG_NAME}.notarization_id"
# This is a *very slow* process, and occasionally the GitHub runners lose the internet connection for a short time
# during the run. So in order to be fault-tolerant, this script polls, instead of using --wait
submit_notarization_request() {
if [[ -s "$ID_FILE" ]]; then
cat "$ID_FILE"
if [[ -s "${ID_FILE}" ]]; then
cat "${ID_FILE}"
return
fi
local out
if ! out=$(xcrun notarytool submit --keychain-profile "$KEYCHAIN_PROFILE" \
--output-format json --no-progress "$DMG_NAME" 2>&1); then
if ! out=$(xcrun notarytool submit --keychain-profile "${KEYCHAIN_PROFILE}" \
--output-format json --no-progress "${DMG_NAME}" 2>&1); then
print -r -- "$out" >&2
return 1
fi
@@ -142,14 +145,14 @@ submit_notarization_request() {
/usr/bin/python3 -c 'import sys, json; print(json.load(sys.stdin).get("id",""))'
)
[[ -n "$id" ]] || { print -r -- "Could not parse submission id" >&2; return 1; }
print -r -- "$id" > "$ID_FILE"
print -r -- "$id" > "${ID_FILE}"
print -r -- "$id" # ID is a string here, not an integer, so I can't just return it
}
wait_for_notarization_result() {
local id="$1" attempt=0
while :; do
if xcrun notarytool wait "$id" --keychain-profile "$KEYCHAIN_PROFILE" \
if xcrun notarytool wait "$id" --keychain-profile "${KEYCHAIN_PROFILE}" \
--timeout 10m --no-progress >/dev/null; then
return 0
fi
@@ -161,7 +164,7 @@ wait_for_notarization_result() {
tmp_json=$(mktemp)
trap 'rm -f "$tmp_json"' EXIT INT TERM
xcrun notarytool info "$id" --keychain-profile "$KEYCHAIN_PROFILE" --output-format json 2>/dev/null > "$tmp_json"
xcrun notarytool info "$id" --keychain-profile "${KEYCHAIN_PROFILE}" --output-format json 2>/dev/null > "$tmp_json"
/usr/bin/python3 - "$tmp_json" <<'PY'
import sys, json
try:
@@ -180,7 +183,7 @@ PY
if [[ $rc == 2 ]]; then
print -r -- "Notarization was not accepted by Apple:" >&2
xcrun notarytool log "$id" --keychain-profile "$KEYCHAIN_PROFILE" >&2
xcrun notarytool log "$id" --keychain-profile "${KEYCHAIN_PROFILE}" >&2
return 3
fi
@@ -204,9 +207,9 @@ print "Notarization submission ID: $id"
if wait_for_notarization_result "$id"; then
print "✅ Notarization succeeded. Stapling..."
xcrun stapler staple "$DMG_NAME"
print "Stapled: $DMG_NAME"
rm -f "$ID_FILE"
xcrun stapler staple "${DMG_NAME}"
print "Stapled: ${DMG_NAME}"
rm -f "${ID_FILE}"
else
rc=$?
print "❌ Notarization failed (code $rc)." >&2

View File

@@ -11,12 +11,10 @@ import SubWCRev
gitInfo = SubWCRev.GitControl()
gitInfo.extractInfo("","")
gitDescription = os.environ['BUILD_TAG']
i = open("src/Build/Version.h.cmake")
content = []
for line in i.readlines():
line = line.replace("-${PACKAGE_VERSION_SUFFIX}",gitDescription)
line = line.replace("${PACKAGE_WCREF}",gitInfo.rev)
line = line.replace("${PACKAGE_WCDATE}",gitInfo.date)
line = line.replace("${PACKAGE_WCURL}",gitInfo.url)
@@ -28,6 +26,14 @@ with open("src/Build/Version.h.cmake", "w") as o:
content.append('#define FCRepositoryBranch "%s"\n' % (gitInfo.branch))
o.writelines(content)
with open("src/Tools/SubWCRev.py", "r") as f:
new_subwcrev = f.read()
new_subwcrev = new_subwcrev.replace("lines = i.writeVersion(lines)",
"#lines = i.writeVersion(lines) # this source package already has git info, we do nothing here")
with open("src/Tools/SubWCRev.py", "w") as f:
f.writelines(new_subwcrev)
with open(os.sys.argv[1], "w") as f:
f.write(f"rev_number: {gitInfo.rev}\n")
f.write(f"branch_name: {gitInfo.branch}\n")
@@ -35,8 +41,9 @@ with open(os.sys.argv[1], "w") as f:
f.write(f"commit_hash: {gitInfo.hash}\n")
f.write(f"remote_url: {gitInfo.url}\n")
p = subprocess.Popen(["git", "-c", "user.name='github-actions[bot]'", "-c", "user.email='41898282+github-actions[bot]@users.noreply.github.com'",
"commit", "-a", "-m", "add git information"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p = subprocess.Popen(["git", "-c", "user.name='github-actions[bot]'", "-c",
"user.email='41898282+github-actions[bot]@users.noreply.github.com'", "commit", "-a", "-m",
"add git version information"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()

View File

@@ -1,8 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
files = [f"{containing_folder}/FreeCAD.app"]
symlinks = {"Applications": "/Applications"}
badge_icon = f"{containing_folder}/FreeCAD.app/Contents/Resources/freecad.icns"
window_rect = ((200, 200), (600, 400))
icon_locations = {"FreeCAD.app": (180, 150), "Applications": (420, 150)}
size = "3g"

View File

@@ -1,63 +0,0 @@
#!/bin/zsh
# SPDX-License-Identifier: LGPL-2.1-or-later
# This script signs and notarizes a FreeCAD.app bundle. It expects that the bundle is in a folder
# by itself (that folder will be used as the basis for the created disk image file, so anything
# else in it will become part of the image). That folder should be located in the same folder as
# this script.
# An environment variable called FREECAD_SIGNING_KEY_ID must exist that contains the ID of a
# Developer ID Application certificate that has been installed into the login keychain. See the
# output of
# security find-identity -p basic -v
# for a list of available keys, and the documentation for
# xcrun notarytool store-credentials
# for instructions on how to configure the credentials for the tool for use before running this
# script.
# CONFIGURATION OPTIONS
CONTAINING_FOLDER="FreeCAD_0.21.1_arm64" # Must contain FreeCAD.app and nothing else
ARCH="arm64" # intel_x86 or arm64
VERSION_MAJOR="0"
VERSION_MINOR="21"
VERSION_PATCH="1"
VERSION_SUFFIX="" # e.g. alpha, beta, RC1, RC2, release
function run_codesign {
echo "Signing $1"
codesign --options runtime -f -s ${FREECAD_SIGNING_KEY_ID} --timestamp --entitlements entitlements.plist $1
}
IFS=$'\n'
dylibs=($(find ${CONTAINING_FOLDER}/FreeCAD.app -name "*.dylib"))
shared_objects=($(find ${CONTAINING_FOLDER}/FreeCAD.app -name "*.so"))
executables=($(file `find . -type f -perm +111 -print` | grep "Mach-O 64-bit executable" | sed 's/:.*//g'))
IFS=$' \t\n' # The default
signed_files=("${dylibs[@]}" "${shared_objects[@]}" "${executables[@]}")
# This list of files is generated from:
# file `find . -type f -perm +111 -print` | grep "Mach-O 64-bit executable" | sed 's/:.*//g'
for exe in ${signed_files}; do
run_codesign "${exe}"
done
# Two additional files that must be signed that aren't caught by the above searches:
run_codesign "${CONTAINING_FOLDER}/FreeCAD.app/Contents/packages.txt"
run_codesign "${CONTAINING_FOLDER}/FreeCAD.app/Contents/Library/QuickLook/QuicklookFCStd.qlgenerator/Contents/MacOS/QuicklookFCStd"
# Finally, sign the app itself (must be done last)
run_codesign "${CONTAINING_FOLDER}/FreeCAD.app"
# Create a disk image from the folder
DMG_NAME="FreeCAD-${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}-mac-${ARCH}.dmg"
echo "Creating disk image ${DMG_NAME}"
pip3 install "dmgbuild[badge_icons]>=1.6.0,<1.7.0"
dmgbuild -s dmg_settings.py -Dcontaining_folder="${CONTAINING_FOLDER}" "FreeCAD" "${DMG_NAME}.dmg"
# Submit it for notarization (requires that an App Store API Key has been set up in the notarytool)
xcrun notarytool submit --wait --keychain-profile "FreeCAD" ${DMG_NAME}
# Assuming that notarization succeeded, it's a good practice to staple that notarization to the DMG
xcrun stapler staple ${DMG_NAME}

View File

@@ -1,206 +0,0 @@
#!/bin/sh
# ***************************************************************************
# * Copyright (c) 2023 Yorik van Havre <yorik@uncreated.net> *
# * and FreeCAD maintainers (looooo, adrianinsaval ) *
# * *
# * 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 *
# * *
# ***************************************************************************
# adapted from https://github.com/FreeCAD/FreeCAD-Bundle/blob/master/conda/linux/create_bundle.sh
# This script produces a FreeCAD AppImage locally using conda repositories. It
# is an all-in-one script that fetches FreeCAD and all dependencies from conda
# repo (it also extracts a couple of XDG files from the FreeCAD source). Everything
# comes from conda repositories, therefore, you need to make sure they contain the
# FreeCAD package you want to build. An easy way to check is by searching for
# available FreeCAD packages:
#
# conda search "freecad=0.21.0" -c adrianinsaval/label/dev -c freecad -c conda-forge
#
# To use this script, you will need:
#
# 1) Mambaforge from https://github.com/conda-forge/miniforge#mambaforge
# if you choose to not install the conda environment when installing Mambaforge,
# you will need to do it before running this script, by running the following
# in your terminal. This will make the "conda" and "mamba" commands available:
# eval "$(/home/yorik/Mambaforge/bin/conda shell.zsh hook)"
# (replace the Mambaforge path by yours and zsh by your shell name)
#
# 2) appimagetool from https://appimage.github.io/appimagetool/
# place it in your exec path so it can be found by this script
#
# 3) a gpg key to sign the package
#
# 4) verify or change the config values here below
#
# 5) If you are on Fedora, there are lines to uncomment below in the script
#
# When done, just run this script and you should get an appimage with the
# corresponding sha hash in the current folder.
#
# To cleanup after build: Just delete the "AppDir" folder (not done automatically since
# next builds can reuse the downloaded packages).
# config
# make sure you have a gpg key for this email
gpg_key="yorik@freecad.org"
# the FreeCAD version we're looking for
target_version="1.1.0"
# make sure target_python matches the one FreeCAD is built with! Check with
# conda search "freecad=1.1.0" -c adrianinsaval/label/dev -c freecad -c conda-forge
target_python="3.10"
# end config
# export MAMBA_NO_BANNER=1
# building needed files
mkdir -p AppDir
cat > AppDir/AppRun <<EOF
#!/bin/bash
HERE="\$(dirname "\$(readlink -f "\${0}")")"
export PREFIX=\${HERE}/usr
export LD_LIBRARY_PATH=\${HERE}/usr/lib\${LD_LIBRARY_PATH:+':'}\$LD_LIBRARY_PATH
export PYTHONHOME=\${HERE}/usr
export PATH_TO_FREECAD_LIBDIR=\${HERE}/usr/lib
# export QT_QPA_PLATFORM_PLUGIN_PATH=\${HERE}/usr/plugins
# export QT_XKB_CONFIG_ROOT=\${HERE}/usr/lib
export FONTCONFIG_FILE=/etc/fonts/fonts.conf
export FONTCONFIG_PATH=/etc/fonts
# Show packages info if DEBUG env variable is set
if [ "\$DEBUG" = 1 ]; then
cat \${HERE}/packages.txt
fi
# SSL
# https://forum.freecadweb.org/viewtopic.php?f=4&t=34873&start=20#p327416
export SSL_CERT_FILE=\$PREFIX/ssl/cacert.pem
# https://github.com/FreeCAD/FreeCAD-AppImage/pull/20
export GIT_SSL_CAINFO=\$HERE/usr/ssl/cacert.pem
# Support for launching other applications (from /usr/bin)
# https://github.com/FreeCAD/FreeCAD-AppImage/issues/30
if [ ! -z "\$1" ] && [ -e "\$HERE/usr/bin/\$1" ] ; then
MAIN="\$HERE/usr/bin/\$1" ; shift
else
MAIN="\$HERE/usr/bin/freecad"
fi
\${MAIN} "\$@"
EOF
chmod +x AppDir/AppRun
# building package name
target_date=$(date +"%Y-%m-%d")
arch=$(uname -m)
package_name="FreeCAD_${target_version}-${target_date}-conda-Linux-${arch}-py$(echo ${target_python} | sed 's/\.//g')"
conda_env="AppDir/usr"
# dependencies
echo "\nCreating the environment"
packages="freecad=${target_version} occt vtk python=${target_python} \
blas=*=openblas numpy matplotlib-base scipy sympy pandas \
six pyyaml pycollada lxml xlutils olefile requests blinker \
opencv qt.py nine docutils calculix opencamlib ifcopenshell \
appimage-updater-bridge"
mamba create -p ${conda_env} ${packages} \
--copy -c adrianinsaval/label/dev \
-c freecad -c conda-forge -y
echo "\n################"
echo "package_name: ${package_name}"
echo "################"
echo "\nInstalling freecad.appimage_updater"
mamba run -p ${conda_env} pip install https://github.com/looooo/freecad.appimage_updater/archive/master.zip
echo "\nUninstalling some unneeded packages"
conda uninstall -p ${conda_env} libclang --force -y
mamba list -p ${conda_env} > AppDir/packages.txt
sed -i "1s/.*/\nLIST OF PACKAGES:/" AppDir/packages.txt
echo "\nDeleting unnecessary stuff"
rm -rf ${conda_env}/include
find ${conda_env} -name \*.a -delete
mv ${conda_env}/bin ${conda_env}/bin_tmp
mkdir ${conda_env}/bin
cp ${conda_env}/bin_tmp/freecad ${conda_env}/bin/
cp ${conda_env}/bin_tmp/freecadcmd ${conda_env}/bin/
cp ${conda_env}/bin_tmp/ccx ${conda_env}/bin/
cp ${conda_env}/bin_tmp/python ${conda_env}/bin/
cp ${conda_env}/bin_tmp/pip ${conda_env}/bin/
cp ${conda_env}/bin_tmp/pyside2-rcc ${conda_env}/bin/
cp ${conda_env}/bin_tmp/assistant ${conda_env}/bin/
sed -i '1s|.*|#!/usr/bin/env python|' ${conda_env}/bin/pip
rm -rf ${conda_env}/bin_tmp
echo "\nCreating qt config"
echo "[Paths]\nPrefix = ./../" > qt.conf
cp qt.conf ${conda_env}/bin/
cp qt.conf ${conda_env}/libexec/
rm qt.conf
echo "\nCopying icons and .desktop file"
mkdir -p ${conda_env}/share/icons/hicolor/scalable/apps/
cp ../../../src/Gui/Icons/freecad.svg ${conda_env}/share/icons/hicolor/scalable/apps/org.freecadweb.FreeCAD.svg
cp ${conda_env}/share/icons/hicolor/scalable/apps/org.freecadweb.FreeCAD.svg AppDir
mkdir -p ${conda_env}/share/icons/hicolor/64x64/apps/
cp ../../../src/Gui/Icons/freecad-icon-64.png ${conda_env}/share/icons/hicolor/64x64/apps/org.freecadweb.FreeCAD.png
cp ${conda_env}/share/icons/hicolor/64x64/apps/org.freecadweb.FreeCAD.png AppDir
mkdir -p ${conda_env}/share/applications/
cp ../../../src/XDGData/org.freecadweb.FreeCAD.desktop ${conda_env}/share/applications/
sed -i "s/Exec\=FreeCAD\ \%F/Exec=AppRun/g" ${conda_env}/share/applications/org.freecadweb.FreeCAD.desktop
cp ${conda_env}/share/applications/org.freecadweb.FreeCAD.desktop AppDir
cp ../../../src/XDGData/org.freecadweb.FreeCAD.appdata.xml.in ${conda_env}/share/metainfo/org.freecadweb.FreeCAD.appdata.xml
sed -i "s/@PACKAGE_VERSION@/${target_version}/g" ${conda_env}/share/metainfo/org.freecadweb.FreeCAD.appdata.xml
sed -i "s/@APPDATA_RELEASE_DATE@/${target_date}/g" ${conda_env}/share/metainfo/org.freecadweb.FreeCAD.appdata.xml
echo "\nCleaning"
# Remove __pycache__ folders and .pyc files
find . -path "*/__pycache__/*" -delete
find . -name "*.pyc" -type f -delete
# reduce size
rm -rf ${conda_env}/conda-meta/
rm -rf ${conda_env}/doc/global/
rm -rf ${conda_env}/share/gtk-doc/
rm -rf ${conda_env}/lib/cmake/
# remove unnecessary development files
find . -name "*.h" -type f -delete
find . -name "*.cmake" -type f -delete
# The following two lines must be uncommented if using this on Fedora 28 and up
# echo "\nAdd libnsl"
# cp ../../libc6/lib/$ARCH-linux-gnu/libnsl* ${conda_env}/lib/
echo "\nCreating the appimage"
chmod a+x ./AppDir/AppRun
appimagetool-${arch}.AppImage --sign --sign-key ${gpg_key} AppDir ${package_name}.AppImage
echo "\nCreating hash"
shasum -a 256 ${package_name}.AppImage > ${package_name}.AppImage-SHA256.txt
echo "\nAll done! You can delete the AppDir folder"