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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 504e192b04318e2d0d3ec9ae31124c0c83260778a671b52499971b67d5f1a02b
4
- data.tar.gz: d0d867c6bd5a395e187a11852e53eb6851a0d6f3af696da4d83ed107750c3f02
3
+ metadata.gz: 6e7470d3ea652657be230f28b42b3035ee0e2c47122dc297533428831fc20b2b
4
+ data.tar.gz: 7e3bf5796d850cc8e600959a348f521d9d7ee10f5f7e329da391c0b125f57db0
5
5
  SHA512:
6
- metadata.gz: '08062d7c33bbdabf112dcb62cc329f2e5ccb2ca9d3a1b0c6356616f47c8384f1ce0c8c317f065ec654a7ac82d574d96caf66b13ff3f110c5eefe2322293971eb'
7
- data.tar.gz: f4249eee93c6096dfd29eb1f7ff75e656604fad010ec349d3ec6ac86cc999232e2a8a8ae0b3ef83af04698473e0530fb0fc44892de8b77b21b8b29f030133a9d
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.2.3 (2023-05-24)
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.2.3)
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.4)
14
- public_suffix (>= 2.0.2, < 6.0)
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.0)
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
- mime-types (3.4.1)
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.2023.0218.1)
28
+ mime-types-data (3.2024.1105)
27
29
  multi_xml (0.6.0)
28
- public_suffix (5.0.1)
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.2.27
57
+ 2.3.19
data/README.md CHANGED
@@ -1,30 +1,31 @@
1
- ## Gurney
1
+ # Gurney
2
2
 
3
- Gurney is a small tool to extract dependencies from project files and report them to a web api.
4
- It can either run locally or as a git post-receive hook in gitlab.
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 git hook, the project gets cloned on the git server and gurney then looks for a `gurney.yml` within the project files.
7
- If its present gurney looks at the pushed branches and analyses the ones specified in the config for dependencies.
8
- It then reports them to the web api also specified in the config.
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
- #### Usage:
15
+ ## Usage
11
16
  ```
12
17
  Usage: gurney [options]
13
- --api-url [API URL]
14
- Url for web api call, can have parameters for <project_id> and <branch>
15
- example: --api-url "http://example.com/project/<project_id>/branch/<branch>"
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 git post-receive hook
20
- --client-hook
21
- Run as a git pre-push hook
22
- -p, --project-id [PROJECT ID] Specify project id for api
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
- #### Sample Config:
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
- ##### Running as a global git hook
38
- To run as a global git hook in your gitlab see https://docs.gitlab.com/ee/administration/custom_hooks.html#set-a-global-git-hook-for-all-repositories
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
- dependencies: dependencies
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} api url is probably wrong")
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
- options = Gurney::CLI::OptionParser.parse(cmd_parameter)
16
-
17
- begin
18
- if options.hook
19
- g = Git.bare(ENV['GIT_DIR'] || Dir.pwd)
20
- else
21
- unless Dir.exists? './.git'
22
- raise Gurney::Error.new('Must be run within a git repository')
23
- end
24
- g = Git.open('.')
25
- end
26
- config_file = MAIN_BRANCHES.find do |branch|
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
- else
61
- current_branch = g.current_branch
62
- unless options.branches.nil? || options.branches.include?(current_branch)
63
- raise Gurney::Error.new('The current branch is not specified in the config.')
64
- end
65
- branches << current_branch
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
- branches.each do |branch|
69
- dependencies = []
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
- yarn_source = Gurney::Source::Yarn.new(yarn_lock: read_file(g, options.hook || options.client_hook, branch, 'yarn.lock'))
72
- dependencies.concat yarn_source.dependencies || []
65
+ def run
66
+ reporting_branches.each do |branch|
67
+ dependencies = []
73
68
 
74
- bundler_source = Gurney::Source::Bundler.new(gemfile_lock: read_file(g, options.hook || options.client_hook, branch, 'Gemfile.lock'))
75
- dependencies.concat bundler_source.dependencies || []
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
- ruby_version_source = Gurney::Source::RubyVersion.new(ruby_version: read_file(g, options.hook || options.client_hook, branch, '.ruby-version'))
78
- dependencies.concat ruby_version_source.dependencies || []
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
- dependencies.compact!
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
- api = Gurney::Api.new(base_url: options.api_url, token: options.api_token)
83
- api.post_dependencies(dependencies: dependencies, branch: branch, project_id: options.project_id)
78
+ dependencies.compact!
84
79
 
85
- dependency_counts = dependencies.group_by(&:ecosystem).map{|ecosystem, dependencies| "#{ecosystem}: #{dependencies.count}" }.join(', ')
86
- puts "Gurney: reported dependencies (#{dependency_counts})"
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
- rescue SystemExit
90
- rescue Gurney::ApiError => e
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
- def self.read_file(git, from_git, branch, filename)
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.exists? filename
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
@@ -1,3 +1,3 @@
1
1
  module Gurney
2
- VERSION = "0.2.3"
2
+ VERSION = '0.4.0'
3
3
  end
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.2.3
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: 2023-05-24 00:00:00.000000000 Z
11
+ date: 2024-11-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize