Signed-off-by: Ivan Schaller <ivan@schaller.sh>
This commit is contained in:
Ivan Schaller 2023-06-21 23:31:47 +02:00
commit a556518577
Signed by: olofvndrhr
GPG key ID: 2A6BE07D99C8C205
34 changed files with 1585 additions and 0 deletions

5
.dockerignore Normal file
View file

@ -0,0 +1,5 @@
development/
dist/
.*/
!development/config/

1
.envrc Normal file
View file

@ -0,0 +1 @@
use asdf

161
.gitignore vendored Normal file
View file

@ -0,0 +1,161 @@
test.ipynb
test.py
mangadexdlp/__pycache__/
test/
downloads/
.vscode/
.pytest_cache&
.vscode/
__pycache__/
.pytest_cache/
chaps.txt
mangas.txt
.idea/
venv
test.sh
.ruff_cache/
development/db-data/
development/redis-data/
development/reports/
development/scripts/
development/media/
### Python template
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/

5
.tool-versions Normal file
View file

@ -0,0 +1,5 @@
python 3.9.13 3.10.5 3.8.13
shellcheck 0.9.0
shfmt 3.6.0
direnv 2.32.2
just 1.13.0

View file

@ -0,0 +1,76 @@
###################
# publish release #
###################
# branch: master
# event: tag
platform: linux/amd64
depends_on:
- tests
clone:
git:
image: woodpeckerci/plugin-git:v1.6.0
when:
event: tag
pipeline:
# build wheel and dist
build-pypi:
image: cr.44net.ch/ci-plugins/tests
pull: true
when:
event: tag
commands:
- python3 -m hatch build --clean
# create release-notes
create-release-notes:
image: cr.44net.ch/baseimages/debian-base
pull: true
when:
event: tag
commands:
- bash get_release_notes.sh ${CI_COMMIT_TAG%%-dev}
# publish release on github (github.com/olofvndrhr/manga-dlp)
publish-release-github:
image: woodpeckerci/plugin-github-release
pull: true
when:
event: tag
settings:
api_key:
from_secret: github-olofvndrhr-token
files: dist/*
title: ${CI_COMMIT_TAG}
note: RELEASENOTES.md
# publish release on gitea (git.44net.ch/olofvndrhr/manga-dlp)
publish-release-gitea:
image: woodpeckerci/plugin-gitea-release
pull: true
when:
event: tag
settings:
api_key:
from_secret: gitea-olofvndrhr-token
base_url: https://git.44net.ch
files: dist/*
title: ${CI_COMMIT_TAG}
note: RELEASENOTES.md
# release pypi
release-pypi:
image: cr.44net.ch/ci-plugins/tests
pull: true
when:
event: tag
secrets:
- source: pypi_username
target: HATCH_INDEX_USER
- source: pypi_token
target: HATCH_INDEX_AUTH
commands:
- python3 -m hatch publish --no-prompt --yes

View file

@ -0,0 +1,39 @@
################
# test release #
################
# branch: master
# event: pull_request
platform: linux/amd64
depends_on:
- tests
clone:
git:
image: woodpeckerci/plugin-git:v1.6.0
when:
branch: master
event: pull_request
pipeline:
# build wheel and dist
test-build-pypi:
image: cr.44net.ch/ci-plugins/tests
pull: true
when:
branch: master
event: pull_request
commands:
- python3 -m hatch build --clean
# create release-notes
test-create-release-notes:
image: cr.44net.ch/baseimages/debian-base
pull: true
when:
branch: master
event: pull_request
commands:
- bash get_release_notes.sh latest
- cat RELEASENOTES.md

51
.woodpecker/tests.yml Normal file
View file

@ -0,0 +1,51 @@
##############################
# code testing and analysis #
#############################
# branch: all
# event: all
platform: linux/amd64
clone:
git:
image: woodpeckerci/plugin-git:v1.6.0
pipeline:
# check code style - shell
test-shfmt:
image: cr.44net.ch/ci-plugins/tests
pull: true
commands:
- just test_shfmt
# check code style - python
test-black:
image: cr.44net.ch/ci-plugins/tests
pull: true
commands:
- just test_black
# check static typing - python
test-pyright:
image: cr.44net.ch/ci-plugins/tests
pull: true
commands:
- just install_deps
- just test_pyright
# ruff test - python
test-ruff:
image: cr.44net.ch/ci-plugins/tests
pull: true
commands:
- just test_ruff
# analyse code with sonarqube and upload it
sonarqube-analysis:
image: cr.44net.ch/ci-plugins/sonar-scanner
pull: true
settings:
sonar_host: https://sonarqube.44net.ch
sonar_token:
from_secret: sq-44net-token
usingProperties: true

24
CHANGELOG.md Normal file
View file

@ -0,0 +1,24 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres
to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
- x
## [0.0.1] - 2023-07-01
### Added
- x
### Fixed
- x
### Changed
- x

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Ivan Schaller
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

1
MANIFEST.in Normal file
View file

@ -0,0 +1 @@
recursive-include netbox_plugin_qrcode *.py

80
README.md Normal file
View file

@ -0,0 +1,80 @@
# netbox_qrgen - python script to download mangas
> Full docs: https://netbox_qrgen.ivn.sh
CI/CD
[![status-badge](https://img.shields.io/drone/build/olofvndrhr/netbox_qrgen?label=tests&server=https%3A%2F%2Fci.44net.ch)](https://ci.44net.ch/olofvndrhr/netbox_qrgen)
[![Last Release](https://img.shields.io/github/release-date/olofvndrhr/netbox_qrgen?label=last%20release)](https://github.com/olofvndrhr/netbox_qrgen/releases)
[![Version](https://img.shields.io/github/v/release/olofvndrhr/netbox_qrgen?label=git%20release)](https://github.com/olofvndrhr/netbox_qrgen/releases)
[![Version PyPi](https://img.shields.io/pypi/v/netbox_qrgen?label=pypi%20release)](https://pypi.org/project/netbox_qrgen/)
Code Analysis
[![Quality Gate Status](https://sonarqube.44net.ch/api/project_badges/measure?project=olofvndrhr%3Anetbox_qrgen&metric=alert_status&token=f9558470580eea5b4899cf33f190eee16011346d)](https://sonarqube.44net.ch/dashboard?id=olofvndrhr%3Anetbox_qrgen)
[![Coverage](https://sonarqube.44net.ch/api/project_badges/measure?project=olofvndrhr%3Anetbox_qrgen&metric=coverage&token=f9558470580eea5b4899cf33f190eee16011346d)](https://sonarqube.44net.ch/dashboard?id=olofvndrhr%3Anetbox_qrgen)
[![Bugs](https://sonarqube.44net.ch/api/project_badges/measure?project=olofvndrhr%3Anetbox_qrgen&metric=bugs&token=f9558470580eea5b4899cf33f190eee16011346d)](https://sonarqube.44net.ch/dashboard?id=olofvndrhr%3Anetbox_qrgen)
[![Security](https://img.shields.io/snyk/vulnerabilities/github/olofvndrhr/netbox_qrgen)](https://app.snyk.io/org/olofvndrhr-t6h/project/aae9609d-a4e4-41f8-b1ac-f2561b2ad4e3)
Meta
[![Code style](https://img.shields.io/badge/code%20style-black-black)](https://github.com/psf/black)
[![Linter](https://img.shields.io/badge/linter-ruff-red)](https://github.com/charliermarsh/ruff)
[![Types](https://img.shields.io/badge/types-pyright-blue)](https://github.com/microsoft/pyright)
[![License](https://img.shields.io/badge/license-MIT-9400d3.svg)](https://snyk.io/learn/what-is-mit-license/)
[![Compatibility](https://img.shields.io/pypi/pyversions/netbox_qrgen)](https://pypi.org/project/netbox_qrgen/)
---
## Description
test
## Features (not complete)
- test
## Usage
### With GitHub
```sh
git clone https://github.com/olofvndrhr/netbox_qrgen.git # clone the repository
cd netbox_qrgen # go in the directory
pip install -r requirements.txt # install required packages
# on windows
python netbox_qrgen.py <options>
# on unix
python3 netbox_qrgen.py <options>
```
### With pip ([pypi](https://pypi.org/project/netbox_qrgen/))
```sh
python3 -m pip install netbox_qrgen # download the package from pypi
python3 -m mangadlp <args> # start the script as a module
OR
netbox_qrgen <args> # call script directly
OR
mangadlp <args> # call script directly
```
## Contribution / Bugs
For suggestions for improvement, just open a pull request.
If you want to add support for a new site, there is an api [template file](contrib/api_template.py) which you can use.
And more infos and tools are in the contrib [README.md](contrib/README.md)
Otherwise, you can open an issue with the name of the site which you want support for (not guaranteed to be
implemented).
If you encounter any bugs, also just open an issue with a description of the problem.
## TODO's
- test

29
development/Dockerfile Normal file
View file

@ -0,0 +1,29 @@
FROM netboxcommunity/netbox:v3.5.4
COPY . /tmp/build/
# install plugins
RUN \
echo "**** installing plugins ****" \
&& /opt/netbox/venv/bin/pip install --no-warn-script-location \
qrcode \
pillow \
netbox-inventory \
/tmp/build/
# cleanup installation
RUN \
echo "**** cleanup ****" \
&& apt-get purge --auto-remove -y \
&& apt-get clean \
&& rm -rf \
/tmp/* \
/var/lib/apt/lists/* \
/var/tmp/*
# activate plugins
ARG SECRET_KEY="dummydummydummydummydummydummydummydummydummydummy"
COPY development/config/plugins.py /etc/netbox/config/plugins.py
RUN \
echo "**** activating plugins ****" \
&& /opt/netbox/venv/bin/python /opt/netbox/netbox/manage.py collectstatic --no-input

45
development/config.env Normal file
View file

@ -0,0 +1,45 @@
DEBUG = True
# server
ALLOWED_HOSTS='localhost ::1 127.0.0.1'
CORS_ORIGIN_ALLOW_ALL=True
# db
DB_HOST=netbox-dev-db
DB_NAME=netbox
DB_PASSWORD="1234"
DB_USER=netbox
# redis
REDIS_DATABASE=0
REDIS_HOST=netbox-dev-redis
REDIS_INSECURE_SKIP_TLS_VERIFY=false
REDIS_PASSWORD=
REDIS_SSL=false
REDIS_CACHE_DATABASE=1
REDIS_CACHE_HOST=netbox-dev-redis
REDIS_CACHE_INSECURE_SKIP_TLS_VERIFY=false
REDIS_CACHE_PASSWORD=
REDIS_CACHE_SSL=false
# other
GRAPHQL_ENABLED=true
HOUSEKEEPING_INTERVAL=86400
MEDIA_ROOT=/opt/netbox/netbox/media
METRICS_ENABLED=false
RELEASE_CHECK_URL=https://api.github.com/repos/netbox-community/netbox/releases
PREFER_IPV4=true
POWERFEED_DEFAULT_AMPERAGE=10
POWERFEED_DEFAULT_VOLTAGE=230
TIME_ZONE=Europe/Zurich
# security
SECRET_KEY='*&J8r3Z%YCqM#GW6J%b7@aC4*&J8r3Z%YCqM#GW6J%b7@aC4*&J8r3Z%YCqM#GW6J%b7@aC4'
SKIP_SUPERUSER=false
SUPERUSER_API_TOKEN='*&J8r3Z%YCqM#GW6J%b7@aC4'
SUPERUSER_EMAIL=admin@abc.net
SUPERUSER_NAME=admin
SUPERUSER_PASSWORD="1234"
WEBHOOKS_ENABLED=true
LOGIN_REQUIRED=true

View file

@ -0,0 +1,351 @@
####
## We recommend to not edit this file.
## Create separate files to overwrite the settings.
## See `extra.py` as an example.
####
import re
from os import environ
from os.path import abspath, dirname, join
from typing import Any, Callable
# For reference see https://docs.netbox.dev/en/stable/configuration/
# Based on https://github.com/netbox-community/netbox/blob/develop/netbox/netbox/configuration_example.py
###
# NetBox-Docker Helper functions
###
# Read secret from file
def _read_secret(secret_name: str, default: str | None = None) -> str | None:
try:
f = open("/run/secrets/" + secret_name, "r", encoding="utf-8")
except EnvironmentError:
return default
else:
with f:
return f.readline().strip()
# If the `map_fn` isn't defined, then the value that is read from the environment (or the default value if not found) is returned.
# If the `map_fn` is defined, then `map_fn` is invoked and the value (that was read from the environment or the default value if not found)
# is passed to it as a parameter. The value returned from `map_fn` is then the return value of this function.
# The `map_fn` is not invoked, if the value (that was read from the environment or the default value if not found) is None.
def _environ_get_and_map(
variable_name: str, default: str | None = None, map_fn: Callable[[str], Any | None] = None
) -> Any | None:
env_value = environ.get(variable_name, default)
if env_value is None:
return env_value
if not map_fn:
return env_value
return map_fn(env_value)
def _AS_BOOL(value):
return value.lower() == "true"
def _AS_INT(value):
return int(value)
def _AS_LIST(value):
return list(filter(None, value.split(" ")))
_BASE_DIR = dirname(dirname(abspath(__file__)))
#########################
# #
# Required settings #
# #
#########################
# This is a list of valid fully-qualified domain names (FQDNs) for the NetBox server. NetBox will not permit write
# access to the server via any other hostnames. The first FQDN in the list will be treated as the preferred name.
#
# Example: ALLOWED_HOSTS = ['netbox.example.com', 'netbox.internal.local']
ALLOWED_HOSTS = environ.get("ALLOWED_HOSTS", "*").split(" ")
# ensure that '*' or 'localhost' is always in ALLOWED_HOSTS (needed for health checks)
if "*" not in ALLOWED_HOSTS and "localhost" not in ALLOWED_HOSTS:
ALLOWED_HOSTS.append("localhost")
# PostgreSQL database configuration. See the Django documentation for a complete list of available parameters:
# https://docs.djangoproject.com/en/stable/ref/settings/#databases
DATABASE = {
"NAME": environ.get("DB_NAME", "netbox"), # Database name
"USER": environ.get("DB_USER", ""), # PostgreSQL username
"PASSWORD": _read_secret("db_password", environ.get("DB_PASSWORD", "")),
# PostgreSQL password
"HOST": environ.get("DB_HOST", "localhost"), # Database server
"PORT": environ.get("DB_PORT", ""), # Database port (leave blank for default)
"OPTIONS": {"sslmode": environ.get("DB_SSLMODE", "prefer")},
# Database connection SSLMODE
"CONN_MAX_AGE": _environ_get_and_map("DB_CONN_MAX_AGE", "300", _AS_INT),
# Max database connection age
"DISABLE_SERVER_SIDE_CURSORS": _environ_get_and_map(
"DB_DISABLE_SERVER_SIDE_CURSORS", "False", _AS_BOOL
),
# Disable the use of server-side cursors transaction pooling
}
# Redis database settings. Redis is used for caching and for queuing background tasks such as webhook events. A separate
# configuration exists for each. Full connection details are required in both sections, and it is strongly recommended
# to use two separate database IDs.
REDIS = {
"tasks": {
"HOST": environ.get("REDIS_HOST", "localhost"),
"PORT": _environ_get_and_map("REDIS_PORT", 6379, _AS_INT),
"USERNAME": environ.get("REDIS_USERNAME", ""),
"PASSWORD": _read_secret("redis_password", environ.get("REDIS_PASSWORD", "")),
"DATABASE": _environ_get_and_map("REDIS_DATABASE", 0, _AS_INT),
"SSL": _environ_get_and_map("REDIS_SSL", "False", _AS_BOOL),
"INSECURE_SKIP_TLS_VERIFY": _environ_get_and_map(
"REDIS_INSECURE_SKIP_TLS_VERIFY", "False", _AS_BOOL
),
},
"caching": {
"HOST": environ.get("REDIS_CACHE_HOST", environ.get("REDIS_HOST", "localhost")),
"PORT": _environ_get_and_map(
"REDIS_CACHE_PORT", environ.get("REDIS_PORT", "6379"), _AS_INT
),
"USERNAME": environ.get("REDIS_CACHE_USERNAME", environ.get("REDIS_USERNAME", "")),
"PASSWORD": _read_secret(
"redis_cache_password",
environ.get("REDIS_CACHE_PASSWORD", environ.get("REDIS_PASSWORD", "")),
),
"DATABASE": _environ_get_and_map("REDIS_CACHE_DATABASE", "1", _AS_INT),
"SSL": _environ_get_and_map("REDIS_CACHE_SSL", environ.get("REDIS_SSL", "False"), _AS_BOOL),
"INSECURE_SKIP_TLS_VERIFY": _environ_get_and_map(
"REDIS_CACHE_INSECURE_SKIP_TLS_VERIFY",
environ.get("REDIS_INSECURE_SKIP_TLS_VERIFY", "False"),
_AS_BOOL,
),
},
}
# This key is used for secure generation of random numbers and strings. It must never be exposed outside of this file.
# For optimal security, SECRET_KEY should be at least 50 characters in length and contain a mix of letters, numbers, and
# symbols. NetBox will not run without this defined. For more information, see
# https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-SECRET_KEY
SECRET_KEY = _read_secret("secret_key", environ.get("SECRET_KEY", ""))
#########################
# #
# Optional settings #
# #
#########################
# # Specify one or more name and email address tuples representing NetBox administrators. These people will be notified of
# # application errors (assuming correct email settings are provided).
# ADMINS = [
# # ['John Doe', 'jdoe@example.com'],
# ]
if "ALLOWED_URL_SCHEMES" in environ:
ALLOWED_URL_SCHEMES = _environ_get_and_map("ALLOWED_URL_SCHEMES", None, _AS_LIST)
# Optionally display a persistent banner at the top and/or bottom of every page. HTML is allowed. To display the same
# content in both banners, define BANNER_TOP and set BANNER_BOTTOM = BANNER_TOP.
if "BANNER_TOP" in environ:
BANNER_TOP = environ.get("BANNER_TOP", None)
if "BANNER_BOTTOM" in environ:
BANNER_BOTTOM = environ.get("BANNER_BOTTOM", None)
# Text to include on the login page above the login form. HTML is allowed.
if "BANNER_LOGIN" in environ:
BANNER_LOGIN = environ.get("BANNER_LOGIN", None)
# Maximum number of days to retain logged changes. Set to 0 to retain changes indefinitely. (Default: 90)
if "CHANGELOG_RETENTION" in environ:
CHANGELOG_RETENTION = _environ_get_and_map("CHANGELOG_RETENTION", None, _AS_INT)
# Maximum number of days to retain job results (scripts and reports). Set to 0 to retain job results in the database indefinitely. (Default: 90)
if "JOBRESULT_RETENTION" in environ:
JOBRESULT_RETENTION = _environ_get_and_map("JOBRESULT_RETENTION", None, _AS_INT)
# API Cross-Origin Resource Sharing (CORS) settings. If CORS_ORIGIN_ALLOW_ALL is set to True, all origins will be
# allowed. Otherwise, define a list of allowed origins using either CORS_ORIGIN_WHITELIST or
# CORS_ORIGIN_REGEX_WHITELIST. For more information, see https://github.com/ottoyiu/django-cors-headers
CORS_ORIGIN_ALLOW_ALL = _environ_get_and_map("CORS_ORIGIN_ALLOW_ALL", "False", _AS_BOOL)
CORS_ORIGIN_WHITELIST = _environ_get_and_map("CORS_ORIGIN_WHITELIST", "https://localhost", _AS_LIST)
CORS_ORIGIN_REGEX_WHITELIST = [
re.compile(r) for r in _environ_get_and_map("CORS_ORIGIN_REGEX_WHITELIST", "", _AS_LIST)
]
# Set to True to enable server debugging. WARNING: Debugging introduces a substantial performance penalty and may reveal
# sensitive information about your installation. Only enable debugging while performing testing.
# Never enable debugging on a production system.
DEBUG = _environ_get_and_map("DEBUG", "False", _AS_BOOL)
# This parameter serves as a safeguard to prevent some potentially dangerous behavior,
# such as generating new database schema migrations.
# Set this to True only if you are actively developing the NetBox code base.
DEVELOPER = _environ_get_and_map("DEVELOPER", "False", _AS_BOOL)
# Email settings
EMAIL = {
"SERVER": environ.get("EMAIL_SERVER", "localhost"),
"PORT": _environ_get_and_map("EMAIL_PORT", 25, _AS_INT),
"USERNAME": environ.get("EMAIL_USERNAME", ""),
"PASSWORD": _read_secret("email_password", environ.get("EMAIL_PASSWORD", "")),
"USE_SSL": _environ_get_and_map("EMAIL_USE_SSL", "False", _AS_BOOL),
"USE_TLS": _environ_get_and_map("EMAIL_USE_TLS", "False", _AS_BOOL),
"SSL_CERTFILE": environ.get("EMAIL_SSL_CERTFILE", ""),
"SSL_KEYFILE": environ.get("EMAIL_SSL_KEYFILE", ""),
"TIMEOUT": _environ_get_and_map("EMAIL_TIMEOUT", 10, _AS_INT), # seconds
"FROM_EMAIL": environ.get("EMAIL_FROM", ""),
}
# Enforcement of unique IP space can be toggled on a per-VRF basis. To enforce unique IP space within the global table
# (all prefixes and IP addresses not assigned to a VRF), set ENFORCE_GLOBAL_UNIQUE to True.
if "ENFORCE_GLOBAL_UNIQUE" in environ:
ENFORCE_GLOBAL_UNIQUE = _environ_get_and_map("ENFORCE_GLOBAL_UNIQUE", None, _AS_BOOL)
# Exempt certain models from the enforcement of view permissions. Models listed here will be viewable by all users and
# by anonymous users. List models in the form `<app>.<model>`. Add '*' to this list to exempt all models.
EXEMPT_VIEW_PERMISSIONS = _environ_get_and_map("EXEMPT_VIEW_PERMISSIONS", "", _AS_LIST)
# HTTP proxies NetBox should use when sending outbound HTTP requests (e.g. for webhooks).
# HTTP_PROXIES = {
# 'http': 'http://10.10.1.10:3128',
# 'https': 'http://10.10.1.10:1080',
# }
# IP addresses recognized as internal to the system. The debugging toolbar will be available only to clients accessing
# NetBox from an internal IP.
INTERNAL_IPS = _environ_get_and_map("INTERNAL_IPS", "127.0.0.1 ::1", _AS_LIST)
# Enable GraphQL API.
if "GRAPHQL_ENABLED" in environ:
GRAPHQL_ENABLED = _environ_get_and_map("GRAPHQL_ENABLED", None, _AS_BOOL)
# # Enable custom logging. Please see the Django documentation for detailed guidance on configuring custom logs:
# # https://docs.djangoproject.com/en/stable/topics/logging/
# LOGGING = {}
# Automatically reset the lifetime of a valid session upon each authenticated request. Enables users to remain
# authenticated to NetBox indefinitely.
LOGIN_PERSISTENCE = _environ_get_and_map("LOGIN_PERSISTENCE", "False", _AS_BOOL)
# Setting this to True will permit only authenticated users to access any part of NetBox. By default, anonymous users
# are permitted to access most data in NetBox (excluding secrets) but not make any changes.
LOGIN_REQUIRED = _environ_get_and_map("LOGIN_REQUIRED", "False", _AS_BOOL)
# The length of time (in seconds) for which a user will remain logged into the web UI before being prompted to
# re-authenticate. (Default: 1209600 [14 days])
LOGIN_TIMEOUT = _environ_get_and_map("LOGIN_TIMEOUT", 1209600, _AS_INT)
# Setting this to True will display a "maintenance mode" banner at the top of every page.
if "MAINTENANCE_MODE" in environ:
MAINTENANCE_MODE = _environ_get_and_map("MAINTENANCE_MODE", None, _AS_BOOL)
# Maps provider
if "MAPS_URL" in environ:
MAPS_URL = environ.get("MAPS_URL", None)
# An API consumer can request an arbitrary number of objects =by appending the "limit" parameter to the URL (e.g.
# "?limit=1000"). This setting defines the maximum limit. Setting it to 0 or None will allow an API consumer to request
# all objects by specifying "?limit=0".
if "MAX_PAGE_SIZE" in environ:
MAX_PAGE_SIZE = _environ_get_and_map("MAX_PAGE_SIZE", None, _AS_INT)
# The file path where uploaded media such as image attachments are stored. A trailing slash is not needed. Note that
# the default value of this setting is derived from the installed location.
MEDIA_ROOT = environ.get("MEDIA_ROOT", join(_BASE_DIR, "media"))
# Expose Prometheus monitoring metrics at the HTTP endpoint '/metrics'
METRICS_ENABLED = _environ_get_and_map("METRICS_ENABLED", "False", _AS_BOOL)
# Determine how many objects to display per page within a list. (Default: 50)
if "PAGINATE_COUNT" in environ:
PAGINATE_COUNT = _environ_get_and_map("PAGINATE_COUNT", None, _AS_INT)
# # Enable installed plugins. Add the name of each plugin to the list.
# PLUGINS = []
# # Plugins configuration settings. These settings are used by various plugins that the user may have installed.
# # Each key in the dictionary is the name of an installed plugin and its value is a dictionary of settings.
# PLUGINS_CONFIG = {
# }
# When determining the primary IP address for a device, IPv6 is preferred over IPv4 by default. Set this to True to
# prefer IPv4 instead.
if "PREFER_IPV4" in environ:
PREFER_IPV4 = _environ_get_and_map("PREFER_IPV4", None, _AS_BOOL)
# The default value for the amperage field when creating new power feeds.
if "POWERFEED_DEFAULT_AMPERAGE" in environ:
POWERFEED_DEFAULT_AMPERAGE = _environ_get_and_map("POWERFEED_DEFAULT_AMPERAGE", None, _AS_INT)
# The default value (percentage) for the max_utilization field when creating new power feeds.
if "POWERFEED_DEFAULT_MAX_UTILIZATION" in environ:
POWERFEED_DEFAULT_MAX_UTILIZATION = _environ_get_and_map(
"POWERFEED_DEFAULT_MAX_UTILIZATION", None, _AS_INT
)
# The default value for the voltage field when creating new power feeds.
if "POWERFEED_DEFAULT_VOLTAGE" in environ:
POWERFEED_DEFAULT_VOLTAGE = _environ_get_and_map("POWERFEED_DEFAULT_VOLTAGE", None, _AS_INT)
# Rack elevation size defaults, in pixels. For best results, the ratio of width to height should be roughly 10:1.
if "RACK_ELEVATION_DEFAULT_UNIT_HEIGHT" in environ:
RACK_ELEVATION_DEFAULT_UNIT_HEIGHT = _environ_get_and_map(
"RACK_ELEVATION_DEFAULT_UNIT_HEIGHT", None, _AS_INT
)
if "RACK_ELEVATION_DEFAULT_UNIT_WIDTH" in environ:
RACK_ELEVATION_DEFAULT_UNIT_WIDTH = _environ_get_and_map(
"RACK_ELEVATION_DEFAULT_UNIT_WIDTH", None, _AS_INT
)
# Remote authentication support
REMOTE_AUTH_ENABLED = _environ_get_and_map("REMOTE_AUTH_ENABLED", "False", _AS_BOOL)
REMOTE_AUTH_BACKEND = environ.get("REMOTE_AUTH_BACKEND", "netbox.authentication.RemoteUserBackend")
REMOTE_AUTH_HEADER = environ.get("REMOTE_AUTH_HEADER", "HTTP_REMOTE_USER")
REMOTE_AUTH_AUTO_CREATE_USER = _environ_get_and_map(
"REMOTE_AUTH_AUTO_CREATE_USER", "True", _AS_BOOL
)
REMOTE_AUTH_DEFAULT_GROUPS = _environ_get_and_map("REMOTE_AUTH_DEFAULT_GROUPS", "", _AS_LIST)
# REMOTE_AUTH_DEFAULT_PERMISSIONS = {}
# This repository is used to check whether there is a new release of NetBox available. Set to None to disable the
# version check or use the URL below to check for release in the official NetBox repository.
RELEASE_CHECK_URL = environ.get("RELEASE_CHECK_URL", None)
# RELEASE_CHECK_URL = 'https://api.github.com/repos/netbox-community/netbox/releases'
# Maximum execution time for background tasks, in seconds.
RQ_DEFAULT_TIMEOUT = _environ_get_and_map("RQ_DEFAULT_TIMEOUT", 300, _AS_INT)
# The name to use for the csrf token cookie.
CSRF_COOKIE_NAME = environ.get("CSRF_COOKIE_NAME", "csrftoken")
# Cross-Site-Request-Forgery-Attack settings. If Netbox is sitting behind a reverse proxy, you might need to set the CSRF_TRUSTED_ORIGINS flag.
# Django 4.0 requires to specify the URL Scheme in this setting. An example environment variable could be specified like:
# CSRF_TRUSTED_ORIGINS=https://demo.netbox.dev http://demo.netbox.dev
CSRF_TRUSTED_ORIGINS = _environ_get_and_map("CSRF_TRUSTED_ORIGINS", "", _AS_LIST)
# The name to use for the session cookie.
SESSION_COOKIE_NAME = environ.get("SESSION_COOKIE_NAME", "sessionid")
# By default, NetBox will store session data in the database. Alternatively, a file path can be specified here to use
# local file storage instead. (This can be useful for enabling authentication on a standby instance with read-only
# database access.) Note that the user as which NetBox runs must have read and write permissions to this path.
SESSION_FILE_PATH = environ.get("SESSION_FILE_PATH", environ.get("SESSIONS_ROOT", None))
# Time zone (default: UTC)
TIME_ZONE = environ.get("TIME_ZONE", "UTC")
# Date/time formatting. See the following link for supported formats:
# https://docs.djangoproject.com/en/stable/ref/templates/builtins/#date
DATE_FORMAT = environ.get("DATE_FORMAT", "N j, Y")
SHORT_DATE_FORMAT = environ.get("SHORT_DATE_FORMAT", "Y-m-d")
TIME_FORMAT = environ.get("TIME_FORMAT", "g:i a")
SHORT_TIME_FORMAT = environ.get("SHORT_TIME_FORMAT", "H:i:s")
DATETIME_FORMAT = environ.get("DATETIME_FORMAT", "N j, Y g:i a")
SHORT_DATETIME_FORMAT = environ.get("SHORT_DATETIME_FORMAT", "Y-m-d H:i")

View file

@ -0,0 +1,55 @@
# # Remove first comment(#) on each line to implement this working logging example.
# # Add LOGLEVEL environment variable to netbox if you use this example & want a different log level.
# from os import environ
LOGLEVEL = "DEBUG"
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"verbose": {
"format": "{levelname} {asctime} {module} {process:d} {thread:d} {message}",
"style": "{",
},
"simple": {
"format": "{levelname} {message}",
"style": "{",
},
},
"filters": {
"require_debug_false": {
"()": "django.utils.log.RequireDebugFalse",
},
},
"handlers": {
"console": {
"level": LOGLEVEL,
"filters": ["require_debug_false"],
"class": "logging.StreamHandler",
"formatter": "simple",
},
"mail_admins": {
"level": LOGLEVEL,
"class": "django.utils.log.AdminEmailHandler",
"filters": ["require_debug_false"],
},
},
"loggers": {
"django": {
"handlers": ["console"],
"propagate": True,
},
"django.request": {
"handlers": ["mail_admins"],
"level": LOGLEVEL,
"propagate": False,
},
"django_auth_ldap": {
"handlers": [
"console",
],
"level": LOGLEVEL,
},
},
}

View file

@ -0,0 +1,49 @@
PLUGINS = [
"netbox_inventory",
"netbox_qrgen",
]
PLUGINS_CONFIG = {
"netbox_inventory": {
"top_level_menu": True,
"used_status_name": "used",
"stored_status_name": "stored",
"sync_hardware_serial_asset_tag": True,
"asset_import_create_purchase": True,
"asset_import_create_device_type": True,
"asset_import_create_module_type": True,
"asset_import_create_inventoryitem_type": True,
"asset_import_create_tenant": True,
"prefill_asset_name_create_inventoryitem": True,
"prefill_asset_tag_create_inventoryitem": True,
},
"netbox_qrgen": {
"qr_with_text": True,
"qr_text_fields": ["name", "serial"],
"qr_font": "TahomaBold",
"qr_custom_text": None,
"qr_text_location": "bottom",
"qr_version": 2,
"qr_error_correction": 2,
"qr_box_size": 6,
"qr_border_size": 4,
"labels": {
"dcim.cable": [
"tenant",
"a_terminations.device",
"a_terminations.name",
"b_terminations.device",
"b_terminations.name",
],
"dcim.rack": [
"tenant",
"site",
"facility_id",
"name",
],
"dcim.device": ["tenant", "name", "serial"],
"dcim.inventoryitem": ["tenant", "name", "serial"],
"circuits.circuit": ["tenant", "name", "serial"],
},
},
}

View file

@ -0,0 +1,85 @@
version: "3"
x-netbox:
&netbox
image: netbox-dev:3.5
build: .
restart: unless-stopped
user: "unit:root"
env_file: config.env
security_opt: [ "no-new-privileges:true" ]
networks:
- netbox
volumes:
- ./config/:/etc/netbox/config/:ro
- ./reports/:/opt/netbox/netbox/reports/
- ./scripts/:/opt/netbox/netbox/scripts/
- ./media/:/opt/netbox/netbox/media/
services:
web:
<<: *netbox
container_name: netbox-dev-web
security_opt: [ "no-new-privileges:true" ]
depends_on:
- db
- redis
ports:
- "8080:8080"
worker:
<<: *netbox
container_name: netbox-dev-worker
depends_on:
- web
command:
[
"/opt/netbox/venv/bin/python",
"/opt/netbox/netbox/manage.py",
"rqworker"
]
housekeeping:
<<: *netbox
container_name: netbox-dev-housekeeping
depends_on:
- web
command: [ "/opt/netbox/housekeeping.sh" ]
db:
image: bitnami/postgresql:14.6.0
container_name: netbox-dev-db
restart: unless-stopped
security_opt: [ "no-new-privileges:true" ]
user: "1000"
networks:
- netbox
volumes:
- ./db-data/:/bitnami/postgresql/
- /etc/localtime:/etc/localtime:ro
environment:
- TZ=Europe/Zurich
- POSTGRESQL_POSTGRES_PASSWORD=${DB_ROOT_PASSWORD}
- POSTGRES_DB=netbox
- POSTGRES_USER=netbox
- POSTGRES_PASSWORD=${DB_PASSWORD}
redis:
image: bitnami/redis:7.0
container_name: netbox-dev-redis
restart: unless-stopped
security_opt: [ "no-new-privileges:true" ]
user: "1000"
networks:
- netbox
volumes:
- ./redis-data/:/bitnami/redis/data/
environment:
- TZ=Europe/Zurich
- ALLOW_EMPTY_PASSWORD=yes
networks:
netbox:
name: netbox-dev
driver: bridge

52
get_release_notes.sh Executable file
View file

@ -0,0 +1,52 @@
#!/bin/bash
# shellcheck disable=SC2016
# script to extract the release notes from the changelog
# show script help
function show_help() {
cat << EOF
Script to generate release-notes from a changelog (CHANGELOG.md)
Usage:
./get_release_notes.sh <new_version>
Example:
./get_release_notes.sh "2.0.5"
or
./get_release_notes.sh "latest"
EOF
exit 0
}
# create changelog for release
function get_release_notes() {
local l_version="${1}"
printf 'Creating release-notes\n'
# check for version
if [[ -z "${l_version}" ]]; then
printf 'You need to specify a version with $1\n'
exit 1
fi
if [[ ${l_version,,} == "latest" ]]; then
l_version="$(grep -o -E "^##\s\[[0-9]{1,2}.[0-9]{1,2}.[0-9]{1,2}\]" CHANGELOG.md | head -n 1 | grep -o -E "[0-9]{1,2}.[0-9]{1,2}.[0-9]{1,2}")"
fi
awk -v ver="[${l_version}]" \
'/^## / { if (p) { exit }; if ($2 == ver) { p=1 } } p && NF' \
'CHANGELOG.md' > 'RELEASENOTES.md'
printf 'Done\n'
}
# check options
case "${1}" in
'--help' | '-h' | 'help')
show_help
;;
*)
get_release_notes "${@}"
;;
esac

131
justfile Executable file
View file

@ -0,0 +1,131 @@
#!/usr/bin/env just --justfile
default: show_receipts
set shell := ["bash", "-uc"]
set dotenv-load := true
#set export
# aliases
alias s := show_receipts
alias i := show_system_info
alias p := prepare_workspace
alias l := lint
alias b := build
# variables
export asdf_version := "v0.11.2"
# default recipe to display help information
show_receipts:
@just --list
show_system_info:
@echo "=================================="
@echo "os : {{os()}}"
@echo "arch: {{arch()}}"
@echo "home: ${HOME}"
@echo "project dir: {{justfile_directory()}}"
@echo "=================================="
install_asdf:
@if asdf --version; then \
echo "asdf already installed installed"; exit 1 \
;fi
@echo "installing asdf"
@echo "asdf version: ${asdf_version}"
@git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch "${asdf_version}"
@echo "adding asdf to .bashrc"
@if ! grep -q ".asdf/asdf.sh" "${HOME}/.bashrc"; then \
echo -e '\n# source asdf' >> "${HOME}/.bashrc" \
;echo 'source "${HOME}/.asdf/asdf.sh"' >> "${HOME}/.bashrc" \
;echo -e 'source "${HOME}/.asdf/completions/asdf.bash"\n' >> "${HOME}/.bashrc" \
;fi
@echo "to load asdf either restart your shell or do: 'source \${HOME}/.bashrc'"
setup_asdf:
@echo "installing asdf bins"
# add plugins
-@asdf plugin add python
-@asdf plugin add just
-@asdf plugin add direnv
# install bins
@asdf install
# setup direnv
@asdf direnv setup --shell bash --version latest
setup_hooks:
@echo "preparing repo hooks"
@if ! lefthook version; then \
echo "lefthook not installed. please install it: https://github.com/evilmartians/lefthook"; exit 1 \
;fi
@echo "installing pre-commit hooks" \
@lefthook install
create_venv:
@echo "creating venv"
@python3 -m pip install --upgrade pip setuptools wheel
@python3 -m venv venv
install_deps:
@echo "installing dependencies"
@pip3 install -r requirements.txt
install_deps_dev:
@echo "installing dev dependencies"
@pip3 install -r requirements_dev.txt
create_reqs:
@echo "creating requirements"
@pipreqs --mode gt --force
test_shfmt:
@find . -type f \( -name "**.sh" -and -not -path "./.**" -and -not -path "./venv**" \) -exec shfmt -d -i 4 -bn -ci -sr "{}" \+;
test_black:
@python3 -m black --check --diff .
test_pyright:
@python3 -m pyright .
test_ruff:
@python3 -m ruff --diff .
test_ci_conf:
@woodpecker-cli lint .woodpecker/
test_pytest:
@python3 -m tox -e basic
test_coverage:
@python3 -m tox -e coverage
test_tox:
@python3 -m tox
build_package:
@python3 -m hatch build --clean
build_container:
@docker build . -f development/Dockerfile -t netbox-dev:3.5
# install dependecies and set everything up
prepare_workspace:
just show_system_info
-just install_asdf
just setup_asdf
just create_venv
just setup_hooks
@echo -e "\n\033[0;32m=== ALL DONE ===\033[0m\n"
lint:
just show_system_info
-just test_ci_conf
just test_black
just test_pyright
just test_ruff
@echo -e "\n\033[0;32m=== ALL DONE ===\033[0m\n"
build:
just build_container
@echo -e "\n\033[0;32m=== ALL DONE ===\033[0m\n"

View file

@ -0,0 +1 @@
__version__ = "0.0.1"

44
netbox_qrgen/__init__.py Normal file
View file

@ -0,0 +1,44 @@
from extras.plugins import PluginConfig
class NetBoxQRGenConfig(PluginConfig):
name = "netbox_qrgen"
verbose_name = "NetBox QRGen"
description = "NetBox plugin to generate QR codes for assets"
version = "0.0.1"
base_url = "qrgen"
min_version = "3.4.0"
author = "Ivan Schaller"
author_email = "ivan@schaller.sh"
default_settings = {
"qr_with_text": True,
"qr_text_fields": ["name", "serial"],
"qr_font": "Tahoma",
"qr_custom_text": None,
"qr_text_location": "right",
"qr_version": 2,
"qr_error_correction": 1,
"qr_box_size": 6,
"qr_border_size": 4,
"labels": {
"dcim.cable": [
"tenant",
"a_terminations.device",
"a_terminations.name",
"b_terminations.device",
"b_terminations.name",
],
"dcim.rack": [
"tenant",
"site",
"facility_id",
"name",
],
"dcim.device": ["tenant", "name", "serial"],
"dcim.inventoryitem": ["tenant", "name", "serial"],
"circuits.circuit": ["tenant", "name", "serial"],
},
}
config = NetBoxQRGenConfig

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
netbox_qrgen/fonts/Tahoma.ttf Executable file

Binary file not shown.

BIN
netbox_qrgen/fonts/TahomaBold.ttf Executable file

Binary file not shown.

View file

@ -0,0 +1,62 @@
from django.conf import settings
from extras.plugins import PluginTemplateExtension
from .utils import generate_qrcode, get_base64
plugin_settings = settings.PLUGINS_CONFIG.get("netbox_qrgen", {})
class QRGen(PluginTemplateExtension):
def get_url(self) -> str:
obj = self.context["object"]
request = self.context["request"]
url: str = request.build_absolute_uri(obj.get_absolute_url())
return url
def get_qrcode(self, url: str) -> str:
img = generate_qrcode(url=url, **plugin_settings)
b64 = get_base64(img)
return b64
def right_page(self):
qr_img = self.get_qrcode(self.get_url())
return self.render(
"netbox_qrgen/qrgen.html",
extra_context={"image": qr_img},
)
class QRGenDevice(QRGen):
model = "dcim.device"
# kind = "device"
class QRGenRack(QRGen):
model = "dcim.rack"
# kind = "rack"
class QRGenCable(QRGen):
model = "dcim.cable"
# kind = "cable"
class QRGenInventoryItem(QRGen):
model = "dcim.inventoryitem"
# kind = "inventoryitem"
class QRGenCircuit(QRGen):
model = "circuits.circuit"
# kind = "circuit"
template_extensions = [
QRGenDevice,
QRGenRack,
QRGenCable,
QRGenInventoryItem,
QRGenCircuit,
]

View file

@ -0,0 +1,26 @@
<script type="text/javascript">
function printImg(source) {
var win = window.open("");
win.document.write(
'<img src="' +
source +
'" onload="window.print();window.close()" />'
);
win.focus();
}
</script>
<div class="card">
<h5 class="card-header">QR Code</h5>
<div class="card-body text-right noprint">
<img src="data:image/png;base64,{{image}}" />
</div>
<div class="card-footer text-end noprint">
<button
onclick="printImg('data:image/png;base64,{{image}}')"
;
class="btn btn-xs btn-primary"
>
<span class="mdi mdi-printer" aria-hidden="true"></span> Print
</button>
</div>
</div>

31
netbox_qrgen/utils.py Normal file
View file

@ -0,0 +1,31 @@
import base64
from io import BytesIO
from typing import Union
import qrcode
from qrcode.image.pure import PyPNGImage
from qrcode.image.svg import SvgPathFillImage
def generate_qrcode(url: str, **kwargs) -> PyPNGImage:
qr = qrcode.QRCode(
version=kwargs["qr_version"],
error_correction=kwargs["qr_error_correction"],
box_size=kwargs["qr_box_size"],
border=kwargs["qr_border_size"],
image_factory=PyPNGImage,
)
qr.add_data(url)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
return img
def get_base64(img: Union[SvgPathFillImage, PyPNGImage]) -> str:
stream = BytesIO()
img.save(stream)
base64_value = base64.b64encode(stream.getvalue()).decode(encoding="ascii")
return base64_value

142
pyproject.toml Normal file
View file

@ -0,0 +1,142 @@
[build-system]
requires = ["hatchling>=1.11.0"]
build-backend = "hatchling.build"
[project]
dynamic = ["version"]
name = "netbox-qrgen"
description = "A netbox plugin to generate qrcodes for assets"
readme = "README.md"
license = "MIT"
requires-python = ">=3.9"
authors = [{ name = "Ivan Schaller", email = "ivan@schaller.sh" }]
keywords = ["netbox", "plugin", "qrcode"]
classifiers = [
"License :: OSI Approved :: MIT License",
"Natural Language :: English",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
]
dependencies = ["qrcode>=7.4.2", "pillow>=9.5.0"]
[project.urls]
Homepage = "https://github.com/olofvndrhr/manga-dlp"
History = "https://github.com/olofvndrhr/manga-dlp/commits/master"
Tracker = "https://github.com/olofvndrhr/manga-dlp/issues"
Source = "https://github.com/olofvndrhr/manga-dlp"
[tool.hatch.version]
path = "netbox_qrgen/__about__.py"
[tool.hatch.build]
ignore-vcs = true
[tool.hatch.build.targets.sdist]
packages = ["netbox_qrgen"]
[tool.hatch.build.targets.wheel]
packages = ["netbox_qrgen"]
# black
[tool.black]
line-length = 100
target-version = ["py39"]
# pyright
[tool.pyright]
typeCheckingMode = "basic"
pythonVersion = "3.9"
reportUnnecessaryTypeIgnoreComment = true
reportShadowedImports = true
reportUnusedExpression = true
reportMatchNotExhaustive = true
# venvPath = "."
# venv = "venv"
# ruff
[tool.ruff]
target-version = "py39"
select = [
"E", # pycodetyle err
"W", # pycodetyle warn
"D", # pydocstyle
"C90", # mccabe
"I", # isort
"PLE", # pylint err
"PLW", # pylint warn
"PLC", # pylint convention
"PLR", # pylint refactor
"F", # pyflakes
"RUF", # ruff specific
]
line-length = 100
fix = true
show-fixes = true
format = "grouped"
ignore-init-module-imports = true
respect-gitignore = true
ignore = ["E501", "D103", "D100", "D102", "PLR2004", "D403"]
#unfixable = ["F401"]
exclude = [
".direnv",
".git",
".mypy_cache",
".ruff_cache",
".svn",
".venv",
"venv",
"__pypackages__",
"build",
"dist",
"venv",
]
[tool.ruff.per-file-ignores]
"__init__.py" = ["D104"]
[tool.ruff.pylint]
max-args = 10
[tool.ruff.mccabe]
max-complexity = 10
[tool.ruff.pydocstyle]
convention = "google"
[tool.ruff.pycodestyle]
max-doc-length = 100
# pytest
[tool.pytest.ini_options]
pythonpath = ["."]
# coverage
[tool.coverage.run]
source = ["mangadlp"]
branch = true
command_line = "-m pytest --exitfirst"
[tool.coverage.report]
# Regexes for lines to exclude from consideration
exclude_lines = [
# Have to re-enable the standard pragma
"pragma: no cover",
# Don't complain about missing debug-only code:
"def __repr__",
"if self.debug",
# Don't complain if tests don't hit defensive assertion code:
"raise AssertionError",
"raise NotImplementedError",
# Don't complain if non-runnable code isn't run:
"if 0:",
"if __name__ == .__main__.:",
# Don't complain about abstract methods, they aren't run:
"@(abc.)?abstractmethod",
]
ignore_errors = true

4
renovate.json Normal file
View file

@ -0,0 +1,4 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["local>44net-assets/docker-renovate-conf"]
}

2
requirements.txt Normal file
View file

@ -0,0 +1,2 @@
qrcode>=7.4.2
pillow>=9.5.0

12
sonar-project.properties Normal file
View file

@ -0,0 +1,12 @@
sonar.projectKey=olofvndrhr:netbox-qrgen
sonar.projectName=olofvndrhr/netbox-qrgen
sonar.links.homepage=https://github.com/olofvndrhr/netbox-qrgen
sonar.links.scm=https://github.com/olofvndrhr/netbox-qrgen
sonar.links.issue=https://github.com/olofvndrhr/netbox-qrgen/issues
sonar.links.ci=https://ci.44net.ch/olofvndrhr/netbox-qrgen
#
sonar.sources=netbox_qrgen
#sonar.tests=
#sonar.exclusions=
sonar.python.version=3.9
#sonar.python.coverage.reportPaths=coverage.xml