xref: /aosp_15_r20/external/perfetto/ui/release/build_all_channels.py (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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