From 87975eaaf342667595d7480ad1ea93739fcb12a0 Mon Sep 17 00:00:00 2001 From: Jeremy Davis Date: Wed, 8 Oct 2025 21:48:40 +1100 Subject: [PATCH 1/2] wip... --- overlays/php/usr/local/bin/tkl-upgrade-php | 136 ++++++++++++--------- 1 file changed, 80 insertions(+), 56 deletions(-) diff --git a/overlays/php/usr/local/bin/tkl-upgrade-php b/overlays/php/usr/local/bin/tkl-upgrade-php index b670ac14..17840aa2 100755 --- a/overlays/php/usr/local/bin/tkl-upgrade-php +++ b/overlays/php/usr/local/bin/tkl-upgrade-php @@ -12,11 +12,12 @@ import sys PHP_VERSION_RE = re.compile(r"^Package: (php\d+\.\d+)-") -def get_output(args): +def get_output(args: list[str]) -> str: return subprocess.check_output(args, text=True).strip() + ARCH = get_output(["dpkg", "--print-architecture"]) -CODENAME = get_output(["lsb_release", "-c"]).split()[1] +CODENAME = get_output(["lsb_release", "-sc"]) SECURITY_NOTICE = """ @@ -30,18 +31,19 @@ NOTE: applies for your use case. """ + def get_supported_versions() -> dict[str, str]: - with request.urlopen('http://www.php.net/releases/active') as iob: + with request.urlopen("https://www.php.net/releases/active") as iob: data = json.loads(iob.read().decode()) versions = {} for major in data.keys(): for major_minor in data[major].keys(): - tags = data[major][major_minor]['tags'] - if 'security' in tags: - versions[major_minor] = 'security only' + tags = data[major][major_minor]["tags"] + if "security" in tags: + versions[major_minor] = "security only" else: - versions[major_minor] = 'supported' + versions[major_minor] = "supported" return versions @@ -68,11 +70,11 @@ def choose_version(choices, version_support, deb_version) -> str: print("Select the PHP version to install:") for i, opt in enumerate(choices): - support = version_support.get(opt[3:], 'end of life') + support = version_support.get(opt[3:], "end of life") if opt[3:] == deb_version: - print(f"{i+1}). {opt} ({support}) (provided by debian)") + print(f"{i + 1}). {opt} ({support}) (provided by debian)") else: - print(f"{i+1}). {opt} ({support})") + print(f"{i + 1}). {opt} ({support})") inp = input(f"\nEnter choice [1-{len(choices)}]: ") @@ -88,6 +90,7 @@ def choose_version(choices, version_support, deb_version) -> str: return choices[inp - 1] + def get_packages(php_version): curr_package = None packages = {} @@ -103,40 +106,67 @@ def get_packages(php_version): curr_package = None return packages.keys() + def debian_version(): - out = get_output(['apt-cache', 'policy', 'php']) - out = out.split('Version table:')[1].strip() + out = get_output(["apt-cache", "policy", "php"]) + out = out.split("Version table:")[1].strip() last_line = None for line in out.splitlines(): - if 'deb.debian.org/debian bookworm/main' in line: + if "deb.debian.org/debian bookworm/main" in line: if last_line: - return last_line.strip().split(':')[1].split('+')[0] + return last_line.strip().split(":")[1].split("+")[0] return None last_line = line return None + def check_new_packages(new_packages): missing_packages = [] found_packages = [] for pkg in new_packages: - if subprocess.run( - ['apt-cache', 'show', pkg], - stdout=DEVNULL, stderr=DEVNULL).returncode != 0: + if ( + subprocess.run( + ["apt-cache", "show", pkg], stdout=DEVNULL, stderr=DEVNULL + ).returncode + != 0 + ): missing_packages.append(pkg) else: found_packages.append(pkg) if missing_packages: - print('The following packages do not exist in the requested PHP version:') - print(' ' + ', '.join(missing_packages)) + print( + "The following packages do not exist in the requested PHP version:" + ) + print(" " + ", ".join(missing_packages)) return found_packages + def is_active(service): - return subprocess.run(['systemctl', 'is-active', '--quiet', service]).returncode == 0 + return ( + subprocess.run( + ["systemctl", "is-active", "--quiet", service] + ).returncode == 0 + ) + -if __name__ == '__main__': +def php_sources(codename: str = CODENAME, enabled: bool = True) -> None: + enabled_str = "yes" if enabled else "no" + with open("/etc/apt/sources.list.d/php.sources", "w") as fob: + fob.write( + "# DEB.SURY.ORG repo for php\n" + "Types: deb\n" + "URIs: https://packages.sury.org/php/\n" + f"Suites: {CODENAME}\n" + "Components: main\n" + f"Enabled: {enabled_str}\n" + "Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg\n" + ) + + +if __name__ == "__main__": print("Starting PHP upgrade script ...") print("Current PHP version") subprocess.run(["/usr/bin/php", "-v"]) @@ -145,72 +175,66 @@ if __name__ == '__main__': ["php", "-r", 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;'] ) - print('Determining supported version ...') + print("Determining supported version ...") version_support = get_supported_versions() deb_version = debian_version() if not deb_version: print('Running "apt-get update" ...') - subprocess.run(['apt-get', 'update']) + subprocess.run(["apt-get", "update"]) deb_version = debian_version() - - new_php_version = choose_version(get_php_versions(), version_support, - deb_version) + new_php_version = choose_version(get_php_versions(), version_support, deb_version) new_php_pkgs = [ pkg.replace("php" + current_php_version, new_php_version) for pkg in get_packages(current_php_version) ] - if new_php_version[3:] == deb_version: - if os.path.isfile('/etc/apt/sources.list.d/php.list'): - os.remove('/etc/apt/sources.list.d/php.list') - subprocess.run(['dpkg', '-r', 'debsuryorg-archive-keyring']) + # remove legacy php.list file if it exists + if os.path.isfile("/etc/apt/sources.list.d/php.list"): + os.remove("/etc/apt/sources.list.d/php.list") + if new_php_version[3:] == deb_version: + if os.path.isfile("/etc/apt/sources.list.d/php.list"): + subprocess.run(["dpkg", "-r", "debsuryorg-archive-keyring"]) else: + print("Downloading Sury Archive Keyring ...") - print('Downloading Sury Archive Keyring ...') - - url = 'https://packages.sury.org/debsuryorg-archive-keyring.deb' + url = "https://packages.sury.org/debsuryorg-archive-keyring.deb" with request.urlopen(url) as iob: - with open('/tmp/debsuryorg-archive.keyring.deb', 'wb') as fob: + with open("/tmp/debsuryorg-archive.keyring.deb", "wb") as fob: fob.write(iob.read()) - subprocess.run(['dpkg', '-i', '/tmp/debsuryorg-archive.keyring.deb']) - - with open('/etc/apt/sources.list.d/php.list', 'w') as fob: - fob.write( - 'deb [signed-by=/usr/share/keyrings/debsuryorg-archive-keyring.gpg]' - f' https://packages.sury.org/php/ {CODENAME} main' - ) + subprocess.run(["dpkg", "-i", "/tmp/debsuryorg-archive.keyring.deb"]) print('Running "apt-get update" ...') - subprocess.run(['apt-get', 'update']) + subprocess.run(["apt-get", "update"]) found_packages = check_new_packages(new_php_pkgs) - print('Installing new packages ...') + print("Installing new packages ...") - subprocess.run(['apt-get', '-y', 'install', *found_packages]) + subprocess.run(["apt-get", "-y", "install", *found_packages]) - print('Configuring Webserver ...') + print("Configuring Webserver ...") - if is_active('apache2'): - subprocess.run(['a2dismod', 'php' + current_php_version]) - subprocess.run(['a2enmod', new_php_version]) - subprocess.run(['systemctl', 'restart', 'apache2']) - elif is_active('nginx'): - subprocess.run(['systemctl', 'restart', new_php_version + '-fpm']) - elif is_active('lighttpd'): - subprocess.run(['systemctl', 'restart', new_php_version + '-fpm']) + if is_active("apache2"): + subprocess.run(["a2dismod", "php" + current_php_version]) + subprocess.run(["a2enmod", new_php_version]) + subprocess.run(["systemctl", "restart", "apache2"]) + elif is_active("nginx"): + subprocess.run(["systemctl", "restart", new_php_version + "-fpm"]) + elif is_active("lighttpd"): + subprocess.run(["systemctl", "restart", new_php_version + "-fpm"]) else: print( "No supported web server detected. Please manually configure " "your web server to use the new PHP version." ) - subprocess.run(['update-alternatives', '--set', 'php', '/usr/bin/' + - new_php_version]) + subprocess.run( + ["update-alternatives", "--set", "php", "/usr/bin/" + new_php_version] + ) - print('PHP upgrade completed successfully.') + print("PHP upgrade completed successfully.") From e28215b5a50154642f336ce66733621ae8e95d8a Mon Sep 17 00:00:00 2001 From: Jeremy Davis Date: Thu, 4 Dec 2025 09:33:17 +1100 Subject: [PATCH 2/2] mostly just typing/linting and minor refactoring - untested and more to do... --- overlays/php/usr/local/bin/tkl-upgrade-php | 45 +++++++++++++--------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/overlays/php/usr/local/bin/tkl-upgrade-php b/overlays/php/usr/local/bin/tkl-upgrade-php index 17840aa2..edf2dde2 100755 --- a/overlays/php/usr/local/bin/tkl-upgrade-php +++ b/overlays/php/usr/local/bin/tkl-upgrade-php @@ -25,7 +25,7 @@ NOTE: Older version of PHP may not be actively maintained, this includes not just bug fixes but also security fixes. - Versions which have reached EOL should not be expected to be secure. + Versions which have reached EOL should not be expected to be secure. It is always encouraged that you only install the newest version that applies for your use case. @@ -51,8 +51,9 @@ def get_php_versions() -> list[str]: """Get all available php versions and return them in the form phpx.y""" print("\nChecking Available PHP Versions") - url = f"https://packages.sury.org/php/dists/{CODENAME}/main/binary-{ARCH}/Packages" - with request.urlopen(url) as iob: + url = "https://packages.sury.org" + pkg_file_url = f"{url}/php/dists/{CODENAME}/main/binary-{ARCH}/Packages" + with request.urlopen(pkg_file_url) as iob: data = iob.read().decode() php_versions = set() @@ -82,29 +83,28 @@ def choose_version(choices, version_support, deb_version) -> str: print("Input must be a number! Exiting") sys.exit(1) - inp = int(inp) + choice_inp = int(inp) - if inp < 1 or inp > len(choices): + if choice_inp < 1 or choice_inp > len(choices): print(f"Input out of range [1-{len(choices)}]! Exiting") sys.exit(1) - return choices[inp - 1] + return choices[choice_inp - 1] -def get_packages(php_version): - curr_package = None - packages = {} +def get_packages(php_version: str) -> list[str]: + curr_package = "" + packages: dict[str, str] = {} for line in get_output( ["apt-cache", "policy", "--", "*" + php_version] ).splitlines(): if not line[0].isspace(): curr_package = line.rstrip(":") - packages[curr_package] = [] - else: - if "Installed: (none)" in line: - del packages[curr_package] - curr_package = None - return packages.keys() + packages[curr_package] = "" + elif "Installed: (none)" in line: + del packages[curr_package] + curr_package = "" + return list(packages.keys()) def debian_version(): @@ -113,7 +113,7 @@ def debian_version(): last_line = None for line in out.splitlines(): - if "deb.debian.org/debian bookworm/main" in line: + if f"deb.debian.org/debian {CODENAME}/main" in line: if last_line: return last_line.strip().split(":")[1].split("+")[0] return None @@ -162,10 +162,15 @@ def php_sources(codename: str = CODENAME, enabled: bool = True) -> None: f"Suites: {CODENAME}\n" "Components: main\n" f"Enabled: {enabled_str}\n" - "Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg\n" + "Signed-By: /usr/share/keyrings/debsuryorg-archive-keyring.gpg\n" ) +def update_pins(new_php_version: str) -> None: + """Update/write apt pins (preferences).""" + # TODO.... + + if __name__ == "__main__": print("Starting PHP upgrade script ...") print("Current PHP version") @@ -184,7 +189,9 @@ if __name__ == "__main__": subprocess.run(["apt-get", "update"]) deb_version = debian_version() - new_php_version = choose_version(get_php_versions(), version_support, deb_version) + new_php_version = choose_version( + get_php_versions(), version_support, deb_version + ) new_php_pkgs = [ pkg.replace("php" + current_php_version, new_php_version) for pkg in get_packages(current_php_version) @@ -196,7 +203,7 @@ if __name__ == "__main__": if new_php_version[3:] == deb_version: if os.path.isfile("/etc/apt/sources.list.d/php.list"): - subprocess.run(["dpkg", "-r", "debsuryorg-archive-keyring"]) + subprocess.run(["dpkg", "-r", "debsuryorg-archive-keyring"]) else: print("Downloading Sury Archive Keyring ...")