Files
moonwell-launcher/docs/production_deploy.md
T
2026-07-28 21:25:06 +04:00

6.1 KiB

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 available on PATH
  • 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:

python -m pip install boto3

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.

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.

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:

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:

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

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

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.

  • 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:

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.