|
| 1 | +name: Check for new MySQL Shell releases |
| 2 | + |
| 3 | + |
| 4 | +# このワークフローは以下を行います: |
| 5 | +# 1. MySQL Shellの最新リリースタグを検出 |
| 6 | +# 2. 現在のDockerイメージのバージョンと比較 |
| 7 | +# 3. 更新が必要な場合は自動的にPRを作成 |
| 8 | + |
| 9 | +# このワークフローは以下を行います: |
| 10 | +# 1. MySQL Shellの最新リリースタグを検出 |
| 11 | +# 2. 現在のDockerイメージのバージョンと比較 |
| 12 | +# 3. 更新が必要な場合は自動的にPRを作成 |
| 13 | + |
| 14 | +on: |
| 15 | + schedule: |
| 16 | + # 毎日 UTC 3時17分に実行 (ランダムな時刻で負荷分散) |
| 17 | + - cron: '17 3 * * *' |
| 18 | + workflow_dispatch: # 手動実行も可能にする |
| 19 | + inputs: |
| 20 | + dry_run: |
| 21 | + description: '実際のPRを作成せずにテスト実行' |
| 22 | + required: false |
| 23 | + default: false |
| 24 | + type: boolean |
| 25 | + innovation_version: |
| 26 | + description: 'テスト用の強制Innovation版バージョン(例:9.3.0)' |
| 27 | + required: false |
| 28 | + type: string |
| 29 | + lts_version: |
| 30 | + description: 'テスト用の強制LTS版バージョン(例:8.4.5)' |
| 31 | + required: false |
| 32 | + type: string |
| 33 | + inputs: |
| 34 | + dry_run: |
| 35 | + description: '実際のPRを作成せずにテスト実行' |
| 36 | + required: false |
| 37 | + default: false |
| 38 | + type: boolean |
| 39 | + innovation_version: |
| 40 | + description: 'テスト用の強制Innovation版バージョン(例:9.3.0)' |
| 41 | + required: false |
| 42 | + type: string |
| 43 | + lts_version: |
| 44 | + description: 'テスト用の強制LTS版バージョン(例:8.4.5)' |
| 45 | + required: false |
| 46 | + type: string |
| 47 | + |
| 48 | +jobs: |
| 49 | + check-release: |
| 50 | + permissions: |
| 51 | + contents: read |
| 52 | + pull-requests: write |
| 53 | + runs-on: ubuntu-latest |
| 54 | + # ジョブの概要説明 |
| 55 | + name: Check and update MySQL Shell versions |
| 56 | + steps: |
| 57 | + # ステップ1: リポジトリの取得 |
| 58 | + - name: Checkout repository |
| 59 | + uses: actions/checkout@v4 |
| 60 | + with: |
| 61 | + # プルリクエスト作成のためにトークンが必要 |
| 62 | + token: ${{ secrets.GITHUB_TOKEN }} |
| 63 | + |
| 64 | + - name: Get current versions |
| 65 | + id: current_versions |
| 66 | + run: | |
| 67 | + # Dockerfileから現在のバージョンを取得 |
| 68 | + if [[ ! -f docker/innovation/Dockerfile ]] || [[ ! -f docker/lts/Dockerfile ]]; then |
| 69 | + echo "::error::Required Dockerfiles not found!" |
| 70 | + exit 1 |
| 71 | + fi |
| 72 | + |
| 73 | + CURRENT_INNOVATION=$(grep -oP '(?<=^ARG MYSQL_SHELL_VERSION=)\d+\.\d+\.\d+' docker/innovation/Dockerfile) |
| 74 | + CURRENT_LTS=$(grep -oP '(?<=^ARG MYSQL_SHELL_VERSION=)\d+\.\d+\.\d+' docker/lts/Dockerfile) |
| 75 | + |
| 76 | + if [[ -z "$CURRENT_INNOVATION" ]] || [[ -z "$CURRENT_LTS" ]]; then |
| 77 | + echo "::error::Failed to extract current versions from Dockerfiles" |
| 78 | + exit 1 |
| 79 | + fi |
| 80 | + |
| 81 | + echo "CURRENT_INNOVATION=${CURRENT_INNOVATION}" >> $GITHUB_OUTPUT |
| 82 | + echo "CURRENT_LTS=${CURRENT_LTS}" >> $GITHUB_OUTPUT |
| 83 | + echo "Current Innovation: $CURRENT_INNOVATION" |
| 84 | + echo "Current LTS: $CURRENT_LTS" |
| 85 | + |
| 86 | + # メジャーバージョンを抽出 (後で使用) |
| 87 | + INNOVATION_MAJOR_VERSION=$(echo "$CURRENT_INNOVATION" | cut -d. -f1) |
| 88 | + LTS_MAJOR_VERSION=$(echo "$CURRENT_LTS" | cut -d. -f1) |
| 89 | + echo "INNOVATION_MAJOR_VERSION=${INNOVATION_MAJOR_VERSION}" >> $GITHUB_OUTPUT |
| 90 | + echo "LTS_MAJOR_VERSION=${LTS_MAJOR_VERSION}" >> $GITHUB_OUTPUT |
| 91 | +
|
| 92 | + - name: Get latest MySQL Shell tags |
| 93 | + id: latest_tags |
| 94 | + run: | |
| 95 | + # GitHub APIからタグ情報を取得 |
| 96 | + INNOVATION_MAJOR="${{ steps.current_versions.outputs.INNOVATION_MAJOR_VERSION }}" |
| 97 | + LTS_MAJOR="${{ steps.current_versions.outputs.LTS_MAJOR_VERSION }}" |
| 98 | + |
| 99 | + # テスト用の手動指定バージョンがあれば使用する |
| 100 | + if [[ "${{ github.event_name }}" == "workflow_dispatch" && -n "${{ github.event.inputs.innovation_version }}" ]]; then |
| 101 | + LATEST_INNOVATION="${{ github.event.inputs.innovation_version }}" |
| 102 | + echo "Using manually specified Innovation version: $LATEST_INNOVATION" |
| 103 | + else |
| 104 | + # GitHub APIからのフェッチを試みる |
| 105 | + API_RESPONSE=$(curl -s -H "Accept: application/vnd.github+json" \ |
| 106 | + -H "X-GitHub-Api-Version: 2022-11-28" \ |
| 107 | + -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ |
| 108 | + "https://api.github.com/repos/mysql/mysql-shell/tags?per_page=100") |
| 109 | + |
| 110 | + if [[ -z "$API_RESPONSE" ]] || [[ "$API_RESPONSE" == *"rate limit"* ]] || [[ "$API_RESPONSE" == *"Bad credentials"* ]]; then |
| 111 | + echo "::error::Failed to fetch data from GitHub API: $(echo "$API_RESPONSE" | grep -o '"message":"[^"]*"' || echo 'Unknown error')" |
| 112 | + exit 1 |
| 113 | + fi |
| 114 | + |
| 115 | + # 正規表現パターンを動的に構築 |
| 116 | + INNOVATION_PATTERN="^${INNOVATION_MAJOR}\\.\\d+\\.\\d+$" |
| 117 | + |
| 118 | + LATEST_INNOVATION=$(echo "$API_RESPONSE" | jq -r --arg pattern "$INNOVATION_PATTERN" '[.[] | select(.name | test($pattern))][0].name') |
| 119 | + fi |
| 120 | +
|
| 121 | + # LTSバージョンの取得(手動指定またはAPI) |
| 122 | + if [[ "${{ github.event_name }}" == "workflow_dispatch" && -n "${{ github.event.inputs.lts_version }}" ]]; then |
| 123 | + LATEST_LTS="${{ github.event.inputs.lts_version }}" |
| 124 | + echo "Using manually specified LTS version: $LATEST_LTS" |
| 125 | + else |
| 126 | + # APIレスポンスを再利用 |
| 127 | + LTS_PATTERN="^${LTS_MAJOR}\\.\\d+\\.\\d+$" |
| 128 | + LATEST_LTS=$(echo "$API_RESPONSE" | jq -r --arg pattern "$LTS_PATTERN" '[.[] | select(.name | test($pattern))][0].name') |
| 129 | + fi |
| 130 | + |
| 131 | + # 結果の検証 |
| 132 | + if [[ -z "$LATEST_INNOVATION" ]] || [[ "$LATEST_INNOVATION" == "null" ]] || [[ -z "$LATEST_LTS" ]] || [[ "$LATEST_LTS" == "null" ]]; then |
| 133 | + echo "::warning::Failed to find matching versions. Using hardcoded patterns as fallback." |
| 134 | + # フォールバック: ハードコードされたバージョンパターン |
| 135 | + LATEST_INNOVATION=${LATEST_INNOVATION:-$(echo "$API_RESPONSE" | jq -r '[.[] | select(.name | test("^9\\.\\d+\\.\\d+$"))][0].name')} |
| 136 | + LATEST_LTS=${LATEST_LTS:-$(echo "$API_RESPONSE" | jq -r '[.[] | select(.name | test("^8\\.\\d+\\.\\d+$"))][0].name')} |
| 137 | + fi |
| 138 | + |
| 139 | + echo "LATEST_INNOVATION=${LATEST_INNOVATION}" >> $GITHUB_OUTPUT |
| 140 | + echo "LATEST_LTS=${LATEST_LTS}" >> $GITHUB_OUTPUT |
| 141 | + echo "Latest Innovation: $LATEST_INNOVATION" |
| 142 | + echo "Latest LTS: $LATEST_LTS" |
| 143 | +
|
| 144 | + - name: Check versions |
| 145 | + id: check_versions |
| 146 | + run: | |
| 147 | + # バージョン比較ユーティリティ関数 |
| 148 | + version_gt() { |
| 149 | + # $1 > $2 の場合に0を返す |
| 150 | + test "$(echo "$1 $2" | tr " " "\n" | sort -V | head -n 1)" != "$1" |
| 151 | + } |
| 152 | + |
| 153 | + CURRENT_INNOVATION="${{ steps.current_versions.outputs.CURRENT_INNOVATION }}" |
| 154 | + LATEST_INNOVATION="${{ steps.latest_tags.outputs.LATEST_INNOVATION }}" |
| 155 | + CURRENT_LTS="${{ steps.current_versions.outputs.CURRENT_LTS }}" |
| 156 | + LATEST_LTS="${{ steps.latest_tags.outputs.LATEST_LTS }}" |
| 157 | + |
| 158 | + INNOVATION_UPDATE_NEEDED="false" |
| 159 | + LTS_UPDATE_NEEDED="false" |
| 160 | + |
| 161 | + # Innovation バージョンのチェック |
| 162 | + if [[ -z "$LATEST_INNOVATION" ]] || [[ "$LATEST_INNOVATION" == "null" ]]; then |
| 163 | + echo "::warning::No valid Innovation version found in API response" |
| 164 | + elif [[ "$CURRENT_INNOVATION" != "$LATEST_INNOVATION" ]]; then |
| 165 | + if version_gt "$LATEST_INNOVATION" "$CURRENT_INNOVATION"; then |
| 166 | + echo "Update needed for Innovation: $CURRENT_INNOVATION -> $LATEST_INNOVATION (newer version available)" |
| 167 | + INNOVATION_UPDATE_NEEDED="true" |
| 168 | + else |
| 169 | + echo "::warning::Latest Innovation version ($LATEST_INNOVATION) is older than current ($CURRENT_INNOVATION). Skipping update." |
| 170 | + fi |
| 171 | + else |
| 172 | + echo "Innovation is up-to-date at version $CURRENT_INNOVATION." |
| 173 | + fi |
| 174 | + |
| 175 | + # LTS バージョンのチェック |
| 176 | + if [[ -z "$LATEST_LTS" ]] || [[ "$LATEST_LTS" == "null" ]]; then |
| 177 | + echo "::warning::No valid LTS version found in API response" |
| 178 | + elif [[ "$CURRENT_LTS" != "$LATEST_LTS" ]]; then |
| 179 | + if version_gt "$LATEST_LTS" "$CURRENT_LTS"; then |
| 180 | + echo "Update needed for LTS: $CURRENT_LTS -> $LATEST_LTS (newer version available)" |
| 181 | + LTS_UPDATE_NEEDED="true" |
| 182 | + else |
| 183 | + echo "::warning::Latest LTS version ($LATEST_LTS) is older than current ($CURRENT_LTS). Skipping update." |
| 184 | + fi |
| 185 | + else |
| 186 | + echo "LTS is up-to-date at version $CURRENT_LTS." |
| 187 | + fi |
| 188 | + |
| 189 | + # テストモードでは常に更新が必要であると報告 |
| 190 | + if [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ github.event.inputs.dry_run }}" == "true" ]]; then |
| 191 | + if [[ -n "${{ github.event.inputs.innovation_version }}" ]]; then |
| 192 | + echo "::notice::テストモード: Innovation更新をテスト実行します" |
| 193 | + INNOVATION_UPDATE_NEEDED="true" |
| 194 | + fi |
| 195 | + if [[ -n "${{ github.event.inputs.lts_version }}" ]]; then |
| 196 | + echo "::notice::テストモード: LTS更新をテスト実行します" |
| 197 | + LTS_UPDATE_NEEDED="true" |
| 198 | + fi |
| 199 | + fi |
| 200 | + |
| 201 | + echo "INNOVATION_UPDATE_NEEDED=${INNOVATION_UPDATE_NEEDED}" >> $GITHUB_OUTPUT |
| 202 | + echo "LTS_UPDATE_NEEDED=${LTS_UPDATE_NEEDED}" >> $GITHUB_OUTPUT |
| 203 | +
|
| 204 | + - name: Update files and create PR if needed |
| 205 | + if: steps.check_versions.outputs.INNOVATION_UPDATE_NEEDED == 'true' || steps.check_versions.outputs.LTS_UPDATE_NEEDED == 'true' |
| 206 | + env: |
| 207 | + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
| 208 | + CURRENT_INNOVATION: ${{ steps.current_versions.outputs.CURRENT_INNOVATION }} |
| 209 | + LATEST_INNOVATION: ${{ steps.latest_tags.outputs.LATEST_INNOVATION }} |
| 210 | + CURRENT_LTS: ${{ steps.current_versions.outputs.CURRENT_LTS }} |
| 211 | + LATEST_LTS: ${{ steps.latest_tags.outputs.LATEST_LTS }} |
| 212 | + DRY_RUN: ${{ github.event.inputs.dry_run == 'true' }} |
| 213 | + run: | |
| 214 | + # dry runモードを確認 |
| 215 | + if [[ "$DRY_RUN" == "true" ]]; then |
| 216 | + echo "::notice::dry runモードで実行しています。実際の変更は行いません。" |
| 217 | + fi |
| 218 | + |
| 219 | + # ブランチ作成 |
| 220 | + BRANCH_NAME="bot/update-mysql-shell-$(date +%Y%m%d%H%M%S)" |
| 221 | + if [[ "$DRY_RUN" != "true" ]]; then |
| 222 | + git config --global user.name 'github-actions[bot]' |
| 223 | + git config --global user.email 'github-actions[bot]@users.noreply.github.com' |
| 224 | + git checkout -b $BRANCH_NAME |
| 225 | + else |
| 226 | + echo "dry run: git checkout -b $BRANCH_NAME" |
| 227 | + fi |
| 228 | + |
| 229 | + PR_BODY="Automated update for MySQL Shell versions.\n\n" |
| 230 | + |
| 231 | + # バージョン更新関数 |
| 232 | + update_version() { |
| 233 | + local type=$1 |
| 234 | + local current_version=$2 |
| 235 | + local new_version=$3 |
| 236 | + local major_version=$(echo "$new_version" | cut -d. -f1) |
| 237 | + local minor_version=$(echo "$new_version" | cut -d. -f2) |
| 238 | + local short_version="${major_version}.${minor_version}" |
| 239 | + |
| 240 | + echo "Updating $type to $new_version (major.minor: $short_version)..." |
| 241 | + |
| 242 | + # Dockerfile の更新 |
| 243 | + if [[ "$DRY_RUN" != "true" ]]; then |
| 244 | + sed -i "s/^ARG MYSQL_SHELL_VERSION=.*/ARG MYSQL_SHELL_VERSION=$new_version/" docker/$type/Dockerfile || { |
| 245 | + echo "::warning::Failed to update version in docker/$type/Dockerfile, but continuing..." |
| 246 | + } |
| 247 | + else |
| 248 | + echo "dry run: docker/$type/Dockerfile 内の ARG MYSQL_SHELL_VERSION=$current_version を $new_version に更新" |
| 249 | + fi |
| 250 | + |
| 251 | + # README.md の更新 |
| 252 | + if [[ "$type" == "innovation" ]]; then |
| 253 | + local match_pattern="Innovation Series ([0-9]\\.[0-9]\\.[x0-9])" |
| 254 | + local replace_value="Innovation Series (${major_version}.${minor_version}.x)" |
| 255 | + local tag_pattern="snickerjp\/docker-mysql-shell:${major_version}\\.[0-9]" |
| 256 | + local tag_replace="snickerjp\/docker-mysql-shell:${short_version}" |
| 257 | + |
| 258 | + if [[ "$DRY_RUN" != "true" ]]; then |
| 259 | + # 各sedコマンドを個別にエラーハンドリング |
| 260 | + sed -i "s/$match_pattern/$replace_value/g" README.md || { |
| 261 | + echo "::warning::Failed to update Innovation Series version in README.md, but continuing..." |
| 262 | + } |
| 263 | + |
| 264 | + sed -i "s/$tag_pattern/$tag_replace/g" README.md || { |
| 265 | + echo "::warning::Failed to update Innovation image tag in README.md, but continuing..." |
| 266 | + } |
| 267 | + else |
| 268 | + echo "dry run: README.md 内の '$match_pattern' を '$replace_value' に更新" |
| 269 | + echo "dry run: README.md 内の '$tag_pattern' を '$tag_replace' に更新" |
| 270 | + fi |
| 271 | + else |
| 272 | + local match_pattern="LTS Series ([0-9]\\.[0-9]\\.[x0-9])" |
| 273 | + local replace_value="LTS Series (${major_version}.${minor_version}.x)" |
| 274 | + local tag_pattern="snickerjp\/docker-mysql-shell:${major_version}\\.[0-9]" |
| 275 | + local tag_replace="snickerjp\/docker-mysql-shell:${short_version}" |
| 276 | + |
| 277 | + if [[ "$DRY_RUN" != "true" ]]; then |
| 278 | + sed -i "s/$match_pattern/$replace_value/g" README.md || { |
| 279 | + echo "::warning::Failed to update LTS Series version in README.md, but continuing..." |
| 280 | + } |
| 281 | + |
| 282 | + sed -i "s/$tag_pattern/$tag_replace/g" README.md || { |
| 283 | + echo "::warning::Failed to update LTS image tag in README.md, but continuing..." |
| 284 | + } |
| 285 | + else |
| 286 | + echo "dry run: README.md 内の '$match_pattern' を '$replace_value' に更新" |
| 287 | + echo "dry run: README.md 内の '$tag_pattern' を '$tag_replace' に更新" |
| 288 | + fi |
| 289 | + fi |
| 290 | + |
| 291 | + # GitHub Actions ワークフローファイルの更新 |
| 292 | + local workflow_pattern="version: ${major_version}\\.[0-9]" |
| 293 | + local workflow_replace="version: ${short_version}" |
| 294 | + for workflow in .github/workflows/docker-*.yml; do |
| 295 | + if [[ -f "$workflow" ]]; then |
| 296 | + if [[ "$DRY_RUN" != "true" ]]; then |
| 297 | + sed -i "s/$workflow_pattern/$workflow_replace/g" "$workflow" || { |
| 298 | + echo "::warning::Failed to update version in $workflow, but continuing..." |
| 299 | + } |
| 300 | + else |
| 301 | + echo "dry run: $workflow 内の '$workflow_pattern' を '$workflow_replace' に更新" |
| 302 | + fi |
| 303 | + fi |
| 304 | + done |
| 305 | + |
| 306 | + # PR本文に変更内容を追加 |
| 307 | + PR_BODY+="* **${type^}:** ${current_version} -> ${new_version}\n" |
| 308 | + |
| 309 | + # 更新が成功したか確認(少なくともDockerfileは必須)- ドライランでは実行しない |
| 310 | + if [[ "$DRY_RUN" != "true" ]]; then |
| 311 | + if ! grep -q "ARG MYSQL_SHELL_VERSION=$new_version" docker/$type/Dockerfile; then |
| 312 | + echo "::warning::Version update in docker/$type/Dockerfile might have failed, but we'll continue..." |
| 313 | + fi |
| 314 | + fi |
| 315 | + } |
| 316 | + |
| 317 | + # Innovation の更新 |
| 318 | + if [[ "${{ steps.check_versions.outputs.INNOVATION_UPDATE_NEEDED }}" == "true" ]]; then |
| 319 | + update_version "innovation" "$CURRENT_INNOVATION" "$LATEST_INNOVATION" |
| 320 | + fi |
| 321 | + |
| 322 | + # LTS の更新 |
| 323 | + if [[ "${{ steps.check_versions.outputs.LTS_UPDATE_NEEDED }}" == "true" ]]; then |
| 324 | + update_version "lts" "$CURRENT_LTS" "$LATEST_LTS" |
| 325 | + fi |
| 326 | + |
| 327 | + # 変更をコミットしてプッシュ |
| 328 | + changed_files=$(git status --porcelain | awk '{print $2}') |
| 329 | + if [[ -z "$changed_files" ]]; then |
| 330 | + echo "No changes to commit." |
| 331 | + exit 0 |
| 332 | + fi |
| 333 | + |
| 334 | + # ファイルの変更をチェック |
| 335 | + echo "Changed files:" |
| 336 | + for file in $changed_files; do |
| 337 | + echo "- $file" |
| 338 | + done |
| 339 | + |
| 340 | + # すべての変更をステージング |
| 341 | + if [[ "$DRY_RUN" != "true" ]]; then |
| 342 | + git add $changed_files |
| 343 | + git commit -m "Update MySQL Shell versions (Innovation: $LATEST_INNOVATION, LTS: $LATEST_LTS)" |
| 344 | + |
| 345 | + # エラーハンドリング付きでプッシュ |
| 346 | + if ! git push origin $BRANCH_NAME; then |
| 347 | + echo "::error::Failed to push changes to GitHub" |
| 348 | + exit 1 |
| 349 | + fi |
| 350 | + |
| 351 | + # プルリクエストを作成 |
| 352 | + if ! gh pr create \ |
| 353 | + --base develop \ |
| 354 | + --head $BRANCH_NAME \ |
| 355 | + --title "Update MySQL Shell versions (Innovation: $LATEST_INNOVATION, LTS: $LATEST_LTS)" \ |
| 356 | + --body "$PR_BODY"; then |
| 357 | + echo "::error::Failed to create Pull Request" |
| 358 | + exit 1 |
| 359 | + fi |
| 360 | + |
| 361 | + echo "Pull request created successfully!" |
| 362 | + else |
| 363 | + echo "dry run: 以下のファイルが変更されます:" |
| 364 | + for file in $changed_files; do |
| 365 | + echo "- $file" |
| 366 | + done |
| 367 | + echo "dry run: コミットメッセージ: Update MySQL Shell versions (Innovation: $LATEST_INNOVATION, LTS: $LATEST_LTS)" |
| 368 | + echo "dry run: PR作成: タイトル: Update MySQL Shell versions (Innovation: $LATEST_INNOVATION, LTS: $LATEST_LTS)" |
| 369 | + echo "dry run: PR本文:" |
| 370 | + echo -e "$PR_BODY" |
| 371 | + echo "dry run終了: 実際の変更は行われていません。" |
| 372 | + fi |
| 373 | +
|
| 374 | + # ステップ6: 更新不要の場合の通知 |
| 375 | + - name: No update needed |
| 376 | + if: steps.check_versions.outputs.INNOVATION_UPDATE_NEEDED == 'false' && steps.check_versions.outputs.LTS_UPDATE_NEEDED == 'false' |
| 377 | + run: | |
| 378 | + echo "::notice::No new MySQL Shell versions found. All versions are up to date." |
| 379 | + echo "Current Innovation: ${{ steps.current_versions.outputs.CURRENT_INNOVATION }}" |
| 380 | + echo "Current LTS: ${{ steps.current_versions.outputs.CURRENT_LTS }}" |
0 commit comments