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(