diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 7e687f86..84a72e8d 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -1,17 +1,20 @@ name: python_lib description: Installs the given GardenLinux Python library inputs: - version: - description: GardenLinux Python library version - default: "main" + version: + description: GardenLinux Python library version + default: "main" + dev: + description: Install development dependencies + default: false runs: - using: composite - steps: - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - name: Install GardenLinux Python library - shell: bash - run: | - pip install git+https://github.com/gardenlinux/python-gardenlinux-lib.git@${{ inputs.version }} + using: composite + steps: + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + - name: Install GardenLinux Python library + shell: bash + run: | + pip install git+https://github.com/gardenlinux/python-gardenlinux-lib.git@${{ inputs.version }} diff --git a/.github/workflows/bandit.yml b/.github/workflows/bandit.yml index 047eeed3..c29de24a 100644 --- a/.github/workflows/bandit.yml +++ b/.github/workflows/bandit.yml @@ -2,40 +2,30 @@ name: security checks on: push: paths-ignore: - - 'README.md' - - 'docs/**' - - '**/README.md' + - "README.md" + - "docs/**" + - "**/README.md" pull_request: paths-ignore: - - 'README.md' - - 'docs/**' - - '**/README.md' + - "README.md" + - "docs/**" + - "**/README.md" permissions: contents: read jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install bandit - - - name: Simple bandit security checks - run: bandit -ll -ii -r . -f json -o bandit-report.json - - - name: Show Report in Action Output - if: always() - run: cat bandit-report.json - - - name: Upload Bandit Scan Artifact - uses: actions/upload-artifact@v4 - if: always() - with: - name: bandit-findings - path: bandit-report.json - + - uses: actions/checkout@v4 + - uses: gardenlinux/python-gardenlinux-lib/.github/actions/setup@main + - name: Simple bandit security checks + run: make security + - name: Show Report in Action Output + if: always() + run: cat bandit-report.json + - name: Upload Bandit Scan Artifact + uses: actions/upload-artifact@v4 + if: always() + with: + name: bandit-findings + path: bandit-report.json diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml index 81e6a948..8acca308 100644 --- a/.github/workflows/black.yml +++ b/.github/workflows/black.yml @@ -7,4 +7,5 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: psf/black@stable + - uses: gardenlinux/python-gardenlinux-lib/.github/actions/setup@main + - run: make lint diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bf8ffb1e..08aef037 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,28 +2,21 @@ name: Build on: push: paths-ignore: - - 'README.md' - - 'docs/**' - - '**/README.md' + - "README.md" + - "docs/**" + - "**/README.md" pull_request: paths-ignore: - - 'README.md' - - 'docs/**' - - '**/README.md' + - "README.md" + - "docs/**" + - "**/README.md" permissions: contents: read jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install poetry - - name: Simple poetry build no package - run: poetry build - + - uses: actions/checkout@v4 + - uses: gardenlinux/python-gardenlinux-lib/.github/actions/setup@main + - name: Simple poetry build no package + run: make build diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 2609d3ca..04deb4aa 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -3,23 +3,15 @@ name: Update Sphinx documentation on: [push, pull_request, workflow_dispatch] permissions: - contents: write + contents: write jobs: docs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - - name: Install dependencies - run: | - pip install sphinx poetry - - name: Sphinx build - run: | - python -m venv venv - source venv/bin/activate - poetry install - sphinx-build docs _build + - uses: gardenlinux/python-gardenlinux-lib/.github/actions/setup@main + - run: make docs - name: Deploy to GitHub Pages uses: peaceiris/actions-gh-pages@v4 if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} diff --git a/.github/workflows/pytests.yml b/.github/workflows/pytests.yml index c1240499..cb4217f2 100644 --- a/.github/workflows/pytests.yml +++ b/.github/workflows/pytests.yml @@ -9,30 +9,10 @@ on: jobs: test: runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - submodules: 'true' - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install poetry - - name: Install dependencies - run: poetry install - - name: Install Zot (OCI Registry) + - uses: actions/checkout@v4 + - uses: gardenlinux/python-gardenlinux-lib/.github/actions/setup@main + - name: Run tests run: | - sudo wget -O /usr/bin/zot https://github.com/project-zot/zot/releases/download/v2.1.0/zot-linux-amd64 - sudo chmod +x /usr/bin/zot - sudo chown root:root /usr/bin/zot - - name: Run tests with pytest - run: | - export GLOCI_REGISTRY_USERNAME="gardenlinux" export GLOCI_REGISTRY_TOKEN="invalid" - poetry run pytest -k "not kms" - + make test diff --git a/.gitignore b/.gitignore index 649122c5..b06abbe6 100644 --- a/.gitignore +++ b/.gitignore @@ -162,3 +162,9 @@ cython_debug/ # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. .idea/ + +# bandit +bandit-report.json + +# zot +test-data/zot diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..15cff14a --- /dev/null +++ b/Makefile @@ -0,0 +1,78 @@ +.PHONY: build install install-dev install-docs test format lint security docs clean help + +POETRY := poetry + +help: + @echo "Available targets:" + @echo " build - Build the package" + @echo " install - Install the package and dependencies" + @echo " install-dev - Install the package and dev dependencies" + @echo " test - Run tests" + @echo " format - Format code with black" + @echo " lint - Run linting checks" + @echo " security - Run security checks with bandit" + @echo " docs - Build the documentation" + @echo " clean - Clean build artifacts and cache" + @echo " help - Show this help message" + +build: install-dev + $(POETRY) build + +install: + $(POETRY) install + +install-dev: + which $(POETRY) || pip install poetry + $(POETRY) install --with dev + +install-docs: install-dev + $(POETRY) install --with dev,docs + +install-test: install-dev + @if [ ! -f ./test-data/zot ]; then \ + if [ "$(shell uname)" = "Darwin" ] && [ "$(shell uname -m)" = "arm64" ]; then \ + curl -L -o ./test-data/zot https://github.com/project-zot/zot/releases/download/v2.1.0/zot-darwin-arm64; \ + elif [ "$(shell uname)" = "Linux" ] && [ "$(shell uname -m)" = "x86_64" ]; then \ + curl -L -o ./test-data/zot https://github.com/project-zot/zot/releases/download/v2.1.0/zot-linux-amd64; \ + else \ + echo "Unsupported platform or architecture"; \ + exit 1; \ + fi; \ + fi + chmod +x ./test-data/zot + rm -rf test-data/gardenlinux + git submodule update --init --recursive + +test: install-test + $(POETRY) run pytest -k "not kms" + +format: install-dev + $(POETRY) run black --extend-exclude test-data/gardenlinux . + +lint: install-dev + $(POETRY) run black --check --extend-exclude test-data/gardenlinux . + +security: install-dev + @if [ "$(CI)" = "true" ]; then \ + $(POETRY) run bandit -ll -ii -r . -f json -o bandit-report.json ; \ + else \ + $(POETRY) run bandit -r . ; \ + fi + +docs: install-docs + $(POETRY) run sphinx-build docs _build + +clean: + rm -rf build/ + rm -rf dist/ + rm -rf *.egg-info/ + rm -rf .eggs/ + rm -rf .pytest_cache/ + rm -rf .coverage + rm -rf htmlcov/ + find . -type d -name __pycache__ -exec rm -rf {} + + find . -type f -name "*.pyc" -delete + find . -type f -name "*.pyo" -delete + find . -type f -name "*.pyd" -delete + rm -rf test-data/zot + cd test-data/gardenlinux && git reset --hard diff --git a/cert/gencert.sh b/cert/gencert.sh deleted file mode 100755 index 051203e9..00000000 --- a/cert/gencert.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) -CERT_NAME="$SCRIPT_DIR/oci-sign" - - -openssl req -x509 -newkey rsa -pkeyopt rsa_keygen_bits:4096 -days 3650 -nodes -keyout $CERT_NAME.key -out $CERT_NAME.crt -subj "/CN=Garden Linux test signing key for oci" - diff --git a/hack/print_feature_extensions.sh b/hack/print_feature_extensions.sh index d9661455..967d57bf 100755 --- a/hack/print_feature_extensions.sh +++ b/hack/print_feature_extensions.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash search_and_print_directories() { diff --git a/poetry.lock b/poetry.lock index 9d89670a..72732c21 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. [[package]] name = "alabaster" @@ -6,7 +6,7 @@ version = "0.7.16" description = "A light, configurable Sphinx theme" optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["docs"] files = [ {file = "alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"}, {file = "alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65"}, @@ -49,7 +49,7 @@ version = "2.17.0" description = "Internationalization utilities" optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["docs"] files = [ {file = "babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2"}, {file = "babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d"}, @@ -58,6 +58,31 @@ files = [ [package.extras] dev = ["backports.zoneinfo ; python_version < \"3.9\"", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata ; sys_platform == \"win32\""] +[[package]] +name = "bandit" +version = "1.8.3" +description = "Security oriented static analyser for python code." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "bandit-1.8.3-py3-none-any.whl", hash = "sha256:28f04dc0d258e1dd0f99dee8eefa13d1cb5e3fde1a5ab0c523971f97b289bcd8"}, + {file = "bandit-1.8.3.tar.gz", hash = "sha256:f5847beb654d309422985c36644649924e0ea4425c76dec2e89110b87506193a"}, +] + +[package.dependencies] +colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} +PyYAML = ">=5.3.1" +rich = "*" +stevedore = ">=1.20.0" + +[package.extras] +baseline = ["GitPython (>=3.1.30)"] +sarif = ["jschema-to-python (>=1.2.3)", "sarif-om (>=1.0.4)"] +test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)"] +toml = ["tomli (>=1.1.0) ; python_version < \"3.11\""] +yaml = ["PyYAML"] + [[package]] name = "black" version = "24.10.0" @@ -151,7 +176,7 @@ version = "2025.1.31" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" -groups = ["main"] +groups = ["main", "docs"] files = [ {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, @@ -244,7 +269,7 @@ version = "3.4.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" -groups = ["main"] +groups = ["main", "docs"] files = [ {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, @@ -361,12 +386,12 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main", "dev"] +groups = ["main", "dev", "docs"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -markers = {main = "sys_platform == \"win32\"", dev = "platform_system == \"Windows\""} +markers = {main = "sys_platform == \"win32\"", dev = "platform_system == \"Windows\"", docs = "sys_platform == \"win32\""} [[package]] name = "cryptography" @@ -428,7 +453,7 @@ version = "0.20.1" description = "Docutils -- Python Documentation Utilities" optional = false python-versions = ">=3.7" -groups = ["main"] +groups = ["docs"] files = [ {file = "docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6"}, {file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"}, @@ -490,7 +515,7 @@ version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" -groups = ["main"] +groups = ["main", "docs"] files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -505,7 +530,7 @@ version = "1.4.1" description = "Getting image size from png/jpeg/jpeg2000/gif file" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -groups = ["main"] +groups = ["docs"] files = [ {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, @@ -529,7 +554,7 @@ version = "3.1.5" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" -groups = ["main"] +groups = ["docs"] files = [ {file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"}, {file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"}, @@ -590,13 +615,38 @@ files = [ [package.dependencies] referencing = ">=0.31.0" +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + [[package]] name = "markupsafe" version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["docs"] files = [ {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, @@ -661,6 +711,18 @@ files = [ {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, ] +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + [[package]] name = "mypy-extensions" version = "1.0.0" @@ -724,7 +786,7 @@ version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] +groups = ["main", "dev", "docs"] files = [ {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, @@ -742,6 +804,21 @@ files = [ {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] +[[package]] +name = "pbr" +version = "6.1.1" +description = "Python Build Reasonableness" +optional = false +python-versions = ">=2.6" +groups = ["dev"] +files = [ + {file = "pbr-6.1.1-py2.py3-none-any.whl", hash = "sha256:38d4daea5d9fa63b3f626131b9d34947fd0c8be9b05a29276870580050a25a76"}, + {file = "pbr-6.1.1.tar.gz", hash = "sha256:93ea72ce6989eb2eed99d0f75721474f69ad88128afdef5ac377eb797c4bf76b"}, +] + +[package.dependencies] +setuptools = "*" + [[package]] name = "platformdirs" version = "4.3.6" @@ -794,7 +871,7 @@ version = "2.19.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["dev", "docs"] files = [ {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, @@ -862,7 +939,7 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -942,7 +1019,7 @@ version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["main", "docs"] files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, @@ -958,6 +1035,26 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "rich" +version = "14.0.0" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.8.0" +groups = ["dev"] +files = [ + {file = "rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0"}, + {file = "rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" +typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.11\""} + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + [[package]] name = "rpds-py" version = "0.22.3" @@ -1089,6 +1186,27 @@ botocore = ">=1.36.0,<2.0a.0" [package.extras] crt = ["botocore[crt] (>=1.36.0,<2.0a.0)"] +[[package]] +name = "setuptools" +version = "80.7.1" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "setuptools-80.7.1-py3-none-any.whl", hash = "sha256:ca5cc1069b85dc23070a6628e6bcecb3292acac802399c7f8edc0100619f9009"}, + {file = "setuptools-80.7.1.tar.gz", hash = "sha256:f6ffc5f0142b1bd8d0ca94ee91b30c0ca862ffd50826da1ea85258a06fd94552"}, +] + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""] +core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.14.*)", "pytest-mypy"] + [[package]] name = "six" version = "1.17.0" @@ -1119,7 +1237,7 @@ version = "2.2.0" description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." optional = false python-versions = "*" -groups = ["main"] +groups = ["docs"] files = [ {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, @@ -1131,7 +1249,7 @@ version = "7.4.7" description = "Python documentation generator" optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["docs"] files = [ {file = "sphinx-7.4.7-py3-none-any.whl", hash = "sha256:c2419e2135d11f1951cd994d6eb18a1835bd8fdd8429f9ca375dc1f3281bd239"}, {file = "sphinx-7.4.7.tar.gz", hash = "sha256:242f92a7ea7e6c5b406fdc2615413890ba9f699114a9c09192d7dfead2ee9cfe"}, @@ -1167,7 +1285,7 @@ version = "2.0.0" description = "Read the Docs theme for Sphinx" optional = false python-versions = ">=3.6" -groups = ["main"] +groups = ["docs"] files = [ {file = "sphinx_rtd_theme-2.0.0-py2.py3-none-any.whl", hash = "sha256:ec93d0856dc280cf3aee9a4c9807c60e027c7f7b461b77aeffed682e68f0e586"}, {file = "sphinx_rtd_theme-2.0.0.tar.gz", hash = "sha256:bd5d7b80622406762073a04ef8fadc5f9151261563d47027de09910ce03afe6b"}, @@ -1187,7 +1305,7 @@ version = "2.0.0" description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["docs"] files = [ {file = "sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5"}, {file = "sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1"}, @@ -1204,7 +1322,7 @@ version = "2.0.0" description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["docs"] files = [ {file = "sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2"}, {file = "sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad"}, @@ -1221,7 +1339,7 @@ version = "2.1.0" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["docs"] files = [ {file = "sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8"}, {file = "sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9"}, @@ -1238,7 +1356,7 @@ version = "4.1" description = "Extension to include jQuery on newer Sphinx releases" optional = false python-versions = ">=2.7" -groups = ["main"] +groups = ["docs"] files = [ {file = "sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a"}, {file = "sphinxcontrib_jquery-4.1-py2.py3-none-any.whl", hash = "sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae"}, @@ -1253,7 +1371,7 @@ version = "1.0.1" description = "A sphinx extension which renders display math in HTML via JavaScript" optional = false python-versions = ">=3.5" -groups = ["main"] +groups = ["docs"] files = [ {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, @@ -1268,7 +1386,7 @@ version = "2.0.0" description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["docs"] files = [ {file = "sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb"}, {file = "sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab"}, @@ -1285,7 +1403,7 @@ version = "2.0.0" description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["docs"] files = [ {file = "sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331"}, {file = "sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d"}, @@ -1296,13 +1414,28 @@ lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] standalone = ["Sphinx (>=5)"] test = ["pytest"] +[[package]] +name = "stevedore" +version = "5.4.1" +description = "Manage dynamic plugins for Python applications" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "stevedore-5.4.1-py3-none-any.whl", hash = "sha256:d10a31c7b86cba16c1f6e8d15416955fc797052351a56af15e608ad20811fcfe"}, + {file = "stevedore-5.4.1.tar.gz", hash = "sha256:3135b5ae50fe12816ef291baff420acb727fcd356106e3e9cbfa9e5985cd6f4b"}, +] + +[package.dependencies] +pbr = ">=2.0.0" + [[package]] name = "tomli" version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] +groups = ["main", "dev", "docs"] markers = "python_version == \"3.10\"" files = [ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, @@ -1358,7 +1491,7 @@ version = "2.3.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["main", "docs"] files = [ {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, @@ -1373,4 +1506,4 @@ zstd = ["zstandard (>=0.18.0)"] [metadata] lock-version = "2.1" python-versions = "^3.10" -content-hash = "301412fce5875d4e31bdea597a1f3f856c06a1bb5c5f9a1d993b4d465889b6f7" +content-hash = "3912bc8cb9cf35d5cdabd52236d746dff0a0a99db04fde2e568a9fd8568faa0e" diff --git a/pyproject.toml b/pyproject.toml index e9ddf684..e25d8731 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,6 @@ networkx = "^3.3" PyYAML = "^6.0.2" pytest = "^8.3.2" gitpython = "^3.1.44" -sphinx-rtd-theme = "^2.0.0" apt-repo = "^0.5" jsonschema = "^4.23.0" oras = { git = "https://github.com/oras-project/oras-py.git", rev="caf8db5b279382335fbb1f6d7402ed9b73618d37" } @@ -22,8 +21,12 @@ cryptography = "^44.0.0" boto3 = "*" [tool.poetry.group.dev.dependencies] +bandit = "^1.8.3" black = "^24.8.0" +[tool.poetry.group.docs.dependencies] +sphinx-rtd-theme = "^2.0.0" + [tool.poetry.scripts] gl-cname = "gardenlinux.features.cname_main:main" gl-features-parse = "gardenlinux.features.__main__:main" diff --git a/test-data/build-test-data.sh b/test-data/build-test-data.sh index 61377235..196d14d8 100755 --- a/test-data/build-test-data.sh +++ b/test-data/build-test-data.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" echo "This will take a while. Building for the following targets:" diff --git a/tests/conftest.py b/tests/conftest.py index 5586b567..1424d4c9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,14 +1,74 @@ -from .helper import call_command +import json import os -import tempfile -from .helper import spawn_background_process -import sys import shutil -import json +import subprocess +import sys +import tempfile +from datetime import datetime, timedelta + import pytest +from cryptography import x509 +from cryptography.x509.oid import NameOID +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import rsa from dotenv import load_dotenv -GL_ROOT_DIR = "test-data/gardenlinux" +from .helper import call_command, spawn_background_process + +TEST_DATA_DIR = "test-data" +GL_ROOT_DIR = f"{TEST_DATA_DIR}/gardenlinux" +CERT_DIR = f"{TEST_DATA_DIR}/cert" + + +def generate_test_certificates(): + """Generate self-signed certificates for testing using cryptography library""" + + os.makedirs(CERT_DIR, exist_ok=True) + key_path = os.path.join(CERT_DIR, "oci-sign.key") + cert_path = os.path.join(CERT_DIR, "oci-sign.crt") + + # Generate private key + private_key = rsa.generate_private_key( + public_exponent=65537, + key_size=4096, + ) + + # Generate certificate + subject = issuer = x509.Name( + [ + x509.NameAttribute( + NameOID.COMMON_NAME, "Garden Linux test signing key for oci" + ), + ] + ) + + cert = ( + x509.CertificateBuilder() + .subject_name(subject) + .issuer_name(issuer) + .public_key(private_key.public_key()) + .serial_number(x509.random_serial_number()) + .not_valid_before(datetime.utcnow()) + .not_valid_after(datetime.utcnow() + timedelta(days=365)) + .sign(private_key, hashes.SHA256()) + ) + + # Write private key + with open(key_path, "wb") as f: + f.write( + private_key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=serialization.NoEncryption(), + ) + ) + os.chmod(key_path, 0o600) + + # Write certificate + with open(cert_path, "wb") as f: + f.write(cert.public_bytes(serialization.Encoding.PEM)) + + print(f"Generated test certificates in {CERT_DIR}") def write_zot_config(config_dict, file_path): @@ -33,7 +93,7 @@ def zot_session(): print(f"Spawning zot registry with config {zot_config_file_path}") zot_process = spawn_background_process( - f"zot serve {zot_config_file_path}", + f"{TEST_DATA_DIR}/zot serve {zot_config_file_path}", stdout=sys.stdout, stderr=sys.stderr, ) @@ -50,12 +110,12 @@ def zot_session(): def pytest_sessionstart(session): - call_command("./cert/gencert.sh") + generate_test_certificates() call_command("./test-data/build-test-data.sh --dummy") def pytest_sessionfinish(session): - if os.path.isfile("./cert/oci-sign.crt"): - os.remove("./cert/oci-sign.crt") - if os.path.isfile("./cert/oci-sign.key"): - os.remove("./cert/oci-sign.key") + if os.path.isfile(CERT_DIR + "/oci-sign.crt"): + os.remove(CERT_DIR + "/oci-sign.crt") + if os.path.isfile(CERT_DIR + "/oci-sign.key"): + os.remove(CERT_DIR + "/oci-sign.key")