# バッチ設計書 79-extract-artifact-packages.ps1

## 概要

本ドキュメントは、.NET runtimeプロジェクトにおけるアーティファクトパッケージ展開スクリプト（extract-artifact-packages.ps1）の設計仕様を記載したものである。

### 本バッチの処理概要

このバッチは、NuGetパッケージ（.nupkg）からセキュリティスキャン対象のファイル（.dll、.exe、.pdb）を抽出するスクリプトである。ビルド成果物のNuGetパッケージを展開し、BinSkimなどのバイナリスキャンツールがスキャンできる形式にする。

**業務上の目的・背景**：.NETのビルド成果物はNuGetパッケージ（.nupkg）としてパッケージングされる。しかし、BinSkimなどのセキュリティスキャンツールは、NuGetパッケージ内のバイナリを直接スキャンすることができない。本スクリプトは、NuGetパッケージを展開して内部のDLL、EXE、PDBファイルを抽出し、セキュリティスキャンが実行できる状態にする。これにより、ビルド成果物に対する包括的なセキュリティ検証が可能となる。

**バッチの実行タイミング**：SDLスキャン実行前に呼び出される。ビルド完了後、アーティファクトに対するセキュリティスキャンの準備段階で実行される。

**主要な処理内容**：
1. 入力ディレクトリの存在確認
2. .nupkgファイルの検索
3. 各パッケージの並列展開（PowerShell Jobs）
4. 関連拡張子（.dll、.exe、.pdb）のファイルのみ抽出
5. パッケージIDごとのディレクトリに出力

**前後の処理との関連**：ビルド完了後、execute-all-sdl-tools.ps1によるSDLスキャン前に呼び出される。抽出されたファイルはBinSkimなどのツールでスキャンされる。

**影響範囲**：展開先ディレクトリ、SDLスキャン対象ファイル

## バッチ種別

データ変換 / 前処理

## 実行スケジュール

| 項目 | 内容 |
|-----|------|
| 実行頻度 | 随時（SDLスキャン実行時） |
| 実行時刻 | SDLパイプライン実行時 |
| 実行曜日 | 該当なし |
| 実行日 | 該当なし |
| トリガー | Azure DevOpsパイプライン |

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| 入力ディレクトリ | InputPathが存在すること |
| .nupkgファイル | InputPath配下にNuGetパッケージが存在すること |
| 書き込み権限 | ExtractPathへの書き込み権限があること |
| tools.ps1 | eng/common/tools.ps1が利用可能であること |

### 実行可否判定

- InputPathが存在しない場合は正常終了（スキップ）
- .nupkgファイルが存在する場合に処理実行

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| InputPath | string | Yes | なし | NuGetパッケージが格納されているディレクトリ |
| ExtractPath | string | Yes | なし | 展開先ディレクトリ |

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| .nupkgファイル | ZIP | NuGetパッケージ（ZIP形式） |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| ExtractPath | ディレクトリ | パッケージIDごとのサブディレクトリ |

### 出力ファイル仕様

| 項目 | 内容 |
|-----|------|
| ディレクトリ名 | {PackageId}（拡張子なし） |
| 出力先 | {ExtractPath}/{PackageId}/ |
| 抽出対象 | .dll、.exe、.pdb のみ |
| ディレクトリ構造 | パッケージ内の相対パスを保持 |

## 処理フロー

### 処理シーケンス

```
1. 初期化
   └─ ErrorActionPreference、StrictMode設定
   └─ $ci変数を$trueに設定、tools.ps1を読み込み
2. ExtractPackageスクリプトブロック定義
   └─ パッケージ展開ロジックを定義
3. ExtractArtifacts関数定義
   └─ 入力パス確認、並列ジョブ起動
4. 入力パス確認
   └─ InputPathが存在しない場合は正常終了
5. パッケージ検索
   └─ Get-ChildItemで*.nupkgを検索
6. 並列展開
   └─ Start-Jobで各パッケージを並列展開
7. ジョブ完了待機
   └─ Wait-Job、Receive-Jobで結果取得
8. 処理時間計測
   └─ Measure-Commandで実行時間を出力
```

### フローチャート

```mermaid
flowchart TD
    A[スクリプト開始] --> B[tools.ps1読み込み]
    B --> C[ExtractPackageスクリプトブロック定義]
    C --> D{InputPath存在?}
    D -->|No| E[正常終了]
    D -->|Yes| F[*.nupkg検索]
    F --> G[ループ: 各パッケージ]
    G --> H[Start-Jobで並列展開]
    H --> I{全パッケージ起動?}
    I -->|No| G
    I -->|Yes| J[Wait-Jobで完了待機]
    J --> K[Receive-Jobで結果取得]
    K --> L[処理完了]
```

### ExtractPackageスクリプトブロック

```mermaid
flowchart TD
    A[ジョブ開始] --> B{PackagePath存在?}
    B -->|No| C[エラー終了]
    B -->|Yes| D[パッケージID取得]
    D --> E[展開先ディレクトリ作成]
    E --> F[ZipFile.OpenRead]
    F --> G[ループ: 各エントリ]
    G --> H{拡張子は.dll/.exe/.pdb?}
    H -->|No| I[スキップ]
    H -->|Yes| J[ターゲットパス構築]
    I --> K{次のエントリ?}
    J --> L[ディレクトリ作成]
    L --> M[ファイル抽出]
    M --> K
    K -->|Yes| G
    K -->|No| N[Zipクローズ]
    N --> O[ジョブ完了]
```

## データベース操作仕様

### 操作別データベース影響一覧

データベース操作なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 1 | パッケージ不存在 | 指定されたパッケージファイルが存在しない | InputPathを確認 |
| 1 | 展開失敗 | ZIPファイルの読み取りまたは展開に失敗 | パッケージの整合性を確認 |
| 1 | 例外発生 | スクリプト実行中の例外 | ログを確認し原因を調査 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし |
| リトライ間隔 | 該当なし |
| リトライ対象エラー | 該当なし |

### 障害時対応

1. エラーメッセージを確認し、失敗したパッケージを特定
2. パッケージファイルの存在と読み取り権限を確認
3. パッケージファイルの整合性を確認（破損していないか）
4. ExtractPathへの書き込み権限を確認
5. 手動でZIP展開を試行して問題を切り分け

## トランザクション仕様

| 項目 | 内容 |
|-----|------|
| トランザクション範囲 | 該当なし（ファイル操作） |
| コミットタイミング | 各ファイル展開完了時 |
| ロールバック条件 | なし（部分展開の可能性あり） |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | 数個〜数百個のNuGetパッケージ |
| 目標処理時間 | パッケージ数とサイズに依存（数秒〜数分） |
| メモリ使用量上限 | パッケージサイズに依存 |

## 排他制御

- 同一ExtractPathへの同時実行は避けるべき
- Start-Jobによる並列処理で内部的に並行実行

## ログ出力

| ログ種別 | 出力タイミング | 出力内容 |
|---------|--------------|---------|
| 情報ログ | パス不存在時 | "Input Path does not exist: {path}" |
| 情報ログ | 展開開始時 | "Extracting {filename} ..." |
| テレメトリ | パッケージ不存在時 | Buildカテゴリで"Input file does not exist" |
| テレメトリ | 例外発生時 | Sdlカテゴリでエラー情報 |

## 監視・アラート

| 監視項目 | 閾値 | アラート先 |
|---------|-----|----------|
| 実行失敗 | 終了コード1 | パイプライン通知 |

## 備考

- NuGetパッケージはZIP形式のため、System.IO.Compression.ZipFileで展開
- 抽出対象は.dll、.exe、.pdbのみ（セキュリティスキャンに必要なファイル）
- Start-Jobによる並列処理でパフォーマンスを向上
- $using:変数でジョブ内から親スコープの変数を参照
- パッケージ内のディレクトリ構造を保持して展開
- Measure-Commandで処理時間を計測・出力
- InputPathが存在しない場合は正常終了（エラーではない）
