rakit 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/rakit/azure/dev_ops.rb +139 -0
- data/lib/rakit/ruby_gems.rb +48 -0
- data/lib/rakit.rb +3 -0
- metadata +48 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ee82c70e32276dc9332649c9932aa9212e358a3dcefacec587070521982d5cae
|
|
4
|
+
data.tar.gz: d22e913a279a3ea5f4b51adc280aab7c1d303e4fd921a9c44ade41b4993471f3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e428b21575d78b7205df71fead16f344ab8c3316b6d6a83a2ec238d367940bfc854be84fa14400759c9c235bbdb061f3ed9e820def62be1515db909484882c79
|
|
7
|
+
data.tar.gz: 600283a6b6f4269af6e0b93a6ccd521786f65bd5c7347c17080fe1aafa8c92e86387362e9ee209995be762343fc23efc35e254a4827a1043cfd7b89cd380af3c
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Azure DevOps Pipelines API: pipeline status.
|
|
4
|
+
# Requires: AZURE_DEVOPS_ORG, AZURE_DEVOPS_PROJECT, AZURE_DEVOPS_PIPELINE_ID, AZURE_DEVOPS_TOKEN (PAT with Build Read).
|
|
5
|
+
require "json"
|
|
6
|
+
require "net/http"
|
|
7
|
+
require "uri"
|
|
8
|
+
|
|
9
|
+
module Rakit
|
|
10
|
+
module Azure
|
|
11
|
+
module DevOps
|
|
12
|
+
PipelineStatus = Struct.new(:successful, :errors, :warnings, keyword_init: true) do
|
|
13
|
+
def self.empty_failure(errors_message)
|
|
14
|
+
new(successful: false, errors: errors_message.to_s, warnings: "")
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
API_VERSION = "7.1"
|
|
19
|
+
BASE_URL = "https://dev.azure.com"
|
|
20
|
+
|
|
21
|
+
class << self
|
|
22
|
+
# Returns the latest pipeline status.
|
|
23
|
+
# Uses ENV: AZURE_DEVOPS_ORG, AZURE_DEVOPS_PROJECT, AZURE_DEVOPS_PIPELINE_ID, AZURE_DEVOPS_TOKEN.
|
|
24
|
+
def get_pipeline_status
|
|
25
|
+
org = ENV["AZURE_DEVOPS_ORG"]
|
|
26
|
+
project = ENV["AZURE_DEVOPS_PROJECT"]
|
|
27
|
+
pipeline_id = ENV["AZURE_DEVOPS_PIPELINE_ID"]
|
|
28
|
+
token = ENV["AZURE_DEVOPS_TOKEN"]
|
|
29
|
+
|
|
30
|
+
if [org, project, pipeline_id, token].any?(&:nil?) || [org, project, pipeline_id, token].any?(&:empty?)
|
|
31
|
+
return PipelineStatus.empty_failure(
|
|
32
|
+
"Missing config: set AZURE_DEVOPS_ORG, AZURE_DEVOPS_PROJECT, AZURE_DEVOPS_PIPELINE_ID, AZURE_DEVOPS_TOKEN"
|
|
33
|
+
)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
run = _list_runs(org, project, pipeline_id.to_s, token).first
|
|
37
|
+
return PipelineStatus.empty_failure("No pipeline run found") unless run
|
|
38
|
+
|
|
39
|
+
timeline = _get_timeline(org, project, pipeline_id.to_s, token, run["id"])
|
|
40
|
+
successful = _run_passed?(run)
|
|
41
|
+
errors = _format_failed_steps(_failed_steps_details(timeline))
|
|
42
|
+
warnings = _format_warnings(_warning_details(timeline))
|
|
43
|
+
|
|
44
|
+
PipelineStatus.new(
|
|
45
|
+
successful: successful,
|
|
46
|
+
errors: errors.to_s,
|
|
47
|
+
warnings: warnings.to_s
|
|
48
|
+
)
|
|
49
|
+
rescue StandardError => e
|
|
50
|
+
PipelineStatus.empty_failure("Pipeline status error: #{e.message}")
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def _base_uri(org, project, path)
|
|
56
|
+
URI("#{BASE_URL}/#{URI.encode_www_form_component(org)}/#{URI.encode_www_form_component(project)}/_apis/#{path}?api-version=#{API_VERSION}")
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def _get(org, project, token, path)
|
|
60
|
+
uri = _base_uri(org, project, path)
|
|
61
|
+
req = Net::HTTP::Get.new(uri)
|
|
62
|
+
req.basic_auth("", token)
|
|
63
|
+
req["Accept"] = "application/json"
|
|
64
|
+
resp = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") { |http| http.request(req) }
|
|
65
|
+
raise "HTTP #{resp.code}: #{resp.body}" unless resp.is_a?(Net::HTTPSuccess)
|
|
66
|
+
|
|
67
|
+
body = resp.body.to_s.strip
|
|
68
|
+
body.empty? ? {} : JSON.parse(body)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def _list_runs(org, project, pipeline_id, token, top: 10)
|
|
72
|
+
data = _get(org, project, token, "pipelines/#{pipeline_id}/runs")
|
|
73
|
+
(data["value"] || []).first(top)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def _get_timeline(org, project, pipeline_id, token, build_id)
|
|
77
|
+
_get(org, project, token, "build/builds/#{build_id}/timeline")
|
|
78
|
+
rescue StandardError => e
|
|
79
|
+
return nil if e.message.include?("404")
|
|
80
|
+
|
|
81
|
+
raise
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def _run_passed?(run)
|
|
85
|
+
run && %w[succeeded succeededwithissues].include?(run["result"]&.downcase)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def _failed_steps_details(timeline)
|
|
89
|
+
return [] unless timeline && timeline["records"].is_a?(Array)
|
|
90
|
+
|
|
91
|
+
timeline["records"]
|
|
92
|
+
.select { |r| r["result"]&.downcase == "failed" }
|
|
93
|
+
.map do |r|
|
|
94
|
+
{
|
|
95
|
+
"name" => r["name"] || r["identifier"] || "Unknown",
|
|
96
|
+
"type" => r["type"],
|
|
97
|
+
"result" => r["result"],
|
|
98
|
+
"issues" => (r["issues"] || []).map { |i| { "type" => i["type"], "message" => i["message"] } }
|
|
99
|
+
}
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def _format_failed_steps(failures)
|
|
104
|
+
return "" if failures.nil? || failures.empty?
|
|
105
|
+
|
|
106
|
+
failures.map do |f|
|
|
107
|
+
lines = ["--- #{f['name']} (#{f['type']}) ---", " Result: #{f['result']}"]
|
|
108
|
+
(f["issues"] || []).each { |iss| lines << " [#{iss['type']}] #{iss['message']}" }
|
|
109
|
+
lines.join("\n")
|
|
110
|
+
end.join("\n\n")
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def _warning_details(timeline)
|
|
114
|
+
return [] unless timeline && timeline["records"].is_a?(Array)
|
|
115
|
+
|
|
116
|
+
timeline["records"].each_with_object([]) do |r, out|
|
|
117
|
+
next unless r["issues"].is_a?(Array)
|
|
118
|
+
|
|
119
|
+
warn_issues = r["issues"].select { |i| i["type"]&.downcase&.include?("warning") }
|
|
120
|
+
next if warn_issues.empty?
|
|
121
|
+
|
|
122
|
+
out << {
|
|
123
|
+
"name" => r["name"] || r["identifier"] || "Unknown",
|
|
124
|
+
"issues" => warn_issues.map { |i| { "type" => i["type"], "message" => i["message"] } }
|
|
125
|
+
}
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def _format_warnings(warnings)
|
|
130
|
+
return "" if warnings.nil? || warnings.empty?
|
|
131
|
+
|
|
132
|
+
warnings.flat_map do |w|
|
|
133
|
+
(w["issues"] || []).map { |iss| " [#{w['name']}] #{iss['message']}" }
|
|
134
|
+
end.join("\n")
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rakit
|
|
4
|
+
module RubyGems
|
|
5
|
+
# Bump the last digit of the version in the gemspec file (e.g. "0.1.0" -> "0.1.1").
|
|
6
|
+
# Writes the file in place. Returns the new version string.
|
|
7
|
+
def self.bump(gemspec_path)
|
|
8
|
+
content = File.read(gemspec_path)
|
|
9
|
+
content.sub!(/^(\s*s\.version\s*=\s*["'])([\d.]+)(["'])/) do
|
|
10
|
+
segs = Regexp.last_match(2).split(".")
|
|
11
|
+
segs[-1] = (segs[-1].to_i + 1).to_s
|
|
12
|
+
"#{Regexp.last_match(1)}#{segs.join('.')}#{Regexp.last_match(3)}"
|
|
13
|
+
end or raise "No s.version line found in #{gemspec_path}"
|
|
14
|
+
File.write(gemspec_path, content)
|
|
15
|
+
content[/s\.version\s*=\s*["']([^"']+)["']/, 1]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.version_published?(name, version)
|
|
19
|
+
require "net/http"
|
|
20
|
+
require "uri"
|
|
21
|
+
uri = URI("https://rubygems.org/api/v2/rubygems/#{URI::DEFAULT_PARSER.escape(name)}/versions/#{URI::DEFAULT_PARSER.escape(version)}.json")
|
|
22
|
+
response = Net::HTTP.get_response(uri)
|
|
23
|
+
response.is_a?(Net::HTTPSuccess)
|
|
24
|
+
rescue StandardError
|
|
25
|
+
false
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Publish the .gem at gem_path to rubygems.org. If that version is already
|
|
29
|
+
# published, warns and returns without pushing. Raises if the file is missing
|
|
30
|
+
# or if gem push fails.
|
|
31
|
+
def self.publish(gem_path)
|
|
32
|
+
raise "Gem not found: #{gem_path}. Run rake package first." unless File.file?(gem_path)
|
|
33
|
+
|
|
34
|
+
base = File.basename(gem_path, ".gem")
|
|
35
|
+
parts = base.split("-")
|
|
36
|
+
version = parts.pop
|
|
37
|
+
name = parts.join("-")
|
|
38
|
+
|
|
39
|
+
if version_published?(name, version)
|
|
40
|
+
warn "publish: Version #{version} of #{name} is already published on rubygems.org. Skipping push. Bump the version in the gemspec to publish again."
|
|
41
|
+
return
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
success = system("gem", "push", gem_path)
|
|
45
|
+
raise "gem push failed" unless success
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
data/lib/rakit.rb
CHANGED
metadata
CHANGED
|
@@ -1,20 +1,65 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rakit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- rakit
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
-
dependencies:
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: google-protobuf
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '3.25'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '3.25'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: grpc-tools
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '1.72'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '1.72'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: rake
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '13'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '13'
|
|
12
54
|
executables: []
|
|
13
55
|
extensions: []
|
|
14
56
|
extra_rdoc_files: []
|
|
15
57
|
files:
|
|
16
58
|
- lib/rakit.rb
|
|
17
|
-
|
|
59
|
+
- lib/rakit/azure/dev_ops.rb
|
|
60
|
+
- lib/rakit/ruby_gems.rb
|
|
61
|
+
licenses:
|
|
62
|
+
- MIT
|
|
18
63
|
metadata: {}
|
|
19
64
|
rdoc_options: []
|
|
20
65
|
require_paths:
|