diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4fe2e26..95a6b52 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,6 +38,10 @@ jobs: - name: Install dependencies run: uv sync --group deployment + ### CREATE LIST OF THIRD-PARTY LICENCES + - name: Create list of third-party licences + run: uv run python packaging/licenses/third_party_licenses.py + ### INSTALL DEPENDENCY WALKER FOR NUITKA ON WINDOWS - name: Download Dependency Walker for Nuitka if: runner.os == 'Windows' @@ -53,6 +57,9 @@ jobs: - name: Build app run: uv run pyside6-deploy --mode standalone --name groupselect2 app/main.py + - name: Copy resources + run: python -c "import shutil; shutil.copytree('app/resources', 'app/groupselect2.dist/resources')" + - name: Upload artifact uses: actions/upload-artifact@v4 with: @@ -87,6 +94,10 @@ jobs: Copy-Item -Recurse "app\groupselect2.dist\*" "$pkg\" Copy-Item "packaging\windows\AppxManifest.xml" "$pkg\" Copy-Item -Recurse "packaging\windows\assets" "$pkg\assets" + Copy-Item "packaging\licenses\PYTHON_LICENSE.txt" "$pkg\" + Copy-Item "THIRD_PARTY_LICENSES.txt" "$pkg\" + Copy-Item "packaging\licenses\PYSIDE6_LICENSES.txt" "$pkg\" + Copy-Item "packaging\licenses\QT_LIBRARY_REPLACEMENT.txt" "$pkg\" - name: Build MSIX with makeappx if: runner.os == 'Windows' diff --git a/app/main.py b/app/main.py index bf2b4e9..2538b04 100644 --- a/app/main.py +++ b/app/main.py @@ -1,4 +1,7 @@ #!/usr/bin/env python3 +from pathlib import Path +import sys + from base_app.AppContext import AppContext from GSProject import GSProject @@ -6,6 +9,12 @@ from GSModelManager import GSModelManager +if getattr(sys, "frozen", False): + RESOURCES_PATH: Path = Path(sys.executable).parent / "resources" +else: + RESOURCES_PATH: Path = Path(__file__).parent / "resources" + + def main(): app_ctx = AppContext( app_name='GroupSelect', @@ -14,6 +23,7 @@ def main(): main_window_cls=GSMainWindow, model_manager_cls=GSModelManager, project_cls=GSProject, + about_html_template=(RESOURCES_PATH / "about.html").read_text(encoding="utf-8") ) app_ctx.launch() diff --git a/app/resources/about.html b/app/resources/about.html new file mode 100644 index 0000000..92dc066 --- /dev/null +++ b/app/resources/about.html @@ -0,0 +1,30 @@ +

{app_name} App by Sortition Foundation

+

Version: {app_version}

+

+ {app_name} is free and open source software, licensed under the + GNU General Public License v3. + Source code is available at + github.com/sortitionfoundation/groupselect-app. +

+

This software was created by Philipp C. Verpoort while an Associate at the + Sortition Foundation. The initial funding for the development was provided by the Involve Foundation. +

+

+ This application is built with + PySide6 and Qt6, used under the + GNU Lesser General Public License v3 (LGPLv3). + Third-party components used in Qt for Python are listed at + doc.qt.io/qtforpython-6/licenses.html. + Qt library files included with this application may be replaced with + compatible modified versions; see QT_LIBRARY_REPLACEMENT.txt for details. +

+

+ This application is built with + Python 3, used under the + Python Software Foundation License v2. +

+

+ Third-party Python package licence notices are listed in + THIRD_PARTY_LICENSES.txt and PYSIDE6_LICENSES.txt, + included with this application. +

diff --git a/packaging/licenses/PYSIDE6_LICENSES.txt b/packaging/licenses/PYSIDE6_LICENSES.txt new file mode 100644 index 0000000..0a28b63 --- /dev/null +++ b/packaging/licenses/PYSIDE6_LICENSES.txt @@ -0,0 +1,178 @@ +PySide6 and Qt6 +=============== +PySide6 is the official Python binding for Qt6, used under the +GNU Lesser General Public License version 3 (LGPLv3). + +Copyright (C) The Qt Company Ltd. + +The Qt source code is available at https://code.qt.io +Third-party components used in Qt for Python are listed at +https://doc.qt.io/qtforpython-6/licenses.html + +---------------------- LGPLv3 ---------------------- + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/packaging/licenses/PYTHON_LICENSE.txt b/packaging/licenses/PYTHON_LICENSE.txt new file mode 100644 index 0000000..e7faf89 --- /dev/null +++ b/packaging/licenses/PYTHON_LICENSE.txt @@ -0,0 +1,40 @@ +1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and + the Individual or Organization ("Licensee") accessing and otherwise using this + software ("Python") in source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby + grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, + analyze, test, perform and/or display publicly, prepare derivative works, + distribute, and otherwise use Python alone or in any derivative + version, provided, however, that PSF's License Agreement and PSF's notice of + copyright, i.e., "Copyright © 2001 Python Software Foundation; All Rights + Reserved" are retained in Python alone or in any derivative version + prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on or + incorporates Python or any part thereof, and wants to make the + derivative work available to others as provided herein, then Licensee hereby + agrees to include in any such work a brief summary of the changes made to Python. + +4. PSF is making Python available to Licensee on an "AS IS" basis. + PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF + EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR + WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE + USE OF PYTHON WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON + FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF + MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, OR ANY DERIVATIVE + THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material breach of + its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any relationship + of agency, partnership, or joint venture between PSF and Licensee. This License + Agreement does not grant permission to use PSF trademarks or trade name in a + trademark sense to endorse or promote products or services of Licensee, or any + third party. + +8. By copying, installing or otherwise using Python, Licensee agrees + to be bound by the terms and conditions of this License Agreement. \ No newline at end of file diff --git a/packaging/licenses/QT_LIBRARY_REPLACEMENT.txt b/packaging/licenses/QT_LIBRARY_REPLACEMENT.txt new file mode 100644 index 0000000..158cdf4 --- /dev/null +++ b/packaging/licenses/QT_LIBRARY_REPLACEMENT.txt @@ -0,0 +1,15 @@ +Qt Library Replacement Notice +============================== + +This application uses Qt6 and PySide6, distributed as separate DLL files +in accordance with the GNU Lesser General Public License version 3 (LGPLv3). + +In compliance with the LGPLv3, you are free to replace the Qt6 and PySide6 +library files (qt6*.dll, pyside6.abi3.dll, shiboken6.abi3.dll, and files +in the PySide6/ subdirectory) with compatible modified versions of your +own, provided the modified libraries remain interface-compatible with the +originals. + +The Qt source code is available at: https://code.qt.io +Build instructions for PySide6 are available at: +https://doc.qt.io/qtforpython-6/gettingstarted/index.html diff --git a/packaging/licenses/third_party_licenses.py b/packaging/licenses/third_party_licenses.py new file mode 100644 index 0000000..94c4851 --- /dev/null +++ b/packaging/licenses/third_party_licenses.py @@ -0,0 +1,32 @@ +import re +import subprocess + + +# Get runtime packages from uv export +export = subprocess.run( + ["uv", "export", "--no-dev", "--no-hashes", "--format=requirements-txt"], + capture_output=True, text=True, check=True +) + +all_packages = [] +for line in export.stdout.splitlines(): + line = line.strip() + if not line or line.startswith("#"): + continue + name = re.split(r'[@=><\s]', line)[0] + if name: + all_packages.append(name) + +pyside_packages = [p for p in all_packages if p.lower().startswith("pyside6") or p.lower() == "shiboken6"] +other_packages = [p for p in all_packages if p not in pyside_packages] + +subprocess.run( + [ + "uv", "run", "pip-licenses", + "--with-authors", "--with-urls", "--with-license-file", "--no-license-path", + "--format=plain-vertical", + "--packages", *other_packages, + "--output-file=THIRD_PARTY_LICENSES.txt", + ], + check=True +) diff --git a/pyproject.toml b/pyproject.toml index 192a271..9447055 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,6 +26,7 @@ deployment = [ "ordered-set>=4.1.0", "patchelf>=0.17.2.4; sys_platform == 'linux'", "pip>=26.0.1", + "pip-licenses>=5.5.1", "setuptools>=81.0.0", "wheel>=0.46.3", "zstandard>=0.25.0", diff --git a/uv.lock b/uv.lock index a0a5721..8e93b83 100644 --- a/uv.lock +++ b/uv.lock @@ -5,7 +5,7 @@ requires-python = "==3.11.*" [[package]] name = "base-app" version = "0.1.0" -source = { git = "https://github.com/sortitionfoundation/base-app/?tag=v0.1.0#789b8847ea44c1fd99a4f59071b9c36faa3555ec" } +source = { git = "https://github.com/sortitionfoundation/base-app/?tag=v0.1.0#a43fc880ec8e81f19b8ffa76f9ba1e05888ffe73" } dependencies = [ { name = "jsonpickle" }, { name = "pyside6" }, @@ -54,6 +54,7 @@ deployment = [ { name = "ordered-set" }, { name = "patchelf", marker = "sys_platform == 'linux'" }, { name = "pip" }, + { name = "pip-licenses" }, { name = "setuptools" }, { name = "wheel" }, { name = "zstandard" }, @@ -73,6 +74,7 @@ deployment = [ { name = "ordered-set", specifier = ">=4.1.0" }, { name = "patchelf", marker = "sys_platform == 'linux'", specifier = ">=0.17.2.4" }, { name = "pip", specifier = ">=26.0.1" }, + { name = "pip-licenses", specifier = ">=5.5.1" }, { name = "setuptools", specifier = ">=81.0.0" }, { name = "wheel", specifier = ">=0.46.3" }, { name = "zstandard", specifier = ">=0.25.0" }, @@ -198,6 +200,30 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/de/f0/c81e05b613866b76d2d1066490adf1a3dbc4ee9d9c839961c3fc8a6997af/pip-26.0.1-py3-none-any.whl", hash = "sha256:bdb1b08f4274833d62c1aa29e20907365a2ceb950410df15fc9521bad440122b", size = 1787723, upload-time = "2026-02-05T02:20:16.416Z" }, ] +[[package]] +name = "pip-licenses" +version = "5.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "prettytable" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/44/4c/b4be9024dae3b5b3c0a6c58cc1d4a35fffe51c3adb835350cb7dcd43b5cd/pip_licenses-5.5.1.tar.gz", hash = "sha256:7df370e6e5024a3f7449abf8e4321ef868ba9a795698ad24ab6851f3e7fc65a7", size = 49108, upload-time = "2026-01-27T21:46:41.432Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/a3/0b369cdffef3746157712804f1ded9856c75aa060217ee206f742c74e753/pip_licenses-5.5.1-py3-none-any.whl", hash = "sha256:ed5e229a93760e529cfa7edaec6630b5a2cd3874c1bddb8019e5f18a723fdead", size = 22108, upload-time = "2026-01-27T21:46:39.766Z" }, +] + +[[package]] +name = "prettytable" +version = "3.17.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/45/b0847d88d6cfeb4413566738c8bbf1e1995fad3d42515327ff32cc1eb578/prettytable-3.17.0.tar.gz", hash = "sha256:59f2590776527f3c9e8cf9fe7b66dd215837cca96a9c39567414cbc632e8ddb0", size = 67892, upload-time = "2025-11-14T17:33:20.212Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ee/8c/83087ebc47ab0396ce092363001fa37c17153119ee282700c0713a195853/prettytable-3.17.0-py3-none-any.whl", hash = "sha256:aad69b294ddbe3e1f95ef8886a060ed1666a0b83018bbf56295f6f226c43d287", size = 34433, upload-time = "2025-11-14T17:33:19.093Z" }, +] + [[package]] name = "pyside6" version = "6.10.2" @@ -306,6 +332,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521, upload-time = "2025-12-13T17:45:33.889Z" }, ] +[[package]] +name = "wcwidth" +version = "0.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/35/a2/8e3becb46433538a38726c948d3399905a4c7cabd0df578ede5dc51f0ec2/wcwidth-0.6.0.tar.gz", hash = "sha256:cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159", size = 159684, upload-time = "2026-02-06T19:19:40.919Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl", hash = "sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad", size = 94189, upload-time = "2026-02-06T19:19:39.646Z" }, +] + [[package]] name = "wheel" version = "0.46.3"