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

Sign up to get free protection for your applications and to get access to all the features.
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: