Skip to content

Instantly share code, notes, and snippets.

@benoit-pierre
Last active August 2, 2016 19:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save benoit-pierre/13029fd15a485468a5ae12feab16297c to your computer and use it in GitHub Desktop.
Save benoit-pierre/13029fd15a485468a5ae12feab16297c to your computer and use it in GitHub Desktop.
script to merge all the current draft releases of a GitHub project into one single draft release
#!/usr/bin/env python
import json
import sys
import requests
import uritemplate
with open('.github.repo', 'r') as fp:
REPOSITORY = fp.read().strip()
RELEASES_URL = 'https://api.github.com/repos/%s/releases' % REPOSITORY
with open('.github.token', 'r') as fp:
TOKEN = fp.read().strip()
dry_run = len(sys.argv) > 1 and '-n' == sys.argv[1]
# List draft releases.
print('release drafts:')
drafts = []
headers = { 'Authorization': 'token ' + TOKEN }
r = requests.get(RELEASES_URL, headers=headers)
assert r.status_code == 200, r
for release in r.json():
if not release['draft']:
continue
print('- %s [%s]' % (release['name'],
release['tag_name']))
drafts.append(release)
if len(drafts) < 2:
print('nothing to do!')
sys.exit(1)
# Find target release (highest combined assets size),
# and it's name, body, and pre-release flag.
target_release = drafts[0]
max_assets_size = 0
field_names = ['name', 'body', 'prerelease']
field_values = [set() for n in field_names]
for release in drafts:
for name, values in zip(field_names, field_values):
val = release.get(name)
if val:
values.add(val)
if release.get('assets') is None:
continue
assets_size = 0
for asset in release['assets']:
if asset.get('state') != 'uploaded':
continue
assets_size += asset['size']
if assets_size > max_assets_size:
max_assets_size = assets_size
target_release = release
# Update target release metadata.
data = {
'name' : ' '.join(field_values[0]),
'draft' : True,
'prerelease': True in field_values[2],
'body' : ' '.join(field_values[1]) or '',
}
print('target release:')
for n, v in sorted(data.items()):
print('- %-16s: %r' % (n, v))
if not dry_run:
r = requests.patch('%s/%s' % (RELEASES_URL, target_release['id']),
headers=headers, data=json.dumps(data))
assert r.status_code == 200, r
target_release = r.json()
upload_url = target_release['upload_url']
# Cleanup target release assets.
print('assets:')
existing_assets = set()
for asset in target_release.get('assets', ()):
if asset['state'] != 'uploaded':
if not dry_run:
r = requests.delete('%s/assets/%s' % (RELEASES_URL, asset['id']),
headers=headers)
assert r.status_code == 204, r
continue
print('- %s' % asset['name'])
existing_assets.add(asset['name'])
# And move other releases assets to the target release.
for release in drafts:
if release['id'] == target_release['id']:
continue
if release.get('assets') is None:
continue
for asset in release['assets']:
if asset['state'] != 'uploaded':
continue
if asset['name'] in existing_assets:
continue
print('+ %s' % asset['name'])
if dry_run:
continue
# Download the asset.
asset_headers = dict(headers)
asset_headers['Accept'] = 'application/octet-stream'
r = requests.get('%s/assets/%s' % (RELEASES_URL, asset['id']),
headers=asset_headers)
assert len(r.content) == asset['size']
# Upload it back to the target release.
asset_url = uritemplate.expand(upload_url, asset)
asset_headers = dict(headers)
asset_headers['Content-Type'] = asset['content_type']
r = requests.post(asset_url, headers=asset_headers, data=r.content)
assert r.status_code == 201, r
existing_assets.add(asset['name'])
if not dry_run:
# Delete the release.
r = requests.delete('%s/%s' % (RELEASES_URL, release['id']),
headers=headers)
assert r.status_code == 204, r
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment