Source code

Revision control

Other Tools

1
# This Source Code Form is subject to the terms of the Mozilla Public
2
# License, v. 2.0. If a copy of the MPL was not distributed with this
3
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5
from datetime import datetime, timedelta
6
import os
7
8
from mozboot import util as mb_util
9
from mozlint import result, pathutils
10
from mozpack import path as mozpath
11
import mozversioncontrol.repoupdate
12
13
from compare_locales.lint.linter import L10nLinter
14
from compare_locales.lint.util import l10n_base_reference_and_tests
15
from compare_locales import parser
16
from compare_locales.paths import TOMLParser, ProjectFiles
17
18
19
LOCALE = 'gecko-strings'
20
21
22
PULL_AFTER = timedelta(days=2)
23
24
25
def lint(paths, lintconfig, **lintargs):
26
l10n_base = mb_util.get_state_dir()
27
root = lintargs['root']
28
exclude = lintconfig.get('exclude')
29
extensions = lintconfig.get('extensions')
30
31
# Load l10n.toml configs
32
l10nconfigs = load_configs(lintconfig, root, l10n_base)
33
34
# Check include paths in l10n.yml if it's in our given paths
35
# Only the l10n.yml will show up here, but if the l10n.toml files
36
# change, we also get the l10n.yml as the toml files are listed as
37
# support files.
38
if lintconfig['path'] in paths:
39
results = validate_linter_includes(lintconfig, l10nconfigs, lintargs)
40
paths.remove(lintconfig['path'])
41
else:
42
results = []
43
44
all_files = []
45
for p in paths:
46
fp = pathutils.FilterPath(p)
47
if fp.isdir:
48
for _, fileobj in fp.finder:
49
all_files.append(fileobj.path)
50
if fp.isfile:
51
all_files.append(p)
52
# Filter again, our directories might have picked up files the
53
# explicitly excluded in the l10n.yml configuration.
54
# `browser/locales/en-US/firefox-l10n.js` is a good example.
55
all_files, _ = pathutils.filterpaths(
56
lintargs['root'], all_files, lintconfig['include'],
57
exclude=exclude, extensions=extensions
58
)
59
# These should be excluded in l10n.yml
60
skips = {p for p in all_files if not parser.hasParser(p)}
61
results.extend(
62
result.from_config(
63
lintconfig,
64
level='warning',
65
path=path,
66
message="file format not supported in compare-locales"
67
)
68
for path in skips
69
)
70
all_files = [p for p in all_files if p not in skips]
71
files = ProjectFiles(LOCALE, l10nconfigs)
72
73
get_reference_and_tests = l10n_base_reference_and_tests(files)
74
75
linter = MozL10nLinter(lintconfig)
76
results += linter.lint(all_files, get_reference_and_tests)
77
return results
78
79
80
def gecko_strings_setup(**lint_args):
81
gs = mozpath.join(mb_util.get_state_dir(), LOCALE)
82
marker = mozpath.join(gs, '.hg', 'l10n_pull_marker')
83
try:
84
last_pull = datetime.fromtimestamp(os.stat(marker).st_mtime)
85
skip_clone = datetime.now() < last_pull + PULL_AFTER
86
except OSError:
87
skip_clone = False
88
if skip_clone:
89
return
90
hg = mozversioncontrol.get_tool_path('hg')
91
mozversioncontrol.repoupdate.update_mercurial_repo(
92
hg,
94
gs
95
)
96
with open(marker, 'w') as fh:
97
fh.flush()
98
99
100
def load_configs(lintconfig, root, l10n_base):
101
'''Load l10n configuration files specified in the linter configuration.'''
102
configs = []
103
env = {
104
'l10n_base': l10n_base
105
}
106
for toml in lintconfig['l10n_configs']:
107
cfg = TOMLParser().parse(
108
mozpath.join(root, toml),
109
env=env,
110
ignore_missing_includes=True
111
)
112
cfg.set_locales([LOCALE], deep=True)
113
configs.append(cfg)
114
return configs
115
116
117
def validate_linter_includes(lintconfig, l10nconfigs, lintargs):
118
'''Check l10n.yml config against l10n.toml configs.'''
119
reference_paths = set(
120
mozpath.relpath(p['reference'].prefix, lintargs['root'])
121
for project in l10nconfigs
122
for config in project.configs
123
for p in config.paths
124
)
125
# Just check for directories
126
reference_dirs = sorted(p for p in reference_paths if os.path.isdir(p))
127
missing_in_yml = [
128
refd for refd in reference_dirs if refd not in lintconfig['include']
129
]
130
# These might be subdirectories in the config, though
131
missing_in_yml = [
132
d for d in missing_in_yml
133
if not any(d.startswith(parent + '/') for parent in lintconfig['include'])
134
]
135
if missing_in_yml:
136
dirs = ', '.join(missing_in_yml)
137
return [result.from_config(
138
lintconfig, path=lintconfig['path'],
139
message='l10n.yml out of sync with l10n.toml, add: ' + dirs
140
)]
141
return []
142
143
144
class MozL10nLinter(L10nLinter):
145
'''Subclass linter to generate the right result type.'''
146
def __init__(self, lintconfig):
147
super(MozL10nLinter, self).__init__()
148
self.lintconfig = lintconfig
149
150
def lint(self, files, get_reference_and_tests):
151
return [
152
result.from_config(self.lintconfig, **result_data)
153
for result_data in super(MozL10nLinter, self).lint(
154
files, get_reference_and_tests
155
)
156
]