Metadata-Version: 2.4
Name: repka-sdk
Version: 1.0.1
Summary: Python SDK and CLI for Repka artifact storage
Project-URL: Homepage, https://github.com/nextgis/repka
Project-URL: Repository, https://github.com/nextgis/repka
Author: NextGIS
License: MIT License
        
        Copyright (c) 2026 NextGIS
        
        Permission is hereby granted, free of charge, to any person obtaining
        a copy of this software and associated documentation files (the
        "Software"), to deal in the Software without restriction, including
        without limitation the rights to use, copy, modify, merge, publish,
        distribute, sublicense, and/or sell copies of the Software, and to
        permit persons to whom the Software is furnished to do so, subject to
        the following conditions:
        
        The above copyright notice and this permission notice shall be
        included in all copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
        EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
        MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
        NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
        LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
        OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
        WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
License-File: LICENSE
Keywords: artifact-storage,nextgis,package-manager,repka
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Archiving :: Packaging
Requires-Python: >=3.8
Requires-Dist: httpx<1.0.0,>=0.27.0
Requires-Dist: keyring<26.0.0,>=25.5.0
Requires-Dist: python-dotenv<2.0.0,>=1.0.1
Requires-Dist: typer<1.0.0,>=0.15.0
Provides-Extra: dev
Requires-Dist: build<2.0.0,>=1.2.0; extra == 'dev'
Requires-Dist: pytest-asyncio<1.0.0,>=0.24.0; extra == 'dev'
Requires-Dist: pytest<9.0.0,>=8.3.0; extra == 'dev'
Requires-Dist: respx<1.0.0,>=0.21.1; extra == 'dev'
Requires-Dist: ruff<1.0.0,>=0.8.0; extra == 'dev'
Requires-Dist: twine<7.0.0,>=6.0.0; extra == 'dev'
Description-Content-Type: text/markdown

# repka-sdk

Python SDK and CLI for Repka artifact storage.

## Features

- Sync client: `RepkaClient`
- Async client: `AsyncRepkaClient`
- CLI: `repka`
- CRUD for repositories, packages, releases
- Asset upload and download
- Server metadata, statistics, asset rights, and sync discovery endpoints
- Repository content helpers for borsch, installer, QGIS, and Debian routes
- Administrative settings, user/group, jobs, sign-code, and Apple certificate
  endpoints are available as raw dictionaries through `client.admin`
- Resolve by id, name and canonical UI/API URL
- `ensure_package`, `ensure_release`, `replace_release`, `add_assets`
- Client-side `latest` reconciliation inside a package
- Config resolution from `.env`, environment variables and system storage

## Installation

```bash
pip install repka-sdk
```

For local development:

```bash
pip install -e .
```

The package name on PyPI is `repka-sdk`. In Python code the import remains
`repka_sdk`.

For development dependencies:

```bash
pip install -e .[dev]
```

Arch Linux packaging files are available in
[packaging/arch](packaging/arch). After a PyPI release, build and install with:

```bash
cd packaging/arch
makepkg -si
```

## Configuration

Read order is fixed:

1. `.env`
2. environment variables
3. system storage
4. built-in `nextgis` server: `https://rm.nextgis.com`

Supported `.env` variables:

- `REPKA_SERVER_URL`
- `REPKA_USERNAME`
- `REPKA_PASSWORD`

Example `.env`:

```dotenv
REPKA_SERVER_URL=https://rm.staging.nextgis.com
REPKA_USERNAME=editor
REPKA_PASSWORD=secret
```

The `.env` file is exposed in the CLI as the reserved server name `dotenv`.
The built-in public server is exposed as the reserved server name `nextgis`.
Use `--server dotenv` to force `.env`; if `REPKA_SERVER_URL` is absent, the CLI
reports a configuration error.

System server configs are stored in the standard config directory under
`repka-sdk/servers.json`. Passwords for named system servers are stored through
`keyring`.

## Sync API

```python
from repka_sdk import BasicAuthProvider
from repka_sdk import RepkaClient
from repka_sdk import ReleaseOptions
from repka_sdk import ReleaseInput

provider = BasicAuthProvider("editor", "secret")

with RepkaClient(auth_provider=provider) as client:
    repositories = client.repositories.list()
    stats = client.server.stats()
    uploaded = client.assets.upload("dist/plugin.zip")
    release = client.releases.ensure(
        "my-package",
        ReleaseInput(
            name="Release 3.2.1",
            tags=["experimental", "latest"],
            options=ReleaseOptions({"dist": "stable"}),
            assets=[uploaded],
        ),
        version_tag="3.2.1",
        enrich_assets=True,
    )
```

Additional server endpoints are exposed directly when automation needs them.
Release publishing itself is repository-type agnostic and goes through
`client.releases` or the `repka release` CLI commands:

```python
with RepkaClient(auth_provider=provider) as client:
    server_options = client.server.options()
    rights = client.assets.rights(42)
    remote_repos = client.sync.list_remote_repositories("borsch")
    remote_packages = client.sync.list_remote_packages(
        "borsch",
        remote_repos[0]["id"],
    )
    users = client.admin.list_users()
```

## Async API

```python
import asyncio

from repka_sdk import AsyncRepkaClient
from repka_sdk import BasicAuthProvider


async def main() -> None:
    provider = BasicAuthProvider("editor", "secret")
    async with AsyncRepkaClient(
        auth_provider=provider,
    ) as client:
        user = await client.auth.whoami()
        print(user.login)


asyncio.run(main())
```

## CLI

Global options:

- `--server`
- `--json`
- `--timeout`
- `--insecure`
- `--verbose`
- `--dry-run`

Examples:

```bash
repka server add staging https://rm.staging.nextgis.com --default
repka server set-url dotenv https://rm.staging.nextgis.com --username editor
repka server get-url dotenv
repka auth login staging --username editor --password-stdin
repka auth login dotenv --username editor --password-stdin --force
repka auth status
repka auth params --json
repka whoami
repka repo list --field id --field type --field name
repka repo list --all
repka repo list --json
repka server list
repka server status
repka server stats --json
repka server remote-repos
repka server remote-packages
repka package ensure --repo my-repo --name my-package --description "SDK managed"
repka release ensure \
  --package my-package \
  --name "Release 1.2.3" \
  --version-tag 1.2.3 \
  --latest \
  --option dist=stable \
  --enrich-assets \
  --file dist/package.zip
repka release get --package my-package "Release 1.2.3"
repka asset list 77 -F id,name,size,downloads
repka asset upload dist/package.zip --json
repka asset download 42 ./downloads/
repka asset download --all 77 ./downloads/
repka asset rights 42 --json
repka browse release 77 --print-url
```

`--dry-run` never performs write operations. It emits the planned action and the
payload that would be sent.

## Examples

See [examples/_common.py](examples/_common.py), synchronous scripts, and async
scripts such as [async_whoami.py](examples/async_whoami.py) and
[async_list_entities.py](examples/async_list_entities.py).

## Tests

```bash
pytest
```

Integration tests are skipped unless `.env` or explicit environment variables
point to a live Repka server.

## Manual Release Smoke Test

These commands exercise the CLI release flow against a configured server. Use a
test repository because the release creation commands modify server state.

```bash
export REPKA_SERVER=staging
export REPKA_REPO=repka-sdk-smoke-repo
export REPKA_PACKAGE=repka-sdk-smoke-package
export REPKA_VERSION=1.0.0-smoke

printf 'repka sdk smoke asset\n' > /tmp/repka-sdk-smoke.txt

repka --server "$REPKA_SERVER" repo list -F id,type,name,description
repka --server "$REPKA_SERVER" package ensure \
  --repo "$REPKA_REPO" \
  --name "$REPKA_PACKAGE" \
  --description "Created by repka-sdk smoke test"

repka --server "$REPKA_SERVER" release create \
  --package "$REPKA_PACKAGE" \
  --name "Smoke $REPKA_VERSION" \
  --version-tag "$REPKA_VERSION" \
  --latest \
  --option channel=smoke \
  --option arch=any \
  --file /tmp/repka-sdk-smoke.txt

repka --server "$REPKA_SERVER" release get \
  --package "$REPKA_PACKAGE" \
  "Smoke $REPKA_VERSION"

repka --server "$REPKA_SERVER" asset list \
  "Smoke $REPKA_VERSION" \
  --package "$REPKA_PACKAGE" \
  -F id,name,size,downloads
```

For idempotent checks, replace `release create` with `release ensure`. Add
`--enrich-assets` when a repeated publish should keep existing files whose names
are absent from the new upload set; files with matching names are replaced. To
test download, take the release ID from `release get` and run:

```bash
repka --server "$REPKA_SERVER" asset download --all <release-id> /tmp
```

## Publishing to PyPI

1. Verify the version in [pyproject.toml](pyproject.toml) and make sure the
   changelog or release notes are ready.
2. Run the local checks:

```bash
ruff check .
ruff format --check .
pytest
```

3. Build and validate the distribution:

```bash
rm -rf dist
python -m build
twine check dist/*
```

4. Optionally publish to TestPyPI first:

```bash
TWINE_USERNAME=__token__ \
TWINE_PASSWORD="$TEST_PYPI_API_TOKEN" \
python -m twine upload --repository testpypi dist/*
```

5. Publish to PyPI:

```bash
TWINE_USERNAME=__token__ \
TWINE_PASSWORD="$PYPI_API_TOKEN" \
python -m twine upload dist/*
```

6. Install the published package in a clean environment and smoke-test the CLI:

```bash
python -m venv /tmp/repka-sdk-release-check
/tmp/repka-sdk-release-check/bin/pip install repka-sdk==1.0.0
/tmp/repka-sdk-release-check/bin/repka --version
/tmp/repka-sdk-release-check/bin/repka server list
```

7. If the PyPI sdist changed, update
   [packaging/arch/PKGBUILD](packaging/arch/PKGBUILD) with the final
   `sha256sum dist/repka_sdk-<version>.tar.gz`.

Build artifact filenames use the normalized Python distribution form, so the
sdist and wheel filenames keep the underscore: `repka_sdk-<version>...`.

## Notes

- Backend uses `packet` internally. The SDK exposes `package`.
- Backend stores release `options` as `key:value|...`. The SDK normalizes them
  to `ReleaseOptions`.
- Release update is file-replacing on the server, so the SDK preserves existing
  asset ids when doing metadata-only updates.
- Clearing all assets from an existing release is intentionally rejected by the
  SDK until the server can safely handle `files: []`; the SDK will not recreate
  a release because that changes its ID.
- `latest` uniqueness is enforced client-side across the whole package.
