-
Notifications
You must be signed in to change notification settings - Fork 52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Windows support #559
Windows support #559
Changes from all commits
b4e70b6
1719638
322afb5
f8f7ce4
8cf9e0b
8eb15dd
76ce349
1f095cb
abb13ed
cf4951e
80d9ea8
877f816
59c40db
e5fb32c
d87ad02
d612642
c08690b
17d3b3a
be633b3
d753976
a06679c
e639e22
00f193a
36c99d3
b7b0cc6
07dc9e8
dfc3f5b
dab4a66
c036b27
0c65c05
780753a
98ba0e9
52f25c7
b492c27
26bc094
58c25ec
cd66987
1afb0fc
5d647ea
c20d612
2d7c953
d759fe9
871f219
5a3ef3a
e083cc8
4918a08
9bb4317
d47c588
d863809
d993d43
99ec7b2
eb31b84
2216f47
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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: | ||
|
@@ -29,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 | ||
|
@@ -42,6 +40,24 @@ 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 | ||
|
||
# This fixes a "DLL not found" issue importing ctypes from the hatch env | ||
- name: Reinstall Python 3.10 on Windows runner | ||
uses: nick-fields/[email protected] | ||
with: | ||
timeout_minutes: 9999 | ||
nkaretnikov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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 | ||
|
@@ -52,7 +68,7 @@ jobs: | |
|
||
- name: Unit Tests | ||
run: | | ||
pytest tests | ||
pytest -vvv tests | ||
nkaretnikov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
integration-test-conda-store-server: | ||
name: "integration-test conda-store-server" | ||
|
@@ -68,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 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The line below uses |
||
stack.enter_context(contextlib.redirect_stderr(action_context.stdout)) | ||
|
||
# create a temporary directory | ||
|
@@ -38,20 +38,23 @@ 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)) | ||
self.log.setLevel(logging.INFO) | ||
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) | ||
nkaretnikov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return result |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,8 @@ | |
from conda_store_server import conda_utils, utils | ||
from pydantic import BaseModel, Field, ValidationError, constr, validator | ||
|
||
ON_WIN = sys.platform.startswith("win") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is already declared in another file. Can we move that definition somewhere where it makes the most sense and import everywhere? Ideally, this should be done in a way that minimizes potential import cycles. |
||
|
||
|
||
def _datetime_factory(offset: datetime.timedelta): | ||
"""utcnow datetime + timezone as string""" | ||
|
@@ -194,20 +196,20 @@ class Settings(BaseModel): | |
metadata={"global": True}, | ||
) | ||
|
||
default_uid: int = Field( | ||
os.getuid(), | ||
default_uid: int | None = Field( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This syntax is Python 3.10+ IIUC. Let's use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually this should be fine, but we need to add There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know what the minimal version is. The point I was trying to make: we don't use it anywhere else. I don't see why we should introduce this and immediately bump our lowest supported version to 3.10+ or require an extra import. Let's just use Optional, like everywhere else. |
||
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}, | ||
) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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", | ||
|
@@ -335,6 +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}" | ||
) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't like this. If we're using |
||
import multiprocessing | ||
|
||
multiprocessing.set_start_method("spawn") | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you remember what failed without this? The effect of this change will be converting Windows paths to posixpaths: >>> import posixpath
>>>
>>> posixpath.join("foo", "bar")
'foo/bar'
>>>
>>> import os
>>> os.path.join("foo","bar")
'foo\\bar' There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Windows backslash paths are not correct for file URLs. Without this change, some of the buttons in the UI don't work because they use |
||
|
||
def delete(self, db, build_id, key): | ||
filename = os.path.join(self.storage_path, key) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -50,13 +50,52 @@ 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 | ||
|
||
return apparent_total_bytes | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have you seen an implementation I shared here? #559 (comment) It fixes some style issues and the code is shorter. I also find it more readable because variables are shorter. The first two ifs can be written as one. Direct link to gist: https://gist.github.com/nkaretnikov/1a66b90a74fa805f1022e90252e54c87 There are also tests there that you could re-use, like the file/directory creation logic. Ignore the part where I check against tools, it should just check against hardcoded numbers. But it's not a blocker anyway. |
||
|
||
|
||
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] | ||
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 | ||
nkaretnikov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
|
||
@contextlib.contextmanager | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Heads up: #634 switches to miniforge. This will need to be updated to match the rest of the jobs once that PR lands.