1# -*- coding: utf-8 -*-
2# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Tools for searching/manipulating the manifests repository."""
7
8from __future__ import print_function
9
10__author__ = 'llozano@google.com (Luis Lozano)'
11
12import copy
13import os
14import re
15import shutil
16import tempfile
17import time
18
19from cros_utils import command_executer
20from cros_utils import logger
21
22MANIFEST_VERSION_MAIN_BRANCH = 'master'
23
24
25def IsCrosVersion(version):
26  match = re.search(r'(\d+\.\d+\.\d+\.\d+)', version)
27  return match is not None
28
29
30def IsRFormatCrosVersion(version):
31  match = re.search(r'(R\d+-\d+\.\d+\.\d+)', version)
32  return match is not None
33
34
35def RFormatCrosVersion(version):
36  assert IsCrosVersion(version)
37  tmp_major, tmp_minor = version.split('.', 1)
38  rformat = 'R' + tmp_major + '-' + tmp_minor
39  assert IsRFormatCrosVersion(rformat)
40  return rformat
41
42
43class ManifestVersions(object):
44  """This class handles interactions with the manifests repo."""
45
46  def __init__(self, internal=True):
47    self.internal = internal
48    self.clone_location = tempfile.mkdtemp()
49    self.ce = command_executer.GetCommandExecuter()
50    if internal:
51      versions_git = ('https://chrome-internal.googlesource.com/'
52                      'chromeos/manifest-versions.git')
53    else:
54      versions_git = (
55          'https://chromium.googlesource.com/chromiumos/manifest-versions.git')
56    commands = [
57        'cd {0}'.format(self.clone_location),
58        'git clone {0}'.format(versions_git)
59    ]
60    ret = self.ce.RunCommands(commands)
61    if ret:
62      logger.GetLogger().LogFatal('Failed to clone manifest-versions.')
63
64  def __del__(self):
65    if self.clone_location:
66      shutil.rmtree(self.clone_location)
67
68  def TimeToVersionChromeOS(self, my_time):
69    """Convert timestamp to version number, in ChromeOS/Paladin."""
70    cur_time = time.mktime(time.gmtime())
71    des_time = float(my_time)
72    if cur_time - des_time > 7000000:
73      logger.GetLogger().LogFatal('The time you specify is too early.')
74    commands = [
75        'cd {0}'.format(self.clone_location), 'cd manifest-versions',
76        'git checkout -f $(git rev-list' +
77        ' --max-count=1 --before={0} origin/{1})'.format(
78            my_time, MANIFEST_VERSION_MAIN_BRANCH)
79    ]
80    ret = self.ce.RunCommands(commands)
81    if ret:
82      logger.GetLogger().LogFatal('Failed to checkout manifest at '
83                                  'specified time')
84    path = os.path.realpath('{0}/manifest-versions/LKGM/lkgm.xml'.format(
85        self.clone_location))
86    pp = path.split('/')
87    new_list = copy.deepcopy(pp)
88    for i, e in enumerate(pp):
89      if e == 'android-LKGM-candidates':
90        new_list[i] = 'paladin'
91    chrome_path = '/'.join(new_list)
92    if not os.path.exists(chrome_path):
93      logger.GetLogger().LogOutput('LKGM path is %s' % path)
94      logger.GetLogger().LogOutput('Cannot find path %s' % chrome_path)
95      pieces = os.path.basename(chrome_path).split('.')
96      pieces = pieces[:-2]
97      new_base = '.'.join(pieces) + '*'
98      wild_path = os.path.join('/', '/'.join(new_list[:-1]), new_base)
99      command = 'ls %s' % wild_path
100      ret, out, _ = self.ce.RunCommandWOutput(command)
101      if ret == 0:
102        out = out.strip()
103        files = out.split('\n')
104        latest = files[-1]
105        small = os.path.basename(latest).split('.xml')[0]
106        version = pp[-2] + '.' + small
107    else:
108      small = os.path.basename(path).split('.xml')[0]
109      version = pp[-2] + '.' + small
110    commands = [
111        'cd {0}'.format(self.clone_location), 'cd manifest-versions',
112        'git checkout {0}'.format(MANIFEST_VERSION_MAIN_BRANCH)
113    ]
114    self.ce.RunCommands(commands)
115    return version
116
117  def TimeToVersion(self, my_time):
118    """Convert timestamp to version number."""
119    cur_time = time.mktime(time.gmtime())
120    des_time = float(my_time)
121    if cur_time - des_time > 7000000:
122      logger.GetLogger().LogFatal('The time you specify is too early.')
123    commands = [
124        'cd {0}'.format(self.clone_location), 'cd manifest-versions',
125        'git checkout -f $(git rev-list' +
126        ' --max-count=1 --before={0} origin/{1})'.format(
127            my_time, MANIFEST_VERSION_MAIN_BRANCH)
128    ]
129    ret = self.ce.RunCommands(commands)
130    if ret:
131      logger.GetLogger().LogFatal('Failed to checkout manifest at '
132                                  'specified time')
133    path = os.path.realpath('{0}/manifest-versions/LKGM/lkgm.xml'.format(
134        self.clone_location))
135    pp = path.split('/')
136    small = os.path.basename(path).split('.xml')[0]
137    version = pp[-2] + '.' + small
138    commands = [
139        'cd {0}'.format(self.clone_location), 'cd manifest-versions',
140        'git checkout {0}'.format(MANIFEST_VERSION_MAIN_BRANCH)
141    ]
142    self.ce.RunCommands(commands)
143    return version
144
145  def GetManifest(self, version, to_file):
146    """Get the manifest file from a given chromeos-internal version."""
147    assert not IsRFormatCrosVersion(version)
148    version = version.split('.', 1)[1]
149    os.chdir(self.clone_location)
150    files = [
151        os.path.join(r, f)
152        for r, _, fs in os.walk('.')
153        for f in fs
154        if version in f
155    ]
156    if files:
157      command = 'cp {0} {1}'.format(files[0], to_file)
158      ret = self.ce.RunCommand(command)
159      if ret:
160        raise RuntimeError('Cannot copy manifest to {0}'.format(to_file))
161    else:
162      raise RuntimeError('Version {0} is not available.'.format(version))
163