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.
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.