Repository navigation
Add bobshell container example with release CI #53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,158 @@ | ||
| name: Build Bobshell Container | ||
|
|
||
| on: | ||
| push: | ||
| branches: | ||
| - main | ||
| paths: | ||
| - 'infra/bobshell/**' | ||
| - '.github/workflows/build-bobshell.yml' | ||
| pull_request: | ||
| branches: | ||
| - main | ||
| paths: | ||
| - 'infra/bobshell/**' | ||
| - '.github/workflows/build-bobshell.yml' | ||
| release: | ||
| types: | ||
| - published | ||
| workflow_dispatch: | ||
|
|
||
| env: | ||
| REGISTRY: ghcr.io | ||
| IMAGE: ghcr.io/ibm/cfex-bobshell | ||
|
|
||
| jobs: | ||
| build: | ||
| name: Build ${{ matrix.platform }} | ||
| runs-on: ${{ matrix.runner }} | ||
| permissions: | ||
| contents: read | ||
| packages: write | ||
|
|
||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| platform: | ||
| - linux/amd64 | ||
| - linux/arm64 | ||
| include: | ||
| - platform: linux/amd64 | ||
| runner: ubuntu-24.04 | ||
| suffix: amd64 | ||
| - platform: linux/arm64 | ||
| runner: ubuntu-24.04-arm | ||
| suffix: arm64 | ||
|
|
||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| persist-credentials: false | ||
|
|
||
| - name: Set up Docker Buildx | ||
| uses: docker/setup-buildx-action@v3 | ||
| with: | ||
| version: latest | ||
|
|
||
| - name: Log in to Container Registry | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| registry: ${{ env.REGISTRY }} | ||
| username: ${{ github.actor }} | ||
| password: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Extract metadata | ||
| id: meta | ||
| uses: docker/metadata-action@v5 | ||
| with: | ||
| images: ${{ env.IMAGE }} | ||
| tags: | | ||
| type=raw,value=${{ matrix.suffix }}-${{ github.sha }} | ||
|
|
||
| - name: Build and push container image | ||
| id: build | ||
| uses: docker/build-push-action@v5 | ||
| with: | ||
| context: ./infra/bobshell | ||
| file: ./infra/bobshell/Containerfile | ||
| platforms: ${{ matrix.platform }} | ||
| push: true | ||
| tags: ${{ steps.meta.outputs.tags }} | ||
| labels: ${{ steps.meta.outputs.labels }} | ||
| cache-from: type=gha,scope=bobshell-${{ matrix.suffix }} | ||
| cache-to: type=gha,mode=max,scope=bobshell-${{ matrix.suffix }} | ||
| provenance: false | ||
|
|
||
| - name: Export digest | ||
| run: | | ||
| mkdir -p /tmp/digests | ||
| digest="${{ steps.build.outputs.digest }}" | ||
| touch "/tmp/digests/${digest#sha256:}" | ||
| echo "Digest for bobshell-${{ matrix.suffix }}: $digest" | ||
|
|
||
| - name: Upload digest | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: digest-bobshell-${{ matrix.suffix }} | ||
| path: /tmp/digests/* | ||
| if-no-files-found: error | ||
| retention-days: 1 | ||
|
|
||
| manifest: | ||
| name: Create Manifest | ||
| needs: build | ||
| runs-on: ubuntu-24.04 | ||
| permissions: | ||
| contents: read | ||
| packages: write | ||
|
|
||
| steps: | ||
| - name: Set up Docker Buildx | ||
| uses: docker/setup-buildx-action@v3 | ||
| with: | ||
| version: latest | ||
|
|
||
| - name: Log in to Container Registry | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| registry: ${{ env.REGISTRY }} | ||
| username: ${{ github.actor }} | ||
| password: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Create and push manifest | ||
| run: | | ||
| SHA=${{ github.sha }} | ||
| IMAGE=${{ env.IMAGE }} | ||
|
|
||
| echo "Creating multiplatform manifest for bobshell..." | ||
|
|
||
| IMAGES=( | ||
| "${IMAGE}:amd64-${SHA}" | ||
| "${IMAGE}:arm64-${SHA}" | ||
| ) | ||
|
|
||
| if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then | ||
| # Release publication: tag with the release name and latest. | ||
| docker buildx imagetools create \ | ||
| --tag "${IMAGE}:${SHA}" \ | ||
| --tag "${IMAGE}:${GITHUB_REF_NAME}" \ | ||
| --tag "${IMAGE}:latest" \ | ||
| "${IMAGES[@]}" | ||
| elif [[ "${{ github.event_name }}" == "push" ]] || [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then | ||
| docker buildx imagetools create \ | ||
| --tag "${IMAGE}:${SHA}" \ | ||
| --tag "${IMAGE}:latest" \ | ||
| "${IMAGES[@]}" | ||
| else | ||
| docker buildx imagetools create \ | ||
| --tag "${IMAGE}:${SHA}" \ | ||
| "${IMAGES[@]}" | ||
| fi | ||
|
|
||
| echo "Manifest created successfully" | ||
|
|
||
| - name: Inspect manifest | ||
| run: | | ||
| echo "Inspecting multiplatform manifest..." | ||
| docker buildx imagetools inspect "${{ env.IMAGE }}:${{ github.sha }}" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| .git | ||
| .gitignore | ||
| .env | ||
| secrets/ | ||
| workspace/ | ||
| docker-compose.yml | ||
| README.md |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| # Never commit secrets or local state | ||
| secrets/ | ||
| workspace/ | ||
| .env |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| # syntax=docker/dockerfile:1 | ||
| # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | ||
| # bobshell - IBM Bob Shell (agentic CLI) container image | ||
| # | ||
| # Based on ubi10/nodejs-24, which provides Node.js 24 (Bob Shell requires | ||
| # Node.js >= 22.15). Bob Shell is installed with the official installer, the | ||
| # IBM license is pre-accepted, and the /workspace directory is pre-trusted so | ||
| # the CLI runs non-interactively. | ||
| # | ||
| # Build (from this directory): | ||
| # docker build -f Containerfile -t bobshell . | ||
| # | ||
| # Pin a Bob Shell version: | ||
| # docker build --build-arg BOB_VERSION=2.0.5 -t bobshell . | ||
| # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | ||
|
|
||
| FROM registry.access.redhat.com/ubi10/nodejs-24-minimal:latest | ||
|
|
||
| # Workspace Bob Shell operates in. Owned by the default user (1001), group | ||
| # root with g=u so arbitrary UIDs in gid 0 (OpenShift-style) can also use it. | ||
| USER root | ||
| RUN mkdir -p /workspace \ | ||
| && chown 1001:0 /workspace \ | ||
| && chmod g=u /workspace | ||
| COPY --chmod=0755 entrypoint.sh /usr/local/bin/bob-entrypoint | ||
| USER 1001 | ||
|
|
||
| # Install Bob Shell via the official installer. The installer verifies the | ||
| # package SHA256 and runs `npm install -g`; the image's npm global prefix | ||
| # (/opt/app-root/src/.npm-global) is writable by user 1001 and on PATH. | ||
| ARG BOB_VERSION="" | ||
| RUN set -eu; \ | ||
| curl -fsSL https://bob.ibm.com/download/bobshell.sh -o /tmp/bobshell.sh; \ | ||
| if [ -n "${BOB_VERSION}" ]; then \ | ||
| bash /tmp/bobshell.sh --pm npm --version "${BOB_VERSION}"; \ | ||
| else \ | ||
| bash /tmp/bobshell.sh --pm npm; \ | ||
| fi; \ | ||
| rm -f /tmp/bobshell.sh /tmp/bobshell-*.tgz /tmp/bobshell-install.log; \ | ||
| command -v bob; \ | ||
| bob --version | ||
|
|
||
| # Pre-accept the IBM license and pre-trust /workspace from the command line | ||
| # so Bob Shell never blocks on first-run prompts. State file formats match | ||
| # what Bob Shell 2.0.x itself writes: | ||
| # ~/.bob/settings/settings.json <- "licenseConsent": true (`bob --accept-license`) | ||
| # ~/.bob/trustedFolders.json <- path -> "TRUST_PARENT" (`bob chat --trust`) | ||
| # The default CMD also passes --accept-license/--trust, so a fresh volume | ||
| # mounted over $HOME (shadowing these files) still starts without prompts. | ||
| RUN mkdir -p "${HOME}/.bob/settings" \ | ||
| && printf '{\n "licenseConsent": true\n}\n' > "${HOME}/.bob/settings/settings.json" \ | ||
| && printf '{\n "/workspace": "TRUST_PARENT"\n}\n' > "${HOME}/.bob/trustedFolders.json" | ||
|
|
||
| WORKDIR /workspace | ||
|
|
||
| LABEL org.opencontainers.image.title="bobshell" | ||
| LABEL org.opencontainers.image.description="IBM Bob Shell agentic CLI on UBI 10 / Node.js 24" | ||
| LABEL org.opencontainers.image.source="https://github.com/IBM/contextforge-examples" | ||
| LABEL org.opencontainers.image.vendor="IBM" | ||
| LABEL org.opencontainers.image.licenses="Apache-2.0" | ||
|
|
||
| ENTRYPOINT ["bob-entrypoint"] | ||
| CMD ["bob", "chat", "--accept-license", "--trust"] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| # Bobshell Container | ||
|
|
||
| [IBM Bob Shell](https://bob.ibm.com/docs/shell) (the `bob` agentic CLI) packaged | ||
| as a container image on `registry.access.redhat.com/ubi10/nodejs-24`. | ||
|
|
||
| - Bob Shell installed via the [official installer](https://bob.ibm.com/docs/shell/getting-started/install-and-setup) | ||
| (SHA256-verified, `npm install -g`). | ||
| - IBM license pre-accepted and `/workspace` pre-trusted at image build time, so | ||
| `bob` never blocks on first-run prompts (see | ||
| [trusted folders](https://bob.ibm.com/docs/shell/security/trusted-folders)). | ||
| - Runs as the non-root default user (UID 1001). | ||
| - Defaults to an interactive `bob chat` session when the container starts. | ||
|
|
||
| ## Configuration | ||
|
|
||
| The container accepts two configurations: | ||
|
|
||
| | Configuration | Mechanism | Container location | Effect | | ||
| | ------------- | --------- | ------------------ | ------ | | ||
| | Bobshell API secret | Compose `secrets:` (or `BOB_API_KEY` env var) | `/run/secrets/bob_api_key` | Exported as `BOB_API_KEY`; used for [API key authentication](https://bob.ibm.com/docs/shell/getting-started/install-and-setup#api-key-authentication) | | ||
| | MCP configuration | Compose `configs:` | `/etc/bob/mcp.json` | Installed as `~/.bob/settings/mcp.json` (user-global [`mcpServers`](https://bob.ibm.com/docs/shell/configuration/mcp/mcp-bobshell) config) unless one already exists | | ||
|
|
||
| An API key (scope **Inference**) is created in the | ||
| [Bob web portal](https://bob.ibm.com/login). | ||
|
|
||
| ## Quick start (Docker Compose) | ||
|
|
||
| ```bash | ||
| cd infra/bobshell | ||
|
|
||
| # Provide the API secret and a project directory | ||
| mkdir -p secrets workspace | ||
| printf '%s' 'your-bob-api-key' > secrets/bob_api_key.txt | ||
| # Change permission on the secrets directory: | ||
| chmod 700 secrets | ||
|
|
||
| # Change permissions on the secrets file: | ||
| chmod 600 secrets/bob_api_key.txt | ||
| # Edit the sample MCP configuration to taste | ||
| $EDITOR mcp.json | ||
|
|
||
| # Interactive session (image is pulled or built locally) | ||
| docker compose run --rm bobshell | ||
|
|
||
| # Headless task | ||
| docker compose run --rm bobshell bob run --accept-license --trust "Explain this project" | ||
|
|
||
| # Build locally instead of pulling the published image | ||
| docker compose build | ||
| ``` | ||
|
|
||
| `docker-compose.yml` mounts `./workspace` at `/workspace` (the pre-trusted | ||
| working directory) and persists `~/.bob` (sessions, tasks, settings) in the | ||
| `bob-state` named volume. | ||
|
|
||
| ## Plain Docker | ||
|
|
||
| ```bash | ||
| docker build -f Containerfile -t bobshell . | ||
|
|
||
| # Interactive | ||
| docker run --rm -it \ | ||
| -e BOB_API_KEY="your-bob-api-key" \ | ||
| -v "$PWD/workspace:/workspace" \ | ||
| bobshell | ||
|
|
||
| # Headless, with MCP config mounted | ||
| docker run --rm \ | ||
| -e BOB_API_KEY="your-bob-api-key" \ | ||
| -v "$PWD/mcp.json:/etc/bob/mcp.json:ro" \ | ||
| -v "$PWD/workspace:/workspace" \ | ||
| bobshell bob run --accept-license --trust "Summarize this repo" | ||
| ``` | ||
|
|
||
| Any command after the image name replaces the default `bob chat` invocation; | ||
| a leading flag (e.g. `--version`) is passed to `bob` itself. | ||
|
|
||
| ## Version pinning | ||
|
|
||
| The image installs the latest Bob Shell release by default. Pin a version at | ||
| build time: | ||
|
|
||
| ```bash | ||
| docker build --build-arg BOB_VERSION=2.0.5 -t bobshell . | ||
| ``` | ||
|
|
||
| ## Publishing (CI) | ||
|
|
||
| `.github/workflows/build-bobshell.yml` builds `linux/amd64` and `linux/arm64` | ||
| images and publishes a multi-arch manifest to | ||
| `ghcr.io/ibm/cfex-bobshell` on: | ||
|
|
||
| - pushes to `main` touching `infra/bobshell/**` (tagged with the commit SHA and `latest`), | ||
| - **release publication** (additionally tagged with the release tag), | ||
| - manual `workflow_dispatch`. | ||
|
|
||
| ## Security notes | ||
|
|
||
| - Never commit `secrets/` or set your API key in any tracked file; the local | ||
| `.gitignore` excludes them, and `.dockerignore` keeps them out of the build | ||
| context. | ||
| - The pre-trust entry covers `/workspace` only; Bob Shell's folder-trust | ||
| protections remain intact for everything else. | ||
| - Run the container with least privilege: it needs no host access beyond the | ||
| mounted workspace and outbound network to `bob.ibm.com`. | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.