1*6dbdd20aSAndroid Build Coastguard Worker#!/usr/bin/env python3 2*6dbdd20aSAndroid Build Coastguard Worker# Copyright (C) 2021 The Android Open Source Project 3*6dbdd20aSAndroid Build Coastguard Worker# 4*6dbdd20aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 5*6dbdd20aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 6*6dbdd20aSAndroid Build Coastguard Worker# You may obtain a copy of the License at 7*6dbdd20aSAndroid Build Coastguard Worker# 8*6dbdd20aSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 9*6dbdd20aSAndroid Build Coastguard Worker# 10*6dbdd20aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 11*6dbdd20aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 12*6dbdd20aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*6dbdd20aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 14*6dbdd20aSAndroid Build Coastguard Worker# limitations under the License. 15*6dbdd20aSAndroid Build Coastguard Worker""" Builds all the revisions in channels.json and deploys them if --upload. 16*6dbdd20aSAndroid Build Coastguard Worker 17*6dbdd20aSAndroid Build Coastguard WorkerSee go/perfetto-ui-autopush for docs on how this works end-to-end. 18*6dbdd20aSAndroid Build Coastguard Worker""" 19*6dbdd20aSAndroid Build Coastguard Worker 20*6dbdd20aSAndroid Build Coastguard Workerimport argparse 21*6dbdd20aSAndroid Build Coastguard Workerimport json 22*6dbdd20aSAndroid Build Coastguard Workerimport os 23*6dbdd20aSAndroid Build Coastguard Workerimport re 24*6dbdd20aSAndroid Build Coastguard Workerimport shutil 25*6dbdd20aSAndroid Build Coastguard Workerimport subprocess 26*6dbdd20aSAndroid Build Coastguard Workerimport sys 27*6dbdd20aSAndroid Build Coastguard Worker 28*6dbdd20aSAndroid Build Coastguard Workerfrom os.path import dirname 29*6dbdd20aSAndroid Build Coastguard Worker 30*6dbdd20aSAndroid Build Coastguard Workerpjoin = os.path.join 31*6dbdd20aSAndroid Build Coastguard Worker 32*6dbdd20aSAndroid Build Coastguard WorkerBUCKET_NAME = 'ui.perfetto.dev' 33*6dbdd20aSAndroid Build Coastguard WorkerCUR_DIR = dirname(os.path.abspath(__file__)) 34*6dbdd20aSAndroid Build Coastguard WorkerROOT_DIR = dirname(dirname(CUR_DIR)) 35*6dbdd20aSAndroid Build Coastguard Worker 36*6dbdd20aSAndroid Build Coastguard Worker 37*6dbdd20aSAndroid Build Coastguard Workerdef check_call_and_log(args): 38*6dbdd20aSAndroid Build Coastguard Worker print(' '.join(args)) 39*6dbdd20aSAndroid Build Coastguard Worker subprocess.check_call(args) 40*6dbdd20aSAndroid Build Coastguard Worker 41*6dbdd20aSAndroid Build Coastguard Worker 42*6dbdd20aSAndroid Build Coastguard Workerdef check_output(args): 43*6dbdd20aSAndroid Build Coastguard Worker return subprocess.check_output(args).decode().strip() 44*6dbdd20aSAndroid Build Coastguard Worker 45*6dbdd20aSAndroid Build Coastguard Worker 46*6dbdd20aSAndroid Build Coastguard Workerdef version_exists(version): 47*6dbdd20aSAndroid Build Coastguard Worker url = 'https://commondatastorage.googleapis.com/%s/%s/manifest.json' % ( 48*6dbdd20aSAndroid Build Coastguard Worker BUCKET_NAME, version) 49*6dbdd20aSAndroid Build Coastguard Worker return 0 == subprocess.call(['curl', '-fLs', '-o', '/dev/null', url]) 50*6dbdd20aSAndroid Build Coastguard Worker 51*6dbdd20aSAndroid Build Coastguard Worker 52*6dbdd20aSAndroid Build Coastguard Workerdef build_git_revision(channel, git_ref, tmp_dir): 53*6dbdd20aSAndroid Build Coastguard Worker workdir = pjoin(tmp_dir, channel) 54*6dbdd20aSAndroid Build Coastguard Worker check_call_and_log(['rm', '-rf', workdir]) 55*6dbdd20aSAndroid Build Coastguard Worker check_call_and_log(['git', 'clone', '--quiet', '--shared', ROOT_DIR, workdir]) 56*6dbdd20aSAndroid Build Coastguard Worker old_cwd = os.getcwd() 57*6dbdd20aSAndroid Build Coastguard Worker os.chdir(workdir) 58*6dbdd20aSAndroid Build Coastguard Worker try: 59*6dbdd20aSAndroid Build Coastguard Worker check_call_and_log(['git', 'reset', '--hard', git_ref]) 60*6dbdd20aSAndroid Build Coastguard Worker check_call_and_log(['git', 'clean', '-dfx']) 61*6dbdd20aSAndroid Build Coastguard Worker git_sha = check_output(['git', 'rev-parse', 'HEAD']) 62*6dbdd20aSAndroid Build Coastguard Worker print('===================================================================') 63*6dbdd20aSAndroid Build Coastguard Worker print('Building UI for channel %s @ %s (%s)' % (channel, git_ref, git_sha)) 64*6dbdd20aSAndroid Build Coastguard Worker print('===================================================================') 65*6dbdd20aSAndroid Build Coastguard Worker version = check_output(['tools/write_version_header.py', '--stdout']) 66*6dbdd20aSAndroid Build Coastguard Worker check_call_and_log(['tools/install-build-deps', '--ui']) 67*6dbdd20aSAndroid Build Coastguard Worker check_call_and_log(['ui/build']) 68*6dbdd20aSAndroid Build Coastguard Worker return version, pjoin(workdir, 'ui/out/dist') 69*6dbdd20aSAndroid Build Coastguard Worker finally: 70*6dbdd20aSAndroid Build Coastguard Worker os.chdir(old_cwd) 71*6dbdd20aSAndroid Build Coastguard Worker 72*6dbdd20aSAndroid Build Coastguard Worker 73*6dbdd20aSAndroid Build Coastguard Workerdef build_all_channels(channels, tmp_dir, merged_dist_dir): 74*6dbdd20aSAndroid Build Coastguard Worker channel_map = {} 75*6dbdd20aSAndroid Build Coastguard Worker for chan in channels: 76*6dbdd20aSAndroid Build Coastguard Worker channel = chan['name'] 77*6dbdd20aSAndroid Build Coastguard Worker git_ref = chan['rev'] 78*6dbdd20aSAndroid Build Coastguard Worker # version here is something like "v1.2.3". 79*6dbdd20aSAndroid Build Coastguard Worker version, dist_dir = build_git_revision(channel, git_ref, tmp_dir) 80*6dbdd20aSAndroid Build Coastguard Worker channel_map[channel] = version 81*6dbdd20aSAndroid Build Coastguard Worker check_call_and_log(['cp', '-an', pjoin(dist_dir, version), merged_dist_dir]) 82*6dbdd20aSAndroid Build Coastguard Worker if channel != 'stable': 83*6dbdd20aSAndroid Build Coastguard Worker continue 84*6dbdd20aSAndroid Build Coastguard Worker # Copy also the /index.html and /service_worker.*, but only for the stable 85*6dbdd20aSAndroid Build Coastguard Worker # channel. The /index.html and SW must be shared between all channels, 86*6dbdd20aSAndroid Build Coastguard Worker # because they are all reachable through ui.perfetto.dev/. Both the index 87*6dbdd20aSAndroid Build Coastguard Worker # and the SQ are supposed to be version-independent (go/perfetto-channels). 88*6dbdd20aSAndroid Build Coastguard Worker # If an accidental incompatibility bug sneaks in, we should much rather 89*6dbdd20aSAndroid Build Coastguard Worker # crash canary (or any other channel) rather than stable. Hence why we copy 90*6dbdd20aSAndroid Build Coastguard Worker # the index+sw from the stable channel. 91*6dbdd20aSAndroid Build Coastguard Worker for fname in os.listdir(dist_dir): 92*6dbdd20aSAndroid Build Coastguard Worker fpath = pjoin(dist_dir, fname) 93*6dbdd20aSAndroid Build Coastguard Worker if os.path.isfile(fpath): 94*6dbdd20aSAndroid Build Coastguard Worker check_call_and_log(['cp', '-an', fpath, merged_dist_dir]) 95*6dbdd20aSAndroid Build Coastguard Worker return channel_map 96*6dbdd20aSAndroid Build Coastguard Worker 97*6dbdd20aSAndroid Build Coastguard Worker 98*6dbdd20aSAndroid Build Coastguard Workerdef main(): 99*6dbdd20aSAndroid Build Coastguard Worker parser = argparse.ArgumentParser() 100*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('--upload', action='store_true') 101*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('--tmp', default='/tmp/perfetto_ui') 102*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('--branch_only') 103*6dbdd20aSAndroid Build Coastguard Worker 104*6dbdd20aSAndroid Build Coastguard Worker args = parser.parse_args() 105*6dbdd20aSAndroid Build Coastguard Worker 106*6dbdd20aSAndroid Build Coastguard Worker # Read the releases.json, which maps channel names to git refs, e.g.: 107*6dbdd20aSAndroid Build Coastguard Worker # {name:'stable', rev:'a0b1c2...0}, {name:'canary', rev:'HEAD'} 108*6dbdd20aSAndroid Build Coastguard Worker channels = [] 109*6dbdd20aSAndroid Build Coastguard Worker with open(pjoin(CUR_DIR, 'channels.json')) as f: 110*6dbdd20aSAndroid Build Coastguard Worker channels = json.load(f)['channels'] 111*6dbdd20aSAndroid Build Coastguard Worker 112*6dbdd20aSAndroid Build Coastguard Worker if args.branch_only: 113*6dbdd20aSAndroid Build Coastguard Worker channels = [{'name': 'branch', 'rev': args.branch_only}] 114*6dbdd20aSAndroid Build Coastguard Worker 115*6dbdd20aSAndroid Build Coastguard Worker merged_dist_dir = pjoin(args.tmp, 'dist') 116*6dbdd20aSAndroid Build Coastguard Worker check_call_and_log(['rm', '-rf', merged_dist_dir]) 117*6dbdd20aSAndroid Build Coastguard Worker shutil.os.makedirs(merged_dist_dir) 118*6dbdd20aSAndroid Build Coastguard Worker channel_map = build_all_channels(channels, args.tmp, merged_dist_dir) 119*6dbdd20aSAndroid Build Coastguard Worker 120*6dbdd20aSAndroid Build Coastguard Worker if not args.branch_only: 121*6dbdd20aSAndroid Build Coastguard Worker print('Updating index in ' + merged_dist_dir) 122*6dbdd20aSAndroid Build Coastguard Worker with open(pjoin(merged_dist_dir, 'index.html'), 'r+') as f: 123*6dbdd20aSAndroid Build Coastguard Worker index_html = f.read() 124*6dbdd20aSAndroid Build Coastguard Worker f.seek(0, 0) 125*6dbdd20aSAndroid Build Coastguard Worker f.truncate() 126*6dbdd20aSAndroid Build Coastguard Worker index_html = re.sub( 127*6dbdd20aSAndroid Build Coastguard Worker r"data-perfetto_version='[^']*'", 128*6dbdd20aSAndroid Build Coastguard Worker "data-perfetto_version='%s'" % json.dumps(channel_map), index_html) 129*6dbdd20aSAndroid Build Coastguard Worker f.write(index_html) 130*6dbdd20aSAndroid Build Coastguard Worker 131*6dbdd20aSAndroid Build Coastguard Worker if not args.upload: 132*6dbdd20aSAndroid Build Coastguard Worker return 133*6dbdd20aSAndroid Build Coastguard Worker 134*6dbdd20aSAndroid Build Coastguard Worker print('===================================================================') 135*6dbdd20aSAndroid Build Coastguard Worker print('Uploading to gs://%s' % BUCKET_NAME) 136*6dbdd20aSAndroid Build Coastguard Worker print('===================================================================') 137*6dbdd20aSAndroid Build Coastguard Worker # TODO(primiano): re-enable caching once the gzip-related outage is restored. 138*6dbdd20aSAndroid Build Coastguard Worker # cache_hdr = 'Cache-Control:public, max-age=3600' 139*6dbdd20aSAndroid Build Coastguard Worker cache_hdr = 'Cache-Control:no-cache' 140*6dbdd20aSAndroid Build Coastguard Worker cp_cmd = ['gsutil', '-m', '-h', cache_hdr, 'cp', '-j', 'html,js,css,wasm,map'] 141*6dbdd20aSAndroid Build Coastguard Worker for name in os.listdir(merged_dist_dir): 142*6dbdd20aSAndroid Build Coastguard Worker path = pjoin(merged_dist_dir, name) 143*6dbdd20aSAndroid Build Coastguard Worker if os.path.isdir(path): 144*6dbdd20aSAndroid Build Coastguard Worker if version_exists(name): 145*6dbdd20aSAndroid Build Coastguard Worker print('Skipping upload of %s because it already exists on GCS' % name) 146*6dbdd20aSAndroid Build Coastguard Worker continue 147*6dbdd20aSAndroid Build Coastguard Worker check_call_and_log(cp_cmd + ['-r', path, 'gs://%s/' % BUCKET_NAME]) 148*6dbdd20aSAndroid Build Coastguard Worker else: 149*6dbdd20aSAndroid Build Coastguard Worker # /index.html or /service_worker.js{,.map} 150*6dbdd20aSAndroid Build Coastguard Worker check_call_and_log(cp_cmd + [path, 'gs://%s/%s' % (BUCKET_NAME, name)]) 151*6dbdd20aSAndroid Build Coastguard Worker 152*6dbdd20aSAndroid Build Coastguard Worker 153*6dbdd20aSAndroid Build Coastguard Workerif __name__ == '__main__': 154*6dbdd20aSAndroid Build Coastguard Worker sys.exit(main()) 155