upstreamwatchr 0.0.42
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/Gemfile +3 -0
- data/README.md +31 -0
- data/bin/upstreamwatchr +43 -0
- data/lib/UpstreamWatchr.rb +8 -0
- data/lib/UpstreamWatchr/gitlabcheck.rb +96 -0
- data/lib/UpstreamWatchr/gitremotes.rb +103 -0
- data/lib/UpstreamWatchr/version.rb +3 -0
- data/upstreamwatchr.gemspec +24 -0
- metadata +112 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e1ec7dfa70e08c99094546080e6180ea953a033e
|
4
|
+
data.tar.gz: 8acfdf0d9507374a9cd4258b1855ba7659c312a8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8fd2ea5f6c64dcc7069e51e85cd0ca3f0869d4a249281f301c7d472d85abd37981cfd41aa123be80e9801b81bb187538d61d2ce8b6b755cb8b59c1da8686dd88
|
7
|
+
data.tar.gz: 368f85a18be9087b18e205ba12e885849d1c43c39ca38e24a945243a9488514a5c16a56c82f5e83a1d5df1c1e4feb1dc0ad108b091c220b93e3c886e67b3f2eb
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# UpstreamWatchr
|
2
|
+
makes it easy to keep track of changes in the upstream repositories of your forks by comparing two git remotes and creating an issue on your fork if it is out of sync.
|
3
|
+
|
4
|
+
|
5
|
+
Currently only GitLab is supported (api wise). This means that it will fail (horribly) if you are using something else for your repos. It also has some requirements on your projects, such as that its needs a hyperlink in your projects `description` to find the upstream repository. Also your project should allow upstreamwatchr's user to read the code and create issues. If your project is not setup this way, UpstreamWatchr will fail, again, horribly.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'upstreamwatchr'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install upstreamwatchr
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
Set `ENV['GITLAB_API_ENDPOINT']` and `ENV['GITLAB_API_PRIVATE_TOKEN']` and use the "binary":
|
24
|
+
```
|
25
|
+
# Check a single project
|
26
|
+
GITLAB_API_ENDPOINT=https://git.acme.com/ GITLAB_API_PRIVATE_TOKEN=abcdefg123456 upstreamwatchr git@githost.acme.com:/path/repo.git
|
27
|
+
```
|
28
|
+
```
|
29
|
+
# Check all available projects
|
30
|
+
GITLAB_API_ENDPOINT=https://git.acme.com/ GITLAB_API_PRIVATE_TOKEN=abcdefg123456 upstreamwatchr"
|
31
|
+
```
|
data/bin/upstreamwatchr
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
#! ruby
|
2
|
+
require 'rainbow/ext/string'
|
3
|
+
|
4
|
+
lib = File.expand_path('../../lib', __FILE__)
|
5
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
6
|
+
require 'upstreamwatchr'
|
7
|
+
|
8
|
+
unless ENV['GITLAB_API_ENDPOINT'] && ENV['GITLAB_API_PRIVATE_TOKEN']
|
9
|
+
puts "Usage:
|
10
|
+
GITLAB_API_ENDPOINT=https://git.acme.com/ GITLAB_API_PRIVATE_TOKEN=abcdefg123456 upstreamwatchr git@githost.acme.com:/path/repo.git
|
11
|
+
or to check all available projects:
|
12
|
+
GITLAB_API_ENDPOINT=https://git.acme.com/ GITLAB_API_PRIVATE_TOKEN=abcdefg123456 upstreamwatchr"
|
13
|
+
exit 1
|
14
|
+
end
|
15
|
+
|
16
|
+
if not ARGV[0]
|
17
|
+
puts "Debug: Not called with a specific project. Checking all I can haz access to!" if ENV['DEBUG']
|
18
|
+
projects = Gitlab.projects(:per_page => 10000)
|
19
|
+
my_uid = Gitlab.user.id
|
20
|
+
projects.select! {|p| ((not p.owner) || p.owner.id != my_uid)}
|
21
|
+
projects.each do |project|
|
22
|
+
begin
|
23
|
+
watch = UpstreamWatchr::GitLabWatchr.new(project.ssh_url_to_repo)
|
24
|
+
info = "Info: #{project.name} (#{project.ssh_url_to_repo}): #{watch.comparator.to_s}"
|
25
|
+
if watch.comparator.has_changes?
|
26
|
+
puts info.color(:red)
|
27
|
+
watch.grumble_in_issue
|
28
|
+
else
|
29
|
+
puts info.color(:green)
|
30
|
+
end
|
31
|
+
rescue => e
|
32
|
+
STDERR.puts "Warn: Failed to check #{project.name} (#{project.ssh_url_to_repo}!"
|
33
|
+
require 'pry'; binding.pry if ENV['DEBUG']
|
34
|
+
STDERR.puts e
|
35
|
+
end
|
36
|
+
end
|
37
|
+
else
|
38
|
+
watch = UpstreamWatchr::GitLabWatchr.new(ARGV[0])
|
39
|
+
puts "Debug: #{project.comparator.to_s}" if ENV['DEBUG']
|
40
|
+
if watch.comparator.has_changes?
|
41
|
+
watch.grumble_in_issue
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'gitlab'
|
2
|
+
|
3
|
+
module UpstreamWatchr
|
4
|
+
class GitLabWatchr
|
5
|
+
attr_reader :origin_url, :origin_project, :comparator
|
6
|
+
|
7
|
+
def initialize(origin_url, branch = 'master')
|
8
|
+
@origin_url = origin_url
|
9
|
+
@branch = branch
|
10
|
+
@comparator = GitRemotes.new(@origin_url, upstream_url)
|
11
|
+
end
|
12
|
+
|
13
|
+
def origin_project
|
14
|
+
@origin_project ||= Gitlab.projects(:per_page => 10000).find {|p| p.ssh_url_to_repo == @origin_url || p.http_url_to_repo == @origin_url || p.web_url == @origin_url }
|
15
|
+
end
|
16
|
+
|
17
|
+
def upstream_url
|
18
|
+
@upstream_url ||= origin_project.description.match(/^Upstream: (\S*)$/)[1]
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_fork
|
22
|
+
puts "Forking #{origin_project.name}..."
|
23
|
+
Gitlab.create_fork(@origin_project.id)
|
24
|
+
rescue Gitlab::Error::Conflict
|
25
|
+
puts "Fork of #{origin_project.name} already exists."
|
26
|
+
end
|
27
|
+
|
28
|
+
def fork
|
29
|
+
@fork ||= (Gitlab.projects(:per_page => 10000).find {|p| p.forked_from_project && p.forked_from_project.id == @origin_project.id } || create_fork)
|
30
|
+
end
|
31
|
+
|
32
|
+
def create_merge_request
|
33
|
+
@comparator.push_to_my_fork(fork.ssh_url_to_repo)
|
34
|
+
|
35
|
+
puts "Creating merge request on #{origin_project.name}..."
|
36
|
+
Gitlab.create_merge_request(fork.id,
|
37
|
+
'UpstreamWatcher detected new upstream changes!',
|
38
|
+
:source_branch => @branch,
|
39
|
+
:target_branch => @branch,
|
40
|
+
:target_project_id => origin_project.id
|
41
|
+
)
|
42
|
+
rescue Gitlab::Error::Conflict
|
43
|
+
puts "Merge request seems to be already there. Good. Previous MR updated with even more upstream changes!"
|
44
|
+
end
|
45
|
+
|
46
|
+
def issue_title
|
47
|
+
"UpstreamWatchr: #{@comparator.upstream_ahead} change(s) to fetch from your upstream project!"
|
48
|
+
end
|
49
|
+
|
50
|
+
def issue_description
|
51
|
+
"UpstreamWatchr noted that your branch '#{@branch}' is #{@comparator.upstream_ahead} commits behind `#{upstream_url}`'s #{@branch}.
|
52
|
+
|
53
|
+
Whenenver you feel confident pulling the upstream changes onto your branch, you can use these git commands:
|
54
|
+
|
55
|
+
```
|
56
|
+
git checkout #{@branch}
|
57
|
+
git pull #{upstream_url} #{@branch}
|
58
|
+
git push origin #{@branch}
|
59
|
+
```
|
60
|
+
|
61
|
+
|
62
|
+
Sincerly, UpstreamWatchr"
|
63
|
+
end
|
64
|
+
|
65
|
+
def issue
|
66
|
+
@issue ||= find_issue || create_issue
|
67
|
+
end
|
68
|
+
|
69
|
+
def find_issue
|
70
|
+
Gitlab.issues(@origin_project.id, :per_page => 1000).find {|i| i.state != 'closed' && i.author.id == Gitlab.user.id && i.title =~ /^UpstreamWatchr:/}
|
71
|
+
end
|
72
|
+
|
73
|
+
def create_issue
|
74
|
+
puts "Warn: Creating new issue '#{issue_title}'"
|
75
|
+
Gitlab.create_issue(@origin_project.id,
|
76
|
+
issue_title,
|
77
|
+
{:description => issue_description}
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
81
|
+
def update_issue
|
82
|
+
puts "Debug: Updating issue '#{issue_title}'" if ENV['DEBUG']
|
83
|
+
Gitlab.edit_issue(@origin_project.id, issue.id, :title => issue_title)
|
84
|
+
end
|
85
|
+
|
86
|
+
def grumble_in_issue
|
87
|
+
changes_in_title = issue.title.match(/(\d+)/)[1].to_i
|
88
|
+
puts "Debug: Issue states '#{changes_in_title} changes'" if ENV['DEBUG']
|
89
|
+
unless changes_in_title == @comparator.upstream_ahead
|
90
|
+
puts "Warn: Going to update issue to '#{issue_title}'"
|
91
|
+
update_issue
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'rugged'
|
2
|
+
require 'tmpdir'
|
3
|
+
|
4
|
+
module UpstreamWatchr
|
5
|
+
class GitRemotes
|
6
|
+
|
7
|
+
def initialize(origin, upstream, main_branch = 'master')
|
8
|
+
@origin = origin
|
9
|
+
@upstream = upstream
|
10
|
+
@main_branch = main_branch
|
11
|
+
|
12
|
+
begin
|
13
|
+
@repo = Rugged::Repository.clone_at(@origin, clone_path, {:bare => true, :credentials => ssh_agent})
|
14
|
+
puts "Debug: cloned #{@origin} to #{clone_path}" if ENV['DEBUG']
|
15
|
+
rescue Rugged::InvalidError
|
16
|
+
puts "Debug: didn't clone (#{@origin} to #{clone_path}), local repo already there." if ENV['DEBUG']
|
17
|
+
@repo = Rugged::Repository.new(clone_path)
|
18
|
+
@repo.remotes['origin'].fetch({:credentials => ssh_agent})
|
19
|
+
@repo.remotes['origin'].save
|
20
|
+
puts "Debug: fetched 'origin' from #{origin}" if ENV['DEBUG']
|
21
|
+
end
|
22
|
+
|
23
|
+
@repo.remotes.create('upstream', @upstream) unless @repo.remotes['upstream']
|
24
|
+
fail "Can not read from upstream. Something seems off with remote #{@upstream}" unless @repo.remotes['upstream'].check_connection(:fetch, {:credentials => ssh_agent})
|
25
|
+
@repo.remotes['upstream'].fetch({:credentials => ssh_agent})
|
26
|
+
@repo.remotes['upstream'].save
|
27
|
+
puts "Debug: fetched 'upstream' from #{@upstream}" if ENV['DEBUG']
|
28
|
+
end
|
29
|
+
|
30
|
+
def ssh_agent
|
31
|
+
lambda { |_, username, _| Rugged::Credentials::SshKeyFromAgent.new(username: username)}
|
32
|
+
end
|
33
|
+
|
34
|
+
def clone_path
|
35
|
+
File.join(Dir.tmpdir, @origin.split('/').last)
|
36
|
+
end
|
37
|
+
|
38
|
+
def push_to_my_fork(my_fork_url, branch = nil)
|
39
|
+
branch ||= @main_branch
|
40
|
+
@repo.remotes.create('my_fork', my_fork_url) unless @repo.remotes['my_fork']
|
41
|
+
|
42
|
+
@repo.reset("upstream/#{branch}", :soft)
|
43
|
+
@repo.remotes["my_fork"].push("refs/heads/#{branch}", {:credentials => ssh_agent})
|
44
|
+
end
|
45
|
+
|
46
|
+
def diff(branch = nil)
|
47
|
+
branch ||= @main_branch
|
48
|
+
origin_target = @repo.references["refs/remotes/origin/#{branch}"].target
|
49
|
+
puts "Debug: origin has its #{branch} at #{origin_target}" if ENV['DEBUG']
|
50
|
+
upstream_target = @repo.references["refs/remotes/upstream/#{branch}"].target
|
51
|
+
puts "Debug: upstream has its #{branch} at #{upstream_target}" if ENV['DEBUG']
|
52
|
+
@repo.diff(origin_target, upstream_target)
|
53
|
+
end
|
54
|
+
|
55
|
+
def ahead_behind(branch = nil)
|
56
|
+
branch ||= @main_branch
|
57
|
+
@repo.ahead_behind(@repo.references["refs/remotes/origin/#{branch}"].target, @repo.references["refs/remotes/upstream/#{branch}"].target)
|
58
|
+
end
|
59
|
+
|
60
|
+
def upstream_ahead
|
61
|
+
ahead_behind[1]
|
62
|
+
end
|
63
|
+
|
64
|
+
def upstream_behind
|
65
|
+
ahead_behind[0]
|
66
|
+
end
|
67
|
+
|
68
|
+
def trees(branch = nil)
|
69
|
+
branch ||= @main_branch
|
70
|
+
[@repo.references["refs/remotes/origin/#{branch}"].target.tree, @repo.references["refs/remotes/upstream/#{branch}"].target.tree]
|
71
|
+
end
|
72
|
+
|
73
|
+
def origin_tree_size(branch = nil)
|
74
|
+
branch ||= @main_branch
|
75
|
+
trees(branch)[0].count
|
76
|
+
end
|
77
|
+
|
78
|
+
def upstream_tree_size(branch = nil)
|
79
|
+
branch ||= @main_branch
|
80
|
+
trees(branch)[1].count
|
81
|
+
end
|
82
|
+
|
83
|
+
def tree_size_diff(branch = nil)
|
84
|
+
branch ||= @main_branch
|
85
|
+
upstream_tree_size(branch) - origin_tree_size(branch)
|
86
|
+
end
|
87
|
+
|
88
|
+
def has_changes?
|
89
|
+
ahead_behind != [0,0]
|
90
|
+
end
|
91
|
+
|
92
|
+
def to_s(branch = nil)
|
93
|
+
branch ||= @main_branch
|
94
|
+
a_b = ahead_behind(branch)
|
95
|
+
if a_b == [0,0]
|
96
|
+
"origin/#{branch} is up-to-date with 'upstream/#{branch}'"
|
97
|
+
else
|
98
|
+
"upstream/#{branch} is #{upstream_ahead} ahead and #{upstream_behind} behind 'origin/#{branch}'"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'upstreamwatchr/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "upstreamwatchr"
|
8
|
+
spec.version = UpstreamWatchr::VERSION
|
9
|
+
spec.authors = ["Marvin Frick"]
|
10
|
+
spec.email = ["marvin.frick@sinnerschrader.com"]
|
11
|
+
spec.summary = %q{watches "upstream" repositories for changes}
|
12
|
+
spec.description = %q{upstreamwatchr makes it easy to keep track of changes in the upstream repositories of your forks by comparing two git remotes and creating an issue on your fork if it is out of sync.}
|
13
|
+
spec.homepage = "https://github.com/MrMarvin/upstreamwatchr"
|
14
|
+
spec.license = "BSD"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
spec.add_development_dependency 'pry', '0.10.1'
|
21
|
+
spec.add_runtime_dependency 'rugged', '0.22.2'
|
22
|
+
spec.add_runtime_dependency 'gitlab', '3.4.0'
|
23
|
+
spec.add_runtime_dependency 'rainbow', '2.0.0'
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: upstreamwatchr
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.42
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Marvin Frick
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-06-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: pry
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.10.1
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.10.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rugged
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.22.2
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.22.2
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: gitlab
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 3.4.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 3.4.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rainbow
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.0.0
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 2.0.0
|
69
|
+
description: upstreamwatchr makes it easy to keep track of changes in the upstream
|
70
|
+
repositories of your forks by comparing two git remotes and creating an issue on
|
71
|
+
your fork if it is out of sync.
|
72
|
+
email:
|
73
|
+
- marvin.frick@sinnerschrader.com
|
74
|
+
executables:
|
75
|
+
- upstreamwatchr
|
76
|
+
extensions: []
|
77
|
+
extra_rdoc_files: []
|
78
|
+
files:
|
79
|
+
- ".gitignore"
|
80
|
+
- Gemfile
|
81
|
+
- README.md
|
82
|
+
- bin/upstreamwatchr
|
83
|
+
- lib/UpstreamWatchr.rb
|
84
|
+
- lib/UpstreamWatchr/gitlabcheck.rb
|
85
|
+
- lib/UpstreamWatchr/gitremotes.rb
|
86
|
+
- lib/UpstreamWatchr/version.rb
|
87
|
+
- upstreamwatchr.gemspec
|
88
|
+
homepage: https://github.com/MrMarvin/upstreamwatchr
|
89
|
+
licenses:
|
90
|
+
- BSD
|
91
|
+
metadata: {}
|
92
|
+
post_install_message:
|
93
|
+
rdoc_options: []
|
94
|
+
require_paths:
|
95
|
+
- lib
|
96
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
requirements: []
|
107
|
+
rubyforge_project:
|
108
|
+
rubygems_version: 2.2.2
|
109
|
+
signing_key:
|
110
|
+
specification_version: 4
|
111
|
+
summary: watches "upstream" repositories for changes
|
112
|
+
test_files: []
|