Skip to content

Commit 52d7648

Browse files
committed
Add manifest generation and update build process in DataLab
1 parent ea4d2c2 commit 52d7648

File tree

4 files changed

+101
-1
lines changed

4 files changed

+101
-1
lines changed

DataLab.spec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ datas = collect_data_files('datalab') + [('datalab\\plugins', 'datalab\\plugins'
99
datas += collect_data_files('guidata') + collect_data_files('plotpy')
1010
datas += collect_data_files('sigima')
1111
datas += copy_metadata('imageio')
12+
datas += [('manifest.json', '.')]
1213

1314
a = Analysis(
1415
['datalab\\start.pyw'],

datalab/widgets/instconfviewer.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@
66

77
from __future__ import annotations
88

9+
import json
910
import locale
1011
import os
1112
import platform
1213
import sys
1314
from importlib.metadata import distributions
15+
from pathlib import Path
1416

1517
from guidata.configtools import get_icon
1618
from guidata.qthelpers import exec_dialog
@@ -52,6 +54,45 @@ def get_installed_package_info() -> str:
5254
return os.linesep.join(result_lines)
5355

5456

57+
def get_manifest_package_info(manifest_path: Path) -> str:
58+
"""Get the list of packages from the build manifest file
59+
60+
Args:
61+
manifest_path: Path to the manifest.json file
62+
63+
Returns:
64+
Formatted string with package list and build information
65+
"""
66+
try:
67+
with open(manifest_path, "r", encoding="utf-8") as f:
68+
manifest = json.load(f)
69+
70+
packages = list(manifest["packages"].items())
71+
packages.sort(key=lambda x: x[0].lower())
72+
73+
# Determine column widths
74+
name_width = max(len(name) for name, _ in packages)
75+
version_width = max(len(version) for _, version in packages)
76+
77+
header = f"{'Package':{name_width}} {'Version':{version_width}}"
78+
separator = f"{'-' * name_width} {'-' * version_width}"
79+
result_lines = [
80+
f"Build time: {manifest['build_time']}",
81+
f"Python version: {manifest['python_version']}",
82+
f"Build system: {manifest['system']} {manifest['release']}",
83+
f"Architecture: {manifest['architecture']}",
84+
"",
85+
header,
86+
separator,
87+
]
88+
for name, version in packages:
89+
result_lines.append(f"{name:{name_width}} {version:{version_width}}")
90+
91+
return os.linesep.join(result_lines)
92+
except Exception as e:
93+
return f"Error reading manifest file: {e}"
94+
95+
5596
def get_install_info() -> str:
5697
"""Get DataLab installation informations
5798
@@ -62,7 +103,15 @@ def get_install_info() -> str:
62103
# Stand-alone version
63104
more_info = "This is the Stand-alone version of DataLab."
64105
more_info += os.linesep * 2
65-
more_info = get_installed_package_info()
106+
107+
# Try to read manifest file from executable root directory
108+
manifest_path = Path(sys.executable).parent / "manifest.json"
109+
if manifest_path.exists():
110+
more_info += get_manifest_package_info(manifest_path)
111+
else:
112+
more_info += "Manifest file not found."
113+
else:
114+
more_info = get_installed_package_info()
66115
info = os.linesep.join(
67116
[
68117
f"DataLab v{datalab.__version__}",

scripts/build_exe.bat

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ for %%s in (16 24 32 48 128 256) do (
4545
magick "%RESPATH%\tmp-*.png" "%RESPATH%\DataLab.ico"
4646
del "%RESPATH%\tmp-*.png"
4747

48+
@REM Generate build manifest
49+
%PYTHON% scripts\generate_manifest.py
50+
4851
@REM Building executable
4952
%PYTHON% -m PyInstaller DataLab.spec --noconfirm --clean
5053

scripts/generate_manifest.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
4+
5+
"""
6+
Generate build manifest for DataLab executable
7+
8+
This script generates a JSON manifest file containing information about the build
9+
environment, Python version, system information, and all installed packages with
10+
their versions. This manifest is then included in the frozen executable and displayed
11+
in the installation configuration viewer.
12+
"""
13+
14+
import json
15+
import platform
16+
from datetime import datetime
17+
from importlib.metadata import distributions
18+
19+
20+
def generate_manifest(file_path="manifest.json"):
21+
"""Generate a manifest file with build information and package list
22+
23+
Args:
24+
file_path: Path to the output manifest JSON file (default: "manifest.json")
25+
"""
26+
manifest = {
27+
"build_time": datetime.utcnow().isoformat() + "Z",
28+
"python_version": platform.python_version(),
29+
"system": platform.system(),
30+
"release": platform.release(),
31+
"architecture": platform.machine(),
32+
"packages": {dist.metadata["Name"]: dist.version for dist in distributions()},
33+
}
34+
35+
with open(file_path, "w", encoding="utf-8") as f:
36+
json.dump(manifest, f, indent=2)
37+
38+
print(f"Manifest written to {file_path}")
39+
print(f" Build time: {manifest['build_time']}")
40+
print(f" Python version: {manifest['python_version']}")
41+
print(f" System: {manifest['system']} {manifest['release']}")
42+
print(f" Architecture: {manifest['architecture']}")
43+
print(f" Total packages: {len(manifest['packages'])}")
44+
45+
46+
if __name__ == "__main__":
47+
generate_manifest()

0 commit comments

Comments
 (0)