diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index be047c7..7a5c1f4 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -78,6 +78,15 @@ jobs:
python -m kleinanzeigen_bot version
python -m kleinanzeigen_bot verify
+ - name: "Build docker image"
+ if: startsWith(matrix.os, 'linux')
+ run: |
+ set -eux
+
+ bash docker/build-image.sh
+
+ docker run --rm kleinanzeigen-bot/kleinanzeigen-bot help
+
- name: py2exe
if: startsWith(matrix.os, 'windows')
run: |
diff --git a/.gitignore b/.gitignore
index b064b98..ced2c7a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,9 @@
# Local work folder that is not checked in
_LOCAL/
+# docker
+.dockerignore
+
# kleinanzeigen_bot
/config.yaml
/data
diff --git a/README.md b/README.md
index fdb66af..3a6d6e1 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,6 @@
**kleinanzeigen-bot** is a console based application to ease publishing of ads to ebay-kleinanzeigen.de.
-
It is a spiritual successor to [AnzeigenOrg/ebayKleinanzeigen](https://github.com/AnzeigenOrg/ebayKleinanzeigen) with the following advantages:
- supports Microsoft Edge browser (Chromium based)
- compatible chromedriver is installed automatically
@@ -77,6 +76,52 @@ It is a spiritual successor to [AnzeigenOrg/ebayKleinanzeigen](https://github.co
python -m kleinanzeigen_bot --help
```
+### Installation using Docker
+
+1. The following components need to be installed:
+ 1. [Docker](https://www.docker.com/)
+ 1. [git client](https://git-scm.com/downloads)
+ 1. [Bash](https://www.gnu.org/software/bash/) (on Windows e.g. via [Cygwin](https://www.cygwin.com/), [MSys2](https://www.msys2.org/) or git)
+ 1. [X11 - X Window System](https://en.wikipedia.org/wiki/X_Window_System) display server (on Windows e.g. https://github.com/P-St/Portable-X-Server/releases/latest)
+
+1. Clone the repo using
+ ```
+ git clone https://github.com/kleinanzeigen-bot/kleinanzeigen-bot/
+ ```
+
+1. Open the cloned directory in a Bash terminal window and navigate to the [docker](docker) subdirectory
+
+1. Execute `bash build-image.sh`
+
+1. Ensure the image is build:
+
+ ```
+ $ docker image ls
+ REPOSITORY TAG IMAGE ID CREATED SIZE
+ kleinanzeigen-bot/kleinanzeigen-bot latest c31fd256eeea 1 minute ago 590MB
+ python 3-slim 2052f0475488 5 days ago 123MB
+ ```
+
+**Running the docker image:**
+1. Ensure the X11 Server is running
+
+1. Run the docker image:
+
+ ```bash
+ X11_DISPLAY=192.168.50.34:0.0 # replace with IP address of workstation where X11 server is running
+
+ DATA_DIR=/var/opt/data/kleinanzeigen-bot # path to config
+
+ # /mnt/data is the container's default working directory
+ docker run --rm --interactive --tty \
+ --shm-size=256m \
+ -e DISPLAY=$X11_DISPLAY \
+ -v $DATA_DIR:/mnt/data \
+ kleinanzeigen-bot/kleinanzeigen-bot \
+ --help
+ ```
+
+
## Usage
```yaml
diff --git a/docker/build-image.sh b/docker/build-image.sh
new file mode 100644
index 0000000..70d58d1
--- /dev/null
+++ b/docker/build-image.sh
@@ -0,0 +1,75 @@
+#!/usr/bin/env bash
+#
+# Copyright (C) 2022 Sebastian Thomschke and contributors
+# SPDX-License-Identifier: AGPL-3.0-or-later
+#
+
+set -eu
+
+#################################################
+# execute script with bash if loaded with other shell interpreter
+#################################################
+if [ -z "${BASH_VERSINFO:-}" ]; then /usr/bin/env bash "$0" "$@"; exit; fi
+
+set -o pipefail
+
+
+#################################################
+# configure error reporting
+#################################################
+trap 'rc=$?; echo >&2 "$(date +%H:%M:%S) Error - exited with status $rc in [$BASH_SOURCE] at line $LINENO:"; cat -n $BASH_SOURCE | tail -n+$((LINENO - 3)) | head -n7' ERR
+
+
+#################################################
+# determine directory of current script
+#################################################
+this_file_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")"; pwd -P)
+project_root=$(cd "$this_file_dir/.."; pwd -P)
+docker_file="$this_file_dir/image/Dockerfile"
+echo "project_root=$project_root"
+
+
+#################################################
+# use .gitignore as .dockerignore
+#################################################
+cp -f "$project_root/.gitignore" "$project_root/.dockerignore"
+
+
+#################################################
+# specify target docker registry/repo
+#################################################
+image_repo=kleinanzeigen-bot/kleinanzeigen-bot
+image_name=$image_repo:latest
+
+
+#################################################
+# build the image
+#################################################
+echo "Building docker image [$image_name] from [$project_root]..."
+docker image pull python:3-slim || true # ensure we have the latest version of the base image
+
+if [[ $OSTYPE == "cygwin" || $OSTYPE == "msys" ]]; then
+ project_root=$(cygpath -w "$project_root")
+ docker_file=$(cygpath -w "$docker_file")
+fi
+
+docker build "$project_root" \
+ --file "$docker_file" \
+ --progress=plain \
+ --build-arg BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \
+ --build-arg GIT_BRANCH="${GIT_BRANCH:-$(git rev-parse --abbrev-ref HEAD)}" \
+ --build-arg GIT_COMMIT_DATE="$(date -d @$(git log -1 --format='%at') --utc +'%Y-%m-%d %H:%M:%S UTC')" \
+ --build-arg GIT_COMMIT_HASH="$(git rev-parse --short HEAD)" \
+ --build-arg GIT_REPO_URL="$(git config --get remote.origin.url)" \
+ -t $image_name \
+ "$@"
+
+
+#################################################
+# push image with tags to remote docker image registry
+#################################################
+if [[ "${DOCKER_PUSH:-0}" == "1" ]]; then
+ docker image tag $image_name docker.io/$image_name
+
+ docker push docker.io/$image_name
+fi
diff --git a/docker/image/Dockerfile b/docker/image/Dockerfile
new file mode 100644
index 0000000..5cf5615
--- /dev/null
+++ b/docker/image/Dockerfile
@@ -0,0 +1,82 @@
+#
+# Copyright (C) 2022 Sebastian Thomschke and contributors
+# SPDX-License-Identifier: AGPL-3.0-or-later
+#
+
+######################
+# runtime image base
+######################
+FROM python:3-slim as runtime-base-image
+
+LABEL maintainer="Sebastian Thomschke"
+
+ARG DEBIAN_FRONTEND=noninteractive
+ARG LC_ALL=C
+
+RUN set -eu \
+ #
+ && apt-get update -y \
+ && echo "#################################################" \
+ && echo "Install Chromium + Driver..." \
+ && echo "#################################################" \
+ && apt-get install --no-install-recommends -y chromium chromium-driver \
+ #
+ && rm -rf \
+ /var/cache/{apt,debconf} \
+ /var/lib/apt/lists/* \
+ /var/log/{apt,alternatives.log,bootstrap.log,dpkg.log} \
+ /tmp/* /var/tmp/*
+
+
+######################
+# build image
+######################
+
+# https://hub.docker.com/_/python?tab=tags&name=3-slim
+FROM python:3-slim AS build-image
+
+RUN apt-get update \
+ && apt-get install --no-install-recommends -y git \
+ && python -m pip install --upgrade pip
+
+COPY kleinanzeigen_bot /opt/app/kleinanzeigen_bot
+COPY .git /opt/app/.git
+COPY *.py *.txt *.toml /opt/app/
+
+RUN cd /opt/app \
+ && ls -la . \
+ && pip install --user . \
+ # generates version.py
+ && python setup.py --version
+
+
+######################
+# final image
+######################
+FROM runtime-base-image
+COPY --from=build-image /root/.local /root/.local
+
+ARG BUILD_DATE
+ARG GIT_COMMIT_HASH
+ARG GIT_COMMIT_DATE
+ARG GIT_REPO_URL
+
+LABEL \
+ org.label-schema.schema-version="1.0" \
+ org.label-schema.build-date=$BUILD_DATE \
+ org.label-schema.vcs-ref=$GIT_COMMIT_HASH \
+ org.label-schema.vcs-url=$GIT_REPO_URL
+
+# https://stackoverflow.com/a/59812588/5116073
+ENV PYTHONUNBUFFERED=1
+ENV DISPLAY=0:0
+
+ENTRYPOINT ["/bin/bash", "/opt/run.sh"]
+
+ENV \
+ INIT_SH_FILE='' \
+ CONFIG_FILE=/mnt/data/config.yaml
+
+COPY docker/image/run.sh /opt/run.sh
+
+VOLUME /mnt/data
diff --git a/docker/image/run.sh b/docker/image/run.sh
new file mode 100644
index 0000000..6428df1
--- /dev/null
+++ b/docker/image/run.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+#
+# Copyright (C) 2022 Sebastian Thomschke and contributors
+# SPDX-License-Identifier: AGPL-3.0-or-later
+#
+
+set -e -u
+
+##############################
+# execute script with bash if loaded with other shell interpreter
+##############################
+if [ -z "${BASH_VERSINFO:-}" ]; then /usr/bin/env bash "$0" "$@"; exit; fi
+
+set -o pipefail
+
+trap 'echo >&2 "$(date +%H:%M:%S) Error - exited with status $? at line $LINENO:"; pr -tn $0 | tail -n+$((LINENO - 3)) | head -n7' ERR
+
+if [ -f "$INIT_SH_FILE" ]; then
+ source "$INIT_SH_FILE"
+fi
+
+cd /mnt/data
+
+python -m kleinanzeigen_bot --config $CONFIG_FILE "$@"
diff --git a/kleinanzeigen_bot/__init__.py b/kleinanzeigen_bot/__init__.py
index f05b434..a4003db 100644
--- a/kleinanzeigen_bot/__init__.py
+++ b/kleinanzeigen_bot/__init__.py
@@ -86,7 +86,7 @@ class KleinanzeigenBot(SeleniumMixin):
if is_frozen():
exe = sys.argv[0]
else:
- exe = f"python -m {os.path.relpath(os.path.join(__file__, '..'))}"
+ exe = "python -m kleinanzeigen_bot"
print(textwrap.dedent(f"""\
Usage: {exe} COMMAND [-v|--verbose] [--config=] [--logfile=]