in new-contributors.py [0:0]
def main() -> None:
# parse command line arguments
parser = argparse.ArgumentParser()
parser.add_argument(
"version",
type=int,
help="Firefox version",
)
parser.add_argument(
"--api-key",
"--apikey",
required=True,
help="Bugzilla API-Key",
)
args = parser.parse_args()
if args.version < 0:
raise Error(f"Invalid version: {args.version}")
# load cache
# store a list of users against each version that were determined to have patches
# landed _prior_ to the specified version
cache_file = Path(__file__).parent / "new-contributors.cache"
try:
with cache_file.open() as f:
cache = json.load(f)
except (FileNotFoundError, ValueError):
cache = []
current_cache = None
for cache_item in cache:
if cache_item["version"] == args.version:
current_cache = cache_item
break
if not current_cache:
current_cache = {"version": args.version, "skip": []}
cache.append(current_cache)
# find bugs fixed in specified version
print(f"looking for bugs fixed in Firefox {args.version}", file=sys.stderr)
bugs = bmo_request(
"bug",
{
"target_milestone": f"{args.version} Branch",
"status": "RESOLVED",
"product": PRODUCTS,
"include_fields": "id,assigned_to,cf_last_resolved",
"order": "cf_last_resolved",
},
api_key=args.api_key,
)["bugs"]
print(f"found {plural(len(bugs), 'bug')}", file=sys.stderr)
if not bugs:
return
# find new assignees
new = {}
for bug in bugs:
assignee = bug["assigned_to"]
# skip users that are clearly employees or contractors
if assignee.endswith(
(
"@getpocket.com",
"@mozilla.com",
"@mozilla.org",
"@mozillafoundation.org",
"@softvision.com",
"@softvision.ro",
"@softvisioninc.eu",
)
):
continue
# skip users we already know are not new
should_skip = False
for cache_item in cache:
if cache_item["version"] <= args.version and assignee in cache_item["skip"]:
should_skip = True
break
if should_skip:
continue
# handle users that we know are new and fixed more than one bug
if assignee in new:
new[assignee]["bugs"].append(bug["id"])
continue
print(f"checking {assignee}", file=sys.stderr, end="")
# always exclude employees; this is quicker than a bug search, and not
# all bugs have correct metadata
users = bmo_request(
"user",
{"names": assignee},
api_key=args.api_key,
)["users"]
is_employee = False
if users:
for group in users[0]["groups"]:
if group["name"] == "mozilla-employee-confidential":
is_employee = True
break
if is_employee:
print(" employee", file=sys.stderr)
current_cache["skip"].append(assignee)
continue
print(" contributor", file=sys.stderr, end="")
# query for bugs fixed by this user before this one
prior_bugs = bmo_request(
"bug",
{
# resolved bugs in our products
"product": PRODUCTS,
"status": "RESOLVED",
# assigned to our user
"emailassigned_to1": "1",
"emailtype1": "exact",
"email1": assignee,
# where the last resolved is older than this bug's
"f1": "cf_last_resolved",
"o1": "lessthan",
"v1": bug["cf_last_resolved"].replace("T", " ").replace("Z", ""),
# and a target_milestone is set (filter our duplicates, etc)
"f2": "target_milestone",
"o2": "notequals",
"v2": "---",
# don't need the full list or count, just need to know if there's any
"limit": 1,
},
api_key=args.api_key,
)["bugs"]
if prior_bugs:
print(" existing", file=sys.stderr)
current_cache["skip"].append(assignee)
continue
# collate in `new` dict
print(" new", file=sys.stderr)
new.setdefault(
assignee,
{
"name": bug["assigned_to_detail"]["real_name"]
or bug["assigned_to_detail"]["nick"],
"bugs": [],
},
)
new[assignee]["bugs"].append(bug["id"])
print(f"found {plural(len(new), 'new contributor')}", file=sys.stderr)
# update cache
with cache_file.open("w") as f:
json.dump(cache, f, indent=2, sort_keys=True)
# generate nucleus output
print(
f"With the release of Firefox {args.version}, we are pleased to welcome "
"the developers who contributed their first code change to Firefox in "
f"this release, {len(new)} of whom were brand new volunteers! Please "
"join us in thanking each of these diligent and enthusiastic "
"individuals, and take a look at their contributions:\n"
)
for user in sorted(new.values(), key=lambda u: u["name"].lower()):
bug_links = ", ".join(
f'<a href="https://bugzilla.mozilla.org/{b}">{b}</a>'
for b in sorted(user["bugs"])
)
print(f"* {user['name']}: {bug_links}")