summaryrefslogtreecommitdiffstats
path: root/chrome/common/extensions/PRESUBMIT.py
blob: d5c19898aa5dd4eeb4e807751e65c8b3a4c06e86 (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
# Copyright (c) 2012 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.

"""Presubmit script for changes affecting extensions.

See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details about the presubmit API built into gcl.
"""
import fnmatch
import os
import re

EXTENSIONS_PATH = os.path.join('chrome', 'common', 'extensions')
DOCS_PATH = os.path.join(EXTENSIONS_PATH, 'docs')
SERVER2_PATH = os.path.join(DOCS_PATH, 'server2')
API_PATH = os.path.join(EXTENSIONS_PATH, 'api')
TEMPLATES_PATH = os.path.join(DOCS_PATH, 'templates')
PRIVATE_TEMPLATES_PATH = os.path.join(TEMPLATES_PATH, 'private')
PUBLIC_TEMPLATES_PATH = os.path.join(TEMPLATES_PATH, 'public')
INTROS_PATH = os.path.join(TEMPLATES_PATH, 'intros')
ARTICLES_PATH = os.path.join(TEMPLATES_PATH, 'articles')

LOCAL_PUBLIC_TEMPLATES_PATH = os.path.join('docs',
                                           'templates',
                                           'public')

def _ReadFile(filename):
  with open(filename) as f:
    return f.read()

def _ListFilesInPublic():
  all_files = []
  for path, dirs, files in os.walk(LOCAL_PUBLIC_TEMPLATES_PATH):
    all_files.extend(
        os.path.join(path, filename)[len(LOCAL_PUBLIC_TEMPLATES_PATH + os.sep):]
        for filename in files)
  return all_files

def _UnixName(name):
  name = os.path.splitext(name)[0]
  s1 = re.sub('([a-z])([A-Z])', r'\1_\2', name)
  s2 = re.sub('([A-Z]+)([A-Z][a-z])', r'\1_\2', s1)
  return s2.replace('.', '_').lower()

def _FindMatchingTemplates(template_name, template_path_list):
  matches = []
  unix_name = _UnixName(template_name)
  for template in template_path_list:
    if unix_name == _UnixName(template.split(os.sep)[-1]):
      matches.append(template)
  return matches

def _SanitizeAPIName(name, api_path):
  if not api_path.endswith(os.sep):
    api_path += os.sep
  filename = os.path.splitext(name)[0][len(api_path):].replace(os.sep, '_')
  if 'experimental' in filename:
    filename = 'experimental_' + filename.replace('experimental_', '')
  return filename

def _CreateIntegrationTestArgs(affected_files):
  if (any(fnmatch.fnmatch(name, '%s*.py' % SERVER2_PATH)
         for name in affected_files) or
      any(fnmatch.fnmatch(name, '%s*' % PRIVATE_TEMPLATES_PATH)
          for name in affected_files)):
    return ['-a']
  args = []
  for name in affected_files:
    if (fnmatch.fnmatch(name, '%s*' % PUBLIC_TEMPLATES_PATH) or
        fnmatch.fnmatch(name, '%s*' % INTROS_PATH) or
        fnmatch.fnmatch(name, '%s*' % ARTICLES_PATH)):
      args.extend(_FindMatchingTemplates(name.split(os.sep)[-1],
                                         _ListFilesInPublic()))
    if fnmatch.fnmatch(name, '%s*' % API_PATH):
      args.extend(_FindMatchingTemplates(_SanitizeAPIName(name, API_PATH),
                                         _ListFilesInPublic()))
  return args

def _CheckHeadingIDs(input_api):
  ids_re = re.compile('<h[23].*id=.*?>')
  headings_re = re.compile('<h[23].*?>')
  bad_files = []
  for name in input_api.AbsoluteLocalPaths():
    if not os.path.exists(name):
      continue
    if (fnmatch.fnmatch(name, '*%s*' % INTROS_PATH) or
        fnmatch.fnmatch(name, '*%s*' % ARTICLES_PATH)):
      contents = input_api.ReadFile(name)
      if (len(re.findall(headings_re, contents)) !=
          len(re.findall(ids_re, contents))):
        bad_files.append(name)
  return bad_files

def _CheckVersions(input_api, output_api, results):
  version = '_VERSION ='
  for affected_file in input_api.AffectedFiles():
    local_path = affected_file.LocalPath()
    if not fnmatch.fnmatch(local_path, '%s*' % SERVER2_PATH):
      continue
    if local_path.endswith('PRESUBMIT.py'):
      continue
    if any(version in line for line in affected_file.NewContents()):
      found = False
      for _, text in affected_file.ChangedContents():
        if version in text:
          found = True
          break
      if not found:
        results.append(output_api.PresubmitPromptWarning(
            '_VERSION of %s needs to be incremented.' % affected_file))

def _CheckLinks(input_api, output_api, results):
  for affected_file in input_api.AffectedFiles():
    name = affected_file.LocalPath()
    absolute_path = affected_file.AbsoluteLocalPath()
    if not os.path.exists(absolute_path):
      continue
    if (fnmatch.fnmatch(name, '%s*' % PUBLIC_TEMPLATES_PATH) or
        fnmatch.fnmatch(name, '%s*' % INTROS_PATH) or
        fnmatch.fnmatch(name, '%s*' % ARTICLES_PATH) or
        fnmatch.fnmatch(name, '%s*' % API_PATH)):
      contents = _ReadFile(absolute_path)
      args = []
      if input_api.platform == 'win32':
        args = [input_api.python_executable]
      args.extend([os.path.join('docs', 'server2', 'link_converter.py'),
                   '-o',
                   '-f',
                   absolute_path])
      output = input_api.subprocess.check_output(
          args,
          cwd=input_api.PresubmitLocalPath(),
          universal_newlines=True)
      if output != contents:
        changes = ''
        for i, (line1, line2) in enumerate(
            zip(contents.split('\n'), output.split('\n'))):
          if line1 != line2:
            changes = ('%s\nLine %d:\n-%s\n+%s\n' %
                (changes, i + 1, line1, line2))
        if changes:
          results.append(output_api.PresubmitPromptWarning(
              'File %s may have an old-style <a> link to an API page. Please '
              'run docs/server2/link_converter.py to convert the link[s], or '
              'convert them manually.\n\nSuggested changes are: %s' %
              (name, changes)))

def _CheckChange(input_api, output_api):
  results = [
      output_api.PresubmitError('File %s needs an id for each heading.' % name)
      for name in _CheckHeadingIDs(input_api)]
  try:
    integration_test = []
    # From depot_tools/presubmit_canned_checks.py:529
    if input_api.platform == 'win32':
      integration_test = [input_api.python_executable]
    integration_test.append(
        os.path.join('docs', 'server2', 'integration_test.py'))
    integration_test.extend(_CreateIntegrationTestArgs(input_api.LocalPaths()))
    input_api.subprocess.check_call(integration_test,
                                    cwd=input_api.PresubmitLocalPath())
  except input_api.subprocess.CalledProcessError:
    results.append(output_api.PresubmitError('IntegrationTest failed!'))
  _CheckVersions(input_api, output_api, results)
  _CheckLinks(input_api, output_api, results)
  return results

def CheckChangeOnUpload(input_api, output_api):
  return _CheckChange(input_api, output_api)

def CheckChangeOnCommit(input_api, output_api):
  return _CheckChange(input_api, output_api)