Skip to content

Commit a56d9f9

Browse files
committed
chore(common): add support for start script
1 parent 815304d commit a56d9f9

3 files changed

Lines changed: 62 additions & 8 deletions

File tree

Makefile

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
APP_DIR := test/my-app
1+
APP_DIR := test/my-app-1
22
BUILD_DIR := /tmp/uv-bp-build
33
CACHE_DIR := /tmp/uv-bp-cache
44
ENV_DIR := /tmp/uv-bp-env
@@ -24,4 +24,15 @@ test-buildpack: clean-test-buildpack
2424

2525
# Start the staged sample app locally using the dependencies prepared by `test-buildpack`.
2626
start-local:
27-
cd $(BUILD_DIR) && /bin/bash -lc 'source .profile.d/python.sh && $(PYTHON_BIN) main.py'
27+
@cd $(BUILD_DIR) && /bin/bash -lc '\
28+
source .profile.d/python.sh && \
29+
if [ -f Procfile ]; then \
30+
WEB_CMD=$$(awk -F": " '\''$$1 == "web" { print $$2; exit }'\'' Procfile); \
31+
else \
32+
WEB_CMD=$$($(BUILDPACK_DIR)/bin/release | awk -F": " '\''$$1 ~ /web/ { print $$2; exit }'\''); \
33+
fi; \
34+
if [ -z "$$WEB_CMD" ]; then \
35+
echo "Could not determine a web command to run locally."; \
36+
exit 1; \
37+
fi; \
38+
eval "$$WEB_CMD"'

bin/compile

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,20 +35,29 @@ if [ -f "pyproject.toml" ] && [ -f "uv.lock" ]; then
3535
PYTHON_VERSION=$("$PYTHON_BIN" -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')
3636
# Stage dependencies into a buildpack-owned location instead of a local venv.
3737
SITE_PACKAGES_DIR="$BUILD_DIR/.python_packages/lib/python${PYTHON_VERSION}/site-packages"
38+
SRC_DIR="$BUILD_DIR/src"
3839
PROFILE_DIR="$BUILD_DIR/.profile.d"
3940
EXPORT_FILE="$BUILD_DIR/.uv-export-requirements.txt"
4041

4142
mkdir -p "$SITE_PACKAGES_DIR" "$PROFILE_DIR"
4243

4344
# Export the exact locked dependency set from uv, then install those
4445
# packages into the staged site-packages directory.
45-
uv export --locked --format requirements-txt -o "$EXPORT_FILE"
46+
uv export --locked --format requirements-txt --no-emit-local -o "$EXPORT_FILE"
4647
"$PYTHON_BIN" -m pip install --no-deps --target "$SITE_PACKAGES_DIR" -r "$EXPORT_FILE"
4748

4849
# Add the staged packages to PYTHONPATH so the app can import them at runtime.
49-
cat > "$PROFILE_DIR/python.sh" <<EOF
50+
# If the project uses a `src/` layout, include that too because the local
51+
# project itself is not installed by the exported third-party requirements.
52+
if [ -d "$SRC_DIR" ]; then
53+
cat > "$PROFILE_DIR/python.sh" <<EOF
54+
export PYTHONPATH="$SRC_DIR:$SITE_PACKAGES_DIR:\${PYTHONPATH}"
55+
EOF
56+
else
57+
cat > "$PROFILE_DIR/python.sh" <<EOF
5058
export PYTHONPATH="$SITE_PACKAGES_DIR:\${PYTHONPATH}"
5159
EOF
60+
fi
5261
else
5362
echo "No supported uv project found. Expected both pyproject.toml and uv.lock."
5463
exit 1

bin/release

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,50 @@
11
#!/usr/bin/env bash
22
# Release script for Python buildpack with uv support
33

4+
if command -v python3 >/dev/null 2>&1; then
5+
PYTHON_BIN="python3"
6+
elif command -v python >/dev/null 2>&1; then
7+
PYTHON_BIN="python"
8+
else
9+
PYTHON_BIN="python"
10+
fi
11+
412
# If the app provides its own Procfile, let Cloud Foundry use that.
513
if [ -f "Procfile" ]; then
614
exit 0
7-
# Otherwise, choose a reasonable default entrypoint based on common app files.
15+
# Otherwise, prefer a `start` entry under `[project.scripts]` in pyproject.toml.
16+
elif [ -n "$PYTHON_BIN" ] && [ -f "pyproject.toml" ]; then
17+
START_TARGET=$("$PYTHON_BIN" -c '
18+
import sys
19+
try:
20+
import tomllib
21+
except ModuleNotFoundError:
22+
import tomli as tomllib
23+
24+
with open("pyproject.toml", "rb") as f:
25+
data = tomllib.load(f)
26+
27+
print(data.get("project", {}).get("scripts", {}).get("start", ""))
28+
')
29+
30+
if [ -n "$START_TARGET" ]; then
31+
MODULE_NAME="${START_TARGET%%:*}"
32+
FUNCTION_NAME="${START_TARGET##*:}"
33+
DEFAULT_WEB_PROCESS="${PYTHON_BIN} -c \"from ${MODULE_NAME} import ${FUNCTION_NAME}; ${FUNCTION_NAME}()\""
34+
elif [ -f "main.py" ]; then
35+
DEFAULT_WEB_PROCESS="${PYTHON_BIN} main.py"
36+
elif [ -f "app.py" ]; then
37+
DEFAULT_WEB_PROCESS="${PYTHON_BIN} app.py"
38+
else
39+
DEFAULT_WEB_PROCESS="${PYTHON_BIN} -m uvicorn main:app --host 0.0.0.0 --port \${PORT:-8000}"
40+
fi
41+
# If pyproject.toml is missing or cannot be parsed here, fall back to common app files.
842
elif [ -f "main.py" ]; then
9-
DEFAULT_WEB_PROCESS="python main.py"
43+
DEFAULT_WEB_PROCESS="${PYTHON_BIN} main.py"
1044
elif [ -f "app.py" ]; then
11-
DEFAULT_WEB_PROCESS="python app.py"
45+
DEFAULT_WEB_PROCESS="${PYTHON_BIN} app.py"
1246
else
13-
DEFAULT_WEB_PROCESS="python -m uvicorn main:app --host 0.0.0.0 --port \${PORT:-8000}"
47+
DEFAULT_WEB_PROCESS="${PYTHON_BIN} -m uvicorn main:app --host 0.0.0.0 --port \${PORT:-8000}"
1448
fi
1549

1650
cat <<EOF

0 commit comments

Comments
 (0)