Skip to content

Commit 2737a04

Browse files
committed
Refactor canary release implementation
1 parent eaad060 commit 2737a04

4 files changed

Lines changed: 140 additions & 73 deletions

File tree

lib/pyxis/commands/internal.rb

Lines changed: 3 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -5,92 +5,22 @@ module Commands
55
class Internal < Thor
66
include Thor::Actions
77

8-
RETICULUM_CI_BUILDS_PREFIX = 'ghcr.io/code0-tech/reticulum/ci-builds/'
9-
CONTAINER_RELEASE_PREFIX = 'registry.gitlab.com/code0-tech/packages/'
10-
118
desc 'release_canary_tmp_branch', ''
129
method_option :build_id_to_promote, required: true, type: :numeric
1310
def release_canary_tmp_branch
14-
component_information = Pyxis::ManagedVersioning::ComponentInfo.new(
15-
build_id: options[:build_id_to_promote]
16-
).execute
17-
18-
raise 'Build not found' if component_information.nil?
19-
20-
GitlabClient.client.create_branch(
21-
Project::Reticulum.api_gitlab_path,
22-
"pyxis/canary-build/#{options[:build_id_to_promote]}",
23-
component_information[:reticulum]
24-
)
25-
26-
version_variables = component_information.map do |component, version|
27-
next nil unless Project.components.include?(component)
28-
29-
["OVERRIDE_#{component}_VERSION", version]
30-
end.compact
31-
32-
create_env_file(
33-
'reticulum_variables',
34-
version_variables + [['C0_GH_TOKEN', Pyxis::Environment.github_reticulum_publish_token]]
35-
)
11+
Release::Canary.new.create_build_branch(options[:build_id_to_promote])
3612
end
3713

3814
desc 'release_canary_tmp_branch_cleanup', ''
3915
method_option :build_id_to_promote, required: true, type: :numeric
4016
def release_canary_tmp_branch_cleanup
41-
GitlabClient.client.delete_branch(
42-
Project::Reticulum.api_gitlab_path,
43-
"pyxis/canary-build/#{options[:build_id_to_promote]}"
44-
)
17+
Release::Canary.new.remove_build_branch(options[:build_id_to_promote])
4518
end
4619

4720
desc 'release_canary_publish_tags', ''
4821
method_option :coordinator_pipeline_id, required: true, type: :numeric
4922
def release_canary_publish_tags
50-
build_id = GitlabClient.client
51-
.list_pipeline_bridges(Project::Pyxis.api_gitlab_path, options[:coordinator_pipeline_id])
52-
.find { |bridge| bridge['name'] == 'release-coordinator:canary:build' }
53-
.dig('downstream_pipeline', 'id')
54-
55-
info = ManagedVersioning::ComponentInfo.new(build_id: build_id)
56-
container_tag = info.find_container_tag_for_build_id
57-
container_tags = info.find_manifests.map do |manifest|
58-
next nil unless Project.components.include?(manifest.first.to_sym)
59-
60-
next "#{manifest.first}:#{container_tag}" if manifest.length == 1
61-
62-
"#{manifest.first}:#{container_tag}-#{manifest.last}"
63-
end.compact
64-
65-
File.write('tmp/gitlab_token', Pyxis::Environment.gitlab_release_tools_token)
66-
run 'crane auth login -u code0-release-tools --password-stdin registry.gitlab.com < tmp/gitlab_token'
67-
68-
overall_success = true
69-
70-
original_pretend = options[:pretend]
71-
options[:pretend] = Pyxis::GlobalStatus.dry_run?
72-
container_tags.each do |tag|
73-
success = run "crane copy #{RETICULUM_CI_BUILDS_PREFIX}#{tag} #{CONTAINER_RELEASE_PREFIX}#{tag}",
74-
abort_on_failure: false
75-
overall_success &&= success
76-
77-
logger.error('Failed to copy container image to release registry', image: tag) unless success
78-
end
79-
options[:pretend] = original_pretend
80-
81-
run 'crane auth logout registry.gitlab.com'
82-
File.delete('tmp/gitlab_token')
83-
84-
abort unless overall_success || Pyxis::GlobalStatus.dry_run?
85-
end
86-
87-
no_commands do
88-
include SemanticLogger::Loggable
89-
90-
def create_env_file(name, variables)
91-
path = File.absolute_path(File.join(__FILE__, "../../../../tmp/#{name}.env"))
92-
File.write(path, variables.map { |k, v| "#{k}=#{v}" }.join("\n"))
93-
end
23+
Release::Canary.new.publish_tags(options[:coordinator_pipeline_id])
9424
end
9525
end
9626
end

lib/pyxis/release/canary.rb

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# frozen_string_literal: true
2+
3+
module Pyxis
4+
module Release
5+
class Canary
6+
include SemanticLogger::Loggable
7+
8+
def create_build_branch(build_to_promote)
9+
component_information = Pyxis::ManagedVersioning::ComponentInfo.new(
10+
build_id: build_to_promote
11+
).execute
12+
13+
raise 'Build not found' if component_information.nil?
14+
15+
GitlabClient.client.create_branch(
16+
Project::Reticulum.api_gitlab_path,
17+
"pyxis/canary-build/#{build_to_promote}",
18+
component_information[:reticulum]
19+
)
20+
21+
version_variables = component_information.map do |component, version|
22+
next nil unless Project.components.include?(component)
23+
24+
["OVERRIDE_#{component}_VERSION", version]
25+
end.compact
26+
27+
Utils::PipelineHelpers.create_env_file(
28+
'reticulum_variables',
29+
version_variables + [['C0_GH_TOKEN', Pyxis::Environment.github_reticulum_publish_token]]
30+
)
31+
end
32+
33+
def remove_build_branch(build_to_promote)
34+
GitlabClient.client.delete_branch(
35+
Project::Reticulum.api_gitlab_path,
36+
"pyxis/canary-build/#{build_to_promote}"
37+
)
38+
end
39+
40+
def publish_tags(coordinator_pipeline_id)
41+
build_id = GitlabClient.client
42+
.list_pipeline_bridges(Project::Pyxis.api_gitlab_path, coordinator_pipeline_id)
43+
.find { |bridge| bridge['name'] == 'release-coordinator:canary:build' }
44+
.dig('downstream_pipeline', 'id')
45+
46+
info = ManagedVersioning::ComponentInfo.new(build_id: build_id)
47+
common = Common.new
48+
49+
success = common.copy_container_images_to_release_registry(info)
50+
51+
raise Pyxis::MessageError, 'Failed to copy all container images' unless success
52+
end
53+
end
54+
end
55+
end

lib/pyxis/release/common.rb

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# frozen_string_literal: true
2+
3+
module Pyxis
4+
module Release
5+
class Common
6+
include SemanticLogger::Loggable
7+
8+
CI_BUILDS_PREFIX = 'ghcr.io/code0-tech/reticulum/ci-builds/'
9+
CONTAINER_RELEASE_REGISTRY = 'registry.gitlab.com'
10+
CONTAINER_RELEASE_PREFIX = "#{CONTAINER_RELEASE_REGISTRY}/code0-tech/packages/".freeze
11+
12+
CONTAINER_RELEASE_PUBLISH_USER = 'code0-release-tools'
13+
14+
def copy_container_images_to_release_registry(component_info)
15+
container_tag = info.find_container_tag_for_build_id
16+
container_tags = component_info.find_manifests.map do |manifest|
17+
next nil unless Project.components.include?(manifest.first.to_sym)
18+
19+
next "#{manifest.first}:#{container_tag}" if manifest.length == 1
20+
21+
"#{manifest.first}:#{container_tag}-#{manifest.last}"
22+
end.compact
23+
24+
success = true
25+
container_tags.each do |tag|
26+
success &&= copy_container_image_to_release_registry(tag)
27+
end
28+
29+
success
30+
end
31+
32+
def copy_container_image_to_release_registry(tag)
33+
with_release_registry_auth do
34+
logger.info('Copying container image to release registry', tag: tag)
35+
return if Pyxis::GlobalStatus.dry_run?
36+
37+
success = system("crane copy #{CI_BUILDS_PREFIX}#{tag} #{CONTAINER_RELEASE_PREFIX}#{tag}")
38+
logger.error('Failed to copy container image to release registry', tag: tag)
39+
success
40+
end
41+
end
42+
43+
def with_release_registry_auth
44+
return yield if File.exist?('tmp/gitlab_token')
45+
46+
logger.info('Authentication with release registry')
47+
File.write('tmp/gitlab_token', Pyxis::Environment.gitlab_release_tools_token)
48+
success = system(
49+
"crane auth login
50+
-u #{CONTAINER_RELEASE_PUBLISH_USER}
51+
--password-stdin #{CONTAINER_RELEASE_REGISTRY}
52+
< tmp/gitlab_token"
53+
)
54+
55+
unless success
56+
logger.error('Failed to authenticate with release registry')
57+
raise Pyxis::Error, 'Failed to authenticate with release registry'
58+
end
59+
60+
yield
61+
ensure
62+
system("crane auth logout #{CONTAINER_RELEASE_REGISTRY}")
63+
File.delete('tmp/gitlab_token')
64+
logger.info('Unauthenticated from release registry')
65+
end
66+
end
67+
end
68+
end
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# frozen_string_literal: true
2+
3+
module Pyxis
4+
module Utils
5+
module PipelineHelpers
6+
module_function
7+
8+
def create_env_file(name, variables)
9+
path = File.absolute_path(File.join(__FILE__, "../../../../tmp/#{name}.env"))
10+
File.write(path, variables.map { |k, v| "#{k}=#{v}" }.join("\n"))
11+
end
12+
end
13+
end
14+
end

0 commit comments

Comments
 (0)