From a595bbba67271550735cf6d1be533b81fb676fee Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Mon, 23 Mar 2026 12:39:17 +0100 Subject: [PATCH] tools/mpremote: Add an alias for codeberg repos. This commit introduces an alias to access codeberg repos from within mpremote's package manager. Right now packages hosted on codeberg could only be referenced by their full URL, unlike other packages hosted on either GitHub or GitLab. To make access those packages easier, now they can be referenced as "codeberg:org/repo@branch". Signed-off-by: Alessandro Gatti --- docs/reference/packages.rst | 11 +++++-- tools/mpremote/README.md | 1 + tools/mpremote/mpremote/main.py | 2 +- tools/mpremote/mpremote/mip.py | 55 ++++++++++++++------------------- 4 files changed, 35 insertions(+), 34 deletions(-) diff --git a/docs/reference/packages.rst b/docs/reference/packages.rst index 5b5f626d45..adc9f95e01 100644 --- a/docs/reference/packages.rst +++ b/docs/reference/packages.rst @@ -38,13 +38,15 @@ install third-party libraries. The simplest way is to download a file directly:: When installing a file directly, the ``target`` argument is still supported to set the destination path, but ``mpy`` and ``version`` are ignored. -The URL can also start with ``github:`` or ``gitlab:`` as a simple way of pointing to content -hosted on GitHub or GitLab:: +The URL can also start with ``github:``, ``gitlab:``, or ``codeberg:`` as a simple +way of pointing to content hosted on GitHub, GitLab, or Codeberg:: >>> mip.install("github:org/repo/path/foo.py") # Uses default branch >>> mip.install("github:org/repo/path/foo.py", version="branch-or-tag") # Optionally specify the branch or tag >>> mip.install("gitlab:org/repo/path/foo.py") # Uses default branch >>> mip.install("gitlab:org/repo/path/foo.py", version="branch-or-tag") # Optionally specify the branch or tag + >>> mip.install("codeberg:org/repo/path/foo.py") # Uses default branch + >>> mip.install("codeberg:org/repo/path/foo.py", version="branch-or-tag") # Optionally specify the branch or tag More sophisticated packages (i.e. with more than one file, or with dependencies) can be downloaded by specifying the path to their ``package.json``. @@ -52,6 +54,7 @@ can be downloaded by specifying the path to their ``package.json``. >>> mip.install("http://example.com/x/package.json") >>> mip.install("github:org/user/path/package.json") >>> mip.install("gitlab:org/user/path/package.json") + >>> mip.install("codeberg:org/user/path/package.json") If no json file is specified, then "package.json" is implicitly added:: @@ -60,6 +63,8 @@ If no json file is specified, then "package.json" is implicitly added:: >>> mip.install("github:org/repo", version="branch-or-tag") >>> mip.install("gitlab:org/repo") # Uses default branch of that repo >>> mip.install("gitlab:org/repo", version="branch-or-tag") + >>> mip.install("codeberg:org/repo") # Uses default branch of that repo + >>> mip.install("codeberg:org/repo", version="branch-or-tag") Using ``mip`` on the Unix port ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -89,6 +94,8 @@ can be used from a host PC to install packages to a locally connected device $ mpremote mip install github:org/repo@branch-or-tag $ mpremote mip install gitlab:org/repo $ mpremote mip install gitlab:org/repo@branch-or-tag + $ mpremote mip install codeberg:org/repo + $ mpremote mip install codeberg:org/repo@branch-or-tag The ``--target=path``, ``--no-mpy``, and ``--index`` arguments can be set:: diff --git a/tools/mpremote/README.md b/tools/mpremote/README.md index 2bf784be29..abd62f9dce 100644 --- a/tools/mpremote/README.md +++ b/tools/mpremote/README.md @@ -82,3 +82,4 @@ Examples: mpremote mip install aioble mpremote mip install github:org/repo@branch mpremote mip install gitlab:org/repo@branch + mpremote mip install codeberg:org/repo@branch diff --git a/tools/mpremote/mpremote/main.py b/tools/mpremote/mpremote/main.py index b31186ba2e..b57de0e25f 100644 --- a/tools/mpremote/mpremote/main.py +++ b/tools/mpremote/mpremote/main.py @@ -246,7 +246,7 @@ def argparse_mip(): cmd_parser.add_argument( "packages", nargs="+", - help="list package specifications, e.g. name, name@version, github:org/repo, github:org/repo@branch, gitlab:org/repo, gitlab:org/repo@branch", + help="list package specifications, e.g. name, name@version, github:org/repo, github:org/repo@branch, gitlab:org/repo, gitlab:org/repo@branch, codeberg:org/repo, codeberg:org/repo@branch", ) return cmd_parser diff --git a/tools/mpremote/mpremote/mip.py b/tools/mpremote/mpremote/mip.py index 0d12ae651e..3748fb980e 100644 --- a/tools/mpremote/mpremote/mip.py +++ b/tools/mpremote/mpremote/mip.py @@ -5,7 +5,6 @@ import urllib.error import urllib.request import json -import tempfile import os import os.path @@ -14,7 +13,19 @@ from .commands import CommandError, show_progress_bar _PACKAGE_INDEX = "https://micropython.org/pi/v2" -allowed_mip_url_prefixes = ("http://", "https://", "github:", "gitlab:") +# Since all URLs are accessed via HTTPS, the URL scheme is added by _rewrite_url. +# The first three format parameters are assumed to be the organisation, the +# repository names, and the branch/tag name, in this order. +_HOSTS = { + # https://codeberg.org/api/v1/repos/{org}/{repo}/raw/{path}?ref={branch} + "codeberg:": "codeberg.org/api/v1/repos/{}/{}/raw/{p}?ref={}", + # https://raw.githubusercontent.com/{org}/{repo}/{branch}/{path} + "github:": "raw.githubusercontent.com/{}/{}/{}/{p}", + # https://gitlab.com/{org}/{repo}/-/raw/{branch}/{path} + "gitlab:": "gitlab.com/{}/{}/-/raw/{}/{p}", +} + +_ALLOWED_MIP_URL_PREFIXES = ("http://", "https://", "codeberg:", "github:", "gitlab:") # This implements os.makedirs(os.dirname(path)) @@ -44,37 +55,19 @@ def _check_exists(transport, path, short_hash): def _rewrite_url(url, branch=None): - if not branch: - branch = "HEAD" - if url.startswith("github:"): - url = url[7:].split("/") - url = ( - "https://raw.githubusercontent.com/" - + url[0] - + "/" - + url[1] - + "/" - + branch - + "/" - + "/".join(url[2:]) - ) - elif url.startswith("gitlab:"): - url = url[7:].split("/") - url = ( - "https://gitlab.com/" - + url[0] - + "/" - + url[1] - + "/-/raw/" - + branch - + "/" - + "/".join(url[2:]) + for provider, url_format in _HOSTS.items(): + if not url.startswith(provider): + continue + components = url[len(provider) :].split("/") + # Add https:// prefix to the final URL. + return _ALLOWED_MIP_URL_PREFIXES[1] + url_format.format( + components[0], components[1], branch or "HEAD", p="/".join(components[2:]) ) return url def _download_file(transport, url, dest): - if url.startswith(allowed_mip_url_prefixes): + if url.startswith(_ALLOWED_MIP_URL_PREFIXES): try: with urllib.request.urlopen(url) as src: data = src.read() @@ -101,7 +94,7 @@ def _download_file(transport, url, dest): def _install_json(transport, package_json_url, index, target, version, mpy): base_url = "" - if package_json_url.startswith(allowed_mip_url_prefixes): + if package_json_url.startswith(_ALLOWED_MIP_URL_PREFIXES): try: with urllib.request.urlopen(_rewrite_url(package_json_url, version)) as response: package_json = json.load(response) @@ -131,7 +124,7 @@ def _install_json(transport, package_json_url, index, target, version, mpy): _download_file(transport, file_url, fs_target_path) for target_path, url in package_json.get("urls", ()): fs_target_path = target + "/" + target_path - if base_url and not url.startswith(allowed_mip_url_prefixes): + if base_url and not url.startswith(_ALLOWED_MIP_URL_PREFIXES): url = f"{base_url}/{url}" # Relative URLs _download_file(transport, _rewrite_url(url, version), fs_target_path) for dep, dep_version in package_json.get("deps", ()): @@ -139,7 +132,7 @@ def _install_json(transport, package_json_url, index, target, version, mpy): def _install_package(transport, package, index, target, version, mpy): - if package.startswith(allowed_mip_url_prefixes): + if package.startswith(_ALLOWED_MIP_URL_PREFIXES): if package.endswith(".py") or package.endswith(".mpy"): print(f"Downloading {package} to {target}") _download_file(