Skip to content

Commit 7caa5a2

Browse files
committed
Add macos packaging.
1 parent b8284f3 commit 7caa5a2

11 files changed

Lines changed: 276 additions & 134 deletions

datashuttle/datashuttle.spec

Lines changed: 0 additions & 57 deletions
This file was deleted.

datashuttle/utils/rclone.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,26 @@
55
from pathlib import Path
66
from subprocess import CompletedProcess
77
from typing import Dict, List, Literal
8+
import sys
9+
import os
810

911
from datashuttle.configs.config_class import Configs
1012
from datashuttle.utils import utils
1113
from datashuttle.utils.custom_types import TopLevelFolder
1214

1315

16+
def get_rclone_name() -> str:
17+
"""
18+
"""
19+
if getattr(sys, 'frozen', False):
20+
# PyInstaller: binary extracted to _MEIPASS
21+
rclone_bin = os.path.join(sys._MEIPASS, 'rclone.exe' if sys.platform.startswith('win') else 'rclone')
22+
else:
23+
# Normal Python execution: use PATH or fixed path
24+
rclone_bin = 'rclone' # or provide full path if needed
25+
26+
return rclone_bin
27+
1428
def call_rclone(command: str, pipe_std: bool = False) -> CompletedProcess:
1529
"""Call rclone with the specified command.
1630
@@ -27,7 +41,8 @@ def call_rclone(command: str, pipe_std: bool = False) -> CompletedProcess:
2741
subprocess.CompletedProcess with `stdout` and `stderr` attributes.
2842
2943
"""
30-
command = "rclone " + command
44+
command = f"{get_rclone_name()} {command}"
45+
3146
if pipe_std:
3247
output = subprocess.run(
3348
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True
@@ -56,7 +71,7 @@ def call_rclone_through_script(command: str) -> CompletedProcess:
5671
"""
5772
system = platform.system()
5873

59-
command = "rclone " + command
74+
command = f"{get_rclone_name()} {command}"
6075

6176
if system == "Windows":
6277
suffix = ".bat"

package/datashuttle.spec

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import os
44
import sys
55
import platform
66
from glob import glob
7+
from pathlib import Path
78

89
# Include .tcss files
910
tcss_files = [
@@ -28,7 +29,7 @@ if not os.path.isfile(rclone_src):
2829
binaries = [(rclone_src, '.')]
2930

3031
a = Analysis(
31-
['datashuttle_launcher.py'],
32+
['datashuttle_launcher.py'], # terminal_launcher
3233
pathex=[os.path.abspath('..')],
3334
binaries=binaries,
3435
datas=tcss_files,
@@ -52,14 +53,27 @@ pyz = PYZ(a.pure)
5253
exe = EXE(
5354
pyz,
5455
a.scripts,
55-
a.binaries,
56-
a.zipfiles,
57-
a.datas,
56+
[],
57+
exclude_binaries=True,
5858
name='datashuttle',
5959
debug=False,
6060
bootloader_ignore_signals=False,
6161
strip=False,
6262
upx=True,
6363
console=True,
64-
onefile=True, # <-- enables one-file mode
64+
disable_windowed_traceback=False,
65+
argv_emulation=False,
66+
target_arch=None,
67+
codesign_identity=None,
68+
entitlements_file=None,
6569
)
70+
71+
coll = COLLECT(
72+
exe,
73+
a.binaries,
74+
a.datas,
75+
strip=False,
76+
upx=True,
77+
upx_exclude=[],
78+
name='datashuttle'
79+
)

package/notes.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
TODO:
2+
1) write package script for macOS and organise code and outputs properly.
3+
2) go back to windows and rework everything
4+
3) text on linux
5+
6+

package/package_macos.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import platform
2+
import shutil
3+
import subprocess
4+
import zipfile
5+
from pathlib import Path
6+
import packaging_utils
7+
import requests
8+
9+
WEZTERM_VERSION = packaging_utils.get_wezterm_version()
10+
WEZTERM_FOLDERNAME = f"WezTerm-macos-{WEZTERM_VERSION}"
11+
WEZTERM_URL = f"https://github.com/wezterm/wezterm/releases/download/{WEZTERM_VERSION}/{WEZTERM_FOLDERNAME}.zip"
12+
13+
# Paths
14+
project_root = Path(__file__).parent
15+
vendored_dir = project_root / "_vendored"
16+
17+
if not (vendored_dir / WEZTERM_FOLDERNAME).exists():
18+
packaging_utils.download_wezterm(vendored_dir, WEZTERM_FOLDERNAME)
19+
20+
if (build_path := project_root / "build").exists():
21+
shutil.rmtree(build_path)
22+
23+
if (dist_path := project_root / "dist").exists():
24+
shutil.rmtree(dist_path)
25+
26+
# Step 2: Run PyInstaller builds
27+
subprocess.run(f"pyinstaller {project_root / 'datashuttle.spec'}", shell=True)
28+
subprocess.run(f"pyinstaller {project_root / 'terminal_launcher_macos.spec'}", shell=True)
29+
30+
app_macos_path = project_root / "dist" / "Datashuttle.app" / "Contents" / "Resources"
31+
32+
shutil.copytree(vendored_dir / f"{WEZTERM_FOLDERNAME}", app_macos_path / "_vendored" / f"{WEZTERM_FOLDERNAME}")
33+
34+
shutil.copytree(project_root / "dist" / "datashuttle" / "_internal", app_macos_path.parent / "Resources" / "_internal")
35+
36+
shutil.copy(project_root / "dist" / "datashuttle" / "datashuttle", app_macos_path.parent / "Resources")
37+
38+
shutil.copy(
39+
project_root / "wezterm_config.lua", app_macos_path.parent / "Resources" / "_vendored" / WEZTERM_FOLDERNAME
40+
)
Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,37 @@
1+
import platform
12
import shutil
23
import subprocess
34
import zipfile
45
from pathlib import Path
56

67
import requests
8+
import packaging_utils
79

810
# Constants
9-
WEZTERM_VERSION = "20240203-110809-5046fc22"
11+
WEZTERM_VERSION = packaging_utils.get_wezterm_version()
1012
WEZTERM_FOLDERNAME = f"WezTerm-windows-{WEZTERM_VERSION}"
1113
WEZTERM_URL = f"https://github.com/wezterm/wezterm/releases/download/{WEZTERM_VERSION}/{WEZTERM_FOLDERNAME}.zip"
1214

1315
# Paths
1416
project_root = Path(__file__).parent
15-
base_path = Path.cwd()
1617
vendored_dir = base_path / "_vendored"
17-
wezterm_extracted_dir = vendored_dir / f"WezTerm-windows-{WEZTERM_VERSION}"
18-
wezterm_zip_path = vendored_dir / f"{WEZTERM_FOLDERNAME}.zip"
1918

20-
# Step 1: Download and extract WezTerm if missing
21-
if not wezterm_extracted_dir.exists():
22-
print("⬇ Downloading WezTerm...")
23-
vendored_dir.mkdir(parents=True, exist_ok=True)
24-
with requests.get(WEZTERM_URL, stream=True) as response:
25-
response.raise_for_status()
26-
with open(wezterm_zip_path, "wb") as f:
27-
for chunk in response.iter_content(8192):
28-
f.write(chunk)
19+
packaging_utils.download_wezterm(vendored_dir, WEZTERM_FOLDERNAME)
2920

30-
print("📦 Extracting WezTerm...")
31-
with zipfile.ZipFile(wezterm_zip_path, "r") as zipf:
32-
zipf.extractall(vendored_dir)
33-
wezterm_zip_path.unlink() # Optional: clean up ZIP
34-
print("✅ WezTerm ready.")
35-
else:
36-
print("✅ WezTerm already present. Skipping download.")
21+
if (build_path := project_root / "build").exists():
22+
shutil.rmtree(build_path)
23+
24+
if (dist_path := project_root / "dist").exists():
25+
shutil.rmtree(dist_path)
26+
27+
# Step 2: Run PyInstaller builds
28+
subprocess.run(f"pyinstaller {project_root / 'datashuttle.spec'}", shell=True)
29+
subprocess.run(f"pyinstaller {project_root / 'terminal_launcher.spec'}", shell=True)
3730

3831
shutil.copy(
3932
base_path / "wezterm_config.lua", vendored_dir / WEZTERM_FOLDERNAME
4033
)
4134

42-
# Step 2: Run PyInstaller builds
43-
subprocess.run(f"pyinstaller {project_root / 'datashuttle.spec'}")
44-
subprocess.run(f"pyinstaller {project_root / 'terminal_launcher.spec'}")
45-
4635
# Step 3: Copy WezTerm into dist/_vendored
4736
dist_dir = base_path / "dist"
4837
terminal_launcher_dist_dir = dist_dir / "terminal_launcher"
@@ -63,3 +52,4 @@
6352
shutil.copy2(item, target)
6453

6554
shutil.rmtree(terminal_launcher_dist_dir)
55+

package/packaging_utils.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import requests
2+
import zipfile
3+
import subprocess
4+
5+
def get_wezterm_version():
6+
return "20240203-110809-5046fc22"
7+
8+
def download_wezterm(vendored_dir, wezterm_foldername):
9+
"""
10+
"""
11+
wezterm_url = f"https://github.com/wezterm/wezterm/releases/download/{get_wezterm_version()}/{wezterm_foldername}.zip"
12+
13+
wezterm_extracted_dir = vendored_dir / wezterm_foldername
14+
wezterm_zip_path = vendored_dir / f"{wezterm_foldername}.zip"
15+
16+
# Step 1: Download and extract WezTerm if missing
17+
if not wezterm_extracted_dir.exists():
18+
print("⬇ Downloading WezTerm...")
19+
vendored_dir.mkdir(parents=True, exist_ok=True)
20+
with requests.get(wezterm_url, stream=True) as response:
21+
response.raise_for_status()
22+
with open(wezterm_zip_path, "wb") as f:
23+
for chunk in response.iter_content(8192):
24+
f.write(chunk)
25+
26+
print("📦 Extracting WezTerm with system unzip...")
27+
subprocess.run(["unzip", "-q", str(wezterm_zip_path), "-d", str(vendored_dir)], check=True)
28+
29+
wezterm_zip_path.unlink() # Optional: clean up ZIP
30+
print("✅ WezTerm ready.")
31+
else:
32+
print("✅ WezTerm already present. Skipping download.")

0 commit comments

Comments
 (0)