Navigation
Package Registry
How to publish and install npm, PyPI, Cargo, Maven, NuGet, Composer and Generic packages in GitRiver, and how to serve external packages through an upstream
GitRiver has a built-in package registry for 7 formats. No separate service is needed - packages are stored alongside your code. The same registry can also serve external packages, fetching them from the upstream and caching them.
This is a free feature - available in Community without restrictions.
Why
- Private packages for your team (not published to npm/PyPI)
- Automatic publishing from CI/CD on every release
- Unified access through the same tokens as repositories
- No dependency on external registries
How the token is passed
Every registry call accepts a token in five ways - pick the one your client can do:
| Way | Form |
|---|---|
| Native scheme | Authorization: Bearer <token> |
| GitHub scheme | Authorization: token <token> |
| GitLab clients | the PRIVATE-TOKEN: <token> header |
| HTTP Basic | Authorization: Basic <base64(name:token)> |
| Browser | the session cookie |
In a Basic pair the token goes in the password position, and the username is not checked at all - the identity comes from the token. That is exactly how pip (https://name:token@host), Maven (<password>) and NuGet (ClearTextPassword) put it, which is why the examples below work as they are.
An account password will not do - only a token is accepted: a personal access token or a deploy token. Login-and-password pairs are parsed only by git over HTTP and Git LFS, which have their own rules and their own failed-attempt counter. Publishing requires a token with write access.
npm
For Node.js projects.
Publishing
Create a .npmrc file in your project:
@owner:registry=https://git.example.com/api/v1/packages/owner/repo/npm
//git.example.com/api/v1/packages/owner/repo/npm:_authToken=YOUR_TOKEN
Then:
npm publish
Installation
npm install @owner/package \
--registry https://git.example.com/api/v1/packages/owner/repo/npm
Or add to your project’s .npmrc to avoid specifying it every time:
@owner:registry=https://git.example.com/api/v1/packages/owner/repo/npm
PyPI
For Python projects.
Publishing
pip install twine
twine upload dist/* \
--repository-url https://git.example.com/api/v1/packages/owner/repo/pypi/legacy/ \
-u __token__ -p YOUR_TOKEN
The login is always
__token__, the password is your personal token or deploy token.
Installation
pip install my-package \
--index-url https://git.example.com/api/v1/packages/owner/repo/pypi/simple
Cargo
For Rust projects.
Setup
Add to ~/.cargo/config.toml (or .cargo/config.toml in your project):
[registries.gitriver]
index = "sparse+https://git.example.com/api/v1/packages/owner/repo/cargo/index/"
Publishing
cargo publish --registry gitriver --token YOUR_TOKEN
Installation
In the dependent project’s Cargo.toml:
[dependencies]
my-lib = { version = "1.0", registry = "gitriver" }
Maven
For Java/Kotlin projects.
Publishing
Add to pom.xml:
<distributionManagement>
<repository>
<id>gitriver</id>
<url>https://git.example.com/api/v1/packages/owner/repo/maven/</url>
</repository>
</distributionManagement>
And in ~/.m2/settings.xml:
<servers>
<server>
<id>gitriver</id>
<username>your_username</username>
<password>YOUR_TOKEN</password>
</server>
</servers>
Then:
mvn deploy
Installation
Add the repository to pom.xml:
<repositories>
<repository>
<id>gitriver</id>
<url>https://git.example.com/api/v1/packages/owner/repo/maven/</url>
</repository>
</repositories>
NuGet
For .NET projects.
Publishing
dotnet nuget push MyPackage.1.0.0.nupkg \
--source https://git.example.com/api/v1/packages/owner/repo/nuget \
--api-key YOUR_TOKEN
Installation
Add a source:
dotnet nuget add source \
https://git.example.com/api/v1/packages/owner/repo/nuget/ \
--name gitriver \
--username your_username \
--password YOUR_TOKEN
Then:
dotnet add package MyPackage
Generic
For arbitrary files (binaries, archives, documentation).
Upload
curl -T myapp-v1.0.tar.gz \
-H "Authorization: Bearer YOUR_TOKEN" \
https://git.example.com/api/v1/packages/owner/repo/generic/myapp/1.0.0/myapp-v1.0.tar.gz
Download
curl -L -O \
https://git.example.com/api/v1/packages/owner/repo/generic/myapp/1.0.0/myapp-v1.0.tar.gz
Composer
Added in 1.1.0.
For PHP projects.
Publishing
Publishing is done not by the composer client but by a GitRiver call: the server takes composer.json from the given tag and reads the name and version from it.
curl -X POST \
-H "Authorization: Bearer YOUR_TOKEN" \
"https://git.example.com/api/v1/packages/owner/repo/composer/publish?tag=v1.0.0"
The tag must exist. Permissions - write access to the repository. The response is 201; a byte-identical repeat is 200, a repeat with different content is 409: a published version is immutable.
Installation
In the project’s composer.json:
{
"repositories": [
{ "type": "composer", "url": "https://git.example.com/api/v1/packages/owner/repo/composer" }
],
"require": { "vendor/package": "^1.0" }
}
The URL is without /packages.json: composer appends it itself.
The token for a private repository goes into auth.json, the bearer section:
{ "bearer": { "git.example.com": "YOUR_TOKEN" } }
Proxied package delivery
Added in 1.1.0.
A repository serves not only its own packages but external ones as well: if a package is not in local storage, the server goes to the upstream itself, caches the response and serves it to the client. The client-facing URL is the same as for private packages - there is nothing to change in the build, you simply replace the external registry address with the GitRiver one.
A local package always wins over an external one: your dependency cannot be substituted by an external package of the same name.
An upstream is configured in Repository settings → Packages → Proxied package delivery:
| Type | Upstream URL |
|---|---|
| npm | https://registry.npmjs.org |
| PyPI | https://pypi.org/simple |
| Cargo | https://index.crates.io |
| Maven | https://repo1.maven.org/maven2 |
| NuGet | https://api.nuget.org/v3/index.json |
| Generic | the root of the external storage |
| Docker | https://registry-1.docker.io (without /v2) |
An upstream has allow and deny name patterns, a maximum file size, a metadata lifetime, its own cache size limit and an air-gapped flag. The upstream token is never handed back out - the response only shows whether one is set. Warm-up is requested separately: packages are fetched in advance, without waiting for the first request.
Cargo: with an upstream configured, a token is required even in a public repository - anonymous callers are not let out through the server, and config.json declares auth-required.
Mirror of an external image registry
A mirror repository is pulled with an ordinary docker pull:
docker pull git.example.com/owner/mirror/library/nginx:1.25
Layers sit in the shared storage alongside local images: deduplication, garbage collection and the quota are shared. An anonymous request is never let out. Images that came from the upstream are not picked up by automatic scanning - a mirror would set off an avalanche of scans; scan them on demand with the “Scan” button.
Instance-wide limits and maintenance
Administration → Storage → Proxied package delivery cache: enabling maintenance, the interval between passes (6 hours by default), the metadata retention period (30 days; 0 - never delete by age), the cache size limit (0 - no limit), and the instance-wide air-gapped flag - no upstream is contacted and only warmed-up content is served.
A maintenance pass removes metadata that has not been requested for longer than the retention period, evicts files above the size limits starting with the least recently requested (there are two limits and they nest: one per upstream and one for the instance), and clears finished warm-up requests from the log. The button runs the same pass immediately.
Auto-Publishing from CI/CD
Typical scenario: on push of a v* tag, automatically build and publish the package.
name: Release
on:
push:
tags: ['v*']
jobs:
publish:
image: node:22
steps:
- run: |
echo "//git.example.com/api/v1/packages/$CI_REPOSITORY_OWNER/$CI_REPOSITORY_NAME/npm:_authToken=$CI_JOB_TOKEN" > .npmrc
npm publish
CI_JOB_TOKEN is automatically available in every CI job and has permissions for the current repository’s registry.
Managing Packages
View, versions, and delete packages: “Packages” tab in the repository.