From ceed8f64bb234bea897cb87d1c62df476fb43e46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=B5=90=E5=8F=8B?= Date: Sun, 14 Jun 2026 06:08:13 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E3=83=AA=E3=83=AA=E3=83=BC?= =?UTF-8?q?=E3=82=B9=E7=94=A8=E3=82=A2=E3=83=BC=E3=82=AB=E3=82=A4=E3=83=96?= =?UTF-8?q?=E7=94=9F=E6=88=90=E3=81=A8=20GitHub=20Actions=20=E5=85=AC?= =?UTF-8?q?=E9=96=8B=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit make package で全プラットフォーム分のクロスコンパイル済みバイナリを deadman.conf・README・LICENSE と同梱し、linux/darwin は .tar.gz、 windows は .zip に固める。アーカイブ名のみバージョンを持ち、中の実行 ファイルは常に素の deadman / deadman.exe とする。SOURCE_DATE_EPOCH と GNU tar の正規化・gzip -n で決定的アーカイブとし、SHA256SUMS を生成する。 zip の変数は ZIP_BIN とし呼び出し時に ZIP=/ZIPOPT= をクリアする (Info-ZIP の zip は環境変数 $ZIP/$ZIPOPT を既定オプションとして読み、 ZIP という変数名を export すると引数解釈が壊れて出力が作られないため)。 タグ push (v*) で release.yml が make package を呼び全プラットフォームを ビルドして GitHub Release へ公開する(fetch-depth:0 でタグ解決、最小権限、 リリースノートは push 時のみ自動生成)。README にリリース版バイナリの インストール手順を追記。 --- .github/workflows/release.yml | 68 +++++++++++++++++++++++++++++++++++ Makefile | 62 ++++++++++++++++++++++++++++++++ README.md | 26 ++++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..1b85f49 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,68 @@ +name: release + +on: + push: + tags: + - "v*" + workflow_dispatch: + inputs: + tag: + description: "再リリースする既存タグ (例 v0.1.0)" + required: true + +# 既定はトークンを read のみに絞り、リリースジョブだけ write に昇格する。 +permissions: + contents: read + +# 同一タグへの連続 push / 手動再実行で公開ジョブが多重起動し、Release 作成や +# アセット添付が競合しないよう直列化する (実行中のジョブはキャンセルしない)。 +concurrency: + group: release-${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag || github.ref_name }} + cancel-in-progress: false + +jobs: + release: + name: Build and publish release archives + runs-on: ubuntu-latest + permissions: + # GitHub Release の作成・アセット添付に必要 (最小権限)。 + contents: write + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + # git describe --tags のタグ解決と SOURCE_DATE_EPOCH (git log %ct) の双方に + # 全履歴・タグが必要。浅いチェックアウトだとバージョンがハッシュへフォール + # バックし、アーカイブの mtime も不定になる。 + fetch-depth: 0 + ref: ${{ github.event_name == 'workflow_dispatch' && format('refs/tags/{0}', github.event.inputs.tag) || github.ref }} + + - name: Set up Go + uses: actions/setup-go@v6 + with: + # バージョンの単一情報源は go.mod。最新パッチで stdlib のセキュリティ修正を取り込む。 + go-version-file: go.mod + check-latest: true + + - name: Run tests + # 公開前にタグのコミットでテストを通し、壊れたバイナリの出荷を防ぐ。 + run: make test + + - name: Build cross-platform archives and checksums + # ビルド・アーカイブ・チェックサムは Makefile が単一の情報源 (CLAUDE.md)。 + run: make package + + - name: Publish GitHub Release + uses: softprops/action-gh-release@v3 + with: + tag_name: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag || github.ref_name }} + # 自動生成ノートはタグ push 時のみ。手動再実行で手書きのリリースノートを上書きしない。 + generate_release_notes: ${{ github.event_name == 'push' }} + files: | + dist/*.tar.gz + dist/*.zip + dist/SHA256SUMS + # GITHUB_TOKEN は自動注入。上の contents:write で fork (yuu61/deadman) へ公開できる。 + # 注意: fork からの pull_request では token が read-only になり Release 作成に失敗するため、 + # この workflow に pull_request trigger を足さないこと。 diff --git a/Makefile b/Makefile index 749e010..969bd49 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,12 @@ GOLANGCI ?= golangci-lint # golangci-lint を `make tools` で入れる際のバージョン (現行 v2 系)。 GOLANGCI_VERSION ?= v2.12.2 +# アーカイブ生成に使う外部ツール (macOS で GNU tar を使う場合は `make package TAR=gtar`)。 +# zip の変数は ZIP_BIN とする: Info-ZIP の zip は環境変数 $ZIP / $ZIPOPT を既定オプション +# として読むため、make 変数を ZIP と名付けて export すると引数解釈が壊れる (出力が作られない)。 +TAR ?= tar +ZIP_BIN ?= zip + BIN_NAME := deadman PKG := ./cmd/deadman BIN_DIR := bin @@ -18,6 +24,15 @@ EXE := $(shell $(GO) env GOEXE) # git から導出するバージョン文字列 (タグが無ければコミットハッシュ、未コミット差分は -dirty)。 VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev) +# アーカイブを決定的にするためコミット日時を SOURCE_DATE_EPOCH に固定する +# (tar の mtime 正規化・gzip -n と併用)。環境変数が設定されていればそちらを優先。 +SOURCE_DATE_EPOCH := $(or $(SOURCE_DATE_EPOCH),$(shell git log -1 --pretty=%ct 2>/dev/null || echo 0)) + +# zip は SOURCE_DATE_EPOCH を解さないため、固める前に touch で各ファイルの mtime を固定する。 +# touch -d @epoch は GNU 専用なので、GNU/BSD 双方が解せる touch -t 形式 (ホスト TZ) へ変換する。 +# date が epoch を扱えない環境でも決定性を失わないよう、最終手段として DOS 下限の 1980-01-01 に倒す。 +SOURCE_DATE_TOUCH := $(or $(shell date -d @$(SOURCE_DATE_EPOCH) +%Y%m%d%H%M.%S 2>/dev/null || date -r $(SOURCE_DATE_EPOCH) +%Y%m%d%H%M.%S 2>/dev/null),198001010000.00) + # リリースビルド用フラグ。-s -w でシンボルを除去、-trimpath で絶対パスを排除して再現性を上げる。 # -X main.version で git 由来のバージョン文字列をバイナリへ埋め込み、TUI のタイトル表示に反映する。 LDFLAGS := -s -w -X main.version=$(VERSION) @@ -48,6 +63,53 @@ cross: ## 全プラットフォーム向けにクロスコンパイル (dist/ $(GO) build $(BUILDFLAGS) -o $$out $(PKG) || exit 1; \ done +.PHONY: package +package: ## リリース用アーカイブ(.tar.gz/.zip)と SHA256SUMS を dist/ に生成 (要 GNU tar/gzip/zip/sha256sum) + @command -v $(ZIP_BIN) >/dev/null 2>&1 || { echo "error: zip が見つかりません (Debian/Ubuntu: apt install zip / macOS: brew install zip)" >&2; exit 1; } + @$(TAR) --version 2>/dev/null | grep -q 'GNU tar' || { echo "error: GNU tar が必要です (macOS: brew install gnu-tar してから make package TAR=gtar)" >&2; exit 1; } + @mkdir -p $(DIST_DIR) + @# 旧アーカイブ・make cross の素バイナリ・中断で残ったステージを一掃して、古い版が混ざらないようにする。 + @rm -rf $(DIST_DIR)/$(BIN_NAME)-* $(DIST_DIR)/SHA256SUMS + @case "$(VERSION)" in \ + *-dirty) echo "warning: VERSION=$(VERSION) — 作業ツリーが汚れています。クリーンなタグ付きの木で実行してください。" >&2 ;; \ + dev) echo "warning: VERSION=dev — git 情報がありません。リリースには git tag vX.Y.Z を推奨します。" >&2 ;; \ + *-[0-9]*-g[0-9a-f]*) echo "warning: VERSION=$(VERSION) はタグから先行したコミットです。リリースはタグ直上のコミットで実行してください。" >&2 ;; \ + v[0-9]*) : ;; \ + *) echo "$(VERSION)" | grep -Eq '^[0-9a-f]{7,40}$$' \ + && echo "warning: VERSION=$(VERSION) はコミットハッシュです。リリース前に git tag vX.Y.Z を付けてください。" >&2 || true ;; \ + esac + @for p in $(PLATFORMS); do \ + os=$${p%/*}; arch=$${p#*/}; \ + ext=; [ "$$os" = windows ] && ext=.exe; \ + name=$(BIN_NAME)-$(VERSION)-$$os-$$arch; \ + stage=$(DIST_DIR)/$$name; \ + echo ">> $$stage"; \ + rm -rf $$stage; mkdir -p $$stage; \ + CGO_ENABLED=0 GOOS=$$os GOARCH=$$arch \ + $(GO) build $(BUILDFLAGS) -o $$stage/$(BIN_NAME)$$ext $(PKG) || exit 1; \ + cp deadman.conf LICENSE $$stage/ || exit 1; \ + sed '/](img\//d' README.md > $$stage/README.md || exit 1; \ + chmod 0755 $$stage/$(BIN_NAME)$$ext; \ + chmod 0644 $$stage/deadman.conf $$stage/README.md $$stage/LICENSE; \ + if [ "$$os" = windows ]; then \ + find $$stage -exec touch -t $(SOURCE_DATE_TOUCH) {} + 2>/dev/null || true; \ + ( cd $(DIST_DIR) && rm -f $$name.zip && ZIP= ZIPOPT= TZ=UTC $(ZIP_BIN) -qrX $$name.zip $$name ) || exit 1; \ + else \ + $(TAR) --sort=name --mtime="@$(SOURCE_DATE_EPOCH)" \ + --owner=0 --group=0 --numeric-owner \ + --pax-option=exthdr.name=%d/PaxHeaders/%f,delete=atime,delete=ctime \ + -C $(DIST_DIR) -cf - $$name | gzip -n > $(DIST_DIR)/$$name.tar.gz || exit 1; \ + fi; \ + rm -rf $$stage; \ + done + @cd $(DIST_DIR) && \ + if command -v sha256sum >/dev/null 2>&1; then HASH="sha256sum"; \ + elif command -v shasum >/dev/null 2>&1; then HASH="shasum -a 256"; \ + else echo "error: sha256sum/shasum が見つかりません" >&2; exit 1; fi; \ + set --; for f in *.tar.gz *.zip; do [ -e "$$f" ] && set -- "$$@" "$$f"; done; \ + if [ "$$#" -gt 0 ]; then $$HASH "$$@" > SHA256SUMS && echo ">> $(DIST_DIR)/SHA256SUMS"; \ + else echo "warning: アーカイブが無いため SHA256SUMS を生成しません。" >&2; fi + .PHONY: install install: ## go install で $GOBIN/$GOPATH/bin に導入 $(GO) install $(BUILDFLAGS) $(PKG) diff --git a/README.md b/README.md index 1bdf48d..3ed7008 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,32 @@ go run ./cmd/deadman deadman.conf go install github.com/yuu61/deadman/cmd/deadman@latest ``` +## インストール(リリース版バイナリ) + +[Releases](https://github.com/yuu61/deadman/releases) から OS / アーキテクチャに合ったアーカイブをダウンロードして展開します。 +linux / macOS は `.tar.gz`、Windows は `.zip` で、ファイル名は `deadman---` 形式です(中の実行ファイル名は常に `deadman` / `deadman.exe`)。 +展開すると `deadman---/` という単一ディレクトリになり、実行ファイル・設定例 `deadman.conf`・README・LICENSE が入っています。 + +```sh +tar xzf deadman-v0.1.0-linux-amd64.tar.gz # Windows: deadman-v0.1.0-windows-amd64.zip を展開 +cd deadman-v0.1.0-linux-amd64 +./deadman deadman.conf # Windows: .\deadman.exe deadman.conf +``` + +同梱の `deadman.conf` は監視対象の**例**です。実際の監視先に書き換えて使ってください(既存の設定ファイルがある場合は上書きに注意)。設定の文法は「[設定ファイル](#設定ファイル)」を参照してください。 + +完全性は Releases に併載の `SHA256SUMS`で検証できます。アーカイブと一緒にダウンロードしてください。 + +```sh +# ダウンロードしたアーカイブと SHA256SUMS を同じディレクトリに置いて実行 +sha256sum --ignore-missing -c SHA256SUMS +# macOS: shasum -a 256 -c SHA256SUMS (全アーカイブを置くか、対象 1 件を手動照合) +``` + +Linux で raw ソケットや権限が必要な場合は「[権限とプラットフォームに関する注意](#権限とプラットフォームに関する注意)」を参照してください。 + +> **メンテナ向け**: 配布アーカイブは `make package` で `dist/` に生成されます(`.tar.gz` / `.zip` と `SHA256SUMS`、要 `zip` / GNU `tar`)。`git tag -a vX.Y.Z` を付けて `git push origin vX.Y.Z` すると `release` ワークフローが全プラットフォーム分をビルドして GitHub Release へ公開します。 + ## 使い方 ```sh From a6c889cdc5bdedb353a1e8cde4e3c4a1f1e1a009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=B5=90=E5=8F=8B?= Date: Sun, 14 Jun 2026 08:17:24 +0900 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20=E3=82=B3=E3=83=BC=E3=83=89=E3=83=AC?= =?UTF-8?q?=E3=83=93=E3=83=A5=E3=83=BC=E6=8C=87=E6=91=98=E5=AF=BE=E5=BF=9C?= =?UTF-8?q?=20=E2=80=94=20tar=20=E5=A4=B1=E6=95=97=E3=81=AE=E4=BC=9D?= =?UTF-8?q?=E6=92=AD=E3=83=BB=E5=85=AC=E9=96=8B=E3=82=BF=E3=82=B0=E3=81=AE?= =?UTF-8?q?=E6=98=8E=E7=A4=BA=E3=83=BBGo=20=E3=82=B3=E3=83=A1=E3=83=B3?= =?UTF-8?q?=E3=83=88=E6=95=B4=E5=90=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Makefile: アーカイブ生成の `tar | gzip` パイプを中間ファイル化。dash は pipefail 非対応でパイプの終了ステータスが gzip 由来になり、tar 失敗時に 破損 .tar.gz を「成功」として公開しうる問題を修正(各コマンドの status を伝播)。 - release.yml: 同一コミットに複数タグがある場合に git describe が tag_name と 別タグを拾わないよう、公開タグを make package へ VERSION として明示的に渡す。 - release.yml: setup-go のコメントを実態に合わせて修正(go-version-file は go.mod の パッチに固定され、check-latest はそのバージョンの確実な取得でありパッチ追従ではない)。 --- .github/workflows/claude-code-review.yml | 2 +- .github/workflows/release.yml | 7 +++++-- Makefile | 3 ++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml index a756465..36e17a0 100644 --- a/.github/workflows/claude-code-review.yml +++ b/.github/workflows/claude-code-review.yml @@ -27,7 +27,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1b85f49..15b3dd5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -41,7 +41,8 @@ jobs: - name: Set up Go uses: actions/setup-go@v6 with: - # バージョンの単一情報源は go.mod。最新パッチで stdlib のセキュリティ修正を取り込む。 + # バージョンの単一情報源は go.mod。check-latest で go.mod が指すバージョンを + # 確実に取得する (ランナー同梱の Go に依存しない)。新しい Go の取り込みは go.mod の更新で行う。 go-version-file: go.mod check-latest: true @@ -51,7 +52,9 @@ jobs: - name: Build cross-platform archives and checksums # ビルド・アーカイブ・チェックサムは Makefile が単一の情報源 (CLAUDE.md)。 - run: make package + # 同一コミットに複数タグがある場合に git describe が別タグを拾わないよう、 + # 公開する tag を VERSION として明示的に渡し tag_name と一致させる。 + run: make package VERSION=${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag || github.ref_name }} - name: Publish GitHub Release uses: softprops/action-gh-release@v3 diff --git a/Makefile b/Makefile index 969bd49..9318ec7 100644 --- a/Makefile +++ b/Makefile @@ -98,7 +98,8 @@ package: ## リリース用アーカイブ(.tar.gz/.zip)と SHA256SUMS を dist/ $(TAR) --sort=name --mtime="@$(SOURCE_DATE_EPOCH)" \ --owner=0 --group=0 --numeric-owner \ --pax-option=exthdr.name=%d/PaxHeaders/%f,delete=atime,delete=ctime \ - -C $(DIST_DIR) -cf - $$name | gzip -n > $(DIST_DIR)/$$name.tar.gz || exit 1; \ + -C $(DIST_DIR) -cf $(DIST_DIR)/$$name.tar $$name || exit 1; \ + gzip -nf $(DIST_DIR)/$$name.tar || exit 1; \ fi; \ rm -rf $$stage; \ done