Skip to content
Open
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
69 changes: 69 additions & 0 deletions .github/workflows/build-on-change.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: "Build on Image Changes"

# Builds both channels when image-files or Dockerfile.template change on any branch.
# Tags include a branch suffix (e.g. 10.44.1e-latest-add-tint2) except on master.

on:
push:
branches: ['**']
paths:
- 'image-files/**'
- 'Dockerfile.template'

jobs:
build:
name: Build ${{ matrix.channel }}
runs-on: ubuntu-latest
strategy:
matrix:
channel: [latest, stable]
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

- name: Extract version from committed Dockerfile
id: version
run: |
ver=$(grep -oP '(?<=INSTALL_FILENAME="ibgateway-)[^-]+' ${{ matrix.channel }}/Dockerfile)
echo "version=$ver" >> $GITHUB_OUTPUT
echo "major_minor=$(echo "$ver" | grep -oP '^\d+\.\d+')" >> $GITHUB_OUTPUT

- name: Regenerate build context with current image-files
run: ./build.sh ${{ matrix.channel }} ${{ steps.version.outputs.version }}

- name: Sanitize branch name for Docker tag
id: branch
run: |
name=$(echo "${GITHUB_REF_NAME}" | sed 's/[^a-zA-Z0-9]/-/g' | tr '[:upper:]' '[:lower:]')
echo "name=$name" >> $GITHUB_OUTPUT
[[ "${GITHUB_REF_NAME}" == "master" ]] && echo "is_master=true" >> $GITHUB_OUTPUT || echo "is_master=false" >> $GITHUB_OUTPUT

- name: Compute image tags
id: tags
env:
IMAGE: ghcr.io/rjemanuele/ibkr
VERSION: ${{ steps.version.outputs.version }}
MAJOR_MINOR: ${{ steps.version.outputs.major_minor }}
CHANNEL: ${{ matrix.channel }}
BRANCH: ${{ steps.branch.outputs.name }}
IS_MASTER: ${{ steps.branch.outputs.is_master }}
run: |
if [[ "$IS_MASTER" == "true" ]]; then
echo "tags=${IMAGE}:${VERSION}-${CHANNEL},${IMAGE}:${MAJOR_MINOR},${IMAGE}:${CHANNEL}" >> $GITHUB_OUTPUT
else
echo "tags=${IMAGE}:${VERSION}-${CHANNEL}-${BRANCH},${IMAGE}:${MAJOR_MINOR}-${BRANCH},${IMAGE}:${CHANNEL}-${BRANCH}" >> $GITHUB_OUTPUT
fi

- name: Log in to Container Registry
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
with:
context: ${{ matrix.channel }}
push: true
tags: ${{ steps.tags.outputs.tags }}
3 changes: 1 addition & 2 deletions .github/workflows/detect-releases.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ jobs:
- name: Get Latest IB Gateway Version
id: version
run: |
res=$(curl -s https://download2.interactivebrokers.com/installers/ibgateway/${{ inputs.channel }}-standalone/version.json)
build_version=$(grep -oP '(?<=buildVersion":")[^"]+' <<< "$res")
build_version=$(./ibkr-version.sh ${{ inputs.channel }})
echo "build_version=$build_version" >> $GITHUB_OUTPUT

- name: Check Latest Version against Releases
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
id: meta
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6
with:
images: ghcr.io/extrange/ibkr
images: ghcr.io/rjemanuele/ibkr
flavor: |
latest=false

Expand Down
76 changes: 76 additions & 0 deletions .github/workflows/manual-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: "Manual Build"

# Trigger from Actions tab: pick a channel and optionally pin a version.
# Useful for rebuilding an older IBKR version with newer image-files changes.

on:
workflow_dispatch:
inputs:
channel:
description: 'Channel'
required: true
type: choice
options: [latest, stable]
version:
description: 'IBKR version (e.g. 10.44.1e). Leave blank for current.'
required: false
type: string

jobs:
build:
name: Build ${{ inputs.channel }} ${{ inputs.version || '(current)' }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

- name: Resolve version
id: version
run: |
if [[ -n "${{ inputs.version }}" ]]; then
ver="${{ inputs.version }}"
else
ver=$(./ibkr-version.sh ${{ inputs.channel }})
fi
echo "version=$ver" >> $GITHUB_OUTPUT
echo "major_minor=$(echo "$ver" | sed 's/\([0-9]*\.[0-9]*\).*/\1/')" >> $GITHUB_OUTPUT

- name: Regenerate build context with current image-files
run: ./build.sh ${{ inputs.channel }} ${{ steps.version.outputs.version }}

- name: Sanitize branch name for Docker tag
id: branch
run: |
name=$(echo "${GITHUB_REF_NAME}" | sed 's/[^a-zA-Z0-9]/-/g' | tr '[:upper:]' '[:lower:]')
echo "name=$name" >> $GITHUB_OUTPUT
[[ "${GITHUB_REF_NAME}" == "master" ]] && echo "is_master=true" >> $GITHUB_OUTPUT || echo "is_master=false" >> $GITHUB_OUTPUT

- name: Compute image tags
id: tags
env:
IMAGE: ghcr.io/rjemanuele/ibkr
VERSION: ${{ steps.version.outputs.version }}
MAJOR_MINOR: ${{ steps.version.outputs.major_minor }}
CHANNEL: ${{ inputs.channel }}
BRANCH: ${{ steps.branch.outputs.name }}
IS_MASTER: ${{ steps.branch.outputs.is_master }}
run: |
if [[ "$IS_MASTER" == "true" ]]; then
echo "tags=${IMAGE}:${VERSION}-${CHANNEL},${IMAGE}:${MAJOR_MINOR},${IMAGE}:${CHANNEL}" >> $GITHUB_OUTPUT
else
echo "tags=${IMAGE}:${VERSION}-${CHANNEL}-${BRANCH},${IMAGE}:${MAJOR_MINOR}-${BRANCH},${IMAGE}:${CHANNEL}-${BRANCH}" >> $GITHUB_OUTPUT
fi

- name: Log in to Container Registry
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
with:
context: ${{ inputs.channel }}
push: true
tags: ${{ steps.tags.outputs.tags }}
8 changes: 5 additions & 3 deletions Dockerfile.template
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
FROM debian:13@sha256:5cf544fad978371b3df255b61e209b373583cb88b733475c86e49faa15ac2104 AS setup

ENV IBC_VERSION=3.22.0
ENV TZ=UTC

RUN apt-get update && \
apt-get install --no-install-recommends -y \
ca-certificates git libxtst6 libgtk-3-0 openbox procps python3 socat tigervnc-standalone-server unzip wget2 xterm \
DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \
ca-certificates git libxtst6 libgtk-3-0 openbox procps python3 socat tigervnc-standalone-server unzip wget2 xterm tint2 tzdata \
# https://github.com/extrange/ibkr-docker/issues/74
libasound2 \
libnss3 \
Expand Down Expand Up @@ -40,9 +41,10 @@ RUN wget2 "https://github.com/extrange/ibkr-docker/releases/download/${VERSION}-

# Copy scripts
COPY image-files/start.sh image-files/replace.sh /
COPY image-files/root/ /root/

RUN mkdir -p ~/ibc && mv /opt/ibc/config.ini ~/ibc/config.ini

RUN chmod a+x ./*.sh /opt/ibc/*.sh /opt/ibc/scripts/*.sh

CMD [ "/start.sh" ]
CMD [ "/start.sh" ]
78 changes: 78 additions & 0 deletions ibkr-version.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#!/bin/bash
# Fetches current or historical IBKR build versions.
#
# Usage:
# ./ibkr-version.sh list current versions (both channels)
# ./ibkr-version.sh latest|stable print current version for one channel
# ./ibkr-version.sh history [latest|stable] list versions from GitHub releases
#
# Override the GitHub repo used for history with IBKR_DOCKER_REPO=owner/repo.
# Defaults to the origin remote of this repo, then extrange/ibkr-docker.

set -euo pipefail

CHANNELS=(latest stable)
IBKR_BASE="https://download2.interactivebrokers.com/installers/ibgateway"

detect_repo() {
git remote get-url origin 2>/dev/null \
| sed 's|.*github\.com[:/]\(.*\)\.git|\1|; s|.*github\.com[:/]\(.*\)|\1|' \
|| echo "extrange/ibkr-docker"
}

fetch_current() {
local channel="$1"
local res
res=$(curl -fsSL "${IBKR_BASE}/${channel}-standalone/version.json")
sed 's/.*"buildVersion":"\([^"]*\)".*/\1/' <<< "$res"
}

list_history() {
local channel_filter="${1:-}"
local repo="${IBKR_DOCKER_REPO:-$(detect_repo)}"
printf "Releases from %s:\n\n" "$repo" >&2

curl -fsSL "https://api.github.com/repos/${repo}/releases?per_page=100" \
| python3 -c "
import json, sys
releases = json.load(sys.stdin)
cf = sys.argv[1] if len(sys.argv) > 1 else ''
rows = []
for r in releases:
tag = r['tag_name']
date = r['published_at'][:10]
parts = tag.rsplit('-', 1)
if len(parts) == 2:
version, channel = parts
if not cf or channel == cf:
rows.append((date, channel, version))
rows.sort(reverse=True)
for date, channel, version in rows:
print(f'{date} {channel:<8} {version}')
" "$channel_filter"
}

usage() {
sed -n '2,8p' "$0" | sed 's/^# \?//' >&2
exit 1
}

case "${1:-}" in
"")
for channel in "${CHANNELS[@]}"; do
printf "%-8s %s\n" "${channel}:" "$(fetch_current "$channel")"
done
;;
latest|stable)
fetch_current "$1"
;;
history)
list_history "${2:-}"
;;
-h|--help|help)
usage
;;
*)
usage
;;
esac
1 change: 1 addition & 0 deletions image-files/root/.config/openbox/autostart
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(while true; do tint2; sleep 2; done) &
16 changes: 15 additions & 1 deletion image-files/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
# Fail fast
set -Eeuo pipefail

# Don't drop core files by default
_acf="${ALLOW_CORE_FILES:-}"
if [[ "${_acf,,}" != "true" ]]; then
ulimit -c 0
fi
unset _acf

export DISPLAY=:0

# Clear previous lockfile
Expand All @@ -14,8 +21,15 @@ Xvnc -SecurityTypes None -AlwaysShared=1 -geometry 1920x1080 :0 &
# Start noVNC server
./noVNC/utils/novnc_proxy --vnc localhost:5900 &

# Wait for Xvnc to be ready before starting openbox
until [ -S /tmp/.X11-unix/X0 ]; do sleep 0.1; done

# Start openbox
openbox &
opts=()
if [ -f "$HOME/.config/openbox/rc.xml" ]; then
opts+=(--config-file "$HOME/.config/openbox/rc.xml")
fi
openbox-session "${opts[@]}" &

# Start either TWS or IB Gateway
if [[ -z ${GATEWAY_OR_TWS:-} ]]; then
Expand Down