docs: add self-contained usage guide #2

Closed
forkfixer-kaki wants to merge 1 commit from forkfixer-kaki/dynapt:docs/self-contained-readme into master

296
README.md
View file

@ -1,3 +1,297 @@
# dynapt
**Dyn**amic **APT** repository
**Dyn**amic **APT** repository.
> [!WARNING]
> dynapt is experimental. The configuration format and package compatibility can change, and the project does not yet provide a stable CLI/GUI or APT transport.
dynapt runs a local APT repository server. When APT requests repository metadata, dynapt downloads the configured packages, generates Debian repository metadata, and serves the resulting files over HTTP.
It supports direct links to DEB packages and AppImages. AppImages are extracted and wrapped into DEB packages using their desktop entry and icon. Packages from GitHub releases can also be downloaded and filtered by file name.
dynapt is not a package manager. It makes packages available to APT, so upgrades, searches, installation, and removal can continue using `apt`, `aptitude`, Synaptic, or other APT frontends.
## Requirements
- Linux with APT.
- `amd64` (`x86_64`) or `arm64` (`aarch64`).
- The repository commands rely on `bash`, `coreutils`, `dpkg`, `dpkg-scanpackages`, `apt-ftparchive`, `apt`, `dpkg-deb`, `unsquashfs`, `file`, `chmod`, and `ln`.
`apt-ftparchive` is provided by `apt-utils`. Install it first on minimal systems where `apt` did not pull it in:
```bash
sudo apt update
sudo apt install apt-utils
```
The APT source below uses `trusted=yes` because dynapt currently does not sign repository metadata. Only use this setup on a trusted local machine and do not expose the dynapt port to the network.
## Installation
Download and install the current development release automatically:
```bash
wget https://git.kaki87.net/KaKi87/dynapt/releases/download/0.0.0-dev.9b8fa75/dynapt-"$(dpkg --print-architecture)".deb
sudo apt install ./dynapt-"$(dpkg --print-architecture)".deb
```
Or download the package manually for:
- [`amd64` (`x86_64`)](https://git.kaki87.net/KaKi87/dynapt/releases/download/0.0.0-dev.9b8fa75/dynapt-amd64.deb)
- [`arm64` (`aarch64`)](https://git.kaki87.net/KaKi87/dynapt/releases/download/0.0.0-dev.9b8fa75/dynapt-arm64.deb)
The packages are also listed on the [release page](https://git.kaki87.net/KaKi87/dynapt/releases).
## Configuration
Create `/opt/dynapt/config.json`:
```bash
sudo nano /opt/dynapt/config.json
```
Start with an empty application list:
```json
{
"port": 3000,
"concurrency": null,
"apps": []
}
```
Change `port` if another service already uses port 3000. `concurrency` limits how many applications are downloaded and packaged at the same time. Use `1` on slower connections or lower-powered devices, or `null` to let all applications run concurrently.
Add the local repository to APT. Replace `3000` if you changed the port in `config.json`:
```bash
echo "deb [trusted=yes] http://127.0.0.1:3000/ /" | sudo tee /etc/apt/sources.list.d/dynapt.list
```
Start the server. It must run as a user that can write to `/opt/dynapt`:
```bash
sudo nohup dynapt >/dev/null 2>&1 &
```
Make it start at boot. Run this as `root`:
```bash
crontab -l | { cat; echo "@reboot dynapt"; } | crontab -
```
The first time APT reads the repository, dynapt downloads and packages the configured applications. It can take longer than a normal APT mirror update.
## Application configuration
Each item in `apps` configures one upstream application.
A direct DEB link uses `url`:
```json
{
"name": "Example",
"url": "https://example.com/download/package.deb"
}
```
A direct AppImage link must set `appimage` to `true`. If the command name should be available in `PATH`, set an object with `bin`:
```json
{
"name": "Example",
"url": "https://example.com/download/package.AppImage",
"appimage": {
"bin": "example"
}
}
```
A GitHub release uses `github` instead of `url`:
```json
{
"name": "Example",
"github": {
"repo": "example/example",
"filter": "*.deb",
"prerelease": true
}
}
```
If a direct URL returns JSON instead of a package, use a `jq` expression to extract the package URL:
```json
{
"name": "Example",
"url": "https://example.com/download/latest.json",
"jq": ".downloads[0].url",
"appimage": true
}
```
### Configuration fields
| Field | Required | Description |
| --- | --- | --- |
| `name` | Yes | Display name used by dynapt. |
| `url` | One of `url` or `github` | Direct HTTPS download URL for a DEB or AppImage. |
| `github.repo` | With `github` | GitHub repository in `owner/name` format. |
| `github.filter` | No | Glob expression used to select release assets. |
| `github.prerelease` | No | Inspect prereleases when `true`. |
| `appimage` | No | Use `true` for an AppImage, or an object containing `bin`. |
| `appimage.bin` | No | Command name to link from `/usr/bin`. |
| `jq` | No | Applied to JSON returned by `url`; requires `url`. |
| `id` | No | Internal UUID used for cache metadata. dynapt creates it automatically. |
If APT updates many GitHub-hosted applications, GitHub API rate limits can interrupt metadata requests. Add a personal access token to the root configuration:
```json
{
"githubAccessToken": "github_pat_example_placeholder"
}
```
## Complete example
The following example contains direct-download configurations that have been useful in the current experimental stage:
```json
{
"port": 3000,
"concurrency": null,
"apps": [
{
"name": "Discord",
"url": "https://discord.com/api/download?platform=linux&format=deb"
},
{
"name": "Free Download Manager",
"url": "https://files2.freedownloadmanager.org/6/latest/freedownloadmanager.deb"
},
{
"name": "kDrive",
"url": "https://www.infomaniak.com/drive/latest",
"jq": ".[\"linux-amd64\"].downloadurl",
"appimage": true
},
{
"name": "Kiro",
"url": "https://prod.download.desktop.kiro.dev/stable/metadata-linux-x64-deb-stable.json",
"jq": ".releases[].updateTo.url | select(endswith(\".deb\"))"
},
{
"name": "LM Studio",
"url": "https://lmstudio.ai/download/latest/linux/x64",
"appimage": true
}
]
}
```
For an `arm64` kDrive client, replace `linux-amd64` in its `jq` filter with `linux-arm64`. The LM Studio example shown here uses the current `x64` download URL.
## Using the repository
After dynapt is configured, refresh APT and install packages using their Debian package names:
```bash
sudo apt update
apt search example
sudo apt install example-package
```
For AppImages, dynapt derives the package name, version, description, architecture, and icon from the desktop entry inside the AppImage. Direct DEBs retain the metadata supplied by their upstream.
The server generates repository metadata when APT requests `/Packages` or `/Release`. Downloads are cached and ETag/GitHub release metadata is reused on later requests.
## Commands
dynapt also includes commands for debugging and maintenance.
Fetch and package applications without first installing an APT package:
```bash
sudo dynapt update
```
Show configured applications and compare their available versions with the versions installed through APT:
```bash
sudo dynapt list
```
Remove downloaded files, generated package data, and cache data:
```bash
sudo dynapt clean
```
Refresh only the dynapt APT source:
```bash
sudo apt update \
-o Dir::Etc::sourcelist="sources.list.d/dynapt.list" \
-o Dir::Etc::sourceparts="-" \
-o APT::Get::List-Cleanup="0"
```
## Troubleshooting
### Logs
Read `/opt/dynapt/dynapt.log` and include the relevant lines together with the APT output when asking for help.
### Download failures
Try `sudo dynapt update` to see fetch, download, AppImage extraction, or package generation errors without involving APT. A missing `unsquashfs`, `file`, `apt-ftparchive`, or disk space can prevent packaging from completing.
### Package appears changed by upstream
Run `dynapt clean`, then refresh from APT:
```bash
sudo dynapt clean
sudo apt update
```
### GitHub rate limits
Requests to GitHub release metadata can be anonymous or authenticated with `githubAccessToken`. Use a token with the minimum public repository read access and do not include the token in a publicly shared configuration file.
### Port is already in use
Either stop the process using port 3000 or change `port` in `/opt/dynapt/config.json` and update the APT source to use the new port.
## Development
dynapt is written in JavaScript and runs on [Deno](https://deno.com/). Install Deno and the Debian packaging and AppImage tools listed in the requirements.
Run the CLI from a checkout:
```bash
./dynapt --version
./dynapt list
./dynapt update
./dynapt serve
```
When run from source, dynapt uses `config.json` and writes cache and package data beneath the checkout rather than `/opt/dynapt`.
Build both Debian packages:
```bash
./build.sh
```
Deno compiles `main.js` for `x86_64-unknown-linux-gnu` and `aarch64-unknown-linux-gnu`, and `dpkg-deb` creates:
- `build/dynapt-amd64.deb`
- `build/dynapt-arm64.deb`
There is not a separate automated test command at the moment. Changes should be checked against the affected CLI commands, server routes, and the packaging steps they touch.
## Acknowledgements
This README incorporates the experimental setup tutorial originally shared as a [Gist by KaKi87](https://gist.github.com/KaKi87/f620788e9901abbfef4978eb7ad358b4). It is now maintained with the code in this repository, as requested in [issue #1](https://git.kaki87.net/KaKi87/dynapt/issues/1).