Skip to content

Commit 3300636

Browse files
committed
Add logging
1 parent 81a75b4 commit 3300636

5 files changed

Lines changed: 157 additions & 25 deletions

File tree

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ jobs:
153153
AZURE_SIGNTOOL_KEY_VAULT_URL: ${{ secrets.AZURE_SIGNTOOL_KEY_VAULT_URL }}
154154
CONSTRUCTOR_EXAMPLES_KEEP_ARTIFACTS: "${{ runner.temp }}/examples_artifacts"
155155
CONSTRUCTOR_SIGNTOOL_PATH: "C:/Program Files (x86)/Windows Kits/10/bin/10.0.26100.0/x86/signtool.exe"
156-
CONSTRUCTOR_VERBOSE: 1
156+
CONSTRUCTOR_VERBOSE: 0
157157
run: |
158158
rm -rf coverage.json
159159
pytest -vv --cov=constructor --cov-branch tests/test_examples.py

constructor/briefcase.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import functools
66
import logging
7+
import os
78
import re
89
import shutil
910
import sys
@@ -249,6 +250,10 @@ class Payload:
249250
archive_name: str = "payload.tar.gz"
250251
conda_exe_name: str = "_conda.exe"
251252

253+
# There might be other ways we want to enable `add_debug_logging`, but it has proven
254+
# very useful at least for the CI environment.
255+
add_debug_logging: bool = bool(os.environ.get("CI")) and os.environ.get("CI") != "0"
256+
252257
@functools.cached_property
253258
def root(self) -> Path:
254259
"""Create root upon first access and cache it."""
@@ -332,6 +337,7 @@ def render_templates(self) -> list[str: TemplateFile]:
332337
context: dict[str, str] = {
333338
"archive_name": self.archive_name,
334339
"conda_exe_name": self.conda_exe_name,
340+
"add_debug": self.add_debug_logging,
335341
}
336342

337343
# Render the templates now using jinja and the defined context
Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,46 @@
1-
set "INSTDIR=%cd%"
1+
@echo {{ 'on' if add_debug else 'off' }}
2+
setlocal
3+
4+
rem Assign INSTDIR and normalize the path
5+
set "INSTDIR=%~dp0.."
6+
for %%I in ("%INSTDIR%") do set "INSTDIR=%%~fI"
7+
28
set "BASE_PATH=%INSTDIR%\base"
39
set "PREFIX=%BASE_PATH%"
410
set "CONDA_EXE=%INSTDIR%\{{ conda_exe_name }}"
511
set "PAYLOAD_TAR=%INSTDIR%\{{ archive_name }}"
612

13+
{%- if add_debug %}
14+
rem Get the name of the install directory
15+
for %%I in ("%INSTDIR%") do set "APPNAME=%%~nxI"
16+
set "LOG=%TEMP%\%APPNAME%-preuninstall.log"
17+
18+
echo ==== pre_uninstall start ==== >> "%LOG%"
19+
echo SCRIPT=%~f0 >> "%LOG%"
20+
echo CWD=%CD% >> "%LOG%"
21+
echo INSTDIR=%INSTDIR% >> "%LOG%"
22+
echo BASE_PATH=%BASE_PATH% >> "%LOG%"
23+
echo CONDA_EXE=%CONDA_EXE% >> "%LOG%"
24+
echo PAYLOAD_TAR=%PAYLOAD_TAR% >> "%LOG%"
25+
"%CONDA_EXE%" --version >> "%LOG%" 2>&1
26+
{%- endif %}
27+
28+
{%- set redir = ' >> "%LOG%" 2>&1' if add_debug else '' %}
29+
{%- set dump_and_exit = 'type "%LOG%" & exit /b %errorlevel%' if add_debug else 'exit /b %errorlevel%' %}
30+
31+
rem Sanity checks
32+
if not exist "%CONDA_EXE%" (
33+
{% if add_debug %}echo [ERROR] CONDA_EXE not found: "%CONDA_EXE%" >> "%LOG%" & type "%LOG%" & {% endif %}exit /b 10
34+
)
35+
736
rem Recreate an empty payload tar. This file was deleted during installation but the
837
rem MSI installer expects it to exist.
938
type nul > "%PAYLOAD_TAR%"
10-
11-
"%CONDA_EXE%" menuinst --prefix "%BASE_PATH%" --remove
12-
if errorlevel 1 (
13-
echo [ERROR] %CONDA_EXE% failed with exit code %errorlevel%.
14-
exit /b %errorlevel%
15-
)
16-
"%CONDA_EXE%" constructor uninstall --prefix "%BASE_PATH%"
1739
if errorlevel 1 (
18-
echo [ERROR] %CONDA_EXE% failed with exit code %errorlevel%.
19-
exit /b %errorlevel%
40+
{% if add_debug %}echo [ERROR] Failed to create "%PAYLOAD_TAR%" >> "%LOG%" & type "%LOG%" & {% endif %}exit /b %errorlevel%
2041
)
42+
43+
"%CONDA_EXE%" constructor uninstall --prefix "%BASE_PATH%"{{ redir }}
44+
if errorlevel 1 ( {{ dump_and_exit }} )
45+
46+
exit /b 0

constructor/briefcase/run_installation.bat

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,61 @@
1-
set "INSTDIR=%cd%"
1+
@echo {{ 'on' if add_debug else 'off' }}
2+
setlocal
3+
4+
rem Assign INSTDIR and normalize the path
5+
set "INSTDIR=%~dp0.."
6+
for %%I in ("%INSTDIR%") do set "INSTDIR=%%~fI"
7+
28
set "BASE_PATH=%INSTDIR%\base"
39
set "PREFIX=%BASE_PATH%"
410
set "CONDA_EXE=%INSTDIR%\{{ conda_exe_name }}"
511
set "PAYLOAD_TAR=%INSTDIR%\{{ archive_name }}"
612

7-
echo Unpacking payload...
8-
"%CONDA_EXE%" constructor extract --prefix "%INSTDIR%" --tar-from-stdin < "%PAYLOAD_TAR%"
9-
"%CONDA_EXE%" constructor --prefix "%BASE_PATH%" --extract-conda-pkgs
10-
13+
set CONDA_EXTRA_SAFETY_CHECKS=no
1114
set CONDA_PROTECT_FROZEN_ENVS=0
12-
set "CONDA_ROOT_PREFIX=%BASE_PATH%"
1315
set CONDA_SAFETY_CHECKS=disabled
14-
set CONDA_EXTRA_SAFETY_CHECKS=no
16+
set "CONDA_ROOT_PREFIX=%BASE_PATH%"
1517
set "CONDA_PKGS_DIRS=%BASE_PATH%\pkgs"
1618

17-
"%CONDA_EXE%" install --offline --file "%BASE_PATH%\conda-meta\initial-state.explicit.txt" -yp "%BASE_PATH%"
19+
{%- if add_debug %}
20+
rem Get the name of the install directory
21+
for %%I in ("%INSTDIR%") do set "APPNAME=%%~nxI"
22+
set "LOG=%TEMP%\%APPNAME%-postinstall.log"
23+
24+
echo ==== run_installation start ==== >> "%LOG%"
25+
echo SCRIPT=%~f0 >> "%LOG%"
26+
echo CWD=%CD% >> "%LOG%"
27+
echo INSTDIR=%INSTDIR% >> "%LOG%"
28+
echo BASE_PATH=%BASE_PATH% >> "%LOG%"
29+
echo CONDA_EXE=%CONDA_EXE% >> "%LOG%"
30+
echo PAYLOAD_TAR=%PAYLOAD_TAR% >> "%LOG%"
31+
{%- endif %}
32+
33+
{%- set redir = ' >> "%LOG%" 2>&1' if add_debug else '' %}
34+
{%- set dump_and_exit = 'type "%LOG%" & exit /b %errorlevel%' if add_debug else 'exit /b %errorlevel%' %}
35+
36+
rem Sanity checks
37+
if not exist "%CONDA_EXE%" (
38+
{% if add_debug %}echo [ERROR] CONDA_EXE not found: "%CONDA_EXE%" >> "%LOG%" & type "%LOG%" & {% endif %}exit /b 10
39+
)
40+
if not exist "%PAYLOAD_TAR%" (
41+
{% if add_debug %}echo [ERROR] PAYLOAD_TAR not found: "%PAYLOAD_TAR%" >> "%LOG%" & type "%LOG%" & {% endif %}exit /b 11
42+
)
43+
44+
echo Unpacking payload...
45+
rem "%CONDA_EXE%" constructor extract --prefix "%INSTDIR%" --tar-from-stdin < "%PAYLOAD_TAR%"{{ redir }}
46+
"%CONDA_EXE%" constructor --prefix "%INSTDIR%" --extract-tarball < "%PAYLOAD_TAR%"{{ redir }}
47+
if errorlevel 1 ( {{ dump_and_exit }} )
48+
49+
rem "%CONDA_EXE%" constructor --prefix "%BASE_PATH%" --extract-conda-pkgs{{ redir }}
50+
"%CONDA_EXE%" constructor --prefix "%BASE_PATH%" --extract-conda-pkgs{{ redir }}
51+
if errorlevel 1 ( {{ dump_and_exit }} )
52+
53+
if not exist "%BASE_PATH%" (
54+
{% if add_debug %}echo [ERROR] "%BASE_PATH%" not found! >> "%LOG%" & type "%LOG%" & {% endif %}exit /b 12
55+
)
56+
57+
"%CONDA_EXE%" install --offline --file "%BASE_PATH%\conda-meta\initial-state.explicit.txt" -yp "%BASE_PATH%"{{ redir }}
58+
if errorlevel 1 ( {{ dump_and_exit }} )
1859

1960
rem Delete the payload to save disk space.
2061
rem A truncated placeholder of 0 bytes is recreated during uninstall

tests/test_examples.py

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -339,10 +339,11 @@ def _sentinel_file_checks(example_path, install_dir):
339339

340340

341341
def is_admin() -> bool:
342-
try:
343-
return ctypes.windll.shell32.IsUserAnAdmin()
344-
except Exception:
345-
return False
342+
#try:
343+
# return ctypes.windll.shell32.IsUserAnAdmin()
344+
#except Exception:
345+
# return False
346+
return False
346347

347348

348349
def calculate_msi_install_path(installer: Path) -> Path:
@@ -355,6 +356,7 @@ def calculate_msi_install_path(installer: Path) -> Path:
355356
else:
356357
local_dir = os.environ.get("LOCALAPPDATA", str(Path.home() / r"AppData\Local"))
357358
root_dir = Path(local_dir) / "Programs"
359+
root_dir.mkdir(parents=True, exist_ok=True)
358360

359361
assert root_dir.is_dir() # Sanity check to avoid strange unexpected errors
360362
return Path(root_dir) / dir_name
@@ -388,8 +390,13 @@ def _run_installer_msi(
388390
"/qn",
389391
]
390392

391-
log_path = Path(os.environ.get("TEMP")) / (install_dir.name + ".log")
393+
log_path = Path(os.environ.get("TEMP")) / (install_dir.name + "-install.log")
392394
cmd.extend(["/L*V", str(log_path)])
395+
396+
post_install_log = Path(os.environ.get("TEMP")) / (install_dir.name + "-postinstall.log")
397+
if post_install_log.exists():
398+
os.remove(post_install_log)
399+
393400
try:
394401
process = _execute(cmd, installer_input=installer_input, timeout=timeout, check=check)
395402
except subprocess.CalledProcessError as e:
@@ -401,9 +408,23 @@ def _run_installer_msi(
401408
log_path.read_text(encoding="utf-16", errors="replace")[-15000:]
402409
) # last 15k chars
403410
print(f"\n=== MSI LOG {log_path} END ===")
411+
if post_install_log.exists():
412+
print(f"\n=== MSI POST INSTALL LOG {post_install_log} START ===")
413+
print(post_install_log.read_text(encoding="utf-8", errors="replace"))
414+
print(f"\n=== MSI POST INSTALL LOG {log_path} END ===")
415+
else:
416+
print(f"\n(post-install log not found at {post_install_log})\n")
404417
raise e
405418
if check:
406419
print("A check for MSI Installers not yet implemented")
420+
421+
# Sanity check the installation directory
422+
expected_items = [install_dir / "base", install_dir / "base" / "conda-meta", install_dir / "_conda.exe"]
423+
missing_items = [item for item in expected_items if not item.exists()]
424+
if missing_items:
425+
missing_items_string = "\n".join(missing_items)
426+
raise Exception(f"Sanity check failed, unable to find expected paths: \n{missing_items_string}")
427+
407428
return process
408429

409430

@@ -419,7 +440,45 @@ def _run_uninstaller_msi(
419440
str(installer),
420441
"/qn",
421442
]
422-
process = _execute(cmd, timeout=timeout, check=check)
443+
444+
# Temporary debug
445+
print("base exists:", (install_dir / "base").exists())
446+
print("conda-meta exists:", (install_dir / "base" / "conda-meta").exists())
447+
print("conda-meta history exists:", (install_dir / "base" / "conda-meta" / "history").exists())
448+
449+
print(f"\n=== Top-level contents of {install_dir} ===")
450+
for p in sorted(install_dir.iterdir()):
451+
kind = "DIR " if p.is_dir() else "FILE"
452+
print(f"{kind:4} {p.name}")
453+
454+
# Add MSI verbose log file
455+
log_path = Path(os.environ.get("TEMP")) / (install_dir.name + "-uninstall.log")
456+
cmd.extend(["/L*V", str(log_path)])
457+
458+
# Add log file for pre_uninstall.bat
459+
pre_uninstall_log = Path(os.environ.get("TEMP")) / (install_dir.name + "-preuninstall.log")
460+
if pre_uninstall_log.exists():
461+
os.remove(pre_uninstall_log)
462+
try:
463+
process = _execute(cmd, installer_input = None, timeout=timeout, check=check)
464+
except subprocess.CalledProcessError:
465+
# Dump pre-uninstall log
466+
if pre_uninstall_log.exists():
467+
print(f"\n=== PRE-UNINSTALL LOG {pre_uninstall_log} START ===")
468+
print(pre_uninstall_log.read_text(encoding="utf-8", errors="replace")[-15000:])
469+
print(f"=== PRE-UNINSTALL LOG {pre_uninstall_log} END ===\n")
470+
else:
471+
print(f"\n(pre-uninstall log not found at {pre_uninstall_log})\n")
472+
473+
# Dump MSI uninstall log (often UTF-16)
474+
if log_path.exists():
475+
print(f"\n=== MSI UNINSTALL LOG {log_path} START ===")
476+
print(log_path.read_text(encoding="utf-16", errors="replace")[-15000:]) # last 15k chars
477+
print(f"=== MSI UNINSTALL LOG {log_path} END ===\n")
478+
else:
479+
print(f"\n(msi uninstall log not found at {log_path})\n")
480+
raise
481+
423482
if check:
424483
# TODO:
425484
# Check log and if there are remaining files, similar to the exe installers

0 commit comments

Comments
 (0)