OLD | NEW |
1 # Copyright 2017 The LUCI Authors. All rights reserved. | 1 # Copyright 2017 The LUCI Authors. All rights reserved. |
2 # Use of this source code is governed under the Apache License, Version 2.0 | 2 # Use of this source code is governed under the Apache License, Version 2.0 |
3 # that can be found in the LICENSE file. | 3 # that can be found in the LICENSE file. |
4 | 4 |
5 import sys | 5 import sys |
6 import time | 6 import time |
7 | 7 |
8 from recipe_engine import package | 8 from recipe_engine import package |
9 | 9 |
10 from .commit_list import CommitList | 10 from .commit_list import CommitList |
(...skipping 13 matching lines...) Expand all Loading... |
24 """ | 24 """ |
25 return { | 25 return { |
26 project_id: CommitList.from_repo_spec(repo_spec) | 26 project_id: CommitList.from_repo_spec(repo_spec) |
27 for project_id, repo_spec in deps.iteritems() | 27 for project_id, repo_spec in deps.iteritems() |
28 } | 28 } |
29 | 29 |
30 | 30 |
31 def find_best_rev(repos): | 31 def find_best_rev(repos): |
32 """Returns the project_id of the best repo to roll. | 32 """Returns the project_id of the best repo to roll. |
33 | 33 |
34 "Best" is determined by "moves the least amount of commits, globally". There | 34 "Best" is determined by "is an interesting commit and moves the least amount |
35 are two ways that rolling a repo can move commits: | 35 of commits, globally". |
| 36 |
| 37 "Interesting" means "the commit modifies one or more recipe related files", |
| 38 and is defined by CommitMetadata.roll_candidate. |
| 39 |
| 40 There are two ways that rolling a repo can move commits: |
36 | 41 |
37 1) As dependencies. Rolling repo A that depends on (B, C) will take | 42 1) As dependencies. Rolling repo A that depends on (B, C) will take |
38 a penalty for each commit that B and C need to move in order to be | 43 a penalty for each commit that B and C need to move in order to be |
39 compatible with the new A revision. | 44 compatible with the new A revision. |
40 | 45 |
41 2) As dependees. Rolling repo A which is depended on by (B, C) will take | 46 2) As dependees. Rolling repo A which is depended on by (B, C) will take |
42 a penalty for each commit that B and C need to move in order to be | 47 a penalty for each commit that B and C need to move in order to be |
43 compatible with the new A revision. | 48 compatible with the new A revision. |
44 | 49 |
45 Each repo is analyzed with these two rules and a score is computed. The score | 50 Each repo is analyzed with these two rules and a score is computed. The score |
(...skipping 24 matching lines...) Expand all Loading... |
70 Returns (str) - The project_id of the repo to advance next. | 75 Returns (str) - The project_id of the repo to advance next. |
71 """ | 76 """ |
72 # The project_ids of all the repos that can move | 77 # The project_ids of all the repos that can move |
73 repo_set = set(repos) | 78 repo_set = set(repos) |
74 | 79 |
75 best_project_id = None | 80 best_project_id = None |
76 best_score = () # (# commits moved, timestamp) | 81 best_score = () # (# commits moved, timestamp) |
77 | 82 |
78 for project_id, clist in repos.iteritems(): | 83 for project_id, clist in repos.iteritems(): |
79 assert isinstance(clist, CommitList) | 84 assert isinstance(clist, CommitList) |
80 candidate = clist.next | 85 candidate, movement_score = clist.next_roll_candidate |
81 if not candidate: | 86 if not candidate: |
82 continue | 87 continue |
83 | 88 |
84 unaccounted_repos = set(repo_set) | 89 unaccounted_repos = set(repo_set) |
85 | 90 |
86 movement_score = 0 | |
87 # first, determine if rolling this repo will force other repos to move. | 91 # first, determine if rolling this repo will force other repos to move. |
88 for d_pid, dep in candidate.spec.deps.iteritems(): | 92 for d_pid, dep in candidate.spec.deps.iteritems(): |
89 unaccounted_repos.discard(d_pid) | 93 unaccounted_repos.discard(d_pid) |
90 movement_score += repos[d_pid].dist_to(dep.revision) | 94 movement_score += repos[d_pid].dist_to(dep.revision) |
91 | 95 |
92 # Next, see if any unaccounted_repos depend on this repo. | 96 # Next, see if any unaccounted_repos depend on this repo. |
93 for pid in unaccounted_repos: | 97 for pid in unaccounted_repos: |
94 movement_score += repos[pid].dist_compatible_with(pid, candidate.revision) | 98 movement_score += repos[pid].dist_compatible_with(pid, candidate.revision) |
95 | 99 |
96 score = (movement_score, candidate.commit_timestamp) | 100 score = (movement_score, candidate.commit_timestamp) |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
197 | 201 |
198 # not on py3 so we can't use print(..., flush=True) :( | 202 # not on py3 so we can't use print(..., flush=True) :( |
199 sys.stdout.write('finding roll candidates... ') | 203 sys.stdout.write('finding roll candidates... ') |
200 sys.stdout.flush() | 204 sys.stdout.flush() |
201 | 205 |
202 repos = get_commitlists(package_spec.deps) | 206 repos = get_commitlists(package_spec.deps) |
203 ret_good, ret_bad = _get_roll_candidates_impl(context, package_spec, repos) | 207 ret_good, ret_bad = _get_roll_candidates_impl(context, package_spec, repos) |
204 | 208 |
205 print('found %d/%d good/bad candidates in %0.2f seconds' % ( | 209 print('found %d/%d good/bad candidates in %0.2f seconds' % ( |
206 len(ret_good), len(ret_bad), time.time()-start)) | 210 len(ret_good), len(ret_bad), time.time()-start)) |
| 211 sys.stdout.flush() |
207 return ret_good, ret_bad, repos | 212 return ret_good, ret_bad, repos |
OLD | NEW |