freshli-commons 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.editorconfig +46 -0
- data/.rspec +3 -0
- data/.rubocop.yml +10 -0
- data/.semver +6 -0
- data/LICENSE +21 -0
- data/README.md +31 -0
- data/Rakefile +76 -0
- data/exe/shutdown-grpc +14 -0
- data/lib/corgibytes/freshli/commons/execute.rb +99 -0
- data/lib/corgibytes/freshli/commons/grpc_client.rb +131 -0
- data/lib/corgibytes/freshli/commons/platform.rb +19 -0
- data/lib/corgibytes/freshli/commons/ports.rb +37 -0
- data/lib/corgibytes/freshli/commons/step_definitions/command.rb +17 -0
- data/lib/corgibytes/freshli/commons/step_definitions/cyclonedx.rb +25 -0
- data/lib/corgibytes/freshli/commons/step_definitions/git.rb +53 -0
- data/lib/corgibytes/freshli/commons/step_definitions/grpc/.gitkeep +0 -0
- data/lib/corgibytes/freshli/commons/step_definitions/grpc/freshli_agent_pb.rb +52 -0
- data/lib/corgibytes/freshli/commons/step_definitions/grpc/freshli_agent_services_pb.rb +33 -0
- data/lib/corgibytes/freshli/commons/step_definitions/grpc/health_pb.rb +42 -0
- data/lib/corgibytes/freshli/commons/step_definitions/grpc/health_services_pb.rb +62 -0
- data/lib/corgibytes/freshli/commons/step_definitions/grpc.rb +127 -0
- data/lib/corgibytes/freshli/commons/step_definitions.rb +6 -0
- data/lib/corgibytes/freshli/commons/test_services.rb +40 -0
- data/lib/corgibytes/freshli/commons.rb +10 -0
- data/protos/corgibytes/freshli/agent/grpc/freshli_agent.proto +45 -0
- data/sig/corgibytes/freshli/commons.rbs +8 -0
- metadata +128 -0
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
data/.rubocop.yml
ADDED
data/.semver
ADDED
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
|
File without changes
|
@@ -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,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,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
|
+
}
|
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: []
|