2022-01-02 21:38:21 +00:00
|
|
|
# Copyright 2022 Luca Weiss
|
2020-02-20 20:07:28 +00:00
|
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
2019-02-09 16:58:54 +00:00
|
|
|
import datetime
|
|
|
|
import fnmatch
|
|
|
|
import logging
|
|
|
|
import os
|
|
|
|
import re
|
|
|
|
import urllib
|
2020-07-28 15:18:22 +00:00
|
|
|
from typing import Optional
|
2019-02-09 16:58:54 +00:00
|
|
|
|
|
|
|
import pmb.helpers.file
|
|
|
|
import pmb.helpers.http
|
|
|
|
import pmb.helpers.pmaports
|
|
|
|
|
|
|
|
req_headers = None
|
|
|
|
req_headers_github = None
|
|
|
|
|
|
|
|
ANITYA_API_BASE = "https://release-monitoring.org/api/v2"
|
|
|
|
GITHUB_API_BASE = "https://api.github.com"
|
|
|
|
GITLAB_HOSTS = [
|
|
|
|
"https://gitlab.com",
|
2021-10-24 14:44:01 +00:00
|
|
|
"https://gitlab.freedesktop.org",
|
|
|
|
"https://gitlab.gnome.org",
|
2019-02-09 16:58:54 +00:00
|
|
|
"https://invent.kde.org",
|
|
|
|
"https://source.puri.sm",
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
def init_req_headers() -> None:
|
|
|
|
global req_headers
|
|
|
|
global req_headers_github
|
|
|
|
# Only initialize them once
|
|
|
|
if req_headers is not None and req_headers_github is not None:
|
|
|
|
return
|
|
|
|
# Generic request headers
|
2020-11-21 13:19:07 +00:00
|
|
|
req_headers = {
|
|
|
|
'User-Agent': f'pmbootstrap/{pmb.config.version} aportupgrade'}
|
2019-02-09 16:58:54 +00:00
|
|
|
|
|
|
|
# Request headers specific to GitHub
|
|
|
|
req_headers_github = dict(req_headers)
|
|
|
|
if os.getenv("GITHUB_TOKEN") is not None:
|
2020-11-21 13:19:07 +00:00
|
|
|
token = os.getenv("GITHUB_TOKEN")
|
|
|
|
req_headers_github['Authorization'] = f'token {token}'
|
2019-02-09 16:58:54 +00:00
|
|
|
else:
|
2020-11-21 13:19:07 +00:00
|
|
|
logging.info("NOTE: Consider using a GITHUB_TOKEN environment variable"
|
|
|
|
" to increase your rate limit")
|
2019-02-09 16:58:54 +00:00
|
|
|
|
|
|
|
|
2020-07-28 15:18:22 +00:00
|
|
|
def get_package_version_info_github(repo_name: str, ref: Optional[str]):
|
2019-02-09 16:58:54 +00:00
|
|
|
logging.debug("Trying GitHub repository: {}".format(repo_name))
|
|
|
|
|
2020-07-28 15:18:22 +00:00
|
|
|
# Get the URL argument to request a special ref, if needed
|
|
|
|
ref_arg = ""
|
|
|
|
if ref is not None:
|
2020-08-20 20:41:15 +00:00
|
|
|
ref_arg = f"?sha={ref}"
|
2019-02-09 16:58:54 +00:00
|
|
|
|
|
|
|
# Get the commits for the repository
|
2020-07-28 15:18:22 +00:00
|
|
|
commits = pmb.helpers.http.retrieve_json(
|
2020-08-20 20:41:15 +00:00
|
|
|
f"{GITHUB_API_BASE}/repos/{repo_name}/commits{ref_arg}",
|
2020-07-28 15:18:22 +00:00
|
|
|
headers=req_headers_github)
|
2019-02-09 16:58:54 +00:00
|
|
|
latest_commit = commits[0]
|
|
|
|
commit_date = latest_commit["commit"]["committer"]["date"]
|
|
|
|
# Extract the time from the field
|
|
|
|
date = datetime.datetime.strptime(commit_date, "%Y-%m-%dT%H:%M:%SZ")
|
|
|
|
return {
|
|
|
|
"sha": latest_commit["sha"],
|
|
|
|
"date": date,
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-07-28 15:18:22 +00:00
|
|
|
def get_package_version_info_gitlab(gitlab_host: str, repo_name: str,
|
|
|
|
ref: Optional[str]):
|
2019-02-09 16:58:54 +00:00
|
|
|
logging.debug("Trying GitLab repository: {}".format(repo_name))
|
|
|
|
|
2020-07-28 15:18:22 +00:00
|
|
|
repo_name_safe = urllib.parse.quote(repo_name, safe='')
|
|
|
|
|
|
|
|
# Get the URL argument to request a special ref, if needed
|
|
|
|
ref_arg = ""
|
|
|
|
if ref is not None:
|
2020-08-20 20:41:15 +00:00
|
|
|
ref_arg = f"?ref_name={ref}"
|
2020-07-28 15:18:22 +00:00
|
|
|
|
2019-02-09 16:58:54 +00:00
|
|
|
# Get the commits for the repository
|
|
|
|
commits = pmb.helpers.http.retrieve_json(
|
2020-11-21 13:19:07 +00:00
|
|
|
f"{gitlab_host}/api/v4/projects/{repo_name_safe}/repository"
|
|
|
|
f"/commits{ref_arg}",
|
2019-02-09 16:58:54 +00:00
|
|
|
headers=req_headers)
|
|
|
|
latest_commit = commits[0]
|
|
|
|
commit_date = latest_commit["committed_date"]
|
|
|
|
# Extract the time from the field
|
|
|
|
# 2019-10-14T09:32:00.000Z / 2019-12-27T07:58:53.000-05:00
|
|
|
|
date = datetime.datetime.strptime(commit_date, "%Y-%m-%dT%H:%M:%S.000%z")
|
|
|
|
return {
|
|
|
|
"sha": latest_commit["id"],
|
|
|
|
"date": date,
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def upgrade_git_package(args, pkgname: str, package) -> bool:
|
|
|
|
"""
|
2020-11-21 13:19:07 +00:00
|
|
|
Update _commit/pkgver/pkgrel in a git-APKBUILD (or pretend to do it if
|
|
|
|
args.dry is set).
|
2019-02-09 16:58:54 +00:00
|
|
|
:param pkgname: the package name
|
|
|
|
:param package: a dict containing package information
|
|
|
|
:returns: if something (would have) been changed
|
|
|
|
"""
|
|
|
|
# Get the wanted source line
|
|
|
|
source = package["source"][0]
|
|
|
|
source = re.split(r"::", source)
|
|
|
|
if 1 <= len(source) <= 2:
|
|
|
|
source = source[-1]
|
|
|
|
else:
|
2020-11-21 13:19:07 +00:00
|
|
|
raise RuntimeError("Unhandled number of source elements. Please open"
|
|
|
|
f" a bug report: {source}")
|
2019-02-09 16:58:54 +00:00
|
|
|
|
|
|
|
verinfo = None
|
|
|
|
|
2020-11-21 13:19:07 +00:00
|
|
|
github_match = re.match(
|
|
|
|
r"https://github\.com/(.+)/(?:archive|releases)", source)
|
|
|
|
gitlab_match = re.match(
|
|
|
|
fr"({'|'.join(GITLAB_HOSTS)})/(.+)/-/archive/", source)
|
2019-02-09 16:58:54 +00:00
|
|
|
if github_match:
|
2020-11-21 13:19:07 +00:00
|
|
|
verinfo = get_package_version_info_github(
|
|
|
|
github_match.group(1), args.ref)
|
2019-02-09 16:58:54 +00:00
|
|
|
elif gitlab_match:
|
2020-11-21 13:19:07 +00:00
|
|
|
verinfo = get_package_version_info_gitlab(
|
|
|
|
gitlab_match.group(1), gitlab_match.group(2), args.ref)
|
2019-02-09 16:58:54 +00:00
|
|
|
|
|
|
|
if verinfo is None:
|
|
|
|
# ignore for now
|
|
|
|
logging.warning("{}: source not handled: {}".format(pkgname, source))
|
|
|
|
return False
|
|
|
|
|
|
|
|
# Get the new commit sha
|
|
|
|
sha = package["_commit"]
|
|
|
|
sha_new = verinfo["sha"]
|
|
|
|
|
|
|
|
# Format the new pkgver, keep the value before _git the same
|
2021-11-06 23:18:04 +00:00
|
|
|
if package["pkgver"] == "9999":
|
|
|
|
pkgver = package["_pkgver"]
|
|
|
|
else:
|
|
|
|
pkgver = package["pkgver"]
|
|
|
|
|
2019-02-09 16:58:54 +00:00
|
|
|
pkgver_match = re.match(r"([\d.]+)_git", pkgver)
|
|
|
|
date_pkgver = verinfo["date"].strftime("%Y%m%d")
|
2020-08-20 20:41:15 +00:00
|
|
|
pkgver_new = f"{pkgver_match.group(1)}_git{date_pkgver}"
|
2019-02-09 16:58:54 +00:00
|
|
|
|
|
|
|
# pkgrel will be zero
|
|
|
|
pkgrel = int(package["pkgrel"])
|
|
|
|
pkgrel_new = 0
|
|
|
|
|
|
|
|
if sha == sha_new:
|
|
|
|
logging.info("{}: up-to-date".format(pkgname))
|
|
|
|
return False
|
|
|
|
|
|
|
|
logging.info("{}: upgrading pmaport".format(pkgname))
|
|
|
|
if args.dry:
|
2020-11-21 13:19:07 +00:00
|
|
|
logging.info(f" Would change _commit from {sha} to {sha_new}")
|
|
|
|
logging.info(f" Would change pkgver from {pkgver} to {pkgver_new}")
|
|
|
|
logging.info(f" Would change pkgrel from {pkgrel} to {pkgrel_new}")
|
2019-02-09 16:58:54 +00:00
|
|
|
return True
|
|
|
|
|
2021-11-06 23:18:04 +00:00
|
|
|
if package["pkgver"] == "9999":
|
|
|
|
pmb.helpers.file.replace_apkbuild(args, pkgname, "_pkgver", pkgver_new)
|
|
|
|
else:
|
|
|
|
pmb.helpers.file.replace_apkbuild(args, pkgname, "pkgver", pkgver_new)
|
2019-02-09 16:58:54 +00:00
|
|
|
pmb.helpers.file.replace_apkbuild(args, pkgname, "pkgrel", pkgrel_new)
|
|
|
|
pmb.helpers.file.replace_apkbuild(args, pkgname, "_commit", sha_new, True)
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
def upgrade_stable_package(args, pkgname: str, package) -> bool:
|
|
|
|
"""
|
2020-11-21 13:19:07 +00:00
|
|
|
Update _commit/pkgver/pkgrel in an APKBUILD (or pretend to do it if
|
|
|
|
args.dry is set).
|
2019-02-09 16:58:54 +00:00
|
|
|
|
|
|
|
:param pkgname: the package name
|
|
|
|
:param package: a dict containing package information
|
|
|
|
:returns: if something (would have) been changed
|
|
|
|
"""
|
2021-10-03 14:20:57 +00:00
|
|
|
|
|
|
|
# Looking up if there's a custom mapping from postmarketOS package name
|
|
|
|
# to Anitya project name.
|
|
|
|
mappings = pmb.helpers.http.retrieve_json(
|
|
|
|
f"{ANITYA_API_BASE}/packages/?distribution=postmarketOS"
|
|
|
|
f"&name={pkgname}", headers=req_headers)
|
|
|
|
if mappings["total_items"] < 1:
|
|
|
|
projects = pmb.helpers.http.retrieve_json(
|
|
|
|
f"{ANITYA_API_BASE}/projects/?name={pkgname}", headers=req_headers)
|
|
|
|
if projects["total_items"] < 1:
|
|
|
|
logging.warning(f"{pkgname}: failed to get Anitya project")
|
2019-02-09 16:58:54 +00:00
|
|
|
return False
|
2021-10-03 14:20:57 +00:00
|
|
|
else:
|
2019-02-09 16:58:54 +00:00
|
|
|
project_name = mappings["items"][0]["project"]
|
2021-10-03 14:20:57 +00:00
|
|
|
ecosystem = mappings["items"][0]["ecosystem"]
|
2019-02-09 16:58:54 +00:00
|
|
|
projects = pmb.helpers.http.retrieve_json(
|
2021-10-03 14:20:57 +00:00
|
|
|
f"{ANITYA_API_BASE}/projects/?name={project_name}&"
|
|
|
|
f"ecosystem={ecosystem}",
|
2020-11-21 13:19:07 +00:00
|
|
|
headers=req_headers)
|
2019-02-09 16:58:54 +00:00
|
|
|
|
2021-10-03 14:20:57 +00:00
|
|
|
if projects["total_items"] < 1:
|
|
|
|
logging.warning(f"{pkgname}: didn't find any projects, can't upgrade!")
|
|
|
|
return False
|
|
|
|
if projects["total_items"] > 1:
|
|
|
|
logging.warning(f"{pkgname}: found more than one project, can't "
|
|
|
|
f"upgrade! Please create an explicit mapping of "
|
|
|
|
f"\"project\" to the package name.")
|
|
|
|
return False
|
|
|
|
|
2019-02-09 16:58:54 +00:00
|
|
|
# Get the first, best-matching item
|
|
|
|
project = projects["items"][0]
|
|
|
|
|
|
|
|
# Check that we got a version number
|
2021-10-03 14:20:57 +00:00
|
|
|
if len(project["stable_versions"]) < 1:
|
2019-02-09 16:58:54 +00:00
|
|
|
logging.warning("{}: got no version number, ignoring".format(pkgname))
|
|
|
|
return False
|
|
|
|
|
2021-10-03 14:20:57 +00:00
|
|
|
version = project["stable_versions"][0]
|
|
|
|
|
2019-02-09 16:58:54 +00:00
|
|
|
# Compare the pmaports version with the project version
|
2021-10-03 14:20:57 +00:00
|
|
|
if package["pkgver"] == version:
|
2019-02-09 16:58:54 +00:00
|
|
|
logging.info("{}: up-to-date".format(pkgname))
|
|
|
|
return False
|
|
|
|
|
2021-11-06 23:18:04 +00:00
|
|
|
if package["pkgver"] == "9999":
|
|
|
|
pkgver = package["_pkgver"]
|
|
|
|
else:
|
|
|
|
pkgver = package["pkgver"]
|
|
|
|
|
2021-10-03 14:20:57 +00:00
|
|
|
pkgver_new = version
|
2019-02-09 16:58:54 +00:00
|
|
|
|
|
|
|
pkgrel = package["pkgrel"]
|
|
|
|
pkgrel_new = 0
|
|
|
|
|
|
|
|
if not pmb.parse.version.validate(pkgver_new):
|
2020-11-21 13:19:07 +00:00
|
|
|
logging.warning(f"{pkgname}: would upgrade to invalid pkgver:"
|
|
|
|
f" {pkgver_new}, ignoring")
|
2019-02-09 16:58:54 +00:00
|
|
|
return False
|
|
|
|
|
|
|
|
logging.info("{}: upgrading pmaport".format(pkgname))
|
|
|
|
if args.dry:
|
2020-11-21 13:19:07 +00:00
|
|
|
logging.info(f" Would change pkgver from {pkgver} to {pkgver_new}")
|
|
|
|
logging.info(f" Would change pkgrel from {pkgrel} to {pkgrel_new}")
|
2019-02-09 16:58:54 +00:00
|
|
|
return True
|
|
|
|
|
2021-11-06 23:18:04 +00:00
|
|
|
if package["pkgver"] == "9999":
|
|
|
|
pmb.helpers.file.replace_apkbuild(args, pkgname, "_pkgver", pkgver_new)
|
|
|
|
else:
|
|
|
|
pmb.helpers.file.replace_apkbuild(args, pkgname, "pkgver", pkgver_new)
|
|
|
|
|
2019-02-09 16:58:54 +00:00
|
|
|
pmb.helpers.file.replace_apkbuild(args, pkgname, "pkgrel", pkgrel_new)
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
def upgrade(args, pkgname, git=True, stable=True) -> bool:
|
|
|
|
"""
|
|
|
|
Find new versions of a single package and upgrade it.
|
|
|
|
|
|
|
|
:param pkgname: the name of the package
|
|
|
|
:param git: True if git packages should be upgraded
|
|
|
|
:param stable: True if stable packages should be upgraded
|
|
|
|
:returns: if something (would have) been changed
|
|
|
|
"""
|
|
|
|
# Initialize request headers
|
|
|
|
init_req_headers()
|
|
|
|
|
|
|
|
package = pmb.helpers.pmaports.get(args, pkgname)
|
|
|
|
# Run the correct function
|
|
|
|
if "_git" in package["pkgver"]:
|
|
|
|
if git:
|
|
|
|
return upgrade_git_package(args, pkgname, package)
|
|
|
|
else:
|
|
|
|
if stable:
|
|
|
|
return upgrade_stable_package(args, pkgname, package)
|
|
|
|
|
|
|
|
|
|
|
|
def upgrade_all(args) -> None:
|
|
|
|
"""
|
|
|
|
Upgrade all packages, based on args.all, args.all_git and args.all_stable.
|
|
|
|
"""
|
|
|
|
for pkgname in pmb.helpers.pmaports.get_list(args):
|
2020-11-21 13:19:07 +00:00
|
|
|
# Always ignore postmarketOS-specific packages that have no upstream
|
|
|
|
# source
|
2019-02-09 16:58:54 +00:00
|
|
|
skip = False
|
|
|
|
for pattern in pmb.config.upgrade_ignore:
|
|
|
|
if fnmatch.fnmatch(pkgname, pattern):
|
|
|
|
skip = True
|
|
|
|
if skip:
|
|
|
|
continue
|
|
|
|
|
2020-11-21 13:19:07 +00:00
|
|
|
upgrade(args, pkgname, args.all or args.all_git,
|
|
|
|
args.all or args.all_stable)
|