From b4e70b69037d0ea53b49d2f12c811af85c550c91 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 5 Sep 2023 15:50:18 -0600 Subject: [PATCH 01/50] Fix pip install -e . on Windows The main.js file from conda-store-ui contains an emoji character that cannot be encoded correctly unless the encoding is set to utf-8, which is not always the default on Windows. --- conda-store-server/hatch_build.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conda-store-server/hatch_build.py b/conda-store-server/hatch_build.py index f73b7ae7d..d2ae57548 100644 --- a/conda-store-server/hatch_build.py +++ b/conda-store-server/hatch_build.py @@ -62,10 +62,10 @@ def initialize(self, version: str, build_data: Dict[str, Any]) -> None: # main.js to enable easy configuration see # conda_store_server/server/templates/conda-store-ui.html # for global variable set - with (source_directory / "main.js").open("r") as source_f: + with (source_directory / "main.js").open("r", encoding='utf-8') as source_f: content = source_f.read() content = re.sub( '"MISSING_ENV_VAR"', "GLOBAL_CONDA_STORE_STATE", content ) - with (destination_directory / "main.js").open("w") as dest_f: + with (destination_directory / "main.js").open("w", encoding='utf-8') as dest_f: dest_f.write(content) From 1719638cd52e9d247d086c01dd53ab44ffca526d Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 6 Sep 2023 15:22:22 -0600 Subject: [PATCH 02/50] Use None for permissions when on Windows Windows uses a different type of permissions system than Unix. For now, using None will cause action_set_conda_prefix_permissions to do nothing. --- conda-store-server/conda_store_server/app.py | 11 ++++++++--- conda-store-server/conda_store_server/schema.py | 13 +++++++------ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/conda-store-server/conda_store_server/app.py b/conda-store-server/conda_store_server/app.py index ce7710981..f9e98616e 100644 --- a/conda-store-server/conda_store_server/app.py +++ b/conda-store-server/conda_store_server/app.py @@ -33,6 +33,8 @@ ) +ON_WIN = sys.platform.startswith("win") + def conda_store_validate_specification( db: Session, conda_store: "CondaStore", @@ -302,21 +304,24 @@ def _default_celery_results_backend(self): ) default_uid = Integer( - os.getuid(), + None if ON_WIN else os.getuid(), help="default uid to assign to built environments", config=True, + allow_none=True, ) default_gid = Integer( - os.getgid(), + None if ON_WIN else os.getgid(), help="default gid to assign to built environments", config=True, + allow_none=True, ) default_permissions = Unicode( - "775", + None if ON_WIN else "775", help="default file permissions to assign to built environments", config=True, + allow_none=True, ) default_docker_base_image = Union( diff --git a/conda-store-server/conda_store_server/schema.py b/conda-store-server/conda_store_server/schema.py index 176659a4b..db4b90254 100644 --- a/conda-store-server/conda_store_server/schema.py +++ b/conda-store-server/conda_store_server/schema.py @@ -10,6 +10,7 @@ from conda_store_server import conda_utils +ON_WIN = sys.platform.startswith("win") def _datetime_factory(offset: datetime.timedelta): """utcnow datetime + timezone as string""" @@ -195,20 +196,20 @@ class Settings(BaseModel): metadata={"global": True}, ) - default_uid: int = Field( - os.getuid(), + default_uid: int | None = Field( + None if ON_WIN else os.getuid(), description="default uid to assign to built environments", metadata={"global": True}, ) - default_gid: int = Field( - os.getgid(), + default_gid: int | None = Field( + None if ON_WIN else os.getgid(), description="default gid to assign to built environments", metadata={"global": True}, ) - default_permissions: str = Field( - "775", + default_permissions: str | None = Field( + None if ON_WIN else "775", description="default file permissions to assign to built environments", metadata={"global": True}, ) From 322afb5510f32e46c873cc2d8209f68ab0abbf99 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 6 Sep 2023 15:39:06 -0600 Subject: [PATCH 03/50] Use the Python API for alembic rather than a subprocess --- conda-store-server/conda_store_server/dbutil.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/conda-store-server/conda_store_server/dbutil.py b/conda-store-server/conda_store_server/dbutil.py index d1c0c7506..13da7026c 100644 --- a/conda-store-server/conda_store_server/dbutil.py +++ b/conda-store-server/conda_store_server/dbutil.py @@ -1,6 +1,5 @@ import os from contextlib import contextmanager -from subprocess import check_call from tempfile import TemporaryDirectory from sqlalchemy import create_engine, inspect @@ -79,6 +78,8 @@ def upgrade(db_url, revision="head"): with _temp_alembic_ini(db_url) as alembic_ini: + alembic_cfg = Config(alembic_ini) + if ( "alembic_version" not in current_table_names and len(current_table_names) > 0 @@ -87,10 +88,10 @@ def upgrade(db_url, revision="head"): # we stamp the revision at the first one, that introduces the alembic revisions. # I chose the leave the revision number hardcoded as it's not something # dynamic, not something we want to change, and tightly related to the codebase - command.stamp(Config(alembic_ini), "48be4072fe58") + command.stamp(alembic_cfg, "48be4072fe58") # After this point, whatever is in the database, Alembic will # believe it's at the first revision. If there are more upgrades/migrations # to run, they'll be at the next step : # run the upgrade. - check_call(["alembic", "-c", alembic_ini, "upgrade", revision]) + command.upgrade(config=alembic_cfg, revision=revision) From f8f7ce426edd4a9721f91e74e1ccb6a2dc1270d8 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 12 Sep 2023 16:59:30 -0600 Subject: [PATCH 04/50] Use python -m conda_lock --- .../conda_store_server/action/install_lockfile.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/conda-store-server/conda_store_server/action/install_lockfile.py b/conda-store-server/conda_store_server/action/install_lockfile.py index 27d68753d..c63be50d1 100644 --- a/conda-store-server/conda_store_server/action/install_lockfile.py +++ b/conda-store-server/conda_store_server/action/install_lockfile.py @@ -1,3 +1,4 @@ +import sys import typing import pathlib import json @@ -16,7 +17,9 @@ def action_install_lockfile( json.dump(conda_lock_spec, f) command = [ - "conda-lock", + sys.executable, + "-m", + "conda_lock", "install", "--validate-platform", "--log-level", From 8cf9e0bfb520a822c814f885c0eea28c12d294e5 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 15 Sep 2023 14:15:03 -0600 Subject: [PATCH 05/50] Lower the batch size for updating packages from 1000 to 990 1000 hits the sqlite3 max expression depth and results in an error. --- conda-store-server/conda_store_server/orm.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/conda-store-server/conda_store_server/orm.py b/conda-store-server/conda_store_server/orm.py index e224fffb9..60ae11b3b 100644 --- a/conda-store-server/conda_store_server/orm.py +++ b/conda-store-server/conda_store_server/orm.py @@ -519,7 +519,8 @@ def update_packages(self, db, subdirs=None): package_builds[package_key].append(new_package_build_dict) logger.info("CondaPackageBuild objects created") - batch_size = 1000 + # sqlite3 has a max expression depth of 1000 + batch_size = 990 all_package_keys = list(package_builds.keys()) for i in range(0, len(all_package_keys), batch_size): From 76ce34924e80cf7345d1c80381f9154d0a672d49 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 18 Sep 2023 16:55:16 -0600 Subject: [PATCH 06/50] Use a pure Python equivalent of du on Windows --- .../conda_store_server/utils.py | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/conda-store-server/conda_store_server/utils.py b/conda-store-server/conda_store_server/utils.py index f4ce9d90d..b9250e43c 100644 --- a/conda-store-server/conda_store_server/utils.py +++ b/conda-store-server/conda_store_server/utils.py @@ -50,11 +50,48 @@ def chdir(directory: pathlib.Path): os.chdir(current_directory) +def du(path): + """ + Pure Python equivalent of du -sb + + Based on https://stackoverflow.com/a/55648984/161801 + """ + if os.path.islink(path): + return os.lstat(path).st_size + if os.path.isfile(path): + st = os.lstat(path) + return st.st_size + apparent_total_bytes = 0 + have = set() + for dirpath, dirnames, filenames in os.walk(path): + apparent_total_bytes += os.lstat(dirpath).st_size + for f in filenames: + fp = os.path.join(dirpath, f) + if os.path.islink(fp): + apparent_total_bytes += os.lstat(fp).st_size + continue + st = os.lstat(fp) + if st.st_ino in have: + continue + have.add(st.st_ino) + apparent_total_bytes += st.st_size + for d in dirnames: + dp = os.path.join(dirpath, d) + if os.path.islink(dp): + apparent_total_bytes += os.lstat(dp).st_size + + # Round up + n_blocks = (apparent_total_bytes + 511) // 512 + return n_blocks + + def disk_usage(path: pathlib.Path): if sys.platform == "darwin": cmd = ["du", "-sAB1", str(path)] - else: + elif sys.platform == "linux": cmd = ["du", "-sb", str(path)] + else: + return str(du(path)) return subprocess.check_output(cmd, encoding="utf-8").split()[0] From abb13edd1b69ddc8cca23d10d512ee5cbd493f54 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 25 Sep 2023 16:54:09 -0600 Subject: [PATCH 07/50] Fix missing parenthesis --- conda-store-server/conda_store_server/orm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conda-store-server/conda_store_server/orm.py b/conda-store-server/conda_store_server/orm.py index 950c1f461..5a9b11762 100644 --- a/conda-store-server/conda_store_server/orm.py +++ b/conda-store-server/conda_store_server/orm.py @@ -517,7 +517,7 @@ def update_packages(self, db, subdirs=None): batch_size = 990 all_package_keys = list(package_builds.keys()) for i in range(0, len(all_package_keys), batch_size): - logger.info(f"handling subset at index {i} (batch size {batch_size}") + logger.info(f"handling subset at index {i} (batch size {batch_size})") subset_keys = all_package_keys[i : i + batch_size] # retrieve the parent packages for the subset From cf4951e1ab8ab49cedca8fe86df06d1c7d09db8a Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 25 Sep 2023 17:00:26 -0600 Subject: [PATCH 08/50] Fix the worker for Windows This disables --beat on Windows and sets FORKED_BY_MULTIPROCESSING=1, which is required to make the default celery pool work on Windows. --- conda-store-server/conda_store_server/worker/app.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/conda-store-server/conda_store_server/worker/app.py b/conda-store-server/conda_store_server/worker/app.py index 24c7a5470..e51a7df4d 100644 --- a/conda-store-server/conda_store_server/worker/app.py +++ b/conda-store-server/conda_store_server/worker/app.py @@ -71,9 +71,18 @@ def start(self): argv = [ "worker", "--loglevel=INFO", - "--beat", ] + # The default Celery pool requires this on Windows. See + # https://stackoverflow.com/questions/37255548/how-to-run-celery-on-windows + if sys.platform == "win32": + os.environ.setdefault('FORKED_BY_MULTIPROCESSING', '1') + else: + # --beat does not work on Windows + argv += [ + "--beat", + ] + if self.concurrency: argv.append(f"--concurrency={self.concurrency}") From 80d9ea863aa0acbc4f3420449ee97ad1e1f65254 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 25 Sep 2023 17:08:53 -0600 Subject: [PATCH 09/50] Use posixpath to construct URLs os.path.join is incorrect for constructing a URL on Windows because URLs should always use forward slashes regardless of platform. --- conda-store-server/conda_store_server/server/app.py | 7 ++++--- conda-store-server/conda_store_server/storage.py | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/conda-store-server/conda_store_server/server/app.py b/conda-store-server/conda_store_server/server/app.py index a96857b28..9a0a91c90 100644 --- a/conda-store-server/conda_store_server/server/app.py +++ b/conda-store-server/conda_store_server/server/app.py @@ -1,5 +1,6 @@ import logging import os +import posixpath import sys import conda_store_server @@ -198,9 +199,9 @@ def trim_slash(url): app = FastAPI( title="conda-store", version=__version__, - openapi_url=os.path.join(self.url_prefix, "openapi.json"), - docs_url=os.path.join(self.url_prefix, "docs"), - redoc_url=os.path.join(self.url_prefix, "redoc"), + openapi_url=posixpath.join(self.url_prefix, "openapi.json"), + docs_url=posixpath.join(self.url_prefix, "docs"), + redoc_url=posixpath.join(self.url_prefix, "redoc"), contact={ "name": "Quansight", "url": "https://quansight.com", diff --git a/conda-store-server/conda_store_server/storage.py b/conda-store-server/conda_store_server/storage.py index 3d014533f..5f349a676 100644 --- a/conda-store-server/conda_store_server/storage.py +++ b/conda-store-server/conda_store_server/storage.py @@ -1,5 +1,6 @@ import io import os +import posixpath import shutil import minio @@ -223,7 +224,7 @@ def get(self, key): return f.read() def get_url(self, key): - return os.path.join(self.storage_url, key) + return posixpath.join(self.storage_url, key) def delete(self, db, build_id, key): filename = os.path.join(self.storage_path, key) From 877f8167b4251fae48caa814668c9ef74b7f9f18 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 5 Oct 2023 12:59:23 -0600 Subject: [PATCH 10/50] Fix login not working consistently on Windows The cookie length was incorrectly using .seconds instead of .total_seconds(). This was only a problem on Windows because Windows has a ~16 ms precision for timedeltas, meaning the .seconds attribute for the difference was sometimes being set to 0. --- conda-store-server/conda_store_server/server/auth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conda-store-server/conda_store_server/server/auth.py b/conda-store-server/conda_store_server/server/auth.py index ec0e2d0ae..fec9300f7 100644 --- a/conda-store-server/conda_store_server/server/auth.py +++ b/conda-store-server/conda_store_server/server/auth.py @@ -436,7 +436,7 @@ async def post_login_method( samesite="strict", domain=self.cookie_domain, # set cookie to expire at same time as jwt - max_age=(authentication_token.exp - datetime.datetime.utcnow()).seconds, + max_age=int((authentication_token.exp - datetime.datetime.utcnow()).total_seconds()), ) return response From 59c40db4adc9daa76c2578ba9ac9d7a05d8d8908 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 5 Oct 2023 14:04:09 -0600 Subject: [PATCH 11/50] Fix test_action_decorator to work on Windows --- conda-store-server/tests/test_actions.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/conda-store-server/tests/test_actions.py b/conda-store-server/tests/test_actions.py index b6ea72a34..d329e608e 100644 --- a/conda-store-server/tests/test_actions.py +++ b/conda-store-server/tests/test_actions.py @@ -15,20 +15,31 @@ def test_action_decorator(): def test_function(context): print("stdout") print("stderr", file=sys.stderr) - context.run(["echo", "subprocess"]) - context.run("echo subprocess_stdout", shell=True) - context.run("echo subprocess_stderr 1>&2", shell=True) + if sys.platform == "win32": + # echo is not a separate program on Windows + context.run(["cmd", "/c", "echo subprocess"]) + context.run("echo subprocess_stdout", shell=True) + context.run("echo subprocess_stderr >&2", shell=True) + else: + context.run(["echo", "subprocess"]) + context.run("echo subprocess_stdout", shell=True) + context.run("echo subprocess_stderr 1>&2", shell=True) context.log.info("log") return pathlib.Path.cwd() context = test_function() + + if sys.platform == "win32": + newline = "\r\n" + else: + newline = "\n" assert ( context.stdout.getvalue() - == "stdout\nstderr\nsubprocess\nsubprocess_stdout\nsubprocess_stderr\nlog\n" + == f"stdout{newline}stderr{newline}subprocess{newline}subprocess_stdout{newline}subprocess_stderr{newline}log{newline}" ) # test that action direction is not the same as outside function assert context.result != pathlib.Path.cwd() - # test that temportary directory is cleaned up + # test that temporary directory is cleaned up assert not context.result.exists() From e5fb32c8c346757f9679deb967b72b998f3006d6 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 5 Oct 2023 14:07:45 -0600 Subject: [PATCH 12/50] Fix test_action_decorator on Windows --- conda-store-server/tests/test_actions.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/conda-store-server/tests/test_actions.py b/conda-store-server/tests/test_actions.py index d329e608e..aa66ed349 100644 --- a/conda-store-server/tests/test_actions.py +++ b/conda-store-server/tests/test_actions.py @@ -19,7 +19,7 @@ def test_function(context): # echo is not a separate program on Windows context.run(["cmd", "/c", "echo subprocess"]) context.run("echo subprocess_stdout", shell=True) - context.run("echo subprocess_stderr >&2", shell=True) + context.run("echo subprocess_stderr>&2", shell=True) else: context.run(["echo", "subprocess"]) context.run("echo subprocess_stdout", shell=True) @@ -28,14 +28,9 @@ def test_function(context): return pathlib.Path.cwd() context = test_function() - - if sys.platform == "win32": - newline = "\r\n" - else: - newline = "\n" assert ( context.stdout.getvalue() - == f"stdout{newline}stderr{newline}subprocess{newline}subprocess_stdout{newline}subprocess_stderr{newline}log{newline}" + == "stdout\nstderr\nsubprocess\nsubprocess_stdout\nsubprocess_stderr\nlog\n" ) # test that action direction is not the same as outside function assert context.result != pathlib.Path.cwd() From d87ad024aeba189400445f5fa91c982a4bc7e5c5 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 5 Oct 2023 14:09:37 -0600 Subject: [PATCH 13/50] Skip test_set_conda_prefix_permissions on Windows --- conda-store-server/tests/test_actions.py | 1 + 1 file changed, 1 insertion(+) diff --git a/conda-store-server/tests/test_actions.py b/conda-store-server/tests/test_actions.py index aa66ed349..9ec528dc2 100644 --- a/conda-store-server/tests/test_actions.py +++ b/conda-store-server/tests/test_actions.py @@ -161,6 +161,7 @@ def test_remove_conda_prefix(tmp_path, simple_conda_lock): assert not conda_prefix.exists() +@pytest.mark.skipif(sys.platform == "win32", reason="permissions are not supported on Windows") def test_set_conda_prefix_permissions(tmp_path, conda_store, simple_conda_lock): conda_prefix = tmp_path / "test" From d6126425e004be6611fabdea8bc3144d4ded5ac7 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 5 Oct 2023 15:54:12 -0600 Subject: [PATCH 14/50] Fix du() on Mac and Windows to return bytes instead of blocks --- conda-store-server/conda_store_server/utils.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/conda-store-server/conda_store_server/utils.py b/conda-store-server/conda_store_server/utils.py index b9250e43c..add8bcd8b 100644 --- a/conda-store-server/conda_store_server/utils.py +++ b/conda-store-server/conda_store_server/utils.py @@ -80,9 +80,7 @@ def du(path): if os.path.islink(dp): apparent_total_bytes += os.lstat(dp).st_size - # Round up - n_blocks = (apparent_total_bytes + 511) // 512 - return n_blocks + return apparent_total_bytes def disk_usage(path: pathlib.Path): @@ -93,8 +91,11 @@ def disk_usage(path: pathlib.Path): else: return str(du(path)) - return subprocess.check_output(cmd, encoding="utf-8").split()[0] - + output = subprocess.check_output(cmd, encoding="utf-8").split()[0] + if sys.platform == "darwin": + # mac du does not have the -b option to return bytes + output = str(int(output)*512) + return output @contextlib.contextmanager def timer(logger, prefix): From 17d3b3a6aa9bc8d7bf4d0c82de3ff384b9c58b28 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 9 Oct 2023 11:52:54 -0600 Subject: [PATCH 15/50] Add Windows to CI --- .github/workflows/tests.yaml | 11 +++- .../environment-windows-dev.yaml | 56 +++++++++++++++++++ 2 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 conda-store-server/environment-windows-dev.yaml diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 6e30cfb58..fa4189ca6 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -14,8 +14,7 @@ jobs: name: "unit-test conda-store-server" strategy: matrix: - # cannot run on windows due to needing fake-chroot for conda-docker - os: [ubuntu-latest, macos-latest] + os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} defaults: run: @@ -42,6 +41,14 @@ jobs: environment-file: conda-store-server/environment-macos-dev.yaml auto-activate-base: false + - name: Set up Python (Windows) + if: matrix.os == 'windows-latest' + uses: conda-incubator/setup-miniconda@v2 + with: + activate-environment: conda-store-server-dev + environment-file: conda-store-server/environment-windows-dev.yaml + auto-activate-base: false + - name: Linting Checks run: | hatch env run -e dev lint diff --git a/conda-store-server/environment-windows-dev.yaml b/conda-store-server/environment-windows-dev.yaml new file mode 100644 index 000000000..5f8db2786 --- /dev/null +++ b/conda-store-server/environment-windows-dev.yaml @@ -0,0 +1,56 @@ +name: conda-store-server-dev +channels: + - conda-forge + - Microsoft +dependencies: + - python ==3.10 + # conda builds + - conda ==23.5.2 + - python-docker + - conda-pack + - conda-lock >=1.0.5 + - mamba + - conda-package-handling + # web server + - celery + - flower + - redis-py + - sqlalchemy<=1.4.47 + - psycopg2 + - pymysql + - requests + - uvicorn + - fastapi + - pydantic < 2.0 + - pyyaml + - traitlets + - yarl + - pyjwt + - filelock + - itsdangerous + - jinja2 + - python-multipart + - alembic + # artifact storage + - minio + # CLI + - typer + + # dev dependencies + - hatch + - pytest + - pytest-celery + - pytest-mock + - black ==22.3.0 + - flake8 + - ruff + - sphinx + - myst-parser + - sphinx-panels + - sphinx-copybutton + - pydata-sphinx-theme + - playwright + - docker-compose + + - pip: + - pytest-playwright From be633b32a05591916c900509f555d54db56fc423 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 9 Oct 2023 12:31:40 -0600 Subject: [PATCH 16/50] Run pre-commit --- .../conda_store_server/action/install_lockfile.py | 2 +- conda-store-server/conda_store_server/app.py | 2 +- conda-store-server/conda_store_server/dbutil.py | 1 - conda-store-server/conda_store_server/schema.py | 1 + conda-store-server/conda_store_server/server/auth.py | 4 +++- conda-store-server/conda_store_server/utils.py | 3 ++- conda-store-server/conda_store_server/worker/app.py | 2 +- conda-store-server/hatch_build.py | 6 ++++-- 8 files changed, 13 insertions(+), 8 deletions(-) diff --git a/conda-store-server/conda_store_server/action/install_lockfile.py b/conda-store-server/conda_store_server/action/install_lockfile.py index f8b349968..23d7d5c94 100644 --- a/conda-store-server/conda_store_server/action/install_lockfile.py +++ b/conda-store-server/conda_store_server/action/install_lockfile.py @@ -1,6 +1,6 @@ -import sys import json import pathlib +import sys import typing from conda_store_server import action diff --git a/conda-store-server/conda_store_server/app.py b/conda-store-server/conda_store_server/app.py index 845b5e8b8..62b88b243 100644 --- a/conda-store-server/conda_store_server/app.py +++ b/conda-store-server/conda_store_server/app.py @@ -31,9 +31,9 @@ ) from traitlets.config import LoggingConfigurable - ON_WIN = sys.platform.startswith("win") + def conda_store_validate_specification( db: Session, conda_store: "CondaStore", diff --git a/conda-store-server/conda_store_server/dbutil.py b/conda-store-server/conda_store_server/dbutil.py index 23d6dbfdc..b76b28ba6 100644 --- a/conda-store-server/conda_store_server/dbutil.py +++ b/conda-store-server/conda_store_server/dbutil.py @@ -77,7 +77,6 @@ def upgrade(db_url, revision="head"): current_table_names = set(inspect(engine).get_table_names()) with _temp_alembic_ini(db_url) as alembic_ini: - alembic_cfg = Config(alembic_ini) if ( diff --git a/conda-store-server/conda_store_server/schema.py b/conda-store-server/conda_store_server/schema.py index ab0262bb9..66948d546 100644 --- a/conda-store-server/conda_store_server/schema.py +++ b/conda-store-server/conda_store_server/schema.py @@ -11,6 +11,7 @@ ON_WIN = sys.platform.startswith("win") + def _datetime_factory(offset: datetime.timedelta): """utcnow datetime + timezone as string""" return datetime.datetime.utcnow() + offset diff --git a/conda-store-server/conda_store_server/server/auth.py b/conda-store-server/conda_store_server/server/auth.py index fec9300f7..86a072b74 100644 --- a/conda-store-server/conda_store_server/server/auth.py +++ b/conda-store-server/conda_store_server/server/auth.py @@ -436,7 +436,9 @@ async def post_login_method( samesite="strict", domain=self.cookie_domain, # set cookie to expire at same time as jwt - max_age=int((authentication_token.exp - datetime.datetime.utcnow()).total_seconds()), + max_age=int( + (authentication_token.exp - datetime.datetime.utcnow()).total_seconds() + ), ) return response diff --git a/conda-store-server/conda_store_server/utils.py b/conda-store-server/conda_store_server/utils.py index add8bcd8b..0bbac9226 100644 --- a/conda-store-server/conda_store_server/utils.py +++ b/conda-store-server/conda_store_server/utils.py @@ -94,9 +94,10 @@ def disk_usage(path: pathlib.Path): output = subprocess.check_output(cmd, encoding="utf-8").split()[0] if sys.platform == "darwin": # mac du does not have the -b option to return bytes - output = str(int(output)*512) + output = str(int(output) * 512) return output + @contextlib.contextmanager def timer(logger, prefix): start_time = time.time() diff --git a/conda-store-server/conda_store_server/worker/app.py b/conda-store-server/conda_store_server/worker/app.py index e51a7df4d..b3278a395 100644 --- a/conda-store-server/conda_store_server/worker/app.py +++ b/conda-store-server/conda_store_server/worker/app.py @@ -76,7 +76,7 @@ def start(self): # The default Celery pool requires this on Windows. See # https://stackoverflow.com/questions/37255548/how-to-run-celery-on-windows if sys.platform == "win32": - os.environ.setdefault('FORKED_BY_MULTIPROCESSING', '1') + os.environ.setdefault("FORKED_BY_MULTIPROCESSING", "1") else: # --beat does not work on Windows argv += [ diff --git a/conda-store-server/hatch_build.py b/conda-store-server/hatch_build.py index c071288ec..02bcb9965 100644 --- a/conda-store-server/hatch_build.py +++ b/conda-store-server/hatch_build.py @@ -61,10 +61,12 @@ def initialize(self, version: str, build_data: Dict[str, Any]) -> None: # main.js to enable easy configuration see # conda_store_server/server/templates/conda-store-ui.html # for global variable set - with (source_directory / "main.js").open("r", encoding='utf-8') as source_f: + with (source_directory / "main.js").open("r", encoding="utf-8") as source_f: content = source_f.read() content = re.sub( '"MISSING_ENV_VAR"', "GLOBAL_CONDA_STORE_STATE", content ) - with (destination_directory / "main.js").open("w", encoding='utf-8') as dest_f: + with (destination_directory / "main.js").open( + "w", encoding="utf-8" + ) as dest_f: dest_f.write(content) From d75397612a1aa80e15636c5507bc22598db26825 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 9 Oct 2023 12:50:25 -0600 Subject: [PATCH 17/50] Try using python -m on CI --- .github/workflows/tests.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index fa4189ca6..bb2ad4207 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -51,15 +51,15 @@ jobs: - name: Linting Checks run: | - hatch env run -e dev lint + python -m hatch env run -e dev lint - name: Release Check run: | - hatch build + python -m hatch build - name: Unit Tests run: | - pytest tests + python -m pytest tests integration-test-conda-store-server: name: "integration-test conda-store-server" From a06679cf30790854a232091cb53b52b84471061c Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 9 Oct 2023 15:28:52 -0600 Subject: [PATCH 18/50] Revert "Try using python -m on CI" This reverts commit d75397612a1aa80e15636c5507bc22598db26825. --- .github/workflows/tests.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index bb2ad4207..fa4189ca6 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -51,15 +51,15 @@ jobs: - name: Linting Checks run: | - python -m hatch env run -e dev lint + hatch env run -e dev lint - name: Release Check run: | - python -m hatch build + hatch build - name: Unit Tests run: | - python -m pytest tests + pytest tests integration-test-conda-store-server: name: "integration-test conda-store-server" From e639e224092b8c22512616a107f8de914074cbcc Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 9 Oct 2023 15:29:07 -0600 Subject: [PATCH 19/50] Try activating environment base on Windows --- .github/workflows/tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index fa4189ca6..1a07168f6 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -47,7 +47,7 @@ jobs: with: activate-environment: conda-store-server-dev environment-file: conda-store-server/environment-windows-dev.yaml - auto-activate-base: false + auto-activate-base: true - name: Linting Checks run: | From 00f193a84617436078b5feb9e8549920f7baac9e Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 9 Oct 2023 16:23:15 -0600 Subject: [PATCH 20/50] Remove mamba from linux CI too --- .github/workflows/tests.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 1a07168f6..d8f1293fa 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -28,7 +28,6 @@ jobs: if: matrix.os == 'ubuntu-latest' uses: conda-incubator/setup-miniconda@v2 with: - mamba-version: "*" activate-environment: conda-store-server-dev environment-file: conda-store-server/environment-dev.yaml auto-activate-base: false From 36c99d372d8648aa29244cd035ba5cea45c2f4fe Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 10 Oct 2023 11:48:19 -0600 Subject: [PATCH 21/50] Try using channel-priority: strict --- .github/workflows/tests.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index d8f1293fa..81fefa446 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -46,7 +46,8 @@ jobs: with: activate-environment: conda-store-server-dev environment-file: conda-store-server/environment-windows-dev.yaml - auto-activate-base: true + auto-activate-base: false + channel-priority: strict - name: Linting Checks run: | From b7b0cc619a649afc5105098aaadc27299ef8c8e2 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 10 Oct 2023 12:06:28 -0600 Subject: [PATCH 22/50] CI test --- .github/workflows/tests.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 81fefa446..5f3a6d1e8 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -47,7 +47,11 @@ jobs: activate-environment: conda-store-server-dev environment-file: conda-store-server/environment-windows-dev.yaml auto-activate-base: false - channel-priority: strict + + - name: Env test + run: | + python -c 'import sys;print(sys.executable)' + python -c 'import ctypes' - name: Linting Checks run: | From 07dc9e8fff32784ac4d4e2c6507475a0ee1bf659 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 10 Oct 2023 12:08:56 -0600 Subject: [PATCH 23/50] Trigger CI From dfc3f5bb67d6a88f6aca0906a7fb402ed8ea1a60 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 10 Oct 2023 13:01:57 -0600 Subject: [PATCH 24/50] Try using only conda-forge on Windows --- .github/workflows/tests.yaml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 5f3a6d1e8..d19bbf3f3 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -47,11 +47,7 @@ jobs: activate-environment: conda-store-server-dev environment-file: conda-store-server/environment-windows-dev.yaml auto-activate-base: false - - - name: Env test - run: | - python -c 'import sys;print(sys.executable)' - python -c 'import ctypes' + channels: conda-forge - name: Linting Checks run: | From dab4a66fb1a75786bbd30857dbf2e4b66226e2a4 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 10 Oct 2023 14:07:13 -0600 Subject: [PATCH 25/50] Try using miniforge --- .github/workflows/tests.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index d19bbf3f3..7d5c61fb5 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -48,6 +48,7 @@ jobs: environment-file: conda-store-server/environment-windows-dev.yaml auto-activate-base: false channels: conda-forge + miniforge-version: latest - name: Linting Checks run: | From c036b27c66c3d1b33204f0a96fb60e35b3b8bb75 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 10 Oct 2023 14:17:42 -0600 Subject: [PATCH 26/50] Set conda-forge as the only channel in ~/.condarc --- .github/workflows/tests.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 7d5c61fb5..3dac93b0b 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -24,6 +24,10 @@ jobs: - name: "Checkout Repository" uses: actions/checkout@v4 + - name: "Create ~/.condarc" + run: | + cat "channels: ['conda-forge']" > ~/.condarc + - name: Set up Python (Linux) if: matrix.os == 'ubuntu-latest' uses: conda-incubator/setup-miniconda@v2 From 0c65c05cd4faf3c23634c3d4d9c01e33c3fee35f Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 10 Oct 2023 14:18:34 -0600 Subject: [PATCH 27/50] Fix command --- .github/workflows/tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 3dac93b0b..b0931dad4 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -26,7 +26,7 @@ jobs: - name: "Create ~/.condarc" run: | - cat "channels: ['conda-forge']" > ~/.condarc + echo "channels: ['conda-forge']" > ~/.condarc - name: Set up Python (Linux) if: matrix.os == 'ubuntu-latest' From 780753ad4ba51267cd81fd0c4bd6707e4b21d2e8 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 10 Oct 2023 14:25:48 -0600 Subject: [PATCH 28/50] Fix shell command --- .github/workflows/tests.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index b0931dad4..79347307a 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -25,8 +25,10 @@ jobs: uses: actions/checkout@v4 - name: "Create ~/.condarc" + if: matrix.os == 'windows-latest' + shell: powershell run: | - echo "channels: ['conda-forge']" > ~/.condarc + echo "channels: ['conda-forge']" > C:\Users\runneradmin\.condarc - name: Set up Python (Linux) if: matrix.os == 'ubuntu-latest' From 98ba0e93265eab70df9ef7a5ee6aec0d9c477d5a Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 10 Oct 2023 14:31:05 -0600 Subject: [PATCH 29/50] Try force reinstalling Python on Windows --- .github/workflows/tests.yaml | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 79347307a..e24f95334 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -24,12 +24,6 @@ jobs: - name: "Checkout Repository" uses: actions/checkout@v4 - - name: "Create ~/.condarc" - if: matrix.os == 'windows-latest' - shell: powershell - run: | - echo "channels: ['conda-forge']" > C:\Users\runneradmin\.condarc - - name: Set up Python (Linux) if: matrix.os == 'ubuntu-latest' uses: conda-incubator/setup-miniconda@v2 @@ -56,6 +50,15 @@ jobs: channels: conda-forge miniforge-version: latest + - name: Reinstall Python 3.10 on Windows runner + uses: nick-fields/retry@v2.8.3 + with: + timeout_minutes: 9999 + max_attempts: 6 + command: + conda install --channel=conda-forge --quiet --yes python=${{ matrix.python }} + if: matrix.os == 'windows-latest' + - name: Linting Checks run: | hatch env run -e dev lint From 52f25c7492ee180c1f1d5c295d92161b1d6a632c Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 10 Oct 2023 14:32:17 -0600 Subject: [PATCH 30/50] Use the correct environment --- .github/workflows/tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index e24f95334..a081f75ae 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -56,7 +56,7 @@ jobs: timeout_minutes: 9999 max_attempts: 6 command: - conda install --channel=conda-forge --quiet --yes python=${{ matrix.python }} + conda install -n conda-store-server-dev --force-reinstall --override-channels --channel=conda-forge --quiet --yes python=${{ matrix.python }} if: matrix.os == 'windows-latest' - name: Linting Checks From b492c2755df4d62fb50231b5cd7b83b8af984aca Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 10 Oct 2023 14:46:12 -0600 Subject: [PATCH 31/50] Revert "Use the correct environment" This reverts commit 52f25c7492ee180c1f1d5c295d92161b1d6a632c. These extra bits are not needed apparently. --- .github/workflows/tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index a081f75ae..e24f95334 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -56,7 +56,7 @@ jobs: timeout_minutes: 9999 max_attempts: 6 command: - conda install -n conda-store-server-dev --force-reinstall --override-channels --channel=conda-forge --quiet --yes python=${{ matrix.python }} + conda install --channel=conda-forge --quiet --yes python=${{ matrix.python }} if: matrix.os == 'windows-latest' - name: Linting Checks From 26bc0946c700ba09a783d8a76c1da455bff1be53 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 10 Oct 2023 14:47:03 -0600 Subject: [PATCH 32/50] Add a comment --- .github/workflows/tests.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index e24f95334..13385d2d6 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -50,6 +50,7 @@ jobs: channels: conda-forge miniforge-version: latest + # This fixes a "DLL not found" issue importing ctypes from the hatch env - name: Reinstall Python 3.10 on Windows runner uses: nick-fields/retry@v2.8.3 with: From 58c25ec012fb2110a19f59b69d4ca5d5b32bfa29 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 10 Oct 2023 14:47:16 -0600 Subject: [PATCH 33/50] Try removing extra setup-miniconda config --- .github/workflows/tests.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 13385d2d6..694d17879 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -47,8 +47,6 @@ jobs: activate-environment: conda-store-server-dev environment-file: conda-store-server/environment-windows-dev.yaml auto-activate-base: false - channels: conda-forge - miniforge-version: latest # This fixes a "DLL not found" issue importing ctypes from the hatch env - name: Reinstall Python 3.10 on Windows runner From cd66987e53b35cf777c64a5a3cc07e22fc278751 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 10 Oct 2023 15:00:39 -0600 Subject: [PATCH 34/50] Run tests in verbose mode --- .github/workflows/tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 694d17879..6c96d2c6a 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -68,7 +68,7 @@ jobs: - name: Unit Tests run: | - pytest tests + pytest -vvv tests integration-test-conda-store-server: name: "integration-test conda-store-server" From 1afb0fc0f5abfa9931a856098bfb61d5adc00df8 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 10 Oct 2023 15:42:09 -0600 Subject: [PATCH 35/50] Add an option to not redirect stderr in context.run This is needed for uses with --json, because otherwise any stderr breaks the json. --- conda-store-server/conda_store_server/action/base.py | 9 ++++++--- conda-store-server/tests/test_actions.py | 3 +++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/conda-store-server/conda_store_server/action/base.py b/conda-store-server/conda_store_server/action/base.py index 43e0991a9..49e6e8e3b 100644 --- a/conda-store-server/conda_store_server/action/base.py +++ b/conda-store-server/conda_store_server/action/base.py @@ -18,7 +18,7 @@ def wrapper(*args, **kwargs): # redirect stdout -> action_context.stdout stack.enter_context(contextlib.redirect_stdout(action_context.stdout)) - # redirect stderr -> action_context.stdout + # redirect stderr -> action_context.stderr stack.enter_context(contextlib.redirect_stderr(action_context.stdout)) # create a temporary directory @@ -38,6 +38,7 @@ class ActionContext: def __init__(self): self.id = str(uuid.uuid4()) self.stdout = io.StringIO() + self.stderr = io.StringIO() self.log = logging.getLogger(f"conda_store_server.action.{self.id}") self.log.propagate = False self.log.addHandler(logging.StreamHandler(stream=self.stdout)) @@ -45,13 +46,15 @@ def __init__(self): self.result = None self.artifacts = {} - def run(self, *args, **kwargs): + def run(self, *args, redirect_stderr=True, **kwargs): result = subprocess.run( *args, **kwargs, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, + stderr=subprocess.STDOUT if redirect_stderr else subprocess.PIPE, encoding="utf-8", ) self.stdout.write(result.stdout) + if not redirect_stderr: + self.stderr.write(result.stderr) return result diff --git a/conda-store-server/tests/test_actions.py b/conda-store-server/tests/test_actions.py index 9ec528dc2..f795a61f6 100644 --- a/conda-store-server/tests/test_actions.py +++ b/conda-store-server/tests/test_actions.py @@ -20,10 +20,12 @@ def test_function(context): context.run(["cmd", "/c", "echo subprocess"]) context.run("echo subprocess_stdout", shell=True) context.run("echo subprocess_stderr>&2", shell=True) + context.run("echo subprocess_stderr_no_redirect>&2", shell=True, redirect_stderr=False) else: context.run(["echo", "subprocess"]) context.run("echo subprocess_stdout", shell=True) context.run("echo subprocess_stderr 1>&2", shell=True) + context.run("echo subprocess_stderr_no_redirect 1>&2", shell=True, redirect_stderr=False) context.log.info("log") return pathlib.Path.cwd() @@ -32,6 +34,7 @@ def test_function(context): context.stdout.getvalue() == "stdout\nstderr\nsubprocess\nsubprocess_stdout\nsubprocess_stderr\nlog\n" ) + assert context.stderr.getvalue() == "subprocess_stderr_no_redirect\n" # test that action direction is not the same as outside function assert context.result != pathlib.Path.cwd() # test that temporary directory is cleaned up From 5d647ead5745c21c05a5c4b7fce46780159e2e56 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 10 Oct 2023 15:45:02 -0600 Subject: [PATCH 36/50] Don't capture stderr with conda env export --json --- .../conda_store_server/action/generate_conda_export.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/conda-store-server/conda_store_server/action/generate_conda_export.py b/conda-store-server/conda_store_server/action/generate_conda_export.py index 9cdedfeb8..cbcc0fe04 100644 --- a/conda-store-server/conda_store_server/action/generate_conda_export.py +++ b/conda-store-server/conda_store_server/action/generate_conda_export.py @@ -17,5 +17,7 @@ def action_generate_conda_export( "--json", ] - result = context.run(command, check=True) + result = context.run(command, check=True, redirect_stderr=False) + if result.stderr: + context.log.warning(f"conda env export stderr: {result.stderr}") return json.loads(result.stdout) From c20d612a5f32ef5806271790081dac0788f346c6 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 10 Oct 2023 16:11:04 -0600 Subject: [PATCH 37/50] Disable mamba in the integration tests too --- .github/workflows/tests.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 6c96d2c6a..6f3a51c1b 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -84,7 +84,6 @@ jobs: - name: Set up Python uses: conda-incubator/setup-miniconda@v2 with: - mamba-version: "*" activate-environment: conda-store-server-dev environment-file: conda-store-server/environment-dev.yaml auto-activate-base: false From 2d7c953c3004e1bafbf46e13ee2591aa223601c7 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 11 Oct 2023 14:34:11 -0600 Subject: [PATCH 38/50] Print conda-store server address to the terminal when using --standalone --- conda-store-server/conda_store_server/server/app.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/conda-store-server/conda_store_server/server/app.py b/conda-store-server/conda_store_server/server/app.py index 9a0a91c90..b057416ec 100644 --- a/conda-store-server/conda_store_server/server/app.py +++ b/conda-store-server/conda_store_server/server/app.py @@ -336,6 +336,8 @@ def start(self): # start worker if in standalone mode if self.standalone: + address = '127.0.0.1' if self.address == '0.0.0.0' else self.address + print(f"Starting standalone conda-store server at http://{address}:{self.port}") import multiprocessing multiprocessing.set_start_method("spawn") From d759fe90fe19b4caa97bd3325018ab51adfa1291 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 11 Oct 2023 14:35:46 -0600 Subject: [PATCH 39/50] Use "localhost" instead of "127.0.0.1" for consistency with the docs --- conda-store-server/conda_store_server/server/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conda-store-server/conda_store_server/server/app.py b/conda-store-server/conda_store_server/server/app.py index b057416ec..9806eeab6 100644 --- a/conda-store-server/conda_store_server/server/app.py +++ b/conda-store-server/conda_store_server/server/app.py @@ -336,7 +336,7 @@ def start(self): # start worker if in standalone mode if self.standalone: - address = '127.0.0.1' if self.address == '0.0.0.0' else self.address + address = 'localhost' if self.address == '0.0.0.0' else self.address print(f"Starting standalone conda-store server at http://{address}:{self.port}") import multiprocessing From 871f2196f39bd05189057ec2abe19d362279bd5f Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 11 Oct 2023 14:48:43 -0600 Subject: [PATCH 40/50] Add a note to the docs that Docker image creation only works on Linux --- docs/user_guide.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/user_guide.md b/docs/user_guide.md index 8e6d728de..20c5c428a 100644 --- a/docs/user_guide.md +++ b/docs/user_guide.md @@ -73,6 +73,10 @@ conda-unpack ### Docker Registry +```{note} +Docker image creation is currently only supported on Linux. +``` + conda-store acts as a docker registry which allows for interesting ways to handle Conda environment. In addition this registry leverages [conda-docker](https://github.com/conda-incubator/conda-docker) which From 5a3ef3a2a4430d8d353f398093a4d7b0100031fa Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 11 Oct 2023 14:52:48 -0600 Subject: [PATCH 41/50] Document that filesystem permissions options aren't supported on Windows --- docs/administration.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/administration.md b/docs/administration.md index a9356e2b6..b806ca055 100644 --- a/docs/administration.md +++ b/docs/administration.md @@ -217,17 +217,20 @@ filesystem. `CondaStore.default_uid` is the uid (user id) to assign to all files and directories in a given built environment. This setting is useful if you want to protect environments from modification from -certain users and groups. +certain users and groups. Note: this configuration option is not +supported on Windows. `CondaStore.default_gid` is the gid (group id) to assign to all files and directories in a given built environment. This setting is useful if you want to protect environments from modification from -certain users and groups. +certain users and groups. Note: this configuration option is not +supported on Windows. `CondaStore.default_permissions` is the filesystem permissions to assign to all files and directories in a given built environment. This setting is useful if you want to protect environments from -modification from certain users and groups. +modification from certain users and groups. Note: this configuration +option is not supported on Windows. `CondaStore.default_docker_base_image` default base image used for the Dockerized environments. Make sure to have a proper glibc within image From e083cc8d4be92266c9ac3754cd1e06cc9b80d92a Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 11 Oct 2023 14:56:24 -0600 Subject: [PATCH 42/50] Fix a formatting issue in the docs --- docs/contributing.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/contributing.md b/docs/contributing.md index 613a2daee..2230ef610 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -71,13 +71,13 @@ conda activate conda-store-server-dev ``` Running `conda-store`. `--standalone` mode launched celery as a -subprocess of the web server. +subprocess of the web server. Run +``` python -m conda_store_server.server --standalone tests/assets/conda_store_standalone_config.py +``` -```` - -Visit [localhost:5000](http://localhost:5000/) +Then visit [localhost:5000](http://localhost:5000/). ### Changes to API From 4918a08738b4db85f8a6436499525c7aad27a080 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 11 Oct 2023 14:56:33 -0600 Subject: [PATCH 43/50] Don't document a config file for using --standalone --- docs/contributing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contributing.md b/docs/contributing.md index 2230ef610..d179df76c 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -74,7 +74,7 @@ Running `conda-store`. `--standalone` mode launched celery as a subprocess of the web server. Run ``` -python -m conda_store_server.server --standalone tests/assets/conda_store_standalone_config.py +python -m conda_store_server.server --standalone ``` Then visit [localhost:5000](http://localhost:5000/). From 9bb431701e96aac237b64a0b217aa6599dfebc49 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 11 Oct 2023 15:58:23 -0600 Subject: [PATCH 44/50] Add FAQ entry for long paths on Windows --- docs/administration.md | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/docs/administration.md b/docs/administration.md index b806ca055..0a0d37429 100644 --- a/docs/administration.md +++ b/docs/administration.md @@ -20,7 +20,7 @@ seen. When conda-store builds a given environment it has to locally install the environment in the directory specified in the [Traitlets](https://traitlets.readthedocs.io/en/stable/using_traitlets.html) -configuration `CondaStore.store_directroy`. Conda environments consist +configuration `CondaStore.store_directory`. Conda environments consist of many hardlinks to small files. This means that the `store_directory` is limited to the number of [IOPS](https://en.wikipedia.org/wiki/IOPS) the directory can @@ -545,9 +545,36 @@ in the BUILDING state and are not currently running on the workers. This feature only works for certain brokers e.g. redis. Database celery brokers are not supported. -This issue occurs when the worker spontaineously dies. This can happen +This issue occurs when the worker spontaneously dies. This can happen for several reasons: - worker is killed due to consuming too much memory (conda solver/builds can consume a lot of memory) - worker was killed for other reasons e.g. forced restart - bugs in conda-store + +### Long paths on Windows + +conda-store supports Windows in standalone mode. However, when creating +environments with certain packages, you may see errors like + +``` +ERROR:root:[WinError 206] The filename or extension is too long: 'C:\\...' +``` + +This error is due to the fact that Windows has a limitation that file paths +cannot be more than 260 characters. The fix is to set the registry key +`Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem\LongPathsEnabled +(Type: REG_DWORD)` to `1`, which removes this MAX_PATH limitation. See [this +Microsoft support +article](https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation) +for more details on how to set this registry key. + +If it is not possible to set this registry key, for instance, because you do +not have access to administrator privileges, you should configure the +conda-store `CondaStore.store_directory` to be as close to the filesystem root +as possible, so that the total length of the paths of package files is +minimized. + +See [conda-store issue +#588](https://github.com/conda-incubator/conda-store/issues/588) for more +details. From d47c58862928bf560102056100809c878a12f111 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 11 Oct 2023 16:18:49 -0600 Subject: [PATCH 45/50] Add a basic test for disk_usage()/du() --- conda-store-server/tests/test_usage.py | 27 ++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 conda-store-server/tests/test_usage.py diff --git a/conda-store-server/tests/test_usage.py b/conda-store-server/tests/test_usage.py new file mode 100644 index 000000000..d07030086 --- /dev/null +++ b/conda-store-server/tests/test_usage.py @@ -0,0 +1,27 @@ +from conda_store_server.utils import disk_usage, du + +# TODO: Add tests for the other functions in utils.py + +def test_disk_usage(tmp_path): + test_dir = tmp_path / "test_dir" + test_dir.mkdir() + test_file = test_dir / "test_file" + test_file.write_text("a"*1000) + test_file2 = test_dir / "test_file2" + test_file2.write_text("b"*1000) + # Test hard links + test_file_hardlink = test_dir / "test_file_hardlink" + test_file_hardlink.hardlink_to(test_file) + + # The exact values depend on the block size and other details. Just check + # that it is in the right range. Note that disk_usage uses the du command + # on Mac and Linux but uses the Python du() function on Windows. + val = disk_usage(test_file) + assert isinstance(val, str) + assert 1000 <= int(val) <= 1500 + assert 1000 <= du(test_file) <= 1500 + + val = disk_usage(test_dir) + assert isinstance(val, str) + assert 2000 <= int(val) <= 2700 + assert 2000 <= du(test_dir) <= 2700 From d863809612380af0633f876268944609b66288ef Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 11 Oct 2023 16:20:10 -0600 Subject: [PATCH 46/50] Run black --- conda-store-server/conda_store_server/server/app.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/conda-store-server/conda_store_server/server/app.py b/conda-store-server/conda_store_server/server/app.py index 9806eeab6..146d2f68b 100644 --- a/conda-store-server/conda_store_server/server/app.py +++ b/conda-store-server/conda_store_server/server/app.py @@ -336,8 +336,10 @@ def start(self): # start worker if in standalone mode if self.standalone: - address = 'localhost' if self.address == '0.0.0.0' else self.address - print(f"Starting standalone conda-store server at http://{address}:{self.port}") + address = "localhost" if self.address == "0.0.0.0" else self.address + print( + f"Starting standalone conda-store server at http://{address}:{self.port}" + ) import multiprocessing multiprocessing.set_start_method("spawn") From d993d43d3264f05258ba889b3758af1e5ee2e1ef Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 11 Oct 2023 16:21:55 -0600 Subject: [PATCH 47/50] Document that there are different environment files for Mac and Windows --- docs/contributing.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/contributing.md b/docs/contributing.md index d179df76c..9c798fe71 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -66,6 +66,8 @@ Install the following dependencies before developing on conda-store. Install the development dependencies and activate the environment. ```shell +# replace this with environment-dev-mac.yaml or environment-dev-windows.yaml +# if you are on Mac or Windows conda env create -f conda-store-server/environment-dev.yaml conda activate conda-store-server-dev ``` @@ -99,6 +101,8 @@ To build the documentation install the development environment via Conda. ```shell +# replace this with environment-dev-mac.yaml or environment-dev-windows.yaml +# if you are on Mac or Windows conda env create -f conda-store-server/environment-dev.yaml conda activate conda-store-server-dev ```` From 99ec7b204dadab0093d53c43b2223c8b71fb7748 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 11 Oct 2023 16:22:32 -0600 Subject: [PATCH 48/50] Fix filenames --- docs/contributing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/contributing.md b/docs/contributing.md index 9c798fe71..193a18818 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -66,7 +66,7 @@ Install the following dependencies before developing on conda-store. Install the development dependencies and activate the environment. ```shell -# replace this with environment-dev-mac.yaml or environment-dev-windows.yaml +# replace this with environment-mac-dev.yaml or environment-windows-dev.yaml # if you are on Mac or Windows conda env create -f conda-store-server/environment-dev.yaml conda activate conda-store-server-dev @@ -101,7 +101,7 @@ To build the documentation install the development environment via Conda. ```shell -# replace this with environment-dev-mac.yaml or environment-dev-windows.yaml +# replace this with environment-mac-dev.yaml or environment-windows-dev.yaml # if you are on Mac or Windows conda env create -f conda-store-server/environment-dev.yaml conda activate conda-store-server-dev From eb31b84ed21e497ee87a542941d92774760fe093 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 11 Oct 2023 16:22:54 -0600 Subject: [PATCH 49/50] Fix filename --- docs/contributing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/contributing.md b/docs/contributing.md index 193a18818..36e819c8d 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -66,7 +66,7 @@ Install the following dependencies before developing on conda-store. Install the development dependencies and activate the environment. ```shell -# replace this with environment-mac-dev.yaml or environment-windows-dev.yaml +# replace this with environment-macos-dev.yaml or environment-windows-dev.yaml # if you are on Mac or Windows conda env create -f conda-store-server/environment-dev.yaml conda activate conda-store-server-dev @@ -101,7 +101,7 @@ To build the documentation install the development environment via Conda. ```shell -# replace this with environment-mac-dev.yaml or environment-windows-dev.yaml +# replace this with environment-macos-dev.yaml or environment-windows-dev.yaml # if you are on Mac or Windows conda env create -f conda-store-server/environment-dev.yaml conda activate conda-store-server-dev From 2216f47c5839c52d0116e6c7fd5987e013b27100 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 12 Oct 2023 11:10:42 -0600 Subject: [PATCH 50/50] Account for the size of the directory itself (which is large on Linux) --- conda-store-server/tests/test_usage.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/conda-store-server/tests/test_usage.py b/conda-store-server/tests/test_usage.py index d07030086..fcf3990a8 100644 --- a/conda-store-server/tests/test_usage.py +++ b/conda-store-server/tests/test_usage.py @@ -5,6 +5,11 @@ def test_disk_usage(tmp_path): test_dir = tmp_path / "test_dir" test_dir.mkdir() + + # This varies across OSes + dir_size = du(test_dir) + assert abs(dir_size - int(disk_usage(test_dir))) <= 1000 + test_file = test_dir / "test_file" test_file.write_text("a"*1000) test_file2 = test_dir / "test_file2" @@ -23,5 +28,5 @@ def test_disk_usage(tmp_path): val = disk_usage(test_dir) assert isinstance(val, str) - assert 2000 <= int(val) <= 2700 - assert 2000 <= du(test_dir) <= 2700 + assert 2000 + dir_size <= int(val) <= 2700 + dir_size + assert 2000 + dir_size <= du(test_dir) <= 2700 + dir_size