Skip to content

Fix --packageRolloutPercentage being silently ignored - #164

Closed
Alexandre Zollinger Chohfi (azchohfi) wants to merge 1 commit into
mainfrom
azchohfi-fix-package-rollout-percentage-ignored
Closed

Fix --packageRolloutPercentage being silently ignored#164
Alexandre Zollinger Chohfi (azchohfi) wants to merge 1 commit into
mainfrom
azchohfi-fix-package-rollout-percentage-ignored

Conversation

@azchohfi

Copy link
Copy Markdown
Collaborator

Problem

msstore publish --packageRolloutPercentage <n> is silently ignored in the common case, and the package ships to 100% of users while the caller believes the rollout is staged at <n>%.

PublishAsync only applied the requested percentage when the submission it had just retrieved already carried a PackageDeliveryOptions.PackageRollout object:

if (submission.PackageDeliveryOptions?.PackageRollout != null && packageRolloutPercentage != null)
{
    submission.PackageDeliveryOptions.PackageRollout.IsPackageRollout = true;
    submission.PackageDeliveryOptions.PackageRollout.PackageRolloutPercentage = packageRolloutPercentage.Value;
}

A freshly created submission generally has no rollout configured, so the guard is false exactly when the user asked for a rollout. There is no error, no warning, and no non-zero exit code — the flag just evaporates.

This is a bad failure mode: it fails open, toward the widest possible audience, and it's invisible until telemetry shows the update reached everyone.

Fix

Materialize the objects when a percentage is supplied, rather than requiring them to pre-exist:

if (packageRolloutPercentage != null)
{
    submission.PackageDeliveryOptions ??= new PackageDeliveryOptions();
    submission.PackageDeliveryOptions.PackageRollout ??= new PackageRollout();
    submission.PackageDeliveryOptions.PackageRollout.IsPackageRollout = true;
    submission.PackageDeliveryOptions.PackageRollout.PackageRolloutPercentage = packageRolloutPercentage.Value;
}

Both the app and flight paths are covered — the mutation happens before the DevCenterSubmission / DevCenterFlightSubmission cast, and the flight update already copies PackageDeliveryOptions onto DevCenterFlightSubmissionUpdate.

Behavior when the option is omitted is unchanged: packageRolloutPercentage is null, so nothing is allocated and the submission's delivery options are left exactly as the service returned them.

Tests

Three tests added to PublishCommandUnitTests:

Test Purpose
...ShouldApplyPackageRolloutPercentageWhenSubmissionHasNoRolloutConfigured App path — asserts UpdateSubmissionAsync receives IsPackageRollout: true / 5%
...ShouldApplyPackageRolloutPercentageForFlights Flight path — same assertion on UpdateFlightSubmissionAsync
...ShouldNotEnableRolloutWhenPercentageIsNotProvided Pins that omitting the option leaves PackageDeliveryOptions untouched

The default fixtures (AddDefaultFakeSubmission / AddDefaultFakeFlightSubmission) already build submissions with no PackageDeliveryOptions, so they reproduce the bug without modification. Verified both new rollout tests fail on main and pass with this change:

Test run summary: Failed! (fix reverted)
  total: 2   failed: 2   succeeded: 0

Full suite green on both target frameworks:

net10.0                      total: 149  failed: 0  succeeded: 139  skipped: 10
net10.0-windows10.0.17763.0  total: 149  failed: 0  succeeded: 141  skipped: 8

Notes

Reported in #149, where the user hit this while trying to build a flight → metadata → staged-rollout pipeline and concluded rollout was flights-only.

Worth calling out separately: msstore submission rollout get|update|halt|finalize already works for non-flighted submissions, but is missing from the Learn commands page, which is what sent that user down the wrong path. That's a docs fix I'll do in windows-dev-docs.

Users on the current release can work around this bug by setting PackageDeliveryOptions.PackageRollout explicitly in the JSON passed to msstore submission update, which is unaffected.

`PublishAsync` only applied the requested rollout when the submission it
had just retrieved already carried a `PackageDeliveryOptions.PackageRollout`
object:

    if (submission.PackageDeliveryOptions?.PackageRollout != null && packageRolloutPercentage != null)

A newly created submission generally has no rollout configured, so that
guard was false in the common case and `--packageRolloutPercentage` was
dropped without any error or warning - the package then shipped to 100%
of users while the caller believed it was staged.

Materialize `PackageDeliveryOptions` and `PackageRollout` when a
percentage is supplied instead of requiring them to pre-exist. Both the
app and flight paths benefit, since the flight update copies
`PackageDeliveryOptions` onto `DevCenterFlightSubmissionUpdate`.

Adds regression coverage for the app and flight paths (both fail without
the fix), plus a test pinning that omitting the option leaves the
submission's delivery options untouched.

Reported in #149.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 134c8b3a-091c-45f1-bca6-61798d1bcfc0

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes staged rollout percentages being ignored when submissions lack rollout configuration.

Changes:

  • Materializes missing rollout objects before applying the percentage.
  • Adds regression coverage for app, flight, and omitted-percentage paths.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
MSStore.CLI/Helpers/IStorePackagedAPIExtensions.cs Initializes and applies rollout settings.
MSStore.CLI.UnitTests/PublishCommandUnitTests.cs Adds rollout regression tests.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@azchohfi

Copy link
Copy Markdown
Collaborator Author

Closing this - the premise doesn't hold up against the live API.

I claimed that a newly created submission comes back without a PackageDeliveryOptions.PackageRollout object, making the guard on IStorePackagedAPIExtensions.cs line 487 false and silently dropping --packageRolloutPercentage. I inferred that from the nullable C# type and from the unit test fixtures, without checking real API responses.

Verified against Partner Center across 6 real submissions (5 apps plus 1 flight submission, covering Published, PendingCommit, and CommitFailed):

PackageDeliveryOptions null : False   (all 6)
PackageRollout         null : False   (all 6)

Every one returns a populated object rather than omitting it:

"PackageRollout": {
  "IsPackageRollout": false,
  "PackageRolloutPercentage": 0,
  "PackageRolloutStatus": "PackageRolloutNotStarted",
  "FallbackSubmissionId": "0"
}

So the existing guard evaluates true in practice and the rollout percentage is applied correctly. There is no user-facing bug here.

The reason the added tests failed on main and passed with the change is that AddDefaultFakeSubmission and AddDefaultFakeFlightSubmission construct submissions with no PackageDeliveryOptions at all. That exercises a branch the real service never produces, so the tests demonstrated mock behavior rather than a defect.

The null-coalescing itself was harmless defensive hardening, but not worth the churn for a branch that doesn't occur, and the PR description asserted a bug that doesn't exist. Closing rather than leaving a misleading record.

Worth noting separately: the test fixtures not matching the real API response shape is what made this look real. If they ever get revisited, populating PackageDeliveryOptions to match live responses would remove that trap.

@azchohfi
Alexandre Zollinger Chohfi (azchohfi) deleted the azchohfi-fix-package-rollout-percentage-ignored branch August 25, 2026 21:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants