forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbuilders.py
147 lines (131 loc) · 5.04 KB
/
builders.py
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
# Copyright 2024 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Utils for interacting with builders & builder props in src."""
import json
import logging
import pathlib
import subprocess
_THIS_DIR = pathlib.Path(__file__).resolve().parent
_SRC_DIR = _THIS_DIR.parents[1]
# TODO(crbug.com/41492688): Support src-internal configs too. When this is done,
# ensure tools/utr/recipe.py is not using the public reclient instance
_BUILDER_PROP_DIRS = _SRC_DIR.joinpath('infra', 'config', 'generated',
'builders')
_INTERNAL_BUILDER_PROP_DIRS = _SRC_DIR.joinpath('internal', 'infra', 'config',
'generated', 'builders')
def find_builder_props(builder_name, bucket_name=None, project_name=None):
"""Finds the checked-in json props file for the builder.
Args:
builder_name: Builder name of the builder
bucket_name: Bucket name of the builder
project_name: Project name of the builder
Returns:
Tuple of (Dict of the builder's input props, LUCI project of the builder).
Both elements will be None if the builder wasn't found.
"""
def _walk_props_dir(props_dir):
matches = []
if not props_dir.exists():
return matches
for bucket_path in props_dir.iterdir():
if not bucket_path.is_dir() or (bucket_name
and bucket_path.name != bucket_name):
continue
for builder_path in bucket_path.iterdir():
if builder_path.name != builder_name:
continue
prop_file = builder_path.joinpath('properties.json')
if not prop_file.exists():
logging.warning(
'Found generated dir for builder at %s, but no prop file?',
builder_path)
continue
matches.append(prop_file)
return matches
possible_matches = []
if not project_name or project_name == 'chrome':
matches = _walk_props_dir(_INTERNAL_BUILDER_PROP_DIRS)
if matches:
project_name = 'chrome'
possible_matches += matches
if not project_name or project_name == 'chromium':
matches = _walk_props_dir(_BUILDER_PROP_DIRS)
if matches:
project_name = 'chromium'
possible_matches += matches
if not possible_matches:
# Try also fetching the props from buildbucket. This will give us needed
# vals like recipe and builder-group name for builders that aren't
# bootstrapped.
if bucket_name and project_name:
logging.info(
'Prop file not found, attempting to fetch props from buildbucket.')
props = fetch_props_from_buildbucket(builder_name, bucket_name,
project_name)
if props:
return props, project_name
logging.error(
'[red]No props found. Are you sure you have the correct project '
'("%s"), bucket ("%s"), and builder name ("%s")?[/]', project_name,
bucket_name, builder_name)
if not _INTERNAL_BUILDER_PROP_DIRS.exists():
logging.warning(
'src-internal not detected in this checkout. Perhaps the builder '
'is a "chrome" one, in which: case make sure to add src-internal to '
"your checkout if a you're a Googler.")
return None, None
if len(possible_matches) > 1:
logging.error(
'[red]Found multiple prop files for builder %s. Pass in a project '
'("-p") and bucket name ("-B").[/]', builder_name)
for m in possible_matches:
logging.error(m)
return None, None
prop_file = possible_matches[0]
logging.debug('Found prop file %s', prop_file)
with open(possible_matches[0]) as f:
props = json.load(f)
return props, project_name
def fetch_props_from_buildbucket(builder_name, bucket_name, project_name):
"""Calls out to buildbucket for the input props for the given builder
Args:
builder_name: Builder name of the builder
bucket_name: Bucket name of the builder
project_name: Project name of the builder
Returns:
Dict of the builder's input props
"""
input_json = {
'id': {
'project': project_name,
'bucket': bucket_name,
'builder': builder_name,
}
}
cmd = [
'luci-auth',
'context',
'--',
'prpc',
'call',
'cr-buildbucket.appspot.com',
'buildbucket.v2.Builders.GetBuilder',
]
logging.debug('Running prpc:')
logging.debug(' '.join(cmd))
p = subprocess.run(cmd,
input=json.dumps(input_json),
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
check=False)
if p.returncode:
logging.warning('Error fetching the build template from buildbucket')
# Use the "basic_logger" here (and below) to avoid rich from coloring random
# bits of the printed error.
logging.getLogger('basic_logger').warning(p.stdout.strip())
return None
builder_info = json.loads(p.stdout)
props_s = builder_info.get('config', {}).get('properties', '{}')
return json.loads(props_s)