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=]