mirror of
https://github.com/oneclickvirt/backtrace.git
synced 2026-10-16 08:25:49 +08:00
67 lines
3.2 KiB
Python
67 lines
3.2 KiB
Python
"""Merge Dependabot Action patch/minor updates after all head checks pass."""
|
|
import json
|
|
import os
|
|
import re
|
|
import time
|
|
import urllib.request
|
|
|
|
repository = os.environ['GITHUB_REPOSITORY']
|
|
event = json.load(open(os.environ['GITHUB_EVENT_PATH']))
|
|
|
|
def call(path, method='GET', body=None):
|
|
req = urllib.request.Request('https://api.github.com/repos/' + repository + path, method=method,
|
|
data=None if body is None else json.dumps(body).encode(),
|
|
headers={'Authorization': 'Bearer ' + os.environ['GH_TOKEN'], 'Accept': 'application/vnd.github+json', 'Content-Type': 'application/json'})
|
|
with urllib.request.urlopen(req, timeout=40) as response:
|
|
value = response.read()
|
|
return json.loads(value) if value else None
|
|
|
|
def versioned_changes(files):
|
|
count = 0
|
|
for file in files:
|
|
path = file['filename']
|
|
if not path.startswith('.github/') or not path.endswith(('.yml', '.yaml')) or 'patch' not in file:
|
|
return False
|
|
old, new = {}, {}
|
|
for line in file['patch'].splitlines():
|
|
if not line.startswith(('-', '+')) or line.startswith(('---', '+++')):
|
|
continue
|
|
match = re.match(r'[+-]\s*(?:-\s*)?uses:\s*([^\s@]+)@[0-9a-f]{40}\s+#\s*v?(\d+)(?:\.(\d+))?(?:\.(\d+))?\s*$', line)
|
|
if not match:
|
|
# Updating any workflow behavior or an unversioned action
|
|
# remains a reviewed change.
|
|
return False
|
|
(old if line[0] == '-' else new)[match[1]] = tuple(int(x or 0) for x in match.groups()[1:])
|
|
if set(old) != set(new):
|
|
return False
|
|
for action in old:
|
|
if old[action][0] != new[action][0] or new[action] < old[action]:
|
|
return False
|
|
count += 1
|
|
return count > 0
|
|
|
|
run = event['workflow_run']
|
|
if run['conclusion'] == 'success' and run['event'] == 'pull_request':
|
|
for reference in run.get('pull_requests', []):
|
|
number = reference['number']
|
|
pr = call('/pulls/' + str(number))
|
|
if pr['state'] != 'open' or pr['user']['login'] != 'dependabot[bot]' or pr['head']['repo']['full_name'] != repository:
|
|
continue
|
|
if pr['head']['sha'] != run['head_sha'] or pr.get('draft'):
|
|
continue
|
|
files = call('/pulls/' + str(number) + '/files?per_page=100')
|
|
if pr['changed_files'] > 100 or not versioned_changes(files):
|
|
continue
|
|
sha = pr['head']['sha']
|
|
for attempt in range(90):
|
|
checks = call('/commits/' + sha + '/check-runs?per_page=100')['check_runs']
|
|
statuses = call('/commits/' + sha + '/status')['statuses']
|
|
if any(x['status'] == 'completed' and x['conclusion'] not in ('success', 'neutral', 'skipped') for x in checks) or any(x['state'] in ('failure', 'error') for x in statuses):
|
|
break
|
|
if checks and all(x['status'] == 'completed' for x in checks) and all(x['state'] == 'success' for x in statuses):
|
|
latest = call('/pulls/' + str(number))
|
|
if latest['head']['sha'] == sha and latest['state'] == 'open':
|
|
call('/pulls/' + str(number) + '/merge', 'PUT', {'sha': sha, 'merge_method': 'squash'})
|
|
break
|
|
time.sleep(20)
|