renuo-cli 4.21.2 → 4.22.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: 9120767921d12d64d169446d7dc3a7e0329f498e80fc5c4f4afca46583ff9418
4
- data.tar.gz: e16008accec27c375cb831856ce4fb9acce1ac2630fcdae9dfffcbc0a9a63c2f
3
+ metadata.gz: 13ccfc20af1ba9cda74f9ffb3a9b8f24cbbf34808a4d94e5baab28f6da384d26
4
+ data.tar.gz: ac1d41b13a258f07268bb78ed0f6b697eaf4abd2ffe4ae324b4c41880bb253df
5
5
  SHA512:
6
- metadata.gz: 93c42b19caa65fb26adf806fe0079c842b6ed8661aaaa639ed5325bc4cd64d62d783363a6374db2aed5b3df50bc6769af4220b703359f75c0a38f5ef77dbc0c8
7
- data.tar.gz: 3966f6f6510994401559c46b32931645d51f726d84a67a402977b5df6d9640b8e146c3208f04d54c05c3e0decee308c54da9a3c952a8ce6a068af637a2551c53
6
+ metadata.gz: 72fa17d236d416cb18c8f99d1e463ee8e90c6740102348df812090269c996337fad30a48c953f94ad31fc710f835d311dbf45a8190eb9e982398f7d404d88358
7
+ data.tar.gz: d0cf0de0355ae2b5bef63e1209b74fd818a362072054386106c495035df5c67053b3e1e18a79eecb480812dfe801acc1974b9f8e3d97dc9e198231c42db0ad0d
data/README.md CHANGED
@@ -14,6 +14,16 @@ gem install renuo-cli
14
14
  renuo -h
15
15
  ```
16
16
 
17
+ ### Shell Autocompletion
18
+
19
+ To enable zsh autocompletion for renuo-cli commands:
20
+
21
+ ```sh
22
+ renuo setup-autocompletion
23
+ ```
24
+
25
+ This will install the completion script and provide instructions to add it to your shell configuration.
26
+
17
27
  ## Development
18
28
 
19
29
  After checking out the repo, run `bin/setup` to install dependencies.
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Renuo::Cli::Commands::ConfigureSemaphore
4
+ include CredentialsHelper
5
+
4
6
  attr_accessor :project_name, :environment, :slack_endpoint
5
7
 
6
8
  command "configure-semaphore" do |c|
@@ -13,7 +15,7 @@ class Renuo::Cli::Commands::ConfigureSemaphore
13
15
 
14
16
  def initialize
15
17
  @project_name = File.basename(Dir.getwd)
16
- @slack_endpoint = "https://hooks.slack.com/services/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"
18
+ @slack_endpoint = credentials[:slack_endpoint]
17
19
  end
18
20
 
19
21
  def run # rubocop:disable Metrics/MethodLength
@@ -175,6 +175,7 @@ class Renuo::Cli::Commands::CreateDeploioApp # rubocop:disable Metrics/ClassLeng
175
175
  --basic-auth=#{environment == "develop"} \\
176
176
  --build-env=SECRET_KEY_BASE="$(rails secret)" \\
177
177
  --language=ruby \\
178
+ --buildpack-stack=heroku \\
178
179
  --size=mini
179
180
  OUTPUT
180
181
  end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../helpers/commander_introspector"
4
+ require_relative "../helpers/zsh_completion_script"
5
+
6
+ class Renuo::Cli::Commands::SetupAutocompletion
7
+ include CommandHelper
8
+
9
+ COMPLETION_DIR = File.expand_path("~/.zfunc")
10
+ COMPLETION_FILENAME = "_renuo"
11
+ COMPLETION_FILE_PATH = File.join(COMPLETION_DIR, COMPLETION_FILENAME)
12
+ VERSION_STAMP_PREFIX = "# renuo-cli-version: "
13
+
14
+ command "setup-autocompletion" do |c|
15
+ c.syntax = "renuo setup-autocompletion"
16
+ c.summary = "Sets up zsh autocompletion for renuo-cli"
17
+ c.description = "Installs zsh autocompletion script for renuo-cli commands and options"
18
+ c.example "renuo setup-autocompletion", "Sets up zsh autocompletion"
19
+ c.action { new.run }
20
+ end
21
+
22
+ def self.refresh_if_stale
23
+ return unless File.exist?(COMPLETION_FILE_PATH)
24
+ return if File.foreach(COMPLETION_FILE_PATH).first(2).last&.include?("#{VERSION_STAMP_PREFIX}#{Renuo::Cli::VERSION}")
25
+
26
+ new.write_completion_file(silent: true)
27
+ end
28
+
29
+ def run
30
+ write_completion_file
31
+ print_installation_instructions
32
+ end
33
+
34
+ def write_completion_file(silent: false)
35
+ say "Setting up zsh autocompletion for renuo-cli...".colorize(:green) unless silent
36
+ FileUtils.mkdir_p(COMPLETION_DIR)
37
+ File.write(COMPLETION_FILE_PATH, stamped_script)
38
+ say "✓ Completion script installed to #{COMPLETION_FILE_PATH}".colorize(:green) unless silent
39
+ end
40
+
41
+ private
42
+
43
+ def stamped_script
44
+ script = completion_script
45
+ first_line, rest = script.split("\n", 2)
46
+ "#{first_line}\n#{VERSION_STAMP_PREFIX}#{Renuo::Cli::VERSION}\n#{rest}"
47
+ end
48
+
49
+ def completion_script
50
+ ZshCompletionScript.new(CommanderIntrospector.commands_data).render
51
+ end
52
+
53
+ def print_installation_instructions
54
+ say ""
55
+ say "To enable autocompletion, add the following to your ~/.zshrc:".colorize(:blue)
56
+ say ""
57
+ say " # Enable renuo-cli autocompletion"
58
+ say " fpath+=#{COMPLETION_DIR}"
59
+ say ""
60
+ say "Then restart your shell or run:".colorize(:blue)
61
+ say " exec zsh"
62
+ say "On the next renuo-cli update, the completion script will be automatically refreshed".colorize(:cyan)
63
+ end
64
+ end
@@ -0,0 +1 @@
1
+ kQgNz+OSHLaY1YKXAXJi5Nb/qP2N2+2BHy2anqFasu1HBitNqrEfV+rfFKNfzFY1hNtAezy3SiCA3csD3jsIa7Ew/UHcys493oGNF6p9m07y7/gE+sK4fiQVIRMWxVZ7RPy5qszve+E=--emz3hoXKrKnjz9cq--GxOYIZ9IVxnCjhrg/3kHpQ==
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "shellwords"
4
+
5
+ module CommanderIntrospector
6
+ module_function
7
+
8
+ HELP_FLAGS = ["-h", "--help"].freeze
9
+
10
+ def commands_data
11
+ runner_commands = Commander::Runner.instance.commands
12
+ entries = runner_commands.reject { |name, _| name.to_s == "help" }
13
+ .map { |name, cmd| build_command_data(name.to_s, cmd) }
14
+ (entries + [help_entry]).sort_by { |cmd| cmd[:name] }
15
+ end
16
+
17
+ def build_command_data(name, command)
18
+ {
19
+ name: name,
20
+ summary: clean(command.summary),
21
+ description: first_line(clean(command.description.to_s)),
22
+ syntax: command.syntax.to_s,
23
+ options: extract_options(command.options),
24
+ examples: extract_examples(name, command.examples)
25
+ }
26
+ end
27
+
28
+ def help_entry
29
+ {
30
+ name: "help",
31
+ summary: "Display global or command help documentation",
32
+ description: "Display global or command help documentation",
33
+ syntax: "renuo help [command]",
34
+ options: [], examples: []
35
+ }
36
+ end
37
+
38
+ def extract_options(options) # rubocop:disable Metrics/AbcSize
39
+ options.filter_map do |opt|
40
+ switches = opt[:switches]
41
+ next if switches.intersect?(HELP_FLAGS)
42
+
43
+ long = switches.find { |s| s.start_with?("--") }
44
+ next unless long
45
+
46
+ {
47
+ flags: [switches.find { |s| s.match?(/\A-[^-]/) }, long.split.first].compact,
48
+ description: clean(opt[:description]),
49
+ argument: switches.filter_map { |s| s[/<([^>]+)>/, 1] }.first
50
+ }
51
+ end
52
+ end
53
+
54
+ def extract_examples(command_name, examples)
55
+ return [] if examples.blank?
56
+
57
+ prefix = ["renuo", *command_name.split]
58
+ examples.filter_map { |pair| pick_command(pair, prefix) }.uniq
59
+ end
60
+
61
+ def pick_command(pair, prefix)
62
+ Array(pair).find do |text|
63
+ next false unless text.is_a?(String)
64
+
65
+ tokens = safe_split(text)
66
+ tokens && tokens.first(prefix.size) == prefix
67
+ end
68
+ end
69
+
70
+ def safe_split(text)
71
+ Shellwords.split(text)
72
+ rescue ArgumentError
73
+ nil
74
+ end
75
+
76
+ def clean(text)
77
+ text.to_s.gsub(/['\[\]]/, "").strip
78
+ end
79
+
80
+ def first_line(text)
81
+ text.split("\n").map(&:strip).reject(&:empty?).first.to_s
82
+ end
83
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/encrypted_configuration"
4
+
5
+ module CredentialsHelper
6
+ CONFIG = ActiveSupport::EncryptedConfiguration.new(
7
+ config_path: File.expand_path("../credentials.yml.enc", __dir__),
8
+ key_path: "", env_key: "RENUO_CLI_MASTER_KEY", raise_if_missing_key: true
9
+ )
10
+
11
+ def credentials
12
+ CONFIG.config
13
+ end
14
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+
5
+ class ZshCompletionScript
6
+ TEMPLATE_PATH = File.expand_path("../templates/zsh_completion.erb", __dir__)
7
+
8
+ def initialize(commands)
9
+ @commands = commands
10
+ end
11
+
12
+ def render
13
+ ERB.new(File.read(TEMPLATE_PATH), trim_mode: "-").result(binding)
14
+ end
15
+
16
+ private
17
+
18
+ def namespaced_groups
19
+ groups.select { |_, sub_commands| sub_commands.first[:subname] }
20
+ end
21
+
22
+ def flat_commands
23
+ groups.reject { |_, sub_commands| sub_commands.first[:subname] }.map { |_, sub_commands| sub_commands.first }
24
+ end
25
+
26
+ def top_level_entries
27
+ groups.map do |name, sub_commands|
28
+ summary = sub_commands.first[:subname] ? "#{name} subcommands" : sub_commands.first[:summary]
29
+ { name: name, summary: summary }
30
+ end
31
+ end
32
+
33
+ def groups
34
+ @groups ||= @commands.each_with_object({}) do |cmd, acc|
35
+ head, sub = cmd[:name].split(" ", 2)
36
+ key = sub ? head : cmd[:name]
37
+ (acc[key] ||= []) << cmd.merge(subname: sub)
38
+ end
39
+ end
40
+
41
+ def command_body(cmd, indent:)
42
+ pad = " " * indent
43
+ messages = message_blocks(cmd, pad)
44
+ return [arguments_block(cmd, pad), *messages].join("\n") if cmd[:options].any?
45
+
46
+ inner_messages = message_blocks(cmd, "#{pad} ")
47
+ [
48
+ %(#{pad}if [[ "${words[CURRENT]}" == -* ]]; then),
49
+ %(#{pad} _arguments '--help[show help]'),
50
+ "#{pad}else",
51
+ *inner_messages,
52
+ "#{pad}fi"
53
+ ].join("\n")
54
+ end
55
+
56
+ def message_blocks(cmd, pad) # rubocop:disable Metrics/AbcSize
57
+ blocks = []
58
+ blocks << message_block("syntax", [cmd[:syntax]], pad) unless cmd[:syntax].to_s.empty?
59
+ blocks << message_block("description", [cmd[:description]], pad) unless cmd[:description].to_s.empty?
60
+ blocks << message_block("example", cmd[:examples], pad) unless cmd[:examples].empty?
61
+ blocks
62
+ end
63
+
64
+ def arguments_block(cmd, pad)
65
+ specs = ["'--help[show help]'"]
66
+ cmd[:options].each { |opt| specs << option_spec(opt) }
67
+ specs << "'*: :( )'"
68
+ "#{pad}_arguments \\\n#{specs.map { |s| "#{pad} #{s}" }.join(" \\\n")}"
69
+ end
70
+
71
+ def option_spec(option)
72
+ flags = option[:flags]
73
+ bracket = "[#{escape_bracket(option[:description])}]"
74
+ arg_part = option[:argument] ? ":#{option[:argument]}:" : ""
75
+ return "'#{flags.first}#{bracket}#{arg_part}'" if flags.size < 2
76
+
77
+ "'(#{flags.join(" ")})'{#{flags.join(",")}}'#{bracket}#{arg_part}'"
78
+ end
79
+
80
+ def message_block(header, lines, pad)
81
+ payload = (["-- #{header} --"] + lines + [""]).join("\n")
82
+ "#{pad}_message -r '#{escape_single(payload)}'"
83
+ end
84
+
85
+ def escape_bracket(text)
86
+ text.to_s.gsub("]") { "\\]" }.gsub("'") { "'\\''" }
87
+ end
88
+
89
+ def escape_single(text)
90
+ text.to_s.gsub("'") { "'\\''" }
91
+ end
92
+ end
@@ -0,0 +1,61 @@
1
+ #compdef renuo
2
+
3
+ _renuo() {
4
+ local curcontext="$curcontext" state
5
+ typeset -A opt_args
6
+
7
+ zstyle ":completion:${curcontext}:*:descriptions" format '\n -- %d --'
8
+ zstyle ":completion:${curcontext}:*" group-name ''
9
+ zstyle ":completion:${curcontext}:*" prefix-needed no
10
+
11
+ _arguments -C \
12
+ '--help[show help]' \
13
+ '--version[show version]' \
14
+ '--trace[show backtrace on error]' \
15
+ '1: :_renuo_commands' \
16
+ '*:: :->args' && return 0
17
+
18
+ case $state in
19
+ args)
20
+ local cmd_key="${words[1]}"
21
+ <% namespaced_groups.each do |group, subs| -%>
22
+ if [[ "$cmd_key" == "<%= group %>" ]]; then
23
+ case "${words[2]}" in
24
+ <% subs.each do |sub| -%>
25
+ <%= sub[:subname] %>)
26
+ <%= command_body(sub, indent: 24) %>
27
+ ;;
28
+ <% end -%>
29
+ esac
30
+ return
31
+ fi
32
+ <% end -%>
33
+ case "$cmd_key" in
34
+ <% flat_commands.each do |cmd| -%>
35
+ <%= cmd[:name] %>)
36
+ <% if cmd[:name] == "help" -%>
37
+ _arguments \
38
+ '--help[show help]' \
39
+ '*:command:_renuo_commands'
40
+ <% else -%>
41
+ <%= command_body(cmd, indent: 20) %>
42
+ <% end -%>
43
+ ;;
44
+ <% end -%>
45
+ esac
46
+ ;;
47
+ esac
48
+ }
49
+
50
+ _renuo_commands() {
51
+ local commands
52
+ commands=(
53
+ <% top_level_entries.each do |entry| -%>
54
+ '<%= entry[:name] %>:<%= entry[:summary] %>'
55
+ <% end -%>
56
+ )
57
+
58
+ _describe 'commands' commands
59
+ }
60
+
61
+ _renuo "$@"
@@ -3,7 +3,7 @@
3
3
  # :nocov:
4
4
  module Renuo
5
5
  class Cli
6
- VERSION = "4.21.2"
6
+ VERSION = "4.22.0"
7
7
  NAME = "renuo-cli"
8
8
  end
9
9
  end
data/lib/renuo/cli.rb CHANGED
@@ -35,6 +35,7 @@ module Renuo
35
35
  program :help_paging, false
36
36
 
37
37
  Dir[File.expand_path("cli/{commands,services}/**/*.rb", __dir__)].each { |f| require f }
38
+ Renuo::Cli::Commands::SetupAutocompletion.refresh_if_stale
38
39
  default_command :help
39
40
  end
40
41
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: renuo-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.21.2
4
+ version: 4.22.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Renuo AG
@@ -88,11 +88,16 @@ files:
88
88
  - lib/renuo/cli/commands/heroku_users.rb
89
89
  - lib/renuo/cli/commands/list_large_git_files.rb
90
90
  - lib/renuo/cli/commands/release.rb
91
+ - lib/renuo/cli/commands/setup_autocompletion.rb
91
92
  - lib/renuo/cli/commands/setup_uptimerobot.rb
92
93
  - lib/renuo/cli/commands/upgrade_laptop.rb
94
+ - lib/renuo/cli/credentials.yml.enc
93
95
  - lib/renuo/cli/helpers/command_helper.rb
96
+ - lib/renuo/cli/helpers/commander_introspector.rb
97
+ - lib/renuo/cli/helpers/credentials_helper.rb
94
98
  - lib/renuo/cli/helpers/environments.rb
95
99
  - lib/renuo/cli/helpers/renuo_version.rb
100
+ - lib/renuo/cli/helpers/zsh_completion_script.rb
96
101
  - lib/renuo/cli/services/cache.rb
97
102
  - lib/renuo/cli/services/cloudfront_config_service.rb
98
103
  - lib/renuo/cli/services/deploio.rb
@@ -106,6 +111,7 @@ files:
106
111
  - lib/renuo/cli/templates/semaphore/semaphore.yml.erb
107
112
  - lib/renuo/cli/templates/slidev/README.md.erb
108
113
  - lib/renuo/cli/templates/slidev/package.json.erb
114
+ - lib/renuo/cli/templates/zsh_completion.erb
109
115
  - lib/renuo/cli/version.rb
110
116
  homepage: https://github.com/renuo/renuo-cli
111
117
  licenses: