Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 19 additions & 7 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,36 @@ COPY Cargo.toml ./
COPY src ./src
COPY crates ./crates

# Build for native architecture
RUN cargo build --release
# Build for native architecture with PostgreSQL support
RUN cargo build --release --no-default-features --features tls,postgres

# Stage 2: Runtime - Distroless
FROM gcr.io/distroless/cc-debian12:nonroot
# Stage 2: Runtime
FROM debian:bookworm-slim

# Install runtime dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
libssl3 \
zlib1g \
curl \
&& rm -rf /var/lib/apt/lists/* \
&& useradd -r -u 1000 -m vein

# Copy binary from builder
COPY --from=builder /build/target/release/vein /usr/local/bin/vein

# Set working directory
# Set working directory and permissions
WORKDIR /data
RUN chown vein:vein /data

USER vein

# Expose port
EXPOSE 8346

# Container healthcheck hitting Vein's liveness endpoint
HEALTHCHECK --interval=30s --timeout=5s --start-period=45s --retries=3 \
CMD ["/usr/local/bin/vein", "health"]
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -fsS http://localhost:8346/up || exit 1

# Run vein
ENTRYPOINT ["/usr/local/bin/vein"]
Expand Down
25 changes: 20 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,19 +268,34 @@ docker run -d \
docker logs -f vein
```

### Using Docker Compose
### Using Docker Compose (Recommended)

The included `docker-compose.yml` spins up a **fully configured, production-ready** Vein proxy with PostgreSQL backend:

```bash
# Start the service
docker-compose up -d
# Clone and start
git clone https://github.com/contriboss/vein.git
cd vein
docker compose up -d

# Verify health
docker compose ps # Both services should show "healthy"

# View logs
docker-compose logs -f
docker compose logs -f vein

# Stop the service
docker-compose down
docker compose down
```

**What's included:**
- PostgreSQL 18 with persistent storage
- Vein proxy with auto-migrations (no manual setup)
- Health checks for both services
- Preconfigured networking

Point your Bundler at `http://localhost:8346` and you're done.

### Custom Configuration

```bash
Expand Down
2 changes: 1 addition & 1 deletion crates/vein-adapter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ tls = ["sqlx/runtime-tokio-rustls"]

[dependencies]
anyhow = "1.0.95"
sqlx = { version = "0.8.2", features = ["runtime-tokio", "chrono"] }
sqlx = { version = "0.8.2", features = ["runtime-tokio", "chrono", "migrate"] }
tokio = { version = "1.43", features = ["fs", "macros", "rt-multi-thread", "sync"] }
chrono = { version = "0.4.38", features = ["serde"] }
serde = { version = "1.0.228", features = ["derive"] }
Expand Down
102 changes: 102 additions & 0 deletions crates/vein-adapter/migrations/postgres/20250111000001_init.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
-- Vein PostgreSQL Schema

CREATE TABLE cached_assets (
kind TEXT NOT NULL,
name TEXT NOT NULL,
version TEXT NOT NULL,
platform TEXT,
path TEXT NOT NULL,
sha256 TEXT NOT NULL,
size_bytes BIGINT NOT NULL,
last_accessed TIMESTAMPTZ DEFAULT NOW(),
CONSTRAINT cached_assets_unique UNIQUE (kind, name, version, platform)
);

CREATE INDEX idx_cached_assets_kind ON cached_assets(kind);
CREATE INDEX idx_cached_assets_name ON cached_assets(name);

CREATE TABLE catalog_gems (
name TEXT PRIMARY KEY,
latest_version TEXT,
synced_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE catalog_meta (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
);

CREATE TABLE gem_metadata (
name TEXT NOT NULL,
version TEXT NOT NULL,
platform TEXT NOT NULL DEFAULT 'ruby',
summary TEXT,
description TEXT,
licenses TEXT,
authors TEXT,
emails TEXT,
homepage TEXT,
documentation_url TEXT,
changelog_url TEXT,
source_code_url TEXT,
bug_tracker_url TEXT,
wiki_url TEXT,
funding_url TEXT,
metadata_json TEXT,
dependencies_json TEXT NOT NULL DEFAULT '[]',
executables_json TEXT,
extensions_json TEXT,
native_languages_json TEXT,
has_native_extensions BOOLEAN NOT NULL DEFAULT FALSE,
has_embedded_binaries BOOLEAN NOT NULL DEFAULT FALSE,
required_ruby_version TEXT,
required_rubygems_version TEXT,
rubygems_version TEXT,
specification_version INTEGER,
built_at TEXT,
size_bytes BIGINT,
sha256 TEXT,
sbom_json TEXT,
PRIMARY KEY (name, version, platform)
);

CREATE TABLE gem_versions (
id BIGSERIAL PRIMARY KEY,
name TEXT NOT NULL,
version TEXT NOT NULL,
platform TEXT,
sha256 TEXT,
published_at TIMESTAMPTZ NOT NULL,
available_after TIMESTAMPTZ NOT NULL,
status TEXT NOT NULL DEFAULT 'quarantine',
status_reason TEXT,
upstream_yanked BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT gem_versions_unique UNIQUE (name, version, platform)
);

CREATE INDEX idx_gem_versions_name ON gem_versions(name);
CREATE INDEX idx_gem_versions_status ON gem_versions(status);
CREATE INDEX idx_gv_available ON gem_versions(available_after);

CREATE TABLE gem_symbols (
id BIGSERIAL PRIMARY KEY,
gem_name TEXT NOT NULL,
gem_version TEXT NOT NULL,
gem_platform TEXT NOT NULL DEFAULT 'ruby',
file_path TEXT NOT NULL,
symbol_type TEXT NOT NULL,
symbol_name TEXT NOT NULL,
parent_name TEXT,
line_number INTEGER,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT gem_symbols_unique UNIQUE (gem_name, gem_version, gem_platform, file_path, symbol_name),
CONSTRAINT fk_gem_symbols_metadata
FOREIGN KEY (gem_name, gem_version, gem_platform)
REFERENCES gem_metadata(name, version, platform)
ON DELETE CASCADE
);

CREATE INDEX idx_gem_symbols_name ON gem_symbols(symbol_name);
CREATE INDEX idx_gem_symbols_gem ON gem_symbols(gem_name, gem_version);
95 changes: 95 additions & 0 deletions crates/vein-adapter/migrations/sqlite/20250111000001_init.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
-- Vein SQLite Schema

CREATE TABLE cached_assets (
kind TEXT NOT NULL,
name TEXT NOT NULL,
version TEXT NOT NULL,
platform TEXT,
path TEXT NOT NULL,
sha256 TEXT NOT NULL,
size_bytes INTEGER NOT NULL,
last_accessed TIMESTAMP DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
PRIMARY KEY (kind, name, version, platform)
);

CREATE TABLE catalog_gems (
name TEXT PRIMARY KEY,
latest_version TEXT,
synced_at TIMESTAMP DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
);

CREATE TABLE catalog_meta (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
);

CREATE TABLE gem_metadata (
name TEXT NOT NULL,
version TEXT NOT NULL,
platform TEXT NOT NULL DEFAULT 'ruby',
summary TEXT,
description TEXT,
licenses TEXT,
authors TEXT,
emails TEXT,
homepage TEXT,
documentation_url TEXT,
changelog_url TEXT,
source_code_url TEXT,
bug_tracker_url TEXT,
wiki_url TEXT,
funding_url TEXT,
metadata_json TEXT,
dependencies_json TEXT NOT NULL DEFAULT '[]',
executables_json TEXT,
extensions_json TEXT,
native_languages_json TEXT,
has_native_extensions INTEGER NOT NULL DEFAULT 0,
has_embedded_binaries INTEGER NOT NULL DEFAULT 0,
required_ruby_version TEXT,
required_rubygems_version TEXT,
rubygems_version TEXT,
specification_version INTEGER,
built_at TEXT,
size_bytes INTEGER,
sha256 TEXT,
sbom_json TEXT,
PRIMARY KEY (name, version, platform)
);

CREATE TABLE gem_versions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
version TEXT NOT NULL,
platform TEXT,
sha256 TEXT,
published_at TIMESTAMP NOT NULL,
available_after TIMESTAMP NOT NULL,
status TEXT NOT NULL DEFAULT 'quarantine',
status_reason TEXT,
upstream_yanked INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
updated_at TIMESTAMP NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
UNIQUE (name, version, platform)
);

CREATE INDEX idx_gem_versions_name ON gem_versions(name);
CREATE INDEX idx_gem_versions_status ON gem_versions(status);
CREATE INDEX idx_gv_available ON gem_versions(available_after);

CREATE TABLE gem_symbols (
id INTEGER PRIMARY KEY AUTOINCREMENT,
gem_name TEXT NOT NULL,
gem_version TEXT NOT NULL,
gem_platform TEXT,
file_path TEXT NOT NULL,
symbol_type TEXT NOT NULL,
symbol_name TEXT NOT NULL,
parent_name TEXT,
line_number INTEGER,
created_at TIMESTAMP DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
UNIQUE(gem_name, gem_version, gem_platform, file_path, symbol_name)
);

CREATE INDEX idx_gem_symbols_name ON gem_symbols(symbol_name);
CREATE INDEX idx_gem_symbols_gem ON gem_symbols(gem_name, gem_version);
6 changes: 0 additions & 6 deletions crates/vein-adapter/src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,14 +128,8 @@ pub trait CacheBackendTrait: Send + Sync {
name: &str,
) -> impl Future<Output = Result<Vec<GemVersion>>> + Send;

fn quarantine_table_exists(&self) -> impl Future<Output = Result<bool>> + Send;

fn run_quarantine_migrations(&self) -> impl Future<Output = Result<()>> + Send;

// ==================== Symbol Indexing Methods ====================

fn run_symbols_migrations(&self) -> impl Future<Output = Result<()>> + Send;

fn insert_symbols(
&self,
gem_name: &str,
Expand Down
Loading