jupyterlab/jupyterlab_server

Add repository url to extension info

nbowditch-einblick opened this issue · 1 comments

Problem

The core issue I'm trying to solve is that our company's logo isn't showing up next to our extension in the Jupyterlab extension manager. However, as I will explain, this is due to a function defined here in the server code.

For reference:

Screenshot 2023-10-30 at 4 13 57 PM

I traced the issue down to this function here:

def get_federated_extensions(labextensions_path: list[str]) -> dict[str, Any]:
    """Get the metadata about federated extensions"""
    federated_extensions = {}
    for ext_dir in labextensions_path:
        # extensions are either top-level directories, or two-deep in @org directories
        for ext_path in chain(
            iglob(pjoin(ext_dir, "[!@]*", "package.json")),
            iglob(pjoin(ext_dir, "@*", "*", "package.json")),
        ):
            with open(ext_path, encoding="utf-8") as fid:
                pkgdata = json.load(fid)
            if pkgdata["name"] not in federated_extensions:
                data = dict(
                    name=pkgdata["name"],
                    version=pkgdata["version"],
                    description=pkgdata.get("description", ""),
                    url=get_package_url(pkgdata),
                    ext_dir=ext_dir,
                    ext_path=osp.dirname(ext_path),
                    is_local=False,
                    dependencies=pkgdata.get("dependencies", dict()),
                    jupyterlab=pkgdata.get("jupyterlab", dict()),
                )
                install_path = osp.join(osp.dirname(ext_path), "install.json")
                if osp.exists(install_path):
                    with open(install_path, encoding="utf-8") as fid:
                        data["install"] = json.load(fid)
                federated_extensions[data["name"]] = data
    return federated_extensions

The data object stored for each extension is missing an optional entry called repository. This is used by jupyterlab to build a "repository_url" field that it sends to the front-end, which the extension-manager may use to get a github user avatar to show next to the extension as a thumbnail.

Details

Each extension thumbnail is just a url pulled from whatever github user is associated with the extension's github page. The logic to get that github url is here. Basically it looks at 2 possible fields from the entry object from the server: "homepage" or "repository_url". We have our homepage set to the pypi page for our extension, so in theory it should look at the latter option.

However, if you look at all the extension entries we get back from the server, all of them have null values for this "repository_url" field, even if they have the appropriate properties set in their package.json:

...
"repository": {
    "type": "git",
    "url": "https://github.com/some-user/some-extension.git"
}
...

I traced this back to the manager.py file in jupyterlab's repo, where it attempts to set this value here:

...
repository_url=data.get("repository", {}).get("url", data.get("repository"))
...

The data dict that it's reading is retrieved from the get_federated_extensions function. If you take a look at that function above, you can notice that there's no logic to add a "repository" field, so above will always evaluate to repository_url=None.

Proposed Solution

In get_federated_extensions, there should be some logic to check if the pkgdata (read in from a package.json file) has a "repository" field, and if so, whether that field's value is a dict with a "url" field. If so, it should apply those values to the data for that extension returned:

def get_federated_extensions(labextensions_path: list[str]) -> dict[str, Any]:
    """Get the metadata about federated extensions"""
    federated_extensions = {}
    for ext_dir in labextensions_path:
        # extensions are either top-level directories, or two-deep in @org directories
        for ext_path in chain(
            iglob(pjoin(ext_dir, "[!@]*", "package.json")),
            iglob(pjoin(ext_dir, "@*", "*", "package.json")),
        ):
            with open(ext_path, encoding="utf-8") as fid:
                pkgdata = json.load(fid)
            if pkgdata["name"] not in federated_extensions:
                data = dict(
                    name=pkgdata["name"],
                    version=pkgdata["version"],
                    description=pkgdata.get("description", ""),
                    url=get_package_url(pkgdata),
                    ext_dir=ext_dir,
                    ext_path=osp.dirname(ext_path),
                    is_local=False,
                    dependencies=pkgdata.get("dependencies", dict()),
                    jupyterlab=pkgdata.get("jupyterlab", dict()),
                )

                # ####################################################
                # NEW LOGIC START ####################################
                # ####################################################

                if "repository" in pkgdata and "url" in pkgdata.get("repository", {}):
                    data["repository"] = dict(url=pkgdata.get("repository").get("url"))

                # ####################################################
                # NEW LOGIC END ######################################
                # ####################################################

                install_path = osp.join(osp.dirname(ext_path), "install.json")
                if osp.exists(install_path):
                    with open(install_path, encoding="utf-8") as fid:
                        data["install"] = json.load(fid)
                federated_extensions[data["name"]] = data
    return federated_extensions

Additional context

None

Thank you for opening your first issue in this project! Engagement like this is essential for open source projects! 🤗

If you haven't done so already, check out Jupyter's Code of Conduct. Also, please try to follow the issue template as it helps other other community members to contribute more effectively.
welcome
You can meet the other Jovyans by joining our Discourse forum. There is also an intro thread there where you can stop by and say Hi! 👋

Welcome to the Jupyter community! 🎉