export-solution について
Power Platform の開発者向けに、Dataverse ソリューションの ZIP をエクスポートして検証するワークフローです。ALM プランとサイトコンポーネントの不足も確認します。
主な機能
- 01ALM プランの鮮度チェック
- 02マニフェストによるソリューション選択
- 03サイトコンポーネントの完全性確認
- 04非同期エクスポートとポーリング
- 05ソリューション ZIP の取得と検証
活用シーン
- 01ステージングや本番環境向けのソリューションエクスポート
- 02ソリューションに不足するサイトコンポーネントの特定
- 03エクスポート前の古い ALM プランの更新
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ microsoft/ power-platform-skills --skill export-solutionインストール後、Claude Code で/export-solutionを入力して読み込まれたか確認します。
同梱ファイルscripts/validate-export.js
詳細
プラグインの確認:
node "${PLUGIN_ROOT}/scripts/check-version.js"を実行します。メッセージが出力された場合は、先にユーザーに表示してから続行してください。
export-solution
Dataverse ソリューションのエクスポートを非同期で開始し、完了するまでポーリングして、ソリューションの ZIP をダウンロードし、検証します。.solution-manifest.json を読み込んでソリューションを特定し、見つからない場合はユーザーに尋ねます。
前提条件
- PAC CLI がインストールされ、認証済みであること
- Azure CLI がインストールされ、ログイン済みであること
- 環境にソリューションが存在すること(必要に応じて、先に
setup-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 "." \
--envUrl "{envUrl from .solution-manifest.json or pac env who, if available}" \
--token "{token, if Phase 1 already acquired one}" \
--solutionId "{solutionId from .solution-manifest.json, if available}"
ヘルパーは { exists, deferred, stale, staleness: { reason, detail }, generatedAt, planStatus, ... } を含む JSON を返します。最新性チェックには環境の認証情報と 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 を実行しますか?"
🚦 ゲート(意図 · export-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 — 古いプラン。 ユーザーに次のように伝えてください。
{generatedAt}に ALM プランが作成されていますが、それ以降にソースソリューションが変更されています({solution.modifiedon}時点)。コンポーネントが変更されている可能性があります。plan-almを再実行すると、分析とレンダリング済み HTML が更新されます。
🚦 ゲート(意図 · export-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 に進みます。 - キャンセル → 正常に終了します。
このゲートがある理由。 export-solution を直接呼び出すと、オーケストレーターによるエクスポート前の完全性チェックなしで ZIP が生成されます。このスキルを単独で実行するユーザーは、ソリューションに追加すべきコンポーネント(クラウドフロー、サイト設定から参照される環境変数値、サンプルデータ参照)を見落としやすく、ステージング環境へのインポートは正常に完了しても、デプロイ後にサイトが壊れる ZIP を出荷してしまうことがあります。エクスポート前の完全性チェックにより、ZIP の作成前にこうした不足が明らかになります。このゲートにより、plan-alm が実行済みである(完全性が検証され、エクスポート対象が適切なソリューション系統に絞られている)か、ユーザーが明示的にこれを省略することを選んだかのいずれかが確実になります。
Phase 1 — 前提条件の確認
この Phase の開始時に、すべてのタスクを作成します。
作成するタスク:
- 「前提条件を確認」
- 「ソリューションを特定」
- 「エクスポートを設定」
- 「非同期エクスポートを開始」
- 「ソリューション ZIP をダウンロード」
- 「エクスポートを検証」
- 「概要を提示」
手順:
verify-alm-prerequisites.jsを--require-manifest付きで実行し、PAC CLI の認証、トークンの取得、API アクセスの確認、および.solution-manifest.jsonの存在を検証します:
出力を JSON として取得し、node "${PLUGIN_ROOT}/scripts/lib/verify-alm-prerequisites.js" --require-manifest.envUrl(envUrlとして保存)と.token(tokenとして保存)を抽出します。スクリプトがゼロ以外の終了コードを返した場合は停止し、不足しているものを説明します(${PLUGIN_ROOT}/references/dataverse-prerequisites.mdを参照)。
Phase 1.5 — 現在の ALM ドキュメントに基づいて判断する
参照:
${PLUGIN_ROOT}/references/alm-docs-grounding.md
この手順は約 30 秒以内に完了させます。MCP の検索/取得でエラーが発生した場合は、1 行のメモを記録して続行します。このスキルはオフラインでも実行できる必要があります。
microsoft_docs_searchを次のクエリで実行します:Power Pages solution export managed unmanaged ExportSolutionAsync ALM。https://learn.microsoft.com/en-us/power-platform/alm/solution-concepts-alm(およびマネージドとアンマネージド、またはソリューションのレイヤー化に関する関連ページを最大 1 件)をmicrosoft_docs_fetchで並行して取得します。- エクスポートのセマンティクス、マネージド/アンマネージドの影響、非同期エクスポートのポーリングについて、Microsoft Learn の現在の説明を 1 段落にまとめます。
${PLUGIN_ROOT}/references/solution-api-patterns.mdと照合し、ExportSolutionAsync/DownloadSolutionExportDataのシグネチャに相違があれば明示します。 - この要約を Phase 2 以降の判断に活用します。スキルの動作を黙って変更しないでください。相違がある場合は、Phase 3 の前にソフト警告としてユーザーに伝えます。
フェーズ 2 — ソリューションの特定
🚦 ゲート(計画 · export-solution:2.identify): プロジェクトルートに
.solution-manifest.jsonがありません。エクスポートを進める前に、ユーザーがソリューションの一意の名前を選択するか、貼り付ける必要があります。これは、下記の手順 3 で「見つからない」分岐の場合にのみ発生します。トリガー: フェーズ 2 の手順 1 でマニフェストが見つかりませんでした。 確認する理由: 間違ったソリューションを自動選択すると、誤ったテーブル/サイト/フローのセットをステージングに送る、管理対象の zip がエクスポートされます。 キャンセル時: 何もありません。ExportSolutionAsync の呼び出しはまだ行われていません。
- プロジェクトルートで
.solution-manifest.jsonを検索します(findProjectRootまたはglob('**/.solution-manifest.json')を使用) - 見つかった場合:
solution.uniqueName、solution.solutionId、environmentUrlを読み取ります- 環境 URL が一致することを確認します(一致しない場合は警告します。環境をまたいだエクスポートの可能性があります)
- 見つからない場合は、
AskUserQuestionを使用してソリューションを選択します:- Dataverse に問い合わせて、利用可能なアンマネージドソリューションを取得し、選択肢として提示します
- 一意の名前を直接貼り付けるための自由入力の選択肢(「その他」)を用意します
- 環境にソリューションが存在することを確認します:
GET {envUrl}/api/data/v9.2/solutions?$filter=uniquename eq '{solutionName}'&$select=solutionid,uniquename,friendlyname,version,ismanaged - ソリューションの詳細を提示し、ユーザーに確認します。
フェーズ 2.5 — エクスポート前の完全性チェック
エクスポートの前に、共有サイトインベントリーヘルパーを実行し、サイトに存在するもののソリューションには含まれていないコンポーネントを検出します。ここで確認することで、不完全なパッケージをステージング/本番環境に送ることを防げます。
node "${PLUGIN_ROOT}/scripts/lib/discover-site-components.js" \
--envUrl "{envUrl}" --token "{token}" \
--siteId "{websiteRecordId}" \
--publisherPrefix "{publisherPrefix from .solution-manifest.json}" \
--solutionId "{solutionId}" \
--projectRoot "."
標準出力を解析し、missing を評価します。ほかの作業に着手する前に、同期後の再確認ゲートで変更点を表示できるよう、同期前の状態を取得します:
PRE_SYNC_VERSION = solutionManifest.solution.version // from .solution-manifest.json read in Phase 2
PRE_SYNC_MISSING = { siteComponents, siteLanguages, cloudFlows, envVarDefinitions, customTables, ... } // from the discovery stdout above
次に:
-
すべての
missing.*配列が空の場合 → 「ソリューションの内容はサイトと一致しています。差分は検出されませんでした。」と報告します。フェーズ 3 に進みます。 -
空でない
missing.*配列がある場合 → 簡潔な概要を提示します:「サイトには存在しますが、ソリューションに含まれていないコンポーネントが {N} 個あります:
- {X} 個のサイトコンポーネント(例:{最初の 3 件の名前}、…)
- {Y} 個のクラウドフロー
- パブリッシャープレフィックスを持つ {Z} 個の環境変数定義
- {W} 個のカスタムテーブル」
🚦 ゲート(進行 · export-solution:2.5.completeness): ソースソリューションは稼働中のサイトに対して不完全です。先に同期するか、不足を記録したうえで現状のままエクスポートするか、中止してください。
次に、
AskUserQuestionを使って質問します:「どのように進めますか?
- 今すぐ同期モードで
/power-pages:setup-solutionを実行する — 不足しているコンポーネントを取り込み、ソリューションのバージョンを上げてから、エクスポート前に再度確認します(推奨) - 現状のままエクスポートする — 現在ソリューションに含まれているものを送ります。不足しているコンポーネントは含まれません
- 中止する — エクスポートする前に調査します」
-
選択肢 1 — 先に同期し、エクスポート前に再確認する:
/power-pages:setup-solutionを呼び出します(既存のマニフェストを自動検出し、同期モードに入り、不足しているコンポーネントを取り込み、バージョンを上げます)。完了を待ちます。setup-solution の最終更新手順ではdocs/.alm-plan-data.jsonにLAST_SYNC_ATが書き込まれるため、その後のcheck-alm-plan.jsの呼び出しで、同期によってsolutions.modifiedonがGENERATED_ATを超えたという理由だけで計画が古いと誤判定されることはありません。鮮度の基準はmax(GENERATED_AT, LAST_SYNC_AT)になります。.solution-manifest.jsonを読み直し、POST_SYNC_VERSION = solutionManifest.solution.versionを取得します。- 検出ヘルパーを再実行します。
missing.*が空でない場合は、上記のフェーズ 2.5 の質問を繰り返します。 - それ以外の場合は、
PRE_SYNC_MISSINGと 2 回目の検出実行のmissing.*との差分をカテゴリごとに計算して、NEWLY_ADOPTEDとします(消えた項目は、setup-solution によってソリューションに取り込まれた項目です)。合計数は、すべてのカテゴリの要素数の合計です。
🚦 ゲート(進行 · export-solution:2.5.post-sync): 同期後の再確認です。ソリューションのバージョンが上がり、コンポーネントが取り込まれました。エクスポートを進める前に、ユーザーが差分を確認します。
-
フェーズ 3 に進む前に、ユーザーに再確認します — これからエクスポートするソリューションは、ユーザーがエクスポートを開始したときに確認したものとは異なります。
AskUserQuestionを使用します:"同期が完了しました。
{solutionUniqueName} は現在 v{POST_SYNC_VERSION} です(以前は v{PRE_SYNC_VERSION})。{NEWLY_ADOPTED.total} 個の新たに採用されたコンポーネントが含まれます:
- {カテゴリ別の最初の 3〜5 件の名前 — シグナルの強いカテゴリを優先:クラウドフロー、サーバーロジック、環境変数定義、続いてサイトコンポーネント}
- {さらに残りがある場合:
+ {N} more across {category list}}
この更新されたソリューションを zip ファイルにエクスポートしようとしています。
エクスポートを続行しますか?"
質問 ヘッダー オプション エクスポートを続行しますか? 同期後の承認 はい — v{POST_SYNC_VERSION} をエクスポートします(推奨)、一時停止 — まず新しいソリューションの内容を確認したい、キャンセル — エクスポートを中止します - はい → 同期後のソリューションでフェーズ 3 に進みます。
- 一時停止 → 短いメモ(「同期後に一時停止しました。v{POST_SYNC_VERSION} をエクスポートする準備ができたら
/power-pages:export-solutionを再実行してください。」)を添えて export-solution を正常に終了します。これにより、ユーザーは同期されたマニフェスト / Dataverse の状態を確認し、手動で再開できます。エクスポート成果物は一切書き込まないでください — エクスポートは行われていません。スキルトラッキングの呼び出しもスキップします。 - キャンセル → スキルを停止します。同じく成果物なし / トラッキングなしのルールが適用されます。
-
オプション 2 — エクスポートマニフェストにギャップを記録し(フェーズ 7 のサマリーを参照)、ユーザーが意図的に除外されたものの監査証跡を確認できるようにします。
-
オプション 3 — スキルを停止します。
同期後のゲートが存在する理由: エクスポートの途中で同期モードが実行されると、ユーザーがスキルを呼び出したときに想定していたものとは異なるソリューションバージョンが生成されます。同期後に再確認することで、zip が生成され、(通常は)
import-solutionを介してそのまま配布される前に、ユーザーがバージョンの更新と新たに採用されたコンポーネントのリストを確認する明示的な機会を得られます。フェーズ 2.5 のトリガーは意図的なものであり、同期後の再確認はその上に重ねられた安全策です。これはdeploy-pipelineのフェーズ 3.5 にある同じゲートと同じ形、同じオプション、同じ監査証跡ルールを踏襲しているため、ユーザーは PP Pipelines の経路を取っても手動のエクスポート/インポートの経路を取っても一貫した動作を目にします。
そもそもフェーズ 2.5 が存在する理由: これまでは、
setup-solutionの後に作成されたコンポーネント(add-server-logicからのサーバーロジック、add-cloud-flowからのフロー、configure-env-variables/setup-authからの環境変数)がエクスポート zip から黙って除外され、ターゲット環境に反映されませんでした。AGENTS.mdにあるデフォルトで ALM を意識する原則は、ソリューションがソース環境を離れるすべてのゲートでこのチェックを要求しています。
フェーズ 3 — エクスポートを設定する
🚦 ゲート(同意 · export-solution:3.export-type): マネージドとアンマネージドの選択は、生成される zip に対して取り消せません。マネージドはターゲット環境で編集できませんが、アンマネージドは編集できます。ステージ戦略と一致しない選択をすると、後続に誤った成果物が渡されます。
すぐに AskUserQuestion を呼び出してください。この選択についてチャットで説明してはいけません。エクスポートに進む前に、ユーザーからその場で回答を得る必要があります。
| 質問 | ヘッダー | 選択肢 |
|---|---|---|
| このソリューションをどのようにエクスポートしますか? マネージドソリューションはターゲット環境で編集できず、クリーンなアップグレード/削除サイクルに対応しています。ステージング環境や本番環境におすすめです。アンマネージドソリューションはターゲット環境で編集できます。開発環境間のデプロイに使用してください。 | エクスポートの種類 | マネージド — ステージング/本番環境向け(おすすめ)、アンマネージド — 開発環境向け |
回答に基づいて、ExportSolutionAsync リクエスト本文の "Managed": true または "Managed": false を設定してください。
🚦 ゲート(計画 · export-solution:3.overwrite): 生成される zip の出力ディレクトリと、上書きするか新しい名前を付けるかの選択です。ターゲットパスに既存の zip が見つかった場合、プロンプトで「上書き/新しい名前を選択/キャンセル」を提示します。
タイミング: マネージド/アンマネージドを選択した後のフェーズ 3。 確認する理由: 自動的に上書きすると、すでに手動でテスト済みのエクスポートが置き換えられる可能性があります。 キャンセルした場合: 何も起こりません。zip は書き込まれません。
AskUserQuestion を使用して、以下についても確認してください。
- 出力ディレクトリ(デフォルト:現在のプロジェクトルート)
- 解決された出力パスにすでに zip が存在する場合:「上書き/新しい名前を選択/キャンセル」
フェーズ 4 — 非同期エクスポートの開始
ステップ 4.0 — ソースソリューションのバージョンを更新します(常時実行)。
エクスポートする前に、ソースソリューションのバージョンのパッチセグメント(4 番目のセグメント)を更新します。これを行わないと、間に setup-solution 同期を挟まずに連続してエクスポートした場合、同じバージョン文字列の zip が生成されます。1 つ目をすでにインストール済みのターゲットに 2 つ目の zip をインポートする際、マネージドソリューションではクリーンなアップグレードパスがなく信頼性に欠け、アンマネージドソリューションでは OverwriteUnmanagedCustomizations: true に依存することになります。
「同期モードでコンポーネントが追加された場合のみ」ではなく、常時実行する理由:
setup-solutionは追加する新しいコンポーネントがある場合にのみバージョンを更新します。ソリューションにすでに含まれているコンポーネントの内容(Web テンプレート、サイト設定値、Web ファイルなど)をユーザーが変更して再エクスポートする場合も、バージョンラベルを必ず増加させる必要があります。そうしないと、手動でのエクスポート/インポートの手順で、古いバージョンの zip が気付かれないまま配布されてしまいます。詳細はsetup-solutionのフェーズ 4 にある「このステップが必要な理由」の注記を参照してください。
node "${PLUGIN_ROOT}/scripts/lib/bump-solution-version.js" \
--envUrl "{envUrl}" \
--token "{token}" \
--uniqueName "{solutionUniqueName}" \
--projectRoot "."
出力を JSON として取得し、.previous を PRE_EXPORT_VERSION、.next を EXPORT_VERSION として保存します。また、マニフェストの同期が成功したことを確認するため、.manifestUpdated / .manifestUpdateReason を確認してください。次のように報告します:「エクスポートのため、ソリューション {solutionUniqueName} のバージョンを v{PRE_EXPORT_VERSION} から v{EXPORT_VERSION} に更新しました。」
--projectRoot "." を指定すると、更新ヘルパーはバージョン更新処理の一部として、.solution-manifest.json の solution.version(単一ソリューション)または該当する solutions[].version(複数ソリューション)フィールドをアトミックに更新します。別途 Edit ステップを実行する必要はありません。マニフェストが存在しない場合、または該当するエントリがない場合は、manifestUpdated: false となり、manifestUpdateReason に理由(no-manifest、no-matching-entry など)が示されます。バージョン更新自体は成功しています。
このセッションの前半ですでにバージョン更新が行われている場合(たとえば、フェーズ 2.5 で
setup-solutionの同期モードが採用済みのコンポーネントとともに実行され、バージョンを更新してからここに戻ってきた場合):ヘルパーは引き続き実行され、再度バージョンを更新します。これは意図的な動作です。同期時の更新は新しいコンポーネントに対応し、エクスポート時の更新は生成された zip に対応します。これらは別の目的であり、バージョンを二度更新してもコストはわずかです(パッチセグメントが 1 つ増えるだけです)。「マニフェストのバージョンがすでに稼働中のソースバージョンと一致している」場合にスキルをスキップするロジックは、意図的にここには追加していません。そのようなロジックを追加すると、「内容を編集したが同期は不要で、バージョン更新も行われなかったため、古いバージョンのままエクスポートされた」という失敗が発生する可能性があります。
ステップ 4.1 — 非同期エクスポートを開始します。
scripts/lib/export-solution-async.js を実行して ExportSolutionAsync を POST し、終端状態になるまでポーリングして AsyncOperationId を取得します。
node "${PLUGIN_ROOT}/scripts/lib/export-solution-async.js" \
--envUrl "{envUrl}" \
--token "{token}" \
--solutionName "{solutionUniqueName}" \
--managed {true|false}
標準出力を JSON として取得し、.asyncOperationId を抽出して asyncOperationId として保存します。
「エクスポートジョブを開始しました。完了するまでポーリングしています...」と報告します。
スクリプトの終了コードを処理します。
- 終了コード 0:ジョブが成功しました。
asyncOperationIdを使ってフェーズ 5 に進みます。 - 終了コード 1:標準エラー出力に失敗メッセージが含まれています。内容を報告して停止します。
- タイムアウト/ポーリング上限到達:エクスポートがまだ実行中であることをユーザーに伝え、管理センターで確認するよう案内します。
フェーズ 5 — ソリューションの zip をダウンロード
scripts/lib/download-export-data.js を実行して DownloadSolutionExportData を POST し、base64 の zip をデコードしてディスクに書き込みます。
node "${PLUGIN_ROOT}/scripts/lib/download-export-data.js" \
--envUrl "{envUrl}" \
--token "{token}" \
--asyncOperationId "{asyncOperationId}" \
--outputPath "{outputDir}/{SolutionUniqueName}_{managed|unmanaged}.zip"
標準出力を JSON として取得し、.zipPath を抽出して zipPath として保存します。また、.fileSizeBytes も取得します。
「ソリューションの zip をダウンロードしています...」と報告します。
スクリプトの終了コードを処理します。
- 終了コード 0:zip が書き込まれました。
zipPathとfileSizeBytesを使ってフェーズ 6 に進みます。 - 終了コード 1:標準エラー出力に失敗メッセージが含まれています。内容を報告して停止します。
フェーズ 6 — エクスポートの検証
- zip ファイルがディスク上に存在することを確認します:
fs.existsSync(zipPath)を確認します。 - ファイルサイズが 1000 バイトを超えていることを確認します。
- zip 内に
Solution.xmlがあることを確認します。unzip -l "{zipPath}" | grep -i solution.xmlを実行するか、Node.js で zip の目次を読み取ります(unzipを使ってBashを使用してください)。- solution.xml が見つからない場合はエラーを報告します。zip が破損しているか、ダウンロードが途中で切れている可能性があります。
フェーズ 7 — 概要の提示
ステップ 7.1 — docs/alm/last-export.json マーカーを書き込みます。
docs/alm/ が存在することを確認してから、マーカーを書き込みます。これにより、後続のスキル(import-solution の差異に関する注意喚起、Manual-path タブをレンダリングする refresh-alm-plan-data.js、「前回のエクスポート以降に変更された項目」の今後のゲート、監査証跡)が、このソースから最後に出荷された内容を把握できます。
node -e "require('fs').mkdirSync('docs/alm',{recursive:true})"
次に、docs/alm/last-export.json を書き込みます。
{
"exportedAt": "<ISO timestamp>",
"solutionUniqueName": "<solutionUniqueName>",
"solutionId": "<solutionId from .solution-manifest.json or Phase 2 query>",
"previousVersion": "<PRE_EXPORT_VERSION from Step 4.0>",
"version": "<EXPORT_VERSION from Step 4.0>",
"managed": <true|false>,
"sourceEnvironmentUrl": "<envUrl>",
"zipPath": "<zipPath>",
"fileSizeBytes": <fileSizeBytes>,
"asyncOperationId": "<asyncOperationId>"
}
このパスは scripts/lib/alm-paths.js に lastExport というキーで登録されています。プログラムから利用する場合は、パス文字列を再度記述するのではなく、almPath(projectRoot, 'lastExport') で解決してください。(スキルの説明では、last-deploy.json、last-import.json、その他の ALM マーカーで使われている規則に合わせて、読みやすさを優先し、パスをそのまま記述しています。)
ステップ 7.2 — 概要を表示します。
| 項目 | 値 |
|---|---|
| ソリューション | {solutionUniqueName} v{EXPORT_VERSION}(v{PRE_EXPORT_VERSION}から変更) |
| エクスポートの種類 | マネージド/アンマネージド |
| ファイル | {zipPath} |
| ファイルサイズ | {size} KB |
| エクスポートジョブ | {AsyncJobId} |
| マーカーの書き込み先 | docs/alm/last-export.json |
次のステップの提案:
/power-pages:import-solutionを実行して、この zip を別の環境にデプロイします/power-pages:setup-pipelineを実行して、このプロセスを CI/CD で自動化します
スキルの使用状況を記録する
参照:
${PLUGIN_ROOT}/references/skill-tracking-reference.md
参照先のスキル使用状況の記録手順に従って、このスキルの使用状況を記録します。--skillName "ExportSolution" を使用してください。
ALM 計画を更新する(存在する場合)
node "${PLUGIN_ROOT}/scripts/lib/refresh-alm-plan-data.js" \
--projectRoot "." \
--phase export-solution \
--render
docs/alm-plan.html を再レンダリングし、このスキルの実行中にエージェントが更新したステップのステータス(Export solution → status-completed)を反映します。docs/.alm-plan-data.json が存在しない場合(単独での実行で、ALM 計画の一部ではない場合)、ヘルパーは ok:false を返して何もせずに終了します。無条件に安全に実行できます。
次のステップをユーザーに提示します(実行順序はユーザーが決定します)。 ヘルパーの stdout JSON には、nextStep: { name, skill: string | null } | null が含まれます。null 以外の場合は、skill に応じて分岐します。skill が null 以外の場合は、ユーザーに 「計画を更新しました。計画上の次のステップは {nextStep.name} です。準備ができたら {nextStep.skill} を実行してください。」 と伝えます。skill が null の場合(Finalize など、ユーザーがコマンドを実行しない内部ステップ)は、ステップ名だけを伝えます。「計画を更新しました。計画上の次のステップは {nextStep.name} です。」 とし、run null は決して表示しないでください。(通常は、エクスポートした zip を確認してから、最初の対象に対して /power-pages:import-solution を実行します。)null の場合、またはヘルパーが ok:false を返した場合は、次のステップについて何も伝えません。次のスキルを自動的に呼び出してはいけません。実行するかどうかはユーザーが決定します。
主な判断ポイント(ユーザーを待つ)
- フェーズ 2:ソリューションの特定 — エクスポートを開始する前に確認します
- フェーズ 2.5:不足分に関するプロンプト(同期を優先/現状のままエクスポート/中止)。稼働中のサイトに、ソリューションに含まれていないコンポーネントがある場合に表示します
- フェーズ 2.5:同期後の承認ゲート — エクスポート途中で同期した場合(オプション 1)に限り実行されます。新しいソリューションのバージョンと新たに取り込まれたコンポーネントを表示し、zip をエクスポートする前に、同期後のソリューションについてユーザーに確認します。一時停止の場合は正常に終了し、キャンセルの場合は中止します。
- フェーズ 3:マネージドかアンマネージドか — 後続のインポート可否に影響します(このエクスポートでは取り消せない選択です)
- フェーズ 4 ステップ 4.0:ユーザーへのプロンプトはありません。
ExportSolutionAsyncの前にバージョンの更新が自動で実行されます。更新後のバージョン(PRE_EXPORT_VERSION → EXPORT_VERSION)はフェーズ 7 の概要に表示されるため、zip に含まれるバージョンをユーザーが確認できます。
エラー処理
- エクスポートジョブが失敗した場合:非同期操作から
messageとfriendlyMessageを表示します - ダウンロードされた
ExportSolutionFileが空の場合:エラーを報告し、再エクスポートを提案します - 自動で再試行してはいけません。失敗を報告し、ユーザーに判断を委ねてください
進捗追跡テーブル
| タスクの内容 | 進行中の表示 | 説明 |
|---|---|---|
| 前提条件の確認 | 前提条件を確認しています | PAC CLI の認証、Azure CLI トークンの取得、API アクセスの確認を行います |
| ソリューションの特定 | ソリューションを特定しています | .solution-manifest.json を読み取るかユーザーに尋ね、環境にソリューションが存在することを確認します |
| エクスポートの設定 | エクスポートを設定しています | マネージドかアンマネージドか、出力ディレクトリをユーザーに尋ねます |
| 非同期エクスポートの開始 | 非同期エクスポートを開始しています | bump-solution-version.js を使用してソースソリューションのバージョンを更新し(ステップ 4.0)、zip に単調増加するバージョンラベルが付くようにします。POST ExportSolutionAsync を実行して AsyncJobId を取得し、完了するまでポーリングします |
| ソリューション zip のダウンロード | ソリューション zip をダウンロードしています | POST DownloadSolutionExportData を実行し、base64 をデコードして zip をディスクに書き込みます |
| エクスポートの検証 | エクスポートを検証しています | zip が存在すること、サイズが 0 より大きいこと、内部に Solution.xml があることを確認します |
| 概要の提示 | 概要を提示しています | docs/alm/last-export.json マーカーを書き込み(alm-paths.js 経由)、zip のパス、サイズ、種類、バージョン更新、おすすめの次のステップを表示します |