nixpkgs/pkgs/by-name/oc/ocis-bin/update.py
Casey Link 7e9e215bbc ocis-bin: support more architectures and add update script
* Adds an update script that can be used by the passthru updateScript [0]
    * this script tracks the Production (aka stable) channel for oCIS https://owncloud.dev/ocis/release_roadmap/#release-types as decided by the package maintainers
* Adds the full range of architectures released by owncloud
    * darwin: amd64, aarch64
    * linux: i686, x86_64, arm, aarch64

[0]: https://github.com/NixOS/nixpkgs/blob/master/pkgs/README.md#automatic-package-updates
2024-07-13 14:39:29 +02:00

216 lines
6.5 KiB
Python
Executable file

#!/usr/bin/env nix-shell
##!nix-shell -I nixpkgs=./. -i python3 -p common-updater-scripts gnused nix coreutils python312
"""
Updater script for the ocis-bin package.
This script fetches an HTML table from a specified URL and parses it to determine the release type
(either "Rolling" or "Production") of a given software version. It uses the built-in urllib.request
for fetching the HTML content and the built-in html.parser for parsing the HTML. By relying only on
standard library modules, we avoid dependencies on third-party libraries, which simplifies deployment
and improves portability.
"""
import urllib.request
import os
import subprocess
import json
import sys
from datetime import datetime
from html.parser import HTMLParser
TRACKING_CHANNEL = "Production" # Either Rolling or Production
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN", None)
class TableParser(HTMLParser):
def __init__(self, version):
super().__init__()
self.version = version
self.in_td = False
self.current_row = []
self.release_type = None
self.in_target_row = False
def handle_starttag(self, tag, attrs):
if tag == "td":
self.in_td = True
if tag == "a":
href = dict(attrs).get("href", "")
if self.version in href:
self.in_target_row = True
def handle_endtag(self, tag):
if tag == "td":
self.in_td = False
if tag == "tr" and self.in_target_row:
self.release_type = self.current_row[1]
self.in_target_row = False
if tag == "tr":
self.current_row = []
def handle_data(self, data):
if self.in_td:
self.current_row.append(data.strip())
def get_release_type(content, version):
parser = TableParser(version)
parser.feed(content)
return parser.release_type
def get_latest_version():
url = "https://api.github.com/repos/owncloud/ocis/releases?per_page=1"
req = urllib.request.Request(url)
if GITHUB_TOKEN:
req.add_header("Authorization", f"Bearer {GITHUB_TOKEN}")
with urllib.request.urlopen(req) as response:
if response.status != 200:
raise Exception(f"HTTP request failed with status {response.status}")
data = response.read()
releases = json.loads(data)
latest_version = releases[0]["tag_name"].lstrip("v")
return latest_version
def get_all_versions():
url = "https://api.github.com/repos/owncloud/ocis/releases"
req = urllib.request.Request(url)
if GITHUB_TOKEN:
req.add_header("Authorization", f"Bearer {GITHUB_TOKEN}")
with urllib.request.urlopen(req) as response:
if response.status != 200:
raise Exception(f"HTTP request failed with status {response.status}")
data = response.read()
releases = json.loads(data)
versions = []
for release in releases:
version = release["tag_name"].lstrip("v")
published_date = datetime.strptime(
release["published_at"], "%Y-%m-%dT%H:%M:%SZ"
)
versions.append({"version": version, "published_date": published_date})
return versions
def get_current_version():
result = subprocess.run(
[
"nix-instantiate",
"--eval",
"-E",
"with import ./. {}; ocis-bin.version or (lib.getVersion ocis-bin)",
],
capture_output=True,
text=True,
)
result.check_returncode()
return result.stdout.strip().strip('"')
def get_hash(os_name, arch, version):
url = f"https://github.com/owncloud/ocis/releases/download/v{version}/ocis-{version}-{os_name}-{arch}"
result = subprocess.run(
["nix-prefetch-url", "--type", "sha256", url], capture_output=True, text=True
)
result.check_returncode()
pkg_hash = result.stdout.strip()
result = subprocess.run(
["nix", "hash", "to-sri", f"sha256:{pkg_hash}"], capture_output=True, text=True
)
result.check_returncode()
return result.stdout.strip()
def update_source_version(pkg_name, version, hash_value, system):
subprocess.run(
[
"update-source-version",
pkg_name,
version,
hash_value,
f"--system={system}",
"--ignore-same-version",
],
check=True,
)
def main():
all_versions = get_all_versions()
latest_version = all_versions[0]
nix_current_version = get_current_version()
current_version = None
for version in all_versions:
if nix_current_version == version["version"]:
current_version = version
break
if not current_version:
print(
f"error: cannot find github release for current nix version of ocis-bin {nix_current_version}"
)
sys.exit(1)
if current_version == latest_version:
print(f"ocis-bin is up-to-date: {current_version}")
return
roadmap_url = "https://owncloud.dev/ocis/release_roadmap/"
response = urllib.request.urlopen(roadmap_url)
content = response.read().decode("utf-8")
latest_version_channel = get_release_type(content, latest_version["version"])
current_version_channel = get_release_type(content, current_version["version"])
target_version = None
if latest_version_channel == TRACKING_CHANNEL:
target_version = latest_version
elif latest_version_channel != TRACKING_CHANNEL:
for version in all_versions:
channel = get_release_type(content, version["version"])
if (
channel == TRACKING_CHANNEL
and version["published_date"] > current_version["published_date"]
):
target_version = version
print(
f"ocis-bin found newer version {version['version']} in channel {TRACKING_CHANNEL}"
)
break
if not target_version:
print(
f"ocis-bin could not find newer version in {TRACKING_CHANNEL} than the current {current_version['version']}"
)
return
systems = [
("darwin", "arm64", "aarch64-darwin"),
("darwin", "amd64", "x86_64-darwin"),
("linux", "arm64", "aarch64-linux"),
("linux", "arm", "armv7l-linux"),
("linux", "amd64", "x86_64-linux"),
("linux", "386", "i686-linux"),
]
for os_name, arch, system in systems:
hash_value = get_hash(os_name, arch, target_version["version"])
update_source_version("ocis-bin", target_version["version"], hash_value, system)
if __name__ == "__main__":
main()