Ejemplo n.º 1
0
def push_open_brush_to_oculus(build_path, release_channel, release_notes):
    assert os.path.isabs(build_path)
    assert os.path.exists(build_path)
    assert release_channel is not None, "You must pass a release channel to push to Oculus Home"

    # TEMP: yucky code to figure out if rift or quest
    build_type = get_oculus_build_type(build_path)
    if build_type == 'rift':
        app_id = TB_OCULUS_RIFT_APP_ID
        args = [
            'ovr-platform-util', 'upload-rift-build', '--app_id', app_id,
            '--build_dir', build_path, '--app_secret',
            get_secret('APP_SECRET_FOR_' + app_id,
                       app_id), '--channel', release_channel, '--version',
            get_build_stamp(build_path), '--notes',
            quote_oculus_release_notes(release_notes), '--launch_file',
            get_oculus_openbrush_exe(build_path), '--redistributables',
            ','.join(OCULUS_RIFT_REDISTS), '--firewall_exceptions', 'true'
        ]
    elif build_type == 'quest':
        import glob
        apks = glob.glob(build_path + '/*.apk')
        if len(apks) != 1:
            raise BuildFailed("No or too many APKs in %s: %s" %
                              (build_path, apks))
        apk = apks[0]
        # This requires a recent build of ovr-platform-util
        app_id = TB_OCULUS_QUEST_APP_ID
        args = [
            'ovr-platform-util',
            'upload-quest-build',
            '--app_id',
            app_id,
            '--app_secret',
            get_secret('APP_SECRET_FOR_' + app_id, app_id),
            '--apk',
            apk,
            # --assets-dir
            # --assets-file-iap-configs-file
            # --obb
            '--channel',
            release_channel,
            '--notes',
            quote_oculus_release_notes(release_notes),
        ]
    else:
        raise BuildFailed("Internal error: %s" % build_type)

    try:
        proc = Popen(args, stdin=PIPE, stdout=PIPE, stderr=STDOUT)
        proc.stdin.close()
    except OSError as e:
        # Probably "cannot find the file specified"
        if 'cannot find the file' in str(e):
            raise BuildFailed(
                "You don't seem to have ovr-platform-util installed.\nDownload it at https://dashboard.oculus.com/tools/cli"
            )
        else:
            raise
    except subprocess.CalledProcessError as e:
        raise BuildFailed("ovr-platform-util failed: %s" % e)

    saw_output = False
    desired_version_code = None
    for line in group_into_lines(unbuffered_reads(proc.stdout)):
        if line.strip():
            saw_output = True
        sys.stdout.write(line)
        # The request will be retried indefinitely, so stall it out
        if 'error occurred. The request will be retried.' in line:
            print()
            get_credential(app_id).delete_secret()
            # Maybe the secret changed; ask user to re-enter it
            raise BuildFailed("Your App Secret might be incorrect. Try again.")
        m = re.search(
            r'higher version code has previously been uploaded .code: (?P<code>\d+)',
            line)
        if m is not None:
            desired_version_code = int(m.group('code')) + 1

        # Example error text:
        # * An APK has already been uploaded with version code 59. Please update the application manifest's version code to 64 or higher and try again.
        m = re.search(r'version code to (?P<code>\d+) or higher and try again',
                      line)
        if m is not None:
            desired_version_code = int(m.group('code'))

    if proc.wait() != 0:
        message = 'ovr-platform-util failed with code %s' % proc.wait()
        if desired_version_code is not None:
            raise BadVersionCode(message, desired_version_code)
        else:
            raise BuildFailed(message)
    if not saw_output:
        raise BuildFailed(
            'ovr-platform-util seemed to do nothing.\nYou probably need a newer version.\nDownload it at https://dashboard.oculus.com/tools/cli'
        )
Ejemplo n.º 2
0
def build(stamp, output_dir, project_dir, exe_base_name,
          experimental, platform, il2cpp, vrsdk, config, for_distribution,
          is_jenkins):
  """Create a build of Tilt Brush.
  Pass:
    stamp - string describing the version+build; will be embedded into the build somehow.
    output_dir - desired output directory name
    project_dir - directory name
    project_name - name of the executable to create (sans extension)
    experimental - boolean
    platform - one of (Windows, OSX, Linux, Android, iOS)
    il2cpp - boolean
    vrsdk - Config.SdkMode; valid values are (Oculus, SteamVR, Monoscopic)
    config - one of (Debug, Release)
    for_distribution - boolean. Enables android signing, version code bump, removal of pdb files.
    is_jenkins - boolean; used to customize stdout logging
  Returns:
    the actual output directory used
  """
  def get_exe_suffix(platform):
    if 'Windows' in platform: return '.exe'
    if 'OSX' in platform: return '.app'
    if 'Linux' in platform: return ''
    if 'Android' in platform: return '.apk'
    if 'iOS' in platform: return ''
    raise InternalError("Don't know executable suffix for %s" % platform)

  try:
    unitybuild.utils.destroy(output_dir)
  except Exception as e:
    print('WARN: could not use %s: %s' % (output_dir, e))
    output_dir = make_unused_directory_name(output_dir)
    print('WARN: using %s intead' % output_dir)
    unitybuild.utils.destroy(output_dir)
  os.makedirs(output_dir)
  logfile = os.path.join(output_dir, 'build_log.txt')

  exe_name = os.path.join(output_dir, exe_base_name + get_exe_suffix(platform))
  cmd_env = os.environ.copy()
  cmdline = [get_unity_exe(get_project_unity_version(project_dir),
                           lenient=is_jenkins),
             '-logFile', logfile,
             '-batchmode',
             # '-nographics',   Might be needed on OSX if running w/o window server?
             '-projectPath', project_dir,
             '-executeMethod', 'BuildTiltBrush.CommandLine',
               '-btb-target', PLATFORM_TO_UNITYTARGET[platform],
               '-btb-out', exe_name,
               '-btb-display', vrsdk]
  if experimental:
    cmdline.append('-btb-experimental')

  if il2cpp:
    cmdline.append('-btb-il2cpp')

  # list of tuples:
  # - the name of the credential in the environment (for Jenkins)
  # - the name of the credential in the keystore (for interactive use)
  required_credentials = []

  if for_distribution and platform == 'Android':
    if vrsdk != 'Oculus':
      raise BuildFailed('Signing is currently only implemented for Oculus Quest')
    keystore = os.path.abspath(os.path.join(project_dir, 'Support/Keystores/TiltBrush.keystore'))
    keystore = keystore.replace('/', '\\')
    if not os.path.exists(keystore):
      raise BuildFailed("To sign you need %s.\n" % keystore)

    cmdline.extend([
      '-btb-keystore-name', keystore,
      '-btb-keyalias-name', 'oculusquest',
    ])
    required_credentials.extend([
      ('BTB_KEYSTORE_PASS', 'Tilt Brush keystore password'),
      ('BTB_KEYALIAS_PASS', 'Tilt Brush Oculus Quest signing key password')])
  cmdline.extend(['-btb-stamp', stamp])

  if config == 'Debug':
    cmdline.extend([
      '-btb-bopt', 'Development',
      '-btb-bopt', 'AllowDebugging',
    ])

  cmdline.append('-quit')

  full_version = "%s-%s" % (get_end_user_version(project_dir), stamp)

  # Populate environment with secrets just before calling subprocess
  for (env_var, credential_name) in required_credentials:
    if env_var not in cmd_env:
      if is_jenkins:
        # TODO(pld): Look into Jenkins plugins to get at these credentials
        raise BuildFailed(
          'Credential "%s" is missing from Jenkins build environment' % env_var)
      else:
        from unitybuild.credentials import get_credential
        cmd_env[env_var] = get_credential(credential_name).get_secret().encode('ascii')
  proc = subprocess.Popen(cmdline, stdout=sys.stdout, stderr=sys.stderr, env=cmd_env)
  del cmd_env

  with unitybuild.utils.ensure_terminate(proc):
    with LogTailer(logfile, disabled=is_jenkins):
      with open(os.path.join(output_dir, 'build_stamp.txt'), 'w') as outf:
        outf.write(full_version)

      # Use wait() instead of communicate() because Windows can't
      # interrupt the thread joins that communicate() uses.
      proc.wait()

  with open(logfile) as inf:
    log = inf.read().replace('\r', '')

  check_compile_output(log)

  if proc.returncode != 0:
    analyze_unity_failure(proc.returncode, log)

  # sanity-checking since we've been seeing bad Oculus builds
  if platform == 'Windows':
    required_files = []
    for f in required_files:
      if not os.path.exists(os.path.join(output_dir, f)):
        raise BuildFailed("""Build is missing the file '%s'
This is a known Unity bug and the only thing to do is try the build
over and over again until it works""" % f)
  return output_dir
Ejemplo n.º 3
0
def get_secret(env_var_name, credential_name):
    """Look in environment and in credentials to fetch a secret."""
    # TODO(pld): env-var path currently unused, since we don't push from Jenkins
    if env_var_name in os.environ:
        return os.environ[env_var_name]
    return get_credential(credential_name).get_secret()
Ejemplo n.º 4
0
def build(
    stamp,
    output_dir,
    project_dir,
    exe_base_name,  # pylint: disable=too-many-statements,too-many-branches,too-many-locals,too-many-arguments
    experimental,
    platform,
    il2cpp,
    vrsdk,
    config,
    for_distribution,
    is_jenkins,
):
    """Create a build of Tilt Brush.
    Pass:
      stamp - string describing the version+build; will be embedded into the build somehow.
      output_dir - desired output directory name
      project_dir - directory name
      project_name - name of the executable to create (sans extension)
      experimental - boolean
      platform - one of (Windows, OSX, Linux, Android, iOS)
      il2cpp - boolean
      vrsdk - Config.SdkMode; valid values are (Oculus, SteamVR, Monoscopic)
      config - one of (Debug, Release)
      for_distribution - boolean. Enables android signing, version code bump, removal of pdb files.
      is_jenkins - boolean; used to customize stdout logging
    Returns:
      the actual output directory used
    """

    def get_exe_name(platform, exe_base_name):
        # This is a manually maintained duplicate of App.cs
        if "Windows" in platform:
            return "%s.exe" % exe_base_name
        if "OSX" in platform:
            return "%s.app" % exe_base_name
        if "Linux" in platform:
            return "%s" % exe_base_name
        if "Android" in platform:
            return "com.%s.%s.apk" % (VENDOR_NAME, exe_base_name)
        if "iOS" in platform:
            return "%s" % exe_base_name
        raise InternalError("Don't know executable name for %s" % platform)

    try:
        unitybuild.utils.destroy(output_dir)
    except Exception as e:  # pylint: disable=broad-except
        print("WARN: could not use %s: %s" % (output_dir, e))
        output_dir = make_unused_directory_name(output_dir)
        print("WARN: using %s intead" % output_dir)
        unitybuild.utils.destroy(output_dir)
    os.makedirs(output_dir)
    logfile = os.path.join(output_dir, "build_log.txt")

    exe_name = os.path.join(output_dir, get_exe_name(platform, exe_base_name))
    cmd_env = os.environ.copy()
    cmdline = [
        get_unity_exe(get_project_unity_version(project_dir), lenient=is_jenkins),
        "-logFile",
        logfile,
        "-batchmode",
        # '-nographics',   Might be needed on OSX if running w/o window server?
        "-projectPath",
        project_dir,
        "-executeMethod",
        "BuildTiltBrush.CommandLine",
        "-btb-target",
        PLATFORM_TO_UNITYTARGET[platform],
        "-btb-out",
        exe_name,
        "-btb-display",
        vrsdk,
    ]
    if experimental:
        cmdline.append("-btb-experimental")

    if il2cpp:
        cmdline.append("-btb-il2cpp")

    # list of tuples:
    # - the name of the credential in the environment (for Jenkins)
    # - the name of the credential in the keystore (for interactive use)
    required_credentials = []

    if for_distribution and platform == "Android":
        if vrsdk != "Oculus":
            raise BuildFailed("Signing is currently only implemented for Oculus Quest")
        keystore = os.path.abspath(
            os.path.join(project_dir, "Support/Keystores/TiltBrush.keystore")
        )
        keystore = keystore.replace("/", "\\")
        if not os.path.exists(keystore):
            raise BuildFailed("To sign you need %s.\n" % keystore)

        cmdline.extend(
            [
                "-btb-keystore-name",
                keystore,
                "-btb-keyalias-name",
                "oculusquest",
            ]
        )
        required_credentials.extend(
            [
                ("BTB_KEYSTORE_PASS", "Tilt Brush keystore password"),
                ("BTB_KEYALIAS_PASS", "Tilt Brush Oculus Quest signing key password"),
            ]
        )
    cmdline.extend(["-btb-stamp", stamp])

    if config == "Debug":
        cmdline.extend(
            [
                "-btb-bopt",
                "Development",
                "-btb-bopt",
                "AllowDebugging",
            ]
        )

    cmdline.append("-quit")

    full_version = "%s-%s" % (get_end_user_version(project_dir), stamp)

    # Populate environment with secrets just before calling subprocess
    for (env_var, credential_name) in required_credentials:
        if env_var not in cmd_env:
            if is_jenkins:
                # TODO(pld): Look into Jenkins plugins to get at these credentials
                raise BuildFailed(
                    'Credential "%s" is missing from Jenkins build environment'
                    % env_var
                )
            cmd_env[env_var] = (
                get_credential(credential_name).get_secret().encode("ascii")
            )
    proc = subprocess.Popen(cmdline, stdout=sys.stdout, stderr=sys.stderr, env=cmd_env)
    del cmd_env

    with unitybuild.utils.ensure_terminate(proc):
        with LogTailer(logfile, disabled=is_jenkins):
            with open(os.path.join(output_dir, "build_stamp.txt"), "w") as outf:
                outf.write(full_version)

            # Use wait() instead of communicate() because Windows can't
            # interrupt the thread joins that communicate() uses.
            proc.wait()

    with open(logfile) as inf:
        log = inf.read().replace("\r", "")

    check_compile_output(log)

    if proc.returncode != 0:
        analyze_unity_failure(proc.returncode, log)

    # sanity-checking since we've been seeing bad Oculus builds
    if platform == "Windows":
        required_files = []
        for f in required_files:
            if not os.path.exists(os.path.join(output_dir, f)):
                raise BuildFailed(
                    """Build is missing the file '%s'
This is a known Unity bug and the only thing to do is try the build
over and over again until it works"""
                    % f
                )
    return output_dir