Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 27 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,8 @@ jobs:
run: ./tools/setup-ffmpeg.ps1 -CorrespondingSourceDestination "dist/windows/OpenStudio-FFmpeg-8.0.1-complete-corresponding-source.zip"

- name: Build validated Microsoft Store package
if: vars.OPENSTUDIO_STORE_ENABLED == 'true'
# Always retain a tag-built MSIX for the first manual Store submission.
# OPENSTUDIO_STORE_ENABLED gates only the credentialed submit-store job.
shell: pwsh
env:
RELEASE_NOTES_FILE: ${{ needs.validate-release-notes.outputs.notes_file }}
Expand All @@ -406,7 +407,6 @@ jobs:
if ($LASTEXITCODE -ne 0) { throw 'Store submission preflight failed.' }

- name: Retain Microsoft Store submission artifact
if: vars.OPENSTUDIO_STORE_ENABLED == 'true'
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: microsoft-store-package
Expand Down Expand Up @@ -1047,9 +1047,9 @@ jobs:
{"tag":"${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || format('v{0}', github.event.inputs.version) }}","channel":"stable","desktopRepo":"${{ github.repository }}"}

submit-store:
name: Submit Microsoft Store update
name: Submit Microsoft Store release
needs: [publish, build-windows, validate-release-notes]
if: vars.OPENSTUDIO_STORE_ENABLED == 'true'
if: vars.OPENSTUDIO_STORE_ENABLED == 'true' && startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-24.04
timeout-minutes: 25
environment: microsoft-store
Expand All @@ -1070,6 +1070,25 @@ jobs:
name: microsoft-store-package
path: dist/store

- name: Require the exact release tag
shell: bash
run: |
if [ "$GITHUB_REF_NAME" != "v${VERSION#v}" ]; then
echo "Store submission requires the workflow version to match its tag." >&2
exit 1
fi

- name: Verify Store API access and draft without mutations
env:
MS_STORE_TENANT_ID: ${{ secrets.MS_STORE_TENANT_ID }}
MS_STORE_CLIENT_ID: ${{ secrets.MS_STORE_CLIENT_ID }}
MS_STORE_CLIENT_SECRET: ${{ secrets.MS_STORE_CLIENT_SECRET }}
run: >-
python3 tools/submit_store_release.py --version "$VERSION"
--package-dir dist/store --notes-file "$RELEASE_NOTES_FILE"
--initial-submission-config packaging/msix/initial-submission.json
--report output/store-preflight.json --preflight

- name: Submit the exact release package and notes
env:
MS_STORE_TENANT_ID: ${{ secrets.MS_STORE_TENANT_ID }}
Expand All @@ -1078,13 +1097,16 @@ jobs:
run: >-
python3 tools/submit_store_release.py --version "$VERSION"
--package-dir dist/store --notes-file "$RELEASE_NOTES_FILE"
--initial-submission-config packaging/msix/initial-submission.json
--report output/store-submission.json --submit

- name: Retain sanitized submission status
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: microsoft-store-submission-${{ github.run_attempt }}
path: output/store-submission.json
path: |
output/store-preflight.json
output/store-submission.json
if-no-files-found: warn
retention-days: 30
79 changes: 66 additions & 13 deletions docs/release-runbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -524,38 +524,91 @@ other listing settings. Publishing a GitHub release does not skip certification.
2. Build the MSIX from the Windows release payload using the existing pinned
WebView2/CRT packaging checks; retain the MSIX as a workflow artifact.
3. Authenticate to the Store API using GitHub environment secrets.
4. Validate package identity, version, SHA256, and the last published Store version.
5. Clone the last published submission; replace only the x64 desktop package and
English release notes. Refuse to overwrite unrelated pending submissions.
4. Run authenticated read-only preflight: validate package identity, version,
SHA256, and either the published baseline or the explicitly pinned initial draft.
5. Clone the last published submission, or adopt the configured initial draft;
replace only the x64 desktop package and English release notes. Refuse to
overwrite unrelated pending submissions. Keep the initial publishing hold.
6. Upload a ZIP containing the MSIX, commit for certification, and report status.
Resume the same tagged/hash-bound submission on retry; never blindly retry
an ambiguous create/commit request or delete a pending submission.
7. Test with fake HTTP/API responses and the actual local MSIX. Run the first live
submission only after the initial manual Store submission and account setup.
7. Test with fake HTTP/API responses and the actual local MSIX. Complete the
initial Partner Center draft (including age ratings) and account setup before
the first live submission. An older published package is not a prerequisite.

The Windows Release job always builds, validates and retains the
`microsoft-store-package` artifact. `OPENSTUDIO_STORE_ENABLED` controls only the
credentialed `submit-store` job. An MSIX packaging or offline validation failure still fails the Windows
release job, so a missing Store artifact cannot silently pass the release gate.

### First Store release from a tag

1. Merge the release and any release-preparation follow-up only after CI passes.
Validate `docs/releases/<version>.md` on the final source, then push the stable
version tag on that merged `main` revision.
2. Before tagging, review `packaging/msix/initial-submission.json`. It permits only
draft `1152921505701841400`, tag `v0.1.02`, and replacement of the existing
`0.0.1.0` package. The draft must have the approved artwork fully uploaded,
age ratings and certification details completed, and publishing mode **Manual**.
Do not publish the old package to establish a baseline.
3. With the GitHub environment configured and `OPENSTUDIO_STORE_ENABLED=true`,
the `submit-store` job follows successful release publication. It downloads
the same run's `microsoft-store-package` artifact and first runs `--preflight`:
credentials are used only for authentication and Store GET requests. A failed
preflight blocks the mutation step and retains a sanitized diagnostic report.
4. The subsequent `--submit` step revalidates current state, adopts only the pinned
draft, preserves saved listing/artwork/audience/settings, replaces the package
and English release notes, and commits it for certification. The initial draft
is never deleted or recreated. Check the retained reports and Partner Center.
5. After certification, publish the qualified new version deliberately. The manual
hold prevents certification from automatically making it public. Later tags
use the normal published-baseline path; the initial pin cannot adopt other drafts.

The config is an explicit one-release opt-in, not permission to adopt arbitrary
pending submissions. The initial path requires exactly one uploaded x64 Desktop
package at the pinned old version, saved English artwork and no unfinished assets.
Retry markers bind the package hash, notes, config and preserved settings. A changed
artifact, draft, hold or listing stops the retry; investigate instead of removing
the marker or repinning blindly. After an ambiguous PUT/upload/commit failure,
rerun the failed job using the same artifact. Already committed submissions are
polled without another upload or commit. No path publishes an older version.

For a credentialed read-only check against a validated tagged artifact, use
`python tools/submit_store_release.py --version v0.1.02 --package-dir dist/store
--notes-file docs/releases/0.1.02.md --initial-submission-config
packaging/msix/initial-submission.json --preflight` (as one command).
Without `--preflight` or `--submit`, validation remains entirely offline. Credentials
stay in environment secrets; never pass them as arguments. Live preflight is not
evidence of certification, API update success, or Store-delivered installation.

### One-time enablement

This repository implements the automation; it cannot provision the owner's
Microsoft tenant or approve the initial Store listing. It stays inactive until:

1. Complete the initial manual Store submission, including age ratings and the
`runFullTrust` explanation, and publish a qualified first package. The current
`0.0.1.0` candidate is not the selected public release.
1. Complete the initial Partner Center draft, including age ratings, approved
artwork, `runFullTrust` explanation and manual publishing hold, and review the
initial-submission pin above. The `0.0.1.0` candidate is not the public release.
2. Link a Microsoft Entra application to Partner Center, assign the required
Manager role, and obtain tenant ID, client ID and client secret. See Microsoft's
[API prerequisites](https://learn.microsoft.com/en-us/windows/uwp/monetize/create-and-manage-submissions-using-windows-store-services).
3. In GitHub repository Settings → Environments, create `microsoft-store`.
Add environment secrets `MS_STORE_TENANT_ID`, `MS_STORE_CLIENT_ID`, and
`MS_STORE_CLIENT_SECRET`. Add the secret directly in GitHub; never paste it in
chat, commit it, or place it in workflow inputs. Rotate it before expiration.
4. Set repository Actions variable `OPENSTUDIO_STORE_ENABLED` to `true` after
the code is merged and the Store flight/installed qualification is complete.
The app identity is fixed to Store ID `9N3MQ442VXGW` and the reserved publisher.
4. After the code passes CI and merges and the selected package's release checks
are complete, set repository Actions variable `OPENSTUDIO_STORE_ENABLED` to
`true` before the release tag. The job's mandatory live preflight must pass
before submission can mutate the draft. The app identity is fixed to Store ID
`9N3MQ442VXGW` and the reserved publisher. Restrict the `microsoft-store`
environment to `v*` tags; branch runs cannot access its credentials. The job
also rejects a manually dispatched version that differs from its tag.
5. Push the normal stable release tag. The `submit-store` job follows `publish`.
To require a human gate, configure required reviewers on the `microsoft-store`
environment. With no reviewer gate, submission is automatic. The existing
Partner Center publish mode remains authoritative after certification.

Once automation adopts the initial draft, make further updates through the API.
Do not edit an API-created pending submission in Partner Center: Microsoft warns
that mixing API and portal edits can invalidate it. If another submission is
pending, resolve it deliberately; automation leaves it intact and fails visibly.
Expand Down Expand Up @@ -591,8 +644,8 @@ Only the existing en-us release notes and x64 Desktop package are replaced;
other architectures and listing settings remain unchanged. An accepted commit can
still be in preprocessing/certification. Check Partner Center's final result.
HTTP reports must exclude tokens, response bodies and SAS upload URLs. A new
artifact hash requires a new package version. Enable the workflow only after the
first manually published, qualified package and required account setup exist.
artifact hash requires a new package version. The initial draft exception requires
the reviewed one-release config; subsequent releases require a published baseline.

## Windows signing with SignPath

Expand Down
Loading
Loading