Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions constructor/header.sh
Original file line number Diff line number Diff line change
Expand Up @@ -598,8 +598,7 @@ CONDA_EXTRA_SAFETY_CHECKS=no \
CONDA_CHANNELS="{{ channels }}" \
CONDA_PKGS_DIRS="$PREFIX/pkgs" \
CONDA_QUIET="$BATCH" \
"$CONDA_EXEC" install --offline --file "$PREFIX/pkgs/env.txt" -yp "$PREFIX" $shortcuts {{ no_rcs_arg }} || exit 1
rm -f "$PREFIX/pkgs/env.txt"
"$CONDA_EXEC" install --offline --file "$PREFIX/conda-meta/initial-state.explicit.txt" -yp "$PREFIX" $shortcuts {{ no_rcs_arg }} || exit 1

{%- if has_conda %}
mkdir -p "$PREFIX/envs"
Expand Down Expand Up @@ -638,8 +637,7 @@ for env_pkgs in "${PREFIX}"/pkgs/envs/*/; do
CONDA_CHANNELS="$env_channels" \
CONDA_PKGS_DIRS="$PREFIX/pkgs" \
CONDA_QUIET="$BATCH" \
"$CONDA_EXEC" install --offline --file "${env_pkgs}env.txt" -yp "$PREFIX/envs/$env_name" $env_shortcuts {{ no_rcs_arg }} || exit 1
rm -f "${env_pkgs}env.txt"
"$CONDA_EXEC" install --offline --file "$PREFIX/envs/$env_name/conda-meta/initial-state.explicit.txt" -yp "$PREFIX/envs/$env_name" $env_shortcuts {{ no_rcs_arg }} || exit 1
done
{%- endif %}

Expand Down
13 changes: 4 additions & 9 deletions constructor/nsis/main.nsi.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -1375,13 +1375,12 @@ Section "Install"
${Print} "Setting up the {{ env.name }} environment..."
SetDetailsPrint listonly

# List of packages to install
SetOutPath "{{ env.env_txt_dir }}"
File "{{ env.env_txt_abspath }}"

# A conda-meta\history file is required for a valid conda prefix
SetOutPath "{{ env.conda_meta }}"
File "{{ env.history_abspath }}"
# List of packages to install, as a lockfile
File "{{ env.lockfile_txt_abspath }}"

# Set channels
System::Call 'kernel32::SetEnvironmentVariable(t,t)i("CONDA_CHANNELS", "{{ channels }}").r0'
Expand All @@ -1391,21 +1390,17 @@ Section "Install"
# Run conda install
${If} $Ana_CreateShortcuts_State = ${BST_CHECKED}
${Print} "Installing packages for {{ env.name }}, creating shortcuts if necessary..."
push '"$INSTDIR\_conda.exe" install --offline -yp "{{ env.prefix }}" --file "{{ env.env_txt }}" {{ env.shortcuts }} {{ env.no_rcs_arg }}'
push '"$INSTDIR\_conda.exe" install --offline -yp "{{ env.prefix }}" --file "{{ env.lockfile_txt }}" {{ env.shortcuts }} {{ env.no_rcs_arg }}'
${Else}
${Print} "Installing packages for {{ env.name }}..."
push '"$INSTDIR\_conda.exe" install --offline -yp "{{ env.prefix }}" --file "{{ env.env_txt }}" --no-shortcuts {{ env.no_rcs_arg }}'
push '"$INSTDIR\_conda.exe" install --offline -yp "{{ env.prefix }}" --file "{{ env.lockfile_txt }}" --no-shortcuts {{ env.no_rcs_arg }}'
${EndIf}
push 'Failed to link extracted packages to {{ env.prefix }}!'
push 'WithLog'
SetDetailsPrint listonly
call AbortRetryNSExecWait
SetDetailsPrint both

# Cleanup {{ env.name }} env.txt
SetOutPath "$INSTDIR"
Delete "{{ env.env_txt }}"

# Restore shipped conda-meta\history for remapped
# channels and retain only the first transaction
SetOutPath "{{ env.conda_meta }}"
Expand Down
6 changes: 2 additions & 4 deletions constructor/osx/run_installation.sh
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,13 @@ CONDA_SAFETY_CHECKS=disabled \
CONDA_EXTRA_SAFETY_CHECKS=no \
CONDA_CHANNELS={{ channels }} \
CONDA_PKGS_DIRS="$PREFIX/pkgs" \
"$CONDA_EXEC" install --offline --file "$PREFIX/pkgs/env.txt" -yp "$PREFIX" $shortcuts {{ no_rcs_arg }}; then
"$CONDA_EXEC" install --offline --file "$PREFIX/conda-meta/initial-state.explicit.txt" -yp "$PREFIX" $shortcuts {{ no_rcs_arg }}; then
echo "ERROR: could not complete the conda install"
exit 1
fi

# Move the prepackaged history file into place
mv "$PREFIX/pkgs/conda-meta/history" "$PREFIX/conda-meta/history"
rm -f "$PREFIX/env.txt"

# Same, but for the extra environments

Expand Down Expand Up @@ -99,10 +98,9 @@ for env_pkgs in "${PREFIX}"/pkgs/envs/*/; do
CONDA_EXTRA_SAFETY_CHECKS=no \
CONDA_CHANNELS="$env_channels" \
CONDA_PKGS_DIRS="$PREFIX/pkgs" \
"$CONDA_EXEC" install --offline --file "${env_pkgs}env.txt" -yp "$PREFIX/envs/$env_name" $env_shortcuts {{ no_rcs_arg }} || exit 1
"$CONDA_EXEC" install --offline --file "$PREFIX/envs/$env_name/conda-meta/initial-state.explicit.txt" -yp "$PREFIX/envs/$env_name" $env_shortcuts {{ no_rcs_arg }} || exit 1
# Move the prepackaged history file into place
mv "${env_pkgs}/conda-meta/history" "$PREFIX/envs/$env_name/conda-meta/history"
rm -f "${env_pkgs}env.txt"
done

# Cleanup!
Expand Down
10 changes: 10 additions & 0 deletions constructor/osxpkg.py
Original file line number Diff line number Diff line change
Expand Up @@ -562,8 +562,18 @@ def create(info, verbose=False):
fresh_dir(PACKAGE_ROOT)
fresh_dir(SCRIPTS_DIR)
pkgs_dir = join(prefix, "pkgs")
conda_meta = join(prefix, "conda-meta")
os.makedirs(pkgs_dir)
os.makedirs(conda_meta)
preconda.write_files(info, pkgs_dir)
# We need to move the initial-state.explicit.txt files from pkgs/ to their conda-meta targets
shutil.move(join(pkgs_dir, "initial-state.explicit.txt"), conda_meta)
if isdir(join(pkgs_dir, "envs")):
for envname in os.listdir(join(pkgs_dir, "envs")):
lockfile = join(pkgs_dir, "envs", envname, "initial-state.explicit.txt")
env_conda_meta = join(prefix, "envs", envname, "conda-meta")
os.makedirs(env_conda_meta)
shutil.move(lockfile, env_conda_meta)
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not super proud of this block, but the alternative is refactoring the logic in preconda so it's multi-directory (and not just assume that everything goes in dst_dir == "pkgs/").

What do you think @marcoesters? Worth the refactor or not? There'll still be differences between Unix and Windows.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if it's much of a refactor. Since write_files writes very specific files with very specific file locations, you could pass in prefix and adjust the paths inside the function accordingly. This would make the SH code a little simpler, too.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's see: fe1e040

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this looks pretty clean and extendable. The directory creation can probably be further abstracted by using Path().parent.mkdir(parents=True, exist_ok=True), but that's probably better suited for #1063.

preconda.copy_extra_files(info.get("extra_files", []), prefix)
# These are the user-provided scripts, maybe patched to have a shebang
# They will be called by a wrapping script added later, if present
Expand Down
10 changes: 5 additions & 5 deletions constructor/preconda.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
except ImportError:
import ruamel_json as json

files = ".constructor-build.info", "urls", "urls.txt", "env.txt"
files = ".constructor-build.info", "urls", "urls.txt", "initial-state.explicit.txt"


def write_index_cache(info, dst_dir, used_packages):
Expand Down Expand Up @@ -172,7 +172,7 @@ def write_files(info, dst_dir):

# base environment file used with conda install --file
# (list of specs/dists to install)
write_env_txt(info, dst_dir, final_urls_md5s)
write_initial_state_explicit_txt(info, dst_dir, final_urls_md5s)

for fn in files:
os.chmod(join(dst_dir, fn), 0o664)
Expand All @@ -185,7 +185,7 @@ def write_files(info, dst_dir):
user_requested_specs = env_config.get("user_requested_specs", env_config.get("specs", ()))
write_conda_meta(info, env_dst_dir, env_urls_md5, user_requested_specs)
# environment installation list
write_env_txt(info, env_dst_dir, env_urls_md5)
write_initial_state_explicit_txt(info, env_dst_dir, env_urls_md5)
# channels
write_channels_txt(info, env_dst_dir, env_config)
# shortcuts
Expand Down Expand Up @@ -245,7 +245,7 @@ def write_repodata_record(info, dst_dir):
json.dump(rr_json, rf, indent=2, sort_keys=True)


def write_env_txt(info, dst_dir, urls):
def write_initial_state_explicit_txt(info, dst_dir, urls):
"""
urls is an iterable of tuples with url and md5 values
"""
Expand All @@ -257,7 +257,7 @@ def write_env_txt(info, dst_dir, urls):
@EXPLICIT
"""
).lstrip()
with open(join(dst_dir, "env.txt"), "w") as envf:
with open(join(dst_dir, "initial-state.explicit.txt"), "w") as envf:
envf.write(header)
for url, md5 in urls:
maybe_different_url = ensure_transmuted_ext(info, url)
Expand Down
10 changes: 8 additions & 2 deletions constructor/shar.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,16 @@ def create(info, verbose=False):
post_t = tarfile.open(postconda_tarball, "w:bz2")
for dist in preconda_files:
fn = filename_dist(dist)
pre_t.add(join(tmp_dir, fn), "pkgs/" + fn)
pre_t.add(
join(tmp_dir, fn),
f"conda-meta/{fn}" if dist == "initial-state.explicit.txt" else f"pkgs/{fn}",
)

for env_name in info.get("_extra_envs_info", ()):
pre_t.add(join(tmp_dir, "envs", env_name, "env.txt"), f"pkgs/envs/{env_name}/env.txt")
pre_t.add(
join(tmp_dir, "envs", env_name, "initial-state.explicit.txt"),
f"envs/{env_name}/conda-meta/initial-state.explicit.txt",
)
pre_t.add(
join(tmp_dir, "envs", env_name, "shortcuts.txt"), f"pkgs/envs/{env_name}/shortcuts.txt"
)
Expand Down
19 changes: 10 additions & 9 deletions constructor/winexe.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,10 @@ def setup_envs_commands(info, dir_path):
{
"name": "base",
"prefix": r"$INSTDIR",
"env_txt": r"$INSTDIR\pkgs\env.txt", # env.txt as seen by the running installer
"env_txt_dir": r"$INSTDIR\pkgs", # env.txt location in the installer filesystem
"env_txt_abspath": join(
dir_path, "env.txt"
), # env.txt path while building the installer
# initial-state.explicit.txt as seen by the running installer
"lockfile_txt": r"$INSTDIR\conda-meta\initial-state.explicit.txt",
# initial-state.explicit.txt path while building the installer
"lockfile_txt_abspath": join(dir_path, "initial-state.explicit.txt"),
"conda_meta": r"$INSTDIR\conda-meta",
"history_abspath": join(dir_path, "conda-meta", "history"),
"final_channels": get_final_channels(info),
Expand All @@ -108,9 +107,12 @@ def setup_envs_commands(info, dir_path):
{
"name": env_name,
"prefix": join("$INSTDIR", "envs", env_name),
"env_txt": join("$INSTDIR", "pkgs", "envs", env_name, "env.txt"),
"env_txt_dir": join("$INSTDIR", "pkgs", "envs", env_name),
"env_txt_abspath": join(dir_path, "envs", env_name, "env.txt"),
"lockfile_txt": join(
"$INSTDIR", "envs", env_name, "conda-meta", "initial-state.explicit.txt"
),
"lockfile_txt_abspath": join(
dir_path, "envs", env_name, "initial-state.explicit.txt"
),
"conda_meta": join("$INSTDIR", "envs", env_name, "conda-meta"),
"history_abspath": join(dir_path, "envs", env_name, "conda-meta", "history"),
"final_channels": get_final_channels(channel_info),
Expand Down Expand Up @@ -175,7 +177,6 @@ def make_nsi(
"licensefile": abspath(info.get("license_file", join(NSIS_DIR, "placeholder_license.txt"))),
"conda_history": "@" + join("conda-meta", "history"),
"conda_exe": "@_conda.exe",
"env_txt": "@env.txt",
"urls_file": "@urls",
"urls_txt_file": "@urls.txt",
"pre_install": "@pre_install.bat",
Expand Down
19 changes: 19 additions & 0 deletions news/1059-lockfiles
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
### Enhancements

* Ship `conda-meta/initial-state.explicit.txt` as a copy of the lockfile that provisions the initial state of each environment. (#1052 via #1059)

### Bug fixes

* <news item>

### Deprecations

* <news item>

### Docs

* <news item>

### Other

* <news item>
12 changes: 11 additions & 1 deletion tests/test_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,17 @@ def test_example_extra_pages_win(tmp_path, request, extra_pages, monkeypatch):
def test_example_extra_envs(tmp_path, request):
input_path = _example_path("extra_envs")
for installer, install_dir in create_installer(input_path, tmp_path):
_run_installer(input_path, installer, install_dir, request=request)
_run_installer(input_path, installer, install_dir, request=request, uninstall=False)
assert (
"@EXPLICIT" in (install_dir / "conda-meta" / "initial-state.explicit.txt").read_text()
)
for env in install_dir.glob("envs/*/conda-meta/"):
envtxt = env / "initial-state.explicit.txt"
assert envtxt.exists()
assert "@EXPLICIT" in envtxt.read_text()

if sys.platform.startswith("win"):
_run_uninstaller_exe(install_dir=install_dir)


def test_example_extra_files(tmp_path, request):
Expand Down
Loading