Files
moonwell-client-core/crates/moonwell-deploy
sindoring 2bdb1798b7 Sync upstream and restore license agreement distribution
Import 32 upstream commits through a5cb73a8 and include original EULA/TOS in client packages. Windows player build, targeted packaging tests, clippy and offline stock agreement scrolling checks pass. Original data remain outside Git. Full workspace suites not rerun.
2026-10-18 13:51:56 +03:00
..

MoonWell deployment

moonwell-deploy builds MPQ patches, stages the Rust player client, generates the launcher manifest, and publishes it to S3. It uses the same wow-mpq Rust builder as MoonWellClientSources, with MPQ v1 and 4 KiB sectors for 1.12.1. Its small single-unit blocks are converted to stored/sectored blocks, preserving their bytes, because the current client reader rejects single-unit entries. The 3.3.5 source tree and its WarcraftXL DLLs are not vanilla client data.

The source project's .env is read directly with --env-file; credentials are never copied into the package or printed. Process environment values take precedence. Supported settings are AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, optional AWS_SESSION_TOKEN, AWS_DEFAULT_REGION, AWS_BUCKET, AWS_ENDPOINT, and AWS_USE_PATH_STYLE_ENDPOINT. Unquoted Windows paths elsewhere in that file are ignored. WOW_HOME is never used as the vanilla data source.

Windows pipeline

From the repository root:

# Build, stage and generate the local manifest; does not upload.
.\scripts\deploy-moonwell.ps1 -DataPath 'D:\WoW-1.12.1\Data' `
  -IconPath 'D:\Branding\MoonWell.ico' -AuthHost 'login.example:3724'

# Check the S3 upload plan without changing any objects.
.\scripts\deploy-moonwell.ps1 -DataPath 'D:\WoW-1.12.1\Data' `
  -IconPath 'D:\Branding\MoonWell.ico' -AuthHost 'login.example:3724' -DryRun

# Build and publish a complete vanilla distribution.
.\scripts\deploy-moonwell.ps1 -DataPath 'D:\WoW-1.12.1\Data' `
  -IconPath 'D:\Branding\MoonWell.ico' -AuthHost 'login.example:3724' -Publish

The default .env location is the sibling MoonWellClientSources/.env. Override it with -EnvFile. Every run creates a new dist/moonwell-<timestamp> directory; -PackagePath overrides it. The local manifest is adjacent to that directory, with the suffix .manifest.json. Staging never changes the source install or deletes an existing directory. With -SkipClientBuild -BinaryPath, provide an already built player executable. Debug instruments must be disabled in the binary distributed to players.

Optional -PatchSource points to your own source tree containing Data/patch-M/, Data/patch-N/, etc. The Rust builder produces Data/patch-M.MPQ and mounts them through the client's vanilla patch chain. A supplied patch source takes precedence over the same named patch in the input install. Locale subdirectories such as Data/ruRU/patch-ruRU-5 are rejected because MoonWell currently has no locale selection or mounting support.

The Windows script includes vcruntime140.dll from Visual Studio's x64 redistributable directory beside the game executable. -VcRuntimePath can select this directory explicitly. Windows 10/11 supplies the Universal CRT. When staging directly through the Rust CLI, include the required runtime DLL before generating or publishing the manifest.

Only vanilla archives that the client mounts are copied from the Data directory. All ten vanilla base archives are required. Input archives may use MPQ v1/v2, as supported by the client reader; the winning Spell.dbc must have 173 columns. These checks reject a normal 3.3.5 installation; they do not prove that a custom patch's models, Lua, or DBC rows are compatible with 1.12.1. Test custom content in the client before publishing it. Install content remains outside Git.

S3 and Web API

cargo run -p moonwell-deploy -- inspect --env-file ..\MoonWellClientSources\.env
cargo run -p moonwell-deploy -- check-storage --env-file ..\MoonWellClientSources\.env
cargo run -p moonwell-deploy -- publish --dir dist\moonwell-package `
  --env-file ..\MoonWellClientSources\.env --dry-run

check-storage uploads a temporary 10 MiB object under moonwell-deploy-check/, checks that its bytes round-trip correctly, then deletes that object. It does not change any release or active manifest.

The inventory is { "buildHash": "...", "files": [{ "path": "...", "size": 123, "sha256": "...", "objectKey": "..." }] }. buildHash uses the existing Python/launcher contract: sort by path, join path:size:sha256 with newline separators, then SHA-256 the UTF-8 bytes. State, .env files, launcher metadata and partial downloads are excluded. Symlinks and case-insensitive duplicate paths are rejected.

Files are streamed (multipart for large objects) into World of Warcraft 1.12.1/releases/<buildHash>/<relative-path>. The release's manifest.json is written after all files, and manifest-1.12.1.json is switched last. Upload failure leaves the active inventory unchanged. No old objects are deleted, so a download link already issued for an older release keeps working. Run one publisher at a time. To roll back, restore a previous release inventory as the active manifest; keep that release's objects.

Large files use four concurrent 8 MiB upload parts. The publisher checks each part's MD5 and the completed object's size and ETag before publishing the inventory. These ETag checks target the configured unencrypted Yandex S3 objects; SHA-256 remains the launcher manifest's integrity check. Repeating publish --dir <existing-package> --env-file .env reuses completed objects only when their content ETag and size match. Interrupted multipart files are uploaded again; completed files are not transmitted again. Files unchanged from the active release retain their verified immutable objectKey, so publishing an executable or runtime update does not upload the game data again. Keep all releases whose objects are referenced by an inventory.

The Laravel web app resolves a requested path through the active manifest's objectKey and signs that S3 object. Its settings must match:

MOONWELL_LAUNCHER_BASE_PATH="World of Warcraft 1.12.1"
MOONWELL_LAUNCHER_MANIFEST_KEY=manifest-1.12.1.json

The Flutter launcher keeps requesting the existing Web API endpoints. It has no S3 credentials. Inventories without objectKey retain the legacy <base-path>/<relative-path> resolution. The old 3.3.5 manifest.json and World of Warcraft/ keys are reserved and cannot be publication targets.

Native macOS binaries must be built on a Mac and packaged with package_macos.py. stage --binary MoonWell.app can stage such a bundle, but publish it with separate --manifest-key and --prefix settings for a launcher distribution targeting macOS. The current API has one active inventory per configured distribution, not automatic OS selection. This Windows pipeline does not build or sign macOS applications. The current Flutter file downloader also does not restore Unix executable bits; install the .app as a bundle with its permissions preserved when testing macOS.

Checks

cargo fmt -p moonwell-deploy -- --check
cargo clippy -p moonwell-deploy --all-targets -- -D warnings
cargo test -p moonwell-deploy

Tests cover actual client MPQ readability, excluded state and secrets, the manifest contract, path validation, upload failures, dry runs and publication ordering. Live client login remains a separate check.