diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 950fb8ef2..6d4935bf1 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -3,13 +3,13 @@ name: Build
on:
push:
branches:
- - 'master'
+ - "master"
paths-ignore:
- - 'docs/**'
- - 'mkdocs.yml'
+ - "docs/**"
+ - "mkdocs.yml"
pull_request:
branches:
- - 'master'
+ - "master"
workflow_dispatch:
inputs:
intergation-tests:
@@ -56,20 +56,20 @@ jobs:
path: frontend/build
python-test:
- needs: [ python-lint, frontend-build ]
+ needs: [python-lint, frontend-build]
runs-on: ${{ matrix.os }}
strategy:
matrix:
- os: [ macos-latest, ubuntu-latest, windows-latest ]
- python-version: [ "3.9", "3.10", "3.11", "3.12", "3.13" ]
+ os: [macos-latest, ubuntu-latest, windows-latest]
+ python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v5
+ uses: astral-sh/setup-uv@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
- run: pip install -U '.[all]' -r requirements_dev.txt
+ run: uv sync --all-extras
- name: Download frontend build
uses: actions/download-artifact@v4
with:
@@ -83,22 +83,26 @@ jobs:
if [ "${{ matrix.os }}" != "macos-latest" ]; then
RUNPOSTGRES="--runpostgres"
fi
- pytest src/tests --runui $RUNPOSTGRES
+ uv run pytest src/tests --runui $RUNPOSTGRES
- name: Run pytest on Windows
if: matrix.os == 'windows-latest'
run: |
- pytest src/tests --runui --runpostgres
+ uv run pytest src/tests --runui --runpostgres
update-get-dstack:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
- needs: [ python-test ]
+ needs: [python-test]
runs-on: ubuntu-latest
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
steps:
+ - name: Set up uv
+ uses: astral-sh/setup-uv@v5
+ with:
+ python-version: 3.11
- name: Install AWS
- run: pip install awscli
+ run: uv tool install awscli
- run: |
VERSION=$((${{ github.run_number }} + ${{ env.BUILD_INCREMENT }}))
echo $VERSION | aws s3 cp - s3://get-dstack/stgn-cli/latest-version --acl public-read
@@ -181,8 +185,12 @@ jobs:
needs: [runner-compile]
runs-on: ubuntu-latest
steps:
+ - name: Set up uv
+ uses: astral-sh/setup-uv@v5
+ with:
+ python-version: 3.11
- name: Install AWS
- run: pip install awscli
+ run: uv tool install awscli
- name: Download Runner
uses: actions/download-artifact@v4
with:
@@ -201,24 +209,24 @@ jobs:
generate-json-schema:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
- needs: [ python-test ]
+ needs: [python-test]
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- - uses: actions/setup-python@v5
+ - uses: astral-sh/setup-uv@v5
with:
python-version: 3.11
- name: Install AWS
- run: pip install awscli
+ run: uv tool install awscli
- name: Install dstack
- run: pip install .
+ run: uv sync
- name: Generate json schema
run: |
- python -c "from dstack._internal.core.models.configurations import DstackConfiguration; print(DstackConfiguration.schema_json(indent=2))" > configuration.json
- python -c "from dstack._internal.core.models.profiles import ProfilesConfig; print(ProfilesConfig.schema_json(indent=2))" > profiles.json
+ uv run python -c "from dstack._internal.core.models.configurations import DstackConfiguration; print(DstackConfiguration.schema_json(indent=2))" > configuration.json
+ uv run python -c "from dstack._internal.core.models.profiles import ProfilesConfig; print(ProfilesConfig.schema_json(indent=2))" > profiles.json
- name: Upload json schema to S3
run: |
VERSION=$((${{ github.run_number }} + ${{ env.BUILD_INCREMENT }}))
@@ -235,20 +243,18 @@ jobs:
working-directory: gateway
steps:
- uses: actions/checkout@v4
- - name: Set up Python 3.11
- uses: actions/setup-python@v5
+ - name: Set up uv
+ uses: astral-sh/setup-uv@v5
with:
python-version: 3.11
- name: Install AWS
- run: pip install awscli
- - name: Install dependencies
- run: pip install wheel build
+ run: uv tool install awscli
- name: Compute version
run: echo VERSION=$((${{ github.run_number }} + ${{ env.BUILD_INCREMENT }})) > $GITHUB_ENV
- name: Build package
run: |
echo "__version__ = \"${{ env.VERSION }}\"" > src/dstack/gateway/version.py
- python -m build .
+ uv build
- name: Upload to S3
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
@@ -257,23 +263,24 @@ jobs:
WHEEL=dstack_gateway-${{ env.VERSION }}-py3-none-any.whl
aws s3 cp dist/$WHEEL "s3://dstack-gateway-downloads/stgn/$WHEEL"
echo "${{ env.VERSION }}" | aws s3 cp - "s3://dstack-gateway-downloads/stgn/latest-version"
-
+
docs-build:
# Skip for PRs from forks since mkdocs-material-insiders is not available in forks
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- - uses: actions/setup-python@v5
+ - uses: astral-sh/setup-uv@v5
with:
python-version: 3.11
- name: Install dstack
run: |
- pip install -e .[server]
+ uv sync --extra server
+ # Move these deps into an extra and install that way
- name: Build
run: |
- pip install pillow cairosvg
+ uv pip install pillow cairosvg
sudo apt-get install -y libcairo2-dev libfreetype6-dev libffi-dev libjpeg-dev libpng-dev libz-dev
- pip install mkdocs-material "mkdocs-material[imaging]" mkdocs-material-extensions mkdocs-redirects mkdocs-gen-files "mkdocstrings[python]" mkdocs-render-swagger-plugin --upgrade
- pip install git+https://${{ secrets.GH_TOKEN }}@github.com/squidfunk/mkdocs-material-insiders.git
- mkdocs build -s
+ uv pip install mkdocs-material "mkdocs-material[imaging]" mkdocs-material-extensions mkdocs-redirects mkdocs-gen-files "mkdocstrings[python]" mkdocs-render-swagger-plugin --upgrade
+ uv pip install git+https://${{ secrets.GH_TOKEN }}@github.com/squidfunk/mkdocs-material-insiders.git
+ uv mkdocs build -s
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index b6ba71d78..ecf412624 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -34,7 +34,7 @@ on:
env:
PACKER_VERSION: "1.9.2"
- BUILD_PREFIX: ${{ inputs.staging && format('stgn-{0}-', github.run_number) || '' }} # staging ? prefix : ''
+ BUILD_PREFIX: ${{ inputs.staging && format('stgn-{0}-', github.run_number) || '' }} # staging ? prefix : ''
jobs:
build-docker:
@@ -45,23 +45,23 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- python: [ "3.9", "3.10", "3.11", "3.12", "3.13" ]
+ python: ["3.9", "3.10", "3.11", "3.12", "3.13"]
steps:
- - name: Checkout repository
- uses: actions/checkout@v4
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v3
- - name: Login to DockerHub
- uses: docker/login-action@v3
- with:
- username: ${{ secrets.DOCKERHUB_USERNAME }}
- password: ${{ secrets.DOCKERHUB_TOKEN }}
- - name: Set up QEMU
- uses: docker/setup-qemu-action@v3
- - name: Build and upload to DockerHub
- run: |
- docker buildx build --platform linux/amd64 --build-arg PYTHON=${{ matrix.python }} --push --provenance=false --tag dstackai/base:py${{ matrix.python }}-${{ inputs.image_version }}-cuda-12.1 -f base/Dockerfile .
- docker buildx build --platform linux/amd64 --build-arg PYTHON=${{ matrix.python }} --build-arg VERSION=${{ inputs.image_version }} --push --provenance=false --tag dstackai/base:py${{ matrix.python }}-${{ inputs.image_version }}-cuda-12.1-devel -f base/devel.Dockerfile .
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+ - name: Login to DockerHub
+ uses: docker/login-action@v3
+ with:
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
+ password: ${{ secrets.DOCKERHUB_TOKEN }}
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v3
+ - name: Build and upload to DockerHub
+ run: |
+ docker buildx build --platform linux/amd64 --build-arg PYTHON=${{ matrix.python }} --push --provenance=false --tag dstackai/base:py${{ matrix.python }}-${{ inputs.image_version }}-cuda-12.1 -f base/Dockerfile .
+ docker buildx build --platform linux/amd64 --build-arg PYTHON=${{ matrix.python }} --build-arg VERSION=${{ inputs.image_version }} --push --provenance=false --tag dstackai/base:py${{ matrix.python }}-${{ inputs.image_version }}-cuda-12.1-devel -f base/devel.Dockerfile .
build-aws-images:
needs: build-docker
@@ -75,7 +75,7 @@ jobs:
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
strategy:
matrix:
- variant: [ "", "-cuda" ]
+ variant: ["", "-cuda"]
steps:
- uses: actions/checkout@v4
- name: Download packer
@@ -87,7 +87,7 @@ jobs:
run: |
./packer build -var-file=versions.json $PROD_VARS -var image_version=${{ inputs.image_version }} -var build_prefix=$BUILD_PREFIX aws-image${{ matrix.variant }}.json
env:
- PROD_VARS: ${{ !inputs.staging && '-var-file=aws-vars-prod.json' || '' }} # production ? var-file : ''
+ PROD_VARS: ${{ !inputs.staging && '-var-file=aws-vars-prod.json' || '' }} # production ? var-file : ''
build-azure-images:
needs: build-docker
@@ -104,7 +104,7 @@ jobs:
VERSION: ${{ github.run_number }}
strategy:
matrix:
- variant: [ "", "-cuda", "-grid" ]
+ variant: ["", "-cuda", "-grid"]
steps:
- uses: actions/checkout@v4
- uses: Azure/login@v2
@@ -135,17 +135,17 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- variant: [ "", "-cuda" ]
+ variant: ["", "-cuda"]
permissions:
- contents: 'read'
- id-token: 'write'
+ contents: "read"
+ id-token: "write"
steps:
- uses: actions/checkout@v4
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v2
with:
- workload_identity_provider: 'projects/531508670106/locations/global/workloadIdentityPools/github-identity-pool/providers/github-id-provider'
- service_account: 'github-actions@dstack.iam.gserviceaccount.com'
+ workload_identity_provider: "projects/531508670106/locations/global/workloadIdentityPools/github-identity-pool/providers/github-id-provider"
+ service_account: "github-actions@dstack.iam.gserviceaccount.com"
create_credentials_file: true
- name: Set up Cloud SDK
uses: google-github-actions/setup-gcloud@v2
@@ -175,7 +175,7 @@ jobs:
OCI_REGION: eu-frankfurt-1
strategy:
matrix:
- variant: [ "", "-cuda" ]
+ variant: ["", "-cuda"]
steps:
- uses: actions/checkout@v4
- name: Setup OCI config
@@ -211,23 +211,23 @@ jobs:
-var oci_subnet_ocid=$OCI_SUBNET \
-var oci_availability_domain=$OCI_AVAILABILITY_DOMAIN \
oci-image${{ matrix.variant }}.json
- - uses: actions/setup-python@v5
+ - uses: astral-sh/setup-uv@v5
with:
- python-version: '3.12'
+ python-version: "3.12"
- name: Install dependencies for publishing
run: |
- pip install .[oci]
+ uv sync --extra oci
- name: Copy image to target regions
if: ${{ !inputs.staging }}
run: |
- python scripts/oci_image_tools.py copy \
+ uv run scripts/oci_image_tools.py copy \
--image ${BUILD_PREFIX}dstack${{ matrix.variant }}-${{ inputs.image_version }} \
--from $OCI_REGION \
--compartment $OCI_COMPARTMENT
- name: Publish image in OCI Marketplace
if: ${{ !inputs.staging }}
run: |
- python scripts/oci_image_tools.py publish \
+ uv run scripts/oci_image_tools.py publish \
--image ${BUILD_PREFIX}dstack${{ matrix.variant }}-${{ inputs.image_version }} \
--compartment $OCI_COMPARTMENT \
--version ${{ inputs.image_version }} \
diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml
index 1c9ae5f1b..02c238fbe 100644
--- a/.github/workflows/docs.yaml
+++ b/.github/workflows/docs.yaml
@@ -11,23 +11,23 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- - uses: actions/setup-python@v5
+ - uses: astral-sh/setup-uv@v5
with:
python-version: 3.11
- name: Install dstack
run: |
if [ -n "${{ inputs.release_tag }}" ]; then
- pip install "dstack[server]==${{ inputs.release_tag }}"
+ uv pip install "dstack[server]==${{ inputs.release_tag }}"
else
- pip install -e .[server]
+ uv sync --extra server
fi
- name: Build
run: |
- pip install pillow cairosvg
+ uv pip install pillow cairosvg
sudo apt-get install -y libcairo2-dev libfreetype6-dev libffi-dev libjpeg-dev libpng-dev libz-dev
- pip install mkdocs-material "mkdocs-material[imaging]" mkdocs-material-extensions mkdocs-redirects mkdocs-gen-files "mkdocstrings[python]" mkdocs-render-swagger-plugin --upgrade
- pip install git+https://${{ secrets.GH_TOKEN }}@github.com/squidfunk/mkdocs-material-insiders.git
- mkdocs build -s
+ uv pip install mkdocs-material "mkdocs-material[imaging]" mkdocs-material-extensions mkdocs-redirects mkdocs-gen-files "mkdocstrings[python]" mkdocs-render-swagger-plugin --upgrade
+ uv pip install git+https://${{ secrets.GH_TOKEN }}@github.com/squidfunk/mkdocs-material-insiders.git
+ uv mkdocs build -s
- name: Deploy
uses: JamesIves/github-pages-deploy-action@v4.6.4
with:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 35a59df0c..3f2710647 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -47,20 +47,20 @@ jobs:
path: frontend/build
python-test:
- needs: [ python-lint, frontend-build ]
+ needs: [python-lint, frontend-build]
runs-on: ${{ matrix.os }}
strategy:
matrix:
- os: [ macos-latest, ubuntu-latest, windows-latest ]
- python-version: [ "3.9", "3.10", "3.11", "3.12", "3.13" ]
+ os: [macos-latest, ubuntu-latest, windows-latest]
+ python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v5
+ uses: astral-sh/setup-uv@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
- run: pip install -U '.[all]' -r requirements_dev.txt
+ run: uv sync --all-extras
- name: Download frontend build
uses: actions/download-artifact@v4
with:
@@ -74,11 +74,11 @@ jobs:
if [ "${{ matrix.os }}" != "macos-latest" ]; then
RUNPOSTGRES="--runpostgres"
fi
- pytest src/tests --runui $RUNPOSTGRES
+ uv run pytest src/tests --runui $RUNPOSTGRES
- name: Run pytest on Windows
if: matrix.os == 'windows-latest'
run: |
- pytest src/tests --runui --runpostgres
+ uv run pytest src/tests --runui --runpostgres
runner-test:
defaults:
@@ -150,14 +150,12 @@ jobs:
working-directory: gateway
steps:
- uses: actions/checkout@v4
- - name: Set up Python 3.11
- uses: actions/setup-python@v5
+ - name: Set up uv
+ uses: astral-sh/setup-uv@v5
with:
python-version: 3.11
- name: Install AWS
- run: pip install awscli
- - name: Install dependencies
- run: pip install wheel build
+ run: uv tool install awscli
- name: Store version
run: echo VERSION=${GITHUB_REF#refs/tags/} > $GITHUB_ENV
- name: Build package
@@ -168,7 +166,7 @@ jobs:
"s|@ https://github.com/dstackai/dstack/archive/refs/heads/master.zip|== ${{ env.VERSION }}|" \
pyproject.toml
diff pyproject.toml pyproject.toml.old > /dev/null && echo "Could not set version" && exit 1
- python -m build .
+ uv build
- name: Upload to S3
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
@@ -182,8 +180,12 @@ jobs:
needs: [runner-compile, gateway-build, python-test]
runs-on: ubuntu-latest
steps:
+ - name: Set up uv
+ uses: astral-sh/setup-uv@v5
+ with:
+ python-version: 3.11
- name: Install AWS
- run: pip install awscli
+ run: uv tool install awscli
- name: Download Runner
uses: actions/download-artifact@v4
with:
@@ -201,15 +203,15 @@ jobs:
aws s3 cp . "s3://dstack-runner-downloads/latest/binaries/" --recursive --exclude "*" --include "dstack-*" --acl public-read
pypi-upload:
- needs: [ python-test, runner-upload ]
+ needs: [python-test, runner-upload]
runs-on: ubuntu-latest
outputs:
LATEST: ${{ steps.set_latest.outputs.LATEST }}
name: Set latest variable
steps:
- uses: actions/checkout@v4
- - name: Set up Python 3.11
- uses: actions/setup-python@v5
+ - name: Set up uv
+ uses: astral-sh/setup-uv@v5
with:
python-version: 3.11
- name: Download frontend build
@@ -217,10 +219,6 @@ jobs:
with:
name: frontend-build
path: src/dstack/_internal/server/statics
- - name: Install dependencies
- run: |
- python -m pip install --upgrade pip
- pip install wheel twine packaging
- name: Set output
id: set_latest
run: |
@@ -235,24 +233,28 @@ jobs:
echo "__is_release__ = True" >> src/dstack/version.py
echo $BASE_IMAGE >> src/dstack/version.py
cp README.md src
- python setup.py sdist bdist_wheel -v
- python -m twine upload --repository pypi --username ${{ secrets.PYPI_USERNAME }} --password ${{ secrets.PYPI_PASSWORD }} dist/*
+ uv build
+ uv publish --username ${{ secrets.PYPI_USERNAME }} --password ${{ secrets.PYPI_PASSWORD }}
update-get-dstack-tag:
- needs: [ pypi-upload ]
+ needs: [pypi-upload]
runs-on: ubuntu-latest
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
steps:
+ - name: Set up uv
+ uses: astral-sh/setup-uv@v5
+ with:
+ python-version: 3.11
- name: Install AWS
- run: pip install awscli
+ run: uv tool install awscli
- run: |
VERSION=${GITHUB_REF#refs/tags/}
echo $VERSION | aws s3 cp - s3://get-dstack/cli/latest-version --acl public-read
server-docker-upload:
- needs: [ pypi-upload ]
+ needs: [pypi-upload]
defaults:
run:
working-directory: docker/server
@@ -289,24 +291,24 @@ jobs:
readme-filepath: ./docker/server/README.md
generate-json-schema:
- needs: [ pypi-upload ]
+ needs: [pypi-upload]
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- - uses: actions/setup-python@v5
+ - uses: astral-sh/setup-uv@v5
with:
python-version: 3.11
- name: Install AWS
- run: pip install awscli
+ run: uv tool install awscli
- name: Install dstack
- run: pip install .
+ run: uv sync
- name: Generate json schema
run: |
- python -c "from dstack._internal.core.models.configurations import DstackConfiguration; print(DstackConfiguration.schema_json(indent=2))" > configuration.json
- python -c "from dstack._internal.core.models.profiles import ProfilesConfig; print(ProfilesConfig.schema_json(indent=2))" > profiles.json
+ uv run python -c "from dstack._internal.core.models.configurations import DstackConfiguration; print(DstackConfiguration.schema_json(indent=2))" > configuration.json
+ uv run python -c "from dstack._internal.core.models.profiles import ProfilesConfig; print(ProfilesConfig.schema_json(indent=2))" > profiles.json
- name: Upload json schema to S3
run: |
VERSION=${GITHUB_REF#refs/tags/}
diff --git a/.gitignore b/.gitignore
index 344e57de0..0f577dce9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,3 +21,4 @@ build/
.fleet
.env
.aider*
+uv.lock
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 94f85b103..3b65f0e94 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -28,8 +28,8 @@ If you make a non-trivial change to `dstack`, we recommend you learn about `dsta
We use [`ruff`](https://docs.astral.sh/ruff/) to format Python code and to sort Python imports. Before committing your changes, run:
-1. `ruff check --fix`
-2. `ruff format`
+1. `uv run ruff check --fix`
+2. `uv run ruff format`
> There are also helper pre-commits installed for [`ruff`](https://docs.astral.sh/ruff/integrations/#pre-commit) that make commits fail if the code is not formatted or the imports are not sorted. They also change the code as required so that you can review the changes and commit again.
@@ -40,14 +40,14 @@ To run Python tests, first ensure you've install dev dependencies as described i
Then you can do:
```shell
-pytest src/tests
+uv run pytest src/tests
```
(Optionally) By default, tests run against SQLite.
Use the `--runpostgres` flag to run the tests against Postgres as well:
```shell
-pytest src/tests --runpostgres
+uv run pytest src/tests --runpostgres
```
## Add a new backend
diff --git a/contributing/DEVELOPMENT.md b/contributing/DEVELOPMENT.md
index 935c1abd2..775a0691e 100644
--- a/contributing/DEVELOPMENT.md
+++ b/contributing/DEVELOPMENT.md
@@ -6,32 +6,31 @@
git clone https://github.com/dstackai/dstack
cd dstack
```
-
-## 2. (Recommended) Create a virtual environment:
-```shell
-python3 -m venv venv
-source venv/bin/activate
-```
-
-## 3. Install `dstack` in editable mode:
+## 2. Install uv:
+
+https://docs.astral.sh/uv/getting-started/installation
```shell
-pip install -e '.[all]'
+curl -LsSf https://astral.sh/uv/install.sh | sh
```
-
-## 4. Install dev dependencies:
+
+## 3. Install `dstack` with all extras and dev dependencies:
```shell
-pip install -r requirements_dev.txt
+uv sync --all-extras
```
-
-## 5. (Recommended) Install pre-commits:
+
+`dstack` will be installed into the project's `.venv` in editable mode and can be run with `uv run dstack`.
+
+Alternatively, if you want to manage virtual environments by yourself, you can install `dstack` into the activated virtual environment with `uv sync --all-extras --active`.
+
+## 4. (Recommended) Install pre-commits:
```shell
-pre-commit install
+uv run pre-commit install
```
-## 6. Frontend
+## 5. Frontend
See [FRONTEND.md](FRONTEND.md) for the details on how to build and develop the frontend.
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 000000000..627990eba
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,169 @@
+[project]
+name = "dstack"
+dynamic = ["version", "readme"]
+authors = [{ name = "Andrey Cheptsov", email = "andrey@dstack.ai" }]
+description = "dstack is an open-source orchestration engine for running AI workloads on any cloud or on-premises."
+requires-python = ">=3.9"
+classifiers = [
+ "Development Status :: 4 - Beta",
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
+ "License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)",
+ "Programming Language :: Python :: 3",
+]
+dependencies = [
+ "pyyaml",
+ "requests",
+ "typing-extensions>=4.0.0",
+ "cryptography",
+ "packaging",
+ "python-dateutil",
+ "cachetools",
+ "gitpython",
+ "jsonschema",
+ "paramiko>=3.2.0",
+ "cursor",
+ "rich",
+ "rich-argparse",
+ "tqdm",
+ "simple-term-menu",
+ "pydantic>=1.10.10,<2.0.0",
+ "pydantic-duality>=1.2.4",
+ "websocket-client",
+ "python-multipart>=0.0.16",
+ "filelock",
+ "psutil",
+ "gpuhunt>=0.1.2,<0.2.0",
+ "argcomplete>=3.5.0",
+]
+
+[project.urls]
+Homepage = "https://dstack.ai"
+Source = "https://github.com/dstackai/dstack"
+Documentation = "https://dstack.ai/docs"
+Issues = "https://github.com/dstackai/dstack/issues"
+Changelog = "https://github.com/dstackai/dstack/releases"
+Discord = "https://discord.gg/u8SmfwPpMd"
+
+[build-system]
+requires = ["hatchling", "hatch-vcs", "hatch-fancy-pypi-readme"]
+build-backend = "hatchling.build"
+
+[project.scripts]
+dstack = "dstack._internal.cli.main:main"
+
+[tool.hatch.version]
+source = "vcs"
+
+[tool.hatch.build.targets.wheel.shared-data]
+"src/dstack/_internal/proxy/gateway/resources" = "dstack/_internal/proxy/gateway/resources"
+"src/dstack/_internal/server/statics" = "dstack/_internal/server/statics"
+
+[tool.hatch.metadata.hooks.fancy-pypi-readme]
+content-type = "text/markdown"
+
+[[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]]
+path = "README.md"
+
+[[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]]
+pattern = '\s*|]*>\s*|\s*|\s*|### Demo\s*'
+replacement = ''
+ignore-case = true
+
+[dependency-groups]
+dev = [
+ "build>=1.2.2.post1",
+ "httpx>=0.28.1",
+ "pre-commit>=4.2.0",
+ "pytest-asyncio>=0.23.8",
+ "pytest-httpbin>=2.1.0",
+ "httpbin>=0.10.2", # indirect to make compatible with Werkzeug 3
+ "pytest~=7.2",
+ "pytest-socket>=0.7.0",
+ "requests-mock>=1.12.1",
+ "openai>=1.68.2",
+ "freezegun>=1.5.1",
+ "ruff>=0.11.2",
+ "testcontainers>=4.9.2",
+]
+
+[project.optional-dependencies]
+gateway = [
+ "fastapi",
+ "starlette>=0.26.0",
+ "uvicorn",
+ "aiorwlock",
+ "aiocache",
+ "httpx",
+ "jinja2",
+]
+server = [
+ "fastapi",
+ "starlette>=0.26.0",
+ "uvicorn",
+ "aiorwlock",
+ "aiocache",
+ "httpx",
+ "jinja2",
+ "watchfiles",
+ "sqlalchemy[asyncio]>=2.0.0",
+ "sqlalchemy_utils>=0.40.0",
+ "alembic>=1.10.2",
+ "apscheduler<4",
+ "aiosqlite",
+ "docker>=6.0.0",
+ "python-dxf==12.1.0",
+ "sentry-sdk[fastapi]",
+ "alembic-postgresql-enum",
+ "asyncpg",
+ "python-json-logger>=3.1.0",
+ "prometheus-client",
+ "grpcio>=1.50",
+]
+aws = [
+ "boto3",
+ "botocore",
+ "dstack[server]",
+]
+azure = [
+ "azure-identity>=1.12.0",
+ "azure-mgmt-subscription>=3.1.1",
+ "azure-mgmt-compute>=29.1.0",
+ "azure-mgmt-network>=23.0.0,<28.0.0",
+ "azure-mgmt-resource>=22.0.0",
+ "azure-mgmt-authorization>=3.0.0",
+ "dstack[server]",
+]
+gcp = [
+ "google-auth>=2.3.0",
+ "google-cloud-storage>=2.0.0",
+ "google-cloud-compute>=1.5.0",
+ "google-cloud-logging>=2.0.0",
+ "google-api-python-client>=2.80.0",
+ "google-cloud-billing>=1.11.0",
+ "google-cloud-tpu>=1.18.3",
+ "dstack[server]",
+]
+datacrunch = [
+ "datacrunch",
+ "dstack[server]",
+]
+kubernetes = [
+ "kubernetes",
+ "dstack[server]",
+]
+lambda = [
+ "boto3",
+ "botocore",
+ "dstack[server]",
+]
+oci = [
+ "oci",
+ "dstack[server]",
+]
+nebius = [
+ "nebius>=0.2.19,<0.3; python_version >= '3.10'",
+ "dstack[server]",
+]
+all = [
+ "dstack[gateway,server,aws,azure,gcp,datacrunch,kubernetes,lambda,nebius,oci]",
+]
diff --git a/requirements_dev.txt b/requirements_dev.txt
deleted file mode 100644
index 78ad4b818..000000000
--- a/requirements_dev.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-build~=1.2 # For building dstack-gateway wheels
-pre-commit
-httpx>=0.23
-pytest~=7.2
-pytest-asyncio>=0.21
-pytest-httpbin==2.1.0
-pytest-socket>=0.7.0
-requests-mock>=1.12.1
-openai>=1.53.0,<2.0.0
-freezegun>=1.2.0
-ruff==0.5.3 # Should match .pre-commit-config.yaml
-testcontainers # testcontainers<4 may not work with asyncpg
diff --git a/setup.py b/setup.py
deleted file mode 100644
index 5da9fe3cb..000000000
--- a/setup.py
+++ /dev/null
@@ -1,180 +0,0 @@
-import re
-import sys
-from pathlib import Path
-
-from setuptools import find_packages, setup
-
-project_dir = Path(__file__).parent
-
-
-def get_version():
- text = (project_dir / "src" / "dstack" / "version.py").read_text()
- match = re.compile(r"__version__\s*=\s*\"?([^\n\"]+)\"?.*").match(text)
- if match:
- if match.group(1) != "None":
- return match.group(1)
- else:
- return None
- else:
- sys.exit("Can't parse version.py")
-
-
-def get_long_description():
- return re.sub(
- r"\s*|]*>\s*|\s*|\s*|### Demo\s*",
- "",
- open(project_dir / "README.md").read(),
- )
-
-
-BASE_DEPS = [
- "pyyaml",
- "requests",
- "typing-extensions>=4.0.0",
- "cryptography",
- "packaging",
- "python-dateutil",
- "cachetools",
- "gitpython",
- "jsonschema",
- "paramiko>=3.2.0",
- "cursor",
- "rich",
- "rich-argparse",
- "tqdm",
- "simple-term-menu",
- "pydantic>=1.10.10,<2.0.0",
- "pydantic-duality>=1.2.4",
- "websocket-client",
- "python-multipart>=0.0.16",
- "filelock",
- "psutil",
- "gpuhunt>=0.1.2,<0.2.0",
- "argcomplete>=3.5.0",
-]
-
-GATEWAY_AND_SERVER_COMMON_DEPS = [
- "fastapi",
- "starlette>=0.26.0",
- "uvicorn",
- "aiorwlock",
- "aiocache",
- "httpx",
- "jinja2",
-]
-
-SERVER_DEPS = GATEWAY_AND_SERVER_COMMON_DEPS + [
- "watchfiles",
- "sqlalchemy[asyncio]>=2.0.0",
- "sqlalchemy_utils>=0.40.0",
- "alembic>=1.10.2",
- "apscheduler<4",
- "aiosqlite",
- "docker>=6.0.0",
- "python-dxf==12.1.0",
- "sentry-sdk[fastapi]",
- "alembic-postgresql-enum",
- "asyncpg",
- "python-json-logger>=3.1.0",
- "prometheus-client",
- "grpcio>=1.50", # indirect
-]
-
-AWS_DEPS = [
- "boto3",
- "botocore",
-]
-
-AZURE_DEPS = [
- "azure-identity>=1.12.0",
- "azure-mgmt-subscription>=3.1.1",
- "azure-mgmt-compute>=29.1.0",
- "azure-mgmt-network>=23.0.0,<28.0.0",
- "azure-mgmt-resource>=22.0.0",
- "azure-mgmt-authorization>=3.0.0",
-]
-
-GCP_DEPS = [
- "google-auth>=2.3.0", # indirect
- "google-cloud-storage>=2.0.0",
- "google-cloud-compute>=1.5.0",
- "google-cloud-logging>=2.0.0",
- "google-api-python-client>=2.80.0",
- "google-cloud-billing>=1.11.0",
- "google-cloud-tpu>=1.18.3",
-]
-
-DATACRUNCH_DEPS = ["datacrunch"]
-
-KUBERNETES_DEPS = ["kubernetes"]
-
-LAMBDA_DEPS = AWS_DEPS
-
-# Nebius requires Python 3.10. On 3.9:
-# `pip install dstack[nebius]` is expected to fail
-# `pip install dstack[all]` is expected to ignore Nebius
-NEBIUS_DEPS = ["nebius>=0.2.19,<0.3"]
-MAYBE_NEBIUS_DEPS = ['nebius>=0.2.19,<0.3; python_version>="3.10"']
-
-OCI_DEPS = ["oci"]
-
-ALL_DEPS = (
- SERVER_DEPS
- + AWS_DEPS
- + AZURE_DEPS
- + GCP_DEPS
- + DATACRUNCH_DEPS
- + KUBERNETES_DEPS
- + MAYBE_NEBIUS_DEPS
- + OCI_DEPS
-)
-
-setup(
- name="dstack",
- version=get_version(),
- author="Andrey Cheptsov",
- author_email="andrey@dstack.ai",
- package_dir={"": "src"},
- packages=find_packages("src"),
- package_data={
- "dstack._internal.proxy.gateway": ["resources/**/*"],
- "dstack._internal.server": ["statics/**/*"],
- },
- include_package_data=True,
- scripts=[],
- entry_points={
- "console_scripts": ["dstack=dstack._internal.cli.main:main"],
- },
- url="https://dstack.ai",
- project_urls={
- "Source": "https://github.com/dstackai/dstack",
- "Docs": "https://dstack.ai/docs",
- "Issues": "https://github.com/dstackai/dstack/issues",
- "Changelog": "https://github.com/dstackai/dstack/releases",
- "Discord": "https://discord.gg/u8SmfwPpMd",
- },
- description="dstack is an open-source orchestration engine for running AI workloads on any cloud or on-premises.",
- long_description=get_long_description(),
- long_description_content_type="text/markdown",
- python_requires=">=3.9",
- install_requires=BASE_DEPS,
- extras_require={
- "all": ALL_DEPS,
- "gateway": GATEWAY_AND_SERVER_COMMON_DEPS,
- "server": SERVER_DEPS,
- "aws": SERVER_DEPS + AWS_DEPS,
- "azure": SERVER_DEPS + AZURE_DEPS,
- "datacrunch": SERVER_DEPS + DATACRUNCH_DEPS,
- "gcp": SERVER_DEPS + GCP_DEPS,
- "kubernetes": SERVER_DEPS + KUBERNETES_DEPS,
- "lambda": SERVER_DEPS + LAMBDA_DEPS,
- "nebius": SERVER_DEPS + NEBIUS_DEPS,
- "oci": SERVER_DEPS + OCI_DEPS,
- },
- classifiers=[
- "Development Status :: 4 - Beta",
- "Topic :: Scientific/Engineering :: Artificial Intelligence",
- "License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)",
- "Programming Language :: Python :: 3",
- ],
-)