Split phrase list out into community-editable phrases.yaml
Some checks are pending
CI / test (push) Waiting to run
Some checks are pending
CI / test (push) Waiting to run
config.yaml previously mixed secrets with the phrase list, forcing private-only edits. Now config supports phrases_file, which is loaded and merged with any private phrases: entries, so the repo-tracked phrases.yaml can be extended via PR while per-deployment additions stay private. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
6e023d1081
commit
f411886d88
5 changed files with 115 additions and 25 deletions
|
|
@ -27,7 +27,9 @@ Forgejo上にbot専用アカウント(例: `yaju-keisatsu-bot`)を作り、そ
|
||||||
cp config.example.yaml config.yaml
|
cp config.example.yaml config.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
`config.yaml` を編集し、`forgejo.base_url` / `forgejo.token` / `webhook_secret` / `bot.username` を埋める。`phrases` は同梱の一部だけなので、必要に応じて自分の `config.yaml` に自由に追加してよい(このリストは本体の意図的にライトな内容にとどめてあります)。
|
`config.yaml` を編集し、`forgejo.base_url` / `forgejo.token` / `webhook_secret` / `bot.username` を埋める。
|
||||||
|
|
||||||
|
語録リストは [phrases.yaml](./phrases.yaml) にコミュニティ管理のものが同梱されており、`config.yaml` の `phrases_file: ./phrases.yaml` で読み込む(デフォルトで設定済み)。追加・削除の提案はPRで歓迎。自分の運用だけで使いたい追加語録があれば、それとは別に `config.yaml` の `phrases:` に書けば `phrases_file` の内容とマージされる。
|
||||||
|
|
||||||
`config.yaml` は秘密情報を含むため絶対にコミットしないこと(`.gitignore` 済み)。
|
`config.yaml` は秘密情報を含むため絶対にコミットしないこと(`.gitignore` 済み)。
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# goroku-keisatsu の設定ファイル例。
|
# yaju-is-always-watching-you の設定ファイル例。
|
||||||
# コピーして config.yaml として使う。秘密情報が入るので config.yaml は絶対にコミットしないこと。
|
# コピーして config.yaml として使う。秘密情報が入るので config.yaml は絶対にコミットしないこと。
|
||||||
|
|
||||||
listen_addr: ":8080"
|
listen_addr: ":8080"
|
||||||
|
|
@ -11,13 +11,13 @@ forgejo:
|
||||||
# 自ホストのForgejoのベースURL
|
# 自ホストのForgejoのベースURL
|
||||||
base_url: "https://git.example.dev"
|
base_url: "https://git.example.dev"
|
||||||
# bot専用アカウントで発行したAPIトークン(Settings > Applications)。
|
# bot専用アカウントで発行したAPIトークン(Settings > Applications)。
|
||||||
# 権限は対象リポジトリへの read/write + issue + pull-request で十分。
|
# スコープは repository(write) + issue(write) で十分。
|
||||||
token: "change-me"
|
token: "change-me"
|
||||||
|
|
||||||
bot:
|
bot:
|
||||||
# 上のtokenを発行したbotアカウントのユーザー名。
|
# 上のtokenを発行したbotアカウントのユーザー名。
|
||||||
# このアカウント自身の発言/pushには反応しない(無限ループ防止)。
|
# このアカウント自身の発言/pushには反応しない(無限ループ防止)。
|
||||||
username: "goroku-keisatsu-bot"
|
username: "yaju-keisatsu-bot"
|
||||||
# 是正PR用に作成するブランチの接頭辞。
|
# 是正PR用に作成するブランチの接頭辞。
|
||||||
branch_prefix: "bot/goroku-fix/"
|
branch_prefix: "bot/goroku-fix/"
|
||||||
# push時にスキャンする対象拡張子。空にすると全ファイルを対象にする。
|
# push時にスキャンする対象拡張子。空にすると全ファイルを対象にする。
|
||||||
|
|
@ -29,18 +29,10 @@ bot:
|
||||||
- ".js"
|
- ".js"
|
||||||
- ".ts"
|
- ".ts"
|
||||||
|
|
||||||
# 検知する語録リスト。有名どころの一部だけを同梱しているので、
|
# phrases_file: リポジトリ同梱のコミュニティ管理語録リスト(phrases.yaml)を読む。
|
||||||
# 必要に応じて自分のconfig.yamlで自由に追加・削除すること。
|
# 相対パスはこのconfig.yamlからの相対で解決される。
|
||||||
phrases:
|
phrases_file: "./phrases.yaml"
|
||||||
- "しょうもな"
|
|
||||||
- "たまげたなあ"
|
# phrases: 自分の運用だけで使いたい追加語録があればここに書く(phrases_fileの内容とマージされる)。
|
||||||
- "変態糞土方"
|
# 空のままでもOK。
|
||||||
- "絶対に許さない"
|
phrases: []
|
||||||
- "24時間戦えますか"
|
|
||||||
- "本当に感謝しております"
|
|
||||||
- "巨大感謝"
|
|
||||||
- "覚悟はいいか?俺はできてる"
|
|
||||||
- "もうだめだ"
|
|
||||||
- "ンアッー"
|
|
||||||
- "ファッ!?"
|
|
||||||
- "24時間タタカエマスカ"
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ package config
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
@ -13,6 +14,14 @@ type Config struct {
|
||||||
WebhookSecret string `yaml:"webhook_secret"`
|
WebhookSecret string `yaml:"webhook_secret"`
|
||||||
Forgejo Forgejo `yaml:"forgejo"`
|
Forgejo Forgejo `yaml:"forgejo"`
|
||||||
Bot Bot `yaml:"bot"`
|
Bot Bot `yaml:"bot"`
|
||||||
|
// PhrasesFile, if set, is loaded and merged with Phrases. It lets a
|
||||||
|
// deployment point at the repo's community-maintained phrases.yaml while
|
||||||
|
// still keeping any private additions inline below.
|
||||||
|
PhrasesFile string `yaml:"phrases_file"`
|
||||||
|
Phrases []string `yaml:"phrases"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type phrasesFile struct {
|
||||||
Phrases []string `yaml:"phrases"`
|
Phrases []string `yaml:"phrases"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -48,6 +57,22 @@ func Load(path string) (*Config, error) {
|
||||||
return nil, fmt.Errorf("parse config: %w", err)
|
return nil, fmt.Errorf("parse config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfg.PhrasesFile != "" {
|
||||||
|
fp := cfg.PhrasesFile
|
||||||
|
if !filepath.IsAbs(fp) {
|
||||||
|
fp = filepath.Join(filepath.Dir(path), fp)
|
||||||
|
}
|
||||||
|
data, err := os.ReadFile(fp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("read phrases_file: %w", err)
|
||||||
|
}
|
||||||
|
var pf phrasesFile
|
||||||
|
if err := yaml.Unmarshal(data, &pf); err != nil {
|
||||||
|
return nil, fmt.Errorf("parse phrases_file: %w", err)
|
||||||
|
}
|
||||||
|
cfg.Phrases = append(pf.Phrases, cfg.Phrases...)
|
||||||
|
}
|
||||||
|
|
||||||
if cfg.Forgejo.BaseURL == "" {
|
if cfg.Forgejo.BaseURL == "" {
|
||||||
return nil, fmt.Errorf("forgejo.base_url is required")
|
return nil, fmt.Errorf("forgejo.base_url is required")
|
||||||
}
|
}
|
||||||
|
|
@ -58,7 +83,7 @@ func Load(path string) (*Config, error) {
|
||||||
return nil, fmt.Errorf("webhook_secret is required")
|
return nil, fmt.Errorf("webhook_secret is required")
|
||||||
}
|
}
|
||||||
if len(cfg.Phrases) == 0 {
|
if len(cfg.Phrases) == 0 {
|
||||||
return nil, fmt.Errorf("phrases list is empty")
|
return nil, fmt.Errorf("phrases list is empty (set phrases and/or phrases_file)")
|
||||||
}
|
}
|
||||||
|
|
||||||
return cfg, nil
|
return cfg, nil
|
||||||
|
|
|
||||||
55
internal/config/config_test.go
Normal file
55
internal/config/config_test.go
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func writeFile(t *testing.T, path, content string) {
|
||||||
|
t.Helper()
|
||||||
|
if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
|
||||||
|
t.Fatalf("write %s: %v", path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoad_PhrasesFileMergesWithInline(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
|
||||||
|
writeFile(t, filepath.Join(dir, "phrases.yaml"), "phrases:\n - しょうもな\n - たまげたなあ\n")
|
||||||
|
writeFile(t, filepath.Join(dir, "config.yaml"), `
|
||||||
|
listen_addr: ":8080"
|
||||||
|
webhook_secret: "s"
|
||||||
|
forgejo:
|
||||||
|
base_url: "http://forgejo:3000"
|
||||||
|
token: "t"
|
||||||
|
phrases_file: "./phrases.yaml"
|
||||||
|
phrases:
|
||||||
|
- 独自の追加語録
|
||||||
|
`)
|
||||||
|
|
||||||
|
cfg, err := Load(filepath.Join(dir, "config.yaml"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Load() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := []string{"しょうもな", "たまげたなあ", "独自の追加語録"}
|
||||||
|
if !reflect.DeepEqual(cfg.Phrases, want) {
|
||||||
|
t.Fatalf("Phrases = %v, want %v", cfg.Phrases, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoad_MissingPhrasesIsError(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
writeFile(t, filepath.Join(dir, "config.yaml"), `
|
||||||
|
webhook_secret: "s"
|
||||||
|
forgejo:
|
||||||
|
base_url: "http://forgejo:3000"
|
||||||
|
token: "t"
|
||||||
|
`)
|
||||||
|
|
||||||
|
if _, err := Load(filepath.Join(dir, "config.yaml")); err == nil {
|
||||||
|
t.Fatal("Load() error = nil, want error for empty phrases")
|
||||||
|
}
|
||||||
|
}
|
||||||
16
phrases.yaml
Normal file
16
phrases.yaml
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
# コミュニティ管理の語録リスト(誰でもPRで追加・削除を提案してOK)。
|
||||||
|
# 個人の運用固有の追加語録は、この場所ではなく各自の config.yaml の
|
||||||
|
# `phrases:` に書くこと(config.yaml はコミットされないので分離される)。
|
||||||
|
phrases:
|
||||||
|
- "しょうもな"
|
||||||
|
- "たまげたなあ"
|
||||||
|
- "変態糞土方"
|
||||||
|
- "絶対に許さない"
|
||||||
|
- "24時間戦えますか"
|
||||||
|
- "本当に感謝しております"
|
||||||
|
- "巨大感謝"
|
||||||
|
- "覚悟はいいか?俺はできてる"
|
||||||
|
- "もうだめだ"
|
||||||
|
- "ンアッー"
|
||||||
|
- "ファッ!?"
|
||||||
|
- "24時間タタカエマスカ"
|
||||||
Loading…
Add table
Reference in a new issue