12 KiB
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-clias the real operator and automation entry pointmetin-release-mcpas 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-launcherconsumes signedmanifest.json+ blob storagem2dev-clientalready hasmake-manifest.pyandsign-manifest.pym2pack-secureowns.m2pbuild, 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
.m2pvalidation 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
.m2prelease 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:
- obtain a prepared client root on Linux
- generate
manifest.json - sign
manifest.json - compute which content-addressed blobs are missing remotely
- upload missing blobs
- upload archived manifest under
manifests/<version>.json - upload archived signature under
manifests/<version>.json.sig - switch top-level
manifest.jsonandmanifest.json.sig - verify the public endpoint
- report final release metadata to ERP
For launcher self-update, the path is separate:
- build win-x64 launcher package on Windows-capable host
- publish Velopack feed into
/launcher/ - 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
- call or absorb
signing- call or absorb
sign-manifest.py
- call or absorb
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
- optional integration with
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:
draftscanningmanifest_builtsigneduploading_blobsuploadedpromotingpublishedverifyingverifiedfailedrolled_back
Each state transition should include:
release_idif known in ERPversionstarted_atfinished_atduration_msstepstatusmessagelog_pathif 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
- generate canonical
metin-release release sign- sign
manifest.json
- sign
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>.jsonand.sig
- upload
metin-release release promote- switch current
manifest.jsonand.sig
- switch current
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-releasemetin-release erp update-releasemetin-release erp append-logmetin-release erp mark-status
m2pack integration
metin-release m2pack buildmetin-release m2pack verifymetin-release m2pack diffmetin-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:
--json
Success output shape:
{
"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:
{
"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:
0success1operator or validation error2remote or network error3signing or integrity error4ERP 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
.m2pmaster 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
.m2pbuild 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_initrelease_inspectrelease_build_manifestrelease_signrelease_diff_remoterelease_upload_blobsrelease_promoterelease_verify_publicrelease_publishrelease_rollbackerp_create_releaseerp_update_releasem2pack_buildm2pack_verifym2pack_diffm2pack_export_runtime_keylauncher_publish
Each tool should document the exact CLI command it runs.
Recommended implementation phases
Phase 1. Asset release CLI
Build first:
release inspectrelease build-manifestrelease signrelease diff-remoterelease upload-blobsrelease promoterelease verify-publicrelease publish
This is the minimum path ERP needs for the current launcher contract.
Phase 2. ERP sync
Build second:
erp create-releaseerp 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 buildm2pack verifym2pack diffm2pack 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
.m2partifacts 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.