diff --git a/.github/prtester-requirements.txt b/.github/prtester-requirements.txt new file mode 100644 index 00000000..4fb08b57 --- /dev/null +++ b/.github/prtester-requirements.txt @@ -0,0 +1,2 @@ +beautifulsoup4>=4.10.0 +requests>=2.26.0 \ No newline at end of file diff --git a/.github/prtester.py b/.github/prtester.py new file mode 100644 index 00000000..56204ce7 --- /dev/null +++ b/.github/prtester.py @@ -0,0 +1,74 @@ +import requests +from bs4 import BeautifulSoup +import random +import json +import os.path + +# This script is specifically written to be used in automation for https://github.com/RSS-Bridge/rss-bridge +# +# This will scrape the whitelisted bridges in the current state (port 3000) and the PR state (port 3001) of +# RSS-Bridge, generate a feed for each of the bridges and save the output as html files. +# It also replaces the default static CSS link with a hardcoded link to @em92's public instance, so viewing +# the HTML file locally will actually work as designed. + +def testBridges(bridges,status): + for bridge in bridges: + if bridge.get('data-ref'): # Some div entries are empty, this ignores those + bridgeid = bridge.get('id') + bridgeid = bridgeid.split('-')[1] # this extracts a readable bridge name from the bridge metadata + bridgestring = '/?action=display&bridge=' + bridgeid + '&format=Html' + forms = bridge.find_all("form") + formid = 1 + for form in forms: + # a bridge can have multiple contexts, named 'forms' in html + # this code will produce a fully working formstring that should create a working feed when called + # this will create an example feed for every single context, to test them all + formstring = '' + errormessages = [] + parameters = form.find_all("input") + lists = form.find_all("select") + # this for/if mess cycles through all available input parameters, checks if it required, then pulls + # the default or examplevalue and then combines it all together into the formstring + # if an example or default value is missing for a required attribute, it will throw an error + # any non-required fields are not tested!!! + for parameter in parameters: + if parameter.get('type') == 'number' or parameter.get('type') == 'text': + if parameter.has_attr('required'): + if parameter.get('placeholder') == '': + if parameter.get('value') == '': + errormessages.append(parameter.get('name')) + else: + formstring = formstring + '&' + parameter.get('name') + '=' + parameter.get('value') + else: + formstring = formstring + '&' + parameter.get('name') + '=' + parameter.get('placeholder') + # same thing, just for checkboxes. If a checkbox is checked per default, it gets added to the formstring + if parameter.get('type') == 'checkbox': + if parameter.has_attr('checked'): + formstring = formstring + '&' + parameter.get('name') + '=on' + for list in lists: + formstring = formstring + '&' + list.get('name') + '=' + list.contents[0].get('value') + if not errormessages: + # if all example/default values are present, form the full request string, run the request, replace the static css + # file with the url of em's public instance and then write it all to file. + r = requests.get(URL + bridgestring + formstring) + pagetext = r.text.replace('static/HtmlFormat.css','https://feed.eugenemolotov.ru/static/HtmlFormat.css') + with open(os.getcwd() + "/results/" + bridgeid + '-' + status + '-context' + str(formid) + '.html', 'w+') as file: + file.write(pagetext) + else: + # if there are errors (which means that a required value has no example or default value), log out which error appeared + with open(os.getcwd() + "/results/" + bridgeid + '-' + status + '-context' + str(formid) + '.html', 'w+') as file: + file.write(str(errormessages)) + formid += 1 + +gitstatus = ["current", "pr"] + +for status in gitstatus: # run this twice, once for the current version, once for the PR version + if status == "current": + port = "3000" # both ports are defined in the corresponding workflow .yml file + elif status == "pr": + port = "3001" + URL = "http://localhost:" + port + page = requests.get(URL) # Use python requests to grab the rss-bridge main page + soup = BeautifulSoup(page.content, "html.parser") # use bs4 to turn the page into soup + bridges = soup.find_all("section") # get a soup-formatted list of all bridges on the rss-bridge page + testBridges(bridges,status) # run the main scraping code with the list of bridges and the info if this is for the current version or the pr version diff --git a/.github/workflows/prhtmlgenerator.yml b/.github/workflows/prhtmlgenerator.yml new file mode 100644 index 00000000..979eea67 --- /dev/null +++ b/.github/workflows/prhtmlgenerator.yml @@ -0,0 +1,59 @@ +name: 'PR Testing' + +on: + pull_request_target: + branches: [ master ] + +jobs: + test-pr: + name: Generate HTML + runs-on: ubuntu-latest + # Needs additional permissions https://github.com/actions/first-interaction/issues/10#issuecomment-1041402989 + steps: + - name: Check out self + uses: actions/checkout@v2.3.2 + with: + ref: ${{github.event.pull_request.head.ref}} + repository: ${{github.event.pull_request.head.repo.full_name}} + - name: Check out rss-bridge + run: | + PR=${{github.event.number}}; + mv .github/prtester-requirements.txt ./requirements.txt; + mv .github/prtester.py .; + wget https://patch-diff.githubusercontent.com/raw/$GITHUB_REPOSITORY/pull/$PR.patch; + cat $PR.patch | grep " bridges/.*\.php" | sed "s= bridges/\(.*\)Bridge.php.*=\1=g" | sort | uniq > whitelist.txt + - name: Start Docker - Current + run: | + docker run -d -v $GITHUB_WORKSPACE/whitelist.txt:/app/whitelist.txt -p 3000:80 ghcr.io/rss-bridge/rss-bridge:latest + - name: Start Docker - PR + run: | + docker build -t prbuild .; + docker run -d -v $GITHUB_WORKSPACE/whitelist.txt:/app/whitelist.txt -p 3001:80 prbuild + - name: Setup python + uses: actions/setup-python@v2 + with: + python-version: '3.7' + cache: 'pip' + - name: Install requirements + run: | + cd $GITHUB_WORKSPACE + pip install -r requirements.txt + - name: Run bridge tests + run: | + mkdir results; + python prtester.py + - name: Find result files + uses: tj-actions/glob@v7.10 + id: artifacts + with: + files: | + ./results/*.html + - name: Upload results to PR + uses: yamanq/pull-request-artifacts@v1.3.1 + with: + commit: ${{ github.event.pull_request.head.sha }} + repo-token: ${{ secrets.GITHUB_TOKEN }} + artifacts-branch: artifacts + artifacts-prefix-url: "https://htmlpreview.github.io/?" + artifacts: | + ${{ steps.artifacts.outputs.paths }} \ No newline at end of file