Skip to content

Commit 7ed5da7

Browse files
committed
Add release checks
1 parent 87b6220 commit 7ed5da7

9 files changed

Lines changed: 260 additions & 5 deletions

File tree

.gitlab/ci/release-coordinator.canary.gitlab-ci.yml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
.release-coordinator:canary:stages:
2-
- release-coordinator:canary:notify-start
2+
- release-coordinator:canary:prepare
33
- release-coordinator:canary:build
44
- release-coordinator:canary:publish
55
- release-coordinator:canary:notify-finish
@@ -11,12 +11,23 @@
1111
release-coordinator:canary:notify-start:
1212
extends:
1313
- .release-coordinator:canary
14-
stage: release-coordinator:canary:notify-start
14+
stage: release-coordinator:canary:prepare
1515
script:
1616
- bin/pyxis internal notify_new_coordinator --coordinator-pipeline-id $CI_PIPELINE_ID
1717
variables:
1818
DRY_RUN: "false"
1919

20+
release-coordinator:canary:check:
21+
extends:
22+
- .release-coordinator:canary
23+
stage: release-coordinator:canary:prepare
24+
needs:
25+
- release-coordinator:canary:notify-start
26+
script:
27+
- bin/pyxis internal check_canary_release --build-id-to-promote $BUILD_ID_TO_PROMOTE
28+
variables:
29+
DRY_RUN: "false"
30+
2031
release-coordinator:canary:tmp-branch:
2132
extends:
2233
- .release-coordinator:canary

Gemfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,5 @@ gem 'semantic_logger', '~> 4.16', require: 'semantic_logger/sync'
1818
gem 'json', '~> 2.12'
1919

2020
gem 'discordrb', '~> 3.7'
21+
22+
gem 'toml-rb', '~> 4.1'

Gemfile.lock

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ GEM
66
ast (2.4.3)
77
base64 (0.3.0)
88
bigdecimal (4.0.1)
9+
citrus (3.0.2)
910
concurrent-ruby (1.3.5)
1011
discordrb (3.7.2)
1112
base64 (~> 0.2)
@@ -91,9 +92,9 @@ GEM
9192
semantic_logger (4.16.1)
9293
concurrent-ruby (~> 1.0)
9394
thor (1.3.2)
94-
unicode-display_width (3.1.4)
95-
unicode-emoji (~> 4.0, >= 4.0.4)
96-
unicode-emoji (4.0.4)
95+
toml-rb (4.1.0)
96+
citrus (~> 3.0, > 3.0)
97+
racc (~> 1.7)
9798
unicode-display_width (3.2.0)
9899
unicode-emoji (~> 4.1)
99100
unicode-emoji (4.2.0)
@@ -119,6 +120,7 @@ DEPENDENCIES
119120
rubocop (~> 1.76)
120121
semantic_logger (~> 4.16)
121122
thor (~> 1.3)
123+
toml-rb (~> 4.1)
122124
zeitwerk (~> 2.7)
123125

124126
BUNDLED WITH
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# frozen_string_literal: true
2+
3+
module Pyxis
4+
module Checks
5+
class AggregatedCheck
6+
include Check
7+
8+
attr_reader :check_context, :checks
9+
10+
def initialize(check_context, checks)
11+
@check_context = check_context
12+
@checks = checks
13+
end
14+
15+
def perform_check!
16+
checks.all?(&:pass?)
17+
end
18+
19+
def status_message
20+
message = []
21+
message << if pass?
22+
"#{icon} #{check_context} checks pass"
23+
else
24+
"#{icon} #{check_context} checks fail"
25+
end
26+
checks.each do |check|
27+
message << ''
28+
message << check.status_message
29+
end
30+
31+
message.join("\n")
32+
end
33+
end
34+
end
35+
end

lib/pyxis/checks/canary_release.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# frozen_string_literal: true
2+
3+
module Pyxis
4+
module Checks
5+
class CanaryRelease < AggregatedCheck
6+
def initialize(build_id_to_promote)
7+
info = Pyxis::ManagedVersioning::ComponentInfo.new(build_id: build_id_to_promote)
8+
super(
9+
'Canary release',
10+
[
11+
TucanaVersionMatch.new(info),
12+
OpenIssues.new('release blocking', ['blocks-releases'])
13+
]
14+
)
15+
end
16+
end
17+
end
18+
end

lib/pyxis/checks/check.rb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# frozen_string_literal: true
2+
3+
module Pyxis
4+
module Checks
5+
module Check
6+
def perform_check!
7+
raise NotImplementedError
8+
end
9+
10+
def status_message
11+
raise NotImplementedError
12+
end
13+
14+
def pass?
15+
return @pass if defined?(@pass)
16+
17+
@pass = perform_check!
18+
end
19+
20+
def icon
21+
if pass?
22+
pass_icon
23+
else
24+
fail_icon
25+
end
26+
end
27+
28+
def pass_icon
29+
'✅' # :white_check_mark:
30+
end
31+
32+
def fail_icon
33+
'❌' # :x:
34+
end
35+
end
36+
end
37+
end

lib/pyxis/checks/open_issues.rb

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# frozen_string_literal: true
2+
3+
module Pyxis
4+
module Checks
5+
class OpenIssues
6+
include Check
7+
8+
attr_reader :check_context, :labels
9+
10+
def initialize(check_context, labels)
11+
@check_context = check_context
12+
@labels = labels
13+
end
14+
15+
def perform_check!
16+
issues.empty?
17+
end
18+
19+
def status_message
20+
return "#{icon} No open #{check_context} issues" if pass?
21+
22+
message = []
23+
message << "#{icon} Open #{check_context} issues"
24+
issues.each do |issue|
25+
message << "- [#{issue.title}](#{issue.html_url})"
26+
end
27+
28+
message.join("\n")
29+
end
30+
31+
private
32+
33+
def issues
34+
@issues ||= GithubClient.octokit.search_issues(
35+
"org:#{GithubClient::ORGANIZATION_NAME} is:open is:issue #{labels.map { |l| "label:#{l}" }.join(' ')}"
36+
).items
37+
end
38+
end
39+
end
40+
end
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# frozen_string_literal: true
2+
3+
module Pyxis
4+
module Checks
5+
class TucanaVersionMatch
6+
include Check
7+
8+
attr_reader :component_info
9+
10+
def initialize(component_info)
11+
@component_info = component_info
12+
end
13+
14+
def perform_check!
15+
[sagittarius_version, aquila_version, draco_version, taurus_version].uniq.size == 1
16+
end
17+
18+
def status_message
19+
return "#{icon} Components use the same tucana version" if pass?
20+
21+
message = []
22+
message << "#{icon} Components use different tucana versions"
23+
message << ''
24+
message << "sagittarius: #{sagittarius_version}"
25+
message << "aquila: #{aquila_version}"
26+
message << "draco: #{draco_version}"
27+
message << "taurus: #{taurus_version}"
28+
29+
message.join("\n")
30+
end
31+
32+
private
33+
34+
def sagittarius_version
35+
return @sagittarius_version if defined?(@sagittarius_version)
36+
37+
gemfile_content = Base64.decode64 GithubClient.octokit.contents(
38+
Project::Sagittarius.github_path,
39+
path: 'Gemfile.lock',
40+
ref: executed_component_info[:sagittarius]
41+
).content
42+
43+
@sagittarius_version = Bundler::LockfileParser.new(gemfile_content)
44+
.specs
45+
.find { |spec| spec.name == 'tucana' }
46+
.version
47+
.to_s
48+
end
49+
50+
def aquila_version
51+
@aquila_version ||= from_cargo_lockfile(
52+
Base64.decode64(
53+
GithubClient.octokit.contents(
54+
Project::Aquila.github_path,
55+
path: 'Cargo.lock',
56+
ref: executed_component_info[:aquila]
57+
).content
58+
)
59+
)
60+
end
61+
62+
def draco_version
63+
@draco_version ||= from_cargo_lockfile(
64+
Base64.decode64(
65+
GithubClient.octokit.contents(
66+
Project::Draco.github_path,
67+
path: 'Cargo.lock',
68+
ref: executed_component_info[:draco]
69+
).content
70+
)
71+
)
72+
end
73+
74+
def taurus_version
75+
@taurus_version ||= from_cargo_lockfile(
76+
Base64.decode64(
77+
GithubClient.octokit.contents(
78+
Project::Taurus.github_path,
79+
path: 'Cargo.lock',
80+
ref: executed_component_info[:taurus]
81+
).content
82+
)
83+
)
84+
end
85+
86+
def from_cargo_lockfile(lockfile)
87+
toml = TomlRB.parse(lockfile, symbolize_keys: true)
88+
toml[:package].find { |package| package[:name] == 'tucana' }[:version]
89+
end
90+
91+
def executed_component_info
92+
@executed_component_info ||= component_info.execute
93+
end
94+
end
95+
end
96+
end

lib/pyxis/commands/internal.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,20 @@ def notify_finish_coordinator
7676
#{pipeline.web_url}
7777
DESC
7878
end
79+
80+
desc 'check_canary_release', ''
81+
method_option :build_id_to_promote, required: true, type: :numeric
82+
def check_canary_release
83+
check = Pyxis::Checks::CanaryRelease.new(options[:build_id_to_promote])
84+
85+
severity = check.pass? ? :info : :error
86+
87+
Pyxis::DiscordClient.new.send_notification(check.status_message, severity)
88+
89+
puts check.status_message
90+
91+
exit(false) unless check.pass?
92+
end
7993
end
8094
end
8195
end

0 commit comments

Comments
 (0)