1#!/usr/bin/env python3 2 3# Copyright 2016 The Meson development team 4 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8 9# http://www.apache.org/licenses/LICENSE-2.0 10 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17# ghwt - GitHub WrapTool 18# 19# An emergency wraptool(1) replacement downloader that downloads 20# directly from GitHub in case wrapdb.mesonbuild.com is down. 21 22import urllib.request, json, sys, os, shutil, subprocess 23import configparser, hashlib 24 25req_timeout = 600.0 26private_repos = {'meson', 'wrapweb', 'meson-ci'} 27spdir = 'subprojects' 28 29def gh_get(url): 30 r = urllib.request.urlopen(url, timeout=req_timeout) 31 jd = json.loads(r.read().decode('utf-8')) 32 return jd 33 34def list_projects(): 35 jd = gh_get('https://api.github.com/orgs/mesonbuild/repos') 36 entries = [entry['name'] for entry in jd] 37 entries = [e for e in entries if e not in private_repos] 38 entries.sort() 39 for i in entries: 40 print(i) 41 return 0 42 43def unpack(sproj, branch): 44 tmpdir = os.path.join(spdir, sproj + '_ghwt') 45 shutil.rmtree(tmpdir, ignore_errors=True) 46 subprocess.check_call(['git', 'clone', '-b', branch, f'https://github.com/mesonbuild/{sproj}.git', tmpdir]) 47 usfile = os.path.join(tmpdir, 'upstream.wrap') 48 assert(os.path.isfile(usfile)) 49 config = configparser.ConfigParser(interpolation=None) 50 config.read(usfile) 51 outdir = os.path.join(spdir, sproj) 52 if 'directory' in config['wrap-file']: 53 outdir = os.path.join(spdir, config['wrap-file']['directory']) 54 if os.path.isdir(outdir): 55 print(f'Subproject is already there. To update, nuke the {outdir} dir and reinstall.') 56 shutil.rmtree(tmpdir) 57 return 1 58 us_url = config['wrap-file']['source_url'] 59 us = urllib.request.urlopen(us_url, timeout=req_timeout).read() 60 h = hashlib.sha256() 61 h.update(us) 62 dig = h.hexdigest() 63 should = config['wrap-file']['source_hash'] 64 if dig != should: 65 print('Incorrect hash on download.') 66 print(' expected:', should) 67 print(' obtained:', dig) 68 return 1 69 ofilename = os.path.join(spdir, config['wrap-file']['source_filename']) 70 with open(ofilename, 'wb') as ofile: 71 ofile.write(us) 72 if 'lead_directory_missing' in config['wrap-file']: 73 os.mkdir(outdir) 74 shutil.unpack_archive(ofilename, outdir) 75 else: 76 shutil.unpack_archive(ofilename, spdir) 77 assert(os.path.isdir(outdir)) 78 shutil.move(os.path.join(tmpdir, '.git'), outdir) 79 subprocess.check_call(['git', 'reset', '--hard'], cwd=outdir) 80 shutil.rmtree(tmpdir) 81 shutil.rmtree(os.path.join(outdir, '.git')) 82 os.unlink(ofilename) 83 84def install(sproj, requested_branch=None): 85 if not os.path.isdir(spdir): 86 print('Run this in your source root and make sure there is a subprojects directory in it.') 87 return 1 88 blist = gh_get(f'https://api.github.com/repos/mesonbuild/{sproj}/branches') 89 blist = [b['name'] for b in blist] 90 blist = [b for b in blist if b != 'master'] 91 blist.sort() 92 branch = blist[-1] 93 if requested_branch is not None: 94 if requested_branch in blist: 95 branch = requested_branch 96 else: 97 print('Could not find user-requested branch', requested_branch) 98 print('Available branches for', sproj, ':') 99 print(blist) 100 return 1 101 print('Using branch', branch) 102 return unpack(sproj, branch) 103 104def print_help(): 105 print('Usage:') 106 print(sys.argv[0], 'list') 107 print(sys.argv[0], 'install', 'package_name', '[branch_name]') 108 109def run(args): 110 if not args or args[0] == '-h' or args[0] == '--help': 111 print_help() 112 return 1 113 command = args[0] 114 args = args[1:] 115 if command == 'list': 116 list_projects() 117 return 0 118 elif command == 'install': 119 if len(args) == 1: 120 return install(args[0]) 121 elif len(args) == 2: 122 return install(args[0], args[1]) 123 else: 124 print_help() 125 return 1 126 else: 127 print('Unknown command') 128 return 1 129 130if __name__ == '__main__': 131 print('This is an emergency wrap downloader. Use only when wrapdb is down.') 132 sys.exit(run(sys.argv[1:])) 133