Keep commit admission on the trusted workflow checkout and reject mixed release inputs before loading repository code. Stage every built product under its commit with receipt-bound summary links, never channel writes. Build both Windows universal bundles through the existing SDK scripts. Keep Store calendar versions separate from sideload app versions so zero- major app versions remain packageable. Reject invalid arguments before modifying bundles. Bind desktop and Termux versions to the source commit, and record Termux cache provenance without labeling commits as tags. Verification: 77 Python tests and 36 JS tests passed. Real makeappx packed and unpacked disposable per-arch and universal packages. Seven official workflow-expression checks, actionlint, syntax, lint and prose passed. No signing, installed-app update, Android build, or remote dispatch ran.
92 lines
4.0 KiB
JavaScript
92 lines
4.0 KiB
JavaScript
#!/usr/bin/env node
|
|
// bundle-store-msixbundle.mjs — the Store-submission bundle step of the
|
|
// desktop-bundled-release workflow's store-publish job.
|
|
//
|
|
// The win32 build legs produce per-arch Store-submission packages
|
|
// (Store-<name>-<fileVersion>-win-<arch>.msix, built with
|
|
// HERMES_DESKTOP_VARIANT=store / the Partner Center identity). This script
|
|
// bundles the x64 + arm64 packages into ONE universal Store .msixbundle for
|
|
// the Windows Store submission. --output-file writes its absolute path for
|
|
// callers, independently of the installer's download/progress logs.
|
|
//
|
|
// The bundle is deliberately left UNSIGNED: the Store re-signs the package
|
|
// with the Microsoft Store certificate on ingestion (same posture as the
|
|
// build legs, whose Store-*.msix ship unsigned — see sign-msix.mjs).
|
|
//
|
|
// Usage (win runner, bash):
|
|
// node scripts/bundle-store-msixbundle.mjs --tag vX.Y.Z
|
|
import { execFileSync } from 'node:child_process'
|
|
import fs from 'node:fs'
|
|
import path from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
import { parseArgs } from 'node:util'
|
|
|
|
import { appIdentity } from './msix-shared.mjs'
|
|
import { ensureWindowsBundleTools } from '../apps/desktop/scripts/windows-bundle-tools.mjs'
|
|
|
|
const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
|
|
|
const { values } = parseArgs({ options: {
|
|
tag: { type: 'string' }, commit: { type: 'string' }, version: { type: 'string' },
|
|
'output-file': { type: 'string' },
|
|
} })
|
|
const tag = values.tag || process.env.HERMES_PAYLOAD_TAG
|
|
const commitBuild = values.commit
|
|
if (commitBuild) {
|
|
if (tag) throw new Error('Commit builds cannot select a release tag')
|
|
process.env.HERMES_BUILD_COMMIT = commitBuild
|
|
process.env.HERMES_PAYLOAD_VERSION = values.version || ''
|
|
} else if (values.version !== undefined) {
|
|
throw new Error('--version requires --commit')
|
|
}
|
|
if (!tag && !commitBuild) {
|
|
console.error('[bundle-store] --tag or --commit is required')
|
|
process.exit(1)
|
|
}
|
|
if (process.platform !== 'win32') {
|
|
console.error('[bundle-store] this job must run on a Windows runner (makeappx)')
|
|
process.exit(1)
|
|
}
|
|
|
|
// product-identity.cjs keys the app name off HERMES_DESKTOP_VARIANT — the
|
|
// Store-submission artifacts carry the Store- prefix + Partner Center
|
|
// identity, so the env var MUST be 'store' before the identity lookup.
|
|
process.env.HERMES_DESKTOP_VARIANT = 'store'
|
|
|
|
const desktop = path.join(REPO_ROOT, 'apps', 'desktop')
|
|
const releaseDir = path.join(desktop, 'release')
|
|
const { name, version, fileVersion } = appIdentity(desktop, tag)
|
|
|
|
// Per-arch Store-submission packages: electron-builder names them with the
|
|
// Store- prefix + appInfo.version (the 3-part or full-canary string), which
|
|
// appIdentity reports as `fileVersion` — same convention as the out-of-store
|
|
// bundle in stage-msixbundle.mjs.
|
|
const storeMsix = (arch) => path.join(releaseDir, `Store-${name}-${fileVersion}-win-${arch}.msix`)
|
|
const x64 = storeMsix('x64')
|
|
const arm64 = storeMsix('arm64')
|
|
if (!fs.existsSync(x64) || !fs.existsSync(arm64)) {
|
|
console.error(`[bundle-store] need both per-arch Store-*.msix to bundle:\n ${x64}\n ${arm64}`)
|
|
process.exit(1)
|
|
}
|
|
|
|
// makeappx bundle /d includes EVERY .msix in the dir — stage only the two
|
|
// Store packages into a clean dir (mirror stage-msixbundle.mjs).
|
|
const staging = path.join(releaseDir, '__store-bundle-staging')
|
|
fs.rmSync(staging, { recursive: true, force: true })
|
|
fs.mkdirSync(staging, { recursive: true })
|
|
fs.copyFileSync(x64, path.join(staging, path.basename(x64)))
|
|
fs.copyFileSync(arm64, path.join(staging, path.basename(arm64)))
|
|
|
|
const bundle = path.join(releaseDir, `Store-${name}-${version}-win.msixbundle`)
|
|
const { makeappx } = await ensureWindowsBundleTools()
|
|
if (fs.existsSync(bundle)) fs.rmSync(bundle, { force: true })
|
|
execFileSync(makeappx, ['bundle', '/o', '/bv', version, '/d', staging, '/p', bundle], {
|
|
stdio: 'inherit'
|
|
})
|
|
fs.rmSync(staging, { recursive: true, force: true })
|
|
|
|
// Download logs can share stdout. The explicit output file is the machine contract.
|
|
const outputFile = values['output-file']
|
|
if (outputFile) fs.writeFileSync(outputFile, bundle, 'utf8')
|
|
console.log(bundle)
|