mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Docker improvements (#3042)
* Simplified dockerfile - Changed from alpine to python:slim - Removed some database libs (because we *connect* to a db, not host it) * - Add gettext as required package - Only create inventree user as part of production build (leave admin access for dev build) * Tweaks for tasks.py * Fix user permissions (drop to inventree user) * Drop to the 'inventree' user level as part of init.sh - As we have mounted volumes at 'run time' we need to ensure that the inventree user has correct permissions! - Ref: https://stackoverflow.com/questions/39397548/how-to-give-non-root-user-in-docker-container-access-to-a-volume-mounted-on-the * Adjust user setup - Only drop to non-root user as part of "production" build - Mounted external volumes make it tricky when in the dev build - Might want to revisit this later on * More dockerfile changes - reduce required system packages - * Add new docker github workflow * Print some more debug * GITHUB_BASE_REF * Add gnupg to base requirements * Improve debug output during testing * Refactoring updates for label printing API - Update weasyprint version to 55.0 - Generate labels as pdf files - Provide filename to label printing plugin - Additional unit testing - Improve extraction of some hidden debug data during TESTING - Fix a spelling mistake (notifaction -> notification) * Working on github action * More testing * Add requirement for pdf2image * Fix label printing plugin and update unit testing * Add required packages for CI * Move docker files to the top level directory - This allows us to build the production image directly from soure - Don't need to re-download the source code from github - Note: The docker install guide will need to be updated! * Fix for docker ci file * Print GIT SHA * Bake git information into the production image * Add some exta docstrings to dockerfile * Simplify version check script * Extract git commit info * Extract docker tag from check_version.py * Newline * More work on the docker workflow * Dockerfile fixes - Directory / path issues * Dockerfile fixes - Directory / path issues * Ignore certain steps on a pull request * Add poppler-utils to CI * Consolidate version check into existing CI file * Don't run docker workflow on pull request * Pass docker image tag through to the build Also check .j2k files * Add supervisord.conf example file back in * Remove --no-cache-dir option from pip install
This commit is contained in:
parent
9a2300d920
commit
b9fd263899
@ -1,4 +1,5 @@
|
|||||||
# InvenTree environment variables for a development setup
|
# InvenTree environment variables for a development setup
|
||||||
|
# These variables will be used by the docker-compose.yml file
|
||||||
|
|
||||||
# Set DEBUG to True for a development setup
|
# Set DEBUG to True for a development setup
|
||||||
INVENTREE_DEBUG=True
|
INVENTREE_DEBUG=True
|
69
.github/workflows/docker.yaml
vendored
Normal file
69
.github/workflows/docker.yaml
vendored
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# Build, test and push InvenTree docker image
|
||||||
|
# This workflow runs under any of the following conditions:
|
||||||
|
#
|
||||||
|
# - Push to the master branch
|
||||||
|
# - Push to the stable branch
|
||||||
|
# - Publish release
|
||||||
|
#
|
||||||
|
# The following actions are performed:
|
||||||
|
#
|
||||||
|
# - Check that the version number matches the current branch or tag
|
||||||
|
# - Build the InvenTree docker image
|
||||||
|
# - Run suite of unit tests against the build image
|
||||||
|
# - Push the compiled, tested image to dockerhub
|
||||||
|
|
||||||
|
name: Docker
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
- 'stable'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
# Build the docker image
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out repo
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Version Check
|
||||||
|
run: |
|
||||||
|
python3 ci/check_version_number.py
|
||||||
|
echo "git_commit_hash=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
||||||
|
echo "git_commit_date=$(git show -s --format=%ci)" >> $GITHUB_ENV
|
||||||
|
- name: Run Unit Tests
|
||||||
|
run: |
|
||||||
|
docker-compose build
|
||||||
|
docker-compose run inventree-dev-server invoke update
|
||||||
|
docker-compose up -d
|
||||||
|
docker-compose run inventree-dev-server invoke wait
|
||||||
|
docker-compose run inventree-dev-server invoke test
|
||||||
|
docker-compose down
|
||||||
|
- name: Set up QEMU
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
uses: docker/setup-qemu-action@v1
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
- name: Login to Dockerhub
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
- name: Build and Push
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||||
|
push: false
|
||||||
|
target: production
|
||||||
|
tags: inventree/inventree:${{ env.docker_tag }}
|
||||||
|
build-args: commit_hash=${{ env.git_commit_hash }},commit_date=${{ env.git_commit_date }},commit_tag=${{ env.docker_tag }}
|
51
.github/workflows/docker_latest.yaml
vendored
51
.github/workflows/docker_latest.yaml
vendored
@ -1,51 +0,0 @@
|
|||||||
# Build and push latest docker image on push to master branch
|
|
||||||
|
|
||||||
name: Docker Build
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- 'master'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
docker:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout Code
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Check version number
|
|
||||||
run: |
|
|
||||||
python3 ci/check_version_number.py --dev
|
|
||||||
- name: Build Docker Image
|
|
||||||
run: |
|
|
||||||
cd docker
|
|
||||||
docker-compose build
|
|
||||||
docker-compose run inventree-dev-server invoke update
|
|
||||||
- name: Run unit tests
|
|
||||||
run: |
|
|
||||||
cd docker
|
|
||||||
docker-compose up -d
|
|
||||||
docker-compose run inventree-dev-server invoke wait
|
|
||||||
docker-compose run inventree-dev-server invoke test
|
|
||||||
docker-compose down
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v1
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v1
|
|
||||||
- name: Login to Dockerhub
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
|
||||||
- name: Build and Push
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
context: ./docker
|
|
||||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
|
||||||
push: true
|
|
||||||
target: production
|
|
||||||
tags: inventree/inventree:latest
|
|
||||||
- name: Image Digest
|
|
||||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
|
42
.github/workflows/docker_stable.yaml
vendored
42
.github/workflows/docker_stable.yaml
vendored
@ -1,42 +0,0 @@
|
|||||||
# Build and push docker image on push to 'stable' branch
|
|
||||||
# Docker build will be uploaded to dockerhub with the 'inventree:stable' tag
|
|
||||||
|
|
||||||
name: Docker Build
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- 'stable'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
docker:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout Code
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Check version number
|
|
||||||
run: |
|
|
||||||
python3 ci/check_version_number.py --release
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v1
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v1
|
|
||||||
- name: Login to Dockerhub
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
|
||||||
- name: Build and Push
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
context: ./docker
|
|
||||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
|
||||||
push: true
|
|
||||||
target: production
|
|
||||||
build-args:
|
|
||||||
branch=stable
|
|
||||||
tags: inventree/inventree:stable
|
|
||||||
- name: Image Digest
|
|
||||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
|
38
.github/workflows/docker_tag.yaml
vendored
38
.github/workflows/docker_tag.yaml
vendored
@ -1,38 +0,0 @@
|
|||||||
# Publish docker images to dockerhub on a tagged release
|
|
||||||
# Docker build will be uploaded to dockerhub with the 'invetree:<tag>' tag
|
|
||||||
|
|
||||||
name: Docker Publish
|
|
||||||
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
publish_image:
|
|
||||||
name: Push InvenTree web server image to dockerhub
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Check out repo
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Check Release tag
|
|
||||||
run: |
|
|
||||||
python3 ci/check_version_number.py --release --tag ${{ github.event.release.tag_name }}
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v1
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v1
|
|
||||||
- name: Login to Dockerhub
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
|
||||||
- name: Build and Push
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
context: ./docker
|
|
||||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
|
||||||
push: true
|
|
||||||
target: production
|
|
||||||
build-args:
|
|
||||||
tag=${{ github.event.release.tag_name }}
|
|
||||||
tags: inventree/inventree:${{ github.event.release.tag_name }}
|
|
11
.github/workflows/qc_checks.yaml
vendored
11
.github/workflows/qc_checks.yaml
vendored
@ -91,6 +91,9 @@ jobs:
|
|||||||
cache: 'pip'
|
cache: 'pip'
|
||||||
- name: Run pre-commit Checks
|
- name: Run pre-commit Checks
|
||||||
uses: pre-commit/action@v2.0.3
|
uses: pre-commit/action@v2.0.3
|
||||||
|
- name: Check version number
|
||||||
|
run: |
|
||||||
|
python3 ci/check_version_number.py
|
||||||
|
|
||||||
python:
|
python:
|
||||||
name: Tests - inventree-python
|
name: Tests - inventree-python
|
||||||
@ -114,7 +117,7 @@ jobs:
|
|||||||
- name: Enviroment Setup
|
- name: Enviroment Setup
|
||||||
uses: ./.github/actions/setup
|
uses: ./.github/actions/setup
|
||||||
with:
|
with:
|
||||||
apt-dependency: gettext
|
apt-dependency: gettext poppler-utils
|
||||||
update: true
|
update: true
|
||||||
- name: Download Python Code For `${{ env.wrapper_name }}`
|
- name: Download Python Code For `${{ env.wrapper_name }}`
|
||||||
run: git clone --depth 1 https://github.com/inventree/${{ env.wrapper_name }} ./${{ env.wrapper_name }}
|
run: git clone --depth 1 https://github.com/inventree/${{ env.wrapper_name }} ./${{ env.wrapper_name }}
|
||||||
@ -147,7 +150,7 @@ jobs:
|
|||||||
- name: Enviroment Setup
|
- name: Enviroment Setup
|
||||||
uses: ./.github/actions/setup
|
uses: ./.github/actions/setup
|
||||||
with:
|
with:
|
||||||
apt-dependency: gettext
|
apt-dependency: gettext poppler-utils
|
||||||
update: true
|
update: true
|
||||||
- name: Coverage Tests
|
- name: Coverage Tests
|
||||||
run: invoke coverage
|
run: invoke coverage
|
||||||
@ -196,7 +199,7 @@ jobs:
|
|||||||
- name: Enviroment Setup
|
- name: Enviroment Setup
|
||||||
uses: ./.github/actions/setup
|
uses: ./.github/actions/setup
|
||||||
with:
|
with:
|
||||||
apt-dependency: gettext libpq-dev
|
apt-dependency: gettext poppler-utils libpq-dev
|
||||||
pip-dependency: psycopg2 django-redis>=5.0.0
|
pip-dependency: psycopg2 django-redis>=5.0.0
|
||||||
update: true
|
update: true
|
||||||
- name: Run Tests
|
- name: Run Tests
|
||||||
@ -239,7 +242,7 @@ jobs:
|
|||||||
- name: Enviroment Setup
|
- name: Enviroment Setup
|
||||||
uses: ./.github/actions/setup
|
uses: ./.github/actions/setup
|
||||||
with:
|
with:
|
||||||
apt-dependency: gettext libmysqlclient-dev
|
apt-dependency: gettext poppler-utils libmysqlclient-dev
|
||||||
pip-dependency: mysqlclient
|
pip-dependency: mysqlclient
|
||||||
update: true
|
update: true
|
||||||
- name: Run Tests
|
- name: Run Tests
|
||||||
|
21
.github/workflows/version.yml
vendored
21
.github/workflows/version.yml
vendored
@ -1,21 +0,0 @@
|
|||||||
# Checks version number
|
|
||||||
name: version number
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
branches-ignore:
|
|
||||||
- l10*
|
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
check_version:
|
|
||||||
name: version number
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout Code
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Check version number
|
|
||||||
run: |
|
|
||||||
python3 ci/check_version_number.py --branch ${{ github.base_ref }}
|
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -8,6 +8,7 @@ __pycache__/
|
|||||||
env/
|
env/
|
||||||
inventree-env/
|
inventree-env/
|
||||||
./build/
|
./build/
|
||||||
|
.cache/
|
||||||
develop-eggs/
|
develop-eggs/
|
||||||
dist/
|
dist/
|
||||||
bin/
|
bin/
|
||||||
@ -26,7 +27,6 @@ var/
|
|||||||
.installed.cfg
|
.installed.cfg
|
||||||
*.egg
|
*.egg
|
||||||
|
|
||||||
|
|
||||||
# Django stuff:
|
# Django stuff:
|
||||||
*.log
|
*.log
|
||||||
local_settings.py
|
local_settings.py
|
||||||
@ -38,6 +38,8 @@ local_settings.py
|
|||||||
# Files used for testing
|
# Files used for testing
|
||||||
dummy_image.*
|
dummy_image.*
|
||||||
_tmp.csv
|
_tmp.csv
|
||||||
|
inventree/label.pdf
|
||||||
|
inventree/label.png
|
||||||
|
|
||||||
# Sphinx files
|
# Sphinx files
|
||||||
docs/_build
|
docs/_build
|
||||||
@ -63,6 +65,7 @@ secret_key.txt
|
|||||||
.idea/
|
.idea/
|
||||||
*.code-workspace
|
*.code-workspace
|
||||||
.vscode/
|
.vscode/
|
||||||
|
.bash_history
|
||||||
|
|
||||||
# Coverage reports
|
# Coverage reports
|
||||||
.coverage
|
.coverage
|
||||||
|
@ -1,37 +1,39 @@
|
|||||||
FROM alpine:3.14 as base
|
# The InvenTree dockerfile provides two build targets:
|
||||||
|
#
|
||||||
|
# production:
|
||||||
|
# - Required files are copied into the image
|
||||||
|
# - Runs InvenTree web server under gunicorn
|
||||||
|
#
|
||||||
|
# dev:
|
||||||
|
# - Expects source directories to be loaded as a run-time volume
|
||||||
|
# - Runs InvenTree web server under django development server
|
||||||
|
# - Monitors source files for any changes, and live-reloads server
|
||||||
|
|
||||||
# GitHub source
|
|
||||||
ARG repository="https://github.com/inventree/InvenTree.git"
|
|
||||||
ARG branch="master"
|
|
||||||
|
|
||||||
# Optionally specify a particular tag to checkout
|
FROM python:3.9-slim as base
|
||||||
ARG tag=""
|
|
||||||
|
# Build arguments for this image
|
||||||
|
ARG commit_hash=""
|
||||||
|
ARG commit_date=""
|
||||||
|
ARG commit_tag=""
|
||||||
|
|
||||||
ENV PYTHONUNBUFFERED 1
|
ENV PYTHONUNBUFFERED 1
|
||||||
|
|
||||||
# Ref: https://github.com/pyca/cryptography/issues/5776
|
# Ref: https://github.com/pyca/cryptography/issues/5776
|
||||||
ENV CRYPTOGRAPHY_DONT_BUILD_RUST 1
|
ENV CRYPTOGRAPHY_DONT_BUILD_RUST 1
|
||||||
|
|
||||||
# InvenTree key settings
|
|
||||||
|
|
||||||
# The INVENTREE_HOME directory is where the InvenTree source repository will be located
|
|
||||||
ENV INVENTREE_HOME="/home/inventree"
|
|
||||||
|
|
||||||
# GitHub settings
|
|
||||||
ENV INVENTREE_GIT_REPO="${repository}"
|
|
||||||
ENV INVENTREE_GIT_BRANCH="${branch}"
|
|
||||||
ENV INVENTREE_GIT_TAG="${tag}"
|
|
||||||
|
|
||||||
ENV INVENTREE_LOG_LEVEL="INFO"
|
ENV INVENTREE_LOG_LEVEL="INFO"
|
||||||
ENV INVENTREE_DOCKER="true"
|
ENV INVENTREE_DOCKER="true"
|
||||||
|
|
||||||
# InvenTree paths
|
# InvenTree paths
|
||||||
|
ENV INVENTREE_HOME="/home/inventree"
|
||||||
ENV INVENTREE_MNG_DIR="${INVENTREE_HOME}/InvenTree"
|
ENV INVENTREE_MNG_DIR="${INVENTREE_HOME}/InvenTree"
|
||||||
ENV INVENTREE_DATA_DIR="${INVENTREE_HOME}/data"
|
ENV INVENTREE_DATA_DIR="${INVENTREE_HOME}/data"
|
||||||
ENV INVENTREE_STATIC_ROOT="${INVENTREE_DATA_DIR}/static"
|
ENV INVENTREE_STATIC_ROOT="${INVENTREE_DATA_DIR}/static"
|
||||||
ENV INVENTREE_MEDIA_ROOT="${INVENTREE_DATA_DIR}/media"
|
ENV INVENTREE_MEDIA_ROOT="${INVENTREE_DATA_DIR}/media"
|
||||||
ENV INVENTREE_PLUGIN_DIR="${INVENTREE_DATA_DIR}/plugins"
|
ENV INVENTREE_PLUGIN_DIR="${INVENTREE_DATA_DIR}/plugins"
|
||||||
|
|
||||||
|
# InvenTree configuration files
|
||||||
ENV INVENTREE_CONFIG_FILE="${INVENTREE_DATA_DIR}/config.yaml"
|
ENV INVENTREE_CONFIG_FILE="${INVENTREE_DATA_DIR}/config.yaml"
|
||||||
ENV INVENTREE_SECRET_KEY_FILE="${INVENTREE_DATA_DIR}/secret_key.txt"
|
ENV INVENTREE_SECRET_KEY_FILE="${INVENTREE_DATA_DIR}/secret_key.txt"
|
||||||
ENV INVENTREE_PLUGIN_FILE="${INVENTREE_DATA_DIR}/plugins.txt"
|
ENV INVENTREE_PLUGIN_FILE="${INVENTREE_DATA_DIR}/plugins.txt"
|
||||||
@ -49,82 +51,83 @@ LABEL org.label-schema.schema-version="1.0" \
|
|||||||
org.label-schema.vendor="inventree" \
|
org.label-schema.vendor="inventree" \
|
||||||
org.label-schema.name="inventree/inventree" \
|
org.label-schema.name="inventree/inventree" \
|
||||||
org.label-schema.url="https://hub.docker.com/r/inventree/inventree" \
|
org.label-schema.url="https://hub.docker.com/r/inventree/inventree" \
|
||||||
org.label-schema.vcs-url=${INVENTREE_GIT_REPO} \
|
org.label-schema.vcs-url="https://github.com/inventree/InvenTree.git" \
|
||||||
org.label-schema.vcs-branch=${INVENTREE_GIT_BRANCH} \
|
org.label-schema.vcs-ref=${commit_tag}
|
||||||
org.label-schema.vcs-ref=${INVENTREE_GIT_TAG}
|
|
||||||
|
|
||||||
# Create user account
|
# RUN apt-get upgrade && apt-get update
|
||||||
RUN addgroup -S inventreegroup && adduser -S inventree -G inventreegroup
|
RUN apt-get update
|
||||||
|
|
||||||
RUN apk -U upgrade
|
|
||||||
|
|
||||||
# Install required system packages
|
# Install required system packages
|
||||||
RUN apk add --no-cache git make bash \
|
RUN apt-get install -y --no-install-recommends \
|
||||||
gcc libgcc g++ libstdc++ \
|
git gcc g++ gettext gnupg \
|
||||||
gnupg \
|
# Weasyprint requirements : https://doc.courtbouillon.org/weasyprint/stable/first_steps.html#debian-11
|
||||||
libjpeg-turbo libjpeg-turbo-dev jpeg jpeg-dev libwebp-dev \
|
poppler-utils libpango-1.0-0 libpangoft2-1.0-0 \
|
||||||
libffi libffi-dev \
|
# Image format support
|
||||||
zlib zlib-dev \
|
libjpeg-dev webp \
|
||||||
# Special deps for WeasyPrint (these will be deprecated once WeasyPrint drops cairo requirement)
|
|
||||||
cairo cairo-dev pango pango-dev gdk-pixbuf \
|
|
||||||
# Fonts
|
|
||||||
fontconfig ttf-droid ttf-liberation ttf-dejavu ttf-opensans font-croscore font-noto \
|
|
||||||
# Core python
|
|
||||||
python3 python3-dev py3-pip \
|
|
||||||
# SQLite support
|
# SQLite support
|
||||||
sqlite \
|
sqlite3 \
|
||||||
# PostgreSQL support
|
# PostgreSQL support
|
||||||
postgresql postgresql-contrib postgresql-dev libpq \
|
libpq-dev \
|
||||||
# MySQL/MariaDB support
|
# MySQL / MariaDB support
|
||||||
mariadb-connector-c mariadb-dev mariadb-client \
|
default-libmysqlclient-dev mariadb-client && \
|
||||||
# Required for python cryptography support
|
apt-get autoclean && apt-get autoremove
|
||||||
openssl-dev musl-dev libffi-dev rust cargo
|
|
||||||
|
|
||||||
# Update pip
|
# Update pip
|
||||||
RUN pip install --upgrade pip
|
RUN pip install --upgrade pip
|
||||||
|
|
||||||
# Install required base-level python packages
|
# Install required base-level python packages
|
||||||
COPY requirements.txt requirements.txt
|
COPY ./docker/requirements.txt base_requirements.txt
|
||||||
RUN pip install --no-cache-dir -U -r requirements.txt
|
RUN pip install --disable-pip-version-check -U -r base_requirements.txt
|
||||||
|
|
||||||
|
# InvenTree production image:
|
||||||
|
# - Copies required files from local directory
|
||||||
|
# - Installs required python packages from requirements.txt
|
||||||
|
# - Starts a gunicorn webserver
|
||||||
|
|
||||||
# Production code (pulled from tagged github release)
|
|
||||||
FROM base as production
|
FROM base as production
|
||||||
|
|
||||||
# Clone source code
|
ENV INVENTREE_DEBUG=False
|
||||||
RUN echo "Downloading InvenTree from ${INVENTREE_GIT_REPO}"
|
|
||||||
|
|
||||||
RUN git clone --branch ${INVENTREE_GIT_BRANCH} --depth 1 ${INVENTREE_GIT_REPO} ${INVENTREE_HOME}
|
# As .git directory is not available in production image, we pass the commit information via ENV
|
||||||
|
ENV INVENTREE_COMMIT_HASH="${commit_hash}"
|
||||||
|
ENV INVENTREE_COMMIT_DATE="${commit_date}"
|
||||||
|
|
||||||
# Ref: https://github.blog/2022-04-12-git-security-vulnerability-announced/
|
# Copy source code
|
||||||
RUN git config --global --add safe.directory ${INVENTREE_HOME}
|
COPY InvenTree ${INVENTREE_HOME}/InvenTree
|
||||||
|
|
||||||
# Checkout against a particular git tag
|
# Copy other key files
|
||||||
RUN if [ -n "${INVENTREE_GIT_TAG}" ] ; then cd ${INVENTREE_HOME} && git fetch --all --tags && git checkout tags/${INVENTREE_GIT_TAG} -b v${INVENTREE_GIT_TAG}-branch ; fi
|
COPY requirements.txt ${INVENTREE_HOME}/requirements.txt
|
||||||
|
COPY tasks.py ${INVENTREE_HOME}/tasks.py
|
||||||
RUN chown -R inventree:inventreegroup ${INVENTREE_HOME}/*
|
COPY docker/gunicorn.conf.py ${INVENTREE_HOME}/gunicorn.conf.py
|
||||||
|
COPY docker/init.sh ${INVENTREE_MNG_DIR}/init.sh
|
||||||
# Drop to the inventree user
|
|
||||||
USER inventree
|
|
||||||
|
|
||||||
# Install InvenTree packages
|
|
||||||
RUN pip3 install --user --no-cache-dir --disable-pip-version-check -r ${INVENTREE_HOME}/requirements.txt
|
|
||||||
|
|
||||||
# Need to be running from within this directory
|
# Need to be running from within this directory
|
||||||
WORKDIR ${INVENTREE_MNG_DIR}
|
WORKDIR ${INVENTREE_MNG_DIR}
|
||||||
|
|
||||||
|
# Drop to the inventree user for the production image
|
||||||
|
RUN adduser inventree
|
||||||
|
RUN chown -R inventree:inventree ${INVENTREE_HOME}
|
||||||
|
|
||||||
|
USER inventree
|
||||||
|
|
||||||
|
# Install InvenTree packages
|
||||||
|
RUN pip3 install --user --disable-pip-version-check -r ${INVENTREE_HOME}/requirements.txt
|
||||||
|
|
||||||
# Server init entrypoint
|
# Server init entrypoint
|
||||||
ENTRYPOINT ["/bin/bash", "../docker/init.sh"]
|
ENTRYPOINT ["/bin/bash", "./init.sh"]
|
||||||
|
|
||||||
# Launch the production server
|
# Launch the production server
|
||||||
# TODO: Work out why environment variables cannot be interpolated in this command
|
# TODO: Work out why environment variables cannot be interpolated in this command
|
||||||
# TODO: e.g. -b ${INVENTREE_WEB_ADDR}:${INVENTREE_WEB_PORT} fails here
|
# TODO: e.g. -b ${INVENTREE_WEB_ADDR}:${INVENTREE_WEB_PORT} fails here
|
||||||
CMD gunicorn -c ./docker/gunicorn.conf.py InvenTree.wsgi -b 0.0.0.0:8000 --chdir ./InvenTree
|
CMD gunicorn -c ./gunicorn.conf.py InvenTree.wsgi -b 0.0.0.0:8000 --chdir ./InvenTree
|
||||||
|
|
||||||
FROM base as dev
|
FROM base as dev
|
||||||
|
|
||||||
# The development image requires the source code to be mounted to /home/inventree/
|
# The development image requires the source code to be mounted to /home/inventree/
|
||||||
# So from here, we don't actually "do" anything, apart from some file management
|
# So from here, we don't actually "do" anything, apart from some file management
|
||||||
|
|
||||||
|
ENV INVENTREE_DEBUG=True
|
||||||
|
|
||||||
ENV INVENTREE_DEV_DIR="${INVENTREE_HOME}/dev"
|
ENV INVENTREE_DEV_DIR="${INVENTREE_HOME}/dev"
|
||||||
|
|
||||||
# Location for python virtual environment
|
# Location for python virtual environment
|
@ -117,6 +117,11 @@ class InvenTreeAPITestCase(UserMixin, APITestCase):
|
|||||||
response = self.client.get(url, data, format='json')
|
response = self.client.get(url, data, format='json')
|
||||||
|
|
||||||
if expected_code is not None:
|
if expected_code is not None:
|
||||||
|
|
||||||
|
if response.status_code != expected_code:
|
||||||
|
print(f"Unexpected response at '{url}':")
|
||||||
|
print(response.data)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, expected_code)
|
self.assertEqual(response.status_code, expected_code)
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
@ -40,7 +40,11 @@ def exception_handler(exc, context):
|
|||||||
if response is None:
|
if response is None:
|
||||||
# DRF handler did not provide a default response for this exception
|
# DRF handler did not provide a default response for this exception
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.TESTING:
|
||||||
|
# If in TESTING mode, re-throw the exception for traceback
|
||||||
|
raise exc
|
||||||
|
elif settings.DEBUG:
|
||||||
|
# If in DEBUG mode, provide error information in the response
|
||||||
error_detail = str(exc)
|
error_detail = str(exc)
|
||||||
else:
|
else:
|
||||||
error_detail = _("Error details can be found in the admin panel")
|
error_detail = _("Error details can be found in the admin panel")
|
||||||
|
@ -129,7 +129,7 @@ def TestIfImageURL(url):
|
|||||||
Simply tests the extension against a set of allowed values
|
Simply tests the extension against a set of allowed values
|
||||||
"""
|
"""
|
||||||
return os.path.splitext(os.path.basename(url))[-1].lower() in [
|
return os.path.splitext(os.path.basename(url))[-1].lower() in [
|
||||||
'.jpg', '.jpeg',
|
'.jpg', '.jpeg', '.j2k',
|
||||||
'.png', '.bmp',
|
'.png', '.bmp',
|
||||||
'.tif', '.tiff',
|
'.tif', '.tiff',
|
||||||
'.webp', '.gif',
|
'.webp', '.gif',
|
||||||
|
@ -380,6 +380,30 @@ class TestVersionNumber(TestCase):
|
|||||||
self.assertTrue(v_d > v_c)
|
self.assertTrue(v_d > v_c)
|
||||||
self.assertTrue(v_d > v_a)
|
self.assertTrue(v_d > v_a)
|
||||||
|
|
||||||
|
def test_commit_info(self):
|
||||||
|
"""Test that the git commit information is extracted successfully"""
|
||||||
|
|
||||||
|
envs = {
|
||||||
|
'INVENTREE_COMMIT_HASH': 'abcdef',
|
||||||
|
'INVENTREE_COMMIT_DATE': '2022-12-31'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check that the environment variables take priority
|
||||||
|
|
||||||
|
with mock.patch.dict(os.environ, envs):
|
||||||
|
self.assertEqual(version.inventreeCommitHash(), 'abcdef')
|
||||||
|
self.assertEqual(version.inventreeCommitDate(), '2022-12-31')
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
# Check that the current .git values work too
|
||||||
|
|
||||||
|
hash = str(subprocess.check_output('git rev-parse --short HEAD'.split()), 'utf-8').strip()
|
||||||
|
self.assertEqual(hash, version.inventreeCommitHash())
|
||||||
|
|
||||||
|
d = str(subprocess.check_output('git show -s --format=%ci'.split()), 'utf-8').strip().split(' ')[0]
|
||||||
|
self.assertEqual(d, version.inventreeCommitDate())
|
||||||
|
|
||||||
|
|
||||||
class CurrencyTests(TestCase):
|
class CurrencyTests(TestCase):
|
||||||
"""
|
"""
|
||||||
|
@ -3,6 +3,7 @@ Version information for InvenTree.
|
|||||||
Provides information on the current InvenTree version
|
Provides information on the current InvenTree version
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
@ -99,6 +100,12 @@ def inventreeDjangoVersion():
|
|||||||
def inventreeCommitHash():
|
def inventreeCommitHash():
|
||||||
""" Returns the git commit hash for the running codebase """
|
""" Returns the git commit hash for the running codebase """
|
||||||
|
|
||||||
|
# First look in the environment variables, i.e. if running in docker
|
||||||
|
commit_hash = os.environ.get('INVENTREE_COMMIT_HASH', '')
|
||||||
|
|
||||||
|
if commit_hash:
|
||||||
|
return commit_hash
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return str(subprocess.check_output('git rev-parse --short HEAD'.split()), 'utf-8').strip()
|
return str(subprocess.check_output('git rev-parse --short HEAD'.split()), 'utf-8').strip()
|
||||||
except: # pragma: no cover
|
except: # pragma: no cover
|
||||||
@ -108,6 +115,12 @@ def inventreeCommitHash():
|
|||||||
def inventreeCommitDate():
|
def inventreeCommitDate():
|
||||||
""" Returns the git commit date for the running codebase """
|
""" Returns the git commit date for the running codebase """
|
||||||
|
|
||||||
|
# First look in the environment variables, e.g. if running in docker
|
||||||
|
commit_date = os.environ.get('INVENTREE_COMMIT_DATE', '')
|
||||||
|
|
||||||
|
if commit_date:
|
||||||
|
return commit_date.split(' ')[0]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
d = str(subprocess.check_output('git show -s --format=%ci'.split()), 'utf-8').strip()
|
d = str(subprocess.check_output('git show -s --format=%ci'.split()), 'utf-8').strip()
|
||||||
return d.split(' ')[0]
|
return d.split(' ')[0]
|
||||||
|
@ -203,7 +203,7 @@ class UIMessageNotification(SingleNotificationMethod):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def trigger_notifaction(obj, category=None, obj_ref='pk', **kwargs):
|
def trigger_notification(obj, category=None, obj_ref='pk', **kwargs):
|
||||||
"""
|
"""
|
||||||
Send out a notification
|
Send out a notification
|
||||||
"""
|
"""
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
from io import BytesIO
|
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import FieldError, ValidationError
|
from django.core.exceptions import FieldError, ValidationError
|
||||||
from django.http import HttpResponse, JsonResponse
|
from django.http import HttpResponse, JsonResponse
|
||||||
from django.urls import include, re_path
|
from django.urls import include, re_path
|
||||||
|
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from PIL import Image
|
|
||||||
from rest_framework import filters, generics
|
from rest_framework import filters, generics
|
||||||
from rest_framework.exceptions import NotFound
|
from rest_framework.exceptions import NotFound
|
||||||
|
|
||||||
@ -137,25 +134,21 @@ class LabelPrintMixin:
|
|||||||
# Label instance
|
# Label instance
|
||||||
label_instance = self.get_object()
|
label_instance = self.get_object()
|
||||||
|
|
||||||
for output in outputs:
|
for idx, output in enumerate(outputs):
|
||||||
"""
|
"""
|
||||||
For each output, we generate a temporary image file,
|
For each output, we generate a temporary image file,
|
||||||
which will then get sent to the printer
|
which will then get sent to the printer
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Generate a png image at 300dpi
|
# Generate PDF data for the label
|
||||||
(img_data, w, h) = output.get_document().write_png(resolution=300)
|
pdf = output.get_document().write_pdf()
|
||||||
|
|
||||||
# Construct a BytesIO object, which can be read by pillow
|
|
||||||
img_bytes = BytesIO(img_data)
|
|
||||||
|
|
||||||
image = Image.open(img_bytes)
|
|
||||||
|
|
||||||
# Offload a background task to print the provided label
|
# Offload a background task to print the provided label
|
||||||
offload_task(
|
offload_task(
|
||||||
plugin_label.print_label,
|
plugin_label.print_label,
|
||||||
plugin.plugin_slug(),
|
plugin.plugin_slug(),
|
||||||
image,
|
pdf,
|
||||||
|
filename=label_names[idx],
|
||||||
label_instance=label_instance,
|
label_instance=label_instance,
|
||||||
user=request.user,
|
user=request.user,
|
||||||
)
|
)
|
||||||
|
@ -24,7 +24,7 @@ def notify_low_stock(part: part.models.Part):
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
common.notifications.trigger_notifaction(
|
common.notifications.trigger_notification(
|
||||||
part,
|
part,
|
||||||
'part.notify_low_stock',
|
'part.notify_low_stock',
|
||||||
target_fnc=part.get_subscribers,
|
target_fnc=part.get_subscribers,
|
||||||
|
@ -1098,7 +1098,7 @@ class PartDetailTests(InvenTreeAPITestCase):
|
|||||||
self.assertIn('Upload a valid image', str(response.data))
|
self.assertIn('Upload a valid image', str(response.data))
|
||||||
|
|
||||||
# Now try to upload a valid image file, in multiple formats
|
# Now try to upload a valid image file, in multiple formats
|
||||||
for fmt in ['jpg', 'png', 'bmp', 'webp']:
|
for fmt in ['jpg', 'j2k', 'png', 'bmp', 'webp']:
|
||||||
fn = f'dummy_image.{fmt}'
|
fn = f'dummy_image.{fmt}'
|
||||||
|
|
||||||
img = PIL.Image.new('RGB', (128, 128), color='red')
|
img = PIL.Image.new('RGB', (128, 128), color='red')
|
||||||
|
@ -1,7 +1,14 @@
|
|||||||
"""Functions to print a label to a mixin printer"""
|
"""Functions to print a label to a mixin printer"""
|
||||||
import logging
|
import logging
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from django.views.debug import ExceptionReporter
|
||||||
|
|
||||||
|
import pdf2image
|
||||||
|
from error_report.models import Error
|
||||||
|
|
||||||
import common.notifications
|
import common.notifications
|
||||||
from plugin.registry import registry
|
from plugin.registry import registry
|
||||||
@ -9,7 +16,7 @@ from plugin.registry import registry
|
|||||||
logger = logging.getLogger('inventree')
|
logger = logging.getLogger('inventree')
|
||||||
|
|
||||||
|
|
||||||
def print_label(plugin_slug, label_image, label_instance=None, user=None):
|
def print_label(plugin_slug, pdf_data, filename=None, label_instance=None, user=None):
|
||||||
"""
|
"""
|
||||||
Print label with the provided plugin.
|
Print label with the provided plugin.
|
||||||
|
|
||||||
@ -19,10 +26,11 @@ def print_label(plugin_slug, label_image, label_instance=None, user=None):
|
|||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
plugin_slug: The unique slug (key) of the plugin
|
plugin_slug: The unique slug (key) of the plugin
|
||||||
label_image: A PIL.Image image object to be printed
|
pdf_data: Binary PDF data
|
||||||
|
filename: The intended name of the printed label
|
||||||
"""
|
"""
|
||||||
|
|
||||||
logger.info(f"Plugin '{plugin_slug}' is printing a label")
|
logger.info(f"Plugin '{plugin_slug}' is printing a label '{filename}'")
|
||||||
|
|
||||||
plugin = registry.plugins.get(plugin_slug, None)
|
plugin = registry.plugins.get(plugin_slug, None)
|
||||||
|
|
||||||
@ -30,8 +38,22 @@ def print_label(plugin_slug, label_image, label_instance=None, user=None):
|
|||||||
logger.error(f"Could not find matching plugin for '{plugin_slug}'")
|
logger.error(f"Could not find matching plugin for '{plugin_slug}'")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# In addition to providing a .pdf image, we'll also provide a .png file
|
||||||
|
png_file = pdf2image.convert_from_bytes(
|
||||||
|
pdf_data,
|
||||||
|
dpi=300,
|
||||||
|
)[0]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
plugin.print_label(label_image, width=label_instance.width, height=label_instance.height)
|
plugin.print_label(
|
||||||
|
pdf_data=pdf_data,
|
||||||
|
png_file=png_file,
|
||||||
|
filename=filename,
|
||||||
|
label_instance=label_instance,
|
||||||
|
width=label_instance.width,
|
||||||
|
height=label_instance.height,
|
||||||
|
user=user
|
||||||
|
)
|
||||||
except Exception as e: # pragma: no cover
|
except Exception as e: # pragma: no cover
|
||||||
# Plugin threw an error - notify the user who attempted to print
|
# Plugin threw an error - notify the user who attempted to print
|
||||||
|
|
||||||
@ -40,13 +62,28 @@ def print_label(plugin_slug, label_image, label_instance=None, user=None):
|
|||||||
'message': str(e),
|
'message': str(e),
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.error(f"Label printing failed: Sending notification to user '{user}'")
|
# Log an error message to the database
|
||||||
|
kind, info, data = sys.exc_info()
|
||||||
|
|
||||||
|
Error.objects.create(
|
||||||
|
kind=kind.__name__,
|
||||||
|
info=info,
|
||||||
|
data='\n'.join(traceback.format_exception(kind, info, data)),
|
||||||
|
path='print_label',
|
||||||
|
html=ExceptionReporter(None, kind, info, data).get_traceback_html(),
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.error(f"Label printing failed: Sending notification to user '{user}'") # pragma: no cover
|
||||||
|
|
||||||
# Throw an error against the plugin instance
|
# Throw an error against the plugin instance
|
||||||
common.notifications.trigger_notifaction(
|
common.notifications.trigger_notification(
|
||||||
plugin.plugin_config(),
|
plugin.plugin_config(),
|
||||||
'label.printing_failed',
|
'label.printing_failed',
|
||||||
targets=[user],
|
targets=[user],
|
||||||
context=ctx,
|
context=ctx,
|
||||||
delivery_methods=[common.notifications.UIMessageNotification]
|
delivery_methods=set([common.notifications.UIMessageNotification])
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if settings.TESTING:
|
||||||
|
# If we are in testing mode, we want to know about this exception
|
||||||
|
raise e
|
||||||
|
@ -22,17 +22,18 @@ class LabelPrintingMixin:
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
self.add_mixin('labels', True, __class__)
|
self.add_mixin('labels', True, __class__)
|
||||||
|
|
||||||
def print_label(self, label, **kwargs):
|
def print_label(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
Callback to print a single label
|
Callback to print a single label
|
||||||
|
|
||||||
Arguments:
|
|
||||||
label: A black-and-white pillow Image object
|
|
||||||
|
|
||||||
kwargs:
|
kwargs:
|
||||||
length: The length of the label (in mm)
|
pdf_data: Raw PDF data of the rendered label
|
||||||
width: The width of the label (in mm)
|
png_file: An in-memory PIL image file, rendered at 300dpi
|
||||||
|
label_instance: The instance of the label model which triggered the print_label() method
|
||||||
|
width: The expected width of the label (in mm)
|
||||||
|
height: The expected height of the label (in mm)
|
||||||
|
filename: The filename of this PDF label
|
||||||
|
user: The user who printed this label
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Unimplemented (to be implemented by the particular plugin class)
|
# Unimplemented (to be implemented by the particular plugin class)
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
"""Unit tests for the label printing mixin"""
|
"""Unit tests for the label printing mixin"""
|
||||||
|
import os
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
from common.models import InvenTreeSetting
|
from common.models import InvenTreeSetting
|
||||||
from InvenTree.api_tester import InvenTreeAPITestCase
|
from InvenTree.api_tester import InvenTreeAPITestCase
|
||||||
from label.models import PartLabel, StockItemLabel, StockLocationLabel
|
from label.models import PartLabel, StockItemLabel, StockLocationLabel
|
||||||
@ -68,7 +71,7 @@ class LabelMixinTests(InvenTreeAPITestCase):
|
|||||||
|
|
||||||
with self.assertRaises(MixinNotImplementedError):
|
with self.assertRaises(MixinNotImplementedError):
|
||||||
plugin = WrongPlugin()
|
plugin = WrongPlugin()
|
||||||
plugin.print_label('test')
|
plugin.print_label(filename='test')
|
||||||
|
|
||||||
def test_installed(self):
|
def test_installed(self):
|
||||||
"""Test that the sample printing plugin is installed"""
|
"""Test that the sample printing plugin is installed"""
|
||||||
@ -167,6 +170,21 @@ class LabelMixinTests(InvenTreeAPITestCase):
|
|||||||
# Print no part
|
# Print no part
|
||||||
self.get(self.do_url(None, plugin_ref, label), expected_code=400)
|
self.get(self.do_url(None, plugin_ref, label), expected_code=400)
|
||||||
|
|
||||||
|
# Test that the labels have been printed
|
||||||
|
# The sample labelling plugin simply prints to file
|
||||||
|
self.assertTrue(os.path.exists('label.pdf'))
|
||||||
|
|
||||||
|
# Read the raw .pdf data - ensure it contains some sensible information
|
||||||
|
with open('label.pdf', 'rb') as f:
|
||||||
|
pdf_data = str(f.read())
|
||||||
|
self.assertIn('WeasyPrint', pdf_data)
|
||||||
|
|
||||||
|
# Check that the .png file has already been created
|
||||||
|
self.assertTrue(os.path.exists('label.png'))
|
||||||
|
|
||||||
|
# And that it is a valid image file
|
||||||
|
Image.open('label.png')
|
||||||
|
|
||||||
def test_printing_endpoints(self):
|
def test_printing_endpoints(self):
|
||||||
"""Cover the endpoints not covered by `test_printing_process`"""
|
"""Cover the endpoints not covered by `test_printing_process`"""
|
||||||
plugin_ref = 'samplelabel'
|
plugin_ref = 'samplelabel'
|
||||||
|
@ -12,7 +12,22 @@ class SampleLabelPrinter(LabelPrintingMixin, InvenTreePlugin):
|
|||||||
SLUG = "samplelabel"
|
SLUG = "samplelabel"
|
||||||
TITLE = "Sample Label Printer"
|
TITLE = "Sample Label Printer"
|
||||||
DESCRIPTION = "A sample plugin which provides a (fake) label printer interface"
|
DESCRIPTION = "A sample plugin which provides a (fake) label printer interface"
|
||||||
VERSION = "0.1"
|
VERSION = "0.2"
|
||||||
|
|
||||||
def print_label(self, label, **kwargs):
|
def print_label(self, **kwargs):
|
||||||
print("OK PRINTING")
|
|
||||||
|
# Test that the expected kwargs are present
|
||||||
|
print(f"Printing Label: {kwargs['filename']} (User: {kwargs['user']})")
|
||||||
|
print(f"Width: {kwargs['width']} x Height: {kwargs['height']}")
|
||||||
|
|
||||||
|
pdf_data = kwargs['pdf_data']
|
||||||
|
png_file = kwargs['png_file']
|
||||||
|
|
||||||
|
filename = kwargs['filename']
|
||||||
|
|
||||||
|
# Dump the PDF to a local file
|
||||||
|
with open(filename, 'wb') as pdf_out:
|
||||||
|
pdf_out.write(pdf_data)
|
||||||
|
|
||||||
|
# Save the PNG to disk
|
||||||
|
png_file.save(filename.replace('.pdf', '.png'))
|
||||||
|
@ -1,8 +1,19 @@
|
|||||||
"""
|
"""
|
||||||
On release, ensure that the release tag matches the InvenTree version number!
|
Ensure that the release tag matches the InvenTree version number:
|
||||||
|
|
||||||
|
master / main branch:
|
||||||
|
- version number must end with 'dev'
|
||||||
|
|
||||||
|
stable branch:
|
||||||
|
- version number must *not* end with 'dev'
|
||||||
|
- version number cannot already exist as a release tag
|
||||||
|
|
||||||
|
tagged branch:
|
||||||
|
- version number must match tag being built
|
||||||
|
- version number cannot already exist as a release tag
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
@ -11,6 +22,15 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
here = os.path.abspath(os.path.dirname(__file__))
|
here = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
|
||||||
|
# GITHUB_REF_TYPE may be either 'branch' or 'tag'
|
||||||
|
GITHUB_REF_TYPE = os.environ['GITHUB_REF_TYPE']
|
||||||
|
|
||||||
|
# GITHUB_REF may be either 'refs/heads/<branch>' or 'refs/heads/<tag>'
|
||||||
|
GITHUB_REF = os.environ['GITHUB_REF']
|
||||||
|
|
||||||
|
# GITHUB_BASE_REF is the base branch e.g. 'master' or 'stable'
|
||||||
|
GITHUB_BASE_REF = os.environ['GITHUB_BASE_REF']
|
||||||
|
|
||||||
version_file = os.path.join(here, '..', 'InvenTree', 'InvenTree', 'version.py')
|
version_file = os.path.join(here, '..', 'InvenTree', 'InvenTree', 'version.py')
|
||||||
|
|
||||||
version = None
|
version = None
|
||||||
@ -30,66 +50,65 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
print(f"InvenTree Version: '{version}'")
|
print(f"InvenTree Version: '{version}'")
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
# Determine which docker tag we are going to use
|
||||||
parser.add_argument('-t', '--tag', help='Compare against specified version tag', action='store')
|
docker_tag = None
|
||||||
parser.add_argument('-r', '--release', help='Check that this is a release version', action='store_true')
|
|
||||||
parser.add_argument('-d', '--dev', help='Check that this is a development version', action='store_true')
|
|
||||||
parser.add_argument('-b', '--branch', help='Check against a particular branch', action='store')
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
if GITHUB_BASE_REF == 'stable' and GITHUB_REF_TYPE == 'branch':
|
||||||
|
print("Checking requirements for 'stable' release")
|
||||||
if args.branch:
|
|
||||||
"""
|
|
||||||
Version number requirement depends on format of branch
|
|
||||||
|
|
||||||
'master': development branch
|
|
||||||
'stable': release branch
|
|
||||||
"""
|
|
||||||
|
|
||||||
print(f"Checking version number for branch '{args.branch}'")
|
|
||||||
|
|
||||||
if args.branch == 'master':
|
|
||||||
print("- This is a development branch")
|
|
||||||
args.dev = True
|
|
||||||
elif args.branch == 'stable':
|
|
||||||
print("- This is a stable release branch")
|
|
||||||
args.release = True
|
|
||||||
|
|
||||||
if args.dev:
|
|
||||||
"""
|
|
||||||
Check that the current verrsion number matches the "development" format
|
|
||||||
e.g. "0.5 dev"
|
|
||||||
"""
|
|
||||||
|
|
||||||
print("Checking development branch")
|
|
||||||
|
|
||||||
pattern = r"^\d+(\.\d+)+ dev$"
|
|
||||||
|
|
||||||
result = re.match(pattern, version)
|
|
||||||
|
|
||||||
if result is None:
|
|
||||||
print(f"Version number '{version}' does not match required pattern for development branch")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
elif args.release:
|
|
||||||
"""
|
|
||||||
Check that the current version number matches the "release" format
|
|
||||||
e.g. "0.5.1"
|
|
||||||
"""
|
|
||||||
|
|
||||||
print("Checking release branch")
|
|
||||||
|
|
||||||
pattern = r"^\d+(\.\d+)+$"
|
pattern = r"^\d+(\.\d+)+$"
|
||||||
|
|
||||||
result = re.match(pattern, version)
|
result = re.match(pattern, version)
|
||||||
|
|
||||||
if result is None:
|
if result is None:
|
||||||
print(f"Version number '{version}' does not match required pattern for stable branch")
|
print(f"Version number '{version}' does not match required pattern for stable branch")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
print(f"Version number '{version}' matches stable branch")
|
||||||
|
|
||||||
if args.tag:
|
docker_tag = 'stable'
|
||||||
if args.tag != version:
|
|
||||||
print(f"Release tag '{args.tag}' does not match INVENTREE_SW_VERSION '{version}'")
|
elif GITHUB_BASE_REF in ['master', 'main'] and GITHUB_REF_TYPE == 'branch':
|
||||||
|
print("Checking requirements for main development branch:")
|
||||||
|
|
||||||
|
pattern = r"^\d+(\.\d+)+ dev$"
|
||||||
|
result = re.match(pattern, version)
|
||||||
|
|
||||||
|
if result is None:
|
||||||
|
print(f"Version number '{version}' does not match required pattern for development branch")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
print(f"Version number '{version}' matches development branch")
|
||||||
|
|
||||||
sys.exit(0)
|
docker_tag = 'latest'
|
||||||
|
|
||||||
|
elif GITHUB_REF_TYPE == 'tag':
|
||||||
|
# GITHUB_REF should be of th eform /refs/heads/<tag>
|
||||||
|
version_tag = GITHUB_REF.split('/')[-1]
|
||||||
|
print(f"Checking requirements for tagged release - '{version_tag}'")
|
||||||
|
|
||||||
|
if version_tag != version:
|
||||||
|
print(f"Version number '{version}' does not match tag '{version_tag}'")
|
||||||
|
sys.exit
|
||||||
|
|
||||||
|
# TODO: Check if there is already a release with this tag!
|
||||||
|
|
||||||
|
docker_tag = version_tag
|
||||||
|
|
||||||
|
else:
|
||||||
|
print("Unsupported branch / version combination:")
|
||||||
|
print(f"InvenTree Version: {version}")
|
||||||
|
print("GITHUB_REF_TYPE:", GITHUB_REF_TYPE)
|
||||||
|
print("GITHUB_REF:", GITHUB_REF)
|
||||||
|
print("GITHUB_BASE_REF:", GITHUB_BASE_REF)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if docker_tag is None:
|
||||||
|
print("Docker tag could not be determined")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print(f"Version check passed for '{version}'!")
|
||||||
|
print(f"Docker tag: '{docker_tag}'")
|
||||||
|
|
||||||
|
# Ref: https://getridbug.com/python/how-to-set-environment-variables-in-github-actions-using-python/
|
||||||
|
with open(os.getenv('GITHUB_ENV'), 'a') as env_file:
|
||||||
|
env_file.write(f"docker_tag={docker_tag}\n")
|
||||||
|
@ -101,4 +101,4 @@ volumes:
|
|||||||
o: bind
|
o: bind
|
||||||
# This directory specified where InvenTree source code is stored "outside" the docker containers
|
# This directory specified where InvenTree source code is stored "outside" the docker containers
|
||||||
# By default, this directory is one level above the "docker" directory
|
# By default, this directory is one level above the "docker" directory
|
||||||
device: ${INVENTREE_EXT_VOLUME:-../}
|
device: ${INVENTREE_EXT_VOLUME:-./}
|
@ -1,4 +1,4 @@
|
|||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
# exit when any command fails
|
# exit when any command fails
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
@ -16,6 +16,12 @@ INVENTREE_WEB_PORT=1337
|
|||||||
INVENTREE_DEBUG=False
|
INVENTREE_DEBUG=False
|
||||||
INVENTREE_LOG_LEVEL=WARNING
|
INVENTREE_LOG_LEVEL=WARNING
|
||||||
|
|
||||||
|
# InvenTree admin account details
|
||||||
|
# Un-comment (and complete) these lines to auto-create an admin acount
|
||||||
|
#INVENTREE_ADMIN_USER=
|
||||||
|
#INVENTREE_ADMIN_PASSWORD=
|
||||||
|
#INVENTREE_ADMIN_EMAIL=
|
||||||
|
|
||||||
# Database configuration options
|
# Database configuration options
|
||||||
# Note: The example setup is for a PostgreSQL database
|
# Note: The example setup is for a PostgreSQL database
|
||||||
INVENTREE_DB_ENGINE=postgresql
|
INVENTREE_DB_ENGINE=postgresql
|
||||||
|
@ -29,16 +29,16 @@ django-sslserver==0.22 # Secure HTTP development server
|
|||||||
django-stdimage==5.1.1 # Advanced ImageField management
|
django-stdimage==5.1.1 # Advanced ImageField management
|
||||||
django-test-migrations==1.1.0 # Unit testing for database migrations
|
django-test-migrations==1.1.0 # Unit testing for database migrations
|
||||||
django-user-sessions==1.7.1 # user sessions in DB
|
django-user-sessions==1.7.1 # user sessions in DB
|
||||||
django-weasyprint==1.0.1 # django weasyprint integration
|
django-weasyprint==2.1.0 # django weasyprint integration
|
||||||
djangorestframework==3.12.4 # DRF framework
|
djangorestframework==3.12.4 # DRF framework
|
||||||
django-xforwardedfor-middleware==2.0 # IP forwarding metadata
|
django-xforwardedfor-middleware==2.0 # IP forwarding metadata
|
||||||
flake8==3.8.3 # PEP checking
|
flake8==3.8.3 # PEP checking
|
||||||
flake8-docstrings==1.6.0 # docstring format testing
|
flake8-docstrings==1.6.0 # docstring format testing
|
||||||
gunicorn>=20.1.0 # Gunicorn web server
|
gunicorn>=20.1.0 # Gunicorn web server
|
||||||
importlib_metadata # Backport for importlib.metadata
|
importlib_metadata # Backport for importlib.metadata
|
||||||
inventree # Install the latest version of the InvenTree API python library
|
|
||||||
isort==5.10.1 # DEV: python import sorting
|
isort==5.10.1 # DEV: python import sorting
|
||||||
markdown==3.3.4 # Force particular version of markdown
|
markdown==3.3.4 # Force particular version of markdown
|
||||||
|
pdf2image==1.16.0 # PDF to image conversion
|
||||||
pep8-naming==0.11.1 # PEP naming convention extension
|
pep8-naming==0.11.1 # PEP naming convention extension
|
||||||
pre-commit==2.19.0 # Git pre-commit
|
pre-commit==2.19.0 # Git pre-commit
|
||||||
pillow==9.1.0 # Image manipulation
|
pillow==9.1.0 # Image manipulation
|
||||||
@ -48,4 +48,4 @@ python-barcode[images]==0.13.1 # Barcode generator
|
|||||||
qrcode[pil]==6.1 # QR code generator
|
qrcode[pil]==6.1 # QR code generator
|
||||||
rapidfuzz==0.7.6 # Fuzzy string matching
|
rapidfuzz==0.7.6 # Fuzzy string matching
|
||||||
tablib[xls,xlsx,yaml] # Support for XLS and XLSX formats
|
tablib[xls,xlsx,yaml] # Support for XLS and XLSX formats
|
||||||
weasyprint==52.5 # PDF generation library (Note: in the future need to update to 53)
|
weasyprint==55.0 # PDF generation library
|
||||||
|
4
tasks.py
4
tasks.py
@ -82,7 +82,7 @@ def plugins(c):
|
|||||||
print(f"Installing plugin packages from '{plugin_file}'")
|
print(f"Installing plugin packages from '{plugin_file}'")
|
||||||
|
|
||||||
# Install the plugins
|
# Install the plugins
|
||||||
c.run(f"pip3 install -U -r '{plugin_file}'")
|
c.run(f"pip3 install --disable-pip-version-check -U -r '{plugin_file}'")
|
||||||
|
|
||||||
|
|
||||||
@task(post=[plugins])
|
@task(post=[plugins])
|
||||||
@ -94,7 +94,7 @@ def install(c):
|
|||||||
print("Installing required python packages from 'requirements.txt'")
|
print("Installing required python packages from 'requirements.txt'")
|
||||||
|
|
||||||
# Install required Python packages with PIP
|
# Install required Python packages with PIP
|
||||||
c.run('pip3 install -U -r requirements.txt')
|
c.run('pip3 install --no-cache-dir --disable-pip-version-check -U -r requirements.txt')
|
||||||
|
|
||||||
|
|
||||||
@task
|
@task
|
||||||
|
Loading…
Reference in New Issue
Block a user