# 機能設計書 15-シェルスクリプト実行

## 概要

本ドキュメントは、Jenkinsにおけるシェルスクリプト実行（Shell）機能の設計について記述する。この機能は、Unix/Linux環境でシェルスクリプトを実行するビルドステップを提供する。

### 本機能の処理概要

シェルスクリプト実行機能は、ビルドステップとしてシェルコマンドを実行する。ユーザーが入力したスクリプトを一時ファイルとして保存し、指定されたシェル（デフォルト: /bin/sh）で実行する。shebang（#!）による任意インタプリタの指定もサポートする。

**業務上の目的・背景**：CI/CDパイプラインでは、コンパイル、テスト、デプロイなど様々な処理をシェルスクリプトで実行することが多い。シェルスクリプト実行機能により、これらの処理をJenkinsジョブに組み込み、自動化することができる。

**機能の利用シーン**：
- ソースコードのコンパイル（make, gcc等）
- テストスクリプトの実行
- ファイル操作、環境構築
- デプロイスクリプトの実行
- 外部コマンド・ツールの呼び出し

**主要な処理内容**：
1. CommandInterpreter基底クラスによる共通処理の継承
2. スクリプト内容の改行コード変換（Unix形式）
3. 一時シェルスクリプトファイル（.sh）の生成
4. buildCommandLine()によるシェル実行コマンド構築
5. Launcher経由でのプロセス起動と実行
6. 終了コードに基づくビルド結果判定

**関連システム・外部連携**：ノード上のシェル（/bin/sh等）、環境変数。

**権限による制御**：Job.CONFIGURE権限でビルドステップの設定変更が可能。シェル実行パスの設定はJenkins.ADMINISTER権限が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 8 | ジョブ設定 | 主画面 | シェルビルドステップの追加・編集 |
| 31 | システム設定 | 設定画面 | デフォルトシェル実行パスの設定 |

## 機能種別

ビルドステップ / プロセス実行

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| command | String | Yes | 実行するシェルスクリプト | - |
| unstableReturn | Integer | No | UNSTABLEとみなす終了コード | 1-255 |
| configuredLocalRules | List | No | 環境変数フィルタルール | - |

### 入力データソース

- ジョブ設定画面からのユーザー入力
- config.xml: ビルドステップ設定の永続化

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 終了コード | int | シェルスクリプトの終了コード |
| 標準出力 | String | コンソールログへの出力 |
| 標準エラー | String | コンソールログへの出力 |

### 出力先

- BuildListener: コンソールログへの出力
- ビルド結果: SUCCESS/UNSTABLE/FAILUREの判定

## 処理フロー

### 処理シーケンス

```
1. Shell.perform()呼び出し（CommandInterpreterから継承）
2. getContents()でスクリプト内容取得
   └─ 改行コードをUnix形式（LF）に変換
   └─ 非ASCII文字対応で先頭に改行追加
3. 一時ファイル作成（.sh拡張子）
4. buildCommandLine()で実行コマンド構築
   └─ shebang（#!）があればそのインタプリタを使用
   └─ なければDescriptor.getShellOrDefault()を使用
5. Launcher.launch()でプロセス起動
   └─ -xeオプションでデバッグ・エラー即時終了
6. 終了コード判定
   └─ 0: SUCCESS
   └─ unstableReturn設定値: UNSTABLE
   └─ その他: FAILURE
```

### フローチャート

```mermaid
flowchart TD
    A[Shell.perform開始] --> B[getContents]
    B --> C[改行コード変換]
    C --> D[非ASCII対応処理]
    D --> E[一時ファイル作成]
    E --> F{shebangあり?}
    F -->|Yes| G[shebangのインタプリタ使用]
    F -->|No| H[getShellOrDefault]
    G --> I[buildCommandLine]
    H --> I
    I --> J[Launcher.launch]
    J --> K{終了コード?}
    K -->|0| L[SUCCESS]
    K -->|unstableReturn| M[UNSTABLE]
    K -->|その他| N[FAILURE]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-15-01 | デフォルトシェル | Unixでは/bin/sh、Windowsではsh | シェル未指定時 |
| BR-15-02 | shebang優先 | スクリプト先頭が#!の場合、指定インタプリタを使用 | shebangあり時 |
| BR-15-03 | 実行オプション | デフォルトで-xeオプション（トレース表示、エラー即時終了） | shebangなし時 |
| BR-15-04 | 改行コード変換 | スクリプトの改行コードをLFに統一 | 常時 |
| BR-15-05 | 終了コード範囲 | unstableReturnは1-255の範囲で設定可能 | バリデーション時 |

### 計算ロジック

- 非ASCII対応: スクリプトが#!で始まらず、先頭が改行でない場合は先頭に改行追加
- 終了コード判定: `exitCode == 0 ? SUCCESS : (exitCode == unstableReturn ? UNSTABLE : FAILURE)`

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

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

本機能はデータベースではなく、XMLファイルベースの設定永続化を使用。

| 操作 | 対象ファイル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| ビルドステップ保存 | jobs/{jobName}/config.xml | UPDATE | Shellステップ設定の永続化 |
| グローバル設定保存 | hudson.tasks.Shell.xml | UPDATE | デフォルトシェル設定の永続化 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 非0終了コード | プロセスエラー | シェルスクリプトがエラー終了 | ビルド失敗 |
| IOException | I/Oエラー | 一時ファイル作成失敗 | ビルド失敗 |
| InterruptedException | 中断 | ユーザーによるビルド中断 | ビルド中断 |

### リトライ仕様

リトライはユーザーによる手動再実行のみ。自動リトライ機構なし。

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

特になし（単一プロセス実行）。

## パフォーマンス要件

特になし（シェルスクリプト実行時間依存）。

## セキュリティ考慮事項

- シェル実行パスの変更はJenkins.ADMINISTER権限が必要
- FormValidation.validateExecutable()でシェルパスの存在確認
- 環境変数フィルタルールで機密情報の制御が可能

## 備考

- @Symbol("shell")でPipeline DSLからはshellとして参照可能
- PersistentDescriptorによりDescriptor設定が自動永続化

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

ShellクラスとCommandInterpreter基底クラスの関係を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Shell.java | `core/src/main/java/hudson/tasks/Shell.java` | シェル実行ビルドステップ |
| 1-2 | CommandInterpreter.java | `core/src/main/java/hudson/tasks/CommandInterpreter.java` | コマンド実行基底クラス |

**読解のコツ**: ShellはCommandInterpreterを継承しており、perform()の主要ロジックは基底クラスに存在する。

#### Step 2: コンストラクタと設定を理解する

DataBoundConstructor/Setterによる設定バインディングを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Shell(String) | `core/src/main/java/hudson/tasks/Shell.java` | コンストラクタ（65-68行目） |
| 2-2 | setUnstableReturn() | `core/src/main/java/hudson/tasks/Shell.java` | UNSTABLEコード設定（127-130行目） |

**主要処理フロー**:
- **65-68行目**: コンストラクタでLineEndingConversion.convertEOL()によりUnix改行に変換
- **127-130行目**: unstableReturn設定（@DataBoundSetter）

#### Step 3: スクリプト生成と実行コマンド構築を理解する

buildCommandLine()とgetContents()の処理を追う。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | buildCommandLine() | `core/src/main/java/hudson/tasks/Shell.java` | 実行コマンド構築（99-110行目） |
| 3-2 | getContents() | `core/src/main/java/hudson/tasks/Shell.java` | スクリプト内容取得（113-115行目） |

**主要処理フロー**:
- **99-110行目**: shebang判定、インタプリタ指定またはデフォルトシェル+"-xe"オプション
- **113-115行目**: addLineFeedForNonASCII()で非ASCII対応

#### Step 4: Descriptorとグローバル設定を理解する

シェル実行パスのグローバル設定を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | DescriptorImpl | `core/src/main/java/hudson/tasks/Shell.java` | Descriptor実装（150-260行目） |
| 4-2 | getShellOrDefault() | `core/src/main/java/hudson/tasks/Shell.java` | デフォルトシェル取得（184-199行目） |

**主要処理フロー**:
- **184-199行目**: getShellOrDefault(VirtualChannel)でリモートノードのシェルを判定
- **250-258行目**: Shellinterpreterでリモート判定（Windows: "sh", Unix: "/bin/sh"）

### プログラム呼び出し階層図

```
AbstractBuild.run()
    │
    └─ Shell.perform() [CommandInterpreterから継承]
           │
           ├─ getContents() [Shell.java:113-115]
           │      │
           │      └─ addLineFeedForNonASCII() [Shell.java:88-96]
           │
           ├─ createScriptFile() [CommandInterpreter]
           │      │
           │      └─ getFileExtension() → ".sh" [Shell.java:118-120]
           │
           ├─ buildCommandLine() [Shell.java:99-110]
           │      │
           │      ├─ [shebang判定]
           │      │
           │      └─ getShellOrDefault() [Shell.java:184-199]
           │             │
           │             └─ Shellinterpreter.call() [リモート]
           │
           └─ Launcher.launch()
                  │
                  └─ [プロセス実行]
```

### データフロー図

```
[入力]                    [処理]                         [出力]

config.xml ────────────▶ Shell(command)
  (スクリプト設定)              │
                              ▼
                        LineEndingConversion ─────────▶ Unix改行形式
                              │
                              ▼
                        addLineFeedForNonASCII ───────▶ スクリプト内容
                              │
                              ▼
                        createScriptFile ─────────────▶ /tmp/*.sh
                              │
                              ▼
                        buildCommandLine ─────────────▶ ["/bin/sh", "-xe", "*.sh"]
                              │
                              ▼
Launcher ─────────────▶ launch() ─────────────────────▶ プロセス実行
                              │
                              ▼
                        [終了コード判定]
                              │
                        ┌─────┼─────┐
                        ▼     ▼     ▼
                      SUCCESS UNSTABLE FAILURE
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Shell.java | `core/src/main/java/hudson/tasks/Shell.java` | ソース | シェル実行ビルドステップ |
| CommandInterpreter.java | `core/src/main/java/hudson/tasks/CommandInterpreter.java` | ソース | コマンド実行基底クラス |
| LineEndingConversion.java | `core/src/main/java/hudson/util/LineEndingConversion.java` | ソース | 改行コード変換ユーティリティ |
| Launcher.java | `core/src/main/java/hudson/Launcher.java` | ソース | プロセス起動クラス |
| EnvVarsFilterLocalRule.java | `core/src/main/java/jenkins/tasks/filters/EnvVarsFilterLocalRule.java` | ソース | 環境変数フィルタルール |
