salt/tools/gh.py

190 lines
6 KiB
Python

"""
These commands are used to interact and make changes to GitHub.
"""
# pylint: disable=resource-leakage,broad-except,3rd-party-module-not-gated
from __future__ import annotations
import logging
from ptscripts import Context, command_group
import tools.utils
import tools.utils.gh
log = logging.getLogger(__name__)
WORKFLOWS = tools.utils.REPO_ROOT / ".github" / "workflows"
TEMPLATES = WORKFLOWS / "templates"
# Define the command group
cgroup = command_group(
name="gh",
help="GitHub Related Commands",
description=__doc__,
)
@cgroup.command(
name="sync-os-labels",
arguments={
"repository": {
"help": "Github repository.",
},
},
)
def sync_os_labels(
ctx: Context, repository: str = "saltstack/salt", color: str = "C2E0C6"
):
"""
Synchronize the GitHub labels to the OS known to be tested.
"""
description_prefix = "Run Tests Against"
known_os = {
"test:os:all": {
"name": "test:os:all",
"color": color,
"description": f"{description_prefix} ALL OS'es",
},
"test:os:macos-12": {
"name": "test:os:macos-12",
"color": color,
"description": f"{description_prefix} MacOS 12",
},
"test:os:macos-13": {
"name": "test:os:macos-13",
"color": color,
"description": f"{description_prefix} MacOS 13",
},
"test:os:macos-13-arm64": {
"name": "test:os:macos-13-arm64",
"color": color,
"description": f"{description_prefix} MacOS 13 Arm64",
},
}
for slug, details in tools.utils.get_golden_images().items():
name = f"test:os:{slug}"
ami_description = (
details["ami_description"]
.replace("CI Image of ", "")
.replace("arm64", "Arm64")
)
known_os[name] = {
"name": name,
"color": color,
"description": f"{description_prefix} {ami_description}",
}
ctx.info(known_os)
github_token = tools.utils.gh.get_github_token(ctx)
if github_token is None:
ctx.error("Querying labels requires being authenticated to GitHub.")
ctx.info(
"Either set 'GITHUB_TOKEN' to a valid token, or configure the 'gh' tool such that "
"'gh auth token' returns a token."
)
ctx.exit(1)
existing_labels = set()
labels_to_update = []
labels_to_delete = set()
shared_context = tools.utils.get_cicd_shared_context()
for slug in shared_context["mandatory_os_slugs"]:
label = f"test:os:{slug}"
labels_to_delete.add(label)
headers = {
"Accept": "application/vnd.github+json",
"Authorization": f"Bearer {github_token}",
"X-GitHub-Api-Version": "2022-11-28",
}
with ctx.web as web:
web.headers.update(headers)
page = 0
while True:
page += 1
params = {
"per_page": 100,
"page": page,
}
ret = web.get(
f"https://api.github.com/repos/{repository}/labels",
params=params,
)
if ret.status_code != 200:
ctx.error(
f"Failed to get the labels for repository {repository!r}: {ret.reason}"
)
ctx.exit(1)
data = ret.json()
if not data:
break
for details in data:
label = details["name"]
if not label.startswith("test:os:"):
continue
existing_labels.add(label)
if label not in known_os:
labels_to_delete.add(details["name"])
continue
if label in known_os:
update_details = known_os.pop(label)
if label in labels_to_delete:
continue
for key, value in update_details.items():
if details[key] != value:
labels_to_update.append(update_details)
break
continue
for slug in shared_context["mandatory_os_slugs"]:
label = f"test:os:{slug}"
if label in known_os:
labels_to_delete.add(label)
known_os.pop(label)
if label in labels_to_update:
labels_to_delete.add(label)
known_os.pop(label)
for label in labels_to_delete:
if label not in existing_labels:
continue
ctx.info(f"Deleting label '{label}' ...")
ret = web.delete(
f"https://api.github.com/repos/{repository}/labels/{label}",
)
if ret.status_code != 204:
ctx.error(
f"Failed to delete label '{label}' for repository {repository!r}: {ret.reason}"
)
ctx.info("Updating OS Labels in GitHub...")
for details in labels_to_update:
label = details["name"]
ctx.info(f"Updating label '{label}' ...")
ret = web.patch(
f"https://api.github.com/repos/{repository}/labels/{label}",
params=details,
)
if ret.status_code != 200:
ctx.error(
f"Failed to update label '{details['name']}' for repository {repository!r}: {ret.reason}"
)
for label, details in known_os.items():
details["name"] = label
ctx.info(f"Creating label: {details} ...")
ret = web.post(
f"https://api.github.com/repos/{repository}/labels",
json=details,
)
if ret.status_code != 201:
ctx.error(
f"Failed to create label '{details['name']}' for repository {repository!r}: {ret.reason}"
)
print(ret.content)