diff --git a/constructor/header.sh b/constructor/header.sh index 81cf0bf2..c72bc6b6 100644 --- a/constructor/header.sh +++ b/constructor/header.sh @@ -492,9 +492,15 @@ unset PYTHON_SYSCONFIGDATA_NAME _CONDA_PYTHON_SYSCONFIGDATA_NAME # the first binary payload: the standalone conda executable printf "Unpacking bootstrapper...\n" -CONDA_EXEC="$PREFIX/_conda" +CONDA_EXEC="$PREFIX/{{ conda_exe_name }}" extract_range "${boundary0}" "${boundary1}" > "$CONDA_EXEC" chmod +x "$CONDA_EXEC" + +{%- if conda_exe_name != "_conda" %} +# In case there are packages that depend on _conda +ln -s "$CONDA_EXEC" "$PREFIX"/_conda +{%- endif %} + {%- for filename, (start, end, executable) in conda_exe_payloads|items %} mkdir -p "$(dirname "$PREFIX/{{ filename }}")" {%- if start == end %} diff --git a/constructor/osx/prepare_installation.sh b/constructor/osx/prepare_installation.sh index cf14cbf6..eb83f37e 100644 --- a/constructor/osx/prepare_installation.sh +++ b/constructor/osx/prepare_installation.sh @@ -22,7 +22,7 @@ PREFIX="$2/{{ pkg_name_lower }}" PREFIX=$(cd "$PREFIX"; pwd) export PREFIX echo "PREFIX=$PREFIX" -CONDA_EXEC="$PREFIX/_conda" +CONDA_EXEC="$PREFIX/{{ conda_exe_name }}" # Installers should ignore pre-existing configuration files. unset CONDARC unset MAMBARC @@ -30,6 +30,11 @@ unset MAMBARC chmod +x "$CONDA_EXEC" +{%- if conda_exe_name != "_conda" %} +# In case there are packages that depend on _conda +ln -s "$CONDA_EXEC" "$PREFIX"/_conda +{%- endif %} + # Create a blank history file so conda thinks this is an existing env mkdir -p "$PREFIX/conda-meta" touch "$PREFIX/conda-meta/history" diff --git a/constructor/osx/run_installation.sh b/constructor/osx/run_installation.sh index 00afff65..ff684697 100644 --- a/constructor/osx/run_installation.sh +++ b/constructor/osx/run_installation.sh @@ -26,7 +26,7 @@ PREFIX="$2/{{ pkg_name_lower }}" PREFIX=$(cd "$PREFIX"; pwd) export PREFIX echo "PREFIX=$PREFIX" -CONDA_EXEC="$PREFIX/_conda" +CONDA_EXEC="$PREFIX/{{ conda_exe_name }}" # Installers should ignore pre-existing configuration files. unset CONDARC unset MAMBARC diff --git a/constructor/osx/run_user_script.sh b/constructor/osx/run_user_script.sh index 2d00b1c7..6209a5b0 100644 --- a/constructor/osx/run_user_script.sh +++ b/constructor/osx/run_user_script.sh @@ -22,7 +22,7 @@ PREFIX="$2/{{ pkg_name_lower }}" PREFIX=$(cd "$PREFIX"; pwd) export PREFIX echo "PREFIX=$PREFIX" -CONDA_EXEC="$PREFIX/_conda" +CONDA_EXEC="$PREFIX/{{ conda_exe_name }}" # /COMMON UTILS # Expose these to user scripts as well diff --git a/constructor/osxpkg.py b/constructor/osxpkg.py index 3785ac61..86dfe84a 100644 --- a/constructor/osxpkg.py +++ b/constructor/osxpkg.py @@ -25,6 +25,7 @@ approx_size_kb, copy_conda_exe, explained_check_call, + format_conda_exe_name, get_final_channels, parse_virtual_specs, rm_rf, @@ -364,6 +365,7 @@ def move_script(src, dst, info, ensure_shebang=False, user_script_type=None): variables["no_rcs_arg"] = info.get("_ignore_condarcs_arg", "") variables["script_env_variables"] = info.get("script_env_variables", {}) variables["initialize_conda"] = info.get("initialize_conda", "classic") + variables["conda_exe_name"] = format_conda_exe_name(info["_conda_exe"]) data = render_template(data, **variables) @@ -556,7 +558,7 @@ def create(info, verbose=False): # 1. Prepare installation # The 'prepare_installation' package contains the prepopulated package cache, the modified - # conda-meta metadata staged into pkgs/conda-meta, _conda (conda-standalone), + # conda-meta metadata staged into pkgs/conda-meta, _conda (conda-standalone, [--conda-exe]), # Optionally, extra files and the user-provided scripts. # We first populate PACKAGE_ROOT with everything needed, and then run pkg build on that dir fresh_dir(PACKAGE_ROOT) @@ -589,7 +591,8 @@ def create(info, verbose=False): for dist in all_dists: os.link(join(CACHE_DIR, dist), join(pkgs_dir, dist)) - copy_conda_exe(prefix, "_conda", info["_conda_exe"]) + exe_name = format_conda_exe_name(info["_conda_exe"]) + copy_conda_exe(prefix, exe_name, info["_conda_exe"]) # Sign conda-standalone so it can pass notarization codesigner = None @@ -604,7 +607,7 @@ def create(info, verbose=False): "com.apple.security.cs.disable-library-validation": True, "com.apple.security.cs.allow-dyld-environment-variables": True, } - codesigner.sign_bundle(join(prefix, "_conda"), entitlements=entitlements) + codesigner.sign_bundle(join(prefix, exe_name), entitlements=entitlements) # This script checks to see if the install location already exists and/or contains spaces # Not to be confused with the user-provided pre_install! diff --git a/constructor/shar.py b/constructor/shar.py index f6361a0f..8475fca3 100644 --- a/constructor/shar.py +++ b/constructor/shar.py @@ -30,6 +30,7 @@ approx_size_kb, copy_conda_exe, filename_dist, + format_conda_exe_name, get_final_channels, hash_files, parse_virtual_specs, @@ -110,6 +111,7 @@ def get_header(conda_exec, tarball, info): virtual_specs = parse_virtual_specs(info) min_osx_version = virtual_specs.get("__osx", {}).get("min") or "" variables["min_osx_version"] = min_osx_version + variables["conda_exe_name"] = format_conda_exe_name(info["_conda_exe"]) min_glibc_version = virtual_specs.get("__glibc", {}).get("min") or "" variables["min_glibc_version"] = min_glibc_version diff --git a/constructor/utils.py b/constructor/utils.py index 705a42bd..c63329c1 100644 --- a/constructor/utils.py +++ b/constructor/utils.py @@ -344,6 +344,31 @@ def identify_conda_exe(conda_exe: str | Path | None = None) -> tuple[StandaloneE return None, None +def format_conda_exe_name(conda_exe: str | Path) -> str: + """Return a formatted alias for given stand-alone executable. + + - If given executable cannot be identified, returns the basename of given executable. + - If stand-alone conda is identified, returns '_conda'. + - If stand-alone mamba/micromamba is identified, returns 'micromamba'. + + Parameters:: + - conda_exe: str | Path + Path to the conda executable to be accounted for. + """ + conda_exe_name, _ = identify_conda_exe(conda_exe) + if conda_exe_name is None: + # This implies that identify_conda_exe failed + return Path(conda_exe).name + if conda_exe_name == StandaloneExe.CONDA: + return "_conda" + elif conda_exe_name == StandaloneExe.MAMBA: + return "micromamba" + else: + # This should never happen, but as a safe-guard in case `identify_conda_exe` is changed without + # accounting for this function. + raise RuntimeError("Unable to format conda exe name") + + def check_version( exe_version: str | VersionOrder | None = None, min_version: str | None = None, diff --git a/news/1090-mamba-standalone-fix b/news/1090-mamba-standalone-fix new file mode 100644 index 00000000..0074a9f6 --- /dev/null +++ b/news/1090-mamba-standalone-fix @@ -0,0 +1,19 @@ +### Enhancements + +* + +### Bug fixes + +* Rename mamba-based standalone binaries to `micromamba` and create a symbolic link to `_conda` for backwards compatibility. (#1033 via #1090) + +### Deprecations + +* + +### Docs + +* + +### Other + +* diff --git a/tests/test_examples.py b/tests/test_examples.py index 7d8913e2..04241cff 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -21,7 +21,12 @@ from conda.models.version import VersionOrder as Version from ruamel.yaml import YAML -from constructor.utils import StandaloneExe, check_version, identify_conda_exe +from constructor.utils import ( + StandaloneExe, + check_version, + format_conda_exe_name, + identify_conda_exe, +) if TYPE_CHECKING: from collections.abc import Generator, Iterable @@ -663,8 +668,9 @@ def test_macos_signing(tmp_path, self_signed_application_certificate_macos): # including binary archives like the PlugIns file cmd = ["pkgutil", "--expand-full", installer, expanded_path] _execute(cmd) + conda_exe_name = format_conda_exe_name(CONSTRUCTOR_CONDA_EXE) components = [ - Path(expanded_path, "prepare_installation.pkg", "Payload", "osx-pkg-test", "_conda"), + Path(expanded_path, "prepare_installation.pkg", "Payload", "osx-pkg-test", conda_exe_name), Path(expanded_path, "Plugins", "ExtraPage.bundle"), ] validated_signatures = [] diff --git a/tests/test_header.py b/tests/test_header.py index 94e5c906..03c5b8a2 100644 --- a/tests/test_header.py +++ b/tests/test_header.py @@ -70,6 +70,7 @@ def test_osxpkg_scripts_shellcheck(arch, check_path_spaces, script): no_rcs_arg="", script_env_variables={}, initialize_conda="condabin", + conda_exe_name="_conda", ) findings, returncode = run_shellcheck(processed) @@ -162,6 +163,7 @@ def test_template_shellcheck( "write_condarc": "", "conda_exe_payloads": conda_exe_payloads_and_size[0], "conda_exe_payloads_size": conda_exe_payloads_and_size[1], + "conda_exe_name": "_conda", }, )