gurney_client 0.2.3 → 0.4.0

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: 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