{"id":16393698,"url":"https://github.com/astariul/github-hosted-pypi","last_synced_at":"2026-03-10T17:05:25.706Z","repository":{"id":44357453,"uuid":"286486701","full_name":"astariul/github-hosted-pypi","owner":"astariul","description":"Your own private PyPi index, github-hosted","archived":false,"fork":false,"pushed_at":"2025-02-10T12:47:00.000Z","size":156,"stargazers_count":215,"open_issues_count":5,"forks_count":5,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-10-05T17:03:27.536Z","etag":null,"topics":["github","github-pages","hacktoberfest","pypi","pypi-package","pypi-packages","python","template"],"latest_commit_sha":null,"homepage":"https://astariul.github.io/github-hosted-pypi/","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/astariul.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-08-10T13:44:30.000Z","updated_at":"2025-09-26T23:21:54.000Z","dependencies_parsed_at":"2024-10-27T10:59:41.456Z","dependency_job_id":"16c180d6-2c97-4323-a81e-9268fa66a263","html_url":"https://github.com/astariul/github-hosted-pypi","commit_stats":null,"previous_names":[],"tags_count":5,"template":true,"template_full_name":null,"purl":"pkg:github/astariul/github-hosted-pypi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/astariul%2Fgithub-hosted-pypi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/astariul%2Fgithub-hosted-pypi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/astariul%2Fgithub-hosted-pypi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/astariul%2Fgithub-hosted-pypi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/astariul","download_url":"https://codeload.github.com/astariul/github-hosted-pypi/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/astariul%2Fgithub-hosted-pypi/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30343823,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-10T15:55:29.454Z","status":"ssl_error","status_checked_at":"2026-03-10T15:54:58.440Z","response_time":106,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["github","github-pages","hacktoberfest","pypi","pypi-package","pypi-packages","python","template"],"created_at":"2024-10-11T04:54:03.062Z","updated_at":"2026-03-10T17:05:25.677Z","avatar_url":"https://github.com/astariul.png","language":"HTML","readme":"\u003ch1 align=\"center\"\u003egithub-hosted-pypi\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\nMake all your private packages accessible in one place\u003cbr\u003ewith this github-hosted PyPi index\n\u003c/p\u003e\n\n---\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"#description\"\u003eDescription\u003c/a\u003e •\n  \u003ca href=\"#try-it-\"\u003eTry it !\u003c/a\u003e •\n  \u003ca href=\"#get-started\"\u003eGet Started\u003c/a\u003e •\n  \u003ca href=\"#modify-indexed-packages\"\u003eModify indexed packages\u003c/a\u003e •\n  \u003ca href=\"#faq\"\u003eFAQ\u003c/a\u003e •\n  \u003ca href=\"#a-word-about-supply-chain-attacks\"\u003eA word about supply chain attacks\u003c/a\u003e •\n  \u003ca href=\"#contribute\"\u003eContribute\u003c/a\u003e •\n  \u003ca href=\"#references\"\u003eReferences\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n## Features\n\n* **:octocat: Github-hosted**\n* **🚀 Template ready to deploy**\n* **🔆 Easy to use** through Github Actions\n* **🚨 Secure** : Warns you if your package is vulnerable to supply chain attacks\n\n## Description\n\nThis repository is a Github page used as a PyPi index, conform to [PEP503](https://www.python.org/dev/peps/pep-0503/).\n\nYou can use it to group all your packages in one place, and access it easily through `pip`, almost like any other package publicly available !\n\n---\n\n_While the PyPi index is public, private packages indexed here are kept private : you will need Github authentication to be able to retrieve it._\n\n## Try it !\n\nVisit [astariul.github.io/github-hosted-pypi/](http://astariul.github.io/github-hosted-pypi/) and try to install packages indexed there !\n\n---\n\nTry to install the package `public-hello` :\n```console\npip install public-hello --extra-index-url https://astariul.github.io/github-hosted-pypi/\n```\n\nIt will also install the package `mydependency`, automatically ! \n\nTry it with :\n\n```python\nfrom public_hello import hi\nprint(hi())\n```\n\nYou can also install a specific version :\n\n```console\npip install public-hello==0.1 --extra-index-url https://astariul.github.io/github-hosted-pypi/\n```\n\n---\n\nNow try to install the package `private-hello` :\n```console\npip install private-hello --extra-index-url https://astariul.github.io/github-hosted-pypi/\n```\n\n_It will not work, because it's private and only me can access it !_\n\n## Get started\n\n* Use this template and create your own repository :\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/astariul/github-hosted-pypi/generate\"\u003e\u003cimg src=\"https://img.shields.io/badge/%20-Use%20this%20template-green?style=for-the-badge\u0026color=347d39\" alt=\"Use template\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n* Go to `Settings` of your repository, and enable Github Page\n* Customize `index.html` and `pkg_template.html` to your liking\n* You're ready to go ! Visit `\u003cuser\u003e.github.io/\u003crepo_name\u003e` to see your PyPi index\n\n## Modify indexed packages\n\nNow that your PyPi index is setup, you can register / update / delete packages indexed.  \n_Github actions are setup to do it automatically for you._\n\nYou just have to :\n* Go to the `Actions` tab of your repository\n* Click the right workflow (`register` / `update` / `delete`) and trigger it manually\n* Fill the form and start the workflow\n* Wait a bit\n* Check the new PR opened (ensure the code added correspond to what you want)\n* Merge the PR\n\n## FAQ\n\n#### Q. Is it secure ?\n\nAs you may know, `pip` can install Github-hosted package if given in the form `pip install git+\u003crepo_link\u003e`. This PyPi index is just an index of links to other Github repository.\n\nGithub pages are public, so this PyPi index is public. But it just contain links to other Github repositories, no code is hosted on this PyPi index !\n\nIf the repository hosting code is private, you will need to authenticate with Github to be able to clone it, effectively making it private.\n\n---\n\nIf you wonder more specifically about supply chain attacks, check [the section about it](#a-word-about-supply-chain-attacks) !\n\n#### Q. What happen behind the scenes ?\n\nWhen running `pip install \u003cpackage_name\u003e --extra-index-url https://astariul.github.io/github-hosted-pypi/`, the following happen :\n\n1. `pip` will look at `https://pypi.org/`, the default, public index, trying to find a package with the specified name.\n2. If it can't find, it will look at `https://astariul.github.io/github-hosted-pypi/`.\n3. If the package is found there, the link of the package is returned to `pip` (`git+\u003crepo_link\u003e@\u003ctag\u003e`).\n4. From this link, `pip` understand it's a Github repository and will clone the repository (at the specific tag) locally.\n5. From the cloned repository, `pip` install the package.\n6. `pip` install any missing dependency with the same steps.\n\n_Authentication happen at step 4, when cloning the repository._\n\n#### Q. What are the best practices for using this PyPi index ?\n\nThe single best practice is using Github releases. This allow your package to have a version referred by a specific tag.  \nTo do this :\n\n* Push your code in a repository.\n* Create a new Github release. Ensure you follow [semantic versioning](https://semver.org/). It will create a tag.\n* Ensure you can install the package with `pip install git+\u003crepo_link\u003e@\u003ctag\u003e`\n* When putting the package on this index, put the full link (`git+\u003crepo_link\u003e@\u003ctag\u003e`).\n\n#### Q. What if the name of my package is already taken by a package in the public index ?\n\nYou can just specify a different name for your indexed package. Just give it a different name in the form when registering it.\n\nFor example if you have a private package named `tensorflow`, when you register it in this index, you can name it `my_cool_tensorflow`, so there is no name-collision with the public package `tensorflow`.  \nThen you can install it with `pip install my_cool_tensorflow --extra-index-url https://astariul.github.io/github-hosted-pypi/`.\n\nThen from `python`, you can just do :\n```python\nimport tensorflow\n```\n\n---\n\n**But be careful about this !** While it's possible to handle it like this, it's always better to have a unique name for your package, to avoid confusion but also for [security](#a-word-about-supply-chain-attacks) !\n\n#### Q. How to download private package from Docker ?\n\nBuilding a Docker image is not interactive, so there is no prompt to type username and password.  \nInstead, you should put your Github credentials in a `.netrc` file, so `pip` can authenticate when cloning from Github.  \nTo do this securely on Docker, you should use Docker secrets. Here is a quick tutorial on how to do :\n\n**Step 1** : Save your credentials in a secret file. Follow this example :\n\n```\nmachine github.com\n\tlogin \u003cgh_user\u003e\n\tpassword \u003cgh_pass\u003e\n```\n\n⚠️ _Syntax is important : ensure you're using **tabulation**, and the line endings are **`\\n`**.  \nSo careful if you're using a IDE that replace tabs by spaces or if you're on Windows (where line endings are `\\r\\n`) !_\n\nLet's name this file `gh_auth.txt`.\n\n**Step 2** : Create your Docker file. In the docker file you should mount the secret file in `.netrc`, and run the command where you need authentication. For example :\n\n```dockerfile\n# syntax=docker/dockerfile:experimental\nFROM python:3\n\nRUN --mount=type=secret,id=gh_auth,dst=/root/.netrc pip install \u003cpackage_name\u003e --extra-index-url https://astariul.github.io/github-hosted-pypi/\n```\n\n**Step 3** : Build your Docker image, specifying the location of the secret created in step 1 :\n\n`sudo DOCKER_BUILDKIT=1 docker build --secret id=gh_auth,src=./gh_auth.txt .`\n\n---\n\n**_If you have any questions or ideas to improve this FAQ, please open a PR / blank issue !_**\n\n## A word about supply chain attacks\n\nAs you saw earlier, this github-hosted PyPi index rely on the `pip` feature `--extra-index-url`. Because of how this feature works, it is vulnerable to supply chain attacks.\n\nFor example, let's say you have a package named `fbi_package` version `2.8.3` hosted on your private PyPi index.\n\nAn attacker could create a malicious package with the same name and a higher version (for example `99.0.0`).  \nWhen you run `pip install fbi_package --extra-index-url my_pypi_index.com`, under the hood `pip` will download the latest version of the package, which is the malicious package !\n\n---\n\nWhile this repository makes it very convenient to have your own PyPi index, be aware that the page is public, therefore anyone can see which package name you're using and create a malicious package with this same name...\n\nThat's why we included automated checks into this private PyPi index. Whenever you access the page of your package, PyPi API is called, and if a package with the same name and a higher version is found, the install command is replaced with a warning.\n\nYou can see a demo of such warning at [https://astariul.github.io/github-hosted-pypi/transformers/](https://astariul.github.io/github-hosted-pypi/transformers/).\n\nIf you see this warning, don't install the package ! Instead, change the name of your package or upgrade the version above its public counterpart.\n\nBe careful out there !\n\n## Contribute\n\nIssues and PR are welcome !\n\nIf you come across anything weird / that can be improved, please get in touch !\n\n## References\n\n**This is greatly inspired from [this repository](https://github.com/ceddlyburge/python-package-server).**  \nIt's just a glorified version, with cleaner pages and github actions for easily adding, updating and removing packages from your index.\n\nAlso check the [blogpost](https://www.freecodecamp.org/news/how-to-use-github-as-a-pypi-server-1c3b0d07db2/) of the original author !\n\n---\n\n_Icon used in the page was made by [Freepik](https://www.flaticon.com/authors/freepik) from [Flaticon](https://www.flaticon.com/)_\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fastariul%2Fgithub-hosted-pypi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fastariul%2Fgithub-hosted-pypi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fastariul%2Fgithub-hosted-pypi/lists"}