f834c5fa86
The launcher synchronizes the current native client, retains manifest-listed ruRU archives and launches the game with WOW_LOCALE=auto. Includes the prepared cleanup of obsolete Extentions directories in both client roots while preserving manifest-required files and unrelated user state. Version: 1.0.7+10. Deployment now judges native OpenSSL commands by their exit codes while retaining key and signature verification. Verified on Windows through FVM: full format/analyze, all 76 tests, production dry run, x64 runtime imports and DSA installer signature. The original dirty checkout was preserved. Publication follows the localized client release.
199 lines
7.8 KiB
Markdown
199 lines
7.8 KiB
Markdown
# Production Launcher Deployment
|
|
|
|
`tool/deploy_launcher.ps1` automates a complete Windows launcher release:
|
|
|
|
1. reads `X.Y.Z+N` from `pubspec.yaml`
|
|
2. verifies that the private DSA key matches the public key embedded in the
|
|
launcher
|
|
3. runs dependency resolution, formatting checks, static analysis, and tests
|
|
4. builds the Windows release with the production API and AppCast URLs
|
|
5. compiles the Inno Setup installer
|
|
6. signs the installer and verifies the signature
|
|
7. generates `appcast.xml` with the real version, byte length, URL, and
|
|
signature
|
|
8. uploads the installer to Yandex Object Storage with public read access
|
|
9. downloads the published installer and compares its SHA-256
|
|
10. publishes AppCast through the service API and verifies the public feed
|
|
|
|
The installer is always uploaded before AppCast. Clients therefore never see a
|
|
release that has not passed the remote download and checksum verification.
|
|
|
|
## Prerequisites
|
|
|
|
The release machine needs:
|
|
|
|
- latest stable Flutter and Dart selected through FVM
|
|
- Inno Setup 6, normally installed at
|
|
`C:\Program Files (x86)\Inno Setup 6\ISCC.exe`
|
|
- OpenSSL, normally supplied by Git for Windows at
|
|
`C:\Program Files\Git\usr\bin\openssl.exe`
|
|
- Python 3 with `boto3`
|
|
- `.moonwell_signing\dsa_priv.pem`
|
|
- the matching public key at `windows\runner\resources\dsa_pub.pem`
|
|
|
|
Install the Python dependency if required:
|
|
|
|
```powershell
|
|
python -m pip install boto3
|
|
```
|
|
|
|
The PowerShell entry point calls `tool/upload_launcher_release.py` as a regular
|
|
Python file. Keep this helper beside the deployment script; it performs the S3
|
|
upload and verifies the uploaded object's byte length.
|
|
|
|
Back up `.moonwell_signing\dsa_priv.pem` in the project secret store. Never
|
|
commit it, upload it to Object Storage, or send it through chat. Losing this key
|
|
prevents released launchers from accepting future updates.
|
|
|
|
The deploy script copies the x64 Visual C++ CRT DLLs from Visual Studio's
|
|
redistributable directory into the application folder before compiling the
|
|
installer. Use `-VcRuntimePath` to override the detected directory. This keeps
|
|
the per-user installer usable without installing system runtimes as an admin;
|
|
Windows 10/11 supplies the Universal CRT. See Microsoft's
|
|
[app-local deployment guidance](https://learn.microsoft.com/en-us/cpp/windows/choosing-a-deployment-method?view=msvc-170).
|
|
|
|
## Local `.env`
|
|
|
|
For local releases, copy `.env.example` to `.env` and fill in the real values.
|
|
The repository ignores `.env` and every `.env.*` variant except the safe
|
|
`.env.example` template.
|
|
|
|
```powershell
|
|
Copy-Item .env.example .env
|
|
```
|
|
|
|
The deploy script loads `.env` automatically. Variables already present in the
|
|
current process or CI environment take precedence over the file. Use
|
|
`-EnvFile C:\secure\moonwell.env` to select a different local file.
|
|
|
|
The following values are supported:
|
|
|
|
| Variable | Required | Purpose |
|
|
| --- | --- | --- |
|
|
| `AWS_ACCESS_KEY_ID` | yes | Yandex Object Storage access key |
|
|
| `AWS_SECRET_ACCESS_KEY` | yes | Yandex Object Storage secret |
|
|
| `LAUNCHER_AUTH_KEY` | yes | `Auth` header for the AppCast service endpoint |
|
|
| `AWS_DEFAULT_REGION` | recommended | Defaults to `ru-central1` |
|
|
| `AWS_ENDPOINT` | optional | Defaults to `https://storage.yandexcloud.net` |
|
|
| `AWS_BUCKET` | optional | Defaults to `warcraft-client` |
|
|
| `AWS_USE_PATH_STYLE_ENDPOINT` | optional | Defaults to `true` |
|
|
|
|
Production CI should load these values from the team secret manager instead of
|
|
creating `.env`. Never commit the real file or put its contents into logs.
|
|
|
|
## Prepare a Release
|
|
|
|
Increase both parts of the version in `pubspec.yaml`:
|
|
|
|
```yaml
|
|
version: 1.0.2+3
|
|
```
|
|
|
|
`X.Y.Z` is the public launcher version. `N` is the Flutter build number.
|
|
Flutter places the complete `X.Y.Z+N` value in the Windows `ProductVersion`,
|
|
so the script writes that same complete value to `sparkle:version` and writes
|
|
`X.Y.Z` to `sparkle:shortVersionString`. Every production release must have an
|
|
`X.Y.Z` value greater than the release currently published in AppCast.
|
|
|
|
The UI reads `X.Y.Z` from the packaged application metadata through
|
|
`package_info_plus`. Version labels on the login and launcher screens therefore
|
|
update automatically after changing `pubspec.yaml`; do not add separate
|
|
hardcoded version strings.
|
|
|
|
Run a dry run first:
|
|
|
|
```powershell
|
|
.\tool\deploy_launcher.ps1 `
|
|
-DryRun `
|
|
-ExpectedVersion 1.0.2 `
|
|
-ReleaseNotes "Исправления и улучшения MoonWell Launcher."
|
|
```
|
|
|
|
Dry run performs the local build, packaging, signing, signature verification,
|
|
and AppCast generation. It writes the generated feed to
|
|
`build\launcher_release\appcast.xml` and does not change S3 or the website.
|
|
|
|
## Deploy to Production
|
|
|
|
After reviewing the dry-run artifacts:
|
|
|
|
```powershell
|
|
.\tool\deploy_launcher.ps1 `
|
|
-ExpectedVersion 1.0.2 `
|
|
-ReleaseNotes "Исправления и улучшения MoonWell Launcher."
|
|
```
|
|
|
|
The production defaults are:
|
|
|
|
- API: `https://moon-well.online`
|
|
- AppCast: `https://moon-well.online/appcast.xml`
|
|
- bucket: `warcraft-client`
|
|
- object: `moonwell_launcher_setup.exe`
|
|
- installer:
|
|
`https://storage.yandexcloud.net/warcraft-client/moonwell_launcher_setup.exe`
|
|
|
|
Use a versioned `-S3Key`, such as
|
|
`moonwell_launcher/releases/1.0.4/moonwell_launcher_setup.exe`, to preserve the
|
|
installer referenced by each signed AppCast. After the feed has been verified,
|
|
publish the same verified installer at `moonwell_launcher_setup.exe` for the
|
|
website's stable download link. Set `MOONWELL_CLIENT_OBJECT_KEY` to that key in
|
|
the web app when its download button should install the launcher. The game
|
|
files use their separate client manifest and release prefix.
|
|
|
|
For FVM, `-FlutterCommand` and `-DartCommand` can point to local `.cmd` wrappers
|
|
that run `call fvm flutter %*` and `call fvm dart %*` respectively and return
|
|
`exit /b %errorlevel%`. Keep wrappers inside ignored `build/` and ensure the
|
|
FVM executable is on `PATH` or use its absolute path in the wrapper.
|
|
|
|
On success, the command prints the deployed version, installer URL, SHA-256,
|
|
and AppCast URL. The tracked root `appcast.xml` contains the exact feed sent to
|
|
the server.
|
|
|
|
## Safety Options
|
|
|
|
- `-ExpectedVersion X.Y.Z` prevents deploying an unintended `pubspec.yaml`
|
|
version.
|
|
- The script rejects a release version that is not newer than the public
|
|
AppCast version.
|
|
- `-Force` bypasses that version guard. Use it only to repeat an already
|
|
published version after confirming that replacing the artifact is intended.
|
|
- `-SkipChecks` skips dependency resolution, formatting, analysis, and tests.
|
|
It should not be used for a normal production release.
|
|
- `-FlutterCommand`, `-DartCommand`, `-InnoSetupCommand`, and
|
|
`-OpenSslCommand` override tool locations when they are not on `PATH`.
|
|
|
|
Run `Get-Help .\tool\deploy_launcher.ps1 -Full` or inspect the parameter block
|
|
for all endpoint, bucket, key, and signing-path overrides.
|
|
|
|
## Failure Recovery
|
|
|
|
The script stops on the first failed command.
|
|
|
|
External commands are judged by their exit codes. Windows PowerShell may display
|
|
normal OpenSSL stderr notices as error records; they do not fail a successful
|
|
command. Key matching and installer-signature verification still must pass.
|
|
|
|
- Failure before S3 upload leaves production unchanged.
|
|
- Failure after S3 upload but before AppCast publication leaves clients on the
|
|
preceding AppCast. Fix the issue and rerun the same release.
|
|
- Failure after AppCast publication requires checking both public URLs and the
|
|
DSA signature before using `-Force`.
|
|
- Never publish AppCast manually before the installer is publicly downloadable
|
|
and its SHA-256 matches the local artifact.
|
|
|
|
After deployment, verify:
|
|
|
|
```powershell
|
|
Invoke-WebRequest `
|
|
-Uri "https://moon-well.online/appcast.xml" `
|
|
-UseBasicParsing
|
|
|
|
Invoke-WebRequest `
|
|
-Uri "https://storage.yandexcloud.net/warcraft-client/moonwell_launcher_setup.exe" `
|
|
-Method Head `
|
|
-UseBasicParsing
|
|
```
|
|
|
|
Rotate any infrastructure credential that was exposed in terminal logs, chat,
|
|
or another non-secret channel.
|