-
Notifications
You must be signed in to change notification settings - Fork 38
Cross platform packaging #587
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
JoeZiminski
wants to merge
36
commits into
main
Choose a base branch
from
cross-platform-packaging
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
36 commits
Select commit
Hold shift + click to select a range
488a520
Add built executable
sumana-2705 ab8dc39
Remove built executable from repo and add PyInstaller .spec file
sumana-2705 feaf295
Update spec file.
JoeZiminski e93b848
Packaging with terminal working on Windows.
JoeZiminski b8284f3
Remove spec from gitignore.
JoeZiminski 7caa5a2
Add macos packaging.
JoeZiminski ff61379
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 94cbea2
Tidying up on windows.
JoeZiminski f188a01
Working on windows.
JoeZiminski 8b826e0
Working on macos but no installer yet.
JoeZiminski 9ae5ded
Merge pull request #535 from sumana-2705/packaging_project
JoeZiminski ace948b
Start playing around with Linux.
JoeZiminski 9e2e168
Start playing around with Linux 2.
JoeZiminski 6685bf7
Start playing around with Linux 3.
JoeZiminski 5b6a91c
Start playing around with Linux 4.
JoeZiminski a941e13
Start playing around with Linux 5.
JoeZiminski 9c95587
Start playing around with Linux 6.
JoeZiminski 3f1ef5c
Start playing around with Linux 7.
JoeZiminski acdaada
Start playing around with Linux 8.
JoeZiminski 6d1c4d1
Working on linux.
JoeZiminski 3848da1
Working on linux2
JoeZiminski a057e63
Merge branch 'cross-platform-packaging' of github.com:neuroinformatic…
JoeZiminski 0313830
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 67af71d
More windows.
JoeZiminski 5f06120
Windows package 2.
JoeZiminski 1325762
Merge branch 'main' into cross-platform-packaging
JoeZiminski df22f0b
update ci.
JoeZiminski 99ee8c5
Remove emojis.
JoeZiminski 1fc6fc2
Remove emojis 2.
JoeZiminski 22859aa
Make rclone search more general.
JoeZiminski a5cbb78
Fixes for ci.
JoeZiminski 8936a62
Fix installer link.
JoeZiminski 32240dc
Small tidy up.
JoeZiminski 80d0f58
Remove linux.
JoeZiminski 3efc710
Tidying up and finalising on Windows.
JoeZiminski 9d75d86
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| name: windows-build | ||
|
|
||
| on: | ||
| push: | ||
| branches: [ main ] | ||
| tags: [ "v*" ] | ||
| pull_request: | ||
|
|
||
| concurrency: | ||
| group: ${{ github.workflow }}-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| jobs: | ||
| build-windows: | ||
| runs-on: windows-latest | ||
|
|
||
| defaults: | ||
| run: | ||
| shell: powershell | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - name: Set up Conda | ||
| uses: conda-incubator/setup-miniconda@v3 | ||
| with: | ||
| python-version: ${{ matrix.python-version }} | ||
| auto-update-conda: true | ||
| channels: conda-forge | ||
| activate-environment: "datashuttle-test" | ||
|
|
||
| - name: Install rclone | ||
| run: | | ||
| conda activate datashuttle-test | ||
| conda install -c conda-forge rclone | ||
|
|
||
| - name: Install dependencies | ||
| run: | | ||
| python -m pip install --upgrade pip | ||
| pip install .[dev] | ||
| pip install pyinstaller | ||
|
|
||
| # Install Inno Setup silently | ||
| - name: Install Inno Setup | ||
| run: | | ||
| choco install innosetup --yes | ||
|
|
||
| - name: Verify Inno Setup installation | ||
| run: | | ||
| Get-Command "C:\Program Files (x86)\Inno Setup 6\ISCC.exe" | ||
|
|
||
| - name: Run Windows packaging script | ||
| run: | | ||
| python package/package_windows.py | ||
|
|
||
| - name: Upload installer artifact | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: windows-installer | ||
| path: package\Output\datashuttle_0.0.0.exe | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -42,4 +42,7 @@ def main() -> None: | |
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() | ||
| try: | ||
| main() | ||
| except: | ||
| input("hello") | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| # Packaging | ||
|
|
||
| Packaging datashuttle is slightly more complex than packaging a simple Python application, | ||
| because we need to vendor the terminal to run datashuttle in, because it can render strangely | ||
| depending on the platform (e.g. macOS terminal, older Windows terminals). | ||
|
|
||
| Wezterm, a cross-platform terminal that renders datashuttle well. It is vendored as part of | ||
| this distribution. | ||
|
|
||
| Packaging is a two-step process. First, we package datashuttle into an executable. | ||
| This executable contains all datashuttle dependencies and can be run from any terminal. | ||
|
|
||
| Next, a simple script ('terminal launcher') that runs the datashuttle executable in the vendored | ||
| Wezterm terminal is packaged using pyinstaller. This is the main entry point for the distribution. | ||
|
|
||
| Finally, this executable is distributed via an installer (inno setup on Windows, TODO on macos (`.dmg`) | ||
| This installs the executable in the proper place on the system, adds shortcuts etc. | ||
|
|
||
| So the call chain is: | ||
| 1) The main executable is 'terminal launcher' | ||
| 2) This opens the vendored Wezterm, and uses it to start the second, datashuttle executable. | ||
|
|
||
| ## The `package` directory | ||
|
|
||
| The path handling is a little fiddly here, and some scrips are shared between platform. | ||
| Therefore, all materials are currently stored in this folder and not refactored into their own folder. | ||
|
|
||
| The key files are: | ||
|
|
||
| - **datashuttle.spec**: The pyinstaller command file that packages datashuttle, through `datashuttle_launcher.py` script. | ||
| This outputs a datashuttle executable that contains all datashuttle dependencies, and can be run through a terminal. | ||
| - **datashuttle_launcher.py**: A wrapper script that `datashuttle.spec` packages, that launches datashuttle. | ||
| - **license.txt**: The datashuttle license | ||
| - **package_<macos_or_windows>.py**: The entry point script that coordinates the packaging of datashuttle on macOS / Windows. | ||
| - **terminal_launcher_<macos_or_windows>.py**: The entry point script for the final datashuttle distribution. | ||
| It opens Wezterm and runs the datashuttle executable in it. | ||
| - **terminal_launcher_macos.spec**: The Pyinstaller command file that packages the `terminal_launcher` script into an executable. | ||
| - **wezterm_config.lua**: The Wezterm config. Use this to change settings that affect how the vendored Wezterm appears. | ||
| - **inno_compile_script.iss**: The Inno script used to package the Windows distribution into an installer. The output | ||
| of this script is shipped. | ||
| - **packaging_utils.py**: General utils used in the packaging scripts. | ||
|
|
||
| ## Windows | ||
|
|
||
| When packaging on Windows, a number of intermediate folders will be created. Under normal circumstances | ||
| they will be deleted, but knowing their contents can be useful for debugging: | ||
|
|
||
| - **build**: Generated by pyinstaller, it contains intermediate files generating during the | ||
| packaging of `datashuttle` and `terminal_launcher`. Generally you do not need to inspect these files, | ||
| they are pyinstaller stuff. | ||
| - **dist**: Initially generated by pyinstaller, this is the complete distribution. During packaging, | ||
| this file is used for the Pyinstaller dist of `datashuttle` and `terminal_launcher`, then we | ||
| copy a few auxillary files (e.g. license, Wezterm config)). Because we are building two Pyinstaller | ||
| packages into one directory, some care had to be taken to avoid configs. The folders included are: | ||
| 1) `datashuttle`: The datashuttle distribution generated by Pyinstaller | ||
| 2) `terminal_launcher.exe` and `_internal` is the `terminal_launcher` distribution generated by Pyinstaller. | ||
| 3) `_vendored` is our vendor folder, that contains the vendored Wezterm temrinal. | ||
| 4) datashuttle license and icon | ||
| - **Output**: This is the output of Inno Setup, containing the installer. This is the final version to be shipped. | ||
|
|
||
|
|
||
|
|
||
|
|
||
| ## macOS |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| DONT FORGET TO INCLUDE THE LICENSE |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| # main.spec | ||
| # -*- mode: python ; coding: utf-8 -*- | ||
| import os | ||
| import sys | ||
| import platform | ||
| from glob import glob | ||
| from pathlib import Path | ||
| import shutil | ||
|
|
||
| # Include .tcss files | ||
| tcss_files = [ | ||
| (f, os.path.join("datashuttle", "tui", "css")) | ||
| for f in glob("../datashuttle/tui/css/*.tcss") | ||
| ] | ||
|
|
||
| # Get current conda environment prefix | ||
| env_prefix = sys.prefix | ||
|
|
||
| # Detect OS and set rclone path | ||
|
|
||
| rclone_src = shutil.which("rclone") | ||
|
|
||
| if rclone_src is None: | ||
| raise FileNotFoundError( | ||
| "rclone not found in PATH. Ensure it is installed before running PyInstaller." | ||
| ) | ||
|
|
||
| binaries = [(rclone_src, ".")] | ||
|
|
||
| a = Analysis( | ||
| ['datashuttle_launcher.py'], # terminal_launcher | ||
| pathex=[os.path.abspath('..')], | ||
| binaries=binaries, | ||
| datas=tcss_files, | ||
| hiddenimports=[ | ||
| 'datashuttle.tui_launcher', | ||
| 'datashuttle.tui.app', | ||
| 'textual.widgets._tab_pane', | ||
| 'textual.widgets._input', | ||
| 'textual.widgets._tree_control', | ||
| 'rich._unicode_data.unicode17-0-0' | ||
| ], | ||
| hookspath=['hooks'], | ||
| hooksconfig={}, | ||
| runtime_hooks=[], | ||
| excludes=[], | ||
| noarchive=False, | ||
| optimize=0, | ||
| ) | ||
|
|
||
| pyz = PYZ(a.pure) | ||
|
|
||
| exe = EXE( | ||
| pyz, | ||
| a.scripts, | ||
| [], | ||
| exclude_binaries=True, | ||
| name='datashuttle', | ||
| debug=False, | ||
| bootloader_ignore_signals=False, | ||
| strip=False, | ||
| upx=True, | ||
| console=True, | ||
| disable_windowed_traceback=False, | ||
| argv_emulation=False, | ||
| target_arch=None, | ||
| codesign_identity=None, | ||
| entitlements_file=None, | ||
| ) | ||
|
|
||
| coll = COLLECT( | ||
| exe, | ||
| a.binaries, | ||
| a.datas, | ||
| strip=False, | ||
| upx=True, | ||
| upx_exclude=[], | ||
| name='datashuttle' | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| import sys | ||
| from pathlib import Path | ||
|
|
||
| # Add project root to sys.path | ||
| project_root = Path(__file__).resolve().parent.parent | ||
| sys.path.insert(0, str(project_root)) | ||
|
|
||
| # Import main from tui_launcher | ||
| from datashuttle.tui_launcher import main as datashuttle_main | ||
|
|
||
|
|
||
| def run(): | ||
| # Simulate: datashuttle launch | ||
| sys.argv = ["datashuttle", "launch"] | ||
| datashuttle_main() | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| try: | ||
| run() | ||
| except Exception as e: | ||
| print(f"\nError: {e}") | ||
| finally: | ||
| input("\nPress Enter to exit...") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| hello world |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Check warning
Code scanning / CodeQL
Workflow does not contain permissions Medium
Copilot Autofix
AI 10 days ago
In general, to fix this class of problem you need to define a
permissionsblock that explicitly restricts theGITHUB_TOKENto the least privileges the workflow actually needs. You can add this either at the top (root) of the workflow to apply to all jobs, or under a specific job to limit only that job.For this particular workflow, the
build-windowsjob only checks out code and uploads an artifact; it doesn’t push code, modify releases, or interact with issues/PRs. These operations work withcontents: read, andactions/upload-artifactdoes not require any repository write permissions. The best minimal fix is therefore to add a root-levelpermissionsblock just after thename:(or afteron:) withcontents: read. This documents the intent and ensures the token cannot be used to write to the repo even if organization defaults are broader.Concretely:
.github/workflows/package_windows.yml.permissions:mapping near the top of the file (e.g., after line 2 or after theon:block) withcontents: read.