win_store_packer builds and publishes Windows Store AppX packages that already contain the bundled Hagicode Server runtime.
It resolves the latest eligible Desktop and Server releases from the Azure index manifests, maps the selected Desktop release to the exact Desktop Git tag, pre-creates the GitHub Release, prepares tagged Desktop source workspaces, stages the Server payload into resources/portable-fixed/current, builds unsigned and signed AppX variants in parallel, uploads both variants to the same GitHub Release, and publishes the resulting metadata from this repository.
The published AppX package is intentionally treated as Steam mode by default. Desktop switches into distributionMode=steam whenever the packaged extra/portable-fixed/current payload validates, so this Store flow ships that payload as the authoritative runtime source and records distributionMode: "steam" plus runtimeSource: "portable-fixed" in the emitted metadata.
hagicode-desktopis a read-only input. This repository expects it atinputs/hagicode-desktopand tracks it through.gitmodules.- The selected Desktop release version is normalized to the exact Git tag
v<desktop-version-without-leading-v>. - The selected Desktop tag is also the source of truth for Store package versioning. Stable tags like
v0.1.56become the Windows package version0.1.56.0. - Non-stable Desktop tags are rejected for Store packaging because Windows package versions must stay numeric.
- Workspace preparation fails fast if that tag does not exist or cannot be checked out cleanly.
- The final packaged runtime must come from
resources/portable-fixed/current, which electron-builder already maps toextra/portable-fixed/currentinside the Store package. - The packaged
portable-fixed/currentpayload is the contract that makes Desktop start in Steam mode for this distribution.
Defines the Store package identity metadata and packaging contract:
packageIdentity.displayNamepackageIdentity.publisherDisplayNamepackageIdentity.publisherpackageIdentity.identityNamepackageIdentity.backgroundColorpackageIdentity.languagesappx.minVersionappx.maxVersionTestedappx.capabilitiespackageVersion.sourcepackageVersion.revisionsigning.publisherSubjectsigning.verificationScriptRelativePathsigning.azure.*supportedWindowsTargetsdesktop.submodulePathdesktop.electronBuilderConfigPathdesktop.runtimeInjectionPath
The packer targets the current Desktop Windows packaging pipeline directly. It prepares runtime resources, runs Desktop production build steps, then invokes scripts/run-electron-builder.js --win appx --config electron-builder.store.<variant>.yml inside the tagged Desktop workspace.
The Store overlay must preserve the Windows capability declarations required by Hagicode Desktop runtime behavior.
runFullTrust: required for the Electron desktop process.internetClient: required for outbound HTTP/HTTPS access such as Hagicode package indices, runtime downloads, GitHub release assets, Azure-hosted metadata, and RSS feeds.internetClientServer: required for torrent-first sharing acceleration because the packaged client can initiate and accept peer traffic while distributing package payloads.privateNetworkClientServer: required because Desktop manages the bundled web service over loopback and also supports binding to private-network addresses such as0.0.0.0for LAN access.
These capabilities are sourced from config/store-package.json and rendered into the generated electron-builder.store.<variant>.yml overlay before the Desktop AppX build runs.
Defines workflow defaults such as:
- default platforms (
win-x64) - artifact names
- default Desktop source path
- schedule cadence
Workflow: .github/workflows/package-release.yml
Manual dispatch inputs:
desktop_version: optional Desktop version selectorserver_version: optional Server version selectorforce_rebuild: bypass duplicate-release detectiondry_run: build and generate publication metadata without writing GitHub Releases
Scheduled runs use the latest eligible Windows Desktop and Server assets from the configured Azure indexes and skip packaging when the derived Store release tag already exists.
package-release.yml first ensures the GitHub Release exists, then builds the unsigned and signed .appx variants in parallel and uploads both assets to the same release. The Store publish job still uses only the unsigned package and is skipped when dry_run is enabled.
This follows the AppX guidance from electron-builder and Microsoft Store: Store submissions do not need to be manually signed in CI because Partner Center signs the package during Store processing. The workflow therefore uses the official microsoft/store-submission@v1 action, builds the product-update payload from the public GitHub Release asset URLs, and submits the unsigned primary AppX package to Partner Center as a packaged app submission.
The signed variant uses Azure Trusted Signing through electron-builder win.azureSignOptions.
The signed variant uses Azure Trusted Signing for the packaged Windows binaries and keeps final AppX signing enabled. The signed build therefore expects the final .appx itself to pass signature verification during the finalize step.
For authentication, only these environment variables are required:
AZURE_CLIENT_IDAZURE_TENANT_IDAZURE_CLIENT_SECRET
The actual Trusted Signing config still needs publisherName, endpoint, certificateProfileName, and codeSigningAccountName in win.azureSignOptions. In this repository, AppX signing uses the full X.500 publisher subject for publisherName, because electron-builder reuses that field when it generates the AppX manifest for Azure Trusted Signing.
By default the repository falls back to signing.publisherSubject, but if AZURE_CODESIGN_APPX_PUBLISHER is present it takes precedence and should contain the full subject string such as CN=Example Publisher, O=Example Corp, C=US.
The remaining Trusted Signing fields can come from config/store-package.json or from the declared fallback environment variables:
AZURE_CODESIGN_ENDPOINTAZURE_CODESIGN_ACCOUNT_NAMEAZURE_CODESIGN_CERTIFICATE_PROFILE_NAME
Those fallback variables are not part of the Azure authentication contract, so win_store_packer no longer treats them as the primary required auth inputs. They are only used to finish rendering win.azureSignOptions for the signed AppX build.
Configure the repository with the Microsoft Store credentials required by microsoft/store-submission@v1:
SELLER_IDTENANT_ID(or the legacyAZURE_AD_TENANT_ID)CLIENT_ID(or the legacyAZURE_AD_APPLICATION_CLIENT_ID)CLIENT_SECRET(or the legacyAZURE_AD_APPLICATION_SECRET)
Also configure MICROSOFT_STORE_PRODUCT_ID as a repository variable or secret so the workflow can target the correct Partner Center product.
From repos/win_store_packer:
npm test
npm run verify:dry-run
npm run verify:publicationResolve a build plan:
node scripts/resolve-dispatch-build-plan.mjs \
--event-name workflow_dispatch \
--desktop-azure-sas-url "<desktop-sas>" \
--server-azure-sas-url "<server-sas>" \
--output build/build-plan.jsonPrepare the Desktop workspace at the selected tag:
node scripts/prepare-packaging-workspace.mjs \
--plan build/build-plan.json \
--platform win-x64 \
--workspace build/store-win-x64 \
--desktop-source inputs/hagicode-desktopStage the Server payload:
node scripts/stage-server-payload.mjs \
--plan build/build-plan.json \
--platform win-x64 \
--workspace build/store-win-x64Build the AppX artifact:
node scripts/build-appx.mjs \
--plan build/build-plan.json \
--platform win-x64 \
--workspace build/store-win-x64-unsigned \
--artifact-variant unsignedBuild the signed AppX variant:
AZURE_CLIENT_ID=... \
AZURE_TENANT_ID=... \
AZURE_CLIENT_SECRET=... \
node scripts/build-appx.mjs \
--plan build/build-plan.json \
--platform win-x64 \
--workspace build/store-win-x64-signed \
--artifact-variant signed \
--signing-mode requiredDry-run the publication flow:
node scripts/publish-release.mjs \
--plan build/build-plan.json \
--artifacts-dir build/store-win-x64 \
--output-dir build/release-metadata \
--force-dry-runPer-platform build outputs are written into the workspace root:
workspace-manifest.jsonworkspace-validation-win-x64.jsonpayload-validation-win-x64.jsonbuild-metadata-win-x64-unsigned.jsonartifact-inventory-win-x64-unsigned.jsonbuild-metadata-win-x64-signed.jsonartifact-inventory-win-x64-signed.jsonrelease-assets/hagicode-store-<release-tag>-win-x64-unsigned.appxrelease-assets/hagicode-store-<release-tag>-win-x64-signed.appx
The build metadata and artifact inventory also record:
distributionMode: "steam"runtimeSource: "portable-fixed"storePackageVersionvariant,signed, andprimaryForStoreSubmission
Publication outputs are written into the publish output directory:
<release-tag>.artifact-inventory.json<release-tag>.release-metadata.json<release-tag>.publish-dry-run.jsonfor dry runs<release-tag>.publication-result.jsonfor real releases
The release metadata and dry-run report also record:
distributionMode: "steam"runtimeSource: "portable-fixed"storePackageVersion
- The unsigned AppX package is always preserved for inspection and troubleshooting.
- The signed AppX package is built as an independent variant with Azure Trusted Signing enabled.
- The unsigned AppX package is always marked
primaryForStoreSubmission: trueand consumed bybuild-store-submission-update.mjs. - When optional signing is enabled, the signed artifact is preserved as an additional sideloading package and is not used for Microsoft Store submission.
- The Store flow preserves Store identity metadata by generating a Store-specific electron-builder config overlay from
config/store-package.json. - The default workflow submits the unsigned AppX package to Partner Center and does not require Azure Artifact Signing.
- GitHub Releases receive the AppX artifact and release metadata JSON; Steam depot data and Azure Steam index updates are out of scope here.