steam_packer is an independent packaging and Azure publication repository.
It accepts a normalized release plan from any non-interactive caller, resolves Desktop and Service manifests from direct Azure authority by default, keeps Steam identity data on the index-site source of truth, assembles the packaging workspace, stages the payload, validates the Desktop-authored bundled toolchain, repacks deterministic platform archives, uploads them to Azure Blob Storage, refreshes the root hagicode-steam/index.json entry, and then creates or updates GitHub Release notes without changing the downstream Steam hydration contract.
- Owns Portable Version packaging-safe workspace assembly
- Owns archive generation, artifact inventory, and checksum emission
- Owns Azure Blob publication and root-index refresh
- Exposes reusable CI and CLI entrypoints for external callers
- Keeps release metadata compatible with downstream Steam hydration consumers
portable-version is currently one caller, but steam_packer is not coupled to that repository.
The primary input is a build-plan JSON artifact. The caller is responsible for producing it.
The plan must contain:
release.tagplatforms[]platformMatrix.include[]upstream.desktop.*upstream.service.*downloads.desktop.containerUrldownloads.service.containerUrlbuild.shouldBuildbuild.forceRebuildbuild.dryRunenvConfig(optional; defaults to{ "HAGICODE_MODE": "steam", "HAGICODE_STEAM_ACHIEVEMENT_SYNC_ENABLED": "true" })handoff.schema = steam-packer-handoff/v1
steam_packer validates the release plan before any workspace mutation. Missing fields fail fast at the build-plan-validation stage.
envConfig rules:
- Only uppercase
HAGICODE_*keys are accepted. - Every value must be a single-line string without NUL bytes.
HAGICODE_MODEis always normalized tosteam.HAGICODE_STEAM_ACHIEVEMENT_SYNC_ENABLEDis always present, defaults totrue, and must betrueorfalsewhen provided.prepare-packaging-workspace.mjswrites the normalized config tohagicode.envbeside the packaged executable and records bothenvConfigandenvFilePathinworkspace-manifest.json.
Reusable workflow:
.github/workflows/package-release.yml
Scheduled trigger behavior:
package-releaseruns automatically every 4 hours withcron: 0 */4 * * *.- Scheduled runs auto-resolve the latest Desktop and Service manifests from the Azure authority containers by default.
- Scheduled runs use the default publication targets:
linux-x64,win-x64, andosx-universal. - The derived Portable Version release tag is checked before packaging. If that release already exists and no manual override is present,
build.shouldBuild=falseand the package/publish jobs are skipped. - The Actions summary records the trigger type, the redacted Desktop / Service manifest sources, the latest upstream versions, the derived release tag,
should_build, and the skip reason when packaging is skipped.
Expected caller behavior:
- Produce a normalized build plan and upload it as an artifact.
- Call the reusable workflow in
steam_packer. steam_packervalidates the plan and fans out packaging by platform.steam_packerpublishes merged metadata and refreshes the Azure root index.
Manual trigger behavior:
workflow_dispatchis supported for maintainers.- Manual dispatch auto-resolves the latest Desktop and Service manifests from direct Azure authority unless a maintainer explicitly passes
--desktop-index-url/--service-index-urlstyle overrides. - Manual dispatch defaults to the three supported publication targets:
linux-x64,win-x64, andosx-universal. - Manual dispatch exposes
force_rebuild,dry_run, and an optionalenv_configJSON string; it does not require a build-plan parameter. - Set
force_rebuild=trueto package and publish even when the derived Portable Version release already exists. - Set
dry_run=trueto run packaging and emit release metadata without writing to the Azure Steam container. - Set
env_configto a JSON object such as{"HAGICODE_LOG_LEVEL":"info","HAGICODE_DEBUG":"false"}when a Steam package needs additional runtime flags. Unsupported keys fail validation before workspace preparation. - Manual runs still require the same Azure SAS secrets as reusable runs.
From repos/steam_packer:
npm test
npm run verify:dry-run
npm run verify:publication
npm run verify:release-planFor fixture-driven diagnostics:
node scripts/run-release-plan.mjs \
--plan /path/to/build-plan.json \
--desktop-asset-source /path/to/desktop.zip \
--service-asset-source /path/to/service.zip \
--force-dry-runFor non-interactive plan generation from the steam_packer repository itself:
node scripts/resolve-dispatch-build-plan.mjs \
--event-name workflow_dispatch \
--desktop-azure-sas-url "<desktop-sas>" \
--service-azure-sas-url "<service-sas>" \
--env-config '{"HAGICODE_LOG_LEVEL":"info"}' \
--output build/build-plan.jsonBy default, that resolver reads index.json directly from the Desktop / Service Azure containers using the supplied SAS URLs. Optional explicit manifest URL overrides remain available for diagnostics or pinned runs, but the default discovery path no longer goes through cached https://index.hagicode.com/desktop/index.json or https://index.hagicode.com/server/index.json.
The generated plan carries:
{
"envConfig": {
"HAGICODE_MODE": "steam",
"HAGICODE_STEAM_ACHIEVEMENT_SYNC_ENABLED": "true",
"HAGICODE_LOG_LEVEL": "info"
}
}and the prepared workspace emits:
HAGICODE_MODE=steam
HAGICODE_STEAM_ACHIEVEMENT_SYNC_ENABLED=true
HAGICODE_LOG_LEVEL=infosteam_packer does not download Node, install OpenSpec, or assemble the portable toolchain. The Desktop asset is the owner of extra/portable-fixed/toolchain; this repository only verifies the Desktop-authored toolchain-manifest.json contract and packages the validated input.
Result stages are attributed as:
build-plan-validationdelegated-packagingazure-publication
steam_packer preserves the existing publication layout:
hagicode-steam/<releaseTag>/<archive>.ziphagicode-steam/<releaseTag>/<releaseTag>.build-manifest.jsonhagicode-steam/<releaseTag>/<releaseTag>.artifact-inventory.jsonhagicode-steam/<releaseTag>/<releaseTag>.checksums.txthagicode-steam/index.json
The root index entry remains compatible with current Steam hydration consumers:
versionmetadata.buildManifestPathmetadata.artifactInventoryPathmetadata.checksumsPathsteamAppIdsteamDepotIds.linuxsteamDepotIds.windowssteamDepotIds.macosartifacts[]
Publication now resolves both steamAppId and steamDepotIds from the shared Steam dataset:
- default
steamAppKey:hagicode - optional override:
--steam-app-key,STEAM_PACKER_STEAM_APP_KEY, orSTEAM_APP_KEY --steam-data-pathorSTEAM_PACKER_STEAM_DATA_PATH- By default,
steam_packerdownloads the dataset fromhttps://index.hagicode.com/steam/index.json. --steam-data-path/STEAM_PACKER_STEAM_DATA_PATHremain compatibility overrides, and can point to either a local JSON file or an explicithttps://...URL.
steam_packer resolves the canonical steamAppId from applications[].key and maps applications[].platformAppIds to steamDepotIds. The resolved values are reused for dry-run output, publication result metadata, and every versions[] entry written back to hagicode-steam/index.json. Legacy entries that predate steamAppId are backfilled from the same resolved value, while any conflicting non-empty steamAppId still aborts the write.
After Azure uploads succeed and the refreshed root index exposes the new version entry, steam_packer creates or updates the matching GitHub Release body for the published tag. The Release body summarizes the published upstream versions, Azure paths, and Steam identity metadata. Release assets remain Azure-only: steam_packer does not upload archives, checksums, or manifests to GitHub Releases.
Local fixtures and explicit manifest overrides are diagnostics tools only. They do not redefine the default provenance rules: Desktop / Service manifests default to Azure authority, and Steam identity data defaults to the index site.
The current Portable Version integration is documented in docs/portable-version-migration.md.