Enable one-click crates.io release for mxc-sdk - #647
Conversation
Add crates.io-required metadata so mxc-sdk and its first-party dependency closure (10 crates) can be published: - workspace: add repository URL; give the 8 first-party path-deps in [workspace.dependencies] explicit versions (cargo publish requires a version on every path dependency). - wxc_common: version its optional `nanvix_common` path-dep. Although it is gated by the off-by-default `microvm` feature, cargo requires every dependency in a published manifest -- optional ones included -- to carry a version and exist on crates.io, so nanvix_common must ship too. It is a leaf (serde/serde_json only). - add description to wxc_common, mxc_pty, and the appcontainer / bubblewrap / lxc / seatbelt common crates. - add license + repository to mxc_telemetry; repository to nanvix_common. - inherit repository on each published crate. - make sandbox_spec publishable (remove publish = false). Validated with `cargo metadata` and per-crate `cargo package --list` (plus `cargo publish --dry-run --no-verify` for leaf crates) against real crates.io. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e5d2aa5b-7f04-4e4d-83d3-a02efe7020ab
New ADO pipeline publishes the mxc-sdk crate and its 10-crate first-party closure to crates.io from a single "Run pipeline" click, with a dryRun parameter (default true) that previews every crate before anything uploads. - .azure-pipelines/scripts/publish_crates_to_cratesio.py: leaf-first ordered publish (mxc_telemetry, nanvix_common, mxc_pty, sandbox_spec, wxc_common, lxc_common, seatbelt_common, appcontainer_common, bwrap_common, mxc-sdk), per-crate version resolved via `cargo metadata`, idempotent version-exists skip via the crates.io sparse index, `cargo publish --no-verify` with a post-publish index-propagation wait, and a --dry-run mode that runs `cargo package --list` (validates each manifest without needing first-party deps on the index yet). Modeled on scripts/seed_feed.py conventions. - .azure-pipelines/1ES.Crate.Release.yml: trigger:none, extends the 1ES Official template; DryRun stage always previews; Publish stage is compiled in only when dryRun=false and gates on manual approval via the MXC-CratesIo-Production environment. Reuses Rust.Toolchain.Public.yml and deliberately skips Cargo.Setup.* so cargo talks to the real crates.io. Secrets stay out of the repo: CARGO_REGISTRY_TOKEN comes from the MXC-CratesIo-Publish variable group and is mapped to env only in the publish step. One-time ADO/crates.io setup is documented in the pipeline header. Validated locally against real crates.io: the full --dry-run passes for all 10 crates; all 10 target names are currently unregistered on crates.io. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e5d2aa5b-7f04-4e4d-83d3-a02efe7020ab
There was a problem hiding this comment.
Pull request overview
This PR enables releasing the Rust mxc-sdk crate to crates.io by (1) making the crate’s first-party dependency closure publishable via Cargo metadata/manifest adjustments, and (2) adding an Azure DevOps “one-click” pipeline plus a helper script to publish the closure leaf-first.
Changes:
- Add crates.io publish metadata to
mxc-sdkand its first-party dependency closure (license/repository/description) and makesandbox_specpublishable. - Convert internal path dependencies used by the publishable closure to dual
{ path, version }socargo publishcan strippathand publish clean manifests. - Add an ADO pipeline (
1ES.Crate.Release.yml) and a Python script to dry-run/package or publish the crate closure with index-propagation waits.
Reviewed changes
Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
src/Cargo.toml |
Adds workspace repository and sets { path, version } for publishable internal workspace deps. |
src/core/wxc_common/Cargo.toml |
Adds publish metadata and ensures optional nanvix_common path dep includes a version for publishing. |
src/core/mxc-sdk/Cargo.toml |
Adds workspace repository metadata needed for crates.io publishing. |
src/core/mxc_pty/Cargo.toml |
Adds publish metadata (repository/description) for the closure. |
src/mxc_telemetry/Cargo.toml |
Adds workspace license/repository metadata for publishing. |
src/core/generated/base_container_specification/Cargo.toml |
Removes publish = false by making sandbox_spec publishable and adds repository metadata. |
src/backends/appcontainer/common/Cargo.toml |
Adds publish metadata for publishing appcontainer_common. |
src/backends/bubblewrap/common/Cargo.toml |
Adds publish metadata for publishing bwrap_common. |
src/backends/lxc/common/Cargo.toml |
Adds publish metadata for publishing lxc_common. |
src/backends/seatbelt/common/Cargo.toml |
Adds publish metadata for publishing seatbelt_common. |
src/backends/nanvix/common/Cargo.toml |
Adds workspace repository metadata so nanvix_common is publishable. |
.azure-pipelines/1ES.Crate.Release.yml |
New ADO pipeline that runs dry-run preview and gated publish to crates.io. |
.azure-pipelines/scripts/publish_crates_to_cratesio.py |
New helper script to dry-run package or publish crates leaf-first and wait for sparse-index propagation. |
| def _publish_one(crate: str, version: str, manifest_path: str, dry_run: bool) -> bool: | ||
| if version in _published_versions(crate): | ||
| print(f"SKIP {crate} {version} - already on crates.io") | ||
| return True | ||
|
|
||
| if dry_run: | ||
| # `cargo package --list` validates the manifest (it rejects a path dep | ||
| # that lacks a version, exactly as publish does) and prints the packaged | ||
| # file set, WITHOUT resolving first-party deps against the crates.io | ||
| # index. `cargo publish --dry-run` cannot be used for a whole-closure | ||
| # preview: it resolves dependencies against the live index, so every | ||
| # non-leaf crate would fail until its first-party deps are actually | ||
| # published. --allow-dirty keeps the preview frictionless if CI leaves | ||
| # the tree in a state cargo considers dirty; the real publish never | ||
| # allows it. | ||
| rc = _run_cargo( | ||
| ["cargo", "package", "-p", crate, "--no-verify", "--allow-dirty", | ||
| "--manifest-path", manifest_path, "--list"] | ||
| ) | ||
| ok = rc == 0 | ||
| print(f"{'OK ' if ok else 'FAIL '} dry-run {crate} {version}") | ||
| return ok |
| # ---------------------------------------------------------------------- | ||
| # Preview: always runs, publishes nothing. Gives the operator the full | ||
| # `cargo publish --dry-run` + packaged-file list for every crate before | ||
| # anything reaches crates.io. | ||
| # ---------------------------------------------------------------------- | ||
| - stage: DryRun | ||
| displayName: "Preview crate publish (dry run)" | ||
| jobs: | ||
| - job: DryRunJob | ||
| displayName: "cargo publish --dry-run" |
| # only cargo config in effect is the repo-root .cargo/config.toml (Windows | ||
| # rustflags only -- no source replacement, and inert on Linux). | ||
|
|
||
| trigger: none |
There was a problem hiding this comment.
issue: To publish to crates.io using the official Microsoft open source account we will need to publish it through the ESRP release system. See: https://eng.ms/docs/microsoft-security/identity/trust-and-security-services/tss-release-distribute/tss-release-esrp-parent/oss-publishing/releasing-open-source/cratesio . You will need to follow that and do something similar to what we do in the 1ES.Release.yml. To be honest, we should be able to reuse that same yaml file to release to either Npm, Crates.io or both.
At the very least that one should be the entry point to all releases, but we can still have template yml files that we point to to keep it clean. Everything in terms of set up on the back end for ESRP should already be set up for us. So, let me know if you have any issues with that.
In the ESRP docs they say the azure rust sdk folks have this working, where they publish all their crate dependencies first then publish their main crate. I believe the yml file they do this in is this one https://github.com/Azure/azure-sdk-for-rust/blob/95eb8c3878cb2c7d3239b5f584b8179ee0483c31/eng/pipelines/templates/stages/archetype-rust-release.yml . We can probably do something similar.
There was a problem hiding this comment.
Thanks Brandon -- followed this. Reworked the crate release to go through ESRP and folded it into 1ES.Release.yml so one pipeline releases npm, crates.io, or both:
1ES.Release.ymlnow takespublishNpm/publishCratesboolean params. The existing NPM stage is gated onpublishNpm, and a newPublish_to_CratesIostage runs the ESRP path.- Following the azure-sdk-for-rust approach, the official build packages the closure leaf-first in a single
cargo packageinvocation (all--packageflags together, so intra-closure deps resolve locally) into themxc-crates-packageartifact. The release job then runs oneEsrpRelease@12task per crate (contenttype: Rust, accountmicrosoft-oss-releases), leaf-first, verifying each on the crates.io sparse index before the next. - Deleted the old token-based
1ES.Crate.Release.yml+publish_crates_to_cratesio.py-- no moreCARGO_REGISTRY_TOKEN. New templates:templates/Package.Crates.Job.yml(packaging) andtemplates/Publish.CratesIo.Job.yml(ESRP release loop).
This also obsoletes the two Copilot comments above (they were on the now-deleted script/pipeline).
One heads-up before we flip publishing on -- publishCrates defaults to false for now. Packaging the true --all-features closure of mxc-sdk surfaced that it now routes through the mxc_engine hub crate and expands to 17 first-party crates. Before a first real publish we still need to: (1) add a version to the 7 path deps in src/Cargo.toml that currently only carry path; (2) resolve isolation_session_bindings being publish = false; and (3) do the OSPO OSS-release registration + reserve the crate names. Happy to take that as a follow-up -- let me know if you'd rather have it in this PR or a separate one.
Resolved Cargo.toml conflicts: - src/Cargo.toml [workspace.dependencies]: kept version pins on wxc_common and appcontainer_common; kept upstream's new mxc_engine and mxc-sdk path deps (left unversioned - not part of the current publish closure). - src/mxc_telemetry/Cargo.toml: kept repository.workspace = true. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e5d2aa5b-7f04-4e4d-83d3-a02efe7020ab
Rework the crates.io release per PR microsoft#647 review feedback (Brandon Bonaby): publish through the ESRP OSS release system instead of a bespoke CARGO_REGISTRY_TOKEN, folded into the existing 1ES.Release.yml so one pipeline releases npm, crates.io, or both (modeled on azure-sdk-for-rust). - scripts/crates_release.py: package/verify-order/stage/wait subcommands; cargo-packages the closure leaf-first, no token, no publish endpoint. - templates/Package.Crates.Job.yml: official-build job -> mxc-crates-package. - templates/Publish.CratesIo.Job.yml: releaseJob, EsrpRelease@12 contenttype Rust, one task per crate leaf-first, verify each on the crates.io index before the next. - 1ES.Release.yml: publishNpm/publishCrates params; gate NPM stage; add Publish_to_CratesIo stage; ESRP one-time-setup notes in the header. - 1ES.Build.Stages.yml: add Package_Crates stage. - Delete 1ES.Crate.Release.yml + publish_crates_to_cratesio.py (token approach). publishCrates defaults to false: enabling actual publishing still needs the crate closure metadata reconciled (7 path deps need versions; isolation_session_bindings is publish=false) plus OSPO OSS-release setup. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 54ab05fd-1ef7-4fa4-b130-ea17a9ef8a47
cmd_package looped `cargo package -p <crate>` one crate at a time, which fails on the first non-leaf crate: packaging a crate alone strips the path from each dependency and cargo then looks for the sibling on the crates.io index, where it is not published yet (e.g. wxc_common -> mxc_telemetry -> "no matching package named `mxc_telemetry` found"). Package the entire closure in a single `cargo package` invocation with every crate passed via -p, so cargo resolves intra-closure deps from target/package/ instead of crates.io (the azure-sdk-for-rust Pack-Crates.ps1 approach). Verified locally: all 9 packageable crates now tar in one run; the only remaining failure is the known mxc-sdk -> mxc_engine missing-version blocker. Also corrected two docstring claims that wrongly implied per-crate `--no-verify` packaging works. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 54ab05fd-1ef7-4fa4-b130-ea17a9ef8a47
|
|
||
| - template: Mxc.Binary.Packaging.Job.yml | ||
|
|
||
| - stage: Package_Crates |
There was a problem hiding this comment.
issue: we should probably add a conditional here to only do this packaging when the isOfficialBuild parameter is true.
There was a problem hiding this comment.
Done in 287305b -- gated the Package_Crates stage on isOfficialBuild in 1ES.Build.Stages.yml, so crate packaging only runs on the official signed build (not PR/CI).
| - script: | | ||
| set -euo pipefail | ||
| python3 "$(script)" wait --order-file "$(orderFile)" --crate "${{ crate }}" | ||
| displayName: "Wait for ${{ crate }} on crates.io index" |
There was a problem hiding this comment.
note: the ESRP task above will actually keep polling until the package is published so we probably don't need this. It's done when the waitforreleasecompletion input to the task is set to true. That said, I'm not against the extra wait task.
There was a problem hiding this comment.
Good point -- waitforreleasecompletion: true already blocks until each ESRP publish finishes. I kept the wait step as a lightweight guard that confirms the crate is resolvable on the feed index before the next dependent publishes, and it only emits a warning (never fails the release) on timeout. It is also skipped in the new cratesDryRun mode. Happy to drop it entirely if you would prefer -- your call. (Also fixed it to query the private feed per your other comment.)
| - name: publishNpm | ||
| displayName: "Publish the npm SDK to npmjs.com" | ||
| type: boolean | ||
| default: true |
There was a problem hiding this comment.
issue: I like the parameters, lets make both false so it's clear in the UI that we expect the person initiating the pipeline run to select one or both.
There was a problem hiding this comment.
Done in 287305b -- publishNpm now defaults to false too, so the run dialog forces the operator to pick npm and/or crates.
| "mxc-sdk", | ||
| ] | ||
|
|
||
| CRATES_IO_SPARSE_INDEX = "https://index.crates.io" |
There was a problem hiding this comment.
issue: So this won't worked in the 1ES pipeline due to network isolation. You'd need to use the private feed's sparse index. Should be this one
So it would look something like this:
AZURE_FEED_REGISTRY = "Mxc-Azure-Feed"
AZURE_FEED_SPARSE_INDEX = (
"https://microsoft.pkgs.visualstudio.com/"
"Dart/_packaging/Mxc-Azure-Feed/Cargo/index"
)
SYSTEM_ACCESS_TOKEN_ENV = "SYSTEM_ACCESSTOKEN"then you can have a helper function in here somewhere:
def _feed_request(url: str) -> urllib.request.Request:
"""Create an Azure Artifacts request authenticated by the pipeline token."""
token = os.environ.get(SYSTEM_ACCESS_TOKEN_ENV)
if not token:
raise RuntimeError(
f"{SYSTEM_ACCESS_TOKEN_ENV} is not set. Map $(System.AccessToken) "
f"to that environment variable in the pipeline task."
)
return urllib.request.Request(
url,
headers={"Authorization": f"Bearer {token}"},
)Then your _published_versions function below can look similar to:
def _published_versions(crate: str) -> set[str]:
"""Return the versions of `crate` already in the Azure Artifacts feed."""
url = f"{AZURE_FEED_SPARSE_INDEX}/{_sparse_index_path(crate)}"
with urllib.request.urlopen(_feed_request(url), timeout=30) as response:
body = response.read().decode("utf-8")
versions: set[str] = set()
for line in body.splitlines():
line = line.strip()
if line:
versions.add(json.loads(line)["vers"])
return versionsYou also have have references to crates.io in the comments of this file as well. You'd want to update those to just say Azure Artifacts Feed or something like that.
There was a problem hiding this comment.
Done in cfaaf18 -- switched the version check from public index.crates.io to the private Mxc-Azure-Feed sparse index (AZURE_FEED_SPARSE_INDEX, matching .azure-pipelines/.cargo/config.toml), added a _feed_request() Bearer-auth helper that reads SYSTEM_ACCESSTOKEN, and rewrote _published_versions() to hit the feed. Mapped $(System.AccessToken) -> SYSTEM_ACCESSTOKEN on the wait step and relabeled the crates.io comments to "Azure Artifacts feed". One assumption: the build identity has read access to Mxc-Azure-Feed -- let me know if that needs onboarding.
| # !!! KNOWN-INCOMPLETE PLACEHOLDER -- DO NOT enable `publishCrates` trusting this | ||
| # list as-is. !!! | ||
| # This is an OUTDATED 10-crate closure. A later refactor inserted the `mxc_engine` | ||
| # hub crate ("shared by the public SDK and the executor binaries") between mxc-sdk | ||
| # and the backends, so the true `--all-features` first-party closure of mxc-sdk is | ||
| # now 17 crates (adds mxc_engine, windows_sandbox_common, isolation_session_common, | ||
| # isolation_session_bindings, wslc_common, hyperlight_common, nanvix_runner). | ||
| # Before the closure can actually publish, three things must be resolved (tracked | ||
| # in the session plan / PR #647 discussion): | ||
| # 1. 7 path deps in src/Cargo.toml [workspace.dependencies] lack a `version` | ||
| # (add `version = "0.7.0"` to match their 8 siblings). | ||
| # 2. `isolation_session_bindings` is `publish = false` (a Windows WinRT bindings | ||
| # crate) -- it must be made publishable or dropped from mxc_engine's published | ||
| # manifest, since crates.io validates even optional/feature-gated deps. | ||
| # 3. This list + the pipeline's `crateOrder` parameter must be reconciled to the | ||
| # final closure and OSPO OSS-release setup / crate-name reservation completed. | ||
| # Until then the pipeline ships with `publishCrates=false` (default) and the | ||
| # `verify-order` guard fails safely if `crateOrder` and this list disagree. |
There was a problem hiding this comment.
issue: We should update this to include all the crates that will be published as part of the mxc-sdk. That said, The crate names themselves should actually be prefixed with mxc- so they are easily discoverable in crates.io.
I think we also need a follow up PR to add a GitHub PR workflow/action to check that all crates are publishable.
There was a problem hiding this comment.
Agreed on all three -- two need a decision/setup before I wire them in, so flagging rather than guessing:
- Full closure: the real
mxc-sdk --all-featuresclosure is 17 crates (themxc_enginehub was added by a later refactor). Before expandingCRATES/crateOrder, two blockers need resolving -- 7 path deps insrc/Cargo.tomllack aversion, andisolation_session_bindingsispublish = false. Want those in this PR, or a follow-up? mxc-prefix: renaming published crate names touches each[package] nameand needs the names reserved on crates.io. Can you confirm the scheme (e.g.wxc_common->mxc-wxc-common)? I will apply it once we agree.- Publishability check action: agree this is the follow-up PR you mentioned; I can open it separately.
The list is a clearly-marked placeholder and publishCrates defaults false, so nothing ships until this is settled.
| # | ||
| # Single entry point for MXC package releases. A run can publish the npm SDK, | ||
| # the crates.io Rust crates, or both, selected by the `publishNpm` / | ||
| # `publishCrates` parameters in the "Run pipeline" dialog. Both feeds publish | ||
| # through ESRP Release, so no registry tokens live in this repo. | ||
| # | ||
| # Artifacts are consumed from the official build (`MXC-Official-Build`): | ||
| # * npm SDK -> `mxc-npm-sdk-package` (Package.NpmSdk.Job.yml) | ||
| # * crates.io -> `mxc-crates-package` (Package.Crates.Job.yml) | ||
| # | ||
| # crates.io specifics (per the ESRP crates.io docs): ESRP publishes pre-built | ||
| # `.crate` files under the official `microsoft-oss-releases` account via the | ||
| # crates.io API, which does NOT enforce dependency order. The crate closure is | ||
| # therefore published leaf-first, one ESRP task per crate, verifying each on the | ||
| # crates.io index before its dependents -- all handled by | ||
| # templates/Publish.CratesIo.Job.yml. | ||
| # | ||
| # --------------------------------------------------------------------------- | ||
| # ONE-TIME crates.io enablement (out of band -- not codeable in this repo) | ||
| # --------------------------------------------------------------------------- | ||
| # 1. OSPO / CELA open-source release registration + checklist (LICENSE, README, | ||
| # SECURITY.md, CONTRIBUTING.md) for publishing MXC to crates.io. | ||
| # 2. Reserve (or first-publish) the crate names under the `microsoft-oss-releases` | ||
| # account: mxc_telemetry, nanvix_common, mxc_pty, sandbox_spec, wxc_common, | ||
| # lxc_common, seatbelt_common, appcontainer_common, bwrap_common, mxc-sdk. | ||
| # 3. Have the ESRP Release team enable the `Rust` content type for this pipeline's | ||
| # ESRP client id (see the "Tenant does not have access to the given Main | ||
| # Publisher" note in the ESRP crates.io docs). The same App-Registration | ||
| # identity used for npm/signing (ESRPInfo below) is reused. | ||
| # 4. Allow read egress to the crates.io index (index.crates.io) on the release | ||
| # pool so the between-crates propagation check can confirm each publish. | ||
| # No CARGO_REGISTRY_TOKEN, variable group, or custom environment is required -- | ||
| # ESRP owns the crates.io credentials, and the 1ES production releaseJob provides | ||
| # the manual release gate. |
There was a problem hiding this comment.
issue: most of this comment can be removed or reduced now I believe.
There was a problem hiding this comment.
Done in 287305b -- trimmed the header to the essentials and dropped the stale "egress to index.crates.io" note (superseded by the private-feed fix).
|
|
||
| # Parameters for ESRP release info will be passed from the ADO UI. | ||
| parameters: | ||
| - name: publishNpm |
There was a problem hiding this comment.
Note: for your testing, you can add a dry-run boolean in here too, that when set won't go into the EsrpRelease@10 tasks. You can actually test this out if you create an in repo branch (noticed this PR is coming from your fork). But yea if you create an in repo branch e.g user/dhoehna/test-rust-publishing with these same changes, you should be able to use the branch to run the official pipeline via https://microsoft.visualstudio.com/Dart/_build?definitionId=190018 and then see any errors that pop up so you can fix them in this PR. Let me know if you don't have access.
There was a problem hiding this comment.
Added a cratesDryRun boolean in 1ES.Release.yml (passed through to Publish.CratesIo.Job.yml) in 287305b: when set, each crate still runs stage + verify-order but skips the EsrpRelease task and the feed wait, so the plumbing runs without publishing.
On the in-repo branch: I do not have push access to microsoft/mxc from here (this PR is from my fork), so I cannot create user/dhoehna/test-rust-publishing myself. Could you create it from these changes (or grant access)? Then I will run pipeline 190018 against it and fix whatever surfaces.
The 1ES official build runs network-isolated and cannot reach the public crates.io sparse index (https://index.crates.io), so crates_release.py's post-publish version check would fail at runtime. Query the private Mxc-Azure-Feed sparse index instead (reachable in-pipeline), authenticated with the build identity's $(System.AccessToken). - crates_release.py: replace CRATES_IO_SPARSE_INDEX with AZURE_FEED_SPARSE_INDEX (matches .azure-pipelines/.cargo/config.toml); add _feed_request() Bearer-auth helper reading SYSTEM_ACCESSTOKEN; _published_versions() now hits the feed; relabel poll/index wording to "Azure Artifacts feed". - Publish.CratesIo.Job.yml: map $(System.AccessToken) -> SYSTEM_ACCESSTOKEN on the wait step so the feed request can authenticate. Addresses Brandon Bonaby's review comment on PR microsoft#647. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 54ab05fd-1ef7-4fa4-b130-ea17a9ef8a47
| # This is an OUTDATED 10-crate closure. A later refactor inserted the `mxc_engine` | ||
| # hub crate ("shared by the public SDK and the executor binaries") between mxc-sdk | ||
| # and the backends, so the true `--all-features` first-party closure of mxc-sdk is | ||
| # now 17 crates (adds mxc_engine, windows_sandbox_common, isolation_session_common, | ||
| # isolation_session_bindings, wslc_common, hyperlight_common, nanvix_runner). |
| python3 "$(script)" stage --order-file "$(orderFile)" --crate "${{ crate }}" --out-dir "$(esrpDir)" | ||
| displayName: "Stage ${{ crate }} for ESRP" | ||
|
|
||
| - task: EsrpRelease@12 |
| # ESRP ran with waitforreleasecompletion:true, so the publish itself already | ||
| # completed; only index propagation is unconfirmed. Warn (don't fail the | ||
| # release) and let the next crate proceed. | ||
| print(f"##vso[task.logissue type=warning]{crate} {version} not confirmed on the " | ||
| f"Azure Artifacts feed within {args.timeout}s; continuing (ESRP reported the " | ||
| f"publish complete).") | ||
| return 0 |
| - name: publishCrates | ||
| displayName: "Publish the Rust crate closure to crates.io" | ||
| type: boolean | ||
| default: false |
Applies Brandon Bonaby's PR microsoft#647 review comments: - 1ES.Build.Stages.yml: gate the Package_Crates stage on isOfficialBuild so crate packaging only runs on the official signed build, not PR/CI. - 1ES.Release.yml: default publishNpm to false too, so the operator must explicitly select npm and/or crates in the run dialog. - 1ES.Release.yml: reduce the oversized header comment (and drop the stale index.crates.io egress note superseded by the private-feed fix). - Add a cratesDryRun parameter (1ES.Release.yml -> Publish.CratesIo.Job.yml) that runs stage + verify-order but skips the EsrpRelease publish and the feed wait, for testing the pipeline without publishing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 54ab05fd-1ef7-4fa4-b130-ea17a9ef8a47
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 16 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (4)
.azure-pipelines/scripts/crates_release.py:99
- The packaged list is not the
mxc-sdkdependency closure:mxc-sdkdirectly depends onmxc_engine(src/core/mxc-sdk/Cargo.toml:15), but that crate and its seven first-party dependencies are absent here, their workspace dependencies still lack versions, andisolation_session_bindingsremains non-publishable. Cargo rejects a packaged crate whose registry manifest contains a versionless path dependency, so the new unconditionalPackage_Cratesstage will fail every official build instead of producing the release artifact. Complete the 17-crate closure and its publish metadata before enabling this stage.
"mxc-sdk",
.azure-pipelines/1ES.Release.yml:45
- These defaults do not provide the advertised safe preview: with both publish flags false the template expands to no release stages, while enabling
publishCrateswithout also changing the secondary option immediately takes the live ESRP branch becausecratesDryRunis false. Default the crate action to a dry run (or provide an equivalent explicit no-op/validation design) so a default run cannot accidentally publish.
default: false
- name: cratesDryRun
displayName: "Crates dry run: stage + verify only, skip the ESRP publish"
type: boolean
default: false
.azure-pipelines/templates/Publish.CratesIo.Job.yml:76
- Every rerun invokes ESRP for every crate unconditionally. The only
_published_versionscall occurs later inwait, so an existing crate version is never skipped and crates.io's duplicate-version rejection will fail a resumed release. Add a pre-publish check that bypasses the ESRP task and wait for versions already present, as promised by the PR's idempotency contract.
- task: EsrpRelease@12
.azure-pipelines/scripts/crates_release.py:308
- Returning success after propagation times out defeats the leaf-first ordering guarantee: the next ESRP upload can run while this dependency is still unresolved and fail after earlier crates have already been released.
waitforreleasecompletionconfirms the ESRP operation, not index visibility, so stop the release when visibility cannot be confirmed.
print(f"##vso[task.logissue type=warning]{crate} {version} not confirmed on the "
f"Azure Artifacts feed within {args.timeout}s; continuing (ESRP reported the "
f"publish complete).")
return 0
| default: false | ||
|
|
||
| jobs: | ||
| - deployment: publish_crates |
Resolve src/Cargo.toml conflict in [workspace.dependencies]: keep main's roxmltree -> quick-xml migration and the new windows_sandbox_lifecycle dependency, alongside the version = "0.7.0" pins added for crate publishing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 54ab05fd-1ef7-4fa4-b130-ea17a9ef8a47
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 16 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (4)
.azure-pipelines/scripts/crates_release.py:89
CRATESis not themxc-sdkdependency closure:mxc-sdkdirectly depends onmxc_engine, which is omitted and still has no registry version insrc/Cargo.toml; its additional first-party dependencies are likewise unresolved. Because the newPackage_Cratesstage invokes this command on every official build,cargo packagewill reject the SDK manifest and no release artifact will be produced. Complete and version the full closure (including resolving the non-publishable bindings crate) before enabling this stage.
CRATES: list[str] = [
.azure-pipelines/1ES.Release.yml:45
- Selecting
publishCrateswhile leaving this option untouched immediately enters the live ESRP branch, contrary to the PR's documented default-on, non-publishing preview. Make dry-run the safe default so an operator must explicitly opt into publication.
default: false
.azure-pipelines/templates/Publish.CratesIo.Job.yml:34
- This deployment is not bound to the
MXC-CratesIo-Productionenvironment, so that environment's manual approval checks will not run;templateContext.isProductiononly classifies the 1ES job. Explicitly target the environment described by the release contract before any publish steps execute.
- deployment: publish_crates
displayName: Publish crates to crates.io (ESRP)
.azure-pipelines/templates/Publish.CratesIo.Job.yml:76
- Every retry invokes ESRP for every crate unconditionally; the existing-version lookup only runs afterward in
wait. Since crates.io rejects an already-published name/version, a retry after a partial release fails on the first completed crate instead of resuming as the PR promises. Add a pre-publish existence check and condition both this task and its wait step on the version being absent.
- task: EsrpRelease@12
|
@bbonaby quick process question. I now have write access to As you noted earlier, this fork-based PR can't actually exercise the release pipeline end-to-end: the official build and Would you like me to abandon this PR and re-create it from an in-repo branch (e.g. |
Yes let's abandon this one and make a new in repo branch for it. You can do the testing for your branch before you create a PR too. That's what I usually do so I can try things out fast without having Github send PR emails to reviewers about new commits. |
|
Closing in favor of an in-repo branch. Per discussion with @bbonaby above, I've moved these changes to A fork PR can't exercise the release pipeline (the official build and |
Summary
Adds a one-click Azure DevOps pipeline that publishes the
mxc-sdkcrate and its first-party dependency closure to crates.io, plus the crate-metadata changes required to make that closure publishable.This branch contains two logical commits:
Cargo.tomlfiles) — registry-agnostic metadata: workspacerepository, per-cratedescription/license/repository, dual{ path, version }on internal path deps, and makingsandbox_specpublishable. Required to publish from anywhere..azure-pipelines/1ES.Crate.Release.ymland.azure-pipelines/scripts/publish_crates_to_cratesio.py.How it works
Run pipelinewith dryRun = true (the default) previews every crate viacargo package --listand publishes nothing.MXC-CratesIo-Productionenvironment, then publishes leaf-first, waiting for index propagation between crates. Re-runs are idempotent (a version already on crates.io is skipped).Secrets stay out of the repo
CARGO_REGISTRY_TOKENcomes from the ADO variable groupMXC-CratesIo-Publishand is mapped to the publish step's env only. Nothing secret is committed.One-time setup (not codeable in the repo)
Variable group, environment + approval, crates.io egress allowlist, crate-name reservation / OSPO sign-off, and registering the pipeline. See the checklist at the bottom of
1ES.Crate.Release.yml.Microsoft Reviewers: Open in CodeFlow