From 7e9e215bbc673d31a5141cc66618608d0abd155a Mon Sep 17 00:00:00 2001 From: Casey Link Date: Mon, 24 Jun 2024 10:23:01 +0200 Subject: [PATCH] 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 --- pkgs/by-name/oc/ocis-bin/package.nix | 59 ++++++-- pkgs/by-name/oc/ocis-bin/update.py | 215 +++++++++++++++++++++++++++ 2 files changed, 258 insertions(+), 16 deletions(-) create mode 100755 pkgs/by-name/oc/ocis-bin/update.py diff --git a/pkgs/by-name/oc/ocis-bin/package.nix b/pkgs/by-name/oc/ocis-bin/package.nix index f64625b7a5ee..572c78ba10f1 100644 --- a/pkgs/by-name/oc/ocis-bin/package.nix +++ b/pkgs/by-name/oc/ocis-bin/package.nix @@ -5,28 +5,46 @@ autoPatchelfHook, }: +let + arch = + { + i686-linux = "386"; + x86_64-linux = "amd64"; + aarch64-linux = "arm64"; + armv7l-linux = "arm"; + x86_64-darwin = "amd64"; + aarch64-darwin = "arm64"; + } + ."${stdenv.hostPlatform.system}" or (throw "Unsupported system: ${stdenv.hostPlatform.system}"); + + os = + if stdenv.isLinux then + "linux" + else if stdenv.isDarwin then + "darwin" + else + throw "Unsupported OS"; + + hash = + { + hash_386-linux = "sha256-4yEgg0Ve8tjNn2weH9d91tfRaU1TE569VvZLxzuzXsw="; + hash_amd64-linux = "sha256-YAIhtHv/cO4yFpkWoRNMf6t4+ifMtGPTcYu84ZMvfD4="; + hash_arm64-linux = "sha256-OdtT9NOhh0Fkk+8CDic0NWWbGflk3FcuKB60OycJU5E="; + hash_arm-linux = "sha256-foMsZ8Nq+Q5lqt2XZCDvQ+/sFM8/1/rPfogzsyrQHqs="; + hash_amd64-darwin = "sha256-6jaX9iqyqztykeXZX3YqwRV/silFiyfeB9gJyreAfF8="; + hash_arm64-darwin = "sha256-KJqMJct7YWocE4eVjMF36adqTIf7WcutZlG3QEoMhCI="; + } + ."hash_${arch}-${os}"; +in stdenv.mkDerivation (finalAttrs: { pname = "ocis-bin"; version = "5.0.5"; - system = - if stdenv.isLinux && stdenv.isx86_64 then - "linux-amd64" - else if stdenv.isLinux && stdenv.isAarch64 then - "linux-arm64" - else - ""; src = fetchurl { - url = "https://github.com/owncloud/ocis/releases/download/v${finalAttrs.version}/ocis-${finalAttrs.version}-${finalAttrs.system}"; - - hash = - if stdenv.isLinux && stdenv.isAarch64 then - "sha256-OdtT9NOhh0Fkk+8CDic0NWWbGflk3FcuKB60OycJU5E=" - else if stdenv.isLinux && stdenv.isx86_64 then - "sha256-YAIhtHv/cO4yFpkWoRNMf6t4+ifMtGPTcYu84ZMvfD4=" - else - builtins.throw "Unsupported platform, please contact Nixpkgs maintainers for ocis package"; + url = "https://github.com/owncloud/ocis/releases/download/v${finalAttrs.version}/ocis-${finalAttrs.version}-${os}-${arch}"; + inherit hash; }; + dontUnpack = true; nativeBuildInputs = [ autoPatchelfHook ]; @@ -37,6 +55,8 @@ stdenv.mkDerivation (finalAttrs: { runHook postInstall ''; + passthru.updateScript = ./update.py; + meta = with lib; { description = "ownCloud Infinite Scale Stack "; homepage = "https://owncloud.dev/ocis/"; @@ -50,6 +70,13 @@ stdenv.mkDerivation (finalAttrs: { danth ramblurr ]; + + platforms = + (lib.intersectLists platforms.linux ( + lib.platforms.arm ++ lib.platforms.aarch64 ++ lib.platforms.x86 + )) + ++ (lib.intersectLists platforms.darwin (lib.platforms.aarch64 ++ lib.platforms.x86_64)); + sourceProvenance = [ sourceTypes.binaryNativeCode ]; mainProgram = "ocis"; }; diff --git a/pkgs/by-name/oc/ocis-bin/update.py b/pkgs/by-name/oc/ocis-bin/update.py new file mode 100755 index 000000000000..e66b39f96f38 --- /dev/null +++ b/pkgs/by-name/oc/ocis-bin/update.py @@ -0,0 +1,215 @@ +#!/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()