diff --git a/.github/workflows/labels.yml b/.github/workflows/labels.yml
index b2c2e821d..ebfafa82a 100644
--- a/.github/workflows/labels.yml
+++ b/.github/workflows/labels.yml
@@ -20,7 +20,7 @@ jobs:
GLOBAL: https://raw.githubusercontent.com/conda/infra/main/.github/global.yml
LOCAL: .github/labels.yml
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- id: has_local
uses: andstor/file-existence-action@v2.0.0
with:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 6e81b4ded..67083b04e 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -17,7 +17,7 @@ repos:
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/asottile/pyupgrade
- rev: v3.10.1
+ rev: v3.13.0
hooks:
- id: pyupgrade
args: ["--py37-plus", "--keep-percent-format"]
diff --git a/CONSTRUCT.md b/CONSTRUCT.md
index 1bf559977..7440b3967 100644
--- a/CONSTRUCT.md
+++ b/CONSTRUCT.md
@@ -388,6 +388,30 @@ _type:_ string
Application name in the Windows "Programs and Features" control panel.
Defaults to `${NAME} ${VERSION} (Python ${PYVERSION} ${ARCH})`.
+### `script_env_variables`
+
+_required:_ no
+_type:_ dictionary
+
+Dictionary of additional environment variables to be made available to
+the pre_install and post_install scripts, in the form of VAR:VALUE
+pairs. These environment variables are in addition to those in the
+`post_install` section above and take precedence in the case of name
+collisions.
+
+On Unix the variable values are automatically single quoted, allowing
+you to supply strings with spaces, without needing to worry about
+escaping. As a consequence, string interpolation is disabled: if you
+need string interpolation, you can apply it in the
+pre_install/post_install script(s). If you need to include single quotes
+in your value, you can escape them by replacing each single quote with
+`'''`.
+
+On Windows, single quotes and double quotes are not supported.
+
+Note that the # (hash) character cannot be used as it denotes yaml
+comments for all platforms.
+
### `pre_install`
_required:_ no
diff --git a/constructor/construct.py b/constructor/construct.py
index 75af79ea1..ea246d400 100644
--- a/constructor/construct.py
+++ b/constructor/construct.py
@@ -276,6 +276,26 @@
Defaults to `${NAME} ${VERSION} (Python ${PYVERSION} ${ARCH})`.
'''),
+ ('script_env_variables', False, (dict,), '''
+Dictionary of additional environment variables to be made available to
+the pre_install and post_install scripts, in the form of VAR:VALUE
+pairs. These environment variables are in addition to those in the
+`post_install` section above and take precedence in the case of name
+collisions.
+
+On Unix the variable values are automatically single quoted, allowing
+you to supply strings with spaces, without needing to worry about
+escaping. As a consequence, string interpolation is disabled: if you
+need string interpolation, you can apply it in the
+pre_install/post_install script(s). If you need to include single quotes
+in your value, you can escape them by replacing each single quote with
+`'\''`.
+
+On Windows, single quotes and double quotes are not supported.
+
+Note that the # (hash) character cannot be used as it denotes yaml
+comments for all platforms.
+'''),
('pre_install', False, str, '''
Path to a pre-install script, run after the package cache has been set, but
before the files are linked to their final locations. As a result, you should
diff --git a/constructor/header.sh b/constructor/header.sh
index 9fd254daa..48db69a8f 100644
--- a/constructor/header.sh
+++ b/constructor/header.sh
@@ -23,6 +23,8 @@ fi
# Export variables to make installer metadata available to pre/post install scripts
# NOTE: If more vars are added, make sure to update the examples/scripts tests too
+
+_SCRIPT_ENV_VARIABLES_='' # Templated extra environment variable(s)
export INSTALLER_NAME='__NAME__'
export INSTALLER_VER='__VERSION__'
export INSTALLER_PLAT='__PLAT__'
@@ -266,10 +268,10 @@ __LICENSE__
EOF
printf "\\n"
printf "Do you accept the license terms? [yes|no]\\n"
- printf "[no] >>> "
+ printf ">>> "
read -r ans
ans=$(echo "${ans}" | tr '[:lower:]' '[:upper:]')
- while [ "$ans" != "YES" ] && [ "$ans" != "NO" ] && [ "$ans" != "" ]
+ while [ "$ans" != "YES" ] && [ "$ans" != "NO" ]
do
printf "Please answer 'yes' or 'no':'\\n"
printf ">>> "
@@ -546,8 +548,14 @@ if [ "$BATCH" = "0" ]; then
#if has_conda and initialize_conda is True
# Interactive mode.
- printf "Do you wish the installer to initialize %s\\n" "${INSTALLER_NAME}"
- printf "by running conda init? [yes|no]\\n"
+ printf "Do you wish to update your shell profile to automatically initialize conda?\\n"
+ printf "This will activate conda on startup and change the command prompt when activated.\\n"
+ printf "If you'd prefer that conda's base environment not be activated on startup,\\n"
+ printf " run the following command when conda is activated:\\n"
+ printf "\\n"
+ printf "conda config --set auto_activate_base false\\n"
+ printf "\\n"
+ printf "You can undo this by running \`conda init --reverse \$SHELL\`? [yes|no]\\n"
printf "[%s] >>> " "$DEFAULT"
read -r ans
if [ "$ans" = "" ]; then
@@ -580,11 +588,6 @@ if [ "$BATCH" = "0" ]; then
esac
fi
fi
- printf "If you'd prefer that conda's base environment not be activated on startup, \\n"
- printf " set the auto_activate_base parameter to false: \\n"
- printf "\\n"
- printf "conda config --set auto_activate_base false\\n"
- printf "\\n"
#endif
printf "Thank you for installing %s!\\n" "${INSTALLER_NAME}"
diff --git a/constructor/nsis/main.nsi.tmpl b/constructor/nsis/main.nsi.tmpl
index c4897dac4..a2ae2fb28 100644
--- a/constructor/nsis/main.nsi.tmpl
+++ b/constructor/nsis/main.nsi.tmpl
@@ -1113,6 +1113,7 @@ Section "Install"
File /nonfatal /r __INDEX_CACHE__
File /r __REPODATA_RECORD__
+ @SCRIPT_ENV_VARIABLES@
System::Call 'kernel32::SetEnvironmentVariable(t,t)i("CONDA_SAFETY_CHECKS", "disabled").r0'
System::Call 'kernel32::SetEnvironmentVariable(t,t)i("CONDA_EXTRA_SAFETY_CHECKS", "no").r0'
System::Call 'kernel32::SetEnvironmentVariable(t,t)i("CONDA_PKGS_DIRS", "$INSTDIR\pkgs")".r0'
diff --git a/constructor/osx/run_user_script.sh b/constructor/osx/run_user_script.sh
index 53bd4501a..9f6291518 100644
--- a/constructor/osx/run_user_script.sh
+++ b/constructor/osx/run_user_script.sh
@@ -31,6 +31,7 @@ export INSTALLER_VER="__VERSION__"
export INSTALLER_PLAT="__PLAT__"
export INSTALLER_TYPE="PKG"
export PRE_OR_POST="__PRE_OR_POST__"
+_SCRIPT_ENV_VARIABLES_='' # Templated extra environment variable(s)
# Run user-provided script
if [ -f "$PREFIX/pkgs/user_${PRE_OR_POST}" ]; then
diff --git a/constructor/osxpkg.py b/constructor/osxpkg.py
index 5758ab0a9..8c72638c7 100644
--- a/constructor/osxpkg.py
+++ b/constructor/osxpkg.py
@@ -6,14 +6,21 @@
from os.path import abspath, dirname, exists, isdir, join
from pathlib import Path
from plistlib import dump as plist_dump
-from subprocess import check_call
from tempfile import NamedTemporaryFile
from . import preconda
from .conda_interface import conda_context
from .construct import ns_platform, parse
from .imaging import write_images
-from .utils import add_condarc, approx_size_kb, fill_template, get_final_channels, preprocess, rm_rf
+from .utils import (
+ add_condarc,
+ approx_size_kb,
+ explained_check_call,
+ fill_template,
+ get_final_channels,
+ preprocess,
+ rm_rf,
+)
OSX_DIR = join(dirname(__file__), "osx")
CACHE_DIR = PACKAGE_ROOT = PACKAGES_DIR = SCRIPTS_DIR = None
@@ -224,10 +231,13 @@ def modify_xml(xml_path, info):
'initialize_by_default', True) else 'false')
path_choice.set('title', "Add conda initialization to the shell")
path_description = """
- If this box is checked, "conda init" will be executed to ensure that
- conda is available in your preferred shell upon startup. If unchecked,
- you must this initialization yourself or activate the environment
- manually for each shell in which you wish to use it."""
+ If this box is checked, conda will be automatically activated in your
+ preferred shell on startup. This will change the command prompt when
+ activated. If your prefer that conda's base environment not be activated
+ on startup, run `conda config --set auto_activate_base false`. You can
+ undo this by running `conda init --reverse ${SHELL}`.
+ If unchecked, you must this initialization yourself or activate the
+ environment manually for each shell in which you wish to use it."""
path_choice.set('description', ' '.join(path_description.split()))
elif ident.endswith('cacheclean'):
path_choice.set('visible', 'true')
@@ -299,8 +309,12 @@ def move_script(src, dst, info, ensure_shebang=False, user_script_type=None):
'REGISTER_ENVS': str(info.get("register_envs", True)).lower(),
}
data = preprocess(data, ppd)
+ custom_variables = info.get('script_env_variables', {})
data = fill_template(data, replace)
+ data = data.replace("_SCRIPT_ENV_VARIABLES_=''", '\n'.join(
+ [f"export {key}='{value}'" for key, value in custom_variables.items()]))
+
with open(dst, 'w') as fo:
if (
ensure_shebang
@@ -340,7 +354,7 @@ def pkgbuild(name, identifier=None, version=None, install_location=None):
args += ["--install-location", install_location]
output = os.path.join(PACKAGES_DIR, f"{name}.pkg")
args += [output]
- check_call(args)
+ explained_check_call(args)
return output
@@ -360,7 +374,7 @@ def pkgbuild_prepare_installation(info):
# set to the sum of the compressed tarballs, which is not representative
try:
# expand to apply patches
- check_call(["pkgutil", "--expand", pkg, f"{pkg}.expanded"])
+ explained_check_call(["pkgutil", "--expand", pkg, f"{pkg}.expanded"])
payload_xml = os.path.join(f"{pkg}.expanded", "PackageInfo")
tree = ET.parse(payload_xml)
root = tree.getroot()
@@ -368,7 +382,7 @@ def pkgbuild_prepare_installation(info):
payload.set("installKBytes", str(approx_pkgs_size_kb))
tree.write(payload_xml)
# repack
- check_call(["pkgutil", "--flatten", f"{pkg}.expanded", pkg])
+ explained_check_call(["pkgutil", "--flatten", f"{pkg}.expanded", pkg])
return pkg
finally:
shutil.rmtree(f"{pkg}.expanded")
@@ -461,7 +475,7 @@ def create(info, verbose=False):
"com.apple.security.cs.allow-dyld-environment-variables": True,
}
plist_dump(plist, f)
- check_call(
+ explained_check_call(
[
# hardcode to system location to avoid accidental clobber in PATH
"/usr/bin/codesign",
@@ -522,11 +536,11 @@ def create(info, verbose=False):
for name in names:
args.extend(['--package', join(PACKAGES_DIR, "%s.pkg" % name)])
args.append(xml_path)
- check_call(args)
+ explained_check_call(args)
modify_xml(xml_path, info)
identity_name = info.get('signing_identity_name')
- check_call([
+ explained_check_call([
"/usr/bin/productbuild",
"--distribution", xml_path,
"--package-path", PACKAGES_DIR,
@@ -534,7 +548,7 @@ def create(info, verbose=False):
"tmp.pkg" if identity_name else info['_outpath']
])
if identity_name:
- check_call([
+ explained_check_call([
# hardcode to system location to avoid accidental clobber in PATH
'/usr/bin/productsign', '--sign', identity_name,
"tmp.pkg",
diff --git a/constructor/shar.py b/constructor/shar.py
index b725da430..513c6bbf4 100644
--- a/constructor/shar.py
+++ b/constructor/shar.py
@@ -93,8 +93,11 @@ def get_header(conda_exec, tarball, info):
data = read_header_template()
data = preprocess(data, ppd)
+ custom_variables = info.get('script_env_variables', {})
data = fill_template(data, replace)
+ data = data.replace("_SCRIPT_ENV_VARIABLES_=''", '\n'.join(
+ [f"export {key}='{value}'" for key, value in custom_variables.items()]))
return data
diff --git a/constructor/utils.py b/constructor/utils.py
index 37eedb41d..b763fab9e 100644
--- a/constructor/utils.py
+++ b/constructor/utils.py
@@ -12,12 +12,21 @@
from os import sep, unlink
from os.path import basename, isdir, isfile, islink, normpath
from shutil import rmtree
+from subprocess import check_call
from ruamel import yaml
logger = logging.getLogger(__name__)
+def explained_check_call(args):
+ """
+ Execute a system process and debug the invocation
+ """
+ logger.debug("Executing: %s", " ".join(args))
+ return check_call(args)
+
+
def filename_dist(dist):
""" Return the filename of a distribution. """
if hasattr(dist, 'to_filename'):
diff --git a/constructor/winexe.py b/constructor/winexe.py
index c17e852a9..3ea7c47b8 100644
--- a/constructor/winexe.py
+++ b/constructor/winexe.py
@@ -87,6 +87,23 @@ def insert_tempfiles_commands(paths: os.PathLike) -> List[str]:
return lines
+def setup_script_env_variables(info) -> List[str]:
+ """Helper function to insert extra environment variables into nsis template.
+
+ Args:
+ info: Dictionary of information parsed from construct.yaml
+
+ Returns:
+ List[str]: Commands to be inserted into nsi template
+ """
+ lines = []
+ for name, value in info.get('script_env_variables', {}).items():
+ lines.append(
+ "System::Call 'kernel32::SetEnvironmentVariable(t,t)i"
+ + f"""("{name}", {str_esc(value)}).r0'""")
+ return lines
+
+
def custom_nsi_insert_from_file(filepath: os.PathLike) -> str:
"""Insert NSI script commands from file.
@@ -336,6 +353,7 @@ def make_nsi(info, dir_path, extra_files=None, temp_extra_files=None):
'${NAME} ${VERSION} (Python ${PYVERSION} ${ARCH})'
)),
('@EXTRA_FILES@', '\n '.join(extra_files_commands(extra_files, dir_path))),
+ ('@SCRIPT_ENV_VARIABLES@', '\n '.join(setup_script_env_variables(info))),
(
'@CUSTOM_WELCOME_FILE@',
custom_nsi_insert_from_file(info.get('welcome_file', ''))
diff --git a/docs/source/construct-yaml.md b/docs/source/construct-yaml.md
index 1bf559977..7440b3967 100644
--- a/docs/source/construct-yaml.md
+++ b/docs/source/construct-yaml.md
@@ -388,6 +388,30 @@ _type:_ string
Application name in the Windows "Programs and Features" control panel.
Defaults to `${NAME} ${VERSION} (Python ${PYVERSION} ${ARCH})`.
+### `script_env_variables`
+
+_required:_ no
+_type:_ dictionary
+
+Dictionary of additional environment variables to be made available to
+the pre_install and post_install scripts, in the form of VAR:VALUE
+pairs. These environment variables are in addition to those in the
+`post_install` section above and take precedence in the case of name
+collisions.
+
+On Unix the variable values are automatically single quoted, allowing
+you to supply strings with spaces, without needing to worry about
+escaping. As a consequence, string interpolation is disabled: if you
+need string interpolation, you can apply it in the
+pre_install/post_install script(s). If you need to include single quotes
+in your value, you can escape them by replacing each single quote with
+`'''`.
+
+On Windows, single quotes and double quotes are not supported.
+
+Note that the # (hash) character cannot be used as it denotes yaml
+comments for all platforms.
+
### `pre_install`
_required:_ no
diff --git a/examples/scripts/construct.yaml b/examples/scripts/construct.yaml
index 97d64e197..e57bdffa0 100644
--- a/examples/scripts/construct.yaml
+++ b/examples/scripts/construct.yaml
@@ -5,6 +5,14 @@ channels:
- http://repo.anaconda.com/pkgs/main/
specs:
- python
+
+script_env_variables:
+ CUSTOM_VARIABLE_1: FIR$T-CUSTOM_'\''STRING'\'' WITH SPACES AND @*! "CHARACTERS" # [not win]
+ CUSTOM_VARIABLE_2: $ECOND-CUSTOM_'\''STRING'\'' WITH SPACES AND @*! "CHARACTERS" # [not win]
+ CUSTOM_VARIABLE_1: FIR$T-CUSTOM_STRING WITH SPACES AND @*! CHARACTERS # [win]
+ CUSTOM_VARIABLE_2: $ECOND-CUSTOM_STRING WITH SPACES AND @*! CHARACTERS # [win]
+
+
pre_install: pre_install.sh # [unix]
pre_install: pre_install.bat # [win]
pre_install_desc: "Adding this description makes the script selectable in the UI"
diff --git a/examples/scripts/post_install.bat b/examples/scripts/post_install.bat
index 6b80f986a..7a435cafc 100644
--- a/examples/scripts/post_install.bat
+++ b/examples/scripts/post_install.bat
@@ -4,4 +4,6 @@ if not "%INSTALLER_VER%" == "X" exit 1
if not "%INSTALLER_PLAT%" == "win-64" exit 1
if not "%INSTALLER_TYPE%" == "EXE" exit 1
if "%PREFIX%" == "" exit 1
+if not "%CUSTOM_VARIABLE_1%" == "FIR$T-CUSTOM_STRING WITH SPACES AND @*! CHARACTERS" exit 1
+if not "%CUSTOM_VARIABLE_2%" == "$ECOND-CUSTOM_STRING WITH SPACES AND @*! CHARACTERS" exit 1
if not exist "%PREFIX%\pre_install_sentinel.txt" exit 1
diff --git a/examples/scripts/post_install.sh b/examples/scripts/post_install.sh
index 54d2b268e..1db67d136 100644
--- a/examples/scripts/post_install.sh
+++ b/examples/scripts/post_install.sh
@@ -9,10 +9,17 @@ echo "INSTALLER_NAME=${INSTALLER_NAME}"
echo "INSTALLER_VER=${INSTALLER_VER}"
echo "INSTALLER_PLAT=${INSTALLER_PLAT}"
echo "INSTALLER_TYPE=${INSTALLER_TYPE}"
+echo "CUSTOM_VARIABLE_1=${CUSTOM_VARIABLE_1}"
+echo "CUSTOM_VARIABLE_2=${CUSTOM_VARIABLE_2}"
echo "PREFIX=${PREFIX}"
test "${INSTALLER_NAME}" = "Scripts"
test "${INSTALLER_VER}" = "X"
+# shellcheck disable=SC2016 # String interpolation disabling is deliberate
+test "${CUSTOM_VARIABLE_1}" = 'FIR$T-CUSTOM_'\''STRING'\'' WITH SPACES AND @*! "CHARACTERS"'
+# shellcheck disable=SC2016 # String interpolation disabling is deliberate
+test "${CUSTOM_VARIABLE_2}" = '$ECOND-CUSTOM_'\''STRING'\'' WITH SPACES AND @*! "CHARACTERS"'
+
if [[ $(uname -s) == Linux ]]; then
if [[ ${INSTALLER_PLAT} != linux-* ]]; then
exit 1
diff --git a/examples/scripts/pre_install.bat b/examples/scripts/pre_install.bat
index d1bd0598a..5ece67c31 100644
--- a/examples/scripts/pre_install.bat
+++ b/examples/scripts/pre_install.bat
@@ -3,4 +3,6 @@ if not "%INSTALLER_VER%" == "X" exit 1
if not "%INSTALLER_PLAT%" == "win-64" exit 1
if not "%INSTALLER_TYPE%" == "EXE" exit 1
if "%PREFIX%" == "" exit 1
+if not "%CUSTOM_VARIABLE_1%" == "FIR$T-CUSTOM_STRING WITH SPACES AND @*! CHARACTERS" exit 1
+if not "%CUSTOM_VARIABLE_2%" == "$ECOND-CUSTOM_STRING WITH SPACES AND @*! CHARACTERS" exit 1
echo Added by pre-install script > "%PREFIX%\pre_install_sentinel.txt"
diff --git a/examples/scripts/pre_install.sh b/examples/scripts/pre_install.sh
index a865dcc0e..df0806980 100644
--- a/examples/scripts/pre_install.sh
+++ b/examples/scripts/pre_install.sh
@@ -6,10 +6,17 @@ echo "INSTALLER_NAME=${INSTALLER_NAME}"
echo "INSTALLER_VER=${INSTALLER_VER}"
echo "INSTALLER_PLAT=${INSTALLER_PLAT}"
echo "INSTALLER_TYPE=${INSTALLER_TYPE}"
+echo "CUSTOM_VARIABLE_1=${CUSTOM_VARIABLE_1}"
+echo "CUSTOM_VARIABLE_2=${CUSTOM_VARIABLE_2}"
echo "PREFIX=${PREFIX}"
test "${INSTALLER_NAME}" = "Scripts"
test "${INSTALLER_VER}" = "X"
+# shellcheck disable=SC2016 # String interpolation disabling is deliberate
+test "${CUSTOM_VARIABLE_1}" = 'FIR$T-CUSTOM_'\''STRING'\'' WITH SPACES AND @*! "CHARACTERS"'
+# shellcheck disable=SC2016 # String interpolation disabling is deliberate
+test "${CUSTOM_VARIABLE_2}" = '$ECOND-CUSTOM_'\''STRING'\'' WITH SPACES AND @*! "CHARACTERS"'
+
if [[ $(uname -s) == Linux ]]; then
if [[ ${INSTALLER_PLAT} != linux-* ]]; then
exit 1
diff --git a/news/713-update-conda-init-wording b/news/713-update-conda-init-wording
new file mode 100644
index 000000000..c1061b881
--- /dev/null
+++ b/news/713-update-conda-init-wording
@@ -0,0 +1,19 @@
+### Enhancements
+
+*
+
+### Bug fixes
+
+*
+
+### Deprecations
+
+*
+
+### Docs
+
+*
+
+### Other
+
+* Clarify consequences for when `conda init` is run during the installation. (#713)
diff --git a/news/718-script_env_variables-option b/news/718-script_env_variables-option
new file mode 100644
index 000000000..9ff8e5f67
--- /dev/null
+++ b/news/718-script_env_variables-option
@@ -0,0 +1,21 @@
+### Enhancements
+
+* Added support for `script_env_variables` allowing specification of
+ environment variables in the `construct.yaml` for use by pre- and
+ post-install scripts.
+
+### Bug fixes
+
+*
+
+### Deprecations
+
+*
+
+### Docs
+
+*
+
+### Other
+
+*
diff --git a/news/722-license-yn b/news/722-license-yn
new file mode 100644
index 000000000..3c74bb242
--- /dev/null
+++ b/news/722-license-yn
@@ -0,0 +1,19 @@
+### Enhancements
+
+*
+
+### Bug fixes
+
+* Fix a regression in the license prompt on SH installers to require a explicit answer instead of defaulting to `no` on Enter. (#721 via #722)
+
+### Deprecations
+
+*
+
+### Docs
+
+*
+
+### Other
+
+*