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
1 change: 0 additions & 1 deletion .base-image-digest

This file was deleted.

97 changes: 34 additions & 63 deletions .github/workflows/docker-build.yml
Original file line number Diff line number Diff line change
@@ -1,75 +1,35 @@
name: Build and Push Docker Image
on:
schedule:
- cron: '0 8 * * *'
push:
branches: [master]
paths:
- 'Dockerfile'
- '.opencode-version'
- 'docker-entrypoint.sh'
- 'config/opencode.json'
pull_request:
paths:
- 'Dockerfile'
- '.opencode-version'
- 'docker-entrypoint.sh'
- 'config/opencode.json'
workflow_dispatch:
inputs:
force:
description: 'Force build even if no changes detected'
push_image:
description: 'Push the built image to Docker Hub'
required: false
default: false
default: true
type: boolean

concurrency:
group: docker-build
group: docker-build-${{ github.ref }}
cancel-in-progress: false
permissions:
contents: write
jobs:
check-for-updates:
runs-on: ubuntu-latest
outputs:
should_build: ${{ steps.check.outputs.changed }}
force_build: ${{ github.event.inputs.force == 'true' }}
steps:
- name: Checkout
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Check for updates
id: check
run: |
CHANGED="false"
COMMIT_MSG=""

# Check base image digest
CURRENT_DIGEST=$(curl -s "https://registry.hub.docker.com/v2/repositories/library/node/tags/alpine" | jq -r '.digest')
STORED_DIGEST=$(cat .base-image-digest 2>/dev/null || echo "")

if [ "$CURRENT_DIGEST" != "$STORED_DIGEST" ]; then
echo "Base image changed: $STORED_DIGEST -> $CURRENT_DIGEST"
echo "$CURRENT_DIGEST" > .base-image-digest
CHANGED="true"
COMMIT_MSG="Update base image digest"
fi

# Check opencode-ai version
CURRENT_OPENCODE=$(curl -s https://registry.npmjs.org/opencode-ai/latest | jq -r '.version')
STORED_OPENCODE=$(cat .opencode-version 2>/dev/null || echo "")

if [ "$CURRENT_OPENCODE" != "$STORED_OPENCODE" ]; then
echo "opencode-ai changed: $STORED_OPENCODE -> $CURRENT_OPENCODE"
echo "$CURRENT_OPENCODE" > .opencode-version
CHANGED="true"
if [ -n "$COMMIT_MSG" ]; then
COMMIT_MSG="$COMMIT_MSG and opencode-ai to v$CURRENT_OPENCODE"
else
COMMIT_MSG="Update opencode-ai to v$CURRENT_OPENCODE"
fi
fi
permissions:
contents: read

echo "changed=$CHANGED" >> $GITHUB_OUTPUT
echo "commit_msg=$COMMIT_MSG" >> $GITHUB_OUTPUT
- name: Update version files
if: steps.check.outputs.changed == 'true'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add .base-image-digest .opencode-version
git commit -m "${{ steps.check.outputs.commit_msg }}"
git push https://x-access-token:${{ secrets.PAT_TOKEN }}@github.com/${{ github.repository }}.git HEAD:master
jobs:
build-and-push:
needs: check-for-updates
if: needs.check-for-updates.outputs.should_build == 'true' || needs.check-for-updates.outputs.force_build == 'true'
runs-on: ubuntu-latest
steps:
- name: Checkout
Expand All @@ -78,20 +38,31 @@ jobs:
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Resolve push flag
id: cfg
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
echo "push=false" >> "$GITHUB_OUTPUT"
elif [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "push=${{ inputs.push_image }}" >> "$GITHUB_OUTPUT"
else
echo "push=true" >> "$GITHUB_OUTPUT"
fi
- name: Login to Docker Hub
if: steps.cfg.outputs.push == 'true'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Generate date tag
id: date
run: echo "tag=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
run: echo "tag=$(date +'%Y-%m-%d')" >> "$GITHUB_OUTPUT"
- name: Build and Push
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
push: ${{ steps.cfg.outputs.push }}
no-cache: true
tags: |
vinnyahh/opencode-box:${{ steps.date.outputs.tag }}
Expand Down
20 changes: 15 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,23 +1,33 @@
FROM node:alpine

# Populated automatically by Docker Buildx per target platform (amd64 / arm64).
ARG TARGETARCH

LABEL org.opencontainers.image.title="opencode-box" \
org.opencontainers.image.description="OpenCode AI coding agent in a container, with Node (npx) and Python (uvx) MCP support" \
org.opencontainers.image.source="https://github.com/vinnyang/opencode-box" \
org.opencontainers.image.licenses="MIT"

# Pin opencode to the tracked version for reproducible builds (the installer reads $VERSION).
# Pin opencode to the tracked version for reproducible builds.
# Copied first so a version bump invalidates the install layer cache.
COPY .opencode-version /tmp/oc-version

RUN apk upgrade --no-cache && \
apk add --no-cache git ripgrep jq python3 py3-pip curl bash tar && \
pip3 install --break-system-packages uv && \
curl -fsSL https://opencode.ai/install | VERSION="$(cat /tmp/oc-version)" bash && \
rm /tmp/oc-version
OC_VER="$(cat /tmp/oc-version)" && \
case "$TARGETARCH" in \
amd64) OC_ARCH="x64" ;; \
arm64) OC_ARCH="arm64" ;; \
*) echo "unsupported TARGETARCH: '$TARGETARCH'" >&2; exit 1 ;; \
esac && \
curl -fsSL -o /tmp/oc.tar.gz \
"https://github.com/anomalyco/opencode/releases/download/v${OC_VER}/opencode-linux-${OC_ARCH}-musl.tar.gz" && \
tar -xzf /tmp/oc.tar.gz -C /usr/local/bin opencode && \
chmod 755 /usr/local/bin/opencode && \
rm /tmp/oc.tar.gz /tmp/oc-version
# Need to compile native MCP deps (Python wheels / node-gyp)? add: build-base python3-dev

ENV PATH="/root/.opencode/bin:$PATH"

RUN mkdir -p /config /data/sessions /data/snapshots /data/log /projects

COPY docker-entrypoint.sh /usr/local/bin/
Expand Down
56 changes: 56 additions & 0 deletions renovate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended",
"docker:enableMajor",
"docker:pinDigests",
"helpers:pinGitHubActionDigests"
],
"labels": ["dependencies"],
"schedule": ["before 4am"],
"timezone": "America/New_York",
"packageRules": [
{
"description": "Auto-merge Docker digest updates (very low risk)",
"matchDatasources": ["docker"],
"matchUpdateTypes": ["digest"],
"automerge": true,
"automergeType": "branch"
},
{
"description": "Auto-merge Docker minor/patch",
"matchDatasources": ["docker"],
"matchUpdateTypes": ["minor", "patch"],
"automerge": true
},
{
"description": "Auto-merge npm patch updates",
"matchDatasources": ["npm"],
"matchUpdateTypes": ["patch"],
"automerge": true
},
{
"description": "Auto-merge GitHub Actions updates",
"matchManagers": ["github-actions"],
"automerge": true
},
{
"description": "Auto-merge opencode patch releases",
"matchDatasources": ["github-releases"],
"matchPackageNames": ["anomalyco/opencode"],
"matchUpdateTypes": ["patch"],
"automerge": true
}
],
"customManagers": [
{
"customType": "regex",
"description": "Track opencode from GitHub releases (same source as the installed binary)",
"managerFilePatterns": ["/(^|/)\\.opencode-version$/"],
"matchStrings": ["(?<currentValue>\\S+)"],
"depNameTemplate": "anomalyco/opencode",
"datasourceTemplate": "github-releases",
"extractVersionTemplate": "^v(?<version>.+)$"
}
]
}
Loading