diff --git a/.github/workflows/build-docker-opencode.yaml b/.github/workflows/build-docker-opencode.yaml new file mode 100644 index 00000000000..00e953ffbbd --- /dev/null +++ b/.github/workflows/build-docker-opencode.yaml @@ -0,0 +1,124 @@ +name: 'build opencode Docker image' + +on: + + workflow_dispatch: # Allow manual triggering from GitHub UI + + schedule: + - cron: '0 2,14 * * *' # runs at least twice every day: 2am and 2pm + + push: + paths: + - 'Dockerfile' + - '.github/workflows/build-docker-opencode.yaml' + - '.hadolint.yaml' + +jobs: + + build_docker_opencode: + runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + contents: read + packages: write + attestations: write + id-token: write + env: + DOCKER_FILE: Dockerfile + REGISTRY: ghcr.io + IMAGE_NAME: ghcr.io/${{ github.repository }} + OPENCODE_CONFIG: opencode.jsonc + + steps: + + - name: 'check environment' + run: | + docker --version + echo "npm version: $(npm --version)" + OPENCODE_VERSION=$(npm view opencode-ai version) + echo "opencode version: $OPENCODE_VERSION" + echo "OPENCODE_VERSION=$OPENCODE_VERSION" >> $GITHUB_ENV + echo "=== env vars ===" + printenv + + - name: 'checkout git code' + uses: actions/checkout@v5 + + - name: 'validate config files' + run: | + # check Dockerfile + docker run --rm -i hadolint/hadolint < $DOCKER_FILE + # check opencode config if it exists + if [[ -f $OPENCODE_CONFIG ]] + then + pipx install check-jsonschema + check-jsonschema --verbose --schemafile https://opencode.ai/config.json $OPENCODE_CONFIG + fi + + - name: 'login to GitHub Container Registry' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.IMAGE_NAME }} + tags: | + type=raw,value={{ date 'YYYY-MM-DD-HH-mm-ss' tz='Europe/Paris' }} + type=raw,latest + type=raw,opencode-${{ env.OPENCODE_VERSION }} + type=sha + + - name: 'set up Docker Buildx' + uses: docker/setup-buildx-action@v3 + env: + # to avoid unknown/unknown arch in container registry + BUILDX_NO_DEFAULT_ATTESTATIONS: 1 + with: + provenance: false # Disable provenance to avoid unknown/unknown + sbom: false # Disable sbom to avoid unknown/unknown + + - name: 'build and push to Github Container Registry' + uses: docker/build-push-action@v6 + with: + platforms: linux/amd64,linux/arm64 + push: true + file: ${{env.DOCKER_FILE}} + tags: | + ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + - name: 'run built image to validate' + run: | + set -x + CONTAINER_NAME="opencode-vibe" + # start detached container + docker run --detach --name "$CONTAINER_NAME" "$IMAGE_NAME:latest" + # validate user in container + WHOAMI=$(docker exec "$CONTAINER_NAME" whoami) + echo "opencode container whoami: $WHOAMI" + if [[ "$WHOAMI" != "opencode" ]]; + then + exit 1 + fi + # run version check in container + wget --quiet -O /usr/local/bin/semver https://raw.githubusercontent.com/fsaintjacques/semver-tool/master/src/semver + chmod +x /usr/local/bin/semver + OC_VERSION=$(docker exec "$CONTAINER_NAME" npm view opencode-ai version) + echo "opencode version: $OC_VERSION" + SEMVER_COMPARE=$(semver compare "$OC_VERSION" "$OPENCODE_VERSION") + if [[ "$SEMVER_COMPARE" -lt 0 ]]; + then + exit 1 + fi + # invoke opencode with free OpenRouter model + docker exec "$CONTAINER_NAME" opencode run --log-level INFO --model 'opencode/glm-4.7-free' 'who are you?' >> opencode.log + echo "opencode container invocation: $(cat opencode.log)" + grep --ignore-case 'opencode' opencode.log + # explicit successful exit to kill container + exit + diff --git a/.hadolint.yaml b/.hadolint.yaml new file mode 100644 index 00000000000..5956c65890a --- /dev/null +++ b/.hadolint.yaml @@ -0,0 +1,3 @@ +# https://github.com/hadolint/hadolint +ignored: + - DL3013 # warning: Pin versions in pip installs.` diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000000..d6e9f938b5c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,90 @@ +FROM debian:trixie +SHELL ["/bin/bash", "-c"] +# escape=\ + +ARG PYTHON_VERSION="3.13" +ARG OTEL_VERSION="0.143.0" + +ENV USER="opencode" +ENV HOME="/home/$USER" +WORKDIR "$HOME" + +# hadolint ignore=DL3008 +RUN apt-get update -y \ + && apt-get upgrade -y \ + && apt-get install -y --no-install-recommends ca-certificates procps jq curl wget unzip git gh nodejs npm \ + && apt-get install -y --no-install-recommends python${PYTHON_VERSION} python${PYTHON_VERSION}-venv python${PYTHON_VERSION} python3-pip \ + && python3 --version \ + && pip3 --version \ + # clean up \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +# OpenTelemetry install +ARG TARGETPLATFORM +# need to be explicitly set for docker build locally on MacOS (automatic on GitHub buildx) +ENV TARGETPLATFORM=${TARGETPLATFORM:-linux/arm64} +ENV OTEL_PLATFORM=${TARGETPLATFORM#*/} +# contrib package is required to have filelog extension +# ARG OTEL_COLL_DEB="otelcol_${OTEL_VERSION}_linux_${OTEL_PLATFORM}.deb" +ARG OTEL_COLL_DEB="otelcol-contrib_${OTEL_VERSION}_linux_${OTEL_PLATFORM}.deb" +RUN curl --location --output "$OTEL_COLL_DEB" "https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v$OTEL_VERSION/$OTEL_COLL_DEB" \ + && dpkg -i "$OTEL_COLL_DEB" \ + && rm "$OTEL_COLL_DEB" + +# OTEL_CONFIG="/etc/otelcol/config.yaml" +ENV OTEL_CONFIG="$HOME/otel-config.yaml" +COPY otel-config.yaml "$OTEL_CONFIG" + +# install last version of uv +ENV PATH="$PATH:/$HOME/.local/bin" +# set to avoid issue describd in https://github.com/hadolint/hadolint/wiki/DL4006 +SHELL ["/bin/bash", "-o", "pipefail", "-c"] +RUN curl -LsSf https://astral.sh/uv/install.sh | sh \ + && source /$HOME/.local/bin/env \ + && uv --version + +# install last version of opencode +# hadolint ignore=DL3016 +RUN npm install -g opencode-ai +ENV PATH="$PATH:/$HOME/.opencode/bin/" + +ENV OPENCODE_CONFIG="$HOME/.config/opencode/opencode.jsonc" +# use * pattern to avoid copy failure if files don't exist +COPY .opencode/* .opencode/ + +# user 'opencode' allows to have all user Opencode data & config on mounted volume +# see https://opencode.ai/docs/troubleshooting/ for content of –/.local +RUN groupadd --system $USER \ + && useradd --system $USER --gid $USER \ + && chown -R "$USER:$USER" $HOME +USER "$USER" + +# for otel extensions - for debugging but should be removed for prod +# http://localhost:55679/debug/servicez +# http://localhost:1777/debug/pprof/ +# http://localhost:13133 +EXPOSE 1777 +EXPOSE 13133 +EXPOSE 55679 + +# expose otel grpc and http endpoints to make them usable from outside of the container +# for debugging but should be removed for prod +EXPOSE 4317 +EXPOSE 4318 + +# no OpenTelemetry by default +ENV WITH_OTEL="false" + +COPY <[A-Z]*) *(?P