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
39 changes: 39 additions & 0 deletions .github/workflows/release-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Release PR

# Creates a release pull request that bumps cmd/sava/version.txt.
# Merging that PR is the release approval: release.yml then builds,
# tags, and publishes using the PR body as release notes.
#
# Note: PRs created with GITHUB_TOKEN do not trigger other workflows,
# so no CI runs on the release PR itself. The diff is version.txt
# only, and release.yml runs go test before publishing.

on:
workflow_dispatch:
inputs:
bump:
description: Version bump level
type: choice
options:
- patch
- minor
- major
default: patch

permissions:
contents: write
pull-requests: write

jobs:
release-pr:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1

- name: Create release PR
env:
GH_TOKEN: ${{ github.token }}
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
make release-pr BUMP="${{ inputs.bump }}"
61 changes: 61 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name: Release

# Runs when a release PR (created by release-pr.yml) is merged:
# builds binaries, creates the version tag, and publishes a GitHub
# Release using the merged PR body as release notes.

on:
push:
branches: [main]
paths: ['cmd/sava/version.txt']

permissions:
contents: write

jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1

- name: Read version
id: version
run: echo "tag=$(cat cmd/sava/version.txt)" >> "$GITHUB_OUTPUT"

- name: Skip if already released
id: guard
env:
TAG: ${{ steps.version.outputs.tag }}
run: |
if git ls-remote --tags origin "refs/tags/$TAG" | grep -q .; then
echo "Tag $TAG already exists; skipping release."
echo "exists=true" >> "$GITHUB_OUTPUT"
fi

- uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0
if: steps.guard.outputs.exists != 'true'
with:
go-version-file: go.mod

- name: Test
if: steps.guard.outputs.exists != 'true'
run: go test ./...

- name: Build binaries
if: steps.guard.outputs.exists != 'true'
run: make release-build TAG="${{ steps.version.outputs.tag }}"

- name: Publish release
if: steps.guard.outputs.exists != 'true'
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ steps.version.outputs.tag }}
run: |
# Release notes = body of the PR that introduced this commit.
gh api "repos/$GITHUB_REPOSITORY/commits/$GITHUB_SHA/pulls" \
--jq '.[0].body // empty' > notes.md
if [ ! -s notes.md ]; then
echo "Release $TAG" > notes.md
fi
gh release create "$TAG" dist/* \
--title "$TAG" --notes-file notes.md --target "$GITHUB_SHA"
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ docs/.obsidian/

# Go build artifacts
/sava

# Release build artifacts
/dist/
29 changes: 29 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
.PHONY: build test fmt vet lint check release-pr release-build

BUMP ?= patch
TAG ?= $(shell cat cmd/sava/version.txt)

build:
go build -o sava ./cmd/sava

test:
go test ./...

fmt:
test -z "$$(gofmt -l .)"

vet:
go vet ./...

lint:
golangci-lint run

check: fmt vet test lint

# --- release (scripts run via bash: no exec permission needed) ---

release-pr:
bash scripts/create-release-pr.sh $(BUMP)

release-build:
bash scripts/build-release.sh $(TAG)
29 changes: 24 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,21 @@ https://gist.github.com/rn404/decf010fc48d7d8688116af0f4427b44
go install github.com/rn404/nippo-cli/cmd/sava@latest
```

ビルド済みバイナリ (macOS / Linux) は
[Releases](https://github.com/rn404/nippo-cli/releases) からも取得できます.

## Release lifecycle

バージョンの単一の真実は `cmd/sava/version.txt` (`go:embed` でバイナリに埋め込み).

1. Actions で「Release PR」workflow を dispatch し, bump レベル (patch / minor / major) を選択
2. `version.txt` を更新した Release PR が自動で作られる (本文には変更点一覧が自動生成される)
3. PR 本文をリリースノートとして整えて, **マージ = リリース承認**
4. マージを検知して release workflow が起動し, テスト・バイナリビルド・タグ作成・GitHub Release 公開まで自動実行 (マージ時点の PR 本文がそのままリリースノートになる)

リリース処理の実体は `scripts/` にあり, Makefile 経由でローカルでも実行できます
(例: `make release-build TAG=v0.1.0` で `dist/` にバイナリを生成).

## Usage

```
Expand Down Expand Up @@ -64,11 +79,15 @@ sava clear -a
# Run from source
go run ./cmd/sava <command>

# Test / format / lint
go test ./...
gofmt -l .
go vet ./...
golangci-lint run # version is pinned in .mise.toml (mise install)
# Test / format / vet / lint at once
make check

# Individual targets
make test
make fmt
make vet
make lint # golangci-lint: version is pinned in .mise.toml (mise install)
make build
```

## History
Expand Down
12 changes: 9 additions & 3 deletions cmd/sava/main.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
package main

import (
_ "embed"
"fmt"
"os"
"strings"

"github.com/spf13/cobra"
)

const appName = "sava"

// version is overridable at build time:
// go build -ldflags "-X main.version=vX.Y.Z"
var version = "v0.1.0"
// versionFile is the single source of truth for the version, bumped
// by the release PR workflow (.github/workflows/release-pr.yml).
//
//go:embed version.txt
var versionFile string

var version = strings.TrimSpace(versionFile)

func main() {
root := newRootCommand()
Expand Down
1 change: 1 addition & 0 deletions cmd/sava/version.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v0.0.0
4 changes: 3 additions & 1 deletion docs/go-migration-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,9 @@ type Log struct {
- [x] **4.2** Phase 0 のサンプルと比較し、機能面のデグレがないことを確認
- 加えて hash バグ・`clear` バグの修正が意図通り機能することを実機確認
- [x] **4.3** README の Usage を Go 版に更新
- [x] **4.4** `go install` 手順を README に整備(goreleaser は任意のため今回は見送り、必要になったら導入)
- [x] **4.4** `go install` 手順を README に整備
- 追記: リリース用 CI を移行後に追加。goreleaser は Actions 許可リストへの追加が必要になるため採用せず、GitHub 製アクション + `go build` + `gh` のみで構成
- ライフサイクル: `release-pr.yml`(dispatch でバージョンバンプ PR を自動作成)→ PR マージ = リリース承認 → `release.yml`(`cmd/sava/version.txt` の変更を検知してテスト・ビルド・タグ・Release 公開、PR 本文がリリースノート)

### Phase 5: Deno 資産の撤去

Expand Down
21 changes: 21 additions & 0 deletions scripts/build-release.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env bash
# Cross-compile release binaries into dist/ with sha256 checksums.
# Usage: build-release.sh <tag>
set -euo pipefail

cd "$(dirname "$0")/.."
tag="${1:?usage: build-release.sh <tag>}"

rm -rf dist
mkdir -p dist

for target in darwin/amd64 darwin/arm64 linux/amd64 linux/arm64; do
export GOOS="${target%/*}" GOARCH="${target#*/}"
name="sava_${tag}_${GOOS}_${GOARCH}"
CGO_ENABLED=0 go build -trimpath -ldflags "-s -w" \
-o "dist/${name}/sava" ./cmd/sava
tar -czf "dist/${name}.tar.gz" -C dist "$name"
rm -r "dist/${name:?}"
done

(cd dist && shasum -a 256 ./* > checksums.txt)
17 changes: 17 additions & 0 deletions scripts/bump-version.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env bash
# Print the next version for the given bump level.
# Usage: bump-version.sh <patch|minor|major>
set -euo pipefail

VERSION_FILE="$(dirname "$0")/../cmd/sava/version.txt"
bump="${1:?usage: bump-version.sh <patch|minor|major>}"

current="$(cat "$VERSION_FILE")"
IFS=. read -r major minor patch <<< "${current#v}"

case "$bump" in
major) echo "v$((major + 1)).0.0" ;;
minor) echo "v${major}.$((minor + 1)).0" ;;
patch) echo "v${major}.${minor}.$((patch + 1))" ;;
*) echo "unknown bump level: $bump" >&2; exit 1 ;;
esac
24 changes: 24 additions & 0 deletions scripts/create-release-pr.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env bash
# Create a release PR that bumps cmd/sava/version.txt.
# Merging the PR is the release approval (see release.yml).
# Usage: create-release-pr.sh <patch|minor|major>
# Requires: gh (authenticated), git push permission.
set -euo pipefail

cd "$(dirname "$0")/.."
bump="${1:?usage: create-release-pr.sh <patch|minor|major>}"

next="$(bash scripts/bump-version.sh "$bump")"
base_sha="$(git rev-parse HEAD)"
repo="$(gh repo view --json nameWithOwner --jq .nameWithOwner)"

git switch -c "release/$next"
echo "$next" > cmd/sava/version.txt
git commit -am "release: $next"
git push -u origin "release/$next"

# Prefill the PR body with auto-generated notes; edit it before
# merging — the merged body becomes the release notes.
notes="$(gh api "repos/$repo/releases/generate-notes" \
-f tag_name="$next" -f target_commitish="$base_sha" --jq .body)"
gh pr create --title "Release $next" --body "$notes"
Loading