Class: Ligarb::GithubReview

Inherits:
Object
  • Object
show all
Defined in:
lib/ligarb/github_review.rb

Overview

Sets up the GitHub-based review scaffolding (.github/ + SETUP.md) in a project — the ‘ligarb setup-github-review` command. This is pure file copying: ligarb never calls Claude or GitHub at runtime. The templates are classified into layers so a future option could split them apart:

- generic layer  : works without Claude (Pages deploy, build check, forms)
- claude layer    : opt-in Claude integration (issue/PR handlers, SETUP.md)

Re-running OVERWRITES the generated files so a project can follow upstream template changes (e.g. after a ligarb upgrade). The user’s own book.yml is never overwritten. Since projects are git repos, ‘git diff` is the safety net for reviewing/reverting changes after a re-sync.

Defined Under Namespace

Classes: Result

Constant Summary collapse

TEMPLATE_DIR =
File.expand_path("../../templates/github_review", __dir__)
GENERIC_FILES =

Generic layer: no Claude dependency.

%w[
  .github/workflows/deploy-book.yml
  .github/workflows/build-check.yml
  .github/ISSUE_TEMPLATE/book-feedback.yml
  .github/ISSUE_TEMPLATE/config.yml
].freeze
CLAUDE_FILES =

Claude integration layer: opt-in.

%w[
  .github/workflows/claude-feedback.yml
  .github/workflows/claude-pr-mention.yml
  SETUP.md
  SETUP.sh
].freeze
TEMPLATE_FILES =
(GENERIC_FILES + CLAUDE_FILES).freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(target) ⇒ GithubReview

Returns a new instance of GithubReview.



64
65
66
# File 'lib/ligarb/github_review.rb', line 64

def initialize(target)
  @target = File.expand_path(target)
end

Class Method Details

.run(directory = nil) ⇒ Object

‘ligarb setup-github-review [DIR]` entry point. Sets up the scaffolding in an existing ligarb project (book.yml must exist), enables the reader feedback UI in book.yml, and prints the remaining manual-setup steps. Safe to re-run to pull in updated templates (generated files are overwritten; book.yml is not).



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/ligarb/github_review.rb', line 46

def self.run(directory = nil)
  target = File.expand_path(directory || ".")
  unless File.exist?(File.join(target, "book.yml"))
    $stderr.puts "Error: book.yml not found in #{target}"
    $stderr.puts "Run 'ligarb init' or 'ligarb write' first, then set up the GitHub review scaffolding."
    exit 1
  end

  reviewer = new(target)
  # book.yml edits must run BEFORE generate so the templates (SETUP.sh,
  # issue forms, README) are substituted with the resolved repository.
  repository = reviewer.ensure_repository_in_book_yml
  enabled = reviewer.enable_in_book_yml
  readme = reviewer.create_readme_if_absent
  result = reviewer.generate
  reviewer.print_notice(result, repository: repository, enabled: enabled, readme: readme)
end

Instance Method Details

#create_readme_if_absentObject

Creates a project README.md that links to the published GitHub Pages site, but only when one does not already exist (the reader’s own README is never overwritten). Returns :created or :present.



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/ligarb/github_review.rb', line 133

def create_readme_if_absent
  readme = File.join(@target, "README.md")
  return :present if File.exist?(readme)

  owner, repo = extract_owner_repo
  pages  = owner && repo ? "https://#{owner}.github.io/#{repo}/" : "https://__OWNER__.github.io/__REPO__/"
  issues = owner && repo ? "https://github.com/#{owner}/#{repo}/issues/new?template=book-feedback.yml" \
                         : "https://github.com/__OWNER__/__REPO__/issues/new?template=book-feedback.yml"
  title = book_title.to_s.empty? ? "Book" : book_title

  File.write(readme, <<~MD)
    # #{title}

    📖 **公開版(GitHub Pages)**: #{pages}

    この本は [ligarb](https://github.com/ko1/ligarb) で生成しています。

    ## フィードバック

    本文の誤り・わかりにくい点・疑問は [Issue](#{issues}) からどうぞ。
    公開ページでは本文を選択して「Report as issue」からも送れます。

    ## ローカルでビルド

    ```bash
    ligarb build   # build/index.html を生成
    ligarb serve   # ローカルプレビュー
    ```

    セットアップ手順は [SETUP.md](SETUP.md) を参照してください。
  MD
  :created
end

#enable_in_book_ymlObject

Ensures ‘github_review.enabled: true` is present in book.yml so the reader feedback UI activates (once `repository` is also set). Appends the key only when absent, preserving existing formatting/comments. Returns :added, :present, or :unsupported (translations hub / unparsable).



101
102
103
104
105
106
107
108
109
110
# File 'lib/ligarb/github_review.rb', line 101

def enable_in_book_yml
  book_yml = File.join(@target, "book.yml")
  data = YAML.safe_load_file(book_yml)
  return :unsupported unless data.is_a?(Hash)
  return :present if data.key?("github_review")

  content = File.read(book_yml).rstrip
  File.write(book_yml, "#{content}\n\ngithub_review:\n  enabled: true\n")
  :added
end

#ensure_repository_in_book_ymlObject

Seeds a default ‘repository:` in book.yml when it has none, guessing github.com/<os-user>/<dir-name>. This drives __OWNER__/__REPO__ substitution and the GH Pages link; the user edits it if the guess is wrong. Returns :added, :present, or :unsupported. (@default_repository is set to the guessed URL when :added, for the notice.)



117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/ligarb/github_review.rb', line 117

def ensure_repository_in_book_yml
  book_yml = File.join(@target, "book.yml")
  data = YAML.safe_load_file(book_yml)
  return :unsupported unless data.is_a?(Hash)
  return :present if data.key?("repository")

  user = ENV["USER"] || ENV["USERNAME"] || "your-github-account"
  @default_repository = "https://github.com/#{user}/#{File.basename(@target)}"
  content = File.read(book_yml).rstrip
  File.write(book_yml, %(#{content}\n\nrepository: "#{@default_repository}"\n))
  :added
end

#generateObject

Writes all template files into the project, substituting __OWNER__ / __REPO__ from book.yml’s ‘repository:`. Existing files are OVERWRITTEN so a project can follow upstream template changes; only files whose content is already identical are left untouched. Returns a Result listing created/updated/unchanged files.



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/ligarb/github_review.rb', line 73

def generate
  owner, repo = extract_owner_repo
  created = []
  updated = []
  unchanged = []

  TEMPLATE_FILES.each do |rel|
    dest = File.join(@target, rel)
    content = render(rel, File.read(File.join(TEMPLATE_DIR, rel)), owner, repo)

    if !File.exist?(dest)
      write_file(dest, content)
      created << rel
    elsif File.read(dest) == content
      unchanged << rel
    else
      write_file(dest, content)
      updated << rel
    end
  end

  Result.new(created: created, updated: updated, unchanged: unchanged)
end


167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/ligarb/github_review.rb', line 167

def print_notice(result, repository:, enabled:, readme:)
  puts "Set up GitHub review scaffolding in #{@target}:"
  result.created.each { |path| puts "  created    #{path}" }
  result.updated.each { |path| puts "  updated    #{path}" }
  puts "  created    README.md (with the GitHub Pages link)" if readme == :created
  case repository
  when :added then puts "  updated    book.yml (repository: #{@default_repository})"
  end
  case enabled
  when :added   then puts "  updated    book.yml (github_review.enabled: true)"
  when :present then puts "  kept       book.yml github_review setting"
  end
  unless result.unchanged.empty?
    puts "  unchanged  #{result.unchanged.size} file(s) already up to date"
  end
  if result.updated.any?
    puts
    puts "Note: existing scaffolding files were overwritten with the latest"
    puts "templates. Review with 'git diff' and revert any local edits you"
    puts "want to keep."
  end
  puts
  puts "Next: edit 'repository:' in book.yml if the guess is wrong, then run"
  puts "the gh CLI quickstart:"
  puts "  bash SETUP.sh    # repo create + secret + Pages + permissions + labels"
  puts
  puts "It still needs a token (see SETUP.md): generate one with"
  puts "'claude setup-token' before running SETUP.sh (it prompts for it)."
end