mcpField
I
import-solution

import-solution

github.com/microsoft云与 DevOps1.1k162 次安装今天更新
代码仓库

关于 import-solution

此技能将解决方案 ZIP 导入目标 Dataverse 环境,并可先进行暂存导入以检查缺失依赖。它面向执行手动环境推广的 Power Platform 项目,也会在导入前检查文件并提示可能需要的后续处理。

主要功能

  1. 01通过 ImportSolutionAsync 导入解决方案 ZIP
  2. 02可选暂存导入以检查依赖
  3. 03检查导入目标并要求确认
  4. 04提示连接引用及云流相关事项

适用场景

  1. 01将已导出的解决方案导入目标环境
  2. 02在正式导入前检查缺失依赖
  3. 03识别导入后需要绑定连接的解决方案

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/microsoft/power-platform-skills --skill import-solution

安装后在 Claude Code 中输入/import-solution确认已加载。

附带文件scripts/validate-import.js

详细介绍

插件检查:运行 node "${PLUGIN_ROOT}/scripts/check-version.js"——如果输出消息,请在继续前展示给用户。

import-solution

通过 ImportSolutionAsync 将解决方案 ZIP 导入目标 Dataverse 环境。支持通过 StageSolution 进行可选的暂存导入,以便在提交前检查是否缺少依赖项。

前提条件

  • 已安装 PAC CLI,并已通过身份验证连接到目标环境
  • 已安装 Azure CLI 并完成登录
  • 解决方案 ZIP 文件已存在于磁盘上(由 export-solution 生成)

阶段

阶段 0 — ALM 计划门控

plan-alm 是入口。 当用户表达 ALM 意图(提升 / 发布 / 部署 / 设置 CI-CD / 移至暂存环境 / 推送到生产环境)时,应首先运行编排器(/power-pages:plan-alm)。此阶段 0 会强制执行这一要求;没有计划时应拒绝继续,而不是只检查一次后就让用户永远忽略。

跳过规则。 如果此技能是作为正在执行的 plan-alm 编排的一部分调用的,则完全跳过阶段 0,直接进入阶段 1。门控辅助程序通过其 inExecution 块提供此信息——当符合以下条件时,静默进入阶段 1:

inExecution.status === "active"

辅助程序根据 docs/.alm-plan-data.json 计算此状态——PLAN_STATUS === "In Execution",且 LAST_INVOCATION_AT 在最近 60 分钟内。每次调用发现计划正在执行时,check-alm-plan.js 都会自动刷新 LAST_INVOCATION_AT,因此链中的每个技能都会让后续技能保持在执行链中——即使部署持续数小时(单个 deploy-pipeline 阶段就可能需要 60 分钟),也不会超出时间窗口,导致执行链被错误地重新分类。停滞的执行链(超过 60 分钟没有心跳)会被重新分类为 stale-heartbeat,阶段 0 门控将正常触发,避免废弃的计划悄然绕过用户确认。

当 inExecution.status 不为 "active"("not-running"、"stale-heartbeat"、"no-plan")时,运行以下阶段 0 门控流程。根据辅助程序返回的其余字段进行分支处理:

步骤 1 — 运行门控辅助程序。

node "${PLUGIN_ROOT}/scripts/lib/check-alm-plan.js" --projectRoot "."

辅助程序会返回 JSON,其中包含 { exists, deferred, stale, staleness: { reason, detail }, generatedAt, planStatus, ... }。如果还需要检查计划的新鲜度,请在阶段 1 获取 --envUrl、--token、--solutionId 后传入;否则,辅助程序只检查计划是否存在,这足以支持以下门控决策。

步骤 2 — 根据结果分支处理。

结果操作
deferred: true用户已明确将此项目的 ALM 延后处理(存在 .alm-deferred 标记)。静默进入阶段 1——不要反复提醒。
exists: false用户尚未运行 plan-alm。请参见步骤 3。
exists: true, stale: false计划为最新状态。静默进入阶段 1。
exists: true, stale: true(原因:solution-modified)解决方案在生成计划后发生了更改。请参见步骤 4。

步骤 3 — 没有计划。 告知用户:

“此项目没有 ALM 计划。/power-pages:plan-alm 可生成计划——它会检测项目状态,询问你的提升策略(PP Pipelines 还是手动导出/导入),并按正确顺序编排相应技能(包括此技能)。要我现在运行 plan-alm 吗?”

🚦 门控(意图 · import-solution:0.no-plan): 当 check-alm-plan.js 返回 exists:false 时,进入拒绝继续的门控。由辅助脚本提供支持。

AskUserQuestion:

问题标题选项
先运行 /power-pages:plan-alm?ALM 计划门控是——现在运行 /power-pages:plan-alm(推荐),不使用计划继续(高级——我知道自己在做什么),取消
  • 是(推荐) → 调用 /power-pages:plan-alm。它会生成计划并返回——plan-alm 是规划器,不会执行部署。随后此技能会重新运行阶段 0 检查(此时 exists:true),并进入阶段 1。
  • 不使用计划继续 → 设置 BYPASSED_PLAN_GATE = true,并进入阶段 1。
  • 取消 → 正常退出。

步骤 4 — 计划已过期。 告知用户:

“ALM 计划生成于 {generatedAt},但源解决方案此后已修改(修改时间为 {solution.modifiedon})。组件可能发生了变化。重新运行 plan-alm 将刷新分析结果和渲染后的 HTML。”

🚦 门控(意图 · import-solution:0.stale-plan): 当 check-alm-plan.js 返回 stale:true 时,失败即关闭的入口门控。由辅助脚本支持。

AskUserQuestion:

问题标题选项
先刷新计划?ALM 计划时效性刷新 — 重新运行 /power-pages:plan-alm(推荐)、继续使用现有计划、取消
  • 刷新(推荐) → 调用 /power-pages:plan-alm。完成后,再运行一次 Phase 0 辅助程序以确认计划为最新;如果仍然过时,显示详情,然后继续执行 Phase 1(不要无限循环)。
  • 继续 → 设置 STALE_PLAN_ACK = true 并继续执行 Phase 1。
  • 取消 → 正常退出。

此门控的存在原因。 直接调用 import-solution 会将 zip 部署到目标环境,但不会经过协调器的部署策略选择或导入后的验证步骤。单独运行此技能的用户经常会跳过分阶段导入依赖项检查、遗漏目标环境的环境变量覆盖值,也没有由计划跟踪的记录来说明哪个环境接收了哪个版本的构件。此门控可确保 plan-alm 已运行(因此已选择策略、已将各阶段的值记录在 deployment-settings.json 中,并且部署可重现),或用户明确选择绕过它。

Phase 1 — 验证先决条件

在此阶段开始时一次性创建所有任务。

要创建的任务:

  1. “验证先决条件”
  2. “定位解决方案文件”
  3. “配置导入”
  4. “暂存解决方案(依赖项检查)”
  5. “导入解决方案”
  6. “验证导入”
  7. “检测云端流”
  8. “显示摘要”

注意:如果导入因 AttachmentBlocked 错误而失败,Phase 5b 补救流程会直接内联运行——无需创建额外任务(它会在“导入解决方案”任务中继续执行)。如果解决方案 zip 中没有 Workflows/*.json 文件,则会自动跳过“检测云端流”任务。

步骤:

  1. 运行 pac env who — 提取 environmentUrl(确认这是目标环境)
  2. 运行 az account get-access-token --resource "{environmentUrl}" --query accessToken -o tsv — 获取令牌
  3. 验证 API 访问:GET {environmentUrl}/api/data/v9.2/WhoAmI
  4. 显示目标环境 URL,并请用户在继续前确认其正确无误。

重要:与用户确认目标环境——导入到错误的环境可能造成干扰。

如果任何检查失败,请停止(参考 ${PLUGIN_ROOT}/references/dataverse-prerequisites.md)。

Phase 1.5 — 依据当前 ALM 文档进行核实

参考:${PLUGIN_ROOT}/references/alm-docs-grounding.md

此步骤最多用时约 30 秒。如果 MCP 搜索或获取出错,记录一行说明并继续——此技能必须能够离线运行。

  1. 使用查询 Power Pages solution import staging missing dependencies ImportSolutionAsync ALM 运行 microsoft_docs_search。
  2. 通过 microsoft_docs_fetch 并行获取 https://learn.microsoft.com/en-us/power-platform/alm/solution-concepts-alm(以及最多一个关于分阶段导入或依赖项处理的相关页面)。
  3. 提炼一段摘要,说明 Microsoft Learn 当前对暂存与直接导入、依赖项解析以及组件级错误处理的介绍。与 ${PLUGIN_ROOT}/references/solution-api-patterns.md 对比,并标出 ImportSolutionAsync / StageSolution 签名是否存在差异。
  4. 使用该摘要为 Phase 2 及后续决策提供依据。不要悄悄更改技能行为——在 Phase 4(实际导入)之前,将任何差异作为非强制警告告知用户。
阶段 2 — 定位解决方案文件
  1. 如果提供了 zip 路径作为参数,请直接使用

  2. 否则,搜索解决方案 zip 文件:glob('**/*.zip', { ignore: ['**/node_modules/**'] })

  3. 对找到的每个 zip 文件,验证其中是否包含 solution.xml:

    • 使用 Bash:unzip -l "{zipPath}" 2>/dev/null | grep -qi solution.xml
  4. 如果找到多个有效的 zip 文件,请让用户选择:

    🚦 关卡(plan · import-solution:2.multiple-zips): 在项目根目录下发现了多个有效的解决方案 zip 文件。由用户选择要导入的文件。取消操作会在与目标环境进行任何交互之前退出。

    使用 AskUserQuestion,为每个有效的 zip 文件提供一个选项——显示文件名、大小和修改日期,方便用户识别正确的文件(通常应选择最近导出的文件)。

  5. 如果未找到有效的 zip 文件:停止并说明原因——请先运行 export-solution,或提供 zip 路径

步骤 5a — 导入前内容检查(确认 zip 文件后、向用户展示之前运行):

检查 zip 文件,列出导入后需要手动完成的事项。按顺序运行所有检查:

# 1. Connection references (require user-binding post-import)
unzip -p "{zipPath}" customizations.xml 2>/dev/null | grep -c '<connectionreference '

# 2. Bots (require republishing in target environment)
unzip -l "{zipPath}" 2>/dev/null | grep -qi "bots/" && echo "found" || echo "none"

# 3. Cloud flows with hardcoded environment URLs (will silently fail in target)
unzip -p "{zipPath}" "Workflows/*.json" 2>/dev/null | grep -o '"organization":\s*"https://[^"]*"' | head -5

根据检查结果构建 postImportWarnings 列表:

检查结果要显示的警告
connectionreference 数量 > 0"⚠️ 连接引用:此解决方案包含 N 个连接(例如 Dataverse 连接器)。导入后,你必须将每个连接引用绑定到目标环境中的有效连接,否则依赖这些连接的云端流将被禁用。"
存在 bots/ 文件夹"⚠️ Copilot Studio 机器人:此解决方案包含一个机器人。导入后,必须在目标环境中重新发布该机器人,才能完成预配。"
找到硬编码的组织 URL"⚠️ 硬编码的环境 URL:云端流中包含硬编码的环境 URL({foundUrl})。这会导致导入后该流仍调用源环境。请在目标环境中编辑该流以更新 URL。"

如果 postImportWarnings 不为空,请在展示 zip 文件详情之前直接显示所有警告(不要通过 AskUserQuestion 显示)。用户应在确认之前看到这些警告。

展示所选 zip 文件的详情(名称、大小、路径)和任何导入前警告,并向用户确认。

阶段 3 — 配置导入

步骤 3.0 — 版本偏差提示(在提问前进行只读检查)。

在询问用户是采用暂存导入还是直接导入之前,查询目标环境中 zip 文件所含解决方案唯一名称对应的已安装解决方案,并将其版本与 zip 文件中的版本进行比较。目的是在用户一路确认之前提示“即将导入的版本与已安装版本相同”——这是手动导出/导入流程中最常见的静默失败模式,因为只有在源端提升版本号,才能生成明确可升级的构件。

  1. 从 zip 文件中的 solution.xml 提取 uniqueName 和 version:

    unzip -p "{zipPath}" solution.xml 2>/dev/null | head -50
    

    从 XML 中解析 <UniqueName> 和 <Version>。分别存储为 ZIP_SOLUTION_NAME 和 ZIP_SOLUTION_VERSION。

  2. 查询目标环境中已安装的解决方案:

    GET {envUrl}/api/data/v9.2/solutions?$filter=uniquename eq '{ZIP_SOLUTION_NAME}'&$select=solutionid,uniquename,version,ismanaged
    

    将结果存储为 INSTALLED(如果筛选结果返回空的 value 数组,则存储为 null)。

  3. 通过共享辅助函数比较版本,然后根据结果分支处理。

    前置条件——如果 INSTALLED 为 null,则跳过整个步骤。 这是首次安装的情况:目标环境中没有可供比较的版本。不要将 null 代入 '{INSTALLED.version}' 后调用辅助函数——这会抛出“version is required”错误,使 Agent 无法进入任何分支。直接跳到下方的暂存/直接导入提示,并将本次导入视为全新安装。

    否则(INSTALLED 不为 null),比较版本:

    重要:不要使用原始的 > / < / === 比较版本字符串——Dataverse 版本由 4 段整数组成(1.0.0.9 与 1.0.0.10),而按字典序比较会将 1.0.0.10 判定为低于 1.0.0.9,导致当天第 10 次部署时版本偏差关卡判断错误。请改用标准辅助函数:

    node -e "console.log(require('${PLUGIN_ROOT}/scripts/lib/bump-solution-version').compareVersions('{ZIP_SOLUTION_VERSION}', '{INSTALLED.version}'))"
    

    该辅助程序在 ZIP < INSTALLED 时返回 -1,相等时返回 0,ZIP > INSTALLED 时返回 1。整数比较规则与 bumpPatchSegment 相同(用零补齐、最多 4 个段、拒绝非整数)。捕获标准输出,去除首尾空白,并将整数存入 VERSION_CMP。如果辅助程序抛出异常(任一侧的版本格式错误),请向用户显示标准错误并停止——版本比较是安全导入的前置条件。

    INSTALLEDVERSION_CMP行为
    null(未调用辅助程序——见上文前置条件)首次安装。保持静默,继续到下方的暂存/直接导入提示。
    非 null1(zip 版本严格更高)正常升级。报告:"目标环境已安装 v{INSTALLED.version};此 zip 的版本为 v{ZIP_SOLUTION_VERSION}。导入将进行升级。"
    非 null0(zip 版本与已安装版本相同)显示下方警告(版本不一致——适用于托管和非托管解决方案)。
    非 null-1(zip 版本严格更低)显示下方警告(降级——适用于托管和非托管解决方案)。

    🚦 关卡(同意 · import-solution:3.0.version-skew): 目标环境中已安装解决方案的版本等于或高于 zip 版本。导入会产生不可预测的升级语义;源端 export-solution 应在每次导出时递增版本。请使用递增后的版本重新导出、强制继续导入,或取消。

    警告提示——AskUserQuestion:

    “目标环境中已安装 {ZIP_SOLUTION_NAME} v{INSTALLED.version}({INSTALLED.ismanaged ? '托管' : '非托管'})。你即将导入的 zip 携带版本 {ZIP_SOLUTION_VERSION}。

    导入相同或更低版本并不可靠:

    • 托管:没有升级沿袭;平台可能根据内部启发式规则应用或拒绝导入。
    • 非托管:行为完全取决于 OverwriteUnmanagedCustomizations: true;目标环境中任何恰好与 zip 组件 ID 匹配的编辑内容,都会在没有版本变更记录的情况下被静默覆盖。

    /power-pages:export-solution 在生成 zip 前始终会递增源版本(自 2026-05-25 起)。如果此 zip 是在该变更前生成的,重新导出即可获得一个严格更高的干净版本。

    你希望如何继续?”

    问题标题选项
    要执行什么操作?版本不一致使用递增后的版本重新导出(推荐——调用 /power-pages:export-solution)、仍然导入(风险自负)、取消
    • 重新导出:调用 /power-pages:export-solution。完成后,使用刚生成的 zip 从阶段 2 重新启动此技能。
    • 仍然导入:设置 SKEW_ACK = true,并继续到下方的暂存/直接导入提示。在 docs/alm/last-import.json 中的 versionSkew: { zipVersion, installedVersion, isManaged, acknowledged: true } 下记录已确认的版本不一致,以供审计。
    • 取消:干净地停止此技能。步骤 3.0 中未对目标环境执行任何写入操作——此步骤为只读。
  4. 如果比较结果属于正常升级情形(或尚未安装该 zip 中的解决方案),请保持静默继续——不要显示提示,只需在最终的 docs/alm/last-import.json 中记录版本差异,以供摘要使用。

🚦 关卡(计划 · import-solution:3.config): 选择暂存或直接导入,以及覆盖选项。取消将在修改目标环境前退出。

通过 AskUserQuestion 询问用户:

关键决策点:暂存还是直接导入

  • 暂存(托管解决方案推荐):先运行 StageSolution 检查缺失的依赖项。在提交导入前显示问题。更安全——暂存步骤完全可逆。
  • 直接:跳过暂存并立即导入。速度更快,但如果缺少依赖项,导入可能会在中途失败。 另外询问:
  • 覆盖非托管自定义项?(默认:是)——当目标环境自定义了相同组件时需要此操作
  • 导入后发布工作流?(默认:是)
阶段 4 — 暂存解决方案(有条件)

仅当用户在阶段 3 中选择暂存导入时,才运行此阶段。

请参阅 ${PLUGIN_ROOT}/references/solution-api-patterns.md 第 5a 节。

  1. 对 zip 文件进行 Base64 编码:
    node "${PLUGIN_ROOT}/scripts/encode-solution-file.js" --zipPath "{zipPath}"
    
  2. 使用 CustomizationFile: {base64} 向 {envUrl}/api/data/v9.2/StageSolution 发送 POST 请求
  3. 解析 StageSolutionResults:
    • 提取 StageSolutionUploadId(在阶段 5 中使用,无需再次对文件进行编码)
    • 检查 MissingDependencies 数组
  4. 如果 MissingDependencies 不为空:
    • 列出每个缺失依赖项的类型和名称
    • 询问用户:“目标环境中缺少这些依赖项。是否仍要继续(可能会失败),还是取消以先安装依赖项?”
    • 如果取消:停止操作,并建议安装缺失的依赖项
  5. 如果 MissingDependencies 为空:报告“未发现缺失的依赖项。可以开始导入。”
阶段 5 — 导入解决方案

请参阅 ${PLUGIN_ROOT}/references/solution-api-patterns.md 第 5b 节。

  1. 准备请求正文(始终使用 CustomizationFile——ImportSolutionAsync 不接受 StageSolutionUploadId):

    • 对 zip 进行编码:node "${PLUGIN_ROOT}/scripts/encode-solution-file.js" --zipPath "{zipPath}"
    • 使用 { CustomizationFile: "{base64}", OverwriteUnmanagedCustomizations: {choice}, PublishWorkflows: {choice} }
  2. 向 {envUrl}/api/data/v9.2/ImportSolutionAsync 发送 POST 请求

  3. 提取 AsyncOperationId 和 ImportJobKey(注意:字段为 ImportJobKey,而不是 ImportJobId)

  4. 报告:“导入作业已启动:{AsyncOperationId}。正在轮询以检查是否完成……”

运行 scripts/poll-async-operation.js:

node "${PLUGIN_ROOT}/scripts/poll-async-operation.js" \
  --asyncJobId "{AsyncOperationId}" \
  --envUrl "{envUrl}" \
  --intervalMs 8000 \
  --maxAttempts 75

处理轮询结果:

  • Succeeded:继续执行阶段 6
  • Failed,且错误为 AttachmentBlocked / 错误代码为 -2147188706:继续执行下面的阶段 5b
  • Failed(其他情况):显示错误消息,查询导入作业以获取组件级错误,然后停止
  • Timeout:告知用户,并建议检查管理中心
阶段 5b — 解决附件限制(有条件)

仅当阶段 5 的轮询因 AttachmentBlocked(-2147188706,或消息包含 AttachmentBlocked 或 not a valid type)而失败时,才运行此阶段。

5b.1 识别解决方案 zip 中被阻止的扩展名

列出 zip 中的所有文件并提取不重复的扩展名:

unzip -l "{zipPath}" | awk '{print $4}' | grep '\.' | sed 's/.*\.//' | sort -u

从环境中获取当前被阻止的附件列表:

pac env list-settings

在输出中找到 blockedattachments 行——其中包含以分号分隔的列表(例如 ade;adp;js;zip;...)。

计算交集:找出解决方案 zip 中也出现在阻止列表里的扩展名。这些类型需要解除阻止。

5b.2 解释问题

告知用户:

“解决方案导入失败,因为目标环境阻止了此解决方案中包含的某些文件类型。解决方案中以下文件扩展名当前受到阻止:{comma-separated list}。这是环境级别的安全设置。要导入此解决方案,需要暂时放宽这些限制。”

5b.3 请求许可

🚦 Gate (consent · import-solution:5b.blocked-attachments): 对 AttachmentBlocked 进行响应式补救——修改环境级 blockedattachments 设置(影响整个租户)。可通过 PPAC 恢复。每次技能调用因该错误而失败时,都会重新触发。 当 plan-alm Manual 路径编排多目标导入(先 Staging,再 Production)时,会对每个目标调用一次 import-solution——如果两个目标都阻止了相同的扩展名,则每个目标都会触发一次 gate(每次都是针对不同环境的独立技能调用)。对 Staging 的许可不适用于 Production。

立即调用 AskUserQuestion——不要将此问题作为聊天消息呈现。技能继续执行前,用户必须实时作答。

问题标题选项
此解决方案包含被此环境附件安全设置阻止的文件类型({list})。是否要为这些特定类型解除阻止,以便导入解决方案?解除附件类型阻止是,为本次导入解除 {list} 的阻止(推荐),否,不更改环境设置

如果选择“否”:停止并告知用户:“这些文件类型仍处于阻止状态,无法继续导入。要手动解除阻止,请前往:Power Platform Admin Center → Environments → {env} → Settings → Product → Features → Blocked Attachments。”

如果选择“是”:继续执行 5b.4。

5b.4 更新被阻止的附件
  1. 解析 blockedattachments 值(以分号分隔)
  2. 仅移除在 5b.1 中识别出的扩展名——保留其他所有扩展名
  3. 更新设置:
    pac env update-settings --name blockedattachments --value "{updated-list-with-types-removed}"
    
  4. 确认更新成功。
5b.5 重试导入

重新编码 zip 并重试 ImportSolutionAsync(重复执行阶段 5 的步骤 1–4,然后再次轮询)。

  • 如果为 Succeeded:继续执行阶段 6
  • 如果再次失败且错误不同:显示新的错误消息并停止——不要继续重试
阶段 6 — 验证导入
  1. 查询解决方案,确认其存在且版本匹配:

    GET {envUrl}/api/data/v9.2/solutions?$filter=uniquename eq '{solutionName}'&$select=solutionid,uniquename,version,ismanaged
    
  2. 查询导入作业以获取组件结果(使用导入响应中的 ImportJobKey):

    GET {envUrl}/api/data/v9.2/importjobs({ImportJobKey})?$select=solutionname,completedon,progress,data
    
    • 解析 data XML 字段以获取各组件的结果(查找 result="failure" 条目)
    • 统计:成功导入数 / 警告数 / 失败数
  3. 确保 docs/alm/ 存在,然后写入 docs/alm/last-import.json 标记(node -e "require('fs').mkdirSync('docs/alm',{recursive:true})"):

    {
      "importedAt": "<ISO timestamp>",
      "solutionName": "<name>",
      "version": "<version>",
      "targetEnvironment": "<envUrl>",
      "asyncOperationId": "<id>",
      "importJobId": "<ImportJobKey value>",
      "status": "<Succeeded|Failed|Partial>",
      "componentResults": { "success": N, "warning": N, "failure": N },
      "versionSkew": null
    }
    

    status 字段用于驱动 refresh-alm-plan-data.js 的步骤同步(completed 与 failed),以更新渲染后的 ALM 计划中各阶段的检查清单。设置如下:

    • 当导入作业的 statecode 为 3(Succeeded)且组件结果显示 failure === 0 时,设为 Succeeded。
    • 当 statecode 为 3 但 componentResults.failure > 0 时,设为 Partial(解决方案已导入,但部分组件未能导入——通常是托管属性冲突或依赖项缺失)。
    • 当 statecode 为 4(Failed)、导入未达到终止状态或所有组件都导入失败时,设为 Failed。如果没有此字段,渲染后的计划会将每次导入都显示为 completed,无论实际结果如何。

    如果用户在步骤 3.0 中确认了同版本或降级导入(SKEW_ACK = true),将 versionSkew 设为:

    { "zipVersion": "<ZIP_SOLUTION_VERSION>", "installedVersion": "<INSTALLED.version>", "isManaged": <bool>, "acknowledged": true }
    

    否则将 versionSkew 保持为 null。

阶段 6b — 设置环境变量值(如有)

成功导入后,环境变量的定义会随解决方案一起迁移,但其值不会。目标环境中会有这些定义记录,但在明确设置之前,值为空。

查询目标环境,查看此解决方案中的环境变量定义:

GET {envUrl}/api/data/v9.2/environmentvariabledefinitions?$filter=introducedversion ne null&$select=schemaname,displayname,type,defaultvalue,environmentvariabledefinitionid

仅筛选 schemaname 以发布者前缀(来自 .solution-manifest.json)开头的定义;如果有 solutioncomponents,也可以与之交叉核对。

对于找到的每个定义,检查目标环境中是否已有值:

GET {envUrl}/api/data/v9.2/environmentvariablevalues?$filter=_environmentvariabledefinitionid_value eq '{id}'&$select=value

🚦 关卡(计划 · import-solution:6b.env-vars): 导入的环境变量定义需要为每个目标环境设置值。用户可以提供值,也可以跳过(使用默认值)。如果没有设置值,运行时会读取默认值,而该默认值可能仅用于开发环境。

如果有任何定义尚无现有值,请通过 AskUserQuestion 向用户询问:

“导入的解决方案包含 {N} 个尚未设置值的环境变量。请为每个变量输入目标值(留空则跳过并使用默认值):

  1. {schemaname}({displayname})— 默认值:{defaultvalue ?? 'none'}
  2. ...”

对于用户提供的每个值,POST 一条 environmentvariablevalue 记录:

POST {envUrl}/api/data/v9.2/environmentvariablevalues
{
  "schemaname": "{schemaname}",
  "value": "{userValue}",
  "[email protected]": "/environmentvariabledefinitions({id})"
}

关于 Secret 类型(type 100000005)的说明: Secret 值会加密存储。POST 操作方式相同,但该值在 UI 中会被遮蔽。用户应提供目标环境的实际 Secret 值(例如,生产租户应用注册的 OAuth 客户端 Secret——与开发环境中的值不同)。

如果用户跳过所有值:告知他们,在设置值之前,站点可能无法正常运行,并提供用于手动设置的 Power Platform 直接 URL: https://{targetEnvHost}/main.aspx?appid=...&etn=environmentvariabledefinition

6b.verify — 确认值已写入。 完成各变量的 POST 后,验证目标环境中是否确实存在每条 environmentvariablevalues 记录。共享辅助脚本 scripts/lib/verify-env-var-values.js 会执行此只读检查,并按架构返回结构化 JSON 结果(landed / missing-value-record / missing-definition / value-mismatch / query-error):

node "${PLUGIN_ROOT}/scripts/lib/verify-env-var-values.js" \
  --envUrl "{targetEnvUrl}" \
  --schemaNames "{comma-separated schema names that the user supplied values for}"

将 stdout 捕获为 JSON。如果 summary.missing > 0 或 summary.error > 0,向用户发出一条警告,并列出受影响的架构名称;但不要阻止导入摘要,导入本身已成功。同一辅助脚本也用于部署时(deploy-pipeline 阶段 7.6.5)和配置时(configure-env-variables 阶段 7);集中执行此检查可确保不同技能向用户显示的消息一致。

阶段 6c — 检测云端流(如有)

导入成功后,检查解决方案是否包含云端流 JSON 文件。使用阶段 2 中找到的 zip 路径。

unzip -l "{zipPath}" | grep -i "^.*Workflows/.*\.json"

如果没有找到匹配的文件,则完全跳过此阶段。

如果找到云端流文件:

  1. 从每个路径中提取流名称(格式:Workflows/FlowName-GUID.json — 移除路径前缀和 GUID 后缀,得到显示名称)

  2. 告知用户:

    “此解决方案包含 {N} 个云端流。导入后,必须在目标环境中将云端流注册到 Power Pages 站点。”

    检测到的流:

    • {FlowName1}
    • {FlowName2} ...

    注册方法:Power Pages Management → 目标环境 → 编辑站点 → Set up → Cloud flows → 注册上面列出的每个流。

    直接链接:https://make.powerpages.microsoft.com/

    🚦 关卡(计划 · import-solution:6c.cloud-flow-register): 导入的解决方案中的云端流需要在目标环境中手动注册。确认 / 暂缓。

  3. 调用 AskUserQuestion:

    问题标题选项
    你是否已将上面列出的云端流注册到 {targetEnvUrl} 中的 Power Pages 站点?云端流注册流已注册 — 继续,我稍后注册
  4. 将用户的回答记录为 cloudFlowStatus:"Registered" 或 "Pending registration"。此状态将显示在阶段 7 的摘要行中 — 无论选择哪个回答,技能都可以继续。

阶段 6d — 检查站点激活状态(如果是 Power Pages 解决方案)

仅当解决方案包含 Power Pages 网站组件(componentType 10374)时才运行此阶段:

GET {envUrl}/api/data/v9.2/solutioncomponents?$filter=_solutionid_value eq '{solutionId}' and componenttype eq 10374&$select=objectid

如果未找到 componentType 10374 记录,则完全跳过此阶段。

如果找到记录,则运行共享激活状态检查(PAC CLI 已通过目标环境的身份验证):

node "${PLUGIN_ROOT}/scripts/check-activation-status.js" --projectRoot "."

评估结果:

  • activated: true:保存 siteUrl,供阶段 7 摘要使用。无需进一步操作。

🚦 关卡(计划 · import-solution:6d.activate): 站点已导入,但尚未在目标环境中激活。请提供立即调用 activate-site 或暂缓的选项。

  • activated: false:通过 AskUserQuestion 询问用户:

    问题标题选项
    Power Pages 站点已导入,但尚未在 {envUrl} 中激活(预配)。立即激活以使其可公开访问。激活站点是,现在激活(推荐),否,我稍后激活
    • 如果选择“是”:调用 /power-pages:activate-site。activate-site 技能将处理子域名选择、确认和预配。
    • 如果选择“否”:在阶段 7 的摘要中注明激活待处理,并提醒用户准备就绪后运行 /power-pages:activate-site。
  • 存在 error:静默跳过 — 不要阻止生成摘要。在阶段 7 中注明无法确定激活状态。

阶段 7 — 显示摘要

显示摘要表:

项目值
解决方案{solutionName} v{version}
目标环境{envUrl}
托管是 / 否
已导入的组件N 成功,N 警告,N 失败
已设置的环境变量值N / N
云端流{cloudFlowStatus}(已注册 / 待注册 / 不适用)
站点激活已在 {siteUrl} 激活 / 待处理 / 不适用
导入作业{importJobId}
记录技能使用情况

参考:${PLUGIN_ROOT}/references/skill-tracking-reference.md

按照参考中的技能跟踪说明记录此技能的使用情况。使用 --skillName "ImportSolution"。

刷新 ALM 计划(如果存在)
node "${PLUGIN_ROOT}/scripts/lib/refresh-alm-plan-data.js" \
  --projectRoot "." \
  --phase import-solution \
  --stageName "{targetLabel}" \
  --render

{targetLabel} 是本次已完成导入所针对的手动路径目标阶段(例如 Staging、Production),由用户说明,或可根据目标环境 URL 推断。该辅助工具会读取 docs/alm/last-import.json,将导入结果(状态、版本、组件数量、组件失败情况)写入 planData.manualImports[targetLabel],并重新渲染 docs/alm-plan.html,使匹配的 Import to {targetLabel} 检查清单步骤显示 IMPORTED / FAILED 徽标,并在行内显示版本和组件数量。后续导入到其他目标时,每个目标都会有自己的条目——渲染器会显示各目标的历史记录,而不是在每次调用时覆盖记录。

如果省略 --stageName,该辅助工具会尝试将 docs/alm/last-import.json 中的 targetEnvironment URL 与 planData.stages[].envUrl 匹配。如果匹配失败(较少见——通常是 planData 中的阶段标签与环境 URL 不匹配),导入结果会记录在一个合成键下,以免悄然丢失;请明确传入 --stageName,以保持渲染后的计划整洁。如果不存在 docs/.alm-plan-data.json,该辅助工具会返回 ok:false,并软性地不执行任何操作。

告知用户下一步(由用户驱动顺序)。 该辅助工具的标准输出 JSON 中包含 nextStep: { name, skill: string | null } | null。如果值非空,请根据 skill 分支处理:当 skill 非空时,告知用户 “计划已更新。计划中的下一步:{nextStep.name} → 准备好后运行 {nextStep.skill}。”;当 skill 为 null(例如 Finalize 这样的内部步骤,没有用户命令)时,只说出步骤名称——“计划已更新。计划中的下一步:{nextStep.name}。”——绝不要输出 run null。(通常是导入到下一个目标,或在当前目标激活站点。)如果值为 null 或辅助工具返回 ok:false,不要提及下一步。绝不要自动调用下一个技能——由用户决定执行。

关键决策点(等待用户)

  1. 阶段 1:确认目标环境——托管解决方案的导入不容易撤销
  2. 阶段 2:如果找到多个 zip 文件,则选择文件
  3. 阶段 3 步骤 3.0:版本差异提示——仅当目标环境已安装该解决方案,且 zip 的版本 ≤ 已安装版本时触发。提供重新导出并提升版本(推荐)、仍然导入(会在 last-import.json 中记录用户已确认)或取消等选项。此项为只读检查;在此处取消不会对目标环境进行任何写入。
  4. 阶段 3:选择暂存导入或直接导入;覆盖自定义项
  5. 阶段 4:选择是否在缺少依赖项的情况下继续
  6. 阶段 5b:同意解除附件类型限制——未经明确批准,绝不修改环境设置
  7. 阶段 6b:环境变量值——如果解决方案包含环境变量定义,而目标环境中没有现有值,则始终提示用户;Secret 类型定义需要用户提供目标环境专用的密钥值
  8. 阶段 6c:云流注册——不会阻塞流程;用户之后可以注册;状态会记录在摘要中
  9. 阶段 6d:站点激活——仅当存在 Power Pages 网站组件且站点尚未激活时执行

错误处理

  • 组件级导入失败:在摘要中报告,不阻止整体流程完成
  • 如果导入异步操作因 AttachmentBlocked (-2147188706) 失败:运行阶段 5b 补救流程(识别受阻类型、征求同意、解除限制、重试)
  • 如果导入异步操作因其他错误失败:显示异步操作记录中的 friendlyMessage,然后停止
  • 绝不尝试回滚——报告成功和失败的部分
  • 未经用户明确批准,绝不修改环境设置(blockedattachments)

进度跟踪表

任务主题activeForm描述
验证前提条件正在验证前提条件确认 PAC CLI 身份验证,获取令牌,并与用户确认目标环境
定位解决方案文件正在定位解决方案文件查找并验证解决方案 zip,确认其中包含 Solution.xml
配置导入正在配置导入步骤 3.0:从 solution.xml 提取 zip 的 uniqueName+version,查询目标环境中已安装的版本;如果 zip 版本 ≤ 已安装版本,则提示版本不匹配的风险(提供重新导出/仍然导入/取消选项);然后询问:暂存还是直接导入、是否覆盖自定义项、是否发布工作流
暂存解决方案(依赖项检查)正在暂存解决方案运行 StageSolution,在提交前检查是否存在缺失的依赖项
导入解决方案正在导入解决方案POST ImportSolutionAsync,并轮询直至完成;如果出现 AttachmentBlocked:识别受阻类型,征得用户同意,通过 pac env update-settings 解除阻止,然后重试
验证导入正在验证导入确认目标环境中的解决方案版本,解析组件结果,并写入 docs/alm/last-import.json
检测云端流正在检测云端流列出 zip 中的 Workflows/*.json 条目;如果存在,则提示用户将流注册到 Power Pages 站点;记录状态(已注册/待注册)
检查站点激活状态正在检查站点激活状态如果解决方案包含 componentType 10374:运行 check-activation-status.js;如果尚未激活,则询问用户并调用 /power-pages:activate-site
展示摘要正在展示摘要显示组件数量(成功/警告/失败)、云端流注册状态、站点激活状态和已设置的环境变量值

评论

支持 Markdown