diff --git a/.github/workflows/installers.yml b/.github/workflows/installers.yml index f1bd4b2d19..0c2873cf31 100644 --- a/.github/workflows/installers.yml +++ b/.github/workflows/installers.yml @@ -30,6 +30,29 @@ on: required: true type: boolean + workflow_call: + inputs: + cura_conan_version: + default: 'cura/latest@ultimaker/testing' + required: true + type: string + conan_args: + default: '' + required: false + type: string + enterprise: + default: false + required: true + type: boolean + staging: + default: false + required: true + type: boolean + nightly: + default: false + required: true + type: boolean + schedule: # Daily at 4:15 CET (main-branch) and 5:15 CET (release-branch) - cron: '15 3 * * *' @@ -109,7 +132,7 @@ jobs: fetch-depth: 1 - name: Download the run info - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: linux-run-info @@ -151,13 +174,13 @@ jobs: f.writelines(f"NIGHTLY_TIME={nightly_creation_time}\n") - name: Download linux installer jobs artifacts - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: ${{ steps.filename.outputs.LINUX }}-AppImage path: installers - name: Download linux installer jobs asc artifacts - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: ${{ steps.filename.outputs.LINUX }}-asc path: installers @@ -175,13 +198,13 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Download win msi installer jobs artifacts - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: ${{ steps.filename.outputs.WIN_MSI }}-msi path: installers - name: Download win exe installer jobs artifacts - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: ${{ steps.filename.outputs.WIN_EXE }}-exe path: installers @@ -199,13 +222,13 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Download MacOS (X64) dmg installer jobs artifacts - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: ${{ steps.filename.outputs.MAC_X64_DMG }}-dmg path: installers - name: Download MacOS (X64) pkg installer jobs artifacts - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: ${{ steps.filename.outputs.MAC_X64_PKG }}-pkg path: installers @@ -223,13 +246,13 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Download MacOS (ARM-64) dmg installer jobs artifacts - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: ${{ steps.filename.outputs.MAC_ARM_DMG }}-dmg path: installers - name: Download MacOS (ARM-64) pkg installer jobs artifacts - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: ${{ steps.filename.outputs.MAC_ARM_PKG }}-pkg path: installers diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 08c59c9158..f9d800c77d 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -34,10 +34,11 @@ on: operating_system: description: 'OS' required: true - default: 'ubuntu-22.04' + default: 'self-hosted-Ubuntu22-X64' type: choice options: - ubuntu-22.04 + - self-hosted-Ubuntu22-X64 jobs: linux-installer: @@ -49,4 +50,4 @@ jobs: staging: ${{ inputs.staging }} architecture: ${{ inputs.architecture }} operating_system: ${{ inputs.operating_system }} - secrets: inherit \ No newline at end of file + secrets: inherit diff --git a/.github/workflows/release-process_feature-freeze.yml b/.github/workflows/release-process_feature-freeze.yml new file mode 100644 index 0000000000..3a07799665 --- /dev/null +++ b/.github/workflows/release-process_feature-freeze.yml @@ -0,0 +1,32 @@ +name: Feature Freeze +run-name: Feature freeze Cura ${{ inputs.cura_version }} by @${{ github.actor }} + +on: + workflow_dispatch: + inputs: + cura_version: + description: 'Cura version major and minor, e.g. 5.7' + required: true + type: string + +jobs: + parse-version: + name: Parse input version string + runs-on: ubuntu-latest + outputs: + package_version: ${{ steps.version_parser.outputs.major }}.${{ steps.version_parser.outputs.minor }}.0-alpha.1 + steps: + - name: Parse version string + id: version_parser + uses: booxmedialtd/ws-action-parse-semver@v1.4.7 + with: + input_string: ${{ inputs.cura_version }}.0 + + feature-freeze: + name: Process feature freeze + uses: Ultimaker/Cura-workflows/.github/workflows/cura-set-packages-versions.yml@main + needs: [parse-version] + with: + cura_version: ${{ needs.parse-version.outputs.package_version }} + create_feature_branch: true + secrets: inherit diff --git a/.github/workflows/release-process_release-candidate.yml b/.github/workflows/release-process_release-candidate.yml new file mode 100644 index 0000000000..3d59817996 --- /dev/null +++ b/.github/workflows/release-process_release-candidate.yml @@ -0,0 +1,179 @@ +name: Prepare Release Candidate +run-name: Release Candidate for Cura ${{ inputs.cura_version }} by @${{ github.actor }} + +on: + workflow_dispatch: + inputs: + cura_version: + description: 'Cura version number, e.g. 5.7.0, 5.7.2 or 5.8.0-beta.2' + required: true + type: string + +jobs: + parse-version: + name: Parse input version string + runs-on: ubuntu-latest + outputs: + version_major: ${{ steps.version_parser.outputs.major }} + version_minor: ${{ steps.version_parser.outputs.minor }} + version_patch: ${{ steps.version_parser.outputs.patch }} + branch_name: ${{ steps.version_parser.outputs.major }}.${{ steps.version_parser.outputs.minor }} + steps: + - name: Parse version string + id: version_parser + uses: booxmedialtd/ws-action-parse-semver@v1.4.7 + with: + input_string: ${{ inputs.cura_version }} + + freeze-packages-versions: + name: Freeze packges versions + uses: Ultimaker/Cura-workflows/.github/workflows/cura-set-packages-versions.yml@main + needs: [parse-version] + with: + cura_version: ${{ inputs.cura_version }} + create_feature_branch: false + secrets: inherit + + find-rc-tag: + name: Find RC tag name + runs-on: ubuntu-latest + needs: [freeze-packages-versions] + outputs: + tag_name: ${{ steps.find-available-tag-name.outputs.tag_name }} + steps: + - name: Checkout repo + uses: actions/checkout@v4 + with: + fetch-tags: true + fetch-depth: 0 + + - name: Find available tag name + id: find-available-tag-name + run: | + VERSION=${{ inputs.cura_version }} + + RC_INDEX=0 + while + RC_INDEX=$((RC_INDEX+1)) + TAG_NAME="$VERSION-RC$RC_INDEX" + [[ $(git tag -l "$TAG_NAME") ]] + do true; done + + echo "tag_name=$TAG_NAME" >> "$GITHUB_OUTPUT" + + create-tags: + name: Create tags + runs-on: ubuntu-latest + needs: [parse-version, find-rc-tag] + strategy: + matrix: + repository: [Cura, Uranium, CuraEngine, cura-binary-data, fdm_materials] + steps: + - name: Checkout repo + uses: actions/checkout@v4 + with: + repository: Ultimaker/${{ matrix.repository }} + ref: ${{ needs.parse-version.outputs.branch_name }} + token: ${{ secrets.CURA_AUTORELEASE_PAT }} + + - name: Create tag + run: | + git tag ${{ needs.find-rc-tag.outputs.tag_name }} + git push origin tag ${{ needs.find-rc-tag.outputs.tag_name }} + + create-dependencies-packages: + name: Create conan packages for dependencies + uses: ultimaker/cura-workflows/.github/workflows/conan-package-release.yml@main + needs: [parse-version, freeze-packages-versions] + strategy: + matrix: + repository: [Cura, Uranium, CuraEngine, cura-binary-data, fdm_materials] + include: + - conan_recipe_root: "." + - repository: Cura + conan_recipe_root: "resources" + with: + repository: ${{ matrix.repository }} + ref_name: ${{ needs.parse-version.outputs.branch_name }} + version: ${{ inputs.cura_version }} + conan_release: true + conan_user_channel: ultimaker/stable + conan_internal: false + conan_latest: true + conan_recipe_root: ${{ matrix.conan_recipe_root }} + secrets: inherit + + create-cura-package: + name: Create conan package for Cura + uses: ultimaker/cura-workflows/.github/workflows/conan-package-release.yml@main + needs: [parse-version, create-dependencies-packages] + with: + repository: Cura + ref_name: ${{ needs.parse-version.outputs.branch_name }} + version: ${{ inputs.cura_version }} + conan_release: true + conan_user_channel: ultimaker/stable + conan_internal: false + conan_latest: true + secrets: inherit + + create-installers: + name: Create installers + uses: ./.github/workflows/installers.yml + needs: [parse-version, create-cura-package] + with: + cura_conan_version: cura/${{ inputs.cura_version }}@/ + enterprise: false + staging: false + nightly: false + secrets: inherit + + create-release-draft: + name: Create the release draft + runs-on: ubuntu-latest + needs: [create-installers, parse-version] + steps: + - name: Checkout Cura repo + uses: actions/checkout@v4 + with: + ref: ${{ needs.parse-version.outputs.branch_name }} + + - name: Extract changelog + run: python ./scripts/extract_changelog.py --version ${{ needs.parse-version.outputs.version_major }}.${{ needs.parse-version.outputs.version_minor }}.${{ needs.parse-version.outputs.version_patch }} --changelog ./resources/texts/change_log.txt > formatted_changelog.txt + + - name: Get commit id for release + id: get-commit-id + uses: iawia002/get-tag-or-commit-id@v1.0.1 + with: + length: 40 + + - name: Create release + uses: notpeelz/action-gh-create-release@v5.0.1 + with: + target: ${{ steps.get-commit-id.outputs.id }} + tag: ${{ inputs.cura_version }} + strategy: replace + title: UltiMaker Cura ${{ inputs.cura_version }} + draft: true + body-source: file + body: formatted_changelog.txt + + - name: Download artifacts + uses: actions/download-artifact@v4.1.7 + with: + path: artifacts + merge-multiple: true + + - name: Upload artifacts + working-directory: artifacts + run: | + gh release upload ${{ inputs.cura_version }} UltiMaker-Cura-${{ inputs.cura_version }}-linux-X64.AppImage --clobber + gh release upload ${{ inputs.cura_version }} UltiMaker-Cura-${{ inputs.cura_version }}-linux-X64.AppImage.asc --clobber + gh release upload ${{ inputs.cura_version }} UltiMaker-Cura-${{ inputs.cura_version }}-macos-ARM64.dmg --clobber + gh release upload ${{ inputs.cura_version }} UltiMaker-Cura-${{ inputs.cura_version }}-macos-ARM64.pkg --clobber + gh release upload ${{ inputs.cura_version }} UltiMaker-Cura-${{ inputs.cura_version }}-macos-X64.dmg --clobber + gh release upload ${{ inputs.cura_version }} UltiMaker-Cura-${{ inputs.cura_version }}-macos-X64.pkg --clobber + gh release upload ${{ inputs.cura_version }} UltiMaker-Cura-${{ inputs.cura_version }}-win64-X64.exe --clobber + gh release upload ${{ inputs.cura_version }} UltiMaker-Cura-${{ inputs.cura_version }}-win64-X64.msi --clobber + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 09f972bb1a..704aa4ddde 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -50,4 +50,4 @@ jobs: staging: ${{ inputs.staging }} architecture: ${{ inputs.architecture }} operating_system: ${{ inputs.operating_system }} - secrets: inherit \ No newline at end of file + secrets: inherit diff --git a/cura/API/Account.py b/cura/API/Account.py index c6027acdb5..d126a52d60 100644 --- a/cura/API/Account.py +++ b/cura/API/Account.py @@ -115,15 +115,15 @@ class Account(QObject): self._update_timer.setSingleShot(True) self._update_timer.timeout.connect(self.sync) - self._sync_services: Dict[str, int] = {} """contains entries "service_name" : SyncState""" - self.syncRequested.connect(self._updatePermissions) + self._sync_services: Dict[str, int] = {} def initialize(self) -> None: self._authorization_service.initialize(self._application.getPreferences()) self._authorization_service.onAuthStateChanged.connect(self._onLoginStateChanged) self._authorization_service.onAuthenticationError.connect(self._onLoginStateChanged) self._authorization_service.accessTokenChanged.connect(self._onAccessTokenChanged) + self._authorization_service.accessTokenChanged.connect(self._updatePermissions) self._authorization_service.loadAuthDataFromPreferences() @pyqtProperty(int, notify=syncStateChanged) diff --git a/plugins/3MFWriter/ThreeMFWriter.py b/plugins/3MFWriter/ThreeMFWriter.py index d33f0e374a..5a9fa487fc 100644 --- a/plugins/3MFWriter/ThreeMFWriter.py +++ b/plugins/3MFWriter/ThreeMFWriter.py @@ -96,7 +96,8 @@ class ThreeMFWriter(MeshWriter): @staticmethod def _convertUMNodeToSavitarNode(um_node, transformation = Matrix(), - exported_settings: Optional[Dict[str, Set[str]]] = None): + exported_settings: Optional[Dict[str, Set[str]]] = None, + center_mesh = False): """Convenience function that converts an Uranium SceneNode object to a SavitarSceneNode :returns: Uranium Scene node. @@ -111,16 +112,20 @@ class ThreeMFWriter(MeshWriter): savitar_node = Savitar.SceneNode() savitar_node.setName(um_node.getName()) - node_matrix = Matrix() mesh_data = um_node.getMeshData() - # compensate for original center position, if object(s) is/are not around its zero position - if mesh_data is not None: - extents = mesh_data.getExtents() - if extents is not None: - # We use a different coordinate space while writing, so flip Z and Y - center_vector = Vector(extents.center.x, extents.center.z, extents.center.y) - node_matrix.setByTranslation(center_vector) - node_matrix.multiply(um_node.getLocalTransformation()) + + if center_mesh: + node_matrix = Matrix() + # compensate for original center position, if object(s) is/are not around its zero position + if mesh_data is not None: + extents = mesh_data.getExtents() + if extents is not None: + # We use a different coordinate space while writing, so flip Z and Y + center_vector = Vector(extents.center.x, extents.center.y, extents.center.z) + node_matrix.setByTranslation(center_vector) + node_matrix.multiply(um_node.getLocalTransformation()) + else: + node_matrix = um_node.getLocalTransformation() matrix_string = ThreeMFWriter._convertMatrixToString(node_matrix.preMultiply(transformation)) @@ -147,7 +152,7 @@ class ThreeMFWriter(MeshWriter): for key in changed_setting_keys: savitar_node.setSetting("cura:" + key, str(stack.getProperty(key, "value"))) else: - # We want to export only the specified settings + # We want to export only the specified settings if um_node.getName() in exported_settings: model_exported_settings = exported_settings[um_node.getName()] @@ -283,7 +288,8 @@ class ThreeMFWriter(MeshWriter): for root_child in node.getChildren(): savitar_node = ThreeMFWriter._convertUMNodeToSavitarNode(root_child, transformation_matrix, - exported_model_settings) + exported_model_settings, + center_mesh = True) if savitar_node: savitar_scene.addSceneNode(savitar_node) else: @@ -442,7 +448,7 @@ class ThreeMFWriter(MeshWriter): def sceneNodesToString(scene_nodes: [SceneNode]) -> str: savitar_scene = Savitar.Scene() for scene_node in scene_nodes: - savitar_node = ThreeMFWriter._convertUMNodeToSavitarNode(scene_node) + savitar_node = ThreeMFWriter._convertUMNodeToSavitarNode(scene_node, center_mesh = True) savitar_scene.addSceneNode(savitar_node) parser = Savitar.ThreeMFParser() scene_string = parser.sceneToString(savitar_scene) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index c73dee2fb0..dd7987bc42 100755 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -544,7 +544,7 @@ class CuraEngineBackend(QObject, Backend): if job.getResult() == StartJobResult.ObjectsWithDisabledExtruder: self._error_message = Message(catalog.i18nc("@info:status", - "Unable to slice because there are objects associated with disabled Extruder %s.") % job.getMessage(), + "Unable to slice because there are objects associated with disabled Extruder %s.") % job.getAssociatedDisabledExtruders(), title = catalog.i18nc("@info:title", "Unable to slice"), message_type = Message.MessageType.WARNING) self._error_message.show() diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index 3a1d7ce431..9caadbdff6 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -146,6 +146,7 @@ class StartSliceJob(Job): self._slice_message: Arcus.PythonMessage = slice_message self._is_cancelled: bool = False self._build_plate_number: Optional[int] = None + self._associated_disabled_extruders: Optional[str] = None # cache for all setting values from all stacks (global & extruder) for the current machine self._all_extruders_settings: Optional[Dict[str, Any]] = None @@ -153,6 +154,9 @@ class StartSliceJob(Job): def getSliceMessage(self) -> Arcus.PythonMessage: return self._slice_message + def getAssociatedDisabledExtruders(self) -> Optional[str]: + return self._associated_disabled_extruders + def setBuildPlate(self, build_plate_number: int) -> None: self._build_plate_number = build_plate_number @@ -334,7 +338,7 @@ class StartSliceJob(Job): if has_model_with_disabled_extruders: self.setResult(StartJobResult.ObjectsWithDisabledExtruder) associated_disabled_extruders = {p + 1 for p in associated_disabled_extruders} - self.setMessage(", ".join(map(str, sorted(associated_disabled_extruders)))) + self._associated_disabled_extruders = ", ".join(map(str, sorted(associated_disabled_extruders))) return # There are cases when there is nothing to slice. This can happen due to one at a time slicing not being diff --git a/requirements.txt b/requirements.txt index 76339c884f..344a0156d8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -206,9 +206,9 @@ chardet==3.0.4 \ idna==2.8 \ --hash=sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407 \ --hash=sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c -attrs==21.2.0 \ - --hash=sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1 \ - --hash=sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb +attrs==21.3.0 \ + --hash=sha256:8f7335278dedd26b58c38e006338242cc0977f06d51579b2b8b87b9b33bff66c \ + --hash=sha256:50f3c9b216dc9021042f71b392859a773b904ce1a029077f58f6598272432045 requests==2.22.0 \ --hash=sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4 \ --hash=sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31 @@ -222,9 +222,9 @@ constantly==15.1.0 \ hyperlink==21.0.0 \ --hash=sha256:427af957daa58bc909471c6c40f74c5450fa123dd093fc53efd2e91d2705a56b \ --hash=sha256:e6b14c37ecb73e89c77d78cdb4c2cc8f3fb59a885c5b3f819ff4ed80f25af1b4 -incremental==21.3.0 \ - --hash=sha256:02f5de5aff48f6b9f665d99d48bfc7ec03b6e3943210de7cfc88856d755d6f57 \ - --hash=sha256:92014aebc6a20b78a8084cdd5645eeaa7f74b8933f70fa3ada2cfbd1e3b54321 +incremental==22.10.0 \ + --hash=sha256:b864a1f30885ee72c5ac2835a761b8fe8aa9c28b9395cacf27286602688d3e51 \ + --hash=sha256:912feeb5e0f7e0188e6f42241d2f450002e11bbc0937c65865045854c24c0bd0 zope.interface==5.4.0 \ --hash=sha256:0f91b5b948686659a8e28b728ff5e74b1be6bf40cb04704453617e5f1e945ef3 \ --hash=sha256:3c02411a3b62668200910090a0dff17c0b25aaa36145082a5a6adf08fa281e54 \ diff --git a/resources/conandata.yml b/resources/conandata.yml new file mode 100644 index 0000000000..c2971e2769 --- /dev/null +++ b/resources/conandata.yml @@ -0,0 +1 @@ +version: "5.8.0-alpha.0" diff --git a/resources/conanfile.py b/resources/conanfile.py index b4cafc705e..fde648eeac 100644 --- a/resources/conanfile.py +++ b/resources/conanfile.py @@ -28,8 +28,6 @@ class CuraResource(ConanFile): self.version = self.conan_data["version"] def export(self): - copy(self, pattern="conandata.yml", src=os.path.join(self.recipe_folder, ".."), dst=self.export_folder, - keep_path=False) copy(self, pattern="LICENSE*", src=os.path.join(self.recipe_folder, ".."), dst=self.export_folder, keep_path=False) update_conandata(self, {"version": self.version}) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 046053e62a..a124145314 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -6217,6 +6217,7 @@ "type": "bool", "default_value": false, "enabled": "resolveOrValue('adhesion_type') == 'raft'", + "resolve": "any(extruderValues('raft_remove_inside_corners'))", "settable_per_mesh": false, "settable_per_extruder": false, "children": @@ -6229,6 +6230,7 @@ "value": "raft_remove_inside_corners", "default_value": false, "enabled": "resolveOrValue('adhesion_type') == 'raft'", + "resolve": "any(extruderValues('raft_base_remove_inside_corners'))", "settable_per_mesh": false, "settable_per_extruder": false }, @@ -6240,6 +6242,7 @@ "value": "raft_remove_inside_corners", "default_value": false, "enabled": "resolveOrValue('adhesion_type') == 'raft'", + "resolve": "any(extruderValues('raft_interface_remove_inside_corners'))", "settable_per_mesh": false, "settable_per_extruder": false }, @@ -6251,6 +6254,7 @@ "value": "raft_remove_inside_corners", "default_value": false, "enabled": "resolveOrValue('adhesion_type') == 'raft'", + "resolve": "any(extruderValues('raft_surface_remove_inside_corners'))", "settable_per_mesh": false, "settable_per_extruder": false } @@ -6854,7 +6858,7 @@ "label": "Prime Tower Type", "description": "How to generate the prime tower: