freshli-commons 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: cc5d50a4b6a1a5df06c1d51cfcc180d8236fd15145b2a205f1d3d7209e78bdd6
4
+ data.tar.gz: d2ad6c97040b1fddc3afb42d8d403b465a55a1d5c7c745f99570c5c8fa463d58
5
+ SHA512:
6
+ metadata.gz: 99e6d91119d93d7468377e2ac87fe6e88bff94471dd61c67617222879167b97af58bc22446eba1771c6e320c5414ab219b37a50910024b7cc11c0410f449a41e
7
+ data.tar.gz: 56e37d6bc7ae6c825681fa1b884611f2a04faa86846a9e519610e322e27e4c2103aa87880d08e355fb80d9f840ffe30d5e1276d9cc3c2a3e74390b24b4931f80
data/.editorconfig ADDED
@@ -0,0 +1,46 @@
1
+ # editorconfig.org
2
+
3
+ # Used the .NET Runtime as the base configuration.
4
+ #
5
+ # https://github.com/dotnet/runtime/blob/main/.editorconfig
6
+
7
+ # top-most EditorConfig file
8
+ root = true
9
+
10
+ # Default settings:
11
+ # A newline ending every file
12
+ # Use 4 spaces as indentation
13
+ [*]
14
+ insert_final_newline = true
15
+ indent_style = space
16
+ indent_size = 2
17
+ trim_trailing_whitespace = true
18
+ tab_width = 8
19
+
20
+ [*.json]
21
+ insert_final_newline = unset
22
+
23
+ # Shell scripts
24
+ [*.sh]
25
+ end_of_line = lf
26
+ [*.{cmd,bat}]
27
+ end_of_line = crlf
28
+
29
+ # Ruby files
30
+ [Gemfile.lock]
31
+ indent_size = unset
32
+
33
+ [LICENSE]
34
+ indent_size = unset
35
+ max_line_length = unset
36
+
37
+ [CONTRIBUTING.md]
38
+ indent_size = unset
39
+ max_line_length = unset
40
+
41
+ [README.md]
42
+ indent_size = unset
43
+ max_line_length = unset
44
+
45
+ [lib/corgibytes/freshli/commons/step_definitions/grpc/**/*.rb]
46
+ generated_code = true
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,10 @@
1
+ AllCops:
2
+ NewCops: enable
3
+ SuggestExtensions: false
4
+ TargetRubyVersion: 3.0
5
+ Exclude:
6
+ - 'lib/corgibytes/freshli/commons/step_definitions/grpc/**/*'
7
+ - 'vendor/bundle/**/*'
8
+
9
+ Style/ConditionalAssignment:
10
+ Enabled: false
data/.semver ADDED
@@ -0,0 +1,6 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 6
4
+ :patch: 0
5
+ :special: ''
6
+ :metadata: ''
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Corgibytes, LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # Corgibytes::Freshli::Commons
2
+
3
+ TODO: Delete this and the text below, and describe your gem
4
+
5
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/corgibytes/freshli/commons`. To experiment with that code, run `bin/console` for an interactive prompt.
6
+
7
+ ## Installation
8
+
9
+ TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
10
+
11
+ Install the gem and add to the application's Gemfile by executing:
12
+
13
+ $ bundle add UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
14
+
15
+ If bundler is not being used to manage dependencies, install the gem by executing:
16
+
17
+ $ gem install UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Development
24
+
25
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
26
+
27
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
28
+
29
+ ## Contributing
30
+
31
+ Bug reports and pull requests are welcome on GitHub at https://github.com/corgibytes/freshli-commons.
data/Rakefile ADDED
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ require 'fileutils'
7
+
8
+ # TODO: Copy the `freshli_agent.proto` from the `corgibytes/freshli` repository
9
+ # instead of having it live in this repository. Similar to what's being done
10
+ # with the health status proto file below.
11
+
12
+ # based on https://stackoverflow.com/a/29743469/243215
13
+ def download(url, target_path)
14
+ # rubocop:disable Security/Open
15
+ require 'open-uri'
16
+ download = URI.open(url)
17
+ # rubocop:enable Security/Open
18
+ IO.copy_stream(download, target_path)
19
+ end
20
+
21
+ # Generate gRPC files from the freshli_agent.proto file
22
+ GENERATED_GRPC_FILES = [
23
+ 'lib/corgibytes/freshli/commons/step_definitions/grpc/freshli_agent_pb.rb',
24
+ 'lib/corgibytes/freshli/commons/step_definitions/grpc/freshli_agent_services_pb.rb',
25
+ 'lib/corgibytes/freshli/commons/step_definitions/grpc/health_services_pb.rb',
26
+ 'lib/corgibytes/freshli/commons/step_definitions/grpc/health_pb.rb'
27
+ ].freeze
28
+ # rubocop:disable Metrics/BlockLength
29
+ namespace :grpc do
30
+ task :generate do
31
+ Rake::Task['grpc:generate:force'].invoke unless GENERATED_GRPC_FILES.all? { |file| File.exist?(file) }
32
+ end
33
+
34
+ namespace :generate do
35
+ desc 'Generate gRPC files even if they already exist'
36
+ task :force do
37
+ system(
38
+ 'bundle exec grpc_tools_ruby_protoc -I ' \
39
+ './protos/corgibytes/freshli/agent/grpc ' \
40
+ '--ruby_out=./lib/corgibytes/freshli/commons/step_definitions/grpc ' \
41
+ '--grpc_out=./lib/corgibytes/freshli/commons/step_definitions/grpc ' \
42
+ './protos/corgibytes/freshli/agent/grpc/freshli_agent.proto'
43
+ )
44
+
45
+ FileUtils.mkdir_p('tmp')
46
+
47
+ download(
48
+ 'https://raw.githubusercontent.com/grpc/grpc/e35cf362a49b4de753cbe69f3e836d2e40408ca2' \
49
+ '/src/proto/grpc/health/v1/health.proto',
50
+ File.expand_path(File.join(File.dirname(__FILE__), 'tmp', 'health.proto'))
51
+ )
52
+ system(
53
+ 'bundle exec grpc_tools_ruby_protoc -I tmp ' \
54
+ '--ruby_out=lib/corgibytes/freshli/commons/step_definitions/grpc ' \
55
+ '--grpc_out=lib/corgibytes/freshli/commons/step_definitions/grpc ' \
56
+ 'tmp/health.proto'
57
+ )
58
+ end
59
+ end
60
+ end
61
+ # rubocop:enable Metrics/BlockLength
62
+
63
+ desc 'Generate gRPC files'
64
+ task grpc: %i[grpc:generate]
65
+
66
+ # Ensure that the grpc files are generated before the tests run
67
+ RSpec::Core::RakeTask.new(spec: 'grpc')
68
+
69
+ require 'rubocop/rake_task'
70
+
71
+ RuboCop::RakeTask.new
72
+
73
+ # Ensure that the grpc files are generated before the build runs
74
+ Rake::Task['build'].enhance(['grpc'])
75
+
76
+ task default: %i[grpc spec rubocop]
data/exe/shutdown-grpc ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # This script helps with cleaning up gRPC servers that might be left running when some of the tests fail.
5
+ # You can pass it multiple port arguments separated by spaces, and it will attempt to connect to and close each one.
6
+
7
+ $LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..', 'features', 'support')))
8
+ $LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..', 'features', 'step_definitions', 'grpc')))
9
+
10
+ require 'grpc_client'
11
+
12
+ ARGV.each do |port|
13
+ GrpcClient.new(port).shutdown!
14
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'open3'
4
+
5
+ module Corgibytes
6
+ module Freshli
7
+ module Commons
8
+ # Contains utility methods for executing commands and working with their output.
9
+ module Execute
10
+ def enable_dotnet_command_colors
11
+ ENV['DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION'] = 'true'
12
+ end
13
+
14
+ def stream_eof?(status)
15
+ status.nil?
16
+ end
17
+
18
+ def stream_open_but_empty?(status)
19
+ status.empty? || status == :wait_readable
20
+ end
21
+
22
+ def safe_to_read?(status)
23
+ !stream_eof?(status) && !stream_open_but_empty?(status)
24
+ end
25
+
26
+ def write_buffered_output_to_correct_stream(buffer, stream, stdout, stderr)
27
+ if stream == stdout
28
+ $stdout.print(buffer)
29
+ elsif stream == stderr
30
+ $stderr.print(buffer)
31
+ end
32
+ end
33
+
34
+ BUFFER_LEN = 128
35
+
36
+ def fill_buffer_from_stream(stream, buffer)
37
+ # loop through reading data until there is an EOF (value is nil)
38
+ # or there is no more data to read (value is empty)
39
+ result = nil
40
+ loop do
41
+ local_buffer = ''.dup
42
+ result = stream.read_nonblock(BUFFER_LEN, local_buffer, exception: false)
43
+ buffer << local_buffer
44
+
45
+ break unless safe_to_read?(result) && buffer.length < BUFFER_LEN
46
+ end
47
+ result
48
+ end
49
+
50
+ def read_streams(for_reading, readable, stdout, stderr)
51
+ # In the case that both streams are readable (and thus have content)
52
+ # read from each of them. In this case, we cannot guarantee any order
53
+ # because we recieve the items at essentially the same time.
54
+ # We can still ensure that we don't mix data incorrectly.
55
+ readable.each do |stream|
56
+ buffer = ''.dup
57
+ result = fill_buffer_from_stream(stream, buffer)
58
+
59
+ for_reading -= [stream] if stream_eof?(result)
60
+
61
+ write_buffered_output_to_correct_stream(buffer, stream, stdout, stderr)
62
+ end
63
+ for_reading
64
+ end
65
+
66
+ def skip_or_read_streams(for_reading, readable, stdout, stderr)
67
+ # readable is nil in the case of a timeout - loop back again
68
+ if readable.nil?
69
+ Thread.pass
70
+ else
71
+ for_reading = read_streams(for_reading, readable, stdout, stderr)
72
+ end
73
+ for_reading
74
+ end
75
+
76
+ WAIT_TIMEOUT = 1
77
+ def execute(command)
78
+ exit_status = nil
79
+ Open3.popen3(command) do |_in, stdout, stderr, wait_thread|
80
+ for_reading = [stdout, stderr]
81
+ until for_reading.empty?
82
+ # IO.select blocks until one of the streams is has something to read
83
+ # or the wait timeout is reached
84
+ readable, _writable, _errors = IO.select(for_reading, [], [], WAIT_TIMEOUT)
85
+ for_reading = skip_or_read_streams(for_reading, readable, stdout, stderr)
86
+ end
87
+
88
+ exit_status = wait_thread.value
89
+ end
90
+ exit_status
91
+ end
92
+
93
+ def null_output_target
94
+ Gem.win_platform? ? 'NUL:' : '/dev/null'
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'timeout'
4
+
5
+ require 'rspec/expectations'
6
+
7
+ $LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), 'step_definitions', 'grpc')))
8
+
9
+ require 'freshli_agent_services_pb'
10
+ require 'health_services_pb'
11
+
12
+ module Corgibytes
13
+ module Freshli
14
+ module Commons
15
+ # Test driver client for communicating with the gRPC API.
16
+ class GrpcClient
17
+ include RSpec::Matchers
18
+
19
+ def initialize(port)
20
+ @port = port
21
+ end
22
+
23
+ def shutdown!
24
+ client = grpc_agent_client_on(@port)
25
+ response = client.shutdown(::Google::Protobuf::Empty.new)
26
+ expect(response).to be_a(::Google::Protobuf::Empty)
27
+ end
28
+
29
+ def detect_manifests(project_path)
30
+ client = grpc_agent_client_on(@captured_port)
31
+ response = client.detect_manifests(::Com::Corgibytes::Freshli::Agent::ProjectLocation.new(path: project_path))
32
+
33
+ result = []
34
+ response.each do |location|
35
+ result << location.path
36
+ end
37
+ result
38
+ end
39
+
40
+ # rubocop:disable Naming/AccessorMethodName
41
+ def get_validating_packages
42
+ client = grpc_agent_client_on(@port)
43
+ response = client.get_validating_packages(::Google::Protobuf::Empty.new)
44
+
45
+ result = []
46
+ response.each do |package|
47
+ result << package.purl
48
+ end
49
+ result
50
+ end
51
+
52
+ def get_validating_repositories
53
+ client = grpc_agent_client_on(@port)
54
+ response = client.get_validating_repositories(::Google::Protobuf::Empty.new)
55
+
56
+ result = []
57
+ response.each do |repository|
58
+ result << repository.url
59
+ end
60
+ result
61
+ end
62
+ # rubocop:enable Naming/AccessorMethodName
63
+
64
+ def process_manifest(manifest_path, moment_in_time)
65
+ client = grpc_agent_client_on(@port)
66
+ response = client.process_manifest(
67
+ ::Com::Corgibytes::Freshli::Agent::ProcessingRequest.new(
68
+ manifest: ::Com::Corgibytes::Freshli::Agent::ManifestLocation.new(path: manifest_path),
69
+ moment: ::Google::Protobuf::Timestamp.from_time(moment_in_time.to_time)
70
+ )
71
+ )
72
+ response.path
73
+ end
74
+
75
+ def retrieve_release_history(package_url)
76
+ client = grpc_agent_client_on(@port)
77
+ response = client.retrieve_release_history(::Com::Corgibytes::Freshli::Agent::Package.new(purl: package_url))
78
+ result = []
79
+ response.each do |release|
80
+ result << {
81
+ version: release.version,
82
+ released_at: release.released_at.to_time.to_datetime.new_offset('0:00')
83
+ }
84
+ end
85
+ result
86
+ end
87
+
88
+ def health_check
89
+ client = Grpc::Health::V1::Health::Stub.new("localhost:#{@port}", :this_channel_is_insecure)
90
+ response = client.check(
91
+ Grpc::Health::V1::HealthCheckRequest.new(
92
+ service: Com::Corgibytes::Freshli::Agent::Agent::Service.service_name
93
+ )
94
+ )
95
+ response.status
96
+ end
97
+
98
+ # rubocop:disable Naming/PredicateName
99
+ def is_running!
100
+ expect(health_check).to eq(:SERVING)
101
+ end
102
+ # rubocop:enable Naming/PredicateName
103
+
104
+ # rubocop:disable Metrics/MethodLength
105
+ def wait_until_running!
106
+ Timeout.timeout(5) do
107
+ loop do
108
+ status = nil
109
+ begin
110
+ status = health_check
111
+ rescue GRPC::Unavailable
112
+ status = nil
113
+ end
114
+
115
+ break if status == :SERVING
116
+
117
+ sleep 0.1
118
+ end
119
+ end
120
+ end
121
+ # rubocop:enable Metrics/MethodLength
122
+
123
+ private
124
+
125
+ def grpc_agent_client_on(_port)
126
+ Com::Corgibytes::Freshli::Agent::Agent::Stub.new("localhost:#{@port}", :this_channel_is_insecure)
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Corgibytes
4
+ module Freshli
5
+ module Commons
6
+ # Contains helper methods for coping with platform specific differences
7
+ module Platform
8
+ def self.null_output_target
9
+ Gem.win_platform? ? 'NUL:' : '/dev/null'
10
+ end
11
+
12
+ def self.normalize_file_separators(value)
13
+ separator = File::ALT_SEPARATOR || File::SEPARATOR
14
+ value.gsub('/', separator)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Corgibytes
4
+ module Freshli
5
+ module Commons
6
+ # Contains utility methods for working with IP ports, specifically determining which ones are available for use.
7
+ module Ports
8
+ # rubocop:disable Metrics/MethodLength
9
+ def self.available?(port)
10
+ max_attempts = 1000
11
+ attempts = 0
12
+ begin
13
+ attempts += 1
14
+ yield(attempts) if block_given?
15
+ attempt_connection(port)
16
+ true
17
+ rescue Errno::EADDRINUSE
18
+ if attempts < max_attempts
19
+ sleep 0.1
20
+ retry
21
+ end
22
+ false
23
+ end
24
+ end
25
+ # rubocop:enable Metrics/MethodLength
26
+
27
+ def self.attempt_connection(port)
28
+ # based on https://stackoverflow.com/a/34375147/243215
29
+ require 'socket'
30
+ socket = Socket.new(Socket::Constants::AF_INET, Socket::Constants::SOCK_STREAM, 0)
31
+ socket.bind(Socket.pack_sockaddr_in(port, '0.0.0.0'))
32
+ socket.close
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'corgibytes/freshli/commons/platform'
4
+ Platform = Corgibytes::Freshli::Commons::Platform
5
+
6
+ # this block is based on https://github.com/cucumber/aruba/blob/v2.1.0/lib/aruba/cucumber/command.rb#L404..L414
7
+ Then(/^it should (pass|fail) with exact output containing file paths:$/) do |pass_fail, expected|
8
+ last_command_started.stop
9
+
10
+ if pass_fail == 'pass'
11
+ expect(last_command_stopped).to be_successfully_executed
12
+ else
13
+ expect(last_command_stopped).not_to be_successfully_executed
14
+ end
15
+
16
+ expect(last_command_stopped).to have_output an_output_string_being_eq(Platform.normalize_file_separators(expected))
17
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'corgibytes/freshli/commons/platform'
4
+ Platform = Corgibytes::Freshli::Commons::Platform
5
+
6
+ Then('the CycloneDX file {string} should be valid') do |bom_path|
7
+ full_bom_path = Platform.normalize_file_separators("#{Aruba.config.working_directory}/#{bom_path}")
8
+ unless system("cyclonedx validate --fail-on-errors --input-file #{full_bom_path}",
9
+ out: Platform.null_output_target, err: Platform.null_output_target)
10
+ raise "CycloneDX file is not valid: #{bom_path}"
11
+ end
12
+ end
13
+
14
+ Then('the CycloneDX file {string} should contain {string}') do |bom_path, package_url|
15
+ full_bom_path = Platform.normalize_file_separators("#{Aruba.config.working_directory}/#{bom_path}")
16
+ bom_file_lines = File.readlines(full_bom_path)
17
+ was_package_url_found = false
18
+ bom_file_lines.each do |line|
19
+ if line.include?(package_url)
20
+ was_package_url_found = true
21
+ break
22
+ end
23
+ end
24
+ raise "Unable to find #{package_url} in #{bom_path}" unless was_package_url_found
25
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+
5
+ require 'corgibytes/freshli/commons/platform'
6
+ Platform = Corgibytes::Freshli::Commons::Platform
7
+
8
+ Given('I clone the git repository {string} with the sha {string}') do |repository_url, sha|
9
+ repositories_dir = "#{Aruba.config.working_directory}/tmp/repositories"
10
+ cloned_dir = "#{repositories_dir}/#{repository_url.split('/').last}"
11
+
12
+ FileUtils.mkdir_p(repositories_dir)
13
+
14
+ unless Dir.exist?(cloned_dir)
15
+ $stdout.print "Cloning #{repository_url} ..."
16
+ unless system(
17
+ "git clone #{repository_url}",
18
+ chdir: repositories_dir,
19
+ out: Platform.null_output_target,
20
+ err: Platform.null_output_target
21
+ )
22
+ raise "Failed to clone #{repository_url}"
23
+ end
24
+
25
+ puts 'done.'
26
+ end
27
+ unless system(
28
+ "git checkout #{sha}",
29
+ chdir: cloned_dir,
30
+ out: Platform.null_output_target,
31
+ err: Platform.null_output_target
32
+ )
33
+ raise "Failed to checkout #{sha}"
34
+ end
35
+ end
36
+
37
+ Then('running git status should not report any modifications for {string}') do |git_repository_path|
38
+ git_repository_path_in_working_directory = "#{Aruba.config.working_directory}/#{git_repository_path}"
39
+ system(
40
+ 'git update-index --refresh',
41
+ chdir: git_repository_path_in_working_directory,
42
+ out: Platform.null_output_target,
43
+ err: Platform.null_output_target
44
+ )
45
+ unless system(
46
+ 'git diff-index --quiet HEAD --',
47
+ chdir: git_repository_path_in_working_directory,
48
+ out: Platform.null_output_target,
49
+ err: Platform.null_output_target
50
+ )
51
+ raise "The working directory is not clean: #{git_repository_path}"
52
+ end
53
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # source: freshli_agent.proto
4
+
5
+ require 'google/protobuf'
6
+
7
+ require 'google/protobuf/empty_pb'
8
+ require 'google/protobuf/timestamp_pb'
9
+
10
+
11
+ descriptor_data = "\n\x13\x66reshli_agent.proto\x12\x1c\x63om.corgibytes.freshli.agent\x1a\x1bgoogle/protobuf/empty.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\x1f\n\x0fProjectLocation\x12\x0c\n\x04path\x18\x01 \x01(\t\" \n\x10ManifestLocation\x12\x0c\n\x04path\x18\x01 \x01(\t\"\x81\x01\n\x11ProcessingRequest\x12@\n\x08manifest\x18\x01 \x01(\x0b\x32..com.corgibytes.freshli.agent.ManifestLocation\x12*\n\x06moment\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"\x1b\n\x0b\x42omLocation\x12\x0c\n\x04path\x18\x01 \x01(\t\"\x17\n\x07Package\x12\x0c\n\x04purl\x18\x01 \x01(\t\"R\n\x0ePackageRelease\x12\x0f\n\x07version\x18\x01 \x01(\t\x12/\n\x0breleased_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"!\n\x12RepositoryLocation\x12\x0b\n\x03url\x18\x01 \x01(\t2\xda\x04\n\x05\x41gent\x12r\n\x0f\x44\x65tectManifests\x12-.com.corgibytes.freshli.agent.ProjectLocation\x1a..com.corgibytes.freshli.agent.ManifestLocation0\x01\x12m\n\x0fProcessManifest\x12/.com.corgibytes.freshli.agent.ProcessingRequest\x1a).com.corgibytes.freshli.agent.BomLocation\x12o\n\x16RetrieveReleaseHistory\x12%.com.corgibytes.freshli.agent.Package\x1a,.com.corgibytes.freshli.agent.PackageRelease0\x01\x12X\n\x15GetValidatingPackages\x12\x16.google.protobuf.Empty\x1a%.com.corgibytes.freshli.agent.Package0\x01\x12g\n\x19GetValidatingRepositories\x12\x16.google.protobuf.Empty\x1a\x30.com.corgibytes.freshli.agent.RepositoryLocation0\x01\x12:\n\x08Shutdown\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Emptyb\x06proto3"
12
+
13
+ pool = Google::Protobuf::DescriptorPool.generated_pool
14
+
15
+ begin
16
+ pool.add_serialized_file(descriptor_data)
17
+ rescue TypeError => e
18
+ # Compatibility code: will be removed in the next major version.
19
+ require 'google/protobuf/descriptor_pb'
20
+ parsed = Google::Protobuf::FileDescriptorProto.decode(descriptor_data)
21
+ parsed.clear_dependency
22
+ serialized = parsed.class.encode(parsed)
23
+ file = pool.add_serialized_file(serialized)
24
+ warn "Warning: Protobuf detected an import path issue while loading generated file #{__FILE__}"
25
+ imports = [
26
+ ["google.protobuf.Timestamp", "google/protobuf/timestamp.proto"],
27
+ ]
28
+ imports.each do |type_name, expected_filename|
29
+ import_file = pool.lookup(type_name).file_descriptor
30
+ if import_file.name != expected_filename
31
+ warn "- #{file.name} imports #{expected_filename}, but that import was loaded as #{import_file.name}"
32
+ end
33
+ end
34
+ warn "Each proto file must use a consistent fully-qualified name."
35
+ warn "This will become an error in the next major version."
36
+ end
37
+
38
+ module Com
39
+ module Corgibytes
40
+ module Freshli
41
+ module Agent
42
+ ProjectLocation = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("com.corgibytes.freshli.agent.ProjectLocation").msgclass
43
+ ManifestLocation = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("com.corgibytes.freshli.agent.ManifestLocation").msgclass
44
+ ProcessingRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("com.corgibytes.freshli.agent.ProcessingRequest").msgclass
45
+ BomLocation = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("com.corgibytes.freshli.agent.BomLocation").msgclass
46
+ Package = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("com.corgibytes.freshli.agent.Package").msgclass
47
+ PackageRelease = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("com.corgibytes.freshli.agent.PackageRelease").msgclass
48
+ RepositoryLocation = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("com.corgibytes.freshli.agent.RepositoryLocation").msgclass
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,33 @@
1
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
2
+ # Source: freshli_agent.proto for package 'com.corgibytes.freshli.agent'
3
+
4
+ require 'grpc'
5
+ require 'freshli_agent_pb'
6
+
7
+ module Com
8
+ module Corgibytes
9
+ module Freshli
10
+ module Agent
11
+ module Agent
12
+ class Service
13
+
14
+ include ::GRPC::GenericService
15
+
16
+ self.marshal_class_method = :encode
17
+ self.unmarshal_class_method = :decode
18
+ self.service_name = 'com.corgibytes.freshli.agent.Agent'
19
+
20
+ rpc :DetectManifests, ::Com::Corgibytes::Freshli::Agent::ProjectLocation, stream(::Com::Corgibytes::Freshli::Agent::ManifestLocation)
21
+ rpc :ProcessManifest, ::Com::Corgibytes::Freshli::Agent::ProcessingRequest, ::Com::Corgibytes::Freshli::Agent::BomLocation
22
+ rpc :RetrieveReleaseHistory, ::Com::Corgibytes::Freshli::Agent::Package, stream(::Com::Corgibytes::Freshli::Agent::PackageRelease)
23
+ rpc :GetValidatingPackages, ::Google::Protobuf::Empty, stream(::Com::Corgibytes::Freshli::Agent::Package)
24
+ rpc :GetValidatingRepositories, ::Google::Protobuf::Empty, stream(::Com::Corgibytes::Freshli::Agent::RepositoryLocation)
25
+ rpc :Shutdown, ::Google::Protobuf::Empty, ::Google::Protobuf::Empty
26
+ end
27
+
28
+ Stub = Service.rpc_stub_class
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # source: health.proto
4
+
5
+ require 'google/protobuf'
6
+
7
+
8
+ descriptor_data = "\n\x0chealth.proto\x12\x0egrpc.health.v1\"%\n\x12HealthCheckRequest\x12\x0f\n\x07service\x18\x01 \x01(\t\"\xa9\x01\n\x13HealthCheckResponse\x12\x41\n\x06status\x18\x01 \x01(\x0e\x32\x31.grpc.health.v1.HealthCheckResponse.ServingStatus\"O\n\rServingStatus\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x0b\n\x07SERVING\x10\x01\x12\x0f\n\x0bNOT_SERVING\x10\x02\x12\x13\n\x0fSERVICE_UNKNOWN\x10\x03\x32\xae\x01\n\x06Health\x12P\n\x05\x43heck\x12\".grpc.health.v1.HealthCheckRequest\x1a#.grpc.health.v1.HealthCheckResponse\x12R\n\x05Watch\x12\".grpc.health.v1.HealthCheckRequest\x1a#.grpc.health.v1.HealthCheckResponse0\x01\x42\x61\n\x11io.grpc.health.v1B\x0bHealthProtoP\x01Z,google.golang.org/grpc/health/grpc_health_v1\xaa\x02\x0eGrpc.Health.V1b\x06proto3"
9
+
10
+ pool = Google::Protobuf::DescriptorPool.generated_pool
11
+
12
+ begin
13
+ pool.add_serialized_file(descriptor_data)
14
+ rescue TypeError => e
15
+ # Compatibility code: will be removed in the next major version.
16
+ require 'google/protobuf/descriptor_pb'
17
+ parsed = Google::Protobuf::FileDescriptorProto.decode(descriptor_data)
18
+ parsed.clear_dependency
19
+ serialized = parsed.class.encode(parsed)
20
+ file = pool.add_serialized_file(serialized)
21
+ warn "Warning: Protobuf detected an import path issue while loading generated file #{__FILE__}"
22
+ imports = [
23
+ ]
24
+ imports.each do |type_name, expected_filename|
25
+ import_file = pool.lookup(type_name).file_descriptor
26
+ if import_file.name != expected_filename
27
+ warn "- #{file.name} imports #{expected_filename}, but that import was loaded as #{import_file.name}"
28
+ end
29
+ end
30
+ warn "Each proto file must use a consistent fully-qualified name."
31
+ warn "This will become an error in the next major version."
32
+ end
33
+
34
+ module Grpc
35
+ module Health
36
+ module V1
37
+ HealthCheckRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.health.v1.HealthCheckRequest").msgclass
38
+ HealthCheckResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.health.v1.HealthCheckResponse").msgclass
39
+ HealthCheckResponse::ServingStatus = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.health.v1.HealthCheckResponse.ServingStatus").enummodule
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,62 @@
1
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
2
+ # Source: health.proto for package 'grpc.health.v1'
3
+ # Original file comments:
4
+ # Copyright 2015 The gRPC Authors
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ # The canonical version of this proto can be found at
19
+ # https://github.com/grpc/grpc-proto/blob/master/grpc/health/v1/health.proto
20
+ #
21
+
22
+ require 'grpc'
23
+ require 'health_pb'
24
+
25
+ module Grpc
26
+ module Health
27
+ module V1
28
+ module Health
29
+ class Service
30
+
31
+ include ::GRPC::GenericService
32
+
33
+ self.marshal_class_method = :encode
34
+ self.unmarshal_class_method = :decode
35
+ self.service_name = 'grpc.health.v1.Health'
36
+
37
+ # If the requested service is unknown, the call will fail with status
38
+ # NOT_FOUND.
39
+ rpc :Check, ::Grpc::Health::V1::HealthCheckRequest, ::Grpc::Health::V1::HealthCheckResponse
40
+ # Performs a watch for the serving status of the requested service.
41
+ # The server will immediately send back a message indicating the current
42
+ # serving status. It will then subsequently send a new message whenever
43
+ # the service's serving status changes.
44
+ #
45
+ # If the requested service is unknown when the call is received, the
46
+ # server will send a message setting the serving status to
47
+ # SERVICE_UNKNOWN but will *not* terminate the call. If at some
48
+ # future point, the serving status of the service becomes known, the
49
+ # server will send a new message with the service's serving status.
50
+ #
51
+ # If the call terminates with status UNIMPLEMENTED, then clients
52
+ # should assume this method is not supported and should not retry the
53
+ # call. If the call terminates with any other status (including OK),
54
+ # clients should retry the call with appropriate exponential backoff.
55
+ rpc :Watch, ::Grpc::Health::V1::HealthCheckRequest, stream(::Grpc::Health::V1::HealthCheckResponse)
56
+ end
57
+
58
+ Stub = Service.rpc_stub_class
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'time'
4
+ require 'google/protobuf/well_known_types'
5
+
6
+ require 'corgibytes/freshli/commons/test_services'
7
+ TestServices = Corgibytes::Freshli::Commons::TestServices
8
+ require 'corgibytes/freshli/commons/grpc_client'
9
+ GrpcClient = Corgibytes::Freshli::Commons::GrpcClient
10
+ require 'corgibytes/freshli/commons/ports'
11
+ Ports = Corgibytes::Freshli::Commons::Ports
12
+
13
+ Then('the freshli_agent.proto gRPC service is running on port {int}') do |port|
14
+ GrpcClient.new(port).is_running!
15
+ end
16
+
17
+ When('I wait for the freshli_agent.proto gRPC service to be running on port {int}') do |port|
18
+ GrpcClient.new(port).wait_until_running!
19
+ end
20
+
21
+ When('the gRPC service on port {int} is sent the shutdown command') do |port|
22
+ GrpcClient.new(port).shutdown!
23
+ end
24
+
25
+ Then('there are no services running on port {int}') do |port|
26
+ expect(Ports.available?(port) { |attempts| log(attempts) }).to be_truthy
27
+ end
28
+
29
+ test_services = TestServices.new
30
+
31
+ Given('a test service is started on port {int}') do |port|
32
+ test_services.start_on(port)
33
+ end
34
+
35
+ When('the test service running on port {int} is stopped') do |port|
36
+ test_services.stop_on(port)
37
+ end
38
+
39
+ When('I call DetectManifests with the full path to {string} on port {int}') do |project_path, port|
40
+ expanded_path = Platform.normalize_file_separators(
41
+ File.expand_path(File.join(aruba.config.home_directory, project_path))
42
+ )
43
+
44
+ @detect_manifests_paths = GrpcClient.new(port).detect_manifests(expanded_path)
45
+ end
46
+
47
+ def expanded_paths_from(doc_string, project_path)
48
+ result = []
49
+ doc_string.each_line do |file_path|
50
+ result << Platform.normalize_file_separators(
51
+ File.expand_path(File.join(aruba.config.home_directory, project_path, file_path.strip))
52
+ )
53
+ end
54
+ result
55
+ end
56
+
57
+ Then('the DetectManifests response contains the following file paths expanded beneath {string}:') do
58
+ |project_path, doc_string|
59
+
60
+ expected_paths = expanded_paths_from(doc_string, project_path)
61
+ expect(@detect_manifests_paths).to eq(expected_paths)
62
+ end
63
+
64
+ When('I call GetValidatingPackages on port {int}') do |port|
65
+ @get_validating_packages_results = GrpcClient.new(port).get_validating_packages
66
+ end
67
+
68
+ Then('the GetValidatingPackages response should contain:') do |doc_string|
69
+ expected_packages = []
70
+ doc_string.each_line do |package_url|
71
+ expected_packages << package_url.strip
72
+ end
73
+
74
+ expect(@get_validating_packages_results).to eq(expected_packages)
75
+ end
76
+
77
+ When('I call GetValidatingRepositories on port {int}') do |port|
78
+ @get_validating_repositories_results = GrpcClient.new(port).get_validating_repositories
79
+ end
80
+
81
+ Then('GetValidatingRepositories response should contain:') do |doc_string|
82
+ expected_repositories = []
83
+ doc_string.each_line do |repository|
84
+ expected_repositories << repository.strip
85
+ end
86
+
87
+ expect(@get_validating_repositories_results).to eq(expected_repositories)
88
+ end
89
+
90
+ When('I call ProcessManifest with the expanded path {string} and the moment {string} on port {int}') do
91
+ |manifest_path, moment_in_time, port|
92
+
93
+ expanded_path = Platform.normalize_file_separators(
94
+ File.expand_path(File.join(aruba.config.home_directory, manifest_path))
95
+ )
96
+ @process_manifest_result = GrpcClient.new(port).process_manifest(
97
+ expanded_path, DateTime.parse(moment_in_time)
98
+ )
99
+ end
100
+
101
+ Then('the ProcessManifest response contains the following file paths expanded beneath {string}:') do
102
+ |project_path, doc_string|
103
+
104
+ expected_paths = expanded_paths_from(doc_string, project_path)
105
+
106
+ expect([@process_manifest_result]).to eq(expected_paths)
107
+ end
108
+
109
+ When('I call RetrieveReleaseHistory with {string} on port {int}') do |package_url, port|
110
+ @retrieve_release_history_results = GrpcClient.new(port).retrieve_release_history(package_url)
111
+ end
112
+
113
+ Then('RetrieveReleaseHistory response should contain the following versions and release dates:') do |doc_string|
114
+ expected_package_releases = []
115
+ doc_string.each_line do |line|
116
+ splits = line.strip.split("\t")
117
+ expected_package_releases << { version: splits[0], released_at: DateTime.parse(splits[1]).new_offset('0:00') }
118
+ end
119
+
120
+ filtered_results = @retrieve_release_history_results.take(expected_package_releases.length)
121
+
122
+ expect(filtered_results).to eq(expected_package_releases)
123
+ end
124
+
125
+ Then('RetrieveReleaseHistory response should be empty') do
126
+ expect(@retrieve_release_history_results).to be_empty
127
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'step_definitions/command'
4
+ require_relative 'step_definitions/cyclonedx'
5
+ require_relative 'step_definitions/git'
6
+ require_relative 'step_definitions/grpc'
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec/expectations'
4
+
5
+ module Corgibytes
6
+ module Freshli
7
+ module Commons
8
+ # Controls running test services on specific ports. Used to force a specific port to be in use.
9
+ class TestServices
10
+ include RSpec::Matchers
11
+
12
+ def initialize
13
+ @test_services = {}
14
+ end
15
+
16
+ def start_on(port)
17
+ expect(@test_services).not_to have_key(port)
18
+
19
+ socket4 = Socket.new(Socket::Constants::AF_INET, Socket::Constants::SOCK_STREAM, 0)
20
+ socket4.bind(Socket.pack_sockaddr_in(port, '0.0.0.0'))
21
+
22
+ # bind to a socket using both ipv4 and ipv6
23
+ socket6 = Socket.new(Socket::Constants::AF_INET6, Socket::Constants::SOCK_STREAM, 0)
24
+ socket6.bind(Socket.pack_sockaddr_in(port, '::'))
25
+
26
+ @test_services[port] = { v4: socket4, v6: socket6 }
27
+ end
28
+
29
+ def stop_on(port)
30
+ expect(@test_services).to have_key(port)
31
+
32
+ socket4 = @test_services[port][:v4]
33
+ socket4.close
34
+ socket6 = @test_services[port][:v6]
35
+ socket6.close
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Corgibytes
4
+ module Freshli
5
+ module Commons
6
+ class Error < StandardError; end
7
+ # Your code goes here...
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,45 @@
1
+ syntax = "proto3";
2
+
3
+ package com.corgibytes.freshli.agent;
4
+
5
+ import "google/protobuf/empty.proto";
6
+ import "google/protobuf/timestamp.proto";
7
+
8
+ service Agent {
9
+ rpc DetectManifests(ProjectLocation) returns (stream ManifestLocation);
10
+ rpc ProcessManifest(ProcessingRequest) returns (BomLocation);
11
+ rpc RetrieveReleaseHistory(Package) returns (stream PackageRelease);
12
+ rpc GetValidatingPackages(google.protobuf.Empty) returns (stream Package);
13
+ rpc GetValidatingRepositories(google.protobuf.Empty) returns (stream RepositoryLocation);
14
+ rpc Shutdown(google.protobuf.Empty) returns (google.protobuf.Empty);
15
+ }
16
+
17
+ message ProjectLocation {
18
+ string path = 1;
19
+ }
20
+
21
+ message ManifestLocation {
22
+ string path = 1;
23
+ }
24
+
25
+ message ProcessingRequest {
26
+ ManifestLocation manifest = 1;
27
+ google.protobuf.Timestamp moment = 2;
28
+ }
29
+
30
+ message BomLocation {
31
+ string path = 1;
32
+ }
33
+
34
+ message Package {
35
+ string purl = 1;
36
+ }
37
+
38
+ message PackageRelease {
39
+ string version = 1;
40
+ google.protobuf.Timestamp released_at = 2;
41
+ }
42
+
43
+ message RepositoryLocation {
44
+ string url = 1;
45
+ }
@@ -0,0 +1,8 @@
1
+ module Corgibytes
2
+ module Freshli
3
+ module Commons
4
+ VERSION: String
5
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
6
+ end
7
+ end
8
+ end
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: freshli-commons
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.6.0
5
+ platform: ruby
6
+ authors:
7
+ - M. Scott Ford
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-08-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: aruba
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 2.1.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 2.1.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: grpc
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec-expectations
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sqlite3
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description:
70
+ email:
71
+ - scott@corgibytes.com
72
+ executables:
73
+ - shutdown-grpc
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".editorconfig"
78
+ - ".rspec"
79
+ - ".rubocop.yml"
80
+ - ".semver"
81
+ - LICENSE
82
+ - README.md
83
+ - Rakefile
84
+ - exe/shutdown-grpc
85
+ - lib/corgibytes/freshli/commons.rb
86
+ - lib/corgibytes/freshli/commons/execute.rb
87
+ - lib/corgibytes/freshli/commons/grpc_client.rb
88
+ - lib/corgibytes/freshli/commons/platform.rb
89
+ - lib/corgibytes/freshli/commons/ports.rb
90
+ - lib/corgibytes/freshli/commons/step_definitions.rb
91
+ - lib/corgibytes/freshli/commons/step_definitions/command.rb
92
+ - lib/corgibytes/freshli/commons/step_definitions/cyclonedx.rb
93
+ - lib/corgibytes/freshli/commons/step_definitions/git.rb
94
+ - lib/corgibytes/freshli/commons/step_definitions/grpc.rb
95
+ - lib/corgibytes/freshli/commons/step_definitions/grpc/.gitkeep
96
+ - lib/corgibytes/freshli/commons/step_definitions/grpc/freshli_agent_pb.rb
97
+ - lib/corgibytes/freshli/commons/step_definitions/grpc/freshli_agent_services_pb.rb
98
+ - lib/corgibytes/freshli/commons/step_definitions/grpc/health_pb.rb
99
+ - lib/corgibytes/freshli/commons/step_definitions/grpc/health_services_pb.rb
100
+ - lib/corgibytes/freshli/commons/test_services.rb
101
+ - protos/corgibytes/freshli/agent/grpc/freshli_agent.proto
102
+ - sig/corgibytes/freshli/commons.rbs
103
+ homepage: https://github.com/corgibytes/freshli-commons
104
+ licenses: []
105
+ metadata:
106
+ homepage_uri: https://github.com/corgibytes/freshli-commons
107
+ source_code_uri: https://github.com/corgibytes/freshli-commons
108
+ rubygems_mfa_required: 'true'
109
+ post_install_message:
110
+ rdoc_options: []
111
+ require_paths:
112
+ - lib
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: 3.0.0
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ requirements: []
124
+ rubygems_version: 3.2.33
125
+ signing_key:
126
+ specification_version: 4
127
+ summary: Common build and testing code that is shared amongst the Freshli repositories
128
+ test_files: []