renuo-cli 4.13.0 → 4.14.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b57fc12b5a4094d854e4c1286323f5ba2a2f14186e4848a8e0d22e8e170588fb
4
- data.tar.gz: f612852d8349bfd0a5961c058b8cd6196dd60056544a72e81cfc0be862e9ba28
3
+ metadata.gz: 8d640a2a344a4f7297eaa13bef43760785530de9f46deef2a4016665b5ced8e8
4
+ data.tar.gz: 1fc8e948cf5c4ef86c6be83f001338cef37a84da78088c7929d162d7e891c6e9
5
5
  SHA512:
6
- metadata.gz: 30791f47ca4ac90b0d1ea21af197c1f2ff4d8bfc763a3b8869848b2042bd68c33cd4fb33ab7ed04e63595f92f8e56631c41ce64a05a002fceb969222279f9bbf
7
- data.tar.gz: ec909b93570706e37b446064b1306c261912e35e8aa166990aa23cc5b70cad02a4efa9e21f5af8954c4748a0786d706bf6f91c366bdfb2a5cad907088bc20587
6
+ metadata.gz: 7a11af0c25dd6c23eecda8f9e588ff9a599714c5e9b8b9ef9d41c3488d28a71ab834c1789cddb4aa81a73eefb72f3ce98d028b263c81fe6dd2f108f58ea3c7af
7
+ data.tar.gz: 5c323269241ed04e97243bf8009aa527fa508491e5a62169d4c77303496a17c1ced584537ab7cea02666d082b8b720730237ea47d7abb967b4a50a447c093ec7
data/README.md CHANGED
@@ -20,7 +20,7 @@ After checking out the repo, run `bin/setup` to install dependencies.
20
20
 
21
21
  Run `rake spec` to run the tests.
22
22
 
23
- Run `ruby -Ilib ./bin/renuo` to run the executable. (e.g. `ruby -Ilib ./bin/renuo -v`)
23
+ Run `bundle exec ruby -Ilib ./bin/renuo` to run the executable. (e.g. `ruby -Ilib ./bin/renuo -v`)
24
24
 
25
25
  You can also run `bin/console` for an interactive prompt that will allow you to experiment.
26
26
 
@@ -83,7 +83,7 @@ class Renuo::Cli::Commands::CheckDeploioStatus
83
83
  when "available", "success"
84
84
  puts "#{type} succeeded"
85
85
  true
86
- when "error", "failed"
86
+ when "error", "failed", "failure"
87
87
  puts fetch_build_logs
88
88
  abort "#{type} failed"
89
89
  else
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../helpers/environments"
4
+
3
5
  class Renuo::Cli::Commands::CreateDeploioApp # rubocop:disable Metrics/ClassLength
4
- DATABASE_SSH_KEY_FILE = "temporary_database_ssh_key"
5
6
  GITHUB_SSH_KEY_FILE_NAME = "temporary_repository_ssh_key"
6
7
  GITHUB_DEPLOY_KEY_TITLE = "deploio-deploy-key"
7
8
  SSH_ALGORITHM = "Ed25519"
9
+ PROJECT_NAME_PREFIX = "renuo-"
8
10
 
9
11
  command "create-deploio-app" do |c|
10
12
  c.syntax = "renuo create-deploio-app"
@@ -32,32 +34,71 @@ class Renuo::Cli::Commands::CreateDeploioApp # rubocop:disable Metrics/ClassLeng
32
34
  cleanup
33
35
  end
34
36
 
37
+ def git_url_valid?(git_url)
38
+ http_git_url_regex = %r{^https?://[\w.-]+/[\w.-]+/[\w.-]+(?:\.git)?/?$}
39
+ ssh_git_url_regex = %r{^(git@[\w.-]+[:|/][\w.-]+/[\w.-]+\.git)$}
40
+ git_url.match?(http_git_url_regex) || git_url.match?(ssh_git_url_regex)
41
+ end
42
+
35
43
  private
36
44
 
37
- def parse_arguments # rubocop:disable Metrics/MethodLength
38
- @project_name = ask("Enter the project name (e.g: my_app): ")
39
- validate_project_name!
45
+ def parse_arguments
46
+ set_project_name
47
+ set_git_url
48
+ set_postgres_version
49
+ set_vault_name
50
+ end
40
51
 
41
- @app_name = ask("Enter the app name (e.g: main): ")
42
- validate_app_name!
52
+ def set_project_name
53
+ loop do
54
+ @project_name = ask("Enter the project name (e.g: my-app): ")
43
55
 
44
- @git_url = ask("Enter the git URL (e.g: git@github.com:my-org/my-app.git): ")
45
- validate_git_url
56
+ break if @project_name&.length&.between?(3, 63) && @project_name.match?(/^[a-z0-9-]+$/)
46
57
 
47
- @postgres_version = ask("Enter the Postgres version (major version only, e.g., 16): ")
48
- validate_postgres_version
58
+ say ">> Project name must be between 3 and 63 characters and can only contain lowercase letters, " \
59
+ "numbers, and hyphens. Please try again.".colorize(:red)
60
+ end
49
61
 
50
- @vault_name = ask("Enter the 1Password vault name (leave empty to skip): ")
51
- if @vault_name.empty?
52
- say "Skipping 1Password vault setup. Defaulting to Deploio."
53
- @vault_name = "Deploio"
54
- else
55
- validate_vault_name
62
+ @project_display_name = @project_name
63
+ @project_name = PROJECT_NAME_PREFIX + @project_name
64
+ end
65
+
66
+ def set_git_url
67
+ loop do
68
+ @git_url = ask("Enter the git URL (e.g: git@github.com:my-org/my-app.git): ")
69
+ break if @git_url.present? && git_url_valid?(@git_url)
70
+
71
+ say ">> Git URL must be provided and valid. Please try again.".colorize(:red)
72
+ end
73
+ end
74
+
75
+ def set_postgres_version
76
+ loop do
77
+ @postgres_version = ask("Enter Postgres version (e.g., 16) or leave empty to skip database creation: ")
78
+ break if @postgres_version.empty? || @postgres_version.match?(/^\d+$/)
79
+
80
+ say ">> The postgres version is invalid. Only major versions are allowed. " \
81
+ "For example, use 16 instead of 16.4. Please try again.".colorize(:red)
82
+ end
83
+ end
84
+
85
+ def set_vault_name # rubocop:disable Metrics/MethodLength
86
+ loop do
87
+ @vault_name = ask("Enter the 1Password vault name (leave empty to skip): ")
88
+ if @vault_name.empty?
89
+ say "Skipping 1Password vault setup. Defaulting to Deploio."
90
+ @vault_name = "Deploio"
91
+ break
92
+ elsif @vault_name.present?
93
+ break
94
+ else
95
+ say ">> The vault name must be provided. Please try again.".colorize(:red)
96
+ end
56
97
  end
57
98
  end
58
99
 
59
100
  def setup_commands
60
- say "# Commands to setup your Deploio application\n".bold
101
+ say "\n# Commands to setup your Deploio applications\n".bold
61
102
  say_project_creation
62
103
  say "\n# Configure repository access\n".bold
63
104
  say_configure_repository_deploy_key
@@ -65,49 +106,24 @@ class Renuo::Cli::Commands::CreateDeploioApp # rubocop:disable Metrics/ClassLeng
65
106
 
66
107
  def setup_environments
67
108
  ENVIRONMENTS.each do |environment|
109
+ unless @postgres_version.empty?
110
+ say "\n# Commands to create #{environment} database \n".bold
111
+ say_database_creation(environment)
112
+ end
113
+
68
114
  say "\n# Commands to create #{environment} application \n".bold
69
- say_database_creation(environment)
70
- say "\n# Commands to create #{environment} database \n".bold
71
115
  say_app_creation(environment)
72
116
  end
73
117
  end
74
118
 
75
119
  def cleanup
76
- say "rm #{GITHUB_SSH_KEY_FILE_NAME}"
77
- end
78
-
79
- def validate_app_name!
80
- abort(">> App name must be provided") if @app_name.blank?
81
- end
82
-
83
- def validate_project_name!
84
- abort(">> Project name must be between 3 and 63 characters.") unless @project_name&.length&.between?(3, 63)
85
- end
86
-
87
- def validate_git_url
88
- abort(">> Git URL must be provided") unless @git_url
89
- http_git_url_regex = URI::DEFAULT_PARSER.make_regexp(%w[http https])
90
- ssh_git_url_regex = %r{^(git@[\w.-]+[:|/][\w.-]+/[\w.-]+\.git)$}
91
- valid_git_url = @git_url.match?(http_git_url_regex) || @git_url.match?(ssh_git_url_regex)
92
- abort(">> Git URL must be valid") unless valid_git_url
93
- end
94
-
95
- def validate_postgres_version
96
- return if @postgres_version.match?(/^\d+$/)
97
-
98
- abort("The postgres version is invalid. Only major versions are allowed (e.g., use 16 instead of 16.4)")
99
- end
100
-
101
- def validate_vault_name
102
- return if @vault_name.present?
103
-
104
- abort("The vault name must be provided")
120
+ say "\n# Cleanup temporary files\n".bold
121
+ say "rm #{GITHUB_SSH_KEY_FILE_NAME} #{GITHUB_SSH_KEY_FILE_NAME}.pub"
105
122
  end
106
123
 
107
124
  def say_project_creation
108
125
  say <<~OUTPUT
109
- nctl create project #{@project_name} \\
110
- --display-name='#{@app_name}'
126
+ nctl create project #{@project_name} --display-name='#{@project_display_name}'
111
127
  OUTPUT
112
128
  end
113
129
 
@@ -121,15 +137,14 @@ class Renuo::Cli::Commands::CreateDeploioApp # rubocop:disable Metrics/ClassLeng
121
137
  end
122
138
 
123
139
  def say_app_creation(environment)
124
- generate_ssh_key(DATABASE_SSH_KEY_FILE)
125
140
  say <<~OUTPUT
126
141
  nctl create app #{environment} \\
127
142
  --project #{@project_name} \\
128
143
  --git-ssh-private-key-from-file=#{GITHUB_SSH_KEY_FILE_NAME} \\
129
144
  --git-url=#{@git_url} \\
130
145
  --git-revision="#{environment}" \\
131
- --basic-auth=false \\ # Disabling Deploio basic auth as Rails app handles authentication
132
- --build-env=SECRET_KEY_BASE='rails secret' \\ # Don't forget to generate the secret key
146
+ --basic-auth=false `# Disabling Deploio basic auth as Rails app handles authentication` \\
147
+ --build-env=SECRET_KEY_BASE='rails secret' `# Do not forget to generate the secret key` \\
133
148
  --language=ruby \\
134
149
  --size=mini
135
150
  OUTPUT
@@ -137,19 +152,19 @@ class Renuo::Cli::Commands::CreateDeploioApp # rubocop:disable Metrics/ClassLeng
137
152
 
138
153
  def generate_ssh_key(file_name)
139
154
  say <<~OUTPUT
140
- ssh-keygen -t #{SSH_ALGORITHM} \\
141
- -f #{file_name} \\
142
- -N ''
143
- OUTPUT
144
-
145
- say <<~OUTPUT
155
+ # Create SSH key item in 1Password directly
146
156
  op item create \\
147
- --title #{file_name} \\
157
+ --category ssh \\
158
+ --ssh-generate-key #{SSH_ALGORITHM.downcase} \\
159
+ --title "#{@project_name}-deploy-key" \\
148
160
  --vault #{@vault_name} \\
149
- --category ssh-key \\
150
- --tags #{@project_name} \\
151
- --ssh-public-key < #{file_name}.pub \\
152
- --ssh-private-key < #{file_name}
161
+ --tags #{@project_name}
162
+
163
+ # Extract keys to temp files for deploio usage
164
+ op item get "#{@project_name}-deploy-key" --vault #{@vault_name} \\
165
+ --reveal --fields "public key" > #{file_name}.pub
166
+ op item get "#{@project_name}-deploy-key" --vault #{@vault_name} --reveal \\
167
+ --fields "private key" --format json | jq -r '.ssh_formats.openssh.value' > #{file_name}
153
168
  OUTPUT
154
169
  end
155
170
 
@@ -164,16 +179,34 @@ class Renuo::Cli::Commands::CreateDeploioApp # rubocop:disable Metrics/ClassLeng
164
179
  return
165
180
  end
166
181
 
167
- repo_name = @git_url[%r{github.com/(.*/.*)\.git}, 1]
182
+ repo_name = extract_repo_name(@git_url)
168
183
  unless repo_name
169
184
  say "# Attention: Deploy key unconfigured because repository URL is not valid".colorize(:orange)
170
185
  return
171
186
  end
172
187
 
173
188
  say <<~OUTPUT
174
- gh repo deploy-key add #{GITHUB_SSH_KEY_FILE_NAME} \\
189
+ gh repo deploy-key add #{GITHUB_SSH_KEY_FILE_NAME}.pub \\
175
190
  --repo #{repo_name} \\
176
191
  --title #{GITHUB_DEPLOY_KEY_TITLE}
177
192
  OUTPUT
178
193
  end
194
+
195
+ def extract_repo_name(git_url)
196
+ return nil unless git_url
197
+
198
+ # Handle HTTPS URLs: https://domain.com/author/repo.git
199
+ if git_url.match?(%r{^https?://})
200
+ match = git_url.match(%r{^https?://[^/]+/(.+?)(?:\.git)?/?$})
201
+ return match[1] if match
202
+ end
203
+
204
+ # Handle SSH URLs: git@domain.com:author/repo.git
205
+ if git_url.start_with?("git@")
206
+ match = git_url.match(/^git@[^:]+:(.+?)(?:\.git)?$/)
207
+ return match[1] if match
208
+ end
209
+
210
+ raise "Repository name not found in git URL: #{git_url}"
211
+ end
179
212
  end
@@ -26,7 +26,6 @@ class Renuo::Cli::Commands::CreateHerokuApp
26
26
  heroku_name = "#{project_name}-#{env}"
27
27
  say "heroku apps:create --region eu #{heroku_name} -t staff"
28
28
  say "heroku domains:add #{heroku_name}.renuoapp.ch --app #{heroku_name}"
29
- say "heroku domains:add #{project_name}-#{MASTER}.renuoapp.ch --app #{heroku_name}" if env == MAIN
30
29
  say "heroku addons:create heroku-postgresql --app #{heroku_name}"
31
30
  say "heroku labs:enable runtime-dyno-metadata --app #{heroku_name}"
32
31
  say "heroku pg:backups:schedule DATABASE_URL --at '02:00 Europe/Zurich' --app #{heroku_name}"
@@ -8,19 +8,13 @@ class Renuo::Cli::Commands::CreatePr
8
8
  "the PR body"
9
9
  c.option "--title <title>", String, "The title of the pull request (optional)"
10
10
  c.option "--redmine-ticket <number>", Integer, "The redmine ticket number (optional)"
11
- c.option "--draft", "Mark the PR as draft"
12
- c.option "--[no-]web-create", "Open web browser to create a pull request (default: true)"
13
11
  c.example "Create a PR with the title 'Implement XYZ'",
14
12
  "renuo create-pr --title 'Implement XYZ'"
15
- c.action do |_, options|
16
- options.default web_create: true
17
- new.run(options)
18
- end
13
+ c.action { |_, options| new.run(options) }
19
14
  end
20
15
 
21
16
  def run(opts)
22
17
  create_pr(opts)
23
- open_pr_in_browser unless opts.web_create
24
18
  end
25
19
 
26
20
  private
@@ -31,8 +25,7 @@ class Renuo::Cli::Commands::CreatePr
31
25
  "--assignee @me",
32
26
  pr_title(opts),
33
27
  "--body \"#{pr_body(opts)}\"",
34
- ("--web" if opts.web_create),
35
- ("--draft" if opts.draft)
28
+ "--web"
36
29
  ].compact.join(" ")
37
30
 
38
31
  puts `#{command}`
@@ -53,8 +46,4 @@ class Renuo::Cli::Commands::CreatePr
53
46
  current_branch = `git branch --show-current`.strip
54
47
  opts.redmine_ticket || current_branch.match(/(\d+)/)&.captures&.first
55
48
  end
56
-
57
- def open_pr_in_browser
58
- puts `gh pr view --web`
59
- end
60
49
  end
@@ -57,8 +57,11 @@ class Renuo::Cli::Commands::Debug
57
57
 
58
58
  if should_scan_deploio && Cache.stored_at("deploio_apps")
59
59
  select_deploio_targets(query).each do |app|
60
- exec_cmd = "nctl exec app #{app[:name]} bash --project #{app[:namespace]}"
61
- menu.choice(exec_cmd) { exec exec_cmd.squish }
60
+ bash_cmd = "nctl exec app #{app[:name]} bash --project #{app[:namespace]}"
61
+ menu.choice(bash_cmd) { exec bash_cmd.squish }
62
+
63
+ rails_console_cmd = "nctl exec app #{app[:name]} bundle exec rails console --project #{app[:namespace]}"
64
+ menu.choice(rails_console_cmd) { exec rails_console_cmd.squish }
62
65
 
63
66
  log_cmd = "nctl logs app #{app[:name]} -f --project #{app[:namespace]}"
64
67
  menu.choice(log_cmd) { exec log_cmd.squish }
@@ -74,7 +77,10 @@ class Renuo::Cli::Commands::Debug
74
77
  exec_cmd = "heroku run bash --app #{app[:name]}"
75
78
  menu.choice(exec_cmd) { exec exec_cmd.squish }
76
79
 
77
- log_cmd = "heroku logs -t --app #{app[:name]}"
80
+ rails_console_cmd = "heroku run bundle exec rails console --app #{app[:name]}"
81
+ menu.choice(rails_console_cmd) { exec rails_console_cmd.squish }
82
+
83
+ log_cmd = "heroku logs -t --app #{app[:name]}"
78
84
  menu.choice(log_cmd) { exec log_cmd.squish }
79
85
 
80
86
  app[:domains].each do |host|
@@ -9,6 +9,7 @@ class Renuo::Cli::Commands::FetchSecrets # rubocop:disable Metrics/ClassLength
9
9
  c.option "--init <private_vault_link>", String, "Initializes the renuo secrets store"
10
10
  c.description = <<~DESCRIPTION
11
11
  Run the command within a project folder to fetch the secrets from the renuo secrets store.
12
+ You need to create a "Secure Note" for your project and right to click copy the private link.
12
13
  The bin/setup of the project might run this command for you.
13
14
  DESCRIPTION
14
15
  c.action { |_, options| options.init ? new.init(options.init) : new.run }
@@ -102,21 +102,21 @@ class Renuo::Cli::Commands::Release # rubocop:disable Metrics/ClassLength
102
102
  @current_version ||= if `#{cmd_in_folder("git tag")}` == ""
103
103
  "0.0.0"
104
104
  else
105
- sorted_tags = `#{cmd_in_folder("git tag --sort=taggerdate")}`.split("\n").reverse
105
+ sorted_tags = `#{cmd_in_folder("git tag --sort=v:refname")}`.split("\n").reverse
106
106
  sorted_tags.find { |tag| tag =~ RenuoVersion::SEMVER_SCHEMA }.strip
107
107
  end
108
108
  end
109
109
 
110
110
  def bump_version
111
- version_bumped = find_and_replace_version
111
+ changed_files = find_and_replace_version
112
112
 
113
- return unless version_bumped
113
+ return unless changed_files.any?
114
114
 
115
- system(cmd_in_folder(%(git add #{find_files_with_version.join(" ")} && git commit -m "Bump version")))
115
+ system(cmd_in_folder(%(git add #{changed_files.join(" ")} && git commit -m "Bump version")))
116
116
  end
117
117
 
118
118
  def find_and_replace_version
119
- find_files_with_version.any? do |file_name|
119
+ find_files_with_version.filter do |file_name|
120
120
  puts "Replace the version in #{file_name}?"
121
121
  print_version_found(file_name)
122
122
  if agree("confirm?")
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Renuo::Cli::Commands::UpgradeLaptop
4
+ include CommandHelper
5
+
4
6
  command "upgrade-laptop" do |c|
5
7
  c.syntax = "renuo upgrade-laptop"
6
8
  c.summary = "Upgrades the installed apps from the app store, macOS and homebrew"
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  MAIN = "main"
4
- MASTER = "master"
5
4
  DEVELOP = "develop"
6
5
  ENVIRONMENTS = [MAIN, DEVELOP].freeze
@@ -7,6 +7,8 @@ agent:
7
7
 
8
8
  blocks:
9
9
  - name: <%= environment %>-deploy
10
+ execution_time_limit:
11
+ minutes: 10
10
12
  task:
11
13
  secrets:
12
14
  - name: <%= project_name %>
@@ -24,21 +24,11 @@ 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 -j 4
27
+ - bundle install
28
28
 
29
29
  blocks:
30
- - name: cache
31
- dependencies: []
32
- execution_time_limit:
33
- minutes: 10
34
- task:
35
- jobs:
36
- - name: cache
37
- commands:
38
- - source .semaphore/bin/cache_store rails
39
-
40
30
  - name: linting
41
- dependencies: [cache]
31
+ dependencies: []
42
32
  execution_time_limit:
43
33
  minutes: 5
44
34
  task:
@@ -48,7 +38,7 @@ blocks:
48
38
  - bin/fastcheck
49
39
 
50
40
  - name: tests
51
- dependencies: [cache]
41
+ dependencies: []
52
42
  execution_time_limit:
53
43
  minutes: 10
54
44
  task:
@@ -59,16 +49,17 @@ blocks:
59
49
  value: test
60
50
  prologue:
61
51
  commands:
62
- - sem-service start postgres
63
- - cp config/application.example.yml config/application.yml
52
+ - sem-service start postgres 16
53
+ - cp .env.example .env
64
54
  - bundle exec rails db:create db:schema:load
65
55
  jobs:
66
56
  - name: tests
67
57
  commands:
68
58
  - bin/check
69
59
  epilogue:
70
- on_fail:
60
+ always:
71
61
  commands:
62
+ - source .semaphore/bin/cache_store rails
72
63
  - mkdir -p log coverage tmp/screenshots tmp/capybara
73
64
  - artifact push job log
74
65
  - artifact push job tmp/screenshots
@@ -3,7 +3,7 @@
3
3
  # :nocov:
4
4
  module Renuo
5
5
  class Cli
6
- VERSION = "4.13.0"
6
+ VERSION = "4.14.1"
7
7
  NAME = "renuo-cli"
8
8
  end
9
9
  end
data/lib/renuo/cli.rb CHANGED
@@ -36,9 +36,11 @@ module Renuo
36
36
 
37
37
  program :version, Renuo::Cli::VERSION
38
38
  program :description, "Renuo CLI"
39
+ program :help_paging, false
39
40
  update
40
41
 
41
42
  Dir[File.expand_path("cli/{commands,services}/*.rb", __dir__)].each { |f| require f }
43
+ default_command :help
42
44
  end
43
45
 
44
46
  private
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: renuo-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.13.0
4
+ version: 4.14.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Renuo AG
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-03-25 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: activesupport
@@ -135,7 +135,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
135
135
  - !ruby/object:Gem::Version
136
136
  version: '0'
137
137
  requirements: []
138
- rubygems_version: 3.6.2
138
+ rubygems_version: 3.7.0
139
139
  specification_version: 4
140
140
  summary: The Renuo CLI automates some common workflows.
141
141
  test_files: []