docs: add metin release cli plan
This commit is contained in:
466
docs/metin-release-cli-plan.md
Normal file
466
docs/metin-release-cli-plan.md
Normal file
@@ -0,0 +1,466 @@
|
||||
# metin-release-cli plan
|
||||
|
||||
Plan for a release orchestration toolchain that stays outside NewERP and lets
|
||||
the ERP module act only as the control plane.
|
||||
|
||||
## Goal
|
||||
|
||||
Build two components:
|
||||
|
||||
- `metin-release-cli` as the real operator and automation entry point
|
||||
- `metin-release-mcp` as a thin MCP wrapper over the CLI
|
||||
|
||||
The ERP module should not own manifest generation, signing, blob publishing, or
|
||||
launcher packaging logic. It should call the CLI through a stable contract and
|
||||
store release metadata, status, logs, and audit trail.
|
||||
|
||||
## Why this split
|
||||
|
||||
Current release concerns already live across multiple repos and runtimes:
|
||||
|
||||
- `metin-launcher` consumes signed `manifest.json` + blob storage
|
||||
- `m2dev-client` already has `make-manifest.py` and `sign-manifest.py`
|
||||
- `m2pack-secure` owns `.m2p` build, verify, diff, and runtime-key export
|
||||
- launcher self-update currently depends on Velopack packaging, which is still
|
||||
Windows-host constrained
|
||||
|
||||
Putting that logic into NewERP would duplicate the release path and make the ERP
|
||||
module too heavy. The CLI should own the workflow. ERP should orchestrate it.
|
||||
|
||||
## Design principles
|
||||
|
||||
- CLI first. Every real operation must be runnable without MCP.
|
||||
- JSON first. Every non-interactive CLI command must support machine-readable
|
||||
output.
|
||||
- Thin wrapper. MCP must call CLI commands, parse JSON, and return it. No
|
||||
duplicated business logic.
|
||||
- Explicit state. Releases move through named stages and emit durable logs.
|
||||
- Secret isolation. Signing keys and content keys stay on the release machine or
|
||||
in a secret store, never in ERP DB.
|
||||
- Linux-first. Manifest build, signing, blob publish, and `.m2p` validation must
|
||||
run on Linux. Windows-only launcher packaging remains a separate step.
|
||||
|
||||
## Scope
|
||||
|
||||
`metin-release-cli` should cover:
|
||||
|
||||
- create and validate a release workspace
|
||||
- build or import a release manifest
|
||||
- sign the manifest
|
||||
- diff against the currently published release
|
||||
- upload missing blobs to the update storage
|
||||
- archive historical manifests
|
||||
- promote a release to current
|
||||
- verify the public endpoint after publish
|
||||
- optionally record release metadata in ERP through HTTP API
|
||||
- optionally manage `.m2p` release artifacts and runtime-key export
|
||||
- optionally run launcher publish as a separate command
|
||||
|
||||
`metin-release-mcp` should cover:
|
||||
|
||||
- expose the same operations as MCP tools
|
||||
- map tool input to CLI flags
|
||||
- read CLI JSON output
|
||||
- pass through logs, status, and errors in structured form
|
||||
|
||||
## Non-goals
|
||||
|
||||
- No manifest generation inside ERP PHP code
|
||||
- No signing inside ERP
|
||||
- No heavy artifact proxying through ERP uploads for multi-GB releases
|
||||
- No duplicated publish logic in MCP
|
||||
- No attempt to hide Windows constraints for Velopack launcher packaging
|
||||
|
||||
## Canonical release path
|
||||
|
||||
For the current launcher contract, the canonical asset release path is:
|
||||
|
||||
1. obtain a prepared client root on Linux
|
||||
2. generate `manifest.json`
|
||||
3. sign `manifest.json`
|
||||
4. compute which content-addressed blobs are missing remotely
|
||||
5. upload missing blobs
|
||||
6. upload archived manifest under `manifests/<version>.json`
|
||||
7. upload archived signature under `manifests/<version>.json.sig`
|
||||
8. switch top-level `manifest.json` and `manifest.json.sig`
|
||||
9. verify the public endpoint
|
||||
10. report final release metadata to ERP
|
||||
|
||||
For launcher self-update, the path is separate:
|
||||
|
||||
1. build win-x64 launcher package on Windows-capable host
|
||||
2. publish Velopack feed into `/launcher/`
|
||||
3. optionally annotate the same release in ERP
|
||||
|
||||
## Proposed architecture
|
||||
|
||||
### 1. metin-release-cli
|
||||
|
||||
Suggested implementation language:
|
||||
|
||||
- Python is the pragmatic default because the current manifest tooling already
|
||||
exists in Python
|
||||
- shell scripts may wrap platform-specific steps, but the primary interface
|
||||
should be one CLI executable with stable subcommands
|
||||
|
||||
Suggested internal modules:
|
||||
|
||||
- `workspace`
|
||||
- resolve source roots, temp dirs, release output dirs
|
||||
- `manifest`
|
||||
- call or absorb `make-manifest.py`
|
||||
- `signing`
|
||||
- call or absorb `sign-manifest.py`
|
||||
- `storage`
|
||||
- remote existence checks
|
||||
- blob upload
|
||||
- manifest archive upload
|
||||
- current manifest promotion
|
||||
- `verify`
|
||||
- verify local artifacts
|
||||
- verify public HTTP endpoint
|
||||
- `erp`
|
||||
- create/update release records in ERP
|
||||
- `m2pack`
|
||||
- optional integration with `m2pack-secure`
|
||||
- `launcher`
|
||||
- optional launcher publish integration
|
||||
|
||||
### 2. metin-release-mcp
|
||||
|
||||
Suggested shape:
|
||||
|
||||
- one MCP server process
|
||||
- each tool maps 1:1 to one CLI subcommand
|
||||
- wrapper only:
|
||||
- validates tool input
|
||||
- spawns CLI
|
||||
- parses JSON stdout
|
||||
- returns structured result
|
||||
- forwards stderr as diagnostic text
|
||||
|
||||
No release decision logic should live here.
|
||||
|
||||
## Release state model
|
||||
|
||||
The CLI should emit explicit states the ERP module can mirror:
|
||||
|
||||
- `draft`
|
||||
- `scanning`
|
||||
- `manifest_built`
|
||||
- `signed`
|
||||
- `uploading_blobs`
|
||||
- `uploaded`
|
||||
- `promoting`
|
||||
- `published`
|
||||
- `verifying`
|
||||
- `verified`
|
||||
- `failed`
|
||||
- `rolled_back`
|
||||
|
||||
Each state transition should include:
|
||||
|
||||
- `release_id` if known in ERP
|
||||
- `version`
|
||||
- `started_at`
|
||||
- `finished_at`
|
||||
- `duration_ms`
|
||||
- `step`
|
||||
- `status`
|
||||
- `message`
|
||||
- `log_path` if available
|
||||
|
||||
## CLI command set
|
||||
|
||||
Suggested commands:
|
||||
|
||||
### Release lifecycle
|
||||
|
||||
- `metin-release release init`
|
||||
- create local workspace metadata for a new version
|
||||
- `metin-release release inspect`
|
||||
- inspect source root, file counts, launcher presence, total bytes
|
||||
- `metin-release release build-manifest`
|
||||
- generate canonical `manifest.json`
|
||||
- `metin-release release sign`
|
||||
- sign `manifest.json`
|
||||
- `metin-release release diff-remote`
|
||||
- compare manifest blobs against remote storage
|
||||
- `metin-release release upload-blobs`
|
||||
- upload only missing blobs
|
||||
- `metin-release release upload-manifest-archive`
|
||||
- upload `manifests/<version>.json` and `.sig`
|
||||
- `metin-release release promote`
|
||||
- switch current `manifest.json` and `.sig`
|
||||
- `metin-release release verify-public`
|
||||
- fetch public manifest, signature, and optional blob samples
|
||||
- `metin-release release publish`
|
||||
- composite command executing the full asset publish flow
|
||||
- `metin-release release rollback`
|
||||
- promote a historical manifest back to current
|
||||
|
||||
### ERP sync
|
||||
|
||||
- `metin-release erp create-release`
|
||||
- `metin-release erp update-release`
|
||||
- `metin-release erp append-log`
|
||||
- `metin-release erp mark-status`
|
||||
|
||||
### m2pack integration
|
||||
|
||||
- `metin-release m2pack build`
|
||||
- `metin-release m2pack verify`
|
||||
- `metin-release m2pack diff`
|
||||
- `metin-release m2pack export-runtime-key`
|
||||
|
||||
### Launcher integration
|
||||
|
||||
- `metin-release launcher publish`
|
||||
- separate path because of Windows packaging constraints
|
||||
|
||||
## JSON contract
|
||||
|
||||
Every automation-facing command should support:
|
||||
|
||||
```text
|
||||
--json
|
||||
```
|
||||
|
||||
Success output shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"command": "release publish",
|
||||
"version": "2026.04.14-1",
|
||||
"status": "published",
|
||||
"artifacts": {
|
||||
"manifest_path": "/abs/path/manifest.json",
|
||||
"signature_path": "/abs/path/manifest.json.sig"
|
||||
},
|
||||
"stats": {
|
||||
"file_count": 123,
|
||||
"blob_count": 9,
|
||||
"uploaded_blob_count": 3,
|
||||
"uploaded_bytes": 1048576
|
||||
},
|
||||
"remote": {
|
||||
"manifest_url": "https://updates.jakubkadlec.dev/manifest.json"
|
||||
},
|
||||
"erp": {
|
||||
"release_id": 42
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Failure output shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": false,
|
||||
"command": "release publish",
|
||||
"version": "2026.04.14-1",
|
||||
"status": "failed",
|
||||
"error": {
|
||||
"code": "blob_upload_failed",
|
||||
"message": "Failed to upload one or more blobs"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Exit code rules:
|
||||
|
||||
- `0` success
|
||||
- `1` operator or validation error
|
||||
- `2` remote or network error
|
||||
- `3` signing or integrity error
|
||||
- `4` ERP sync error
|
||||
|
||||
## ERP integration contract
|
||||
|
||||
The ERP module should integrate with the CLI at the orchestration layer only.
|
||||
|
||||
Recommended boundary:
|
||||
|
||||
- ERP creates a draft release record
|
||||
- ERP stores operator intent, notes, target channel, and visibility
|
||||
- ERP triggers the CLI through a worker, SSH command, queue job, or agent
|
||||
- CLI performs release operations
|
||||
- CLI pushes structured status updates back to ERP
|
||||
- ERP renders the timeline, logs, artifacts, and rollback actions
|
||||
|
||||
Recommended ERP-owned data:
|
||||
|
||||
- release version
|
||||
- notes
|
||||
- previous release reference
|
||||
- publish status
|
||||
- operator identity
|
||||
- started/finished timestamps
|
||||
- manifest metadata
|
||||
- remote verification status
|
||||
- launcher publish status
|
||||
- pointers to logs and artifacts
|
||||
|
||||
CLI-owned data:
|
||||
|
||||
- actual manifest generation
|
||||
- signing
|
||||
- file hashing
|
||||
- blob deduplication
|
||||
- upload semantics
|
||||
- runtime-key export
|
||||
- launcher packaging invocation
|
||||
|
||||
## Remote storage contract
|
||||
|
||||
For the current launcher path, the CLI should treat this as canonical:
|
||||
|
||||
- `/var/www/updates.jakubkadlec.dev/files/<hash-prefix>/<sha256>`
|
||||
- `/var/www/updates.jakubkadlec.dev/manifests/<version>.json`
|
||||
- `/var/www/updates.jakubkadlec.dev/manifests/<version>.json.sig`
|
||||
- `/var/www/updates.jakubkadlec.dev/manifest.json`
|
||||
- `/var/www/updates.jakubkadlec.dev/manifest.json.sig`
|
||||
- `/var/www/updates.jakubkadlec.dev/launcher/*`
|
||||
|
||||
Promotion safety rule:
|
||||
|
||||
- upload immutable blobs first
|
||||
- upload archived versioned manifest second
|
||||
- replace current manifest last
|
||||
|
||||
## Secrets and security
|
||||
|
||||
- launcher manifest signing key remains outside ERP
|
||||
- `.m2p` master keys remain outside ERP
|
||||
- CLI reads secrets from:
|
||||
- fixed secure file paths
|
||||
- environment variables
|
||||
- secret manager integration later
|
||||
- CLI must redact secrets from logs and JSON output
|
||||
|
||||
Never return:
|
||||
|
||||
- raw private keys
|
||||
- runtime master keys
|
||||
- decrypted secret material
|
||||
|
||||
## Linux and Wine posture
|
||||
|
||||
Supported on Linux now:
|
||||
|
||||
- client tree scan
|
||||
- manifest build
|
||||
- manifest signing
|
||||
- blob upload and promotion
|
||||
- `.m2p` build and verification
|
||||
- Wine-based smoke validation if needed
|
||||
|
||||
Not fully Linux-native yet:
|
||||
|
||||
- Velopack packaging for win-x64 launcher release
|
||||
|
||||
Implication:
|
||||
|
||||
- asset release commands should be Linux-first
|
||||
- launcher publish should remain an explicit separate command and may require a
|
||||
Windows runner or Windows host until the toolchain changes
|
||||
|
||||
## MCP tool set
|
||||
|
||||
Suggested MCP tools:
|
||||
|
||||
- `release_init`
|
||||
- `release_inspect`
|
||||
- `release_build_manifest`
|
||||
- `release_sign`
|
||||
- `release_diff_remote`
|
||||
- `release_upload_blobs`
|
||||
- `release_promote`
|
||||
- `release_verify_public`
|
||||
- `release_publish`
|
||||
- `release_rollback`
|
||||
- `erp_create_release`
|
||||
- `erp_update_release`
|
||||
- `m2pack_build`
|
||||
- `m2pack_verify`
|
||||
- `m2pack_diff`
|
||||
- `m2pack_export_runtime_key`
|
||||
- `launcher_publish`
|
||||
|
||||
Each tool should document the exact CLI command it runs.
|
||||
|
||||
## Recommended implementation phases
|
||||
|
||||
### Phase 1. Asset release CLI
|
||||
|
||||
Build first:
|
||||
|
||||
- `release inspect`
|
||||
- `release build-manifest`
|
||||
- `release sign`
|
||||
- `release diff-remote`
|
||||
- `release upload-blobs`
|
||||
- `release promote`
|
||||
- `release verify-public`
|
||||
- `release publish`
|
||||
|
||||
This is the minimum path ERP needs for the current launcher contract.
|
||||
|
||||
### Phase 2. ERP sync
|
||||
|
||||
Build second:
|
||||
|
||||
- `erp create-release`
|
||||
- `erp update-release`
|
||||
- CLI status callbacks or polling contract
|
||||
|
||||
This lets the ERP module focus on UI, permissions, and audit trail.
|
||||
|
||||
### Phase 3. MCP wrapper
|
||||
|
||||
Build third:
|
||||
|
||||
- expose the Phase 1 and 2 CLI commands as MCP tools
|
||||
- no new logic
|
||||
|
||||
### Phase 4. m2pack path
|
||||
|
||||
Build fourth:
|
||||
|
||||
- `m2pack build`
|
||||
- `m2pack verify`
|
||||
- `m2pack diff`
|
||||
- `m2pack export-runtime-key`
|
||||
|
||||
Only after the signed-manifest release path is stable.
|
||||
|
||||
### Phase 5. Launcher release path
|
||||
|
||||
Build fifth:
|
||||
|
||||
- `launcher publish`
|
||||
- Windows-capable execution environment
|
||||
- optional ERP annotation
|
||||
|
||||
## Open decisions
|
||||
|
||||
- Whether the CLI should directly push status to ERP or ERP should poll CLI job
|
||||
results
|
||||
- Whether release workspaces are local-only or persisted on a shared release
|
||||
host
|
||||
- Whether `.m2p` artifacts become part of the same release object now or later
|
||||
- Whether launcher release should be represented as:
|
||||
- separate release type
|
||||
- or a child job under the same release
|
||||
|
||||
## Recommendation
|
||||
|
||||
Start with:
|
||||
|
||||
- one Linux-first `metin-release-cli`
|
||||
- one thin `metin-release-mcp`
|
||||
- ERP module consuming only CLI results and statuses
|
||||
|
||||
Do not start by embedding release logic in ERP. That will create a second
|
||||
source of truth and make later `.m2p` and launcher evolution harder.
|
||||
Reference in New Issue
Block a user