gurney_client 0.2.3 → 0.4.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 +4 -4
- data/CHANGELOG.md +9 -1
- data/Gemfile.lock +10 -8
- data/README.md +30 -21
- data/lib/gurney/api.rb +6 -5
- data/lib/gurney/cli.rb +90 -81
- data/lib/gurney/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6e7470d3ea652657be230f28b42b3035ee0e2c47122dc297533428831fc20b2b
|
4
|
+
data.tar.gz: 7e3bf5796d850cc8e600959a348f521d9d7ee10f5f7e329da391c0b125f57db0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c04cfc2febd10dcb6be12b4b8466f5c11d308954c57a7588a0bb41100f64eabc4656867af4d97d1c2d40ee607d5a3206e8fc3c0477021d04efd471c599abd540
|
7
|
+
data.tar.gz: cb327dd15bfd5aea5c30a87ff271555c17c9e29c8ec35379fa9bdb4ce3fd21b935e6ba80d1a0057abdd7f75b76d263da1089b209bce7576c857ca570f0f563e8
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# Gurney changelog
|
2
2
|
|
3
|
-
## 0.
|
3
|
+
## 0.4.0 (2024-11-15)
|
4
|
+
* Added: Reporting of the repository path as identifier. Should it ever change,
|
5
|
+
it is an indicator for an unchanged gurney.yml in a project fork, and the
|
6
|
+
API may respond with an error.
|
7
|
+
|
8
|
+
## 0.3.0 (2024-11-14)
|
9
|
+
* Added: Compatibility with Ruby 3
|
10
|
+
* Fixed: Support UTF-8 chars in branch names
|
4
11
|
|
12
|
+
## 0.2.3 (2023-05-24)
|
5
13
|
* Added: Suppress Bundler's "the Bundler version is older than the one in the lockfile" warning.
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
gurney_client (0.
|
4
|
+
gurney_client (0.4.0)
|
5
5
|
bundler (< 3)
|
6
6
|
colorize (~> 0.8)
|
7
7
|
git (~> 1.5)
|
@@ -10,22 +10,24 @@ PATH
|
|
10
10
|
GEM
|
11
11
|
remote: https://rubygems.org/
|
12
12
|
specs:
|
13
|
-
addressable (2.8.
|
14
|
-
public_suffix (>= 2.0.2, <
|
13
|
+
addressable (2.8.7)
|
14
|
+
public_suffix (>= 2.0.2, < 7.0)
|
15
15
|
byebug (11.0.1)
|
16
16
|
colorize (0.8.1)
|
17
17
|
diff-lcs (1.3)
|
18
|
-
git (1.
|
18
|
+
git (1.19.1)
|
19
19
|
addressable (~> 2.8)
|
20
20
|
rchardet (~> 1.8)
|
21
21
|
httparty (0.17.3)
|
22
22
|
mime-types (~> 3.0)
|
23
23
|
multi_xml (>= 0.5.2)
|
24
|
-
|
24
|
+
logger (1.6.1)
|
25
|
+
mime-types (3.6.0)
|
26
|
+
logger
|
25
27
|
mime-types-data (~> 3.2015)
|
26
|
-
mime-types-data (3.
|
28
|
+
mime-types-data (3.2024.1105)
|
27
29
|
multi_xml (0.6.0)
|
28
|
-
public_suffix (5.
|
30
|
+
public_suffix (5.1.1)
|
29
31
|
rake (13.0.1)
|
30
32
|
rchardet (1.8.0)
|
31
33
|
rspec (3.9.0)
|
@@ -52,4 +54,4 @@ DEPENDENCIES
|
|
52
54
|
rspec
|
53
55
|
|
54
56
|
BUNDLED WITH
|
55
|
-
2.
|
57
|
+
2.3.19
|
data/README.md
CHANGED
@@ -1,30 +1,31 @@
|
|
1
|
-
|
1
|
+
# Gurney
|
2
2
|
|
3
|
-
Gurney is a small tool to extract dependencies from project files and report
|
4
|
-
|
3
|
+
Gurney is a small tool to extract dependencies from project files and report
|
4
|
+
them to a web API. Modes:
|
5
|
+
- normal
|
6
|
+
- local pre-push hook
|
7
|
+
- remote post-receive hook
|
8
|
+
Usually, we configure the latter on our Git server to automatically run on each
|
9
|
+
push, with the API url passed as command line option.
|
5
10
|
|
6
|
-
When run as a
|
7
|
-
|
8
|
-
|
11
|
+
When run as a post-receive hook, Gurney will make a bare copy of the project and
|
12
|
+
look for a gurney.yml file. If present, Gurney looks at the configured branches
|
13
|
+
and collects their dependencies. These are reported to the web API.
|
9
14
|
|
10
|
-
|
15
|
+
## Usage
|
11
16
|
```
|
12
17
|
Usage: gurney [options]
|
13
|
-
--api-url [API URL]
|
14
|
-
|
15
|
-
|
16
|
-
--api-token [API TOKEN]
|
17
|
-
Token to be send to the api in the X-AuthToken header
|
18
|
+
--api-url [API URL] Url for web API call, can have parameters for <project_id> and <branch>
|
19
|
+
Example: --api-url "https://example.com/project/<project_id>/branch/<branch>"
|
20
|
+
--api-token [API TOKEN] Token to be sent to the API in the X-AuthToken header
|
18
21
|
-c, --config [CONFIG FILE] Config file to use
|
19
|
-
-h, --hook Run as a
|
20
|
-
--client-hook
|
21
|
-
|
22
|
-
|
23
|
-
--help
|
24
|
-
Prints this help
|
22
|
+
-h, --hook Run as a Git post-receive hook
|
23
|
+
--client-hook Run as a Git pre-push hook
|
24
|
+
-p, --project-id [PROJECT ID] Specify project id for API
|
25
|
+
--help Print this help
|
25
26
|
```
|
26
27
|
|
27
|
-
|
28
|
+
## Sample Config
|
28
29
|
```yaml
|
29
30
|
project_id: 1
|
30
31
|
branches:
|
@@ -34,5 +35,13 @@ api_url: http://example.com/dep_reporter/project/<project_id>/branch/<branch>
|
|
34
35
|
api_token: 1234567890
|
35
36
|
```
|
36
37
|
|
37
|
-
|
38
|
-
|
38
|
+
## Running as a global Git hook
|
39
|
+
See https://docs.gitlab.com/ee/administration/server_hooks.html#create-global-server-hooks-for-all-repositories
|
40
|
+
|
41
|
+
## Development
|
42
|
+
You can run Gurney locally from another directory like this:
|
43
|
+
|
44
|
+
```bash
|
45
|
+
cd some/real/project
|
46
|
+
ruby -I path/to/gurney/lib path/to/gurney/exe/gurney
|
47
|
+
```
|
data/lib/gurney/api.rb
CHANGED
@@ -9,13 +9,14 @@ module Gurney
|
|
9
9
|
@token = token
|
10
10
|
end
|
11
11
|
|
12
|
-
def post_dependencies(dependencies:, branch:, project_id:)
|
13
|
-
data = {
|
14
|
-
|
15
|
-
|
12
|
+
def post_dependencies(dependencies:, branch:, project_id:, repo_path: nil)
|
13
|
+
data = { dependencies: dependencies }
|
14
|
+
data[:repository_path] = repo_path if repo_path
|
15
|
+
|
16
16
|
url = base_url
|
17
17
|
url.gsub! '<project_id>', CGI.escape(project_id)
|
18
18
|
url.gsub! '<branch>', CGI.escape(branch)
|
19
|
+
|
19
20
|
post_json(url, data.to_json)
|
20
21
|
end
|
21
22
|
|
@@ -31,7 +32,7 @@ module Gurney
|
|
31
32
|
)
|
32
33
|
unless response.success?
|
33
34
|
if response.code == 404
|
34
|
-
raise ApiError.new("#{response.code}
|
35
|
+
raise ApiError.new("#{response.code} API url is probably wrong")
|
35
36
|
else
|
36
37
|
raise ApiError.new("#{response.code} #{response.body}")
|
37
38
|
end
|
data/lib/gurney/cli.rb
CHANGED
@@ -6,102 +6,116 @@ require 'git'
|
|
6
6
|
require 'fileutils'
|
7
7
|
|
8
8
|
module Gurney
|
9
|
+
|
10
|
+
class Error < StandardError; end
|
11
|
+
|
9
12
|
class CLI
|
10
13
|
HOOK_STDIN_REGEX = /(?<old>[0-9a-f]{40}) (?<new>[0-9a-f]{40}) refs\/heads\/(?<ref>\w+)/m
|
11
14
|
CLIENT_HOOK_STDIN_REGEX = /refs\/heads\/(?<ref>\w+) (?<new>[0-9a-f]{40}) refs\/heads\/(?<remote_ref>\w+) (?<remote_sha>[0-9a-f]{40})/m
|
12
15
|
MAIN_BRANCHES = ['master', 'main'].freeze
|
13
16
|
|
14
17
|
def self.run(cmd_parameter=[])
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
file = read_file(g, options.hook, branch, options.config_file)
|
28
|
-
break file if file
|
29
|
-
end
|
30
|
-
if !config_file && options.hook
|
31
|
-
# dont run as a hook with no config
|
32
|
-
exit 0
|
33
|
-
end
|
34
|
-
config_file ||= '---'
|
35
|
-
config = Gurney::Config.from_yaml(config_file)
|
36
|
-
|
37
|
-
options.branches ||= config&.branches
|
38
|
-
options.branches ||= config&.branches
|
39
|
-
options.api_token ||= config&.api_token
|
40
|
-
options.api_url ||= config&.api_url
|
41
|
-
options.project_id ||= config&.project_id
|
42
|
-
|
43
|
-
if [options.project_id, options.branches, options.api_url, options.api_token].any?(&:nil?)
|
44
|
-
raise Gurney::Error.new("Either provide in a config file or set the flags for project id, branches, api url and api token")
|
45
|
-
end
|
46
|
-
|
47
|
-
branches = []
|
48
|
-
if options.hook || options.client_hook
|
49
|
-
# we get passed changed branches and refs via stdin
|
50
|
-
$stdin.each_line do |line|
|
51
|
-
regex = options.client_hook ? CLIENT_HOOK_STDIN_REGEX : HOOK_STDIN_REGEX
|
52
|
-
matches = line.match(regex)
|
53
|
-
if matches && matches[:new] != '0' * 40
|
54
|
-
if options.branches.include? matches[:ref]
|
55
|
-
branches << matches[:ref]
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
18
|
+
new(cmd_parameter).run
|
19
|
+
rescue SystemExit
|
20
|
+
# Do nothing
|
21
|
+
rescue Gurney::ApiError => e
|
22
|
+
puts "Gurney API error".red
|
23
|
+
puts e.message.red
|
24
|
+
rescue Gurney::Error => e
|
25
|
+
puts "Gurney error: #{e.message}".red
|
26
|
+
rescue Exception
|
27
|
+
puts "Gurney: an unexpected error occurred".red
|
28
|
+
raise
|
29
|
+
end
|
59
30
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
31
|
+
def initialize(cmd_parameter=[])
|
32
|
+
@options = Gurney::CLI::OptionParser.parse(cmd_parameter)
|
33
|
+
@git = if options.hook
|
34
|
+
Git.bare(ENV['GIT_DIR'] || Dir.pwd)
|
35
|
+
else
|
36
|
+
unless Dir.exist? './.git'
|
37
|
+
raise Gurney::Error.new('Must be run within a git repository')
|
66
38
|
end
|
39
|
+
Git.open('.')
|
40
|
+
end
|
67
41
|
|
68
|
-
|
69
|
-
|
42
|
+
config_file = MAIN_BRANCHES.find do |branch|
|
43
|
+
file = read_file(options.hook, branch, options.config_file)
|
44
|
+
break file if file
|
45
|
+
end
|
46
|
+
if options.hook && !config_file
|
47
|
+
# Git hooks are activated by the config file. Without, do nothing.
|
48
|
+
exit 0
|
49
|
+
end
|
50
|
+
config_file ||= '---'
|
51
|
+
config = Gurney::Config.from_yaml(config_file)
|
52
|
+
|
53
|
+
options.branches ||= config&.branches
|
54
|
+
options.branches ||= config&.branches
|
55
|
+
options.api_token ||= config&.api_token
|
56
|
+
options.api_url ||= config&.api_url
|
57
|
+
options.project_id ||= config&.project_id
|
58
|
+
|
59
|
+
missing_options = [:project_id, :branches, :api_url, :api_token].select { |option| options.send(option).nil? }
|
60
|
+
# Use the line below in development
|
61
|
+
# missing_options = [:project_id, :branches, :api_token].select { |option| options.send(option).nil? }
|
62
|
+
raise Gurney::Error.new("Incomplete config - missing #{missing_options.map(&:inspect).join(', ')}.") unless missing_options.empty?
|
63
|
+
end
|
70
64
|
|
71
|
-
|
72
|
-
|
65
|
+
def run
|
66
|
+
reporting_branches.each do |branch|
|
67
|
+
dependencies = []
|
73
68
|
|
74
|
-
|
75
|
-
|
69
|
+
yarn_source = Gurney::Source::Yarn.new(yarn_lock: read_file(options.hook || options.client_hook, branch, 'yarn.lock'))
|
70
|
+
dependencies.concat yarn_source.dependencies || []
|
76
71
|
|
77
|
-
|
78
|
-
|
72
|
+
bundler_source = Gurney::Source::Bundler.new(gemfile_lock: read_file(options.hook || options.client_hook, branch, 'Gemfile.lock'))
|
73
|
+
dependencies.concat bundler_source.dependencies || []
|
79
74
|
|
80
|
-
|
75
|
+
ruby_version_source = Gurney::Source::RubyVersion.new(ruby_version: read_file(options.hook || options.client_hook, branch, '.ruby-version'))
|
76
|
+
dependencies.concat ruby_version_source.dependencies || []
|
81
77
|
|
82
|
-
|
83
|
-
api.post_dependencies(dependencies: dependencies, branch: branch, project_id: options.project_id)
|
78
|
+
dependencies.compact!
|
84
79
|
|
85
|
-
|
86
|
-
|
87
|
-
end
|
80
|
+
api = Gurney::Api.new(base_url: options.api_url, token: options.api_token)
|
81
|
+
api.post_dependencies(dependencies: dependencies, branch: branch, project_id: options.project_id, repo_path: git.repo.path)
|
88
82
|
|
89
|
-
|
90
|
-
|
91
|
-
puts "Gurney: api error".red
|
92
|
-
puts e.message.red
|
93
|
-
rescue Gurney::Error => e
|
94
|
-
puts "Gurney: error".red
|
95
|
-
puts e.message.red
|
96
|
-
rescue Exception => e
|
97
|
-
puts "Gurney: an unexpected error occurred".red
|
98
|
-
raise
|
83
|
+
dependency_counts = dependencies.group_by(&:ecosystem).map{|ecosystem, dependencies| "#{ecosystem}: #{dependencies.count}" }.join(', ')
|
84
|
+
puts "Gurney: reported dependencies (#{dependency_counts})"
|
99
85
|
end
|
100
86
|
end
|
101
87
|
|
102
88
|
private
|
103
89
|
|
104
|
-
|
90
|
+
attr_accessor :git, :options
|
91
|
+
|
92
|
+
def reporting_branches
|
93
|
+
branches = []
|
94
|
+
if options.hook || options.client_hook
|
95
|
+
# We get changed branches and refs via stdin
|
96
|
+
# See https://git-scm.com/docs/githooks#post-receive
|
97
|
+
$stdin.each_line do |line|
|
98
|
+
regex = options.client_hook ? CLIENT_HOOK_STDIN_REGEX : HOOK_STDIN_REGEX
|
99
|
+
line.force_encoding(Encoding::UTF_8)
|
100
|
+
matches = line.match(regex)
|
101
|
+
if matches && matches[:new] != '0' * 40
|
102
|
+
if options.branches.include? matches[:ref]
|
103
|
+
branches << matches[:ref]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
else
|
108
|
+
current_branch = git.current_branch
|
109
|
+
unless options.branches.nil? || options.branches.include?(current_branch)
|
110
|
+
raise Gurney::Error.new('The current branch is not specified in the config.')
|
111
|
+
end
|
112
|
+
branches << current_branch
|
113
|
+
end
|
114
|
+
|
115
|
+
branches
|
116
|
+
end
|
117
|
+
|
118
|
+
def read_file(from_git, branch, filename)
|
105
119
|
if from_git
|
106
120
|
begin
|
107
121
|
git.show("#{branch}:#{filename}")
|
@@ -109,14 +123,9 @@ module Gurney
|
|
109
123
|
# happens if branch does not exist
|
110
124
|
end
|
111
125
|
else
|
112
|
-
if File.
|
113
|
-
return File.read filename
|
114
|
-
end
|
126
|
+
File.read(filename) if File.exist?(filename)
|
115
127
|
end
|
116
128
|
end
|
117
129
|
|
118
130
|
end
|
119
|
-
|
120
|
-
class Error < Exception
|
121
|
-
end
|
122
131
|
end
|
data/lib/gurney/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gurney_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Martin Schaflitzl
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-11-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|