2018-11-28 19:58:44 -05:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
|
|
import json
|
|
|
|
import os
|
|
|
|
import subprocess
|
|
|
|
import sys
|
|
|
|
import sh
|
|
|
|
import base64
|
|
|
|
from datetime import date
|
|
|
|
from sh.contrib import git
|
|
|
|
|
|
|
|
sys.path.append("adabot")
|
|
|
|
import adabot.github_requests as github
|
|
|
|
|
2019-01-14 16:30:09 -05:00
|
|
|
SUPPORTED_PORTS = ["nrf", "atmel-samd"]
|
2018-11-28 19:58:44 -05:00
|
|
|
|
|
|
|
BIN = ('bin',)
|
|
|
|
UF2 = ('uf2',)
|
|
|
|
BIN_UF2 = ('bin', 'uf2')
|
|
|
|
HEX = ('hex',)
|
|
|
|
|
|
|
|
# Default extensions
|
|
|
|
extension_by_port = {
|
2018-12-11 14:51:07 -05:00
|
|
|
"nrf": UF2,
|
2018-11-28 19:58:44 -05:00
|
|
|
"atmel-samd": UF2,
|
|
|
|
}
|
|
|
|
|
|
|
|
# Per board overrides
|
|
|
|
extension_by_board = {
|
2018-12-11 14:51:07 -05:00
|
|
|
# samd
|
2018-11-30 13:58:27 -05:00
|
|
|
"arduino_mkr1300": BIN,
|
2018-11-28 19:58:44 -05:00
|
|
|
"arduino_zero": BIN,
|
|
|
|
"feather_m0_adalogger": BIN_UF2,
|
|
|
|
"feather_m0_basic": BIN_UF2,
|
2019-03-05 13:11:10 -05:00
|
|
|
"feather_m0_rfm69": BIN_UF2,
|
|
|
|
"feather_m0_rfm9x": BIN_UF2,
|
2018-12-11 14:51:07 -05:00
|
|
|
|
|
|
|
# nRF52840 dev kits that may not have UF2 bootloaders,
|
2018-12-07 20:37:25 -05:00
|
|
|
"makerdiary_nrf52840_mdk": HEX,
|
2019-01-15 04:37:22 -05:00
|
|
|
"makerdiary_nrf52840_mdk_usb_dongle": HEX,
|
2018-12-11 14:51:07 -05:00
|
|
|
"pca10056": BIN_UF2,
|
|
|
|
"pca10059": BIN_UF2
|
2018-11-28 19:58:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
def get_languages():
|
|
|
|
languages = []
|
|
|
|
for f in os.scandir("../locale"):
|
|
|
|
if f.name.endswith(".po"):
|
|
|
|
languages.append(f.name[:-3])
|
|
|
|
return languages
|
|
|
|
|
|
|
|
def get_board_mapping():
|
|
|
|
boards = {}
|
|
|
|
for port in SUPPORTED_PORTS:
|
|
|
|
board_path = os.path.join("../ports", port, "boards")
|
|
|
|
for board_path in os.scandir(board_path):
|
|
|
|
if board_path.is_dir():
|
|
|
|
board_files = os.listdir(board_path.path)
|
|
|
|
board_id = board_path.name
|
|
|
|
extensions = extension_by_port[port]
|
|
|
|
extensions = extension_by_board.get(board_path.name, extensions)
|
|
|
|
boards[board_id] = {"port": port, "extensions": extensions, "download_count": 0}
|
|
|
|
return boards
|
|
|
|
|
|
|
|
def get_version_info():
|
|
|
|
version = None
|
|
|
|
sha = git("rev-parse", "--short", "HEAD").stdout.decode("utf-8")
|
|
|
|
try:
|
2018-12-10 13:13:09 -05:00
|
|
|
version = git("describe", "--tags", "--exact-match").stdout.decode("utf-8").strip()
|
2018-11-28 19:58:44 -05:00
|
|
|
except sh.ErrorReturnCode_128:
|
|
|
|
# No exact match
|
|
|
|
pass
|
|
|
|
|
|
|
|
if "TRAVIS" in os.environ and os.environ["TRAVIS"] == "true":
|
|
|
|
sha = os.environ["TRAVIS_COMMIT"]
|
|
|
|
if os.environ["TRAVIS_PULL_REQUEST"] != "false":
|
|
|
|
sha = os.environ["TRAVIS_PULL_REQUEST_SHA"]
|
|
|
|
|
|
|
|
if not version:
|
|
|
|
version="{}-{}".format(date.today().strftime("%Y%m%d"), sha[:7])
|
|
|
|
|
|
|
|
return sha, version
|
|
|
|
|
|
|
|
def get_current_info():
|
|
|
|
response = github.get("/repos/adafruit/circuitpython-org/git/refs/heads/master")
|
|
|
|
if not response.ok:
|
|
|
|
print(response.text)
|
|
|
|
raise RuntimeError("cannot get master sha")
|
|
|
|
commit_sha = response.json()["object"]["sha"]
|
|
|
|
|
|
|
|
response = github.get("/repos/adafruit/circuitpython-org/contents/_data/files.json?ref=" + commit_sha)
|
|
|
|
if not response.ok:
|
|
|
|
print(response.text)
|
|
|
|
raise RuntimeError("cannot get previous files.json")
|
|
|
|
|
|
|
|
response = response.json()
|
|
|
|
|
|
|
|
git_info = commit_sha, response["sha"]
|
2019-03-11 20:07:15 -04:00
|
|
|
current_list = json.loads(base64.b64decode(response["content"]).decode("utf-8"))
|
|
|
|
current_info = {}
|
|
|
|
for info in current_list:
|
|
|
|
current_info[info["id"]] = info
|
|
|
|
return git_info, current_info
|
2018-11-28 19:58:44 -05:00
|
|
|
|
|
|
|
def create_pr(changes, updated, git_info):
|
|
|
|
commit_sha, original_blob_sha = git_info
|
|
|
|
branch_name = "new_release_" + changes["new_release"]
|
|
|
|
|
2019-03-11 20:07:15 -04:00
|
|
|
# Convert the dictionary to a list of boards. Liquid templates only handle arrays.
|
|
|
|
updated_list = []
|
|
|
|
all_ids = sorted(updated.keys())
|
|
|
|
for id in all_ids:
|
|
|
|
info = updated[id]
|
|
|
|
info["id"] = id
|
|
|
|
updated_list.append(info)
|
|
|
|
|
|
|
|
updated = json.dumps(updated_list, sort_keys=True, indent=4).encode("utf-8") + b"\n"
|
2018-11-28 19:58:44 -05:00
|
|
|
print(updated.decode("utf-8"))
|
|
|
|
pr_title = "Automated website update for release {}".format(changes["new_release"])
|
|
|
|
boards = ""
|
|
|
|
if changes["new_boards"]:
|
|
|
|
boards = "New boards:\n* " + "\n* ".join(changes["new_boards"])
|
|
|
|
languages = ""
|
|
|
|
if changes["new_languages"]:
|
|
|
|
languages = "New languages:\n* " + "\n* ".join(changes["new_languages"])
|
|
|
|
message = "Automated website update for release {} by AdaBot.\n\n{}\n\n{}\n".format(
|
|
|
|
changes["new_release"],
|
|
|
|
boards,
|
|
|
|
languages
|
|
|
|
)
|
|
|
|
|
|
|
|
create_branch = {
|
|
|
|
"ref": "refs/heads/" + branch_name,
|
|
|
|
"sha": commit_sha
|
|
|
|
}
|
|
|
|
response = github.post("/repos/adafruit-adabot/circuitpython-org/git/refs", json=create_branch)
|
|
|
|
if not response.ok and response.json()["message"] != "Reference already exists":
|
|
|
|
print("unable to create branch")
|
|
|
|
print(response.text)
|
|
|
|
return
|
|
|
|
|
|
|
|
update_file = {
|
|
|
|
"message": message,
|
|
|
|
"content": base64.b64encode(updated).decode("utf-8"),
|
|
|
|
"sha": original_blob_sha,
|
|
|
|
"branch": branch_name
|
|
|
|
}
|
|
|
|
|
|
|
|
response = github.put("/repos/adafruit-adabot/circuitpython-org/contents/_data/files.json", json=update_file)
|
|
|
|
if not response.ok:
|
|
|
|
print("unable to post new file")
|
|
|
|
print(response.text)
|
|
|
|
return
|
|
|
|
pr_info = {
|
|
|
|
"title": pr_title,
|
|
|
|
"head": "adafruit-adabot:" + branch_name,
|
|
|
|
"base": "master",
|
|
|
|
"body": message,
|
|
|
|
"maintainer_can_modify": True
|
|
|
|
}
|
|
|
|
response = github.post("/repos/adafruit/circuitpython-org/pulls", json=pr_info)
|
|
|
|
if not response.ok:
|
|
|
|
print("unable to create pr")
|
|
|
|
print(response.text)
|
|
|
|
return
|
|
|
|
print(changes)
|
|
|
|
print(pr_info)
|
|
|
|
|
|
|
|
def update_downloads(boards, release):
|
|
|
|
response = github.get("/repos/adafruit/circuitpython/releases/tags/{}".format(release))
|
|
|
|
if not response.ok:
|
|
|
|
print(response.text)
|
|
|
|
raise RuntimeError("cannot get previous release info")
|
|
|
|
|
|
|
|
assets = response.json()["assets"]
|
|
|
|
for asset in assets:
|
2019-01-24 13:22:14 -05:00
|
|
|
board_name = asset["name"].split("-")[2]
|
|
|
|
if board_name not in boards:
|
|
|
|
continue
|
|
|
|
boards[board_name]["download_count"] += asset["download_count"]
|
2018-11-28 19:58:44 -05:00
|
|
|
|
|
|
|
|
|
|
|
def print_active_user():
|
|
|
|
response = github.get("/user")
|
|
|
|
if response.ok:
|
|
|
|
print("Logged in as {}".format(response.json()["login"]))
|
|
|
|
else:
|
|
|
|
print("Not logged in")
|
|
|
|
|
|
|
|
def generate_download_info():
|
|
|
|
boards = {}
|
|
|
|
errors = []
|
|
|
|
|
2018-11-30 15:59:05 -05:00
|
|
|
new_tag = os.environ["TRAVIS_TAG"]
|
2018-11-28 19:58:44 -05:00
|
|
|
|
|
|
|
changes = {
|
|
|
|
"new_release": new_tag,
|
|
|
|
"new_boards": [],
|
|
|
|
"new_languages": []
|
|
|
|
}
|
|
|
|
|
|
|
|
print_active_user()
|
|
|
|
|
|
|
|
sha, this_version = get_version_info()
|
|
|
|
|
|
|
|
git_info, current_info = get_current_info()
|
|
|
|
|
|
|
|
languages = get_languages()
|
|
|
|
|
|
|
|
new_stable = "-" not in new_tag
|
|
|
|
|
|
|
|
previous_releases = set()
|
|
|
|
previous_languages = set()
|
|
|
|
|
|
|
|
# Delete the release we are replacing
|
|
|
|
for board in current_info:
|
|
|
|
info = current_info[board]
|
|
|
|
for version in info["versions"]:
|
|
|
|
previous_releases.add(version["version"])
|
|
|
|
previous_languages.update(version["files"].keys())
|
|
|
|
if version["stable"] == new_stable:
|
|
|
|
info["versions"].remove(version)
|
|
|
|
|
|
|
|
board_mapping = get_board_mapping()
|
|
|
|
|
|
|
|
print(previous_releases)
|
|
|
|
for release in previous_releases:
|
|
|
|
update_downloads(board_mapping, release)
|
|
|
|
|
|
|
|
for port in SUPPORTED_PORTS:
|
|
|
|
board_path = os.path.join("../ports", port, "boards")
|
|
|
|
for board_path in os.scandir(board_path):
|
|
|
|
if board_path.is_dir():
|
2018-12-10 16:17:05 -05:00
|
|
|
board_files = os.listdir(board_path.path)
|
2018-11-28 19:58:44 -05:00
|
|
|
board_id = board_path.name
|
|
|
|
board_info = board_mapping[board_id]
|
|
|
|
|
|
|
|
if board_id not in current_info:
|
|
|
|
changes["new_boards"].append(board_id)
|
|
|
|
current_info[board_id] = {"downloads": 0,
|
|
|
|
"versions": []}
|
|
|
|
|
|
|
|
new_version = {
|
|
|
|
"stable": new_stable,
|
|
|
|
"version": new_tag,
|
|
|
|
"files": {}
|
|
|
|
}
|
|
|
|
for language in languages:
|
|
|
|
files = []
|
|
|
|
new_version["files"][language] = files
|
|
|
|
for extension in board_info["extensions"]:
|
|
|
|
files.append("https://github.com/adafruit/circuitpython/releases/download/{tag}/adafruit-circuitpython-{board}-{language}-{tag}.{extension}".format(tag=new_tag, board=board_id, language=language, extension=extension))
|
|
|
|
current_info[board_id]["downloads"] = board_info["download_count"]
|
|
|
|
current_info[board_id]["versions"].append(new_version)
|
|
|
|
|
|
|
|
changes["new_languages"] = set(languages) - previous_languages
|
|
|
|
|
2018-12-10 16:17:05 -05:00
|
|
|
if changes["new_release"]:
|
2018-11-28 19:58:44 -05:00
|
|
|
create_pr(changes, current_info, git_info)
|
2018-12-10 16:17:05 -05:00
|
|
|
else:
|
|
|
|
print("No new release to update")
|
2018-11-28 19:58:44 -05:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
2018-11-30 15:59:05 -05:00
|
|
|
if "TRAVIS_TAG" in os.environ and os.environ["TRAVIS_TAG"]:
|
2018-11-28 19:58:44 -05:00
|
|
|
generate_download_info()
|
|
|
|
else:
|
|
|
|
print("skipping website update because this isn't a tag")
|