Skip to content
GitRiverGitRiver
RU
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.