mirror of
https://github.com/headroomlabs-ai/headroom.git
synced 2026-08-27 14:17:10 -04:00
fix(docker): ship Bedrock auth and current registry (#2982)
## Description Fixes #1551 and #1692. Every published Headroom Docker image now installs the existing `bedrock` extra, so `--backend bedrock` can authenticate with temporary STS, SSO, and credential-process credentials instead of failing because `botocore` is absent. Public Docker instructions now consistently use `ghcr.io/headroomlabs-ai/headroom`. Several still pointed at the old personal package, which is frozen at 0.27.0 and caused users to report that no latest image existed. ## Type of Change - [x] Bug fix - [ ] New feature - [ ] Breaking change - [x] Documentation update - [x] Build / CI ## Changes Made - Add `bedrock` to the standalone Dockerfile default extras. - Add `bedrock` to all nine root/code/slim/nonroot bake targets. - Replace obsolete personal GHCR references in README, llms.txt, Compose guidance, testing guidance, and wiki docs. - Add release contract tests for Bedrock dependencies and the current organization registry. ## Testing - [x] Focused Docker release and Bedrock preflight tests pass. - [x] Full updater suites pass: 69 tests. - [x] `uv run ruff check tests/test_release_workflows.py` - [x] `docker buildx bake --print` - [x] `git diff --check` ## Real Behavior Proof Before this change, every published bake target installed only `proxy` or `proxy,code`, so `AWS_SESSION_TOKEN` selected an unavailable botocore path. Public copy-paste commands also referenced `ghcr.io/chopratejas/headroom`, which the existing migration code and changelog identify as frozen at 0.27.0. After this change, all nine parsed bake targets install `bedrock`; the regression resolves that package extra and confirms `boto3` plus `botocore`. Every public Docker instruction covered by the contract names `ghcr.io/headroomlabs-ai/headroom`. ## Runtime Rollout Safety This changes image contents and documentation only; proxy routing and non-Docker installs are unchanged. Static AWS credentials remain unaffected. Existing manifests using the deprecated image continue to be migrated by the established install-state logic. Rollback is a Docker/bake extras and documentation revert. ## Review Readiness - [x] Two related Docker blockers batched in one PR - [x] Regression coverage included - [x] No unrelated lockfile changes - [x] Ready for review
This commit is contained in:
parent
ddd2a259ec
commit
eafdf11a2c
9 changed files with 74 additions and 19 deletions
|
|
@ -47,7 +47,10 @@ COPY Cargo.toml Cargo.lock rust-toolchain.toml ./
|
||||||
COPY crates/ crates/
|
COPY crates/ crates/
|
||||||
COPY headroom/ headroom/
|
COPY headroom/ headroom/
|
||||||
|
|
||||||
ARG HEADROOM_EXTRAS=proxy,code
|
# The standalone Dockerfile must support every backend advertised by
|
||||||
|
# `headroom proxy --backend`, including Bedrock temporary/SSO credentials.
|
||||||
|
# Those credentials require botocore (GH #1551), supplied by [bedrock].
|
||||||
|
ARG HEADROOM_EXTRAS=proxy,code,bedrock
|
||||||
RUN --mount=type=cache,target=/root/.cache/uv \
|
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||||
--mount=type=cache,target=/usr/local/cargo/registry \
|
--mount=type=cache,target=/usr/local/cargo/registry \
|
||||||
--mount=type=cache,target=/usr/local/cargo/git \
|
--mount=type=cache,target=/usr/local/cargo/git \
|
||||||
|
|
|
||||||
|
|
@ -415,7 +415,7 @@ Everything in this repo stays open source (Apache 2.0). The managed offering is
|
||||||
uv tool install --python 3.13 "headroom-ai[all]" # CLI, isolated app env
|
uv tool install --python 3.13 "headroom-ai[all]" # CLI, isolated app env
|
||||||
pip install "headroom-ai[all]" # Python, everything — includes the `headroom` CLI
|
pip install "headroom-ai[all]" # Python, everything — includes the `headroom` CLI
|
||||||
npm install headroom-ai # TypeScript SDK (library only — no `headroom` CLI)
|
npm install headroom-ai # TypeScript SDK (library only — no `headroom` CLI)
|
||||||
docker pull ghcr.io/chopratejas/headroom:latest
|
docker pull ghcr.io/headroomlabs-ai/headroom:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
Granular extras: `[proxy]`, `[mcp]`, `[ml]` (Kompress-v2-base), `[code]`, `[memory]`, `[vector]` (optional HNSW backend — needs a C++ toolchain, not in `[all]`), `[relevance]`, `[image]`, `[agno]`, `[langchain]`, `[evals]`, `[pytorch-mps]` (Apple-GPU memory-embedder offload — set `HEADROOM_EMBEDDER_RUNTIME=pytorch_mps`). Requires **Python 3.10+**.
|
Granular extras: `[proxy]`, `[mcp]`, `[ml]` (Kompress-v2-base), `[code]`, `[memory]`, `[vector]` (optional HNSW backend — needs a C++ toolchain, not in `[all]`), `[relevance]`, `[image]`, `[agno]`, `[langchain]`, `[evals]`, `[pytorch-mps]` (Apple-GPU memory-embedder offload — set `HEADROOM_EMBEDDER_RUNTIME=pytorch_mps`). Requires **Python 3.10+**.
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,7 @@ There is **no native Windows wheel yet**, so pick one:
|
||||||
|
|
||||||
**A. Mechanism test (easiest — Docker Desktop or WSL2):**
|
**A. Mechanism test (easiest — Docker Desktop or WSL2):**
|
||||||
```powershell
|
```powershell
|
||||||
$env:HEADROOM_DOCKER_IMAGE = "ghcr.io/chopratejas/headroom:<branch-tag>" # ask the maintainer for the tag
|
$env:HEADROOM_DOCKER_IMAGE = "ghcr.io/headroomlabs-ai/headroom:<branch-tag>" # ask the maintainer for the tag
|
||||||
# run the Docker-native installer (scripts/install.ps1), then:
|
# run the Docker-native installer (scripts/install.ps1), then:
|
||||||
$env:GITHUB_COPILOT_TOKEN = "<your-token>"
|
$env:GITHUB_COPILOT_TOKEN = "<your-token>"
|
||||||
headroom wrap copilot --subscription -- --model gpt-4o -p "Reply with: HEADROOM_OK"
|
headroom wrap copilot --subscription -- --model gpt-4o -p "Reply with: HEADROOM_OK"
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ target "runtime-default" {
|
||||||
inherits = ["_common", "docker-metadata-action"]
|
inherits = ["_common", "docker-metadata-action"]
|
||||||
target = "runtime"
|
target = "runtime"
|
||||||
args = {
|
args = {
|
||||||
HEADROOM_EXTRAS = "proxy"
|
HEADROOM_EXTRAS = "proxy,bedrock"
|
||||||
RUNTIME_USER = "nonroot"
|
RUNTIME_USER = "nonroot"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -19,7 +19,7 @@ target "runtime" {
|
||||||
inherits = ["_common", "docker-metadata-action"]
|
inherits = ["_common", "docker-metadata-action"]
|
||||||
target = "runtime"
|
target = "runtime"
|
||||||
args = {
|
args = {
|
||||||
HEADROOM_EXTRAS = "proxy"
|
HEADROOM_EXTRAS = "proxy,bedrock"
|
||||||
RUNTIME_USER = "root"
|
RUNTIME_USER = "root"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -28,7 +28,7 @@ target "runtime-nonroot" {
|
||||||
inherits = ["_common", "docker-metadata-action"]
|
inherits = ["_common", "docker-metadata-action"]
|
||||||
target = "runtime"
|
target = "runtime"
|
||||||
args = {
|
args = {
|
||||||
HEADROOM_EXTRAS = "proxy"
|
HEADROOM_EXTRAS = "proxy,bedrock"
|
||||||
RUNTIME_USER = "nonroot"
|
RUNTIME_USER = "nonroot"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -37,7 +37,7 @@ target "runtime-code" {
|
||||||
inherits = ["_common", "docker-metadata-action"]
|
inherits = ["_common", "docker-metadata-action"]
|
||||||
target = "runtime"
|
target = "runtime"
|
||||||
args = {
|
args = {
|
||||||
HEADROOM_EXTRAS = "proxy,code"
|
HEADROOM_EXTRAS = "proxy,code,bedrock"
|
||||||
RUNTIME_USER = "root"
|
RUNTIME_USER = "root"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -46,7 +46,7 @@ target "runtime-code-nonroot" {
|
||||||
inherits = ["_common", "docker-metadata-action"]
|
inherits = ["_common", "docker-metadata-action"]
|
||||||
target = "runtime"
|
target = "runtime"
|
||||||
args = {
|
args = {
|
||||||
HEADROOM_EXTRAS = "proxy,code"
|
HEADROOM_EXTRAS = "proxy,code,bedrock"
|
||||||
RUNTIME_USER = "nonroot"
|
RUNTIME_USER = "nonroot"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -55,7 +55,7 @@ target "runtime-slim" {
|
||||||
inherits = ["_common", "docker-metadata-action"]
|
inherits = ["_common", "docker-metadata-action"]
|
||||||
target = "runtime-slim"
|
target = "runtime-slim"
|
||||||
args = {
|
args = {
|
||||||
HEADROOM_EXTRAS = "proxy"
|
HEADROOM_EXTRAS = "proxy,bedrock"
|
||||||
RUNTIME_USER = "root"
|
RUNTIME_USER = "root"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -64,7 +64,7 @@ target "runtime-slim-nonroot" {
|
||||||
inherits = ["_common", "docker-metadata-action"]
|
inherits = ["_common", "docker-metadata-action"]
|
||||||
target = "runtime-slim"
|
target = "runtime-slim"
|
||||||
args = {
|
args = {
|
||||||
HEADROOM_EXTRAS = "proxy"
|
HEADROOM_EXTRAS = "proxy,bedrock"
|
||||||
RUNTIME_USER = "nonroot"
|
RUNTIME_USER = "nonroot"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -73,7 +73,7 @@ target "runtime-code-slim" {
|
||||||
inherits = ["_common", "docker-metadata-action"]
|
inherits = ["_common", "docker-metadata-action"]
|
||||||
target = "runtime-slim"
|
target = "runtime-slim"
|
||||||
args = {
|
args = {
|
||||||
HEADROOM_EXTRAS = "proxy,code"
|
HEADROOM_EXTRAS = "proxy,code,bedrock"
|
||||||
RUNTIME_USER = "root"
|
RUNTIME_USER = "root"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -82,7 +82,7 @@ target "runtime-code-slim-nonroot" {
|
||||||
inherits = ["_common", "docker-metadata-action"]
|
inherits = ["_common", "docker-metadata-action"]
|
||||||
target = "runtime-slim"
|
target = "runtime-slim"
|
||||||
args = {
|
args = {
|
||||||
HEADROOM_EXTRAS = "proxy,code"
|
HEADROOM_EXTRAS = "proxy,code,bedrock"
|
||||||
RUNTIME_USER = "nonroot"
|
RUNTIME_USER = "nonroot"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
# 3. point your LLM client at http://localhost:8787 (proxy)
|
# 3. point your LLM client at http://localhost:8787 (proxy)
|
||||||
#
|
#
|
||||||
# Just want the proxy without the memory features? You can run the proxy image
|
# Just want the proxy without the memory features? You can run the proxy image
|
||||||
# on its own (`docker run -p 8787:8787 ghcr.io/chopratejas/headroom`); the two
|
# on its own (`docker run -p 8787:8787 ghcr.io/headroomlabs-ai/headroom`); the two
|
||||||
# database services below are only required for the memory/relevance features.
|
# database services below are only required for the memory/relevance features.
|
||||||
#
|
#
|
||||||
# Ports exposed on the host:
|
# Ports exposed on the host:
|
||||||
|
|
|
||||||
2
llms.txt
2
llms.txt
|
|
@ -19,7 +19,7 @@ The canonical, always-current documentation index lives at the docs site below.
|
||||||
|
|
||||||
- Python: `pip install headroom-ai` (add `[all]` for every optional extra)
|
- Python: `pip install headroom-ai` (add `[all]` for every optional extra)
|
||||||
- TypeScript / Node: `npm install headroom-ai` (or `pnpm add headroom-ai`, `bun add headroom-ai`)
|
- TypeScript / Node: `npm install headroom-ai` (or `pnpm add headroom-ai`, `bun add headroom-ai`)
|
||||||
- Docker: `docker run -p 8787:8787 ghcr.io/chopratejas/headroom:latest`
|
- Docker: `docker run -p 8787:8787 ghcr.io/headroomlabs-ai/headroom:latest`
|
||||||
- Run the proxy: `headroom proxy --port 8787` then point any client at `http://127.0.0.1:8787`
|
- Run the proxy: `headroom proxy --port 8787` then point any client at `http://127.0.0.1:8787`
|
||||||
- Wrap an agent in one command: `headroom wrap claude` (also: `codex`, `copilot`, `cursor`, `aider`, `opencode`, `cline`, `continue`, `goose`, `openhands`, `openclaw`, `vibe`, `omp`)
|
- Wrap an agent in one command: `headroom wrap claude` (also: `codex`, `copilot`, `cursor`, `aider`, `opencode`, `cline`, `continue`, `goose`, `openhands`, `openclaw`, `vibe`, `omp`)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,62 @@ from pathlib import Path
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import yaml
|
import yaml
|
||||||
|
from packaging.requirements import Requirement
|
||||||
|
|
||||||
|
try:
|
||||||
|
import tomllib
|
||||||
|
except ModuleNotFoundError: # pragma: no cover - Python 3.10 fallback
|
||||||
|
import tomli as tomllib # type: ignore[no-redef]
|
||||||
|
|
||||||
ROOT = Path(__file__).resolve().parent.parent
|
ROOT = Path(__file__).resolve().parent.parent
|
||||||
|
|
||||||
|
|
||||||
|
def test_every_published_docker_variant_includes_bedrock_auth_dependencies() -> None:
|
||||||
|
"""Every image that advertises ``--backend bedrock`` must ship botocore.
|
||||||
|
|
||||||
|
Temporary AWS credentials take LiteLLM's botocore-backed authentication
|
||||||
|
path. The default images previously installed only ``proxy``/``code``, so
|
||||||
|
the documented Docker Bedrock command failed at runtime with
|
||||||
|
``No module named 'botocore'`` (#1551). Keep the standalone Dockerfile and
|
||||||
|
every bake target on the existing ``bedrock`` package extra.
|
||||||
|
"""
|
||||||
|
dockerfile = (ROOT / "Dockerfile").read_text(encoding="utf-8")
|
||||||
|
assert "ARG HEADROOM_EXTRAS=proxy,code,bedrock" in dockerfile
|
||||||
|
|
||||||
|
bake = (ROOT / "docker-bake.hcl").read_text(encoding="utf-8")
|
||||||
|
extras_lines = [
|
||||||
|
line.strip() for line in bake.splitlines() if line.strip().startswith("HEADROOM_EXTRAS =")
|
||||||
|
]
|
||||||
|
assert len(extras_lines) == 9
|
||||||
|
assert all("bedrock" in line for line in extras_lines), extras_lines
|
||||||
|
|
||||||
|
project = tomllib.loads((ROOT / "pyproject.toml").read_text(encoding="utf-8"))
|
||||||
|
bedrock_names = {
|
||||||
|
Requirement(requirement).name
|
||||||
|
for requirement in project["project"]["optional-dependencies"]["bedrock"]
|
||||||
|
}
|
||||||
|
assert {"boto3", "botocore"} <= bedrock_names
|
||||||
|
|
||||||
|
|
||||||
|
def test_public_docker_instructions_use_the_current_organization_package() -> None:
|
||||||
|
"""Do not send users back to the personal GHCR package frozen at 0.27.0."""
|
||||||
|
public_docs = (
|
||||||
|
"README.md",
|
||||||
|
"llms.txt",
|
||||||
|
"docker-compose.yml",
|
||||||
|
"TESTING-copilot-subscription.md",
|
||||||
|
"wiki/cli.md",
|
||||||
|
"wiki/docker-install.md",
|
||||||
|
)
|
||||||
|
deprecated = "ghcr.io/chopratejas/headroom"
|
||||||
|
current = "ghcr.io/headroomlabs-ai/headroom"
|
||||||
|
|
||||||
|
for relative_path in public_docs:
|
||||||
|
content = (ROOT / relative_path).read_text(encoding="utf-8")
|
||||||
|
assert deprecated not in content, relative_path
|
||||||
|
assert current in content, relative_path
|
||||||
|
|
||||||
|
|
||||||
def test_docker_workflow_normalizes_repository_name_for_signing() -> None:
|
def test_docker_workflow_normalizes_repository_name_for_signing() -> None:
|
||||||
content = (ROOT / ".github" / "workflows" / "docker.yml").read_text(encoding="utf-8")
|
content = (ROOT / ".github" / "workflows" / "docker.yml").read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -637,7 +637,7 @@ Options:
|
||||||
(already the default).
|
(already the default).
|
||||||
--image TEXT Docker image to use when runtime=docker or
|
--image TEXT Docker image to use when runtime=docker or
|
||||||
preset=persistent-docker. [default:
|
preset=persistent-docker. [default:
|
||||||
ghcr.io/chopratejas/headroom:latest]
|
ghcr.io/headroomlabs-ai/headroom:latest]
|
||||||
-?, --help Show this message and exit.
|
-?, --help Show this message and exit.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -665,7 +665,7 @@ headroom install apply --preset persistent-docker --scope user
|
||||||
| `--memory` | off | Enable persistent memory in the managed runtime |
|
| `--memory` | off | Enable persistent memory in the managed runtime |
|
||||||
| `--telemetry` | off | Opt in to anonymous telemetry (off by default) |
|
| `--telemetry` | off | Opt in to anonymous telemetry (off by default) |
|
||||||
| `--no-telemetry` | off | Force anonymous telemetry off (already the default) |
|
| `--no-telemetry` | off | Force anonymous telemetry off (already the default) |
|
||||||
| `--image` | `ghcr.io/chopratejas/headroom:latest` | Docker image for Docker-backed installs |
|
| `--image` | `ghcr.io/headroomlabs-ai/headroom:latest` | Docker image for Docker-backed installs |
|
||||||
|
|
||||||
`apply` stores a manifest under
|
`apply` stores a manifest under
|
||||||
`${HEADROOM_WORKSPACE_DIR}/deploy/<profile>/manifest.json` (default
|
`${HEADROOM_WORKSPACE_DIR}/deploy/<profile>/manifest.json` (default
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ irm https://raw.githubusercontent.com/chopratejas/headroom/main/scripts/install.
|
||||||
## What the installer does
|
## What the installer does
|
||||||
|
|
||||||
1. Verifies Docker is installed and available.
|
1. Verifies Docker is installed and available.
|
||||||
2. Pulls `ghcr.io/chopratejas/headroom:latest` by default, or reuses / pulls `HEADROOM_DOCKER_IMAGE` when you set a custom image override.
|
2. Pulls `ghcr.io/headroomlabs-ai/headroom:latest` by default, or reuses / pulls `HEADROOM_DOCKER_IMAGE` when you set a custom image override.
|
||||||
3. Installs a `headroom` wrapper into `~/.local/bin` or `~/bin`.
|
3. Installs a `headroom` wrapper into `~/.local/bin` or `~/bin`.
|
||||||
4. Updates shell startup files so the wrapper directory is on `PATH`.
|
4. Updates shell startup files so the wrapper directory is on `PATH`.
|
||||||
|
|
||||||
|
|
@ -41,7 +41,7 @@ The wrapper keeps Headroom inside Docker and mounts host state back into the con
|
||||||
|
|
||||||
Port `8787` stays the default, so `http://localhost:8787` works the same way as a native install.
|
Port `8787` stays the default, so `http://localhost:8787` works the same way as a native install.
|
||||||
|
|
||||||
Published releases also push versioned GHCR tags such as `ghcr.io/chopratejas/headroom:0.5.26`, and those images are built with the same synced package version used for the matching PyPI and npm release.
|
Published releases also push versioned GHCR tags such as `ghcr.io/headroomlabs-ai/headroom:0.35.0`, and those images are built with the same synced package version used for the matching PyPI and npm release.
|
||||||
|
|
||||||
## How the wrapper behaves
|
## How the wrapper behaves
|
||||||
|
|
||||||
|
|
@ -63,7 +63,7 @@ docker run --rm -it \
|
||||||
-p 8787:8787 \
|
-p 8787:8787 \
|
||||||
-v "$PWD:/workspace" \
|
-v "$PWD:/workspace" \
|
||||||
-w /workspace \
|
-w /workspace \
|
||||||
ghcr.io/chopratejas/headroom:latest \
|
ghcr.io/headroomlabs-ai/headroom:latest \
|
||||||
headroom proxy --host 0.0.0.0 --port 8787
|
headroom proxy --host 0.0.0.0 --port 8787
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue