renuo-cli 4.22.0 → 4.24.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 13ccfc20af1ba9cda74f9ffb3a9b8f24cbbf34808a4d94e5baab28f6da384d26
4
- data.tar.gz: ac1d41b13a258f07268bb78ed0f6b697eaf4abd2ffe4ae324b4c41880bb253df
3
+ metadata.gz: df7e7a6d263ccafd4c253e16e68a8ce4dbb489e1f726d6f8e55988fa82a3c9f3
4
+ data.tar.gz: be67e8df4fedb4a8e08913e2758a83ab43a4ee5e7117277db93a4fac64e6326e
5
5
  SHA512:
6
- metadata.gz: 72fa17d236d416cb18c8f99d1e463ee8e90c6740102348df812090269c996337fad30a48c953f94ad31fc710f835d311dbf45a8190eb9e982398f7d404d88358
7
- data.tar.gz: d0cf0de0355ae2b5bef63e1209b74fd818a362072054386106c495035df5c67053b3e1e18a79eecb480812dfe801acc1974b9f8e3d97dc9e198231c42db0ad0d
6
+ metadata.gz: bf10d74c836b636236cedf3acceea7cf01ee682ef5f79d153f5d4c08a39a0d17ff8f574e3c49e6505e289ed4b178dedf2eca1ec42af1018085c3c22ab4911a34
7
+ data.tar.gz: 19320a9d06dd5d2d4cde92bd86c68f3d6efd5509ccfb4d18cdcebc26653f9109c0cd6fbedb9755ed68ba7744ed9d4c9a40ecf86842ea637572440fd27918c2b8
data/README.md CHANGED
@@ -30,7 +30,8 @@ After checking out the repo, run `bin/setup` to install dependencies.
30
30
 
31
31
  Run `rake spec` to run the tests.
32
32
 
33
- Run `bundle exec ruby -Ilib ./bin/renuo` to run the executable. (e.g. `ruby -Ilib ./bin/renuo -v`)
33
+ Run `bin/renuo` to run the executable against the working tree. (e.g. `bin/renuo -v`)
34
+ The shipped executable lives in `exe/renuo`; `bin/renuo` is a bundler binstub that loads it with the local code.
34
35
 
35
36
  You can also run `bin/console` for an interactive prompt that will allow you to experiment.
36
37
 
@@ -46,7 +47,7 @@ This binds the `renuo` command to your local version, so you can run commands di
46
47
 
47
48
  ```shell
48
49
  renuo -h
49
- renuo generate_github_settings
50
+ renuo generate-password
50
51
  ```
51
52
 
52
53
  To install this gem onto your local machine, run `bundle exec rake install`.
data/exe/renuo ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "renuo/cli"
5
+ Renuo::Cli.new.start
@@ -1,14 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # :nocov:
3
+ # simplecov:disable
4
4
  module Renuo::Cli::Commands::Ci
5
5
  class CheckDeploioStatus
6
6
  TIMEOUT_IN_SECONDS = 600
7
7
  INTERVAL_IN_SECONDS = 5
8
+ LOG_DRAIN_SECONDS = 2
8
9
 
9
10
  command "ci check-deploio-status" do |c|
10
11
  c.syntax = "renuo ci check-deploio-status [options] <app-name>"
11
- c.summary = "Checks the build and release status of the deployment."
12
+ c.summary = "Checks the build status of the deployment."
12
13
  c.description = c.summary
13
14
  c.option "-p", "--project <name>", String, "The name of the project"
14
15
  c.option "-g", "--git-revision <hash>", String, "The Git revision"
@@ -16,13 +17,9 @@ module Renuo::Cli::Commands::Ci
16
17
  end
17
18
 
18
19
  def run
19
- puts "(1/2) Checking build status for revision #{@revision}..."
20
- poll "build"
21
- abort "build check timed out after #{TIMEOUT_IN_SECONDS} seconds" if build.nil?
22
-
23
- puts "(2/2) Checking release status for build #{build_name}..."
24
- poll "release"
25
- abort "release check timed out after #{TIMEOUT_IN_SECONDS} seconds" if release.nil?
20
+ puts "Checking build status for revision #{@revision}..."
21
+ succeeded = with_streamed_build_logs { poll_build }
22
+ abort "build check timed out after #{TIMEOUT_IN_SECONDS} seconds" unless succeeded
26
23
  end
27
24
 
28
25
  private
@@ -41,81 +38,83 @@ module Renuo::Cli::Commands::Ci
41
38
  abort unless system cmd
42
39
  end
43
40
 
44
- def fetch(type)
45
- command = "nctl get #{type} -a #{@app} -p #{@project} -o yaml"
41
+ def fetch_builds
42
+ command = "nctl get builds -a #{@app} -p #{@project} -o yaml"
46
43
  stdout, stderr, status = Open3.capture3 command
47
44
 
48
- abort "error fetching #{type} information: #{stderr}" unless status.success?
45
+ abort "error fetching build information: #{stderr}" unless status.success?
49
46
  Psych.load_stream stdout
50
47
  end
51
48
 
52
- def fetch_build_logs
53
- command = "nctl logs build -a #{@app} -p #{@project} -l 5000 --no-labels"
54
- stdout, stderr, status = Open3.capture3 command
49
+ def with_streamed_build_logs
50
+ pid = Process.spawn(
51
+ "nctl logs application #{@app} -p #{@project} --type=build -f --lines=100 --since 15m --no-labels",
52
+ out: $stdout, err: $stdout
53
+ )
54
+ yield
55
+ ensure
56
+ sleep LOG_DRAIN_SECONDS
57
+ stop_streaming(pid)
58
+ end
55
59
 
56
- puts "error fetching build logs: #{stderr}" unless status.success?
57
- stdout
60
+ def stop_streaming(pid)
61
+ return unless pid
62
+
63
+ Process.kill("KILL", pid)
64
+ Process.wait(pid)
65
+ rescue Errno::ESRCH, Errno::ECHILD
66
+ nil
58
67
  end
59
68
 
60
69
  def build
61
- @build ||= fetch("builds").find do |build|
70
+ @build ||= fetch_builds.find do |build|
62
71
  build.dig("spec", "forProvider", "sourceConfig", "git", "revision") == @revision
63
72
  end
64
73
  end
65
74
 
66
75
  def build_name
67
- @build_name ||= build.dig("metadata", "name")
76
+ build.dig("metadata", "name")
68
77
  end
69
78
 
70
79
  def build_status
71
80
  build.dig("status", "atProvider", "buildStatus")
72
81
  end
73
82
 
74
- def release
75
- @release ||= fetch("releases").find do |release|
76
- release.dig("spec", "forProvider", "build", "name") == build_name
77
- end
78
- end
79
-
80
- def release_status
81
- release.dig("status", "atProvider", "releaseStatus")
82
- end
83
-
84
- def succeeded?(status, type) # rubocop:disable Metrics/MethodLength
85
- time = "[#{Time.now.utc.iso8601}]"
83
+ def build_succeeded? # rubocop:disable Metrics/MethodLength
84
+ time = Time.current.iso8601.to_s
86
85
 
87
- case status
86
+ case build_status
88
87
  when "available", "success"
89
- puts "#{time} #{type} succeeded"
90
- true
91
- when "superseded"
92
- puts "#{time} release was superseded"
88
+ puts "[RENUO-CLI] #{time} build #{build_name} succeeded"
93
89
  true
94
90
  when "paused"
95
- abort "#{time} app is paused"
91
+ abort "[RENUO-CLI] #{time} app is paused"
96
92
  when "error", "failed", "failure"
97
- puts fetch_build_logs
98
- abort "#{time} #{type} failed"
93
+ abort "[RENUO-CLI] #{time} build #{build_name} failed"
99
94
  else
100
- puts "#{time} #{type} status is #{status}, waiting..."
101
- instance_variable_set :"@#{type}", nil
95
+ puts "[RENUO-CLI] #{time} build #{build_name} status is #{build_status}, waiting..."
96
+ false
102
97
  end
103
98
  end
104
99
 
105
- def poll(type)
100
+ def poll_build # rubocop:disable Metrics/MethodLength
106
101
  elapsed = 0
107
102
 
108
103
  while elapsed < TIMEOUT_IN_SECONDS
109
- if send type
110
- break if succeeded?(send(:"#{type}_status"), type)
104
+ if build
105
+ return true if build_succeeded?
106
+
107
+ @build = nil
111
108
  else
112
- puts "no matching #{type} found, waiting..."
109
+ puts "no matching build found, waiting..."
113
110
  end
114
111
 
115
112
  sleep INTERVAL_IN_SECONDS
116
113
  elapsed += INTERVAL_IN_SECONDS
117
114
  end
115
+
116
+ false
118
117
  end
119
118
  end
120
119
  end
121
- # :nocov:
120
+ # simplecov:enable
@@ -15,7 +15,7 @@ module Renuo::Cli::Commands::Ci
15
15
  end
16
16
 
17
17
  def run
18
- system! "nctl update app #{@app} -p #{@project} --env APP_REVISION=#{@git_revision} " \
18
+ system! "nctl update app #{@app} -p #{@project} " \
19
19
  "#{"--build-env RUBY_VERSION=#{File.read(".ruby-version").strip} " if @docker}" \
20
20
  "--git-revision #{@git_revision} --skip-repo-access-check"
21
21
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # :nocov:
3
+ # simplecov:disable
4
4
  class Renuo::Cli::Commands::CreateAwsProject
5
5
  command "create-aws-project" do |c|
6
6
  c.syntax = "renuo create-aws-project"
@@ -139,4 +139,4 @@ class Renuo::Cli::Commands::CreateAwsProject
139
139
  CLOUDFRONT_COMMANDS
140
140
  end
141
141
  end
142
- # :nocov:
142
+ # simplecov:enable
@@ -96,11 +96,11 @@ class Renuo::Cli::Commands::CreateDeploioApp # rubocop:disable Metrics/ClassLeng
96
96
 
97
97
  def set_postgres_version
98
98
  loop do
99
- @postgres_version = ask("Enter Postgres version (e.g., 17) or leave empty to skip database creation: ")
99
+ @postgres_version = ask("Enter Postgres version (e.g., 18) or leave empty to skip database creation: ")
100
100
  break if @postgres_version.empty? || @postgres_version.match?(/^\d+$/)
101
101
 
102
102
  say ">> The postgres version is invalid. Only major versions are allowed. " \
103
- "For example, use 17 instead of 17.4. Please try again.".colorize(:red)
103
+ "For example, use 18 instead of 18.4. Please try again.".colorize(:red)
104
104
  end
105
105
  end
106
106
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # :nocov:
3
+ # simplecov:disable
4
4
 
5
5
  class Renuo::Cli::Commands::CreateDeploioObjectStorage
6
6
  include CommandHelper
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../helpers/environments"
4
+
5
+ class Renuo::Cli::Commands::CreateNineCloudvm
6
+ SSH_KEY_FILE_NAME = "temporary_cloudvm_ssh_key"
7
+ SSH_ALGORITHM = "Ed25519"
8
+ DEFAULT_LOCATION = "nine-es34"
9
+ DEFAULT_MACHINE_TYPE = "nine-small-1"
10
+ DEFAULT_OS = "debian13"
11
+ VPS_SETUP_GUIDE_REPO = "https://github.com/renuo/vps-setup-guide"
12
+
13
+ command "create-nine-cloudvm" do |c|
14
+ c.syntax = "renuo create-nine-cloudvm [project-name]"
15
+ c.summary = "Generates the script necessary to setup a cloud VM on Nine."
16
+ c.description = <<~DESC
17
+ Generates the script necessary to setup a cloud VM on Nine.
18
+ This command will:
19
+ - Prompt for the organization, project name, location, machine type, OS, and 1Password vault name.
20
+ - Validate the provided inputs.
21
+ - Generate an SSH key (stored in 1Password) to access the cloud VM.
22
+ - Generate the commands to create a cloud VM for each environment on Nine.
23
+ DESC
24
+ c.option "-o", "--org <organization>", String, "The organization name"
25
+ c.example "renuo create-nine-cloudvm my-app",
26
+ "Prompts the user for the necessary information and generates the commands " \
27
+ "to create the cloud VMs on Nine."
28
+ c.action { |args, options| new.run(args, options) }
29
+ end
30
+
31
+ def run(args, options = {})
32
+ @project_name, = args
33
+ @options = options
34
+ parse_arguments
35
+ setup_ssh_key
36
+ setup_environments
37
+ say_configuration_note
38
+ ensure
39
+ cleanup
40
+ end
41
+
42
+ private
43
+
44
+ def valid_name?(name)
45
+ name&.length&.between?(3, 63) && name.match?(/^[a-z0-9-]+$/)
46
+ end
47
+
48
+ def parse_arguments
49
+ set_organization
50
+ set_project_name
51
+ set_location
52
+ set_machine_type
53
+ set_os
54
+ set_vault_name
55
+ end
56
+
57
+ def set_project_name
58
+ loop do
59
+ break if valid_name?(@project_name)
60
+
61
+ say ">> Project name must be between 3 and 63 characters and can only contain lowercase letters, " \
62
+ "numbers, and hyphens. Please try again.".colorize(:red)
63
+
64
+ @project_name = ask("Enter the project name (e.g: my-app): ")
65
+ end
66
+
67
+ @project_name_with_org_prefix = "#{@organization}-#{@project_name}"
68
+ end
69
+
70
+ def set_organization
71
+ @organization = @options.org if @options.org
72
+
73
+ loop do
74
+ break if valid_name?(@organization)
75
+
76
+ say ">> Organization name must be between 3 and 63 characters and can only contain lowercase letters, " \
77
+ "numbers, and hyphens. Please try again.".colorize(:red)
78
+
79
+ @organization = ask("Enter the organization name (default: renuo): ") { |q| q.default = "renuo" }
80
+ end
81
+ end
82
+
83
+ def set_location
84
+ @location = ask("Enter the location (default: #{DEFAULT_LOCATION}): ") { |q| q.default = DEFAULT_LOCATION }
85
+ end
86
+
87
+ def set_machine_type
88
+ @machine_type = ask("Enter the machine type (default: #{DEFAULT_MACHINE_TYPE}): ") do |q|
89
+ q.default = DEFAULT_MACHINE_TYPE
90
+ end
91
+ end
92
+
93
+ def set_os
94
+ @os = ask("Enter the operating system (default: #{DEFAULT_OS}): ") { |q| q.default = DEFAULT_OS }
95
+ end
96
+
97
+ def set_vault_name
98
+ @vault_name = ask("An SSH key to access the cloud VM will be stored on 1Password. " \
99
+ "Enter the 1Password vault name: ") do |q|
100
+ q.default = "Deploio"
101
+ end
102
+ end
103
+
104
+ def setup_ssh_key
105
+ say "\n# Generate the SSH key to access the cloud VM\n".bold
106
+ generate_ssh_key(SSH_KEY_FILE_NAME)
107
+ end
108
+
109
+ def setup_environments
110
+ ENVIRONMENTS.each do |environment|
111
+ say "\n# Commands to create #{environment} cloud VM \n".bold
112
+ say_cloudvm_creation(environment)
113
+ end
114
+ end
115
+
116
+ def say_configuration_note
117
+ say "\n# Configure the cloud VMs\n".bold
118
+ say <<~OUTPUT
119
+ Once the cloud VMs are up, configure them (SSH access, monitoring, backups) with the
120
+ Ansible VPS setup guide: #{VPS_SETUP_GUIDE_REPO}
121
+ OUTPUT
122
+ end
123
+
124
+ def cleanup
125
+ say "\n# Cleanup temporary files\n".bold
126
+ say "rm #{SSH_KEY_FILE_NAME} #{SSH_KEY_FILE_NAME}.pub"
127
+ end
128
+
129
+ def say_cloudvm_creation(environment)
130
+ say <<~OUTPUT
131
+ nctl create cloudvm #{environment} \\
132
+ --project #{@project_name_with_org_prefix} \\
133
+ --wait \\
134
+ --location="#{@location}" \\
135
+ --machine-type="#{@machine_type}" \\
136
+ --hostname="#{hostname(environment)}" \\
137
+ --os="#{@os}" \\
138
+ --ssh-keys-from-files=#{SSH_KEY_FILE_NAME}.pub
139
+ OUTPUT
140
+ end
141
+
142
+ def hostname(environment)
143
+ "#{@project_name_with_org_prefix}-#{environment}"
144
+ end
145
+
146
+ def generate_ssh_key(file_name)
147
+ say <<~OUTPUT
148
+ # Create SSH key item in 1Password directly
149
+ op item create \\
150
+ --account renuo.1password.com \\
151
+ --category ssh \\
152
+ --ssh-generate-key #{SSH_ALGORITHM.downcase} \\
153
+ --title "#{@project_name_with_org_prefix}-cloudvm-key" \\
154
+ --vault #{@vault_name} \\
155
+ --tags #{@project_name_with_org_prefix}
156
+
157
+ # Extract keys to temp files for cloud VM usage
158
+ op item get "#{@project_name_with_org_prefix}-cloudvm-key" --vault #{@vault_name} \\
159
+ --account renuo.1password.com \\
160
+ --reveal --fields "public key" > #{file_name}.pub
161
+ op item get "#{@project_name_with_org_prefix}-cloudvm-key" --vault #{@vault_name} --reveal \\
162
+ --account renuo.1password.com \\
163
+ --fields "private key" --format json | jq -r '.ssh_formats.openssh.value' > #{file_name}
164
+ OUTPUT
165
+ end
166
+ end
@@ -1,10 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Renuo::Cli::Commands::SetupUptimerobot
4
- CONTACT_GROUP_IDS = "2811620_0_0-6298845_0_0-5223624_0_0"
5
- HTTPS_MONITORING = 1
6
- STATUS_PAUSED = 0
7
-
8
4
  command "setup-uptimerobot" do |c|
9
5
  c.syntax = "renuo setup-uptimerobot"
10
6
  c.summary = "Sets up uptimerobot for the given project"
@@ -17,23 +13,37 @@ class Renuo::Cli::Commands::SetupUptimerobot
17
13
 
18
14
  def initialize(args)
19
15
  abort("No project name given.") if args.nil?
20
- @api_key = ask_for_api_key
21
16
  @url = validate_url(args)
17
+ @api_key = read_api_key
22
18
  end
23
19
 
24
20
  def run
25
- robot_obj = monitoring_call(:new, create_robot_params)
26
- validate_new_project(robot_obj)
27
- response = monitoring_call(:edit, edit_robot_params(robot_obj["monitor"]["id"]))
28
- final_command_status(response)
21
+ monitor = api_call(Net::HTTP::Post, "/monitors", create_monitor_params)
22
+ api_call(Net::HTTP::Post, "/monitors/#{monitor["id"]}/pause")
23
+ say "Successfully configured uptimerobot 🤩😎👍"
24
+ say "The monitor https://dashboard.uptimerobot.com/monitors/#{monitor["id"]} checks from Europe " \
25
+ "and is paused for now..."
26
+ say "You can start it once your app is ready to go live 🤠"
29
27
  end
30
28
 
31
29
  private
32
30
 
33
- def ask_for_api_key
31
+ def read_api_key
34
32
  say "This command will configure uptimerobot for your project."
35
- say "Please give in the api_key for uptimerobot"
36
- ask("API-key: ")
33
+ ENV.fetch("UPTIMEROBOT_API_KEY") { read_api_key_from_1password }
34
+ end
35
+
36
+ LIST_API_KEY_CMD = "op item list --tags renuo-cli-uptimerobot-api-key --format=json"
37
+ EXTRACT_API_KEY_CMD = "op item get --reveal --field credential"
38
+ def read_api_key_from_1password
39
+ say "Fetching the uptimerobot api_key from 1Password..."
40
+ stdout, stderr, status = Open3.capture3("#{LIST_API_KEY_CMD} | #{EXTRACT_API_KEY_CMD}")
41
+ abort("Failed to fetch the uptimerobot api_key from 1Password: #{stderr.strip}") unless status.success?
42
+
43
+ api_key = stdout.strip
44
+ abort("No 1Password item tagged 'renuo-cli-uptimerobot-api-key' was found.") if api_key.empty?
45
+
46
+ api_key
37
47
  end
38
48
 
39
49
  def validate_url(args)
@@ -42,54 +52,38 @@ class Renuo::Cli::Commands::SetupUptimerobot
42
52
  URI.parse(args.first)
43
53
  end
44
54
 
45
- def create_robot_params
55
+ UPTIME_SLACK_CHANNEL_ALERT_CONTACT_ID = "7783511"
56
+ EUROPE_REGION = "eu"
57
+ def create_monitor_params
46
58
  {
47
- friendly_name: @url.host.downcase,
59
+ friendlyName: @url.host.downcase,
48
60
  url: @url.to_s,
49
- type: HTTPS_MONITORING,
61
+ type: "HTTP",
50
62
  interval: 300,
51
- alert_contacts: CONTACT_GROUP_IDS
63
+ timeout: 30,
64
+ checkSSLErrors: true,
65
+ # notify #uptime after 10 minutes of downtime, without re-notifying
66
+ assignedAlertContacts: [{ alertContactId: UPTIME_SLACK_CHANNEL_ALERT_CONTACT_ID, threshold: 10, recurrence: 0 }],
67
+ regionData: { REGION: [EUROPE_REGION], MANUAL_SELECTED: true },
68
+ domainExpirationReminder: false
52
69
  }
53
70
  end
54
71
 
55
- def edit_robot_params(uptimerobot_monitoring_id)
56
- { id: uptimerobot_monitoring_id, status: STATUS_PAUSED }
57
- end
58
-
59
- def final_command_status(response)
60
- if response["stat"] == "ok"
61
- say "Successfully configured uptimerobot 🤩😎👍"
62
- say "The status for the monitored project has paused for now..."
63
- say "You can start it once your app is ready to go live 🤠"
64
- else
65
- say "Something went wrong during the configuration...😕. Uptimerobot returned '#{response["error"]["message"]}'"
66
- end
67
- end
72
+ API_URL = "https://api.uptimerobot.com/v3"
73
+ def api_call(request_class, path, params = {})
74
+ uri = URI("#{API_URL}#{path}")
75
+ request = request_class.new(uri, "Authorization" => "Bearer #{@api_key}", "Content-Type" => "application/json")
76
+ request.body = params.to_json
77
+ response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(request) }
78
+ abort("An error occurred. Uptimerobot returned '#{error_message(response)}'") unless response.is_a?(Net::HTTPSuccess)
68
79
 
69
- def validate_new_project(robot_obj)
70
- return if robot_obj["stat"] == "ok"
71
-
72
- abort("An error occoured. Uptimerobot returned '#{robot_obj["error"]["message"]}'")
73
- end
74
-
75
- def https_request(path, params = {})
76
- uri = URI(path)
77
- req = request_body(uri, params)
78
- Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
79
- http.request(req)
80
- end
81
- end
82
-
83
- def request_body(uri, params)
84
- params[:api_key] = @api_key
85
- data = URI.encode_www_form params
86
- req = Net::HTTP::Post.new(uri)
87
- req.body = data
88
- req
80
+ JSON.parse(response.body)
89
81
  end
90
82
 
91
- def monitoring_call(action, params)
92
- response = https_request("https://api.uptimerobot.com/v2/#{action}Monitor", params)
93
- JSON.parse(response.body)
83
+ def error_message(response)
84
+ body = JSON.parse(response.body)
85
+ Array(body["message"] || body["error"] || response.body).join(", ")
86
+ rescue JSON::ParserError
87
+ response.body
94
88
  end
95
89
  end
@@ -24,7 +24,7 @@ global_job_config:
24
24
  - source .semaphore/bin/cache_restore rails
25
25
  - bundle config set deployment 'true'
26
26
  - bundle config set path 'vendor/bundle'
27
- - bundle install
27
+ - bundle install --retry 3
28
28
 
29
29
  blocks:
30
30
  - name: linting
@@ -49,7 +49,7 @@ blocks:
49
49
  value: test
50
50
  prologue:
51
51
  commands:
52
- - sem-service start postgres 17
52
+ - sem-service start postgres 18
53
53
  - cp .env.example .env
54
54
  - bundle exec rails db:create db:schema:load
55
55
  jobs:
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # :nocov:
3
+ # simplecov:disable
4
4
  module Renuo
5
5
  class Cli
6
- VERSION = "4.22.0"
6
+ VERSION = "4.24.0"
7
7
  NAME = "renuo-cli"
8
8
  end
9
9
  end
10
- # :nocov:
10
+ # simplecov:enable
data/lib/renuo/cli.rb CHANGED
@@ -21,7 +21,7 @@ def agree(question)
21
21
  super
22
22
  end
23
23
 
24
- # :nocov:
24
+ # simplecov:disable
25
25
  module Renuo
26
26
  class Cli
27
27
  module Commands; end
@@ -40,4 +40,4 @@ module Renuo
40
40
  end
41
41
  end
42
42
  end
43
- # :nocov:
43
+ # simplecov:enable
metadata CHANGED
@@ -1,11 +1,11 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: renuo-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.22.0
4
+ version: 4.24.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Renuo AG
8
- bindir: bin
8
+ bindir: exe
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
@@ -62,9 +62,8 @@ extra_rdoc_files: []
62
62
  files:
63
63
  - LICENSE.txt
64
64
  - README.md
65
- - bin/renuo
65
+ - exe/renuo
66
66
  - lib/renuo/cli.rb
67
- - lib/renuo/cli/commands/check_deploio_status.rb
68
67
  - lib/renuo/cli/commands/ci/check_deploio_status.rb
69
68
  - lib/renuo/cli/commands/ci/setup.rb
70
69
  - lib/renuo/cli/commands/ci/update_deploio_app.rb
@@ -77,6 +76,7 @@ files:
77
76
  - lib/renuo/cli/commands/create_deploio_object_storage.rb
78
77
  - lib/renuo/cli/commands/create_heroku_app.rb
79
78
  - lib/renuo/cli/commands/create_new_logins.rb
79
+ - lib/renuo/cli/commands/create_nine_cloudvm.rb
80
80
  - lib/renuo/cli/commands/create_pr.rb
81
81
  - lib/renuo/cli/commands/create_slidev_presentation.rb
82
82
  - lib/renuo/cli/commands/debug.rb
@@ -132,7 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
132
132
  - !ruby/object:Gem::Version
133
133
  version: '0'
134
134
  requirements: []
135
- rubygems_version: 4.0.8
135
+ rubygems_version: 4.0.15
136
136
  specification_version: 4
137
137
  summary: The Renuo CLI automates some common workflows.
138
138
  test_files: []
data/bin/renuo DELETED
@@ -1,4 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'renuo/cli'
4
- Renuo::Cli.new.start
@@ -1,120 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # :nocov:
4
- class Renuo::Cli::Commands::CheckDeploioStatus
5
- TIMEOUT_IN_SECONDS = 600
6
- INTERVAL_IN_SECONDS = 5
7
-
8
- command "check-deploio-status" do |c|
9
- c.syntax = "renuo check-deploio-status"
10
- c.summary = "DEPRECATED: Use 'renuo ci check-deploio-status' instead."
11
- c.description = "Checks the build and release status of the deployment."
12
- c.option "-a", "--app <name>", String, "The name of the app"
13
- c.option "-p", "--project <name>", String, "The name of the project"
14
- c.option "-g", "--git-revision <revision>", String, "The git revision to check"
15
- c.action { |_, options| new(options).run }
16
- end
17
-
18
- def initialize(options)
19
- @revision = options.git_revision || `git rev-parse HEAD`.strip
20
- @app = options.app || ENV.fetch("DEPLOIO_APP_NAME", nil)
21
- @project = options.project || ENV.fetch("DEPLOIO_PROJECT", nil)
22
-
23
- abort "missing app name" if @app.nil?
24
- abort "missing project name" if @project.nil?
25
- end
26
-
27
- def run
28
- warn "DEPRECATED: Use 'renuo ci check-deploio-status' instead."
29
- puts "(1/2) Checking build status for revision #{@revision}..."
30
- poll "build"
31
- abort "build check timed out after #{TIMEOUT_IN_SECONDS} seconds" if build.nil?
32
-
33
- puts "(2/2) Checking release status for build #{build_name}..."
34
- poll "release"
35
- abort "release check timed out after #{TIMEOUT_IN_SECONDS} seconds" if release.nil?
36
- end
37
-
38
- private
39
-
40
- def system!(cmd)
41
- abort unless system cmd
42
- end
43
-
44
- def fetch(type)
45
- command = "nctl get #{type} -a #{@app} -p #{@project} -o yaml"
46
- stdout, stderr, status = Open3.capture3 command
47
-
48
- abort "error fetching #{type} information: #{stderr}" unless status.success?
49
- Psych.load_stream stdout
50
- end
51
-
52
- def fetch_build_logs
53
- command = "nctl logs build -a #{@app} -p #{@project} -l 5000 --no-labels"
54
- stdout, stderr, status = Open3.capture3 command
55
-
56
- puts "error fetching build logs: #{stderr}" unless status.success?
57
- stdout
58
- end
59
-
60
- def build
61
- @build ||= fetch("builds").find do |build|
62
- build.dig("spec", "forProvider", "sourceConfig", "git", "revision") == @revision
63
- end
64
- end
65
-
66
- def build_name
67
- @build_name ||= build.dig("metadata", "name")
68
- end
69
-
70
- def build_status
71
- build.dig("status", "atProvider", "buildStatus")
72
- end
73
-
74
- def release
75
- @release ||= fetch("releases").find do |release|
76
- release.dig("spec", "forProvider", "build", "name") == build_name
77
- end
78
- end
79
-
80
- def release_status
81
- release.dig("status", "atProvider", "releaseStatus")
82
- end
83
-
84
- def succeeded?(status, type) # rubocop:disable Metrics/MethodLength
85
- time = "[#{Time.now.utc.iso8601}]"
86
-
87
- case status
88
- when "available", "success"
89
- puts "#{time} #{type} succeeded"
90
- true
91
- when "superseded"
92
- puts "#{time} release was superseded"
93
- true
94
- when "paused"
95
- abort "#{time} app is paused"
96
- when "error", "failed", "failure"
97
- puts fetch_build_logs
98
- abort "#{time} #{type} failed"
99
- else
100
- puts "#{time} #{type} status is #{status}, waiting..."
101
- instance_variable_set :"@#{type}", nil
102
- end
103
- end
104
-
105
- def poll(type)
106
- elapsed = 0
107
-
108
- while elapsed < TIMEOUT_IN_SECONDS
109
- if send type
110
- break if succeeded?(send(:"#{type}_status"), type)
111
- else
112
- puts "no matching #{type} found, waiting..."
113
- end
114
-
115
- sleep INTERVAL_IN_SECONDS
116
- elapsed += INTERVAL_IN_SECONDS
117
- end
118
- end
119
- end
120
- # :nocov: