revlist.py (4367B)
1 import argparse 2 import os 3 import time 4 from typing import Any, Iterator, Tuple 5 6 from tools.wpt.testfiles import get_git_cmd 7 8 here = os.path.dirname(__file__) 9 wpt_root = os.path.abspath(os.path.join(here, os.pardir, os.pardir)) 10 11 12 def calculate_cutoff_date(until: int, epoch: int, offset: int) -> int: 13 return (((until - offset) // epoch) * epoch) + offset 14 15 16 def parse_epoch(string: str) -> int: 17 UNIT_DICT = {"h": 3600, "d": 86400, "w": 604800} 18 base = string[:-1] 19 unit = string[-1:] 20 if base.isdigit() and unit in UNIT_DICT: 21 return int(base) * UNIT_DICT[unit] 22 raise argparse.ArgumentTypeError('must be digits followed by h/d/w') 23 24 25 def get_tagged_revisions(pattern: str) -> Iterator[Tuple[str, str, int]]: 26 ''' 27 Iterates the tagged revisions as (tag name, commit sha, committer date) tuples. 28 ''' 29 git = get_git_cmd(wpt_root) 30 args = [ 31 pattern, 32 '--sort=-committerdate', 33 '--format=%(refname:lstrip=2) %(objectname) %(committerdate:raw)', 34 '--count=100000' 35 ] 36 ref_list = git("for-each-ref", *args) # type: ignore 37 for line in ref_list.splitlines(): 38 if not line: 39 continue 40 tag, commit, date, _ = line.split(" ") 41 date = int(date) 42 yield tag, commit, date 43 44 45 def get_epoch_revisions(epoch: int, until: int, max_count: int) -> Iterator[str]: 46 # Set an offset to start to count the weekly epoch from 47 # Monday 00:00:00. This is particularly important for the weekly epoch 48 # because fix the start of the epoch to Monday. This offset is calculated 49 # from Thursday, 1 January 1970 0:00:00 to Monday, 5 January 1970 0:00:00 50 epoch_offset = 345600 51 count = 0 52 53 # Iterates the tagged revisions in descending order finding the more 54 # recent commit still older than a "cutoff_date" value. 55 # When a commit is found "cutoff_date" is set to a new value multiplier of 56 # "epoch" but still below of the date of the current commit found. 57 # This needed to deal with intervals where no candidates were found 58 # for the current "epoch" and the next candidate found is yet below 59 # the lower values of the interval (it is the case of J and I for the 60 # interval between Wed and Tue, in the example). The algorithm fix 61 # the next "cutoff_date" value based on the date value of the current one 62 # skipping the intermediate values. 63 # The loop ends once we reached the required number of revisions to return 64 # or the are no more tagged revisions or the cutoff_date reach zero. 65 # 66 # Fri Sat Sun Mon Tue Wed Thu Fri Sat 67 # | | | | | | | | | 68 # -A---B-C---DEF---G---H--IJ----------K-----L-M----N--O-- 69 # ^ 70 # now 71 # Expected result: N,M,K,J,H,G,F,C,A 72 73 cutoff_date = calculate_cutoff_date(until, epoch, epoch_offset) 74 for _, commit, date in get_tagged_revisions("refs/tags/merge_pr_*"): 75 if count >= max_count: 76 return 77 if date < cutoff_date: 78 yield commit 79 count += 1 80 cutoff_date = calculate_cutoff_date(date, epoch, epoch_offset) 81 82 83 def get_parser() -> argparse.ArgumentParser: 84 parser = argparse.ArgumentParser() 85 parser.add_argument("--epoch", 86 default="1d", 87 type=parse_epoch, 88 help="regular interval of time selected to get the " 89 "tagged revisions. Valid values are digits " 90 "followed by h/d/w (e.x. 9h, 9d, 9w ...) where " 91 "the mimimun selectable interval is one hour " 92 "(1h)") 93 parser.add_argument("--max-count", 94 default=1, 95 type=int, 96 help="maximum number of revisions to be returned by " 97 "the command") 98 return parser 99 100 101 def run_rev_list(**kwargs: Any) -> None: 102 # "epoch_threshold" is a safety margin. After this time it is fine to 103 # assume that any tags are created and pushed. 104 epoch_threshold = 600 105 until = int(time.time()) - epoch_threshold 106 for line in get_epoch_revisions(kwargs["epoch"], until, kwargs["max_count"]): 107 print(line)