Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .claude/hooks/post-edit.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Write / Edit の後に実行するフック (.claude/settings.json の PostToolUse)
// - .cs: UTF-8 の BOM がなければ付ける (CLAUDE.md のコード規約)
// - .github/workflows/*.yml: gh actions-lock --verify-local でロックファイルの漏れを確認する
import { readFileSync, writeFileSync } from "node:fs";
import { spawnSync } from "node:child_process";

const input = JSON.parse(readFileSync(0, "utf8"));
const file = input.tool_input?.file_path ?? input.tool_response?.filePath;
if (!file) process.exit(0);
const path = file.replaceAll("\\", "/");

if (path.endsWith(".cs")) {
const bytes = readFileSync(file);
if (!(bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf)) {
writeFileSync(file, Buffer.concat([Buffer.from([0xef, 0xbb, 0xbf]), bytes]));
}
} else if (/\/\.github\/workflows\/[^/]+\.ya?ml$/.test(path)) {
const result = spawnSync("gh", ["actions-lock", "--verify-local"], {
cwd: process.env.CLAUDE_PROJECT_DIR || process.cwd(),
encoding: "utf8",
});
// gh や拡張機能がない環境では何もしない
if (result.error) process.exit(0);
if (result.status !== 0) {
console.log(JSON.stringify({
decision: "block",
reason: "actions.lock がワークフローと一致していません。gh actions-lock でロックし直し、gh actions-lock --verify で確認してください。\n"
+ (result.stdout + result.stderr).trim(),
}));
}
}
16 changes: 16 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/post-edit.mjs\"",
"timeout": 30
}
]
}
]
}
}
29 changes: 19 additions & 10 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@

## 言語

- 日本向けのライブラリなので、ユーザーとのやり取り・ドキュメント(README、CLAUDE.md など)・コードコメント・XML ドキュメントコメント・コミットメッセージはすべて日本語で書く。
- 日本向けのライブラリなので、ユーザーとのやり取り・ドキュメント(README、CLAUDE.md など)・コードコメント・XML ドキュメントコメント・コミットメッセージ・PR の説明はすべて日本語で書く。

## リポジトリ運用

- コミット・push・PR 作成は頼まれたときだけ行う。master で作業せず、ブランチを切ってから PR にする(依存する変更が続くときは、頼まれたら stacked PR にする)。
- PR のマージはユーザーが行う。「マージした」と言われたら master を pull し、マージ済みのブランチを削除する。

## 構成・ビルド

Expand All @@ -15,8 +20,9 @@
- `ZipConvertCustomerBarCode.Drawing`: System.Drawing.Common による描画(Windows のみ)。
- `ZipConvertCustomerBarCode.SkiaSharp`: SkiaSharp による描画。名前空間は `SkiaSharp` との衝突を避けるため `ZipConvertCustomerBarCode.Skia`。
- `netstandard2.0` の既定 C# は 7.3。`#if NET8_0_OR_GREATER` の外では `record` や null 許容参照型など新しい構文は使わない。
- バージョン・著作権表示は `Directory.Build.props` にまとめている。
- バージョン・著作権表示・パッケージ情報は `Directory.Build.props` にまとめている。
- 公開メンバーには XML ドキュメントコメントを付ける(`GenerateDocumentationFile` 有効のため、ないと警告になる)。
- `.cs` ファイルは UTF-8(BOM 付き)。新規ファイルにも BOM を付ける(`.claude/settings.json` のフックが Write / Edit 後に自動で補う)。改行は `core.autocrlf=true` に従う(Windows の作業ツリーでは CRLF、リポジトリ内は LF)。

## テスト

Expand All @@ -32,19 +38,22 @@
## CI・リリース

- `.github/workflows/build.yml`: push・PR で Ubuntu と Windows × コアの両方のビルドをテストし、パッケージを作る。
- リリースは `v2.0.0` のようなタグを push すると、タグのバージョンで3パッケージを nuget.org に公開する(Trusted Publishing。ワークフローのファイル名 `build.yml` と Environment 名 `release` を nuget.org に登録しているので、どちらも名前を変えない)。
- リリースは `v2.0.0` のようなタグを push すると、タグのバージョンで3パッケージを nuget.org に公開する(Trusted Publishing。ワークフローのファイル名 `build.yml` と Environment 名 `release` を nuget.org に登録しているので、どちらも名前を変えない)。nuget.org 側のポリシーは「新しいバージョンの追加のみ」なので、公開済みのバージョンは出し直せない(修正は次のバージョンで出す)。
- 公開ジョブは GitHub の `release` Environment で動く(`v*` タグのみ、cssho の承認後に実行。Secret `NUGET_USER` もこの Environment にある)。`Directory.Build.props` の `Version` はローカル用で、公開時はタグが優先される。
- ワークフローのアクションは `.github/workflows/actions.lock`(`gh actions-lock`)でコミットに固定している。`uses:` を追加・変更したら `gh actions-lock` でロックし直し、`gh actions-lock --verify` で確認してから、ロックファイルとワークフローを一緒にコミットする。CI の `verify-lockfile.yml` が PR・push・週1回で検証する。アクションの更新は Dependabot(`.github/dependabot.yml`、既定の cooldown でリリースから3日待つ)が、ロックファイルの作り直しも含めて PR にする。
- パッケージ作成時の検証(`EnablePackageValidation`)で、net8.0 版から netstandard2.0 版の公開 API が欠けているとエラー(CP0002)になる。

## コード規約

- `.cs` ファイルは UTF-8(BOM 付き)・CRLF 改行。どちらも維持する(新規ファイルにも BOM を付ける)。
- ワークフローのアクションは `.github/workflows/actions.lock`(`gh actions-lock`)でコミットに固定している。`uses:` を追加・変更したら `gh actions-lock` でロックし直し、`gh actions-lock --verify` で確認してから、ロックファイルとワークフローを一緒にコミットする(ワークフローを編集すると、フックが `--verify-local` で漏れを知らせる)。JSON 出力は `--json=valid,findings` のように `=` でつなぐ(空白区切りだとパスとして解釈される)。
- CI の `verify-lockfile.yml` が PR・push・週1回でロックファイルを検証する。アクションの更新は Dependabot(`.github/dependabot.yml`、既定の cooldown でリリースから3日待つ)が、ロックファイルの作り直しも含めて PR にする。
- パッケージ作成時の検証(`EnablePackageValidation`)で、net8.0 版から netstandard2.0 版の公開 API が欠けているとエラー(CP0002)になる(net8.0 版にだけ API を足すのは検出しない)。

## バーコード処理

- 住所の変換(`BarCode.cs`)は日本郵便のバーコードマニュアル(http://www.post.japanpost.jp/zipcode/zipmanual/p19.html)に従う。コード中の番号付きコメント(1.〜6.、補足ルール1〜5)はマニュアルの手順番号に対応している。
- マニュアルでは住所を「住所A(町域名まで)」と「住所B(町域名以降)」に分け、住所Bだけから抜き出す。`Create(郵便番号, 住所)` は住所全体を住所Bとして扱うため、町域名に特定文字の前の漢数字を含む住所(一番町、八丁堀など)では誤る。これは既知の制限で、`Create(郵便番号, 住所A, 住所B)` を推奨している。
- マニュアルでは住所を「住所A(町域名まで)」と「住所B(町域名以降)」に分け、住所Bだけから抜き出す。`Create(郵便番号, 住所)` は住所全体を住所Bとして扱うため、町域名に特定文字の前の漢数字を含む住所(一番町、八丁堀など)では誤る。これは既知の制限で、`Create(郵便番号, 住所A, 住所B)` か `ZipCodeDictionary` を渡す `Create` を推奨している。
- バーコードは常に 23 シンボル: スタート + 郵便番号 7 桁 + 住所表示番号 13 枠(不足分は `CC4` で埋める)+ チェックデジット + ストップ。
- バーの形は `CustomerBarCode.cs` の `BarPatternDic` で定義(1 シンボル = 幅 12px に 3 本、x=0/4/8、幅 2px、上・中・下段 各 4px)。この表は旧バージョンの GIF 画像とピクセル単位で一致することを確認済みなので、変更するときは既存出力と比較する。
- 描画パッケージは `GetBars()` の矩形を白地に黒で塗るだけにし、バーコードのロジックはコアに置く。

## 郵便番号データ・住所照合

- `ZipCodeDictionary` は利用者が渡す utf_ken_all.csv(UTF-8)と JIGYOSYO.CSV(Shift_JIS)を読む。Shift_JIS は .NET Core 以降では利用者が `CodePagesEncodingProvider` を登録する必要がある(コアに依存を足さないため、ライブラリ側では登録しない)。読み込み時の都道府県名の検証で、文字コードの取り違えを検出している。
- 住所照合(`AddressMatcher`、`ZipCodeDictionary` の検索)は呼び出し回数が多く、LINQ や `ToUpperInvariant` だけで目に見えて遅くなったことがある。変更したら実データで計測する(英字の大文字化は ASCII のみを手で変換している)。
- 事業所名での検索は、名前の完全一致を優先する。部分一致は誤検出が多かったため、番地なども一致する場合だけ採用する。
Loading