summaryrefslogtreecommitdiffstats
path: root/mojo/services/upload_service.py
blob: 184e35fd031da0399ef689185fb729e930f7a0cd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#!/usr/bin/env python
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import argparse
import imp
import os
import subprocess
import sys
import tempfile
import time
import zipfile

SERVICES = ["html_viewer", "network_service", "network_service_apptests"]

# A service does not need to expose interfaces. Those that do expose interfaces
# have their mojoms located in the directories listed below, in paths relative
# to the directory of this script.
MOJOMS_IN_DIR = {
    "network_service": os.path.join("network", "public", "interfaces")
}

# The network service is downloaded out-of-band rather than dynamically by the
# shell and thus can be stored zipped in the cloud. Other services are intended
# to be downloaded dynamically by the shell, which doesn't currently understand
# zipped binaries.
SERVICES_WITH_ZIPPED_BINARIES = ["network_service", "network_service_apptests"]

if not sys.platform.startswith("linux"):
  print "Only support linux for now"
  sys.exit(1)

root_path = os.path.realpath(
    os.path.join(
        os.path.dirname(
            os.path.realpath(__file__)),
        os.pardir,
        os.pardir))

find_depot_tools_path = os.path.join(root_path, "tools", "find_depot_tools.py")
find_depot_tools = imp.load_source("find_depot_tools", find_depot_tools_path)

depot_tools_path = find_depot_tools.add_depot_tools_to_path()
gsutil_exe = os.path.join(depot_tools_path, "third_party", "gsutil", "gsutil")

def get_version_name(custom_build):
  if custom_build:
    branch = subprocess.check_output(
        ["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=root_path).strip()
    try:
      base = subprocess.check_output(
          ["git", "config", "--get", "branch." + branch + ".base"],
          cwd=root_path).strip()
      issue = subprocess.check_output(
          ["git", "config", "--get", "branch." + branch + ".rietveldissue"],
          cwd=root_path).strip()
      patchset = subprocess.check_output(
          ["git", "config", "--get", "branch." + branch + ".rietveldpatchset"],
          cwd=root_path).strip()
    except subprocess.CalledProcessError:
      return None

    if not base or not issue or not patchset:
      return None
    else:
      return "custom_build_base_%s_issue_%s_patchset_%s" % (base, issue,
                                                            patchset)
  else:
    return subprocess.check_output(["git", "rev-parse", "HEAD"],
                                   cwd=root_path).strip()

def gsutil_cp(source, dest, dry_run):
  if dry_run:
    print "gsutil cp %s %s" % (source, dest)
  else:
    subprocess.check_call([gsutil_exe, "cp", source, dest])


def upload_mojoms(version_name, service, absolute_mojom_directory_path,
                  dry_run):
  dest = "gs://mojo/" + service + "/" + version_name + "/" + "mojoms.zip"

  with tempfile.NamedTemporaryFile() as mojom_zip_file:
    with zipfile.ZipFile(mojom_zip_file, 'w') as z:
      for root, _, files in os.walk(absolute_mojom_directory_path):
        for filename in files:
          absolute_file_path = os.path.join(root, filename)
          relative_file_path = os.path.relpath(absolute_file_path, root)
          z.write(absolute_file_path, relative_file_path)
    gsutil_cp(mojom_zip_file.name, dest, dry_run)


def upload_binary(version_name, service, binary_dir, platform, dry_run):
  dest_dir = "gs://mojo/" + service + "/" + version_name + "/" + platform + "/"
  should_zip = service in SERVICES_WITH_ZIPPED_BINARIES
  binary_name = service + ".mojo"
  absolute_binary_path = os.path.join(root_path, binary_dir, binary_name)

  if not should_zip:
    # Upload the binary.
    dest = dest_dir + binary_name
    gsutil_cp(absolute_binary_path, dest, dry_run)

    # Update the pointer to the service's location to point to the
    # newly-uploaded binary.
    service_location = dest.replace("gs://", "https://storage.googleapis.com/")
    location_file = ("gs://mojo/services/" + platform + "/" + service +
                     "_location")
    with tempfile.NamedTemporaryFile() as tmp:
      tmp.write(service_location)
      tmp.flush()
      gsutil_cp(tmp.name, location_file, dry_run)
    return

  # Zip the binary before uploading it to the cloud.
  dest = dest_dir + binary_name + ".zip"
  with tempfile.NamedTemporaryFile() as binary_zip_file:
    with zipfile.ZipFile(binary_zip_file, 'w') as z:
      with open(absolute_binary_path) as service_binary:
        zipinfo = zipfile.ZipInfo(binary_name)
        zipinfo.external_attr = 0o777 << 16
        zipinfo.compress_type = zipfile.ZIP_DEFLATED
        zipinfo.date_time = time.gmtime(os.path.getmtime(absolute_binary_path))
        z.writestr(zipinfo, service_binary.read())
    gsutil_cp(binary_zip_file.name, dest, dry_run)


def main():
  parser = argparse.ArgumentParser(
      description="Upload service mojoms and binaries to Google storage")
  parser.add_argument("-n", "--dry-run", action="store_true", help="Dry run")
  parser.add_argument(
      "--linux-x64-binary-dir",
      help="Path to the dir containing the linux-x64 service binary relative "
           "to the repo root, e.g. out/Release")
  parser.add_argument(
      "--android-arm-binary-dir",
      help="Path to the dir containing the android-arm service binary relative "
           "to the repo root, e.g. out/android_Release")
  parser.add_argument("service",
                      help="The service to be uploaded (one of %s)" % SERVICES)
  parser.add_argument(
      "--custom-build", action="store_true",
      help="Indicates that this is a build with change that is not committed. "
           "The change must be uploaded to Rietveld. The script needs to be "
           "run from the branch associated with the change.")
  args = parser.parse_args()

  if args.service not in SERVICES:
    print args.service + " is not one of the recognized services:"
    print SERVICES
    return 1

  version_name = get_version_name(args.custom_build)
  if args.custom_build and not version_name:
    print ("When uploading a custom build, the corresponding change to source "
           "code must be uploaded to Rietveld. Besides, this script needs to "
           "be run from the branch associated with the change.")
    return 1

  if args.service in MOJOMS_IN_DIR:
    script_dir = os.path.dirname(os.path.realpath(__file__))
    absolute_mojom_directory_path = os.path.join(script_dir,
                                                 MOJOMS_IN_DIR[args.service])
    upload_mojoms(version_name, args.service, absolute_mojom_directory_path,
                  args.dry_run)

  if args.linux_x64_binary_dir:
    upload_binary(version_name, args.service, args.linux_x64_binary_dir,
                  "linux-x64", args.dry_run)

  if args.android_arm_binary_dir:
    upload_binary(version_name, args.service, args.android_arm_binary_dir,
                  "android-arm", args.dry_run)

  if not args.dry_run:
    print "Uploaded artifacts for version %s" % (version_name, )
  else:
    print "No artifacts uploaded (dry run)"
  return 0

if __name__ == '__main__':
  sys.exit(main())