diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 08dd8d45..967b4c91 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -79,16 +79,12 @@ jobs: with: name: HTML results path: build/html_report - - run: pip install requests - - name: Send linter results to GitHub + - name: Generate GitHub annotations JSON and process check result shell: python - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - ACTUALLY_SEND: ${{ github.event.type != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} run: | import os import sys - import requests + import json import xml.etree.ElementTree as ET OVERALL_STABLE_RULES=[ @@ -141,18 +137,6 @@ jobs: "whitespace", ] - # None of the available actions seem to do what I want, they all do stupid things like adding another check... let's just do it manually - # GitHub also doesn't seem to provide you with the check suite or check run ID, so we have to get it from the action ID via the API - s = requests.Session() - s.headers.update({ - 'Authorization': 'token ' + os.environ['GITHUB_TOKEN'], - 'Accept': 'application/vnd.github.antiope-preview+json' # Annotations are still technically a preview feature of the API - }) - action_run = s.get(os.environ['GITHUB_API_URL'] + "/repos/" + os.environ['GITHUB_REPOSITORY'] + "/actions/runs/" + os.environ['GITHUB_RUN_ID']).json() - check_suite = s.get(action_run['check_suite_url']).json() - check_suite_runs = s.get(check_suite['check_runs_url']).json() - check_run = check_suite_runs['check_runs'][0] # NOTE: This assumes that the 'lint' job is the first one in the workflow. You could find it by name if you really wanted. - def we_care_about(file_name, type): if 'CBot' in file_name: return type in OVERALL_STABLE_RULES @@ -161,6 +145,7 @@ jobs: results = ET.parse('build/colobot_lint_report.xml') annotations = [] + stable_annotations = [] for error in results.find('errors').findall('error'): location = error.find('location') file_name = os.path.relpath(location.get('file'), os.environ['GITHUB_WORKSPACE']) @@ -175,42 +160,35 @@ jobs: elif severity == 'information': gh_severity = 'notice' - if not we_care_about(file_name, type): - # don't send the unstable rules to github at all as there are way too many of them and it would overload the API rate limit - continue - - print('{}:{}: [{}] {}'.format(file_name, line_num, type, msg)) - - annotations.append({ + annotation = { 'path': file_name, 'start_line': line_num, 'end_line': line_num, 'annotation_level': gh_severity, 'title': type, 'message': msg - }) + } + annotations.append(annotation) - summary = 'colobot-lint found {} issues'.format(len(annotations)) - all_ok = len(annotations) == 0 + if we_care_about(file_name, type): + # don't send the unstable rules to github at all as there are way too many of them and it would overload the API rate limit + stable_annotations.append(annotation) + print('{}:{}: [{}] {}'.format(file_name, line_num, type, msg)) + + summary = 'colobot-lint found {} issues'.format(len(stable_annotations)) + all_ok = len(stable_annotations) == 0 print('Conclusion: {}'.format(summary)) - if os.environ['ACTUALLY_SEND'] != "true": - print('Skip uploading the results as annotations because tokens from forks are readonly and there seems to be no way to do it. Blame GitHub Actions devs.') - else: - # Annotations have to be sent in batches of 50 - first = True - while first or len(annotations) > 0: - first = False - to_send = annotations[:50] - annotations = annotations[50:] - data = { - 'output': { - 'title': summary, - 'summary': summary, - 'annotations': to_send - } - } - r = s.patch(check_run['url'], json=data) - r.raise_for_status() - + with open("build/annotations.json", "w") as f: + json.dump(annotations, f, indent=4) + with open("build/stable_annotations.json", "w") as f: + json.dump(stable_annotations, f, indent=4) sys.exit(0 if all_ok else 1) + - name: Upload results (JSON) + uses: actions/upload-artifact@v2 + with: + name: JSON results + path: | + build/annotations.json + build/stable_annotations.json + if: ${{ always() }} diff --git a/.github/workflows/lint_upload_results.yml b/.github/workflows/lint_upload_results.yml new file mode 100644 index 00000000..3adb3c54 --- /dev/null +++ b/.github/workflows/lint_upload_results.yml @@ -0,0 +1,67 @@ +name: Linter upload results + +# Upload linter results after succesful linter run +# This is done in a separate workflow to safely use the read-write GitHub token +# See https://securitylab.github.com/research/github-actions-preventing-pwn-requests + +on: + workflow_run: + workflows: ["Linter"] + types: + - completed + +jobs: + lint_upload: + runs-on: ubuntu-16.04 + steps: + - run: pip install requests + - name: Download linter results + uses: dawidd6/action-download-artifact@v2 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + workflow: lint.yml + run_id: ${{ github.event.workflow_run.id }} + name: JSON results + path: results + - name: Send linter results to GitHub + shell: python + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RUN_ID: ${{ github.event.workflow_run.id }} + run: | + import os + import json + import requests + + # Load the results from the lint job artifact + with open("results/stable_annotations.json", "r") as f: + annotations = json.load(f) + summary = 'colobot-lint found {} issues'.format(len(annotations)) + + # None of the available actions seem to do what I want, they all do stupid things like adding another check... let's just do it manually + # GitHub also doesn't seem to provide you with the check suite or check run ID, so we have to get it from the action ID via the API + s = requests.Session() + s.headers.update({ + 'Authorization': 'token ' + os.environ['GITHUB_TOKEN'], + 'Accept': 'application/vnd.github.antiope-preview+json' # Annotations are still technically a preview feature of the API + }) + action_run = s.get(os.environ['GITHUB_API_URL'] + "/repos/" + os.environ['GITHUB_REPOSITORY'] + "/actions/runs/" + os.environ['RUN_ID']).json() + check_suite = s.get(action_run['check_suite_url']).json() + check_suite_runs = s.get(check_suite['check_runs_url']).json() + check_run = check_suite_runs['check_runs'][0] # NOTE: This assumes that the 'lint' job is the first one in the workflow. You could find it by name if you really wanted. + + # Annotations have to be sent in batches of 50 + first = True + while first or len(annotations) > 0: + first = False + to_send = annotations[:50] + annotations = annotations[50:] + data = { + 'output': { + 'title': summary, + 'summary': summary, + 'annotations': to_send + } + } + r = s.patch(check_run['url'], json=data) + r.raise_for_status()