mirror of
https://github.com/hanzoai/pack.git
synced 2026-09-30 23:43:13 +00:00
fix: detect psycopg3 and add required apt packages (#350)
* feat: add psycopg (psycopg3) to postgres dependency detection Added psycopg to the list of Python dependencies that trigger installation of PostgreSQL libraries (libpq-dev for build, libpq5 for runtime). While psycopg[binary] includes a bundled libpq, the base psycopg package and psycopg[c] both require system libpq. Since we cannot determine which variant is being used from dependency files, we conservatively install libpq when any psycopg dependency is detected. Also added integration test example for psycopg3 to verify the dependency detection works correctly. * chore: add tmp/ to .gitignore * test: enhance psycopg3 example to test actual database connectivity Updated the python-psycopg3 example to: - Connect to a real PostgreSQL database - Execute test queries to verify connectivity - Use docker-compose.yml to provide postgres service for integration tests - Assert on multiple output strings to verify full connection lifecycle This provides a comprehensive test that psycopg can successfully use the system libpq libraries to connect to and query PostgreSQL. * test: upgrade psycopg3 example to Python 3.14 and assert major version Updated the python-psycopg3 example to: - Use Python 3.14 (latest stable release) - Specify psycopg>=3.2,<4.0 to ensure psycopg3 is used - Add runtime check that verifies psycopg major version is 3 - Update test assertion to match new output message This ensures the test explicitly validates we're using psycopg3 (not psycopg2) and tests compatibility with the latest Python version. * refactor: convert python-psycopg3 example to use uv and latest Python - Changed .python-version from 3.14 to "latest" - Converted from pip to uv package manager - Created pyproject.toml with psycopg>=3.2,<4.0 dependency - Generated uv.lock for reproducible builds - Removed requirements.txt - Reverted .gitignore changes (removed tmp/) * test: update snapshot --------- Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
committed by
GitHub
co-authored by
Hanzo Dev
parent
652698eecb
commit
ac447bc76f
+201
@@ -0,0 +1,201 @@
|
||||
{
|
||||
"caches": {
|
||||
"apt": {
|
||||
"directory": "/var/cache/apt",
|
||||
"type": "locked"
|
||||
},
|
||||
"apt-lists": {
|
||||
"directory": "/var/lib/apt/lists",
|
||||
"type": "locked"
|
||||
},
|
||||
"uv": {
|
||||
"directory": "/opt/uv-cache",
|
||||
"type": "shared"
|
||||
}
|
||||
},
|
||||
"deploy": {
|
||||
"base": {
|
||||
"step": "packages:apt:runtime"
|
||||
},
|
||||
"inputs": [
|
||||
{
|
||||
"include": [
|
||||
"/mise/shims",
|
||||
"/mise/installs",
|
||||
"/usr/local/bin/mise",
|
||||
"/etc/mise/config.toml",
|
||||
"/root/.local/state/mise",
|
||||
".python-version"
|
||||
],
|
||||
"step": "packages:mise"
|
||||
},
|
||||
{
|
||||
"include": [
|
||||
"/app/.venv"
|
||||
],
|
||||
"step": "build"
|
||||
},
|
||||
{
|
||||
"exclude": [
|
||||
".venv"
|
||||
],
|
||||
"include": [
|
||||
"."
|
||||
],
|
||||
"step": "build"
|
||||
}
|
||||
],
|
||||
"startCommand": "python main.py",
|
||||
"variables": {
|
||||
"PIP_DEFAULT_TIMEOUT": "100",
|
||||
"PIP_DISABLE_PIP_VERSION_CHECK": "1",
|
||||
"PYTHONDONTWRITEBYTECODE": "1",
|
||||
"PYTHONFAULTHANDLER": "1",
|
||||
"PYTHONHASHSEED": "random",
|
||||
"PYTHONUNBUFFERED": "1"
|
||||
}
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"caches": [
|
||||
"apt",
|
||||
"apt-lists"
|
||||
],
|
||||
"commands": [
|
||||
{
|
||||
"cmd": "sh -c 'apt-get update \u0026\u0026 apt-get install -y libpq-dev'",
|
||||
"customName": "install apt packages: libpq-dev"
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
"image": "ghcr.io/railwayapp/railpack-builder:latest"
|
||||
}
|
||||
],
|
||||
"name": "packages:apt:build"
|
||||
},
|
||||
{
|
||||
"assets": {
|
||||
"mise.toml": "[mise.toml]"
|
||||
},
|
||||
"commands": [
|
||||
{
|
||||
"path": "/mise/shims"
|
||||
},
|
||||
{
|
||||
"dest": ".python-version",
|
||||
"src": ".python-version"
|
||||
},
|
||||
{
|
||||
"customName": "create mise config",
|
||||
"name": "mise.toml",
|
||||
"path": "/etc/mise/config.toml"
|
||||
},
|
||||
{
|
||||
"cmd": "mise install",
|
||||
"customName": "install mise packages: python, uv"
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
"step": "packages:apt:build"
|
||||
}
|
||||
],
|
||||
"name": "packages:mise",
|
||||
"variables": {
|
||||
"MISE_CACHE_DIR": "/mise/cache",
|
||||
"MISE_CONFIG_DIR": "/mise",
|
||||
"MISE_DATA_DIR": "/mise",
|
||||
"MISE_IDIOMATIC_VERSION_FILE_ENABLE_TOOLS": "python,node,ruby,elixir,go,java,yarn",
|
||||
"MISE_INSTALLS_DIR": "/mise/installs",
|
||||
"MISE_NODE_VERIFY": "false",
|
||||
"MISE_PARANOID": "1",
|
||||
"MISE_PYTHON_COMPILE": "false",
|
||||
"MISE_SHIMS_DIR": "/mise/shims"
|
||||
}
|
||||
},
|
||||
{
|
||||
"caches": [
|
||||
"uv"
|
||||
],
|
||||
"commands": [
|
||||
{
|
||||
"dest": "pyproject.toml",
|
||||
"src": "pyproject.toml"
|
||||
},
|
||||
{
|
||||
"dest": "uv.lock",
|
||||
"src": "uv.lock"
|
||||
},
|
||||
{
|
||||
"path": "/root/.local/bin"
|
||||
},
|
||||
{
|
||||
"path": "/app/.venv/bin"
|
||||
},
|
||||
{
|
||||
"cmd": "uv sync --locked --no-dev --no-install-project"
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
"step": "packages:mise"
|
||||
}
|
||||
],
|
||||
"name": "install",
|
||||
"variables": {
|
||||
"PIP_DEFAULT_TIMEOUT": "100",
|
||||
"PIP_DISABLE_PIP_VERSION_CHECK": "1",
|
||||
"PYTHONDONTWRITEBYTECODE": "1",
|
||||
"PYTHONFAULTHANDLER": "1",
|
||||
"PYTHONHASHSEED": "random",
|
||||
"PYTHONUNBUFFERED": "1",
|
||||
"UV_CACHE_DIR": "/opt/uv-cache",
|
||||
"UV_COMPILE_BYTECODE": "1",
|
||||
"UV_LINK_MODE": "copy",
|
||||
"UV_PYTHON_DOWNLOADS": "never",
|
||||
"VIRTUAL_ENV": "/app/.venv"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commands": [
|
||||
{
|
||||
"cmd": "uv sync --locked --no-dev --no-editable"
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
"step": "install"
|
||||
},
|
||||
{
|
||||
"include": [
|
||||
"."
|
||||
],
|
||||
"local": true
|
||||
}
|
||||
],
|
||||
"name": "build",
|
||||
"secrets": [
|
||||
"*"
|
||||
]
|
||||
},
|
||||
{
|
||||
"caches": [
|
||||
"apt",
|
||||
"apt-lists"
|
||||
],
|
||||
"commands": [
|
||||
{
|
||||
"cmd": "sh -c 'apt-get update \u0026\u0026 apt-get install -y libpq5'",
|
||||
"customName": "install apt packages: libpq5"
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
"image": "ghcr.io/railwayapp/railpack-runtime:latest"
|
||||
}
|
||||
],
|
||||
"name": "packages:apt:runtime"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -423,7 +423,7 @@ func (p *PythonProvider) installNeedsAllFiles(ctx *generate.GenerateContext) boo
|
||||
func (p *PythonProvider) usesPostgres(ctx *generate.GenerateContext) bool {
|
||||
djangoPythonRe := regexp.MustCompile(`django.db.backends.postgresql`)
|
||||
containsDjangoPostgres := len(ctx.App.FindFilesWithContent("**/*.py", djangoPythonRe)) > 0
|
||||
return p.usesDep(ctx, "psycopg2") || p.usesDep(ctx, "psycopg2-binary") || containsDjangoPostgres
|
||||
return p.usesDep(ctx, "psycopg2") || p.usesDep(ctx, "psycopg2-binary") || p.usesDep(ctx, "psycopg") || containsDjangoPostgres
|
||||
}
|
||||
|
||||
func (p *PythonProvider) usesMysql(ctx *generate.GenerateContext) bool {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
latest
|
||||
@@ -0,0 +1,12 @@
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:17-alpine
|
||||
environment:
|
||||
POSTGRES_DB: test_db
|
||||
POSTGRES_USER: test_user
|
||||
POSTGRES_PASSWORD: test_password
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U test_user -d test_db"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
@@ -0,0 +1,32 @@
|
||||
import os
|
||||
import psycopg
|
||||
|
||||
print("psycopg version:", psycopg.__version__)
|
||||
|
||||
major_version = int(psycopg.__version__.split('.')[0])
|
||||
if major_version != 3:
|
||||
print(f"ERROR: Expected psycopg 3.x but got {psycopg.__version__}")
|
||||
exit(1)
|
||||
|
||||
print("Using psycopg3 - libpq is available")
|
||||
|
||||
database_url = os.environ.get("DATABASE_URL")
|
||||
if not database_url:
|
||||
print("No DATABASE_URL provided, skipping connection test")
|
||||
exit(0)
|
||||
|
||||
print(f"Connecting to database...")
|
||||
conn = psycopg.connect(database_url)
|
||||
print("Successfully connected to PostgreSQL!")
|
||||
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("SELECT version();")
|
||||
version = cur.fetchone()[0]
|
||||
print(f"PostgreSQL version: {version}")
|
||||
|
||||
cur.execute("SELECT 1 + 1 AS result;")
|
||||
result = cur.fetchone()[0]
|
||||
print(f"Test query result: 1 + 1 = {result}")
|
||||
|
||||
conn.close()
|
||||
print("Connection closed successfully")
|
||||
@@ -0,0 +1,9 @@
|
||||
[project]
|
||||
name = "python-psycopg3"
|
||||
version = "0.1.0"
|
||||
description = "Test psycopg3 with PostgreSQL"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.11"
|
||||
dependencies = [
|
||||
"psycopg>=3.2,<4.0",
|
||||
]
|
||||
@@ -0,0 +1,13 @@
|
||||
[
|
||||
{
|
||||
"expectedOutput": [
|
||||
"Using psycopg3 - libpq is available",
|
||||
"Successfully connected to PostgreSQL!",
|
||||
"Test query result: 1 + 1 = 2",
|
||||
"Connection closed successfully"
|
||||
],
|
||||
"envs": {
|
||||
"DATABASE_URL": "postgresql://test_user:test_password@postgres:5432/test_db"
|
||||
}
|
||||
}
|
||||
]
|
||||
Generated
+45
@@ -0,0 +1,45 @@
|
||||
version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.11"
|
||||
|
||||
[[package]]
|
||||
name = "psycopg"
|
||||
version = "3.2.12"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
||||
{ name = "tzdata", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a8/77/c72d10262b872617e509a0c60445afcc4ce2cd5cd6bc1c97700246d69c85/psycopg-3.2.12.tar.gz", hash = "sha256:85c08d6f6e2a897b16280e0ff6406bef29b1327c045db06d21f364d7cd5da90b", size = 160642, upload-time = "2025-10-26T00:46:03.045Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/28/8c4f90e415411dc9c78d6ba10b549baa324659907c13f64bfe3779d4066c/psycopg-3.2.12-py3-none-any.whl", hash = "sha256:8a1611a2d4c16ae37eada46438be9029a35bb959bb50b3d0e1e93c0f3d54c9ee", size = 206765, upload-time = "2025-10-26T00:10:42.173Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "python-psycopg3"
|
||||
version = "0.1.0"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "psycopg" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [{ name = "psycopg", specifier = ">=3.2,<4.0" }]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.15.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tzdata"
|
||||
version = "2025.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" },
|
||||
]
|
||||
Reference in New Issue
Block a user