commenter 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 90d4268c6a071ae365af24ef05a5c19dcdfd6fe03545c05a84767f91f70dc411
4
- data.tar.gz: b44468b6f9cc18e0726c1df1c1433247e8fc19f6ff9fafc4c3f0eff341a21785
3
+ metadata.gz: 077fd6e045907b803d3f6b33d305bab0a4544b37ede3855fb26af1974f48385a
4
+ data.tar.gz: ffe50dadee86e040ce1b0b5fbb367f5e5233f4bd668248e9f473c2f9bfbccfb3
5
5
  SHA512:
6
- metadata.gz: 00d4838eadd5c0751199832ff6401a753b2bc4c27cd94d02f6c0d2bab6392a629c216c6cc350766c0d35920f871b4e2bd74102967460e370ebc15fc005a17e9c
7
- data.tar.gz: '09420029845d4e6b776eeb40b726326486a409dd8f0ec33be792dcc193e30f5d5774e99777cd64e85c31a5137a69d2648eac4ab06bde2a5ea93a7d2569d0e90a'
6
+ metadata.gz: f3a1e36e43d5dc1d16717afab89546c3c8151c38c56c8b2f8cdbf5885a0cb5d38a34f6a6a1869892bc07a1ff0efe4f4d229a4137241565d74d4e4920555eba4b
7
+ data.tar.gz: d368ce137f669d56ec8c72da5c3a106c7acc95e19c8daa0c3371e09cc91c5af79e6d0af0cfd4498ac466aef96c26377c5020647b8132a59fce453574a303217a
data/.rubocop.yml CHANGED
@@ -16,3 +16,7 @@ Metrics/ModuleLength:
16
16
  Metrics/BlockLength:
17
17
  Exclude:
18
18
  - "spec/**/*"
19
+
20
+ Metrics/ClassLength:
21
+ Exclude:
22
+ - "lib/commenter/cli.rb"
data/CONTEXT.md CHANGED
@@ -23,5 +23,11 @@ keep this current when concepts sharpen.
23
23
  from ISO/CS editors; also carries reviewer remark threads.
24
24
  - **OSD** — ISO Online Standards Development; its XLSX comment exports come
25
25
  in resolved and unresolved variants.
26
+ - **Stage Comparison** — how the comment set evolved between two ballots
27
+ (new, withdrawn, repeated, revised; resolved = disposition recorded).
28
+ `BallotDiff`.
29
+ - **GitHub Sync** — reconciliation of the comment YAML with GitHub issues
30
+ (YAML as source of truth): create missing, refresh content per conflict
31
+ policy, close dispositioned issues. `GitHubSync`.
26
32
  - **Unique ID** — stage-aware identity of a comment's GitHub issue
27
33
  (`[DIS] GB-001` by default), used for duplicate detection.
data/README.adoc CHANGED
@@ -234,6 +234,20 @@ issue counts as *open*; no disposition at all counts as *undecided*). Use
234
234
  `--format yaml` for machine-readable output, and `-o report.md` to write to a
235
235
  file — the Markdown pastes directly into a GitHub issue or WG minutes.
236
236
 
237
+ === Comparing ballots between stages
238
+
239
+ The same comment ID (`DE-001`) recurs at every ballot stage; comparing two
240
+ ballot sheets shows how the set evolved between CD and DIS:
241
+
242
+ [source,shell]
243
+ ----
244
+ commenter diff cd-ballot.yaml dis-ballot.yaml
245
+ ----
246
+
247
+ Comments are matched by ID and classified as new, withdrawn, repeated
248
+ (unchanged text), or revised; a comment whose later version carries a
249
+ disposition counts as resolved. Markdown (default) or `--format yaml`.
250
+
237
251
  === Filling DOCX templates from YAML
238
252
 
239
253
  This gem contains a command-line utility to fill a DOCX template with comments
@@ -464,6 +478,35 @@ The `unique_id` is available as a variable in title and body templates:
464
478
 
465
479
  This renders to: `[DIS] GB-001: Clause 5.1 summary (ISO/DIS 2533)`
466
480
 
481
+ ==== Synchronizing with GitHub issues
482
+
483
+ `github-create` is one-shot: it creates missing issues and skips existing
484
+ ones. `github-sync` keeps the YAML and the issues in step, treating the YAML
485
+ as the source of truth:
486
+
487
+ [source,shell]
488
+ ----
489
+ commenter github-sync --config github_config.yaml comments.yaml
490
+ ----
491
+
492
+ For each comment it creates a missing issue, refreshes issue content from
493
+ the YAML, and reconciles issue state. Two policies govern the parts that
494
+ can collide:
495
+
496
+ `--conflict yaml` (default):: Re-render the issue title and body from the
497
+ YAML when they differ (YAML wins).
498
+ `--conflict github`:: Leave differing issue content untouched (edits made
499
+ on GitHub win).
500
+ `--conflict skip`:: Take no action on differing content; the result is
501
+ reported as a conflict.
502
+
503
+ By default an open issue whose comment has a recorded disposition
504
+ (`observations`) is closed — that is what keeps the
505
+ `github-retrieve` round-trip working, since observations are retrieved from
506
+ closed issues. Disable with `--no-close-on-disposition`.
507
+
508
+ The milestone is resolved once per run and reused for every issue.
509
+
467
510
  ==== Retrieving observations from GitHub issues
468
511
 
469
512
  ===== Basic usage
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Commenter
4
+ # Stage-to-stage comparison of two ballots: how the comment set evolved
5
+ # between e.g. CD and DIS — new comments, withdrawn comments, repeated
6
+ # unchanged comments, and revised ones. Comments are matched by ID (member
7
+ # body + number), which repeats across stages.
8
+ module BallotDiff
9
+ module_function
10
+
11
+ def diff(before_sheet, after_sheet)
12
+ before_comments = indexed(before_sheet)
13
+ after_comments = indexed(after_sheet)
14
+
15
+ result = { new: [], withdrawn: [], repeated: [], revised: [], resolved: [] }
16
+ after_comments.each do |id, after|
17
+ before = before_comments[id]
18
+ if before.nil?
19
+ result[:new] << id
20
+ elsif identical?(before, after)
21
+ result[:repeated] << id
22
+ else
23
+ result[:revised] << id
24
+ end
25
+ result[:resolved] << id if dispositioned?(after)
26
+ end
27
+ before_comments.each_key { |id| result[:withdrawn] << id unless after_comments.key?(id) }
28
+
29
+ result.each_value(&:sort!)
30
+ result
31
+ end
32
+
33
+ def to_markdown(before_sheet, after_sheet)
34
+ data = diff(before_sheet, after_sheet)
35
+ lines = []
36
+ lines << "| Category | Count | Comments |"
37
+ lines << "|----------|-------|----------|"
38
+ data.each do |category, ids|
39
+ label = category.to_s.tr("_", " ").capitalize
40
+ listed = ids.length <= 10 ? ids.join(", ") : "#{ids.first(10).join(", ")} …"
41
+ lines << "| #{label} | #{ids.length} | #{listed} |"
42
+ end
43
+ lines << ""
44
+ lines << "Resolved (disposition recorded in the later stage): #{data[:resolved].length}"
45
+ lines.join("\n")
46
+ end
47
+
48
+ def indexed(sheet)
49
+ sheet.comments.each_with_object({}) do |comment, index|
50
+ key = comment.id.to_s
51
+ index[key] = comment unless key.empty?
52
+ end
53
+ end
54
+
55
+ def identical?(before, after)
56
+ before.comments == after.comments && before.proposed_change == after.proposed_change
57
+ end
58
+
59
+ def dispositioned?(comment)
60
+ observation = comment.observations.to_s.strip
61
+ !observation.empty? || DispositionStatus.match(comment.resolution_status)
62
+ end
63
+ end
64
+ end
data/lib/commenter/cli.rb CHANGED
@@ -65,6 +65,27 @@ module Commenter
65
65
  end
66
66
  end
67
67
 
68
+ desc "diff BEFORE.yaml AFTER.yaml", "Compare two ballots (e.g. CD vs DIS) by comment ID"
69
+ option :output, type: :string, aliases: :o, desc: "Output file (default: stdout)"
70
+ option :format, type: :string, default: "markdown", enum: %w[markdown yaml], desc: "Report format"
71
+ def diff(before_yaml, after_yaml)
72
+ before_sheet = CommentSheet.from_yaml(File.read(before_yaml))
73
+ after_sheet = CommentSheet.from_yaml(File.read(after_yaml))
74
+
75
+ report = if options[:format] == "yaml"
76
+ BallotDiff.diff(before_sheet, after_sheet).to_yaml
77
+ else
78
+ BallotDiff.to_markdown(before_sheet, after_sheet)
79
+ end
80
+
81
+ if options[:output]
82
+ File.write(options[:output], report)
83
+ puts "Wrote comparison to #{options[:output]}"
84
+ else
85
+ puts report
86
+ end
87
+ end
88
+
68
89
  desc "fill INPUT.yaml", "Fill DOCX template from YAML comments"
69
90
  option :output, type: :string, aliases: :o, default: "filled_comments.docx", desc: "Output DOCX file"
70
91
  option :template, type: :string, aliases: :t, desc: "Custom template file"
@@ -160,6 +181,66 @@ module Commenter
160
181
  exit 1
161
182
  end
162
183
 
184
+ desc "github-sync INPUT.yaml", "Synchronize comments with GitHub issues (YAML is the source of truth)"
185
+ option :config, type: :string, aliases: :c, required: true, desc: "GitHub configuration YAML file"
186
+ option :output, type: :string, aliases: :o, desc: "Output YAML file (default: update original)"
187
+ option :stage, type: :string, desc: "Override approval stage (WD/CD/DIS/FDIS/PRF/PUB)"
188
+ option :milestone, type: :string, desc: "Override milestone name or number"
189
+ option :assignee, type: :string, desc: "Override assignee GitHub handle"
190
+ option :title_template, type: :string, desc: "Custom title template"
191
+ option :body_template, type: :string, desc: "Custom body template"
192
+ option :dry_run, type: :boolean, desc: "Preview sync actions without applying them"
193
+ option :conflict, type: :string, default: "yaml", enum: %w[yaml github skip],
194
+ desc: "Conflict policy: yaml re-renders the issue, " \
195
+ "github keeps the issue content, skip reports and leaves it"
196
+ option :close_on_disposition, type: :boolean, default: true,
197
+ desc: "Close open issues whose comment has a recorded " \
198
+ "disposition"
199
+ def github_sync(input_yaml)
200
+ sync = GitHubSync.new(options[:config], options[:title_template], options[:body_template])
201
+
202
+ sync_options = {
203
+ stage: options[:stage],
204
+ milestone: options[:milestone],
205
+ assignee: options[:assignee],
206
+ conflict: options[:conflict],
207
+ close_on_disposition: options[:close_on_disposition],
208
+ dry_run: options[:dry_run],
209
+ output: options[:output]
210
+ }.compact
211
+ results = sync.sync(input_yaml, sync_options)
212
+
213
+ if options[:dry_run]
214
+ puts "DRY RUN - Planned sync actions:"
215
+ puts "=" * 50
216
+ results.each do |result|
217
+ issue = result[:issue_number] ? "issue ##{result[:issue_number]}" : "no issue yet"
218
+ puts "#{result[:comment_id]} (#{issue}): #{result[:actions].join(", ")}"
219
+ end
220
+ puts "-" * 30
221
+ puts "Based on the issue state last recorded in the YAML; run without --dry-run for a live pass."
222
+ else
223
+ puts "GitHub sync results:"
224
+ puts "=" * 40
225
+ results.each do |result|
226
+ case result[:status]
227
+ when :created then puts "\u2713 #{result[:comment_id]}: Created issue ##{result[:issue_number]} " \
228
+ "(#{result[:issue_url]})"
229
+ when :updated then puts "\u21bb #{result[:comment_id]}: Updated issue ##{result[:issue_number]} " \
230
+ "(#{result[:actions].join(", ")})"
231
+ when :closed then puts "\u2713 #{result[:comment_id]}: Closed issue ##{result[:issue_number]} (disposition recorded)"
232
+ when :updated_and_closed then puts "\u21bb\u2713 #{result[:comment_id]}: Updated and closed issue ##{result[:issue_number]}"
233
+ when :unchanged then puts "- #{result[:comment_id]}: Issue ##{result[:issue_number]} in sync"
234
+ when :error then puts "\u2717 #{result[:comment_id]}: Error - #{result[:message]}"
235
+ end
236
+ end
237
+ puts "Updated YAML file: #{options[:output] || input_yaml}"
238
+ end
239
+ rescue StandardError => e
240
+ puts "Error: #{e.message}"
241
+ exit 1
242
+ end
243
+
163
244
  desc "github-retrieve INPUT.yaml", "Retrieve observations from GitHub issues"
164
245
  option :config, type: :string, aliases: :c, required: true, desc: "GitHub configuration YAML file"
165
246
  option :output, type: :string, aliases: :o, desc: "Output YAML file (default: update original)"
@@ -250,8 +250,12 @@ module Commenter
250
250
  puts "[GitHubIssueCreator] Using milestone number: #{milestone_config["number"]}"
251
251
  milestone_config["number"]
252
252
  elsif milestone_config["name"]
253
- puts "[GitHubIssueCreator] Using milestone name: #{milestone_config["name"]}"
254
- resolve_milestone_by_name_or_number(milestone_config["name"])
253
+ # Resolved once per run and memoized: resolving per comment caused
254
+ # duplicated milestone fetches (see 3a06c3d).
255
+ @milestone_number ||= begin
256
+ puts "[GitHubIssueCreator] Using milestone name: #{milestone_config["name"]}"
257
+ resolve_milestone_by_name_or_number(milestone_config["name"])
258
+ end
255
259
  end
256
260
  end
257
261
 
@@ -276,13 +280,14 @@ module Commenter
276
280
  def update_yaml_with_github_info(yaml_file, comment_sheet, results, options)
277
281
  # Update comments with GitHub information
278
282
  results.each do |result|
279
- next unless result[:status] == :created
283
+ next unless result[:issue_number]
280
284
 
281
285
  comment = comment_sheet.comments.find { |c| c.id == result[:comment_id] }
282
286
  next unless comment
283
287
 
284
288
  comment.record_github_issue(issue_number: result[:issue_number], issue_url: result[:issue_url],
285
- status: "open", created_at: Time.now.utc.iso8601)
289
+ status: result[:status] == :created ? "open" : comment.github_status,
290
+ created_at: comment.github_created_at || Time.now.utc.iso8601)
286
291
  end
287
292
 
288
293
  # Write updated YAML
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Commenter
4
+ # Synchronizes the comment YAML with GitHub issues, treating the YAML as
5
+ # the source of truth: comments without an issue get one, issue content is
6
+ # refreshed from the YAML (per the conflict policy), and issues whose
7
+ # comment has a recorded disposition can be closed.
8
+ class GitHubSync < GitHubIssueCreator
9
+ # Returns the sync actions for one comment by comparing the rendered
10
+ # issue content against the issue's current state. Pure — no network —
11
+ # so the conflict matrix is directly specifiable.
12
+ #
13
+ # conflict policy:
14
+ # yaml - re-render title/body over the issue (YAML wins)
15
+ # github - leave differing issue content untouched (GitHub wins)
16
+ # skip - take no action on differing content, report the conflict
17
+ def self.reconcile_actions(issue:, rendered:, disposition:, conflict: "yaml",
18
+ close_on_disposition: true)
19
+ actions = []
20
+ differing = [rendered[:title].to_s.strip, rendered[:body].to_s.strip] !=
21
+ [issue[:title].to_s.strip, issue[:body].to_s.strip]
22
+
23
+ if differing
24
+ case conflict
25
+ when "yaml" then actions << :update_content
26
+ when "github" then actions << :keep_github_content
27
+ when "skip" then actions << :conflict
28
+ end
29
+ end
30
+
31
+ actions << :close if close_on_disposition && issue[:state] == "open" && disposition && !disposition.strip.empty?
32
+
33
+ actions
34
+ end
35
+
36
+ def sync(yaml_file, options = {})
37
+ comment_sheet = CommentSheet.from_yaml(File.read(yaml_file))
38
+ comment_sheet.stage = options[:stage] if options[:stage]
39
+
40
+ results = comment_sheet.comments.map do |comment|
41
+ options[:dry_run] ? plan(comment, options) : reconcile(comment, comment_sheet, options)
42
+ end
43
+
44
+ update_yaml_with_github_info(yaml_file, comment_sheet, results, options) unless options[:dry_run]
45
+ results
46
+ end
47
+
48
+ private
49
+
50
+ # Dry-run preview, offline: comments with a known issue are planned from
51
+ # the last state recorded in the YAML, not from a live fetch.
52
+ def plan(comment, options)
53
+ return { comment_id: comment.id, actions: [:create] } unless comment.has_github_issue?
54
+
55
+ actions = [:reconcile]
56
+ if close_on_disposition?(options) && comment.github_status == "open" &&
57
+ comment.observations && !comment.observations.strip.empty?
58
+ actions << :close
59
+ end
60
+ { comment_id: comment.id, issue_number: comment.github_issue_number, actions: actions }
61
+ end
62
+
63
+ def reconcile(comment, comment_sheet, options)
64
+ issue = find_existing_issue(comment, comment_sheet)
65
+ return create_issue(comment, comment_sheet, options) unless issue
66
+
67
+ title = @title_template.render(template_variables(comment, comment_sheet))
68
+ body = @body_template.render(template_variables(comment, comment_sheet))
69
+ actions = self.class.reconcile_actions(
70
+ issue: { title: issue.title, body: issue.body, state: issue.state },
71
+ rendered: { title: title, body: body },
72
+ disposition: comment.observations,
73
+ conflict: options[:conflict] || "yaml",
74
+ close_on_disposition: close_on_disposition?(options)
75
+ )
76
+
77
+ apply(comment, issue, title, body, actions)
78
+ rescue Octokit::Error => e
79
+ { comment_id: comment.id, status: :error, message: e.message }
80
+ end
81
+
82
+ def apply(comment, issue, title, body, actions)
83
+ result = { comment_id: comment.id, issue_number: issue.number, status: :unchanged, actions: actions }
84
+ comment.record_github_issue(issue_number: issue.number, issue_url: issue.html_url, status: issue.state)
85
+
86
+ if actions.include?(:update_content)
87
+ @github_client.update_issue(@repo, issue.number, title: title, body: body)
88
+ result[:status] = :updated
89
+ end
90
+
91
+ if actions.include?(:close)
92
+ @github_client.close_issue(@repo, issue.number)
93
+ comment.github.status = "closed"
94
+ comment.github.updated_at = Time.now.utc.iso8601
95
+ result[:status] = actions.include?(:update_content) ? :updated_and_closed : :closed
96
+ end
97
+
98
+ result
99
+ end
100
+
101
+ def close_on_disposition?(options)
102
+ options.fetch(:close_on_disposition, true)
103
+ end
104
+ end
105
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Commenter
4
- VERSION = "0.4.0"
4
+ VERSION = "0.5.0"
5
5
  end
data/lib/commenter.rb CHANGED
@@ -13,6 +13,7 @@ module Commenter
13
13
  autoload :DispositionStatus, "commenter/disposition_status"
14
14
  autoload :Ballot, "commenter/ballot"
15
15
  autoload :BallotReport, "commenter/ballot_report"
16
+ autoload :BallotDiff, "commenter/ballot_diff"
16
17
  autoload :Comment, "commenter/comment"
17
18
  autoload :CommentSheet, "commenter/comment_sheet"
18
19
  autoload :GitHubSession, "commenter/github_session"
@@ -20,4 +21,5 @@ module Commenter
20
21
  autoload :Filler, "commenter/filler"
21
22
  autoload :GitHubIssueCreator, "commenter/github_integration"
22
23
  autoload :GitHubIssueRetriever, "commenter/github_integration"
24
+ autoload :GitHubSync, "commenter/github_sync"
23
25
  end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe Commenter::BallotDiff do
6
+ def sheet(comments)
7
+ Commenter::CommentSheet.new(comments: comments)
8
+ end
9
+
10
+ def comment(id, text: "Text for #{id}", observations: nil)
11
+ Commenter::Comment.new(id: id, body: id.split("-").first, comments: text, observations: observations)
12
+ end
13
+
14
+ let(:cd) do
15
+ sheet([
16
+ comment("DE-001"),
17
+ comment("DE-002", text: "Old wording"),
18
+ comment("US-001")
19
+ ])
20
+ end
21
+
22
+ let(:dis) do
23
+ sheet([
24
+ comment("DE-001"),
25
+ comment("DE-002", text: "Revised wording"),
26
+ comment("US-001", observations: "Accepted."),
27
+ comment("JP-001")
28
+ ])
29
+ end
30
+
31
+ it "classifies new, withdrawn, repeated, and revised comments" do
32
+ result = described_class.diff(cd, dis)
33
+
34
+ expect(result[:new]).to eq(["JP-001"])
35
+ expect(result[:withdrawn]).to eq([]) # US-001 present in both
36
+ expect(result[:repeated]).to eq(%w[DE-001 US-001])
37
+ expect(result[:revised]).to eq(["DE-002"])
38
+ end
39
+
40
+ it "counts comments with a disposition in the later stage as resolved" do
41
+ result = described_class.diff(cd, dis)
42
+
43
+ expect(result[:resolved]).to eq(["US-001"])
44
+ end
45
+
46
+ it "reports withdrawn comments" do
47
+ result = described_class.diff(dis, cd)
48
+
49
+ expect(result[:withdrawn]).to eq(["JP-001"])
50
+ expect(result[:new]).to eq([])
51
+ end
52
+
53
+ it "renders a markdown table" do
54
+ markdown = described_class.to_markdown(cd, dis)
55
+
56
+ expect(markdown).to start_with("| Category | Count | Comments |")
57
+ expect(markdown).to include("| New | 1 | JP-001 |")
58
+ expect(markdown).to include("| Revised | 1 | DE-002 |")
59
+ expect(markdown).to include("Resolved (disposition recorded in the later stage): 1")
60
+ end
61
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+ require "tempfile"
5
+
6
+ RSpec.describe Commenter::GitHubSync do
7
+ describe ".reconcile_actions" do
8
+ def actions(**overrides)
9
+ described_class.reconcile_actions(
10
+ issue: { title: "Title", body: "Body", state: "open" },
11
+ rendered: { title: "Title", body: "Body" },
12
+ disposition: nil,
13
+ **overrides
14
+ )
15
+ end
16
+
17
+ it "reports nothing when the issue is in sync" do
18
+ expect(actions).to eq([])
19
+ end
20
+
21
+ it "re-renders differing content under the yaml policy" do
22
+ expect(actions(conflict: "yaml", issue: { title: "Title", body: "Old body", state: "open" })).to eq([:update_content])
23
+ end
24
+
25
+ it "keeps differing GitHub content under the github policy" do
26
+ expect(actions(conflict: "github", issue: { title: "Title", body: "Edited on GitHub", state: "open" }))
27
+ .to eq([:keep_github_content])
28
+ end
29
+
30
+ it "reports a conflict under the skip policy" do
31
+ expect(actions(conflict: "skip", issue: { title: "Edited on GitHub", body: "Body", state: "open" })).to eq([:conflict])
32
+ end
33
+
34
+ it "closes an open issue whose comment has a disposition" do
35
+ expect(actions(disposition: "Accepted.")).to eq([:close])
36
+ end
37
+
38
+ it "combines content update and close" do
39
+ expect(actions(issue: { title: "Title", body: "Old", state: "open" }, disposition: "Accepted.")).to eq(%i[update_content close])
40
+ end
41
+
42
+ it "does not close a closed issue, an undispositioned comment, or when disabled" do
43
+ expect(actions(disposition: "Accepted.", issue: { title: "Title", body: "Body", state: "closed" })).to eq([])
44
+ expect(actions(disposition: " ")).to eq([])
45
+ expect(actions(disposition: "Accepted.", close_on_disposition: false)).to eq([])
46
+ end
47
+
48
+ it "ignores whitespace when comparing content" do
49
+ expect(actions(issue: { title: "Title", body: " Body ", state: "open" })).to eq([])
50
+ end
51
+ end
52
+
53
+ describe "#sync dry run" do
54
+ let(:config_file) do
55
+ file = Tempfile.new(["config", ".yaml"])
56
+ file.write({ "github" => { "repository" => "test-org/test-repo", "token" => "test-token" } }.to_yaml)
57
+ file.close
58
+ file
59
+ end
60
+
61
+ def yaml_file(comments)
62
+ file = Tempfile.new(["comments", ".yaml"])
63
+ file.write({ "version" => "2012-03", "stage" => "DIS", "comments" => comments }.to_yaml)
64
+ file.close
65
+ file
66
+ end
67
+
68
+ after { config_file.unlink }
69
+
70
+ it "plans creation for comments without issues and closure for dispositioned open ones" do
71
+ input = yaml_file([
72
+ { "id" => "US-001", "body" => "US", "comments" => "No issue yet" },
73
+ { "id" => "US-002", "body" => "US", "comments" => "Resolved", "observations" => "Accepted.",
74
+ "github" => { "issue_number" => 12, "status" => "open" } }
75
+ ])
76
+
77
+ sync = described_class.new(config_file.path)
78
+ results = sync.sync(input.path, dry_run: true)
79
+
80
+ expect(results[0]).to eq(comment_id: "US-001", actions: [:create])
81
+ expect(results[1][:issue_number]).to eq(12)
82
+ expect(results[1][:actions]).to include(:close)
83
+
84
+ input.unlink
85
+ end
86
+ end
87
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: commenter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose
@@ -152,6 +152,7 @@ files:
152
152
  - exe/commenter
153
153
  - lib/commenter.rb
154
154
  - lib/commenter/ballot.rb
155
+ - lib/commenter/ballot_diff.rb
155
156
  - lib/commenter/ballot_report.rb
156
157
  - lib/commenter/cli.rb
157
158
  - lib/commenter/comment.rb
@@ -163,6 +164,7 @@ files:
163
164
  - lib/commenter/github_info.rb
164
165
  - lib/commenter/github_integration.rb
165
166
  - lib/commenter/github_session.rb
167
+ - lib/commenter/github_sync.rb
166
168
  - lib/commenter/parser.rb
167
169
  - lib/commenter/parser/osd_xlsx_parser.rb
168
170
  - lib/commenter/parser/track_change_docx_parser.rb
@@ -170,6 +172,7 @@ files:
170
172
  - schema/iso_comment_2012-03.yaml
171
173
  - schema/iso_comment_osd.yaml
172
174
  - sig/commenter.rbs
175
+ - spec/commenter/ballot_diff_spec.rb
173
176
  - spec/commenter/ballot_report_spec.rb
174
177
  - spec/commenter/ballot_spec.rb
175
178
  - spec/commenter/cli_spec.rb
@@ -179,6 +182,7 @@ files:
179
182
  - spec/commenter/disposition_status_spec.rb
180
183
  - spec/commenter/filler_spec.rb
181
184
  - spec/commenter/github_integration_spec.rb
185
+ - spec/commenter/github_sync_spec.rb
182
186
  - spec/commenter/osd_xlsx_parser_spec.rb
183
187
  - spec/commenter/track_change_docx_parser_spec.rb
184
188
  - spec/commenter_spec.rb