#!/usr/bin/python3 # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. """Create (or find) the Bugzilla bug for a new Nightly development cycle. If a bug with the expected summary already exists, its ID is reused so that retries don't produce duplicates. Usage: BUGZILLA_API_KEY= python3 create-nightly-bug.py Set --staging to use bugzilla-dev.allizom.org for testing. The bug ID is printed to stdout; status messages go to stderr. """ import json import os import sys import urllib.error import urllib.parse import urllib.request BUGZILLA_PROD_URL = "https://bugzilla.mozilla.org/rest/bug" BUGZILLA_STAGING_URL = "https://bugzilla-dev.allizom.org/rest/bug" PRODUCT = "Firefox for Android" COMPONENT = "General" SUMMARY_TEMPLATE = "Start the nightly {} development cycle" RELEASE_CHECKLIST_URL = ( "https://firefox-source-docs.mozilla.org/mobile/android/fenix/" "release-checklist.html" "#dev-team-starting-the-next-nightly-development-cycle" ) def _bugzilla_request(url, data=None, api_key=None): if api_key: sep = "&" if "?" in url else "?" url = f"{url}{sep}api_key={api_key}" req = urllib.request.Request( url, data=data, headers={"Content-Type": "application/json"} if data else {}, ) try: with urllib.request.urlopen(req) as resp: return json.loads(resp.read()) except urllib.error.HTTPError as e: body = e.read().decode("utf-8", errors="replace") print(f"ERROR: Bugzilla API returned {e.code}: {body}", file=sys.stderr) sys.exit(1) def find_existing_bug(base_url, version, api_key): params = urllib.parse.urlencode({ "product": PRODUCT, "component": COMPONENT, "summary": SUMMARY_TEMPLATE.format(version), }) result = _bugzilla_request(f"{base_url}?{params}", api_key=api_key) expected = SUMMARY_TEMPLATE.format(version) for bug in result.get("bugs", []): if bug["summary"] == expected: return bug["id"] return None def create_bug(base_url, version, api_key): summary = SUMMARY_TEMPLATE.format(version) description = ( "Follow **[[Dev team] Starting the next Nightly development cycle]" f"({RELEASE_CHECKLIST_URL})**" ) payload = json.dumps({ "product": PRODUCT, "component": COMPONENT, "summary": summary, "description": description, "type": "task", "version": "unspecified", "op_sys": "Android", "platform": "All", }).encode("utf-8") result = _bugzilla_request(base_url, data=payload, api_key=api_key) return result["id"] def main(): args = sys.argv[1:] staging = "--staging" in args if staging: args.remove("--staging") if len(args) != 1 or not args[0].isdigit(): print("Usage: create-nightly-bug.py [--staging] VERSION", file=sys.stderr) print("Example: create-nightly-bug.py 150", file=sys.stderr) print(" create-nightly-bug.py --staging 150", file=sys.stderr) print("", file=sys.stderr) print("Requires BUGZILLA_API_KEY environment variable.", file=sys.stderr) sys.exit(1) version = args[0] base_url = BUGZILLA_STAGING_URL if staging else BUGZILLA_PROD_URL api_key = os.environ.get("BUGZILLA_API_KEY") if not api_key: print( "ERROR: BUGZILLA_API_KEY environment variable is not set.", file=sys.stderr, ) sys.exit(1) if staging: print("Using staging instance (bugzilla-dev.allizom.org).", file=sys.stderr) existing = find_existing_bug(base_url, version, api_key) if existing: print( f"Bug {existing} already exists for nightly {version}.", file=sys.stderr, ) print(existing) return bug_id = create_bug(base_url, version, api_key) print(f"Created bug {bug_id} for nightly {version}.", file=sys.stderr) print(bug_id) if __name__ == "__main__": main()