Fri, 03 Jun 2022 19:54:57 +0200
CycloneDX Interface
- added capability to create a Software Bill of Materials (SBOM) file in CycloneDX format
- added this capability to the pip Interface and Project
--- a/docs/changelog Fri Jun 03 12:09:02 2022 +0200 +++ b/docs/changelog Fri Jun 03 19:54:57 2022 +0200 @@ -2,6 +2,13 @@ ---------- Version 22.7: - bug fixes +- CycloneDX Interface + -- added capability to create a Software Bill of Materials (SBOM) file in + CycloneDX format +- pip Interface + -- added SBOM capability +- Project + -- added SBOM capability Version 22.6: - bug fixes
--- a/eric7.epj Fri Jun 03 12:09:02 2022 +0200 +++ b/eric7.epj Fri Jun 03 19:54:57 2022 +0200 @@ -275,6 +275,7 @@ "eric7/CondaInterface/CondaPackageDetailsWidget.ui", "eric7/CondaInterface/CondaPackagesWidget.ui", "eric7/Cooperation/ChatWidget.ui", + "eric7/CycloneDXInterface/CycloneDXConfigDialog.ui", "eric7/DataViews/CodeMetricsDialog.ui", "eric7/DataViews/PyCoverageDialog.ui", "eric7/DataViews/PyCoverageHtmlReportDialog.ui", @@ -987,6 +988,9 @@ "eric7/Cooperation/CooperationClient.py", "eric7/Cooperation/CooperationServer.py", "eric7/Cooperation/__init__.py", + "eric7/CycloneDXInterface/CycloneDXConfigDialog.py", + "eric7/CycloneDXInterface/CycloneDXUtilities.py", + "eric7/CycloneDXInterface/__init__.py", "eric7/DataViews/CodeMetrics.py", "eric7/DataViews/CodeMetricsDialog.py", "eric7/DataViews/PyCoverageDialog.py",
--- a/eric7/APIs/Python3/eric7.api Fri Jun 03 12:09:02 2022 +0200 +++ b/eric7/APIs/Python3/eric7.api Fri Jun 03 19:54:57 2022 +0200 @@ -167,6 +167,15 @@ eric7.Cooperation.CooperationServer.CooperationServer.newConnection?7 eric7.Cooperation.CooperationServer.CooperationServer.startListening?4(port=-1, findFreePort=False) eric7.Cooperation.CooperationServer.CooperationServer?1(address, parent=None) +eric7.CycloneDXInterface.CycloneDXConfigDialog.CycloneDXConfigDialog.DefaultFileFormat?7 +eric7.CycloneDXInterface.CycloneDXConfigDialog.CycloneDXConfigDialog.DefaultFileNames?7 +eric7.CycloneDXInterface.CycloneDXConfigDialog.CycloneDXConfigDialog.Sources?7 +eric7.CycloneDXInterface.CycloneDXConfigDialog.CycloneDXConfigDialog.SupportedSchemas?7 +eric7.CycloneDXInterface.CycloneDXConfigDialog.CycloneDXConfigDialog.getData?4() +eric7.CycloneDXInterface.CycloneDXConfigDialog.CycloneDXConfigDialog.on_fileFormatComboBox_currentTextChanged?4(fileFormat) +eric7.CycloneDXInterface.CycloneDXConfigDialog.CycloneDXConfigDialog?1(environment, parent=None) +eric7.CycloneDXInterface.CycloneDXUtilities.CycloneDXEnvironmentParser?1(venvName) +eric7.CycloneDXInterface.CycloneDXUtilities.createCycloneDXFile?4(venvName) eric7.DataViews.CodeMetrics.COMMENT?7 eric7.DataViews.CodeMetrics.DEDENT?7 eric7.DataViews.CodeMetrics.EMPTY?7
--- a/eric7/APIs/Python3/eric7.bas Fri Jun 03 12:09:02 2022 +0200 +++ b/eric7/APIs/Python3/eric7.bas Fri Jun 03 19:54:57 2022 +0200 @@ -130,6 +130,8 @@ CreateDialogCodeDialog QDialog Ui_CreateDialogCodeDialog CustomHelpFormatter argparse.HelpFormatter CustomNamespace argparse.Namespace +CycloneDXConfigDialog QDialog Ui_CycloneDXConfigDialog +CycloneDXEnvironmentParser BaseParser DateTimeVisitor ast.NodeVisitor DebugClient DebugBase DebugClientBase ThreadExtension DebugServer QTcpServer
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric7/CycloneDXInterface/CycloneDXConfigDialog.py Fri Jun 03 19:54:57 2022 +0200 @@ -0,0 +1,157 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2022 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing a dialog to configure the CycloneDX SBOM generation. +""" + +import os + +from PyQt6.QtCore import pyqtSlot +from PyQt6.QtWidgets import QDialog + +from EricWidgets.EricApplication import ericApp +from EricWidgets.EricPathPicker import EricPathPickerModes + +from .Ui_CycloneDXConfigDialog import Ui_CycloneDXConfigDialog + + +class CycloneDXConfigDialog(QDialog, Ui_CycloneDXConfigDialog): + """ + Class implementing a dialog to configure the CycloneDX SBOM generation. + """ + SupportedSchemas = { + "JSON": ["1.4", "1.3", "1.2"], + "XML": ["1.4", "1.3", "1.2", "1.1", "1.0"], + } + Sources = { + "pipenv": "Pipfile.lock", + "poetry": "poetry.lock", + "requirements": "requirements.txt", + } + DefaultFileFormat = "JSON" + DefaultFileNames = { + "JSON": "cyclonedx.json", + "XML": "cyclonedx.xml", + } + + def __init__(self, environment, parent=None): + """ + Constructor + + @param environment name of the virtual environment + @type str + @param parent reference to the parent widget (defaults to None) + @type QWidget (optional) + """ + super().__init__(parent) + self.setupUi(self) + + if environment == "<project>": + project = ericApp().getObject("Project") + self.__defaultDirectory = project.getProjectPath() + else: + venvManager = ericApp().getObject("VirtualEnvManager") + self.__defaultDirectory = venvManager.getVirtualenvDirectory( + environment) + + self.environmentLabel.setText(environment) + + self.pipenvButton.setEnabled(os.path.isfile(os.path.join( + self.__defaultDirectory, + CycloneDXConfigDialog.Sources["pipenv"] + ))) + self.poetryButton.setEnabled(os.path.isfile(os.path.join( + self.__defaultDirectory, + CycloneDXConfigDialog.Sources["poetry"] + ))) + self.requirementsButton.setEnabled(os.path.isfile(os.path.join( + self.__defaultDirectory, + CycloneDXConfigDialog.Sources["requirements"] + ))) + + self.filePicker.setMode(EricPathPickerModes.SAVE_FILE_OVERWRITE_MODE) + self.filePicker.setDefaultDirectory(self.__defaultDirectory) + + self.fileFormatComboBox.setCurrentText( + CycloneDXConfigDialog.DefaultFileFormat) + self.on_fileFormatComboBox_currentTextChanged( + CycloneDXConfigDialog.DefaultFileFormat) + + msh = self.minimumSizeHint() + self.resize(max(self.width(), msh.width()), msh.height()) + + @pyqtSlot(str) + def on_fileFormatComboBox_currentTextChanged(self, fileFormat): + """ + Private slot to handle the selection of a SBOM file format. + + @param fileFormat selected format + @type str + """ + # re-populate the file schema combo box + self.schemaVersionComboBox.clear() + self.schemaVersionComboBox.addItems( + CycloneDXConfigDialog.SupportedSchemas[fileFormat]) + + # set the file filter + if fileFormat == "JSON": + self.filePicker.setFilters( + self.tr("JSON Files (*.json);;All Files (*)")) + elif fileFormat == "XML": + self.filePicker.setFilters( + self.tr("XML Files (*.xml);;All Files (*)")) + else: + self.filePicker.setFilters(self.tr("All Files (*)")) + + def getData(self): + """ + Public method to get the SBOM configuration data. + + @return tuple containing the input source, the input file name, the + file format, the schema version and the path of the SBOM file to + be written + @rtype tuple of (str, str, str, str, str) + """ + if self.environmentButton.isChecked(): + inputSource = "environment" + inputFile = None + elif self.pipenvButton.isChecked(): + inputSource = "pipenv" + inputFile = os.path.join( + self.__defaultDirectory, + CycloneDXConfigDialog.Sources["pipenv"] + ) + elif self.poetryButton.isChecked(): + inputSource = "poetry" + inputFile = os.path.join( + self.__defaultDirectory, + CycloneDXConfigDialog.Sources["poetry"] + ) + elif self.requirementsButton.isChecked(): + inputSource = "requirements" + inputFile = os.path.join( + self.__defaultDirectory, + CycloneDXConfigDialog.Sources["requirements"] + ) + else: + # should not happen + inputSource = None + inputFile = None + + fileFormat = self.fileFormatComboBox.currentText() + schemaVersion = self.schemaVersionComboBox.currentText() + sbomFile = self.filePicker.text() + if not sbomFile: + try: + sbomFile = os.path.join( + self.__defaultDirectory, + CycloneDXConfigDialog.DefaultFileNames[fileFormat] + ) + except KeyError: + # should not happen + sbomFile = None + + return inputSource, inputFile, fileFormat, schemaVersion, sbomFile
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric7/CycloneDXInterface/CycloneDXConfigDialog.ui Fri Jun 03 19:54:57 2022 +0200 @@ -0,0 +1,228 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>CycloneDXConfigDialog</class> + <widget class="QDialog" name="CycloneDXConfigDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>650</width> + <height>278</height> + </rect> + </property> + <property name="windowTitle"> + <string>SBOM Configuration</string> + </property> + <property name="sizeGripEnabled"> + <bool>true</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Environment:</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="environmentLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Input</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QRadioButton" name="environmentButton"> + <property name="toolTip"> + <string>Select to build SBOM from environment</string> + </property> + <property name="text"> + <string>Environment</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="pipenvButton"> + <property name="toolTip"> + <string>Select to build SBOM from 'Pipfile.lock' file</string> + </property> + <property name="text"> + <string>PipEnv</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="poetryButton"> + <property name="toolTip"> + <string>Select to build SBOM from 'poetry.lock' file</string> + </property> + <property name="text"> + <string>Poetry</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="requirementsButton"> + <property name="toolTip"> + <string>Select to build SBOM from 'requirements.txt' file</string> + </property> + <property name="text"> + <string>Requirements</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string>SBOM Output</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>File Format:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QComboBox" name="fileFormatComboBox"> + <property name="toolTip"> + <string>Select the format of the SBOM file</string> + </property> + <item> + <property name="text"> + <string>JSON</string> + </property> + </item> + <item> + <property name="text"> + <string>XML</string> + </property> + </item> + </widget> + </item> + <item row="0" column="2"> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>413</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Schema Version:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QComboBox" name="schemaVersionComboBox"> + <property name="toolTip"> + <string>Select the SBOM schema version of the SBOM file</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>File Name:</string> + </property> + </widget> + </item> + <item row="2" column="1" colspan="2"> + <widget class="EricPathPicker" name="filePicker" native="true"> + <property name="focusPolicy"> + <enum>Qt::StrongFocus</enum> + </property> + <property name="toolTip"> + <string>Enter the file path for the SBOM file (leave empty for default)</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>EricPathPicker</class> + <extends>QWidget</extends> + <header>EricWidgets/EricPathPicker.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>CycloneDXConfigDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>CycloneDXConfigDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric7/CycloneDXInterface/CycloneDXUtilities.py Fri Jun 03 19:54:57 2022 +0200 @@ -0,0 +1,152 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2022 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing the interface to CycloneDX. +""" + +import os + +from PyQt6.QtCore import QCoreApplication +from PyQt6.QtWidgets import QDialog + +from EricWidgets.EricApplication import ericApp +from EricWidgets import EricMessageBox + +from packageurl import PackageURL + +from cyclonedx.model import LicenseChoice +from cyclonedx.model.bom import Bom +from cyclonedx.model.component import Component +from cyclonedx.output import ( + OutputFormat, SchemaVersion, get_instance as get_output_instance +) +from cyclonedx.parser import BaseParser + +from cyclonedx_py.parser.pipenv import PipEnvFileParser +from cyclonedx_py.parser.poetry import PoetryFileParser +from cyclonedx_py.parser.requirements import RequirementsFileParser + + +class CycloneDXEnvironmentParser(BaseParser): + """ + Class implementing a parser to get package data for a named environment. + """ + def __init__(self, venvName): + """ + Constructor + + @param venvName name of the virtual environment + @type str + """ + super().__init__() + + pip = ericApp().getObject("Pip") + packages = pip.getLicenses(venvName) + for package in packages: + comp = Component( + name=package["Name"], + version=package["Version"], + author=package["Author"], + description=package["Description"], + purl=PackageURL( + type='pypi', + name=package["Name"], + version=package["Version"] + ) + ) + for lic in package["License"].split(";"): + comp.licenses.add( + LicenseChoice(license_expression=lic.strip()) + ) + + self._components.append(comp) + + +def createCycloneDXFile(venvName): + """ + Function to create a CyccloneDX SBOM file. + + @param venvName name of the virtual environment + @type str + @exception RuntimeError raised to indicate illegal creation parameters + """ + from .CycloneDXConfigDialog import CycloneDXConfigDialog + dlg = CycloneDXConfigDialog(venvName) + if dlg.exec() == QDialog.DialogCode.Accepted: + inputSource, inputFile, fileFormat, schemaVersion, sbomFile = ( + dlg.getData() + ) + + if inputSource not in ("environment", "pipenv", "poetry", + "requirements"): + raise RuntimeError("Unsupported input source given.") + if fileFormat not in ("XML", "JSON"): + raise RuntimeError("Unsupported SBOM file format given.") + + if inputSource == "environment": + parser = CycloneDXEnvironmentParser(venvName) + else: + # all other parsers need an input file + if not os.path.isfile(inputFile): + EricMessageBox.warning( + None, + QCoreApplication.translate( + "CycloneDX", "CycloneDX - SBOM Creation"), + QCoreApplication.translate( + "CycloneDX", + "<p>The configured input file <b>{0}</b> does not" + " exist. Aborting...</p>" + ).format(inputFile) + ) + return + + if inputSource == "pipenv": + parser = PipEnvFileParser(pipenv_lock_filename=inputFile) + elif inputSource == "poetry": + parser = PoetryFileParser(poetry_lock_filename=inputFile) + elif inputSource == "requirements": + parser = RequirementsFileParser(requirements_file=inputFile) + + if fileFormat == "XML": + outputFormat = OutputFormat.XML + elif fileFormat == "JSON": + outputFormat = OutputFormat.JSON + + if parser.has_warnings(): + excludedList = ["<li>{0}</li>".format(warning.get_item()) + for warning in parser.get_warnings()] + EricMessageBox.warning( + None, + QCoreApplication.translate( + "CycloneDX", "CycloneDX - SBOM Creation"), + QCoreApplication.translate( + "CycloneDX", + "<p>Some of the dependencies do not have pinned version" + " numbers.<ul>{0}</ul>The above listed packages will NOT" + " be included in the generated CycloneDX SBOM file as" + " version is a mandatory field.</p>" + ).format("".join(excludedList)) + ) + + bom = Bom.from_parser(parser=parser) + output = get_output_instance( + bom=bom, + output_format=outputFormat, + schema_version=SchemaVersion['V{0}'.format( + schemaVersion.replace('.', '_') + )] + ) + output.output_to_file(filename=sbomFile, allow_overwrite=True) + + EricMessageBox.information( + None, + QCoreApplication.translate( + "CycloneDX", "CycloneDX - SBOM Creation"), + QCoreApplication.translate( + "CycloneDX", + "<p>The SBOM data was written to file <b>{0}</b>.</p>" + ).format(sbomFile) + )
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric7/CycloneDXInterface/__init__.py Fri Jun 03 19:54:57 2022 +0200 @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2022 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Package implementing the interface to the CycloneDX tool for the generation +of Software Bill of Materials files. +""" + +from .CycloneDXUtilities import createCycloneDXFile + +__all__ = [createCycloneDXFile]
--- a/eric7/Documentation/Help/source.qhp Fri Jun 03 12:09:02 2022 +0200 +++ b/eric7/Documentation/Help/source.qhp Fri Jun 03 19:54:57 2022 +0200 @@ -28,6 +28,10 @@ <section title="eric7.Cooperation.CooperationClient" ref="eric7.Cooperation.CooperationClient.html" /> <section title="eric7.Cooperation.CooperationServer" ref="eric7.Cooperation.CooperationServer.html" /> </section> + <section title="eric7.CycloneDXInterface" ref="index-eric7.CycloneDXInterface.html"> + <section title="eric7.CycloneDXInterface.CycloneDXConfigDialog" ref="eric7.CycloneDXInterface.CycloneDXConfigDialog.html" /> + <section title="eric7.CycloneDXInterface.CycloneDXUtilities" ref="eric7.CycloneDXInterface.CycloneDXUtilities.html" /> + </section> <section title="eric7.DataViews" ref="index-eric7.DataViews.html"> <section title="eric7.DataViews.CodeMetrics" ref="eric7.DataViews.CodeMetrics.html" /> <section title="eric7.DataViews.CodeMetricsDialog" ref="eric7.DataViews.CodeMetricsDialog.html" /> @@ -3287,6 +3291,15 @@ <keyword name="CustomHelpFormatter._format_action" id="CustomHelpFormatter._format_action" ref="eric7.PipInterface.piplicenses.html#CustomHelpFormatter._format_action" /> <keyword name="CustomHelpFormatter._split_lines" id="CustomHelpFormatter._split_lines" ref="eric7.PipInterface.piplicenses.html#CustomHelpFormatter._split_lines" /> <keyword name="CustomNamespace" id="CustomNamespace" ref="eric7.PipInterface.piplicenses.html#CustomNamespace" /> + <keyword name="CycloneDXConfigDialog" id="CycloneDXConfigDialog" ref="eric7.CycloneDXInterface.CycloneDXConfigDialog.html#CycloneDXConfigDialog" /> + <keyword name="CycloneDXConfigDialog (Constructor)" id="CycloneDXConfigDialog (Constructor)" ref="eric7.CycloneDXInterface.CycloneDXConfigDialog.html#CycloneDXConfigDialog.__init__" /> + <keyword name="CycloneDXConfigDialog (Module)" id="CycloneDXConfigDialog (Module)" ref="eric7.CycloneDXInterface.CycloneDXConfigDialog.html" /> + <keyword name="CycloneDXConfigDialog.getData" id="CycloneDXConfigDialog.getData" ref="eric7.CycloneDXInterface.CycloneDXConfigDialog.html#CycloneDXConfigDialog.getData" /> + <keyword name="CycloneDXConfigDialog.on_fileFormatComboBox_currentTextChanged" id="CycloneDXConfigDialog.on_fileFormatComboBox_currentTextChanged" ref="eric7.CycloneDXInterface.CycloneDXConfigDialog.html#CycloneDXConfigDialog.on_fileFormatComboBox_currentTextChanged" /> + <keyword name="CycloneDXEnvironmentParser" id="CycloneDXEnvironmentParser" ref="eric7.CycloneDXInterface.CycloneDXUtilities.html#CycloneDXEnvironmentParser" /> + <keyword name="CycloneDXEnvironmentParser (Constructor)" id="CycloneDXEnvironmentParser (Constructor)" ref="eric7.CycloneDXInterface.CycloneDXUtilities.html#CycloneDXEnvironmentParser.__init__" /> + <keyword name="CycloneDXInterface (Package)" id="CycloneDXInterface (Package)" ref="index-eric7.CycloneDXInterface.html" /> + <keyword name="CycloneDXUtilities (Module)" id="CycloneDXUtilities (Module)" ref="eric7.CycloneDXInterface.CycloneDXUtilities.html" /> <keyword name="DataViews (Package)" id="DataViews (Package)" ref="index-eric7.DataViews.html" /> <keyword name="DateTimeVisitor" id="DateTimeVisitor" ref="eric7.Plugins.CheckerPlugins.CodeStyleChecker.Miscellaneous.MiscellaneousChecker.html#DateTimeVisitor" /> <keyword name="DateTimeVisitor (Constructor)" id="DateTimeVisitor (Constructor)" ref="eric7.Plugins.CheckerPlugins.CodeStyleChecker.Miscellaneous.MiscellaneousChecker.html#DateTimeVisitor.__init__" /> @@ -11369,6 +11382,7 @@ <keyword name="PipPackagesWidget.__allUpdateableItems" id="PipPackagesWidget.__allUpdateableItems" ref="eric7.PipInterface.PipPackagesWidget.html#PipPackagesWidget.__allUpdateableItems" /> <keyword name="PipPackagesWidget.__availablePipVersion" id="PipPackagesWidget.__availablePipVersion" ref="eric7.PipInterface.PipPackagesWidget.html#PipPackagesWidget.__availablePipVersion" /> <keyword name="PipPackagesWidget.__clearVulnerabilityInfo" id="PipPackagesWidget.__clearVulnerabilityInfo" ref="eric7.PipInterface.PipPackagesWidget.html#PipPackagesWidget.__clearVulnerabilityInfo" /> + <keyword name="PipPackagesWidget.__createSBOMFile" id="PipPackagesWidget.__createSBOMFile" ref="eric7.PipInterface.PipPackagesWidget.html#PipPackagesWidget.__createSBOMFile" /> <keyword name="PipPackagesWidget.__editConfiguration" id="PipPackagesWidget.__editConfiguration" ref="eric7.PipInterface.PipPackagesWidget.html#PipPackagesWidget.__editConfiguration" /> <keyword name="PipPackagesWidget.__editUserConfiguration" id="PipPackagesWidget.__editUserConfiguration" ref="eric7.PipInterface.PipPackagesWidget.html#PipPackagesWidget.__editUserConfiguration" /> <keyword name="PipPackagesWidget.__editVirtualenvConfiguration" id="PipPackagesWidget.__editVirtualenvConfiguration" ref="eric7.PipInterface.PipPackagesWidget.html#PipPackagesWidget.__editVirtualenvConfiguration" /> @@ -11831,6 +11845,7 @@ <keyword name="Project.__checkProjectFileGroup" id="Project.__checkProjectFileGroup" ref="eric7.Project.Project.html#Project.__checkProjectFileGroup" /> <keyword name="Project.__cleanupMake" id="Project.__cleanupMake" ref="eric7.Project.Project.html#Project.__cleanupMake" /> <keyword name="Project.__closeAllWindows" id="Project.__closeAllWindows" ref="eric7.Project.Project.html#Project.__closeAllWindows" /> + <keyword name="Project.__createSBOMFile" id="Project.__createSBOMFile" ref="eric7.Project.Project.html#Project.__createSBOMFile" /> <keyword name="Project.__createSnapshotSource" id="Project.__createSnapshotSource" ref="eric7.Project.Project.html#Project.__createSnapshotSource" /> <keyword name="Project.__createZipDirEntries" id="Project.__createZipDirEntries" ref="eric7.Project.Project.html#Project.__createZipDirEntries" /> <keyword name="Project.__deleteDebugProperties" id="Project.__deleteDebugProperties" ref="eric7.Project.Project.html#Project.__deleteDebugProperties" /> @@ -11868,6 +11883,7 @@ <keyword name="Project.__showContextMenuDebugger" id="Project.__showContextMenuDebugger" ref="eric7.Project.Project.html#Project.__showContextMenuDebugger" /> <keyword name="Project.__showContextMenuGraphics" id="Project.__showContextMenuGraphics" ref="eric7.Project.Project.html#Project.__showContextMenuGraphics" /> <keyword name="Project.__showContextMenuMake" id="Project.__showContextMenuMake" ref="eric7.Project.Project.html#Project.__showContextMenuMake" /> + <keyword name="Project.__showContextMenuOthers" id="Project.__showContextMenuOthers" ref="eric7.Project.Project.html#Project.__showContextMenuOthers" /> <keyword name="Project.__showContextMenuPackagers" id="Project.__showContextMenuPackagers" ref="eric7.Project.Project.html#Project.__showContextMenuPackagers" /> <keyword name="Project.__showContextMenuRecent" id="Project.__showContextMenuRecent" ref="eric7.Project.Project.html#Project.__showContextMenuRecent" /> <keyword name="Project.__showContextMenuSession" id="Project.__showContextMenuSession" ref="eric7.Project.Project.html#Project.__showContextMenuSession" /> @@ -18346,6 +18362,7 @@ <keyword name="createConfigurationPage" id="createConfigurationPage" ref="eric7.Plugins.PluginVcsPySvn.html#createConfigurationPage" /> <keyword name="createConfigurationPage" id="createConfigurationPage" ref="eric7.Plugins.PluginVcsSubversion.html#createConfigurationPage" /> <keyword name="createCreateProcess" id="createCreateProcess" ref="eric7.DebugClients.Python.MultiProcessDebugExtension.html#createCreateProcess" /> + <keyword name="createCycloneDXFile" id="createCycloneDXFile" ref="eric7.CycloneDXInterface.CycloneDXUtilities.html#createCycloneDXFile" /> <keyword name="createDebuggerInterfaceNone" id="createDebuggerInterfaceNone" ref="eric7.Debugger.DebuggerInterfaceNone.html#createDebuggerInterfaceNone" /> <keyword name="createDebuggerInterfacePython3" id="createDebuggerInterfacePython3" ref="eric7.Debugger.DebuggerInterfacePython.html#createDebuggerInterfacePython3" /> <keyword name="createDefaultConfig" id="createDefaultConfig" ref="eric7.Plugins.VcsPlugins.vcsPySvn.SvnUtilities.html#createDefaultConfig" /> @@ -19271,6 +19288,8 @@ <file>eric7.Cooperation.Connection.html</file> <file>eric7.Cooperation.CooperationClient.html</file> <file>eric7.Cooperation.CooperationServer.html</file> + <file>eric7.CycloneDXInterface.CycloneDXConfigDialog.html</file> + <file>eric7.CycloneDXInterface.CycloneDXUtilities.html</file> <file>eric7.DataViews.CodeMetrics.html</file> <file>eric7.DataViews.CodeMetricsDialog.html</file> <file>eric7.DataViews.PyCoverageDialog.html</file> @@ -20406,6 +20425,7 @@ <file>eric7.eric7config.html</file> <file>index-eric7.CondaInterface.html</file> <file>index-eric7.Cooperation.html</file> + <file>index-eric7.CycloneDXInterface.html</file> <file>index-eric7.DataViews.html</file> <file>index-eric7.DebugClients.Python.html</file> <file>index-eric7.DebugClients.html</file>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric7/Documentation/Source/eric7.CycloneDXInterface.CycloneDXConfigDialog.html Fri Jun 03 19:54:57 2022 +0200 @@ -0,0 +1,131 @@ +<!DOCTYPE html> +<html><head> +<title>eric7.CycloneDXInterface.CycloneDXConfigDialog</title> +<meta charset="UTF-8"> +<link rel="stylesheet" href="styles.css"> +</head> +<body> +<a NAME="top" ID="top"></a> +<h1>eric7.CycloneDXInterface.CycloneDXConfigDialog</h1> + +<p> +Module implementing a dialog to configure the CycloneDX SBOM generation. +</p> +<h3>Global Attributes</h3> + +<table> +<tr><td>None</td></tr> +</table> +<h3>Classes</h3> + +<table> + +<tr> +<td><a href="#CycloneDXConfigDialog">CycloneDXConfigDialog</a></td> +<td>Class implementing a dialog to configure the CycloneDX SBOM generation.</td> +</tr> +</table> +<h3>Functions</h3> + +<table> +<tr><td>None</td></tr> +</table> +<hr /> +<hr /> +<a NAME="CycloneDXConfigDialog" ID="CycloneDXConfigDialog"></a> +<h2>CycloneDXConfigDialog</h2> + +<p> + Class implementing a dialog to configure the CycloneDX SBOM generation. +</p> +<h3>Derived from</h3> +QDialog, Ui_CycloneDXConfigDialog +<h3>Class Attributes</h3> + +<table> +<tr><td>DefaultFileFormat</td></tr><tr><td>DefaultFileNames</td></tr><tr><td>Sources</td></tr><tr><td>SupportedSchemas</td></tr> +</table> +<h3>Class Methods</h3> + +<table> +<tr><td>None</td></tr> +</table> +<h3>Methods</h3> + +<table> + +<tr> +<td><a href="#CycloneDXConfigDialog.__init__">CycloneDXConfigDialog</a></td> +<td>Constructor</td> +</tr> +<tr> +<td><a href="#CycloneDXConfigDialog.getData">getData</a></td> +<td>Public method to get the SBOM configuration data.</td> +</tr> +<tr> +<td><a href="#CycloneDXConfigDialog.on_fileFormatComboBox_currentTextChanged">on_fileFormatComboBox_currentTextChanged</a></td> +<td>Private slot to handle the selection of a SBOM file format.</td> +</tr> +</table> +<h3>Static Methods</h3> + +<table> +<tr><td>None</td></tr> +</table> + +<a NAME="CycloneDXConfigDialog.__init__" ID="CycloneDXConfigDialog.__init__"></a> +<h4>CycloneDXConfigDialog (Constructor)</h4> +<b>CycloneDXConfigDialog</b>(<i>environment, parent=None</i>) + +<p> + Constructor +</p> +<dl> + +<dt><i>environment</i> (str)</dt> +<dd> +name of the virtual environment +</dd> +<dt><i>parent</i> (QWidget (optional))</dt> +<dd> +reference to the parent widget (defaults to None) +</dd> +</dl> +<a NAME="CycloneDXConfigDialog.getData" ID="CycloneDXConfigDialog.getData"></a> +<h4>CycloneDXConfigDialog.getData</h4> +<b>getData</b>(<i></i>) + +<p> + Public method to get the SBOM configuration data. +</p> +<dl> +<dt>Return:</dt> +<dd> +tuple containing the input source, the input file name, the + file format, the schema version and the path of the SBOM file to + be written +</dd> +</dl> +<dl> +<dt>Return Type:</dt> +<dd> +tuple of (str, str, str, str, str) +</dd> +</dl> +<a NAME="CycloneDXConfigDialog.on_fileFormatComboBox_currentTextChanged" ID="CycloneDXConfigDialog.on_fileFormatComboBox_currentTextChanged"></a> +<h4>CycloneDXConfigDialog.on_fileFormatComboBox_currentTextChanged</h4> +<b>on_fileFormatComboBox_currentTextChanged</b>(<i>fileFormat</i>) + +<p> + Private slot to handle the selection of a SBOM file format. +</p> +<dl> + +<dt><i>fileFormat</i> (str)</dt> +<dd> +selected format +</dd> +</dl> +<div align="right"><a href="#top">Up</a></div> +<hr /> +</body></html> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric7/Documentation/Source/eric7.CycloneDXInterface.CycloneDXUtilities.html Fri Jun 03 19:54:57 2022 +0200 @@ -0,0 +1,112 @@ +<!DOCTYPE html> +<html><head> +<title>eric7.CycloneDXInterface.CycloneDXUtilities</title> +<meta charset="UTF-8"> +<link rel="stylesheet" href="styles.css"> +</head> +<body> +<a NAME="top" ID="top"></a> +<h1>eric7.CycloneDXInterface.CycloneDXUtilities</h1> + +<p> +Module implementing the interface to CycloneDX. +</p> +<h3>Global Attributes</h3> + +<table> +<tr><td>None</td></tr> +</table> +<h3>Classes</h3> + +<table> + +<tr> +<td><a href="#CycloneDXEnvironmentParser">CycloneDXEnvironmentParser</a></td> +<td>Class implementing a parser to get package data for a named environment.</td> +</tr> +</table> +<h3>Functions</h3> + +<table> + +<tr> +<td><a href="#createCycloneDXFile">createCycloneDXFile</a></td> +<td>Function to create a CyccloneDX SBOM file.</td> +</tr> +</table> +<hr /> +<hr /> +<a NAME="CycloneDXEnvironmentParser" ID="CycloneDXEnvironmentParser"></a> +<h2>CycloneDXEnvironmentParser</h2> + +<p> + Class implementing a parser to get package data for a named environment. +</p> +<h3>Derived from</h3> +BaseParser +<h3>Class Attributes</h3> + +<table> +<tr><td>None</td></tr> +</table> +<h3>Class Methods</h3> + +<table> +<tr><td>None</td></tr> +</table> +<h3>Methods</h3> + +<table> + +<tr> +<td><a href="#CycloneDXEnvironmentParser.__init__">CycloneDXEnvironmentParser</a></td> +<td>Constructor</td> +</tr> +</table> +<h3>Static Methods</h3> + +<table> +<tr><td>None</td></tr> +</table> + +<a NAME="CycloneDXEnvironmentParser.__init__" ID="CycloneDXEnvironmentParser.__init__"></a> +<h4>CycloneDXEnvironmentParser (Constructor)</h4> +<b>CycloneDXEnvironmentParser</b>(<i>venvName</i>) + +<p> + Constructor +</p> +<dl> + +<dt><i>venvName</i> (str)</dt> +<dd> +name of the virtual environment +</dd> +</dl> +<div align="right"><a href="#top">Up</a></div> +<hr /> +<hr /> +<a NAME="createCycloneDXFile" ID="createCycloneDXFile"></a> +<h2>createCycloneDXFile</h2> +<b>createCycloneDXFile</b>(<i>venvName</i>) + +<p> + Function to create a CyccloneDX SBOM file. +</p> +<dl> + +<dt><i>venvName</i> (str)</dt> +<dd> +name of the virtual environment +</dd> +</dl> +<dl> + +<dt>Raises <b>RuntimeError</b>:</dt> +<dd> +raised to indicate illegal creation parameters +</dd> +</dl> +<div align="right"><a href="#top">Up</a></div> +<hr /> +</body></html> \ No newline at end of file
--- a/eric7/Documentation/Source/eric7.PipInterface.PipLicensesDialog.html Fri Jun 03 12:09:02 2022 +0200 +++ b/eric7/Documentation/Source/eric7.PipInterface.PipLicensesDialog.html Fri Jun 03 19:54:57 2022 +0200 @@ -111,14 +111,14 @@ </dl> <a NAME="PipLicensesDialog.__filterPackagesByLicense" ID="PipLicensesDialog.__filterPackagesByLicense"></a> <h4>PipLicensesDialog.__filterPackagesByLicense</h4> -<b>__filterPackagesByLicense</b>(<i>license</i>) +<b>__filterPackagesByLicense</b>(<i>licenseName</i>) <p> Private slot to filter the list of packages by license. </p> <dl> -<dt><i>license</i> (str)</dt> +<dt><i>licenseName</i> (str)</dt> <dd> license name </dd>
--- a/eric7/Documentation/Source/eric7.PipInterface.PipPackagesWidget.html Fri Jun 03 12:09:02 2022 +0200 +++ b/eric7/Documentation/Source/eric7.PipInterface.PipPackagesWidget.html Fri Jun 03 19:54:57 2022 +0200 @@ -83,6 +83,10 @@ <td>Private slot to clear the vulnerability info.</td> </tr> <tr> +<td><a href="#PipPackagesWidget.__createSBOMFile">__createSBOMFile</a></td> +<td>Private slot to create a "Software Bill Of Material" file.</td> +</tr> +<tr> <td><a href="#PipPackagesWidget.__editConfiguration">__editConfiguration</a></td> <td>Private method to edit a configuration.</td> </tr> @@ -494,6 +498,13 @@ <p> Private slot to clear the vulnerability info. </p> +<a NAME="PipPackagesWidget.__createSBOMFile" ID="PipPackagesWidget.__createSBOMFile"></a> +<h4>PipPackagesWidget.__createSBOMFile</h4> +<b>__createSBOMFile</b>(<i></i>) + +<p> + Private slot to create a "Software Bill Of Material" file. +</p> <a NAME="PipPackagesWidget.__editConfiguration" ID="PipPackagesWidget.__editConfiguration"></a> <h4>PipPackagesWidget.__editConfiguration</h4> <b>__editConfiguration</b>(<i>venvName=""</i>)
--- a/eric7/Documentation/Source/eric7.Project.Project.html Fri Jun 03 12:09:02 2022 +0200 +++ b/eric7/Documentation/Source/eric7.Project.Project.html Fri Jun 03 19:54:57 2022 +0200 @@ -285,6 +285,10 @@ <td>Private method to close all project related windows.</td> </tr> <tr> +<td><a href="#Project.__createSBOMFile">__createSBOMFile</a></td> +<td>Private slot to create a SBOM file of the project dependencies.</td> +</tr> +<tr> <td><a href="#Project.__createSnapshotSource">__createSnapshotSource</a></td> <td>Private method to create a snapshot plugin version.</td> </tr> @@ -433,6 +437,10 @@ <td>Private slot called before the make menu is shown.</td> </tr> <tr> +<td><a href="#Project.__showContextMenuOthers">__showContextMenuOthers</a></td> +<td>Private slot called before the 'Other Tools' menu is shown.</td> +</tr> +<tr> <td><a href="#Project.__showContextMenuPackagers">__showContextMenuPackagers</a></td> <td>Private slot called before the packagers menu is shown.</td> </tr> @@ -1241,6 +1249,13 @@ <p> Private method to close all project related windows. </p> +<a NAME="Project.__createSBOMFile" ID="Project.__createSBOMFile"></a> +<h4>Project.__createSBOMFile</h4> +<b>__createSBOMFile</b>(<i></i>) + +<p> + Private slot to create a SBOM file of the project dependencies. +</p> <a NAME="Project.__createSnapshotSource" ID="Project.__createSnapshotSource"></a> <h4>Project.__createSnapshotSource</h4> <b>__createSnapshotSource</b>(<i>filename</i>) @@ -1685,6 +1700,13 @@ <p> Private slot called before the make menu is shown. </p> +<a NAME="Project.__showContextMenuOthers" ID="Project.__showContextMenuOthers"></a> +<h4>Project.__showContextMenuOthers</h4> +<b>__showContextMenuOthers</b>(<i></i>) + +<p> + Private slot called before the 'Other Tools' menu is shown. +</p> <a NAME="Project.__showContextMenuPackagers" ID="Project.__showContextMenuPackagers"></a> <h4>Project.__showContextMenuPackagers</h4> <b>__showContextMenuPackagers</b>(<i></i>)
--- a/eric7/Documentation/Source/eric7.Utilities.__init__.html Fri Jun 03 12:09:02 2022 +0200 +++ b/eric7/Documentation/Source/eric7.Utilities.__init__.html Fri Jun 03 19:54:57 2022 +0200 @@ -1661,7 +1661,8 @@ </p> <p> The file names for the test file is built by prepending the string - "test" and "test_" to the file name passed into this function. + "test" and "test_" to the file name passed into this function and + by appending the string "_test". </p> <dl>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric7/Documentation/Source/index-eric7.CycloneDXInterface.html Fri Jun 03 19:54:57 2022 +0200 @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<html><head> +<title>eric7.CycloneDXInterface</title> +<meta charset="UTF-8"> +<link rel="stylesheet" href="styles.css"> +</head> +<body> +<h1>eric7.CycloneDXInterface</h1> + +<p> +Package implementing the interface to the CycloneDX tool for the generation +of Software Bill of Materials files. +</p> + + +<h3>Modules</h3> +<table> + +<tr> +<td><a href="eric7.CycloneDXInterface.CycloneDXConfigDialog.html">CycloneDXConfigDialog</a></td> +<td>Module implementing a dialog to configure the CycloneDX SBOM generation.</td> +</tr> +<tr> +<td><a href="eric7.CycloneDXInterface.CycloneDXUtilities.html">CycloneDXUtilities</a></td> +<td>Module implementing the interface to CycloneDX.</td> +</tr> +</table> +</body></html> \ No newline at end of file
--- a/eric7/Documentation/Source/index-eric7.html Fri Jun 03 12:09:02 2022 +0200 +++ b/eric7/Documentation/Source/index-eric7.html Fri Jun 03 19:54:57 2022 +0200 @@ -27,6 +27,10 @@ <td>Package containing the modules dealing with cooperation.</td> </tr> <tr> +<td><a href="index-eric7.CycloneDXInterface.html">CycloneDXInterface</a></td> +<td>Package implementing the interface to the CycloneDX tool for the generation of Software Bill of Materials files.</td> +</tr> +<tr> <td><a href="index-eric7.DataViews.html">DataViews</a></td> <td>Package containing modules for viewing various data.</td> </tr>
--- a/eric7/PipInterface/PipPackagesWidget.py Fri Jun 03 12:09:02 2022 +0200 +++ b/eric7/PipInterface/PipPackagesWidget.py Fri Jun 03 19:54:57 2022 +0200 @@ -1200,6 +1200,10 @@ self.tr("Update Vulnerability Database"), self.__updateVulnerabilityDbCache) self.__pipMenu.addSeparator() + self.__cyclonedxAct = self.__pipMenu.addAction( + self.tr("Create SBOM file"), + self.__createSBOMFile) + self.__pipMenu.addSeparator() self.__cacheInfoAct = self.__pipMenu.addAction( self.tr("Show Cache Info..."), self.__showCacheInfo) @@ -1260,6 +1264,8 @@ self.__checkVulnerabilityAct.setEnabled( enable & self.vulnerabilityCheckBox.isEnabled()) + self.__cyclonedxAct.setEnabled(enable) + self.__showLicensesDialogAct.setEnabled(enable) @pyqtSlot() @@ -1850,3 +1856,19 @@ parent=self ) dlg.exec() + + ################################################################## + ## Interface to create a SBOM file using CycloneDX + ################################################################## + + @pyqtSlot() + def __createSBOMFile(self): + """ + Private slot to create a "Software Bill Of Material" file. + """ + import CycloneDXInterface + + venvName = self.environmentsComboBox.currentText() + if venvName == self.__pip.getProjectEnvironmentString(): + venvName = "<project>" + CycloneDXInterface.createCycloneDXFile(venvName)
--- a/eric7/Project/Project.py Fri Jun 03 12:09:02 2022 +0200 +++ b/eric7/Project/Project.py Fri Jun 03 19:54:57 2022 +0200 @@ -2341,6 +2341,7 @@ self.pdata["MAKEPARAMS"]["MakeEnabled"]) self.menuMakeAct.setEnabled( self.pdata["MAKEPARAMS"]["MakeEnabled"]) + self.menuOtherToolsAct.setEnabled(True) self.projectAboutToBeCreated.emit() @@ -3033,6 +3034,7 @@ self.pdata["MAKEPARAMS"]["MakeEnabled"]) self.menuMakeAct.setEnabled( self.pdata["MAKEPARAMS"]["MakeEnabled"]) + self.menuOtherToolsAct.setEnabled(True) # open a project debugger properties file being quiet # about errors @@ -3289,6 +3291,7 @@ self.pluginGrp.setEnabled(False) self.makeGrp.setEnabled(False) self.menuMakeAct.setEnabled(False) + self.menuOtherToolsAct.setEnabled(False) self.__model.projectClosed() self.projectClosedHooks.emit() @@ -4386,6 +4389,21 @@ self.makeTestAct.triggered.connect( lambda: self.__executeMake(questionOnly=True)) self.actions.append(self.makeTestAct) + + self.createSBOMAct = EricAction( + self.tr('Create SBOM File'), + self.tr('Create &SBOM File'), 0, 0, + self, 'project_create_sbom') + self.createSBOMAct.setStatusTip( + self.tr("Create a SBOM file of the project dependencies.")) + self.createSBOMAct.setWhatsThis(self.tr( + """<b>Create SBOM File</b>""" + """<p>This allows the creation of a SBOM file of the project""" + """ dependencies. This may be based on various input sources""" + """ and will be saved as a CycloneDX SBOM file.</p>""" + )) + self.createSBOMAct.triggered.connect(self.__createSBOMFile) + self.actions.append(self.createSBOMAct) self.closeAct.setEnabled(False) self.saveAct.setEnabled(False) @@ -4424,6 +4442,7 @@ self.apidocMenu = QMenu(self.tr('Source &Documentation'), toolsMenu) self.apidocMenu.setTearOffEnabled(True) self.makeMenu = QMenu(self.tr('Make'), toolsMenu) + self.othersMenu = QMenu(self.tr('Other Tools'), toolsMenu) self.__menus = { "Main": menu, @@ -4437,6 +4456,7 @@ "Debugger": self.debuggerMenu, "Packagers": self.packagersMenu, "Make": self.makeMenu, + "OtherTools": self.othersMenu, } # connect the aboutToShow signals @@ -4451,6 +4471,7 @@ self.sessionMenu.aboutToShow.connect(self.__showContextMenuSession) self.debuggerMenu.aboutToShow.connect(self.__showContextMenuDebugger) self.makeMenu.aboutToShow.connect(self.__showContextMenuMake) + self.othersMenu.aboutToShow.connect(self.__showContextMenuOthers) menu.aboutToShow.connect(self.__showMenu) # build the show menu @@ -4483,6 +4504,10 @@ self.makeMenu.addActions(self.makeGrp.actions()) self.makeMenu.addSeparator() + # build the 'Other Tools' menu + self.othersMenu.setTearOffEnabled(True) + self.othersMenu.addAction(self.createSBOMAct) + # build the project main menu menu.setTearOffEnabled(True) menu.addActions(self.actGrp1.actions()) @@ -4519,6 +4544,8 @@ self.menuApidocAct = toolsMenu.addMenu(self.apidocMenu) toolsMenu.addSeparator() self.menuPackagersAct = toolsMenu.addMenu(self.packagersMenu) + toolsMenu.addSeparator() + self.menuOtherToolsAct = toolsMenu.addMenu(self.othersMenu) self.menuCheckAct.setEnabled(False) self.menuShowAct.setEnabled(False) @@ -4528,6 +4555,7 @@ self.menuApidocAct.setEnabled(False) self.menuPackagersAct.setEnabled(False) self.menuMakeAct.setEnabled(False) + self.menuOtherToolsAct.setEnabled(False) self.__menu = menu self.__toolsMenu = toolsMenu @@ -5914,6 +5942,25 @@ @rtype str """ return self.pdata["DOCSTRING"] + + ######################################################################### + ## Below are methods implementing the 'SBOM' support + ######################################################################### + + def __showContextMenuOthers(self): + """ + Private slot called before the 'Other Tools' menu is shown. + """ + self.showMenu.emit("OtherTools", self.othersMenu) + + @pyqtSlot() + def __createSBOMFile(self): + """ + Private slot to create a SBOM file of the project dependencies. + """ + import CycloneDXInterface + + CycloneDXInterface.createCycloneDXFile("<project>") # # eflag: noqa = M601
--- a/scripts/install.py Fri Jun 03 12:09:02 2022 +0200 +++ b/scripts/install.py Fri Jun 03 19:54:57 2022 +0200 @@ -1621,6 +1621,8 @@ "jedi": ("jedi", ""), "packaging": ("packaging", ""), "pipdeptree": ("pipdeptree", ""), + "cyclonedx-python-lib": ("cyclonedx", ""), + "cyclonedx-bom": ("cyclonedx_py", ""), } if not ignorePyqt6Tools: optionalModulesList["qt6-applications"] = ("qt6_applications", "")