Add a Phase 4 'm2pack commands' section to docs/cli.md with each subcommand's flags and a pointer at the m2pack-secure repo for installation. Update README.md with a short m2pack paragraph and append the Phase 4 entry to the Unreleased CHANGELOG section.
221 lines
5.9 KiB
Markdown
221 lines
5.9 KiB
Markdown
# metin-release — CLI reference
|
|
|
|
Phase 1 `release …` commands and Phase 4 `m2pack …` commands. All
|
|
subcommands share the top-level flags.
|
|
|
|
## Top-level flags
|
|
|
|
| Flag | Description |
|
|
|---|---|
|
|
| `--version` | Print package version and exit. |
|
|
| `--json` | Emit only the JSON envelope on stdout (stderr may still carry logs). |
|
|
| `-v`, `--verbose` | Verbose stderr logging. |
|
|
| `-q`, `--quiet` | Suppress stderr logging entirely. |
|
|
|
|
## Output envelope
|
|
|
|
Every command writes a JSON envelope on stdout:
|
|
|
|
```json
|
|
{
|
|
"ok": true,
|
|
"command": "release inspect",
|
|
"status": "inspected",
|
|
"stats": { "...": "..." }
|
|
}
|
|
```
|
|
|
|
On failure:
|
|
|
|
```json
|
|
{
|
|
"ok": false,
|
|
"command": "release inspect",
|
|
"status": "failed",
|
|
"error": { "code": "source_not_found", "message": "..." }
|
|
}
|
|
```
|
|
|
|
## Exit codes
|
|
|
|
| Code | Meaning |
|
|
|---|---|
|
|
| 0 | Success |
|
|
| 1 | Operator or validation error |
|
|
| 2 | Remote / network error |
|
|
| 3 | Signing or integrity error |
|
|
| 4 | Reserved (ERP sync, Phase 2+) |
|
|
|
|
## `release inspect`
|
|
|
|
Scan a client source root and report file counts plus launcher/main-exe detection.
|
|
|
|
```
|
|
metin-release release inspect --source /path/to/client
|
|
```
|
|
|
|
No writes. JSON stats: `source_path`, `file_count`, `total_bytes`, `launcher_present`, `main_exe_present`.
|
|
|
|
## `release build-manifest`
|
|
|
|
Wraps `make-manifest.py` to produce `manifest.json` for a source tree.
|
|
|
|
```
|
|
metin-release release build-manifest \
|
|
--source /path/to/client \
|
|
--version 2026.04.14-1 \
|
|
--out /tmp/release/manifest.json \
|
|
[--previous 2026.04.13-3] \
|
|
[--notes release-notes.md] \
|
|
[--launcher Metin2Launcher.exe] \
|
|
[--created-at 2026-04-14T12:00:00Z]
|
|
```
|
|
|
|
Override the wrapped script path via the `METIN_RELEASE_MAKE_MANIFEST` env var.
|
|
|
|
## `release sign`
|
|
|
|
Wraps `sign-manifest.py`. Requires an absolute path to a chmod-600 raw 32-byte Ed25519 key.
|
|
|
|
```
|
|
metin-release release sign \
|
|
--manifest /tmp/release/manifest.json \
|
|
--key /home/you/.config/metin/launcher-signing-key \
|
|
[--out /tmp/release/manifest.json.sig]
|
|
```
|
|
|
|
Override the wrapped script path via `METIN_RELEASE_SIGN_MANIFEST`.
|
|
|
|
## `release diff-remote`
|
|
|
|
HEADs every unique blob hash from a manifest against `<base-url>/files/<hh>/<hash>`.
|
|
|
|
```
|
|
metin-release release diff-remote \
|
|
--manifest /tmp/release/manifest.json \
|
|
--base-url https://updates.example.com
|
|
```
|
|
|
|
## `release upload-blobs`
|
|
|
|
rsyncs the release directory (excluding `manifest.json` and `.sig`) to a target.
|
|
|
|
```
|
|
metin-release release upload-blobs \
|
|
--release-dir /tmp/release \
|
|
--rsync-target user@host:/var/www/updates/ \
|
|
[--dry-run] [--yes]
|
|
```
|
|
|
|
## `release promote`
|
|
|
|
Pushes only `manifest.json` + `manifest.json.sig` to the target top-level — makes the new release live.
|
|
|
|
```
|
|
metin-release release promote \
|
|
--release-dir /tmp/release \
|
|
--rsync-target user@host:/var/www/updates/ \
|
|
[--dry-run] [--yes]
|
|
```
|
|
|
|
## `release verify-public`
|
|
|
|
GETs `manifest.json` + `manifest.json.sig` from a public URL and verifies the Ed25519 signature. Optionally spot-checks random blobs with `--sample-blobs N`.
|
|
|
|
```
|
|
metin-release release verify-public \
|
|
--base-url https://updates.example.com \
|
|
--public-key <hex-or-path-to-hex-file> \
|
|
[--sample-blobs 5]
|
|
```
|
|
|
|
## `release publish`
|
|
|
|
Composite: build-manifest → sign → stage blob tree → upload-blobs → promote → verify-public. Short-circuits on the first failure; the JSON envelope includes a `stages` array with `{name, status, duration_ms, error?}` per step.
|
|
|
|
```
|
|
metin-release release publish \
|
|
--source /path/to/client \
|
|
--version 2026.04.14-1 \
|
|
--out /tmp/release \
|
|
--key /home/you/.config/metin/launcher-signing-key \
|
|
--rsync-target user@host:/var/www/updates/ \
|
|
--base-url https://updates.example.com \
|
|
--public-key <hex-or-path-to-hex-file> \
|
|
[--previous ...] [--notes ...] [--launcher ...] \
|
|
[--created-at ...] [--sample-blobs N] \
|
|
[--yes] [--force] [--dry-run-upload]
|
|
```
|
|
|
|
## m2pack commands
|
|
|
|
Phase 4 subcommands wrap the `m2pack-secure` binary and translate its
|
|
JSON envelopes into the standard metin-release result envelope. The
|
|
binary is **not** shipped with this CLI — build it from
|
|
[`metin-server/m2pack-secure`](https://gitea.jakubkadlec.dev/metin-server/m2pack-secure)
|
|
and either put it on `PATH` or point at it via the `M2PACK_BINARY`
|
|
environment variable.
|
|
|
|
All m2pack commands pass through `--json` to the real tool, so the
|
|
raw m2pack envelope is always available under `data.m2pack`. When
|
|
m2pack exits non-zero or emits non-JSON output the wrapper raises a
|
|
subprocess error with `m2pack_failed` / `m2pack_invalid_json` /
|
|
`m2pack_empty_output` error codes.
|
|
|
|
### `m2pack build`
|
|
|
|
Build a signed `.m2p` archive from a client asset directory.
|
|
|
|
```
|
|
metin-release m2pack build \
|
|
--input /path/to/client-assets \
|
|
--output /path/to/out.m2p \
|
|
--key /path/to/content.key \
|
|
--sign-secret-key /path/to/signing.sk \
|
|
[--key-id N]
|
|
```
|
|
|
|
### `m2pack verify`
|
|
|
|
Verify an `.m2p` archive's signature (and optionally full-decrypt it).
|
|
|
|
```
|
|
metin-release m2pack verify \
|
|
--archive /path/to/a.m2p \
|
|
[--public-key /path/to/signing.pub] \
|
|
[--key /path/to/content.key]
|
|
```
|
|
|
|
Passing `--key` enables full-decrypt verification; omitting it only
|
|
checks manifest structure and signature.
|
|
|
|
### `m2pack diff`
|
|
|
|
Diff two directories and/or `.m2p` archives. Either side can be a
|
|
directory or an archive; m2pack figures it out.
|
|
|
|
```
|
|
metin-release m2pack diff --left /old --right /new.m2p
|
|
```
|
|
|
|
The wrapper promotes m2pack's added/removed/changed/unchanged counts
|
|
into `data.stats` when available.
|
|
|
|
### `m2pack export-runtime-key`
|
|
|
|
Export a launcher runtime-key payload (json or raw blob) from a master
|
|
content key + signing public key. Used to seed the launcher's bundled
|
|
runtime-key file during release workflows.
|
|
|
|
```
|
|
metin-release m2pack export-runtime-key \
|
|
--key /path/to/content.key \
|
|
--public-key /path/to/signing.pub \
|
|
--output /path/to/runtime-key.json \
|
|
[--key-id N] [--format json|blob]
|
|
```
|
|
|
|
See `docs/key-rotation.md` in `m2pack-secure` for when to re-export
|
|
runtime keys.
|
|
|