From d43d0646cd3455fdccf961ce221caf88a926c7a8 Mon Sep 17 00:00:00 2001 From: chris Date: Wed, 12 Dec 2018 19:57:03 +0100 Subject: [PATCH] ship it --- Dockerfile | 11 ++++ app.py | 128 +++++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 6 +++ 3 files changed, 145 insertions(+) create mode 100644 Dockerfile create mode 100644 app.py create mode 100644 requirements.txt diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..622fd84 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +FROM reg.zknt.org/zknt/python:3.6.6-r0 + +COPY requirements.txt /app/requirements.txt +WORKDIR /app +RUN apk add --no-cache gcc libxml2 libxslt libxml2-dev musl-dev libxslt-dev python3-dev && pip3 install -r requirements.txt && apk del gcc libxml2-dev musl-dev libxslt-dev python3-dev + +COPY . /app +ENV FLASK_APP=app.py +VOLUME /data +EXPOSE 5000 +ENTRYPOINT flask run --host=0.0.0.0 diff --git a/app.py b/app.py new file mode 100644 index 0000000..d1f8ec9 --- /dev/null +++ b/app.py @@ -0,0 +1,128 @@ +import dbm +import json + +import requests +from bs4 import BeautifulSoup +from flask import Flask + + +class Store: + def __init__(self, dbo): + self.db = dbo + + def add_version(self, pkg, version, bdate, branch='v3.8', arch='x86_64'): + pkg_key = json.dumps((pkg, branch, arch)) + versions = json.loads(self.db.get(pkg_key, "{}")) + if bdate not in versions: + versions[bdate] = version + self.db[pkg_key] = json.dumps(versions) + + def get_feed(self, pkgs=None, branch='v3.8', arch='x86_64'): + items = [] + if not pkgs: + pkgs = ['bash'] + for pkg in pkgs: + pkg_key = json.dumps((pkg, branch, arch)) + items = items + [(pkg, x[0], x[1]) for x in json.loads(self.db.get(pkg_key, "{}")).items()] + return gen_feed(items) + + +app = Flask(__name__) +db = dbm.open('/data/cache', 'c') +pkg_store = Store(db) + + +def get_release(pkg, branch="v3.8", arch="x86_64"): + req = requests.get( + "https://pkgs.alpinelinux.org/packages?name={}&branch={}&arch={}".format( + pkg, + branch, + arch, + ) + ) + soup = BeautifulSoup(req.text, 'html.parser') + if not soup.find_all("td", class_="version"): + return None + if branch == "edge": # literal edge case -_- + ver_link = soup.find_all("td", class_="version")[0].contents[1] + version = ver_link.find_all("a")[0].contents[0] + else: + version = soup.find_all("td", class_="version")[0].contents[0] + bdate = soup.find_all("td", class_="bdate")[0].contents[0] + return (version, bdate) + + +def gen_feed(items): + from feedgen.feed import FeedGenerator + from slugify import slugify + feedgen = FeedGenerator() + feedgen.id('https://alprss.zknt.org/feed/1') + feedgen.title('alpine packagefeed') + feedgen.link(href='https://alprss.zknt.org/', rel="alternate") + feedgen.description('packages') + for item in items: + feedentry = feedgen.add_entry() + feedentry.id('https://alprss.zknt.org/{}/{}-{}'.format(slugify(item[0]), slugify(item[2]), slugify(item[1]))) + feedentry.title("{} version: {}".format(item[0], item[2])) + feedentry.published(item[1] + ' UTC') + return feedgen + + +@app.route('/rss') +def handle(): + """ Dump all known packags to feed. """ + for pkg in ["bash"]: + try: + version, bdate = get_release(pkg) + pkg_store.add_version(pkg, version, bdate) + except TypeError: + pass + return pkg_store.get_feed().rss_str() + + +@app.route('/rss/') +def handle_list(pkglist): + for pkg in pkglist.split(','): + try: + version, bdate = get_release(pkg) + pkg_store.add_version(pkg, version, bdate) + except TypeError: + pass + return pkg_store.get_feed(pkglist.split(',')).rss_str() + + +@app.route('/rss///') +def handle_params(branch, arch, pkglist): + for pkg in pkglist.split(','): + try: + version, bdate = get_release(pkg, branch=branch, arch=arch) + pkg_store.add_version(pkg, version, bdate, branch, arch) + except TypeError: + pass + return pkg_store.get_feed(pkglist.split(','), branch, arch).rss_str() + + +@app.route('/') +def index(): + import markdown + return markdown.markdown(""" +# alprss + +Provide alpine package search results as RSS feed. + +## Usage: + + GET /rss/your,package,list + +This will combine results for the packages `your`, `package` and `list`. +Requesting information on non-existant packages will result in empty output. +Search is restricted to alpines latest stable branch, and arch x86_64 by default. + + GET /rss/branch/arch/your,package,list + +Same as above, but queries supplied alpine branch and arch. + +### Example: + + curl https://alprss.zknt.org/rss/vim,emacs +""") diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..52e7e5f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +beautifulsoup4==4.6.3 +feedgen==0.7.0 +requests==2.20.1 +python-slugify==1.2.6 +Flask==1.0.2 +Markdown==3.0.1