Skip to content

Feat/sakura#1

Merged
blancnoir256 merged 60 commits intomasterfrom
feat/sakura
Feb 25, 2026
Merged

Feat/sakura#1
blancnoir256 merged 60 commits intomasterfrom
feat/sakura

Conversation

@blancnoir256
Copy link
Collaborator

@blancnoir256 blancnoir256 commented Jan 12, 2026

DevOpsBotをさくらVPSのAPIに対応させました。

CIがまだ整備できていません。
かつまだテストしてないのでDraftです。

このあとローカルテストします。

Summary by CodeRabbit

  • New Features

    • Sakuraサーバー統合:ページネーション対応のサーバー一覧取得、重複除去、整形テーブル出力を追加
    • 再起動コマンド:引数検証強化とSakuraベアラートークンによる再起動リクエスト対応を追加
  • Refactor

    • ConoHaからSakuraへプロバイダ移行、設定キーとCLI表示名を更新
    • プロジェクト名を sakura-DevOpsBot に変更
  • Chores

    • CI/CDワークフロー、リリース/イメージタグ処理、ビルド設定を更新
    • モジュール依存関係を調整、docker-compose を .gitignore に追加

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates DevOpsBot from ConoHa VPS API to Sakura VPS API. The implementation includes updating the module name, replacing the authentication mechanism from token-based to Bearer token authentication, and adapting all API endpoints and response structures to match the Sakura VPS API specification.

Changes:

  • Renamed module from github.com/traPtitech/DevOpsBot to github.com/traPtitech/sakura-DevOpsBot
  • Replaced ConoHa API configuration and authentication with Sakura VPS Bearer token authentication
  • Updated server operations (restart, hosts) to use Sakura VPS API endpoints and response structures
  • Removed unused dependencies (golang.org/x/sync) and simplified the restart command to remove the soft/hard restart type distinction

Reviewed changes

Copilot reviewed 17 out of 18 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
go.mod Updated module name and removed golang.org/x/sync dependency
go.sum Updated checksums for dependency changes
main.go Updated import path to use sakura-DevOpsBot module name
Dockerfile Updated build command to reference new module name
cmd/root.go Updated import paths
cmd/server.go Updated import paths
pkg/config/config.go Replaced ConoHa configuration with Sakura VPS configuration including origin, API path, and bearer token
pkg/server/token.go Commented out entire ConoHa token authentication implementation
pkg/server/server.go Added Japanese comment for command registration
pkg/server/restart.go Refactored to use Sakura VPS force-reboot endpoint, simplified to single server ID argument, removed soft/hard restart types
pkg/server/hosts.go Completely rewritten to match Sakura VPS API response structure, removed parallel goroutine fetching, updated logging format
pkg/bot/bot.go Updated import paths
pkg/bot/command.go Updated import paths
pkg/bot/help.go Updated import paths
pkg/bot/traq/bot.go Updated import paths, reordered imports
pkg/bot/traq/context.go Updated import paths, reordered imports
pkg/bot/slack/bot.go Updated import paths, reordered imports
pkg/bot/slack/context.go Updated import paths, reordered imports

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 48 to 56
logStr := fmt.Sprintf(`Request
- URL: %s
- RestartType: %s

Response
- Header: %+v
- Body: %s
- Status: %s (Expected: 202)
`, req.URL.String(), restartType, resp.Header, string(respBody), resp.Status)
`, req.URL.String(), resp.Header, string(respBody), resp.Status)
log.Println(logStr)
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logging the full response body and headers may expose sensitive information. Consider reviewing what information is included in the response and whether it's safe to log it. If the response contains any authentication tokens, server credentials, or other sensitive data, this could be a security concern.

Copilot uses AI. Check for mistakes.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@blancnoir256
Copy link
Collaborator Author

テスト確認できました。

@blancnoir256 blancnoir256 marked this pull request as ready for review January 19, 2026 14:51
@blancnoir256 blancnoir256 requested a review from cp-20 January 19, 2026 14:54
Copy link

@cp-20 cp-20 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

動作として動いてるなら良さそうで、仕様的なところ (+nits) でいくつかコメントしました 別々のコードでデプロイするにしろ、一緒のコードでデプロイするにしろ、適当にコメントを反映させてもらえると良いと思います

Comment on lines +22 to 25
serverID, err := strconv.Atoi(args[0])
if err != nil {
return fmt.Errorf("invalid arguments, server id must be an integer: %q", args[0])
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

仕様として Name も受け付けて内部で Name から ID への変換をやるとかすると使い勝手が良いかもだけど、仕様がやや複雑になるのでどっちもどっち

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

やりたいです。
やります。

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これはやらないの?

@coderabbitai
Copy link

coderabbitai bot commented Feb 25, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 890a4eb and b4dda62.

📒 Files selected for processing (1)
  • pkg/server/restart.go

📝 Walkthrough

Walkthrough

プロジェクトを sakura-DevOpsBot にリブランドし、ConoHa 統合を廃止して Sakura API へ移行。モジュール/インポート、設定スキーマ、サーバー API(ページング対応)および CI/リリース設定とビルド設定が更新された。

Changes

Cohort / File(s) Summary
CI / リリース設定
​.github/workflows/build.yml, ​.github/workflows/release.yml, .goreleaser.yml, Dockerfile
GitHub Actions の checkout/setup-go を v6 に更新。release.yml で REGISTRY/IMAGE_NAME/権限を追加・イメージタグ処理を registry ベースへ変更。goreleaser/Dockerfile のプロジェクト名や ldflags/ENV 表記を sakura-DevOpsBot に更新。
VCS / メタ
.gitignore
docker-compose.yaml を .gitignore に追加。
Go モジュール & エントリ
go.mod, main.go
module 宣言を github.com/traPtitech/sakura-DevOpsBot に変更。依存に github.com/mattn/go-runewidth を追加、いくつかの間接依存を更新。main の import パスを更新。
CLI コマンド / ルート
cmd/root.go, cmd/server.go
モジュールインポートと CLI 名/説明を DevOpsBot→sakura-DevOpsBot/Sakura に更新。
Bot コード(インポート更新)
pkg/bot/..., pkg/bot/slack/..., pkg/bot/traq/..., pkg/bot/*
内部インポートパスを新モジュールへ置換。Slack/Traq 周りで stringslog/slog などのインポート追加。help バナー表記を更新。
設定スキーマ
pkg/config/config.go
ServersConfig を Conoha 構造(Identity/Compute/Username/Password/TenantID)から Sakura 構造(Origin、ServersAPIURLPath、BearerToken)へ置換し、デフォルトキー値を更新。
サーバー操作(一覧 / 再起動 等)
pkg/server/hosts.go, pkg/server/restart.go, pkg/server/server.go, pkg/server/token.go
hosts.go: Conoha 単一取得から Sakura のページング API へ移行、レスポンス型を変更しページ走査・重複除去・整列テーブル出力を追加。restart.go: ID 引数厳格化、Bearer トークン利用で force-reboot エンドポイントに POST。token.go: Conoha トークン取得関数を削除。server.go: サブコマンド登録を hosts→info に変更。

Sequence Diagram(s)

sequenceDiagram
    participant CLI as "CLI"
    participant Config as "Config\n(pkg/config)"
    participant SakuraAPI as "Sakura API\n(HTTP)"
    participant Console as "Console\n(Table Printer)"

    rect rgba(60,120,255,0.5)
    CLI->>Config: 設定読み込み\n(servers.sakura.origin,\nserversAPIURLPath,bearerToken)
    end

    rect rgba(20,200,100,0.5)
    CLI->>SakuraAPI: GET /servers?per_page=...&page=1\nAuthorization: Bearer <token>
    SakuraAPI-->>CLI: 200 {results,count,next,...}
    CLI->>SakuraAPI: (繰返し) GET /servers?page=N
    SakuraAPI-->>CLI: 200 {results,next/previous}
    end

    rect rgba(200,120,20,0.5)
    CLI->>Console: 集約データを渡して表示\n(printAlignedTable)
    Console-->>CLI: 完了ログ (総件数)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Feat/sakura #1 — 同様のリブランドと ConoHa→Sakura 移行(モジュール名変更、hosts/restart の Sakura 対応、CI/Docker/goreleaser 更新)を行う PR。

Suggested reviewers

  • Kentaro1043
  • cp-20
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed プルリクエストのタイトル「Feat/sakura」は変更内容全体を簡潔に表現しており、プロジェクトをConoHaからさくらVPSに対応させるという主要な変更を明確に示しています。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/sakura

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
pkg/server/server.go (1)

1-1: ⚠️ Potential issue | 🟡 Minor

パッケージコメントが旧プロバイダー名のままです。

ConoHa への言及が残っており、Sakura への移行後も旧名称が参照されています。

🔧 修正案
-// Package server provides ConoHa server related operations
+// Package server provides Sakura server related operations
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/server/server.go` at line 1,
パッケージコメントが旧プロバイダー名「ConoHa」を参照したままなので、pkg/serverのパッケージコメント(ファイル冒頭の package server
コメント)を「Sakura」へ表記を差し替えし、説明文中のプロバイダー名や関連記述もSakuraに合わせて更新してください(パッケージコメントの文言をSakura向けに整えることで一貫性を保ちます)。
.github/workflows/release.yml (1)

38-40: ⚠️ Potential issue | 🟠 Major

image ジョブに packages: write を明示してください。

Lines 55-60 で GHCR への認証に ${{ secrets.GITHUB_TOKEN }} を使用し、Lines 62-71 でイメージをプッシュしていますが、ジョブレベルの permissions が指定されていません。リポジトリまたは組織のデフォルト権限設定が不十分な場合、プッシュに失敗する可能性があります。packages: write(および contents: read)をジョブに明示することで、環境依存のエラーを回避できます。

修正案
  image:
    name: Build Docker Image
    runs-on: ubuntu-latest
+    permissions:
+      contents: read
+      packages: write
    steps:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release.yml around lines 38 - 40, The "image" job (Build
Docker Image) lacks explicit job-level permissions needed for GHCR pushes; add a
permissions block on the image job that includes at minimum packages: write and
contents: read so the job can authenticate with the GITHUB_TOKEN used for login
and push (see the GHCR auth and docker push steps referenced in the diff).
Locate the image job definition (name: Build Docker Image / job id image) and
insert a permissions section with packages: write and contents: read to avoid
failures when repository/org default permissions are restricted.
pkg/server/restart.go (1)

44-45: ⚠️ Potential issue | 🟠 Major

外部 API 呼び出しにタイムアウトが設定されていません。

Line 44 の http.DefaultClient はタイムアウト未設定のため、ネットワーク障害時にサーバー再起動コマンドが無限にハングする可能性があります。明示的な Timeout を持つカスタム http.Client を使用してください。

修正案
 import (
 	"fmt"
 	"log"
 	"net/http"
 	"strconv"
 	"strings"
+	"time"
@@
-	resp, err := http.DefaultClient.Do(req)
+	client := &http.Client{Timeout: 15 * time.Second}
+	resp, err := client.Do(req)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/server/restart.go` around lines 44 - 45, http.DefaultClient.Do(req) has
no timeout and can hang; replace it by creating and using a custom *http.Client
with a sensible Timeout (e.g., time.Duration) and call client.Do(req) instead of
http.DefaultClient.Do(req); update the code around where resp, err :=
http.DefaultClient.Do(req) is used (in restart.go) to construct the client (and
optionally use a request context with timeout) and use client.Do(req) so the
external restart API cannot hang indefinitely.
🧹 Nitpick comments (4)
.gitignore (1)

10-10: docker-compose.yaml の ignore は運用意図を再確認してください。

ルートの docker-compose.yaml を無条件で除外すると、開発/検証用の共通定義を共有できなくなる可能性があります。ローカル差分や機密値回避が目的なら、docker-compose.override.yaml.env の除外に寄せる方が一般的です。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.gitignore at line 10, ルートの "docker-compose.yaml" を無条件で .gitignore
に追加しているため共有すべき開発/検証用定義が除外されてしまう問題があります。該当の ignore
行を削除してリポジトリで基本定義を保持し、機密やローカル差分は "docker-compose.override.yaml" や
"docker-compose.local.yaml"、および ".env" を個別に .gitignore に追加して運用するようにしてください(または
".env.example" をリポジトリに残して使い方を README に明記してください)。
pkg/server/server.go (1)

18-18: 型名 hostsCommand とコマンドキー "info" が一致していません。

hostsCommand という型名は旧来の "hosts" コマンドに由来しており、新しいキー "info" と意味的に対応していません。将来の保守性のために型名を infoCommand に改名することを検討してください。

♻️ 修正案(hostsCommand → infoCommand)

server.go:

-	cmd.Commands["info"] = &hostsCommand{}
+	cmd.Commands["info"] = &infoCommand{}

hostsCommand を定義しているファイル(例: hosts.go)内でも型名を変更:

-type hostsCommand struct { ... }
-func (c *hostsCommand) Execute(...) { ... }
+type infoCommand struct { ... }
+func (c *infoCommand) Execute(...) { ... }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/server/server.go` at line 18, The symbol mapping cmd.Commands["info"]
currently instantiates a type named hostsCommand which is semantically
mismatched; rename the type hostsCommand to infoCommand (and any
constructor/receiver method names) in its defining file and update all
references (methods, receivers, interface implementations) to use infoCommand,
then update the registration in server.go to construct &infoCommand{} instead of
&hostsCommand{} so cmd.Commands["info"], the type name, and all usages are
consistent.
cmd/root.go (1)

42-42: コメント内の旧プロジェクト名が残っています。

コメントアウトされたフラグのデフォルトパスが旧プロジェクト名のままです。将来有効化された際に混乱を招く可能性があります。

🔧 修正案
-	// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.DevOpsBot.yaml)")
+	// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.sakura-DevOpsBot.yaml)")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/root.go` at line 42, コメントアウトされたフラグのデフォルトパスに旧プロジェクト名 ".DevOpsBot.yaml"
が残っているため、将来有効化されたときに誤解を招きます — 更新対象は rootCmd.PersistentFlags().StringVar 呼び出しと
cfgFile 変数の説明文字列で、コメント内のデフォルトパスを現在のプロジェクト名(または汎用的な "$HOME/.config.yaml"
など)に置き換えるか、デフォルト表記自体を削除して将来の混乱を防いでください。
pkg/server/restart.go (1)

31-33: 設定値は書き換えず、ローカル変数で URL パスを正規化してください。

Line 31-33 でグローバル設定を更新しているため、副作用が残ります。Line 37 で使う値だけローカルで組み立てる方が安全です。

修正案
- if !strings.HasPrefix(config.C.Servers.Sakura.ServersAPIURLPath, "/") {
-   config.C.Servers.Sakura.ServersAPIURLPath = "/" + config.C.Servers.Sakura.ServersAPIURLPath
- }
+ apiPath := config.C.Servers.Sakura.ServersAPIURLPath
+ if !strings.HasPrefix(apiPath, "/") {
+   apiPath = "/" + apiPath
+ }

  req, err := sling.New().
    Base(config.C.Servers.Sakura.Origin).
-   Post(fmt.Sprintf("%s/%d/force-reboot", config.C.Servers.Sakura.ServersAPIURLPath, serverID)).
+   Post(fmt.Sprintf("%s/%d/force-reboot", apiPath, serverID)).

Also applies to: 37-37

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/server/restart.go` around lines 31 - 33, Don't mutate the global
config.C.Servers.Sakura.ServersAPIURLPath; instead create a local variable
(e.g., normalizedPath) that ensures it begins with "/" and use that local
variable when building the URL later (the usage at the code that currently reads
the value on line 37). Update the code around the adjustment to compute
normalizedPath from config.C.Servers.Sakura.ServersAPIURLPath without assigning
back to config, and replace subsequent uses (the URL assembly that references
ServersAPIURLPath) to use normalizedPath.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/release.yml:
- Around line 17-18: goreleaser ジョブで現在設定されている permissions の "contents: read" は
GitHub リリースの作成/更新を行う GoReleaser 実行時に不足するため、workflow の goreleaser ジョブの
permissions ブロックで "contents: write" に変更して書き込み権限を付与してください(参照箇所: goreleaser ジョブの
permissions 定義)。

In `@pkg/server/hosts.go`:
- Around line 101-103: The code trims config.C.Servers.Sakura.BearerToken only
for the empty check but uses the original value when sending the Authorization
header, which can cause auth failures if the configured token has surrounding
whitespace; normalize the token once (e.g., token :=
strings.TrimSpace(config.C.Servers.Sakura.BearerToken) or assign the trimmed
value back to config.C.Servers.Sakura.BearerToken) and use that trimmed value
both for the empty check and when setting the request header (also apply the
same change at the other occurrence mentioned around lines 118-119).
- Around line 125-126: The call uses http.DefaultClient.Do(req) without a
timeout which can hang; replace it with an http.Client that has a reasonable
Timeout (or perform the request with a context having a deadline) and use that
client to execute the request instead of http.DefaultClient. Specifically,
change the code that builds and sends the request (the variable req and the call
http.DefaultClient.Do(req) in hosts.go) to either create a client like
http.Client{Timeout: time.Second * <N>} and call client.Do(req) or wrap req with
a context via context.WithTimeout and use http.NewRequestWithContext so the
request is cancelled after the timeout. Ensure you import time/context if needed
and choose an appropriate timeout value.

In `@pkg/server/restart.go`:
- Around line 22-25: strconv.Atoi の後に負数や 0 を通してしまう問題があるため、serverID
の下限チェックを追加してください: strconv.Atoi でパースした直後(serverID 変数がある箇所)に serverID <= 0
を判定して早期に fmt.Errorf で「server id must be a positive
integer」等のわかりやすいエラーメッセージを返すように修正してください(参照: serverID, strconv.Atoi)。

---

Outside diff comments:
In @.github/workflows/release.yml:
- Around line 38-40: The "image" job (Build Docker Image) lacks explicit
job-level permissions needed for GHCR pushes; add a permissions block on the
image job that includes at minimum packages: write and contents: read so the job
can authenticate with the GITHUB_TOKEN used for login and push (see the GHCR
auth and docker push steps referenced in the diff). Locate the image job
definition (name: Build Docker Image / job id image) and insert a permissions
section with packages: write and contents: read to avoid failures when
repository/org default permissions are restricted.

In `@pkg/server/restart.go`:
- Around line 44-45: http.DefaultClient.Do(req) has no timeout and can hang;
replace it by creating and using a custom *http.Client with a sensible Timeout
(e.g., time.Duration) and call client.Do(req) instead of
http.DefaultClient.Do(req); update the code around where resp, err :=
http.DefaultClient.Do(req) is used (in restart.go) to construct the client (and
optionally use a request context with timeout) and use client.Do(req) so the
external restart API cannot hang indefinitely.

In `@pkg/server/server.go`:
- Line 1: パッケージコメントが旧プロバイダー名「ConoHa」を参照したままなので、pkg/serverのパッケージコメント(ファイル冒頭の
package server
コメント)を「Sakura」へ表記を差し替えし、説明文中のプロバイダー名や関連記述もSakuraに合わせて更新してください(パッケージコメントの文言をSakura向けに整えることで一貫性を保ちます)。

---

Nitpick comments:
In @.gitignore:
- Line 10: ルートの "docker-compose.yaml" を無条件で .gitignore
に追加しているため共有すべき開発/検証用定義が除外されてしまう問題があります。該当の ignore
行を削除してリポジトリで基本定義を保持し、機密やローカル差分は "docker-compose.override.yaml" や
"docker-compose.local.yaml"、および ".env" を個別に .gitignore に追加して運用するようにしてください(または
".env.example" をリポジトリに残して使い方を README に明記してください)。

In `@cmd/root.go`:
- Line 42: コメントアウトされたフラグのデフォルトパスに旧プロジェクト名 ".DevOpsBot.yaml"
が残っているため、将来有効化されたときに誤解を招きます — 更新対象は rootCmd.PersistentFlags().StringVar 呼び出しと
cfgFile 変数の説明文字列で、コメント内のデフォルトパスを現在のプロジェクト名(または汎用的な "$HOME/.config.yaml"
など)に置き換えるか、デフォルト表記自体を削除して将来の混乱を防いでください。

In `@pkg/server/restart.go`:
- Around line 31-33: Don't mutate the global
config.C.Servers.Sakura.ServersAPIURLPath; instead create a local variable
(e.g., normalizedPath) that ensures it begins with "/" and use that local
variable when building the URL later (the usage at the code that currently reads
the value on line 37). Update the code around the adjustment to compute
normalizedPath from config.C.Servers.Sakura.ServersAPIURLPath without assigning
back to config, and replace subsequent uses (the URL assembly that references
ServersAPIURLPath) to use normalizedPath.

In `@pkg/server/server.go`:
- Line 18: The symbol mapping cmd.Commands["info"] currently instantiates a type
named hostsCommand which is semantically mismatched; rename the type
hostsCommand to infoCommand (and any constructor/receiver method names) in its
defining file and update all references (methods, receivers, interface
implementations) to use infoCommand, then update the registration in server.go
to construct &infoCommand{} instead of &hostsCommand{} so cmd.Commands["info"],
the type name, and all usages are consistent.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a689e6f and 9bdd06e.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (21)
  • .github/workflows/build.yml
  • .github/workflows/release.yml
  • .gitignore
  • .goreleaser.yml
  • Dockerfile
  • cmd/root.go
  • cmd/server.go
  • go.mod
  • main.go
  • pkg/bot/bot.go
  • pkg/bot/command.go
  • pkg/bot/help.go
  • pkg/bot/slack/bot.go
  • pkg/bot/slack/context.go
  • pkg/bot/traq/bot.go
  • pkg/bot/traq/context.go
  • pkg/config/config.go
  • pkg/server/hosts.go
  • pkg/server/restart.go
  • pkg/server/server.go
  • pkg/server/token.go
💤 Files with no reviewable changes (1)
  • pkg/server/token.go

blancnoir256 and others added 2 commits February 25, 2026 22:48
権限不足だった

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
サーバーID加減チェック

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/release.yml (1)

38-60: ⚠️ Potential issue | 🔴 Critical

image ジョブに packages: write 権限が不足しています。

GHCR へ Docker イメージを push するには GITHUB_TOKENpackages: write が必要です。このジョブには permissions ブロックがなく、ワークフローにも top-level の permissions 定義がないため、リポジトリのデフォルト権限に依存します。GitHub の新規リポジトリデフォルトは read-only であるため、push 時に 403 エラーで失敗します。

修正案
  image:
    name: Build Docker Image
    runs-on: ubuntu-latest
+   permissions:
+     packages: write
    steps:

github.repository_owner の大文字小文字問題

github.repository_ownertraPtitech のようにオーナー名の原表記を返します。OCI イメージ参照は小文字でなければならないため、タグ生成時(70-71 行目)で大文字を含む場合、"invalid reference format" エラーで push が失敗します。

小文字変換の例
      - name: Extract version from tag
        id: version
        run: echo "tag=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT

+     - name: Lowercase owner
+       id: owner
+       run: echo "name=$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT
+
      - name: Set up QEMU

その後、タグ参照を以下のように置き換えます:

          tags: |
-           ${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:latest
-           ${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.tag }}
+           ${{ env.REGISTRY }}/${{ steps.owner.outputs.name }}/${{ env.IMAGE_NAME }}:latest
+           ${{ env.REGISTRY }}/${{ steps.owner.outputs.name }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.tag }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release.yml around lines 38 - 60, The image job is missing
write permission for packages and uses the raw github.repository_owner which can
contain uppercase letters; add a permissions block granting packages: write for
the image job (so the GITHUB_TOKEN used in the docker/login-action can push to
GHCR) and update any tag/registry string generation that references
github.repository_owner to use the toLower(...) expression (e.g.
toLower(github.repository_owner)) so OCI image names are all lowercase; locate
these changes around the job named "image", the step id "version" that writes
the tag, and the docker/login-action step that uses ${{ github.repository_owner
}} / ${{ env.REGISTRY }}.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In @.github/workflows/release.yml:
- Around line 38-60: The image job is missing write permission for packages and
uses the raw github.repository_owner which can contain uppercase letters; add a
permissions block granting packages: write for the image job (so the
GITHUB_TOKEN used in the docker/login-action can push to GHCR) and update any
tag/registry string generation that references github.repository_owner to use
the toLower(...) expression (e.g. toLower(github.repository_owner)) so OCI image
names are all lowercase; locate these changes around the job named "image", the
step id "version" that writes the tag, and the docker/login-action step that
uses ${{ github.repository_owner }} / ${{ env.REGISTRY }}.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9bdd06e and df66f92.

📒 Files selected for processing (1)
  • .github/workflows/release.yml

@blancnoir256 blancnoir256 requested a review from cp-20 February 25, 2026 14:08
Copy link

@cp-20 cp-20 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

動いてるなら良いのでは

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/server/restart.go (1)

52-52: ⚠️ Potential issue | 🟠 Major

外部API呼び出し2箇所ともタイムアウト未設定です

Line 52 と Line 92 の http.DefaultClient.Do(req) は、API停止時に処理がハングするリスクがあります。共通のタイムアウト付きクライアントを使ってください。

修正例
 import (
 	"encoding/json"
 	"fmt"
 	"io"
 	"log"
 	"net/http"
 	"sort"
 	"strconv"
 	"strings"
+	"time"
@@
-	serverID, serverName, err := resolveServerID(token, serverKey)
+	client := &http.Client{Timeout: 15 * time.Second}
+	serverID, serverName, err := resolveServerID(client, token, serverKey)
@@
-	resp, err := http.DefaultClient.Do(req)
+	resp, err := client.Do(req)
@@
-func resolveServerID(token string, serverKey string) (int, string, error) {
+func resolveServerID(client *http.Client, token string, serverKey string) (int, string, error) {
@@
-		resp, err := http.DefaultClient.Do(req)
+		resp, err := client.Do(req)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/server/restart.go` at line 52, The two calls using
http.DefaultClient.Do(req) (the occurrences of "resp, err :=
http.DefaultClient.Do(req)") lack timeouts and can hang; create and use a shared
http.Client with a Timeout (e.g., var httpClient = &http.Client{Timeout:
10*time.Second}) or use request contexts with timeouts (req =
req.WithContext(ctxWithTimeout)) and replace both DefaultClient.Do usages with
httpClient.Do(req) (and add the necessary time import), ensuring both call sites
in restart.go use the same timeout-enabled client.
♻️ Duplicate comments (2)
pkg/server/hosts.go (1)

126-126: ⚠️ Potential issue | 🟠 Major

HTTPリクエストが無期限待機になり得ます

Line 126 の http.DefaultClient.Do(req) はタイムアウト未設定のため、外部API遅延時にコマンドが戻らなくなる可能性があります。タイムアウト付き http.Clientcontext.WithTimeout を使ってください。

修正例
 import (
 	"encoding/json"
 	"fmt"
 	"io"
 	"log"
 	"net/http"
 	"os"
 	"sort"
 	"strings"
+	"time"
@@
-	for {
+	client := &http.Client{Timeout: 15 * time.Second}
+	for {
@@
-		resp, err := http.DefaultClient.Do(req)
+		resp, err := client.Do(req)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/server/hosts.go` at line 126, The http.DefaultClient.Do(req) call in
hosts.go can hang because no timeout is set; replace it by creating a
timeout-aware context or client: use context.WithTimeout(...) to derive a ctx
and attach it to the request (via http.NewRequestWithContext or req =
req.WithContext(ctx)) or instantiate an http.Client with a non-zero Timeout and
call that client's Do; ensure you cancel the context (or use defer cancel()),
use the same request variable (req) and keep the resp/err handling (and
resp.Body close) intact so the request won't block indefinitely.
.github/workflows/release.yml (1)

17-18: contents: write が正しく設定されています。

以前のレビューで指摘された問題(contents: read では GoReleaser がリリースを作成できない)は、contents: write に修正されています。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release.yml around lines 17 - 18, The permissions setting
in the workflow currently uses "permissions: contents: write" which fixes the
earlier issue where GoReleaser needed write access; ensure the "permissions"
section keeps "contents: write" (not "contents: read") so GoReleaser can create
releases and do not revert this change in the "permissions" block of the release
workflow.
🧹 Nitpick comments (1)
.github/workflows/release.yml (1)

64-66: ログインのユーザー名と image タグのオーナー名の大文字小文字が不統一です。

Line 65 の usernamegithub.repository_owner(元のケース)を使用していますが、Line 76-77 の image タグでは steps.owner.outputs.name(小文字化済み)を使っています。GHCR のログイン自体はケースインセンシティブのため動作上の問題はありませんが、統一性のために小文字化済みの値を使用することを検討してください。

♻️ 修正案: username を小文字化済みの値に統一
      - name: Login to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
-          username: ${{ github.repository_owner }}
+          username: ${{ steps.owner.outputs.name }}
          password: ${{ secrets.GITHUB_TOKEN }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release.yml around lines 64 - 66, The username value is
inconsistent with the image tag owner casing — change the login username to use
the already lowercased owner value to match the image tags by replacing the
current username reference (github.repository_owner) with the lowercased output
(steps.owner.outputs.name) or apply toLower(github.repository_owner); update the
username field so it uses steps.owner.outputs.name (or the toLower variant) to
keep casing consistent with the image tag usage.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/release.yml:
- Around line 51-53: The step "Lowercase owner" (id: owner) exposes a shell
injection risk by expanding ${{ github.repository_owner }} directly in the run
command; instead set github.repository_owner into an environment variable for
that step (e.g., OWNER) and reference the environment variable inside the run so
the workflow expression is not directly expanded into the shell, then write the
lowercased value to GITHUB_OUTPUT (preserve use of tr '[:upper:]' '[:lower:]' on
the env var and continue appending to $GITHUB_OUTPUT).

In `@pkg/server/restart.go`:
- Around line 68-74: resolveServerID currently returns int causing downcast from
int64 elsewhere and potential overflow on 32-bit platforms; change
resolveServerID's return type from int to int64, replace strconv.Atoi with
strconv.ParseInt (base 10, 64-bit) to parse into an int64, validate positivity
against 0, and return the parsed int64 along with the same string/error
behavior; update all callers that expect int (e.g., places converting from int64
at call sites) to accept the new int64 return type so no narrowing cast occurs.

---

Outside diff comments:
In `@pkg/server/restart.go`:
- Line 52: The two calls using http.DefaultClient.Do(req) (the occurrences of
"resp, err := http.DefaultClient.Do(req)") lack timeouts and can hang; create
and use a shared http.Client with a Timeout (e.g., var httpClient =
&http.Client{Timeout: 10*time.Second}) or use request contexts with timeouts
(req = req.WithContext(ctxWithTimeout)) and replace both DefaultClient.Do usages
with httpClient.Do(req) (and add the necessary time import), ensuring both call
sites in restart.go use the same timeout-enabled client.

---

Duplicate comments:
In @.github/workflows/release.yml:
- Around line 17-18: The permissions setting in the workflow currently uses
"permissions: contents: write" which fixes the earlier issue where GoReleaser
needed write access; ensure the "permissions" section keeps "contents: write"
(not "contents: read") so GoReleaser can create releases and do not revert this
change in the "permissions" block of the release workflow.

In `@pkg/server/hosts.go`:
- Line 126: The http.DefaultClient.Do(req) call in hosts.go can hang because no
timeout is set; replace it by creating a timeout-aware context or client: use
context.WithTimeout(...) to derive a ctx and attach it to the request (via
http.NewRequestWithContext or req = req.WithContext(ctx)) or instantiate an
http.Client with a non-zero Timeout and call that client's Do; ensure you cancel
the context (or use defer cancel()), use the same request variable (req) and
keep the resp/err handling (and resp.Body close) intact so the request won't
block indefinitely.

---

Nitpick comments:
In @.github/workflows/release.yml:
- Around line 64-66: The username value is inconsistent with the image tag owner
casing — change the login username to use the already lowercased owner value to
match the image tags by replacing the current username reference
(github.repository_owner) with the lowercased output (steps.owner.outputs.name)
or apply toLower(github.repository_owner); update the username field so it uses
steps.owner.outputs.name (or the toLower variant) to keep casing consistent with
the image tag usage.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between df66f92 and 890a4eb.

📒 Files selected for processing (3)
  • .github/workflows/release.yml
  • pkg/server/hosts.go
  • pkg/server/restart.go

Comment on lines +51 to +53
- name: Lowercase owner
id: owner
run: echo "name=$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

スクリプトインジェクションリスク: ${{ github.repository_owner }} を直接シェルコマンドに展開しています。

GitHub Actions の式展開はシェル実行前に行われるため、シングルクォートでも保護されません。github.repository_owner は英数字とハイフンのみに制限されているため実害は低いですが、環境変数経由に統一することが安全なパターンです。

🛡️ 修正案: 環境変数経由でインジェクションリスクを回避
      - name: Lowercase owner
        id: owner
-        run: echo "name=$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT
+        run: echo "name=$(echo "$OWNER" | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT
+        env:
+          OWNER: ${{ github.repository_owner }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release.yml around lines 51 - 53, The step "Lowercase
owner" (id: owner) exposes a shell injection risk by expanding ${{
github.repository_owner }} directly in the run command; instead set
github.repository_owner into an environment variable for that step (e.g., OWNER)
and reference the environment variable inside the run so the workflow expression
is not directly expanded into the shell, then write the lowercased value to
GITHUB_OUTPUT (preserve use of tr '[:upper:]' '[:lower:]' on the env var and
continue appending to $GITHUB_OUTPUT).

Comment on lines 68 to 74
func resolveServerID(token string, serverKey string) (int, string, error) {
if serverID, err := strconv.Atoi(serverKey); err == nil {
if serverID <= 0 {
return 0, "", fmt.Errorf("invalid arguments, server id must be a positive integer: %q", serverKey)
}
return serverID, "(id specified)", nil
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, let's find and examine the restart.go file
find . -name "restart.go" -path "*/server/*" | head -5

Repository: traPtitech/sakura-DevOpsBot

Length of output: 94


🏁 Script executed:

#!/bin/bash
# Read the file to see the full context around the mentioned lines
wc -l pkg/server/restart.go

Repository: traPtitech/sakura-DevOpsBot

Length of output: 96


🏁 Script executed:

#!/bin/bash
# Check the resolveServerID function and surrounding code
sed -n '60,160p' pkg/server/restart.go

Repository: traPtitech/sakura-DevOpsBot

Length of output: 2936


🏁 Script executed:

#!/bin/bash
# Search for type definitions that might contain ID fields
rg "type.*struct" pkg/server/ -A 10 | grep -A 10 "ID"

Repository: traPtitech/sakura-DevOpsBot

Length of output: 2233


🏁 Script executed:

#!/bin/bash
# Search for usages of resolveServerID
rg "resolveServerID" pkg/server/

Repository: traPtitech/sakura-DevOpsBot

Length of output: 254


🏁 Script executed:

#!/bin/bash
# Check the requestServerRestart function to see how resolveServerID is used
sed -n '40,67p' pkg/server/restart.go

Repository: traPtitech/sakura-DevOpsBot

Length of output: 793


intへの縮小変換でIDオーバーフロー余地があります

APIのサーバーID は int64 ですが、resolveServerID 関数は int を返しており、ライン 141 と 154 で int64 から int への縮小変換が発生しています。32ビット環境では int64 値が 2^31 を超える場合にオーバーフローする可能性があるため、resolveServerID の戻り値型を int64 に統一し、strconv.Atoistrconv.ParseInt に変更することを推奨します。

修正例
-func resolveServerID(token string, serverKey string) (int, string, error) {
-	if serverID, err := strconv.Atoi(serverKey); err == nil {
+func resolveServerID(token string, serverKey string) (int64, string, error) {
+	if serverID, err := strconv.ParseInt(serverKey, 10, 64); err == nil {
 		if serverID <= 0 {
 			return 0, "", fmt.Errorf("invalid arguments, server id must be a positive integer: %q", serverKey)
 		}
 		return serverID, "(id specified)", nil
 	}
@@
-		return int(exactMatches[0].ID), exactMatches[0].Name, nil
+		return exactMatches[0].ID, exactMatches[0].Name, nil
@@
-		return int(matched[0].ID), matched[0].Name, nil
+		return matched[0].ID, matched[0].Name, nil
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func resolveServerID(token string, serverKey string) (int, string, error) {
if serverID, err := strconv.Atoi(serverKey); err == nil {
if serverID <= 0 {
return 0, "", fmt.Errorf("invalid arguments, server id must be a positive integer: %q", serverKey)
}
return serverID, "(id specified)", nil
}
func resolveServerID(token string, serverKey string) (int64, string, error) {
if serverID, err := strconv.ParseInt(serverKey, 10, 64); err == nil {
if serverID <= 0 {
return 0, "", fmt.Errorf("invalid arguments, server id must be a positive integer: %q", serverKey)
}
return serverID, "(id specified)", nil
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/server/restart.go` around lines 68 - 74, resolveServerID currently
returns int causing downcast from int64 elsewhere and potential overflow on
32-bit platforms; change resolveServerID's return type from int to int64,
replace strconv.Atoi with strconv.ParseInt (base 10, 64-bit) to parse into an
int64, validate positivity against 0, and return the parsed int64 along with the
same string/error behavior; update all callers that expect int (e.g., places
converting from int64 at call sites) to accept the new int64 return type so no
narrowing cast occurs.

@blancnoir256 blancnoir256 merged commit cc16d83 into master Feb 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants