Files
metin-release-cli/docs/cli.md
2026-04-14 18:59:50 +02:00

3.8 KiB

metin-release — CLI reference

Phase 1 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:

{
  "ok": true,
  "command": "release inspect",
  "status": "inspected",
  "stats": { "...": "..." }
}

On failure:

{
  "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]