mirror of
https://github.com/release-engineering/repo-autoindex.git
synced 2025-02-23 13:42:52 +00:00
commit
66097c6725
16 changed files with 429 additions and 48 deletions
28
.github/workflows/ci.yml
vendored
Normal file
28
.github/workflows/ci.yml
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
run-ci:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.9"
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install -U pip
|
||||
pip install tox
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
tox
|
29
.github/workflows/poetry-publish.yml
vendored
Normal file
29
.github/workflows/poetry-publish.yml
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
name: Publish on PyPI
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.9"
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install -U pip
|
||||
pip install poetry
|
||||
|
||||
- name: Publish
|
||||
run: |
|
||||
poetry publish --build -vv --no-interaction
|
||||
env:
|
||||
POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_TOKEN }}
|
24
.github/workflows/poetry-update-merge.yml
vendored
Normal file
24
.github/workflows/poetry-update-merge.yml
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
on:
|
||||
workflow_dispatch: {}
|
||||
schedule:
|
||||
# Tuesday
|
||||
- cron: "20 20 * * 2"
|
||||
|
||||
name: "poetry: merge PR"
|
||||
jobs:
|
||||
poetry_update_merge:
|
||||
name: poetry update automerge
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Look up pull request
|
||||
uses: juliangruber/find-pull-request-action@v1
|
||||
id: find-pull-request
|
||||
with:
|
||||
branch: deps/poetry-update
|
||||
- name: Merge Pull Request
|
||||
uses: juliangruber/merge-pull-request-action@v1
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
number: ${{ steps.find-pull-request.outputs.number }}
|
||||
method: merge
|
||||
if: ${{ steps.find-pull-request.outputs.number }}
|
38
.github/workflows/poetry-update.yml
vendored
Normal file
38
.github/workflows/poetry-update.yml
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
on:
|
||||
workflow_dispatch: {}
|
||||
schedule:
|
||||
# Sunday
|
||||
- cron: "20 20 * * 0"
|
||||
|
||||
name: "poetry: create PR"
|
||||
jobs:
|
||||
poetry_update:
|
||||
name: poetry update
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.9
|
||||
- name: poetry update
|
||||
uses: technote-space/create-pr-action@v2
|
||||
with:
|
||||
EXECUTE_COMMANDS: |
|
||||
python -m pip install -U pip
|
||||
pip install poetry
|
||||
poetry update
|
||||
COMMIT_MESSAGE: "chore: scheduled poetry update"
|
||||
COMMIT_NAME: "GitHub Actions"
|
||||
COMMIT_EMAIL: "noreply@github.com"
|
||||
GITHUB_TOKEN: ${{ secrets.APPROVAL_TOKEN }}
|
||||
PR_BRANCH_PREFIX: deps/
|
||||
PR_BRANCH_NAME: poetry-update
|
||||
PR_TITLE: "chore: scheduled poetry update"
|
||||
PR_BODY: |-
|
||||
## Update dependencies
|
||||
|
||||
This is a scheduled update of Python dependencies within this
|
||||
repo managed by poetry.
|
||||
|
||||
This change will be approved automatically and merged within
|
||||
a few days if all checks have succeeded.
|
17
.pre-commit-config.yaml
Normal file
17
.pre-commit-config.yaml
Normal file
|
@ -0,0 +1,17 @@
|
|||
repos:
|
||||
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 22.3.0
|
||||
hooks:
|
||||
- id: black
|
||||
|
||||
- repo: https://github.com/pycqa/isort/
|
||||
rev: 5.10.1
|
||||
hooks:
|
||||
- id: isort
|
||||
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.3.0
|
||||
hooks:
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
210
poetry.lock
generated
210
poetry.lock
generated
|
@ -37,6 +37,14 @@ category = "main"
|
|||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "atomicwrites"
|
||||
version = "1.4.0"
|
||||
description = "Atomic file writes."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[[package]]
|
||||
name = "attrs"
|
||||
version = "21.4.0"
|
||||
|
@ -53,15 +61,23 @@ tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>
|
|||
|
||||
[[package]]
|
||||
name = "charset-normalizer"
|
||||
version = "2.0.12"
|
||||
version = "2.1.0"
|
||||
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.5.0"
|
||||
python-versions = ">=3.6.0"
|
||||
|
||||
[package.extras]
|
||||
unicode_backport = ["unicodedata2"]
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.5"
|
||||
description = "Cross-platform colored terminal text."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "defusedxml"
|
||||
version = "0.7.1"
|
||||
|
@ -86,6 +102,14 @@ category = "main"
|
|||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[[package]]
|
||||
name = "iniconfig"
|
||||
version = "1.1.1"
|
||||
description = "iniconfig: brain-dead simple config-ini parsing"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "jinja2"
|
||||
version = "3.1.2"
|
||||
|
@ -116,6 +140,111 @@ category = "main"
|
|||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[[package]]
|
||||
name = "mypy"
|
||||
version = "0.961"
|
||||
description = "Optional static typing for Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
mypy-extensions = ">=0.4.3"
|
||||
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
|
||||
typing-extensions = ">=3.10"
|
||||
|
||||
[package.extras]
|
||||
dmypy = ["psutil (>=4.0)"]
|
||||
python2 = ["typed-ast (>=1.4.0,<2)"]
|
||||
reports = ["lxml"]
|
||||
|
||||
[[package]]
|
||||
name = "mypy-extensions"
|
||||
version = "0.4.3"
|
||||
description = "Experimental type system extensions for programs checked with the mypy typechecker."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "21.3"
|
||||
description = "Core utilities for Python packages"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "1.0.0"
|
||||
description = "plugin and hook calling mechanisms for python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.extras]
|
||||
dev = ["pre-commit", "tox"]
|
||||
testing = ["pytest", "pytest-benchmark"]
|
||||
|
||||
[[package]]
|
||||
name = "py"
|
||||
version = "1.11.0"
|
||||
description = "library with cross-python path, ini-parsing, io, code, log facilities"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "pyparsing"
|
||||
version = "3.0.9"
|
||||
description = "pyparsing module - Classes and methods to define and execute parsing grammars"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6.8"
|
||||
|
||||
[package.extras]
|
||||
diagrams = ["railroad-diagrams", "jinja2"]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "7.1.2"
|
||||
description = "pytest: simple powerful testing with Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
|
||||
attrs = ">=19.2.0"
|
||||
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
||||
iniconfig = "*"
|
||||
packaging = "*"
|
||||
pluggy = ">=0.12,<2.0"
|
||||
py = ">=1.8.2"
|
||||
tomli = ">=1.0.0"
|
||||
|
||||
[package.extras]
|
||||
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"]
|
||||
|
||||
[[package]]
|
||||
name = "tomli"
|
||||
version = "2.0.1"
|
||||
description = "A lil' TOML parser"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.2.0"
|
||||
description = "Backported and Experimental Type Hints for Python 3.7+"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[[package]]
|
||||
name = "yarl"
|
||||
version = "1.7.2"
|
||||
|
@ -130,8 +259,8 @@ multidict = ">=4.0"
|
|||
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.10"
|
||||
content-hash = "a608104bb5d9bb1fa717110e3bc51579a748fdfbe58bb7ae36d61a030b8e222c"
|
||||
python-versions = ">=3.9,<4"
|
||||
content-hash = "ec537cb22f7488971d12450524aec30918e6d42c6eac78fcd4022fe8293d935a"
|
||||
|
||||
[metadata.files]
|
||||
aiohttp = [
|
||||
|
@ -216,13 +345,21 @@ async-timeout = [
|
|||
{file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"},
|
||||
{file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"},
|
||||
]
|
||||
atomicwrites = [
|
||||
{file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
|
||||
{file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
|
||||
]
|
||||
attrs = [
|
||||
{file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"},
|
||||
{file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"},
|
||||
]
|
||||
charset-normalizer = [
|
||||
{file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"},
|
||||
{file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"},
|
||||
{file = "charset-normalizer-2.1.0.tar.gz", hash = "sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413"},
|
||||
{file = "charset_normalizer-2.1.0-py3-none-any.whl", hash = "sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5"},
|
||||
]
|
||||
colorama = [
|
||||
{file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"},
|
||||
{file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
|
||||
]
|
||||
defusedxml = [
|
||||
{file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"},
|
||||
|
@ -293,6 +430,10 @@ idna = [
|
|||
{file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},
|
||||
{file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
|
||||
]
|
||||
iniconfig = [
|
||||
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
|
||||
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
|
||||
]
|
||||
jinja2 = [
|
||||
{file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"},
|
||||
{file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"},
|
||||
|
@ -400,6 +541,63 @@ multidict = [
|
|||
{file = "multidict-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:4bae31803d708f6f15fd98be6a6ac0b6958fcf68fda3c77a048a4f9073704aae"},
|
||||
{file = "multidict-6.0.2.tar.gz", hash = "sha256:5ff3bd75f38e4c43f1f470f2df7a4d430b821c4ce22be384e1459cb57d6bb013"},
|
||||
]
|
||||
mypy = [
|
||||
{file = "mypy-0.961-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:697540876638ce349b01b6786bc6094ccdaba88af446a9abb967293ce6eaa2b0"},
|
||||
{file = "mypy-0.961-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b117650592e1782819829605a193360a08aa99f1fc23d1d71e1a75a142dc7e15"},
|
||||
{file = "mypy-0.961-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bdd5ca340beffb8c44cb9dc26697628d1b88c6bddf5c2f6eb308c46f269bb6f3"},
|
||||
{file = "mypy-0.961-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3e09f1f983a71d0672bbc97ae33ee3709d10c779beb613febc36805a6e28bb4e"},
|
||||
{file = "mypy-0.961-cp310-cp310-win_amd64.whl", hash = "sha256:e999229b9f3198c0c880d5e269f9f8129c8862451ce53a011326cad38b9ccd24"},
|
||||
{file = "mypy-0.961-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b24be97351084b11582fef18d79004b3e4db572219deee0212078f7cf6352723"},
|
||||
{file = "mypy-0.961-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f4a21d01fc0ba4e31d82f0fff195682e29f9401a8bdb7173891070eb260aeb3b"},
|
||||
{file = "mypy-0.961-cp36-cp36m-win_amd64.whl", hash = "sha256:439c726a3b3da7ca84a0199a8ab444cd8896d95012c4a6c4a0d808e3147abf5d"},
|
||||
{file = "mypy-0.961-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5a0b53747f713f490affdceef835d8f0cb7285187a6a44c33821b6d1f46ed813"},
|
||||
{file = "mypy-0.961-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0e9f70df36405c25cc530a86eeda1e0867863d9471fe76d1273c783df3d35c2e"},
|
||||
{file = "mypy-0.961-cp37-cp37m-win_amd64.whl", hash = "sha256:b88f784e9e35dcaa075519096dc947a388319cb86811b6af621e3523980f1c8a"},
|
||||
{file = "mypy-0.961-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d5aaf1edaa7692490f72bdb9fbd941fbf2e201713523bdb3f4038be0af8846c6"},
|
||||
{file = "mypy-0.961-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9f5f5a74085d9a81a1f9c78081d60a0040c3efb3f28e5c9912b900adf59a16e6"},
|
||||
{file = "mypy-0.961-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f4b794db44168a4fc886e3450201365c9526a522c46ba089b55e1f11c163750d"},
|
||||
{file = "mypy-0.961-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:64759a273d590040a592e0f4186539858c948302c653c2eac840c7a3cd29e51b"},
|
||||
{file = "mypy-0.961-cp38-cp38-win_amd64.whl", hash = "sha256:63e85a03770ebf403291ec50097954cc5caf2a9205c888ce3a61bd3f82e17569"},
|
||||
{file = "mypy-0.961-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5f1332964963d4832a94bebc10f13d3279be3ce8f6c64da563d6ee6e2eeda932"},
|
||||
{file = "mypy-0.961-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:006be38474216b833eca29ff6b73e143386f352e10e9c2fbe76aa8549e5554f5"},
|
||||
{file = "mypy-0.961-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9940e6916ed9371809b35b2154baf1f684acba935cd09928952310fbddaba648"},
|
||||
{file = "mypy-0.961-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a5ea0875a049de1b63b972456542f04643daf320d27dc592d7c3d9cd5d9bf950"},
|
||||
{file = "mypy-0.961-cp39-cp39-win_amd64.whl", hash = "sha256:1ece702f29270ec6af25db8cf6185c04c02311c6bb21a69f423d40e527b75c56"},
|
||||
{file = "mypy-0.961-py3-none-any.whl", hash = "sha256:03c6cc893e7563e7b2949b969e63f02c000b32502a1b4d1314cabe391aa87d66"},
|
||||
{file = "mypy-0.961.tar.gz", hash = "sha256:f730d56cb924d371c26b8eaddeea3cc07d78ff51c521c6d04899ac6904b75492"},
|
||||
]
|
||||
mypy-extensions = [
|
||||
{file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
|
||||
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
|
||||
]
|
||||
packaging = [
|
||||
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
|
||||
{file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
|
||||
]
|
||||
pluggy = [
|
||||
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
|
||||
{file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
|
||||
]
|
||||
py = [
|
||||
{file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
|
||||
{file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
|
||||
]
|
||||
pyparsing = [
|
||||
{file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"},
|
||||
{file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"},
|
||||
]
|
||||
pytest = [
|
||||
{file = "pytest-7.1.2-py3-none-any.whl", hash = "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c"},
|
||||
{file = "pytest-7.1.2.tar.gz", hash = "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"},
|
||||
]
|
||||
tomli = [
|
||||
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
|
||||
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
||||
]
|
||||
typing-extensions = [
|
||||
{file = "typing_extensions-4.2.0-py3-none-any.whl", hash = "sha256:6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708"},
|
||||
{file = "typing_extensions-4.2.0.tar.gz", hash = "sha256:f1c24655a0da0d1b67f07e17a5e6b2a105894e6824b92096378bb3668ef02376"},
|
||||
]
|
||||
yarl = [
|
||||
{file = "yarl-1.7.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f2a8508f7350512434e41065684076f640ecce176d262a7d54f0da41d99c5a95"},
|
||||
{file = "yarl-1.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da6df107b9ccfe52d3a48165e48d72db0eca3e3029b5b8cb4fe6ee3cb870ba8b"},
|
||||
|
|
|
@ -6,16 +6,21 @@ authors = ["Rohan McGovern <rmcgover@redhat.com>"]
|
|||
license = "GPLv3"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.10"
|
||||
aiohttp = "^3.8.1"
|
||||
defusedxml = "^0.7.1"
|
||||
Jinja2 = "^3.1.2"
|
||||
python = ">=3.9,<4"
|
||||
aiohttp = ">=3.8.1"
|
||||
defusedxml = ">=0.7.1"
|
||||
Jinja2 = ">=3.1.2"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
pytest = ">=7.1.2"
|
||||
mypy = ">=0.961"
|
||||
|
||||
[tool.poetry.scripts]
|
||||
"repo-autoindex" = "repo_autoindex.cmd:entrypoint"
|
||||
|
||||
[tool.isort]
|
||||
profile = "black"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
from .api import autoindex
|
||||
from .base import Fetcher, GeneratedIndex
|
||||
|
||||
from .api import autoindex
|
||||
__all__ = ["autoindex", "Fetcher", "GeneratedIndex"]
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import logging
|
||||
import gzip
|
||||
import logging
|
||||
from collections.abc import AsyncGenerator
|
||||
from typing import Optional
|
||||
|
||||
import aiohttp
|
||||
|
||||
|
@ -35,7 +36,7 @@ def http_fetcher(session: aiohttp.ClientSession) -> Fetcher:
|
|||
async def autoindex(
|
||||
url: str,
|
||||
*,
|
||||
fetcher: Fetcher = None,
|
||||
fetcher: Optional[Fetcher] = None,
|
||||
index_href_suffix: str = "",
|
||||
) -> AsyncGenerator[GeneratedIndex, None]:
|
||||
if fetcher is None:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from dataclasses import dataclass
|
||||
from typing import Optional, TypeVar, Type
|
||||
from collections.abc import Awaitable, Callable, AsyncGenerator
|
||||
from abc import ABC, abstractmethod
|
||||
from collections.abc import AsyncGenerator, Awaitable, Callable
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional, Type, TypeVar
|
||||
|
||||
T = TypeVar("T")
|
||||
Fetcher = Callable[[str], Awaitable[Optional[str]]]
|
||||
|
@ -21,8 +21,8 @@ class GeneratedIndex:
|
|||
class IndexEntry:
|
||||
href: str
|
||||
text: str
|
||||
time: str = "-"
|
||||
size: str = "-"
|
||||
time: str = ""
|
||||
size: str = ""
|
||||
padding: str = ""
|
||||
icon: str = ICON_OTHER
|
||||
|
||||
|
@ -46,7 +46,7 @@ class Repo(ABC):
|
|||
self.fetcher = fetcher
|
||||
|
||||
@abstractmethod
|
||||
async def render_index(
|
||||
def render_index(
|
||||
self, index_href_suffix: str
|
||||
) -> AsyncGenerator[GeneratedIndex, None]:
|
||||
pass
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
from collections.abc import AsyncGenerator
|
||||
import logging
|
||||
import argparse
|
||||
import asyncio
|
||||
import gzip
|
||||
import logging
|
||||
import os
|
||||
import argparse
|
||||
|
||||
from repo_autoindex import autoindex
|
||||
|
||||
LOG = logging.getLogger("autoindex")
|
||||
|
||||
|
||||
async def dump_autoindices(args):
|
||||
async def dump_autoindices(args: argparse.Namespace) -> None:
|
||||
index_filename = args.index_filename
|
||||
async for index in autoindex(args.url, index_href_suffix=index_filename):
|
||||
os.makedirs(index.relative_dir or ".", exist_ok=True)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import os
|
||||
from collections.abc import Iterable
|
||||
from dataclasses import replace
|
||||
import os
|
||||
|
||||
import jinja2
|
||||
|
||||
|
@ -10,7 +10,7 @@ TEMPLATE_DIR = os.path.join(os.path.dirname(__file__), "templates")
|
|||
|
||||
|
||||
class TemplateContext:
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
self.env = jinja2.Environment(
|
||||
autoescape=True, loader=jinja2.FileSystemLoader(TEMPLATE_DIR)
|
||||
)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from dataclasses import dataclass, field, replace
|
||||
from collections.abc import Iterable
|
||||
|
||||
from .base import IndexEntry, ICON_FOLDER
|
||||
from .base import ICON_FOLDER, IndexEntry
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -11,7 +12,9 @@ class TreeNode:
|
|||
|
||||
|
||||
def treeify(
|
||||
all_entries: list[IndexEntry], relative_dir: str = "", index_href_suffix: str = ""
|
||||
all_entries: Iterable[IndexEntry],
|
||||
relative_dir: str = "",
|
||||
index_href_suffix: str = "",
|
||||
) -> TreeNode:
|
||||
out = TreeNode(relative_dir=relative_dir)
|
||||
|
||||
|
@ -24,7 +27,7 @@ def treeify(
|
|||
)
|
||||
)
|
||||
|
||||
entries_by_leading_dir = {}
|
||||
entries_by_leading_dir: dict[str, list[IndexEntry]] = {}
|
||||
for entry in all_entries:
|
||||
subdir = entry.href.removeprefix(relative_dir + "/")
|
||||
components = subdir.split("/", 1)
|
||||
|
|
|
@ -1,21 +1,34 @@
|
|||
import datetime
|
||||
import logging
|
||||
import os
|
||||
import datetime
|
||||
from collections.abc import AsyncGenerator, Generator, Iterable
|
||||
from dataclasses import dataclass
|
||||
from typing import Type, Optional
|
||||
from collections.abc import Generator, AsyncGenerator, Iterable
|
||||
|
||||
from defusedxml import pulldom
|
||||
from xml.dom.pulldom import START_ELEMENT, END_ELEMENT, DOMEventStream
|
||||
from typing import Optional, Type
|
||||
from xml.dom.minidom import Element
|
||||
from xml.dom.pulldom import END_ELEMENT, START_ELEMENT, DOMEventStream
|
||||
|
||||
from .base import Repo, Fetcher, GeneratedIndex, IndexEntry, ICON_PACKAGE, ICON_FOLDER
|
||||
from defusedxml import pulldom # type: ignore
|
||||
|
||||
from .base import ICON_FOLDER, ICON_PACKAGE, Fetcher, GeneratedIndex, IndexEntry, Repo
|
||||
from .template import TemplateContext
|
||||
from .tree import treeify
|
||||
|
||||
LOG = logging.getLogger("autoindex")
|
||||
|
||||
|
||||
def get_tag(elem: Element, name: str) -> Element:
|
||||
elems: list[Element] = elem.getElementsByTagName(name) # type: ignore
|
||||
return elems[0]
|
||||
|
||||
|
||||
def get_text_tag(elem: Element, name: str) -> str:
|
||||
tagnode = get_tag(elem, name)
|
||||
child = tagnode.firstChild
|
||||
# TODO: raise proper error if missing
|
||||
assert child
|
||||
return str(child.toxml())
|
||||
|
||||
|
||||
@dataclass
|
||||
class Package:
|
||||
href: str
|
||||
|
@ -25,10 +38,10 @@ class Package:
|
|||
@classmethod
|
||||
def from_element(cls, elem: Element) -> "Package":
|
||||
return cls(
|
||||
href=elem.getElementsByTagName("location")[0].attributes["href"].value,
|
||||
href=get_tag(elem, "location").attributes["href"].value,
|
||||
# TODO: tolerate some of these being absent or wrong.
|
||||
time=elem.getElementsByTagName("time")[0].attributes["file"].value,
|
||||
size=elem.getElementsByTagName("size")[0].attributes["package"].value,
|
||||
time=get_tag(elem, "time").attributes["file"].value,
|
||||
size=get_tag(elem, "size").attributes["package"].value,
|
||||
)
|
||||
|
||||
@property
|
||||
|
@ -44,7 +57,7 @@ class Package:
|
|||
|
||||
def pulldom_elements(
|
||||
xml_str: str, path_matcher, attr_matcher=lambda _: True
|
||||
) -> Generator[Element]:
|
||||
) -> Generator[Element, None, None]:
|
||||
stream = pulldom.parseString(xml_str)
|
||||
current_path = []
|
||||
for event, node in stream:
|
||||
|
@ -90,8 +103,11 @@ class YumRepo(Repo):
|
|||
)
|
||||
)
|
||||
if len(revision_nodes) == 1:
|
||||
timestamp_node = revision_nodes[0].firstChild
|
||||
# TODO: raise proper error
|
||||
assert timestamp_node
|
||||
time = datetime.datetime.utcfromtimestamp(
|
||||
float(revision_nodes[0].firstChild.toxml())
|
||||
int(timestamp_node.toxml())
|
||||
).isoformat()
|
||||
|
||||
out.append(
|
||||
|
@ -99,7 +115,7 @@ class YumRepo(Repo):
|
|||
href="repodata/repomd.xml",
|
||||
text="repomd.xml",
|
||||
time=time,
|
||||
size=size,
|
||||
size=str(size),
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -110,14 +126,14 @@ class YumRepo(Repo):
|
|||
attr_matcher=lambda attrs: attrs.get("type"),
|
||||
)
|
||||
)
|
||||
data_nodes.sort(key=lambda node: node.attributes["type"].value)
|
||||
data_nodes.sort(key=lambda node: str(node.attributes["type"].value))
|
||||
|
||||
for node in data_nodes:
|
||||
href = node.getElementsByTagName("location")[0].attributes["href"].value
|
||||
href = get_tag(node, "location").attributes["href"].value
|
||||
basename = os.path.basename(href)
|
||||
timestamp = node.getElementsByTagName("timestamp")[0].firstChild.toxml()
|
||||
timestamp = get_text_tag(node, "timestamp")
|
||||
time = datetime.datetime.utcfromtimestamp(float(timestamp)).isoformat()
|
||||
size = int(node.getElementsByTagName("size")[0].firstChild.toxml())
|
||||
size = int(get_text_tag(node, "size"))
|
||||
|
||||
out.append(
|
||||
IndexEntry(
|
||||
|
@ -143,12 +159,15 @@ class YumRepo(Repo):
|
|||
assert len(primary_nodes) == 1
|
||||
primary_node = primary_nodes[0]
|
||||
|
||||
location = primary_node.getElementsByTagName("location")[0]
|
||||
location = get_tag(primary_node, "location")
|
||||
href = location.attributes["href"].value
|
||||
|
||||
primary_url = "/".join([self.base_url, href])
|
||||
primary_xml = await self.fetcher(primary_url)
|
||||
|
||||
# TODO: raise proper error if missing
|
||||
assert primary_xml
|
||||
|
||||
return sorted(
|
||||
[p.index_entry for p in self.__packages_from_primary(primary_xml)],
|
||||
key=lambda e: e.text,
|
||||
|
@ -174,7 +193,7 @@ class YumRepo(Repo):
|
|||
self,
|
||||
entries: Iterable[IndexEntry],
|
||||
index_href_suffix: str,
|
||||
) -> Generator[GeneratedIndex, None]:
|
||||
) -> Generator[GeneratedIndex, None, None]:
|
||||
ctx = TemplateContext()
|
||||
nodes = [treeify(entries, index_href_suffix=index_href_suffix)]
|
||||
while nodes:
|
||||
|
@ -196,7 +215,7 @@ class YumRepo(Repo):
|
|||
|
||||
if repomd_xml is None:
|
||||
# not yum repo
|
||||
return
|
||||
return None
|
||||
|
||||
# it is a yum repo
|
||||
return cls(url, repomd_xml, fetcher)
|
||||
|
|
3
tests/test_import.py
Normal file
3
tests/test_import.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
def test_can_import():
|
||||
"""A trivial test, to be removed once real tests are implemented."""
|
||||
import repo_autoindex
|
16
tox.ini
Normal file
16
tox.ini
Normal file
|
@ -0,0 +1,16 @@
|
|||
[tox]
|
||||
isolated_build = True
|
||||
envlist = py39,mypy
|
||||
envdir = {toxworkdir}/poetry
|
||||
|
||||
[testenv]
|
||||
deps = poetry
|
||||
skip_install = True
|
||||
commands =
|
||||
poetry install -v
|
||||
py.test -v {posargs}
|
||||
|
||||
[testenv:mypy]
|
||||
commands =
|
||||
poetry install -v
|
||||
poetry run mypy --strict --disable-error-code no-untyped-def repo_autoindex
|
Loading…
Add table
Reference in a new issue