Skip to content

Commit

Permalink
Merge branch 'main' of github.com:conda/constructor into register-envs
Browse files Browse the repository at this point in the history
  • Loading branch information
jaimergp committed Oct 6, 2023
2 parents b3e5d5b + 2302872 commit c44d85d
Show file tree
Hide file tree
Showing 20 changed files with 226 additions and 24 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/labels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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/[email protected]
with:
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
24 changes: 24 additions & 0 deletions CONSTRUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,30 @@ _type:_ string<br/>
Application name in the Windows "Programs and Features" control panel.
Defaults to `${NAME} ${VERSION} (Python ${PYVERSION} ${ARCH})`.

### `script_env_variables`

_required:_ no<br/>
_type:_ dictionary<br/>

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<br/>
Expand Down
20 changes: 20 additions & 0 deletions constructor/construct.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 12 additions & 9 deletions constructor/header.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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__'
Expand Down Expand Up @@ -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 ">>> "
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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}"
Expand Down
1 change: 1 addition & 0 deletions constructor/nsis/main.nsi.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
1 change: 1 addition & 0 deletions constructor/osx/run_user_script.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
40 changes: 27 additions & 13 deletions constructor/osxpkg.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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')
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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


Expand All @@ -360,15 +374,15 @@ 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()
payload = root.find("payload")
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")
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -522,19 +536,19 @@ 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,
"--identifier", info.get("reverse_domain_identifier", info['name']),
"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",
Expand Down
3 changes: 3 additions & 0 deletions constructor/shar.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down
9 changes: 9 additions & 0 deletions constructor/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'):
Expand Down
18 changes: 18 additions & 0 deletions constructor/winexe.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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', ''))
Expand Down
24 changes: 24 additions & 0 deletions docs/source/construct-yaml.md
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,30 @@ _type:_ string<br/>
Application name in the Windows "Programs and Features" control panel.
Defaults to `${NAME} ${VERSION} (Python ${PYVERSION} ${ARCH})`.

### `script_env_variables`

_required:_ no<br/>
_type:_ dictionary<br/>

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<br/>
Expand Down
8 changes: 8 additions & 0 deletions examples/scripts/construct.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions examples/scripts/post_install.bat
Original file line number Diff line number Diff line change
Expand Up @@ -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
7 changes: 7 additions & 0 deletions examples/scripts/post_install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit c44d85d

Please sign in to comment.