gh-pr-backport 0.1.0.alpha2 → 0.1.0.alpha3

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: 5d1c05225c80e6b714a85021a0918e18c050a5cde87251b1c6818b0f80e26ef0
4
- data.tar.gz: 940d33dba73c8c043604a868bcea44269f4f19d14c11474c64650bf2acd61721
3
+ metadata.gz: 3fd9ac1d7f61467e3a140371a8ccf44623c929458e5e81653131ecbff31dc12d
4
+ data.tar.gz: f40d8da0820b9c29eb1e393c17a5a7a7f4ba00eb69fad170e16ffb3e633285f0
5
5
  SHA512:
6
- metadata.gz: d3be7947fd595a682d309db9b1c236df2ec94239ca238db3f06aec9b0fae93173941191960362b8ffaa1fa0dfad1b1ae703b32d3cb1215bf044f6a99b268c1af
7
- data.tar.gz: 307f651897173c79379ba41aa4b8b64043aec1127c7e76f3592f0e310e39a43806c45e919ea55b8cc13fb0383025fcad6e50b537284c5f722e7145560d81d20b
6
+ metadata.gz: 5c6feefcaedf0e35e70fc0995553342b980c6e3128c3c7140adc62eb824773e8c7e47d3bfb28d54b8c8ff19c3adeb2cf40caef663025da54558de161a76a5d7e
7
+ data.tar.gz: 5bbda3418e08593d87ee9c1d21106140f0158af05c182159233f26edf5c4e0aa94d4d1ade4a1d5b767a800c07078fd60f00a98b71cecbfc9d5059ae14f2f6642
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- gh-pr-backport (0.1.0.alpha2)
4
+ gh-pr-backport (0.1.0.alpha3)
5
5
  hashie (~> 3.5, >= 3.5.7)
6
6
  octokit (~> 4.8, >= 4.8.0)
7
7
 
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'gh_pr_backport'
4
+
5
+ backporter = GhPrBackport::AtOnceBackporter.new
6
+ backporter.run
@@ -8,4 +8,6 @@ module GhPrBackport
8
8
  class PushFailed < StandardError; end
9
9
 
10
10
  autoload :Backporter, 'gh_pr_backport/backporter'
11
+ autoload :AtOnceBackporter, 'gh_pr_backport/at_once_backporter'
12
+ autoload :Common, 'gh_pr_backport/common'
11
13
  end
@@ -0,0 +1,94 @@
1
+ require 'octokit'
2
+
3
+ module GhPrBackport
4
+ class AtOnceBackporter
5
+ include GhPrBackport::Common
6
+
7
+ def run
8
+ raise GhPrBackport::DirectoryIsMessy unless clean?
9
+ create_todo_file
10
+ open_editor
11
+ run_todos_from_file
12
+ end
13
+
14
+ def open_editor
15
+ unless system("#{optional_str ENV['EDITOR'], 'vim'} #{todo_file}")
16
+ raise 'Editor returned non-zero exit status.'
17
+ end
18
+ end
19
+
20
+ def run_todos_from_file
21
+ todo_commits = read_todo_file
22
+ if todo_commits.empty?
23
+ puts 'Nothing to do.'
24
+ return
25
+ end
26
+ system("git cherry-pick -m 1 -x #{todo_commits.join(' ')}")
27
+ end
28
+
29
+ def list_todo_prs
30
+ picked_pr_numbers = list_picked_prs.map { |commit| commit[:pr_number] }.to_set
31
+ list_merged_prs.select { |commit| !picked_pr_numbers.include?(commit[:pr_number]) }
32
+ end
33
+
34
+ def list_merged_prs
35
+ parse_log_stream(`git log --merges #{log_format_option} HEAD..origin/HEAD`)
36
+ .select { |commit| commit[:pr_number] }
37
+ end
38
+
39
+ def list_picked_prs
40
+ parse_log_stream(`git log #{log_format_option} origin/HEAD..HEAD`)
41
+ .select { |commit| commit[:pr_number] }
42
+ end
43
+
44
+ private
45
+
46
+ def git_dir
47
+ optional_str ENV['GIT_DIR'], '.git'
48
+ end
49
+
50
+ def todo_file
51
+ File.join(git_dir, '.git-pr-backport-todo')
52
+ end
53
+
54
+ def read_todo_file
55
+ File.readlines(todo_file)
56
+ .map(&:strip)
57
+ .reject { |line| line == '' || line.start_with?('#') }
58
+ .each_with_index { |line, i| raise "Parse error in line #{i + 1}: #{line}" unless line =~ /^[0-9A-Za-z]+(?:\s|\z)/ }
59
+ .map { |line| line.split(/\s+/, 2)[0] }
60
+ end
61
+
62
+ def create_todo_file
63
+ File.write(todo_file, list_todo_prs.map do |commit|
64
+ "# #{commit[:hash][0..7]} ##{commit[:pr_number]} #{commit[:body].lines[0]} (#{pr_to_link(commit)})"
65
+ end.join("\n"))
66
+ end
67
+
68
+ def pr_to_link(commit)
69
+ "#{repo.url}/pull/#{commit[:pr_number]}"
70
+ end
71
+
72
+ def log_format_option
73
+ # null terminated
74
+ '--format=format:%H%n%B%x00'
75
+ end
76
+
77
+ def parse_log_stream(stream)
78
+ stream
79
+ .split("\0\n")
80
+ .map { |commit_data| parse_commit_data(commit_data) }
81
+ .reverse
82
+ end
83
+
84
+ def parse_commit_data(commit_data)
85
+ hash, subject, body = commit_data.split("\n", 3).map(&:strip)
86
+ pr_number = subject.match(/\AMerge pull request #([1-9][0-9]*) from/)&.tap { |m| break m[1].to_i }
87
+ { hash: hash, pr_number: pr_number, subject: subject, body: body || '' }
88
+ end
89
+
90
+ def optional_str(str, default)
91
+ str == '' ? default : str || default
92
+ end
93
+ end
94
+ end
@@ -1,10 +1,11 @@
1
1
  require 'uri'
2
2
 
3
3
  require 'hashie'
4
- require 'octokit'
5
4
 
6
5
  module GhPrBackport
7
6
  class Backporter
7
+ include GhPrBackport::Common
8
+
8
9
  def initialize(original_pr_number:, staging_branch: nil)
9
10
  @client = client
10
11
  @repo = repo
@@ -39,16 +40,6 @@ module GhPrBackport
39
40
  end
40
41
  end
41
42
 
42
- def client
43
- client = Octokit::Client.new(access_token: `git config backport.token`.chomp)
44
- client.scopes # Validate token. If the token is invalid, then raise an error.
45
- client
46
- end
47
-
48
- def clean?
49
- `git status --porcelain`.chomp.empty?
50
- end
51
-
52
43
  def create_backport_pull_request
53
44
  body = "##{@original.number} のバックポートです\r\n\r\n" +
54
45
  @original.body.lines.map { |l| "> #{l}" }.join
@@ -87,16 +78,6 @@ module GhPrBackport
87
78
  `git config -f #{path}/.git-pr-release pr-release.branch.staging`.chomp
88
79
  end
89
80
 
90
- def repo
91
- remote = `git config remote.origin.url`.chomp
92
- repo_name = if remote.start_with?('git@github.com:')
93
- remote.split(':', 2)[1].sub(/.git$/, '')
94
- else
95
- URI.parse(remote).path.sub(%r{^\/}, '').sub(/.git$/, '')
96
- end
97
- Octokit::Repository.new(repo_name)
98
- end
99
-
100
81
  def system!(program, *args, error_class)
101
82
  raise error_class unless system(program, *args)
102
83
  true
@@ -0,0 +1,25 @@
1
+ require 'octokit'
2
+
3
+ module GhPrBackport
4
+ module Common
5
+ def client
6
+ client = Octokit::Client.new(access_token: `git config backport.token`.chomp)
7
+ client.scopes # Validate token. If the token is invalid, then raise an error.
8
+ client
9
+ end
10
+
11
+ def clean?
12
+ `git status --porcelain`.chomp.empty?
13
+ end
14
+
15
+ def repo
16
+ remote = `git config remote.origin.url`.chomp
17
+ repo_name = if remote.start_with?('git@github.com:')
18
+ remote.split(':', 2)[1].sub(/.git$/, '')
19
+ else
20
+ URI.parse(remote).path.sub(%r{^\/}, '').sub(/.git$/, '')
21
+ end
22
+ Octokit::Repository.new(repo_name)
23
+ end
24
+ end
25
+ end
@@ -1,3 +1,3 @@
1
1
  module GhPrBackport
2
- VERSION = '0.1.0.alpha2'.freeze
2
+ VERSION = '0.1.0.alpha3'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gh-pr-backport
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.alpha2
4
+ version: 0.1.0.alpha3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Takeru Naito
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-04-16 00:00:00.000000000 Z
11
+ date: 2018-04-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -111,6 +111,7 @@ email:
111
111
  - takeru.naito@gmail.com
112
112
  executables:
113
113
  - gh-pr-backport
114
+ - gh-pr-backport-at-once
114
115
  extensions: []
115
116
  extra_rdoc_files: []
116
117
  files:
@@ -127,9 +128,12 @@ files:
127
128
  - bin/console
128
129
  - bin/setup
129
130
  - exe/gh-pr-backport
131
+ - exe/gh-pr-backport-at-once
130
132
  - gh-pr-backport.gemspec
131
133
  - lib/gh_pr_backport.rb
134
+ - lib/gh_pr_backport/at_once_backporter.rb
132
135
  - lib/gh_pr_backport/backporter.rb
136
+ - lib/gh_pr_backport/common.rb
133
137
  - lib/gh_pr_backport/version.rb
134
138
  homepage: https://github.com/elim/gh-pr-backport
135
139
  licenses: