Skip to content

feat(fx-core): add prepared SharePoint publication - #16950

Open
Bert Jansen (jansenbe) wants to merge 12 commits into
OfficeDev:devfrom
jansenbe:jansenbe-atk-sharepoint-publish-actions
Open

Bert Jansen (jansenbe) wants to merge 12 commits into
OfficeDev:devfrom
jansenbe:jansenbe-atk-sharepoint-publish-actions

Conversation

@jansenbe

@jansenbe Bert Jansen (jansenbe) commented Nov 7, 2026 •

Copy link
Copy Markdown

Summary

  • Adds opt-in spfx/preparePublication to inspect the completed SPPKG, extract the supported API plugin, remove the embedded agent package from a separate SharePoint deployment copy, prepare the authoritative app package, and persist SHA-256 lineage in a project-local journal.
  • Extends existing spfx/deploy and teamsApp/publishAppPackage actions only when prepared-publication inputs are supplied. Requires explicit approval, checks identity/version/collisions, uses single-attempt writes, verifies SharePoint bytes and deployment before Graph submission, and blocks automatic replay after rejected or unknown writes.
  • Adds the supported offline API to lean @microsoft/teamsfx-core/build/sharepointPackage:
    assembleSharePointApplicationPackage(input: AssembleSharePointApplicationPackageInput): Promise<Result<SharePointApplicationPackage, SharePointApplicationPackageError>>.
    Input is the authoritative applicationPackage: Buffer plus selected sharePointPackage: Buffer. Output includes application ZIP bytes/hash, source application/SPPKG hashes, plugin hash, app ID/version, product ID, managed reference kind/ID/path, and { level: "local-structural", tenantNeutral: true }.
  • Offline assembly shares the bounded production inspector/assembler, initializes no lifecycle/auth services, accesses no filesystem/network, and writes no journal. The caller owns persistence/export. Offline assembly retains literal {{TENANT_MCP_URL}} in the managed runtime URL. Tenant-bound preparation replaces that contribution in the staged M365 application ZIP using the verified catalog HTTPS origin and selected solution ID before hashing/submission; it does not broadly substitute unrelated text. Original application ZIP and SPPKG remain unchanged.
  • Connector-only agentConnectors[].toolSource.plugin registration is supported only where the schema defines it (currently devPreview). GA through v1.30 fails with UnsupportedAgentConnectorManifestVersion. No automatic upgrade, remoteMcpServer substitution, schema-invalid output, or synthetic agent. GA connector-only support is an unresolved future capability outside this draft by explicit decision.
  • FxCoreClient.publish still executes YAML publish: unchanged. Ordinary non-prepared behavior and packages/cli remain unchanged; no WIQD source is included or modified.

Spec and design

Acceptance criteria to tests

AC Coverage
SPFX-PREP-01 Bounded package/component inspection and plugin extraction
SPFX-PREP-02/05 Authoritative app assembly, deterministic artifacts, final schemas, token tenant/audience mismatch, devPreview connectors and typed GA refusal
SPFX-PREP-03/04 OPC transformation/preservation; traversal, duplicate/header/compression/expansion/metadata limits; post-fstat growth capped at limit + 1 byte; rejection before parsing/staging
SPFX-PREP-06 Deploy integration: collisions/downgrades/reuse, bound YAML hashes, source/artifact identity, and preflight refusal before approval/writes
SPFX-PREP-07/09 Real publish-client YAML scenario: SharePoint-first ordering and hash-bound submitted bytes; catalog-response completeness/pagination and single-attempt Graph outcomes
SPFX-PREP-08 Identity-wide concurrency; cancellation/read-poll/settlement boundaries; rejected/unknown writes block replay; atomic storage/lock failures, external journal changes and previous-transformation compatibility preserve recovery data
SPFX-PREP-10 Existing actions retain the non-prepared path
SPFX-PREP-11 Allowlisted recovery diagnostics preserve durable stage/hash/identity evidence and reject unsafe service codes/request IDs/error names
SPFX-PREP-12 Public lean-entry neutral assembly/provenance, real converter validation/refusal, determinism, preserved content and offline side-effect isolation
SPFX-PREP-13 Package-to-prepare handoff replaces the managed entry once and resolves the endpoint; legacy content-hash migration leaves exactly one resolvable binding-hash reference, including connector-to-agent root-manifest persistence
SPFX-PREP-14 Public API malformed/oversized ZIP and per-part 1 MiB metadata cap (input/output), ownership/path collisions, corrupt managed content and orphaned deterministic plugin files

Historical validation at 65322350e8b7a73b6c7b77cc9ac937c3d0861ef9

The following CI and full-suite evidence belongs to that earlier tests-only head; it is retained as history and is not claimed as validation of later commits.

Official CI results for this head

  • codecov/patch: SUCCESS — 91.62% of diff hit, target 88.88%.
  • codecov/project: SUCCESS — 89.01%, reported +0.12% compared with 0f174df.
  • Codecov report: 1,630 patch hits, 64 misses, 85 partials; project 34,651 hits / 38,928 locations.
  • Unit-test workflow: SUCCESS, including unit-test and scenario-tooling-windows.
  • Format, sensitive-content, YAML-lint and CLA checks passed.

Local evidence (Node 22, packages/fx-core)

  • npm run test:unit passed: 339 files passed, 1 skipped; 7,796 tests passed, 35 skipped, 1 todo, 0 failed. Full-package Istanbul coverage: lines 94.10%, statements 93.72%, branches 88.70%, functions 93.20%. The unchanged global coverage gate passed.
  • Branch-complete patch measurement against PR base 0f174dfa8ab211614517a4a13fabdbfc99e06310: 91.62% (1,630/1,779), matching official Codecov. This calculation uses the LCOV union of line/branch locations on diff-added source lines and counts uncovered branches as partial/uncovered locations. It also reproduced the preceding live result 69.03% (1,228/1,779) before these tests.
  • Correction: the previous report's 83.68% (1,390/1,661) counted ordinary line hits, not Codecov's branch-complete patch metric. Whole-file percentages are not the patch gate either.
  • npm run build passed, including scenario-tooling type-check.
  • eslint --quiet and prettier --check on all five changed test files passed. Test-tsconfig type-check reports zero errors in those five files; the complete test-tsconfig has errors elsewhere and is not claimed green.
  • Full-package npm run lint -- --quiet still reports the unnecessary assertion at PluginManifestUtils.ts:407. This file is byte-identical to PR base 0f174dfa: Git blob 968da2160f7558905f32f9007f6ed69a8bfbd0d2 in both base and head. It was not changed or suppressed.
  • Full-suite output still includes one non-failing Assertion failed, previously traced to unchanged tests/component/provisionUtils.test.ts:9 importing console.assert; this is not represented as a clean-output run.
  • No tenant/Azure operations/sign-in. Unrelated existing worktree changes were not included.

Earlier runtime corrections remain covered: tenant/audience verification before prepared writes, identity-lock error preservation, bounded-read change detection without metadata-only ctime false positives, deferred-HTTP settlement tests, interleaved catalog requests, and typed pre-write errors with recovery data. This follow-up changes none of those inputs. The separate ctime check in openPlugin/fileSystem.ts:193 remains untouched. A generator concurrency flake was observed historically; the current full run above passed.

Separately disclosed gates

  • Aggregate CodeQL is still NEUTRAL on this head, with exact summary: "Code scanning cannot determine the alerts introduced by this pull request, because 1 configuration present on refs/heads/dev was not found" (API upload/<default>). Both language jobs succeeded, but those successes and zero annotations are not a clean aggregate result. No suppression, permission broadening or workflow change.
  • Missing qualifying ATK work item: pr-title fails until linked.
  • Human design/AC Gates 1 and 2 remain deferred by the user; no live-tenant qualification is claimed.
  • The full-package lint and test-tsconfig limitations above remain separate from the now-passing Codecov approval gates.

Deferred gates and open questions

At the user's direction, vibe-coding human Gate 1 (resolve spec questions) and Gate 2 (approve the acceptance criteria) are deferred to draft review. Maintainers should review the ACs and decide:

  • Whether archive/metadata limits cover the supported SPFx producer matrix.
  • Whether catalog read-back fields/download permissions suffice across supported clouds, or additional clouds must remain unsupported.
  • Whether explicit approval works for hosts without interactive UI.
  • Whether definitive rejection can retry from the same journal or requires operator reconciliation.
  • Which generated component schema defines approved {{TENANT_ORIGIN}} UI CSP fields; no field path is qualified, so no broad substitution is performed.

GA connector-only registration is a future capability excluded from this draft by decision, not a pending decision for this draft.

Work item and follow-up

Current PR state at the latest refresh: open and not draft; this update does not change PR state. WEX PBI 3359458 is context only and does not satisfy lint-pr; a qualifying ATK work item must be linked to clear the failing pr-title check. Consumer-adapter adoption follows separately; this PR modifies ATK only.

Latest reviewer follow-up — a85ae56a70712c44ad540bcf0da1a678bc00db87

Addressed all four Sebastien Levert inline findings; the threads are intentionally left unresolved for the PR author to resolve manually:

  • F1 — nested agent paths: action file values are relative to the referenced agent manifest, while managedReference.entryPath separately identifies the root-relative ZIP entry. Preparation/schema and artifact-lineage validation resolve references consistently. Transformation identity is v4; v2/v3 retain their prior root-path interpretation for compatibility.
  • F2 — completed replay: the deploy action now accepts only a fully confirmed submitted journal (including SharePoint upload/deploy, Graph submission, and submitted app ID) after the existing source/artifact/hash/identity checks, then returns without remote writes. This lets the composed YAML lifecycle replay and lets publish return the recorded result.
  • F3 — verified-stage reporting: lastSharePointVerifiedState is emitted only when durable upload and deployment states are both confirmed, not merely because the journal is terminal.
  • F4 — write diagnostics: SharePoint upload/deploy failures retain the caught error as cause; the error-data projection still exposes only allowlisted HTTP status, service code, and request ID.

Verification at this head

  • Focused fx-core tests: 5 files, 285 tests passed (packageFile, preparedRecovery, preparedPublication.contracts, preparedPublication.integration, preparedPublication).
  • tsc -p . --incremental false and typecheck:scenario-tooling: passed.
  • Prettier check for changed source/tests/spec and git diff --check: passed. Changed-file precommit (eslint --fix --quiet, Prettier) passed. Full package lint remains blocked by the same unrelated teamsApp/utils/utils.ts:407 unnecessary assertion; the file blob matches origin/dev (d3ec652b9b426f62a7732725cd4c277923cf5e26).
  • Focused Istanbul measurement for the four publication source modules: lines 88.09% overall (selected source files; not the PR patch-coverage metric).
  • Latest observed GitHub checks: unit-test, scenario-tooling, Analyze (C# and JavaScript), codecov/patch, codecov/project, and CLA passed; format, sensitive-content, and YAML lint were pending at the last refresh; pr-title failed because a qualifying ATK work item is missing; CodeQL was skipped. The historical CodeQL note below refers to an earlier head; it is not a result for this commit.
  • Commit a85ae56a70712c44ad540bcf0da1a678bc00db87 is pushed to the existing PR branch. Precommit was run directly and passed; the commit hook itself could not launch /bin/bash in this Windows environment. No tenant operations were run. Existing unrelated worktree changes remain unstaged.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@codecov

codecov Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.43646% with 155 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.00%. Comparing base (0f174df) to head (a85ae56).
⚠️ Report is 3 commits behind head on dev.

Files with missing lines Patch % Lines
.../driver/deploy/spfx/utility/preparedPublication.ts 91.31% 23 Missing and 29 partials ⚠️
...e/src/component/driver/deploy/spfx/deployDriver.ts 86.15% 16 Missing and 24 partials ⚠️
...t/driver/deploy/spfx/utility/applicationPackage.ts 91.63% 10 Missing and 13 partials ⚠️
...src/component/driver/teamsApp/publishAppPackage.ts 89.83% 8 Missing and 10 partials ⚠️
...omponent/driver/deploy/spfx/utility/packageFile.ts 93.48% 7 Missing and 10 partials ⚠️
...ent/driver/deploy/spfx/preparePublicationDriver.ts 97.14% 0 Missing and 2 partials ⚠️
...ponent/driver/deploy/spfx/utility/tokenIdentity.ts 94.87% 2 Missing ⚠️
packages/fx-core/src/common/wrappedAxiosClient.ts 0.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##              dev   #16950      +/-   ##
==========================================
+ Coverage   88.88%   89.00%   +0.12%     
==========================================
  Files         689      695       +6     
  Lines       37101    38959    +1858     
  Branches     9302     9934     +632     
==========================================
+ Hits        32977    34677    +1700     
- Misses       2206     2270      +64     
- Partials     1918     2012      +94     
Files with missing lines Coverage Δ
packages/fx-core/src/client/graphClient.ts 95.40% <100.00%> (+0.90%) ⬆️
...s/fx-core/src/component/configManager/validator.ts 75.00% <ø> (ø)
.../component/driver/deploy/spfx/utility/spoClient.ts 100.00% <100.00%> (ø)
packages/fx-core/src/sharepointPackage.ts 100.00% <100.00%> (ø)
packages/fx-core/src/common/wrappedAxiosClient.ts 89.66% <0.00%> (-0.34%) ⬇️
...ent/driver/deploy/spfx/preparePublicationDriver.ts 97.14% <97.14%> (ø)
...ponent/driver/deploy/spfx/utility/tokenIdentity.ts 94.87% <94.87%> (ø)
...omponent/driver/deploy/spfx/utility/packageFile.ts 93.48% <93.48%> (ø)
...src/component/driver/teamsApp/publishAppPackage.ts 90.97% <89.83%> (-2.29%) ⬇️
...t/driver/deploy/spfx/utility/applicationPackage.ts 91.63% <91.63%> (ø)
... and 2 more

... and 6 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Teams manifest schemas through v1.30 define ToolSource with remoteMcpServer only; plugin exists only in devPreview. Connector-only SharePoint assembly and preparation now fail with UnsupportedAgentConnectorManifestVersion instead of emitting schema-invalid output. Regressions use the genuine TeamsManifestConverter.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@azure-pipelines

Copy link
Copy Markdown
Contributor
There was an error handling pipeline event 0f4eee12-b214-4de0-9993-e22652d75944.

…e auth errors

- Re-verify Graph/SharePoint token tenant and audience against the journal
  before every prepared write (tokenIdentity.ts).
- Scope identity-lock failures to lock acquisition so inner errors keep codes.
- Replace ctime drift checks with size/mtime/content checks on bounded reads.
- Preserve typed pre-write FxErrors in publishAppPackage and attach
  allowlisted recovery data instead of relabeling them.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Adds AC-tagged contract tests for token tenant/audience mismatch, identity
lock conflicts and simultaneous preparations, interleaved two-tenant catalog
requests, cancellation at read/poll/approval/settlement boundaries, deploy and
submission refusal branches, driver input/output validation, and journal,
source and artifact integrity checks. Timing tests use deferred fakes instead
of wall-clock waits.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Exercise bounded archives, public assembly refusals, catalog validation, and durable mutation/preflight failures without changing coverage thresholds or runtime behavior.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated review with GitHub Copilot of head 65322350e8b7a73b6c7b77cc9ac937c3d0861ef9 against base 0f174dfa8ab211614517a4a13fabdbfc99e06310. The 681 focused tests, source typecheck, and changed-production-file lint passed; the four findings below were reproduced with isolated offline probes. No live-tenant validation was performed.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

This branch has not been deployed

No deployments
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.

3 participants