Init commit
This commit is contained in:
commit
9256e4232f
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
.dev/
|
||||
venv/
|
||||
out/
|
8
.vscode/extensions.json
vendored
Normal file
8
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"ms-python.python",
|
||||
"ms-python.vscode-pylance",
|
||||
"oderwat.indent-rainbow",
|
||||
"timonwong.shellcheck"
|
||||
]
|
||||
}
|
11
.vscode/settings.json
vendored
Normal file
11
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"python.languageSever": "Pylance",
|
||||
"python.linting.enabled": true,
|
||||
"python.linting.pylintEnabled": true,
|
||||
"python.linting.flake8Enabled": true,
|
||||
"python.linting.pycodestyleEnabled": true,
|
||||
"[python]": {
|
||||
"editor.rulers": [120],
|
||||
},
|
||||
"editor.tabSize": 4
|
||||
}
|
20
README.md
Normal file
20
README.md
Normal file
@ -0,0 +1,20 @@
|
||||
# Wheelie
|
||||
|
||||
Current flow:
|
||||
|
||||
1. Use `alpine.Dockerfile` to create a build-enviroment.
|
||||
2. Generate wheels with docker run.
|
||||
|
||||
```bash
|
||||
docker run -v "$PWD/out:/build" wheelie \
|
||||
--find-links=https://rox-wheels.s3.eu-north-1.amazonaws.com/alpine/3.13 \
|
||||
--trusted-host rox-wheels.s3-website.eu-north-1.amazonaws.com \
|
||||
cryptography==3.4.1
|
||||
```
|
||||
|
||||
3. Upload the wheels to S3 with `generator/main.py`. Takes S3 info as enviroment variables.
|
||||
|
||||
```bash
|
||||
cd generator
|
||||
python3 main.py -d alpine -r 3.13 -l -a
|
||||
```
|
36
alpine.Dockerfile
Normal file
36
alpine.Dockerfile
Normal file
@ -0,0 +1,36 @@
|
||||
ARG REL
|
||||
|
||||
FROM ghcr.io/linuxserver/baseimage-alpine:${REL}
|
||||
|
||||
RUN \
|
||||
echo "**** install build packages ****" && \
|
||||
apk add --no-cache --virtual=build-dependencies \
|
||||
autoconf \
|
||||
build-base \
|
||||
ca-certificates \
|
||||
cargo \
|
||||
cmake \
|
||||
curl \
|
||||
cython \
|
||||
g++ \
|
||||
gcc \
|
||||
git \
|
||||
glib-dev \
|
||||
gnupg \
|
||||
jq \
|
||||
libffi-dev \
|
||||
libstdc++ \
|
||||
libxml2-dev \
|
||||
libxslt \
|
||||
libxslt-dev \
|
||||
linux-headers \
|
||||
scons \
|
||||
make \
|
||||
openssl \
|
||||
openssl-dev \
|
||||
py3-pip \
|
||||
python3-dev && \
|
||||
pip3 install -U --no-cache-dir pip wheel setuptools && \
|
||||
mkdir -p /build
|
||||
|
||||
ENTRYPOINT [ "pip3", "wheel", "--wheel-dir=/build", "--find-links=/build", "--no-cache-dir" ]
|
15
build.sh
Normal file
15
build.sh
Normal file
@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
BUCKET=https://rox-wheels.s3.eu-north-1.amazonaws.com/alpine/3.13
|
||||
|
||||
REL=3.12
|
||||
|
||||
docker build -t wheelie -f alpine.Dockerfile --build-arg=REL=${REL} .
|
||||
|
||||
while IFS="" read -r p || [ -n "$p" ]
|
||||
do
|
||||
docker run -v "$PWD/out:/build" wheelie --find-links=$BUCKET --trusted-host rox-wheels.s3-website.eu-north-1.amazonaws.com "$p"
|
||||
done < requirements.txt
|
||||
|
||||
cd generator || return
|
||||
python3 main.py -d alpine -r ${REL} -l -a
|
7
generator/example.env
Normal file
7
generator/example.env
Normal file
@ -0,0 +1,7 @@
|
||||
S3_REGION=
|
||||
S3_BUCKET=
|
||||
|
||||
S3_ACCESS_KEY=
|
||||
S3_SECRET_KEY=
|
||||
|
||||
S3_IS_MINIO=
|
153
generator/main.py
Normal file
153
generator/main.py
Normal file
@ -0,0 +1,153 @@
|
||||
from argparse import ArgumentParser
|
||||
from io import BytesIO
|
||||
from json import dumps, loads
|
||||
from ntpath import basename
|
||||
from os import environ, listdir
|
||||
from os.path import isfile, join
|
||||
|
||||
import botocore.exceptions as botoexeptions
|
||||
from boto3 import client
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
|
||||
if not environ.get("CI"):
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv(dotenv_path="../.dev/.env")
|
||||
|
||||
mime = {"json": "application/json",
|
||||
"whl": "application/x-wheel+zip", # Not a "real" type
|
||||
"html": "text/html"
|
||||
}
|
||||
|
||||
|
||||
class Bucket():
|
||||
def __init__(self) -> None:
|
||||
self.client = client(
|
||||
"s3",
|
||||
region_name=environ.get("S3_REGION"),
|
||||
aws_access_key_id=environ.get("S3_ACCESS_KEY"),
|
||||
aws_secret_access_key=environ.get("S3_SECRET_KEY"),
|
||||
endpoint_url=environ.get("S3_IS_MINIO", None)
|
||||
)
|
||||
self.bucket_name = environ.get("S3_BUCKET")
|
||||
self._list()
|
||||
|
||||
def _list(self) -> list:
|
||||
response = self.client.list_buckets()
|
||||
buckets = []
|
||||
for bucket in response["Buckets"]:
|
||||
buckets.append(bucket)
|
||||
return buckets
|
||||
|
||||
def download(self, bucket, filepath) -> bytes:
|
||||
_file = BytesIO()
|
||||
self.client.download_fileobj(bucket, filepath, _file)
|
||||
_file.seek(0)
|
||||
return _file.read()
|
||||
|
||||
def upload(self, bucket, filepath, file_):
|
||||
m_type = mime[filepath.split(".")[-1]]
|
||||
self.client.upload_fileobj(file_, bucket, filepath, ExtraArgs={
|
||||
"ContentType": m_type, "ACL": "public-read", "CacheControl": "no-cache"})
|
||||
|
||||
|
||||
class Index(Bucket):
|
||||
def __init__(self, distribution: str, release: str, folder: str) -> None:
|
||||
super().__init__()
|
||||
self.distro = distribution
|
||||
self.rel = release
|
||||
self.path = folder
|
||||
self.get_manifest()
|
||||
self._gather()
|
||||
self.loader = FileSystemLoader("templates")
|
||||
self.jinja = Environment(loader=self.loader)
|
||||
if environ.get("S3_IS_MINIO"):
|
||||
self.upload_listing(self.create_listing(
|
||||
["alpine/", "ubuntu/"]
|
||||
), path_="index.html")
|
||||
self.upload_listing(self.create_listing(
|
||||
["3.13/", "3.12/"]
|
||||
), path_="alpine/index.html")
|
||||
self.upload_listing(self.create_listing(
|
||||
["bionic/", "focal/"]
|
||||
), path_="ubuntu/index.html")
|
||||
|
||||
def _gather(self) -> list:
|
||||
existing_files = []
|
||||
for key in self.manifest:
|
||||
existing_files.append(key)
|
||||
new_files = []
|
||||
for file_ in listdir(self.path):
|
||||
_dir = join(self.path, file_)
|
||||
if isfile(_dir) and not file_.endswith("any.whl"):
|
||||
file_name = basename(_dir)
|
||||
if file_name not in existing_files:
|
||||
new_files.append(file_name)
|
||||
self.new_files = new_files
|
||||
self.files = existing_files + new_files
|
||||
self.files.sort(key=lambda v: v.upper())
|
||||
|
||||
def get_manifest(self) -> dict:
|
||||
filepath = f"{self.distro}/{self.rel}/manifest.json"
|
||||
try:
|
||||
self.manifest = loads(self.download(self.bucket_name, filepath).decode("UTF-8"))
|
||||
except botoexeptions.ClientError:
|
||||
self.upload_manifest({})
|
||||
self.manifest = {}
|
||||
|
||||
def upload_manifest(self, obj: dict):
|
||||
filepath = f"{self.distro}/{self.rel}/manifest.json"
|
||||
obj = bytes(dumps(obj), "ascii")
|
||||
file_ = BytesIO(obj)
|
||||
file_.seek(0)
|
||||
self.upload(self.bucket_name, filepath, file_)
|
||||
|
||||
def add(self):
|
||||
filepath = f"{self.distro}/{self.rel}/"
|
||||
for wheel in self.new_files:
|
||||
file_ = open(self.path + wheel, "rb")
|
||||
self.upload(self.bucket_name, filepath + wheel, file_)
|
||||
self.manifest[wheel] = {"distribution": self.distro, "release": self.rel}
|
||||
self.upload_manifest(self.manifest)
|
||||
self.upload_listing(self.create_listing(self.files))
|
||||
|
||||
def create_listing(self, wheels: list) -> str:
|
||||
template = self.jinja.get_template("template.j2")
|
||||
output = template.render(wheels=wheels)
|
||||
return output
|
||||
|
||||
def upload_listing(self, obj: str, path_=None):
|
||||
filepath = path_ if path_ else f"{self.distro}/{self.rel}/index.html"
|
||||
obj = bytes(obj, "ascii")
|
||||
file_ = BytesIO(obj)
|
||||
file_.seek(0)
|
||||
self.upload(self.bucket_name, filepath, file_)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = ArgumentParser(prog="Wheelie", description="Program to upload and present wheels to S3 buckets")
|
||||
|
||||
parser.add_argument("-d", "--distribution", type=str,
|
||||
help="Distribution the wheels are target for.", choices=["alpine", "ubuntu"], required=True)
|
||||
|
||||
parser.add_argument("-r", "--release", type=str,
|
||||
help="Release of the distribution.", choices=["3.12", "3.13", "focal"], required=True)
|
||||
|
||||
parser.add_argument("-p", "--path", type=str, default="../out/",
|
||||
help="Path to the wheels")
|
||||
|
||||
parser.add_argument("-l", "--list", action="store_true", help="Lists files that would be added")
|
||||
parser.add_argument("-a", "--add", action="store_true", help="Adds new files, and builds the index")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
distro = args.distribution
|
||||
rel = args.release
|
||||
folder = args.path
|
||||
|
||||
inx = Index(distribution=distro, release=rel, folder=folder)
|
||||
|
||||
if args.list:
|
||||
print(inx.new_files if inx.new_files != [] else "No new files")
|
||||
|
||||
if args.add:
|
||||
inx.add()
|
2
generator/requirements.txt
Normal file
2
generator/requirements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
jinja2
|
||||
boto3
|
13
generator/templates/template.j2
Normal file
13
generator/templates/template.j2
Normal file
@ -0,0 +1,13 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>wheels.linuxserver.io</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Precompiled wheels commonly used in Linuxserver's images</h1>
|
||||
|
||||
{%- for wheel in wheels %}
|
||||
<a href='{{ wheel | e }}'>{{ wheel | e }}</a>
|
||||
<br />
|
||||
{%- endfor %}
|
||||
</body>
|
||||
</html>
|
2
requirements.txt
Normal file
2
requirements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
cryptography==3.4.1
|
||||
PyNaCl==1.4.0
|
21
setup.cfg
Normal file
21
setup.cfg
Normal file
@ -0,0 +1,21 @@
|
||||
[flake8]
|
||||
max-line-length = 120
|
||||
count = True
|
||||
statistics = True
|
||||
|
||||
[isort]
|
||||
no_lines_before = LOCALFOLDER
|
||||
indent = " "
|
||||
line_length = 120
|
||||
balanced_wrapping = True
|
||||
multi_line_output = 4
|
||||
|
||||
[pycodestyle]
|
||||
max_line_length = 120
|
||||
|
||||
[pylint.FORMAT]
|
||||
max-line-length=120
|
||||
|
||||
[pylint.BASIC]
|
||||
variable-naming-style=camelCase
|
||||
constant-naming-style=camelCase
|
Loading…
Reference in New Issue
Block a user