Skip to content

Commit a408532

Browse files
authored
Merge pull request #28 from AllenInstitute/feature/upgrade-to-uv
Upgrade package to uv
2 parents 190ac3c + 45b4afb commit a408532

File tree

37 files changed

+2431
-224
lines changed

37 files changed

+2431
-224
lines changed

.github/workflows/build.yml

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ on:
77
- '**/_version.py'
88
push:
99
branches: [ main ]
10+
tags-ignore:
11+
- 'v*'
1012
paths-ignore:
1113
- '**/_version.py'
1214
workflow_dispatch:
@@ -20,22 +22,42 @@ jobs:
2022
strategy:
2123
fail-fast: false
2224
matrix:
23-
python-version: ["3.9", "3.10", "3.11", "3.12"]
25+
python-version: ["3.10", "3.11", "3.12", "3.13"]
26+
uv-resolution: ["highest", "lowest"]
27+
exclude:
28+
- python-version: "3.11"
29+
uv-resolution: "lowest"
30+
- python-version: "3.12"
31+
uv-resolution: "lowest"
32+
env:
33+
PYTHON_VERSION: ${{ matrix.python-version }}
2434
steps:
2535
- uses: actions/checkout@v4
26-
- name: Set up Python ${{ matrix.python-version }}
27-
uses: actions/setup-python@v4
36+
- name: Install uv
37+
id: setup-uv
38+
uses: astral-sh/setup-uv@v7
2839
with:
29-
python-version: ${{ matrix.python-version }}
30-
cache: 'pip'
40+
python-version: ${{ github.event.inputs.python-version }}
3141
- name: Set up AllenInstitute Repo Authorization
3242
uses: ./.github/actions/configure-org-repo-authorization
3343
with:
3444
token: ${{ secrets.AI_PACKAGES_TOKEN }}
3545
ssh_private_key: ${{ secrets.AIBSGITHUB_PRIVATE_KEY }}
3646
- name: Run Release
3747
run: |
38-
make release
48+
if [ ${{ matrix.uv-resolution }} == "highest" ]; then
49+
make release
50+
else
51+
# Install dependencies
52+
uv sync --frozen --group dev --resolution ${{ matrix.uv-resolution }}
53+
54+
# Linting
55+
uv run ruff check
56+
uv run mypy ./
57+
58+
# Testing
59+
uv run pytest -vv --durations=10
60+
fi
3961
shell: bash
4062
- name: Upload coverage reports
4163
if: |
@@ -46,11 +68,10 @@ jobs:
4668
|| (github.event_name == 'push' && github.ref_name == 'main')
4769
|| github.event_name == 'workflow_dispatch'
4870
)
49-
&& matrix.python-version == '3.11'
71+
&& matrix.python-version == '3.12'
5072
}}
5173
uses: codecov/codecov-action@v5
5274
with:
5375
# https://github.com/codecov/codecov-action#arguments
5476
token: ${{ secrets.CODECOV_TOKEN }}
5577
env_vars: PYTHON_VERSION
56-

.github/workflows/release.yml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ on:
1919
type: choice
2020
required: true
2121
default: "3.11"
22-
options: ["3.9", "3.10", "3.11", "3.12"]
22+
options: ["3.10", "3.11", "3.12", "3.13"]
2323

2424
jobs:
2525
bump:
@@ -41,11 +41,11 @@ jobs:
4141
uses: actions/checkout@v4
4242
with:
4343
ssh-key: ${{ secrets.AIBSGITHUB_PRIVATE_KEY }}
44-
- name: Set up Python ${{ github.event.inputs.python-version }}
45-
uses: actions/setup-python@v4
44+
- name: Install uv
45+
id: setup-uv
46+
uses: astral-sh/setup-uv@v7
4647
with:
4748
python-version: ${{ github.event.inputs.python-version }}
48-
cache: 'pip'
4949
- name: Set up AllenInstitute Repo Authorization
5050
uses: ./.github/actions/configure-org-repo-authorization
5151
with:
@@ -63,7 +63,7 @@ jobs:
6363
- name: Bump version with bumpversion
6464
run: |
6565
source .venv/bin/activate
66-
bump-my-version bump ${{ github.event.inputs.part }}
66+
uvx --from bump-my-version bump-my-version bump ${{ github.event.inputs.part }}
6767
- name: Commit and push with tags
6868
if: ${{ github.event.inputs.dry-run == 'false' }}
6969
run: |
@@ -106,11 +106,11 @@ jobs:
106106
with:
107107
ref: ${{ needs.bump.outputs.VERSION_TAG }}
108108
ssh-key: ${{ secrets.AIBSGITHUB_PRIVATE_KEY }}
109-
- name: Set up Python ${{ github.event.inputs.python-version }}
110-
uses: actions/setup-python@v4
109+
- name: Install uv
110+
id: setup-uv
111+
uses: astral-sh/setup-uv@v7
111112
with:
112113
python-version: ${{ github.event.inputs.python-version }}
113-
cache: 'pip'
114114
- name: Set up AllenInstitute Repo Authorization
115115
uses: ./.github/actions/configure-org-repo-authorization
116116
with:

Makefile

Lines changed: 33 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ COVERAGE_DIR := $(PACKAGE_DIR)/build/documentation/coverage/
1313

1414
DEP_FILES := $(wildcard setup.*) $(wildcard requirements*.txt) $(wildcard pyproject.toml)
1515

16-
1716
####################
1817
##@ Helper Commands
1918
####################
@@ -27,7 +26,6 @@ help: ## Display this help
2726
##@ Cleaning Commands
2827
######################
2928

30-
3129
clean-venv: ## Clean virtualenv
3230
rm -rf $(VENV)/
3331

@@ -60,37 +58,32 @@ obliterate: clean-venv clean ## alias to clean, clean-venv
6058

6159
.PHONY: clean-venv clean-install-stamp clean-build clean-pyc clean-test clean obliterate
6260

63-
6461
#########################
6562
##@ Installation Commands
6663
#########################
6764

68-
create-venv: $(PYTHON) ## Creates virtualenv
69-
$(PYTHON):
70-
python3 -m venv $(VENV) --prompt $(shell basename $(PACKAGE_DIR))
71-
$(PYTHON) -m pip install --upgrade pip
65+
.uv: ## Check that uv is installed
66+
@uv -V || echo 'Please install uv: https://docs.astral.sh/uv/getting-started/installation/'
7267

73-
install: $(INSTALL_STAMP) ## Installs package dependencies
74-
$(INSTALL_STAMP): $(PYTHON) $(DEP_FILES)
75-
@. $(VENV_BIN)/activate;\
76-
$(PIP) install -e .[dev];
77-
@touch $(INSTALL_STAMP)
68+
install: .uv ## Installs development (dev/lint) related dependencies
69+
uv sync --frozen --group dev
7870

79-
install-release: clean-install-stamp $(PYTHON) $(DEP_FILES) ## Installs package for release
80-
@. $(VENV_BIN)/activate;\
81-
$(PIP) install .[release]
71+
install-release: .uv ## Installs package dependencies
72+
uv sync --frozen --group release
8273

83-
install-force: clean-install-stamp install ## Force install package dependencies
74+
75+
rebuild-lockfile: .uv ## Rebuilds the lockfile
76+
uv lock --upgrade
8477

8578
link-packages: ## Link local packages to virtualenv
8679
@parent_dir=$$(dirname $$(pwd)); \
8780
local_packages=$$(ls $$parent_dir); \
88-
dependencies=$$($(PIP) list --format freeze --exclude-editable | awk -F '==' '{print $$1}');\
81+
dependencies=$$(uv pip list --format freeze --exclude-editable | awk -F '==' '{print $$1}');\
8982
for local_package in $$local_packages; do \
9083
for dependency in $$dependencies; do \
9184
if [ $$local_package == $$dependency ]; then \
9285
echo "Reinstalling $$local_package dependency to local override"; \
93-
$(PIP) install -e $$parent_dir/$$local_package --no-deps; \
86+
uv add -v --editable --frozen $$parent_dir/$$local_package; \
9487
fi \
9588
done; \
9689
done
@@ -99,72 +92,64 @@ unlink-packages: ## Unlink local packages from virtualenv
9992
@parent_dir=$$(dirname $$(pwd)); \
10093
this_package=$$(basename $$(pwd)); \
10194
local_packages=$$(ls $$parent_dir); \
102-
dependencies=$$($(PIP) list --format freeze --editable | awk -F '==' '{print $$1}');\
95+
dependencies=$$(uv pip list --format freeze --editable | awk -F '==' '{print $$1}');\
10396
is_found=0; \
10497
for local_package in $$local_packages; do \
10598
for dependency in $$dependencies; do \
10699
if [ $$local_package == $$dependency ] && [ $$local_package != $$this_package ]; then \
107100
is_found=1; \
101+
uv remove --frozen $$local_package; \
108102
fi; \
109103
done \
110104
done; \
111105
if [ $$is_found == 1 ]; then \
112106
echo "Found dependencies installed locally, reinstalling..."; \
113-
make clean-install-stamp install; \
107+
make install; \
114108
fi
115109

116-
.PHONY: create-venv install install-force link-packages unlink-packages
110+
.PHONY: .uv install install-release install rebuild-lockfile link-packages unlink-packages
117111

118112
#######################
119113
##@ Formatting Commands
120114
#######################
121115

122-
lint-black: $(INSTALL_STAMP) ## Run black (check only)
123-
$(VENV_BIN)/black ./ --check
124-
125-
lint-isort: $(INSTALL_STAMP) ## Run isort (check only)
126-
$(VENV_BIN)/isort ./ --check
127-
128-
lint-mypy: $(INSTALL_STAMP) ## Run mypy
129-
$(VENV_BIN)/mypy ./
116+
lint-ruff: install ## Run ruff checker
117+
uv run ruff check
130118

119+
lint-mypy: install ## Run mypy
120+
uv run mypy ./
131121

132-
lint: lint-isort lint-black lint-mypy ## Run all lint targets (black, isort, mypy)
122+
lint: lint-ruff lint-mypy ## Run all lint targets (ruff, mypy)
133123

134124

135-
format-black: $(INSTALL_STAMP) ## Format code using black
136-
$(VENV_BIN)/black ./
125+
format-ruff: install ## Run ruff formatter
126+
uv run ruff check --fix
127+
uv run ruff format
137128

138-
format-isort: $(INSTALL_STAMP) ## Format code using isort
139-
$(VENV_BIN)/isort ./
129+
format: format-ruff ## Run all formatters (ruff)
140130

141-
142-
format: format-isort format-black ## Run all formatters (black, isort)
143-
144-
.PHONY: lint-isort lint-black lint-mypy lint format-lint format-black format-mypy format
131+
.PHONY: lint-ruff lint-mypy lint format-ruff format-mypy format
145132

146133
#####################
147134
##@ Testing Commands
148135
#####################
149136

150-
pytest: $(INSTALL_STAMP) ## Run test (pytest)
151-
$(VENV_BIN)/pytest -vv --durations=10
152-
153-
tox: $(INSTALL_STAMP) ## Run Test in tox environment
154-
$(VENV_BIN)/tox
137+
pytest: install ## Run test (pytest)
138+
uv run pytest -vv --durations=10
155139

156140
test: pytest ## Run Standard Tests
157141

158-
.PHONY: pytest tox test
159-
142+
.PHONY: pytest test
160143

161144
#####################
162145
##@ Inspect Commands
163146
#####################
164147

165-
coverage-server: $(INSTALL_STAMP) ## Run coverage server
148+
coverage-server: install ## Run coverage server
166149
$(PYTHON) -m http.server $(COVERAGE_SERVER_PORT) -d $(COVERAGE_DIR)
167150

151+
.PHONY: coverage-server
152+
168153

169154
#####################
170155
##@ Docker Commands
@@ -183,8 +168,7 @@ docker-build: ## Build docker image
183168
#####################
184169

185170
dist: install-release ## Build source and wheel package
186-
@. $(VENV_BIN)/activate;\
187-
$(PYTHON) -m build;
171+
uv build
188172

189173
reinstall: obliterate install ## Recreate environment and install
190174

@@ -194,4 +178,4 @@ post-build: lint test ## Run linters and tests
194178
release: pre-build build post-build ## Runs pre-build, build, post-build
195179
run: release
196180

197-
.PHONY: reinstall pre-build build post-build release run
181+
.PHONY: dist reinstall pre-build build post-build release run

docker/Dockerfile

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,66 @@
1-
FROM public.ecr.aws/lambda/python:3.11
1+
# Pin the Python toolchain used in the builder; must match Lambda runtime major.minor
2+
ARG PYTHON_VERSION=3.11
3+
24

35
# Resources:
46
# - https://gallery.ecr.aws/lambda/python
57
# - https://docs.aws.amazon.com/lambda/latest/dg/python-image.html
68
# - https://docs.aws.amazon.com/lambda/latest/dg/images-test.html
79
# - https://github.com/aws/aws-lambda-python-runtime-interface-client
810

9-
# update system
11+
12+
#####################################
13+
############## STAGE 0 ##############
14+
########### UV BINARIES #############
15+
#####################################
16+
FROM ghcr.io/astral-sh/uv:latest AS uvbin
17+
18+
#####################################
19+
############## STAGE 1 ##############
20+
################ Builder ############
21+
#####################################
22+
FROM public.ecr.aws/lambda/python:${PYTHON_VERSION} AS builder
23+
24+
# Minimal tools for resolving/installing deps from the monorepo
25+
RUN yum update -y \
26+
&& yum install -y \
27+
git \
28+
openssh-clients \
29+
&& yum clean all \
30+
&& rm -rf /var/cache/yum
31+
32+
# Bring in uv from the official Astral image (builder only)
33+
COPY --from=uvbin /uv /uvx /usr/local/bin/
34+
35+
# Prepare for private repos if needed
36+
RUN mkdir -p -m 0700 /root/.ssh && ssh-keyscan github.com >> /root/.ssh/known_hosts
37+
38+
# Copy the monorepo so relative path installs work
39+
ENV REPO_ROOT=/asset-input
40+
COPY . ${REPO_ROOT}
41+
WORKDIR ${REPO_ROOT}
42+
43+
# Vendor all Python deps (including awslambdaric and local pkgs) into a folder
44+
# that will become ${LAMBDA_TASK_ROOT} in the runtime image.
45+
ENV LAYER_DIR=/opt/lambda-task
46+
RUN --mount=type=ssh \
47+
uv export --frozen --no-dev --group docker --no-editable -o requirements-autogen.txt && \
48+
uv pip install -r requirements-autogen.txt --target ${LAYER_DIR}
49+
50+
# Clean up build-time artifacts that might have been created (defensive)
51+
RUN find ${LAYER_DIR} -type d -name "__pycache__" -prune -exec rm -rf {} +
52+
53+
# Copy entrypoint script alongside the vendored packages
54+
RUN cp ${REPO_ROOT}/docker/docker-entrypoint.sh ${LAYER_DIR}/docker-entrypoint.sh \
55+
&& chmod +x ${LAYER_DIR}/docker-entrypoint.sh
56+
57+
#####################################
58+
############## STAGE 2 ##############
59+
############## Runtime ##############
60+
#####################################
61+
FROM public.ecr.aws/lambda/python:${PYTHON_VERSION}
62+
63+
# Base image is Amazon Linux; install helpful tools (retain previous behavior)
1064
RUN yum update -y \
1165
&& yum install -y \
1266
curl \
@@ -15,30 +69,20 @@ RUN yum update -y \
1569
wget \
1670
aws-cli \
1771
git \
18-
&& rm -rf /var/lib/apt/lists/*
72+
&& yum clean all \
73+
&& rm -rf /var/cache/yum
1974

20-
RUN mkdir -p -m 0700 ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts
21-
22-
# Base image has an old aws cli version (v1.x), this will replace that
75+
# Upgrade AWS CLI to v2
2376
ADD https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip /tmp
2477
RUN unzip /tmp/awscli-exe-linux-x86_64.zip -d /tmp \
2578
&& rm /tmp/awscli-exe-linux-x86_64.zip \
2679
&& /tmp/aws/install
2780

28-
COPY . /asset-input
29-
30-
RUN --mount=type=ssh cd /asset-input \
31-
&& python3 -m pip install --upgrade pip \
32-
&& pip3 install .[lambda] \
33-
--no-cache-dir \
34-
--target "${LAMBDA_TASK_ROOT}" \
35-
&& cp docker/docker-entrypoint.sh ${LAMBDA_TASK_ROOT}/docker-entrypoint.sh \
36-
&& chmod +x ${LAMBDA_TASK_ROOT}/docker-entrypoint.sh
81+
# Copy only the vendored application deps into the Lambda task root
82+
# (No uv, no build tools end up in the final image)
83+
COPY --from=builder /opt/lambda-task ${LAMBDA_TASK_ROOT}
3784

3885
ENV PYTHONPATH="${LAMBDA_TASK_ROOT}:${PYTHONPATH}"
3986
ENV PATH="${LAMBDA_TASK_ROOT}/bin:${PATH}"
4087

41-
RUN rm -rf /asset-input
42-
4388
ENTRYPOINT [ "bash", "-c", "${LAMBDA_TASK_ROOT}/docker-entrypoint.sh" ]
44-

0 commit comments

Comments
 (0)