diff --git a/.github/workflows/cura-all-installers.yml b/.github/workflows/cura-all-installers.yml index b024d035a5..4729c4198a 100644 --- a/.github/workflows/cura-all-installers.yml +++ b/.github/workflows/cura-all-installers.yml @@ -1,5 +1,5 @@ name: Cura All Installers -run-name: ${{ inputs.cura_conan_version }} for exe ${{ inputs.build_windows_exe }}, msi ${{ inputs.build_windows_msi }}, dmg ${{ inputs.build_macos }}, appimage ${{ inputs.build_linux }} - enterprise ${{ inputs.enterprise }} +run-name: ${{ inputs.cura_conan_version }} for exe ${{ inputs.build_windows_exe }}, msi ${{ inputs.build_windows_msi }}, dmg ${{ inputs.build_macos }}, pkg ${{ inputs.build_macos_installer }}, appimage ${{ inputs.build_linux }} - enterprise ${{ inputs.enterprise }} on: workflow_dispatch: @@ -40,7 +40,7 @@ on: required: true type: boolean build_windows_msi: - description: 'Build for Windows msi' + description: 'Build for msi+pkg' default: true required: true type: boolean @@ -50,7 +50,7 @@ on: required: true type: boolean build_macos: - description: 'Build for MacOS' + description: 'Build dmg for MacOS' default: true required: true type: boolean @@ -120,7 +120,7 @@ jobs: msi_installer: false secrets: inherit - macos-installer-create: + macos-dmg-create: if: ${{ inputs.build_macos }} uses: ./.github/workflows/cura-installer.yml with: @@ -134,3 +134,18 @@ jobs: installer: ${{ inputs.installer }} msi_installer: false secrets: inherit + + macos-installer-create: + if: ${{ inputs.build_macos }} + uses: ./.github/workflows/cura-installer.yml + with: + platform: 'macos-11' + os_name: 'mac' + cura_conan_version: ${{ inputs.cura_conan_version }} + conan_args: ${{ inputs.conan_args }} + conan_config: ${{ inputs.conan_config }} + enterprise: ${{ inputs.enterprise }} + staging: ${{ inputs.staging }} + installer: ${{ inputs.installer }} + msi_installer: true + secrets: inherit diff --git a/.github/workflows/cura-installer.yml b/.github/workflows/cura-installer.yml index fef4547891..56c885e0c5 100644 --- a/.github/workflows/cura-installer.yml +++ b/.github/workflows/cura-installer.yml @@ -120,7 +120,7 @@ jobs: - name: Install MacOS system requirements if: ${{ runner.os == 'Macos' }} - run: brew install autoconf automake ninja + run: brew install autoconf automake ninja create-dmg # Delete create-dmg when deprecating dmg - name: Install Linux system requirements if: ${{ runner.os == 'Linux' }} @@ -251,7 +251,7 @@ jobs: if "${{ runner.os }}" == "Windows": installer_ext = "msi" if "${{ inputs.msi_installer }}" == "true" else "exe" elif "${{ runner.os }}" == "macOS": - installer_ext = "pkg" + installer_ext = "pkg" if "${{ inputs.msi_installer }}" == "true" else "dmg" else: installer_ext = "AppImage" output_env = os.environ["GITHUB_OUTPUT"] @@ -308,9 +308,9 @@ jobs: run: python ../cura_inst/packaging/AppImage/create_appimage.py ./UltiMaker-Cura $CURA_VERSION_FULL "${{ steps.filename.outputs.FULL_INSTALLER_FILENAME }}" working-directory: dist - - name: Create the MacOS pkg (Bash) + - name: Create the MacOS dmg and/or pkg (Bash) if: ${{ github.event.inputs.installer == 'true' && runner.os == 'Macos' }} - run: python ../cura_inst/packaging/MacOS/build_macos.py . "${{ steps.filename.outputs.FULL_INSTALLER_FILENAME }}" + run: python ../cura_inst/packaging/MacOS/build_macos.py ../cura_inst . $CURA_CONAN_VERSION "${{ steps.filename.outputs.FULL_INSTALLER_FILENAME }}" "$CURA_APP_NAME" working-directory: dist - name: Upload the artifacts diff --git a/UltiMaker-Cura.spec.jinja b/UltiMaker-Cura.spec.jinja index ba5b6e11c2..3d540d3b8f 100644 --- a/UltiMaker-Cura.spec.jinja +++ b/UltiMaker-Cura.spec.jinja @@ -253,9 +253,9 @@ class UMBUNDLE(BUNDLE): app = UMBUNDLE( coll, - name='{{ name }}.app', + name='{{ display_name }}.app', icon={{ icon }}, - bundle_identifier={{ osx_bundle_identifier }}, + bundle_identifier={{ osx_bundle_identifier }} + "_" + '{{ display_name }}'.replace(" ", "_") + "_" {{ short_version }}, version={{ version }}, info_plist={ 'CFBundleDisplayName': '{{ display_name }}', @@ -264,6 +264,7 @@ app = UMBUNDLE( 'CFBundleExecutable': '{{ name }}', 'CFBundleInfoDictionaryVersion': '6.0', 'CFBundlePackageType': 'APPL', + 'CFBundleVersionString': {{ version }}, 'CFBundleShortVersionString': {{ short_version }}, 'CFBundleDocumentTypes': [{ 'CFBundleTypeRole': 'Viewer', diff --git a/conanfile.py b/conanfile.py index 9a2c1773ed..7dd3c9db9b 100644 --- a/conanfile.py +++ b/conanfile.py @@ -241,7 +241,7 @@ class CuraConan(ConanFile): with open(Path(location, "UltiMaker-Cura.spec"), "w") as f: f.write(pyinstaller.render( name = str(self.options.display_name).replace(" ", "-"), - display_name = self.options.display_name, + display_name = self._app_name, entrypoint = entrypoint_location, datas = datas, binaries = binaries, diff --git a/packaging/MacOS/build_macos.py b/packaging/MacOS/build_macos.py index 06880cf9b7..dcde629900 100644 --- a/packaging/MacOS/build_macos.py +++ b/packaging/MacOS/build_macos.py @@ -9,12 +9,31 @@ from pathlib import Path ULTIMAKER_CURA_DOMAIN = os.environ.get("ULTIMAKER_CURA_DOMAIN", "nl.ultimaker.cura") -def build_pkg(dist_path: str, app_filename: str, component_filename: str, installer_filename: str) -> None: +def build_dmg(source_path: str, dist_path: str, filename: str, app_name: str) -> None: + create_dmg_executable = os.environ.get("CREATE_DMG_EXECUTABLE", "create-dmg") + + arguments = [create_dmg_executable, + "--window-pos", "640", "360", + "--window-size", "690", "503", + "--app-drop-link", "520", "272", + "--volicon", f"{source_path}/packaging/icons/VolumeIcons_Cura.icns", + "--icon-size", "90", + "--icon", app_name, "169", "272", + "--eula", f"{source_path}/packaging/cura_license.txt", + "--background", f"{source_path}/packaging/MacOs/cura_background_dmg.png", + f"{dist_path}/{filename}", + f"{dist_path}/{app_name}"] + + subprocess.run(arguments) + + +def build_pkg(dist_path: str, app_filename: str, component_filename: str, cura_version: str, installer_filename: str) -> None: """ Builds and signs the pkg installer. @param dist_path: Path to put output pkg in @param app_filename: name of the .app file to bundle inside the pkg @param component_filename: Name of the pkg component package to bundle the app in + @param cura_version: The version is used when automatically replacing existing versions with the installer. @param installer_filename: Name of the installer that contains the component package """ pkg_build_executable = os.environ.get("PKG_BUILD_EXECUTABLE", "pkgbuild") @@ -24,6 +43,7 @@ def build_pkg(dist_path: str, app_filename: str, component_filename: str, instal # This builds the component package that contains UltiMaker-Cura.app. This component package will be bundled in a distribution package. pkg_build_arguments = [ pkg_build_executable, + "--identifier", f"{ULTIMAKER_CURA_DOMAIN}_{cura_version}", # If we want to replace previous version automatically remove {cure_version} "--component", Path(dist_path, app_filename), Path(dist_path, component_filename), @@ -80,7 +100,7 @@ def notarize_file(dist_path: str, filename: str) -> None: subprocess.run(notarize_arguments) -def create_pkg_installer(filename: str, dist_path: str) -> None: +def create_pkg_installer(filename: str, dist_path: str, cura_version: str, app_name: str) -> None: """ Creates a pkg installer from {filename}.app called {filename}-Installer.pkg The final package structure is UltiMaker-Cura-XXX-Installer.pkg[UltiMaker-Cura.pkg[UltiMaker-Cura.app]]. The outer @@ -92,19 +112,44 @@ def create_pkg_installer(filename: str, dist_path: str) -> None: """ filename_stem = Path(filename).stem - cura_component_package_name = f"{filename_stem}-Component.pkg" # This is a component package that is nested inside the installer, it contains the UltiMaker-Cura.app file - app_name = "UltiMaker-Cura.app" # This is the app file that will end up in your applications folder + cura_component_package_name = f"{filename_stem}-Component.pkg" # This is a component package that is nested inside the installer, it contains the UltiMaker-Cura.app file This is the app file that will end up in your applications folder - build_pkg(dist_path, app_name, cura_component_package_name, filename) + build_pkg(dist_path, app_name, cura_component_package_name, cura_version, filename) notarize = bool(os.environ.get("NOTARIZE_INSTALLER", "FALSE")) if notarize: notarize_file(dist_path, filename) + +def create_dmg(filename: str, dist_path: str, source_path: str, app_name: str) -> None: + """ Creates a dmg executable from UltiMaker-Cura.app named {filename}.dmg + + @param filename: The name of the app file and the output dmg file without the extension + @param dist_path: The location to read the app from and save the dmg to + @param source_path: The location of the project source files + """ + + build_dmg(source_path, dist_path, filename, app_name) + + notarize_dmg = bool(os.environ.get("NOTARIZE_DMG", "TRUE")) + if notarize_dmg: + notarize_file(dist_path, filename) + + if __name__ == "__main__": parser = argparse.ArgumentParser(description = "Create installer for Cura.") - parser.add_argument("dist_path", type = str, help="Path to Pyinstaller dist folder") - parser.add_argument("filename", type = str, help = "Filename of the pkg (e.g. 'UltiMaker-Cura-5.1.0-beta-Macos-X64.pkg')") + parser.add_argument("source_path", type = str, help = "Path to Pyinstaller source folder") + parser.add_argument("dist_path", type = str, help = "Path to Pyinstaller dist folder") + parser.add_argument("cura_conan_version", type = str, help="The version of cura") + parser.add_argument("filename", type = str, help = "Filename of the pkg/dmg (e.g. 'UltiMaker-Cura-5.1.0-beta-Macos-X64.pkg' or 'UltiMaker-Cura-5.1.0-beta-Macos-X64.dmg')") + parser.add_argument("app_name", type = str, help = "Filename of the .app that will be contained within the dmg/pkg") args = parser.parse_args() - create_pkg_installer(args.filename, args.dist_path) + cura_version = args.cura_conan_version.split("/")[-1] + + app_name = f"{args.app_name}.app" + + if Path(args.filename).suffix == ".pkg": + create_pkg_installer(args.filename, args.dist_path, cura_version, app_name) + else: + create_dmg(args.filename, args.dist_path, args.source_path, app_name)