プロジェクト

全般

プロフィール

Wiki » 履歴 » バージョン 1

廣瀬 僚一, 2026/06/01 17:05

1 1 廣瀬 僚一
# MainWP 更新履歴 Redmine 自動連携
2
3
## 概要
4
5
MainWP でプラグイン・テーマ・WordPress 本体を更新した際に、Redmine へ自動でチケットを作成する仕組み。
6
7
- **対象サーバー**: MainWP ダッシュボードサーバー
8
- **方式**: MU-Plugins に WordPress フックを記述し、Redmine REST API へ直接 POST
9
- **WP Webhooks プラグインは不使用**(Basic 認証・IP 制限の影響を避けるため)
10
11
---
12
13
## ファイル構成
14
15
```
16
/wp-content/mu-plugins/mainwp-webhook.php   ← メインファイル
17
/wp-content/mainwp-redmine-debug.log        ← デバッグログ(確認後削除可)
18
```
19
20
---
21
22
## 設置ファイル
23
24
### `/wp-content/mu-plugins/mainwp-webhook.php`
25
26
```php
27
<?php
28
/**
29
 * MainWP更新後にRedmineへ直接通知
30
 * 設置場所: /wp-content/mu-plugins/mainwp-webhook.php
31
 */
32
33
define( 'REDMINE_URL',     'https://your-redmine.example.com' );
34
define( 'REDMINE_API_KEY', 'YOUR_REDMINE_API_KEY' );
35
define( 'REDMINE_PROJECT', 'wordpress-updates' );
36
37
// ===== プラグイン・テーマ・翻訳の更新後 =====
38
add_action( 'mainwp_install_update_actions', function( $website, $action, $data, $type ) {
39
    if ( 'updated' !== $action ) return;
40
    if ( ! in_array( $type, array( 'plugin', 'theme', 'trans' ) ) ) return;
41
42
    $type_label = [
43
        'plugin' => 'プラグイン',
44
        'theme'  => 'テーマ',
45
        'trans'  => '翻訳',
46
    ][ $type ] ?? $type;
47
48
    $site     = $website->url ?? '不明';
49
    $datetime = ( new DateTime( 'now', new DateTimeZone( 'Asia/Tokyo' ) ) )->format( 'Y-m-d H:i:s' );
50
51
    // WPバージョン取得
52
    $site_info  = json_decode( $website->site_info ?? '{}', true );
53
    $wp_version = $site_info['wpversion'] ?? '不明';
54
55
    // 更新内容を取得
56
    $updated_data = isset( $data['updated_data'] ) ? $data['updated_data'] : array();
57
    $details = '';
58
    if ( is_array( $updated_data ) && ! empty( $updated_data ) ) {
59
        foreach ( $updated_data as $item ) {
60
            $name = $item['name']        ?? $item['slug'] ?? '不明';
61
            $old  = $item['old_version'] ?? '?';
62
            $new  = $item['version']     ?? '?';
63
            $details .= "- {$name}: {$old} → {$new}\n";
64
        }
65
    }
66
    if ( empty( $details ) ) return;
67
68
    $subject = "[WP更新] {$site} / {$type_label}";
69
    $body    = "## 更新情報\n\n"
70
             . "- **サイト:** {$site}\n"
71
             . "- **種別:** {$type_label}\n"
72
             . "- **WPバージョン:** {$wp_version}\n"
73
             . "- **作業日時:** {$datetime}\n\n"
74
             . "## 更新内容\n\n{$details}";
75
76
    mainwp_post_to_redmine( $subject, $body );
77
}, 10, 4 );
78
79
// ===== WordPress本体の更新後 =====
80
add_action( 'mainwp_after_wp_update', function( $information, $website ) {
81
    $site     = $website->url ?? '不明';
82
    $datetime = ( new DateTime( 'now', new DateTimeZone( 'Asia/Tokyo' ) ) )->format( 'Y-m-d H:i:s' );
83
84
    // wp_upgradesから更新前後バージョンを取得
85
    $wp_upgrades = json_decode( $website->wp_upgrades ?? '{}', true );
86
    $old = $wp_upgrades['current'] ?? $information['old_version'] ?? '?';
87
    $new = $wp_upgrades['new']     ?? $information['new_version'] ?? '?';
88
89
    $subject = "[WP更新] {$site} / WordPress本体";
90
    $body    = "## 更新情報\n\n"
91
             . "- **サイト:** {$site}\n"
92
             . "- **種別:** WordPress本体\n"
93
             . "- **更新前:** {$old}\n"
94
             . "- **更新後:** {$new}\n"
95
             . "- **作業日時:** {$datetime}\n";
96
97
    mainwp_post_to_redmine( $subject, $body );
98
}, 10, 2 );
99
100
// ===== Redmine API へ直接POST =====
101
function mainwp_post_to_redmine( $subject, $body ) {
102
    $data = [
103
        'issue' => [
104
            'project_id'  => REDMINE_PROJECT,
105
            'subject'     => $subject,
106
            'description' => $body,
107
            'tracker_id'  => 2,
108
            'status_id'   => 5,   // 終了
109
            'done_ratio'  => 100,
110
        ]
111
    ];
112
113
    $ch = curl_init( REDMINE_URL . '/issues.json' );
114
    curl_setopt_array( $ch, [
115
        CURLOPT_RETURNTRANSFER => true,
116
        CURLOPT_POST           => true,
117
        CURLOPT_POSTFIELDS     => json_encode( $data ),
118
        CURLOPT_HTTPHEADER     => [
119
            'Content-Type: application/json',
120
            'X-Redmine-API-Key: ' . REDMINE_API_KEY,
121
        ],
122
        CURLOPT_TIMEOUT => 15,
123
    ] );
124
125
    $response = curl_exec( $ch );
126
    $status   = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
127
    curl_close( $ch );
128
129
    // デバッグログ(確認後削除してOK)
130
    file_put_contents(
131
        WP_CONTENT_DIR . '/mainwp-redmine-debug.log',
132
        date('Y-m-d H:i:s') . " status={$status} response={$response}\n",
133
        FILE_APPEND
134
    );
135
}
136
```
137
138
---
139
140
## 設定値
141
142
| 定数 | 説明 |
143
|------|------|
144
| `REDMINE_URL` | Redmine の URL(末尾スラッシュなし) |
145
| `REDMINE_API_KEY` | Redmine の個人 API キー(マイアカウント画面で確認) |
146
| `REDMINE_PROJECT` | Redmine のプロジェクト識別子 |
147
148
### Redmine チケット設定
149
150
| 項目 | 値 | 説明 |
151
|------|----|------|
152
| `tracker_id` | `2` | トラッカー ID(環境に合わせて変更) |
153
| `status_id` | `5` | 終了ステータス |
154
| `done_ratio` | `100` | 進捗率 100% |
155
156
---
157
158
## 動作する WordPress フック
159
160
| フック名 | タイミング | 引数 |
161
|----------|-----------|------|
162
| `mainwp_install_update_actions` | プラグイン・テーマ・翻訳の更新後 | `$website, $action, $data, $type` |
163
| `mainwp_after_wp_update` | WordPress 本体の更新後 | `$information, $website` |
164
165
### バージョン情報の取得元
166
167
| 情報 | 取得元 |
168
|------|--------|
169
| プラグイン更新前バージョン | `$data['updated_data'][]['old_version']` |
170
| プラグイン更新後バージョン | `$data['updated_data'][]['version']` |
171
| WP バージョン | `$website->site_info` の `wpversion` |
172
| WP 本体更新前バージョン | `$website->wp_upgrades` の `current` |
173
| WP 本体更新後バージョン | `$website->wp_upgrades` の `new` |
174
175
---
176
177
## 調査過程でわかったこと
178
179
- `mainwp_after_plugin_theme_translation_update` フックは存在するが、**引数 `$information` が空で渡ってくる**ため使用不可
180
- バージョン情報は `$information` ではなく `$data['updated_data']` に格納されている
181
- `mainwp_install_update_actions` フックが正しいバージョン情報を持つ唯一のフック
182
- WP Webhooks プラグインの無料版では `WordPress hook fired` が使えない(Pro 限定)
183
- Basic 認証・IP 制限がかかった環境では `wp_remote_post` による自サーバーへの HTTP リクエストが 401 で弾かれるため、中継 PHP ファイルを経由せず Redmine API へ直接 POST する構成が必要
184
185
---
186
187
## デバッグログの確認
188
189
```bash
190
cat /var/www/html/wp-content/mainwp-redmine-debug.log
191
```
192
193
`status=201` であれば Redmine へのチケット作成成功。確認後はログ出力コードを削除してよい。